1/*
2 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11// Own include file
12#include "webrtc/modules/video_render/windows/video_render_direct3d9.h"
13
14// System include files
15#include <windows.h>
16
17// WebRtc include files
18#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
19#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
20#include "webrtc/system_wrappers/interface/event_wrapper.h"
21#include "webrtc/system_wrappers/interface/thread_wrapper.h"
22#include "webrtc/system_wrappers/interface/trace.h"
23
24namespace webrtc {
25
26// A structure for our custom vertex type
27struct CUSTOMVERTEX
28{
29    FLOAT x, y, z;
30    DWORD color; // The vertex color
31    FLOAT u, v;
32};
33
34// Our custom FVF, which describes our custom vertex structure
35#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)
36
37/*
38 *
39 *    D3D9Channel
40 *
41 */
42D3D9Channel::D3D9Channel(LPDIRECT3DDEVICE9 pd3DDevice,
43                                 CriticalSectionWrapper* critSect,
44                                 Trace* trace) :
45    _width(0),
46    _height(0),
47    _pd3dDevice(pd3DDevice),
48    _pTexture(NULL),
49    _bufferIsUpdated(false),
50    _critSect(critSect),
51    _streamId(0),
52    _zOrder(0),
53    _startWidth(0),
54    _startHeight(0),
55    _stopWidth(0),
56    _stopHeight(0)
57{
58
59}
60
61D3D9Channel::~D3D9Channel()
62{
63    //release the texture
64    if (_pTexture != NULL)
65    {
66        _pTexture->Release();
67        _pTexture = NULL;
68    }
69}
70
71void D3D9Channel::SetStreamSettings(uint16_t streamId,
72                                        uint32_t zOrder,
73                                        float startWidth,
74                                        float startHeight,
75                                        float stopWidth,
76                                        float stopHeight)
77{
78    _streamId = streamId;
79    _zOrder = zOrder;
80    _startWidth = startWidth;
81    _startHeight = startHeight;
82    _stopWidth = stopWidth;
83    _stopHeight = stopHeight;
84}
85
86int D3D9Channel::GetStreamSettings(uint16_t streamId,
87                                       uint32_t& zOrder,
88                                       float& startWidth,
89                                       float& startHeight,
90                                       float& stopWidth,
91                                       float& stopHeight)
92{
93    streamId = _streamId;
94    zOrder = _zOrder;
95    startWidth = _startWidth;
96    startHeight = _startHeight;
97    stopWidth = _stopWidth;
98    stopHeight = _stopHeight;
99    return 0;
100}
101
102int D3D9Channel::GetTextureWidth()
103{
104    return _width;
105}
106
107int D3D9Channel::GetTextureHeight()
108{
109    return _height;
110}
111
112// Called from video engine when a the frame size changed
113int D3D9Channel::FrameSizeChange(int width, int height, int numberOfStreams)
114{
115    WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
116                 "FrameSizeChange, wifth: %d, height: %d, streams: %d", width,
117                 height, numberOfStreams);
118
119    CriticalSectionScoped cs(_critSect);
120    _width = width;
121    _height = height;
122
123    //clean the previous texture
124    if (_pTexture != NULL)
125    {
126        _pTexture->Release();
127        _pTexture = NULL;
128    }
129
130    HRESULT ret = E_POINTER;
131
132    if (_pd3dDevice)
133      ret = _pd3dDevice->CreateTexture(_width, _height, 1, 0, D3DFMT_A8R8G8B8,
134                                       D3DPOOL_MANAGED, &_pTexture, NULL);
135
136    if (FAILED(ret))
137    {
138        _pTexture = NULL;
139        return -1;
140    }
141
142    return 0;
143}
144
145int32_t D3D9Channel::RenderFrame(const uint32_t streamId,
146                                 I420VideoFrame& videoFrame)
147{
148    CriticalSectionScoped cs(_critSect);
149    if (_width != videoFrame.width() || _height != videoFrame.height())
150    {
151        if (FrameSizeChange(videoFrame.width(), videoFrame.height(), 1) == -1)
152        {
153            return -1;
154        }
155    }
156    return DeliverFrame(videoFrame);
157}
158
159// Called from video engine when a new frame should be rendered.
160int D3D9Channel::DeliverFrame(const I420VideoFrame& videoFrame) {
161  WEBRTC_TRACE(kTraceStream, kTraceVideo, -1,
162               "DeliverFrame to D3D9Channel");
163
164  CriticalSectionScoped cs(_critSect);
165
166  // FIXME if _bufferIsUpdated is still true (not be renderred), do we want to
167  // update the texture? probably not
168  if (_bufferIsUpdated) {
169    WEBRTC_TRACE(kTraceStream, kTraceVideo, -1,
170                 "Last frame hasn't been rendered yet. Drop this frame.");
171    return -1;
172  }
173
174  if (!_pd3dDevice) {
175    WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
176                 "D3D for rendering not initialized.");
177    return -1;
178  }
179
180  if (!_pTexture) {
181    WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
182                 "Texture for rendering not initialized.");
183    return -1;
184  }
185
186  D3DLOCKED_RECT lr;
187
188  if (FAILED(_pTexture->LockRect(0, &lr, NULL, 0))) {
189    WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
190                 "Failed to lock a texture in D3D9 Channel.");
191    return -1;
192  }
193  UCHAR* pRect = (UCHAR*) lr.pBits;
194
195  ConvertFromI420(videoFrame, kARGB, 0, pRect);
196
197  if (FAILED(_pTexture->UnlockRect(0))) {
198    WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
199                 "Failed to unlock a texture in D3D9 Channel.");
200    return -1;
201  }
202
203  _bufferIsUpdated = true;
204  return 0;
205}
206
207// Called by d3d channel owner to indicate the frame/texture has been rendered off
208int D3D9Channel::RenderOffFrame()
209{
210    WEBRTC_TRACE(kTraceStream, kTraceVideo, -1,
211                 "Frame has been rendered to the screen.");
212    CriticalSectionScoped cs(_critSect);
213    _bufferIsUpdated = false;
214    return 0;
215}
216
217// Called by d3d channel owner to check if the texture is updated
218int D3D9Channel::IsUpdated(bool& isUpdated)
219{
220    CriticalSectionScoped cs(_critSect);
221    isUpdated = _bufferIsUpdated;
222    return 0;
223}
224
225// Called by d3d channel owner to get the texture
226LPDIRECT3DTEXTURE9 D3D9Channel::GetTexture()
227{
228    CriticalSectionScoped cs(_critSect);
229    return _pTexture;
230}
231
232int D3D9Channel::ReleaseTexture()
233{
234    CriticalSectionScoped cs(_critSect);
235
236    //release the texture
237    if (_pTexture != NULL)
238    {
239        _pTexture->Release();
240        _pTexture = NULL;
241    }
242    _pd3dDevice = NULL;
243    return 0;
244}
245
246int D3D9Channel::RecreateTexture(LPDIRECT3DDEVICE9 pd3DDevice)
247{
248    CriticalSectionScoped cs(_critSect);
249
250    _pd3dDevice = pd3DDevice;
251
252    if (_pTexture != NULL)
253    {
254        _pTexture->Release();
255        _pTexture = NULL;
256    }
257
258    HRESULT ret;
259
260    ret = _pd3dDevice->CreateTexture(_width, _height, 1, 0, D3DFMT_A8R8G8B8,
261                                     D3DPOOL_MANAGED, &_pTexture, NULL);
262
263    if (FAILED(ret))
264    {
265        _pTexture = NULL;
266        return -1;
267    }
268
269    return 0;
270}
271
272/*
273 *
274 *    VideoRenderDirect3D9
275 *
276 */
277VideoRenderDirect3D9::VideoRenderDirect3D9(Trace* trace,
278                                                   HWND hWnd,
279                                                   bool fullScreen) :
280    _refD3DCritsect(*CriticalSectionWrapper::CreateCriticalSection()),
281    _trace(trace),
282    _hWnd(hWnd),
283    _fullScreen(fullScreen),
284    _pTextureLogo(NULL),
285    _pVB(NULL),
286    _pd3dDevice(NULL),
287    _pD3D(NULL),
288    _d3dChannels(),
289    _d3dZorder(),
290    _screenUpdateThread(NULL),
291    _screenUpdateEvent(NULL),
292    _logoLeft(0),
293    _logoTop(0),
294    _logoRight(0),
295    _logoBottom(0),
296    _pd3dSurface(NULL),
297    _totalMemory(0),
298    _availableMemory(0)
299{
300    _screenUpdateThread = ThreadWrapper::CreateThread(ScreenUpdateThreadProc,
301                                                      this, kRealtimePriority);
302    _screenUpdateEvent = EventWrapper::Create();
303    SetRect(&_originalHwndRect, 0, 0, 0, 0);
304}
305
306VideoRenderDirect3D9::~VideoRenderDirect3D9()
307{
308    //NOTE: we should not enter CriticalSection in here!
309
310    // Signal event to exit thread, then delete it
311    ThreadWrapper* tmpPtr = _screenUpdateThread;
312    _screenUpdateThread = NULL;
313    if (tmpPtr)
314    {
315        tmpPtr->SetNotAlive();
316        _screenUpdateEvent->Set();
317        _screenUpdateEvent->StopTimer();
318
319        if (tmpPtr->Stop())
320        {
321            delete tmpPtr;
322        }
323    }
324    delete _screenUpdateEvent;
325
326    //close d3d device
327    CloseDevice();
328
329    // Delete all channels
330    std::map<int, D3D9Channel*>::iterator it = _d3dChannels.begin();
331    while (it != _d3dChannels.end())
332    {
333        delete it->second;
334        it = _d3dChannels.erase(it);
335    }
336    // Clean the zOrder map
337    _d3dZorder.clear();
338
339    if (_fullScreen)
340    {
341        // restore hwnd to original size and position
342        ::SetWindowPos(_hWnd, HWND_NOTOPMOST, _originalHwndRect.left,
343                       _originalHwndRect.top, _originalHwndRect.right
344                               - _originalHwndRect.left,
345                       _originalHwndRect.bottom - _originalHwndRect.top,
346                       SWP_FRAMECHANGED);
347        ::RedrawWindow(_hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW
348                | RDW_ERASE);
349        ::RedrawWindow(NULL, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW
350                | RDW_ERASE);
351    }
352
353    delete &_refD3DCritsect;
354}
355
356DWORD VideoRenderDirect3D9::GetVertexProcessingCaps()
357{
358    D3DCAPS9 caps;
359    DWORD dwVertexProcessing = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
360    if (SUCCEEDED(_pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
361                                       &caps)))
362    {
363        if ((caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
364                == D3DDEVCAPS_HWTRANSFORMANDLIGHT)
365        {
366            dwVertexProcessing = D3DCREATE_HARDWARE_VERTEXPROCESSING;
367        }
368    }
369    return dwVertexProcessing;
370}
371
372int VideoRenderDirect3D9::InitializeD3D(HWND hWnd,
373                                            D3DPRESENT_PARAMETERS* pd3dpp)
374{
375    // initialize Direct3D
376    if (NULL == (_pD3D = Direct3DCreate9(D3D_SDK_VERSION)))
377    {
378        return -1;
379    }
380
381    // determine what type of vertex processing to use based on the device capabilities
382    DWORD dwVertexProcessing = GetVertexProcessingCaps();
383
384    // get the display mode
385    D3DDISPLAYMODE d3ddm;
386    _pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
387    pd3dpp->BackBufferFormat = d3ddm.Format;
388
389    // create the D3D device
390    if (FAILED(_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
391                                   dwVertexProcessing | D3DCREATE_MULTITHREADED
392                                           | D3DCREATE_FPU_PRESERVE, pd3dpp,
393                                   &_pd3dDevice)))
394    {
395        //try the ref device
396        if (FAILED(_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF,
397                                       hWnd, dwVertexProcessing
398                                               | D3DCREATE_MULTITHREADED
399                                               | D3DCREATE_FPU_PRESERVE,
400                                       pd3dpp, &_pd3dDevice)))
401        {
402            return -1;
403        }
404    }
405
406    return 0;
407}
408
409int VideoRenderDirect3D9::ResetDevice()
410{
411    WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
412                 "VideoRenderDirect3D9::ResetDevice");
413
414    CriticalSectionScoped cs(&_refD3DCritsect);
415
416    //release the channel texture
417    std::map<int, D3D9Channel*>::iterator it;
418    it = _d3dChannels.begin();
419    while (it != _d3dChannels.end())
420    {
421        if (it->second)
422        {
423            it->second->ReleaseTexture();
424        }
425        it++;
426    }
427
428    //close d3d device
429    if (CloseDevice() != 0)
430    {
431        WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
432                     "VideoRenderDirect3D9::ResetDevice failed to CloseDevice");
433        return -1;
434    }
435
436    //reinit d3d device
437    if (InitDevice() != 0)
438    {
439        WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
440                     "VideoRenderDirect3D9::ResetDevice failed to InitDevice");
441        return -1;
442    }
443
444    //recreate channel texture
445    it = _d3dChannels.begin();
446    while (it != _d3dChannels.end())
447    {
448        if (it->second)
449        {
450            it->second->RecreateTexture(_pd3dDevice);
451        }
452        it++;
453    }
454
455    return 0;
456}
457
458int VideoRenderDirect3D9::InitDevice()
459{
460    // Set up the structure used to create the D3DDevice
461    ZeroMemory(&_d3dpp, sizeof(_d3dpp));
462    _d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
463    _d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
464    if (GetWindowRect(_hWnd, &_originalHwndRect) == 0)
465    {
466        WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
467                     "VideoRenderDirect3D9::InitDevice Could not get window size");
468        return -1;
469    }
470    if (!_fullScreen)
471    {
472        _winWidth = _originalHwndRect.right - _originalHwndRect.left;
473        _winHeight = _originalHwndRect.bottom - _originalHwndRect.top;
474        _d3dpp.Windowed = TRUE;
475        _d3dpp.BackBufferHeight = 0;
476        _d3dpp.BackBufferWidth = 0;
477    }
478    else
479    {
480        _winWidth = (LONG) ::GetSystemMetrics(SM_CXSCREEN);
481        _winHeight = (LONG) ::GetSystemMetrics(SM_CYSCREEN);
482        _d3dpp.Windowed = FALSE;
483        _d3dpp.BackBufferWidth = _winWidth;
484        _d3dpp.BackBufferHeight = _winHeight;
485        _d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
486    }
487
488    if (InitializeD3D(_hWnd, &_d3dpp) == -1)
489    {
490        WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
491                     "VideoRenderDirect3D9::InitDevice failed in InitializeD3D");
492        return -1;
493    }
494
495    // Turn off culling, so we see the front and back of the triangle
496    _pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
497
498    // Turn off D3D lighting, since we are providing our own vertex colors
499    _pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
500
501    // Settings for alpha blending
502    _pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
503    _pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
504    _pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
505
506    _pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
507    _pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
508    _pd3dDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
509
510    // Initialize Vertices
511    CUSTOMVERTEX Vertices[] = {
512            //front
513            { -1.0f, -1.0f, 0.0f, 0xffffffff, 0, 1 }, { -1.0f, 1.0f, 0.0f,
514                    0xffffffff, 0, 0 },
515            { 1.0f, -1.0f, 0.0f, 0xffffffff, 1, 1 }, { 1.0f, 1.0f, 0.0f,
516                    0xffffffff, 1, 0 } };
517
518    // Create the vertex buffer.
519    if (FAILED(_pd3dDevice->CreateVertexBuffer(sizeof(Vertices), 0,
520                                               D3DFVF_CUSTOMVERTEX,
521                                               D3DPOOL_DEFAULT, &_pVB, NULL )))
522    {
523        WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
524                     "Failed to create the vertex buffer.");
525        return -1;
526    }
527
528    // Now we fill the vertex buffer.
529    VOID* pVertices;
530    if (FAILED(_pVB->Lock(0, sizeof(Vertices), (void**) &pVertices, 0)))
531    {
532        WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
533                     "Failed to lock the vertex buffer.");
534        return -1;
535    }
536    memcpy(pVertices, Vertices, sizeof(Vertices));
537    _pVB->Unlock();
538
539    return 0;
540}
541
542int32_t VideoRenderDirect3D9::Init()
543{
544    WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
545                 "VideoRenderDirect3D9::Init");
546
547    CriticalSectionScoped cs(&_refD3DCritsect);
548
549    // Start rendering thread...
550    if (!_screenUpdateThread)
551    {
552        WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Thread not created");
553        return -1;
554    }
555    unsigned int threadId;
556    _screenUpdateThread->Start(threadId);
557
558    // Start the event triggering the render process
559    unsigned int monitorFreq = 60;
560    DEVMODE dm;
561    // initialize the DEVMODE structure
562    ZeroMemory(&dm, sizeof(dm));
563    dm.dmSize = sizeof(dm);
564    if (0 != EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm))
565    {
566        monitorFreq = dm.dmDisplayFrequency;
567    }
568    _screenUpdateEvent->StartTimer(true, 1000 / monitorFreq);
569
570    return InitDevice();
571}
572
573int32_t VideoRenderDirect3D9::ChangeWindow(void* window)
574{
575    WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
576    return -1;
577}
578
579int VideoRenderDirect3D9::UpdateRenderSurface()
580{
581    CriticalSectionScoped cs(&_refD3DCritsect);
582
583    // Check if there are any updated buffers
584    bool updated = false;
585    std::map<int, D3D9Channel*>::iterator it;
586    it = _d3dChannels.begin();
587    while (it != _d3dChannels.end())
588    {
589
590        D3D9Channel* channel = it->second;
591        channel->IsUpdated(updated);
592        if (updated)
593        {
594            break;
595        }
596        it++;
597    }
598    //nothing is updated, continue
599    if (!updated)
600        return -1;
601
602    // Clear the backbuffer to a black color
603    _pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f,
604                       0);
605
606    // Begin the scene
607    if (SUCCEEDED(_pd3dDevice->BeginScene()))
608    {
609        _pd3dDevice->SetStreamSource(0, _pVB, 0, sizeof(CUSTOMVERTEX));
610        _pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
611
612        D3DXMATRIX matWorld;
613        D3DXMATRIX matWorldTemp;
614
615        //draw all the channels
616        //get texture from the channels
617        LPDIRECT3DTEXTURE9 textureFromChannel = NULL;
618        DWORD textureWidth, textureHeight;
619
620        std::multimap<int, unsigned int>::reverse_iterator it;
621        it = _d3dZorder.rbegin();
622        while (it != _d3dZorder.rend())
623        {
624            // loop through all channels and streams in Z order
625            int channel = it->second & 0x0000ffff;
626
627            std::map<int, D3D9Channel*>::iterator ddIt;
628            ddIt = _d3dChannels.find(channel);
629            if (ddIt != _d3dChannels.end())
630            {
631                // found the channel
632                D3D9Channel* channelObj = ddIt->second;
633                if (channelObj)
634                {
635                    textureFromChannel = channelObj->GetTexture();
636                    textureWidth = channelObj->GetTextureWidth();
637                    textureHeight = channelObj->GetTextureHeight();
638
639                    uint32_t zOrder;
640                    float startWidth, startHeight, stopWidth, stopHeight;
641                    channelObj->GetStreamSettings(0, zOrder, startWidth,
642                                                  startHeight, stopWidth,
643                                                  stopHeight);
644
645                    //draw the video stream
646                    UpdateVerticeBuffer(_pVB, 0, startWidth, startHeight,
647                                        stopWidth, stopHeight);
648                    _pd3dDevice->SetTexture(0, textureFromChannel);
649                    _pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
650
651                    //Notice channel that this frame as been rendered
652                    channelObj->RenderOffFrame();
653                }
654            }
655            it++;
656        }
657
658        //draw the logo
659        if (_pTextureLogo)
660        {
661            UpdateVerticeBuffer(_pVB, 0, _logoLeft, _logoTop, _logoRight,
662                                _logoBottom);
663            _pd3dDevice->SetTexture(0, _pTextureLogo);
664            _pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
665        }
666
667        // End the scene
668        _pd3dDevice->EndScene();
669    }
670
671    // Present the backbuffer contents to the display
672    _pd3dDevice->Present(NULL, NULL, NULL, NULL );
673
674    return 0;
675}
676
677//set the  alpha value of the pixal with a particular colorkey as 0
678int VideoRenderDirect3D9::SetTransparentColor(LPDIRECT3DTEXTURE9 pTexture,
679                                                  DDCOLORKEY* transparentColorKey,
680                                                  DWORD width,
681                                                  DWORD height)
682{
683    D3DLOCKED_RECT lr;
684    if (!pTexture)
685        return -1;
686
687    CriticalSectionScoped cs(&_refD3DCritsect);
688    if (SUCCEEDED(pTexture->LockRect(0, &lr, NULL, D3DLOCK_DISCARD)))
689    {
690        for (DWORD y = 0; y < height; y++)
691        {
692            DWORD dwOffset = y * width;
693
694            for (DWORD x = 0; x < width; x)
695            {
696                DWORD temp = ((DWORD*) lr.pBits)[dwOffset + x];
697                if ((temp & 0x00FFFFFF)
698                        == transparentColorKey->dwColorSpaceLowValue)
699                {
700                    temp &= 0x00FFFFFF;
701                }
702                else
703                {
704                    temp |= 0xFF000000;
705                }
706                ((DWORD*) lr.pBits)[dwOffset + x] = temp;
707                x++;
708            }
709        }
710        pTexture->UnlockRect(0);
711        return 0;
712    }
713    return -1;
714}
715
716/*
717 *
718 *    Rendering process
719 *
720 */
721bool VideoRenderDirect3D9::ScreenUpdateThreadProc(void* obj)
722{
723    return static_cast<VideoRenderDirect3D9*> (obj)->ScreenUpdateProcess();
724}
725
726bool VideoRenderDirect3D9::ScreenUpdateProcess()
727{
728    _screenUpdateEvent->Wait(100);
729
730    if (!_screenUpdateThread)
731    {
732        //stop the thread
733        return false;
734    }
735    if (!_pd3dDevice)
736    {
737        WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
738                     "d3dDevice not created.");
739        return true;
740    }
741
742    HRESULT hr = _pd3dDevice->TestCooperativeLevel();
743
744    if (SUCCEEDED(hr))
745    {
746        UpdateRenderSurface();
747    }
748
749    if (hr == D3DERR_DEVICELOST)
750    {
751        //Device is lost and cannot be reset yet
752
753    }
754    else if (hr == D3DERR_DEVICENOTRESET)
755    {
756        //Lost but we can reset it now
757        //Note: the standard way is to call Reset, however for some reason doesn't work here.
758        //so we will release the device and create it again.
759        ResetDevice();
760    }
761
762    return true;
763}
764
765int VideoRenderDirect3D9::CloseDevice()
766{
767    CriticalSectionScoped cs(&_refD3DCritsect);
768    WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
769                 "VideoRenderDirect3D9::CloseDevice");
770
771    if (_pTextureLogo != NULL)
772    {
773        _pTextureLogo->Release();
774        _pTextureLogo = NULL;
775    }
776
777    if (_pVB != NULL)
778    {
779        _pVB->Release();
780        _pVB = NULL;
781    }
782
783    if (_pd3dDevice != NULL)
784    {
785        _pd3dDevice->Release();
786        _pd3dDevice = NULL;
787    }
788
789    if (_pD3D != NULL)
790    {
791        _pD3D->Release();
792        _pD3D = NULL;
793    }
794
795    if (_pd3dSurface != NULL)
796        _pd3dSurface->Release();
797    return 0;
798}
799
800D3D9Channel* VideoRenderDirect3D9::GetD3DChannel(int channel)
801{
802    std::map<int, D3D9Channel*>::iterator ddIt;
803    ddIt = _d3dChannels.find(channel & 0x0000ffff);
804    D3D9Channel* ddobj = NULL;
805    if (ddIt != _d3dChannels.end())
806    {
807        ddobj = ddIt->second;
808    }
809    if (ddobj == NULL)
810    {
811        WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
812                     "Direct3D render failed to find channel");
813        return NULL;
814    }
815    return ddobj;
816}
817
818int32_t VideoRenderDirect3D9::DeleteChannel(const uint32_t streamId)
819{
820    CriticalSectionScoped cs(&_refD3DCritsect);
821
822
823    std::multimap<int, unsigned int>::iterator it;
824    it = _d3dZorder.begin();
825    while (it != _d3dZorder.end())
826    {
827        if ((streamId & 0x0000ffff) == (it->second & 0x0000ffff))
828        {
829            it = _d3dZorder.erase(it);
830            break;
831        }
832        it++;
833    }
834
835    std::map<int, D3D9Channel*>::iterator ddIt;
836    ddIt = _d3dChannels.find(streamId & 0x0000ffff);
837    if (ddIt != _d3dChannels.end())
838    {
839        delete ddIt->second;
840        _d3dChannels.erase(ddIt);
841        return 0;
842    }
843    return -1;
844}
845
846VideoRenderCallback* VideoRenderDirect3D9::CreateChannel(const uint32_t channel,
847                                                                 const uint32_t zOrder,
848                                                                 const float left,
849                                                                 const float top,
850                                                                 const float right,
851                                                                 const float bottom)
852{
853    CriticalSectionScoped cs(&_refD3DCritsect);
854
855    //FIXME this should be done in VideoAPIWindows? stop the frame deliver first
856    //remove the old channel
857    DeleteChannel(channel);
858
859    D3D9Channel* d3dChannel = new D3D9Channel(_pd3dDevice,
860                                                      &_refD3DCritsect, _trace);
861    d3dChannel->SetStreamSettings(0, zOrder, left, top, right, bottom);
862
863    // store channel
864    _d3dChannels[channel & 0x0000ffff] = d3dChannel;
865
866    // store Z order
867    // default streamID is 0
868    _d3dZorder.insert(
869                      std::pair<int, unsigned int>(zOrder, channel & 0x0000ffff));
870
871    return d3dChannel;
872}
873
874int32_t VideoRenderDirect3D9::GetStreamSettings(const uint32_t channel,
875                                                const uint16_t streamId,
876                                                uint32_t& zOrder,
877                                                float& left, float& top,
878                                                float& right, float& bottom)
879{
880    std::map<int, D3D9Channel*>::iterator ddIt;
881    ddIt = _d3dChannels.find(channel & 0x0000ffff);
882    D3D9Channel* ddobj = NULL;
883    if (ddIt != _d3dChannels.end())
884    {
885        ddobj = ddIt->second;
886    }
887    if (ddobj == NULL)
888    {
889        WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
890                     "Direct3D render failed to find channel");
891        return -1;
892    }
893    // Only allow one stream per channel, demuxing is
894    return ddobj->GetStreamSettings(0, zOrder, left, top, right, bottom);
895    //return ddobj->GetStreamSettings(streamId, zOrder, left, top, right, bottom);
896}
897
898int VideoRenderDirect3D9::UpdateVerticeBuffer(LPDIRECT3DVERTEXBUFFER9 pVB,
899                                                  int offset,
900                                                  float startWidth,
901                                                  float startHeight,
902                                                  float stopWidth,
903                                                  float stopHeight)
904{
905    if (pVB == NULL)
906        return -1;
907
908    float left, right, top, bottom;
909
910    //update the vertice buffer
911    //0,1 => -1,1
912    left = startWidth * 2 - 1;
913    right = stopWidth * 2 - 1;
914
915    //0,1 => 1,-1
916    top = 1 - startHeight * 2;
917    bottom = 1 - stopHeight * 2;
918
919    CUSTOMVERTEX newVertices[] = {
920            //logo
921            { left, bottom, 0.0f, 0xffffffff, 0, 1 }, { left, top, 0.0f,
922                    0xffffffff, 0, 0 },
923            { right, bottom, 0.0f, 0xffffffff, 1, 1 }, { right, top, 0.0f,
924                    0xffffffff, 1, 0 }, };
925    // Now we fill the vertex buffer.
926    VOID* pVertices;
927    if (FAILED(pVB->Lock(sizeof(CUSTOMVERTEX) * offset, sizeof(newVertices),
928                         (void**) &pVertices, 0)))
929    {
930        WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
931                     "Failed to lock the vertex buffer.");
932        return -1;
933    }
934    memcpy(pVertices, newVertices, sizeof(newVertices));
935    pVB->Unlock();
936
937    return 0;
938}
939
940int32_t VideoRenderDirect3D9::StartRender()
941{
942    WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
943    return 0;
944}
945
946int32_t VideoRenderDirect3D9::StopRender()
947{
948    WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
949    return 0;
950}
951
952bool VideoRenderDirect3D9::IsFullScreen()
953{
954    return _fullScreen;
955}
956
957int32_t VideoRenderDirect3D9::SetCropping(const uint32_t channel,
958                                          const uint16_t streamId,
959                                          const float left, const float top,
960                                          const float right, const float bottom)
961{
962    WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
963    return 0;
964}
965
966int32_t VideoRenderDirect3D9::SetTransparentBackground(
967                                                                 const bool enable)
968{
969    WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
970    return 0;
971}
972
973int32_t VideoRenderDirect3D9::SetText(const uint8_t textId,
974                                      const uint8_t* text,
975                                      const int32_t textLength,
976                                      const uint32_t colorText,
977                                      const uint32_t colorBg,
978                                      const float left, const float top,
979                                      const float rigth, const float bottom)
980{
981    WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
982    return 0;
983}
984
985int32_t VideoRenderDirect3D9::SetBitmap(const void* bitMap,
986                                        const uint8_t pictureId,
987                                        const void* colorKey,
988                                        const float left, const float top,
989                                        const float right, const float bottom)
990{
991    if (!bitMap)
992    {
993        if (_pTextureLogo != NULL)
994        {
995            _pTextureLogo->Release();
996            _pTextureLogo = NULL;
997        }
998        WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, "Remove bitmap.");
999        return 0;
1000    }
1001
1002    // sanity
1003    if (left > 1.0f || left < 0.0f ||
1004        top > 1.0f || top < 0.0f ||
1005        right > 1.0f || right < 0.0f ||
1006        bottom > 1.0f || bottom < 0.0f)
1007    {
1008        WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
1009                     "Direct3D SetBitmap invalid parameter");
1010        return -1;
1011    }
1012
1013    if ((bottom <= top) || (right <= left))
1014    {
1015        WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
1016                     "Direct3D SetBitmap invalid parameter");
1017        return -1;
1018    }
1019
1020    CriticalSectionScoped cs(&_refD3DCritsect);
1021
1022    unsigned char* srcPtr;
1023    HGDIOBJ oldhand;
1024    BITMAPINFO pbi;
1025    BITMAP bmap;
1026    HDC hdcNew;
1027    hdcNew = CreateCompatibleDC(0);
1028    // Fill out the BITMAP structure.
1029    GetObject((HBITMAP)bitMap, sizeof(bmap), &bmap);
1030    //Select the bitmap handle into the new device context.
1031    oldhand = SelectObject(hdcNew, (HGDIOBJ) bitMap);
1032    // we are done with this object
1033    DeleteObject(oldhand);
1034    pbi.bmiHeader.biSize = 40;
1035    pbi.bmiHeader.biWidth = bmap.bmWidth;
1036    pbi.bmiHeader.biHeight = bmap.bmHeight;
1037    pbi.bmiHeader.biPlanes = 1;
1038    pbi.bmiHeader.biBitCount = bmap.bmBitsPixel;
1039    pbi.bmiHeader.biCompression = BI_RGB;
1040    pbi.bmiHeader.biSizeImage = bmap.bmWidth * bmap.bmHeight * 3;
1041    srcPtr = new unsigned char[bmap.bmWidth * bmap.bmHeight * 4];
1042    // the original un-stretched image in RGB24
1043    int pixelHeight = GetDIBits(hdcNew, (HBITMAP)bitMap, 0, bmap.bmHeight, srcPtr, &pbi,
1044                                DIB_RGB_COLORS);
1045    if (pixelHeight == 0)
1046    {
1047        WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
1048                     "Direct3D failed to GetDIBits in SetBitmap");
1049        delete[] srcPtr;
1050        return -1;
1051    }
1052    DeleteDC(hdcNew);
1053    if (pbi.bmiHeader.biBitCount != 24 && pbi.bmiHeader.biBitCount != 32)
1054    {
1055        WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
1056                     "Direct3D failed to SetBitmap invalid bit depth");
1057        delete[] srcPtr;
1058        return -1;
1059    }
1060
1061    HRESULT ret;
1062    //release the previous logo texture
1063    if (_pTextureLogo != NULL)
1064    {
1065        _pTextureLogo->Release();
1066        _pTextureLogo = NULL;
1067    }
1068    ret = _pd3dDevice->CreateTexture(bmap.bmWidth, bmap.bmHeight, 1, 0,
1069                                     D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
1070                                     &_pTextureLogo, NULL);
1071    if (FAILED(ret))
1072    {
1073        _pTextureLogo = NULL;
1074        delete[] srcPtr;
1075        return -1;
1076    }
1077    if (!_pTextureLogo)
1078    {
1079        WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
1080                     "Texture for rendering not initialized.");
1081        delete[] srcPtr;
1082        return -1;
1083    }
1084
1085    D3DLOCKED_RECT lr;
1086    if (FAILED(_pTextureLogo->LockRect(0, &lr, NULL, 0)))
1087    {
1088        delete[] srcPtr;
1089        return -1;
1090    }
1091    unsigned char* dstPtr = (UCHAR*) lr.pBits;
1092    int pitch = bmap.bmWidth * 4;
1093
1094    if (pbi.bmiHeader.biBitCount == 24)
1095    {
1096        ConvertRGB24ToARGB(srcPtr, dstPtr, bmap.bmWidth, bmap.bmHeight, 0);
1097    }
1098    else
1099    {
1100        unsigned char* srcTmp = srcPtr + (bmap.bmWidth * 4) * (bmap.bmHeight - 1);
1101        for (int i = 0; i < bmap.bmHeight; ++i)
1102        {
1103            memcpy(dstPtr, srcTmp, bmap.bmWidth * 4);
1104            srcTmp -= bmap.bmWidth * 4;
1105            dstPtr += pitch;
1106        }
1107    }
1108
1109    delete[] srcPtr;
1110    if (FAILED(_pTextureLogo->UnlockRect(0)))
1111    {
1112        return -1;
1113    }
1114
1115    if (colorKey)
1116    {
1117        DDCOLORKEY* ddColorKey =
1118                static_cast<DDCOLORKEY*> (const_cast<void*> (colorKey));
1119        SetTransparentColor(_pTextureLogo, ddColorKey, bmap.bmWidth,
1120                            bmap.bmHeight);
1121    }
1122
1123    //update the vertice buffer
1124    //0,1 => -1,1
1125    _logoLeft = left;
1126    _logoRight = right;
1127
1128    //0,1 => 1,-1
1129    _logoTop = top;
1130    _logoBottom = bottom;
1131
1132    return 0;
1133
1134}
1135
1136int32_t VideoRenderDirect3D9::GetGraphicsMemory(uint64_t& totalMemory,
1137                                                uint64_t& availableMemory)
1138{
1139    if (_totalMemory == -1 || _availableMemory == -1)
1140    {
1141        totalMemory = 0;
1142        availableMemory = 0;
1143        return -1;
1144    }
1145    totalMemory = _totalMemory;
1146    availableMemory = _availableMemory;
1147    return 0;
1148}
1149
1150int32_t VideoRenderDirect3D9::ConfigureRenderer(const uint32_t channel,
1151                                                const uint16_t streamId,
1152                                                const unsigned int zOrder,
1153                                                const float left,
1154                                                const float top,
1155                                                const float right,
1156                                                const float bottom)
1157{
1158    std::map<int, D3D9Channel*>::iterator ddIt;
1159    ddIt = _d3dChannels.find(channel & 0x0000ffff);
1160    D3D9Channel* ddobj = NULL;
1161    if (ddIt != _d3dChannels.end())
1162    {
1163        ddobj = ddIt->second;
1164    }
1165    if (ddobj == NULL)
1166    {
1167        WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
1168                     "Direct3D render failed to find channel");
1169        return -1;
1170    }
1171    // Only allow one stream per channel, demuxing is
1172    ddobj->SetStreamSettings(0, zOrder, left, top, right, bottom);
1173
1174    return 0;
1175}
1176
1177}  // namespace webrtc
1178