很久没写日志了,很久没写代码了!
首先在.h中定义变量:
变量声明
int
m_nWidth;
int m_nHeight; // 保存视频宽高信息
IDirect3D9 * m_pD3D;
IDirect3DDevice9 * m_pd3dDevice;
IDirect3DSurface9 * m_pd3dSurface; // D3D绘图用变量
CRect m_rtViewport; // 视频显示区域(要保持宽高比)
int m_nHeight; // 保存视频宽高信息
IDirect3D9 * m_pD3D;
IDirect3DDevice9 * m_pd3dDevice;
IDirect3DSurface9 * m_pd3dSurface; // D3D绘图用变量
CRect m_rtViewport; // 视频显示区域(要保持宽高比)
要在.cpp中构造函数中初始化NULL,在析构函数或者反初始化函数中Release变量,这里略过!
其次,添加函数SetVideoSize:
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_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的数据格式):
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();
}
{
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函数将图像显示出来:
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 );
}
}
{
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来输入视频数据即可。