Implemented name/value pair stream descriptors.
Implemented user-specified bandwidth. Change-Id: If01f26867d4285bcbc899310d3f1b98b928bed89
This commit is contained in:
parent
50aea3e6f6
commit
efe52e62dc
|
@ -11,6 +11,7 @@
|
|||
#include "app/mpd_flags.h"
|
||||
#include "app/muxer_flags.h"
|
||||
#include "app/packager_util.h"
|
||||
#include "app/stream_descriptor.h"
|
||||
#include "app/widevine_encryption_flags.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/stl_util.h"
|
||||
|
@ -31,18 +32,23 @@ namespace {
|
|||
const char kUsage[] =
|
||||
"Packager driver program. Sample Usage:\n"
|
||||
"%s [flags] <stream_descriptor> ...\n"
|
||||
"stream_descriptor may be repeated and consists of a tuplet as follows:\n"
|
||||
"<input_file>#<stream_selector>,<output_file>[,<segment_template>]\n"
|
||||
" - input_file is a file path or network stream URL.\n"
|
||||
" - stream_selector is one of 'audio', 'video', or stream number.\n"
|
||||
" - output_file is the output file (single file) or initialization file"
|
||||
" path (multiple file)."
|
||||
" - segment_template is an optional value which specifies the naming"
|
||||
" pattern for the segment files, and that the stream should be split into"
|
||||
" multiple files. Its presence should be consistent across streams.\n";
|
||||
|
||||
typedef std::vector<std::string> StringVector;
|
||||
|
||||
"stream_descriptor consists of comma separated field_name/value pairs:\n"
|
||||
"field_name=value,[field_name=value,]...\n"
|
||||
"Supported field names are as follows:\n"
|
||||
" - input (in): Required input/source media file path or network stream "
|
||||
"URL.\n"
|
||||
" - stream_selector (stream): Required field with value 'audio', 'video', "
|
||||
"or stream number (zero based).\n"
|
||||
" - output (out): Required output file (single file) or initialization "
|
||||
"file path (multiple file).\n"
|
||||
" - segment_template (segment): Optional value which specifies the "
|
||||
"naming pattern for the segment files, and that the stream should be "
|
||||
"split into multiple files. Its presence should be consistent across "
|
||||
"streams.\n"
|
||||
" - bandwidth (bw): Optional value which contains a user-specified "
|
||||
"content bit rate for the stream, in bits/sec. If specified, this value is "
|
||||
"propagated to the $Bandwidth$ template parameter for segment names. "
|
||||
"If not specified, its value may be estimated.\n";
|
||||
} // namespace
|
||||
|
||||
namespace media {
|
||||
|
@ -88,7 +94,7 @@ class RemuxJob : public base::SimpleThread {
|
|||
DISALLOW_COPY_AND_ASSIGN(RemuxJob);
|
||||
};
|
||||
|
||||
bool CreateRemuxJobs(const StringVector& stream_descriptors,
|
||||
bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors,
|
||||
const MuxerOptions& muxer_options,
|
||||
EncryptionKeySource* key_source,
|
||||
MpdNotifier* mpd_notifier,
|
||||
|
@ -97,55 +103,40 @@ bool CreateRemuxJobs(const StringVector& stream_descriptors,
|
|||
DCHECK(muxer_listeners);
|
||||
DCHECK(remux_jobs);
|
||||
|
||||
// Sort the stream descriptors so that we can group muxers by demux.
|
||||
StringVector sorted_descriptors(stream_descriptors);
|
||||
std::sort(sorted_descriptors.begin(), sorted_descriptors.end());
|
||||
|
||||
std::string previous_file_path;
|
||||
for (StringVector::const_iterator stream_iter = sorted_descriptors.begin();
|
||||
stream_iter != sorted_descriptors.end();
|
||||
std::string previous_input;
|
||||
for (StreamDescriptorList::const_iterator stream_iter =
|
||||
stream_descriptors.begin();
|
||||
stream_iter != stream_descriptors.end();
|
||||
++stream_iter) {
|
||||
// Process stream descriptor.
|
||||
StringVector descriptor;
|
||||
base::SplitString(*stream_iter, ',', &descriptor);
|
||||
if ((descriptor.size() < 2) || (descriptor.size() > 3)) {
|
||||
LOG(ERROR)
|
||||
<< "Malformed stream descriptor (invalid number of components).";
|
||||
return false;
|
||||
}
|
||||
size_t hash_pos = descriptor[0].find('#');
|
||||
if (hash_pos == std::string::npos) {
|
||||
LOG(ERROR)
|
||||
<< "Malformed stream descriptor (stream selector unspecified).";
|
||||
return false;
|
||||
}
|
||||
MuxerOptions stream_muxer_options(muxer_options);
|
||||
std::string file_path(descriptor[0].substr(0, hash_pos));
|
||||
std::string stream_selector(descriptor[0].substr(hash_pos + 1));
|
||||
stream_muxer_options.output_file_name = descriptor[1];
|
||||
if (descriptor.size() == 3) {
|
||||
stream_muxer_options.segment_template = descriptor[2];
|
||||
if (!ValidateSegmentTemplate(stream_muxer_options.segment_template)) {
|
||||
stream_muxer_options.output_file_name = stream_iter->output;
|
||||
if (!stream_iter->segment_template.empty()) {
|
||||
if (!ValidateSegmentTemplate(stream_iter->segment_template)) {
|
||||
LOG(ERROR) << "ERROR: segment template with '"
|
||||
<< stream_muxer_options.segment_template << "' is invalid.";
|
||||
<< stream_iter->segment_template << "' is invalid.";
|
||||
return false;
|
||||
}
|
||||
stream_muxer_options.segment_template = stream_iter->segment_template;
|
||||
}
|
||||
stream_muxer_options.bandwidth = stream_iter->bandwidth;
|
||||
|
||||
if (file_path != previous_file_path) {
|
||||
if (stream_iter->input != previous_input) {
|
||||
// New remux job needed. Create demux and job thread.
|
||||
scoped_ptr<Demuxer> demux(new Demuxer(file_path, NULL));
|
||||
scoped_ptr<Demuxer> demux(new Demuxer(stream_iter->input, NULL));
|
||||
Status status = demux->Initialize();
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "Demuxer failed to initialize: " << status.ToString();
|
||||
return false;
|
||||
}
|
||||
if (FLAGS_dump_stream_info) {
|
||||
printf("\nFile \"%s\":\n", file_path.c_str());
|
||||
printf("\nFile \"%s\":\n", stream_iter->input.c_str());
|
||||
DumpStreamInfo(demux->streams());
|
||||
if (stream_iter->output.empty())
|
||||
continue; // just need stream info.
|
||||
}
|
||||
remux_jobs->push_back(new RemuxJob(demux.Pass()));
|
||||
previous_file_path = file_path;
|
||||
previous_input = stream_iter->input;
|
||||
}
|
||||
DCHECK(!remux_jobs->empty());
|
||||
|
||||
|
@ -183,7 +174,7 @@ bool CreateRemuxJobs(const StringVector& stream_descriptors,
|
|||
}
|
||||
|
||||
if (!AddStreamToMuxer(remux_jobs->back()->demuxer()->streams(),
|
||||
stream_selector,
|
||||
stream_iter->stream_selector,
|
||||
muxer.get()))
|
||||
return false;
|
||||
remux_jobs->back()->AddMuxer(muxer.Pass());
|
||||
|
@ -222,7 +213,7 @@ Status RunRemuxJobs(const std::vector<RemuxJob*>& remux_jobs) {
|
|||
return status;
|
||||
}
|
||||
|
||||
bool RunPackager(const StringVector& stream_descriptors) {
|
||||
bool RunPackager(const StreamDescriptorList& stream_descriptors) {
|
||||
if (!AssignFlagsFromProfile())
|
||||
return false;
|
||||
|
||||
|
@ -305,8 +296,12 @@ int main(int argc, char** argv) {
|
|||
LOG(ERROR) << "Could not initialize libcrypto threading.";
|
||||
return 1;
|
||||
}
|
||||
StringVector stream_descriptors;
|
||||
for (int i = 1; i < argc; ++i)
|
||||
stream_descriptors.push_back(argv[i]);
|
||||
// TODO(tinskip): Make InsertStreamDescriptor a member of
|
||||
// StreamDescriptorList.
|
||||
media::StreamDescriptorList stream_descriptors;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (!InsertStreamDescriptor(argv[i], &stream_descriptors))
|
||||
return 1;
|
||||
}
|
||||
return media::RunPackager(stream_descriptors) ? 0 : 1;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
// 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 "app/stream_descriptor.h"
|
||||
|
||||
#include "app/packager_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/string_split.h"
|
||||
|
||||
namespace media {
|
||||
|
||||
namespace {
|
||||
|
||||
enum FieldType {
|
||||
kUnknownField = 0,
|
||||
kStreamSelectorField,
|
||||
kInputField,
|
||||
kOutputField,
|
||||
kSegmentTemplateField,
|
||||
kBandwidthField,
|
||||
};
|
||||
|
||||
struct FieldNameToTypeMapping {
|
||||
const char* field_name;
|
||||
FieldType field_type;
|
||||
};
|
||||
|
||||
const FieldNameToTypeMapping kFieldNameTypeMappings[] = {
|
||||
{ "stream_selector", kStreamSelectorField },
|
||||
{ "stream", kStreamSelectorField },
|
||||
{ "input", kInputField },
|
||||
{ "in", kInputField },
|
||||
{ "output", kOutputField },
|
||||
{ "out", kOutputField },
|
||||
{ "init_segment", kOutputField },
|
||||
{ "segment_template", kSegmentTemplateField },
|
||||
{ "template", kSegmentTemplateField },
|
||||
{ "bandwidth", kBandwidthField },
|
||||
{ "bw", kBandwidthField },
|
||||
{ "bitrate", kBandwidthField },
|
||||
};
|
||||
|
||||
FieldType GetFieldType(const std::string& field_name) {
|
||||
for (size_t idx = 0; idx < arraysize(kFieldNameTypeMappings); ++idx) {
|
||||
if (field_name == kFieldNameTypeMappings[idx].field_name)
|
||||
return kFieldNameTypeMappings[idx].field_type;
|
||||
}
|
||||
return kUnknownField;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
StreamDescriptor::StreamDescriptor() : bandwidth(0) {}
|
||||
|
||||
StreamDescriptor::~StreamDescriptor() {}
|
||||
|
||||
bool InsertStreamDescriptor(const std::string& descriptor_string,
|
||||
StreamDescriptorList* descriptor_list) {
|
||||
StreamDescriptor descriptor;
|
||||
|
||||
// Split descriptor string into name/value pairs.
|
||||
base::StringPairs pairs;
|
||||
if (!base::SplitStringIntoKeyValuePairs(descriptor_string,
|
||||
'=',
|
||||
',',
|
||||
&pairs)) {
|
||||
LOG(ERROR) << "Invalid stream descriptors name/value pairs.";
|
||||
return false;
|
||||
}
|
||||
for (base::StringPairs::const_iterator iter = pairs.begin();
|
||||
iter != pairs.end(); ++iter) {
|
||||
switch (GetFieldType(iter->first)) {
|
||||
case kStreamSelectorField:
|
||||
descriptor.stream_selector = iter->second;
|
||||
break;
|
||||
case kInputField:
|
||||
descriptor.input = iter->second;
|
||||
break;
|
||||
case kOutputField:
|
||||
descriptor.output = iter->second;
|
||||
break;
|
||||
case kSegmentTemplateField:
|
||||
descriptor.segment_template = iter->second;
|
||||
break;
|
||||
case kBandwidthField: {
|
||||
unsigned bw;
|
||||
if (!base::StringToUint(iter->second, &bw)) {
|
||||
LOG(ERROR) << "Non-numeric bandwidth specified.";
|
||||
return false;
|
||||
}
|
||||
descriptor.bandwidth = bw;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first
|
||||
<< "\").";
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Validate and insert the descriptor
|
||||
if (descriptor.input.empty()) {
|
||||
LOG(ERROR) << "Stream input not specified.";
|
||||
return false;
|
||||
}
|
||||
if (!FLAGS_dump_stream_info && descriptor.stream_selector.empty()) {
|
||||
LOG(ERROR) << "Stream stream_selector not specified.";
|
||||
return false;
|
||||
}
|
||||
if (!FLAGS_dump_stream_info && descriptor.output.empty()) {
|
||||
LOG(ERROR) << "Stream output not specified.";
|
||||
return false;
|
||||
}
|
||||
descriptor_list->insert(descriptor);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace media
|
|
@ -0,0 +1,53 @@
|
|||
// 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
|
||||
|
||||
#ifndef APP_STREAM_DESCRIPTOR_H_
|
||||
#define APP_STREAM_DESCRIPTOR_H_
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
namespace media {
|
||||
|
||||
/// Defines a single input/output stream, it's input source, output destination,
|
||||
/// stream selector, and optional segment template and user-specified bandwidth.
|
||||
struct StreamDescriptor {
|
||||
StreamDescriptor();
|
||||
~StreamDescriptor();
|
||||
|
||||
std::string stream_selector;
|
||||
std::string input;
|
||||
std::string output;
|
||||
std::string segment_template;
|
||||
uint32 bandwidth;
|
||||
};
|
||||
|
||||
class StreamDescriptorCompareFn {
|
||||
public:
|
||||
bool operator()(const StreamDescriptor& a, const StreamDescriptor& b) {
|
||||
return a.input < b.input;
|
||||
}
|
||||
};
|
||||
|
||||
/// Sorted list of StreamDescriptor.
|
||||
typedef std::multiset<StreamDescriptor, StreamDescriptorCompareFn>
|
||||
StreamDescriptorList;
|
||||
|
||||
/// Parses a descriptor string, and inserts into sorted list of stream
|
||||
/// descriptors.
|
||||
/// @param descriptor_string contains comma separate name-value pairs describing
|
||||
/// the stream.
|
||||
/// @param descriptor_list is a pointer to the sorted descriptor list into
|
||||
/// which the new descriptor should be inserted.
|
||||
/// @return true if successful, false otherwise. May print error messages.
|
||||
bool InsertStreamDescriptor(const std::string& descriptor_string,
|
||||
StreamDescriptorList* descriptor_list);
|
||||
|
||||
} // namespace media
|
||||
|
||||
#endif // APP_STREAM_DESCRIPTOR_H_
|
|
@ -15,7 +15,8 @@ MuxerOptions::MuxerOptions()
|
|||
segment_sap_aligned(false),
|
||||
fragment_sap_aligned(false),
|
||||
normalize_presentation_timestamp(false),
|
||||
num_subsegments_per_sidx(0) {}
|
||||
num_subsegments_per_sidx(0),
|
||||
bandwidth(0) {}
|
||||
MuxerOptions::~MuxerOptions() {}
|
||||
|
||||
} // namespace media
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
namespace media {
|
||||
|
||||
/// This structure contains the list of configuration options for Muxer.
|
||||
|
@ -62,6 +64,9 @@ struct MuxerOptions {
|
|||
|
||||
/// Specify temporary directory for intermediate files.
|
||||
std::string temp_dir;
|
||||
|
||||
/// User-specified bandwidth for the stream. zero means "unspecified".
|
||||
uint32 bandwidth;
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
|
|
|
@ -63,10 +63,10 @@ bool ValidateSegmentTemplate(const std::string& segment_template) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// TODO(kqyang): Support "RepresentationID" and "Bandwidth".
|
||||
if (identifier == "RepresentationID" || identifier == "Bandwidth") {
|
||||
NOTIMPLEMENTED() << "SegmentTemplate: $RepresentationID$ and $Bandwidth$ "
|
||||
"are not supported yet.";
|
||||
// TODO(kqyang): Support "RepresentationID".
|
||||
if (identifier == "RepresentationID") {
|
||||
NOTIMPLEMENTED() << "SegmentTemplate: $RepresentationID$ is not supported "
|
||||
"yet.";
|
||||
return false;
|
||||
} else if (identifier == "Number") {
|
||||
has_number = true;
|
||||
|
@ -77,7 +77,7 @@ bool ValidateSegmentTemplate(const std::string& segment_template) {
|
|||
LOG(ERROR) << "SegmentTemplate: $$ should not have any format tags.";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
} else if (identifier != "Bandwidth") {
|
||||
LOG(ERROR) << "SegmentTemplate: '$" << splits[i]
|
||||
<< "$' is not a valid identifier.";
|
||||
return false;
|
||||
|
@ -99,7 +99,8 @@ bool ValidateSegmentTemplate(const std::string& segment_template) {
|
|||
|
||||
std::string GetSegmentName(const std::string& segment_template,
|
||||
uint64 segment_start_time,
|
||||
uint32 segment_index) {
|
||||
uint32 segment_index,
|
||||
uint32 bandwidth) {
|
||||
DCHECK(ValidateSegmentTemplate(segment_template));
|
||||
|
||||
std::vector<std::string> splits;
|
||||
|
@ -122,7 +123,8 @@ 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");
|
||||
DCHECK(identifier == "Number" || identifier == "Time" ||
|
||||
identifier == "Bandwidth");
|
||||
|
||||
std::string format_tag;
|
||||
if (format_pos != std::string::npos) {
|
||||
|
@ -142,6 +144,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 == "Bandwidth") {
|
||||
segment_name +=
|
||||
base::StringPrintf(format_tag.c_str(), bandwidth);
|
||||
}
|
||||
}
|
||||
return segment_name;
|
||||
|
|
|
@ -27,10 +27,12 @@ bool ValidateSegmentTemplate(const std::string& segment_template);
|
|||
/// 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.
|
||||
/// @param bandwidth represents the bit rate, in bits/sec, of the stream.
|
||||
/// @return The segment name with identifier substituted.
|
||||
std::string GetSegmentName(const std::string& segment_template,
|
||||
uint64 segment_start_time,
|
||||
uint32 segment_index);
|
||||
uint32 segment_index,
|
||||
uint32 bandwidth);
|
||||
|
||||
} // namespace media
|
||||
|
||||
|
|
|
@ -18,6 +18,11 @@ TEST(MuxerUtilTest, ValidateSegmentTemplate) {
|
|||
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$"));
|
||||
|
||||
// Bandwidth without Number or Time should not be valid.
|
||||
EXPECT_FALSE(ValidateSegmentTemplate("$Bandwidth$"));
|
||||
|
||||
// Escape sequence "$$".
|
||||
EXPECT_TRUE(ValidateSegmentTemplate("foo$Time$__$$loo"));
|
||||
|
@ -32,9 +37,8 @@ TEST(MuxerUtilTest, ValidateSegmentTemplate) {
|
|||
EXPECT_FALSE(ValidateSegmentTemplate("$Number$$Time$"));
|
||||
EXPECT_FALSE(ValidateSegmentTemplate("foo$Number$_$Time$loo"));
|
||||
|
||||
// $RepresentationID$ and $Bandwidth$ not implemented yet.
|
||||
// $RepresentationID$ not implemented yet.
|
||||
EXPECT_FALSE(ValidateSegmentTemplate("$RepresentationID$__$Time$"));
|
||||
EXPECT_FALSE(ValidateSegmentTemplate("foo$Bandwidth$$Time$"));
|
||||
|
||||
// Unknown identifier.
|
||||
EXPECT_FALSE(ValidateSegmentTemplate("$foo$$Time$"));
|
||||
|
@ -58,47 +62,94 @@ TEST(MuxerUtilTest, ValidateSegmentTemplateWithFormatTag) {
|
|||
TEST(MuxerUtilTest, GetSegmentName) {
|
||||
const uint64 kSegmentStartTime = 180180;
|
||||
const uint32 kSegmentIndex = 11;
|
||||
|
||||
EXPECT_EQ("12", GetSegmentName("$Number$", kSegmentStartTime, kSegmentIndex));
|
||||
const uint32 kBandwidth = 1234;
|
||||
EXPECT_EQ("12", GetSegmentName("$Number$",
|
||||
kSegmentStartTime,
|
||||
kSegmentIndex,
|
||||
kBandwidth));
|
||||
EXPECT_EQ("012",
|
||||
GetSegmentName("$Number%03d$", kSegmentStartTime, kSegmentIndex));
|
||||
GetSegmentName("$Number%03d$",
|
||||
kSegmentStartTime,
|
||||
kSegmentIndex,
|
||||
kBandwidth));
|
||||
EXPECT_EQ(
|
||||
"12$foo$00012",
|
||||
GetSegmentName(
|
||||
"$Number%01d$$$foo$$$Number%05d$", kSegmentStartTime, kSegmentIndex));
|
||||
"$Number%01d$$$foo$$$Number%05d$",
|
||||
kSegmentStartTime,
|
||||
kSegmentIndex,
|
||||
kBandwidth));
|
||||
|
||||
EXPECT_EQ("180180",
|
||||
GetSegmentName("$Time$", kSegmentStartTime, kSegmentIndex));
|
||||
GetSegmentName("$Time$",
|
||||
kSegmentStartTime,
|
||||
kSegmentIndex,
|
||||
kBandwidth));
|
||||
EXPECT_EQ("foo$_$18018000180180.m4s",
|
||||
GetSegmentName("foo$$_$$$Time%01d$$Time%08d$.m4s",
|
||||
kSegmentStartTime,
|
||||
kSegmentIndex));
|
||||
kSegmentIndex,
|
||||
kBandwidth));
|
||||
|
||||
// Combo values.
|
||||
EXPECT_EQ("12-1234",
|
||||
GetSegmentName("$Number$-$Bandwidth$",
|
||||
kSegmentStartTime,
|
||||
kSegmentIndex,
|
||||
kBandwidth));
|
||||
EXPECT_EQ("012-001234",
|
||||
GetSegmentName("$Number%03d$-$Bandwidth%06d$",
|
||||
kSegmentStartTime,
|
||||
kSegmentIndex,
|
||||
kBandwidth));
|
||||
|
||||
// Format specifier edge cases.
|
||||
EXPECT_EQ("12",
|
||||
GetSegmentName("$Number%00d$", kSegmentStartTime, kSegmentIndex));
|
||||
GetSegmentName("$Number%00d$",
|
||||
kSegmentStartTime,
|
||||
kSegmentIndex,
|
||||
kBandwidth));
|
||||
EXPECT_EQ("00012",
|
||||
GetSegmentName("$Number%005d$", kSegmentStartTime, kSegmentIndex));
|
||||
GetSegmentName("$Number%005d$",
|
||||
kSegmentStartTime,
|
||||
kSegmentIndex,
|
||||
kBandwidth));
|
||||
}
|
||||
|
||||
TEST(MuxerUtilTest, GetSegmentNameWithIndexZero) {
|
||||
const uint64 kSegmentStartTime = 0;
|
||||
const uint32 kSegmentIndex = 0;
|
||||
|
||||
EXPECT_EQ("1", GetSegmentName("$Number$", kSegmentStartTime, kSegmentIndex));
|
||||
const uint32 kBandwidth = 0;
|
||||
EXPECT_EQ("1", GetSegmentName("$Number$",
|
||||
kSegmentStartTime,
|
||||
kSegmentIndex,
|
||||
kBandwidth));
|
||||
EXPECT_EQ("001",
|
||||
GetSegmentName("$Number%03d$", kSegmentStartTime, kSegmentIndex));
|
||||
GetSegmentName("$Number%03d$",
|
||||
kSegmentStartTime,
|
||||
kSegmentIndex,
|
||||
kBandwidth));
|
||||
|
||||
EXPECT_EQ("0", GetSegmentName("$Time$", kSegmentStartTime, kSegmentIndex));
|
||||
EXPECT_EQ("0", GetSegmentName("$Time$",
|
||||
kSegmentStartTime,
|
||||
kSegmentIndex,
|
||||
kBandwidth));
|
||||
EXPECT_EQ("00000000.m4s",
|
||||
GetSegmentName("$Time%08d$.m4s", kSegmentStartTime, kSegmentIndex));
|
||||
GetSegmentName("$Time%08d$.m4s",
|
||||
kSegmentStartTime,
|
||||
kSegmentIndex,
|
||||
kBandwidth));
|
||||
}
|
||||
|
||||
TEST(MuxerUtilTest, GetSegmentNameLargeTime) {
|
||||
const uint64 kSegmentStartTime = 1601599839840ULL;
|
||||
const uint32 kSegmentIndex = 8888888;
|
||||
|
||||
const uint32 kBandwidth = 444444;
|
||||
EXPECT_EQ("1601599839840",
|
||||
GetSegmentName("$Time$", kSegmentStartTime, kSegmentIndex));
|
||||
GetSegmentName("$Time$",
|
||||
kSegmentStartTime,
|
||||
kSegmentIndex,
|
||||
kBandwidth));
|
||||
}
|
||||
|
||||
} // namespace media
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
namespace media {
|
||||
|
||||
enum StreamType {
|
||||
kStreamUnknown = 0,
|
||||
kStreamAudio,
|
||||
kStreamVideo,
|
||||
};
|
||||
|
|
|
@ -148,7 +148,8 @@ Status MultiSegmentSegmenter::WriteSegment() {
|
|||
} else {
|
||||
file = File::Open(GetSegmentName(options().segment_template,
|
||||
sidx()->earliest_presentation_time,
|
||||
num_segments_++).c_str(),
|
||||
num_segments_++,
|
||||
options().bandwidth).c_str(),
|
||||
"w");
|
||||
if (file == NULL) {
|
||||
return Status(error::FILE_FAILURE,
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
'app/packager_main.cc',
|
||||
'app/packager_util.cc',
|
||||
'app/packager_util.h',
|
||||
'app/stream_descriptor.cc',
|
||||
'app/stream_descriptor.h',
|
||||
'app/widevine_encryption_flags.cc',
|
||||
'app/widevine_encryption_flags.h',
|
||||
],
|
||||
|
|
Loading…
Reference in New Issue