tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

AppleDecoderModule.cpp (10026B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "AppleDecoderModule.h"
      8 
      9 #include <dlfcn.h>
     10 
     11 #include "AOMDecoder.h"
     12 #include "AppleATDecoder.h"
     13 #include "AppleVTDecoder.h"
     14 #include "H265.h"
     15 #include "MP4Decoder.h"
     16 #include "VPXDecoder.h"
     17 #include "VideoUtils.h"
     18 #include "mozilla/Logging.h"
     19 #include "mozilla/ScopeExit.h"
     20 #include "mozilla/StaticPrefs_media.h"
     21 #include "mozilla/gfx/gfxVars.h"
     22 
     23 extern "C" {
     24 // Only exists from MacOS 11
     25 extern void VTRegisterSupplementalVideoDecoderIfAvailable(
     26    CMVideoCodecType codecType) __attribute__((weak_import));
     27 }
     28 
     29 namespace mozilla {
     30 
     31 using media::DecodeSupport;
     32 using media::DecodeSupportSet;
     33 using media::MCSInfo;
     34 using media::MediaCodec;
     35 
     36 static inline CMVideoCodecType GetCMVideoCodecType(const MediaCodec& aCodec) {
     37  switch (aCodec) {
     38    case MediaCodec::H264:
     39      return kCMVideoCodecType_H264;
     40    case MediaCodec::AV1:
     41      return kCMVideoCodecType_AV1;
     42    case MediaCodec::VP9:
     43      return kCMVideoCodecType_VP9;
     44    case MediaCodec::HEVC:
     45      return kCMVideoCodecType_HEVC;
     46    default:
     47      return static_cast<CMVideoCodecType>(0);
     48  }
     49 }
     50 
     51 /* static */
     52 void AppleDecoderModule::Init() {
     53  if (sInitialized) {
     54    return;
     55  }
     56 
     57  // Initialize all values to false first.
     58  for (auto& support : sCanUseHWDecoder) {
     59    support = false;
     60  }
     61 
     62  // H264 HW is supported since 10.6.
     63  sCanUseHWDecoder[MediaCodec::H264] = CanCreateHWDecoder(MediaCodec::H264);
     64  // HEVC HW is supported since 10.13.
     65  sCanUseHWDecoder[MediaCodec::HEVC] = CanCreateHWDecoder(MediaCodec::HEVC);
     66  // VP9 HW is supported since 11.0 on Apple silicon.
     67  sCanUseHWDecoder[MediaCodec::VP9] =
     68      RegisterSupplementalDecoder(MediaCodec::VP9) &&
     69      CanCreateHWDecoder(MediaCodec::VP9);
     70  // AV1 HW is supported since 14.0 on Apple silicon.
     71  sCanUseHWDecoder[MediaCodec::AV1] =
     72      RegisterSupplementalDecoder(MediaCodec::AV1) &&
     73      CanCreateHWDecoder(MediaCodec::AV1);
     74 
     75  sInitialized = true;
     76 }
     77 
     78 nsresult AppleDecoderModule::Startup() {
     79  if (!sInitialized) {
     80    return NS_ERROR_FAILURE;
     81  }
     82  return NS_OK;
     83 }
     84 
     85 already_AddRefed<MediaDataDecoder> AppleDecoderModule::CreateVideoDecoder(
     86    const CreateDecoderParams& aParams) {
     87  if (Supports(SupportDecoderParams(aParams), nullptr /* diagnostics */)
     88          .isEmpty()) {
     89    return nullptr;
     90  }
     91  RefPtr<MediaDataDecoder> decoder;
     92  if (IsVideoSupported(aParams.VideoConfig(), aParams.mOptions)) {
     93    decoder = new AppleVTDecoder(aParams.VideoConfig(), aParams.mImageContainer,
     94                                 aParams.mOptions, aParams.mKnowsCompositor,
     95                                 aParams.mTrackingId);
     96  }
     97  return decoder.forget();
     98 }
     99 
    100 already_AddRefed<MediaDataDecoder> AppleDecoderModule::CreateAudioDecoder(
    101    const CreateDecoderParams& aParams) {
    102  if (Supports(SupportDecoderParams(aParams), nullptr /* diagnostics */)
    103          .isEmpty()) {
    104    return nullptr;
    105  }
    106  RefPtr<MediaDataDecoder> decoder = new AppleATDecoder(aParams.AudioConfig());
    107  return decoder.forget();
    108 }
    109 
    110 DecodeSupportSet AppleDecoderModule::SupportsMimeType(
    111    const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const {
    112  bool checkSupport =
    113      aMimeType.EqualsLiteral("audio/mp4a-latm") ||
    114      MP4Decoder::IsH264(aMimeType) || VPXDecoder::IsVP9(aMimeType) ||
    115      AOMDecoder::IsAV1(aMimeType) || MP4Decoder::IsHEVC(aMimeType);
    116  DecodeSupportSet supportType{};
    117 
    118  if (checkSupport) {
    119    UniquePtr<TrackInfo> trackInfo = CreateTrackInfoWithMIMEType(aMimeType);
    120    if (trackInfo && trackInfo->IsAudio()) {
    121      supportType = DecodeSupport::SoftwareDecode;
    122    } else if (trackInfo && trackInfo->IsVideo()) {
    123      supportType = Supports(SupportDecoderParams(*trackInfo), aDiagnostics);
    124    }
    125  }
    126 
    127  MOZ_LOG(sPDMLog, LogLevel::Debug,
    128          ("Apple decoder %s requested type '%s'",
    129           supportType.isEmpty() ? "rejects" : "supports",
    130           aMimeType.BeginReading()));
    131  return supportType;
    132 }
    133 
    134 DecodeSupportSet AppleDecoderModule::Supports(
    135    const SupportDecoderParams& aParams,
    136    DecoderDoctorDiagnostics* aDiagnostics) const {
    137  const auto& trackInfo = aParams.mConfig;
    138  if (trackInfo.IsAudio()) {
    139    return SupportsMimeType(trackInfo.mMimeType, aDiagnostics);
    140  }
    141  const bool checkSupport = trackInfo.GetAsVideoInfo() &&
    142                            IsVideoSupported(*trackInfo.GetAsVideoInfo());
    143  DecodeSupportSet dss{};
    144  if (!checkSupport) {
    145    return dss;
    146  }
    147  const MediaCodec codec =
    148      MCSInfo::GetMediaCodecFromMimeType(trackInfo.mMimeType);
    149  if (sCanUseHWDecoder[codec]) {
    150    dss += DecodeSupport::HardwareDecode;
    151  }
    152  switch (codec) {
    153    case MediaCodec::VP8:
    154      [[fallthrough]];
    155    case MediaCodec::VP9:
    156      if (StaticPrefs::media_rdd_vpx_enabled()) {
    157        dss += DecodeSupport::SoftwareDecode;
    158      }
    159      break;
    160    default:
    161      dss += DecodeSupport::SoftwareDecode;
    162      break;
    163  }
    164  return dss;
    165 }
    166 
    167 bool AppleDecoderModule::IsVideoSupported(
    168    const VideoInfo& aConfig,
    169    const CreateDecoderParams::OptionSet& aOptions) const {
    170  if (MP4Decoder::IsH264(aConfig.mMimeType)) {
    171    return true;
    172  }
    173  if (MP4Decoder::IsHEVC(aConfig.mMimeType)) {
    174    return StaticPrefs::media_hevc_enabled();
    175  }
    176  if (AOMDecoder::IsAV1(aConfig.mMimeType)) {
    177    if (!sCanUseHWDecoder[MediaCodec::AV1] ||
    178        aOptions.contains(
    179            CreateDecoderParams::Option::HardwareDecoderNotAllowed)) {
    180      return false;
    181    }
    182 
    183    // HW AV1 decoder only supports 8 or 10 bit color.
    184    if (aConfig.mColorDepth != gfx::ColorDepth::COLOR_8 &&
    185        aConfig.mColorDepth != gfx::ColorDepth::COLOR_10) {
    186      return false;
    187    }
    188 
    189    if (aConfig.mColorSpace.isSome()) {
    190      if (*aConfig.mColorSpace == gfx::YUVColorSpace::Identity) {
    191        // HW AV1 decoder doesn't support RGB
    192        return false;
    193      }
    194    }
    195 
    196    if (aConfig.mExtraData && aConfig.mExtraData->Length() < 2) {
    197      return true;  // Assume it's okay.
    198    }
    199    // top 3 bits are the profile.
    200    int profile = aConfig.mExtraData->ElementAt(1) >> 5;
    201    // 0 is main profile
    202    return profile == 0;
    203  }
    204 
    205  if (!VPXDecoder::IsVP9(aConfig.mMimeType) ||
    206      !sCanUseHWDecoder[MediaCodec::VP9] ||
    207      aOptions.contains(
    208          CreateDecoderParams::Option::HardwareDecoderNotAllowed)) {
    209    return false;
    210  }
    211  if (VPXDecoder::IsVP9(aConfig.mMimeType) &&
    212      aOptions.contains(CreateDecoderParams::Option::LowLatency)) {
    213    // SVC layers are unsupported, and may be used in low latency use cases
    214    // (WebRTC).
    215    return false;
    216  }
    217  if (aConfig.HasAlpha()) {
    218    return false;
    219  }
    220 
    221  // HW VP9 decoder only supports 8 or 10 bit color.
    222  if (aConfig.mColorDepth != gfx::ColorDepth::COLOR_8 &&
    223      aConfig.mColorDepth != gfx::ColorDepth::COLOR_10) {
    224    return false;
    225  }
    226 
    227  // See if we have a vpcC box, and check further constraints.
    228  // HW VP9 Decoder supports Profile 0 & 2 (YUV420)
    229  if (aConfig.mExtraData && aConfig.mExtraData->Length() < 5) {
    230    return true;  // Assume it's okay.
    231  }
    232  int profile = aConfig.mExtraData->ElementAt(4);
    233 
    234  return profile == 0 || profile == 2;
    235 }
    236 
    237 /* static */
    238 bool AppleDecoderModule::CanCreateHWDecoder(const MediaCodec& aCodec) {
    239  // Check whether HW decode should even be enabled
    240  if (!gfx::gfxVars::CanUseHardwareVideoDecoding() || XRE_IsUtilityProcess()) {
    241    return false;
    242  }
    243 
    244  if (!VTIsHardwareDecodeSupported(GetCMVideoCodecType(aCodec))) {
    245    return false;
    246  }
    247 
    248  // H264 hardware decoding has been supported since macOS 10.6 on most Intel
    249  // GPUs (Sandy Bridge and later, 2011). If VTIsHardwareDecodeSupported is
    250  // already true, there's no need for further verification.
    251  if (aCodec == MediaCodec::H264) {
    252    return true;
    253  }
    254 
    255  // Build up a fake extradata to create an actual decoder to verify
    256  VideoInfo info(1920, 1080);
    257  if (aCodec == MediaCodec::AV1) {
    258    info.mMimeType = "video/av1";
    259    bool hasSeqHdr;
    260    AOMDecoder::AV1SequenceInfo seqInfo;
    261    AOMDecoder::OperatingPoint op;
    262    seqInfo.mOperatingPoints.AppendElement(op);
    263    seqInfo.mImage = {1920, 1080};
    264    AOMDecoder::WriteAV1CBox(seqInfo, info.mExtraData, hasSeqHdr);
    265  } else if (aCodec == MediaCodec::VP9) {
    266    info.mMimeType = "video/vp9";
    267    VPXDecoder::GetVPCCBox(info.mExtraData, VPXDecoder::VPXStreamInfo());
    268  } else if (aCodec == MediaCodec::HEVC) {
    269    // Although HEVC hardware decoding is supported starting with macOS 10.13
    270    // and we only support macOS 10.15+, Intel GPUs (Skylake and later, 2015)
    271    // that support HEVC are not old enough to skip verification.
    272    info.mMimeType = "video/hevc";
    273    info.mExtraData = H265::CreateFakeExtraData();
    274  }
    275 
    276  RefPtr<AppleVTDecoder> decoder =
    277      new AppleVTDecoder(info, nullptr, {}, nullptr, Nothing());
    278  auto release = MakeScopeExit([&]() { decoder->Shutdown(); });
    279  if (NS_FAILED(decoder->InitializeSession())) {
    280    MOZ_LOG(sPDMLog, LogLevel::Debug,
    281            ("Failed to initializing VT HW decoder session"));
    282    return false;
    283  }
    284  nsAutoCString failureReason;
    285  bool hwSupport = decoder->IsHardwareAccelerated(failureReason);
    286  if (!hwSupport) {
    287    MOZ_LOG(
    288        sPDMLog, LogLevel::Debug,
    289        ("VT decoder failed to use HW : '%s'", failureReason.BeginReading()));
    290  }
    291  return hwSupport;
    292 }
    293 
    294 /* static */
    295 bool AppleDecoderModule::RegisterSupplementalDecoder(const MediaCodec& aCodec) {
    296 #ifdef XP_MACOSX
    297  static bool sRegisterIfAvailable = [&]() {
    298    if (__builtin_available(macos 11.0, *)) {
    299      VTRegisterSupplementalVideoDecoderIfAvailable(
    300          GetCMVideoCodecType(aCodec));
    301      return true;
    302    }
    303    return false;
    304  }();
    305  return sRegisterIfAvailable;
    306 #else  // iOS
    307  return false;
    308 #endif
    309 }
    310 
    311 /* static */
    312 already_AddRefed<PlatformDecoderModule> AppleDecoderModule::Create() {
    313  return MakeAndAddRef<AppleDecoderModule>();
    314 }
    315 
    316 }  // namespace mozilla