123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 |
- // Copyright (c) 2011 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 <stddef.h>
- #include <stdint.h>
- #include <cstring>
- #include "base/bind.h"
- #include "base/files/memory_mapped_file.h"
- #include "base/logging.h"
- #include "media/base/audio_decoder_config.h"
- #include "media/base/media.h"
- #include "media/base/media_util.h"
- #include "media/base/test_data_util.h"
- #include "media/base/video_decoder_config.h"
- #include "media/filters/ffmpeg_glue.h"
- #include "media/filters/in_memory_url_protocol.h"
- #include "testing/gtest/include/gtest/gtest.h"
- namespace media {
- class FFmpegCommonTest : public testing::Test {
- public:
- FFmpegCommonTest() {}
- ~FFmpegCommonTest() override = default;
- };
- uint8_t kExtraData[5] = {0x00, 0x01, 0x02, 0x03, 0x04};
- template <typename T>
- void TestConfigConvertExtraData(
- AVStream* stream,
- T* decoder_config,
- const base::RepeatingCallback<bool(const AVStream*, T*)>& converter_fn) {
- // Should initially convert.
- EXPECT_TRUE(converter_fn.Run(stream, decoder_config));
- // Store orig to let FFmpeg free whatever it allocated.
- AVCodecParameters* codec_parameters = stream->codecpar;
- uint8_t* orig_extradata = codec_parameters->extradata;
- int orig_extradata_size = codec_parameters->extradata_size;
- // Valid combination: extra_data = nullptr && size = 0.
- codec_parameters->extradata = nullptr;
- codec_parameters->extradata_size = 0;
- EXPECT_TRUE(converter_fn.Run(stream, decoder_config));
- EXPECT_EQ(static_cast<size_t>(codec_parameters->extradata_size),
- decoder_config->extra_data().size());
- // Valid combination: extra_data = non-nullptr && size > 0.
- codec_parameters->extradata = &kExtraData[0];
- codec_parameters->extradata_size = std::size(kExtraData);
- EXPECT_TRUE(converter_fn.Run(stream, decoder_config));
- EXPECT_EQ(static_cast<size_t>(codec_parameters->extradata_size),
- decoder_config->extra_data().size());
- EXPECT_EQ(
- 0, memcmp(codec_parameters->extradata, &decoder_config->extra_data()[0],
- decoder_config->extra_data().size()));
- // Possible combination: extra_data = nullptr && size != 0, but the converter
- // function considers this valid and having no extra_data, due to behavior of
- // avcodec_parameters_to_context().
- codec_parameters->extradata = nullptr;
- codec_parameters->extradata_size = 10;
- EXPECT_TRUE(converter_fn.Run(stream, decoder_config));
- EXPECT_EQ(0UL, decoder_config->extra_data().size());
- // Invalid combination: extra_data = non-nullptr && size = 0.
- codec_parameters->extradata = &kExtraData[0];
- codec_parameters->extradata_size = 0;
- EXPECT_FALSE(converter_fn.Run(stream, decoder_config));
- // Restore orig values for sane cleanup.
- codec_parameters->extradata = orig_extradata;
- codec_parameters->extradata_size = orig_extradata_size;
- }
- TEST_F(FFmpegCommonTest, AVStreamToDecoderConfig) {
- // Open a file to get a real AVStreams from FFmpeg.
- base::MemoryMappedFile file;
- ASSERT_TRUE(file.Initialize(GetTestDataFilePath("bear-320x240.webm")));
- InMemoryUrlProtocol protocol(file.data(), file.length(), false);
- FFmpegGlue glue(&protocol);
- ASSERT_TRUE(glue.OpenContext());
- AVFormatContext* format_context = glue.format_context();
- // Find the audio and video streams and test valid and invalid combinations
- // for extradata and extradata_size.
- bool found_audio = false;
- bool found_video = false;
- for (size_t i = 0;
- i < format_context->nb_streams && (!found_audio || !found_video);
- ++i) {
- AVStream* stream = format_context->streams[i];
- AVCodecParameters* codec_parameters = stream->codecpar;
- AVMediaType codec_type = codec_parameters->codec_type;
- if (codec_type == AVMEDIA_TYPE_AUDIO) {
- if (found_audio)
- continue;
- found_audio = true;
- AudioDecoderConfig audio_config;
- TestConfigConvertExtraData(
- stream, &audio_config,
- base::BindRepeating(&AVStreamToAudioDecoderConfig));
- } else if (codec_type == AVMEDIA_TYPE_VIDEO) {
- if (found_video)
- continue;
- found_video = true;
- VideoDecoderConfig video_config;
- TestConfigConvertExtraData(
- stream, &video_config,
- base::BindRepeating(&AVStreamToVideoDecoderConfig));
- } else {
- // Only process audio/video.
- continue;
- }
- }
- ASSERT_TRUE(found_audio);
- ASSERT_TRUE(found_video);
- }
- TEST_F(FFmpegCommonTest, AVStreamToAudioDecoderConfig_OpusAmbisonics_4ch) {
- base::MemoryMappedFile file;
- ASSERT_TRUE(file.Initialize(
- GetTestDataFilePath("bear-opus-end-trimming-4ch-channelmapping2.webm")));
- InMemoryUrlProtocol protocol(file.data(), file.length(), false);
- FFmpegGlue glue(&protocol);
- ASSERT_TRUE(glue.OpenContext());
- AVFormatContext* format_context = glue.format_context();
- EXPECT_EQ(static_cast<unsigned int>(1), format_context->nb_streams);
- AVStream* stream = format_context->streams[0];
- AVCodecParameters* codec_parameters = stream->codecpar;
- EXPECT_EQ(AVMEDIA_TYPE_AUDIO, codec_parameters->codec_type);
- AudioDecoderConfig audio_config;
- ASSERT_TRUE(AVStreamToAudioDecoderConfig(stream, &audio_config));
- EXPECT_EQ(AudioCodec::kOpus, audio_config.codec());
- EXPECT_EQ(CHANNEL_LAYOUT_QUAD, audio_config.channel_layout());
- EXPECT_EQ(4, audio_config.channels());
- }
- TEST_F(FFmpegCommonTest, AVStreamToAudioDecoderConfig_OpusAmbisonics_11ch) {
- base::MemoryMappedFile file;
- ASSERT_TRUE(file.Initialize(
- GetTestDataFilePath("bear-opus-end-trimming-11ch-channelmapping2.webm")));
- InMemoryUrlProtocol protocol(file.data(), file.length(), false);
- FFmpegGlue glue(&protocol);
- ASSERT_TRUE(glue.OpenContext());
- AVFormatContext* format_context = glue.format_context();
- EXPECT_EQ(static_cast<unsigned int>(1), format_context->nb_streams);
- AVStream* stream = format_context->streams[0];
- AVCodecParameters* codec_parameters = stream->codecpar;
- EXPECT_EQ(AVMEDIA_TYPE_AUDIO, codec_parameters->codec_type);
- AudioDecoderConfig audio_config;
- ASSERT_TRUE(AVStreamToAudioDecoderConfig(stream, &audio_config));
- EXPECT_EQ(AudioCodec::kOpus, audio_config.codec());
- EXPECT_EQ(CHANNEL_LAYOUT_DISCRETE, audio_config.channel_layout());
- EXPECT_EQ(11, audio_config.channels());
- }
- TEST_F(FFmpegCommonTest, AVStreamToAudioDecoderConfig_9ch_wav) {
- base::MemoryMappedFile file;
- ASSERT_TRUE(file.Initialize(GetTestDataFilePath("9ch.wav")));
- InMemoryUrlProtocol protocol(file.data(), file.length(), false);
- FFmpegGlue glue(&protocol);
- ASSERT_TRUE(glue.OpenContext());
- AVFormatContext* format_context = glue.format_context();
- EXPECT_EQ(static_cast<unsigned int>(1), format_context->nb_streams);
- AVStream* stream = format_context->streams[0];
- AVCodecParameters* codec_parameters = stream->codecpar;
- EXPECT_EQ(AVMEDIA_TYPE_AUDIO, codec_parameters->codec_type);
- AudioDecoderConfig audio_config;
- ASSERT_TRUE(AVStreamToAudioDecoderConfig(stream, &audio_config));
- EXPECT_EQ(AudioCodec::kPCM, audio_config.codec());
- EXPECT_EQ(CHANNEL_LAYOUT_DISCRETE, audio_config.channel_layout());
- EXPECT_EQ(9, audio_config.channels());
- }
- TEST_F(FFmpegCommonTest, TimeBaseConversions) {
- const int64_t test_data[][5] = {
- {1, 2, 1, 500000, 1}, {1, 3, 1, 333333, 1}, {1, 3, 2, 666667, 2},
- };
- for (size_t i = 0; i < std::size(test_data); ++i) {
- SCOPED_TRACE(i);
- AVRational time_base;
- time_base.num = static_cast<int>(test_data[i][0]);
- time_base.den = static_cast<int>(test_data[i][1]);
- base::TimeDelta time_delta =
- ConvertFromTimeBase(time_base, test_data[i][2]);
- EXPECT_EQ(time_delta.InMicroseconds(), test_data[i][3]);
- EXPECT_EQ(ConvertToTimeBase(time_base, time_delta), test_data[i][4]);
- }
- }
- TEST_F(FFmpegCommonTest, VerifyFormatSizes) {
- for (AVSampleFormat format = AV_SAMPLE_FMT_NONE;
- format < AV_SAMPLE_FMT_NB;
- format = static_cast<AVSampleFormat>(format + 1)) {
- std::vector<AVCodecID> codec_ids(1, AV_CODEC_ID_NONE);
- if (format == AV_SAMPLE_FMT_S32)
- codec_ids.push_back(AV_CODEC_ID_PCM_S24LE);
- for (const auto& codec_id : codec_ids) {
- SampleFormat sample_format =
- AVSampleFormatToSampleFormat(format, codec_id);
- if (sample_format == kUnknownSampleFormat) {
- // This format not supported, so skip it.
- continue;
- }
- // Have FFMpeg compute the size of a buffer of 1 channel / 1 frame
- // with 1 byte alignment to make sure the sizes match.
- int single_buffer_size =
- av_samples_get_buffer_size(NULL, 1, 1, format, 1);
- int bytes_per_channel = SampleFormatToBytesPerChannel(sample_format);
- EXPECT_EQ(bytes_per_channel, single_buffer_size);
- }
- }
- }
- // Verifies there are no collisions of the codec name hashes used for UMA. Also
- // includes code for updating the histograms XML.
- TEST_F(FFmpegCommonTest, VerifyUmaCodecHashes) {
- const AVCodecDescriptor* desc = avcodec_descriptor_next(nullptr);
- std::map<int32_t, const char*> sorted_hashes;
- while (desc) {
- const int32_t hash = HashCodecName(desc->name);
- // Ensure there are no collisions.
- ASSERT_TRUE(sorted_hashes.find(hash) == sorted_hashes.end());
- sorted_hashes[hash] = desc->name;
- desc = avcodec_descriptor_next(desc);
- }
- // Add a none entry for when no codec is detected.
- static const char kUnknownCodec[] = "none";
- const int32_t hash = HashCodecName(kUnknownCodec);
- ASSERT_TRUE(sorted_hashes.find(hash) == sorted_hashes.end());
- sorted_hashes[hash] = kUnknownCodec;
- // Uncomment the following lines to generate the "FFmpegCodecHashes" enum for
- // usage in the histogram metrics file. While it regenerates *ALL* values, it
- // should only be used to *ADD* values to histograms file. Never delete any
- // values; diff should verify.
- #if 0
- static const std::vector<std::pair<std::string, int32_t>> kDeprecatedHashes =
- {
- {"brender_pix_deprecated", -1866047250},
- {"adpcm_vima_deprecated", -1782518388},
- {"pcm_s32le_planar_deprecated", -1328796639},
- {"webp_deprecated", -993429906},
- {"paf_video_deprecated", -881893142},
- {"vima_deprecated", -816209197},
- {"iff_byterun1", -777478450},
- {"paf_audio_deprecated", -630356729},
- {"exr_deprecated", -418117523},
- {"hevc_deprecated", -414733739},
- {"vp7_deprecated", -197551526},
- {"escape130_deprecated", 73149662},
- {"tak_deprecated", 1041617024},
- {"opus_deprecated", 1165132763},
- {"g2m_deprecated", 1194572884},
- {"pcm_s24le_planar_deprecated", 1535518292},
- {"sanm_deprecated", 2047102762},
- {"mpegvideo_xvmc_deprecated", 1550758811},
- {"voxware_deprecated", 1656834662}
- };
- for (auto& kv : kDeprecatedHashes)
- sorted_hashes[kv.second] = kv.first.c_str();
- printf("<enum name=\"FFmpegCodecHashes\">\n");
- for (const auto& kv : sorted_hashes)
- printf(" <int value=\"%d\" label=\"%s\"/>\n", kv.first, kv.second);
- 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;
- }
- }
- }
- #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;
- }
- }
- }
- 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;
- }
- }
- }
- #endif // BUILDFLAG(ENABLE_PLATFORM_HEVC)
- #endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
- // Verifies that the HDR Metadata and VideoColorSpace are correctly parsed.
- TEST_F(FFmpegCommonTest, VerifyHDRMetadataAndColorSpaceInfo) {
- // Open a file to get a real AVStreams from FFmpeg.
- base::MemoryMappedFile file;
- ASSERT_TRUE(file.Initialize(GetTestDataFilePath("colour.webm")));
- InMemoryUrlProtocol protocol(file.data(), file.length(), false);
- FFmpegGlue glue(&protocol);
- ASSERT_TRUE(glue.OpenContext());
- AVFormatContext* format_context = glue.format_context();
- ASSERT_EQ(format_context->nb_streams, 1u);
- AVStream* stream = format_context->streams[0];
- AVCodecParameters* codec_parameters = stream->codecpar;
- AVMediaType codec_type = codec_parameters->codec_type;
- ASSERT_EQ(codec_type, AVMEDIA_TYPE_VIDEO);
- VideoDecoderConfig video_config;
- EXPECT_TRUE(AVStreamToVideoDecoderConfig(stream, &video_config));
- ASSERT_TRUE(video_config.hdr_metadata().has_value());
- EXPECT_EQ(30.0,
- video_config.hdr_metadata()->color_volume_metadata.luminance_min);
- EXPECT_EQ(40.0,
- video_config.hdr_metadata()->color_volume_metadata.luminance_max);
- EXPECT_EQ(gfx::PointF(0.1, 0.2),
- video_config.hdr_metadata()->color_volume_metadata.primary_r);
- EXPECT_EQ(gfx::PointF(0.1, 0.2),
- video_config.hdr_metadata()->color_volume_metadata.primary_g);
- EXPECT_EQ(gfx::PointF(0.1, 0.2),
- video_config.hdr_metadata()->color_volume_metadata.primary_b);
- EXPECT_EQ(gfx::PointF(0.1, 0.2),
- video_config.hdr_metadata()->color_volume_metadata.white_point);
- EXPECT_EQ(VideoColorSpace(VideoColorSpace::PrimaryID::SMPTEST428_1,
- VideoColorSpace::TransferID::LOG,
- VideoColorSpace::MatrixID::RGB,
- gfx::ColorSpace::RangeID::FULL),
- video_config.color_space_info());
- }
- } // namespace media
|