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