7 #include "packager/mpd/base/mpd_builder.h" 11 #include "packager/base/files/file_path.h" 12 #include "packager/base/logging.h" 13 #include "packager/base/optional.h" 14 #include "packager/base/strings/string_number_conversions.h" 15 #include "packager/base/strings/stringprintf.h" 16 #include "packager/base/synchronization/lock.h" 17 #include "packager/base/time/default_clock.h" 18 #include "packager/base/time/time.h" 19 #include "packager/mpd/base/adaptation_set.h" 20 #include "packager/mpd/base/mpd_utils.h" 21 #include "packager/mpd/base/period.h" 22 #include "packager/mpd/base/representation.h" 23 #include "packager/mpd/base/xml/xml_node.h" 24 #include "packager/version/version.h" 33 void AddMpdNameSpaceInfo(XmlNode* mpd) {
36 static const char kXmlNamespace[] =
"urn:mpeg:dash:schema:mpd:2011";
37 static const char kXmlNamespaceXsi[] =
38 "http://www.w3.org/2001/XMLSchema-instance";
39 static const char kXmlNamespaceXlink[] =
"http://www.w3.org/1999/xlink";
40 static const char kDashSchemaMpd2011[] =
41 "urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd";
42 static const char kCencNamespace[] =
"urn:mpeg:cenc:2013";
44 mpd->SetStringAttribute(
"xmlns", kXmlNamespace);
45 mpd->SetStringAttribute(
"xmlns:xsi", kXmlNamespaceXsi);
46 mpd->SetStringAttribute(
"xmlns:xlink", kXmlNamespaceXlink);
47 mpd->SetStringAttribute(
"xsi:schemaLocation", kDashSchemaMpd2011);
48 mpd->SetStringAttribute(
"xmlns:cenc", kCencNamespace);
51 bool Positive(
double d) {
57 std::string XmlDateTimeNowWithOffset(
58 int32_t offset_seconds,
60 base::Time time = clock->Now();
61 time += base::TimeDelta::FromSeconds(offset_seconds);
62 base::Time::Exploded time_exploded;
63 time.UTCExplode(&time_exploded);
65 return base::StringPrintf(
"%4d-%02d-%02dT%02d:%02d:%02dZ", time_exploded.year,
66 time_exploded.month, time_exploded.day_of_month,
67 time_exploded.hour, time_exploded.minute,
68 time_exploded.second);
71 void SetIfPositive(
const char* attr_name,
double value, XmlNode* mpd) {
72 if (Positive(value)) {
73 mpd->SetStringAttribute(attr_name, SecondsToXmlDuration(value));
77 std::string MakePathRelative(
const std::string& media_path,
78 const FilePath& parent_path) {
79 FilePath relative_path;
80 const FilePath child_path = FilePath::FromUTF8Unsafe(media_path);
82 parent_path.AppendRelativePath(child_path, &relative_path);
84 relative_path = child_path;
85 return relative_path.NormalizePathSeparatorsTo(
'/').AsUTF8Unsafe();
89 class LibXmlInitializer {
91 LibXmlInitializer() : initialized_(false) {
92 base::AutoLock lock(lock_);
99 ~LibXmlInitializer() {
100 base::AutoLock lock(lock_);
103 initialized_ =
false;
111 DISALLOW_COPY_AND_ASSIGN(LibXmlInitializer);
117 : mpd_options_(mpd_options), clock_(new
base::DefaultClock()) {}
119 MpdBuilder::~MpdBuilder() {}
122 base_urls_.push_back(base_url);
126 for (
auto& period : periods_) {
127 const double kPeriodTimeDriftThresholdInSeconds = 1.0;
129 std::fabs(period->start_time_in_seconds() - start_time_in_seconds) <
130 kPeriodTimeDriftThresholdInSeconds;
134 periods_.emplace_back(
new Period(period_counter_++, start_time_in_seconds,
135 mpd_options_, &representation_counter_));
136 return periods_.back().get();
141 static LibXmlInitializer lib_xml_initializer;
143 xml::scoped_xml_ptr<xmlDoc> doc(GenerateMpd());
147 static const int kNiceFormat = 1;
148 int doc_str_size = 0;
149 xmlChar* doc_str =
nullptr;
150 xmlDocDumpFormatMemoryEnc(doc.get(), &doc_str, &doc_str_size,
"UTF-8",
152 output->assign(doc_str, doc_str + doc_str_size);
160 xmlDocPtr MpdBuilder::GenerateMpd() {
162 static const char kXmlVersion[] =
"1.0";
163 xml::scoped_xml_ptr<xmlDoc> doc(xmlNewDoc(BAD_CAST kXmlVersion));
167 for (
const std::string& base_url : base_urls_) {
168 XmlNode xml_base_url(
"BaseURL");
175 bool output_period_duration =
false;
176 if (mpd_options_.mpd_type == MpdType::kStatic) {
177 UpdatePeriodDurationAndPresentationTimestamp();
181 output_period_duration = periods_.size() > 1;
184 for (
const auto& period : periods_) {
185 xml::scoped_xml_ptr<xmlNode> period_node(
186 period->GetXml(output_period_duration));
187 if (!period_node || !mpd.
AddChild(std::move(period_node)))
191 AddMpdNameSpaceInfo(&mpd);
193 static const char kOnDemandProfile[] =
194 "urn:mpeg:dash:profile:isoff-on-demand:2011";
195 static const char kLiveProfile[] =
196 "urn:mpeg:dash:profile:isoff-live:2011";
197 switch (mpd_options_.dash_profile) {
198 case DashProfile::kOnDemand:
201 case DashProfile::kLive:
205 NOTREACHED() <<
"Unknown DASH profile: " 206 <<
static_cast<int>(mpd_options_.dash_profile);
210 AddCommonMpdInfo(&mpd);
211 switch (mpd_options_.mpd_type) {
212 case MpdType::kStatic:
213 AddStaticMpdInfo(&mpd);
215 case MpdType::kDynamic:
216 AddDynamicMpdInfo(&mpd);
221 NOTREACHED() <<
"Unknown MPD type: " 222 <<
static_cast<int>(mpd_options_.mpd_type);
227 const std::string version = GetPackagerVersion();
228 if (!version.empty()) {
229 std::string version_string =
230 base::StringPrintf(
"Generated with %s version %s",
231 GetPackagerProjectUrl().c_str(), version.c_str());
232 xml::scoped_xml_ptr<xmlNode> comment(
233 xmlNewDocComment(doc.get(), BAD_CAST version_string.c_str()));
234 xmlDocSetRootElement(doc.get(), comment.get());
235 xmlAddSibling(comment.release(), mpd.
Release());
237 xmlDocSetRootElement(doc.get(), mpd.
Release());
239 return doc.release();
242 void MpdBuilder::AddCommonMpdInfo(
XmlNode* mpd_node) {
248 LOG(ERROR) <<
"minBufferTime value not specified.";
253 void MpdBuilder::AddStaticMpdInfo(
XmlNode* mpd_node) {
255 DCHECK_EQ(MpdType::kStatic, mpd_options_.mpd_type);
257 static const char kStaticMpdType[] =
"static";
260 SecondsToXmlDuration(GetStaticMpdDuration()));
263 void MpdBuilder::AddDynamicMpdInfo(
XmlNode* mpd_node) {
265 DCHECK_EQ(MpdType::kDynamic, mpd_options_.mpd_type);
267 static const char kDynamicMpdType[] =
"dynamic";
272 XmlDateTimeNowWithOffset(0, clock_.get()));
276 if (availability_start_time_.empty()) {
277 double earliest_presentation_time;
278 if (GetEarliestTimestamp(&earliest_presentation_time)) {
279 availability_start_time_ = XmlDateTimeNowWithOffset(
280 -std::ceil(earliest_presentation_time), clock_.get());
282 LOG(ERROR) <<
"Could not determine the earliest segment presentation " 283 "time for availabilityStartTime calculation.";
287 if (!availability_start_time_.empty())
289 availability_start_time_);
293 "minimumUpdatePeriod",
296 LOG(WARNING) <<
"The profile is dynamic but no minimumUpdatePeriod " 300 SetIfPositive(
"timeShiftBufferDepth",
302 SetIfPositive(
"suggestedPresentationDelay",
303 mpd_options_.mpd_params.suggested_presentation_delay, mpd_node);
306 void MpdBuilder::AddUtcTiming(
XmlNode* mpd_node) {
308 DCHECK_EQ(MpdType::kDynamic, mpd_options_.mpd_type);
311 mpd_options_.mpd_params.utc_timings) {
312 XmlNode utc_timing_node(
"UTCTiming");
319 float MpdBuilder::GetStaticMpdDuration() {
320 DCHECK_EQ(MpdType::kStatic, mpd_options_.mpd_type);
322 float total_duration = 0.0f;
323 for (
const auto& period : periods_) {
324 total_duration += period->duration_seconds();
326 return total_duration;
329 bool MpdBuilder::GetEarliestTimestamp(
double* timestamp_seconds) {
330 DCHECK(timestamp_seconds);
331 DCHECK(!periods_.empty());
332 double timestamp = 0;
333 double earliest_timestamp = -1;
338 for (
const auto* adaptation_set : periods_.front()->GetAdaptationSets()) {
339 for (
const auto* representation : adaptation_set->GetRepresentations()) {
340 if (representation->GetStartAndEndTimestamps(×tamp,
nullptr) &&
341 (earliest_timestamp < 0 || timestamp < earliest_timestamp)) {
342 earliest_timestamp = timestamp;
346 if (earliest_timestamp < 0)
348 *timestamp_seconds = earliest_timestamp;
352 void MpdBuilder::UpdatePeriodDurationAndPresentationTimestamp() {
353 DCHECK_EQ(MpdType::kStatic, mpd_options_.mpd_type);
355 for (
const auto& period : periods_) {
356 std::list<Representation*> video_representations;
357 std::list<Representation*> non_video_representations;
358 for (
const auto& adaptation_set : period->GetAdaptationSets()) {
359 const auto& representations = adaptation_set->GetRepresentations();
360 if (adaptation_set->IsVideo()) {
361 video_representations.insert(video_representations.end(),
362 representations.begin(),
363 representations.end());
365 non_video_representations.insert(non_video_representations.end(),
366 representations.begin(),
367 representations.end());
371 base::Optional<double> earliest_start_time;
372 base::Optional<double> latest_end_time;
374 const auto& representations = video_representations.size() > 0
375 ? video_representations
376 : non_video_representations;
377 for (
const auto& representation : representations) {
378 double start_time = 0;
380 if (representation->GetStartAndEndTimestamps(&start_time, &end_time)) {
381 earliest_start_time =
382 std::min(earliest_start_time.value_or(start_time), start_time);
384 std::max(latest_end_time.value_or(end_time), end_time);
388 if (!earliest_start_time)
391 period->set_duration_seconds(*latest_end_time - *earliest_start_time);
393 double presentation_time_offset = *earliest_start_time;
394 for (
const auto& adaptation_set : period->GetAdaptationSets()) {
395 for (
const auto& representation : adaptation_set->GetRepresentations()) {
396 representation->SetPresentationTimeOffset(presentation_time_offset);
403 MediaInfo* media_info) {
405 const std::string kFileProtocol(
"file://");
406 std::string mpd_file_path = (mpd_path.find(kFileProtocol) == 0)
407 ? mpd_path.substr(kFileProtocol.size())
410 if (!mpd_file_path.empty()) {
411 const FilePath mpd_dir(FilePath::FromUTF8Unsafe(mpd_file_path)
413 .AsEndingWithSeparator());
414 if (!mpd_dir.empty()) {
415 if (media_info->has_media_file_name()) {
416 media_info->set_media_file_url(
417 MakePathRelative(media_info->media_file_name(), mpd_dir));
419 if (media_info->has_init_segment_name()) {
420 media_info->set_init_segment_url(
421 MakePathRelative(media_info->init_segment_name(), mpd_dir));
423 if (media_info->has_segment_template()) {
424 media_info->set_segment_template_url(
425 MakePathRelative(media_info->segment_template(), mpd_dir));
UTCTimings. For dynamic MPD only.
scoped_xml_ptr< xmlNode > PassScopedPtr()
All the methods that are virtual are virtual for mocking.
void SetStringAttribute(const char *attribute_name, const std::string &attribute)
bool AddChild(scoped_xml_ptr< xmlNode > child)
virtual Period * GetOrCreatePeriod(double start_time_in_seconds)
MpdBuilder(const MpdOptions &mpd_options)
void AddBaseUrl(const std::string &base_url)
virtual bool ToString(std::string *output)
static void MakePathsRelativeToMpd(const std::string &mpd_path, MediaInfo *media_info)
double minimum_update_period
void SetContent(const std::string &content)
double time_shift_buffer_depth