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(TrackType track_type, EncryptionKey* key) {
241  DCHECK(key);
242  if (encryption_key_map_.find(track_type) == encryption_key_map_.end()) {
243  return Status(error::INTERNAL_ERROR,
244  "Cannot find key of type " + TrackTypeToString(track_type));
245  }
246  *key = *encryption_key_map_[track_type];
247  return Status::OK;
248 }
249 
250 Status WidevineKeySource::GetKey(const std::vector<uint8_t>& key_id,
251  EncryptionKey* key) {
252  DCHECK(key);
253  for (const auto& pair : encryption_key_map_) {
254  if (pair.second->key_id == key_id) {
255  *key = *pair.second;
256  return Status::OK;
257  }
258  }
259  return Status(error::INTERNAL_ERROR,
260  "Cannot find key with specified key ID");
261 }
262 
263 Status WidevineKeySource::GetCryptoPeriodKey(uint32_t crypto_period_index,
264  TrackType track_type,
265  EncryptionKey* key) {
266  DCHECK(key_production_thread_.HasBeenStarted());
267  // TODO(kqyang): This is not elegant. Consider refactoring later.
268  {
269  base::AutoLock scoped_lock(lock_);
270  if (!key_production_started_) {
271  // Another client may have a slightly smaller starting crypto period
272  // index. Set the initial value to account for that.
273  first_crypto_period_index_ =
274  crypto_period_index ? crypto_period_index - 1 : 0;
275  DCHECK(!key_pool_);
276  key_pool_.reset(new EncryptionKeyQueue(crypto_period_count_,
277  first_crypto_period_index_));
278  start_key_production_.Signal();
279  key_production_started_ = true;
280  }
281  }
282  return GetKeyInternal(crypto_period_index, track_type, key);
283 }
284 
285 void WidevineKeySource::set_signer(std::unique_ptr<RequestSigner> signer) {
286  signer_ = std::move(signer);
287 }
288 
290  std::unique_ptr<KeyFetcher> key_fetcher) {
291  key_fetcher_ = std::move(key_fetcher);
292 }
293 
294 Status WidevineKeySource::GetKeyInternal(uint32_t crypto_period_index,
295  TrackType track_type,
296  EncryptionKey* key) {
297  DCHECK(key_pool_);
298  DCHECK(key);
299  DCHECK_LE(track_type, NUM_VALID_TRACK_TYPES);
300  DCHECK_NE(track_type, TRACK_TYPE_UNKNOWN);
301 
302  std::shared_ptr<EncryptionKeyMap> encryption_key_map;
303  Status status = key_pool_->Peek(crypto_period_index, &encryption_key_map,
304  kGetKeyTimeoutInSeconds * 1000);
305  if (!status.ok()) {
306  if (status.error_code() == error::STOPPED) {
307  CHECK(!common_encryption_request_status_.ok());
308  return common_encryption_request_status_;
309  }
310  return status;
311  }
312 
313  if (encryption_key_map->find(track_type) == encryption_key_map->end()) {
314  return Status(error::INTERNAL_ERROR,
315  "Cannot find key of type " + TrackTypeToString(track_type));
316  }
317  *key = *encryption_key_map->at(track_type);
318  return Status::OK;
319 }
320 
321 void WidevineKeySource::FetchKeysTask() {
322  // Wait until key production is signaled.
323  start_key_production_.Wait();
324  if (!key_pool_ || key_pool_->Stopped())
325  return;
326 
327  Status status = FetchKeysInternal(kEnableKeyRotation,
328  first_crypto_period_index_,
329  false);
330  while (status.ok()) {
331  first_crypto_period_index_ += crypto_period_count_;
332  status = FetchKeysInternal(kEnableKeyRotation,
333  first_crypto_period_index_,
334  false);
335  }
336  common_encryption_request_status_ = status;
337  key_pool_->Stop();
338 }
339 
340 Status WidevineKeySource::FetchKeysInternal(bool enable_key_rotation,
341  uint32_t first_crypto_period_index,
342  bool widevine_classic) {
343  std::string request;
344  FillRequest(enable_key_rotation,
345  first_crypto_period_index,
346  &request);
347 
348  std::string message;
349  Status status = GenerateKeyMessage(request, &message);
350  if (!status.ok())
351  return status;
352  VLOG(1) << "Message: " << message;
353 
354  std::string raw_response;
355  int64_t sleep_duration = kFirstRetryDelayMilliseconds;
356 
357  // Perform client side retries if seeing server transient error to workaround
358  // server limitation.
359  for (int i = 0; i < kNumTransientErrorRetries; ++i) {
360  status = key_fetcher_->FetchKeys(server_url_, message, &raw_response);
361  if (status.ok()) {
362  VLOG(1) << "Retry [" << i << "] Response:" << raw_response;
363 
364  std::string response;
365  if (!DecodeResponse(raw_response, &response)) {
366  return Status(error::SERVER_ERROR,
367  "Failed to decode response '" + raw_response + "'.");
368  }
369 
370  bool transient_error = false;
371  if (ExtractEncryptionKey(enable_key_rotation,
372  widevine_classic,
373  response,
374  &transient_error))
375  return Status::OK;
376 
377  if (!transient_error) {
378  return Status(
379  error::SERVER_ERROR,
380  "Failed to extract encryption key from '" + response + "'.");
381  }
382  } else if (status.error_code() != error::TIME_OUT) {
383  return status;
384  }
385 
386  // Exponential backoff.
387  if (i != kNumTransientErrorRetries - 1) {
388  base::PlatformThread::Sleep(
389  base::TimeDelta::FromMilliseconds(sleep_duration));
390  sleep_duration *= 2;
391  }
392  }
393  return Status(error::SERVER_ERROR,
394  "Failed to recover from server internal error.");
395 }
396 
397 void WidevineKeySource::FillRequest(bool enable_key_rotation,
398  uint32_t first_crypto_period_index,
399  std::string* request) {
400  DCHECK(request);
401  DCHECK(!request_dict_.empty());
402 
403  // Build tracks.
404  base::ListValue* tracks = new base::ListValue();
405 
406  base::DictionaryValue* track_sd = new base::DictionaryValue();
407  track_sd->SetString("type", "SD");
408  tracks->Append(track_sd);
409  base::DictionaryValue* track_hd = new base::DictionaryValue();
410  track_hd->SetString("type", "HD");
411  tracks->Append(track_hd);
412  base::DictionaryValue* track_uhd1 = new base::DictionaryValue();
413  track_uhd1->SetString("type", "UHD1");
414  tracks->Append(track_uhd1);
415  base::DictionaryValue* track_uhd2 = new base::DictionaryValue();
416  track_uhd2->SetString("type", "UHD2");
417  tracks->Append(track_uhd2);
418  base::DictionaryValue* track_audio = new base::DictionaryValue();
419  track_audio->SetString("type", "AUDIO");
420  tracks->Append(track_audio);
421 
422  request_dict_.Set("tracks", tracks);
423 
424  // Build DRM types.
425  base::ListValue* drm_types = new base::ListValue();
426  drm_types->AppendString("WIDEVINE");
427  request_dict_.Set("drm_types", drm_types);
428 
429  // Build key rotation fields.
430  if (enable_key_rotation) {
431  // Javascript/JSON does not support int64_t or unsigned numbers. Use double
432  // instead as 32-bit integer can be lossless represented using double.
433  request_dict_.SetDouble("first_crypto_period_index",
434  first_crypto_period_index);
435  request_dict_.SetInteger("crypto_period_count", crypto_period_count_);
436  }
437 
438  base::JSONWriter::WriteWithOptions(
439  request_dict_,
440  // Write doubles that have no fractional part as a normal integer, i.e.
441  // without using exponential notation or appending a '.0'.
442  base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION, request);
443 }
444 
445 Status WidevineKeySource::GenerateKeyMessage(const std::string& request,
446  std::string* message) {
447  DCHECK(message);
448 
449  std::string request_base64_string;
450  base::Base64Encode(request, &request_base64_string);
451 
452  base::DictionaryValue request_dict;
453  request_dict.SetString("request", request_base64_string);
454 
455  // Sign the request.
456  if (signer_) {
457  std::string signature;
458  if (!signer_->GenerateSignature(request, &signature))
459  return Status(error::INTERNAL_ERROR, "Signature generation failed.");
460 
461  std::string signature_base64_string;
462  base::Base64Encode(signature, &signature_base64_string);
463 
464  request_dict.SetString("signature", signature_base64_string);
465  request_dict.SetString("signer", signer_->signer_name());
466  }
467 
468  base::JSONWriter::Write(request_dict, message);
469  return Status::OK;
470 }
471 
472 bool WidevineKeySource::DecodeResponse(
473  const std::string& raw_response,
474  std::string* response) {
475  DCHECK(response);
476 
477  // Extract base64 formatted response from JSON formatted raw response.
478  std::unique_ptr<base::Value> root(base::JSONReader::Read(raw_response));
479  if (!root) {
480  LOG(ERROR) << "'" << raw_response << "' is not in JSON format.";
481  return false;
482  }
483  const base::DictionaryValue* response_dict = NULL;
484  RCHECK(root->GetAsDictionary(&response_dict));
485 
486  std::string response_base64_string;
487  RCHECK(response_dict->GetString("response", &response_base64_string));
488  RCHECK(base::Base64Decode(response_base64_string, response));
489  return true;
490 }
491 
492 bool WidevineKeySource::ExtractEncryptionKey(
493  bool enable_key_rotation,
494  bool widevine_classic,
495  const std::string& response,
496  bool* transient_error) {
497  DCHECK(transient_error);
498  *transient_error = false;
499 
500  std::unique_ptr<base::Value> root(base::JSONReader::Read(response));
501  if (!root) {
502  LOG(ERROR) << "'" << response << "' is not in JSON format.";
503  return false;
504  }
505 
506  const base::DictionaryValue* license_dict = NULL;
507  RCHECK(root->GetAsDictionary(&license_dict));
508 
509  std::string license_status;
510  RCHECK(license_dict->GetString("status", &license_status));
511  if (license_status != kLicenseStatusOK) {
512  LOG(ERROR) << "Received non-OK license response: " << response;
513  *transient_error = (license_status == kLicenseStatusTransientError);
514  return false;
515  }
516 
517  const base::ListValue* tracks;
518  RCHECK(license_dict->GetList("tracks", &tracks));
519  // Should have at least one track per crypto_period.
520  RCHECK(enable_key_rotation ? tracks->GetSize() >= 1 * crypto_period_count_
521  : tracks->GetSize() >= 1);
522 
523  int current_crypto_period_index = first_crypto_period_index_;
524 
525  EncryptionKeyMap encryption_key_map;
526  for (size_t i = 0; i < tracks->GetSize(); ++i) {
527  const base::DictionaryValue* track_dict;
528  RCHECK(tracks->GetDictionary(i, &track_dict));
529 
530  if (enable_key_rotation) {
531  int crypto_period_index;
532  RCHECK(
533  track_dict->GetInteger("crypto_period_index", &crypto_period_index));
534  if (crypto_period_index != current_crypto_period_index) {
535  if (crypto_period_index != current_crypto_period_index + 1) {
536  LOG(ERROR) << "Expecting crypto period index "
537  << current_crypto_period_index << " or "
538  << current_crypto_period_index + 1 << "; Seen "
539  << crypto_period_index << " at track " << i;
540  return false;
541  }
542  if (!PushToKeyPool(&encryption_key_map))
543  return false;
544  ++current_crypto_period_index;
545  }
546  }
547 
548  std::string track_type_str;
549  RCHECK(track_dict->GetString("type", &track_type_str));
550  TrackType track_type = GetTrackTypeFromString(track_type_str);
551  DCHECK_NE(TRACK_TYPE_UNKNOWN, track_type);
552  RCHECK(encryption_key_map.find(track_type) == encryption_key_map.end());
553 
554  std::unique_ptr<EncryptionKey> encryption_key(new EncryptionKey());
555 
556  if (!GetKeyFromTrack(*track_dict, &encryption_key->key))
557  return false;
558 
559  // Get key ID and PSSH data for CENC content only.
560  if (!widevine_classic) {
561  if (!GetKeyIdFromTrack(*track_dict, &encryption_key->key_id))
562  return false;
563 
564  ProtectionSystemSpecificInfo info;
565  info.add_key_id(encryption_key->key_id);
566  info.set_system_id(kWidevineSystemId, arraysize(kWidevineSystemId));
567  info.set_pssh_box_version(0);
568 
569  std::vector<uint8_t> pssh_data;
570  if (!GetPsshDataFromTrack(*track_dict, &pssh_data))
571  return false;
572  info.set_pssh_data(pssh_data);
573 
574  encryption_key->key_system_info.push_back(info);
575  }
576  encryption_key_map[track_type] = std::move(encryption_key);
577  }
578 
579  // If the flag exists, create a common system ID PSSH box that contains the
580  // key IDs of all the keys.
581  if (add_common_pssh_ && !widevine_classic) {
582  std::set<std::vector<uint8_t>> key_ids;
583  for (const EncryptionKeyMap::value_type& pair : encryption_key_map) {
584  key_ids.insert(pair.second->key_id);
585  }
586 
587  // Create a common system PSSH box.
588  ProtectionSystemSpecificInfo info;
589  info.set_system_id(kCommonSystemId, arraysize(kCommonSystemId));
590  info.set_pssh_box_version(1);
591  for (const std::vector<uint8_t>& key_id : key_ids) {
592  info.add_key_id(key_id);
593  }
594 
595  for (const EncryptionKeyMap::value_type& pair : encryption_key_map) {
596  pair.second->key_system_info.push_back(info);
597  }
598  }
599 
600  DCHECK(!encryption_key_map.empty());
601  if (!enable_key_rotation) {
602  // Merge with previously requested keys.
603  for (auto& pair : encryption_key_map)
604  encryption_key_map_[pair.first] = std::move(pair.second);
605  return true;
606  }
607  return PushToKeyPool(&encryption_key_map);
608 }
609 
610 bool WidevineKeySource::PushToKeyPool(
611  EncryptionKeyMap* encryption_key_map) {
612  DCHECK(key_pool_);
613  DCHECK(encryption_key_map);
614  auto encryption_key_map_shared = std::make_shared<EncryptionKeyMap>();
615  encryption_key_map_shared->swap(*encryption_key_map);
616  Status status = key_pool_->Push(encryption_key_map_shared, kInfiniteTimeout);
617  if (!status.ok()) {
618  DCHECK_EQ(error::STOPPED, status.error_code());
619  return false;
620  }
621  return true;
622 }
623 
624 } // namespace media
625 } // namespace shaka
Status GetCryptoPeriodKey(uint32_t crypto_period_index, TrackType track_type, EncryptionKey *key) override
Status GetKey(TrackType track_type, EncryptionKey *key) override
static std::string TrackTypeToString(TrackType track_type)
Convert TrackType to string.
Definition: key_source.cc:37
static bool ParseBoxes(const uint8_t *data, size_t data_size, std::vector< ProtectionSystemSpecificInfo > *pssh_boxes)
static TrackType GetTrackTypeFromString(const std::string &track_type_string)
Convert string representation of track type to enum representation.
Definition: key_source.cc:19
void set_key_fetcher(std::unique_ptr< KeyFetcher > key_fetcher)
WidevineKeySource(const std::string &server_url, bool add_common_pssh)
void set_signer(std::unique_ptr< RequestSigner > signer)
Status FetchKeys(EmeInitDataType init_data_type, const std::vector< uint8_t > &init_data) override