From 90731d79df7a83a4f4def94900d13d2b48109dfb Mon Sep 17 00:00:00 2001 From: Rintaro Kuroiwa Date: Thu, 18 Feb 2016 19:12:52 -0800 Subject: [PATCH] Unit stream to byte stream converter Change-Id: Iafe1989019e306281f17d80faa348262043cd3b8 --- packager/media/filters/filters.gyp | 7 +- .../nal_unit_to_byte_stream_converter.cc | 190 +++++++++ .../nal_unit_to_byte_stream_converter.h | 62 +++ ..._unit_to_byte_stream_converter_unittest.cc | 374 ++++++++++++++++++ 4 files changed, 631 insertions(+), 2 deletions(-) create mode 100644 packager/media/filters/nal_unit_to_byte_stream_converter.cc create mode 100644 packager/media/filters/nal_unit_to_byte_stream_converter.h create mode 100644 packager/media/filters/nal_unit_to_byte_stream_converter_unittest.cc diff --git a/packager/media/filters/filters.gyp b/packager/media/filters/filters.gyp index 6ab721fe15..40616c3c39 100644 --- a/packager/media/filters/filters.gyp +++ b/packager/media/filters/filters.gyp @@ -19,14 +19,16 @@ 'decoder_configuration.h', 'ec3_audio_util.cc', 'ec3_audio_util.h', - 'hevc_decoder_configuration.cc', - 'hevc_decoder_configuration.h', 'h264_bit_reader.cc', 'h264_bit_reader.h', 'h264_byte_to_unit_stream_converter.cc', 'h264_byte_to_unit_stream_converter.h', 'h264_parser.cc', 'h264_parser.h', + 'hevc_decoder_configuration.cc', + 'hevc_decoder_configuration.h', + 'nal_unit_to_byte_stream_converter.cc', + 'nal_unit_to_byte_stream_converter.h', 'nalu_reader.cc', 'nalu_reader.h', 'vp_codec_configuration.cc', @@ -51,6 +53,7 @@ 'h264_byte_to_unit_stream_converter_unittest.cc', 'h264_parser_unittest.cc', 'hevc_decoder_configuration_unittest.cc', + 'nal_unit_to_byte_stream_converter_unittest.cc', 'nalu_reader_unittest.cc', 'vp_codec_configuration_unittest.cc', 'vp8_parser_unittest.cc', diff --git a/packager/media/filters/nal_unit_to_byte_stream_converter.cc b/packager/media/filters/nal_unit_to_byte_stream_converter.cc new file mode 100644 index 0000000000..355747373f --- /dev/null +++ b/packager/media/filters/nal_unit_to_byte_stream_converter.cc @@ -0,0 +1,190 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include "packager/media/filters/nal_unit_to_byte_stream_converter.h" + +#include + +#include "packager/base/logging.h" +#include "packager/media/base/bit_reader.h" +#include "packager/media/base/buffer_reader.h" +#include "packager/media/base/buffer_writer.h" +#include "packager/media/base/macros.h" +#include "packager/media/filters/avc_decoder_configuration.h" +#include "packager/media/filters/nalu_reader.h" + +namespace edash_packager { +namespace media { + +namespace { + +const uint8_t kNaluStartCode[] = {0x00, 0x00, 0x00, 0x01}; + +const uint8_t kEmulationPreventionByte = 0x03; + +const uint8_t kAccessUnitDelimiterRbspAnyPrimaryPicType = 0xF0; + +// Inserts emulation byte where necessary. +void EscapeRawByteSequencePayload(const uint8_t* input, + size_t input_size, + BufferWriter* output_writer) { + // Keep track of consecutive zeros that it has seen (not including the current + // byte), so that the algorithm doesn't need to go back to check the same + // bytes. + int consecutive_zero_count = 0; + for (size_t i = 0; i < input_size; ++i) { + if (consecutive_zero_count <= 1) { + output_writer->AppendInt(input[i]); + } else if (consecutive_zero_count == 2) { + if (input[i] == 0 || input[i] == 1 || input[i] == 2 || input[i] == 3) { + // Must be escaped. + output_writer->AppendInt(kEmulationPreventionByte); + } + output_writer->AppendInt(input[i]); + // Note that input[i] can be 0. + // 00 00 00 00 00 00 should become + // 00 00 03 00 00 03 00 00 03 + // So consecutive_zero_count is reset here and incremented below if + // input[i] is 0. + consecutive_zero_count = 0; + } + + consecutive_zero_count = input[i] == 0 ? consecutive_zero_count + 1 : 0; + } + + // ISO 14496-10 Section 7.4.1.1 mentions that if the last byte is 0 (which + // only happens if RBSP has cabac_zero_word), 0x03 must be appended. + if (consecutive_zero_count > 0) { + DCHECK_GT(input_size, 0u); + DCHECK_EQ(input[input_size - 1], 0u); + output_writer->AppendInt(kEmulationPreventionByte); + } +} + +void AppendNalu(const Nalu& nalu, + int nalu_length_size, + bool escape_data, + BufferWriter* buffer_writer) { + if (escape_data) { + EscapeRawByteSequencePayload( + nalu.data(), nalu.header_size() + nalu.payload_size(), buffer_writer); + } else { + buffer_writer->AppendArray(nalu.data(), + nalu.header_size() + nalu.payload_size()); + } +} + +void AddAccessUnitDelimiter(BufferWriter* buffer_writer) { + buffer_writer->AppendInt(static_cast(Nalu::H264_AUD)); + // For now, primary_pic_type is 7 which is "anything". + buffer_writer->AppendInt(kAccessUnitDelimiterRbspAnyPrimaryPicType); +} + +} // namespace + +NalUnitToByteStreamConverter::NalUnitToByteStreamConverter() + : nalu_length_size_(0), escape_data_(false) {} +NalUnitToByteStreamConverter::~NalUnitToByteStreamConverter() {} + +bool NalUnitToByteStreamConverter::Initialize( + const uint8_t* decoder_configuration_data, + size_t decoder_configuration_data_size, + bool escape_data) { + escape_data_ = escape_data; + if (!decoder_configuration_data || decoder_configuration_data_size == 0) { + LOG(ERROR) << "Decoder conguration is empty."; + return false; + } + + AVCDecoderConfiguration decoder_config; + if (!decoder_config.Parse(std::vector( + decoder_configuration_data, + decoder_configuration_data + decoder_configuration_data_size))) { + return false; + } + + if (decoder_config.nalu_count() < 2) { + LOG(ERROR) << "Cannot find SPS or PPS."; + return false; + } + + nalu_length_size_ = decoder_config.nalu_length_size(); + + BufferWriter buffer_writer(decoder_configuration_data_size); + bool found_sps = false; + bool found_pps = false; + for (uint32_t i = 0; i < decoder_config.nalu_count(); ++i) { + const Nalu& nalu = decoder_config.nalu(i); + if (nalu.type() == Nalu::H264NaluType::H264_SPS) { + buffer_writer.AppendArray(kNaluStartCode, arraysize(kNaluStartCode)); + AppendNalu(nalu, nalu_length_size_, escape_data, &buffer_writer); + found_sps = true; + } else if (nalu.type() == Nalu::H264NaluType::H264_PPS) { + buffer_writer.AppendArray(kNaluStartCode, arraysize(kNaluStartCode)); + AppendNalu(nalu, nalu_length_size_, escape_data, &buffer_writer); + found_pps = true; + } + } + if (!found_sps || !found_pps) { + LOG(ERROR) << "Failed to find SPS or PPS."; + return false; + } + + buffer_writer.SwapBuffer(&decoder_configuration_in_byte_stream_); + return true; +} + +// This ignores all AUD, SPS, and PPS in the sample. Instead uses the data +// parsed in Initialize(). +bool NalUnitToByteStreamConverter::ConvertUnitToByteStream( + const uint8_t* sample, + size_t sample_size, + bool is_key_frame, + std::vector* output) { + if (!sample || sample_size == 0) { + LOG(WARNING) << "Sample is empty."; + return true; + } + + BufferWriter buffer_writer(sample_size); + buffer_writer.AppendArray(kNaluStartCode, arraysize(kNaluStartCode)); + AddAccessUnitDelimiter(&buffer_writer); + if (is_key_frame) + buffer_writer.AppendVector(decoder_configuration_in_byte_stream_); + + NaluReader nalu_reader(NaluReader::NaluType::kH264, nalu_length_size_, sample, + sample_size); + Nalu nalu; + NaluReader::Result result = nalu_reader.Advance(&nalu); + + while (result == NaluReader::kOk) { + switch (nalu.type()) { + case Nalu::H264_AUD: + FALLTHROUGH_INTENDED; + case Nalu::H264_SPS: + FALLTHROUGH_INTENDED; + case Nalu::H264_PPS: + break; + default: + buffer_writer.AppendArray(kNaluStartCode, arraysize(kNaluStartCode)); + AppendNalu(nalu, nalu_length_size_, escape_data_, &buffer_writer); + break; + } + result = nalu_reader.Advance(&nalu); + } + + DCHECK_NE(result, NaluReader::kOk); + if (result != NaluReader::kEOStream) { + LOG(ERROR) << "Stopped reading before end of stream."; + return false; + } + + buffer_writer.SwapBuffer(output); + return true; +} + +} // namespace media +} // namespace edash_packager diff --git a/packager/media/filters/nal_unit_to_byte_stream_converter.h b/packager/media/filters/nal_unit_to_byte_stream_converter.h new file mode 100644 index 0000000000..ada87aea96 --- /dev/null +++ b/packager/media/filters/nal_unit_to_byte_stream_converter.h @@ -0,0 +1,62 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#ifndef PACKAGER_MEDIA_FILTERS_NAL_UNIT_TO_BYTE_STREAM_CONVERTER_H_ +#define PACKAGER_MEDIA_FILTERS_NAL_UNIT_TO_BYTE_STREAM_CONVERTER_H_ + +#include +#include + +#include "packager/base/macros.h" +#include "packager/base/memory/ref_counted.h" + +namespace edash_packager { +namespace media { + +class VideoStreamInfo; + +class NalUnitToByteStreamConverter { + public: + NalUnitToByteStreamConverter(); + ~NalUnitToByteStreamConverter(); + + /// This must be called before calling other methods. + /// @param decoder_configuration_data is the pointer to a decoder config data. + /// @param decoder_configuration_data_size is the size of @a + /// decoder_configuration_data. + /// @param escape_data flags whether the decoder configuration and data + /// passed to ConvertUnitToByteStream() should be escaped with + /// emulation prevention byte. + /// @return true on success, false otherwise. + bool Initialize(const uint8_t* decoder_configuration_data, + size_t decoder_configuration_data_size, + bool escape_data); + + /// Converts unit stream to byte stream using the data passed to Initialize(). + /// The method will function correctly even if @a sample is encrypted using + /// SAMPLE-AES encryption. + /// @param sample is the sample to be converted. + /// @param sample_size is the size of @a sample. + /// @param output is set to the the converted sample, on success. + /// @return true on success, false otherwise. + bool ConvertUnitToByteStream(const uint8_t* sample, size_t sample_size, + bool is_key_frame, + std::vector* output); + + private: + friend class NalUnitToByteStreamConverterTest; + + int nalu_length_size_; + std::vector decoder_configuration_in_byte_stream_; + bool escape_data_; + + DISALLOW_COPY_AND_ASSIGN(NalUnitToByteStreamConverter); +}; + +} // namespace media +} // namespace edash_packager + +#endif // PACKAGER_MEDIA_FILTERS_NAL_UNIT_TO_BYTE_STREAM_CONVERTER_H_ diff --git a/packager/media/filters/nal_unit_to_byte_stream_converter_unittest.cc b/packager/media/filters/nal_unit_to_byte_stream_converter_unittest.cc new file mode 100644 index 0000000000..56146291b2 --- /dev/null +++ b/packager/media/filters/nal_unit_to_byte_stream_converter_unittest.cc @@ -0,0 +1,374 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include + +#include "packager/media/base/media_sample.h" +#include "packager/media/filters/nal_unit_to_byte_stream_converter.h" + +namespace edash_packager { +namespace media { + +namespace { + +// This should be valud AVCDecoderConfigurationRecord that can be parsed by +// NalUnitToByteStreamConverter. +const uint8_t kTestAVCDecoderConfigurationRecord[] = { + 0x01, // configuration version (must be 1) + 0x00, // AVCProfileIndication (bogus) + 0x00, // profile_compatibility (bogus) + 0x00, // AVCLevelIndication (bogus) + 0xFF, // Length size minus 1 == 3 + 0xE1, // 1 sps. + 0x00, 0x1D, // SPS length == 29 + // Some valid SPS data. + 0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4, + 0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91, + 0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA, + 0x60, 0x0F, 0x16, 0x2D, 0x96, + 0x01, // 1 pps. + 0x00, 0x0A, // PPS length == 10 + // The content of PPS is not checked except the type. + 0x68, 0xFE, 0xFD, 0xFC, 0xFB, 0x11, 0x12, 0x13, 0x14, 0x15, +}; + +const bool kEscapeData = true; +const bool kIsKeyFrame = true; + +} // namespace + +class NalUnitToByteStreamConverterTest : public ::testing::Test { + public: + NalUnitToByteStreamConverter converter_; +}; + +// Expect a valid AVCDecoderConfigurationRecord to pass. +TEST(NalUnitToByteStreamConverterTest, ParseAVCDecoderConfigurationRecord) { + NalUnitToByteStreamConverter converter; + EXPECT_TRUE( + converter.Initialize(kTestAVCDecoderConfigurationRecord, + arraysize(kTestAVCDecoderConfigurationRecord), + kEscapeData)); + EXPECT_TRUE( + converter.Initialize(kTestAVCDecoderConfigurationRecord, + arraysize(kTestAVCDecoderConfigurationRecord), + !kEscapeData)); +} + +// Empty AVCDecoderConfigurationRecord should return false. +TEST(NalUnitToByteStreamConverterTest, EmptyAVCDecoderConfigurationRecord) { + NalUnitToByteStreamConverter converter; + EXPECT_FALSE(converter.Initialize(nullptr, 102, kEscapeData)); + EXPECT_FALSE( + converter.Initialize(kTestAVCDecoderConfigurationRecord, 0, kEscapeData)); +} + +// If there is no SPS, Initialize() should fail. +TEST(NalUnitToByteStreamConverterTest, NoSps) { + NalUnitToByteStreamConverter converter; + const uint8_t kNoSps[] = { + 0x01, // configuration version (must be 1) + 0x00, // AVCProfileIndication (bogus) + 0x00, // profile_compatibility (bogus) + 0x00, // AVCLevelIndication (bogus) + 0xFF, // Length size minus 1 == 3 + 0xE0, // 0 sps. + // The rest doesn't really matter, Initialize() should fail. + 0x01, // 1 pps. + 0x00, 0x0A, // PPS length == 10 + 0x68, 0xFE, 0xFD, 0xFC, 0xFB, 0x11, 0x12, 0x13, 0x14, 0x15, + }; + + EXPECT_FALSE(converter.Initialize(kNoSps, arraysize(kNoSps), !kEscapeData)); +} + +// If there is no PPS, Initialize() should fail. +TEST(NalUnitToByteStreamConverterTest, NoPps) { + NalUnitToByteStreamConverter converter; + const uint8_t kNoPps[] = { + 0x01, // configuration version (must be 1) + 0x00, // AVCProfileIndication (bogus) + 0x00, // profile_compatibility (bogus) + 0x00, // AVCLevelIndication (bogus) + 0xFF, // Length size minus 1 == 3 + 0xE1, // 1 sps. + 0x00, 0x1D, // SPS length == 29 + // Some valid SPS data. + 0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4, + 0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91, + 0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA, + 0x60, 0x0F, 0x16, 0x2D, 0x96, + 0x00, // 0 pps. + }; + + EXPECT_FALSE(converter.Initialize(kNoPps, arraysize(kNoPps), !kEscapeData)); +} + +// If the length of SPS is 0 then Initialize() should fail. +TEST(NalUnitToByteStreamConverterTest, ZeroLengthSps) { + NalUnitToByteStreamConverter converter; + const uint8_t kZeroLengthSps[] = { + 0x01, // configuration version (must be 1) + 0x00, // AVCProfileIndication (bogus) + 0x00, // profile_compatibility (bogus) + 0x00, // AVCLevelIndication (bogus) + 0xFF, // Length size minus 1 == 3 + 0xE1, // 1 sps. + 0x00, 0x00, // SPS length == 0 + 0x01, // 1 pps. + 0x00, 0x0A, // PPS length == 10 + 0x68, 0xFE, 0xFD, 0xFC, 0xFB, 0x11, 0x12, 0x13, 0x14, 0x15, + }; + + EXPECT_FALSE(converter.Initialize(kZeroLengthSps, arraysize(kZeroLengthSps), + !kEscapeData)); +} + +// If the length of PPS is 0 then Initialize() should fail. +TEST(NalUnitToByteStreamConverterTest, ZeroLengthPps) { + NalUnitToByteStreamConverter converter; + const uint8_t kZeroLengthPps[] = { + 0x01, // configuration version (must be 1) + 0x00, // AVCProfileIndication (bogus) + 0x00, // profile_compatibility (bogus) + 0x00, // AVCLevelIndication (bogus) + 0xFF, // Length size minus 1 == 3 + 0xE1, // 1 sps. + 0x00, 0x05, // SPS length == 5 + 0x00, 0x00, 0x00, 0x01, 0x02, + 0x01, // 1 pps. + 0x00, 0x00, // PPS length == 0 + }; + + EXPECT_FALSE(converter.Initialize(kZeroLengthPps, arraysize(kZeroLengthPps), + !kEscapeData)); +} + +TEST(NalUnitToByteStreamConverterTest, ConvertUnitToByteStream) { + // Only the type of the NAL units are checked. + // This does not contain AUD, SPS, nor PPS. + const uint8_t kUnitStreamLikeMediaSample[] = { + 0x00, 0x00, 0x00, 0x0A, // Size 10 NALU. + 0x00, // Unspecified NAL unit type. + 0xFD, 0x78, 0xA4, 0xC3, 0x82, 0x62, 0x11, 0x29, 0x77, + }; + NalUnitToByteStreamConverter converter; + EXPECT_TRUE( + converter.Initialize(kTestAVCDecoderConfigurationRecord, + arraysize(kTestAVCDecoderConfigurationRecord), + !kEscapeData)); + + std::vector output; + EXPECT_TRUE(converter.ConvertUnitToByteStream( + kUnitStreamLikeMediaSample, arraysize(kUnitStreamLikeMediaSample), + kIsKeyFrame, &output)); + + const uint8_t kExpectedOutput[] = { + 0x00, 0x00, 0x00, 0x01, // Start code. + 0x09, // AUD type. + 0xF0, // primary pic type is anything. + 0x00, 0x00, 0x00, 0x01, // Start code. + // Some valid SPS data. + 0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4, + 0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91, + 0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA, + 0x60, 0x0F, 0x16, 0x2D, 0x96, + 0x00, 0x00, 0x00, 0x01, // Start code. + 0x68, 0xFE, 0xFD, 0xFC, 0xFB, 0x11, 0x12, 0x13, 0x14, 0x15, // PPS. + 0x00, 0x00, 0x00, 0x01, // Start code. + // The input NALU. + 0x00, // Unspecified NALU type. + 0xFD, 0x78, 0xA4, 0xC3, 0x82, 0x62, 0x11, 0x29, 0x77, + }; + + EXPECT_EQ(std::vector(kExpectedOutput, + kExpectedOutput + arraysize(kExpectedOutput)), + output); +} + +// Verify that escaping works on all data. +TEST(NalUnitToByteStreamConverterTest, ConvertUnitToByteStreamWithEscape) { + // Only the type of the NAL units are checked. + // This does not contain AUD, SPS, nor PPS. + const uint8_t kUnitStreamLikeMediaSample[] = { + 0x00, 0x00, 0x00, 0x0A, // Size 10 NALU. + 0x00, // Unspecified NAL unit type. + 0x06, 0x00, 0x00, 0x00, 0xDF, 0x62, 0x11, 0x29, 0x77, + }; + NalUnitToByteStreamConverter converter; + EXPECT_TRUE( + converter.Initialize(kTestAVCDecoderConfigurationRecord, + arraysize(kTestAVCDecoderConfigurationRecord), + kEscapeData)); + + std::vector output; + EXPECT_TRUE(converter.ConvertUnitToByteStream( + kUnitStreamLikeMediaSample, arraysize(kUnitStreamLikeMediaSample), + kIsKeyFrame, &output)); + + const uint8_t kExpectedOutput[] = { + 0x00, 0x00, 0x00, 0x01, // Start code. + 0x09, // AUD type. + 0xF0, // primary pic type is anything. + 0x00, 0x00, 0x00, 0x01, // Start code. + // Some valid SPS data. + 0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4, + 0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91, + // Note that extra 0x03 is added. + 0x00, 0x00, 0x03, 0x03, 0x03, 0xE9, 0x00, 0x00, + 0xEA, 0x60, 0x0F, 0x16, 0x2D, 0x96, + 0x00, 0x00, 0x00, 0x01, // Start code. + 0x68, 0xFE, 0xFD, 0xFC, 0xFB, 0x11, 0x12, 0x13, 0x14, 0x15, // PPS. + 0x00, 0x00, 0x00, 0x01, // Start code. + // The input NALU. + 0x00, // Unspecified NALU type. + 0x06, 0x00, 0x00, 0x03, 0x00, 0xDF, 0x62, 0x11, 0x29, 0x77, + }; + + EXPECT_EQ(std::vector(kExpectedOutput, + kExpectedOutput + arraysize(kExpectedOutput)), + output); +} + +// NALU ending with 0 must have 3 appended. +TEST(NalUnitToByteStreamConverterTest, NaluEndingWithZero) { + const uint8_t kNaluEndingWithZero[] = { + 0x00, 0x00, 0x00, 0x03, // Size 10 NALU. + 0x00, // Unspecified NAL unit type. + 0xAA, 0x00, // Ends with 0. + }; + NalUnitToByteStreamConverter converter; + EXPECT_TRUE( + converter.Initialize(kTestAVCDecoderConfigurationRecord, + arraysize(kTestAVCDecoderConfigurationRecord), + kEscapeData)); + + std::vector output; + EXPECT_TRUE(converter.ConvertUnitToByteStream(kNaluEndingWithZero, + arraysize(kNaluEndingWithZero), + !kIsKeyFrame, &output)); + + const uint8_t kExpectedOutput[] = { + 0x00, 0x00, 0x00, 0x01, // Start code. + 0x09, // AUD type. + 0xF0, // primary pic type is anything. + 0x00, 0x00, 0x00, 0x01, // Start code. + // The input NALU. + 0x00, // Unspecified NALU type. + 0xAA, 0x00, 0x03, // 0x03 at the end because the original ends with 0. + }; + + EXPECT_EQ(std::vector(kExpectedOutput, + kExpectedOutput + arraysize(kExpectedOutput)), + output); +} + +// Verify that if it is not a key frame then SPS and PPS from decoder +// configuration is not used. +TEST(NalUnitToByteStreamConverterTest, NonKeyFrameSample) { + const uint8_t kNonKeyFrameStream[] = { + 0x00, 0x00, 0x00, 0x03, // Size 10 NALU. + 0x00, // Unspecified NAL unit type. + 0x33, 0x88, + }; + NalUnitToByteStreamConverter converter; + EXPECT_TRUE( + converter.Initialize(kTestAVCDecoderConfigurationRecord, + arraysize(kTestAVCDecoderConfigurationRecord), + kEscapeData)); + + std::vector output; + EXPECT_TRUE(converter.ConvertUnitToByteStream(kNonKeyFrameStream, + arraysize(kNonKeyFrameStream), + !kIsKeyFrame, &output)); + + const uint8_t kExpectedOutput[] = { + 0x00, 0x00, 0x00, 0x01, // Start code. + 0x09, // AUD type. + 0xF0, // Anything. + 0x00, 0x00, 0x00, 0x01, // Start code. + // The input NALU. + 0x00, // Unspecified NALU type. + 0x33, 0x88, + }; + + EXPECT_EQ(std::vector(kExpectedOutput, + kExpectedOutput + arraysize(kExpectedOutput)), + output); +} + +// Bug found during unit testing. +// The zeros aren't contiguous but the escape byte was inserted. +TEST(NalUnitToByteStreamConverterTest, DispersedZeros) { + const uint8_t kDispersedZeros[] = { + 0x00, 0x00, 0x00, 0x08, // Size 10 NALU. + 0x00, // Unspecified NAL unit type. + // After 2 zeros (including the first byte of the NALU followed by 0, 1, + // 2, or 3 caused it to insert the escape byte. + 0x11, 0x00, + 0x01, 0x00, 0x02, 0x00, 0x44, + }; + NalUnitToByteStreamConverter converter; + EXPECT_TRUE( + converter.Initialize(kTestAVCDecoderConfigurationRecord, + arraysize(kTestAVCDecoderConfigurationRecord), + kEscapeData)); + + std::vector output; + EXPECT_TRUE(converter.ConvertUnitToByteStream( + kDispersedZeros, arraysize(kDispersedZeros), !kIsKeyFrame, &output)); + + const uint8_t kExpectedOutput[] = { + 0x00, 0x00, 0x00, 0x01, // Start code. + 0x09, // AUD type. + 0xF0, // Anything. + 0x00, 0x00, 0x00, 0x01, // Start code. + // The input NALU. + 0x00, // Unspecified NAL unit type. + 0x11, 0x00, 0x01, 0x00, 0x02, 0x00, 0x44, + }; + + EXPECT_EQ(std::vector(kExpectedOutput, + kExpectedOutput + arraysize(kExpectedOutput)), + output); +} + +// Verify that CnovertUnitToByteStream() with escape_data = false works. +TEST(NalUnitToByteStreamConverterTest, DoNotEscape) { + // This has sequences that should be escaped if escape_data = true. + const uint8_t kNotEscaped[] = { + 0x00, 0x00, 0x00, 0x0C, // Size 12 NALU. + 0x00, // Unspecified NAL unit type. + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, + }; + + NalUnitToByteStreamConverter converter; + EXPECT_TRUE( + converter.Initialize(kTestAVCDecoderConfigurationRecord, + arraysize(kTestAVCDecoderConfigurationRecord), + !kEscapeData)); + + std::vector output; + EXPECT_TRUE(converter.ConvertUnitToByteStream( + kNotEscaped, arraysize(kNotEscaped), !kIsKeyFrame, &output)); + + const uint8_t kExpectedOutput[] = { + 0x00, 0x00, 0x00, 0x01, // Start code. + 0x09, // AUD type. + 0xF0, // Anything. + 0x00, 0x00, 0x00, 0x01, // Start code. + // Should be the same as the input. + 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, + }; + + EXPECT_EQ(std::vector(kExpectedOutput, + kExpectedOutput + arraysize(kExpectedOutput)), + output); +} + +} // namespace media +} // namespace edash_packager