Support $DecodeTime$ as filename template variable, work-in-progress

This commit is contained in:
Gabe Kopley 2016-07-11 14:40:02 -07:00
parent ad3c1768bb
commit 7b75369b97
9 changed files with 45 additions and 10 deletions

View File

@ -157,7 +157,7 @@ muxer_options.output_file_name = …;
// Specify output segment name pattern for generated segments. It can
// furthermore be configured by using a subset of the SegmentTemplate
// identifiers: $RepresentationID$, $Number$, $Bandwidth$ and $Time$.
// identifiers: $RepresentationID$, $Number$, $Bandwidth$, $Time$ and $DecodeTime$.
// Optional.
muxer_options.segment_template = …;

View File

@ -46,6 +46,10 @@ bool ValidateSegmentTemplate(const std::string& segment_template) {
// ISO/IEC 23009-1:2012 5.3.9.4.4 Template-based Segment URL construction.
// Allowed identifiers: $$, $RepresentationID$, $Number$, $Bandwidth$, $Time$.
// "$" always appears in pairs, so there should be odd number of splits.
// We also allow $DecodeTime$ in order to help work around this longstanding
// bug in Chrome:
// https://bugs.chromium.org/p/chromium/issues/detail?id=398130#c13
// cf. https://github.com/google/shaka-packager/issues/113
if (splits.size() % 2 == 0) {
LOG(ERROR) << "SegmentTemplate: '$' should appear in pairs.";
return false;
@ -72,7 +76,7 @@ bool ValidateSegmentTemplate(const std::string& segment_template) {
return false;
} else if (identifier == "Number") {
has_number = true;
} else if (identifier == "Time") {
} else if (identifier == "Time" || identifier == "DecodeTime") {
has_time = true;
} else if (identifier == "") {
if (format_pos != std::string::npos) {
@ -86,11 +90,11 @@ bool ValidateSegmentTemplate(const std::string& segment_template) {
}
}
if (has_number && has_time) {
LOG(ERROR) << "SegmentTemplate: $Number$ and $Time$ should not co-exist.";
LOG(ERROR) << "SegmentTemplate: $Number$ and $Time$/$DecodeTime$ should not co-exist.";
return false;
}
if (!has_number && !has_time) {
LOG(ERROR) << "SegmentTemplate: $Number$ or $Time$ should exist.";
LOG(ERROR) << "SegmentTemplate: $Number$ or $Time$/$DecodeTime$ should exist.";
return false;
}
// Note: The below check is skipped.
@ -101,6 +105,7 @@ bool ValidateSegmentTemplate(const std::string& segment_template) {
std::string GetSegmentName(const std::string& segment_template,
uint64_t segment_start_time,
uint64_t segment_start_decode_time,
uint32_t segment_index,
uint32_t bandwidth) {
DCHECK(ValidateSegmentTemplate(segment_template));
@ -126,7 +131,7 @@ std::string GetSegmentName(const std::string& segment_template,
size_t format_pos = splits[i].find('%');
std::string identifier = splits[i].substr(0, format_pos);
DCHECK(identifier == "Number" || identifier == "Time" ||
identifier == "Bandwidth");
identifier == "DecodeTime" || identifier == "Bandwidth");
std::string format_tag;
if (format_pos != std::string::npos) {
@ -146,6 +151,9 @@ std::string GetSegmentName(const std::string& segment_template,
} else if (identifier == "Time") {
segment_name +=
base::StringPrintf(format_tag.c_str(), segment_start_time);
} else if (identifier == "DecodeTime") {
segment_name +=
base::StringPrintf(format_tag.c_str(), segment_start_decode_time);
} else if (identifier == "Bandwidth") {
segment_name += base::StringPrintf(format_tag.c_str(),
static_cast<uint64_t>(bandwidth));

View File

@ -36,6 +36,7 @@ bool ValidateSegmentTemplate(const std::string& segment_template);
/// @return The segment name with identifier substituted.
std::string GetSegmentName(const std::string& segment_template,
uint64_t segment_start_time,
uint64_t segment_start_decode_time,
uint32_t segment_index,
uint32_t bandwidth);

View File

@ -16,6 +16,7 @@ TEST(MuxerUtilTest, ValidateSegmentTemplate) {
EXPECT_TRUE(ValidateSegmentTemplate("$Number$"));
EXPECT_TRUE(ValidateSegmentTemplate("$Time$"));
EXPECT_TRUE(ValidateSegmentTemplate("$DecodeTime$"));
EXPECT_TRUE(ValidateSegmentTemplate("$Time$$Time$"));
EXPECT_TRUE(ValidateSegmentTemplate("foo$Time$goo"));
EXPECT_TRUE(ValidateSegmentTemplate("$Number$_$Number$"));
@ -30,7 +31,7 @@ TEST(MuxerUtilTest, ValidateSegmentTemplate) {
EXPECT_TRUE(ValidateSegmentTemplate("foo$Time$$$"));
EXPECT_TRUE(ValidateSegmentTemplate("$$$Time$$$"));
// Missing $Number$ / $Time$.
// Missing $Number$ / $Time$ / $DecodeTime$.
EXPECT_FALSE(ValidateSegmentTemplate("$$"));
EXPECT_FALSE(ValidateSegmentTemplate("foo$$goo"));
@ -62,15 +63,18 @@ TEST(MuxerUtilTest, ValidateSegmentTemplateWithFormatTag) {
TEST(MuxerUtilTest, GetSegmentName) {
const uint64_t kSegmentStartTime = 180180;
const uint64_t kSegmentStartDecodeTime = 180173;
const uint32_t kSegmentIndex = 11;
const uint32_t kBandwidth = 1234;
EXPECT_EQ("12", GetSegmentName("$Number$",
kSegmentStartTime,
kSegmentStartTime,
kSegmentIndex,
kBandwidth));
EXPECT_EQ("012",
GetSegmentName("$Number%03d$",
kSegmentStartTime,
kSegmentStartDecodeTime,
kSegmentIndex,
kBandwidth));
EXPECT_EQ(
@ -78,17 +82,26 @@ TEST(MuxerUtilTest, GetSegmentName) {
GetSegmentName(
"$Number%01d$$$foo$$$Number%05d$",
kSegmentStartTime,
kSegmentStartDecodeTime,
kSegmentIndex,
kBandwidth));
EXPECT_EQ("180180",
GetSegmentName("$Time$",
kSegmentStartTime,
kSegmentStartDecodeTime,
kSegmentIndex,
kBandwidth));
EXPECT_EQ("180173",
GetSegmentName("$DecodeTime$",
kSegmentStartTime,
kSegmentStartDecodeTime,
kSegmentIndex,
kBandwidth));
EXPECT_EQ("foo$_$18018000180180.m4s",
GetSegmentName("foo$$_$$$Time%01d$$Time%08d$.m4s",
kSegmentStartTime,
kSegmentStartDecodeTime,
kSegmentIndex,
kBandwidth));
@ -96,11 +109,13 @@ TEST(MuxerUtilTest, GetSegmentName) {
EXPECT_EQ("12-1234",
GetSegmentName("$Number$-$Bandwidth$",
kSegmentStartTime,
kSegmentStartDecodeTime,
kSegmentIndex,
kBandwidth));
EXPECT_EQ("012-001234",
GetSegmentName("$Number%03d$-$Bandwidth%06d$",
kSegmentStartTime,
kSegmentStartDecodeTime,
kSegmentIndex,
kBandwidth));
@ -108,47 +123,56 @@ TEST(MuxerUtilTest, GetSegmentName) {
EXPECT_EQ("12",
GetSegmentName("$Number%00d$",
kSegmentStartTime,
kSegmentStartDecodeTime,
kSegmentIndex,
kBandwidth));
EXPECT_EQ("00012",
GetSegmentName("$Number%005d$",
kSegmentStartTime,
kSegmentStartDecodeTime,
kSegmentIndex,
kBandwidth));
}
TEST(MuxerUtilTest, GetSegmentNameWithIndexZero) {
const uint64_t kSegmentStartTime = 0;
const uint64_t kSegmentStartDecodeTime = 0;
const uint32_t kSegmentIndex = 0;
const uint32_t kBandwidth = 0;
EXPECT_EQ("1", GetSegmentName("$Number$",
kSegmentStartTime,
kSegmentStartDecodeTime,
kSegmentIndex,
kBandwidth));
EXPECT_EQ("001",
GetSegmentName("$Number%03d$",
kSegmentStartTime,
kSegmentStartDecodeTime,
kSegmentIndex,
kBandwidth));
EXPECT_EQ("0", GetSegmentName("$Time$",
kSegmentStartTime,
kSegmentStartDecodeTime,
kSegmentIndex,
kBandwidth));
EXPECT_EQ("00000000.m4s",
GetSegmentName("$Time%08d$.m4s",
kSegmentStartTime,
kSegmentStartDecodeTime,
kSegmentIndex,
kBandwidth));
}
TEST(MuxerUtilTest, GetSegmentNameLargeTime) {
const uint64_t kSegmentStartTime = 1601599839840ULL;
const uint64_t kSegmentStartDecodeTime = 1337;
const uint32_t kSegmentIndex = 8888888;
const uint32_t kBandwidth = 444444;
EXPECT_EQ("1601599839840",
GetSegmentName("$Time$",
kSegmentStartTime,
kSegmentStartDecodeTime,
kSegmentIndex,
kBandwidth));
}

View File

@ -116,7 +116,7 @@ Status TsSegmenter::OpenNewSegmentIfClosed(uint32_t next_pts) {
if (ts_writer_file_opened_)
return Status::OK;
const std::string segment_name =
GetSegmentName(muxer_options_.segment_template, next_pts,
GetSegmentName(muxer_options_.segment_template, next_pts, next_pts,
segment_number_++, muxer_options_.bandwidth);
if (!ts_writer_->NewSegment(segment_name))
return Status(error::MUXER_FAILURE, "Failed to initilize TsPacketWriter.");

View File

@ -150,6 +150,7 @@ Status MultiSegmentSegmenter::WriteSegment() {
} else {
file_name = GetSegmentName(options().segment_template,
sidx()->earliest_presentation_time,
moof()->tracks[0].decode_time.decode_time,
num_segments_++, options().bandwidth);
file = File::Open(file_name.c_str(), "w");
if (file == NULL) {

View File

@ -111,6 +111,7 @@ class Segmenter {
Movie* moov() { return moov_.get(); }
BufferWriter* fragment_buffer() { return fragment_buffer_.get(); }
SegmentIndex* sidx() { return sidx_.get(); }
MovieFragment* moof() { return moof_.get() ; } // NOTE delete me!
MuxerListener* muxer_listener() { return muxer_listener_; }
uint64_t progress_target() { return progress_target_; }

View File

@ -77,8 +77,8 @@ Status MultiSegmentSegmenter::NewSegment(uint64_t start_timescale) {
// Create a new file for the new segment.
std::string segment_name =
GetSegmentName(options().segment_template, start_timescale, num_segment_,
options().bandwidth);
GetSegmentName(options().segment_template, start_timescale,
start_timescale, num_segment_, options().bandwidth);
writer_.reset(new MkvWriter);
Status status = writer_->Open(segment_name);
if (!status.ok())

View File

@ -101,7 +101,7 @@ std::string SegmentTestBase::OutputFileName() const {
}
std::string SegmentTestBase::TemplateFileName(int number) const {
return GetSegmentName(segment_template_, 0, number, 0);
return GetSegmentName(segment_template_, 0, 0, number, 0);
}
SegmentTestBase::ClusterParser::ClusterParser() : in_cluster_(false) {}