Implement multi DRM support. (Part 1)

- Refactoring current clearkey DRM support for Widevine key source.
- Add playready pssh box in Widevine key source encrypted content.

Partially addresses issue #245

Change-Id: Id30ef02ff9f8dc0e2c58d62479e07896452919cc
This commit is contained in:
Haoming Chen 2017-09-12 12:22:39 -07:00
parent 62bfc1c6c1
commit 76d68de6c1
33 changed files with 1083 additions and 213 deletions

View File

@ -15,6 +15,7 @@
#include "packager/app/muxer_flags.h" #include "packager/app/muxer_flags.h"
#include "packager/app/packager_util.h" #include "packager/app/packager_util.h"
#include "packager/app/playready_key_encryption_flags.h" #include "packager/app/playready_key_encryption_flags.h"
#include "packager/app/protection_system_flags.h"
#include "packager/app/raw_key_encryption_flags.h" #include "packager/app/raw_key_encryption_flags.h"
#include "packager/app/stream_descriptor.h" #include "packager/app/stream_descriptor.h"
#include "packager/app/vlog_flags.h" #include "packager/app/vlog_flags.h"
@ -279,6 +280,9 @@ base::Optional<PackagingParams> GetPackagingParams() {
int num_key_providers = 0; int num_key_providers = 0;
EncryptionParams& encryption_params = packaging_params.encryption_params; EncryptionParams& encryption_params = packaging_params.encryption_params;
encryption_params.generate_common_pssh = FLAGS_generate_common_pssh;
encryption_params.generate_playready_pssh = FLAGS_generate_playready_pssh;
encryption_params.generate_widevine_pssh = FLAGS_generate_widevine_pssh;
if (FLAGS_enable_widevine_encryption) { if (FLAGS_enable_widevine_encryption) {
encryption_params.key_provider = KeyProvider::kWidevine; encryption_params.key_provider = KeyProvider::kWidevine;
++num_key_providers; ++num_key_providers;
@ -313,7 +317,6 @@ base::Optional<PackagingParams> GetPackagingParams() {
case KeyProvider::kWidevine: { case KeyProvider::kWidevine: {
WidevineEncryptionParams& widevine = encryption_params.widevine; WidevineEncryptionParams& widevine = encryption_params.widevine;
widevine.key_server_url = FLAGS_key_server_url; widevine.key_server_url = FLAGS_key_server_url;
widevine.include_common_pssh = FLAGS_include_common_pssh;
widevine.content_id = FLAGS_content_id_bytes; widevine.content_id = FLAGS_content_id_bytes;
widevine.policy = FLAGS_policy; widevine.policy = FLAGS_policy;

View File

@ -49,6 +49,14 @@ std::unique_ptr<RequestSigner> CreateSigner(const WidevineSigner& signer) {
std::unique_ptr<KeySource> CreateEncryptionKeySource( std::unique_ptr<KeySource> CreateEncryptionKeySource(
FourCC protection_scheme, FourCC protection_scheme,
const EncryptionParams& encryption_params) { const EncryptionParams& encryption_params) {
int protection_systems_flags(0);
if (encryption_params.generate_common_pssh)
protection_systems_flags |= COMMON_PROTECTION_SYSTEM_FLAG;
if (encryption_params.generate_playready_pssh)
protection_systems_flags |= PLAYREADY_PROTECTION_SYSTEM_FLAG;
if (encryption_params.generate_widevine_pssh)
protection_systems_flags |= WIDEVINE_PROTECTION_SYSTEM_FLAG;
std::unique_ptr<KeySource> encryption_key_source; std::unique_ptr<KeySource> encryption_key_source;
switch (encryption_params.key_provider) { switch (encryption_params.key_provider) {
case KeyProvider::kWidevine: { case KeyProvider::kWidevine: {
@ -63,7 +71,7 @@ std::unique_ptr<KeySource> CreateEncryptionKeySource(
} }
std::unique_ptr<WidevineKeySource> widevine_key_source( std::unique_ptr<WidevineKeySource> widevine_key_source(
new WidevineKeySource(widevine.key_server_url, new WidevineKeySource(widevine.key_server_url,
widevine.include_common_pssh)); protection_systems_flags));
widevine_key_source->set_protection_scheme(protection_scheme); widevine_key_source->set_protection_scheme(protection_scheme);
if (!widevine.signer.signer_name.empty()) { if (!widevine.signer.signer_name.empty()) {
std::unique_ptr<RequestSigner> request_signer( std::unique_ptr<RequestSigner> request_signer(
@ -85,10 +93,12 @@ std::unique_ptr<KeySource> CreateEncryptionKeySource(
break; break;
} }
case KeyProvider::kRawKey: { case KeyProvider::kRawKey: {
// TODO(hmchen): add multiple DRM support for raw key source.
encryption_key_source = RawKeySource::Create(encryption_params.raw_key); encryption_key_source = RawKeySource::Create(encryption_params.raw_key);
break; break;
} }
case KeyProvider::kPlayready: { case KeyProvider::kPlayready: {
// TODO(hmchen): add multiple DRM support for playready key source.
const PlayreadyEncryptionParams& playready = encryption_params.playready; const PlayreadyEncryptionParams& playready = encryption_params.playready;
if (!playready.key_id.empty() || !playready.key.empty()) { if (!playready.key_id.empty() || !playready.key.empty()) {
if (playready.key_id.empty() || playready.key.empty()) { if (playready.key_id.empty() || playready.key.empty()) {
@ -151,9 +161,9 @@ std::unique_ptr<KeySource> CreateDecryptionKeySource(
LOG(ERROR) << "'key_server_url' should not be empty."; LOG(ERROR) << "'key_server_url' should not be empty.";
return std::unique_ptr<KeySource>(); return std::unique_ptr<KeySource>();
} }
std::unique_ptr<WidevineKeySource> widevine_key_source( std::unique_ptr<WidevineKeySource> widevine_key_source(new WidevineKeySource(
new WidevineKeySource(widevine.key_server_url, widevine.key_server_url,
true /* commmon pssh, does not matter here */)); WIDEVINE_PROTECTION_SYSTEM_FLAG /* value does not matter here */));
if (!widevine.signer.signer_name.empty()) { if (!widevine.signer.signer_name.empty()) {
std::unique_ptr<RequestSigner> request_signer( std::unique_ptr<RequestSigner> request_signer(
CreateSigner(widevine.signer)); CreateSigner(widevine.signer));

View File

@ -0,0 +1,26 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
//
// Defines command line flags for protection systems.
#include "packager/app/protection_system_flags.h"
DEFINE_bool(generate_common_pssh,
false,
"When specified, generate an additional v1 PSSH box for the common "
"system ID. See: https://goo.gl/s8RIhr."
"The flag is default to be true if --enable_raw_key_encryption "
"is set and no other pssh flags are specified.");
DEFINE_bool(generate_playready_pssh,
false,
"When specified, include a Playready PSSH box."
"A playready PSSH is always generated regardless of the value of "
"--generate_playready_pssh for --enable_playready_encryption.");
DEFINE_bool(generate_widevine_pssh,
false,
"When specified, include a Widevine PSSH box. "
"A widevine PSSH is always generated regardless of the value of "
"--generate_widevine_pssh for --enable_widevine_encryption.");

View File

@ -0,0 +1,18 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
//
// Defines command line flags for protection systems.
#ifndef PACKAGER_APP_PROTECTION_SYSTEM_FLAGS_H_
#define PACKAGER_APP_PROTECTION_SYSTEM_FLAGS_H_
#include <gflags/gflags.h>
DECLARE_bool(generate_common_pssh);
DECLARE_bool(generate_playready_pssh);
DECLARE_bool(generate_widevine_pssh);
#endif // PACKAGER_APP_PROTECTION_SYSTEM_FLAGS_H_

View File

@ -23,11 +23,6 @@ DEFINE_bool(enable_widevine_decryption,
"Enable decryption with Widevine license server/proxy. User should " "Enable decryption with Widevine license server/proxy. User should "
"provide either AES signing key (--aes_signing_key, " "provide either AES signing key (--aes_signing_key, "
"--aes_signing_iv) or RSA signing key (--rsa_signing_key_path)."); "--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: "
"https://goo.gl/s8RIhr");
DEFINE_string(key_server_url, "", "Key server url. Required for encryption and " DEFINE_string(key_server_url, "", "Key server url. Required for encryption and "
"decryption"); "decryption");
DEFINE_hex_bytes(content_id, "", "Content Id (hex)."); DEFINE_hex_bytes(content_id, "", "Content Id (hex).");
@ -117,11 +112,6 @@ bool ValidateWidevineCryptoFlags() {
widevine_encryption_label)) { widevine_encryption_label)) {
success = false; 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) { if (FLAGS_max_sd_pixels <= 0) {
PrintError("--max_sd_pixels must be positive."); PrintError("--max_sd_pixels must be positive.");

View File

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

View File

@ -17,9 +17,11 @@
#include "packager/base/strings/stringprintf.h" #include "packager/base/strings/stringprintf.h"
#include "packager/hls/base/media_playlist.h" #include "packager/hls/base/media_playlist.h"
#include "packager/media/base/protection_system_specific_info.h" #include "packager/media/base/protection_system_specific_info.h"
#include "packager/media/base/raw_key_pssh_generator.h"
#include "packager/media/base/raw_key_source.h" #include "packager/media/base/raw_key_source.h"
#include "packager/media/base/widevine_key_source.h" #include "packager/media/base/widevine_key_source.h"
#include "packager/media/base/widevine_pssh_data.pb.h" #include "packager/media/base/widevine_pssh_data.pb.h"
#include "packager/media/base/widevine_pssh_generator.h"
namespace shaka { namespace shaka {

View File

@ -14,9 +14,11 @@
#include "packager/hls/base/mock_media_playlist.h" #include "packager/hls/base/mock_media_playlist.h"
#include "packager/hls/base/simple_hls_notifier.h" #include "packager/hls/base/simple_hls_notifier.h"
#include "packager/media/base/protection_system_specific_info.h" #include "packager/media/base/protection_system_specific_info.h"
#include "packager/media/base/raw_key_pssh_generator.h"
#include "packager/media/base/raw_key_source.h" #include "packager/media/base/raw_key_source.h"
#include "packager/media/base/widevine_key_source.h" #include "packager/media/base/widevine_key_source.h"
#include "packager/media/base/widevine_pssh_data.pb.h" #include "packager/media/base/widevine_pssh_data.pb.h"
#include "packager/media/base/widevine_pssh_generator.h"
namespace shaka { namespace shaka {
namespace hls { namespace hls {

View File

@ -7,6 +7,9 @@
#include "packager/media/base/key_source.h" #include "packager/media/base/key_source.h"
#include "packager/base/logging.h" #include "packager/base/logging.h"
#include "packager/media/base/playready_pssh_generator.h"
#include "packager/media/base/raw_key_pssh_generator.h"
#include "packager/media/base/widevine_pssh_generator.h"
namespace shaka { namespace shaka {
namespace media { namespace media {
@ -15,9 +18,54 @@ EncryptionKey::EncryptionKey() {}
EncryptionKey::~EncryptionKey() {} EncryptionKey::~EncryptionKey() {}
KeySource::KeySource() {} KeySource::KeySource(int protection_systems_flags) {
if (protection_systems_flags & COMMON_PROTECTION_SYSTEM_FLAG) {
pssh_generators_.emplace_back(new RawKeyPsshGenerator());
}
if (protection_systems_flags & PLAYREADY_PROTECTION_SYSTEM_FLAG) {
pssh_generators_.emplace_back(new PlayReadyPsshGenerator());
}
if (protection_systems_flags & WIDEVINE_PROTECTION_SYSTEM_FLAG) {
pssh_generators_.emplace_back(new WidevinePsshGenerator());
}
}
KeySource::~KeySource() {} KeySource::~KeySource() {}
Status KeySource::UpdateProtectionSystemInfo(
EncryptionKeyMap* encryption_key_map) {
for (const auto& pssh_generator : pssh_generators_) {
const bool support_multiple_keys = pssh_generator->SupportMultipleKeys();
if (support_multiple_keys) {
ProtectionSystemSpecificInfo info;
std::vector<std::vector<uint8_t>> key_ids;
for (const EncryptionKeyMap::value_type& pair : *encryption_key_map) {
key_ids.push_back(pair.second->key_id);
}
Status status = pssh_generator->GeneratePsshFromKeyIds(key_ids, &info);
if (!status.ok()) {
return status;
}
for (const EncryptionKeyMap::value_type& pair : *encryption_key_map) {
pair.second->key_system_info.push_back(info);
}
} else {
for (const EncryptionKeyMap::value_type& pair : *encryption_key_map) {
ProtectionSystemSpecificInfo info;
Status status = pssh_generator->GeneratePsshFromKeyIdAndKey(
pair.second->key_id, pair.second->key, &info);
if (!status.ok()) {
return status;
}
pair.second->key_system_info.push_back(info);
}
}
}
return Status::OK;
}
} // namespace media } // namespace media
} // namespace shaka } // namespace shaka

View File

@ -7,10 +7,13 @@
#ifndef PACKAGER_MEDIA_BASE_KEY_SOURCE_H_ #ifndef PACKAGER_MEDIA_BASE_KEY_SOURCE_H_
#define PACKAGER_MEDIA_BASE_KEY_SOURCE_H_ #define PACKAGER_MEDIA_BASE_KEY_SOURCE_H_
#include <map>
#include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include "packager/media/base/protection_system_specific_info.h" #include "packager/media/base/protection_system_specific_info.h"
#include "packager/media/base/pssh_generator.h"
#include "packager/status.h" #include "packager/status.h"
namespace shaka { namespace shaka {
@ -41,10 +44,13 @@ struct EncryptionKey {
std::vector<uint8_t> iv; std::vector<uint8_t> iv;
}; };
typedef std::map<std::string, std::unique_ptr<EncryptionKey>> EncryptionKeyMap;
/// KeySource is responsible for encryption key acquisition. /// KeySource is responsible for encryption key acquisition.
class KeySource { class KeySource {
public: public:
KeySource(); explicit KeySource(int protection_systems_flags);
virtual ~KeySource(); virtual ~KeySource();
/// Fetch keys based on the specified encrypted media init data. /// Fetch keys based on the specified encrypted media init data.
@ -81,7 +87,14 @@ class KeySource {
const std::string& stream_label, const std::string& stream_label,
EncryptionKey* key) = 0; EncryptionKey* key) = 0;
protected:
/// Update the protection sysmtem specific info for the encryption keys.
/// @param encryption_key_map is a map of encryption keys for all tracks.
Status UpdateProtectionSystemInfo(EncryptionKeyMap* encryption_key_map);
private: private:
std::vector<std::unique_ptr<PsshGenerator>> pssh_generators_;
DISALLOW_COPY_AND_ASSIGN(KeySource); DISALLOW_COPY_AND_ASSIGN(KeySource);
}; };

View File

@ -72,10 +72,18 @@
'offset_byte_queue.h', 'offset_byte_queue.h',
'playready_key_source.cc', 'playready_key_source.cc',
'playready_key_source.h', 'playready_key_source.h',
'playready_pssh_generator.cc',
'playready_pssh_generator.h',
'producer_consumer_queue.h', 'producer_consumer_queue.h',
'protection_system_specific_info.cc', 'protection_system_specific_info.cc',
'protection_system_specific_info.h', 'protection_system_specific_info.h',
'pssh_generator.cc',
'pssh_generator.h',
'pssh_generator_util.cc',
'pssh_generator_util.h',
'range.h', 'range.h',
'raw_key_pssh_generator.cc',
'raw_key_pssh_generator.h',
'raw_key_source.cc', 'raw_key_source.cc',
'raw_key_source.h', 'raw_key_source.h',
'rcheck.h', 'rcheck.h',
@ -97,6 +105,8 @@
'video_stream_info.h', 'video_stream_info.h',
'widevine_key_source.cc', 'widevine_key_source.cc',
'widevine_key_source.h', 'widevine_key_source.h',
'widevine_pssh_generator.cc',
'widevine_pssh_generator.h'
], ],
'dependencies': [ 'dependencies': [
'widevine_pssh_data_proto', 'widevine_pssh_data_proto',
@ -150,6 +160,7 @@
'offset_byte_queue_unittest.cc', 'offset_byte_queue_unittest.cc',
'producer_consumer_queue_unittest.cc', 'producer_consumer_queue_unittest.cc',
'protection_system_specific_info_unittest.cc', 'protection_system_specific_info_unittest.cc',
'pssh_generator_unittest.cc',
'raw_key_source_unittest.cc', 'raw_key_source_unittest.cc',
'rsa_key_unittest.cc', 'rsa_key_unittest.cc',
'status_test_util_unittest.cc', 'status_test_util_unittest.cc',

View File

@ -6,7 +6,6 @@
#include "packager/media/base/playready_key_source.h" #include "packager/media/base/playready_key_source.h"
#include <openssl/aes.h>
#include <algorithm> #include <algorithm>
#include "packager/base/base64.h" #include "packager/base/base64.h"
#include "packager/base/logging.h" #include "packager/base/logging.h"
@ -14,6 +13,8 @@
#include "packager/base/strings/string_util.h" #include "packager/base/strings/string_util.h"
#include "packager/media/base/buffer_writer.h" #include "packager/media/base/buffer_writer.h"
#include "packager/media/base/http_key_fetcher.h" #include "packager/media/base/http_key_fetcher.h"
#include "packager/media/base/key_source.h"
#include "packager/media/base/playready_pssh_generator.h"
namespace shaka { namespace shaka {
namespace media { namespace media {
@ -21,16 +22,6 @@ namespace media {
namespace { namespace {
const uint32_t kHttpFetchTimeout = 60; // In seconds const uint32_t kHttpFetchTimeout = 60; // In seconds
const std::string kPlayHeaderObject_4_1 = "<WRMHEADER "
"xmlns=\"http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader\" "
"version=\"4.1.0.0\"><DATA><PROTECTINFO>"
"<KID VALUE=\"$0\" ALGID=\"AESCTR\" CHECKSUM=\"$1\"></KID></PROTECTINFO>"
"</DATA></WRMHEADER>";
const std::string kPlayHeaderObject_4_0 = "<WRMHEADER "
"xmlns=\"http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader\" "
"version=\"4.0.0.0\"><DATA><PROTECTINFO><KEYLEN>16</KEYLEN>"
"<ALGID>AESCTR</ALGID></PROTECTINFO><KID>$0</KID><CHECKSUM>$1</CHECKSUM>"
"</DATA></WRMHEADER>";
const std::string kAcquireLicenseRequest = const std::string kAcquireLicenseRequest =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<soap:Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" " "<soap:Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" "
@ -67,117 +58,29 @@ bool Base64StringToBytes(const std::string& base64_string,
bytes->assign(str.begin(), str.end()); bytes->assign(str.begin(), str.end());
return true; return true;
} }
// Converts the key_id's endianness.
std::vector<uint8_t> ConvertGuidEndianness(const std::vector<uint8_t>& input) {
std::vector<uint8_t> output = input;
if (output.size() > 7) { // Defensive check.
output[0] = input[3];
output[1] = input[2];
output[2] = input[1];
output[3] = input[0];
output[4] = input[5];
output[5] = input[4];
output[6] = input[7];
output[7] = input[6];
// 8-15 are an array of bytes with no endianness.
}
return output;
} }
// Generates the data section of a PlayReady PSSH. PlayReadyKeySource::PlayReadyKeySource(const std::string& server_url)
// PlayReady PSSH Data is a PlayReady Header Object. : KeySource(PLAYREADY_PROTECTION_SYSTEM_FLAG),
// Format is outlined in the following document. encryption_key_(new EncryptionKey),
// http://download.microsoft.com/download/2/3/8/238F67D9-1B8B-48D3-AB83-9C00112268B2/PlayReady%20Header%20Object%202015-08-13-FINAL-CL.PDF server_url_(server_url) {}
Status GeneratePlayReadyPsshData(const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key,
std::vector<uint8_t>* output) {
CHECK(output);
std::vector<uint8_t> key_id_converted = ConvertGuidEndianness(key_id);
std::vector<uint8_t> encrypted_key_id(key_id_converted.size());
std::unique_ptr<AES_KEY> aes_key (new AES_KEY);
CHECK_EQ(AES_set_encrypt_key(key.data(), key.size() * 8, aes_key.get()), 0);
AES_ecb_encrypt(key_id_converted.data(), encrypted_key_id.data(),
aes_key.get(), AES_ENCRYPT);
std::string checksum = std::string(encrypted_key_id.begin(),
encrypted_key_id.end()).substr(0, 8);
std::string base64_checksum;
base::Base64Encode(checksum, &base64_checksum);
std::string base64_key_id;
base::Base64Encode(std::string(key_id_converted.begin(),
key_id_converted.end()),
&base64_key_id);
std::string playready_header = kPlayHeaderObject_4_0;
base::ReplaceFirstSubstringAfterOffset(
&playready_header, 0, "$0", base64_key_id);
base::ReplaceFirstSubstringAfterOffset(
&playready_header, 0, "$1", base64_checksum);
// Create a PlayReady Record.
// Outline in section '2.PlayReady Records' of
// 'PlayReady Header Object' document. Note data is in little endian format.
std::vector<uint16_t> record_value =
std::vector<uint16_t>(playready_header.begin(), playready_header.end());
BufferWriter writer_pr_record;
uint16_t record_type = 1; // Indicates that the record contains a rights management header.
uint16_t record_length = record_value.size() * 2;
writer_pr_record.AppendInt(static_cast<uint8_t>(record_type & 0xff));
writer_pr_record.AppendInt(static_cast<uint8_t>((record_type >> 8) & 0xff));
writer_pr_record.AppendInt(static_cast<uint8_t>(record_length & 0xff));
writer_pr_record.AppendInt(static_cast<uint8_t>((record_length >> 8) & 0xff));
for (auto record_item: record_value) {
writer_pr_record.AppendInt(static_cast<uint8_t>(record_item & 0xff));
writer_pr_record.AppendInt(static_cast<uint8_t>((record_item >> 8) & 0xff));
}
// Create the PlayReady Header object.
// Outline in section '1.PlayReady Header Objects' of
// 'PlayReady Header Object' document. Note data is in little endian format.
BufferWriter writer_pr_header_object;
uint32_t playready_header_length = writer_pr_record.Size() + 4 + 2;
uint16_t record_count = 1;
writer_pr_header_object.AppendInt(
static_cast<uint8_t>(playready_header_length & 0xff));
writer_pr_header_object.AppendInt(
static_cast<uint8_t>((playready_header_length >> 8) & 0xff));
writer_pr_header_object.AppendInt(
static_cast<uint8_t>((playready_header_length >> 16) & 0xff));
writer_pr_header_object.AppendInt(
static_cast<uint8_t>((playready_header_length >> 24) & 0xff));
writer_pr_header_object.AppendInt(
static_cast<uint8_t>(record_count & 0xff));
writer_pr_header_object.AppendInt(
static_cast<uint8_t>((record_count >> 8) & 0xff));
writer_pr_header_object.AppendBuffer(writer_pr_record);
*output = std::vector<uint8_t>(writer_pr_header_object.Buffer(),
writer_pr_header_object.Buffer() +
writer_pr_header_object.Size());
return Status::OK;
}
} // namespace
PlayReadyKeySource::PlayReadyKeySource( PlayReadyKeySource::PlayReadyKeySource(
const std::string& server_url) const std::string& server_url,
: encryption_key_(new EncryptionKey),
server_url_(server_url) {
}
PlayReadyKeySource::PlayReadyKeySource(const std::string& server_url,
const std::string& client_cert_file, const std::string& client_cert_file,
const std::string& client_cert_private_key_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)
: encryption_key_(new EncryptionKey), : KeySource(PLAYREADY_PROTECTION_SYSTEM_FLAG),
encryption_key_(new EncryptionKey),
server_url_(server_url), server_url_(server_url),
client_cert_file_(client_cert_file), client_cert_file_(client_cert_file),
client_cert_private_key_file_(client_cert_private_key_file), client_cert_private_key_file_(client_cert_private_key_file),
client_cert_private_key_password_(client_cert_private_key_password) { client_cert_private_key_password_(client_cert_private_key_password) {}
}
PlayReadyKeySource::PlayReadyKeySource( PlayReadyKeySource::PlayReadyKeySource(
std::unique_ptr<EncryptionKey> encryption_key) std::unique_ptr<EncryptionKey> encryption_key)
: encryption_key_(std::move(encryption_key)) { : KeySource(PLAYREADY_PROTECTION_SYSTEM_FLAG),
} encryption_key_(std::move(encryption_key)) {}
PlayReadyKeySource::~PlayReadyKeySource() {} PlayReadyKeySource::~PlayReadyKeySource() {}
@ -187,16 +90,17 @@ std::unique_ptr<PlayReadyKeySource> PlayReadyKeySource::CreateFromKeyAndKeyId(
std::unique_ptr<EncryptionKey> encryption_key(new EncryptionKey); std::unique_ptr<EncryptionKey> encryption_key(new EncryptionKey);
encryption_key->key_id = key_id; encryption_key->key_id = key_id;
encryption_key->key = key; encryption_key->key = key;
std::vector<uint8_t> pssh_data;
Status status = GeneratePlayReadyPsshData(
encryption_key->key_id, encryption_key->key, &pssh_data);
if (!status.ok()) {
LOG(ERROR) << status.ToString();
return std::unique_ptr<PlayReadyKeySource>();
}
ProtectionSystemSpecificInfo info; ProtectionSystemSpecificInfo info;
info.add_key_id(encryption_key->key_id); info.add_key_id(key_id);
info.set_system_id(kPlayReadySystemId, arraysize(kPlayReadySystemId)); 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); info.set_pssh_data(pssh_data);
encryption_key->key_system_info.push_back(info); encryption_key->key_system_info.push_back(info);

View File

@ -16,10 +16,6 @@
namespace shaka { namespace shaka {
namespace media { namespace media {
// SystemID defined for PlayReady Drm.
const uint8_t kPlayReadySystemId[] = {0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42,
0x86, 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95};
/// A key source that uses playready for encryption. /// A key source that uses playready for encryption.
class PlayReadyKeySource : public KeySource { class PlayReadyKeySource : public KeySource {
public: public:

View File

@ -0,0 +1,185 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "packager/media/base/playready_pssh_generator.h"
#include <openssl/aes.h>
#include <algorithm>
#include <memory>
#include <set>
#include <vector>
#include "packager/base/base64.h"
#include "packager/base/logging.h"
#include "packager/base/strings/string_number_conversions.h"
#include "packager/base/strings/string_util.h"
#include "packager/media/base/buffer_writer.h"
#include "packager/media/base/http_key_fetcher.h"
#include "packager/media/base/key_source.h"
#include "packager/media/base/playready_key_source.h"
namespace shaka {
namespace media {
namespace {
const std::string kPlayHeaderObject_4_1 =
"<WRMHEADER "
"xmlns=\"http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader\" "
"version=\"4.1.0.0\"><DATA><PROTECTINFO>"
"<KID VALUE=\"$0\" ALGID=\"AESCTR\" CHECKSUM=\"$1\"></KID></PROTECTINFO>"
"</DATA></WRMHEADER>";
const std::string kPlayHeaderObject_4_0 =
"<WRMHEADER "
"xmlns=\"http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader\" "
"version=\"4.0.0.0\"><DATA><PROTECTINFO><KEYLEN>16</KEYLEN>"
"<ALGID>AESCTR</ALGID></PROTECTINFO><KID>$0</KID><CHECKSUM>$1</CHECKSUM>"
"</DATA></WRMHEADER>";
// Converts the key_id's endianness.
std::vector<uint8_t> ConvertGuidEndianness(const std::vector<uint8_t>& input) {
std::vector<uint8_t> output = input;
if (output.size() > 7) { // Defensive check.
output[0] = input[3];
output[1] = input[2];
output[2] = input[1];
output[3] = input[0];
output[4] = input[5];
output[5] = input[4];
output[6] = input[7];
output[7] = input[6];
// 8-15 are an array of bytes with no endianness.
}
return output;
}
// Generates the data section of a PlayReady PSSH.
// PlayReady PSSH Data is a PlayReady Header Object.
// Format is outlined in the following document.
// http://download.microsoft.com/download/2/3/8/238F67D9-1B8B-48D3-AB83-9C00112268B2/PlayReady%20Header%20Object%202015-08-13-FINAL-CL.PDF
Status GeneratePlayReadyPsshData(const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key,
std::vector<uint8_t>* output) {
CHECK(output);
std::vector<uint8_t> key_id_converted = ConvertGuidEndianness(key_id);
std::vector<uint8_t> encrypted_key_id(key_id_converted.size());
std::unique_ptr<AES_KEY> aes_key(new AES_KEY);
CHECK_EQ(AES_set_encrypt_key(key.data(), key.size() * 8, aes_key.get()), 0);
AES_ecb_encrypt(key_id_converted.data(), encrypted_key_id.data(),
aes_key.get(), AES_ENCRYPT);
std::string checksum =
std::string(encrypted_key_id.begin(), encrypted_key_id.end())
.substr(0, 8);
std::string base64_checksum;
base::Base64Encode(checksum, &base64_checksum);
std::string base64_key_id;
base::Base64Encode(
std::string(key_id_converted.begin(), key_id_converted.end()),
&base64_key_id);
std::string playready_header = kPlayHeaderObject_4_0;
base::ReplaceFirstSubstringAfterOffset(&playready_header, 0, "$0",
base64_key_id);
base::ReplaceFirstSubstringAfterOffset(&playready_header, 0, "$1",
base64_checksum);
// Create a PlayReady Record.
// Outline in section '2.PlayReady Records' of
// 'PlayReady Header Object' document. Note data is in little endian format.
std::vector<uint16_t> record_value =
std::vector<uint16_t>(playready_header.begin(), playready_header.end());
shaka::media::BufferWriter writer_pr_record;
uint16_t record_type =
1; // Indicates that the record contains a rights management header.
uint16_t record_length = record_value.size() * 2;
writer_pr_record.AppendInt(static_cast<uint8_t>(record_type & 0xff));
writer_pr_record.AppendInt(static_cast<uint8_t>((record_type >> 8) & 0xff));
writer_pr_record.AppendInt(static_cast<uint8_t>(record_length & 0xff));
writer_pr_record.AppendInt(static_cast<uint8_t>((record_length >> 8) & 0xff));
for (auto record_item : record_value) {
writer_pr_record.AppendInt(static_cast<uint8_t>(record_item & 0xff));
writer_pr_record.AppendInt(static_cast<uint8_t>((record_item >> 8) & 0xff));
}
// Create the PlayReady Header object.
// Outline in section '1.PlayReady Header Objects' of
// 'PlayReady Header Object' document. Note data is in little endian format.
shaka::media::BufferWriter writer_pr_header_object;
uint32_t playready_header_length = writer_pr_record.Size() + 4 + 2;
uint16_t record_count = 1;
writer_pr_header_object.AppendInt(
static_cast<uint8_t>(playready_header_length & 0xff));
writer_pr_header_object.AppendInt(
static_cast<uint8_t>((playready_header_length >> 8) & 0xff));
writer_pr_header_object.AppendInt(
static_cast<uint8_t>((playready_header_length >> 16) & 0xff));
writer_pr_header_object.AppendInt(
static_cast<uint8_t>((playready_header_length >> 24) & 0xff));
writer_pr_header_object.AppendInt(static_cast<uint8_t>(record_count & 0xff));
writer_pr_header_object.AppendInt(
static_cast<uint8_t>((record_count >> 8) & 0xff));
writer_pr_header_object.AppendBuffer(writer_pr_record);
*output = std::vector<uint8_t>(
writer_pr_header_object.Buffer(),
writer_pr_header_object.Buffer() + writer_pr_header_object.Size());
return Status::OK;
}
} // namespace
PlayReadyPsshGenerator::PlayReadyPsshGenerator()
: system_id_(std::begin(kPlayReadySystemId), std::end(kPlayReadySystemId)) {
}
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;
}
uint8_t PlayReadyPsshGenerator::PsshBoxVersion() const {
return 1;
}
const std::vector<uint8_t>& PlayReadyPsshGenerator::SystemId() const {
return system_id_;
}
base::Optional<std::vector<uint8_t>>
PlayReadyPsshGenerator::GeneratePsshDataFromKeyIdAndKey(
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key) const {
std::vector<uint8_t> pssh_data;
Status status = GeneratePlayReadyPsshData(key_id, key, &pssh_data);
if (!status.ok()) {
LOG(ERROR) << status.ToString();
return base::nullopt;
}
return pssh_data;
}
base::Optional<std::vector<uint8_t>>
PlayReadyPsshGenerator::GeneratePsshDataFromKeyIds(
const std::vector<std::vector<uint8_t>>& key_ids) const {
NOTIMPLEMENTED();
return base::nullopt;
}
} // namespace media
} // namespace shaka

View File

@ -0,0 +1,61 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#ifndef MEDIA_BASE_PLAYREADY_PSSH_GENERATOR_H_
#define MEDIA_BASE_PLAYREADY_PSSH_GENERATOR_H_
#include <vector>
#include "packager/media/base/pssh_generator.h"
#include "testing/gtest/include/gtest/gtest_prod.h"
namespace shaka {
namespace media {
// SystemID defined for PlayReady Drm.
const uint8_t kPlayReadySystemId[] = {0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40,
0x42, 0x86, 0xab, 0x92, 0xe6, 0x5b,
0xe0, 0x88, 0x5f, 0x95};
class PlayReadyPsshGenerator : public PsshGenerator {
public:
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;
/// @}
private:
PlayReadyPsshGenerator& operator=(const PlayReadyPsshGenerator&) = delete;
PlayReadyPsshGenerator(const PlayReadyPsshGenerator&) = delete;
// PsshGenerator implemetation overrides.
uint8_t PsshBoxVersion() const override;
const std::vector<uint8_t>& SystemId() const override;
base::Optional<std::vector<uint8_t>> GeneratePsshDataFromKeyIds(
const std::vector<std::vector<uint8_t>>& key_ids) const override;
base::Optional<std::vector<uint8_t>> GeneratePsshDataFromKeyIdAndKey(
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key) const override;
std::vector<uint8_t> system_id_;
FRIEND_TEST(PsshGeneratorTest, GeneratePlayReadyPsshDataFromKeyIdAndKey);
};
} // namespace media
} // namespace shaka
#endif // MEDIA_BASE_PLAYREADY_PSSH_GENERATOR_H_

View File

@ -4,8 +4,8 @@
// license that can be found in the LICENSE file or at // license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd // https://developers.google.com/open-source/licenses/bsd
#ifndef PACKAGER_MEDIA_BASE_PSSH_H_ #ifndef PACKAGER_MEDIA_BASE_PROTECTION_SYSTEM_SPECIFIC_INFO_H_
#define PACKAGER_MEDIA_BASE_PSSH_H_ #define PACKAGER_MEDIA_BASE_PROTECTION_SYSTEM_SPECIFIC_INFO_H_
#include <stdint.h> #include <stdint.h>
#include <vector> #include <vector>
@ -13,6 +13,10 @@
#include "packager/base/logging.h" #include "packager/base/logging.h"
#include "packager/media/base/buffer_reader.h" #include "packager/media/base/buffer_reader.h"
#define COMMON_PROTECTION_SYSTEM_FLAG 0x01
#define PLAYREADY_PROTECTION_SYSTEM_FLAG 0x02
#define WIDEVINE_PROTECTION_SYSTEM_FLAG 0x04
namespace shaka { namespace shaka {
namespace media { namespace media {
@ -70,4 +74,4 @@ class ProtectionSystemSpecificInfo {
} // namespace media } // namespace media
} // namespace shaka } // namespace shaka
#endif // PACKAGER_MEDIA_BASE_PSSH_H_ #endif // PACKAGER_MEDIA_BASE_PROTECTION_SYSTEM_SPECIFIC_INFO_H_

View File

@ -0,0 +1,61 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "packager/media/base/pssh_generator.h"
namespace shaka {
namespace media {
PsshGenerator::PsshGenerator() {}
PsshGenerator::~PsshGenerator() {}
Status PsshGenerator::GeneratePsshFromKeyIds(
const std::vector<std::vector<uint8_t>>& key_ids,
ProtectionSystemSpecificInfo* info) const {
base::Optional<std::vector<uint8_t>> pssh_data =
GeneratePsshDataFromKeyIds(key_ids);
if (!pssh_data) {
return Status(error::ENCRYPTION_FAILURE,
"Fail to generate PSSH data from multiple Key IDs.");
}
info->set_pssh_data(pssh_data.value());
info->clear_key_ids();
for (const auto& key_id : key_ids) {
info->add_key_id(key_id);
}
info->set_pssh_box_version(PsshBoxVersion());
const std::vector<uint8_t>& system_id = SystemId();
info->set_system_id(system_id.data(), system_id.size());
return Status::OK;
}
Status PsshGenerator::GeneratePsshFromKeyIdAndKey(
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key,
ProtectionSystemSpecificInfo* info) const {
base::Optional<std::vector<uint8_t>> pssh_data =
GeneratePsshDataFromKeyIdAndKey(key_id, key);
if (!pssh_data) {
return Status(error::ENCRYPTION_FAILURE,
"Fail to generate PSSH data from Key ID and Key.");
}
info->set_pssh_data(pssh_data.value());
info->clear_key_ids();
info->add_key_id(key_id);
info->set_pssh_box_version(PsshBoxVersion());
const std::vector<uint8_t>& system_id = SystemId();
info->set_system_id(system_id.data(), system_id.size());
return Status::OK;
}
} // namespace media
} // namespace shaka

View File

@ -0,0 +1,73 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#ifndef PACKAGER_MEDIA_BASE_PSSH_GENERATOR_H_
#define PACKAGER_MEDIA_BASE_PSSH_GENERATOR_H_
#include <vector>
#include "packager/base/optional.h"
#include "packager/media/base/protection_system_specific_info.h"
#include "packager/status.h"
// TODO(hmchen): move pssh related files into a sperate folder.
namespace shaka {
namespace media {
struct EncryptionKey;
class PsshGenerator {
public:
PsshGenerator();
virtual ~PsshGenerator() = 0;
/// @return whether the PSSH generates the PSSH box based on multiple key
/// IDs.
virtual bool SupportMultipleKeys() = 0;
/// Generate the PSSH and set the ProtectionSystemSpecificInfo.
/// @param key_ids is a vector of key IDs for all tracks.
/// @param info is a pointer to the ProtectionSystemSpecificInfo for setting
/// the PSSH box.
Status GeneratePsshFromKeyIds(
const std::vector<std::vector<uint8_t>>& key_ids,
ProtectionSystemSpecificInfo* info) const;
/// Generate the PSSH and set the ProtectionSystemSpecificInfo.
/// @param key_id is a the unique identifier for the key.
/// @param key is the content key.
/// @param info is a pointer to the ProtectionSystemSpecificInfo for setting
/// the PSSH box.
Status GeneratePsshFromKeyIdAndKey(const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key,
ProtectionSystemSpecificInfo* info) const;
private:
/// Return the PSSH data generated from multiple |key_ids| on success. If
/// error happens, it returns no value.
/// @param key_ids is a set of key IDs for all tracks.
virtual base::Optional<std::vector<uint8_t>> GeneratePsshDataFromKeyIds(
const std::vector<std::vector<uint8_t>>& key_ids) const = 0;
/// Return the PSSH data generated from a pair of |key_id| and |key| on
/// success. If error happens, it returns no value.
/// @param key_id is a the unique identifier for the key.
/// @param key is the key for generating the PSSH box.
virtual base::Optional<std::vector<uint8_t>> GeneratePsshDataFromKeyIdAndKey(
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key) const = 0;
/// Return the PSSH box version.
virtual uint8_t PsshBoxVersion() const = 0;
/// Return the System ID.
virtual const std::vector<uint8_t>& SystemId() const = 0;
};
} // namespace media
} // namespace shaka
#endif // PACKAGER_MEDIA_BASE_PSSH_GENERATOR_H_

View File

@ -0,0 +1,189 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include <gtest/gtest.h>
#include "packager/media/base/playready_pssh_generator.h"
#include "packager/media/base/raw_key_pssh_generator.h"
#include "packager/media/base/widevine_pssh_generator.h"
#include "packager/status_test_util.h"
namespace shaka {
namespace media {
namespace {
// Key ID and key should be 16 bytes.
const uint8_t kTestKeyId1[] = {'k', 'e', 'y', 'i', 'd', '1', '~', '~',
'~', '~', '~', '~', '~', '~', '~', '~'};
const uint8_t kTestKey1[] = {'c', 'o', 'n', 't', 'e', 'n', 't', 'k',
'e', 'y', '1', '~', '~', '~', '~', '~'};
const uint8_t kTestKeyId2[] = {'k', 'e', 'y', 'i', 'd', '2', '~', '~',
'~', '~', '~', '~', '~', '~', '~', '~'};
const char kExpectedPlayReadyPsshData[] = {
'\x6', '\x2', '\x0', '\x0', '\x1', '\x0', '\x1', '\x0', '\xfc', '\x1',
'<', '\x0', 'W', '\x0', 'R', '\x0', 'M', '\x0', 'H', '\x0',
'E', '\x0', 'A', '\x0', 'D', '\x0', 'E', '\x0', 'R', '\x0',
' ', '\x0', 'x', '\x0', 'm', '\x0', 'l', '\x0', 'n', '\x0',
's', '\x0', '=', '\x0', '"', '\x0', 'h', '\x0', 't', '\x0',
't', '\x0', 'p', '\x0', ':', '\x0', '/', '\x0', '/', '\x0',
's', '\x0', 'c', '\x0', 'h', '\x0', 'e', '\x0', 'm', '\x0',
'a', '\x0', 's', '\x0', '.', '\x0', 'm', '\x0', 'i', '\x0',
'c', '\x0', 'r', '\x0', 'o', '\x0', 's', '\x0', 'o', '\x0',
'f', '\x0', 't', '\x0', '.', '\x0', 'c', '\x0', 'o', '\x0',
'm', '\x0', '/', '\x0', 'D', '\x0', 'R', '\x0', 'M', '\x0',
'/', '\x0', '2', '\x0', '0', '\x0', '0', '\x0', '7', '\x0',
'/', '\x0', '0', '\x0', '3', '\x0', '/', '\x0', 'P', '\x0',
'l', '\x0', 'a', '\x0', 'y', '\x0', 'R', '\x0', 'e', '\x0',
'a', '\x0', 'd', '\x0', 'y', '\x0', 'H', '\x0', 'e', '\x0',
'a', '\x0', 'd', '\x0', 'e', '\x0', 'r', '\x0', '"', '\x0',
' ', '\x0', 'v', '\x0', 'e', '\x0', 'r', '\x0', 's', '\x0',
'i', '\x0', 'o', '\x0', 'n', '\x0', '=', '\x0', '"', '\x0',
'4', '\x0', '.', '\x0', '0', '\x0', '.', '\x0', '0', '\x0',
'.', '\x0', '0', '\x0', '"', '\x0', '>', '\x0', '<', '\x0',
'D', '\x0', 'A', '\x0', 'T', '\x0', 'A', '\x0', '>', '\x0',
'<', '\x0', 'P', '\x0', 'R', '\x0', 'O', '\x0', 'T', '\x0',
'E', '\x0', 'C', '\x0', 'T', '\x0', 'I', '\x0', 'N', '\x0',
'F', '\x0', 'O', '\x0', '>', '\x0', '<', '\x0', 'K', '\x0',
'E', '\x0', 'Y', '\x0', 'L', '\x0', 'E', '\x0', 'N', '\x0',
'>', '\x0', '1', '\x0', '6', '\x0', '<', '\x0', '/', '\x0',
'K', '\x0', 'E', '\x0', 'Y', '\x0', 'L', '\x0', 'E', '\x0',
'N', '\x0', '>', '\x0', '<', '\x0', 'A', '\x0', 'L', '\x0',
'G', '\x0', 'I', '\x0', 'D', '\x0', '>', '\x0', 'A', '\x0',
'E', '\x0', 'S', '\x0', 'C', '\x0', 'T', '\x0', 'R', '\x0',
'<', '\x0', '/', '\x0', 'A', '\x0', 'L', '\x0', 'G', '\x0',
'I', '\x0', 'D', '\x0', '>', '\x0', '<', '\x0', '/', '\x0',
'P', '\x0', 'R', '\x0', 'O', '\x0', 'T', '\x0', 'E', '\x0',
'C', '\x0', 'T', '\x0', 'I', '\x0', 'N', '\x0', 'F', '\x0',
'O', '\x0', '>', '\x0', '<', '\x0', 'K', '\x0', 'I', '\x0',
'D', '\x0', '>', '\x0', 'a', '\x0', 'X', '\x0', 'l', '\x0',
'l', '\x0', 'a', '\x0', 'z', '\x0', 'F', '\x0', 'k', '\x0',
'f', '\x0', 'n', '\x0', '5', '\x0', '+', '\x0', 'f', '\x0',
'n', '\x0', '5', '\x0', '+', '\x0', 'f', '\x0', 'n', '\x0',
'5', '\x0', '+', '\x0', 'f', '\x0', 'g', '\x0', '=', '\x0',
'=', '\x0', '<', '\x0', '/', '\x0', 'K', '\x0', 'I', '\x0',
'D', '\x0', '>', '\x0', '<', '\x0', 'C', '\x0', 'H', '\x0',
'E', '\x0', 'C', '\x0', 'K', '\x0', 'S', '\x0', 'U', '\x0',
'M', '\x0', '>', '\x0', 'u', '\x0', 'F', '\x0', 'Y', '\x0',
'/', '\x0', 'O', '\x0', 'i', '\x0', 'r', '\x0', 'Q', '\x0',
'j', '\x0', '/', '\x0', 'U', '\x0', '=', '\x0', '<', '\x0',
'/', '\x0', 'C', '\x0', 'H', '\x0', 'E', '\x0', 'C', '\x0',
'K', '\x0', 'S', '\x0', 'U', '\x0', 'M', '\x0', '>', '\x0',
'<', '\x0', '/', '\x0', 'D', '\x0', 'A', '\x0', 'T', '\x0',
'A', '\x0', '>', '\x0', '<', '\x0', '/', '\x0', 'W', '\x0',
'R', '\x0', 'M', '\x0', 'H', '\x0', 'E', '\x0', 'A', '\x0',
'D', '\x0', 'E', '\x0', 'R', '\x0', '>', '\x0',
};
const char kExpectedWidevinePsshData[] = {
'\x12', '\x10', 'k', 'e', 'y', 'i', 'd', '1', '~', '~', '~', '~',
'~', '~', '~', '~', '~', '~', '\x12', '\x10', 'k', 'e', 'y', 'i',
'd', '2', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~',
};
std::vector<uint8_t> GetTestKeyId1() {
return std::vector<uint8_t>(std::begin(kTestKeyId1), std::end(kTestKeyId1));
}
std::vector<uint8_t> GetTestKey1() {
return std::vector<uint8_t>(std::begin(kTestKey1), std::end(kTestKey1));
}
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
// that for each generator, it can generate PSSH from a pair of key id and key
// or multiple key ids. Since some of generating methods are not implemented yet
// (or not needed), tests make sure those methods return failure.
class PsshGeneratorTest : public ::testing::Test {};
// TODO(hmchen): move each PsshGenerateTest for each specific key system
// to each individual files (e.g., playready_pssh_generate_unittest.cc).
TEST(PsshGeneratorTest, GeneratePlayReadyPsshDataFromKeyIds) {
const std::vector<std::vector<uint8_t>> kTestKeyIds = {GetTestKeyId1(),
GetTestKeyId2()};
std::unique_ptr<PlayReadyPsshGenerator> playready_pssh_generator(
new PlayReadyPsshGenerator());
ProtectionSystemSpecificInfo info;
EXPECT_NOT_OK(
playready_pssh_generator->GeneratePsshFromKeyIds(kTestKeyIds, &info));
}
// TODO(hmchen): change the testing interface from
// GeneratePsshDataFromKeyIdAndKey to GeneratePsshFromKeyIdAndKey, after the
// later one is not used as a static function.
TEST(PsshGeneratorTest, GeneratePlayReadyPsshDataFromKeyIdAndKey) {
const std::vector<uint8_t> kTestKeyId = GetTestKeyId1();
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);
const std::vector<uint8_t> kExpectedPsshData = GetExpectedPlayReadyPsshData();
EXPECT_EQ(kExpectedPsshData, pssh_data.value());
}
TEST(PsshGeneratorTest, GenerateRawKeyPsshDataFromKeyIds) {
const std::vector<std::vector<uint8_t>> kTestKeyIds = {GetTestKeyId1(),
GetTestKeyId2()};
std::unique_ptr<RawKeyPsshGenerator> raw_key_pssh_generator(
new RawKeyPsshGenerator());
ProtectionSystemSpecificInfo info;
EXPECT_OK(raw_key_pssh_generator->GeneratePsshFromKeyIds(kTestKeyIds, &info));
// Intentionally empty pssh data for raw key.
EXPECT_TRUE(info.pssh_data().empty());
}
TEST(PsshGeneratorTest, GenerateRawKeyPsshDataFromKeyIdAndKey) {
const std::vector<uint8_t> kTestKeyId = GetTestKeyId1();
const std::vector<uint8_t> kTestKey = GetTestKey1();
std::unique_ptr<RawKeyPsshGenerator> raw_key_pssh_generator(
new RawKeyPsshGenerator());
ProtectionSystemSpecificInfo info;
EXPECT_NOT_OK(raw_key_pssh_generator->GeneratePsshFromKeyIdAndKey(
kTestKeyId, kTestKey, &info));
}
TEST(PsshGeneratorTest, GenerateWidevinePsshDataFromKeyIds) {
const std::vector<std::vector<uint8_t>> kTestKeyIds = {GetTestKeyId1(),
GetTestKeyId2()};
std::unique_ptr<WidevinePsshGenerator> widevine_pssh_generator(
new WidevinePsshGenerator());
ProtectionSystemSpecificInfo info;
ASSERT_OK(
widevine_pssh_generator->GeneratePsshFromKeyIds(kTestKeyIds, &info));
const std::vector<uint8_t> kExpectedPsshData = GetExpectedWidevinePsshData();
EXPECT_EQ(kExpectedPsshData, info.pssh_data());
}
TEST(PsshGeneratorTest, GenerateWidevinyPsshDataFromKeyIdAndKey) {
const std::vector<uint8_t> kTestKeyId = GetTestKeyId1();
const std::vector<uint8_t> kTestKey = GetTestKey1();
std::unique_ptr<WidevinePsshGenerator> widevine_pssh_generator(
new WidevinePsshGenerator());
ProtectionSystemSpecificInfo info;
EXPECT_NOT_OK(widevine_pssh_generator->GeneratePsshFromKeyIdAndKey(
kTestKeyId, kTestKey, &info));
}
} // namespace media
} // namespace shaka

View File

@ -0,0 +1,30 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "packager/media/base/pssh_generator_util.h"
#include <string>
#include "packager/media/base/widevine_pssh_data.pb.h"
namespace shaka {
namespace media {
namespace {
std::vector<uint8_t> StringToBytes(const std::string& string) {
return std::vector<uint8_t>(string.begin(), string.end());
}
} // namespace
std::vector<uint8_t> GenerateWidevinePsshDataFromKeyIds(
const std::vector<std::vector<uint8_t>>& key_ids) {
media::WidevinePsshData widevine_pssh_data;
for (const std::vector<uint8_t>& key_id : key_ids)
widevine_pssh_data.add_key_id(key_id.data(), key_id.size());
return StringToBytes(widevine_pssh_data.SerializeAsString());
}
} // namespace media
} // namespace shaka

View File

@ -0,0 +1,24 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#ifndef PACKAGER_MEDIA_BASE_PSSH_GENERATOR_UTIL_H_
#define PACKAGER_MEDIA_BASE_PSSH_GENERATOR_UTIL_H_
#include <iostream>
#include <memory>
#include <set>
#include <vector>
namespace shaka {
namespace media {
std::vector<uint8_t> GenerateWidevinePsshDataFromKeyIds(
const std::vector<std::vector<uint8_t>>& key_ids);
} // namespace media
} // namespace shaka
#endif // PACKAGER_MEDIA_BASE_PSSH_GENERATOR_UTIL_H_

View File

@ -0,0 +1,46 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "packager/media/base/raw_key_pssh_generator.h"
#include "packager/media/base/raw_key_source.h"
namespace shaka {
namespace media {
RawKeyPsshGenerator::RawKeyPsshGenerator()
: system_id_(std::begin(kCommonSystemId), std::end(kCommonSystemId)) {}
RawKeyPsshGenerator::~RawKeyPsshGenerator() {}
bool RawKeyPsshGenerator::SupportMultipleKeys() {
return true;
}
uint8_t RawKeyPsshGenerator::PsshBoxVersion() const {
return 1;
}
const std::vector<uint8_t>& RawKeyPsshGenerator::SystemId() const {
return system_id_;
}
base::Optional<std::vector<uint8_t>>
RawKeyPsshGenerator::GeneratePsshDataFromKeyIdAndKey(
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key) const {
NOTIMPLEMENTED();
return base::nullopt;
}
base::Optional<std::vector<uint8_t>>
RawKeyPsshGenerator::GeneratePsshDataFromKeyIds(
const std::vector<std::vector<uint8_t>>& key_ids) const {
// Intentionally empty PSSH data for RawKey.
return std::vector<uint8_t>();
}
} // namespace media
} // namespace shaka

View File

@ -0,0 +1,58 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#ifndef MEDIA_BASE_RAW_KEY_PSSH_GENERATOR_H_
#define MEDIA_BASE_RAW_KEY_PSSH_GENERATOR_H_
#include <vector>
#include "packager/media/base/key_source.h"
#include "packager/media/base/protection_system_specific_info.h"
#include "packager/media/base/pssh_generator.h"
#include "testing/gtest/include/gtest/gtest_prod.h"
namespace shaka {
namespace media {
// Common SystemID defined by EME, which requires Key System implementations
// supporting ISO Common Encryption to support this SystemID and format.
// https://goo.gl/kUv2Xd
const uint8_t kCommonSystemId[] = {0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2,
0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e,
0x52, 0xe2, 0xfb, 0x4b};
class RawKeyPsshGenerator : public PsshGenerator {
public:
RawKeyPsshGenerator();
~RawKeyPsshGenerator() override;
/// @name PsshGenerator implemetation overrides.
/// @{
bool SupportMultipleKeys() override;
/// @}
private:
RawKeyPsshGenerator& operator=(const RawKeyPsshGenerator&) = delete;
RawKeyPsshGenerator(const RawKeyPsshGenerator&) = delete;
// PsshGenerator implemetation overrides.
uint8_t PsshBoxVersion() const override;
const std::vector<uint8_t>& SystemId() const override;
base::Optional<std::vector<uint8_t>> GeneratePsshDataFromKeyIds(
const std::vector<std::vector<uint8_t>>& key_ids) const override;
base::Optional<std::vector<uint8_t>> GeneratePsshDataFromKeyIdAndKey(
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key) const override;
std::vector<uint8_t> system_id_;
};
} // namespace media
} // namespace shaka
#endif // MEDIA_BASE_RAW_KEY_PSSH_GENERATOR_H_

View File

@ -9,6 +9,7 @@
#include <algorithm> #include <algorithm>
#include "packager/base/logging.h" #include "packager/base/logging.h"
#include "packager/base/strings/string_number_conversions.h" #include "packager/base/strings/string_number_conversions.h"
#include "packager/media/base/raw_key_pssh_generator.h"
namespace { namespace {
const char kEmptyDrmLabel[] = ""; const char kEmptyDrmLabel[] = "";
@ -152,9 +153,10 @@ std::unique_ptr<RawKeySource> RawKeySource::Create(
new RawKeySource(std::move(encryption_key_map))); new RawKeySource(std::move(encryption_key_map)));
} }
RawKeySource::RawKeySource() {} RawKeySource::RawKeySource() : KeySource(COMMON_PROTECTION_SYSTEM_FLAG) {}
RawKeySource::RawKeySource(EncryptionKeyMap&& encryption_key_map) RawKeySource::RawKeySource(EncryptionKeyMap&& encryption_key_map)
: encryption_key_map_(std::move(encryption_key_map)) {} : KeySource(COMMON_PROTECTION_SYSTEM_FLAG),
encryption_key_map_(std::move(encryption_key_map)) {}
} // namespace media } // namespace media
} // namespace shaka } // namespace shaka

View File

@ -17,13 +17,6 @@
namespace shaka { namespace shaka {
namespace media { namespace media {
// Common SystemID defined by EME, which requires Key System implementations
// supporting ISO Common Encryption to support this SystemID and format.
// https://goo.gl/kUv2Xd
const uint8_t kCommonSystemId[] = {0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2,
0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e,
0x52, 0xe2, 0xfb, 0x4b};
// Unofficial fairplay system id extracted from // Unofficial fairplay system id extracted from
// https://forums.developer.apple.com/thread/6185. // https://forums.developer.apple.com/thread/6185.
const uint8_t kFairplaySystemId[] = {0x29, 0x70, 0x1F, 0xE4, 0x3C, 0xC7, const uint8_t kFairplaySystemId[] = {0x29, 0x70, 0x1F, 0xE4, 0x3C, 0xC7,

View File

@ -6,8 +6,6 @@
#include "packager/media/base/widevine_key_source.h" #include "packager/media/base/widevine_key_source.h"
#include <set>
#include "packager/base/base64.h" #include "packager/base/base64.h"
#include "packager/base/bind.h" #include "packager/base/bind.h"
#include "packager/base/json/json_reader.h" #include "packager/base/json/json_reader.h"
@ -17,10 +15,10 @@
#include "packager/media/base/network_util.h" #include "packager/media/base/network_util.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/protection_system_specific_info.h"
#include "packager/media/base/raw_key_source.h" #include "packager/media/base/pssh_generator_util.h"
#include "packager/media/base/rcheck.h" #include "packager/media/base/rcheck.h"
#include "packager/media/base/request_signer.h" #include "packager/media/base/request_signer.h"
#include "packager/media/base/widevine_pssh_data.pb.h" #include "packager/media/base/widevine_pssh_generator.h"
namespace shaka { namespace shaka {
namespace media { namespace media {
@ -44,18 +42,6 @@ const int kDefaultCryptoPeriodCount = 10;
const int kGetKeyTimeoutInSeconds = 5 * 60; // 5 minutes. const int kGetKeyTimeoutInSeconds = 5 * 60; // 5 minutes.
const int kKeyFetchTimeoutInSeconds = 60; // 1 minute. const int kKeyFetchTimeoutInSeconds = 60; // 1 minute.
std::vector<uint8_t> StringToBytes(const std::string& string) {
return std::vector<uint8_t>(string.begin(), string.end());
}
std::vector<uint8_t> WidevinePsshFromKeyId(
const std::vector<std::vector<uint8_t>>& key_ids) {
media::WidevinePsshData widevine_pssh_data;
for (const std::vector<uint8_t>& key_id : key_ids)
widevine_pssh_data.add_key_id(key_id.data(), key_id.size());
return StringToBytes(widevine_pssh_data.SerializeAsString());
}
bool Base64StringToBytes(const std::string& base64_string, bool Base64StringToBytes(const std::string& base64_string,
std::vector<uint8_t>* bytes) { std::vector<uint8_t>* bytes) {
DCHECK(bytes); DCHECK(bytes);
@ -128,15 +114,16 @@ bool IsProtectionSchemeValid(FourCC protection_scheme) {
} // namespace } // namespace
WidevineKeySource::WidevineKeySource(const std::string& server_url, WidevineKeySource::WidevineKeySource(const std::string& server_url,
bool add_common_pssh) int protection_system_flags)
: key_production_thread_("KeyProductionThread", // Widevine PSSH is fetched from Widevine license server.
: KeySource(protection_system_flags & ~WIDEVINE_PROTECTION_SYSTEM_FLAG),
key_production_thread_("KeyProductionThread",
base::Bind(&WidevineKeySource::FetchKeysTask, base::Bind(&WidevineKeySource::FetchKeysTask,
base::Unretained(this))), base::Unretained(this))),
key_fetcher_(new HttpKeyFetcher(kKeyFetchTimeoutInSeconds)), key_fetcher_(new HttpKeyFetcher(kKeyFetchTimeoutInSeconds)),
server_url_(server_url), server_url_(server_url),
crypto_period_count_(kDefaultCryptoPeriodCount), crypto_period_count_(kDefaultCryptoPeriodCount),
protection_scheme_(FOURCC_cenc), protection_scheme_(FOURCC_cenc),
add_common_pssh_(add_common_pssh),
key_production_started_(false), key_production_started_(false),
start_key_production_(base::WaitableEvent::ResetPolicy::AUTOMATIC, start_key_production_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED), base::WaitableEvent::InitialState::NOT_SIGNALED),
@ -191,14 +178,14 @@ Status WidevineKeySource::FetchKeys(EmeInitDataType init_data_type,
init_data.data(), init_data.size(), &protection_systems_info)) { init_data.data(), init_data.size(), &protection_systems_info)) {
return Status(error::PARSER_FAILURE, "Error parsing the PSSH boxes."); return Status(error::PARSER_FAILURE, "Error parsing the PSSH boxes.");
} }
for (const auto& info: protection_systems_info) { for (const auto& info : protection_systems_info) {
// Use Widevine PSSH if available otherwise construct a Widevine PSSH // Use Widevine PSSH if available otherwise construct a Widevine PSSH
// from the first available key ids. // from the first available key ids.
if (info.system_id() == widevine_system_id) { if (info.system_id() == widevine_system_id) {
pssh_data = info.pssh_data(); pssh_data = info.pssh_data();
break; break;
} else if (pssh_data.empty() && !info.key_ids().empty()) { } else if (pssh_data.empty() && !info.key_ids().empty()) {
pssh_data = WidevinePsshFromKeyId(info.key_ids()); pssh_data = GenerateWidevinePsshDataFromKeyIds(info.key_ids());
// Continue to see if there is any Widevine PSSH. The KeyId generated // Continue to see if there is any Widevine PSSH. The KeyId generated
// PSSH is only used if a Widevine PSSH could not be found. // PSSH is only used if a Widevine PSSH could not be found.
continue; continue;
@ -208,9 +195,10 @@ Status WidevineKeySource::FetchKeys(EmeInitDataType init_data_type,
return Status(error::INVALID_ARGUMENT, "No supported PSSHs found."); return Status(error::INVALID_ARGUMENT, "No supported PSSHs found.");
break; break;
} }
case EmeInitDataType::WEBM: case EmeInitDataType::WEBM: {
pssh_data = WidevinePsshFromKeyId({init_data}); pssh_data = GenerateWidevinePsshDataFromKeyIds({init_data});
break; break;
}
case EmeInitDataType::WIDEVINE_CLASSIC: case EmeInitDataType::WIDEVINE_CLASSIC:
if (init_data.size() < sizeof(asset_id)) if (init_data.size() < sizeof(asset_id))
return Status(error::INVALID_ARGUMENT, "Invalid asset id."); return Status(error::INVALID_ARGUMENT, "Invalid asset id.");
@ -585,24 +573,9 @@ bool WidevineKeySource::ExtractEncryptionKey(
encryption_key_map[stream_label] = std::move(encryption_key); encryption_key_map[stream_label] = std::move(encryption_key);
} }
// If the flag exists, create a common system ID PSSH box that contains the if (!widevine_classic) {
// key IDs of all the keys. if (!UpdateProtectionSystemInfo(&encryption_key_map).ok()) {
if (add_common_pssh_ && !widevine_classic) { return false;
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);
} }
} }

View File

@ -18,10 +18,6 @@
namespace shaka { namespace shaka {
namespace media { namespace media {
const uint8_t kWidevineSystemId[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6,
0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
0xd5, 0x1d, 0x21, 0xed};
class KeyFetcher; class KeyFetcher;
class RequestSigner; class RequestSigner;
template <class T> class ProducerConsumerQueue; template <class T> class ProducerConsumerQueue;
@ -31,7 +27,10 @@ template <class T> class ProducerConsumerQueue;
class WidevineKeySource : public KeySource { class WidevineKeySource : public KeySource {
public: public:
/// @param server_url is the Widevine common encryption server url. /// @param server_url is the Widevine common encryption server url.
WidevineKeySource(const std::string& server_url, bool add_common_pssh); /// @param proteciton_systems_flags is the flags indicating which PSSH should
/// be included.
WidevineKeySource(const std::string& server_url,
int protection_systems_flags);
~WidevineKeySource() override; ~WidevineKeySource() override;
@ -72,8 +71,6 @@ class WidevineKeySource : public KeySource {
void set_group_id(const std::vector<uint8_t>& group_id); void set_group_id(const std::vector<uint8_t>& group_id);
private: private:
typedef std::map<std::string, std::unique_ptr<EncryptionKey>>
EncryptionKeyMap;
typedef ProducerConsumerQueue<std::shared_ptr<EncryptionKeyMap>> typedef ProducerConsumerQueue<std::shared_ptr<EncryptionKeyMap>>
EncryptionKeyQueue; EncryptionKeyQueue;
@ -124,7 +121,6 @@ class WidevineKeySource : public KeySource {
const uint32_t crypto_period_count_; const uint32_t crypto_period_count_;
FourCC protection_scheme_; FourCC protection_scheme_;
base::Lock lock_; base::Lock lock_;
bool add_common_pssh_;
bool key_production_started_; bool key_production_started_;
base::WaitableEvent start_key_production_; base::WaitableEvent start_key_production_;
uint32_t first_crypto_period_index_; uint32_t first_crypto_period_index_;

View File

@ -13,9 +13,11 @@
#include "packager/base/strings/string_number_conversions.h" #include "packager/base/strings/string_number_conversions.h"
#include "packager/base/strings/stringprintf.h" #include "packager/base/strings/stringprintf.h"
#include "packager/media/base/key_fetcher.h" #include "packager/media/base/key_fetcher.h"
#include "packager/media/base/raw_key_source.h" #include "packager/media/base/playready_pssh_generator.h"
#include "packager/media/base/raw_key_pssh_generator.h"
#include "packager/media/base/request_signer.h" #include "packager/media/base/request_signer.h"
#include "packager/media/base/widevine_key_source.h" #include "packager/media/base/widevine_key_source.h"
#include "packager/media/base/widevine_pssh_generator.h"
#include "packager/status_test_util.h" #include "packager/status_test_util.h"
using ::testing::_; using ::testing::_;
@ -103,7 +105,11 @@ std::string GetMockKeyId(const std::string& track_type) {
} }
std::string GetMockKey(const std::string& track_type) { std::string GetMockKey(const std::string& track_type) {
return "MockKey" + track_type; // The key must be 16 characters, in case the key is needed to generate a
// PlayReady pssh.
std::string key = "MockKey" + track_type;
key.resize(16, '~');
return key;
} }
std::string GetMockPsshData() { std::string GetMockPsshData() {
@ -189,8 +195,13 @@ class WidevineKeySourceTest : public Test {
} }
void CreateWidevineKeySource() { void CreateWidevineKeySource() {
int protection_system_flags = WIDEVINE_PROTECTION_SYSTEM_FLAG;
if (add_common_pssh_)
protection_system_flags |= COMMON_PROTECTION_SYSTEM_FLAG;
if (add_playready_pssh_)
protection_system_flags |= PLAYREADY_PROTECTION_SYSTEM_FLAG;
widevine_key_source_.reset( widevine_key_source_.reset(
new WidevineKeySource(kServerUrl, add_common_pssh_)); new WidevineKeySource(kServerUrl, protection_system_flags));
widevine_key_source_->set_protection_scheme(protection_scheme_); widevine_key_source_->set_protection_scheme(protection_scheme_);
widevine_key_source_->set_key_fetcher(std::move(mock_key_fetcher_)); widevine_key_source_->set_key_fetcher(std::move(mock_key_fetcher_));
} }
@ -202,8 +213,9 @@ class WidevineKeySourceTest : public Test {
ASSERT_OK(widevine_key_source_->GetKey(stream_label, &encryption_key)); ASSERT_OK(widevine_key_source_->GetKey(stream_label, &encryption_key));
EXPECT_EQ(GetMockKey(stream_label), ToString(encryption_key.key)); EXPECT_EQ(GetMockKey(stream_label), ToString(encryption_key.key));
if (!classic) { if (!classic) {
ASSERT_EQ(add_common_pssh_ ? 2u : 1u, size_t num_key_system_info =
encryption_key.key_system_info.size()); 1 + (add_common_pssh_ ? 1 : 0) + (add_playready_pssh_ ? 1 : 0);
ASSERT_EQ(num_key_system_info, encryption_key.key_system_info.size());
EXPECT_EQ(GetMockKeyId(stream_label), ToString(encryption_key.key_id)); EXPECT_EQ(GetMockKeyId(stream_label), ToString(encryption_key.key_id));
EXPECT_EQ(GetMockPsshData(), EXPECT_EQ(GetMockPsshData(),
ToString(encryption_key.key_system_info[0].pssh_data())); ToString(encryption_key.key_system_info[0].pssh_data()));
@ -226,6 +238,24 @@ class WidevineKeySourceTest : public Test {
EXPECT_THAT(key_ids, testing::Contains(key_id)); EXPECT_THAT(key_ids, testing::Contains(key_id));
} }
} }
if (add_playready_pssh_) {
const std::vector<uint8_t> playready_system_id(
kPlayReadySystemId,
kPlayReadySystemId + arraysize(kPlayReadySystemId));
// Playready pssh index depends on if there has common pssh box.
const uint8_t playready_index = 1 + (add_common_pssh_ ? 1 : 0);
ASSERT_EQ(
playready_system_id,
encryption_key.key_system_info[playready_index].system_id());
const std::vector<std::vector<uint8_t>>& key_ids =
encryption_key.key_system_info[playready_index].key_ids();
// Each of the keys contains its corresponding key ID.
ASSERT_EQ(1u, key_ids.size());
EXPECT_EQ(ToString(key_ids[0]), GetMockKeyId(stream_label));
}
} }
} }
} }
@ -234,6 +264,7 @@ class WidevineKeySourceTest : public Test {
std::unique_ptr<WidevineKeySource> widevine_key_source_; std::unique_ptr<WidevineKeySource> widevine_key_source_;
std::vector<uint8_t> content_id_; std::vector<uint8_t> content_id_;
bool add_common_pssh_ = false; bool add_common_pssh_ = false;
bool add_playready_pssh_ = false;
FourCC protection_scheme_ = FOURCC_cenc; FourCC protection_scheme_ = FOURCC_cenc;
private: private:
@ -300,11 +331,12 @@ TEST_F(WidevineKeySourceTest, NoRetryOnUnknownError) {
class WidevineKeySourceParameterizedTest class WidevineKeySourceParameterizedTest
: public WidevineKeySourceTest, : public WidevineKeySourceTest,
public WithParamInterface<std::tr1::tuple<bool, FourCC>> { public WithParamInterface<std::tr1::tuple<bool, bool, FourCC>> {
public: public:
WidevineKeySourceParameterizedTest() { WidevineKeySourceParameterizedTest() {
add_common_pssh_ = std::tr1::get<0>(GetParam()); add_common_pssh_ = std::tr1::get<0>(GetParam());
protection_scheme_ = std::tr1::get<1>(GetParam()); add_playready_pssh_ = std::tr1::get<1>(GetParam());
protection_scheme_ = std::tr1::get<2>(GetParam());
} }
}; };
@ -442,7 +474,11 @@ const char kCryptoPeriodTrackFormat[] =
"\"crypto_period_index\":%u}"; "\"crypto_period_index\":%u}";
std::string GetMockKey(const std::string& track_type, uint32_t index) { std::string GetMockKey(const std::string& track_type, uint32_t index) {
return "MockKey" + track_type + "@" + base::UintToString(index); // The key must be 16 characters, in case the key is needed to generate a
// PlayReady pssh.
std::string key = "MockKey" + track_type + "@" + base::UintToString(index);
key.resize(16, '~');
return key;
} }
std::string GenerateMockKeyRotationLicenseResponse( std::string GenerateMockKeyRotationLicenseResponse(
@ -532,6 +568,7 @@ TEST_P(WidevineKeySourceParameterizedTest, KeyRotationTest) {
INSTANTIATE_TEST_CASE_P(WidevineKeySourceInstance, INSTANTIATE_TEST_CASE_P(WidevineKeySourceInstance,
WidevineKeySourceParameterizedTest, WidevineKeySourceParameterizedTest,
Combine(Bool(), Combine(Bool(),
Bool(),
Values(FOURCC_cenc, Values(FOURCC_cenc,
FOURCC_cbcs, FOURCC_cbcs,
FOURCC_cens, FOURCC_cens,

View File

@ -0,0 +1,47 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "packager/media/base/widevine_pssh_generator.h"
#include "packager/media/base/pssh_generator_util.h"
#include "packager/media/base/widevine_key_source.h"
namespace shaka {
namespace media {
WidevinePsshGenerator::WidevinePsshGenerator()
: system_id_(std::begin(kWidevineSystemId), std::end(kWidevineSystemId)) {}
WidevinePsshGenerator::~WidevinePsshGenerator() {}
bool WidevinePsshGenerator::SupportMultipleKeys() {
return true;
}
uint8_t WidevinePsshGenerator::PsshBoxVersion() const {
// This is for backward compatibility.
return 0;
}
const std::vector<uint8_t>& WidevinePsshGenerator::SystemId() const {
return system_id_;
}
base::Optional<std::vector<uint8_t>>
WidevinePsshGenerator::GeneratePsshDataFromKeyIds(
const std::vector<std::vector<uint8_t>>& key_ids) const {
return GenerateWidevinePsshDataFromKeyIds(key_ids);
}
base::Optional<std::vector<uint8_t>>
WidevinePsshGenerator::GeneratePsshDataFromKeyIdAndKey(
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key) const {
NOTIMPLEMENTED();
return base::nullopt;
}
} // namespace media
} // namespace shaka

View File

@ -0,0 +1,56 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#ifndef MEDIA_BASE_WIDEVINE_PSSH_GENERATOR_H_
#define MEDIA_BASE_WIDEVINE_PSSH_GENERATOR_H_
#include <vector>
#include "packager/media/base/key_source.h"
#include "packager/media/base/protection_system_specific_info.h"
#include "testing/gtest/include/gtest/gtest_prod.h"
namespace shaka {
namespace media {
const uint8_t kWidevineSystemId[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6,
0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
0xd5, 0x1d, 0x21, 0xed};
class WidevineKeySource;
class WidevinePsshGenerator : public PsshGenerator {
public:
WidevinePsshGenerator();
~WidevinePsshGenerator() override;
/// @name PsshGenerator implemetation overrides.
/// @{
bool SupportMultipleKeys() override;
/// @}
private:
WidevinePsshGenerator& operator=(const WidevinePsshGenerator&) = delete;
WidevinePsshGenerator(const WidevinePsshGenerator&) = delete;
// PsshGenerator implemetation overrides.
uint8_t PsshBoxVersion() const override;
const std::vector<uint8_t>& SystemId() const override;
base::Optional<std::vector<uint8_t>> GeneratePsshDataFromKeyIds(
const std::vector<std::vector<uint8_t>>& key_ids) const override;
base::Optional<std::vector<uint8_t>> GeneratePsshDataFromKeyIdAndKey(
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key) const override;
std::vector<uint8_t> system_id_;
};
} // namespace media
} // namespace shaka
#endif // MEDIA_BASE_WIDEVINE_PSSH_GENERATOR_H_

View File

@ -51,11 +51,6 @@ struct WidevineSigner {
struct WidevineEncryptionParams { struct WidevineEncryptionParams {
/// Widevine license / key server URL. /// Widevine license / key server URL.
std::string key_server_url; std::string key_server_url;
/// Generates and includes an additional v1 PSSH box for the common system ID.
/// See: https://goo.gl/s8RIhr.
// TODO(kqyang): Move to EncryptionParams and support common PSSH generation
// in all key providers.
bool include_common_pssh = false;
/// Content identifier. /// Content identifier.
std::vector<uint8_t> content_id; std::vector<uint8_t> content_id;
/// The name of a stored policy, which specifies DRM content rights. /// The name of a stored policy, which specifies DRM content rights.
@ -128,6 +123,20 @@ struct EncryptionParams {
PlayreadyEncryptionParams playready; PlayreadyEncryptionParams playready;
RawKeyParams raw_key; RawKeyParams raw_key;
/// When it is true, generate a v1 PSSH box for the common
/// system ID. See: https://goo.gl/s8RIhr.
/// The flag is default to be true if --enable_raw_key_encryption
/// is set and no other pssh flags are specified.
bool generate_common_pssh = false;
/// When it is true, include a Playready PSSH box.
/// A playready PSSH is always generated regardless of the value of
/// --generate_playready_pssh for --enable_playready_encryption.
bool generate_playready_pssh = false;
/// When it is true, include a widevine PSSH box.
/// A widevine PSSH is always generated regardless of the value of
/// --generate_widevine_pssh for --enable_widevine_encryption.
bool generate_widevine_pssh = false;
/// Clear lead duration in seconds. /// Clear lead duration in seconds.
double clear_lead_in_seconds = 0; double clear_lead_in_seconds = 0;
/// The protection scheme: "cenc", "cens", "cbc1", "cbcs". /// The protection scheme: "cenc", "cens", "cbc1", "cbcs".

View File

@ -81,6 +81,8 @@
'app/playready_key_encryption_flags.h', 'app/playready_key_encryption_flags.h',
'app/raw_key_encryption_flags.cc', 'app/raw_key_encryption_flags.cc',
'app/raw_key_encryption_flags.h', 'app/raw_key_encryption_flags.h',
'app/protection_system_flags.cc',
'app/protection_system_flags.h',
'app/retired_flags.cc', 'app/retired_flags.cc',
'app/retired_flags.h', 'app/retired_flags.h',
'app/stream_descriptor.cc', 'app/stream_descriptor.cc',

View File

@ -13,5 +13,7 @@
#define EXPECT_OK(val) EXPECT_EQ(shaka::Status::OK, (val)) #define EXPECT_OK(val) EXPECT_EQ(shaka::Status::OK, (val))
#define ASSERT_OK(val) ASSERT_EQ(shaka::Status::OK, (val)) #define ASSERT_OK(val) ASSERT_EQ(shaka::Status::OK, (val))
#define EXPECT_NOT_OK(val) EXPECT_NE(shaka::Status::OK, (val))
#define ASSERT_NOT_OK(val) ASSERT_NE(shaka::Status::OK, (val))
#endif // PACKAGER_STATUS_TEST_UTIL_H_ #endif // PACKAGER_STATUS_TEST_UTIL_H_