Shaka Packager SDK
stream_descriptor.cc
1 // Copyright 2014 Google LLC. 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 <absl/log/log.h>
10 #include <absl/strings/numbers.h>
11 #include <absl/strings/str_split.h>
12 
13 #include <packager/kv_pairs/kv_pairs.h>
14 #include <packager/utils/string_trim_split.h>
15 
16 namespace shaka {
17 
18 namespace {
19 
20 enum FieldType {
21  kUnknownField = 0,
22  kStreamSelectorField,
23  kInputField,
24  kOutputField,
25  kSegmentTemplateField,
26  kBandwidthField,
27  kLanguageField,
28  kCcIndexField,
29  kOutputFormatField,
30  kHlsNameField,
31  kHlsGroupIdField,
32  kHlsPlaylistNameField,
33  kHlsIframePlaylistNameField,
34  kTrickPlayFactorField,
35  kSkipEncryptionField,
36  kDrmStreamLabelField,
37  kHlsCharacteristicsField,
38  kDashAccessiblitiesField,
39  kDashRolesField,
40  kDashOnlyField,
41  kHlsOnlyField,
42  kDashLabelField,
43  kForcedSubtitleField,
44  kInputFormatField,
45 };
46 
47 struct FieldNameToTypeMapping {
48  const char* field_name;
49  FieldType field_type;
50 };
51 
52 const FieldNameToTypeMapping kFieldNameTypeMappings[] = {
53  {"stream_selector", kStreamSelectorField},
54  {"stream", kStreamSelectorField},
55  {"input", kInputField},
56  {"in", kInputField},
57  {"output", kOutputField},
58  {"out", kOutputField},
59  {"init_segment", kOutputField},
60  {"segment_template", kSegmentTemplateField},
61  {"template", kSegmentTemplateField},
62  {"bandwidth", kBandwidthField},
63  {"bw", kBandwidthField},
64  {"bitrate", kBandwidthField},
65  {"language", kLanguageField},
66  {"lang", kLanguageField},
67  {"cc_index", kCcIndexField},
68  {"output_format", kOutputFormatField},
69  {"format", kOutputFormatField},
70  {"hls_name", kHlsNameField},
71  {"hls_group_id", kHlsGroupIdField},
72  {"playlist_name", kHlsPlaylistNameField},
73  {"iframe_playlist_name", kHlsIframePlaylistNameField},
74  {"trick_play_factor", kTrickPlayFactorField},
75  {"tpf", kTrickPlayFactorField},
76  {"skip_encryption", kSkipEncryptionField},
77  {"drm_stream_label", kDrmStreamLabelField},
78  {"drm_label", kDrmStreamLabelField},
79  {"hls_characteristics", kHlsCharacteristicsField},
80  {"characteristics", kHlsCharacteristicsField},
81  {"charcs", kHlsCharacteristicsField},
82  {"dash_accessibilities", kDashAccessiblitiesField},
83  {"dash_accessibility", kDashAccessiblitiesField},
84  {"accessibilities", kDashAccessiblitiesField},
85  {"accessibility", kDashAccessiblitiesField},
86  {"dash_roles", kDashRolesField},
87  {"dash_role", kDashRolesField},
88  {"roles", kDashRolesField},
89  {"role", kDashRolesField},
90  {"dash_only", kDashOnlyField},
91  {"hls_only", kHlsOnlyField},
92  {"dash_label", kDashLabelField},
93  {"forced_subtitle", kForcedSubtitleField},
94  {"input_format", kInputFormatField},
95 };
96 
97 FieldType GetFieldType(const std::string& field_name) {
98  for (size_t idx = 0; idx < std::size(kFieldNameTypeMappings); ++idx) {
99  if (field_name == kFieldNameTypeMappings[idx].field_name)
100  return kFieldNameTypeMappings[idx].field_type;
101  }
102  return kUnknownField;
103 }
104 
105 } // anonymous namespace
106 
107 std::optional<StreamDescriptor> ParseStreamDescriptor(
108  const std::string& descriptor_string) {
109  StreamDescriptor descriptor;
110 
111  // Split descriptor string into name/value pairs.
112  std::vector<KVPair> kv_pairs =
113  SplitStringIntoKeyValuePairs(descriptor_string, '=', ',');
114  if (kv_pairs.empty()) {
115  LOG(ERROR) << "Invalid stream descriptors name/value pairs: "
116  << descriptor_string;
117  return std::nullopt;
118  }
119  std::vector<absl::string_view> tokens;
120 
121  for (const auto& pair : kv_pairs) {
122  switch (GetFieldType(pair.first)) {
123  case kStreamSelectorField:
124  descriptor.stream_selector = pair.second;
125  break;
126  case kInputField:
127  descriptor.input = pair.second;
128  break;
129  case kOutputField:
130  descriptor.output = pair.second;
131  break;
132  case kSegmentTemplateField:
133  descriptor.segment_template = pair.second;
134  break;
135  case kBandwidthField: {
136  unsigned bw;
137  if (!absl::SimpleAtoi(pair.second, &bw)) {
138  LOG(ERROR) << "Non-numeric bandwidth specified.";
139  return std::nullopt;
140  }
141  descriptor.bandwidth = bw;
142  break;
143  }
144  case kLanguageField: {
145  descriptor.language = pair.second;
146  break;
147  }
148  case kCcIndexField: {
149  unsigned index;
150  if (!absl::SimpleAtoi(pair.second, &index)) {
151  LOG(ERROR) << "Non-numeric cc_index specified.";
152  return std::nullopt;
153  }
154  descriptor.cc_index = index;
155  break;
156  }
157  case kOutputFormatField: {
158  descriptor.output_format = pair.second;
159  break;
160  }
161  case kHlsNameField: {
162  descriptor.hls_name = pair.second;
163  break;
164  }
165  case kHlsGroupIdField: {
166  descriptor.hls_group_id = pair.second;
167  break;
168  }
169  case kHlsPlaylistNameField: {
170  descriptor.hls_playlist_name = pair.second;
171  break;
172  }
173  case kHlsIframePlaylistNameField: {
174  descriptor.hls_iframe_playlist_name = pair.second;
175  break;
176  }
177  case kTrickPlayFactorField: {
178  unsigned factor;
179  if (!absl::SimpleAtoi(pair.second, &factor)) {
180  LOG(ERROR) << "Non-numeric trick play factor " << pair.second
181  << " specified.";
182  return std::nullopt;
183  }
184  if (factor == 0) {
185  LOG(ERROR) << "Stream trick_play_factor should be > 0.";
186  return std::nullopt;
187  }
188  descriptor.trick_play_factor = factor;
189  break;
190  }
191  case kSkipEncryptionField: {
192  unsigned skip_encryption_value;
193  if (!absl::SimpleAtoi(pair.second, &skip_encryption_value)) {
194  LOG(ERROR) << "Non-numeric option for skip encryption field "
195  "specified ("
196  << pair.second << ").";
197  return std::nullopt;
198  }
199  if (skip_encryption_value > 1) {
200  LOG(ERROR) << "skip_encryption should be either 0 or 1.";
201  return std::nullopt;
202  }
203 
204  descriptor.skip_encryption = skip_encryption_value > 0;
205  break;
206  }
207  case kDrmStreamLabelField:
208  descriptor.drm_label = pair.second;
209  break;
210  case kHlsCharacteristicsField:
211  descriptor.hls_characteristics =
212  SplitAndTrimSkipEmpty(pair.second, ';');
213  break;
214  case kDashAccessiblitiesField: {
215  descriptor.dash_accessiblities =
216  SplitAndTrimSkipEmpty(pair.second, ';');
217  for (const std::string& accessibility :
218  descriptor.dash_accessiblities) {
219  size_t pos = accessibility.find('=');
220  if (pos == std::string::npos) {
221  LOG(ERROR) << "Accessibility should be in scheme=value format, "
222  "but seeing "
223  << accessibility;
224  return std::nullopt;
225  }
226  }
227  } break;
228  case kDashRolesField:
229  descriptor.dash_roles = SplitAndTrimSkipEmpty(pair.second, ';');
230  break;
231  case kDashOnlyField:
232  unsigned dash_only_value;
233  if (!absl::SimpleAtoi(pair.second, &dash_only_value)) {
234  LOG(ERROR) << "Non-numeric option for dash_only field "
235  "specified ("
236  << pair.second << ").";
237  return std::nullopt;
238  }
239  if (dash_only_value > 1) {
240  LOG(ERROR) << "dash_only should be either 0 or 1.";
241  return std::nullopt;
242  }
243  descriptor.dash_only = dash_only_value > 0;
244  break;
245  case kHlsOnlyField:
246  unsigned hls_only_value;
247  if (!absl::SimpleAtoi(pair.second, &hls_only_value)) {
248  LOG(ERROR) << "Non-numeric option for hls_only field "
249  "specified ("
250  << pair.second << ").";
251  return std::nullopt;
252  }
253  if (hls_only_value > 1) {
254  LOG(ERROR) << "hls_only should be either 0 or 1.";
255  return std::nullopt;
256  }
257  descriptor.hls_only = hls_only_value > 0;
258  break;
259  case kDashLabelField:
260  descriptor.dash_label = pair.second;
261  break;
262  case kForcedSubtitleField:
263  unsigned forced_subtitle_value;
264  if (!absl::SimpleAtoi(pair.second, &forced_subtitle_value)) {
265  LOG(ERROR) << "Non-numeric option for forced field "
266  "specified ("
267  << pair.second << ").";
268  return std::nullopt;
269  }
270  if (forced_subtitle_value > 1) {
271  LOG(ERROR) << "forced should be either 0 or 1.";
272  return std::nullopt;
273  }
274  descriptor.forced_subtitle = forced_subtitle_value > 0;
275  break;
276  case kInputFormatField: {
277  descriptor.input_format = pair.second;
278  break;
279  }
280  default:
281  LOG(ERROR) << "Unknown field in stream descriptor (\"" << pair.first
282  << "\").";
283  return std::nullopt;
284  }
285  }
286 
287  if (descriptor.forced_subtitle) {
288  auto itr = std::find(descriptor.dash_roles.begin(),
289  descriptor.dash_roles.end(), "forced-subtitle");
290  if (itr == descriptor.dash_roles.end()) {
291  descriptor.dash_roles.push_back("forced-subtitle");
292  }
293  }
294 
295  return descriptor;
296 }
297 
298 } // namespace shaka
All the methods that are virtual are virtual for mocking.
Definition: crypto_flags.cc:66
std::optional< StreamDescriptor > ParseStreamDescriptor(const std::string &descriptor_string)