Refactor KeySource::FetchKeys to use EME init data types
Change-Id: I517ea992a8868d7382988a8837f4f05df9f974d7
This commit is contained in:
parent
9d101f85e3
commit
fb0790e1d5
|
@ -15,18 +15,8 @@ namespace media {
|
||||||
|
|
||||||
FixedKeySource::~FixedKeySource() {}
|
FixedKeySource::~FixedKeySource() {}
|
||||||
|
|
||||||
Status FixedKeySource::FetchKeys(const std::vector<uint8_t>& pssh_box) {
|
Status FixedKeySource::FetchKeys(EmeInitDataType init_data_type,
|
||||||
// Do nothing for fixed key encryption/decryption.
|
const std::vector<uint8_t>& init_data) {
|
||||||
return Status::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status FixedKeySource::FetchKeys(
|
|
||||||
const std::vector<std::vector<uint8_t>>& key_ids) {
|
|
||||||
// Do nothing for fixed key encryption/decryption.
|
|
||||||
return Status::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status FixedKeySource::FetchKeys(uint32_t asset_id) {
|
|
||||||
// Do nothing for fixed key encryption/decryption.
|
// Do nothing for fixed key encryption/decryption.
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,10 +30,8 @@ class FixedKeySource : public KeySource {
|
||||||
|
|
||||||
/// @name KeySource implementation overrides.
|
/// @name KeySource implementation overrides.
|
||||||
/// @{
|
/// @{
|
||||||
Status FetchKeys(const std::vector<uint8_t>& pssh_box) override;
|
Status FetchKeys(EmeInitDataType init_data_type,
|
||||||
Status FetchKeys(const std::vector<std::vector<uint8_t>>& key_ids) override;
|
const std::vector<uint8_t>& init_data) override;
|
||||||
Status FetchKeys(uint32_t asset_id) override;
|
|
||||||
|
|
||||||
Status GetKey(TrackType track_type, EncryptionKey* key) override;
|
Status GetKey(TrackType track_type, EncryptionKey* key) override;
|
||||||
Status GetKey(const std::vector<uint8_t>& key_id,
|
Status GetKey(const std::vector<uint8_t>& key_id,
|
||||||
EncryptionKey* key) override;
|
EncryptionKey* key) override;
|
||||||
|
|
|
@ -16,6 +16,21 @@
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
|
/// Encrypted media init data types. It is extended from:
|
||||||
|
/// https://www.w3.org/TR/eme-initdata-registry/#registry.
|
||||||
|
enum class EmeInitDataType {
|
||||||
|
UNKNOWN,
|
||||||
|
/// One or multiple PSSH boxes.
|
||||||
|
CENC,
|
||||||
|
/// WebM init data is basically KeyId.
|
||||||
|
WEBM,
|
||||||
|
/// JSON formatted key ids.
|
||||||
|
KEYIDS,
|
||||||
|
/// Widevine classic asset id.
|
||||||
|
WIDEVINE_CLASSIC,
|
||||||
|
MAX = WIDEVINE_CLASSIC
|
||||||
|
};
|
||||||
|
|
||||||
struct EncryptionKey {
|
struct EncryptionKey {
|
||||||
EncryptionKey();
|
EncryptionKey();
|
||||||
~EncryptionKey();
|
~EncryptionKey();
|
||||||
|
@ -43,22 +58,12 @@ class KeySource {
|
||||||
KeySource();
|
KeySource();
|
||||||
virtual ~KeySource();
|
virtual ~KeySource();
|
||||||
|
|
||||||
/// Fetch keys for CENC from the key server.
|
/// Fetch keys based on the specified encrypted media init data.
|
||||||
/// @param pssh_box The entire PSSH box for the content to be decrypted
|
/// @param init_data_type specifies the encrypted media init data type.
|
||||||
|
/// @param init_data contains the init data.
|
||||||
/// @return OK on success, an error status otherwise.
|
/// @return OK on success, an error status otherwise.
|
||||||
virtual Status FetchKeys(const std::vector<uint8_t>& pssh_box) = 0;
|
virtual Status FetchKeys(EmeInitDataType init_data_type,
|
||||||
|
const std::vector<uint8_t>& init_data) = 0;
|
||||||
/// Fetch keys for CENC from the key server.
|
|
||||||
/// @param key_ids the key IDs for the keys to fetch from the server.
|
|
||||||
/// @return OK on success, an error status otherwise.
|
|
||||||
virtual Status FetchKeys(
|
|
||||||
const std::vector<std::vector<uint8_t>>& key_ids) = 0;
|
|
||||||
|
|
||||||
/// Fetch keys for WVM decryption from the key server.
|
|
||||||
/// @param asset_id is the Widevine Classic asset ID for the content to be
|
|
||||||
/// decrypted.
|
|
||||||
/// @return OK on success, an error status otherwise.
|
|
||||||
virtual Status FetchKeys(uint32_t asset_id) = 0;
|
|
||||||
|
|
||||||
/// Get encryption key of the specified track type.
|
/// Get encryption key of the specified track type.
|
||||||
/// @param track_type is the type of track for which retrieving the key.
|
/// @param track_type is the type of track for which retrieving the key.
|
||||||
|
|
|
@ -310,19 +310,9 @@ Status PlayReadyKeySource::FetchKeysWithProgramIdentifier(
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status PlayReadyKeySource::FetchKeys(const std::vector<uint8_t>& pssh_box) {
|
Status PlayReadyKeySource::FetchKeys(EmeInitDataType init_data_type,
|
||||||
// Does nothing for playready encryption/decryption.
|
const std::vector<uint8_t>& init_data) {
|
||||||
return Status::OK;
|
// Do nothing for playready encryption/decryption.
|
||||||
}
|
|
||||||
|
|
||||||
Status PlayReadyKeySource::FetchKeys(
|
|
||||||
const std::vector<std::vector<uint8_t>>& key_ids) {
|
|
||||||
// Does nothing for playready encryption/decryption.
|
|
||||||
return Status::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status PlayReadyKeySource::FetchKeys(uint32_t asset_id) {
|
|
||||||
// Does nothing for playready encryption/decryption.
|
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,10 +40,8 @@ class PlayReadyKeySource : public KeySource {
|
||||||
|
|
||||||
/// @name KeySource implementation overrides.
|
/// @name KeySource implementation overrides.
|
||||||
/// @{
|
/// @{
|
||||||
Status FetchKeys(const std::vector<uint8_t>& pssh_box) override;
|
Status FetchKeys(EmeInitDataType init_data_type,
|
||||||
Status FetchKeys(const std::vector<std::vector<uint8_t>>& key_ids) override;
|
const std::vector<uint8_t>& init_data) override;
|
||||||
Status FetchKeys(uint32_t asset_id) override;
|
|
||||||
|
|
||||||
Status GetKey(TrackType track_type, EncryptionKey* key) override;
|
Status GetKey(TrackType track_type, EncryptionKey* key) override;
|
||||||
Status GetKey(const std::vector<uint8_t>& key_id,
|
Status GetKey(const std::vector<uint8_t>& key_id,
|
||||||
EncryptionKey* key) override;
|
EncryptionKey* key) override;
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "packager/base/json/json_writer.h"
|
#include "packager/base/json/json_writer.h"
|
||||||
#include "packager/media/base/fixed_key_source.h"
|
#include "packager/media/base/fixed_key_source.h"
|
||||||
#include "packager/media/base/http_key_fetcher.h"
|
#include "packager/media/base/http_key_fetcher.h"
|
||||||
|
#include "packager/media/base/network_util.h"
|
||||||
#include "packager/media/base/producer_consumer_queue.h"
|
#include "packager/media/base/producer_consumer_queue.h"
|
||||||
#include "packager/media/base/protection_system_specific_info.h"
|
#include "packager/media/base/protection_system_specific_info.h"
|
||||||
#include "packager/media/base/rcheck.h"
|
#include "packager/media/base/rcheck.h"
|
||||||
|
@ -41,6 +42,18 @@ const int kDefaultCryptoPeriodCount = 10;
|
||||||
const int kGetKeyTimeoutInSeconds = 5 * 60; // 5 minutes.
|
const int kGetKeyTimeoutInSeconds = 5 * 60; // 5 minutes.
|
||||||
const int kKeyFetchTimeoutInSeconds = 60; // 1 minute.
|
const int kKeyFetchTimeoutInSeconds = 60; // 1 minute.
|
||||||
|
|
||||||
|
std::vector<uint8_t> StringToBytes(const std::string& string) {
|
||||||
|
return std::vector<uint8_t>(string.begin(), string.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> WidevinePsshFromKeyId(
|
||||||
|
const std::vector<std::vector<uint8_t>>& key_ids) {
|
||||||
|
media::WidevinePsshData widevine_pssh_data;
|
||||||
|
for (const std::vector<uint8_t>& key_id : key_ids)
|
||||||
|
widevine_pssh_data.add_key_id(key_id.data(), key_id.size());
|
||||||
|
return StringToBytes(widevine_pssh_data.SerializeAsString());
|
||||||
|
}
|
||||||
|
|
||||||
bool Base64StringToBytes(const std::string& base64_string,
|
bool Base64StringToBytes(const std::string& base64_string,
|
||||||
std::vector<uint8_t>* bytes) {
|
std::vector<uint8_t>* bytes) {
|
||||||
DCHECK(bytes);
|
DCHECK(bytes);
|
||||||
|
@ -147,61 +160,63 @@ Status WidevineKeySource::FetchKeys(const std::vector<uint8_t>& content_id,
|
||||||
return FetchKeysInternal(!kEnableKeyRotation, 0, false);
|
return FetchKeysInternal(!kEnableKeyRotation, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Status WidevineKeySource::FetchKeys(const std::vector<uint8_t>& pssh_box) {
|
Status WidevineKeySource::FetchKeys(EmeInitDataType init_data_type,
|
||||||
const std::vector<uint8_t> widevine_system_id(
|
const std::vector<uint8_t>& init_data) {
|
||||||
kWidevineSystemId, kWidevineSystemId + arraysize(kWidevineSystemId));
|
std::vector<uint8_t> pssh_data;
|
||||||
|
uint32_t asset_id = 0;
|
||||||
ProtectionSystemSpecificInfo info;
|
switch (init_data_type) {
|
||||||
if (!info.Parse(pssh_box.data(), pssh_box.size()))
|
case EmeInitDataType::CENC: {
|
||||||
return Status(error::PARSER_FAILURE, "Error parsing the PSSH box.");
|
const std::vector<uint8_t> widevine_system_id(
|
||||||
|
kWidevineSystemId, kWidevineSystemId + arraysize(kWidevineSystemId));
|
||||||
if (info.system_id() == widevine_system_id) {
|
std::vector<ProtectionSystemSpecificInfo> protection_systems_info;
|
||||||
base::AutoLock scoped_lock(lock_);
|
if (!ProtectionSystemSpecificInfo::ParseBoxes(
|
||||||
request_dict_.Clear();
|
init_data.data(), init_data.size(), &protection_systems_info)) {
|
||||||
std::string pssh_data_base64_string;
|
return Status(error::PARSER_FAILURE, "Error parsing the PSSH boxes.");
|
||||||
|
}
|
||||||
BytesToBase64String(info.pssh_data(), &pssh_data_base64_string);
|
for (const auto& info: protection_systems_info) {
|
||||||
request_dict_.SetString("pssh_data", pssh_data_base64_string);
|
// Use Widevine PSSH if available otherwise construct a Widevine PSSH
|
||||||
return FetchKeysInternal(!kEnableKeyRotation, 0, false);
|
// from the first available key ids.
|
||||||
} else if (!info.key_ids().empty()) {
|
if (info.system_id() == widevine_system_id) {
|
||||||
// This is not a Widevine PSSH box. Try making the request for the key-IDs.
|
pssh_data = info.pssh_data();
|
||||||
// Even if this is a different key-system, it should still work. Either
|
break;
|
||||||
// the server will not recognize it and return an error, or it will
|
} else if (pssh_data.empty() && !info.key_ids().empty()) {
|
||||||
// recognize it and the key must be correct (or the content is bad).
|
pssh_data = WidevinePsshFromKeyId(info.key_ids());
|
||||||
return FetchKeys(info.key_ids());
|
// Continue to see if there is any Widevine PSSH. The KeyId generated
|
||||||
|
// PSSH is only used if a Widevine PSSH could not be found.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pssh_data.empty())
|
||||||
|
return Status(error::INVALID_ARGUMENT, "No supported PSSHs found.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case EmeInitDataType::WEBM:
|
||||||
|
pssh_data = WidevinePsshFromKeyId({init_data});
|
||||||
|
break;
|
||||||
|
case EmeInitDataType::WIDEVINE_CLASSIC:
|
||||||
|
if (init_data.size() < sizeof(asset_id))
|
||||||
|
return Status(error::INVALID_ARGUMENT, "Invalid asset id.");
|
||||||
|
asset_id = ntohlFromBuffer(init_data.data());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG(ERROR) << "Init data type " << static_cast<int>(init_data_type)
|
||||||
|
<< " not supported.";
|
||||||
|
return Status(error::INVALID_ARGUMENT, "Unsupported init data type.");
|
||||||
|
}
|
||||||
|
const bool widevine_classic =
|
||||||
|
init_data_type == EmeInitDataType::WIDEVINE_CLASSIC;
|
||||||
|
base::AutoLock scoped_lock(lock_);
|
||||||
|
request_dict_.Clear();
|
||||||
|
if (widevine_classic) {
|
||||||
|
// Javascript/JSON does not support int64_t or unsigned numbers. Use double
|
||||||
|
// instead as 32-bit integer can be lossless represented using double.
|
||||||
|
request_dict_.SetDouble("asset_id", asset_id);
|
||||||
} else {
|
} else {
|
||||||
return Status(error::NOT_FOUND, "No key IDs given in PSSH box.");
|
std::string pssh_data_base64_string;
|
||||||
|
BytesToBase64String(pssh_data, &pssh_data_base64_string);
|
||||||
|
request_dict_.SetString("pssh_data", pssh_data_base64_string);
|
||||||
}
|
}
|
||||||
}
|
return FetchKeysInternal(!kEnableKeyRotation, 0, widevine_classic);
|
||||||
|
|
||||||
Status WidevineKeySource::FetchKeys(
|
|
||||||
const std::vector<std::vector<uint8_t>>& key_ids) {
|
|
||||||
base::AutoLock scoped_lock(lock_);
|
|
||||||
request_dict_.Clear();
|
|
||||||
std::string pssh_data_base64_string;
|
|
||||||
|
|
||||||
// Generate Widevine PSSH data from the key-IDs.
|
|
||||||
WidevinePsshData widevine_pssh_data;
|
|
||||||
for (size_t i = 0; i < key_ids.size(); i++) {
|
|
||||||
widevine_pssh_data.add_key_id(key_ids[i].data(), key_ids[i].size());
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string serialized_string = widevine_pssh_data.SerializeAsString();
|
|
||||||
std::vector<uint8_t> pssh_data(serialized_string.begin(),
|
|
||||||
serialized_string.end());
|
|
||||||
|
|
||||||
BytesToBase64String(pssh_data, &pssh_data_base64_string);
|
|
||||||
request_dict_.SetString("pssh_data", pssh_data_base64_string);
|
|
||||||
return FetchKeysInternal(!kEnableKeyRotation, 0, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Status WidevineKeySource::FetchKeys(uint32_t asset_id) {
|
|
||||||
base::AutoLock scoped_lock(lock_);
|
|
||||||
request_dict_.Clear();
|
|
||||||
// Javascript/JSON does not support int64_t or unsigned numbers. Use double
|
|
||||||
// instead as 32-bit integer can be lossless represented using double.
|
|
||||||
request_dict_.SetDouble("asset_id", asset_id);
|
|
||||||
return FetchKeysInternal(!kEnableKeyRotation, 0, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Status WidevineKeySource::GetKey(TrackType track_type, EncryptionKey* key) {
|
Status WidevineKeySource::GetKey(TrackType track_type, EncryptionKey* key) {
|
||||||
|
@ -566,7 +581,9 @@ bool WidevineKeySource::ExtractEncryptionKey(
|
||||||
|
|
||||||
DCHECK(!encryption_key_map.empty());
|
DCHECK(!encryption_key_map.empty());
|
||||||
if (!enable_key_rotation) {
|
if (!enable_key_rotation) {
|
||||||
encryption_key_map_.swap(encryption_key_map);
|
// Merge with previously requested keys.
|
||||||
|
for (auto& pair : encryption_key_map)
|
||||||
|
encryption_key_map_[pair.first] = std::move(pair.second);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return PushToKeyPool(&encryption_key_map);
|
return PushToKeyPool(&encryption_key_map);
|
||||||
|
|
|
@ -36,10 +36,8 @@ class WidevineKeySource : public KeySource {
|
||||||
|
|
||||||
/// @name KeySource implementation overrides.
|
/// @name KeySource implementation overrides.
|
||||||
/// @{
|
/// @{
|
||||||
Status FetchKeys(const std::vector<uint8_t>& pssh_box) override;
|
Status FetchKeys(EmeInitDataType init_data_type,
|
||||||
Status FetchKeys(const std::vector<std::vector<uint8_t>>& key_ids) override;
|
const std::vector<uint8_t>& init_data) override;
|
||||||
Status FetchKeys(uint32_t asset_id) override;
|
|
||||||
|
|
||||||
Status GetKey(TrackType track_type, EncryptionKey* key) override;
|
Status GetKey(TrackType track_type, EncryptionKey* key) override;
|
||||||
Status GetKey(const std::vector<uint8_t>& key_id,
|
Status GetKey(const std::vector<uint8_t>& key_id,
|
||||||
EncryptionKey* key) override;
|
EncryptionKey* key) override;
|
||||||
|
|
|
@ -75,6 +75,7 @@ const uint8_t kRequestKeyId[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
|
||||||
// 32-bit with leading bit set, to verify that big uint32_t can be handled
|
// 32-bit with leading bit set, to verify that big uint32_t can be handled
|
||||||
// correctly.
|
// correctly.
|
||||||
const uint32_t kClassicAssetId = 0x80038cd9;
|
const uint32_t kClassicAssetId = 0x80038cd9;
|
||||||
|
const uint8_t kClassicAssetIdBytes[] = {0x80, 0x03, 0x8c, 0xd9};
|
||||||
|
|
||||||
std::string Base64Encode(const std::string& input) {
|
std::string Base64Encode(const std::string& input) {
|
||||||
std::string output;
|
std::string output;
|
||||||
|
@ -319,11 +320,11 @@ TEST_P(WidevineKeySourceTest, LicenseStatusCencWithPsshBoxOK) {
|
||||||
widevine_key_source_->set_signer(std::move(mock_request_signer_));
|
widevine_key_source_->set_signer(std::move(mock_request_signer_));
|
||||||
std::vector<uint8_t> pssh_box(kRequestPsshBox,
|
std::vector<uint8_t> pssh_box(kRequestPsshBox,
|
||||||
kRequestPsshBox + arraysize(kRequestPsshBox));
|
kRequestPsshBox + arraysize(kRequestPsshBox));
|
||||||
ASSERT_OK(widevine_key_source_->FetchKeys(pssh_box));
|
ASSERT_OK(widevine_key_source_->FetchKeys(EmeInitDataType::CENC, pssh_box));
|
||||||
VerifyKeys(false);
|
VerifyKeys(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(WidevineKeySourceTest, LicenseStatusCencWithKeyIdsOK) {
|
TEST_P(WidevineKeySourceTest, LicenseStatusCencWithKeyIdOK) {
|
||||||
std::string expected_pssh_data(
|
std::string expected_pssh_data(
|
||||||
kRequestPsshDataFromKeyIds,
|
kRequestPsshDataFromKeyIds,
|
||||||
kRequestPsshDataFromKeyIds + arraysize(kRequestPsshDataFromKeyIds));
|
kRequestPsshDataFromKeyIds + arraysize(kRequestPsshDataFromKeyIds));
|
||||||
|
@ -341,10 +342,9 @@ TEST_P(WidevineKeySourceTest, LicenseStatusCencWithKeyIdsOK) {
|
||||||
|
|
||||||
CreateWidevineKeySource();
|
CreateWidevineKeySource();
|
||||||
widevine_key_source_->set_signer(std::move(mock_request_signer_));
|
widevine_key_source_->set_signer(std::move(mock_request_signer_));
|
||||||
std::vector<std::vector<uint8_t>> key_ids;
|
std::vector<uint8_t> key_id(kRequestKeyId,
|
||||||
key_ids.push_back(std::vector<uint8_t>(
|
kRequestKeyId + arraysize(kRequestKeyId));
|
||||||
kRequestKeyId, kRequestKeyId + arraysize(kRequestKeyId)));
|
ASSERT_OK(widevine_key_source_->FetchKeys(EmeInitDataType::WEBM, key_id));
|
||||||
ASSERT_OK(widevine_key_source_->FetchKeys(key_ids));
|
|
||||||
VerifyKeys(false);
|
VerifyKeys(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,7 +363,10 @@ TEST_P(WidevineKeySourceTest, LicenseStatusClassicOK) {
|
||||||
|
|
||||||
CreateWidevineKeySource();
|
CreateWidevineKeySource();
|
||||||
widevine_key_source_->set_signer(std::move(mock_request_signer_));
|
widevine_key_source_->set_signer(std::move(mock_request_signer_));
|
||||||
ASSERT_OK(widevine_key_source_->FetchKeys(kClassicAssetId));
|
ASSERT_OK(widevine_key_source_->FetchKeys(
|
||||||
|
EmeInitDataType::WIDEVINE_CLASSIC,
|
||||||
|
std::vector<uint8_t>(std::begin(kClassicAssetIdBytes),
|
||||||
|
std::end(kClassicAssetIdBytes))));
|
||||||
VerifyKeys(true);
|
VerifyKeys(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -640,27 +640,18 @@ bool MP4MediaParser::FetchKeysIfNecessary(
|
||||||
if (!decryption_key_source_)
|
if (!decryption_key_source_)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
Status status;
|
std::vector<uint8_t> pssh_raw_data;
|
||||||
for (std::vector<ProtectionSystemSpecificHeader>::const_iterator iter =
|
for (const auto& header : headers) {
|
||||||
headers.begin(); iter != headers.end(); ++iter) {
|
pssh_raw_data.insert(pssh_raw_data.end(), header.raw_box.begin(),
|
||||||
status = decryption_key_source_->FetchKeys(iter->raw_box);
|
header.raw_box.end());
|
||||||
if (!status.ok()) {
|
|
||||||
// If there is an error, try using the next PSSH box and report if none
|
|
||||||
// work.
|
|
||||||
VLOG(1) << "Unable to fetch decryption keys: " << status
|
|
||||||
<< ", trying the next PSSH box";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
Status status =
|
||||||
|
decryption_key_source_->FetchKeys(EmeInitDataType::CENC, pssh_raw_data);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
LOG(ERROR) << "Error fetching decryption keys: " << status;
|
LOG(ERROR) << "Error fetching decryption keys: " << status;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
LOG(ERROR) << "No viable 'pssh' box found for content decryption.";
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MP4MediaParser::EnqueueSample(bool* err) {
|
bool MP4MediaParser::EnqueueSample(bool* err) {
|
||||||
|
|
|
@ -31,7 +31,9 @@ const char kKeyId[] = "0123456789012345";
|
||||||
|
|
||||||
class MockKeySource : public FixedKeySource {
|
class MockKeySource : public FixedKeySource {
|
||||||
public:
|
public:
|
||||||
MOCK_METHOD1(FetchKeys, Status(const std::vector<uint8_t>& pssh_data));
|
MOCK_METHOD2(FetchKeys,
|
||||||
|
Status(EmeInitDataType init_data_type,
|
||||||
|
const std::vector<uint8_t>& init_data));
|
||||||
MOCK_METHOD2(GetKey,
|
MOCK_METHOD2(GetKey,
|
||||||
Status(const std::vector<uint8_t>& key_id, EncryptionKey* key));
|
Status(const std::vector<uint8_t>& key_id, EncryptionKey* key));
|
||||||
};
|
};
|
||||||
|
@ -247,7 +249,7 @@ TEST_F(MP4MediaParserTest, CencInitWithoutDecryptionSource) {
|
||||||
|
|
||||||
TEST_F(MP4MediaParserTest, CencWithDecryptionSourceAndAuxInMdat) {
|
TEST_F(MP4MediaParserTest, CencWithDecryptionSourceAndAuxInMdat) {
|
||||||
MockKeySource mock_key_source;
|
MockKeySource mock_key_source;
|
||||||
EXPECT_CALL(mock_key_source, FetchKeys(_)).WillOnce(Return(Status::OK));
|
EXPECT_CALL(mock_key_source, FetchKeys(_, _)).WillOnce(Return(Status::OK));
|
||||||
|
|
||||||
EncryptionKey encryption_key;
|
EncryptionKey encryption_key;
|
||||||
encryption_key.key.assign(kKey, kKey + strlen(kKey));
|
encryption_key.key.assign(kKey, kKey + strlen(kKey));
|
||||||
|
@ -266,7 +268,7 @@ TEST_F(MP4MediaParserTest, CencWithDecryptionSourceAndAuxInMdat) {
|
||||||
|
|
||||||
TEST_F(MP4MediaParserTest, CencWithDecryptionSourceAndSenc) {
|
TEST_F(MP4MediaParserTest, CencWithDecryptionSourceAndSenc) {
|
||||||
MockKeySource mock_key_source;
|
MockKeySource mock_key_source;
|
||||||
EXPECT_CALL(mock_key_source, FetchKeys(_)).WillOnce(Return(Status::OK));
|
EXPECT_CALL(mock_key_source, FetchKeys(_, _)).WillOnce(Return(Status::OK));
|
||||||
|
|
||||||
EncryptionKey encryption_key;
|
EncryptionKey encryption_key;
|
||||||
encryption_key.key.assign(kKey, kKey + strlen(kKey));
|
encryption_key.key.assign(kKey, kKey + strlen(kKey));
|
||||||
|
|
|
@ -242,17 +242,19 @@ bool WebMMediaParser::FetchKeysIfNecessary(
|
||||||
if (!decryption_key_source_)
|
if (!decryption_key_source_)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
std::vector<std::vector<uint8_t>> key_ids;
|
Status status;
|
||||||
if (!audio_encryption_key_id.empty()) {
|
if (!audio_encryption_key_id.empty()) {
|
||||||
key_ids.push_back(std::vector<uint8_t>(audio_encryption_key_id.begin(),
|
status.Update(decryption_key_source_->FetchKeys(
|
||||||
audio_encryption_key_id.end()));
|
EmeInitDataType::WEBM,
|
||||||
|
std::vector<uint8_t>(audio_encryption_key_id.begin(),
|
||||||
|
audio_encryption_key_id.end())));
|
||||||
}
|
}
|
||||||
if (!video_encryption_key_id.empty()) {
|
if (!video_encryption_key_id.empty()) {
|
||||||
key_ids.push_back(std::vector<uint8_t>(video_encryption_key_id.begin(),
|
status.Update(decryption_key_source_->FetchKeys(
|
||||||
video_encryption_key_id.end()));
|
EmeInitDataType::WEBM,
|
||||||
|
std::vector<uint8_t>(video_encryption_key_id.begin(),
|
||||||
|
video_encryption_key_id.end())));
|
||||||
}
|
}
|
||||||
|
|
||||||
Status status = decryption_key_source_->FetchKeys(key_ids);
|
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
LOG(ERROR) << "Error fetching decryption keys: " << status;
|
LOG(ERROR) << "Error fetching decryption keys: " << status;
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1058,21 +1058,23 @@ bool WvmMediaParser::EmitSample(uint32_t parsed_audio_or_video_stream_id,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WvmMediaParser::GetAssetKey(const uint32_t asset_id,
|
bool WvmMediaParser::GetAssetKey(const uint8_t* asset_id,
|
||||||
EncryptionKey* encryption_key) {
|
EncryptionKey* encryption_key) {
|
||||||
DCHECK(decryption_key_source_);
|
DCHECK(decryption_key_source_);
|
||||||
Status status = decryption_key_source_->FetchKeys(asset_id);
|
Status status = decryption_key_source_->FetchKeys(
|
||||||
|
EmeInitDataType::WIDEVINE_CLASSIC,
|
||||||
|
std::vector<uint8_t>(asset_id, asset_id + sizeof(uint32_t)));
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
LOG(ERROR) << "Fetch Key(s) failed for AssetID = " << asset_id
|
LOG(ERROR) << "Fetch Key(s) failed for AssetID = "
|
||||||
<< ", error = " << status;
|
<< ntohlFromBuffer(asset_id) << ", error = " << status;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = decryption_key_source_->GetKey(KeySource::TRACK_TYPE_HD,
|
status = decryption_key_source_->GetKey(KeySource::TRACK_TYPE_HD,
|
||||||
encryption_key);
|
encryption_key);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
LOG(ERROR) << "Fetch Key(s) failed for AssetID = " << asset_id
|
LOG(ERROR) << "Fetch Key(s) failed for AssetID = "
|
||||||
<< ", error = " << status;
|
<< ntohlFromBuffer(asset_id) << ", error = " << status;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1097,22 +1099,17 @@ bool WvmMediaParser::ProcessEcm() {
|
||||||
ecm_data += sizeof(uint32_t); // old version field - skip.
|
ecm_data += sizeof(uint32_t); // old version field - skip.
|
||||||
ecm_data += sizeof(uint32_t); // clear lead - skip.
|
ecm_data += sizeof(uint32_t); // clear lead - skip.
|
||||||
ecm_data += sizeof(uint32_t); // system id(includes ECM version) - skip.
|
ecm_data += sizeof(uint32_t); // system id(includes ECM version) - skip.
|
||||||
uint32_t asset_id = ntohlFromBuffer(ecm_data);
|
|
||||||
if (asset_id == 0) {
|
|
||||||
LOG(ERROR) << "AssetID in ECM is not valid.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ecm_data += sizeof(uint32_t); // asset_id.
|
|
||||||
EncryptionKey encryption_key;
|
EncryptionKey encryption_key;
|
||||||
if (!GetAssetKey(asset_id, &encryption_key)) {
|
if (!GetAssetKey(ecm_data, &encryption_key)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (encryption_key.key.size() < kAssetKeySizeBytes) {
|
if (encryption_key.key.size() < kAssetKeySizeBytes) {
|
||||||
LOG(ERROR) << "Asset Key size of " << encryption_key.key.size()
|
LOG(ERROR) << "Asset Key size of " << encryption_key.key.size()
|
||||||
<< " for AssetID = " << asset_id
|
<< " for AssetID = " << ntohlFromBuffer(ecm_data)
|
||||||
<< " is less than minimum asset key size.";
|
<< " is less than minimum asset key size.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
ecm_data += sizeof(uint32_t); // asset_id.
|
||||||
// Legacy WVM content may have asset keys > 16 bytes.
|
// Legacy WVM content may have asset keys > 16 bytes.
|
||||||
// Use only the first 16 bytes of the asset key to get
|
// Use only the first 16 bytes of the asset key to get
|
||||||
// the content key.
|
// the content key.
|
||||||
|
|
|
@ -200,7 +200,7 @@ class WvmMediaParser : public MediaParser {
|
||||||
// to ouput media sample as encrypted.
|
// to ouput media sample as encrypted.
|
||||||
bool Output(bool must_process_encrypted);
|
bool Output(bool must_process_encrypted);
|
||||||
|
|
||||||
bool GetAssetKey(const uint32_t asset_id, EncryptionKey* encryption_key);
|
bool GetAssetKey(const uint8_t* asset_id, EncryptionKey* encryption_key);
|
||||||
|
|
||||||
// Callback invoked by the ES media parser
|
// Callback invoked by the ES media parser
|
||||||
// to emit a new audio/video access unit.
|
// to emit a new audio/video access unit.
|
||||||
|
|
|
@ -52,7 +52,9 @@ class MockKeySource : public FixedKeySource {
|
||||||
MockKeySource() {}
|
MockKeySource() {}
|
||||||
~MockKeySource() override {}
|
~MockKeySource() override {}
|
||||||
|
|
||||||
MOCK_METHOD1(FetchKeys, Status(uint32_t asset_id));
|
MOCK_METHOD2(FetchKeys,
|
||||||
|
Status(EmeInitDataType init_data_type,
|
||||||
|
const std::vector<uint8_t>& init_data));
|
||||||
MOCK_METHOD2(GetKey, Status(TrackType track_type,
|
MOCK_METHOD2(GetKey, Status(TrackType track_type,
|
||||||
EncryptionKey* key));
|
EncryptionKey* key));
|
||||||
|
|
||||||
|
@ -185,7 +187,7 @@ TEST_F(WvmMediaParserTest, ParseWvmInitWithoutKeySource) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvmMediaParserTest, ParseWvm) {
|
TEST_F(WvmMediaParserTest, ParseWvm) {
|
||||||
EXPECT_CALL(*key_source_, FetchKeys(_)).WillOnce(Return(Status::OK));
|
EXPECT_CALL(*key_source_, FetchKeys(_, _)).WillOnce(Return(Status::OK));
|
||||||
EXPECT_CALL(*key_source_, GetKey(_, _))
|
EXPECT_CALL(*key_source_, GetKey(_, _))
|
||||||
.WillOnce(DoAll(SetArgPointee<1>(encryption_key_), Return(Status::OK)));
|
.WillOnce(DoAll(SetArgPointee<1>(encryption_key_), Return(Status::OK)));
|
||||||
Parse(kWvmFile);
|
Parse(kWvmFile);
|
||||||
|
@ -196,7 +198,7 @@ TEST_F(WvmMediaParserTest, ParseWvm) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvmMediaParserTest, ParseWvmWith64ByteAssetKey) {
|
TEST_F(WvmMediaParserTest, ParseWvmWith64ByteAssetKey) {
|
||||||
EXPECT_CALL(*key_source_, FetchKeys(_)).WillOnce(Return(Status::OK));
|
EXPECT_CALL(*key_source_, FetchKeys(_, _)).WillOnce(Return(Status::OK));
|
||||||
// WVM uses only the first 16 bytes of the asset key.
|
// WVM uses only the first 16 bytes of the asset key.
|
||||||
encryption_key_.key.resize(64);
|
encryption_key_.key.resize(64);
|
||||||
encryption_key_.key.assign(k64ByteAssetKey, k64ByteAssetKey + 64);
|
encryption_key_.key.assign(k64ByteAssetKey, k64ByteAssetKey + 64);
|
||||||
|
@ -209,7 +211,7 @@ TEST_F(WvmMediaParserTest, ParseWvmWith64ByteAssetKey) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvmMediaParserTest, ParseMultiConfigWvm) {
|
TEST_F(WvmMediaParserTest, ParseMultiConfigWvm) {
|
||||||
EXPECT_CALL(*key_source_, FetchKeys(_)).WillOnce(Return(Status::OK));
|
EXPECT_CALL(*key_source_, FetchKeys(_, _)).WillOnce(Return(Status::OK));
|
||||||
EXPECT_CALL(*key_source_, GetKey(_, _))
|
EXPECT_CALL(*key_source_, GetKey(_, _))
|
||||||
.WillOnce(DoAll(SetArgPointee<1>(encryption_key_), Return(Status::OK)));
|
.WillOnce(DoAll(SetArgPointee<1>(encryption_key_), Return(Status::OK)));
|
||||||
Parse(kMultiConfigWvmFile);
|
Parse(kMultiConfigWvmFile);
|
||||||
|
|
Loading…
Reference in New Issue