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.
|
||||
|
||||
Joey Parrish <joeyparrish@google.com>
|
||||
Kongqun Yang <kqyang@google.com>
|
||||
Rintaro Kuroiwa <rkuroiwa@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/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<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);
|
||||
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<xmlNode>::type AdaptationSet::GetXml() {
|
|||
}
|
||||
|
||||
adaptation_set.SetId(id_);
|
||||
if (!lang_.empty() && lang_ != "und") {
|
||||
adaptation_set.SetStringAttribute("lang", LanguageToShortestForm(lang_));
|
||||
}
|
||||
return adaptation_set.PassScopedPtr();
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ class MpdBuilder {
|
|||
|
||||
/// Adds <AdaptationSet> 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);
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -63,7 +63,9 @@ class SimpleMpdNotifier : public MpdNotifier {
|
|||
|
||||
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_;
|
||||
|
||||
typedef std::map<uint32_t, Representation*> RepresentationMap;
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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 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);
|
||||
|
|
|
@ -88,14 +88,15 @@ bool SetMediaInfosToMpdBuilder(const std::list<MediaInfo>& 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<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();
|
||||
it != media_infos.end();
|
||||
++it) {
|
||||
|
@ -103,15 +104,22 @@ bool SetMediaInfosToMpdBuilder(const std::list<MediaInfo>& 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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue