summaryrefslogtreecommitdiffstats
path: root/dom/media/platforms/wmf
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/platforms/wmf')
-rw-r--r--dom/media/platforms/wmf/MFTEncoder.cpp22
-rw-r--r--dom/media/platforms/wmf/WMF.h43
-rw-r--r--dom/media/platforms/wmf/WMFDataEncoderUtils.cpp221
-rw-r--r--dom/media/platforms/wmf/WMFDataEncoderUtils.h140
-rw-r--r--dom/media/platforms/wmf/WMFDecoderModule.cpp34
-rw-r--r--dom/media/platforms/wmf/WMFDecoderModule.h15
-rw-r--r--dom/media/platforms/wmf/WMFEncoderModule.cpp3
-rw-r--r--dom/media/platforms/wmf/WMFMediaDataEncoder.cpp399
-rw-r--r--dom/media/platforms/wmf/WMFMediaDataEncoder.h302
-rw-r--r--dom/media/platforms/wmf/WMFVideoMFTManager.cpp6
-rw-r--r--dom/media/platforms/wmf/moz.build2
11 files changed, 752 insertions, 435 deletions
diff --git a/dom/media/platforms/wmf/MFTEncoder.cpp b/dom/media/platforms/wmf/MFTEncoder.cpp
index 410da2733c..424ba7055b 100644
--- a/dom/media/platforms/wmf/MFTEncoder.cpp
+++ b/dom/media/platforms/wmf/MFTEncoder.cpp
@@ -10,6 +10,7 @@
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/mscom/Utils.h"
#include "WMFUtils.h"
+#include <comdef.h>
// Missing from MinGW.
#ifndef CODECAPI_AVEncAdaptiveMode
@@ -231,6 +232,7 @@ HRESULT MFTEncoder::Create(const GUID& aSubtype) {
RefPtr<IMFActivate> factory = CreateFactory(aSubtype);
if (!factory) {
+ MFT_ENC_LOGE("CreateFactory error");
return E_FAIL;
}
@@ -238,12 +240,18 @@ HRESULT MFTEncoder::Create(const GUID& aSubtype) {
RefPtr<IMFTransform> encoder;
HRESULT hr = factory->ActivateObject(
IID_PPV_ARGS(static_cast<IMFTransform**>(getter_AddRefs(encoder))));
- NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+ if (FAILED(hr)) {
+ _com_error error(hr);
+ MFT_ENC_LOGE("MFTEncoder::Create: error = 0x%lX, %ls", hr,
+ error.ErrorMessage());
+ return hr;
+ }
RefPtr<ICodecAPI> config;
// Avoid IID_PPV_ARGS() here for MingGW fails to declare UUID for ICodecAPI.
hr = encoder->QueryInterface(IID_ICodecAPI, getter_AddRefs(config));
if (FAILED(hr)) {
+ MFT_ENC_LOGE("QueryInterface IID_ICodecAPI error");
encoder = nullptr;
factory->ShutdownObject();
return hr;
@@ -276,7 +284,12 @@ MFTEncoder::SetMediaTypes(IMFMediaType* aInputType, IMFMediaType* aOutputType) {
MOZ_ASSERT(aInputType && aOutputType);
AsyncMFTResult asyncMFT = AttemptEnableAsync();
- NS_ENSURE_TRUE(asyncMFT.isOk(), asyncMFT.unwrapErr());
+ if (asyncMFT.isErr()) {
+ HRESULT hr = asyncMFT.inspectErr();
+ _com_error error(hr);
+ MFT_ENC_LOGE("AttemptEnableAsync error: %ls", error.ErrorMessage());
+ return asyncMFT.inspectErr();
+ }
HRESULT hr = GetStreamIDs();
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
@@ -325,6 +338,7 @@ MFTEncoder::AsyncMFTResult MFTEncoder::AttemptEnableAsync() {
IMFAttributes* pAttributes = nullptr;
HRESULT hr = mEncoder->GetAttributes(&pAttributes);
if (FAILED(hr)) {
+ MFT_ENC_LOGE("Encoder->GetAttribute error");
return AsyncMFTResult(hr);
}
@@ -337,6 +351,10 @@ MFTEncoder::AsyncMFTResult MFTEncoder::AttemptEnableAsync() {
}
pAttributes->Release();
+ if (FAILED(hr)) {
+ MFT_ENC_LOGE("Setting async unlock");
+ }
+
return SUCCEEDED(hr) ? AsyncMFTResult(async) : AsyncMFTResult(hr);
}
diff --git a/dom/media/platforms/wmf/WMF.h b/dom/media/platforms/wmf/WMF.h
index 740442ceda..86afcb8e5c 100644
--- a/dom/media/platforms/wmf/WMF.h
+++ b/dom/media/platforms/wmf/WMF.h
@@ -23,6 +23,7 @@
#include <codecapi.h>
#include "mozilla/Atomics.h"
+#include "mozilla/AppShutdown.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/StaticMutex.h"
#include "nsThreadUtils.h"
@@ -74,7 +75,8 @@ class MediaFoundationInitializer final {
if (sIsShutdown) {
return false;
}
- return Get()->mHasInitialized;
+ auto* rv = Get();
+ return rv ? rv->mHasInitialized : false;
}
private:
@@ -82,17 +84,36 @@ class MediaFoundationInitializer final {
{
StaticMutexAutoLock lock(sCreateMutex);
if (!sInitializer) {
+ // Already in shutdown.
+ if (AppShutdown::GetCurrentShutdownPhase() !=
+ ShutdownPhase::NotInShutdown) {
+ sIsShutdown = true;
+ return nullptr;
+ }
sInitializer.reset(new MediaFoundationInitializer());
- GetMainThreadSerialEventTarget()->Dispatch(
- NS_NewRunnableFunction("MediaFoundationInitializer::Get", [&] {
- // Need to run this before MTA thread gets destroyed.
- RunOnShutdown(
- [&] {
- sInitializer.reset();
- sIsShutdown = true;
- },
- ShutdownPhase::XPCOMShutdown);
- }));
+ auto shutdownCleanUp = [&] {
+ if (AppShutdown::GetCurrentShutdownPhase() !=
+ ShutdownPhase::NotInShutdown) {
+ sInitializer.reset();
+ sIsShutdown = true;
+ return;
+ }
+ // As MFShutdown needs to run on the MTA thread that is destroyed
+ // on XPCOMShutdownThreads, so we need to run cleanup before that
+ // phase.
+ RunOnShutdown(
+ [&]() {
+ sInitializer.reset();
+ sIsShutdown = true;
+ },
+ ShutdownPhase::XPCOMShutdown);
+ };
+ if (NS_IsMainThread()) {
+ shutdownCleanUp();
+ } else {
+ GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
+ "MediaFoundationInitializer::Get", shutdownCleanUp));
+ }
}
}
return sInitializer.get();
diff --git a/dom/media/platforms/wmf/WMFDataEncoderUtils.cpp b/dom/media/platforms/wmf/WMFDataEncoderUtils.cpp
new file mode 100644
index 0000000000..3bab3ccb46
--- /dev/null
+++ b/dom/media/platforms/wmf/WMFDataEncoderUtils.cpp
@@ -0,0 +1,221 @@
+/* -*- 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/. */
+
+#include "WMFDataEncoderUtils.h"
+
+#include "EncoderConfig.h"
+#include "MFTEncoder.h"
+#include "MediaData.h"
+#include "mozilla/Logging.h"
+
+namespace mozilla {
+
+#define WMF_ENC_LOG(arg, ...) \
+ MOZ_LOG(mozilla::sPEMLog, mozilla::LogLevel::Error, \
+ ("WMFDataEncoderUtils::%s: " arg, __func__, ##__VA_ARGS__))
+
+GUID CodecToSubtype(CodecType aCodec) {
+ switch (aCodec) {
+ case CodecType::H264:
+ return MFVideoFormat_H264;
+ case CodecType::VP8:
+ return MFVideoFormat_VP80;
+ case CodecType::VP9:
+ return MFVideoFormat_VP90;
+ default:
+ return GUID_NULL;
+ }
+}
+
+bool CanCreateWMFEncoder(CodecType aCodec) {
+ bool canCreate = false;
+ mscom::EnsureMTA([&]() {
+ if (!wmf::MediaFoundationInitializer::HasInitialized()) {
+ return;
+ }
+ // Try HW encoder first.
+ auto enc = MakeRefPtr<MFTEncoder>(false /* HW not allowed */);
+ canCreate = SUCCEEDED(enc->Create(CodecToSubtype(aCodec)));
+ if (!canCreate) {
+ // Try SW encoder.
+ enc = MakeRefPtr<MFTEncoder>(true /* HW not allowed */);
+ canCreate = SUCCEEDED(enc->Create(CodecToSubtype(aCodec)));
+ }
+ });
+ return canCreate;
+}
+
+static already_AddRefed<MediaByteBuffer> ParseH264Parameters(
+ nsTArray<uint8_t>& aHeader, const bool aAsAnnexB) {
+ size_t length = aHeader.Length();
+ auto annexB = MakeRefPtr<MediaByteBuffer>(length);
+ PodCopy(annexB->Elements(), aHeader.Elements(), length);
+ annexB->SetLength(length);
+ if (aAsAnnexB) {
+ return annexB.forget();
+ }
+
+ // Convert to avcC.
+ nsTArray<AnnexB::NALEntry> paramSets;
+ AnnexB::ParseNALEntries(
+ Span<const uint8_t>(annexB->Elements(), annexB->Length()), paramSets);
+
+ auto avcc = MakeRefPtr<MediaByteBuffer>();
+ AnnexB::NALEntry& sps = paramSets.ElementAt(0);
+ AnnexB::NALEntry& pps = paramSets.ElementAt(1);
+ const uint8_t* spsPtr = annexB->Elements() + sps.mOffset;
+ H264::WriteExtraData(
+ avcc, spsPtr[1], spsPtr[2], spsPtr[3],
+ Span<const uint8_t>(spsPtr, sps.mSize),
+ Span<const uint8_t>(annexB->Elements() + pps.mOffset, pps.mSize));
+ return avcc.forget();
+}
+
+static uint32_t GetProfile(H264_PROFILE aProfileLevel) {
+ switch (aProfileLevel) {
+ case H264_PROFILE_BASE:
+ return eAVEncH264VProfile_Base;
+ case H264_PROFILE_MAIN:
+ return eAVEncH264VProfile_Main;
+ case H264_PROFILE_HIGH:
+ return eAVEncH264VProfile_High;
+ default:
+ return eAVEncH264VProfile_unknown;
+ }
+}
+
+already_AddRefed<IMFMediaType> CreateInputType(EncoderConfig& aConfig) {
+ RefPtr<IMFMediaType> type;
+ HRESULT hr = wmf::MFCreateMediaType(getter_AddRefs(type));
+ if (FAILED(hr)) {
+ WMF_ENC_LOG("MFCreateMediaType (input) error: %lx", hr);
+ return nullptr;
+ }
+ hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
+ if (FAILED(hr)) {
+ WMF_ENC_LOG("Create input type: SetGUID (major type) error: %lx", hr);
+ return nullptr;
+ }
+ // Always NV12 input
+ hr = type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12);
+ if (FAILED(hr)) {
+ WMF_ENC_LOG("Create input type: SetGUID (subtype) error: %lx", hr);
+ return nullptr;
+ }
+ hr = type->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
+ if (FAILED(hr)) {
+ WMF_ENC_LOG("Create input type: interlace mode (input) error: %lx", hr);
+ return nullptr;
+ }
+ // WMF requires a framerate to intialize properly. Provide something
+ // reasonnable if not provided.
+ if (!aConfig.mFramerate) {
+ aConfig.mFramerate = 30;
+ }
+ if (aConfig.mFramerate) {
+ hr = MFSetAttributeRatio(type, MF_MT_FRAME_RATE, aConfig.mFramerate, 1);
+ if (FAILED(hr)) {
+ WMF_ENC_LOG("Create input type: frame rate (input) error: %lx", hr);
+ return nullptr;
+ }
+ }
+ hr = MFSetAttributeSize(type, MF_MT_FRAME_SIZE, aConfig.mSize.width,
+ aConfig.mSize.height);
+ if (FAILED(hr)) {
+ WMF_ENC_LOG("Create input type: frame size (input) error: %lx", hr);
+ return nullptr;
+ }
+ return type.forget();
+}
+
+already_AddRefed<IMFMediaType> CreateOutputType(EncoderConfig& aConfig) {
+ RefPtr<IMFMediaType> type;
+ HRESULT hr = wmf::MFCreateMediaType(getter_AddRefs(type));
+ if (FAILED(hr)) {
+ WMF_ENC_LOG("MFCreateMediaType (output) error: %lx", hr);
+ return nullptr;
+ }
+ hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
+ if (FAILED(hr)) {
+ WMF_ENC_LOG("Create output type: set major type error: %lx", hr);
+ return nullptr;
+ }
+ hr = type->SetGUID(MF_MT_SUBTYPE, CodecToSubtype(aConfig.mCodec));
+ if (FAILED(hr)) {
+ WMF_ENC_LOG("Create output type: set subtype error: %lx", hr);
+ return nullptr;
+ }
+ // A bitrate need to be set here, attempt to make an educated guess if none is
+ // provided. This could be per codec to have nicer defaults.
+ size_t longDimension = std::max(aConfig.mSize.width, aConfig.mSize.height);
+ if (!aConfig.mBitrate) {
+ if (longDimension < 720) {
+ aConfig.mBitrate = 2000000;
+ } else if (longDimension < 1080) {
+ aConfig.mBitrate = 4000000;
+ } else {
+ aConfig.mBitrate = 8000000;
+ }
+ }
+ // No way to set variable / constant here.
+ hr = type->SetUINT32(MF_MT_AVG_BITRATE, aConfig.mBitrate);
+ if (FAILED(hr)) {
+ WMF_ENC_LOG("Create output type: set bitrate error: %lx", hr);
+ return nullptr;
+ }
+ hr = type->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
+ if (FAILED(hr)) {
+ WMF_ENC_LOG("Create output type set interlave mode error: %lx", hr);
+ return nullptr;
+ }
+ // A positive rate must always be preset here, see the Input config part.
+ MOZ_ASSERT(aConfig.mFramerate);
+ if (aConfig.mFramerate) {
+ hr = MFSetAttributeRatio(type, MF_MT_FRAME_RATE, aConfig.mFramerate, 1);
+ if (FAILED(hr)) {
+ WMF_ENC_LOG("Create output type set frame rate error: %lx", hr);
+ return nullptr;
+ }
+ }
+ // Required
+ hr = MFSetAttributeSize(type, MF_MT_FRAME_SIZE, aConfig.mSize.width,
+ aConfig.mSize.height);
+ if (FAILED(hr)) {
+ WMF_ENC_LOG("Create output type set frame size error: %lx", hr);
+ return nullptr;
+ }
+
+ if (aConfig.mCodecSpecific) {
+ if (aConfig.mCodecSpecific->is<H264Specific>()) {
+ MOZ_ASSERT(aConfig.mCodec == CodecType::H264);
+ hr = FAILED(type->SetUINT32(
+ MF_MT_MPEG2_PROFILE,
+ GetProfile(aConfig.mCodecSpecific->as<H264Specific>().mProfile)));
+ if (hr) {
+ WMF_ENC_LOG("Create output type set profile error: %lx", hr);
+ return nullptr;
+ }
+ }
+ }
+
+ return type.forget();
+}
+
+HRESULT SetMediaTypes(RefPtr<MFTEncoder>& aEncoder, EncoderConfig& aConfig) {
+ RefPtr<IMFMediaType> inputType = CreateInputType(aConfig);
+ if (!inputType) {
+ return E_FAIL;
+ }
+
+ RefPtr<IMFMediaType> outputType = CreateOutputType(aConfig);
+ if (!outputType) {
+ return E_FAIL;
+ }
+
+ return aEncoder->SetMediaTypes(inputType, outputType);
+}
+
+} // namespace mozilla
diff --git a/dom/media/platforms/wmf/WMFDataEncoderUtils.h b/dom/media/platforms/wmf/WMFDataEncoderUtils.h
index 19f04e768f..0bb4e00086 100644
--- a/dom/media/platforms/wmf/WMFDataEncoderUtils.h
+++ b/dom/media/platforms/wmf/WMFDataEncoderUtils.h
@@ -2,13 +2,16 @@
* 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/. */
-#include "WMFMediaDataEncoder.h"
-
+#ifndef WMFDATAENCODERUTILS_H_
+#define WMFDATAENCODERUTILS_H_
+#include <mfapi.h>
+#include "EncoderConfig.h"
#include "AnnexB.h"
#include "H264.h"
#include "libyuv.h"
#include "mozilla/Logging.h"
#include "mozilla/mscom/EnsureMTA.h"
+#include "WMF.h"
#define WMF_ENC_LOGD(arg, ...) \
MOZ_LOG( \
@@ -21,133 +24,24 @@
namespace mozilla {
-extern LazyLogModule sPEMLog;
-
-static const GUID CodecToSubtype(CodecType aCodec) {
- switch (aCodec) {
- case CodecType::H264:
- return MFVideoFormat_H264;
- case CodecType::VP8:
- return MFVideoFormat_VP80;
- case CodecType::VP9:
- return MFVideoFormat_VP90;
- default:
- return GUID_NULL;
- }
-}
-
-bool CanCreateWMFEncoder(CodecType aCodec) {
- bool canCreate = false;
- mscom::EnsureMTA([&]() {
- if (!wmf::MediaFoundationInitializer::HasInitialized()) {
- return;
- }
- // Try HW encoder first.
- auto enc = MakeRefPtr<MFTEncoder>(false /* HW not allowed */);
- canCreate = SUCCEEDED(enc->Create(CodecToSubtype(aCodec)));
- if (!canCreate) {
- // Try SW encoder.
- enc = MakeRefPtr<MFTEncoder>(true /* HW not allowed */);
- canCreate = SUCCEEDED(enc->Create(CodecToSubtype(aCodec)));
- }
- });
- return canCreate;
-}
-
-static already_AddRefed<MediaByteBuffer> ParseH264Parameters(
- nsTArray<uint8_t>& aHeader, const bool aAsAnnexB) {
- size_t length = aHeader.Length();
- auto annexB = MakeRefPtr<MediaByteBuffer>(length);
- PodCopy(annexB->Elements(), aHeader.Elements(), length);
- annexB->SetLength(length);
- if (aAsAnnexB) {
- return annexB.forget();
- }
+class MFTEncoder;
- // Convert to avcC.
- nsTArray<AnnexB::NALEntry> paramSets;
- AnnexB::ParseNALEntries(
- Span<const uint8_t>(annexB->Elements(), annexB->Length()), paramSets);
-
- auto avcc = MakeRefPtr<MediaByteBuffer>();
- AnnexB::NALEntry& sps = paramSets.ElementAt(0);
- AnnexB::NALEntry& pps = paramSets.ElementAt(1);
- const uint8_t* spsPtr = annexB->Elements() + sps.mOffset;
- H264::WriteExtraData(
- avcc, spsPtr[1], spsPtr[2], spsPtr[3],
- Span<const uint8_t>(spsPtr, sps.mSize),
- Span<const uint8_t>(annexB->Elements() + pps.mOffset, pps.mSize));
- return avcc.forget();
-}
-
-static uint32_t GetProfile(H264_PROFILE aProfileLevel) {
- switch (aProfileLevel) {
- case H264_PROFILE_BASE:
- return eAVEncH264VProfile_Base;
- case H264_PROFILE_MAIN:
- return eAVEncH264VProfile_Main;
- default:
- return eAVEncH264VProfile_unknown;
- }
-}
+extern LazyLogModule sPEMLog;
-already_AddRefed<IMFMediaType> CreateInputType(EncoderConfig& aConfig) {
- RefPtr<IMFMediaType> type;
- return SUCCEEDED(wmf::MFCreateMediaType(getter_AddRefs(type))) &&
- SUCCEEDED(
- type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video)) &&
- SUCCEEDED(type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12)) &&
- SUCCEEDED(type->SetUINT32(MF_MT_INTERLACE_MODE,
- MFVideoInterlace_Progressive)) &&
- SUCCEEDED(MFSetAttributeRatio(type, MF_MT_FRAME_RATE,
- aConfig.mFramerate, 1)) &&
- SUCCEEDED(MFSetAttributeSize(type, MF_MT_FRAME_SIZE,
- aConfig.mSize.width,
- aConfig.mSize.height))
- ? type.forget()
- : nullptr;
-}
+GUID CodecToSubtype(CodecType aCodec);
-already_AddRefed<IMFMediaType> CreateOutputType(EncoderConfig& aConfig) {
- RefPtr<IMFMediaType> type;
- if (FAILED(wmf::MFCreateMediaType(getter_AddRefs(type))) ||
- FAILED(type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video)) ||
- FAILED(type->SetGUID(MF_MT_SUBTYPE, CodecToSubtype(aConfig.mCodec))) ||
- FAILED(type->SetUINT32(MF_MT_AVG_BITRATE, aConfig.mBitrate)) ||
- FAILED(type->SetUINT32(MF_MT_INTERLACE_MODE,
- MFVideoInterlace_Progressive)) ||
- FAILED(
- MFSetAttributeRatio(type, MF_MT_FRAME_RATE, aConfig.mFramerate, 1)) ||
- FAILED(MFSetAttributeSize(type, MF_MT_FRAME_SIZE, aConfig.mSize.width,
- aConfig.mSize.height))) {
- return nullptr;
- }
- if (aConfig.mCodecSpecific) {
- if (aConfig.mCodecSpecific->is<H264Specific>()) {
- if (FAILED(type->SetUINT32(
- MF_MT_MPEG2_PROFILE,
- GetProfile(
- aConfig.mCodecSpecific->as<H264Specific>().mProfile)))) {
- return nullptr;
- }
- }
- }
+bool CanCreateWMFEncoder(CodecType aCodec);
- return type.forget();
-}
+already_AddRefed<MediaByteBuffer> ParseH264Parameters(
+ nsTArray<uint8_t>& aHeader, const bool aAsAnnexB);
+uint32_t GetProfile(H264_PROFILE aProfileLevel);
-HRESULT SetMediaTypes(RefPtr<MFTEncoder>& aEncoder, EncoderConfig& aConfig) {
- RefPtr<IMFMediaType> inputType = CreateInputType(aConfig);
- if (!inputType) {
- return E_FAIL;
- }
+already_AddRefed<IMFMediaType> CreateInputType(EncoderConfig& aConfig);
- RefPtr<IMFMediaType> outputType = CreateOutputType(aConfig);
- if (!outputType) {
- return E_FAIL;
- }
+already_AddRefed<IMFMediaType> CreateOutputType(EncoderConfig& aConfig);
- return aEncoder->SetMediaTypes(inputType, outputType);
-}
+HRESULT SetMediaTypes(RefPtr<MFTEncoder>& aEncoder, EncoderConfig& aConfig);
} // namespace mozilla
+
+#endif // WMFDATAENCODERUTILS_H_
diff --git a/dom/media/platforms/wmf/WMFDecoderModule.cpp b/dom/media/platforms/wmf/WMFDecoderModule.cpp
index b3aae1e750..79556b061b 100644
--- a/dom/media/platforms/wmf/WMFDecoderModule.cpp
+++ b/dom/media/platforms/wmf/WMFDecoderModule.cpp
@@ -85,13 +85,9 @@ static bool IsRemoteAcceleratedCompositor(
ident.mParentProcessType == GeckoProcessType_GPU;
}
-static Atomic<bool> sSupportedTypesInitialized(false);
-static EnumSet<WMFStreamType> sSupportedTypes;
-static EnumSet<WMFStreamType> sLackOfExtensionTypes;
-
/* static */
void WMFDecoderModule::Init(Config aConfig) {
- MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
+ // TODO : add an assertion to prevent this from running on main thread.
if (XRE_IsContentProcess()) {
// If we're in the content process and the UseGPUDecoder pref is set, it
// means that we've given up on the GPU process (it's been crashing) so we
@@ -134,6 +130,7 @@ void WMFDecoderModule::Init(Config aConfig) {
sDXVAEnabled = sDXVAEnabled && hwVideo;
mozilla::mscom::EnsureMTA([&]() {
+ StaticMutexAutoLock lock(sMutex);
// Store the supported MFT decoders.
sSupportedTypes.clear();
sLackOfExtensionTypes.clear();
@@ -163,7 +160,10 @@ void WMFDecoderModule::Init(Config aConfig) {
}
});
- sSupportedTypesInitialized = true;
+ {
+ StaticMutexAutoLock lock(sMutex);
+ sSupportedTypesInitialized = true;
+ }
WmfDecoderModuleMarkerAndLog("WMFInit Result",
"WMFDecoderModule::Init finishing");
@@ -270,15 +270,13 @@ HRESULT WMFDecoderModule::CreateMFTDecoder(const WMFStreamType& aType,
/* static */
bool WMFDecoderModule::CanCreateMFTDecoder(const WMFStreamType& aType) {
MOZ_ASSERT(WMFStreamType::Unknown < aType && aType < WMFStreamType::SENTINEL);
- if (!sSupportedTypesInitialized) {
- if (NS_IsMainThread()) {
- Init();
- } else {
- nsCOMPtr<nsIRunnable> runnable =
- NS_NewRunnableFunction("WMFDecoderModule::Init", [&]() { Init(); });
- SyncRunnable::DispatchToThread(GetMainThreadSerialEventTarget(),
- runnable);
- }
+ bool hasInitialized = false;
+ {
+ StaticMutexAutoLock lock(sMutex);
+ hasInitialized = sSupportedTypesInitialized;
+ }
+ if (!hasInitialized) {
+ Init();
}
// Check prefs here rather than CreateMFTDecoder so that prefs aren't baked
@@ -324,7 +322,7 @@ bool WMFDecoderModule::CanCreateMFTDecoder(const WMFStreamType& aType) {
break;
}
}
-
+ StaticMutexAutoLock lock(sMutex);
return sSupportedTypes.contains(aType);
}
@@ -380,6 +378,7 @@ media::DecodeSupportSet WMFDecoderModule::Supports(
return media::DecodeSupport::SoftwareDecode;
}
}
+ StaticMutexAutoLock lock(sMutex);
return sLackOfExtensionTypes.contains(type)
? media::DecodeSupport::UnsureDueToLackOfExtension
: media::DecodeSupportSet{};
@@ -486,6 +485,9 @@ bool WMFDecoderModule::IsHEVCSupported() {
return sForceEnableHEVC || StaticPrefs::media_wmf_hevc_enabled() == 1;
}
+/* static */
+void WMFDecoderModule::DisableForceEnableHEVC() { sForceEnableHEVC = false; }
+
} // namespace mozilla
#undef WFM_DECODER_MODULE_STATUS_MARKER
diff --git a/dom/media/platforms/wmf/WMFDecoderModule.h b/dom/media/platforms/wmf/WMFDecoderModule.h
index 3b130fd657..6debdc5836 100644
--- a/dom/media/platforms/wmf/WMFDecoderModule.h
+++ b/dom/media/platforms/wmf/WMFDecoderModule.h
@@ -10,6 +10,7 @@
# include "PlatformDecoderModule.h"
# include "WMF.h"
# include "WMFUtils.h"
+# include "mozilla/Atomics.h"
namespace mozilla {
@@ -43,7 +44,9 @@ class WMFDecoderModule : public PlatformDecoderModule {
ForceEnableHEVC,
};
- // Called on main thread.
+ // Can be called on any thread, but avoid calling this on the main thread
+ // because the initialization takes long time and we don't want to block the
+ // main thread.
static void Init(Config aConfig = Config::None);
// Called from any thread, must call init first
@@ -53,16 +56,24 @@ class WMFDecoderModule : public PlatformDecoderModule {
RefPtr<MFTDecoder>& aDecoder);
static bool CanCreateMFTDecoder(const WMFStreamType& aType);
+ static void DisableForceEnableHEVC();
+
private:
// This is used for GPU process only, where we can't set the preference
// directly (it can only set in the parent process) So we need a way to force
// enable the HEVC in order to report the support information via telemetry.
- static inline bool sForceEnableHEVC = false;
+ static inline Atomic<bool> sForceEnableHEVC{false};
static bool IsHEVCSupported();
WMFDecoderModule() = default;
virtual ~WMFDecoderModule() = default;
+
+ static inline StaticMutex sMutex;
+ static inline bool sSupportedTypesInitialized MOZ_GUARDED_BY(sMutex) = false;
+ static inline EnumSet<WMFStreamType> sSupportedTypes MOZ_GUARDED_BY(sMutex);
+ static inline EnumSet<WMFStreamType> sLackOfExtensionTypes
+ MOZ_GUARDED_BY(sMutex);
};
} // namespace mozilla
diff --git a/dom/media/platforms/wmf/WMFEncoderModule.cpp b/dom/media/platforms/wmf/WMFEncoderModule.cpp
index 7b5af9bf50..9f44ce0c5e 100644
--- a/dom/media/platforms/wmf/WMFEncoderModule.cpp
+++ b/dom/media/platforms/wmf/WMFEncoderModule.cpp
@@ -26,6 +26,9 @@ bool WMFEncoderModule::Supports(const EncoderConfig& aConfig) const {
if (aConfig.IsAudio()) {
return false;
}
+ if (aConfig.mScalabilityMode != ScalabilityMode::None) {
+ return false;
+ }
return SupportsCodec(aConfig.mCodec);
}
diff --git a/dom/media/platforms/wmf/WMFMediaDataEncoder.cpp b/dom/media/platforms/wmf/WMFMediaDataEncoder.cpp
new file mode 100644
index 0000000000..fcacedbd05
--- /dev/null
+++ b/dom/media/platforms/wmf/WMFMediaDataEncoder.cpp
@@ -0,0 +1,399 @@
+/* -*- 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/. */
+
+#include "WMFMediaDataEncoder.h"
+
+#include "ImageContainer.h"
+#include "ImageConversion.h"
+#include "MFTEncoder.h"
+#include "PlatformEncoderModule.h"
+#include "TimeUnits.h"
+#include "WMFDataEncoderUtils.h"
+#include "WMFUtils.h"
+#include <comdef.h>
+#include "mozilla/WindowsProcessMitigations.h"
+
+namespace mozilla {
+
+using InitPromise = MediaDataEncoder::InitPromise;
+using EncodePromise = MediaDataEncoder::EncodePromise;
+using ReconfigurationPromise = MediaDataEncoder::ReconfigurationPromise;
+
+WMFMediaDataEncoder::WMFMediaDataEncoder(const EncoderConfig& aConfig,
+ const RefPtr<TaskQueue>& aTaskQueue)
+ : mConfig(aConfig),
+ mTaskQueue(aTaskQueue),
+ mHardwareNotAllowed(aConfig.mHardwarePreference ==
+ HardwarePreference::RequireSoftware ||
+ IsWin32kLockedDown()) {
+ WMF_ENC_LOGE("WMFMediaDataEncoder ctor: %s, (hw not allowed: %s)",
+ aConfig.ToString().get(), mHardwareNotAllowed ? "yes" : "no");
+ MOZ_ASSERT(mTaskQueue);
+}
+
+RefPtr<InitPromise> WMFMediaDataEncoder::Init() {
+ return InvokeAsync(mTaskQueue, this, __func__,
+ &WMFMediaDataEncoder::ProcessInit);
+}
+RefPtr<EncodePromise> WMFMediaDataEncoder::Encode(const MediaData* aSample) {
+ MOZ_ASSERT(aSample);
+
+ RefPtr<const VideoData> sample(aSample->As<const VideoData>());
+
+ return InvokeAsync<RefPtr<const VideoData>>(
+ mTaskQueue, this, __func__, &WMFMediaDataEncoder::ProcessEncode,
+ std::move(sample));
+}
+RefPtr<EncodePromise> WMFMediaDataEncoder::Drain() {
+ return InvokeAsync(mTaskQueue, __func__,
+ [self = RefPtr<WMFMediaDataEncoder>(this)]() {
+ nsTArray<RefPtr<IMFSample>> outputs;
+ return SUCCEEDED(self->mEncoder->Drain(outputs))
+ ? self->ProcessOutputSamples(outputs)
+ : EncodePromise::CreateAndReject(
+ NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
+ });
+}
+RefPtr<ShutdownPromise> WMFMediaDataEncoder::Shutdown() {
+ return InvokeAsync(mTaskQueue, __func__,
+ [self = RefPtr<WMFMediaDataEncoder>(this)]() {
+ if (self->mEncoder) {
+ self->mEncoder->Destroy();
+ self->mEncoder = nullptr;
+ }
+ return ShutdownPromise::CreateAndResolve(true, __func__);
+ });
+}
+RefPtr<GenericPromise> WMFMediaDataEncoder::SetBitrate(uint32_t aBitsPerSec) {
+ return InvokeAsync(
+ mTaskQueue, __func__,
+ [self = RefPtr<WMFMediaDataEncoder>(this), aBitsPerSec]() {
+ MOZ_ASSERT(self->mEncoder);
+ return SUCCEEDED(self->mEncoder->SetBitrate(aBitsPerSec))
+ ? GenericPromise::CreateAndResolve(true, __func__)
+ : GenericPromise::CreateAndReject(
+ NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR, __func__);
+ });
+}
+
+RefPtr<ReconfigurationPromise> WMFMediaDataEncoder::Reconfigure(
+ const RefPtr<const EncoderConfigurationChangeList>& aConfigurationChanges) {
+ // General reconfiguration interface not implemented right now
+ return MediaDataEncoder::ReconfigurationPromise::CreateAndReject(
+ NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
+};
+
+nsCString WMFMediaDataEncoder::GetDescriptionName() const {
+ return MFTEncoder::GetFriendlyName(CodecToSubtype(mConfig.mCodec));
+}
+
+RefPtr<InitPromise> WMFMediaDataEncoder::ProcessInit() {
+ AssertOnTaskQueue();
+
+ MOZ_ASSERT(!mEncoder,
+ "Should not initialize encoder again without shutting down");
+
+ if (!wmf::MediaFoundationInitializer::HasInitialized()) {
+ return InitPromise::CreateAndReject(
+ MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+ RESULT_DETAIL("Can't create the MFT encoder.")),
+ __func__);
+ }
+
+ RefPtr<MFTEncoder> encoder = new MFTEncoder(mHardwareNotAllowed);
+ HRESULT hr;
+ mscom::EnsureMTA([&]() { hr = InitMFTEncoder(encoder); });
+
+ if (FAILED(hr)) {
+ _com_error error(hr);
+ WMF_ENC_LOGE("init MFTEncoder: error = 0x%lX, %ls", hr,
+ error.ErrorMessage());
+ return InitPromise::CreateAndReject(
+ MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+ RESULT_DETAIL("Can't create the MFT encoder.")),
+ __func__);
+ }
+
+ mEncoder = std::move(encoder);
+ FillConfigData();
+ return InitPromise::CreateAndResolve(TrackInfo::TrackType::kVideoTrack,
+ __func__);
+}
+
+HRESULT WMFMediaDataEncoder::InitMFTEncoder(RefPtr<MFTEncoder>& aEncoder) {
+ HRESULT hr = aEncoder->Create(CodecToSubtype(mConfig.mCodec));
+ if (FAILED(hr)) {
+ _com_error error(hr);
+ WMF_ENC_LOGE("MFTEncoder::Create: error = 0x%lX, %ls", hr,
+ error.ErrorMessage());
+ return hr;
+ }
+
+ hr = SetMediaTypes(aEncoder, mConfig);
+ if (FAILED(hr)) {
+ _com_error error(hr);
+ WMF_ENC_LOGE("MFTEncoder::SetMediaType: error = 0x%lX, %ls", hr,
+ error.ErrorMessage());
+ return hr;
+ }
+
+ hr = aEncoder->SetModes(mConfig.mBitrate);
+ if (FAILED(hr)) {
+ _com_error error(hr);
+ WMF_ENC_LOGE("MFTEncoder::SetMode: error = 0x%lX, %ls", hr,
+ error.ErrorMessage());
+ return hr;
+ }
+
+ return S_OK;
+}
+
+void WMFMediaDataEncoder::FillConfigData() {
+ nsTArray<UINT8> header;
+ NS_ENSURE_TRUE_VOID(SUCCEEDED(mEncoder->GetMPEGSequenceHeader(header)));
+
+ mConfigData =
+ header.Length() > 0
+ ? ParseH264Parameters(header, mConfig.mUsage == Usage::Realtime)
+ : nullptr;
+}
+
+RefPtr<EncodePromise> WMFMediaDataEncoder::ProcessEncode(
+ RefPtr<const VideoData>&& aSample) {
+ AssertOnTaskQueue();
+ MOZ_ASSERT(mEncoder);
+ MOZ_ASSERT(aSample);
+
+ RefPtr<IMFSample> nv12 = ConvertToNV12InputSample(std::move(aSample));
+ if (!nv12 || FAILED(mEncoder->PushInput(std::move(nv12)))) {
+ WMF_ENC_LOGE("failed to process input sample");
+ return EncodePromise::CreateAndReject(
+ MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+ RESULT_DETAIL("Failed to process input.")),
+ __func__);
+ }
+
+ nsTArray<RefPtr<IMFSample>> outputs;
+ HRESULT hr = mEncoder->TakeOutput(outputs);
+ if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
+ FillConfigData();
+ } else if (FAILED(hr)) {
+ WMF_ENC_LOGE("failed to process output");
+ return EncodePromise::CreateAndReject(
+ MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+ RESULT_DETAIL("Failed to process output.")),
+ __func__);
+ }
+
+ return ProcessOutputSamples(outputs);
+}
+
+already_AddRefed<IMFSample> WMFMediaDataEncoder::ConvertToNV12InputSample(
+ RefPtr<const VideoData>&& aData) {
+ AssertOnTaskQueue();
+ MOZ_ASSERT(mEncoder);
+
+ struct NV12Info {
+ int32_t mYStride = 0;
+ int32_t mUVStride = 0;
+ size_t mYLength = 0;
+ size_t mBufferLength = 0;
+ } info;
+
+ if (const layers::PlanarYCbCrImage* image =
+ aData->mImage->AsPlanarYCbCrImage()) {
+ // Assume this is I420. If it's not, the whole process fails in
+ // ConvertToNV12 below.
+ const layers::PlanarYCbCrData* yuv = image->GetData();
+ info.mYStride = yuv->mYStride;
+ info.mUVStride = yuv->mCbCrStride * 2;
+ info.mYLength = info.mYStride * yuv->YDataSize().height;
+ info.mBufferLength =
+ info.mYLength + (info.mUVStride * yuv->CbCrDataSize().height);
+ } else {
+ info.mYStride = aData->mImage->GetSize().width;
+ info.mUVStride = info.mYStride;
+
+ const int32_t yHeight = aData->mImage->GetSize().height;
+ const int32_t uvHeight = yHeight / 2;
+
+ CheckedInt<size_t> yLength(info.mYStride);
+ yLength *= yHeight;
+ if (!yLength.isValid()) {
+ WMF_ENC_LOGE("yLength overflows");
+ return nullptr;
+ }
+ info.mYLength = yLength.value();
+
+ CheckedInt<size_t> uvLength(info.mUVStride);
+ uvLength *= uvHeight;
+ if (!uvLength.isValid()) {
+ WMF_ENC_LOGE("uvLength overflows");
+ return nullptr;
+ }
+
+ CheckedInt<size_t> length(yLength);
+ length += uvLength;
+ if (!length.isValid()) {
+ WMF_ENC_LOGE("length overflows");
+ return nullptr;
+ }
+ info.mBufferLength = length.value();
+ }
+
+ RefPtr<IMFSample> input;
+ HRESULT hr = mEncoder->CreateInputSample(&input, info.mBufferLength);
+ if (FAILED(hr)) {
+ _com_error error(hr);
+ WMF_ENC_LOGE("CreateInputSample: error = 0x%lX, %ls", hr,
+ error.ErrorMessage());
+ return nullptr;
+ }
+
+ RefPtr<IMFMediaBuffer> buffer;
+ hr = input->GetBufferByIndex(0, getter_AddRefs(buffer));
+ if (FAILED(hr)) {
+ _com_error error(hr);
+ WMF_ENC_LOGE("GetBufferByIndex: error = 0x%lX, %ls", hr,
+ error.ErrorMessage());
+ return nullptr;
+ }
+
+ hr = buffer->SetCurrentLength(info.mBufferLength);
+ if (FAILED(hr)) {
+ _com_error error(hr);
+ WMF_ENC_LOGE("SetCurrentLength: error = 0x%lX, %ls", hr,
+ error.ErrorMessage());
+ return nullptr;
+ }
+
+ LockBuffer lockBuffer(buffer);
+ hr = lockBuffer.Result();
+ if (FAILED(hr)) {
+ _com_error error(hr);
+ WMF_ENC_LOGE("LockBuffer: error = 0x%lX, %ls", hr, error.ErrorMessage());
+ return nullptr;
+ }
+
+ nsresult rv =
+ ConvertToNV12(aData->mImage, lockBuffer.Data(), info.mYStride,
+ lockBuffer.Data() + info.mYLength, info.mUVStride);
+ if (NS_FAILED(rv)) {
+ WMF_ENC_LOGE("Failed to convert to NV12");
+ return nullptr;
+ }
+
+ hr = input->SetSampleTime(UsecsToHNs(aData->mTime.ToMicroseconds()));
+ if (FAILED(hr)) {
+ _com_error error(hr);
+ WMF_ENC_LOGE("SetSampleTime: error = 0x%lX, %ls", hr, error.ErrorMessage());
+ return nullptr;
+ }
+
+ hr = input->SetSampleDuration(UsecsToHNs(aData->mDuration.ToMicroseconds()));
+ if (FAILED(hr)) {
+ _com_error error(hr);
+ WMF_ENC_LOGE("SetSampleDuration: error = 0x%lX, %ls", hr,
+ error.ErrorMessage());
+ return nullptr;
+ }
+
+ return input.forget();
+}
+
+RefPtr<EncodePromise> WMFMediaDataEncoder::ProcessOutputSamples(
+ nsTArray<RefPtr<IMFSample>>& aSamples) {
+ EncodedData frames;
+ for (auto sample : aSamples) {
+ RefPtr<MediaRawData> frame = IMFSampleToMediaData(sample);
+ if (frame) {
+ frames.AppendElement(std::move(frame));
+ } else {
+ WMF_ENC_LOGE("failed to convert output frame");
+ }
+ }
+ aSamples.Clear();
+ return EncodePromise::CreateAndResolve(std::move(frames), __func__);
+}
+
+already_AddRefed<MediaRawData> WMFMediaDataEncoder::IMFSampleToMediaData(
+ RefPtr<IMFSample>& aSample) {
+ AssertOnTaskQueue();
+ MOZ_ASSERT(aSample);
+
+ RefPtr<IMFMediaBuffer> buffer;
+ HRESULT hr = aSample->GetBufferByIndex(0, getter_AddRefs(buffer));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ LockBuffer lockBuffer(buffer);
+ NS_ENSURE_TRUE(SUCCEEDED(lockBuffer.Result()), nullptr);
+
+ LONGLONG time = 0;
+ hr = aSample->GetSampleTime(&time);
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ LONGLONG duration = 0;
+ hr = aSample->GetSampleDuration(&duration);
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ bool isKeyframe =
+ MFGetAttributeUINT32(aSample, MFSampleExtension_CleanPoint, false);
+
+ auto frame = MakeRefPtr<MediaRawData>();
+ if (!WriteFrameData(frame, lockBuffer, isKeyframe)) {
+ return nullptr;
+ }
+
+ frame->mTime = media::TimeUnit::FromMicroseconds(HNsToUsecs(time));
+ frame->mDuration = media::TimeUnit::FromMicroseconds(HNsToUsecs(duration));
+ frame->mKeyframe = isKeyframe;
+
+ return frame.forget();
+}
+
+bool WMFMediaDataEncoder::WriteFrameData(RefPtr<MediaRawData>& aDest,
+ LockBuffer& aSrc, bool aIsKeyframe) {
+ if (mConfig.mCodec == CodecType::H264) {
+ size_t prependLength = 0;
+ RefPtr<MediaByteBuffer> avccHeader;
+ if (aIsKeyframe && mConfigData) {
+ if (mConfig.mUsage == Usage::Realtime) {
+ prependLength = mConfigData->Length();
+ } else {
+ avccHeader = mConfigData;
+ }
+ }
+
+ UniquePtr<MediaRawDataWriter> writer(aDest->CreateWriter());
+ if (!writer->SetSize(prependLength + aSrc.Length())) {
+ WMF_ENC_LOGE("fail to allocate output buffer");
+ return false;
+ }
+
+ if (prependLength > 0) {
+ PodCopy(writer->Data(), mConfigData->Elements(), prependLength);
+ }
+ PodCopy(writer->Data() + prependLength, aSrc.Data(), aSrc.Length());
+
+ if (mConfig.mUsage != Usage::Realtime &&
+ !AnnexB::ConvertSampleToAVCC(aDest, avccHeader)) {
+ WMF_ENC_LOGE("fail to convert annex-b sample to AVCC");
+ return false;
+ }
+
+ return true;
+ }
+ UniquePtr<MediaRawDataWriter> writer(aDest->CreateWriter());
+ if (!writer->SetSize(aSrc.Length())) {
+ WMF_ENC_LOGE("fail to allocate output buffer");
+ return false;
+ }
+
+ PodCopy(writer->Data(), aSrc.Data(), aSrc.Length());
+ return true;
+}
+
+} // namespace mozilla
diff --git a/dom/media/platforms/wmf/WMFMediaDataEncoder.h b/dom/media/platforms/wmf/WMFMediaDataEncoder.h
index 31a63c8347..db1077e699 100644
--- a/dom/media/platforms/wmf/WMFMediaDataEncoder.h
+++ b/dom/media/platforms/wmf/WMFMediaDataEncoder.h
@@ -7,84 +7,30 @@
#ifndef WMFMediaDataEncoder_h_
#define WMFMediaDataEncoder_h_
-#include "ImageContainer.h"
#include "MFTEncoder.h"
#include "PlatformEncoderModule.h"
-#include "TimeUnits.h"
#include "WMFDataEncoderUtils.h"
#include "WMFUtils.h"
+#include <comdef.h>
+#include "mozilla/WindowsProcessMitigations.h"
namespace mozilla {
class WMFMediaDataEncoder final : public MediaDataEncoder {
public:
WMFMediaDataEncoder(const EncoderConfig& aConfig,
- const RefPtr<TaskQueue>& aTaskQueue)
- : mConfig(aConfig),
- mTaskQueue(aTaskQueue),
- mHardwareNotAllowed(aConfig.mHardwarePreference ==
- HardwarePreference::RequireSoftware ||
- aConfig.mHardwarePreference ==
- HardwarePreference::None) {
- MOZ_ASSERT(mTaskQueue);
- }
+ const RefPtr<TaskQueue>& aTaskQueue);
- RefPtr<InitPromise> Init() override {
- return InvokeAsync(mTaskQueue, this, __func__,
- &WMFMediaDataEncoder::ProcessInit);
- }
- RefPtr<EncodePromise> Encode(const MediaData* aSample) override {
- MOZ_ASSERT(aSample);
-
- RefPtr<const VideoData> sample(aSample->As<const VideoData>());
-
- return InvokeAsync<RefPtr<const VideoData>>(
- mTaskQueue, this, __func__, &WMFMediaDataEncoder::ProcessEncode,
- std::move(sample));
- }
- RefPtr<EncodePromise> Drain() override {
- return InvokeAsync(
- mTaskQueue, __func__, [self = RefPtr<WMFMediaDataEncoder>(this)]() {
- nsTArray<RefPtr<IMFSample>> outputs;
- return SUCCEEDED(self->mEncoder->Drain(outputs))
- ? self->ProcessOutputSamples(outputs)
- : EncodePromise::CreateAndReject(
- NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
- });
- }
- RefPtr<ShutdownPromise> Shutdown() override {
- return InvokeAsync(
- mTaskQueue, __func__, [self = RefPtr<WMFMediaDataEncoder>(this)]() {
- if (self->mEncoder) {
- self->mEncoder->Destroy();
- self->mEncoder = nullptr;
- }
- return ShutdownPromise::CreateAndResolve(true, __func__);
- });
- }
- RefPtr<GenericPromise> SetBitrate(uint32_t aBitsPerSec) override {
- return InvokeAsync(
- mTaskQueue, __func__,
- [self = RefPtr<WMFMediaDataEncoder>(this), aBitsPerSec]() {
- MOZ_ASSERT(self->mEncoder);
- return SUCCEEDED(self->mEncoder->SetBitrate(aBitsPerSec))
- ? GenericPromise::CreateAndResolve(true, __func__)
- : GenericPromise::CreateAndReject(
- NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR, __func__);
- });
- }
+ RefPtr<InitPromise> Init() override;
+ RefPtr<EncodePromise> Encode(const MediaData* aSample) override;
+ RefPtr<EncodePromise> Drain() override;
+ RefPtr<ShutdownPromise> Shutdown() override;
+ RefPtr<GenericPromise> SetBitrate(uint32_t aBitsPerSec) override;
RefPtr<ReconfigurationPromise> Reconfigure(
const RefPtr<const EncoderConfigurationChangeList>& aConfigurationChanges)
- override {
- // General reconfiguration interface not implemented right now
- return MediaDataEncoder::ReconfigurationPromise::CreateAndReject(
- NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
- };
-
- nsCString GetDescriptionName() const override {
- return MFTEncoder::GetFriendlyName(CodecToSubtype(mConfig.mCodec));
- }
+ override;
+ nsCString GetDescriptionName() const override;
private:
// Automatically lock/unlock IMFMediaBuffer.
@@ -107,233 +53,29 @@ class WMFMediaDataEncoder final : public MediaDataEncoder {
private:
RefPtr<IMFMediaBuffer> mBuffer;
- BYTE* mBytes;
- DWORD mCapacity;
- DWORD mLength;
- HRESULT mResult;
+ BYTE* mBytes{};
+ DWORD mCapacity{};
+ DWORD mLength{};
+ HRESULT mResult{};
};
- RefPtr<InitPromise> ProcessInit() {
- AssertOnTaskQueue();
-
- MOZ_ASSERT(!mEncoder,
- "Should not initialize encoder again without shutting down");
-
- if (!wmf::MediaFoundationInitializer::HasInitialized()) {
- return InitPromise::CreateAndReject(
- MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
- RESULT_DETAIL("Can't create the MFT encoder.")),
- __func__);
- }
-
- RefPtr<MFTEncoder> encoder = new MFTEncoder(mHardwareNotAllowed);
- HRESULT hr;
- mscom::EnsureMTA([&]() { hr = InitMFTEncoder(encoder); });
-
- if (FAILED(hr)) {
- WMF_ENC_LOGE("init MFTEncoder: error = 0x%lX", hr);
- return InitPromise::CreateAndReject(
- MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
- RESULT_DETAIL("Can't create the MFT encoder.")),
- __func__);
- }
-
- mEncoder = std::move(encoder);
- FillConfigData();
- return InitPromise::CreateAndResolve(TrackInfo::TrackType::kVideoTrack,
- __func__);
- }
-
- HRESULT InitMFTEncoder(RefPtr<MFTEncoder>& aEncoder) {
- HRESULT hr = aEncoder->Create(CodecToSubtype(mConfig.mCodec));
- NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
-
- hr = SetMediaTypes(aEncoder, mConfig);
- NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
-
- hr = aEncoder->SetModes(mConfig.mBitrate);
- NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+ RefPtr<InitPromise> ProcessInit();
- return S_OK;
- }
+ HRESULT InitMFTEncoder(RefPtr<MFTEncoder>& aEncoder);
+ void FillConfigData();
- void FillConfigData() {
- nsTArray<UINT8> header;
- NS_ENSURE_TRUE_VOID(SUCCEEDED(mEncoder->GetMPEGSequenceHeader(header)));
-
- mConfigData =
- header.Length() > 0
- ? ParseH264Parameters(header, mConfig.mUsage == Usage::Realtime)
- : nullptr;
- }
-
- RefPtr<EncodePromise> ProcessEncode(RefPtr<const VideoData>&& aSample) {
- AssertOnTaskQueue();
- MOZ_ASSERT(mEncoder);
- MOZ_ASSERT(aSample);
-
- RefPtr<IMFSample> nv12 = ConvertToNV12InputSample(std::move(aSample));
- if (!nv12 || FAILED(mEncoder->PushInput(std::move(nv12)))) {
- WMF_ENC_LOGE("failed to process input sample");
- return EncodePromise::CreateAndReject(
- MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
- RESULT_DETAIL("Failed to process input.")),
- __func__);
- }
-
- nsTArray<RefPtr<IMFSample>> outputs;
- HRESULT hr = mEncoder->TakeOutput(outputs);
- if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
- FillConfigData();
- } else if (FAILED(hr)) {
- WMF_ENC_LOGE("failed to process output");
- return EncodePromise::CreateAndReject(
- MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
- RESULT_DETAIL("Failed to process output.")),
- __func__);
- }
-
- return ProcessOutputSamples(outputs);
- }
+ RefPtr<EncodePromise> ProcessEncode(RefPtr<const VideoData>&& aSample);
already_AddRefed<IMFSample> ConvertToNV12InputSample(
- RefPtr<const VideoData>&& aData) {
- AssertOnTaskQueue();
- MOZ_ASSERT(mEncoder);
-
- const layers::PlanarYCbCrImage* image = aData->mImage->AsPlanarYCbCrImage();
- // TODO: Take care non planar Y-Cb-Cr image (Bug 1881647).
- NS_ENSURE_TRUE(image, nullptr);
-
- const layers::PlanarYCbCrData* yuv = image->GetData();
- auto ySize = yuv->YDataSize();
- auto cbcrSize = yuv->CbCrDataSize();
- size_t yLength = yuv->mYStride * ySize.height;
- size_t length = yLength + (yuv->mCbCrStride * cbcrSize.height * 2);
-
- RefPtr<IMFSample> input;
- HRESULT hr = mEncoder->CreateInputSample(&input, length);
- NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
-
- RefPtr<IMFMediaBuffer> buffer;
- hr = input->GetBufferByIndex(0, getter_AddRefs(buffer));
- NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
-
- hr = buffer->SetCurrentLength(length);
- NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
-
- LockBuffer lockBuffer(buffer);
- NS_ENSURE_TRUE(SUCCEEDED(lockBuffer.Result()), nullptr);
-
- // TODO: Take care non I420 image (Bug 1881647).
- bool ok = libyuv::I420ToNV12(
- yuv->mYChannel, yuv->mYStride, yuv->mCbChannel,
- yuv->mCbCrStride, yuv->mCrChannel, yuv->mCbCrStride,
- lockBuffer.Data(), yuv->mYStride, lockBuffer.Data() + yLength,
- yuv->mCbCrStride * 2, ySize.width, ySize.height) == 0;
- NS_ENSURE_TRUE(ok, nullptr);
-
- hr = input->SetSampleTime(UsecsToHNs(aData->mTime.ToMicroseconds()));
- NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
-
- hr =
- input->SetSampleDuration(UsecsToHNs(aData->mDuration.ToMicroseconds()));
- NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
-
- return input.forget();
- }
+ RefPtr<const VideoData>&& aData);
RefPtr<EncodePromise> ProcessOutputSamples(
- nsTArray<RefPtr<IMFSample>>& aSamples) {
- EncodedData frames;
- for (auto sample : aSamples) {
- RefPtr<MediaRawData> frame = IMFSampleToMediaData(sample);
- if (frame) {
- frames.AppendElement(std::move(frame));
- } else {
- WMF_ENC_LOGE("failed to convert output frame");
- }
- }
- aSamples.Clear();
- return EncodePromise::CreateAndResolve(std::move(frames), __func__);
- }
-
+ nsTArray<RefPtr<IMFSample>>& aSamples);
already_AddRefed<MediaRawData> IMFSampleToMediaData(
- RefPtr<IMFSample>& aSample) {
- AssertOnTaskQueue();
- MOZ_ASSERT(aSample);
-
- RefPtr<IMFMediaBuffer> buffer;
- HRESULT hr = aSample->GetBufferByIndex(0, getter_AddRefs(buffer));
- NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
-
- LockBuffer lockBuffer(buffer);
- NS_ENSURE_TRUE(SUCCEEDED(lockBuffer.Result()), nullptr);
-
- LONGLONG time = 0;
- hr = aSample->GetSampleTime(&time);
- NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
-
- LONGLONG duration = 0;
- hr = aSample->GetSampleDuration(&duration);
- NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
-
- bool isKeyframe =
- MFGetAttributeUINT32(aSample, MFSampleExtension_CleanPoint, false);
-
- auto frame = MakeRefPtr<MediaRawData>();
- if (!WriteFrameData(frame, lockBuffer, isKeyframe)) {
- return nullptr;
- }
-
- frame->mTime = media::TimeUnit::FromMicroseconds(HNsToUsecs(time));
- frame->mDuration = media::TimeUnit::FromMicroseconds(HNsToUsecs(duration));
- frame->mKeyframe = isKeyframe;
-
- return frame.forget();
- }
+ RefPtr<IMFSample>& aSample);
bool WriteFrameData(RefPtr<MediaRawData>& aDest, LockBuffer& aSrc,
- bool aIsKeyframe) {
- if (mConfig.mCodec == CodecType::H264) {
- size_t prependLength = 0;
- RefPtr<MediaByteBuffer> avccHeader;
- if (aIsKeyframe && mConfigData) {
- if (mConfig.mUsage == Usage::Realtime) {
- prependLength = mConfigData->Length();
- } else {
- avccHeader = mConfigData;
- }
- }
-
- UniquePtr<MediaRawDataWriter> writer(aDest->CreateWriter());
- if (!writer->SetSize(prependLength + aSrc.Length())) {
- WMF_ENC_LOGE("fail to allocate output buffer");
- return false;
- }
-
- if (prependLength > 0) {
- PodCopy(writer->Data(), mConfigData->Elements(), prependLength);
- }
- PodCopy(writer->Data() + prependLength, aSrc.Data(), aSrc.Length());
-
- if (mConfig.mUsage != Usage::Realtime &&
- !AnnexB::ConvertSampleToAVCC(aDest, avccHeader)) {
- WMF_ENC_LOGE("fail to convert annex-b sample to AVCC");
- return false;
- }
-
- return true;
- }
- UniquePtr<MediaRawDataWriter> writer(aDest->CreateWriter());
- if (!writer->SetSize(aSrc.Length())) {
- WMF_ENC_LOGE("fail to allocate output buffer");
- return false;
- }
-
- PodCopy(writer->Data(), aSrc.Data(), aSrc.Length());
- return true;
- }
+ bool aIsKeyframe);
void AssertOnTaskQueue() { MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); }
diff --git a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
index 65480c4a01..2344de94d9 100644
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -711,6 +711,8 @@ WMFVideoMFTManager::CreateBasicVideoFrame(IMFSample* aSample,
aStage.SetImageFormat(DecodeStage::P016);
}
aStage.SetResolution(videoWidth, videoHeight);
+ aStage.SetStartTimeAndEndTime(v->mTime.ToMicroseconds(),
+ v->GetEndTime().ToMicroseconds());
});
v.forget(aOutVideoData);
@@ -753,7 +755,6 @@ WMFVideoMFTManager::CreateD3DVideoFrame(IMFSample* aSample,
TimeUnit::FromMicroseconds(-1));
NS_ENSURE_TRUE(v, E_FAIL);
- v.forget(aOutVideoData);
mPerformanceRecorder.Record(pts.ToMicroseconds(), [&](DecodeStage& aStage) {
aStage.SetColorDepth(mVideoInfo.mColorDepth);
@@ -771,8 +772,11 @@ WMFVideoMFTManager::CreateD3DVideoFrame(IMFSample* aSample,
aStage.SetImageFormat(DecodeStage::P016);
}
aStage.SetResolution(size.width, size.height);
+ aStage.SetStartTimeAndEndTime(v->mTime.ToMicroseconds(),
+ v->GetEndTime().ToMicroseconds());
});
+ v.forget(aOutVideoData);
return S_OK;
}
diff --git a/dom/media/platforms/wmf/moz.build b/dom/media/platforms/wmf/moz.build
index 9e0f3aa94a..13f754af29 100644
--- a/dom/media/platforms/wmf/moz.build
+++ b/dom/media/platforms/wmf/moz.build
@@ -57,9 +57,11 @@ UNIFIED_SOURCES += [
"MFTDecoder.cpp",
"MFTEncoder.cpp",
"WMFAudioMFTManager.cpp",
+ "WMFDataEncoderUtils.cpp",
"WMFDecoderModule.cpp",
"WMFEncoderModule.cpp",
"WMFMediaDataDecoder.cpp",
+ "WMFMediaDataEncoder.cpp",
"WMFVideoMFTManager.cpp",
]