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