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:
parent
77ec23afe4
commit
3cc86c62b5
|
@ -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());
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
<< "\").";
|
<< "\").";
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue