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