DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerator
xml_node.cc
1 // Copyright 2014 Google Inc. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file or at
5 // https://developers.google.com/open-source/licenses/bsd
6 
7 #include "packager/mpd/base/xml/xml_node.h"
8 
9 #include <limits>
10 #include <set>
11 
12 #include "packager/base/logging.h"
13 #include "packager/base/macros.h"
14 #include "packager/base/stl_util.h"
15 #include "packager/base/strings/string_number_conversions.h"
16 #include "packager/mpd/base/media_info.pb.h"
17 #include "packager/mpd/base/segment_info.h"
18 
20 
21 using edash_packager::MediaInfo;
22 typedef edash_packager::MediaInfo::AudioInfo AudioInfo;
23 typedef edash_packager::MediaInfo::VideoInfo VideoInfo;
24 typedef MediaInfo::ContentProtectionXml ContentProtectionXml;
25 typedef ContentProtectionXml::AttributeNameValuePair AttributeNameValuePair;
26 
27 namespace edash_packager {
28 
29 namespace {
30 
31 std::string RangeToString(const Range& range) {
32  return base::Uint64ToString(range.begin()) + "-" +
33  base::Uint64ToString(range.end());
34 }
35 
36 bool PopulateSegmentTimeline(const std::list<SegmentInfo>& segment_infos,
37  XmlNode* segment_timeline) {
38  for (std::list<SegmentInfo>::const_iterator it = segment_infos.begin();
39  it != segment_infos.end();
40  ++it) {
41  XmlNode s_element("S");
42  s_element.SetIntegerAttribute("t", it->start_time);
43  s_element.SetIntegerAttribute("d", it->duration);
44  if (it->repeat > 0)
45  s_element.SetIntegerAttribute("r", it->repeat);
46 
47  CHECK(segment_timeline->AddChild(s_element.PassScopedPtr()));
48  }
49 
50  return true;
51 }
52 
53 } // namespace
54 
55 namespace xml {
56 
57 XmlNode::XmlNode(const char* name) : node_(xmlNewNode(NULL, BAD_CAST name)) {
58  DCHECK(name);
59  DCHECK(node_);
60 }
61 
62 XmlNode::~XmlNode() {}
63 
64 bool XmlNode::AddChild(scoped_xml_ptr<xmlNode> child) {
65  DCHECK(node_);
66  DCHECK(child);
67  if (!xmlAddChild(node_.get(), child.get()))
68  return false;
69 
70  // Reaching here means the ownership of |child| transfered to |node_|.
71  // Release the pointer so that it doesn't get destructed in this scope.
72  ignore_result(child.release());
73  return true;
74 }
75 
76 bool XmlNode::AddElements(const std::vector<Element>& elements) {
77  for (size_t element_index = 0; element_index < elements.size();
78  ++element_index) {
79  const Element& child_element = elements[element_index];
80  XmlNode child_node(child_element.name.c_str());
81  for (std::map<std::string, std::string>::const_iterator attribute_it =
82  child_element.attributes.begin();
83  attribute_it != child_element.attributes.end(); ++attribute_it) {
84  child_node.SetStringAttribute(attribute_it->first.c_str(),
85  attribute_it->second);
86  }
87  // Recursively set children for the child.
88  if (!child_node.AddElements(child_element.subelements))
89  return false;
90 
91  child_node.SetContent(child_element.content);
92 
93  if (!xmlAddChild(node_.get(), child_node.GetRawPtr())) {
94  LOG(ERROR) << "Failed to set child " << child_element.name
95  << " to parent element "
96  << reinterpret_cast<const char*>(node_->name);
97  return false;
98  }
99  // Reaching here means the ownership of |child_node| transfered to |node_|.
100  // Release the pointer so that it doesn't get destructed in this scope.
101  ignore_result(child_node.Release());
102  }
103  return true;
104 }
105 
106 void XmlNode::SetStringAttribute(const char* attribute_name,
107  const std::string& attribute) {
108  DCHECK(node_);
109  DCHECK(attribute_name);
110  xmlSetProp(node_.get(), BAD_CAST attribute_name, BAD_CAST attribute.c_str());
111 }
112 
113 void XmlNode::SetIntegerAttribute(const char* attribute_name, uint64_t number) {
114  DCHECK(node_);
115  DCHECK(attribute_name);
116  xmlSetProp(node_.get(),
117  BAD_CAST attribute_name,
118  BAD_CAST (base::Uint64ToString(number).c_str()));
119 }
120 
121 void XmlNode::SetFloatingPointAttribute(const char* attribute_name,
122  double number) {
123  DCHECK(node_);
124  DCHECK(attribute_name);
125  xmlSetProp(node_.get(),
126  BAD_CAST attribute_name,
127  BAD_CAST (base::DoubleToString(number).c_str()));
128 }
129 
130 void XmlNode::SetId(uint32_t id) {
131  SetIntegerAttribute("id", id);
132 }
133 
134 void XmlNode::SetContent(const std::string& content) {
135  DCHECK(node_);
136  xmlNodeSetContent(node_.get(), BAD_CAST content.c_str());
137 }
138 
139 scoped_xml_ptr<xmlNode> XmlNode::PassScopedPtr() {
140  DVLOG(2) << "Passing node_.";
141  DCHECK(node_);
142  return node_.Pass();
143 }
144 
145 xmlNodePtr XmlNode::Release() {
146  DVLOG(2) << "Releasing node_.";
147  DCHECK(node_);
148  return node_.release();
149 }
150 
151 xmlNodePtr XmlNode::GetRawPtr() {
152  return node_.get();
153 }
154 
155 RepresentationBaseXmlNode::RepresentationBaseXmlNode(const char* name)
156  : XmlNode(name) {}
157 RepresentationBaseXmlNode::~RepresentationBaseXmlNode() {}
158 
159 bool RepresentationBaseXmlNode::AddContentProtectionElements(
160  const std::list<ContentProtectionElement>& content_protection_elements) {
161  std::list<ContentProtectionElement>::const_iterator content_protection_it =
162  content_protection_elements.begin();
163  for (; content_protection_it != content_protection_elements.end();
164  ++content_protection_it) {
165  if (!AddContentProtectionElement(*content_protection_it))
166  return false;
167  }
168 
169  return true;
170 }
171 
172 bool RepresentationBaseXmlNode::AddContentProtectionElement(
173  const ContentProtectionElement& content_protection_element) {
174  XmlNode content_protection_node("ContentProtection");
175 
176  // @value is an optional attribute.
177  if (!content_protection_element.value.empty()) {
178  content_protection_node.SetStringAttribute(
179  "value", content_protection_element.value);
180  }
181  content_protection_node.SetStringAttribute(
182  "schemeIdUri", content_protection_element.scheme_id_uri);
183 
184  typedef std::map<std::string, std::string> AttributesMapType;
185  const AttributesMapType& additional_attributes =
186  content_protection_element.additional_attributes;
187 
188  AttributesMapType::const_iterator attributes_it =
189  additional_attributes.begin();
190  for (; attributes_it != additional_attributes.end(); ++attributes_it) {
191  content_protection_node.SetStringAttribute(attributes_it->first.c_str(),
192  attributes_it->second);
193  }
194 
195  if (!content_protection_node.AddElements(
196  content_protection_element.subelements)) {
197  return false;
198  }
199  return AddChild(content_protection_node.PassScopedPtr());
200 }
201 
202 AdaptationSetXmlNode::AdaptationSetXmlNode()
203  : RepresentationBaseXmlNode("AdaptationSet") {}
204 AdaptationSetXmlNode::~AdaptationSetXmlNode() {}
205 
206 void AdaptationSetXmlNode::AddRoleElement(const std::string& scheme_id_uri,
207  const std::string& value) {
208  XmlNode role("Role");
209  role.SetStringAttribute("schemeIdUri", scheme_id_uri);
210  role.SetStringAttribute("value", value);
211  AddChild(role.PassScopedPtr());
212 }
213 
214 RepresentationXmlNode::RepresentationXmlNode()
215  : RepresentationBaseXmlNode("Representation") {}
216 RepresentationXmlNode::~RepresentationXmlNode() {}
217 
218 bool RepresentationXmlNode::AddVideoInfo(const VideoInfo& video_info) {
219  if (!video_info.has_width() || !video_info.has_height()) {
220  LOG(ERROR) << "Missing width or height for adding a video info.";
221  return false;
222  }
223 
224  if (video_info.has_pixel_width() && video_info.has_pixel_height()) {
225  SetStringAttribute("sar", base::IntToString(video_info.pixel_width()) +
226  ":" +
227  base::IntToString(video_info.pixel_height()));
228  }
229 
230  SetIntegerAttribute("width", video_info.width());
231  SetIntegerAttribute("height", video_info.height());
232  SetStringAttribute("frameRate",
233  base::IntToString(video_info.time_scale()) + "/" +
234  base::IntToString(video_info.frame_duration()));
235  return true;
236 }
237 
238 bool RepresentationXmlNode::AddAudioInfo(const AudioInfo& audio_info) {
239  if (!AddAudioChannelInfo(audio_info))
240  return false;
241 
242  AddAudioSamplingRateInfo(audio_info);
243  return true;
244 }
245 
246 bool RepresentationXmlNode::AddVODOnlyInfo(const MediaInfo& media_info) {
247  if (media_info.has_media_file_name()) {
248  XmlNode base_url("BaseURL");
249  base_url.SetContent(media_info.media_file_name());
250 
251  if (!AddChild(base_url.PassScopedPtr()))
252  return false;
253  }
254 
255  const bool need_segment_base = media_info.has_index_range() ||
256  media_info.has_init_range() ||
257  media_info.has_reference_time_scale();
258 
259  if (need_segment_base) {
260  XmlNode segment_base("SegmentBase");
261  if (media_info.has_index_range()) {
262  segment_base.SetStringAttribute("indexRange",
263  RangeToString(media_info.index_range()));
264  }
265 
266  if (media_info.has_reference_time_scale()) {
267  segment_base.SetIntegerAttribute("timescale",
268  media_info.reference_time_scale());
269  }
270 
271  if (media_info.has_init_range()) {
272  XmlNode initialization("Initialization");
273  initialization.SetStringAttribute("range",
274  RangeToString(media_info.init_range()));
275 
276  if (!segment_base.AddChild(initialization.PassScopedPtr()))
277  return false;
278  }
279 
280  if (!AddChild(segment_base.PassScopedPtr()))
281  return false;
282  }
283 
284  if (media_info.has_media_duration_seconds()) {
285  // Adding 'duration' attribute, so that this information can be used when
286  // generating one MPD file. This should be removed from the final MPD.
287  SetFloatingPointAttribute("duration", media_info.media_duration_seconds());
288  }
289 
290  return true;
291 }
292 
294  const MediaInfo& media_info,
295  const std::list<SegmentInfo>& segment_infos,
296  uint32_t start_number) {
297  XmlNode segment_template("SegmentTemplate");
298  if (media_info.has_reference_time_scale()) {
299  segment_template.SetIntegerAttribute("timescale",
300  media_info.reference_time_scale());
301  }
302 
303  if (media_info.has_init_segment_name()) {
304  // The spec does not allow '$Number$' and '$Time$' in initialization
305  // attribute.
306  // TODO(rkuroiwa, kqyang): Swap this check out with a better check. These
307  // templates allow formatting as well.
308  const std::string& init_segment_name = media_info.init_segment_name();
309  if (init_segment_name.find("$Number$") != std::string::npos ||
310  init_segment_name.find("$Time$") != std::string::npos) {
311  LOG(ERROR) << "$Number$ and $Time$ cannot be used for "
312  "SegmentTemplate@initialization";
313  return false;
314  }
315  segment_template.SetStringAttribute("initialization",
316  media_info.init_segment_name());
317  }
318 
319  if (media_info.has_segment_template()) {
320  segment_template.SetStringAttribute("media", media_info.segment_template());
321 
322  // TODO(rkuroiwa): Need a better check. $$Number is legitimate but not a
323  // template.
324  if (media_info.segment_template().find("$Number") != std::string::npos) {
325  DCHECK_GE(start_number, 1u);
326  segment_template.SetIntegerAttribute("startNumber", start_number);
327  }
328  }
329 
330  // TODO(rkuroiwa): Find out when a live MPD doesn't require SegmentTimeline.
331  XmlNode segment_timeline("SegmentTimeline");
332 
333  return PopulateSegmentTimeline(segment_infos, &segment_timeline) &&
334  segment_template.AddChild(segment_timeline.PassScopedPtr()) &&
335  AddChild(segment_template.PassScopedPtr());
336 }
337 
338 bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) {
339  const uint32_t num_channels = audio_info.num_channels();
340  XmlNode audio_channel_config("AudioChannelConfiguration");
341  const char kAudioChannelConfigScheme[] =
342  "urn:mpeg:dash:23003:3:audio_channel_configuration:2011";
343  audio_channel_config.SetStringAttribute("schemeIdUri",
344  kAudioChannelConfigScheme);
345  audio_channel_config.SetIntegerAttribute("value", num_channels);
346 
347  return AddChild(audio_channel_config.PassScopedPtr());
348 }
349 
350 // MPD expects one number for sampling frequency, or if it is a range it should
351 // be space separated.
352 void RepresentationXmlNode::AddAudioSamplingRateInfo(
353  const AudioInfo& audio_info) {
354  if (audio_info.has_sampling_frequency())
355  SetIntegerAttribute("audioSamplingRate", audio_info.sampling_frequency());
356 }
357 
358 } // namespace xml
359 } // namespace edash_packager
bool AddVODOnlyInfo(const MediaInfo &media_info)
Definition: xml_node.cc:246
bool AddLiveOnlyInfo(const MediaInfo &media_info, const std::list< SegmentInfo > &segment_infos, uint32_t start_number)
Definition: xml_node.cc:293
void SetStringAttribute(const char *attribute_name, const std::string &attribute)
Definition: xml_node.cc:106
void SetFloatingPointAttribute(const char *attribute_name, double number)
Definition: xml_node.cc:121
bool AddAudioInfo(const MediaInfo::AudioInfo &audio_info)
Definition: xml_node.cc:238
void SetId(uint32_t id)
Definition: xml_node.cc:130
bool AddChild(scoped_xml_ptr< xmlNode > child)
Definition: xml_node.cc:64
XmlNode(const char *name)
Definition: xml_node.cc:57
void SetIntegerAttribute(const char *attribute_name, uint64_t number)
Definition: xml_node.cc:113
bool AddElements(const std::vector< Element > &elements)
Adds Elements to this node using the Element struct.
Definition: xml_node.cc:76
bool AddVideoInfo(const MediaInfo::VideoInfo &video_info)
Definition: xml_node.cc:218
scoped_xml_ptr< xmlNode > PassScopedPtr()
Definition: xml_node.cc:139
void SetContent(const std::string &content)
Definition: xml_node.cc:134
void AddRoleElement(const std::string &scheme_id_uri, const std::string &value)
Definition: xml_node.cc:206