Shaka Packager SDK
Loading...
Searching...
No Matches
nal_unit_to_byte_stream_converter.cc
1// Copyright 2016 Google LLC. 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/codecs/nal_unit_to_byte_stream_converter.h>
8
9#include <cstddef>
10#include <cstdint>
11#include <cstring>
12#include <list>
13#include <vector>
14
15#include <absl/log/check.h>
16#include <absl/log/log.h>
17
18#include <packager/macros/compiler.h>
19#include <packager/media/base/buffer_writer.h>
20#include <packager/media/base/decrypt_config.h>
21#include <packager/media/codecs/nalu_reader.h>
22
23namespace shaka {
24namespace media {
25
26namespace {
27
28const bool kEscapeData = true;
29const uint8_t kNaluStartCode[] = {0x00, 0x00, 0x00, 0x01};
30
31const uint8_t kEmulationPreventionByte = 0x03;
32
33const uint8_t kAccessUnitDelimiterRbspAnyPrimaryPicType = 0xF0;
34
35bool IsNaluEqual(const Nalu& left, const Nalu& right) {
36 if (left.type() != right.type())
37 return false;
38 const size_t left_size = left.header_size() + left.payload_size();
39 const size_t right_size = right.header_size() + right.payload_size();
40 if (left_size != right_size)
41 return false;
42 return memcmp(left.data(), right.data(), left_size) == 0;
43}
44
45void AppendNalu(const Nalu& nalu,
46 int /*nalu_length_size*/,
47 bool escape_data,
48 BufferWriter* buffer_writer) {
49 if (escape_data) {
50 EscapeNalByteSequence(nalu.data(), nalu.header_size() + nalu.payload_size(),
51 buffer_writer);
52 } else {
53 buffer_writer->AppendArray(nalu.data(),
54 nalu.header_size() + nalu.payload_size());
55 }
56}
57
58void AddAccessUnitDelimiter(BufferWriter* buffer_writer) {
59 buffer_writer->AppendInt(static_cast<uint8_t>(Nalu::H264_AUD));
60 // For now, primary_pic_type is 7 which is "anything".
61 buffer_writer->AppendInt(kAccessUnitDelimiterRbspAnyPrimaryPicType);
62}
63
64} // namespace
65
66void EscapeNalByteSequence(const uint8_t* input,
67 size_t input_size,
68 BufferWriter* output_writer) {
69 // Keep track of consecutive zeros that it has seen (not including the current
70 // byte), so that the algorithm doesn't need to go back to check the same
71 // bytes.
72 int consecutive_zero_count = 0;
73 for (size_t i = 0; i < input_size; ++i) {
74 if (consecutive_zero_count <= 1) {
75 output_writer->AppendInt(input[i]);
76 } else if (consecutive_zero_count == 2) {
77 if (input[i] == 0 || input[i] == 1 || input[i] == 2 || input[i] == 3) {
78 // Must be escaped.
79 output_writer->AppendInt(kEmulationPreventionByte);
80 }
81
82 output_writer->AppendInt(input[i]);
83 // Note that input[i] can be 0.
84 // 00 00 00 00 00 00 should become
85 // 00 00 03 00 00 03 00 00 03
86 // So consecutive_zero_count is reset here and incremented below if
87 // input[i] is 0.
88 consecutive_zero_count = 0;
89 }
90
91 consecutive_zero_count = input[i] == 0 ? consecutive_zero_count + 1 : 0;
92 }
93
94 // ISO 14496-10 Section 7.4.1.1 mentions that if the last byte is 0 (which
95 // only happens if RBSP has cabac_zero_word), 0x03 must be appended.
96 if (consecutive_zero_count > 0) {
97 DCHECK_GT(input_size, 0u);
98 DCHECK_EQ(input[input_size - 1], 0u);
99 output_writer->AppendInt(kEmulationPreventionByte);
100 }
101}
102
103// This functions creates a new subsample entry (|clear_bytes|, |cipher_bytes|)
104// and appends it to |subsamples|. It splits the oversized (64KB) clear_bytes
105// into smaller ones.
106void AppendSubsamples(uint32_t clear_bytes,
107 uint32_t cipher_bytes,
108 std::vector<SubsampleEntry>* subsamples) {
109 while (clear_bytes > UINT16_MAX) {
110 subsamples->emplace_back(UINT16_MAX, 0);
111 clear_bytes -= UINT16_MAX;
112 }
113 subsamples->emplace_back(clear_bytes, cipher_bytes);
114}
115
116// TODO(hmchen): Wrap methods of processing subsamples into a separate class,
117// e.g., SubsampleReader.
118// This function finds the range of the subsamples corresponding a NAL unit
119// size. If a subsample crosses the boundary of two NAL units, it is split into
120// smaller subsamples. Each call processes one NAL unit and it assumes the input
121// NAL unit is already aligned with subsamples->at(start_subsample_id).
122//
123// An example of calling multiple times on each NAL unit is as follow:
124//
125// Input:
126//
127// Nalu 0 Nalu 1 Nalu 2
128// | | |
129// v v v
130// | clear | cipher | clear | clear | clear | cipher |
131//
132// | Subsample 0 | Subsample 1 |
133//
134// Output:
135//
136// | Subsample 0 | Subsample 1 | Subsample 2 | Subsample 3 |
137//
138// Nalu 0: start_subsample_id = 0, next_subsample_id = 2
139// Nalu 1: start_subsample_id = 2, next_subsample_id = 3
140// Nalu 2: start_subsample_id = 3, next_subsample_id = 4
141bool AlignSubsamplesWithNalu(size_t nalu_size,
142 size_t start_subsample_id,
143 std::vector<SubsampleEntry>* subsamples,
144 size_t* next_subsample_id) {
145 DCHECK(subsamples && !subsamples->empty());
146 size_t subsample_id = start_subsample_id;
147 size_t nalu_size_remain = nalu_size;
148 size_t subsample_bytes = 0;
149 while (subsample_id < subsamples->size()) {
150 subsample_bytes = subsamples->at(subsample_id).clear_bytes +
151 subsamples->at(subsample_id).cipher_bytes;
152 if (nalu_size_remain <= subsample_bytes) {
153 break;
154 }
155 nalu_size_remain -= subsample_bytes;
156 subsample_id++;
157 }
158
159 if (subsample_id == subsamples->size()) {
160 DCHECK_GT(nalu_size_remain, 0u);
161 LOG(ERROR)
162 << "Total size of NAL unit is larger than the size of subsamples.";
163 return false;
164 }
165
166 if (nalu_size_remain == subsample_bytes) {
167 *next_subsample_id = subsample_id + 1;
168 return true;
169 }
170
171 DCHECK_GT(subsample_bytes, nalu_size_remain);
172 size_t clear_bytes = subsamples->at(subsample_id).clear_bytes;
173 size_t new_clear_bytes = 0;
174 size_t new_cipher_bytes = 0;
175 if (nalu_size_remain < clear_bytes) {
176 new_clear_bytes = nalu_size_remain;
177 } else {
178 new_clear_bytes = clear_bytes;
179 new_cipher_bytes = nalu_size_remain - clear_bytes;
180 }
181 subsamples->insert(subsamples->begin() + subsample_id,
182 SubsampleEntry(static_cast<uint16_t>(new_clear_bytes),
183 static_cast<uint32_t>(new_cipher_bytes)));
184 subsample_id++;
185 subsamples->at(subsample_id).clear_bytes -=
186 static_cast<uint16_t>(new_clear_bytes);
187 subsamples->at(subsample_id).cipher_bytes -=
188 static_cast<uint32_t>(new_cipher_bytes);
189 *next_subsample_id = subsample_id;
190 return true;
191}
192
193// This function tries to merge clear-only into clear+cipher subsamples. This
194// merge makes sure the clear_bytes will not exceed the clear size limits
195// (2^16 bytes).
196std::vector<SubsampleEntry> MergeSubsamples(
197 const std::vector<SubsampleEntry>& subsamples) {
198 std::vector<SubsampleEntry> new_subsamples;
199 uint32_t clear_bytes = 0;
200 for (size_t i = 0; i < subsamples.size(); ++i) {
201 clear_bytes += subsamples[i].clear_bytes;
202 // Add new subsample(s).
203 if (subsamples[i].cipher_bytes > 0 || i == subsamples.size() - 1) {
204 AppendSubsamples(clear_bytes, subsamples[i].cipher_bytes,
205 &new_subsamples);
206 clear_bytes = 0;
207 }
208 }
209 return new_subsamples;
210}
211
212NalUnitToByteStreamConverter::NalUnitToByteStreamConverter()
213 : nalu_length_size_(0) {}
214NalUnitToByteStreamConverter::~NalUnitToByteStreamConverter() {}
215
216bool NalUnitToByteStreamConverter::Initialize(
217 const uint8_t* decoder_configuration_data,
218 size_t decoder_configuration_data_size) {
219 if (!decoder_configuration_data || decoder_configuration_data_size == 0) {
220 LOG(ERROR) << "Decoder conguration is empty.";
221 return false;
222 }
223
224 if (!decoder_config_.Parse(std::vector<uint8_t>(
225 decoder_configuration_data,
226 decoder_configuration_data + decoder_configuration_data_size))) {
227 return false;
228 }
229
230 if (decoder_config_.nalu_count() < 2) {
231 LOG(ERROR) << "Cannot find SPS or PPS.";
232 return false;
233 }
234
235 nalu_length_size_ = decoder_config_.nalu_length_size();
236
237 BufferWriter buffer_writer(decoder_configuration_data_size);
238 bool found_sps = false;
239 bool found_pps = false;
240 for (uint32_t i = 0; i < decoder_config_.nalu_count(); ++i) {
241 const Nalu& nalu = decoder_config_.nalu(i);
242 if (nalu.type() == Nalu::H264NaluType::H264_SPS) {
243 buffer_writer.AppendArray(kNaluStartCode, std::size(kNaluStartCode));
244 AppendNalu(nalu, nalu_length_size_, !kEscapeData, &buffer_writer);
245 found_sps = true;
246 } else if (nalu.type() == Nalu::H264NaluType::H264_PPS) {
247 buffer_writer.AppendArray(kNaluStartCode, std::size(kNaluStartCode));
248 AppendNalu(nalu, nalu_length_size_, !kEscapeData, &buffer_writer);
249 found_pps = true;
250 } else if (nalu.type() == Nalu::H264NaluType::H264_SPSExtension) {
251 buffer_writer.AppendArray(kNaluStartCode, std::size(kNaluStartCode));
252 AppendNalu(nalu, nalu_length_size_, !kEscapeData, &buffer_writer);
253 }
254 }
255 if (!found_sps || !found_pps) {
256 LOG(ERROR) << "Failed to find SPS or PPS.";
257 return false;
258 }
259
260 buffer_writer.SwapBuffer(&decoder_configuration_in_byte_stream_);
261 return true;
262}
263
264bool NalUnitToByteStreamConverter::ConvertUnitToByteStream(
265 const uint8_t* sample,
266 size_t sample_size,
267 bool is_key_frame,
268 std::vector<uint8_t>* output) {
269 return ConvertUnitToByteStreamWithSubsamples(
270 sample, sample_size, is_key_frame, false, output,
271 nullptr); // Skip subsample update.
272}
273
274// This ignores all AUD, SPS, and PPS in the sample. Instead uses the data
275// parsed in Initialize(). However, if the SPS and PPS are different to
276// those parsed in Initialized(), they are kept.
277bool NalUnitToByteStreamConverter::ConvertUnitToByteStreamWithSubsamples(
278 const uint8_t* sample,
279 size_t sample_size,
280 bool is_key_frame,
281 bool escape_encrypted_nalu,
282 std::vector<uint8_t>* output,
283 std::vector<SubsampleEntry>* subsamples) {
284 if (!sample || sample_size == 0) {
285 LOG(WARNING) << "Sample is empty.";
286 return true;
287 }
288
289 std::vector<SubsampleEntry> temp_subsamples;
290
291 BufferWriter buffer_writer(sample_size);
292 buffer_writer.AppendArray(kNaluStartCode, std::size(kNaluStartCode));
293 AddAccessUnitDelimiter(&buffer_writer);
294 if (is_key_frame)
295 buffer_writer.AppendVector(decoder_configuration_in_byte_stream_);
296
297 if (subsamples && !subsamples->empty()) {
298 // The inserted part in buffer_writer is all clear. Add a corresponding
299 // all-clear subsample.
300 AppendSubsamples(static_cast<uint32_t>(buffer_writer.Size()), 0u,
301 &temp_subsamples);
302 }
303
304 NaluReader nalu_reader(Nalu::kH264, nalu_length_size_, sample, sample_size);
305 Nalu nalu;
306 NaluReader::Result result = nalu_reader.Advance(&nalu);
307
308 size_t start_subsample_id = 0;
309 size_t next_subsample_id = 0;
310 while (result == NaluReader::kOk) {
311 const size_t old_nalu_size =
312 nalu_length_size_ + nalu.header_size() + nalu.payload_size();
313 if (subsamples && !subsamples->empty()) {
314 if (!AlignSubsamplesWithNalu(old_nalu_size, start_subsample_id,
315 subsamples, &next_subsample_id)) {
316 return false;
317 }
318 }
319 switch (nalu.type()) {
320 case Nalu::H264_AUD:
321 break;
322 case Nalu::H264_SPS:
323 FALLTHROUGH_INTENDED;
324 case Nalu::H264_SPSExtension:
325 FALLTHROUGH_INTENDED;
326 case Nalu::H264_PPS: {
327 // Also write this SPS/PPS if it is not the same as SPS/PPS in decoder
328 // configuration, which is already written.
329 //
330 // For more information see:
331 // - github.com/shaka-project/shaka-packager/issues/327
332 // - ISO/IEC 14496-15 5.4.5 Sync Sample
333 //
334 // TODO(kqyang): Parse sample data to figure out which SPS/PPS the
335 // sample actually uses and include that only.
336 bool new_decoder_config = true;
337 for (size_t i = 0; i < decoder_config_.nalu_count(); ++i) {
338 if (IsNaluEqual(decoder_config_.nalu(i), nalu)) {
339 new_decoder_config = false;
340 break;
341 }
342 }
343 if (!new_decoder_config)
344 break;
345 FALLTHROUGH_INTENDED;
346 }
347 default:
348 bool escape_data = false;
349 if (subsamples && !subsamples->empty()) {
350 if (escape_encrypted_nalu) {
351 for (size_t i = start_subsample_id; i < next_subsample_id; ++i) {
352 if (subsamples->at(i).cipher_bytes != 0) {
353 escape_data = true;
354 break;
355 }
356 }
357 }
358 }
359 buffer_writer.AppendArray(kNaluStartCode, std::size(kNaluStartCode));
360 AppendNalu(nalu, nalu_length_size_, escape_data, &buffer_writer);
361
362 if (subsamples && !subsamples->empty()) {
363 temp_subsamples.emplace_back(
364 static_cast<uint16_t>(std::size(kNaluStartCode)), 0u);
365 // Update the first subsample of each NAL unit, which replaces NAL
366 // unit length field with start code. Note that if the escape_data is
367 // true, the total data size and the cipher_bytes may be changed.
368 // However, since the escape_data for encrypted nalu is only used in
369 // Sample-AES, which means the subsample is not really used,
370 // inaccurate subsamples should not be a big deal.
371 if (subsamples->at(start_subsample_id).clear_bytes <
372 nalu_length_size_) {
373 LOG(ERROR) << "Clear bytes ("
374 << subsamples->at(start_subsample_id).clear_bytes
375 << ") in start subsample of NAL unit is less than NAL "
376 "unit length size ("
377 << nalu_length_size_
378 << "). The NAL unit length size is (partially) "
379 "encrypted. In that case, it cannot be "
380 "converted to byte stream.";
381 return false;
382 }
383 subsamples->at(start_subsample_id).clear_bytes -= nalu_length_size_;
384 temp_subsamples.insert(temp_subsamples.end(),
385 subsamples->begin() + start_subsample_id,
386 subsamples->begin() + next_subsample_id);
387 }
388 break;
389 }
390
391 start_subsample_id = next_subsample_id;
392 result = nalu_reader.Advance(&nalu);
393 }
394
395 DCHECK_NE(result, NaluReader::kOk);
396 if (result != NaluReader::kEOStream) {
397 LOG(ERROR) << "Stopped reading before end of stream.";
398 return false;
399 }
400
401 buffer_writer.SwapBuffer(output);
402 if (subsamples && !subsamples->empty()) {
403 if (next_subsample_id < subsamples->size()) {
404 LOG(ERROR)
405 << "The total size of NAL unit is shorter than the subsample size.";
406 return false;
407 }
408 // This function may modify the new_subsamples. But since it creates a
409 // merged verion and assign to the output subsamples, the input one is no
410 // longer used.
411 *subsamples = MergeSubsamples(temp_subsamples);
412 }
413 return true;
414}
415
416} // namespace media
417} // namespace shaka
Result Advance(Nalu *nalu)
uint64_t header_size() const
The size of the header, e.g. 1 for H.264.
uint64_t payload_size() const
Size of this Nalu minus header_size().
All the methods that are virtual are virtual for mocking.