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