Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef DOM_MEDIA_PLATFORM_WMF_MFTENCODER_H
#define DOM_MEDIA_PLATFORM_WMF_MFTENCODER_H
#include <wrl.h>
#include <deque>
#include <functional>
#include <queue>
#include "EncoderConfig.h"
#include "WMF.h"
#include "mozilla/DataMutex.h"
#include "mozilla/DefineEnum.h"
#include "mozilla/EnumSet.h"
#include "mozilla/MozPromise.h"
#include "mozilla/RefPtr.h"
#include "mozilla/ResultVariant.h"
#include "nsDeque.h"
#include "nsISupportsImpl.h"
#include "nsTArray.h"
namespace mozilla {
class MFTEventSource;
class MFTEncoder final {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MFTEncoder)
struct InputSample {
RefPtr<IMFSample> mSample{};
bool mKeyFrameRequested = false;
};
using MPEGHeader = nsTArray<UINT8>;
struct OutputSample {
RefPtr<IMFSample> mSample{};
MPEGHeader mHeader;
};
using EncodedData = nsTArray<OutputSample>;
using EncodePromise =
MozPromise<EncodedData, MediaResult, /* IsExclusive = */ true>;
enum class HWPreference {
HardwareOnly,
SoftwareOnly,
PreferHardware,
PreferSoftware
};
explicit MFTEncoder(const HWPreference aHWPreference)
: mHWPreference(aHWPreference) {}
HRESULT Create(const GUID& aSubtype, const gfx::IntSize& aFrameSize,
const EncoderConfig::CodecSpecific& aCodecSpecific);
HRESULT Destroy();
HRESULT SetMediaTypes(IMFMediaType* aInputType, IMFMediaType* aOutputType);
HRESULT SetModes(const EncoderConfig& aConfig);
HRESULT SetBitrate(UINT32 aBitsPerSec);
bool IsHardwareAccelerated() const;
RefPtr<EncodePromise> Encode(InputSample&& aInput);
RefPtr<EncodePromise> Drain();
HRESULT CreateInputSample(RefPtr<IMFSample>* aSample, size_t aSize);
Result<MPEGHeader, HRESULT> GetMPEGSequenceHeader();
static nsCString GetFriendlyName(const GUID& aSubtype);
struct Info final {
GUID mSubtype;
nsCString mName;
};
struct Factory {
MOZ_DEFINE_ENUM_CLASS_WITH_TOSTRING_AT_CLASS_SCOPE(
Provider, (HW_AMD, HW_Intel, HW_NVIDIA, HW_Qualcomm, HW_Unknown, SW))
Provider mProvider;
Microsoft::WRL::ComPtr<IMFActivate> mActivate;
nsCString mName;
Factory(Provider aProvider,
Microsoft::WRL::ComPtr<IMFActivate>&& aActivate);
Factory(Factory&& aOther) = default;
Factory(const Factory& aOther) = delete;
~Factory();
explicit operator bool() const { return mActivate; }
HRESULT Shutdown();
};
private:
friend class MFTEventSource;
~MFTEncoder() { Destroy(); };
static nsTArray<Info>& Infos();
static nsTArray<Info> Enumerate();
static Maybe<Info> GetInfo(const GUID& aSubtype);
// APIs for synchronous processing model.
Result<EncodedData, MediaResult> EncodeSync(InputSample&& aInput);
Result<EncodedData, MediaResult> DrainSync();
Result<EncodedData, HRESULT> PullOutputs();
// APIs for asynchronous processing model for regular usage.
Result<EncodedData, MediaResult> EncodeAsync(InputSample&& aInput);
Result<EncodedData, MediaResult> DrainAsync();
MOZ_DEFINE_ENUM_CLASS_WITH_TOSTRING_AT_CLASS_SCOPE(
ProcessedResult, (AllAvailableInputsProcessed, InputProcessed,
OutputYielded, DrainComplete));
using ProcessedResults = EnumSet<ProcessedResult>;
Result<ProcessedResults, HRESULT> ProcessPendingEvents();
Result<MediaEventType, HRESULT> GetPendingEvent();
// For realtime usage in asynchronous processing model only.
RefPtr<EncodePromise> EncodeWithAsyncCallback(InputSample&& aInput);
RefPtr<EncodePromise> DrainWithAsyncCallback();
RefPtr<EncodePromise> PrepareForDrain();
RefPtr<EncodePromise> StartDraining();
void EventHandler(MediaEventType aEventType, HRESULT aStatus);
void MaybeResolveOrRejectEncodePromise();
void MaybeResolveOrRejectDrainPromise();
void MaybeResolveOrRejectPreDrainPromise();
void MaybeResolveOrRejectAnyPendingPromise(
const MediaResult& aResult = NS_OK);
// APIs for asynchronous processing model regardless of usages.
Result<ProcessedResult, HRESULT> ProcessEvent(MediaEventType aType);
Result<ProcessedResult, HRESULT> ProcessInput();
Result<ProcessedResult, HRESULT> ProcessOutput();
Result<ProcessedResult, HRESULT> ProcessDrainComplete();
// Utilities for both processing models.
class OutputResult {
public:
explicit OutputResult(already_AddRefed<IMFSample> aSample)
: mSample(aSample), mHeader() {}
explicit OutputResult(MPEGHeader&& aHeader)
: mSample(nullptr), mHeader(std::move(aHeader)) {}
bool IsSample() const { return mSample != nullptr; }
bool IsHeader() const { return !IsSample(); }
already_AddRefed<IMFSample> TakeSample() {
MOZ_ASSERT(IsSample());
return mSample.forget();
}
MPEGHeader TakeHeader() {
MOZ_ASSERT(IsHeader());
return std::move(mHeader);
}
private:
RefPtr<IMFSample> mSample;
MPEGHeader mHeader;
};
Result<OutputResult, HRESULT> GetOutputOrNewHeader();
// Set the output type to the first available type found for the output
// stream.
HRESULT UpdateOutputType();
HRESULT ProcessOutput(RefPtr<IMFSample>& aSample, DWORD& aOutputStatus,
DWORD& aBufferStatus);
HRESULT ProcessInput(InputSample&& aInput);
bool IsAsync() const { return mAsyncEventSource; }
// Return true when successfully enabled, false for MFT that doesn't support
// async processing model, and error otherwise.
using AsyncMFTResult = Result<bool, HRESULT>;
AsyncMFTResult AttemptEnableAsync();
HRESULT GetStreamIDs();
GUID MatchInputSubtype(IMFMediaType* aInputType);
HRESULT SendMFTMessage(MFT_MESSAGE_TYPE aMsg, ULONG_PTR aData);
MOZ_DEFINE_ENUM_CLASS_WITH_TOSTRING_AT_CLASS_SCOPE(
State,
(Uninited, Initializing, Inited, Encoding, PreDraining, Draining, Error));
void SetState(State aState);
const HWPreference mHWPreference;
RefPtr<IMFTransform> mEncoder;
// For MFT object creation. See
Maybe<Factory> mFactory;
// For encoder configuration. See
RefPtr<ICodecAPI> mConfig;
DWORD mInputStreamID;
DWORD mOutputStreamID;
MFT_INPUT_STREAM_INFO mInputStreamInfo;
MFT_OUTPUT_STREAM_INFO mOutputStreamInfo;
bool mOutputStreamProvidesSample;
State mState = State::Uninited;
bool mIsRealtime = false;
// The following members are used only for asynchronous processing model
size_t mNumNeedInput;
std::deque<InputSample> mPendingInputs;
nsTArray<OutputSample> mOutputs;
// Holds a temporary MPEGSequenceHeader to be attached to the first output
// packet after format renegotiation.
MPEGHeader mOutputHeader;
RefPtr<MFTEventSource> mAsyncEventSource;
// The following members are used only for realtime asynchronous processing
// model.
MediaResult mPendingError;
MozPromiseHolder<EncodePromise> mEncodePromise;
MozPromiseHolder<EncodePromise> mDrainPromise;
MozPromiseHolder<EncodePromise> mPreDrainPromise;
// Use to resolve the encode promise if mAsyncEventSource doesn't response in
// time.
nsCOMPtr<nsITimer> mTimer;
};
class MFTEventSource final : public IMFAsyncCallback {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MFTEventSource)
// A basic IMFMediaEventGenerator wrapper that does not support retrieving
// events from asynchronous callbacks when constructed this way. Events should
// instead be obtained by calling GetEvent().
explicit MFTEventSource(
already_AddRefed<IMFMediaEventGenerator> aEventGenerator)
: MFTEventSource(GetCurrentSerialEventTarget(), nullptr,
std::move(aEventGenerator)) {}
// This constructor creates an MFTEventSource that forwards events from
// asynchronous callbacks directly to the MFTEncoder's event handler. In this
// usage, GetEvent() should not be called, as events are handled
// automatically.
MFTEventSource(MFTEncoder* aEncoder,
already_AddRefed<IMFMediaEventGenerator> aEventGenerator)
: MFTEventSource(GetCurrentSerialEventTarget(), aEncoder,
std::move(aEventGenerator)) {}
bool CanForwardEvents() const { return mEncoder; }
Result<MediaEventType, HRESULT> GetEvent(DWORD aFlags);
HRESULT BeginEventListening();
// IMFAsyncCallback implementations:
STDMETHODIMP GetParameters(DWORD* aFlags, DWORD* aQueue) override;
// Invoke() can be called on any thread by the OS, but it will forward the
// event to the MFTEncoder's working thread.
STDMETHODIMP Invoke(IMFAsyncResult* aAsyncResult) override;
STDMETHODIMP QueryInterface(REFIID aIID, void** aPPV) override;
// AddRef() and Release() are implemented by
// NS_INLINE_DECL_THREADSAFE_REFCOUNTING.
using Id = size_t;
const Id mId;
private:
MFTEventSource(nsISerialEventTarget* aEncoderThread, MFTEncoder* aEncoder,
already_AddRefed<IMFMediaEventGenerator> aEventGenerator);
~MFTEventSource();
static Id GenerateId() {
static Id sNextId = 0;
return sNextId++;
}
// The following members are used to forwards events from any OS thread to the
// MFTEncoder's working thread.
const nsCOMPtr<nsISerialEventTarget> mEncoderThread;
const RefPtr<MFTEncoder> mEncoder;
// When acting as a simple wrapper for IMFMediaEventGenerator, mEventGenerator
// is always accessed from a single thread, making locking effectively
// cost-free. In scenarios where MFTEventSource forwards events to MFTEncoder,
// mEventGenerator will be accessed from multiple threads: event requests are
// made on the MFTEncoder's working thread (via BeginEventListening()), while
// event delivery occurs on the OS thread (via Invoke()). Since these
// operations do not happen concurrently, the overhead of DataMutex locking is
// negligible. DataMutex is used here to clarify that event requests and
// deliveries are performed on separate threads. Furthermore, because
// MFTEncoder might release MFTEventSource while waiting for an event—and the
// Windows Media Foundation documentation does not specify whether releasing
// IMFMediaEventGenerator cancels pending event waits—we release
// mEventGenerator in the MFTEventSource destructor to ensure all pending
// events are properly handled, rather than resetting it when MFTEncoder
// releases MFTEventSource.
DataMutex<RefPtr<IMFMediaEventGenerator>> mEventGenerator;
};
} // namespace mozilla
#endif // DOM_MEDIA_PLATFORM_WMF_MFTENCODER_H