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 
142  return NaluReader::kOk;
143 }
144 
146  if (stream_size_ >= 3) {
147  if (IsStartCode(stream_))
148  return true;
149  }
150  if (stream_size_ >= 4) {
151  if (stream_[0] == 0x00 && IsStartCode(stream_ + 1))
152  return true;
153  }
154  return false;
155 }
156 
157 // static
158 bool NaluReader::FindStartCode(const uint8_t* data,
159  uint64_t data_size,
160  uint64_t* offset,
161  uint8_t* start_code_size) {
162  uint64_t bytes_left = data_size;
163 
164  while (bytes_left >= 3) {
165  if (IsStartCode(data)) {
166  // Found three-byte start code, set pointer at its beginning.
167  *offset = data_size - bytes_left;
168  *start_code_size = 3;
169 
170  // If there is a zero byte before this start code,
171  // then it's actually a four-byte start code, so backtrack one byte.
172  if (*offset > 0 && *(data - 1) == 0x00) {
173  --(*offset);
174  ++(*start_code_size);
175  }
176 
177  return true;
178  }
179 
180  ++data;
181  --bytes_left;
182  }
183 
184  // End of data: offset is pointing to the first byte that was not considered
185  // as a possible start of a start code.
186  *offset = data_size - bytes_left;
187  *start_code_size = 0;
188  return false;
189 }
190 
191 bool NaluReader::LocateNaluByStartCode(uint64_t* nalu_size,
192  uint8_t* start_code_size) {
193  // Find the start code of next NALU.
194  uint64_t nalu_start_off = 0;
195  uint8_t annexb_start_code_size = 0;
196  if (!FindStartCode(stream_, stream_size_,
197  &nalu_start_off, &annexb_start_code_size)) {
198  DVLOG(4) << "Could not find start code, end of stream?";
199  return false;
200  }
201 
202  // Move the stream to the beginning of the NALU (pointing at the start code).
203  stream_ += nalu_start_off;
204  stream_size_ -= nalu_start_off;
205 
206  const uint8_t* nalu_data = stream_ + annexb_start_code_size;
207  uint64_t max_nalu_data_size = stream_size_ - annexb_start_code_size;
208  if (max_nalu_data_size <= 0) {
209  DVLOG(3) << "End of stream";
210  return false;
211  }
212 
213  // Find the start code of next NALU;
214  // if successful, |nalu_size_without_start_code| is the number of bytes from
215  // after previous start code to before this one;
216  // if next start code is not found, it is still a valid NALU since there
217  // are some bytes left after the first start code: all the remaining bytes
218  // belong to the current NALU.
219  uint64_t nalu_size_without_start_code = 0;
220  uint8_t next_start_code_size = 0;
221  if (!FindStartCode(nalu_data, max_nalu_data_size,
222  &nalu_size_without_start_code, &next_start_code_size)) {
223  nalu_size_without_start_code = max_nalu_data_size;
224  }
225  *nalu_size = nalu_size_without_start_code + annexb_start_code_size;
226  *start_code_size = annexb_start_code_size;
227  return true;
228 }
229 
230 } // namespace media
231 } // 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