DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerator
nalu_reader.cc
1 // Copyright 2016 Google Inc. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file or at
5 // https://developers.google.com/open-source/licenses/bsd
6 
7 #include "packager/media/filters/nalu_reader.h"
8 
9 #include "packager/base/logging.h"
10 #include "packager/media/base/buffer_reader.h"
11 #include "packager/media/filters/h264_parser.h"
12 
13 namespace edash_packager {
14 namespace media {
15 
16 namespace {
17 inline bool IsStartCode(const uint8_t* data) {
18  return data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x01;
19 }
20 } // namespace
21 
22 Nalu::Nalu()
23  : data_(nullptr),
24  header_size_(0),
25  payload_size_(0),
26  ref_idc_(0),
27  nuh_layer_id_(0),
28  nuh_temporal_id_(0),
29  type_(0),
30  is_video_slice_(false) {}
31 
32 bool Nalu::InitializeFromH264(const uint8_t* data, uint64_t size) {
33  DCHECK(data);
34  if (size == 0)
35  return false;
36  uint8_t header = data[0];
37  if ((header & 0x80) != 0)
38  return false;
39 
40  data_ = data;
41  header_size_ = 1;
42  payload_size_ = size - header_size_;
43  ref_idc_ = (header >> 5) & 0x3;
44  type_ = header & 0x1F;
45  is_video_slice_ = (type_ >= Nalu::H264_NonIDRSlice &&
46  type_ <= Nalu::H264_IDRSlice);
47  return true;
48 }
49 
50 bool Nalu::InitializeFromH265(const uint8_t* data, uint64_t size) {
51  DCHECK(data);
52  if (size < 2)
53  return false;
54  uint16_t header = (data[0] << 8) | data[1];
55  if ((header & 0x8000) != 0)
56  return false;
57 
58  data_ = data;
59  header_size_ = 2;
60  payload_size_ = size - header_size_;
61 
62  type_ = (header >> 9) & 0x3F;
63  nuh_layer_id_ = (header >> 3) & 0x3F;
64  nuh_temporal_id_ = (header & 0x7) - 1;
65 
66  // Don't treat reserved VCL types as video slices since we cannot parse them.
67  is_video_slice_ =
68  (type_ >= Nalu::H265_TRAIL_N && type_ <= Nalu::H265_RASL_R) ||
69  (type_ >= Nalu::H265_BLA_W_LP && type_ <= Nalu::H265_CRA_NUT);
70  return true;
71 }
72 
73 NaluReader::NaluReader(NaluType type,
74  uint8_t nal_length_size,
75  const uint8_t* stream,
76  uint64_t stream_size)
77  : stream_(stream),
78  stream_size_(stream_size),
79  nalu_type_(type),
80  nalu_length_size_(nal_length_size),
81  format_(nal_length_size == 0 ? kAnnexbByteStreamFormat
82  : kNalUnitStreamFormat) {
83  DCHECK(stream);
84 }
85 NaluReader::~NaluReader() {}
86 
87 NaluReader::Result NaluReader::Advance(Nalu* nalu) {
88  if (stream_size_ <= 0)
89  return NaluReader::kEOStream;
90 
91  uint8_t nalu_length_size_or_start_code_size;
92  uint64_t nalu_length;
93  if (format_ == kAnnexbByteStreamFormat) {
94  // This will move |stream_| to the start code.
95  uint64_t nalu_length_with_header;
96  if (!LocateNaluByStartCode(&nalu_length_with_header,
97  &nalu_length_size_or_start_code_size)) {
98  LOG(ERROR) << "Could not find next NALU, bytes left in stream: "
99  << stream_size_;
100  // This is actually an error. Since we always move to past the end of
101  // each NALU, if there is no next start code, then this is the first call
102  // and there are no start codes in the stream.
103  return NaluReader::kInvalidStream;
104  }
105  nalu_length = nalu_length_with_header - nalu_length_size_or_start_code_size;
106  } else {
107  BufferReader reader(stream_, stream_size_);
108  if (!reader.ReadNBytesInto8(&nalu_length, nalu_length_size_))
109  return NaluReader::kInvalidStream;
110  nalu_length_size_or_start_code_size = nalu_length_size_;
111 
112  if (nalu_length + nalu_length_size_ > stream_size_) {
113  LOG(ERROR) << "NALU length exceeds stream size: "
114  << stream_size_ << " < " << nalu_length;
115  return NaluReader::kInvalidStream;
116  }
117  if (nalu_length == 0) {
118  LOG(ERROR) << "NALU size 0";
119  return NaluReader::kInvalidStream;
120  }
121  }
122 
123  const uint8_t* nalu_data = stream_ + nalu_length_size_or_start_code_size;
124  if (nalu_type_ == kH264) {
125  if (!nalu->InitializeFromH264(nalu_data, nalu_length))
126  return NaluReader::kInvalidStream;
127  } else {
128  DCHECK_EQ(kH265, nalu_type_);
129  if (!nalu->InitializeFromH265(nalu_data, nalu_length))
130  return NaluReader::kInvalidStream;
131  }
132 
133  // Move parser state to after this NALU, so next time Advance
134  // is called, we will effectively be skipping it.
135  stream_ += nalu_length_size_or_start_code_size + nalu_length;
136  stream_size_ -= nalu_length_size_or_start_code_size + nalu_length;
137 
138  DVLOG(4) << "NALU type: " << static_cast<int>(nalu->type())
139  << " at: " << reinterpret_cast<const void*>(nalu->data())
140  << " data size: " << nalu->payload_size()
141  << " ref: " << static_cast<int>(nalu->ref_idc());
142 
143  return NaluReader::kOk;
144 }
145 
147  if (stream_size_ >= 3) {
148  if (IsStartCode(stream_))
149  return true;
150  }
151  if (stream_size_ >= 4) {
152  if (stream_[0] == 0x00 && IsStartCode(stream_ + 1))
153  return true;
154  }
155  return false;
156 }
157 
158 // static
159 bool NaluReader::FindStartCode(const uint8_t* data,
160  uint64_t data_size,
161  uint64_t* offset,
162  uint8_t* start_code_size) {
163  uint64_t bytes_left = data_size;
164 
165  while (bytes_left >= 3) {
166  if (IsStartCode(data)) {
167  // Found three-byte start code, set pointer at its beginning.
168  *offset = data_size - bytes_left;
169  *start_code_size = 3;
170 
171  // If there is a zero byte before this start code,
172  // then it's actually a four-byte start code, so backtrack one byte.
173  if (*offset > 0 && *(data - 1) == 0x00) {
174  --(*offset);
175  ++(*start_code_size);
176  }
177 
178  return true;
179  }
180 
181  ++data;
182  --bytes_left;
183  }
184 
185  // End of data: offset is pointing to the first byte that was not considered
186  // as a possible start of a start code.
187  *offset = data_size - bytes_left;
188  *start_code_size = 0;
189  return false;
190 }
191 
192 bool NaluReader::LocateNaluByStartCode(uint64_t* nalu_size,
193  uint8_t* start_code_size) {
194  // Find the start code of next NALU.
195  uint64_t nalu_start_off = 0;
196  uint8_t annexb_start_code_size = 0;
197  if (!FindStartCode(stream_, stream_size_,
198  &nalu_start_off, &annexb_start_code_size)) {
199  DVLOG(4) << "Could not find start code, end of stream?";
200  return false;
201  }
202 
203  // Move the stream to the beginning of the NALU (pointing at the start code).
204  stream_ += nalu_start_off;
205  stream_size_ -= nalu_start_off;
206 
207  const uint8_t* nalu_data = stream_ + annexb_start_code_size;
208  uint64_t max_nalu_data_size = stream_size_ - annexb_start_code_size;
209  if (max_nalu_data_size <= 0) {
210  DVLOG(3) << "End of stream";
211  return false;
212  }
213 
214  // Find the start code of next NALU;
215  // if successful, |nalu_size_without_start_code| is the number of bytes from
216  // after previous start code to before this one;
217  // if next start code is not found, it is still a valid NALU since there
218  // are some bytes left after the first start code: all the remaining bytes
219  // belong to the current NALU.
220  uint64_t nalu_size_without_start_code = 0;
221  uint8_t next_start_code_size = 0;
222  if (!FindStartCode(nalu_data, max_nalu_data_size,
223  &nalu_size_without_start_code, &next_start_code_size)) {
224  nalu_size_without_start_code = max_nalu_data_size;
225  }
226  *nalu_size = nalu_size_without_start_code + annexb_start_code_size;
227  *start_code_size = annexb_start_code_size;
228  return true;
229 }
230 
231 } // namespace media
232 } // namespace edash_packager
NaluReader(NaluType type, uint8_t nal_length_size, const uint8_t *stream, uint64_t stream_size)
Definition: nalu_reader.cc:73
bool ReadNBytesInto8(uint64_t *v, size_t num_bytes) WARN_UNUSED_RESULT