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