Skip to content

Commit

Permalink
Implement parsing for AV1 profile information.
Browse files Browse the repository at this point in the history
Not all platforms support non-main profiles and it looks like this
is starting to see some usage. We can properly parse the profile
by using the AV1C parser we already have for MSE.

R=cassew

Fixed: 1402222
Change-Id: I7343f0bcaeb3fd2e70b0fc9b3ba4e9c62a0ebb77
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4117912
Commit-Queue: Dale Curtis <dalecurtis@chromium.org>
Reviewed-by: Will Cassella <cassew@chromium.org>
Auto-Submit: Dale Curtis <dalecurtis@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1085259}
  • Loading branch information
dalecurtis authored and Chromium LUCI CQ committed Dec 20, 2022
1 parent 4e0ae07 commit 50fc14c
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 76 deletions.
11 changes: 11 additions & 0 deletions media/ffmpeg/ffmpeg_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -632,9 +632,20 @@ bool AVStreamToVideoDecoderConfig(const AVStream* stream,
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 // BUILDFLAG(ENABLE_AV1_DECODER)
case VideoCodec::kTheora:
profile = THEORAPROFILE_ANY;
break;
Expand Down
108 changes: 41 additions & 67 deletions media/ffmpeg/ffmpeg_common_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,32 @@ void TestConfigConvertExtraData(
codec_parameters->extradata_size = orig_extradata_size;
}

void VerifyProfileTest(const char* file_name,
VideoCodecProfile expected_profile) {
// Open a file to get a real AVStreams from FFmpeg.
base::MemoryMappedFile file;
ASSERT_TRUE(file.Initialize(GetTestDataFilePath(file_name)));
InMemoryUrlProtocol protocol(file.data(), file.length(), false);
FFmpegGlue glue(&protocol);
ASSERT_TRUE(glue.OpenContext());
AVFormatContext* format_context = glue.format_context();

for (size_t i = 0; i < format_context->nb_streams; ++i) {
AVStream* stream = format_context->streams[i];
AVCodecParameters* codec_parameters = stream->codecpar;
AVMediaType codec_type = codec_parameters->codec_type;

if (codec_type == AVMEDIA_TYPE_VIDEO) {
VideoDecoderConfig video_config;
EXPECT_TRUE(AVStreamToVideoDecoderConfig(stream, &video_config));
EXPECT_EQ(expected_profile, video_config.profile());
} else {
// Only process video.
continue;
}
}
}

TEST_F(FFmpegCommonTest, AVStreamToDecoderConfig) {
// Open a file to get a real AVStreams from FFmpeg.
base::MemoryMappedFile file;
Expand Down Expand Up @@ -298,80 +324,19 @@ TEST_F(FFmpegCommonTest, VerifyUmaCodecHashes) {
printf("</enum>\n");
#endif
}

#if BUILDFLAG(USE_PROPRIETARY_CODECS)
TEST_F(FFmpegCommonTest, VerifyH264Profile) {
// Open a file to get a real AVStreams from FFmpeg.
base::MemoryMappedFile file;
ASSERT_TRUE(file.Initialize(GetTestDataFilePath("bear-1280x720.mp4")));
InMemoryUrlProtocol protocol(file.data(), file.length(), false);
FFmpegGlue glue(&protocol);
ASSERT_TRUE(glue.OpenContext());
AVFormatContext* format_context = glue.format_context();

for (size_t i = 0; i < format_context->nb_streams; ++i) {
AVStream* stream = format_context->streams[i];
AVCodecParameters* codec_parameters = stream->codecpar;
AVMediaType codec_type = codec_parameters->codec_type;

if (codec_type == AVMEDIA_TYPE_VIDEO) {
VideoDecoderConfig video_config;
EXPECT_TRUE(AVStreamToVideoDecoderConfig(stream, &video_config));
EXPECT_EQ(H264PROFILE_HIGH, video_config.profile());
} else {
// Only process video.
continue;
}
}
VerifyProfileTest("bear-1280x720.mp4", H264PROFILE_HIGH);
}

#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
TEST_F(FFmpegCommonTest, VerifyH265MainProfile) {
// Open a file to get a real AVStreams from FFmpeg.
base::MemoryMappedFile file;
ASSERT_TRUE(file.Initialize(GetTestDataFilePath("bear-1280x720-hevc.mp4")));
InMemoryUrlProtocol protocol(file.data(), file.length(), false);
FFmpegGlue glue(&protocol);
ASSERT_TRUE(glue.OpenContext());
AVFormatContext* format_context = glue.format_context();

for (size_t i = 0; i < format_context->nb_streams; ++i) {
AVStream* stream = format_context->streams[i];
AVCodecParameters* codec_parameters = stream->codecpar;
AVMediaType codec_type = codec_parameters->codec_type;

if (codec_type == AVMEDIA_TYPE_VIDEO) {
VideoDecoderConfig video_config;
EXPECT_TRUE(AVStreamToVideoDecoderConfig(stream, &video_config));
EXPECT_EQ(HEVCPROFILE_MAIN, video_config.profile());
} else {
// Only process video.
continue;
}
}
VerifyProfileTest("bear-1280x720-hevc.mp4", HEVCPROFILE_MAIN);
}
TEST_F(FFmpegCommonTest, VerifyH265Main10Profile) {
// Open a file to get a real AVStreams from FFmpeg.
base::MemoryMappedFile file;
ASSERT_TRUE(
file.Initialize(GetTestDataFilePath("bear-1280x720-hevc-10bit.mp4")));
InMemoryUrlProtocol protocol(file.data(), file.length(), false);
FFmpegGlue glue(&protocol);
ASSERT_TRUE(glue.OpenContext());
AVFormatContext* format_context = glue.format_context();

for (size_t i = 0; i < format_context->nb_streams; ++i) {
AVStream* stream = format_context->streams[i];
AVCodecParameters* codec_parameters = stream->codecpar;
AVMediaType codec_type = codec_parameters->codec_type;

if (codec_type == AVMEDIA_TYPE_VIDEO) {
VideoDecoderConfig video_config;
EXPECT_TRUE(AVStreamToVideoDecoderConfig(stream, &video_config));
EXPECT_EQ(HEVCPROFILE_MAIN10, video_config.profile());
} else {
// Only process video.
continue;
}
}
TEST_F(FFmpegCommonTest, VerifyH265Main10Profile) {
VerifyProfileTest("bear-1280x720-hevc-10bit.mp4", HEVCPROFILE_MAIN10);
}
#endif // BUILDFLAG(ENABLE_PLATFORM_HEVC)
#endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
Expand Down Expand Up @@ -413,4 +378,13 @@ TEST_F(FFmpegCommonTest, VerifyHDRMetadataAndColorSpaceInfo) {
gfx::ColorSpace::RangeID::FULL),
video_config.color_space_info());
}

#if BUILDFLAG(ENABLE_AV1_DECODER)
TEST_F(FFmpegCommonTest, VerifyAv1Profiles) {
VerifyProfileTest("blackwhite_yuv444p_av1.mp4", AV1PROFILE_PROFILE_HIGH);
VerifyProfileTest("blackwhite_yuv444p_av1.webm", AV1PROFILE_PROFILE_HIGH);
VerifyProfileTest("bear-av1.mp4", AV1PROFILE_PROFILE_MAIN);
}
#endif

} // namespace media
25 changes: 17 additions & 8 deletions media/formats/mp4/box_definitions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,6 @@ bool AVCDecoderConfigurationRecord::Parse(BoxReader* reader) {

bool AVCDecoderConfigurationRecord::Parse(const uint8_t* data, int data_size) {
BufferReader reader(data, data_size);
// TODO(wolenetz): Questionable MediaLog usage, http://crbug.com/712310
NullMediaLog media_log;
return ParseInternal(&reader, &media_log);
}
Expand Down Expand Up @@ -868,8 +867,7 @@ bool VPCodecConfigurationRecord::Parse(BoxReader* reader) {
}

#if BUILDFLAG(ENABLE_AV1_DECODER)
AV1CodecConfigurationRecord::AV1CodecConfigurationRecord()
: profile(VIDEO_CODEC_PROFILE_UNKNOWN) {}
AV1CodecConfigurationRecord::AV1CodecConfigurationRecord() = default;

AV1CodecConfigurationRecord::AV1CodecConfigurationRecord(
const AV1CodecConfigurationRecord& other) = default;
Expand All @@ -880,6 +878,16 @@ FourCC AV1CodecConfigurationRecord::BoxType() const {
return FOURCC_AV1C;
}

bool AV1CodecConfigurationRecord::Parse(BoxReader* reader) {
return ParseInternal(reader, reader->media_log());
}

bool AV1CodecConfigurationRecord::Parse(const uint8_t* data, int data_size) {
BufferReader reader(data, data_size);
NullMediaLog media_log;
return ParseInternal(&reader, &media_log);
}

// Parse the AV1CodecConfigurationRecord, which has the following format:
// unsigned int (1) marker = 1;
// unsigned int (7) version = 1;
Expand All @@ -902,18 +910,19 @@ FourCC AV1CodecConfigurationRecord::BoxType() const {
// }
//
// unsigned int (8)[] configOBUs;
bool AV1CodecConfigurationRecord::Parse(BoxReader* reader) {
bool AV1CodecConfigurationRecord::ParseInternal(BufferReader* reader,
MediaLog* media_log) {
uint8_t av1c_byte = 0;
RCHECK(reader->Read1(&av1c_byte));
const uint8_t av1c_marker = av1c_byte >> 7;
const uint8_t av1c_marker = av1c_byte >> 7;
if (!av1c_marker) {
MEDIA_LOG(ERROR, reader->media_log()) << "Unsupported av1C: marker unset.";
MEDIA_LOG(ERROR, media_log) << "Unsupported av1C: marker unset.";
return false;
}

const uint8_t av1c_version = av1c_byte & 0b01111111;
if (av1c_version != 1) {
MEDIA_LOG(ERROR, reader->media_log())
MEDIA_LOG(ERROR, media_log)
<< "Unsupported av1C: unexpected version number: " << av1c_version;
return false;
}
Expand All @@ -931,7 +940,7 @@ bool AV1CodecConfigurationRecord::Parse(BoxReader* reader) {
profile = AV1PROFILE_PROFILE_PRO;
break;
default:
MEDIA_LOG(ERROR, reader->media_log())
MEDIA_LOG(ERROR, media_log)
<< "Unsupported av1C: unknown profile 0x" << std::hex << seq_profile;
return false;
}
Expand Down
11 changes: 10 additions & 1 deletion media/formats/mp4/box_definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,16 @@ struct MEDIA_EXPORT VPCodecConfigurationRecord : Box {
struct MEDIA_EXPORT AV1CodecConfigurationRecord : Box {
DECLARE_BOX_METHODS(AV1CodecConfigurationRecord);

VideoCodecProfile profile;
// Parses AV1CodecConfigurationRecord data encoded in |data|.
// Note: This method is intended to parse data outside the MP4StreamParser
// context and therefore the box header is not expected to be present
// in |data|
bool Parse(const uint8_t* data, int data_size);

VideoCodecProfile profile = VIDEO_CODEC_PROFILE_UNKNOWN;

private:
bool ParseInternal(BufferReader* reader, MediaLog* media_log);
};
#endif

Expand Down
6 changes: 6 additions & 0 deletions media/test/data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ The video license is [libaom LICENSE].
The first frame of blackwhite\_yuv444p.mp4 coded in AV1 by the following command.
`ffmpeg -i blackwhite_yuv444p.mp4 -strict -2 -vcodec av1 -vframes 1 blackwhite_yuv444p-frame.av1.ivf`

#### blackwhite_yuv444p_av1.mp4
`ffmpeg -i blackwhite_yuv444p-frame.av1.ivf -y -vcodec copy blackwhite_yuv444p_av1.mp4`

#### blackwhite_yuv444p_av1.webm
`ffmpeg -i blackwhite_yuv444p-frame.av1.ivf -y -vcodec copy blackwhite_yuv444p_av1.webm`

#### av1-film\_grain.ivf
AV1 data where film grain feature is used.
This is the same as av1-1-b8-23-film\_grain-50.ivf in [libaom test vectors].
Expand Down
Binary file added media/test/data/blackwhite_yuv444p_av1.mp4
Binary file not shown.
Binary file added media/test/data/blackwhite_yuv444p_av1.webm
Binary file not shown.

0 comments on commit 50fc14c

Please sign in to comment.