From cb18ee61270694e743970100bf2871bd67a44906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A1ri=20Tristan=20Helgason?= Date: Wed, 2 Nov 2016 15:07:02 +0100 Subject: [PATCH] Add support for 3-byte headers in VideoToolbox NALU parser. BUG=webrtc:6278 R=tkchin@webrtc.org, tommi@webrtc.org Review URL: https://codereview.webrtc.org/2356793002 . Cr-Commit-Position: refs/heads/master@{#14889} --- webrtc/common_video/h264/h264_common.cc | 3 +- .../codecs/h264/h264_video_toolbox_nalu.cc | 56 +++++-------------- .../codecs/h264/h264_video_toolbox_nalu.h | 8 ++- .../h264/h264_video_toolbox_nalu_unittest.cc | 23 ++++++-- 4 files changed, 40 insertions(+), 50 deletions(-) diff --git a/webrtc/common_video/h264/h264_common.cc b/webrtc/common_video/h264/h264_common.cc index c17b118ce0..fe55b02d2c 100644 --- a/webrtc/common_video/h264/h264_common.cc +++ b/webrtc/common_video/h264/h264_common.cc @@ -21,8 +21,9 @@ std::vector FindNaluIndices(const uint8_t* buffer, // given a 3-byte sequence we're looking at, if the 3rd byte isn't 1 or 0, // skip ahead to the next 3-byte sequence. 0s and 1s are relatively rare, so // this will skip the majority of reads/checks. - RTC_CHECK_GE(buffer_size, kNaluShortStartSequenceSize); std::vector sequences; + if (buffer_size < kNaluShortStartSequenceSize) + return sequences; const size_t end = buffer_size - kNaluShortStartSequenceSize; for (size_t i = 0; i < end;) { if (buffer[i + 2] > 1) { diff --git a/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_nalu.cc b/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_nalu.cc index c6c0406128..357b4d4b4a 100644 --- a/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_nalu.cc +++ b/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_nalu.cc @@ -19,13 +19,13 @@ #include "webrtc/base/checks.h" #include "webrtc/base/logging.h" -#include "webrtc/common_video/h264/h264_common.h" namespace webrtc { -using H264::NaluType; using H264::kAud; using H264::kSps; +using H264::NaluIndex; +using H264::NaluType; using H264::ParseNaluType; const char kAnnexBHeaderBytes[4] = {0, 0, 0, 1}; @@ -57,8 +57,7 @@ bool H264CMSampleBufferToAnnexBBuffer( LOG(LS_ERROR) << "Failed to get parameter set."; return false; } - // TODO(tkchin): handle other potential sizes. - RTC_DCHECK_EQ(nalu_header_size, 4); + RTC_CHECK_EQ(nalu_header_size, kAvccHeaderByteSize); RTC_DCHECK_EQ(param_set_count, 2u); // Truncate any previous data in the buffer without changing its capacity. @@ -140,7 +139,7 @@ bool H264CMSampleBufferToAnnexBBuffer( frag_lengths.push_back(packet_size); nalu_offset += sizeof(kAnnexBHeaderBytes) + packet_size; - size_t bytes_written = packet_size + nalu_header_size; + size_t bytes_written = packet_size + sizeof(kAnnexBHeaderBytes); bytes_remaining -= bytes_written; data_ptr += bytes_written; } @@ -312,11 +311,10 @@ CMVideoFormatDescriptionRef CreateVideoFormatDescription( AnnexBBufferReader::AnnexBBufferReader(const uint8_t* annexb_buffer, size_t length) - : start_(annexb_buffer), offset_(0), next_offset_(0), length_(length) { + : start_(annexb_buffer), length_(length) { RTC_DCHECK(annexb_buffer); - offset_ = FindNextNaluHeader(start_, length_, 0); - next_offset_ = - FindNextNaluHeader(start_, length_, offset_ + sizeof(kAnnexBHeaderBytes)); + offsets_ = H264::FindNaluIndices(annexb_buffer, length); + offset_ = offsets_.begin(); } bool AnnexBBufferReader::ReadNalu(const uint8_t** out_nalu, @@ -326,46 +324,20 @@ bool AnnexBBufferReader::ReadNalu(const uint8_t** out_nalu, *out_nalu = nullptr; *out_length = 0; - size_t data_offset = offset_ + sizeof(kAnnexBHeaderBytes); - if (data_offset > length_) { + if (offset_ == offsets_.end()) { return false; } - *out_nalu = start_ + data_offset; - *out_length = next_offset_ - data_offset; - offset_ = next_offset_; - next_offset_ = - FindNextNaluHeader(start_, length_, offset_ + sizeof(kAnnexBHeaderBytes)); + *out_nalu = start_ + offset_->payload_start_offset; + *out_length = offset_->payload_size; + ++offset_; return true; } size_t AnnexBBufferReader::BytesRemaining() const { - return length_ - offset_; -} - -size_t AnnexBBufferReader::FindNextNaluHeader(const uint8_t* start, - size_t length, - size_t offset) const { - RTC_DCHECK(start); - if (offset + sizeof(kAnnexBHeaderBytes) > length) { - return length; + if (offset_ == offsets_.end()) { + return 0; } - // NALUs are separated by an 00 00 00 01 header. Scan the byte stream - // starting from the offset for the next such sequence. - const uint8_t* current = start + offset; - // The loop reads sizeof(kAnnexBHeaderBytes) at a time, so stop when there - // aren't enough bytes remaining. - const uint8_t* const end = start + length - sizeof(kAnnexBHeaderBytes); - while (current < end) { - if (current[3] > 1) { - current += 4; - } else if (current[3] == 1 && current[2] == 0 && current[1] == 0 && - current[0] == 0) { - return current - start; - } else { - ++current; - } - } - return length; + return length_ - offset_->start_offset; } AvccBufferWriter::AvccBufferWriter(uint8_t* const avcc_buffer, size_t length) diff --git a/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_nalu.h b/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_nalu.h index 50942536f3..7862c3b618 100644 --- a/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_nalu.h +++ b/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_nalu.h @@ -17,10 +17,14 @@ #if defined(WEBRTC_VIDEO_TOOLBOX_SUPPORTED) #include +#include #include "webrtc/base/buffer.h" +#include "webrtc/common_video/h264/h264_common.h" #include "webrtc/modules/include/module_common_types.h" +using webrtc::H264::NaluIndex; + namespace webrtc { // Converts a sample buffer emitted from the VideoToolbox encoder into a buffer @@ -79,8 +83,8 @@ class AnnexBBufferReader final { size_t offset) const; const uint8_t* const start_; - size_t offset_; - size_t next_offset_; + std::vector offsets_; + std::vector::iterator offset_; const size_t length_; }; diff --git a/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_nalu_unittest.cc b/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_nalu_unittest.cc index b5e3195cac..df71f54fa4 100644 --- a/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_nalu_unittest.cc +++ b/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_nalu_unittest.cc @@ -87,11 +87,25 @@ TEST(AnnexBBufferReaderTest, TestReadSingleNalu) { EXPECT_EQ(0u, nalu_length); } +TEST(AnnexBBufferReaderTest, TestReadSingleNalu3ByteHeader) { + const uint8_t annex_b_test_data[] = {0x00, 0x00, 0x01, 0xAA}; + AnnexBBufferReader reader(annex_b_test_data, arraysize(annex_b_test_data)); + const uint8_t* nalu = nullptr; + size_t nalu_length = 0; + EXPECT_EQ(arraysize(annex_b_test_data), reader.BytesRemaining()); + EXPECT_TRUE(reader.ReadNalu(&nalu, &nalu_length)); + EXPECT_EQ(annex_b_test_data + 3, nalu); + EXPECT_EQ(1u, nalu_length); + EXPECT_EQ(0u, reader.BytesRemaining()); + EXPECT_FALSE(reader.ReadNalu(&nalu, &nalu_length)); + EXPECT_EQ(nullptr, nalu); + EXPECT_EQ(0u, nalu_length); +} + TEST(AnnexBBufferReaderTest, TestReadMissingNalu) { // clang-format off const uint8_t annex_b_test_data[] = {0x01, 0x00, 0x01, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF}; // clang-format on AnnexBBufferReader reader(annex_b_test_data, arraysize(annex_b_test_data)); @@ -108,9 +122,8 @@ TEST(AnnexBBufferReaderTest, TestReadMultipleNalus) { const uint8_t annex_b_test_data[] = {0x00, 0x00, 0x00, 0x01, 0xFF, 0x01, 0x00, 0x01, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, - 0x00, 0x00, 0x00, 0x01, 0xAA, 0xBB}; + 0x00, 0x00, 0x01, 0xAA, 0xBB}; // clang-format on AnnexBBufferReader reader(annex_b_test_data, arraysize(annex_b_test_data)); const uint8_t* nalu = nullptr; @@ -118,10 +131,10 @@ TEST(AnnexBBufferReaderTest, TestReadMultipleNalus) { EXPECT_EQ(arraysize(annex_b_test_data), reader.BytesRemaining()); EXPECT_TRUE(reader.ReadNalu(&nalu, &nalu_length)); EXPECT_EQ(annex_b_test_data + 4, nalu); - EXPECT_EQ(11u, nalu_length); + EXPECT_EQ(8u, nalu_length); EXPECT_EQ(6u, reader.BytesRemaining()); EXPECT_TRUE(reader.ReadNalu(&nalu, &nalu_length)); - EXPECT_EQ(annex_b_test_data + 19, nalu); + EXPECT_EQ(annex_b_test_data + 16, nalu); EXPECT_EQ(2u, nalu_length); EXPECT_EQ(0u, reader.BytesRemaining()); EXPECT_FALSE(reader.ReadNalu(&nalu, &nalu_length));