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
This commit is contained in:
Joey Parrish 2015-02-02 11:27:32 -08:00
parent 77ec23afe4
commit 3cc86c62b5
7 changed files with 41 additions and 7 deletions

View File

@ -48,7 +48,10 @@ const char kUsage[] =
" - bandwidth (bw): Optional value which contains a user-specified " " - bandwidth (bw): Optional value which contains a user-specified "
"content bit rate for the stream, in bits/sec. If specified, this value is " "content bit rate for the stream, in bits/sec. If specified, this value is "
"propagated to the $Bandwidth$ template parameter for segment names. " "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 { enum ExitStatus {
kSuccess = 0, kSuccess = 0,
@ -161,11 +164,11 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors,
scoped_ptr<MuxerListener> muxer_listener; scoped_ptr<MuxerListener> muxer_listener;
DCHECK(!(FLAGS_output_media_info && mpd_notifier)); DCHECK(!(FLAGS_output_media_info && mpd_notifier));
if (FLAGS_output_media_info) { 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"; stream_muxer_options.output_file_name + ".media_info";
scoped_ptr<VodMediaInfoDumpMuxerListener> scoped_ptr<VodMediaInfoDumpMuxerListener>
vod_media_info_dump_muxer_listener( 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( vod_media_info_dump_muxer_listener->SetContentProtectionSchemeIdUri(
FLAGS_scheme_id_uri); FLAGS_scheme_id_uri);
muxer_listener = vod_media_info_dump_muxer_listener.Pass(); 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(), if (!AddStreamToMuxer(remux_jobs->back()->demuxer()->streams(),
stream_iter->stream_selector, stream_iter->stream_selector,
stream_iter->language,
muxer.get())) muxer.get()))
return false; return false;
remux_jobs->back()->AddMuxer(muxer.Pass()); remux_jobs->back()->AddMuxer(muxer.Pass());

View File

@ -179,6 +179,7 @@ MediaStream* FindFirstAudioStream(const std::vector<MediaStream*>& streams) {
bool AddStreamToMuxer(const std::vector<MediaStream*>& streams, bool AddStreamToMuxer(const std::vector<MediaStream*>& streams,
const std::string& stream_selector, const std::string& stream_selector,
const std::string& language_override,
Muxer* muxer) { Muxer* muxer) {
DCHECK(muxer); DCHECK(muxer);
@ -207,6 +208,11 @@ bool AddStreamToMuxer(const std::vector<MediaStream*>& streams,
LOG(ERROR) << "No " << stream_selector << " stream found in the input."; LOG(ERROR) << "No " << stream_selector << " stream found in the input.";
return false; return false;
} }
if (!language_override.empty()) {
stream->info()->set_language(language_override);
}
muxer->AddStream(stream); muxer->AddStream(stream);
return true; return true;
} }

View File

@ -59,9 +59,12 @@ bool GetMpdOptions(edash_packager::MpdOptions* mpd_options);
/// "audio" to select the first audio track, "video" to select the first /// "audio" to select the first audio track, "video" to select the first
/// video track, or a decimal number indicating which track number to /// video track, or a decimal number indicating which track number to
/// select (start at "1"). /// 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. /// @return true if successful, false otherwise.
bool AddStreamToMuxer(const std::vector<MediaStream*>& streams, bool AddStreamToMuxer(const std::vector<MediaStream*>& streams,
const std::string& stream_selector, const std::string& stream_selector,
const std::string& language_override,
Muxer* muxer); Muxer* muxer);
} // namespace media } // namespace media

View File

@ -10,6 +10,7 @@
#include "packager/base/logging.h" #include "packager/base/logging.h"
#include "packager/base/strings/string_number_conversions.h" #include "packager/base/strings/string_number_conversions.h"
#include "packager/base/strings/string_split.h" #include "packager/base/strings/string_split.h"
#include "packager/mpd/base/language_utils.h"
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
@ -23,6 +24,7 @@ enum FieldType {
kOutputField, kOutputField,
kSegmentTemplateField, kSegmentTemplateField,
kBandwidthField, kBandwidthField,
kLanguageField,
}; };
struct FieldNameToTypeMapping { struct FieldNameToTypeMapping {
@ -43,6 +45,8 @@ const FieldNameToTypeMapping kFieldNameTypeMappings[] = {
{ "bandwidth", kBandwidthField }, { "bandwidth", kBandwidthField },
{ "bw", kBandwidthField }, { "bw", kBandwidthField },
{ "bitrate", kBandwidthField }, { "bitrate", kBandwidthField },
{ "language", kLanguageField },
{ "lang", kLanguageField },
}; };
FieldType GetFieldType(const std::string& field_name) { FieldType GetFieldType(const std::string& field_name) {
@ -96,6 +100,16 @@ bool InsertStreamDescriptor(const std::string& descriptor_string,
descriptor.bandwidth = bw; descriptor.bandwidth = bw;
break; 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: default:
LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first
<< "\")."; << "\").";

View File

@ -26,6 +26,7 @@ struct StreamDescriptor {
std::string output; std::string output;
std::string segment_template; std::string segment_template;
uint32_t bandwidth; uint32_t bandwidth;
std::string language;
}; };
class StreamDescriptorCompareFn { class StreamDescriptorCompareFn {

View File

@ -60,6 +60,8 @@ class StreamInfo : public base::RefCountedThreadSafe<StreamInfo> {
codec_string_ = codec_string; codec_string_ = codec_string;
} }
void set_language(const std::string& language) { language_ = language; }
protected: protected:
friend class base::RefCountedThreadSafe<StreamInfo>; friend class base::RefCountedThreadSafe<StreamInfo>;
virtual ~StreamInfo(); virtual ~StreamInfo();

View File

@ -141,10 +141,14 @@ void MP4Muxer::InitializeTrak(const StreamInfo* info, Track* trak) {
trak->media.header.timescale = info->time_scale(); trak->media.header.timescale = info->time_scale();
trak->media.header.duration = 0; trak->media.header.duration = 0;
if (!info->language().empty()) { if (!info->language().empty()) {
DCHECK_EQ(info->language().size(), const size_t language_size = arraysize(trak->media.header.language) - 1;
arraysize(trak->media.header.language) - 1); if (info->language().size() != language_size) {
strcpy(trak->media.header.language, info->language().c_str()); LOG(WARNING) << "'" << info->language() << "' is not a valid ISO-639-2 "
trak->media.header.language[info->language().size()] = '\0'; << "language code, ignoring.";
} else {
memcpy(trak->media.header.language, info->language().c_str(),
language_size + 1);
}
} }
} }