From 8cb3be74b14b2c55f5b5462c7ce58c4f98b2f66a Mon Sep 17 00:00:00 2001 From: KongQun Yang Date: Thu, 22 May 2014 12:39:30 -0700 Subject: [PATCH] Support mpd generation in packager app Change-Id: I9dfa8ca254d3d3e0210feeecc7389f360ab4b6a7 --- app/mpd_flags.cc | 28 ++++++++++++++++++ app/mpd_flags.h | 19 +++++++++++++ app/muxer_flags.cc | 14 --------- app/muxer_flags.h | 4 --- app/packager_main.cc | 57 +++++++++++++++++++++++++++++++++++-- app/single_packager_main.cc | 47 ++++++++++++++++++++++++++++++ packager.gyp | 6 ++++ 7 files changed, 154 insertions(+), 21 deletions(-) create mode 100644 app/mpd_flags.cc create mode 100644 app/mpd_flags.h diff --git a/app/mpd_flags.cc b/app/mpd_flags.cc new file mode 100644 index 0000000000..78ca76a715 --- /dev/null +++ b/app/mpd_flags.cc @@ -0,0 +1,28 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd +// +// Defines Mpd flags. + +#include "app/mpd_flags.h" + +DEFINE_bool(output_media_info, + false, + "Create a human readable format of MediaInfo. The output file name " + "will be the name specified by output flag, suffixed with " + "'.media_info'."); +DEFINE_string(mpd_output, "", "MPD output file name."); +DEFINE_string(scheme_id_uri, + "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed", + "This flag only applies if output_media_info is true. This value " + "will be set in MediaInfo if the stream is encrypted. " + "If the stream is encrypted, MPD requires a " + "element which requires the schemeIdUri attribute. " + "Default value is Widevine PSSH system ID, and it is valid only " + "for ISO BMFF."); +DEFINE_string(base_urls, + "", + "Comma separated BaseURLs for the MPD. The values will be added " + "as element(s) immediately under the element."); diff --git a/app/mpd_flags.h b/app/mpd_flags.h new file mode 100644 index 0000000000..2021fe1f7d --- /dev/null +++ b/app/mpd_flags.h @@ -0,0 +1,19 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd +// +// Defines Mpd flags. + +#ifndef APP_MPD_FLAGS_H_ +#define APP_MPD_FLAGS_H_ + +#include + +DECLARE_bool(output_media_info); +DECLARE_string(mpd_output); +DECLARE_string(scheme_id_uri); +DECLARE_string(base_urls); + +#endif // APP_MPD_FLAGS_H_ diff --git a/app/muxer_flags.cc b/app/muxer_flags.cc index 33ff59e89b..744a191368 100644 --- a/app/muxer_flags.cc +++ b/app/muxer_flags.cc @@ -51,17 +51,3 @@ DEFINE_string(temp_dir, "Specify a directory in which to store temporary (intermediate) " " files. Used only if single_segment=true."); -// Flags for MuxerListener. -DEFINE_bool(output_media_info, - true, - "Create a human readable format of MediaInfo. The output file name " - "will be the name specified by output flag, suffixed with " - "'.media_info'."); -DEFINE_string(scheme_id_uri, - "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed", - "This flag only applies if output_media_info is true. This value " - "will be set in MediaInfo if the stream is encrypted. " - "If the stream is encrypted, MPD requires a " - "element which requires the schemeIdUri attribute. " - "Default value is Widevine PSSH system ID, and it is valid only " - "for ISO BMFF."); diff --git a/app/muxer_flags.h b/app/muxer_flags.h index 4886899234..8b598573c5 100644 --- a/app/muxer_flags.h +++ b/app/muxer_flags.h @@ -22,8 +22,4 @@ DECLARE_bool(normalize_presentation_timestamp); DECLARE_int32(num_subsegments_per_sidx); DECLARE_string(temp_dir); -// Flags for MuxerListener. -DECLARE_bool(output_media_info); -DECLARE_string(scheme_id_uri); - #endif // APP_MUXER_FLAGS_H_ diff --git a/app/packager_main.cc b/app/packager_main.cc index b00715d60d..f68b74de27 100644 --- a/app/packager_main.cc +++ b/app/packager_main.cc @@ -8,9 +8,9 @@ #include "app/fixed_key_encryption_flags.h" #include "app/libcrypto_threading.h" -#include "app/muxer_flags.h" #include "app/packager_common.h" -#include "app/single_muxer_flags.h" +#include "app/mpd_flags.h" +#include "app/muxer_flags.h" #include "app/widevine_encryption_flags.h" #include "base/logging.h" #include "base/stl_util.h" @@ -21,7 +21,10 @@ #include "media/base/encryption_key_source.h" #include "media/base/muxer_options.h" #include "media/base/muxer_util.h" +#include "media/event/mpd_notify_muxer_listener.h" #include "media/formats/mp4/mp4_muxer.h" +#include "mpd/base/mpd_builder.h" +#include "mpd/base/simple_mpd_notifier.h" namespace { const char kUsage[] = @@ -43,6 +46,15 @@ typedef std::vector StringVector; namespace media { +using dash_packager::DashProfile; +using dash_packager::kOnDemandProfile; +using dash_packager::kLiveProfile; +using dash_packager::MpdNotifier; +using dash_packager::MpdOptions; +using dash_packager::SimpleMpdNotifier; +using event::MpdNotifyMuxerListener; +using event::MuxerListener; + // Demux, Mux(es) and worker thread used to remux a source file/stream. class RemuxJob : public base::SimpleThread { public: @@ -77,7 +89,10 @@ class RemuxJob : public base::SimpleThread { bool CreateRemuxJobs(const StringVector& stream_descriptors, const MuxerOptions& muxer_options, EncryptionKeySource* key_source, + MpdNotifier* mpd_notifier, + std::vector* muxer_listeners, std::vector* remux_jobs) { + DCHECK(muxer_listeners); DCHECK(remux_jobs); // Sort the stream descriptors so that we can group muxers by demux. @@ -140,6 +155,15 @@ bool CreateRemuxJobs(const StringVector& stream_descriptors, FLAGS_crypto_period_duration); } + if (mpd_notifier) { + scoped_ptr mpd_notify_muxer_listener( + new MpdNotifyMuxerListener(mpd_notifier)); + mpd_notify_muxer_listener->SetContentProtectionSchemeIdUri( + FLAGS_scheme_id_uri); + muxer_listeners->push_back(mpd_notify_muxer_listener.release()); + muxer->SetMuxerListener(muxer_listeners->back()); + } + if (!AddStreamToMuxer(remux_jobs->back()->demuxer()->streams(), stream_selector, muxer.get())) @@ -181,6 +205,11 @@ Status RunRemuxJobs(const std::vector& remux_jobs) { } bool RunPackager(const StringVector& stream_descriptors) { + if (FLAGS_output_media_info) { + NOTIMPLEMENTED() << "ERROR: --output_media_info is not supported yet."; + return false; + } + // Get basic muxer options. MuxerOptions muxer_options; if (!GetMuxerOptions(&muxer_options)) @@ -194,13 +223,35 @@ bool RunPackager(const StringVector& stream_descriptors) { return false; } + scoped_ptr mpd_notifier; + if (!FLAGS_mpd_output.empty()) { + DashProfile profile = + FLAGS_single_segment ? kOnDemandProfile : kLiveProfile; + std::vector base_urls; + base::SplitString(FLAGS_base_urls, ',', &base_urls); + // TODO(rkuroiwa,kqyang): Get mpd options from command line. + mpd_notifier.reset(new SimpleMpdNotifier(profile, MpdOptions(), base_urls, + FLAGS_mpd_output)); + if (!mpd_notifier->Init()) { + LOG(ERROR) << "MpdNotifier failed to initialize."; + return false; + } + } + + // TODO(kqyang): Should Muxer::SetMuxerListener take owership of the + // muxer_listeners object? Then we can get rid of |muxer_listeners|. + std::vector muxer_listeners; + STLElementDeleter > deleter(&muxer_listeners); std::vector remux_jobs; STLElementDeleter > scoped_jobs_deleter(&remux_jobs); if (!CreateRemuxJobs(stream_descriptors, muxer_options, encryption_key_source.get(), - &remux_jobs)) + mpd_notifier.get(), + &muxer_listeners, + &remux_jobs)) { return false; + } Status status = RunRemuxJobs(remux_jobs); if (!status.ok()) { diff --git a/app/single_packager_main.cc b/app/single_packager_main.cc index cc7cf95362..fb4bf737bc 100644 --- a/app/single_packager_main.cc +++ b/app/single_packager_main.cc @@ -8,20 +8,25 @@ #include "app/fixed_key_encryption_flags.h" #include "app/packager_common.h" +#include "app/mpd_flags.h" #include "app/muxer_flags.h" #include "app/single_muxer_flags.h" #include "app/widevine_encryption_flags.h" #include "base/file_util.h" #include "base/logging.h" +#include "base/strings/string_split.h" #include "base/strings/stringprintf.h" #include "media/base/demuxer.h" #include "media/base/encryption_key_source.h" #include "media/base/muxer_options.h" #include "media/base/muxer_util.h" +#include "media/event/mpd_notify_muxer_listener.h" #include "media/event/vod_media_info_dump_muxer_listener.h" #include "media/file/file.h" #include "media/file/file_closer.h" #include "media/formats/mp4/mp4_muxer.h" +#include "mpd/base/mpd_builder.h" +#include "mpd/base/simple_mpd_notifier.h" namespace { const char kUsage[] = @@ -51,6 +56,25 @@ bool GetSingleMuxerOptions(MuxerOptions* muxer_options) { bool RunPackager(const std::string& input) { Status status; + if (FLAGS_output_media_info && !FLAGS_mpd_output.empty()) { + NOTIMPLEMENTED() << "ERROR: --output_media_info and --mpd_output cannot be " + "enabled together."; + return false; + } + + if (!FLAGS_single_segment) { + if (FLAGS_output_media_info) { + LOG(ERROR) << "ERROR: --output_media_info can be enabled only if " + "--single_segment is true."; + return false; + } + if (!FLAGS_mpd_output.empty() && FLAGS_segment_template.empty()) { + LOG(ERROR) << "ERROR: --segment_template is required for live mpd " + "profile generation."; + return false; + } + } + // Get muxer options from commandline flags. MuxerOptions muxer_options; if (!GetSingleMuxerOptions(&muxer_options)) @@ -93,6 +117,29 @@ bool RunPackager(const std::string& input) { muxer->SetMuxerListener(muxer_listener.get()); } + scoped_ptr mpd_notifier; + if (!FLAGS_mpd_output.empty()) { + dash_packager::DashProfile profile = FLAGS_single_segment + ? dash_packager::kOnDemandProfile + : dash_packager::kLiveProfile; + std::vector base_urls; + base::SplitString(FLAGS_base_urls, ',', &base_urls); + // TODO(rkuroiwa,kqyang): Get mpd options from command line. + mpd_notifier.reset(new dash_packager::SimpleMpdNotifier( + profile, dash_packager::MpdOptions(), base_urls, FLAGS_mpd_output)); + if (!mpd_notifier->Init()) { + LOG(ERROR) << "MpdNotifier failed to initialize."; + return false; + } + + scoped_ptr mpd_notify_muxer_listener( + new event::MpdNotifyMuxerListener(mpd_notifier.get())); + mpd_notify_muxer_listener->SetContentProtectionSchemeIdUri( + FLAGS_scheme_id_uri); + muxer_listener = mpd_notify_muxer_listener.Pass(); + muxer->SetMuxerListener(muxer_listener.get()); + } + if (!AddStreamToMuxer(demuxer.streams(), FLAGS_stream, muxer.get())) return false; diff --git a/packager.gyp b/packager.gyp index a031b3e3bc..985dcc2f7e 100644 --- a/packager.gyp +++ b/packager.gyp @@ -23,6 +23,8 @@ 'app/fixed_key_encryption_flags.h', 'app/libcrypto_threading.cc', 'app/libcrypto_threading.h', + 'app/mpd_flags.cc', + 'app/mpd_flags.h', 'app/muxer_flags.cc', 'app/muxer_flags.h', 'app/packager_common.cc', @@ -38,6 +40,7 @@ 'media/formats/mp2t/mp2t.gyp:mp2t', 'media/formats/mp4/mp4.gyp:mp4', 'media/formats/mpeg/mpeg.gyp:mpeg', + 'mpd/mpd.gyp:mpd_builder', 'third_party/gflags/gflags.gyp:gflags', ], 'conditions': [ @@ -54,6 +57,8 @@ 'sources': [ 'app/fixed_key_encryption_flags.cc', 'app/fixed_key_encryption_flags.h', + 'app/mpd_flags.cc', + 'app/mpd_flags.h', 'app/muxer_flags.cc', 'app/muxer_flags.h', 'app/packager_common.cc', @@ -71,6 +76,7 @@ 'media/formats/mp2t/mp2t.gyp:mp2t', 'media/formats/mp4/mp4.gyp:mp4', 'media/formats/mpeg/mpeg.gyp:mpeg', + 'mpd/mpd.gyp:mpd_builder', 'third_party/gflags/gflags.gyp:gflags', ], },