DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerator
muxer_util.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/media/base/muxer_util.h"
8 
9 #include <inttypes.h>
10 
11 #include <string>
12 #include <vector>
13 
14 #include "packager/base/logging.h"
15 #include "packager/base/strings/string_number_conversions.h"
16 #include "packager/base/strings/string_split.h"
17 #include "packager/base/strings/stringprintf.h"
18 
19 namespace edash_packager {
20 namespace {
21 bool ValidateFormatTag(const std::string& format_tag) {
22  DCHECK(!format_tag.empty());
23  // Format tag should follow this prototype: %0[width]d.
24  if (format_tag.size() > 3 && format_tag[0] == '%' && format_tag[1] == '0' &&
25  format_tag[format_tag.size() - 1] == 'd') {
26  unsigned out;
27  if (base::StringToUint(format_tag.substr(2, format_tag.size() - 3), &out))
28  return true;
29  }
30  LOG(ERROR) << "SegmentTemplate: Format tag should follow this prototype: "
31  << "%0[width]d if exist.";
32  return false;
33 }
34 } // namespace
35 
36 namespace media {
37 
38 bool ValidateSegmentTemplate(const std::string& segment_template) {
39  if (segment_template.empty())
40  return false;
41 
42  std::vector<std::string> splits;
43  base::SplitString(segment_template, '$', &splits);
44 
45  // ISO/IEC 23009-1:2012 5.3.9.4.4 Template-based Segment URL construction.
46  // Allowed identifiers: $$, $RepresentationID$, $Number$, $Bandwidth$, $Time$.
47  // "$" always appears in pairs, so there should be odd number of splits.
48  if (splits.size() % 2 == 0) {
49  LOG(ERROR) << "SegmentTemplate: '$' should appear in pairs.";
50  return false;
51  }
52 
53  bool has_number = false;
54  bool has_time = false;
55  // Every second substring in split output should be an identifier.
56  for (size_t i = 1; i < splits.size(); i += 2) {
57  // Each identifier may be suffixed, within the enclosing ‘$’ characters,
58  // with an additional format tag aligned with the printf format tag as
59  // defined in IEEE 1003.1-2008 [10] following this prototype: %0[width]d.
60  size_t format_pos = splits[i].find('%');
61  std::string identifier = splits[i].substr(0, format_pos);
62  if (format_pos != std::string::npos) {
63  if (!ValidateFormatTag(splits[i].substr(format_pos)))
64  return false;
65  }
66 
67  // TODO(kqyang): Support "RepresentationID".
68  if (identifier == "RepresentationID") {
69  NOTIMPLEMENTED() << "SegmentTemplate: $RepresentationID$ is not supported "
70  "yet.";
71  return false;
72  } else if (identifier == "Number") {
73  has_number = true;
74  } else if (identifier == "Time") {
75  has_time = true;
76  } else if (identifier == "") {
77  if (format_pos != std::string::npos) {
78  LOG(ERROR) << "SegmentTemplate: $$ should not have any format tags.";
79  return false;
80  }
81  } else if (identifier != "Bandwidth") {
82  LOG(ERROR) << "SegmentTemplate: '$" << splits[i]
83  << "$' is not a valid identifier.";
84  return false;
85  }
86  }
87  if (has_number && has_time) {
88  LOG(ERROR) << "SegmentTemplate: $Number$ and $Time$ should not co-exist.";
89  return false;
90  }
91  if (!has_number && !has_time) {
92  LOG(ERROR) << "SegmentTemplate: $Number$ or $Time$ should exist.";
93  return false;
94  }
95  // Note: The below check is skipped.
96  // Strings outside identifiers shall only contain characters that are
97  // permitted within URLs according to RFC 1738.
98  return true;
99 }
100 
101 std::string GetSegmentName(const std::string& segment_template,
102  uint64_t segment_start_time,
103  uint32_t segment_index,
104  uint32_t bandwidth) {
105  DCHECK(ValidateSegmentTemplate(segment_template));
106 
107  std::vector<std::string> splits;
108  base::SplitString(segment_template, '$', &splits);
109  // "$" always appears in pairs, so there should be odd number of splits.
110  DCHECK_EQ(1u, splits.size() % 2);
111 
112  std::string segment_name;
113  for (size_t i = 0; i < splits.size(); ++i) {
114  // Every second substring in split output should be an identifier.
115  // Simply copy the non-identifier part.
116  if (i % 2 == 0) {
117  segment_name += splits[i];
118  continue;
119  }
120  if (splits[i].empty()) {
121  // "$$" is an escape sequence, replaced with a single "$".
122  segment_name += "$";
123  continue;
124  }
125  size_t format_pos = splits[i].find('%');
126  std::string identifier = splits[i].substr(0, format_pos);
127  DCHECK(identifier == "Number" || identifier == "Time" ||
128  identifier == "Bandwidth");
129 
130  std::string format_tag;
131  if (format_pos != std::string::npos) {
132  format_tag = splits[i].substr(format_pos);
133  DCHECK(ValidateFormatTag(format_tag));
134  // Replace %d formatting to correctly format uint64_t.
135  format_tag = format_tag.substr(0, format_tag.size() - 1) + PRIu64;
136  } else {
137  // Default format tag "%01d", modified to format uint64_t correctly.
138  format_tag = "%01" PRIu64;
139  }
140 
141  if (identifier == "Number") {
142  // SegmentNumber starts from 1.
143  segment_name += base::StringPrintf(
144  format_tag.c_str(), static_cast<uint64_t>(segment_index + 1));
145  } else if (identifier == "Time") {
146  segment_name +=
147  base::StringPrintf(format_tag.c_str(), segment_start_time);
148  } else if (identifier == "Bandwidth") {
149  segment_name += base::StringPrintf(format_tag.c_str(),
150  static_cast<uint64_t>(bandwidth));
151  }
152  }
153  return segment_name;
154 }
155 
156 } // namespace media
157 } // namespace edash_packager