Change FetchKey usages.
Add another overload to fetch by key ID directly, used by WebM. Changed the one using PSSH data in favor of the entire PSSH box. This also moves the Widevine proto to media base. Now, the Widevine key source handles creating the Widevine specific PSSH data. Change-Id: I6f4633facad39207809ffbad970635d1f9d70983
This commit is contained in:
parent
90731d79df
commit
c4246d04fd
|
@ -11,9 +11,6 @@
|
||||||
#include "packager/media/base/buffer_writer.h"
|
#include "packager/media/base/buffer_writer.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const uint8_t kWidevineSystemId[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6,
|
|
||||||
0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
|
|
||||||
0xd5, 0x1d, 0x21, 0xed};
|
|
||||||
// TODO(kqyang): Consider making it configurable.
|
// TODO(kqyang): Consider making it configurable.
|
||||||
const char kDefaultUUID[] = "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed";
|
const char kDefaultUUID[] = "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed";
|
||||||
const char kDefaultSystemName[] = "";
|
const char kDefaultSystemName[] = "";
|
||||||
|
@ -33,7 +30,12 @@ Status KeySource::FetchKeys(const std::vector<uint8_t>& content_id,
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status KeySource::FetchKeys(const std::vector<uint8_t>& pssh_data) {
|
Status KeySource::FetchKeys(const std::vector<uint8_t>& pssh_box) {
|
||||||
|
// Do nothing for fixed key encryption/decryption.
|
||||||
|
return Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status KeySource::FetchKeys(const std::vector<std::vector<uint8_t>>& key_ids) {
|
||||||
// Do nothing for fixed key encryption/decryption.
|
// Do nothing for fixed key encryption/decryption.
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,10 @@
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
|
const uint8_t kWidevineSystemId[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6,
|
||||||
|
0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
|
||||||
|
0xd5, 0x1d, 0x21, 0xed};
|
||||||
|
|
||||||
struct EncryptionKey {
|
struct EncryptionKey {
|
||||||
EncryptionKey();
|
EncryptionKey();
|
||||||
~EncryptionKey();
|
~EncryptionKey();
|
||||||
|
@ -47,10 +51,14 @@ class KeySource {
|
||||||
const std::string& policy);
|
const std::string& policy);
|
||||||
|
|
||||||
/// Fetch keys for CENC from the key server.
|
/// Fetch keys for CENC from the key server.
|
||||||
/// @param pssh_data is the Data portion of the PSSH box for the content
|
/// @param pssh_box The entire PSSH box for the content to be decrypted
|
||||||
/// to be decrypted.
|
|
||||||
/// @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_data);
|
virtual Status FetchKeys(const std::vector<uint8_t>& pssh_box);
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
|
||||||
/// Fetch keys for WVM decryption from the key server.
|
/// Fetch keys for WVM decryption from the key server.
|
||||||
/// @param asset_id is the Widevine Classic asset ID for the content to be
|
/// @param asset_id is the Widevine Classic asset ID for the content to be
|
||||||
|
|
|
@ -83,6 +83,7 @@
|
||||||
'widevine_key_source.h',
|
'widevine_key_source.h',
|
||||||
],
|
],
|
||||||
'dependencies': [
|
'dependencies': [
|
||||||
|
'widevine_pssh_data_proto',
|
||||||
'../../base/base.gyp:base',
|
'../../base/base.gyp:base',
|
||||||
'../../third_party/boringssl/boringssl.gyp:boringssl',
|
'../../third_party/boringssl/boringssl.gyp:boringssl',
|
||||||
'../../third_party/curl/curl.gyp:libcurl',
|
'../../third_party/curl/curl.gyp:libcurl',
|
||||||
|
|
|
@ -14,7 +14,9 @@
|
||||||
#include "packager/base/stl_util.h"
|
#include "packager/base/stl_util.h"
|
||||||
#include "packager/media/base/http_key_fetcher.h"
|
#include "packager/media/base/http_key_fetcher.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/request_signer.h"
|
#include "packager/media/base/request_signer.h"
|
||||||
|
#include "packager/media/base/widevine_pssh_data.pb.h"
|
||||||
|
|
||||||
#define RCHECK(x) \
|
#define RCHECK(x) \
|
||||||
do { \
|
do { \
|
||||||
|
@ -172,10 +174,49 @@ 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_data) {
|
Status WidevineKeySource::FetchKeys(const std::vector<uint8_t>& pssh_box) {
|
||||||
|
const std::vector<uint8_t> widevine_system_id(
|
||||||
|
kWidevineSystemId, kWidevineSystemId + arraysize(kWidevineSystemId));
|
||||||
|
|
||||||
|
ProtectionSystemSpecificInfo info;
|
||||||
|
if (!info.Parse(pssh_box.data(), pssh_box.size()))
|
||||||
|
return Status(error::PARSER_FAILURE, "Error parsing the PSSH box.");
|
||||||
|
|
||||||
|
if (info.system_id() == widevine_system_id) {
|
||||||
|
base::AutoLock scoped_lock(lock_);
|
||||||
|
request_dict_.Clear();
|
||||||
|
std::string pssh_data_base64_string;
|
||||||
|
|
||||||
|
BytesToBase64String(info.pssh_data(), &pssh_data_base64_string);
|
||||||
|
request_dict_.SetString("pssh_data", pssh_data_base64_string);
|
||||||
|
return FetchKeysInternal(!kEnableKeyRotation, 0, false);
|
||||||
|
} else if (!info.key_ids().empty()) {
|
||||||
|
// This is not a Widevine PSSH box. Try making the request for the key-IDs.
|
||||||
|
// Even if this is a different key-system, it should still work. Either
|
||||||
|
// the server will not recognize it and return an error, or it will
|
||||||
|
// recognize it and the key must be correct (or the content is bad).
|
||||||
|
return FetchKeys(info.key_ids());
|
||||||
|
} else {
|
||||||
|
return Status(error::NOT_FOUND, "No key IDs given in PSSH box.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Status WidevineKeySource::FetchKeys(
|
||||||
|
const std::vector<std::vector<uint8_t>>& key_ids) {
|
||||||
base::AutoLock scoped_lock(lock_);
|
base::AutoLock scoped_lock(lock_);
|
||||||
request_dict_.Clear();
|
request_dict_.Clear();
|
||||||
std::string pssh_data_base64_string;
|
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);
|
BytesToBase64String(pssh_data, &pssh_data_base64_string);
|
||||||
request_dict_.SetString("pssh_data", pssh_data_base64_string);
|
request_dict_.SetString("pssh_data", pssh_data_base64_string);
|
||||||
return FetchKeysInternal(!kEnableKeyRotation, 0, false);
|
return FetchKeysInternal(!kEnableKeyRotation, 0, false);
|
||||||
|
|
|
@ -34,7 +34,8 @@ class WidevineKeySource : public KeySource {
|
||||||
/// @{
|
/// @{
|
||||||
Status FetchKeys(const std::vector<uint8_t>& content_id,
|
Status FetchKeys(const std::vector<uint8_t>& content_id,
|
||||||
const std::string& policy) override;
|
const std::string& policy) override;
|
||||||
Status FetchKeys(const std::vector<uint8_t>& pssh_data) override;
|
Status FetchKeys(const std::vector<uint8_t>& pssh_box) override;
|
||||||
|
Status FetchKeys(const std::vector<std::vector<uint8_t>>& key_ids) override;
|
||||||
Status FetchKeys(uint32_t asset_id) override;
|
Status FetchKeys(uint32_t asset_id) override;
|
||||||
|
|
||||||
Status GetKey(TrackType track_type, EncryptionKey* key) override;
|
Status GetKey(TrackType track_type, EncryptionKey* key) override;
|
||||||
|
|
|
@ -57,7 +57,15 @@ const char kTrackFormat[] =
|
||||||
const char kClassicTrackFormat[] = "{\"type\":\"%s\",\"key\":\"%s\"}";
|
const char kClassicTrackFormat[] = "{\"type\":\"%s\",\"key\":\"%s\"}";
|
||||||
const char kLicenseResponseFormat[] = "{\"status\":\"%s\",\"tracks\":[%s]}";
|
const char kLicenseResponseFormat[] = "{\"status\":\"%s\",\"tracks\":[%s]}";
|
||||||
const char kHttpResponseFormat[] = "{\"response\":\"%s\"}";
|
const char kHttpResponseFormat[] = "{\"response\":\"%s\"}";
|
||||||
|
const uint8_t kRequestPsshBox[] = {
|
||||||
|
0, 0, 0, 41, 'p', 's', 's', 'h', 0, 0, 0,
|
||||||
|
0, 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8,
|
||||||
|
0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, 0, 0, 0, 0x09, 'P',
|
||||||
|
'S', 'S', 'H', ' ', 'd', 'a', 't', 'a'};
|
||||||
const char kRequestPsshData[] = "PSSH data";
|
const char kRequestPsshData[] = "PSSH data";
|
||||||
|
const uint8_t kRequestPsshDataFromKeyIds[] = {0x12, 0x06, 0x00, 0x01,
|
||||||
|
0x02, 0x03, 0x04, 0x05};
|
||||||
|
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;
|
||||||
|
@ -266,7 +274,7 @@ TEST_F(WidevineKeySourceTest, LicenseStatusCencNotOK) {
|
||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WidevineKeySourceTest, LicenseStatusCencWithPsshDataOK) {
|
TEST_F(WidevineKeySourceTest, LicenseStatusCencWithPsshBoxOK) {
|
||||||
std::string expected_message =
|
std::string expected_message =
|
||||||
base::StringPrintf(kExpectedRequestMessageWithPsshFormat,
|
base::StringPrintf(kExpectedRequestMessageWithPsshFormat,
|
||||||
Base64Encode(kRequestPsshData).c_str());
|
Base64Encode(kRequestPsshData).c_str());
|
||||||
|
@ -281,11 +289,34 @@ TEST_F(WidevineKeySourceTest, LicenseStatusCencWithPsshDataOK) {
|
||||||
|
|
||||||
CreateWidevineKeySource();
|
CreateWidevineKeySource();
|
||||||
widevine_key_source_->set_signer(mock_request_signer_.Pass());
|
widevine_key_source_->set_signer(mock_request_signer_.Pass());
|
||||||
std::vector<uint8_t> pssh_data(
|
std::vector<uint8_t> pssh_box(kRequestPsshBox,
|
||||||
reinterpret_cast<const uint8_t*>(kRequestPsshData),
|
kRequestPsshBox + arraysize(kRequestPsshBox));
|
||||||
reinterpret_cast<const uint8_t*>(kRequestPsshData) +
|
ASSERT_OK(widevine_key_source_->FetchKeys(pssh_box));
|
||||||
strlen(kRequestPsshData));
|
VerifyKeys(false);
|
||||||
ASSERT_OK(widevine_key_source_->FetchKeys(pssh_data));
|
}
|
||||||
|
|
||||||
|
TEST_F(WidevineKeySourceTest, LicenseStatusCencWithKeyIdsOK) {
|
||||||
|
std::string expected_pssh_data(
|
||||||
|
kRequestPsshDataFromKeyIds,
|
||||||
|
kRequestPsshDataFromKeyIds + arraysize(kRequestPsshDataFromKeyIds));
|
||||||
|
std::string expected_message =
|
||||||
|
base::StringPrintf(kExpectedRequestMessageWithPsshFormat,
|
||||||
|
Base64Encode(expected_pssh_data).c_str());
|
||||||
|
EXPECT_CALL(*mock_request_signer_,
|
||||||
|
GenerateSignature(StrEq(expected_message), _))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<1>(kMockSignature), Return(true)));
|
||||||
|
|
||||||
|
std::string mock_response = base::StringPrintf(
|
||||||
|
kHttpResponseFormat, Base64Encode(GenerateMockLicenseResponse()).c_str());
|
||||||
|
EXPECT_CALL(*mock_key_fetcher_, FetchKeys(_, _, _))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK)));
|
||||||
|
|
||||||
|
CreateWidevineKeySource();
|
||||||
|
widevine_key_source_->set_signer(mock_request_signer_.Pass());
|
||||||
|
std::vector<std::vector<uint8_t>> key_ids;
|
||||||
|
key_ids.push_back(std::vector<uint8_t>(
|
||||||
|
kRequestKeyId, kRequestKeyId + arraysize(kRequestKeyId)));
|
||||||
|
ASSERT_OK(widevine_key_source_->FetchKeys(key_ids));
|
||||||
VerifyKeys(false);
|
VerifyKeys(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
// This file defines Widevine Pssh Data proto format.
|
// This file defines Widevine Pssh Data proto format.
|
||||||
|
|
||||||
syntax = "proto2";
|
syntax = "proto2";
|
||||||
|
option optimize_for = LITE_RUNTIME;
|
||||||
|
|
||||||
package edash_packager.media;
|
package edash_packager.media;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include "packager/media/base/key_source.h"
|
#include "packager/media/base/key_source.h"
|
||||||
#include "packager/media/base/macros.h"
|
#include "packager/media/base/macros.h"
|
||||||
#include "packager/media/base/media_sample.h"
|
#include "packager/media/base/media_sample.h"
|
||||||
#include "packager/media/base/protection_system_specific_info.h"
|
|
||||||
#include "packager/media/base/video_stream_info.h"
|
#include "packager/media/base/video_stream_info.h"
|
||||||
#include "packager/media/file/file.h"
|
#include "packager/media/file/file.h"
|
||||||
#include "packager/media/file/file_closer.h"
|
#include "packager/media/file/file_closer.h"
|
||||||
|
@ -83,7 +82,6 @@ AudioCodec FourCCToAudioCodec(FourCC fourcc) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char kWidevineKeySystemId[] = "edef8ba979d64acea3c827dcd51d21ed";
|
|
||||||
// Default DTS audio number of channels for 5.1 channel layout.
|
// Default DTS audio number of channels for 5.1 channel layout.
|
||||||
const uint8_t kDtsAudioNumChannels = 6;
|
const uint8_t kDtsAudioNumChannels = 6;
|
||||||
|
|
||||||
|
@ -579,22 +577,23 @@ bool MP4MediaParser::FetchKeysIfNecessary(
|
||||||
if (!decryption_key_source_)
|
if (!decryption_key_source_)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// TODO(tinskip): Pass in raw 'pssh' boxes to FetchKeys. This will allow
|
Status status;
|
||||||
// supporting multiple keysystems. Move this to KeySource.
|
|
||||||
std::vector<uint8_t> widevine_system_id;
|
|
||||||
base::HexStringToBytes(kWidevineKeySystemId, &widevine_system_id);
|
|
||||||
for (std::vector<ProtectionSystemSpecificHeader>::const_iterator iter =
|
for (std::vector<ProtectionSystemSpecificHeader>::const_iterator iter =
|
||||||
headers.begin(); iter != headers.end(); ++iter) {
|
headers.begin(); iter != headers.end(); ++iter) {
|
||||||
ProtectionSystemSpecificInfo info;
|
status = decryption_key_source_->FetchKeys(iter->raw_box);
|
||||||
RCHECK(info.Parse(iter->raw_box.data(), iter->raw_box.size()));
|
if (!status.ok()) {
|
||||||
if (info.system_id() == widevine_system_id) {
|
// If there is an error, try using the next PSSH box and report if none
|
||||||
Status status = decryption_key_source_->FetchKeys(info.pssh_data());
|
// work.
|
||||||
if (!status.ok()) {
|
VLOG(1) << "Unable to fetch decryption keys: " << status
|
||||||
LOG(ERROR) << "Error fetching decryption keys: " << status;
|
<< ", trying the next PSSH box";
|
||||||
return false;
|
continue;
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!status.ok()) {
|
||||||
|
LOG(ERROR) << "Error fetching decryption keys: " << status;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(ERROR) << "No viable 'pssh' box found for content decryption.";
|
LOG(ERROR) << "No viable 'pssh' box found for content decryption.";
|
||||||
|
|
|
@ -58,7 +58,6 @@
|
||||||
'../../../third_party/boringssl/boringssl.gyp:boringssl',
|
'../../../third_party/boringssl/boringssl.gyp:boringssl',
|
||||||
'../../../third_party/libwebm/libwebm.gyp:mkvmuxer',
|
'../../../third_party/libwebm/libwebm.gyp:mkvmuxer',
|
||||||
'../../base/media_base.gyp:media_base',
|
'../../base/media_base.gyp:media_base',
|
||||||
'../../base/media_base.gyp:widevine_pssh_data_proto',
|
|
||||||
'../../filters/filters.gyp:filters'
|
'../../filters/filters.gyp:filters'
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include "packager/base/logging.h"
|
#include "packager/base/logging.h"
|
||||||
#include "packager/media/base/buffer_writer.h"
|
#include "packager/media/base/buffer_writer.h"
|
||||||
#include "packager/media/base/timestamp.h"
|
#include "packager/media/base/timestamp.h"
|
||||||
#include "packager/media/base/widevine_pssh_data.pb.h"
|
|
||||||
#include "packager/media/formats/webm/webm_cluster_parser.h"
|
#include "packager/media/formats/webm/webm_cluster_parser.h"
|
||||||
#include "packager/media/formats/webm/webm_constants.h"
|
#include "packager/media/formats/webm/webm_constants.h"
|
||||||
#include "packager/media/formats/webm/webm_content_encodings.h"
|
#include "packager/media/formats/webm/webm_content_encodings.h"
|
||||||
|
@ -239,20 +238,21 @@ bool WebMMediaParser::FetchKeysIfNecessary(
|
||||||
const std::string& video_encryption_key_id) {
|
const std::string& video_encryption_key_id) {
|
||||||
if (audio_encryption_key_id.empty() && video_encryption_key_id.empty())
|
if (audio_encryption_key_id.empty() && video_encryption_key_id.empty())
|
||||||
return true;
|
return true;
|
||||||
// An error will be returned later if the samples need to be derypted.
|
// An error will be returned later if the samples need to be decrypted.
|
||||||
if (!decryption_key_source_)
|
if (!decryption_key_source_)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Generate WidevinePsshData from key_id.
|
std::vector<std::vector<uint8_t>> key_ids;
|
||||||
WidevinePsshData widevine_pssh_data;
|
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(),
|
||||||
widevine_pssh_data.add_key_id(audio_encryption_key_id);
|
audio_encryption_key_id.end()));
|
||||||
if (!video_encryption_key_id.empty())
|
}
|
||||||
widevine_pssh_data.add_key_id(video_encryption_key_id);
|
if (!video_encryption_key_id.empty()) {
|
||||||
|
key_ids.push_back(std::vector<uint8_t>(video_encryption_key_id.begin(),
|
||||||
|
video_encryption_key_id.end()));
|
||||||
|
}
|
||||||
|
|
||||||
const std::string serialized_string = widevine_pssh_data.SerializeAsString();
|
Status status = decryption_key_source_->FetchKeys(key_ids);
|
||||||
Status status = decryption_key_source_->FetchKeys(
|
|
||||||
std::vector<uint8_t>(serialized_string.begin(), serialized_string.end()));
|
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
LOG(ERROR) << "Error fetching decryption keys: " << status;
|
LOG(ERROR) << "Error fetching decryption keys: " << status;
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Reference in New Issue