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:
Jacob Trimble 2016-02-19 10:24:33 -08:00
parent 90731d79df
commit c4246d04fd
10 changed files with 125 additions and 42 deletions

View File

@ -11,9 +11,6 @@
#include "packager/media/base/buffer_writer.h"
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.
const char kDefaultUUID[] = "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed";
const char kDefaultSystemName[] = "";
@ -33,7 +30,12 @@ Status KeySource::FetchKeys(const std::vector<uint8_t>& content_id,
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.
return Status::OK;
}

View File

@ -15,6 +15,10 @@
namespace edash_packager {
namespace media {
const uint8_t kWidevineSystemId[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6,
0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
0xd5, 0x1d, 0x21, 0xed};
struct EncryptionKey {
EncryptionKey();
~EncryptionKey();
@ -47,10 +51,14 @@ class KeySource {
const std::string& policy);
/// Fetch keys for CENC from the key server.
/// @param pssh_data is the Data portion of the PSSH box for the content
/// to be decrypted.
/// @param pssh_box The entire PSSH box for the content to be decrypted
/// @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.
/// @param asset_id is the Widevine Classic asset ID for the content to be

View File

@ -83,6 +83,7 @@
'widevine_key_source.h',
],
'dependencies': [
'widevine_pssh_data_proto',
'../../base/base.gyp:base',
'../../third_party/boringssl/boringssl.gyp:boringssl',
'../../third_party/curl/curl.gyp:libcurl',

View File

@ -14,7 +14,9 @@
#include "packager/base/stl_util.h"
#include "packager/media/base/http_key_fetcher.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/widevine_pssh_data.pb.h"
#define RCHECK(x) \
do { \
@ -172,10 +174,49 @@ Status WidevineKeySource::FetchKeys(const std::vector<uint8_t>& content_id,
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_);
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);

View File

@ -34,7 +34,8 @@ class WidevineKeySource : public KeySource {
/// @{
Status FetchKeys(const std::vector<uint8_t>& content_id,
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 GetKey(TrackType track_type, EncryptionKey* key) override;

View File

@ -57,7 +57,15 @@ const char kTrackFormat[] =
const char kClassicTrackFormat[] = "{\"type\":\"%s\",\"key\":\"%s\"}";
const char kLicenseResponseFormat[] = "{\"status\":\"%s\",\"tracks\":[%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 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
// correctly.
const uint32_t kClassicAssetId = 0x80038cd9;
@ -266,7 +274,7 @@ TEST_F(WidevineKeySourceTest, LicenseStatusCencNotOK) {
.error_code());
}
TEST_F(WidevineKeySourceTest, LicenseStatusCencWithPsshDataOK) {
TEST_F(WidevineKeySourceTest, LicenseStatusCencWithPsshBoxOK) {
std::string expected_message =
base::StringPrintf(kExpectedRequestMessageWithPsshFormat,
Base64Encode(kRequestPsshData).c_str());
@ -281,11 +289,34 @@ TEST_F(WidevineKeySourceTest, LicenseStatusCencWithPsshDataOK) {
CreateWidevineKeySource();
widevine_key_source_->set_signer(mock_request_signer_.Pass());
std::vector<uint8_t> pssh_data(
reinterpret_cast<const uint8_t*>(kRequestPsshData),
reinterpret_cast<const uint8_t*>(kRequestPsshData) +
strlen(kRequestPsshData));
ASSERT_OK(widevine_key_source_->FetchKeys(pssh_data));
std::vector<uint8_t> pssh_box(kRequestPsshBox,
kRequestPsshBox + arraysize(kRequestPsshBox));
ASSERT_OK(widevine_key_source_->FetchKeys(pssh_box));
VerifyKeys(false);
}
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);
}

View File

@ -7,6 +7,7 @@
// This file defines Widevine Pssh Data proto format.
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package edash_packager.media;

View File

@ -17,7 +17,6 @@
#include "packager/media/base/key_source.h"
#include "packager/media/base/macros.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/file/file.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.
const uint8_t kDtsAudioNumChannels = 6;
@ -579,22 +577,23 @@ bool MP4MediaParser::FetchKeysIfNecessary(
if (!decryption_key_source_)
return true;
// TODO(tinskip): Pass in raw 'pssh' boxes to FetchKeys. This will allow
// supporting multiple keysystems. Move this to KeySource.
std::vector<uint8_t> widevine_system_id;
base::HexStringToBytes(kWidevineKeySystemId, &widevine_system_id);
Status status;
for (std::vector<ProtectionSystemSpecificHeader>::const_iterator iter =
headers.begin(); iter != headers.end(); ++iter) {
ProtectionSystemSpecificInfo info;
RCHECK(info.Parse(iter->raw_box.data(), iter->raw_box.size()));
if (info.system_id() == widevine_system_id) {
Status status = decryption_key_source_->FetchKeys(info.pssh_data());
status = decryption_key_source_->FetchKeys(iter->raw_box);
if (!status.ok()) {
LOG(ERROR) << "Error fetching decryption keys: " << status;
return false;
// 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;
}
if (!status.ok()) {
LOG(ERROR) << "Error fetching decryption keys: " << status;
return false;
}
LOG(ERROR) << "No viable 'pssh' box found for content decryption.";

View File

@ -58,7 +58,6 @@
'../../../third_party/boringssl/boringssl.gyp:boringssl',
'../../../third_party/libwebm/libwebm.gyp:mkvmuxer',
'../../base/media_base.gyp:media_base',
'../../base/media_base.gyp:widevine_pssh_data_proto',
'../../filters/filters.gyp:filters'
],
},

View File

@ -11,7 +11,6 @@
#include "packager/base/logging.h"
#include "packager/media/base/buffer_writer.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_constants.h"
#include "packager/media/formats/webm/webm_content_encodings.h"
@ -239,20 +238,21 @@ bool WebMMediaParser::FetchKeysIfNecessary(
const std::string& video_encryption_key_id) {
if (audio_encryption_key_id.empty() && video_encryption_key_id.empty())
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_)
return true;
// Generate WidevinePsshData from key_id.
WidevinePsshData widevine_pssh_data;
if (!audio_encryption_key_id.empty())
widevine_pssh_data.add_key_id(audio_encryption_key_id);
if (!video_encryption_key_id.empty())
widevine_pssh_data.add_key_id(video_encryption_key_id);
std::vector<std::vector<uint8_t>> key_ids;
if (!audio_encryption_key_id.empty()) {
key_ids.push_back(std::vector<uint8_t>(audio_encryption_key_id.begin(),
audio_encryption_key_id.end()));
}
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(
std::vector<uint8_t>(serialized_string.begin(), serialized_string.end()));
Status status = decryption_key_source_->FetchKeys(key_ids);
if (!status.ok()) {
LOG(ERROR) << "Error fetching decryption keys: " << status;
return false;