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