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;
90 data.hls_group_id = stream.hls_group_id;
91 data.hls_name = stream.hls_name;
92 data.hls_playlist_name = stream.hls_playlist_name;
93 data.hls_iframe_playlist_name = stream.hls_iframe_playlist_name;
100 bool DetermineTextFileCodec(
const std::string& file, std::string* out) {
105 LOG(ERROR) <<
"Failed to open file " << file
106 <<
" to determine file format.";
110 const uint8_t* content_data =
111 reinterpret_cast<const uint8_t*
>(content.data());
112 MediaContainerName container_name =
113 DetermineContainer(content_data, content.size());
115 if (container_name == CONTAINER_WEBVTT) {
120 if (container_name == CONTAINER_TTML) {
128 MediaContainerName GetOutputFormat(
const StreamDescriptor& descriptor) {
129 if (!descriptor.output_format.empty()) {
130 MediaContainerName format =
131 DetermineContainerFromFormatName(descriptor.output_format);
132 if (format == CONTAINER_UNKNOWN) {
133 LOG(ERROR) <<
"Unable to determine output format from '" 134 << descriptor.output_format <<
"'.";
139 base::Optional<MediaContainerName> format_from_output;
140 base::Optional<MediaContainerName> format_from_segment;
141 if (!descriptor.output.empty()) {
142 format_from_output = DetermineContainerFromFileName(descriptor.output);
143 if (format_from_output.value() == CONTAINER_UNKNOWN) {
144 LOG(ERROR) <<
"Unable to determine output format from '" 145 << descriptor.output <<
"'.";
148 if (!descriptor.segment_template.empty()) {
149 format_from_segment =
150 DetermineContainerFromFileName(descriptor.segment_template);
151 if (format_from_segment.value() == CONTAINER_UNKNOWN) {
152 LOG(ERROR) <<
"Unable to determine output format from '" 153 << descriptor.segment_template <<
"'.";
157 if (format_from_output && format_from_segment) {
158 if (format_from_output.value() != format_from_segment.value()) {
159 LOG(ERROR) <<
"Output format determined from '" << descriptor.output
160 <<
"' differs from output format determined from '" 161 << descriptor.segment_template <<
"'.";
162 return CONTAINER_UNKNOWN;
166 if (format_from_output)
167 return format_from_output.value();
168 if (format_from_segment)
169 return format_from_segment.value();
170 return CONTAINER_UNKNOWN;
173 Status ValidateStreamDescriptor(
bool dump_stream_info,
174 const StreamDescriptor& stream) {
175 if (stream.input.empty()) {
176 return Status(error::INVALID_ARGUMENT,
"Stream input not specified.");
181 if (dump_stream_info && stream.output.empty() &&
182 stream.segment_template.empty()) {
186 if (stream.output.empty() && stream.segment_template.empty()) {
187 return Status(error::INVALID_ARGUMENT,
188 "Streams must specify 'output' or 'segment template'.");
192 if (stream.stream_selector.empty()) {
193 return Status(error::INVALID_ARGUMENT,
194 "Stream stream_selector not specified.");
198 if (stream.segment_template.length()) {
199 RETURN_IF_ERROR(ValidateSegmentTemplate(stream.segment_template));
204 const MediaContainerName output_format = GetOutputFormat(stream);
206 if (output_format == CONTAINER_UNKNOWN) {
207 return Status(error::INVALID_ARGUMENT,
"Unsupported output format.");
208 }
else if (output_format == MediaContainerName::CONTAINER_MPEG2TS) {
209 if (stream.segment_template.empty()) {
211 error::INVALID_ARGUMENT,
212 "Please specify 'segment_template'. Single file TS output is " 219 if (stream.output.length()) {
220 return Status(error::INVALID_ARGUMENT,
221 "All TS segments must be self-initializing. Stream " 222 "descriptors 'output' or 'init_segment' are not allowed.");
224 }
else if (output_format == CONTAINER_WEBVTT ||
225 output_format == CONTAINER_AAC || output_format == CONTAINER_AC3 ||
226 output_format == CONTAINER_EAC3) {
229 if (stream.segment_template.length() && stream.output.length()) {
231 error::INVALID_ARGUMENT,
232 "Segmented WebVTT or PackedAudio output cannot have an init segment. " 233 "Do not specify stream descriptors 'output' or 'init_segment' when " 234 "using 'segment_template'.");
239 if (stream.segment_template.length() && stream.output.empty()) {
240 return Status(error::INVALID_ARGUMENT,
241 "Please specify 'init_segment'. All non-TS multi-segment " 242 "content must provide an init segment.");
246 if (stream.output.find(
'$') != std::string::npos) {
247 if (output_format == CONTAINER_WEBVTT) {
249 error::UNIMPLEMENTED,
250 "WebVTT output with one file per Representation per Period " 251 "is not supported yet. Please use fMP4 instead. If that needs to be " 252 "supported, please file a feature request on GitHub.");
257 RETURN_IF_ERROR(ValidateSegmentTemplate(stream.output));
263 Status ValidateParams(
const PackagingParams& packaging_params,
264 const std::vector<StreamDescriptor>& stream_descriptors) {
265 if (!packaging_params.chunking_params.segment_sap_aligned &&
266 packaging_params.chunking_params.subsegment_sap_aligned) {
267 return Status(error::INVALID_ARGUMENT,
268 "Setting segment_sap_aligned to false but " 269 "subsegment_sap_aligned to true is not allowed.");
272 if (stream_descriptors.empty()) {
273 return Status(error::INVALID_ARGUMENT,
274 "Stream descriptors cannot be empty.");
279 const bool on_demand_dash_profile =
280 stream_descriptors.begin()->segment_template.empty();
281 for (
const auto& descriptor : stream_descriptors) {
282 if (on_demand_dash_profile != descriptor.segment_template.empty()) {
283 return Status(error::INVALID_ARGUMENT,
284 "Inconsistent stream descriptor specification: " 285 "segment_template should be specified for none or all " 286 "stream descriptors.");
289 RETURN_IF_ERROR(ValidateStreamDescriptor(
290 packaging_params.test_params.dump_stream_info, descriptor));
292 if (base::StartsWith(descriptor.input,
"udp://",
293 base::CompareCase::SENSITIVE)) {
294 const HlsParams& hls_params = packaging_params.hls_params;
295 if (!hls_params.master_playlist_output.empty() &&
296 hls_params.playlist_type == HlsPlaylistType::kVod) {
298 <<
"Seeing UDP input with HLS Playlist Type set to VOD. The " 299 "playlists will only be generated when UDP socket is closed. " 300 "If you want to do live packaging, --hls_playlist_type needs to " 308 if (packaging_params.output_media_info && !on_demand_dash_profile) {
310 return Status(error::UNIMPLEMENTED,
311 "--output_media_info is only supported for on-demand profile " 312 "(not using segment_template).");
318 bool StreamDescriptorCompareFn(
const StreamDescriptor& a,
319 const StreamDescriptor& b) {
325 if (a.input == b.input) {
326 if (a.stream_selector == b.stream_selector) {
329 return a.trick_play_factor < b.trick_play_factor;
331 return a.stream_selector < b.stream_selector;
335 return a.input < b.input;
340 class FakeClock :
public base::Clock {
342 base::Time Now()
override {
return base::Time(); }
345 bool StreamInfoToTextMediaInfo(
const StreamDescriptor& stream_descriptor,
346 MediaInfo* text_media_info) {
348 if (!DetermineTextFileCodec(stream_descriptor.input, &codec)) {
349 LOG(ERROR) <<
"Failed to determine the text file format for " 350 << stream_descriptor.input;
354 MediaInfo::TextInfo* text_info = text_media_info->mutable_text_info();
355 text_info->set_codec(codec);
357 const std::string& language = stream_descriptor.language;
358 if (!language.empty()) {
359 text_info->set_language(language);
362 text_media_info->set_media_file_name(stream_descriptor.output);
363 text_media_info->set_container_type(MediaInfo::CONTAINER_TEXT);
365 if (stream_descriptor.bandwidth != 0) {
366 text_media_info->set_bandwidth(stream_descriptor.bandwidth);
371 const int kDefaultTextBandwidth = 256;
372 text_media_info->set_bandwidth(kDefaultTextBandwidth);
381 Status CreateDemuxer(
const StreamDescriptor& stream,
382 const PackagingParams& packaging_params,
383 std::shared_ptr<Demuxer>* new_demuxer) {
384 std::shared_ptr<Demuxer> demuxer = std::make_shared<Demuxer>(stream.input);
385 demuxer->set_dump_stream_info(packaging_params.test_params.dump_stream_info);
387 if (packaging_params.decryption_params.key_provider != KeyProvider::kNone) {
388 std::unique_ptr<KeySource> decryption_key_source(
389 CreateDecryptionKeySource(packaging_params.decryption_params));
390 if (!decryption_key_source) {
392 error::INVALID_ARGUMENT,
393 "Must define decryption key source when defining key provider");
395 demuxer->SetKeySource(std::move(decryption_key_source));
398 *new_demuxer = std::move(demuxer);
402 std::shared_ptr<MediaHandler> CreateEncryptionHandler(
403 const PackagingParams& packaging_params,
404 const StreamDescriptor& stream,
405 KeySource* key_source) {
406 if (stream.skip_encryption) {
415 EncryptionParams encryption_params = packaging_params.encryption_params;
420 if (GetOutputFormat(stream) == CONTAINER_MPEG2TS ||
421 GetOutputFormat(stream) == CONTAINER_AAC ||
422 GetOutputFormat(stream) == CONTAINER_AC3 ||
423 GetOutputFormat(stream) == CONTAINER_EAC3) {
424 VLOG(1) <<
"Use Apple Sample AES encryption for MPEG2TS or Packed Audio.";
425 encryption_params.protection_scheme = kAppleSampleAesProtectionScheme;
428 if (!stream.drm_label.empty()) {
429 const std::string& drm_label = stream.drm_label;
430 encryption_params.stream_label_func =
431 [drm_label](
const EncryptionParams::EncryptedStreamAttributes&) {
434 }
else if (!encryption_params.stream_label_func) {
435 const int kDefaultMaxSdPixels = 768 * 576;
436 const int kDefaultMaxHdPixels = 1920 * 1080;
437 const int kDefaultMaxUhd1Pixels = 4096 * 2160;
438 encryption_params.stream_label_func = std::bind(
440 kDefaultMaxHdPixels, kDefaultMaxUhd1Pixels, std::placeholders::_1);
443 return std::make_shared<EncryptionHandler>(encryption_params, key_source);
446 std::unique_ptr<TextChunker> CreateTextChunker(
447 const ChunkingParams& chunking_params) {
448 const float segment_length_in_seconds =
449 chunking_params.segment_duration_in_seconds;
450 return std::unique_ptr<TextChunker>(
451 new TextChunker(segment_length_in_seconds));
454 Status CreateHlsTextJob(
const StreamDescriptor& stream,
455 const PackagingParams& packaging_params,
456 std::unique_ptr<MuxerListener> muxer_listener,
457 SyncPointQueue* sync_points,
458 JobManager* job_manager) {
459 DCHECK(muxer_listener);
462 if (stream.segment_template.empty()) {
463 return Status(error::INVALID_ARGUMENT,
464 "Cannot output text (" + stream.input +
465 ") to HLS with no segment template");
471 MuxerOptions muxer_options = CreateMuxerOptions(stream, packaging_params);
472 muxer_options.bandwidth = stream.bandwidth ? stream.bandwidth : 256;
474 auto output = std::make_shared<WebVttTextOutputHandler>(
475 muxer_options, std::move(muxer_listener));
477 std::unique_ptr<FileReader> reader;
481 std::make_shared<WebVttParser>(std::move(reader), stream.language);
482 auto padder = std::make_shared<TextPadder>(kDefaultTextZeroBiasMs);
483 auto cue_aligner = sync_points
484 ? std::make_shared<CueAlignmentHandler>(sync_points)
486 auto chunker = CreateTextChunker(packaging_params.chunking_params);
488 job_manager->Add(
"Segmented Text Job", parser);
490 return MediaHandler::Chain({std::move(parser), std::move(padder),
491 std::move(cue_aligner), std::move(chunker),
495 Status CreateWebVttToMp4TextJob(
const StreamDescriptor& stream,
496 const PackagingParams& packaging_params,
497 std::unique_ptr<MuxerListener> muxer_listener,
498 SyncPointQueue* sync_points,
499 MuxerFactory* muxer_factory,
500 std::shared_ptr<OriginHandler>* root) {
501 std::unique_ptr<FileReader> reader;
505 std::make_shared<WebVttParser>(std::move(reader), stream.language);
506 auto padder = std::make_shared<TextPadder>(kDefaultTextZeroBiasMs);
508 auto text_to_mp4 = std::make_shared<WebVttToMp4Handler>();
509 auto muxer = muxer_factory->CreateMuxer(GetOutputFormat(stream), stream);
510 muxer->SetMuxerListener(std::move(muxer_listener));
513 std::shared_ptr<MediaHandler> cue_aligner;
515 cue_aligner = std::make_shared<CueAlignmentHandler>(sync_points);
518 std::shared_ptr<MediaHandler> chunker =
519 CreateTextChunker(packaging_params.chunking_params);
523 return MediaHandler::Chain({std::move(parser), std::move(padder),
524 std::move(cue_aligner), std::move(chunker),
525 std::move(text_to_mp4), std::move(muxer)});
528 Status CreateTextJobs(
529 const std::vector<std::reference_wrapper<const StreamDescriptor>>& streams,
530 const PackagingParams& packaging_params,
531 SyncPointQueue* sync_points,
532 MuxerListenerFactory* muxer_listener_factory,
533 MuxerFactory* muxer_factory,
534 MpdNotifier* mpd_notifier,
535 JobManager* job_manager) {
536 DCHECK(muxer_listener_factory);
538 for (
const StreamDescriptor& stream : streams) {
544 const auto input_container = DetermineContainerFromFileName(stream.input);
545 const auto output_container = GetOutputFormat(stream);
547 if (input_container != CONTAINER_WEBVTT) {
548 return Status(error::INVALID_ARGUMENT,
549 "Text output format is not support for " + stream.input);
552 if (output_container == CONTAINER_MOV) {
553 std::unique_ptr<MuxerListener> muxer_listener =
554 muxer_listener_factory->CreateListener(ToMuxerListenerData(stream));
556 std::shared_ptr<OriginHandler> root;
557 RETURN_IF_ERROR(CreateWebVttToMp4TextJob(
558 stream, packaging_params, std::move(muxer_listener), sync_points,
559 muxer_factory, &root));
561 job_manager->Add(
"MP4 text job", std::move(root));
563 std::unique_ptr<MuxerListener> hls_listener =
564 muxer_listener_factory->CreateHlsListener(
565 ToMuxerListenerData(stream));
569 if (stream.segment_template.empty() || !stream.output.empty()) {
570 return Status(error::INVALID_ARGUMENT,
571 "segment_template needs to be specified for HLS text " 572 "output. Single file output is not supported yet.");
576 if (mpd_notifier && !stream.segment_template.empty()) {
577 return Status(error::INVALID_ARGUMENT,
578 "Cannot create text output for MPD with segment output.");
584 RETURN_IF_ERROR(CreateHlsTextJob(stream, packaging_params,
585 std::move(hls_listener), sync_points,
589 if (!stream.output.empty()) {
590 if (!
File::Copy(stream.input.c_str(), stream.output.c_str())) {
593 &error,
"Failed to copy the input file (%s) to output file (%s).",
594 stream.input.c_str(), stream.output.c_str());
595 return Status(error::FILE_FAILURE, error);
598 MediaInfo text_media_info;
599 if (!StreamInfoToTextMediaInfo(stream, &text_media_info)) {
600 return Status(error::INVALID_ARGUMENT,
601 "Could not create media info for stream.");
608 if (mpd_notifier->NotifyNewContainer(text_media_info, &unused)) {
609 mpd_notifier->Flush();
611 return Status(error::PARSER_FAILURE,
612 "Failed to process text file " + stream.input);
616 if (packaging_params.output_media_info) {
618 text_media_info, stream.output + kMediaInfoSuffix);
627 Status CreateAudioVideoJobs(
628 const std::vector<std::reference_wrapper<const StreamDescriptor>>& streams,
629 const PackagingParams& packaging_params,
630 KeySource* encryption_key_source,
631 SyncPointQueue* sync_points,
632 MuxerListenerFactory* muxer_listener_factory,
633 MuxerFactory* muxer_factory,
634 JobManager* job_manager) {
635 DCHECK(muxer_listener_factory);
636 DCHECK(muxer_factory);
641 std::map<std::string, std::shared_ptr<Demuxer>> sources;
642 std::map<std::string, std::shared_ptr<MediaHandler>> cue_aligners;
644 for (
const StreamDescriptor& stream : streams) {
645 bool seen_input_before = sources.find(stream.input) != sources.end();
646 if (seen_input_before) {
651 CreateDemuxer(stream, packaging_params, &sources[stream.input]));
652 cue_aligners[stream.input] =
653 sync_points ? std::make_shared<CueAlignmentHandler>(sync_points)
657 for (
auto& source : sources) {
658 job_manager->Add(
"RemuxJob", source.second);
663 std::shared_ptr<MediaHandler> replicator;
665 std::string previous_input;
666 std::string previous_selector;
668 for (
const StreamDescriptor& stream : streams) {
670 auto& demuxer = sources[stream.input];
671 auto& cue_aligner = cue_aligners[stream.input];
673 const bool new_input_file = stream.input != previous_input;
674 const bool new_stream =
675 new_input_file || previous_selector != stream.stream_selector;
676 previous_input = stream.input;
677 previous_selector = stream.stream_selector;
681 if (stream.output.empty() && stream.segment_template.empty()) {
689 if (!stream.language.empty()) {
690 demuxer->SetLanguageOverride(stream.stream_selector, stream.language);
693 replicator = std::make_shared<Replicator>();
695 std::make_shared<ChunkingHandler>(packaging_params.chunking_params);
696 auto encryptor = CreateEncryptionHandler(packaging_params, stream,
697 encryption_key_source);
702 MediaHandler::Chain({cue_aligner, chunker, encryptor, replicator}));
704 demuxer->SetHandler(stream.stream_selector, cue_aligner));
706 RETURN_IF_ERROR(MediaHandler::Chain({chunker, encryptor, replicator}));
707 RETURN_IF_ERROR(demuxer->SetHandler(stream.stream_selector, chunker));
712 std::shared_ptr<Muxer> muxer =
713 muxer_factory->CreateMuxer(GetOutputFormat(stream), stream);
715 return Status(error::INVALID_ARGUMENT,
"Failed to create muxer for " +
717 stream.stream_selector);
720 std::unique_ptr<MuxerListener> muxer_listener =
721 muxer_listener_factory->CreateListener(ToMuxerListenerData(stream));
722 muxer->SetMuxerListener(std::move(muxer_listener));
725 std::shared_ptr<MediaHandler> trick_play =
726 stream.trick_play_factor
727 ? std::make_shared<TrickPlayHandler>(stream.trick_play_factor)
730 RETURN_IF_ERROR(MediaHandler::Chain({replicator, trick_play, muxer}));
736 Status CreateAllJobs(
const std::vector<StreamDescriptor>& stream_descriptors,
737 const PackagingParams& packaging_params,
738 MpdNotifier* mpd_notifier,
739 KeySource* encryption_key_source,
740 SyncPointQueue* sync_points,
741 MuxerListenerFactory* muxer_listener_factory,
742 MuxerFactory* muxer_factory,
743 JobManager* job_manager) {
744 DCHECK(muxer_factory);
745 DCHECK(muxer_listener_factory);
749 std::vector<std::reference_wrapper<const StreamDescriptor>> text_streams;
750 std::vector<std::reference_wrapper<const StreamDescriptor>>
753 bool has_transport_audio_video_streams =
false;
754 bool has_non_transport_audio_video_streams =
false;
756 for (
const StreamDescriptor& stream : stream_descriptors) {
760 if (stream.stream_selector ==
"text") {
761 text_streams.push_back(stream);
763 audio_video_streams.push_back(stream);
765 switch (GetOutputFormat(stream)) {
766 case CONTAINER_MPEG2TS:
770 has_transport_audio_video_streams =
true;
773 has_non_transport_audio_video_streams =
true;
781 std::sort(audio_video_streams.begin(), audio_video_streams.end(),
782 media::StreamDescriptorCompareFn);
784 if (!text_streams.empty()) {
785 PackagingParams text_packaging_params = packaging_params;
786 if (text_packaging_params.transport_stream_timestamp_offset_ms > 0) {
787 if (has_transport_audio_video_streams &&
788 has_non_transport_audio_video_streams) {
789 LOG(WARNING) <<
"There may be problems mixing transport streams and " 790 "non-transport streams. For example, the subtitles may " 791 "be out of sync with non-transport streams.";
792 }
else if (has_non_transport_audio_video_streams) {
795 text_packaging_params.transport_stream_timestamp_offset_ms = 0;
799 RETURN_IF_ERROR(CreateTextJobs(text_streams, text_packaging_params,
800 sync_points, muxer_listener_factory,
801 muxer_factory, mpd_notifier, job_manager));
804 RETURN_IF_ERROR(CreateAudioVideoJobs(
805 audio_video_streams, packaging_params, encryption_key_source, sync_points,
806 muxer_listener_factory, muxer_factory, job_manager));
809 return job_manager->InitializeJobs();
815 struct Packager::PackagerInternal {
816 media::FakeClock fake_clock;
817 std::unique_ptr<KeySource> encryption_key_source;
818 std::unique_ptr<MpdNotifier> mpd_notifier;
819 std::unique_ptr<hls::HlsNotifier> hls_notifier;
820 BufferCallbackParams buffer_callback_params;
821 std::unique_ptr<media::JobManager> job_manager;
824 Packager::Packager() {}
826 Packager::~Packager() {}
830 const std::vector<StreamDescriptor>& stream_descriptors) {
832 static base::AtExitManager exit;
836 return Status(error::INVALID_ARGUMENT,
"Already initialized.");
838 RETURN_IF_ERROR(media::ValidateParams(packaging_params, stream_descriptors));
841 SetPackagerVersionForTesting(
845 std::unique_ptr<PackagerInternal>
internal(
new PackagerInternal);
849 internal->encryption_key_source = CreateEncryptionKeySource(
850 static_cast<media::FourCC>(
853 if (!internal->encryption_key_source)
854 return Status(error::INVALID_ARGUMENT,
"Failed to create key source.");
863 if (internal->buffer_callback_params.write_func) {
865 internal->buffer_callback_params, mpd_params.
mpd_output);
878 const bool on_demand_dash_profile =
879 stream_descriptors.begin()->segment_template.empty();
880 const double target_segment_duration =
882 const MpdOptions mpd_options = media::GetMpdOptions(
883 on_demand_dash_profile, mpd_params, target_segment_duration);
885 if (!internal->mpd_notifier->Init()) {
886 LOG(ERROR) <<
"MpdNotifier failed to initialize.";
887 return Status(error::INVALID_ARGUMENT,
888 "Failed to initialize MpdNotifier.");
896 std::unique_ptr<SyncPointQueue> sync_points;
901 internal->job_manager.reset(
new JobManager(std::move(sync_points)));
903 std::vector<StreamDescriptor> streams_for_jobs;
909 if (internal->buffer_callback_params.read_func) {
914 if (internal->buffer_callback_params.write_func) {
918 internal->buffer_callback_params, descriptor.segment_template);
926 error::INVALID_ARGUMENT,
927 "Unknown/invalid language specified: " + descriptor.language);
931 streams_for_jobs.push_back(copy);
941 internal->hls_notifier.get());
943 RETURN_IF_ERROR(media::CreateAllJobs(
944 streams_for_jobs, packaging_params, internal->mpd_notifier.get(),
945 internal->encryption_key_source.get(),
946 internal->job_manager->sync_points(), &muxer_listener_factory,
947 &muxer_factory,
internal->job_manager.get()));
949 internal_ = std::move(
internal);
955 return Status(error::INVALID_ARGUMENT,
"Not yet initialized.");
957 RETURN_IF_ERROR(internal_->job_manager->RunJobs());
959 if (internal_->hls_notifier) {
960 if (!internal_->hls_notifier->Flush())
961 return Status(error::INVALID_ARGUMENT,
"Failed to flush Hls.");
963 if (internal_->mpd_notifier) {
964 if (!internal_->mpd_notifier->Flush())
965 return Status(error::INVALID_ARGUMENT,
"Failed to flush Mpd.");
972 LOG(INFO) <<
"Not yet initialized. Return directly.";
975 internal_->job_manager->CancelJobs();
979 return GetPackagerVersion();
987 if (stream_attributes.stream_type ==
988 EncryptionParams::EncryptedStreamAttributes::kAudio)
990 if (stream_attributes.stream_type ==
991 EncryptionParams::EncryptedStreamAttributes::kVideo) {
992 const int pixels = stream_attributes.oneof.video.width *
993 stream_attributes.oneof.video.height;
994 if (pixels <= max_sd_pixels)
996 if (pixels <= max_hd_pixels)
998 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::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()
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)
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.