From 3cc86c62b58d19a8078699c19ba1946645a1bc6a Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Mon, 2 Feb 2015 11:27:32 -0800 Subject: [PATCH] Add a stream descriptor field for language. If a stream has no language metadata, or has the wrong metadata, this field allows the user to override this on the command-line. This also maps all specified languages on the command-line to ISO-639-2 tags as required by the MP4 muxer, so that ISO-639-1 tags specified on the command-line do not cause a DCHECK to fail. b/18613148 Change-Id: I473baeecbb3d388db5e06d080179ec6a332b4794 --- packager/app/packager_main.cc | 10 +++++++--- packager/app/packager_util.cc | 6 ++++++ packager/app/packager_util.h | 3 +++ packager/app/stream_descriptor.cc | 14 ++++++++++++++ packager/app/stream_descriptor.h | 1 + packager/media/base/stream_info.h | 2 ++ packager/media/formats/mp4/mp4_muxer.cc | 12 ++++++++---- 7 files changed, 41 insertions(+), 7 deletions(-) diff --git a/packager/app/packager_main.cc b/packager/app/packager_main.cc index 62e1b55699..c91805fdfe 100644 --- a/packager/app/packager_main.cc +++ b/packager/app/packager_main.cc @@ -48,7 +48,10 @@ const char kUsage[] = " - 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"; + "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"; enum ExitStatus { kSuccess = 0, @@ -161,11 +164,11 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors, scoped_ptr muxer_listener; DCHECK(!(FLAGS_output_media_info && mpd_notifier)); if (FLAGS_output_media_info) { - const std::string output_mpd_file_name = + const std::string output_media_info_file_name = stream_muxer_options.output_file_name + ".media_info"; scoped_ptr vod_media_info_dump_muxer_listener( - new VodMediaInfoDumpMuxerListener(output_mpd_file_name)); + new VodMediaInfoDumpMuxerListener(output_media_info_file_name)); vod_media_info_dump_muxer_listener->SetContentProtectionSchemeIdUri( FLAGS_scheme_id_uri); muxer_listener = vod_media_info_dump_muxer_listener.Pass(); @@ -185,6 +188,7 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors, if (!AddStreamToMuxer(remux_jobs->back()->demuxer()->streams(), stream_iter->stream_selector, + stream_iter->language, muxer.get())) return false; remux_jobs->back()->AddMuxer(muxer.Pass()); diff --git a/packager/app/packager_util.cc b/packager/app/packager_util.cc index 1314d484d5..05a8ba65d0 100644 --- a/packager/app/packager_util.cc +++ b/packager/app/packager_util.cc @@ -179,6 +179,7 @@ MediaStream* FindFirstAudioStream(const std::vector& streams) { bool AddStreamToMuxer(const std::vector& streams, const std::string& stream_selector, + const std::string& language_override, Muxer* muxer) { DCHECK(muxer); @@ -207,6 +208,11 @@ bool AddStreamToMuxer(const std::vector& streams, LOG(ERROR) << "No " << stream_selector << " stream found in the input."; return false; } + + if (!language_override.empty()) { + stream->info()->set_language(language_override); + } + muxer->AddStream(stream); return true; } diff --git a/packager/app/packager_util.h b/packager/app/packager_util.h index e495e7d02d..fa7ff0ecf6 100644 --- a/packager/app/packager_util.h +++ b/packager/app/packager_util.h @@ -59,9 +59,12 @@ bool GetMpdOptions(edash_packager::MpdOptions* mpd_options); /// "audio" to select the first audio track, "video" to select the first /// video track, or a decimal number indicating which track number to /// select (start at "1"). +/// @param language_override is a string which, if non-empty, overrides the +/// stream's language metadata. /// @return true if successful, false otherwise. bool AddStreamToMuxer(const std::vector& streams, const std::string& stream_selector, + const std::string& language_override, Muxer* muxer); } // namespace media diff --git a/packager/app/stream_descriptor.cc b/packager/app/stream_descriptor.cc index 0178bdc030..ef7bc0cfaf 100644 --- a/packager/app/stream_descriptor.cc +++ b/packager/app/stream_descriptor.cc @@ -10,6 +10,7 @@ #include "packager/base/logging.h" #include "packager/base/strings/string_number_conversions.h" #include "packager/base/strings/string_split.h" +#include "packager/mpd/base/language_utils.h" namespace edash_packager { namespace media { @@ -23,6 +24,7 @@ enum FieldType { kOutputField, kSegmentTemplateField, kBandwidthField, + kLanguageField, }; struct FieldNameToTypeMapping { @@ -43,6 +45,8 @@ const FieldNameToTypeMapping kFieldNameTypeMappings[] = { { "bandwidth", kBandwidthField }, { "bw", kBandwidthField }, { "bitrate", kBandwidthField }, + { "language", kLanguageField }, + { "lang", kLanguageField }, }; FieldType GetFieldType(const std::string& field_name) { @@ -96,6 +100,16 @@ bool InsertStreamDescriptor(const std::string& descriptor_string, descriptor.bandwidth = bw; break; } + case kLanguageField: { + std::string language = LanguageToISO_639_2(iter->second); + if (language == "und") { + LOG(ERROR) << "Unknown/invalid language specified: " << iter->second; + return false; + } + DCHECK_EQ(3, language.size()); + descriptor.language = language; + 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 1bd3de5cd0..e124053e67 100644 --- a/packager/app/stream_descriptor.h +++ b/packager/app/stream_descriptor.h @@ -26,6 +26,7 @@ struct StreamDescriptor { std::string output; std::string segment_template; uint32_t bandwidth; + std::string language; }; class StreamDescriptorCompareFn { diff --git a/packager/media/base/stream_info.h b/packager/media/base/stream_info.h index 2f539ea9a1..628e80b53b 100644 --- a/packager/media/base/stream_info.h +++ b/packager/media/base/stream_info.h @@ -60,6 +60,8 @@ class StreamInfo : public base::RefCountedThreadSafe { codec_string_ = codec_string; } + void set_language(const std::string& language) { language_ = language; } + protected: friend class base::RefCountedThreadSafe; virtual ~StreamInfo(); diff --git a/packager/media/formats/mp4/mp4_muxer.cc b/packager/media/formats/mp4/mp4_muxer.cc index c711738449..ba6721c4c8 100644 --- a/packager/media/formats/mp4/mp4_muxer.cc +++ b/packager/media/formats/mp4/mp4_muxer.cc @@ -141,10 +141,14 @@ void MP4Muxer::InitializeTrak(const StreamInfo* info, Track* trak) { trak->media.header.timescale = info->time_scale(); trak->media.header.duration = 0; if (!info->language().empty()) { - DCHECK_EQ(info->language().size(), - arraysize(trak->media.header.language) - 1); - strcpy(trak->media.header.language, info->language().c_str()); - trak->media.header.language[info->language().size()] = '\0'; + const size_t language_size = arraysize(trak->media.header.language) - 1; + if (info->language().size() != language_size) { + LOG(WARNING) << "'" << info->language() << "' is not a valid ISO-639-2 " + << "language code, ignoring."; + } else { + memcpy(trak->media.header.language, info->language().c_str(), + language_size + 1); + } } }