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  bool set_width,
220  bool set_height,
221  bool set_frame_rate) {
222  if (!video_info.has_width() || !video_info.has_height()) {
223  LOG(ERROR) << "Missing width or height for adding a video info.";
224  return false;
225  }
226 
227  if (video_info.has_pixel_width() && video_info.has_pixel_height()) {
228  SetStringAttribute("sar", base::IntToString(video_info.pixel_width()) +
229  ":" +
230  base::IntToString(video_info.pixel_height()));
231  }
232 
233  if (set_width)
234  SetIntegerAttribute("width", video_info.width());
235  if (set_height)
236  SetIntegerAttribute("height", video_info.height());
237  if (set_frame_rate) {
238  SetStringAttribute("frameRate",
239  base::IntToString(video_info.time_scale()) + "/" +
240  base::IntToString(video_info.frame_duration()));
241  }
242  return true;
243 }
244 
245 bool RepresentationXmlNode::AddAudioInfo(const AudioInfo& audio_info) {
246  if (!AddAudioChannelInfo(audio_info))
247  return false;
248 
249  AddAudioSamplingRateInfo(audio_info);
250  return true;
251 }
252 
253 bool RepresentationXmlNode::AddVODOnlyInfo(const MediaInfo& media_info) {
254  if (media_info.has_media_file_name()) {
255  XmlNode base_url("BaseURL");
256  base_url.SetContent(media_info.media_file_name());
257 
258  if (!AddChild(base_url.PassScopedPtr()))
259  return false;
260  }
261 
262  const bool need_segment_base = media_info.has_index_range() ||
263  media_info.has_init_range() ||
264  media_info.has_reference_time_scale();
265 
266  if (need_segment_base) {
267  XmlNode segment_base("SegmentBase");
268  if (media_info.has_index_range()) {
269  segment_base.SetStringAttribute("indexRange",
270  RangeToString(media_info.index_range()));
271  }
272 
273  if (media_info.has_reference_time_scale()) {
274  segment_base.SetIntegerAttribute("timescale",
275  media_info.reference_time_scale());
276  }
277 
278  if (media_info.has_init_range()) {
279  XmlNode initialization("Initialization");
280  initialization.SetStringAttribute("range",
281  RangeToString(media_info.init_range()));
282 
283  if (!segment_base.AddChild(initialization.PassScopedPtr()))
284  return false;
285  }
286 
287  if (!AddChild(segment_base.PassScopedPtr()))
288  return false;
289  }
290 
291  if (media_info.has_media_duration_seconds()) {
292  // Adding 'duration' attribute, so that this information can be used when
293  // generating one MPD file. This should be removed from the final MPD.
294  SetFloatingPointAttribute("duration", media_info.media_duration_seconds());
295  }
296 
297  return true;
298 }
299 
301  const MediaInfo& media_info,
302  const std::list<SegmentInfo>& segment_infos,
303  uint32_t start_number) {
304  XmlNode segment_template("SegmentTemplate");
305  if (media_info.has_reference_time_scale()) {
306  segment_template.SetIntegerAttribute("timescale",
307  media_info.reference_time_scale());
308  }
309 
310  if (media_info.has_init_segment_name()) {
311  // The spec does not allow '$Number$' and '$Time$' in initialization
312  // attribute.
313  // TODO(rkuroiwa, kqyang): Swap this check out with a better check. These
314  // templates allow formatting as well.
315  const std::string& init_segment_name = media_info.init_segment_name();
316  if (init_segment_name.find("$Number$") != std::string::npos ||
317  init_segment_name.find("$Time$") != std::string::npos) {
318  LOG(ERROR) << "$Number$ and $Time$ cannot be used for "
319  "SegmentTemplate@initialization";
320  return false;
321  }
322  segment_template.SetStringAttribute("initialization",
323  media_info.init_segment_name());
324  }
325 
326  if (media_info.has_segment_template()) {
327  segment_template.SetStringAttribute("media", media_info.segment_template());
328 
329  // TODO(rkuroiwa): Need a better check. $$Number is legitimate but not a
330  // template.
331  if (media_info.segment_template().find("$Number") != std::string::npos) {
332  DCHECK_GE(start_number, 1u);
333  segment_template.SetIntegerAttribute("startNumber", start_number);
334  }
335  }
336 
337  // TODO(rkuroiwa): Find out when a live MPD doesn't require SegmentTimeline.
338  XmlNode segment_timeline("SegmentTimeline");
339 
340  return PopulateSegmentTimeline(segment_infos, &segment_timeline) &&
341  segment_template.AddChild(segment_timeline.PassScopedPtr()) &&
342  AddChild(segment_template.PassScopedPtr());
343 }
344 
345 bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) {
346  const uint32_t num_channels = audio_info.num_channels();
347  XmlNode audio_channel_config("AudioChannelConfiguration");
348  const char kAudioChannelConfigScheme[] =
349  "urn:mpeg:dash:23003:3:audio_channel_configuration:2011";
350  audio_channel_config.SetStringAttribute("schemeIdUri",
351  kAudioChannelConfigScheme);
352  audio_channel_config.SetIntegerAttribute("value", num_channels);
353 
354  return AddChild(audio_channel_config.PassScopedPtr());
355 }
356 
357 // MPD expects one number for sampling frequency, or if it is a range it should
358 // be space separated.
359 void RepresentationXmlNode::AddAudioSamplingRateInfo(
360  const AudioInfo& audio_info) {
361  if (audio_info.has_sampling_frequency())
362  SetIntegerAttribute("audioSamplingRate", audio_info.sampling_frequency());
363 }
364 
365 } // namespace xml
366 } // namespace edash_packager
bool AddVODOnlyInfo(const MediaInfo &media_info)
Definition: xml_node.cc:253
bool AddLiveOnlyInfo(const MediaInfo &media_info, const std::list< SegmentInfo > &segment_infos, uint32_t start_number)
Definition: xml_node.cc:300
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:245
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
scoped_xml_ptr< xmlNode > PassScopedPtr()
Definition: xml_node.cc:139
void SetContent(const std::string &content)
Definition: xml_node.cc:134
bool AddVideoInfo(const MediaInfo::VideoInfo &video_info, bool set_width, bool set_height, bool set_frame_rate)
Definition: xml_node.cc:218
void AddRoleElement(const std::string &scheme_id_uri, const std::string &value)
Definition: xml_node.cc:206