diff options
Diffstat (limited to 'dom/media/platforms/wmf')
-rw-r--r-- | dom/media/platforms/wmf/MFTEncoder.cpp | 22 | ||||
-rw-r--r-- | dom/media/platforms/wmf/WMF.h | 43 | ||||
-rw-r--r-- | dom/media/platforms/wmf/WMFDataEncoderUtils.cpp | 221 | ||||
-rw-r--r-- | dom/media/platforms/wmf/WMFDataEncoderUtils.h | 140 | ||||
-rw-r--r-- | dom/media/platforms/wmf/WMFDecoderModule.cpp | 34 | ||||
-rw-r--r-- | dom/media/platforms/wmf/WMFDecoderModule.h | 15 | ||||
-rw-r--r-- | dom/media/platforms/wmf/WMFEncoderModule.cpp | 3 | ||||
-rw-r--r-- | dom/media/platforms/wmf/WMFMediaDataEncoder.cpp | 399 | ||||
-rw-r--r-- | dom/media/platforms/wmf/WMFMediaDataEncoder.h | 302 | ||||
-rw-r--r-- | dom/media/platforms/wmf/WMFVideoMFTManager.cpp | 6 | ||||
-rw-r--r-- | dom/media/platforms/wmf/moz.build | 2 |
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", ] |