DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations 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 shaka {
16 namespace {
17 
18 std::string TextCodecString(const MediaInfo& media_info) {
19  CHECK(media_info.has_text_info());
20  const std::string& format = media_info.text_info().format();
21  // DASH IOP mentions that the codec for ttml in mp4 is stpp.
22  if (format == "ttml" &&
23  (media_info.container_type() == MediaInfo::CONTAINER_MP4)) {
24  return "stpp";
25  }
26 
27  // Otherwise codec doesn't need to be specified, e.g. vtt and ttml+xml are
28  // obvious from the mime type.
29  return "";
30 }
31 
32 } // namespace
33 
34 bool HasVODOnlyFields(const MediaInfo& media_info) {
35  return media_info.has_init_range() || media_info.has_index_range() ||
36  media_info.has_media_file_name();
37 }
38 
39 bool HasLiveOnlyFields(const MediaInfo& media_info) {
40  return media_info.has_init_segment_name() ||
41  media_info.has_segment_template() ||
42  media_info.has_segment_duration_seconds();
43 }
44 
45 void RemoveDuplicateAttributes(
46  ContentProtectionElement* content_protection_element) {
47  DCHECK(content_protection_element);
48  typedef std::map<std::string, std::string> AttributesMap;
49 
50  AttributesMap& attributes = content_protection_element->additional_attributes;
51  if (!content_protection_element->value.empty())
52  attributes.erase("value");
53 
54  if (!content_protection_element->scheme_id_uri.empty())
55  attributes.erase("schemeIdUri");
56 }
57 
58 std::string GetLanguage(const MediaInfo& media_info) {
59  std::string lang;
60  if (media_info.has_audio_info()) {
61  lang = media_info.audio_info().language();
62  } else if (media_info.has_text_info()) {
63  lang = media_info.text_info().language();
64  }
65  return lang;
66 }
67 
68 std::string GetCodecs(const MediaInfo& media_info) {
69  CHECK(OnlyOneTrue(media_info.has_video_info(), media_info.has_audio_info(),
70  media_info.has_text_info()));
71 
72  if (media_info.has_video_info()) {
73  if (media_info.container_type() == MediaInfo::CONTAINER_WEBM) {
74  std::string codec = media_info.video_info().codec().substr(0, 4);
75  // media_info.video_info().codec() contains new revised codec string
76  // specified by "VPx in ISO BMFF" document, which is not compatible to
77  // old codec strings in WebM. Hack it here before all browsers support
78  // new codec strings.
79  if (codec == "vp08")
80  return "vp8";
81  if (codec == "vp09")
82  return "vp9";
83  }
84  return media_info.video_info().codec();
85  }
86 
87  if (media_info.has_audio_info())
88  return media_info.audio_info().codec();
89 
90  if (media_info.has_text_info())
91  return TextCodecString(media_info);
92 
93  NOTREACHED();
94  return "";
95 }
96 
97 std::string GetBaseCodec(const MediaInfo& media_info) {
98  std::string codec;
99  if (media_info.has_video_info()) {
100  codec = media_info.video_info().codec();
101  } else if (media_info.has_audio_info()) {
102  codec = media_info.audio_info().codec();
103  } else if (media_info.has_text_info()) {
104  codec = media_info.text_info().format();
105  }
106  // Convert, for example, "mp4a.40.2" to simply "mp4a".
107  // "mp4a.40.2" and "mp4a.40.5" can exist in the same AdaptationSet.
108  size_t dot = codec.find('.');
109  if (dot != std::string::npos) {
110  codec.erase(dot);
111  }
112  return codec;
113 }
114 
115 std::string GetAdaptationSetKey(const MediaInfo& media_info) {
116  std::string key;
117 
118  if (media_info.has_video_info()) {
119  key.append("video:");
120  } else if (media_info.has_audio_info()) {
121  key.append("audio:");
122  } else if (media_info.has_text_info()) {
123  key.append(MediaInfo_TextInfo_TextType_Name(media_info.text_info().type()));
124  key.append(":");
125  } else {
126  key.append("unknown:");
127  }
128 
129  key.append(MediaInfo_ContainerType_Name(media_info.container_type()));
130  key.append(":");
131  key.append(GetBaseCodec(media_info));
132  key.append(":");
133  key.append(GetLanguage(media_info));
134 
135  return key;
136 }
137 
138 std::string SecondsToXmlDuration(double seconds) {
139  return "PT" + DoubleToString(seconds) + "S";
140 }
141 
142 bool GetDurationAttribute(xmlNodePtr node, float* duration) {
143  DCHECK(node);
144  DCHECK(duration);
145  static const char kDuration[] = "duration";
146  xml::scoped_xml_ptr<xmlChar> duration_value(
147  xmlGetProp(node, BAD_CAST kDuration));
148 
149  if (!duration_value)
150  return false;
151 
152  double duration_double_precision = 0.0;
153  if (!base::StringToDouble(reinterpret_cast<const char*>(duration_value.get()),
154  &duration_double_precision)) {
155  return false;
156  }
157 
158  *duration = static_cast<float>(duration_double_precision);
159  return true;
160 }
161 
162 bool MoreThanOneTrue(bool b1, bool b2, bool b3) {
163  return (b1 && b2) || (b2 && b3) || (b3 && b1);
164 }
165 
166 bool AtLeastOneTrue(bool b1, bool b2, bool b3) { return b1 || b2 || b3; }
167 
168 bool OnlyOneTrue(bool b1, bool b2, bool b3) {
169  return !MoreThanOneTrue(b1, b2, b3) && AtLeastOneTrue(b1, b2, b3);
170 }
171 
172 // Implement our own DoubleToString as base::DoubleToString uses third_party
173 // library dmg_fp.
174 std::string DoubleToString(double value) {
175  std::ostringstream stringstream;
176  stringstream << value;
177  return stringstream.str();
178 }
179 
180 // Coverts binary data into human readable UUID format.
181 bool HexToUUID(const std::string& data, std::string* uuid_format) {
182  DCHECK(uuid_format);
183  const size_t kExpectedUUIDSize = 16;
184  if (data.size() != kExpectedUUIDSize) {
185  LOG(ERROR) << "UUID size is expected to be " << kExpectedUUIDSize
186  << " but is " << data.size() << " and the data in hex is "
187  << base::HexEncode(data.data(), data.size());
188  return false;
189  }
190 
191  const std::string hex_encoded =
192  base::ToLowerASCII(base::HexEncode(data.data(), data.size()));
193  DCHECK_EQ(hex_encoded.size(), kExpectedUUIDSize * 2);
194  base::StringPiece all(hex_encoded);
195  // Note UUID has 5 parts separated with dashes.
196  // e.g. 123e4567-e89b-12d3-a456-426655440000
197  // These StringPieces have each part.
198  base::StringPiece first = all.substr(0, 8);
199  base::StringPiece second = all.substr(8, 4);
200  base::StringPiece third = all.substr(12, 4);
201  base::StringPiece fourth = all.substr(16, 4);
202  base::StringPiece fifth = all.substr(20, 12);
203 
204  // 32 hexadecimal characters with 4 hyphens.
205  const size_t kHumanReadableUUIDSize = 36;
206  uuid_format->reserve(kHumanReadableUUIDSize);
207  first.CopyToString(uuid_format);
208  uuid_format->append("-");
209  second.AppendToString(uuid_format);
210  uuid_format->append("-");
211  third.AppendToString(uuid_format);
212  uuid_format->append("-");
213  fourth.AppendToString(uuid_format);
214  uuid_format->append("-");
215  fifth.AppendToString(uuid_format);
216  return true;
217 }
218 
219 void UpdateContentProtectionPsshHelper(
220  const std::string& drm_uuid,
221  const std::string& pssh,
222  std::list<ContentProtectionElement>* content_protection_elements) {
223  const std::string drm_uuid_schemd_id_uri_form = "urn:uuid:" + drm_uuid;
224  for (std::list<ContentProtectionElement>::iterator protection =
225  content_protection_elements->begin();
226  protection != content_protection_elements->end(); ++protection) {
227  if (protection->scheme_id_uri != drm_uuid_schemd_id_uri_form) {
228  continue;
229  }
230 
231  for (std::vector<Element>::iterator subelement =
232  protection->subelements.begin();
233  subelement != protection->subelements.end(); ++subelement) {
234  if (subelement->name == kPsshElementName) {
235  // For now, we want to remove the PSSH element because some players do
236  // not support updating pssh.
237  protection->subelements.erase(subelement);
238 
239  // TODO(rkuroiwa): Uncomment this and remove the line above when
240  // shaka-player supports updating PSSH.
241  // subelement->content = pssh;
242  return;
243  }
244  }
245 
246  // Reaching here means <cenc:pssh> does not exist under the
247  // ContentProtection element. Add it.
248  // TODO(rkuroiwa): Uncomment this when shaka-player supports updating PSSH.
249  // Element cenc_pssh;
250  // cenc_pssh.name = kPsshElementName;
251  // cenc_pssh.content = pssh;
252  // protection->subelements.push_back(cenc_pssh);
253  return;
254  }
255 
256  // Reaching here means that ContentProtection for the DRM does not exist.
257  // Add it.
258  ContentProtectionElement content_protection;
259  content_protection.scheme_id_uri = drm_uuid_schemd_id_uri_form;
260  // TODO(rkuroiwa): Uncomment this when shaka-player supports updating PSSH.
261  // Element cenc_pssh;
262  // cenc_pssh.name = kPsshElementName;
263  // cenc_pssh.content = pssh;
264  // content_protection.subelements.push_back(cenc_pssh);
265  content_protection_elements->push_back(content_protection);
266  return;
267 }
268 
269 namespace {
270 // Helper function. This works because Representation and AdaptationSet both
271 // have AddContentProtectionElement().
272 template <typename ContentProtectionParent>
273 void AddContentProtectionElementsHelperTemplated(
274  const MediaInfo& media_info,
275  ContentProtectionParent* parent) {
276  DCHECK(parent);
277  if (!media_info.has_protected_content())
278  return;
279 
280  const MediaInfo::ProtectedContent& protected_content =
281  media_info.protected_content();
282 
283  // DASH MPD spec specifies a default ContentProtection element for ISO BMFF
284  // (MP4) files.
285  const bool is_mp4_container =
286  media_info.container_type() == MediaInfo::CONTAINER_MP4;
287  std::string key_id_uuid_format;
288  if (protected_content.has_default_key_id()) {
289  if (!HexToUUID(protected_content.default_key_id(), &key_id_uuid_format)) {
290  LOG(ERROR) << "Failed to convert default key ID into UUID format.";
291  }
292  }
293 
294  if (is_mp4_container) {
295  ContentProtectionElement mp4_content_protection;
296  mp4_content_protection.scheme_id_uri = kEncryptedMp4Scheme;
297  mp4_content_protection.value = protected_content.protection_scheme();
298  if (!key_id_uuid_format.empty()) {
299  mp4_content_protection.additional_attributes["cenc:default_KID"] =
300  key_id_uuid_format;
301  }
302 
303  parent->AddContentProtectionElement(mp4_content_protection);
304  }
305 
306  for (int i = 0; i < protected_content.content_protection_entry().size();
307  ++i) {
308  const MediaInfo::ProtectedContent::ContentProtectionEntry& entry =
309  protected_content.content_protection_entry(i);
310  if (!entry.has_uuid()) {
311  LOG(WARNING)
312  << "ContentProtectionEntry was specified but no UUID is set for "
313  << entry.name_version() << ", skipping.";
314  continue;
315  }
316 
317  ContentProtectionElement drm_content_protection;
318  drm_content_protection.scheme_id_uri = "urn:uuid:" + entry.uuid();
319  if (entry.has_name_version())
320  drm_content_protection.value = entry.name_version();
321 
322  if (entry.has_pssh()) {
323  std::string base64_encoded_pssh;
324  base::Base64Encode(
325  base::StringPiece(entry.pssh().data(), entry.pssh().size()),
326  &base64_encoded_pssh);
327  Element cenc_pssh;
328  cenc_pssh.name = kPsshElementName;
329  cenc_pssh.content = base64_encoded_pssh;
330  drm_content_protection.subelements.push_back(cenc_pssh);
331  }
332 
333  if (!key_id_uuid_format.empty() && !is_mp4_container) {
334  drm_content_protection.additional_attributes["cenc:default_KID"] =
335  key_id_uuid_format;
336  }
337 
338  parent->AddContentProtectionElement(drm_content_protection);
339  }
340 
341  LOG_IF(WARNING, protected_content.content_protection_entry().size() == 0)
342  << "The media is encrypted but no content protection specified.";
343 }
344 } // namespace
345 
346 void AddContentProtectionElements(const MediaInfo& media_info,
347  Representation* parent) {
348  AddContentProtectionElementsHelperTemplated(media_info, parent);
349 }
350 
351 void AddContentProtectionElements(const MediaInfo& media_info,
352  AdaptationSet* parent) {
353  AddContentProtectionElementsHelperTemplated(media_info, parent);
354 }
355 
356 
357 } // namespace shaka
void AddContentProtectionElements(const MediaInfo &media_info, Representation *parent)
Definition: mpd_utils.cc:346
bool HexToUUID(const std::string &data, std::string *uuid_format)
Definition: mpd_utils.cc:181
std::string DoubleToString(double value)
Definition: mpd_utils.cc:174