视频显示之Directdraw
日期:2014-09-25点击次数:8221
在监控平台中流的接收,传输,显示,存储这几大功能都缺一不可,由于最近工作中完成的是显示部分的接口功能,于是总结一下与大家分享,主要介绍其中的一种显示方法,即windows下的2D显示之Directdraw。
DirectDraw是DirectX中提供直接操纵显存、执行硬件映射、硬件覆盖及切换显示页等功能的组件。它不但兼容已有的Windows应用程序和驱动程序,而且还兼容许多显示卡:从简单的SVGA到支持图像剪裁、拉伸和非RGB格式图像的高档显示卡。DirectDrawHAL(hardware-abstraction layer)抽象了显示卡的硬件功能,以设备无关的方式提供了一些以前是设备相关的功能,如多显示页技术、访问并控制显示卡映射寄存器、支持3DZ-Buffer、支持Z-order硬件覆盖、访问图像拉伸硬件,以及同时访问标准显存和控制显存。正因为DirectDraw有这些优点,现在许多基于Windows95/98/NT的游戏程序都使用了DirectDraw;而它的设备无关性使开发者摆脱了繁重的显示卡接口工作,集中精力实现程序的主要功能。DirectX中的DDraw.dll实现了DirectDraw 所需要的函数、对象及其接口。
下图所示的是Directdraw的整个框架及大致的开发流程,后面部分是具体接口的调用:
按照上面的流程图,我整理出如下的调用步骤:
1. 在程序中添加ddraw.lib的库
2. 创建DirectDraw对象
LPDIRECTDRAW m_lpDD; // DirectDraw 对象指针
DirectDrawCreate(NULL, //代表显卡驱动程序的GUID,为NULL表示采样系统默认的驱动程序
&m_lpDD, //IDirectDraw接口对象
NULL);
3. 设置DirectDraw的工作模式
m_lpDD->SetCooperativeLevel(m_hWnd, //与DirectDraw对象关联的窗口句柄
DDSCL_NORMAL);//普通模式DDSCL_NORMAL,独占模式DDSCL_EXCLUSIVE
4. 创建物理屏幕(主表面)
简单的说DirectDraw表面是一个用于存储图像数据的线性内存区域,表面的大小是以象素为单位,用宽和高来定义,所以可以认为表面是一个用来画图的矩形区域。
主表面:每个DirectDraw都有一个主表面,主表面相当于用户的显示器,它的内容是可见的,是根据显示器的显示模式设定宽和高。
LPDIRECTDRAWSURFACE m_lpDDSPrimary; // DirectDraw 物理表面指针
DDSURFACEDESC ddsd;
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS ;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
ddsd.dwBackBufferCount = 1;
if (m_lpDD->CreateSurface(&ddsd, &m_lpDDSPrimary, NULL) != DD_OK) {
return FALSE;
}
5. 创建Clipper
IDirectDrawClipper* lpClipper = NULL;
HRESULT hRet = m_lpDD->CreateClipper(0, &lpClipper, NULL);
if (hRet != DD_OK) {
return FALSE;
}
hRet = lpClipper->SetHWnd(0, m_hWnd);
if (hRet != DD_OK) {
return FALSE;
}
hRet = m_lpDDSPrimary->SetClipper(lpClipper);
if (hRet != DD_OK) {
return FALSE;
}
6. 创建逻辑屏幕(离表面,从表面)
离表面:类似后缓冲区,不是紧挨着主表面,经常被用来存储位图。
DDSURFACEDESC ddsd;
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;//DDSCAPS_OVERLAY;
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
ddsd.dwWidth = width;
ddsd.dwHeight = height;
ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC | DDPF_YUV;
ddsd.ddpfPixelFormat.dwFourCC = MAKEFOURCC('Y','V', '1', '2');
ddsd.ddpfPixelFormat.dwYUVBitCount = 8;
HRESULT hRet;
LPDIRECTDRAWSURFACE m_lpDDSOverlay; // DirectDraw 离表面指针
if (FAILED(hRet = m_lpDD->CreateSurface(&ddsd, &m_lpDDSOverlay, NULL))) {
return FALSE;
}
7. 显示
HRESULT hRet; // DirectDraw 函¡¥数ºy返¤¦Ì回?值¦Ì
DDSURFACEDESC ddsd; // DirectDraw 表À¨ª面?描¨¨述º?
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
EnterCriticalSection(&m_cs);
// Lock down the surface so we can modify it's contents.
if (m_lpDDSOverlay) {
//将解码得到的YUV数据拷贝到YUV表面
hRet = m_lpDDSOverlay->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL);
if (FAILED(hRet))
return ;
int width = m_rcSrcFrame.right;
int height = m_rcSrcFrame.bottom;
LPBYTE lpSurf = (LPBYTE)ddsd.lpSurface;
LPBYTE lpY = buf; //Y数据位置
LPBYTE lpU = buf + width*height; //U数据位置
LPBYTE lpV = lpU + width*height/4; //V数据位置
//填充逻辑表面
if(lpSurf) {
int i;
for (i=0; i<height; i++){ // fill Y data
memcpy(lpSurf, lpY, ddsd.dwWidth);
lpY += width;
lpSurf += ddsd.lPitch;
}
for (i=0; i<height/2; i++){ // fill V data
memcpy(lpSurf, lpV, ddsd.dwWidth/2);
lpV += width/2;
lpSurf += ddsd.lPitch/2;
}
for (i=0; i<height/2; i++){ // fill U data
memcpy(lpSurf, lpU, ddsd.dwWidth/2);
lpU += width/2;
lpSurf += ddsd.lPitch/2;
}
}
m_lpDDSOverlay->Unlock(NULL);
GetClientRect(m_hWnd, &m_rcDst);
ClientToScreen(m_hWnd, (LPPOINT)&m_rcDst.left);
ClientToScreen(m_hWnd, (LPPOINT)&m_rcDst.right);
if (m_lpDDSPrimary) {
hRet = m_lpDDSPrimary->Blt(&m_rcDst, m_lpDDSOverlay, &m_rcSrcFrame, DDBLT_WAIT, NULL);
if (DDERR_SURFACELOST == hRet) {
DDrawDispReinit(m_hWnd, width, height);
}
m_lpDDSPrimary->Flip(NULL,0); //切换物理屏幕和逻辑屏幕
}
}
LeaveCriticalSection(&m_cs);
8.释放
释放所创建的DirectDraw对象,调用Release方法
Directdraw在显示视频的时候用的是双缓冲,及离表面和主表面(有的地方也称为物理表面和逻辑表面),双缓冲交替刷新,加速了这个过程的效率。
研发部 陈娟