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* representation_counter)
41  : id_(period_id),
42  start_time_in_seconds_(start_time_in_seconds),
43  mpd_options_(mpd_options),
44  representation_counter_(representation_counter) {}
45 
47  const MediaInfo& media_info,
48  bool content_protection_in_adaptation_set) {
49  // AdaptationSets with the same key should only differ in ContentProtection,
50  // which also means that if |content_protection_in_adaptation_set| is false,
51  // there should be at most one entry in |adaptation_sets|.
52  const std::string key = GetAdaptationSetKey(media_info);
53  std::list<AdaptationSet*>& adaptation_sets = adaptation_set_list_map_[key];
54  if (content_protection_in_adaptation_set) {
55  for (AdaptationSet* adaptation_set : adaptation_sets) {
56  if (protected_adaptation_set_map_.Match(*adaptation_set, media_info))
57  return adaptation_set;
58  }
59  } else {
60  if (!adaptation_sets.empty()) {
61  DCHECK_EQ(adaptation_sets.size(), 1u);
62  return adaptation_sets.front();
63  }
64  }
65  // None of the adaptation sets match with the new content protection.
66  // Need a new one.
67  const std::string language = GetLanguage(media_info);
68  std::unique_ptr<AdaptationSet> new_adaptation_set =
69  NewAdaptationSet(language, mpd_options_, representation_counter_);
70  if (!SetNewAdaptationSetAttributes(language, media_info, adaptation_sets,
71  new_adaptation_set.get())) {
72  return nullptr;
73  }
74 
75  if (content_protection_in_adaptation_set &&
76  media_info.has_protected_content()) {
77  protected_adaptation_set_map_.Register(*new_adaptation_set, media_info);
78  AddContentProtectionElements(media_info, new_adaptation_set.get());
79 
80  for (AdaptationSet* adaptation_set : adaptation_sets) {
81  if (protected_adaptation_set_map_.Switchable(*adaptation_set,
82  *new_adaptation_set)) {
83  adaptation_set->AddAdaptationSetSwitching(new_adaptation_set.get());
84  new_adaptation_set->AddAdaptationSetSwitching(adaptation_set);
85  }
86  }
87  }
88  AdaptationSet* adaptation_set_ptr = new_adaptation_set.get();
89  adaptation_sets.push_back(adaptation_set_ptr);
90  adaptation_sets_.emplace_back(std::move(new_adaptation_set));
91  return adaptation_set_ptr;
92 }
93 
94 xml::scoped_xml_ptr<xmlNode> Period::GetXml(bool output_period_duration) {
95  adaptation_sets_.sort(
96  [](const std::unique_ptr<AdaptationSet>& adaptation_set_a,
97  const std::unique_ptr<AdaptationSet>& adaptation_set_b) {
98  if (!adaptation_set_a->has_id())
99  return false;
100  if (!adaptation_set_b->has_id())
101  return true;
102  return adaptation_set_a->id() < adaptation_set_b->id();
103  });
104 
105  xml::XmlNode period("Period");
106 
107  // Required for 'dynamic' MPDs.
108  period.SetId(id_);
109  // Iterate thru AdaptationSets and add them to one big Period element.
110  for (const auto& adaptation_set : adaptation_sets_) {
111  xml::scoped_xml_ptr<xmlNode> child(adaptation_set->GetXml());
112  if (!child || !period.AddChild(std::move(child)))
113  return nullptr;
114  }
115 
116  if (output_period_duration) {
117  period.SetStringAttribute("duration",
118  SecondsToXmlDuration(duration_seconds_));
119  } else if (mpd_options_.mpd_type == MpdType::kDynamic) {
120  period.SetStringAttribute("start",
121  SecondsToXmlDuration(start_time_in_seconds_));
122  }
123  return period.PassScopedPtr();
124 }
125 
126 const std::list<AdaptationSet*> Period::GetAdaptationSets() const {
127  std::list<AdaptationSet*> adaptation_sets;
128  for (const auto& adaptation_set : adaptation_sets_) {
129  adaptation_sets.push_back(adaptation_set.get());
130  }
131  return adaptation_sets;
132 }
133 
134 std::unique_ptr<AdaptationSet> Period::NewAdaptationSet(
135  const std::string& language,
136  const MpdOptions& options,
137  base::AtomicSequenceNumber* representation_counter) {
138  return std::unique_ptr<AdaptationSet>(
139  new AdaptationSet(language, options, representation_counter));
140 }
141 
142 bool Period::SetNewAdaptationSetAttributes(
143  const std::string& language,
144  const MediaInfo& media_info,
145  const std::list<AdaptationSet*>& adaptation_sets,
146  AdaptationSet* new_adaptation_set) {
147  if (!language.empty() && language == mpd_options_.mpd_params.default_language)
148  new_adaptation_set->AddRole(AdaptationSet::kRoleMain);
149 
150  if (media_info.has_video_info()) {
151  // Because 'language' is ignored for videos, |adaptation_sets| must have
152  // all the video AdaptationSets.
153  if (adaptation_sets.size() > 1) {
154  new_adaptation_set->AddRole(AdaptationSet::kRoleMain);
155  } else if (adaptation_sets.size() == 1) {
156  (*adaptation_sets.begin())->AddRole(AdaptationSet::kRoleMain);
157  new_adaptation_set->AddRole(AdaptationSet::kRoleMain);
158  }
159 
160  if (media_info.video_info().has_playback_rate()) {
161  const AdaptationSet* trick_play_reference_adaptation_set =
162  FindOriginalAdaptationSetForTrickPlay(media_info);
163  if (!trick_play_reference_adaptation_set) {
164  LOG(ERROR) << "Failed to find original AdaptationSet for trick play.";
165  return false;
166  }
167  new_adaptation_set->AddTrickPlayReference(
168  trick_play_reference_adaptation_set);
169  }
170  } else if (media_info.has_text_info()) {
171  // IOP requires all AdaptationSets to have (sub)segmentAlignment set to
172  // true, so carelessly set it to true.
173  // In practice it doesn't really make sense to adapt between text tracks.
174  new_adaptation_set->ForceSetSegmentAlignment(true);
175  }
176  return true;
177 }
178 
179 const AdaptationSet* Period::FindOriginalAdaptationSetForTrickPlay(
180  const MediaInfo& media_info) {
181  MediaInfo media_info_no_trickplay = media_info;
182  media_info_no_trickplay.mutable_video_info()->clear_playback_rate();
183 
184  std::string key = GetAdaptationSetKey(media_info_no_trickplay);
185  const std::list<AdaptationSet*>& adaptation_sets =
186  adaptation_set_list_map_[key];
187  for (AdaptationSet* adaptation_set : adaptation_sets) {
188  if (protected_adaptation_set_map_.Match(*adaptation_set, media_info)) {
189  return adaptation_set;
190  }
191  }
192  return nullptr;
193 }
194 
195 void Period::ProtectedAdaptationSetMap::Register(
196  const AdaptationSet& adaptation_set,
197  const MediaInfo& media_info) {
198  DCHECK(!ContainsKey(protected_content_map_, &adaptation_set));
199  protected_content_map_[&adaptation_set] = media_info.protected_content();
200 }
201 
202 bool Period::ProtectedAdaptationSetMap::Match(
203  const AdaptationSet& adaptation_set,
204  const MediaInfo& media_info) {
205  const auto protected_content_it =
206  protected_content_map_.find(&adaptation_set);
207  // If the AdaptationSet ID is not registered in the map, then it is clear
208  // content.
209  if (protected_content_it == protected_content_map_.end())
210  return !media_info.has_protected_content();
211  if (!media_info.has_protected_content())
212  return false;
213  return ProtectedContentEq(protected_content_it->second,
214  media_info.protected_content());
215 }
216 
217 bool Period::ProtectedAdaptationSetMap::Switchable(
218  const AdaptationSet& adaptation_set_a,
219  const AdaptationSet& adaptation_set_b) {
220  const auto protected_content_it_a =
221  protected_content_map_.find(&adaptation_set_a);
222  const auto protected_content_it_b =
223  protected_content_map_.find(&adaptation_set_b);
224 
225  if (protected_content_it_a == protected_content_map_.end())
226  return protected_content_it_b == protected_content_map_.end();
227  if (protected_content_it_b == protected_content_map_.end())
228  return false;
229  // Get all the UUIDs of the AdaptationSet. If another AdaptationSet has the
230  // same UUIDs then those are switchable.
231  return GetUUIDs(protected_content_it_a->second) ==
232  GetUUIDs(protected_content_it_b->second);
233 }
234 
235 } // namespace shaka
std::string default_language
Definition: mpd_params.h:56
virtual AdaptationSet * GetOrCreateAdaptationSet(const MediaInfo &media_info, bool content_protection_in_adaptation_set)
Definition: period.cc:46
scoped_xml_ptr< xmlNode > PassScopedPtr()
Definition: xml_node.cc:169
All the methods that are virtual are virtual for mocking.
void SetStringAttribute(const char *attribute_name, const std::string &attribute)
Definition: xml_node.cc:137
bool AddChild(scoped_xml_ptr< xmlNode > child)
Definition: xml_node.cc:95
virtual void AddRole(Role role)
xml::scoped_xml_ptr< xmlNode > GetXml(bool output_period_duration)
Definition: period.cc:94
void AddContentProtectionElements(const MediaInfo &media_info, Representation *parent)
Definition: mpd_utils.cc:369
void SetId(uint32_t id)
Definition: xml_node.cc:160
virtual void ForceSetSegmentAlignment(bool segment_alignment)
const std::list< AdaptationSet * > GetAdaptationSets() const
Definition: period.cc:126
Defines Mpd Options.
Definition: mpd_options.h:25
Period(uint32_t period_id, double start_time_in_seconds, const MpdOptions &mpd_options, base::AtomicSequenceNumber *representation_counter)
Definition: period.cc:37
virtual void AddTrickPlayReference(const AdaptationSet *adaptation_set)