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) {