text: /*
   *  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/interface/critical_section_wrapper.h"
  #include "webrtc/system_wrappers/interface/event_wrapper.h"
  #include "webrtc/system_wrappers/interface/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 = ThreadWrapper::CreateThread(
          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
      ThreadWrapper* 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(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);
  
          // D3DXMATRIX matWorld;
          // D3DXMATRIX matWorldTemp;
  
          //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)
  {
      if (_totalMemory == -1 || _availableMemory == -1)
      {
          totalMemory = 0;
          availableMemory = 0;
          return -1;
      }
      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
  