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