DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
stream_descriptor.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/app/stream_descriptor.h"
8 
9 #include "packager/app/packager_util.h"
10 #include "packager/base/logging.h"
11 #include "packager/base/strings/string_number_conversions.h"
12 #include "packager/base/strings/string_split.h"
13 #include "packager/media/base/container_names.h"
14 #include "packager/media/base/language_utils.h"
15 
16 namespace shaka {
17 namespace media {
18 
19 namespace {
20 
21 enum FieldType {
22  kUnknownField = 0,
23  kStreamSelectorField,
24  kInputField,
25  kOutputField,
26  kSegmentTemplateField,
27  kBandwidthField,
28  kLanguageField,
29  kOutputFormatField,
30  kHlsNameField,
31  kHlsGroupIdField,
32  kHlsPlaylistNameField,
33  kTrickPlayFactorField,
34  kSkipEncryptionField,
35 };
36 
37 struct FieldNameToTypeMapping {
38  const char* field_name;
39  FieldType field_type;
40 };
41 
42 const FieldNameToTypeMapping kFieldNameTypeMappings[] = {
43  {"stream_selector", kStreamSelectorField},
44  {"stream", kStreamSelectorField},
45  {"input", kInputField},
46  {"in", kInputField},
47  {"output", kOutputField},
48  {"out", kOutputField},
49  {"init_segment", kOutputField},
50  {"segment_template", kSegmentTemplateField},
51  {"template", kSegmentTemplateField},
52  {"bandwidth", kBandwidthField},
53  {"bw", kBandwidthField},
54  {"bitrate", kBandwidthField},
55  {"language", kLanguageField},
56  {"lang", kLanguageField},
57  {"output_format", kOutputFormatField},
58  {"format", kOutputFormatField},
59  {"hls_name", kHlsNameField},
60  {"hls_group_id", kHlsGroupIdField},
61  {"playlist_name", kHlsPlaylistNameField},
62  {"trick_play_factor", kTrickPlayFactorField},
63  {"tpf", kTrickPlayFactorField},
64  {"skip_encryption", kSkipEncryptionField},
65 };
66 
67 FieldType GetFieldType(const std::string& field_name) {
68  for (size_t idx = 0; idx < arraysize(kFieldNameTypeMappings); ++idx) {
69  if (field_name == kFieldNameTypeMappings[idx].field_name)
70  return kFieldNameTypeMappings[idx].field_type;
71  }
72  return kUnknownField;
73 }
74 
75 } // anonymous namespace
76 
77 StreamDescriptor::StreamDescriptor() {}
78 
79 StreamDescriptor::~StreamDescriptor() {}
80 
81 bool InsertStreamDescriptor(const std::string& descriptor_string,
82  StreamDescriptorList* descriptor_list) {
83  StreamDescriptor descriptor;
84 
85  // Split descriptor string into name/value pairs.
86  base::StringPairs pairs;
87  if (!base::SplitStringIntoKeyValuePairs(descriptor_string,
88  '=',
89  ',',
90  &pairs)) {
91  LOG(ERROR) << "Invalid stream descriptors name/value pairs.";
92  return false;
93  }
94  for (base::StringPairs::const_iterator iter = pairs.begin();
95  iter != pairs.end(); ++iter) {
96  switch (GetFieldType(iter->first)) {
97  case kStreamSelectorField:
98  descriptor.stream_selector = iter->second;
99  break;
100  case kInputField:
101  descriptor.input = iter->second;
102  break;
103  case kOutputField:
104  descriptor.output = iter->second;
105  break;
106  case kSegmentTemplateField:
107  descriptor.segment_template = iter->second;
108  break;
109  case kBandwidthField: {
110  unsigned bw;
111  if (!base::StringToUint(iter->second, &bw)) {
112  LOG(ERROR) << "Non-numeric bandwidth specified.";
113  return false;
114  }
115  descriptor.bandwidth = bw;
116  break;
117  }
118  case kLanguageField: {
119  std::string language = LanguageToISO_639_2(iter->second);
120  if (language == "und") {
121  LOG(ERROR) << "Unknown/invalid language specified: " << iter->second;
122  return false;
123  }
124  descriptor.language = language;
125  break;
126  }
127  case kOutputFormatField: {
128  MediaContainerName output_format =
129  DetermineContainerFromFormatName(iter->second);
130  if (output_format == CONTAINER_UNKNOWN) {
131  LOG(ERROR) << "Unrecognized output format " << iter->second;
132  return false;
133  }
134  descriptor.output_format = output_format;
135  break;
136  }
137  case kHlsNameField: {
138  descriptor.hls_name = iter->second;
139  break;
140  }
141  case kHlsGroupIdField: {
142  descriptor.hls_group_id = iter->second;
143  break;
144  }
145  case kHlsPlaylistNameField: {
146  descriptor.hls_playlist_name = iter->second;
147  break;
148  }
149  case kTrickPlayFactorField: {
150  unsigned factor;
151  if (!base::StringToUint(iter->second, &factor)) {
152  LOG(ERROR) << "Non-numeric trick play factor " << iter->second
153  << " specified.";
154  return false;
155  }
156  if (factor == 0) {
157  LOG(ERROR) << "Stream trick_play_factor should be > 0.";
158  return false;
159  }
160  descriptor.trick_play_factor = factor;
161  break;
162  }
163  case kSkipEncryptionField: {
164  unsigned skip_encryption_value;
165  if (!base::StringToUint(iter->second, &skip_encryption_value)) {
166  LOG(ERROR) << "Non-numeric option for skip encryption field "
167  "specified (" << iter->second << ").";
168  return false;
169  }
170  if (skip_encryption_value > 1) {
171  LOG(ERROR) << "skip_encryption should be either 0 or 1.";
172  return false;
173  }
174 
175  descriptor.skip_encryption = skip_encryption_value > 0;
176  break;
177  }
178  default:
179  LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first
180  << "\").";
181  return false;
182  }
183  }
184  // Validate and insert the descriptor
185  if (descriptor.input.empty()) {
186  LOG(ERROR) << "Stream input not specified.";
187  return false;
188  }
189  if (!FLAGS_dump_stream_info && descriptor.stream_selector.empty()) {
190  LOG(ERROR) << "Stream stream_selector not specified.";
191  return false;
192  }
193 
194  if (descriptor.output_format == CONTAINER_UNKNOWN) {
195  const std::string& output_name = descriptor.output.empty()
196  ? descriptor.segment_template
197  : descriptor.output;
198  if (!output_name.empty()) {
199  descriptor.output_format = DetermineContainerFromFileName(output_name);
200  if (descriptor.output_format == CONTAINER_UNKNOWN) {
201  LOG(ERROR) << "Unable to determine output format for file "
202  << output_name;
203  return false;
204  }
205  }
206  }
207 
208  if (descriptor.output_format == MediaContainerName::CONTAINER_MPEG2TS) {
209  if (descriptor.segment_template.empty()) {
210  LOG(ERROR) << "Please specify segment_template. Single file TS output is "
211  "not supported.";
212  return false;
213  }
214  // Note that MPEG2 TS doesn't need a separate initialization segment, so
215  // output field is not needed.
216  if (!descriptor.output.empty()) {
217  LOG(WARNING) << "TS output '" << descriptor.output
218  << "' ignored. TS muxer does not support initialization "
219  "segment generation.";
220  }
221  }
222 
223  // For TS output, segment template is sufficient, and does not require an
224  // output entry.
225  const bool output_specified =
226  !descriptor.output.empty() ||
227  (descriptor.output_format == CONTAINER_MPEG2TS &&
228  !descriptor.segment_template.empty());
229  if (!FLAGS_dump_stream_info && !output_specified) {
230  LOG(ERROR) << "Stream output not specified.";
231  return false;
232  }
233  descriptor_list->insert(descriptor);
234  return true;
235 }
236 
237 } // namespace media
238 } // namespace shaka
std::string LanguageToISO_639_2(const std::string &language)