7 #include <packager/packager.h>
13 #include <absl/log/check.h>
14 #include <absl/log/log.h>
15 #include <absl/strings/match.h>
16 #include <absl/strings/str_format.h>
18 #include <packager/app/job_manager.h>
19 #include <packager/app/muxer_factory.h>
20 #include <packager/app/packager_util.h>
21 #include <packager/app/single_thread_job_manager.h>
22 #include <packager/file.h>
23 #include <packager/hls/base/hls_notifier.h>
24 #include <packager/hls/base/simple_hls_notifier.h>
25 #include <packager/macros/logging.h>
26 #include <packager/macros/status.h>
27 #include <packager/media/base/cc_stream_filter.h>
28 #include <packager/media/base/language_utils.h>
29 #include <packager/media/base/muxer.h>
30 #include <packager/media/base/muxer_util.h>
31 #include <packager/media/chunking/chunking_handler.h>
32 #include <packager/media/chunking/cue_alignment_handler.h>
33 #include <packager/media/chunking/text_chunker.h>
34 #include <packager/media/crypto/encryption_handler.h>
35 #include <packager/media/demuxer/demuxer.h>
36 #include <packager/media/event/muxer_listener_factory.h>
37 #include <packager/media/event/vod_media_info_dump_muxer_listener.h>
38 #include <packager/media/formats/ttml/ttml_to_mp4_handler.h>
39 #include <packager/media/formats/webvtt/text_padder.h>
40 #include <packager/media/formats/webvtt/webvtt_to_mp4_handler.h>
41 #include <packager/media/replicator/replicator.h>
42 #include <packager/media/trick_play/trick_play_handler.h>
43 #include <packager/mpd/base/media_info.pb.h>
44 #include <packager/mpd/base/simple_mpd_notifier.h>
45 #include <packager/version/version.h>
51 using media::JobManager;
52 using media::KeySource;
53 using media::MuxerOptions;
54 using media::SingleThreadJobManager;
55 using media::SyncPointQueue;
60 const char kMediaInfoSuffix[] =
".media_info";
62 MuxerListenerFactory::StreamData ToMuxerListenerData(
63 const StreamDescriptor& stream) {
64 MuxerListenerFactory::StreamData data;
65 data.media_info_output = stream.output;
67 data.hls_group_id = stream.hls_group_id;
68 data.hls_name = stream.hls_name;
69 data.hls_playlist_name = stream.hls_playlist_name;
70 data.hls_iframe_playlist_name = stream.hls_iframe_playlist_name;
71 data.hls_characteristics = stream.hls_characteristics;
72 data.forced_subtitle = stream.forced_subtitle;
73 data.hls_only = stream.hls_only;
75 data.dash_accessiblities = stream.dash_accessiblities;
76 data.dash_roles = stream.dash_roles;
77 data.dash_only = stream.dash_only;
78 data.index = stream.index;
79 data.dash_label = stream.dash_label;
80 data.input_format = stream.input_format;
87 bool DetermineTextFileCodec(
const std::string& file, std::string* out) {
91 if (!File::ReadFileToString(file.c_str(), &content)) {
92 LOG(ERROR) <<
"Failed to open file " << file
93 <<
" to determine file format.";
97 const uint8_t* content_data =
98 reinterpret_cast<const uint8_t*
>(content.data());
99 MediaContainerName container_name =
100 DetermineContainer(content_data, content.size());
102 if (container_name == CONTAINER_WEBVTT) {
107 if (container_name == CONTAINER_TTML) {
115 MediaContainerName GetOutputFormat(
const StreamDescriptor& descriptor) {
116 if (!descriptor.output_format.empty()) {
117 MediaContainerName format =
118 DetermineContainerFromFormatName(descriptor.output_format);
119 if (format == CONTAINER_UNKNOWN) {
120 LOG(ERROR) <<
"Unable to determine output format from '"
121 << descriptor.output_format <<
"'.";
126 std::optional<MediaContainerName> format_from_output;
127 std::optional<MediaContainerName> format_from_segment;
128 if (!descriptor.output.empty()) {
129 format_from_output = DetermineContainerFromFileName(descriptor.output);
130 if (format_from_output.value() == CONTAINER_UNKNOWN) {
131 LOG(ERROR) <<
"Unable to determine output format from '"
132 << descriptor.output <<
"'.";
135 if (!descriptor.segment_template.empty()) {
136 format_from_segment =
137 DetermineContainerFromFileName(descriptor.segment_template);
138 if (format_from_segment.value() == CONTAINER_UNKNOWN) {
139 LOG(ERROR) <<
"Unable to determine output format from '"
140 << descriptor.segment_template <<
"'.";
144 if (format_from_output && format_from_segment) {
145 if (format_from_output.value() != format_from_segment.value()) {
146 LOG(ERROR) <<
"Output format determined from '" << descriptor.output
147 <<
"' differs from output format determined from '"
148 << descriptor.segment_template <<
"'.";
149 return CONTAINER_UNKNOWN;
153 if (format_from_output)
154 return format_from_output.value();
155 if (format_from_segment)
156 return format_from_segment.value();
157 return CONTAINER_UNKNOWN;
160 MediaContainerName GetTextOutputCodec(
const StreamDescriptor& descriptor) {
161 const auto output_container = GetOutputFormat(descriptor);
162 if (output_container != CONTAINER_MOV)
163 return output_container;
165 const auto input_container = DetermineContainerFromFileName(descriptor.input);
166 if (absl::AsciiStrToLower(descriptor.output_format) ==
"vtt+mp4" ||
167 absl::AsciiStrToLower(descriptor.output_format) ==
"webvtt+mp4") {
168 return CONTAINER_WEBVTT;
169 }
else if (absl::AsciiStrToLower(descriptor.output_format) !=
"ttml+mp4" &&
170 input_container == CONTAINER_WEBVTT) {
172 return CONTAINER_WEBVTT;
175 return CONTAINER_TTML;
179 bool IsTextStream(
const StreamDescriptor& stream) {
180 if (stream.stream_selector ==
"text")
182 if (absl::AsciiStrToLower(stream.output_format) ==
"vtt+mp4" ||
183 absl::AsciiStrToLower(stream.output_format) ==
"webvtt+mp4" ||
184 absl::AsciiStrToLower(stream.output_format) ==
"ttml+mp4") {
188 auto output_format = GetOutputFormat(stream);
189 return output_format == CONTAINER_WEBVTT || output_format == CONTAINER_TTML;
192 Status ValidateStreamDescriptor(
bool dump_stream_info,
193 const StreamDescriptor& stream) {
194 if (stream.input.empty()) {
195 return Status(error::INVALID_ARGUMENT,
"Stream input not specified.");
200 if (dump_stream_info && stream.output.empty() &&
201 stream.segment_template.empty()) {
205 if (stream.output.empty() && stream.segment_template.empty()) {
206 return Status(error::INVALID_ARGUMENT,
207 "Streams must specify 'output' or 'segment template'.");
211 if (stream.stream_selector.empty()) {
212 return Status(error::INVALID_ARGUMENT,
213 "Stream stream_selector not specified.");
217 if (stream.segment_template.length()) {
218 RETURN_IF_ERROR(ValidateSegmentTemplate(stream.segment_template));
223 const MediaContainerName output_format = GetOutputFormat(stream);
225 if (output_format == CONTAINER_UNKNOWN) {
226 return Status(error::INVALID_ARGUMENT,
"Unsupported output format.");
229 if (output_format == CONTAINER_WEBVTT || output_format == CONTAINER_TTML ||
230 output_format == CONTAINER_AAC || output_format == CONTAINER_MP3 ||
231 output_format == CONTAINER_AC3 || output_format == CONTAINER_EAC3 ||
232 output_format == CONTAINER_MPEG2TS) {
235 if (stream.segment_template.length() && stream.output.length()) {
237 error::INVALID_ARGUMENT,
238 "Segmented subtitles, PackedAudio or TS output cannot have an init "
239 "segment. Do not specify stream descriptors 'output' or "
240 "'init_segment' when 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 (packaging_params.chunking_params.start_segment_number < 0) {
279 return Status(error::INVALID_ARGUMENT,
280 "Negative --start_segment_number is not allowed.");
283 if (stream_descriptors.empty()) {
284 return Status(error::INVALID_ARGUMENT,
285 "Stream descriptors cannot be empty.");
290 const bool on_demand_dash_profile =
291 stream_descriptors.begin()->segment_template.empty();
292 std::set<std::string> outputs;
293 std::set<std::string> segment_templates;
294 for (
const auto& descriptor : stream_descriptors) {
295 if (on_demand_dash_profile != descriptor.segment_template.empty()) {
296 return Status(error::INVALID_ARGUMENT,
297 "Inconsistent stream descriptor specification: "
298 "segment_template should be specified for none or all "
299 "stream descriptors.");
302 RETURN_IF_ERROR(ValidateStreamDescriptor(
303 packaging_params.test_params.dump_stream_info, descriptor));
305 if (absl::StartsWith(descriptor.input,
"udp://")) {
306 const HlsParams& hls_params = packaging_params.hls_params;
307 if (!hls_params.master_playlist_output.empty() &&
308 hls_params.playlist_type == HlsPlaylistType::kVod) {
310 <<
"Seeing UDP input with HLS Playlist Type set to VOD. The "
311 "playlists will only be generated when UDP socket is closed. "
312 "If you want to do live packaging, --hls_playlist_type needs to "
319 if (!descriptor.output.empty()) {
320 if (outputs.find(descriptor.output) != outputs.end()) {
322 error::INVALID_ARGUMENT,
323 "Seeing duplicated outputs '" + descriptor.output +
324 "' in stream descriptors. Every output must be unique.");
326 outputs.insert(descriptor.output);
328 if (!descriptor.segment_template.empty()) {
329 if (segment_templates.find(descriptor.segment_template) !=
330 segment_templates.end()) {
331 return Status(error::INVALID_ARGUMENT,
332 "Seeing duplicated segment templates '" +
333 descriptor.segment_template +
334 "' in stream descriptors. Every segment template "
337 segment_templates.insert(descriptor.segment_template);
341 if (packaging_params.output_media_info && !on_demand_dash_profile) {
343 return Status(error::UNIMPLEMENTED,
344 "--output_media_info is only supported for on-demand profile "
345 "(not using segment_template).");
348 if (on_demand_dash_profile &&
349 !packaging_params.mpd_params.mpd_output.empty() &&
350 !packaging_params.mp4_output_params.generate_sidx_in_media_segments &&
351 !packaging_params.mpd_params.use_segment_list) {
352 return Status(error::UNIMPLEMENTED,
353 "--generate_sidx_in_media_segments is required for DASH "
354 "on-demand profile (not using segment_template or segment list).");
357 if (packaging_params.chunking_params.low_latency_dash_mode &&
358 packaging_params.chunking_params.subsegment_duration_in_seconds) {
365 return Status(error::INVALID_ARGUMENT,
366 "--fragment_duration cannot be set "
367 "if --low_latency_dash_mode is enabled.");
370 if (packaging_params.mpd_params.low_latency_dash_mode &&
371 packaging_params.mpd_params.utc_timings.empty()) {
373 return Status(error::INVALID_ARGUMENT,
374 "--utc_timings must be be set "
375 "if --low_latency_dash_mode is enabled.");
381 bool StreamDescriptorCompareFn(
const StreamDescriptor& a,
382 const StreamDescriptor& b) {
388 if (a.input == b.input) {
389 if (a.stream_selector == b.stream_selector) {
392 return a.trick_play_factor < b.trick_play_factor;
394 return a.stream_selector < b.stream_selector;
397 return a.input < b.input;
402 class FakeClock :
public Clock {
404 time_point now() noexcept
override {
405 return std::chrono::system_clock::time_point(std::chrono::seconds(0));
409 bool StreamInfoToTextMediaInfo(
const StreamDescriptor& stream_descriptor,
410 MediaInfo* text_media_info) {
412 if (!DetermineTextFileCodec(stream_descriptor.input, &codec)) {
413 LOG(ERROR) <<
"Failed to determine the text file format for "
414 << stream_descriptor.input;
418 MediaInfo::TextInfo* text_info = text_media_info->mutable_text_info();
419 text_info->set_codec(codec);
421 const std::string& language = stream_descriptor.language;
422 if (!language.empty()) {
423 text_info->set_language(language);
426 if (stream_descriptor.index.has_value()) {
427 text_media_info->set_index(stream_descriptor.index.value());
430 text_media_info->set_media_file_name(stream_descriptor.output);
431 text_media_info->set_container_type(MediaInfo::CONTAINER_TEXT);
433 if (stream_descriptor.bandwidth != 0) {
434 text_media_info->set_bandwidth(stream_descriptor.bandwidth);
439 const int kDefaultTextBandwidth = 256;
440 text_media_info->set_bandwidth(kDefaultTextBandwidth);
443 if (!stream_descriptor.dash_roles.empty()) {
444 for (
const auto& dash_role : stream_descriptor.dash_roles) {
445 text_media_info->add_dash_roles(dash_role);
455 Status CreateDemuxer(
const StreamDescriptor& stream,
456 const PackagingParams& packaging_params,
457 std::shared_ptr<Demuxer>* new_demuxer) {
458 std::shared_ptr<Demuxer> demuxer = std::make_shared<Demuxer>(stream.input);
459 demuxer->set_dump_stream_info(packaging_params.test_params.dump_stream_info);
460 demuxer->set_input_format(stream.input_format);
462 if (packaging_params.decryption_params.key_provider != KeyProvider::kNone) {
463 std::unique_ptr<KeySource> decryption_key_source(
464 CreateDecryptionKeySource(packaging_params.decryption_params));
465 if (!decryption_key_source) {
467 error::INVALID_ARGUMENT,
468 "Must define decryption key source when defining key provider");
470 demuxer->SetKeySource(std::move(decryption_key_source));
473 *new_demuxer = std::move(demuxer);
477 std::shared_ptr<MediaHandler> CreateEncryptionHandler(
478 const PackagingParams& packaging_params,
479 const StreamDescriptor& stream,
480 KeySource* key_source) {
481 if (stream.skip_encryption) {
490 EncryptionParams encryption_params = packaging_params.encryption_params;
495 if (GetOutputFormat(stream) == CONTAINER_MPEG2TS ||
496 GetOutputFormat(stream) == CONTAINER_AAC ||
497 GetOutputFormat(stream) == CONTAINER_AC3 ||
498 GetOutputFormat(stream) == CONTAINER_EAC3) {
499 VLOG(1) <<
"Use Apple Sample AES encryption for MPEG2TS or Packed Audio.";
500 encryption_params.protection_scheme = kAppleSampleAesProtectionScheme;
503 if (!stream.drm_label.empty()) {
504 const std::string& drm_label = stream.drm_label;
505 encryption_params.stream_label_func =
506 [drm_label](
const EncryptionParams::EncryptedStreamAttributes&) {
509 }
else if (!encryption_params.stream_label_func) {
510 const int kDefaultMaxSdPixels = 768 * 576;
511 const int kDefaultMaxHdPixels = 1920 * 1080;
512 const int kDefaultMaxUhd1Pixels = 4096 * 2160;
513 encryption_params.stream_label_func = std::bind(
514 &Packager::DefaultStreamLabelFunction, kDefaultMaxSdPixels,
515 kDefaultMaxHdPixels, kDefaultMaxUhd1Pixels, std::placeholders::_1);
518 return std::make_shared<EncryptionHandler>(encryption_params, key_source);
521 std::unique_ptr<MediaHandler> CreateTextChunker(
522 const ChunkingParams& chunking_params) {
523 const float segment_length_in_seconds =
524 chunking_params.segment_duration_in_seconds;
525 return std::unique_ptr<MediaHandler>(
new TextChunker(
526 segment_length_in_seconds, chunking_params.start_segment_number));
529 Status CreateTtmlJobs(
530 const std::vector<std::reference_wrapper<const StreamDescriptor>>& streams,
531 const PackagingParams& packaging_params,
532 SyncPointQueue* sync_points,
533 MuxerFactory* muxer_factory,
534 MpdNotifier* mpd_notifier,
535 JobManager* job_manager) {
537 for (
const StreamDescriptor& stream : streams) {
539 if (!packaging_params.hls_params.master_playlist_output.empty() &&
541 return Status(error::INVALID_ARGUMENT,
542 "HLS does not support TTML in xml format.");
545 if (!stream.segment_template.empty()) {
546 return Status(error::INVALID_ARGUMENT,
547 "Segmented TTML is not supported.");
550 if (GetOutputFormat(stream) != CONTAINER_TTML) {
551 return Status(error::INVALID_ARGUMENT,
552 "Converting TTML to other formats is not supported");
555 if (!stream.output.empty()) {
556 if (!File::Copy(stream.input.c_str(), stream.output.c_str())) {
558 absl::StrAppendFormat(
559 &error,
"Failed to copy the input file (%s) to output file (%s).",
560 stream.input.c_str(), stream.output.c_str());
561 return Status(error::FILE_FAILURE, error);
564 MediaInfo text_media_info;
565 if (!StreamInfoToTextMediaInfo(stream, &text_media_info)) {
566 return Status(error::INVALID_ARGUMENT,
567 "Could not create media info for stream.");
574 if (mpd_notifier->NotifyNewContainer(text_media_info, &unused)) {
575 mpd_notifier->Flush();
577 return Status(error::PARSER_FAILURE,
578 "Failed to process text file " + stream.input);
582 if (packaging_params.output_media_info) {
584 text_media_info, stream.output + kMediaInfoSuffix);
592 Status CreateAudioVideoJobs(
593 const std::vector<std::reference_wrapper<const StreamDescriptor>>& streams,
594 const PackagingParams& packaging_params,
595 KeySource* encryption_key_source,
596 SyncPointQueue* sync_points,
597 MuxerListenerFactory* muxer_listener_factory,
598 MuxerFactory* muxer_factory,
599 JobManager* job_manager) {
600 DCHECK(muxer_listener_factory);
601 DCHECK(muxer_factory);
606 std::map<std::string, std::shared_ptr<Demuxer>> sources;
607 std::map<std::string, std::shared_ptr<MediaHandler>> cue_aligners;
609 for (
const StreamDescriptor& stream : streams) {
610 bool seen_input_before = sources.find(stream.input) != sources.end();
611 if (seen_input_before) {
616 CreateDemuxer(stream, packaging_params, &sources[stream.input]));
617 cue_aligners[stream.input] =
618 sync_points ? std::make_shared<CueAlignmentHandler>(sync_points)
622 for (
auto& source : sources) {
623 job_manager->Add(
"RemuxJob", source.second);
628 std::shared_ptr<MediaHandler> replicator;
630 std::string previous_input;
631 std::string previous_selector;
633 for (
const StreamDescriptor& stream : streams) {
635 auto& demuxer = sources[stream.input];
636 auto& cue_aligner = cue_aligners[stream.input];
638 const bool new_input_file = stream.input != previous_input;
639 const bool new_stream =
640 new_input_file || previous_selector != stream.stream_selector;
641 const bool is_text = IsTextStream(stream);
642 previous_input = stream.input;
643 previous_selector = stream.stream_selector;
647 if (stream.output.empty() && stream.segment_template.empty()) {
655 if (!stream.language.empty()) {
656 demuxer->SetLanguageOverride(stream.stream_selector, stream.language);
659 std::vector<std::shared_ptr<MediaHandler>> handlers;
661 handlers.emplace_back(std::make_shared<TextPadder>(
662 packaging_params.default_text_zero_bias_ms));
665 handlers.emplace_back(cue_aligner);
668 handlers.emplace_back(std::make_shared<ChunkingHandler>(
669 packaging_params.chunking_params));
670 handlers.emplace_back(CreateEncryptionHandler(packaging_params, stream,
671 encryption_key_source));
674 replicator = std::make_shared<Replicator>();
675 handlers.emplace_back(replicator);
677 RETURN_IF_ERROR(MediaHandler::Chain(handlers));
678 RETURN_IF_ERROR(demuxer->SetHandler(stream.stream_selector, handlers[0]));
682 const auto output_format = GetOutputFormat(stream);
683 std::shared_ptr<Muxer> muxer =
684 muxer_factory->CreateMuxer(output_format, stream);
686 return Status(error::INVALID_ARGUMENT,
"Failed to create muxer for " +
688 stream.stream_selector);
691 std::unique_ptr<MuxerListener> muxer_listener =
692 muxer_listener_factory->CreateListener(ToMuxerListenerData(stream));
693 muxer->SetMuxerListener(std::move(muxer_listener));
695 std::vector<std::shared_ptr<MediaHandler>> handlers;
696 handlers.emplace_back(replicator);
699 if (stream.trick_play_factor) {
700 handlers.emplace_back(
701 std::make_shared<TrickPlayHandler>(stream.trick_play_factor));
704 if (stream.cc_index >= 0) {
705 handlers.emplace_back(
706 std::make_shared<CcStreamFilter>(stream.language, stream.cc_index));
710 (!stream.segment_template.empty() || output_format == CONTAINER_MOV)) {
711 handlers.emplace_back(
712 CreateTextChunker(packaging_params.chunking_params));
715 if (is_text && output_format == CONTAINER_MOV) {
716 const auto output_codec = GetTextOutputCodec(stream);
717 if (output_codec == CONTAINER_WEBVTT) {
718 handlers.emplace_back(std::make_shared<WebVttToMp4Handler>());
719 }
else if (output_codec == CONTAINER_TTML) {
720 handlers.emplace_back(std::make_shared<ttml::TtmlToMp4Handler>());
724 handlers.emplace_back(muxer);
725 RETURN_IF_ERROR(MediaHandler::Chain(handlers));
731 Status CreateAllJobs(
const std::vector<StreamDescriptor>& stream_descriptors,
732 const PackagingParams& packaging_params,
733 MpdNotifier* mpd_notifier,
734 KeySource* encryption_key_source,
735 SyncPointQueue* sync_points,
736 MuxerListenerFactory* muxer_listener_factory,
737 MuxerFactory* muxer_factory,
738 JobManager* job_manager) {
739 DCHECK(muxer_factory);
740 DCHECK(muxer_listener_factory);
744 std::vector<std::reference_wrapper<const StreamDescriptor>> ttml_streams;
745 std::vector<std::reference_wrapper<const StreamDescriptor>>
748 bool has_transport_audio_video_streams =
false;
749 bool has_non_transport_audio_video_streams =
false;
751 for (
const StreamDescriptor& stream : stream_descriptors) {
752 const auto input_container = DetermineContainerFromFileName(stream.input);
753 const auto output_format = GetOutputFormat(stream);
754 if (input_container == CONTAINER_TTML) {
755 ttml_streams.push_back(stream);
757 audio_video_streams.push_back(stream);
758 switch (output_format) {
759 case CONTAINER_MPEG2TS:
764 has_transport_audio_video_streams =
true;
767 case CONTAINER_WEBVTT:
770 has_non_transport_audio_video_streams =
true;
778 std::sort(audio_video_streams.begin(), audio_video_streams.end(),
779 media::StreamDescriptorCompareFn);
781 if (packaging_params.transport_stream_timestamp_offset_ms > 0) {
782 if (has_transport_audio_video_streams &&
783 has_non_transport_audio_video_streams) {
784 LOG(WARNING) <<
"There may be problems mixing transport streams and "
785 "non-transport streams. For example, the subtitles may "
786 "be out of sync with non-transport streams.";
787 }
else if (has_non_transport_audio_video_streams) {
790 muxer_factory->SetTsStreamOffset(0);
794 RETURN_IF_ERROR(CreateTtmlJobs(ttml_streams, packaging_params, sync_points,
795 muxer_factory, mpd_notifier, job_manager));
796 RETURN_IF_ERROR(CreateAudioVideoJobs(
797 audio_video_streams, packaging_params, encryption_key_source, sync_points,
798 muxer_listener_factory, muxer_factory, job_manager));
801 return job_manager->InitializeJobs();
807 struct Packager::PackagerInternal {
808 std::shared_ptr<media::FakeClock> fake_clock;
809 std::unique_ptr<KeySource> encryption_key_source;
810 std::unique_ptr<MpdNotifier> mpd_notifier;
811 std::unique_ptr<hls::HlsNotifier> hls_notifier;
812 BufferCallbackParams buffer_callback_params;
813 std::unique_ptr<media::JobManager> job_manager;
816 Packager::Packager() {}
818 Packager::~Packager() {}
820 Status Packager::Initialize(
821 const PackagingParams& packaging_params,
822 const std::vector<StreamDescriptor>& stream_descriptors) {
824 return Status(error::INVALID_ARGUMENT,
"Already initialized.");
826 RETURN_IF_ERROR(media::ValidateParams(packaging_params, stream_descriptors));
828 if (!packaging_params.test_params.injected_library_version.empty()) {
829 SetPackagerVersionForTesting(
830 packaging_params.test_params.injected_library_version);
833 std::unique_ptr<PackagerInternal>
internal(
new PackagerInternal);
836 if (packaging_params.encryption_params.key_provider != KeyProvider::kNone) {
837 internal->encryption_key_source = CreateEncryptionKeySource(
838 static_cast<media::FourCC
>(
839 packaging_params.encryption_params.protection_scheme),
840 packaging_params.encryption_params);
841 if (!internal->encryption_key_source)
842 return Status(error::INVALID_ARGUMENT,
"Failed to create key source.");
846 MpdParams mpd_params = packaging_params.mpd_params;
847 HlsParams hls_params = packaging_params.hls_params;
851 const double target_segment_duration =
852 packaging_params.chunking_params.segment_duration_in_seconds;
853 mpd_params.target_segment_duration = target_segment_duration;
854 hls_params.target_segment_duration = target_segment_duration;
857 internal->buffer_callback_params = packaging_params.buffer_callback_params;
858 if (internal->buffer_callback_params.write_func) {
859 mpd_params.mpd_output = File::MakeCallbackFileName(
860 internal->buffer_callback_params, mpd_params.mpd_output);
861 hls_params.master_playlist_output = File::MakeCallbackFileName(
862 internal->buffer_callback_params, hls_params.master_playlist_output);
868 mpd_params.default_language =
870 mpd_params.default_text_language =
872 hls_params.default_language =
874 hls_params.default_text_language =
876 hls_params.is_independent_segments =
877 packaging_params.chunking_params.segment_sap_aligned;
879 if (!mpd_params.mpd_output.empty()) {
880 const bool on_demand_dash_profile =
881 stream_descriptors.begin()->segment_template.empty();
882 const MpdOptions mpd_options =
883 media::GetMpdOptions(on_demand_dash_profile, mpd_params);
884 internal->mpd_notifier.reset(
new SimpleMpdNotifier(mpd_options));
885 if (!internal->mpd_notifier->Init()) {
886 LOG(ERROR) <<
"MpdNotifier failed to initialize.";
887 return Status(error::INVALID_ARGUMENT,
888 "Failed to initialize MpdNotifier.");
892 if (!hls_params.master_playlist_output.empty()) {
893 internal->hls_notifier.reset(
new hls::SimpleHlsNotifier(hls_params));
896 std::unique_ptr<SyncPointQueue> sync_points;
897 if (!packaging_params.ad_cue_generator_params.cue_points.empty()) {
899 new SyncPointQueue(packaging_params.ad_cue_generator_params));
901 if (packaging_params.single_threaded) {
902 internal->job_manager.reset(
903 new SingleThreadJobManager(std::move(sync_points)));
905 internal->job_manager.reset(
new JobManager(std::move(sync_points)));
908 std::vector<StreamDescriptor> streams_for_jobs;
910 for (
const StreamDescriptor& descriptor : stream_descriptors) {
912 StreamDescriptor copy = descriptor;
914 if (internal->buffer_callback_params.read_func) {
915 copy.input = File::MakeCallbackFileName(internal->buffer_callback_params,
919 if (internal->buffer_callback_params.write_func) {
920 copy.output = File::MakeCallbackFileName(internal->buffer_callback_params,
922 copy.segment_template = File::MakeCallbackFileName(
923 internal->buffer_callback_params, descriptor.segment_template);
927 if (!copy.language.empty()) {
929 if (copy.language ==
"und") {
931 error::INVALID_ARGUMENT,
932 "Unknown/invalid language specified: " + descriptor.language);
936 streams_for_jobs.push_back(copy);
939 media::MuxerFactory muxer_factory(packaging_params);
940 if (packaging_params.test_params.inject_fake_clock) {
941 internal->fake_clock.reset(
new media::FakeClock());
942 muxer_factory.OverrideClock(internal->fake_clock);
945 media::MuxerListenerFactory muxer_listener_factory(
946 packaging_params.output_media_info,
947 packaging_params.mpd_params.use_segment_list,
948 internal->mpd_notifier.get(), internal->hls_notifier.get());
950 RETURN_IF_ERROR(media::CreateAllJobs(
951 streams_for_jobs, packaging_params, internal->mpd_notifier.get(),
952 internal->encryption_key_source.get(),
953 internal->job_manager->sync_points(), &muxer_listener_factory,
954 &muxer_factory, internal->job_manager.get()));
956 internal_ = std::move(
internal);
960 Status Packager::Run() {
962 return Status(error::INVALID_ARGUMENT,
"Not yet initialized.");
964 RETURN_IF_ERROR(internal_->job_manager->RunJobs());
966 if (internal_->hls_notifier) {
967 if (!internal_->hls_notifier->Flush())
968 return Status(error::INVALID_ARGUMENT,
"Failed to flush Hls.");
970 if (internal_->mpd_notifier) {
971 if (!internal_->mpd_notifier->Flush())
972 return Status(error::INVALID_ARGUMENT,
"Failed to flush Mpd.");
977 void Packager::Cancel() {
979 LOG(INFO) <<
"Not yet initialized. Return directly.";
982 internal_->job_manager->CancelJobs();
985 std::string Packager::GetLibraryVersion() {
986 return GetPackagerVersion();
989 std::string Packager::DefaultStreamLabelFunction(
993 const EncryptionParams::EncryptedStreamAttributes& stream_attributes) {
994 if (stream_attributes.stream_type ==
995 EncryptionParams::EncryptedStreamAttributes::kAudio)
997 if (stream_attributes.stream_type ==
998 EncryptionParams::EncryptedStreamAttributes::kVideo) {
999 const int pixels = stream_attributes.oneof.video.width *
1000 stream_attributes.oneof.video.height;
1001 if (pixels <= max_sd_pixels)
1003 if (pixels <= max_hd_pixels)
1005 if (pixels <= max_uhd1_pixels)
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)