7#include <packager/media/base/widevine_key_source.h>
12#include <absl/base/internal/endian.h>
13#include <absl/flags/flag.h>
14#include <absl/log/check.h>
15#include <absl/strings/escaping.h>
17#include <packager/macros/logging.h>
18#include <packager/media/base/http_key_fetcher.h>
19#include <packager/media/base/producer_consumer_queue.h>
20#include <packager/media/base/protection_system_ids.h>
21#include <packager/media/base/protection_system_specific_info.h>
22#include <packager/media/base/proto_json_util.h>
23#include <packager/media/base/pssh_generator_util.h>
24#include <packager/media/base/rcheck.h>
25#include <packager/media/base/request_signer.h>
26#include <packager/media/base/widevine_common_encryption.pb.h>
31 "Specify the optional video feature, e.g. HDR.");
37const bool kEnableKeyRotation =
true;
41const int kNumTransientErrorRetries = 5;
42const int kFirstRetryDelayMilliseconds = 1000;
46const int kDefaultCryptoPeriodCount = 10;
47const int kGetKeyTimeoutInSeconds = 5 * 60;
48const int kKeyFetchTimeoutInSeconds = 60;
50CommonEncryptionRequest::ProtectionScheme ToCommonEncryptionProtectionScheme(
51 FourCC protection_scheme) {
52 switch (protection_scheme) {
54 return CommonEncryptionRequest::CENC;
56 case kAppleSampleAesProtectionScheme:
58 return CommonEncryptionRequest::CBCS;
60 return CommonEncryptionRequest::CBC1;
62 return CommonEncryptionRequest::CENS;
64 LOG(WARNING) <<
"Ignore unrecognized protection scheme "
65 << FourCCToString(protection_scheme);
66 return CommonEncryptionRequest::UNSPECIFIED;
70ProtectionSystemSpecificInfo ProtectionSystemInfoFromPsshProto(
71 const CommonEncryptionResponse::Track::Pssh& pssh_proto) {
72 PsshBoxBuilder pssh_builder;
73 pssh_builder.set_system_id(kWidevineSystemId, std::size(kWidevineSystemId));
75 if (pssh_proto.has_boxes()) {
76 return {pssh_builder.system_id(),
77 std::vector<uint8_t>(pssh_proto.boxes().begin(),
78 pssh_proto.boxes().end())};
80 pssh_builder.set_pssh_box_version(0);
81 const std::vector<uint8_t> pssh_data(pssh_proto.data().begin(),
82 pssh_proto.data().end());
83 pssh_builder.set_pssh_data(pssh_data);
84 return {pssh_builder.system_id(), pssh_builder.CreateBox()};
91 ProtectionSystem protection_systems,
92 FourCC protection_scheme)
94 : generate_widevine_protection_system_(
97 protection_systems == ProtectionSystem::kNone ||
98 has_flag(protection_systems, ProtectionSystem::kWidevine)),
100 server_url_(server_url),
101 crypto_period_count_(kDefaultCryptoPeriodCount),
102 protection_scheme_(protection_scheme),
103 key_production_thread_(
106WidevineKeySource::~WidevineKeySource() {
111 if (!start_key_production_.HasBeenNotified())
112 start_key_production_.Notify();
113 key_production_thread_.join();
117 const std::string& policy) {
118 absl::MutexLock scoped_lock(mutex_);
119 common_encryption_request_.reset(
new CommonEncryptionRequest);
120 common_encryption_request_->set_content_id(content_id.data(),
122 common_encryption_request_->set_policy(policy);
123 common_encryption_request_->set_protection_scheme(
124 ToCommonEncryptionProtectionScheme(protection_scheme_));
125 if (enable_entitlement_license_)
126 common_encryption_request_->set_enable_entitlement_license(
true);
128 return FetchKeysInternal(!kEnableKeyRotation, 0,
false);
132 const std::vector<uint8_t>& init_data) {
133 std::vector<uint8_t> pssh_data;
134 uint32_t asset_id = 0;
135 switch (init_data_type) {
136 case EmeInitDataType::CENC: {
137 const std::vector<uint8_t> widevine_system_id(
138 kWidevineSystemId, kWidevineSystemId + std::size(kWidevineSystemId));
139 std::vector<ProtectionSystemSpecificInfo> protection_systems_info;
141 init_data.data(), init_data.size(), &protection_systems_info)) {
142 return Status(error::PARSER_FAILURE,
"Error parsing the PSSH boxes.");
144 for (
const auto& info : protection_systems_info) {
145 std::unique_ptr<PsshBoxBuilder> pssh_builder =
148 return Status(error::PARSER_FAILURE,
"Error parsing the PSSH box.");
151 if (info.system_id == widevine_system_id) {
152 pssh_data = pssh_builder->pssh_data();
154 }
else if (pssh_data.empty() && !pssh_builder->key_ids().empty()) {
156 GenerateWidevinePsshDataFromKeyIds(pssh_builder->key_ids());
162 if (pssh_data.empty())
163 return Status(error::INVALID_ARGUMENT,
"No supported PSSHs found.");
166 case EmeInitDataType::WEBM: {
167 pssh_data = GenerateWidevinePsshDataFromKeyIds({init_data});
170 case EmeInitDataType::WIDEVINE_CLASSIC:
171 if (init_data.size() <
sizeof(asset_id))
172 return Status(error::INVALID_ARGUMENT,
"Invalid asset id.");
173 asset_id = absl::big_endian::Load32(init_data.data());
176 LOG(ERROR) <<
"Init data type " <<
static_cast<int>(init_data_type)
177 <<
" not supported.";
178 return Status(error::INVALID_ARGUMENT,
"Unsupported init data type.");
180 const bool widevine_classic =
181 init_data_type == EmeInitDataType::WIDEVINE_CLASSIC;
182 absl::MutexLock scoped_lock(mutex_);
183 common_encryption_request_.reset(
new CommonEncryptionRequest);
184 if (widevine_classic) {
185 common_encryption_request_->set_asset_id(asset_id);
187 common_encryption_request_->set_pssh_data(pssh_data.data(),
190 return FetchKeysInternal(!kEnableKeyRotation, 0, widevine_classic);
196 if (encryption_key_map_.find(stream_label) == encryption_key_map_.end()) {
197 return Status(error::INTERNAL_ERROR,
198 "Cannot find key for '" + stream_label +
"'.");
200 *key = *encryption_key_map_[stream_label];
207 for (
const auto& pair : encryption_key_map_) {
208 if (pair.second->key_id == key_id) {
213 return Status(error::INTERNAL_ERROR,
"Cannot find key with specified key ID");
217 uint32_t crypto_period_index,
218 int32_t crypto_period_duration_in_seconds,
219 const std::string& stream_label,
223 absl::MutexLock scoped_lock(mutex_);
224 if (!key_production_started_) {
225 crypto_period_duration_in_seconds_ = crypto_period_duration_in_seconds;
228 first_crypto_period_index_ =
229 crypto_period_index ? crypto_period_index - 1 : 0;
231 const size_t queue_size = crypto_period_count_ * 10;
234 start_key_production_.Notify();
235 key_production_started_ =
true;
236 }
else if (crypto_period_duration_in_seconds_ !=
237 crypto_period_duration_in_seconds) {
238 return Status(error::INVALID_ARGUMENT,
239 "Crypto period duration should not change.");
242 return GetKeyInternal(crypto_period_index, stream_label, key);
246 signer_ = std::move(signer);
250 std::unique_ptr<KeyFetcher> key_fetcher) {
251 key_fetcher_ = std::move(key_fetcher);
254Status WidevineKeySource::GetKeyInternal(uint32_t crypto_period_index,
255 const std::string& stream_label,
260 std::shared_ptr<EncryptionKeyMap> encryption_key_map;
261 Status status = key_pool_->Peek(crypto_period_index, &encryption_key_map,
262 kGetKeyTimeoutInSeconds * 1000);
264 if (status.error_code() == error::STOPPED) {
265 CHECK(!common_encryption_request_status_.ok());
266 return common_encryption_request_status_;
271 if (encryption_key_map->find(stream_label) == encryption_key_map->end()) {
272 return Status(error::INTERNAL_ERROR,
273 "Cannot find key for '" + stream_label +
"'.");
275 *key = *encryption_key_map->at(stream_label);
279void WidevineKeySource::FetchKeysTask() {
281 start_key_production_.WaitForNotification();
282 if (!key_pool_ || key_pool_->Stopped())
286 FetchKeysInternal(kEnableKeyRotation, first_crypto_period_index_,
false);
287 while (status.ok()) {
288 first_crypto_period_index_ += crypto_period_count_;
289 status = FetchKeysInternal(kEnableKeyRotation, first_crypto_period_index_,
292 common_encryption_request_status_ = status;
296Status WidevineKeySource::FetchKeysInternal(
bool enable_key_rotation,
297 uint32_t first_crypto_period_index,
298 bool widevine_classic) {
299 CommonEncryptionRequest request;
300 FillRequest(enable_key_rotation, first_crypto_period_index, &request);
303 Status status = GenerateKeyMessage(request, &message);
306 VLOG(1) <<
"Message: " << message;
308 std::string raw_response;
309 int64_t sleep_duration = kFirstRetryDelayMilliseconds;
313 for (
int i = 0; i < kNumTransientErrorRetries; ++i) {
314 status = key_fetcher_->FetchKeys(server_url_, message, &raw_response);
316 VLOG(1) <<
"Retry [" << i <<
"] Response:" << raw_response;
318 bool transient_error =
false;
319 if (ExtractEncryptionKey(enable_key_rotation, widevine_classic,
320 raw_response, &transient_error))
323 if (!transient_error) {
326 "Failed to extract encryption key from '" + raw_response +
"'.");
328 }
else if (status.error_code() != error::TIME_OUT) {
333 if (i != kNumTransientErrorRetries - 1) {
334 std::this_thread::sleep_for(std::chrono::milliseconds(sleep_duration));
338 return Status(error::SERVER_ERROR,
339 "Failed to recover from server internal error.");
342void WidevineKeySource::FillRequest(
bool enable_key_rotation,
343 uint32_t first_crypto_period_index,
344 CommonEncryptionRequest* request) {
345 DCHECK(common_encryption_request_);
347 *request = *common_encryption_request_;
349 request->add_tracks()->set_type(
"SD");
350 request->add_tracks()->set_type(
"HD");
351 request->add_tracks()->set_type(
"UHD1");
352 request->add_tracks()->set_type(
"UHD2");
353 request->add_tracks()->set_type(
"AUDIO");
355 request->add_drm_types(ModularDrmType::WIDEVINE);
357 if (enable_key_rotation) {
358 request->set_first_crypto_period_index(first_crypto_period_index);
359 request->set_crypto_period_count(crypto_period_count_);
360 request->set_crypto_period_seconds(crypto_period_duration_in_seconds_);
363 if (!group_id_.empty())
364 request->set_group_id(group_id_.data(), group_id_.size());
366 std::string video_feature = absl::GetFlag(FLAGS_video_feature);
367 if (!video_feature.empty())
368 request->set_video_feature(video_feature);
371Status WidevineKeySource::GenerateKeyMessage(
372 const CommonEncryptionRequest& request,
373 std::string* message) {
376 SignedModularDrmRequest signed_request;
377 signed_request.set_request(MessageToJsonString(request));
381 std::string signature;
382 if (!signer_->GenerateSignature(signed_request.request(), &signature))
383 return Status(error::INTERNAL_ERROR,
"Signature generation failed.");
385 signed_request.set_signature(signature);
386 signed_request.set_signer(signer_->signer_name());
389 *message = MessageToJsonString(signed_request);
393bool WidevineKeySource::ExtractEncryptionKey(
bool enable_key_rotation,
394 bool widevine_classic,
395 const std::string& response,
396 bool* transient_error) {
397 DCHECK(transient_error);
398 *transient_error =
false;
400 SignedModularDrmResponse signed_response_proto;
401 if (!JsonStringToMessage(response, &signed_response_proto)) {
402 LOG(ERROR) <<
"Failed to convert JSON to proto: " << response;
406 CommonEncryptionResponse response_proto;
407 if (!JsonStringToMessage(signed_response_proto.response(), &response_proto)) {
408 LOG(ERROR) <<
"Failed to convert JSON to proto: "
409 << signed_response_proto.response();
413 if (response_proto.status() != CommonEncryptionResponse::OK) {
414 LOG(ERROR) <<
"Received non-OK license response: " << response;
418 (response_proto.status() == CommonEncryptionResponse::INTERNAL_ERROR);
422 RCHECK(enable_key_rotation
423 ? response_proto.tracks_size() >= crypto_period_count_
424 : response_proto.tracks_size() >= 1);
426 uint32_t current_crypto_period_index = first_crypto_period_index_;
428 std::vector<std::vector<uint8_t>> key_ids;
429 for (
const auto& track : response_proto.tracks()) {
430 if (!widevine_classic)
431 key_ids.emplace_back(track.key_id().begin(), track.key_id().end());
434 EncryptionKeyMap encryption_key_map;
435 for (
const auto& track : response_proto.tracks()) {
436 VLOG(2) <<
"track " << track.ShortDebugString();
438 if (enable_key_rotation) {
439 if (track.crypto_period_index() != current_crypto_period_index) {
440 if (track.crypto_period_index() != current_crypto_period_index + 1) {
441 LOG(ERROR) <<
"Expecting crypto period index "
442 << current_crypto_period_index <<
" or "
443 << current_crypto_period_index + 1 <<
"; Seen "
444 << track.crypto_period_index();
447 if (!PushToKeyPool(&encryption_key_map))
449 ++current_crypto_period_index;
453 const std::string& stream_label = track.type();
454 RCHECK(encryption_key_map.find(stream_label) == encryption_key_map.end());
456 std::unique_ptr<EncryptionKey> encryption_key(
new EncryptionKey());
457 encryption_key->key.assign(track.key().begin(), track.key().end());
460 if (!widevine_classic) {
461 encryption_key->key_id.assign(track.key_id().begin(),
462 track.key_id().end());
463 encryption_key->iv.assign(track.iv().begin(), track.iv().end());
464 encryption_key->key_ids = key_ids;
466 if (generate_widevine_protection_system_) {
467 if (track.pssh_size() != 1) {
468 LOG(ERROR) <<
"Expecting one and only one pssh, seeing "
469 << track.pssh_size();
472 encryption_key->key_system_info.push_back(
473 ProtectionSystemInfoFromPsshProto(track.pssh(0)));
476 encryption_key_map[stream_label] = std::move(encryption_key);
479 DCHECK(!encryption_key_map.empty());
480 if (!enable_key_rotation) {
482 for (
auto& pair : encryption_key_map)
483 encryption_key_map_[pair.first] = std::move(pair.second);
487 return PushToKeyPool(&encryption_key_map);
490bool WidevineKeySource::PushToKeyPool(EncryptionKeyMap* encryption_key_map) {
492 DCHECK(encryption_key_map);
493 auto encryption_key_map_shared = std::make_shared<EncryptionKeyMap>();
494 encryption_key_map_shared->swap(*encryption_key_map);
495 Status status = key_pool_->Push(encryption_key_map_shared, kInfiniteTimeout);
497 DCHECK_EQ(error::STOPPED, status.error_code());
All the methods that are virtual are virtual for mocking.