Direct3D显示视频

来源:互联网 发布:试衣服的软件 编辑:程序博客网 时间:2024/05/21 11:35

Direct3D显示视频

 

对于视频的显示已经不推荐使用DirectDraw了,但可以使用Direct3D技术。对此,本人做了一些摸索之后,写了如下一个简单的例子,该例子创建Direct3D对象、Direct3D设备、Direct3D的RGB32纹理,然后装载一个位图并将位图数据更新纹理并显示。我们可以稍作修改,即可实现显示视频的包装类:创建D3DFMT_UYVY或D3DFMT_YUY2纹理,将解码的I420数据转换成对应的UYVY或YUY2格式,然后更新纹理。既然是例子,就要简单,因此有些细节及一些严密的判断没有实现。

 

#ifndef _BOFUL_DIRECT3D_HELPER_H_
#define _BOFUL_DIRECT3D_HELPER_H_


#include "d3dx9.h"
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")

#pragma pack(push, 1)

typedef struct
{
    FLOAT       x,y,z;      // vertex untransformed position
    FLOAT       rhw;        // eye distance
    D3DCOLOR    diffuse;    // diffuse color
    FLOAT       tu, tv;     // texture relative coordinates
} CUSTOMVERTEX;


#pragma pack(pop, 1)

class CD3DHelper
{
 
public: 
 CD3DHelper();
 ~CD3DHelper();

 unsigned long InitDirect3D( HWND hwnd, unsigned long lWidth, unsigned long lHeight );
 bool DrawImage(byte *pVideoBuff,int iVideoPitch);
 void ReleaseDirect3D();
 
private:
 void Lock();
 void UnLock();

private:
 CRITICAL_SECTION  m_critial;
 HWND     m_hVideoWnd;  // 视频窗口

private:
 LPDIRECT3D9    m_pDirect3D9;
 LPDIRECT3DDEVICE9  m_pDirect3DDevice;

 LPDIRECT3DSURFACE9  m_pDirect3DSurfaceRender;
 LPDIRECT3DTEXTURE9  m_pDirect3DTexture;
 LPDIRECT3DVERTEXBUFFER9 m_pDirect3DVertexBuffer;

 // 图像的大小
 unsigned long   m_lVideoWidth;
 unsigned long   m_lVideoHeight;
};

#endif //_BOFUL_DIRECT3D_HELPER_H_

 

// Custom flexible vertex format (FVF), which describes custom vertex structure
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }


CD3DHelper::CD3DHelper()
{
 InitializeCriticalSection(&m_critial);

 // Direct3D
 m_pDirect3D9    = NULL;
 m_pDirect3DDevice   = NULL;

 m_pDirect3DSurfaceRender = NULL;
 m_pDirect3DTexture   = NULL;
 m_pDirect3DVertexBuffer  = NULL;

 m_lVideoWidth  = 0;
 m_lVideoHeight  = 0;
}

CD3DHelper::~CD3DHelper()
{
 ReleaseDirect3D();
 DeleteCriticalSection(&m_critial);
}

void CD3DHelper::Lock()
{
 EnterCriticalSection(&m_critial);

}

void CD3DHelper::UnLock()
{
 LeaveCriticalSection(&m_critial);
}


void CD3DHelper::ReleaseDirect3D()
{
 Lock();
 SAFE_RELEASE(m_pDirect3DVertexBuffer);
 SAFE_RELEASE(m_pDirect3DTexture);
 SAFE_RELEASE(m_pDirect3DSurfaceRender);
 SAFE_RELEASE(m_pDirect3DDevice);
 SAFE_RELEASE(m_pDirect3D9);
 UnLock();
}


unsigned long CD3DHelper::InitDirect3D( HWND hwnd, unsigned long lWidth, unsigned long lHeight )
{
 HRESULT lRet;
 ReleaseDirect3D();
 Lock();
 // 创建Direct3D对象
 m_pDirect3D9 = Direct3DCreate9( D3D_SDK_VERSION );
 if ( m_pDirect3D9 == NULL )
 {
  UnLock();
  return 1;
 }

 if ( lWidth == 0 || lHeight == 0 )
 {
  CRect rt;
  GetClientRect( hwnd, &rt );
  lWidth = rt.Width();
  lHeight = rt.Height();
 }

 // 检查是否支持后台渲染和窗口的显示
 D3DDISPLAYMODE d3dDisplayMode;
 lRet = m_pDirect3D9->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3dDisplayMode );
 if ( FAILED(lRet) )
 {
  UnLock();
  return 1;
 }

 // 创建Direct3D设备
 D3DPRESENT_PARAMETERS d3dpp;
 ZeroMemory( &d3dpp, sizeof(d3dpp) );
 d3dpp.Flags = D3DPRESENTFLAG_VIDEO;
 d3dpp.Windowed   = TRUE;
 d3dpp.hDeviceWindow  = hwnd;
 d3dpp.BackBufferWidth = lWidth;   

 d3dpp.BackBufferHeight = lHeight;
 d3dpp.SwapEffect  = D3DSWAPEFFECT_COPY;
 d3dpp.MultiSampleType   = D3DMULTISAMPLE_NONE;
 d3dpp.PresentationInterval   = D3DPRESENT_INTERVAL_DEFAULT;
 d3dpp.BackBufferFormat = d3dDisplayMode.Format;
 d3dpp.BackBufferCount        = 1;
 d3dpp.EnableAutoDepthStencil = FALSE;
 
 m_hVideoWnd = hwnd;
 m_lVideoWidth = lWidth;
 m_lVideoHeight = lHeight;
 
 // 创建硬件渲染设备
 lRet = m_pDirect3D9->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, NULL,
  D3DCREATE_SOFTWARE_VERTEXPROCESSING|D3DCREATE_MULTITHREADED, &d3dpp, &m_pDirect3DDevice );
 
 // Texture coordinates outside the range [0.0, 1.0] are set
    // to the texture color at 0.0 or 1.0, respectively.
    IDirect3DDevice9_SetSamplerState(m_pDirect3DDevice, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
    IDirect3DDevice9_SetSamplerState(m_pDirect3DDevice, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
 
    // Set linear filtering quality
    IDirect3DDevice9_SetSamplerState(m_pDirect3DDevice, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
    IDirect3DDevice9_SetSamplerState(m_pDirect3DDevice, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
 
    // set maximum ambient light
    IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255));
 
    // Turn off culling
    IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_CULLMODE, D3DCULL_NONE);
 
    // Turn off the zbuffer
    IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_ZENABLE, D3DZB_FALSE);
 
    // Turn off lights
    IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_LIGHTING, FALSE);
 
    // Enable dithering
    IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_DITHERENABLE, TRUE);
 
    // disable stencil
    IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_STENCILENABLE, FALSE);
 
    // manage blending
    IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_ALPHABLENDENABLE, TRUE);
    IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
    IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
    IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_ALPHATESTENABLE,TRUE);
    IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_ALPHAREF, 0x10);
    IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_ALPHAFUNC,D3DCMP_GREATER);
 
    // Set texture states
    IDirect3DDevice9_SetTextureStageState(m_pDirect3DDevice, 0, D3DTSS_COLOROP,D3DTOP_MODULATE);
    IDirect3DDevice9_SetTextureStageState(m_pDirect3DDevice, 0, D3DTSS_COLORARG1,D3DTA_TEXTURE);
    IDirect3DDevice9_SetTextureStageState(m_pDirect3DDevice, 0, D3DTSS_COLORARG2,D3DTA_DIFFUSE);
 
    // turn off alpha operation
    IDirect3DDevice9_SetTextureStageState(m_pDirect3DDevice, 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);

 // 创建纹理
 lRet = m_pDirect3DDevice->CreateTexture(lWidth, lHeight, 1, D3DUSAGE_SOFTWAREPROCESSING,
           D3DFMT_X8R8G8B8,
           D3DPOOL_MANAGED,
           &m_pDirect3DTexture, NULL );
 if ( FAILED(lRet) )
 {
  TRACE( TEXT("创建纹理失败!返回值:0x%08x/n"), lRet );
  UnLock();
  return 1;
 }

 // 创建顶点缓冲区

 lRet = m_pDirect3DDevice->CreateVertexBuffer( 4 * sizeof(CUSTOMVERTEX),
  0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &m_pDirect3DVertexBuffer, NULL );
 if ( FAILED(lRet) )
 {
  UnLock();
  return 1;
 }

 // 填充顶点缓冲区

 CUSTOMVERTEX *pVertex;
 lRet = m_pDirect3DVertexBuffer->Lock( 0, 4 * sizeof(CUSTOMVERTEX), (void**)&pVertex, 0 );
 if ( FAILED(lRet) )
 {
  UnLock();
  return 1;
 }

 /* -0.5f is a "feature" of DirectX and it seems to apply to Direct3d also */
    pVertex[0].x       = -0.5f;       // left
    pVertex[0].y       = -0.5f;       // top
    pVertex[0].z       = 0.0f;
    pVertex[0].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
    pVertex[0].rhw     = 1.0f;
    pVertex[0].tu      = 0.0f;
    pVertex[0].tv      = 0.0f;
 
    pVertex[1].x       = lWidth - 0.5f;    // right
    pVertex[1].y       = -0.5f;       // top
    pVertex[1].z       = 0.0f;
    pVertex[1].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
    pVertex[1].rhw     = 1.0f;
    pVertex[1].tu      = 1.0f;
    pVertex[1].tv      = 0.0f;
 
    pVertex[2].x       = lWidth - 0.5f;    // right
    pVertex[2].y       = lHeight - 0.5f;   // bottom
    pVertex[2].z       = 0.0f;
    pVertex[2].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
    pVertex[2].rhw     = 1.0f;
    pVertex[2].tu      = 1.0f;
    pVertex[2].tv      = 1.0f;
 
    pVertex[3].x       = -0.5f;       // left
    pVertex[3].y       = lHeight - 0.5f;   // bottom
    pVertex[3].z       = 0.0f;
    pVertex[3].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
    pVertex[3].rhw     = 1.0f;
    pVertex[3].tu      = 0.0f;
    pVertex[3].tv      = 1.0f;

 m_pDirect3DVertexBuffer->Unlock();
 UnLock();
 return 0;
}

bool CD3DHelper::DrawImage(byte *pVideoBuff,int iVideoPitch)
{
 LRESULT lRet;
 if(pVideoBuff == NULL || m_pDirect3DDevice == NULL) return false;
 HRESULT hr;
 hr = IDirect3DDevice9_Clear(m_pDirect3DDevice, 0, NULL, D3DCLEAR_TARGET,
  D3DCOLOR_XRGB(255, 0, 0), 1.0f, 0);
 
 if ( FAILED(m_pDirect3DDevice->BeginScene()) )
 {
  return false;
 }

 lRet = m_pDirect3DDevice->SetTexture( 0, m_pDirect3DTexture );
 D3DLOCKED_RECT d3dLock;
 lRet = m_pDirect3DTexture->LockRect( 0, &d3dLock, 0, 0 );
 if ( SUCCEEDED(lRet) )
 {
  // 拷贝图像数据到纹理中
  byte *pSrc = pVideoBuff;
  byte *pDest = (byte *)d3dLock.pBits;
  for(unsigned long i=0; i< m_lVideoHeight; i++)
  {
   memcpy( pDest, pSrc, iVideoPitch );
   pDest += d3dLock.Pitch;
   pSrc += iVideoPitch;
  }
  
  m_pDirect3DTexture->UnlockRect( 0 );
 }

 m_pDirect3DDevice->SetStreamSource( 0, m_pDirect3DVertexBuffer,
  0, sizeof(CUSTOMVERTEX) );
 lRet = m_pDirect3DDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
 m_pDirect3DDevice->DrawPrimitive( D3DPT_TRIANGLEFAN, 0, 2 );
 m_pDirect3DDevice->EndScene();
 m_pDirect3DDevice->Present( NULL, NULL, NULL, NULL );
 return true;
}

上面的类非常简单,那如何使用呢?请参看如下代码:

CD3DHelper m_d3dHelper;

m_d3dHelper.InitDirect3D( GetSafeHwnd(), 720, 576 );

HDC hScreenDC = CreateDC( TEXT("DISPLAY"), NULL, NULL, NULL);
 if(!hScreenDC) return;
 HBITMAP hBmpPreview = (HBITMAP)LoadImage(AfxGetInstanceHandle(), TEXT("c://720_576.bmp"),IMAGE_BITMAP,0,0, LR_LOADFROMFILE);
 unsigned long lBufferSize = 720 * 576 * 4;
 byte *buff = new byte[lBufferSize];
 int iVideoPitch = 0;
 GetRGBFromBitmap(buff,hScreenDC,hBmpPreview,720,576,&lBufferSize,&iVideoPitch);
 
 if(hBmpPreview) DeleteObject(hBmpPreview);
 if(hScreenDC) DeleteDC(hScreenDC);
 if(buff) delete buff;
 m_d3dHelper.DrawImage( buff, iVideoPitch );

 m_d3dHelper.ReleaseDirect3D();

 

 此时则实现了初始化、装载位图、画位图、释放等功能。至于上面用到的GetRGBFromBitmap()函数见后面的代码:

 #define WIDTHBYTES(bits) ((DWORD)(((bits)+31) & (~31)) / 8)

// vertically flip a buffer
// note, this operates on a buffer of widthBytes bytes, not pixels!!!
BOOL VertFlipBuf(BYTE  * inbuf, UINT widthBytes, UINT height)
{  
 BYTE  *tb1;
 BYTE  *tb2;
 
 if (inbuf==NULL)
  return FALSE;
 
 UINT bufsize;
 
 bufsize=widthBytes;
 
 tb1= (BYTE *)new BYTE[bufsize];
 if (tb1==NULL) {
  return FALSE;
 }
 
 tb2= (BYTE *)new BYTE [bufsize];
 if (tb2==NULL) {
  delete [] tb1;
  return FALSE;
 }
 
 UINT row_cnt;    
 ULONG off1=0;
 ULONG off2=0;
 
 for (row_cnt=0;row_cnt<(height+1)/2;row_cnt++) {
  off1=row_cnt*bufsize;
  off2=((height-1)-row_cnt)*bufsize;  
  
  memcpy(tb1,inbuf+off1,bufsize);
  memcpy(tb2,inbuf+off2,bufsize); 
  memcpy(inbuf+off1,tb2,bufsize);
  memcpy(inbuf+off2,tb1,bufsize);
 } 
 delete [] tb1;
 delete [] tb2;
 return TRUE;

//根据位图句柄得到其位图数据的RGB,pBuffSize为以字节为单位的图像大小,这主要是考虑DWORD对齐的问题
BYTE * GetRGBFromBitmap(BYTE *buff,HDC hDC,HBITMAP hBitmap,int iWidth,int iHeight,unsigned long *pBuffSize,int *piPitch)
{
 if(buff == NULL) return NULL;
 BITMAPINFOHEADER bi;     //位图文件头结构
 bi.biSize = sizeof(BITMAPINFOHEADER);
 bi.biWidth = iWidth;
 bi.biHeight = iHeight;
 bi.biPlanes = 1;
 bi.biBitCount = 32;
 bi.biCompression = BI_RGB;
 bi.biSizeImage = 0;
 bi.biXPelsPerMeter = 0;
 bi.biYPelsPerMeter = 0;
 bi.biClrUsed = 0;
 bi.biClrImportant = 0;
 
 int lines = GetDIBits(hDC, hBitmap, 0, iHeight, buff, (LPBITMAPINFO)&bi, DIB_RGB_COLORS);
 
 //如果高度为正数,则DIB为从下到上的,此时需要垂直翻转数据
 iWidth = WIDTHBYTES(iWidth * 32) ; //借用iWidth用于存放DWORD对齐的宽度
 VertFlipBuf(buff,iWidth,iHeight);
 *pBuffSize = iWidth * abs(iHeight);
 *piPitch = iWidth;
 return buff;
}

 

要想看到运行的结果,需要设置系统的显示属性窗口中的“颜色质量”为“最高(32位)”(在桌面上单击鼠标右键,在弹出菜单中选择“属性”,此时就会出现“显示 属性”窗口),同时在c盘根目录下存在位图文件720_576.bmp,最后新建一个MFC的对话框程序,将上面的代码加入并调用显示图片。

 

 原创作者陈丹宇(http://hi.csdn.net/happyweb ),注明作者及出处,欢迎转载。