登录 用户中心() [退出] 后台管理 注册
   
您的位置: 首页 >> 程序员学前班[不再更新,只读] >> 主题: [YUV]两篇高质量的 directdraw 文章     [回主站]     [分站链接]
标题
[YUV]两篇高质量的 directdraw 文章
clq
浏览(0) + 2010-08-16 15:27:36 发表 编辑

关键字:

http://blog.csdn.net/wtxidian/archive/2007/01/08/1476676.aspx
http://blog.csdn.net/wangchenggggdn/archive/2010/07/05/5713075.aspx


clq
2010-8-16 15:29:07 发表 编辑

如何使用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;i Height;i++)
  {
   memcpy(lpSurf, PtrY, ddsd.dwWidth);
   PtrY += BpS;
   lpSurf += ddsd.lPitch;
  }

  for (int i=0;i Height/2;i++)
  {
   memcpy(lpSurf, PtrV, ddsd.dwWidth/2);
   PtrV += BpS;
   lpSurf += ddsd.lPitch/2;
  }
  for (int i=0;i Height/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占用率降低了一半。

 

发表于 @ 2007年01月08日 00:02:00


clq
2010-8-16 15:29:45 发表 编辑

yuv显卡转换rgb

首先要感谢ffmpeg, 如果没有它,所有做电脑视频----包括PC, 嵌入式, DV/DC,DVD机等公司(也包括我们公司), 一大半得关门。没有它,一些中小公司将无法研发这些编解码器, 没有了这些技术基础,产品将无从谈起;没有它, PC上常见的Mplayer, KMPlayer, 暴风影音等都不会存在!所以,在很多情况下,做视频软件,ffmpeg是软件的底层库,是基础平台。

        其次要感谢显卡的超强功能。显卡越来越强大,有些显卡的GPU能力甚至要超过了PC上CPU的计算能力-----是不是主次颠倒了?正是有了这些显卡,逼真高效的游戏才成为了可能。

       现在我要说的,就是ffmpeg的解码后,多路高效显示的一点点小技巧。

       微软推出DirectShow时,可以说是天生为流媒体开发而制作的, 因为它提供的功能太强大了:多种格式视频显示、视频音频同步、视频合成、视频分离等等令人激动的功能。如果是单路或几路视频显示,当然用 DirectShow是最好的选择,但是,如果要显示的视频路非常多,例如25路,使用它就会发现资源占用率极高,一路视频显示,不算解码,要4个线程! 并且图像合成时CPU占有率极高----总而言之,DirectShow不适合多路视频的专业监控。

       DirectDraw是我发现的在Windows平台下最佳的解决方案,唯一的缺点就是,你需要做一些视频图像的处理,这需要更强的专业知识和更多的开发时间。事实上,从某种程度上来说,你就是在开发一款mini型的DirectShow COM.

      不必多说,转入正文。

       ffmpeg解码出来后,一般会生成YV12格式,在2005年以后出产的显卡,它可以直接放到显存中直接显示的---当然,这并不 是绝对的,有些显卡,例如明基一些笔记本就不支持YV12。这种做法显然是最高效的,中间没有转换格式,数据量也是最小的。可是,有时我们需要对视频做一 些特殊处理,例如,在视频上放一些文字,显示一些时间等,这种情况下,因为在DirectX提供的YUV表面上是无法得到HDC句柄的,如果直接操作 YUV数据, 那非常的麻烦--你自已要完成提供画线,字体合成之类工作,也就是说,你不能使用Win32 API, 要自已写类似的API Function. 实际上有个很简单的办法,那就是利用显卡自已的格式转换功能!

      显卡一般支持YUV格式直接转到RGB24/RGB32。至于显卡支持具体的格式,请用DirectX Caps来查询就知道了。要实现上述功能,其实是很简单,创建主表面-->创建RGB从表面---->创建YV12从表面,然后将YV12数 据复制到 YV12表面, Blt到RGB表面(在这一步中显卡自动完成YV12到RGB的转换), 然后取RGB表面的HDC, 就可以利用TextOut, FillRect, Line之类的Win32 API来绘图写字了,最后,将RGB表面Blt到主表面,这个过程就算是结束了。

       需要说明的是,这个过程只用到显卡的运算能力,没有用到CPU,所以CPU占有率不会提高,但对于显卡来说,要占用显卡的GPU和显存的带宽。显卡的性能就显得比较重要了。

      以下是实现代码:(由于商业原因,一些细节代码被取消了,但整体技术实现流程是完整的)

class CVideoDraw

{

LPDIRECTDRAW7           m_lpDD;    // DirectDraw 对象指针

LPDIRECTDRAWSURFACE7    m_lpDDSPrimary;  // DirectDraw 主表面指针

LPDIRECTDRAWSURFACE7    m_lpDDSOffScrYUV;  // DirectDraw 离屏表面指针

LPDIRECTDRAWSURFACE7    m_lpDDSOffScrRGB;  

DDSURFACEDESC2          m_ddsd;    // DirectDraw 表面描述

RECT                    m_rcDraw;

HWND                    m_hWnd;

bool InitDirectX(HWND hWnd, int nWidth, int nHeight);

void ClearDirectX();

bool Draw(LPBYTE pBuffer, int nWidth, int nHeight, int nSec);

};

bool CVideoDraw::InitDirectX(HWND hWnd, int nWidth, int nHeight) {
if (DirectDrawCreateEx(NULL, (LPVOID*)&m_lpDD, IID_IDirectDraw7, NULL) != DD_OK)
   return false;
  
if (m_lpDD->SetCooperativeLevel(hWnd, DDSCL_NORMAL) != DD_OK){
   ClearDirectX();
   return false;
}

ZeroMemory(&m_ddsd, sizeof(m_ddsd));
m_ddsd.dwSize = sizeof(m_ddsd);
m_ddsd.dwFlags = DDSD_CAPS;
m_ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
if (m_lpDD->CreateSurface(&m_ddsd, &m_lpDDSPrimary, NULL) != DD_OK){
   ClearDirectX();
   return false;
}

LPDIRECTDRAWCLIPPER pcClipper;
if( m_lpDD->CreateClipper(0, &pcClipper, NULL) != DD_OK) {
   ClearDirectX();
   return false;
}

if( pcClipper->SetHWnd(0, m_hWnd) != DD_OK) {
   ClearDirectX();
   return false;
}

if( m_lpDDSPrimary->SetClipper(pcClipper) != DD_OK) {
   ClearDirectX();
   return false;
}

pcClipper->Release();

// 创建离屏表面对象
ZeroMemory(&m_ddsd, sizeof(m_ddsd));
m_ddsd.dwSize = sizeof(m_ddsd);
m_ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN|DDSCAPS_VIDEOMEMORY ; // DDSCAPS_VIDEOMEMORY; //DDSCAPS_OVERLAY DDSCAPS_OFFSCREENPLAIN;
m_ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
m_ddsd.dwWidth = nWidth;
m_ddsd.dwHeight = nHeight;
m_ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
m_ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC | DDPF_YUV ;
m_ddsd.ddpfPixelFormat.dwFourCC = MAKEFOURCC('Y','V','1','2');
m_ddsd.ddpfPixelFormat.dwYUVBitCount = 8;
if (m_lpDD->CreateSurface(&m_ddsd, &m_lpDDSOffScrYUV, NULL) != DD_OK) {
   ClearDirectX();
   return false;
}

ZeroMemory(&m_ddsd, sizeof(m_ddsd));
m_ddsd.dwSize = sizeof(m_ddsd);
m_ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN|DDSCAPS_VIDEOMEMORY; // DDSCAPS_VIDEOMEMORY; //DDSCAPS_OVERLAY DDSCAPS_OFFSCREENPLAIN;
m_ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
m_ddsd.dwWidth = nWidth;
m_ddsd.dwHeight = nHeight;
if (m_lpDD->CreateSurface(&m_ddsd, &m_lpDDSOffScrRGB, NULL) != DD_OK) {
   ClearDirectX();
   return false;
}

return true;
}

void CVideoDraw::ClearDirectX() {
if( m_lpDD != NULL ){
   SAFE_RELEASES(m_lpDDSOffScrRGB);
   SAFE_RELEASES(m_lpDDSOffScrYUV);
   SAFE_RELEASES(m_lpDDSPrimary);
   SAFE_RELEASES(m_lpDD);
}
}

bool CVideoDraw::Draw(LPBYTE pBuffer, int nWidth, int nHeight, int nSec) {
HRESULT ddRval;
RECT rctDest;    // 目标区域
RECT rctSour;    // 源区域

for(int nTry=0; nTry < 5; nTry++){
   ddRval = m_lpDDSOffScrYUV->Lock(NULL,&m_ddsd, DDLOCK_WAIT|DDLOCK_WRITEONLY, NULL);
   if( ddRval == DDERR_SURFACELOST ) {
    ddRval = m_lpDDSOffScrYUV->Restore();
   }
   if( ddRval == DD_OK ){
    break;
   }
}

if( ddRval != DD_OK ) return false;

int i=0;
LPBYTE lpSurf = (LPBYTE)m_ddsd.lpSurface;
LPBYTE lpY = (LPBYTE)pBuffer;
LPBYTE lpV = (LPBYTE)(pBuffer + nWidth * nHeight);
LPBYTE lpU = (LPBYTE)(pBuffer + nWidth * nHeight * 5 / 4);

int nOffset = 0;
int value1 = 0 ;
int value2 = 0 ;
int value3 = 0 ;
int value4 = 0 ;

lpY += nOffset;
for(i=0; i<m_ddsd.dwHeight; i++)
{
   memcpy(lpSurf, lpY, m_ddsd.dwWidth);
   lpY += nWidth;
   lpSurf += m_ddsd.lPitch;
}

value1 = m_ddsd.dwHeight/2;
value2 = m_ddsd.dwWidth / 2;
value3 = nWidth / 2;
value4 = m_ddsd.lPitch / 2;

for(i=0; i<value1; i++)
{
   memcpy(lpSurf, lpU, value2);
   lpU += value3;
   lpSurf += value4;
}

for(i=0; i<value1; i++)
{
   memcpy(lpSurf, lpV, value2);
   lpV += value3;
   lpSurf += value4;
}
m_lpDDSOffScrYUV->Unlock(NULL);

SetRect(&rctDest, 0,0, nWidth, nHeight);
ddRval = m_lpDDSOffScrRGB->Blt(&rctDest, m_lpDDSOffScrYUV, NULL, DDBLT_WAIT, NULL);
if( ddRval != DD_OK)
   return false;

ddRval = m_lpDDSOffScrRGB->Lock(NULL,&m_ddsd, DDLOCK_WAIT|DDLOCK_WRITEONLY, NULL);
if( ddRval == DDERR_SURFACELOST )
   ddRval = m_lpDDSOffScrRGB->Restore();
  
if( ddRval != DD_OK )
   return false;

HDC hdc = NULL;
m_lpDDSOffScrRGB->GetDC(&hdc);
if( hdc )
{
   TCHAR szText[64];
   int thh = nSec/3600;
   int tmm = (nSec%3600)/60;
   int tss = nSec%60;
   wsprintf(szText, _T("%02d:%02d:%02d"), thh, tmm, tss);
   SetBkMode(hdc, TRANSPARENT);
   ::SetTextColor(hdc, RGB(255,0,0));
   TextOut(hdc, 1, 1, szText, wcslen(szText));
   m_lpDDSOffScrRGB->ReleaseDC(hdc);
}
m_lpDDSOffScrRGB->Unlock(NULL);

// Blt到主表面上
rctSour.left = 0;
rctSour.top = 0;
rctSour.right = m_ddsd.dwWidth;
rctSour.bottom = m_ddsd.dwHeight;
rctDest = m_rcDraw;
::ClientToScreen(m_hWnd, (LPPOINT)&rctDest.left);
::ClientToScreen(m_hWnd, (LPPOINT)&rctDest.right);

ddRval = m_lpDDSPrimary->Blt(&rctDest, m_lpDDSOffScrRGB, NULL, DDBLT_WAIT, NULL);

return (ddRval==DD_OK);
}

发表于 @ 2010年07月05日 10:53:00



总数:2 页次:1/1 首页 尾页  
总数:2 页次:1/1 首页 尾页  


所在合集/目录



发表评论:
文本/html模式切换 插入图片 文本/html模式切换


附件:



NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.

Copyright © 2005-2020 clq, All Rights Reserved
版权所有
桂ICP备15002303号-1