7 #include "packager/media/formats/mp4/mp4_muxer.h" 11 #include "packager/base/time/clock.h" 12 #include "packager/base/time/time.h" 13 #include "packager/file/file.h" 14 #include "packager/media/base/aes_encryptor.h" 15 #include "packager/media/base/audio_stream_info.h" 16 #include "packager/media/base/fourccs.h" 17 #include "packager/media/base/key_source.h" 18 #include "packager/media/base/media_sample.h" 19 #include "packager/media/base/text_stream_info.h" 20 #include "packager/media/base/video_stream_info.h" 21 #include "packager/media/codecs/es_descriptor.h" 22 #include "packager/media/event/muxer_listener.h" 23 #include "packager/media/formats/mp4/box_definitions.h" 24 #include "packager/media/formats/mp4/multi_segment_segmenter.h" 25 #include "packager/media/formats/mp4/single_segment_segmenter.h" 26 #include "packager/status_macros.h" 36 void SetStartAndEndFromOffsetAndSize(
size_t offset,
40 range->start =
static_cast<uint32_t
>(offset);
42 range->end = range->start +
static_cast<uint32_t
>(size) - 1;
45 FourCC CodecToFourCC(Codec codec, H26xStreamFormat h26x_stream_format) {
48 return h26x_stream_format ==
49 H26xStreamFormat::kNalUnitStreamWithParameterSetNalus
53 return h26x_stream_format ==
54 H26xStreamFormat::kNalUnitStreamWithParameterSetNalus
88 void GenerateSinf(FourCC old_type,
89 const EncryptionConfig& encryption_config,
90 ProtectionSchemeInfo* sinf) {
91 sinf->format.format = old_type;
93 DCHECK_NE(encryption_config.protection_scheme, FOURCC_NULL);
94 sinf->type.type = encryption_config.protection_scheme;
97 const int kCencSchemeVersion = 0x00010000;
98 sinf->type.version = kCencSchemeVersion;
100 auto& track_encryption = sinf->info.track_encryption;
101 track_encryption.default_is_protected = 1;
103 track_encryption.default_crypt_byte_block =
104 encryption_config.crypt_byte_block;
105 track_encryption.default_skip_byte_block = encryption_config.skip_byte_block;
106 switch (encryption_config.protection_scheme) {
109 DCHECK_EQ(track_encryption.default_crypt_byte_block, 0u);
110 DCHECK_EQ(track_encryption.default_skip_byte_block, 0u);
113 track_encryption.version = 0;
117 if (track_encryption.default_skip_byte_block == 0) {
120 track_encryption.default_crypt_byte_block = 0;
125 track_encryption.version = 1;
128 NOTREACHED() <<
"Unexpected protection scheme " 129 << encryption_config.protection_scheme;
132 track_encryption.default_per_sample_iv_size =
133 encryption_config.per_sample_iv_size;
134 track_encryption.default_constant_iv = encryption_config.constant_iv;
135 track_encryption.default_kid = encryption_config.key_id;
141 MP4Muxer::~MP4Muxer() {}
143 Status MP4Muxer::InitializeMuxer() {
145 to_be_initialized_ =
true;
149 Status MP4Muxer::Finalize() {
154 DCHECK(to_be_initialized_);
156 <<
"' which does not contain any sample.";
160 Status segmenter_finalized = segmenter_->Finalize();
162 if (!segmenter_finalized.ok())
163 return segmenter_finalized;
165 FireOnMediaEndEvent();
171 if (to_be_initialized_) {
172 RETURN_IF_ERROR(UpdateEditListOffsetFromSample(sample));
173 RETURN_IF_ERROR(DelayInitializeMuxer());
174 to_be_initialized_ =
false;
177 return segmenter_->AddSample(stream_id, sample);
180 Status MP4Muxer::FinalizeSegment(
size_t stream_id,
183 VLOG(3) <<
"Finalizing " << (segment_info.is_subsegment ?
"sub" :
"")
184 <<
"segment " << segment_info.start_timestamp <<
" duration " 185 << segment_info.duration;
186 return segmenter_->FinalizeSegment(stream_id, segment_info);
189 Status MP4Muxer::DelayInitializeMuxer() {
190 DCHECK(!streams().empty());
192 std::unique_ptr<FileType> ftyp(
new FileType);
193 std::unique_ptr<Movie> moov(
new Movie);
195 ftyp->major_brand = FOURCC_isom;
196 ftyp->compatible_brands.push_back(FOURCC_iso8);
197 ftyp->compatible_brands.push_back(FOURCC_mp41);
198 ftyp->compatible_brands.push_back(FOURCC_dash);
200 if (streams().size() == 1) {
201 FourCC codec_fourcc = FOURCC_NULL;
202 if (streams()[0]->stream_type() == kStreamVideo) {
204 CodecToFourCC(streams()[0]->codec(),
205 static_cast<const VideoStreamInfo*>(streams()[0].
get())
206 ->h26x_stream_format());
207 if (codec_fourcc != FOURCC_NULL)
208 ftyp->compatible_brands.push_back(codec_fourcc);
214 if (codec_fourcc != FOURCC_avc3 && codec_fourcc != FOURCC_hev1)
215 ftyp->compatible_brands.push_back(FOURCC_cmfc);
218 moov->header.creation_time = IsoTimeNow();
219 moov->header.modification_time = IsoTimeNow();
220 moov->header.next_track_id =
static_cast<uint32_t
>(streams().size()) + 1;
222 moov->tracks.resize(streams().size());
223 moov->extends.tracks.resize(streams().size());
226 for (uint32_t i = 0; i < streams().size(); ++i) {
227 const StreamInfo* stream = streams()[i].get();
228 Track& trak = moov->tracks[i];
229 trak.header.track_id = i + 1;
232 trex.track_id = trak.header.track_id;
233 trex.default_sample_description_index = 1;
235 bool generate_trak_result =
false;
236 switch (stream->stream_type()) {
238 generate_trak_result = GenerateVideoTrak(
239 static_cast<const VideoStreamInfo*>(stream), &trak, i + 1);
242 generate_trak_result = GenerateAudioTrak(
243 static_cast<const AudioStreamInfo*>(stream), &trak, i + 1);
246 generate_trak_result = GenerateTextTrak(
247 static_cast<const TextStreamInfo*>(stream), &trak, i + 1);
250 NOTIMPLEMENTED() <<
"Not implemented for stream type: " 251 << stream->stream_type();
253 if (!generate_trak_result)
254 return Status(error::MUXER_FAILURE,
"Failed to generate trak.");
258 if (edit_list_offset_.value() > 0) {
260 entry.media_time = edit_list_offset_.value();
261 entry.media_rate_integer = 1;
262 trak.edit.list.edits.push_back(entry);
266 const auto& key_system_info = stream->encryption_config().key_system_info;
267 moov->pssh.resize(key_system_info.size());
268 for (
size_t j = 0; j < key_system_info.size(); j++)
269 moov->pssh[j].raw_box = key_system_info[j].psshs;
281 const Status segmenter_initialized =
282 segmenter_->Initialize(streams(), muxer_listener(), progress_listener());
283 if (!segmenter_initialized.ok())
284 return segmenter_initialized;
286 FireOnMediaStartEvent();
291 if (edit_list_offset_)
294 const int64_t pts = sample.pts();
295 const int64_t dts = sample.dts();
327 const int64_t pts_dts_offset = pts - dts;
328 if (pts_dts_offset > 0) {
330 LOG(ERROR) <<
"Negative presentation timestamp (" << pts
331 <<
") is not supported when there is an offset between " 332 "presentation timestamp and decoding timestamp (" 334 return Status(error::MUXER_FAILURE,
335 "Unsupported negative pts when there is an offset between " 338 edit_list_offset_ = pts_dts_offset;
341 if (pts_dts_offset < 0) {
342 LOG(ERROR) <<
"presentation timestamp (" << pts
343 <<
") is not supposed to be greater than decoding timestamp (" 345 return Status(error::MUXER_FAILURE,
"Not expecting pts < dts.");
347 edit_list_offset_ = std::max(-sample.pts(),
static_cast<int64_t
>(0));
352 int64_t now = IsoTimeNow();
353 trak->header.creation_time = now;
354 trak->header.modification_time = now;
355 trak->header.duration = 0;
356 trak->media.header.creation_time = now;
357 trak->media.header.modification_time = now;
358 trak->media.header.timescale = info->time_scale();
359 trak->media.header.duration = 0;
360 if (!info->language().empty()) {
362 std::string main_language = info->language();
363 size_t dash = main_language.find(
'-');
364 if (dash != std::string::npos) {
365 main_language.erase(dash);
369 if (main_language.size() != 3) {
370 LOG(WARNING) <<
"'" << main_language <<
"' is not a valid ISO-639-2 " 371 <<
"language code, ignoring.";
373 trak->media.header.language.code = main_language;
381 InitializeTrak(video_info, trak);
387 if (pixel_width == 0 || pixel_height == 0) {
388 LOG(WARNING) <<
"pixel width/height are not set. Assuming 1:1.";
392 const double sample_aspect_ratio =
393 static_cast<double>(pixel_width) / pixel_height;
394 trak->header.width = video_info->width() * sample_aspect_ratio * 0x10000;
395 trak->header.height = video_info->height() * 0x10000;
399 CodecToFourCC(video_info->codec(), video_info->h26x_stream_format());
400 video.width = video_info->width();
401 video.height = video_info->height();
402 video.codec_configuration.data = video_info->codec_config();
403 if (pixel_width != 1 || pixel_height != 1) {
404 video.pixel_aspect.h_spacing = pixel_width;
405 video.pixel_aspect.v_spacing = pixel_height;
409 trak->media.information.sample_table.description;
410 sample_description.type = kVideo;
411 sample_description.video_entries.push_back(video);
413 if (video_info->is_encrypted()) {
414 if (video_info->has_clear_lead()) {
416 sample_description.video_entries.push_back(video);
420 GenerateSinf(entry.format, video_info->encryption_config(), &entry.sinf);
421 entry.format = FOURCC_encv;
429 InitializeTrak(audio_info, trak);
431 trak->header.volume = 0x100;
435 CodecToFourCC(audio_info->codec(), H26xStreamFormat::kUnSpecified);
436 switch(audio_info->codec()){
438 audio.esds.es_descriptor.set_object_type(
439 ObjectType::kISO_14496_3);
440 audio.esds.es_descriptor.set_esid(track_id);
441 audio.esds.es_descriptor.set_decoder_specific_info(
442 audio_info->codec_config());
443 audio.esds.es_descriptor.set_max_bitrate(audio_info->max_bitrate());
444 audio.esds.es_descriptor.set_avg_bitrate(audio_info->avg_bitrate());
451 audio.ddts.extra_data = audio_info->codec_config();
452 audio.ddts.max_bitrate = audio_info->max_bitrate();
453 audio.ddts.avg_bitrate = audio_info->avg_bitrate();
454 audio.ddts.sampling_frequency = audio_info->sampling_frequency();
455 audio.ddts.pcm_sample_depth = audio_info->sample_bits();
458 audio.dac3.data = audio_info->codec_config();
461 audio.dec3.data = audio_info->codec_config();
464 audio.dfla.data = audio_info->codec_config();
467 audio.dops.opus_identification_header = audio_info->codec_config();
470 NOTIMPLEMENTED() <<
" Unsupported audio codec " << audio_info->codec();
474 if (audio_info->codec() == kCodecAC3 || audio_info->codec() == kCodecEAC3) {
477 audio.channelcount = 2;
478 audio.samplesize = 16;
480 audio.channelcount = audio_info->num_channels();
481 audio.samplesize = audio_info->sample_bits();
483 audio.samplerate = audio_info->sampling_frequency();
484 SampleTable& sample_table = trak->media.information.sample_table;
486 sample_description.type = kAudio;
487 sample_description.audio_entries.push_back(audio);
489 if (audio_info->is_encrypted()) {
490 if (audio_info->has_clear_lead()) {
492 sample_description.audio_entries.push_back(audio);
496 GenerateSinf(entry.format, audio_info->encryption_config(), &entry.sinf);
497 entry.format = FOURCC_enca;
502 if (audio_info->codec() == kCodecOpus) {
503 sample_table.sample_group_descriptions.resize(1);
505 sample_table.sample_group_descriptions.back();
506 sample_group_description.grouping_type = FOURCC_roll;
507 sample_group_description.audio_roll_recovery_entries.resize(1);
510 const uint64_t kNanosecondsPerSecond = 1000000000ull;
511 sample_group_description.audio_roll_recovery_entries[0].roll_distance =
512 (0 - (audio_info->seek_preroll_ns() * audio.samplerate +
513 kNanosecondsPerSecond / 2)) /
514 kNanosecondsPerSecond;
516 sample_table.sample_to_groups.resize(1);
517 SampleToGroup& sample_to_group = sample_table.sample_to_groups.back();
518 sample_to_group.grouping_type = FOURCC_roll;
520 sample_to_group.entries.resize(1);
523 sample_to_group_entry.sample_count = 0;
524 sample_to_group_entry.group_description_index =
525 SampleToGroupEntry::kTrackGroupDescriptionIndexBase + 1;
526 }
else if (audio_info->seek_preroll_ns() != 0) {
527 LOG(WARNING) <<
"Unexpected seek preroll for codec " << audio_info->codec();
535 InitializeTrak(text_info, trak);
537 if (text_info->codec_string() ==
"wvtt") {
540 webvtt.format = FOURCC_wvtt;
541 webvtt.config.config.assign(text_info->codec_config().begin(),
542 text_info->codec_config().end());
546 webvtt.label.source_label =
"source_label";
548 trak->media.information.sample_table.description;
549 sample_description.type = kText;
550 sample_description.text_entries.push_back(webvtt);
553 NOTIMPLEMENTED() << text_info->codec_string()
554 <<
" handling not implemented yet.";
558 base::Optional<Range> MP4Muxer::GetInitRangeStartAndEnd() {
559 size_t range_offset = 0;
560 size_t range_size = 0;
561 const bool has_range = segmenter_->GetInitRange(&range_offset, &range_size);
564 return base::nullopt;
567 SetStartAndEndFromOffsetAndSize(range_offset, range_size, &range);
571 base::Optional<Range> MP4Muxer::GetIndexRangeStartAndEnd() {
572 size_t range_offset = 0;
573 size_t range_size = 0;
574 const bool has_range = segmenter_->GetIndexRange(&range_offset, &range_size);
577 return base::nullopt;
580 SetStartAndEndFromOffsetAndSize(range_offset, range_size, &range);
584 void MP4Muxer::FireOnMediaStartEvent() {
585 if (!muxer_listener())
588 if (streams().size() > 1) {
589 LOG(ERROR) <<
"MuxerListener cannot take more than 1 stream.";
592 DCHECK(!streams().empty()) <<
"Media started without a stream.";
594 const uint32_t timescale = segmenter_->GetReferenceTimeScale();
595 muxer_listener()->
OnMediaStart(options(), *streams().front(), timescale,
596 MuxerListener::kContainerMp4);
599 void MP4Muxer::FireOnMediaEndEvent() {
600 if (!muxer_listener())
604 media_range.
init_range = GetInitRangeStartAndEnd();
605 media_range.
index_range = GetIndexRangeStartAndEnd();
608 const float duration_seconds =
static_cast<float>(segmenter_->GetDuration());
609 muxer_listener()->
OnMediaEnd(media_range, duration_seconds);
612 uint64_t MP4Muxer::IsoTimeNow() {
614 const uint64_t kIsomTimeOffset = 2082844800l;
615 return kIsomTimeOffset +
616 (clock() ? clock()->Now() : base::Time::Now()).ToDoubleT();
All the methods that are virtual are virtual for mocking.
bool include_pssh_in_stream