From 05f56827287b46f495ed74eef6877e28b967c1f0 Mon Sep 17 00:00:00 2001 From: Jacob Trimble Date: Wed, 16 Dec 2015 10:20:13 -0800 Subject: [PATCH] Added stream descriptor for output format. Added two stream descriptors that are both used to tell the output format. If it is not given, the packager will use the output file extension to determine the output format. Change-Id: Ib8d5dcf52956c2d451e77ea6a90d9502d4a77064 --- packager/app/packager_main.cc | 42 ++++++++++--------- packager/app/stream_descriptor.cc | 16 ++++++- packager/app/stream_descriptor.h | 3 ++ packager/media/base/container_names.cc | 32 ++++++++++++++ packager/media/base/container_names.h | 13 +++++- .../media/base/container_names_unittest.cc | 29 +++++++++++++ 6 files changed, 114 insertions(+), 21 deletions(-) diff --git a/packager/app/packager_main.cc b/packager/app/packager_main.cc index 47696ca329..a48b1b5b4c 100644 --- a/packager/app/packager_main.cc +++ b/packager/app/packager_main.cc @@ -19,7 +19,6 @@ #include "packager/base/logging.h" #include "packager/base/stl_util.h" #include "packager/base/strings/string_split.h" -#include "packager/base/strings/string_util.h" #include "packager/base/strings/stringprintf.h" #include "packager/base/threading/simple_thread.h" #include "packager/base/time/clock.h" @@ -67,7 +66,10 @@ const char kUsage[] = "If not specified, its value may be estimated.\n" " - language (lang): Optional value which contains a user-specified " "language tag. If specified, this value overrides any language metadata " - "in the input track.\n"; + "in the input track.\n" + " - output_format (format): Optional value which specifies the format " + "of the output files (MP4 or WebM). If not specified, it will be " + "derived from the file extension of the output file.\n"; const char kMediaInfoSuffix[] = ".media_info"; @@ -183,22 +185,13 @@ bool StreamInfoToTextMediaInfo(const StreamDescriptor& stream_descriptor, return true; } -scoped_ptr CreateOutputMuxer(const MuxerOptions& options) { - // TODO(modmaker): Add a config option for output format - const std::string& file_name = options.output_file_name; - if (base::EndsWith(file_name, ".webm", - base::CompareCase::INSENSITIVE_ASCII)) { +scoped_ptr CreateOutputMuxer(const MuxerOptions& options, + MediaContainerName container) { + if (container == CONTAINER_WEBM) { return scoped_ptr(new webm::WebMMuxer(options)); - } else if (base::EndsWith(file_name, ".mp4", - base::CompareCase::INSENSITIVE_ASCII) || - base::EndsWith(file_name, ".m4a", - base::CompareCase::INSENSITIVE_ASCII) || - base::EndsWith(file_name, ".m4v", - base::CompareCase::INSENSITIVE_ASCII)) { - return scoped_ptr(new mp4::MP4Muxer(options)); } else { - LOG(ERROR) << "Unrecognized output format " << file_name; - return NULL; + DCHECK_EQ(container, CONTAINER_MOV); + return scoped_ptr(new mp4::MP4Muxer(options)); } } @@ -282,9 +275,20 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors, } DCHECK(!remux_jobs->empty()); - scoped_ptr muxer(CreateOutputMuxer(stream_muxer_options)); - if (!muxer) - return false; + MediaContainerName output_format = stream_iter->output_format; + if (output_format == CONTAINER_UNKNOWN) { + output_format = + DetermineContainerFromFileName(stream_muxer_options.output_file_name); + + if (output_format == CONTAINER_UNKNOWN) { + LOG(ERROR) << "Unable to determine output format for file " + << stream_muxer_options.output_file_name; + return false; + } + } + + scoped_ptr muxer( + CreateOutputMuxer(stream_muxer_options, output_format)); if (FLAGS_use_fake_clock_for_muxer) muxer->set_clock(fake_clock); if (key_source) { diff --git a/packager/app/stream_descriptor.cc b/packager/app/stream_descriptor.cc index d447e50d6b..80e19bf629 100644 --- a/packager/app/stream_descriptor.cc +++ b/packager/app/stream_descriptor.cc @@ -25,6 +25,7 @@ enum FieldType { kSegmentTemplateField, kBandwidthField, kLanguageField, + kOutputFormatField, }; struct FieldNameToTypeMapping { @@ -47,6 +48,8 @@ const FieldNameToTypeMapping kFieldNameTypeMappings[] = { { "bitrate", kBandwidthField }, { "language", kLanguageField }, { "lang", kLanguageField }, + { "output_format", kOutputFormatField }, + { "format", kOutputFormatField }, }; FieldType GetFieldType(const std::string& field_name) { @@ -59,7 +62,8 @@ FieldType GetFieldType(const std::string& field_name) { } // anonymous namespace -StreamDescriptor::StreamDescriptor() : bandwidth(0) {} +StreamDescriptor::StreamDescriptor() + : bandwidth(0), output_format(CONTAINER_UNKNOWN) {} StreamDescriptor::~StreamDescriptor() {} @@ -110,6 +114,16 @@ bool InsertStreamDescriptor(const std::string& descriptor_string, descriptor.language = language; break; } + case kOutputFormatField: { + MediaContainerName output_format = + DetermineContainerFromFormatName(iter->second); + if (output_format == CONTAINER_UNKNOWN) { + LOG(ERROR) << "Unrecognized output format " << iter->second; + return false; + } + descriptor.output_format = output_format; + break; + } default: LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first << "\")."; diff --git a/packager/app/stream_descriptor.h b/packager/app/stream_descriptor.h index e124053e67..65b2e9c1e5 100644 --- a/packager/app/stream_descriptor.h +++ b/packager/app/stream_descriptor.h @@ -12,6 +12,8 @@ #include #include +#include "media/base/container_names.h" + namespace edash_packager { namespace media { @@ -27,6 +29,7 @@ struct StreamDescriptor { std::string segment_template; uint32_t bandwidth; std::string language; + MediaContainerName output_format; }; class StreamDescriptorCompareFn { diff --git a/packager/media/base/container_names.cc b/packager/media/base/container_names.cc index ab0fcd7667..2f6577cdbc 100644 --- a/packager/media/base/container_names.cc +++ b/packager/media/base/container_names.cc @@ -12,6 +12,7 @@ #include #include "packager/base/logging.h" +#include "packager/base/strings/string_util.h" #include "packager/media/base/bit_reader.h" #include "packager/mpd/base/xml/scoped_xml_ptr.h" @@ -1714,5 +1715,36 @@ MediaContainerName DetermineContainer(const uint8_t* buffer, int buffer_size) { return CONTAINER_UNKNOWN; } +MediaContainerName DetermineContainerFromFormatName( + const std::string& format_name) { + if (base::EqualsCaseInsensitiveASCII(format_name, "webm")) { + return CONTAINER_WEBM; + } else if (base::EqualsCaseInsensitiveASCII(format_name, "m4a") || + base::EqualsCaseInsensitiveASCII(format_name, "m4v") || + base::EqualsCaseInsensitiveASCII(format_name, "mp4") || + base::EqualsCaseInsensitiveASCII(format_name, "mov")) { + return CONTAINER_MOV; + } else { + return CONTAINER_UNKNOWN; + } +} + +MediaContainerName DetermineContainerFromFileName( + const std::string& file_name) { + if (base::EndsWith(file_name, ".webm", + base::CompareCase::INSENSITIVE_ASCII)) { + return CONTAINER_WEBM; + } else if (base::EndsWith(file_name, ".mp4", + base::CompareCase::INSENSITIVE_ASCII) || + base::EndsWith(file_name, ".m4a", + base::CompareCase::INSENSITIVE_ASCII) || + base::EndsWith(file_name, ".m4v", + base::CompareCase::INSENSITIVE_ASCII)) { + return CONTAINER_MOV; + } else { + return CONTAINER_UNKNOWN; + } +} + } // namespace media } // namespace edash_packager diff --git a/packager/media/base/container_names.h b/packager/media/base/container_names.h index 7239fe1f59..8895e3ab2e 100644 --- a/packager/media/base/container_names.h +++ b/packager/media/base/container_names.h @@ -7,6 +7,8 @@ #include +#include + namespace edash_packager { namespace media { @@ -56,9 +58,18 @@ enum MediaContainerName { CONTAINER_MAX // Must be last }; -/// Determine the container type. +/// Determine the container type from input data. MediaContainerName DetermineContainer(const uint8_t* buffer, int buffer_size); +/// Determine the container type from the format name. +/// @param format_name Specifies the format, e.g. 'webm', 'mov', 'mp4'. +MediaContainerName DetermineContainerFromFormatName( + const std::string& format_name); + +/// Determine the container type from the file extension. +/// @param file_name Specifies the file name, e.g. 'file.webm', 'video.mp4'. +MediaContainerName DetermineContainerFromFileName(const std::string& file_name); + } // namespace media } // namespace edash_packager diff --git a/packager/media/base/container_names_unittest.cc b/packager/media/base/container_names_unittest.cc index 8af3192c79..61c48ec0c9 100644 --- a/packager/media/base/container_names_unittest.cc +++ b/packager/media/base/container_names_unittest.cc @@ -83,6 +83,35 @@ uint8_t kBug263073Buffer[] = { 0x67, 0x64, 0x00, 0x28, 0xac, 0x2c, 0xa4, 0x01, 0xe0, 0x08, 0x9f, 0x97, 0x01, 0x52, 0x02, 0x02, 0x02, 0x80, 0x00, 0x01}; +TEST(ContainerNamesTest, FromFormatName) { + EXPECT_EQ(CONTAINER_WEBM, DetermineContainerFromFormatName("webm")); + EXPECT_EQ(CONTAINER_WEBM, DetermineContainerFromFormatName("WeBm")); + EXPECT_EQ(CONTAINER_MOV, DetermineContainerFromFormatName("m4a")); + EXPECT_EQ(CONTAINER_MOV, DetermineContainerFromFormatName("m4v")); + EXPECT_EQ(CONTAINER_MOV, DetermineContainerFromFormatName("M4v")); + EXPECT_EQ(CONTAINER_MOV, DetermineContainerFromFormatName("mov")); + EXPECT_EQ(CONTAINER_MOV, DetermineContainerFromFormatName("mp4")); + EXPECT_EQ(CONTAINER_MOV, DetermineContainerFromFormatName("Mp4")); + EXPECT_EQ(CONTAINER_UNKNOWN, DetermineContainerFromFormatName("cat")); + EXPECT_EQ(CONTAINER_UNKNOWN, DetermineContainerFromFormatName("amp4")); + EXPECT_EQ(CONTAINER_UNKNOWN, DetermineContainerFromFormatName(" mp4")); + EXPECT_EQ(CONTAINER_UNKNOWN, DetermineContainerFromFormatName("")); +} + +TEST(ContainerNamesTest, FromFileName) { + EXPECT_EQ(CONTAINER_WEBM, DetermineContainerFromFileName("test.webm")); + EXPECT_EQ(CONTAINER_WEBM, DetermineContainerFromFileName("another.wEbM")); + EXPECT_EQ(CONTAINER_MOV, DetermineContainerFromFileName("test.m4a")); + EXPECT_EQ(CONTAINER_MOV, DetermineContainerFromFileName("file.m4v")); + EXPECT_EQ(CONTAINER_MOV, DetermineContainerFromFileName("a file .m4V")); + EXPECT_EQ(CONTAINER_MOV, DetermineContainerFromFileName("2_more-files.mp4")); + EXPECT_EQ(CONTAINER_MOV, DetermineContainerFromFileName("foo.bar.MP4")); + EXPECT_EQ(CONTAINER_UNKNOWN, DetermineContainerFromFileName("a_bad.gif")); + EXPECT_EQ(CONTAINER_UNKNOWN, DetermineContainerFromFileName("a bad.m4v-")); + EXPECT_EQ(CONTAINER_UNKNOWN, DetermineContainerFromFileName("a.m4v.")); + EXPECT_EQ(CONTAINER_UNKNOWN, DetermineContainerFromFileName("")); +} + // Test that containers that start with fixed strings are handled correctly. // This is to verify that the TAG matches the first 4 characters of the string. TEST(ContainerNamesTest, CheckFixedStrings) {