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:
parent
62bfc1c6c1
commit
76d68de6c1
|
@ -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;
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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.");
|
|
@ -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_
|
|
@ -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.");
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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_
|
||||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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".
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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_
|
||||||
|
|
Loading…
Reference in New Issue