#pragma once ////////////////////////////////////////////////////////////////////////// // // WmfMediaEngine.h : Playback helper class. // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved. // // refer to: // a. // https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/multimedia/mediafoundation/protectedplayback // b. https://docs.microsoft.com/en-us/windows/win32/medfound/seeking--fast-forward--and-reverse-play // ////////////////////////////////////////////////////////////////////////// #if defined(_WIN32) # include #endif #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) # include // C RunTime Header Files # include # include // OpenFile dialog # include // Media Foundation headers # include # include # include # include # include // More DRM errors. // EVR headers # include // Safe string functions # include # include "MFUtils.h" # include # include # include "MediaEngine.h" # include # include # include # include "yasio/byte_buffer.hpp" NS_AX_BEGIN using namespace MFUtils; # define CMD_PENDING 0x01 # define CMD_PENDING_SEEK 0x02 # define CMD_PENDING_RATE 0x04 class WmfMediaEngine : public IMFAsyncCallback, public MediaEngine { enum Command { CmdNone = 0, CmdStop, CmdStart, CmdPause, CmdSeek, }; class CritSec { private: CRITICAL_SECTION m_criticalSection; public: CritSec() { InitializeCriticalSection(&m_criticalSection); } ~CritSec() { DeleteCriticalSection(&m_criticalSection); } void Lock() { EnterCriticalSection(&m_criticalSection); } void Unlock() { LeaveCriticalSection(&m_criticalSection); } }; class AutoLock { private: CritSec* m_pCriticalSection; public: AutoLock(CritSec& crit) { m_pCriticalSection = &crit; m_pCriticalSection->Lock(); } ~AutoLock() { m_pCriticalSection->Unlock(); } }; public: // Constructor is private. Use static CreateInstance method to instantiate. WmfMediaEngine(); HRESULT Initialize(); // IUnknown methods STDMETHODIMP QueryInterface(REFIID iid, void** ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // IMFAsyncCallback methods STDMETHODIMP GetParameters(DWORD*, DWORD*) { // Implementation of this method is optional. return E_NOTIMPL; } STDMETHODIMP Invoke(IMFAsyncResult* pAsyncResult); void fireMediaEvent(MEMediaEventType event) { if (_onMediaEvent) _onMediaEvent(event); } void setCallbacks(std::function onMediaEvent, std::function onVideoFrame) override { _onMediaEvent = std::move(onMediaEvent); _onVideoFrame = std::move(onVideoFrame); } // Playback bool open(std::string_view sourceUri) override; bool close() override; HRESULT Shutdown(); HRESULT HandleEvent(IMFMediaEvent* pUnkPtr); MEMediaState getState() const override { return m_state; } const GUID& GetVideoOutputFormat() const { return m_VideoOutputFormat; } // Video functionality bool setLoop(bool bLooping) override { m_bLooping = bLooping; return true; } void setAutoPlay(bool bAutoPlay) override { m_bAutoPlay = bAutoPlay; } BOOL CanSeek() const; MFTIME GetDuration() const; MFTIME GetCurrentPosition() const; bool setCurrentTime(double sec) override { return SUCCEEDED(SetPosition(static_cast((std::nano::den / 100) * sec))); } // Set position in 100ns units, will reply if play ended // see: https://docs.microsoft.com/en-us/windows/win32/medfound/mf-pd-duration-attribute HRESULT SetPosition(MFTIME hnsPosition); BOOL CanScrub() const; HRESULT Scrub(BOOL bScrub); BOOL CanFastForward() const; BOOL CanRewind() const; bool setRate(double fRate) override; HRESULT FastForward(); HRESULT Rewind(); bool play() override; bool pause() override; bool stop() override; bool isPlaybackEnded() const override { return m_bPlaybackEnded; } void HandleVideoSample(const uint8_t* buf, size_t len); //bool GetLastVideoSample(MEVideoTextueSample& sample) const override; bool transferVideoFrame() override; protected: HRESULT SetPositionInternal(const MFTIME& hnsPosition); HRESULT StartPlayback(const MFTIME* hnsPosition); HRESULT CommitRateChange(float fRate, BOOL bThin); float GetNominalRate() const; HRESULT UpdatePendingCommands(Command cmd); HRESULT GetNativeVideoSize(DWORD* cx, DWORD* cy); void ClearPendingBuffers(); protected: // Destructor is private. Caller should call Release. virtual ~WmfMediaEngine(); HRESULT CreateOutputNode(IMFStreamDescriptor* pSourceSD, IMFTopologyNode** ppNode); HRESULT CreateSession(); HRESULT CloseSession(); HRESULT CreateMediaSource(const WCHAR* sURL); HRESULT CreateTopologyFromSource(IMFTopology** ppTopology); HRESULT AddBranchToPartialTopology(IMFTopology* pTopology, IMFPresentationDescriptor* pSourcePD, DWORD iStream); // Media event handlers HRESULT OnTopologyReady(IMFMediaEvent* pEvent); HRESULT OnPlayEnded(IMFMediaEvent* pEvent); HRESULT OnSessionStart(HRESULT hr); HRESULT OnSessionStop(HRESULT hr); HRESULT OnSessionPause(HRESULT hr); HRESULT OnSessionEnded(HRESULT hr); long m_nRefCount; // Reference count. TComPtr m_pSession; TComPtr m_pSource; TComPtr m_PresentDescriptor; TComPtr m_RateControl; TComPtr m_RateSupport; TComPtr m_pClock; TComPtr m_videoInputType; DWORD m_caps = 0; // Session caps. MFTIME m_hnsDuration = 0; // Duration of the current presentation. BOOL m_bCanScrub = FALSE; // Does the current session support rate = 0. float m_fPrevRate = 1.0f; // Describes the current or requested state, with respect to seeking and // playback rate. struct SeekState { Command command; float fRate; // Playback rate BOOL bThin; // Thinned playback? MFTIME hnsStart; // Start position }; SeekState m_nominal{CmdStop, 1.0, FALSE, 0}; // Current nominal state. SeekState m_request{CmdNone, 1.0, FALSE, 0}; // Pending request state. BOOL m_bPending = FALSE; // Is a request pending? std::atomic m_bOpenPending = false; std::atomic m_bClosePending = false; bool m_bPlaybackEnded = false; mutable CritSec m_critsec; // Protects the seeking and rate-change states. std::atomic m_state = MEMediaState::Closed; // Current state of the media session. HANDLE m_hOpenEvent = nullptr; // App window to receive events. HANDLE m_hCloseEvent = nullptr; // Event to wait on while closing MEIntPoint m_videoExtent; MEIntPoint m_frameExtent; // may same with m_videoExtent BOOL m_bLooping = FALSE; BOOL m_bAutoPlay = TRUE; BOOL m_bIsH264 = FALSE; BOOL m_bIsHEVC = FALSE; // hvc1,hev1 GUID m_VideoOutputFormat{}; std::function _onMediaEvent; std::function _onVideoFrame; MEVideoPixelFormat m_videoPF = MEVideoPixelFormat::INVALID; mutable std::mutex m_frameBuffer1Mtx; yasio::byte_buffer m_frameBuffer1; // for write yasio::byte_buffer m_frameBuffer2; // for read }; struct WmfMediaEngineFactory : public MediaEngineFactory { MediaEngine* createMediaEngine() override { auto engine = new WmfMediaEngine(); auto hr = engine->Initialize(); if (SUCCEEDED(hr)) return engine; engine->Release(); return nullptr; } void destroyMediaEngine(MediaEngine* me) override { auto wmfme = static_cast(me); wmfme->Shutdown(); wmfme->Release(); } }; NS_AX_END #endif