閱讀295 返回首頁    go 阿裏雲 go 技術社區[雲棲]


使用DirectDraw直接顯示YUV視頻數據

最近在編寫一個進行視頻播放的ActiveX控件,工作已經接近尾聲,現將其中顯示YUV數據的使用DirectDraw的一些經驗總結如下:(解碼部分不是我編寫的,我負責從網絡接收數據,將數據傳給解碼器,並將解碼得到的YUV數據進行顯示,最初在顯示部分我是先將YUV數據轉換為RGB數據,再以位圖的形式顯示到屏幕上,但發現CPU占用率比較高,後來改用DirectDraw直接顯示YUV數據)

1.在DirectDraw中創建YUV表麵

  與一般表麵不同的是,創建YUV表麵時需要指定象素格式,並指定YUV數據的FourCC碼,關於FourCC碼可以參考微軟MSDN站點上的說明,下麵是具體的創建方法:(以YUV4:2:0格式為例,其中drawwidth和drawheight是欲顯示圖像的寬度和高度,以象素為單位)

LPDIRECTDRAW7           lpDD;    // DirectDraw 對象指針
LPDIRECTDRAWSURFACE7    lpDDSPrimary;  // DirectDraw 主表麵指針
LPDIRECTDRAWSURFACE7    lpDDSOffScr;  // DirectDraw 離屏表麵指針
DDSURFACEDESC2   ddsd;    // DirectDraw 表麵描述

  // 創建DirectCraw對象
  if (DirectDrawCreateEx(NULL, (VOID**)&lpDD, IID_IDirectDraw7, NULL) != DD_OK)
 {
  //MessageBox("Error Create DDraw.");
  return FALSE;
 }

 // 設置協作層
    if (lpDD->SetCooperativeLevel(hWnd,
   DDSCL_NORMAL | DDSCL_NOWINDOWCHANGES) != DD_OK)
  {
  //MessageBox("Error Create Level.", s);
        return FALSE;
 }

    // 創建主表麵
 ZeroMemory(&ddsd, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);
    ddsd.dwFlags = DDSD_CAPS;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
    if (lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL) != DD_OK)
  {
  //MessageBox("Error Create Primary Surface.", s);
        return FALSE;
 }
   
 LPDIRECTDRAWCLIPPER  pcClipper;   // Cliper
 if( lpDD->CreateClipper( 0, &pcClipper, NULL ) != DD_OK )
        return FALSE;

    if( pcClipper->SetHWnd( 0, hWnd ) != DD_OK )
    {
        pcClipper->Release();
        return FALSE;
    }

    if( lpDDSPrimary->SetClipper( pcClipper ) != DD_OK )
    {
        pcClipper->Release();
        return FALSE;
    }

    // Done with clipper
    pcClipper->Release();

 // 創建YUV表麵 
 ZeroMemory(&ddsd, sizeof(ddsd));
 ddsd.dwSize = sizeof(ddsd);
 ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
 ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
 ddsd.dwWidth = drawwidth;
 ddsd.dwHeight = drawheight;
 ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
 ddsd.ddpfPixelFormat.dwFlags  = DDPF_FOURCC | DDPF_YUV ;
 ddsd.ddpfPixelFormat.dwFourCC = MAKEFOURCC('Y','V', '1', '2');
 ddsd.ddpfPixelFormat.dwYUVBitCount = 8;
 if (lpDD->CreateSurface(&ddsd, &lpDDSOffScr, NULL) != DD_OK)
  {
  //MessageBox("Error Create Off Surface.", s);
        return FALSE;
 }

2.將解碼得到的YUV數據拷貝到YUV表麵

  設解碼得到的YUV數據的指針分別是Y,U,V, 每行數據長度為BPS,具體拷貝代碼如下,這裏需要特別注意每拷一行都要對寫指針加ddsd.lPitch(對於Y)或ddsd.lPitch/2(對於UV):

 LPBYTE lpSurf = (LPBYTE)ddsd.lpSurface;
 LPBYTE PtrY = Y;
 LPBYTE PtrU = U;
 LPBYTE PtrV = V;
 
 do {
  ddRval = lpDDSOffScr->Lock(NULL,&ddsd,DDLOCK_WAIT | DDLOCK_WRITEONLY,NULL);
 } while(ddRval == DDERR_WASSTILLDRAWING);
 if(ddRval != DD_OK)
  return 1;

// 填充離屏表麵
 if(lpSurf)
 {
  for (int i=0;iHeight;i++)
  {
   memcpy(lpSurf, PtrY, ddsd.dwWidth);
   PtrY += BpS;
   lpSurf += ddsd.lPitch;
  }

  for (int i=0;iHeight/2;i++)
  {
   memcpy(lpSurf, PtrV, ddsd.dwWidth/2);
   PtrV += BpS;
   lpSurf += ddsd.lPitch/2;
  }
  for (int i=0;iHeight/2;i++)
  {
   memcpy(lpSurf, PtrU, ddsd.dwWidth/2);
   PtrU += BpS;
   lpSurf += ddsd.lPitch/2;
  }

 }
 
 lpDDSOffScr->Unlock(NULL);

3.YUV表麵的顯示

  現在就可以直接將YUV表麵Blt到主表麵或後備表麵進行顯示了:(設lpDDSBack為後備表麵)

  ddRval = lpDDSBack->Blt(NULL, lpDDSOffScr, NULL, DDBLT_WAIT, NULL);

這樣就實現了YUV數據的顯示,對比發現使用DirectDraw直接進行YUV數據顯示,CPU占用率降低了一半。

最後更新:2017-04-03 16:48:57

  上一篇:go HDU 4667 精度+凸包+圓切線
  下一篇:go 利用jailkit-2.16.tar.gz + ssh 進行 chroot 操作[備忘]