7 #include "packager/mpd/base/xml/xml_node.h" 9 #include <gflags/gflags.h> 14 #include "packager/base/logging.h" 15 #include "packager/base/macros.h" 16 #include "packager/base/strings/string_number_conversions.h" 17 #include "packager/base/sys_byteorder.h" 18 #include "packager/mpd/base/media_info.pb.h" 19 #include "packager/mpd/base/mpd_utils.h" 20 #include "packager/mpd/base/segment_info.h" 22 DEFINE_bool(segment_template_constant_duration,
24 "Generates SegmentTemplate@duration if all segments except the " 25 "last one has the same duration if this flag is set to true.");
30 typedef MediaInfo::AudioInfo AudioInfo;
31 typedef MediaInfo::VideoInfo VideoInfo;
34 const char kEC3Codec[] =
"ec-3";
36 std::string RangeToString(
const Range& range) {
37 return base::Uint64ToString(range.begin()) +
"-" +
38 base::Uint64ToString(range.end());
43 bool IsTimelineConstantDuration(
const std::list<SegmentInfo>& segment_infos,
44 uint32_t start_number) {
45 if (!FLAGS_segment_template_constant_duration)
48 DCHECK(!segment_infos.empty());
49 if (segment_infos.size() > 2)
52 const SegmentInfo& first_segment = segment_infos.front();
53 if (first_segment.start_time != first_segment.duration * (start_number - 1))
56 if (segment_infos.size() == 1)
59 const SegmentInfo& last_segment = segment_infos.back();
60 if (last_segment.repeat != 0)
63 const int64_t expected_last_segment_start_time =
64 first_segment.start_time +
65 first_segment.duration * (first_segment.repeat + 1);
66 return expected_last_segment_start_time == last_segment.start_time;
69 bool PopulateSegmentTimeline(
const std::list<SegmentInfo>& segment_infos,
70 XmlNode* segment_timeline) {
71 for (
const SegmentInfo& segment_info : segment_infos) {
72 XmlNode s_element(
"S");
73 s_element.SetIntegerAttribute(
"t", segment_info.start_time);
74 s_element.SetIntegerAttribute(
"d", segment_info.duration);
75 if (segment_info.repeat > 0)
76 s_element.SetIntegerAttribute(
"r", segment_info.repeat);
78 CHECK(segment_timeline->AddChild(s_element.PassScopedPtr()));
84 void CollectNamespaceFromName(
const std::string& name,
85 std::set<std::string>* namespaces) {
86 const size_t pos = name.find(
':');
87 if (pos != std::string::npos)
88 namespaces->insert(name.substr(0, pos));
91 void TraverseAttrsAndCollectNamespaces(
const xmlAttr* attr,
92 std::set<std::string>* namespaces) {
93 for (
const xmlAttr* cur_attr = attr; cur_attr; cur_attr = cur_attr->next) {
94 CollectNamespaceFromName(reinterpret_cast<const char*>(cur_attr->name),
99 void TraverseNodesAndCollectNamespaces(
const xmlNode* node,
100 std::set<std::string>* namespaces) {
101 for (
const xmlNode* cur_node = node; cur_node; cur_node = cur_node->next) {
102 CollectNamespaceFromName(reinterpret_cast<const char*>(cur_node->name),
105 TraverseNodesAndCollectNamespaces(cur_node->children, namespaces);
106 TraverseAttrsAndCollectNamespaces(cur_node->properties, namespaces);
119 XmlNode::~XmlNode() {}
124 if (!xmlAddChild(node_.get(), child.get()))
129 ignore_result(child.release());
134 for (
size_t element_index = 0; element_index < elements.size();
136 const Element& child_element = elements[element_index];
137 XmlNode child_node(child_element.name.c_str());
138 for (std::map<std::string, std::string>::const_iterator attribute_it =
139 child_element.attributes.begin();
140 attribute_it != child_element.attributes.end(); ++attribute_it) {
142 attribute_it->second);
147 child_node.SetContent(child_element.content);
150 if (!child_node.AddElements(child_element.subelements))
153 if (!xmlAddChild(node_.get(), child_node.GetRawPtr())) {
154 LOG(ERROR) <<
"Failed to set child " << child_element.name
155 <<
" to parent element " 156 <<
reinterpret_cast<const char*
>(node_->name);
161 ignore_result(child_node.Release());
167 const std::string& attribute) {
169 DCHECK(attribute_name);
170 xmlSetProp(node_.get(), BAD_CAST attribute_name, BAD_CAST attribute.c_str());
175 DCHECK(attribute_name);
176 xmlSetProp(node_.get(),
177 BAD_CAST attribute_name,
178 BAD_CAST (base::Uint64ToString(number).c_str()));
184 DCHECK(attribute_name);
185 xmlSetProp(node_.get(), BAD_CAST attribute_name,
186 BAD_CAST(base::DoubleToString(number).c_str()));
195 xmlNodeSetContent(node_.get(), BAD_CAST content.c_str());
199 std::set<std::string> namespaces;
200 TraverseNodesAndCollectNamespaces(node_.get(), &namespaces);
205 DVLOG(2) <<
"Passing node_.";
207 return std::move(node_);
211 DVLOG(2) <<
"Releasing node_.";
213 return node_.release();
220 RepresentationBaseXmlNode::RepresentationBaseXmlNode(
const char* name)
222 RepresentationBaseXmlNode::~RepresentationBaseXmlNode() {}
224 bool RepresentationBaseXmlNode::AddContentProtectionElements(
225 const std::list<ContentProtectionElement>& content_protection_elements) {
226 std::list<ContentProtectionElement>::const_iterator content_protection_it =
227 content_protection_elements.begin();
228 for (; content_protection_it != content_protection_elements.end();
229 ++content_protection_it) {
230 if (!AddContentProtectionElement(*content_protection_it))
238 const std::string& scheme_id_uri,
239 const std::string& value) {
240 AddDescriptor(
"SupplementalProperty", scheme_id_uri, value);
244 const std::string& scheme_id_uri,
245 const std::string& value) {
246 AddDescriptor(
"EssentialProperty", scheme_id_uri, value);
250 const std::string& descriptor_name,
251 const std::string& scheme_id_uri,
252 const std::string& value) {
253 XmlNode descriptor(descriptor_name.c_str());
256 descriptor.SetStringAttribute(
"value", value);
257 return AddChild(descriptor.PassScopedPtr());
260 bool RepresentationBaseXmlNode::AddContentProtectionElement(
262 XmlNode content_protection_node(
"ContentProtection");
265 if (!content_protection_element.value.empty()) {
267 "value", content_protection_element.value);
270 "schemeIdUri", content_protection_element.scheme_id_uri);
272 typedef std::map<std::string, std::string> AttributesMapType;
273 const AttributesMapType& additional_attributes =
274 content_protection_element.additional_attributes;
276 AttributesMapType::const_iterator attributes_it =
277 additional_attributes.begin();
278 for (; attributes_it != additional_attributes.end(); ++attributes_it) {
280 attributes_it->second);
284 content_protection_element.subelements)) {
290 AdaptationSetXmlNode::AdaptationSetXmlNode()
292 AdaptationSetXmlNode::~AdaptationSetXmlNode() {}
295 const std::string& scheme_id_uri,
296 const std::string& value) {
297 AddDescriptor(
"Accessibility", scheme_id_uri, value);
301 const std::string& value) {
302 AddDescriptor(
"Role", scheme_id_uri, value);
305 RepresentationXmlNode::RepresentationXmlNode()
307 RepresentationXmlNode::~RepresentationXmlNode() {}
312 bool set_frame_rate) {
313 if (!video_info.has_width() || !video_info.has_height()) {
314 LOG(ERROR) <<
"Missing width or height for adding a video info.";
318 if (video_info.has_pixel_width() && video_info.has_pixel_height()) {
321 base::IntToString(video_info.pixel_height()));
328 if (set_frame_rate) {
330 base::IntToString(video_info.time_scale()) +
"/" +
331 base::IntToString(video_info.frame_duration()));
334 if (video_info.has_playback_rate()) {
336 base::IntToString(video_info.playback_rate()));
347 if (!AddAudioChannelInfo(audio_info))
350 AddAudioSamplingRateInfo(audio_info);
355 if (media_info.has_media_file_url()) {
357 base_url.
SetContent(media_info.media_file_url());
363 const bool need_segment_base = media_info.has_index_range() ||
364 media_info.has_init_range() ||
365 media_info.has_reference_time_scale();
367 if (need_segment_base) {
368 XmlNode segment_base(
"SegmentBase");
369 if (media_info.has_index_range()) {
371 RangeToString(media_info.index_range()));
374 if (media_info.has_reference_time_scale()) {
376 media_info.reference_time_scale());
379 if (media_info.has_presentation_time_offset()) {
381 media_info.presentation_time_offset());
384 if (media_info.has_init_range()) {
385 XmlNode initialization(
"Initialization");
387 RangeToString(media_info.init_range()));
401 const MediaInfo& media_info,
402 const std::list<SegmentInfo>& segment_infos,
403 uint32_t start_number) {
404 XmlNode segment_template(
"SegmentTemplate");
405 if (media_info.has_reference_time_scale()) {
407 media_info.reference_time_scale());
410 if (media_info.has_presentation_time_offset()) {
412 media_info.presentation_time_offset());
415 if (media_info.has_init_segment_url()) {
417 media_info.init_segment_url());
420 if (media_info.has_segment_template_url()) {
422 media_info.segment_template_url());
426 if (!segment_infos.empty()) {
429 if (IsTimelineConstantDuration(segment_infos, start_number)) {
431 segment_infos.front().duration);
433 XmlNode segment_timeline(
"SegmentTimeline");
434 if (!PopulateSegmentTimeline(segment_infos, &segment_timeline) ||
443 bool RepresentationXmlNode::AddAudioChannelInfo(
const AudioInfo& audio_info) {
444 std::string audio_channel_config_scheme;
445 std::string audio_channel_config_value;
447 if (audio_info.codec() == kEC3Codec) {
450 const uint16_t ec3_channel_map =
451 base::HostToNet16(audio_info.codec_specific_data().ec3_channel_map());
452 audio_channel_config_value =
453 base::HexEncode(&ec3_channel_map,
sizeof(ec3_channel_map));
454 audio_channel_config_scheme =
455 "tag:dolby.com,2014:dash:audio_channel_configuration:2011";
457 audio_channel_config_value = base::UintToString(audio_info.num_channels());
458 audio_channel_config_scheme =
459 "urn:mpeg:dash:23003:3:audio_channel_configuration:2011";
462 return AddDescriptor(
"AudioChannelConfiguration", audio_channel_config_scheme,
463 audio_channel_config_value);
468 void RepresentationXmlNode::AddAudioSamplingRateInfo(
469 const AudioInfo& audio_info) {
470 if (audio_info.has_sampling_frequency())
bool AddVideoInfo(const MediaInfo::VideoInfo &video_info, bool set_width, bool set_height, bool set_frame_rate)
bool AddDescriptor(const std::string &descriptor_name, const std::string &scheme_id_uri, const std::string &value)
std::set< std::string > ExtractReferencedNamespaces()
void SetFloatingPointAttribute(const char *attribute_name, double number)
scoped_xml_ptr< xmlNode > PassScopedPtr()
XmlNode(const char *name)
All the methods that are virtual are virtual for mocking.
bool AddVODOnlyInfo(const MediaInfo &media_info)
void AddEssentialProperty(const std::string &scheme_id_uri, const std::string &value)
void SetStringAttribute(const char *attribute_name, const std::string &attribute)
bool AddChild(scoped_xml_ptr< xmlNode > child)
bool AddLiveOnlyInfo(const MediaInfo &media_info, const std::list< SegmentInfo > &segment_infos, uint32_t start_number)
void AddAccessibilityElement(const std::string &scheme_id_uri, const std::string &value)
bool AddElements(const std::vector< Element > &elements)
Adds Elements to this node using the Element struct.
void AddSupplementalProperty(const std::string &scheme_id_uri, const std::string &value)
void SetIntegerAttribute(const char *attribute_name, uint64_t number)
void AddRoleElement(const std::string &scheme_id_uri, const std::string &value)
void SetContent(const std::string &content)
bool AddAudioInfo(const MediaInfo::AudioInfo &audio_info)