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