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
|
||||
// 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 = …;
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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_; }
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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) {}
|
||||
|
|
Loading…
Reference in New Issue