Moved Nalu data pointer position.
Now the Nalu data pointer points to the start of the NALU header rather than pointing to the start code. Added a new method on NaluReader to check whether the stream starts with a start code. Change-Id: Ifaecbe0c911aa5cffdf0a966028e6cada8621cc3
This commit is contained in:
parent
96abd90ca2
commit
0d3951ff74
|
@ -49,7 +49,7 @@ bool AVCDecoderConfiguration::Parse(const std::vector<uint8_t>& 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_,
|
||||
|
|
|
@ -34,26 +34,16 @@ 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) {
|
||||
if (!reader.StartsWithStartCode()) {
|
||||
LOG(ERROR) << "H.264 byte stream frame did not begin with start code.";
|
||||
return false;
|
||||
}
|
||||
first_nalu = false;
|
||||
}
|
||||
|
||||
while (reader.Advance(&nalu) == NaluReader::kOk) {
|
||||
ProcessNalu(nalu, &output_buffer);
|
||||
}
|
||||
|
||||
if (first_nalu) {
|
||||
LOG(ERROR) << "H.264 byte stream frame did not contain start codes.";
|
||||
return false;
|
||||
}
|
||||
|
||||
output_buffer.SwapBuffer(output_frame);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -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<uint8_t> 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;
|
||||
|
|
|
@ -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<int>(nalu->type())
|
||||
<< " at: " << reinterpret_cast<const void*>(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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -49,6 +49,15 @@ VideoCodec GetVideoCodec(const StreamInfo& stream_info) {
|
|||
static_cast<const VideoStreamInfo&>(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<const VideoStreamInfo&>(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<MediaSample> 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,14 +241,15 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> 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<uint8_t*>(nalu_data), cipher_bytes);
|
||||
|
||||
AddSubsamples(accumulated_clear_bytes + current_clear_bytes,
|
||||
AddSubsamples(
|
||||
accumulated_clear_bytes + nalu_length_size_ + current_clear_bytes,
|
||||
cipher_bytes, &sample_encryption_entry.subsamples);
|
||||
accumulated_clear_bytes = 0;
|
||||
} else {
|
||||
|
@ -265,17 +276,8 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
|
|||
return Status::OK;
|
||||
}
|
||||
|
||||
uint8_t EncryptingFragmenter::GetNaluLengthSize() {
|
||||
if (info_->stream_type() != kStreamVideo)
|
||||
return 0;
|
||||
|
||||
const VideoStreamInfo& video_stream_info =
|
||||
static_cast<const VideoStreamInfo&>(*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
|
||||
|
|
|
@ -66,16 +66,16 @@ class EncryptingFragmenter : public Fragmenter {
|
|||
void EncryptBytes(uint8_t* data, uint32_t size);
|
||||
Status EncryptSample(scoped_refptr<MediaSample> 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<StreamInfo> info_;
|
||||
scoped_ptr<EncryptionKey> encryption_key_;
|
||||
scoped_ptr<AesCtrEncryptor> 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<VPxParser> vpx_parser_;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue