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/segment_coordinator.h>
34#include <packager/media/chunking/text_chunker.h>
35#include <packager/media/crypto/encryption_handler.h>
36#include <packager/media/demuxer/demuxer.h>
37#include <packager/media/event/muxer_listener_factory.h>
38#include <packager/media/event/vod_media_info_dump_muxer_listener.h>
39#include <packager/media/formats/ttml/ttml_to_mp4_handler.h>
40#include <packager/media/formats/webvtt/text_padder.h>
41#include <packager/media/formats/webvtt/webvtt_to_mp4_handler.h>
42#include <packager/media/replicator/replicator.h>
43#include <packager/media/trick_play/trick_play_handler.h>
44#include <packager/mpd/base/media_info.pb.h>
45#include <packager/mpd/base/simple_mpd_notifier.h>
46#include <packager/version/version.h>
52using media::JobManager;
53using media::KeySource;
54using media::MuxerOptions;
55using media::SingleThreadJobManager;
56using media::SyncPointQueue;
61const char kMediaInfoSuffix[] =
".media_info";
63MuxerListenerFactory::StreamData ToMuxerListenerData(
64 const StreamDescriptor& stream) {
65 MuxerListenerFactory::StreamData data;
66 data.media_info_output = stream.output;
68 data.hls_group_id = stream.hls_group_id;
69 data.hls_name = stream.hls_name;
70 data.hls_playlist_name = stream.hls_playlist_name;
71 data.hls_iframe_playlist_name = stream.hls_iframe_playlist_name;
72 data.hls_characteristics = stream.hls_characteristics;
73 data.forced_subtitle = stream.forced_subtitle;
74 data.hls_only = stream.hls_only;
76 data.dash_accessiblities = stream.dash_accessiblities;
77 data.dash_roles = stream.dash_roles;
78 data.dash_only = stream.dash_only;
79 data.index = stream.index;
80 data.dash_label = stream.dash_label;
81 data.input_format = stream.input_format;
88bool DetermineTextFileCodec(
const std::string& file, std::string* out) {
92 if (!File::ReadFileToString(file.c_str(), &content)) {
93 LOG(ERROR) <<
"Failed to open file " << file
94 <<
" to determine file format.";
98 const uint8_t* content_data =
99 reinterpret_cast<const uint8_t*
>(content.data());
100 MediaContainerName container_name =
101 DetermineContainer(content_data, content.size());
103 if (container_name == CONTAINER_WEBVTT) {
108 if (container_name == CONTAINER_TTML) {
116MediaContainerName GetOutputFormat(
const StreamDescriptor& descriptor) {
117 if (!descriptor.output_format.empty()) {
118 MediaContainerName format =
119 DetermineContainerFromFormatName(descriptor.output_format);
120 if (format == CONTAINER_UNKNOWN) {
121 LOG(ERROR) <<
"Unable to determine output format from '"
122 << descriptor.output_format <<
"'.";
127 std::optional<MediaContainerName> format_from_output;
128 std::optional<MediaContainerName> format_from_segment;
129 if (!descriptor.output.empty()) {
130 format_from_output = DetermineContainerFromFileName(descriptor.output);
131 if (format_from_output.value() == CONTAINER_UNKNOWN) {
132 LOG(ERROR) <<
"Unable to determine output format from '"
133 << descriptor.output <<
"'.";
136 if (!descriptor.segment_template.empty()) {
137 format_from_segment =
138 DetermineContainerFromFileName(descriptor.segment_template);
139 if (format_from_segment.value() == CONTAINER_UNKNOWN) {
140 LOG(ERROR) <<
"Unable to determine output format from '"
141 << descriptor.segment_template <<
"'.";
145 if (format_from_output && format_from_segment) {
146 if (format_from_output.value() != format_from_segment.value()) {
147 LOG(ERROR) <<
"Output format determined from '" << descriptor.output
148 <<
"' differs from output format determined from '"
149 << descriptor.segment_template <<
"'.";
150 return CONTAINER_UNKNOWN;
154 if (format_from_output)
155 return format_from_output.value();
156 if (format_from_segment)
157 return format_from_segment.value();
158 return CONTAINER_UNKNOWN;
161MediaContainerName GetTextOutputCodec(
const StreamDescriptor& descriptor) {
162 const auto output_container = GetOutputFormat(descriptor);
163 if (output_container != CONTAINER_MOV)
164 return output_container;
166 const auto input_container = DetermineContainerFromFileName(descriptor.input);
167 if (absl::AsciiStrToLower(descriptor.output_format) ==
"vtt+mp4" ||
168 absl::AsciiStrToLower(descriptor.output_format) ==
"webvtt+mp4") {
169 return CONTAINER_WEBVTT;
170 }
else if (absl::AsciiStrToLower(descriptor.output_format) !=
"ttml+mp4" &&
171 input_container == CONTAINER_WEBVTT) {
173 return CONTAINER_WEBVTT;
176 return CONTAINER_TTML;
180bool IsTextStream(
const StreamDescriptor& stream) {
181 if (stream.stream_selector ==
"text")
183 if (absl::AsciiStrToLower(stream.output_format) ==
"vtt+mp4" ||
184 absl::AsciiStrToLower(stream.output_format) ==
"webvtt+mp4" ||
185 absl::AsciiStrToLower(stream.output_format) ==
"ttml+mp4") {
189 auto output_format = GetOutputFormat(stream);
190 return output_format == CONTAINER_WEBVTT || output_format == CONTAINER_TTML;
193Status ValidateStreamDescriptor(
bool dump_stream_info,
194 const StreamDescriptor& stream) {
195 if (stream.input.empty()) {
196 return Status(error::INVALID_ARGUMENT,
"Stream input not specified.");
201 if (dump_stream_info && stream.output.empty() &&
202 stream.segment_template.empty()) {
206 if (stream.output.empty() && stream.segment_template.empty()) {
207 return Status(error::INVALID_ARGUMENT,
208 "Streams must specify 'output' or 'segment template'.");
212 if (stream.stream_selector.empty()) {
213 return Status(error::INVALID_ARGUMENT,
214 "Stream stream_selector not specified.");
218 if (stream.segment_template.length()) {
219 RETURN_IF_ERROR(ValidateSegmentTemplate(stream.segment_template));
224 const MediaContainerName output_format = GetOutputFormat(stream);
226 if (output_format == CONTAINER_UNKNOWN) {
227 return Status(error::INVALID_ARGUMENT,
"Unsupported output format.");
230 if (output_format == CONTAINER_WEBVTT || output_format == CONTAINER_TTML ||
231 output_format == CONTAINER_AAC || output_format == CONTAINER_MP3 ||
232 output_format == CONTAINER_AC3 || output_format == CONTAINER_EAC3 ||
233 output_format == CONTAINER_MPEG2TS) {
236 if (stream.segment_template.length() && stream.output.length()) {
238 error::INVALID_ARGUMENT,
239 "Segmented subtitles, PackedAudio or TS output cannot have an init "
240 "segment. Do not specify stream descriptors 'output' or "
241 "'init_segment' when using 'segment_template'.");
246 if (stream.segment_template.length() && stream.output.empty()) {
247 return Status(error::INVALID_ARGUMENT,
248 "Please specify 'init_segment'. All non-TS multi-segment "
249 "content must provide an init segment.");
253 if (stream.output.find(
'$') != std::string::npos) {
254 if (output_format == CONTAINER_WEBVTT) {
256 error::UNIMPLEMENTED,
257 "WebVTT output with one file per Representation per Period "
258 "is not supported yet. Please use fMP4 instead. If that needs to be "
259 "supported, please file a feature request on GitHub.");
264 RETURN_IF_ERROR(ValidateSegmentTemplate(stream.output));
270Status ValidateParams(
const PackagingParams& packaging_params,
271 const std::vector<StreamDescriptor>& stream_descriptors) {
272 if (!packaging_params.chunking_params.segment_sap_aligned &&
273 packaging_params.chunking_params.subsegment_sap_aligned) {
274 return Status(error::INVALID_ARGUMENT,
275 "Setting segment_sap_aligned to false but "
276 "subsegment_sap_aligned to true is not allowed.");
279 if (packaging_params.chunking_params.start_segment_number < 0) {
280 return Status(error::INVALID_ARGUMENT,
281 "Negative --start_segment_number is not allowed.");
284 if (stream_descriptors.empty()) {
285 return Status(error::INVALID_ARGUMENT,
286 "Stream descriptors cannot be empty.");
291 const bool on_demand_dash_profile =
292 stream_descriptors.begin()->segment_template.empty();
293 std::set<std::string> outputs;
294 std::set<std::string> segment_templates;
295 for (
const auto& descriptor : stream_descriptors) {
296 if (on_demand_dash_profile != descriptor.segment_template.empty()) {
297 return Status(error::INVALID_ARGUMENT,
298 "Inconsistent stream descriptor specification: "
299 "segment_template should be specified for none or all "
300 "stream descriptors.");
303 RETURN_IF_ERROR(ValidateStreamDescriptor(
304 packaging_params.test_params.dump_stream_info, descriptor));
306 if (absl::StartsWith(descriptor.input,
"udp://")) {
307 const HlsParams& hls_params = packaging_params.hls_params;
308 if (!hls_params.master_playlist_output.empty() &&
309 hls_params.playlist_type == HlsPlaylistType::kVod) {
311 <<
"Seeing UDP input with HLS Playlist Type set to VOD. The "
312 "playlists will only be generated when UDP socket is closed. "
313 "If you want to do live packaging, --hls_playlist_type needs to "
320 if (!descriptor.output.empty()) {
321 if (outputs.find(descriptor.output) != outputs.end()) {
323 error::INVALID_ARGUMENT,
324 "Seeing duplicated outputs '" + descriptor.output +
325 "' in stream descriptors. Every output must be unique.");
327 outputs.insert(descriptor.output);
329 if (!descriptor.segment_template.empty()) {
330 if (segment_templates.find(descriptor.segment_template) !=
331 segment_templates.end()) {
332 return Status(error::INVALID_ARGUMENT,
333 "Seeing duplicated segment templates '" +
334 descriptor.segment_template +
335 "' in stream descriptors. Every segment template "
338 segment_templates.insert(descriptor.segment_template);
342 if (packaging_params.output_media_info && !on_demand_dash_profile) {
344 return Status(error::UNIMPLEMENTED,
345 "--output_media_info is only supported for on-demand profile "
346 "(not using segment_template).");
349 if (on_demand_dash_profile &&
350 !packaging_params.mpd_params.mpd_output.empty() &&
351 !packaging_params.mp4_output_params.generate_sidx_in_media_segments &&
352 !packaging_params.mpd_params.use_segment_list) {
354 error::UNIMPLEMENTED,
355 "--generate_sidx_in_media_segments is required for DASH "
356 "on-demand profile (not using segment_template or segment list).");
359 if (packaging_params.chunking_params.low_latency_dash_mode &&
360 packaging_params.chunking_params.subsegment_duration_in_seconds) {
367 return Status(error::INVALID_ARGUMENT,
368 "--fragment_duration cannot be set "
369 "if --low_latency_dash_mode is enabled.");
372 if (packaging_params.mpd_params.low_latency_dash_mode &&
373 packaging_params.mpd_params.utc_timings.empty()) {
375 return Status(error::INVALID_ARGUMENT,
376 "--utc_timings must be be set "
377 "if --low_latency_dash_mode is enabled.");
383bool StreamDescriptorCompareFn(
const StreamDescriptor& a,
384 const StreamDescriptor& b) {
390 if (a.input == b.input) {
391 if (a.stream_selector == b.stream_selector) {
394 return a.trick_play_factor < b.trick_play_factor;
396 return a.stream_selector < b.stream_selector;
399 return a.input < b.input;
404class FakeClock :
public Clock {
406 time_point now() noexcept
override {
407 return std::chrono::system_clock::time_point(std::chrono::seconds(0));
411bool StreamInfoToTextMediaInfo(
const StreamDescriptor& stream_descriptor,
412 MediaInfo* text_media_info) {
414 if (!DetermineTextFileCodec(stream_descriptor.input, &codec)) {
415 LOG(ERROR) <<
"Failed to determine the text file format for "
416 << stream_descriptor.input;
420 MediaInfo::TextInfo* text_info = text_media_info->mutable_text_info();
421 text_info->set_codec(codec);
423 const std::string& language = stream_descriptor.language;
424 if (!language.empty()) {
425 text_info->set_language(language);
428 if (stream_descriptor.index.has_value()) {
429 text_media_info->set_index(stream_descriptor.index.value());
432 text_media_info->set_media_file_name(stream_descriptor.output);
433 text_media_info->set_container_type(MediaInfo::CONTAINER_TEXT);
435 if (stream_descriptor.bandwidth != 0) {
436 text_media_info->set_bandwidth(stream_descriptor.bandwidth);
441 const int kDefaultTextBandwidth = 256;
442 text_media_info->set_bandwidth(kDefaultTextBandwidth);
445 if (!stream_descriptor.dash_roles.empty()) {
446 for (
const auto& dash_role : stream_descriptor.dash_roles) {
447 text_media_info->add_dash_roles(dash_role);
457Status CreateDemuxer(
const StreamDescriptor& stream,
458 const PackagingParams& packaging_params,
459 std::shared_ptr<Demuxer>* new_demuxer) {
460 std::shared_ptr<Demuxer> demuxer = std::make_shared<Demuxer>(stream.input);
461 demuxer->set_dump_stream_info(packaging_params.test_params.dump_stream_info);
462 demuxer->set_input_format(stream.input_format);
464 if (packaging_params.decryption_params.key_provider != KeyProvider::kNone) {
465 std::unique_ptr<KeySource> decryption_key_source(
466 CreateDecryptionKeySource(packaging_params.decryption_params));
467 if (!decryption_key_source) {
469 error::INVALID_ARGUMENT,
470 "Must define decryption key source when defining key provider");
472 demuxer->SetKeySource(std::move(decryption_key_source));
475 *new_demuxer = std::move(demuxer);
479std::shared_ptr<MediaHandler> CreateEncryptionHandler(
480 const PackagingParams& packaging_params,
481 const StreamDescriptor& stream,
482 KeySource* key_source) {
483 if (stream.skip_encryption) {
492 EncryptionParams encryption_params = packaging_params.encryption_params;
497 if (GetOutputFormat(stream) == CONTAINER_MPEG2TS ||
498 GetOutputFormat(stream) == CONTAINER_AAC ||
499 GetOutputFormat(stream) == CONTAINER_AC3 ||
500 GetOutputFormat(stream) == CONTAINER_EAC3) {
501 VLOG(1) <<
"Use Apple Sample AES encryption for MPEG2TS or Packed Audio.";
502 encryption_params.protection_scheme = kAppleSampleAesProtectionScheme;
505 if (!stream.drm_label.empty()) {
506 const std::string& drm_label = stream.drm_label;
507 encryption_params.stream_label_func =
508 [drm_label](
const EncryptionParams::EncryptedStreamAttributes&) {
511 }
else if (!encryption_params.stream_label_func) {
512 const int kDefaultMaxSdPixels = 768 * 576;
513 const int kDefaultMaxHdPixels = 1920 * 1080;
514 const int kDefaultMaxUhd1Pixels = 4096 * 2160;
515 encryption_params.stream_label_func = std::bind(
516 &Packager::DefaultStreamLabelFunction, kDefaultMaxSdPixels,
517 kDefaultMaxHdPixels, kDefaultMaxUhd1Pixels, std::placeholders::_1);
520 return std::make_shared<EncryptionHandler>(encryption_params, key_source);
523std::unique_ptr<MediaHandler> CreateTextChunker(
524 const ChunkingParams& chunking_params,
525 bool use_segment_coordinator =
false) {
526 const float segment_length_in_seconds =
527 chunking_params.segment_duration_in_seconds;
528 return std::unique_ptr<MediaHandler>(
new TextChunker(
529 segment_length_in_seconds, chunking_params.start_segment_number,
530 chunking_params.ts_ttx_heartbeat_shift, use_segment_coordinator));
533Status CreateTtmlJobs(
534 const std::vector<std::reference_wrapper<const StreamDescriptor>>& streams,
535 const PackagingParams& packaging_params,
536 SyncPointQueue* sync_points,
537 MuxerFactory* muxer_factory,
538 MpdNotifier* mpd_notifier,
539 JobManager* job_manager) {
541 for (
const StreamDescriptor& stream : streams) {
543 if (!packaging_params.hls_params.master_playlist_output.empty() &&
545 return Status(error::INVALID_ARGUMENT,
546 "HLS does not support TTML in xml format.");
549 if (!stream.segment_template.empty()) {
550 return Status(error::INVALID_ARGUMENT,
551 "Segmented TTML is not supported.");
554 if (GetOutputFormat(stream) != CONTAINER_TTML) {
555 return Status(error::INVALID_ARGUMENT,
556 "Converting TTML to other formats is not supported");
559 if (!stream.output.empty()) {
560 if (!File::Copy(stream.input.c_str(), stream.output.c_str())) {
562 absl::StrAppendFormat(
563 &error,
"Failed to copy the input file (%s) to output file (%s).",
564 stream.input.c_str(), stream.output.c_str());
565 return Status(error::FILE_FAILURE, error);
568 MediaInfo text_media_info;
569 if (!StreamInfoToTextMediaInfo(stream, &text_media_info)) {
570 return Status(error::INVALID_ARGUMENT,
571 "Could not create media info for stream.");
578 if (mpd_notifier->NotifyNewContainer(text_media_info, &unused)) {
579 mpd_notifier->Flush();
581 return Status(error::PARSER_FAILURE,
582 "Failed to process text file " + stream.input);
586 if (packaging_params.output_media_info) {
588 text_media_info, stream.output + kMediaInfoSuffix);
596Status CreateAudioVideoJobs(
597 const std::vector<std::reference_wrapper<const StreamDescriptor>>& streams,
598 const PackagingParams& packaging_params,
599 KeySource* encryption_key_source,
600 SyncPointQueue* sync_points,
601 MuxerListenerFactory* muxer_listener_factory,
602 MuxerFactory* muxer_factory,
603 JobManager* job_manager) {
604 DCHECK(muxer_listener_factory);
605 DCHECK(muxer_factory);
610 std::map<std::string, std::shared_ptr<Demuxer>> sources;
611 std::map<std::string, std::shared_ptr<MediaHandler>> cue_aligners;
612 std::map<std::string, std::shared_ptr<SegmentCoordinator>>
613 segment_coordinators;
615 for (
const StreamDescriptor& stream : streams) {
616 bool seen_input_before = sources.find(stream.input) != sources.end();
617 if (seen_input_before) {
622 CreateDemuxer(stream, packaging_params, &sources[stream.input]));
623 cue_aligners[stream.input] =
624 sync_points ? std::make_shared<CueAlignmentHandler>(sync_points)
626 segment_coordinators[stream.input] = std::make_shared<SegmentCoordinator>();
629 for (
auto& source : sources) {
630 job_manager->Add(
"RemuxJob", source.second);
635 std::shared_ptr<MediaHandler> replicator;
637 std::string previous_input;
638 std::string previous_selector;
641 std::map<std::string, size_t> stream_counters;
643 for (
const StreamDescriptor& stream : streams) {
645 auto& demuxer = sources[stream.input];
646 auto& cue_aligner = cue_aligners[stream.input];
647 auto& segment_coordinator = segment_coordinators[stream.input];
649 const bool new_input_file = stream.input != previous_input;
650 const bool new_stream =
651 new_input_file || previous_selector != stream.stream_selector;
652 const bool is_text = IsTextStream(stream);
653 const bool is_teletext = is_text && stream.cc_index >= 0;
655 previous_input = stream.input;
656 previous_selector = stream.stream_selector;
660 if (stream.output.empty() && stream.segment_template.empty()) {
668 if (!stream.language.empty()) {
669 demuxer->SetLanguageOverride(stream.stream_selector, stream.language);
672 std::vector<std::shared_ptr<MediaHandler>> handlers;
678 if (is_text && stream.cc_index < 0) {
679 handlers.emplace_back(std::make_shared<TextPadder>(
680 packaging_params.default_text_zero_bias_ms));
683 handlers.emplace_back(cue_aligner);
687 size_t stream_index = stream_counters[stream.input]++;
689 segment_coordinator->MarkAsTeletextStream(stream_index);
695 handlers.emplace_back(std::make_shared<ChunkingHandler>(
696 packaging_params.chunking_params));
697 handlers.emplace_back(segment_coordinator);
698 handlers.emplace_back(CreateEncryptionHandler(packaging_params, stream,
699 encryption_key_source));
703 handlers.emplace_back(segment_coordinator);
706 replicator = std::make_shared<Replicator>();
707 handlers.emplace_back(replicator);
709 RETURN_IF_ERROR(MediaHandler::Chain(handlers));
710 RETURN_IF_ERROR(demuxer->SetHandler(stream.stream_selector, handlers[0]));
714 const auto output_format = GetOutputFormat(stream);
715 std::shared_ptr<Muxer> muxer =
716 muxer_factory->CreateMuxer(output_format, stream);
718 return Status(error::INVALID_ARGUMENT,
"Failed to create muxer for " +
720 stream.stream_selector);
723 std::unique_ptr<MuxerListener> muxer_listener =
724 muxer_listener_factory->CreateListener(ToMuxerListenerData(stream));
725 muxer->SetMuxerListener(std::move(muxer_listener));
727 std::vector<std::shared_ptr<MediaHandler>> handlers;
728 handlers.emplace_back(replicator);
731 if (stream.trick_play_factor) {
732 handlers.emplace_back(
733 std::make_shared<TrickPlayHandler>(stream.trick_play_factor));
736 if (stream.cc_index >= 0) {
737 handlers.emplace_back(
738 std::make_shared<CcStreamFilter>(stream.language, stream.cc_index));
742 (!stream.segment_template.empty() || output_format == CONTAINER_MOV)) {
744 bool use_coordinator = is_teletext;
745 handlers.emplace_back(
746 CreateTextChunker(packaging_params.chunking_params, use_coordinator));
749 if (is_text && output_format == CONTAINER_MOV) {
750 const auto output_codec = GetTextOutputCodec(stream);
751 if (output_codec == CONTAINER_WEBVTT) {
752 handlers.emplace_back(std::make_shared<WebVttToMp4Handler>());
753 }
else if (output_codec == CONTAINER_TTML) {
754 handlers.emplace_back(std::make_shared<ttml::TtmlToMp4Handler>());
758 handlers.emplace_back(muxer);
759 RETURN_IF_ERROR(MediaHandler::Chain(handlers));
765Status CreateAllJobs(
const std::vector<StreamDescriptor>& stream_descriptors,
766 const PackagingParams& packaging_params,
767 MpdNotifier* mpd_notifier,
768 KeySource* encryption_key_source,
769 SyncPointQueue* sync_points,
770 MuxerListenerFactory* muxer_listener_factory,
771 MuxerFactory* muxer_factory,
772 JobManager* job_manager) {
773 DCHECK(muxer_factory);
774 DCHECK(muxer_listener_factory);
778 std::vector<std::reference_wrapper<const StreamDescriptor>> ttml_streams;
779 std::vector<std::reference_wrapper<const StreamDescriptor>>
782 bool has_transport_audio_video_streams =
false;
783 bool has_non_transport_audio_video_streams =
false;
785 for (
const StreamDescriptor& stream : stream_descriptors) {
786 const auto input_container = DetermineContainerFromFileName(stream.input);
787 const auto output_format = GetOutputFormat(stream);
788 if (input_container == CONTAINER_TTML) {
789 ttml_streams.push_back(stream);
791 audio_video_streams.push_back(stream);
792 switch (output_format) {
793 case CONTAINER_MPEG2TS:
798 has_transport_audio_video_streams =
true;
801 case CONTAINER_WEBVTT:
804 has_non_transport_audio_video_streams =
true;
812 std::sort(audio_video_streams.begin(), audio_video_streams.end(),
813 media::StreamDescriptorCompareFn);
815 if (packaging_params.transport_stream_timestamp_offset_ms > 0) {
816 if (has_transport_audio_video_streams &&
817 has_non_transport_audio_video_streams) {
818 LOG(WARNING) <<
"There may be problems mixing transport streams and "
819 "non-transport streams. For example, the subtitles may "
820 "be out of sync with non-transport streams.";
821 }
else if (has_non_transport_audio_video_streams) {
824 muxer_factory->SetTsStreamOffset(0);
828 RETURN_IF_ERROR(CreateTtmlJobs(ttml_streams, packaging_params, sync_points,
829 muxer_factory, mpd_notifier, job_manager));
830 RETURN_IF_ERROR(CreateAudioVideoJobs(
831 audio_video_streams, packaging_params, encryption_key_source, sync_points,
832 muxer_listener_factory, muxer_factory, job_manager));
835 return job_manager->InitializeJobs();
841struct Packager::PackagerInternal {
842 std::shared_ptr<media::FakeClock> fake_clock;
843 std::unique_ptr<KeySource> encryption_key_source;
844 std::unique_ptr<MpdNotifier> mpd_notifier;
845 std::unique_ptr<hls::HlsNotifier> hls_notifier;
846 BufferCallbackParams buffer_callback_params;
847 std::unique_ptr<media::JobManager> job_manager;
850Packager::Packager() {}
852Packager::~Packager() {}
854Status Packager::Initialize(
855 const PackagingParams& packaging_params,
856 const std::vector<StreamDescriptor>& stream_descriptors) {
858 return Status(error::INVALID_ARGUMENT,
"Already initialized.");
860 RETURN_IF_ERROR(media::ValidateParams(packaging_params, stream_descriptors));
862 if (!packaging_params.test_params.injected_library_version.empty()) {
863 SetPackagerVersionForTesting(
864 packaging_params.test_params.injected_library_version);
867 std::unique_ptr<PackagerInternal> internal(
new PackagerInternal);
870 if (packaging_params.encryption_params.key_provider != KeyProvider::kNone) {
871 internal->encryption_key_source = CreateEncryptionKeySource(
872 static_cast<media::FourCC
>(
873 packaging_params.encryption_params.protection_scheme),
874 packaging_params.encryption_params);
875 if (!internal->encryption_key_source)
876 return Status(error::INVALID_ARGUMENT,
"Failed to create key source.");
880 MpdParams mpd_params = packaging_params.mpd_params;
881 HlsParams hls_params = packaging_params.hls_params;
885 const double target_segment_duration =
886 packaging_params.chunking_params.segment_duration_in_seconds;
887 mpd_params.target_segment_duration = target_segment_duration;
888 hls_params.target_segment_duration = target_segment_duration;
891 internal->buffer_callback_params = packaging_params.buffer_callback_params;
892 if (internal->buffer_callback_params.write_func) {
893 mpd_params.mpd_output = File::MakeCallbackFileName(
894 internal->buffer_callback_params, mpd_params.mpd_output);
895 hls_params.master_playlist_output = File::MakeCallbackFileName(
896 internal->buffer_callback_params, hls_params.master_playlist_output);
902 mpd_params.default_language =
904 mpd_params.default_text_language =
906 hls_params.default_language =
908 hls_params.default_text_language =
910 hls_params.is_independent_segments =
911 packaging_params.chunking_params.segment_sap_aligned;
913 for (
const auto& caption : packaging_params.closed_captions) {
914 CeaCaption dash_caption = caption;
916 mpd_params.closed_captions.push_back(dash_caption);
918 CeaCaption hls_caption = caption;
920 hls_params.closed_captions.push_back(hls_caption);
923 if (!mpd_params.mpd_output.empty()) {
924 const bool on_demand_dash_profile =
925 stream_descriptors.begin()->segment_template.empty();
926 const MpdOptions mpd_options =
927 media::GetMpdOptions(on_demand_dash_profile, mpd_params);
928 internal->mpd_notifier.reset(
new SimpleMpdNotifier(mpd_options));
929 if (!internal->mpd_notifier->Init()) {
930 LOG(ERROR) <<
"MpdNotifier failed to initialize.";
931 return Status(error::INVALID_ARGUMENT,
932 "Failed to initialize MpdNotifier.");
936 if (!hls_params.master_playlist_output.empty()) {
937 internal->hls_notifier.reset(
new hls::SimpleHlsNotifier(hls_params));
940 std::unique_ptr<SyncPointQueue> sync_points;
941 if (!packaging_params.ad_cue_generator_params.cue_points.empty()) {
943 new SyncPointQueue(packaging_params.ad_cue_generator_params));
945 if (packaging_params.single_threaded) {
946 internal->job_manager.reset(
947 new SingleThreadJobManager(std::move(sync_points)));
949 internal->job_manager.reset(
new JobManager(std::move(sync_points)));
952 std::vector<StreamDescriptor> streams_for_jobs;
954 for (
const StreamDescriptor& descriptor : stream_descriptors) {
956 StreamDescriptor copy = descriptor;
958 if (internal->buffer_callback_params.read_func) {
959 copy.input = File::MakeCallbackFileName(internal->buffer_callback_params,
963 if (internal->buffer_callback_params.write_func) {
964 copy.output = File::MakeCallbackFileName(internal->buffer_callback_params,
966 copy.segment_template = File::MakeCallbackFileName(
967 internal->buffer_callback_params, descriptor.segment_template);
971 if (!copy.language.empty()) {
973 if (copy.language ==
"und") {
975 error::INVALID_ARGUMENT,
976 "Unknown/invalid language specified: " + descriptor.language);
980 streams_for_jobs.push_back(copy);
983 media::MuxerFactory muxer_factory(packaging_params);
984 if (packaging_params.test_params.inject_fake_clock) {
985 internal->fake_clock.reset(
new media::FakeClock());
986 muxer_factory.OverrideClock(internal->fake_clock);
989 media::MuxerListenerFactory muxer_listener_factory(
990 packaging_params.output_media_info,
991 packaging_params.mpd_params.use_segment_list,
992 internal->mpd_notifier.get(), 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);
1004Status Packager::Run() {
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.");
1021void Packager::Cancel() {
1023 LOG(INFO) <<
"Not yet initialized. Return directly.";
1026 internal_->job_manager->CancelJobs();
1029std::string Packager::GetLibraryVersion() {
1030 return GetPackagerVersion();
1033std::string Packager::DefaultStreamLabelFunction(
1036 int max_uhd1_pixels,
1037 const EncryptionParams::EncryptedStreamAttributes& stream_attributes) {
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)
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)