Refactor DashIopMpdNotifier Part 1

Clean up DashIopMpdNotifier to make it easier to add Period class.
- Consolidate protected AdaptationSet grouping related logic to the
  internal ProtectedAdaptationSetMap class
- Clean up AdaptationSet creation logic and updating logic

Change-Id: Idd5b39bb89d38e490ca4f561a6b840ccc9f1e40a
This commit is contained in:
KongQun Yang 2017-12-12 20:14:44 -08:00
parent ab19082c20
commit 99469834e8
3 changed files with 105 additions and 153 deletions

View File

@ -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<AdaptationSet*>& 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<AdaptationSet*>& 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<std::string> adaptation_set_uuids =
GetUUIDs(protected_content_it->second);
std::list<AdaptationSet*>& 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<AdaptationSet*>::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<int>(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<AdaptationSet*>* adaptation_sets) {
const std::list<AdaptationSet*>& 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<AdaptationSet*>& 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

View File

@ -62,40 +62,18 @@ class DashIopMpdNotifier : public MpdNotifier {
// Maps representation ID to Representation.
typedef std::map<uint32_t, Representation*> RepresentationMap;
// Maps AdaptationSet ID to ProtectedContent.
typedef std::map<uint32_t, MediaInfo::ProtectedContent> 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<AdaptationSet*>& 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<AdaptationSet*>* adaptation_sets);
// Helper function to get a new AdaptationSet and set new AdaptationSet
// attributes.
AdaptationSet* NewAdaptationSet(
const MediaInfo& media_info,
const std::list<AdaptationSet*>& 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<std::string, std::list<AdaptationSet*>> 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<uint32_t, MediaInfo::ProtectedContent> protected_content_map_;
};
ProtectedAdaptationSetMap protected_adaptation_set_map_;
// MPD output path.
std::string output_path_;

View File

@ -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()));