This patch enables VideoDecoder to return "supported" when users request H265 video decoding on macOS by adding and modifying the necessary functions. Additionally, this patch introduces an H265 codec enum type to bypass an assertion that checks the mapping between H265 codec string and internal codec enum type for VideoEncoder. Since the underlying encoder does not yet support H265, VideoEncoder will continue to return "unsupported". Differential Revision: https://phabricator.services.mozilla.com/D236651
212 lines
7.3 KiB
C++
212 lines
7.3 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "PlatformEncoderModule.h"
|
|
#include "H264.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "mozilla/ToString.h"
|
|
|
|
namespace mozilla {
|
|
|
|
extern LazyLogModule sPEMLog;
|
|
#define LOGD(fmt, ...) \
|
|
MOZ_LOG(sPEMLog, mozilla::LogLevel::Debug, \
|
|
("PEM: %s: " fmt, __func__, ##__VA_ARGS__))
|
|
|
|
// TODO: Automatically generate this (Bug 1865896)
|
|
const char* GetCodecTypeString(const CodecType& aCodecType) {
|
|
switch (aCodecType) {
|
|
case CodecType::_BeginVideo_:
|
|
return "_BeginVideo_";
|
|
case CodecType::H264:
|
|
return "H264";
|
|
case CodecType::H265:
|
|
return "H265";
|
|
case CodecType::VP8:
|
|
return "VP8";
|
|
case CodecType::VP9:
|
|
return "VP9";
|
|
case CodecType::AV1:
|
|
return "AV1";
|
|
case CodecType::_EndVideo_: // CodecType::_BeginAudio_
|
|
return "_EndVideo_/_BeginAudio_";
|
|
case CodecType::Opus:
|
|
return "Opus";
|
|
case CodecType::Vorbis:
|
|
return "Vorbis";
|
|
case CodecType::Flac:
|
|
return "Flac";
|
|
case CodecType::AAC:
|
|
return "AAC";
|
|
case CodecType::PCM:
|
|
return "PCM";
|
|
break;
|
|
case CodecType::G722:
|
|
return "G722";
|
|
case CodecType::_EndAudio_:
|
|
return "_EndAudio_";
|
|
case CodecType::Unknown:
|
|
return "Unknown";
|
|
}
|
|
MOZ_ASSERT_UNREACHABLE("undefined codec type");
|
|
return "Undefined";
|
|
}
|
|
|
|
RefPtr<PlatformEncoderModule::CreateEncoderPromise>
|
|
PlatformEncoderModule::AsyncCreateEncoder(const EncoderConfig& aEncoderConfig,
|
|
const RefPtr<TaskQueue>& aTaskQueue) {
|
|
RefPtr<MediaDataEncoder> encoder;
|
|
MediaResult result = NS_OK;
|
|
if (aEncoderConfig.IsAudio()) {
|
|
encoder = CreateAudioEncoder(aEncoderConfig, aTaskQueue);
|
|
} else if (aEncoderConfig.IsVideo()) {
|
|
encoder = CreateVideoEncoder(aEncoderConfig, aTaskQueue);
|
|
}
|
|
if (!encoder) {
|
|
if (NS_FAILED(result)) {
|
|
return CreateEncoderPromise::CreateAndReject(result, __func__);
|
|
}
|
|
return CreateEncoderPromise::CreateAndReject(
|
|
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
|
nsPrintfCString("Error creating encoder for %d",
|
|
static_cast<int>(aEncoderConfig.mCodec))
|
|
.get()),
|
|
__func__);
|
|
}
|
|
return CreateEncoderPromise::CreateAndResolve(encoder, __func__);
|
|
}
|
|
|
|
template <typename T>
|
|
nsCString MaybeToString(const Maybe<T>& aMaybe) {
|
|
return nsPrintfCString(
|
|
"%s", aMaybe.isSome() ? ToString(aMaybe.value()).c_str() : "nothing");
|
|
}
|
|
|
|
struct ConfigurationChangeToString {
|
|
nsCString operator()(const DimensionsChange& aDimensionsChange) {
|
|
return nsPrintfCString("Dimensions: %dx%d", aDimensionsChange.get().width,
|
|
aDimensionsChange.get().height);
|
|
}
|
|
nsCString operator()(const DisplayDimensionsChange& aDisplayDimensionChange) {
|
|
if (aDisplayDimensionChange.get().isNothing()) {
|
|
return nsCString("Display dimensions: nothing");
|
|
}
|
|
gfx::IntSize displayDimensions = aDisplayDimensionChange.get().value();
|
|
return nsPrintfCString("Display dimensions: %dx%d", displayDimensions.width,
|
|
displayDimensions.height);
|
|
}
|
|
nsCString operator()(const BitrateChange& aBitrateChange) {
|
|
if (aBitrateChange.get().isSome()) {
|
|
return nsLiteralCString("Bitrate: nothing");
|
|
}
|
|
return nsPrintfCString("Bitrate: %skbps",
|
|
MaybeToString(aBitrateChange.get()).get());
|
|
}
|
|
nsCString operator()(const FramerateChange& aFramerateChange) {
|
|
if (aFramerateChange.get().isNothing()) {
|
|
return nsCString("Framerate: nothing");
|
|
}
|
|
return nsPrintfCString("Framerate: %lfHz", aFramerateChange.get().value());
|
|
}
|
|
nsCString operator()(const BitrateModeChange& aBitrateModeChange) {
|
|
return nsPrintfCString("Bitrate mode: %s",
|
|
aBitrateModeChange.get() == BitrateMode::Constant
|
|
? "Constant"
|
|
: "Variable");
|
|
}
|
|
nsCString operator()(const UsageChange& aUsageChange) {
|
|
return nsPrintfCString(
|
|
"Usage mode: %s",
|
|
aUsageChange.get() == Usage::Realtime ? "Realtime" : "Recoding");
|
|
}
|
|
nsCString operator()(const ContentHintChange& aContentHintChange) {
|
|
return nsPrintfCString("Content hint: %s",
|
|
MaybeToString(aContentHintChange.get()).get());
|
|
}
|
|
nsCString operator()(const SampleRateChange& aSampleRateChange) {
|
|
return nsPrintfCString("Sample rate %" PRIu32 "Hz",
|
|
aSampleRateChange.get());
|
|
}
|
|
nsCString operator()(const NumberOfChannelsChange& aNumberOfChannelsChange) {
|
|
return nsPrintfCString("Channels: %" PRIu32 "Hz",
|
|
aNumberOfChannelsChange.get());
|
|
}
|
|
};
|
|
|
|
nsString EncoderConfigurationChangeList::ToString() const {
|
|
nsString rv(
|
|
NS_LITERAL_STRING_FROM_CSTRING("EncoderConfigurationChangeList:"_ns));
|
|
for (const EncoderConfigurationItem& change : mChanges) {
|
|
nsCString str = change.match(ConfigurationChangeToString());
|
|
rv.AppendPrintf("- %s\n", str.get());
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
bool CanLikelyEncode(const EncoderConfig& aConfig) {
|
|
if (aConfig.mCodec == CodecType::H264) {
|
|
if (!aConfig.mCodecSpecific ||
|
|
!aConfig.mCodecSpecific->is<H264Specific>()) {
|
|
LOGD(
|
|
"Error: asking for support codec for h264 without h264 specific "
|
|
"config.");
|
|
return false;
|
|
}
|
|
H264Specific specific = aConfig.mCodecSpecific->as<H264Specific>();
|
|
int width = aConfig.mSize.width;
|
|
int height = aConfig.mSize.height;
|
|
if (width % 2 || !width) {
|
|
LOGD("Invalid width of %d for h264", width);
|
|
return false;
|
|
}
|
|
if (height % 2 || !height) {
|
|
LOGD("Invalid height of %d for h264", height);
|
|
return false;
|
|
}
|
|
if (specific.mProfile != H264_PROFILE_BASE &&
|
|
specific.mProfile != H264_PROFILE_MAIN &&
|
|
specific.mProfile != H264_PROFILE_HIGH) {
|
|
LOGD("Invalid profile of %x for h264", specific.mProfile);
|
|
return false;
|
|
}
|
|
// x264 (Linux software) and some Windows configuration (e.g. some nvidia
|
|
// hardware) support level 62 which supports 8k
|
|
if ((specific.mLevel >= H264_LEVEL::H264_LEVEL_6) &&
|
|
(width > 2 * 4096 || height > 2 * 4096)) {
|
|
LOGD("Invalid size of %dx%d for h264", width, height);
|
|
return false;
|
|
}
|
|
// Levels strictly below 6 are limited to 4096x4096px
|
|
if (specific.mLevel < H264_LEVEL::H264_LEVEL_6 &&
|
|
(width > 4096 || height > 4096)) {
|
|
LOGD("Invalid size of %dx%d for h264", width, height);
|
|
return false;
|
|
}
|
|
}
|
|
if (aConfig.mCodec == CodecType::VP8) {
|
|
int width = aConfig.mSize.width;
|
|
int height = aConfig.mSize.height;
|
|
if (width > 2 << 13 || height > 2 << 13) {
|
|
LOGD("Invalid size of %dx%d for VP8", width, height);
|
|
return false;
|
|
}
|
|
}
|
|
if (aConfig.mCodec == CodecType::VP9) {
|
|
int width = aConfig.mSize.width;
|
|
int height = aConfig.mSize.height;
|
|
if (width > 2 << 15 || height > 2 << 15) {
|
|
LOGD("Invalid size of %dx%d for VP9", width, height);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace mozilla
|
|
|
|
#undef LOGD
|