diff --git a/packager/mpd/base/dash_iop_mpd_notifier.cc b/packager/mpd/base/dash_iop_mpd_notifier.cc index 507b09dae4..ba93ed0a6c 100644 --- a/packager/mpd/base/dash_iop_mpd_notifier.cc +++ b/packager/mpd/base/dash_iop_mpd_notifier.cc @@ -63,15 +63,8 @@ bool DashIopMpdNotifier::NotifyNewContainer(const MediaInfo& media_info, return false; base::AutoLock auto_lock(lock_); - const std::string key = GetAdaptationSetKey(media_info); - AdaptationSet* adaptation_set = GetAdaptationSetForMediaInfo(key, media_info); + AdaptationSet* adaptation_set = GetOrCreateAdaptationSet(media_info); DCHECK(adaptation_set); - if (media_info.has_text_info()) { - // IOP requires all AdaptationSets to have (sub)segmentAlignment set to - // true, so carelessly set it to true. - // In practice it doesn't really make sense to adapt between text tracks. - adaptation_set->ForceSetSegmentAlignment(true); - } MediaInfo adjusted_media_info(media_info); MpdBuilder::MakePathsRelativeToMpd(output_path_, &adjusted_media_info); @@ -82,8 +75,6 @@ bool DashIopMpdNotifier::NotifyNewContainer(const MediaInfo& media_info, representation_id_to_adaptation_set_[representation->id()] = adaptation_set; - SetAdaptationSetSwitching(key, adaptation_set); - *container_id = representation->id(); DCHECK(!ContainsKey(representation_map_, representation->id())); representation_map_[representation->id()] = representation; @@ -150,128 +141,49 @@ bool DashIopMpdNotifier::Flush() { return WriteMpdToFile(output_path_, mpd_builder_.get()); } -AdaptationSet* DashIopMpdNotifier::ReuseAdaptationSet( - const std::list& adaptation_sets, - const MediaInfo& media_info) { - const bool has_protected_content = media_info.has_protected_content(); - for (AdaptationSet* adaptation_set : adaptation_sets) { - ProtectedContentMap::const_iterator protected_content_it = - protected_content_map_.find(adaptation_set->id()); - - // If the AdaptationSet ID is not registered in the map, then it is clear - // content. - if (protected_content_it == protected_content_map_.end()) { - // Can reuse the AdaptationSet without content protection. - if (!has_protected_content) { - return adaptation_set; - } - continue; - } - - if (ProtectedContentEq(protected_content_it->second, - media_info.protected_content())) { - // Content protection info matches. Reuse the AdaptationSet. - return adaptation_set; - } - } - - return nullptr; -} - -AdaptationSet* DashIopMpdNotifier::GetAdaptationSetForMediaInfo( - const std::string& key, +AdaptationSet* DashIopMpdNotifier::GetOrCreateAdaptationSet( const MediaInfo& media_info) { + const std::string key = GetAdaptationSetKey(media_info); std::list& adaptation_sets = adaptation_set_list_map_[key]; - if (adaptation_sets.empty()) - return NewAdaptationSet(media_info, &adaptation_sets); - - AdaptationSet* reuse_adaptation_set = - ReuseAdaptationSet(adaptation_sets, media_info); - if (reuse_adaptation_set) - return reuse_adaptation_set; - + for (AdaptationSet* adaptation_set : adaptation_sets) { + if (protected_adaptation_set_map_.Match(*adaptation_set, media_info)) + return adaptation_set; + } // None of the adaptation sets match with the new content protection. // Need a new one. - return NewAdaptationSet(media_info, &adaptation_sets); -} + AdaptationSet* new_adaptation_set = + NewAdaptationSet(media_info, adaptation_sets); + if (media_info.has_protected_content()) { + protected_adaptation_set_map_.Register(*new_adaptation_set, media_info); + AddContentProtectionElements(media_info, new_adaptation_set); -// Get all the UUIDs of the AdaptationSet. If another AdaptationSet has the -// same UUIDs then those are switchable. -void DashIopMpdNotifier::SetAdaptationSetSwitching( - const std::string& key, - AdaptationSet* adaptation_set) { - // This adaptation set is already visited. - if (!adaptation_set->adaptation_set_switching_ids().empty()) - return; - - ProtectedContentMap::const_iterator protected_content_it = - protected_content_map_.find(adaptation_set->id()); - // Clear contents should be in one AdaptationSet and may not be switchable - // with encrypted contents. - if (protected_content_it == protected_content_map_.end()) { - DVLOG(1) << "No content protection set for AdaptationSet@id=" - << adaptation_set->id(); - return; - } - - // Get all the UUIDs of the ContentProtections in AdaptationSet. - std::set adaptation_set_uuids = - GetUUIDs(protected_content_it->second); - - std::list& same_type_adapatation_sets = - adaptation_set_list_map_[key]; - DCHECK(!same_type_adapatation_sets.empty()) - << "same_type_adapatation_sets should not be null, it should at least " - "contain adaptation_set"; - - for (std::list::iterator adaptation_set_it = - same_type_adapatation_sets.begin(); - adaptation_set_it != same_type_adapatation_sets.end(); - ++adaptation_set_it) { - const uint32_t loop_adaptation_set_id = (*adaptation_set_it)->id(); - if (loop_adaptation_set_id == adaptation_set->id() || - !ContainsKey(protected_content_map_, loop_adaptation_set_id)) { - continue; - } - - const MediaInfo::ProtectedContent& loop_protected_content = - protected_content_map_[loop_adaptation_set_id]; - if (static_cast(adaptation_set_uuids.size()) != - loop_protected_content.content_protection_entry().size()) { - // Different number of UUIDs, may not be switchable. - continue; - } - - if (adaptation_set_uuids == GetUUIDs(loop_protected_content)) { - AdaptationSet& uuid_match_adaptation_set = **adaptation_set_it; - // They match. These AdaptationSets are switchable. - uuid_match_adaptation_set.AddAdaptationSetSwitching(adaptation_set->id()); - adaptation_set->AddAdaptationSetSwitching(uuid_match_adaptation_set.id()); + // Set adaptation set switching. + for (AdaptationSet* adaptation_set : adaptation_sets) { + if (protected_adaptation_set_map_.Switchable(*adaptation_set, + *new_adaptation_set)) { + new_adaptation_set->AddAdaptationSetSwitching(adaptation_set->id()); + adaptation_set->AddAdaptationSetSwitching(new_adaptation_set->id()); + } } } + adaptation_sets.push_back(new_adaptation_set); + return new_adaptation_set; } AdaptationSet* DashIopMpdNotifier::NewAdaptationSet( const MediaInfo& media_info, - std::list* adaptation_sets) { + const std::list& adaptation_sets) { std::string language = GetLanguage(media_info); AdaptationSet* new_adaptation_set = mpd_builder_->AddAdaptationSet(language); - if (media_info.has_protected_content()) { - DCHECK(!ContainsKey(protected_content_map_, new_adaptation_set->id())); - protected_content_map_[new_adaptation_set->id()] = - media_info.protected_content(); - AddContentProtectionElements(media_info, new_adaptation_set); - } - adaptation_sets->push_back(new_adaptation_set); if (media_info.has_video_info()) { // Because 'lang' is ignored for videos, |adaptation_sets| must have // all the video AdaptationSets. - if (adaptation_sets->size() > 2) { + if (adaptation_sets.size() > 1) { new_adaptation_set->AddRole(AdaptationSet::kRoleMain); - } else if (adaptation_sets->size() == 2) { + } else if (adaptation_sets.size() == 1) { // Set "main" Role for both AdaptatoinSets. - (*adaptation_sets->begin())->AddRole(AdaptationSet::kRoleMain); + (*adaptation_sets.begin())->AddRole(AdaptationSet::kRoleMain); new_adaptation_set->AddRole(AdaptationSet::kRoleMain); } @@ -285,6 +197,11 @@ AdaptationSet* DashIopMpdNotifier::NewAdaptationSet( DCHECK_NE(new_adaptation_set->id(), trick_play_reference_id); new_adaptation_set->AddTrickPlayReferenceId(trick_play_reference_id); } + } else if (media_info.has_text_info()) { + // IOP requires all AdaptationSets to have (sub)segmentAlignment set to + // true, so carelessly set it to true. + // In practice it doesn't really make sense to adapt between text tracks. + new_adaptation_set->ForceSetSegmentAlignment(true); } return new_adaptation_set; } @@ -294,22 +211,57 @@ bool DashIopMpdNotifier::FindOriginalAdaptationSetForTrickPlay( uint32_t* main_adaptation_set_id) { MediaInfo media_info_no_trickplay = media_info; media_info_no_trickplay.mutable_video_info()->clear_playback_rate(); + std::string key = GetAdaptationSetKey(media_info_no_trickplay); const std::list& adaptation_sets = adaptation_set_list_map_[key]; - if (adaptation_sets.empty()) { - return false; + 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 false; +} - AdaptationSet* reuse_adaptation_set = - ReuseAdaptationSet(adaptation_sets, media_info); - if (!reuse_adaptation_set) { +void DashIopMpdNotifier::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(); +} + +bool DashIopMpdNotifier::ProtectedAdaptationSetMap::Match( + const AdaptationSet& adaptation_set, + const MediaInfo& media_info) { + const auto protected_content_it = + protected_content_map_.find(adaptation_set.id()); + // If the AdaptationSet ID is not registered in the map, then it is clear + // content. + if (protected_content_it == protected_content_map_.end()) + return !media_info.has_protected_content(); + if (!media_info.has_protected_content()) return false; - } + return ProtectedContentEq(protected_content_it->second, + media_info.protected_content()); +} - *main_adaptation_set_id = reuse_adaptation_set->id(); +bool DashIopMpdNotifier::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()); + const auto protected_content_it_b = + protected_content_map_.find(adaptation_set_b.id()); - return true; + if (protected_content_it_a == protected_content_map_.end()) + return protected_content_it_b == protected_content_map_.end(); + if (protected_content_it_b == protected_content_map_.end()) + return false; + // Get all the UUIDs of the AdaptationSet. If another AdaptationSet has the + // same UUIDs then those are switchable. + return GetUUIDs(protected_content_it_a->second) == + GetUUIDs(protected_content_it_b->second); } } // namespace shaka diff --git a/packager/mpd/base/dash_iop_mpd_notifier.h b/packager/mpd/base/dash_iop_mpd_notifier.h index 947dd588bb..93a3ed9134 100644 --- a/packager/mpd/base/dash_iop_mpd_notifier.h +++ b/packager/mpd/base/dash_iop_mpd_notifier.h @@ -62,40 +62,18 @@ class DashIopMpdNotifier : public MpdNotifier { // Maps representation ID to Representation. typedef std::map RepresentationMap; - // Maps AdaptationSet ID to ProtectedContent. - typedef std::map ProtectedContentMap; - - // Find reusable AdaptationSet, instead of creating a new AdaptationSet for - // the |media_info|. There are two cases that an |existing_adaptation_set| - // can be used: - // 1) The media info does not have protected content and there is an existing - // unprotected content AdapationSet. - // 2) The media info has protected content and there is an exisiting - // AdaptationSet, which has same MediaInfo::ProtectedContent protobuf. - // Returns the reusable AdaptationSet pointer if found, otherwise returns - // nullptr. - AdaptationSet* ReuseAdaptationSet( - const std::list& adaptation_sets, - const MediaInfo& media_info); - // Checks the protected_content field of media_info and returns a non-null // AdaptationSet* for a new Representation. // This does not necessarily return a new AdaptationSet. If // media_info.protected_content completely matches with an existing // AdaptationSet, then it will return the pointer. - AdaptationSet* GetAdaptationSetForMediaInfo(const std::string& key, - const MediaInfo& media_info); + AdaptationSet* GetOrCreateAdaptationSet(const MediaInfo& media_info); - // Sets adaptation set switching. If adaptation set switching is already - // set, then this returns immediately. - void SetAdaptationSetSwitching(const std::string& key, - AdaptationSet* adaptation_set); - - // Helper function to get a new AdaptationSet; registers the values - // to the fields (maps) of the instance. - // If the media is encrypted, registers data to protected_content_map_. - AdaptationSet* NewAdaptationSet(const MediaInfo& media_info, - std::list* adaptation_sets); + // Helper function to get a new AdaptationSet and set new AdaptationSet + // attributes. + AdaptationSet* NewAdaptationSet( + const MediaInfo& media_info, + const std::list& adaptation_sets); // Gets the original AdaptationSet which the trick play video belongs // to and returns the id of the original adapatation set. @@ -119,8 +97,30 @@ class DashIopMpdNotifier : public MpdNotifier { std::map> adaptation_set_list_map_; RepresentationMap representation_map_; - // Used to check whether a Representation should be added to an AdaptationSet. - ProtectedContentMap protected_content_map_; + // Tracks ProtectedContent in AdaptationSet. + class ProtectedAdaptationSetMap { + public: + ProtectedAdaptationSetMap() = default; + // Register the |adaptation_set| with associated |media_info| in the map. + void Register(const AdaptationSet& adaptation_set, + const MediaInfo& media_info); + // Check if the protected content associated with |adaptation_set| matches + // with the one in |media_info|. + bool Match(const AdaptationSet& adaptation_set, + const MediaInfo& media_info); + // Check if the two adaptation sets are switchable. + bool Switchable(const AdaptationSet& adaptation_set_a, + const AdaptationSet& adaptation_set_b); + + private: + ProtectedAdaptationSetMap(const ProtectedAdaptationSetMap&) = delete; + ProtectedAdaptationSetMap& operator=(const ProtectedAdaptationSetMap&) = + delete; + + // Maps AdaptationSet ID to ProtectedContent. + std::map protected_content_map_; + }; + ProtectedAdaptationSetMap protected_adaptation_set_map_; // MPD output path. std::string output_path_; diff --git a/packager/mpd/base/dash_iop_mpd_notifier_unittest.cc b/packager/mpd/base/dash_iop_mpd_notifier_unittest.cc index 9c144390e0..43a40279ae 100644 --- a/packager/mpd/base/dash_iop_mpd_notifier_unittest.cc +++ b/packager/mpd/base/dash_iop_mpd_notifier_unittest.cc @@ -344,13 +344,13 @@ TEST_F(DashIopMpdNotifierTest, EXPECT_CALL(*mock_mpd_builder, AddAdaptationSet(_)) .WillOnce(Return(hd_adaptation_set.get())); - // Called twice for the same reason as above. - EXPECT_CALL(*hd_adaptation_set, AddContentProtectionElement(_)).Times(2); // Add main Role here for both. EXPECT_CALL(*sd_adaptation_set, AddRole(AdaptationSet::kRoleMain)); EXPECT_CALL(*hd_adaptation_set, AddRole(AdaptationSet::kRoleMain)); + // Called twice for the same reason as above. + EXPECT_CALL(*hd_adaptation_set, AddContentProtectionElement(_)).Times(2); EXPECT_CALL(*hd_adaptation_set, AddRepresentation(_)) .WillOnce(Return(hd_representation.get()));