Shaka Player Embedded
search_registry.cc
Go to the documentation of this file.
1 // Copyright 2017 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
16 
17 #include <memory>
18 #include <utility>
19 
23 #include "src/core/ref_ptr.h"
25 #include "src/js/js_error.h"
27 #include "src/mapping/js_utils.h"
28 
29 
30 namespace shaka {
31 namespace js {
32 namespace eme {
33 
34 namespace {
35 
36 bool IsPersistentSessionType(MediaKeySessionType type) {
37  return type == MediaKeySessionType::PersistentLicense;
38 }
39 
40 bool SupportsContentType(const std::string& content_type) {
41  return mse::MediaSource::IsTypeSupported(content_type);
42 }
43 
44 bool GetSupportedConfiguration(
45  std::shared_ptr<ImplementationFactory> implementation,
46  const MediaKeySystemConfiguration& candidate_config,
47  MediaKeySystemConfiguration* supported_config) {
48  // 1. Let accumulated configuration be a new MediaKeySystemConfiguration
49  // dictionary.
50  // Note: accumulated configuration == supported_config
51 
52  // 2. Set the label member of accumulated configuration to equal the label
53  // member of candidate configuration.
54  supported_config->label = candidate_config.label;
55 
56  // 3. If the initDataTypes member of candidate configuration is non-empty, run
57  // the following steps:
58  if (!candidate_config.initDataTypes.empty()) {
59  // 1. Let supported types be an empty sequence of DOMStrings.
60  supported_config->initDataTypes.clear();
61 
62  // 2. For each value in candidate configuration's initDataTypes member:
63  for (auto& init_data_type : candidate_config.initDataTypes) {
64  // 1. Let initDataType be the value.
65  // 2. If the implementation supports generating requests based on
66  // initDataType, add initDataType to supported types. String comparison is
67  // case-sensitive. The empty string is never supported.
68  if (implementation->SupportsInitDataType(init_data_type))
69  supported_config->initDataTypes.push_back(init_data_type);
70  }
71 
72  // 3. If supported types is empty, return NotSupported.
73  if (supported_config->initDataTypes.empty()) {
74  VLOG(1) << "None of the init data types are supported";
75  return false;
76  }
77  }
78 
79  // 4. Let distinctive identifier requirement be the value of candidate
80  // configuration's distinctiveIdentifier member.
81  MediaKeysRequirement distinctive_identifier =
82  candidate_config.distinctiveIdentifier;
83  // NA: 5. If distinctive identifier requirement is "optional" and Distinctive
84  // Identifiers are not allowed according to restrictions, set distinctive
85  // identifier requirement to "not-allowed".
86  // 6. Follow the steps for distinctive identifier requirement from the
87  // following list:
88  switch (distinctive_identifier) {
89  case MediaKeysRequirement::Required:
90  if (implementation->DistinctiveIdentifier() ==
91  MediaKeysRequirement::NotAllowed) {
92  VLOG(1) << "Distinctive identifier is required by app, but unsupported "
93  "by implementation";
94  return false;
95  }
96  break;
97  case MediaKeysRequirement::Optional:
98  break;
99  case MediaKeysRequirement::NotAllowed:
100  if (implementation->DistinctiveIdentifier() ==
101  MediaKeysRequirement::Required) {
102  VLOG(1) << "Distinctive identifier is required by implementation, but "
103  "app doesn't allow it";
104  return false;
105  }
106  break;
107  }
108 
109  // 7. Set the distinctiveIdentifier member of accumulated configuration to
110  // equal distinctive identifier requirement.
111  supported_config->distinctiveIdentifier = distinctive_identifier;
112 
113  // 8. Let persistent state requirement be equal to the value of candidate
114  // configuration's persistentState member.
115  MediaKeysRequirement persistent_state = candidate_config.persistentState;
116  // NA: 9. If persistent state requirement is "optional" and persisting state
117  // is not allowed according to restrictions, set persistent state requirement
118  // to "not-allowed".
119  // 10. Follow the steps for persistent state requirement from the following
120  // list:
121  switch (persistent_state) {
122  case MediaKeysRequirement::Required:
123  if (implementation->PersistentState() ==
124  MediaKeysRequirement::NotAllowed) {
125  VLOG(1) << "Persistent state is required by app, but unsupported by "
126  "implementation";
127  return false;
128  }
129  break;
130  case MediaKeysRequirement::Optional:
131  break;
132  case MediaKeysRequirement::NotAllowed:
133  if (implementation->PersistentState() == MediaKeysRequirement::Required) {
134  VLOG(1) << "Persistent state is required by implementation, but app "
135  "doesn't allow it";
136  return false;
137  }
138  break;
139  }
140 
141  // 11. Set the persistentState member of accumulated configuration to equal
142  // the value of persistent state requirement.
143  supported_config->persistentState = persistent_state;
144 
145  // 12. Follow the steps for the first matching condition from the following
146  // list:
147  std::vector<MediaKeySessionType> session_types =
148  candidate_config.sessionTypes;
149  if (session_types.empty())
150  session_types.push_back(MediaKeySessionType::Temporary);
151 
152  // 13. For each value in session types.
153  for (auto& session_type : session_types) {
154  // 1. Let session type be the value.
155 
156  // 2. If accumulated configuration's persistentState value is "not-allowed"
157  // and the Is persistent session type? algorithm returns true for session
158  // type return NotSupported.
159  if (supported_config->persistentState == MediaKeysRequirement::NotAllowed &&
160  IsPersistentSessionType(session_type)) {
161  VLOG(1) << "Request for persistent session but persistentState is "
162  "'not-allowed'";
163  return false;
164  }
165 
166  // 3. If the implementation does not support session type in combination
167  // with accumulated configuration and restrictions for other reasons, return
168  // NotSupported.
169  if (!implementation->SupportsSessionType(session_type)) {
170  VLOG(1) << "Implementation doesn't support session type";
171  return false;
172  }
173 
174  // 4. If accumulated configuration's persistentState value is "optional" and
175  // the result of running the Is persistent session type? algorithm on
176  // session type is true, change accumulated configuration's persistentState
177  // value to "required".
178  if (IsPersistentSessionType(session_type)) {
179  // The value NotAllowed was handled above, so the value is either
180  // Optional or already Required.
181  supported_config->persistentState = MediaKeysRequirement::Required;
182  }
183  }
184 
185  // 14. Set the sessionTypes member of accumulated configuration to session
186  // types.
187  supported_config->sessionTypes = session_types;
188 
189  // 15. If the videoCapabilities and audioCapabilities members in candidate
190  // configuration are both empty, return NotSupported.
191  if (candidate_config.audioCapabilities.empty() &&
192  candidate_config.videoCapabilities.empty()) {
193  VLOG(1) << "No audio/video capabilities given";
194  return false;
195  }
196 
197  // 16. If the videoCapabilities member in candidate configuration is non-empty
198  if (!candidate_config.videoCapabilities.empty()) {
199  for (auto& video_cap : candidate_config.videoCapabilities) {
200  if (SupportsContentType(video_cap.contentType) &&
201  implementation->SupportsVideoRobustness(video_cap.robustness)) {
202  supported_config->videoCapabilities.push_back(video_cap);
203  }
204  }
205 
206  if (supported_config->videoCapabilities.empty()) {
207  VLOG(1) << "None of the video capabilities are supported";
208  return false;
209  }
210  }
211 
212  // 17. If the audioCapabilities member in candidate configuration is non-empty
213  if (!candidate_config.audioCapabilities.empty()) {
214  for (auto& audio_cap : candidate_config.audioCapabilities) {
215  if (SupportsContentType(audio_cap.contentType) &&
216  implementation->SupportsAudioRobustness(audio_cap.robustness)) {
217  supported_config->audioCapabilities.push_back(audio_cap);
218  }
219  }
220 
221  if (supported_config->audioCapabilities.empty()) {
222  VLOG(1) << "None of the audio capabilities are supported";
223  return false;
224  }
225  }
226 
227  // 18. If accumulated configuration's distinctiveIdentifier value is
228  // "optional", follow the steps for the first matching condition from the
229  // following list:
230  if (supported_config->distinctiveIdentifier ==
231  MediaKeysRequirement::Optional) {
232  if (implementation->DistinctiveIdentifier() ==
233  MediaKeysRequirement::Required) {
234  supported_config->distinctiveIdentifier = MediaKeysRequirement::Required;
235  } else {
236  supported_config->distinctiveIdentifier =
237  MediaKeysRequirement::NotAllowed;
238  }
239  }
240 
241  // 19. If accumulated configuration's persistentState value is "optional",
242  // follow the steps for the first matching condition from the following list:
243  if (supported_config->persistentState == MediaKeysRequirement::Optional) {
244  if (implementation->PersistentState() == MediaKeysRequirement::Required) {
245  supported_config->persistentState = MediaKeysRequirement::Required;
246  } else {
247  supported_config->persistentState = MediaKeysRequirement::NotAllowed;
248  }
249  }
250 
251  // Ignore remaining steps since they pertain to consent.
252 
253  return true;
254 }
255 
256 } // namespace
257 
258 SearchRegistry::SearchRegistry(Promise promise, std::string key_system,
259  std::vector<MediaKeySystemConfiguration> configs)
260  : promise_(MakeJsRef<Promise>(std::move(promise))),
261  key_system_(std::move(key_system)) {
262  for (auto& config : configs) {
263  configs_.emplace_back(
264  MakeJsRef<MediaKeySystemConfiguration>(std::move(config)));
265  }
266 }
267 
269 
272 
274  // 1. If keySystem is not one of the Key Systems supported by the user agent,
275  // reject promise with a NotSupportedError. String comparison is
276  // case-sensitive.
277  // 2. Let implementation be the implementation of keySystem.
278  auto implementation = ImplementationRegistry::GetImplementation(key_system_);
279  if (!implementation) {
280  VLOG(1) << "No implementation found for: " << key_system_;
281  promise_->RejectWith(JsError::DOMException(
282  NotSupportedError, "Key system " + key_system_ + " is not supported."));
283  return;
284  }
285 
286  // 3. For each value in supportedConfigurations:
287  for (auto& candidate_config : configs_) {
288  // 1. Let candidate configuration be the value.
289  // 2. Let supported configuration be the result of executing the Get
290  // Supported Configuration algorithm on implementation, candidate
291  // configuration, and origin.
292  // 3. If supported configuration is not NotSupported, run the following
293  // steps:
294  MediaKeySystemConfiguration supported_config;
295  if (GetSupportedConfiguration(implementation, *candidate_config,
296  &supported_config)) {
297  // 1. Let access be a new MediaKeySystemAccess object, and initialize it
298  // as follows:
300  key_system_, supported_config, implementation));
301 
302  // 2. Resolve promise with access and abort the parallel steps of this
303  // algorithm.
304  LocalVar<JsValue> value(ToJsValue(access));
305  promise_->ResolveWith(value);
306  return;
307  }
308  }
309 
310  // 4. Reject promise with NotSupportedError.
311  promise_->RejectWith(JsError::DOMException(
312  NotSupportedError, "None of the given configurations are supported."));
313 }
314 
315 } // namespace eme
316 } // namespace js
317 } // namespace shaka
ReturnVal< JsValue > ToJsValue(T &&source)
Definition: convert_js.h:381
static std::shared_ptr< ImplementationFactory > GetImplementation(const std::string &key_system)
ExceptionCode type
SearchRegistry & operator=(const SearchRegistry &)=delete
RefPtr< T > MakeJsRef(Args &&... args)
Definition: js_utils.h:52
static JsError DOMException(ExceptionCode code)
Definition: js_error.cc:115
static bool IsTypeSupported(const std::string &mime_type)
Definition: media_source.cc:92
SearchRegistry(Promise promise, std::string key_system, std::vector< MediaKeySystemConfiguration > configs)