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
24 namespace webrtc {
25
26 // A structure for our custom vertex type
27 struct 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 */
D3D9Channel(LPDIRECT3DDEVICE9 pd3DDevice,CriticalSectionWrapper * critSect,Trace * trace)42 D3D9Channel::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
~D3D9Channel()61 D3D9Channel::~D3D9Channel()
62 {
63 //release the texture
64 if (_pTexture != NULL)
65 {
66 _pTexture->Release();
67 _pTexture = NULL;
68 }
69 }
70
SetStreamSettings(uint16_t streamId,uint32_t zOrder,float startWidth,float startHeight,float stopWidth,float stopHeight)71 void 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
GetStreamSettings(uint16_t streamId,uint32_t & zOrder,float & startWidth,float & startHeight,float & stopWidth,float & stopHeight)86 int 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
GetTextureWidth()102 int D3D9Channel::GetTextureWidth()
103 {
104 return _width;
105 }
106
GetTextureHeight()107 int D3D9Channel::GetTextureHeight()
108 {
109 return _height;
110 }
111
112 // Called from video engine when a the frame size changed
FrameSizeChange(int width,int height,int numberOfStreams)113 int 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
RenderFrame(const uint32_t streamId,I420VideoFrame & videoFrame)145 int32_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.
DeliverFrame(const I420VideoFrame & videoFrame)160 int 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
RenderOffFrame()208 int 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
IsUpdated(bool & isUpdated)218 int 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
GetTexture()226 LPDIRECT3DTEXTURE9 D3D9Channel::GetTexture()
227 {
228 CriticalSectionScoped cs(_critSect);
229 return _pTexture;
230 }
231
ReleaseTexture()232 int 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
RecreateTexture(LPDIRECT3DDEVICE9 pd3DDevice)246 int 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 */
VideoRenderDirect3D9(Trace * trace,HWND hWnd,bool fullScreen)277 VideoRenderDirect3D9::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(-1),
298 _availableMemory(-1)
299 {
300 _screenUpdateThread = ThreadWrapper::CreateThread(ScreenUpdateThreadProc,
301 this, kRealtimePriority);
302 _screenUpdateEvent = EventWrapper::Create();
303 SetRect(&_originalHwndRect, 0, 0, 0, 0);
304 }
305
~VideoRenderDirect3D9()306 VideoRenderDirect3D9::~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
GetVertexProcessingCaps()356 DWORD 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
InitializeD3D(HWND hWnd,D3DPRESENT_PARAMETERS * pd3dpp)372 int 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
ResetDevice()409 int 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
InitDevice()458 int 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
Init()542 int32_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
ChangeWindow(void * window)573 int32_t VideoRenderDirect3D9::ChangeWindow(void* window)
574 {
575 WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
576 return -1;
577 }
578
UpdateRenderSurface()579 int 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
SetTransparentColor(LPDIRECT3DTEXTURE9 pTexture,DDCOLORKEY * transparentColorKey,DWORD width,DWORD height)678 int 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 */
ScreenUpdateThreadProc(void * obj)721 bool VideoRenderDirect3D9::ScreenUpdateThreadProc(void* obj)
722 {
723 return static_cast<VideoRenderDirect3D9*> (obj)->ScreenUpdateProcess();
724 }
725
ScreenUpdateProcess()726 bool 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
CloseDevice()765 int 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
GetD3DChannel(int channel)800 D3D9Channel* 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
DeleteChannel(const uint32_t streamId)818 int32_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
CreateChannel(const uint32_t channel,const uint32_t zOrder,const float left,const float top,const float right,const float bottom)846 VideoRenderCallback* 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
GetStreamSettings(const uint32_t channel,const uint16_t streamId,uint32_t & zOrder,float & left,float & top,float & right,float & bottom)874 int32_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
UpdateVerticeBuffer(LPDIRECT3DVERTEXBUFFER9 pVB,int offset,float startWidth,float startHeight,float stopWidth,float stopHeight)898 int 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
StartRender()940 int32_t VideoRenderDirect3D9::StartRender()
941 {
942 WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
943 return 0;
944 }
945
StopRender()946 int32_t VideoRenderDirect3D9::StopRender()
947 {
948 WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
949 return 0;
950 }
951
IsFullScreen()952 bool VideoRenderDirect3D9::IsFullScreen()
953 {
954 return _fullScreen;
955 }
956
SetCropping(const uint32_t channel,const uint16_t streamId,const float left,const float top,const float right,const float bottom)957 int32_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
SetTransparentBackground(const bool enable)966 int32_t VideoRenderDirect3D9::SetTransparentBackground(
967 const bool enable)
968 {
969 WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported.");
970 return 0;
971 }
972
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)973 int32_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
SetBitmap(const void * bitMap,const uint8_t pictureId,const void * colorKey,const float left,const float top,const float right,const float bottom)985 int32_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
GetGraphicsMemory(uint64_t & totalMemory,uint64_t & availableMemory)1136 int32_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
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)1150 int32_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