使用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占用率降低了一半。
最新文章
- Caliburn.Micro学习笔记(五)----协同IResult
- 【原】iOS学习之Socket
- 纯CSS打造银色MacBook Air(完整版)
- StoreKit framework
- Delphi XE5 android popumenu
- SET NOCOUNT 的意义.
- org.eclipse.birt.report.data.oda.jdbc.JDBCException: Missing properties in Connection.open(Propertie
- 移动OA日程支持费用及评论
- 关于CSS格式与布局中的基础知识的简单操作
- WPF 10天修炼 第九天 - 几何图形
- MODIS数据的下载(新地址)
- Java多线程系列——计数器 CountDownLatch
- IntelliJ IDEA(九) :插件(转)
- Sql Server简单加密与解密 【转】
- 用CSS让字体在一行内显示不换行
- SWIFT中获取配置文件路径的方法
- gitlab安装教程、gitlab官网、英文文档
- python 中面向对象编程的几个概念
- iOS 数组集合操作(交集,并集,差集,子集)
- 【[ZJOI2005]午餐】
热门文章
- 面试中的Java链表
- typedef如何显示变量类型名
- pip相关工具使用小结
- Power shell 重启IIS
- 关于C#连接Oracle数据库 尝试加载Oracle客户端时引发BadImageFormatException 如果在安装32位Oracle客户端组件的情况下以64位模式运行,将出现此问题
- POJ 1681 Painter's Problem [高斯消元XOR]
- DNA序列组装(贪婪算法)
- qt程序启动画面
- [Python Study Notes]CS架构远程访问获取信息--Client端v1.0
- youtube视频字幕下载