Split NALU enumeration into its own class.
Removed the code for NALU splitting in the H.264 parser and moved it to its own class. Also added support for length-prefixed NALU splitting for use with the encrypting fragmenter. Change-Id: I1e91266681f1b117fb2382cf80590651efc06619
This commit is contained in:
parent
6f3e5c77b7
commit
890c601dce
|
@ -48,8 +48,9 @@ bool AVCDecoderConfiguration::Parse(const std::vector<uint8_t>& data) {
|
|||
|
||||
H264Parser parser;
|
||||
int sps_id = 0;
|
||||
RCHECK(parser.ParseSPSFromArray(reader.data() + reader.pos(), sps_length,
|
||||
&sps_id) == H264Parser::kOk);
|
||||
Nalu nalu;
|
||||
RCHECK(nalu.InitializeFromH264(reader.data() + reader.pos(), sps_length, 0));
|
||||
RCHECK(parser.ParseSPS(nalu, &sps_id) == H264Parser::kOk);
|
||||
return ExtractResolutionFromSps(*parser.GetSPS(sps_id), &coded_width_,
|
||||
&coded_height_, &pixel_width_,
|
||||
&pixel_height_);
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
'h264_byte_to_unit_stream_converter.h',
|
||||
'h264_parser.cc',
|
||||
'h264_parser.h',
|
||||
'nalu_reader.cc',
|
||||
'nalu_reader.h',
|
||||
'vp_codec_configuration.cc',
|
||||
'vp_codec_configuration.h',
|
||||
'vp8_parser.cc',
|
||||
|
@ -47,6 +49,7 @@
|
|||
'h264_byte_to_unit_stream_converter_unittest.cc',
|
||||
'h264_parser_unittest.cc',
|
||||
'hevc_decoder_configuration_unittest.cc',
|
||||
'nalu_reader_unittest.cc',
|
||||
'vp_codec_configuration_unittest.cc',
|
||||
'vp8_parser_unittest.cc',
|
||||
'vp9_parser_unittest.cc',
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include "packager/media/filters/h264_byte_to_unit_stream_converter.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "packager/base/logging.h"
|
||||
#include "packager/media/base/buffer_writer.h"
|
||||
#include "packager/media/filters/h264_parser.h"
|
||||
|
@ -32,58 +34,50 @@ bool H264ByteToUnitStreamConverter::ConvertByteStreamToNalUnitStream(
|
|||
|
||||
BufferWriter output_buffer(input_frame_size + kStreamConversionOverhead);
|
||||
|
||||
const uint8_t* input_ptr(input_frame);
|
||||
const uint8_t* input_end(input_ptr + input_frame_size);
|
||||
off_t next_start_code_offset;
|
||||
off_t next_start_code_size;
|
||||
bool first_nalu(true);
|
||||
while (H264Parser::FindStartCode(input_ptr,
|
||||
input_end - input_ptr,
|
||||
&next_start_code_offset,
|
||||
&next_start_code_size)) {
|
||||
Nalu nalu;
|
||||
NaluReader reader(kIsAnnexbByteStream, input_frame, input_frame_size);
|
||||
while (reader.Advance(&nalu) == NaluReader::kOk) {
|
||||
if (first_nalu) {
|
||||
if (next_start_code_offset != 0) {
|
||||
if (nalu.data() != input_frame) {
|
||||
LOG(ERROR) << "H.264 byte stream frame did not begin with start code.";
|
||||
return false;
|
||||
}
|
||||
first_nalu = false;
|
||||
} else {
|
||||
ProcessNalu(input_ptr, next_start_code_offset, &output_buffer);
|
||||
}
|
||||
input_ptr += next_start_code_offset + next_start_code_size;
|
||||
|
||||
ProcessNalu(nalu, &output_buffer);
|
||||
}
|
||||
|
||||
if (first_nalu) {
|
||||
LOG(ERROR) << "H.264 byte stream frame did not contain start codes.";
|
||||
return false;
|
||||
} else {
|
||||
ProcessNalu(input_ptr, input_end - input_ptr, &output_buffer);
|
||||
}
|
||||
|
||||
output_buffer.SwapBuffer(output_frame);
|
||||
return true;
|
||||
}
|
||||
|
||||
void H264ByteToUnitStreamConverter::ProcessNalu(const uint8_t* nalu_ptr,
|
||||
size_t nalu_size,
|
||||
void H264ByteToUnitStreamConverter::ProcessNalu(const Nalu& nalu,
|
||||
BufferWriter* output_buffer) {
|
||||
DCHECK(nalu_ptr);
|
||||
DCHECK(nalu.data());
|
||||
DCHECK(output_buffer);
|
||||
|
||||
if (!nalu_size)
|
||||
return; // Edge case.
|
||||
// Skip the start code, but keep the 1-byte NALU type.
|
||||
const uint8_t* nalu_ptr = nalu.data() + nalu.header_size() - 1;
|
||||
const uint64_t nalu_size = nalu.data_size() + 1;
|
||||
DCHECK_LE(nalu_size, std::numeric_limits<uint32_t>::max());
|
||||
|
||||
uint8_t nalu_type = *nalu_ptr & 0x0f;
|
||||
switch (nalu_type) {
|
||||
case H264NALU::kSPS:
|
||||
switch (nalu.type()) {
|
||||
case Nalu::H264_SPS:
|
||||
// Grab SPS NALU.
|
||||
last_sps_.assign(nalu_ptr, nalu_ptr + nalu_size);
|
||||
return;
|
||||
case H264NALU::kPPS:
|
||||
case Nalu::H264_PPS:
|
||||
// Grab PPS NALU.
|
||||
last_pps_.assign(nalu_ptr, nalu_ptr + nalu_size);
|
||||
return;
|
||||
case H264NALU::kAUD:
|
||||
case Nalu::H264_AUD:
|
||||
// Ignore AUD NALU.
|
||||
return;
|
||||
default:
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace edash_packager {
|
|||
namespace media {
|
||||
|
||||
class BufferWriter;
|
||||
class Nalu;
|
||||
|
||||
/// 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).
|
||||
|
@ -46,8 +47,7 @@ class H264ByteToUnitStreamConverter {
|
|||
bool GetAVCDecoderConfigurationRecord(std::vector<uint8_t>* decoder_config);
|
||||
|
||||
private:
|
||||
void ProcessNalu(const uint8_t* nalu_ptr,
|
||||
size_t nalu_size,
|
||||
void ProcessNalu(const Nalu& nalu,
|
||||
BufferWriter* output_buffer);
|
||||
|
||||
std::vector<uint8_t> last_sps_;
|
||||
|
|
|
@ -97,10 +97,6 @@ bool H264SliceHeader::IsSISlice() const {
|
|||
return (slice_type % 5 == kSISlice);
|
||||
}
|
||||
|
||||
H264NALU::H264NALU() {
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
|
||||
H264SPS::H264SPS() {
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
|
@ -120,7 +116,7 @@ H264SEIMessage::H264SEIMessage() {
|
|||
#define READ_BITS_OR_RETURN(num_bits, out) \
|
||||
do { \
|
||||
int _out; \
|
||||
if (!br_.ReadBits(num_bits, &_out)) { \
|
||||
if (!br->ReadBits(num_bits, &_out)) { \
|
||||
DVLOG(1) \
|
||||
<< "Error in stream: unexpected EOS while trying to read " #out; \
|
||||
return kInvalidStream; \
|
||||
|
@ -131,7 +127,7 @@ H264SEIMessage::H264SEIMessage() {
|
|||
#define READ_BOOL_OR_RETURN(out) \
|
||||
do { \
|
||||
int _out; \
|
||||
if (!br_.ReadBits(1, &_out)) { \
|
||||
if (!br->ReadBits(1, &_out)) { \
|
||||
DVLOG(1) \
|
||||
<< "Error in stream: unexpected EOS while trying to read " #out; \
|
||||
return kInvalidStream; \
|
||||
|
@ -141,7 +137,7 @@ H264SEIMessage::H264SEIMessage() {
|
|||
|
||||
#define READ_UE_OR_RETURN(out) \
|
||||
do { \
|
||||
if (ReadUE(out) != kOk) { \
|
||||
if (ReadUE(br, out) != kOk) { \
|
||||
DVLOG(1) << "Error in stream: invalid value while trying to read " #out; \
|
||||
return kInvalidStream; \
|
||||
} \
|
||||
|
@ -149,7 +145,7 @@ H264SEIMessage::H264SEIMessage() {
|
|||
|
||||
#define READ_SE_OR_RETURN(out) \
|
||||
do { \
|
||||
if (ReadSE(out) != kOk) { \
|
||||
if (ReadSE(br, out) != kOk) { \
|
||||
DVLOG(1) << "Error in stream: invalid value while trying to read " #out; \
|
||||
return kInvalidStream; \
|
||||
} \
|
||||
|
@ -188,28 +184,13 @@ static const int kTableSarHeight[] = {
|
|||
COMPILE_ASSERT(arraysize(kTableSarWidth) == arraysize(kTableSarHeight),
|
||||
sar_tables_must_have_same_size);
|
||||
|
||||
H264Parser::H264Parser() {
|
||||
Reset();
|
||||
}
|
||||
H264Parser::H264Parser() {}
|
||||
|
||||
H264Parser::~H264Parser() {
|
||||
STLDeleteValues(&active_SPSes_);
|
||||
STLDeleteValues(&active_PPSes_);
|
||||
}
|
||||
|
||||
void H264Parser::Reset() {
|
||||
stream_ = NULL;
|
||||
bytes_left_ = 0;
|
||||
}
|
||||
|
||||
void H264Parser::SetStream(const uint8_t* stream, off_t stream_size) {
|
||||
DCHECK(stream);
|
||||
DCHECK_GT(stream_size, 0);
|
||||
|
||||
stream_ = stream;
|
||||
bytes_left_ = stream_size;
|
||||
}
|
||||
|
||||
const H264PPS* H264Parser::GetPPS(int pps_id) {
|
||||
return active_PPSes_[pps_id];
|
||||
}
|
||||
|
@ -218,87 +199,7 @@ const H264SPS* H264Parser::GetSPS(int sps_id) {
|
|||
return active_SPSes_[sps_id];
|
||||
}
|
||||
|
||||
static inline bool IsStartCode(const uint8_t* data) {
|
||||
return data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x01;
|
||||
}
|
||||
|
||||
// static
|
||||
bool H264Parser::FindStartCode(const uint8_t* data,
|
||||
off_t data_size,
|
||||
off_t* offset,
|
||||
off_t* start_code_size) {
|
||||
DCHECK_GE(data_size, 0);
|
||||
off_t bytes_left = data_size;
|
||||
|
||||
while (bytes_left >= 3) {
|
||||
if (IsStartCode(data)) {
|
||||
// Found three-byte start code, set pointer at its beginning.
|
||||
*offset = data_size - bytes_left;
|
||||
*start_code_size = 3;
|
||||
|
||||
// If there is a zero byte before this start code,
|
||||
// then it's actually a four-byte start code, so backtrack one byte.
|
||||
if (*offset > 0 && *(data - 1) == 0x00) {
|
||||
--(*offset);
|
||||
++(*start_code_size);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
++data;
|
||||
--bytes_left;
|
||||
}
|
||||
|
||||
// End of data: offset is pointing to the first byte that was not considered
|
||||
// as a possible start of a start code.
|
||||
// Note: there is no security issue when receiving a negative |data_size|
|
||||
// since in this case, |bytes_left| is equal to |data_size| and thus
|
||||
// |*offset| is equal to 0 (valid offset).
|
||||
*offset = data_size - bytes_left;
|
||||
*start_code_size = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool H264Parser::LocateNALU(off_t* nalu_size, off_t* start_code_size) {
|
||||
// Find the start code of next NALU.
|
||||
off_t nalu_start_off = 0;
|
||||
off_t annexb_start_code_size = 0;
|
||||
if (!FindStartCode(stream_, bytes_left_,
|
||||
&nalu_start_off, &annexb_start_code_size)) {
|
||||
DVLOG(4) << "Could not find start code, end of stream?";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move the stream to the beginning of the NALU (pointing at the start code).
|
||||
stream_ += nalu_start_off;
|
||||
bytes_left_ -= nalu_start_off;
|
||||
|
||||
const uint8_t* nalu_data = stream_ + annexb_start_code_size;
|
||||
off_t max_nalu_data_size = bytes_left_ - annexb_start_code_size;
|
||||
if (max_nalu_data_size <= 0) {
|
||||
DVLOG(3) << "End of stream";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the start code of next NALU;
|
||||
// if successful, |nalu_size_without_start_code| is the number of bytes from
|
||||
// after previous start code to before this one;
|
||||
// if next start code is not found, it is still a valid NALU since there
|
||||
// are some bytes left after the first start code: all the remaining bytes
|
||||
// belong to the current NALU.
|
||||
off_t next_start_code_size = 0;
|
||||
off_t nalu_size_without_start_code = 0;
|
||||
if (!FindStartCode(nalu_data, max_nalu_data_size,
|
||||
&nalu_size_without_start_code, &next_start_code_size)) {
|
||||
nalu_size_without_start_code = max_nalu_data_size;
|
||||
}
|
||||
*nalu_size = nalu_size_without_start_code + annexb_start_code_size;
|
||||
*start_code_size = annexb_start_code_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
H264Parser::Result H264Parser::ReadUE(int* val) {
|
||||
H264Parser::Result H264Parser::ReadUE(H264BitReader* br, int* val) {
|
||||
int num_bits = -1;
|
||||
int bit;
|
||||
int rest;
|
||||
|
@ -323,12 +224,12 @@ H264Parser::Result H264Parser::ReadUE(int* val) {
|
|||
return kOk;
|
||||
}
|
||||
|
||||
H264Parser::Result H264Parser::ReadSE(int* val) {
|
||||
H264Parser::Result H264Parser::ReadSE(H264BitReader* br, int* val) {
|
||||
int ue;
|
||||
Result res;
|
||||
|
||||
// See Chapter 9 in the spec.
|
||||
res = ReadUE(&ue);
|
||||
res = ReadUE(br, &ue);
|
||||
if (res != kOk)
|
||||
return res;
|
||||
|
||||
|
@ -340,46 +241,6 @@ H264Parser::Result H264Parser::ReadSE(int* val) {
|
|||
return kOk;
|
||||
}
|
||||
|
||||
H264Parser::Result H264Parser::AdvanceToNextNALU(H264NALU* nalu) {
|
||||
off_t start_code_size;
|
||||
off_t nalu_size_with_start_code;
|
||||
if (!LocateNALU(&nalu_size_with_start_code, &start_code_size)) {
|
||||
DVLOG(4) << "Could not find next NALU, bytes left in stream: "
|
||||
<< bytes_left_;
|
||||
return kEOStream;
|
||||
}
|
||||
|
||||
nalu->data = stream_ + start_code_size;
|
||||
nalu->size = nalu_size_with_start_code - start_code_size;
|
||||
DVLOG(4) << "NALU found: size=" << nalu_size_with_start_code;
|
||||
|
||||
// Initialize bit reader at the start of found NALU.
|
||||
if (!br_.Initialize(nalu->data, nalu->size))
|
||||
return kEOStream;
|
||||
|
||||
// Move parser state to after this NALU, so next time AdvanceToNextNALU
|
||||
// is called, we will effectively be skipping it;
|
||||
// other parsing functions will use the position saved
|
||||
// in bit reader for parsing, so we don't have to remember it here.
|
||||
stream_ += nalu_size_with_start_code;
|
||||
bytes_left_ -= nalu_size_with_start_code;
|
||||
|
||||
// Read NALU header, skip the forbidden_zero_bit, but check for it.
|
||||
int data;
|
||||
READ_BITS_OR_RETURN(1, &data);
|
||||
TRUE_OR_RETURN(data == 0);
|
||||
|
||||
READ_BITS_OR_RETURN(2, &nalu->nal_ref_idc);
|
||||
READ_BITS_OR_RETURN(5, &nalu->nal_unit_type);
|
||||
|
||||
DVLOG(4) << "NALU type: " << static_cast<int>(nalu->nal_unit_type)
|
||||
<< " at: " << reinterpret_cast<const void*>(nalu->data)
|
||||
<< " size: " << nalu->size
|
||||
<< " ref: " << static_cast<int>(nalu->nal_ref_idc);
|
||||
|
||||
return kOk;
|
||||
}
|
||||
|
||||
// Default scaling lists (per spec).
|
||||
static const int kDefault4x4Intra[kH264ScalingList4x4Length] = {
|
||||
6, 13, 13, 20, 20, 20, 28, 28, 28, 28, 32, 32, 32, 37, 37, 42, };
|
||||
|
@ -503,7 +364,8 @@ static void FallbackScalingList8x8(
|
|||
}
|
||||
}
|
||||
|
||||
H264Parser::Result H264Parser::ParseScalingList(int size,
|
||||
H264Parser::Result H264Parser::ParseScalingList(H264BitReader* br,
|
||||
int size,
|
||||
int* scaling_list,
|
||||
bool* use_default) {
|
||||
// See chapter 7.3.2.1.1.1.
|
||||
|
@ -532,7 +394,8 @@ H264Parser::Result H264Parser::ParseScalingList(int size,
|
|||
return kOk;
|
||||
}
|
||||
|
||||
H264Parser::Result H264Parser::ParseSPSScalingLists(H264SPS* sps) {
|
||||
H264Parser::Result H264Parser::ParseSPSScalingLists(H264BitReader* br,
|
||||
H264SPS* sps) {
|
||||
// See 7.4.2.1.1.
|
||||
bool seq_scaling_list_present_flag;
|
||||
bool use_default;
|
||||
|
@ -543,7 +406,8 @@ H264Parser::Result H264Parser::ParseSPSScalingLists(H264SPS* sps) {
|
|||
READ_BOOL_OR_RETURN(&seq_scaling_list_present_flag);
|
||||
|
||||
if (seq_scaling_list_present_flag) {
|
||||
res = ParseScalingList(arraysize(sps->scaling_list4x4[i]),
|
||||
res = ParseScalingList(br,
|
||||
arraysize(sps->scaling_list4x4[i]),
|
||||
sps->scaling_list4x4[i],
|
||||
&use_default);
|
||||
if (res != kOk)
|
||||
|
@ -563,7 +427,8 @@ H264Parser::Result H264Parser::ParseSPSScalingLists(H264SPS* sps) {
|
|||
READ_BOOL_OR_RETURN(&seq_scaling_list_present_flag);
|
||||
|
||||
if (seq_scaling_list_present_flag) {
|
||||
res = ParseScalingList(arraysize(sps->scaling_list8x8[i]),
|
||||
res = ParseScalingList(br,
|
||||
arraysize(sps->scaling_list8x8[i]),
|
||||
sps->scaling_list8x8[i],
|
||||
&use_default);
|
||||
if (res != kOk)
|
||||
|
@ -581,7 +446,8 @@ H264Parser::Result H264Parser::ParseSPSScalingLists(H264SPS* sps) {
|
|||
return kOk;
|
||||
}
|
||||
|
||||
H264Parser::Result H264Parser::ParsePPSScalingLists(const H264SPS& sps,
|
||||
H264Parser::Result H264Parser::ParsePPSScalingLists(H264BitReader* br,
|
||||
const H264SPS& sps,
|
||||
H264PPS* pps) {
|
||||
// See 7.4.2.2.
|
||||
bool pic_scaling_list_present_flag;
|
||||
|
@ -592,7 +458,8 @@ H264Parser::Result H264Parser::ParsePPSScalingLists(const H264SPS& sps,
|
|||
READ_BOOL_OR_RETURN(&pic_scaling_list_present_flag);
|
||||
|
||||
if (pic_scaling_list_present_flag) {
|
||||
res = ParseScalingList(arraysize(pps->scaling_list4x4[i]),
|
||||
res = ParseScalingList(br,
|
||||
arraysize(pps->scaling_list4x4[i]),
|
||||
pps->scaling_list4x4[i],
|
||||
&use_default);
|
||||
if (res != kOk)
|
||||
|
@ -621,7 +488,8 @@ H264Parser::Result H264Parser::ParsePPSScalingLists(const H264SPS& sps,
|
|||
READ_BOOL_OR_RETURN(&pic_scaling_list_present_flag);
|
||||
|
||||
if (pic_scaling_list_present_flag) {
|
||||
res = ParseScalingList(arraysize(pps->scaling_list8x8[i]),
|
||||
res = ParseScalingList(br,
|
||||
arraysize(pps->scaling_list8x8[i]),
|
||||
pps->scaling_list8x8[i],
|
||||
&use_default);
|
||||
if (res != kOk)
|
||||
|
@ -649,7 +517,7 @@ H264Parser::Result H264Parser::ParsePPSScalingLists(const H264SPS& sps,
|
|||
}
|
||||
|
||||
H264Parser::Result H264Parser::ParseAndIgnoreHRDParameters(
|
||||
bool* hrd_parameters_present) {
|
||||
H264BitReader* br, bool* hrd_parameters_present) {
|
||||
int data;
|
||||
READ_BOOL_OR_RETURN(&data); // {nal,vcl}_hrd_parameters_present_flag
|
||||
if (!data)
|
||||
|
@ -671,7 +539,8 @@ H264Parser::Result H264Parser::ParseAndIgnoreHRDParameters(
|
|||
return kOk;
|
||||
}
|
||||
|
||||
H264Parser::Result H264Parser::ParseVUIParameters(H264SPS* sps) {
|
||||
H264Parser::Result H264Parser::ParseVUIParameters(H264BitReader* br,
|
||||
H264SPS* sps) {
|
||||
bool aspect_ratio_info_present_flag;
|
||||
READ_BOOL_OR_RETURN(&aspect_ratio_info_present_flag);
|
||||
if (aspect_ratio_info_present_flag) {
|
||||
|
@ -721,12 +590,12 @@ H264Parser::Result H264Parser::ParseVUIParameters(H264SPS* sps) {
|
|||
|
||||
// Read and ignore NAL HRD parameters, if present.
|
||||
bool hrd_parameters_present = false;
|
||||
Result res = ParseAndIgnoreHRDParameters(&hrd_parameters_present);
|
||||
Result res = ParseAndIgnoreHRDParameters(br, &hrd_parameters_present);
|
||||
if (res != kOk)
|
||||
return res;
|
||||
|
||||
// Read and ignore VCL HRD parameters, if present.
|
||||
res = ParseAndIgnoreHRDParameters(&hrd_parameters_present);
|
||||
res = ParseAndIgnoreHRDParameters(br, &hrd_parameters_present);
|
||||
if (res != kOk)
|
||||
return res;
|
||||
|
||||
|
@ -761,10 +630,13 @@ static void FillDefaultSeqScalingLists(H264SPS* sps) {
|
|||
sps->scaling_list8x8[i][j] = 16;
|
||||
}
|
||||
|
||||
H264Parser::Result H264Parser::ParseSPS(int* sps_id) {
|
||||
H264Parser::Result H264Parser::ParseSPS(const Nalu& nalu, int* sps_id) {
|
||||
// See 7.4.2.1.
|
||||
int data;
|
||||
Result res;
|
||||
H264BitReader reader;
|
||||
reader.Initialize(nalu.data() + nalu.header_size(), nalu.data_size());
|
||||
H264BitReader* br = &reader;
|
||||
|
||||
*sps_id = -1;
|
||||
|
||||
|
@ -804,7 +676,7 @@ H264Parser::Result H264Parser::ParseSPS(int* sps_id) {
|
|||
|
||||
if (sps->seq_scaling_matrix_present_flag) {
|
||||
DVLOG(4) << "Scaling matrix present";
|
||||
res = ParseSPSScalingLists(sps.get());
|
||||
res = ParseSPSScalingLists(br, sps.get());
|
||||
if (res != kOk)
|
||||
return res;
|
||||
} else {
|
||||
|
@ -870,7 +742,7 @@ H264Parser::Result H264Parser::ParseSPS(int* sps_id) {
|
|||
READ_BOOL_OR_RETURN(&sps->vui_parameters_present_flag);
|
||||
if (sps->vui_parameters_present_flag) {
|
||||
DVLOG(4) << "VUI parameters present";
|
||||
res = ParseVUIParameters(sps.get());
|
||||
res = ParseVUIParameters(br, sps.get());
|
||||
if (res != kOk)
|
||||
return res;
|
||||
}
|
||||
|
@ -883,10 +755,13 @@ H264Parser::Result H264Parser::ParseSPS(int* sps_id) {
|
|||
return kOk;
|
||||
}
|
||||
|
||||
H264Parser::Result H264Parser::ParsePPS(int* pps_id) {
|
||||
H264Parser::Result H264Parser::ParsePPS(const Nalu& nalu, int* pps_id) {
|
||||
// See 7.4.2.2.
|
||||
const H264SPS* sps;
|
||||
Result res;
|
||||
H264BitReader reader;
|
||||
reader.Initialize(nalu.data() + nalu.header_size(), nalu.data_size());
|
||||
H264BitReader* br = &reader;
|
||||
|
||||
*pps_id = -1;
|
||||
|
||||
|
@ -932,13 +807,13 @@ H264Parser::Result H264Parser::ParsePPS(int* pps_id) {
|
|||
READ_BOOL_OR_RETURN(&pps->constrained_intra_pred_flag);
|
||||
READ_BOOL_OR_RETURN(&pps->redundant_pic_cnt_present_flag);
|
||||
|
||||
if (br_.HasMoreRBSPData()) {
|
||||
if (br->HasMoreRBSPData()) {
|
||||
READ_BOOL_OR_RETURN(&pps->transform_8x8_mode_flag);
|
||||
READ_BOOL_OR_RETURN(&pps->pic_scaling_matrix_present_flag);
|
||||
|
||||
if (pps->pic_scaling_matrix_present_flag) {
|
||||
DVLOG(4) << "Picture scaling matrix present";
|
||||
res = ParsePPSScalingLists(*sps, pps.get());
|
||||
res = ParsePPSScalingLists(br, *sps, pps.get());
|
||||
if (res != kOk)
|
||||
return res;
|
||||
}
|
||||
|
@ -954,29 +829,8 @@ H264Parser::Result H264Parser::ParsePPS(int* pps_id) {
|
|||
return kOk;
|
||||
}
|
||||
|
||||
H264Parser::Result H264Parser::ParseSPSFromArray(
|
||||
const uint8_t* sps_data,
|
||||
size_t sps_data_length,
|
||||
int* sps_id) {
|
||||
br_.Initialize(sps_data, sps_data_length);
|
||||
|
||||
int data;
|
||||
READ_BITS_OR_RETURN(1, &data);
|
||||
// First bit must be 0.
|
||||
TRUE_OR_RETURN(data == 0);
|
||||
int nal_ref_idc;
|
||||
READ_BITS_OR_RETURN(2, &nal_ref_idc);
|
||||
// From the spec "nal_ref_idc shall not be equal to 0 for sequence parameter
|
||||
// set".
|
||||
TRUE_OR_RETURN(nal_ref_idc != 0);
|
||||
int nal_unit_type;
|
||||
READ_BITS_OR_RETURN(5, &nal_unit_type);
|
||||
TRUE_OR_RETURN(nal_unit_type == H264NALU::kSPS);
|
||||
|
||||
return ParseSPS(sps_id);
|
||||
}
|
||||
|
||||
H264Parser::Result H264Parser::ParseRefPicListModification(
|
||||
H264BitReader* br,
|
||||
int num_ref_idx_active_minus1,
|
||||
H264ModificationOfPicNum* ref_list_mods) {
|
||||
H264ModificationOfPicNum* pic_num_mod;
|
||||
|
@ -1020,13 +874,13 @@ H264Parser::Result H264Parser::ParseRefPicListModification(
|
|||
}
|
||||
|
||||
H264Parser::Result H264Parser::ParseRefPicListModifications(
|
||||
H264SliceHeader* shdr) {
|
||||
H264BitReader* br, H264SliceHeader* shdr) {
|
||||
Result res;
|
||||
|
||||
if (!shdr->IsISlice() && !shdr->IsSISlice()) {
|
||||
READ_BOOL_OR_RETURN(&shdr->ref_pic_list_modification_flag_l0);
|
||||
if (shdr->ref_pic_list_modification_flag_l0) {
|
||||
res = ParseRefPicListModification(shdr->num_ref_idx_l0_active_minus1,
|
||||
res = ParseRefPicListModification(br, shdr->num_ref_idx_l0_active_minus1,
|
||||
shdr->ref_list_l0_modifications);
|
||||
if (res != kOk)
|
||||
return res;
|
||||
|
@ -1036,7 +890,7 @@ H264Parser::Result H264Parser::ParseRefPicListModifications(
|
|||
if (shdr->IsBSlice()) {
|
||||
READ_BOOL_OR_RETURN(&shdr->ref_pic_list_modification_flag_l1);
|
||||
if (shdr->ref_pic_list_modification_flag_l1) {
|
||||
res = ParseRefPicListModification(shdr->num_ref_idx_l1_active_minus1,
|
||||
res = ParseRefPicListModification(br, shdr->num_ref_idx_l1_active_minus1,
|
||||
shdr->ref_list_l1_modifications);
|
||||
if (res != kOk)
|
||||
return res;
|
||||
|
@ -1047,12 +901,12 @@ H264Parser::Result H264Parser::ParseRefPicListModifications(
|
|||
}
|
||||
|
||||
H264Parser::Result H264Parser::ParseWeightingFactors(
|
||||
H264BitReader* br,
|
||||
int num_ref_idx_active_minus1,
|
||||
int chroma_array_type,
|
||||
int luma_log2_weight_denom,
|
||||
int chroma_log2_weight_denom,
|
||||
H264WeightingFactors* w_facts) {
|
||||
|
||||
int def_luma_weight = 1 << luma_log2_weight_denom;
|
||||
int def_chroma_weight = 1 << chroma_log2_weight_denom;
|
||||
|
||||
|
@ -1091,7 +945,8 @@ H264Parser::Result H264Parser::ParseWeightingFactors(
|
|||
return kOk;
|
||||
}
|
||||
|
||||
H264Parser::Result H264Parser::ParsePredWeightTable(const H264SPS& sps,
|
||||
H264Parser::Result H264Parser::ParsePredWeightTable(H264BitReader* br,
|
||||
const H264SPS& sps,
|
||||
H264SliceHeader* shdr) {
|
||||
READ_UE_OR_RETURN(&shdr->luma_log2_weight_denom);
|
||||
TRUE_OR_RETURN(shdr->luma_log2_weight_denom < 8);
|
||||
|
@ -1100,7 +955,8 @@ H264Parser::Result H264Parser::ParsePredWeightTable(const H264SPS& sps,
|
|||
READ_UE_OR_RETURN(&shdr->chroma_log2_weight_denom);
|
||||
TRUE_OR_RETURN(shdr->chroma_log2_weight_denom < 8);
|
||||
|
||||
Result res = ParseWeightingFactors(shdr->num_ref_idx_l0_active_minus1,
|
||||
Result res = ParseWeightingFactors(br,
|
||||
shdr->num_ref_idx_l0_active_minus1,
|
||||
sps.chroma_array_type,
|
||||
shdr->luma_log2_weight_denom,
|
||||
shdr->chroma_log2_weight_denom,
|
||||
|
@ -1109,7 +965,8 @@ H264Parser::Result H264Parser::ParsePredWeightTable(const H264SPS& sps,
|
|||
return res;
|
||||
|
||||
if (shdr->IsBSlice()) {
|
||||
res = ParseWeightingFactors(shdr->num_ref_idx_l1_active_minus1,
|
||||
res = ParseWeightingFactors(br,
|
||||
shdr->num_ref_idx_l1_active_minus1,
|
||||
sps.chroma_array_type,
|
||||
shdr->luma_log2_weight_denom,
|
||||
shdr->chroma_log2_weight_denom,
|
||||
|
@ -1121,7 +978,8 @@ H264Parser::Result H264Parser::ParsePredWeightTable(const H264SPS& sps,
|
|||
return kOk;
|
||||
}
|
||||
|
||||
H264Parser::Result H264Parser::ParseDecRefPicMarking(H264SliceHeader* shdr) {
|
||||
H264Parser::Result H264Parser::ParseDecRefPicMarking(H264BitReader* br,
|
||||
H264SliceHeader* shdr) {
|
||||
if (shdr->idr_pic_flag) {
|
||||
READ_BOOL_OR_RETURN(&shdr->no_output_of_prior_pics_flag);
|
||||
READ_BOOL_OR_RETURN(&shdr->long_term_reference_flag);
|
||||
|
@ -1166,19 +1024,22 @@ H264Parser::Result H264Parser::ParseDecRefPicMarking(H264SliceHeader* shdr) {
|
|||
return kOk;
|
||||
}
|
||||
|
||||
H264Parser::Result H264Parser::ParseSliceHeader(const H264NALU& nalu,
|
||||
H264Parser::Result H264Parser::ParseSliceHeader(const Nalu& nalu,
|
||||
H264SliceHeader* shdr) {
|
||||
// See 7.4.3.
|
||||
const H264SPS* sps;
|
||||
const H264PPS* pps;
|
||||
Result res;
|
||||
H264BitReader reader;
|
||||
reader.Initialize(nalu.data() + nalu.header_size(), nalu.data_size());
|
||||
H264BitReader* br = &reader;
|
||||
|
||||
memset(shdr, 0, sizeof(*shdr));
|
||||
|
||||
shdr->idr_pic_flag = (nalu.nal_unit_type == 5);
|
||||
shdr->nal_ref_idc = nalu.nal_ref_idc;
|
||||
shdr->nalu_data = nalu.data;
|
||||
shdr->nalu_size = nalu.size;
|
||||
shdr->idr_pic_flag = (nalu.type() == 5);
|
||||
shdr->nal_ref_idc = nalu.ref_idc();
|
||||
shdr->nalu_data = nalu.data() + nalu.header_size();
|
||||
shdr->nalu_size = nalu.data_size();
|
||||
|
||||
READ_UE_OR_RETURN(&shdr->first_mb_in_slice);
|
||||
READ_UE_OR_RETURN(&shdr->slice_type);
|
||||
|
@ -1255,23 +1116,23 @@ H264Parser::Result H264Parser::ParseSliceHeader(const H264NALU& nalu,
|
|||
TRUE_OR_RETURN(shdr->num_ref_idx_l1_active_minus1 < 16);
|
||||
}
|
||||
|
||||
if (nalu.nal_unit_type == H264NALU::kCodedSliceExtension) {
|
||||
if (nalu.type() == Nalu::H264_CodedSliceExtension) {
|
||||
return kUnsupportedStream;
|
||||
} else {
|
||||
res = ParseRefPicListModifications(shdr);
|
||||
res = ParseRefPicListModifications(br, shdr);
|
||||
if (res != kOk)
|
||||
return res;
|
||||
}
|
||||
|
||||
if ((pps->weighted_pred_flag && (shdr->IsPSlice() || shdr->IsSPSlice())) ||
|
||||
(pps->weighted_bipred_idc == 1 && shdr->IsBSlice())) {
|
||||
res = ParsePredWeightTable(*sps, shdr);
|
||||
res = ParsePredWeightTable(br, *sps, shdr);
|
||||
if (res != kOk)
|
||||
return res;
|
||||
}
|
||||
|
||||
if (nalu.nal_ref_idc != 0) {
|
||||
res = ParseDecRefPicMarking(shdr);
|
||||
if (nalu.ref_idc() != 0) {
|
||||
res = ParseDecRefPicMarking(br, shdr);
|
||||
if (res != kOk)
|
||||
return res;
|
||||
}
|
||||
|
@ -1308,14 +1169,18 @@ H264Parser::Result H264Parser::ParseSliceHeader(const H264NALU& nalu,
|
|||
return kUnsupportedStream;
|
||||
}
|
||||
|
||||
size_t epb = br_.NumEmulationPreventionBytesRead();
|
||||
shdr->header_bit_size = (shdr->nalu_size - epb) * 8 - br_.NumBitsLeft();
|
||||
size_t epb = br->NumEmulationPreventionBytesRead();
|
||||
shdr->header_bit_size = (shdr->nalu_size - epb) * 8 - br->NumBitsLeft();
|
||||
|
||||
return kOk;
|
||||
}
|
||||
|
||||
H264Parser::Result H264Parser::ParseSEI(H264SEIMessage* sei_msg) {
|
||||
H264Parser::Result H264Parser::ParseSEI(const Nalu& nalu,
|
||||
H264SEIMessage* sei_msg) {
|
||||
int byte;
|
||||
H264BitReader reader;
|
||||
reader.Initialize(nalu.data() + nalu.header_size(), nalu.data_size());
|
||||
H264BitReader* br = &reader;
|
||||
|
||||
memset(sei_msg, 0, sizeof(*sei_msg));
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <map>
|
||||
|
||||
#include "packager/media/filters/h264_bit_reader.h"
|
||||
#include "packager/media/filters/nalu_reader.h"
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
|
@ -27,33 +28,6 @@ bool ExtractResolutionFromSps(const H264SPS& sps,
|
|||
uint32_t* pixel_width,
|
||||
uint32_t* pixel_height);
|
||||
|
||||
// For explanations of each struct and its members, see H.264 specification
|
||||
// at http://www.itu.int/rec/T-REC-H.264.
|
||||
struct H264NALU {
|
||||
H264NALU();
|
||||
|
||||
enum Type {
|
||||
kUnspecified = 0,
|
||||
kNonIDRSlice = 1,
|
||||
kIDRSlice = 5,
|
||||
kSEIMessage = 6,
|
||||
kSPS = 7,
|
||||
kPPS = 8,
|
||||
kAUD = 9,
|
||||
kEOSeq = 10,
|
||||
kEOStream = 11,
|
||||
kCodedSliceExtension = 20,
|
||||
};
|
||||
|
||||
// After (without) start code; we don't own the underlying memory
|
||||
// and a shallow copy should be made when copying this struct.
|
||||
const uint8_t* data;
|
||||
off_t size; // From after start code to start code of next NALU (or EOS).
|
||||
|
||||
int nal_ref_idc;
|
||||
int nal_unit_type;
|
||||
};
|
||||
|
||||
enum {
|
||||
kH264ScalingList4x4Length = 16,
|
||||
kH264ScalingList8x8Length = 64,
|
||||
|
@ -276,40 +250,10 @@ class H264Parser {
|
|||
kEOStream, // end of stream
|
||||
};
|
||||
|
||||
// Find offset from start of data to next NALU start code
|
||||
// and size of found start code (3 or 4 bytes).
|
||||
// If no start code is found, offset is pointing to the first unprocessed byte
|
||||
// (i.e. the first byte that was not considered as a possible start of a start
|
||||
// code) and |*start_code_size| is set to 0.
|
||||
// Preconditions:
|
||||
// - |data_size| >= 0
|
||||
// Postconditions:
|
||||
// - |*offset| is between 0 and |data_size| included.
|
||||
// It is strictly less than |data_size| if |data_size| > 0.
|
||||
// - |*start_code_size| is either 0, 3 or 4.
|
||||
static bool FindStartCode(const uint8_t* data,
|
||||
off_t data_size,
|
||||
off_t* offset,
|
||||
off_t* start_code_size);
|
||||
|
||||
H264Parser();
|
||||
~H264Parser();
|
||||
|
||||
void Reset();
|
||||
// Set current stream pointer to |stream| of |stream_size| in bytes,
|
||||
// |stream| owned by caller.
|
||||
void SetStream(const uint8_t* stream, off_t stream_size);
|
||||
|
||||
// Read the stream to find the next NALU, identify it and return
|
||||
// that information in |*nalu|. This advances the stream to the beginning
|
||||
// of this NALU, but not past it, so subsequent calls to NALU-specific
|
||||
// parsing functions (ParseSPS, etc.) will parse this NALU.
|
||||
// If the caller wishes to skip the current NALU, it can call this function
|
||||
// again, instead of any NALU-type specific parse functions below.
|
||||
Result AdvanceToNextNALU(H264NALU* nalu);
|
||||
|
||||
// NALU-specific parsing functions.
|
||||
// These should be called after AdvanceToNextNALU().
|
||||
|
||||
// SPSes and PPSes are owned by the parser class and the memory for their
|
||||
// structures is managed here, not by the caller, as they are reused
|
||||
|
@ -319,15 +263,8 @@ class H264Parser {
|
|||
// of the parsed structure in |*pps_id|/|*sps_id|.
|
||||
// To get a pointer to a given SPS/PPS structure, use GetSPS()/GetPPS(),
|
||||
// passing the returned |*sps_id|/|*pps_id| as parameter.
|
||||
// methods with a scoped_ptr and adding an AtEOS() function to check for EOS
|
||||
// if Parse*() return NULL.
|
||||
Result ParseSPS(int* sps_id);
|
||||
Result ParsePPS(int* pps_id);
|
||||
|
||||
// Samme as ParseSPS but instead uses |sps_data|.
|
||||
Result ParseSPSFromArray(const uint8_t* sps_data,
|
||||
size_t sps_data_size,
|
||||
int* sps_id);
|
||||
Result ParseSPS(const Nalu& nalu, int* sps_id);
|
||||
Result ParsePPS(const Nalu& nalu, int* pps_id);
|
||||
|
||||
// Return a pointer to SPS/PPS with given |sps_id|/|pps_id| or NULL if not
|
||||
// present.
|
||||
|
@ -341,64 +278,57 @@ class H264Parser {
|
|||
|
||||
// Parse a slice header, returning it in |*shdr|. |*nalu| must be set to
|
||||
// the NALU returned from AdvanceToNextNALU() and corresponding to |*shdr|.
|
||||
Result ParseSliceHeader(const H264NALU& nalu, H264SliceHeader* shdr);
|
||||
Result ParseSliceHeader(const Nalu& nalu, H264SliceHeader* shdr);
|
||||
|
||||
// Parse a SEI message, returning it in |*sei_msg|, provided and managed
|
||||
// by the caller.
|
||||
Result ParseSEI(H264SEIMessage* sei_msg);
|
||||
Result ParseSEI(const Nalu& nalu, H264SEIMessage* sei_msg);
|
||||
|
||||
private:
|
||||
// Move the stream pointer to the beginning of the next NALU,
|
||||
// i.e. pointing at the next start code.
|
||||
// Return true if a NALU has been found.
|
||||
// If a NALU is found:
|
||||
// - its size in bytes is returned in |*nalu_size| and includes
|
||||
// the start code as well as the trailing zero bits.
|
||||
// - the size in bytes of the start code is returned in |*start_code_size|.
|
||||
bool LocateNALU(off_t* nalu_size, off_t* start_code_size);
|
||||
|
||||
// Exp-Golomb code parsing as specified in chapter 9.1 of the spec.
|
||||
// Read one unsigned exp-Golomb code from the stream and return in |*val|.
|
||||
Result ReadUE(int* val);
|
||||
Result ReadUE(H264BitReader* br, int* val);
|
||||
|
||||
// Read one signed exp-Golomb code from the stream and return in |*val|.
|
||||
Result ReadSE(int* val);
|
||||
Result ReadSE(H264BitReader* br, int* val);
|
||||
|
||||
// Parse scaling lists (see spec).
|
||||
Result ParseScalingList(int size, int* scaling_list, bool* use_default);
|
||||
Result ParseSPSScalingLists(H264SPS* sps);
|
||||
Result ParsePPSScalingLists(const H264SPS& sps, H264PPS* pps);
|
||||
Result ParseScalingList(H264BitReader* br,
|
||||
int size,
|
||||
int* scaling_list,
|
||||
bool* use_default);
|
||||
Result ParseSPSScalingLists(H264BitReader* br, H264SPS* sps);
|
||||
Result ParsePPSScalingLists(H264BitReader* br,
|
||||
const H264SPS& sps,
|
||||
H264PPS* pps);
|
||||
|
||||
// Parse optional VUI parameters in SPS (see spec).
|
||||
Result ParseVUIParameters(H264SPS* sps);
|
||||
Result ParseVUIParameters(H264BitReader* br, H264SPS* sps);
|
||||
// Set |hrd_parameters_present| to true only if they are present.
|
||||
Result ParseAndIgnoreHRDParameters(bool* hrd_parameters_present);
|
||||
Result ParseAndIgnoreHRDParameters(H264BitReader* br,
|
||||
bool* hrd_parameters_present);
|
||||
|
||||
// Parse reference picture lists' modifications (see spec).
|
||||
Result ParseRefPicListModifications(H264SliceHeader* shdr);
|
||||
Result ParseRefPicListModification(int num_ref_idx_active_minus1,
|
||||
Result ParseRefPicListModifications(H264BitReader* br, H264SliceHeader* shdr);
|
||||
Result ParseRefPicListModification(H264BitReader* br,
|
||||
int num_ref_idx_active_minus1,
|
||||
H264ModificationOfPicNum* ref_list_mods);
|
||||
|
||||
// Parse prediction weight table (see spec).
|
||||
Result ParsePredWeightTable(const H264SPS& sps, H264SliceHeader* shdr);
|
||||
Result ParsePredWeightTable(H264BitReader* br,
|
||||
const H264SPS& sps,
|
||||
H264SliceHeader* shdr);
|
||||
|
||||
// Parse weighting factors (see spec).
|
||||
Result ParseWeightingFactors(int num_ref_idx_active_minus1,
|
||||
Result ParseWeightingFactors(H264BitReader* br,
|
||||
int num_ref_idx_active_minus1,
|
||||
int chroma_array_type,
|
||||
int luma_log2_weight_denom,
|
||||
int chroma_log2_weight_denom,
|
||||
H264WeightingFactors* w_facts);
|
||||
|
||||
// Parse decoded reference picture marking information (see spec).
|
||||
Result ParseDecRefPicMarking(H264SliceHeader* shdr);
|
||||
|
||||
// Pointer to the current NALU in the stream.
|
||||
const uint8_t* stream_;
|
||||
|
||||
// Bytes left in the stream after the current NALU.
|
||||
off_t bytes_left_;
|
||||
|
||||
H264BitReader br_;
|
||||
Result ParseDecRefPicMarking(H264BitReader* br, H264SliceHeader* shdr);
|
||||
|
||||
// PPSes and SPSes stored for future reference.
|
||||
typedef std::map<int, H264SPS*> SPSById;
|
||||
|
|
|
@ -12,6 +12,12 @@
|
|||
namespace edash_packager {
|
||||
namespace media {
|
||||
|
||||
namespace {
|
||||
// The test data does not include a start code, the start of the data is the
|
||||
// NALU header.
|
||||
const uint8_t kStartCodeSize = 0;
|
||||
}
|
||||
|
||||
TEST(H264ParserTest, StreamFileParsing) {
|
||||
std::vector<uint8_t> buffer = ReadTestDataFile("test-25fps.h264");
|
||||
|
||||
|
@ -19,42 +25,43 @@ TEST(H264ParserTest, StreamFileParsing) {
|
|||
int num_nalus = 759;
|
||||
|
||||
H264Parser parser;
|
||||
parser.SetStream(vector_as_array(&buffer), buffer.size());
|
||||
NaluReader reader(kIsAnnexbByteStream, vector_as_array(&buffer),
|
||||
buffer.size());
|
||||
|
||||
// Parse until the end of stream/unsupported stream/error in stream is found.
|
||||
int num_parsed_nalus = 0;
|
||||
while (true) {
|
||||
H264SliceHeader shdr;
|
||||
H264SEIMessage sei_msg;
|
||||
H264NALU nalu;
|
||||
H264Parser::Result res = parser.AdvanceToNextNALU(&nalu);
|
||||
if (res == H264Parser::kEOStream) {
|
||||
Nalu nalu;
|
||||
NaluReader::Result res = reader.Advance(&nalu);
|
||||
if (res == NaluReader::kEOStream) {
|
||||
DVLOG(1) << "Number of successfully parsed NALUs before EOS: "
|
||||
<< num_parsed_nalus;
|
||||
ASSERT_EQ(num_nalus, num_parsed_nalus);
|
||||
return;
|
||||
}
|
||||
ASSERT_EQ(res, H264Parser::kOk);
|
||||
ASSERT_EQ(res, NaluReader::kOk);
|
||||
|
||||
++num_parsed_nalus;
|
||||
|
||||
int id;
|
||||
switch (nalu.nal_unit_type) {
|
||||
case H264NALU::kIDRSlice:
|
||||
case H264NALU::kNonIDRSlice:
|
||||
switch (nalu.type()) {
|
||||
case Nalu::H264_IDRSlice:
|
||||
case Nalu::H264_NonIDRSlice:
|
||||
ASSERT_EQ(parser.ParseSliceHeader(nalu, &shdr), H264Parser::kOk);
|
||||
break;
|
||||
|
||||
case H264NALU::kSPS:
|
||||
ASSERT_EQ(parser.ParseSPS(&id), H264Parser::kOk);
|
||||
case Nalu::H264_SPS:
|
||||
ASSERT_EQ(parser.ParseSPS(nalu, &id), H264Parser::kOk);
|
||||
break;
|
||||
|
||||
case H264NALU::kPPS:
|
||||
ASSERT_EQ(parser.ParsePPS(&id), H264Parser::kOk);
|
||||
case Nalu::H264_PPS:
|
||||
ASSERT_EQ(parser.ParsePPS(nalu, &id), H264Parser::kOk);
|
||||
break;
|
||||
|
||||
case H264NALU::kSEIMessage:
|
||||
ASSERT_EQ(parser.ParseSEI(&sei_msg), H264Parser::kOk);
|
||||
case Nalu::H264_SEIMessage:
|
||||
ASSERT_EQ(parser.ParseSEI(nalu, &sei_msg), H264Parser::kOk);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -73,8 +80,9 @@ TEST(H264ParserTest, ExtractResolutionFromSpsData) {
|
|||
|
||||
H264Parser parser;
|
||||
int sps_id = 0;
|
||||
ASSERT_EQ(H264Parser::kOk,
|
||||
parser.ParseSPSFromArray(kSps, arraysize(kSps), &sps_id));
|
||||
Nalu nalu;
|
||||
ASSERT_TRUE(nalu.InitializeFromH264(kSps, arraysize(kSps), kStartCodeSize));
|
||||
ASSERT_EQ(H264Parser::kOk, parser.ParseSPS(nalu, &sps_id));
|
||||
|
||||
uint32_t coded_width = 0;
|
||||
uint32_t coded_height = 0;
|
||||
|
@ -97,8 +105,9 @@ TEST(H264ParserTest, ExtractResolutionFromSpsDataWithCropping) {
|
|||
|
||||
H264Parser parser;
|
||||
int sps_id = 0;
|
||||
ASSERT_EQ(H264Parser::kOk,
|
||||
parser.ParseSPSFromArray(kSps, arraysize(kSps), &sps_id));
|
||||
Nalu nalu;
|
||||
ASSERT_TRUE(nalu.InitializeFromH264(kSps, arraysize(kSps), kStartCodeSize));
|
||||
ASSERT_EQ(H264Parser::kOk, parser.ParseSPS(nalu, &sps_id));
|
||||
|
||||
uint32_t coded_width = 0;
|
||||
uint32_t coded_height = 0;
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
// 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/nalu_reader.h"
|
||||
|
||||
#include "packager/base/logging.h"
|
||||
#include "packager/media/base/buffer_reader.h"
|
||||
#include "packager/media/filters/h264_parser.h"
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
|
||||
namespace {
|
||||
inline bool IsStartCode(const uint8_t* data) {
|
||||
return data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x01;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Nalu::Nalu()
|
||||
: data_(nullptr),
|
||||
data_size_(0),
|
||||
header_size_(0),
|
||||
ref_idc_(0),
|
||||
type_(0),
|
||||
is_video_slice_(false) {}
|
||||
|
||||
bool Nalu::InitializeFromH264(const uint8_t* data,
|
||||
uint64_t size,
|
||||
uint8_t start_code_size) {
|
||||
DCHECK(data);
|
||||
DCHECK_GT(size, start_code_size);
|
||||
uint8_t header = data[start_code_size];
|
||||
if ((header & 0x80) != 0)
|
||||
return false;
|
||||
|
||||
data_ = data;
|
||||
header_size_ = start_code_size + 1;
|
||||
data_size_ = size - start_code_size - 1;
|
||||
ref_idc_ = (header >> 5) & 0x3;
|
||||
type_ = header & 0x1F;
|
||||
is_video_slice_ = (type_ >= Nalu::H264_NonIDRSlice &&
|
||||
type_ <= Nalu::H264_IDRSlice);
|
||||
return true;
|
||||
}
|
||||
|
||||
NaluReader::NaluReader(uint8_t nal_length_size,
|
||||
const uint8_t* stream,
|
||||
uint64_t stream_size)
|
||||
: stream_(stream),
|
||||
stream_size_(stream_size),
|
||||
nalu_length_size_(nal_length_size),
|
||||
format_(nal_length_size == 0 ? kAnnexbByteStreamFormat
|
||||
: kNalUnitStreamFormat) {
|
||||
DCHECK(stream);
|
||||
}
|
||||
NaluReader::~NaluReader() {}
|
||||
|
||||
NaluReader::Result NaluReader::Advance(Nalu* nalu) {
|
||||
if (stream_size_ <= 0)
|
||||
return NaluReader::kEOStream;
|
||||
|
||||
uint8_t nalu_length_size_or_start_code_size;
|
||||
uint64_t nalu_length_with_header;
|
||||
if (format_ == kAnnexbByteStreamFormat) {
|
||||
// This will move |stream_| to the start code.
|
||||
if (!LocateNaluByStartCode(&nalu_length_with_header,
|
||||
&nalu_length_size_or_start_code_size)) {
|
||||
LOG(ERROR) << "Could not find next NALU, bytes left in stream: "
|
||||
<< stream_size_;
|
||||
// This is actually an error. Since we always move to past the end of
|
||||
// each NALU, if there is no next start code, then this is the first call
|
||||
// and there are no start codes in the stream.
|
||||
return NaluReader::kInvalidStream;
|
||||
}
|
||||
} else {
|
||||
uint64_t nalu_length;
|
||||
BufferReader reader(stream_, stream_size_);
|
||||
if (!reader.ReadNBytesInto8(&nalu_length, nalu_length_size_))
|
||||
return NaluReader::kInvalidStream;
|
||||
nalu_length_size_or_start_code_size = nalu_length_size_;
|
||||
|
||||
if (nalu_length + nalu_length_size_ > stream_size_) {
|
||||
LOG(ERROR) << "NALU length exceeds stream size: "
|
||||
<< stream_size_ << " < " << nalu_length;
|
||||
return NaluReader::kInvalidStream;
|
||||
}
|
||||
if (nalu_length == 0) {
|
||||
LOG(ERROR) << "NALU size 0";
|
||||
return NaluReader::kInvalidStream;
|
||||
}
|
||||
nalu_length_with_header = nalu_length + nalu_length_size_;
|
||||
}
|
||||
|
||||
if (!nalu->InitializeFromH264(stream_, nalu_length_with_header,
|
||||
nalu_length_size_or_start_code_size))
|
||||
return NaluReader::kInvalidStream;
|
||||
|
||||
// Move parser state to after this NALU, so next time Advance
|
||||
// is called, we will effectively be skipping it.
|
||||
stream_ += nalu_length_with_header;
|
||||
stream_size_ -= nalu_length_with_header;
|
||||
|
||||
DVLOG(4) << "NALU type: " << static_cast<int>(nalu->type())
|
||||
<< " at: " << reinterpret_cast<const void*>(nalu->data())
|
||||
<< " data size: " << nalu->data_size()
|
||||
<< " ref: " << static_cast<int>(nalu->ref_idc());
|
||||
|
||||
return NaluReader::kOk;
|
||||
}
|
||||
|
||||
// static
|
||||
bool NaluReader::FindStartCode(const uint8_t* data,
|
||||
uint64_t data_size,
|
||||
uint64_t* offset,
|
||||
uint8_t* start_code_size) {
|
||||
uint64_t bytes_left = data_size;
|
||||
|
||||
while (bytes_left >= 3) {
|
||||
if (IsStartCode(data)) {
|
||||
// Found three-byte start code, set pointer at its beginning.
|
||||
*offset = data_size - bytes_left;
|
||||
*start_code_size = 3;
|
||||
|
||||
// If there is a zero byte before this start code,
|
||||
// then it's actually a four-byte start code, so backtrack one byte.
|
||||
if (*offset > 0 && *(data - 1) == 0x00) {
|
||||
--(*offset);
|
||||
++(*start_code_size);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
++data;
|
||||
--bytes_left;
|
||||
}
|
||||
|
||||
// End of data: offset is pointing to the first byte that was not considered
|
||||
// as a possible start of a start code.
|
||||
*offset = data_size - bytes_left;
|
||||
*start_code_size = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NaluReader::LocateNaluByStartCode(uint64_t* nalu_size,
|
||||
uint8_t* start_code_size) {
|
||||
// Find the start code of next NALU.
|
||||
uint64_t nalu_start_off = 0;
|
||||
uint8_t annexb_start_code_size = 0;
|
||||
if (!FindStartCode(stream_, stream_size_,
|
||||
&nalu_start_off, &annexb_start_code_size)) {
|
||||
DVLOG(4) << "Could not find start code, end of stream?";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move the stream to the beginning of the NALU (pointing at the start code).
|
||||
stream_ += nalu_start_off;
|
||||
stream_size_ -= nalu_start_off;
|
||||
|
||||
const uint8_t* nalu_data = stream_ + annexb_start_code_size;
|
||||
uint64_t max_nalu_data_size = stream_size_ - annexb_start_code_size;
|
||||
if (max_nalu_data_size <= 0) {
|
||||
DVLOG(3) << "End of stream";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the start code of next NALU;
|
||||
// if successful, |nalu_size_without_start_code| is the number of bytes from
|
||||
// after previous start code to before this one;
|
||||
// if next start code is not found, it is still a valid NALU since there
|
||||
// are some bytes left after the first start code: all the remaining bytes
|
||||
// belong to the current NALU.
|
||||
uint64_t nalu_size_without_start_code = 0;
|
||||
uint8_t next_start_code_size = 0;
|
||||
if (!FindStartCode(nalu_data, max_nalu_data_size,
|
||||
&nalu_size_without_start_code, &next_start_code_size)) {
|
||||
nalu_size_without_start_code = max_nalu_data_size;
|
||||
}
|
||||
*nalu_size = nalu_size_without_start_code + annexb_start_code_size;
|
||||
*start_code_size = annexb_start_code_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace media
|
||||
} // namespace edash_packager
|
|
@ -0,0 +1,139 @@
|
|||
// 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_NALU_READER_H_
|
||||
#define MEDIA_FILTERS_NALU_READER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "packager/base/compiler_specific.h"
|
||||
#include "packager/base/macros.h"
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
|
||||
// Used as the |nalu_length_size| argument to NaluReader to indicate to use
|
||||
// AnnexB byte streams. An AnnexB byte stream starts with 3 or 4 byte start
|
||||
// codes instead of a fixed size NAL unit length.
|
||||
const uint8_t kIsAnnexbByteStream = 0;
|
||||
|
||||
/// For explanations of each struct and its members, see H.264 specification
|
||||
/// at http://www.itu.int/rec/T-REC-H.264.
|
||||
class Nalu {
|
||||
public:
|
||||
enum H264NaluType {
|
||||
H264_Unspecified = 0,
|
||||
H264_NonIDRSlice = 1,
|
||||
H264_IDRSlice = 5,
|
||||
H264_SEIMessage = 6,
|
||||
H264_SPS = 7,
|
||||
H264_PPS = 8,
|
||||
H264_AUD = 9,
|
||||
H264_EOSeq = 10,
|
||||
H264_CodedSliceExtension = 20,
|
||||
};
|
||||
|
||||
Nalu();
|
||||
|
||||
bool InitializeFromH264(const uint8_t* data,
|
||||
uint64_t size,
|
||||
uint8_t start_code_size) WARN_UNUSED_RESULT;
|
||||
|
||||
const uint8_t* data() const { return data_; }
|
||||
uint64_t data_size() const { return data_size_; }
|
||||
uint64_t header_size() const { return header_size_; }
|
||||
|
||||
int ref_idc() const { return ref_idc_; }
|
||||
int type() const { return type_; }
|
||||
bool is_video_slice() const { return is_video_slice_; }
|
||||
|
||||
private:
|
||||
// A pointer to the NALU (i.e. points to the header). This pointer is not
|
||||
// owned by this instance.
|
||||
const uint8_t* data_;
|
||||
uint64_t data_size_;
|
||||
uint64_t header_size_;
|
||||
|
||||
int ref_idc_;
|
||||
int type_;
|
||||
bool is_video_slice_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Nalu);
|
||||
};
|
||||
|
||||
/// Helper class used to read NAL units based on several formats:
|
||||
/// * Annex B H.264/h.265
|
||||
/// * NAL Unit Stream
|
||||
class NaluReader {
|
||||
public:
|
||||
enum Result {
|
||||
kOk,
|
||||
kInvalidStream, // error in stream
|
||||
kEOStream, // end of stream
|
||||
};
|
||||
|
||||
/// @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
|
||||
/// unit stream.
|
||||
NaluReader(uint8_t nal_length_size,
|
||||
const uint8_t* stream,
|
||||
uint64_t stream_size);
|
||||
~NaluReader();
|
||||
|
||||
// Find offset from start of data to next NALU start code
|
||||
// and size of found start code (3 or 4 bytes).
|
||||
// If no start code is found, offset is pointing to the first unprocessed byte
|
||||
// (i.e. the first byte that was not considered as a possible start of a start
|
||||
// code) and |*start_code_size| is set to 0.
|
||||
// Postconditions:
|
||||
// - |*offset| is between 0 and |data_size| included.
|
||||
// It is strictly less than |data_size| if |data_size| > 0.
|
||||
// - |*start_code_size| is either 0, 3 or 4.
|
||||
static bool FindStartCode(const uint8_t* data,
|
||||
uint64_t data_size,
|
||||
uint64_t* offset,
|
||||
uint8_t* start_code_size);
|
||||
|
||||
/// Reads a NALU from the stream into |*nalu|, if one exists, and then
|
||||
/// advances to the next NALU.
|
||||
/// @param nalu contains the NALU read if it exists.
|
||||
/// @return kOk if a NALU is read; kEOStream if the stream is at the
|
||||
/// end-of-stream; kInvalidStream on error.
|
||||
Result Advance(Nalu* nalu);
|
||||
|
||||
private:
|
||||
enum Format {
|
||||
kAnnexbByteStreamFormat,
|
||||
kNalUnitStreamFormat
|
||||
};
|
||||
|
||||
// Move the stream pointer to the beginning of the next NALU,
|
||||
// i.e. pointing at the next start code.
|
||||
// Return true if a NALU has been found.
|
||||
// If a NALU is found:
|
||||
// - its size in bytes is returned in |*nalu_size| and includes
|
||||
// the start code as well as the trailing zero bits.
|
||||
// - the size in bytes of the start code is returned in |*start_code_size|.
|
||||
bool LocateNaluByStartCode(uint64_t* nalu_size, uint8_t* start_code_size);
|
||||
|
||||
// Pointer to the current NALU in the stream.
|
||||
const uint8_t* stream_;
|
||||
// The remaining size of the stream.
|
||||
uint64_t stream_size_;
|
||||
// The number of bytes the prefix length is; only valid if format is
|
||||
// kAnnexbByteStreamFormat.
|
||||
uint8_t nalu_length_size_;
|
||||
// The format of the stream.
|
||||
Format format_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NaluReader);
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
} // namespace edash_packager
|
||||
|
||||
#endif // MEDIA_FILTERS_NALU_READER_H_
|
|
@ -0,0 +1,145 @@
|
|||
// 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/filters/nalu_reader.h"
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
|
||||
TEST(NaluReaderTest, StartCodeSearch) {
|
||||
const uint8_t kNaluData[] = {
|
||||
0x01, 0x00, 0x00, 0x04, 0x23, 0x56,
|
||||
// First NALU
|
||||
0x00, 0x00, 0x01, 0x12, 0x34, 0x56, 0x78,
|
||||
// Second NALU
|
||||
0x00, 0x00, 0x00, 0x01, 0x67, 0xbb, 0xcc, 0xdd
|
||||
};
|
||||
|
||||
NaluReader reader(kIsAnnexbByteStream, kNaluData, arraysize(kNaluData));
|
||||
|
||||
Nalu nalu;
|
||||
ASSERT_EQ(NaluReader::kOk, reader.Advance(&nalu));
|
||||
EXPECT_EQ(kNaluData + 6, nalu.data());
|
||||
EXPECT_EQ(3u, nalu.data_size());
|
||||
EXPECT_EQ(4u, nalu.header_size());
|
||||
EXPECT_EQ(0, nalu.ref_idc());
|
||||
EXPECT_EQ(0x12, nalu.type());
|
||||
|
||||
ASSERT_EQ(NaluReader::kOk, reader.Advance(&nalu));
|
||||
EXPECT_EQ(kNaluData + 13, nalu.data());
|
||||
EXPECT_EQ(3u, nalu.data_size());
|
||||
EXPECT_EQ(5u, nalu.header_size());
|
||||
EXPECT_EQ(3, nalu.ref_idc());
|
||||
EXPECT_EQ(7, nalu.type());
|
||||
|
||||
EXPECT_EQ(NaluReader::kEOStream, reader.Advance(&nalu));
|
||||
}
|
||||
|
||||
TEST(NaluReaderTest, OneByteNaluLength) {
|
||||
const uint8_t kNaluData[] = {
|
||||
// First NALU
|
||||
0x05, 0x08, 0x01, 0x02, 0x03, 0x04,
|
||||
// Second NALU
|
||||
0x06, 0x67, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e
|
||||
};
|
||||
|
||||
NaluReader reader(1, kNaluData, arraysize(kNaluData));
|
||||
|
||||
Nalu nalu;
|
||||
ASSERT_EQ(NaluReader::kOk, reader.Advance(&nalu));
|
||||
EXPECT_EQ(kNaluData, nalu.data());
|
||||
EXPECT_EQ(4u, nalu.data_size());
|
||||
EXPECT_EQ(2u, nalu.header_size());
|
||||
EXPECT_EQ(0, nalu.ref_idc());
|
||||
EXPECT_EQ(8, nalu.type());
|
||||
|
||||
ASSERT_EQ(NaluReader::kOk, reader.Advance(&nalu));
|
||||
EXPECT_EQ(kNaluData + 6, nalu.data());
|
||||
EXPECT_EQ(5u, nalu.data_size());
|
||||
EXPECT_EQ(2u, nalu.header_size());
|
||||
EXPECT_EQ(3, nalu.ref_idc());
|
||||
EXPECT_EQ(7, nalu.type());
|
||||
|
||||
EXPECT_EQ(NaluReader::kEOStream, reader.Advance(&nalu));
|
||||
}
|
||||
|
||||
TEST(NaluReaderTest, ThreeByteNaluLength) {
|
||||
const uint8_t kNaluData[] = {
|
||||
// First NALU
|
||||
0x00, 0x00, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
|
||||
// Second NALU
|
||||
0x00, 0x00, 0x03, 0x67, 0x0a, 0x0b
|
||||
};
|
||||
|
||||
NaluReader reader(3, kNaluData, arraysize(kNaluData));
|
||||
|
||||
Nalu nalu;
|
||||
ASSERT_EQ(NaluReader::kOk, reader.Advance(&nalu));
|
||||
EXPECT_EQ(kNaluData, nalu.data());
|
||||
EXPECT_EQ(6u, nalu.data_size());
|
||||
EXPECT_EQ(4u, nalu.header_size());
|
||||
EXPECT_EQ(0, nalu.ref_idc());
|
||||
EXPECT_EQ(8, nalu.type());
|
||||
|
||||
ASSERT_EQ(NaluReader::kOk, reader.Advance(&nalu));
|
||||
EXPECT_EQ(kNaluData + 10, nalu.data());
|
||||
EXPECT_EQ(2u, nalu.data_size());
|
||||
EXPECT_EQ(4u, nalu.header_size());
|
||||
EXPECT_EQ(3, nalu.ref_idc());
|
||||
EXPECT_EQ(7, nalu.type());
|
||||
|
||||
EXPECT_EQ(NaluReader::kEOStream, reader.Advance(&nalu));
|
||||
}
|
||||
|
||||
TEST(NaluReaderTest, ErrorForNotEnoughForNaluLength) {
|
||||
const uint8_t kNaluData[] = {
|
||||
// First NALU
|
||||
0x00
|
||||
};
|
||||
|
||||
NaluReader reader(3, kNaluData, arraysize(kNaluData));
|
||||
|
||||
Nalu nalu;
|
||||
EXPECT_EQ(NaluReader::kInvalidStream, reader.Advance(&nalu));
|
||||
}
|
||||
|
||||
TEST(NaluReaderTest, ErrorForNaluLengthExceedsRemainingData) {
|
||||
const uint8_t kNaluData[] = {
|
||||
// First NALU
|
||||
0xFF, 0x08, 0x00
|
||||
};
|
||||
|
||||
NaluReader reader(1, kNaluData, arraysize(kNaluData));
|
||||
|
||||
Nalu nalu;
|
||||
EXPECT_EQ(NaluReader::kInvalidStream, reader.Advance(&nalu));
|
||||
|
||||
// Another test for off by one.
|
||||
const uint8_t kNaluData2[] = {
|
||||
// First NALU
|
||||
0x04, 0x08, 0x00, 0x00
|
||||
};
|
||||
|
||||
NaluReader reader2(1, kNaluData2, arraysize(kNaluData2));
|
||||
EXPECT_EQ(NaluReader::kInvalidStream, reader2.Advance(&nalu));
|
||||
}
|
||||
|
||||
TEST(NaluReaderTest, ErrorForForbiddenBitSet) {
|
||||
const uint8_t kNaluData[] = {
|
||||
// First NALU
|
||||
0x03, 0x80, 0x00, 0x00
|
||||
};
|
||||
|
||||
NaluReader reader(1, kNaluData, arraysize(kNaluData));
|
||||
|
||||
Nalu nalu;
|
||||
EXPECT_EQ(NaluReader::kInvalidStream, reader.Advance(&nalu));
|
||||
}
|
||||
|
||||
} // namespace media
|
||||
} // namespace edash_packager
|
|
@ -118,21 +118,23 @@ bool EsParserH264::FindAUD(int64_t* stream_pos) {
|
|||
es_queue_->PeekAt(*stream_pos, &es, &size);
|
||||
|
||||
// Find a start code and move the stream to the start code parser position.
|
||||
off_t start_code_offset;
|
||||
off_t start_code_size;
|
||||
bool start_code_found = H264Parser::FindStartCode(
|
||||
uint64_t start_code_offset;
|
||||
uint8_t start_code_size;
|
||||
bool start_code_found = NaluReader::FindStartCode(
|
||||
es, size, &start_code_offset, &start_code_size);
|
||||
*stream_pos += start_code_offset;
|
||||
|
||||
// No H264 start code found or NALU type not available yet.
|
||||
if (!start_code_found || start_code_offset + start_code_size >= size)
|
||||
if (!start_code_found ||
|
||||
start_code_offset + start_code_size >= static_cast<uint64_t>(size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Exit the parser loop when an AUD is found.
|
||||
// Note: NALU header for an AUD:
|
||||
// - nal_ref_idc must be 0
|
||||
// - nal_unit_type must be H264NALU::kAUD
|
||||
if (es[start_code_offset + start_code_size] == H264NALU::kAUD)
|
||||
// - ref_idc must be 0
|
||||
// - type must be Nalu::H264_AUD
|
||||
if (es[start_code_offset + start_code_size] == Nalu::H264_AUD)
|
||||
break;
|
||||
|
||||
// The current NALU is not an AUD, skip the start code
|
||||
|
@ -180,41 +182,40 @@ bool EsParserH264::ParseInternal() {
|
|||
int access_unit_size = base::checked_cast<int, int64_t>(
|
||||
next_access_unit_pos_ - current_access_unit_pos_);
|
||||
DCHECK_LE(access_unit_size, size);
|
||||
h264_parser_->SetStream(es, access_unit_size);
|
||||
NaluReader reader(kIsAnnexbByteStream, es, access_unit_size);
|
||||
|
||||
while (true) {
|
||||
Nalu nalu;
|
||||
bool is_eos = false;
|
||||
H264NALU nalu;
|
||||
switch (h264_parser_->AdvanceToNextNALU(&nalu)) {
|
||||
case H264Parser::kOk:
|
||||
switch (reader.Advance(&nalu)) {
|
||||
case NaluReader::kOk:
|
||||
break;
|
||||
case H264Parser::kInvalidStream:
|
||||
case H264Parser::kUnsupportedStream:
|
||||
return false;
|
||||
case H264Parser::kEOStream:
|
||||
case NaluReader::kEOStream:
|
||||
is_eos = true;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
if (is_eos)
|
||||
break;
|
||||
|
||||
switch (nalu.nal_unit_type) {
|
||||
case H264NALU::kAUD: {
|
||||
DVLOG(LOG_LEVEL_ES) << "NALU: AUD";
|
||||
switch (nalu.type()) {
|
||||
case Nalu::H264_AUD: {
|
||||
DVLOG(LOG_LEVEL_ES) << "Nalu: AUD";
|
||||
break;
|
||||
}
|
||||
case H264NALU::kSPS: {
|
||||
DVLOG(LOG_LEVEL_ES) << "NALU: SPS";
|
||||
case Nalu::H264_SPS: {
|
||||
DVLOG(LOG_LEVEL_ES) << "Nalu: SPS";
|
||||
int sps_id;
|
||||
if (h264_parser_->ParseSPS(&sps_id) != H264Parser::kOk)
|
||||
if (h264_parser_->ParseSPS(nalu, &sps_id) != H264Parser::kOk)
|
||||
return false;
|
||||
decoder_config_check_pending_ = true;
|
||||
break;
|
||||
}
|
||||
case H264NALU::kPPS: {
|
||||
DVLOG(LOG_LEVEL_ES) << "NALU: PPS";
|
||||
case Nalu::H264_PPS: {
|
||||
DVLOG(LOG_LEVEL_ES) << "Nalu: PPS";
|
||||
int pps_id;
|
||||
if (h264_parser_->ParsePPS(&pps_id) != H264Parser::kOk) {
|
||||
if (h264_parser_->ParsePPS(nalu, &pps_id) != H264Parser::kOk) {
|
||||
// Allow PPS parsing to fail if waiting for SPS.
|
||||
if (last_video_decoder_config_)
|
||||
return false;
|
||||
|
@ -223,10 +224,10 @@ bool EsParserH264::ParseInternal() {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case H264NALU::kIDRSlice:
|
||||
case H264NALU::kNonIDRSlice: {
|
||||
is_key_frame = (nalu.nal_unit_type == H264NALU::kIDRSlice);
|
||||
DVLOG(LOG_LEVEL_ES) << "NALU: slice IDR=" << is_key_frame;
|
||||
case Nalu::H264_IDRSlice:
|
||||
case Nalu::H264_NonIDRSlice: {
|
||||
is_key_frame = (nalu.type() == Nalu::H264_IDRSlice);
|
||||
DVLOG(LOG_LEVEL_ES) << "Nalu: slice IDR=" << is_key_frame;
|
||||
H264SliceHeader shdr;
|
||||
if (h264_parser_->ParseSliceHeader(nalu, &shdr) != H264Parser::kOk) {
|
||||
// Only accept an invalid SPS/PPS at the beginning when the stream
|
||||
|
@ -239,7 +240,7 @@ bool EsParserH264::ParseInternal() {
|
|||
break;
|
||||
}
|
||||
default: {
|
||||
DVLOG(LOG_LEVEL_ES) << "NALU: " << nalu.nal_unit_type;
|
||||
DVLOG(LOG_LEVEL_ES) << "Nalu: " << nalu.type();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,9 +56,9 @@ std::vector<Packet> GetAccessUnits(const uint8_t* stream, size_t stream_size) {
|
|||
size_t offset = 0;
|
||||
while (true) {
|
||||
// Find the next start code.
|
||||
off_t relative_offset = 0;
|
||||
off_t start_code_size = 0;
|
||||
bool success = H264Parser::FindStartCode(
|
||||
uint64_t relative_offset = 0;
|
||||
uint8_t start_code_size = 0;
|
||||
bool success = NaluReader::FindStartCode(
|
||||
&stream[offset], stream_size - offset,
|
||||
&relative_offset, &start_code_size);
|
||||
if (!success)
|
||||
|
@ -79,8 +79,8 @@ std::vector<Packet> GetAccessUnits(const uint8_t* stream, size_t stream_size) {
|
|||
int nal_unit_type = stream[offset] & 0x1f;
|
||||
|
||||
// We assume there is only one slice per access unit.
|
||||
if (nal_unit_type == H264NALU::kIDRSlice ||
|
||||
nal_unit_type == H264NALU::kNonIDRSlice) {
|
||||
if (nal_unit_type == Nalu::H264_IDRSlice ||
|
||||
nal_unit_type == Nalu::H264_NonIDRSlice) {
|
||||
start_access_unit = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "packager/media/base/buffer_reader.h"
|
||||
#include "packager/media/base/key_source.h"
|
||||
#include "packager/media/base/media_sample.h"
|
||||
#include "packager/media/filters/nalu_reader.h"
|
||||
#include "packager/media/filters/vp8_parser.h"
|
||||
#include "packager/media/filters/vp9_parser.h"
|
||||
#include "packager/media/formats/mp4/box_definitions.h"
|
||||
|
@ -181,25 +182,21 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
|
|||
data += frame.frame_size;
|
||||
}
|
||||
} else {
|
||||
BufferReader reader(data, sample->data_size());
|
||||
while (reader.HasBytes(1)) {
|
||||
uint64_t nalu_length;
|
||||
if (!reader.ReadNBytesInto8(&nalu_length, nalu_length_size_))
|
||||
return Status(error::MUXER_FAILURE, "Fail to read nalu_length.");
|
||||
|
||||
if (!reader.SkipBytes(nalu_length)) {
|
||||
return Status(error::MUXER_FAILURE,
|
||||
"Sample size does not match nalu_length.");
|
||||
}
|
||||
NaluReader reader(nalu_length_size_, data, sample->data_size());
|
||||
|
||||
Nalu nalu;
|
||||
NaluReader::Result result;
|
||||
while ((result = reader.Advance(&nalu)) == NaluReader::kOk) {
|
||||
SubsampleEntry subsample;
|
||||
subsample.clear_bytes = nalu_length_size_ + 1;
|
||||
subsample.cipher_bytes = nalu_length - 1;
|
||||
subsample.clear_bytes = nalu.header_size();
|
||||
subsample.cipher_bytes = nalu.data_size();
|
||||
sample_encryption_entry.subsamples.push_back(subsample);
|
||||
|
||||
EncryptBytes(data + subsample.clear_bytes, subsample.cipher_bytes);
|
||||
data += nalu_length_size_ + nalu_length;
|
||||
EncryptBytes(const_cast<uint8_t*>(nalu.data() + nalu.header_size()),
|
||||
subsample.cipher_bytes);
|
||||
}
|
||||
if (result != NaluReader::kEOStream)
|
||||
return Status(error::MUXER_FAILURE, "Failed to parse NAL units.");
|
||||
}
|
||||
|
||||
// The length of per-sample auxiliary datum, defined in CENC ch. 7.
|
||||
|
|
Loading…
Reference in New Issue