分享

Windows下最简单的D3D视频显示(YV12)

 SamBookshelf 2013-08-19

很久没写日志了,很久没写代码了!

首先在.h中定义变量:

int m_nWidth;
int m_nHeight;//保存视频宽高信息
IDirect3D9 * m_pD3D;
IDirect3DDevice9
* m_pd3dDevice;
IDirect3DSurface9
* m_pd3dSurface;//D3D绘图用变量
CRect m_rtViewport;//视频显示区域(要保持宽高比)

要在.cpp中构造函数中初始化NULL,在析构函数或者反初始化函数中Release变量,这里略过!


其次,添加函数SetVideoSize:


void Cxxx::SetVideoSize(long lWidth,long lHeight)
{
m_nWidth
= lWidth;
m_nHeight
= lHeight;

if(m_pD3D != NULL)
{
m_pD3D
->Release();
m_pD3D
= NULL;
}
if(m_pd3dDevice != NULL)
{
m_pd3dDevice
->Release();
m_pd3dDevice
= NULL;
}
if(m_pd3dSurface != NULL)
{
m_pd3dSurface
->Release();
m_pd3dSurface
= NULL;
}

m_pD3D
= Direct3DCreate9( D3D_SDK_VERSION );
if( m_pD3D == NULL )
return;

D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(
&d3dpp, sizeof(d3dpp) );
d3dpp.Windowed
= TRUE;
d3dpp.SwapEffect
= D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat
= D3DFMT_UNKNOWN;

double dbAspect = (double)lWidth / lHeight;
CRect rtClient;

m_PresentWnd.GetClientRect(
&rtClient);
m_rtViewport
= rtClient;
if(rtClient.Width() > rtClient.Height() * dbAspect)
{
//width lager than height,adjust the width
int nValidW(static_cast<int>(rtClient.Height() * dbAspect));
int nLost(rtClient.Width() - nValidW);
m_rtViewport.left
+= nLost / 2;
m_rtViewport.right
= m_rtViewport.left + nValidW;
}
else
{
//height lager than width,adjust the height
int nValidH(static_cast<int>(rtClient.Width() / dbAspect));
int nLost(rtClient.Height() - nValidH);
m_rtViewport.top
+= nLost / 2;
m_rtViewport.bottom
= m_rtViewport.top + nValidH;
}

if( FAILED( m_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_PresentWnd.GetSafeHwnd(),
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &m_pd3dDevice ) ) )
return;

if( FAILED(m_pd3dDevice->CreateOffscreenPlainSurface(
lWidth,lHeight,
(D3DFORMAT)MAKEFOURCC(
'Y', 'V', '1', '2'),
D3DPOOL_DEFAULT,
&m_pd3dSurface,
NULL)))
return;
}

其中,m_PresentWnd为定义显示区域的窗口对象。 

再次添加SetYUVbuffer函数用于设置显示数据(这里只表达了显示YV12的数据格式):

void Cxxx::SetYUVbuffer(LPBYTE pBuffer,long lLen)
{
if(m_pd3dSurface == NULL)return;
D3DLOCKED_RECT d3d_rect;
if( FAILED(m_pd3dSurface->LockRect(&d3d_rect,NULL,D3DLOCK_DONOTWAIT)))
return ;

const int w = m_nWidth,h = m_nHeight;
BYTE
* const p = (BYTE *)d3d_rect.pBits;
const int stride = d3d_rect.Pitch;
int i = 0;
for(i = 0;i < h;i ++)
{
memcpy(p
+ i * stride,pBuffer + i * w, w);
}
for(i = 0;i < h/2;i ++)
{
memcpy(p
+ stride * h + i * stride / 2,pBuffer + w * h + w * h / 4 + i * w / 2, w / 2);
}
for(i = 0;i < h/2;i ++)
{
memcpy(p
+ stride * h + stride * h / 4 + i * stride / 2,pBuffer + w * h + i * w / 2, w / 2);
}

if( FAILED(m_pd3dSurface->UnlockRect()))
{
return ;
}

DrawImage();
}

注意:这里的函数参数pBuffer中所传递的YV12数据,其Pitch等于Width,并且YUV数据一次排列,无Padding数据。

最后添加函数DrawImage函数将图像显示出来:


void Cxxx::DrawImage()
{
if (m_pd3dDevice != NULL)
{
m_pd3dDevice
->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0 );
m_pd3dDevice
->BeginScene();
IDirect3DSurface9
* pBackBuffer = NULL;

m_pd3dDevice
->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,&pBackBuffer);
m_pd3dDevice
->StretchRect(m_pd3dSurface,NULL,pBackBuffer,&m_rtViewport,D3DTEXF_LINEAR);
m_pd3dDevice
->EndScene();
m_pd3dDevice
->Present( NULL, NULL, NULL, NULL );
}
}

至此,函数编码结束。

调用过程:

当图像画幅改变时,首先调用SerVideoSize设置新的图像画幅;

然后就定时的调用SetYUVBuffer来输入视频数据即可。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多