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