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:
parent
ab19082c20
commit
99469834e8
|
@ -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
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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()));
|
||||
|
||||
|
|
Loading…
Reference in New Issue