Add H.265 byte to unit stream converter.
Also renames NaluReader::NaluType to CodecType. Issue #46 Change-Id: I37d21fcb1109659f14baf43308a94c5e2d9ac2d1
This commit is contained in:
parent
f1e4f74a14
commit
404660cbdb
|
@ -23,10 +23,14 @@
|
||||||
'h264_byte_to_unit_stream_converter.h',
|
'h264_byte_to_unit_stream_converter.h',
|
||||||
'h264_parser.cc',
|
'h264_parser.cc',
|
||||||
'h264_parser.h',
|
'h264_parser.h',
|
||||||
|
'h265_byte_to_unit_stream_converter.cc',
|
||||||
|
'h265_byte_to_unit_stream_converter.h',
|
||||||
'h265_parser.cc',
|
'h265_parser.cc',
|
||||||
'h265_parser.h',
|
'h265_parser.h',
|
||||||
'h26x_bit_reader.cc',
|
'h26x_bit_reader.cc',
|
||||||
'h26x_bit_reader.h',
|
'h26x_bit_reader.h',
|
||||||
|
'h26x_byte_to_unit_stream_converter.cc',
|
||||||
|
'h26x_byte_to_unit_stream_converter.h',
|
||||||
'hevc_decoder_configuration.cc',
|
'hevc_decoder_configuration.cc',
|
||||||
'hevc_decoder_configuration.h',
|
'hevc_decoder_configuration.h',
|
||||||
'nal_unit_to_byte_stream_converter.cc',
|
'nal_unit_to_byte_stream_converter.cc',
|
||||||
|
@ -53,6 +57,7 @@
|
||||||
'ec3_audio_util_unittest.cc',
|
'ec3_audio_util_unittest.cc',
|
||||||
'h264_byte_to_unit_stream_converter_unittest.cc',
|
'h264_byte_to_unit_stream_converter_unittest.cc',
|
||||||
'h264_parser_unittest.cc',
|
'h264_parser_unittest.cc',
|
||||||
|
'h265_byte_to_unit_stream_converter_unittest.cc',
|
||||||
'h265_parser_unittest.cc',
|
'h265_parser_unittest.cc',
|
||||||
'h26x_bit_reader_unittest.cc',
|
'h26x_bit_reader_unittest.cc',
|
||||||
'hevc_decoder_configuration_unittest.cc',
|
'hevc_decoder_configuration_unittest.cc',
|
||||||
|
|
|
@ -15,74 +15,12 @@
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
namespace {
|
H264ByteToUnitStreamConverter::H264ByteToUnitStreamConverter()
|
||||||
// Additional space to reserve for output frame. This value ought to be enough
|
: H26xByteToUnitStreamConverter(NaluReader::kH264) {}
|
||||||
// to acommodate frames consisting of 100 NAL units with 3-byte start codes.
|
|
||||||
const size_t kStreamConversionOverhead = 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
H264ByteToUnitStreamConverter::H264ByteToUnitStreamConverter() {}
|
|
||||||
|
|
||||||
H264ByteToUnitStreamConverter::~H264ByteToUnitStreamConverter() {}
|
H264ByteToUnitStreamConverter::~H264ByteToUnitStreamConverter() {}
|
||||||
|
|
||||||
bool H264ByteToUnitStreamConverter::ConvertByteStreamToNalUnitStream(
|
bool H264ByteToUnitStreamConverter::GetDecoderConfigurationRecord(
|
||||||
const uint8_t* input_frame,
|
std::vector<uint8_t>* decoder_config) const {
|
||||||
size_t input_frame_size,
|
|
||||||
std::vector<uint8_t>* output_frame) {
|
|
||||||
DCHECK(input_frame);
|
|
||||||
DCHECK(output_frame);
|
|
||||||
|
|
||||||
BufferWriter output_buffer(input_frame_size + kStreamConversionOverhead);
|
|
||||||
|
|
||||||
Nalu nalu;
|
|
||||||
NaluReader reader(NaluReader::kH264, kIsAnnexbByteStream, input_frame,
|
|
||||||
input_frame_size);
|
|
||||||
if (!reader.StartsWithStartCode()) {
|
|
||||||
LOG(ERROR) << "H.264 byte stream frame did not begin with start code.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
while (reader.Advance(&nalu) == NaluReader::kOk) {
|
|
||||||
ProcessNalu(nalu, &output_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
output_buffer.SwapBuffer(output_frame);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void H264ByteToUnitStreamConverter::ProcessNalu(const Nalu& nalu,
|
|
||||||
BufferWriter* output_buffer) {
|
|
||||||
DCHECK(nalu.data());
|
|
||||||
DCHECK(output_buffer);
|
|
||||||
|
|
||||||
// Skip the start code, but keep the 1-byte NALU type.
|
|
||||||
const uint8_t* nalu_ptr = nalu.data();
|
|
||||||
const uint64_t nalu_size = nalu.payload_size() + nalu.header_size();
|
|
||||||
DCHECK_LE(nalu_size, std::numeric_limits<uint32_t>::max());
|
|
||||||
|
|
||||||
switch (nalu.type()) {
|
|
||||||
case Nalu::H264_SPS:
|
|
||||||
// Grab SPS NALU.
|
|
||||||
last_sps_.assign(nalu_ptr, nalu_ptr + nalu_size);
|
|
||||||
return;
|
|
||||||
case Nalu::H264_PPS:
|
|
||||||
// Grab PPS NALU.
|
|
||||||
last_pps_.assign(nalu_ptr, nalu_ptr + nalu_size);
|
|
||||||
return;
|
|
||||||
case Nalu::H264_AUD:
|
|
||||||
// Ignore AUD NALU.
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
// Copy all other NALUs.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append 4-byte length and NAL unit data to the buffer.
|
|
||||||
output_buffer->AppendInt(static_cast<uint32_t>(nalu_size));
|
|
||||||
output_buffer->AppendArray(nalu_ptr, nalu_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool H264ByteToUnitStreamConverter::GetAVCDecoderConfigurationRecord(
|
|
||||||
std::vector<uint8_t>* decoder_config) {
|
|
||||||
DCHECK(decoder_config);
|
DCHECK(decoder_config);
|
||||||
|
|
||||||
if ((last_sps_.size() < 4) || last_pps_.empty()) {
|
if ((last_sps_.size() < 4) || last_pps_.empty()) {
|
||||||
|
@ -108,10 +46,35 @@ bool H264ByteToUnitStreamConverter::GetAVCDecoderConfigurationRecord(
|
||||||
buffer.AppendInt(num_pps);
|
buffer.AppendInt(num_pps);
|
||||||
buffer.AppendInt(static_cast<uint16_t>(last_pps_.size()));
|
buffer.AppendInt(static_cast<uint16_t>(last_pps_.size()));
|
||||||
buffer.AppendVector(last_pps_);
|
buffer.AppendVector(last_pps_);
|
||||||
buffer.SwapBuffer(decoder_config);
|
|
||||||
|
|
||||||
|
buffer.SwapBuffer(decoder_config);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool H264ByteToUnitStreamConverter::ProcessNalu(const Nalu& nalu) {
|
||||||
|
DCHECK(nalu.data());
|
||||||
|
|
||||||
|
// Skip the start code, but keep the 1-byte NALU type.
|
||||||
|
const uint8_t* nalu_ptr = nalu.data();
|
||||||
|
const uint64_t nalu_size = nalu.payload_size() + nalu.header_size();
|
||||||
|
|
||||||
|
switch (nalu.type()) {
|
||||||
|
case Nalu::H264_SPS:
|
||||||
|
// Grab SPS NALU.
|
||||||
|
last_sps_.assign(nalu_ptr, nalu_ptr + nalu_size);
|
||||||
|
return true;
|
||||||
|
case Nalu::H264_PPS:
|
||||||
|
// Grab PPS NALU.
|
||||||
|
last_pps_.assign(nalu_ptr, nalu_ptr + nalu_size);
|
||||||
|
return true;
|
||||||
|
case Nalu::H264_AUD:
|
||||||
|
// Ignore AUD NALU.
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
// Have the base class handle other NALU types.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace edash_packager
|
} // namespace edash_packager
|
||||||
|
|
|
@ -12,46 +12,31 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "packager/media/filters/h26x_byte_to_unit_stream_converter.h"
|
||||||
|
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
class BufferWriter;
|
|
||||||
class Nalu;
|
|
||||||
|
|
||||||
/// Class which converts H.264 byte streams (as specified in ISO/IEC 14496-10
|
/// Class which converts H.264 byte streams (as specified in ISO/IEC 14496-10
|
||||||
/// Annex B) into H.264 NAL unit streams (as specified in ISO/IEC 14496-15).
|
/// Annex B) into H.264 NAL unit streams (as specified in ISO/IEC 14496-15).
|
||||||
class H264ByteToUnitStreamConverter {
|
class H264ByteToUnitStreamConverter : public H26xByteToUnitStreamConverter {
|
||||||
public:
|
public:
|
||||||
static const size_t kUnitStreamNaluLengthSize = 4;
|
|
||||||
|
|
||||||
H264ByteToUnitStreamConverter();
|
H264ByteToUnitStreamConverter();
|
||||||
~H264ByteToUnitStreamConverter();
|
~H264ByteToUnitStreamConverter() override;
|
||||||
|
|
||||||
/// Converts a whole AVC byte stream encoded video frame to NAL unit stream
|
/// @name H26xByteToUnitStreamConverter implementation override.
|
||||||
/// format.
|
/// @{
|
||||||
/// @param input_frame is a buffer containing a whole H.264 frame in byte
|
bool GetDecoderConfigurationRecord(
|
||||||
/// stream format.
|
std::vector<uint8_t>* decoder_config) const override;
|
||||||
/// @param input_frame_size is the size of the H.264 frame, in bytes.
|
/// @}
|
||||||
/// @param output_frame is a pointer to a vector which will receive the
|
|
||||||
/// converted frame.
|
|
||||||
/// @return true if successful, false otherwise.
|
|
||||||
bool ConvertByteStreamToNalUnitStream(const uint8_t* input_frame,
|
|
||||||
size_t input_frame_size,
|
|
||||||
std::vector<uint8_t>* output_frame);
|
|
||||||
|
|
||||||
/// Synthesizes an AVCDecoderConfigurationRecord from the SPS and PPS NAL
|
|
||||||
/// units extracted from the AVC byte stream.
|
|
||||||
/// @param decoder_config is a pointer to a vector, which on successful
|
|
||||||
/// return will contain the computed AVCDecoderConfigurationRecord.
|
|
||||||
/// @return true if successful, or false otherwise.
|
|
||||||
bool GetAVCDecoderConfigurationRecord(std::vector<uint8_t>* decoder_config);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ProcessNalu(const Nalu& nalu,
|
bool ProcessNalu(const Nalu& nalu) override;
|
||||||
BufferWriter* output_buffer);
|
|
||||||
|
|
||||||
std::vector<uint8_t> last_sps_;
|
std::vector<uint8_t> last_sps_;
|
||||||
std::vector<uint8_t> last_pps_;
|
std::vector<uint8_t> last_pps_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(H264ByteToUnitStreamConverter);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
|
@ -40,7 +40,7 @@ TEST(H264ByteToUnitStreamConverter, ConversionSuccess) {
|
||||||
ASSERT_TRUE(base::HexStringToBytes(kExpectedConfigRecord,
|
ASSERT_TRUE(base::HexStringToBytes(kExpectedConfigRecord,
|
||||||
&expected_decoder_config));
|
&expected_decoder_config));
|
||||||
std::vector<uint8_t> decoder_config;
|
std::vector<uint8_t> decoder_config;
|
||||||
ASSERT_TRUE(converter.GetAVCDecoderConfigurationRecord(&decoder_config));
|
ASSERT_TRUE(converter.GetDecoderConfigurationRecord(&decoder_config));
|
||||||
EXPECT_EQ(expected_decoder_config, decoder_config);
|
EXPECT_EQ(expected_decoder_config, decoder_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ TEST(H264ByteToUnitStreamConverter, ConversionFailure) {
|
||||||
input_frame.size(),
|
input_frame.size(),
|
||||||
&output_frame));
|
&output_frame));
|
||||||
std::vector<uint8_t> decoder_config;
|
std::vector<uint8_t> decoder_config;
|
||||||
EXPECT_FALSE(converter.GetAVCDecoderConfigurationRecord(&decoder_config));
|
EXPECT_FALSE(converter.GetDecoderConfigurationRecord(&decoder_config));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
// 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/h265_byte_to_unit_stream_converter.h"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include "packager/base/logging.h"
|
||||||
|
#include "packager/media/base/buffer_writer.h"
|
||||||
|
#include "packager/media/base/rcheck.h"
|
||||||
|
#include "packager/media/filters/h265_parser.h"
|
||||||
|
|
||||||
|
namespace edash_packager {
|
||||||
|
namespace media {
|
||||||
|
|
||||||
|
H265ByteToUnitStreamConverter::H265ByteToUnitStreamConverter()
|
||||||
|
: H26xByteToUnitStreamConverter(NaluReader::kH265) {}
|
||||||
|
H265ByteToUnitStreamConverter::~H265ByteToUnitStreamConverter() {}
|
||||||
|
|
||||||
|
bool H265ByteToUnitStreamConverter::GetDecoderConfigurationRecord(
|
||||||
|
std::vector<uint8_t>* decoder_config) const {
|
||||||
|
DCHECK(decoder_config);
|
||||||
|
|
||||||
|
if (last_sps_.empty() || last_pps_.empty() || last_vps_.empty()) {
|
||||||
|
// No data available to construct HEVCDecoderConfigurationRecord.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to parse the SPS to get the data to add to the record.
|
||||||
|
int id;
|
||||||
|
Nalu nalu;
|
||||||
|
H265Parser parser;
|
||||||
|
RCHECK(nalu.InitializeFromH265(last_sps_.data(), last_sps_.size()));
|
||||||
|
RCHECK(parser.ParseSps(nalu, &id) == H265Parser::kOk);
|
||||||
|
const H265Sps* sps = parser.GetSps(id);
|
||||||
|
|
||||||
|
// Construct an HEVCDecoderConfigurationRecord containing a single SPS, PPS,
|
||||||
|
// and VPS NALU. Please refer to ISO/IEC 14496-15 for format specifics.
|
||||||
|
BufferWriter buffer(last_sps_.size() + last_pps_.size() + last_vps_.size() +
|
||||||
|
100);
|
||||||
|
buffer.AppendInt(static_cast<uint8_t>(1) /* version */);
|
||||||
|
// (1) general_profile_space, general_tier_flag, general_profile_idc
|
||||||
|
// (4) general_profile_compatibility_flags
|
||||||
|
// (6) general_constraint_indicator_flags
|
||||||
|
// (1) general_level_idc
|
||||||
|
// Skip Nalu header (2) and the first byte of the SPS to get the
|
||||||
|
// profile_tier_level.
|
||||||
|
buffer.AppendArray(&last_sps_[2+1], 12);
|
||||||
|
// min_spacial_segmentation_idc = 0 (Unknown)
|
||||||
|
// TODO(modmaker): Parse vui_parameters and update this.
|
||||||
|
buffer.AppendInt(static_cast<uint16_t>(0xf000));
|
||||||
|
buffer.AppendInt(static_cast<uint8_t>(0xfc) /* parallelismType = 0 */);
|
||||||
|
buffer.AppendInt(static_cast<uint8_t>(0xfc | sps->chroma_format_idc));
|
||||||
|
buffer.AppendInt(static_cast<uint8_t>(0xf8 | sps->bit_depth_luma_minus8));
|
||||||
|
buffer.AppendInt(static_cast<uint8_t>(0xf8 | sps->bit_depth_chroma_minus8));
|
||||||
|
buffer.AppendInt(static_cast<uint16_t>(0) /* avgFrameRate */);
|
||||||
|
// Following flags are 0:
|
||||||
|
// constantFrameRate
|
||||||
|
// numTemporalLayers
|
||||||
|
// temporalIdNested
|
||||||
|
buffer.AppendInt(static_cast<uint8_t>(kUnitStreamNaluLengthSize - 1));
|
||||||
|
buffer.AppendInt(static_cast<uint8_t>(3) /* numOfArrays */);
|
||||||
|
|
||||||
|
// SPS
|
||||||
|
const uint8_t kArrayCompleteness = 0x80;
|
||||||
|
buffer.AppendInt(static_cast<uint8_t>(kArrayCompleteness | Nalu::H265_SPS));
|
||||||
|
buffer.AppendInt(static_cast<uint16_t>(1) /* numNalus */);
|
||||||
|
buffer.AppendInt(static_cast<uint16_t>(last_sps_.size()));
|
||||||
|
buffer.AppendVector(last_sps_);
|
||||||
|
|
||||||
|
// PPS
|
||||||
|
buffer.AppendInt(static_cast<uint8_t>(kArrayCompleteness | Nalu::H265_PPS));
|
||||||
|
buffer.AppendInt(static_cast<uint16_t>(1) /* numNalus */);
|
||||||
|
buffer.AppendInt(static_cast<uint16_t>(last_pps_.size()));
|
||||||
|
buffer.AppendVector(last_pps_);
|
||||||
|
|
||||||
|
// VPS
|
||||||
|
buffer.AppendInt(static_cast<uint8_t>(kArrayCompleteness | Nalu::H265_VPS));
|
||||||
|
buffer.AppendInt(static_cast<uint16_t>(1) /* numNalus */);
|
||||||
|
buffer.AppendInt(static_cast<uint16_t>(last_vps_.size()));
|
||||||
|
buffer.AppendVector(last_vps_);
|
||||||
|
|
||||||
|
buffer.SwapBuffer(decoder_config);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool H265ByteToUnitStreamConverter::ProcessNalu(const Nalu& nalu) {
|
||||||
|
DCHECK(nalu.data());
|
||||||
|
|
||||||
|
// Skip the start code, but keep the 2-byte NALU header.
|
||||||
|
const uint8_t* nalu_ptr = nalu.data();
|
||||||
|
const uint64_t nalu_size = nalu.payload_size() + nalu.header_size();
|
||||||
|
|
||||||
|
switch (nalu.type()) {
|
||||||
|
case Nalu::H265_SPS:
|
||||||
|
// Grab SPS NALU.
|
||||||
|
last_sps_.assign(nalu_ptr, nalu_ptr + nalu_size);
|
||||||
|
return true;
|
||||||
|
case Nalu::H265_PPS:
|
||||||
|
// Grab PPS NALU.
|
||||||
|
last_pps_.assign(nalu_ptr, nalu_ptr + nalu_size);
|
||||||
|
return true;
|
||||||
|
case Nalu::H265_VPS:
|
||||||
|
// Grab VPS NALU.
|
||||||
|
last_vps_.assign(nalu_ptr, nalu_ptr + nalu_size);
|
||||||
|
return true;
|
||||||
|
case Nalu::H265_AUD:
|
||||||
|
// Ignore AUD NALU.
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
// Have the base class handle other NALU types.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace media
|
||||||
|
} // namespace edash_packager
|
|
@ -0,0 +1,46 @@
|
||||||
|
// 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 MEDIA_FILTERS_H265_BYTE_TO_UNIT_STREAM_CONVERTER_H_
|
||||||
|
#define MEDIA_FILTERS_H265_BYTE_TO_UNIT_STREAM_CONVERTER_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "packager/media/filters/h26x_byte_to_unit_stream_converter.h"
|
||||||
|
|
||||||
|
namespace edash_packager {
|
||||||
|
namespace media {
|
||||||
|
|
||||||
|
/// Class which converts H.265 byte streams (as specified in ISO/IEC 14496-10
|
||||||
|
/// Annex B) into H.265 NAL unit streams (as specified in ISO/IEC 14496-15).
|
||||||
|
class H265ByteToUnitStreamConverter : public H26xByteToUnitStreamConverter {
|
||||||
|
public:
|
||||||
|
H265ByteToUnitStreamConverter();
|
||||||
|
~H265ByteToUnitStreamConverter() override;
|
||||||
|
|
||||||
|
/// @name H26xByteToUnitStreamConverter implementation override.
|
||||||
|
/// @{
|
||||||
|
bool GetDecoderConfigurationRecord(
|
||||||
|
std::vector<uint8_t>* decoder_config) const override;
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool ProcessNalu(const Nalu& nalu) override;
|
||||||
|
|
||||||
|
std::vector<uint8_t> last_sps_;
|
||||||
|
std::vector<uint8_t> last_pps_;
|
||||||
|
std::vector<uint8_t> last_vps_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(H265ByteToUnitStreamConverter);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace media
|
||||||
|
} // namespace edash_packager
|
||||||
|
|
||||||
|
#endif // MEDIA_FILTERS_H265_BYTE_TO_UNIT_STREAM_CONVERTER_H_
|
|
@ -0,0 +1,75 @@
|
||||||
|
// 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 <stdio.h>
|
||||||
|
|
||||||
|
#include "packager/base/strings/string_number_conversions.h"
|
||||||
|
#include "packager/media/filters/h265_byte_to_unit_stream_converter.h"
|
||||||
|
#include "packager/media/filters/hevc_decoder_configuration.h"
|
||||||
|
#include "packager/media/test/test_data_util.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const char kExpectedConfigRecord[] =
|
||||||
|
"01016000000300900000030000f000fcfdf8f800000303a10001002e42010101600000"
|
||||||
|
"030090000003000003005da0028080241f265999a4932bffc0d5c0d640400000030040"
|
||||||
|
"00000602a2000100074401c172b46240a00001001840010c01ffff0160000003009000"
|
||||||
|
"0003000003005d999809";
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace edash_packager {
|
||||||
|
namespace media {
|
||||||
|
|
||||||
|
TEST(H265ByteToUnitStreamConverter, ConversionSuccess) {
|
||||||
|
std::vector<uint8_t> input_frame =
|
||||||
|
ReadTestDataFile("hevc-byte-stream-frame.h265");
|
||||||
|
ASSERT_FALSE(input_frame.empty());
|
||||||
|
|
||||||
|
std::vector<uint8_t> expected_output_frame =
|
||||||
|
ReadTestDataFile("hevc-unit-stream-frame.h265");
|
||||||
|
ASSERT_FALSE(expected_output_frame.empty());
|
||||||
|
|
||||||
|
H265ByteToUnitStreamConverter converter;
|
||||||
|
std::vector<uint8_t> output_frame;
|
||||||
|
ASSERT_TRUE(converter.ConvertByteStreamToNalUnitStream(input_frame.data(),
|
||||||
|
input_frame.size(),
|
||||||
|
&output_frame));
|
||||||
|
EXPECT_EQ(expected_output_frame, output_frame);
|
||||||
|
|
||||||
|
std::vector<uint8_t> expected_decoder_config;
|
||||||
|
ASSERT_TRUE(base::HexStringToBytes(kExpectedConfigRecord,
|
||||||
|
&expected_decoder_config));
|
||||||
|
std::vector<uint8_t> decoder_config;
|
||||||
|
ASSERT_TRUE(converter.GetDecoderConfigurationRecord(&decoder_config));
|
||||||
|
EXPECT_EQ(expected_decoder_config, decoder_config);
|
||||||
|
|
||||||
|
// Double-check that it can be parsed.
|
||||||
|
HEVCDecoderConfiguration conf;
|
||||||
|
ASSERT_TRUE(conf.Parse(decoder_config));
|
||||||
|
// The order is SPS, PPS, VPS.
|
||||||
|
ASSERT_EQ(3u, conf.nalu_count());
|
||||||
|
EXPECT_EQ(Nalu::H265_SPS, conf.nalu(0).type());
|
||||||
|
EXPECT_EQ(Nalu::H265_PPS, conf.nalu(1).type());
|
||||||
|
EXPECT_EQ(Nalu::H265_VPS, conf.nalu(2).type());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(H265ByteToUnitStreamConverter, ConversionFailure) {
|
||||||
|
std::vector<uint8_t> input_frame(100, 0);
|
||||||
|
|
||||||
|
H265ByteToUnitStreamConverter converter;
|
||||||
|
std::vector<uint8_t> output_frame;
|
||||||
|
EXPECT_FALSE(converter.ConvertByteStreamToNalUnitStream(input_frame.data(),
|
||||||
|
0,
|
||||||
|
&output_frame));
|
||||||
|
EXPECT_FALSE(converter.ConvertByteStreamToNalUnitStream(input_frame.data(),
|
||||||
|
input_frame.size(),
|
||||||
|
&output_frame));
|
||||||
|
std::vector<uint8_t> decoder_config;
|
||||||
|
EXPECT_FALSE(converter.GetDecoderConfigurationRecord(&decoder_config));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // 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
|
||||||
|
|
||||||
|
#include "packager/media/filters/h26x_byte_to_unit_stream_converter.h"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include "packager/base/logging.h"
|
||||||
|
#include "packager/media/base/buffer_writer.h"
|
||||||
|
|
||||||
|
namespace edash_packager {
|
||||||
|
namespace media {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Additional space to reserve for output frame. This value ought to be enough
|
||||||
|
// to acommodate frames consisting of 100 NAL units with 3-byte start codes.
|
||||||
|
const size_t kStreamConversionOverhead = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
H26xByteToUnitStreamConverter::H26xByteToUnitStreamConverter(
|
||||||
|
NaluReader::CodecType type)
|
||||||
|
: type_(type) {}
|
||||||
|
H26xByteToUnitStreamConverter::~H26xByteToUnitStreamConverter() {}
|
||||||
|
|
||||||
|
bool H26xByteToUnitStreamConverter::ConvertByteStreamToNalUnitStream(
|
||||||
|
const uint8_t* input_frame,
|
||||||
|
size_t input_frame_size,
|
||||||
|
std::vector<uint8_t>* output_frame) {
|
||||||
|
DCHECK(input_frame);
|
||||||
|
DCHECK(output_frame);
|
||||||
|
|
||||||
|
BufferWriter output_buffer(input_frame_size + kStreamConversionOverhead);
|
||||||
|
|
||||||
|
Nalu nalu;
|
||||||
|
NaluReader reader(type_, kIsAnnexbByteStream, input_frame, input_frame_size);
|
||||||
|
if (!reader.StartsWithStartCode()) {
|
||||||
|
LOG(ERROR) << "H.26x byte stream frame did not begin with start code.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (reader.Advance(&nalu) == NaluReader::kOk) {
|
||||||
|
const uint64_t nalu_size = nalu.payload_size() + nalu.header_size();
|
||||||
|
DCHECK_LE(nalu_size, std::numeric_limits<uint32_t>::max());
|
||||||
|
|
||||||
|
if (ProcessNalu(nalu))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Append 4-byte length and NAL unit data to the buffer.
|
||||||
|
output_buffer.AppendInt(static_cast<uint32_t>(nalu_size));
|
||||||
|
output_buffer.AppendArray(nalu.data(), nalu_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
output_buffer.SwapBuffer(output_frame);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace media
|
||||||
|
} // namespace edash_packager
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
// 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 MEDIA_FILTERS_H26X_BYTE_TO_UNIT_STREAM_CONVERTER_H_
|
||||||
|
#define MEDIA_FILTERS_H26X_BYTE_TO_UNIT_STREAM_CONVERTER_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "packager/media/filters/nalu_reader.h"
|
||||||
|
|
||||||
|
namespace edash_packager {
|
||||||
|
namespace media {
|
||||||
|
|
||||||
|
class BufferWriter;
|
||||||
|
|
||||||
|
/// A base class that is used to convert H.26x byte streams to NAL unit streams.
|
||||||
|
class H26xByteToUnitStreamConverter {
|
||||||
|
public:
|
||||||
|
static const size_t kUnitStreamNaluLengthSize = 4;
|
||||||
|
|
||||||
|
H26xByteToUnitStreamConverter(NaluReader::CodecType type);
|
||||||
|
virtual ~H26xByteToUnitStreamConverter();
|
||||||
|
|
||||||
|
/// Converts a whole byte stream encoded video frame to NAL unit stream
|
||||||
|
/// format.
|
||||||
|
/// @param input_frame is a buffer containing a whole H.26x frame in byte
|
||||||
|
/// stream format.
|
||||||
|
/// @param input_frame_size is the size of the H.26x frame, in bytes.
|
||||||
|
/// @param output_frame is a pointer to a vector which will receive the
|
||||||
|
/// converted frame.
|
||||||
|
/// @return true if successful, false otherwise.
|
||||||
|
bool ConvertByteStreamToNalUnitStream(const uint8_t* input_frame,
|
||||||
|
size_t input_frame_size,
|
||||||
|
std::vector<uint8_t>* output_frame);
|
||||||
|
|
||||||
|
/// Creates either an AVCDecoderConfigurationRecord or a
|
||||||
|
/// HEVCDecoderConfigurationRecord from the units extracted from the byte
|
||||||
|
/// stream.
|
||||||
|
/// @param decoder_config is a pointer to a vector, which on successful
|
||||||
|
/// return will contain the computed record.
|
||||||
|
/// @return true if successful, or false otherwise.
|
||||||
|
virtual bool GetDecoderConfigurationRecord(
|
||||||
|
std::vector<uint8_t>* decoder_config) const = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Process the given Nalu. If this returns true, it was handled and should
|
||||||
|
// not be copied to the buffer.
|
||||||
|
virtual bool ProcessNalu(const Nalu& nalu) = 0;
|
||||||
|
|
||||||
|
NaluReader::CodecType type_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(H26xByteToUnitStreamConverter);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace media
|
||||||
|
} // namespace edash_packager
|
||||||
|
|
||||||
|
#endif // MEDIA_FILTERS_H26x_BYTE_TO_UNIT_STREAM_CONVERTER_H_
|
||||||
|
|
|
@ -155,7 +155,7 @@ bool NalUnitToByteStreamConverter::ConvertUnitToByteStream(
|
||||||
if (is_key_frame)
|
if (is_key_frame)
|
||||||
buffer_writer.AppendVector(decoder_configuration_in_byte_stream_);
|
buffer_writer.AppendVector(decoder_configuration_in_byte_stream_);
|
||||||
|
|
||||||
NaluReader nalu_reader(NaluReader::NaluType::kH264, nalu_length_size_, sample,
|
NaluReader nalu_reader(NaluReader::kH264, nalu_length_size_, sample,
|
||||||
sample_size);
|
sample_size);
|
||||||
Nalu nalu;
|
Nalu nalu;
|
||||||
NaluReader::Result result = nalu_reader.Advance(&nalu);
|
NaluReader::Result result = nalu_reader.Advance(&nalu);
|
||||||
|
|
|
@ -142,7 +142,7 @@ bool Nalu::InitializeFromH265(const uint8_t* data, uint64_t size) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
NaluReader::NaluReader(NaluType type,
|
NaluReader::NaluReader(CodecType type,
|
||||||
uint8_t nal_length_size,
|
uint8_t nal_length_size,
|
||||||
const uint8_t* stream,
|
const uint8_t* stream,
|
||||||
uint64_t stream_size)
|
uint64_t stream_size)
|
||||||
|
|
|
@ -125,7 +125,7 @@ class NaluReader {
|
||||||
kInvalidStream, // error in stream
|
kInvalidStream, // error in stream
|
||||||
kEOStream, // end of stream
|
kEOStream, // end of stream
|
||||||
};
|
};
|
||||||
enum NaluType {
|
enum CodecType {
|
||||||
kH264,
|
kH264,
|
||||||
kH265,
|
kH265,
|
||||||
};
|
};
|
||||||
|
@ -133,7 +133,7 @@ class NaluReader {
|
||||||
/// @param nalu_length_size should be set to 0 for AnnexB byte streams;
|
/// @param nalu_length_size should be set to 0 for AnnexB byte streams;
|
||||||
/// otherwise, it indicates the size of NAL unit length for the NAL
|
/// otherwise, it indicates the size of NAL unit length for the NAL
|
||||||
/// unit stream.
|
/// unit stream.
|
||||||
NaluReader(NaluType type,
|
NaluReader(CodecType type,
|
||||||
uint8_t nal_length_size,
|
uint8_t nal_length_size,
|
||||||
const uint8_t* stream,
|
const uint8_t* stream,
|
||||||
uint64_t stream_size);
|
uint64_t stream_size);
|
||||||
|
@ -183,7 +183,7 @@ class NaluReader {
|
||||||
// The remaining size of the stream.
|
// The remaining size of the stream.
|
||||||
uint64_t stream_size_;
|
uint64_t stream_size_;
|
||||||
// The type of NALU being read.
|
// The type of NALU being read.
|
||||||
NaluType nalu_type_;
|
CodecType nalu_type_;
|
||||||
// The number of bytes the prefix length is; only valid if format is
|
// The number of bytes the prefix length is; only valid if format is
|
||||||
// kAnnexbByteStreamFormat.
|
// kAnnexbByteStreamFormat.
|
||||||
uint8_t nalu_length_size_;
|
uint8_t nalu_length_size_;
|
||||||
|
|
|
@ -329,7 +329,7 @@ bool EsParserH264::EmitFrame(int64_t access_unit_pos,
|
||||||
|
|
||||||
bool EsParserH264::UpdateVideoDecoderConfig(const H264Sps* sps) {
|
bool EsParserH264::UpdateVideoDecoderConfig(const H264Sps* sps) {
|
||||||
std::vector<uint8_t> decoder_config_record;
|
std::vector<uint8_t> decoder_config_record;
|
||||||
if (!stream_converter_->GetAVCDecoderConfigurationRecord(
|
if (!stream_converter_->GetDecoderConfigurationRecord(
|
||||||
&decoder_config_record)) {
|
&decoder_config_record)) {
|
||||||
DLOG(ERROR) << "Failure to construct an AVCDecoderConfigurationRecord";
|
DLOG(ERROR) << "Failure to construct an AVCDecoderConfigurationRecord";
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -238,7 +238,7 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
|
||||||
data += frame.frame_size;
|
data += frame.frame_size;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const NaluReader::NaluType nalu_type =
|
const NaluReader::CodecType nalu_type =
|
||||||
(video_codec_ == kCodecHVC1 || video_codec_ == kCodecHEV1)
|
(video_codec_ == kCodecHVC1 || video_codec_ == kCodecHEV1)
|
||||||
? NaluReader::kH265
|
? NaluReader::kH265
|
||||||
: NaluReader::kH264;
|
: NaluReader::kH264;
|
||||||
|
|
|
@ -827,7 +827,7 @@ bool WvmMediaParser::Output(bool output_encrypted_sample) {
|
||||||
// Set extra data for video stream from AVC Decoder Config Record.
|
// Set extra data for video stream from AVC Decoder Config Record.
|
||||||
// Also, set codec string from the AVC Decoder Config Record.
|
// Also, set codec string from the AVC Decoder Config Record.
|
||||||
std::vector<uint8_t> decoder_config_record;
|
std::vector<uint8_t> decoder_config_record;
|
||||||
byte_to_unit_stream_converter_.GetAVCDecoderConfigurationRecord(
|
byte_to_unit_stream_converter_.GetDecoderConfigurationRecord(
|
||||||
&decoder_config_record);
|
&decoder_config_record);
|
||||||
for (uint32_t i = 0; i < stream_infos_.size(); i++) {
|
for (uint32_t i = 0; i < stream_infos_.size(); i++) {
|
||||||
if (stream_infos_[i]->stream_type() == media::kStreamVideo &&
|
if (stream_infos_[i]->stream_type() == media::kStreamVideo &&
|
||||||
|
|
|
@ -87,3 +87,6 @@ bear_no_i_frame_start.h264
|
||||||
|
|
||||||
avc-byte-stream-frame.h264 - Single IDR frame extracted from test-25fps.h264 in Annex B byte stream format.
|
avc-byte-stream-frame.h264 - Single IDR frame extracted from test-25fps.h264 in Annex B byte stream format.
|
||||||
avc-unit-stream-frame.h264 - Single IDR frame from avc-byte-stream-frame.h264 converted to unit stream format.
|
avc-unit-stream-frame.h264 - Single IDR frame from avc-byte-stream-frame.h264 converted to unit stream format.
|
||||||
|
|
||||||
|
hevc-byte-stream-frame.h265 - Several video frames with SPS/PPS/VPS manually extracted from an H.265 stream, in Annex B byte stream format.
|
||||||
|
hevc-byte-stream-frame.h265 - hevc-byte-stream-frame.h265 converted to unit stream format.
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue