blob: 336edbb492f3103470fd7b7b7a6b54f53b299059 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/ffmpeg/ffmpeg_common.h"
#include "base/hash/sha1.h"
#include "base/logging.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/decoder_buffer.h"
#include "media/base/encryption_scheme.h"
#include "media/base/media_util.h"
#include "media/base/video_decoder_config.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"
#endif
namespace media {
namespace {
EncryptionScheme GetEncryptionScheme(const AVStream* stream) {
AVDictionaryEntry* key =
av_dict_get(stream->metadata, "enc_key_id", nullptr, 0);
return key ? EncryptionScheme::kCenc : EncryptionScheme::kUnencrypted;
}
} // namespace
// Why AV_INPUT_BUFFER_PADDING_SIZE? FFmpeg assumes all input buffers are
// padded. Check here to ensure FFmpeg only receives data padded to its
// specifications.
static_assert(DecoderBuffer::kPaddingSize >= AV_INPUT_BUFFER_PADDING_SIZE,
"DecoderBuffer padding size does not fit ffmpeg requirement");
// Alignment requirement by FFmpeg for input and output buffers. This need to
// be updated to match FFmpeg when it changes.
#if defined(ARCH_CPU_ARM_FAMILY)
static const int kFFmpegBufferAddressAlignment = 16;
#else
static const int kFFmpegBufferAddressAlignment = 32;
#endif
// Check here to ensure FFmpeg only receives data aligned to its specifications.
static_assert(
DecoderBuffer::kAlignmentSize >= kFFmpegBufferAddressAlignment &&
DecoderBuffer::kAlignmentSize % kFFmpegBufferAddressAlignment == 0,
"DecoderBuffer alignment size does not fit ffmpeg requirement");
// Allows faster SIMD YUV convert. Also, FFmpeg overreads/-writes occasionally.
// See video_get_buffer() in libavcodec/utils.c.
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::TimeDelta::FromMicroseconds(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 kCodecAAC;
#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
case AV_CODEC_ID_AC3:
return kCodecAC3;
case AV_CODEC_ID_EAC3:
return kCodecEAC3;
#endif
case AV_CODEC_ID_MP3:
return kCodecMP3;
case AV_CODEC_ID_VORBIS:
return kCodecVorbis;
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 kCodecPCM;
case AV_CODEC_ID_PCM_S16BE:
return kCodecPCM_S16BE;
case AV_CODEC_ID_PCM_S24BE:
return kCodecPCM_S24BE;
case AV_CODEC_ID_FLAC:
return kCodecFLAC;
case AV_CODEC_ID_AMR_NB:
return kCodecAMR_NB;
case AV_CODEC_ID_AMR_WB:
return kCodecAMR_WB;
case AV_CODEC_ID_GSM_MS:
return kCodecGSM_MS;
case AV_CODEC_ID_PCM_ALAW:
return kCodecPCM_ALAW;
case AV_CODEC_ID_PCM_MULAW:
return kCodecPCM_MULAW;
case AV_CODEC_ID_OPUS:
return kCodecOpus;
case AV_CODEC_ID_ALAC:
return kCodecALAC;
#if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
case AV_CODEC_ID_MPEGH_3D_AUDIO:
return kCodecMpegHAudio;
#endif
default:
DVLOG(1) << "Unknown audio CodecID: " << codec_id;
}
return kUnknownAudioCodec;
}
AVCodecID AudioCodecToCodecID(AudioCodec audio_codec,
SampleFormat sample_format) {
switch (audio_codec) {
case kCodecAAC:
return AV_CODEC_ID_AAC;
case kCodecALAC:
return AV_CODEC_ID_ALAC;
case kCodecMP3:
return AV_CODEC_ID_MP3;
case kCodecPCM:
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 kCodecPCM_S16BE:
return AV_CODEC_ID_PCM_S16BE;
case kCodecPCM_S24BE:
return AV_CODEC_ID_PCM_S24BE;
case kCodecVorbis:
return AV_CODEC_ID_VORBIS;
case kCodecFLAC:
return AV_CODEC_ID_FLAC;
case kCodecAMR_NB:
return AV_CODEC_ID_AMR_NB;
case kCodecAMR_WB:
return AV_CODEC_ID_AMR_WB;
case kCodecGSM_MS:
return AV_CODEC_ID_GSM_MS;
case kCodecPCM_ALAW:
return AV_CODEC_ID_PCM_ALAW;
case kCodecPCM_MULAW:
return AV_CODEC_ID_PCM_MULAW;
case kCodecOpus:
return AV_CODEC_ID_OPUS;
#if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
case kCodecMpegHAudio:
return AV_CODEC_ID_MPEGH_3D_AUDIO;
#endif
default:
DVLOG(1) << "Unknown AudioCodec: " << audio_codec;
}
return AV_CODEC_ID_NONE;
}
// Converts an FFmpeg video codec ID into its corresponding supported codec id.
static VideoCodec CodecIDToVideoCodec(AVCodecID codec_id) {
switch (codec_id) {
case AV_CODEC_ID_H264:
return kCodecH264;
#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
case AV_CODEC_ID_HEVC:
return kCodecHEVC;
#endif
case AV_CODEC_ID_THEORA:
return kCodecTheora;
case AV_CODEC_ID_MPEG4:
return kCodecMPEG4;
case AV_CODEC_ID_VP8:
return kCodecVP8;
case AV_CODEC_ID_VP9:
return kCodecVP9;
case AV_CODEC_ID_AV1:
return kCodecAV1;
default:
DVLOG(1) << "Unknown video CodecID: " << codec_id;
}
return kUnknownVideoCodec;
}
AVCodecID VideoCodecToCodecID(VideoCodec video_codec) {
switch (video_codec) {
case kCodecH264:
return AV_CODEC_ID_H264;
#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
case kCodecHEVC:
return AV_CODEC_ID_HEVC;
#endif
case kCodecTheora:
return AV_CODEC_ID_THEORA;
case kCodecMPEG4:
return AV_CODEC_ID_MPEG4;
case kCodecVP8:
return AV_CODEC_ID_VP8;
case kCodecVP9:
return AV_CODEC_ID_VP9;
case kCodecAV1:
return AV_CODEC_ID_AV1;
default:
DVLOG(1) << "Unknown VideoCodec: " << video_codec;
}
return AV_CODEC_ID_NONE;
}
static VideoCodecProfile ProfileIDToVideoCodecProfile(int profile) {
// Clear out the CONSTRAINED & INTRA flags which are strict subsets of the
// corresponding profiles with which they're used.
profile &= ~FF_PROFILE_H264_CONSTRAINED;
profile &= ~FF_PROFILE_H264_INTRA;
switch (profile) {
case FF_PROFILE_H264_BASELINE:
return H264PROFILE_BASELINE;
case FF_PROFILE_H264_MAIN:
return H264PROFILE_MAIN;
case FF_PROFILE_H264_EXTENDED:
return H264PROFILE_EXTENDED;
case FF_PROFILE_H264_HIGH:
return H264PROFILE_HIGH;
case FF_PROFILE_H264_HIGH_10:
return H264PROFILE_HIGH10PROFILE;
case FF_PROFILE_H264_HIGH_422:
return H264PROFILE_HIGH422PROFILE;
case FF_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 FF_PROFILE_H264_BASELINE;
case H264PROFILE_MAIN:
return FF_PROFILE_H264_MAIN;
case H264PROFILE_EXTENDED:
return FF_PROFILE_H264_EXTENDED;
case H264PROFILE_HIGH:
return FF_PROFILE_H264_HIGH;
case H264PROFILE_HIGH10PROFILE:
return FF_PROFILE_H264_HIGH_10;
case H264PROFILE_HIGH422PROFILE:
return FF_PROFILE_H264_HIGH_422;
case H264PROFILE_HIGH444PREDICTIVEPROFILE:
return FF_PROFILE_H264_HIGH_444_PREDICTIVE;
default:
DVLOG(1) << "Unknown VideoCodecProfile: " << profile;
}
return FF_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;
// pcm_s24le is treated as a codec with sample format s32 in ffmpeg
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->channels > 8
? CHANNEL_LAYOUT_DISCRETE
: ChannelLayoutToChromeChannelLayout(codec_context->channel_layout,
codec_context->channels);
int sample_rate = codec_context->sample_rate;
switch (codec) {
// For AC3/EAC3 we enable only demuxing, but not decoding, so FFmpeg does
// not fill |sample_fmt|.
case kCodecAC3:
case kCodecEAC3:
#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
// The spec for AC3/EAC3 audio is ETSI TS 102 366. According to sections
// F.3.1 and F.5.1 in that spec the sample_format for AC3/EAC3 must be 16.
sample_format = kSampleFormatS16;
#else
NOTREACHED();
#endif
break;
#if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
case kCodecMpegHAudio:
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::TimeDelta::FromMicroseconds(
codec_context->seek_preroll * 1000000.0 / codec_context->sample_rate);
}
// AVStream occasionally has invalid extra data. See http://crbug.com/517163
if ((codec_context->extradata_size == 0) !=
(codec_context->extradata == nullptr)) {
LOG(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.assign(codec_context->extradata,
codec_context->extradata + codec_context->extradata_size);
}
config->Initialize(codec, sample_format, channel_layout, sample_rate,
extra_data, encryption_scheme, seek_preroll,
codec_context->delay);
if (channel_layout == CHANNEL_LAYOUT_DISCRETE)
config->SetChannelsForDiscrete(codec_context->channels);
#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
// These are bitstream formats unknown to ffmpeg, so they don't have
// a known sample format size.
if (codec == kCodecAC3 || codec == kCodecEAC3)
return true;
#endif
#if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
if (codec == kCodecMpegHAudio)
return true;
#endif
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
// TODO(dalecurtis): Just use the profile from the codec context if ffmpeg
// ever starts supporting xHE-AAC.
if (codec == kCodecAAC && codec_context->profile == FF_PROFILE_UNKNOWN) {
// Errors aren't fatal here, so just drop any MediaLog messages.
NullMediaLog media_log;
mp4::AAC aac_parser;
if (aac_parser.Parse(extra_data, &media_log))
config->set_profile(aac_parser.GetProfile());
}
#endif
// Verify that AudioConfig.bits_per_channel was calculated correctly for
// codecs that have |sample_fmt| set by FFmpeg.
DCHECK_EQ(av_get_bytes_per_sample(codec_context->sample_fmt) * 8,
config->bits_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;
}
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());
// TODO(scherkus): should we set |channel_layout|? I'm not sure if FFmpeg uses
// said information to decode.
codec_context->channels = config.channels();
codec_context->sample_rate = config.samples_per_second();
if (config.extra_data().empty()) {
codec_context->extradata = nullptr;
codec_context->extradata_size = 0;
} else {
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));
memcpy(codec_context->extradata, &config.extra_data()[0],
config.extra_data().size());
memset(codec_context->extradata + config.extra_data().size(), '\0',
AV_INPUT_BUFFER_PADDING_SIZE);
}
}
bool AVStreamToVideoDecoderConfig(const AVStream* stream,
VideoDecoderConfig* config) {
std::unique_ptr<AVCodecContext, ScopedPtrAVFreeContext> codec_context(
AVStreamToAVCodecContext(stream));
if (!codec_context)
return false;
// TODO(vrk): This assumes decoded frame data starts at (0, 0), which is true
// for now, but may not always be true forever. Fix this in the future.
gfx::Rect visible_rect(codec_context->width, codec_context->height);
gfx::Size coded_size = visible_rect.size();
AVRational aspect_ratio = {1, 1};
if (stream->sample_aspect_ratio.num)
aspect_ratio = stream->sample_aspect_ratio;
else if (codec_context->sample_aspect_ratio.num)
aspect_ratio = codec_context->sample_aspect_ratio;
VideoCodec codec = CodecIDToVideoCodec(codec_context->codec_id);
gfx::Size natural_size =
GetNaturalSize(visible_rect.size(), aspect_ratio.num, aspect_ratio.den);
// Without the ffmpeg decoder configured, libavformat is unable to get the
// profile, format, or coded size. So choose sensible defaults and let
// decoders fail later if the configuration is actually unsupported.
//
// TODO(chcunningham): We need real profiles for all of the codecs below to
// actually handle capabilities requests correctly. http://crbug.com/784610
VideoCodecProfile profile = VIDEO_CODEC_PROFILE_UNKNOWN;
switch (codec) {
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
case kCodecH264: {
profile = ProfileIDToVideoCodecProfile(codec_context->profile);
// if the profile is still unknown, try to extract it from
// the extradata using the internal parser
if (profile == VIDEO_CODEC_PROFILE_UNKNOWN && codec_context->extradata &&
codec_context->extradata_size) {
mp4::AVCDecoderConfigurationRecord avc_config;
if (avc_config.Parse(codec_context->extradata,
codec_context->extradata_size)) {
profile = ProfileIDToVideoCodecProfile(avc_config.profile_indication);
}
}
// All the heuristics failed, let's assign a default profile
if (profile == VIDEO_CODEC_PROFILE_UNKNOWN)
profile = H264PROFILE_BASELINE;
break;
}
#endif
case kCodecVP8:
profile = VP8PROFILE_ANY;
break;
case kCodecVP9:
switch (codec_context->profile) {
case FF_PROFILE_VP9_0:
profile = VP9PROFILE_PROFILE0;
break;
case FF_PROFILE_VP9_1:
profile = VP9PROFILE_PROFILE1;
break;
case FF_PROFILE_VP9_2:
profile = VP9PROFILE_PROFILE2;
break;
case FF_PROFILE_VP9_3:
profile = VP9PROFILE_PROFILE3;
break;
default:
profile = VP9PROFILE_MIN;
break;
}
break;
case kCodecAV1:
profile = AV1PROFILE_PROFILE_MAIN;
break;
#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
case kCodecHEVC:
profile = HEVCPROFILE_MAIN;
break;
#endif
case kCodecTheora:
profile = THEORAPROFILE_ANY;
break;
default:
profile = ProfileIDToVideoCodecProfile(codec_context->profile);
}
auto* alpha_mode = av_dict_get(stream->metadata, "alpha_mode", nullptr, 0);
const bool has_alpha = alpha_mode && !strcmp(alpha_mode->value, "1");
VideoRotation video_rotation = VIDEO_ROTATION_0;
int rotation = 0;
AVDictionaryEntry* rotation_entry = NULL;
rotation_entry = av_dict_get(stream->metadata, "rotate", nullptr, 0);
if (rotation_entry && rotation_entry->value && rotation_entry->value[0])
base::StringToInt(rotation_entry->value, &rotation);
switch (rotation) {
case 0:
break;
case 90:
video_rotation = VIDEO_ROTATION_90;
break;
case 180:
video_rotation = VIDEO_ROTATION_180;
break;
case 270:
video_rotation = VIDEO_ROTATION_270;
break;
default:
DLOG(ERROR) << "Unsupported video rotation metadata: " << rotation;
break;
}
// Prefer the color space found by libavcodec if available.
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);
if (!color_space.IsSpecified()) {
// VP9 frames may have color information, but that information cannot
// express new color spaces, like HDR. For that reason, color space
// information from the container should take precedence over color space
// information from the VP9 stream. However, if we infer the color space
// based on resolution here, it looks as if it came from the container.
// Since this inference causes color shifts and is slated to go away
// we just skip it for VP9 and leave the color space undefined, which
// will make the VP9 decoder behave correctly..
// We also ignore the resolution for AV1, since it's new and it's easy
// to make it behave correctly from the get-go.
// TODO(hubbe): Skip this inference for all codecs.
if (codec_context->codec_id != AV_CODEC_ID_VP9 &&
codec_context->codec_id != AV_CODEC_ID_AV1) {
// Otherwise, assume that SD video is usually Rec.601, and HD is usually
// Rec.709.
color_space = (natural_size.height() < 720) ? VideoColorSpace::REC601()
: VideoColorSpace::REC709();
}
}
// AVCodecContext occasionally has invalid extra data. See
// http://crbug.com/517163
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.assign(codec_context->extradata,
codec_context->extradata + codec_context->extradata_size);
}
// TODO(tmathmeyer) ffmpeg can't provide us with an actual video rotation yet.
config->Initialize(codec, profile,
has_alpha ? VideoDecoderConfig::AlphaMode::kHasAlpha
: VideoDecoderConfig::AlphaMode::kIsOpaque,
color_space, VideoTransformation(video_rotation),
coded_size, visible_rect, natural_size, extra_data,
GetEncryptionScheme(stream));
if (stream->nb_side_data) {
for (int i = 0; i < stream->nb_side_data; ++i) {
AVPacketSideData side_data = stream->side_data[i];
if (side_data.type != AV_PKT_DATA_MASTERING_DISPLAY_METADATA)
continue;
gfx::HDRMetadata hdr_metadata{};
AVMasteringDisplayMetadata* metadata =
reinterpret_cast<AVMasteringDisplayMetadata*>(side_data.data);
if (metadata->has_primaries) {
hdr_metadata.mastering_metadata.primary_r =
gfx::PointF(av_q2d(metadata->display_primaries[0][0]),
av_q2d(metadata->display_primaries[0][1]));
hdr_metadata.mastering_metadata.primary_g =
gfx::PointF(av_q2d(metadata->display_primaries[1][0]),
av_q2d(metadata->display_primaries[1][1]));
hdr_metadata.mastering_metadata.primary_b =
gfx::PointF(av_q2d(metadata->display_primaries[2][0]),
av_q2d(metadata->display_primaries[2][1]));
hdr_metadata.mastering_metadata.white_point = gfx::PointF(
av_q2d(metadata->white_point[0]), av_q2d(metadata->white_point[1]));
}
if (metadata->has_luminance) {
hdr_metadata.mastering_metadata.luminance_max =
av_q2d(metadata->max_luminance);
hdr_metadata.mastering_metadata.luminance_min =
av_q2d(metadata->min_luminance);
}
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;
if (config.extra_data().empty()) {
codec_context->extradata = nullptr;
codec_context->extradata_size = 0;
} else {
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));
memcpy(codec_context->extradata, &config.extra_data()[0],
config.extra_data().size());
memset(codec_context->extradata + config.extra_data().size(), '\0',
AV_INPUT_BUFFER_PADDING_SIZE);
}
}
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_2_1:
return CHANNEL_LAYOUT_2_1;
case AV_CH_LAYOUT_SURROUND:
return CHANNEL_LAYOUT_SURROUND;
case AV_CH_LAYOUT_4POINT0:
return CHANNEL_LAYOUT_4_0;
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_7POINT0:
return CHANNEL_LAYOUT_7_0;
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_STEREO_DOWNMIX:
return CHANNEL_LAYOUT_STEREO_DOWNMIX;
case AV_CH_LAYOUT_2POINT1:
return CHANNEL_LAYOUT_2POINT1;
case AV_CH_LAYOUT_3POINT1:
return CHANNEL_LAYOUT_3_1;
case AV_CH_LAYOUT_4POINT1:
return CHANNEL_LAYOUT_4_1;
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_FRONT:
return CHANNEL_LAYOUT_7_0_FRONT;
#ifdef AV_CH_LAYOUT_7POINT1_WIDE_BACK
case AV_CH_LAYOUT_7POINT1_WIDE_BACK:
return CHANNEL_LAYOUT_7_1_WIDE_BACK;
#endif
case AV_CH_LAYOUT_OCTAGONAL:
return CHANNEL_LAYOUT_OCTAGONAL;
default:
// FFmpeg channel_layout is 0 for .wav and .mp3. Attempt to guess layout
// based on the channel count.
return GuessChannelLayout(channels);
}
}
#if !defined(ARCH_CPU_LITTLE_ENDIAN)
#error The code below assumes little-endianness.
#endif
VideoPixelFormat AVPixelFormatToVideoPixelFormat(AVPixelFormat pixel_format) {
// The YUVJ alternatives are FFmpeg's (deprecated, but still in use) way to
// specify a pixel format and full range color combination.
switch (pixel_format) {
case AV_PIX_FMT_YUV444P:
case AV_PIX_FMT_YUVJ444P:
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:
return PIXEL_FORMAT_YUV420P9;
case AV_PIX_FMT_YUV420P10LE:
return PIXEL_FORMAT_YUV420P10;
case AV_PIX_FMT_YUV420P12LE:
return PIXEL_FORMAT_YUV420P12;
case AV_PIX_FMT_YUV422P9LE:
return PIXEL_FORMAT_YUV422P9;
case AV_PIX_FMT_YUV422P10LE:
return PIXEL_FORMAT_YUV422P10;
case AV_PIX_FMT_YUV422P12LE:
return PIXEL_FORMAT_YUV422P12;
case AV_PIX_FMT_YUV444P9LE:
return PIXEL_FORMAT_YUV444P9;
case AV_PIX_FMT_YUV444P10LE:
return PIXEL_FORMAT_YUV444P10;
case AV_PIX_FMT_YUV444P12LE:
return PIXEL_FORMAT_YUV444P12;
case AV_PIX_FMT_P016LE:
return PIXEL_FORMAT_P016LE;
default:
DVLOG(1) << "Unsupported AVPixelFormat: " << pixel_format;
}
return PIXEL_FORMAT_UNKNOWN;
}
VideoColorSpace AVColorSpaceToColorSpace(AVColorSpace color_space,
AVColorRange color_range) {
// TODO(hubbe): make this better
if (color_range == AVCOL_RANGE_JPEG)
return VideoColorSpace::JPEG();
switch (color_space) {
case AVCOL_SPC_UNSPECIFIED:
break;
case AVCOL_SPC_BT709:
return VideoColorSpace::REC709();
case AVCOL_SPC_SMPTE170M:
case AVCOL_SPC_BT470BG:
return VideoColorSpace::REC601();
default:
DVLOG(1) << "Unknown AVColorSpace: " << color_space;
}
return VideoColorSpace();
}
std::string AVErrorToString(int errnum) {
char errbuf[AV_ERROR_MAX_STRING_SIZE] = {0};
av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE);
return std::string(errbuf);
}
int32_t HashCodecName(const char* codec_name) {
// Use the first 32-bits from the SHA1 hash as the identifier.
int32_t hash;
memcpy(&hash, base::SHA1HashString(codec_name).substr(0, 4).c_str(), 4);
return hash;
}
} // namespace media