tor-browser

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

FFmpegEncoderModule.cpp (7750B)


      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 "FFmpegEncoderModule.h"
      8 
      9 #include "EncoderConfig.h"
     10 #include "FFmpegAudioEncoder.h"
     11 #include "FFmpegLog.h"
     12 #include "FFmpegUtils.h"
     13 #include "FFmpegVideoEncoder.h"
     14 
     15 #ifdef DEBUG
     16 #  include "mozilla/AppShutdown.h"
     17 #endif
     18 
     19 #include "mozilla/StaticPrefs_media.h"
     20 #include "mozilla/gfx/gfxVars.h"
     21 #include "prenv.h"
     22 
     23 // This must be the last header included
     24 #include "FFmpegLibs.h"
     25 
     26 using mozilla::media::EncodeSupport;
     27 using mozilla::media::EncodeSupportSet;
     28 
     29 namespace mozilla {
     30 
     31 template <int V>
     32 /* static */ void FFmpegEncoderModule<V>::Init(const FFmpegLibWrapper* aLib) {
     33 #if (defined(XP_WIN) || defined(MOZ_WIDGET_GTK) ||                \
     34     defined(MOZ_WIDGET_ANDROID)) &&                              \
     35    defined(MOZ_USE_HWDECODE) && !defined(MOZ_FFVPX_AUDIOONLY) && \
     36    LIBAVCODEC_VERSION_MAJOR >= 58
     37 #  ifdef XP_WIN
     38  if (!XRE_IsGPUProcess())
     39 #  else
     40  if (!XRE_IsRDDProcess() &&
     41      !(XRE_IsParentProcess() && PR_GetEnv("MOZ_RUN_GTEST")))
     42 #  endif
     43  {
     44    MOZ_LOG(sPEMLog, LogLevel::Debug,
     45            ("No support in %s process", XRE_GetProcessTypeString()));
     46    return;
     47  }
     48 
     49  if (!gfx::gfxVars::IsInitialized()) {
     50    MOZ_ASSERT(AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdown));
     51    return;
     52  }
     53 
     54  struct CodecEntry {
     55    AVCodecID mId;
     56    bool mHwAllowed;
     57  };
     58 
     59  const CodecEntry kCodecIDs[] = {
     60  // The following open video codecs can be encoded via hardware by using the
     61  // system ffmpeg or ffvpx.
     62 #  if LIBAVCODEC_VERSION_MAJOR >= 59
     63      {AV_CODEC_ID_AV1, gfx::gfxVars::UseAV1HwEncode()},
     64 #  endif
     65      {AV_CODEC_ID_VP9, gfx::gfxVars::UseVP9HwEncode()},
     66 #  if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_ANDROID)
     67      {AV_CODEC_ID_VP8, gfx::gfxVars::UseVP8HwEncode()},
     68 #  endif
     69 
     70 #  if defined(MOZ_WIDGET_GTK) && !defined(FFVPX_VERSION)
     71      // These proprietary video codecs can only be encoded via hardware by
     72      // using the system ffmpeg, not supported by ffvpx.
     73      {AV_CODEC_ID_HEVC, gfx::gfxVars::UseHEVCHwEncode()},
     74      {AV_CODEC_ID_H264, gfx::gfxVars::UseH264HwEncode()},
     75 #  endif
     76 #  if defined(MOZ_WIDGET_ANDROID)
     77      // These proprietary codecs can only be encoded via MediaCodec encoders,
     78      // but the underlying implementation may be software or hardware.
     79      {AV_CODEC_ID_HEVC, true},
     80      {AV_CODEC_ID_H264, true},
     81 #  endif
     82  };
     83 
     84  // Reset the list of supported hardware codecs and reevaluate them.
     85  auto hwCodecs = sSupportedHWCodecs.Lock();
     86  hwCodecs->Clear();
     87  for (const auto& entry : kCodecIDs) {
     88    if (!entry.mHwAllowed) {
     89      MOZ_LOG(
     90          sPEMLog, LogLevel::Debug,
     91          ("Hw codec disabled by gfxVars for %s", AVCodecToString(entry.mId)));
     92      continue;
     93    }
     94 
     95    const auto* codec =
     96        FFmpegDataEncoder<V>::FindHardwareEncoder(aLib, entry.mId);
     97    if (!codec) {
     98      MOZ_LOG(sPEMLog, LogLevel::Debug,
     99              ("No hw codec or encoder for %s", AVCodecToString(entry.mId)));
    100      continue;
    101    }
    102 
    103    hwCodecs->AppendElement(entry.mId);
    104    MOZ_LOG(sPEMLog, LogLevel::Debug,
    105            ("Support %s for hw encoding", AVCodecToString(entry.mId)));
    106  }
    107 #endif  // (XP_WIN || MOZ_WIDGET_GTK || MOZ_WIDGET_ANDROID) && MOZ_USE_HWDECODE
    108        // && !MOZ_FFVPX_AUDIOONLY && LIBAVCODEC_VERSION_MAJOR >= 58
    109 }  // namespace mozilla
    110 
    111 template <int V>
    112 EncodeSupportSet FFmpegEncoderModule<V>::Supports(
    113    const EncoderConfig& aConfig) const {
    114  if (!CanLikelyEncode(aConfig)) {
    115    return EncodeSupportSet{};
    116  }
    117  // We only support L1T2 and L1T3 ScalabilityMode in VPX and AV1 encoders via
    118  // libvpx and libaom for now.
    119  if ((aConfig.mScalabilityMode != ScalabilityMode::None)) {
    120    if (aConfig.mCodec == CodecType::AV1) {
    121      // libaom only supports SVC in CBR mode.
    122      if (aConfig.mBitrateMode != BitrateMode::Constant) {
    123        return EncodeSupportSet{};
    124      }
    125    } else if (aConfig.mCodec != CodecType::VP8 &&
    126               aConfig.mCodec != CodecType::VP9) {
    127      return EncodeSupportSet{};
    128    }
    129  }
    130  auto support = SupportsCodec(aConfig.mCodec);
    131  if (aConfig.mHardwarePreference == HardwarePreference::RequireHardware &&
    132      !support.contains(EncodeSupport::HardwareEncode)) {
    133    return {};
    134  }
    135  if (aConfig.mHardwarePreference == HardwarePreference::RequireSoftware &&
    136      !support.contains(EncodeSupport::SoftwareEncode)) {
    137    return {};
    138  }
    139  return support;
    140 }
    141 
    142 template <int V>
    143 EncodeSupportSet FFmpegEncoderModule<V>::SupportsCodec(CodecType aCodec) const {
    144  AVCodecID id = GetFFmpegEncoderCodecId<V>(aCodec);
    145  if (id == AV_CODEC_ID_NONE) {
    146    return EncodeSupportSet{};
    147  }
    148 #if LIBAVCODEC_VERSION_MAJOR >= 58
    149  if (id == AV_CODEC_ID_HEVC && !StaticPrefs::media_hevc_enabled()) {
    150    return EncodeSupportSet{};
    151  }
    152 #endif
    153  EncodeSupportSet supports;
    154 #ifdef MOZ_USE_HWDECODE
    155  if (StaticPrefs::media_ffvpx_hw_enabled()) {
    156    // We don't need to check the gfxVars again because we check them when we
    157    // populated sSupportedHWCodecs.
    158    auto hwCodecs = sSupportedHWCodecs.Lock();
    159    if (hwCodecs->Contains(static_cast<uint32_t>(id))) {
    160 #  ifdef MOZ_WIDGET_ANDROID
    161      // Because we don't provide software implementations of H264 or HEVC on
    162      // Android, we must use the platform software encoders even if true
    163      // hardware encoding support is missing.
    164      switch (id) {
    165        case AV_CODEC_ID_H264:
    166          supports += gfx::gfxVars::UseH264HwEncode()
    167                          ? EncodeSupport::HardwareEncode
    168                          : EncodeSupport::SoftwareEncode;
    169          break;
    170        case AV_CODEC_ID_HEVC:
    171          supports += gfx::gfxVars::UseHEVCHwEncode()
    172                          ? EncodeSupport::HardwareEncode
    173                          : EncodeSupport::SoftwareEncode;
    174          break;
    175        default:
    176          supports += EncodeSupport::HardwareEncode;
    177          break;
    178      }
    179 #  else
    180      supports += EncodeSupport::HardwareEncode;
    181 #  endif
    182    }
    183  }
    184 #endif
    185  if (FFmpegDataEncoder<V>::FindSoftwareEncoder(mLib, id)) {
    186    supports += EncodeSupport::SoftwareEncode;
    187  }
    188  return supports;
    189 }
    190 
    191 template <int V>
    192 already_AddRefed<MediaDataEncoder> FFmpegEncoderModule<V>::CreateVideoEncoder(
    193    const EncoderConfig& aConfig, const RefPtr<TaskQueue>& aTaskQueue) const {
    194  AVCodecID codecId = GetFFmpegEncoderCodecId<V>(aConfig.mCodec);
    195  if (codecId == AV_CODEC_ID_NONE) {
    196    FFMPEGV_LOG("No ffmpeg encoder for %s", EnumValueToString(aConfig.mCodec));
    197    return nullptr;
    198  }
    199 
    200  RefPtr<MediaDataEncoder> encoder =
    201      new FFmpegVideoEncoder<V>(mLib, codecId, aTaskQueue, aConfig);
    202  FFMPEGV_LOG("ffmpeg %s encoder: %s has been created",
    203              EnumValueToString(aConfig.mCodec),
    204              encoder->GetDescriptionName().get());
    205  return encoder.forget();
    206 }
    207 
    208 template <int V>
    209 already_AddRefed<MediaDataEncoder> FFmpegEncoderModule<V>::CreateAudioEncoder(
    210    const EncoderConfig& aConfig, const RefPtr<TaskQueue>& aTaskQueue) const {
    211  AVCodecID codecId = GetFFmpegEncoderCodecId<V>(aConfig.mCodec);
    212  if (codecId == AV_CODEC_ID_NONE) {
    213    FFMPEGV_LOG("No ffmpeg encoder for %s", EnumValueToString(aConfig.mCodec));
    214    return nullptr;
    215  }
    216 
    217  RefPtr<MediaDataEncoder> encoder =
    218      new FFmpegAudioEncoder<V>(mLib, codecId, aTaskQueue, aConfig);
    219  FFMPEGA_LOG("ffmpeg %s encoder: %s has been created",
    220              EnumValueToString(aConfig.mCodec),
    221              encoder->GetDescriptionName().get());
    222  return encoder.forget();
    223 }
    224 
    225 template class FFmpegEncoderModule<LIBAV_VER>;
    226 
    227 }  // namespace mozilla