Support segment template identifier $Time$
Also add support for format tags. Also change default fragment_duration to 10s, i.e. to have the same value as --segment_duration. So by default, only one fragment per (sub)segment is generated. Change-Id: I21123723c3998b656037a397eb7b58b3d91721bb
This commit is contained in:
parent
72ad649ac4
commit
5b1980651f
|
@ -9,7 +9,7 @@
|
||||||
#include "app/muxer_flags.h"
|
#include "app/muxer_flags.h"
|
||||||
|
|
||||||
DEFINE_double(clear_lead,
|
DEFINE_double(clear_lead,
|
||||||
10.0,
|
10.0f,
|
||||||
"Clear lead in seconds if encryption is enabled.");
|
"Clear lead in seconds if encryption is enabled.");
|
||||||
|
|
||||||
DEFINE_bool(single_segment,
|
DEFINE_bool(single_segment,
|
||||||
|
@ -26,7 +26,7 @@ DEFINE_bool(segment_sap_aligned,
|
||||||
true,
|
true,
|
||||||
"Force segments to begin with stream access points.");
|
"Force segments to begin with stream access points.");
|
||||||
DEFINE_double(fragment_duration,
|
DEFINE_double(fragment_duration,
|
||||||
2.0f,
|
10.0f,
|
||||||
"Fragment duration in seconds. Should not be larger than "
|
"Fragment duration in seconds. Should not be larger than "
|
||||||
"the segment duration. Actual fragment durations may not be "
|
"the segment duration. Actual fragment durations may not be "
|
||||||
"exactly as requested.");
|
"exactly as requested.");
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "media/base/demuxer.h"
|
#include "media/base/demuxer.h"
|
||||||
#include "media/base/encryption_key_source.h"
|
#include "media/base/encryption_key_source.h"
|
||||||
#include "media/base/muxer_options.h"
|
#include "media/base/muxer_options.h"
|
||||||
|
#include "media/base/muxer_util.h"
|
||||||
#include "media/formats/mp4/mp4_muxer.h"
|
#include "media/formats/mp4/mp4_muxer.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -105,8 +106,14 @@ bool CreateRemuxJobs(const StringVector& stream_descriptors,
|
||||||
std::string file_path(descriptor[0].substr(0, hash_pos));
|
std::string file_path(descriptor[0].substr(0, hash_pos));
|
||||||
std::string stream_selector(descriptor[0].substr(hash_pos + 1));
|
std::string stream_selector(descriptor[0].substr(hash_pos + 1));
|
||||||
stream_muxer_options.output_file_name = descriptor[1];
|
stream_muxer_options.output_file_name = descriptor[1];
|
||||||
if (descriptor.size() == 3)
|
if (descriptor.size() == 3) {
|
||||||
stream_muxer_options.segment_template = descriptor[2];
|
stream_muxer_options.segment_template = descriptor[2];
|
||||||
|
if (!ValidateSegmentTemplate(stream_muxer_options.segment_template)) {
|
||||||
|
LOG(ERROR) << "ERROR: segment template with '"
|
||||||
|
<< stream_muxer_options.segment_template << "' is invalid.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (file_path != previous_file_path) {
|
if (file_path != previous_file_path) {
|
||||||
// New remux job needed. Create demux and job thread.
|
// New remux job needed. Create demux and job thread.
|
||||||
|
|
|
@ -21,7 +21,5 @@ DEFINE_string(output,
|
||||||
"initialization segment name.");
|
"initialization segment name.");
|
||||||
DEFINE_string(segment_template,
|
DEFINE_string(segment_template,
|
||||||
"",
|
"",
|
||||||
"Output segment name pattern for generated segments. It "
|
"Segment template pattern for generated segments. It should "
|
||||||
"can furthermore be configured using a subset of "
|
"comply with ISO/IEC 23009-1:2012 5.3.9.4.4.");
|
||||||
"SegmentTemplate identifiers: $Number$, $Bandwidth$ and "
|
|
||||||
"$Time$.");
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "media/base/demuxer.h"
|
#include "media/base/demuxer.h"
|
||||||
#include "media/base/encryption_key_source.h"
|
#include "media/base/encryption_key_source.h"
|
||||||
#include "media/base/muxer_options.h"
|
#include "media/base/muxer_options.h"
|
||||||
|
#include "media/base/muxer_util.h"
|
||||||
#include "media/event/vod_media_info_dump_muxer_listener.h"
|
#include "media/event/vod_media_info_dump_muxer_listener.h"
|
||||||
#include "media/file/file.h"
|
#include "media/file/file.h"
|
||||||
#include "media/file/file_closer.h"
|
#include "media/file/file_closer.h"
|
||||||
|
@ -37,6 +38,12 @@ bool GetSingleMuxerOptions(MuxerOptions* muxer_options) {
|
||||||
|
|
||||||
muxer_options->output_file_name = FLAGS_output;
|
muxer_options->output_file_name = FLAGS_output;
|
||||||
muxer_options->segment_template = FLAGS_segment_template;
|
muxer_options->segment_template = FLAGS_segment_template;
|
||||||
|
if (!muxer_options->segment_template.empty() &&
|
||||||
|
!ValidateSegmentTemplate(muxer_options->segment_template)) {
|
||||||
|
LOG(ERROR) << "ERROR: segment template with '"
|
||||||
|
<< muxer_options->segment_template << "' is invalid.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -44,6 +51,11 @@ bool GetSingleMuxerOptions(MuxerOptions* muxer_options) {
|
||||||
bool RunPackager(const std::string& input) {
|
bool RunPackager(const std::string& input) {
|
||||||
Status status;
|
Status status;
|
||||||
|
|
||||||
|
// Get muxer options from commandline flags.
|
||||||
|
MuxerOptions muxer_options;
|
||||||
|
if (!GetSingleMuxerOptions(&muxer_options))
|
||||||
|
return false;
|
||||||
|
|
||||||
// Setup and initialize Demuxer.
|
// Setup and initialize Demuxer.
|
||||||
Demuxer demuxer(input, NULL);
|
Demuxer demuxer(input, NULL);
|
||||||
status = demuxer.Initialize();
|
status = demuxer.Initialize();
|
||||||
|
@ -62,10 +74,6 @@ bool RunPackager(const std::string& input) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup muxer.
|
// Setup muxer.
|
||||||
MuxerOptions muxer_options;
|
|
||||||
if (!GetSingleMuxerOptions(&muxer_options))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
scoped_ptr<Muxer> muxer(new mp4::MP4Muxer(muxer_options));
|
scoped_ptr<Muxer> muxer(new mp4::MP4Muxer(muxer_options));
|
||||||
scoped_ptr<event::MuxerListener> muxer_listener;
|
scoped_ptr<event::MuxerListener> muxer_listener;
|
||||||
scoped_ptr<File, FileCloser> mpd_file;
|
scoped_ptr<File, FileCloser> mpd_file;
|
||||||
|
|
|
@ -86,6 +86,8 @@
|
||||||
'muxer.h',
|
'muxer.h',
|
||||||
'muxer_options.cc',
|
'muxer_options.cc',
|
||||||
'muxer_options.h',
|
'muxer_options.h',
|
||||||
|
'muxer_util.cc',
|
||||||
|
'muxer_util.h',
|
||||||
'offset_byte_queue.cc',
|
'offset_byte_queue.cc',
|
||||||
'offset_byte_queue.h',
|
'offset_byte_queue.h',
|
||||||
'producer_consumer_queue.h',
|
'producer_consumer_queue.h',
|
||||||
|
@ -121,6 +123,7 @@
|
||||||
'container_names_unittest.cc',
|
'container_names_unittest.cc',
|
||||||
'fake_prng.cc', # For rsa_key_unittest
|
'fake_prng.cc', # For rsa_key_unittest
|
||||||
'fake_prng.h', # For rsa_key_unittest
|
'fake_prng.h', # For rsa_key_unittest
|
||||||
|
'muxer_util_unittest.cc',
|
||||||
'offset_byte_queue_unittest.cc',
|
'offset_byte_queue_unittest.cc',
|
||||||
'producer_consumer_queue_unittest.cc',
|
'producer_consumer_queue_unittest.cc',
|
||||||
'rsa_key_unittest.cc',
|
'rsa_key_unittest.cc',
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
// Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
#include "media/base/muxer_util.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "base/strings/string_number_conversions.h"
|
||||||
|
#include "base/strings/string_split.h"
|
||||||
|
#include "base/strings/stringprintf.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
bool ValidateFormatTag(const std::string& format_tag) {
|
||||||
|
DCHECK(!format_tag.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;
|
||||||
|
}
|
||||||
|
LOG(ERROR) << "SegmentTemplate: Format tag should follow this prototype: "
|
||||||
|
<< "%0[width]d if exist.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace media {
|
||||||
|
|
||||||
|
bool ValidateSegmentTemplate(const std::string& segment_template) {
|
||||||
|
if (segment_template.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::vector<std::string> splits;
|
||||||
|
base::SplitString(segment_template, '$', &splits);
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
if (splits.size() % 2 == 0) {
|
||||||
|
LOG(ERROR) << "SegmentTemplate: '$' should appear in pairs.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_number = false;
|
||||||
|
bool has_time = false;
|
||||||
|
// Every second substring in split output should be an identifier.
|
||||||
|
for (size_t i = 1; i < splits.size(); i += 2) {
|
||||||
|
// Each identifier may be suffixed, within the enclosing ‘$’ characters,
|
||||||
|
// with an additional format tag aligned with the printf format tag as
|
||||||
|
// defined in IEEE 1003.1-2008 [10] following this prototype: %0[width]d.
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(kqyang): Support "RepresentationID" and "Bandwidth".
|
||||||
|
if (identifier == "RepresentationID" || identifier == "Bandwidth") {
|
||||||
|
NOTIMPLEMENTED() << "SegmentTemplate: $RepresentationID$ and $Bandwidth$ "
|
||||||
|
"are not supported yet.";
|
||||||
|
return false;
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG(ERROR) << "SegmentTemplate: '$" << splits[i]
|
||||||
|
<< "$' is not a valid identifier.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has_number && has_time) {
|
||||||
|
LOG(ERROR) << "SegmentTemplate: $Number$ and $Time$ should not co-exist.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!has_number && !has_time) {
|
||||||
|
LOG(ERROR) << "SegmentTemplate: $Number$ or $Time$ should exist.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Note: The below check is skipped.
|
||||||
|
// Strings outside identifiers shall only contain characters that are
|
||||||
|
// permitted within URLs according to RFC 1738.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetSegmentName(const std::string& segment_template,
|
||||||
|
uint64 segment_start_time,
|
||||||
|
uint32 segment_index) {
|
||||||
|
DCHECK(ValidateSegmentTemplate(segment_template));
|
||||||
|
|
||||||
|
std::vector<std::string> splits;
|
||||||
|
base::SplitString(segment_template, '$', &splits);
|
||||||
|
// "$" always appears in pairs, so there should be odd number of splits.
|
||||||
|
DCHECK_EQ(1u, splits.size() % 2);
|
||||||
|
|
||||||
|
std::string segment_name;
|
||||||
|
for (size_t i = 0; i < splits.size(); ++i) {
|
||||||
|
// Every second substring in split output should be an identifier.
|
||||||
|
// Simply copy the non-identifier part.
|
||||||
|
if (i % 2 == 0) {
|
||||||
|
segment_name += splits[i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (splits[i].empty()) {
|
||||||
|
// "$$" is an escape sequence, replaced with a single "$".
|
||||||
|
segment_name += "$";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
size_t format_pos = splits[i].find('%');
|
||||||
|
std::string identifier = splits[i].substr(0, format_pos);
|
||||||
|
DCHECK(identifier == "Number" || identifier == "Time");
|
||||||
|
|
||||||
|
std::string format_tag;
|
||||||
|
if (format_pos != std::string::npos) {
|
||||||
|
format_tag = splits[i].substr(format_pos);
|
||||||
|
DCHECK(ValidateFormatTag(format_tag));
|
||||||
|
// Replace %d formatting with %lu formatting to correctly format uint64.
|
||||||
|
format_tag = format_tag.substr(0, format_tag.size() - 1) + "lu";
|
||||||
|
} else {
|
||||||
|
// Default format tag "%01d", modified to format uint64 correctly.
|
||||||
|
format_tag = "%01lu";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (identifier == "Number") {
|
||||||
|
// SegmentNumber starts from 1.
|
||||||
|
segment_name += base::StringPrintf(
|
||||||
|
format_tag.c_str(), static_cast<uint64>(segment_index + 1));
|
||||||
|
} else if (identifier == "Time") {
|
||||||
|
segment_name +=
|
||||||
|
base::StringPrintf(format_tag.c_str(), segment_start_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return segment_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace media
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
//
|
||||||
|
// Muxer utility functions.
|
||||||
|
|
||||||
|
#ifndef MEDIA_BASE_MUXER_UTIL_H_
|
||||||
|
#define MEDIA_BASE_MUXER_UTIL_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "base/basictypes.h"
|
||||||
|
|
||||||
|
namespace media {
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
|
||||||
|
/// Build the segment name from provided input.
|
||||||
|
/// @param segment_template is the segment template pattern, which should
|
||||||
|
/// comply with ISO/IEC 23009-1:2012 5.3.9.4.4.
|
||||||
|
/// @param segment_start_time specifies the segment start time.
|
||||||
|
/// @param segment_index specifies the segment index.
|
||||||
|
/// @return The segment name with identifier substituted.
|
||||||
|
std::string GetSegmentName(const std::string& segment_template,
|
||||||
|
uint64 segment_start_time,
|
||||||
|
uint32 segment_index);
|
||||||
|
|
||||||
|
} // namespace media
|
||||||
|
|
||||||
|
#endif // MEDIA_BASE_MUXER_UTIL_H_
|
|
@ -0,0 +1,104 @@
|
||||||
|
// Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
#include "media/base/muxer_util.h"
|
||||||
|
|
||||||
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace media {
|
||||||
|
|
||||||
|
TEST(MuxerUtilTest, ValidateSegmentTemplate) {
|
||||||
|
EXPECT_FALSE(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$"));
|
||||||
|
|
||||||
|
// Escape sequence "$$".
|
||||||
|
EXPECT_TRUE(ValidateSegmentTemplate("foo$Time$__$$loo"));
|
||||||
|
EXPECT_TRUE(ValidateSegmentTemplate("foo$Time$$$"));
|
||||||
|
EXPECT_TRUE(ValidateSegmentTemplate("$$$Time$$$"));
|
||||||
|
|
||||||
|
// Missing $Number$ / $Time$.
|
||||||
|
EXPECT_FALSE(ValidateSegmentTemplate("$$"));
|
||||||
|
EXPECT_FALSE(ValidateSegmentTemplate("foo$$goo"));
|
||||||
|
|
||||||
|
// $Number$, $Time$ should not co-exist.
|
||||||
|
EXPECT_FALSE(ValidateSegmentTemplate("$Number$$Time$"));
|
||||||
|
EXPECT_FALSE(ValidateSegmentTemplate("foo$Number$_$Time$loo"));
|
||||||
|
|
||||||
|
// $RepresentationID$ and $Bandwidth$ not implemented yet.
|
||||||
|
EXPECT_FALSE(ValidateSegmentTemplate("$RepresentationID$__$Time$"));
|
||||||
|
EXPECT_FALSE(ValidateSegmentTemplate("foo$Bandwidth$$Time$"));
|
||||||
|
|
||||||
|
// Unknown identifier.
|
||||||
|
EXPECT_FALSE(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$"));
|
||||||
|
// $$ should not have any format tag.
|
||||||
|
EXPECT_FALSE(ValidateSegmentTemplate("$%01d$$Time$"));
|
||||||
|
// Format specifier edge cases.
|
||||||
|
EXPECT_TRUE(ValidateSegmentTemplate("$Time%00d$"));
|
||||||
|
EXPECT_TRUE(ValidateSegmentTemplate("$Time%005d$"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MuxerUtilTest, GetSegmentName) {
|
||||||
|
const uint64 kSegmentStartTime = 180180;
|
||||||
|
const uint32 kSegmentIndex = 11;
|
||||||
|
|
||||||
|
EXPECT_EQ("12", GetSegmentName("$Number$", kSegmentStartTime, kSegmentIndex));
|
||||||
|
EXPECT_EQ("012",
|
||||||
|
GetSegmentName("$Number%03d$", kSegmentStartTime, kSegmentIndex));
|
||||||
|
EXPECT_EQ(
|
||||||
|
"12$foo$00012",
|
||||||
|
GetSegmentName(
|
||||||
|
"$Number%01d$$$foo$$$Number%05d$", kSegmentStartTime, kSegmentIndex));
|
||||||
|
|
||||||
|
EXPECT_EQ("180180",
|
||||||
|
GetSegmentName("$Time$", kSegmentStartTime, kSegmentIndex));
|
||||||
|
EXPECT_EQ("foo$_$18018000180180.m4s",
|
||||||
|
GetSegmentName("foo$$_$$$Time%01d$$Time%08d$.m4s",
|
||||||
|
kSegmentStartTime,
|
||||||
|
kSegmentIndex));
|
||||||
|
// Format specifier edge cases.
|
||||||
|
EXPECT_EQ("12",
|
||||||
|
GetSegmentName("$Number%00d$", kSegmentStartTime, kSegmentIndex));
|
||||||
|
EXPECT_EQ("00012",
|
||||||
|
GetSegmentName("$Number%005d$", kSegmentStartTime, kSegmentIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MuxerUtilTest, GetSegmentNameWithIndexZero) {
|
||||||
|
const uint64 kSegmentStartTime = 0;
|
||||||
|
const uint32 kSegmentIndex = 0;
|
||||||
|
|
||||||
|
EXPECT_EQ("1", GetSegmentName("$Number$", kSegmentStartTime, kSegmentIndex));
|
||||||
|
EXPECT_EQ("001",
|
||||||
|
GetSegmentName("$Number%03d$", kSegmentStartTime, kSegmentIndex));
|
||||||
|
|
||||||
|
EXPECT_EQ("0", GetSegmentName("$Time$", kSegmentStartTime, kSegmentIndex));
|
||||||
|
EXPECT_EQ("00000000.m4s",
|
||||||
|
GetSegmentName("$Time%08d$.m4s", kSegmentStartTime, kSegmentIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MuxerUtilTest, GetSegmentNameLargeTime) {
|
||||||
|
const uint64 kSegmentStartTime = 1601599839840;
|
||||||
|
const uint32 kSegmentIndex = 8888888;
|
||||||
|
|
||||||
|
EXPECT_EQ("1601599839840",
|
||||||
|
GetSegmentName("$Time$", kSegmentStartTime, kSegmentIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace media
|
|
@ -11,6 +11,7 @@
|
||||||
#include "media/base/buffer_writer.h"
|
#include "media/base/buffer_writer.h"
|
||||||
#include "media/base/media_stream.h"
|
#include "media/base/media_stream.h"
|
||||||
#include "media/base/muxer_options.h"
|
#include "media/base/muxer_options.h"
|
||||||
|
#include "media/base/muxer_util.h"
|
||||||
#include "media/event/muxer_listener.h"
|
#include "media/event/muxer_listener.h"
|
||||||
#include "media/file/file.h"
|
#include "media/file/file.h"
|
||||||
#include "media/formats/mp4/box_definitions.h"
|
#include "media/formats/mp4/box_definitions.h"
|
||||||
|
@ -145,10 +146,10 @@ Status MultiSegmentSegmenter::WriteSegment() {
|
||||||
"Cannot open file for append " + options().output_file_name);
|
"Cannot open file for append " + options().output_file_name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
file_name = options().segment_template;
|
file = File::Open(GetSegmentName(options().segment_template,
|
||||||
ReplaceSubstringsAfterOffset(
|
sidx()->earliest_presentation_time,
|
||||||
&file_name, 0, "$Number$", base::UintToString(++num_segments_));
|
num_segments_++).c_str(),
|
||||||
file = File::Open(file_name.c_str(), "w");
|
"w");
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
return Status(error::FILE_FAILURE,
|
return Status(error::FILE_FAILURE,
|
||||||
"Cannot open file for write " + file_name);
|
"Cannot open file for write " + file_name);
|
||||||
|
|
Loading…
Reference in New Issue