| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media/filters/h265_to_annex_b_bitstream_converter.h" |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| |
| #include "media/formats/mp4/box_definitions.h" |
| #include "media/formats/mp4/hevc.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace media { |
| |
| class H265ToAnnexBBitstreamConverterTest : public testing::Test { |
| public: |
| H265ToAnnexBBitstreamConverterTest( |
| const H265ToAnnexBBitstreamConverterTest&) = delete; |
| H265ToAnnexBBitstreamConverterTest& operator=( |
| const H265ToAnnexBBitstreamConverterTest&) = delete; |
| |
| protected: |
| H265ToAnnexBBitstreamConverterTest() = default; |
| |
| ~H265ToAnnexBBitstreamConverterTest() override = default; |
| |
| protected: |
| mp4::HEVCDecoderConfigurationRecord hevc_config_; |
| }; |
| |
| static const uint8_t kHeaderDataOkWithFieldLen4[] = { |
| 0x01, 0x01, 0x60, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x96, 0xf0, 0x00, 0xfc, 0xfd, 0xf8, 0xf8, 0x00, 0x00, 0x0f, |
| 0x03, 0xa0, 0x00, 0x01, 0x00, 0x18, 0x40, 0x01, 0x0c, 0x01, 0xff, |
| 0xff, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x03, |
| 0x00, 0x00, 0x03, 0x00, 0x96, 0x9d, 0xc0, 0x90, 0xa1, 0x00, 0x01, |
| 0x00, 0x29, 0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, |
| 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x96, 0xa0, 0x03, |
| 0xc0, 0x80, 0x10, 0xe5, 0x96, 0x77, 0x92, 0x46, 0xda, 0xf0, 0x10, |
| 0x10, 0x00, 0x00, 0x3e, 0x80, 0x00, 0x06, 0x1a, 0x80, 0x80, 0xa2, |
| 0x00, 0x01, 0x00, 0x06, 0x44, 0x01, 0xc1, 0x73, 0xd1, 0x89}; |
| |
| static const uint8_t kPacketDataOkWithFieldLen4[] = { |
| 0x00, 0x00, 0x00, 0x2d, 0x00, 0x01, 0xe0, 0xa6, 0xf5, 0xd7, |
| 0xd2, 0x24, 0x0a, 0x19, 0x1a, 0xa0, 0xdc, 0x8c, 0x68, 0x5e, |
| 0x35, 0x20, 0x40, 0x64, 0x1c, 0x86, 0x81, 0x8a, 0x25, 0x5d, |
| 0x65, 0x6c, 0xfe, 0x80, 0x7a, 0xe3, 0xf4, 0x63, 0xe1, 0xcf, |
| 0xf2, 0x6e, 0x92, 0x1e, 0xff, 0xd3, 0x65, 0xd9, 0x60}; |
| |
| TEST_F(H265ToAnnexBBitstreamConverterTest, Success) { |
| // Initialize converter. |
| std::unique_ptr<uint8_t[]> output; |
| H265ToAnnexBBitstreamConverter converter; |
| |
| // Parse the headers. |
| EXPECT_TRUE(converter.ParseConfiguration(kHeaderDataOkWithFieldLen4, |
| sizeof(kHeaderDataOkWithFieldLen4), |
| &hevc_config_)); |
| uint32_t config_size = converter.GetConfigSize(hevc_config_); |
| EXPECT_GT(config_size, 0U); |
| |
| // Go on with converting the headers. |
| output.reset(new uint8_t[config_size]); |
| EXPECT_TRUE(output.get() != nullptr); |
| EXPECT_TRUE(converter.ConvertHEVCDecoderConfigToByteStream( |
| hevc_config_, output.get(), &config_size)); |
| |
| // Calculate buffer size for actual NAL unit. |
| uint32_t output_size = converter.CalculateNeededOutputBufferSize( |
| kPacketDataOkWithFieldLen4, sizeof(kPacketDataOkWithFieldLen4), |
| &hevc_config_); |
| EXPECT_GT(output_size, 0U); |
| output.reset(new uint8_t[output_size]); |
| EXPECT_TRUE(output.get() != nullptr); |
| |
| uint32_t output_size_left_for_nal_unit = output_size; |
| // Do the conversion for actual NAL unit. |
| EXPECT_TRUE(converter.ConvertNalUnitStreamToByteStream( |
| kPacketDataOkWithFieldLen4, sizeof(kPacketDataOkWithFieldLen4), |
| &hevc_config_, output.get(), &output_size_left_for_nal_unit)); |
| } |
| |
| TEST_F(H265ToAnnexBBitstreamConverterTest, FailureHeaderBufferOverflow) { |
| // Initialize converter |
| H265ToAnnexBBitstreamConverter converter; |
| |
| // Simulate 10 nalu_array HEVCDecoderConfigurationRecord, |
| // which would extend beyond the buffer. |
| uint8_t corrupted_header[sizeof(kHeaderDataOkWithFieldLen4)]; |
| memcpy(corrupted_header, kHeaderDataOkWithFieldLen4, |
| sizeof(kHeaderDataOkWithFieldLen4)); |
| // 23th byte contain the number of nalu arrays |
| corrupted_header[22] = corrupted_header[22] | 0xA; |
| |
| // Parse the headers |
| EXPECT_FALSE(converter.ParseConfiguration( |
| corrupted_header, sizeof(corrupted_header), &hevc_config_)); |
| } |
| |
| TEST_F(H265ToAnnexBBitstreamConverterTest, FailureNalUnitBreakage) { |
| // Initialize converter. |
| std::unique_ptr<uint8_t[]> output; |
| H265ToAnnexBBitstreamConverter converter; |
| |
| // Parse the headers. |
| EXPECT_TRUE(converter.ParseConfiguration(kHeaderDataOkWithFieldLen4, |
| sizeof(kHeaderDataOkWithFieldLen4), |
| &hevc_config_)); |
| uint32_t config_size = converter.GetConfigSize(hevc_config_); |
| EXPECT_GT(config_size, 0U); |
| |
| // Go on with converting the headers. |
| output.reset(new uint8_t[config_size]); |
| EXPECT_TRUE(output.get() != nullptr); |
| EXPECT_TRUE(converter.ConvertHEVCDecoderConfigToByteStream( |
| hevc_config_, output.get(), &config_size)); |
| |
| // Simulate NAL unit broken in middle by writing only some of the data. |
| uint8_t corrupted_nal_unit[sizeof(kPacketDataOkWithFieldLen4) - 30]; |
| memcpy(corrupted_nal_unit, kPacketDataOkWithFieldLen4, |
| sizeof(kPacketDataOkWithFieldLen4) - 30); |
| |
| // Calculate buffer size for actual NAL unit, should return 0 because of |
| // incomplete input buffer. |
| uint32_t output_size = converter.CalculateNeededOutputBufferSize( |
| corrupted_nal_unit, sizeof(corrupted_nal_unit), &hevc_config_); |
| EXPECT_EQ(output_size, 0U); |
| |
| // Ignore the error and try to go on with conversion simulating wrong usage. |
| output_size = sizeof(kPacketDataOkWithFieldLen4); |
| output.reset(new uint8_t[output_size]); |
| EXPECT_TRUE(output.get() != nullptr); |
| |
| uint32_t output_size_left_for_nal_unit = output_size; |
| // Do the conversion for actual NAL unit, expecting failure. |
| EXPECT_FALSE(converter.ConvertNalUnitStreamToByteStream( |
| corrupted_nal_unit, sizeof(corrupted_nal_unit), &hevc_config_, |
| output.get(), &output_size_left_for_nal_unit)); |
| EXPECT_EQ(output_size_left_for_nal_unit, 0U); |
| } |
| |
| TEST_F(H265ToAnnexBBitstreamConverterTest, FailureTooSmallOutputBuffer) { |
| // Initialize converter. |
| std::unique_ptr<uint8_t[]> output; |
| H265ToAnnexBBitstreamConverter converter; |
| |
| // Parse the headers. |
| EXPECT_TRUE(converter.ParseConfiguration(kHeaderDataOkWithFieldLen4, |
| sizeof(kHeaderDataOkWithFieldLen4), |
| &hevc_config_)); |
| uint32_t config_size = converter.GetConfigSize(hevc_config_); |
| EXPECT_GT(config_size, 0U); |
| uint32_t real_config_size = config_size; |
| |
| // Go on with converting the headers with too small buffer. |
| config_size -= 10; |
| output.reset(new uint8_t[config_size]); |
| EXPECT_TRUE(output.get() != nullptr); |
| EXPECT_FALSE(converter.ConvertHEVCDecoderConfigToByteStream( |
| hevc_config_, output.get(), &config_size)); |
| EXPECT_EQ(config_size, 0U); |
| |
| // Still too small (but only 1 byte short). |
| config_size = real_config_size - 1; |
| output.reset(new uint8_t[config_size]); |
| EXPECT_TRUE(output.get() != nullptr); |
| EXPECT_FALSE(converter.ConvertHEVCDecoderConfigToByteStream( |
| hevc_config_, output.get(), &config_size)); |
| EXPECT_EQ(config_size, 0U); |
| |
| // Finally, retry with valid buffer. |
| config_size = real_config_size; |
| output.reset(new uint8_t[config_size]); |
| EXPECT_TRUE(output.get() != nullptr); |
| EXPECT_TRUE(converter.ConvertHEVCDecoderConfigToByteStream( |
| hevc_config_, output.get(), &config_size)); |
| |
| // Calculate buffer size for actual NAL unit. |
| uint32_t output_size = converter.CalculateNeededOutputBufferSize( |
| kPacketDataOkWithFieldLen4, sizeof(kPacketDataOkWithFieldLen4), |
| &hevc_config_); |
| EXPECT_GT(output_size, 0U); |
| // Simulate too small output buffer. |
| output_size -= 1; |
| output.reset(new uint8_t[output_size]); |
| EXPECT_TRUE(output.get() != nullptr); |
| |
| uint32_t output_size_left_for_nal_unit = output_size; |
| // Do the conversion for actual NAL unit (expect failure). |
| EXPECT_FALSE(converter.ConvertNalUnitStreamToByteStream( |
| kPacketDataOkWithFieldLen4, sizeof(kPacketDataOkWithFieldLen4), |
| &hevc_config_, output.get(), &output_size_left_for_nal_unit)); |
| EXPECT_EQ(output_size_left_for_nal_unit, 0U); |
| } |
| |
| static const uint8_t kCorruptedPacketConfiguration[] = { |
| 0x01, 0x01, 0x60, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x96, 0xf0, 0x00, 0xfc, 0xfd, 0xf8, 0xf8, 0x00, 0x00, 0x0f, |
| 0x03, 0xa0, 0x00, 0x01, 0x00, 0x18, 0x40, 0x01, 0x0c, 0x01, 0xff, |
| 0xff, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x03, |
| 0x00, 0x00, 0x03, 0x00, 0x96, 0x9d, 0xc0, 0x90, 0xa1, 0x00, 0x01, |
| 0x00, 0x29, 0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, |
| 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x96, 0xa0, 0x03, |
| 0xc0, 0x80, 0x10, 0xe5, 0x96, 0x77, 0x92, 0x46, 0xda, 0xf0, 0x10, |
| 0x10, 0x00, 0x00, 0x3e, 0x80, 0x00, 0x06, 0x1a, 0x80, 0x80, 0xa2, |
| 0x00, 0x01, 0x00, 0x06, 0x44, 0x01, 0xc1, 0x73, 0xd1, 0x89}; |
| |
| static const uint8_t kCorruptedPacketData[] = { |
| 0x00, 0x00, 0x00, 0x15, 0x01, 0x9f, 0x6e, 0xbc, 0x85, 0x3f, |
| 0x0f, 0x87, 0x47, 0xa8, 0xd7, 0x5b, 0xfc, 0xb8, 0xfd, 0x3f, |
| 0x57, 0x0e, 0xac, 0xf5, 0x4c, 0x01, 0x2e, 0x57}; |
| |
| TEST_F(H265ToAnnexBBitstreamConverterTest, CorruptedPacket) { |
| // Initialize converter. |
| std::unique_ptr<uint8_t[]> output; |
| H265ToAnnexBBitstreamConverter converter; |
| |
| // Parse the headers. |
| EXPECT_TRUE(converter.ParseConfiguration( |
| kCorruptedPacketConfiguration, sizeof(kCorruptedPacketConfiguration), |
| &hevc_config_)); |
| uint32_t config_size = converter.GetConfigSize(hevc_config_); |
| EXPECT_GT(config_size, 0U); |
| |
| // Go on with converting the headers. |
| output.reset(new uint8_t[config_size]); |
| EXPECT_TRUE(converter.ConvertHEVCDecoderConfigToByteStream( |
| hevc_config_, output.get(), &config_size)); |
| |
| // Expect an error here. |
| uint32_t output_size = converter.CalculateNeededOutputBufferSize( |
| kCorruptedPacketData, sizeof(kCorruptedPacketData), &hevc_config_); |
| EXPECT_EQ(output_size, 0U); |
| } |
| |
| } // namespace media |