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