7 #include "packager/media/base/widevine_key_source.h"
9 #include <gflags/gflags.h>
11 #include "packager/base/base64.h"
12 #include "packager/base/bind.h"
13 #include "packager/base/strings/string_number_conversions.h"
14 #include "packager/media/base/http_key_fetcher.h"
15 #include "packager/media/base/network_util.h"
16 #include "packager/media/base/producer_consumer_queue.h"
17 #include "packager/media/base/protection_system_ids.h"
18 #include "packager/media/base/protection_system_specific_info.h"
19 #include "packager/media/base/proto_json_util.h"
20 #include "packager/media/base/pssh_generator_util.h"
21 #include "packager/media/base/rcheck.h"
22 #include "packager/media/base/request_signer.h"
23 #include "packager/media/base/widevine_common_encryption.pb.h"
25 DEFINE_string(video_feature,
27 "Specify the optional video feature, e.g. HDR.");
33 const bool kEnableKeyRotation =
true;
37 const int kNumTransientErrorRetries = 5;
38 const int kFirstRetryDelayMilliseconds = 1000;
42 const int kDefaultCryptoPeriodCount = 10;
43 const int kGetKeyTimeoutInSeconds = 5 * 60;
44 const int kKeyFetchTimeoutInSeconds = 60;
46 CommonEncryptionRequest::ProtectionScheme ToCommonEncryptionProtectionScheme(
47 FourCC protection_scheme) {
48 switch (protection_scheme) {
50 return CommonEncryptionRequest::CENC;
52 case kAppleSampleAesProtectionScheme:
54 return CommonEncryptionRequest::CBCS;
56 return CommonEncryptionRequest::CBC1;
58 return CommonEncryptionRequest::CENS;
60 LOG(WARNING) <<
"Ignore unrecognized protection scheme "
61 << FourCCToString(protection_scheme);
62 return CommonEncryptionRequest::UNSPECIFIED;
66 ProtectionSystemSpecificInfo ProtectionSystemInfoFromPsshProto(
67 const CommonEncryptionResponse::Track::Pssh& pssh_proto) {
68 PsshBoxBuilder pssh_builder;
69 pssh_builder.set_system_id(kWidevineSystemId, arraysize(kWidevineSystemId));
71 if (pssh_proto.has_boxes()) {
72 return {pssh_builder.system_id(),
73 std::vector<uint8_t>(pssh_proto.boxes().begin(),
74 pssh_proto.boxes().end())};
76 pssh_builder.set_pssh_box_version(0);
77 const std::vector<uint8_t> pssh_data(pssh_proto.data().begin(),
78 pssh_proto.data().end());
79 pssh_builder.set_pssh_data(pssh_data);
80 return {pssh_builder.system_id(), pssh_builder.CreateBox()};
88 FourCC protection_scheme)
90 : generate_widevine_protection_system_(
95 key_production_thread_(
"KeyProductionThread",
97 base::Unretained(this))),
99 server_url_(server_url),
100 crypto_period_count_(kDefaultCryptoPeriodCount),
101 protection_scheme_(protection_scheme),
102 start_key_production_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
103 base::WaitableEvent::InitialState::NOT_SIGNALED) {
104 key_production_thread_.Start();
107 WidevineKeySource::~WidevineKeySource() {
110 if (key_production_thread_.HasBeenStarted()) {
113 start_key_production_.Signal();
114 key_production_thread_.Join();
119 const std::string& policy) {
120 base::AutoLock scoped_lock(lock_);
121 common_encryption_request_.reset(
new CommonEncryptionRequest);
122 common_encryption_request_->set_content_id(content_id.data(),
124 common_encryption_request_->set_policy(policy);
125 common_encryption_request_->set_protection_scheme(
126 ToCommonEncryptionProtectionScheme(protection_scheme_));
127 if (enable_entitlement_license_)
128 common_encryption_request_->set_enable_entitlement_license(
true);
130 return FetchKeysInternal(!kEnableKeyRotation, 0,
false);
134 const std::vector<uint8_t>& init_data) {
135 std::vector<uint8_t> pssh_data;
136 uint32_t asset_id = 0;
137 switch (init_data_type) {
138 case EmeInitDataType::CENC: {
139 const std::vector<uint8_t> widevine_system_id(
140 kWidevineSystemId, kWidevineSystemId + arraysize(kWidevineSystemId));
141 std::vector<ProtectionSystemSpecificInfo> protection_systems_info;
143 init_data.data(), init_data.size(), &protection_systems_info)) {
144 return Status(error::PARSER_FAILURE,
"Error parsing the PSSH boxes.");
146 for (
const auto& info : protection_systems_info) {
147 std::unique_ptr<PsshBoxBuilder> pssh_builder =
150 return Status(error::PARSER_FAILURE,
"Error parsing the PSSH box.");
153 if (info.system_id == widevine_system_id) {
154 pssh_data = pssh_builder->pssh_data();
156 }
else if (pssh_data.empty() && !pssh_builder->key_ids().empty()) {
158 GenerateWidevinePsshDataFromKeyIds(pssh_builder->key_ids());
164 if (pssh_data.empty())
165 return Status(error::INVALID_ARGUMENT,
"No supported PSSHs found.");
168 case EmeInitDataType::WEBM: {
169 pssh_data = GenerateWidevinePsshDataFromKeyIds({init_data});
172 case EmeInitDataType::WIDEVINE_CLASSIC:
173 if (init_data.size() <
sizeof(asset_id))
174 return Status(error::INVALID_ARGUMENT,
"Invalid asset id.");
175 asset_id = ntohlFromBuffer(init_data.data());
178 LOG(ERROR) <<
"Init data type " <<
static_cast<int>(init_data_type)
179 <<
" not supported.";
180 return Status(error::INVALID_ARGUMENT,
"Unsupported init data type.");
182 const bool widevine_classic =
183 init_data_type == EmeInitDataType::WIDEVINE_CLASSIC;
184 base::AutoLock scoped_lock(lock_);
185 common_encryption_request_.reset(
new CommonEncryptionRequest);
186 if (widevine_classic) {
187 common_encryption_request_->set_asset_id(asset_id);
189 common_encryption_request_->set_pssh_data(pssh_data.data(),
192 return FetchKeysInternal(!kEnableKeyRotation, 0, widevine_classic);
198 if (encryption_key_map_.find(stream_label) == encryption_key_map_.end()) {
199 return Status(error::INTERNAL_ERROR,
200 "Cannot find key for '" + stream_label +
"'.");
202 *key = *encryption_key_map_[stream_label];
209 for (
const auto& pair : encryption_key_map_) {
210 if (pair.second->key_id == key_id) {
215 return Status(error::INTERNAL_ERROR,
216 "Cannot find key with specified key ID");
220 uint32_t crypto_period_duration_in_seconds,
221 const std::string& stream_label,
223 DCHECK(key_production_thread_.HasBeenStarted());
226 base::AutoLock scoped_lock(lock_);
227 if (!key_production_started_) {
228 crypto_period_duration_in_seconds_ = crypto_period_duration_in_seconds;
231 first_crypto_period_index_ =
232 crypto_period_index ? crypto_period_index - 1 : 0;
234 const size_t queue_size = crypto_period_count_ * 10;
237 start_key_production_.Signal();
238 key_production_started_ =
true;
239 }
else if (crypto_period_duration_in_seconds_ !=
240 crypto_period_duration_in_seconds) {
241 return Status(error::INVALID_ARGUMENT,
242 "Crypto period duration should not change.");
245 return GetKeyInternal(crypto_period_index, stream_label, key);
249 signer_ = std::move(signer);
253 std::unique_ptr<KeyFetcher> key_fetcher) {
254 key_fetcher_ = std::move(key_fetcher);
257 Status WidevineKeySource::GetKeyInternal(uint32_t crypto_period_index,
258 const std::string& stream_label,
263 std::shared_ptr<EncryptionKeyMap> encryption_key_map;
264 Status status = key_pool_->Peek(crypto_period_index, &encryption_key_map,
265 kGetKeyTimeoutInSeconds * 1000);
267 if (status.error_code() == error::STOPPED) {
268 CHECK(!common_encryption_request_status_.ok());
269 return common_encryption_request_status_;
274 if (encryption_key_map->find(stream_label) == encryption_key_map->end()) {
275 return Status(error::INTERNAL_ERROR,
276 "Cannot find key for '" + stream_label +
"'.");
278 *key = *encryption_key_map->at(stream_label);
282 void WidevineKeySource::FetchKeysTask() {
284 start_key_production_.Wait();
285 if (!key_pool_ || key_pool_->Stopped())
288 Status status = FetchKeysInternal(kEnableKeyRotation,
289 first_crypto_period_index_,
291 while (status.ok()) {
292 first_crypto_period_index_ += crypto_period_count_;
293 status = FetchKeysInternal(kEnableKeyRotation,
294 first_crypto_period_index_,
297 common_encryption_request_status_ = status;
301 Status WidevineKeySource::FetchKeysInternal(
bool enable_key_rotation,
302 uint32_t first_crypto_period_index,
303 bool widevine_classic) {
304 CommonEncryptionRequest request;
305 FillRequest(enable_key_rotation, first_crypto_period_index, &request);
308 Status status = GenerateKeyMessage(request, &message);
311 VLOG(1) <<
"Message: " << message;
313 std::string raw_response;
314 int64_t sleep_duration = kFirstRetryDelayMilliseconds;
318 for (
int i = 0; i < kNumTransientErrorRetries; ++i) {
319 status = key_fetcher_->FetchKeys(server_url_, message, &raw_response);
321 VLOG(1) <<
"Retry [" << i <<
"] Response:" << raw_response;
323 bool transient_error =
false;
324 if (ExtractEncryptionKey(enable_key_rotation, widevine_classic,
325 raw_response, &transient_error))
328 if (!transient_error) {
331 "Failed to extract encryption key from '" + raw_response +
"'.");
333 }
else if (status.error_code() != error::TIME_OUT) {
338 if (i != kNumTransientErrorRetries - 1) {
339 base::PlatformThread::Sleep(
340 base::TimeDelta::FromMilliseconds(sleep_duration));
344 return Status(error::SERVER_ERROR,
345 "Failed to recover from server internal error.");
348 void WidevineKeySource::FillRequest(
bool enable_key_rotation,
349 uint32_t first_crypto_period_index,
350 CommonEncryptionRequest* request) {
351 DCHECK(common_encryption_request_);
353 *request = *common_encryption_request_;
355 request->add_tracks()->set_type(
"SD");
356 request->add_tracks()->set_type(
"HD");
357 request->add_tracks()->set_type(
"UHD1");
358 request->add_tracks()->set_type(
"UHD2");
359 request->add_tracks()->set_type(
"AUDIO");
361 request->add_drm_types(ModularDrmType::WIDEVINE);
363 if (enable_key_rotation) {
364 request->set_first_crypto_period_index(first_crypto_period_index);
365 request->set_crypto_period_count(crypto_period_count_);
366 request->set_crypto_period_seconds(crypto_period_duration_in_seconds_);
369 if (!group_id_.empty())
370 request->set_group_id(group_id_.data(), group_id_.size());
372 if (!FLAGS_video_feature.empty())
373 request->set_video_feature(FLAGS_video_feature);
376 Status WidevineKeySource::GenerateKeyMessage(
377 const CommonEncryptionRequest& request,
378 std::string* message) {
381 SignedModularDrmRequest signed_request;
382 signed_request.set_request(MessageToJsonString(request));
386 std::string signature;
387 if (!signer_->GenerateSignature(signed_request.request(), &signature))
388 return Status(error::INTERNAL_ERROR,
"Signature generation failed.");
390 signed_request.set_signature(signature);
391 signed_request.set_signer(signer_->signer_name());
394 *message = MessageToJsonString(signed_request);
398 bool WidevineKeySource::ExtractEncryptionKey(
399 bool enable_key_rotation,
400 bool widevine_classic,
401 const std::string& response,
402 bool* transient_error) {
403 DCHECK(transient_error);
404 *transient_error =
false;
406 SignedModularDrmResponse signed_response_proto;
407 if (!JsonStringToMessage(response, &signed_response_proto)) {
408 LOG(ERROR) <<
"Failed to convert JSON to proto: " << response;
412 CommonEncryptionResponse response_proto;
413 if (!JsonStringToMessage(signed_response_proto.response(), &response_proto)) {
414 LOG(ERROR) <<
"Failed to convert JSON to proto: "
415 << signed_response_proto.response();
419 if (response_proto.status() != CommonEncryptionResponse::OK) {
420 LOG(ERROR) <<
"Received non-OK license response: " << response;
424 (response_proto.status() == CommonEncryptionResponse::INTERNAL_ERROR);
428 RCHECK(enable_key_rotation
429 ? response_proto.tracks_size() >= crypto_period_count_
430 : response_proto.tracks_size() >= 1);
432 uint32_t current_crypto_period_index = first_crypto_period_index_;
434 std::vector<std::vector<uint8_t>> key_ids;
435 for (
const auto& track : response_proto.tracks()) {
436 if (!widevine_classic)
437 key_ids.emplace_back(track.key_id().begin(), track.key_id().end());
440 EncryptionKeyMap encryption_key_map;
441 for (
const auto& track : response_proto.tracks()) {
442 VLOG(2) <<
"track " << track.ShortDebugString();
444 if (enable_key_rotation) {
445 if (track.crypto_period_index() != current_crypto_period_index) {
446 if (track.crypto_period_index() != current_crypto_period_index + 1) {
447 LOG(ERROR) <<
"Expecting crypto period index "
448 << current_crypto_period_index <<
" or "
449 << current_crypto_period_index + 1 <<
"; Seen "
450 << track.crypto_period_index();
453 if (!PushToKeyPool(&encryption_key_map))
455 ++current_crypto_period_index;
459 const std::string& stream_label = track.type();
460 RCHECK(encryption_key_map.find(stream_label) == encryption_key_map.end());
462 std::unique_ptr<EncryptionKey> encryption_key(
new EncryptionKey());
463 encryption_key->key.assign(track.key().begin(), track.key().end());
466 if (!widevine_classic) {
467 encryption_key->key_id.assign(track.key_id().begin(),
468 track.key_id().end());
469 encryption_key->iv.assign(track.iv().begin(), track.iv().end());
470 encryption_key->key_ids = key_ids;
472 if (generate_widevine_protection_system_) {
473 if (track.pssh_size() != 1) {
474 LOG(ERROR) <<
"Expecting one and only one pssh, seeing "
475 << track.pssh_size();
478 encryption_key->key_system_info.push_back(
479 ProtectionSystemInfoFromPsshProto(track.pssh(0)));
482 encryption_key_map[stream_label] = std::move(encryption_key);
485 DCHECK(!encryption_key_map.empty());
486 if (!enable_key_rotation) {
488 for (
auto& pair : encryption_key_map)
489 encryption_key_map_[pair.first] = std::move(pair.second);
493 return PushToKeyPool(&encryption_key_map);
496 bool WidevineKeySource::PushToKeyPool(
497 EncryptionKeyMap* encryption_key_map) {
499 DCHECK(encryption_key_map);
500 auto encryption_key_map_shared = std::make_shared<EncryptionKeyMap>();
501 encryption_key_map_shared->swap(*encryption_key_map);
502 Status status = key_pool_->Push(encryption_key_map_shared, kInfiniteTimeout);
504 DCHECK_EQ(error::STOPPED, status.error_code());
All the methods that are virtual are virtual for mocking.