X-Git-Url: https://git.webkit.org/?p=WebKit-https.git;a=blobdiff_plain;f=Source%2FWebCore%2Fplatform%2Fgraphics%2Fwin%2FMediaPlayerPrivateMediaFoundation.cpp;h=ce8e33eef19b74bb998e278110d9e7094f9045ad;hp=8a1cee6fb236ea3e8a6fdda2f10b1579f5b26c6e;hb=aa36cb4397e2b7fadcd0357b73fbc2c3dc000974;hpb=a77663c334c6c670b8ac381c747b806a159129dc diff --git a/Source/WebCore/platform/graphics/win/MediaPlayerPrivateMediaFoundation.cpp b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateMediaFoundation.cpp index 8a1cee6fb236..ce8e33eef19b 100644 --- a/Source/WebCore/platform/graphics/win/MediaPlayerPrivateMediaFoundation.cpp +++ b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateMediaFoundation.cpp @@ -27,18 +27,42 @@ #include "config.h" #include "MediaPlayerPrivateMediaFoundation.h" +#include "CachedResourceLoader.h" +#include "FrameView.h" #include "GraphicsContext.h" +#include "HostWindow.h" #include "NotImplemented.h" #if USE(MEDIA_FOUNDATION) -namespace WebCore { +#include -// TODO: Implement video functionality using Media Foundation +namespace WebCore { MediaPlayerPrivateMediaFoundation::MediaPlayerPrivateMediaFoundation(MediaPlayer* player) : m_player(player) + , m_visible(false) + , m_loadingProgress(false) + , m_paused(false) + , m_hasAudio(false) + , m_hasVideo(false) + , m_hwndVideo(nullptr) + , m_readyState(MediaPlayer::HaveNothing) + , m_mediaSession(nullptr) + , m_sourceResolver(nullptr) + , m_mediaSource(nullptr) + , m_topology(nullptr) + , m_sourcePD(nullptr) + , m_videoDisplay(nullptr) +{ + createSession(); + createVideoWindow(); +} + +MediaPlayerPrivateMediaFoundation::~MediaPlayerPrivateMediaFoundation() { + destroyVideoWindow(); + endSession(); } PassOwnPtr MediaPlayerPrivateMediaFoundation::create(MediaPlayer* player) @@ -60,19 +84,23 @@ bool MediaPlayerPrivateMediaFoundation::isAvailable() void MediaPlayerPrivateMediaFoundation::getSupportedTypes(HashSet& types) { - notImplemented(); - types = HashSet(); + types.add(String("video/mp4")); } MediaPlayer::SupportsType MediaPlayerPrivateMediaFoundation::supportsType(const MediaEngineSupportParameters& parameters) { - notImplemented(); + if (parameters.type.isNull() || parameters.type.isEmpty()) + return MediaPlayer::IsNotSupported; + + if (parameters.type == "video/mp4") + return MediaPlayer::IsSupported; + return MediaPlayer::IsNotSupported; } -void MediaPlayerPrivateMediaFoundation::load(const String&) +void MediaPlayerPrivateMediaFoundation::load(const String& url) { - notImplemented(); + startCreateMediaSource(url); } void MediaPlayerPrivateMediaFoundation::cancelLoad() @@ -82,35 +110,47 @@ void MediaPlayerPrivateMediaFoundation::cancelLoad() void MediaPlayerPrivateMediaFoundation::play() { - notImplemented(); + if (!m_mediaSession) + return; + + PROPVARIANT varStart; + PropVariantInit(&varStart); + varStart.vt = VT_EMPTY; + + HRESULT hr = m_mediaSession->Start(nullptr, &varStart); + ASSERT(SUCCEEDED(hr)); + + PropVariantClear(&varStart); + + m_paused = !SUCCEEDED(hr); } void MediaPlayerPrivateMediaFoundation::pause() { - notImplemented(); + if (!m_mediaSession) + return; + + m_paused = SUCCEEDED(m_mediaSession->Pause()); } IntSize MediaPlayerPrivateMediaFoundation::naturalSize() const { - notImplemented(); - return IntSize(0, 0); + return m_size; } bool MediaPlayerPrivateMediaFoundation::hasVideo() const { - notImplemented(); - return false; + return m_hasVideo; } bool MediaPlayerPrivateMediaFoundation::hasAudio() const { - notImplemented(); - return false; + return m_hasAudio; } -void MediaPlayerPrivateMediaFoundation::setVisible(bool) +void MediaPlayerPrivateMediaFoundation::setVisible(bool visible) { - notImplemented(); + m_visible = visible; } bool MediaPlayerPrivateMediaFoundation::seeking() const @@ -121,8 +161,7 @@ bool MediaPlayerPrivateMediaFoundation::seeking() const bool MediaPlayerPrivateMediaFoundation::paused() const { - notImplemented(); - return false; + return m_paused; } MediaPlayer::NetworkState MediaPlayerPrivateMediaFoundation::networkState() const @@ -133,8 +172,7 @@ MediaPlayer::NetworkState MediaPlayerPrivateMediaFoundation::networkState() cons MediaPlayer::ReadyState MediaPlayerPrivateMediaFoundation::readyState() const { - notImplemented(); - return MediaPlayer::HaveNothing; + return m_readyState; } std::unique_ptr MediaPlayerPrivateMediaFoundation::buffered() const @@ -145,13 +183,32 @@ std::unique_ptr MediaPlayerPrivateMediaFoundation::buffered( bool MediaPlayerPrivateMediaFoundation::didLoadingProgress() const { - notImplemented(); - return false; + return m_loadingProgress; } -void MediaPlayerPrivateMediaFoundation::setSize(const IntSize&) +void MediaPlayerPrivateMediaFoundation::setSize(const IntSize& size) { - notImplemented(); + m_size = size; + + if (!m_videoDisplay) + return; + + LayoutSize scrollOffset; + + FrameView* view = nullptr; + if (m_player && m_player->cachedResourceLoader() && m_player->cachedResourceLoader()->document()) + view = m_player->cachedResourceLoader()->document()->view(); + if (view) + scrollOffset = view->scrollOffsetForFixedPosition(); + + int xPos = -scrollOffset.width().toInt() + m_lastPaintRect.x(); + int yPos = -scrollOffset.height().toInt() + m_lastPaintRect.y(); + + if (m_hwndVideo && !m_lastPaintRect.isEmpty()) + ::MoveWindow(m_hwndVideo, xPos, yPos, m_size.width(), m_size.height(), FALSE); + + RECT rc = { 0, 0, m_size.width(), m_size.height() }; + m_videoDisplay->SetVideoPosition(nullptr, &rc); } void MediaPlayerPrivateMediaFoundation::paint(GraphicsContext* context, const IntRect& rect) @@ -160,10 +217,397 @@ void MediaPlayerPrivateMediaFoundation::paint(GraphicsContext* context, const In || !m_player->visible()) return; - // TODO: Paint the contents of the video to the context + m_lastPaintRect = rect; + + // We currently let Media Foundation handle the drawing, by providing a handle to the window to draw in. + // We should instead read individual frames from the stream, and paint them into the graphics context here. + notImplemented(); } +bool MediaPlayerPrivateMediaFoundation::createSession() +{ + if (FAILED(MFStartup(MF_VERSION, MFSTARTUP_FULL))) + return false; + + if (FAILED(MFCreateMediaSession(nullptr, &m_mediaSession))) + return false; + + // Get next event. + AsyncCallback* callback = new AsyncCallback(this, true); + HRESULT hr = m_mediaSession->BeginGetEvent(callback, nullptr); + ASSERT(SUCCEEDED(hr)); + + return true; +} + +bool MediaPlayerPrivateMediaFoundation::endSession() +{ + if (m_mediaSession) { + m_mediaSession->Shutdown(); + m_mediaSession->Release(); + m_mediaSession = nullptr; + } + + HRESULT hr = MFShutdown(); + ASSERT(SUCCEEDED(hr)); + + return true; +} + +bool MediaPlayerPrivateMediaFoundation::startCreateMediaSource(const String& url) +{ + if (FAILED(MFCreateSourceResolver(&m_sourceResolver))) + return false; + + COMPtr cancelCookie; + Vector urlSource = url.charactersWithNullTermination(); + + AsyncCallback* callback = new AsyncCallback(this, false); + + if (FAILED(m_sourceResolver->BeginCreateObjectFromURL(urlSource.data(), MF_RESOLUTION_MEDIASOURCE, nullptr, &cancelCookie, callback, nullptr))) + return false; + + return true; +} + +bool MediaPlayerPrivateMediaFoundation::endCreatedMediaSource(IMFAsyncResult* asyncResult) +{ + MF_OBJECT_TYPE objectType; + COMPtr source; + + HRESULT hr = m_sourceResolver->EndCreateObjectFromURL(asyncResult, &objectType, &source); + if (FAILED(hr)) + return false; + + hr = source->QueryInterface(IID_PPV_ARGS(&m_mediaSource)); + if (FAILED(hr)) + return false; + + hr = asyncResult->GetStatus(); + m_loadingProgress = SUCCEEDED(hr); + + callOnMainThread([this] { + onCreatedMediaSource(); + }); + + return true; +} + +bool MediaPlayerPrivateMediaFoundation::endGetEvent(IMFAsyncResult* asyncResult) +{ + COMPtr event; + + // Get the event from the event queue. + HRESULT hr = m_mediaSession->EndGetEvent(asyncResult, &event); + if (FAILED(hr)) + return false; + + // Get the event type. + MediaEventType mediaEventType; + hr = event->GetType(&mediaEventType); + if (FAILED(hr)) + return false; + + switch (mediaEventType) { + case MESessionTopologySet: + callOnMainThread([this] { + onTopologySet(); + }); + break; + + case MESessionClosed: + break; + } + + if (mediaEventType != MESessionClosed) { + // For all other events, ask the media session for the + // next event in the queue. + AsyncCallback* callback = new AsyncCallback(this, true); + + hr = m_mediaSession->BeginGetEvent(callback, nullptr); + if (FAILED(hr)) + return false; + } + + return true; +} + +bool MediaPlayerPrivateMediaFoundation::createTopologyFromSource() +{ + // Create a new topology. + if (FAILED(MFCreateTopology(&m_topology))) + return false; + + // Create the presentation descriptor for the media source. + if (FAILED(m_mediaSource->CreatePresentationDescriptor(&m_sourcePD))) + return false; + + // Get the number of streams in the media source. + DWORD sourceStreams = 0; + if (FAILED(m_sourcePD->GetStreamDescriptorCount(&sourceStreams))) + return false; + + // For each stream, create the topology nodes and add them to the topology. + for (DWORD i = 0; i < sourceStreams; i++) { + if (!addBranchToPartialTopology(i)) + return false; + } + + return true; +} + +bool MediaPlayerPrivateMediaFoundation::addBranchToPartialTopology(int stream) +{ + // Get the stream descriptor for this stream. + COMPtr sourceSD; + BOOL selected = FALSE; + if (FAILED(m_sourcePD->GetStreamDescriptorByIndex(stream, &selected, &sourceSD))) + return false; + + // Create the topology branch only if the stream is selected. + // Otherwise, do nothing. + if (!selected) + return true; + + // Create a source node for this stream. + COMPtr sourceNode; + if (!createSourceStreamNode(sourceSD, sourceNode)) + return false; + + COMPtr outputNode; + if (!createOutputNode(sourceSD, outputNode)) + return false; + + // Add both nodes to the topology. + if (FAILED(m_topology->AddNode(sourceNode.get()))) + return false; + + if (FAILED(m_topology->AddNode(outputNode.get()))) + return false; + + // Connect the source node to the output node. + if (FAILED(sourceNode->ConnectOutput(0, outputNode.get(), 0))) + return false; + + return true; +} + +LRESULT CALLBACK MediaPlayerPrivateMediaFoundation::VideoViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return DefWindowProc(hWnd, message, wParam, lParam); +} + +LPCWSTR MediaPlayerPrivateMediaFoundation::registerVideoWindowClass() +{ + const LPCWSTR kVideoWindowClassName = L"WebVideoWindowClass"; + + static bool haveRegisteredWindowClass = false; + if (haveRegisteredWindowClass) + return kVideoWindowClassName; + + haveRegisteredWindowClass = true; + + WNDCLASSEX wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_DBLCLKS; + wcex.lpfnWndProc = VideoViewWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = nullptr; + wcex.hIcon = nullptr; + wcex.hCursor = ::LoadCursor(0, IDC_ARROW); + wcex.hbrBackground = nullptr; + wcex.lpszMenuName = nullptr; + wcex.lpszClassName = kVideoWindowClassName; + wcex.hIconSm = nullptr; + + if (RegisterClassEx(&wcex)) + return kVideoWindowClassName; + + return nullptr; +} + +void MediaPlayerPrivateMediaFoundation::createVideoWindow() +{ + HWND hWndParent = nullptr; + FrameView* view = nullptr; + if (!m_player || !m_player->cachedResourceLoader() || !m_player->cachedResourceLoader()->document()) + return; + view = m_player->cachedResourceLoader()->document()->view(); + if (!view || !view->hostWindow()) + return; + hWndParent = view->hostWindow()->platformPageClient(); + + m_hwndVideo = CreateWindowEx(WS_EX_NOACTIVATE | WS_EX_TRANSPARENT, registerVideoWindowClass(), 0, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + 0, 0, 0, 0, hWndParent, 0, 0, 0); +} + +void MediaPlayerPrivateMediaFoundation::destroyVideoWindow() +{ + if (m_hwndVideo) { + DestroyWindow(m_hwndVideo); + m_hwndVideo = nullptr; + } +} + +bool MediaPlayerPrivateMediaFoundation::createOutputNode(COMPtr sourceSD, COMPtr& node) +{ + if (!sourceSD) + return false; + +#ifndef NDEBUG + // Get the stream ID. + DWORD streamID = 0; + sourceSD->GetStreamIdentifier(&streamID); // Just for debugging, ignore any failures. +#endif + + COMPtr handler; + if (FAILED(sourceSD->GetMediaTypeHandler(&handler))) + return false; + + GUID guidMajorType = GUID_NULL; + if (FAILED(handler->GetMajorType(&guidMajorType))) + return false; + + // Create a downstream node. + if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node))) + return false; + + // Create an IMFActivate object for the renderer, based on the media type. + COMPtr rendererActivate; + if (MFMediaType_Audio == guidMajorType) { + // Create the audio renderer. + if (FAILED(MFCreateAudioRendererActivate(&rendererActivate))) + return false; + m_hasAudio = true; + } else if (MFMediaType_Video == guidMajorType) { + // Create the video renderer. + if (FAILED(MFCreateVideoRendererActivate(m_hwndVideo, &rendererActivate))) + return false; + m_hasVideo = true; + } else + return false; + + // Set the IActivate object on the output node. + if (FAILED(node->SetObject(rendererActivate.get()))) + return false; + + return true; +} + +bool MediaPlayerPrivateMediaFoundation::createSourceStreamNode(COMPtr sourceSD, COMPtr& node) +{ + if (!m_mediaSource || !m_sourcePD || !sourceSD) + return false; + + // Create the source-stream node. + HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); + if (FAILED(hr)) + return false; + + // Set attribute: Pointer to the media source. + hr = node->SetUnknown(MF_TOPONODE_SOURCE, m_mediaSource.get()); + if (FAILED(hr)) + return false; + + // Set attribute: Pointer to the presentation descriptor. + hr = node->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, m_sourcePD.get()); + if (FAILED(hr)) + return false; + + // Set attribute: Pointer to the stream descriptor. + hr = node->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, sourceSD.get()); + if (FAILED(hr)) + return false; + + return true; +} + +void MediaPlayerPrivateMediaFoundation::onCreatedMediaSource() +{ + if (!createTopologyFromSource()) + return; + + // Set the topology on the media session. + HRESULT hr = m_mediaSession->SetTopology(0, m_topology.get()); + ASSERT(SUCCEEDED(hr)); +} + +void MediaPlayerPrivateMediaFoundation::onTopologySet() +{ + if (FAILED(MFGetService(m_mediaSession.get(), MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_videoDisplay)))) + return; + + ASSERT(m_videoDisplay); + + RECT rc = { 0, 0, m_size.width(), m_size.height() }; + m_videoDisplay->SetVideoPosition(nullptr, &rc); + + m_readyState = MediaPlayer::HaveFutureData; + + ASSERT(m_player); + m_player->readyStateChanged(); + + play(); + m_player->playbackStateChanged(); +} + +MediaPlayerPrivateMediaFoundation::AsyncCallback::AsyncCallback(MediaPlayerPrivateMediaFoundation* mediaPlayer, bool event) + : m_refCount(0) + , m_mediaPlayer(mediaPlayer) + , m_event(event) +{ +} + +MediaPlayerPrivateMediaFoundation::AsyncCallback::~AsyncCallback() +{ +} + +HRESULT MediaPlayerPrivateMediaFoundation::AsyncCallback::QueryInterface(REFIID riid, __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject) +{ + if (!IsEqualGUID(riid, IID_IMFAsyncCallback)) { + *ppvObject = nullptr; + return E_NOINTERFACE; + } + *ppvObject = this; + AddRef(); + return S_OK; +} + +ULONG STDMETHODCALLTYPE MediaPlayerPrivateMediaFoundation::AsyncCallback::AddRef() +{ + m_refCount++; + return m_refCount; +} + +ULONG STDMETHODCALLTYPE MediaPlayerPrivateMediaFoundation::AsyncCallback::Release() +{ + m_refCount--; + ULONG refCount = m_refCount; + if (!refCount) + delete this; + return refCount; +} + +HRESULT STDMETHODCALLTYPE MediaPlayerPrivateMediaFoundation::AsyncCallback::GetParameters(__RPC__out DWORD *pdwFlags, __RPC__out DWORD *pdwQueue) +{ + // Returning E_NOTIMPL gives default values. + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE MediaPlayerPrivateMediaFoundation::AsyncCallback::Invoke(__RPC__in_opt IMFAsyncResult *pAsyncResult) +{ + if (m_event) + m_mediaPlayer->endGetEvent(pAsyncResult); + else + m_mediaPlayer->endCreatedMediaSource(pAsyncResult); + + return S_OK; +} + } #endif