Unit stream to byte stream converter
Change-Id: Iafe1989019e306281f17d80faa348262043cd3b8
This commit is contained in:
parent
9d67f545ed
commit
90731d79df
|
@ -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',
|
||||
|
|
|
@ -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 <list>
|
||||
|
||||
#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<uint8_t>(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<uint8_t>(
|
||||
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<uint8_t>* 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
|
|
@ -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 <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
#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<uint8_t>* output);
|
||||
|
||||
private:
|
||||
friend class NalUnitToByteStreamConverterTest;
|
||||
|
||||
int nalu_length_size_;
|
||||
std::vector<uint8_t> 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_
|
|
@ -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 <gtest/gtest.h>
|
||||
|
||||
#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<uint8_t> 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<uint8_t>(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<uint8_t> 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<uint8_t>(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<uint8_t> 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<uint8_t>(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<uint8_t> 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<uint8_t>(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<uint8_t> 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<uint8_t>(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<uint8_t> 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<uint8_t>(kExpectedOutput,
|
||||
kExpectedOutput + arraysize(kExpectedOutput)),
|
||||
output);
|
||||
}
|
||||
|
||||
} // namespace media
|
||||
} // namespace edash_packager
|
Loading…
Reference in New Issue