[Win][HighDPI] Video window placement is incorrect.
[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 "HostWindow.h"
34 #include "NotImplemented.h"
35 #include "SoftLinking.h"
36
37 #if USE(MEDIA_FOUNDATION)
38
39 #include <wtf/MainThread.h>
40
41 SOFT_LINK_LIBRARY(Mf);
42 SOFT_LINK_OPTIONAL(Mf, MFCreateSourceResolver, HRESULT, STDAPICALLTYPE, (IMFSourceResolver**));
43 SOFT_LINK_OPTIONAL(Mf, MFCreateMediaSession, HRESULT, STDAPICALLTYPE, (IMFAttributes*, IMFMediaSession**));
44 SOFT_LINK_OPTIONAL(Mf, MFCreateTopology, HRESULT, STDAPICALLTYPE, (IMFTopology**));
45 SOFT_LINK_OPTIONAL(Mf, MFCreateTopologyNode, HRESULT, STDAPICALLTYPE, (MF_TOPOLOGY_TYPE, IMFTopologyNode**));
46 SOFT_LINK_OPTIONAL(Mf, MFGetService, HRESULT, STDAPICALLTYPE, (IUnknown*, REFGUID, REFIID, LPVOID*));
47 SOFT_LINK_OPTIONAL(Mf, MFCreateAudioRendererActivate, HRESULT, STDAPICALLTYPE, (IMFActivate**));
48 SOFT_LINK_OPTIONAL(Mf, MFCreateVideoRendererActivate, HRESULT, STDAPICALLTYPE, (HWND, IMFActivate**));
49
50 SOFT_LINK_LIBRARY(Mfplat);
51 SOFT_LINK_OPTIONAL(Mfplat, MFStartup, HRESULT, STDAPICALLTYPE, (ULONG, DWORD));
52 SOFT_LINK_OPTIONAL(Mfplat, MFShutdown, HRESULT, STDAPICALLTYPE, ());
53
54 namespace WebCore {
55
56 MediaPlayerPrivateMediaFoundation::MediaPlayerPrivateMediaFoundation(MediaPlayer* player) 
57     : m_player(player)
58     , m_visible(false)
59     , m_loadingProgress(false)
60     , m_paused(false)
61     , m_hasAudio(false)
62     , m_hasVideo(false)
63     , m_hwndVideo(nullptr)
64     , m_readyState(MediaPlayer::HaveNothing)
65     , m_weakPtrFactory(this)
66 {
67     createSession();
68     createVideoWindow();
69 }
70
71 MediaPlayerPrivateMediaFoundation::~MediaPlayerPrivateMediaFoundation()
72 {
73     notifyDeleted();
74     destroyVideoWindow();
75     endSession();
76 }
77
78 void MediaPlayerPrivateMediaFoundation::registerMediaEngine(MediaEngineRegistrar registrar)
79 {
80     if (isAvailable())
81         registrar([](MediaPlayer* player) { return std::make_unique<MediaPlayerPrivateMediaFoundation>(player); },
82             getSupportedTypes, supportsType, 0, 0, 0, 0);
83 }
84
85 bool MediaPlayerPrivateMediaFoundation::isAvailable() 
86 {
87     notImplemented();
88     return true;
89 }
90
91 void MediaPlayerPrivateMediaFoundation::getSupportedTypes(HashSet<String>& types)
92 {
93     types.add(String("video/mp4"));
94 }
95
96 MediaPlayer::SupportsType MediaPlayerPrivateMediaFoundation::supportsType(const MediaEngineSupportParameters& parameters)
97 {
98     if (parameters.type.isNull() || parameters.type.isEmpty())
99         return MediaPlayer::IsNotSupported;
100
101     if (parameters.type == "video/mp4")
102         return MediaPlayer::IsSupported;
103
104     return MediaPlayer::IsNotSupported;
105 }
106
107 void MediaPlayerPrivateMediaFoundation::load(const String& url)
108 {
109     startCreateMediaSource(url);
110 }
111
112 void MediaPlayerPrivateMediaFoundation::cancelLoad()
113 {
114     notImplemented();
115 }
116
117 void MediaPlayerPrivateMediaFoundation::play()
118 {
119     if (!m_mediaSession)
120         return;
121
122     PROPVARIANT varStart;
123     PropVariantInit(&varStart);
124     varStart.vt = VT_EMPTY;
125
126     HRESULT hr = m_mediaSession->Start(nullptr, &varStart);
127     ASSERT(SUCCEEDED(hr));
128
129     PropVariantClear(&varStart);
130
131     m_paused = !SUCCEEDED(hr);
132 }
133
134 void MediaPlayerPrivateMediaFoundation::pause()
135 {
136     if (!m_mediaSession)
137         return;
138
139     m_paused = SUCCEEDED(m_mediaSession->Pause());
140 }
141
142 FloatSize MediaPlayerPrivateMediaFoundation::naturalSize() const 
143 {
144     return m_size;
145 }
146
147 bool MediaPlayerPrivateMediaFoundation::hasVideo() const
148 {
149     return m_hasVideo;
150 }
151
152 bool MediaPlayerPrivateMediaFoundation::hasAudio() const
153 {
154     return m_hasAudio;
155 }
156
157 void MediaPlayerPrivateMediaFoundation::setVisible(bool visible)
158 {
159     m_visible = visible;
160 }
161
162 bool MediaPlayerPrivateMediaFoundation::seeking() const
163 {
164     // We assume seeking is immediately complete.
165     return false;
166 }
167
168 void MediaPlayerPrivateMediaFoundation::seekDouble(double time)
169 {
170     const double tenMegahertz = 10000000;
171     PROPVARIANT propVariant;
172     PropVariantInit(&propVariant);
173     propVariant.vt = VT_I8;
174     propVariant.hVal.QuadPart = static_cast<__int64>(time * tenMegahertz);
175     
176     HRESULT hr = m_mediaSession->Start(&GUID_NULL, &propVariant);
177     ASSERT(SUCCEEDED(hr));
178     PropVariantClear(&propVariant);
179 }
180
181 double MediaPlayerPrivateMediaFoundation::durationDouble() const
182 {
183     const double tenMegahertz = 10000000;
184     if (!m_mediaSource)
185         return 0;
186
187     IMFPresentationDescriptor* descriptor;
188     if (!SUCCEEDED(m_mediaSource->CreatePresentationDescriptor(&descriptor)))
189         return 0;
190     
191     UINT64 duration;
192     if (!SUCCEEDED(descriptor->GetUINT64(MF_PD_DURATION, &duration)))
193         duration = 0;
194     descriptor->Release();
195     
196     return static_cast<double>(duration) / tenMegahertz;
197 }
198
199 bool MediaPlayerPrivateMediaFoundation::paused() const
200 {
201     return m_paused;
202 }
203
204 MediaPlayer::NetworkState MediaPlayerPrivateMediaFoundation::networkState() const
205
206     notImplemented();
207     return MediaPlayer::Empty;
208 }
209
210 MediaPlayer::ReadyState MediaPlayerPrivateMediaFoundation::readyState() const
211 {
212     return m_readyState;
213 }
214
215 std::unique_ptr<PlatformTimeRanges> MediaPlayerPrivateMediaFoundation::buffered() const
216
217     notImplemented();
218     return std::make_unique<PlatformTimeRanges>();
219 }
220
221 bool MediaPlayerPrivateMediaFoundation::didLoadingProgress() const
222 {
223     return m_loadingProgress;
224 }
225
226 void MediaPlayerPrivateMediaFoundation::setSize(const IntSize& size)
227 {
228     m_size = size;
229
230     if (!m_videoDisplay)
231         return;
232
233     LayoutSize scrollOffset;
234     IntPoint positionInWindow(m_lastPaintRect.location());
235
236     FrameView* view = nullptr;
237     float deviceScaleFactor = 1.0f;
238     if (m_player && m_player->cachedResourceLoader() && m_player->cachedResourceLoader()->document()) {
239         view = m_player->cachedResourceLoader()->document()->view();
240         deviceScaleFactor = m_player->cachedResourceLoader()->document()->deviceScaleFactor();
241     }
242
243     if (view) {
244         scrollOffset = view->scrollOffsetForFixedPosition();
245         positionInWindow = view->convertToContainingWindow(IntPoint(m_lastPaintRect.location()));
246     }
247
248     positionInWindow.move(-scrollOffset.width().toInt(), -scrollOffset.height().toInt());
249
250     int x = positionInWindow.x() * deviceScaleFactor;
251     int y = positionInWindow.y() * deviceScaleFactor;
252     int w = m_size.width() * deviceScaleFactor;
253     int h = m_size.height() * deviceScaleFactor;
254
255     if (m_hwndVideo && !m_lastPaintRect.isEmpty())
256         ::MoveWindow(m_hwndVideo, x, y, w, h, FALSE);
257
258     RECT rc = { 0, 0, w, h };
259     m_videoDisplay->SetVideoPosition(nullptr, &rc);
260 }
261
262 void MediaPlayerPrivateMediaFoundation::paint(GraphicsContext& context, const FloatRect& rect)
263 {
264     if (context.paintingDisabled()
265         || !m_player->visible())
266         return;
267
268     m_lastPaintRect = rect;
269
270     // We currently let Media Foundation handle the drawing, by providing a handle to the window to draw in.
271     // We should instead read individual frames from the stream, and paint them into the graphics context here.
272
273     notImplemented();
274 }
275
276 bool MediaPlayerPrivateMediaFoundation::createSession()
277 {
278     if (!MFStartupPtr() || !MFCreateMediaSessionPtr())
279         return false;
280
281     if (FAILED(MFStartupPtr()(MF_VERSION, MFSTARTUP_FULL)))
282         return false;
283
284     if (FAILED(MFCreateMediaSessionPtr()(nullptr, &m_mediaSession)))
285         return false;
286
287     // Get next event.
288     AsyncCallback* callback = new AsyncCallback(this, true);
289     HRESULT hr = m_mediaSession->BeginGetEvent(callback, nullptr);
290     ASSERT(SUCCEEDED(hr));
291
292     return true;
293 }
294
295 bool MediaPlayerPrivateMediaFoundation::endSession()
296 {
297     if (m_mediaSession) {
298         m_mediaSession->Shutdown();
299         m_mediaSession = nullptr;
300     }
301
302     if (!MFShutdownPtr())
303         return false;
304
305     HRESULT hr = MFShutdownPtr()();
306     ASSERT(SUCCEEDED(hr));
307
308     return true;
309 }
310
311 bool MediaPlayerPrivateMediaFoundation::startCreateMediaSource(const String& url)
312 {
313     if (!MFCreateSourceResolverPtr())
314         return false;
315
316     if (FAILED(MFCreateSourceResolverPtr()(&m_sourceResolver)))
317         return false;
318
319     COMPtr<IUnknown> cancelCookie;
320     Vector<UChar> urlSource = url.charactersWithNullTermination();
321
322     AsyncCallback* callback = new AsyncCallback(this, false);
323
324     if (FAILED(m_sourceResolver->BeginCreateObjectFromURL(urlSource.data(), MF_RESOLUTION_MEDIASOURCE, nullptr, &cancelCookie, callback, nullptr)))
325         return false;
326
327     return true;
328 }
329
330 bool MediaPlayerPrivateMediaFoundation::endCreatedMediaSource(IMFAsyncResult* asyncResult)
331 {
332     MF_OBJECT_TYPE objectType;
333     COMPtr<IUnknown> source;
334
335     HRESULT hr = m_sourceResolver->EndCreateObjectFromURL(asyncResult, &objectType, &source);
336     if (FAILED(hr))
337         return false;
338
339     hr = source->QueryInterface(IID_PPV_ARGS(&m_mediaSource));
340     if (FAILED(hr))
341         return false;
342
343     hr = asyncResult->GetStatus();
344     m_loadingProgress = SUCCEEDED(hr);
345
346     auto weakPtr = m_weakPtrFactory.createWeakPtr();
347     callOnMainThread([weakPtr] {
348         if (!weakPtr)
349             return;
350         weakPtr->onCreatedMediaSource();
351     });
352
353     return true;
354 }
355
356 bool MediaPlayerPrivateMediaFoundation::endGetEvent(IMFAsyncResult* asyncResult)
357 {
358     COMPtr<IMFMediaEvent> event;
359
360     if (!m_mediaSession)
361         return false;
362
363     // Get the event from the event queue.
364     HRESULT hr = m_mediaSession->EndGetEvent(asyncResult, &event);
365     if (FAILED(hr))
366         return false;
367
368     // Get the event type.
369     MediaEventType mediaEventType;
370     hr = event->GetType(&mediaEventType);
371     if (FAILED(hr))
372         return false;
373
374     switch (mediaEventType) {
375     case MESessionTopologySet: {
376         auto weakPtr = m_weakPtrFactory.createWeakPtr();
377         callOnMainThread([weakPtr] {
378             if (!weakPtr)
379                 return;
380             weakPtr->onTopologySet();
381         });
382         break;
383     }
384
385     case MESessionClosed:
386         break;
387     }
388
389     if (mediaEventType != MESessionClosed) {
390         // For all other events, ask the media session for the
391         // next event in the queue.
392         AsyncCallback* callback = new AsyncCallback(this, true);
393
394         hr = m_mediaSession->BeginGetEvent(callback, nullptr);
395         if (FAILED(hr))
396             return false;
397     }
398
399     return true;
400 }
401
402 bool MediaPlayerPrivateMediaFoundation::createTopologyFromSource()
403 {
404     if (!MFCreateTopologyPtr())
405         return false;
406
407     // Create a new topology.
408     if (FAILED(MFCreateTopologyPtr()(&m_topology)))
409         return false;
410
411     // Create the presentation descriptor for the media source.
412     if (FAILED(m_mediaSource->CreatePresentationDescriptor(&m_sourcePD)))
413         return false;
414
415     // Get the number of streams in the media source.
416     DWORD sourceStreams = 0;
417     if (FAILED(m_sourcePD->GetStreamDescriptorCount(&sourceStreams)))
418         return false;
419
420     // For each stream, create the topology nodes and add them to the topology.
421     for (DWORD i = 0; i < sourceStreams; i++) {
422         if (!addBranchToPartialTopology(i))
423             return false;
424     }
425
426     return true;
427 }
428
429 bool MediaPlayerPrivateMediaFoundation::addBranchToPartialTopology(int stream)
430 {
431     // Get the stream descriptor for this stream.
432     COMPtr<IMFStreamDescriptor> sourceSD;
433     BOOL selected = FALSE;
434     if (FAILED(m_sourcePD->GetStreamDescriptorByIndex(stream, &selected, &sourceSD)))
435         return false;
436
437     // Create the topology branch only if the stream is selected.
438     // Otherwise, do nothing.
439     if (!selected)
440         return true;
441
442     // Create a source node for this stream.
443     COMPtr<IMFTopologyNode> sourceNode;
444     if (!createSourceStreamNode(sourceSD, sourceNode))
445         return false;
446
447     COMPtr<IMFTopologyNode> outputNode;
448     if (!createOutputNode(sourceSD, outputNode))
449         return false;
450
451     // Add both nodes to the topology.
452     if (FAILED(m_topology->AddNode(sourceNode.get())))
453         return false;
454
455     if (FAILED(m_topology->AddNode(outputNode.get())))
456         return false;
457
458     // Connect the source node to the output node.
459     if (FAILED(sourceNode->ConnectOutput(0, outputNode.get(), 0)))
460         return false;
461
462     return true;
463 }
464
465 LRESULT CALLBACK MediaPlayerPrivateMediaFoundation::VideoViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
466 {
467     return DefWindowProc(hWnd, message, wParam, lParam);
468 }
469
470 LPCWSTR MediaPlayerPrivateMediaFoundation::registerVideoWindowClass()
471 {
472     const LPCWSTR kVideoWindowClassName = L"WebVideoWindowClass";
473
474     static bool haveRegisteredWindowClass = false;
475     if (haveRegisteredWindowClass)
476         return kVideoWindowClassName;
477
478     haveRegisteredWindowClass = true;
479
480     WNDCLASSEX wcex;
481
482     wcex.cbSize = sizeof(WNDCLASSEX);
483
484     wcex.style = CS_DBLCLKS;
485     wcex.lpfnWndProc = VideoViewWndProc;
486     wcex.cbClsExtra = 0;
487     wcex.cbWndExtra = 0;
488     wcex.hInstance = nullptr;
489     wcex.hIcon = nullptr;
490     wcex.hCursor = ::LoadCursor(0, IDC_ARROW);
491     wcex.hbrBackground = nullptr;
492     wcex.lpszMenuName = nullptr;
493     wcex.lpszClassName = kVideoWindowClassName;
494     wcex.hIconSm = nullptr;
495
496     if (RegisterClassEx(&wcex))
497         return kVideoWindowClassName;
498
499     return nullptr;
500 }
501
502 void MediaPlayerPrivateMediaFoundation::createVideoWindow()
503 {
504     HWND hWndParent = nullptr;
505     FrameView* view = nullptr;
506     if (!m_player || !m_player->cachedResourceLoader() || !m_player->cachedResourceLoader()->document())
507         return;
508     view = m_player->cachedResourceLoader()->document()->view();
509     if (!view || !view->hostWindow())
510         return;
511     hWndParent = view->hostWindow()->platformPageClient();
512
513     m_hwndVideo = CreateWindowEx(WS_EX_NOACTIVATE | WS_EX_TRANSPARENT, registerVideoWindowClass(), 0, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
514         0, 0, 0, 0, hWndParent, 0, 0, 0);
515 }
516
517 void MediaPlayerPrivateMediaFoundation::destroyVideoWindow()
518 {
519     if (m_hwndVideo) {
520         DestroyWindow(m_hwndVideo);
521         m_hwndVideo = nullptr;
522     }
523 }
524
525 void MediaPlayerPrivateMediaFoundation::addListener(MediaPlayerListener* listener)
526 {
527     LockHolder locker(m_mutexListeners);
528
529     m_listeners.add(listener);
530 }
531
532 void MediaPlayerPrivateMediaFoundation::removeListener(MediaPlayerListener* listener)
533 {
534     LockHolder locker(m_mutexListeners);
535
536     m_listeners.remove(listener);
537 }
538
539 void MediaPlayerPrivateMediaFoundation::notifyDeleted()
540 {
541     LockHolder locker(m_mutexListeners);
542
543     for (HashSet<MediaPlayerListener*>::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
544         (*it)->onMediaPlayerDeleted();
545 }
546
547 bool MediaPlayerPrivateMediaFoundation::createOutputNode(COMPtr<IMFStreamDescriptor> sourceSD, COMPtr<IMFTopologyNode>& node)
548 {
549     if (!MFCreateTopologyNodePtr() || !MFCreateAudioRendererActivatePtr() || !MFCreateVideoRendererActivatePtr())
550         return false;
551
552     if (!sourceSD)
553         return false;
554
555 #ifndef NDEBUG
556     // Get the stream ID.
557     DWORD streamID = 0;
558     sourceSD->GetStreamIdentifier(&streamID); // Just for debugging, ignore any failures.
559 #endif
560
561     COMPtr<IMFMediaTypeHandler> handler;
562     if (FAILED(sourceSD->GetMediaTypeHandler(&handler)))
563         return false;
564
565     GUID guidMajorType = GUID_NULL;
566     if (FAILED(handler->GetMajorType(&guidMajorType)))
567         return false;
568
569     // Create a downstream node.
570     if (FAILED(MFCreateTopologyNodePtr()(MF_TOPOLOGY_OUTPUT_NODE, &node)))
571         return false;
572
573     // Create an IMFActivate object for the renderer, based on the media type.
574     COMPtr<IMFActivate> rendererActivate;
575     if (MFMediaType_Audio == guidMajorType) {
576         // Create the audio renderer.
577         if (FAILED(MFCreateAudioRendererActivatePtr()(&rendererActivate)))
578             return false;
579         m_hasAudio = true;
580     } else if (MFMediaType_Video == guidMajorType) {
581         // Create the video renderer.
582         if (FAILED(MFCreateVideoRendererActivatePtr()(m_hwndVideo, &rendererActivate)))
583             return false;
584         m_hasVideo = true;
585     } else
586         return false;
587
588     // Set the IActivate object on the output node.
589     if (FAILED(node->SetObject(rendererActivate.get())))
590         return false;
591
592     return true;
593 }
594
595 bool MediaPlayerPrivateMediaFoundation::createSourceStreamNode(COMPtr<IMFStreamDescriptor> sourceSD, COMPtr<IMFTopologyNode>& node)
596 {
597     if (!MFCreateTopologyNodePtr())
598         return false;
599
600     if (!m_mediaSource || !m_sourcePD || !sourceSD)
601         return false;
602
603     // Create the source-stream node.
604     HRESULT hr = MFCreateTopologyNodePtr()(MF_TOPOLOGY_SOURCESTREAM_NODE, &node);
605     if (FAILED(hr))
606         return false;
607
608     // Set attribute: Pointer to the media source.
609     hr = node->SetUnknown(MF_TOPONODE_SOURCE, m_mediaSource.get());
610     if (FAILED(hr))
611         return false;
612
613     // Set attribute: Pointer to the presentation descriptor.
614     hr = node->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, m_sourcePD.get());
615     if (FAILED(hr))
616         return false;
617
618     // Set attribute: Pointer to the stream descriptor.
619     hr = node->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, sourceSD.get());
620     if (FAILED(hr))
621         return false;
622
623     return true;
624 }
625
626 void MediaPlayerPrivateMediaFoundation::onCreatedMediaSource()
627 {
628     if (!createTopologyFromSource())
629         return;
630
631     // Set the topology on the media session.
632     HRESULT hr = m_mediaSession->SetTopology(0, m_topology.get());
633     ASSERT(SUCCEEDED(hr));
634 }
635
636 void MediaPlayerPrivateMediaFoundation::onTopologySet()
637 {
638     if (!MFGetServicePtr())
639         return;
640
641     if (FAILED(MFGetServicePtr()(m_mediaSession.get(), MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_videoDisplay))))
642         return;
643
644     ASSERT(m_videoDisplay);
645
646     RECT rc = { 0, 0, m_size.width(), m_size.height() };
647     m_videoDisplay->SetVideoPosition(nullptr, &rc);
648
649     m_readyState = MediaPlayer::HaveFutureData;
650
651     ASSERT(m_player);
652     m_player->readyStateChanged();
653
654     play();
655     m_player->playbackStateChanged();
656 }
657
658 MediaPlayerPrivateMediaFoundation::AsyncCallback::AsyncCallback(MediaPlayerPrivateMediaFoundation* mediaPlayer, bool event)
659     : m_refCount(0)
660     , m_mediaPlayer(mediaPlayer)
661     , m_event(event)
662 {
663     if (m_mediaPlayer)
664         m_mediaPlayer->addListener(this);
665 }
666
667 MediaPlayerPrivateMediaFoundation::AsyncCallback::~AsyncCallback()
668 {
669     if (m_mediaPlayer)
670         m_mediaPlayer->removeListener(this);
671 }
672
673 HRESULT MediaPlayerPrivateMediaFoundation::AsyncCallback::QueryInterface(REFIID riid, __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject)
674 {
675     if (!IsEqualGUID(riid, IID_IMFAsyncCallback)) {
676         *ppvObject = nullptr;
677         return E_NOINTERFACE;
678     }
679     *ppvObject = this;
680     AddRef();
681     return S_OK;
682 }
683
684 ULONG STDMETHODCALLTYPE MediaPlayerPrivateMediaFoundation::AsyncCallback::AddRef()
685 {
686     m_refCount++;
687     return m_refCount;
688 }
689
690 ULONG STDMETHODCALLTYPE MediaPlayerPrivateMediaFoundation::AsyncCallback::Release()
691 {
692     m_refCount--;
693     ULONG refCount = m_refCount;
694     if (!refCount)
695         delete this;
696     return refCount;
697 }
698
699 HRESULT STDMETHODCALLTYPE MediaPlayerPrivateMediaFoundation::AsyncCallback::GetParameters(__RPC__out DWORD *pdwFlags, __RPC__out DWORD *pdwQueue)
700 {
701     // Returning E_NOTIMPL gives default values.
702     return E_NOTIMPL;
703 }
704
705 HRESULT STDMETHODCALLTYPE MediaPlayerPrivateMediaFoundation::AsyncCallback::Invoke(__RPC__in_opt IMFAsyncResult *pAsyncResult)
706 {
707     LockHolder locker(m_mutex);
708
709     if (!m_mediaPlayer)
710         return S_OK;
711
712     if (m_event)
713         m_mediaPlayer->endGetEvent(pAsyncResult);
714     else
715         m_mediaPlayer->endCreatedMediaSource(pAsyncResult);
716
717     return S_OK;
718 }
719
720 void MediaPlayerPrivateMediaFoundation::AsyncCallback::onMediaPlayerDeleted()
721 {
722     LockHolder locker(m_mutex);
723
724     m_mediaPlayer = nullptr;
725 }
726
727 }
728
729 #endif