From 6ffa344553a5adde1505a448c3c6c27a42bd68fd Mon Sep 17 00:00:00 2001 From: KongQun Yang Date: Thu, 25 Jan 2018 15:04:59 -0800 Subject: [PATCH] Moves AdaptationSet@id management out of Period class It is now managed in SimpleMpdNotifier. This avoids unnecessary increment in AdaptationSet id counter. Also makes sure the AdaptationSet is sorted by id in XML output. Change-Id: Ibcd0b047a71c19cd30ad7d8af9a2ed0bb05e043e --- .../encryption-and-ad-cues/output.mpd | 4 +- .../output.mpd | 18 +-- packager/mpd/base/adaptation_set.cc | 38 ++--- packager/mpd/base/adaptation_set.h | 39 +++--- packager/mpd/base/adaptation_set_unittest.cc | 103 +++++++------- packager/mpd/base/mock_mpd_builder.cc | 8 +- packager/mpd/base/mock_mpd_builder.h | 9 +- packager/mpd/base/mpd_builder.cc | 6 +- packager/mpd/base/mpd_builder.h | 1 - packager/mpd/base/period.cc | 70 +++++----- packager/mpd/base/period.h | 29 ++-- packager/mpd/base/period_unittest.cc | 132 +++++++++--------- packager/mpd/base/simple_mpd_notifier.cc | 89 ++++++------ packager/mpd/base/simple_mpd_notifier.h | 12 +- .../mpd/base/simple_mpd_notifier_unittest.cc | 12 +- ..._video_media_info1_expected_mpd_output.txt | 4 +- .../video_media_info1_expected_mpd_output.txt | 2 +- ...eo_media_info1and2_expected_mpd_output.txt | 2 +- 18 files changed, 288 insertions(+), 290 deletions(-) diff --git a/packager/app/test/testdata/encryption-and-ad-cues/output.mpd b/packager/app/test/testdata/encryption-and-ad-cues/output.mpd index 2a0ee722f5..cef6ca18fd 100644 --- a/packager/app/test/testdata/encryption-and-ad-cues/output.mpd +++ b/packager/app/test/testdata/encryption-and-ad-cues/output.mpd @@ -14,7 +14,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== @@ -41,7 +41,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/live-static-profile-and-ad-cues/output.mpd b/packager/app/test/testdata/live-static-profile-and-ad-cues/output.mpd index 6399903550..378814600e 100644 --- a/packager/app/test/testdata/live-static-profile-and-ad-cues/output.mpd +++ b/packager/app/test/testdata/live-static-profile-and-ad-cues/output.mpd @@ -23,6 +23,15 @@ + + + + + + + + + @@ -33,14 +42,5 @@ - - - - - - - - - diff --git a/packager/mpd/base/adaptation_set.cc b/packager/mpd/base/adaptation_set.cc index b4f755c228..38173e86a3 100644 --- a/packager/mpd/base/adaptation_set.cc +++ b/packager/mpd/base/adaptation_set.cc @@ -167,12 +167,10 @@ class RepresentationStateChangeListenerImpl } // namespace -AdaptationSet::AdaptationSet(uint32_t adaptation_set_id, - const std::string& lang, +AdaptationSet::AdaptationSet(const std::string& lang, const MpdOptions& mpd_options, base::AtomicSequenceNumber* counter) : representation_counter_(counter), - id_(adaptation_set_id), lang_(lang), mpd_options_(mpd_options), segments_aligned_(kSegmentAlignmentUnknown), @@ -245,7 +243,8 @@ xml::scoped_xml_ptr AdaptationSet::GetXml() { bool suppress_representation_height = false; bool suppress_representation_frame_rate = false; - adaptation_set.SetId(id_); + if (id_) + adaptation_set.SetId(id_.value()); adaptation_set.SetStringAttribute("contentType", content_type_); if (!lang_.empty() && lang_ != "und") { adaptation_set.SetStringAttribute("lang", LanguageToShortestForm(lang_)); @@ -296,22 +295,24 @@ xml::scoped_xml_ptr AdaptationSet::GetXml() { return xml::scoped_xml_ptr(); } - if (!trick_play_reference_ids_.empty()) { - std::string id_string; - for (uint32_t id : trick_play_reference_ids_) { - id_string += std::to_string(id) + ","; - } - DCHECK(!id_string.empty()); - id_string.resize(id_string.size() - 1); + std::string trick_play_reference_ids; + for (const AdaptationSet* adaptation_set : trick_play_references_) { + if (!trick_play_reference_ids.empty()) + trick_play_reference_ids += ','; + CHECK(adaptation_set->has_id()); + trick_play_reference_ids += std::to_string(adaptation_set->id()); + } + if (!trick_play_reference_ids.empty()) { adaptation_set.AddEssentialProperty( - "http://dashif.org/guidelines/trickmode", id_string); + "http://dashif.org/guidelines/trickmode", trick_play_reference_ids); } std::string switching_ids; - for (uint32_t id : adaptation_set_switching_ids_) { + for (const AdaptationSet* adaptation_set : switchable_adaptation_sets_) { if (!switching_ids.empty()) switching_ids += ','; - switching_ids += base::UintToString(id); + CHECK(adaptation_set->has_id()); + switching_ids += std::to_string(adaptation_set->id()); } if (!switching_ids.empty()) { adaptation_set.AddSupplementalProperty( @@ -343,8 +344,9 @@ void AdaptationSet::ForceSetSegmentAlignment(bool segment_alignment) { force_set_segment_alignment_ = true; } -void AdaptationSet::AddAdaptationSetSwitching(uint32_t adaptation_set_id) { - adaptation_set_switching_ids_.push_back(adaptation_set_id); +void AdaptationSet::AddAdaptationSetSwitching( + const AdaptationSet* adaptation_set) { + switchable_adaptation_sets_.push_back(adaptation_set); } // Check segmentAlignment for Live here. Storing all start_time and duration @@ -371,8 +373,8 @@ void AdaptationSet::OnSetFrameRateForRepresentation(uint32_t representation_id, RecordFrameRate(frame_duration, timescale); } -void AdaptationSet::AddTrickPlayReferenceId(uint32_t id) { - trick_play_reference_ids_.insert(id); +void AdaptationSet::AddTrickPlayReference(const AdaptationSet* adaptation_set) { + trick_play_references_.push_back(adaptation_set); } const std::list AdaptationSet::GetRepresentations() const { diff --git a/packager/mpd/base/adaptation_set.h b/packager/mpd/base/adaptation_set.h index 39d9e9c8e3..cfb6ac2fa4 100644 --- a/packager/mpd/base/adaptation_set.h +++ b/packager/mpd/base/adaptation_set.h @@ -18,6 +18,7 @@ #include #include "packager/base/atomic_sequence_num.h" +#include "packager/base/optional.h" #include "packager/mpd/base/xml/scoped_xml_ptr.h" namespace shaka { @@ -113,12 +114,15 @@ class AdaptationSet { /// attribute. virtual void ForceSetSegmentAlignment(bool segment_alignment); - /// Adds the id of the adaptation set this adaptation set can switch to. - /// @param adaptation_set_id is the id of the switchable adaptation set. - virtual void AddAdaptationSetSwitching(uint32_t adaptation_set_id); + /// Adds the adaptation set this adaptation set can switch to. + /// @param adaptation_set points to the switchable adaptation set. + virtual void AddAdaptationSetSwitching(const AdaptationSet* adaptation_set); + + /// @return true if id is set, false otherwise. + bool has_id() const { return static_cast(id_); } // Must be unique in the Period. - uint32_t id() const { return id_; } + uint32_t id() const { return id_.value(); } /// Set AdaptationSet@id. /// @param id is the new ID to be set. @@ -155,10 +159,9 @@ class AdaptationSet { uint32_t frame_duration, uint32_t timescale); - /// Add the id of the adaptation set this trick play adaptation set belongs - /// to. - /// @param id the id of the reference (or main) adapation set. - virtual void AddTrickPlayReferenceId(uint32_t id); + /// Add the adaptation set this trick play adaptation set belongs to. + /// @param adaptation_set points to the reference (or main) adapation set. + virtual void AddTrickPlayReference(const AdaptationSet* adaptation_set); // Return the list of Representations in this AdaptationSet. const std::list GetRepresentations() const; @@ -167,15 +170,13 @@ class AdaptationSet { bool IsVideo() const; protected: - /// @param adaptation_set_id is an ID number for this AdaptationSet. /// @param lang is the language of this AdaptationSet. Mainly relevant for /// audio. /// @param mpd_options is the options for this MPD. /// @param mpd_type is the type of this MPD. /// @param representation_counter is a Counter for assigning ID numbers to /// Representation. It can not be NULL. - AdaptationSet(uint32_t adaptation_set_id, - const std::string& lang, + AdaptationSet(const std::string& lang, const MpdOptions& mpd_options, base::AtomicSequenceNumber* representation_counter); @@ -235,12 +236,12 @@ class AdaptationSet { base::AtomicSequenceNumber* const representation_counter_; - uint32_t id_; + base::Optional id_; const std::string lang_; const MpdOptions& mpd_options_; - // The ids of the adaptation sets this adaptation set can switch to. - std::vector adaptation_set_switching_ids_; + // An array of adaptation sets this adaptation set can switch to. + std::vector switchable_adaptation_sets_; // Video widths and heights of Representations. Note that this is a set; if // there is only 1 resolution, then @width & @height should be set, otherwise @@ -286,11 +287,11 @@ class AdaptationSet { // reasonable and may cause an out-of-memory problem. RepresentationTimeline representation_segment_start_times_; - // Record the reference id for the original adaptation sets the trick play - // stream belongs to. This is a set because the trick play streams may be for - // multiple AdaptationSets (e.g. SD and HD videos in different AdaptationSets - // can share the same trick play stream.) - std::set trick_play_reference_ids_; + // Record the original AdaptationSets the trick play stream belongs to. There + // can be more than one reference AdaptationSets as multiple streams e.g. SD + // and HD videos in different AdaptationSets can share the same trick play + // stream. + std::vector trick_play_references_; }; } // namespace shaka diff --git a/packager/mpd/base/adaptation_set_unittest.cc b/packager/mpd/base/adaptation_set_unittest.cc index d11d431158..7ef85c15f7 100644 --- a/packager/mpd/base/adaptation_set_unittest.cc +++ b/packager/mpd/base/adaptation_set_unittest.cc @@ -21,16 +21,14 @@ using ::testing::Not; namespace shaka { namespace { -const uint32_t kAnyAdaptationSetId = 1; const char kNoLanguage[] = ""; } // namespace class AdaptationSetTest : public ::testing::Test { public: - std::unique_ptr CreateAdaptationSet(uint32_t adaptation_set_id, - const std::string& lang) { - return std::unique_ptr(new AdaptationSet( - adaptation_set_id, lang, mpd_options_, &representation_counter_)); + std::unique_ptr CreateAdaptationSet(const std::string& lang) { + return std::unique_ptr( + new AdaptationSet(lang, mpd_options_, &representation_counter_)); } protected: @@ -49,15 +47,24 @@ class LiveAdaptationSetTest : public AdaptationSetTest { }; TEST_F(AdaptationSetTest, AddAdaptationSetSwitching) { - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); - adaptation_set->AddAdaptationSetSwitching(1); - adaptation_set->AddAdaptationSetSwitching(2); - adaptation_set->AddAdaptationSetSwitching(8); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); + + auto adaptation_set_1 = CreateAdaptationSet(kNoLanguage); + adaptation_set_1->set_id(1); + adaptation_set->AddAdaptationSetSwitching(adaptation_set_1.get()); + + auto adaptation_set_2 = CreateAdaptationSet(kNoLanguage); + adaptation_set_2->set_id(2); + adaptation_set->AddAdaptationSetSwitching(adaptation_set_2.get()); + + auto adaptation_set_8 = CreateAdaptationSet(kNoLanguage); + adaptation_set_8->set_id(8); + adaptation_set->AddAdaptationSetSwitching(adaptation_set_8.get()); // The empty contentType is sort of a side effect of being able to generate an // MPD without adding any Representations. const char kExpectedOutput[] = - "" + "" " " @@ -80,7 +87,7 @@ TEST_F(AdaptationSetTest, CheckAdaptationSetVideoContentType) { "}\n" "container_type: CONTAINER_MP4\n"; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); adaptation_set->AddRepresentation(ConvertToMediaInfo(kVideoMediaInfo)); EXPECT_THAT(adaptation_set->GetXml().get(), AttributeEqual("contentType", "video")); @@ -98,7 +105,7 @@ TEST_F(AdaptationSetTest, CheckAdaptationSetAudioContentType) { "}\n" "container_type: CONTAINER_MP4\n"; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); adaptation_set->AddRepresentation(ConvertToMediaInfo(kAudioMediaInfo)); EXPECT_THAT(adaptation_set->GetXml().get(), AttributeEqual("contentType", "audio")); @@ -114,7 +121,7 @@ TEST_F(AdaptationSetTest, CheckAdaptationSetTextContentType) { "}\n" "container_type: CONTAINER_TEXT\n"; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, "en"); + auto adaptation_set = CreateAdaptationSet("en"); adaptation_set->AddRepresentation(ConvertToMediaInfo(kTextMediaInfo)); EXPECT_THAT(adaptation_set->GetXml().get(), AttributeEqual("contentType", "text")); @@ -133,7 +140,7 @@ TEST_F(AdaptationSetTest, CopyRepresentation) { "}\n" "container_type: CONTAINER_MP4\n"; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); Representation* representation = adaptation_set->AddRepresentation(ConvertToMediaInfo(kVideoMediaInfo)); @@ -144,7 +151,7 @@ TEST_F(AdaptationSetTest, CopyRepresentation) { // Verify that language passed to the constructor sets the @lang field is set. TEST_F(AdaptationSetTest, CheckLanguageAttributeSet) { - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, "en"); + auto adaptation_set = CreateAdaptationSet("en"); EXPECT_THAT(adaptation_set->GetXml().get(), AttributeEqual("lang", "en")); } @@ -152,26 +159,27 @@ TEST_F(AdaptationSetTest, CheckLanguageAttributeSet) { TEST_F(AdaptationSetTest, CheckConvertLanguageWithSubtag) { // "por-BR" is the long tag for Brazillian Portuguese. The short tag // is "pt-BR", which is what should appear in the manifest. - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, "por-BR"); + auto adaptation_set = CreateAdaptationSet("por-BR"); EXPECT_THAT(adaptation_set->GetXml().get(), AttributeEqual("lang", "pt-BR")); } TEST_F(AdaptationSetTest, CheckAdaptationSetId) { + auto adaptation_set = CreateAdaptationSet(kNoLanguage); const uint32_t kAdaptationSetId = 42; - auto adaptation_set = CreateAdaptationSet(kAdaptationSetId, kNoLanguage); + adaptation_set->set_id(kAdaptationSetId); EXPECT_THAT(adaptation_set->GetXml().get(), AttributeEqual("id", std::to_string(kAdaptationSetId))); } // Verify AdaptationSet::AddRole() works for "main" role. TEST_F(AdaptationSetTest, AdaptationAddRoleElementMain) { - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); adaptation_set->AddRole(AdaptationSet::kRoleMain); // The empty contentType is sort of a side effect of being able to generate an // MPD without adding any Representations. const char kExpectedOutput[] = - "\n" + "\n" " \n" ""; EXPECT_THAT(adaptation_set->GetXml().get(), XmlNodeEqual(kExpectedOutput)); @@ -180,7 +188,7 @@ TEST_F(AdaptationSetTest, AdaptationAddRoleElementMain) { // Add Role, ContentProtection, and Representation elements. Verify that // ContentProtection -> Role -> Representation are in order. TEST_F(AdaptationSetTest, CheckContentProtectionRoleRepresentationOrder) { - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); adaptation_set->AddRole(AdaptationSet::kRoleMain); ContentProtectionElement any_content_protection; any_content_protection.scheme_id_uri = "any_scheme"; @@ -197,7 +205,7 @@ TEST_F(AdaptationSetTest, CheckContentProtectionRoleRepresentationOrder) { xml::scoped_xml_ptr adaptation_set_xml(adaptation_set->GetXml()); const char kExpectedOutput[] = - "\n" + "\n" " \n" " \n" " AddRepresentation(ConvertToMediaInfo(kVideoMediaInfo1))); ASSERT_TRUE( @@ -265,7 +273,7 @@ TEST_F(AdaptationSetTest, AdapatationSetMaxFrameRate) { " frame_duration: 200\n" "}\n" "container_type: 1\n"; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); ASSERT_TRUE(adaptation_set->AddRepresentation( ConvertToMediaInfo(kVideoMediaInfo30fps))); ASSERT_TRUE(adaptation_set->AddRepresentation( @@ -307,7 +315,7 @@ TEST_F(AdaptationSetTest, "}\n" "container_type: 1\n"; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); Representation* representation_480p = adaptation_set->AddRepresentation(ConvertToMediaInfo(k480pMediaInfo)); Representation* representation_360p = @@ -391,7 +399,7 @@ TEST_F(AdaptationSetTest, AdaptationSetParAllSame) { "}\n" "container_type: 1\n"; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); ASSERT_TRUE( adaptation_set->AddRepresentation(ConvertToMediaInfo(k480pVideoInfo))); ASSERT_TRUE( @@ -432,7 +440,7 @@ TEST_F(AdaptationSetTest, AdaptationSetParDifferent) { "}\n" "container_type: 1\n"; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); ASSERT_TRUE( adaptation_set->AddRepresentation(ConvertToMediaInfo(k16by9VideoInfo))); ASSERT_TRUE( @@ -455,7 +463,7 @@ TEST_F(AdaptationSetTest, AdaptationSetParUnknown) { "}\n" "container_type: 1\n"; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); ASSERT_TRUE(adaptation_set->AddRepresentation( ConvertToMediaInfo(kUknownPixelWidthAndHeight))); @@ -487,7 +495,7 @@ TEST_F(AdaptationSetTest, AdapatationSetMaxFrameRateIntegerDivisionEdgeCase) { " frame_duration: 3\n" "}\n" "container_type: 1\n"; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); ASSERT_TRUE( adaptation_set->AddRepresentation(ConvertToMediaInfo(kVideoMediaInfo1))); ASSERT_TRUE( @@ -552,7 +560,7 @@ TEST_F(AdaptationSetTest, BubbleUpAttributesToAdaptationSet) { "}\n" "container_type: 1\n"; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); ASSERT_TRUE(adaptation_set->AddRepresentation(ConvertToMediaInfo(k1080p))); xml::scoped_xml_ptr all_attributes_on_adaptation_set( @@ -610,7 +618,7 @@ TEST_F(AdaptationSetTest, GetRepresentations) { "}\n" "container_type: 1\n"; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); Representation* representation1 = adaptation_set->AddRepresentation(ConvertToMediaInfo(kMediaInfo1)); @@ -622,8 +630,7 @@ TEST_F(AdaptationSetTest, GetRepresentations) { EXPECT_THAT(adaptation_set->GetRepresentations(), ElementsAre(representation1, representation2)); - auto new_adaptation_set = - CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto new_adaptation_set = CreateAdaptationSet(kNoLanguage); Representation* new_representation2 = new_adaptation_set->CopyRepresentation(*representation2); Representation* new_representation1 = @@ -668,7 +675,7 @@ TEST_F(OnDemandAdaptationSetTest, SubsegmentAlignment) { const uint64_t kDuration = 10u; const uint64_t kAnySize = 19834u; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); Representation* representation_480p = adaptation_set->AddRepresentation(ConvertToMediaInfo(k480pMediaInfo)); // Add a subsegment immediately before adding the 360p Representation. @@ -720,7 +727,7 @@ TEST_F(OnDemandAdaptationSetTest, ForceSetsubsegmentAlignment) { " pixel_height: 1\n" "}\n" "container_type: 1\n"; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); Representation* representation_480p = adaptation_set->AddRepresentation(ConvertToMediaInfo(k480pMediaInfo)); Representation* representation_360p = @@ -768,7 +775,7 @@ TEST_F(LiveAdaptationSetTest, SegmentAlignment) { " pixel_height: 1\n" "}\n" "container_type: 1\n"; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); Representation* representation_480p = adaptation_set->AddRepresentation(ConvertToMediaInfo(k480pMediaInfo)); Representation* representation_360p = @@ -815,7 +822,7 @@ TEST_F(OnDemandAdaptationSetTest, AdapatationSetWidthAndHeight) { " frame_duration: 200\n" "}\n" "container_type: 1\n"; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); ASSERT_TRUE( adaptation_set->AddRepresentation(ConvertToMediaInfo(kVideoMediaInfo1))); ASSERT_TRUE( @@ -849,7 +856,7 @@ TEST_F(OnDemandAdaptationSetTest, AdaptationSetMaxWidthAndMaxHeight) { " frame_duration: 100\n" "}\n" "container_type: 1\n"; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); ASSERT_TRUE(adaptation_set->AddRepresentation( ConvertToMediaInfo(kVideoMediaInfo1080p))); ASSERT_TRUE(adaptation_set->AddRepresentation( @@ -875,7 +882,7 @@ TEST_F(AdaptationSetTest, SetSampleDuration) { "}\n" "container_type: 1\n"; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); const MediaInfo video_media_info = ConvertToMediaInfo(kVideoMediaInfo); Representation* representation = @@ -911,13 +918,13 @@ TEST_F(AdaptationSetTest, AdaptationSetAddContentProtectionAndUpdate) { pssh.content = "any value"; content_protection.subelements.push_back(pssh); - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage); + auto adaptation_set = CreateAdaptationSet(kNoLanguage); ASSERT_TRUE(adaptation_set->AddRepresentation( ConvertToMediaInfo(kVideoMediaInfo1080p))); adaptation_set->AddContentProtectionElement(content_protection); const char kExpectedOutput1[] = - "" " UpdateContentProtectionPssh( "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed", "new pssh value"); const char kExpectedOutput2[] = - "" " AddRepresentation( ConvertToMediaInfo(kVideoMediaInfo1080p))); adaptation_set->AddContentProtectionElement(content_protection); const char kExpectedOutput1[] = - "" " UpdateContentProtectionPssh( "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed", "added pssh value"); const char kExpectedOutput2[] = - "" " " + "" " " " AddRepresentation(ConvertToMediaInfo(kTestMediaInfo)); ASSERT_TRUE(audio_representation); @@ -1076,7 +1083,7 @@ TEST_F(OnDemandAdaptationSetTest, Text) { "container_type: CONTAINER_TEXT\n"; const char kExpectedOutput[] = - "" + "" " \n" " " ""; - auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, "en"); + auto adaptation_set = CreateAdaptationSet("en"); Representation* text_representation = adaptation_set->AddRepresentation(ConvertToMediaInfo(kTextMediaInfo)); ASSERT_TRUE(text_representation); diff --git a/packager/mpd/base/mock_mpd_builder.cc b/packager/mpd/base/mock_mpd_builder.cc index bd5c6fe7af..5856c0199a 100644 --- a/packager/mpd/base/mock_mpd_builder.cc +++ b/packager/mpd/base/mock_mpd_builder.cc @@ -17,14 +17,10 @@ MockPeriod::MockPeriod(uint32_t period_id, double start_time_in_seconds) : Period(period_id, start_time_in_seconds, kDefaultMpdOptions, - &sequence_counter_, &sequence_counter_) {} -MockAdaptationSet::MockAdaptationSet(uint32_t adaptation_set_id) - : AdaptationSet(adaptation_set_id, - kEmptyLang, - kDefaultMpdOptions, - &sequence_counter_) {} +MockAdaptationSet::MockAdaptationSet() + : AdaptationSet(kEmptyLang, kDefaultMpdOptions, &sequence_counter_) {} MockAdaptationSet::~MockAdaptationSet() {} MockRepresentation::MockRepresentation(uint32_t representation_id) diff --git a/packager/mpd/base/mock_mpd_builder.h b/packager/mpd/base/mock_mpd_builder.h index d51d50d51d..720f622748 100644 --- a/packager/mpd/base/mock_mpd_builder.h +++ b/packager/mpd/base/mock_mpd_builder.h @@ -43,8 +43,7 @@ class MockPeriod : public Period { class MockAdaptationSet : public AdaptationSet { public: - // |adaptation_set_id| is the id for the AdaptationSet. - explicit MockAdaptationSet(uint32_t adaptation_set_id); + MockAdaptationSet(); ~MockAdaptationSet() override; MOCK_METHOD1(AddRepresentation, Representation*(const MediaInfo& media_info)); @@ -56,8 +55,10 @@ class MockAdaptationSet : public AdaptationSet { void(const std::string& drm_uuid, const std::string& pssh)); MOCK_METHOD1(AddRole, void(AdaptationSet::Role role)); MOCK_METHOD1(ForceSetSegmentAlignment, void(bool segment_alignment)); - MOCK_METHOD1(AddAdaptationSetSwitching, void(uint32_t adaptation_set_id)); - MOCK_METHOD1(AddTrickPlayReferenceId, void(uint32_t id)); + MOCK_METHOD1(AddAdaptationSetSwitching, + void(const AdaptationSet* adaptation_set)); + MOCK_METHOD1(AddTrickPlayReference, + void(const AdaptationSet* adaptation_set)); private: // Only for constructing the super class. Not used for testing. diff --git a/packager/mpd/base/mpd_builder.cc b/packager/mpd/base/mpd_builder.cc index b1fc1656d7..aceba15600 100644 --- a/packager/mpd/base/mpd_builder.cc +++ b/packager/mpd/base/mpd_builder.cc @@ -131,9 +131,9 @@ Period* MpdBuilder::GetOrCreatePeriod(double start_time_in_seconds) { if (match) return period.get(); } - periods_.emplace_back( - new Period(period_counter_.GetNext(), start_time_in_seconds, mpd_options_, - &adaptation_set_counter_, &representation_counter_)); + periods_.emplace_back(new Period(period_counter_.GetNext(), + start_time_in_seconds, mpd_options_, + &representation_counter_)); return periods_.back().get(); } diff --git a/packager/mpd/base/mpd_builder.h b/packager/mpd/base/mpd_builder.h index acbc0ce89c..6d3993bc79 100644 --- a/packager/mpd/base/mpd_builder.h +++ b/packager/mpd/base/mpd_builder.h @@ -120,7 +120,6 @@ class MpdBuilder { std::string availability_start_time_; base::AtomicSequenceNumber period_counter_; - base::AtomicSequenceNumber adaptation_set_counter_; base::AtomicSequenceNumber representation_counter_; // By default, this returns the current time. This can be injected for diff --git a/packager/mpd/base/period.cc b/packager/mpd/base/period.cc index df6882ff2f..51e2d548d5 100644 --- a/packager/mpd/base/period.cc +++ b/packager/mpd/base/period.cc @@ -37,12 +37,10 @@ std::set GetUUIDs( Period::Period(uint32_t period_id, double start_time_in_seconds, const MpdOptions& mpd_options, - base::AtomicSequenceNumber* adaptation_set_counter, base::AtomicSequenceNumber* representation_counter) : id_(period_id), start_time_in_seconds_(start_time_in_seconds), mpd_options_(mpd_options), - adaptation_set_counter_(adaptation_set_counter), representation_counter_(representation_counter) {} AdaptationSet* Period::GetOrCreateAdaptationSet( @@ -66,10 +64,9 @@ AdaptationSet* Period::GetOrCreateAdaptationSet( } // None of the adaptation sets match with the new content protection. // Need a new one. - std::string language = GetLanguage(media_info); + const std::string language = GetLanguage(media_info); std::unique_ptr new_adaptation_set = - NewAdaptationSet(adaptation_set_counter_->GetNext(), language, - mpd_options_, representation_counter_); + NewAdaptationSet(language, mpd_options_, representation_counter_); if (!SetNewAdaptationSetAttributes(language, media_info, adaptation_sets, new_adaptation_set.get())) { return nullptr; @@ -83,25 +80,35 @@ AdaptationSet* Period::GetOrCreateAdaptationSet( for (AdaptationSet* adaptation_set : adaptation_sets) { if (protected_adaptation_set_map_.Switchable(*adaptation_set, *new_adaptation_set)) { - adaptation_set->AddAdaptationSetSwitching(new_adaptation_set->id()); - new_adaptation_set->AddAdaptationSetSwitching(adaptation_set->id()); + adaptation_set->AddAdaptationSetSwitching(new_adaptation_set.get()); + new_adaptation_set->AddAdaptationSetSwitching(adaptation_set); } } } 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); + adaptation_sets_.emplace_back(std::move(new_adaptation_set)); return adaptation_set_ptr; } -xml::scoped_xml_ptr Period::GetXml(bool output_period_duration) const { +xml::scoped_xml_ptr Period::GetXml(bool output_period_duration) { + adaptation_sets_.sort( + [](const std::unique_ptr& adaptation_set_a, + const std::unique_ptr& adaptation_set_b) { + if (!adaptation_set_a->has_id()) + return false; + if (!adaptation_set_b->has_id()) + return true; + return adaptation_set_a->id() < adaptation_set_b->id(); + }); + xml::XmlNode period("Period"); // Required for 'dynamic' MPDs. period.SetId(id_); // Iterate thru AdaptationSets and add them to one big Period element. - for (const auto& adaptation_set_pair : adaptation_set_map_) { - xml::scoped_xml_ptr child(adaptation_set_pair.second->GetXml()); + for (const auto& adaptation_set : adaptation_sets_) { + xml::scoped_xml_ptr child(adaptation_set->GetXml()); if (!child || !period.AddChild(std::move(child))) return nullptr; } @@ -118,19 +125,18 @@ xml::scoped_xml_ptr Period::GetXml(bool output_period_duration) const { const std::list Period::GetAdaptationSets() const { std::list adaptation_sets; - for (const auto& adaptation_set_pair : adaptation_set_map_) { - adaptation_sets.push_back(adaptation_set_pair.second.get()); + for (const auto& adaptation_set : adaptation_sets_) { + adaptation_sets.push_back(adaptation_set.get()); } return adaptation_sets; } std::unique_ptr Period::NewAdaptationSet( - uint32_t adaptation_set_id, const std::string& language, const MpdOptions& options, base::AtomicSequenceNumber* representation_counter) { - return std::unique_ptr(new AdaptationSet( - adaptation_set_id, language, options, representation_counter)); + return std::unique_ptr( + new AdaptationSet(language, options, representation_counter)); } bool Period::SetNewAdaptationSetAttributes( @@ -152,14 +158,14 @@ bool Period::SetNewAdaptationSetAttributes( } if (media_info.video_info().has_playback_rate()) { - uint32_t trick_play_reference_id = 0; - if (!FindOriginalAdaptationSetForTrickPlay(media_info, - &trick_play_reference_id)) { - LOG(ERROR) << "Failed to find main adaptation set for trick play."; + const AdaptationSet* trick_play_reference_adaptation_set = + FindOriginalAdaptationSetForTrickPlay(media_info); + if (!trick_play_reference_adaptation_set) { + LOG(ERROR) << "Failed to find original AdaptationSet for trick play."; return false; } - DCHECK_NE(new_adaptation_set->id(), trick_play_reference_id); - new_adaptation_set->AddTrickPlayReferenceId(trick_play_reference_id); + new_adaptation_set->AddTrickPlayReference( + trick_play_reference_adaptation_set); } } else if (media_info.has_text_info()) { // IOP requires all AdaptationSets to have (sub)segmentAlignment set to @@ -170,9 +176,8 @@ bool Period::SetNewAdaptationSetAttributes( return true; } -bool Period::FindOriginalAdaptationSetForTrickPlay( - const MediaInfo& media_info, - uint32_t* main_adaptation_set_id) { +const AdaptationSet* Period::FindOriginalAdaptationSetForTrickPlay( + const MediaInfo& media_info) { MediaInfo media_info_no_trickplay = media_info; media_info_no_trickplay.mutable_video_info()->clear_playback_rate(); @@ -181,25 +186,24 @@ bool Period::FindOriginalAdaptationSetForTrickPlay( adaptation_set_list_map_[key]; for (AdaptationSet* adaptation_set : adaptation_sets) { if (protected_adaptation_set_map_.Match(*adaptation_set, media_info)) { - *main_adaptation_set_id = adaptation_set->id(); - return true; + return adaptation_set; } } - return false; + return nullptr; } void Period::ProtectedAdaptationSetMap::Register( const AdaptationSet& adaptation_set, const MediaInfo& media_info) { - DCHECK(!ContainsKey(protected_content_map_, adaptation_set.id())); - protected_content_map_[adaptation_set.id()] = media_info.protected_content(); + DCHECK(!ContainsKey(protected_content_map_, &adaptation_set)); + protected_content_map_[&adaptation_set] = media_info.protected_content(); } bool Period::ProtectedAdaptationSetMap::Match( const AdaptationSet& adaptation_set, const MediaInfo& media_info) { const auto protected_content_it = - protected_content_map_.find(adaptation_set.id()); + protected_content_map_.find(&adaptation_set); // If the AdaptationSet ID is not registered in the map, then it is clear // content. if (protected_content_it == protected_content_map_.end()) @@ -214,9 +218,9 @@ bool Period::ProtectedAdaptationSetMap::Switchable( const AdaptationSet& adaptation_set_a, const AdaptationSet& adaptation_set_b) { const auto protected_content_it_a = - protected_content_map_.find(adaptation_set_a.id()); + protected_content_map_.find(&adaptation_set_a); const auto protected_content_it_b = - protected_content_map_.find(adaptation_set_b.id()); + protected_content_map_.find(&adaptation_set_b); if (protected_content_it_a == protected_content_map_.end()) return protected_content_it_b == protected_content_map_.end(); diff --git a/packager/mpd/base/period.h b/packager/mpd/base/period.h index 054113e0b1..e4955d3578 100644 --- a/packager/mpd/base/period.h +++ b/packager/mpd/base/period.h @@ -13,6 +13,7 @@ #include #include "packager/base/atomic_sequence_num.h" +#include "packager/base/optional.h" #include "packager/mpd/base/adaptation_set.h" #include "packager/mpd/base/media_info.pb.h" #include "packager/mpd/base/xml/scoped_xml_ptr.h" @@ -41,6 +42,8 @@ class Period { /// element. This affects how MediaInfo in AdaptationSets are matched. /// @return the AdaptationSet matching @a media_info if found; otherwise /// return a new AdaptationSet. + // TODO(kqyang): Move |content_protection_in_adaptation_set| to Period + // constructor. virtual AdaptationSet* GetOrCreateAdaptationSet( const MediaInfo& media_info, bool content_protection_in_adaptation_set); @@ -48,7 +51,7 @@ class Period { /// Generates xml element with its child AdaptationSet elements. /// @return On success returns a non-NULL scoped_xml_ptr. Otherwise returns a /// NULL scoped_xml_ptr. - xml::scoped_xml_ptr GetXml(bool output_period_duration) const; + xml::scoped_xml_ptr GetXml(bool output_period_duration); /// @return The list of AdaptationSets in this Period. const std::list GetAdaptationSets() const; @@ -68,14 +71,11 @@ class Period { /// @param period_id is an ID number for this Period. /// @param start_time_in_seconds is the start time for this Period. /// @param mpd_options is the options for this MPD. - /// @param adaptation_set_counter is a counter for assigning ID numbers to - /// AdaptationSet. It can not be NULL. /// @param representation_counter is a counter for assigning ID numbers to /// Representation. It can not be NULL. Period(uint32_t period_id, double start_time_in_seconds, const MpdOptions& mpd_options, - base::AtomicSequenceNumber* adaptation_set_counter, base::AtomicSequenceNumber* representation_counter); private: @@ -87,7 +87,6 @@ class Period { // Calls AdaptationSet constructor. For mock injection. virtual std::unique_ptr NewAdaptationSet( - uint32_t adaptation_set_id, const std::string& lang, const MpdOptions& options, base::AtomicSequenceNumber* representation_counter); @@ -99,24 +98,19 @@ class Period { const std::list& adaptation_sets, AdaptationSet* new_adaptation_set); - // Gets the original AdaptationSet which the trick play video belongs - // to and returns the id of the original adapatation set. + // Gets the original AdaptationSet which the trick play video belongs to. // It is assumed that the corresponding AdaptationSet has been created before // the trick play AdaptationSet. - // Returns true if main_adaptation_id is found, otherwise false; - bool FindOriginalAdaptationSetForTrickPlay( - const MediaInfo& media_info, - uint32_t* original_adaptation_set_id); + // Returns the original AdaptationSet if found, otherwise returns nullptr; + const AdaptationSet* FindOriginalAdaptationSetForTrickPlay( + const MediaInfo& media_info); const uint32_t id_; const double start_time_in_seconds_; double duration_seconds_ = 0; const MpdOptions& mpd_options_; - base::AtomicSequenceNumber* const adaptation_set_counter_; base::AtomicSequenceNumber* const representation_counter_; - // adaptation_id => Adaptation map. It also keeps the adaptation_sets_ sorted - // by default. - std::map> adaptation_set_map_; + std::list> adaptation_sets_; // 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 @@ -144,8 +138,9 @@ class Period { ProtectedAdaptationSetMap& operator=(const ProtectedAdaptationSetMap&) = delete; - // AdaptationSet id => ProtectedContent map. - std::map protected_content_map_; + // AdaptationSet => ProtectedContent map. + std::map + protected_content_map_; }; ProtectedAdaptationSetMap protected_adaptation_set_map_; }; diff --git a/packager/mpd/base/period_unittest.cc b/packager/mpd/base/period_unittest.cc index 678cee0b1c..7e94befb54 100644 --- a/packager/mpd/base/period_unittest.cc +++ b/packager/mpd/base/period_unittest.cc @@ -26,8 +26,6 @@ namespace shaka { namespace { const uint32_t kDefaultPeriodId = 9u; const double kDefaultPeriodStartTime = 5.6; -const uint32_t kDefaultAdaptationSetId = 0u; -const uint32_t kTrickPlayAdaptationSetId = 1u; const bool kOutputPeriodDuration = true; bool ElementEqual(const Element& lhs, const Element& rhs) { @@ -80,12 +78,10 @@ class TestablePeriod : public Period { : Period(kDefaultPeriodId, kDefaultPeriodStartTime, mpd_options, - &sequence_number_, &sequence_number_) {} - MOCK_METHOD4(NewAdaptationSet, + MOCK_METHOD3(NewAdaptationSet, std::unique_ptr( - uint32_t adaptation_set_id, const std::string& lang, const MpdOptions& options, base::AtomicSequenceNumber* representation_counter)); @@ -101,8 +97,7 @@ class PeriodTest : public ::testing::TestWithParam { public: PeriodTest() : testable_period_(mpd_options_), - default_adaptation_set_( - new StrictMock(kDefaultAdaptationSetId)), + default_adaptation_set_(new StrictMock()), default_adaptation_set_ptr_(default_adaptation_set_.get()) {} void SetUp() override { content_protection_in_adaptation_set_ = GetParam(); } @@ -130,7 +125,7 @@ TEST_P(PeriodTest, GetXml) { "}\n" "container_type: 1\n"; - EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _)) + EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _)) .WillOnce(Return(ByMove(std::move(default_adaptation_set_)))); ASSERT_EQ(default_adaptation_set_ptr_, @@ -142,7 +137,7 @@ TEST_P(PeriodTest, GetXml) { "" // ContentType and Representation elements are populated after // Representation::Init() is called. - " " + " " ""; EXPECT_THAT(testable_period_.GetXml(!kOutputPeriodDuration).get(), XmlNodeEqual(kExpectedXml)); @@ -162,7 +157,7 @@ TEST_P(PeriodTest, DynamicMpdGetXml) { "container_type: 1\n"; mpd_options_.mpd_type = MpdType::kDynamic; - EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _)) + EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _)) .WillOnce(Return(ByMove(std::move(default_adaptation_set_)))); ASSERT_EQ(default_adaptation_set_ptr_, @@ -174,7 +169,7 @@ TEST_P(PeriodTest, DynamicMpdGetXml) { "" // ContentType and Representation elements are populated after // Representation::Init() is called. - " " + " " ""; EXPECT_THAT(testable_period_.GetXml(!kOutputPeriodDuration).get(), XmlNodeEqual(kExpectedXml)); @@ -193,7 +188,7 @@ TEST_P(PeriodTest, SetDurationAndGetXml) { "}\n" "container_type: 1\n"; - EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _)) + EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _)) .WillOnce(Return(ByMove(std::move(default_adaptation_set_)))); ASSERT_EQ(default_adaptation_set_ptr_, @@ -207,7 +202,7 @@ TEST_P(PeriodTest, SetDurationAndGetXml) { "" // ContentType and Representation elements are populated after // Representation::Init() is called. - " " + " " ""; EXPECT_THAT(testable_period_.GetXml(kOutputPeriodDuration).get(), XmlNodeEqual(kExpectedXml)); @@ -215,7 +210,7 @@ TEST_P(PeriodTest, SetDurationAndGetXml) { "" // ContentType and Representation elements are populated after // Representation::Init() is called. - " " + " " ""; EXPECT_THAT(testable_period_.GetXml(!kOutputPeriodDuration).get(), XmlNodeEqual(kExpectedXmlSuppressDuration)); @@ -230,7 +225,7 @@ TEST_P(PeriodTest, Text) { "}\n" "container_type: CONTAINER_TEXT\n"; - EXPECT_CALL(testable_period_, NewAdaptationSet(_, Eq("en"), _, _)) + EXPECT_CALL(testable_period_, NewAdaptationSet(Eq("en"), _, _)) .WillOnce(Return(ByMove(std::move(default_adaptation_set_)))); EXPECT_CALL(*default_adaptation_set_ptr_, ForceSetSegmentAlignment(true)); @@ -240,7 +235,6 @@ TEST_P(PeriodTest, Text) { content_protection_in_adaptation_set_)); } -// Verify AddTrickPlayReferenceId is called. TEST_P(PeriodTest, TrickPlayWithMatchingAdaptationSet) { const char kVideoMediaInfo[] = "video_info {\n" @@ -267,15 +261,15 @@ TEST_P(PeriodTest, TrickPlayWithMatchingAdaptationSet) { "container_type: 1\n"; std::unique_ptr> trick_play_adaptation_set( - new StrictMock(kTrickPlayAdaptationSetId)); + new StrictMock()); auto* trick_play_adaptation_set_ptr = trick_play_adaptation_set.get(); - EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _)) + EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _)) .WillOnce(Return(ByMove(std::move(default_adaptation_set_)))) .WillOnce(Return(ByMove(std::move(trick_play_adaptation_set)))); EXPECT_CALL(*trick_play_adaptation_set_ptr, - AddTrickPlayReferenceId(Eq(kDefaultAdaptationSetId))); + AddTrickPlayReference(Eq(default_adaptation_set_ptr_))); ASSERT_EQ(default_adaptation_set_ptr_, testable_period_.GetOrCreateAdaptationSet( @@ -314,9 +308,9 @@ TEST_P(PeriodTest, TrickPlayWithNoMatchingAdaptationSet) { "container_type: 1\n"; std::unique_ptr> trick_play_adaptation_set( - new StrictMock(kTrickPlayAdaptationSetId)); + new StrictMock()); - EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _)) + EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _)) .WillOnce(Return(ByMove(std::move(default_adaptation_set_)))) .WillOnce(Return(ByMove(std::move(trick_play_adaptation_set)))); @@ -396,18 +390,16 @@ TEST_P(PeriodTest, DifferentProtectedContent) { // Not using default mocks in this test so that we can keep track of // mocks by named mocks. - const uint32_t kSdAdaptationSetId = 2u; - const uint32_t kHdAdaptationSetId = 3u; std::unique_ptr> sd_adaptation_set( - new StrictMock(kSdAdaptationSetId)); + new StrictMock()); auto* sd_adaptation_set_ptr = sd_adaptation_set.get(); std::unique_ptr> hd_adaptation_set( - new StrictMock(kHdAdaptationSetId)); + new StrictMock()); auto* hd_adaptation_set_ptr = hd_adaptation_set.get(); InSequence in_sequence; - EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _)) + EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _)) .WillOnce(Return(ByMove(std::move(sd_adaptation_set)))); if (content_protection_in_adaptation_set_) { @@ -418,7 +410,7 @@ TEST_P(PeriodTest, DifferentProtectedContent) { *sd_adaptation_set_ptr, AddContentProtectionElement(ContentProtectionElementEq(sd_my_drm))); - EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _)) + EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _)) .WillOnce(Return(ByMove(std::move(hd_adaptation_set)))); // Add main Role here for both. @@ -502,7 +494,7 @@ TEST_P(PeriodTest, SameProtectedContent) { InSequence in_sequence; // Only called once. - EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _)) + EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _)) .WillOnce(Return(ByMove(std::move(default_adaptation_set_)))); if (content_protection_in_adaptation_set_) { @@ -579,22 +571,24 @@ TEST_P(PeriodTest, SetAdaptationSetSwitching) { const uint32_t kSdAdaptationSetId = 6u; const uint32_t kHdAdaptationSetId = 7u; std::unique_ptr> sd_adaptation_set( - new StrictMock(kSdAdaptationSetId)); + new StrictMock()); auto* sd_adaptation_set_ptr = sd_adaptation_set.get(); + sd_adaptation_set_ptr->set_id(kSdAdaptationSetId); std::unique_ptr> hd_adaptation_set( - new StrictMock(kHdAdaptationSetId)); + new StrictMock()); auto* hd_adaptation_set_ptr = hd_adaptation_set.get(); + hd_adaptation_set_ptr->set_id(kHdAdaptationSetId); InSequence in_sequence; - EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _)) + EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _)) .WillOnce(Return(ByMove(std::move(sd_adaptation_set)))); if (content_protection_in_adaptation_set_) { EXPECT_CALL(*sd_adaptation_set_ptr, AddContentProtectionElement(_)) .Times(2); - EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _)) + EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _)) .WillOnce(Return(ByMove(std::move(hd_adaptation_set)))); // Add main Role here for both. @@ -605,9 +599,9 @@ TEST_P(PeriodTest, SetAdaptationSetSwitching) { .Times(2); EXPECT_CALL(*sd_adaptation_set_ptr, - AddAdaptationSetSwitching(kHdAdaptationSetId)); + AddAdaptationSetSwitching(hd_adaptation_set_ptr)); EXPECT_CALL(*hd_adaptation_set_ptr, - AddAdaptationSetSwitching(kSdAdaptationSetId)); + AddAdaptationSetSwitching(sd_adaptation_set_ptr)); } ASSERT_EQ(sd_adaptation_set_ptr, testable_period_.GetOrCreateAdaptationSet( @@ -643,11 +637,12 @@ TEST_P(PeriodTest, SetAdaptationSetSwitching) { const uint32_t k4kAdaptationSetId = 4000u; std::unique_ptr> fourk_adaptation_set( - new StrictMock(k4kAdaptationSetId)); + new StrictMock()); auto* fourk_adaptation_set_ptr = fourk_adaptation_set.get(); + fourk_adaptation_set_ptr->set_id(k4kAdaptationSetId); if (content_protection_in_adaptation_set_) { - EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _)) + EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _)) .WillOnce(Return(ByMove(std::move(fourk_adaptation_set)))); EXPECT_CALL(*fourk_adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain)); @@ -655,13 +650,13 @@ TEST_P(PeriodTest, SetAdaptationSetSwitching) { .Times(2); EXPECT_CALL(*sd_adaptation_set_ptr, - AddAdaptationSetSwitching(k4kAdaptationSetId)); + AddAdaptationSetSwitching(fourk_adaptation_set_ptr)); EXPECT_CALL(*fourk_adaptation_set_ptr, - AddAdaptationSetSwitching(kSdAdaptationSetId)); + AddAdaptationSetSwitching(sd_adaptation_set_ptr)); EXPECT_CALL(*hd_adaptation_set_ptr, - AddAdaptationSetSwitching(k4kAdaptationSetId)); + AddAdaptationSetSwitching(fourk_adaptation_set_ptr)); EXPECT_CALL(*fourk_adaptation_set_ptr, - AddAdaptationSetSwitching(kHdAdaptationSetId)); + AddAdaptationSetSwitching(hd_adaptation_set_ptr)); } ASSERT_EQ(content_protection_in_adaptation_set_ ? fourk_adaptation_set_ptr @@ -716,22 +711,24 @@ TEST_P(PeriodTest, DoNotSetAdaptationSetSwitchingIfContentTypesDifferent) { const uint32_t kVideoAdaptationSetId = 6u; const uint32_t kAudioAdaptationSetId = 7u; std::unique_ptr> video_adaptation_set( - new StrictMock(kVideoAdaptationSetId)); + new StrictMock()); auto* video_adaptation_set_ptr = video_adaptation_set.get(); + video_adaptation_set_ptr->set_id(kVideoAdaptationSetId); std::unique_ptr> audio_adaptation_set( - new StrictMock(kAudioAdaptationSetId)); + new StrictMock()); auto* audio_adaptation_set_ptr = audio_adaptation_set.get(); + audio_adaptation_set_ptr->set_id(kAudioAdaptationSetId); InSequence in_sequence; - EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _)) + EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _)) .WillOnce(Return(ByMove(std::move(video_adaptation_set)))); if (content_protection_in_adaptation_set_) { EXPECT_CALL(*video_adaptation_set_ptr, AddContentProtectionElement(_)) .Times(2); } - EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _)) + EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _)) .WillOnce(Return(ByMove(std::move(audio_adaptation_set)))); if (content_protection_in_adaptation_set_) { EXPECT_CALL(*audio_adaptation_set_ptr, AddContentProtectionElement(_)) @@ -796,17 +793,17 @@ TEST_P(PeriodTest, SplitAdaptationSetsByLanguageAndCodec) { "media_duration_seconds: 10.5\n"; std::unique_ptr> aac_eng_adaptation_set( - new StrictMock(1)); + new StrictMock()); auto* aac_eng_adaptation_set_ptr = aac_eng_adaptation_set.get(); std::unique_ptr> aac_ger_adaptation_set( - new StrictMock(2)); + new StrictMock()); auto* aac_ger_adaptation_set_ptr = aac_ger_adaptation_set.get(); std::unique_ptr> vorbis_german_adaptation_set( - new StrictMock(3)); + new StrictMock()); auto* vorbis_german_adaptation_set_ptr = vorbis_german_adaptation_set.get(); // We expect three AdaptationSets. - EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _)) + 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(vorbis_german_adaptation_set)))); @@ -855,13 +852,13 @@ TEST_P(PeriodTest, GetAdaptationSets) { "media_duration_seconds: 10.5\n"; std::unique_ptr> adaptation_set_1( - new StrictMock(1)); + new StrictMock()); auto* adaptation_set_1_ptr = adaptation_set_1.get(); std::unique_ptr> adaptation_set_2( - new StrictMock(2)); + new StrictMock()); auto* adaptation_set_2_ptr = adaptation_set_2.get(); - EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _)) + EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _)) .WillOnce(Return(ByMove(std::move(adaptation_set_1)))) .WillOnce(Return(ByMove(std::move(adaptation_set_2)))); @@ -878,7 +875,7 @@ TEST_P(PeriodTest, GetAdaptationSets) { ElementsAre(adaptation_set_1_ptr, adaptation_set_2_ptr)); } -TEST_P(PeriodTest, GetAdaptationSetsOrderedByAdaptationSetId) { +TEST_P(PeriodTest, OrderedByAdaptationSetId) { const char kContent1[] = "audio_info {\n" " codec: 'mp4a.40.2'\n" @@ -903,25 +900,34 @@ TEST_P(PeriodTest, GetAdaptationSetsOrderedByAdaptationSetId) { "media_duration_seconds: 10.5\n"; std::unique_ptr> adaptation_set_1( - new StrictMock(1)); + new StrictMock()); auto* adaptation_set_1_ptr = adaptation_set_1.get(); std::unique_ptr> adaptation_set_2( - new StrictMock(2)); + new StrictMock()); 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)))); + EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _)) + .WillOnce(Return(ByMove(std::move(adaptation_set_1)))) + .WillOnce(Return(ByMove(std::move(adaptation_set_2)))); - 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)); + ASSERT_EQ(adaptation_set_2_ptr, testable_period_.GetOrCreateAdaptationSet( + ConvertToMediaInfo(kContent2), + content_protection_in_adaptation_set_)); + + adaptation_set_1_ptr->set_id(2); + adaptation_set_2_ptr->set_id(1); + const char kExpectedXml[] = + R"()" + // ContentType and Representation elements are populated after + // Representation::Init() is called. + R"( )" + R"( )" + R"()"; + EXPECT_THAT(testable_period_.GetXml(!kOutputPeriodDuration).get(), + XmlNodeEqual(kExpectedXml)); } INSTANTIATE_TEST_CASE_P(ContentProtectionInAdaptationSet, PeriodTest, diff --git a/packager/mpd/base/simple_mpd_notifier.cc b/packager/mpd/base/simple_mpd_notifier.cc index b15a6fd08b..b03d3d38d1 100644 --- a/packager/mpd/base/simple_mpd_notifier.cc +++ b/packager/mpd/base/simple_mpd_notifier.cc @@ -43,15 +43,31 @@ bool SimpleMpdNotifier::NotifyNewContainer(const MediaInfo& media_info, MediaInfo adjusted_media_info(media_info); MpdBuilder::MakePathsRelativeToMpd(output_path_, &adjusted_media_info); - const Representation* kNoOriginalRepresentation = nullptr; - const double kPeriodStartTimeSeconds = 0.0; base::AutoLock auto_lock(lock_); - const Representation* representation = AddRepresentationToPeriod( - adjusted_media_info, kNoOriginalRepresentation, kPeriodStartTimeSeconds); + const double kPeriodStartTimeSeconds = 0.0; + Period* period = mpd_builder_->GetOrCreatePeriod(kPeriodStartTimeSeconds); + DCHECK(period); + AdaptationSet* adaptation_set = period->GetOrCreateAdaptationSet( + media_info, content_protection_in_adaptation_set_); + DCHECK(adaptation_set); + if (!adaptation_set->has_id()) + adaptation_set->set_id(next_adaptation_set_id_++); + Representation* representation = + adaptation_set->AddRepresentation(adjusted_media_info); if (!representation) return false; + *container_id = representation->id(); + if (content_protection_in_adaptation_set_) { + // ContentProtection elements are already added to AdaptationSet above. + // Use RepresentationId to AdaptationSet map to update ContentProtection + // in AdaptationSet in NotifyEncryptionUpdate. + representation_id_to_adaptation_set_[representation->id()] = adaptation_set; + } else { + AddContentProtectionElements(media_info, representation); + } + representation_map_[representation->id()] = representation; return true; } @@ -96,16 +112,32 @@ bool SimpleMpdNotifier::NotifyCueEvent(uint32_t container_id, const MediaInfo& media_info = original_representation->GetMediaInfo(); const double period_start_time_seconds = static_cast(timestamp) / media_info.reference_time_scale(); - const Representation* new_representation = AddRepresentationToPeriod( - media_info, original_representation, period_start_time_seconds); - if (!new_representation) + + Period* period = mpd_builder_->GetOrCreatePeriod(period_start_time_seconds); + DCHECK(period); + AdaptationSet* adaptation_set = period->GetOrCreateAdaptationSet( + media_info, content_protection_in_adaptation_set_); + DCHECK(adaptation_set); + if (!adaptation_set->has_id()) { + adaptation_set->set_id(original_adaptation_set->id()); + } else { + DCHECK_EQ(adaptation_set->id(), original_adaptation_set->id()); + } + + Representation* representation = + adaptation_set->CopyRepresentation(*original_representation); + if (!representation) return false; - // TODO(kqyang): Pass the ID to GetOrCreateAdaptationSet instead? - AdaptationSet* new_adaptation_set = - representation_id_to_adaptation_set_[container_id]; - DCHECK(new_adaptation_set); - new_adaptation_set->set_id(original_adaptation_set->id()); + if (content_protection_in_adaptation_set_) { + // ContentProtection elements are already added to AdaptationSet above. + // Use RepresentationId to AdaptationSet map to update ContentProtection + // in AdaptationSet in NotifyEncryptionUpdate. + representation_id_to_adaptation_set_[representation->id()] = adaptation_set; + } else { + AddContentProtectionElements(media_info, representation); + } + representation_map_[representation->id()] = representation; return true; } @@ -138,37 +170,4 @@ bool SimpleMpdNotifier::Flush() { return WriteMpdToFile(output_path_, mpd_builder_.get()); } -Representation* SimpleMpdNotifier::AddRepresentationToPeriod( - const MediaInfo& media_info, - const Representation* original_representation, - double period_start_time_seconds) { - Period* period = mpd_builder_->GetOrCreatePeriod(period_start_time_seconds); - DCHECK(period); - - AdaptationSet* adaptation_set = period->GetOrCreateAdaptationSet( - media_info, content_protection_in_adaptation_set_); - DCHECK(adaptation_set); - - Representation* representation = nullptr; - if (original_representation) { - representation = - adaptation_set->CopyRepresentation(*original_representation); - } else { - representation = adaptation_set->AddRepresentation(media_info); - } - if (!representation) - return nullptr; - - if (content_protection_in_adaptation_set_) { - // ContentProtection elements are already added to AdaptationSet above. - // Use RepresentationId to AdaptationSet map to update ContentProtection - // in AdaptationSet in NotifyEncryptionUpdate. - representation_id_to_adaptation_set_[representation->id()] = adaptation_set; - } else { - AddContentProtectionElements(media_info, representation); - } - representation_map_[representation->id()] = representation; - return representation; -} - } // namespace shaka diff --git a/packager/mpd/base/simple_mpd_notifier.h b/packager/mpd/base/simple_mpd_notifier.h index a4b39ef285..0c10cfb377 100644 --- a/packager/mpd/base/simple_mpd_notifier.h +++ b/packager/mpd/base/simple_mpd_notifier.h @@ -56,17 +56,6 @@ class SimpleMpdNotifier : public MpdNotifier { friend class SimpleMpdNotifierTest; - // Add a new representation. If |original_representation| is not nullptr, the - // new Representation will clone from it; otherwise the new Representation is - // created from |media_info|. - // The new Representation will be added to Period with the specified start - // time. - // Returns the new Representation on success; otherwise a nullptr is returned. - Representation* AddRepresentationToPeriod( - const MediaInfo& media_info, - const Representation* original_representation, - double period_start_time_seconds); - // Testing only method. Returns a pointer to MpdBuilder. MpdBuilder* MpdBuilderForTesting() const { return mpd_builder_.get(); } @@ -81,6 +70,7 @@ class SimpleMpdNotifier : public MpdNotifier { bool content_protection_in_adaptation_set_ = true; base::Lock lock_; + uint32_t next_adaptation_set_id_ = 0; // Maps Representation ID to Representation. std::map representation_map_; // Maps Representation ID to AdaptationSet. This is for updating the PSSH. diff --git a/packager/mpd/base/simple_mpd_notifier_unittest.cc b/packager/mpd/base/simple_mpd_notifier_unittest.cc index a1724aa264..f9d3492867 100644 --- a/packager/mpd/base/simple_mpd_notifier_unittest.cc +++ b/packager/mpd/base/simple_mpd_notifier_unittest.cc @@ -28,7 +28,6 @@ using ::testing::StrEq; namespace { const uint32_t kDefaultPeriodId = 0u; const double kDefaultPeriodStartTime = 0.0; -const uint32_t kDefaultAdaptationSetId = 0u; const uint32_t kDefaultTimeScale = 10; const bool kContentProtectionInAdaptationSet = true; @@ -43,8 +42,7 @@ class SimpleMpdNotifierTest : public ::testing::Test { SimpleMpdNotifierTest() : default_mock_period_( new MockPeriod(kDefaultPeriodId, kDefaultPeriodStartTime)), - default_mock_adaptation_set_( - new MockAdaptationSet(kDefaultAdaptationSetId)) {} + default_mock_adaptation_set_(new MockAdaptationSet()) {} void SetUp() override { ASSERT_TRUE(base::CreateTemporaryFile(&temp_file_path_)); @@ -207,7 +205,7 @@ TEST_F(SimpleMpdNotifierTest, NotifyCueEvent) { std::unique_ptr mock_period( new MockPeriod(kDefaultPeriodId, kDefaultPeriodStartTime)); std::unique_ptr mock_adaptation_set( - new MockAdaptationSet(kDefaultAdaptationSetId)); + new MockAdaptationSet()); std::unique_ptr mock_representation( new MockRepresentation(kRepresentationId)); @@ -229,7 +227,7 @@ TEST_F(SimpleMpdNotifierTest, NotifyCueEvent) { std::unique_ptr mock_period2( new MockPeriod(kAnotherPeriodId, kArbitraryPeriodStartTime)); std::unique_ptr mock_adaptation_set2( - new MockAdaptationSet(kDefaultAdaptationSetId)); + new MockAdaptationSet()); std::unique_ptr mock_representation2( new MockRepresentation(kRepresentationId)); @@ -339,8 +337,8 @@ TEST_F(SimpleMpdNotifierTest, MultipleMediaInfo) { SimpleMpdNotifier notifier(empty_mpd_option_); std::unique_ptr mock_mpd_builder(new MockMpdBuilder()); - std::unique_ptr adaptation_set1(new MockAdaptationSet(1)); - std::unique_ptr adaptation_set2(new MockAdaptationSet(2)); + std::unique_ptr adaptation_set1(new MockAdaptationSet()); + std::unique_ptr adaptation_set2(new MockAdaptationSet()); std::unique_ptr representation1( new MockRepresentation(1)); diff --git a/packager/mpd/test/data/audio_media_info1_video_media_info1_expected_mpd_output.txt b/packager/mpd/test/data/audio_media_info1_video_media_info1_expected_mpd_output.txt index fe1ba62c4f..88f9eebb34 100644 --- a/packager/mpd/test/data/audio_media_info1_video_media_info1_expected_mpd_output.txt +++ b/packager/mpd/test/data/audio_media_info1_video_media_info1_expected_mpd_output.txt @@ -1,7 +1,7 @@ - + test_output_file_name1.mp4 @@ -9,7 +9,7 @@ - + test_output_file_name_audio1.mp4 diff --git a/packager/mpd/test/data/video_media_info1_expected_mpd_output.txt b/packager/mpd/test/data/video_media_info1_expected_mpd_output.txt index 2201487900..5e4775bc1a 100644 --- a/packager/mpd/test/data/video_media_info1_expected_mpd_output.txt +++ b/packager/mpd/test/data/video_media_info1_expected_mpd_output.txt @@ -1,7 +1,7 @@ - + test_output_file_name1.mp4 diff --git a/packager/mpd/test/data/video_media_info1and2_expected_mpd_output.txt b/packager/mpd/test/data/video_media_info1and2_expected_mpd_output.txt index bd6b978a50..5a4052b25c 100644 --- a/packager/mpd/test/data/video_media_info1and2_expected_mpd_output.txt +++ b/packager/mpd/test/data/video_media_info1and2_expected_mpd_output.txt @@ -1,7 +1,7 @@ - + test_output_file_name1.mp4