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