我们如果是在在RGB视频上画图(直线,矩形等),一般采用双缓冲区继续,使用内存MemoryDC,来实现画的图形在视频上显示不闪烁的功能,但是我们知道用RGB显示视频都是使用GDI进行渲染,这样很耗CPU,那么我们能不能在YUV上进行视频渲染呢,答案是肯定的,使用ddraw直接显示yuv就ok了,可以支持yuv422和yuv420的直接使用显卡显示,不耗CPU,但是我们在使用ddraw显示时,然后在配合GDI画图(直线或矩形等),画的图形是闪烁的,原因是我们在ddraw直接显示yuv视频时,使用的是离屏表面的方法,将yuv数据拷贝到离屏表面,然后在blt到主表面,这样用gdi画图时,和视频刷新不同步,造成闪烁,那么我们怎么解决该问题呢?方法如下:
新增加一个离屏表面,我们定义成osd离屏表面吧,我们将yuv数据拷贝到离屏表面后,在将该离屏表面blt到osd离屏表面,然后在osd离屏表面上画直线或矩形,画完后在blt到主表面,这样画的图形就不会闪烁了。
直接上源码吧,注意,我没有对画图的部分进行封装,感兴趣的朋友可以自己封装;
- #ifndef _DIRECTDRAW_H_
- #define _DIRECTDRAW_H_
- #pragma once
- #include "ddraw.h"
-
- #define FOURCC_YUYV 0x32595559 // MAKEFOURCC( 'Y ', 'U ', 'Y ', '2 ')
- #define FOURCC_UYVY 0x59565955 // MAKEFOURCC( 'U ', 'Y ', 'V ', 'Y ')
-
- #define YUV_UYVY 1
- #define YUV_YUYV 2
-
- class __declspec(dllexport) CDirectDraw
- {
- public:
- CDirectDraw(void);
- ~CDirectDraw(void);
-
- bool DirectDrawInit(HWND hWnd, int width, int height,DWORD dwYuvFourCC);
- bool DisPlayYUVData(byte *pYUVData,int bYuvType,RECT rect);
- void DirectDrawDeInit(void);
- protected:
- LPDIRECTDRAW7 lpDD;
- LPDIRECTDRAWSURFACE7 lpDDSPrimary;
- LPDIRECTDRAWSURFACE7 lpDDSOffScr;
- DDSURFACEDESC2 ddsd;
-
- LPDIRECTDRAWSURFACE7 m_pOsdSurface;
-
- };
-
- #endif
DirectDraw.cpp 源文件如下:
- #include "StdAfx.h"
- #include "DirectDraw.h"
- #include <string>
- using namespace std;
-
- CDirectDraw::CDirectDraw(void)
- {
-
- lpDD=NULL;
- lpDDSPrimary=NULL;
- lpDDSOffScr=NULL;
- m_pOsdSurface=NULL;
-
- }
-
- CDirectDraw::~CDirectDraw(void)
- {
-
- DirectDrawDeInit();
- }
-
-
- bool CDirectDraw::DirectDrawInit(HWND hWnd, int width, int height,DWORD dwYuvFourCC)
- {
-
- HRESULT hr;
-
- if (DirectDrawCreateEx(NULL, (VOID**)&lpDD, IID_IDirectDraw7, NULL) != DD_OK)
- {
- return false;
- }
-
-
- if (lpDD->SetCooperativeLevel(hWnd,
- DDSCL_NORMAL | DDSCL_NOWINDOWCHANGES) != DD_OK)
- {
- return false;
- }
-
-
- LPVOID lpSurface = NULL;
- ZeroMemory(&ddsd, sizeof(ddsd));
- ZeroMemory(&ddsd.ddpfPixelFormat, sizeof(DDPIXELFORMAT));
- ddsd.dwSize = sizeof(ddsd);
- ddsd.dwFlags = DDSD_CAPS;
- ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
-
- if (lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL) != DD_OK)
- {
- return false;
- }
-
- LPDIRECTDRAWCLIPPER pcClipper;
- 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;
- }
-
-
- pcClipper->Release();
-
-
- ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY ;
- 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.dwYUVBitCount = 8;
- ddsd.ddpfPixelFormat.dwFourCC =dwYuvFourCC;
-
- hr=lpDD->CreateSurface(&ddsd, &lpDDSOffScr, NULL);
-
- if ( hr!= DD_OK)
- {
- return false;
- }
-
- #if 1
-
-
- ZeroMemory(&ddsd, sizeof(ddsd));
- ddsd.dwSize = sizeof(ddsd);
- ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
- ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
- ddsd.dwWidth = width;
- ddsd.dwHeight = height;
-
- hr = lpDD->CreateSurface(&ddsd, &m_pOsdSurface, NULL);
- if ( hr != DD_OK)
- {
-
-
-
- return false;
- }
-
- #endif
-
-
- return true;
- }
-
-
-
- bool CDirectDraw::DisPlayYUVData(byte *pYUVdata,int bYuvType,RECT rect)
- {
-
- byte *pSurf;
- int yuv_type=bYuvType;
-
- HRESULT hr;
- hr=lpDDSOffScr->Lock(NULL,&ddsd,DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT,NULL);
-
-
- if (hr==DDERR_SURFACELOST)
- {
- TRACE("off surface lost,restore offscr\n");
- hr=lpDDSOffScr->Restore();
- hr=lpDDSOffScr->Lock(NULL,&ddsd,DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT,NULL);
- }
-
- if (FAILED(hr))
- {
- return DD_FALSE;
- }
-
-
- if (yuv_type==YUV_UYVY)
- {
- ddsd.ddpfPixelFormat.dwFourCC =FOURCC_UYVY;
- }else
- {
- ddsd.ddpfPixelFormat.dwFourCC =FOURCC_YUYV;
- }
-
- pSurf=(LPBYTE)ddsd.lpSurface;
- if (pSurf)
- {
- for(unsigned int i=0; i < ddsd.dwHeight; i++)
- {
- memcpy(pSurf,pYUVdata,ddsd.dwWidth*2);
- pYUVdata+=ddsd.dwWidth*2;
- pSurf+=ddsd.lPitch;
- }
-
- }
-
-
- lpDDSOffScr->Unlock(NULL);
-
-
- #if 1
-
- HRESULT ddrval;
- ddrval = m_pOsdSurface->Blt(&rect, lpDDSOffScr, NULL, DDBLT_WAIT, NULL);
- if (ddrval != DD_OK)
- {
- ddrval = lpDDSPrimary->Blt(&rect, lpDDSOffScr, &rect, DDBLT_WAIT, NULL);
- }
- else
- {
- HDC hDC = NULL;
- ddrval = m_pOsdSurface->GetDC(&hDC);
- if ((ddrval == DD_OK)&&(hDC != NULL))
- {
-
-
- SetTextColor(hDC,RGB(255,0,0));
- SetBkColor(hDC,RGB(0,255,0));
-
- CString m_sOsdMsg=_T("hello world");
- TextOut(hDC, rect.left+100,rect.top+200 , m_sOsdMsg, m_sOsdMsg.GetLength());
-
-
- HPEN hpen = CreatePen (PS_SOLID, 1, RGB(255, 0, 0));
- SelectObject (hDC, hpen);
- HBRUSH hbrush = CreateSolidBrush (RGB(0, 255, 0));
- SelectObject (hDC, hbrush);
-
-
- Rectangle(hDC, rect.left+100, rect.top+100, rect.left+200, rect.top+200);
-
-
- RECT rect1;
- rect1.left=rect.left+200;
- rect1.top=rect.top+200;
- rect1.right=rect.left+300;
- rect1.bottom=rect.top+300;
-
- FrameRect(hDC,&rect1,CreateSolidBrush(RGB(255,0,0)));
-
-
- MoveToEx(hDC,rect.left+50,rect.top+50,NULL);
- LineTo(hDC,rect.left+350,rect.top+350);
-
- m_pOsdSurface->ReleaseDC(hDC);
- lpDDSPrimary->Blt(&rect, m_pOsdSurface, &rect, DDBLT_WAIT, NULL);
- }
- }
-
- #else
-
- HRESULT ddrval;
- ddrval=lpDDSPrimary->Blt(&rect, lpDDSOffScr, NULL, DDBLT_WAIT, NULL);
-
- if (ddrval==DDERR_SURFACELOST)
- {
- TRACE("primary surface lost,restore all surfaces\n");
- lpDDSPrimary->Restore();
- }
- #endif
-
-
- return DD_OK;
-
- }
-
-
- void CDirectDraw::DirectDrawDeInit(void)
- {
- if (lpDDSOffScr != NULL)
- {
- lpDDSOffScr->Release();
- lpDDSOffScr = NULL;
- }
-
- if (lpDDSPrimary != NULL)
- {
- lpDDSPrimary->Release();
- lpDDSPrimary = NULL;
- }
-
- if (lpDD != NULL)
- {
- lpDD->Release();
- lpDD = NULL;
- }
-
- if (m_pOsdSurface!=NULL)
- {
- m_pOsdSurface->Release();
- m_pOsdSurface=NULL;
- }
- }
测试结果如下:
ddraw gdi 画图知识如下:
由于DirectDraw并没有提供画点、线,圆等的语句,所以我们要借助Windows GDI函数来完成这些工作。就像输出文字时一样,我们先要获得页面的HDC:
HDC hdc;
lpDDSXXX->GetDC(&hdc);
画点是最简单的,SetPixel (hdc, x, y, RGB(r, g, b));即可在屏幕的(x,y)坐标处画上一个指定颜色的点。
如果需要画线等,我们需要创建"画笔":
HPEN hpen = CreatePen (PS_SOLID, 5, RGB(r, g, b));
CreatePen的第一个参数意义为画笔样式,常用的有PS_SOLID(普通画笔)和PS_DOT(由间断点组成的画笔,需要设置画笔宽度为1)。第二个参数是画笔的宽度,第三个参数是画笔的颜色。
接着将画笔给HDC:
SelectObject (hdc, hpen);
移动画笔到(x1,y1):
MoveToEx (hdc, x1, y1, NULL);
从画图起始位置向(x2,y2)坐标处画线:
LineTo (hdc, x2, y2);
下面列出一些常用的画图语句,使用方法和画线差不多,设定完画笔即可使用:
Rectangle(hdc, x1, y1, x2, y2); //画矩形
Ellipse(hdc, x1, y1, x2, y2); //画椭圆
值得注意的是我们画的图形将由一个"刷子"来填充,使用最简单的单色刷子的方法是:
HBRUSH hbrush = CreateSolidBrush (RGB(r, g, b));//创建刷子
SelectObject (hdc, hbrush); //使用刷子
画完后,我们要记住释放HDC:
lpDDSXXX->ReleaseDC(hdc);