Support entitlement license with Widevine key server
- Added a new --enable_entitlement_license flag, which sets 'enable_entitlement_license' in Widevine CommonEncryptionRequest; - Support 'boxes' in Widevine CommonEncryptionResponse. b/78171767 Change-Id: Id399fc7fcb2948c571e12c8af7687cfcfcef41fe
This commit is contained in:
parent
7f7843d241
commit
59941a101a
|
@ -323,6 +323,7 @@ base::Optional<PackagingParams> GetPackagingParams() {
|
|||
widevine.content_id = FLAGS_content_id_bytes;
|
||||
widevine.policy = FLAGS_policy;
|
||||
widevine.group_id = FLAGS_group_id_bytes;
|
||||
widevine.enable_entitlement_license = FLAGS_enable_entitlement_license;
|
||||
if (!GetWidevineSigner(&widevine.signer))
|
||||
return base::nullopt;
|
||||
break;
|
||||
|
|
|
@ -81,6 +81,8 @@ std::unique_ptr<KeySource> CreateEncryptionKeySource(
|
|||
widevine_key_source->set_signer(std::move(request_signer));
|
||||
}
|
||||
widevine_key_source->set_group_id(widevine.group_id);
|
||||
widevine_key_source->set_enable_entitlement_license(
|
||||
widevine.enable_entitlement_license);
|
||||
|
||||
Status status =
|
||||
widevine_key_source->FetchKeys(widevine.content_id, widevine.policy);
|
||||
|
|
|
@ -59,6 +59,9 @@ DEFINE_int32(crypto_period_duration,
|
|||
"Crypto period duration in seconds. If it is non-zero, key "
|
||||
"rotation is enabled.");
|
||||
DEFINE_hex_bytes(group_id, "", "Identifier for a group of licenses (hex).");
|
||||
DEFINE_bool(enable_entitlement_license,
|
||||
false,
|
||||
"Enable entitlement license when using Widevine key server.");
|
||||
|
||||
namespace shaka {
|
||||
namespace {
|
||||
|
|
|
@ -27,6 +27,7 @@ DECLARE_hex_bytes(aes_signing_iv);
|
|||
DECLARE_string(rsa_signing_key_path);
|
||||
DECLARE_int32(crypto_period_duration);
|
||||
DECLARE_hex_bytes(group_id);
|
||||
DECLARE_bool(enable_entitlement_license);
|
||||
|
||||
namespace shaka {
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include "packager/media/base/protection_system_specific_info.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "packager/media/base/buffer_reader.h"
|
||||
#include "packager/media/base/buffer_writer.h"
|
||||
#include "packager/media/base/fourccs.h"
|
||||
|
@ -33,7 +35,9 @@ bool ProtectionSystemSpecificInfo::ParseBoxes(
|
|||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
std::vector<ProtectionSystemSpecificInfo>* pssh_infos) {
|
||||
std::map<std::vector<uint8_t>, size_t> info_map;
|
||||
pssh_infos->clear();
|
||||
|
||||
BufferReader reader(data, data_size);
|
||||
while (reader.HasBytes(1)) {
|
||||
uint32_t size;
|
||||
|
@ -41,10 +45,17 @@ bool ProtectionSystemSpecificInfo::ParseBoxes(
|
|||
RCHECK(reader.SkipBytes(size - 4));
|
||||
RCHECK(size > kPsshBoxHeaderSize + kSystemIdSize);
|
||||
|
||||
const std::vector<uint8_t> system_id(
|
||||
data + kPsshBoxHeaderSize, data + kPsshBoxHeaderSize + kSystemIdSize);
|
||||
auto iter = info_map.find(system_id);
|
||||
if (iter != info_map.end()) {
|
||||
ProtectionSystemSpecificInfo& info = (*pssh_infos)[iter->second];
|
||||
info.psshs.insert(info.psshs.end(), data, data + size);
|
||||
} else {
|
||||
pssh_infos->push_back(
|
||||
{std::vector<uint8_t>(data + kPsshBoxHeaderSize,
|
||||
data + kPsshBoxHeaderSize + kSystemIdSize),
|
||||
std::vector<uint8_t>(data, data + size)});
|
||||
{system_id, std::vector<uint8_t>(data, data + size)});
|
||||
info_map[system_id] = pssh_infos->size() - 1;
|
||||
}
|
||||
|
||||
data += size;
|
||||
}
|
||||
|
@ -63,7 +74,6 @@ std::unique_ptr<PsshBoxBuilder> PsshBoxBuilder::ParseFromBox(
|
|||
uint32_t version_and_flags;
|
||||
RETURN_NULL_IF_FALSE(reader.Read4(&size));
|
||||
RETURN_NULL_IF_FALSE(reader.Read4(&box_type));
|
||||
RETURN_NULL_IF_FALSE(size == data_size);
|
||||
RETURN_NULL_IF_FALSE(box_type == FOURCC_pssh);
|
||||
RETURN_NULL_IF_FALSE(reader.Read4(&version_and_flags));
|
||||
|
||||
|
@ -90,10 +100,7 @@ std::unique_ptr<PsshBoxBuilder> PsshBoxBuilder::ParseFromBox(
|
|||
RETURN_NULL_IF_FALSE(
|
||||
reader.ReadToVector(&pssh_builder->pssh_data_, pssh_data_size));
|
||||
|
||||
// We should be at the end of the data. The reader should be initialized to
|
||||
// the data and size according to the size field of the box; therefore it
|
||||
// is an error if there are bytes remaining.
|
||||
RETURN_NULL_IF_FALSE(!reader.HasBytes(1));
|
||||
// Ignore extra data if there is any.
|
||||
return pssh_builder;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,15 +13,15 @@ namespace shaka {
|
|||
namespace media {
|
||||
|
||||
namespace {
|
||||
const uint8_t kV0BoxArray[] = {
|
||||
const uint8_t kSystemId1V0BoxArray[] = {
|
||||
0x00, 0x00, 0x00, 0x21, 'p', 's', 's', 'h', // Header
|
||||
0x00, 0x00, 0x00, 0x00, // Version = 0, flags = 0
|
||||
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, // System ID
|
||||
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
|
||||
0x00, 0x00, 0x00, 0x01, // Data size(1)
|
||||
0xFF
|
||||
0xFF,
|
||||
};
|
||||
const uint8_t kV1BoxArray[] = {
|
||||
const uint8_t kSystemId1V1BoxArray[] = {
|
||||
0x00, 0x00, 0x00, 0x35, 'p', 's', 's', 'h', // Header
|
||||
0x01, 0x00, 0x00, 0x00, // Version = 1, flags = 0
|
||||
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, // System ID
|
||||
|
@ -30,35 +30,69 @@ const uint8_t kV1BoxArray[] = {
|
|||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // First KID
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, // Data size(1)
|
||||
0xFF
|
||||
0xFF,
|
||||
};
|
||||
const uint8_t kSystemId2V0BoxArray[] = {
|
||||
0x00, 0x00, 0x00, 0x21, 'p', 's', 's', 'h', // Header
|
||||
0x00, 0x00, 0x00, 0x00, // Version = 0, flags = 0
|
||||
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, // System ID
|
||||
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11,
|
||||
0x00, 0x00, 0x00, 0x01, // Data size(1)
|
||||
0xFF,
|
||||
};
|
||||
|
||||
const uint8_t kTestSystemIdArray[] = {
|
||||
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, // System ID
|
||||
const uint8_t kTestSystemId1Array[] = {
|
||||
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11,
|
||||
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
|
||||
};
|
||||
const uint8_t kTestSystemId2Array[] = {
|
||||
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
|
||||
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11,
|
||||
};
|
||||
const uint8_t kTestKeyIdArray[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // First KID
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
const uint8_t kTestPsshDataArray[] = {0xFF};
|
||||
|
||||
std::vector<uint8_t> ConcatVectors(const std::vector<uint8_t>& a,
|
||||
const std::vector<uint8_t>& b) {
|
||||
std::vector<uint8_t> out;
|
||||
out.insert(out.end(), a.begin(), a.end());
|
||||
out.insert(out.end(), b.begin(), b.end());
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ConcatVectors(const std::vector<uint8_t>& a,
|
||||
const std::vector<uint8_t>& b,
|
||||
const std::vector<uint8_t>& c) {
|
||||
return ConcatVectors(ConcatVectors(a, b), c);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class PsshTest : public ::testing::Test {
|
||||
public:
|
||||
PsshTest()
|
||||
: v0_box_(kV0BoxArray, kV0BoxArray + arraysize(kV0BoxArray)),
|
||||
v1_box_(kV1BoxArray, kV1BoxArray + arraysize(kV1BoxArray)),
|
||||
test_system_id_(kTestSystemIdArray,
|
||||
kTestSystemIdArray + arraysize(kTestSystemIdArray)),
|
||||
test_key_id_(kTestKeyIdArray,
|
||||
kTestKeyIdArray + arraysize(kTestKeyIdArray)),
|
||||
test_pssh_data_(kTestPsshDataArray,
|
||||
kTestPsshDataArray + arraysize(kTestPsshDataArray)) {}
|
||||
: system_id1_v0_box_(std::begin(kSystemId1V0BoxArray),
|
||||
std::end(kSystemId1V0BoxArray)),
|
||||
system_id1_v1_box_(std::begin(kSystemId1V1BoxArray),
|
||||
std::end(kSystemId1V1BoxArray)),
|
||||
system_id2_v0_box_(std::begin(kSystemId2V0BoxArray),
|
||||
std::end(kSystemId2V0BoxArray)),
|
||||
test_system_id1_(std::begin(kTestSystemId1Array),
|
||||
std::end(kTestSystemId1Array)),
|
||||
test_system_id2_(std::begin(kTestSystemId2Array),
|
||||
std::end(kTestSystemId2Array)),
|
||||
test_key_id_(std::begin(kTestKeyIdArray), std::end(kTestKeyIdArray)),
|
||||
test_pssh_data_(std::begin(kTestPsshDataArray),
|
||||
std::end(kTestPsshDataArray)) {}
|
||||
|
||||
const std::vector<uint8_t> v0_box_;
|
||||
const std::vector<uint8_t> v1_box_;
|
||||
const std::vector<uint8_t> test_system_id_;
|
||||
const std::vector<uint8_t> system_id1_v0_box_;
|
||||
const std::vector<uint8_t> system_id1_v1_box_;
|
||||
const std::vector<uint8_t> system_id2_v0_box_;
|
||||
const std::vector<uint8_t> test_system_id1_;
|
||||
const std::vector<uint8_t> test_system_id2_;
|
||||
const std::vector<uint8_t> test_key_id_;
|
||||
const std::vector<uint8_t> test_pssh_data_;
|
||||
};
|
||||
|
@ -66,17 +100,17 @@ class PsshTest : public ::testing::Test {
|
|||
TEST_F(PsshTest, ParseBoxes_SupportsV0) {
|
||||
std::vector<ProtectionSystemSpecificInfo> info;
|
||||
ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes(
|
||||
v0_box_.data(), v0_box_.size(), &info));
|
||||
system_id1_v0_box_.data(), system_id1_v0_box_.size(), &info));
|
||||
|
||||
ASSERT_EQ(1u, info.size());
|
||||
EXPECT_EQ(test_system_id_, info[0].system_id);
|
||||
EXPECT_EQ(test_system_id1_, info[0].system_id);
|
||||
|
||||
std::unique_ptr<PsshBoxBuilder> pssh_builder =
|
||||
PsshBoxBuilder::ParseFromBox(info[0].psshs.data(), info[0].psshs.size());
|
||||
ASSERT_TRUE(pssh_builder);
|
||||
|
||||
ASSERT_EQ(0u, pssh_builder->key_ids().size());
|
||||
EXPECT_EQ(test_system_id_, pssh_builder->system_id());
|
||||
EXPECT_EQ(test_system_id1_, pssh_builder->system_id());
|
||||
EXPECT_EQ(test_pssh_data_, pssh_builder->pssh_data());
|
||||
EXPECT_EQ(0, pssh_builder->pssh_box_version());
|
||||
}
|
||||
|
@ -84,80 +118,74 @@ TEST_F(PsshTest, ParseBoxes_SupportsV0) {
|
|||
TEST_F(PsshTest, ParseBoxes_SupportsV1) {
|
||||
std::vector<ProtectionSystemSpecificInfo> info;
|
||||
ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes(
|
||||
v1_box_.data(), v1_box_.size(), &info));
|
||||
system_id1_v1_box_.data(), system_id1_v1_box_.size(), &info));
|
||||
|
||||
ASSERT_EQ(1u, info.size());
|
||||
EXPECT_EQ(test_system_id_, info[0].system_id);
|
||||
EXPECT_EQ(test_system_id1_, info[0].system_id);
|
||||
|
||||
std::unique_ptr<PsshBoxBuilder> pssh_builder =
|
||||
PsshBoxBuilder::ParseFromBox(info[0].psshs.data(), info[0].psshs.size());
|
||||
ASSERT_TRUE(pssh_builder);
|
||||
|
||||
ASSERT_EQ(1u, pssh_builder->key_ids().size());
|
||||
EXPECT_EQ(test_system_id_, pssh_builder->system_id());
|
||||
EXPECT_EQ(test_system_id1_, pssh_builder->system_id());
|
||||
EXPECT_EQ(test_key_id_, pssh_builder->key_ids()[0]);
|
||||
EXPECT_EQ(test_pssh_data_, pssh_builder->pssh_data());
|
||||
EXPECT_EQ(1, pssh_builder->pssh_box_version());
|
||||
}
|
||||
|
||||
TEST_F(PsshTest, ParseBoxes_SupportsConcatenatedBoxes) {
|
||||
std::vector<uint8_t> data;
|
||||
data.insert(data.end(), v1_box_.begin(), v1_box_.end());
|
||||
data.insert(data.end(), v0_box_.begin(), v0_box_.end());
|
||||
data.insert(data.end(), v1_box_.begin(), v1_box_.end());
|
||||
std::vector<uint8_t> data =
|
||||
ConcatVectors(system_id1_v0_box_, system_id2_v0_box_, system_id1_v1_box_);
|
||||
|
||||
std::vector<ProtectionSystemSpecificInfo> info;
|
||||
ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes(data.data(), data.size(),
|
||||
&info));
|
||||
ASSERT_EQ(3u, info.size());
|
||||
// The PSSHs are grouped by system id. Since there are only two system ids,
|
||||
// there are two ProtectionSystemSpecificInfo.
|
||||
ASSERT_EQ(2u, info.size());
|
||||
EXPECT_EQ(ConcatVectors(system_id1_v0_box_, system_id1_v1_box_),
|
||||
info[0].psshs);
|
||||
EXPECT_EQ(system_id2_v0_box_, info[1].psshs);
|
||||
|
||||
std::unique_ptr<PsshBoxBuilder> pssh_builder =
|
||||
PsshBoxBuilder::ParseFromBox(info[0].psshs.data(), info[0].psshs.size());
|
||||
ASSERT_TRUE(pssh_builder);
|
||||
|
||||
ASSERT_EQ(1u, pssh_builder->key_ids().size());
|
||||
EXPECT_EQ(test_system_id_, pssh_builder->system_id());
|
||||
EXPECT_EQ(test_key_id_, pssh_builder->key_ids()[0]);
|
||||
ASSERT_EQ(0u, pssh_builder->key_ids().size());
|
||||
EXPECT_EQ(test_system_id1_, pssh_builder->system_id());
|
||||
EXPECT_EQ(test_pssh_data_, pssh_builder->pssh_data());
|
||||
EXPECT_EQ(1, pssh_builder->pssh_box_version());
|
||||
EXPECT_EQ(0, pssh_builder->pssh_box_version());
|
||||
|
||||
pssh_builder =
|
||||
PsshBoxBuilder::ParseFromBox(info[1].psshs.data(), info[1].psshs.size());
|
||||
ASSERT_TRUE(pssh_builder);
|
||||
|
||||
ASSERT_EQ(0u, pssh_builder->key_ids().size());
|
||||
EXPECT_EQ(test_system_id_, pssh_builder->system_id());
|
||||
EXPECT_EQ(test_system_id2_, pssh_builder->system_id());
|
||||
EXPECT_EQ(test_pssh_data_, pssh_builder->pssh_data());
|
||||
EXPECT_EQ(0, pssh_builder->pssh_box_version());
|
||||
|
||||
pssh_builder =
|
||||
PsshBoxBuilder::ParseFromBox(info[2].psshs.data(), info[2].psshs.size());
|
||||
ASSERT_TRUE(pssh_builder);
|
||||
|
||||
ASSERT_EQ(1u, pssh_builder->key_ids().size());
|
||||
EXPECT_EQ(test_system_id_, pssh_builder->system_id());
|
||||
EXPECT_EQ(test_key_id_, pssh_builder->key_ids()[0]);
|
||||
EXPECT_EQ(test_pssh_data_, pssh_builder->pssh_data());
|
||||
EXPECT_EQ(1, pssh_builder->pssh_box_version());
|
||||
}
|
||||
|
||||
TEST_F(PsshTest, CreateBox_MakesV0Boxes) {
|
||||
PsshBoxBuilder pssh_builder;
|
||||
pssh_builder.set_system_id(kTestSystemIdArray, arraysize(kTestSystemIdArray));
|
||||
pssh_builder.set_system_id(kTestSystemId1Array,
|
||||
arraysize(kTestSystemId1Array));
|
||||
pssh_builder.set_pssh_data(test_pssh_data_);
|
||||
pssh_builder.set_pssh_box_version(0);
|
||||
|
||||
EXPECT_EQ(v0_box_, pssh_builder.CreateBox());
|
||||
EXPECT_EQ(system_id1_v0_box_, pssh_builder.CreateBox());
|
||||
}
|
||||
|
||||
TEST_F(PsshTest, CreateBox_MakesV1Boxes) {
|
||||
PsshBoxBuilder pssh_builder;
|
||||
pssh_builder.add_key_id(test_key_id_);
|
||||
pssh_builder.set_system_id(kTestSystemIdArray, arraysize(kTestSystemIdArray));
|
||||
pssh_builder.set_system_id(kTestSystemId1Array,
|
||||
arraysize(kTestSystemId1Array));
|
||||
pssh_builder.set_pssh_data(test_pssh_data_);
|
||||
pssh_builder.set_pssh_box_version(1);
|
||||
|
||||
EXPECT_EQ(v1_box_, pssh_builder.CreateBox());
|
||||
EXPECT_EQ(system_id1_v1_box_, pssh_builder.CreateBox());
|
||||
}
|
||||
|
||||
} // namespace media
|
||||
|
|
|
@ -57,6 +57,24 @@ CommonEncryptionRequest::ProtectionScheme ToCommonEncryptionProtectionScheme(
|
|||
}
|
||||
}
|
||||
|
||||
ProtectionSystemSpecificInfo ProtectionSystemInfoFromPsshProto(
|
||||
const CommonEncryptionResponse::Track::Pssh& pssh_proto) {
|
||||
PsshBoxBuilder pssh_builder;
|
||||
pssh_builder.set_system_id(kWidevineSystemId, arraysize(kWidevineSystemId));
|
||||
|
||||
if (pssh_proto.has_boxes()) {
|
||||
return {pssh_builder.system_id(),
|
||||
std::vector<uint8_t>(pssh_proto.boxes().begin(),
|
||||
pssh_proto.boxes().end())};
|
||||
} else {
|
||||
pssh_builder.set_pssh_box_version(0);
|
||||
const std::vector<uint8_t> pssh_data(pssh_proto.data().begin(),
|
||||
pssh_proto.data().end());
|
||||
pssh_builder.set_pssh_data(pssh_data);
|
||||
return {pssh_builder.system_id(), pssh_builder.CreateBox()};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
WidevineKeySource::WidevineKeySource(const std::string& server_url,
|
||||
|
@ -73,7 +91,8 @@ WidevineKeySource::WidevineKeySource(const std::string& server_url,
|
|||
key_production_started_(false),
|
||||
start_key_production_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
|
||||
base::WaitableEvent::InitialState::NOT_SIGNALED),
|
||||
first_crypto_period_index_(0) {
|
||||
first_crypto_period_index_(0),
|
||||
enable_entitlement_license_(false) {
|
||||
key_production_thread_.Start();
|
||||
}
|
||||
|
||||
|
@ -97,6 +116,8 @@ Status WidevineKeySource::FetchKeys(const std::vector<uint8_t>& content_id,
|
|||
common_encryption_request_->set_policy(policy);
|
||||
common_encryption_request_->set_protection_scheme(
|
||||
ToCommonEncryptionProtectionScheme(protection_scheme_));
|
||||
if (enable_entitlement_license_)
|
||||
common_encryption_request_->set_enable_entitlement_license(true);
|
||||
|
||||
return FetchKeysInternal(!kEnableKeyRotation, 0, false);
|
||||
}
|
||||
|
@ -218,10 +239,6 @@ void WidevineKeySource::set_key_fetcher(
|
|||
key_fetcher_ = std::move(key_fetcher);
|
||||
}
|
||||
|
||||
void WidevineKeySource::set_group_id(const std::vector<uint8_t>& group_id) {
|
||||
group_id_ = group_id;
|
||||
}
|
||||
|
||||
Status WidevineKeySource::GetKeyInternal(uint32_t crypto_period_index,
|
||||
const std::string& stream_label,
|
||||
EncryptionKey* key) {
|
||||
|
@ -430,19 +447,8 @@ bool WidevineKeySource::ExtractEncryptionKey(
|
|||
<< track.pssh_size();
|
||||
return false;
|
||||
}
|
||||
const auto& pssh_proto = track.pssh(0);
|
||||
const std::vector<uint8_t> pssh_data(pssh_proto.data().begin(),
|
||||
pssh_proto.data().end());
|
||||
|
||||
PsshBoxBuilder pssh_builder;
|
||||
pssh_builder.add_key_id(encryption_key->key_id);
|
||||
pssh_builder.set_system_id(kWidevineSystemId,
|
||||
arraysize(kWidevineSystemId));
|
||||
pssh_builder.set_pssh_box_version(0);
|
||||
pssh_builder.set_pssh_data(pssh_data);
|
||||
|
||||
encryption_key->key_system_info.push_back(
|
||||
{pssh_builder.system_id(), pssh_builder.CreateBox()});
|
||||
ProtectionSystemInfoFromPsshProto(track.pssh(0)));
|
||||
}
|
||||
encryption_key_map[stream_label] = std::move(encryption_key);
|
||||
}
|
||||
|
|
|
@ -68,9 +68,12 @@ class WidevineKeySource : public KeySource {
|
|||
/// @param key_fetcher points to the @b KeyFetcher object to be injected.
|
||||
void set_key_fetcher(std::unique_ptr<KeyFetcher> key_fetcher);
|
||||
|
||||
// Set the group id for the key source
|
||||
// @param group_id group identifier
|
||||
void set_group_id(const std::vector<uint8_t>& group_id);
|
||||
void set_group_id(const std::vector<uint8_t>& group_id) {
|
||||
group_id_ = group_id;
|
||||
}
|
||||
void set_enable_entitlement_license(bool enable_entitlement_license) {
|
||||
enable_entitlement_license_ = enable_entitlement_license;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef ProducerConsumerQueue<std::shared_ptr<EncryptionKeyMap>>
|
||||
|
@ -125,6 +128,7 @@ class WidevineKeySource : public KeySource {
|
|||
base::WaitableEvent start_key_production_;
|
||||
uint32_t first_crypto_period_index_;
|
||||
std::vector<uint8_t> group_id_;
|
||||
bool enable_entitlement_license_;
|
||||
std::unique_ptr<EncryptionKeyQueue> key_pool_;
|
||||
EncryptionKeyMap encryption_key_map_; // For non key rotation request.
|
||||
Status common_encryption_request_status_;
|
||||
|
|
|
@ -56,6 +56,12 @@ const char kExpectedRequestMessageFormat[] =
|
|||
R"("tracks":[{"type":"SD"},{"type":"HD"},{"type":"UHD1"},)"
|
||||
R"({"type":"UHD2"},{"type":"AUDIO"}],)"
|
||||
R"("drm_types":["WIDEVINE"],"protection_scheme":"%s"})";
|
||||
const char kExpectedRequestMessageFormatWithEntitlement[] =
|
||||
R"({"content_id":"%s","policy":"%s",)"
|
||||
R"("tracks":[{"type":"SD"},{"type":"HD"},{"type":"UHD1"},)"
|
||||
R"({"type":"UHD2"},{"type":"AUDIO"}],)"
|
||||
R"("drm_types":["WIDEVINE"],"protection_scheme":"%s",)"
|
||||
R"("enable_entitlement_license":true})";
|
||||
const char kExpectedRequestMessageWithAssetIdFormat[] =
|
||||
R"({"tracks":[{"type":"SD"},{"type":"HD"},{"type":"UHD1"},)"
|
||||
R"({"type":"UHD2"},{"type":"AUDIO"}],)"
|
||||
|
@ -68,6 +74,9 @@ const char kExpectedSignedMessageFormat[] =
|
|||
R"({"request":"%s","signature":"%s","signer":"%s"})";
|
||||
const char kTrackFormat[] = R"({"type":"%s","key_id":"%s","key":"%s",)"
|
||||
R"("pssh":[{"drm_type":"WIDEVINE","data":"%s"}]})";
|
||||
const char kTrackFormatWithBoxes[] =
|
||||
R"({"type":"%s","key_id":"%s","key":"%s",)"
|
||||
R"("pssh":[{"drm_type":"WIDEVINE","data":"%s","boxes":"%s"}]})";
|
||||
const char kClassicTrackFormat[] = R"({"type":"%s","key":"%s"})";
|
||||
const char kLicenseResponseFormat[] = R"({"status":"%s","tracks":[%s]})";
|
||||
const char kHttpResponseFormat[] = R"({"response":"%s"})";
|
||||
|
@ -115,6 +124,21 @@ std::string GetMockPsshData() {
|
|||
return kRequestPsshData;
|
||||
}
|
||||
|
||||
std::string GenerateMockLicenseResponseWithBoxes(const std::string& boxes) {
|
||||
const std::string kTrackTypes[] = {"SD", "HD", "UHD1", "UHD2", "AUDIO"};
|
||||
std::string tracks;
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
if (!tracks.empty())
|
||||
tracks += ",";
|
||||
tracks += base::StringPrintf(
|
||||
kTrackFormatWithBoxes, kTrackTypes[i].c_str(),
|
||||
Base64Encode(GetMockKeyId(kTrackTypes[i])).c_str(),
|
||||
Base64Encode(GetMockKey(kTrackTypes[i])).c_str(),
|
||||
Base64Encode(GetMockPsshData()).c_str(), boxes.c_str());
|
||||
}
|
||||
return base::StringPrintf(kLicenseResponseFormat, "OK", tracks.c_str());
|
||||
}
|
||||
|
||||
std::string GenerateMockLicenseResponse() {
|
||||
const std::string kTrackTypes[] = {"SD", "HD", "UHD1", "UHD2", "AUDIO"};
|
||||
std::string tracks;
|
||||
|
@ -323,6 +347,26 @@ TEST_F(WidevineKeySourceTest, NoRetryOnUnknownError) {
|
|||
widevine_key_source_->FetchKeys(content_id_, kPolicy).error_code());
|
||||
}
|
||||
|
||||
TEST_F(WidevineKeySourceTest, BoxesInResponse) {
|
||||
const char kMockBoxes[] = "mock_pssh_boxes";
|
||||
std::string mock_response = base::StringPrintf(
|
||||
kHttpResponseFormat, Base64Encode(GenerateMockLicenseResponseWithBoxes(
|
||||
Base64Encode(kMockBoxes)))
|
||||
.c_str());
|
||||
|
||||
EXPECT_CALL(*mock_key_fetcher_, FetchKeys(_, _, _))
|
||||
.WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK)));
|
||||
|
||||
CreateWidevineKeySource();
|
||||
ASSERT_OK(widevine_key_source_->FetchKeys(content_id_, kPolicy));
|
||||
|
||||
const char kHdStreamLabel[] = "HD";
|
||||
EncryptionKey encryption_key;
|
||||
ASSERT_OK(widevine_key_source_->GetKey(kHdStreamLabel, &encryption_key));
|
||||
ASSERT_EQ(1u, encryption_key.key_system_info.size());
|
||||
ASSERT_EQ(kMockBoxes, ToString(encryption_key.key_system_info.front().psshs));
|
||||
}
|
||||
|
||||
class WidevineKeySourceParameterizedTest
|
||||
: public WidevineKeySourceTest,
|
||||
public WithParamInterface<std::tr1::tuple<bool, bool, FourCC>> {
|
||||
|
@ -453,6 +497,21 @@ TEST_P(WidevineKeySourceParameterizedTest, LicenseStatusClassicOK) {
|
|||
VerifyKeys(true);
|
||||
}
|
||||
|
||||
TEST_P(WidevineKeySourceParameterizedTest, VerifyEntitlementLicenseRequest) {
|
||||
const std::string expected_message =
|
||||
base::StringPrintf(kExpectedRequestMessageFormatWithEntitlement,
|
||||
Base64Encode(kContentId).c_str(), kPolicy,
|
||||
GetExpectedProtectionScheme().c_str());
|
||||
EXPECT_CALL(*mock_request_signer_,
|
||||
GenerateSignature(StrEq(expected_message), _))
|
||||
.WillOnce(Return(false));
|
||||
|
||||
CreateWidevineKeySource();
|
||||
widevine_key_source_->set_enable_entitlement_license(true);
|
||||
widevine_key_source_->set_signer(std::move(mock_request_signer_));
|
||||
ASSERT_NOT_OK(widevine_key_source_->FetchKeys(content_id_, kPolicy));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
const char kCryptoPeriodRequestMessageFormat[] =
|
||||
|
|
|
@ -59,6 +59,8 @@ struct WidevineEncryptionParams {
|
|||
WidevineSigner signer;
|
||||
/// Group identifier, if present licenses will belong to this group.
|
||||
std::vector<uint8_t> group_id;
|
||||
/// Enables entitlement license when set to true.
|
||||
bool enable_entitlement_license;
|
||||
};
|
||||
|
||||
/// PlayReady encryption parameters.
|
||||
|
|
Loading…
Reference in New Issue