Shaka Packager SDK
Loading...
Searching...
No Matches
h264_parser.cc
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <packager/media/codecs/h264_parser.h>
6
7#include <memory>
8
9#include <absl/log/check.h>
10#include <absl/log/log.h>
11
12#include <packager/macros/logging.h>
13#include <packager/media/base/buffer_reader.h>
14
15#define LOG_ERROR_ONCE(msg) \
16 do { \
17 static bool logged_once = false; \
18 LOG_IF(ERROR, !logged_once) << msg; \
19 logged_once = true; \
20 } while (0)
21
22namespace shaka {
23namespace media {
24
25// Implemented according to ISO/IEC 14496-10:2005 7.4.2.1 Sequence parameter set
26// RBSP semantics.
27bool ExtractResolutionFromSps(const H264Sps& sps,
28 uint32_t* coded_width,
29 uint32_t* coded_height,
30 uint32_t* pixel_width,
31 uint32_t* pixel_height) {
32 int crop_x = 0;
33 int crop_y = 0;
34 if (sps.frame_cropping_flag) {
35 int sub_width_c = 0;
36 int sub_height_c = 0;
37 // Table 6-1.
38 switch (sps.chroma_format_idc) {
39 case 0: // monochrome
40 // SubWidthC and SubHeightC are not defined for monochrome. For ease of
41 // computation afterwards, assign both to 1.
42 sub_width_c = 1;
43 sub_height_c = 1;
44 break;
45 case 1: // 4:2:0
46 sub_width_c = 2;
47 sub_height_c = 2;
48 break;
49 case 2: // 4:2:2
50 sub_width_c = 2;
51 sub_height_c = 1;
52 break;
53 case 3: // 4:4:4
54 sub_width_c = 1;
55 sub_height_c = 1;
56 break;
57 default:
58 LOG(ERROR) << "Unexpected chroma_format_idc " << sps.chroma_format_idc;
59 return false;
60 }
61
62 // Formula 7-16, 7-17, 7-18, 7-19.
63 int crop_unit_x = sub_width_c;
64 int crop_unit_y = sub_height_c * (2 - (sps.frame_mbs_only_flag ? 1 : 0));
65 crop_x = crop_unit_x *
66 (sps.frame_crop_left_offset + sps.frame_crop_right_offset);
67 crop_y = crop_unit_y *
68 (sps.frame_crop_top_offset + sps.frame_crop_bottom_offset);
69 }
70
71 // Formula 7-10, 7-11.
72 int pic_width_in_mbs = sps.pic_width_in_mbs_minus1 + 1;
73 *coded_width = pic_width_in_mbs * 16 - crop_x;
74
75 // Formula 7-13, 7-15.
76 int pic_height_in_mbs = (2 - (sps.frame_mbs_only_flag ? 1 : 0)) *
77 (sps.pic_height_in_map_units_minus1 + 1);
78 *coded_height = pic_height_in_mbs * 16 - crop_y;
79
80 // 0 means it wasn't in the SPS and therefore assume 1.
81 *pixel_width = sps.sar_width == 0 ? 1 : sps.sar_width;
82 *pixel_height = sps.sar_height == 0 ? 1 : sps.sar_height;
83 DVLOG(2) << "Found coded_width: " << *coded_width
84 << " coded_height: " << *coded_height
85 << " pixel_width: " << *pixel_width
86 << " pixel_height: " << *pixel_height;
87 return true;
88}
89
90bool H264SliceHeader::IsPSlice() const {
91 return (slice_type % 5 == kPSlice);
92}
93
94bool H264SliceHeader::IsBSlice() const {
95 return (slice_type % 5 == kBSlice);
96}
97
98bool H264SliceHeader::IsISlice() const {
99 return (slice_type % 5 == kISlice);
100}
101
102bool H264SliceHeader::IsSPSlice() const {
103 return (slice_type % 5 == kSPSlice);
104}
105
106bool H264SliceHeader::IsSISlice() const {
107 return (slice_type % 5 == kSISlice);
108}
109
110#define READ_BITS_OR_RETURN(num_bits, out) \
111 do { \
112 if (!br->ReadBits(num_bits, (out))) { \
113 DVLOG(1) \
114 << "Error in stream: unexpected EOS while trying to read " #out; \
115 return kInvalidStream; \
116 } \
117 } while (0)
118
119#define READ_LONG_OR_RETURN(out) \
120 do { \
121 long _out; \
122 int _tmp_out; \
123 READ_BITS_OR_RETURN(16, &_tmp_out); \
124 _out = (long)(_tmp_out) << 16; \
125 READ_BITS_OR_RETURN(16, &_tmp_out); \
126 _out |= _tmp_out; \
127 *(out) = _out; \
128 } while (0)
129
130#define READ_BOOL_OR_RETURN(out) \
131 do { \
132 int _out; \
133 if (!br->ReadBits(1, &_out)) { \
134 DVLOG(1) \
135 << "Error in stream: unexpected EOS while trying to read " #out; \
136 return kInvalidStream; \
137 } \
138 *(out) = _out != 0; \
139 } while (0)
140
141#define READ_UE_OR_RETURN(out) \
142 do { \
143 if (!br->ReadUE(out)) { \
144 DVLOG(1) << "Error in stream: invalid value while trying to read " #out; \
145 return kInvalidStream; \
146 } \
147 } while (0)
148
149#define READ_SE_OR_RETURN(out) \
150 do { \
151 if (!br->ReadSE(out)) { \
152 DVLOG(1) << "Error in stream: invalid value while trying to read " #out; \
153 return kInvalidStream; \
154 } \
155 } while (0)
156
157#define IN_RANGE_OR_RETURN(val, min, max) \
158 do { \
159 if ((val) < (min) || (val) > (max)) { \
160 DVLOG(1) << "Error in stream: invalid value, expected " #val " to be" \
161 << " in range [" << (min) << ":" << (max) << "]" \
162 << " found " << (val) << " instead"; \
163 return kInvalidStream; \
164 } \
165 } while (0)
166
167#define TRUE_OR_RETURN(a) \
168 do { \
169 if (!(a)) { \
170 DVLOG(1) << "Error in stream: invalid value, expected " << #a; \
171 return kInvalidStream; \
172 } \
173 } while (0)
174
175enum AspectRatioIdc {
176 kExtendedSar = 255,
177};
178
179// ISO 14496 part 10
180// VUI parameters: Table E-1 "Meaning of sample aspect ratio indicator"
181static const int kTableSarWidth[] = {0, 1, 12, 10, 16, 40, 24, 20, 32,
182 80, 18, 15, 64, 160, 4, 3, 2};
183static const int kTableSarHeight[] = {0, 1, 11, 11, 11, 33, 11, 11, 11,
184 33, 11, 11, 33, 99, 3, 2, 1};
185static_assert(std::size(kTableSarWidth) == std::size(kTableSarHeight),
186 "sar_tables_must_have_same_size");
187
188H264Parser::H264Parser() {}
189
190H264Parser::~H264Parser() {}
191
192const H264Pps* H264Parser::GetPps(int pps_id) {
193 return active_PPSes_[pps_id].get();
194}
195
196const H264Sps* H264Parser::GetSps(int sps_id) {
197 return active_SPSes_[sps_id].get();
198}
199
200// Default scaling lists (per spec).
201static const int kDefault4x4Intra[kH264ScalingList4x4Length] = {
202 6, 13, 13, 20, 20, 20, 28, 28, 28, 28, 32, 32, 32, 37, 37, 42,
203};
204
205static const int kDefault4x4Inter[kH264ScalingList4x4Length] = {
206 10, 14, 14, 20, 20, 20, 24, 24, 24, 24, 27, 27, 27, 30, 30, 34,
207};
208
209static const int kDefault8x8Intra[kH264ScalingList8x8Length] = {
210 6, 10, 10, 13, 11, 13, 16, 16, 16, 16, 18, 18, 18, 18, 18, 23,
211 23, 23, 23, 23, 23, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27,
212 27, 27, 27, 27, 29, 29, 29, 29, 29, 29, 29, 31, 31, 31, 31, 31,
213 31, 33, 33, 33, 33, 33, 36, 36, 36, 36, 38, 38, 38, 40, 40, 42,
214};
215
216static const int kDefault8x8Inter[kH264ScalingList8x8Length] = {
217 9, 13, 13, 15, 13, 15, 17, 17, 17, 17, 19, 19, 19, 19, 19, 21,
218 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 24, 24, 24, 24,
219 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27, 27,
220 27, 28, 28, 28, 28, 28, 30, 30, 30, 30, 32, 32, 32, 33, 33, 35,
221};
222
223static inline void DefaultScalingList4x4(
224 int i,
225 int scaling_list4x4[][kH264ScalingList4x4Length]) {
226 DCHECK_LT(i, 6);
227
228 if (i < 3)
229 memcpy(scaling_list4x4[i], kDefault4x4Intra, sizeof(kDefault4x4Intra));
230 else if (i < 6)
231 memcpy(scaling_list4x4[i], kDefault4x4Inter, sizeof(kDefault4x4Inter));
232}
233
234static inline void DefaultScalingList8x8(
235 int i,
236 int scaling_list8x8[][kH264ScalingList8x8Length]) {
237 DCHECK_LT(i, 6);
238
239 if (i % 2 == 0)
240 memcpy(scaling_list8x8[i], kDefault8x8Intra, sizeof(kDefault8x8Intra));
241 else
242 memcpy(scaling_list8x8[i], kDefault8x8Inter, sizeof(kDefault8x8Inter));
243}
244
245static void FallbackScalingList4x4(
246 int i,
247 const int default_scaling_list_intra[],
248 const int default_scaling_list_inter[],
249 int scaling_list4x4[][kH264ScalingList4x4Length]) {
250 static const int kScalingList4x4ByteSize =
251 sizeof(scaling_list4x4[0][0]) * kH264ScalingList4x4Length;
252
253 switch (i) {
254 case 0:
255 memcpy(scaling_list4x4[i], default_scaling_list_intra,
256 kScalingList4x4ByteSize);
257 break;
258
259 case 1:
260 memcpy(scaling_list4x4[i], scaling_list4x4[0], kScalingList4x4ByteSize);
261 break;
262
263 case 2:
264 memcpy(scaling_list4x4[i], scaling_list4x4[1], kScalingList4x4ByteSize);
265 break;
266
267 case 3:
268 memcpy(scaling_list4x4[i], default_scaling_list_inter,
269 kScalingList4x4ByteSize);
270 break;
271
272 case 4:
273 memcpy(scaling_list4x4[i], scaling_list4x4[3], kScalingList4x4ByteSize);
274 break;
275
276 case 5:
277 memcpy(scaling_list4x4[i], scaling_list4x4[4], kScalingList4x4ByteSize);
278 break;
279
280 default:
281 NOTIMPLEMENTED() << "index out of range [0,5]: " << i;
282 break;
283 }
284}
285
286static void FallbackScalingList8x8(
287 int i,
288 const int default_scaling_list_intra[],
289 const int default_scaling_list_inter[],
290 int scaling_list8x8[][kH264ScalingList8x8Length]) {
291 static const int kScalingList8x8ByteSize =
292 sizeof(scaling_list8x8[0][0]) * kH264ScalingList8x8Length;
293
294 switch (i) {
295 case 0:
296 memcpy(scaling_list8x8[i], default_scaling_list_intra,
297 kScalingList8x8ByteSize);
298 break;
299
300 case 1:
301 memcpy(scaling_list8x8[i], default_scaling_list_inter,
302 kScalingList8x8ByteSize);
303 break;
304
305 case 2:
306 memcpy(scaling_list8x8[i], scaling_list8x8[0], kScalingList8x8ByteSize);
307 break;
308
309 case 3:
310 memcpy(scaling_list8x8[i], scaling_list8x8[1], kScalingList8x8ByteSize);
311 break;
312
313 case 4:
314 memcpy(scaling_list8x8[i], scaling_list8x8[2], kScalingList8x8ByteSize);
315 break;
316
317 case 5:
318 memcpy(scaling_list8x8[i], scaling_list8x8[3], kScalingList8x8ByteSize);
319 break;
320
321 default:
322 NOTIMPLEMENTED() << "index out of range [0,5]: " << i;
323 break;
324 }
325}
326
327H264Parser::Result H264Parser::ParseScalingList(H26xBitReader* br,
328 int size,
329 int* scaling_list,
330 bool* use_default) {
331 // See chapter 7.3.2.1.1.1.
332 int last_scale = 8;
333 int next_scale = 8;
334 int delta_scale;
335
336 *use_default = false;
337
338 for (int j = 0; j < size; ++j) {
339 if (next_scale != 0) {
340 READ_SE_OR_RETURN(&delta_scale);
341 IN_RANGE_OR_RETURN(delta_scale, -128, 127);
342 next_scale = (last_scale + delta_scale + 256) & 0xff;
343
344 if (j == 0 && next_scale == 0) {
345 *use_default = true;
346 return kOk;
347 }
348 }
349
350 scaling_list[j] = (next_scale == 0) ? last_scale : next_scale;
351 last_scale = scaling_list[j];
352 }
353
354 return kOk;
355}
356
357H264Parser::Result H264Parser::ParseSpsScalingLists(H26xBitReader* br,
358 H264Sps* sps) {
359 // See 7.4.2.1.1.
360 bool seq_scaling_list_present_flag;
361 bool use_default;
362 Result res;
363
364 // Parse scaling_list4x4.
365 for (int i = 0; i < 6; ++i) {
366 READ_BOOL_OR_RETURN(&seq_scaling_list_present_flag);
367
368 if (seq_scaling_list_present_flag) {
369 res = ParseScalingList(br, std::size(sps->scaling_list4x4[i]),
370 sps->scaling_list4x4[i], &use_default);
371 if (res != kOk)
372 return res;
373
374 if (use_default)
375 DefaultScalingList4x4(i, sps->scaling_list4x4);
376
377 } else {
378 FallbackScalingList4x4(i, kDefault4x4Intra, kDefault4x4Inter,
379 sps->scaling_list4x4);
380 }
381 }
382
383 // Parse scaling_list8x8.
384 for (int i = 0; i < ((sps->chroma_format_idc != 3) ? 2 : 6); ++i) {
385 READ_BOOL_OR_RETURN(&seq_scaling_list_present_flag);
386
387 if (seq_scaling_list_present_flag) {
388 res = ParseScalingList(br, std::size(sps->scaling_list8x8[i]),
389 sps->scaling_list8x8[i], &use_default);
390 if (res != kOk)
391 return res;
392
393 if (use_default)
394 DefaultScalingList8x8(i, sps->scaling_list8x8);
395
396 } else {
397 FallbackScalingList8x8(i, kDefault8x8Intra, kDefault8x8Inter,
398 sps->scaling_list8x8);
399 }
400 }
401
402 return kOk;
403}
404
405H264Parser::Result H264Parser::ParsePpsScalingLists(H26xBitReader* br,
406 const H264Sps& sps,
407 H264Pps* pps) {
408 // See 7.4.2.2.
409 bool pic_scaling_list_present_flag;
410 bool use_default;
411 Result res;
412
413 for (int i = 0; i < 6; ++i) {
414 READ_BOOL_OR_RETURN(&pic_scaling_list_present_flag);
415
416 if (pic_scaling_list_present_flag) {
417 res = ParseScalingList(br, std::size(pps->scaling_list4x4[i]),
418 pps->scaling_list4x4[i], &use_default);
419 if (res != kOk)
420 return res;
421
422 if (use_default)
423 DefaultScalingList4x4(i, pps->scaling_list4x4);
424
425 } else {
426 if (sps.seq_scaling_matrix_present_flag) {
427 // Table 7-2 fallback rule A in spec.
428 FallbackScalingList4x4(i, kDefault4x4Intra, kDefault4x4Inter,
429 pps->scaling_list4x4);
430 } else {
431 // Table 7-2 fallback rule B in spec.
432 FallbackScalingList4x4(i, sps.scaling_list4x4[0],
433 sps.scaling_list4x4[3], pps->scaling_list4x4);
434 }
435 }
436 }
437
438 if (pps->transform_8x8_mode_flag) {
439 for (int i = 0; i < ((sps.chroma_format_idc != 3) ? 2 : 6); ++i) {
440 READ_BOOL_OR_RETURN(&pic_scaling_list_present_flag);
441
442 if (pic_scaling_list_present_flag) {
443 res = ParseScalingList(br, std::size(pps->scaling_list8x8[i]),
444 pps->scaling_list8x8[i], &use_default);
445 if (res != kOk)
446 return res;
447
448 if (use_default)
449 DefaultScalingList8x8(i, pps->scaling_list8x8);
450
451 } else {
452 if (sps.seq_scaling_matrix_present_flag) {
453 // Table 7-2 fallback rule A in spec.
454 FallbackScalingList8x8(i, kDefault8x8Intra, kDefault8x8Inter,
455 pps->scaling_list8x8);
456 } else {
457 // Table 7-2 fallback rule B in spec.
458 FallbackScalingList8x8(i, sps.scaling_list8x8[0],
459 sps.scaling_list8x8[1], pps->scaling_list8x8);
460 }
461 }
462 }
463 }
464 return kOk;
465}
466
467H264Parser::Result H264Parser::ParseAndIgnoreHRDParameters(
468 H26xBitReader* br,
469 bool* hrd_parameters_present) {
470 int data;
471 READ_BOOL_OR_RETURN(&data); // {nal,vcl}_hrd_parameters_present_flag
472 if (!data)
473 return kOk;
474
475 *hrd_parameters_present = true;
476
477 int cpb_cnt_minus1;
478 READ_UE_OR_RETURN(&cpb_cnt_minus1);
479 IN_RANGE_OR_RETURN(cpb_cnt_minus1, 0, 31);
480 READ_BITS_OR_RETURN(8, &data); // bit_rate_scale, cpb_size_scale
481 for (int i = 0; i <= cpb_cnt_minus1; ++i) {
482 READ_UE_OR_RETURN(&data); // bit_rate_value_minus1[i]
483 READ_UE_OR_RETURN(&data); // cpb_size_value_minus1[i]
484 READ_BOOL_OR_RETURN(&data); // cbr_flag
485 }
486 READ_BITS_OR_RETURN(20, &data); // cpb/dpb delays, etc.
487
488 return kOk;
489}
490
491H264Parser::Result H264Parser::ParseVUIParameters(H26xBitReader* br,
492 H264Sps* sps) {
493 bool aspect_ratio_info_present_flag;
494 READ_BOOL_OR_RETURN(&aspect_ratio_info_present_flag);
495 if (aspect_ratio_info_present_flag) {
496 int aspect_ratio_idc;
497 READ_BITS_OR_RETURN(8, &aspect_ratio_idc);
498 if (aspect_ratio_idc == kExtendedSar) {
499 READ_BITS_OR_RETURN(16, &sps->sar_width);
500 READ_BITS_OR_RETURN(16, &sps->sar_height);
501 } else {
502 const int max_aspect_ratio_idc = std::size(kTableSarWidth) - 1;
503 IN_RANGE_OR_RETURN(aspect_ratio_idc, 0, max_aspect_ratio_idc);
504 sps->sar_width = kTableSarWidth[aspect_ratio_idc];
505 sps->sar_height = kTableSarHeight[aspect_ratio_idc];
506 }
507 }
508
509 int data;
510 // Read and ignore overscan and video signal type info.
511 READ_BOOL_OR_RETURN(&data); // overscan_info_present_flag
512 if (data)
513 READ_BOOL_OR_RETURN(&data); // overscan_appropriate_flag
514
515 READ_BOOL_OR_RETURN(&data); // video_signal_type_present_flag
516 if (data) {
517 READ_BITS_OR_RETURN(3, &data); // video_format
518 READ_BOOL_OR_RETURN(&data); // video_full_range_flag
519 READ_BOOL_OR_RETURN(&data); // colour_description_present_flag
520 if (data) {
521 READ_BITS_OR_RETURN(8, &sps->color_primaries); // colour primaries
522 READ_BITS_OR_RETURN(8, &sps->transfer_characteristics);
523 READ_BITS_OR_RETURN(8, &sps->matrix_coefficients); // matrix coeffs
524 }
525 }
526
527 READ_BOOL_OR_RETURN(&data); // chroma_loc_info_present_flag
528 if (data) {
529 READ_UE_OR_RETURN(&data); // chroma_sample_loc_type_top_field
530 READ_UE_OR_RETURN(&data); // chroma_sample_loc_type_bottom_field
531 }
532
533 // Read timing info.
534 READ_BOOL_OR_RETURN(&sps->timing_info_present_flag);
535 if (sps->timing_info_present_flag) {
536 READ_LONG_OR_RETURN(&sps->num_units_in_tick);
537 READ_LONG_OR_RETURN(&sps->time_scale);
538 READ_BOOL_OR_RETURN(&sps->fixed_frame_rate_flag);
539 }
540
541 // Read and ignore NAL HRD parameters, if present.
542 bool hrd_parameters_present = false;
543 Result res = ParseAndIgnoreHRDParameters(br, &hrd_parameters_present);
544 if (res != kOk)
545 return res;
546
547 // Read and ignore VCL HRD parameters, if present.
548 res = ParseAndIgnoreHRDParameters(br, &hrd_parameters_present);
549 if (res != kOk)
550 return res;
551
552 if (hrd_parameters_present) // One of NAL or VCL params present is enough.
553 READ_BOOL_OR_RETURN(&data); // low_delay_hrd_flag
554
555 READ_BOOL_OR_RETURN(&data); // pic_struct_present_flag
556 READ_BOOL_OR_RETURN(&sps->bitstream_restriction_flag);
557 if (sps->bitstream_restriction_flag) {
558 READ_BOOL_OR_RETURN(&data); // motion_vectors_over_pic_boundaries_flag
559 READ_UE_OR_RETURN(&data); // max_bytes_per_pic_denom
560 READ_UE_OR_RETURN(&data); // max_bits_per_mb_denom
561 READ_UE_OR_RETURN(&data); // log2_max_mv_length_horizontal
562 READ_UE_OR_RETURN(&data); // log2_max_mv_length_vertical
563 READ_UE_OR_RETURN(&sps->max_num_reorder_frames);
564 READ_UE_OR_RETURN(&sps->max_dec_frame_buffering);
565 TRUE_OR_RETURN(sps->max_dec_frame_buffering >= sps->max_num_ref_frames);
566 IN_RANGE_OR_RETURN(sps->max_num_reorder_frames, 0,
567 sps->max_dec_frame_buffering);
568 }
569
570 return kOk;
571}
572
573static void FillDefaultSeqScalingLists(H264Sps* sps) {
574 for (int i = 0; i < 6; ++i)
575 for (int j = 0; j < kH264ScalingList4x4Length; ++j)
576 sps->scaling_list4x4[i][j] = 16;
577
578 for (int i = 0; i < 6; ++i)
579 for (int j = 0; j < kH264ScalingList8x8Length; ++j)
580 sps->scaling_list8x8[i][j] = 16;
581}
582
583H264Parser::Result H264Parser::ParseSps(const Nalu& nalu, int* sps_id) {
584 // See 7.4.2.1.
585 int data;
586 Result res;
587 H26xBitReader reader;
588 reader.Initialize(nalu.data() + nalu.header_size(), nalu.payload_size());
589 H26xBitReader* br = &reader;
590
591 *sps_id = -1;
592
593 std::unique_ptr<H264Sps> sps(new H264Sps());
594
595 READ_BITS_OR_RETURN(8, &sps->profile_idc);
596 READ_BOOL_OR_RETURN(&sps->constraint_set0_flag);
597 READ_BOOL_OR_RETURN(&sps->constraint_set1_flag);
598 READ_BOOL_OR_RETURN(&sps->constraint_set2_flag);
599 READ_BOOL_OR_RETURN(&sps->constraint_set3_flag);
600 READ_BOOL_OR_RETURN(&sps->constraint_set4_flag);
601 READ_BOOL_OR_RETURN(&sps->constraint_set5_flag);
602 READ_BITS_OR_RETURN(2, &data); // reserved_zero_2bits
603 READ_BITS_OR_RETURN(8, &sps->level_idc);
604 READ_UE_OR_RETURN(&sps->seq_parameter_set_id);
605 TRUE_OR_RETURN(sps->seq_parameter_set_id < 32);
606
607 if (sps->profile_idc == 100 || sps->profile_idc == 110 ||
608 sps->profile_idc == 122 || sps->profile_idc == 244 ||
609 sps->profile_idc == 44 || sps->profile_idc == 83 ||
610 sps->profile_idc == 86 || sps->profile_idc == 118 ||
611 sps->profile_idc == 128) {
612 READ_UE_OR_RETURN(&sps->chroma_format_idc);
613 TRUE_OR_RETURN(sps->chroma_format_idc < 4);
614
615 if (sps->chroma_format_idc == 3)
616 READ_BOOL_OR_RETURN(&sps->separate_colour_plane_flag);
617
618 READ_UE_OR_RETURN(&sps->bit_depth_luma_minus8);
619 TRUE_OR_RETURN(sps->bit_depth_luma_minus8 < 7);
620
621 READ_UE_OR_RETURN(&sps->bit_depth_chroma_minus8);
622 TRUE_OR_RETURN(sps->bit_depth_chroma_minus8 < 7);
623
624 READ_BOOL_OR_RETURN(&sps->qpprime_y_zero_transform_bypass_flag);
625 READ_BOOL_OR_RETURN(&sps->seq_scaling_matrix_present_flag);
626
627 if (sps->seq_scaling_matrix_present_flag) {
628 DVLOG(4) << "Scaling matrix present";
629 res = ParseSpsScalingLists(br, sps.get());
630 if (res != kOk)
631 return res;
632 } else {
633 FillDefaultSeqScalingLists(sps.get());
634 }
635 } else {
636 sps->chroma_format_idc = 1;
637 FillDefaultSeqScalingLists(sps.get());
638 }
639
640 if (sps->separate_colour_plane_flag)
641 sps->chroma_array_type = 0;
642 else
643 sps->chroma_array_type = sps->chroma_format_idc;
644
645 READ_UE_OR_RETURN(&sps->log2_max_frame_num_minus4);
646 TRUE_OR_RETURN(sps->log2_max_frame_num_minus4 < 13);
647
648 READ_UE_OR_RETURN(&sps->pic_order_cnt_type);
649 TRUE_OR_RETURN(sps->pic_order_cnt_type < 3);
650
651 sps->expected_delta_per_pic_order_cnt_cycle = 0;
652 if (sps->pic_order_cnt_type == 0) {
653 READ_UE_OR_RETURN(&sps->log2_max_pic_order_cnt_lsb_minus4);
654 TRUE_OR_RETURN(sps->log2_max_pic_order_cnt_lsb_minus4 < 13);
655 } else if (sps->pic_order_cnt_type == 1) {
656 READ_BOOL_OR_RETURN(&sps->delta_pic_order_always_zero_flag);
657 READ_SE_OR_RETURN(&sps->offset_for_non_ref_pic);
658 READ_SE_OR_RETURN(&sps->offset_for_top_to_bottom_field);
659 READ_UE_OR_RETURN(&sps->num_ref_frames_in_pic_order_cnt_cycle);
660 TRUE_OR_RETURN(sps->num_ref_frames_in_pic_order_cnt_cycle < 255);
661
662 for (int i = 0; i < sps->num_ref_frames_in_pic_order_cnt_cycle; ++i) {
663 READ_SE_OR_RETURN(&sps->offset_for_ref_frame[i]);
664 sps->expected_delta_per_pic_order_cnt_cycle +=
665 sps->offset_for_ref_frame[i];
666 }
667 }
668
669 READ_UE_OR_RETURN(&sps->max_num_ref_frames);
670 READ_BOOL_OR_RETURN(&sps->gaps_in_frame_num_value_allowed_flag);
671
672 READ_UE_OR_RETURN(&sps->pic_width_in_mbs_minus1);
673 READ_UE_OR_RETURN(&sps->pic_height_in_map_units_minus1);
674
675 READ_BOOL_OR_RETURN(&sps->frame_mbs_only_flag);
676 if (!sps->frame_mbs_only_flag)
677 READ_BOOL_OR_RETURN(&sps->mb_adaptive_frame_field_flag);
678
679 READ_BOOL_OR_RETURN(&sps->direct_8x8_inference_flag);
680
681 READ_BOOL_OR_RETURN(&sps->frame_cropping_flag);
682 if (sps->frame_cropping_flag) {
683 READ_UE_OR_RETURN(&sps->frame_crop_left_offset);
684 READ_UE_OR_RETURN(&sps->frame_crop_right_offset);
685 READ_UE_OR_RETURN(&sps->frame_crop_top_offset);
686 READ_UE_OR_RETURN(&sps->frame_crop_bottom_offset);
687 }
688
689 READ_BOOL_OR_RETURN(&sps->vui_parameters_present_flag);
690 if (sps->vui_parameters_present_flag) {
691 DVLOG(4) << "VUI parameters present";
692 res = ParseVUIParameters(br, sps.get());
693 if (res != kOk)
694 return res;
695 }
696
697 // If an SPS with the same id already exists, replace it.
698 *sps_id = sps->seq_parameter_set_id;
699 active_SPSes_[*sps_id] = std::move(sps);
700
701 return kOk;
702}
703
704H264Parser::Result H264Parser::ParsePps(const Nalu& nalu, int* pps_id) {
705 // See 7.4.2.2.
706 const H264Sps* sps;
707 Result res;
708 H26xBitReader reader;
709 reader.Initialize(nalu.data() + nalu.header_size(), nalu.payload_size());
710 H26xBitReader* br = &reader;
711
712 *pps_id = -1;
713
714 std::unique_ptr<H264Pps> pps(new H264Pps());
715
716 READ_UE_OR_RETURN(&pps->pic_parameter_set_id);
717 READ_UE_OR_RETURN(&pps->seq_parameter_set_id);
718 TRUE_OR_RETURN(pps->seq_parameter_set_id < 32);
719
720 sps = GetSps(pps->seq_parameter_set_id);
721 TRUE_OR_RETURN(sps);
722
723 READ_BOOL_OR_RETURN(&pps->entropy_coding_mode_flag);
724 READ_BOOL_OR_RETURN(&pps->bottom_field_pic_order_in_frame_present_flag);
725
726 READ_UE_OR_RETURN(&pps->num_slice_groups_minus1);
727 if (pps->num_slice_groups_minus1 > 1) {
728 LOG_ERROR_ONCE("Slice groups not supported");
729 return kUnsupportedStream;
730 }
731
732 READ_UE_OR_RETURN(&pps->num_ref_idx_l0_default_active_minus1);
733 TRUE_OR_RETURN(pps->num_ref_idx_l0_default_active_minus1 < 32);
734
735 READ_UE_OR_RETURN(&pps->num_ref_idx_l1_default_active_minus1);
736 TRUE_OR_RETURN(pps->num_ref_idx_l1_default_active_minus1 < 32);
737
738 READ_BOOL_OR_RETURN(&pps->weighted_pred_flag);
739 READ_BITS_OR_RETURN(2, &pps->weighted_bipred_idc);
740 TRUE_OR_RETURN(pps->weighted_bipred_idc < 3);
741
742 READ_SE_OR_RETURN(&pps->pic_init_qp_minus26);
743 IN_RANGE_OR_RETURN(pps->pic_init_qp_minus26, -26, 25);
744
745 READ_SE_OR_RETURN(&pps->pic_init_qs_minus26);
746 IN_RANGE_OR_RETURN(pps->pic_init_qs_minus26, -26, 25);
747
748 READ_SE_OR_RETURN(&pps->chroma_qp_index_offset);
749 IN_RANGE_OR_RETURN(pps->chroma_qp_index_offset, -12, 12);
750 pps->second_chroma_qp_index_offset = pps->chroma_qp_index_offset;
751
752 READ_BOOL_OR_RETURN(&pps->deblocking_filter_control_present_flag);
753 READ_BOOL_OR_RETURN(&pps->constrained_intra_pred_flag);
754 READ_BOOL_OR_RETURN(&pps->redundant_pic_cnt_present_flag);
755
756 if (br->HasMoreRBSPData()) {
757 READ_BOOL_OR_RETURN(&pps->transform_8x8_mode_flag);
758 READ_BOOL_OR_RETURN(&pps->pic_scaling_matrix_present_flag);
759
760 if (pps->pic_scaling_matrix_present_flag) {
761 DVLOG(4) << "Picture scaling matrix present";
762 res = ParsePpsScalingLists(br, *sps, pps.get());
763 if (res != kOk)
764 return res;
765 }
766
767 READ_SE_OR_RETURN(&pps->second_chroma_qp_index_offset);
768 }
769
770 // If a PPS with the same id already exists, replace it.
771 *pps_id = pps->pic_parameter_set_id;
772 active_PPSes_[*pps_id] = std::move(pps);
773
774 return kOk;
775}
776
777H264Parser::Result H264Parser::ParseRefPicListModification(
778 H26xBitReader* br,
779 int num_ref_idx_active_minus1,
780 H264ModificationOfPicNum* ref_list_mods) {
781 H264ModificationOfPicNum* pic_num_mod;
782
783 if (num_ref_idx_active_minus1 >= 32)
784 return kInvalidStream;
785
786 for (int i = 0; i < 32; ++i) {
787 pic_num_mod = &ref_list_mods[i];
788 READ_UE_OR_RETURN(&pic_num_mod->modification_of_pic_nums_idc);
789 TRUE_OR_RETURN(pic_num_mod->modification_of_pic_nums_idc < 4);
790
791 switch (pic_num_mod->modification_of_pic_nums_idc) {
792 case 0:
793 case 1:
794 READ_UE_OR_RETURN(&pic_num_mod->abs_diff_pic_num_minus1);
795 break;
796
797 case 2:
798 READ_UE_OR_RETURN(&pic_num_mod->long_term_pic_num);
799 break;
800
801 case 3:
802 // Per spec, list cannot be empty.
803 if (i == 0)
804 return kInvalidStream;
805 return kOk;
806
807 default:
808 return kInvalidStream;
809 }
810 }
811
812 // If we got here, we didn't get loop end marker prematurely,
813 // so make sure it is there for our client.
814 int modification_of_pic_nums_idc;
815 READ_UE_OR_RETURN(&modification_of_pic_nums_idc);
816 TRUE_OR_RETURN(modification_of_pic_nums_idc == 3);
817
818 return kOk;
819}
820
821H264Parser::Result H264Parser::ParseRefPicListModifications(
822 H26xBitReader* br,
823 H264SliceHeader* shdr) {
824 Result res;
825
826 if (!shdr->IsISlice() && !shdr->IsSISlice()) {
827 READ_BOOL_OR_RETURN(&shdr->ref_pic_list_modification_flag_l0);
828 if (shdr->ref_pic_list_modification_flag_l0) {
829 res = ParseRefPicListModification(br, shdr->num_ref_idx_l0_active_minus1,
830 shdr->ref_list_l0_modifications);
831 if (res != kOk)
832 return res;
833 }
834 }
835
836 if (shdr->IsBSlice()) {
837 READ_BOOL_OR_RETURN(&shdr->ref_pic_list_modification_flag_l1);
838 if (shdr->ref_pic_list_modification_flag_l1) {
839 res = ParseRefPicListModification(br, shdr->num_ref_idx_l1_active_minus1,
840 shdr->ref_list_l1_modifications);
841 if (res != kOk)
842 return res;
843 }
844 }
845
846 return kOk;
847}
848
849H264Parser::Result H264Parser::ParseWeightingFactors(
850 H26xBitReader* br,
851 int num_ref_idx_active_minus1,
852 int chroma_array_type,
853 int luma_log2_weight_denom,
854 int chroma_log2_weight_denom,
855 H264WeightingFactors* w_facts) {
856 int def_luma_weight = 1 << luma_log2_weight_denom;
857 int def_chroma_weight = 1 << chroma_log2_weight_denom;
858
859 for (int i = 0; i < num_ref_idx_active_minus1 + 1; ++i) {
860 READ_BOOL_OR_RETURN(&w_facts->luma_weight_flag[i]);
861 if (w_facts->luma_weight_flag[i]) {
862 READ_SE_OR_RETURN(&w_facts->luma_weight[i]);
863 IN_RANGE_OR_RETURN(w_facts->luma_weight[i], -128, 127);
864
865 READ_SE_OR_RETURN(&w_facts->luma_offset[i]);
866 IN_RANGE_OR_RETURN(w_facts->luma_offset[i], -128, 127);
867 } else {
868 w_facts->luma_weight[i] = def_luma_weight;
869 w_facts->luma_offset[i] = 0;
870 }
871
872 if (chroma_array_type != 0) {
873 READ_BOOL_OR_RETURN(&w_facts->chroma_weight_flag[i]);
874 if (w_facts->chroma_weight_flag[i]) {
875 for (int j = 0; j < 2; ++j) {
876 READ_SE_OR_RETURN(&w_facts->chroma_weight[i][j]);
877 IN_RANGE_OR_RETURN(w_facts->chroma_weight[i][j], -128, 127);
878
879 READ_SE_OR_RETURN(&w_facts->chroma_offset[i][j]);
880 IN_RANGE_OR_RETURN(w_facts->chroma_offset[i][j], -128, 127);
881 }
882 } else {
883 for (int j = 0; j < 2; ++j) {
884 w_facts->chroma_weight[i][j] = def_chroma_weight;
885 w_facts->chroma_offset[i][j] = 0;
886 }
887 }
888 }
889 }
890
891 return kOk;
892}
893
894H264Parser::Result H264Parser::ParsePredWeightTable(H26xBitReader* br,
895 const H264Sps& sps,
896 H264SliceHeader* shdr) {
897 READ_UE_OR_RETURN(&shdr->luma_log2_weight_denom);
898 TRUE_OR_RETURN(shdr->luma_log2_weight_denom < 8);
899
900 if (sps.chroma_array_type != 0)
901 READ_UE_OR_RETURN(&shdr->chroma_log2_weight_denom);
902 TRUE_OR_RETURN(shdr->chroma_log2_weight_denom < 8);
903
904 Result res = ParseWeightingFactors(
905 br, shdr->num_ref_idx_l0_active_minus1, sps.chroma_array_type,
906 shdr->luma_log2_weight_denom, shdr->chroma_log2_weight_denom,
907 &shdr->pred_weight_table_l0);
908 if (res != kOk)
909 return res;
910
911 if (shdr->IsBSlice()) {
912 res = ParseWeightingFactors(
913 br, shdr->num_ref_idx_l1_active_minus1, sps.chroma_array_type,
914 shdr->luma_log2_weight_denom, shdr->chroma_log2_weight_denom,
915 &shdr->pred_weight_table_l1);
916 if (res != kOk)
917 return res;
918 }
919
920 return kOk;
921}
922
923H264Parser::Result H264Parser::ParseDecRefPicMarking(H26xBitReader* br,
924 H264SliceHeader* shdr) {
925 if (shdr->idr_pic_flag) {
926 READ_BOOL_OR_RETURN(&shdr->no_output_of_prior_pics_flag);
927 READ_BOOL_OR_RETURN(&shdr->long_term_reference_flag);
928 } else {
929 READ_BOOL_OR_RETURN(&shdr->adaptive_ref_pic_marking_mode_flag);
930
931 H264DecRefPicMarking* marking;
932 if (shdr->adaptive_ref_pic_marking_mode_flag) {
933 size_t i;
934 for (i = 0; i < std::size(shdr->ref_pic_marking); ++i) {
935 marking = &shdr->ref_pic_marking[i];
936
937 READ_UE_OR_RETURN(&marking->memory_mgmnt_control_operation);
938 if (marking->memory_mgmnt_control_operation == 0)
939 break;
940
941 if (marking->memory_mgmnt_control_operation == 1 ||
942 marking->memory_mgmnt_control_operation == 3)
943 READ_UE_OR_RETURN(&marking->difference_of_pic_nums_minus1);
944
945 if (marking->memory_mgmnt_control_operation == 2)
946 READ_UE_OR_RETURN(&marking->long_term_pic_num);
947
948 if (marking->memory_mgmnt_control_operation == 3 ||
949 marking->memory_mgmnt_control_operation == 6)
950 READ_UE_OR_RETURN(&marking->long_term_frame_idx);
951
952 if (marking->memory_mgmnt_control_operation == 4)
953 READ_UE_OR_RETURN(&marking->max_long_term_frame_idx_plus1);
954
955 if (marking->memory_mgmnt_control_operation > 6)
956 return kInvalidStream;
957 }
958
959 if (i == std::size(shdr->ref_pic_marking)) {
960 LOG_ERROR_ONCE("Ran out of dec ref pic marking fields");
961 return kUnsupportedStream;
962 }
963 }
964 }
965
966 return kOk;
967}
968
969H264Parser::Result H264Parser::ParseSliceHeader(const Nalu& nalu,
970 H264SliceHeader* shdr) {
971 // See 7.4.3.
972 const H264Sps* sps;
973 const H264Pps* pps;
974 Result res;
975 H26xBitReader reader;
976 reader.Initialize(nalu.data() + nalu.header_size(), nalu.payload_size());
977 H26xBitReader* br = &reader;
978
979 *shdr = {};
980
981 shdr->idr_pic_flag = (nalu.type() == 5);
982 shdr->nal_ref_idc = nalu.ref_idc();
983 shdr->nalu_data = nalu.data();
984 shdr->nalu_size = nalu.header_size() + nalu.payload_size();
985
986 READ_UE_OR_RETURN(&shdr->first_mb_in_slice);
987 READ_UE_OR_RETURN(&shdr->slice_type);
988 TRUE_OR_RETURN(shdr->slice_type < 10);
989
990 READ_UE_OR_RETURN(&shdr->pic_parameter_set_id);
991
992 pps = GetPps(shdr->pic_parameter_set_id);
993 TRUE_OR_RETURN(pps);
994
995 sps = GetSps(pps->seq_parameter_set_id);
996 TRUE_OR_RETURN(sps);
997
998 if (sps->separate_colour_plane_flag) {
999 LOG_ERROR_ONCE("Interlaced streams not supported");
1000 return kUnsupportedStream;
1001 }
1002
1003 READ_BITS_OR_RETURN(sps->log2_max_frame_num_minus4 + 4, &shdr->frame_num);
1004 if (!sps->frame_mbs_only_flag) {
1005 READ_BOOL_OR_RETURN(&shdr->field_pic_flag);
1006 if (shdr->field_pic_flag) {
1007 LOG_ERROR_ONCE("Interlaced streams not supported");
1008 return kUnsupportedStream;
1009 }
1010 }
1011
1012 if (shdr->idr_pic_flag)
1013 READ_UE_OR_RETURN(&shdr->idr_pic_id);
1014
1015 if (sps->pic_order_cnt_type == 0) {
1016 READ_BITS_OR_RETURN(sps->log2_max_pic_order_cnt_lsb_minus4 + 4,
1017 &shdr->pic_order_cnt_lsb);
1018 if (pps->bottom_field_pic_order_in_frame_present_flag &&
1019 !shdr->field_pic_flag)
1020 READ_SE_OR_RETURN(&shdr->delta_pic_order_cnt_bottom);
1021 }
1022
1023 if (sps->pic_order_cnt_type == 1 && !sps->delta_pic_order_always_zero_flag) {
1024 READ_SE_OR_RETURN(&shdr->delta_pic_order_cnt[0]);
1025 if (pps->bottom_field_pic_order_in_frame_present_flag &&
1026 !shdr->field_pic_flag)
1027 READ_SE_OR_RETURN(&shdr->delta_pic_order_cnt[1]);
1028 }
1029
1030 if (pps->redundant_pic_cnt_present_flag) {
1031 READ_UE_OR_RETURN(&shdr->redundant_pic_cnt);
1032 TRUE_OR_RETURN(shdr->redundant_pic_cnt < 128);
1033 }
1034
1035 if (shdr->IsBSlice())
1036 READ_BOOL_OR_RETURN(&shdr->direct_spatial_mv_pred_flag);
1037
1038 if (shdr->IsPSlice() || shdr->IsSPSlice() || shdr->IsBSlice()) {
1039 READ_BOOL_OR_RETURN(&shdr->num_ref_idx_active_override_flag);
1040 if (shdr->num_ref_idx_active_override_flag) {
1041 READ_UE_OR_RETURN(&shdr->num_ref_idx_l0_active_minus1);
1042 if (shdr->IsBSlice())
1043 READ_UE_OR_RETURN(&shdr->num_ref_idx_l1_active_minus1);
1044 } else {
1045 shdr->num_ref_idx_l0_active_minus1 =
1046 pps->num_ref_idx_l0_default_active_minus1;
1047 if (shdr->IsBSlice()) {
1048 shdr->num_ref_idx_l1_active_minus1 =
1049 pps->num_ref_idx_l1_default_active_minus1;
1050 }
1051 }
1052 }
1053 if (shdr->field_pic_flag) {
1054 TRUE_OR_RETURN(shdr->num_ref_idx_l0_active_minus1 < 32);
1055 TRUE_OR_RETURN(shdr->num_ref_idx_l1_active_minus1 < 32);
1056 } else {
1057 TRUE_OR_RETURN(shdr->num_ref_idx_l0_active_minus1 < 16);
1058 TRUE_OR_RETURN(shdr->num_ref_idx_l1_active_minus1 < 16);
1059 }
1060
1061 if (nalu.type() == Nalu::H264_CodedSliceExtension) {
1062 return kUnsupportedStream;
1063 } else {
1064 res = ParseRefPicListModifications(br, shdr);
1065 if (res != kOk)
1066 return res;
1067 }
1068
1069 if ((pps->weighted_pred_flag && (shdr->IsPSlice() || shdr->IsSPSlice())) ||
1070 (pps->weighted_bipred_idc == 1 && shdr->IsBSlice())) {
1071 res = ParsePredWeightTable(br, *sps, shdr);
1072 if (res != kOk)
1073 return res;
1074 }
1075
1076 if (nalu.ref_idc() != 0) {
1077 res = ParseDecRefPicMarking(br, shdr);
1078 if (res != kOk)
1079 return res;
1080 }
1081
1082 if (pps->entropy_coding_mode_flag && !shdr->IsISlice() &&
1083 !shdr->IsSISlice()) {
1084 READ_UE_OR_RETURN(&shdr->cabac_init_idc);
1085 TRUE_OR_RETURN(shdr->cabac_init_idc < 3);
1086 }
1087
1088 READ_SE_OR_RETURN(&shdr->slice_qp_delta);
1089
1090 if (shdr->IsSPSlice() || shdr->IsSISlice()) {
1091 if (shdr->IsSPSlice())
1092 READ_BOOL_OR_RETURN(&shdr->sp_for_switch_flag);
1093 READ_SE_OR_RETURN(&shdr->slice_qs_delta);
1094 }
1095
1096 if (pps->deblocking_filter_control_present_flag) {
1097 READ_UE_OR_RETURN(&shdr->disable_deblocking_filter_idc);
1098 TRUE_OR_RETURN(shdr->disable_deblocking_filter_idc < 3);
1099
1100 if (shdr->disable_deblocking_filter_idc != 1) {
1101 READ_SE_OR_RETURN(&shdr->slice_alpha_c0_offset_div2);
1102 IN_RANGE_OR_RETURN(shdr->slice_alpha_c0_offset_div2, -6, 6);
1103
1104 READ_SE_OR_RETURN(&shdr->slice_beta_offset_div2);
1105 IN_RANGE_OR_RETURN(shdr->slice_beta_offset_div2, -6, 6);
1106 }
1107 }
1108
1109 if (pps->num_slice_groups_minus1 > 0) {
1110 LOG_ERROR_ONCE("Slice groups not supported");
1111 return kUnsupportedStream;
1112 }
1113
1114 shdr->header_bit_size = nalu.payload_size() * 8 - br->NumBitsLeft();
1115 return kOk;
1116}
1117
1118H264Parser::Result H264Parser::ParseSEI(const Nalu& nalu,
1119 H264SEIMessage* sei_msg) {
1120 int byte;
1121 H26xBitReader reader;
1122 reader.Initialize(nalu.data() + nalu.header_size(), nalu.payload_size());
1123 H26xBitReader* br = &reader;
1124
1125 *sei_msg = {};
1126
1127 READ_BITS_OR_RETURN(8, &byte);
1128 while (byte == 0xff) {
1129 sei_msg->type += 255;
1130 READ_BITS_OR_RETURN(8, &byte);
1131 }
1132 sei_msg->type += byte;
1133
1134 READ_BITS_OR_RETURN(8, &byte);
1135 while (byte == 0xff) {
1136 sei_msg->payload_size += 255;
1137 READ_BITS_OR_RETURN(8, &byte);
1138 }
1139 sei_msg->payload_size += byte;
1140
1141 DVLOG(4) << "Found SEI message type: " << sei_msg->type
1142 << " payload size: " << sei_msg->payload_size;
1143
1144 switch (sei_msg->type) {
1145 case H264SEIMessage::kSEIRecoveryPoint:
1146 READ_UE_OR_RETURN(&sei_msg->recovery_point.recovery_frame_cnt);
1147 READ_BOOL_OR_RETURN(&sei_msg->recovery_point.exact_match_flag);
1148 READ_BOOL_OR_RETURN(&sei_msg->recovery_point.broken_link_flag);
1149 READ_BITS_OR_RETURN(2, &sei_msg->recovery_point.changing_slice_group_idc);
1150 break;
1151
1152 default:
1153 DVLOG(4) << "Unsupported SEI message";
1154 break;
1155 }
1156
1157 return kOk;
1158}
1159
1160} // namespace media
1161} // namespace shaka
All the methods that are virtual are virtual for mocking.