Split AdaptationSets by content type, container, codec, and language
This adds a new function to key AdaptationSets based on the characteristics that require separate sets. With this, we are now able to package multi-codec assets at once (AVC1/MP4 and VP9/WebM in the same manifest, but in different AdaptationSets). Fixes b/27534371 Change-Id: Ie82fd096d2ef99fd23e323d60fd0849b1e8d7500
This commit is contained in:
parent
b6718a37c5
commit
f3e19fc002
|
@ -74,15 +74,8 @@ bool DashIopMpdNotifier::NotifyNewContainer(const MediaInfo& media_info,
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
base::AutoLock auto_lock(lock_);
|
base::AutoLock auto_lock(lock_);
|
||||||
std::string lang;
|
const std::string key = GetAdaptationSetKey(media_info);
|
||||||
if (media_info.has_audio_info()) {
|
AdaptationSet* adaptation_set = GetAdaptationSetForMediaInfo(key, media_info);
|
||||||
lang = media_info.audio_info().language();
|
|
||||||
} else if (media_info.has_text_info()) {
|
|
||||||
lang = media_info.text_info().language();
|
|
||||||
}
|
|
||||||
|
|
||||||
AdaptationSet* adaptation_set =
|
|
||||||
GetAdaptationSetForMediaInfo(media_info, content_type, lang);
|
|
||||||
DCHECK(adaptation_set);
|
DCHECK(adaptation_set);
|
||||||
if (media_info.has_text_info()) {
|
if (media_info.has_text_info()) {
|
||||||
// IOP requires all AdaptationSets to have (sub)segmentAlignment set to
|
// IOP requires all AdaptationSets to have (sub)segmentAlignment set to
|
||||||
|
@ -100,7 +93,7 @@ bool DashIopMpdNotifier::NotifyNewContainer(const MediaInfo& media_info,
|
||||||
|
|
||||||
representation_id_to_adaptation_set_[representation->id()] = adaptation_set;
|
representation_id_to_adaptation_set_[representation->id()] = adaptation_set;
|
||||||
|
|
||||||
SetGroupId(content_type, lang, adaptation_set);
|
SetGroupId(key, adaptation_set);
|
||||||
|
|
||||||
*container_id = representation->id();
|
*container_id = representation->id();
|
||||||
DCHECK(!ContainsKey(representation_map_, representation->id()));
|
DCHECK(!ContainsKey(representation_map_, representation->id()));
|
||||||
|
@ -169,13 +162,11 @@ bool DashIopMpdNotifier::Flush() {
|
||||||
}
|
}
|
||||||
|
|
||||||
AdaptationSet* DashIopMpdNotifier::GetAdaptationSetForMediaInfo(
|
AdaptationSet* DashIopMpdNotifier::GetAdaptationSetForMediaInfo(
|
||||||
const MediaInfo& media_info,
|
const std::string& key,
|
||||||
ContentType content_type,
|
const MediaInfo& media_info) {
|
||||||
const std::string& language) {
|
std::list<AdaptationSet*>& adaptation_sets = adaptation_set_list_map_[key];
|
||||||
std::list<AdaptationSet*>& adaptation_sets =
|
|
||||||
adaptation_set_list_map_[content_type][language];
|
|
||||||
if (adaptation_sets.empty())
|
if (adaptation_sets.empty())
|
||||||
return NewAdaptationSet(media_info, language, &adaptation_sets);
|
return NewAdaptationSet(media_info, &adaptation_sets);
|
||||||
|
|
||||||
const bool has_protected_content = media_info.has_protected_content();
|
const bool has_protected_content = media_info.has_protected_content();
|
||||||
|
|
||||||
|
@ -204,13 +195,12 @@ AdaptationSet* DashIopMpdNotifier::GetAdaptationSetForMediaInfo(
|
||||||
|
|
||||||
// None of the adaptation sets match with the new content protection.
|
// None of the adaptation sets match with the new content protection.
|
||||||
// Need a new one.
|
// Need a new one.
|
||||||
return NewAdaptationSet(media_info, language, &adaptation_sets);
|
return NewAdaptationSet(media_info, &adaptation_sets);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all the UUIDs of the AdaptationSet. If another AdaptationSet has the
|
// Get all the UUIDs of the AdaptationSet. If another AdaptationSet has the
|
||||||
// same UUIDs then those should be groupable.
|
// same UUIDs then those should be groupable.
|
||||||
void DashIopMpdNotifier::SetGroupId(ContentType type,
|
void DashIopMpdNotifier::SetGroupId(const std::string& key,
|
||||||
const std::string& language,
|
|
||||||
AdaptationSet* adaptation_set) {
|
AdaptationSet* adaptation_set) {
|
||||||
if (adaptation_set->Group() >= 0) // @group already assigned.
|
if (adaptation_set->Group() >= 0) // @group already assigned.
|
||||||
return;
|
return;
|
||||||
|
@ -230,7 +220,7 @@ void DashIopMpdNotifier::SetGroupId(ContentType type,
|
||||||
GetUUIDs(protected_content_it->second);
|
GetUUIDs(protected_content_it->second);
|
||||||
|
|
||||||
std::list<AdaptationSet*>& same_type_adapatation_sets =
|
std::list<AdaptationSet*>& same_type_adapatation_sets =
|
||||||
adaptation_set_list_map_[type][language];
|
adaptation_set_list_map_[key];
|
||||||
DCHECK(!same_type_adapatation_sets.empty())
|
DCHECK(!same_type_adapatation_sets.empty())
|
||||||
<< "same_type_adapatation_sets should not be null, it should at least "
|
<< "same_type_adapatation_sets should not be null, it should at least "
|
||||||
"contain adaptation_set";
|
"contain adaptation_set";
|
||||||
|
@ -270,8 +260,8 @@ void DashIopMpdNotifier::SetGroupId(ContentType type,
|
||||||
|
|
||||||
AdaptationSet* DashIopMpdNotifier::NewAdaptationSet(
|
AdaptationSet* DashIopMpdNotifier::NewAdaptationSet(
|
||||||
const MediaInfo& media_info,
|
const MediaInfo& media_info,
|
||||||
const std::string& language,
|
|
||||||
std::list<AdaptationSet*>* adaptation_sets) {
|
std::list<AdaptationSet*>* adaptation_sets) {
|
||||||
|
std::string language = GetLanguage(media_info);
|
||||||
AdaptationSet* new_adaptation_set = mpd_builder_->AddAdaptationSet(language);
|
AdaptationSet* new_adaptation_set = mpd_builder_->AddAdaptationSet(language);
|
||||||
if (media_info.has_protected_content()) {
|
if (media_info.has_protected_content()) {
|
||||||
DCHECK(!ContainsKey(protected_content_map_, new_adaptation_set->id()));
|
DCHECK(!ContainsKey(protected_content_map_, new_adaptation_set->id()));
|
||||||
|
|
|
@ -70,22 +70,17 @@ class DashIopMpdNotifier : public MpdNotifier {
|
||||||
// This does not necessarily return a new AdaptationSet. If
|
// This does not necessarily return a new AdaptationSet. If
|
||||||
// media_info.protected_content completely matches with an existing
|
// media_info.protected_content completely matches with an existing
|
||||||
// AdaptationSet, then it will return the pointer.
|
// AdaptationSet, then it will return the pointer.
|
||||||
AdaptationSet* GetAdaptationSetForMediaInfo(const MediaInfo& media_info,
|
AdaptationSet* GetAdaptationSetForMediaInfo(const std::string& key,
|
||||||
ContentType type,
|
const MediaInfo& media_info);
|
||||||
const std::string& language);
|
|
||||||
|
|
||||||
// Sets a group id for |adaptation_set| if applicable.
|
// Sets a group id for |adaptation_set| if applicable.
|
||||||
// If a group ID is already assigned, then this returns immediately.
|
// If a group ID is already assigned, then this returns immediately.
|
||||||
// |type| and |language| are the type and language of |adaptation_set|.
|
void SetGroupId(const std::string& key, AdaptationSet* adaptation_set);
|
||||||
void SetGroupId(ContentType type,
|
|
||||||
const std::string& language,
|
|
||||||
AdaptationSet* adaptation_set);
|
|
||||||
|
|
||||||
// Helper function to get a new AdaptationSet; registers the values
|
// Helper function to get a new AdaptationSet; registers the values
|
||||||
// to the fields (maps) of the instance.
|
// to the fields (maps) of the instance.
|
||||||
// If the media is encrypted, registers data to protected_content_map_.
|
// If the media is encrypted, registers data to protected_content_map_.
|
||||||
AdaptationSet* NewAdaptationSet(const MediaInfo& media_info,
|
AdaptationSet* NewAdaptationSet(const MediaInfo& media_info,
|
||||||
const std::string& language,
|
|
||||||
std::list<AdaptationSet*>* adaptation_sets);
|
std::list<AdaptationSet*>* adaptation_sets);
|
||||||
|
|
||||||
// Testing only method. Returns a pointer to MpdBuilder.
|
// Testing only method. Returns a pointer to MpdBuilder.
|
||||||
|
@ -98,10 +93,7 @@ class DashIopMpdNotifier : public MpdNotifier {
|
||||||
mpd_builder_ = mpd_builder.Pass();
|
mpd_builder_ = mpd_builder.Pass();
|
||||||
}
|
}
|
||||||
|
|
||||||
// [type][lang] = list<AdaptationSet>
|
std::map<std::string, std::list<AdaptationSet*>> adaptation_set_list_map_;
|
||||||
// Note: lang can be empty, e.g. for video.
|
|
||||||
std::map<ContentType, std::map<std::string, std::list<AdaptationSet*> > >
|
|
||||||
adaptation_set_list_map_;
|
|
||||||
RepresentationMap representation_map_;
|
RepresentationMap representation_map_;
|
||||||
|
|
||||||
// Used to check whether a Representation should be added to an AdaptationSet.
|
// Used to check whether a Representation should be added to an AdaptationSet.
|
||||||
|
|
|
@ -776,6 +776,102 @@ TEST_F(DashIopMpdNotifierTest, NotifyNewContainerAndSampleDurationNoMock) {
|
||||||
EXPECT_TRUE(notifier.Flush());
|
EXPECT_TRUE(notifier.Flush());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't put different audio languages or codecs in the same AdaptationSet.
|
||||||
|
TEST_P(DashIopMpdNotifierTest, SplitAdaptationSetsByLanguageAndCodec) {
|
||||||
|
// MP4, English
|
||||||
|
const char kAudioContent1[] =
|
||||||
|
"audio_info {\n"
|
||||||
|
" codec: 'mp4a.40.2'\n"
|
||||||
|
" sampling_frequency: 44100\n"
|
||||||
|
" time_scale: 1200\n"
|
||||||
|
" num_channels: 2\n"
|
||||||
|
" language: 'eng'\n"
|
||||||
|
"}\n"
|
||||||
|
"reference_time_scale: 50\n"
|
||||||
|
"container_type: CONTAINER_MP4\n"
|
||||||
|
"media_duration_seconds: 10.5\n";
|
||||||
|
|
||||||
|
// MP4, German
|
||||||
|
const char kAudioContent2[] =
|
||||||
|
"audio_info {\n"
|
||||||
|
" codec: 'mp4a.40.2'\n"
|
||||||
|
" sampling_frequency: 44100\n"
|
||||||
|
" time_scale: 1200\n"
|
||||||
|
" num_channels: 2\n"
|
||||||
|
" language: 'ger'\n"
|
||||||
|
"}\n"
|
||||||
|
"reference_time_scale: 50\n"
|
||||||
|
"container_type: CONTAINER_MP4\n"
|
||||||
|
"media_duration_seconds: 10.5\n";
|
||||||
|
|
||||||
|
// WebM, German
|
||||||
|
const char kAudioContent3[] =
|
||||||
|
"audio_info {\n"
|
||||||
|
" codec: 'vorbis'\n"
|
||||||
|
" sampling_frequency: 44100\n"
|
||||||
|
" time_scale: 1200\n"
|
||||||
|
" num_channels: 2\n"
|
||||||
|
" language: 'ger'\n"
|
||||||
|
"}\n"
|
||||||
|
"reference_time_scale: 50\n"
|
||||||
|
"container_type: CONTAINER_WEBM\n"
|
||||||
|
"media_duration_seconds: 10.5\n";
|
||||||
|
|
||||||
|
// WebM, German again
|
||||||
|
const char kAudioContent4[] =
|
||||||
|
"audio_info {\n"
|
||||||
|
" codec: 'vorbis'\n"
|
||||||
|
" sampling_frequency: 44100\n"
|
||||||
|
" time_scale: 1200\n"
|
||||||
|
" num_channels: 2\n"
|
||||||
|
" language: 'ger'\n"
|
||||||
|
"}\n"
|
||||||
|
"reference_time_scale: 50\n"
|
||||||
|
"container_type: CONTAINER_WEBM\n"
|
||||||
|
"media_duration_seconds: 10.5\n";
|
||||||
|
|
||||||
|
DashIopMpdNotifier notifier(dash_profile(), empty_mpd_option_,
|
||||||
|
empty_base_urls_, output_path_);
|
||||||
|
scoped_ptr<MockMpdBuilder> mock_mpd_builder(new MockMpdBuilder(mpd_type()));
|
||||||
|
|
||||||
|
scoped_ptr<MockAdaptationSet> adaptation_set1(new MockAdaptationSet(1));
|
||||||
|
scoped_ptr<MockAdaptationSet> adaptation_set2(new MockAdaptationSet(2));
|
||||||
|
scoped_ptr<MockAdaptationSet> adaptation_set3(new MockAdaptationSet(3));
|
||||||
|
|
||||||
|
scoped_ptr<MockRepresentation> representation1(new MockRepresentation(1));
|
||||||
|
scoped_ptr<MockRepresentation> representation2(new MockRepresentation(2));
|
||||||
|
scoped_ptr<MockRepresentation> representation3(new MockRepresentation(3));
|
||||||
|
scoped_ptr<MockRepresentation> representation4(new MockRepresentation(4));
|
||||||
|
|
||||||
|
// We expect three AdaptationSets.
|
||||||
|
EXPECT_CALL(*mock_mpd_builder, AddAdaptationSet(_))
|
||||||
|
.WillOnce(Return(adaptation_set1.get()))
|
||||||
|
.WillOnce(Return(adaptation_set2.get()))
|
||||||
|
.WillOnce(Return(adaptation_set3.get()));
|
||||||
|
// The first AdaptationSet should have Eng MP4, one Representation.
|
||||||
|
EXPECT_CALL(*adaptation_set1, AddRepresentation(_))
|
||||||
|
.WillOnce(Return(representation1.get()));
|
||||||
|
// The second AdaptationSet should have Ger MP4, one Representation.
|
||||||
|
EXPECT_CALL(*adaptation_set2, AddRepresentation(_))
|
||||||
|
.WillOnce(Return(representation2.get()));
|
||||||
|
// The third AdaptationSet should have Ger WebM, two Representations.
|
||||||
|
EXPECT_CALL(*adaptation_set3, AddRepresentation(_))
|
||||||
|
.WillOnce(Return(representation3.get()))
|
||||||
|
.WillOnce(Return(representation4.get()));
|
||||||
|
|
||||||
|
uint32_t unused_container_id;
|
||||||
|
SetMpdBuilder(¬ifier, mock_mpd_builder.Pass());
|
||||||
|
EXPECT_TRUE(notifier.NotifyNewContainer(
|
||||||
|
ConvertToMediaInfo(kAudioContent1), &unused_container_id));
|
||||||
|
EXPECT_TRUE(notifier.NotifyNewContainer(
|
||||||
|
ConvertToMediaInfo(kAudioContent2), &unused_container_id));
|
||||||
|
EXPECT_TRUE(notifier.NotifyNewContainer(
|
||||||
|
ConvertToMediaInfo(kAudioContent3), &unused_container_id));
|
||||||
|
EXPECT_TRUE(notifier.NotifyNewContainer(
|
||||||
|
ConvertToMediaInfo(kAudioContent4), &unused_container_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(StaticAndDynamic,
|
INSTANTIATE_TEST_CASE_P(StaticAndDynamic,
|
||||||
DashIopMpdNotifierTest,
|
DashIopMpdNotifierTest,
|
||||||
::testing::Values(MpdBuilder::kStatic,
|
::testing::Values(MpdBuilder::kStatic,
|
||||||
|
|
|
@ -57,6 +57,16 @@ void RemoveDuplicateAttributes(
|
||||||
attributes.erase("schemeIdUri");
|
attributes.erase("schemeIdUri");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string GetLanguage(const MediaInfo& media_info) {
|
||||||
|
std::string lang;
|
||||||
|
if (media_info.has_audio_info()) {
|
||||||
|
lang = media_info.audio_info().language();
|
||||||
|
} else if (media_info.has_text_info()) {
|
||||||
|
lang = media_info.text_info().language();
|
||||||
|
}
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
|
||||||
std::string GetCodecs(const MediaInfo& media_info) {
|
std::string GetCodecs(const MediaInfo& media_info) {
|
||||||
CHECK(OnlyOneTrue(media_info.has_video_info(), media_info.has_audio_info(),
|
CHECK(OnlyOneTrue(media_info.has_video_info(), media_info.has_audio_info(),
|
||||||
media_info.has_text_info()));
|
media_info.has_text_info()));
|
||||||
|
@ -86,6 +96,47 @@ std::string GetCodecs(const MediaInfo& media_info) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string GetBaseCodec(const MediaInfo& media_info) {
|
||||||
|
std::string codec;
|
||||||
|
if (media_info.has_video_info()) {
|
||||||
|
codec = media_info.video_info().codec();
|
||||||
|
} else if (media_info.has_audio_info()) {
|
||||||
|
codec = media_info.audio_info().codec();
|
||||||
|
} else if (media_info.has_text_info()) {
|
||||||
|
codec = media_info.text_info().format();
|
||||||
|
}
|
||||||
|
// Convert, for example, "mp4a.40.2" to simply "mp4a".
|
||||||
|
// "mp4a.40.2" and "mp4a.40.5" can exist in the same AdaptationSet.
|
||||||
|
size_t dot = codec.find('.');
|
||||||
|
if (dot != std::string::npos) {
|
||||||
|
codec.erase(dot);
|
||||||
|
}
|
||||||
|
return codec;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetAdaptationSetKey(const MediaInfo& media_info) {
|
||||||
|
std::string key;
|
||||||
|
|
||||||
|
if (media_info.has_video_info()) {
|
||||||
|
key.append("video:");
|
||||||
|
} else if (media_info.has_audio_info()) {
|
||||||
|
key.append("audio:");
|
||||||
|
} else if (media_info.has_text_info()) {
|
||||||
|
key.append(MediaInfo_TextInfo_TextType_Name(media_info.text_info().type()));
|
||||||
|
key.append(":");
|
||||||
|
} else {
|
||||||
|
key.append("unknown:");
|
||||||
|
}
|
||||||
|
|
||||||
|
key.append(MediaInfo_ContainerType_Name(media_info.container_type()));
|
||||||
|
key.append(":");
|
||||||
|
key.append(GetBaseCodec(media_info));
|
||||||
|
key.append(":");
|
||||||
|
key.append(GetLanguage(media_info));
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
std::string SecondsToXmlDuration(double seconds) {
|
std::string SecondsToXmlDuration(double seconds) {
|
||||||
return "PT" + base::DoubleToString(seconds) + "S";
|
return "PT" + base::DoubleToString(seconds) + "S";
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,10 +40,20 @@ bool HasLiveOnlyFields(const MediaInfo& media_info);
|
||||||
void RemoveDuplicateAttributes(
|
void RemoveDuplicateAttributes(
|
||||||
ContentProtectionElement* content_protection_element);
|
ContentProtectionElement* content_protection_element);
|
||||||
|
|
||||||
|
// Returns a language tag. May be blank for video.
|
||||||
|
std::string GetLanguage(const MediaInfo& media_info);
|
||||||
|
|
||||||
// Returns a 'codecs' string that has all the video and audio codecs joined with
|
// Returns a 'codecs' string that has all the video and audio codecs joined with
|
||||||
// comma.
|
// comma.
|
||||||
std::string GetCodecs(const MediaInfo& media_info);
|
std::string GetCodecs(const MediaInfo& media_info);
|
||||||
|
|
||||||
|
// Returns a codec string without variants. For example, "mp4a" instead of
|
||||||
|
// "mp4a.40.2". May return a format for text streams.
|
||||||
|
std::string GetBaseCodec(const MediaInfo& media_info);
|
||||||
|
|
||||||
|
// Returns a key made from the characteristics that separate AdaptationSets.
|
||||||
|
std::string GetAdaptationSetKey(const MediaInfo& media_info);
|
||||||
|
|
||||||
std::string SecondsToXmlDuration(double seconds);
|
std::string SecondsToXmlDuration(double seconds);
|
||||||
|
|
||||||
// Tries to get "duration" attribute from |node|. On success |duration| is set.
|
// Tries to get "duration" attribute from |node|. On success |duration| is set.
|
||||||
|
|
|
@ -44,15 +44,12 @@ bool SimpleMpdNotifier::NotifyNewContainer(const MediaInfo& media_info,
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
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.
|
||||||
std::string lang;
|
std::string key = GetAdaptationSetKey(media_info);
|
||||||
if (media_info.has_audio_info()) {
|
std::string lang = GetLanguage(media_info);
|
||||||
lang = media_info.audio_info().language();
|
AdaptationSet** adaptation_set = &adaptation_set_map_[key];
|
||||||
} else if (media_info.has_text_info()) {
|
|
||||||
lang = media_info.text_info().language();
|
|
||||||
}
|
|
||||||
AdaptationSet** adaptation_set = &adaptation_set_map_[content_type][lang];
|
|
||||||
if (*adaptation_set == NULL)
|
if (*adaptation_set == NULL)
|
||||||
*adaptation_set = mpd_builder_->AddAdaptationSet(lang);
|
*adaptation_set = mpd_builder_->AddAdaptationSet(lang);
|
||||||
|
|
||||||
|
|
|
@ -74,9 +74,7 @@ class SimpleMpdNotifier : public MpdNotifier {
|
||||||
scoped_ptr<MpdBuilder> mpd_builder_;
|
scoped_ptr<MpdBuilder> mpd_builder_;
|
||||||
base::Lock lock_;
|
base::Lock lock_;
|
||||||
|
|
||||||
// [type][lang] = AdaptationSet
|
typedef std::map<std::string, AdaptationSet*> AdaptationSetMap;
|
||||||
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;
|
||||||
|
|
|
@ -308,6 +308,101 @@ TEST_P(SimpleMpdNotifierTest, UpdateEncryption) {
|
||||||
container_id, "myuuid", std::vector<uint8_t>(), kBogusNewPsshVector));
|
container_id, "myuuid", std::vector<uint8_t>(), kBogusNewPsshVector));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't put different audio languages or codecs in the same AdaptationSet.
|
||||||
|
TEST_P(SimpleMpdNotifierTest, SplitAdaptationSetsByLanguageAndCodec) {
|
||||||
|
// MP4, English
|
||||||
|
const char kAudioContent1[] =
|
||||||
|
"audio_info {\n"
|
||||||
|
" codec: 'mp4a.40.2'\n"
|
||||||
|
" sampling_frequency: 44100\n"
|
||||||
|
" time_scale: 1200\n"
|
||||||
|
" num_channels: 2\n"
|
||||||
|
" language: 'eng'\n"
|
||||||
|
"}\n"
|
||||||
|
"reference_time_scale: 50\n"
|
||||||
|
"container_type: CONTAINER_MP4\n"
|
||||||
|
"media_duration_seconds: 10.5\n";
|
||||||
|
|
||||||
|
// MP4, German
|
||||||
|
const char kAudioContent2[] =
|
||||||
|
"audio_info {\n"
|
||||||
|
" codec: 'mp4a.40.2'\n"
|
||||||
|
" sampling_frequency: 44100\n"
|
||||||
|
" time_scale: 1200\n"
|
||||||
|
" num_channels: 2\n"
|
||||||
|
" language: 'ger'\n"
|
||||||
|
"}\n"
|
||||||
|
"reference_time_scale: 50\n"
|
||||||
|
"container_type: CONTAINER_MP4\n"
|
||||||
|
"media_duration_seconds: 10.5\n";
|
||||||
|
|
||||||
|
// WebM, German
|
||||||
|
const char kAudioContent3[] =
|
||||||
|
"audio_info {\n"
|
||||||
|
" codec: 'vorbis'\n"
|
||||||
|
" sampling_frequency: 44100\n"
|
||||||
|
" time_scale: 1200\n"
|
||||||
|
" num_channels: 2\n"
|
||||||
|
" language: 'ger'\n"
|
||||||
|
"}\n"
|
||||||
|
"reference_time_scale: 50\n"
|
||||||
|
"container_type: CONTAINER_WEBM\n"
|
||||||
|
"media_duration_seconds: 10.5\n";
|
||||||
|
|
||||||
|
// WebM, German again
|
||||||
|
const char kAudioContent4[] =
|
||||||
|
"audio_info {\n"
|
||||||
|
" codec: 'vorbis'\n"
|
||||||
|
" sampling_frequency: 44100\n"
|
||||||
|
" time_scale: 1200\n"
|
||||||
|
" num_channels: 2\n"
|
||||||
|
" language: 'ger'\n"
|
||||||
|
"}\n"
|
||||||
|
"reference_time_scale: 50\n"
|
||||||
|
"container_type: CONTAINER_WEBM\n"
|
||||||
|
"media_duration_seconds: 10.5\n";
|
||||||
|
|
||||||
|
SimpleMpdNotifier notifier(kOnDemandProfile, empty_mpd_option_,
|
||||||
|
empty_base_urls_, output_path_);
|
||||||
|
scoped_ptr<MockMpdBuilder> mock_mpd_builder(StaticMpdBuilderMock());
|
||||||
|
|
||||||
|
scoped_ptr<MockAdaptationSet> adaptation_set1(new MockAdaptationSet(1));
|
||||||
|
scoped_ptr<MockAdaptationSet> adaptation_set2(new MockAdaptationSet(2));
|
||||||
|
scoped_ptr<MockAdaptationSet> adaptation_set3(new MockAdaptationSet(3));
|
||||||
|
|
||||||
|
scoped_ptr<MockRepresentation> representation1(new MockRepresentation(1));
|
||||||
|
scoped_ptr<MockRepresentation> representation2(new MockRepresentation(2));
|
||||||
|
scoped_ptr<MockRepresentation> representation3(new MockRepresentation(3));
|
||||||
|
scoped_ptr<MockRepresentation> representation4(new MockRepresentation(4));
|
||||||
|
|
||||||
|
// We expect three AdaptationSets.
|
||||||
|
EXPECT_CALL(*mock_mpd_builder, AddAdaptationSet(_))
|
||||||
|
.WillOnce(Return(adaptation_set1.get()))
|
||||||
|
.WillOnce(Return(adaptation_set2.get()))
|
||||||
|
.WillOnce(Return(adaptation_set3.get()));
|
||||||
|
// The first AdaptationSet should have Eng MP4, one Representation.
|
||||||
|
EXPECT_CALL(*adaptation_set1, AddRepresentation(_))
|
||||||
|
.WillOnce(Return(representation1.get()));
|
||||||
|
// The second AdaptationSet should have Ger MP4, one Representation.
|
||||||
|
EXPECT_CALL(*adaptation_set2, AddRepresentation(_))
|
||||||
|
.WillOnce(Return(representation2.get()));
|
||||||
|
// The third AdaptationSet should have Ger WebM, two Representations.
|
||||||
|
EXPECT_CALL(*adaptation_set3, AddRepresentation(_))
|
||||||
|
.WillOnce(Return(representation3.get()))
|
||||||
|
.WillOnce(Return(representation4.get()));
|
||||||
|
|
||||||
|
uint32_t unused_container_id;
|
||||||
|
SetMpdBuilder(¬ifier, mock_mpd_builder.Pass());
|
||||||
|
EXPECT_TRUE(notifier.NotifyNewContainer(
|
||||||
|
ConvertToMediaInfo(kAudioContent1), &unused_container_id));
|
||||||
|
EXPECT_TRUE(notifier.NotifyNewContainer(
|
||||||
|
ConvertToMediaInfo(kAudioContent2), &unused_container_id));
|
||||||
|
EXPECT_TRUE(notifier.NotifyNewContainer(
|
||||||
|
ConvertToMediaInfo(kAudioContent3), &unused_container_id));
|
||||||
|
EXPECT_TRUE(notifier.NotifyNewContainer(
|
||||||
|
ConvertToMediaInfo(kAudioContent4), &unused_container_id));
|
||||||
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(StaticAndDynamic,
|
INSTANTIATE_TEST_CASE_P(StaticAndDynamic,
|
||||||
SimpleMpdNotifierTest,
|
SimpleMpdNotifierTest,
|
||||||
::testing::Values(MpdBuilder::kStatic,
|
::testing::Values(MpdBuilder::kStatic,
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
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
|
|
|
@ -1,20 +0,0 @@
|
||||||
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
|
|
|
@ -1,20 +0,0 @@
|
||||||
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
|
|
|
@ -1,38 +0,0 @@
|
||||||
<?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" width="720" height="480" frameRate="10/1" contentType="video" par="3:2">
|
|
||||||
<Representation id="3" bandwidth="7620" codecs="avc1.010101" mimeType="video/mp4" sar="1:1">
|
|
||||||
<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" contentType="audio">
|
|
||||||
<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" contentType="audio">
|
|
||||||
<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>
|
|
|
@ -1,22 +0,0 @@
|
||||||
bandwidth: 7620
|
|
||||||
video_info {
|
|
||||||
codec: "avc1.010101"
|
|
||||||
width: 720
|
|
||||||
height: 480
|
|
||||||
time_scale: 10
|
|
||||||
frame_duration: 1
|
|
||||||
pixel_width: 1
|
|
||||||
pixel_height: 1
|
|
||||||
}
|
|
||||||
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
|
|
|
@ -23,14 +23,6 @@ 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[] =
|
||||||
|
@ -50,9 +42,6 @@ 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);
|
||||||
|
|
Loading…
Reference in New Issue