Shaka Packager SDK
Loading...
Searching...
No Matches
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
16namespace shaka {
17
18namespace {
19
20enum 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
47struct FieldNameToTypeMapping {
48 const char* field_name;
49 FieldType field_type;
50};
51
52const 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
97FieldType 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
107std::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.
std::optional< StreamDescriptor > ParseStreamDescriptor(const std::string &descriptor_string)