Implement multi DRM support. (Part 3)

- Allow including Widevine and Common SystemID PSSH boxes
  for PlayReadyKeySource.
- --playready_key_id and --playready_key flags are deprecated.
- --enable_raw_key_encryption already supports playready PSSH generation.

Addresses issue #245

Change-Id: I072d4f43a3239875959e4c5b1eb6854415d7367e
This commit is contained in:
Haoming Chen 2018-04-20 10:46:14 -07:00
parent 2563ae7fe9
commit aa4b573342
12 changed files with 60 additions and 120 deletions

View File

@ -337,8 +337,6 @@ base::Optional<PackagingParams> GetPackagingParams() {
FLAGS_client_cert_private_key_file;
playready.client_cert_private_key_password =
FLAGS_client_cert_private_key_password;
playready.key_id = FLAGS_playready_key_id_bytes;
playready.key = FLAGS_playready_key_bytes;
break;
}
case KeyProvider::kRawKey: {

View File

@ -98,16 +98,8 @@ std::unique_ptr<KeySource> CreateEncryptionKeySource(
break;
}
case KeyProvider::kPlayready: {
// TODO(hmchen): add multiple DRM support for playready key source.
const PlayreadyEncryptionParams& playready = encryption_params.playready;
if (!playready.key_id.empty() || !playready.key.empty()) {
if (playready.key_id.empty() || playready.key.empty()) {
LOG(ERROR) << "Either playready key_id or key is not set.";
return nullptr;
}
encryption_key_source = PlayReadyKeySource::CreateFromKeyAndKeyId(
playready.key_id, playready.key);
} else if (!playready.key_server_url.empty() ||
if (!playready.key_server_url.empty() ||
!playready.program_identifier.empty()) {
if (playready.key_server_url.empty() ||
playready.program_identifier.empty()) {
@ -128,10 +120,11 @@ std::unique_ptr<KeySource> CreateEncryptionKeySource(
playready_key_source.reset(new PlayReadyKeySource(
playready.key_server_url, playready.client_cert_file,
playready.client_cert_private_key_file,
playready.client_cert_private_key_password));
playready.client_cert_private_key_password,
protection_systems_flags));
} else {
playready_key_source.reset(
new PlayReadyKeySource(playready.key_server_url));
playready_key_source.reset(new PlayReadyKeySource(
playready.key_server_url, protection_systems_flags));
}
if (!playready.ca_file.empty()) {
playready_key_source->SetCaFile(playready.ca_file);

View File

@ -16,8 +16,6 @@ DEFINE_bool(enable_playready_encryption,
DEFINE_string(playready_server_url, "", "PlayReady packaging server url.");
DEFINE_string(program_identifier, "",
"Program identifier for packaging request.");
DEFINE_hex_bytes(playready_key_id, "", "PlayReady key id in hex.");
DEFINE_hex_bytes(playready_key, "", "PlayReady key in hex.");
DEFINE_string(ca_file, "",
"Absolute path to the Certificate Authority file for the "
"server cert. PEM format");
@ -29,6 +27,9 @@ DEFINE_string(client_cert_private_key_password, "",
"Password to the private key file.");
namespace shaka {
namespace {
const bool kFlagIsOptional = true;
}
bool ValidatePRCryptoFlags() {
bool success = true;
@ -36,35 +37,11 @@ bool ValidatePRCryptoFlags() {
const char playready_label[] = "--enable_playready_encryption";
bool playready_enabled = FLAGS_enable_playready_encryption;
if (!ValidateFlag("playready_server_url", FLAGS_playready_server_url,
playready_enabled, true, playready_label)) {
playready_enabled, !kFlagIsOptional, playready_label)) {
success = false;
}
if (!ValidateFlag("program_identifier", FLAGS_program_identifier,
playready_enabled, true, playready_label)) {
success = false;
}
bool use_packaging = !FLAGS_playready_server_url.empty() &&
!FLAGS_program_identifier.empty();
if (!ValidateFlag("playready_key_id", FLAGS_playready_key_id_bytes,
playready_enabled, true, playready_label)) {
success = false;
}
if (!ValidateFlag("playready_key", FLAGS_playready_key_bytes,
playready_enabled, true, playready_label)) {
success = false;
}
bool use_raw_key = !FLAGS_playready_key_id_bytes.empty() &&
!FLAGS_playready_key_bytes.empty();
if (playready_enabled && !use_packaging && !use_raw_key) {
PrintError("combination of --playready_server_url and "
"--program_identifier or --playready_key_id and playready_key are "
"required");
success = false;
}
if (use_packaging && use_raw_key) {
PrintError("combination of --playready_server_url, --program_identifier, "
"--playready_key_id, and playready_key is not valid");
playready_enabled, !kFlagIsOptional, playready_label)) {
success = false;
}
return success;

View File

@ -16,8 +16,6 @@
DECLARE_bool(enable_playready_encryption);
DECLARE_string(playready_server_url);
DECLARE_string(program_identifier);
DECLARE_hex_bytes(playready_key_id);
DECLARE_hex_bytes(playready_key);
DECLARE_string(ca_file);
DECLARE_string(client_cert_file);
DECLARE_string(client_cert_private_key_file);

View File

@ -20,6 +20,14 @@ DEFINE_double(availability_time_offset,
0,
"This flag is deprecated. Use suggested_presentation_delay "
"instead which can achieve similar effect.");
DEFINE_string(playready_key_id,
"",
"This flag is deprecated. Use --enable_raw_key_encryption with "
"--generate_playready_pssh to generate PlayReady PSSH.");
DEFINE_string(playready_key,
"",
"This flag is deprecated. Use --enable_raw_key_encryption with "
"--generate_playready_pssh to generate PlayReady PSSH.");
// The current gflags library does not provide a way to check whether a flag is
// set in command line. If a flag has a different value to its default value,
@ -47,3 +55,5 @@ DEFINE_validator(profile, &InformRetiredStringFlag);
DEFINE_validator(single_segment, &InformRetiredDefaultTrueFlag);
DEFINE_validator(webm_subsample_encryption, &InformRetiredDefaultTrueFlag);
DEFINE_validator(availability_time_offset, &InformRetiredDefaultDoubleFlag);
DEFINE_validator(playready_key_id, &InformRetiredStringFlag);
DEFINE_validator(playready_key, &InformRetiredStringFlag);

View File

@ -10,3 +10,5 @@ DECLARE_string(profile);
DECLARE_bool(single_segment);
DECLARE_bool(webm_subsample_encryption);
DECLARE_double(availability_time_offset);
DECLARE_string(playready_key_id);
DECLARE_string(playready_key);

View File

@ -60,8 +60,10 @@ bool Base64StringToBytes(const std::string& base64_string,
}
}
PlayReadyKeySource::PlayReadyKeySource(const std::string& server_url)
: KeySource(PLAYREADY_PROTECTION_SYSTEM_FLAG),
PlayReadyKeySource::PlayReadyKeySource(const std::string& server_url,
int protection_system_flags)
// Playready PSSH is retrived from Playready server response.
: KeySource(protection_system_flags & ~PLAYREADY_PROTECTION_SYSTEM_FLAG),
encryption_key_(new EncryptionKey),
server_url_(server_url) {}
@ -69,8 +71,10 @@ PlayReadyKeySource::PlayReadyKeySource(
const std::string& server_url,
const std::string& client_cert_file,
const std::string& client_cert_private_key_file,
const std::string& client_cert_private_key_password)
: KeySource(PLAYREADY_PROTECTION_SYSTEM_FLAG),
const std::string& client_cert_private_key_password,
int protection_system_flags)
// Playready PSSH is retrived from Playready server response.
: KeySource(protection_system_flags & ~PLAYREADY_PROTECTION_SYSTEM_FLAG),
encryption_key_(new EncryptionKey),
server_url_(server_url),
client_cert_file_(client_cert_file),
@ -84,30 +88,6 @@ PlayReadyKeySource::PlayReadyKeySource(
PlayReadyKeySource::~PlayReadyKeySource() {}
std::unique_ptr<PlayReadyKeySource> PlayReadyKeySource::CreateFromKeyAndKeyId(
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key) {
std::unique_ptr<EncryptionKey> encryption_key(new EncryptionKey);
encryption_key->key_id = key_id;
encryption_key->key = key;
ProtectionSystemSpecificInfo info;
info.add_key_id(key_id);
info.set_system_id(kPlayReadySystemId, arraysize(kPlayReadySystemId));
std::vector<uint8_t> pssh_data =
PlayReadyPsshGenerator::GeneratePsshFromKeyIdAndKey(key_id, key);
if (pssh_data.empty()) {
DLOG(INFO) << "Fail to generate PlayReady PSSH.";
return nullptr;
}
info.set_pssh_data(pssh_data);
encryption_key->key_system_info.push_back(info);
return std::unique_ptr<PlayReadyKeySource>(
new PlayReadyKeySource(std::move(encryption_key)));
}
Status RetrieveTextInXMLElement(const std::string& element,
const std::string& xml,
std::string* value) {
@ -203,7 +183,15 @@ Status PlayReadyKeySource::FetchKeysWithProgramIdentifier(
}
status = SetKeyInformationFromServerResponse(acquire_license_response,
encryption_key.get());
encryption_key_ = std::move(encryption_key);
// Playready does not specify different streams.
const char kEmptyDrmLabel[] = "";
EncryptionKeyMap encryption_key_map;
encryption_key_map[kEmptyDrmLabel] = std::move(encryption_key);
status = UpdateProtectionSystemInfo(&encryption_key_map);
if (!status.ok()) {
return status;
}
encryption_key_ = std::move(encryption_key_map[kEmptyDrmLabel]);
return Status::OK;
}

View File

@ -21,17 +21,23 @@ class PlayReadyKeySource : public KeySource {
public:
/// Creates a new PlayReadyKeySource from the given packaging information.
/// @param server_url PlayReady packaging server url.
PlayReadyKeySource(const std::string& server_url);
/// @param proteciton_systems_flags is the flags indicating which PSSH should
/// be included.
PlayReadyKeySource(const std::string& server_url,
int protection_scheme_flags);
/// Creates a new PlayReadyKeySource from the given packaging information.
/// @param server_url PlayReady packaging server url.
/// @param client_cert_file absolute path to a client certificate.
/// @param client_cert_private_key_file absolute path to the private file
/// for the client certificate.
/// @param client_cert_private_key_password password for the private key.
/// @param proteciton_systems_flags is the flags indicating which PSSH should
/// be included.
PlayReadyKeySource(const std::string& server_url,
const std::string& client_cert_file,
const std::string& client_cert_private_key_file,
const std::string& client_cert_private_key_password);
const std::string& client_cert_private_key_password,
int protection_scheme_flags);
~PlayReadyKeySource() override;
/// @name KeySource implementation overrides.

View File

@ -133,22 +133,6 @@ PlayReadyPsshGenerator::PlayReadyPsshGenerator()
PlayReadyPsshGenerator::~PlayReadyPsshGenerator() {}
// TODO(hmchen): remove this static function when implementing the multi
// DRM support for PlayReadyKeySource.
// static
std::vector<uint8_t> PlayReadyPsshGenerator::GeneratePsshFromKeyIdAndKey(
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key) {
std::vector<uint8_t> pssh_data;
Status status = GeneratePlayReadyPsshData(key_id, key, &pssh_data);
if (!status.ok()) {
LOG(ERROR) << status.ToString();
return std::vector<uint8_t>();
}
return pssh_data;
}
bool PlayReadyPsshGenerator::SupportMultipleKeys() {
return false;
}

View File

@ -25,10 +25,6 @@ class PlayReadyPsshGenerator : public PsshGenerator {
PlayReadyPsshGenerator();
~PlayReadyPsshGenerator() override;
static std::vector<uint8_t> GeneratePsshFromKeyIdAndKey(
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key);
/// @name PsshGenerator implemetation overrides.
/// @{
bool SupportMultipleKeys() override;

View File

@ -4,6 +4,7 @@
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "packager/media/base/playready_pssh_generator.h"
@ -11,6 +12,8 @@
#include "packager/media/base/widevine_pssh_generator.h"
#include "packager/status_test_util.h"
using ::testing::ElementsAreArray;
namespace shaka {
namespace media {
namespace {
@ -94,16 +97,6 @@ std::vector<uint8_t> GetTestKey1() {
std::vector<uint8_t> GetTestKeyId2() {
return std::vector<uint8_t>(std::begin(kTestKeyId2), std::end(kTestKeyId2));
}
std::vector<uint8_t> GetExpectedPlayReadyPsshData() {
return std::vector<uint8_t>(std::begin(kExpectedPlayReadyPsshData),
std::end(kExpectedPlayReadyPsshData));
}
std::vector<uint8_t> GetExpectedWidevinePsshData() {
return std::vector<uint8_t>(std::begin(kExpectedWidevinePsshData),
std::end(kExpectedWidevinePsshData));
}
} // namespace
// Folowing tests test PlayReady, RawKey and Widevine PSSH generators. Note
@ -132,13 +125,13 @@ TEST(PsshGeneratorTest, GeneratePlayReadyPsshDataFromKeyIdAndKey) {
const std::vector<uint8_t> kTestKey = GetTestKey1();
std::unique_ptr<PlayReadyPsshGenerator> playready_pssh_generator(
new PlayReadyPsshGenerator());
base::Optional<std::vector<uint8_t>> pssh_data =
playready_pssh_generator->GeneratePsshDataFromKeyIdAndKey(kTestKeyId,
kTestKey);
ASSERT_TRUE(pssh_data);
ProtectionSystemSpecificInfo info;
EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey(
kTestKeyId, kTestKey, &info));
const std::vector<uint8_t> kExpectedPsshData = GetExpectedPlayReadyPsshData();
EXPECT_EQ(kExpectedPsshData, pssh_data.value());
EXPECT_THAT(info.pssh_data(),
ElementsAreArray(std::begin(kExpectedPlayReadyPsshData),
std::end(kExpectedPlayReadyPsshData)));
}
TEST(PsshGeneratorTest, GenerateRawKeyPsshDataFromKeyIds) {
@ -171,8 +164,9 @@ TEST(PsshGeneratorTest, GenerateWidevinePsshDataFromKeyIds) {
ASSERT_OK(
widevine_pssh_generator->GeneratePsshFromKeyIds(kTestKeyIds, &info));
const std::vector<uint8_t> kExpectedPsshData = GetExpectedWidevinePsshData();
EXPECT_EQ(kExpectedPsshData, info.pssh_data());
EXPECT_THAT(info.pssh_data(),
ElementsAreArray(std::begin(kExpectedWidevinePsshData),
std::end(kExpectedWidevinePsshData)));
}
TEST(PsshGeneratorTest, GenerateWidevinyPsshDataFromKeyIdAndKey) {

View File

@ -82,12 +82,6 @@ struct PlayreadyEncryptionParams {
std::string client_cert_private_key_file;
/// Password to the private key file.
std::string client_cert_private_key_password;
// TODO(kqyang): move raw playready key generation to RawKey.
/// Provides a raw Playready KeyId.
std::vector<uint8_t> key_id;
/// Provides a raw Playready Key.
std::vector<uint8_t> key;
};
/// Raw key encryption/decryption parameters, i.e. with key parameters provided.