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

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

首先在.h中定义变量:

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

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

其次,添加函数SetVideoSize:

ContractedBlock.gif ExpandedBlockStart.gif 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的数据格式):

ContractedBlock.gif ExpandedBlockStart.gif SetYUVbuffer
 
   
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函数将图像显示出来:

ContractedBlock.gif ExpandedBlockStart.gif 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来输入视频数据即可。

转载于:https://www.cnblogs.com/yunsean/archive/2011/03/31/2000671.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值