7#include <packager/media/base/muxer_util.h>
15#include <absl/log/check.h>
16#include <absl/strings/numbers.h>
17#include <absl/strings/str_format.h>
18#include <absl/strings/str_split.h>
20#include <packager/status.h>
24Status ValidateFormatTag(
const std::string& format_tag) {
25 if (format_tag.empty()) {
26 return Status(error::INVALID_ARGUMENT,
"Format tag should not be empty");
30 if (format_tag.size() > 3 && format_tag[0] ==
'%' && format_tag[1] ==
'0' &&
31 format_tag[format_tag.size() - 1] ==
'd') {
33 if (absl::SimpleAtoi(format_tag.substr(2, format_tag.size() - 3), &out)) {
39 error::INVALID_ARGUMENT,
40 "Format tag should follow this prototype: %0[width]d if exist.");
46Status ValidateSegmentTemplate(
const std::string& segment_template) {
47 if (segment_template.empty()) {
48 return Status(error::INVALID_ARGUMENT,
49 "Segment template should not be empty.");
52 std::vector<std::string> splits = absl::StrSplit(segment_template,
"$");
57 if (splits.size() % 2 == 0) {
58 return Status(error::INVALID_ARGUMENT,
59 "In segment templates, '$' should appear in pairs.");
62 bool has_number =
false;
63 bool has_time =
false;
65 for (
size_t i = 1; i < splits.size(); i += 2) {
69 size_t format_pos = splits[i].find(
'%');
70 std::string identifier = splits[i].substr(0, format_pos);
71 if (format_pos != std::string::npos) {
72 Status tag_check = ValidateFormatTag(splits[i].substr(format_pos));
73 if (!tag_check.ok()) {
79 if (identifier ==
"RepresentationID") {
82 "Segment template flag $RepresentationID$ is not supported yet.");
83 }
else if (identifier ==
"Number") {
85 }
else if (identifier ==
"Time") {
87 }
else if (identifier ==
"") {
88 if (format_pos != std::string::npos) {
89 return Status(error::INVALID_ARGUMENT,
90 "'$$' should not have any format tags.");
92 }
else if (identifier !=
"Bandwidth") {
93 return Status(error::INVALID_ARGUMENT,
94 "'$" + splits[i] +
"$' is not a valid identifier.");
97 if (has_number && has_time) {
99 error::INVALID_ARGUMENT,
100 "In segment templates $Number$ and $Time$ should not co-exist.");
102 if (!has_number && !has_time) {
103 return Status(error::INVALID_ARGUMENT,
104 "In segment templates $Number$ or $Time$ should exist.");
112std::string GetSegmentName(
const std::string& segment_template,
113 int64_t segment_start_time,
114 uint32_t segment_number,
115 uint32_t bandwidth) {
116 DCHECK_EQ(Status::OK, ValidateSegmentTemplate(segment_template));
118 std::vector<std::string> splits = absl::StrSplit(segment_template,
"$");
120 DCHECK_EQ(1u, splits.size() % 2);
122 std::string segment_name;
123 for (
size_t i = 0; i < splits.size(); ++i) {
127 segment_name += splits[i];
130 if (splits[i].empty()) {
135 size_t format_pos = splits[i].find(
'%');
136 std::string identifier = splits[i].substr(0, format_pos);
137 DCHECK(identifier ==
"Number" || identifier ==
"Time" ||
138 identifier ==
"Bandwidth");
140 std::string format_tag;
141 if (format_pos != std::string::npos) {
142 format_tag = splits[i].substr(format_pos);
143 DCHECK_EQ(Status::OK, ValidateFormatTag(format_tag));
145 format_tag = format_tag.substr(0, format_tag.size() - 1) + PRIu64;
148 format_tag =
"%01" PRIu64;
154 std::vector<absl::FormatArg> format_args;
155 absl::UntypedFormatSpec format(format_tag);
156 if (identifier ==
"Number") {
158 format_args.emplace_back(
static_cast<uint64_t
>(segment_number));
159 }
else if (identifier ==
"Time") {
160 format_args.emplace_back(
static_cast<uint64_t
>(segment_start_time));
161 }
else if (identifier ==
"Bandwidth") {
162 format_args.emplace_back(
static_cast<uint64_t
>(bandwidth));
165 std::string format_output;
166 if (absl::FormatUntyped(&format_output, format, format_args)) {
167 segment_name += format_output;
All the methods that are virtual are virtual for mocking.