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
This commit is contained in:
parent
80db1c7bbf
commit
77ec23afe4
|
@ -22,6 +22,7 @@
|
||||||
#
|
#
|
||||||
# Please keep the list sorted.
|
# Please keep the list sorted.
|
||||||
|
|
||||||
|
Joey Parrish <joeyparrish@google.com>
|
||||||
Kongqun Yang <kqyang@google.com>
|
Kongqun Yang <kqyang@google.com>
|
||||||
Rintaro Kuroiwa <rkuroiwa@google.com>
|
Rintaro Kuroiwa <rkuroiwa@google.com>
|
||||||
Thomas Inskip <tinskip@google.com>
|
Thomas Inskip <tinskip@google.com>
|
||||||
|
|
|
@ -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
|
|
@ -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 <string>
|
||||||
|
|
||||||
|
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_
|
|
@ -21,6 +21,7 @@
|
||||||
#include "packager/base/time/time.h"
|
#include "packager/base/time/time.h"
|
||||||
#include "packager/media/file/file.h"
|
#include "packager/media/file/file.h"
|
||||||
#include "packager/mpd/base/content_protection_element.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/mpd_utils.h"
|
||||||
#include "packager/mpd/base/xml/xml_node.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);
|
base_urls_.push_back(base_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
AdaptationSet* MpdBuilder::AddAdaptationSet() {
|
AdaptationSet* MpdBuilder::AddAdaptationSet(const std::string& lang) {
|
||||||
base::AutoLock scoped_lock(lock_);
|
base::AutoLock scoped_lock(lock_);
|
||||||
scoped_ptr<AdaptationSet> adaptation_set(new AdaptationSet(
|
scoped_ptr<AdaptationSet> adaptation_set(new AdaptationSet(
|
||||||
adaptation_set_counter_.GetNext(), mpd_options_, &representation_counter_));
|
adaptation_set_counter_.GetNext(), lang, mpd_options_,
|
||||||
|
&representation_counter_));
|
||||||
|
|
||||||
DCHECK(adaptation_set);
|
DCHECK(adaptation_set);
|
||||||
adaptation_sets_.push_back(adaptation_set.get());
|
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,
|
AdaptationSet::AdaptationSet(uint32_t adaptation_set_id,
|
||||||
|
const std::string& lang,
|
||||||
const MpdOptions& mpd_options,
|
const MpdOptions& mpd_options,
|
||||||
base::AtomicSequenceNumber* counter)
|
base::AtomicSequenceNumber* counter)
|
||||||
: representations_deleter_(&representations_),
|
: representations_deleter_(&representations_),
|
||||||
representation_counter_(counter),
|
representation_counter_(counter),
|
||||||
id_(adaptation_set_id),
|
id_(adaptation_set_id),
|
||||||
|
lang_(lang),
|
||||||
mpd_options_(mpd_options) {
|
mpd_options_(mpd_options) {
|
||||||
DCHECK(counter);
|
DCHECK(counter);
|
||||||
}
|
}
|
||||||
|
@ -498,6 +502,9 @@ xml::ScopedXmlPtr<xmlNode>::type AdaptationSet::GetXml() {
|
||||||
}
|
}
|
||||||
|
|
||||||
adaptation_set.SetId(id_);
|
adaptation_set.SetId(id_);
|
||||||
|
if (!lang_.empty() && lang_ != "und") {
|
||||||
|
adaptation_set.SetStringAttribute("lang", LanguageToShortestForm(lang_));
|
||||||
|
}
|
||||||
return adaptation_set.PassScopedPtr();
|
return adaptation_set.PassScopedPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ class MpdBuilder {
|
||||||
|
|
||||||
/// Adds <AdaptationSet> to the MPD.
|
/// Adds <AdaptationSet> to the MPD.
|
||||||
/// @return The new adaptation set, which is owned by this instance.
|
/// @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.
|
/// Write the MPD to specified file.
|
||||||
/// @param[out] output_file is MPD destination. output_file will be
|
/// @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
|
/// @param representation_counter is a Counter for assigning ID numbers to
|
||||||
/// Representation. It can not be NULL.
|
/// Representation. It can not be NULL.
|
||||||
AdaptationSet(uint32_t adaptation_set_id,
|
AdaptationSet(uint32_t adaptation_set_id,
|
||||||
const MpdOptions& mpd_options_,
|
const std::string& lang,
|
||||||
|
const MpdOptions& mpd_options,
|
||||||
base::AtomicSequenceNumber* representation_counter);
|
base::AtomicSequenceNumber* representation_counter);
|
||||||
|
|
||||||
// Gets the earliest, normalized segment timestamp. Returns true if
|
// Gets the earliest, normalized segment timestamp. Returns true if
|
||||||
|
@ -196,6 +197,7 @@ class AdaptationSet {
|
||||||
base::AtomicSequenceNumber* const representation_counter_;
|
base::AtomicSequenceNumber* const representation_counter_;
|
||||||
|
|
||||||
const uint32_t id_;
|
const uint32_t id_;
|
||||||
|
const std::string lang_;
|
||||||
const MpdOptions& mpd_options_;
|
const MpdOptions& mpd_options_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(AdaptationSet);
|
DISALLOW_COPY_AND_ASSIGN(AdaptationSet);
|
||||||
|
|
|
@ -74,7 +74,7 @@ class MpdBuilderTest: public ::testing::Test {
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void AddRepresentation(const MediaInfo& media_info) {
|
void AddRepresentation(const MediaInfo& media_info) {
|
||||||
AdaptationSet* adaptation_set = mpd_.AddAdaptationSet();
|
AdaptationSet* adaptation_set = mpd_.AddAdaptationSet("");
|
||||||
ASSERT_TRUE(adaptation_set);
|
ASSERT_TRUE(adaptation_set);
|
||||||
|
|
||||||
Representation* representation =
|
Representation* representation =
|
||||||
|
@ -294,7 +294,7 @@ TEST_F(StaticMpdBuilderTest, CheckAdaptationSetId) {
|
||||||
const uint32_t kAdaptationSetId = 42;
|
const uint32_t kAdaptationSetId = 42;
|
||||||
|
|
||||||
AdaptationSet adaptation_set(
|
AdaptationSet adaptation_set(
|
||||||
kAdaptationSetId, MpdOptions(), &sequence_counter);
|
kAdaptationSetId, "", MpdOptions(), &sequence_counter);
|
||||||
ASSERT_NO_FATAL_FAILURE(CheckIdEqual(kAdaptationSetId, &adaptation_set));
|
ASSERT_NO_FATAL_FAILURE(CheckIdEqual(kAdaptationSetId, &adaptation_set));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,10 +321,10 @@ TEST_F(StaticMpdBuilderTest, VideoAndAudio) {
|
||||||
MediaInfo audio_media_info = GetTestMediaInfo(kFileNameAudioMediaInfo1);
|
MediaInfo audio_media_info = GetTestMediaInfo(kFileNameAudioMediaInfo1);
|
||||||
|
|
||||||
// The order matters here to check against expected output.
|
// 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);
|
ASSERT_TRUE(video_adaptation_set);
|
||||||
|
|
||||||
AdaptationSet* audio_adaptation_set = mpd_.AddAdaptationSet();
|
AdaptationSet* audio_adaptation_set = mpd_.AddAdaptationSet("");
|
||||||
ASSERT_TRUE(audio_adaptation_set);
|
ASSERT_TRUE(audio_adaptation_set);
|
||||||
|
|
||||||
Representation* audio_representation =
|
Representation* audio_representation =
|
||||||
|
@ -344,7 +344,7 @@ TEST_F(StaticMpdBuilderTest, AudioChannelConfigurationWithContentProtection) {
|
||||||
MediaInfo encrypted_audio_media_info =
|
MediaInfo encrypted_audio_media_info =
|
||||||
GetTestMediaInfo(kFileNameEncytpedAudioMediaInfo);
|
GetTestMediaInfo(kFileNameEncytpedAudioMediaInfo);
|
||||||
|
|
||||||
AdaptationSet* audio_adaptation_set = mpd_.AddAdaptationSet();
|
AdaptationSet* audio_adaptation_set = mpd_.AddAdaptationSet("");
|
||||||
ASSERT_TRUE(audio_adaptation_set);
|
ASSERT_TRUE(audio_adaptation_set);
|
||||||
|
|
||||||
Representation* audio_representation =
|
Representation* audio_representation =
|
||||||
|
@ -367,7 +367,7 @@ TEST_F(StaticMpdBuilderTest, MediaInfoMissingBandwidth) {
|
||||||
|
|
||||||
TEST_F(StaticMpdBuilderTest, WriteToFile) {
|
TEST_F(StaticMpdBuilderTest, WriteToFile) {
|
||||||
MediaInfo video_media_info = GetTestMediaInfo(kFileNameVideoMediaInfo1);
|
MediaInfo video_media_info = GetTestMediaInfo(kFileNameVideoMediaInfo1);
|
||||||
AdaptationSet* video_adaptation_set = mpd_.AddAdaptationSet();
|
AdaptationSet* video_adaptation_set = mpd_.AddAdaptationSet("");
|
||||||
ASSERT_TRUE(video_adaptation_set);
|
ASSERT_TRUE(video_adaptation_set);
|
||||||
|
|
||||||
Representation* video_representation =
|
Representation* video_representation =
|
||||||
|
|
|
@ -48,9 +48,13 @@ bool SimpleMpdNotifier::NotifyNewContainer(const MediaInfo& media_info,
|
||||||
base::AutoLock auto_lock(lock_);
|
base::AutoLock auto_lock(lock_);
|
||||||
// TODO(kqyang): Consider adding a new method MpdBuilder::AddRepresentation.
|
// TODO(kqyang): Consider adding a new method MpdBuilder::AddRepresentation.
|
||||||
// Most of the codes here can be moved inside.
|
// 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)
|
if (*adaptation_set == NULL)
|
||||||
*adaptation_set = mpd_builder_->AddAdaptationSet();
|
*adaptation_set = mpd_builder_->AddAdaptationSet(lang);
|
||||||
|
|
||||||
DCHECK(*adaptation_set);
|
DCHECK(*adaptation_set);
|
||||||
MediaInfo adjusted_media_info(media_info);
|
MediaInfo adjusted_media_info(media_info);
|
||||||
|
|
|
@ -63,7 +63,9 @@ class SimpleMpdNotifier : public MpdNotifier {
|
||||||
|
|
||||||
base::Lock lock_;
|
base::Lock lock_;
|
||||||
|
|
||||||
typedef std::map<ContentType, AdaptationSet*> AdaptationSetMap;
|
// [type][lang] = AdaptationSet
|
||||||
|
typedef std::map<ContentType, std::map<std::string, AdaptationSet*> >
|
||||||
|
AdaptationSetMap;
|
||||||
AdaptationSetMap adaptation_set_map_;
|
AdaptationSetMap adaptation_set_map_;
|
||||||
|
|
||||||
typedef std::map<uint32_t, Representation*> RepresentationMap;
|
typedef std::map<uint32_t, Representation*> RepresentationMap;
|
||||||
|
|
|
@ -37,6 +37,8 @@
|
||||||
'base/bandwidth_estimator.h',
|
'base/bandwidth_estimator.h',
|
||||||
'base/content_protection_element.cc',
|
'base/content_protection_element.cc',
|
||||||
'base/content_protection_element.h',
|
'base/content_protection_element.h',
|
||||||
|
'base/language_utils.cc',
|
||||||
|
'base/language_utils.h',
|
||||||
'base/mpd_builder.cc',
|
'base/mpd_builder.cc',
|
||||||
'base/mpd_builder.h',
|
'base/mpd_builder.h',
|
||||||
'base/mpd_notifier.h',
|
'base/mpd_notifier.h',
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT10.5S">
|
||||||
|
<Period>
|
||||||
|
<AdaptationSet id="0">
|
||||||
|
<Representation id="3" bandwidth="7620" codecs="avc1.010101" mimeType="video/mp4" width="720" height="480">
|
||||||
|
<BaseURL>test_output_file_name1.mp4</BaseURL>
|
||||||
|
<SegmentBase indexRange="121-221" timescale="1000">
|
||||||
|
<Initialization range="0-120"/>
|
||||||
|
</SegmentBase>
|
||||||
|
</Representation>
|
||||||
|
</AdaptationSet>
|
||||||
|
<AdaptationSet id="1" lang="en">
|
||||||
|
<Representation id="0" bandwidth="400" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||||
|
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||||
|
<BaseURL>test_output_file_name_audio_eng1.mp4</BaseURL>
|
||||||
|
<SegmentBase indexRange="121-221" timescale="50">
|
||||||
|
<Initialization range="0-120"/>
|
||||||
|
</SegmentBase>
|
||||||
|
</Representation>
|
||||||
|
<Representation id="1" bandwidth="800" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||||
|
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||||
|
<BaseURL>test_output_file_name_audio_eng2.mp4</BaseURL>
|
||||||
|
<SegmentBase indexRange="121-221" timescale="50">
|
||||||
|
<Initialization range="0-120"/>
|
||||||
|
</SegmentBase>
|
||||||
|
</Representation>
|
||||||
|
</AdaptationSet>
|
||||||
|
<AdaptationSet id="2" lang="de">
|
||||||
|
<Representation id="2" bandwidth="400" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||||
|
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||||
|
<BaseURL>test_output_file_name_audio_ger1.mp4</BaseURL>
|
||||||
|
<SegmentBase indexRange="121-221" timescale="50">
|
||||||
|
<Initialization range="0-120"/>
|
||||||
|
</SegmentBase>
|
||||||
|
</Representation>
|
||||||
|
</AdaptationSet>
|
||||||
|
</Period>
|
||||||
|
</MPD>
|
|
@ -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
|
|
@ -20,6 +20,14 @@ const char kFileNameVideoMediaInfo1[] = "video_media_info1.txt";
|
||||||
const char kFileNameVideoMediaInfo2[] = "video_media_info2.txt";
|
const char kFileNameVideoMediaInfo2[] = "video_media_info2.txt";
|
||||||
const char kFileNameAudioMediaInfo1[] = "audio_media_info1.txt";
|
const char kFileNameAudioMediaInfo1[] = "audio_media_info1.txt";
|
||||||
const char kFileNameEncytpedAudioMediaInfo[] = "encrypted_audio_media_info.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.
|
// These are the expected output files.
|
||||||
const char kFileNameExpectedMpdOutputVideo1[] =
|
const char kFileNameExpectedMpdOutputVideo1[] =
|
||||||
|
@ -39,6 +47,9 @@ const char kFileNameExpectedMpdOutputEncryptedAudio[] =
|
||||||
|
|
||||||
const char kFileNameExpectedMpdOutputDynamicNormal[] = "dynamic_normal_mpd.txt";
|
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
|
// Returns the path to test data with |file_name|. Use constants above to get
|
||||||
// path to the test files.
|
// path to the test files.
|
||||||
base::FilePath GetTestDataFilePath(const std::string& file_name);
|
base::FilePath GetTestDataFilePath(const std::string& file_name);
|
||||||
|
|
|
@ -88,14 +88,15 @@ bool SetMediaInfosToMpdBuilder(const std::list<MediaInfo>& media_infos,
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
DCHECK(mpd_builder);
|
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<std::string, std::map<std::string, AdaptationSet*> > 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<MediaInfo>::const_iterator it = media_infos.begin();
|
for (std::list<MediaInfo>::const_iterator it = media_infos.begin();
|
||||||
it != media_infos.end();
|
it != media_infos.end();
|
||||||
++it) {
|
++it) {
|
||||||
|
@ -103,15 +104,22 @@ bool SetMediaInfosToMpdBuilder(const std::list<MediaInfo>& media_infos,
|
||||||
DCHECK(OnlyOneTrue(
|
DCHECK(OnlyOneTrue(
|
||||||
HasVideo(media_info), HasAudio(media_info), HasText(media_info)));
|
HasVideo(media_info), HasAudio(media_info), HasText(media_info)));
|
||||||
|
|
||||||
Representation* representation = NULL;
|
std::string lang;
|
||||||
|
AdaptationSet** adaptation_set = NULL;
|
||||||
if (HasVideo(media_info)) {
|
if (HasVideo(media_info)) {
|
||||||
representation = video_adaptation_set->AddRepresentation(media_info);
|
adaptation_set = &map["video"][lang];
|
||||||
} else if (HasAudio(media_info)) {
|
} 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)) {
|
} 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) {
|
if (!representation) {
|
||||||
LOG(ERROR) << "Failed to add representation.";
|
LOG(ERROR) << "Failed to add representation.";
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -19,7 +19,7 @@ TEST(MpdWriterTest, VideoMediaInfo) {
|
||||||
MpdWriter mpd_writer;
|
MpdWriter mpd_writer;
|
||||||
base::FilePath media_info_file = GetTestDataFilePath(kFileNameVideoMediaInfo1);
|
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;
|
std::string generated_mpd;
|
||||||
ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd));
|
ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd));
|
||||||
ASSERT_TRUE(ValidateMpdSchema(generated_mpd));
|
ASSERT_TRUE(ValidateMpdSchema(generated_mpd));
|
||||||
|
@ -36,8 +36,8 @@ TEST(MpdWriterTest, TwoVideoMediaInfo) {
|
||||||
base::FilePath media_info_file2 =
|
base::FilePath media_info_file2 =
|
||||||
GetTestDataFilePath(kFileNameVideoMediaInfo2);
|
GetTestDataFilePath(kFileNameVideoMediaInfo2);
|
||||||
|
|
||||||
ASSERT_TRUE(mpd_writer.AddFile(media_info_file1.value().c_str(), ""));
|
ASSERT_TRUE(mpd_writer.AddFile(media_info_file1.value(), ""));
|
||||||
ASSERT_TRUE(mpd_writer.AddFile(media_info_file2.value().c_str(), ""));
|
ASSERT_TRUE(mpd_writer.AddFile(media_info_file2.value(), ""));
|
||||||
|
|
||||||
std::string generated_mpd;
|
std::string generated_mpd;
|
||||||
ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd));
|
ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd));
|
||||||
|
@ -52,7 +52,7 @@ TEST(MpdWriterTest, AudioMediaInfo) {
|
||||||
MpdWriter mpd_writer;
|
MpdWriter mpd_writer;
|
||||||
base::FilePath media_info_file = GetTestDataFilePath(kFileNameAudioMediaInfo1);
|
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;
|
std::string generated_mpd;
|
||||||
ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd));
|
ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd));
|
||||||
ASSERT_TRUE(ValidateMpdSchema(generated_mpd));
|
ASSERT_TRUE(ValidateMpdSchema(generated_mpd));
|
||||||
|
@ -69,8 +69,8 @@ TEST(MpdWriterTest, VideoAudioMediaInfo) {
|
||||||
base::FilePath video_media_info =
|
base::FilePath video_media_info =
|
||||||
GetTestDataFilePath(kFileNameVideoMediaInfo1);
|
GetTestDataFilePath(kFileNameVideoMediaInfo1);
|
||||||
|
|
||||||
ASSERT_TRUE(mpd_writer.AddFile(audio_media_info.value().c_str(), ""));
|
ASSERT_TRUE(mpd_writer.AddFile(audio_media_info.value(), ""));
|
||||||
ASSERT_TRUE(mpd_writer.AddFile(video_media_info.value().c_str(), ""));
|
ASSERT_TRUE(mpd_writer.AddFile(video_media_info.value(), ""));
|
||||||
|
|
||||||
std::string generated_mpd;
|
std::string generated_mpd;
|
||||||
ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd));
|
ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd));
|
||||||
|
@ -86,8 +86,7 @@ TEST(MpdWriterTest, EncryptedAudioMediaInfo) {
|
||||||
base::FilePath encrypted_audio_media_info =
|
base::FilePath encrypted_audio_media_info =
|
||||||
GetTestDataFilePath(kFileNameEncytpedAudioMediaInfo);
|
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;
|
std::string generated_mpd;
|
||||||
ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd));
|
ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd));
|
||||||
|
@ -97,4 +96,29 @@ TEST(MpdWriterTest, EncryptedAudioMediaInfo) {
|
||||||
generated_mpd, kFileNameExpectedMpdOutputEncryptedAudio));
|
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
|
} // namespace edash_packager
|
||||||
|
|
Loading…
Reference in New Issue