DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerator
mpd_utils.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/mpd_utils.h"
8 
9 #include <libxml/tree.h>
10 
11 #include "packager/base/logging.h"
12 #include "packager/base/strings/string_number_conversions.h"
13 #include "packager/mpd/base/xml/scoped_xml_ptr.h"
14 
15 namespace edash_packager {
16 namespace {
17 
18 std::string TextCodecString(
19  const edash_packager::MediaInfo& media_info) {
20  CHECK(media_info.has_text_info());
21  const std::string& format = media_info.text_info().format();
22  // DASH IOP mentions that the codec for ttml in mp4 is stpp.
23  if (format == "ttml" &&
24  (media_info.container_type() == MediaInfo::CONTAINER_MP4)) {
25  return "stpp";
26  }
27 
28  // Otherwise codec doesn't need to be specified, e.g. vtt and ttml+xml are
29  // obvious from the mime type.
30  return "";
31 }
32 
33 } // namespace
34 
35 bool HasVODOnlyFields(const MediaInfo& media_info) {
36  return media_info.has_init_range() || media_info.has_index_range() ||
37  media_info.has_media_file_name() ||
38  media_info.has_media_duration_seconds();
39 }
40 
41 bool HasLiveOnlyFields(const MediaInfo& media_info) {
42  return media_info.has_init_segment_name() ||
43  media_info.has_segment_template() ||
44  media_info.has_segment_duration_seconds();
45 }
46 
47 void RemoveDuplicateAttributes(
48  ContentProtectionElement* content_protection_element) {
49  DCHECK(content_protection_element);
50  typedef std::map<std::string, std::string> AttributesMap;
51 
52  AttributesMap& attributes = content_protection_element->additional_attributes;
53  if (!content_protection_element->value.empty())
54  attributes.erase("value");
55 
56  if (!content_protection_element->scheme_id_uri.empty())
57  attributes.erase("schemeIdUri");
58 }
59 
60 std::string GetCodecs(const MediaInfo& media_info) {
61  CHECK(OnlyOneTrue(media_info.has_video_info(), media_info.has_audio_info(),
62  media_info.has_text_info()));
63 
64  if (media_info.has_video_info())
65  return media_info.video_info().codec();
66 
67  if (media_info.has_audio_info())
68  return media_info.audio_info().codec();
69 
70  if (media_info.has_text_info())
71  return TextCodecString(media_info);
72 
73  NOTREACHED();
74  return "";
75 }
76 
77 std::string SecondsToXmlDuration(double seconds) {
78  return "PT" + base::DoubleToString(seconds) + "S";
79 }
80 
81 bool GetDurationAttribute(xmlNodePtr node, float* duration) {
82  DCHECK(node);
83  DCHECK(duration);
84  static const char kDuration[] = "duration";
85  xml::ScopedXmlPtr<xmlChar>::type duration_value(
86  xmlGetProp(node, BAD_CAST kDuration));
87 
88  if (!duration_value)
89  return false;
90 
91  double duration_double_precision = 0.0;
92  if (!base::StringToDouble(reinterpret_cast<const char*>(duration_value.get()),
93  &duration_double_precision)) {
94  return false;
95  }
96 
97  *duration = static_cast<float>(duration_double_precision);
98  return true;
99 }
100 
101 bool MoreThanOneTrue(bool b1, bool b2, bool b3) {
102  return (b1 && b2) || (b2 && b3) || (b3 && b1);
103 }
104 
105 bool AtLeastOneTrue(bool b1, bool b2, bool b3) { return b1 || b2 || b3; }
106 
107 bool OnlyOneTrue(bool b1, bool b2, bool b3) {
108  return !MoreThanOneTrue(b1, b2, b3) && AtLeastOneTrue(b1, b2, b3);
109 }
110 
111 // Coverts binary data into human readable UUID format.
112 bool HexToUUID(const std::string& data, std::string* uuid_format) {
113  DCHECK(uuid_format);
114  const size_t kExpectedUUIDSize = 16;
115  if (data.size() != kExpectedUUIDSize) {
116  LOG(ERROR) << "UUID size is expected to be " << kExpectedUUIDSize
117  << " but is " << data.size() << " and the data in hex is "
118  << base::HexEncode(data.data(), data.size());
119  return false;
120  }
121 
122  const std::string hex_encoded =
123  base::StringToLowerASCII(base::HexEncode(data.data(), data.size()));
124  DCHECK_EQ(hex_encoded.size(), kExpectedUUIDSize * 2);
125  base::StringPiece all(hex_encoded);
126  // Note UUID has 5 parts separated with dashes.
127  // e.g. 123e4567-e89b-12d3-a456-426655440000
128  // These StringPieces have each part.
129  base::StringPiece first = all.substr(0, 8);
130  base::StringPiece second = all.substr(8, 4);
131  base::StringPiece third = all.substr(12, 4);
132  base::StringPiece fourth = all.substr(16, 4);
133  base::StringPiece fifth = all.substr(20, 12);
134 
135  // 32 hexadecimal characters with 4 hyphens.
136  const size_t kHumanReadableUUIDSize = 36;
137  uuid_format->reserve(kHumanReadableUUIDSize);
138  first.CopyToString(uuid_format);
139  uuid_format->append("-");
140  second.AppendToString(uuid_format);
141  uuid_format->append("-");
142  third.AppendToString(uuid_format);
143  uuid_format->append("-");
144  fourth.AppendToString(uuid_format);
145  uuid_format->append("-");
146  fifth.AppendToString(uuid_format);
147  return true;
148 }
149 
150 void UpdateContentProtectionPsshHelper(
151  const std::string& drm_uuid,
152  const std::string& pssh,
153  std::list<ContentProtectionElement>* content_protection_elements) {
154  const std::string drm_uuid_schemd_id_uri_form = "urn:uuid:" + drm_uuid;
155  for (std::list<ContentProtectionElement>::iterator protection =
156  content_protection_elements->begin();
157  protection != content_protection_elements->end(); ++protection) {
158  if (protection->scheme_id_uri != drm_uuid_schemd_id_uri_form) {
159  continue;
160  }
161 
162  for (std::vector<Element>::iterator subelement =
163  protection->subelements.begin();
164  subelement != protection->subelements.end(); ++subelement) {
165  if (subelement->name == kPsshElementName) {
166  // For now, we want to remove the PSSH element because some players do
167  // not support updating pssh.
168  protection->subelements.erase(subelement);
169 
170  // TODO(rkuroiwa): Uncomment this and remove the line above when
171  // shaka-player supports updating PSSH.
172  // subelement->content = pssh;
173  return;
174  }
175  }
176 
177  // Reaching here means <cenc:pssh> does not exist under the
178  // ContentProtection element. Add it.
179  // TODO(rkuroiwa): Uncomment this when shaka-player supports updating PSSH.
180  // Element cenc_pssh;
181  // cenc_pssh.name = kPsshElementName;
182  // cenc_pssh.content = pssh;
183  // protection->subelements.push_back(cenc_pssh);
184  return;
185  }
186 
187  // Reaching here means that ContentProtection for the DRM does not exist.
188  // Add it.
189  ContentProtectionElement content_protection;
190  content_protection.scheme_id_uri = drm_uuid_schemd_id_uri_form;
191  // TODO(rkuroiwa): Uncomment this when shaka-player supports updating PSSH.
192  // Element cenc_pssh;
193  // cenc_pssh.name = kPsshElementName;
194  // cenc_pssh.content = pssh;
195  // content_protection.subelements.push_back(cenc_pssh);
196  content_protection_elements->push_back(content_protection);
197  return;
198 }
199 
200 namespace {
201 // Helper function. This works because Representation and AdaptationSet both
202 // have AddContentProtectionElement().
203 template <typename ContentProtectionParent>
204 void AddContentProtectionElementsHelperTemplated(
205  const MediaInfo& media_info,
206  ContentProtectionParent* parent) {
207  DCHECK(parent);
208  if (!media_info.has_protected_content())
209  return;
210 
211  const MediaInfo::ProtectedContent& protected_content =
212  media_info.protected_content();
213 
214  // DASH MPD spec specifies a default ContentProtection element for ISO BMFF
215  // (MP4) files.
216  const bool is_mp4_container =
217  media_info.container_type() == MediaInfo::CONTAINER_MP4;
218  if (is_mp4_container) {
219  ContentProtectionElement mp4_content_protection;
220  mp4_content_protection.scheme_id_uri = kEncryptedMp4Scheme;
221  mp4_content_protection.value = kEncryptedMp4Value;
222  if (protected_content.has_default_key_id()) {
223  std::string key_id_uuid_format;
224  if (HexToUUID(protected_content.default_key_id(), &key_id_uuid_format)) {
225  mp4_content_protection.additional_attributes["cenc:default_KID"] =
226  key_id_uuid_format;
227  } else {
228  LOG(ERROR) << "Failed to convert default key ID into UUID format.";
229  }
230  }
231 
232  parent->AddContentProtectionElement(mp4_content_protection);
233  }
234 
235  for (int i = 0; i < protected_content.content_protection_entry().size();
236  ++i) {
237  const MediaInfo::ProtectedContent::ContentProtectionEntry& entry =
238  protected_content.content_protection_entry(i);
239  if (!entry.has_uuid()) {
240  LOG(WARNING)
241  << "ContentProtectionEntry was specified but no UUID is set for "
242  << entry.name_version() << ", skipping.";
243  continue;
244  }
245 
246  ContentProtectionElement drm_content_protection;
247  drm_content_protection.scheme_id_uri = "urn:uuid:" + entry.uuid();
248  if (entry.has_name_version())
249  drm_content_protection.value = entry.name_version();
250 
251  if (entry.has_pssh()) {
252  std::string base64_encoded_pssh;
253  base::Base64Encode(entry.pssh(), &base64_encoded_pssh);
254  Element cenc_pssh;
255  cenc_pssh.name = kPsshElementName;
256  cenc_pssh.content = base64_encoded_pssh;
257  drm_content_protection.subelements.push_back(cenc_pssh);
258  }
259 
260  parent->AddContentProtectionElement(drm_content_protection);
261  }
262 
263  LOG_IF(WARNING, protected_content.content_protection_entry().size() == 0)
264  << "The media is encrypted but no content protection specified.";
265 }
266 } // namespace
267 
268 void AddContentProtectionElements(const MediaInfo& media_info,
269  Representation* parent) {
270  AddContentProtectionElementsHelperTemplated(media_info, parent);
271 }
272 
273 void AddContentProtectionElements(const MediaInfo& media_info,
274  AdaptationSet* parent) {
275  AddContentProtectionElementsHelperTemplated(media_info, parent);
276 }
277 
278 
279 } // namespace edash_packager
void AddContentProtectionElements(const MediaInfo &media_info, Representation *parent)
Definition: mpd_utils.cc:268
bool HexToUUID(const std::string &data, std::string *uuid_format)
Definition: mpd_utils.cc:112