SkeyePlayer依附D3DRender弱小的渲染能力咱们能够实现很多视频编辑性能,比方电子放大性能,本文将深刻D3DRender渲染引擎库代码,重点讲述其如何采纳surface离屏外表技术来实现渲染视频图像出现,以实现在surface上做电子放大缩略图显示等性能。
1. D3DRender初始化D3D创立设施
首先,咱们须要创立一个D3D9设施用于操作系统软硬件资源来为咱们的视频渲染服务,这个代码很简略,依照Direct3D教程即可实现,如下代码所示:
pD3D = Direct3DCreate9(D3D_SDK_VERSION); if (NULL == pD3D) { errCode = D3D_NOT_ENABLED; return false; } //获取显卡张数 int adaptnum = pD3D->GetAdapterCount(); //_TRACE("共[%d]张显卡.\n", adaptnum); //获取显卡反对的显示格局 if (FAILED(pD3D->GetAdapterDisplayMode(nAdapterNo, &d3dDisplayMode )) ) { __SAFE_RELEASE(pD3D); return false; } //_TRACE("显卡信息: %d X %d\tRefreshRate: %d\tFormat: %d\n", d3dDisplayMode.Width, d3dDisplayMode.Height, d3dDisplayMode.RefreshRate, d3dDisplayMode.Format); if (FAILED(pD3D->CheckDeviceFormat(nAdapterNo, D3DDEVTYPE_HAL, d3dDisplayMode.Format, 0, D3DRTYPE_SURFACE, d3dFormat))) { _TRACE("CheckDeviceFormat 不反对指定的格局..\n"); errCode = D3D_FORMAT_NOT_SUPPORT; __SAFE_RELEASE(pD3D); return false; } //查看输出格局到显示格局的转换是否反对 if ( FAILED(pD3D->CheckDeviceFormatConversion(nAdapterNo, D3DDEVTYPE_HAL, d3dFormat, d3dDisplayMode.Format) ) ) { errCode = D3D_FORMAT_NOT_SUPPORT; __SAFE_RELEASE(pD3D); return false; } //查看是否反对硬件顶点渲染形式 if ( FAILED(pD3D->GetDeviceCaps(nAdapterNo, D3DDEVTYPE_HAL, &d3dCaps) ) ) { errCode = D3D_VERTEX_HAL_NOT_SUPPORT; __SAFE_RELEASE(pD3D); return false; } //检测硬件是否反对变换和灯光 INT vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; if ( d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) { vp = D3DCREATE_HARDWARE_VERTEXPROCESSING; //硬件反对 } else { vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; //软件反对 } memset(&d3dParameters, 0, sizeof(D3DPRESENT_PARAMETERS)); D3DFORMAT d3dFormatRender = D3DFMT_UNKNOWN; d3dParameters.Windowed = TRUE; d3dParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;//D3DSWAPEFFECT_DISCARD;//D3DSWAPEFFECT_COPY;//D3DSWAPEFFECT_DISCARD; //如果想通过GetBackBuffer取得后备缓冲内容打印屏幕画面,则不能应用 D3DSWAPEFFECT_DISCARD d3dParameters.BackBufferFormat = d3dFormatRender;//D3DFMT_R5G6B5;//D3DFMT_R5G6B5;//D3DFMT_UNKNOWN; //应用桌面格局 d3dParameters.Flags = D3DPRESENTFLAG_VIDEO;//D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;//D3DPRESENTFLAG_VIDEO;// | D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; d3dParameters.BackBufferCount = 1; d3dParameters.BackBufferWidth = width; d3dParameters.BackBufferHeight = height; if (NULL == pD3dDevice) { HRESULT hr = pD3D->CreateDevice(nAdapterNo, D3DDEVTYPE_HAL, m_hWnd, vp, &d3dParameters, &pD3dDevice); if ( FAILED(hr) ) { _TRACE("D3D Create Device fail.."); switch (hr) { case D3DERR_DEVICELOST: { _TRACE("D3DERR_DEVICELOST\n"); } break; case D3DERR_INVALIDCALL: { _TRACE("D3DERR_INVALIDCALL\n"); } break; case D3DERR_NOTAVAILABLE: { _TRACE("D3DERR_NOTAVAILABLE\n"); } break; case D3DERR_OUTOFVIDEOMEMORY: { _TRACE("D3DERR_OUTOFVIDEOMEMORY\n"); } break; default: break; } errCode = D3D_DEVICE_CREATE_FAIL; __SAFE_RELEASE(pD3D); return false; } } if ( FAILED( pD3dDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pD3dBackBuffer ) ) ) { errCode = D3D_GETBACKBUFFER_FAIL; __SAFE_RELEASE(pD3dDevice); __SAFE_RELEASE(pD3D); return false; } if ( FAILED( pD3dBackBuffer->GetDesc( &surfaceDesc ) ) ) { errCode = D3D_GETBACKBUFFER_FAIL; __SAFE_RELEASE(pD3dDevice); __SAFE_RELEASE(pD3D); return false; }
如上代码所示,咱们依据须要渲染的视频长宽创立一个反对硬件加速D3D渲染设施,为视频图像渲染做筹备。
2. D3DRender创立离屏外表
2D图像渲染有两种形式,一种是是采纳纹理的形式加载,还有一张是通过surface离屏外表绘制的形式(相似于DDraw), 纹理加载形式较为简单,本文采纳比较简单surface外表绘制的形式渲染视频图像;首先,通过第一节中创立的D3D设施,在其后盾缓冲区(硬件加速相当于在显卡的显存中)咱们创立一个视频分辨率大小的离屏外表,代码如下:
HRESULT hr = pD3dDevice->CreateOffscreenPlainSurface( width, height, d3dFormat, D3DPOOL_DEFAULT, &d3d9SurfaceNode.pD3d9SurfaceNode[ch].pSurface, NULL); if (FAILED(hr)) { errCode = D3D_CREATESURFACE_FAIL; return NULL; } d3d9SurfaceNode.pD3d9SurfaceNode[ch].width = width; d3d9SurfaceNode.pD3d9SurfaceNode[ch].height = height;
3. D3DRender通过离屏外表渲染视频图像数据
创立好离屏外表(后文统称surface)后,咱们就能够将视频解码后的图像数据(YUV/RGB)填充到surface下面去,如下代码所示:
// 视频图像渲染 [4/14/2019 Dingshuai] if (NULL != pBuff) { //if (nSurfaceNum>1) EnterCriticalSection(&m_crit); //if ( FAILED( pD3d9Surface->pSurface->LockRect( &lockedRect, NULL, D3DLOCK_DONOTWAIT | D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD) ))//D3DLOCK_DISCARD) ))// D3DLOCK_NO_DIRTY_UPDATE | D3DLOCK_NOSYSLOCK | D3DLOCK_READONLY ) )) //if ( FAILED( pD3d9Surface->pSurface->LockRect( &lockedRect, NULL, D3DLOCK_NO_DIRTY_UPDATE | D3DLOCK_NOSYSLOCK | D3DLOCK_READONLY ) )) DWORD dwFlag = D3DLOCK_DONOTWAIT | D3DLOCK_DISCARD | D3DLOCK_NOSYSLOCK; //D3DERR_INVALIDCALL; //D3DERR_WASSTILLDRAWING; HRESULT hr = pD3d9Surface->pSurface->LockRect( &lockedRect, NULL, dwFlag);//D3DLOCK_DONOTWAIT | D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD); if ( FAILED( hr ))//D3DLOCK_DISCARD) ))// D3DLOCK_NO_DIRTY_UPDATE | D3DLOCK_NOSYSLOCK | D3DLOCK_READONLY ) )) { if (hr == D3DERR_INVALIDCALL) { errCode = D3D_LOCKSURFACE_FAIL; } else if (hr == D3DERR_WASSTILLDRAWING) { errCode = D3D_LOCKSURFACE_FAIL; } errCode = D3D_LOCKSURFACE_FAIL; //LeaveCriticalSection(&pD3d9Surface->crit); break; } FillDataToSurface((unsigned char *)lockedRect.pBits, pBuff, width, height, lockedRect.Pitch, d3dFormat); if ( FAILED (pD3d9Surface->pSurface->UnlockRect() )) { errCode = D3D_UNLOCKSURFACE_FAIL; //LeaveCriticalSection(&pD3d9Surface->crit); break; } }
向surface拷贝数据之前须要调用surface的LockRect函数锁住外表,免得呈现内存拜访抵触导致呈现未知的谬误。
surface填充数据当前,咱们须要把surface上填充的内容拷贝到后盾缓冲区中,而后做对立的渲染出现,如下代码所示:
// 应用自定义外表填充后盾缓存 RECT rcTmp; SetRect(&rcTmp, 0, 0, width, height); if ( FAILED(pD3dDevice->StretchRect(pD3d9Surface->pSurface, lpRectSrc/*&rcTmp*/, pD3dBackBuffer, &rcDst, D3DTEXF_NONE ) )) { errCode = D3D_UPDATESURFACE_FAIL; break; } HRESULT hr = 0; hr = pD3dDevice->Present(NULL, &rcDst, hWnd, NULL);
4. D3DRender电子放大性能实现
通过以上3节咱们根本曾经分明了整个D3DRender渲染的具体流程,上面咱们来实现电子放大性能;首先,咱们通过libSkeyePlayerPro_SetElectronicZoomStartPoint接口设置放大的起始点坐标,通过libSkeyePlayerPro_SetElectronicZoomEndPoint接口设置放大的起点坐标,从而确定放大的视频区域大小;显示线程通过坐标换算解决,获取到选区在视频上的坐标区域,而后填充数据选区的矩形大小,如下代码所示:
//电子放大 if ((pMediaChannel->pElectoricZoom) && (pMediaChannel->pElectoricZoom->zoomIn==0x01) ) { pChannelManager->ElectronicZoomProcess(pMediaChannel, &pMediaChannel->yuvFrame[iDisplayYuvIdx].frameinfo); if (pMediaChannel->mediaDisplay.renderFormat == RENDER_FORMAT_RGB24_GDI) { GDI_ResetDragPoint(pMediaChannel->mediaDisplay.d3dHandle); } else { D3D_ResetSelZone(pMediaChannel->mediaDisplay.d3dHandle); } } RECT rcSrc; RECT rcDst; if ( ! IsRectEmpty(&pMediaChannel->mediaDisplay.rcSrcRender)) { CopyRect(&rcSrc, &pMediaChannel->mediaDisplay.rcSrcRender); } else { SetRect(&rcSrc, 0, 0, width, height); }
在以上代码中,ElectronicZoomProcess函数负责把windows窗口坐标系转换成图像坐标系,而后将换算后的视频抉择矩形区域拷贝到源选区rcSrc矩形区域内;最初通过D3D_UpdateData接口传入做电子放大解决;咱们回顾第三节将传入的源选区视频图像拷贝到后盾缓冲中,这时候通过后盾缓冲渲染出现进去的视频就是咱们通过电子放大后的视频区域,为了不便观看,咱们将残缺的视频图像通过缩略图的形式绘制到右下角,如下代码所示:
bool bDrawThumnail = false; RECT rcSrc; SetRect(&rcSrc, 0, 0, width, height); if (!EqualRect(lpRectSrc, &rcSrc)) { RECT rcDst2; CopyRect(&rcDst2, lpRectDst); rcDst2.left = lpRectDst->right-(lpRectDst->right-lpRectDst->left)/4; rcDst2.top = lpRectDst->bottom-(lpRectDst->bottom-lpRectDst->top)/4; rcDst2.right -= 10; rcDst2.bottom-= 10; int tLeft = rcDst2.left+(int)(float)((float)(rcDst2.right-rcDst2.left)*((float)pD3d9Surface->pipRect.left/100.0f)); int tTop = rcDst2.top+(int)(float)((float)((rcDst2.bottom-rcDst2.top)*(float)pD3d9Surface->pipRect.top/100.0f)); int tRight = rcDst2.left+(int)(float)((float)(rcDst2.right-rcDst2.left)*((float)pD3d9Surface->pipRect.right/100.0f)); int tBottom = rcDst2.top+(int)(float)((float)((rcDst2.bottom-rcDst2.top)*(float)pD3d9Surface->pipRect.bottom/100.0f)); if ( FAILED(pD3dDevice->StretchRect(pD3d9Surface->pSurface, /*lpRectSrc*/&rcSrc, pD3dBackBuffer, &rcDst2, D3DTEXF_NONE ) )) { errCode = D3D_UPDATESURFACE_FAIL; //LeaveCriticalSection(&pD3d9Surface->crit); break; } bDrawThumnail = true; }
至此,电子放大性能就实现了,因为surface渲染形式的方便性,咱们简直不必批改几行代码即可实现这个看似很简单的性能,电子放大性能如下图所示: