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