DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
widevine_key_source.cc
1 // Copyright 2014 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/media/base/widevine_key_source.h"
8 
9 #include <set>
10 
11 #include "packager/base/base64.h"
12 #include "packager/base/bind.h"
13 #include "packager/base/json/json_reader.h"
14 #include "packager/base/json/json_writer.h"
15 #include "packager/base/strings/string_number_conversions.h"
16 #include "packager/media/base/fixed_key_source.h"
17 #include "packager/media/base/http_key_fetcher.h"
18 #include "packager/media/base/network_util.h"
19 #include "packager/media/base/producer_consumer_queue.h"
20 #include "packager/media/base/protection_system_specific_info.h"
21 #include "packager/media/base/rcheck.h"
22 #include "packager/media/base/request_signer.h"
23 #include "packager/media/base/widevine_pssh_data.pb.h"
24 
25 namespace shaka {
26 namespace media {
27 namespace {
28 
29 const bool kEnableKeyRotation = true;
30 
31 const char kLicenseStatusOK[] = "OK";
32 // Server may return INTERNAL_ERROR intermittently, which is a transient error
33 // and the next client request may succeed without problem.
34 const char kLicenseStatusTransientError[] = "INTERNAL_ERROR";
35 
36 // Number of times to retry requesting keys in case of a transient error from
37 // the server.
38 const int kNumTransientErrorRetries = 5;
39 const int kFirstRetryDelayMilliseconds = 1000;
40 
41 // Default crypto period count, which is the number of keys to fetch on every
42 // key rotation enabled request.
43 const int kDefaultCryptoPeriodCount = 10;
44 const int kGetKeyTimeoutInSeconds = 5 * 60; // 5 minutes.
45 const int kKeyFetchTimeoutInSeconds = 60; // 1 minute.
46 
47 std::vector<uint8_t> StringToBytes(const std::string& string) {
48  return std::vector<uint8_t>(string.begin(), string.end());
49 }
50 
51 std::vector<uint8_t> WidevinePsshFromKeyId(
52  const std::vector<std::vector<uint8_t>>& key_ids) {
53  media::WidevinePsshData widevine_pssh_data;
54  for (const std::vector<uint8_t>& key_id : key_ids)
55  widevine_pssh_data.add_key_id(key_id.data(), key_id.size());
56  return StringToBytes(widevine_pssh_data.SerializeAsString());
57 }
58 
59 bool Base64StringToBytes(const std::string& base64_string,
60  std::vector<uint8_t>* bytes) {
61  DCHECK(bytes);
62  std::string str;
63  if (!base::Base64Decode(base64_string, &str))
64  return false;
65  bytes->assign(str.begin(), str.end());
66  return true;
67 }
68 
69 void BytesToBase64String(const std::vector<uint8_t>& bytes,
70  std::string* base64_string) {
71  DCHECK(base64_string);
72  base::Base64Encode(base::StringPiece(reinterpret_cast<const char*>
73  (bytes.data()), bytes.size()),
74  base64_string);
75 }
76 
77 bool GetKeyFromTrack(const base::DictionaryValue& track_dict,
78  std::vector<uint8_t>* key) {
79  DCHECK(key);
80  std::string key_base64_string;
81  RCHECK(track_dict.GetString("key", &key_base64_string));
82  VLOG(2) << "Key:" << key_base64_string;
83  RCHECK(Base64StringToBytes(key_base64_string, key));
84  return true;
85 }
86 
87 bool GetKeyIdFromTrack(const base::DictionaryValue& track_dict,
88  std::vector<uint8_t>* key_id) {
89  DCHECK(key_id);
90  std::string key_id_base64_string;
91  RCHECK(track_dict.GetString("key_id", &key_id_base64_string));
92  VLOG(2) << "Keyid:" << key_id_base64_string;
93  RCHECK(Base64StringToBytes(key_id_base64_string, key_id));
94  return true;
95 }
96 
97 bool GetPsshDataFromTrack(const base::DictionaryValue& track_dict,
98  std::vector<uint8_t>* pssh_data) {
99  DCHECK(pssh_data);
100 
101  const base::ListValue* pssh_list;
102  RCHECK(track_dict.GetList("pssh", &pssh_list));
103  // Invariant check. We don't want to crash in release mode if possible.
104  // The following code handles it gracefully if GetSize() does not return 1.
105  DCHECK_EQ(1u, pssh_list->GetSize());
106 
107  const base::DictionaryValue* pssh_dict;
108  RCHECK(pssh_list->GetDictionary(0, &pssh_dict));
109  std::string drm_type;
110  RCHECK(pssh_dict->GetString("drm_type", &drm_type));
111  if (drm_type != "WIDEVINE") {
112  LOG(ERROR) << "Expecting drm_type 'WIDEVINE', get '" << drm_type << "'.";
113  return false;
114  }
115  std::string pssh_data_base64_string;
116  RCHECK(pssh_dict->GetString("data", &pssh_data_base64_string));
117 
118  VLOG(2) << "Pssh Data:" << pssh_data_base64_string;
119  RCHECK(Base64StringToBytes(pssh_data_base64_string, pssh_data));
120  return true;
121 }
122 
123 bool IsProtectionSchemeValid(FourCC protection_scheme) {
124  return protection_scheme == FOURCC_cenc || protection_scheme == FOURCC_cbcs ||
125  protection_scheme == FOURCC_cbc1 || protection_scheme == FOURCC_cens;
126 }
127 
128 } // namespace
129 
130 WidevineKeySource::WidevineKeySource(const std::string& server_url,
131  bool add_common_pssh)
132  : key_production_thread_("KeyProductionThread",
133  base::Bind(&WidevineKeySource::FetchKeysTask,
134  base::Unretained(this))),
135  key_fetcher_(new HttpKeyFetcher(kKeyFetchTimeoutInSeconds)),
136  server_url_(server_url),
137  crypto_period_count_(kDefaultCryptoPeriodCount),
138  protection_scheme_(FOURCC_cenc),
139  add_common_pssh_(add_common_pssh),
140  key_production_started_(false),
141  start_key_production_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
142  base::WaitableEvent::InitialState::NOT_SIGNALED),
143  first_crypto_period_index_(0) {
144  key_production_thread_.Start();
145 }
146 
147 WidevineKeySource::~WidevineKeySource() {
148  if (key_pool_)
149  key_pool_->Stop();
150  if (key_production_thread_.HasBeenStarted()) {
151  // Signal the production thread to start key production if it is not
152  // signaled yet so the thread can be joined.
153  start_key_production_.Signal();
154  key_production_thread_.Join();
155  }
156 }
157 
158 Status WidevineKeySource::FetchKeys(const std::vector<uint8_t>& content_id,
159  const std::string& policy) {
160  base::AutoLock scoped_lock(lock_);
161  request_dict_.Clear();
162  std::string content_id_base64_string;
163  BytesToBase64String(content_id, &content_id_base64_string);
164  request_dict_.SetString("content_id", content_id_base64_string);
165  request_dict_.SetString("policy", policy);
166 
167  FourCC protection_scheme = protection_scheme_;
168  // Treat sample aes as a variant of cbcs.
169  if (protection_scheme == kAppleSampleAesProtectionScheme)
170  protection_scheme = FOURCC_cbcs;
171  if (IsProtectionSchemeValid(protection_scheme)) {
172  request_dict_.SetInteger("protection_scheme", protection_scheme);
173  } else {
174  LOG(WARNING) << "Ignore unrecognized protection scheme "
175  << FourCCToString(protection_scheme);
176  }
177 
178  return FetchKeysInternal(!kEnableKeyRotation, 0, false);
179 }
180 
181 Status WidevineKeySource::FetchKeys(EmeInitDataType init_data_type,
182  const std::vector<uint8_t>& init_data) {
183  std::vector<uint8_t> pssh_data;
184  uint32_t asset_id = 0;
185  switch (init_data_type) {
186  case EmeInitDataType::CENC: {
187  const std::vector<uint8_t> widevine_system_id(
188  kWidevineSystemId, kWidevineSystemId + arraysize(kWidevineSystemId));
189  std::vector<ProtectionSystemSpecificInfo> protection_systems_info;
191  init_data.data(), init_data.size(), &protection_systems_info)) {
192  return Status(error::PARSER_FAILURE, "Error parsing the PSSH boxes.");
193  }
194  for (const auto& info: protection_systems_info) {
195  // Use Widevine PSSH if available otherwise construct a Widevine PSSH
196  // from the first available key ids.
197  if (info.system_id() == widevine_system_id) {
198  pssh_data = info.pssh_data();
199  break;
200  } else if (pssh_data.empty() && !info.key_ids().empty()) {
201  pssh_data = WidevinePsshFromKeyId(info.key_ids());
202  // Continue to see if there is any Widevine PSSH. The KeyId generated
203  // PSSH is only used if a Widevine PSSH could not be found.
204  continue;
205  }
206  }
207  if (pssh_data.empty())
208  return Status(error::INVALID_ARGUMENT, "No supported PSSHs found.");
209  break;
210  }
211  case EmeInitDataType::WEBM:
212  pssh_data = WidevinePsshFromKeyId({init_data});
213  break;
214  case EmeInitDataType::WIDEVINE_CLASSIC:
215  if (init_data.size() < sizeof(asset_id))
216  return Status(error::INVALID_ARGUMENT, "Invalid asset id.");
217  asset_id = ntohlFromBuffer(init_data.data());
218  break;
219  default:
220  LOG(ERROR) << "Init data type " << static_cast<int>(init_data_type)
221  << " not supported.";
222  return Status(error::INVALID_ARGUMENT, "Unsupported init data type.");
223  }
224  const bool widevine_classic =
225  init_data_type == EmeInitDataType::WIDEVINE_CLASSIC;
226  base::AutoLock scoped_lock(lock_);
227  request_dict_.Clear();
228  if (widevine_classic) {
229  // Javascript/JSON does not support int64_t or unsigned numbers. Use double
230  // instead as 32-bit integer can be lossless represented using double.
231  request_dict_.SetDouble("asset_id", asset_id);
232  } else {
233  std::string pssh_data_base64_string;
234  BytesToBase64String(pssh_data, &pssh_data_base64_string);
235  request_dict_.SetString("pssh_data", pssh_data_base64_string);
236  }
237  return FetchKeysInternal(!kEnableKeyRotation, 0, widevine_classic);
238 }
239 
240 Status WidevineKeySource::GetKey(const std::string& stream_label,
241  EncryptionKey* key) {
242  DCHECK(key);
243  if (encryption_key_map_.find(stream_label) == encryption_key_map_.end()) {
244  return Status(error::INTERNAL_ERROR,
245  "Cannot find key for '" + stream_label + "'.");
246  }
247  *key = *encryption_key_map_[stream_label];
248  return Status::OK;
249 }
250 
251 Status WidevineKeySource::GetKey(const std::vector<uint8_t>& key_id,
252  EncryptionKey* key) {
253  DCHECK(key);
254  for (const auto& pair : encryption_key_map_) {
255  if (pair.second->key_id == key_id) {
256  *key = *pair.second;
257  return Status::OK;
258  }
259  }
260  return Status(error::INTERNAL_ERROR,
261  "Cannot find key with specified key ID");
262 }
263 
264 Status WidevineKeySource::GetCryptoPeriodKey(uint32_t crypto_period_index,
265  const std::string& stream_label,
266  EncryptionKey* key) {
267  DCHECK(key_production_thread_.HasBeenStarted());
268  // TODO(kqyang): This is not elegant. Consider refactoring later.
269  {
270  base::AutoLock scoped_lock(lock_);
271  if (!key_production_started_) {
272  // Another client may have a slightly smaller starting crypto period
273  // index. Set the initial value to account for that.
274  first_crypto_period_index_ =
275  crypto_period_index ? crypto_period_index - 1 : 0;
276  DCHECK(!key_pool_);
277  key_pool_.reset(new EncryptionKeyQueue(crypto_period_count_,
278  first_crypto_period_index_));
279  start_key_production_.Signal();
280  key_production_started_ = true;
281  }
282  }
283  return GetKeyInternal(crypto_period_index, stream_label, key);
284 }
285 
286 void WidevineKeySource::set_signer(std::unique_ptr<RequestSigner> signer) {
287  signer_ = std::move(signer);
288 }
289 
291  std::unique_ptr<KeyFetcher> key_fetcher) {
292  key_fetcher_ = std::move(key_fetcher);
293 }
294 
295 void WidevineKeySource::set_group_id(const std::vector<uint8_t>& group_id) {
296  group_id_ = group_id;
297 }
298 
299 Status WidevineKeySource::GetKeyInternal(uint32_t crypto_period_index,
300  const std::string& stream_label,
301  EncryptionKey* key) {
302  DCHECK(key_pool_);
303  DCHECK(key);
304 
305  std::shared_ptr<EncryptionKeyMap> encryption_key_map;
306  Status status = key_pool_->Peek(crypto_period_index, &encryption_key_map,
307  kGetKeyTimeoutInSeconds * 1000);
308  if (!status.ok()) {
309  if (status.error_code() == error::STOPPED) {
310  CHECK(!common_encryption_request_status_.ok());
311  return common_encryption_request_status_;
312  }
313  return status;
314  }
315 
316  if (encryption_key_map->find(stream_label) == encryption_key_map->end()) {
317  return Status(error::INTERNAL_ERROR,
318  "Cannot find key for '" + stream_label + "'.");
319  }
320  *key = *encryption_key_map->at(stream_label);
321  return Status::OK;
322 }
323 
324 void WidevineKeySource::FetchKeysTask() {
325  // Wait until key production is signaled.
326  start_key_production_.Wait();
327  if (!key_pool_ || key_pool_->Stopped())
328  return;
329 
330  Status status = FetchKeysInternal(kEnableKeyRotation,
331  first_crypto_period_index_,
332  false);
333  while (status.ok()) {
334  first_crypto_period_index_ += crypto_period_count_;
335  status = FetchKeysInternal(kEnableKeyRotation,
336  first_crypto_period_index_,
337  false);
338  }
339  common_encryption_request_status_ = status;
340  key_pool_->Stop();
341 }
342 
343 Status WidevineKeySource::FetchKeysInternal(bool enable_key_rotation,
344  uint32_t first_crypto_period_index,
345  bool widevine_classic) {
346  std::string request;
347  FillRequest(enable_key_rotation,
348  first_crypto_period_index,
349  &request);
350 
351  std::string message;
352  Status status = GenerateKeyMessage(request, &message);
353  if (!status.ok())
354  return status;
355  VLOG(1) << "Message: " << message;
356 
357  std::string raw_response;
358  int64_t sleep_duration = kFirstRetryDelayMilliseconds;
359 
360  // Perform client side retries if seeing server transient error to workaround
361  // server limitation.
362  for (int i = 0; i < kNumTransientErrorRetries; ++i) {
363  status = key_fetcher_->FetchKeys(server_url_, message, &raw_response);
364  if (status.ok()) {
365  VLOG(1) << "Retry [" << i << "] Response:" << raw_response;
366 
367  std::string response;
368  if (!DecodeResponse(raw_response, &response)) {
369  return Status(error::SERVER_ERROR,
370  "Failed to decode response '" + raw_response + "'.");
371  }
372 
373  bool transient_error = false;
374  if (ExtractEncryptionKey(enable_key_rotation,
375  widevine_classic,
376  response,
377  &transient_error))
378  return Status::OK;
379 
380  if (!transient_error) {
381  return Status(
382  error::SERVER_ERROR,
383  "Failed to extract encryption key from '" + response + "'.");
384  }
385  } else if (status.error_code() != error::TIME_OUT) {
386  return status;
387  }
388 
389  // Exponential backoff.
390  if (i != kNumTransientErrorRetries - 1) {
391  base::PlatformThread::Sleep(
392  base::TimeDelta::FromMilliseconds(sleep_duration));
393  sleep_duration *= 2;
394  }
395  }
396  return Status(error::SERVER_ERROR,
397  "Failed to recover from server internal error.");
398 }
399 
400 void WidevineKeySource::FillRequest(bool enable_key_rotation,
401  uint32_t first_crypto_period_index,
402  std::string* request) {
403  DCHECK(request);
404  DCHECK(!request_dict_.empty());
405 
406  // Build tracks.
407  base::ListValue* tracks = new base::ListValue();
408 
409  base::DictionaryValue* track_sd = new base::DictionaryValue();
410  track_sd->SetString("type", "SD");
411  tracks->Append(track_sd);
412  base::DictionaryValue* track_hd = new base::DictionaryValue();
413  track_hd->SetString("type", "HD");
414  tracks->Append(track_hd);
415  base::DictionaryValue* track_uhd1 = new base::DictionaryValue();
416  track_uhd1->SetString("type", "UHD1");
417  tracks->Append(track_uhd1);
418  base::DictionaryValue* track_uhd2 = new base::DictionaryValue();
419  track_uhd2->SetString("type", "UHD2");
420  tracks->Append(track_uhd2);
421  base::DictionaryValue* track_audio = new base::DictionaryValue();
422  track_audio->SetString("type", "AUDIO");
423  tracks->Append(track_audio);
424 
425  request_dict_.Set("tracks", tracks);
426 
427  // Build DRM types.
428  base::ListValue* drm_types = new base::ListValue();
429  drm_types->AppendString("WIDEVINE");
430  request_dict_.Set("drm_types", drm_types);
431 
432  // Build key rotation fields.
433  if (enable_key_rotation) {
434  // Javascript/JSON does not support int64_t or unsigned numbers. Use double
435  // instead as 32-bit integer can be lossless represented using double.
436  request_dict_.SetDouble("first_crypto_period_index",
437  first_crypto_period_index);
438  request_dict_.SetInteger("crypto_period_count", crypto_period_count_);
439  }
440 
441  // Set group id if present.
442  if (!group_id_.empty()) {
443  std::string group_id_base64;
444  BytesToBase64String(group_id_, &group_id_base64);
445  request_dict_.SetString("group_id", group_id_base64);
446  }
447 
448  base::JSONWriter::WriteWithOptions(
449  request_dict_,
450  // Write doubles that have no fractional part as a normal integer, i.e.
451  // without using exponential notation or appending a '.0'.
452  base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION, request);
453 }
454 
455 Status WidevineKeySource::GenerateKeyMessage(const std::string& request,
456  std::string* message) {
457  DCHECK(message);
458 
459  std::string request_base64_string;
460  base::Base64Encode(request, &request_base64_string);
461 
462  base::DictionaryValue request_dict;
463  request_dict.SetString("request", request_base64_string);
464 
465  // Sign the request.
466  if (signer_) {
467  std::string signature;
468  if (!signer_->GenerateSignature(request, &signature))
469  return Status(error::INTERNAL_ERROR, "Signature generation failed.");
470 
471  std::string signature_base64_string;
472  base::Base64Encode(signature, &signature_base64_string);
473 
474  request_dict.SetString("signature", signature_base64_string);
475  request_dict.SetString("signer", signer_->signer_name());
476  }
477 
478  base::JSONWriter::Write(request_dict, message);
479  return Status::OK;
480 }
481 
482 bool WidevineKeySource::DecodeResponse(
483  const std::string& raw_response,
484  std::string* response) {
485  DCHECK(response);
486 
487  // Extract base64 formatted response from JSON formatted raw response.
488  std::unique_ptr<base::Value> root(base::JSONReader::Read(raw_response));
489  if (!root) {
490  LOG(ERROR) << "'" << raw_response << "' is not in JSON format.";
491  return false;
492  }
493  const base::DictionaryValue* response_dict = NULL;
494  RCHECK(root->GetAsDictionary(&response_dict));
495 
496  std::string response_base64_string;
497  RCHECK(response_dict->GetString("response", &response_base64_string));
498  RCHECK(base::Base64Decode(response_base64_string, response));
499  return true;
500 }
501 
502 bool WidevineKeySource::ExtractEncryptionKey(
503  bool enable_key_rotation,
504  bool widevine_classic,
505  const std::string& response,
506  bool* transient_error) {
507  DCHECK(transient_error);
508  *transient_error = false;
509 
510  std::unique_ptr<base::Value> root(base::JSONReader::Read(response));
511  if (!root) {
512  LOG(ERROR) << "'" << response << "' is not in JSON format.";
513  return false;
514  }
515 
516  const base::DictionaryValue* license_dict = NULL;
517  RCHECK(root->GetAsDictionary(&license_dict));
518 
519  std::string license_status;
520  RCHECK(license_dict->GetString("status", &license_status));
521  if (license_status != kLicenseStatusOK) {
522  LOG(ERROR) << "Received non-OK license response: " << response;
523  *transient_error = (license_status == kLicenseStatusTransientError);
524  return false;
525  }
526 
527  const base::ListValue* tracks;
528  RCHECK(license_dict->GetList("tracks", &tracks));
529  // Should have at least one track per crypto_period.
530  RCHECK(enable_key_rotation ? tracks->GetSize() >= 1 * crypto_period_count_
531  : tracks->GetSize() >= 1);
532 
533  int current_crypto_period_index = first_crypto_period_index_;
534 
535  EncryptionKeyMap encryption_key_map;
536  for (size_t i = 0; i < tracks->GetSize(); ++i) {
537  const base::DictionaryValue* track_dict;
538  RCHECK(tracks->GetDictionary(i, &track_dict));
539 
540  if (enable_key_rotation) {
541  int crypto_period_index;
542  RCHECK(
543  track_dict->GetInteger("crypto_period_index", &crypto_period_index));
544  if (crypto_period_index != current_crypto_period_index) {
545  if (crypto_period_index != current_crypto_period_index + 1) {
546  LOG(ERROR) << "Expecting crypto period index "
547  << current_crypto_period_index << " or "
548  << current_crypto_period_index + 1 << "; Seen "
549  << crypto_period_index << " at track " << i;
550  return false;
551  }
552  if (!PushToKeyPool(&encryption_key_map))
553  return false;
554  ++current_crypto_period_index;
555  }
556  }
557 
558  std::string stream_label;
559  RCHECK(track_dict->GetString("type", &stream_label));
560  RCHECK(encryption_key_map.find(stream_label) == encryption_key_map.end());
561 
562  std::unique_ptr<EncryptionKey> encryption_key(new EncryptionKey());
563 
564  if (!GetKeyFromTrack(*track_dict, &encryption_key->key))
565  return false;
566 
567  // Get key ID and PSSH data for CENC content only.
568  if (!widevine_classic) {
569  if (!GetKeyIdFromTrack(*track_dict, &encryption_key->key_id))
570  return false;
571 
572  ProtectionSystemSpecificInfo info;
573  info.add_key_id(encryption_key->key_id);
574  info.set_system_id(kWidevineSystemId, arraysize(kWidevineSystemId));
575  info.set_pssh_box_version(0);
576 
577  std::vector<uint8_t> pssh_data;
578  if (!GetPsshDataFromTrack(*track_dict, &pssh_data))
579  return false;
580  info.set_pssh_data(pssh_data);
581 
582  encryption_key->key_system_info.push_back(info);
583  }
584  encryption_key_map[stream_label] = std::move(encryption_key);
585  }
586 
587  // If the flag exists, create a common system ID PSSH box that contains the
588  // key IDs of all the keys.
589  if (add_common_pssh_ && !widevine_classic) {
590  std::set<std::vector<uint8_t>> key_ids;
591  for (const EncryptionKeyMap::value_type& pair : encryption_key_map) {
592  key_ids.insert(pair.second->key_id);
593  }
594 
595  // Create a common system PSSH box.
596  ProtectionSystemSpecificInfo info;
597  info.set_system_id(kCommonSystemId, arraysize(kCommonSystemId));
598  info.set_pssh_box_version(1);
599  for (const std::vector<uint8_t>& key_id : key_ids) {
600  info.add_key_id(key_id);
601  }
602 
603  for (const EncryptionKeyMap::value_type& pair : encryption_key_map) {
604  pair.second->key_system_info.push_back(info);
605  }
606  }
607 
608  DCHECK(!encryption_key_map.empty());
609  if (!enable_key_rotation) {
610  // Merge with previously requested keys.
611  for (auto& pair : encryption_key_map)
612  encryption_key_map_[pair.first] = std::move(pair.second);
613  return true;
614  }
615  return PushToKeyPool(&encryption_key_map);
616 }
617 
618 bool WidevineKeySource::PushToKeyPool(
619  EncryptionKeyMap* encryption_key_map) {
620  DCHECK(key_pool_);
621  DCHECK(encryption_key_map);
622  auto encryption_key_map_shared = std::make_shared<EncryptionKeyMap>();
623  encryption_key_map_shared->swap(*encryption_key_map);
624  Status status = key_pool_->Push(encryption_key_map_shared, kInfiniteTimeout);
625  if (!status.ok()) {
626  DCHECK_EQ(error::STOPPED, status.error_code());
627  return false;
628  }
629  return true;
630 }
631 
632 } // namespace media
633 } // namespace shaka
static bool ParseBoxes(const uint8_t *data, size_t data_size, std::vector< ProtectionSystemSpecificInfo > *pssh_boxes)
Status GetKey(const std::string &stream_label, EncryptionKey *key) override
void set_key_fetcher(std::unique_ptr< KeyFetcher > key_fetcher)
WidevineKeySource(const std::string &server_url, bool add_common_pssh)
Status GetCryptoPeriodKey(uint32_t crypto_period_index, const std::string &stream_label, EncryptionKey *key) override
void set_signer(std::unique_ptr< RequestSigner > signer)
Status FetchKeys(EmeInitDataType init_data_type, const std::vector< uint8_t > &init_data) override