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/single_thread_job_manager.h"
16 #include "packager/app/stream_descriptor.h"
17 #include "packager/base/at_exit.h"
18 #include "packager/base/files/file_path.h"
19 #include "packager/base/logging.h"
20 #include "packager/base/optional.h"
21 #include "packager/base/path_service.h"
22 #include "packager/base/strings/string_util.h"
23 #include "packager/base/strings/stringprintf.h"
24 #include "packager/base/threading/simple_thread.h"
25 #include "packager/base/time/clock.h"
26 #include "packager/file/file.h"
27 #include "packager/hls/base/hls_notifier.h"
28 #include "packager/hls/base/simple_hls_notifier.h"
29 #include "packager/media/base/cc_stream_filter.h"
30 #include "packager/media/base/container_names.h"
31 #include "packager/media/base/fourccs.h"
32 #include "packager/media/base/key_source.h"
33 #include "packager/media/base/language_utils.h"
34 #include "packager/media/base/muxer.h"
35 #include "packager/media/base/muxer_options.h"
36 #include "packager/media/base/muxer_util.h"
37 #include "packager/media/chunking/chunking_handler.h"
38 #include "packager/media/chunking/cue_alignment_handler.h"
39 #include "packager/media/chunking/text_chunker.h"
40 #include "packager/media/crypto/encryption_handler.h"
41 #include "packager/media/demuxer/demuxer.h"
42 #include "packager/media/event/muxer_listener_factory.h"
43 #include "packager/media/event/vod_media_info_dump_muxer_listener.h"
44 #include "packager/media/formats/ttml/ttml_to_mp4_handler.h"
45 #include "packager/media/formats/webvtt/text_padder.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::SingleThreadJobManager;
63 using media::SyncPointQueue;
68 const char kMediaInfoSuffix[] =
".media_info";
70 const int64_t kDefaultTextZeroBiasMs = 10 * 60 * 1000;
72 MuxerListenerFactory::StreamData ToMuxerListenerData(
73 const StreamDescriptor& stream) {
74 MuxerListenerFactory::StreamData data;
75 data.media_info_output = stream.output;
77 data.hls_group_id = stream.hls_group_id;
78 data.hls_name = stream.hls_name;
79 data.hls_playlist_name = stream.hls_playlist_name;
80 data.hls_iframe_playlist_name = stream.hls_iframe_playlist_name;
81 data.hls_characteristics = stream.hls_characteristics;
82 data.hls_only = stream.hls_only;
84 data.dash_accessiblities = stream.dash_accessiblities;
85 data.dash_roles = stream.dash_roles;
86 data.dash_only = stream.dash_only;
93 bool DetermineTextFileCodec(
const std::string& file, std::string* out) {
98 LOG(ERROR) <<
"Failed to open file " << file
99 <<
" to determine file format.";
103 const uint8_t* content_data =
104 reinterpret_cast<const uint8_t*
>(content.data());
105 MediaContainerName container_name =
106 DetermineContainer(content_data, content.size());
108 if (container_name == CONTAINER_WEBVTT) {
113 if (container_name == CONTAINER_TTML) {
121 MediaContainerName GetOutputFormat(
const StreamDescriptor& descriptor) {
122 if (!descriptor.output_format.empty()) {
123 MediaContainerName format =
124 DetermineContainerFromFormatName(descriptor.output_format);
125 if (format == CONTAINER_UNKNOWN) {
126 LOG(ERROR) <<
"Unable to determine output format from '"
127 << descriptor.output_format <<
"'.";
132 base::Optional<MediaContainerName> format_from_output;
133 base::Optional<MediaContainerName> format_from_segment;
134 if (!descriptor.output.empty()) {
135 format_from_output = DetermineContainerFromFileName(descriptor.output);
136 if (format_from_output.value() == CONTAINER_UNKNOWN) {
137 LOG(ERROR) <<
"Unable to determine output format from '"
138 << descriptor.output <<
"'.";
141 if (!descriptor.segment_template.empty()) {
142 format_from_segment =
143 DetermineContainerFromFileName(descriptor.segment_template);
144 if (format_from_segment.value() == CONTAINER_UNKNOWN) {
145 LOG(ERROR) <<
"Unable to determine output format from '"
146 << descriptor.segment_template <<
"'.";
150 if (format_from_output && format_from_segment) {
151 if (format_from_output.value() != format_from_segment.value()) {
152 LOG(ERROR) <<
"Output format determined from '" << descriptor.output
153 <<
"' differs from output format determined from '"
154 << descriptor.segment_template <<
"'.";
155 return CONTAINER_UNKNOWN;
159 if (format_from_output)
160 return format_from_output.value();
161 if (format_from_segment)
162 return format_from_segment.value();
163 return CONTAINER_UNKNOWN;
166 MediaContainerName GetTextOutputCodec(
const StreamDescriptor& descriptor) {
167 const auto output_container = GetOutputFormat(descriptor);
168 if (output_container != CONTAINER_MOV)
169 return output_container;
171 const auto input_container = DetermineContainerFromFileName(descriptor.input);
172 if (base::EqualsCaseInsensitiveASCII(descriptor.output_format,
"vtt+mp4") ||
173 base::EqualsCaseInsensitiveASCII(descriptor.output_format,
175 return CONTAINER_WEBVTT;
176 }
else if (!base::EqualsCaseInsensitiveASCII(descriptor.output_format,
178 input_container == CONTAINER_WEBVTT) {
180 return CONTAINER_WEBVTT;
183 return CONTAINER_TTML;
187 bool IsTextStream(
const StreamDescriptor& stream) {
188 if (stream.stream_selector ==
"text")
190 if (base::EqualsCaseInsensitiveASCII(stream.output_format,
"vtt+mp4") ||
191 base::EqualsCaseInsensitiveASCII(stream.output_format,
"webvtt+mp4") ||
192 base::EqualsCaseInsensitiveASCII(stream.output_format,
"ttml+mp4")) {
196 auto output_format = GetOutputFormat(stream);
197 return output_format == CONTAINER_WEBVTT || output_format == CONTAINER_TTML;
200 Status ValidateStreamDescriptor(
bool dump_stream_info,
201 const StreamDescriptor& stream) {
202 if (stream.input.empty()) {
203 return Status(error::INVALID_ARGUMENT,
"Stream input not specified.");
208 if (dump_stream_info && stream.output.empty() &&
209 stream.segment_template.empty()) {
213 if (stream.output.empty() && stream.segment_template.empty()) {
214 return Status(error::INVALID_ARGUMENT,
215 "Streams must specify 'output' or 'segment template'.");
219 if (stream.stream_selector.empty()) {
220 return Status(error::INVALID_ARGUMENT,
221 "Stream stream_selector not specified.");
225 if (stream.segment_template.length()) {
226 RETURN_IF_ERROR(ValidateSegmentTemplate(stream.segment_template));
231 const MediaContainerName output_format = GetOutputFormat(stream);
233 if (output_format == CONTAINER_UNKNOWN) {
234 return Status(error::INVALID_ARGUMENT,
"Unsupported output format.");
236 if (output_format == MediaContainerName::CONTAINER_MPEG2TS) {
237 if (stream.segment_template.empty()) {
239 error::INVALID_ARGUMENT,
240 "Please specify 'segment_template'. Single file TS output is "
247 if (stream.output.length()) {
248 return Status(error::INVALID_ARGUMENT,
249 "All TS segments must be self-initializing. Stream "
250 "descriptors 'output' or 'init_segment' are not allowed.");
252 }
else if (output_format == CONTAINER_WEBVTT ||
253 output_format == CONTAINER_TTML ||
254 output_format == CONTAINER_AAC || output_format == CONTAINER_MP3 ||
255 output_format == CONTAINER_AC3 ||
256 output_format == CONTAINER_EAC3) {
259 if (stream.segment_template.length() && stream.output.length()) {
261 error::INVALID_ARGUMENT,
262 "Segmented subtitles or PackedAudio output cannot have an init "
263 "segment. Do not specify stream descriptors 'output' or "
264 "'init_segment' when using 'segment_template'.");
269 if (stream.segment_template.length() && stream.output.empty()) {
270 return Status(error::INVALID_ARGUMENT,
271 "Please specify 'init_segment'. All non-TS multi-segment "
272 "content must provide an init segment.");
276 if (stream.output.find(
'$') != std::string::npos) {
277 if (output_format == CONTAINER_WEBVTT) {
279 error::UNIMPLEMENTED,
280 "WebVTT output with one file per Representation per Period "
281 "is not supported yet. Please use fMP4 instead. If that needs to be "
282 "supported, please file a feature request on GitHub.");
287 RETURN_IF_ERROR(ValidateSegmentTemplate(stream.output));
293 Status ValidateParams(
const PackagingParams& packaging_params,
294 const std::vector<StreamDescriptor>& stream_descriptors) {
295 if (!packaging_params.chunking_params.segment_sap_aligned &&
296 packaging_params.chunking_params.subsegment_sap_aligned) {
297 return Status(error::INVALID_ARGUMENT,
298 "Setting segment_sap_aligned to false but "
299 "subsegment_sap_aligned to true is not allowed.");
302 if (stream_descriptors.empty()) {
303 return Status(error::INVALID_ARGUMENT,
304 "Stream descriptors cannot be empty.");
309 const bool on_demand_dash_profile =
310 stream_descriptors.begin()->segment_template.empty();
311 std::set<std::string> outputs;
312 std::set<std::string> segment_templates;
313 for (
const auto& descriptor : stream_descriptors) {
314 if (on_demand_dash_profile != descriptor.segment_template.empty()) {
315 return Status(error::INVALID_ARGUMENT,
316 "Inconsistent stream descriptor specification: "
317 "segment_template should be specified for none or all "
318 "stream descriptors.");
321 RETURN_IF_ERROR(ValidateStreamDescriptor(
322 packaging_params.test_params.dump_stream_info, descriptor));
324 if (base::StartsWith(descriptor.input,
"udp://",
325 base::CompareCase::SENSITIVE)) {
326 const HlsParams& hls_params = packaging_params.hls_params;
327 if (!hls_params.master_playlist_output.empty() &&
328 hls_params.playlist_type == HlsPlaylistType::kVod) {
330 <<
"Seeing UDP input with HLS Playlist Type set to VOD. The "
331 "playlists will only be generated when UDP socket is closed. "
332 "If you want to do live packaging, --hls_playlist_type needs to "
339 if (!descriptor.output.empty()) {
340 if (outputs.find(descriptor.output) != outputs.end()) {
342 error::INVALID_ARGUMENT,
343 "Seeing duplicated outputs '" + descriptor.output +
344 "' in stream descriptors. Every output must be unique.");
346 outputs.insert(descriptor.output);
348 if (!descriptor.segment_template.empty()) {
349 if (segment_templates.find(descriptor.segment_template) !=
350 segment_templates.end()) {
351 return Status(error::INVALID_ARGUMENT,
352 "Seeing duplicated segment templates '" +
353 descriptor.segment_template +
354 "' in stream descriptors. Every segment template "
357 segment_templates.insert(descriptor.segment_template);
361 if (packaging_params.output_media_info && !on_demand_dash_profile) {
363 return Status(error::UNIMPLEMENTED,
364 "--output_media_info is only supported for on-demand profile "
365 "(not using segment_template).");
368 if (on_demand_dash_profile &&
369 !packaging_params.mpd_params.mpd_output.empty() &&
370 !packaging_params.mp4_output_params.generate_sidx_in_media_segments) {
371 return Status(error::UNIMPLEMENTED,
372 "--generate_sidx_in_media_segments is required for DASH "
373 "on-demand profile (not using segment_template).");
379 bool StreamDescriptorCompareFn(
const StreamDescriptor& a,
380 const StreamDescriptor& b) {
386 if (a.input == b.input) {
387 if (a.stream_selector == b.stream_selector) {
390 return a.trick_play_factor < b.trick_play_factor;
392 return a.stream_selector < b.stream_selector;
395 return a.input < b.input;
400 class FakeClock :
public base::Clock {
402 base::Time Now()
override {
return base::Time(); }
405 bool StreamInfoToTextMediaInfo(
const StreamDescriptor& stream_descriptor,
406 MediaInfo* text_media_info) {
408 if (!DetermineTextFileCodec(stream_descriptor.input, &codec)) {
409 LOG(ERROR) <<
"Failed to determine the text file format for "
410 << stream_descriptor.input;
414 MediaInfo::TextInfo* text_info = text_media_info->mutable_text_info();
415 text_info->set_codec(codec);
417 const std::string& language = stream_descriptor.language;
418 if (!language.empty()) {
419 text_info->set_language(language);
422 text_media_info->set_media_file_name(stream_descriptor.output);
423 text_media_info->set_container_type(MediaInfo::CONTAINER_TEXT);
425 if (stream_descriptor.bandwidth != 0) {
426 text_media_info->set_bandwidth(stream_descriptor.bandwidth);
431 const int kDefaultTextBandwidth = 256;
432 text_media_info->set_bandwidth(kDefaultTextBandwidth);
435 if (!stream_descriptor.dash_roles.empty()) {
436 for (
const auto& dash_role : stream_descriptor.dash_roles) {
437 text_media_info->add_dash_roles(dash_role);
447 Status CreateDemuxer(
const StreamDescriptor& stream,
448 const PackagingParams& packaging_params,
449 std::shared_ptr<Demuxer>* new_demuxer) {
450 std::shared_ptr<Demuxer> demuxer = std::make_shared<Demuxer>(stream.input);
451 demuxer->set_dump_stream_info(packaging_params.test_params.dump_stream_info);
453 if (packaging_params.decryption_params.key_provider != KeyProvider::kNone) {
454 std::unique_ptr<KeySource> decryption_key_source(
455 CreateDecryptionKeySource(packaging_params.decryption_params));
456 if (!decryption_key_source) {
458 error::INVALID_ARGUMENT,
459 "Must define decryption key source when defining key provider");
461 demuxer->SetKeySource(std::move(decryption_key_source));
464 *new_demuxer = std::move(demuxer);
468 std::shared_ptr<MediaHandler> CreateEncryptionHandler(
469 const PackagingParams& packaging_params,
470 const StreamDescriptor& stream,
471 KeySource* key_source) {
472 if (stream.skip_encryption) {
481 EncryptionParams encryption_params = packaging_params.encryption_params;
486 if (GetOutputFormat(stream) == CONTAINER_MPEG2TS ||
487 GetOutputFormat(stream) == CONTAINER_AAC ||
488 GetOutputFormat(stream) == CONTAINER_AC3 ||
489 GetOutputFormat(stream) == CONTAINER_EAC3) {
490 VLOG(1) <<
"Use Apple Sample AES encryption for MPEG2TS or Packed Audio.";
491 encryption_params.protection_scheme = kAppleSampleAesProtectionScheme;
494 if (!stream.drm_label.empty()) {
495 const std::string& drm_label = stream.drm_label;
496 encryption_params.stream_label_func =
497 [drm_label](
const EncryptionParams::EncryptedStreamAttributes&) {
500 }
else if (!encryption_params.stream_label_func) {
501 const int kDefaultMaxSdPixels = 768 * 576;
502 const int kDefaultMaxHdPixels = 1920 * 1080;
503 const int kDefaultMaxUhd1Pixels = 4096 * 2160;
504 encryption_params.stream_label_func = std::bind(
506 kDefaultMaxHdPixels, kDefaultMaxUhd1Pixels, std::placeholders::_1);
509 return std::make_shared<EncryptionHandler>(encryption_params, key_source);
512 std::unique_ptr<MediaHandler> CreateTextChunker(
513 const ChunkingParams& chunking_params) {
514 const float segment_length_in_seconds =
515 chunking_params.segment_duration_in_seconds;
516 return std::unique_ptr<MediaHandler>(
517 new TextChunker(segment_length_in_seconds));
520 Status CreateTtmlJobs(
521 const std::vector<std::reference_wrapper<const StreamDescriptor>>& streams,
522 const PackagingParams& packaging_params,
523 SyncPointQueue* sync_points,
524 MuxerFactory* muxer_factory,
525 MpdNotifier* mpd_notifier,
526 JobManager* job_manager) {
528 for (
const StreamDescriptor& stream : streams) {
530 if (!packaging_params.hls_params.master_playlist_output.empty() &&
532 return Status(error::INVALID_ARGUMENT,
533 "HLS does not support TTML in xml format.");
536 if (!stream.segment_template.empty()) {
537 return Status(error::INVALID_ARGUMENT,
538 "Segmented TTML is not supported.");
541 if (GetOutputFormat(stream) != CONTAINER_TTML) {
542 return Status(error::INVALID_ARGUMENT,
543 "Converting TTML to other formats is not supported");
546 if (!stream.output.empty()) {
547 if (!
File::Copy(stream.input.c_str(), stream.output.c_str())) {
550 &error,
"Failed to copy the input file (%s) to output file (%s).",
551 stream.input.c_str(), stream.output.c_str());
552 return Status(error::FILE_FAILURE, error);
555 MediaInfo text_media_info;
556 if (!StreamInfoToTextMediaInfo(stream, &text_media_info)) {
557 return Status(error::INVALID_ARGUMENT,
558 "Could not create media info for stream.");
565 if (mpd_notifier->NotifyNewContainer(text_media_info, &unused)) {
566 mpd_notifier->Flush();
568 return Status(error::PARSER_FAILURE,
569 "Failed to process text file " + stream.input);
573 if (packaging_params.output_media_info) {
575 text_media_info, stream.output + kMediaInfoSuffix);
583 Status CreateAudioVideoJobs(
584 const std::vector<std::reference_wrapper<const StreamDescriptor>>& streams,
585 const PackagingParams& packaging_params,
586 KeySource* encryption_key_source,
587 SyncPointQueue* sync_points,
588 MuxerListenerFactory* muxer_listener_factory,
589 MuxerFactory* muxer_factory,
590 JobManager* job_manager) {
591 DCHECK(muxer_listener_factory);
592 DCHECK(muxer_factory);
597 std::map<std::string, std::shared_ptr<Demuxer>> sources;
598 std::map<std::string, std::shared_ptr<MediaHandler>> cue_aligners;
600 for (
const StreamDescriptor& stream : streams) {
601 bool seen_input_before = sources.find(stream.input) != sources.end();
602 if (seen_input_before) {
607 CreateDemuxer(stream, packaging_params, &sources[stream.input]));
608 cue_aligners[stream.input] =
609 sync_points ? std::make_shared<CueAlignmentHandler>(sync_points)
613 for (
auto& source : sources) {
614 job_manager->Add(
"RemuxJob", source.second);
619 std::shared_ptr<MediaHandler> replicator;
621 std::string previous_input;
622 std::string previous_selector;
624 for (
const StreamDescriptor& stream : streams) {
626 auto& demuxer = sources[stream.input];
627 auto& cue_aligner = cue_aligners[stream.input];
629 const bool new_input_file = stream.input != previous_input;
630 const bool new_stream =
631 new_input_file || previous_selector != stream.stream_selector;
632 const bool is_text = IsTextStream(stream);
633 previous_input = stream.input;
634 previous_selector = stream.stream_selector;
638 if (stream.output.empty() && stream.segment_template.empty()) {
646 if (!stream.language.empty()) {
647 demuxer->SetLanguageOverride(stream.stream_selector, stream.language);
650 std::vector<std::shared_ptr<MediaHandler>> handlers;
652 handlers.emplace_back(
653 std::make_shared<TextPadder>(kDefaultTextZeroBiasMs));
656 handlers.emplace_back(cue_aligner);
659 handlers.emplace_back(std::make_shared<ChunkingHandler>(
660 packaging_params.chunking_params));
661 handlers.emplace_back(CreateEncryptionHandler(packaging_params, stream,
662 encryption_key_source));
665 replicator = std::make_shared<Replicator>();
666 handlers.emplace_back(replicator);
668 RETURN_IF_ERROR(MediaHandler::Chain(handlers));
669 RETURN_IF_ERROR(demuxer->SetHandler(stream.stream_selector, handlers[0]));
673 const auto output_format = GetOutputFormat(stream);
674 std::shared_ptr<Muxer> muxer =
675 muxer_factory->CreateMuxer(output_format, stream);
677 return Status(error::INVALID_ARGUMENT,
"Failed to create muxer for " +
679 stream.stream_selector);
682 std::unique_ptr<MuxerListener> muxer_listener =
683 muxer_listener_factory->CreateListener(ToMuxerListenerData(stream));
684 muxer->SetMuxerListener(std::move(muxer_listener));
686 std::vector<std::shared_ptr<MediaHandler>> handlers;
687 handlers.emplace_back(replicator);
690 if (stream.trick_play_factor) {
691 handlers.emplace_back(
692 std::make_shared<TrickPlayHandler>(stream.trick_play_factor));
695 if (stream.cc_index >= 0) {
696 handlers.emplace_back(
697 std::make_shared<CcStreamFilter>(stream.language, stream.cc_index));
701 (!stream.segment_template.empty() || output_format == CONTAINER_MOV)) {
702 handlers.emplace_back(
703 CreateTextChunker(packaging_params.chunking_params));
706 if (is_text && output_format == CONTAINER_MOV) {
707 const auto output_codec = GetTextOutputCodec(stream);
708 if (output_codec == CONTAINER_WEBVTT) {
709 handlers.emplace_back(std::make_shared<WebVttToMp4Handler>());
710 }
else if (output_codec == CONTAINER_TTML) {
711 handlers.emplace_back(std::make_shared<ttml::TtmlToMp4Handler>());
715 handlers.emplace_back(muxer);
716 RETURN_IF_ERROR(MediaHandler::Chain(handlers));
722 Status CreateAllJobs(
const std::vector<StreamDescriptor>& stream_descriptors,
723 const PackagingParams& packaging_params,
724 MpdNotifier* mpd_notifier,
725 KeySource* encryption_key_source,
726 SyncPointQueue* sync_points,
727 MuxerListenerFactory* muxer_listener_factory,
728 MuxerFactory* muxer_factory,
729 JobManager* job_manager) {
730 DCHECK(muxer_factory);
731 DCHECK(muxer_listener_factory);
735 std::vector<std::reference_wrapper<const StreamDescriptor>> ttml_streams;
736 std::vector<std::reference_wrapper<const StreamDescriptor>>
739 bool has_transport_audio_video_streams =
false;
740 bool has_non_transport_audio_video_streams =
false;
742 for (
const StreamDescriptor& stream : stream_descriptors) {
743 const auto input_container = DetermineContainerFromFileName(stream.input);
744 const auto output_format = GetOutputFormat(stream);
745 if (input_container == CONTAINER_TTML) {
746 ttml_streams.push_back(stream);
748 audio_video_streams.push_back(stream);
749 switch (output_format) {
750 case CONTAINER_MPEG2TS:
755 has_transport_audio_video_streams =
true;
758 case CONTAINER_WEBVTT:
761 has_non_transport_audio_video_streams =
true;
769 std::sort(audio_video_streams.begin(), audio_video_streams.end(),
770 media::StreamDescriptorCompareFn);
772 if (packaging_params.transport_stream_timestamp_offset_ms > 0) {
773 if (has_transport_audio_video_streams &&
774 has_non_transport_audio_video_streams) {
775 LOG(WARNING) <<
"There may be problems mixing transport streams and "
776 "non-transport streams. For example, the subtitles may "
777 "be out of sync with non-transport streams.";
778 }
else if (has_non_transport_audio_video_streams) {
781 muxer_factory->SetTsStreamOffset(0);
785 RETURN_IF_ERROR(CreateTtmlJobs(ttml_streams, packaging_params, sync_points,
786 muxer_factory, mpd_notifier, job_manager));
787 RETURN_IF_ERROR(CreateAudioVideoJobs(
788 audio_video_streams, packaging_params, encryption_key_source, sync_points,
789 muxer_listener_factory, muxer_factory, job_manager));
792 return job_manager->InitializeJobs();
798 struct Packager::PackagerInternal {
799 media::FakeClock fake_clock;
800 std::unique_ptr<KeySource> encryption_key_source;
801 std::unique_ptr<MpdNotifier> mpd_notifier;
802 std::unique_ptr<hls::HlsNotifier> hls_notifier;
803 BufferCallbackParams buffer_callback_params;
804 std::unique_ptr<media::JobManager> job_manager;
807 Packager::Packager() {}
809 Packager::~Packager() {}
813 const std::vector<StreamDescriptor>& stream_descriptors) {
815 static base::AtExitManager exit;
819 return Status(error::INVALID_ARGUMENT,
"Already initialized.");
821 RETURN_IF_ERROR(media::ValidateParams(packaging_params, stream_descriptors));
824 SetPackagerVersionForTesting(
828 std::unique_ptr<PackagerInternal>
internal(
new PackagerInternal);
832 internal->encryption_key_source = CreateEncryptionKeySource(
833 static_cast<media::FourCC
>(
836 if (!internal->encryption_key_source)
837 return Status(error::INVALID_ARGUMENT,
"Failed to create key source.");
846 const double target_segment_duration =
853 if (internal->buffer_callback_params.write_func) {
855 internal->buffer_callback_params, mpd_params.
mpd_output);
871 hls_params.is_independent_segments =
875 const bool on_demand_dash_profile =
876 stream_descriptors.begin()->segment_template.empty();
878 media::GetMpdOptions(on_demand_dash_profile, mpd_params);
880 if (!internal->mpd_notifier->Init()) {
881 LOG(ERROR) <<
"MpdNotifier failed to initialize.";
882 return Status(error::INVALID_ARGUMENT,
883 "Failed to initialize MpdNotifier.");
891 std::unique_ptr<SyncPointQueue> sync_points;
897 internal->job_manager.reset(
900 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)
static std::string MakeCallbackFileName(const BufferCallbackParams &callback_params, const std::string &name)
static bool ReadFileToString(const char *file_name, std::string *contents)
static bool Copy(const char *from_file_name, const char *to_file_name)
void Cancel()
Cancel packaging. Note that it has to be called from another thread.
static std::string DefaultStreamLabelFunction(int max_sd_pixels, int max_hd_pixels, int max_uhd1_pixels, const EncryptionParams::EncryptedStreamAttributes &stream_attributes)
static std::string GetLibraryVersion()
Status Initialize(const PackagingParams &packaging_params, const std::vector< StreamDescriptor > &stream_descriptors)
All the methods that are virtual are virtual for mocking.
std::string LanguageToISO_639_2(const std::string &language)
std::string LanguageToShortestForm(const std::string &language)
std::vector< Cuepoint > cue_points
List of cuepoints.
double segment_duration_in_seconds
Segment duration in seconds.
Encrypted stream information that is used to determine stream label.
std::string default_text_language
double target_segment_duration
std::string default_language
std::string master_playlist_output
HLS master playlist output path.
DASH MPD related parameters.
std::string default_language
double target_segment_duration
std::string mpd_output
MPD output file path.
std::string default_text_language
EncryptionParams encryption_params
Encryption and Decryption Parameters.
HlsParams hls_params
HLS related parameters.
AdCueGeneratorParams ad_cue_generator_params
Out of band cuepoint parameters.
BufferCallbackParams buffer_callback_params
Buffer callback params.
ChunkingParams chunking_params
Chunking (segmentation) related parameters.
MpdParams mpd_params
DASH MPD related parameters.
Defines a single input/output stream.
std::string input
Input/source media file path or network stream URL. Required.
std::string segment_template
Specifies segment template. Can be empty.
std::string injected_library_version