Made Verify Functions Return Status
There are multiple functions we use to verify input from the user and must of them would return a boolean expect for the top level function which would return a Status. Each function would log a message but to the user the error was not clear. This change makes each verify function return a status so that the actual problem will be surfaced easier. Change-Id: I12ab43f8ca3a4d379b4309336644a70eb8cbbbe0
This commit is contained in:
parent
f2cbe8f9c1
commit
7c5508555c
|
@ -19,26 +19,33 @@
|
|||
|
||||
namespace shaka {
|
||||
namespace {
|
||||
bool ValidateFormatTag(const std::string& format_tag) {
|
||||
DCHECK(!format_tag.empty());
|
||||
Status ValidateFormatTag(const std::string& format_tag) {
|
||||
if (format_tag.empty()) {
|
||||
return Status(error::INVALID_ARGUMENT, "Format tag should not be empty");
|
||||
}
|
||||
|
||||
// Format tag should follow this prototype: %0[width]d.
|
||||
if (format_tag.size() > 3 && format_tag[0] == '%' && format_tag[1] == '0' &&
|
||||
format_tag[format_tag.size() - 1] == 'd') {
|
||||
unsigned out;
|
||||
if (base::StringToUint(format_tag.substr(2, format_tag.size() - 3), &out))
|
||||
return true;
|
||||
if (base::StringToUint(format_tag.substr(2, format_tag.size() - 3), &out)) {
|
||||
return Status::OK;
|
||||
}
|
||||
LOG(ERROR) << "SegmentTemplate: Format tag should follow this prototype: "
|
||||
<< "%0[width]d if exist.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return Status(
|
||||
error::INVALID_ARGUMENT,
|
||||
"Format tag should follow this prototype: %0[width]d if exist.");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace media {
|
||||
|
||||
bool ValidateSegmentTemplate(const std::string& segment_template) {
|
||||
if (segment_template.empty())
|
||||
return false;
|
||||
Status ValidateSegmentTemplate(const std::string& segment_template) {
|
||||
if (segment_template.empty()) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"Segment template should not be empty.");
|
||||
}
|
||||
|
||||
std::vector<std::string> splits = base::SplitString(
|
||||
segment_template, "$", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
|
||||
|
@ -47,8 +54,8 @@ bool ValidateSegmentTemplate(const std::string& segment_template) {
|
|||
// Allowed identifiers: $$, $RepresentationID$, $Number$, $Bandwidth$, $Time$.
|
||||
// "$" always appears in pairs, so there should be odd number of splits.
|
||||
if (splits.size() % 2 == 0) {
|
||||
LOG(ERROR) << "SegmentTemplate: '$' should appear in pairs.";
|
||||
return false;
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"In segment templates, '$' should appear in pairs.");
|
||||
}
|
||||
|
||||
bool has_number = false;
|
||||
|
@ -61,49 +68,51 @@ bool ValidateSegmentTemplate(const std::string& segment_template) {
|
|||
size_t format_pos = splits[i].find('%');
|
||||
std::string identifier = splits[i].substr(0, format_pos);
|
||||
if (format_pos != std::string::npos) {
|
||||
if (!ValidateFormatTag(splits[i].substr(format_pos)))
|
||||
return false;
|
||||
Status tag_check = ValidateFormatTag(splits[i].substr(format_pos));
|
||||
if (!tag_check.ok()) {
|
||||
return tag_check;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(kqyang): Support "RepresentationID".
|
||||
if (identifier == "RepresentationID") {
|
||||
NOTIMPLEMENTED() << "SegmentTemplate: $RepresentationID$ is not supported "
|
||||
"yet.";
|
||||
return false;
|
||||
return Status(
|
||||
error::UNIMPLEMENTED,
|
||||
"Segment template flag $RepresentationID$ is not supported yet.");
|
||||
} else if (identifier == "Number") {
|
||||
has_number = true;
|
||||
} else if (identifier == "Time") {
|
||||
has_time = true;
|
||||
} else if (identifier == "") {
|
||||
if (format_pos != std::string::npos) {
|
||||
LOG(ERROR) << "SegmentTemplate: $$ should not have any format tags.";
|
||||
return false;
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"'$$' should not have any format tags.");
|
||||
}
|
||||
} else if (identifier != "Bandwidth") {
|
||||
LOG(ERROR) << "SegmentTemplate: '$" << splits[i]
|
||||
<< "$' is not a valid identifier.";
|
||||
return false;
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"'$" + splits[i] + "$' is not a valid identifier.");
|
||||
}
|
||||
}
|
||||
if (has_number && has_time) {
|
||||
LOG(ERROR) << "SegmentTemplate: $Number$ and $Time$ should not co-exist.";
|
||||
return false;
|
||||
return Status(
|
||||
error::INVALID_ARGUMENT,
|
||||
"In segment templates $Number$ and $Time$ should not co-exist.");
|
||||
}
|
||||
if (!has_number && !has_time) {
|
||||
LOG(ERROR) << "SegmentTemplate: $Number$ or $Time$ should exist.";
|
||||
return false;
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"In segment templates $Number$ or $Time$ should exist.");
|
||||
}
|
||||
// Note: The below check is skipped.
|
||||
// Strings outside identifiers shall only contain characters that are
|
||||
// permitted within URLs according to RFC 1738.
|
||||
return true;
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
std::string GetSegmentName(const std::string& segment_template,
|
||||
uint64_t segment_start_time,
|
||||
uint32_t segment_index,
|
||||
uint32_t bandwidth) {
|
||||
DCHECK(ValidateSegmentTemplate(segment_template));
|
||||
DCHECK_EQ(Status::OK, ValidateSegmentTemplate(segment_template));
|
||||
|
||||
std::vector<std::string> splits = base::SplitString(
|
||||
segment_template, "$", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
|
||||
|
@ -131,7 +140,7 @@ std::string GetSegmentName(const std::string& segment_template,
|
|||
std::string format_tag;
|
||||
if (format_pos != std::string::npos) {
|
||||
format_tag = splits[i].substr(format_pos);
|
||||
DCHECK(ValidateFormatTag(format_tag));
|
||||
DCHECK_EQ(Status::OK, ValidateFormatTag(format_tag));
|
||||
// Replace %d formatting to correctly format uint64_t.
|
||||
format_tag = format_tag.substr(0, format_tag.size() - 1) + PRIu64;
|
||||
} else {
|
||||
|
|
|
@ -23,9 +23,9 @@ class StreamInfo;
|
|||
/// Validates the segment template against segment URL construction rule
|
||||
/// specified in ISO/IEC 23009-1:2012 5.3.9.4.4.
|
||||
/// @param segment_template is the template to be validated.
|
||||
/// @return true if the segment template complies with
|
||||
// ISO/IEC 23009-1:2012 5.3.9.4.4, false otherwise.
|
||||
bool ValidateSegmentTemplate(const std::string& segment_template);
|
||||
/// @return OK if the segment template complies with
|
||||
// ISO/IEC 23009-1:2012 5.3.9.4.4.
|
||||
Status ValidateSegmentTemplate(const std::string& segment_template);
|
||||
|
||||
/// Build the segment name from provided input.
|
||||
/// @param segment_template is the segment template pattern, which should
|
||||
|
|
|
@ -12,52 +12,52 @@ namespace shaka {
|
|||
namespace media {
|
||||
|
||||
TEST(MuxerUtilTest, ValidateSegmentTemplate) {
|
||||
EXPECT_FALSE(ValidateSegmentTemplate(""));
|
||||
EXPECT_NE(Status::OK, ValidateSegmentTemplate(""));
|
||||
|
||||
EXPECT_TRUE(ValidateSegmentTemplate("$Number$"));
|
||||
EXPECT_TRUE(ValidateSegmentTemplate("$Time$"));
|
||||
EXPECT_TRUE(ValidateSegmentTemplate("$Time$$Time$"));
|
||||
EXPECT_TRUE(ValidateSegmentTemplate("foo$Time$goo"));
|
||||
EXPECT_TRUE(ValidateSegmentTemplate("$Number$_$Number$"));
|
||||
EXPECT_TRUE(ValidateSegmentTemplate("$Number$$Bandwidth$"));
|
||||
EXPECT_TRUE(ValidateSegmentTemplate("$Time$$Bandwidth$"));
|
||||
EXPECT_EQ(Status::OK, ValidateSegmentTemplate("$Number$"));
|
||||
EXPECT_EQ(Status::OK, ValidateSegmentTemplate("$Time$"));
|
||||
EXPECT_EQ(Status::OK, ValidateSegmentTemplate("$Time$$Time$"));
|
||||
EXPECT_EQ(Status::OK, ValidateSegmentTemplate("foo$Time$goo"));
|
||||
EXPECT_EQ(Status::OK, ValidateSegmentTemplate("$Number$_$Number$"));
|
||||
EXPECT_EQ(Status::OK, ValidateSegmentTemplate("$Number$$Bandwidth$"));
|
||||
EXPECT_EQ(Status::OK, ValidateSegmentTemplate("$Time$$Bandwidth$"));
|
||||
|
||||
// Bandwidth without Number or Time should not be valid.
|
||||
EXPECT_FALSE(ValidateSegmentTemplate("$Bandwidth$"));
|
||||
EXPECT_NE(Status::OK, ValidateSegmentTemplate("$Bandwidth$"));
|
||||
|
||||
// Escape sequence "$$".
|
||||
EXPECT_TRUE(ValidateSegmentTemplate("foo$Time$__$$loo"));
|
||||
EXPECT_TRUE(ValidateSegmentTemplate("foo$Time$$$"));
|
||||
EXPECT_TRUE(ValidateSegmentTemplate("$$$Time$$$"));
|
||||
EXPECT_EQ(Status::OK, ValidateSegmentTemplate("foo$Time$__$$loo"));
|
||||
EXPECT_EQ(Status::OK, ValidateSegmentTemplate("foo$Time$$$"));
|
||||
EXPECT_EQ(Status::OK, ValidateSegmentTemplate("$$$Time$$$"));
|
||||
|
||||
// Missing $Number$ / $Time$.
|
||||
EXPECT_FALSE(ValidateSegmentTemplate("$$"));
|
||||
EXPECT_FALSE(ValidateSegmentTemplate("foo$$goo"));
|
||||
EXPECT_NE(Status::OK, ValidateSegmentTemplate("$$"));
|
||||
EXPECT_NE(Status::OK, ValidateSegmentTemplate("foo$$goo"));
|
||||
|
||||
// $Number$, $Time$ should not co-exist.
|
||||
EXPECT_FALSE(ValidateSegmentTemplate("$Number$$Time$"));
|
||||
EXPECT_FALSE(ValidateSegmentTemplate("foo$Number$_$Time$loo"));
|
||||
EXPECT_NE(Status::OK, ValidateSegmentTemplate("$Number$$Time$"));
|
||||
EXPECT_NE(Status::OK, ValidateSegmentTemplate("foo$Number$_$Time$loo"));
|
||||
|
||||
// $RepresentationID$ not implemented yet.
|
||||
EXPECT_FALSE(ValidateSegmentTemplate("$RepresentationID$__$Time$"));
|
||||
EXPECT_NE(Status::OK, ValidateSegmentTemplate("$RepresentationID$__$Time$"));
|
||||
|
||||
// Unknown identifier.
|
||||
EXPECT_FALSE(ValidateSegmentTemplate("$foo$$Time$"));
|
||||
EXPECT_NE(Status::OK, ValidateSegmentTemplate("$foo$$Time$"));
|
||||
}
|
||||
|
||||
TEST(MuxerUtilTest, ValidateSegmentTemplateWithFormatTag) {
|
||||
EXPECT_TRUE(ValidateSegmentTemplate("$Time%01d$"));
|
||||
EXPECT_TRUE(ValidateSegmentTemplate("$Time%05d$"));
|
||||
EXPECT_FALSE(ValidateSegmentTemplate("$Time%1d$"));
|
||||
EXPECT_FALSE(ValidateSegmentTemplate("$Time%$"));
|
||||
EXPECT_FALSE(ValidateSegmentTemplate("$Time%01$"));
|
||||
EXPECT_FALSE(ValidateSegmentTemplate("$Time%0xd$"));
|
||||
EXPECT_FALSE(ValidateSegmentTemplate("$Time%03xd$"));
|
||||
EXPECT_EQ(Status::OK, ValidateSegmentTemplate("$Time%01d$"));
|
||||
EXPECT_EQ(Status::OK, ValidateSegmentTemplate("$Time%05d$"));
|
||||
EXPECT_NE(Status::OK, ValidateSegmentTemplate("$Time%1d$"));
|
||||
EXPECT_NE(Status::OK, ValidateSegmentTemplate("$Time%$"));
|
||||
EXPECT_NE(Status::OK, ValidateSegmentTemplate("$Time%01$"));
|
||||
EXPECT_NE(Status::OK, ValidateSegmentTemplate("$Time%0xd$"));
|
||||
EXPECT_NE(Status::OK, ValidateSegmentTemplate("$Time%03xd$"));
|
||||
// $$ should not have any format tag.
|
||||
EXPECT_FALSE(ValidateSegmentTemplate("$%01d$$Time$"));
|
||||
EXPECT_NE(Status::OK, ValidateSegmentTemplate("$%01d$$Time$"));
|
||||
// Format specifier edge cases.
|
||||
EXPECT_TRUE(ValidateSegmentTemplate("$Time%00d$"));
|
||||
EXPECT_TRUE(ValidateSegmentTemplate("$Time%005d$"));
|
||||
EXPECT_EQ(Status::OK, ValidateSegmentTemplate("$Time%00d$"));
|
||||
EXPECT_EQ(Status::OK, ValidateSegmentTemplate("$Time%005d$"));
|
||||
}
|
||||
|
||||
TEST(MuxerUtilTest, GetSegmentName) {
|
||||
|
|
|
@ -97,66 +97,84 @@ MediaContainerName GetOutputFormat(const StreamDescriptor& descriptor) {
|
|||
return output_format;
|
||||
}
|
||||
|
||||
bool ValidateStreamDescriptor(bool dump_stream_info,
|
||||
const StreamDescriptor& descriptor) {
|
||||
// Validate and insert the descriptor
|
||||
if (descriptor.input.empty()) {
|
||||
LOG(ERROR) << "Stream input not specified.";
|
||||
return false;
|
||||
}
|
||||
if (!dump_stream_info && descriptor.stream_selector.empty()) {
|
||||
LOG(ERROR) << "Stream stream_selector not specified.";
|
||||
return false;
|
||||
Status ValidateStreamDescriptor(bool dump_stream_info,
|
||||
const StreamDescriptor& stream) {
|
||||
if (stream.input.empty()) {
|
||||
return Status(error::INVALID_ARGUMENT, "Stream input not specified.");
|
||||
}
|
||||
|
||||
// We should have either output or segment_template specified.
|
||||
const bool output_specified =
|
||||
!descriptor.output.empty() || !descriptor.segment_template.empty();
|
||||
if (!output_specified) {
|
||||
if (!dump_stream_info) {
|
||||
LOG(ERROR) << "Stream output not specified.";
|
||||
return false;
|
||||
// The only time a stream can have no outputs, is when dump stream info is
|
||||
// set.
|
||||
if (dump_stream_info && stream.output.empty() &&
|
||||
stream.segment_template.empty()) {
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
if (stream.output.empty() && stream.segment_template.empty()) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"Streams must specify 'output' or 'segment template'.");
|
||||
}
|
||||
|
||||
// Whenever there is output, a stream must be selected.
|
||||
if (stream.stream_selector.empty()) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"Stream stream_selector not specified.");
|
||||
}
|
||||
|
||||
// If a segment template is provided, it must be valid.
|
||||
if (stream.segment_template.length()) {
|
||||
Status template_check = ValidateSegmentTemplate(stream.segment_template);
|
||||
if (!template_check.ok()) {
|
||||
return template_check;
|
||||
}
|
||||
}
|
||||
|
||||
// There are some specifics that must be checked based on which format
|
||||
// we are writing to.
|
||||
const MediaContainerName output_format = GetOutputFormat(stream);
|
||||
|
||||
if (output_format == CONTAINER_UNKNOWN) {
|
||||
return Status(error::INVALID_ARGUMENT, "Unsupported output format.");
|
||||
} else if (output_format == MediaContainerName::CONTAINER_MPEG2TS) {
|
||||
if (stream.segment_template.empty()) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"Please specify segment_template. Single file TS output is "
|
||||
"not supported.");
|
||||
}
|
||||
|
||||
// Right now the init segment is saved in |output| for multi-segment
|
||||
// content. However, for TS all segments must be self-initializing so
|
||||
// there cannot be an init segment.
|
||||
if (stream.output.length()) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"All TS segments must be self-initializing. Stream "
|
||||
"descriptors 'output' or 'init_segment' are not allowed.");
|
||||
}
|
||||
} else {
|
||||
const MediaContainerName output_format = GetOutputFormat(descriptor);
|
||||
if (output_format == CONTAINER_UNKNOWN)
|
||||
return false;
|
||||
|
||||
if (output_format == MediaContainerName::CONTAINER_MPEG2TS) {
|
||||
if (descriptor.segment_template.empty()) {
|
||||
LOG(ERROR) << "Please specify segment_template. Single file TS output "
|
||||
"is not supported.";
|
||||
return false;
|
||||
// For any other format, if there is a segment template, there must be an
|
||||
// init segment provided.
|
||||
if (stream.segment_template.length() && stream.output.empty()) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"Please specify 'init_segment'. All non-TS multi-segment "
|
||||
"content must provide an init segment.");
|
||||
}
|
||||
// Note that MPEG2 TS doesn't need a separate initialization segment, so
|
||||
// output field is not needed.
|
||||
if (!descriptor.output.empty()) {
|
||||
LOG(WARNING) << "TS init_segment '" << descriptor.output
|
||||
<< "' ignored. TS muxer does not support initialization "
|
||||
"segment generation.";
|
||||
}
|
||||
} else {
|
||||
if (descriptor.output.empty()) {
|
||||
LOG(ERROR) << "init_segment is required for format " << output_format;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidateParams(const PackagingParams& packaging_params,
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
Status ValidateParams(const PackagingParams& packaging_params,
|
||||
const std::vector<StreamDescriptor>& stream_descriptors) {
|
||||
if (!packaging_params.chunking_params.segment_sap_aligned &&
|
||||
packaging_params.chunking_params.subsegment_sap_aligned) {
|
||||
LOG(ERROR) << "Setting segment_sap_aligned to false but "
|
||||
"subsegment_sap_aligned to true is not allowed.";
|
||||
return false;
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"Setting segment_sap_aligned to false but "
|
||||
"subsegment_sap_aligned to true is not allowed.");
|
||||
}
|
||||
|
||||
if (stream_descriptors.empty()) {
|
||||
LOG(ERROR) << "Stream descriptors cannot be empty.";
|
||||
return false;
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"Stream descriptors cannot be empty.");
|
||||
}
|
||||
|
||||
// On demand profile generates single file segment while live profile
|
||||
|
@ -165,23 +183,28 @@ bool ValidateParams(const PackagingParams& packaging_params,
|
|||
stream_descriptors.begin()->segment_template.empty();
|
||||
for (const auto& descriptor : stream_descriptors) {
|
||||
if (on_demand_dash_profile != descriptor.segment_template.empty()) {
|
||||
LOG(ERROR) << "Inconsistent stream descriptor specification: "
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"Inconsistent stream descriptor specification: "
|
||||
"segment_template should be specified for none or all "
|
||||
"stream descriptors.";
|
||||
return false;
|
||||
}
|
||||
if (!ValidateStreamDescriptor(packaging_params.test_params.dump_stream_info,
|
||||
descriptor))
|
||||
return false;
|
||||
}
|
||||
if (packaging_params.output_media_info && !on_demand_dash_profile) {
|
||||
// TODO(rkuroiwa, kqyang): Support partial media info dump for live.
|
||||
NOTIMPLEMENTED() << "ERROR: --output_media_info is only supported for "
|
||||
"on-demand profile (not using segment_template).";
|
||||
return false;
|
||||
"stream descriptors.");
|
||||
}
|
||||
|
||||
return true;
|
||||
Status stream_check = ValidateStreamDescriptor(
|
||||
packaging_params.test_params.dump_stream_info, descriptor);
|
||||
|
||||
if (!stream_check.ok()) {
|
||||
return stream_check;
|
||||
}
|
||||
}
|
||||
|
||||
if (packaging_params.output_media_info && !on_demand_dash_profile) {
|
||||
// TODO(rkuroiwa, kqyang): Support partial media info dump for live.
|
||||
return Status(error::UNIMPLEMENTED,
|
||||
"--output_media_info is only supported for on-demand profile "
|
||||
"(not using segment_template).");
|
||||
}
|
||||
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
class StreamDescriptorCompareFn {
|
||||
|
@ -364,10 +387,10 @@ Status CreateRemuxJobs(const StreamDescriptorList& stream_descriptors,
|
|||
stream_muxer_options.temp_dir = packaging_params.temp_dir;
|
||||
stream_muxer_options.output_file_name = stream_iter->output;
|
||||
if (!stream_iter->segment_template.empty()) {
|
||||
if (!ValidateSegmentTemplate(stream_iter->segment_template)) {
|
||||
return Status(
|
||||
error::INVALID_ARGUMENT,
|
||||
"Invalid segment template: " + stream_iter->segment_template);
|
||||
Status template_check =
|
||||
ValidateSegmentTemplate(stream_iter->segment_template);
|
||||
if (!template_check.ok()) {
|
||||
return template_check;
|
||||
}
|
||||
stream_muxer_options.segment_template = stream_iter->segment_template;
|
||||
}
|
||||
|
@ -605,8 +628,11 @@ Status Packager::Initialize(
|
|||
if (internal_)
|
||||
return Status(error::INVALID_ARGUMENT, "Already initialized.");
|
||||
|
||||
if (!media::ValidateParams(packaging_params, stream_descriptors))
|
||||
return Status(error::INVALID_ARGUMENT, "Invalid packaging params.");
|
||||
Status param_check =
|
||||
media::ValidateParams(packaging_params, stream_descriptors);
|
||||
if (!param_check.ok()) {
|
||||
return param_check;
|
||||
}
|
||||
|
||||
if (!packaging_params.test_params.injected_library_version.empty()) {
|
||||
SetPackagerVersionForTesting(
|
||||
|
|
Loading…
Reference in New Issue