diff --git a/packager/media/filters/avc_decoder_configuration.cc b/packager/media/filters/avc_decoder_configuration.cc index 5412515626..92c891fd3b 100644 --- a/packager/media/filters/avc_decoder_configuration.cc +++ b/packager/media/filters/avc_decoder_configuration.cc @@ -49,7 +49,7 @@ bool AVCDecoderConfiguration::Parse(const std::vector& data) { H264Parser parser; int sps_id = 0; Nalu nalu; - RCHECK(nalu.InitializeFromH264(reader.data() + reader.pos(), sps_length, 0)); + RCHECK(nalu.InitializeFromH264(reader.data() + reader.pos(), sps_length)); RCHECK(parser.ParseSPS(nalu, &sps_id) == H264Parser::kOk); return ExtractResolutionFromSps(*parser.GetSPS(sps_id), &coded_width_, &coded_height_, &pixel_width_, diff --git a/packager/media/filters/h264_byte_to_unit_stream_converter.cc b/packager/media/filters/h264_byte_to_unit_stream_converter.cc index 0d3be5d7e9..0b72b433d3 100644 --- a/packager/media/filters/h264_byte_to_unit_stream_converter.cc +++ b/packager/media/filters/h264_byte_to_unit_stream_converter.cc @@ -34,25 +34,15 @@ bool H264ByteToUnitStreamConverter::ConvertByteStreamToNalUnitStream( BufferWriter output_buffer(input_frame_size + kStreamConversionOverhead); - bool first_nalu(true); Nalu nalu; NaluReader reader(kIsAnnexbByteStream, input_frame, input_frame_size); - while (reader.Advance(&nalu) == NaluReader::kOk) { - if (first_nalu) { - if (nalu.data() != input_frame) { - LOG(ERROR) << "H.264 byte stream frame did not begin with start code."; - return false; - } - first_nalu = false; - } - - ProcessNalu(nalu, &output_buffer); - } - - if (first_nalu) { - LOG(ERROR) << "H.264 byte stream frame did not contain start codes."; + 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; diff --git a/packager/media/filters/h264_parser_unittest.cc b/packager/media/filters/h264_parser_unittest.cc index 51abd221b4..937091825b 100644 --- a/packager/media/filters/h264_parser_unittest.cc +++ b/packager/media/filters/h264_parser_unittest.cc @@ -12,12 +12,6 @@ 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 buffer = ReadTestDataFile("test-25fps.h264"); @@ -81,7 +75,7 @@ TEST(H264ParserTest, ExtractResolutionFromSpsData) { H264Parser parser; int sps_id = 0; Nalu nalu; - ASSERT_TRUE(nalu.InitializeFromH264(kSps, arraysize(kSps), kStartCodeSize)); + ASSERT_TRUE(nalu.InitializeFromH264(kSps, arraysize(kSps))); ASSERT_EQ(H264Parser::kOk, parser.ParseSPS(nalu, &sps_id)); uint32_t coded_width = 0; @@ -106,7 +100,7 @@ TEST(H264ParserTest, ExtractResolutionFromSpsDataWithCropping) { H264Parser parser; int sps_id = 0; Nalu nalu; - ASSERT_TRUE(nalu.InitializeFromH264(kSps, arraysize(kSps), kStartCodeSize)); + ASSERT_TRUE(nalu.InitializeFromH264(kSps, arraysize(kSps))); ASSERT_EQ(H264Parser::kOk, parser.ParseSPS(nalu, &sps_id)); uint32_t coded_width = 0; diff --git a/packager/media/filters/nalu_reader.cc b/packager/media/filters/nalu_reader.cc index 3d254a19bd..f5794c7f00 100644 --- a/packager/media/filters/nalu_reader.cc +++ b/packager/media/filters/nalu_reader.cc @@ -27,18 +27,16 @@ Nalu::Nalu() type_(0), is_video_slice_(false) {} -bool Nalu::InitializeFromH264(const uint8_t* data, - uint64_t size, - uint8_t start_code_size) { +bool Nalu::InitializeFromH264(const uint8_t* data, uint64_t size) { DCHECK(data); - DCHECK_GT(size, start_code_size); - uint8_t header = data[start_code_size]; + DCHECK_GT(size, 0u); + uint8_t header = data[0]; if ((header & 0x80) != 0) return false; data_ = data; - header_size_ = start_code_size + 1; - data_size_ = size - start_code_size - 1; + header_size_ = 1; + data_size_ = size - 1; ref_idc_ = (header >> 5) & 0x3; type_ = header & 0x1F; is_video_slice_ = (type_ >= Nalu::H264_NonIDRSlice && @@ -63,9 +61,10 @@ NaluReader::Result NaluReader::Advance(Nalu* nalu) { return NaluReader::kEOStream; uint8_t nalu_length_size_or_start_code_size; - uint64_t nalu_length_with_header; + uint64_t nalu_length; if (format_ == kAnnexbByteStreamFormat) { // This will move |stream_| to the start code. + uint64_t nalu_length_with_header; if (!LocateNaluByStartCode(&nalu_length_with_header, &nalu_length_size_or_start_code_size)) { LOG(ERROR) << "Could not find next NALU, bytes left in stream: " @@ -75,8 +74,8 @@ NaluReader::Result NaluReader::Advance(Nalu* nalu) { // and there are no start codes in the stream. return NaluReader::kInvalidStream; } + nalu_length = nalu_length_with_header - nalu_length_size_or_start_code_size; } else { - uint64_t nalu_length; BufferReader reader(stream_, stream_size_); if (!reader.ReadNBytesInto8(&nalu_length, nalu_length_size_)) return NaluReader::kInvalidStream; @@ -91,17 +90,16 @@ NaluReader::Result NaluReader::Advance(Nalu* nalu) { 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)) + const uint8_t* nalu_data = stream_ + nalu_length_size_or_start_code_size; + if (!nalu->InitializeFromH264(nalu_data, nalu_length)) 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; + stream_ += nalu_length_size_or_start_code_size + nalu_length; + stream_size_ -= nalu_length_size_or_start_code_size + nalu_length; DVLOG(4) << "NALU type: " << static_cast(nalu->type()) << " at: " << reinterpret_cast(nalu->data()) @@ -111,6 +109,18 @@ NaluReader::Result NaluReader::Advance(Nalu* nalu) { return NaluReader::kOk; } +bool NaluReader::StartsWithStartCode() { + if (stream_size_ >= 3) { + if (IsStartCode(stream_)) + return true; + } + if (stream_size_ >= 4) { + if (stream_[0] == 0x00 && IsStartCode(stream_ + 1)) + return true; + } + return false; +} + // static bool NaluReader::FindStartCode(const uint8_t* data, uint64_t data_size, diff --git a/packager/media/filters/nalu_reader.h b/packager/media/filters/nalu_reader.h index 88b4147216..d6cc548dbc 100644 --- a/packager/media/filters/nalu_reader.h +++ b/packager/media/filters/nalu_reader.h @@ -40,8 +40,7 @@ class Nalu { Nalu(); bool InitializeFromH264(const uint8_t* data, - uint64_t size, - uint8_t start_code_size) WARN_UNUSED_RESULT; + uint64_t size) WARN_UNUSED_RESULT; const uint8_t* data() const { return data_; } uint64_t data_size() const { return data_size_; } @@ -105,6 +104,9 @@ class NaluReader { /// end-of-stream; kInvalidStream on error. Result Advance(Nalu* nalu); + /// @returns true if the current position points to a start code. + bool StartsWithStartCode(); + private: enum Format { kAnnexbByteStreamFormat, diff --git a/packager/media/filters/nalu_reader_unittest.cc b/packager/media/filters/nalu_reader_unittest.cc index 3430a48d87..7cf1473017 100644 --- a/packager/media/filters/nalu_reader_unittest.cc +++ b/packager/media/filters/nalu_reader_unittest.cc @@ -24,16 +24,16 @@ TEST(NaluReaderTest, StartCodeSearch) { Nalu nalu; ASSERT_EQ(NaluReader::kOk, reader.Advance(&nalu)); - EXPECT_EQ(kNaluData + 6, nalu.data()); + EXPECT_EQ(kNaluData + 9, nalu.data()); EXPECT_EQ(3u, nalu.data_size()); - EXPECT_EQ(4u, nalu.header_size()); + EXPECT_EQ(1u, 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(kNaluData + 17, nalu.data()); EXPECT_EQ(3u, nalu.data_size()); - EXPECT_EQ(5u, nalu.header_size()); + EXPECT_EQ(1u, nalu.header_size()); EXPECT_EQ(3, nalu.ref_idc()); EXPECT_EQ(7, nalu.type()); @@ -52,44 +52,44 @@ TEST(NaluReaderTest, OneByteNaluLength) { Nalu nalu; ASSERT_EQ(NaluReader::kOk, reader.Advance(&nalu)); - EXPECT_EQ(kNaluData, nalu.data()); + EXPECT_EQ(kNaluData + 1, nalu.data()); EXPECT_EQ(4u, nalu.data_size()); - EXPECT_EQ(2u, nalu.header_size()); + EXPECT_EQ(1u, 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(kNaluData + 7, nalu.data()); EXPECT_EQ(5u, nalu.data_size()); - EXPECT_EQ(2u, nalu.header_size()); + EXPECT_EQ(1u, nalu.header_size()); EXPECT_EQ(3, nalu.ref_idc()); EXPECT_EQ(7, nalu.type()); EXPECT_EQ(NaluReader::kEOStream, reader.Advance(&nalu)); } -TEST(NaluReaderTest, ThreeByteNaluLength) { +TEST(NaluReaderTest, FourByteNaluLength) { const uint8_t kNaluData[] = { // First NALU - 0x00, 0x00, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x00, 0x00, 0x00, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // Second NALU - 0x00, 0x00, 0x03, 0x67, 0x0a, 0x0b + 0x00, 0x00, 0x00, 0x03, 0x67, 0x0a, 0x0b }; - NaluReader reader(3, kNaluData, arraysize(kNaluData)); + NaluReader reader(4, kNaluData, arraysize(kNaluData)); Nalu nalu; ASSERT_EQ(NaluReader::kOk, reader.Advance(&nalu)); - EXPECT_EQ(kNaluData, nalu.data()); + EXPECT_EQ(kNaluData + 4, nalu.data()); EXPECT_EQ(6u, nalu.data_size()); - EXPECT_EQ(4u, nalu.header_size()); + EXPECT_EQ(1u, 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(kNaluData + 15, nalu.data()); EXPECT_EQ(2u, nalu.data_size()); - EXPECT_EQ(4u, nalu.header_size()); + EXPECT_EQ(1u, nalu.header_size()); EXPECT_EQ(3, nalu.ref_idc()); EXPECT_EQ(7, nalu.type()); diff --git a/packager/media/formats/mp4/encrypting_fragmenter.cc b/packager/media/formats/mp4/encrypting_fragmenter.cc index 4ee46dbd70..bfdcd2d541 100644 --- a/packager/media/formats/mp4/encrypting_fragmenter.cc +++ b/packager/media/formats/mp4/encrypting_fragmenter.cc @@ -49,6 +49,15 @@ VideoCodec GetVideoCodec(const StreamInfo& stream_info) { static_cast(stream_info); return video_stream_info.codec(); } + +uint8_t GetNaluLengthSize(const StreamInfo& stream_info) { + if (stream_info.stream_type() != kStreamVideo) + return 0; + + const VideoStreamInfo& video_stream_info = + static_cast(stream_info); + return video_stream_info.nalu_length_size(); +} } // namespace EncryptingFragmenter::EncryptingFragmenter( @@ -59,6 +68,7 @@ EncryptingFragmenter::EncryptingFragmenter( : Fragmenter(traf), info_(info), encryption_key_(encryption_key.Pass()), + nalu_length_size_(GetNaluLengthSize(*info)), clear_time_(clear_time) { DCHECK(encryption_key_); VideoCodec video_codec = GetVideoCodec(*info); @@ -213,7 +223,7 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr sample) { data += frame.frame_size; } } else { - NaluReader reader(GetNaluLengthSize(), data, sample->data_size()); + NaluReader reader(nalu_length_size_, data, sample->data_size()); // Store the current length of clear data. This is used to squash // multiple unencrypted NAL units into fewer subsample entries. @@ -231,15 +241,16 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr sample) { if (video_slice_header_size < 0) return Status(error::MUXER_FAILURE, "Failed to read slice header."); - const uint64_t current_clear_bytes = - nalu.header_size() + video_slice_header_size; + const uint64_t current_clear_bytes = nalu.header_size() + + video_slice_header_size; const uint64_t cipher_bytes = nalu.data_size() - video_slice_header_size; const uint8_t* nalu_data = nalu.data() + current_clear_bytes; EncryptBytes(const_cast(nalu_data), cipher_bytes); - AddSubsamples(accumulated_clear_bytes + current_clear_bytes, - cipher_bytes, &sample_encryption_entry.subsamples); + AddSubsamples( + accumulated_clear_bytes + nalu_length_size_ + current_clear_bytes, + cipher_bytes, &sample_encryption_entry.subsamples); accumulated_clear_bytes = 0; } else { // For non-video-slice NAL units, don't encrypt. @@ -265,17 +276,8 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr sample) { return Status::OK; } -uint8_t EncryptingFragmenter::GetNaluLengthSize() { - if (info_->stream_type() != kStreamVideo) - return 0; - - const VideoStreamInfo& video_stream_info = - static_cast(*info_); - return video_stream_info.nalu_length_size(); -} - bool EncryptingFragmenter::IsSubsampleEncryptionRequired() { - return vpx_parser_ || GetNaluLengthSize() != 0; + return vpx_parser_ || nalu_length_size_ != 0; } } // namespace mp4 diff --git a/packager/media/formats/mp4/encrypting_fragmenter.h b/packager/media/formats/mp4/encrypting_fragmenter.h index 031fe32530..0767dc298d 100644 --- a/packager/media/formats/mp4/encrypting_fragmenter.h +++ b/packager/media/formats/mp4/encrypting_fragmenter.h @@ -66,16 +66,16 @@ class EncryptingFragmenter : public Fragmenter { void EncryptBytes(uint8_t* data, uint32_t size); Status EncryptSample(scoped_refptr sample); - // If this stream contains AVC, subsample encryption specifies that the size - // and type of NAL units remain unencrypted. This function returns the size of - // the size field in bytes. Can be 1, 2 or 4 bytes. - uint8_t GetNaluLengthSize(); // Should we enable subsample encryption? bool IsSubsampleEncryptionRequired(); scoped_refptr info_; scoped_ptr encryption_key_; scoped_ptr encryptor_; + // If this stream contains AVC, subsample encryption specifies that the size + // and type of NAL units remain unencrypted. This function returns the size of + // the size field in bytes. Can be 1, 2 or 4 bytes. + const uint8_t nalu_length_size_; int64_t clear_time_; scoped_ptr vpx_parser_; diff --git a/packager/media/formats/mp4/video_slice_header_parser.cc b/packager/media/formats/mp4/video_slice_header_parser.cc index 2350427351..e8f9050e57 100644 --- a/packager/media/formats/mp4/video_slice_header_parser.cc +++ b/packager/media/formats/mp4/video_slice_header_parser.cc @@ -13,10 +13,6 @@ namespace edash_packager { namespace media { namespace mp4 { -namespace { -const uint8_t kStartCodeSize = 0; -} - H264VideoSliceHeaderParser::H264VideoSliceHeaderParser() {} H264VideoSliceHeaderParser::~H264VideoSliceHeaderParser() {} @@ -39,7 +35,7 @@ bool H264VideoSliceHeaderParser::Initialize( int id; Nalu nalu; - RCHECK(nalu.InitializeFromH264(data, size, kStartCodeSize)); + RCHECK(nalu.InitializeFromH264(data, size)); RCHECK(parser_.ParseSPS(nalu, &id) == H264Parser::kOk); } @@ -53,7 +49,7 @@ bool H264VideoSliceHeaderParser::Initialize( int id; Nalu nalu; - RCHECK(nalu.InitializeFromH264(data, size, kStartCodeSize)); + RCHECK(nalu.InitializeFromH264(data, size)); RCHECK(parser_.ParsePPS(nalu, &id) == H264Parser::kOk); } diff --git a/packager/media/formats/mp4/video_slice_header_parser_unittest.cc b/packager/media/formats/mp4/video_slice_header_parser_unittest.cc index c87d0e5827..cd541b8080 100644 --- a/packager/media/formats/mp4/video_slice_header_parser_unittest.cc +++ b/packager/media/formats/mp4/video_slice_header_parser_unittest.cc @@ -31,7 +31,6 @@ TEST(H264VideoSliceHeaderParserTest, BasicSupport) { 0x00, 0x06, // Size 0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0 }; - const uint8_t kStartCodeSize = 0; const uint8_t kData[] = { // Incomplete data, but we only care about the header size. 0x65, 0x88, 0x84, 0x00, 0x21, 0xff, 0xcf, 0x73, 0xc7, 0x24, @@ -44,7 +43,7 @@ TEST(H264VideoSliceHeaderParserTest, BasicSupport) { ASSERT_TRUE(parser.Initialize(extra_data)); Nalu nalu; - ASSERT_TRUE(nalu.InitializeFromH264(kData, arraysize(kData), kStartCodeSize)); + ASSERT_TRUE(nalu.InitializeFromH264(kData, arraysize(kData))); // Real header size is 34 bits, but we round up to 5 bytes. EXPECT_EQ(5, parser.GetHeaderSize(nalu)); }