WMFDataEncoderUtils.cpp (8887B)
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 "WMFDataEncoderUtils.h" 8 9 #include "EncoderConfig.h" 10 #include "MFTEncoder.h" 11 #include "MediaData.h" 12 #include "mozilla/Logging.h" 13 #include "mozilla/gfx/gfxVars.h" 14 15 using mozilla::media::EncodeSupport; 16 using mozilla::media::EncodeSupportSet; 17 18 namespace mozilla { 19 20 #define WMF_ENC_LOG(arg, ...) \ 21 MOZ_LOG(mozilla::sPEMLog, mozilla::LogLevel::Error, \ 22 ("WMFDataEncoderUtils::%s: " arg, __func__, ##__VA_ARGS__)) 23 24 GUID CodecToSubtype(CodecType aCodec) { 25 switch (aCodec) { 26 case CodecType::H264: 27 return MFVideoFormat_H264; 28 case CodecType::VP8: 29 return MFVideoFormat_VP80; 30 case CodecType::VP9: 31 return MFVideoFormat_VP90; 32 default: 33 return GUID_NULL; 34 } 35 } 36 37 static bool CanUseWMFHwEncoder(CodecType aCodec) { 38 if (!gfx::gfxVars::IsInitialized()) { 39 return false; 40 } 41 42 switch (aCodec) { 43 case CodecType::H264: 44 return gfx::gfxVars::UseH264HwEncode(); 45 case CodecType::VP8: 46 return gfx::gfxVars::UseVP8HwEncode(); 47 case CodecType::VP9: 48 return gfx::gfxVars::UseVP9HwEncode(); 49 default: 50 return false; 51 } 52 } 53 54 EncodeSupportSet CanCreateWMFEncoder(const EncoderConfig& aConfig) { 55 EncodeSupportSet supports; 56 mscom::EnsureMTA([&]() { 57 if (!wmf::MediaFoundationInitializer::HasInitialized()) { 58 return; 59 } 60 // Try HW encoder if allowed by graphics and not disallowed by the caller. 61 if (aConfig.mHardwarePreference != HardwarePreference::RequireSoftware) { 62 if (CanUseWMFHwEncoder(aConfig.mCodec)) { 63 auto hwEnc = 64 MakeRefPtr<MFTEncoder>(MFTEncoder::HWPreference::HardwareOnly); 65 if (SUCCEEDED(hwEnc->Create(CodecToSubtype(aConfig.mCodec), 66 aConfig.mSize, aConfig.mCodecSpecific))) { 67 supports += EncodeSupport::HardwareEncode; 68 } 69 } else { 70 WMF_ENC_LOG("HW encoder is disabled for %s", 71 EnumValueToString(aConfig.mCodec)); 72 } 73 } 74 if (aConfig.mHardwarePreference != HardwarePreference::RequireHardware) { 75 // Try SW encoder if not disallowed by the caller. 76 auto swEnc = 77 MakeRefPtr<MFTEncoder>(MFTEncoder::HWPreference::SoftwareOnly); 78 if (SUCCEEDED(swEnc->Create(CodecToSubtype(aConfig.mCodec), aConfig.mSize, 79 aConfig.mCodecSpecific))) { 80 supports += EncodeSupport::SoftwareEncode; 81 } 82 } 83 84 WMF_ENC_LOG( 85 "%s encoder support for %s", 86 supports.contains(EncodeSupportSet(EncodeSupport::HardwareEncode, 87 EncodeSupport::SoftwareEncode)) 88 ? "HW | SW" 89 : supports.contains(EncodeSupport::HardwareEncode) ? "HW" 90 : supports.contains(EncodeSupport::SoftwareEncode) ? "SW" 91 : "No", 92 aConfig.ToString().get()); 93 }); 94 return supports; 95 } 96 97 static already_AddRefed<MediaByteBuffer> ParseH264Parameters( 98 const nsTArray<uint8_t>& aHeader, const bool aAsAnnexB) { 99 if (!aAsAnnexB) { 100 return AnnexB::ExtractExtraDataForAVCC(aHeader).forget(); 101 } 102 size_t length = aHeader.Length(); 103 auto annexB = MakeRefPtr<MediaByteBuffer>(length); 104 PodCopy(annexB->Elements(), aHeader.Elements(), length); 105 annexB->SetLength(length); 106 return annexB.forget(); 107 } 108 109 static uint32_t GetProfile(H264_PROFILE aProfileLevel) { 110 switch (aProfileLevel) { 111 case H264_PROFILE_BASE: 112 return eAVEncH264VProfile_Base; 113 case H264_PROFILE_MAIN: 114 return eAVEncH264VProfile_Main; 115 case H264_PROFILE_HIGH: 116 return eAVEncH264VProfile_High; 117 default: 118 return eAVEncH264VProfile_unknown; 119 } 120 } 121 122 already_AddRefed<IMFMediaType> CreateInputType(EncoderConfig& aConfig) { 123 RefPtr<IMFMediaType> type; 124 HRESULT hr = wmf::MFCreateMediaType(getter_AddRefs(type)); 125 if (FAILED(hr)) { 126 WMF_ENC_LOG("MFCreateMediaType (input) error: %lx", hr); 127 return nullptr; 128 } 129 hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); 130 if (FAILED(hr)) { 131 WMF_ENC_LOG("Create input type: SetGUID (major type) error: %lx", hr); 132 return nullptr; 133 } 134 // Always NV12 input 135 hr = type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12); 136 if (FAILED(hr)) { 137 WMF_ENC_LOG("Create input type: SetGUID (subtype) error: %lx", hr); 138 return nullptr; 139 } 140 hr = type->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive); 141 if (FAILED(hr)) { 142 WMF_ENC_LOG("Create input type: interlace mode (input) error: %lx", hr); 143 return nullptr; 144 } 145 // WMF requires a framerate to intialize properly. Provide something 146 // reasonnable if not provided. 147 if (!aConfig.mFramerate) { 148 aConfig.mFramerate = 30; 149 } 150 if (aConfig.mFramerate) { 151 hr = MFSetAttributeRatio(type, MF_MT_FRAME_RATE, aConfig.mFramerate, 1); 152 if (FAILED(hr)) { 153 WMF_ENC_LOG("Create input type: frame rate (input) error: %lx", hr); 154 return nullptr; 155 } 156 } 157 hr = MFSetAttributeSize(type, MF_MT_FRAME_SIZE, aConfig.mSize.width, 158 aConfig.mSize.height); 159 if (FAILED(hr)) { 160 WMF_ENC_LOG("Create input type: frame size (input) error: %lx", hr); 161 return nullptr; 162 } 163 return type.forget(); 164 } 165 166 already_AddRefed<IMFMediaType> CreateOutputType(EncoderConfig& aConfig) { 167 RefPtr<IMFMediaType> type; 168 HRESULT hr = wmf::MFCreateMediaType(getter_AddRefs(type)); 169 if (FAILED(hr)) { 170 WMF_ENC_LOG("MFCreateMediaType (output) error: %lx", hr); 171 return nullptr; 172 } 173 hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); 174 if (FAILED(hr)) { 175 WMF_ENC_LOG("Create output type: set major type error: %lx", hr); 176 return nullptr; 177 } 178 hr = type->SetGUID(MF_MT_SUBTYPE, CodecToSubtype(aConfig.mCodec)); 179 if (FAILED(hr)) { 180 WMF_ENC_LOG("Create output type: set subtype error: %lx", hr); 181 return nullptr; 182 } 183 // A bitrate need to be set here, attempt to make an educated guess if none 184 // is provided. This could be per codec to have nicer defaults. 185 size_t longDimension = std::max(aConfig.mSize.width, aConfig.mSize.height); 186 if (!aConfig.mBitrate) { 187 if (longDimension < 720) { 188 aConfig.mBitrate = 2000000; 189 } else if (longDimension < 1080) { 190 aConfig.mBitrate = 4000000; 191 } else { 192 aConfig.mBitrate = 8000000; 193 } 194 } 195 // No way to set variable / constant here. 196 hr = type->SetUINT32(MF_MT_AVG_BITRATE, aConfig.mBitrate); 197 if (FAILED(hr)) { 198 WMF_ENC_LOG("Create output type: set bitrate error: %lx", hr); 199 return nullptr; 200 } 201 hr = type->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive); 202 if (FAILED(hr)) { 203 WMF_ENC_LOG("Create output type set interlave mode error: %lx", hr); 204 return nullptr; 205 } 206 // A positive rate must always be preset here, see the Input config part. 207 MOZ_ASSERT(aConfig.mFramerate); 208 if (aConfig.mFramerate) { 209 hr = MFSetAttributeRatio(type, MF_MT_FRAME_RATE, aConfig.mFramerate, 1); 210 if (FAILED(hr)) { 211 WMF_ENC_LOG("Create output type set frame rate error: %lx", hr); 212 return nullptr; 213 } 214 } 215 // Required 216 hr = MFSetAttributeSize(type, MF_MT_FRAME_SIZE, aConfig.mSize.width, 217 aConfig.mSize.height); 218 if (FAILED(hr)) { 219 WMF_ENC_LOG("Create output type set frame size error: %lx", hr); 220 return nullptr; 221 } 222 223 if (aConfig.mCodecSpecific.is<H264Specific>()) { 224 MOZ_ASSERT(aConfig.mCodec == CodecType::H264); 225 hr = type->SetUINT32( 226 MF_MT_MPEG2_PROFILE, 227 GetProfile(aConfig.mCodecSpecific.as<H264Specific>().mProfile)); 228 if (FAILED(hr)) { 229 WMF_ENC_LOG("Create output type set profile error: %lx", hr); 230 return nullptr; 231 } 232 } 233 234 // Set keyframe distance through both media type and codec API for better 235 // compatibility. Some encoders may only support one of these methods. 236 // `AVEncVideoMaxKeyframeDistance` is set in `MFTEncoder::SetModes`. 237 uint32_t interval = SaturatingCast<uint32_t>(aConfig.mKeyframeInterval); 238 if (interval > 0) { 239 hr = type->SetUINT32(MF_MT_MAX_KEYFRAME_SPACING, interval); 240 if (FAILED(hr)) { 241 WMF_ENC_LOG("Create output type set keyframe interval error: %lx", hr); 242 return nullptr; 243 } 244 WMF_ENC_LOG("Set MAX_KEYFRAME_SPACING to %u", interval); 245 } 246 247 return type.forget(); 248 } 249 250 HRESULT SetMediaTypes(RefPtr<MFTEncoder>& aEncoder, EncoderConfig& aConfig) { 251 RefPtr<IMFMediaType> inputType = CreateInputType(aConfig); 252 if (!inputType) { 253 return E_FAIL; 254 } 255 256 RefPtr<IMFMediaType> outputType = CreateOutputType(aConfig); 257 if (!outputType) { 258 return E_FAIL; 259 } 260 261 return aEncoder->SetMediaTypes(inputType, outputType); 262 } 263 264 } // namespace mozilla