Add packager flag to include common PSSH with Widevine.

The flag --include_common_pssh will add another PSSH box in addition
to the Widevine one which will contain the key IDs of all the keys.

Closes #88

Change-Id: Ic719b19747530f0e4856cfb36471a644d572a734
This commit is contained in:
Jacob Trimble 2016-03-21 11:09:24 -07:00
parent 94b4c52bf5
commit 6f8cbf90b9
8 changed files with 91 additions and 30 deletions

View File

@ -80,7 +80,7 @@ scoped_ptr<KeySource> CreateEncryptionKeySource() {
scoped_ptr<KeySource> encryption_key_source;
if (FLAGS_enable_widevine_encryption) {
scoped_ptr<WidevineKeySource> widevine_key_source(
new WidevineKeySource(FLAGS_key_server_url));
new WidevineKeySource(FLAGS_key_server_url, FLAGS_include_common_pssh));
if (!FLAGS_signer.empty()) {
scoped_ptr<RequestSigner> request_signer(CreateSigner());
if (!request_signer)
@ -111,7 +111,7 @@ scoped_ptr<KeySource> CreateDecryptionKeySource() {
scoped_ptr<KeySource> decryption_key_source;
if (FLAGS_enable_widevine_decryption) {
scoped_ptr<WidevineKeySource> widevine_key_source(
new WidevineKeySource(FLAGS_key_server_url));
new WidevineKeySource(FLAGS_key_server_url, FLAGS_include_common_pssh));
if (!FLAGS_signer.empty()) {
scoped_ptr<RequestSigner> request_signer(CreateSigner());
if (!request_signer)

View File

@ -23,6 +23,11 @@ DEFINE_bool(enable_widevine_decryption,
"Enable decryption with Widevine license server/proxy. User should "
"provide either AES signing key (--aes_signing_key, "
"--aes_signing_iv) or RSA signing key (--rsa_signing_key_path).");
DEFINE_bool(include_common_pssh,
false,
"When using Widevine encryption, include an additional v1 PSSH box "
"for the common system ID that includes the key IDs. See: "
"http://goo.gl/PHZDAF");
DEFINE_string(key_server_url, "", "Key server url. Required for encryption and "
"decryption");
DEFINE_string(content_id, "", "Content Id (hex).");
@ -104,6 +109,11 @@ bool ValidateWidevineCryptoFlags() {
widevine_encryption_label)) {
success = false;
}
if (FLAGS_include_common_pssh && !FLAGS_enable_widevine_encryption) {
PrintError("--include_common_pssh is only valid with "
"--enable_widevine_encryption");
success = false;
}
if (FLAGS_max_sd_pixels <= 0) {
PrintError("--max_sd_pixels must be positive.");

View File

@ -13,6 +13,7 @@
DECLARE_bool(enable_widevine_encryption);
DECLARE_bool(enable_widevine_decryption);
DECLARE_bool(include_common_pssh);
DECLARE_string(key_server_url);
DECLARE_string(content_id);
DECLARE_string(policy);

View File

@ -12,15 +12,6 @@
namespace edash_packager {
namespace media {
namespace {
// Common SystemID defined by EME, which requires Key System implementations
// supporting ISO Common Encryption to support this SystemID and format.
// http://goo.gl/PHZDAF
const uint8_t kCommonSystemId[] = {0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2,
0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e,
0x52, 0xe2, 0xfb, 0x4b};
} // namespace
FixedKeySource::~FixedKeySource() {}
Status FixedKeySource::FetchKeys(const std::vector<uint8_t>& pssh_box) {

View File

@ -16,6 +16,13 @@
namespace edash_packager {
namespace media {
// Common SystemID defined by EME, which requires Key System implementations
// supporting ISO Common Encryption to support this SystemID and format.
// http://goo.gl/PHZDAF
const uint8_t kCommonSystemId[] = {0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2,
0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e,
0x52, 0xe2, 0xfb, 0x4b};
/// A key source that uses fixed keys for encryption.
class FixedKeySource : public KeySource {
public:

View File

@ -6,12 +6,15 @@
#include "packager/media/base/widevine_key_source.h"
#include <set>
#include "packager/base/base64.h"
#include "packager/base/bind.h"
#include "packager/base/json/json_reader.h"
#include "packager/base/json/json_writer.h"
#include "packager/base/memory/ref_counted.h"
#include "packager/base/stl_util.h"
#include "packager/media/base/fixed_key_source.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"
@ -142,13 +145,15 @@ class WidevineKeySource::RefCountedEncryptionKeyMap
DISALLOW_COPY_AND_ASSIGN(RefCountedEncryptionKeyMap);
};
WidevineKeySource::WidevineKeySource(const std::string& server_url)
WidevineKeySource::WidevineKeySource(const std::string& server_url,
bool add_common_pssh)
: key_production_thread_("KeyProductionThread",
base::Bind(&WidevineKeySource::FetchKeysTask,
base::Unretained(this))),
key_fetcher_(new HttpKeyFetcher(kKeyFetchTimeoutInSeconds)),
server_url_(server_url),
crypto_period_count_(kDefaultCryptoPeriodCount),
add_common_pssh_(add_common_pssh),
key_production_started_(false),
start_key_production_(false, false),
first_crypto_period_index_(0) {
@ -572,8 +577,27 @@ bool WidevineKeySource::ExtractEncryptionKey(
encryption_key_map[track_type] = encryption_key.release();
}
// NOTE: To support version 1 pssh, update ProtectionSystemSpecificInfo to
// include all key IDs in |encryption_key_map|.
// If the flag exists, create a common system ID PSSH box that contains the
// key IDs of all the keys.
if (add_common_pssh_ && !widevine_classic) {
std::set<std::vector<uint8_t>> key_ids;
for (const EncryptionKeyMap::value_type& pair : encryption_key_map) {
key_ids.insert(pair.second->key_id);
}
// Create a common system PSSH box.
ProtectionSystemSpecificInfo info;
info.set_system_id(kCommonSystemId, arraysize(kCommonSystemId));
info.set_pssh_box_version(1);
for (const std::vector<uint8_t>& key_id : key_ids) {
info.add_key_id(key_id);
}
for (const EncryptionKeyMap::value_type& pair : encryption_key_map) {
pair.second->key_system_info.push_back(info);
}
}
DCHECK(!encryption_key_map.empty());
if (!enable_key_rotation) {
encryption_key_map_ = encryption_key_map;

View File

@ -26,7 +26,7 @@ template <class T> class ProducerConsumerQueue;
class WidevineKeySource : public KeySource {
public:
/// @param server_url is the Widevine common encryption server url.
explicit WidevineKeySource(const std::string& server_url);
WidevineKeySource(const std::string& server_url, bool add_common_pssh);
~WidevineKeySource() override;
@ -113,6 +113,7 @@ class WidevineKeySource : public KeySource {
const uint32_t crypto_period_count_;
base::Lock lock_;
bool add_common_pssh_;
bool key_production_started_;
base::WaitableEvent start_key_production_;
uint32_t first_crypto_period_index_;

View File

@ -7,9 +7,12 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <algorithm>
#include "packager/base/base64.h"
#include "packager/base/strings/string_number_conversions.h"
#include "packager/base/strings/stringprintf.h"
#include "packager/media/base/fixed_key_source.h"
#include "packager/media/base/key_fetcher.h"
#include "packager/media/base/request_signer.h"
#include "packager/media/base/test/status_test_util.h"
@ -156,7 +159,8 @@ class MockKeyFetcher : public KeyFetcher {
DISALLOW_COPY_AND_ASSIGN(MockKeyFetcher);
};
class WidevineKeySourceTest : public ::testing::Test {
class WidevineKeySourceTest : public ::testing::Test,
public ::testing::WithParamInterface<bool> {
public:
WidevineKeySourceTest()
: mock_request_signer_(new MockRequestSigner(kSignerName)),
@ -170,7 +174,7 @@ class WidevineKeySourceTest : public ::testing::Test {
protected:
void CreateWidevineKeySource() {
widevine_key_source_.reset(new WidevineKeySource(kServerUrl));
widevine_key_source_.reset(new WidevineKeySource(kServerUrl, GetParam()));
widevine_key_source_->set_key_fetcher(mock_key_fetcher_.Pass());
}
@ -183,11 +187,30 @@ class WidevineKeySourceTest : public ::testing::Test {
&encryption_key));
EXPECT_EQ(GetMockKey(kTrackTypes[i]), ToString(encryption_key.key));
if (!classic) {
ASSERT_EQ(1u, encryption_key.key_system_info.size());
ASSERT_EQ(GetParam() ? 2u : 1u, encryption_key.key_system_info.size());
EXPECT_EQ(GetMockKeyId(kTrackTypes[i]),
ToString(encryption_key.key_id));
EXPECT_EQ(GetMockPsshData(kTrackTypes[i]),
ToString(encryption_key.key_system_info[0].pssh_data()));
if (GetParam()) {
// Each of the keys contains all the key IDs.
const std::vector<uint8_t> common_system_id(
kCommonSystemId, kCommonSystemId + arraysize(kCommonSystemId));
ASSERT_EQ(common_system_id,
encryption_key.key_system_info[1].system_id());
const std::vector<std::vector<uint8_t>>& key_ids =
encryption_key.key_system_info[1].key_ids();
ASSERT_EQ(arraysize(kTrackTypes), key_ids.size());
for (size_t j = 0; j < arraysize(kTrackTypes); ++j) {
// Because they are stored in a std::set, the order may change.
const std::string key_id_str = GetMockKeyId(kTrackTypes[j]);
const std::vector<uint8_t> key_id(key_id_str.begin(),
key_id_str.end());
EXPECT_THAT(key_ids, testing::Contains(key_id));
}
}
}
}
}
@ -200,7 +223,7 @@ class WidevineKeySourceTest : public ::testing::Test {
DISALLOW_COPY_AND_ASSIGN(WidevineKeySourceTest);
};
TEST_F(WidevineKeySourceTest, GetTrackTypeFromString) {
TEST_P(WidevineKeySourceTest, GetTrackTypeFromString) {
EXPECT_EQ(KeySource::TRACK_TYPE_SD,
KeySource::GetTrackTypeFromString("SD"));
EXPECT_EQ(KeySource::TRACK_TYPE_HD,
@ -211,7 +234,7 @@ TEST_F(WidevineKeySourceTest, GetTrackTypeFromString) {
KeySource::GetTrackTypeFromString("FOO"));
}
TEST_F(WidevineKeySourceTest, GenerateSignatureFailure) {
TEST_P(WidevineKeySourceTest, GenerateSignatureFailure) {
EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _))
.WillOnce(Return(false));
@ -223,7 +246,7 @@ TEST_F(WidevineKeySourceTest, GenerateSignatureFailure) {
// Check whether expected request message and post data was generated and
// verify the correct behavior on http failure.
TEST_F(WidevineKeySourceTest, HttpFetchFailure) {
TEST_P(WidevineKeySourceTest, HttpFetchFailure) {
std::string expected_message = base::StringPrintf(
kExpectedRequestMessageFormat, Base64Encode(kContentId).c_str(), kPolicy);
EXPECT_CALL(*mock_request_signer_,
@ -246,7 +269,7 @@ TEST_F(WidevineKeySourceTest, HttpFetchFailure) {
widevine_key_source_->FetchKeys(content_id_, kPolicy));
}
TEST_F(WidevineKeySourceTest, LicenseStatusCencOK) {
TEST_P(WidevineKeySourceTest, LicenseStatusCencOK) {
std::string mock_response = base::StringPrintf(
kHttpResponseFormat, Base64Encode(GenerateMockLicenseResponse()).c_str());
@ -258,7 +281,7 @@ TEST_F(WidevineKeySourceTest, LicenseStatusCencOK) {
VerifyKeys(false);
}
TEST_F(WidevineKeySourceTest, LicenseStatusCencNotOK) {
TEST_P(WidevineKeySourceTest, LicenseStatusCencNotOK) {
std::string mock_response = base::StringPrintf(
kHttpResponseFormat, Base64Encode(
GenerateMockClassicLicenseResponse()).c_str());
@ -272,7 +295,7 @@ TEST_F(WidevineKeySourceTest, LicenseStatusCencNotOK) {
.error_code());
}
TEST_F(WidevineKeySourceTest, LicenseStatusCencWithPsshBoxOK) {
TEST_P(WidevineKeySourceTest, LicenseStatusCencWithPsshBoxOK) {
std::string expected_message =
base::StringPrintf(kExpectedRequestMessageWithPsshFormat,
Base64Encode(kRequestPsshData).c_str());
@ -293,7 +316,7 @@ TEST_F(WidevineKeySourceTest, LicenseStatusCencWithPsshBoxOK) {
VerifyKeys(false);
}
TEST_F(WidevineKeySourceTest, LicenseStatusCencWithKeyIdsOK) {
TEST_P(WidevineKeySourceTest, LicenseStatusCencWithKeyIdsOK) {
std::string expected_pssh_data(
kRequestPsshDataFromKeyIds,
kRequestPsshDataFromKeyIds + arraysize(kRequestPsshDataFromKeyIds));
@ -318,7 +341,7 @@ TEST_F(WidevineKeySourceTest, LicenseStatusCencWithKeyIdsOK) {
VerifyKeys(false);
}
TEST_F(WidevineKeySourceTest, LicenseStatusClassicOK) {
TEST_P(WidevineKeySourceTest, LicenseStatusClassicOK) {
std::string expected_message = base::StringPrintf(
kExpectedRequestMessageWithAssetIdFormat, kClassicAssetId);
EXPECT_CALL(*mock_request_signer_,
@ -337,7 +360,7 @@ TEST_F(WidevineKeySourceTest, LicenseStatusClassicOK) {
VerifyKeys(true);
}
TEST_F(WidevineKeySourceTest, RetryOnHttpTimeout) {
TEST_P(WidevineKeySourceTest, RetryOnHttpTimeout) {
std::string mock_response = base::StringPrintf(
kHttpResponseFormat, Base64Encode(GenerateMockLicenseResponse()).c_str());
@ -351,7 +374,7 @@ TEST_F(WidevineKeySourceTest, RetryOnHttpTimeout) {
VerifyKeys(false);
}
TEST_F(WidevineKeySourceTest, RetryOnTransientError) {
TEST_P(WidevineKeySourceTest, RetryOnTransientError) {
std::string mock_license_status = base::StringPrintf(
kLicenseResponseFormat, kLicenseStatusTransientError, "");
std::string mock_response = base::StringPrintf(
@ -371,7 +394,7 @@ TEST_F(WidevineKeySourceTest, RetryOnTransientError) {
VerifyKeys(false);
}
TEST_F(WidevineKeySourceTest, NoRetryOnUnknownError) {
TEST_P(WidevineKeySourceTest, NoRetryOnUnknownError) {
std::string mock_license_status = base::StringPrintf(
kLicenseResponseFormat, kLicenseStatusUnknownError, "");
std::string mock_response = base::StringPrintf(
@ -425,7 +448,7 @@ std::string GenerateMockKeyRotationLicenseResponse(
} // namespace
TEST_F(WidevineKeySourceTest, KeyRotationTest) {
TEST_P(WidevineKeySourceTest, KeyRotationTest) {
const uint32_t kFirstCryptoPeriodIndex = 8;
const uint32_t kCryptoPeriodCount = 10;
// Array of indexes to be checked.
@ -491,5 +514,9 @@ TEST_F(WidevineKeySourceTest, KeyRotationTest) {
EXPECT_EQ(error::INVALID_ARGUMENT, status.error_code());
}
INSTANTIATE_TEST_CASE_P(WidevineKeySourceInstance,
WidevineKeySourceTest,
::testing::Bool());
} // namespace media
} // namespace edash_packager