☠☠ backed out by 6e2551ab206b ☠ ☠ | |
author | Jean-Yves Avenard <jyavenard@mozilla.com> |
Wed, 12 Aug 2020 04:08:43 +0000 | |
changeset 544361 | 8d70f3cb5e3b28e7e55e3ce23f66a99104034148 |
parent 544360 | 5c3c1ec039d2579557cf4f15789c0d7d356a0e78 |
child 544362 | e97937bf5e3a386e08d67112e5cbc2bd56777137 |
push id | 123990 |
push user | jyavenard@mozilla.com |
push date | Wed, 12 Aug 2020 05:42:22 +0000 |
treeherder | autoland@a0f450666c5b [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jolin |
bugs | 1657521 |
milestone | 81.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/media/platforms/apple/AppleDecoderModule.cpp +++ b/dom/media/platforms/apple/AppleDecoderModule.cpp @@ -1,64 +1,166 @@ /* -*- 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 "AppleDecoderModule.h" + +#include <dlfcn.h> + #include "AppleATDecoder.h" -#include "AppleDecoderModule.h" #include "AppleVTDecoder.h" +#include "MP4Decoder.h" +#include "VPXDecoder.h" #include "mozilla/DebugOnly.h" #include "mozilla/Logging.h" #include "mozilla/gfx/gfxVars.h" +extern "C" { +// Only exists from MacOS 11 +extern void VTRegisterSupplementalVideoDecoderIfAvailable( + CMVideoCodecType codecType) __attribute__((weak_import)); +extern Boolean VTIsHardwareDecodeSupported(CMVideoCodecType codecType) + __attribute__((weak_import)); +} + namespace mozilla { bool AppleDecoderModule::sInitialized = false; bool AppleDecoderModule::sCanUseHardwareVideoDecoder = true; +bool AppleDecoderModule::sCanUseVP9Decoder = false; AppleDecoderModule::AppleDecoderModule() {} AppleDecoderModule::~AppleDecoderModule() {} /* static */ void AppleDecoderModule::Init() { if (sInitialized) { return; } sCanUseHardwareVideoDecoder = gfx::gfxVars::CanUseHardwareVideoDecoding(); sInitialized = true; + if (RegisterSupplementalVP9Decoder()) { + sCanUseVP9Decoder = CanCreateVP9Decoder(); + } } nsresult AppleDecoderModule::Startup() { if (!sInitialized) { return NS_ERROR_FAILURE; } return NS_OK; } already_AddRefed<MediaDataDecoder> AppleDecoderModule::CreateVideoDecoder( const CreateDecoderParams& aParams) { - RefPtr<MediaDataDecoder> decoder = new AppleVTDecoder( - aParams.VideoConfig(), aParams.mTaskQueue, aParams.mImageContainer, - aParams.mOptions, aParams.mKnowsCompositor); + RefPtr<MediaDataDecoder> decoder; + if (IsVideoSupported(aParams.VideoConfig(), aParams.mOptions)) { + decoder = new AppleVTDecoder(aParams.VideoConfig(), aParams.mTaskQueue, + aParams.mImageContainer, aParams.mOptions, + aParams.mKnowsCompositor); + } return decoder.forget(); } already_AddRefed<MediaDataDecoder> AppleDecoderModule::CreateAudioDecoder( const CreateDecoderParams& aParams) { RefPtr<MediaDataDecoder> decoder = new AppleATDecoder(aParams.AudioConfig(), aParams.mTaskQueue); return decoder.forget(); } bool AppleDecoderModule::SupportsMimeType( const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const { return aMimeType.EqualsLiteral("audio/mpeg") || aMimeType.EqualsLiteral("audio/mp4a-latm") || - aMimeType.EqualsLiteral("video/mp4") || - aMimeType.EqualsLiteral("video/avc"); + MP4Decoder::IsH264(aMimeType) || VPXDecoder::IsVP9(aMimeType); +} + +bool AppleDecoderModule::Supports( + const TrackInfo& aTrackInfo, DecoderDoctorDiagnostics* aDiagnostics) const { + if (aTrackInfo.IsAudio()) { + return SupportsMimeType(aTrackInfo.mMimeType, aDiagnostics); + } + return aTrackInfo.GetAsVideoInfo() && + IsVideoSupported(*aTrackInfo.GetAsVideoInfo()); +} + +bool AppleDecoderModule::IsVideoSupported( + const VideoInfo& aConfig, + const CreateDecoderParams::OptionSet& aOptions) const { + if (MP4Decoder::IsH264(aConfig.mMimeType)) { + return true; + } + if (!VPXDecoder::IsVP9(aConfig.mMimeType) || !sCanUseVP9Decoder || + aOptions.contains( + CreateDecoderParams::Option::HardwareDecoderNotAllowed)) { + return false; + } + if (aConfig.HasAlpha()) { + return false; + } + + // HW VP9 decoder only supports 8 or 10 bit color. + if (aConfig.mColorDepth != gfx::ColorDepth::COLOR_8 && + aConfig.mColorDepth != gfx::ColorDepth::COLOR_10) { + return false; + } + + // See if we have a vpcC box, and check further constraints. + // HW VP9 Decoder supports Profile 0 & 2 (YUV420) + if (aConfig.mExtraData && aConfig.mExtraData->Length() < 5) { + return true; // Assume it's okay. + } + int profile = aConfig.mExtraData->ElementAt(4); + + if (profile != 0 && profile != 2) { + return false; + } + + return true; +} + +/* static */ +bool AppleDecoderModule::CanCreateVP9Decoder() { + // We must wrap the code within __builtin_available to avoid compilation + // warning as VTIsHardwareDecodeSupported is only available from macOS 10.13. + if (__builtin_available(macOS 10.13, *)) { + // Only use VP9 decoder if it's hardware accelerated. + if (!sCanUseHardwareVideoDecoder || !VTIsHardwareDecodeSupported || + !VTIsHardwareDecodeSupported(kCMVideoCodecType_VP9)) { + return false; + } + + VideoInfo info(1920, 1080); + VPXDecoder::GetVPCCBox(info.mExtraData, VPXDecoder::VPXStreamInfo()); + + RefPtr<AppleVTDecoder> decoder = + new AppleVTDecoder(info, nullptr, nullptr, {}, nullptr); + nsAutoCString reason; + MediaResult rv = decoder->InitializeSession(); + bool isHardwareAccelerated = decoder->IsHardwareAccelerated(reason); + decoder->Shutdown(); + + return NS_SUCCEEDED(rv) && isHardwareAccelerated; + } + + return false; +} + +/* static */ +bool AppleDecoderModule::RegisterSupplementalVP9Decoder() { + static bool sRegisterIfAvailable = []() { + if (VTRegisterSupplementalVideoDecoderIfAvailable) { + VTRegisterSupplementalVideoDecoderIfAvailable(kCMVideoCodecType_VP9); + return true; + } + return false; + }(); + return sRegisterIfAvailable; } } // namespace mozilla
--- a/dom/media/platforms/apple/AppleDecoderModule.h +++ b/dom/media/platforms/apple/AppleDecoderModule.h @@ -24,19 +24,32 @@ class AppleDecoderModule : public Platfo // Decode thread. already_AddRefed<MediaDataDecoder> CreateAudioDecoder( const CreateDecoderParams& aParams) override; bool SupportsMimeType(const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const override; + bool Supports(const TrackInfo& aTrackInfo, + DecoderDoctorDiagnostics* aDiagnostics) const override; + static void Init(); static bool sCanUseHardwareVideoDecoder; + static bool sCanUseVP9Decoder; + + static constexpr int kCMVideoCodecType_VP9{'vp09'}; private: static bool sInitialized; + bool IsVideoSupported(const VideoInfo& aConfig, + const CreateDecoderParams::OptionSet& aOptions = + CreateDecoderParams::OptionSet()) const; + // Enable VP9 HW decoder. + static bool RegisterSupplementalVP9Decoder(); + // Return true if a dummy hardware VP9 decoder could be created. + static bool CanCreateVP9Decoder(); }; } // namespace mozilla #endif // mozilla_AppleDecoderModule_h
--- a/dom/media/platforms/apple/AppleVTDecoder.cpp +++ b/dom/media/platforms/apple/AppleVTDecoder.cpp @@ -6,25 +6,26 @@ #include "AppleVTDecoder.h" #include <CoreVideo/CVPixelBufferIOSurface.h> #include <IOSurface/IOSurface.h> #include "AppleDecoderModule.h" #include "AppleUtils.h" +#include "H264.h" +#include "MP4Decoder.h" #include "MacIOSurfaceImage.h" #include "MediaData.h" -#include "mozilla/ArrayUtils.h" -#include "H264.h" -#include "nsThreadUtils.h" -#include "mozilla/Logging.h" +#include "VPXDecoder.h" #include "VideoUtils.h" #include "gfxPlatform.h" -#include "MacIOSurfaceImage.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/Logging.h" +#include "nsThreadUtils.h" #define LOG(...) DDMOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, __VA_ARGS__) #define LOGEX(_this, ...) \ DDMOZ_LOGEX(_this, sPDMLog, mozilla::LogLevel::Debug, __VA_ARGS__) namespace mozilla { using namespace layers; @@ -37,20 +38,27 @@ AppleVTDecoder::AppleVTDecoder(const Vid mPictureWidth(aConfig.mImage.width), mPictureHeight(aConfig.mImage.height), mDisplayWidth(aConfig.mDisplay.width), mDisplayHeight(aConfig.mDisplay.height), mColorSpace(aConfig.mColorSpace == gfx::YUVColorSpace::UNKNOWN ? DefaultColorSpace({mPictureWidth, mPictureHeight}) : aConfig.mColorSpace), mColorRange(aConfig.mColorRange), + mStreamType(MP4Decoder::IsH264(aConfig.mMimeType) + ? StreamType::H264 + : VPXDecoder::IsVP9(aConfig.mMimeType) + ? StreamType::VP9 + : StreamType::Unknown), mTaskQueue(aTaskQueue), - mMaxRefFrames(aOptions.contains(CreateDecoderParams::Option::LowLatency) - ? 0 - : H264::ComputeMaxRefFrames(aConfig.mExtraData)), + mMaxRefFrames( + mStreamType != StreamType::H264 || + aOptions.contains(CreateDecoderParams::Option::LowLatency) + ? 0 + : H264::ComputeMaxRefFrames(aConfig.mExtraData)), mImageContainer(aImageContainer), mKnowsCompositor(aKnowsCompositor) #ifdef MOZ_WIDGET_UIKIT , mUseSoftwareImages(true) #else , mUseSoftwareImages(false) @@ -59,19 +67,20 @@ AppleVTDecoder::AppleVTDecoder(const Vid mIsFlushing(false), mMonitor("AppleVTDecoder"), mPromise(&mMonitor), // To ensure our PromiseHolder is only ever accessed // with the monitor held. mFormat(nullptr), mSession(nullptr), mIsHardwareAccelerated(false) { MOZ_COUNT_CTOR(AppleVTDecoder); + MOZ_ASSERT(mStreamType != StreamType::Unknown); // TODO: Verify aConfig.mime_type. - LOG("Creating AppleVTDecoder for %dx%d h.264 video", mDisplayWidth, - mDisplayHeight); + LOG("Creating AppleVTDecoder for %dx%d %s video", mDisplayWidth, + mDisplayHeight, mStreamType == StreamType::H264 ? "H.264" : "VP9"); } AppleVTDecoder::~AppleVTDecoder() { MOZ_COUNT_DTOR(AppleVTDecoder); } RefPtr<MediaDataDecoder::InitPromise> AppleVTDecoder::Init() { MediaResult rv = InitializeSession(); if (NS_SUCCEEDED(rv)) { @@ -491,19 +500,22 @@ nsresult AppleVTDecoder::WaitForAsynchro return NS_OK; } MediaResult AppleVTDecoder::InitializeSession() { OSStatus rv; AutoCFRelease<CFDictionaryRef> extensions = CreateDecoderExtensions(); - rv = CMVideoFormatDescriptionCreate(kCFAllocatorDefault, - kCMVideoCodecType_H264, mPictureWidth, - mPictureHeight, extensions, &mFormat); + rv = CMVideoFormatDescriptionCreate( + kCFAllocatorDefault, + mStreamType == StreamType::H264 + ? kCMVideoCodecType_H264 + : CMVideoCodecType(AppleDecoderModule::kCMVideoCodecType_VP9), + mPictureWidth, mPictureHeight, extensions, &mFormat); if (rv != noErr) { return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, RESULT_DETAIL("Couldn't create format description!")); } // Contruct video decoder selection spec. AutoCFRelease<CFDictionaryRef> spec = CreateDecoderSpecification(); @@ -534,21 +546,22 @@ MediaResult AppleVTDecoder::InitializeSe mIsHardwareAccelerated = rv == noErr && isUsingHW == kCFBooleanTrue; LOG("AppleVTDecoder: %s hardware accelerated decoding", mIsHardwareAccelerated ? "using" : "not using"); return NS_OK; } CFDictionaryRef AppleVTDecoder::CreateDecoderExtensions() { - AutoCFRelease<CFDataRef> avc_data = CFDataCreate( + AutoCFRelease<CFDataRef> data = CFDataCreate( kCFAllocatorDefault, mExtraData->Elements(), mExtraData->Length()); - const void* atomsKey[] = {CFSTR("avcC")}; - const void* atomsValue[] = {avc_data}; + const void* atomsKey[1]; + atomsKey[0] = mStreamType == StreamType::H264 ? CFSTR("avcC") : CFSTR("vpcC"); + const void* atomsValue[] = {data}; static_assert(ArrayLength(atomsKey) == ArrayLength(atomsValue), "Non matching keys/values array size"); AutoCFRelease<CFDictionaryRef> atoms = CFDictionaryCreate( kCFAllocatorDefault, atomsKey, atomsValue, ArrayLength(atomsKey), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); const void* extensionKeys[] = {
--- a/dom/media/platforms/apple/AppleVTDecoder.h +++ b/dom/media/platforms/apple/AppleVTDecoder.h @@ -5,16 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_AppleVTDecoder_h #define mozilla_AppleVTDecoder_h #include <CoreFoundation/CFDictionary.h> // For CFDictionaryRef #include <CoreMedia/CoreMedia.h> // For CMVideoFormatDescriptionRef #include <VideoToolbox/VideoToolbox.h> // For VTDecompressionSessionRef +#include "AppleDecoderModule.h" #include "PlatformDecoderModule.h" #include "ReorderQueue.h" #include "TimeUnits.h" #include "mozilla/Atomics.h" #include "mozilla/gfx/Types.h" namespace mozilla { @@ -65,16 +66,17 @@ class AppleVTDecoder : public MediaDataD } // Access from the taskqueue and the decoder's thread. // OutputFrame is thread-safe. void OutputFrame(CVPixelBufferRef aImage, AppleFrameRef aFrameRef); void OnDecodeError(OSStatus aError); private: + friend class AppleDecoderModule; // To access InitializeSession. virtual ~AppleVTDecoder(); RefPtr<FlushPromise> ProcessFlush(); RefPtr<DecodePromise> ProcessDrain(); void ProcessShutdown(); void ProcessDecode(MediaRawData* aSample); void MaybeResolveBufferedFrames(); void AssertOnTaskQueueThread() { @@ -93,16 +95,18 @@ class AppleVTDecoder : public MediaDataD const gfx::ColorRange mColorRange; // Method to set up the decompression session. MediaResult InitializeSession(); nsresult WaitForAsynchronousFrames(); CFDictionaryRef CreateDecoderSpecification(); CFDictionaryRef CreateDecoderExtensions(); + enum class StreamType { Unknown, H264, VP9 }; + const StreamType mStreamType; const RefPtr<TaskQueue> mTaskQueue; const uint32_t mMaxRefFrames; const RefPtr<layers::ImageContainer> mImageContainer; const RefPtr<layers::KnowsCompositor> mKnowsCompositor; const bool mUseSoftwareImages; // Set on reader/decode thread calling Flush() to indicate that output is // not required and so input samples on mTaskQueue need not be processed.
--- a/dom/media/platforms/moz.build +++ b/dom/media/platforms/moz.build @@ -104,16 +104,23 @@ if CONFIG['MOZ_APPLEMEDIA']: ] LOCAL_INCLUDES += [ '/media/libyuv/libyuv/include', ] OS_LIBS += [ '-framework AudioToolbox', '-framework CoreMedia', '-framework VideoToolbox', + # For some unknown reason, the documented method of using weak_import + # attribute doesn't work with VideoToolbox's functions. + # We want to lazily load _VTRegisterSupplementalVideoDecoderIfAvailable + # symbol as it's only available in macOS 11 and later. + '-Wl,-U,_VTRegisterSupplementalVideoDecoderIfAvailable', + # Same for VTIsHardwareDecodeSupported available from macOS 10.13. + '-Wl,-U,_VTIsHardwareDecodeSupported', ] include('/ipc/chromium/chromium-config.mozbuild') if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android': EXPORTS += [ 'android/AndroidDecoderModule.h', 'android/AndroidEncoderModule.h',
--- a/dom/media/platforms/wrappers/MediaChangeMonitor.cpp +++ b/dom/media/platforms/wrappers/MediaChangeMonitor.cpp @@ -151,17 +151,22 @@ class VPXChangeMonitor : public MediaCha public: explicit VPXChangeMonitor(const VideoInfo& aInfo) : mCurrentConfig(aInfo), mCodec(VPXDecoder::IsVP8(aInfo.mMimeType) ? VPXDecoder::Codec::VP8 : VPXDecoder::Codec::VP9) { mTrackInfo = new TrackInfoSharedPtr(mCurrentConfig, mStreamID++); } - bool CanBeInstantiated() const override { return true; } + bool CanBeInstantiated() const override { + // We want to see at least one sample before we create a decoder so that we + // can create the vpcC content on mCurrentConfig.mExtraData. + return mCodec == VPXDecoder::Codec::VP8 || mInfo || + mCurrentConfig.mCrypto.IsEncrypted(); + } MediaResult CheckForChange(MediaRawData* aSample) override { // Don't look at encrypted content. if (aSample->mCrypto.IsEncrypted()) { return NS_OK; } // For both VP8 and VP9, we only look for resolution changes // on keyframes. Other resolution changes are invalid. @@ -189,16 +194,19 @@ class VPXChangeMonitor : public MediaCha } mInfo = Some(info); // For the first frame, we leave the mDisplay/mImage untouched as they // contain aspect ratio (AR) information set by the demuxer. // The AR data isn't found in the VP8/VP9 bytestream. mCurrentConfig.mColorDepth = gfx::ColorDepthForBitDepth(info.mBitDepth); mCurrentConfig.mColorSpace = info.ColorSpace(); mCurrentConfig.mColorRange = info.ColorRange(); + if (mCodec == VPXDecoder::Codec::VP9) { + VPXDecoder::GetVPCCBox(mCurrentConfig.mExtraData, info); + } mTrackInfo = new TrackInfoSharedPtr(mCurrentConfig, mStreamID++); return rv; } const TrackInfo& Config() const override { return mCurrentConfig; } MediaResult PrepareSample(MediaDataDecoder::ConversionRequired aConversion,