7 #include "packager/packager.h" 11 #include "packager/app/job_manager.h" 12 #include "packager/app/libcrypto_threading.h" 13 #include "packager/app/muxer_factory.h" 14 #include "packager/app/packager_util.h" 15 #include "packager/app/stream_descriptor.h" 16 #include "packager/base/at_exit.h" 17 #include "packager/base/files/file_path.h" 18 #include "packager/base/logging.h" 19 #include "packager/base/optional.h" 20 #include "packager/base/path_service.h" 21 #include "packager/base/strings/string_util.h" 22 #include "packager/base/strings/stringprintf.h" 23 #include "packager/base/threading/simple_thread.h" 24 #include "packager/base/time/clock.h" 25 #include "packager/file/file.h" 26 #include "packager/hls/base/hls_notifier.h" 27 #include "packager/hls/base/simple_hls_notifier.h" 28 #include "packager/media/base/container_names.h" 29 #include "packager/media/base/fourccs.h" 30 #include "packager/media/base/key_source.h" 31 #include "packager/media/base/language_utils.h" 32 #include "packager/media/base/muxer.h" 33 #include "packager/media/base/muxer_options.h" 34 #include "packager/media/base/muxer_util.h" 35 #include "packager/media/chunking/chunking_handler.h" 36 #include "packager/media/chunking/cue_alignment_handler.h" 37 #include "packager/media/chunking/text_chunker.h" 38 #include "packager/media/crypto/encryption_handler.h" 39 #include "packager/media/demuxer/demuxer.h" 40 #include "packager/media/event/muxer_listener_factory.h" 41 #include "packager/media/event/vod_media_info_dump_muxer_listener.h" 42 #include "packager/media/formats/webvtt/text_padder.h" 43 #include "packager/media/formats/webvtt/text_readers.h" 44 #include "packager/media/formats/webvtt/webvtt_parser.h" 45 #include "packager/media/formats/webvtt/webvtt_text_output_handler.h" 46 #include "packager/media/formats/webvtt/webvtt_to_mp4_handler.h" 47 #include "packager/media/replicator/replicator.h" 48 #include "packager/media/trick_play/trick_play_handler.h" 49 #include "packager/mpd/base/media_info.pb.h" 50 #include "packager/mpd/base/mpd_builder.h" 51 #include "packager/mpd/base/simple_mpd_notifier.h" 52 #include "packager/status_macros.h" 53 #include "packager/version/version.h" 59 using media::JobManager;
60 using media::KeySource;
61 using media::MuxerOptions;
62 using media::SyncPointQueue;
67 const char kMediaInfoSuffix[] =
".media_info";
69 const int64_t kDefaultTextZeroBiasMs = 10 * 60 * 1000;
71 MuxerOptions CreateMuxerOptions(
const StreamDescriptor& stream,
72 const PackagingParams& params) {
75 options.mp4_params = params.mp4_output_params;
76 options.transport_stream_timestamp_offset_ms =
77 params.transport_stream_timestamp_offset_ms;
78 options.temp_dir = params.temp_dir;
79 options.bandwidth = stream.bandwidth;
80 options.output_file_name = stream.output;
81 options.segment_template = stream.segment_template;
86 MuxerListenerFactory::StreamData ToMuxerListenerData(
87 const StreamDescriptor& stream) {
88 MuxerListenerFactory::StreamData data;
89 data.media_info_output = stream.output;
91 data.hls_group_id = stream.hls_group_id;
92 data.hls_name = stream.hls_name;
93 data.hls_playlist_name = stream.hls_playlist_name;
94 data.hls_iframe_playlist_name = stream.hls_iframe_playlist_name;
95 data.hls_characteristics = stream.hls_characteristics;
97 data.dash_accessiblities = stream.dash_accessiblities;
98 data.dash_roles = stream.dash_roles;
105 bool DetermineTextFileCodec(
const std::string& file, std::string* out) {
110 LOG(ERROR) <<
"Failed to open file " << file
111 <<
" to determine file format.";
115 const uint8_t* content_data =
116 reinterpret_cast<const uint8_t*
>(content.data());
117 MediaContainerName container_name =
118 DetermineContainer(content_data, content.size());
120 if (container_name == CONTAINER_WEBVTT) {
125 if (container_name == CONTAINER_TTML) {
133 MediaContainerName GetOutputFormat(
const StreamDescriptor& descriptor) {
134 if (!descriptor.output_format.empty()) {
135 MediaContainerName format =
136 DetermineContainerFromFormatName(descriptor.output_format);
137 if (format == CONTAINER_UNKNOWN) {
138 LOG(ERROR) <<
"Unable to determine output format from '" 139 << descriptor.output_format <<
"'.";
144 base::Optional<MediaContainerName> format_from_output;
145 base::Optional<MediaContainerName> format_from_segment;
146 if (!descriptor.output.empty()) {
147 format_from_output = DetermineContainerFromFileName(descriptor.output);
148 if (format_from_output.value() == CONTAINER_UNKNOWN) {
149 LOG(ERROR) <<
"Unable to determine output format from '" 150 << descriptor.output <<
"'.";
153 if (!descriptor.segment_template.empty()) {
154 format_from_segment =
155 DetermineContainerFromFileName(descriptor.segment_template);
156 if (format_from_segment.value() == CONTAINER_UNKNOWN) {
157 LOG(ERROR) <<
"Unable to determine output format from '" 158 << descriptor.segment_template <<
"'.";
162 if (format_from_output && format_from_segment) {
163 if (format_from_output.value() != format_from_segment.value()) {
164 LOG(ERROR) <<
"Output format determined from '" << descriptor.output
165 <<
"' differs from output format determined from '" 166 << descriptor.segment_template <<
"'.";
167 return CONTAINER_UNKNOWN;
171 if (format_from_output)
172 return format_from_output.value();
173 if (format_from_segment)
174 return format_from_segment.value();
175 return CONTAINER_UNKNOWN;
178 Status ValidateStreamDescriptor(
bool dump_stream_info,
179 const StreamDescriptor& stream) {
180 if (stream.input.empty()) {
181 return Status(error::INVALID_ARGUMENT,
"Stream input not specified.");
186 if (dump_stream_info && stream.output.empty() &&
187 stream.segment_template.empty()) {
191 if (stream.output.empty() && stream.segment_template.empty()) {
192 return Status(error::INVALID_ARGUMENT,
193 "Streams must specify 'output' or 'segment template'.");
197 if (stream.stream_selector.empty()) {
198 return Status(error::INVALID_ARGUMENT,
199 "Stream stream_selector not specified.");
203 if (stream.segment_template.length()) {
204 RETURN_IF_ERROR(ValidateSegmentTemplate(stream.segment_template));
209 const MediaContainerName output_format = GetOutputFormat(stream);
211 if (output_format == CONTAINER_UNKNOWN) {
212 return Status(error::INVALID_ARGUMENT,
"Unsupported output format.");
214 if (output_format == MediaContainerName::CONTAINER_MPEG2TS) {
215 if (stream.segment_template.empty()) {
217 error::INVALID_ARGUMENT,
218 "Please specify 'segment_template'. Single file TS output is " 225 if (stream.output.length()) {
226 return Status(error::INVALID_ARGUMENT,
227 "All TS segments must be self-initializing. Stream " 228 "descriptors 'output' or 'init_segment' are not allowed.");
230 }
else if (output_format == CONTAINER_WEBVTT ||
231 output_format == CONTAINER_AAC || output_format == CONTAINER_AC3 ||
232 output_format == CONTAINER_EAC3) {
235 if (stream.segment_template.length() && stream.output.length()) {
237 error::INVALID_ARGUMENT,
238 "Segmented WebVTT or PackedAudio output cannot have an init segment. " 239 "Do not specify stream descriptors 'output' or 'init_segment' when " 240 "using 'segment_template'.");
245 if (stream.segment_template.length() && stream.output.empty()) {
246 return Status(error::INVALID_ARGUMENT,
247 "Please specify 'init_segment'. All non-TS multi-segment " 248 "content must provide an init segment.");
252 if (stream.output.find(
'$') != std::string::npos) {
253 if (output_format == CONTAINER_WEBVTT) {
255 error::UNIMPLEMENTED,
256 "WebVTT output with one file per Representation per Period " 257 "is not supported yet. Please use fMP4 instead. If that needs to be " 258 "supported, please file a feature request on GitHub.");
263 RETURN_IF_ERROR(ValidateSegmentTemplate(stream.output));
269 Status ValidateParams(
const PackagingParams& packaging_params,
270 const std::vector<StreamDescriptor>& stream_descriptors) {
271 if (!packaging_params.chunking_params.segment_sap_aligned &&
272 packaging_params.chunking_params.subsegment_sap_aligned) {
273 return Status(error::INVALID_ARGUMENT,
274 "Setting segment_sap_aligned to false but " 275 "subsegment_sap_aligned to true is not allowed.");
278 if (stream_descriptors.empty()) {
279 return Status(error::INVALID_ARGUMENT,
280 "Stream descriptors cannot be empty.");
285 const bool on_demand_dash_profile =
286 stream_descriptors.begin()->segment_template.empty();
287 std::set<std::string> outputs;
288 std::set<std::string> segment_templates;
289 for (
const auto& descriptor : stream_descriptors) {
290 if (on_demand_dash_profile != descriptor.segment_template.empty()) {
291 return Status(error::INVALID_ARGUMENT,
292 "Inconsistent stream descriptor specification: " 293 "segment_template should be specified for none or all " 294 "stream descriptors.");
297 RETURN_IF_ERROR(ValidateStreamDescriptor(
298 packaging_params.test_params.dump_stream_info, descriptor));
300 if (base::StartsWith(descriptor.input,
"udp://",
301 base::CompareCase::SENSITIVE)) {
302 const HlsParams& hls_params = packaging_params.hls_params;
303 if (!hls_params.master_playlist_output.empty() &&
304 hls_params.playlist_type == HlsPlaylistType::kVod) {
306 <<
"Seeing UDP input with HLS Playlist Type set to VOD. The " 307 "playlists will only be generated when UDP socket is closed. " 308 "If you want to do live packaging, --hls_playlist_type needs to " 315 if (!descriptor.output.empty()) {
316 if (outputs.find(descriptor.output) != outputs.end()) {
318 error::INVALID_ARGUMENT,
319 "Seeing duplicated outputs '" + descriptor.output +
320 "' in stream descriptors. Every output must be unique.");
322 outputs.insert(descriptor.output);
324 if (!descriptor.segment_template.empty()) {
325 if (segment_templates.find(descriptor.segment_template) !=
326 segment_templates.end()) {
327 return Status(error::INVALID_ARGUMENT,
328 "Seeing duplicated segment templates '" +
329 descriptor.segment_template +
330 "' in stream descriptors. Every segment template " 333 segment_templates.insert(descriptor.segment_template);
337 if (packaging_params.output_media_info && !on_demand_dash_profile) {
339 return Status(error::UNIMPLEMENTED,
340 "--output_media_info is only supported for on-demand profile " 341 "(not using segment_template).");
347 bool StreamDescriptorCompareFn(
const StreamDescriptor& a,
348 const StreamDescriptor& b) {
354 if (a.input == b.input) {
355 if (a.stream_selector == b.stream_selector) {
358 return a.trick_play_factor < b.trick_play_factor;
360 return a.stream_selector < b.stream_selector;
363 return a.input < b.input;
368 class FakeClock :
public base::Clock {
370 base::Time Now()
override {
return base::Time(); }
373 bool StreamInfoToTextMediaInfo(
const StreamDescriptor& stream_descriptor,
374 MediaInfo* text_media_info) {
376 if (!DetermineTextFileCodec(stream_descriptor.input, &codec)) {
377 LOG(ERROR) <<
"Failed to determine the text file format for " 378 << stream_descriptor.input;
382 MediaInfo::TextInfo* text_info = text_media_info->mutable_text_info();
383 text_info->set_codec(codec);
385 const std::string& language = stream_descriptor.language;
386 if (!language.empty()) {
387 text_info->set_language(language);
390 text_media_info->set_media_file_name(stream_descriptor.output);
391 text_media_info->set_container_type(MediaInfo::CONTAINER_TEXT);
393 if (stream_descriptor.bandwidth != 0) {
394 text_media_info->set_bandwidth(stream_descriptor.bandwidth);
399 const int kDefaultTextBandwidth = 256;
400 text_media_info->set_bandwidth(kDefaultTextBandwidth);
409 Status CreateDemuxer(
const StreamDescriptor& stream,
410 const PackagingParams& packaging_params,
411 std::shared_ptr<Demuxer>* new_demuxer) {
412 std::shared_ptr<Demuxer> demuxer = std::make_shared<Demuxer>(stream.input);
413 demuxer->set_dump_stream_info(packaging_params.test_params.dump_stream_info);
415 if (packaging_params.decryption_params.key_provider != KeyProvider::kNone) {
416 std::unique_ptr<KeySource> decryption_key_source(
417 CreateDecryptionKeySource(packaging_params.decryption_params));
418 if (!decryption_key_source) {
420 error::INVALID_ARGUMENT,
421 "Must define decryption key source when defining key provider");
423 demuxer->SetKeySource(std::move(decryption_key_source));
426 *new_demuxer = std::move(demuxer);
430 std::shared_ptr<MediaHandler> CreateEncryptionHandler(
431 const PackagingParams& packaging_params,
432 const StreamDescriptor& stream,
433 KeySource* key_source) {
434 if (stream.skip_encryption) {
443 EncryptionParams encryption_params = packaging_params.encryption_params;
448 if (GetOutputFormat(stream) == CONTAINER_MPEG2TS ||
449 GetOutputFormat(stream) == CONTAINER_AAC ||
450 GetOutputFormat(stream) == CONTAINER_AC3 ||
451 GetOutputFormat(stream) == CONTAINER_EAC3) {
452 VLOG(1) <<
"Use Apple Sample AES encryption for MPEG2TS or Packed Audio.";
453 encryption_params.protection_scheme = kAppleSampleAesProtectionScheme;
456 if (!stream.drm_label.empty()) {
457 const std::string& drm_label = stream.drm_label;
458 encryption_params.stream_label_func =
459 [drm_label](
const EncryptionParams::EncryptedStreamAttributes&) {
462 }
else if (!encryption_params.stream_label_func) {
463 const int kDefaultMaxSdPixels = 768 * 576;
464 const int kDefaultMaxHdPixels = 1920 * 1080;
465 const int kDefaultMaxUhd1Pixels = 4096 * 2160;
466 encryption_params.stream_label_func = std::bind(
468 kDefaultMaxHdPixels, kDefaultMaxUhd1Pixels, std::placeholders::_1);
471 return std::make_shared<EncryptionHandler>(encryption_params, key_source);
474 std::unique_ptr<TextChunker> CreateTextChunker(
475 const ChunkingParams& chunking_params) {
476 const float segment_length_in_seconds =
477 chunking_params.segment_duration_in_seconds;
478 return std::unique_ptr<TextChunker>(
479 new TextChunker(segment_length_in_seconds));
482 Status CreateHlsTextJob(
const StreamDescriptor& stream,
483 const PackagingParams& packaging_params,
484 std::unique_ptr<MuxerListener> muxer_listener,
485 SyncPointQueue* sync_points,
486 JobManager* job_manager) {
487 DCHECK(muxer_listener);
490 if (stream.segment_template.empty()) {
491 return Status(error::INVALID_ARGUMENT,
492 "Cannot output text (" + stream.input +
493 ") to HLS with no segment template");
499 MuxerOptions muxer_options = CreateMuxerOptions(stream, packaging_params);
500 muxer_options.bandwidth = stream.bandwidth ? stream.bandwidth : 256;
502 auto output = std::make_shared<WebVttTextOutputHandler>(
503 muxer_options, std::move(muxer_listener));
505 std::unique_ptr<FileReader> reader;
509 std::make_shared<WebVttParser>(std::move(reader), stream.language);
510 auto padder = std::make_shared<TextPadder>(kDefaultTextZeroBiasMs);
511 auto cue_aligner = sync_points
512 ? std::make_shared<CueAlignmentHandler>(sync_points)
514 auto chunker = CreateTextChunker(packaging_params.chunking_params);
516 job_manager->Add(
"Segmented Text Job", parser);
518 return MediaHandler::Chain({std::move(parser), std::move(padder),
519 std::move(cue_aligner), std::move(chunker),
523 Status CreateWebVttToMp4TextJob(
const StreamDescriptor& stream,
524 const PackagingParams& packaging_params,
525 std::unique_ptr<MuxerListener> muxer_listener,
526 SyncPointQueue* sync_points,
527 MuxerFactory* muxer_factory,
528 std::shared_ptr<OriginHandler>* root) {
529 std::unique_ptr<FileReader> reader;
533 std::make_shared<WebVttParser>(std::move(reader), stream.language);
534 auto padder = std::make_shared<TextPadder>(kDefaultTextZeroBiasMs);
536 auto text_to_mp4 = std::make_shared<WebVttToMp4Handler>();
537 auto muxer = muxer_factory->CreateMuxer(GetOutputFormat(stream), stream);
538 muxer->SetMuxerListener(std::move(muxer_listener));
541 std::shared_ptr<MediaHandler> cue_aligner;
543 cue_aligner = std::make_shared<CueAlignmentHandler>(sync_points);
546 std::shared_ptr<MediaHandler> chunker =
547 CreateTextChunker(packaging_params.chunking_params);
551 return MediaHandler::Chain({std::move(parser), std::move(padder),
552 std::move(cue_aligner), std::move(chunker),
553 std::move(text_to_mp4), std::move(muxer)});
556 Status CreateTextJobs(
557 const std::vector<std::reference_wrapper<const StreamDescriptor>>& streams,
558 const PackagingParams& packaging_params,
559 SyncPointQueue* sync_points,
560 MuxerListenerFactory* muxer_listener_factory,
561 MuxerFactory* muxer_factory,
562 MpdNotifier* mpd_notifier,
563 JobManager* job_manager) {
564 DCHECK(muxer_listener_factory);
566 for (
const StreamDescriptor& stream : streams) {
573 const auto input_container = DetermineContainerFromFileName(stream.input);
574 const auto output_container = GetOutputFormat(stream);
576 if (input_container != CONTAINER_WEBVTT &&
577 input_container != CONTAINER_TTML) {
578 return Status(error::INVALID_ARGUMENT,
579 "Text output format is not support for " + stream.input);
582 if (output_container == CONTAINER_MOV) {
583 if (input_container == CONTAINER_TTML) {
584 return Status(error::INVALID_ARGUMENT,
585 "TTML in MP4 is not supported yet. Please follow " 586 "https://github.com/google/shaka-packager/issues/87 for " 590 std::unique_ptr<MuxerListener> muxer_listener =
591 muxer_listener_factory->CreateListener(ToMuxerListenerData(stream));
593 std::shared_ptr<OriginHandler> root;
594 RETURN_IF_ERROR(CreateWebVttToMp4TextJob(
595 stream, packaging_params, std::move(muxer_listener), sync_points,
596 muxer_factory, &root));
598 job_manager->Add(
"MP4 text job", std::move(root));
600 std::unique_ptr<MuxerListener> hls_listener =
601 muxer_listener_factory->CreateHlsListener(
602 ToMuxerListenerData(stream));
606 if (input_container == CONTAINER_TTML) {
607 return Status(error::INVALID_ARGUMENT,
608 "HLS does not support TTML in xml format.");
610 if (stream.segment_template.empty() || !stream.output.empty()) {
611 return Status(error::INVALID_ARGUMENT,
612 "segment_template needs to be specified for HLS text " 613 "output. Single file output is not supported yet.");
617 if (mpd_notifier && !stream.segment_template.empty()) {
618 return Status(error::INVALID_ARGUMENT,
619 "Cannot create text output for MPD with segment output.");
625 RETURN_IF_ERROR(CreateHlsTextJob(stream, packaging_params,
626 std::move(hls_listener), sync_points,
630 if (!stream.output.empty()) {
631 if (!
File::Copy(stream.input.c_str(), stream.output.c_str())) {
634 &error,
"Failed to copy the input file (%s) to output file (%s).",
635 stream.input.c_str(), stream.output.c_str());
636 return Status(error::FILE_FAILURE, error);
639 MediaInfo text_media_info;
640 if (!StreamInfoToTextMediaInfo(stream, &text_media_info)) {
641 return Status(error::INVALID_ARGUMENT,
642 "Could not create media info for stream.");
649 if (mpd_notifier->NotifyNewContainer(text_media_info, &unused)) {
650 mpd_notifier->Flush();
652 return Status(error::PARSER_FAILURE,
653 "Failed to process text file " + stream.input);
657 if (packaging_params.output_media_info) {
659 text_media_info, stream.output + kMediaInfoSuffix);
668 Status CreateAudioVideoJobs(
669 const std::vector<std::reference_wrapper<const StreamDescriptor>>& streams,
670 const PackagingParams& packaging_params,
671 KeySource* encryption_key_source,
672 SyncPointQueue* sync_points,
673 MuxerListenerFactory* muxer_listener_factory,
674 MuxerFactory* muxer_factory,
675 JobManager* job_manager) {
676 DCHECK(muxer_listener_factory);
677 DCHECK(muxer_factory);
682 std::map<std::string, std::shared_ptr<Demuxer>> sources;
683 std::map<std::string, std::shared_ptr<MediaHandler>> cue_aligners;
685 for (
const StreamDescriptor& stream : streams) {
686 bool seen_input_before = sources.find(stream.input) != sources.end();
687 if (seen_input_before) {
692 CreateDemuxer(stream, packaging_params, &sources[stream.input]));
693 cue_aligners[stream.input] =
694 sync_points ? std::make_shared<CueAlignmentHandler>(sync_points)
698 for (
auto& source : sources) {
699 job_manager->Add(
"RemuxJob", source.second);
704 std::shared_ptr<MediaHandler> replicator;
706 std::string previous_input;
707 std::string previous_selector;
709 for (
const StreamDescriptor& stream : streams) {
711 auto& demuxer = sources[stream.input];
712 auto& cue_aligner = cue_aligners[stream.input];
714 const bool new_input_file = stream.input != previous_input;
715 const bool new_stream =
716 new_input_file || previous_selector != stream.stream_selector;
717 previous_input = stream.input;
718 previous_selector = stream.stream_selector;
722 if (stream.output.empty() && stream.segment_template.empty()) {
730 if (!stream.language.empty()) {
731 demuxer->SetLanguageOverride(stream.stream_selector, stream.language);
734 replicator = std::make_shared<Replicator>();
736 std::make_shared<ChunkingHandler>(packaging_params.chunking_params);
737 auto encryptor = CreateEncryptionHandler(packaging_params, stream,
738 encryption_key_source);
743 MediaHandler::Chain({cue_aligner, chunker, encryptor, replicator}));
745 demuxer->SetHandler(stream.stream_selector, cue_aligner));
747 RETURN_IF_ERROR(MediaHandler::Chain({chunker, encryptor, replicator}));
748 RETURN_IF_ERROR(demuxer->SetHandler(stream.stream_selector, chunker));
753 std::shared_ptr<Muxer> muxer =
754 muxer_factory->CreateMuxer(GetOutputFormat(stream), stream);
756 return Status(error::INVALID_ARGUMENT,
"Failed to create muxer for " +
758 stream.stream_selector);
761 std::unique_ptr<MuxerListener> muxer_listener =
762 muxer_listener_factory->CreateListener(ToMuxerListenerData(stream));
763 muxer->SetMuxerListener(std::move(muxer_listener));
766 std::shared_ptr<MediaHandler> trick_play =
767 stream.trick_play_factor
768 ? std::make_shared<TrickPlayHandler>(stream.trick_play_factor)
771 RETURN_IF_ERROR(MediaHandler::Chain({replicator, trick_play, muxer}));
777 Status CreateAllJobs(
const std::vector<StreamDescriptor>& stream_descriptors,
778 const PackagingParams& packaging_params,
779 MpdNotifier* mpd_notifier,
780 KeySource* encryption_key_source,
781 SyncPointQueue* sync_points,
782 MuxerListenerFactory* muxer_listener_factory,
783 MuxerFactory* muxer_factory,
784 JobManager* job_manager) {
785 DCHECK(muxer_factory);
786 DCHECK(muxer_listener_factory);
790 std::vector<std::reference_wrapper<const StreamDescriptor>> text_streams;
791 std::vector<std::reference_wrapper<const StreamDescriptor>>
794 bool has_transport_audio_video_streams =
false;
795 bool has_non_transport_audio_video_streams =
false;
797 for (
const StreamDescriptor& stream : stream_descriptors) {
801 if (stream.stream_selector ==
"text") {
802 text_streams.push_back(stream);
804 audio_video_streams.push_back(stream);
806 switch (GetOutputFormat(stream)) {
807 case CONTAINER_MPEG2TS:
811 has_transport_audio_video_streams =
true;
814 has_non_transport_audio_video_streams =
true;
822 std::sort(audio_video_streams.begin(), audio_video_streams.end(),
823 media::StreamDescriptorCompareFn);
825 if (!text_streams.empty()) {
826 PackagingParams text_packaging_params = packaging_params;
827 if (text_packaging_params.transport_stream_timestamp_offset_ms > 0) {
828 if (has_transport_audio_video_streams &&
829 has_non_transport_audio_video_streams) {
830 LOG(WARNING) <<
"There may be problems mixing transport streams and " 831 "non-transport streams. For example, the subtitles may " 832 "be out of sync with non-transport streams.";
833 }
else if (has_non_transport_audio_video_streams) {
836 text_packaging_params.transport_stream_timestamp_offset_ms = 0;
840 RETURN_IF_ERROR(CreateTextJobs(text_streams, text_packaging_params,
841 sync_points, muxer_listener_factory,
842 muxer_factory, mpd_notifier, job_manager));
845 RETURN_IF_ERROR(CreateAudioVideoJobs(
846 audio_video_streams, packaging_params, encryption_key_source, sync_points,
847 muxer_listener_factory, muxer_factory, job_manager));
850 return job_manager->InitializeJobs();
856 struct Packager::PackagerInternal {
857 media::FakeClock fake_clock;
858 std::unique_ptr<KeySource> encryption_key_source;
859 std::unique_ptr<MpdNotifier> mpd_notifier;
860 std::unique_ptr<hls::HlsNotifier> hls_notifier;
861 BufferCallbackParams buffer_callback_params;
862 std::unique_ptr<media::JobManager> job_manager;
865 Packager::Packager() {}
867 Packager::~Packager() {}
871 const std::vector<StreamDescriptor>& stream_descriptors) {
873 static base::AtExitManager exit;
877 return Status(error::INVALID_ARGUMENT,
"Already initialized.");
879 RETURN_IF_ERROR(media::ValidateParams(packaging_params, stream_descriptors));
882 SetPackagerVersionForTesting(
886 std::unique_ptr<PackagerInternal>
internal(
new PackagerInternal);
890 internal->encryption_key_source = CreateEncryptionKeySource(
891 static_cast<media::FourCC>(
894 if (!internal->encryption_key_source)
895 return Status(error::INVALID_ARGUMENT,
"Failed to create key source.");
904 const double target_segment_duration =
911 if (internal->buffer_callback_params.write_func) {
913 internal->buffer_callback_params, mpd_params.
mpd_output);
931 const bool on_demand_dash_profile =
932 stream_descriptors.begin()->segment_template.empty();
934 media::GetMpdOptions(on_demand_dash_profile, mpd_params);
936 if (!internal->mpd_notifier->Init()) {
937 LOG(ERROR) <<
"MpdNotifier failed to initialize.";
938 return Status(error::INVALID_ARGUMENT,
939 "Failed to initialize MpdNotifier.");
947 std::unique_ptr<SyncPointQueue> sync_points;
952 internal->job_manager.reset(
new JobManager(std::move(sync_points)));
954 std::vector<StreamDescriptor> streams_for_jobs;
960 if (internal->buffer_callback_params.read_func) {
965 if (internal->buffer_callback_params.write_func) {
969 internal->buffer_callback_params, descriptor.segment_template);
977 error::INVALID_ARGUMENT,
978 "Unknown/invalid language specified: " + descriptor.language);
982 streams_for_jobs.push_back(copy);
992 internal->hls_notifier.get());
994 RETURN_IF_ERROR(media::CreateAllJobs(
995 streams_for_jobs, packaging_params, internal->mpd_notifier.get(),
996 internal->encryption_key_source.get(),
997 internal->job_manager->sync_points(), &muxer_listener_factory,
998 &muxer_factory,
internal->job_manager.get()));
1000 internal_ = std::move(
internal);
1006 return Status(error::INVALID_ARGUMENT,
"Not yet initialized.");
1008 RETURN_IF_ERROR(internal_->job_manager->RunJobs());
1010 if (internal_->hls_notifier) {
1011 if (!internal_->hls_notifier->Flush())
1012 return Status(error::INVALID_ARGUMENT,
"Failed to flush Hls.");
1014 if (internal_->mpd_notifier) {
1015 if (!internal_->mpd_notifier->Flush())
1016 return Status(error::INVALID_ARGUMENT,
"Failed to flush Mpd.");
1023 LOG(INFO) <<
"Not yet initialized. Return directly.";
1026 internal_->job_manager->CancelJobs();
1030 return GetPackagerVersion();
1036 int max_uhd1_pixels,
1038 if (stream_attributes.stream_type ==
1039 EncryptionParams::EncryptedStreamAttributes::kAudio)
1041 if (stream_attributes.stream_type ==
1042 EncryptionParams::EncryptedStreamAttributes::kVideo) {
1043 const int pixels = stream_attributes.oneof.video.width *
1044 stream_attributes.oneof.video.height;
1045 if (pixels <= max_sd_pixels)
1047 if (pixels <= max_hd_pixels)
1049 if (pixels <= max_uhd1_pixels)
BufferCallbackParams buffer_callback_params
Buffer callback params.
std::string master_playlist_output
HLS master playlist output path.
DASH MPD related parameters.
Defines a single input/output stream.
std::string input
Input/source media file path or network stream URL. Required.
HlsParams hls_params
HLS related parameters.
Status Initialize(const PackagingParams &packaging_params, const std::vector< StreamDescriptor > &stream_descriptors)
std::string default_language
static std::string DefaultStreamLabelFunction(int max_sd_pixels, int max_hd_pixels, int max_uhd1_pixels, const EncryptionParams::EncryptedStreamAttributes &stream_attributes)
ChunkingParams chunking_params
Chunking (segmentation) related parameters.
std::string default_text_language
std::vector< Cuepoint > cue_points
List of cuepoints.
std::string LanguageToShortestForm(const std::string &language)
std::string segment_template
Specifies segment template. Can be empty.
static bool Copy(const char *from_file_name, const char *to_file_name)
static bool ReadFileToString(const char *file_name, std::string *contents)
All the methods that are virtual are virtual for mocking.
static std::string GetLibraryVersion()
double target_segment_duration
std::string LanguageToISO_639_2(const std::string &language)
std::string injected_library_version
MpdParams mpd_params
DASH MPD related parameters.
AdCueGeneratorParams ad_cue_generator_params
Out of band cuepoint parameters.
EncryptionParams encryption_params
Encryption and Decryption Parameters.
std::string mpd_output
MPD output file path.
static std::string MakeCallbackFileName(const BufferCallbackParams &callback_params, const std::string &name)
double target_segment_duration
Encrypted stream information that is used to determine stream label.
std::string default_language
double segment_duration_in_seconds
Segment duration in seconds.
void Cancel()
Cancel packaging. Note that it has to be called from another thread.
std::string default_text_language