Shaka Packager SDK
Loading...
Searching...
No Matches
mp4_media_parser.cc
1// Copyright (c) 2012 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/formats/mp4/mp4_media_parser.h>
6
7#include <algorithm>
8#include <cstddef>
9#include <cstdint>
10#include <functional>
11#include <limits>
12#include <memory>
13#include <string>
14#include <utility>
15#include <vector>
16
17#include <absl/flags/flag.h>
18#include <absl/log/check.h>
19#include <absl/log/log.h>
20
21#include <packager/file.h>
22#include <packager/file/file_closer.h>
23#include <packager/macros/compiler.h>
24#include <packager/macros/logging.h>
25#include <packager/media/base/audio_stream_info.h>
26#include <packager/media/base/decrypt_config.h>
27#include <packager/media/base/decryptor_source.h>
28#include <packager/media/base/fourccs.h>
29#include <packager/media/base/key_source.h>
30#include <packager/media/base/media_sample.h>
31#include <packager/media/base/rcheck.h>
32#include <packager/media/base/stream_info.h>
33#include <packager/media/base/video_stream_info.h>
34#include <packager/media/base/video_util.h>
35#include <packager/media/codecs/aac_audio_specific_config.h>
36#include <packager/media/codecs/ac3_audio_util.h>
37#include <packager/media/codecs/ac4_audio_util.h>
38#include <packager/media/codecs/av1_codec_configuration_record.h>
39#include <packager/media/codecs/avc_decoder_configuration_record.h>
40#include <packager/media/codecs/dovi_decoder_configuration_record.h>
41#include <packager/media/codecs/ec3_audio_util.h>
42#include <packager/media/codecs/es_descriptor.h>
43#include <packager/media/codecs/hevc_decoder_configuration_record.h>
44#include <packager/media/codecs/iamf_audio_util.h>
45#include <packager/media/codecs/vp_codec_configuration_record.h>
46#include <packager/media/formats/mp4/box_definitions.h>
47#include <packager/media/formats/mp4/box_reader.h>
48#include <packager/media/formats/mp4/track_run_iterator.h>
49#include <packager/status.h>
50
51ABSL_FLAG(bool,
52 use_dovi_supplemental_codecs,
53 false,
54 "Set to true to signal DolbyVision using the modern supplemental "
55 "codecs approach instead of the legacy "
56 "duplicate representations approach");
57
58namespace shaka {
59namespace media {
60namespace mp4 {
61namespace {
62
63int64_t Rescale(int64_t time_in_old_scale,
64 int32_t old_scale,
65 int32_t new_scale) {
66 return (static_cast<double>(time_in_old_scale) / old_scale) * new_scale;
67}
68
69H26xStreamFormat GetH26xStreamFormat(FourCC fourcc) {
70 switch (fourcc) {
71 case FOURCC_avc1:
72 case FOURCC_dvh1:
73 case FOURCC_hvc1:
74 return H26xStreamFormat::kNalUnitStreamWithoutParameterSetNalus;
75 case FOURCC_avc3:
76 case FOURCC_dvhe:
77 case FOURCC_hev1:
78 return H26xStreamFormat::kNalUnitStreamWithParameterSetNalus;
79 default:
80 return H26xStreamFormat::kUnSpecified;
81 }
82}
83
84Codec FourCCToCodec(FourCC fourcc) {
85 switch (fourcc) {
86 case FOURCC_av01:
87 return kCodecAV1;
88 case FOURCC_avc1:
89 case FOURCC_avc3:
90 return kCodecH264;
91 case FOURCC_dvh1:
92 case FOURCC_dvhe:
93 return kCodecH265DolbyVision;
94 case FOURCC_hev1:
95 case FOURCC_hvc1:
96 return kCodecH265;
97 case FOURCC_vp08:
98 return kCodecVP8;
99 case FOURCC_vp09:
100 return kCodecVP9;
101 case FOURCC_Opus:
102 return kCodecOpus;
103 case FOURCC_dtsc:
104 return kCodecDTSC;
105 case FOURCC_dtsh:
106 return kCodecDTSH;
107 case FOURCC_dtsl:
108 return kCodecDTSL;
109 case FOURCC_dtse:
110 return kCodecDTSE;
111 case FOURCC_dtsx:
112 return kCodecDTSX;
113 case FOURCC_dtsp:
114 return kCodecDTSP;
115 case FOURCC_dtsm:
116 return kCodecDTSM;
117 case FOURCC_ac_3:
118 return kCodecAC3;
119 case FOURCC_ec_3:
120 return kCodecEAC3;
121 case FOURCC_ac_4:
122 return kCodecAC4;
123 case FOURCC_alac:
124 return kCodecALAC;
125 case FOURCC_fLaC:
126 return kCodecFlac;
127 case FOURCC_iamf:
128 return kCodecIAMF;
129 case FOURCC_ipcm:
130 return kCodecPcm;
131 case FOURCC_mha1:
132 return kCodecMha1;
133 case FOURCC_mhm1:
134 return kCodecMhm1;
135 default:
136 return kUnknownCodec;
137 }
138}
139
140Codec ObjectTypeToCodec(ObjectType object_type) {
141 switch (object_type) {
142 case ObjectType::kISO_14496_3:
143 case ObjectType::kISO_13818_7_AAC_LC:
144 return kCodecAAC;
145 case ObjectType::kDTSC:
146 return kCodecDTSC;
147 case ObjectType::kDTSE:
148 return kCodecDTSE;
149 case ObjectType::kDTSH:
150 return kCodecDTSH;
151 case ObjectType::kDTSL:
152 return kCodecDTSL;
153 default:
154 return kUnknownCodec;
155 }
156}
157
158std::vector<uint8_t> GetDOVIDecoderConfig(
159 const std::vector<CodecConfiguration>& configs) {
160 for (const CodecConfiguration& config : configs) {
161 if (config.box_type == FOURCC_dvcC || config.box_type == FOURCC_dvvC) {
162 return config.data;
163 }
164 }
165 return std::vector<uint8_t>();
166}
167
168std::vector<uint8_t> GetLHEVCDecoderConfig(
169 const std::vector<CodecConfiguration>& configs) {
170 for (const CodecConfiguration& config : configs) {
171 if (config.box_type == FOURCC_lhvC) {
172 return config.data;
173 }
174 }
175 return std::vector<uint8_t>();
176}
177
178bool UpdateCodecStringForDolbyVision(
179 FourCC actual_format,
180 const std::vector<CodecConfiguration>& configs,
181 std::string* codec_string) {
182 DOVIDecoderConfigurationRecord dovi_config;
183 if (!dovi_config.Parse(GetDOVIDecoderConfig(configs))) {
184 LOG(ERROR) << "Failed to parse Dolby Vision decoder "
185 "configuration record.";
186 return false;
187 }
188 switch (actual_format) {
189 case FOURCC_dvh1:
190 case FOURCC_dvhe:
191 case FOURCC_dav1:
192 // Non-Backward compatibility mode. Replace the code string with
193 // Dolby Vision only.
194 *codec_string = dovi_config.GetCodecString(actual_format);
195 break;
196 case FOURCC_hev1:
197 // Backward compatibility mode. Two codecs are signalled: base codec
198 // without Dolby Vision and HDR with Dolby Vision.
199 *codec_string += ";" + dovi_config.GetCodecString(FOURCC_dvhe);
200 break;
201 case FOURCC_hvc1:
202 // See above.
203 *codec_string += ";" + dovi_config.GetCodecString(FOURCC_dvh1);
204 break;
205 case FOURCC_av01:
206 *codec_string += ";" + dovi_config.GetCodecString(FOURCC_dav1);
207 break;
208 default:
209 LOG(ERROR) << "Unsupported format with extra codec "
210 << FourCCToString(actual_format);
211 return false;
212 }
213 return true;
214}
215
216bool UpdateDolbyVisionInfo(FourCC actual_format,
217 const std::vector<CodecConfiguration>& configs,
218 uint8_t transfer_characteristics,
219 std::string* codec_string,
220 std::string* dovi_supplemental_codec_string,
221 FourCC* dovi_compatible_brand) {
222 DOVIDecoderConfigurationRecord dovi_config;
223 if (!dovi_config.Parse(GetDOVIDecoderConfig(configs))) {
224 LOG(ERROR) << "Failed to parse Dolby Vision decoder "
225 "configuration record.";
226 return false;
227 }
228 switch (actual_format) {
229 case FOURCC_dvh1:
230 case FOURCC_dvhe:
231 case FOURCC_dav1:
232 // Non-Backward compatibility mode. Replace the code string with
233 // Dolby Vision only.
234 *codec_string = dovi_config.GetCodecString(actual_format);
235 break;
236 case FOURCC_hev1:
237 // Backward compatibility mode. Use supplemental codec indicating Dolby
238 // Dolby Vision content.
239 *dovi_supplemental_codec_string = dovi_config.GetCodecString(FOURCC_dvhe);
240 break;
241 case FOURCC_hvc1:
242 // See above.
243 *dovi_supplemental_codec_string = dovi_config.GetCodecString(FOURCC_dvh1);
244 break;
245 case FOURCC_av01:
246 *dovi_supplemental_codec_string = dovi_config.GetCodecString(FOURCC_dav1);
247 break;
248 default:
249 LOG(ERROR) << "Unsupported format with extra codec "
250 << FourCCToString(actual_format);
251 return false;
252 }
253 *dovi_compatible_brand =
254 dovi_config.GetDoViCompatibleBrand(transfer_characteristics);
255 return true;
256}
257
258bool UpdateLHEVCInfo(FourCC actual_format,
259 HEVCDecoderConfigurationRecord& hevc_config,
260 const std::vector<CodecConfiguration>& configs,
261 std::string* codec_string) {
262 if (!hevc_config.ParseLHEVCConfig(GetLHEVCDecoderConfig(configs))) {
263 LOG(ERROR) << "Failed to parse L-HEVC decoder "
264 "configuration record.";
265 return false;
266 }
267 *codec_string = hevc_config.GetCodecString(actual_format);
268 return true;
269}
270
271const uint64_t kNanosecondsPerSecond = 1000000000ull;
272
273} // namespace
274
275MP4MediaParser::MP4MediaParser()
276 : state_(kWaitingForInit),
277 decryption_key_source_(NULL),
278 moof_head_(0),
279 mdat_tail_(0) {}
280
281MP4MediaParser::~MP4MediaParser() {}
282
283void MP4MediaParser::Init(const InitCB& init_cb,
284 const NewMediaSampleCB& new_media_sample_cb,
285 const NewTextSampleCB& new_text_sample_cb,
286 KeySource* decryption_key_source) {
287 DCHECK_EQ(state_, kWaitingForInit);
288 DCHECK(init_cb_ == nullptr);
289 DCHECK(init_cb != nullptr);
290 DCHECK(new_media_sample_cb != nullptr);
291
292 ChangeState(kParsingBoxes);
293 init_cb_ = init_cb;
294 new_sample_cb_ = new_media_sample_cb;
295 decryption_key_source_ = decryption_key_source;
296 if (decryption_key_source)
297 decryptor_source_.reset(new DecryptorSource(decryption_key_source));
298}
299
300void MP4MediaParser::Reset() {
301 queue_.Reset();
302 runs_.reset();
303 moof_head_ = 0;
304 mdat_tail_ = 0;
305}
306
307bool MP4MediaParser::Flush() {
308 DCHECK_NE(state_, kWaitingForInit);
309 Reset();
310 ChangeState(kParsingBoxes);
311 return true;
312}
313
314bool MP4MediaParser::Parse(const uint8_t* buf, int size) {
315 DCHECK_NE(state_, kWaitingForInit);
316
317 if (state_ == kError)
318 return false;
319
320 queue_.Push(buf, size);
321
322 bool result, err = false;
323
324 do {
325 if (state_ == kParsingBoxes) {
326 result = ParseBox(&err);
327 } else {
328 DCHECK_EQ(kEmittingSamples, state_);
329 result = EnqueueSample(&err);
330 if (result) {
331 int64_t max_clear = runs_->GetMaxClearOffset() + moof_head_;
332 err = !ReadAndDiscardMDATsUntil(max_clear);
333 }
334 }
335 } while (result && !err);
336
337 if (err) {
338 DLOG(ERROR) << "Error while parsing MP4";
339 moov_.reset();
340 Reset();
341 ChangeState(kError);
342 return false;
343 }
344
345 return true;
346}
347
348bool MP4MediaParser::LoadMoov(const std::string& file_path) {
349 std::unique_ptr<File, FileCloser> file(
350 File::OpenWithNoBuffering(file_path.c_str(), "r"));
351 if (!file) {
352 LOG(ERROR) << "Unable to open media file '" << file_path << "'";
353 return false;
354 }
355 if (!file->Seek(0)) {
356 LOG(WARNING) << "Filesystem does not support seeking on file '" << file_path
357 << "'";
358 return false;
359 }
360
361 uint64_t file_position(0);
362 bool mdat_seen(false);
363 while (true) {
364 const uint32_t kBoxHeaderReadSize(16);
365 std::vector<uint8_t> buffer(kBoxHeaderReadSize);
366 int64_t bytes_read = file->Read(&buffer[0], kBoxHeaderReadSize);
367 if (bytes_read == 0) {
368 LOG(ERROR) << "Could not find 'moov' box in file '" << file_path << "'";
369 return false;
370 }
371 if (bytes_read < kBoxHeaderReadSize) {
372 LOG(ERROR) << "Error reading media file '" << file_path << "'";
373 return false;
374 }
375 uint64_t box_size;
376 FourCC box_type;
377 bool err;
378 if (!BoxReader::StartBox(&buffer[0], kBoxHeaderReadSize, &box_type,
379 &box_size, &err)) {
380 LOG(ERROR) << "Could not start box from file '" << file_path << "'";
381 return false;
382 }
383 if (box_type == FOURCC_mdat) {
384 mdat_seen = true;
385 } else if (box_type == FOURCC_moov) {
386 if (!mdat_seen) {
387 // 'moov' is before 'mdat'. Nothing to do.
388 break;
389 }
390 // 'mdat' before 'moov'. Read and parse 'moov'.
391 if (!Parse(&buffer[0], bytes_read)) {
392 LOG(ERROR) << "Error parsing mp4 file '" << file_path << "'";
393 return false;
394 }
395 uint64_t bytes_to_read = box_size - bytes_read;
396 buffer.resize(bytes_to_read);
397 while (bytes_to_read > 0) {
398 bytes_read = file->Read(&buffer[0], bytes_to_read);
399 if (bytes_read <= 0) {
400 LOG(ERROR) << "Error reading 'moov' contents from file '" << file_path
401 << "'";
402 return false;
403 }
404 if (!Parse(&buffer[0], bytes_read)) {
405 LOG(ERROR) << "Error parsing mp4 file '" << file_path << "'";
406 return false;
407 }
408 bytes_to_read -= bytes_read;
409 }
410 queue_.Reset(); // So that we don't need to adjust data offsets.
411 mdat_tail_ = 0; // So it will skip boxes until mdat.
412 break; // Done.
413 }
414 file_position += box_size;
415 if (!file->Seek(file_position)) {
416 LOG(ERROR) << "Error skipping box in mp4 file '" << file_path << "'";
417 return false;
418 }
419 }
420 return true;
421}
422
423bool MP4MediaParser::ParseBox(bool* err) {
424 const uint8_t* buf;
425 int size;
426 queue_.Peek(&buf, &size);
427 if (!size)
428 return false;
429
430 std::unique_ptr<BoxReader> reader(BoxReader::ReadBox(buf, size, err));
431 if (reader.get() == NULL)
432 return false;
433
434 if (reader->type() == FOURCC_mdat) {
435 if (!moov_) {
436 // For seekable files, we seek to the 'moov' and load the 'moov' first
437 // then seek back (see LoadMoov function for details); we do not support
438 // having 'mdat' before 'moov' for non-seekable files. The code ends up
439 // here only if it is a non-seekable file.
440 NOTIMPLEMENTED() << " Non-seekable Files with 'mdat' box before 'moov' "
441 "box is not supported.";
442 *err = true;
443 return false;
444 } else {
445 // This can happen if there are unused 'mdat' boxes, which is unusual
446 // but allowed by the spec. Ignore the 'mdat' and proceed.
447 LOG(INFO)
448 << "Ignore unused 'mdat' box - this could be as a result of extra "
449 "not usable 'mdat' or 'mdat' associated with unrecognized track.";
450 }
451 }
452
453 // Set up mdat offset for ReadMDATsUntil().
454 mdat_tail_ = queue_.head() + reader->size();
455
456 if (reader->type() == FOURCC_moov) {
457 *err = !ParseMoov(reader.get());
458 } else if (reader->type() == FOURCC_moof) {
459 moof_head_ = queue_.head();
460 *err = !ParseMoof(reader.get());
461
462 // Return early to avoid evicting 'moof' data from queue. Auxiliary info may
463 // be located anywhere in the file, including inside the 'moof' itself.
464 // (Since 'default-base-is-moof' is mandated, no data references can come
465 // before the head of the 'moof', so keeping this box around is sufficient.)
466 return !(*err);
467 } else {
468 VLOG(2) << "Skipping top-level box: " << FourCCToString(reader->type());
469 }
470
471 queue_.Pop(static_cast<int>(reader->size()));
472 return !(*err);
473}
474
475bool MP4MediaParser::ParseMoov(BoxReader* reader) {
476 if (moov_)
477 return true; // Already parsed the 'moov' box.
478
479 moov_.reset(new Movie);
480 RCHECK(moov_->Parse(reader));
481 runs_.reset();
482
483 std::vector<std::shared_ptr<StreamInfo>> streams;
484
485 bool use_dovi_supplemental =
486 absl::GetFlag(FLAGS_use_dovi_supplemental_codecs);
487
488 for (std::vector<Track>::const_iterator track = moov_->tracks.begin();
489 track != moov_->tracks.end(); ++track) {
490 const int32_t timescale = track->media.header.timescale;
491
492 // Calculate duration (based on timescale).
493 int64_t duration = 0;
494 if (track->media.header.duration > 0) {
495 duration = track->media.header.duration;
496 } else if (moov_->extends.header.fragment_duration > 0) {
497 DCHECK(moov_->header.timescale != 0);
498 duration = Rescale(moov_->extends.header.fragment_duration,
499 moov_->header.timescale, timescale);
500 } else if (moov_->header.duration > 0 &&
501 moov_->header.duration != std::numeric_limits<uint64_t>::max()) {
502 DCHECK(moov_->header.timescale != 0);
503 duration =
504 Rescale(moov_->header.duration, moov_->header.timescale, timescale);
505 }
506
507 const SampleDescription& samp_descr =
508 track->media.information.sample_table.description;
509
510 size_t desc_idx = 0;
511
512 // Read sample description index from mvex if it exists otherwise read
513 // from the first entry in Sample To Chunk box.
514 if (moov_->extends.tracks.size() > 0) {
515 for (size_t t = 0; t < moov_->extends.tracks.size(); t++) {
516 const TrackExtends& trex = moov_->extends.tracks[t];
517 if (trex.track_id == track->header.track_id) {
518 desc_idx = trex.default_sample_description_index;
519 break;
520 }
521 }
522 } else {
523 const std::vector<ChunkInfo>& chunk_info =
524 track->media.information.sample_table.sample_to_chunk.chunk_info;
525 RCHECK(chunk_info.size() > 0);
526 desc_idx = chunk_info[0].sample_description_index;
527 }
528 RCHECK(desc_idx > 0);
529 desc_idx -= 1; // BMFF descriptor index is one-based
530
531 if (samp_descr.type == kAudio) {
532 RCHECK(!samp_descr.audio_entries.empty());
533
534 // It is not uncommon to find otherwise-valid files with incorrect sample
535 // description indices, so we fail gracefully in that case.
536 if (desc_idx >= samp_descr.audio_entries.size())
537 desc_idx = 0;
538
539 const AudioSampleEntry& entry = samp_descr.audio_entries[desc_idx];
540 const FourCC actual_format = entry.GetActualFormat();
541 Codec codec = FourCCToCodec(actual_format);
542 uint8_t num_channels = entry.channelcount;
543 uint32_t sampling_frequency = entry.samplerate;
544 uint64_t codec_delay_ns = 0;
545 uint8_t audio_object_type = 0;
546 uint32_t max_bitrate = 0;
547 uint32_t avg_bitrate = 0;
548 std::vector<uint8_t> codec_config;
549
550 switch (actual_format) {
551 case FOURCC_mp4a: {
552 const DecoderConfigDescriptor& decoder_config =
553 entry.esds.es_descriptor.decoder_config_descriptor();
554 max_bitrate = decoder_config.max_bitrate();
555 avg_bitrate = decoder_config.avg_bitrate();
556
557 codec = ObjectTypeToCodec(decoder_config.object_type());
558 if (codec == kCodecAAC) {
559 const AACAudioSpecificConfig& aac_audio_specific_config =
560 entry.esds.aac_audio_specific_config;
561 num_channels = aac_audio_specific_config.GetNumChannels();
562 sampling_frequency =
563 aac_audio_specific_config.GetSamplesPerSecond();
564 audio_object_type = aac_audio_specific_config.GetAudioObjectType();
565 codec_config =
566 decoder_config.decoder_specific_info_descriptor().data();
567 } else if (codec == kUnknownCodec) {
568 // Intentionally not to fail in the parser as there may be multiple
569 // streams in the source content, which allows the supported stream
570 // to be packaged. An error will be returned if the unsupported
571 // stream is passed to the muxer.
572 LOG(WARNING) << "Unsupported audio object type "
573 << static_cast<int>(decoder_config.object_type())
574 << " in stsd.es_desriptor.";
575 }
576 break;
577 }
578 case FOURCC_dtsc:
579 FALLTHROUGH_INTENDED;
580 case FOURCC_dtse:
581 FALLTHROUGH_INTENDED;
582 case FOURCC_dtsh:
583 FALLTHROUGH_INTENDED;
584 case FOURCC_dtsl:
585 FALLTHROUGH_INTENDED;
586 case FOURCC_dtsm:
587 codec_config = entry.ddts.extra_data;
588 max_bitrate = entry.ddts.max_bitrate;
589 avg_bitrate = entry.ddts.avg_bitrate;
590 break;
591 case FOURCC_dtsx:
592 codec_config = entry.udts.data;
593 break;
594 case FOURCC_ac_3:
595 codec_config = entry.dac3.data;
596 num_channels = static_cast<uint8_t>(GetAc3NumChannels(codec_config));
597 break;
598 case FOURCC_ec_3:
599 codec_config = entry.dec3.data;
600 num_channels = static_cast<uint8_t>(GetEc3NumChannels(codec_config));
601 break;
602 case FOURCC_ac_4:
603 codec_config = entry.dac4.data;
604 // Stop the process if have errors when parsing AC-4 dac4 box,
605 // bitstream version 0 (has beed deprecated) and contains multiple
606 // presentations in single AC-4 stream (only used for broadcast).
607 if (!GetAc4CodecInfo(codec_config, &audio_object_type)) {
608 LOG(ERROR) << "Failed to parse dac4.";
609 return false;
610 }
611 break;
612 case FOURCC_alac:
613 codec_config = entry.alac.data;
614 break;
615 case FOURCC_fLaC:
616 codec_config = entry.dfla.data;
617 break;
618 case FOURCC_Opus:
619 codec_config = entry.dops.opus_identification_header;
620 codec_delay_ns =
621 entry.dops.preskip * kNanosecondsPerSecond / sampling_frequency;
622 break;
623 case FOURCC_iamf:
624 codec_config = entry.iacb.data;
625 if (!GetIamfCodecStringInfo(codec_config, audio_object_type)) {
626 LOG(ERROR) << "Failed to parse iamf.";
627 return false;
628 }
629 break;
630 case FOURCC_mha1:
631 case FOURCC_mhm1:
632 codec_config = entry.mhac.data;
633 audio_object_type = entry.mhac.mpeg_h_3da_profile_level_indication;
634 break;
635 default:
636 // Intentionally not to fail in the parser as there may be multiple
637 // streams in the source content, which allows the supported stream to
638 // be packaged.
639 // An error will be returned if the unsupported stream is passed to
640 // the muxer.
641 LOG(WARNING) << "Unsupported audio format '"
642 << FourCCToString(actual_format) << "' in stsd box.";
643 break;
644 }
645
646 // Extract possible seek preroll.
647 uint64_t seek_preroll_ns = 0;
648 for (const auto& sample_group_description :
649 track->media.information.sample_table.sample_group_descriptions) {
650 if (sample_group_description.grouping_type != FOURCC_roll)
651 continue;
652 const auto& audio_roll_recovery_entries =
653 sample_group_description.audio_roll_recovery_entries;
654 if (audio_roll_recovery_entries.size() != 1) {
655 LOG(WARNING) << "Unexpected number of entries in "
656 "SampleGroupDescription table with grouping type "
657 "'roll'.";
658 break;
659 }
660 const int16_t roll_distance_in_samples =
661 audio_roll_recovery_entries[0].roll_distance;
662 if (roll_distance_in_samples < 0) {
663 // IAMF requires the `samplerate` field to be set to 0.
664 // (https://aomediacodec.github.io/iamf/#iasampleentry-section)
665 if (actual_format == FOURCC_iamf)
666 continue;
667
668 RCHECK((sampling_frequency != 0));
669 seek_preroll_ns = kNanosecondsPerSecond *
670 (-roll_distance_in_samples) / sampling_frequency;
671 } else {
672 LOG(WARNING)
673 << "Roll distance is supposed to be negative, but seeing "
674 << roll_distance_in_samples;
675 }
676 break;
677 }
678
679 // The stream will be decrypted if a |decryptor_source_| is available.
680 const bool is_encrypted =
681 decryptor_source_
682 ? false
683 : entry.sinf.info.track_encryption.default_is_protected == 1;
684 DVLOG(1) << "is_audio_track_encrypted_: " << is_encrypted;
685 streams.emplace_back(new AudioStreamInfo(
686 track->header.track_id, timescale, duration, codec,
687 AudioStreamInfo::GetCodecString(codec, audio_object_type),
688 codec_config.data(), codec_config.size(), entry.samplesize,
689 num_channels, sampling_frequency, seek_preroll_ns, codec_delay_ns,
690 max_bitrate, avg_bitrate, track->media.header.language.code,
691 is_encrypted));
692 }
693
694 if (samp_descr.type == kVideo) {
695 RCHECK(!samp_descr.video_entries.empty());
696 if (desc_idx >= samp_descr.video_entries.size())
697 desc_idx = 0;
698 const VideoSampleEntry& entry = samp_descr.video_entries[desc_idx];
699 std::vector<uint8_t> codec_configuration_data =
700 entry.codec_configuration.data;
701
702 uint32_t coded_width = entry.width;
703 uint32_t coded_height = entry.height;
704 uint32_t pixel_width = entry.pixel_aspect.h_spacing;
705 uint32_t pixel_height = entry.pixel_aspect.v_spacing;
706 if (pixel_width == 0 && pixel_height == 0) {
707 DerivePixelWidthHeight(coded_width, coded_height, track->header.width,
708 track->header.height, &pixel_width,
709 &pixel_height);
710 }
711 std::string codec_string;
712 std::string dovi_supplemental_codec_string("");
713 FourCC dovi_compatible_brand = FOURCC_NULL;
714 uint8_t nalu_length_size = 0;
715 uint8_t transfer_characteristics = 0;
716 uint8_t color_primaries = 0;
717 uint8_t matrix_coefficients = 0;
718
719 const FourCC actual_format = entry.GetActualFormat();
720 const Codec video_codec = FourCCToCodec(actual_format);
721 switch (actual_format) {
722 case FOURCC_av01: {
723 AV1CodecConfigurationRecord av1_config;
724 if (!av1_config.Parse(codec_configuration_data)) {
725 LOG(ERROR) << "Failed to parse av1c.";
726 return false;
727 }
728 // Generate the full codec string if the colr atom is present.
729 if (entry.colr.color_parameter_type != FOURCC_NULL) {
730 transfer_characteristics = entry.colr.transfer_characteristics;
731 color_primaries = entry.colr.color_primaries;
732 matrix_coefficients = entry.colr.matrix_coefficients;
733 codec_string = av1_config.GetCodecString(
734 color_primaries, transfer_characteristics, matrix_coefficients,
735 entry.colr.video_full_range_flag);
736 } else {
737 codec_string = av1_config.GetCodecString();
738 }
739
740 if (!entry.extra_codec_configs.empty()) {
741 // |extra_codec_configs| is present only for Dolby Vision.
742 if (use_dovi_supplemental) {
743 if (!UpdateDolbyVisionInfo(
744 actual_format, entry.extra_codec_configs,
745 transfer_characteristics, &codec_string,
746 &dovi_supplemental_codec_string,
747 &dovi_compatible_brand)) {
748 return false;
749 }
750 } else {
751 if (!UpdateCodecStringForDolbyVision(actual_format,
752 entry.extra_codec_configs,
753 &codec_string)) {
754 return false;
755 }
756 }
757 }
758 break;
759 }
760 case FOURCC_avc1:
761 case FOURCC_avc3: {
762 AVCDecoderConfigurationRecord avc_config;
763 if (!avc_config.Parse(codec_configuration_data)) {
764 LOG(ERROR) << "Failed to parse avcc.";
765 return false;
766 }
767 codec_string = avc_config.GetCodecString(actual_format);
768 nalu_length_size = avc_config.nalu_length_size();
769 transfer_characteristics = avc_config.transfer_characteristics();
770 color_primaries = avc_config.color_primaries();
771 matrix_coefficients = avc_config.matrix_coefficients();
772
773 // Use configurations from |avc_config| if it is valid.
774 if (avc_config.coded_width() != 0) {
775 DCHECK_NE(avc_config.coded_height(), 0u);
776 if (coded_width != avc_config.coded_width() ||
777 coded_height != avc_config.coded_height()) {
778 LOG(WARNING) << "Resolution in VisualSampleEntry (" << coded_width
779 << "," << coded_height
780 << ") does not match with resolution in "
781 "AVCDecoderConfigurationRecord ("
782 << avc_config.coded_width() << ","
783 << avc_config.coded_height()
784 << "). Use AVCDecoderConfigurationRecord.";
785 coded_width = avc_config.coded_width();
786 coded_height = avc_config.coded_height();
787 }
788
789 DCHECK_NE(avc_config.pixel_width(), 0u);
790 DCHECK_NE(avc_config.pixel_height(), 0u);
791 if (pixel_width != avc_config.pixel_width() ||
792 pixel_height != avc_config.pixel_height()) {
793 LOG_IF(WARNING, pixel_width != 1 || pixel_height != 1)
794 << "Pixel aspect ratio in PASP box (" << pixel_width << ","
795 << pixel_height
796 << ") does not match with SAR in "
797 "AVCDecoderConfigurationRecord "
798 "("
799 << avc_config.pixel_width() << ","
800 << avc_config.pixel_height()
801 << "). Use AVCDecoderConfigurationRecord.";
802 pixel_width = avc_config.pixel_width();
803 pixel_height = avc_config.pixel_height();
804 }
805 }
806 break;
807 }
808 case FOURCC_dvh1:
809 case FOURCC_dvhe:
810 case FOURCC_hev1:
811 case FOURCC_hvc1: {
812 HEVCDecoderConfigurationRecord hevc_config;
813 if (!hevc_config.Parse(codec_configuration_data)) {
814 LOG(ERROR) << "Failed to parse hevc.";
815 return false;
816 }
817 codec_string = hevc_config.GetCodecString(actual_format);
818 nalu_length_size = hevc_config.nalu_length_size();
819 transfer_characteristics = hevc_config.transfer_characteristics();
820 color_primaries = hevc_config.color_primaries();
821 matrix_coefficients = hevc_config.matrix_coefficients();
822
823 if (!entry.extra_codec_configs.empty()) {
824 // |extra_codec_configs| is present for Dolby Vision and/or
825 // stereo MV-HEVC.
826 if (entry.HaveDolbyVisionConfig()) {
827 if (use_dovi_supplemental) {
828 if (!UpdateDolbyVisionInfo(
829 actual_format, entry.extra_codec_configs,
830 transfer_characteristics, &codec_string,
831 &dovi_supplemental_codec_string,
832 &dovi_compatible_brand)) {
833 return false;
834 }
835 } else {
836 if (!UpdateCodecStringForDolbyVision(actual_format,
837 entry.extra_codec_configs,
838 &codec_string)) {
839 return false;
840 }
841 }
842 }
843 if (entry.HaveLHEVCConfig()) {
844 if (!UpdateLHEVCInfo(actual_format, hevc_config,
845 entry.extra_codec_configs, &codec_string)) {
846 return false;
847 }
848 }
849 }
850 break;
851 }
852 case FOURCC_vp08:
853 case FOURCC_vp09: {
854 VPCodecConfigurationRecord vp_config;
855 if (!vp_config.ParseMP4(codec_configuration_data)) {
856 LOG(ERROR) << "Failed to parse vpcc.";
857 return false;
858 }
859 if (actual_format == FOURCC_vp09 &&
860 (!vp_config.is_level_set() || vp_config.level() == 0)) {
861 const double kUnknownSampleDuration = 0.0;
862 vp_config.SetVP9Level(coded_width, coded_height,
863 kUnknownSampleDuration);
864 vp_config.WriteMP4(&codec_configuration_data);
865 }
866 codec_string = vp_config.GetCodecString(video_codec);
867 break;
868 }
869 default:
870 // Intentionally not to fail in the parser as there may be multiple
871 // streams in the source content, which allows the supported stream to
872 // be packaged.
873 // An error will be returned if the unsupported stream is passed to
874 // the muxer.
875 LOG(WARNING) << "Unsupported video format '"
876 << FourCCToString(actual_format) << "' in stsd box.";
877 break;
878 }
879
880 // The stream will be decrypted if a |decryptor_source_| is available.
881 const bool is_encrypted =
882 decryptor_source_
883 ? false
884 : entry.sinf.info.track_encryption.default_is_protected == 1;
885 DVLOG(1) << "is_video_track_encrypted_: " << is_encrypted;
886 std::shared_ptr<VideoStreamInfo> video_stream_info(new VideoStreamInfo(
887 track->header.track_id, timescale, duration, video_codec,
888 GetH26xStreamFormat(actual_format), codec_string,
889 codec_configuration_data.data(), codec_configuration_data.size(),
890 coded_width, coded_height, pixel_width, pixel_height, color_primaries,
891 matrix_coefficients, transfer_characteristics,
892 0, // trick_play_factor
893 nalu_length_size, track->media.header.language.code, is_encrypted));
894
895 if (use_dovi_supplemental) {
896 video_stream_info->set_supplemental_codec(
897 dovi_supplemental_codec_string);
898 video_stream_info->set_compatible_brand(dovi_compatible_brand);
899 }
900 video_stream_info->set_layered_codec_config(
901 GetLHEVCDecoderConfig(entry.extra_codec_configs));
902 video_stream_info->set_extra_config(entry.ExtraCodecConfigsAsVector());
903 video_stream_info->set_colr_data((entry.colr.raw_box).data(),
904 (entry.colr.raw_box).size());
905
906 // Set pssh raw data if it has.
907 if (moov_->pssh.size() > 0) {
908 std::vector<uint8_t> pssh_raw_data;
909 for (const auto& pssh : moov_->pssh) {
910 pssh_raw_data.insert(pssh_raw_data.end(), pssh.raw_box.begin(),
911 pssh.raw_box.end());
912 }
913 video_stream_info->set_eme_init_data(pssh_raw_data.data(),
914 pssh_raw_data.size());
915 }
916
917 streams.push_back(video_stream_info);
918 }
919 }
920
921 init_cb_(streams);
922 if (!FetchKeysIfNecessary(moov_->pssh))
923 return false;
924 runs_.reset(new TrackRunIterator(moov_.get()));
925 RCHECK(runs_->Init());
926 ChangeState(kEmittingSamples);
927 return true;
928}
929
930bool MP4MediaParser::ParseMoof(BoxReader* reader) {
931 // Must already have initialization segment.
932 RCHECK(moov_.get());
933 MovieFragment moof;
934 RCHECK(moof.Parse(reader));
935 if (!runs_)
936 runs_.reset(new TrackRunIterator(moov_.get()));
937 RCHECK(runs_->Init(moof));
938 if (!FetchKeysIfNecessary(moof.pssh))
939 return false;
940 ChangeState(kEmittingSamples);
941 return true;
942}
943
944bool MP4MediaParser::FetchKeysIfNecessary(
945 const std::vector<ProtectionSystemSpecificHeader>& headers) {
946 if (headers.empty())
947 return true;
948
949 // An error will be returned later if the samples need to be decrypted.
950 if (!decryption_key_source_)
951 return true;
952
953 std::vector<uint8_t> pssh_raw_data;
954 for (const auto& header : headers) {
955 pssh_raw_data.insert(pssh_raw_data.end(), header.raw_box.begin(),
956 header.raw_box.end());
957 }
958 Status status =
959 decryption_key_source_->FetchKeys(EmeInitDataType::CENC, pssh_raw_data);
960 if (!status.ok()) {
961 LOG(ERROR) << "Error fetching decryption keys: " << status;
962 return false;
963 }
964 return true;
965}
966
967bool MP4MediaParser::EnqueueSample(bool* err) {
968 if (!runs_->IsRunValid()) {
969 // Remain in kEnqueueingSamples state, discarding data, until the end of
970 // the current 'mdat' box has been appended to the queue.
971 if (!queue_.Trim(mdat_tail_))
972 return false;
973
974 ChangeState(kParsingBoxes);
975 return true;
976 }
977
978 if (!runs_->IsSampleValid()) {
979 runs_->AdvanceRun();
980 return true;
981 }
982
983 DCHECK(!(*err));
984
985 const uint8_t* buf;
986 int buf_size;
987 queue_.Peek(&buf, &buf_size);
988 if (!buf_size)
989 return false;
990
991 // Skip this entire track if it is not audio nor video.
992 if (!runs_->is_audio() && !runs_->is_video())
993 runs_->AdvanceRun();
994
995 // Attempt to cache the auxiliary information first. Aux info is usually
996 // placed in a contiguous block before the sample data, rather than being
997 // interleaved. If we didn't cache it, this would require that we retain the
998 // start of the segment buffer while reading samples. Aux info is typically
999 // quite small compared to sample data, so this pattern is useful on
1000 // memory-constrained devices where the source buffer consumes a substantial
1001 // portion of the total system memory.
1002 if (runs_->AuxInfoNeedsToBeCached()) {
1003 queue_.PeekAt(runs_->aux_info_offset() + moof_head_, &buf, &buf_size);
1004 if (buf_size < runs_->aux_info_size())
1005 return false;
1006 *err = !runs_->CacheAuxInfo(buf, buf_size);
1007 return !*err;
1008 }
1009
1010 int64_t sample_offset = runs_->sample_offset() + moof_head_;
1011 queue_.PeekAt(sample_offset, &buf, &buf_size);
1012 if (buf_size < runs_->sample_size()) {
1013 if (sample_offset < queue_.head()) {
1014 LOG(ERROR) << "Incorrect sample offset " << sample_offset << " < "
1015 << queue_.head();
1016 *err = true;
1017 }
1018 return false;
1019 }
1020
1021 const uint8_t* media_data = buf;
1022 const size_t media_data_size = runs_->sample_size();
1023 // Use a dummy data size of 0 to avoid copying overhead.
1024 // Actual media data is set later.
1025 const size_t kDummyDataSize = 0;
1026 std::shared_ptr<MediaSample> stream_sample(
1027 MediaSample::CopyFrom(media_data, kDummyDataSize, runs_->is_keyframe()));
1028
1029 if (runs_->is_encrypted()) {
1030 std::shared_ptr<uint8_t> decrypted_media_data(
1031 new uint8_t[media_data_size], std::default_delete<uint8_t[]>());
1032 std::unique_ptr<DecryptConfig> decrypt_config = runs_->GetDecryptConfig();
1033 if (!decrypt_config) {
1034 *err = true;
1035 LOG(ERROR) << "Missing decrypt config.";
1036 return false;
1037 }
1038
1039 if (!decryptor_source_) {
1040 stream_sample->SetData(media_data, media_data_size);
1041 // If the demuxer does not have the decryptor_source_, store
1042 // decrypt_config so that the demuxed sample can be decrypted later.
1043 stream_sample->set_decrypt_config(std::move(decrypt_config));
1044 stream_sample->set_is_encrypted(true);
1045 } else {
1046 if (!decryptor_source_->DecryptSampleBuffer(decrypt_config.get(),
1047 media_data, media_data_size,
1048 decrypted_media_data.get())) {
1049 *err = true;
1050 LOG(ERROR) << "Cannot decrypt samples.";
1051 return false;
1052 }
1053 stream_sample->TransferData(std::move(decrypted_media_data),
1054 media_data_size);
1055 }
1056 } else {
1057 stream_sample->SetData(media_data, media_data_size);
1058 }
1059
1060 stream_sample->set_dts(runs_->dts());
1061 stream_sample->set_pts(runs_->cts());
1062 stream_sample->set_duration(runs_->duration());
1063
1064 DVLOG(3) << "Pushing frame: "
1065 << ", key=" << runs_->is_keyframe() << ", dur=" << runs_->duration()
1066 << ", dts=" << runs_->dts() << ", cts=" << runs_->cts()
1067 << ", size=" << runs_->sample_size();
1068
1069 if (!new_sample_cb_(runs_->track_id(), stream_sample)) {
1070 *err = true;
1071 LOG(ERROR) << "Failed to process the sample.";
1072 return false;
1073 }
1074
1075 runs_->AdvanceSample();
1076 return true;
1077}
1078
1079bool MP4MediaParser::ReadAndDiscardMDATsUntil(const int64_t offset) {
1080 bool err = false;
1081 while (mdat_tail_ < offset) {
1082 const uint8_t* buf;
1083 int size;
1084 queue_.PeekAt(mdat_tail_, &buf, &size);
1085
1086 FourCC type;
1087 uint64_t box_sz;
1088 if (!BoxReader::StartBox(buf, size, &type, &box_sz, &err))
1089 break;
1090
1091 mdat_tail_ += box_sz;
1092 }
1093 queue_.Trim(std::min(mdat_tail_, offset));
1094 return !err;
1095}
1096
1097void MP4MediaParser::ChangeState(State new_state) {
1098 DVLOG(2) << "Changing state: " << new_state;
1099 state_ = new_state;
1100}
1101
1102} // namespace mp4
1103} // namespace media
1104} // namespace shaka
DecryptorSource wraps KeySource and is responsible for decryptor management.
KeySource is responsible for encryption key acquisition.
Definition key_source.h:56
std::function< bool(uint32_t track_id, std::shared_ptr< MediaSample > media_sample)> NewMediaSampleCB
std::function< bool(uint32_t track_id, std::shared_ptr< TextSample > text_sample)> NewTextSampleCB
std::function< void(const std::vector< std::shared_ptr< StreamInfo > > &stream_info)> InitCB
All the methods that are virtual are virtual for mocking.