| Index: webrtc/modules/video_render/windows/video_render_direct3d9.cc
|
| diff --git a/webrtc/modules/video_render/windows/video_render_direct3d9.cc b/webrtc/modules/video_render/windows/video_render_direct3d9.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b59b944e483405818db95708c5793ac8cdc28ede
|
| --- /dev/null
|
| +++ b/webrtc/modules/video_render/windows/video_render_direct3d9.cc
|
| @@ -0,0 +1,1160 @@
|
| +/*
|
| + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
| + *
|
| + * Use of this source code is governed by a BSD-style license
|
| + * that can be found in the LICENSE file in the root of the source
|
| + * tree. An additional intellectual property rights grant can be found
|
| + * in the file PATENTS. All contributing project authors may
|
| + * be found in the AUTHORS file in the root of the source tree.
|
| + */
|
| +
|
| +// Own include file
|
| +#include "webrtc/modules/video_render/windows/video_render_direct3d9.h"
|
| +
|
| +// System include files
|
| +#include <windows.h>
|
| +
|
| +// WebRtc include files
|
| +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
|
| +#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
|
| +#include "webrtc/system_wrappers/include/event_wrapper.h"
|
| +#include "webrtc/system_wrappers/include/trace.h"
|
| +
|
| +namespace webrtc {
|
| +
|
| +// A structure for our custom vertex type
|
| +struct CUSTOMVERTEX
|
| +{
|
| + FLOAT x, y, z;
|
| + DWORD color; // The vertex color
|
| + FLOAT u, v;
|
| +};
|
| +
|
| +// Our custom FVF, which describes our custom vertex structure
|
| +#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)
|
| +
|
| +/*
|
| + *
|
| + * D3D9Channel
|
| + *
|
| + */
|
| +D3D9Channel::D3D9Channel(LPDIRECT3DDEVICE9 pd3DDevice,
|
| + CriticalSectionWrapper* critSect,
|
| + Trace* trace) :
|
| + _width(0),
|
| + _height(0),
|
| + _pd3dDevice(pd3DDevice),
|
| + _pTexture(NULL),
|
| + _bufferIsUpdated(false),
|
| + _critSect(critSect),
|
| + _streamId(0),
|
| + _zOrder(0),
|
| + _startWidth(0),
|
| + _startHeight(0),
|
| + _stopWidth(0),
|
| + _stopHeight(0)
|
| +{
|
| +
|
| +}
|
| +
|
| +D3D9Channel::~D3D9Channel()
|
| +{
|
| + //release the texture
|
| + if (_pTexture != NULL)
|
| + {
|
| + _pTexture->Release();
|
| + _pTexture = NULL;
|
| + }
|
| +}
|
| +
|
| +void D3D9Channel::SetStreamSettings(uint16_t streamId,
|
| + uint32_t zOrder,
|
| + float startWidth,
|
| + float startHeight,
|
| + float stopWidth,
|
| + float stopHeight)
|
| +{
|
| + _streamId = streamId;
|
| + _zOrder = zOrder;
|
| + _startWidth = startWidth;
|
| + _startHeight = startHeight;
|
| + _stopWidth = stopWidth;
|
| + _stopHeight = stopHeight;
|
| +}
|
| +
|
| +int D3D9Channel::GetStreamSettings(uint16_t streamId,
|
| + uint32_t& zOrder,
|
| + float& startWidth,
|
| + float& startHeight,
|
| + float& stopWidth,
|
| + float& stopHeight)
|
| +{
|
| + streamId = _streamId;
|
| + zOrder = _zOrder;
|
| + startWidth = _startWidth;
|
| + startHeight = _startHeight;
|
| + stopWidth = _stopWidth;
|
| + stopHeight = _stopHeight;
|
| + return 0;
|
| +}
|
| +
|
| +int D3D9Channel::GetTextureWidth()
|
| +{
|
| + return _width;
|
| +}
|
| +
|
| +int D3D9Channel::GetTextureHeight()
|
| +{
|
| + return _height;
|
| +}
|
| +
|
| +// Called from video engine when a the frame size changed
|
| +int D3D9Channel::FrameSizeChange(int width, int height, int numberOfStreams)
|
| +{
|
| + WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
|
| + "FrameSizeChange, wifth: %d, height: %d, streams: %d", width,
|
| + height, numberOfStreams);
|
| +
|
| + CriticalSectionScoped cs(_critSect);
|
| + _width = width;
|
| + _height = height;
|
| +
|
| + //clean the previous texture
|
| + if (_pTexture != NULL)
|
| + {
|
| + _pTexture->Release();
|
| + _pTexture = NULL;
|
| + }
|
| +
|
| + HRESULT ret = E_POINTER;
|
| +
|
| + if (_pd3dDevice)
|
| + ret = _pd3dDevice->CreateTexture(_width, _height, 1, 0, D3DFMT_A8R8G8B8,
|
| + D3DPOOL_MANAGED, &_pTexture, NULL);
|
| +
|
| + if (FAILED(ret))
|
| + {
|
| + _pTexture = NULL;
|
| + return -1;
|
| + }
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int32_t D3D9Channel::RenderFrame(const uint32_t streamId,
|
| + const VideoFrame& videoFrame) {
|
| + CriticalSectionScoped cs(_critSect);
|
| + if (_width != videoFrame.width() || _height != videoFrame.height())
|
| + {
|
| + if (FrameSizeChange(videoFrame.width(), videoFrame.height(), 1) == -1)
|
| + {
|
| + return -1;
|
| + }
|
| + }
|
| + return DeliverFrame(videoFrame);
|
| +}
|
| +
|
| +// Called from video engine when a new frame should be rendered.
|
| +int D3D9Channel::DeliverFrame(const VideoFrame& videoFrame) {
|
| + WEBRTC_TRACE(kTraceStream, kTraceVideo, -1,
|
| + "DeliverFrame to D3D9Channel");
|
| +
|
| + CriticalSectionScoped cs(_critSect);
|
| +
|
| + // FIXME if _bufferIsUpdated is still true (not be renderred), do we want to
|
| + // update the texture? probably not
|
| + if (_bufferIsUpdated) {
|
| + WEBRTC_TRACE(kTraceStream, kTraceVideo, -1,
|
| + "Last frame hasn't been rendered yet. Drop this frame.");
|
| + return -1;
|
| + }
|
| +
|
| + if (!_pd3dDevice) {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "D3D for rendering not initialized.");
|
| + return -1;
|
| + }
|
| +
|
| + if (!_pTexture) {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "Texture for rendering not initialized.");
|
| + return -1;
|
| + }
|
| +
|
| + D3DLOCKED_RECT lr;
|
| +
|
| + if (FAILED(_pTexture->LockRect(0, &lr, NULL, 0))) {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "Failed to lock a texture in D3D9 Channel.");
|
| + return -1;
|
| + }
|
| + UCHAR* pRect = (UCHAR*) lr.pBits;
|
| +
|
| + ConvertFromI420(videoFrame, kARGB, 0, pRect);
|
| +
|
| + if (FAILED(_pTexture->UnlockRect(0))) {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "Failed to unlock a texture in D3D9 Channel.");
|
| + return -1;
|
| + }
|
| +
|
| + _bufferIsUpdated = true;
|
| + return 0;
|
| +}
|
| +
|
| +// Called by d3d channel owner to indicate the frame/texture has been rendered off
|
| +int D3D9Channel::RenderOffFrame()
|
| +{
|
| + WEBRTC_TRACE(kTraceStream, kTraceVideo, -1,
|
| + "Frame has been rendered to the screen.");
|
| + CriticalSectionScoped cs(_critSect);
|
| + _bufferIsUpdated = false;
|
| + return 0;
|
| +}
|
| +
|
| +// Called by d3d channel owner to check if the texture is updated
|
| +int D3D9Channel::IsUpdated(bool& isUpdated)
|
| +{
|
| + CriticalSectionScoped cs(_critSect);
|
| + isUpdated = _bufferIsUpdated;
|
| + return 0;
|
| +}
|
| +
|
| +// Called by d3d channel owner to get the texture
|
| +LPDIRECT3DTEXTURE9 D3D9Channel::GetTexture()
|
| +{
|
| + CriticalSectionScoped cs(_critSect);
|
| + return _pTexture;
|
| +}
|
| +
|
| +int D3D9Channel::ReleaseTexture()
|
| +{
|
| + CriticalSectionScoped cs(_critSect);
|
| +
|
| + //release the texture
|
| + if (_pTexture != NULL)
|
| + {
|
| + _pTexture->Release();
|
| + _pTexture = NULL;
|
| + }
|
| + _pd3dDevice = NULL;
|
| + return 0;
|
| +}
|
| +
|
| +int D3D9Channel::RecreateTexture(LPDIRECT3DDEVICE9 pd3DDevice)
|
| +{
|
| + CriticalSectionScoped cs(_critSect);
|
| +
|
| + _pd3dDevice = pd3DDevice;
|
| +
|
| + if (_pTexture != NULL)
|
| + {
|
| + _pTexture->Release();
|
| + _pTexture = NULL;
|
| + }
|
| +
|
| + HRESULT ret;
|
| +
|
| + ret = _pd3dDevice->CreateTexture(_width, _height, 1, 0, D3DFMT_A8R8G8B8,
|
| + D3DPOOL_MANAGED, &_pTexture, NULL);
|
| +
|
| + if (FAILED(ret))
|
| + {
|
| + _pTexture = NULL;
|
| + return -1;
|
| + }
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +/*
|
| + *
|
| + * VideoRenderDirect3D9
|
| + *
|
| + */
|
| +VideoRenderDirect3D9::VideoRenderDirect3D9(Trace* trace,
|
| + HWND hWnd,
|
| + bool fullScreen) :
|
| + _refD3DCritsect(*CriticalSectionWrapper::CreateCriticalSection()),
|
| + _trace(trace),
|
| + _hWnd(hWnd),
|
| + _fullScreen(fullScreen),
|
| + _pTextureLogo(NULL),
|
| + _pVB(NULL),
|
| + _pd3dDevice(NULL),
|
| + _pD3D(NULL),
|
| + _d3dChannels(),
|
| + _d3dZorder(),
|
| + _screenUpdateEvent(NULL),
|
| + _logoLeft(0),
|
| + _logoTop(0),
|
| + _logoRight(0),
|
| + _logoBottom(0),
|
| + _pd3dSurface(NULL),
|
| + _totalMemory(0),
|
| + _availableMemory(0)
|
| +{
|
| + _screenUpdateThread.reset(new rtc::PlatformThread(
|
| + ScreenUpdateThreadProc, this, "ScreenUpdateThread"));
|
| + _screenUpdateEvent = EventTimerWrapper::Create();
|
| + SetRect(&_originalHwndRect, 0, 0, 0, 0);
|
| +}
|
| +
|
| +VideoRenderDirect3D9::~VideoRenderDirect3D9()
|
| +{
|
| + //NOTE: we should not enter CriticalSection in here!
|
| +
|
| + // Signal event to exit thread, then delete it
|
| + rtc::PlatformThread* tmpPtr = _screenUpdateThread.release();
|
| + if (tmpPtr)
|
| + {
|
| + _screenUpdateEvent->Set();
|
| + _screenUpdateEvent->StopTimer();
|
| +
|
| + tmpPtr->Stop();
|
| + delete tmpPtr;
|
| + }
|
| + delete _screenUpdateEvent;
|
| +
|
| + //close d3d device
|
| + CloseDevice();
|
| +
|
| + // Delete all channels
|
| + std::map<int, D3D9Channel*>::iterator it = _d3dChannels.begin();
|
| + while (it != _d3dChannels.end())
|
| + {
|
| + delete it->second;
|
| + it = _d3dChannels.erase(it);
|
| + }
|
| + // Clean the zOrder map
|
| + _d3dZorder.clear();
|
| +
|
| + if (_fullScreen)
|
| + {
|
| + // restore hwnd to original size and position
|
| + ::SetWindowPos(_hWnd, HWND_NOTOPMOST, _originalHwndRect.left,
|
| + _originalHwndRect.top, _originalHwndRect.right
|
| + - _originalHwndRect.left,
|
| + _originalHwndRect.bottom - _originalHwndRect.top,
|
| + SWP_FRAMECHANGED);
|
| + ::RedrawWindow(_hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW
|
| + | RDW_ERASE);
|
| + ::RedrawWindow(NULL, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW
|
| + | RDW_ERASE);
|
| + }
|
| +
|
| + delete &_refD3DCritsect;
|
| +}
|
| +
|
| +DWORD VideoRenderDirect3D9::GetVertexProcessingCaps()
|
| +{
|
| + D3DCAPS9 caps;
|
| + DWORD dwVertexProcessing = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
|
| + if (SUCCEEDED(_pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
|
| + &caps)))
|
| + {
|
| + if ((caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
|
| + == D3DDEVCAPS_HWTRANSFORMANDLIGHT)
|
| + {
|
| + dwVertexProcessing = D3DCREATE_HARDWARE_VERTEXPROCESSING;
|
| + }
|
| + }
|
| + return dwVertexProcessing;
|
| +}
|
| +
|
| +int VideoRenderDirect3D9::InitializeD3D(HWND hWnd,
|
| + D3DPRESENT_PARAMETERS* pd3dpp)
|
| +{
|
| + // initialize Direct3D
|
| + if (NULL == (_pD3D = Direct3DCreate9(D3D_SDK_VERSION)))
|
| + {
|
| + return -1;
|
| + }
|
| +
|
| + // determine what type of vertex processing to use based on the device capabilities
|
| + DWORD dwVertexProcessing = GetVertexProcessingCaps();
|
| +
|
| + // get the display mode
|
| + D3DDISPLAYMODE d3ddm;
|
| + _pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
|
| + pd3dpp->BackBufferFormat = d3ddm.Format;
|
| +
|
| + // create the D3D device
|
| + if (FAILED(_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
|
| + dwVertexProcessing | D3DCREATE_MULTITHREADED
|
| + | D3DCREATE_FPU_PRESERVE, pd3dpp,
|
| + &_pd3dDevice)))
|
| + {
|
| + //try the ref device
|
| + if (FAILED(_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF,
|
| + hWnd, dwVertexProcessing
|
| + | D3DCREATE_MULTITHREADED
|
| + | D3DCREATE_FPU_PRESERVE,
|
| + pd3dpp, &_pd3dDevice)))
|
| + {
|
| + return -1;
|
| + }
|
| + }
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int VideoRenderDirect3D9::ResetDevice()
|
| +{
|
| + WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
|
| + "VideoRenderDirect3D9::ResetDevice");
|
| +
|
| + CriticalSectionScoped cs(&_refD3DCritsect);
|
| +
|
| + //release the channel texture
|
| + std::map<int, D3D9Channel*>::iterator it;
|
| + it = _d3dChannels.begin();
|
| + while (it != _d3dChannels.end())
|
| + {
|
| + if (it->second)
|
| + {
|
| + it->second->ReleaseTexture();
|
| + }
|
| + it++;
|
| + }
|
| +
|
| + //close d3d device
|
| + if (CloseDevice() != 0)
|
| + {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "VideoRenderDirect3D9::ResetDevice failed to CloseDevice");
|
| + return -1;
|
| + }
|
| +
|
| + //reinit d3d device
|
| + if (InitDevice() != 0)
|
| + {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "VideoRenderDirect3D9::ResetDevice failed to InitDevice");
|
| + return -1;
|
| + }
|
| +
|
| + //recreate channel texture
|
| + it = _d3dChannels.begin();
|
| + while (it != _d3dChannels.end())
|
| + {
|
| + if (it->second)
|
| + {
|
| + it->second->RecreateTexture(_pd3dDevice);
|
| + }
|
| + it++;
|
| + }
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int VideoRenderDirect3D9::InitDevice()
|
| +{
|
| + // Set up the structure used to create the D3DDevice
|
| + ZeroMemory(&_d3dpp, sizeof(_d3dpp));
|
| + _d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
| + _d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
|
| + if (GetWindowRect(_hWnd, &_originalHwndRect) == 0)
|
| + {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "VideoRenderDirect3D9::InitDevice Could not get window size");
|
| + return -1;
|
| + }
|
| + if (!_fullScreen)
|
| + {
|
| + _winWidth = _originalHwndRect.right - _originalHwndRect.left;
|
| + _winHeight = _originalHwndRect.bottom - _originalHwndRect.top;
|
| + _d3dpp.Windowed = TRUE;
|
| + _d3dpp.BackBufferHeight = 0;
|
| + _d3dpp.BackBufferWidth = 0;
|
| + }
|
| + else
|
| + {
|
| + _winWidth = (LONG) ::GetSystemMetrics(SM_CXSCREEN);
|
| + _winHeight = (LONG) ::GetSystemMetrics(SM_CYSCREEN);
|
| + _d3dpp.Windowed = FALSE;
|
| + _d3dpp.BackBufferWidth = _winWidth;
|
| + _d3dpp.BackBufferHeight = _winHeight;
|
| + _d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
|
| + }
|
| +
|
| + if (InitializeD3D(_hWnd, &_d3dpp) == -1)
|
| + {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "VideoRenderDirect3D9::InitDevice failed in InitializeD3D");
|
| + return -1;
|
| + }
|
| +
|
| + // Turn off culling, so we see the front and back of the triangle
|
| + _pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
| +
|
| + // Turn off D3D lighting, since we are providing our own vertex colors
|
| + _pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
|
| +
|
| + // Settings for alpha blending
|
| + _pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
|
| + _pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
| + _pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
| +
|
| + _pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
|
| + _pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
|
| + _pd3dDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
|
| +
|
| + // Initialize Vertices
|
| + CUSTOMVERTEX Vertices[] = {
|
| + //front
|
| + { -1.0f, -1.0f, 0.0f, 0xffffffff, 0, 1 }, { -1.0f, 1.0f, 0.0f,
|
| + 0xffffffff, 0, 0 },
|
| + { 1.0f, -1.0f, 0.0f, 0xffffffff, 1, 1 }, { 1.0f, 1.0f, 0.0f,
|
| + 0xffffffff, 1, 0 } };
|
| +
|
| + // Create the vertex buffer.
|
| + if (FAILED(_pd3dDevice->CreateVertexBuffer(sizeof(Vertices), 0,
|
| + D3DFVF_CUSTOMVERTEX,
|
| + D3DPOOL_DEFAULT, &_pVB, NULL )))
|
| + {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "Failed to create the vertex buffer.");
|
| + return -1;
|
| + }
|
| +
|
| + // Now we fill the vertex buffer.
|
| + VOID* pVertices;
|
| + if (FAILED(_pVB->Lock(0, sizeof(Vertices), (void**) &pVertices, 0)))
|
| + {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "Failed to lock the vertex buffer.");
|
| + return -1;
|
| + }
|
| + memcpy(pVertices, Vertices, sizeof(Vertices));
|
| + _pVB->Unlock();
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int32_t VideoRenderDirect3D9::Init()
|
| +{
|
| + WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
|
| + "VideoRenderDirect3D9::Init");
|
| +
|
| + CriticalSectionScoped cs(&_refD3DCritsect);
|
| +
|
| + // Start rendering thread...
|
| + if (!_screenUpdateThread)
|
| + {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Thread not created");
|
| + return -1;
|
| + }
|
| + _screenUpdateThread->Start();
|
| + _screenUpdateThread->SetPriority(rtc::kRealtimePriority);
|
| +
|
| + // Start the event triggering the render process
|
| + unsigned int monitorFreq = 60;
|
| + DEVMODE dm;
|
| + // initialize the DEVMODE structure
|
| + ZeroMemory(&dm, sizeof(dm));
|
| + dm.dmSize = sizeof(dm);
|
| + if (0 != EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm))
|
| + {
|
| + monitorFreq = dm.dmDisplayFrequency;
|
| + }
|
| + _screenUpdateEvent->StartTimer(true, 1000 / monitorFreq);
|
| +
|
| + return InitDevice();
|
| +}
|
| +
|
| +int32_t VideoRenderDirect3D9::ChangeWindow(void* window)
|
| +{
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
|
| + return -1;
|
| +}
|
| +
|
| +int VideoRenderDirect3D9::UpdateRenderSurface()
|
| +{
|
| + CriticalSectionScoped cs(&_refD3DCritsect);
|
| +
|
| + // Check if there are any updated buffers
|
| + bool updated = false;
|
| + std::map<int, D3D9Channel*>::iterator it;
|
| + it = _d3dChannels.begin();
|
| + while (it != _d3dChannels.end())
|
| + {
|
| +
|
| + D3D9Channel* channel = it->second;
|
| + channel->IsUpdated(updated);
|
| + if (updated)
|
| + {
|
| + break;
|
| + }
|
| + it++;
|
| + }
|
| + //nothing is updated, continue
|
| + if (!updated)
|
| + return -1;
|
| +
|
| + // Clear the backbuffer to a black color
|
| + _pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f,
|
| + 0);
|
| +
|
| + // Begin the scene
|
| + if (SUCCEEDED(_pd3dDevice->BeginScene()))
|
| + {
|
| + _pd3dDevice->SetStreamSource(0, _pVB, 0, sizeof(CUSTOMVERTEX));
|
| + _pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
|
| +
|
| + //draw all the channels
|
| + //get texture from the channels
|
| + LPDIRECT3DTEXTURE9 textureFromChannel = NULL;
|
| + DWORD textureWidth, textureHeight;
|
| +
|
| + std::multimap<int, unsigned int>::reverse_iterator it;
|
| + it = _d3dZorder.rbegin();
|
| + while (it != _d3dZorder.rend())
|
| + {
|
| + // loop through all channels and streams in Z order
|
| + int channel = it->second & 0x0000ffff;
|
| +
|
| + std::map<int, D3D9Channel*>::iterator ddIt;
|
| + ddIt = _d3dChannels.find(channel);
|
| + if (ddIt != _d3dChannels.end())
|
| + {
|
| + // found the channel
|
| + D3D9Channel* channelObj = ddIt->second;
|
| + if (channelObj)
|
| + {
|
| + textureFromChannel = channelObj->GetTexture();
|
| + textureWidth = channelObj->GetTextureWidth();
|
| + textureHeight = channelObj->GetTextureHeight();
|
| +
|
| + uint32_t zOrder;
|
| + float startWidth, startHeight, stopWidth, stopHeight;
|
| + channelObj->GetStreamSettings(0, zOrder, startWidth,
|
| + startHeight, stopWidth,
|
| + stopHeight);
|
| +
|
| + //draw the video stream
|
| + UpdateVerticeBuffer(_pVB, 0, startWidth, startHeight,
|
| + stopWidth, stopHeight);
|
| + _pd3dDevice->SetTexture(0, textureFromChannel);
|
| + _pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
|
| +
|
| + //Notice channel that this frame as been rendered
|
| + channelObj->RenderOffFrame();
|
| + }
|
| + }
|
| + it++;
|
| + }
|
| +
|
| + //draw the logo
|
| + if (_pTextureLogo)
|
| + {
|
| + UpdateVerticeBuffer(_pVB, 0, _logoLeft, _logoTop, _logoRight,
|
| + _logoBottom);
|
| + _pd3dDevice->SetTexture(0, _pTextureLogo);
|
| + _pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
|
| + }
|
| +
|
| + // End the scene
|
| + _pd3dDevice->EndScene();
|
| + }
|
| +
|
| + // Present the backbuffer contents to the display
|
| + _pd3dDevice->Present(NULL, NULL, NULL, NULL );
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +//set the alpha value of the pixal with a particular colorkey as 0
|
| +int VideoRenderDirect3D9::SetTransparentColor(LPDIRECT3DTEXTURE9 pTexture,
|
| + DDCOLORKEY* transparentColorKey,
|
| + DWORD width,
|
| + DWORD height)
|
| +{
|
| + D3DLOCKED_RECT lr;
|
| + if (!pTexture)
|
| + return -1;
|
| +
|
| + CriticalSectionScoped cs(&_refD3DCritsect);
|
| + if (SUCCEEDED(pTexture->LockRect(0, &lr, NULL, D3DLOCK_DISCARD)))
|
| + {
|
| + for (DWORD y = 0; y < height; y++)
|
| + {
|
| + DWORD dwOffset = y * width;
|
| +
|
| + for (DWORD x = 0; x < width; x)
|
| + {
|
| + DWORD temp = ((DWORD*) lr.pBits)[dwOffset + x];
|
| + if ((temp & 0x00FFFFFF)
|
| + == transparentColorKey->dwColorSpaceLowValue)
|
| + {
|
| + temp &= 0x00FFFFFF;
|
| + }
|
| + else
|
| + {
|
| + temp |= 0xFF000000;
|
| + }
|
| + ((DWORD*) lr.pBits)[dwOffset + x] = temp;
|
| + x++;
|
| + }
|
| + }
|
| + pTexture->UnlockRect(0);
|
| + return 0;
|
| + }
|
| + return -1;
|
| +}
|
| +
|
| +/*
|
| + *
|
| + * Rendering process
|
| + *
|
| + */
|
| +bool VideoRenderDirect3D9::ScreenUpdateThreadProc(void* obj)
|
| +{
|
| + return static_cast<VideoRenderDirect3D9*> (obj)->ScreenUpdateProcess();
|
| +}
|
| +
|
| +bool VideoRenderDirect3D9::ScreenUpdateProcess()
|
| +{
|
| + _screenUpdateEvent->Wait(100);
|
| +
|
| + if (!_screenUpdateThread)
|
| + {
|
| + //stop the thread
|
| + return false;
|
| + }
|
| + if (!_pd3dDevice)
|
| + {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "d3dDevice not created.");
|
| + return true;
|
| + }
|
| +
|
| + HRESULT hr = _pd3dDevice->TestCooperativeLevel();
|
| +
|
| + if (SUCCEEDED(hr))
|
| + {
|
| + UpdateRenderSurface();
|
| + }
|
| +
|
| + if (hr == D3DERR_DEVICELOST)
|
| + {
|
| + //Device is lost and cannot be reset yet
|
| +
|
| + }
|
| + else if (hr == D3DERR_DEVICENOTRESET)
|
| + {
|
| + //Lost but we can reset it now
|
| + //Note: the standard way is to call Reset, however for some reason doesn't work here.
|
| + //so we will release the device and create it again.
|
| + ResetDevice();
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +int VideoRenderDirect3D9::CloseDevice()
|
| +{
|
| + CriticalSectionScoped cs(&_refD3DCritsect);
|
| + WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
|
| + "VideoRenderDirect3D9::CloseDevice");
|
| +
|
| + if (_pTextureLogo != NULL)
|
| + {
|
| + _pTextureLogo->Release();
|
| + _pTextureLogo = NULL;
|
| + }
|
| +
|
| + if (_pVB != NULL)
|
| + {
|
| + _pVB->Release();
|
| + _pVB = NULL;
|
| + }
|
| +
|
| + if (_pd3dDevice != NULL)
|
| + {
|
| + _pd3dDevice->Release();
|
| + _pd3dDevice = NULL;
|
| + }
|
| +
|
| + if (_pD3D != NULL)
|
| + {
|
| + _pD3D->Release();
|
| + _pD3D = NULL;
|
| + }
|
| +
|
| + if (_pd3dSurface != NULL)
|
| + _pd3dSurface->Release();
|
| + return 0;
|
| +}
|
| +
|
| +D3D9Channel* VideoRenderDirect3D9::GetD3DChannel(int channel)
|
| +{
|
| + std::map<int, D3D9Channel*>::iterator ddIt;
|
| + ddIt = _d3dChannels.find(channel & 0x0000ffff);
|
| + D3D9Channel* ddobj = NULL;
|
| + if (ddIt != _d3dChannels.end())
|
| + {
|
| + ddobj = ddIt->second;
|
| + }
|
| + if (ddobj == NULL)
|
| + {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "Direct3D render failed to find channel");
|
| + return NULL;
|
| + }
|
| + return ddobj;
|
| +}
|
| +
|
| +int32_t VideoRenderDirect3D9::DeleteChannel(const uint32_t streamId)
|
| +{
|
| + CriticalSectionScoped cs(&_refD3DCritsect);
|
| +
|
| +
|
| + std::multimap<int, unsigned int>::iterator it;
|
| + it = _d3dZorder.begin();
|
| + while (it != _d3dZorder.end())
|
| + {
|
| + if ((streamId & 0x0000ffff) == (it->second & 0x0000ffff))
|
| + {
|
| + it = _d3dZorder.erase(it);
|
| + break;
|
| + }
|
| + it++;
|
| + }
|
| +
|
| + std::map<int, D3D9Channel*>::iterator ddIt;
|
| + ddIt = _d3dChannels.find(streamId & 0x0000ffff);
|
| + if (ddIt != _d3dChannels.end())
|
| + {
|
| + delete ddIt->second;
|
| + _d3dChannels.erase(ddIt);
|
| + return 0;
|
| + }
|
| + return -1;
|
| +}
|
| +
|
| +VideoRenderCallback* VideoRenderDirect3D9::CreateChannel(const uint32_t channel,
|
| + const uint32_t zOrder,
|
| + const float left,
|
| + const float top,
|
| + const float right,
|
| + const float bottom)
|
| +{
|
| + CriticalSectionScoped cs(&_refD3DCritsect);
|
| +
|
| + //FIXME this should be done in VideoAPIWindows? stop the frame deliver first
|
| + //remove the old channel
|
| + DeleteChannel(channel);
|
| +
|
| + D3D9Channel* d3dChannel = new D3D9Channel(_pd3dDevice,
|
| + &_refD3DCritsect, _trace);
|
| + d3dChannel->SetStreamSettings(0, zOrder, left, top, right, bottom);
|
| +
|
| + // store channel
|
| + _d3dChannels[channel & 0x0000ffff] = d3dChannel;
|
| +
|
| + // store Z order
|
| + // default streamID is 0
|
| + _d3dZorder.insert(
|
| + std::pair<int, unsigned int>(zOrder, channel & 0x0000ffff));
|
| +
|
| + return d3dChannel;
|
| +}
|
| +
|
| +int32_t VideoRenderDirect3D9::GetStreamSettings(const uint32_t channel,
|
| + const uint16_t streamId,
|
| + uint32_t& zOrder,
|
| + float& left, float& top,
|
| + float& right, float& bottom)
|
| +{
|
| + std::map<int, D3D9Channel*>::iterator ddIt;
|
| + ddIt = _d3dChannels.find(channel & 0x0000ffff);
|
| + D3D9Channel* ddobj = NULL;
|
| + if (ddIt != _d3dChannels.end())
|
| + {
|
| + ddobj = ddIt->second;
|
| + }
|
| + if (ddobj == NULL)
|
| + {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "Direct3D render failed to find channel");
|
| + return -1;
|
| + }
|
| + // Only allow one stream per channel, demuxing is
|
| + return ddobj->GetStreamSettings(0, zOrder, left, top, right, bottom);
|
| +}
|
| +
|
| +int VideoRenderDirect3D9::UpdateVerticeBuffer(LPDIRECT3DVERTEXBUFFER9 pVB,
|
| + int offset,
|
| + float startWidth,
|
| + float startHeight,
|
| + float stopWidth,
|
| + float stopHeight)
|
| +{
|
| + if (pVB == NULL)
|
| + return -1;
|
| +
|
| + float left, right, top, bottom;
|
| +
|
| + //update the vertice buffer
|
| + //0,1 => -1,1
|
| + left = startWidth * 2 - 1;
|
| + right = stopWidth * 2 - 1;
|
| +
|
| + //0,1 => 1,-1
|
| + top = 1 - startHeight * 2;
|
| + bottom = 1 - stopHeight * 2;
|
| +
|
| + CUSTOMVERTEX newVertices[] = {
|
| + //logo
|
| + { left, bottom, 0.0f, 0xffffffff, 0, 1 }, { left, top, 0.0f,
|
| + 0xffffffff, 0, 0 },
|
| + { right, bottom, 0.0f, 0xffffffff, 1, 1 }, { right, top, 0.0f,
|
| + 0xffffffff, 1, 0 }, };
|
| + // Now we fill the vertex buffer.
|
| + VOID* pVertices;
|
| + if (FAILED(pVB->Lock(sizeof(CUSTOMVERTEX) * offset, sizeof(newVertices),
|
| + (void**) &pVertices, 0)))
|
| + {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "Failed to lock the vertex buffer.");
|
| + return -1;
|
| + }
|
| + memcpy(pVertices, newVertices, sizeof(newVertices));
|
| + pVB->Unlock();
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int32_t VideoRenderDirect3D9::StartRender()
|
| +{
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
|
| + return 0;
|
| +}
|
| +
|
| +int32_t VideoRenderDirect3D9::StopRender()
|
| +{
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
|
| + return 0;
|
| +}
|
| +
|
| +bool VideoRenderDirect3D9::IsFullScreen()
|
| +{
|
| + return _fullScreen;
|
| +}
|
| +
|
| +int32_t VideoRenderDirect3D9::SetCropping(const uint32_t channel,
|
| + const uint16_t streamId,
|
| + const float left, const float top,
|
| + const float right, const float bottom)
|
| +{
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
|
| + return 0;
|
| +}
|
| +
|
| +int32_t VideoRenderDirect3D9::SetTransparentBackground(
|
| + const bool enable)
|
| +{
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
|
| + return 0;
|
| +}
|
| +
|
| +int32_t VideoRenderDirect3D9::SetText(const uint8_t textId,
|
| + const uint8_t* text,
|
| + const int32_t textLength,
|
| + const uint32_t colorText,
|
| + const uint32_t colorBg,
|
| + const float left, const float top,
|
| + const float rigth, const float bottom)
|
| +{
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
|
| + return 0;
|
| +}
|
| +
|
| +int32_t VideoRenderDirect3D9::SetBitmap(const void* bitMap,
|
| + const uint8_t pictureId,
|
| + const void* colorKey,
|
| + const float left, const float top,
|
| + const float right, const float bottom)
|
| +{
|
| + if (!bitMap)
|
| + {
|
| + if (_pTextureLogo != NULL)
|
| + {
|
| + _pTextureLogo->Release();
|
| + _pTextureLogo = NULL;
|
| + }
|
| + WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, "Remove bitmap.");
|
| + return 0;
|
| + }
|
| +
|
| + // sanity
|
| + if (left > 1.0f || left < 0.0f ||
|
| + top > 1.0f || top < 0.0f ||
|
| + right > 1.0f || right < 0.0f ||
|
| + bottom > 1.0f || bottom < 0.0f)
|
| + {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "Direct3D SetBitmap invalid parameter");
|
| + return -1;
|
| + }
|
| +
|
| + if ((bottom <= top) || (right <= left))
|
| + {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "Direct3D SetBitmap invalid parameter");
|
| + return -1;
|
| + }
|
| +
|
| + CriticalSectionScoped cs(&_refD3DCritsect);
|
| +
|
| + unsigned char* srcPtr;
|
| + HGDIOBJ oldhand;
|
| + BITMAPINFO pbi;
|
| + BITMAP bmap;
|
| + HDC hdcNew;
|
| + hdcNew = CreateCompatibleDC(0);
|
| + // Fill out the BITMAP structure.
|
| + GetObject((HBITMAP)bitMap, sizeof(bmap), &bmap);
|
| + //Select the bitmap handle into the new device context.
|
| + oldhand = SelectObject(hdcNew, (HGDIOBJ) bitMap);
|
| + // we are done with this object
|
| + DeleteObject(oldhand);
|
| + pbi.bmiHeader.biSize = 40;
|
| + pbi.bmiHeader.biWidth = bmap.bmWidth;
|
| + pbi.bmiHeader.biHeight = bmap.bmHeight;
|
| + pbi.bmiHeader.biPlanes = 1;
|
| + pbi.bmiHeader.biBitCount = bmap.bmBitsPixel;
|
| + pbi.bmiHeader.biCompression = BI_RGB;
|
| + pbi.bmiHeader.biSizeImage = bmap.bmWidth * bmap.bmHeight * 3;
|
| + srcPtr = new unsigned char[bmap.bmWidth * bmap.bmHeight * 4];
|
| + // the original un-stretched image in RGB24
|
| + int pixelHeight = GetDIBits(hdcNew, (HBITMAP)bitMap, 0, bmap.bmHeight, srcPtr, &pbi,
|
| + DIB_RGB_COLORS);
|
| + if (pixelHeight == 0)
|
| + {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "Direct3D failed to GetDIBits in SetBitmap");
|
| + delete[] srcPtr;
|
| + return -1;
|
| + }
|
| + DeleteDC(hdcNew);
|
| + if (pbi.bmiHeader.biBitCount != 24 && pbi.bmiHeader.biBitCount != 32)
|
| + {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "Direct3D failed to SetBitmap invalid bit depth");
|
| + delete[] srcPtr;
|
| + return -1;
|
| + }
|
| +
|
| + HRESULT ret;
|
| + //release the previous logo texture
|
| + if (_pTextureLogo != NULL)
|
| + {
|
| + _pTextureLogo->Release();
|
| + _pTextureLogo = NULL;
|
| + }
|
| + ret = _pd3dDevice->CreateTexture(bmap.bmWidth, bmap.bmHeight, 1, 0,
|
| + D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
|
| + &_pTextureLogo, NULL);
|
| + if (FAILED(ret))
|
| + {
|
| + _pTextureLogo = NULL;
|
| + delete[] srcPtr;
|
| + return -1;
|
| + }
|
| + if (!_pTextureLogo)
|
| + {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "Texture for rendering not initialized.");
|
| + delete[] srcPtr;
|
| + return -1;
|
| + }
|
| +
|
| + D3DLOCKED_RECT lr;
|
| + if (FAILED(_pTextureLogo->LockRect(0, &lr, NULL, 0)))
|
| + {
|
| + delete[] srcPtr;
|
| + return -1;
|
| + }
|
| + unsigned char* dstPtr = (UCHAR*) lr.pBits;
|
| + int pitch = bmap.bmWidth * 4;
|
| +
|
| + if (pbi.bmiHeader.biBitCount == 24)
|
| + {
|
| + ConvertRGB24ToARGB(srcPtr, dstPtr, bmap.bmWidth, bmap.bmHeight, 0);
|
| + }
|
| + else
|
| + {
|
| + unsigned char* srcTmp = srcPtr + (bmap.bmWidth * 4) * (bmap.bmHeight - 1);
|
| + for (int i = 0; i < bmap.bmHeight; ++i)
|
| + {
|
| + memcpy(dstPtr, srcTmp, bmap.bmWidth * 4);
|
| + srcTmp -= bmap.bmWidth * 4;
|
| + dstPtr += pitch;
|
| + }
|
| + }
|
| +
|
| + delete[] srcPtr;
|
| + if (FAILED(_pTextureLogo->UnlockRect(0)))
|
| + {
|
| + return -1;
|
| + }
|
| +
|
| + if (colorKey)
|
| + {
|
| + DDCOLORKEY* ddColorKey =
|
| + static_cast<DDCOLORKEY*> (const_cast<void*> (colorKey));
|
| + SetTransparentColor(_pTextureLogo, ddColorKey, bmap.bmWidth,
|
| + bmap.bmHeight);
|
| + }
|
| +
|
| + //update the vertice buffer
|
| + //0,1 => -1,1
|
| + _logoLeft = left;
|
| + _logoRight = right;
|
| +
|
| + //0,1 => 1,-1
|
| + _logoTop = top;
|
| + _logoBottom = bottom;
|
| +
|
| + return 0;
|
| +
|
| +}
|
| +
|
| +int32_t VideoRenderDirect3D9::GetGraphicsMemory(uint64_t& totalMemory,
|
| + uint64_t& availableMemory)
|
| +{
|
| + totalMemory = _totalMemory;
|
| + availableMemory = _availableMemory;
|
| + return 0;
|
| +}
|
| +
|
| +int32_t VideoRenderDirect3D9::ConfigureRenderer(const uint32_t channel,
|
| + const uint16_t streamId,
|
| + const unsigned int zOrder,
|
| + const float left,
|
| + const float top,
|
| + const float right,
|
| + const float bottom)
|
| +{
|
| + std::map<int, D3D9Channel*>::iterator ddIt;
|
| + ddIt = _d3dChannels.find(channel & 0x0000ffff);
|
| + D3D9Channel* ddobj = NULL;
|
| + if (ddIt != _d3dChannels.end())
|
| + {
|
| + ddobj = ddIt->second;
|
| + }
|
| + if (ddobj == NULL)
|
| + {
|
| + WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
|
| + "Direct3D render failed to find channel");
|
| + return -1;
|
| + }
|
| + // Only allow one stream per channel, demuxing is
|
| + ddobj->SetStreamSettings(0, zOrder, left, top, right, bottom);
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +} // namespace webrtc
|
|
|