2022-08-26 15:44:59 +00:00
|
|
|
// Copyright 2014 Google LLC. All rights reserved.
|
2014-06-27 23:07:36 +00:00
|
|
|
//
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file or at
|
|
|
|
// https://developers.google.com/open-source/licenses/bsd
|
|
|
|
|
2023-10-10 23:51:11 +00:00
|
|
|
#include <packager/app/stream_descriptor.h>
|
2014-06-27 23:07:36 +00:00
|
|
|
|
2023-09-09 00:44:17 +00:00
|
|
|
#include <absl/strings/numbers.h>
|
|
|
|
#include <absl/strings/str_split.h>
|
|
|
|
#include <glog/logging.h>
|
2014-06-27 23:07:36 +00:00
|
|
|
|
2023-10-11 08:49:50 +00:00
|
|
|
#include <packager/kv_pairs/kv_pairs.h>
|
|
|
|
#include <packager/utils/string_trim_split.h>
|
|
|
|
|
2016-05-20 21:19:33 +00:00
|
|
|
namespace shaka {
|
2014-06-27 23:07:36 +00:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
enum FieldType {
|
|
|
|
kUnknownField = 0,
|
|
|
|
kStreamSelectorField,
|
|
|
|
kInputField,
|
|
|
|
kOutputField,
|
|
|
|
kSegmentTemplateField,
|
|
|
|
kBandwidthField,
|
2015-02-02 19:27:32 +00:00
|
|
|
kLanguageField,
|
2020-12-11 20:58:26 +00:00
|
|
|
kCcIndexField,
|
2015-12-16 18:20:13 +00:00
|
|
|
kOutputFormatField,
|
2016-04-16 22:58:47 +00:00
|
|
|
kHlsNameField,
|
|
|
|
kHlsGroupIdField,
|
|
|
|
kHlsPlaylistNameField,
|
2018-02-01 20:27:30 +00:00
|
|
|
kHlsIframePlaylistNameField,
|
2017-05-15 16:22:06 +00:00
|
|
|
kTrickPlayFactorField,
|
2017-05-22 17:17:58 +00:00
|
|
|
kSkipEncryptionField,
|
2017-09-20 22:49:00 +00:00
|
|
|
kDrmStreamLabelField,
|
2018-10-10 22:30:28 +00:00
|
|
|
kHlsCharacteristicsField,
|
2019-06-13 06:01:16 +00:00
|
|
|
kDashAccessiblitiesField,
|
|
|
|
kDashRolesField,
|
2020-03-06 18:19:47 +00:00
|
|
|
kDashOnlyField,
|
|
|
|
kHlsOnlyField,
|
2014-06-27 23:07:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct FieldNameToTypeMapping {
|
|
|
|
const char* field_name;
|
|
|
|
FieldType field_type;
|
|
|
|
};
|
|
|
|
|
|
|
|
const FieldNameToTypeMapping kFieldNameTypeMappings[] = {
|
2016-04-16 22:58:47 +00:00
|
|
|
{"stream_selector", kStreamSelectorField},
|
|
|
|
{"stream", kStreamSelectorField},
|
|
|
|
{"input", kInputField},
|
|
|
|
{"in", kInputField},
|
|
|
|
{"output", kOutputField},
|
|
|
|
{"out", kOutputField},
|
|
|
|
{"init_segment", kOutputField},
|
|
|
|
{"segment_template", kSegmentTemplateField},
|
|
|
|
{"template", kSegmentTemplateField},
|
|
|
|
{"bandwidth", kBandwidthField},
|
|
|
|
{"bw", kBandwidthField},
|
|
|
|
{"bitrate", kBandwidthField},
|
|
|
|
{"language", kLanguageField},
|
|
|
|
{"lang", kLanguageField},
|
2020-12-11 20:58:26 +00:00
|
|
|
{"cc_index", kCcIndexField},
|
2016-04-16 22:58:47 +00:00
|
|
|
{"output_format", kOutputFormatField},
|
|
|
|
{"format", kOutputFormatField},
|
|
|
|
{"hls_name", kHlsNameField},
|
|
|
|
{"hls_group_id", kHlsGroupIdField},
|
|
|
|
{"playlist_name", kHlsPlaylistNameField},
|
2018-02-01 20:27:30 +00:00
|
|
|
{"iframe_playlist_name", kHlsIframePlaylistNameField},
|
2017-05-15 16:22:06 +00:00
|
|
|
{"trick_play_factor", kTrickPlayFactorField},
|
|
|
|
{"tpf", kTrickPlayFactorField},
|
2017-05-22 17:17:58 +00:00
|
|
|
{"skip_encryption", kSkipEncryptionField},
|
2017-09-20 22:49:00 +00:00
|
|
|
{"drm_stream_label", kDrmStreamLabelField},
|
|
|
|
{"drm_label", kDrmStreamLabelField},
|
2018-10-10 22:30:28 +00:00
|
|
|
{"hls_characteristics", kHlsCharacteristicsField},
|
|
|
|
{"characteristics", kHlsCharacteristicsField},
|
|
|
|
{"charcs", kHlsCharacteristicsField},
|
2019-06-13 06:01:16 +00:00
|
|
|
{"dash_accessibilities", kDashAccessiblitiesField},
|
|
|
|
{"dash_accessibility", kDashAccessiblitiesField},
|
|
|
|
{"accessibilities", kDashAccessiblitiesField},
|
|
|
|
{"accessibility", kDashAccessiblitiesField},
|
|
|
|
{"dash_roles", kDashRolesField},
|
|
|
|
{"dash_role", kDashRolesField},
|
|
|
|
{"roles", kDashRolesField},
|
|
|
|
{"role", kDashRolesField},
|
2020-03-06 18:19:47 +00:00
|
|
|
{"dash_only", kDashOnlyField},
|
|
|
|
{"hls_only", kHlsOnlyField},
|
2014-06-27 23:07:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
FieldType GetFieldType(const std::string& field_name) {
|
2023-09-09 00:44:17 +00:00
|
|
|
for (size_t idx = 0; idx < std::size(kFieldNameTypeMappings); ++idx) {
|
2014-06-27 23:07:36 +00:00
|
|
|
if (field_name == kFieldNameTypeMappings[idx].field_name)
|
|
|
|
return kFieldNameTypeMappings[idx].field_type;
|
|
|
|
}
|
|
|
|
return kUnknownField;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
2023-09-09 00:44:17 +00:00
|
|
|
std::optional<StreamDescriptor> ParseStreamDescriptor(
|
2017-05-22 20:31:41 +00:00
|
|
|
const std::string& descriptor_string) {
|
2014-06-27 23:07:36 +00:00
|
|
|
StreamDescriptor descriptor;
|
|
|
|
|
|
|
|
// Split descriptor string into name/value pairs.
|
2023-09-09 00:44:17 +00:00
|
|
|
std::vector<KVPair> kv_pairs =
|
|
|
|
SplitStringIntoKeyValuePairs(descriptor_string, '=', ',');
|
|
|
|
if (kv_pairs.empty()) {
|
2018-07-11 18:57:21 +00:00
|
|
|
LOG(ERROR) << "Invalid stream descriptors name/value pairs: "
|
|
|
|
<< descriptor_string;
|
2023-09-09 00:44:17 +00:00
|
|
|
return std::nullopt;
|
2014-06-27 23:07:36 +00:00
|
|
|
}
|
2023-09-09 00:44:17 +00:00
|
|
|
std::vector<absl::string_view> tokens;
|
|
|
|
|
|
|
|
for (const auto& pair : kv_pairs) {
|
|
|
|
switch (GetFieldType(pair.first)) {
|
2014-06-27 23:07:36 +00:00
|
|
|
case kStreamSelectorField:
|
2023-09-09 00:44:17 +00:00
|
|
|
descriptor.stream_selector = pair.second;
|
2014-06-27 23:07:36 +00:00
|
|
|
break;
|
|
|
|
case kInputField:
|
2023-09-09 00:44:17 +00:00
|
|
|
descriptor.input = pair.second;
|
2014-06-27 23:07:36 +00:00
|
|
|
break;
|
|
|
|
case kOutputField:
|
2023-09-09 00:44:17 +00:00
|
|
|
descriptor.output = pair.second;
|
2014-06-27 23:07:36 +00:00
|
|
|
break;
|
|
|
|
case kSegmentTemplateField:
|
2023-09-09 00:44:17 +00:00
|
|
|
descriptor.segment_template = pair.second;
|
2014-06-27 23:07:36 +00:00
|
|
|
break;
|
|
|
|
case kBandwidthField: {
|
|
|
|
unsigned bw;
|
2023-09-09 00:44:17 +00:00
|
|
|
if (!absl::SimpleAtoi(pair.second, &bw)) {
|
2014-06-27 23:07:36 +00:00
|
|
|
LOG(ERROR) << "Non-numeric bandwidth specified.";
|
2023-09-09 00:44:17 +00:00
|
|
|
return std::nullopt;
|
2014-06-27 23:07:36 +00:00
|
|
|
}
|
|
|
|
descriptor.bandwidth = bw;
|
|
|
|
break;
|
|
|
|
}
|
2015-02-02 19:27:32 +00:00
|
|
|
case kLanguageField: {
|
2023-09-09 00:44:17 +00:00
|
|
|
descriptor.language = pair.second;
|
2015-02-02 19:27:32 +00:00
|
|
|
break;
|
|
|
|
}
|
2020-12-11 20:58:26 +00:00
|
|
|
case kCcIndexField: {
|
|
|
|
unsigned index;
|
2023-09-09 00:44:17 +00:00
|
|
|
if (!absl::SimpleAtoi(pair.second, &index)) {
|
2020-12-11 20:58:26 +00:00
|
|
|
LOG(ERROR) << "Non-numeric cc_index specified.";
|
2023-09-09 00:44:17 +00:00
|
|
|
return std::nullopt;
|
2020-12-11 20:58:26 +00:00
|
|
|
}
|
|
|
|
descriptor.cc_index = index;
|
|
|
|
break;
|
|
|
|
}
|
2015-12-16 18:20:13 +00:00
|
|
|
case kOutputFormatField: {
|
2023-09-09 00:44:17 +00:00
|
|
|
descriptor.output_format = pair.second;
|
2015-12-16 18:20:13 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-16 22:58:47 +00:00
|
|
|
case kHlsNameField: {
|
2023-09-09 00:44:17 +00:00
|
|
|
descriptor.hls_name = pair.second;
|
2016-04-16 22:58:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case kHlsGroupIdField: {
|
2023-09-09 00:44:17 +00:00
|
|
|
descriptor.hls_group_id = pair.second;
|
2016-04-16 22:58:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case kHlsPlaylistNameField: {
|
2023-09-09 00:44:17 +00:00
|
|
|
descriptor.hls_playlist_name = pair.second;
|
2016-04-16 22:58:47 +00:00
|
|
|
break;
|
|
|
|
}
|
2018-02-01 20:27:30 +00:00
|
|
|
case kHlsIframePlaylistNameField: {
|
2023-09-09 00:44:17 +00:00
|
|
|
descriptor.hls_iframe_playlist_name = pair.second;
|
2018-02-01 20:27:30 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-05-15 16:22:06 +00:00
|
|
|
case kTrickPlayFactorField: {
|
|
|
|
unsigned factor;
|
2023-09-09 00:44:17 +00:00
|
|
|
if (!absl::SimpleAtoi(pair.second, &factor)) {
|
|
|
|
LOG(ERROR) << "Non-numeric trick play factor " << pair.second
|
2017-03-21 23:14:46 +00:00
|
|
|
<< " specified.";
|
2023-09-09 00:44:17 +00:00
|
|
|
return std::nullopt;
|
2017-03-21 23:14:46 +00:00
|
|
|
}
|
2017-05-15 16:22:06 +00:00
|
|
|
if (factor == 0) {
|
|
|
|
LOG(ERROR) << "Stream trick_play_factor should be > 0.";
|
2023-09-09 00:44:17 +00:00
|
|
|
return std::nullopt;
|
2017-03-21 23:14:46 +00:00
|
|
|
}
|
2017-05-15 16:22:06 +00:00
|
|
|
descriptor.trick_play_factor = factor;
|
2017-03-21 23:14:46 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-05-22 17:17:58 +00:00
|
|
|
case kSkipEncryptionField: {
|
|
|
|
unsigned skip_encryption_value;
|
2023-09-09 00:44:17 +00:00
|
|
|
if (!absl::SimpleAtoi(pair.second, &skip_encryption_value)) {
|
2017-05-22 17:17:58 +00:00
|
|
|
LOG(ERROR) << "Non-numeric option for skip encryption field "
|
2023-09-09 00:44:17 +00:00
|
|
|
"specified ("
|
|
|
|
<< pair.second << ").";
|
|
|
|
return std::nullopt;
|
2017-05-22 17:17:58 +00:00
|
|
|
}
|
|
|
|
if (skip_encryption_value > 1) {
|
|
|
|
LOG(ERROR) << "skip_encryption should be either 0 or 1.";
|
2023-09-09 00:44:17 +00:00
|
|
|
return std::nullopt;
|
2017-05-22 17:17:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
descriptor.skip_encryption = skip_encryption_value > 0;
|
|
|
|
break;
|
|
|
|
}
|
2018-10-10 22:30:28 +00:00
|
|
|
case kDrmStreamLabelField:
|
2023-09-09 00:44:17 +00:00
|
|
|
descriptor.drm_label = pair.second;
|
2017-09-20 22:49:00 +00:00
|
|
|
break;
|
2018-10-10 22:30:28 +00:00
|
|
|
case kHlsCharacteristicsField:
|
|
|
|
descriptor.hls_characteristics =
|
2023-09-09 00:44:17 +00:00
|
|
|
SplitAndTrimSkipEmpty(pair.second, ';');
|
2018-10-10 22:30:28 +00:00
|
|
|
break;
|
2023-09-09 00:44:17 +00:00
|
|
|
case kDashAccessiblitiesField: {
|
2019-06-13 06:01:16 +00:00
|
|
|
descriptor.dash_accessiblities =
|
2023-09-09 00:44:17 +00:00
|
|
|
SplitAndTrimSkipEmpty(pair.second, ';');
|
2019-06-13 06:01:16 +00:00
|
|
|
for (const std::string& accessibility :
|
|
|
|
descriptor.dash_accessiblities) {
|
|
|
|
size_t pos = accessibility.find('=');
|
|
|
|
if (pos == std::string::npos) {
|
2023-09-09 00:44:17 +00:00
|
|
|
LOG(ERROR) << "Accessibility should be in scheme=value format, "
|
|
|
|
"but seeing "
|
|
|
|
<< accessibility;
|
|
|
|
return std::nullopt;
|
2019-06-13 06:01:16 +00:00
|
|
|
}
|
|
|
|
}
|
2023-09-09 00:44:17 +00:00
|
|
|
} break;
|
2019-06-13 06:01:16 +00:00
|
|
|
case kDashRolesField:
|
2023-09-09 00:44:17 +00:00
|
|
|
descriptor.dash_roles = SplitAndTrimSkipEmpty(pair.second, ';');
|
2019-06-13 06:01:16 +00:00
|
|
|
break;
|
2020-03-06 18:19:47 +00:00
|
|
|
case kDashOnlyField:
|
|
|
|
unsigned dash_only_value;
|
2023-09-09 00:44:17 +00:00
|
|
|
if (!absl::SimpleAtoi(pair.second, &dash_only_value)) {
|
2020-03-06 18:19:47 +00:00
|
|
|
LOG(ERROR) << "Non-numeric option for dash_only field "
|
2023-09-09 00:44:17 +00:00
|
|
|
"specified ("
|
|
|
|
<< pair.second << ").";
|
|
|
|
return std::nullopt;
|
2020-03-06 18:19:47 +00:00
|
|
|
}
|
|
|
|
if (dash_only_value > 1) {
|
|
|
|
LOG(ERROR) << "dash_only should be either 0 or 1.";
|
2023-09-09 00:44:17 +00:00
|
|
|
return std::nullopt;
|
2020-03-06 18:19:47 +00:00
|
|
|
}
|
|
|
|
descriptor.dash_only = dash_only_value > 0;
|
|
|
|
break;
|
|
|
|
case kHlsOnlyField:
|
|
|
|
unsigned hls_only_value;
|
2023-09-09 00:44:17 +00:00
|
|
|
if (!absl::SimpleAtoi(pair.second, &hls_only_value)) {
|
2020-03-06 18:19:47 +00:00
|
|
|
LOG(ERROR) << "Non-numeric option for hls_only field "
|
2023-09-09 00:44:17 +00:00
|
|
|
"specified ("
|
|
|
|
<< pair.second << ").";
|
|
|
|
return std::nullopt;
|
2020-03-06 18:19:47 +00:00
|
|
|
}
|
|
|
|
if (hls_only_value > 1) {
|
|
|
|
LOG(ERROR) << "hls_only should be either 0 or 1.";
|
2023-09-09 00:44:17 +00:00
|
|
|
return std::nullopt;
|
2020-03-06 18:19:47 +00:00
|
|
|
}
|
|
|
|
descriptor.hls_only = hls_only_value > 0;
|
|
|
|
break;
|
2014-06-27 23:07:36 +00:00
|
|
|
default:
|
2023-09-09 00:44:17 +00:00
|
|
|
LOG(ERROR) << "Unknown field in stream descriptor (\"" << pair.first
|
2014-06-27 23:07:36 +00:00
|
|
|
<< "\").";
|
2023-09-09 00:44:17 +00:00
|
|
|
return std::nullopt;
|
2014-06-27 23:07:36 +00:00
|
|
|
}
|
|
|
|
}
|
2017-05-22 20:31:41 +00:00
|
|
|
return descriptor;
|
2014-06-27 23:07:36 +00:00
|
|
|
}
|
|
|
|
|
2016-05-20 21:19:33 +00:00
|
|
|
} // namespace shaka
|