ffmpeg_common_unittest.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. // Copyright (c) 2011 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "media/ffmpeg/ffmpeg_common.h"
  5. #include <stddef.h>
  6. #include <stdint.h>
  7. #include <cstring>
  8. #include "base/bind.h"
  9. #include "base/files/memory_mapped_file.h"
  10. #include "base/logging.h"
  11. #include "media/base/audio_decoder_config.h"
  12. #include "media/base/media.h"
  13. #include "media/base/media_util.h"
  14. #include "media/base/test_data_util.h"
  15. #include "media/base/video_decoder_config.h"
  16. #include "media/filters/ffmpeg_glue.h"
  17. #include "media/filters/in_memory_url_protocol.h"
  18. #include "testing/gtest/include/gtest/gtest.h"
  19. namespace media {
  20. class FFmpegCommonTest : public testing::Test {
  21. public:
  22. FFmpegCommonTest() {}
  23. ~FFmpegCommonTest() override = default;
  24. };
  25. uint8_t kExtraData[5] = {0x00, 0x01, 0x02, 0x03, 0x04};
  26. template <typename T>
  27. void TestConfigConvertExtraData(
  28. AVStream* stream,
  29. T* decoder_config,
  30. const base::RepeatingCallback<bool(const AVStream*, T*)>& converter_fn) {
  31. // Should initially convert.
  32. EXPECT_TRUE(converter_fn.Run(stream, decoder_config));
  33. // Store orig to let FFmpeg free whatever it allocated.
  34. AVCodecParameters* codec_parameters = stream->codecpar;
  35. uint8_t* orig_extradata = codec_parameters->extradata;
  36. int orig_extradata_size = codec_parameters->extradata_size;
  37. // Valid combination: extra_data = nullptr && size = 0.
  38. codec_parameters->extradata = nullptr;
  39. codec_parameters->extradata_size = 0;
  40. EXPECT_TRUE(converter_fn.Run(stream, decoder_config));
  41. EXPECT_EQ(static_cast<size_t>(codec_parameters->extradata_size),
  42. decoder_config->extra_data().size());
  43. // Valid combination: extra_data = non-nullptr && size > 0.
  44. codec_parameters->extradata = &kExtraData[0];
  45. codec_parameters->extradata_size = std::size(kExtraData);
  46. EXPECT_TRUE(converter_fn.Run(stream, decoder_config));
  47. EXPECT_EQ(static_cast<size_t>(codec_parameters->extradata_size),
  48. decoder_config->extra_data().size());
  49. EXPECT_EQ(
  50. 0, memcmp(codec_parameters->extradata, &decoder_config->extra_data()[0],
  51. decoder_config->extra_data().size()));
  52. // Possible combination: extra_data = nullptr && size != 0, but the converter
  53. // function considers this valid and having no extra_data, due to behavior of
  54. // avcodec_parameters_to_context().
  55. codec_parameters->extradata = nullptr;
  56. codec_parameters->extradata_size = 10;
  57. EXPECT_TRUE(converter_fn.Run(stream, decoder_config));
  58. EXPECT_EQ(0UL, decoder_config->extra_data().size());
  59. // Invalid combination: extra_data = non-nullptr && size = 0.
  60. codec_parameters->extradata = &kExtraData[0];
  61. codec_parameters->extradata_size = 0;
  62. EXPECT_FALSE(converter_fn.Run(stream, decoder_config));
  63. // Restore orig values for sane cleanup.
  64. codec_parameters->extradata = orig_extradata;
  65. codec_parameters->extradata_size = orig_extradata_size;
  66. }
  67. TEST_F(FFmpegCommonTest, AVStreamToDecoderConfig) {
  68. // Open a file to get a real AVStreams from FFmpeg.
  69. base::MemoryMappedFile file;
  70. ASSERT_TRUE(file.Initialize(GetTestDataFilePath("bear-320x240.webm")));
  71. InMemoryUrlProtocol protocol(file.data(), file.length(), false);
  72. FFmpegGlue glue(&protocol);
  73. ASSERT_TRUE(glue.OpenContext());
  74. AVFormatContext* format_context = glue.format_context();
  75. // Find the audio and video streams and test valid and invalid combinations
  76. // for extradata and extradata_size.
  77. bool found_audio = false;
  78. bool found_video = false;
  79. for (size_t i = 0;
  80. i < format_context->nb_streams && (!found_audio || !found_video);
  81. ++i) {
  82. AVStream* stream = format_context->streams[i];
  83. AVCodecParameters* codec_parameters = stream->codecpar;
  84. AVMediaType codec_type = codec_parameters->codec_type;
  85. if (codec_type == AVMEDIA_TYPE_AUDIO) {
  86. if (found_audio)
  87. continue;
  88. found_audio = true;
  89. AudioDecoderConfig audio_config;
  90. TestConfigConvertExtraData(
  91. stream, &audio_config,
  92. base::BindRepeating(&AVStreamToAudioDecoderConfig));
  93. } else if (codec_type == AVMEDIA_TYPE_VIDEO) {
  94. if (found_video)
  95. continue;
  96. found_video = true;
  97. VideoDecoderConfig video_config;
  98. TestConfigConvertExtraData(
  99. stream, &video_config,
  100. base::BindRepeating(&AVStreamToVideoDecoderConfig));
  101. } else {
  102. // Only process audio/video.
  103. continue;
  104. }
  105. }
  106. ASSERT_TRUE(found_audio);
  107. ASSERT_TRUE(found_video);
  108. }
  109. TEST_F(FFmpegCommonTest, AVStreamToAudioDecoderConfig_OpusAmbisonics_4ch) {
  110. base::MemoryMappedFile file;
  111. ASSERT_TRUE(file.Initialize(
  112. GetTestDataFilePath("bear-opus-end-trimming-4ch-channelmapping2.webm")));
  113. InMemoryUrlProtocol protocol(file.data(), file.length(), false);
  114. FFmpegGlue glue(&protocol);
  115. ASSERT_TRUE(glue.OpenContext());
  116. AVFormatContext* format_context = glue.format_context();
  117. EXPECT_EQ(static_cast<unsigned int>(1), format_context->nb_streams);
  118. AVStream* stream = format_context->streams[0];
  119. AVCodecParameters* codec_parameters = stream->codecpar;
  120. EXPECT_EQ(AVMEDIA_TYPE_AUDIO, codec_parameters->codec_type);
  121. AudioDecoderConfig audio_config;
  122. ASSERT_TRUE(AVStreamToAudioDecoderConfig(stream, &audio_config));
  123. EXPECT_EQ(AudioCodec::kOpus, audio_config.codec());
  124. EXPECT_EQ(CHANNEL_LAYOUT_QUAD, audio_config.channel_layout());
  125. EXPECT_EQ(4, audio_config.channels());
  126. }
  127. TEST_F(FFmpegCommonTest, AVStreamToAudioDecoderConfig_OpusAmbisonics_11ch) {
  128. base::MemoryMappedFile file;
  129. ASSERT_TRUE(file.Initialize(
  130. GetTestDataFilePath("bear-opus-end-trimming-11ch-channelmapping2.webm")));
  131. InMemoryUrlProtocol protocol(file.data(), file.length(), false);
  132. FFmpegGlue glue(&protocol);
  133. ASSERT_TRUE(glue.OpenContext());
  134. AVFormatContext* format_context = glue.format_context();
  135. EXPECT_EQ(static_cast<unsigned int>(1), format_context->nb_streams);
  136. AVStream* stream = format_context->streams[0];
  137. AVCodecParameters* codec_parameters = stream->codecpar;
  138. EXPECT_EQ(AVMEDIA_TYPE_AUDIO, codec_parameters->codec_type);
  139. AudioDecoderConfig audio_config;
  140. ASSERT_TRUE(AVStreamToAudioDecoderConfig(stream, &audio_config));
  141. EXPECT_EQ(AudioCodec::kOpus, audio_config.codec());
  142. EXPECT_EQ(CHANNEL_LAYOUT_DISCRETE, audio_config.channel_layout());
  143. EXPECT_EQ(11, audio_config.channels());
  144. }
  145. TEST_F(FFmpegCommonTest, AVStreamToAudioDecoderConfig_9ch_wav) {
  146. base::MemoryMappedFile file;
  147. ASSERT_TRUE(file.Initialize(GetTestDataFilePath("9ch.wav")));
  148. InMemoryUrlProtocol protocol(file.data(), file.length(), false);
  149. FFmpegGlue glue(&protocol);
  150. ASSERT_TRUE(glue.OpenContext());
  151. AVFormatContext* format_context = glue.format_context();
  152. EXPECT_EQ(static_cast<unsigned int>(1), format_context->nb_streams);
  153. AVStream* stream = format_context->streams[0];
  154. AVCodecParameters* codec_parameters = stream->codecpar;
  155. EXPECT_EQ(AVMEDIA_TYPE_AUDIO, codec_parameters->codec_type);
  156. AudioDecoderConfig audio_config;
  157. ASSERT_TRUE(AVStreamToAudioDecoderConfig(stream, &audio_config));
  158. EXPECT_EQ(AudioCodec::kPCM, audio_config.codec());
  159. EXPECT_EQ(CHANNEL_LAYOUT_DISCRETE, audio_config.channel_layout());
  160. EXPECT_EQ(9, audio_config.channels());
  161. }
  162. TEST_F(FFmpegCommonTest, TimeBaseConversions) {
  163. const int64_t test_data[][5] = {
  164. {1, 2, 1, 500000, 1}, {1, 3, 1, 333333, 1}, {1, 3, 2, 666667, 2},
  165. };
  166. for (size_t i = 0; i < std::size(test_data); ++i) {
  167. SCOPED_TRACE(i);
  168. AVRational time_base;
  169. time_base.num = static_cast<int>(test_data[i][0]);
  170. time_base.den = static_cast<int>(test_data[i][1]);
  171. base::TimeDelta time_delta =
  172. ConvertFromTimeBase(time_base, test_data[i][2]);
  173. EXPECT_EQ(time_delta.InMicroseconds(), test_data[i][3]);
  174. EXPECT_EQ(ConvertToTimeBase(time_base, time_delta), test_data[i][4]);
  175. }
  176. }
  177. TEST_F(FFmpegCommonTest, VerifyFormatSizes) {
  178. for (AVSampleFormat format = AV_SAMPLE_FMT_NONE;
  179. format < AV_SAMPLE_FMT_NB;
  180. format = static_cast<AVSampleFormat>(format + 1)) {
  181. std::vector<AVCodecID> codec_ids(1, AV_CODEC_ID_NONE);
  182. if (format == AV_SAMPLE_FMT_S32)
  183. codec_ids.push_back(AV_CODEC_ID_PCM_S24LE);
  184. for (const auto& codec_id : codec_ids) {
  185. SampleFormat sample_format =
  186. AVSampleFormatToSampleFormat(format, codec_id);
  187. if (sample_format == kUnknownSampleFormat) {
  188. // This format not supported, so skip it.
  189. continue;
  190. }
  191. // Have FFMpeg compute the size of a buffer of 1 channel / 1 frame
  192. // with 1 byte alignment to make sure the sizes match.
  193. int single_buffer_size =
  194. av_samples_get_buffer_size(NULL, 1, 1, format, 1);
  195. int bytes_per_channel = SampleFormatToBytesPerChannel(sample_format);
  196. EXPECT_EQ(bytes_per_channel, single_buffer_size);
  197. }
  198. }
  199. }
  200. // Verifies there are no collisions of the codec name hashes used for UMA. Also
  201. // includes code for updating the histograms XML.
  202. TEST_F(FFmpegCommonTest, VerifyUmaCodecHashes) {
  203. const AVCodecDescriptor* desc = avcodec_descriptor_next(nullptr);
  204. std::map<int32_t, const char*> sorted_hashes;
  205. while (desc) {
  206. const int32_t hash = HashCodecName(desc->name);
  207. // Ensure there are no collisions.
  208. ASSERT_TRUE(sorted_hashes.find(hash) == sorted_hashes.end());
  209. sorted_hashes[hash] = desc->name;
  210. desc = avcodec_descriptor_next(desc);
  211. }
  212. // Add a none entry for when no codec is detected.
  213. static const char kUnknownCodec[] = "none";
  214. const int32_t hash = HashCodecName(kUnknownCodec);
  215. ASSERT_TRUE(sorted_hashes.find(hash) == sorted_hashes.end());
  216. sorted_hashes[hash] = kUnknownCodec;
  217. // Uncomment the following lines to generate the "FFmpegCodecHashes" enum for
  218. // usage in the histogram metrics file. While it regenerates *ALL* values, it
  219. // should only be used to *ADD* values to histograms file. Never delete any
  220. // values; diff should verify.
  221. #if 0
  222. static const std::vector<std::pair<std::string, int32_t>> kDeprecatedHashes =
  223. {
  224. {"brender_pix_deprecated", -1866047250},
  225. {"adpcm_vima_deprecated", -1782518388},
  226. {"pcm_s32le_planar_deprecated", -1328796639},
  227. {"webp_deprecated", -993429906},
  228. {"paf_video_deprecated", -881893142},
  229. {"vima_deprecated", -816209197},
  230. {"iff_byterun1", -777478450},
  231. {"paf_audio_deprecated", -630356729},
  232. {"exr_deprecated", -418117523},
  233. {"hevc_deprecated", -414733739},
  234. {"vp7_deprecated", -197551526},
  235. {"escape130_deprecated", 73149662},
  236. {"tak_deprecated", 1041617024},
  237. {"opus_deprecated", 1165132763},
  238. {"g2m_deprecated", 1194572884},
  239. {"pcm_s24le_planar_deprecated", 1535518292},
  240. {"sanm_deprecated", 2047102762},
  241. {"mpegvideo_xvmc_deprecated", 1550758811},
  242. {"voxware_deprecated", 1656834662}
  243. };
  244. for (auto& kv : kDeprecatedHashes)
  245. sorted_hashes[kv.second] = kv.first.c_str();
  246. printf("<enum name=\"FFmpegCodecHashes\">\n");
  247. for (const auto& kv : sorted_hashes)
  248. printf(" <int value=\"%d\" label=\"%s\"/>\n", kv.first, kv.second);
  249. printf("</enum>\n");
  250. #endif
  251. }
  252. #if BUILDFLAG(USE_PROPRIETARY_CODECS)
  253. TEST_F(FFmpegCommonTest, VerifyH264Profile) {
  254. // Open a file to get a real AVStreams from FFmpeg.
  255. base::MemoryMappedFile file;
  256. ASSERT_TRUE(file.Initialize(GetTestDataFilePath("bear-1280x720.mp4")));
  257. InMemoryUrlProtocol protocol(file.data(), file.length(), false);
  258. FFmpegGlue glue(&protocol);
  259. ASSERT_TRUE(glue.OpenContext());
  260. AVFormatContext* format_context = glue.format_context();
  261. for (size_t i = 0; i < format_context->nb_streams; ++i) {
  262. AVStream* stream = format_context->streams[i];
  263. AVCodecParameters* codec_parameters = stream->codecpar;
  264. AVMediaType codec_type = codec_parameters->codec_type;
  265. if (codec_type == AVMEDIA_TYPE_VIDEO) {
  266. VideoDecoderConfig video_config;
  267. EXPECT_TRUE(AVStreamToVideoDecoderConfig(stream, &video_config));
  268. EXPECT_EQ(H264PROFILE_HIGH, video_config.profile());
  269. } else {
  270. // Only process video.
  271. continue;
  272. }
  273. }
  274. }
  275. #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
  276. TEST_F(FFmpegCommonTest, VerifyH265MainProfile) {
  277. // Open a file to get a real AVStreams from FFmpeg.
  278. base::MemoryMappedFile file;
  279. ASSERT_TRUE(file.Initialize(GetTestDataFilePath("bear-1280x720-hevc.mp4")));
  280. InMemoryUrlProtocol protocol(file.data(), file.length(), false);
  281. FFmpegGlue glue(&protocol);
  282. ASSERT_TRUE(glue.OpenContext());
  283. AVFormatContext* format_context = glue.format_context();
  284. for (size_t i = 0; i < format_context->nb_streams; ++i) {
  285. AVStream* stream = format_context->streams[i];
  286. AVCodecParameters* codec_parameters = stream->codecpar;
  287. AVMediaType codec_type = codec_parameters->codec_type;
  288. if (codec_type == AVMEDIA_TYPE_VIDEO) {
  289. VideoDecoderConfig video_config;
  290. EXPECT_TRUE(AVStreamToVideoDecoderConfig(stream, &video_config));
  291. EXPECT_EQ(HEVCPROFILE_MAIN, video_config.profile());
  292. } else {
  293. // Only process video.
  294. continue;
  295. }
  296. }
  297. }
  298. TEST_F(FFmpegCommonTest, VerifyH265Main10Profile) {
  299. // Open a file to get a real AVStreams from FFmpeg.
  300. base::MemoryMappedFile file;
  301. ASSERT_TRUE(
  302. file.Initialize(GetTestDataFilePath("bear-1280x720-hevc-10bit.mp4")));
  303. InMemoryUrlProtocol protocol(file.data(), file.length(), false);
  304. FFmpegGlue glue(&protocol);
  305. ASSERT_TRUE(glue.OpenContext());
  306. AVFormatContext* format_context = glue.format_context();
  307. for (size_t i = 0; i < format_context->nb_streams; ++i) {
  308. AVStream* stream = format_context->streams[i];
  309. AVCodecParameters* codec_parameters = stream->codecpar;
  310. AVMediaType codec_type = codec_parameters->codec_type;
  311. if (codec_type == AVMEDIA_TYPE_VIDEO) {
  312. VideoDecoderConfig video_config;
  313. EXPECT_TRUE(AVStreamToVideoDecoderConfig(stream, &video_config));
  314. EXPECT_EQ(HEVCPROFILE_MAIN10, video_config.profile());
  315. } else {
  316. // Only process video.
  317. continue;
  318. }
  319. }
  320. }
  321. #endif // BUILDFLAG(ENABLE_PLATFORM_HEVC)
  322. #endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
  323. // Verifies that the HDR Metadata and VideoColorSpace are correctly parsed.
  324. TEST_F(FFmpegCommonTest, VerifyHDRMetadataAndColorSpaceInfo) {
  325. // Open a file to get a real AVStreams from FFmpeg.
  326. base::MemoryMappedFile file;
  327. ASSERT_TRUE(file.Initialize(GetTestDataFilePath("colour.webm")));
  328. InMemoryUrlProtocol protocol(file.data(), file.length(), false);
  329. FFmpegGlue glue(&protocol);
  330. ASSERT_TRUE(glue.OpenContext());
  331. AVFormatContext* format_context = glue.format_context();
  332. ASSERT_EQ(format_context->nb_streams, 1u);
  333. AVStream* stream = format_context->streams[0];
  334. AVCodecParameters* codec_parameters = stream->codecpar;
  335. AVMediaType codec_type = codec_parameters->codec_type;
  336. ASSERT_EQ(codec_type, AVMEDIA_TYPE_VIDEO);
  337. VideoDecoderConfig video_config;
  338. EXPECT_TRUE(AVStreamToVideoDecoderConfig(stream, &video_config));
  339. ASSERT_TRUE(video_config.hdr_metadata().has_value());
  340. EXPECT_EQ(30.0,
  341. video_config.hdr_metadata()->color_volume_metadata.luminance_min);
  342. EXPECT_EQ(40.0,
  343. video_config.hdr_metadata()->color_volume_metadata.luminance_max);
  344. EXPECT_EQ(gfx::PointF(0.1, 0.2),
  345. video_config.hdr_metadata()->color_volume_metadata.primary_r);
  346. EXPECT_EQ(gfx::PointF(0.1, 0.2),
  347. video_config.hdr_metadata()->color_volume_metadata.primary_g);
  348. EXPECT_EQ(gfx::PointF(0.1, 0.2),
  349. video_config.hdr_metadata()->color_volume_metadata.primary_b);
  350. EXPECT_EQ(gfx::PointF(0.1, 0.2),
  351. video_config.hdr_metadata()->color_volume_metadata.white_point);
  352. EXPECT_EQ(VideoColorSpace(VideoColorSpace::PrimaryID::SMPTEST428_1,
  353. VideoColorSpace::TransferID::LOG,
  354. VideoColorSpace::MatrixID::RGB,
  355. gfx::ColorSpace::RangeID::FULL),
  356. video_config.color_space_info());
  357. }
  358. } // namespace media