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_.GetNext(),
135 start_time_in_seconds, mpd_options_,
136 &representation_counter_));
137 return periods_.back().get();
142 static LibXmlInitializer lib_xml_initializer;
144 xml::scoped_xml_ptr<xmlDoc> doc(GenerateMpd());
148 static const int kNiceFormat = 1;
149 int doc_str_size = 0;
150 xmlChar* doc_str =
nullptr;
151 xmlDocDumpFormatMemoryEnc(doc.get(), &doc_str, &doc_str_size,
"UTF-8",
153 output->assign(doc_str, doc_str + doc_str_size);
161 xmlDocPtr MpdBuilder::GenerateMpd() {
163 static const char kXmlVersion[] =
"1.0";
164 xml::scoped_xml_ptr<xmlDoc> doc(xmlNewDoc(BAD_CAST kXmlVersion));
168 for (
const std::string& base_url : base_urls_) {
169 XmlNode xml_base_url(
"BaseURL");
176 bool output_period_duration =
false;
177 if (mpd_options_.mpd_type == MpdType::kStatic) {
178 UpdatePeriodDurationAndPresentationTimestamp();
182 output_period_duration = periods_.size() > 1;
185 for (
const auto& period : periods_) {
186 xml::scoped_xml_ptr<xmlNode> period_node(
187 period->GetXml(output_period_duration));
188 if (!period_node || !mpd.
AddChild(std::move(period_node)))
192 AddMpdNameSpaceInfo(&mpd);
194 static const char kOnDemandProfile[] =
195 "urn:mpeg:dash:profile:isoff-on-demand:2011";
196 static const char kLiveProfile[] =
197 "urn:mpeg:dash:profile:isoff-live:2011";
198 switch (mpd_options_.dash_profile) {
199 case DashProfile::kOnDemand:
202 case DashProfile::kLive:
206 NOTREACHED() <<
"Unknown DASH profile: " 207 <<
static_cast<int>(mpd_options_.dash_profile);
211 AddCommonMpdInfo(&mpd);
212 switch (mpd_options_.mpd_type) {
213 case MpdType::kStatic:
214 AddStaticMpdInfo(&mpd);
216 case MpdType::kDynamic:
217 AddDynamicMpdInfo(&mpd);
222 NOTREACHED() <<
"Unknown MPD type: " 223 <<
static_cast<int>(mpd_options_.mpd_type);
228 const std::string version = GetPackagerVersion();
229 if (!version.empty()) {
230 std::string version_string =
231 base::StringPrintf(
"Generated with %s version %s",
232 GetPackagerProjectUrl().c_str(), version.c_str());
233 xml::scoped_xml_ptr<xmlNode> comment(
234 xmlNewDocComment(doc.get(), BAD_CAST version_string.c_str()));
235 xmlDocSetRootElement(doc.get(), comment.get());
236 xmlAddSibling(comment.release(), mpd.
Release());
238 xmlDocSetRootElement(doc.get(), mpd.
Release());
240 return doc.release();
243 void MpdBuilder::AddCommonMpdInfo(
XmlNode* mpd_node) {
249 LOG(ERROR) <<
"minBufferTime value not specified.";
254 void MpdBuilder::AddStaticMpdInfo(
XmlNode* mpd_node) {
256 DCHECK_EQ(MpdType::kStatic, mpd_options_.mpd_type);
258 static const char kStaticMpdType[] =
"static";
261 SecondsToXmlDuration(GetStaticMpdDuration()));
264 void MpdBuilder::AddDynamicMpdInfo(
XmlNode* mpd_node) {
266 DCHECK_EQ(MpdType::kDynamic, mpd_options_.mpd_type);
268 static const char kDynamicMpdType[] =
"dynamic";
273 XmlDateTimeNowWithOffset(0, clock_.get()));
277 if (availability_start_time_.empty()) {
278 double earliest_presentation_time;
279 if (GetEarliestTimestamp(&earliest_presentation_time)) {
280 availability_start_time_ = XmlDateTimeNowWithOffset(
281 -std::ceil(earliest_presentation_time), clock_.get());
283 LOG(ERROR) <<
"Could not determine the earliest segment presentation " 284 "time for availabilityStartTime calculation.";
288 if (!availability_start_time_.empty())
290 availability_start_time_);
294 "minimumUpdatePeriod",
297 LOG(WARNING) <<
"The profile is dynamic but no minimumUpdatePeriod " 301 SetIfPositive(
"timeShiftBufferDepth",
303 SetIfPositive(
"suggestedPresentationDelay",
304 mpd_options_.mpd_params.suggested_presentation_delay, mpd_node);
307 void MpdBuilder::AddUtcTiming(
XmlNode* mpd_node) {
309 DCHECK_EQ(MpdType::kDynamic, mpd_options_.mpd_type);
312 mpd_options_.mpd_params.utc_timings) {
313 XmlNode utc_timing_node(
"UTCTiming");
320 float MpdBuilder::GetStaticMpdDuration() {
321 DCHECK_EQ(MpdType::kStatic, mpd_options_.mpd_type);
323 float total_duration = 0.0f;
324 for (
const auto& period : periods_) {
325 total_duration += period->duration_seconds();
327 return total_duration;
330 bool MpdBuilder::GetEarliestTimestamp(
double* timestamp_seconds) {
331 DCHECK(timestamp_seconds);
332 DCHECK(!periods_.empty());
333 double timestamp = 0;
334 double earliest_timestamp = -1;
339 for (
const auto* adaptation_set : periods_.front()->GetAdaptationSets()) {
340 for (
const auto* representation : adaptation_set->GetRepresentations()) {
341 if (representation->GetStartAndEndTimestamps(×tamp,
nullptr) &&
342 (earliest_timestamp < 0 || timestamp < earliest_timestamp)) {
343 earliest_timestamp = timestamp;
347 if (earliest_timestamp < 0)
349 *timestamp_seconds = earliest_timestamp;
353 void MpdBuilder::UpdatePeriodDurationAndPresentationTimestamp() {
354 DCHECK_EQ(MpdType::kStatic, mpd_options_.mpd_type);
356 for (
const auto& period : periods_) {
357 std::list<Representation*> video_representations;
358 std::list<Representation*> non_video_representations;
359 for (
const auto& adaptation_set : period->GetAdaptationSets()) {
360 const auto& representations = adaptation_set->GetRepresentations();
361 if (adaptation_set->IsVideo()) {
362 video_representations.insert(video_representations.end(),
363 representations.begin(),
364 representations.end());
366 non_video_representations.insert(non_video_representations.end(),
367 representations.begin(),
368 representations.end());
372 base::Optional<double> earliest_start_time;
373 base::Optional<double> latest_end_time;
375 const auto& representations = video_representations.size() > 0
376 ? video_representations
377 : non_video_representations;
378 for (
const auto& representation : representations) {
379 double start_time = 0;
381 if (representation->GetStartAndEndTimestamps(&start_time, &end_time)) {
382 earliest_start_time =
383 std::min(earliest_start_time.value_or(start_time), start_time);
385 std::max(latest_end_time.value_or(end_time), end_time);
389 if (!earliest_start_time)
392 period->set_duration_seconds(*latest_end_time - *earliest_start_time);
394 double presentation_time_offset = *earliest_start_time;
395 for (
const auto& adaptation_set : period->GetAdaptationSets()) {
396 for (
const auto& representation : adaptation_set->GetRepresentations()) {
397 representation->SetPresentationTimeOffset(presentation_time_offset);
404 MediaInfo* media_info) {
406 const std::string kFileProtocol(
"file://");
407 std::string mpd_file_path = (mpd_path.find(kFileProtocol) == 0)
408 ? mpd_path.substr(kFileProtocol.size())
411 if (!mpd_file_path.empty()) {
412 const FilePath mpd_dir(FilePath::FromUTF8Unsafe(mpd_file_path)
414 .AsEndingWithSeparator());
415 if (!mpd_dir.empty()) {
416 if (media_info->has_media_file_name()) {
417 media_info->set_media_file_url(
418 MakePathRelative(media_info->media_file_name(), mpd_dir));
420 if (media_info->has_init_segment_name()) {
421 media_info->set_init_segment_url(
422 MakePathRelative(media_info->init_segment_name(), mpd_dir));
424 if (media_info->has_segment_template()) {
425 media_info->set_segment_template_url(
426 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