#include "media/ffmpeg/ffmpeg_common.h"
#include "base/containers/span.h"
#include "base/feature_list.h"
#include "base/hash/sha1.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/audio_timestamp_helper.h"
#include "media/base/decoder_buffer.h"
#include "media/base/encryption_scheme.h"
#include "media/base/media_util.h"
#include "media/base/supported_types.h"
#include "media/base/video_aspect_ratio.h"
#include "media/base/video_color_space.h"
#include "media/base/video_decoder_config.h"
#include "media/base/video_frame.h"
#include "media/base/video_util.h"
#include "media/formats/mp4/box_definitions.h"
#include "media/media_buildflags.h"
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
#include "media/formats/mp4/aac.h"
#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
#include "media/formats/mp4/hevc.h"
#endif
#endif
namespace media {
namespace {
BASE_FEATURE(kStrictFFmpegCodecs, base::FEATURE_ENABLED_BY_DEFAULT);
EncryptionScheme GetEncryptionScheme(const AVStream* stream) {
AVDictionaryEntry* key =
av_dict_get(stream->metadata, "enc_key_id", nullptr, 0);
return key ? EncryptionScheme::kCenc : EncryptionScheme::kUnencrypted;
}
VideoDecoderConfig::AlphaMode GetAlphaMode(const AVStream* stream) {
AVDictionaryEntry* alpha_mode =
av_dict_get(stream->metadata, "alpha_mode", nullptr, 0);
return alpha_mode && std::string_view(alpha_mode->value) == "1"
? VideoDecoderConfig::AlphaMode::kHasAlpha
: VideoDecoderConfig::AlphaMode::kIsOpaque;
}
VideoColorSpace GetGuessedColorSpace(const VideoColorSpace& color_space) {
return VideoColorSpace::FromGfxColorSpace(
color_space.GuessGfxColorSpace());
}
const char* GetAllowedVideoDecoders() {
#if BUILDFLAG(USE_PROPRIETARY_CODECS) && BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
return "h264";
#else
return "";
#endif
}
void ApplyCodecContextSecuritySettings(AVCodecContext* codec_context) {
if (!codec_context->codec_whitelist) {
codec_context->codec_whitelist =
av_strdup(codec_context->codec_type == AVMEDIA_TYPE_AUDIO
? GetAllowedAudioDecoders()
: GetAllowedVideoDecoders());
}
if (base::FeatureList::IsEnabled(kStrictFFmpegCodecs)) {
codec_context->err_recognition |= AV_EF_EXPLODE;
}
}
template <typename T>
void CopyBufferFromConfig(const T& config, AVCodecContext* codec_context) {
if (config.extra_data().empty()) {
codec_context->extradata = nullptr;
codec_context->extradata_size = 0;
return;
}
codec_context->extradata_size = config.extra_data().size();
codec_context->extradata = reinterpret_cast<uint8_t*>(
av_malloc(config.extra_data().size() + AV_INPUT_BUFFER_PADDING_SIZE));
base::span allocated_extradata = UNSAFE_BUFFERS(
base::span(codec_context->extradata,
static_cast<size_t>(config.extra_data().size() +
AV_INPUT_BUFFER_PADDING_SIZE)));
auto [extradata, padding] =
allocated_extradata.split_at(config.extra_data().size());
extradata.copy_from_nonoverlapping(config.extra_data());
std::ranges::fill(padding, '\0');
}
base::span<const uint32_t> GetSkipSamples(const AVPacket* packet) {
size_t skip_samples_size = 0;
const uint32_t* skip_samples_ptr =
reinterpret_cast<const uint32_t*>(av_packet_get_side_data(
packet, AV_PKT_DATA_SKIP_SAMPLES, &skip_samples_size));
return UNSAFE_BUFFERS(
base::span<const uint32_t>(skip_samples_ptr, skip_samples_size));
}
}
static const int kFFmpegOutputBufferPaddingSize = 16;
static_assert(VideoFrame::kFrameSizePadding >= kFFmpegOutputBufferPaddingSize,
"VideoFrame padding size does not fit ffmpeg requirement");
static_assert(
VideoFrame::kFrameAddressAlignment >= kFFmpegBufferAddressAlignment &&
VideoFrame::kFrameAddressAlignment % kFFmpegBufferAddressAlignment == 0,
"VideoFrame frame address alignment does not fit ffmpeg requirement");
static const AVRational kMicrosBase = { 1, base::Time::kMicrosecondsPerSecond };
base::TimeDelta ConvertFromTimeBase(const AVRational& time_base,
int64_t timestamp) {
int64_t microseconds = av_rescale_q(timestamp, time_base, kMicrosBase);
return base::Microseconds(microseconds);
}
int64_t ConvertToTimeBase(const AVRational& time_base,
const base::TimeDelta& timestamp) {
return av_rescale_q(timestamp.InMicroseconds(), kMicrosBase, time_base);
}
AudioCodec CodecIDToAudioCodec(AVCodecID codec_id) {
switch (codec_id) {
case AV_CODEC_ID_AAC:
return AudioCodec::kAAC;
#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
case AV_CODEC_ID_AC3:
return AudioCodec::kAC3;
case AV_CODEC_ID_EAC3:
return AudioCodec::kEAC3;
#endif
case AV_CODEC_ID_MP3:
return AudioCodec::kMP3;
case AV_CODEC_ID_VORBIS:
return AudioCodec::kVorbis;
case AV_CODEC_ID_PCM_U8:
case AV_CODEC_ID_PCM_S16LE:
case AV_CODEC_ID_PCM_S24LE:
case AV_CODEC_ID_PCM_S32LE:
case AV_CODEC_ID_PCM_F32LE:
return AudioCodec::kPCM;
case AV_CODEC_ID_PCM_S16BE:
return AudioCodec::kPCM_S16BE;
case AV_CODEC_ID_PCM_S24BE:
return AudioCodec::kPCM_S24BE;
case AV_CODEC_ID_FLAC:
return AudioCodec::kFLAC;
case AV_CODEC_ID_PCM_ALAW:
return AudioCodec::kPCM_ALAW;
case AV_CODEC_ID_PCM_MULAW:
return AudioCodec::kPCM_MULAW;
case AV_CODEC_ID_OPUS:
return AudioCodec::kOpus;
case AV_CODEC_ID_ALAC:
return AudioCodec::kALAC;
#if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
case AV_CODEC_ID_MPEGH_3D_AUDIO:
return AudioCodec::kMpegHAudio;
#endif
default:
DVLOG(1) << "Unknown audio CodecID: " << codec_id;
}
return AudioCodec::kUnknown;
}
AVCodecID AudioCodecToCodecID(AudioCodec audio_codec,
SampleFormat sample_format) {
switch (audio_codec) {
case AudioCodec::kAAC:
return AV_CODEC_ID_AAC;
case AudioCodec::kALAC:
return AV_CODEC_ID_ALAC;
case AudioCodec::kMP3:
return AV_CODEC_ID_MP3;
case AudioCodec::kPCM:
switch (sample_format) {
case kSampleFormatU8:
return AV_CODEC_ID_PCM_U8;
case kSampleFormatS16:
return AV_CODEC_ID_PCM_S16LE;
case kSampleFormatS24:
return AV_CODEC_ID_PCM_S24LE;
case kSampleFormatS32:
return AV_CODEC_ID_PCM_S32LE;
case kSampleFormatF32:
return AV_CODEC_ID_PCM_F32LE;
default:
DVLOG(1) << "Unsupported sample format: " << sample_format;
}
break;
case AudioCodec::kPCM_S16BE:
return AV_CODEC_ID_PCM_S16BE;
case AudioCodec::kPCM_S24BE:
return AV_CODEC_ID_PCM_S24BE;
case AudioCodec::kVorbis:
return AV_CODEC_ID_VORBIS;
case AudioCodec::kFLAC:
return AV_CODEC_ID_FLAC;
case AudioCodec::kPCM_ALAW:
return AV_CODEC_ID_PCM_ALAW;
case AudioCodec::kPCM_MULAW:
return AV_CODEC_ID_PCM_MULAW;
case AudioCodec::kOpus:
return AV_CODEC_ID_OPUS;
#if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
case AudioCodec::kMpegHAudio:
return AV_CODEC_ID_MPEGH_3D_AUDIO;
#endif
default:
DVLOG(1) << "Unknown AudioCodec: " << audio_codec;
}
return AV_CODEC_ID_NONE;
}
static VideoCodec CodecIDToVideoCodec(AVCodecID codec_id) {
switch (codec_id) {
case AV_CODEC_ID_H264:
return VideoCodec::kH264;
#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
case AV_CODEC_ID_HEVC:
return VideoCodec::kHEVC;
#endif
case AV_CODEC_ID_THEORA:
return VideoCodec::kTheora;
case AV_CODEC_ID_MPEG4:
return VideoCodec::kMPEG4;
case AV_CODEC_ID_VP8:
return VideoCodec::kVP8;
case AV_CODEC_ID_VP9:
return VideoCodec::kVP9;
case AV_CODEC_ID_AV1:
return VideoCodec::kAV1;
default:
DVLOG(1) << "Unknown video CodecID: " << codec_id;
}
return VideoCodec::kUnknown;
}
AVCodecID VideoCodecToCodecID(VideoCodec video_codec) {
switch (video_codec) {
case VideoCodec::kH264:
return AV_CODEC_ID_H264;
#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
case VideoCodec::kHEVC:
return AV_CODEC_ID_HEVC;
#endif
case VideoCodec::kTheora:
return AV_CODEC_ID_THEORA;
case VideoCodec::kMPEG4:
return AV_CODEC_ID_MPEG4;
case VideoCodec::kVP8:
return AV_CODEC_ID_VP8;
case VideoCodec::kVP9:
return AV_CODEC_ID_VP9;
case VideoCodec::kAV1:
return AV_CODEC_ID_AV1;
default:
DVLOG(1) << "Unknown VideoCodec: " << video_codec;
}
return AV_CODEC_ID_NONE;
}
static VideoCodecProfile ProfileIDToVideoCodecProfile(int profile) {
profile &= ~AV_PROFILE_H264_CONSTRAINED;
profile &= ~AV_PROFILE_H264_INTRA;
switch (profile) {
case AV_PROFILE_H264_BASELINE:
return H264PROFILE_BASELINE;
case AV_PROFILE_H264_MAIN:
return H264PROFILE_MAIN;
case AV_PROFILE_H264_EXTENDED:
return H264PROFILE_EXTENDED;
case AV_PROFILE_H264_HIGH:
return H264PROFILE_HIGH;
case AV_PROFILE_H264_HIGH_10:
return H264PROFILE_HIGH10PROFILE;
case AV_PROFILE_H264_HIGH_422:
return H264PROFILE_HIGH422PROFILE;
case AV_PROFILE_H264_HIGH_444_PREDICTIVE:
return H264PROFILE_HIGH444PREDICTIVEPROFILE;
default:
DVLOG(1) << "Unknown profile id: " << profile;
}
return VIDEO_CODEC_PROFILE_UNKNOWN;
}
static int VideoCodecProfileToProfileID(VideoCodecProfile profile) {
switch (profile) {
case H264PROFILE_BASELINE:
return AV_PROFILE_H264_BASELINE;
case H264PROFILE_MAIN:
return AV_PROFILE_H264_MAIN;
case H264PROFILE_EXTENDED:
return AV_PROFILE_H264_EXTENDED;
case H264PROFILE_HIGH:
return AV_PROFILE_H264_HIGH;
case H264PROFILE_HIGH10PROFILE:
return AV_PROFILE_H264_HIGH_10;
case H264PROFILE_HIGH422PROFILE:
return AV_PROFILE_H264_HIGH_422;
case H264PROFILE_HIGH444PREDICTIVEPROFILE:
return AV_PROFILE_H264_HIGH_444_PREDICTIVE;
default:
DVLOG(1) << "Unknown VideoCodecProfile: " << profile;
}
return AV_PROFILE_UNKNOWN;
}
SampleFormat AVSampleFormatToSampleFormat(AVSampleFormat sample_format,
AVCodecID codec_id) {
switch (sample_format) {
case AV_SAMPLE_FMT_U8:
return kSampleFormatU8;
case AV_SAMPLE_FMT_S16:
return kSampleFormatS16;
case AV_SAMPLE_FMT_S32:
if (codec_id == AV_CODEC_ID_PCM_S24LE)
return kSampleFormatS24;
else
return kSampleFormatS32;
case AV_SAMPLE_FMT_FLT:
return kSampleFormatF32;
case AV_SAMPLE_FMT_S16P:
return kSampleFormatPlanarS16;
case AV_SAMPLE_FMT_S32P:
return kSampleFormatPlanarS32;
case AV_SAMPLE_FMT_FLTP:
return kSampleFormatPlanarF32;
default:
DVLOG(1) << "Unknown AVSampleFormat: " << sample_format;
}
return kUnknownSampleFormat;
}
static AVSampleFormat SampleFormatToAVSampleFormat(SampleFormat sample_format) {
switch (sample_format) {
case kSampleFormatU8:
return AV_SAMPLE_FMT_U8;
case kSampleFormatS16:
return AV_SAMPLE_FMT_S16;
case kSampleFormatS24:
case kSampleFormatS32:
return AV_SAMPLE_FMT_S32;
case kSampleFormatF32:
return AV_SAMPLE_FMT_FLT;
case kSampleFormatPlanarS16:
return AV_SAMPLE_FMT_S16P;
case kSampleFormatPlanarF32:
return AV_SAMPLE_FMT_FLTP;
default:
DVLOG(1) << "Unknown SampleFormat: " << sample_format;
}
return AV_SAMPLE_FMT_NONE;
}
bool AVCodecContextToAudioDecoderConfig(const AVCodecContext* codec_context,
EncryptionScheme encryption_scheme,
AudioDecoderConfig* config) {
DCHECK_EQ(codec_context->codec_type, AVMEDIA_TYPE_AUDIO);
AudioCodec codec = CodecIDToAudioCodec(codec_context->codec_id);
SampleFormat sample_format = AVSampleFormatToSampleFormat(
codec_context->sample_fmt, codec_context->codec_id);
ChannelLayout channel_layout =
codec_context->ch_layout.nb_channels > 8
? CHANNEL_LAYOUT_DISCRETE
: ChannelLayoutToChromeChannelLayout(
codec_context->ch_layout.u.mask,
codec_context->ch_layout.nb_channels);
switch (codec) {
case AudioCodec::kAC3:
case AudioCodec::kEAC3:
#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
sample_format = kSampleFormatS16;
break;
#else
NOTREACHED();
#endif
#if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
case AudioCodec::kMpegHAudio:
channel_layout = CHANNEL_LAYOUT_BITSTREAM;
sample_format = kSampleFormatMpegHAudio;
break;
#endif
default:
break;
}
base::TimeDelta seek_preroll;
if (codec_context->seek_preroll > 0) {
seek_preroll = base::Microseconds(codec_context->seek_preroll * 1000000.0 /
codec_context->sample_rate);
}
if ((codec_context->extradata_size == 0) !=
(codec_context->extradata == nullptr)) {
DLOG(ERROR) << __func__
<< (codec_context->extradata == nullptr ? " NULL" : " Non-NULL")
<< " extra data cannot have size of "
<< codec_context->extradata_size << ".";
return false;
}
std::vector<uint8_t> extra_data;
if (codec_context->extradata_size > 0) {
extra_data.resize(codec_context->extradata_size);
base::span(extra_data)
.copy_from_nonoverlapping(AVCodecContextExtraDataToSpan(codec_context));
}
config->Initialize(codec, sample_format, channel_layout, codec_context->sample_rate,
extra_data, encryption_scheme, seek_preroll,
codec_context->delay);
if (channel_layout == CHANNEL_LAYOUT_DISCRETE)
config->SetChannelsForDiscrete(codec_context->ch_layout.nb_channels);
#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
if (codec == AudioCodec::kAC3 || codec == AudioCodec::kEAC3)
return true;
#endif
#if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
if (codec == AudioCodec::kMpegHAudio)
return true;
#endif
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
if (codec == AudioCodec::kAAC) {
if (codec_context->profile == AV_PROFILE_UNKNOWN ||
codec_context->profile == mp4::AAC::kXHeAAcType - 1) {
NullMediaLog media_log;
mp4::AAC aac_parser;
if (aac_parser.Parse(extra_data, &media_log))
config->set_profile(aac_parser.GetProfile());
}
}
#endif
DCHECK_EQ(av_get_bytes_per_sample(codec_context->sample_fmt),
config->bytes_per_channel());
return true;
}
std::unique_ptr<AVCodecContext, ScopedPtrAVFreeContext>
AVStreamToAVCodecContext(const AVStream* stream) {
std::unique_ptr<AVCodecContext, ScopedPtrAVFreeContext> codec_context(
avcodec_alloc_context3(nullptr));
if (avcodec_parameters_to_context(codec_context.get(), stream->codecpar) <
0) {
return nullptr;
}
ApplyCodecContextSecuritySettings(codec_context.get());
return codec_context;
}
bool AVStreamToAudioDecoderConfig(const AVStream* stream,
AudioDecoderConfig* config) {
std::unique_ptr<AVCodecContext, ScopedPtrAVFreeContext> codec_context(
AVStreamToAVCodecContext(stream));
if (!codec_context)
return false;
return AVCodecContextToAudioDecoderConfig(
codec_context.get(), GetEncryptionScheme(stream), config);
}
void AudioDecoderConfigToAVCodecContext(const AudioDecoderConfig& config,
AVCodecContext* codec_context) {
codec_context->codec_type = AVMEDIA_TYPE_AUDIO;
codec_context->codec_id = AudioCodecToCodecID(config.codec(),
config.sample_format());
codec_context->sample_fmt = SampleFormatToAVSampleFormat(
config.sample_format());
codec_context->ch_layout.nb_channels = config.channels();
codec_context->sample_rate = config.samples_per_second();
CopyBufferFromConfig(config, codec_context);
ApplyCodecContextSecuritySettings(codec_context);
}
bool AVStreamToVideoDecoderConfig(const AVStream* stream,
VideoDecoderConfig* config) {
std::unique_ptr<AVCodecContext, ScopedPtrAVFreeContext> codec_context(
AVStreamToAVCodecContext(stream));
if (!codec_context)
return false;
gfx::Rect visible_rect(codec_context->width, codec_context->height);
gfx::Size coded_size = visible_rect.size();
gfx::HDRMetadata hdr_metadata;
VideoAspectRatio aspect_ratio;
if (stream->sample_aspect_ratio.num) {
aspect_ratio = VideoAspectRatio::PAR(stream->sample_aspect_ratio.num,
stream->sample_aspect_ratio.den);
} else if (codec_context->sample_aspect_ratio.num) {
aspect_ratio =
VideoAspectRatio::PAR(codec_context->sample_aspect_ratio.num,
codec_context->sample_aspect_ratio.den);
}
gfx::Size natural_size = aspect_ratio.GetNaturalSize(visible_rect);
VideoCodec codec = CodecIDToVideoCodec(codec_context->codec_id);
VideoCodecProfile profile = VIDEO_CODEC_PROFILE_UNKNOWN;
VideoColorSpace color_space =
VideoColorSpace(codec_context->color_primaries, codec_context->color_trc,
codec_context->colorspace,
codec_context->color_range == AVCOL_RANGE_JPEG
? gfx::ColorSpace::RangeID::FULL
: gfx::ColorSpace::RangeID::LIMITED);
VideoPixelFormat pixel_format =
AVPixelFormatToVideoPixelFormat(codec_context->pix_fmt);
VideoDecoderConfig::AlphaMode alpha_mode = GetAlphaMode(stream);
VideoChromaSampling chroma_sampling =
VideoPixelFormatToChromaSampling(pixel_format);
switch (codec) {
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
case VideoCodec::kH264: {
profile = ProfileIDToVideoCodecProfile(codec_context->profile);
if (profile == VIDEO_CODEC_PROFILE_UNKNOWN && codec_context->extradata &&
codec_context->extradata_size) {
mp4::AVCDecoderConfigurationRecord avc_config;
if (avc_config.Parse(
AVCodecContextExtraDataToSpan(codec_context.get()))) {
profile = ProfileIDToVideoCodecProfile(avc_config.profile_indication);
}
}
if (profile == VIDEO_CODEC_PROFILE_UNKNOWN)
profile = H264PROFILE_BASELINE;
break;
}
#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
case VideoCodec::kHEVC: {
int hevc_profile = -1;
if (codec_context->extradata && codec_context->extradata_size) {
mp4::HEVCDecoderConfigurationRecord hevc_config;
if (hevc_config.Parse(
AVCodecContextExtraDataToSpan(codec_context.get()))) {
hevc_profile = hevc_config.general_profile_idc;
#if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
if (!color_space.IsSpecified()) {
color_space = hevc_config.GetColorSpace();
}
hdr_metadata = hevc_config.GetHDRMetadata();
alpha_mode = hevc_config.GetAlphaMode();
chroma_sampling = hevc_config.GetChromaSampling();
#endif
}
}
switch (hevc_profile) {
case 1:
profile = HEVCPROFILE_MAIN;
break;
case 2:
profile = HEVCPROFILE_MAIN10;
break;
case 3:
profile = HEVCPROFILE_MAIN_STILL_PICTURE;
break;
case 4:
profile = HEVCPROFILE_REXT;
break;
case 5:
profile = HEVCPROFILE_HIGH_THROUGHPUT;
break;
case 6:
profile = HEVCPROFILE_MULTIVIEW_MAIN;
break;
case 7:
profile = HEVCPROFILE_SCALABLE_MAIN;
break;
case 8:
profile = HEVCPROFILE_3D_MAIN;
break;
case 9:
profile = HEVCPROFILE_SCREEN_EXTENDED;
break;
case 10:
profile = HEVCPROFILE_SCALABLE_REXT;
break;
case 11:
profile = HEVCPROFILE_HIGH_THROUGHPUT_SCREEN_EXTENDED;
break;
default:
profile = HEVCPROFILE_MAIN;
break;
}
break;
}
#endif
#endif
case VideoCodec::kVP8:
profile = VP8PROFILE_ANY;
break;
case VideoCodec::kVP9:
switch (codec_context->profile) {
case AV_PROFILE_VP9_0:
profile = VP9PROFILE_PROFILE0;
break;
case AV_PROFILE_VP9_1:
profile = VP9PROFILE_PROFILE1;
break;
case AV_PROFILE_VP9_2:
profile = VP9PROFILE_PROFILE2;
break;
case AV_PROFILE_VP9_3:
profile = VP9PROFILE_PROFILE3;
break;
default:
profile = VP9PROFILE_MIN;
break;
}
break;
#if BUILDFLAG(ENABLE_AV1_DECODER)
case VideoCodec::kAV1:
profile = AV1PROFILE_PROFILE_MAIN;
if (codec_context->extradata && codec_context->extradata_size) {
mp4::AV1CodecConfigurationRecord av1_config;
if (av1_config.Parse(codec_context->extradata,
codec_context->extradata_size)) {
profile = av1_config.profile;
} else {
DLOG(WARNING) << "Failed to parse AV1 extra data for profile.";
}
}
break;
#endif
case VideoCodec::kTheora:
profile = THEORAPROFILE_ANY;
break;
default:
profile = ProfileIDToVideoCodecProfile(codec_context->profile);
}
if (!color_space.IsSpecified()) {
if (codec_context->codec_id != AV_CODEC_ID_VP9 &&
codec_context->codec_id != AV_CODEC_ID_AV1) {
color_space = (natural_size.height() < 720) ? VideoColorSpace::REC601()
: VideoColorSpace::REC709();
}
} else if ((codec_context->codec_id == AV_CODEC_ID_HEVC ||
codec_context->codec_id == AV_CODEC_ID_H264) &&
codec_context->colorspace == AVCOL_SPC_RGB &&
chroma_sampling != VideoChromaSampling::k444) {
color_space = VideoColorSpace::REC709();
} else if (codec_context->codec_id == AV_CODEC_ID_HEVC &&
(color_space.primaries == VideoColorSpace::PrimaryID::INVALID ||
color_space.transfer == VideoColorSpace::TransferID::INVALID ||
color_space.matrix == VideoColorSpace::MatrixID::INVALID) &&
pixel_format == PIXEL_FORMAT_I420) {
color_space = GetGuessedColorSpace(color_space);
}
if (codec_context->extradata != nullptr &&
codec_context->extradata_size == 0) {
DLOG(ERROR) << __func__ << " Non-Null extra data cannot have size of 0.";
return false;
}
std::vector<uint8_t> extra_data;
if (codec_context->extradata_size > 0) {
extra_data.resize(codec_context->extradata_size);
base::span(extra_data)
.copy_from_nonoverlapping(
AVCodecContextExtraDataToSpan(codec_context.get()));
}
VideoTransformation video_transformation = VideoTransformation();
for (const auto& side_data :
AVCodecParametersCodedSideToSpan(stream->codecpar)) {
switch (side_data.type) {
case AV_PKT_DATA_DISPLAYMATRIX: {
CHECK_EQ(side_data.size, sizeof(int32_t) * 3 * 3);
video_transformation = VideoTransformation::FromFFmpegDisplayMatrix(
reinterpret_cast<int32_t*>(side_data.data));
break;
}
case AV_PKT_DATA_MASTERING_DISPLAY_METADATA: {
AVMasteringDisplayMetadata* mdcv =
reinterpret_cast<AVMasteringDisplayMetadata*>(side_data.data);
gfx::HdrMetadataSmpteSt2086 smpte_st_2086;
if (mdcv->has_primaries) {
smpte_st_2086.primaries = {
static_cast<float>(av_q2d(mdcv->display_primaries[0][0])),
static_cast<float>(av_q2d(mdcv->display_primaries[0][1])),
static_cast<float>(av_q2d(mdcv->display_primaries[1][0])),
static_cast<float>(av_q2d(mdcv->display_primaries[1][1])),
static_cast<float>(av_q2d(mdcv->display_primaries[2][0])),
static_cast<float>(av_q2d(mdcv->display_primaries[2][1])),
static_cast<float>(av_q2d(mdcv->white_point[0])),
static_cast<float>(av_q2d(mdcv->white_point[1])),
};
}
if (mdcv->has_luminance) {
smpte_st_2086.luminance_max = av_q2d(mdcv->max_luminance);
smpte_st_2086.luminance_min = av_q2d(mdcv->min_luminance);
}
if (mdcv->has_primaries || mdcv->has_luminance) {
hdr_metadata.smpte_st_2086 = smpte_st_2086;
}
break;
}
case AV_PKT_DATA_CONTENT_LIGHT_LEVEL: {
AVContentLightMetadata* clli =
reinterpret_cast<AVContentLightMetadata*>(side_data.data);
hdr_metadata.cta_861_3 =
gfx::HdrMetadataCta861_3(clli->MaxCLL, clli->MaxFALL);
break;
}
#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
case AV_PKT_DATA_DOVI_CONF: {
AVDOVIDecoderConfigurationRecord* dovi =
reinterpret_cast<AVDOVIDecoderConfigurationRecord*>(side_data.data);
VideoType type;
type.codec = VideoCodec::kDolbyVision;
type.level = dovi->dv_level;
type.color_space = color_space;
type.hdr_metadata_type = gfx::HdrMetadataType::kNone;
switch (dovi->dv_profile) {
case 0:
type.profile = VideoCodecProfile::DOLBYVISION_PROFILE0;
break;
case 5:
type.profile = VideoCodecProfile::DOLBYVISION_PROFILE5;
break;
case 7:
type.profile = VideoCodecProfile::DOLBYVISION_PROFILE7;
break;
case 8:
type.profile = VideoCodecProfile::DOLBYVISION_PROFILE8;
break;
case 9:
type.profile = VideoCodecProfile::DOLBYVISION_PROFILE9;
break;
default:
type.profile = VideoCodecProfile::VIDEO_CODEC_PROFILE_UNKNOWN;
break;
}
if (media::IsDecoderSupportedVideoType(type)) {
codec = type.codec;
profile = type.profile;
}
break;
}
#endif
default:
break;
}
}
config->Initialize(codec, profile, alpha_mode, color_space,
video_transformation, coded_size, visible_rect,
natural_size, extra_data, GetEncryptionScheme(stream));
config->set_aspect_ratio(aspect_ratio);
if (hdr_metadata.IsValid()) {
config->set_hdr_metadata(hdr_metadata);
}
return true;
}
void VideoDecoderConfigToAVCodecContext(
const VideoDecoderConfig& config,
AVCodecContext* codec_context) {
codec_context->codec_type = AVMEDIA_TYPE_VIDEO;
codec_context->codec_id = VideoCodecToCodecID(config.codec());
codec_context->profile = VideoCodecProfileToProfileID(config.profile());
codec_context->coded_width = config.coded_size().width();
codec_context->coded_height = config.coded_size().height();
if (config.color_space_info().range == gfx::ColorSpace::RangeID::FULL)
codec_context->color_range = AVCOL_RANGE_JPEG;
CopyBufferFromConfig(config, codec_context);
ApplyCodecContextSecuritySettings(codec_context);
}
ChannelLayout ChannelLayoutToChromeChannelLayout(int64_t layout, int channels) {
switch (layout) {
case AV_CH_LAYOUT_MONO:
return CHANNEL_LAYOUT_MONO;
case AV_CH_LAYOUT_STEREO:
return CHANNEL_LAYOUT_STEREO;
case AV_CH_LAYOUT_2POINT1:
return CHANNEL_LAYOUT_2POINT1;
case AV_CH_LAYOUT_2_1:
return CHANNEL_LAYOUT_2_1;
case AV_CH_LAYOUT_SURROUND:
return CHANNEL_LAYOUT_SURROUND;
case AV_CH_LAYOUT_3POINT1:
return CHANNEL_LAYOUT_3_1;
case AV_CH_LAYOUT_4POINT0:
return CHANNEL_LAYOUT_4_0;
case AV_CH_LAYOUT_4POINT1:
return CHANNEL_LAYOUT_4_1;
case AV_CH_LAYOUT_2_2:
return CHANNEL_LAYOUT_2_2;
case AV_CH_LAYOUT_QUAD:
return CHANNEL_LAYOUT_QUAD;
case AV_CH_LAYOUT_5POINT0:
return CHANNEL_LAYOUT_5_0;
case AV_CH_LAYOUT_5POINT1:
return CHANNEL_LAYOUT_5_1;
case AV_CH_LAYOUT_5POINT0_BACK:
return CHANNEL_LAYOUT_5_0_BACK;
case AV_CH_LAYOUT_5POINT1_BACK:
return CHANNEL_LAYOUT_5_1_BACK;
case AV_CH_LAYOUT_6POINT0:
return CHANNEL_LAYOUT_6_0;
case AV_CH_LAYOUT_6POINT0_FRONT:
return CHANNEL_LAYOUT_6_0_FRONT;
case AV_CH_LAYOUT_HEXAGONAL:
return CHANNEL_LAYOUT_HEXAGONAL;
case AV_CH_LAYOUT_6POINT1:
return CHANNEL_LAYOUT_6_1;
case AV_CH_LAYOUT_6POINT1_BACK:
return CHANNEL_LAYOUT_6_1_BACK;
case AV_CH_LAYOUT_6POINT1_FRONT:
return CHANNEL_LAYOUT_6_1_FRONT;
case AV_CH_LAYOUT_7POINT0:
return CHANNEL_LAYOUT_7_0;
case AV_CH_LAYOUT_7POINT0_FRONT:
return CHANNEL_LAYOUT_7_0_FRONT;
case AV_CH_LAYOUT_7POINT1:
return CHANNEL_LAYOUT_7_1;
case AV_CH_LAYOUT_7POINT1_WIDE:
return CHANNEL_LAYOUT_7_1_WIDE;
case AV_CH_LAYOUT_7POINT1_WIDE_BACK:
return CHANNEL_LAYOUT_7_1_WIDE_BACK;
case AV_CH_LAYOUT_OCTAGONAL:
return CHANNEL_LAYOUT_OCTAGONAL;
case AV_CH_LAYOUT_STEREO_DOWNMIX:
return CHANNEL_LAYOUT_STEREO_DOWNMIX;
case AV_CH_FRONT_CENTER | AV_CH_LOW_FREQUENCY:
return CHANNEL_LAYOUT_1_1;
case AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_LOW_FREQUENCY |
AV_CH_BACK_CENTER:
return CHANNEL_LAYOUT_3_1_BACK;
default:
return GuessChannelLayout(channels);
}
}
#if !defined(ARCH_CPU_LITTLE_ENDIAN)
#error The code below assumes little-endianness.
#endif
VideoPixelFormat AVPixelFormatToVideoPixelFormat(AVPixelFormat pixel_format) {
switch (pixel_format) {
case AV_PIX_FMT_YUV444P:
case AV_PIX_FMT_YUVJ444P:
case AV_PIX_FMT_GBRP:
return PIXEL_FORMAT_I444;
case AV_PIX_FMT_YUV420P:
case AV_PIX_FMT_YUVJ420P:
return PIXEL_FORMAT_I420;
case AV_PIX_FMT_YUV422P:
case AV_PIX_FMT_YUVJ422P:
return PIXEL_FORMAT_I422;
case AV_PIX_FMT_YUVA420P:
return PIXEL_FORMAT_I420A;
case AV_PIX_FMT_YUV420P9LE:
case AV_PIX_FMT_YUV420P10LE:
return PIXEL_FORMAT_YUV420P10;
case AV_PIX_FMT_YUV420P12LE:
return PIXEL_FORMAT_YUV420P12;
case AV_PIX_FMT_YUV422P9LE:
case AV_PIX_FMT_YUV422P10LE:
return PIXEL_FORMAT_YUV422P10;
case AV_PIX_FMT_YUV422P12LE:
return PIXEL_FORMAT_YUV422P12;
case AV_PIX_FMT_YUV444P9LE:
case AV_PIX_FMT_GBRP9LE:
case AV_PIX_FMT_YUV444P10LE:
case AV_PIX_FMT_GBRP10LE:
return PIXEL_FORMAT_YUV444P10;
case AV_PIX_FMT_YUV444P12LE:
case AV_PIX_FMT_GBRP12LE:
return PIXEL_FORMAT_YUV444P12;
default:
DVLOG(1) << "Unsupported pixel format: " << pixel_format;
return PIXEL_FORMAT_UNKNOWN;
}
}
std::string AVErrorToString(int errnum) {
char errbuf[AV_ERROR_MAX_STRING_SIZE] = {};
av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE);
return std::string(errbuf);
}
int32_t HashCodecName(const char* codec_name) {
int32_t hash;
base::byte_span_from_ref(hash).copy_from_nonoverlapping(
base::as_byte_span(base::SHA1HashString(codec_name)).first<4>());
return hash;
}
const char* GetAllowedAudioDecoders() {
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
#define EXTRA_CODECS ",aac"
#else
#define EXTRA_CODECS
#endif
static constexpr std::string_view kAllowedAudioCodecs =
"vorbis,libopus,flac,pcm_u8,pcm_s16le,pcm_s24le,pcm_s32le,pcm_f32le,"
"mp3,pcm_s16be,pcm_s24be,pcm_mulaw,pcm_alaw" EXTRA_CODECS;
#undef EXTRA_CODECS
return kAllowedAudioCodecs.data();
}
base::TimeDelta ConvertStreamTimestamp(const AVRational& time_base,
int64_t timestamp) {
if (timestamp == kNoFFmpegTimestamp) {
return kNoTimestamp;
}
return ConvertFromTimeBase(time_base, timestamp);
}
std::optional<DecoderBufferSideData::DiscardPadding>
GetDiscardPaddingFromAVPacket(const AVPacket* packet, int samples_per_second) {
constexpr int kSkipSamplesValidSize = 10;
constexpr int kSkipEndSamplesOffset = 1;
base::span<const uint32_t> skip_samples = GetSkipSamples(packet);
if (skip_samples.size() >= kSkipSamplesValidSize) {
auto discard_front_samples = static_cast<int>(skip_samples[0]);
if (discard_front_samples < 0) {
DLOG(ERROR) << "Negative skip samples are not allowed.";
discard_front_samples = 0;
}
const auto discard_end_samples =
static_cast<int>(skip_samples[kSkipEndSamplesOffset]);
if (discard_front_samples || discard_end_samples) {
const auto front_discard = AudioTimestampHelper::FramesToTime(
discard_front_samples, samples_per_second);
return std::make_pair(front_discard,
AudioTimestampHelper::FramesToTime(
discard_end_samples, samples_per_second));
}
}
if (packet->flags & AV_PKT_FLAG_DISCARD) {
return std::make_pair(kInfiniteDuration, base::TimeDelta());
}
return std::nullopt;
}
}