[WinCairo][MediaFoundation] Video rendered at wrong position.
[WebKit-https.git] / Source / WebCore / platform / graphics / win / MediaPlayerPrivateMediaFoundation.cpp
1 /*
2  * Copyright (C) 2014 Alex Christensen <achristensen@webkit.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "MediaPlayerPrivateMediaFoundation.h"
29
30 #include "CachedResourceLoader.h"
31 #include "FrameView.h"
32 #include "GraphicsContext.h"
33 #include "HWndDC.h"
34 #include "HostWindow.h"
35 #include "NotImplemented.h"
36 #if USE(CAIRO)
37 #include "PlatformContextCairo.h"
38 #endif
39 #include "SoftLinking.h"
40
41 #if USE(MEDIA_FOUNDATION)
42
43 #include <wtf/MainThread.h>
44
45 SOFT_LINK_LIBRARY(Mf);
46 SOFT_LINK_OPTIONAL(Mf, MFCreateSourceResolver, HRESULT, STDAPICALLTYPE, (IMFSourceResolver**));
47 SOFT_LINK_OPTIONAL(Mf, MFCreateMediaSession, HRESULT, STDAPICALLTYPE, (IMFAttributes*, IMFMediaSession**));
48 SOFT_LINK_OPTIONAL(Mf, MFCreateTopology, HRESULT, STDAPICALLTYPE, (IMFTopology**));
49 SOFT_LINK_OPTIONAL(Mf, MFCreateTopologyNode, HRESULT, STDAPICALLTYPE, (MF_TOPOLOGY_TYPE, IMFTopologyNode**));
50 SOFT_LINK_OPTIONAL(Mf, MFGetService, HRESULT, STDAPICALLTYPE, (IUnknown*, REFGUID, REFIID, LPVOID*));
51 SOFT_LINK_OPTIONAL(Mf, MFCreateAudioRendererActivate, HRESULT, STDAPICALLTYPE, (IMFActivate**));
52 SOFT_LINK_OPTIONAL(Mf, MFCreateVideoRendererActivate, HRESULT, STDAPICALLTYPE, (HWND, IMFActivate**));
53 SOFT_LINK_OPTIONAL(Mf, MFCreateSampleGrabberSinkActivate, HRESULT, STDAPICALLTYPE, (IMFMediaType*, IMFSampleGrabberSinkCallback*, IMFActivate**));
54
55 SOFT_LINK_LIBRARY(Mfplat);
56 SOFT_LINK_OPTIONAL(Mfplat, MFStartup, HRESULT, STDAPICALLTYPE, (ULONG, DWORD));
57 SOFT_LINK_OPTIONAL(Mfplat, MFShutdown, HRESULT, STDAPICALLTYPE, ());
58 SOFT_LINK_OPTIONAL(Mfplat, MFCreateMemoryBuffer, HRESULT, STDAPICALLTYPE, (DWORD, IMFMediaBuffer**));
59 SOFT_LINK_OPTIONAL(Mfplat, MFCreateSample, HRESULT, STDAPICALLTYPE, (IMFSample**));
60 SOFT_LINK_OPTIONAL(Mfplat, MFCreateMediaType, HRESULT, STDAPICALLTYPE, (IMFMediaType**));
61 SOFT_LINK_OPTIONAL(Mfplat, MFFrameRateToAverageTimePerFrame, HRESULT, STDAPICALLTYPE, (UINT32, UINT32, UINT64*));
62
63 STDAPI MFCreateMediaType(_Outptr_ IMFMediaType**  ppMFType)
64 {
65     return MFCreateMediaTypePtr()(ppMFType);
66 }
67
68 SOFT_LINK_LIBRARY(evr);
69 SOFT_LINK_OPTIONAL(evr, MFCreateVideoSampleFromSurface, HRESULT, STDAPICALLTYPE, (IUnknown*, IMFSample**));
70
71 SOFT_LINK_LIBRARY(Dxva2);
72 SOFT_LINK_OPTIONAL(Dxva2, DXVA2CreateDirect3DDeviceManager9, HRESULT, STDAPICALLTYPE, (UINT*, IDirect3DDeviceManager9**));
73
74 SOFT_LINK_LIBRARY(D3d9);
75 SOFT_LINK_OPTIONAL(D3d9, Direct3DCreate9Ex, HRESULT, STDAPICALLTYPE, (UINT, IDirect3D9Ex**));
76
77 // MFSamplePresenterSampleCounter
78 // Data type: UINT32
79 //
80 // Version number for the video samples. When the presenter increments the version
81 // number, all samples with the previous version number are stale and should be
82 // discarded.
83 static const GUID MFSamplePresenterSampleCounter =
84 { 0x869f1f7c, 0x3496, 0x48a9, { 0x88, 0xe3, 0x69, 0x85, 0x79, 0xd0, 0x8c, 0xb6 } };
85
86 // MFSamplePresenterSampleSwapChain
87 // Data type: IUNKNOWN
88 // 
89 // Pointer to a Direct3D swap chain.
90 static const GUID MFSamplePresenterSampleSwapChain =
91 { 0x24a2e076, 0x3673, 0x433d, { 0x87, 0x4, 0x55, 0x2b, 0x1f, 0x5c, 0x16, 0x8c } };
92
93 namespace WebCore {
94
95 MediaPlayerPrivateMediaFoundation::MediaPlayerPrivateMediaFoundation(MediaPlayer* player) 
96     : m_player(player)
97     , m_visible(false)
98     , m_loadingProgress(false)
99     , m_paused(false)
100     , m_hasAudio(false)
101     , m_hasVideo(false)
102     , m_hwndVideo(nullptr)
103     , m_readyState(MediaPlayer::HaveNothing)
104     , m_weakPtrFactory(this)
105 {
106     createSession();
107     createVideoWindow();
108 }
109
110 MediaPlayerPrivateMediaFoundation::~MediaPlayerPrivateMediaFoundation()
111 {
112     notifyDeleted();
113     destroyVideoWindow();
114     endSession();
115 }
116
117 void MediaPlayerPrivateMediaFoundation::registerMediaEngine(MediaEngineRegistrar registrar)
118 {
119     if (isAvailable()) {
120         registrar([](MediaPlayer* player) { return std::make_unique<MediaPlayerPrivateMediaFoundation>(player); },
121             getSupportedTypes, supportsType, 0, 0, 0, 0);
122     }
123 }
124
125 bool MediaPlayerPrivateMediaFoundation::isAvailable() 
126 {
127     notImplemented();
128     return true;
129 }
130
131 void MediaPlayerPrivateMediaFoundation::getSupportedTypes(HashSet<String>& types)
132 {
133     types.add(String("video/mp4"));
134 }
135
136 MediaPlayer::SupportsType MediaPlayerPrivateMediaFoundation::supportsType(const MediaEngineSupportParameters& parameters)
137 {
138     if (parameters.type.isNull() || parameters.type.isEmpty())
139         return MediaPlayer::IsNotSupported;
140
141     if (parameters.type == "video/mp4")
142         return MediaPlayer::IsSupported;
143
144     return MediaPlayer::IsNotSupported;
145 }
146
147 void MediaPlayerPrivateMediaFoundation::load(const String& url)
148 {
149     startCreateMediaSource(url);
150 }
151
152 void MediaPlayerPrivateMediaFoundation::cancelLoad()
153 {
154     notImplemented();
155 }
156
157 void MediaPlayerPrivateMediaFoundation::play()
158 {
159     if (!m_mediaSession)
160         return;
161
162     PROPVARIANT varStart;
163     PropVariantInit(&varStart);
164     varStart.vt = VT_EMPTY;
165
166     HRESULT hr = m_mediaSession->Start(nullptr, &varStart);
167     ASSERT(SUCCEEDED(hr));
168
169     PropVariantClear(&varStart);
170
171     m_paused = !SUCCEEDED(hr);
172 }
173
174 void MediaPlayerPrivateMediaFoundation::pause()
175 {
176     if (!m_mediaSession)
177         return;
178
179     m_paused = SUCCEEDED(m_mediaSession->Pause());
180 }
181
182 FloatSize MediaPlayerPrivateMediaFoundation::naturalSize() const 
183 {
184     return m_size;
185 }
186
187 bool MediaPlayerPrivateMediaFoundation::hasVideo() const
188 {
189     return m_hasVideo;
190 }
191
192 bool MediaPlayerPrivateMediaFoundation::hasAudio() const
193 {
194     return m_hasAudio;
195 }
196
197 void MediaPlayerPrivateMediaFoundation::setVisible(bool visible)
198 {
199     m_visible = visible;
200 }
201
202 bool MediaPlayerPrivateMediaFoundation::seeking() const
203 {
204     // We assume seeking is immediately complete.
205     return false;
206 }
207
208 void MediaPlayerPrivateMediaFoundation::seekDouble(double time)
209 {
210     const double tenMegahertz = 10000000;
211     PROPVARIANT propVariant;
212     PropVariantInit(&propVariant);
213     propVariant.vt = VT_I8;
214     propVariant.hVal.QuadPart = static_cast<__int64>(time * tenMegahertz);
215     
216     HRESULT hr = m_mediaSession->Start(&GUID_NULL, &propVariant);
217     ASSERT(SUCCEEDED(hr));
218     PropVariantClear(&propVariant);
219 }
220
221 double MediaPlayerPrivateMediaFoundation::durationDouble() const
222 {
223     const double tenMegahertz = 10000000;
224     if (!m_mediaSource)
225         return 0;
226
227     IMFPresentationDescriptor* descriptor;
228     if (!SUCCEEDED(m_mediaSource->CreatePresentationDescriptor(&descriptor)))
229         return 0;
230     
231     UINT64 duration;
232     if (!SUCCEEDED(descriptor->GetUINT64(MF_PD_DURATION, &duration)))
233         duration = 0;
234     descriptor->Release();
235     
236     return static_cast<double>(duration) / tenMegahertz;
237 }
238
239 bool MediaPlayerPrivateMediaFoundation::paused() const
240 {
241     return m_paused;
242 }
243
244 MediaPlayer::NetworkState MediaPlayerPrivateMediaFoundation::networkState() const
245
246     notImplemented();
247     return MediaPlayer::Empty;
248 }
249
250 MediaPlayer::ReadyState MediaPlayerPrivateMediaFoundation::readyState() const
251 {
252     return m_readyState;
253 }
254
255 std::unique_ptr<PlatformTimeRanges> MediaPlayerPrivateMediaFoundation::buffered() const
256
257     notImplemented();
258     return std::make_unique<PlatformTimeRanges>();
259 }
260
261 bool MediaPlayerPrivateMediaFoundation::didLoadingProgress() const
262 {
263     return m_loadingProgress;
264 }
265
266 void MediaPlayerPrivateMediaFoundation::setSize(const IntSize& size)
267 {
268     m_size = size;
269
270     if (!m_videoDisplay)
271         return;
272
273     LayoutSize scrollOffset;
274     IntPoint positionInWindow(m_lastPaintRect.location());
275
276     FrameView* view = nullptr;
277     float deviceScaleFactor = 1.0f;
278     if (m_player && m_player->cachedResourceLoader() && m_player->cachedResourceLoader()->document()) {
279         view = m_player->cachedResourceLoader()->document()->view();
280         deviceScaleFactor = m_player->cachedResourceLoader()->document()->deviceScaleFactor();
281     }
282
283     if (view) {
284         scrollOffset = view->scrollOffsetForFixedPosition();
285         positionInWindow = view->convertToContainingWindow(IntPoint(m_lastPaintRect.location()));
286     }
287
288     positionInWindow.move(-scrollOffset.width().toInt(), -scrollOffset.height().toInt());
289
290     int x = positionInWindow.x() * deviceScaleFactor;
291     int y = positionInWindow.y() * deviceScaleFactor;
292     int w = m_size.width() * deviceScaleFactor;
293     int h = m_size.height() * deviceScaleFactor;
294
295     if (m_hwndVideo)
296         ::MoveWindow(m_hwndVideo, x, y, w, h, FALSE);
297
298     RECT rc = { 0, 0, w, h };
299     m_videoDisplay->SetVideoPosition(nullptr, &rc);
300 }
301
302 void MediaPlayerPrivateMediaFoundation::paint(GraphicsContext& context, const FloatRect& rect)
303 {
304     if (context.paintingDisabled() || !m_player->visible())
305         return;
306
307     m_lastPaintRect = rect;
308
309     if (m_presenter)
310         m_presenter->paintCurrentFrame(context, rect);
311 }
312
313 bool MediaPlayerPrivateMediaFoundation::createSession()
314 {
315     if (!MFStartupPtr() || !MFCreateMediaSessionPtr())
316         return false;
317
318     if (FAILED(MFStartupPtr()(MF_VERSION, MFSTARTUP_FULL)))
319         return false;
320
321     if (FAILED(MFCreateMediaSessionPtr()(nullptr, &m_mediaSession)))
322         return false;
323
324     // Get next event.
325     AsyncCallback* callback = new AsyncCallback(this, true);
326     HRESULT hr = m_mediaSession->BeginGetEvent(callback, nullptr);
327     ASSERT(SUCCEEDED(hr));
328
329     return true;
330 }
331
332 bool MediaPlayerPrivateMediaFoundation::endSession()
333 {
334     if (m_mediaSession) {
335         m_mediaSession->Shutdown();
336         m_mediaSession = nullptr;
337     }
338
339     if (!MFShutdownPtr())
340         return false;
341
342     HRESULT hr = MFShutdownPtr()();
343     ASSERT(SUCCEEDED(hr));
344
345     return true;
346 }
347
348 bool MediaPlayerPrivateMediaFoundation::startCreateMediaSource(const String& url)
349 {
350     if (!MFCreateSourceResolverPtr())
351         return false;
352
353     if (FAILED(MFCreateSourceResolverPtr()(&m_sourceResolver)))
354         return false;
355
356     COMPtr<IUnknown> cancelCookie;
357     Vector<UChar> urlSource = url.charactersWithNullTermination();
358
359     AsyncCallback* callback = new AsyncCallback(this, false);
360
361     if (FAILED(m_sourceResolver->BeginCreateObjectFromURL(urlSource.data(), MF_RESOLUTION_MEDIASOURCE, nullptr, &cancelCookie, callback, nullptr)))
362         return false;
363
364     return true;
365 }
366
367 bool MediaPlayerPrivateMediaFoundation::endCreatedMediaSource(IMFAsyncResult* asyncResult)
368 {
369     MF_OBJECT_TYPE objectType;
370     COMPtr<IUnknown> source;
371
372     HRESULT hr = m_sourceResolver->EndCreateObjectFromURL(asyncResult, &objectType, &source);
373     if (FAILED(hr))
374         return false;
375
376     hr = source->QueryInterface(IID_PPV_ARGS(&m_mediaSource));
377     if (FAILED(hr))
378         return false;
379
380     hr = asyncResult->GetStatus();
381     m_loadingProgress = SUCCEEDED(hr);
382
383     auto weakPtr = m_weakPtrFactory.createWeakPtr();
384     callOnMainThread([weakPtr] {
385         if (!weakPtr)
386             return;
387         weakPtr->onCreatedMediaSource();
388     });
389
390     return true;
391 }
392
393 bool MediaPlayerPrivateMediaFoundation::endGetEvent(IMFAsyncResult* asyncResult)
394 {
395     COMPtr<IMFMediaEvent> event;
396
397     if (!m_mediaSession)
398         return false;
399
400     // Get the event from the event queue.
401     HRESULT hr = m_mediaSession->EndGetEvent(asyncResult, &event);
402     if (FAILED(hr))
403         return false;
404
405     // Get the event type.
406     MediaEventType mediaEventType;
407     hr = event->GetType(&mediaEventType);
408     if (FAILED(hr))
409         return false;
410
411     switch (mediaEventType) {
412     case MESessionTopologySet: {
413         auto weakPtr = m_weakPtrFactory.createWeakPtr();
414         callOnMainThread([weakPtr] {
415             if (!weakPtr)
416                 return;
417             weakPtr->onTopologySet();
418         });
419         break;
420     }
421
422     case MESessionClosed:
423         break;
424
425     case MEMediaSample:
426         break;
427     }
428
429     if (mediaEventType != MESessionClosed) {
430         // For all other events, ask the media session for the
431         // next event in the queue.
432         AsyncCallback* callback = new AsyncCallback(this, true);
433
434         hr = m_mediaSession->BeginGetEvent(callback, nullptr);
435         if (FAILED(hr))
436             return false;
437     }
438
439     return true;
440 }
441
442 bool MediaPlayerPrivateMediaFoundation::createTopologyFromSource()
443 {
444     if (!MFCreateTopologyPtr())
445         return false;
446
447     // Create a new topology.
448     if (FAILED(MFCreateTopologyPtr()(&m_topology)))
449         return false;
450
451     // Create the presentation descriptor for the media source.
452     if (FAILED(m_mediaSource->CreatePresentationDescriptor(&m_sourcePD)))
453         return false;
454
455     // Get the number of streams in the media source.
456     DWORD sourceStreams = 0;
457     if (FAILED(m_sourcePD->GetStreamDescriptorCount(&sourceStreams)))
458         return false;
459
460     // For each stream, create the topology nodes and add them to the topology.
461     for (DWORD i = 0; i < sourceStreams; i++) {
462         if (!addBranchToPartialTopology(i))
463             return false;
464     }
465
466     return true;
467 }
468
469 bool MediaPlayerPrivateMediaFoundation::addBranchToPartialTopology(int stream)
470 {
471     // Get the stream descriptor for this stream.
472     COMPtr<IMFStreamDescriptor> sourceSD;
473     BOOL selected = FALSE;
474     if (FAILED(m_sourcePD->GetStreamDescriptorByIndex(stream, &selected, &sourceSD)))
475         return false;
476
477     // Create the topology branch only if the stream is selected.
478     // Otherwise, do nothing.
479     if (!selected)
480         return true;
481
482     // Create a source node for this stream.
483     COMPtr<IMFTopologyNode> sourceNode;
484     if (!createSourceStreamNode(sourceSD, sourceNode))
485         return false;
486
487     COMPtr<IMFTopologyNode> outputNode;
488     if (!createOutputNode(sourceSD, outputNode))
489         return false;
490
491     // Add both nodes to the topology.
492     if (FAILED(m_topology->AddNode(sourceNode.get())))
493         return false;
494
495     if (FAILED(m_topology->AddNode(outputNode.get())))
496         return false;
497
498     // Connect the source node to the output node.
499     if (FAILED(sourceNode->ConnectOutput(0, outputNode.get(), 0)))
500         return false;
501
502     return true;
503 }
504
505 LRESULT CALLBACK MediaPlayerPrivateMediaFoundation::VideoViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
506 {
507     return DefWindowProc(hWnd, message, wParam, lParam);
508 }
509
510 LPCWSTR MediaPlayerPrivateMediaFoundation::registerVideoWindowClass()
511 {
512     const LPCWSTR kVideoWindowClassName = L"WebVideoWindowClass";
513
514     static bool haveRegisteredWindowClass = false;
515     if (haveRegisteredWindowClass)
516         return kVideoWindowClassName;
517
518     haveRegisteredWindowClass = true;
519
520     WNDCLASSEX wcex;
521
522     wcex.cbSize = sizeof(WNDCLASSEX);
523
524     wcex.style = CS_DBLCLKS;
525     wcex.lpfnWndProc = VideoViewWndProc;
526     wcex.cbClsExtra = 0;
527     wcex.cbWndExtra = 0;
528     wcex.hInstance = nullptr;
529     wcex.hIcon = nullptr;
530     wcex.hCursor = ::LoadCursor(0, IDC_ARROW);
531     wcex.hbrBackground = nullptr;
532     wcex.lpszMenuName = nullptr;
533     wcex.lpszClassName = kVideoWindowClassName;
534     wcex.hIconSm = nullptr;
535
536     if (RegisterClassEx(&wcex))
537         return kVideoWindowClassName;
538
539     return nullptr;
540 }
541
542 void MediaPlayerPrivateMediaFoundation::createVideoWindow()
543 {
544     HWND hWndParent = nullptr;
545     FrameView* view = nullptr;
546     if (!m_player || !m_player->cachedResourceLoader() || !m_player->cachedResourceLoader()->document())
547         return;
548     view = m_player->cachedResourceLoader()->document()->view();
549     if (!view || !view->hostWindow())
550         return;
551     hWndParent = view->hostWindow()->platformPageClient();
552
553     m_hwndVideo = CreateWindowEx(WS_EX_NOACTIVATE | WS_EX_TRANSPARENT, registerVideoWindowClass(), 0, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
554         0, 0, 0, 0, hWndParent, 0, 0, 0);
555 }
556
557 void MediaPlayerPrivateMediaFoundation::destroyVideoWindow()
558 {
559     if (m_hwndVideo) {
560         DestroyWindow(m_hwndVideo);
561         m_hwndVideo = nullptr;
562     }
563 }
564
565 void MediaPlayerPrivateMediaFoundation::invalidateFrameView()
566 {
567     FrameView* view = nullptr;
568     if (!m_player || !m_player->cachedResourceLoader() || !m_player->cachedResourceLoader()->document())
569         return;
570     view = m_player->cachedResourceLoader()->document()->view();
571     if (!view)
572         return;
573
574     view->invalidate();
575 }
576
577 void MediaPlayerPrivateMediaFoundation::addListener(MediaPlayerListener* listener)
578 {
579     LockHolder locker(m_mutexListeners);
580
581     m_listeners.add(listener);
582 }
583
584 void MediaPlayerPrivateMediaFoundation::removeListener(MediaPlayerListener* listener)
585 {
586     LockHolder locker(m_mutexListeners);
587
588     m_listeners.remove(listener);
589 }
590
591 void MediaPlayerPrivateMediaFoundation::notifyDeleted()
592 {
593     LockHolder locker(m_mutexListeners);
594
595     for (HashSet<MediaPlayerListener*>::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
596         (*it)->onMediaPlayerDeleted();
597 }
598
599 bool MediaPlayerPrivateMediaFoundation::createOutputNode(COMPtr<IMFStreamDescriptor> sourceSD, COMPtr<IMFTopologyNode>& node)
600 {
601     if (!MFCreateTopologyNodePtr() || !MFCreateAudioRendererActivatePtr() || !MFCreateVideoRendererActivatePtr())
602         return false;
603
604     if (!sourceSD)
605         return false;
606
607 #ifndef NDEBUG
608     // Get the stream ID.
609     DWORD streamID = 0;
610     sourceSD->GetStreamIdentifier(&streamID); // Just for debugging, ignore any failures.
611 #endif
612
613     COMPtr<IMFMediaTypeHandler> handler;
614     if (FAILED(sourceSD->GetMediaTypeHandler(&handler)))
615         return false;
616
617     GUID guidMajorType = GUID_NULL;
618     if (FAILED(handler->GetMajorType(&guidMajorType)))
619         return false;
620
621     // Create a downstream node.
622     if (FAILED(MFCreateTopologyNodePtr()(MF_TOPOLOGY_OUTPUT_NODE, &node)))
623         return false;
624
625     // Create an IMFActivate object for the renderer, based on the media type.
626     COMPtr<IMFActivate> rendererActivate;
627     if (MFMediaType_Audio == guidMajorType) {
628         // Create the audio renderer.
629         if (FAILED(MFCreateAudioRendererActivatePtr()(&rendererActivate)))
630             return false;
631         m_hasAudio = true;
632     } else if (MFMediaType_Video == guidMajorType) {
633         // Create the video renderer.
634         if (FAILED(MFCreateVideoRendererActivatePtr()(nullptr, &rendererActivate)))
635             return false;
636
637         m_presenter = new CustomVideoPresenter(this);
638         m_presenter->SetVideoWindow(m_hwndVideo);
639         if (FAILED(rendererActivate->SetUnknown(MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_ACTIVATE, static_cast<IMFActivate*>(m_presenter.get()))))
640             return false;
641         m_hasVideo = true;
642     } else
643         return false;
644
645     // Set the IActivate object on the output node.
646     if (FAILED(node->SetObject(rendererActivate.get())))
647         return false;
648
649     return true;
650 }
651
652 bool MediaPlayerPrivateMediaFoundation::createSourceStreamNode(COMPtr<IMFStreamDescriptor> sourceSD, COMPtr<IMFTopologyNode>& node)
653 {
654     if (!MFCreateTopologyNodePtr())
655         return false;
656
657     if (!m_mediaSource || !m_sourcePD || !sourceSD)
658         return false;
659
660     // Create the source-stream node.
661     HRESULT hr = MFCreateTopologyNodePtr()(MF_TOPOLOGY_SOURCESTREAM_NODE, &node);
662     if (FAILED(hr))
663         return false;
664
665     // Set attribute: Pointer to the media source.
666     hr = node->SetUnknown(MF_TOPONODE_SOURCE, m_mediaSource.get());
667     if (FAILED(hr))
668         return false;
669
670     // Set attribute: Pointer to the presentation descriptor.
671     hr = node->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, m_sourcePD.get());
672     if (FAILED(hr))
673         return false;
674
675     // Set attribute: Pointer to the stream descriptor.
676     hr = node->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, sourceSD.get());
677     if (FAILED(hr))
678         return false;
679
680     return true;
681 }
682
683 void MediaPlayerPrivateMediaFoundation::onCreatedMediaSource()
684 {
685     if (!createTopologyFromSource())
686         return;
687
688     // Set the topology on the media session.
689     HRESULT hr = m_mediaSession->SetTopology(0, m_topology.get());
690     ASSERT(SUCCEEDED(hr));
691 }
692
693 void MediaPlayerPrivateMediaFoundation::onTopologySet()
694 {
695     if (!MFGetServicePtr())
696         return;
697
698     if (SUCCEEDED(MFGetServicePtr()(m_mediaSession.get(), MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_videoDisplay)))) {
699         ASSERT(m_videoDisplay);
700         RECT rc = { 0, 0, m_size.width(), m_size.height() };
701         m_videoDisplay->SetVideoPosition(nullptr, &rc);
702     }
703
704     m_readyState = MediaPlayer::HaveFutureData;
705
706     ASSERT(m_player);
707     m_player->readyStateChanged();
708
709     play();
710     m_player->playbackStateChanged();
711 }
712
713 MediaPlayerPrivateMediaFoundation::AsyncCallback::AsyncCallback(MediaPlayerPrivateMediaFoundation* mediaPlayer, bool event)
714     : m_refCount(0)
715     , m_mediaPlayer(mediaPlayer)
716     , m_event(event)
717 {
718     if (m_mediaPlayer)
719         m_mediaPlayer->addListener(this);
720 }
721
722 MediaPlayerPrivateMediaFoundation::AsyncCallback::~AsyncCallback()
723 {
724     if (m_mediaPlayer)
725         m_mediaPlayer->removeListener(this);
726 }
727
728 HRESULT MediaPlayerPrivateMediaFoundation::AsyncCallback::QueryInterface(REFIID riid, __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject)
729 {
730     if (!IsEqualGUID(riid, IID_IMFAsyncCallback)) {
731         *ppvObject = nullptr;
732         return E_NOINTERFACE;
733     }
734     *ppvObject = this;
735     AddRef();
736     return S_OK;
737 }
738
739 ULONG STDMETHODCALLTYPE MediaPlayerPrivateMediaFoundation::AsyncCallback::AddRef()
740 {
741     m_refCount++;
742     return m_refCount;
743 }
744
745 ULONG STDMETHODCALLTYPE MediaPlayerPrivateMediaFoundation::AsyncCallback::Release()
746 {
747     m_refCount--;
748     ULONG refCount = m_refCount;
749     if (!refCount)
750         delete this;
751     return refCount;
752 }
753
754 HRESULT STDMETHODCALLTYPE MediaPlayerPrivateMediaFoundation::AsyncCallback::GetParameters(__RPC__out DWORD *pdwFlags, __RPC__out DWORD *pdwQueue)
755 {
756     // Returning E_NOTIMPL gives default values.
757     return E_NOTIMPL;
758 }
759
760 HRESULT STDMETHODCALLTYPE MediaPlayerPrivateMediaFoundation::AsyncCallback::Invoke(__RPC__in_opt IMFAsyncResult *pAsyncResult)
761 {
762     LockHolder locker(m_mutex);
763
764     if (!m_mediaPlayer)
765         return S_OK;
766
767     if (m_event)
768         m_mediaPlayer->endGetEvent(pAsyncResult);
769     else
770         m_mediaPlayer->endCreatedMediaSource(pAsyncResult);
771
772     return S_OK;
773 }
774
775 void MediaPlayerPrivateMediaFoundation::AsyncCallback::onMediaPlayerDeleted()
776 {
777     LockHolder locker(m_mutex);
778
779     m_mediaPlayer = nullptr;
780 }
781
782 MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::CustomVideoPresenter(MediaPlayerPrivateMediaFoundation* mediaPlayer)
783     : m_mediaPlayer(mediaPlayer)
784 {
785     if (m_mediaPlayer)
786         m_mediaPlayer->addListener(this);
787
788     m_sourceRect.top = 0;
789     m_sourceRect.left = 0;
790     m_sourceRect.bottom = 1;
791     m_sourceRect.right = 1;
792
793     m_presenterEngine = std::make_unique<Direct3DPresenter>();
794     if (!m_presenterEngine)
795         return;
796
797     m_scheduler.setPresenter(m_presenterEngine.get());
798 }
799
800 MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::~CustomVideoPresenter()
801 {
802     if (m_mediaPlayer)
803         m_mediaPlayer->removeListener(this);
804 }
805
806 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::QueryInterface(REFIID riid, __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject)
807 {
808     *ppvObject = nullptr;
809     if (IsEqualGUID(riid, IID_IMFGetService))
810         *ppvObject = static_cast<IMFGetService*>(this);
811     else if (IsEqualGUID(riid, IID_IMFActivate))
812         *ppvObject = static_cast<IMFActivate*>(this);
813     else if (IsEqualGUID(riid, IID_IMFVideoDisplayControl))
814         *ppvObject = static_cast<IMFVideoDisplayControl*>(this);
815     else if (IsEqualGUID(riid, IID_IMFVideoPresenter))
816         *ppvObject = static_cast<IMFVideoPresenter*>(this);
817     else if (IsEqualGUID(riid, IID_IMFClockStateSink))
818         *ppvObject = static_cast<IMFClockStateSink*>(this);
819     else if (IsEqualGUID(riid, IID_IMFVideoDeviceID))
820         *ppvObject = static_cast<IMFVideoDeviceID*>(this);
821     else if (IsEqualGUID(riid, IID_IMFTopologyServiceLookupClient))
822         *ppvObject = static_cast<IMFTopologyServiceLookupClient*>(this);
823     else if (IsEqualGUID(riid, IID_IUnknown))
824         *ppvObject = static_cast<IMFVideoPresenter*>(this);
825     else if (IsEqualGUID(riid, IID_IMFAsyncCallback))
826         *ppvObject = static_cast<IMFAsyncCallback*>(this);
827     else
828         return E_NOINTERFACE;
829
830     AddRef();
831     return S_OK;
832 }
833
834 ULONG MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::AddRef()
835 {
836     m_refCount++;
837     return m_refCount;
838 }
839
840 ULONG MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::Release()
841 {
842     m_refCount--;
843     ULONG refCount = m_refCount;
844     if (!refCount)
845         delete this;
846     return refCount;
847 }
848
849 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset)
850 {
851     LockHolder locker(m_lock);
852
853     // After shutdown, we cannot start.
854     HRESULT hr = checkShutdown();
855     if (FAILED(hr))
856         return hr;
857
858     m_renderState = RenderStateStarted;
859
860     if (isActive()) {
861         if (llClockStartOffset != PRESENTATION_CURRENT_POSITION) {
862             // This is a seek request, flush pending samples.
863             flush();
864         }
865     }
866
867     processOutputLoop();
868
869     return S_OK;
870 }
871
872 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::OnClockStop(MFTIME hnsSystemTime)
873 {
874     LockHolder locker(m_lock);
875
876     HRESULT hr = checkShutdown();
877     if (FAILED(hr))
878         return hr;
879
880     if (m_renderState != RenderStateStopped) {
881         m_renderState = RenderStateStopped;
882         flush();
883     }
884
885     return S_OK;
886 }
887
888 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::OnClockPause(MFTIME hnsSystemTime)
889 {
890     LockHolder locker(m_lock);
891
892     // After shutdown, we cannot pause.
893     HRESULT hr = checkShutdown();
894     if (FAILED(hr))
895         return hr;
896
897     m_renderState = RenderStatePaused;
898
899     return S_OK;
900 }
901
902 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::OnClockRestart(MFTIME hnsSystemTime)
903 {
904     LockHolder locker(m_lock);
905
906     HRESULT hr = checkShutdown();
907     if (FAILED(hr))
908         return hr;
909
910     ASSERT(m_renderState == RenderStatePaused);
911
912     m_renderState = RenderStateStarted;
913
914     processOutputLoop();
915
916     return S_OK;
917 }
918
919 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::OnClockSetRate(MFTIME hnsSystemTime, float rate)
920 {
921     LockHolder locker(m_lock);
922
923     HRESULT hr = checkShutdown();
924     if (FAILED(hr))
925         return hr;
926
927     m_rate = rate;
928
929     m_scheduler.setClockRate(rate);
930
931     return S_OK;
932 }
933
934 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::ProcessMessage(MFVP_MESSAGE_TYPE eMessage, ULONG_PTR ulParam)
935 {
936     LockHolder locker(m_lock);
937
938     HRESULT hr = checkShutdown();
939     if (FAILED(hr))
940         return hr;
941
942     switch (eMessage) {
943     case MFVP_MESSAGE_FLUSH:
944         hr = flush();
945         break;
946
947     case MFVP_MESSAGE_INVALIDATEMEDIATYPE:
948         hr = renegotiateMediaType();
949         break;
950
951     case MFVP_MESSAGE_PROCESSINPUTNOTIFY:
952         // A new input sample is available. 
953         hr = processInputNotify();
954         break;
955
956     case MFVP_MESSAGE_BEGINSTREAMING:
957         hr = beginStreaming();
958         break;
959
960     case MFVP_MESSAGE_ENDSTREAMING:
961         hr = endStreaming();
962         break;
963
964     case MFVP_MESSAGE_ENDOFSTREAM:
965         m_endStreaming = true;
966         hr = checkEndOfStream();
967         break;
968
969     default:
970         hr = E_INVALIDARG;
971         break;
972     }
973
974     return hr;
975 }
976
977 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::GetCurrentMediaType(_Outptr_  IMFVideoMediaType **ppMediaType)
978 {
979     LockHolder locker(m_lock);
980
981     if (!ppMediaType)
982         return E_POINTER;
983
984     HRESULT hr = checkShutdown();
985     if (FAILED(hr))
986         return hr;
987
988     if (!m_mediaType)
989         return MF_E_NOT_INITIALIZED;
990
991     return m_mediaType->QueryInterface(__uuidof(IMFVideoMediaType), (void**)&ppMediaType);
992 }
993
994 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::GetDeviceID(IID* pDeviceID)
995 {
996     if (!pDeviceID)
997         return E_POINTER;
998
999     *pDeviceID = __uuidof(IDirect3DDevice9);
1000     return S_OK;
1001 }
1002
1003 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::InitServicePointers(IMFTopologyServiceLookup *pLookup)
1004 {
1005     if (!pLookup)
1006         return E_POINTER;
1007
1008     HRESULT hr = S_OK;
1009
1010     LockHolder locker(m_lock);
1011
1012     if (isActive())
1013         return MF_E_INVALIDREQUEST;
1014
1015     m_clock = nullptr;
1016     m_mixer = nullptr;
1017     m_mediaEventSink = nullptr;
1018
1019     // Lookup the services.
1020
1021     DWORD objectCount = 1;
1022     hr = pLookup->LookupService(MF_SERVICE_LOOKUP_GLOBAL, 0, MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_clock), &objectCount);
1023     // The clock service is optional.
1024
1025     objectCount = 1;
1026     hr = pLookup->LookupService(MF_SERVICE_LOOKUP_GLOBAL, 0, MR_VIDEO_MIXER_SERVICE, IID_PPV_ARGS(&m_mixer), &objectCount);
1027     if (FAILED(hr))
1028         return hr;
1029
1030     hr = configureMixer(m_mixer.get());
1031     if (FAILED(hr))
1032         return hr;
1033
1034     objectCount = 1;
1035     hr = pLookup->LookupService(MF_SERVICE_LOOKUP_GLOBAL, 0, MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_mediaEventSink), &objectCount);
1036     if (FAILED(hr))
1037         return hr;
1038
1039     m_renderState = RenderStateStopped;
1040
1041     return S_OK;
1042 }
1043
1044 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::ReleaseServicePointers()
1045 {
1046     LockHolder locker(m_lock);
1047
1048     m_renderState = RenderStateShutdown;
1049
1050     flush();
1051
1052     setMediaType(nullptr);
1053
1054     m_clock = nullptr;
1055     m_mixer = nullptr;
1056     m_mediaEventSink = nullptr;
1057
1058     return S_OK;
1059 }
1060
1061 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::GetService(REFGUID guidService, REFIID riid, LPVOID* ppvObject)
1062 {
1063     if (!ppvObject)
1064         return E_POINTER;
1065
1066     // We only support MR_VIDEO_RENDER_SERVICE.
1067     if (guidService != MR_VIDEO_RENDER_SERVICE)
1068         return MF_E_UNSUPPORTED_SERVICE;
1069
1070     HRESULT hr = m_presenterEngine->getService(guidService, riid, ppvObject);
1071
1072     if (FAILED(hr))
1073         hr = QueryInterface(riid, ppvObject);
1074
1075     return hr;
1076 }
1077
1078 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::ActivateObject(REFIID riid, void **ppv)
1079 {
1080     if (!ppv)
1081         return E_POINTER;
1082
1083     if (riid == IID_IMFVideoPresenter) {
1084         *ppv = static_cast<IMFVideoPresenter*>(this);
1085         return S_OK;
1086     }
1087     return E_FAIL;
1088 }
1089
1090 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::DetachObject()
1091 {
1092     return S_OK;
1093 }
1094
1095 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::ShutdownObject()
1096 {
1097     return S_OK;
1098 }
1099
1100 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::SetVideoWindow(HWND hwndVideo)
1101 {
1102     LockHolder locker(m_lock);
1103
1104     if (!IsWindow(hwndVideo))
1105         return E_INVALIDARG;
1106
1107     HRESULT hr = S_OK;
1108     HWND oldHwnd = m_presenterEngine->getVideoWindow();
1109
1110     if (oldHwnd != hwndVideo) {
1111         // This will create a new Direct3D device.
1112         hr = m_presenterEngine->setVideoWindow(hwndVideo);
1113
1114         notifyEvent(EC_DISPLAY_CHANGED, 0, 0);
1115     }
1116
1117     return hr;
1118 }
1119
1120 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::GetVideoWindow(HWND* phwndVideo)
1121 {
1122     LockHolder locker(m_lock);
1123
1124     if (!phwndVideo)
1125         return E_POINTER;
1126
1127     *phwndVideo = m_presenterEngine->getVideoWindow();
1128
1129     return S_OK;
1130 }
1131
1132 static HRESULT setMixerSourceRect(IMFTransform* mixer, const MFVideoNormalizedRect& sourceRect)
1133 {
1134     if (!mixer)
1135         return E_POINTER;
1136
1137     COMPtr<IMFAttributes> attributes;
1138
1139     HRESULT hr = mixer->GetAttributes(&attributes);
1140     if (FAILED(hr))
1141         return hr;
1142
1143     return attributes->SetBlob(VIDEO_ZOOM_RECT, (const UINT8*)&sourceRect, sizeof(sourceRect));
1144 }
1145
1146 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::SetVideoPosition(const MFVideoNormalizedRect* pnrcSource, const LPRECT prcDest)
1147 {
1148     LockHolder locker(m_lock);
1149
1150     // First, check that the parameters are valid.
1151
1152     if (!pnrcSource && !prcDest)
1153         return E_POINTER;
1154
1155     if (pnrcSource) {
1156         if ((pnrcSource->left > pnrcSource->right) || (pnrcSource->top > pnrcSource->bottom))
1157             return E_INVALIDARG;
1158
1159         // The source rectangle must be normalized.
1160         if ((pnrcSource->left < 0) || (pnrcSource->right > 1) || (pnrcSource->top < 0) || (pnrcSource->bottom > 1))
1161             return E_INVALIDARG;
1162     }
1163
1164     if (prcDest) {
1165         if ((prcDest->left > prcDest->right) || (prcDest->top > prcDest->bottom))
1166             return E_INVALIDARG;
1167     }
1168
1169     HRESULT hr = S_OK;
1170
1171     // Set the source rectangle.
1172     if (pnrcSource) {
1173         m_sourceRect = *pnrcSource;
1174
1175         if (m_mixer) {
1176             hr = setMixerSourceRect(m_mixer.get(), m_sourceRect);
1177             if (FAILED(hr))
1178                 return hr;
1179         }
1180     }
1181
1182     // Set the destination rectangle.
1183     if (prcDest) {
1184         RECT rcOldDest = m_presenterEngine->getDestinationRect();
1185
1186         // If the destination rectangle hasn't changed, we are done.
1187         if (!EqualRect(&rcOldDest, prcDest)) {
1188             hr = m_presenterEngine->setDestinationRect(*prcDest);
1189             if (FAILED(hr))
1190                 return hr;
1191
1192             // We need to change the media type when the destination rectangle has changed.
1193             if (m_mixer) {
1194                 hr = renegotiateMediaType();
1195                 if (hr == MF_E_TRANSFORM_TYPE_NOT_SET) {
1196                     // This is not a critical failure; the EVR will let us know when
1197                     // we have to set the mixer media type.
1198                     hr = S_OK;
1199                 } else {
1200                     if (FAILED(hr))
1201                         return hr;
1202
1203                     // We have successfully changed the media type,
1204                     // ask for a repaint of the current frame.
1205                     m_repaint = true;
1206                     processOutput();
1207                 }
1208             }
1209         }
1210     }
1211
1212     return S_OK;
1213 }
1214
1215 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::GetVideoPosition(MFVideoNormalizedRect* pnrcSource, LPRECT prcDest)
1216 {
1217     LockHolder locker(m_lock);
1218
1219     if (!pnrcSource || !prcDest)
1220         return E_POINTER;
1221
1222     *pnrcSource = m_sourceRect;
1223     *prcDest = m_presenterEngine->getDestinationRect();
1224
1225     return S_OK;
1226 }
1227
1228 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::RepaintVideo()
1229 {
1230     LockHolder locker(m_lock);
1231
1232     HRESULT hr = checkShutdown();
1233     if (FAILED(hr))
1234         return hr;
1235
1236     // Check that at least one sample has been presented.
1237     if (m_prerolled) {
1238         m_repaint = true;
1239         processOutput();
1240     }
1241
1242     return S_OK;
1243 }
1244
1245 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::Invoke(IMFAsyncResult* pAsyncResult)
1246 {
1247     return onSampleFree(pAsyncResult);
1248 }
1249
1250 void MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::onMediaPlayerDeleted()
1251 {
1252     m_mediaPlayer = nullptr;
1253 }
1254
1255 void MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::paintCurrentFrame(GraphicsContext& context, const FloatRect& r)
1256 {
1257     if (m_presenterEngine)
1258         m_presenterEngine->paintCurrentFrame(context, r);
1259 }
1260
1261 bool MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::isActive() const
1262 {
1263     return ((m_renderState == RenderStateStarted) || (m_renderState == RenderStatePaused));
1264 }
1265
1266 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::configureMixer(IMFTransform* mixer)
1267 {
1268     COMPtr<IMFVideoDeviceID> videoDeviceID;
1269     HRESULT hr = mixer->QueryInterface(__uuidof(IMFVideoDeviceID), (void**)&videoDeviceID);
1270     if (FAILED(hr))
1271         return hr;
1272
1273     IID deviceID = GUID_NULL;
1274     hr = videoDeviceID->GetDeviceID(&deviceID);
1275     if (FAILED(hr))
1276         return hr;
1277
1278     // The mixer must have this device ID.
1279     if (!IsEqualGUID(deviceID, __uuidof(IDirect3DDevice9)))
1280         return MF_E_INVALIDREQUEST;
1281
1282     setMixerSourceRect(mixer, m_sourceRect);
1283
1284     return S_OK;
1285 }
1286
1287 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::flush()
1288 {
1289     m_prerolled = false;
1290
1291     // Flush the sceduler.
1292     // This call will block until the scheduler thread has finished flushing.
1293     m_scheduler.flush();
1294
1295     if (m_renderState == RenderStateStopped)
1296         m_presenterEngine->presentSample(nullptr, 0);
1297
1298     return S_OK;
1299 }
1300
1301 static bool areMediaTypesEqual(IMFMediaType* type1, IMFMediaType* type2)
1302 {
1303     if (!type1 && !type2)
1304         return true;
1305     if (!type1 || !type2)
1306         return false;
1307
1308     DWORD flags = 0;
1309     return S_OK == type1->IsEqual(type2, &flags);
1310 }
1311
1312 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::setMediaType(IMFMediaType* mediaType)
1313 {
1314     if (!mediaType) {
1315         m_mediaType = nullptr;
1316         releaseResources();
1317         return S_OK;
1318     }
1319
1320     // If we have shut down, we cannot set the media type.
1321     HRESULT hr = checkShutdown();
1322     if (FAILED(hr)) {
1323         releaseResources();
1324         return hr;
1325     }
1326
1327     if (areMediaTypesEqual(m_mediaType.get(), mediaType))
1328         return S_OK;
1329
1330     m_mediaType = nullptr;
1331     releaseResources();
1332
1333     // Get allocated samples from the presenter.
1334     VideoSampleList sampleQueue;
1335     hr = m_presenterEngine->createVideoSamples(mediaType, sampleQueue);
1336     if (FAILED(hr)) {
1337         releaseResources();
1338         return hr;
1339     }
1340
1341     // Set the token counter on each sample.
1342     // This will help us to determine when they are invalid, and can be released.
1343     for (auto sample : sampleQueue) {
1344         hr = sample->SetUINT32(MFSamplePresenterSampleCounter, m_tokenCounter);
1345         if (FAILED(hr)) {
1346             releaseResources();
1347             return hr;
1348         }
1349     }
1350
1351     // Add the samples to the sample pool.
1352     hr = m_samplePool.initialize(sampleQueue);
1353     if (FAILED(hr)) {
1354         releaseResources();
1355         return hr;
1356     }
1357
1358     // Set the frame rate. 
1359     MFRatio fps = { 0, 0 };
1360     hr = MFGetAttributeRatio(mediaType, MF_MT_FRAME_RATE, (UINT32*)&fps.Numerator, (UINT32*)&fps.Denominator);
1361     if (SUCCEEDED(hr) && fps.Numerator && fps.Denominator)
1362         m_scheduler.setFrameRate(fps);
1363     else {
1364         // We could not get the frame ret, use default.
1365         const MFRatio defaultFrameRate = { 30, 1 };
1366         m_scheduler.setFrameRate(defaultFrameRate);
1367     }
1368
1369     ASSERT(mediaType);
1370     m_mediaType = mediaType;
1371
1372     return S_OK;
1373 }
1374
1375 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::checkShutdown() const
1376 {
1377     if (m_renderState == RenderStateShutdown)
1378         return MF_E_SHUTDOWN;
1379     return S_OK;
1380 }
1381
1382 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::renegotiateMediaType()
1383 {
1384     HRESULT hr = S_OK;
1385
1386     if (!m_mixer)
1387         return MF_E_INVALIDREQUEST;
1388
1389     // Iterate over the available output types of the mixer.
1390
1391     DWORD typeIndex = 0;
1392     bool foundMediaType = false;
1393     while (!foundMediaType && (hr != MF_E_NO_MORE_TYPES)) {
1394         // Get the next available media type.
1395         COMPtr<IMFMediaType> mixerType;
1396         hr = m_mixer->GetOutputAvailableType(0, typeIndex++, &mixerType);
1397         if (FAILED(hr))
1398             break;
1399
1400         // Do we support this media type?
1401         hr = isMediaTypeSupported(mixerType.get());
1402         if (FAILED(hr))
1403             break;
1404
1405         // Make adjustments to proposed media type.
1406         COMPtr<IMFMediaType> optimalType;
1407         hr = createOptimalVideoType(mixerType.get(), &optimalType);
1408         if (FAILED(hr))
1409             break;
1410
1411         // Test whether the mixer can accept the modified media type
1412         hr = m_mixer->SetOutputType(0, optimalType.get(), MFT_SET_TYPE_TEST_ONLY);
1413         if (FAILED(hr))
1414             break;
1415
1416         // Try to set the new media type
1417
1418         hr = setMediaType(optimalType.get());
1419         if (FAILED(hr))
1420             break;
1421
1422         hr = m_mixer->SetOutputType(0, optimalType.get(), 0);
1423
1424         ASSERT(SUCCEEDED(hr));
1425
1426         if (FAILED(hr))
1427             setMediaType(nullptr);
1428         else
1429             foundMediaType = true;
1430     }
1431
1432     return hr;
1433 }
1434
1435 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::processInputNotify()
1436 {
1437     // We have a new sample.
1438     m_sampleNotify = true;
1439
1440     if (!m_mediaType) {
1441         // The media type is not valid.
1442         return MF_E_TRANSFORM_TYPE_NOT_SET;
1443     } 
1444     
1445     // Invalidate the video area
1446     if (m_mediaPlayer) {
1447         auto weakPtr = m_mediaPlayer->m_weakPtrFactory.createWeakPtr();
1448         callOnMainThread([weakPtr] {
1449             if (weakPtr)
1450                 weakPtr->invalidateFrameView();
1451         });
1452     }
1453
1454     // Process sample
1455     processOutputLoop();
1456
1457     return S_OK;
1458 }
1459
1460 static float MFOffsetToFloat(const MFOffset& offset)
1461 {
1462     const int denominator = std::numeric_limits<WORD>::max() + 1;
1463     return offset.value + (float(offset.fract) / denominator);
1464 }
1465
1466 static MFOffset MakeOffset(float v)
1467 {
1468     // v = offset.value + (offset.fract / denominator), where denominator = 65536.0f.
1469     const int denominator = std::numeric_limits<WORD>::max() + 1;
1470     MFOffset offset;
1471     offset.value = short(v);
1472     offset.fract = WORD(denominator * (v - offset.value));
1473     return offset;
1474 }
1475
1476 static MFVideoArea MakeArea(float x, float y, DWORD width, DWORD height)
1477 {
1478     MFVideoArea area;
1479     area.OffsetX = MakeOffset(x);
1480     area.OffsetY = MakeOffset(y);
1481     area.Area.cx = width;
1482     area.Area.cy = height;
1483     return area;
1484 }
1485
1486 static HRESULT validateVideoArea(const MFVideoArea& area, UINT32 width, UINT32 height)
1487 {
1488     float fOffsetX = MFOffsetToFloat(area.OffsetX);
1489     float fOffsetY = MFOffsetToFloat(area.OffsetY);
1490
1491     if (((LONG)fOffsetX + area.Area.cx > width) || ((LONG)fOffsetY + area.Area.cy > height))
1492         return MF_E_INVALIDMEDIATYPE;
1493     return S_OK;
1494 }
1495
1496 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::beginStreaming()
1497 {
1498     return m_scheduler.startScheduler(m_clock.get());
1499 }
1500
1501 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::endStreaming()
1502 {
1503     return m_scheduler.stopScheduler();
1504 }
1505
1506 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::checkEndOfStream()
1507 {
1508     if (!m_endStreaming) {
1509         // We have not received the end-of-stream message from the EVR.
1510         return S_OK;
1511     }
1512
1513     if (m_sampleNotify) {
1514         // There is still input samples available for the mixer. 
1515         return S_OK;
1516     }
1517
1518     if (m_samplePool.areSamplesPending()) {
1519         // There are samples scheduled for rendering.
1520         return S_OK;
1521     }
1522
1523     // We are done, notify the EVR.
1524     notifyEvent(EC_COMPLETE, (LONG_PTR)S_OK, 0);
1525     m_endStreaming = false;
1526     return S_OK;
1527 }
1528
1529 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::isMediaTypeSupported(IMFMediaType* mediaType)
1530 {
1531     COMPtr<IMFMediaType> proposedVideoType = mediaType;
1532
1533     // We don't support compressed media types.
1534     BOOL compressed = FALSE;
1535     HRESULT hr = proposedVideoType->IsCompressedFormat(&compressed);
1536     if (FAILED(hr))
1537         return hr;
1538     if (compressed)
1539         return MF_E_INVALIDMEDIATYPE;
1540
1541     // Validate the format.
1542     GUID guidSubType = GUID_NULL;
1543     hr = proposedVideoType->GetGUID(MF_MT_SUBTYPE, &guidSubType);
1544     if (FAILED(hr))
1545         return hr;
1546     D3DFORMAT d3dFormat = (D3DFORMAT)guidSubType.Data1;
1547
1548     // Check if the format can be used as backbuffer format.
1549     hr = m_presenterEngine->checkFormat(d3dFormat);
1550     if (FAILED(hr))
1551         return hr;
1552
1553     // Check interlaced formats.
1554     MFVideoInterlaceMode interlaceMode = MFVideoInterlace_Unknown;
1555     hr = proposedVideoType->GetUINT32(MF_MT_INTERLACE_MODE, (UINT32*)&interlaceMode);
1556     if (FAILED(hr))
1557         return hr;
1558
1559     if (interlaceMode != MFVideoInterlace_Progressive)
1560         return MF_E_INVALIDMEDIATYPE;
1561
1562     UINT32 width = 0, height = 0;
1563     hr = MFGetAttributeSize(proposedVideoType.get(), MF_MT_FRAME_SIZE, &width, &height);
1564     if (FAILED(hr))
1565         return hr;
1566
1567     // Validate apertures.
1568     MFVideoArea videoCropArea;
1569     if (SUCCEEDED(proposedVideoType->GetBlob(MF_MT_PAN_SCAN_APERTURE, (UINT8*)&videoCropArea, sizeof(MFVideoArea), nullptr)))
1570         validateVideoArea(videoCropArea, width, height);
1571     if (SUCCEEDED(proposedVideoType->GetBlob(MF_MT_GEOMETRIC_APERTURE, (UINT8*)&videoCropArea, sizeof(MFVideoArea), nullptr)))
1572         validateVideoArea(videoCropArea, width, height);
1573     if (SUCCEEDED(proposedVideoType->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, (UINT8*)&videoCropArea, sizeof(MFVideoArea), nullptr)))
1574         validateVideoArea(videoCropArea, width, height);
1575     
1576     return S_OK;
1577 }
1578
1579 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::createOptimalVideoType(IMFMediaType* proposedType, IMFMediaType** optimalType)
1580 {
1581     COMPtr<IMFMediaType> optimalVideoType;
1582     HRESULT hr = MFCreateMediaType(&optimalVideoType);
1583     if (FAILED(hr))
1584         return hr;
1585     hr = optimalVideoType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
1586     if (FAILED(hr))
1587         return hr;
1588
1589     hr = proposedType->CopyAllItems(optimalVideoType.get());
1590     if (FAILED(hr))
1591         return hr;
1592
1593     // We now modify the new media type.
1594
1595     // We assume that the monitor's pixel aspect ratio is 1:1,
1596     // and that the pixel aspect ratio is preserved by the presenter.
1597     hr = MFSetAttributeRatio(optimalVideoType.get(), MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
1598     if (FAILED(hr))
1599         return hr;
1600
1601     // Get the output rectangle.
1602     RECT rcOutput = m_presenterEngine->getDestinationRect();
1603     if (IsRectEmpty(&rcOutput)) {
1604         hr = calculateOutputRectangle(proposedType, rcOutput);
1605         if (FAILED(hr))
1606             return hr;
1607     }
1608
1609     hr = optimalVideoType->SetUINT32(MF_MT_YUV_MATRIX, MFVideoTransferMatrix_BT709);
1610     if (FAILED(hr))
1611         return hr;
1612
1613     hr = optimalVideoType->SetUINT32(MF_MT_TRANSFER_FUNCTION, MFVideoTransFunc_709);
1614     if (FAILED(hr))
1615         return hr;
1616
1617     hr = optimalVideoType->SetUINT32(MF_MT_VIDEO_PRIMARIES, MFVideoPrimaries_BT709);
1618     if (FAILED(hr))
1619         return hr;
1620
1621     hr = optimalVideoType->SetUINT32(MF_MT_VIDEO_NOMINAL_RANGE, MFNominalRange_16_235);
1622     if (FAILED(hr))
1623         return hr;
1624
1625     hr = optimalVideoType->SetUINT32(MF_MT_VIDEO_LIGHTING, MFVideoLighting_dim);
1626     if (FAILED(hr))
1627         return hr;
1628
1629     hr = MFSetAttributeSize(optimalVideoType.get(), MF_MT_FRAME_SIZE, rcOutput.right, rcOutput.bottom);
1630     if (FAILED(hr))
1631         return hr;
1632
1633     MFVideoArea displayArea = MakeArea(0, 0, rcOutput.right, rcOutput.bottom);
1634
1635     hr = optimalVideoType->SetUINT32(MF_MT_PAN_SCAN_ENABLED, FALSE);
1636     if (FAILED(hr))
1637         return hr;
1638
1639     hr = optimalVideoType->SetBlob(MF_MT_GEOMETRIC_APERTURE, (UINT8*)&displayArea, sizeof(MFVideoArea));
1640     if (FAILED(hr))
1641         return hr;
1642
1643     hr = optimalVideoType->SetBlob(MF_MT_PAN_SCAN_APERTURE, (UINT8*)&displayArea, sizeof(MFVideoArea));
1644     if (FAILED(hr))
1645         return hr;
1646
1647     hr = optimalVideoType->SetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, (UINT8*)&displayArea, sizeof(MFVideoArea));
1648     if (FAILED(hr))
1649         return hr;
1650
1651     *optimalType = optimalVideoType.leakRef();
1652
1653     return S_OK;
1654 }
1655
1656 static RECT correctAspectRatio(const RECT& src, const MFRatio& srcPAR, const MFRatio& destPAR)
1657 {
1658     RECT rc = { 0, 0, src.right - src.left, src.bottom - src.top };
1659
1660     if ((srcPAR.Numerator * destPAR.Denominator) != (srcPAR.Denominator * destPAR.Numerator)) {
1661         // The source and destination aspect ratios are different
1662
1663         // Transform the source aspect ratio to 1:1
1664         if (srcPAR.Numerator > srcPAR.Denominator)
1665             rc.right = MulDiv(rc.right, srcPAR.Numerator, srcPAR.Denominator);
1666         else if (srcPAR.Numerator < srcPAR.Denominator)
1667             rc.bottom = MulDiv(rc.bottom, srcPAR.Denominator, srcPAR.Numerator);
1668
1669
1670         // Transform to destination aspect ratio.
1671         if (destPAR.Numerator > destPAR.Denominator)
1672             rc.bottom = MulDiv(rc.bottom, destPAR.Numerator, destPAR.Denominator);
1673         else if (destPAR.Numerator < destPAR.Denominator)
1674             rc.right = MulDiv(rc.right, destPAR.Denominator, destPAR.Numerator);
1675
1676     }
1677
1678     return rc;
1679 }
1680
1681 static HRESULT GetVideoDisplayArea(IMFMediaType* type, MFVideoArea* area)
1682 {
1683     if (!type || !area)
1684         return E_POINTER;
1685
1686     HRESULT hr = S_OK;
1687     UINT32 width = 0, height = 0;
1688
1689     BOOL bPanScan = MFGetAttributeUINT32(type, MF_MT_PAN_SCAN_ENABLED, FALSE);
1690
1691     if (bPanScan)
1692         hr = type->GetBlob(MF_MT_PAN_SCAN_APERTURE, (UINT8*)area, sizeof(MFVideoArea), nullptr);
1693
1694     if (!bPanScan || hr == MF_E_ATTRIBUTENOTFOUND) {
1695         hr = type->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, (UINT8*)area, sizeof(MFVideoArea), nullptr);
1696
1697         if (hr == MF_E_ATTRIBUTENOTFOUND)
1698             hr = type->GetBlob(MF_MT_GEOMETRIC_APERTURE, (UINT8*)area, sizeof(MFVideoArea), nullptr);
1699
1700         if (hr == MF_E_ATTRIBUTENOTFOUND) {
1701             hr = MFGetAttributeSize(type, MF_MT_FRAME_SIZE, &width, &height);
1702             if (SUCCEEDED(hr))
1703                 *area = MakeArea(0.0, 0.0, width, height);
1704         }
1705     }
1706
1707     return hr;
1708 }
1709
1710 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::calculateOutputRectangle(IMFMediaType* proposedType, RECT& outputRect)
1711 {
1712     COMPtr<IMFMediaType> proposedVideoType = proposedType;
1713
1714     UINT32 srcWidth = 0, srcHeight = 0;
1715     HRESULT hr = MFGetAttributeSize(proposedVideoType.get(), MF_MT_FRAME_SIZE, &srcWidth, &srcHeight);
1716     if (FAILED(hr))
1717         return hr;
1718
1719     MFVideoArea displayArea;
1720     ZeroMemory(&displayArea, sizeof(displayArea));
1721
1722     hr = GetVideoDisplayArea(proposedVideoType.get(), &displayArea);
1723     if (FAILED(hr))
1724         return hr;
1725
1726     LONG offsetX = (LONG)MFOffsetToFloat(displayArea.OffsetX);
1727     LONG offsetY = (LONG)MFOffsetToFloat(displayArea.OffsetY);
1728
1729     // Check if the display area is valid.
1730     // If it is valid, we use it. If not, we use the frame dimensions.
1731
1732     RECT rcOutput;
1733
1734     if (displayArea.Area.cx != 0
1735         && displayArea.Area.cy != 0
1736         && offsetX + displayArea.Area.cx <= srcWidth
1737         && offsetY + displayArea.Area.cy <= srcHeight) {
1738         rcOutput.left = offsetX;
1739         rcOutput.right = offsetX + displayArea.Area.cx;
1740         rcOutput.top = offsetY;
1741         rcOutput.bottom = offsetY + displayArea.Area.cy;
1742     } else {
1743         rcOutput.left = 0;
1744         rcOutput.top = 0;
1745         rcOutput.right = srcWidth;
1746         rcOutput.bottom = srcHeight;
1747     }
1748
1749     // Correct aspect ratio.
1750
1751     MFRatio inputPAR = { 1, 1 };
1752     MFRatio outputPAR = { 1, 1 }; // We assume the monitor's pixels are square.
1753     MFGetAttributeRatio(proposedVideoType.get(), MF_MT_PIXEL_ASPECT_RATIO, (UINT32*)&inputPAR.Numerator, (UINT32*)&inputPAR.Denominator);
1754     outputRect = correctAspectRatio(rcOutput, inputPAR, outputPAR);
1755
1756     return S_OK;
1757 }
1758
1759 void MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::processOutputLoop()
1760 {
1761     // Get video frames from the mixer and schedule them for presentation.
1762     HRESULT hr = S_OK;
1763
1764     while (hr == S_OK) {
1765         if (!m_sampleNotify) {
1766             // Currently no more input samples.
1767             hr = MF_E_TRANSFORM_NEED_MORE_INPUT;
1768             break;
1769         }
1770
1771         // We break from the loop if we fail to process a sample.
1772         hr = processOutput();
1773     }
1774
1775     if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
1776         checkEndOfStream();
1777 }
1778
1779 static HRESULT setDesiredSampleTime(IMFSample* sample, const LONGLONG& sampleTime, const LONGLONG& duration)
1780 {
1781     // To tell the mixer to give us an earlier frame for repainting, we can set the desired sample time.
1782     // We have to clear the desired sample time before reusing the sample.
1783
1784     if (!sample)
1785         return E_POINTER;
1786
1787     COMPtr<IMFDesiredSample> desired;
1788
1789     HRESULT hr = sample->QueryInterface(__uuidof(IMFDesiredSample), (void**)&desired);
1790
1791     if (SUCCEEDED(hr))
1792         desired->SetDesiredSampleTimeAndDuration(sampleTime, duration);
1793
1794     return hr;
1795 }
1796
1797 static HRESULT clearDesiredSampleTime(IMFSample* sample)
1798 {
1799     if (!sample)
1800         return E_POINTER;
1801
1802     // We need to retrieve some attributes we have set on the sample before we call 
1803     // IMFDesiredSample::Clear(), and set them once more, since they are cleared by
1804     // the Clear() call.
1805
1806     UINT32 counter = MFGetAttributeUINT32(sample, MFSamplePresenterSampleCounter, (UINT32)-1);
1807
1808     COMPtr<IUnknown> swapChain;
1809     sample->GetUnknown(MFSamplePresenterSampleSwapChain, IID_IUnknown, (void**)&swapChain);
1810
1811     COMPtr<IMFDesiredSample> desired;
1812     HRESULT hr = sample->QueryInterface(__uuidof(IMFDesiredSample), (void**)&desired);
1813     if (SUCCEEDED(hr)) {
1814         desired->Clear();
1815
1816         hr = sample->SetUINT32(MFSamplePresenterSampleCounter, counter);
1817         if (FAILED(hr))
1818             return hr;
1819
1820         if (swapChain) {
1821             hr = sample->SetUnknown(MFSamplePresenterSampleSwapChain, swapChain.get());
1822             if (FAILED(hr))
1823                 return hr;
1824         }
1825     }
1826
1827     return hr;
1828 }
1829
1830 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::processOutput()
1831 {
1832     // This method will try to get a new sample from the mixer.
1833     // It is called when the mixer has a new sample, or when repainting the last frame.
1834
1835     ASSERT(m_sampleNotify || m_repaint);
1836
1837     LONGLONG mixerStartTime = 0, mixerEndTime = 0;
1838     MFTIME systemTime = 0;
1839     bool repaint = m_repaint;  
1840
1841     // If the clock has not started, we only present the first sample. 
1842
1843     if ((m_renderState != RenderStateStarted) && !m_repaint && m_prerolled)
1844         return S_FALSE;
1845
1846     if (!m_mixer)
1847         return MF_E_INVALIDREQUEST;
1848
1849     // Get a free sample from the pool.
1850     COMPtr<IMFSample> sample;
1851     HRESULT hr = m_samplePool.getSample(sample);
1852     if (hr == MF_E_SAMPLEALLOCATOR_EMPTY)
1853         return S_FALSE; // We will try again later when there are free samples
1854
1855     if (FAILED(hr))
1856         return hr;
1857
1858     ASSERT(sample);
1859
1860     ASSERT(MFGetAttributeUINT32(sample.get(), MFSamplePresenterSampleCounter, (UINT32)-1) == m_tokenCounter);
1861
1862     if (m_repaint) {
1863         // Get the most recent sample from the mixer.
1864         setDesiredSampleTime(sample.get(), m_scheduler.lastSampleTime(), m_scheduler.frameDuration());
1865         m_repaint = false;
1866     } else {
1867         // Clear the desired sample time to get the next sample in the stream.
1868         clearDesiredSampleTime(sample.get());
1869
1870         if (m_clock) {
1871             // Get the starting time of the ProcessOutput call.
1872             m_clock->GetCorrelatedTime(0, &mixerStartTime, &systemTime);
1873         }
1874     }
1875
1876     // Get a sample from the mixer. 
1877     MFT_OUTPUT_DATA_BUFFER dataBuffer;
1878     ZeroMemory(&dataBuffer, sizeof(dataBuffer));
1879
1880     dataBuffer.dwStreamID = 0;
1881     dataBuffer.pSample = sample.get();
1882     dataBuffer.dwStatus = 0;
1883
1884     DWORD status = 0;
1885     hr = m_mixer->ProcessOutput(0, 1, &dataBuffer, &status);
1886
1887     // Release events. There are usually no events returned,
1888     // but in case there are, we should release them.
1889     if (dataBuffer.pEvents)
1890         dataBuffer.pEvents->Release();
1891
1892     if (FAILED(hr)) {
1893         HRESULT hr2 = m_samplePool.returnSample(sample.get());
1894         if (FAILED(hr2))
1895             return hr2;
1896
1897         if (hr == MF_E_TRANSFORM_TYPE_NOT_SET) {
1898             // The media type has not been set, renegotiate.
1899             hr = renegotiateMediaType();
1900         } else if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
1901             // The media type changed, reset it.
1902             setMediaType(nullptr);
1903         } else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
1904             // The mixer needs more input.
1905             m_sampleNotify = false;
1906         }
1907     } else {
1908         // We have got a sample from the mixer.
1909
1910         if (m_clock && !repaint) {
1911             // Notify the EVR about latency.
1912             m_clock->GetCorrelatedTime(0, &mixerEndTime, &systemTime);
1913
1914             LONGLONG latencyTime = mixerEndTime - mixerStartTime;
1915             notifyEvent(EC_PROCESSING_LATENCY, (LONG_PTR)&latencyTime, 0);
1916         }
1917
1918         // Make sure we are notified when the sample is released
1919         hr = trackSample(sample.get());
1920         if (FAILED(hr))
1921             return hr;
1922
1923         // Deliver the sample for scheduling
1924         hr = deliverSample(sample.get(), repaint);
1925         if (FAILED(hr))
1926             return hr;
1927
1928         // At least one sample has been presented now.
1929         m_prerolled = true;
1930     }
1931
1932     return hr;
1933 }
1934
1935 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::deliverSample(IMFSample* sample, bool repaint)
1936 {
1937     if (!sample)
1938         return E_POINTER;
1939
1940     Direct3DPresenter::DeviceState state = Direct3DPresenter::DeviceOK;
1941
1942     // Determine if the sample should be presented immediately.
1943     bool presentNow = ((m_renderState != RenderStateStarted) || isScrubbing() || repaint);
1944
1945     HRESULT hr = m_presenterEngine->checkDeviceState(state);
1946
1947     if (SUCCEEDED(hr))
1948         hr = m_scheduler.scheduleSample(sample, presentNow);
1949
1950     if (FAILED(hr)) {
1951         // Streaming has failed, notify the EVR.
1952         notifyEvent(EC_ERRORABORT, hr, 0);
1953     } else if (state == Direct3DPresenter::DeviceReset)
1954         notifyEvent(EC_DISPLAY_CHANGED, S_OK, 0);
1955
1956     return hr;
1957 }
1958
1959 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::trackSample(IMFSample* sample)
1960 {
1961     if (!sample)
1962         return E_POINTER;
1963
1964     COMPtr<IMFTrackedSample> tracked;
1965
1966     HRESULT hr = sample->QueryInterface(__uuidof(IMFTrackedSample), (void**)&tracked);
1967     if (FAILED(hr))
1968         return hr;
1969
1970     if (!tracked)
1971         return E_POINTER;
1972
1973     // Set callback object on which the onSampleFree method is invoked when the sample is no longer used.
1974     return tracked->SetAllocator(this, nullptr);
1975 }
1976
1977 void MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::releaseResources()
1978 {
1979     // The token counter is incremented to indicate that existing samples are
1980     // invalid and can be disposed in the method onSampleFree.
1981     m_tokenCounter++;
1982
1983     flush();
1984
1985     m_samplePool.clear();
1986
1987     if (m_presenterEngine)
1988         m_presenterEngine->releaseResources();
1989 }
1990
1991 HRESULT MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::onSampleFree(IMFAsyncResult* result)
1992 {
1993     if (!result)
1994         return E_POINTER;
1995
1996     COMPtr<IUnknown> object;
1997     HRESULT hr = result->GetObject(&object);
1998     if (FAILED(hr)) {
1999         notifyEvent(EC_ERRORABORT, hr, 0);
2000         return hr;
2001     }
2002
2003     COMPtr<IMFSample> sample;
2004     hr = object->QueryInterface(__uuidof(IMFSample), (void**)&sample);
2005     if (FAILED(hr)) {
2006         notifyEvent(EC_ERRORABORT, hr, 0);
2007         return hr;
2008     }
2009
2010     m_lock.lock();
2011
2012     if (MFGetAttributeUINT32(sample.get(), MFSamplePresenterSampleCounter, (UINT32)-1) == m_tokenCounter) {
2013         hr = m_samplePool.returnSample(sample.get());
2014
2015         // Do more processing, since a free sample is available
2016         if (SUCCEEDED(hr))
2017             processOutputLoop();
2018     }
2019
2020     m_lock.unlock();
2021
2022     if (FAILED(hr))
2023         notifyEvent(EC_ERRORABORT, hr, 0);
2024
2025     return hr;
2026 }
2027
2028 void MediaPlayerPrivateMediaFoundation::CustomVideoPresenter::notifyEvent(long EventCode, LONG_PTR Param1, LONG_PTR Param2)
2029 {
2030     if (m_mediaEventSink)
2031         m_mediaEventSink->Notify(EventCode, Param1, Param2);
2032 }
2033
2034 HRESULT MediaPlayerPrivateMediaFoundation::VideoSamplePool::getSample(COMPtr<IMFSample>& sample)
2035 {
2036     LockHolder locker(m_lock);
2037
2038     if (!m_initialized)
2039         return MF_E_NOT_INITIALIZED;
2040
2041     if (m_videoSampleQueue.isEmpty())
2042         return MF_E_SAMPLEALLOCATOR_EMPTY;
2043
2044     sample = m_videoSampleQueue.takeFirst();
2045
2046     m_pending++;
2047
2048     return S_OK;
2049 }
2050
2051 HRESULT MediaPlayerPrivateMediaFoundation::VideoSamplePool::returnSample(IMFSample* sample)
2052 {
2053     if (!sample)
2054         return E_POINTER;
2055
2056     LockHolder locker(m_lock);
2057
2058     if (!m_initialized)
2059         return MF_E_NOT_INITIALIZED;
2060
2061     m_videoSampleQueue.append(sample);
2062     m_pending--;
2063     return S_OK;
2064 }
2065
2066 bool MediaPlayerPrivateMediaFoundation::VideoSamplePool::areSamplesPending()
2067 {
2068     LockHolder locker(m_lock);
2069
2070     if (!m_initialized)
2071         return FALSE;
2072
2073     return (m_pending > 0);
2074 }
2075
2076 HRESULT MediaPlayerPrivateMediaFoundation::VideoSamplePool::initialize(VideoSampleList& samples)
2077 {
2078     LockHolder locker(m_lock);
2079
2080     if (m_initialized)
2081         return MF_E_INVALIDREQUEST;
2082
2083     // Copy the samples
2084     for (auto sample : samples)
2085         m_videoSampleQueue.append(sample);
2086
2087     m_initialized = true;
2088     samples.clear();
2089
2090     return S_OK;
2091 }
2092
2093 void MediaPlayerPrivateMediaFoundation::VideoSamplePool::clear()
2094 {
2095     LockHolder locker(m_lock);
2096
2097     m_videoSampleQueue.clear();
2098     m_initialized = false;
2099     m_pending = 0;
2100 }
2101
2102
2103 // Scheduler thread messages.
2104
2105 enum ScheduleEvent {
2106     EventTerminate = WM_USER,
2107     EventSchedule,
2108     EventFlush
2109 };
2110
2111 void MediaPlayerPrivateMediaFoundation::VideoScheduler::setFrameRate(const MFRatio& fps)
2112 {
2113     UINT64 avgTimePerFrame = 0;
2114     MFFrameRateToAverageTimePerFramePtr()(fps.Numerator, fps.Denominator, &avgTimePerFrame);
2115
2116     m_frameDuration = (MFTIME)avgTimePerFrame;
2117 }
2118
2119 HRESULT MediaPlayerPrivateMediaFoundation::VideoScheduler::startScheduler(IMFClock* clock)
2120 {
2121     if (m_schedulerThread.isValid())
2122         return E_UNEXPECTED;
2123
2124     HRESULT hr = S_OK;
2125
2126     m_clock = clock;
2127
2128     // Use high timer resolution.
2129     timeBeginPeriod(1);
2130
2131     // Create an event to signal that the scheduler thread has started.
2132     m_threadReadyEvent = ::CreateEvent(nullptr, FALSE, FALSE, nullptr);
2133     if (!m_threadReadyEvent.isValid())
2134         return HRESULT_FROM_WIN32(GetLastError());
2135
2136     // Create an event to signal that the flush has completed.
2137     m_flushEvent = ::CreateEvent(nullptr, FALSE, FALSE, nullptr);
2138     if (!m_flushEvent.isValid())
2139         return HRESULT_FROM_WIN32(GetLastError());
2140
2141     // Start scheduler thread.
2142     DWORD threadID = 0;
2143     m_schedulerThread = ::CreateThread(nullptr, 0, schedulerThreadProc, (LPVOID)this, 0, &threadID);
2144     if (!m_schedulerThread.isValid())
2145         return HRESULT_FROM_WIN32(GetLastError());
2146
2147     HANDLE hObjects[] = { m_threadReadyEvent.get(), m_schedulerThread.get() };
2148
2149     // Wait for the thread to start
2150     DWORD result = ::WaitForMultipleObjects(2, hObjects, FALSE, INFINITE);
2151     if (WAIT_OBJECT_0 != result) {
2152         // The thread has terminated.
2153         m_schedulerThread.clear();
2154         return E_UNEXPECTED;
2155     }
2156
2157     m_threadID = threadID;
2158
2159     return hr;
2160 }
2161
2162 HRESULT MediaPlayerPrivateMediaFoundation::VideoScheduler::stopScheduler()
2163 {
2164     if (!m_schedulerThread.isValid())
2165         return S_OK;
2166
2167     // Terminate the scheduler thread
2168     ::PostThreadMessage(m_threadID, EventTerminate, 0, 0);
2169
2170     // Wait for the scheduler thread to finish.
2171     ::WaitForSingleObject(m_schedulerThread.get(), INFINITE);
2172
2173     LockHolder locker(m_lock);
2174
2175     m_schedulerThread.clear();
2176     m_flushEvent.clear();
2177
2178     // Throw away samples
2179     m_scheduledSamples.clear();
2180
2181     // Clear previously set timer resolution.
2182     timeEndPeriod(1);
2183
2184     return S_OK;
2185 }
2186
2187 HRESULT MediaPlayerPrivateMediaFoundation::VideoScheduler::flush()
2188 {
2189     // This method will wait for the flush to finish on the worker thread.
2190
2191     if (m_schedulerThread.isValid()) {
2192         ::PostThreadMessage(m_threadID, EventFlush, 0, 0);
2193
2194         HANDLE objects[] = { m_flushEvent.get(), m_schedulerThread.get() };
2195
2196         const int schedulerTimeout = 5000;
2197
2198         // Wait for the flush to finish or the thread to terminate.
2199         ::WaitForMultipleObjects(2, objects, FALSE, schedulerTimeout);
2200     }
2201
2202     return S_OK;
2203 }
2204
2205 HRESULT MediaPlayerPrivateMediaFoundation::VideoScheduler::scheduleSample(IMFSample* sample, bool presentNow)
2206 {
2207     if (!sample)
2208         return E_POINTER;
2209
2210     if (!m_presenter)
2211         return MF_E_NOT_INITIALIZED;
2212
2213     if (!m_schedulerThread.isValid())
2214         return MF_E_NOT_INITIALIZED;
2215
2216     DWORD exitCode = 0;
2217     ::GetExitCodeThread(m_schedulerThread.get(), &exitCode);
2218
2219     if (exitCode != STILL_ACTIVE)
2220         return E_FAIL;
2221
2222     if (presentNow || !m_clock)
2223         m_presenter->presentSample(sample, 0);
2224     else {
2225         // Submit the sample for scheduling.
2226         LockHolder locker(m_lock);
2227         m_scheduledSamples.append(sample);
2228
2229         ::PostThreadMessage(m_threadID, EventSchedule, 0, 0);
2230     }
2231
2232     return S_OK;
2233 }
2234
2235 HRESULT MediaPlayerPrivateMediaFoundation::VideoScheduler::processSamplesInQueue(LONG& nextSleep)
2236 {
2237     HRESULT hr = S_OK;
2238     LONG wait = 0;
2239
2240     // Process samples as long as there are samples in the queue, and they have not arrived too early.
2241
2242     while (true) {
2243         COMPtr<IMFSample> sample;
2244
2245         if (true) {
2246             LockHolder locker(m_lock);
2247             if (m_scheduledSamples.isEmpty())
2248                 break;
2249             sample = m_scheduledSamples.takeFirst();
2250         }
2251
2252         // Process the sample.
2253         // If the sample has arrived too early, wait will be > 0,
2254         // and the scheduler should go to sleep.
2255         hr = processSample(sample.get(), wait);
2256
2257         if (FAILED(hr))
2258             break;
2259
2260         if (wait > 0)
2261             break;
2262     }
2263
2264     if (!wait) {
2265         // The queue is empty. Sleep until the next message arrives.
2266         wait = INFINITE;
2267     }
2268
2269     nextSleep = wait;
2270     return hr;
2271 }
2272
2273 // MFTimeToMilliseconds: Convert 100-nanosecond time to milliseconds.
2274 static LONG MFTimeToMilliseconds(const LONGLONG& time)
2275 {
2276     return (time / 10000);
2277 }
2278
2279 HRESULT MediaPlayerPrivateMediaFoundation::VideoScheduler::processSample(IMFSample* sample, LONG& nextSleep)
2280 {
2281     if (!sample)
2282         return E_POINTER;
2283
2284     HRESULT hr = S_OK;
2285
2286     LONGLONG presentationTime = 0;
2287     LONGLONG timeNow = 0;
2288     MFTIME systemTime = 0;
2289
2290     bool presentNow = true;
2291     LONG nextSleepTime = 0;
2292
2293     if (m_clock) {
2294         // Get the time stamp of the sample.
2295         // A sample can possibly have no time stamp.
2296         hr = sample->GetSampleTime(&presentationTime);
2297
2298         // Get the clock time.
2299         // If the sample does not have a time stamp, the clock time is not needed.
2300         if (SUCCEEDED(hr))
2301             hr = m_clock->GetCorrelatedTime(0, &timeNow, &systemTime);
2302
2303         // Determine the time until the sample should be presented.
2304         // Samples arriving late, will have negative values.
2305         LONGLONG timeDelta = presentationTime - timeNow;
2306         if (m_playbackRate < 0) {
2307             // Reverse delta for reverse playback.
2308             timeDelta = -timeDelta;
2309         }
2310
2311         LONGLONG frameDurationOneFourth = m_frameDuration / 4;
2312
2313         if (timeDelta < -frameDurationOneFourth) {
2314             // The sample has arrived late. 
2315             presentNow = true;
2316         } else if (timeDelta > (3 * frameDurationOneFourth)) {
2317             // We can sleep, the sample has arrived too early.
2318             nextSleepTime = MFTimeToMilliseconds(timeDelta - (3 * frameDurationOneFourth));
2319
2320             // Since sleeping is using the system clock, we need to convert the sleep time
2321             // from presentation time to system time.
2322             nextSleepTime = (LONG)(nextSleepTime / fabsf(m_playbackRate));
2323
2324             presentNow = false;
2325         }
2326     }
2327
2328     if (presentNow)
2329         hr = m_presenter->presentSample(sample, presentationTime);
2330     else {
2331         // Return the sample to the queue, since it is not ready.
2332         LockHolder locker(m_lock);
2333         m_scheduledSamples.prepend(sample);
2334     }
2335
2336     nextSleep = nextSleepTime;
2337
2338     return hr;
2339 }
2340
2341 DWORD WINAPI MediaPlayerPrivateMediaFoundation::VideoScheduler::schedulerThreadProc(LPVOID lpParameter)
2342 {
2343     VideoScheduler* scheduler = reinterpret_cast<VideoScheduler*>(lpParameter);
2344     if (!scheduler)
2345         return static_cast<DWORD>(-1);
2346     return scheduler->schedulerThreadProcPrivate();
2347 }
2348
2349 DWORD MediaPlayerPrivateMediaFoundation::VideoScheduler::schedulerThreadProcPrivate()
2350 {
2351     HRESULT hr = S_OK;
2352
2353     // This will force a message queue to be created for the thread.
2354     MSG msg;
2355     PeekMessage(&msg, nullptr, WM_USER, WM_USER, PM_NOREMOVE);
2356
2357     // The thread is ready.
2358     SetEvent(m_threadReadyEvent.get());
2359
2360     LONG wait = INFINITE;
2361     bool exitThread = false;
2362     while (!exitThread) {
2363         // Wait for messages
2364         DWORD result = MsgWaitForMultipleObjects(0, nullptr, FALSE, wait, QS_POSTMESSAGE);
2365
2366         if (result == WAIT_TIMEOUT) {
2367             hr = processSamplesInQueue(wait);
2368             if (FAILED(hr))
2369                 exitThread = true;
2370         }
2371
2372         while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
2373             bool processSamples = true;
2374
2375             switch (msg.message) {
2376             case EventTerminate:
2377                 exitThread = true;
2378                 break;
2379
2380             case EventFlush:
2381                 {
2382                     LockHolder lock(m_lock);
2383                     m_scheduledSamples.clear();
2384                 }
2385                 wait = INFINITE;
2386                 SetEvent(m_flushEvent.get());
2387                 break;
2388
2389             case EventSchedule:
2390                 if (processSamples) {
2391                     hr = processSamplesInQueue(wait);
2392                     if (FAILED(hr))
2393                         exitThread = true;
2394                     processSamples = (wait != INFINITE);
2395                 }
2396                 break;
2397             }
2398         }
2399     }
2400     return (SUCCEEDED(hr) ? 0 : 1);
2401 }
2402
2403 static HRESULT findAdapter(IDirect3D9* direct3D9, HMONITOR monitor, UINT& adapterID)
2404 {
2405     HRESULT hr = E_FAIL;
2406
2407     UINT adapterCount = direct3D9->GetAdapterCount();
2408     for (UINT i = 0; i < adapterCount; i++) {
2409         HMONITOR monitorTmp = direct3D9->GetAdapterMonitor(i);
2410
2411         if (!monitorTmp)
2412             break;
2413
2414         if (monitorTmp == monitor) {
2415             adapterID = i;
2416             hr = S_OK;
2417             break;
2418         }
2419     }
2420
2421     return hr;
2422 }
2423
2424 MediaPlayerPrivateMediaFoundation::Direct3DPresenter::Direct3DPresenter()
2425 {
2426     SetRectEmpty(&m_destRect);
2427
2428     ZeroMemory(&m_displayMode, sizeof(m_displayMode));
2429
2430     HRESULT hr = initializeD3D();
2431
2432     if (FAILED(hr))
2433         return;
2434
2435     createD3DDevice();
2436 }
2437
2438 MediaPlayerPrivateMediaFoundation::Direct3DPresenter::~Direct3DPresenter()
2439 {
2440 }
2441
2442 HRESULT MediaPlayerPrivateMediaFoundation::Direct3DPresenter::getService(REFGUID guidService, REFIID riid, void** ppv)
2443 {
2444     ASSERT(ppv);
2445
2446     HRESULT hr = S_OK;
2447
2448     if (riid == __uuidof(IDirect3DDeviceManager9)) {
2449         if (!m_deviceManager)
2450             hr = MF_E_UNSUPPORTED_SERVICE;
2451         else {
2452             *ppv = m_deviceManager.get();
2453             m_deviceManager->AddRef();
2454         }
2455     } else
2456         hr = MF_E_UNSUPPORTED_SERVICE;
2457
2458     return hr;
2459 }
2460
2461 HRESULT MediaPlayerPrivateMediaFoundation::Direct3DPresenter::checkFormat(D3DFORMAT format)
2462 {
2463     HRESULT hr = S_OK;
2464
2465     UINT adapter = D3DADAPTER_DEFAULT;
2466     D3DDEVTYPE type = D3DDEVTYPE_HAL;
2467
2468     if (m_device) {
2469         D3DDEVICE_CREATION_PARAMETERS params;
2470         hr = m_device->GetCreationParameters(&params);
2471         if (FAILED(hr))
2472             return hr;
2473
2474         adapter = params.AdapterOrdinal;
2475         type = params.DeviceType;
2476     }
2477
2478     D3DDISPLAYMODE mode;
2479     hr = m_direct3D9->GetAdapterDisplayMode(adapter, &mode);
2480     if (FAILED(hr))
2481         return hr;
2482
2483     return m_direct3D9->CheckDeviceType(adapter, type, mode.Format, format, TRUE);
2484 }
2485
2486 HRESULT MediaPlayerPrivateMediaFoundation::Direct3DPresenter::setVideoWindow(HWND hwnd)
2487 {
2488     ASSERT(IsWindow(hwnd));
2489     ASSERT(hwnd != m_hwnd);
2490
2491     {
2492         LockHolder locker(m_lock);
2493         m_hwnd = hwnd;
2494         updateDestRect();
2495     }
2496
2497     return createD3DDevice();
2498 }
2499
2500 HRESULT MediaPlayerPrivateMediaFoundation::Direct3DPresenter::setDestinationRect(const RECT& rcDest)
2501 {
2502     if (EqualRect(&rcDest, &m_destRect))
2503         return S_OK;
2504
2505     LockHolder locker(m_lock);
2506
2507     m_destRect = rcDest;
2508     updateDestRect();
2509
2510     return S_OK;
2511 }
2512
2513 HRESULT MediaPlayerPrivateMediaFoundation::Direct3DPresenter::createVideoSamples(IMFMediaType* format, VideoSampleList& videoSampleQueue)
2514 {
2515     // Create video samples matching the supplied format.
2516     // A swap chain with a single back buffer will be created for each video sample.
2517     // The mixer will render to the back buffer through a surface kept by the sample.
2518     // The surface can be rendered to a window by presenting the swap chain.
2519     // In our case the surface is transferred to system memory, and rendered to a graphics context.
2520
2521     if (!m_hwnd)
2522         return MF_E_INVALIDREQUEST;
2523
2524     if (!format)
2525         return MF_E_UNEXPECTED;
2526
2527
2528     LockHolder locker(m_lock);
2529
2530     releaseResources();
2531
2532     D3DPRESENT_PARAMETERS presentParameters;
2533     HRESULT hr = getSwapChainPresentParameters(format, &presentParameters);
2534     if (FAILED(hr)) {
2535         releaseResources();
2536         return hr;
2537     }
2538
2539     updateDestRect();
2540
2541     static const int presenterBufferCount = 3;
2542
2543     for (int i = 0; i < presenterBufferCount; i++) {
2544         COMPtr<IDirect3DSwapChain9> swapChain;
2545         hr = m_device->CreateAdditionalSwapChain(&presentParameters, &swapChain);
2546         if (FAILED(hr)) {
2547             releaseResources();
2548             return hr;
2549         }
2550
2551         COMPtr<IMFSample> videoSample;
2552         hr = createD3DSample(swapChain.get(), videoSample);
2553         if (FAILED(hr)) {
2554             releaseResources();
2555             return hr;
2556         }
2557
2558         videoSampleQueue.append(videoSample);
2559
2560         // Keep the swap chain alive by setting it as a custom attribute on the sample.
2561         hr = videoSample->SetUnknown(MFSamplePresenterSampleSwapChain, swapChain.get());
2562         if (FAILED(hr)) {
2563             releaseResources();
2564             return hr;
2565         }
2566     }
2567
2568     return hr;
2569 }
2570
2571 void MediaPlayerPrivateMediaFoundation::Direct3DPresenter::releaseResources()
2572 {
2573     m_surfaceRepaint = nullptr;
2574 }
2575
2576 HRESULT MediaPlayerPrivateMediaFoundation::Direct3DPresenter::checkDeviceState(DeviceState& state)
2577 {
2578     LockHolder locker(m_lock);
2579
2580     HRESULT hr = m_device->CheckDeviceState(m_hwnd);
2581
2582     state = DeviceOK;
2583
2584     // Not all failure codes are critical.
2585
2586     switch (hr) {
2587     case S_OK:
2588     case S_PRESENT_OCCLUDED:
2589     case S_PRESENT_MODE_CHANGED:
2590         hr = S_OK;
2591         break;
2592
2593     case D3DERR_DEVICELOST:
2594     case D3DERR_DEVICEHUNG:
2595         hr = createD3DDevice();
2596         if (FAILED(hr))
2597             return hr;
2598         state = DeviceReset;
2599         hr = S_OK;
2600         break;
2601
2602     case D3DERR_DEVICEREMOVED:
2603         state = DeviceRemoved;
2604         break;
2605
2606     case E_INVALIDARG:
2607         // This might happen if the window has been destroyed, or is not valid.
2608         // A new device will be created if a new window is set.
2609         hr = S_OK;
2610     }
2611
2612     return hr;
2613 }
2614
2615 HRESULT MediaPlayerPrivateMediaFoundation::Direct3DPresenter::presentSample(IMFSample* sample, LONGLONG targetPresentationTime)
2616 {
2617     HRESULT hr = S_OK;
2618
2619     LockHolder locker(m_lock);
2620
2621     COMPtr<IDirect3DSurface9> surface;
2622
2623     if (sample) {
2624         COMPtr<IMFMediaBuffer> buffer;
2625         hr = sample->GetBufferByIndex(0, &buffer);
2626         hr = MFGetServicePtr()(buffer.get(), MR_BUFFER_SERVICE, __uuidof(IDirect3DSurface9), (void**)&surface);
2627     } else if (m_surfaceRepaint) {
2628         // Use the last surface.
2629         surface = m_surfaceRepaint;
2630     }
2631
2632     if (surface) {
2633         UINT width = m_destRect.right - m_destRect.left;
2634         UINT height = m_destRect.bottom - m_destRect.top;
2635
2636         if (width > 0 && height > 0) {
2637             if (!m_memSurface || m_width != width || m_height != height) {
2638                 hr = m_device->CreateOffscreenPlainSurface(width, height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &m_memSurface, nullptr);
2639                 m_width = width;
2640                 m_height = height;
2641             }
2642             // Copy data from video memory to system memory
2643             hr = m_device->GetRenderTargetData(surface.get(), m_memSurface.get());
2644             if (FAILED(hr)) {
2645                 m_memSurface = nullptr;
2646                 hr = S_OK;
2647             }
2648         }
2649
2650         // Since we want to draw to the GraphicsContext provided in the paint method,
2651         // and not draw directly to the window, we skip presenting the swap chain:
2652
2653         // COMPtr<IDirect3DSwapChain9> swapChain;
2654         // hr = surface->GetContainer(__uuidof(IDirect3DSwapChain9), (LPVOID*)&swapChain));
2655         // hr = presentSwapChain(swapChain, surface));
2656
2657         // Keep the last surface for repaints.
2658         m_surfaceRepaint = surface;
2659     }
2660
2661     if (FAILED(hr)) {
2662         if (hr == D3DERR_DEVICELOST || hr == D3DERR_DEVICENOTRESET || hr == D3DERR_DEVICEHUNG) {
2663             // Ignore this error. We have to reset or recreate the device.
2664             // The presenter will handle this when checking the device state the next time.
2665             hr = S_OK;
2666         }
2667     }
2668     return hr;
2669 }
2670
2671 void MediaPlayerPrivateMediaFoundation::Direct3DPresenter::paintCurrentFrame(WebCore::GraphicsContext& context, const WebCore::FloatRect& destRect)
2672 {
2673     UINT width = m_destRect.right - m_destRect.left;
2674     UINT height = m_destRect.bottom - m_destRect.top;
2675
2676     if (!width || !height)
2677         return;
2678
2679     LockHolder locker(m_lock);
2680
2681     if (!m_memSurface)
2682         return;
2683
2684     D3DLOCKED_RECT lockedRect;
2685     if (SUCCEEDED(m_memSurface->LockRect(&lockedRect, nullptr, D3DLOCK_READONLY))) {
2686         void* data = lockedRect.pBits;
2687         int pitch = lockedRect.Pitch;
2688 #if USE(CAIRO)
2689         WebCore::PlatformContextCairo* ctxt = context.platformContext();
2690         cairo_surface_t* image = cairo_image_surface_create_for_data(static_cast<unsigned char*>(data), CAIRO_FORMAT_ARGB32, width, height, pitch);
2691         FloatRect srcRect(0, 0, width, height);
2692         ctxt->drawSurfaceToContext(image, destRect, srcRect, context);
2693         cairo_surface_destroy(image);
2694 #else
2695 #error "Platform needs to implement drawing of Direct3D surface to graphics context!"
2696 #endif
2697         m_memSurface->UnlockRect();
2698     }
2699 }
2700
2701 HRESULT MediaPlayerPrivateMediaFoundation::Direct3DPresenter::initializeD3D()
2702 {
2703     ASSERT(!m_direct3D9);
2704     ASSERT(!m_deviceManager);
2705
2706     HRESULT hr = Direct3DCreate9ExPtr()(D3D_SDK_VERSION, &m_direct3D9);
2707     if (FAILED(hr))
2708         return hr;
2709
2710     return DXVA2CreateDirect3DDeviceManager9Ptr()(&m_deviceResetToken, &m_deviceManager);
2711 }
2712
2713 HRESULT MediaPlayerPrivateMediaFoundation::Direct3DPresenter::createD3DDevice()
2714 {
2715     HRESULT hr = S_OK;
2716     UINT adapterID = D3DADAPTER_DEFAULT;
2717
2718     LockHolder locker(m_lock);
2719
2720     if (!m_direct3D9 || !m_deviceManager)
2721         return MF_E_NOT_INITIALIZED;
2722
2723     HWND hwnd = GetDesktopWindow();
2724
2725     // We create additional swap chains to present the video frames,
2726     // and do not use the implicit swap chain of the device.
2727     // The size of the back buffer is 1 x 1.
2728
2729     D3DPRESENT_PARAMETERS pp;
2730     ZeroMemory(&pp, sizeof(pp));
2731
2732     pp.BackBufferWidth = 1;
2733     pp.BackBufferHeight = 1;
2734     pp.Windowed = TRUE;
2735     pp.SwapEffect = D3DSWAPEFFECT_COPY;
2736     pp.BackBufferFormat = D3DFMT_UNKNOWN;
2737     pp.hDeviceWindow = hwnd;
2738     pp.Flags = D3DPRESENTFLAG_VIDEO;
2739     pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
2740
2741     if (m_hwnd) {
2742         HMONITOR monitor = MonitorFromWindow(m_hwnd, MONITOR_DEFAULTTONEAREST);
2743
2744         hr = findAdapter(m_direct3D9.get(), monitor, adapterID);
2745         if (FAILED(hr))
2746             return hr;
2747     }
2748
2749     D3DCAPS9 ddCaps;
2750     ZeroMemory(&ddCaps, sizeof(ddCaps));
2751
2752     hr = m_direct3D9->GetDeviceCaps(adapterID, D3DDEVTYPE_HAL, &ddCaps);
2753     if (FAILED(hr))
2754         return hr;
2755
2756     DWORD flags = D3DCREATE_NOWINDOWCHANGES | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE;
2757
2758     if (ddCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
2759         flags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
2760     else
2761         flags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
2762
2763     COMPtr<IDirect3DDevice9Ex> device;
2764     hr = m_direct3D9->CreateDeviceEx(adapterID, D3DDEVTYPE_HAL, pp.hDeviceWindow, flags, &pp, nullptr, &device);
2765     if (FAILED(hr))
2766         return hr;
2767
2768     hr = m_direct3D9->GetAdapterDisplayMode(adapterID, &m_displayMode);
2769     if (FAILED(hr))
2770         return hr;
2771
2772     hr = m_deviceManager->ResetDevice(device.get(), m_deviceResetToken);
2773     if (FAILED(hr))
2774         return hr;
2775
2776     m_device = device;
2777
2778     return hr;
2779 }
2780
2781 HRESULT MediaPlayerPrivateMediaFoundation::Direct3DPresenter::createD3DSample(IDirect3DSwapChain9* swapChain, COMPtr<IMFSample>& videoSample)
2782 {
2783     COMPtr<IDirect3DSurface9> surface;
2784     HRESULT hr = swapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &surface);
2785     if (FAILED(hr))
2786         return hr;
2787
2788     D3DCOLOR colorBlack = D3DCOLOR_ARGB(0xFF, 0x00, 0x00, 0x00);
2789     hr = m_device->ColorFill(surface.get(), nullptr, colorBlack);
2790     if (FAILED(hr))
2791         return hr;
2792
2793     return MFCreateVideoSampleFromSurfacePtr()(surface.get(), &videoSample);
2794 }
2795
2796 HRESULT MediaPlayerPrivateMediaFoundation::Direct3DPresenter::presentSwapChain(IDirect3DSwapChain9* swapChain, IDirect3DSurface9* surface)
2797 {
2798     if (!m_hwnd)
2799         return MF_E_INVALIDREQUEST;
2800
2801     return swapChain->Present(nullptr, &m_destRect, m_hwnd, nullptr, 0);
2802 }
2803
2804 HRESULT MediaPlayerPrivateMediaFoundation::Direct3DPresenter::getSwapChainPresentParameters(IMFMediaType* type, D3DPRESENT_PARAMETERS* presentParams)
2805 {
2806     if (!m_hwnd)
2807         return MF_E_INVALIDREQUEST;
2808
2809     COMPtr<IMFMediaType> videoType = type;
2810
2811     UINT32 width = 0, height = 0;
2812     HRESULT hr = MFGetAttributeSize(videoType.get(), MF_MT_FRAME_SIZE, &width, &height);
2813     if (FAILED(hr))
2814         return hr;
2815
2816     GUID guidSubType = GUID_NULL;
2817     hr = videoType->GetGUID(MF_MT_SUBTYPE, &guidSubType);
2818     if (FAILED(hr))
2819         return hr;
2820
2821     DWORD d3dFormat = guidSubType.Data1;
2822
2823     ZeroMemory(presentParams, sizeof(D3DPRESENT_PARAMETERS));
2824     presentParams->BackBufferWidth = width;
2825     presentParams->BackBufferHeight = height;
2826     presentParams->Windowed = TRUE;
2827     presentParams->SwapEffect = D3DSWAPEFFECT_COPY;
2828     presentParams->BackBufferFormat = (D3DFORMAT)d3dFormat;
2829     presentParams->hDeviceWindow = m_hwnd;
2830     presentParams->Flags = D3DPRESENTFLAG_VIDEO;
2831     presentParams->PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
2832
2833     D3DDEVICE_CREATION_PARAMETERS params;
2834     hr = m_device->GetCreationParameters(&params);
2835     if (FAILED(hr))
2836         return hr;
2837
2838     if (params.DeviceType != D3DDEVTYPE_HAL)
2839         presentParams->Flags |= D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
2840
2841     return S_OK;
2842 }
2843
2844 HRESULT MediaPlayerPrivateMediaFoundation::Direct3DPresenter::updateDestRect()
2845 {
2846     if (!m_hwnd)
2847         return S_FALSE;
2848
2849     RECT rcView;
2850     GetClientRect(m_hwnd, &rcView);
2851
2852     // Clip to the client area of the window.
2853     if (m_destRect.right > rcView.right)
2854         m_destRect.right = rcView.right;
2855
2856     if (m_destRect.bottom > rcView.bottom)
2857         m_destRect.bottom = rcView.bottom;
2858
2859     return S_OK;
2860 }
2861
2862 } // namespace WebCore
2863
2864 #endif