7 #include "packager/packager.h"
11 #include "packager/app/libcrypto_threading.h"
12 #include "packager/app/packager_util.h"
13 #include "packager/app/stream_descriptor.h"
14 #include "packager/base/at_exit.h"
15 #include "packager/base/files/file_path.h"
16 #include "packager/base/logging.h"
17 #include "packager/base/path_service.h"
18 #include "packager/base/strings/stringprintf.h"
19 #include "packager/base/threading/simple_thread.h"
20 #include "packager/base/time/clock.h"
21 #include "packager/file/file.h"
22 #include "packager/hls/base/hls_notifier.h"
23 #include "packager/hls/base/simple_hls_notifier.h"
24 #include "packager/media/ad_cue_generator/ad_cue_generator.h"
25 #include "packager/media/base/container_names.h"
26 #include "packager/media/base/fourccs.h"
27 #include "packager/media/base/key_source.h"
28 #include "packager/media/base/language_utils.h"
29 #include "packager/media/base/muxer_options.h"
30 #include "packager/media/base/muxer_util.h"
31 #include "packager/media/chunking/chunking_handler.h"
32 #include "packager/media/crypto/encryption_handler.h"
33 #include "packager/media/demuxer/demuxer.h"
34 #include "packager/media/event/combined_muxer_listener.h"
35 #include "packager/media/event/hls_notify_muxer_listener.h"
36 #include "packager/media/event/mpd_notify_muxer_listener.h"
37 #include "packager/media/event/vod_media_info_dump_muxer_listener.h"
38 #include "packager/media/formats/mp2t/ts_muxer.h"
39 #include "packager/media/formats/mp4/mp4_muxer.h"
40 #include "packager/media/formats/webm/webm_muxer.h"
41 #include "packager/media/replicator/replicator.h"
42 #include "packager/media/trick_play/trick_play_handler.h"
43 #include "packager/mpd/base/dash_iop_mpd_notifier.h"
44 #include "packager/mpd/base/media_info.pb.h"
45 #include "packager/mpd/base/mpd_builder.h"
46 #include "packager/mpd/base/simple_mpd_notifier.h"
47 #include "packager/version/version.h"
53 using media::KeySource;
54 using media::MuxerOptions;
59 const char kMediaInfoSuffix[] =
".media_info";
64 std::string DetermineTextFileFormat(
const std::string& file) {
67 LOG(ERROR) <<
"Failed to open file " << file
68 <<
" to determine file format.";
71 MediaContainerName container_name = DetermineContainer(
72 reinterpret_cast<const uint8_t*>(content.data()), content.size());
73 if (container_name == CONTAINER_WEBVTT) {
75 }
else if (container_name == CONTAINER_TTML) {
82 MediaContainerName GetOutputFormat(
const StreamDescriptor& descriptor) {
83 MediaContainerName output_format = CONTAINER_UNKNOWN;
84 if (!descriptor.output_format.empty()) {
85 output_format = DetermineContainerFromFormatName(descriptor.output_format);
86 if (output_format == CONTAINER_UNKNOWN) {
87 LOG(ERROR) <<
"Unable to determine output format from '"
88 << descriptor.output_format <<
"'.";
91 const std::string& output_name = descriptor.output.empty()
92 ? descriptor.segment_template
94 if (output_name.empty())
95 return CONTAINER_UNKNOWN;
96 output_format = DetermineContainerFromFileName(output_name);
97 if (output_format == CONTAINER_UNKNOWN) {
98 LOG(ERROR) <<
"Unable to determine output format from '" << output_name
102 return output_format;
105 Status ValidateStreamDescriptor(
bool dump_stream_info,
106 const StreamDescriptor& stream) {
107 if (stream.input.empty()) {
108 return Status(error::INVALID_ARGUMENT,
"Stream input not specified.");
113 if (dump_stream_info && stream.output.empty() &&
114 stream.segment_template.empty()) {
118 if (stream.output.empty() && stream.segment_template.empty()) {
119 return Status(error::INVALID_ARGUMENT,
120 "Streams must specify 'output' or 'segment template'.");
124 if (stream.stream_selector.empty()) {
125 return Status(error::INVALID_ARGUMENT,
126 "Stream stream_selector not specified.");
130 if (stream.segment_template.length()) {
131 Status template_check = ValidateSegmentTemplate(stream.segment_template);
132 if (!template_check.ok()) {
133 return template_check;
139 const MediaContainerName output_format = GetOutputFormat(stream);
141 if (output_format == CONTAINER_UNKNOWN) {
142 return Status(error::INVALID_ARGUMENT,
"Unsupported output format.");
143 }
else if (output_format == MediaContainerName::CONTAINER_MPEG2TS) {
144 if (stream.segment_template.empty()) {
145 return Status(error::INVALID_ARGUMENT,
146 "Please specify segment_template. Single file TS output is "
153 if (stream.output.length()) {
154 return Status(error::INVALID_ARGUMENT,
155 "All TS segments must be self-initializing. Stream "
156 "descriptors 'output' or 'init_segment' are not allowed.");
161 if (stream.segment_template.length() && stream.output.empty()) {
162 return Status(error::INVALID_ARGUMENT,
163 "Please specify 'init_segment'. All non-TS multi-segment "
164 "content must provide an init segment.");
171 Status ValidateParams(
const PackagingParams& packaging_params,
172 const std::vector<StreamDescriptor>& stream_descriptors) {
173 if (!packaging_params.chunking_params.segment_sap_aligned &&
174 packaging_params.chunking_params.subsegment_sap_aligned) {
175 return Status(error::INVALID_ARGUMENT,
176 "Setting segment_sap_aligned to false but "
177 "subsegment_sap_aligned to true is not allowed.");
180 if (stream_descriptors.empty()) {
181 return Status(error::INVALID_ARGUMENT,
182 "Stream descriptors cannot be empty.");
187 const bool on_demand_dash_profile =
188 stream_descriptors.begin()->segment_template.empty();
189 for (
const auto& descriptor : stream_descriptors) {
190 if (on_demand_dash_profile != descriptor.segment_template.empty()) {
191 return Status(error::INVALID_ARGUMENT,
192 "Inconsistent stream descriptor specification: "
193 "segment_template should be specified for none or all "
194 "stream descriptors.");
197 Status stream_check = ValidateStreamDescriptor(
198 packaging_params.test_params.dump_stream_info, descriptor);
200 if (!stream_check.ok()) {
205 if (packaging_params.output_media_info && !on_demand_dash_profile) {
207 return Status(error::UNIMPLEMENTED,
208 "--output_media_info is only supported for on-demand profile "
209 "(not using segment_template).");
215 bool StreamDescriptorCompareFn(
const StreamDescriptor& a,
216 const StreamDescriptor& b) {
217 if (a.input == b.input) {
218 if (a.stream_selector == b.stream_selector) {
221 if (a.trick_play_factor == 0 || b.trick_play_factor == 0) {
222 return a.trick_play_factor == 0;
224 return a.trick_play_factor > b.trick_play_factor;
227 return a.stream_selector < b.stream_selector;
231 return a.input < b.input;
236 class FakeClock :
public base::Clock {
238 base::Time Now()
override {
return base::Time(); }
241 class Job :
public base::SimpleThread {
243 Job(
const std::string& name, std::shared_ptr<OriginHandler> work)
244 : SimpleThread(name),
246 wait_(base::WaitableEvent::ResetPolicy::MANUAL,
247 base::WaitableEvent::InitialState::NOT_SIGNALED) {}
251 status_ = work_->Initialize();
259 const Status& status()
const {
return status_; }
261 base::WaitableEvent* wait() {
return &wait_; }
264 Job(
const Job&) =
delete;
265 Job& operator=(
const Job&) =
delete;
267 void Run()
override {
269 status_ = work_->Run();
273 std::shared_ptr<OriginHandler> work_;
276 base::WaitableEvent wait_;
279 bool StreamInfoToTextMediaInfo(
const StreamDescriptor& stream_descriptor,
280 MediaInfo* text_media_info) {
281 const std::string& language = stream_descriptor.language;
282 const std::string format = DetermineTextFileFormat(stream_descriptor.input);
283 if (format.empty()) {
284 LOG(ERROR) <<
"Failed to determine the text file format for "
285 << stream_descriptor.input;
289 if (!
File::Copy(stream_descriptor.input.c_str(),
290 stream_descriptor.output.c_str())) {
291 LOG(ERROR) <<
"Failed to copy the input file (" << stream_descriptor.input
292 <<
") to output file (" << stream_descriptor.output <<
").";
296 text_media_info->set_media_file_name(stream_descriptor.output);
297 text_media_info->set_container_type(MediaInfo::CONTAINER_TEXT);
299 if (stream_descriptor.bandwidth != 0) {
300 text_media_info->set_bandwidth(stream_descriptor.bandwidth);
305 const int kDefaultTextBandwidth = 256;
306 text_media_info->set_bandwidth(kDefaultTextBandwidth);
309 MediaInfo::TextInfo* text_info = text_media_info->mutable_text_info();
310 text_info->set_format(format);
311 if (!language.empty())
312 text_info->set_language(language);
317 std::unique_ptr<MuxerListener> CreateMuxerListener(
318 const StreamDescriptor& stream,
320 bool output_media_info,
321 MpdNotifier* mpd_notifier,
322 hls::HlsNotifier* hls_notifier) {
323 std::unique_ptr<CombinedMuxerListener> combined_listener(
324 new CombinedMuxerListener);
326 if (output_media_info) {
327 std::unique_ptr<MuxerListener> listener(
328 new VodMediaInfoDumpMuxerListener(stream.output + kMediaInfoSuffix));
329 combined_listener->AddListener(std::move(listener));
333 std::unique_ptr<MuxerListener> listener(
334 new MpdNotifyMuxerListener(mpd_notifier));
335 combined_listener->AddListener(std::move(listener));
341 std::string group_id = stream.hls_group_id;
342 std::string name = stream.hls_name;
343 std::string hls_playlist_name = stream.hls_playlist_name;
344 if (group_id.empty())
347 name = base::StringPrintf(
"stream_%d", stream_number);
348 if (hls_playlist_name.empty())
349 hls_playlist_name = base::StringPrintf(
"stream_%d.m3u8", stream_number);
351 std::unique_ptr<MuxerListener> listener(
new HlsNotifyMuxerListener(
352 hls_playlist_name, name, group_id, hls_notifier));
353 combined_listener->AddListener(std::move(listener));
356 return std::move(combined_listener);
359 std::shared_ptr<Muxer> CreateMuxer(
const PackagingParams& packaging_params,
360 const StreamDescriptor& stream,
362 std::unique_ptr<MuxerListener> listener) {
363 const MediaContainerName format = GetOutputFormat(stream);
365 MuxerOptions options;
366 options.mp4_params = packaging_params.mp4_output_params;
367 options.temp_dir = packaging_params.temp_dir;
368 options.bandwidth = stream.bandwidth;
369 options.output_file_name = stream.output;
370 options.segment_template = stream.segment_template;
372 std::shared_ptr<Muxer> muxer;
376 muxer = std::make_shared<webm::WebMMuxer>(options);
378 case CONTAINER_MPEG2TS:
379 muxer = std::make_shared<mp2t::TsMuxer>(options);
382 muxer = std::make_shared<mp4::MP4Muxer>(options);
385 LOG(ERROR) <<
"Cannot support muxing to " << format;
396 muxer->set_clock(clock);
400 muxer->SetMuxerListener(std::move(listener));
406 std::shared_ptr<MediaHandler> CreateEncryptionHandler(
407 const PackagingParams& packaging_params,
408 const StreamDescriptor& stream,
409 KeySource* key_source) {
410 if (stream.skip_encryption) {
419 EncryptionParams encryption_params = packaging_params.encryption_params;
424 if (GetOutputFormat(stream) == CONTAINER_MPEG2TS) {
425 VLOG(1) <<
"Use Apple Sample AES encryption for MPEG2TS.";
426 encryption_params.protection_scheme = kAppleSampleAesProtectionScheme;
429 if (!stream.drm_label.empty()) {
430 const std::string& drm_label = stream.drm_label;
431 encryption_params.stream_label_func =
432 [drm_label](
const EncryptionParams::EncryptedStreamAttributes&) {
435 }
else if (!encryption_params.stream_label_func) {
436 const int kDefaultMaxSdPixels = 768 * 576;
437 const int kDefaultMaxHdPixels = 1920 * 1080;
438 const int kDefaultMaxUhd1Pixels = 4096 * 2160;
439 encryption_params.stream_label_func = std::bind(
441 kDefaultMaxHdPixels, kDefaultMaxUhd1Pixels, std::placeholders::_1);
444 return std::make_shared<EncryptionHandler>(encryption_params, key_source);
447 Status CreateTextJobs(
448 const std::vector<std::reference_wrapper<const StreamDescriptor>>& streams,
449 const PackagingParams& packaging_params,
450 MpdNotifier* mpd_notifier,
451 std::vector<std::unique_ptr<Job>>* jobs) {
452 for (
const StreamDescriptor& stream : streams) {
453 const MediaContainerName output_format = GetOutputFormat(stream);
455 if (output_format == CONTAINER_MOV) {
460 MediaInfo text_media_info;
461 if (!StreamInfoToTextMediaInfo(stream, &text_media_info)) {
462 return Status(error::INVALID_ARGUMENT,
463 "Could not create media info for stream.");
468 if (mpd_notifier->NotifyNewContainer(text_media_info, &unused)) {
469 mpd_notifier->Flush();
471 return Status(error::PARSER_FAILURE,
472 "Failed to process text file " + stream.input);
476 if (packaging_params.output_media_info) {
478 text_media_info, stream.output + kMediaInfoSuffix);
486 Status CreateAudioVideoJobs(
487 int first_stream_number,
488 const std::vector<std::reference_wrapper<const StreamDescriptor>>& streams,
489 const PackagingParams& packaging_params,
490 FakeClock* fake_clock,
491 KeySource* encryption_key_source,
492 MpdNotifier* mpd_notifier,
493 hls::HlsNotifier* hls_notifier,
494 std::vector<std::unique_ptr<Job>>* jobs) {
498 std::shared_ptr<Demuxer> demuxer;
501 std::shared_ptr<MediaHandler> replicator;
503 std::string previous_input;
504 std::string previous_selector;
508 int stream_number = first_stream_number - 1;
510 for (
const StreamDescriptor& stream : streams) {
514 if (previous_input != stream.input) {
515 demuxer = std::make_shared<Demuxer>(stream.input);
517 demuxer->set_dump_stream_info(
518 packaging_params.test_params.dump_stream_info);
519 if (packaging_params.decryption_params.key_provider !=
520 KeyProvider::kNone) {
521 std::unique_ptr<KeySource> decryption_key_source(
522 CreateDecryptionKeySource(packaging_params.decryption_params));
523 if (!decryption_key_source) {
525 error::INVALID_ARGUMENT,
526 "Must define decryption key source when defining key provider");
528 demuxer->SetKeySource(std::move(decryption_key_source));
531 jobs->emplace_back(
new media::Job(
"RemuxJob", demuxer));
534 if (!stream.language.empty()) {
535 demuxer->SetLanguageOverride(stream.stream_selector, stream.language);
538 const bool new_stream = previous_input != stream.input ||
539 previous_selector != stream.stream_selector;
540 previous_input = stream.input;
541 previous_selector = stream.stream_selector;
545 if (stream.output.empty() && stream.segment_template.empty()) {
550 std::shared_ptr<MediaHandler> ad_cue_generator;
551 if (!packaging_params.ad_cue_generator_params.cue_points.empty()) {
552 ad_cue_generator = std::make_shared<AdCueGenerator>(
553 packaging_params.ad_cue_generator_params);
556 replicator = std::make_shared<Replicator>();
558 std::shared_ptr<MediaHandler> chunker =
559 std::make_shared<ChunkingHandler>(packaging_params.chunking_params);
561 std::shared_ptr<MediaHandler> encryptor = CreateEncryptionHandler(
562 packaging_params, stream, encryption_key_source);
565 if (ad_cue_generator) {
567 demuxer->SetHandler(stream.stream_selector, ad_cue_generator));
568 status.Update(ad_cue_generator->AddHandler(chunker));
570 status.Update(demuxer->SetHandler(stream.stream_selector, chunker));
573 status.Update(chunker->AddHandler(encryptor));
574 status.Update(encryptor->AddHandler(replicator));
576 status.Update(chunker->AddHandler(replicator));
583 if (!stream.language.empty()) {
584 demuxer->SetLanguageOverride(stream.stream_selector, stream.language);
589 std::unique_ptr<MuxerListener> muxer_listener = CreateMuxerListener(
590 stream, stream_number, packaging_params.output_media_info, mpd_notifier,
592 std::shared_ptr<Muxer> muxer = CreateMuxer(
593 packaging_params, stream,
594 packaging_params.test_params.inject_fake_clock ? fake_clock :
nullptr,
595 std::move(muxer_listener));
598 return Status(error::INVALID_ARGUMENT,
"Failed to create muxer for " +
600 stream.stream_selector);
603 std::shared_ptr<MediaHandler> trick_play;
604 if (stream.trick_play_factor) {
605 trick_play = std::make_shared<TrickPlayHandler>(stream.trick_play_factor);
610 status.Update(replicator->AddHandler(trick_play));
611 status.Update(trick_play->AddHandler(muxer));
613 status.Update(replicator->AddHandler(muxer));
624 Status CreateAllJobs(
const std::vector<StreamDescriptor>& stream_descriptors,
625 const PackagingParams& packaging_params,
626 FakeClock* fake_clock,
627 KeySource* encryption_key_source,
628 MpdNotifier* mpd_notifier,
629 hls::HlsNotifier* hls_notifier,
630 std::vector<std::unique_ptr<Job>>* jobs) {
634 std::vector<std::reference_wrapper<const StreamDescriptor>> text_streams;
635 std::vector<std::reference_wrapper<const StreamDescriptor>>
638 for (
const StreamDescriptor& stream : stream_descriptors) {
642 if (stream.stream_selector ==
"text") {
643 text_streams.push_back(stream);
645 audio_video_streams.push_back(stream);
651 std::sort(audio_video_streams.begin(), audio_video_streams.end(),
652 media::StreamDescriptorCompareFn);
657 CreateTextJobs(text_streams, packaging_params, mpd_notifier, jobs));
659 int stream_number = text_streams.size();
661 status.Update(CreateAudioVideoJobs(
662 stream_number, audio_video_streams, packaging_params, fake_clock,
663 encryption_key_source, mpd_notifier, hls_notifier, jobs));
670 for (
const std::unique_ptr<Job>& job : *jobs) {
672 status.Update(job->status());
678 Status RunJobs(
const std::vector<std::unique_ptr<Job>>& jobs) {
684 std::vector<Job*> active_jobs;
685 std::vector<base::WaitableEvent*> active_waits;
689 for (
auto& job : jobs) {
692 active_jobs.push_back(job.get());
693 active_waits.push_back(job->wait());
698 while (status.ok() && active_jobs.size()) {
702 base::WaitableEvent::WaitMany(active_waits.data(), active_waits.size());
703 Job* job = active_jobs[done];
706 status.Update(job->status());
709 active_jobs.erase(active_jobs.begin() + done);
710 active_waits.erase(active_waits.begin() + done);
715 for (
auto& job : active_jobs) {
719 for (
auto& job : active_jobs) {
729 struct Packager::PackagerInternal {
730 media::FakeClock fake_clock;
731 std::unique_ptr<KeySource> encryption_key_source;
732 std::unique_ptr<MpdNotifier> mpd_notifier;
733 std::unique_ptr<hls::HlsNotifier> hls_notifier;
734 std::vector<std::unique_ptr<media::Job>> jobs;
735 BufferCallbackParams buffer_callback_params;
738 Packager::Packager() {}
740 Packager::~Packager() {}
744 const std::vector<StreamDescriptor>& stream_descriptors) {
746 static base::AtExitManager exit;
750 return Status(error::INVALID_ARGUMENT,
"Already initialized.");
753 media::ValidateParams(packaging_params, stream_descriptors);
754 if (!param_check.ok()) {
759 SetPackagerVersionForTesting(
763 std::unique_ptr<PackagerInternal>
internal(
new PackagerInternal);
767 internal->encryption_key_source = CreateEncryptionKeySource(
768 static_cast<media::FourCC>(
771 if (!internal->encryption_key_source)
772 return Status(error::INVALID_ARGUMENT,
"Failed to create key source.");
781 if (internal->buffer_callback_params.write_func) {
783 internal->buffer_callback_params, mpd_params.
mpd_output);
789 const bool on_demand_dash_profile =
790 stream_descriptors.begin()->segment_template.empty();
792 media::GetMpdOptions(on_demand_dash_profile, mpd_params);
798 if (!internal->mpd_notifier->Init()) {
799 LOG(ERROR) <<
"MpdNotifier failed to initialize.";
800 return Status(error::INVALID_ARGUMENT,
801 "Failed to initialize MpdNotifier.");
806 base::FilePath master_playlist_path(
808 base::FilePath master_playlist_name = master_playlist_path.BaseName();
813 master_playlist_path.DirName().AsEndingWithSeparator().AsUTF8Unsafe(),
814 master_playlist_name.AsUTF8Unsafe()));
817 std::vector<StreamDescriptor> streams_for_jobs;
823 if (internal->buffer_callback_params.read_func) {
828 if (internal->buffer_callback_params.write_func) {
832 internal->buffer_callback_params, descriptor.segment_template);
840 error::INVALID_ARGUMENT,
841 "Unknown/invalid language specified: " + descriptor.language);
845 streams_for_jobs.push_back(copy);
848 Status status = media::CreateAllJobs(
849 streams_for_jobs, packaging_params, &internal->fake_clock,
850 internal->encryption_key_source.get(),
internal->mpd_notifier.get(),
851 internal->hls_notifier.get(), &
internal->jobs);
857 internal_ = std::move(
internal);
863 return Status(error::INVALID_ARGUMENT,
"Not yet initialized.");
864 Status status = media::RunJobs(internal_->jobs);
868 if (internal_->hls_notifier) {
869 if (!internal_->hls_notifier->Flush())
870 return Status(error::INVALID_ARGUMENT,
"Failed to flush Hls.");
872 if (internal_->mpd_notifier) {
873 if (!internal_->mpd_notifier->Flush())
874 return Status(error::INVALID_ARGUMENT,
"Failed to flush Mpd.");
881 LOG(INFO) <<
"Not yet initialized. Return directly.";
884 for (
const std::unique_ptr<media::Job>& job : internal_->jobs)
889 return GetPackagerVersion();
897 if (stream_attributes.stream_type ==
898 EncryptionParams::EncryptedStreamAttributes::kAudio)
900 if (stream_attributes.stream_type ==
901 EncryptionParams::EncryptedStreamAttributes::kVideo) {
902 const int pixels = stream_attributes.oneof.video.width *
903 stream_attributes.oneof.video.height;
904 if (pixels <= max_sd_pixels)
906 if (pixels <= max_hd_pixels)
908 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)
static std::string DefaultStreamLabelFunction(int max_sd_pixels, int max_hd_pixels, int max_uhd1_pixels, const EncryptionParams::EncryptedStreamAttributes &stream_attributes)
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)
bool generate_dash_if_iop_compliant_mpd
Try to generate DASH-IF IOP compliant MPD.
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.
double time_shift_buffer_depth
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.
void Cancel()
Cancel packaging. Note that it has to be called from another thread.
HlsPlaylistType playlist_type
HLS playlist type. See HLS specification for details.