Support $DecodeTime$ as filename template variable, work-in-progress
This commit is contained in:
parent
ad3c1768bb
commit
7b75369b97
|
@ -157,7 +157,7 @@ muxer_options.output_file_name = …;
|
||||||
|
|
||||||
// Specify output segment name pattern for generated segments. It can
|
// Specify output segment name pattern for generated segments. It can
|
||||||
// furthermore be configured by using a subset of the SegmentTemplate
|
// furthermore be configured by using a subset of the SegmentTemplate
|
||||||
// identifiers: $RepresentationID$, $Number$, $Bandwidth$ and $Time$.
|
// identifiers: $RepresentationID$, $Number$, $Bandwidth$, $Time$ and $DecodeTime$.
|
||||||
// Optional.
|
// Optional.
|
||||||
muxer_options.segment_template = …;
|
muxer_options.segment_template = …;
|
||||||
|
|
||||||
|
|
|
@ -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.
|
// ISO/IEC 23009-1:2012 5.3.9.4.4 Template-based Segment URL construction.
|
||||||
// Allowed identifiers: $$, $RepresentationID$, $Number$, $Bandwidth$, $Time$.
|
// Allowed identifiers: $$, $RepresentationID$, $Number$, $Bandwidth$, $Time$.
|
||||||
// "$" always appears in pairs, so there should be odd number of splits.
|
// "$" 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) {
|
if (splits.size() % 2 == 0) {
|
||||||
LOG(ERROR) << "SegmentTemplate: '$' should appear in pairs.";
|
LOG(ERROR) << "SegmentTemplate: '$' should appear in pairs.";
|
||||||
return false;
|
return false;
|
||||||
|
@ -72,7 +76,7 @@ bool ValidateSegmentTemplate(const std::string& segment_template) {
|
||||||
return false;
|
return false;
|
||||||
} else if (identifier == "Number") {
|
} else if (identifier == "Number") {
|
||||||
has_number = true;
|
has_number = true;
|
||||||
} else if (identifier == "Time") {
|
} else if (identifier == "Time" || identifier == "DecodeTime") {
|
||||||
has_time = true;
|
has_time = true;
|
||||||
} else if (identifier == "") {
|
} else if (identifier == "") {
|
||||||
if (format_pos != std::string::npos) {
|
if (format_pos != std::string::npos) {
|
||||||
|
@ -86,11 +90,11 @@ bool ValidateSegmentTemplate(const std::string& segment_template) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (has_number && has_time) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
if (!has_number && !has_time) {
|
if (!has_number && !has_time) {
|
||||||
LOG(ERROR) << "SegmentTemplate: $Number$ or $Time$ should exist.";
|
LOG(ERROR) << "SegmentTemplate: $Number$ or $Time$/$DecodeTime$ should exist.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Note: The below check is skipped.
|
// 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,
|
std::string GetSegmentName(const std::string& segment_template,
|
||||||
uint64_t segment_start_time,
|
uint64_t segment_start_time,
|
||||||
|
uint64_t segment_start_decode_time,
|
||||||
uint32_t segment_index,
|
uint32_t segment_index,
|
||||||
uint32_t bandwidth) {
|
uint32_t bandwidth) {
|
||||||
DCHECK(ValidateSegmentTemplate(segment_template));
|
DCHECK(ValidateSegmentTemplate(segment_template));
|
||||||
|
@ -126,7 +131,7 @@ std::string GetSegmentName(const std::string& segment_template,
|
||||||
size_t format_pos = splits[i].find('%');
|
size_t format_pos = splits[i].find('%');
|
||||||
std::string identifier = splits[i].substr(0, format_pos);
|
std::string identifier = splits[i].substr(0, format_pos);
|
||||||
DCHECK(identifier == "Number" || identifier == "Time" ||
|
DCHECK(identifier == "Number" || identifier == "Time" ||
|
||||||
identifier == "Bandwidth");
|
identifier == "DecodeTime" || identifier == "Bandwidth");
|
||||||
|
|
||||||
std::string format_tag;
|
std::string format_tag;
|
||||||
if (format_pos != std::string::npos) {
|
if (format_pos != std::string::npos) {
|
||||||
|
@ -146,6 +151,9 @@ std::string GetSegmentName(const std::string& segment_template,
|
||||||
} else if (identifier == "Time") {
|
} else if (identifier == "Time") {
|
||||||
segment_name +=
|
segment_name +=
|
||||||
base::StringPrintf(format_tag.c_str(), segment_start_time);
|
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") {
|
} else if (identifier == "Bandwidth") {
|
||||||
segment_name += base::StringPrintf(format_tag.c_str(),
|
segment_name += base::StringPrintf(format_tag.c_str(),
|
||||||
static_cast<uint64_t>(bandwidth));
|
static_cast<uint64_t>(bandwidth));
|
||||||
|
|
|
@ -36,6 +36,7 @@ bool ValidateSegmentTemplate(const std::string& segment_template);
|
||||||
/// @return The segment name with identifier substituted.
|
/// @return The segment name with identifier substituted.
|
||||||
std::string GetSegmentName(const std::string& segment_template,
|
std::string GetSegmentName(const std::string& segment_template,
|
||||||
uint64_t segment_start_time,
|
uint64_t segment_start_time,
|
||||||
|
uint64_t segment_start_decode_time,
|
||||||
uint32_t segment_index,
|
uint32_t segment_index,
|
||||||
uint32_t bandwidth);
|
uint32_t bandwidth);
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ TEST(MuxerUtilTest, ValidateSegmentTemplate) {
|
||||||
|
|
||||||
EXPECT_TRUE(ValidateSegmentTemplate("$Number$"));
|
EXPECT_TRUE(ValidateSegmentTemplate("$Number$"));
|
||||||
EXPECT_TRUE(ValidateSegmentTemplate("$Time$"));
|
EXPECT_TRUE(ValidateSegmentTemplate("$Time$"));
|
||||||
|
EXPECT_TRUE(ValidateSegmentTemplate("$DecodeTime$"));
|
||||||
EXPECT_TRUE(ValidateSegmentTemplate("$Time$$Time$"));
|
EXPECT_TRUE(ValidateSegmentTemplate("$Time$$Time$"));
|
||||||
EXPECT_TRUE(ValidateSegmentTemplate("foo$Time$goo"));
|
EXPECT_TRUE(ValidateSegmentTemplate("foo$Time$goo"));
|
||||||
EXPECT_TRUE(ValidateSegmentTemplate("$Number$_$Number$"));
|
EXPECT_TRUE(ValidateSegmentTemplate("$Number$_$Number$"));
|
||||||
|
@ -30,7 +31,7 @@ TEST(MuxerUtilTest, ValidateSegmentTemplate) {
|
||||||
EXPECT_TRUE(ValidateSegmentTemplate("foo$Time$$$"));
|
EXPECT_TRUE(ValidateSegmentTemplate("foo$Time$$$"));
|
||||||
EXPECT_TRUE(ValidateSegmentTemplate("$$$Time$$$"));
|
EXPECT_TRUE(ValidateSegmentTemplate("$$$Time$$$"));
|
||||||
|
|
||||||
// Missing $Number$ / $Time$.
|
// Missing $Number$ / $Time$ / $DecodeTime$.
|
||||||
EXPECT_FALSE(ValidateSegmentTemplate("$$"));
|
EXPECT_FALSE(ValidateSegmentTemplate("$$"));
|
||||||
EXPECT_FALSE(ValidateSegmentTemplate("foo$$goo"));
|
EXPECT_FALSE(ValidateSegmentTemplate("foo$$goo"));
|
||||||
|
|
||||||
|
@ -62,15 +63,18 @@ TEST(MuxerUtilTest, ValidateSegmentTemplateWithFormatTag) {
|
||||||
|
|
||||||
TEST(MuxerUtilTest, GetSegmentName) {
|
TEST(MuxerUtilTest, GetSegmentName) {
|
||||||
const uint64_t kSegmentStartTime = 180180;
|
const uint64_t kSegmentStartTime = 180180;
|
||||||
|
const uint64_t kSegmentStartDecodeTime = 180173;
|
||||||
const uint32_t kSegmentIndex = 11;
|
const uint32_t kSegmentIndex = 11;
|
||||||
const uint32_t kBandwidth = 1234;
|
const uint32_t kBandwidth = 1234;
|
||||||
EXPECT_EQ("12", GetSegmentName("$Number$",
|
EXPECT_EQ("12", GetSegmentName("$Number$",
|
||||||
|
kSegmentStartTime,
|
||||||
kSegmentStartTime,
|
kSegmentStartTime,
|
||||||
kSegmentIndex,
|
kSegmentIndex,
|
||||||
kBandwidth));
|
kBandwidth));
|
||||||
EXPECT_EQ("012",
|
EXPECT_EQ("012",
|
||||||
GetSegmentName("$Number%03d$",
|
GetSegmentName("$Number%03d$",
|
||||||
kSegmentStartTime,
|
kSegmentStartTime,
|
||||||
|
kSegmentStartDecodeTime,
|
||||||
kSegmentIndex,
|
kSegmentIndex,
|
||||||
kBandwidth));
|
kBandwidth));
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
|
@ -78,17 +82,26 @@ TEST(MuxerUtilTest, GetSegmentName) {
|
||||||
GetSegmentName(
|
GetSegmentName(
|
||||||
"$Number%01d$$$foo$$$Number%05d$",
|
"$Number%01d$$$foo$$$Number%05d$",
|
||||||
kSegmentStartTime,
|
kSegmentStartTime,
|
||||||
|
kSegmentStartDecodeTime,
|
||||||
kSegmentIndex,
|
kSegmentIndex,
|
||||||
kBandwidth));
|
kBandwidth));
|
||||||
|
|
||||||
EXPECT_EQ("180180",
|
EXPECT_EQ("180180",
|
||||||
GetSegmentName("$Time$",
|
GetSegmentName("$Time$",
|
||||||
kSegmentStartTime,
|
kSegmentStartTime,
|
||||||
|
kSegmentStartDecodeTime,
|
||||||
|
kSegmentIndex,
|
||||||
|
kBandwidth));
|
||||||
|
EXPECT_EQ("180173",
|
||||||
|
GetSegmentName("$DecodeTime$",
|
||||||
|
kSegmentStartTime,
|
||||||
|
kSegmentStartDecodeTime,
|
||||||
kSegmentIndex,
|
kSegmentIndex,
|
||||||
kBandwidth));
|
kBandwidth));
|
||||||
EXPECT_EQ("foo$_$18018000180180.m4s",
|
EXPECT_EQ("foo$_$18018000180180.m4s",
|
||||||
GetSegmentName("foo$$_$$$Time%01d$$Time%08d$.m4s",
|
GetSegmentName("foo$$_$$$Time%01d$$Time%08d$.m4s",
|
||||||
kSegmentStartTime,
|
kSegmentStartTime,
|
||||||
|
kSegmentStartDecodeTime,
|
||||||
kSegmentIndex,
|
kSegmentIndex,
|
||||||
kBandwidth));
|
kBandwidth));
|
||||||
|
|
||||||
|
@ -96,11 +109,13 @@ TEST(MuxerUtilTest, GetSegmentName) {
|
||||||
EXPECT_EQ("12-1234",
|
EXPECT_EQ("12-1234",
|
||||||
GetSegmentName("$Number$-$Bandwidth$",
|
GetSegmentName("$Number$-$Bandwidth$",
|
||||||
kSegmentStartTime,
|
kSegmentStartTime,
|
||||||
|
kSegmentStartDecodeTime,
|
||||||
kSegmentIndex,
|
kSegmentIndex,
|
||||||
kBandwidth));
|
kBandwidth));
|
||||||
EXPECT_EQ("012-001234",
|
EXPECT_EQ("012-001234",
|
||||||
GetSegmentName("$Number%03d$-$Bandwidth%06d$",
|
GetSegmentName("$Number%03d$-$Bandwidth%06d$",
|
||||||
kSegmentStartTime,
|
kSegmentStartTime,
|
||||||
|
kSegmentStartDecodeTime,
|
||||||
kSegmentIndex,
|
kSegmentIndex,
|
||||||
kBandwidth));
|
kBandwidth));
|
||||||
|
|
||||||
|
@ -108,47 +123,56 @@ TEST(MuxerUtilTest, GetSegmentName) {
|
||||||
EXPECT_EQ("12",
|
EXPECT_EQ("12",
|
||||||
GetSegmentName("$Number%00d$",
|
GetSegmentName("$Number%00d$",
|
||||||
kSegmentStartTime,
|
kSegmentStartTime,
|
||||||
|
kSegmentStartDecodeTime,
|
||||||
kSegmentIndex,
|
kSegmentIndex,
|
||||||
kBandwidth));
|
kBandwidth));
|
||||||
EXPECT_EQ("00012",
|
EXPECT_EQ("00012",
|
||||||
GetSegmentName("$Number%005d$",
|
GetSegmentName("$Number%005d$",
|
||||||
kSegmentStartTime,
|
kSegmentStartTime,
|
||||||
|
kSegmentStartDecodeTime,
|
||||||
kSegmentIndex,
|
kSegmentIndex,
|
||||||
kBandwidth));
|
kBandwidth));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MuxerUtilTest, GetSegmentNameWithIndexZero) {
|
TEST(MuxerUtilTest, GetSegmentNameWithIndexZero) {
|
||||||
const uint64_t kSegmentStartTime = 0;
|
const uint64_t kSegmentStartTime = 0;
|
||||||
|
const uint64_t kSegmentStartDecodeTime = 0;
|
||||||
const uint32_t kSegmentIndex = 0;
|
const uint32_t kSegmentIndex = 0;
|
||||||
const uint32_t kBandwidth = 0;
|
const uint32_t kBandwidth = 0;
|
||||||
EXPECT_EQ("1", GetSegmentName("$Number$",
|
EXPECT_EQ("1", GetSegmentName("$Number$",
|
||||||
kSegmentStartTime,
|
kSegmentStartTime,
|
||||||
|
kSegmentStartDecodeTime,
|
||||||
kSegmentIndex,
|
kSegmentIndex,
|
||||||
kBandwidth));
|
kBandwidth));
|
||||||
EXPECT_EQ("001",
|
EXPECT_EQ("001",
|
||||||
GetSegmentName("$Number%03d$",
|
GetSegmentName("$Number%03d$",
|
||||||
kSegmentStartTime,
|
kSegmentStartTime,
|
||||||
|
kSegmentStartDecodeTime,
|
||||||
kSegmentIndex,
|
kSegmentIndex,
|
||||||
kBandwidth));
|
kBandwidth));
|
||||||
|
|
||||||
EXPECT_EQ("0", GetSegmentName("$Time$",
|
EXPECT_EQ("0", GetSegmentName("$Time$",
|
||||||
kSegmentStartTime,
|
kSegmentStartTime,
|
||||||
|
kSegmentStartDecodeTime,
|
||||||
kSegmentIndex,
|
kSegmentIndex,
|
||||||
kBandwidth));
|
kBandwidth));
|
||||||
EXPECT_EQ("00000000.m4s",
|
EXPECT_EQ("00000000.m4s",
|
||||||
GetSegmentName("$Time%08d$.m4s",
|
GetSegmentName("$Time%08d$.m4s",
|
||||||
kSegmentStartTime,
|
kSegmentStartTime,
|
||||||
|
kSegmentStartDecodeTime,
|
||||||
kSegmentIndex,
|
kSegmentIndex,
|
||||||
kBandwidth));
|
kBandwidth));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MuxerUtilTest, GetSegmentNameLargeTime) {
|
TEST(MuxerUtilTest, GetSegmentNameLargeTime) {
|
||||||
const uint64_t kSegmentStartTime = 1601599839840ULL;
|
const uint64_t kSegmentStartTime = 1601599839840ULL;
|
||||||
|
const uint64_t kSegmentStartDecodeTime = 1337;
|
||||||
const uint32_t kSegmentIndex = 8888888;
|
const uint32_t kSegmentIndex = 8888888;
|
||||||
const uint32_t kBandwidth = 444444;
|
const uint32_t kBandwidth = 444444;
|
||||||
EXPECT_EQ("1601599839840",
|
EXPECT_EQ("1601599839840",
|
||||||
GetSegmentName("$Time$",
|
GetSegmentName("$Time$",
|
||||||
kSegmentStartTime,
|
kSegmentStartTime,
|
||||||
|
kSegmentStartDecodeTime,
|
||||||
kSegmentIndex,
|
kSegmentIndex,
|
||||||
kBandwidth));
|
kBandwidth));
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,7 @@ Status TsSegmenter::OpenNewSegmentIfClosed(uint32_t next_pts) {
|
||||||
if (ts_writer_file_opened_)
|
if (ts_writer_file_opened_)
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
const std::string segment_name =
|
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);
|
segment_number_++, muxer_options_.bandwidth);
|
||||||
if (!ts_writer_->NewSegment(segment_name))
|
if (!ts_writer_->NewSegment(segment_name))
|
||||||
return Status(error::MUXER_FAILURE, "Failed to initilize TsPacketWriter.");
|
return Status(error::MUXER_FAILURE, "Failed to initilize TsPacketWriter.");
|
||||||
|
|
|
@ -150,6 +150,7 @@ Status MultiSegmentSegmenter::WriteSegment() {
|
||||||
} else {
|
} else {
|
||||||
file_name = GetSegmentName(options().segment_template,
|
file_name = GetSegmentName(options().segment_template,
|
||||||
sidx()->earliest_presentation_time,
|
sidx()->earliest_presentation_time,
|
||||||
|
moof()->tracks[0].decode_time.decode_time,
|
||||||
num_segments_++, options().bandwidth);
|
num_segments_++, options().bandwidth);
|
||||||
file = File::Open(file_name.c_str(), "w");
|
file = File::Open(file_name.c_str(), "w");
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
|
|
|
@ -111,6 +111,7 @@ class Segmenter {
|
||||||
Movie* moov() { return moov_.get(); }
|
Movie* moov() { return moov_.get(); }
|
||||||
BufferWriter* fragment_buffer() { return fragment_buffer_.get(); }
|
BufferWriter* fragment_buffer() { return fragment_buffer_.get(); }
|
||||||
SegmentIndex* sidx() { return sidx_.get(); }
|
SegmentIndex* sidx() { return sidx_.get(); }
|
||||||
|
MovieFragment* moof() { return moof_.get() ; } // NOTE delete me!
|
||||||
MuxerListener* muxer_listener() { return muxer_listener_; }
|
MuxerListener* muxer_listener() { return muxer_listener_; }
|
||||||
uint64_t progress_target() { return progress_target_; }
|
uint64_t progress_target() { return progress_target_; }
|
||||||
|
|
||||||
|
|
|
@ -77,8 +77,8 @@ Status MultiSegmentSegmenter::NewSegment(uint64_t start_timescale) {
|
||||||
|
|
||||||
// Create a new file for the new segment.
|
// Create a new file for the new segment.
|
||||||
std::string segment_name =
|
std::string segment_name =
|
||||||
GetSegmentName(options().segment_template, start_timescale, num_segment_,
|
GetSegmentName(options().segment_template, start_timescale,
|
||||||
options().bandwidth);
|
start_timescale, num_segment_, options().bandwidth);
|
||||||
writer_.reset(new MkvWriter);
|
writer_.reset(new MkvWriter);
|
||||||
Status status = writer_->Open(segment_name);
|
Status status = writer_->Open(segment_name);
|
||||||
if (!status.ok())
|
if (!status.ok())
|
||||||
|
|
|
@ -101,7 +101,7 @@ std::string SegmentTestBase::OutputFileName() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SegmentTestBase::TemplateFileName(int number) 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) {}
|
SegmentTestBase::ClusterParser::ClusterParser() : in_cluster_(false) {}
|
||||||
|
|
Loading…
Reference in New Issue