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