Order Representations and AdaptationSets by id()

Change-Id: I04509819c1f8fa78e4826d53966531bf98e90849
This commit is contained in:
KongQun Yang 2018-01-18 10:55:36 -08:00
parent 6d0a6bb120
commit 322337a958
6 changed files with 109 additions and 50 deletions

View File

@ -196,8 +196,9 @@ Representation* AdaptationSet::AddRepresentation(const MediaInfo& media_info) {
return NULL;
}
UpdateFromMediaInfo(media_info);
representations_.push_back(std::move(new_representation));
return representations_.back().get();
Representation* representation_ptr = new_representation.get();
representation_map_[representation_ptr->id()] = std::move(new_representation);
return representation_ptr;
}
Representation* AdaptationSet::CopyRepresentationWithTimeOffset(
@ -211,8 +212,9 @@ Representation* AdaptationSet::CopyRepresentationWithTimeOffset(
representation, presentation_time_offset, std::move(listener)));
UpdateFromMediaInfo(new_representation->GetMediaInfo());
representations_.push_back(std::move(new_representation));
return representations_.back().get();
Representation* representation_ptr = new_representation.get();
representation_map_[representation_ptr->id()] = std::move(new_representation);
return representation_ptr;
}
void AdaptationSet::AddContentProtectionElement(
@ -320,8 +322,8 @@ xml::scoped_xml_ptr<xmlNode> AdaptationSet::GetXml() {
for (AdaptationSet::Role role : roles_)
adaptation_set.AddRoleElement("urn:mpeg:dash:role:2011", RoleToText(role));
for (const std::unique_ptr<Representation>& representation :
representations_) {
for (const auto& representation_pair : representation_map_) {
const auto& representation = representation_pair.second;
if (suppress_representation_width)
representation->SuppressOnce(Representation::kSuppressWidth);
if (suppress_representation_height)
@ -376,9 +378,8 @@ void AdaptationSet::AddTrickPlayReferenceId(uint32_t id) {
const std::list<Representation*> AdaptationSet::GetRepresentations() const {
std::list<Representation*> representations;
for (const std::unique_ptr<Representation>& representation :
representations_) {
representations.push_back(representation.get());
for (const auto& representation_pair : representation_map_) {
representations.push_back(representation_pair.second.get());
}
return representations;
}
@ -450,7 +451,7 @@ void AdaptationSet::CheckLiveSegmentAlignment(uint32_t representation_id,
representation_start_times.push_back(start_time);
// There's no way to detemine whether the segments are aligned if some
// representations do not have any segments.
if (representation_segment_start_times_.size() != representations_.size())
if (representation_segment_start_times_.size() != representation_map_.size())
return;
DCHECK(!representation_start_times.empty());

View File

@ -229,7 +229,9 @@ class AdaptationSet {
void RecordFrameRate(uint32_t frame_duration, uint32_t timescale);
std::list<ContentProtectionElement> content_protection_elements_;
std::list<std::unique_ptr<Representation>> representations_;
// representation_id => Representation map. It also keeps the representations_
// sorted by default.
std::map<uint32_t, std::unique_ptr<Representation>> representation_map_;
base::AtomicSequenceNumber* const representation_counter_;

View File

@ -15,8 +15,8 @@
#include "packager/mpd/test/mpd_builder_test_helper.h"
#include "packager/mpd/test/xml_compare.h"
using ::testing::ElementsAre;
using ::testing::Not;
using ::testing::UnorderedElementsAre;
namespace shaka {
@ -590,7 +590,7 @@ TEST_F(AdaptationSetTest, BubbleUpAttributesToAdaptationSet) {
}
TEST_F(AdaptationSetTest, GetRepresentations) {
const char k480pMediaInfo[] =
const char kMediaInfo1[] =
"video_info {\n"
" codec: 'avc1'\n"
" width: 720\n"
@ -601,7 +601,7 @@ TEST_F(AdaptationSetTest, GetRepresentations) {
" pixel_height: 9\n"
"}\n"
"container_type: 1\n";
const char k360pMediaInfo[] =
const char kMediaInfo2[] =
"video_info {\n"
" codec: 'avc1'\n"
" width: 640\n"
@ -615,15 +615,29 @@ TEST_F(AdaptationSetTest, GetRepresentations) {
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
Representation* representation_480p =
adaptation_set->AddRepresentation(ConvertToMediaInfo(k480pMediaInfo));
Representation* representation1 =
adaptation_set->AddRepresentation(ConvertToMediaInfo(kMediaInfo1));
EXPECT_THAT(adaptation_set->GetRepresentations(),
UnorderedElementsAre(representation_480p));
ElementsAre(representation1));
Representation* representation_360p =
adaptation_set->AddRepresentation(ConvertToMediaInfo(k360pMediaInfo));
Representation* representation2 =
adaptation_set->AddRepresentation(ConvertToMediaInfo(kMediaInfo2));
EXPECT_THAT(adaptation_set->GetRepresentations(),
UnorderedElementsAre(representation_360p, representation_480p));
ElementsAre(representation1, representation2));
auto new_adaptation_set =
CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
const uint64_t kPresentationTimeOffset = 80;
Representation* new_representation2 =
new_adaptation_set->CopyRepresentationWithTimeOffset(
*representation2, kPresentationTimeOffset);
Representation* new_representation1 =
new_adaptation_set->CopyRepresentationWithTimeOffset(
*representation1, kPresentationTimeOffset);
EXPECT_THAT(new_adaptation_set->GetRepresentations(),
// Elements are ordered by id().
ElementsAre(new_representation1, new_representation2));
}
// Verify that subsegmentAlignment is set to true if all the Representations'

View File

@ -88,9 +88,10 @@ AdaptationSet* Period::GetOrCreateAdaptationSet(
}
}
}
adaptation_sets.push_back(new_adaptation_set.get());
adaptation_sets_.push_back(std::move(new_adaptation_set));
return adaptation_sets_.back().get();
AdaptationSet* adaptation_set_ptr = new_adaptation_set.get();
adaptation_sets.push_back(adaptation_set_ptr);
adaptation_set_map_[adaptation_set_ptr->id()] = std::move(new_adaptation_set);
return adaptation_set_ptr;
}
xml::scoped_xml_ptr<xmlNode> Period::GetXml() {
@ -99,8 +100,8 @@ xml::scoped_xml_ptr<xmlNode> Period::GetXml() {
// Required for 'dynamic' MPDs.
period.SetId(id_);
// Iterate thru AdaptationSets and add them to one big Period element.
for (const auto& adaptation_set : adaptation_sets_) {
xml::scoped_xml_ptr<xmlNode> child(adaptation_set->GetXml());
for (const auto& adaptation_set_pair : adaptation_set_map_) {
xml::scoped_xml_ptr<xmlNode> child(adaptation_set_pair.second->GetXml());
if (!child || !period.AddChild(std::move(child)))
return nullptr;
}
@ -115,9 +116,8 @@ xml::scoped_xml_ptr<xmlNode> Period::GetXml() {
const std::list<AdaptationSet*> Period::GetAdaptationSets() const {
std::list<AdaptationSet*> adaptation_sets;
for (const std::unique_ptr<AdaptationSet>& adaptation_set :
adaptation_sets_) {
adaptation_sets.push_back(adaptation_set.get());
for (const auto& adaptation_set_pair : adaptation_set_map_) {
adaptation_sets.push_back(adaptation_set_pair.second.get());
}
return adaptation_sets;
}

View File

@ -105,8 +105,9 @@ class Period {
const MpdOptions& mpd_options_;
base::AtomicSequenceNumber* const adaptation_set_counter_;
base::AtomicSequenceNumber* const representation_counter_;
// The list of AdaptationSets in this Period.
std::list<std::unique_ptr<AdaptationSet>> adaptation_sets_;
// adaptation_id => Adaptation map. It also keeps the adaptation_sets_ sorted
// by default.
std::map<uint32_t, std::unique_ptr<AdaptationSet>> adaptation_set_map_;
// AdaptationSets grouped by a specific adaptation set grouping key.
// AdaptationSets with the same key contain identical parameters except
// ContentProtection parameters. A single AdaptationSet would be created

View File

@ -21,7 +21,6 @@ using ::testing::Eq;
using ::testing::InSequence;
using ::testing::Return;
using ::testing::StrictMock;
using ::testing::UnorderedElementsAre;
namespace shaka {
namespace {
@ -788,7 +787,7 @@ TEST_P(PeriodTest, SplitAdaptationSetsByLanguageAndCodec) {
}
TEST_P(PeriodTest, GetAdaptationSets) {
const char kAacEnglishAudioContent[] =
const char kContent1[] =
"audio_info {\n"
" codec: 'mp4a.40.2'\n"
" sampling_frequency: 44100\n"
@ -799,7 +798,7 @@ TEST_P(PeriodTest, GetAdaptationSets) {
"reference_time_scale: 50\n"
"container_type: CONTAINER_MP4\n"
"media_duration_seconds: 10.5\n";
const char kAacGermanAudioContent[] =
const char kContent2[] =
"audio_info {\n"
" codec: 'mp4a.40.2'\n"
" sampling_frequency: 44100\n"
@ -811,33 +810,75 @@ TEST_P(PeriodTest, GetAdaptationSets) {
"container_type: CONTAINER_MP4\n"
"media_duration_seconds: 10.5\n";
std::unique_ptr<StrictMock<MockAdaptationSet>> aac_eng_adaptation_set(
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_1(
new StrictMock<MockAdaptationSet>(1));
auto* aac_eng_adaptation_set_ptr = aac_eng_adaptation_set.get();
std::unique_ptr<StrictMock<MockAdaptationSet>> aac_ger_adaptation_set(
auto* adaptation_set_1_ptr = adaptation_set_1.get();
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_2(
new StrictMock<MockAdaptationSet>(2));
auto* aac_ger_adaptation_set_ptr = aac_ger_adaptation_set.get();
auto* adaptation_set_2_ptr = adaptation_set_2.get();
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _))
.WillOnce(Return(ByMove(std::move(aac_eng_adaptation_set))))
.WillOnce(Return(ByMove(std::move(aac_ger_adaptation_set))));
.WillOnce(Return(ByMove(std::move(adaptation_set_1))))
.WillOnce(Return(ByMove(std::move(adaptation_set_2))));
ASSERT_EQ(aac_eng_adaptation_set_ptr,
testable_period_.GetOrCreateAdaptationSet(
ConvertToMediaInfo(kAacEnglishAudioContent),
ASSERT_EQ(adaptation_set_1_ptr, testable_period_.GetOrCreateAdaptationSet(
ConvertToMediaInfo(kContent1),
content_protection_in_adaptation_set_));
EXPECT_THAT(testable_period_.GetAdaptationSets(),
UnorderedElementsAre(aac_eng_adaptation_set_ptr));
ElementsAre(adaptation_set_1_ptr));
ASSERT_EQ(aac_ger_adaptation_set_ptr,
testable_period_.GetOrCreateAdaptationSet(
ConvertToMediaInfo(kAacGermanAudioContent),
ASSERT_EQ(adaptation_set_2_ptr, testable_period_.GetOrCreateAdaptationSet(
ConvertToMediaInfo(kContent2),
content_protection_in_adaptation_set_));
EXPECT_THAT(testable_period_.GetAdaptationSets(),
UnorderedElementsAre(aac_eng_adaptation_set_ptr,
aac_ger_adaptation_set_ptr));
ElementsAre(adaptation_set_1_ptr, adaptation_set_2_ptr));
}
TEST_P(PeriodTest, GetAdaptationSetsOrderedByAdaptationSetId) {
const char kContent1[] =
"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";
const char kContent2[] =
"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";
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_1(
new StrictMock<MockAdaptationSet>(1));
auto* adaptation_set_1_ptr = adaptation_set_1.get();
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_2(
new StrictMock<MockAdaptationSet>(2));
auto* adaptation_set_2_ptr = adaptation_set_2.get();
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _))
.WillOnce(Return(ByMove(std::move(adaptation_set_2))))
.WillOnce(Return(ByMove(std::move(adaptation_set_1))));
ASSERT_EQ(adaptation_set_2_ptr, testable_period_.GetOrCreateAdaptationSet(
ConvertToMediaInfo(kContent2),
content_protection_in_adaptation_set_));
ASSERT_EQ(adaptation_set_1_ptr, testable_period_.GetOrCreateAdaptationSet(
ConvertToMediaInfo(kContent1),
content_protection_in_adaptation_set_));
EXPECT_THAT(testable_period_.GetAdaptationSets(),
// Elements are ordered by id().
ElementsAre(adaptation_set_1_ptr, adaptation_set_2_ptr));
}
INSTANTIATE_TEST_CASE_P(ContentProtectionInAdaptationSet,
PeriodTest,
::testing::Bool());