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

View File

@ -229,7 +229,9 @@ class AdaptationSet {
void RecordFrameRate(uint32_t frame_duration, uint32_t timescale); void RecordFrameRate(uint32_t frame_duration, uint32_t timescale);
std::list<ContentProtectionElement> content_protection_elements_; 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_; base::AtomicSequenceNumber* const representation_counter_;

View File

@ -15,8 +15,8 @@
#include "packager/mpd/test/mpd_builder_test_helper.h" #include "packager/mpd/test/mpd_builder_test_helper.h"
#include "packager/mpd/test/xml_compare.h" #include "packager/mpd/test/xml_compare.h"
using ::testing::ElementsAre;
using ::testing::Not; using ::testing::Not;
using ::testing::UnorderedElementsAre;
namespace shaka { namespace shaka {
@ -590,7 +590,7 @@ TEST_F(AdaptationSetTest, BubbleUpAttributesToAdaptationSet) {
} }
TEST_F(AdaptationSetTest, GetRepresentations) { TEST_F(AdaptationSetTest, GetRepresentations) {
const char k480pMediaInfo[] = const char kMediaInfo1[] =
"video_info {\n" "video_info {\n"
" codec: 'avc1'\n" " codec: 'avc1'\n"
" width: 720\n" " width: 720\n"
@ -601,7 +601,7 @@ TEST_F(AdaptationSetTest, GetRepresentations) {
" pixel_height: 9\n" " pixel_height: 9\n"
"}\n" "}\n"
"container_type: 1\n"; "container_type: 1\n";
const char k360pMediaInfo[] = const char kMediaInfo2[] =
"video_info {\n" "video_info {\n"
" codec: 'avc1'\n" " codec: 'avc1'\n"
" width: 640\n" " width: 640\n"
@ -615,15 +615,29 @@ TEST_F(AdaptationSetTest, GetRepresentations) {
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
Representation* representation_480p = Representation* representation1 =
adaptation_set->AddRepresentation(ConvertToMediaInfo(k480pMediaInfo)); adaptation_set->AddRepresentation(ConvertToMediaInfo(kMediaInfo1));
EXPECT_THAT(adaptation_set->GetRepresentations(), EXPECT_THAT(adaptation_set->GetRepresentations(),
UnorderedElementsAre(representation_480p)); ElementsAre(representation1));
Representation* representation_360p = Representation* representation2 =
adaptation_set->AddRepresentation(ConvertToMediaInfo(k360pMediaInfo)); adaptation_set->AddRepresentation(ConvertToMediaInfo(kMediaInfo2));
EXPECT_THAT(adaptation_set->GetRepresentations(), 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' // 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()); AdaptationSet* adaptation_set_ptr = new_adaptation_set.get();
adaptation_sets_.push_back(std::move(new_adaptation_set)); adaptation_sets.push_back(adaptation_set_ptr);
return adaptation_sets_.back().get(); adaptation_set_map_[adaptation_set_ptr->id()] = std::move(new_adaptation_set);
return adaptation_set_ptr;
} }
xml::scoped_xml_ptr<xmlNode> Period::GetXml() { xml::scoped_xml_ptr<xmlNode> Period::GetXml() {
@ -99,8 +100,8 @@ xml::scoped_xml_ptr<xmlNode> Period::GetXml() {
// Required for 'dynamic' MPDs. // Required for 'dynamic' MPDs.
period.SetId(id_); period.SetId(id_);
// Iterate thru AdaptationSets and add them to one big Period element. // Iterate thru AdaptationSets and add them to one big Period element.
for (const auto& adaptation_set : adaptation_sets_) { for (const auto& adaptation_set_pair : adaptation_set_map_) {
xml::scoped_xml_ptr<xmlNode> child(adaptation_set->GetXml()); xml::scoped_xml_ptr<xmlNode> child(adaptation_set_pair.second->GetXml());
if (!child || !period.AddChild(std::move(child))) if (!child || !period.AddChild(std::move(child)))
return nullptr; return nullptr;
} }
@ -115,9 +116,8 @@ xml::scoped_xml_ptr<xmlNode> Period::GetXml() {
const std::list<AdaptationSet*> Period::GetAdaptationSets() const { const std::list<AdaptationSet*> Period::GetAdaptationSets() const {
std::list<AdaptationSet*> adaptation_sets; std::list<AdaptationSet*> adaptation_sets;
for (const std::unique_ptr<AdaptationSet>& adaptation_set : for (const auto& adaptation_set_pair : adaptation_set_map_) {
adaptation_sets_) { adaptation_sets.push_back(adaptation_set_pair.second.get());
adaptation_sets.push_back(adaptation_set.get());
} }
return adaptation_sets; return adaptation_sets;
} }

View File

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

View File

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