blob: 0de19dba25c13ad4ca98e711b563889ffa53743a [file] [log] [blame]
/*
* Copyright (C) 2014 Alex Christensen <achristensen@webkit.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "COMPtr.h"
#include "MediaPlayerPrivate.h"
#include <D3D9.h>
#include <Dxva2api.h>
#include <Mfapi.h>
#include <Mferror.h>
#include <Mfidl.h>
#include <evcode.h>
#include <evr.h>
#include <wtf/Deque.h>
#include <wtf/Lock.h>
#include <wtf/ThreadingPrimitives.h>
#include <wtf/WeakPtr.h>
#include <wtf/win/Win32Handle.h>
namespace WebCore {
class MediaPlayerPrivateMediaFoundation final : public MediaPlayerPrivateInterface, public CanMakeWeakPtr<MediaPlayerPrivateMediaFoundation> {
WTF_MAKE_FAST_ALLOCATED;
public:
explicit MediaPlayerPrivateMediaFoundation(MediaPlayer*);
~MediaPlayerPrivateMediaFoundation();
static void registerMediaEngine(MediaEngineRegistrar);
static void getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>& types);
static MediaPlayer::SupportsType supportsType(const MediaEngineSupportParameters&);
static bool isAvailable();
void load(const String& url) final;
void cancelLoad() final;
void play() final;
void pause() final;
bool supportsFullscreen() const final;
FloatSize naturalSize() const final;
bool hasVideo() const final;
bool hasAudio() const final;
void setPageIsVisible(bool) final;
bool seeking() const final;
void seek(float) final;
void setRate(float) final;
float duration() const final;
float currentTime() const final;
bool paused() const final;
void setVolume(float) final;
void setMuted(bool) final;
MediaPlayer::NetworkState networkState() const final;
MediaPlayer::ReadyState readyState() const final;
float maxTimeSeekable() const final;
std::unique_ptr<PlatformTimeRanges> buffered() const final;
bool didLoadingProgress() const final;
void setSize(const IntSize&) final;
void paint(GraphicsContext&, const FloatRect&) final;
DestinationColorSpace colorSpace() final;
private:
float maxTimeLoaded() const { return m_maxTimeLoaded; }
WeakPtr<MediaPlayerPrivateMediaFoundation> m_weakThis;
MediaPlayer* m_player;
IntSize m_size;
bool m_visible;
bool m_loadingProgress;
bool m_paused;
bool m_seeking { false };
bool m_sessionEnded { false };
bool m_hasAudio;
bool m_hasVideo;
float m_volume;
mutable float m_maxTimeLoaded { 0 };
MediaPlayer::NetworkState m_networkState;
MediaPlayer::ReadyState m_readyState;
class MediaPlayerListener;
HashSet<MediaPlayerListener*> m_listeners;
Lock m_mutexListeners;
FloatSize m_cachedNaturalSize;
mutable Lock m_cachedNaturalSizeLock;
COMPtr<IMFMediaSession> m_mediaSession;
COMPtr<IMFSourceResolver> m_sourceResolver;
COMPtr<IMFMediaSource> m_mediaSource;
COMPtr<IMFTopology> m_topology;
COMPtr<IMFPresentationDescriptor> m_sourcePD;
COMPtr<IMFVideoDisplayControl> m_videoDisplay;
bool createSession();
bool startSession();
bool endSession();
bool startCreateMediaSource(const String& url);
bool endCreatedMediaSource(IMFAsyncResult*);
bool endGetEvent(IMFAsyncResult*);
bool createTopologyFromSource();
bool addBranchToPartialTopology(int stream);
bool createOutputNode(COMPtr<IMFStreamDescriptor> sourceSD, COMPtr<IMFTopologyNode>&);
bool createSourceStreamNode(COMPtr<IMFStreamDescriptor> sourceSD, COMPtr<IMFTopologyNode>&);
void updateReadyState();
COMPtr<IMFVideoDisplayControl> videoDisplay();
void onCreatedMediaSource();
void onTopologySet();
void onBufferingStarted();
void onBufferingStopped();
void onSessionStarted();
void onSessionEnded();
HWND hostWindow();
void invalidateFrameView();
void addListener(MediaPlayerListener*);
void removeListener(MediaPlayerListener*);
void setNaturalSize(const FloatSize&);
void notifyDeleted();
bool setAllChannelVolumes(float);
class MediaPlayerListener {
public:
MediaPlayerListener() = default;
virtual ~MediaPlayerListener() = default;
virtual void onMediaPlayerDeleted() { }
};
class AsyncCallback : public IMFAsyncCallback, public MediaPlayerListener {
WTF_MAKE_FAST_ALLOCATED;
public:
AsyncCallback(MediaPlayerPrivateMediaFoundation*, bool event);
~AsyncCallback();
HRESULT STDMETHODCALLTYPE QueryInterface(_In_ REFIID riid, __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject) override;
ULONG STDMETHODCALLTYPE AddRef() override;
ULONG STDMETHODCALLTYPE Release() override;
HRESULT STDMETHODCALLTYPE GetParameters(__RPC__out DWORD *pdwFlags, __RPC__out DWORD *pdwQueue) override;
HRESULT STDMETHODCALLTYPE Invoke(__RPC__in_opt IMFAsyncResult *pAsyncResult) override;
void onMediaPlayerDeleted() override;
private:
ULONG m_refCount;
MediaPlayerPrivateMediaFoundation* m_mediaPlayer;
bool m_event;
Lock m_mutex;
};
typedef Deque<COMPtr<IMFSample>> VideoSampleList;
class VideoSamplePool {
WTF_MAKE_FAST_ALLOCATED;
public:
VideoSamplePool() = default;
virtual ~VideoSamplePool() = default;
HRESULT initialize(VideoSampleList& samples);
void clear();
HRESULT getSample(COMPtr<IMFSample>&);
HRESULT returnSample(IMFSample*);
bool areSamplesPending();
private:
Lock m_lock;
VideoSampleList m_videoSampleQueue;
bool m_initialized { false };
unsigned m_pending { 0 };
};
class Direct3DPresenter;
class VideoScheduler {
WTF_MAKE_FAST_ALLOCATED;
public:
VideoScheduler() = default;
virtual ~VideoScheduler() = default;
void setPresenter(Direct3DPresenter* presenter) { m_presenter = presenter; }
void setFrameRate(const MFRatio& fps);
void setClockRate(float rate) { m_playbackRate = rate; }
const LONGLONG& lastSampleTime() const { return m_lastSampleTime; }
const LONGLONG& frameDuration() const { return m_frameDuration; }
HRESULT startScheduler(IMFClock*);
HRESULT stopScheduler();
HRESULT scheduleSample(IMFSample*, bool presentNow);
HRESULT processSamplesInQueue(LONG& nextSleep);
HRESULT processSample(IMFSample*, LONG& nextSleep);
HRESULT flush();
private:
static DWORD WINAPI schedulerThreadProc(LPVOID lpParameter);
DWORD schedulerThreadProcPrivate();
Deque<COMPtr<IMFSample>> m_scheduledSamples;
Lock m_lock;
COMPtr<IMFClock> m_clock;
Direct3DPresenter* m_presenter { nullptr };
DWORD m_threadID { 0 };
WTF::Win32Handle m_schedulerThread;
WTF::Win32Handle m_threadReadyEvent;
WTF::Win32Handle m_flushEvent;
float m_playbackRate { 1.0f };
MFTIME m_frameDuration { 0 };
MFTIME m_lastSampleTime { 0 };
std::atomic<bool> m_exitThread { false };
void stopThread() { m_exitThread = true; }
};
class Direct3DPresenter {
WTF_MAKE_FAST_ALLOCATED;
public:
Direct3DPresenter();
~Direct3DPresenter();
enum DeviceState {
DeviceOK,
DeviceReset,
DeviceRemoved,
};
// Returns the IDirect3DDeviceManager9 interface.
HRESULT getService(REFGUID guidService, REFIID riid, void** ppv);
HRESULT checkFormat(D3DFORMAT);
HRESULT setVideoWindow(HWND);
HWND getVideoWindow() const { return m_hwnd; }
HRESULT setDestinationRect(const RECT& destRect);
RECT getDestinationRect() const { return m_destRect; };
HRESULT createVideoSamples(IMFMediaType* format, VideoSampleList& videoSampleQueue);
void releaseResources();
HRESULT checkDeviceState(DeviceState&);
HRESULT presentSample(IMFSample*, LONGLONG target);
UINT refreshRate() const { return m_displayMode.RefreshRate; }
void paintCurrentFrame(GraphicsContext&, const FloatRect&);
private:
HRESULT initializeD3D();
HRESULT getSwapChainPresentParameters(IMFMediaType*, D3DPRESENT_PARAMETERS* presentParams);
HRESULT createD3DDevice();
HRESULT createD3DSample(IDirect3DSwapChain9*, COMPtr<IMFSample>& videoSample);
UINT m_deviceResetToken { 0 };
HWND m_hwnd { nullptr };
RECT m_destRect;
D3DDISPLAYMODE m_displayMode;
Lock m_lock;
COMPtr<IDirect3D9Ex> m_direct3D9;
COMPtr<IDirect3DDevice9Ex> m_device;
COMPtr<IDirect3DDeviceManager9> m_deviceManager;
COMPtr<IDirect3DSurface9> m_surfaceRepaint;
COMPtr<IDirect3DSurface9> m_memSurface;
int m_width { 0 };
int m_height { 0 };
};
class CustomVideoPresenter
: public IMFVideoPresenter
, public IMFVideoDeviceID
, public IMFTopologyServiceLookupClient
, public IMFGetService
, public IMFActivate
, public IMFVideoDisplayControl
, public IMFAsyncCallback
, public MediaPlayerListener {
WTF_MAKE_FAST_ALLOCATED;
public:
CustomVideoPresenter(MediaPlayerPrivateMediaFoundation*);
~CustomVideoPresenter();
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject) override;
ULONG STDMETHODCALLTYPE AddRef() override;
ULONG STDMETHODCALLTYPE Release() override;
// IMFClockStateSink
HRESULT STDMETHODCALLTYPE OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset) override;
HRESULT STDMETHODCALLTYPE OnClockStop(MFTIME hnsSystemTime) override;
HRESULT STDMETHODCALLTYPE OnClockPause(MFTIME hnsSystemTime) override;
HRESULT STDMETHODCALLTYPE OnClockRestart(MFTIME hnsSystemTime) override;
HRESULT STDMETHODCALLTYPE OnClockSetRate(MFTIME hnsSystemTime, float flRate) override;
// IMFVideoPresenter
HRESULT STDMETHODCALLTYPE ProcessMessage(MFVP_MESSAGE_TYPE eMessage, ULONG_PTR ulParam) override;
HRESULT STDMETHODCALLTYPE GetCurrentMediaType(_Outptr_ IMFVideoMediaType **ppMediaType) override;
// IMFVideoDeviceID
HRESULT STDMETHODCALLTYPE GetDeviceID(IID* pDeviceID) override;
// IMFTopologyServiceLookupClient
HRESULT STDMETHODCALLTYPE InitServicePointers(_In_ IMFTopologyServiceLookup *pLookup) override;
HRESULT STDMETHODCALLTYPE ReleaseServicePointers(void) override;
// IMFGetService
HRESULT STDMETHODCALLTYPE GetService(REFGUID guidService, REFIID riid, LPVOID *ppvObject) override;
// IMFActivate
HRESULT STDMETHODCALLTYPE ActivateObject(REFIID riid, void **ppv) override;
HRESULT STDMETHODCALLTYPE DetachObject() override;
HRESULT STDMETHODCALLTYPE ShutdownObject() override;
// IMFAttributes
HRESULT STDMETHODCALLTYPE GetItem(__RPC__in REFGUID guidKey, __RPC__inout_opt PROPVARIANT *pValue) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetItemType(__RPC__in REFGUID guidKey, __RPC__out MF_ATTRIBUTE_TYPE *pType) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE CompareItem(__RPC__in REFGUID guidKey, __RPC__in REFPROPVARIANT Value, __RPC__out BOOL *pbResult) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE Compare(__RPC__in_opt IMFAttributes *pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, __RPC__out BOOL *pbResult) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetUINT32(__RPC__in REFGUID guidKey, __RPC__out UINT32 *punValue) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetUINT64(__RPC__in REFGUID guidKey, __RPC__out UINT64 *punValue) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetDouble(__RPC__in REFGUID guidKey, __RPC__out double *pfValue) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetGUID(__RPC__in REFGUID guidKey, __RPC__out GUID *pguidValue) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetStringLength(__RPC__in REFGUID guidKey, __RPC__out UINT32 *pcchLength) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetString(__RPC__in REFGUID guidKey, __RPC__out_ecount_full(cchBufSize) LPWSTR pwszValue, UINT32 cchBufSize, __RPC__inout_opt UINT32 *pcchLength) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetAllocatedString(__RPC__in REFGUID guidKey, __RPC__deref_out_ecount_full_opt((*pcchLength + 1)) LPWSTR *ppwszValue, __RPC__out UINT32 *pcchLength) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetBlobSize(__RPC__in REFGUID guidKey, __RPC__out UINT32 *pcbBlobSize) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetBlob(__RPC__in REFGUID guidKey, __RPC__out_ecount_full(cbBufSize) UINT8 *pBuf, UINT32 cbBufSize, __RPC__inout_opt UINT32 *pcbBlobSize) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetAllocatedBlob(__RPC__in REFGUID guidKey, __RPC__deref_out_ecount_full_opt(*pcbSize) UINT8 **ppBuf, __RPC__out UINT32 *pcbSize) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetUnknown(__RPC__in REFGUID guidKey, __RPC__in REFIID riid, __RPC__deref_out_opt LPVOID *ppv) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE SetItem(__RPC__in REFGUID guidKey, __RPC__in REFPROPVARIANT Value) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE DeleteItem(__RPC__in REFGUID guidKey) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE DeleteAllItems(void) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE SetUINT32(__RPC__in REFGUID guidKey, UINT32 unValue) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE SetUINT64(__RPC__in REFGUID guidKey, UINT64 unValue) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE SetDouble(__RPC__in REFGUID guidKey, double fValue) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE SetGUID(__RPC__in REFGUID guidKey, __RPC__in REFGUID guidValue) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE SetString(__RPC__in REFGUID guidKey, __RPC__in_string LPCWSTR wszValue) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE SetBlob(__RPC__in REFGUID guidKey, __RPC__in_ecount_full(cbBufSize) const UINT8 *pBuf, UINT32 cbBufSize) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE SetUnknown(__RPC__in REFGUID guidKey, __RPC__in_opt IUnknown *pUnknown) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE LockStore(void) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE UnlockStore(void) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetCount(__RPC__out UINT32 *pcItems) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetItemByIndex(UINT32 unIndex, __RPC__out GUID *pguidKey, __RPC__inout_opt PROPVARIANT *pValue) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE CopyAllItems(__RPC__in_opt IMFAttributes *pDest) override { return E_NOTIMPL; }
// IMFVideoDisplayControl
HRESULT STDMETHODCALLTYPE GetNativeVideoSize(SIZE* pszVideo, SIZE* pszARVideo) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetIdealVideoSize(SIZE* pszMin, SIZE* pszMax) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE SetVideoPosition(const MFVideoNormalizedRect* pnrcSource, const LPRECT prcDest) override;
HRESULT STDMETHODCALLTYPE GetVideoPosition(MFVideoNormalizedRect* pnrcSource, LPRECT prcDest) override;
HRESULT STDMETHODCALLTYPE SetAspectRatioMode(DWORD dwAspectRatioMode) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetAspectRatioMode(DWORD* pdwAspectRatioMode) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE SetVideoWindow(HWND hwndVideo) override;
HRESULT STDMETHODCALLTYPE GetVideoWindow(HWND* phwndVideo) override;
HRESULT STDMETHODCALLTYPE RepaintVideo() override;
HRESULT STDMETHODCALLTYPE GetCurrentImage(BITMAPINFOHEADER* pBih, BYTE** pDib, DWORD* pcbDib, LONGLONG* pTimeStamp) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE SetBorderColor(COLORREF Clr) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetBorderColor(COLORREF* pClr) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE SetRenderingPrefs(DWORD dwRenderFlags) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetRenderingPrefs(DWORD* pdwRenderFlags) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE SetFullscreen(BOOL bFullscreen) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE GetFullscreen(BOOL* pbFullscreen) override { return E_NOTIMPL; }
// IMFAsyncCallback methods
HRESULT STDMETHODCALLTYPE GetParameters(DWORD*, DWORD*) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE Invoke(IMFAsyncResult* pAsyncResult) override;
// MediaPlayerListener
void onMediaPlayerDeleted() override;
void paintCurrentFrame(GraphicsContext&, const FloatRect&);
private:
ULONG m_refCount { 0 };
Lock m_lock;
MediaPlayerPrivateMediaFoundation* m_mediaPlayer;
enum RenderState {
RenderStateStarted = 1,
RenderStateStopped,
RenderStatePaused,
RenderStateShutdown,
};
RenderState m_renderState { RenderStateShutdown };
COMPtr<IMFClock> m_clock;
COMPtr<IMediaEventSink> m_mediaEventSink;
COMPtr<IMFTransform> m_mixer;
COMPtr<IMFMediaType> m_mediaType;
std::unique_ptr<Direct3DPresenter> m_presenterEngine;
MFVideoNormalizedRect m_sourceRect;
bool m_sampleNotify { false };
bool m_prerolled { false };
bool m_repaint { false };
bool m_endStreaming { false };
VideoScheduler m_scheduler;
VideoSamplePool m_samplePool;
unsigned m_tokenCounter { 0 };
float m_rate { 1.0f };
bool isActive() const;
bool isScrubbing() const { return m_rate == 0.0f; }
HRESULT configureMixer(IMFTransform* mixer);
HRESULT flush();
HRESULT setMediaType(IMFMediaType*);
HRESULT checkShutdown() const;
HRESULT renegotiateMediaType();
HRESULT processInputNotify();
HRESULT beginStreaming();
HRESULT endStreaming();
HRESULT checkEndOfStream();
HRESULT isMediaTypeSupported(IMFMediaType*);
HRESULT createOptimalVideoType(IMFMediaType* proposedType, IMFMediaType** optimalType);
HRESULT calculateOutputRectangle(IMFMediaType* proposedType, RECT& outputRect);
void processOutputLoop();
HRESULT processOutput();
HRESULT deliverSample(IMFSample*, bool repaint);
HRESULT trackSample(IMFSample*);
void releaseResources();
HRESULT onSampleFree(IMFAsyncResult*);
void notifyEvent(long EventCode, LONG_PTR Param1, LONG_PTR Param2);
};
COMPtr<CustomVideoPresenter> m_presenter;
};
}