Shaka Packager SDK
period.cc
1 // Copyright 2017 Google Inc. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file or at
5 // https://developers.google.com/open-source/licenses/bsd
6 
7 #include "packager/mpd/base/period.h"
8 
9 #include "packager/base/stl_util.h"
10 #include "packager/mpd/base/adaptation_set.h"
11 #include "packager/mpd/base/mpd_options.h"
12 #include "packager/mpd/base/mpd_utils.h"
13 #include "packager/mpd/base/xml/xml_node.h"
14 
15 namespace shaka {
16 namespace {
17 
18 // The easiest way to check whether two protobufs are equal, is to compare the
19 // serialized version.
20 bool ProtectedContentEq(
21  const MediaInfo::ProtectedContent& content_protection1,
22  const MediaInfo::ProtectedContent& content_protection2) {
23  return content_protection1.SerializeAsString() ==
24  content_protection2.SerializeAsString();
25 }
26 
27 std::set<std::string> GetUUIDs(
28  const MediaInfo::ProtectedContent& protected_content) {
29  std::set<std::string> uuids;
30  for (const auto& entry : protected_content.content_protection_entry())
31  uuids.insert(entry.uuid());
32  return uuids;
33 }
34 
35 } // namespace
36 
37 Period::Period(uint32_t period_id,
38  double start_time_in_seconds,
39  const MpdOptions& mpd_options,
40  base::AtomicSequenceNumber* adaptation_set_counter,
41  base::AtomicSequenceNumber* representation_counter)
42  : id_(period_id),
43  start_time_in_seconds_(start_time_in_seconds),
44  mpd_options_(mpd_options),
45  adaptation_set_counter_(adaptation_set_counter),
46  representation_counter_(representation_counter) {}
47 
49  const MediaInfo& media_info,
50  bool content_protection_in_adaptation_set) {
51  // AdaptationSets with the same key should only differ in ContentProtection,
52  // which also means that if |content_protection_in_adaptation_set| is false,
53  // there should be at most one entry in |adaptation_sets|.
54  const std::string key = GetAdaptationSetKey(media_info);
55  std::list<AdaptationSet*>& adaptation_sets = adaptation_set_list_map_[key];
56  if (content_protection_in_adaptation_set) {
57  for (AdaptationSet* adaptation_set : adaptation_sets) {
58  if (protected_adaptation_set_map_.Match(*adaptation_set, media_info))
59  return adaptation_set;
60  }
61  } else {
62  if (!adaptation_sets.empty()) {
63  DCHECK_EQ(adaptation_sets.size(), 1u);
64  return adaptation_sets.front();
65  }
66  }
67  // None of the adaptation sets match with the new content protection.
68  // Need a new one.
69  std::string language = GetLanguage(media_info);
70  std::unique_ptr<AdaptationSet> new_adaptation_set =
71  NewAdaptationSet(adaptation_set_counter_->GetNext(), language,
72  mpd_options_, representation_counter_);
73  if (!SetNewAdaptationSetAttributes(language, media_info, adaptation_sets,
74  new_adaptation_set.get())) {
75  return nullptr;
76  }
77 
78  if (content_protection_in_adaptation_set &&
79  media_info.has_protected_content()) {
80  protected_adaptation_set_map_.Register(*new_adaptation_set, media_info);
81  AddContentProtectionElements(media_info, new_adaptation_set.get());
82 
83  for (AdaptationSet* adaptation_set : adaptation_sets) {
84  if (protected_adaptation_set_map_.Switchable(*adaptation_set,
85  *new_adaptation_set)) {
86  adaptation_set->AddAdaptationSetSwitching(new_adaptation_set->id());
87  new_adaptation_set->AddAdaptationSetSwitching(adaptation_set->id());
88  }
89  }
90  }
91  AdaptationSet* adaptation_set_ptr = new_adaptation_set.get();
92  adaptation_sets.push_back(adaptation_set_ptr);
93  adaptation_set_map_[adaptation_set_ptr->id()] = std::move(new_adaptation_set);
94  return adaptation_set_ptr;
95 }
96 
97 xml::scoped_xml_ptr<xmlNode> Period::GetXml() const {
98  xml::XmlNode period("Period");
99 
100  // Required for 'dynamic' MPDs.
101  period.SetId(id_);
102  // Iterate thru AdaptationSets and add them to one big Period element.
103  for (const auto& adaptation_set_pair : adaptation_set_map_) {
104  xml::scoped_xml_ptr<xmlNode> child(adaptation_set_pair.second->GetXml());
105  if (!child || !period.AddChild(std::move(child)))
106  return nullptr;
107  }
108 
109  if (duration_seconds_ != 0) {
110  period.SetStringAttribute("duration",
111  SecondsToXmlDuration(duration_seconds_));
112  } else if (mpd_options_.mpd_type == MpdType::kDynamic ||
113  start_time_in_seconds_ != 0) {
114  period.SetStringAttribute("start",
115  SecondsToXmlDuration(start_time_in_seconds_));
116  }
117  return period.PassScopedPtr();
118 }
119 
120 const std::list<AdaptationSet*> Period::GetAdaptationSets() const {
121  std::list<AdaptationSet*> adaptation_sets;
122  for (const auto& adaptation_set_pair : adaptation_set_map_) {
123  adaptation_sets.push_back(adaptation_set_pair.second.get());
124  }
125  return adaptation_sets;
126 }
127 
128 std::unique_ptr<AdaptationSet> Period::NewAdaptationSet(
129  uint32_t adaptation_set_id,
130  const std::string& language,
131  const MpdOptions& options,
132  base::AtomicSequenceNumber* representation_counter) {
133  return std::unique_ptr<AdaptationSet>(new AdaptationSet(
134  adaptation_set_id, language, options, representation_counter));
135 }
136 
137 bool Period::SetNewAdaptationSetAttributes(
138  const std::string& language,
139  const MediaInfo& media_info,
140  const std::list<AdaptationSet*>& adaptation_sets,
141  AdaptationSet* new_adaptation_set) {
142  if (!language.empty() && language == mpd_options_.mpd_params.default_language)
143  new_adaptation_set->AddRole(AdaptationSet::kRoleMain);
144 
145  if (media_info.has_video_info()) {
146  // Because 'language' is ignored for videos, |adaptation_sets| must have
147  // all the video AdaptationSets.
148  if (adaptation_sets.size() > 1) {
149  new_adaptation_set->AddRole(AdaptationSet::kRoleMain);
150  } else if (adaptation_sets.size() == 1) {
151  (*adaptation_sets.begin())->AddRole(AdaptationSet::kRoleMain);
152  new_adaptation_set->AddRole(AdaptationSet::kRoleMain);
153  }
154 
155  if (media_info.video_info().has_playback_rate()) {
156  uint32_t trick_play_reference_id = 0;
157  if (!FindOriginalAdaptationSetForTrickPlay(media_info,
158  &trick_play_reference_id)) {
159  LOG(ERROR) << "Failed to find main adaptation set for trick play.";
160  return false;
161  }
162  DCHECK_NE(new_adaptation_set->id(), trick_play_reference_id);
163  new_adaptation_set->AddTrickPlayReferenceId(trick_play_reference_id);
164  }
165  } else if (media_info.has_text_info()) {
166  // IOP requires all AdaptationSets to have (sub)segmentAlignment set to
167  // true, so carelessly set it to true.
168  // In practice it doesn't really make sense to adapt between text tracks.
169  new_adaptation_set->ForceSetSegmentAlignment(true);
170  }
171  return true;
172 }
173 
174 bool Period::FindOriginalAdaptationSetForTrickPlay(
175  const MediaInfo& media_info,
176  uint32_t* main_adaptation_set_id) {
177  MediaInfo media_info_no_trickplay = media_info;
178  media_info_no_trickplay.mutable_video_info()->clear_playback_rate();
179 
180  std::string key = GetAdaptationSetKey(media_info_no_trickplay);
181  const std::list<AdaptationSet*>& adaptation_sets =
182  adaptation_set_list_map_[key];
183  for (AdaptationSet* adaptation_set : adaptation_sets) {
184  if (protected_adaptation_set_map_.Match(*adaptation_set, media_info)) {
185  *main_adaptation_set_id = adaptation_set->id();
186  return true;
187  }
188  }
189  return false;
190 }
191 
192 void Period::ProtectedAdaptationSetMap::Register(
193  const AdaptationSet& adaptation_set,
194  const MediaInfo& media_info) {
195  DCHECK(!ContainsKey(protected_content_map_, adaptation_set.id()));
196  protected_content_map_[adaptation_set.id()] = media_info.protected_content();
197 }
198 
199 bool Period::ProtectedAdaptationSetMap::Match(
200  const AdaptationSet& adaptation_set,
201  const MediaInfo& media_info) {
202  const auto protected_content_it =
203  protected_content_map_.find(adaptation_set.id());
204  // If the AdaptationSet ID is not registered in the map, then it is clear
205  // content.
206  if (protected_content_it == protected_content_map_.end())
207  return !media_info.has_protected_content();
208  if (!media_info.has_protected_content())
209  return false;
210  return ProtectedContentEq(protected_content_it->second,
211  media_info.protected_content());
212 }
213 
214 bool Period::ProtectedAdaptationSetMap::Switchable(
215  const AdaptationSet& adaptation_set_a,
216  const AdaptationSet& adaptation_set_b) {
217  const auto protected_content_it_a =
218  protected_content_map_.find(adaptation_set_a.id());
219  const auto protected_content_it_b =
220  protected_content_map_.find(adaptation_set_b.id());
221 
222  if (protected_content_it_a == protected_content_map_.end())
223  return protected_content_it_b == protected_content_map_.end();
224  if (protected_content_it_b == protected_content_map_.end())
225  return false;
226  // Get all the UUIDs of the AdaptationSet. If another AdaptationSet has the
227  // same UUIDs then those are switchable.
228  return GetUUIDs(protected_content_it_a->second) ==
229  GetUUIDs(protected_content_it_b->second);
230 }
231 
232 } // namespace shaka
std::string default_language
Definition: mpd_params.h:46
virtual AdaptationSet * GetOrCreateAdaptationSet(const MediaInfo &media_info, bool content_protection_in_adaptation_set)
Definition: period.cc:48
xml::scoped_xml_ptr< xmlNode > GetXml() const
Definition: period.cc:97
scoped_xml_ptr< xmlNode > PassScopedPtr()
Definition: xml_node.cc:136
All the methods that are virtual are virtual for mocking.
void SetStringAttribute(const char *attribute_name, const std::string &attribute)
Definition: xml_node.cc:104
bool AddChild(scoped_xml_ptr< xmlNode > child)
Definition: xml_node.cc:62
virtual void AddTrickPlayReferenceId(uint32_t id)
virtual void AddRole(Role role)
void AddContentProtectionElements(const MediaInfo &media_info, Representation *parent)
Definition: mpd_utils.cc:368
void SetId(uint32_t id)
Definition: xml_node.cc:127
virtual void ForceSetSegmentAlignment(bool segment_alignment)
const std::list< AdaptationSet * > GetAdaptationSets() const
Definition: period.cc:120
Defines Mpd Options.
Definition: mpd_options.h:25
Period(uint32_t period_id, double start_time_in_seconds, const MpdOptions &mpd_options, base::AtomicSequenceNumber *adaptation_set_counter, base::AtomicSequenceNumber *representation_counter)
Definition: period.cc:37