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 
23 namespace webrtc {
24 
25 // A structure for our custom vertex type
26 struct 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  */
D3D9Channel(LPDIRECT3DDEVICE9 pd3DDevice,CriticalSectionWrapper * critSect,Trace * trace)41 D3D9Channel::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 
~D3D9Channel()60 D3D9Channel::~D3D9Channel()
61 {
62     //release the texture
63     if (_pTexture != NULL)
64     {
65         _pTexture->Release();
66         _pTexture = NULL;
67     }
68 }
69 
SetStreamSettings(uint16_t streamId,uint32_t zOrder,float startWidth,float startHeight,float stopWidth,float stopHeight)70 void 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 
GetStreamSettings(uint16_t streamId,uint32_t & zOrder,float & startWidth,float & startHeight,float & stopWidth,float & stopHeight)85 int 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 
GetTextureWidth()101 int D3D9Channel::GetTextureWidth()
102 {
103     return _width;
104 }
105 
GetTextureHeight()106 int D3D9Channel::GetTextureHeight()
107 {
108     return _height;
109 }
110 
111 // Called from video engine when a the frame size changed
FrameSizeChange(int width,int height,int numberOfStreams)112 int 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 
RenderFrame(const uint32_t streamId,const VideoFrame & videoFrame)144 int32_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.
DeliverFrame(const VideoFrame & videoFrame)158 int 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
RenderOffFrame()206 int 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
IsUpdated(bool & isUpdated)216 int 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
GetTexture()224 LPDIRECT3DTEXTURE9 D3D9Channel::GetTexture()
225 {
226     CriticalSectionScoped cs(_critSect);
227     return _pTexture;
228 }
229 
ReleaseTexture()230 int 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 
RecreateTexture(LPDIRECT3DDEVICE9 pd3DDevice)244 int 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  */
VideoRenderDirect3D9(Trace * trace,HWND hWnd,bool fullScreen)275 VideoRenderDirect3D9::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 
~VideoRenderDirect3D9()303 VideoRenderDirect3D9::~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 
GetVertexProcessingCaps()349 DWORD 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 
InitializeD3D(HWND hWnd,D3DPRESENT_PARAMETERS * pd3dpp)365 int 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 
ResetDevice()402 int 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 
InitDevice()451 int 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 
Init()535 int32_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 
ChangeWindow(void * window)566 int32_t VideoRenderDirect3D9::ChangeWindow(void* window)
567 {
568     WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
569     return -1;
570 }
571 
UpdateRenderSurface()572 int 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
SetTransparentColor(LPDIRECT3DTEXTURE9 pTexture,DDCOLORKEY * transparentColorKey,DWORD width,DWORD height)668 int 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  */
ScreenUpdateThreadProc(void * obj)711 bool VideoRenderDirect3D9::ScreenUpdateThreadProc(void* obj)
712 {
713     return static_cast<VideoRenderDirect3D9*> (obj)->ScreenUpdateProcess();
714 }
715 
ScreenUpdateProcess()716 bool 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 
CloseDevice()755 int 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 
GetD3DChannel(int channel)790 D3D9Channel* 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 
DeleteChannel(const uint32_t streamId)808 int32_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 
CreateChannel(const uint32_t channel,const uint32_t zOrder,const float left,const float top,const float right,const float bottom)836 VideoRenderCallback* 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 
GetStreamSettings(const uint32_t channel,const uint16_t streamId,uint32_t & zOrder,float & left,float & top,float & right,float & bottom)864 int32_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 
UpdateVerticeBuffer(LPDIRECT3DVERTEXBUFFER9 pVB,int offset,float startWidth,float startHeight,float stopWidth,float stopHeight)887 int 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 
StartRender()929 int32_t VideoRenderDirect3D9::StartRender()
930 {
931     WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
932     return 0;
933 }
934 
StopRender()935 int32_t VideoRenderDirect3D9::StopRender()
936 {
937     WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
938     return 0;
939 }
940 
IsFullScreen()941 bool VideoRenderDirect3D9::IsFullScreen()
942 {
943     return _fullScreen;
944 }
945 
SetCropping(const uint32_t channel,const uint16_t streamId,const float left,const float top,const float right,const float bottom)946 int32_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 
SetTransparentBackground(const bool enable)955 int32_t VideoRenderDirect3D9::SetTransparentBackground(
956                                                                  const bool enable)
957 {
958     WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
959     return 0;
960 }
961 
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)962 int32_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 
SetBitmap(const void * bitMap,const uint8_t pictureId,const void * colorKey,const float left,const float top,const float right,const float bottom)974 int32_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 
GetGraphicsMemory(uint64_t & totalMemory,uint64_t & availableMemory)1125 int32_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 
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)1139 int32_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