From 77ec23afe4103d0d7f1029a3682033e9115ea741 Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Mon, 2 Feb 2015 09:26:09 -0800 Subject: [PATCH] Split up AdaptationSets by language. Additionally, for XML schema correctness, convert ISO-639-2 language tags (three-letter codes) to ISO-639-1 tags (two-letter codes) when possible. This follows BCP-47, which says to always use the shortest language tag when there are multiple possibilities. b/18613148 Change-Id: I120fb7b42ac4da5feb4ca046bba93d9e58acd7a9 --- CONTRIBUTORS | 1 + packager/mpd/base/language_utils.cc | 114 ++++++++++++++++++ packager/mpd/base/language_utils.h | 28 +++++ packager/mpd/base/mpd_builder.cc | 11 +- packager/mpd/base/mpd_builder.h | 6 +- packager/mpd/base/mpd_builder_unittest.cc | 12 +- packager/mpd/base/simple_mpd_notifier.cc | 8 +- packager/mpd/base/simple_mpd_notifier.h | 4 +- packager/mpd/mpd.gyp | 2 + .../test/data/language_audio_media_info1.txt | 20 +++ .../test/data/language_audio_media_info2.txt | 20 +++ .../test/data/language_audio_media_info3.txt | 20 +++ ...guage_audio_media_info_expected_output.txt | 38 ++++++ .../test/data/language_video_media_info1.txt | 19 +++ packager/mpd/test/mpd_builder_test_helper.h | 11 ++ packager/mpd/util/mpd_writer.cc | 30 +++-- packager/mpd/util/mpd_writer_unittest.cc | 40 ++++-- 17 files changed, 352 insertions(+), 32 deletions(-) create mode 100644 packager/mpd/base/language_utils.cc create mode 100644 packager/mpd/base/language_utils.h create mode 100644 packager/mpd/test/data/language_audio_media_info1.txt create mode 100644 packager/mpd/test/data/language_audio_media_info2.txt create mode 100644 packager/mpd/test/data/language_audio_media_info3.txt create mode 100644 packager/mpd/test/data/language_audio_media_info_expected_output.txt create mode 100644 packager/mpd/test/data/language_video_media_info1.txt diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 765f8e58e1..c9ff421a48 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -22,6 +22,7 @@ # # Please keep the list sorted. +Joey Parrish Kongqun Yang Rintaro Kuroiwa Thomas Inskip diff --git a/packager/mpd/base/language_utils.cc b/packager/mpd/base/language_utils.cc new file mode 100644 index 0000000000..eeec25cd75 --- /dev/null +++ b/packager/mpd/base/language_utils.cc @@ -0,0 +1,114 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include "packager/mpd/base/language_utils.h" + +#include "packager/base/logging.h" + +namespace { + +// A map from 3-letter language codes (ISO 639-2) to 2-letter language codes +// (ISO 639-1) for all languages which have both in the registry. +typedef struct { + const char iso_639_2[4]; // 3 letters + nul + const char iso_639_1[3]; // 2 letters + nul +} LanguageMapPairType; +const LanguageMapPairType kLanguageMap[] = { + { "aar", "aa" }, { "abk", "ab" }, { "afr", "af" }, { "aka", "ak" }, + { "alb", "sq" }, { "amh", "am" }, { "ara", "ar" }, { "arg", "an" }, + { "arm", "hy" }, { "asm", "as" }, { "ava", "av" }, { "ave", "ae" }, + { "aym", "ay" }, { "aze", "az" }, { "bak", "ba" }, { "bam", "bm" }, + { "baq", "eu" }, { "bel", "be" }, { "ben", "bn" }, { "bih", "bh" }, + { "bis", "bi" }, { "bod", "bo" }, { "bos", "bs" }, { "bre", "br" }, + { "bul", "bg" }, { "bur", "my" }, { "cat", "ca" }, { "ces", "cs" }, + { "cha", "ch" }, { "che", "ce" }, { "chi", "zh" }, { "chu", "cu" }, + { "chv", "cv" }, { "cor", "kw" }, { "cos", "co" }, { "cre", "cr" }, + { "cym", "cy" }, { "cze", "cs" }, { "dan", "da" }, { "deu", "de" }, + { "div", "dv" }, { "dut", "nl" }, { "dzo", "dz" }, { "ell", "el" }, + { "eng", "en" }, { "epo", "eo" }, { "est", "et" }, { "eus", "eu" }, + { "ewe", "ee" }, { "fao", "fo" }, { "fas", "fa" }, { "fij", "fj" }, + { "fin", "fi" }, { "fra", "fr" }, { "fre", "fr" }, { "fry", "fy" }, + { "ful", "ff" }, { "geo", "ka" }, { "ger", "de" }, { "gla", "gd" }, + { "gle", "ga" }, { "glg", "gl" }, { "glv", "gv" }, { "gre", "el" }, + { "grn", "gn" }, { "guj", "gu" }, { "hat", "ht" }, { "hau", "ha" }, + { "heb", "he" }, { "her", "hz" }, { "hin", "hi" }, { "hmo", "ho" }, + { "hrv", "hr" }, { "hun", "hu" }, { "hye", "hy" }, { "ibo", "ig" }, + { "ice", "is" }, { "ido", "io" }, { "iii", "ii" }, { "iku", "iu" }, + { "ile", "ie" }, { "ina", "ia" }, { "ind", "id" }, { "ipk", "ik" }, + { "isl", "is" }, { "ita", "it" }, { "jav", "jv" }, { "jpn", "ja" }, + { "kal", "kl" }, { "kan", "kn" }, { "kas", "ks" }, { "kat", "ka" }, + { "kau", "kr" }, { "kaz", "kk" }, { "khm", "km" }, { "kik", "ki" }, + { "kin", "rw" }, { "kir", "ky" }, { "kom", "kv" }, { "kon", "kg" }, + { "kor", "ko" }, { "kua", "kj" }, { "kur", "ku" }, { "lao", "lo" }, + { "lat", "la" }, { "lav", "lv" }, { "lim", "li" }, { "lin", "ln" }, + { "lit", "lt" }, { "ltz", "lb" }, { "lub", "lu" }, { "lug", "lg" }, + { "mac", "mk" }, { "mah", "mh" }, { "mal", "ml" }, { "mao", "mi" }, + { "mar", "mr" }, { "may", "ms" }, { "mkd", "mk" }, { "mlg", "mg" }, + { "mlt", "mt" }, { "mon", "mn" }, { "mri", "mi" }, { "msa", "ms" }, + { "mya", "my" }, { "nau", "na" }, { "nav", "nv" }, { "nbl", "nr" }, + { "nde", "nd" }, { "ndo", "ng" }, { "nep", "ne" }, { "nld", "nl" }, + { "nno", "nn" }, { "nob", "nb" }, { "nor", "no" }, { "nya", "ny" }, + { "oci", "oc" }, { "oji", "oj" }, { "ori", "or" }, { "orm", "om" }, + { "oss", "os" }, { "pan", "pa" }, { "per", "fa" }, { "pli", "pi" }, + { "pol", "pl" }, { "por", "pt" }, { "pus", "ps" }, { "que", "qu" }, + { "roh", "rm" }, { "ron", "ro" }, { "rum", "ro" }, { "run", "rn" }, + { "rus", "ru" }, { "sag", "sg" }, { "san", "sa" }, { "sin", "si" }, + { "slk", "sk" }, { "slo", "sk" }, { "slv", "sl" }, { "sme", "se" }, + { "smo", "sm" }, { "sna", "sn" }, { "snd", "sd" }, { "som", "so" }, + { "sot", "st" }, { "spa", "es" }, { "sqi", "sq" }, { "srd", "sc" }, + { "srp", "sr" }, { "ssw", "ss" }, { "sun", "su" }, { "swa", "sw" }, + { "swe", "sv" }, { "tah", "ty" }, { "tam", "ta" }, { "tat", "tt" }, + { "tel", "te" }, { "tgk", "tg" }, { "tgl", "tl" }, { "tha", "th" }, + { "tib", "bo" }, { "tir", "ti" }, { "ton", "to" }, { "tsn", "tn" }, + { "tso", "ts" }, { "tuk", "tk" }, { "tur", "tr" }, { "twi", "tw" }, + { "uig", "ug" }, { "ukr", "uk" }, { "urd", "ur" }, { "uzb", "uz" }, + { "ven", "ve" }, { "vie", "vi" }, { "vol", "vo" }, { "wel", "cy" }, + { "wln", "wa" }, { "wol", "wo" }, { "xho", "xh" }, { "yid", "yi" }, + { "yor", "yo" }, { "zha", "za" }, { "zho", "zh" }, { "zul", "zu" }, +}; + +} // namespace + +namespace edash_packager { + +std::string LanguageToShortestForm(const std::string& language) { + if (language.size() == 2) { + // Presumably already a valid ISO-639-1 code, and therefore conforms to + // BCP-47's requirement to use the shortest possible code. + return language; + } + + for (size_t i = 0; i < arraysize(kLanguageMap); ++i) { + if (language == kLanguageMap[i].iso_639_2) { + return kLanguageMap[i].iso_639_1; + } + } + + // This could happen legitimately for languages which have no 2-letter code, + // but that would imply that the input language code is a 3-letter code. + DCHECK_EQ(3, language.size()); + return language; +} + +std::string LanguageToISO_639_2(const std::string& language) { + if (language.size() == 3) { + // Presumably already a valid ISO-639-2 code. + return language; + } + + for (size_t i = 0; i < arraysize(kLanguageMap); ++i) { + if (language == kLanguageMap[i].iso_639_1) { + return kLanguageMap[i].iso_639_2; + } + } + + LOG(WARNING) << "No equivalent 3-letter language code for " << language; + // This is probably a mistake on the part of the user and should be treated + // as invalid input. + return "und"; +} + +} // namespace edash_packager diff --git a/packager/mpd/base/language_utils.h b/packager/mpd/base/language_utils.h new file mode 100644 index 0000000000..650256a2ca --- /dev/null +++ b/packager/mpd/base/language_utils.h @@ -0,0 +1,28 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd +// +// Funtions used by MpdBuilder class to generate an MPD file. + +#ifndef MPD_BASE_LANGUAGE_UTILS_H_ +#define MPD_BASE_LANGUAGE_UTILS_H_ + +#include + +namespace edash_packager { + +/// Convert a language tag to its shortest form, as required by RFC 5646 +/// indicated in the MPD spec. Assumes the input is a valid ISO-639-2 or +/// ISO-639-1 language tag. Regions and variants are not supported. +std::string LanguageToShortestForm(const std::string& language); + +/// Convert a language tag to a 3-letter ISO-639-2 code, as required by the ISO +/// BMFF spec. The input is assumed to be a valid ISO-639-2 or ISO-639-1 +/// language code. Regions and variants are not supported. +std::string LanguageToISO_639_2(const std::string& language); + +} // namespace edash_packager + +#endif // MPD_BASE_LANGUAGE_UTILS_H_ diff --git a/packager/mpd/base/mpd_builder.cc b/packager/mpd/base/mpd_builder.cc index 902d185bfe..3e133e8b05 100644 --- a/packager/mpd/base/mpd_builder.cc +++ b/packager/mpd/base/mpd_builder.cc @@ -21,6 +21,7 @@ #include "packager/base/time/time.h" #include "packager/media/file/file.h" #include "packager/mpd/base/content_protection_element.h" +#include "packager/mpd/base/language_utils.h" #include "packager/mpd/base/mpd_utils.h" #include "packager/mpd/base/xml/xml_node.h" @@ -201,10 +202,11 @@ void MpdBuilder::AddBaseUrl(const std::string& base_url) { base_urls_.push_back(base_url); } -AdaptationSet* MpdBuilder::AddAdaptationSet() { +AdaptationSet* MpdBuilder::AddAdaptationSet(const std::string& lang) { base::AutoLock scoped_lock(lock_); scoped_ptr adaptation_set(new AdaptationSet( - adaptation_set_counter_.GetNext(), mpd_options_, &representation_counter_)); + adaptation_set_counter_.GetNext(), lang, mpd_options_, + &representation_counter_)); DCHECK(adaptation_set); adaptation_sets_.push_back(adaptation_set.get()); @@ -447,11 +449,13 @@ void MpdBuilder::MakePathsRelativeToMpd(const std::string& mpd_path, } AdaptationSet::AdaptationSet(uint32_t adaptation_set_id, + const std::string& lang, const MpdOptions& mpd_options, base::AtomicSequenceNumber* counter) : representations_deleter_(&representations_), representation_counter_(counter), id_(adaptation_set_id), + lang_(lang), mpd_options_(mpd_options) { DCHECK(counter); } @@ -498,6 +502,9 @@ xml::ScopedXmlPtr::type AdaptationSet::GetXml() { } adaptation_set.SetId(id_); + if (!lang_.empty() && lang_ != "und") { + adaptation_set.SetStringAttribute("lang", LanguageToShortestForm(lang_)); + } return adaptation_set.PassScopedPtr(); } diff --git a/packager/mpd/base/mpd_builder.h b/packager/mpd/base/mpd_builder.h index 5ab753511a..18b21bd708 100644 --- a/packager/mpd/base/mpd_builder.h +++ b/packager/mpd/base/mpd_builder.h @@ -66,7 +66,7 @@ class MpdBuilder { /// Adds to the MPD. /// @return The new adaptation set, which is owned by this instance. - AdaptationSet* AddAdaptationSet(); + AdaptationSet* AddAdaptationSet(const std::string& lang); /// Write the MPD to specified file. /// @param[out] output_file is MPD destination. output_file will be @@ -180,7 +180,8 @@ class AdaptationSet { /// @param representation_counter is a Counter for assigning ID numbers to /// Representation. It can not be NULL. AdaptationSet(uint32_t adaptation_set_id, - const MpdOptions& mpd_options_, + const std::string& lang, + const MpdOptions& mpd_options, base::AtomicSequenceNumber* representation_counter); // Gets the earliest, normalized segment timestamp. Returns true if @@ -196,6 +197,7 @@ class AdaptationSet { base::AtomicSequenceNumber* const representation_counter_; const uint32_t id_; + const std::string lang_; const MpdOptions& mpd_options_; DISALLOW_COPY_AND_ASSIGN(AdaptationSet); diff --git a/packager/mpd/base/mpd_builder_unittest.cc b/packager/mpd/base/mpd_builder_unittest.cc index d722edc10a..6736d40fcf 100644 --- a/packager/mpd/base/mpd_builder_unittest.cc +++ b/packager/mpd/base/mpd_builder_unittest.cc @@ -74,7 +74,7 @@ class MpdBuilderTest: public ::testing::Test { protected: void AddRepresentation(const MediaInfo& media_info) { - AdaptationSet* adaptation_set = mpd_.AddAdaptationSet(); + AdaptationSet* adaptation_set = mpd_.AddAdaptationSet(""); ASSERT_TRUE(adaptation_set); Representation* representation = @@ -294,7 +294,7 @@ TEST_F(StaticMpdBuilderTest, CheckAdaptationSetId) { const uint32_t kAdaptationSetId = 42; AdaptationSet adaptation_set( - kAdaptationSetId, MpdOptions(), &sequence_counter); + kAdaptationSetId, "", MpdOptions(), &sequence_counter); ASSERT_NO_FATAL_FAILURE(CheckIdEqual(kAdaptationSetId, &adaptation_set)); } @@ -321,10 +321,10 @@ TEST_F(StaticMpdBuilderTest, VideoAndAudio) { MediaInfo audio_media_info = GetTestMediaInfo(kFileNameAudioMediaInfo1); // The order matters here to check against expected output. - AdaptationSet* video_adaptation_set = mpd_.AddAdaptationSet(); + AdaptationSet* video_adaptation_set = mpd_.AddAdaptationSet(""); ASSERT_TRUE(video_adaptation_set); - AdaptationSet* audio_adaptation_set = mpd_.AddAdaptationSet(); + AdaptationSet* audio_adaptation_set = mpd_.AddAdaptationSet(""); ASSERT_TRUE(audio_adaptation_set); Representation* audio_representation = @@ -344,7 +344,7 @@ TEST_F(StaticMpdBuilderTest, AudioChannelConfigurationWithContentProtection) { MediaInfo encrypted_audio_media_info = GetTestMediaInfo(kFileNameEncytpedAudioMediaInfo); - AdaptationSet* audio_adaptation_set = mpd_.AddAdaptationSet(); + AdaptationSet* audio_adaptation_set = mpd_.AddAdaptationSet(""); ASSERT_TRUE(audio_adaptation_set); Representation* audio_representation = @@ -367,7 +367,7 @@ TEST_F(StaticMpdBuilderTest, MediaInfoMissingBandwidth) { TEST_F(StaticMpdBuilderTest, WriteToFile) { MediaInfo video_media_info = GetTestMediaInfo(kFileNameVideoMediaInfo1); - AdaptationSet* video_adaptation_set = mpd_.AddAdaptationSet(); + AdaptationSet* video_adaptation_set = mpd_.AddAdaptationSet(""); ASSERT_TRUE(video_adaptation_set); Representation* video_representation = diff --git a/packager/mpd/base/simple_mpd_notifier.cc b/packager/mpd/base/simple_mpd_notifier.cc index d737fa8bbd..7ffaed40d6 100644 --- a/packager/mpd/base/simple_mpd_notifier.cc +++ b/packager/mpd/base/simple_mpd_notifier.cc @@ -48,9 +48,13 @@ bool SimpleMpdNotifier::NotifyNewContainer(const MediaInfo& media_info, base::AutoLock auto_lock(lock_); // TODO(kqyang): Consider adding a new method MpdBuilder::AddRepresentation. // Most of the codes here can be moved inside. - AdaptationSet** adaptation_set = &adaptation_set_map_[content_type]; + std::string lang; + if (media_info.audio_info().size() > 0) { + lang = media_info.audio_info(0).language(); + } + AdaptationSet** adaptation_set = &adaptation_set_map_[content_type][lang]; if (*adaptation_set == NULL) - *adaptation_set = mpd_builder_->AddAdaptationSet(); + *adaptation_set = mpd_builder_->AddAdaptationSet(lang); DCHECK(*adaptation_set); MediaInfo adjusted_media_info(media_info); diff --git a/packager/mpd/base/simple_mpd_notifier.h b/packager/mpd/base/simple_mpd_notifier.h index 3143ab8b04..7050174f90 100644 --- a/packager/mpd/base/simple_mpd_notifier.h +++ b/packager/mpd/base/simple_mpd_notifier.h @@ -63,7 +63,9 @@ class SimpleMpdNotifier : public MpdNotifier { base::Lock lock_; - typedef std::map AdaptationSetMap; + // [type][lang] = AdaptationSet + typedef std::map > + AdaptationSetMap; AdaptationSetMap adaptation_set_map_; typedef std::map RepresentationMap; diff --git a/packager/mpd/mpd.gyp b/packager/mpd/mpd.gyp index 9c6d93dd19..72c96053a8 100644 --- a/packager/mpd/mpd.gyp +++ b/packager/mpd/mpd.gyp @@ -37,6 +37,8 @@ 'base/bandwidth_estimator.h', 'base/content_protection_element.cc', 'base/content_protection_element.h', + 'base/language_utils.cc', + 'base/language_utils.h', 'base/mpd_builder.cc', 'base/mpd_builder.h', 'base/mpd_notifier.h', diff --git a/packager/mpd/test/data/language_audio_media_info1.txt b/packager/mpd/test/data/language_audio_media_info1.txt new file mode 100644 index 0000000000..568d1c020a --- /dev/null +++ b/packager/mpd/test/data/language_audio_media_info1.txt @@ -0,0 +1,20 @@ +bandwidth: 400 +audio_info { + codec: "mp4a.40.2" + sampling_frequency: 44100 + time_scale: 1200 + num_channels: 2 + language: "eng" +} +init_range { + begin: 0 + end: 120 +} +index_range { + begin: 121 + end: 221 +} +reference_time_scale: 50 +container_type: 1 +media_file_name: "test_output_file_name_audio_eng1.mp4" +media_duration_seconds: 10.5 diff --git a/packager/mpd/test/data/language_audio_media_info2.txt b/packager/mpd/test/data/language_audio_media_info2.txt new file mode 100644 index 0000000000..b59a7da75b --- /dev/null +++ b/packager/mpd/test/data/language_audio_media_info2.txt @@ -0,0 +1,20 @@ +bandwidth: 800 +audio_info { + codec: "mp4a.40.2" + sampling_frequency: 44100 + time_scale: 1200 + num_channels: 2 + language: "eng" +} +init_range { + begin: 0 + end: 120 +} +index_range { + begin: 121 + end: 221 +} +reference_time_scale: 50 +container_type: 1 +media_file_name: "test_output_file_name_audio_eng2.mp4" +media_duration_seconds: 10.5 diff --git a/packager/mpd/test/data/language_audio_media_info3.txt b/packager/mpd/test/data/language_audio_media_info3.txt new file mode 100644 index 0000000000..07563cd5ca --- /dev/null +++ b/packager/mpd/test/data/language_audio_media_info3.txt @@ -0,0 +1,20 @@ +bandwidth: 400 +audio_info { + codec: "mp4a.40.2" + sampling_frequency: 44100 + time_scale: 1200 + num_channels: 2 + language: "ger" +} +init_range { + begin: 0 + end: 120 +} +index_range { + begin: 121 + end: 221 +} +reference_time_scale: 50 +container_type: 1 +media_file_name: "test_output_file_name_audio_ger1.mp4" +media_duration_seconds: 10.5 diff --git a/packager/mpd/test/data/language_audio_media_info_expected_output.txt b/packager/mpd/test/data/language_audio_media_info_expected_output.txt new file mode 100644 index 0000000000..7ae93977ad --- /dev/null +++ b/packager/mpd/test/data/language_audio_media_info_expected_output.txt @@ -0,0 +1,38 @@ + + + + + + test_output_file_name1.mp4 + + + + + + + + + test_output_file_name_audio_eng1.mp4 + + + + + + + test_output_file_name_audio_eng2.mp4 + + + + + + + + + test_output_file_name_audio_ger1.mp4 + + + + + + + diff --git a/packager/mpd/test/data/language_video_media_info1.txt b/packager/mpd/test/data/language_video_media_info1.txt new file mode 100644 index 0000000000..f8cf2ef12a --- /dev/null +++ b/packager/mpd/test/data/language_video_media_info1.txt @@ -0,0 +1,19 @@ +bandwidth: 7620 +video_info { + codec: "avc1.010101" + width: 720 + height: 480 + time_scale: 10 +} +init_range { + begin: 0 + end: 120 +} +index_range { + begin: 121 + end: 221 +} +reference_time_scale: 1000 +container_type: 1 +media_file_name: "test_output_file_name1.mp4" +media_duration_seconds: 10.5 diff --git a/packager/mpd/test/mpd_builder_test_helper.h b/packager/mpd/test/mpd_builder_test_helper.h index 33c3a1678b..7bfbf6a7be 100644 --- a/packager/mpd/test/mpd_builder_test_helper.h +++ b/packager/mpd/test/mpd_builder_test_helper.h @@ -20,6 +20,14 @@ const char kFileNameVideoMediaInfo1[] = "video_media_info1.txt"; const char kFileNameVideoMediaInfo2[] = "video_media_info2.txt"; const char kFileNameAudioMediaInfo1[] = "audio_media_info1.txt"; const char kFileNameEncytpedAudioMediaInfo[] = "encrypted_audio_media_info.txt"; +const char kFileNameLanguageAudioMediaInfo1[] = + "language_audio_media_info1.txt"; +const char kFileNameLanguageAudioMediaInfo2[] = + "language_audio_media_info2.txt"; +const char kFileNameLanguageAudioMediaInfo3[] = + "language_audio_media_info3.txt"; +const char kFileNameLanguageVideoMediaInfo1[] = + "language_video_media_info1.txt"; // These are the expected output files. const char kFileNameExpectedMpdOutputVideo1[] = @@ -39,6 +47,9 @@ const char kFileNameExpectedMpdOutputEncryptedAudio[] = const char kFileNameExpectedMpdOutputDynamicNormal[] = "dynamic_normal_mpd.txt"; +const char kFileNameExpectedMpdOutputLanguageAudio[] = + "language_audio_media_info_expected_output.txt"; + // Returns the path to test data with |file_name|. Use constants above to get // path to the test files. base::FilePath GetTestDataFilePath(const std::string& file_name); diff --git a/packager/mpd/util/mpd_writer.cc b/packager/mpd/util/mpd_writer.cc index 04f0c29de3..1ec35ae462 100644 --- a/packager/mpd/util/mpd_writer.cc +++ b/packager/mpd/util/mpd_writer.cc @@ -88,14 +88,15 @@ bool SetMediaInfosToMpdBuilder(const std::list& media_infos, return false; DCHECK(mpd_builder); - AdaptationSet* video_adaptation_set = - has_video ? mpd_builder->AddAdaptationSet() : NULL; - AdaptationSet* audio_adaptation_set = - has_audio ? mpd_builder->AddAdaptationSet() : NULL; - AdaptationSet* text_adaptation_set = - has_text ? mpd_builder->AddAdaptationSet() : NULL; - DCHECK(video_adaptation_set || audio_adaptation_set || text_adaptation_set); + // [type][lang] = AdaptationSet + std::map > map; + // This puts video sets into the map first, which keeps some pre-existing + // test expectations from changing. + if (has_video) { + map["video"][""] = mpd_builder->AddAdaptationSet(""); + } + for (std::list::const_iterator it = media_infos.begin(); it != media_infos.end(); ++it) { @@ -103,15 +104,22 @@ bool SetMediaInfosToMpdBuilder(const std::list& media_infos, DCHECK(OnlyOneTrue( HasVideo(media_info), HasAudio(media_info), HasText(media_info))); - Representation* representation = NULL; + std::string lang; + AdaptationSet** adaptation_set = NULL; if (HasVideo(media_info)) { - representation = video_adaptation_set->AddRepresentation(media_info); + adaptation_set = &map["video"][lang]; } else if (HasAudio(media_info)) { - representation = audio_adaptation_set->AddRepresentation(media_info); + lang = media_info.audio_info(0).language(); + adaptation_set = &map["audio"][lang]; } else if (HasText(media_info)) { - representation = text_adaptation_set->AddRepresentation(media_info); + adaptation_set = &map["text"][lang]; + } + if (!*adaptation_set) { + *adaptation_set = mpd_builder->AddAdaptationSet(lang); } + Representation* representation = + (*adaptation_set)->AddRepresentation(media_info); if (!representation) { LOG(ERROR) << "Failed to add representation."; return false; diff --git a/packager/mpd/util/mpd_writer_unittest.cc b/packager/mpd/util/mpd_writer_unittest.cc index 7e569c9f98..4e77e8e80e 100644 --- a/packager/mpd/util/mpd_writer_unittest.cc +++ b/packager/mpd/util/mpd_writer_unittest.cc @@ -19,7 +19,7 @@ TEST(MpdWriterTest, VideoMediaInfo) { MpdWriter mpd_writer; base::FilePath media_info_file = GetTestDataFilePath(kFileNameVideoMediaInfo1); - ASSERT_TRUE(mpd_writer.AddFile(media_info_file.value().c_str(), "")); + ASSERT_TRUE(mpd_writer.AddFile(media_info_file.value(), "")); std::string generated_mpd; ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd)); ASSERT_TRUE(ValidateMpdSchema(generated_mpd)); @@ -36,8 +36,8 @@ TEST(MpdWriterTest, TwoVideoMediaInfo) { base::FilePath media_info_file2 = GetTestDataFilePath(kFileNameVideoMediaInfo2); - ASSERT_TRUE(mpd_writer.AddFile(media_info_file1.value().c_str(), "")); - ASSERT_TRUE(mpd_writer.AddFile(media_info_file2.value().c_str(), "")); + ASSERT_TRUE(mpd_writer.AddFile(media_info_file1.value(), "")); + ASSERT_TRUE(mpd_writer.AddFile(media_info_file2.value(), "")); std::string generated_mpd; ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd)); @@ -52,7 +52,7 @@ TEST(MpdWriterTest, AudioMediaInfo) { MpdWriter mpd_writer; base::FilePath media_info_file = GetTestDataFilePath(kFileNameAudioMediaInfo1); - ASSERT_TRUE(mpd_writer.AddFile(media_info_file.value().c_str(), "")); + ASSERT_TRUE(mpd_writer.AddFile(media_info_file.value(), "")); std::string generated_mpd; ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd)); ASSERT_TRUE(ValidateMpdSchema(generated_mpd)); @@ -69,8 +69,8 @@ TEST(MpdWriterTest, VideoAudioMediaInfo) { base::FilePath video_media_info = GetTestDataFilePath(kFileNameVideoMediaInfo1); - ASSERT_TRUE(mpd_writer.AddFile(audio_media_info.value().c_str(), "")); - ASSERT_TRUE(mpd_writer.AddFile(video_media_info.value().c_str(), "")); + ASSERT_TRUE(mpd_writer.AddFile(audio_media_info.value(), "")); + ASSERT_TRUE(mpd_writer.AddFile(video_media_info.value(), "")); std::string generated_mpd; ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd)); @@ -86,8 +86,7 @@ TEST(MpdWriterTest, EncryptedAudioMediaInfo) { base::FilePath encrypted_audio_media_info = GetTestDataFilePath(kFileNameEncytpedAudioMediaInfo); - ASSERT_TRUE(mpd_writer.AddFile(encrypted_audio_media_info.value().c_str(), - "")); + ASSERT_TRUE(mpd_writer.AddFile(encrypted_audio_media_info.value(), "")); std::string generated_mpd; ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd)); @@ -97,4 +96,29 @@ TEST(MpdWriterTest, EncryptedAudioMediaInfo) { generated_mpd, kFileNameExpectedMpdOutputEncryptedAudio)); } +TEST(MpdWriterTest, LanguageMediaInfo) { + MpdWriter mpd_writer; + base::FilePath audio_media_info1 = + GetTestDataFilePath(kFileNameLanguageAudioMediaInfo1); + base::FilePath audio_media_info2 = + GetTestDataFilePath(kFileNameLanguageAudioMediaInfo2); + base::FilePath audio_media_info3 = + GetTestDataFilePath(kFileNameLanguageAudioMediaInfo3); + base::FilePath video_media_info1 = + GetTestDataFilePath(kFileNameLanguageVideoMediaInfo1); + + ASSERT_TRUE(mpd_writer.AddFile(audio_media_info1.value(), "")); + ASSERT_TRUE(mpd_writer.AddFile(audio_media_info2.value(), "")); + ASSERT_TRUE(mpd_writer.AddFile(audio_media_info3.value(), "")); + ASSERT_TRUE(mpd_writer.AddFile(video_media_info1.value(), "")); + + std::string generated_mpd; + ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd)); + ASSERT_TRUE(ValidateMpdSchema(generated_mpd)); + + ASSERT_NO_FATAL_FAILURE(ExpectMpdToEqualExpectedOutputFile( + generated_mpd, + kFileNameExpectedMpdOutputLanguageAudio)); +} + } // namespace edash_packager