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