tor-browser

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

FFmpegDecoderModule.h (11821B)


      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 #ifndef __FFmpegDecoderModule_h__
      8 #define __FFmpegDecoderModule_h__
      9 
     10 #include "FFmpegAudioDecoder.h"
     11 #include "FFmpegLibWrapper.h"
     12 #include "FFmpegUtils.h"
     13 #include "FFmpegVideoDecoder.h"
     14 #include "MP4Decoder.h"
     15 #include "PlatformDecoderModule.h"
     16 #include "VPXDecoder.h"
     17 #include "VideoUtils.h"
     18 #include "mozilla/DataMutex.h"
     19 #include "mozilla/StaticPrefs_media.h"
     20 #include "mozilla/gfx/gfxVars.h"
     21 #include "prenv.h"
     22 
     23 #ifdef DEBUG
     24 #  include "mozilla/AppShutdown.h"
     25 #endif
     26 
     27 namespace mozilla {
     28 
     29 template <int V>
     30 class FFmpegDecoderModule : public PlatformDecoderModule {
     31 public:
     32  const char* Name() const override {
     33 #ifdef FFVPX_VERSION
     34    return "FFmpeg(FFVPX)";
     35 #else
     36    return "FFmpeg(OS library)";
     37 #endif
     38  }
     39  static void Init(const FFmpegLibWrapper* aLib) {
     40 #if (defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || \
     41     defined(MOZ_WIDGET_ANDROID)) &&               \
     42    defined(MOZ_USE_HWDECODE) && !defined(MOZ_FFVPX_AUDIOONLY)
     43 #  ifdef XP_WIN
     44    if (!XRE_IsGPUProcess()) {
     45      return;
     46    }
     47 #  else
     48    if (!XRE_IsRDDProcess() && !XRE_IsUtilityProcess() &&
     49        !(XRE_IsParentProcess() && PR_GetEnv("MOZ_RUN_GTEST"))) {
     50      return;
     51    }
     52 #  endif
     53 
     54    if (!gfx::gfxVars::IsInitialized()) {
     55      MOZ_ASSERT(AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdown));
     56      return;
     57    }
     58 
     59    const AVHWDeviceType kDeviceTypes[] = {
     60 #  ifdef XP_WIN
     61        AV_HWDEVICE_TYPE_D3D11VA,
     62 #  endif
     63 #  ifdef MOZ_WIDGET_GTK
     64        AV_HWDEVICE_TYPE_VAAPI,
     65        AV_HWDEVICE_TYPE_NONE,  // Placeholder for V4L2.
     66 #  endif
     67 #  ifdef MOZ_WIDGET_ANDROID
     68        AV_HWDEVICE_TYPE_MEDIACODEC,
     69        AV_HWDEVICE_TYPE_NONE,  // Placeholder for audio.
     70 #  endif
     71    };
     72 
     73    struct CodecEntry {
     74      AVCodecID mId;
     75      bool mHwAllowed;
     76    };
     77 
     78    const CodecEntry kCodecIDs[] = {
     79    // The following open video codecs can be decoded via hardware by using the
     80    // system ffmpeg or ffvpx.
     81 #  if LIBAVCODEC_VERSION_MAJOR >= 59
     82        {AV_CODEC_ID_AV1, gfx::gfxVars::UseAV1HwDecode()},
     83 #  endif
     84 #  if LIBAVCODEC_VERSION_MAJOR >= 55
     85        {AV_CODEC_ID_VP9, gfx::gfxVars::UseVP9HwDecode()},
     86 #  endif
     87 #  if (defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_ANDROID)) && \
     88      LIBAVCODEC_VERSION_MAJOR >= 54
     89        {AV_CODEC_ID_VP8, gfx::gfxVars::UseVP8HwDecode()},
     90 #  endif
     91 
     92 #  if defined(MOZ_WIDGET_GTK) && !defined(FFVPX_VERSION)
     93    // These proprietary video codecs can only be decoded via hardware by using
     94    // the system ffmpeg, not supported by ffvpx.
     95 #    if LIBAVCODEC_VERSION_MAJOR >= 55
     96        {AV_CODEC_ID_HEVC, gfx::gfxVars::UseHEVCHwDecode()},
     97 #    endif
     98        {AV_CODEC_ID_H264, gfx::gfxVars::UseH264HwDecode()},
     99 #  endif
    100 #  ifdef MOZ_WIDGET_ANDROID
    101        // These proprietary codecs can only be decoded via MediaCodec decoders,
    102        // but the underlying implementation may be software or hardware.
    103        {AV_CODEC_ID_HEVC, true},
    104        {AV_CODEC_ID_H264, true},
    105        {AV_CODEC_ID_AAC, true},
    106 #  endif
    107    };
    108 
    109    {
    110      // Reset the list of supported hardware codecs and reevaluate them.
    111      auto hwCodecs = sSupportedHWCodecs.Lock();
    112      hwCodecs->Clear();
    113      for (const auto& entry : kCodecIDs) {
    114        if (!entry.mHwAllowed) {
    115          MOZ_LOG(sPDMLog, LogLevel::Debug,
    116                  ("Hw codec disabled by gfxVars for %s",
    117                   AVCodecToString(entry.mId)));
    118          continue;
    119        }
    120 
    121        const AVCodec* codec = nullptr;
    122        for (const auto& deviceType : kDeviceTypes) {
    123          codec = FFmpegVideoDecoder<V>::FindVideoHardwareAVCodec(
    124              aLib, entry.mId, deviceType);
    125          if (codec) {
    126            break;
    127          }
    128        }
    129 
    130        if (!codec) {
    131          MOZ_LOG(
    132              sPDMLog, LogLevel::Debug,
    133              ("No hw codec or decoder for %s", AVCodecToString(entry.mId)));
    134          continue;
    135        }
    136 
    137        hwCodecs->AppendElement(entry.mId);
    138        MOZ_LOG(sPDMLog, LogLevel::Debug,
    139                ("Support %s for hw decoding", AVCodecToString(entry.mId)));
    140      }
    141    }
    142 #endif  // (XP_WIN || MOZ_WIDGET_GTK || MOZ_WIDGET_ANDROID) && MOZ_USE_HWDECODE
    143        // && !MOZ_FFVPX_AUDIOONLY
    144  }
    145 
    146  static already_AddRefed<PlatformDecoderModule> Create(
    147      const FFmpegLibWrapper* aLib) {
    148    RefPtr<PlatformDecoderModule> pdm = new FFmpegDecoderModule(aLib);
    149 
    150    return pdm.forget();
    151  }
    152 
    153  explicit FFmpegDecoderModule(const FFmpegLibWrapper* aLib) : mLib(aLib) {}
    154  virtual ~FFmpegDecoderModule() = default;
    155 
    156  already_AddRefed<MediaDataDecoder> CreateVideoDecoder(
    157      const CreateDecoderParams& aParams) override {
    158    if (Supports(SupportDecoderParams(aParams), nullptr).isEmpty()) {
    159      return nullptr;
    160    }
    161    auto decoder = MakeRefPtr<FFmpegVideoDecoder<V>>(
    162        mLib, aParams.VideoConfig(), aParams.mKnowsCompositor,
    163        aParams.mImageContainer,
    164        aParams.mOptions.contains(CreateDecoderParams::Option::LowLatency),
    165        aParams.mOptions.contains(
    166            CreateDecoderParams::Option::HardwareDecoderNotAllowed),
    167        aParams.mOptions.contains(
    168            CreateDecoderParams::Option::Output8BitPerChannel),
    169        aParams.mTrackingId, aParams.mCDM);
    170 
    171    // Ensure that decoding is exclusively performed using HW decoding in
    172    // the GPU process. If FFmpeg does not support HW decoding, reset the
    173    // decoder to allow PDMFactory to select an alternative HW-capable decoder
    174    // module if available. In contrast, in the RDD process, it is acceptable
    175    // to fallback to SW decoding when HW decoding is not available.
    176    if (XRE_IsGPUProcess()) {
    177      AVCodecID videoCodec =
    178          FFmpegVideoDecoder<V>::GetCodecId(aParams.mConfig.mMimeType);
    179      if (IsHWDecodingSupported(videoCodec) &&
    180          !decoder->IsHardwareAccelerated()) {
    181        MOZ_LOG(sPDMLog, LogLevel::Debug,
    182                ("FFmpeg video decoder can't perform hw decoding, abort!"));
    183        (void)decoder->Shutdown();
    184        decoder = nullptr;
    185      }
    186    }
    187    return decoder.forget();
    188  }
    189 
    190  already_AddRefed<MediaDataDecoder> CreateAudioDecoder(
    191      const CreateDecoderParams& aParams) override {
    192    if (Supports(SupportDecoderParams(aParams), nullptr).isEmpty()) {
    193      return nullptr;
    194    }
    195    RefPtr<MediaDataDecoder> decoder = new FFmpegAudioDecoder<V>(mLib, aParams);
    196    return decoder.forget();
    197  }
    198 
    199  media::DecodeSupportSet SupportsMimeType(
    200      const nsACString& aMimeType,
    201      DecoderDoctorDiagnostics* aDiagnostics) const override {
    202    UniquePtr<TrackInfo> trackInfo = CreateTrackInfoWithMIMEType(aMimeType);
    203    if (!trackInfo) {
    204      return media::DecodeSupportSet{};
    205    }
    206    return Supports(SupportDecoderParams(*trackInfo), aDiagnostics);
    207  }
    208 
    209  media::DecodeSupportSet Supports(
    210      const SupportDecoderParams& aParams,
    211      DecoderDoctorDiagnostics* aDiagnostics) const override {
    212    // This should only be supported by MFMediaEngineDecoderModule.
    213    if (aParams.mMediaEngineId) {
    214      return media::DecodeSupportSet{};
    215    }
    216 
    217    // Temporary - forces use of VPXDecoder when alpha is present.
    218    // Bug 1263836 will handle alpha scenario once implemented. It will shift
    219    // the check for alpha to PDMFactory but not itself remove the need for a
    220    // check.
    221    const auto& trackInfo = aParams.mConfig;
    222    const nsACString& mimeType = trackInfo.mMimeType;
    223    if (VPXDecoder::IsVPX(mimeType) && trackInfo.GetAsVideoInfo()->HasAlpha()) {
    224      MOZ_LOG(sPDMLog, LogLevel::Debug,
    225              ("FFmpeg decoder rejects requested type '%s'",
    226               mimeType.BeginReading()));
    227      return media::DecodeSupportSet{};
    228    }
    229 
    230    if (VPXDecoder::IsVP9(mimeType) &&
    231        aParams.mOptions.contains(CreateDecoderParams::Option::LowLatency)) {
    232      // SVC layers are unsupported, and may be used in low latency use cases
    233      // (WebRTC).
    234      MOZ_LOG(sPDMLog, LogLevel::Debug,
    235              ("FFmpeg decoder rejects requested type '%s' due to low latency",
    236               mimeType.BeginReading()));
    237      return media::DecodeSupportSet{};
    238    }
    239 
    240    if (MP4Decoder::IsHEVC(mimeType) && !StaticPrefs::media_hevc_enabled()) {
    241      MOZ_LOG(
    242          sPDMLog, LogLevel::Debug,
    243          ("FFmpeg decoder rejects requested type '%s' due to being disabled "
    244           "by the pref",
    245           mimeType.BeginReading()));
    246      return media::DecodeSupportSet{};
    247    }
    248 
    249    AVCodecID videoCodec = FFmpegVideoDecoder<V>::GetCodecId(mimeType);
    250    AVCodecID audioCodec = FFmpegAudioDecoder<V>::GetCodecId(
    251        mimeType,
    252        trackInfo.GetAsAudioInfo() ? *trackInfo.GetAsAudioInfo() : AudioInfo());
    253    if (audioCodec == AV_CODEC_ID_NONE && videoCodec == AV_CODEC_ID_NONE) {
    254      MOZ_LOG(sPDMLog, LogLevel::Debug,
    255              ("FFmpeg decoder rejects requested type '%s'",
    256               mimeType.BeginReading()));
    257      return media::DecodeSupportSet{};
    258    }
    259    AVCodecID codecId =
    260        audioCodec != AV_CODEC_ID_NONE ? audioCodec : videoCodec;
    261 
    262    media::DecodeSupportSet supports;
    263    if (IsSWDecodingSupported(codecId)) {
    264      supports += media::DecodeSupport::SoftwareDecode;
    265    }
    266    if (IsHWDecodingSupported(codecId)) {
    267 #ifdef MOZ_WIDGET_ANDROID
    268      // Because we don't provide software implementations of H264 or HEVC on
    269      // Android, we must use the platform software decoders even if true
    270      // hardware decoding support is missing.
    271      switch (codecId) {
    272        case AV_CODEC_ID_H264:
    273          supports += gfx::gfxVars::UseH264HwDecode()
    274                          ? media::DecodeSupport::HardwareDecode
    275                          : media::DecodeSupport::SoftwareDecode;
    276          break;
    277        case AV_CODEC_ID_HEVC:
    278          supports += gfx::gfxVars::UseHEVCHwDecode()
    279                          ? media::DecodeSupport::HardwareDecode
    280                          : media::DecodeSupport::SoftwareDecode;
    281          break;
    282        default:
    283          supports += media::DecodeSupport::HardwareDecode;
    284          break;
    285      }
    286 #else
    287      supports += media::DecodeSupport::HardwareDecode;
    288 #endif
    289    }
    290 
    291 #ifdef XP_WIN
    292    // TODO : add this for Android as well in bug 1974849.
    293    MOZ_ASSERT_IF(XRE_IsGPUProcess() && IsVideoCodec(codecId),
    294                  !supports.contains(media::DecodeSupport::SoftwareDecode));
    295 #endif
    296 
    297    MOZ_LOG(
    298        sPDMLog, LogLevel::Debug,
    299        ("FFmpeg decoder %s requested type '%s'",
    300         supports.isEmpty() ? "rejects" : "supports", mimeType.BeginReading()));
    301    return supports;
    302  }
    303 
    304 protected:
    305  bool SupportsColorDepth(
    306      gfx::ColorDepth aColorDepth,
    307      DecoderDoctorDiagnostics* aDiagnostics) const override {
    308 #if defined(MOZ_WIDGET_ANDROID)
    309    return aColorDepth == gfx::ColorDepth::COLOR_8;
    310 #endif
    311    return true;
    312  }
    313 
    314  bool IsSWDecodingSupported(const AVCodecID& aCodec) const {
    315 #ifdef XP_WIN
    316    // SW video decoding is not allowed in the GPU process.
    317    if (IsVideoCodec(aCodec) && XRE_IsGPUProcess()) {
    318      return false;
    319    }
    320 #endif
    321    return FFmpegDataDecoder<V>::FindSoftwareAVCodec(mLib, aCodec);
    322  }
    323 
    324  bool IsHWDecodingSupported(AVCodecID aCodec) const {
    325 #ifdef FFVPX_VERSION
    326    if (!StaticPrefs::media_ffvpx_hw_enabled()) {
    327      return false;
    328    }
    329 #endif
    330    // We don't need to check the gfxVars again because we check them when we
    331    // populated sSupportedHWCodecs.
    332    auto hwCodecs = sSupportedHWCodecs.Lock();
    333    return hwCodecs->Contains(aCodec);
    334  }
    335 
    336 private:
    337  const FFmpegLibWrapper* mLib;
    338  MOZ_RUNINIT static inline StaticDataMutex<nsTArray<AVCodecID>>
    339      sSupportedHWCodecs{"sSupportedHWCodecs"};
    340 };
    341 
    342 }  // namespace mozilla
    343 
    344 #endif  // __FFmpegDecoderModule_h__