From 76d68de6c109e43b40df10fd26c13f1357efb3ae Mon Sep 17 00:00:00 2001 From: Haoming Chen Date: Tue, 12 Sep 2017 12:22:39 -0700 Subject: [PATCH] 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 --- packager/app/packager_main.cc | 5 +- packager/app/packager_util.cc | 18 +- packager/app/protection_system_flags.cc | 26 +++ packager/app/protection_system_flags.h | 18 ++ packager/app/widevine_encryption_flags.cc | 10 - packager/app/widevine_encryption_flags.h | 1 - packager/hls/base/simple_hls_notifier.cc | 2 + .../hls/base/simple_hls_notifier_unittest.cc | 2 + packager/media/base/key_source.cc | 50 ++++- packager/media/base/key_source.h | 15 +- packager/media/base/media_base.gyp | 11 + packager/media/base/playready_key_source.cc | 138 ++----------- packager/media/base/playready_key_source.h | 4 - .../media/base/playready_pssh_generator.cc | 185 +++++++++++++++++ .../media/base/playready_pssh_generator.h | 61 ++++++ .../base/protection_system_specific_info.h | 10 +- packager/media/base/pssh_generator.cc | 61 ++++++ packager/media/base/pssh_generator.h | 73 +++++++ .../media/base/pssh_generator_unittest.cc | 189 ++++++++++++++++++ packager/media/base/pssh_generator_util.cc | 30 +++ packager/media/base/pssh_generator_util.h | 24 +++ packager/media/base/raw_key_pssh_generator.cc | 46 +++++ packager/media/base/raw_key_pssh_generator.h | 58 ++++++ packager/media/base/raw_key_source.cc | 6 +- packager/media/base/raw_key_source.h | 7 - packager/media/base/widevine_key_source.cc | 55 ++--- packager/media/base/widevine_key_source.h | 12 +- .../base/widevine_key_source_unittest.cc | 53 ++++- .../media/base/widevine_pssh_generator.cc | 47 +++++ packager/media/base/widevine_pssh_generator.h | 56 ++++++ packager/media/public/crypto_params.h | 19 +- packager/packager.gyp | 2 + packager/status_test_util.h | 2 + 33 files changed, 1083 insertions(+), 213 deletions(-) create mode 100644 packager/app/protection_system_flags.cc create mode 100644 packager/app/protection_system_flags.h create mode 100644 packager/media/base/playready_pssh_generator.cc create mode 100644 packager/media/base/playready_pssh_generator.h create mode 100644 packager/media/base/pssh_generator.cc create mode 100644 packager/media/base/pssh_generator.h create mode 100644 packager/media/base/pssh_generator_unittest.cc create mode 100644 packager/media/base/pssh_generator_util.cc create mode 100644 packager/media/base/pssh_generator_util.h create mode 100644 packager/media/base/raw_key_pssh_generator.cc create mode 100644 packager/media/base/raw_key_pssh_generator.h create mode 100644 packager/media/base/widevine_pssh_generator.cc create mode 100644 packager/media/base/widevine_pssh_generator.h diff --git a/packager/app/packager_main.cc b/packager/app/packager_main.cc index b49749be2e..b65b9e02fe 100644 --- a/packager/app/packager_main.cc +++ b/packager/app/packager_main.cc @@ -15,6 +15,7 @@ #include "packager/app/muxer_flags.h" #include "packager/app/packager_util.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/stream_descriptor.h" #include "packager/app/vlog_flags.h" @@ -279,6 +280,9 @@ base::Optional GetPackagingParams() { int num_key_providers = 0; 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) { encryption_params.key_provider = KeyProvider::kWidevine; ++num_key_providers; @@ -313,7 +317,6 @@ base::Optional GetPackagingParams() { case KeyProvider::kWidevine: { WidevineEncryptionParams& widevine = encryption_params.widevine; widevine.key_server_url = FLAGS_key_server_url; - widevine.include_common_pssh = FLAGS_include_common_pssh; widevine.content_id = FLAGS_content_id_bytes; widevine.policy = FLAGS_policy; diff --git a/packager/app/packager_util.cc b/packager/app/packager_util.cc index 9e25dbc5f5..19b825745d 100644 --- a/packager/app/packager_util.cc +++ b/packager/app/packager_util.cc @@ -49,6 +49,14 @@ std::unique_ptr CreateSigner(const WidevineSigner& signer) { std::unique_ptr CreateEncryptionKeySource( FourCC protection_scheme, 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 encryption_key_source; switch (encryption_params.key_provider) { case KeyProvider::kWidevine: { @@ -63,7 +71,7 @@ std::unique_ptr CreateEncryptionKeySource( } std::unique_ptr widevine_key_source( new WidevineKeySource(widevine.key_server_url, - widevine.include_common_pssh)); + protection_systems_flags)); widevine_key_source->set_protection_scheme(protection_scheme); if (!widevine.signer.signer_name.empty()) { std::unique_ptr request_signer( @@ -85,10 +93,12 @@ std::unique_ptr CreateEncryptionKeySource( break; } case KeyProvider::kRawKey: { + // TODO(hmchen): add multiple DRM support for raw key source. encryption_key_source = RawKeySource::Create(encryption_params.raw_key); break; } case KeyProvider::kPlayready: { + // TODO(hmchen): add multiple DRM support for playready key source. const PlayreadyEncryptionParams& playready = encryption_params.playready; if (!playready.key_id.empty() || !playready.key.empty()) { if (playready.key_id.empty() || playready.key.empty()) { @@ -151,9 +161,9 @@ std::unique_ptr CreateDecryptionKeySource( LOG(ERROR) << "'key_server_url' should not be empty."; return std::unique_ptr(); } - std::unique_ptr widevine_key_source( - new WidevineKeySource(widevine.key_server_url, - true /* commmon pssh, does not matter here */)); + std::unique_ptr widevine_key_source(new WidevineKeySource( + widevine.key_server_url, + WIDEVINE_PROTECTION_SYSTEM_FLAG /* value does not matter here */)); if (!widevine.signer.signer_name.empty()) { std::unique_ptr request_signer( CreateSigner(widevine.signer)); diff --git a/packager/app/protection_system_flags.cc b/packager/app/protection_system_flags.cc new file mode 100644 index 0000000000..11951c6dee --- /dev/null +++ b/packager/app/protection_system_flags.cc @@ -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."); diff --git a/packager/app/protection_system_flags.h b/packager/app/protection_system_flags.h new file mode 100644 index 0000000000..39c185e16d --- /dev/null +++ b/packager/app/protection_system_flags.h @@ -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 + +DECLARE_bool(generate_common_pssh); +DECLARE_bool(generate_playready_pssh); +DECLARE_bool(generate_widevine_pssh); + +#endif // PACKAGER_APP_PROTECTION_SYSTEM_FLAGS_H_ diff --git a/packager/app/widevine_encryption_flags.cc b/packager/app/widevine_encryption_flags.cc index 9604978fdc..66757c71dc 100644 --- a/packager/app/widevine_encryption_flags.cc +++ b/packager/app/widevine_encryption_flags.cc @@ -23,11 +23,6 @@ DEFINE_bool(enable_widevine_decryption, "Enable decryption with Widevine license server/proxy. User should " "provide either AES signing key (--aes_signing_key, " "--aes_signing_iv) or RSA signing key (--rsa_signing_key_path)."); -DEFINE_bool(include_common_pssh, - false, - "When using Widevine encryption, include an additional v1 PSSH box " - "for the common system ID that includes the key IDs. See: " - "https://goo.gl/s8RIhr"); DEFINE_string(key_server_url, "", "Key server url. Required for encryption and " "decryption"); DEFINE_hex_bytes(content_id, "", "Content Id (hex)."); @@ -117,11 +112,6 @@ bool ValidateWidevineCryptoFlags() { widevine_encryption_label)) { success = false; } - if (FLAGS_include_common_pssh && !FLAGS_enable_widevine_encryption) { - PrintError("--include_common_pssh is only valid with " - "--enable_widevine_encryption"); - success = false; - } if (FLAGS_max_sd_pixels <= 0) { PrintError("--max_sd_pixels must be positive."); diff --git a/packager/app/widevine_encryption_flags.h b/packager/app/widevine_encryption_flags.h index c584bf49b9..d15407b483 100644 --- a/packager/app/widevine_encryption_flags.h +++ b/packager/app/widevine_encryption_flags.h @@ -15,7 +15,6 @@ DECLARE_bool(enable_widevine_encryption); DECLARE_bool(enable_widevine_decryption); -DECLARE_bool(include_common_pssh); DECLARE_string(key_server_url); DECLARE_hex_bytes(content_id); DECLARE_string(policy); diff --git a/packager/hls/base/simple_hls_notifier.cc b/packager/hls/base/simple_hls_notifier.cc index a491b1adf4..6f89ccb99e 100644 --- a/packager/hls/base/simple_hls_notifier.cc +++ b/packager/hls/base/simple_hls_notifier.cc @@ -17,9 +17,11 @@ #include "packager/base/strings/stringprintf.h" #include "packager/hls/base/media_playlist.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/widevine_key_source.h" #include "packager/media/base/widevine_pssh_data.pb.h" +#include "packager/media/base/widevine_pssh_generator.h" namespace shaka { diff --git a/packager/hls/base/simple_hls_notifier_unittest.cc b/packager/hls/base/simple_hls_notifier_unittest.cc index 2036edc2bf..cff6625463 100644 --- a/packager/hls/base/simple_hls_notifier_unittest.cc +++ b/packager/hls/base/simple_hls_notifier_unittest.cc @@ -14,9 +14,11 @@ #include "packager/hls/base/mock_media_playlist.h" #include "packager/hls/base/simple_hls_notifier.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/widevine_key_source.h" #include "packager/media/base/widevine_pssh_data.pb.h" +#include "packager/media/base/widevine_pssh_generator.h" namespace shaka { namespace hls { diff --git a/packager/media/base/key_source.cc b/packager/media/base/key_source.cc index b652396e85..97d6a17078 100644 --- a/packager/media/base/key_source.cc +++ b/packager/media/base/key_source.cc @@ -7,6 +7,9 @@ #include "packager/media/base/key_source.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 media { @@ -15,9 +18,54 @@ 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() {} +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> 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 shaka diff --git a/packager/media/base/key_source.h b/packager/media/base/key_source.h index 6cd279505c..a18b0ad28f 100644 --- a/packager/media/base/key_source.h +++ b/packager/media/base/key_source.h @@ -7,10 +7,13 @@ #ifndef PACKAGER_MEDIA_BASE_KEY_SOURCE_H_ #define PACKAGER_MEDIA_BASE_KEY_SOURCE_H_ +#include +#include #include #include #include "packager/media/base/protection_system_specific_info.h" +#include "packager/media/base/pssh_generator.h" #include "packager/status.h" namespace shaka { @@ -41,10 +44,13 @@ struct EncryptionKey { std::vector iv; }; +typedef std::map> EncryptionKeyMap; + /// KeySource is responsible for encryption key acquisition. class KeySource { public: - KeySource(); + explicit KeySource(int protection_systems_flags); + virtual ~KeySource(); /// Fetch keys based on the specified encrypted media init data. @@ -81,7 +87,14 @@ class KeySource { const std::string& stream_label, 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: + std::vector> pssh_generators_; + DISALLOW_COPY_AND_ASSIGN(KeySource); }; diff --git a/packager/media/base/media_base.gyp b/packager/media/base/media_base.gyp index 221a1b848a..b857070a42 100644 --- a/packager/media/base/media_base.gyp +++ b/packager/media/base/media_base.gyp @@ -72,10 +72,18 @@ 'offset_byte_queue.h', 'playready_key_source.cc', 'playready_key_source.h', + 'playready_pssh_generator.cc', + 'playready_pssh_generator.h', 'producer_consumer_queue.h', 'protection_system_specific_info.cc', 'protection_system_specific_info.h', + 'pssh_generator.cc', + 'pssh_generator.h', + 'pssh_generator_util.cc', + 'pssh_generator_util.h', 'range.h', + 'raw_key_pssh_generator.cc', + 'raw_key_pssh_generator.h', 'raw_key_source.cc', 'raw_key_source.h', 'rcheck.h', @@ -97,6 +105,8 @@ 'video_stream_info.h', 'widevine_key_source.cc', 'widevine_key_source.h', + 'widevine_pssh_generator.cc', + 'widevine_pssh_generator.h' ], 'dependencies': [ 'widevine_pssh_data_proto', @@ -150,6 +160,7 @@ 'offset_byte_queue_unittest.cc', 'producer_consumer_queue_unittest.cc', 'protection_system_specific_info_unittest.cc', + 'pssh_generator_unittest.cc', 'raw_key_source_unittest.cc', 'rsa_key_unittest.cc', 'status_test_util_unittest.cc', diff --git a/packager/media/base/playready_key_source.cc b/packager/media/base/playready_key_source.cc index 053326a636..c8a55e2da8 100644 --- a/packager/media/base/playready_key_source.cc +++ b/packager/media/base/playready_key_source.cc @@ -6,7 +6,6 @@ #include "packager/media/base/playready_key_source.h" -#include #include #include "packager/base/base64.h" #include "packager/base/logging.h" @@ -14,6 +13,8 @@ #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_pssh_generator.h" namespace shaka { namespace media { @@ -21,16 +22,6 @@ namespace media { namespace { const uint32_t kHttpFetchTimeout = 60; // In seconds -const std::string kPlayHeaderObject_4_1 = "" - "" - ""; -const std::string kPlayHeaderObject_4_0 = "16" - "AESCTR$0$1" - ""; const std::string kAcquireLicenseRequest = "" "assign(str.begin(), str.end()); return true; } - -// Converts the key_id's endianness. -std::vector ConvertGuidEndianness(const std::vector& input) { - std::vector 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& key_id, - const std::vector& key, - std::vector* output) { - CHECK(output); - std::vector key_id_converted = ConvertGuidEndianness(key_id); - std::vector encrypted_key_id(key_id_converted.size()); - std::unique_ptr 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 record_value = - std::vector(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(record_type & 0xff)); - writer_pr_record.AppendInt(static_cast((record_type >> 8) & 0xff)); - writer_pr_record.AppendInt(static_cast(record_length & 0xff)); - writer_pr_record.AppendInt(static_cast((record_length >> 8) & 0xff)); - for (auto record_item: record_value) { - writer_pr_record.AppendInt(static_cast(record_item & 0xff)); - writer_pr_record.AppendInt(static_cast((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(playready_header_length & 0xff)); - writer_pr_header_object.AppendInt( - static_cast((playready_header_length >> 8) & 0xff)); - writer_pr_header_object.AppendInt( - static_cast((playready_header_length >> 16) & 0xff)); - writer_pr_header_object.AppendInt( - static_cast((playready_header_length >> 24) & 0xff)); - writer_pr_header_object.AppendInt( - static_cast(record_count & 0xff)); - writer_pr_header_object.AppendInt( - static_cast((record_count >> 8) & 0xff)); - writer_pr_header_object.AppendBuffer(writer_pr_record); - *output = std::vector(writer_pr_header_object.Buffer(), - writer_pr_header_object.Buffer() + - writer_pr_header_object.Size()); - return Status::OK; -} - -} // namespace +PlayReadyKeySource::PlayReadyKeySource(const std::string& server_url) + : KeySource(PLAYREADY_PROTECTION_SYSTEM_FLAG), + encryption_key_(new EncryptionKey), + server_url_(server_url) {} PlayReadyKeySource::PlayReadyKeySource( - const std::string& server_url) - : encryption_key_(new EncryptionKey), - server_url_(server_url) { -} - -PlayReadyKeySource::PlayReadyKeySource(const std::string& server_url, + const std::string& server_url, const std::string& client_cert_file, const std::string& client_cert_private_key_file, const std::string& client_cert_private_key_password) - : encryption_key_(new EncryptionKey), + : KeySource(PLAYREADY_PROTECTION_SYSTEM_FLAG), + encryption_key_(new EncryptionKey), server_url_(server_url), client_cert_file_(client_cert_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( std::unique_ptr encryption_key) - : encryption_key_(std::move(encryption_key)) { -} + : KeySource(PLAYREADY_PROTECTION_SYSTEM_FLAG), + encryption_key_(std::move(encryption_key)) {} PlayReadyKeySource::~PlayReadyKeySource() {} @@ -187,16 +90,17 @@ std::unique_ptr PlayReadyKeySource::CreateFromKeyAndKeyId( std::unique_ptr encryption_key(new EncryptionKey); encryption_key->key_id = key_id; encryption_key->key = key; - std::vector 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(); - } + ProtectionSystemSpecificInfo info; - info.add_key_id(encryption_key->key_id); + info.add_key_id(key_id); info.set_system_id(kPlayReadySystemId, arraysize(kPlayReadySystemId)); + + std::vector pssh_data = + PlayReadyPsshGenerator::GeneratePsshFromKeyIdAndKey(key_id, key); + if (pssh_data.empty()) { + DLOG(INFO) << "Fail to generate PlayReady PSSH."; + return nullptr; + } info.set_pssh_data(pssh_data); encryption_key->key_system_info.push_back(info); diff --git a/packager/media/base/playready_key_source.h b/packager/media/base/playready_key_source.h index c3576ec43f..e95fa81498 100644 --- a/packager/media/base/playready_key_source.h +++ b/packager/media/base/playready_key_source.h @@ -16,10 +16,6 @@ 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}; - /// A key source that uses playready for encryption. class PlayReadyKeySource : public KeySource { public: diff --git a/packager/media/base/playready_pssh_generator.cc b/packager/media/base/playready_pssh_generator.cc new file mode 100644 index 0000000000..b6801feedc --- /dev/null +++ b/packager/media/base/playready_pssh_generator.cc @@ -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 +#include +#include +#include +#include + +#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 = + "" + "" + ""; +const std::string kPlayHeaderObject_4_0 = + "16" + "AESCTR$0$1" + ""; + +// Converts the key_id's endianness. +std::vector ConvertGuidEndianness(const std::vector& input) { + std::vector 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& key_id, + const std::vector& key, + std::vector* output) { + CHECK(output); + std::vector key_id_converted = ConvertGuidEndianness(key_id); + std::vector encrypted_key_id(key_id_converted.size()); + std::unique_ptr 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 record_value = + std::vector(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(record_type & 0xff)); + writer_pr_record.AppendInt(static_cast((record_type >> 8) & 0xff)); + writer_pr_record.AppendInt(static_cast(record_length & 0xff)); + writer_pr_record.AppendInt(static_cast((record_length >> 8) & 0xff)); + for (auto record_item : record_value) { + writer_pr_record.AppendInt(static_cast(record_item & 0xff)); + writer_pr_record.AppendInt(static_cast((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(playready_header_length & 0xff)); + writer_pr_header_object.AppendInt( + static_cast((playready_header_length >> 8) & 0xff)); + writer_pr_header_object.AppendInt( + static_cast((playready_header_length >> 16) & 0xff)); + writer_pr_header_object.AppendInt( + static_cast((playready_header_length >> 24) & 0xff)); + writer_pr_header_object.AppendInt(static_cast(record_count & 0xff)); + writer_pr_header_object.AppendInt( + static_cast((record_count >> 8) & 0xff)); + writer_pr_header_object.AppendBuffer(writer_pr_record); + *output = std::vector( + 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 PlayReadyPsshGenerator::GeneratePsshFromKeyIdAndKey( + const std::vector& key_id, + const std::vector& key) { + std::vector pssh_data; + Status status = GeneratePlayReadyPsshData(key_id, key, &pssh_data); + if (!status.ok()) { + LOG(ERROR) << status.ToString(); + return std::vector(); + } + + return pssh_data; +} + +bool PlayReadyPsshGenerator::SupportMultipleKeys() { + return false; +} + +uint8_t PlayReadyPsshGenerator::PsshBoxVersion() const { + return 1; +} + +const std::vector& PlayReadyPsshGenerator::SystemId() const { + return system_id_; +} + +base::Optional> +PlayReadyPsshGenerator::GeneratePsshDataFromKeyIdAndKey( + const std::vector& key_id, + const std::vector& key) const { + std::vector 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> +PlayReadyPsshGenerator::GeneratePsshDataFromKeyIds( + const std::vector>& key_ids) const { + NOTIMPLEMENTED(); + return base::nullopt; +} +} // namespace media +} // namespace shaka diff --git a/packager/media/base/playready_pssh_generator.h b/packager/media/base/playready_pssh_generator.h new file mode 100644 index 0000000000..ff5a163b3e --- /dev/null +++ b/packager/media/base/playready_pssh_generator.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 + +#ifndef MEDIA_BASE_PLAYREADY_PSSH_GENERATOR_H_ +#define MEDIA_BASE_PLAYREADY_PSSH_GENERATOR_H_ + +#include + +#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 GeneratePsshFromKeyIdAndKey( + const std::vector& key_id, + const std::vector& 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& SystemId() const override; + + base::Optional> GeneratePsshDataFromKeyIds( + const std::vector>& key_ids) const override; + + base::Optional> GeneratePsshDataFromKeyIdAndKey( + const std::vector& key_id, + const std::vector& key) const override; + + std::vector system_id_; + + FRIEND_TEST(PsshGeneratorTest, GeneratePlayReadyPsshDataFromKeyIdAndKey); +}; + +} // namespace media +} // namespace shaka + +#endif // MEDIA_BASE_PLAYREADY_PSSH_GENERATOR_H_ diff --git a/packager/media/base/protection_system_specific_info.h b/packager/media/base/protection_system_specific_info.h index b25703d982..76d80de225 100644 --- a/packager/media/base/protection_system_specific_info.h +++ b/packager/media/base/protection_system_specific_info.h @@ -4,8 +4,8 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -#ifndef PACKAGER_MEDIA_BASE_PSSH_H_ -#define PACKAGER_MEDIA_BASE_PSSH_H_ +#ifndef PACKAGER_MEDIA_BASE_PROTECTION_SYSTEM_SPECIFIC_INFO_H_ +#define PACKAGER_MEDIA_BASE_PROTECTION_SYSTEM_SPECIFIC_INFO_H_ #include #include @@ -13,6 +13,10 @@ #include "packager/base/logging.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 media { @@ -70,4 +74,4 @@ class ProtectionSystemSpecificInfo { } // namespace media } // namespace shaka -#endif // PACKAGER_MEDIA_BASE_PSSH_H_ +#endif // PACKAGER_MEDIA_BASE_PROTECTION_SYSTEM_SPECIFIC_INFO_H_ diff --git a/packager/media/base/pssh_generator.cc b/packager/media/base/pssh_generator.cc new file mode 100644 index 0000000000..9741741b6b --- /dev/null +++ b/packager/media/base/pssh_generator.cc @@ -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>& key_ids, + ProtectionSystemSpecificInfo* info) const { + base::Optional> 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& system_id = SystemId(); + info->set_system_id(system_id.data(), system_id.size()); + + return Status::OK; +} + +Status PsshGenerator::GeneratePsshFromKeyIdAndKey( + const std::vector& key_id, + const std::vector& key, + ProtectionSystemSpecificInfo* info) const { + base::Optional> 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& system_id = SystemId(); + info->set_system_id(system_id.data(), system_id.size()); + + return Status::OK; +} + +} // namespace media +} // namespace shaka diff --git a/packager/media/base/pssh_generator.h b/packager/media/base/pssh_generator.h new file mode 100644 index 0000000000..c96aeb89ca --- /dev/null +++ b/packager/media/base/pssh_generator.h @@ -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 + +#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>& 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& key_id, + const std::vector& 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> GeneratePsshDataFromKeyIds( + const std::vector>& 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> GeneratePsshDataFromKeyIdAndKey( + const std::vector& key_id, + const std::vector& key) const = 0; + + /// Return the PSSH box version. + virtual uint8_t PsshBoxVersion() const = 0; + + /// Return the System ID. + virtual const std::vector& SystemId() const = 0; +}; + +} // namespace media +} // namespace shaka + +#endif // PACKAGER_MEDIA_BASE_PSSH_GENERATOR_H_ diff --git a/packager/media/base/pssh_generator_unittest.cc b/packager/media/base/pssh_generator_unittest.cc new file mode 100644 index 0000000000..87c25ba833 --- /dev/null +++ b/packager/media/base/pssh_generator_unittest.cc @@ -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 + +#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 GetTestKeyId1() { + return std::vector(std::begin(kTestKeyId1), std::end(kTestKeyId1)); +} + +std::vector GetTestKey1() { + return std::vector(std::begin(kTestKey1), std::end(kTestKey1)); +} + +std::vector GetTestKeyId2() { + return std::vector(std::begin(kTestKeyId2), std::end(kTestKeyId2)); +} + +std::vector GetExpectedPlayReadyPsshData() { + return std::vector(std::begin(kExpectedPlayReadyPsshData), + std::end(kExpectedPlayReadyPsshData)); +} + +std::vector GetExpectedWidevinePsshData() { + return std::vector(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> kTestKeyIds = {GetTestKeyId1(), + GetTestKeyId2()}; + std::unique_ptr 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 kTestKeyId = GetTestKeyId1(); + const std::vector kTestKey = GetTestKey1(); + std::unique_ptr playready_pssh_generator( + new PlayReadyPsshGenerator()); + base::Optional> pssh_data = + playready_pssh_generator->GeneratePsshDataFromKeyIdAndKey(kTestKeyId, + kTestKey); + ASSERT_TRUE(pssh_data); + + const std::vector kExpectedPsshData = GetExpectedPlayReadyPsshData(); + EXPECT_EQ(kExpectedPsshData, pssh_data.value()); +} + +TEST(PsshGeneratorTest, GenerateRawKeyPsshDataFromKeyIds) { + const std::vector> kTestKeyIds = {GetTestKeyId1(), + GetTestKeyId2()}; + std::unique_ptr 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 kTestKeyId = GetTestKeyId1(); + const std::vector kTestKey = GetTestKey1(); + std::unique_ptr 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> kTestKeyIds = {GetTestKeyId1(), + GetTestKeyId2()}; + std::unique_ptr widevine_pssh_generator( + new WidevinePsshGenerator()); + ProtectionSystemSpecificInfo info; + ASSERT_OK( + widevine_pssh_generator->GeneratePsshFromKeyIds(kTestKeyIds, &info)); + + const std::vector kExpectedPsshData = GetExpectedWidevinePsshData(); + EXPECT_EQ(kExpectedPsshData, info.pssh_data()); +} + +TEST(PsshGeneratorTest, GenerateWidevinyPsshDataFromKeyIdAndKey) { + const std::vector kTestKeyId = GetTestKeyId1(); + const std::vector kTestKey = GetTestKey1(); + std::unique_ptr widevine_pssh_generator( + new WidevinePsshGenerator()); + ProtectionSystemSpecificInfo info; + EXPECT_NOT_OK(widevine_pssh_generator->GeneratePsshFromKeyIdAndKey( + kTestKeyId, kTestKey, &info)); +} + +} // namespace media +} // namespace shaka diff --git a/packager/media/base/pssh_generator_util.cc b/packager/media/base/pssh_generator_util.cc new file mode 100644 index 0000000000..5a74f61ca1 --- /dev/null +++ b/packager/media/base/pssh_generator_util.cc @@ -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 + +#include "packager/media/base/widevine_pssh_data.pb.h" + +namespace shaka { +namespace media { +namespace { + +std::vector StringToBytes(const std::string& string) { + return std::vector(string.begin(), string.end()); +} +} // namespace + +std::vector GenerateWidevinePsshDataFromKeyIds( + const std::vector>& key_ids) { + media::WidevinePsshData widevine_pssh_data; + for (const std::vector& 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 diff --git a/packager/media/base/pssh_generator_util.h b/packager/media/base/pssh_generator_util.h new file mode 100644 index 0000000000..a237320529 --- /dev/null +++ b/packager/media/base/pssh_generator_util.h @@ -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 +#include +#include +#include + +namespace shaka { +namespace media { + +std::vector GenerateWidevinePsshDataFromKeyIds( + const std::vector>& key_ids); + +} // namespace media +} // namespace shaka + +#endif // PACKAGER_MEDIA_BASE_PSSH_GENERATOR_UTIL_H_ diff --git a/packager/media/base/raw_key_pssh_generator.cc b/packager/media/base/raw_key_pssh_generator.cc new file mode 100644 index 0000000000..05efc87545 --- /dev/null +++ b/packager/media/base/raw_key_pssh_generator.cc @@ -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& RawKeyPsshGenerator::SystemId() const { + return system_id_; +} + +base::Optional> +RawKeyPsshGenerator::GeneratePsshDataFromKeyIdAndKey( + const std::vector& key_id, + const std::vector& key) const { + NOTIMPLEMENTED(); + return base::nullopt; +} + +base::Optional> +RawKeyPsshGenerator::GeneratePsshDataFromKeyIds( + const std::vector>& key_ids) const { + // Intentionally empty PSSH data for RawKey. + return std::vector(); +} +} // namespace media +} // namespace shaka diff --git a/packager/media/base/raw_key_pssh_generator.h b/packager/media/base/raw_key_pssh_generator.h new file mode 100644 index 0000000000..bdf1dd4989 --- /dev/null +++ b/packager/media/base/raw_key_pssh_generator.h @@ -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 + +#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& SystemId() const override; + + base::Optional> GeneratePsshDataFromKeyIds( + const std::vector>& key_ids) const override; + + base::Optional> GeneratePsshDataFromKeyIdAndKey( + const std::vector& key_id, + const std::vector& key) const override; + + std::vector system_id_; +}; +} // namespace media +} // namespace shaka + +#endif // MEDIA_BASE_RAW_KEY_PSSH_GENERATOR_H_ diff --git a/packager/media/base/raw_key_source.cc b/packager/media/base/raw_key_source.cc index 2e5c3ac01c..c43061e1ab 100644 --- a/packager/media/base/raw_key_source.cc +++ b/packager/media/base/raw_key_source.cc @@ -9,6 +9,7 @@ #include #include "packager/base/logging.h" #include "packager/base/strings/string_number_conversions.h" +#include "packager/media/base/raw_key_pssh_generator.h" namespace { const char kEmptyDrmLabel[] = ""; @@ -152,9 +153,10 @@ std::unique_ptr RawKeySource::Create( new RawKeySource(std::move(encryption_key_map))); } -RawKeySource::RawKeySource() {} +RawKeySource::RawKeySource() : KeySource(COMMON_PROTECTION_SYSTEM_FLAG) {} 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 shaka diff --git a/packager/media/base/raw_key_source.h b/packager/media/base/raw_key_source.h index 4785233e1b..1f5e83a4b5 100644 --- a/packager/media/base/raw_key_source.h +++ b/packager/media/base/raw_key_source.h @@ -17,13 +17,6 @@ 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}; - // Unofficial fairplay system id extracted from // https://forums.developer.apple.com/thread/6185. const uint8_t kFairplaySystemId[] = {0x29, 0x70, 0x1F, 0xE4, 0x3C, 0xC7, diff --git a/packager/media/base/widevine_key_source.cc b/packager/media/base/widevine_key_source.cc index a8ebe3b9a8..5f8f45e23c 100644 --- a/packager/media/base/widevine_key_source.cc +++ b/packager/media/base/widevine_key_source.cc @@ -6,8 +6,6 @@ #include "packager/media/base/widevine_key_source.h" -#include - #include "packager/base/base64.h" #include "packager/base/bind.h" #include "packager/base/json/json_reader.h" @@ -17,10 +15,10 @@ #include "packager/media/base/network_util.h" #include "packager/media/base/producer_consumer_queue.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/request_signer.h" -#include "packager/media/base/widevine_pssh_data.pb.h" +#include "packager/media/base/widevine_pssh_generator.h" namespace shaka { namespace media { @@ -44,18 +42,6 @@ const int kDefaultCryptoPeriodCount = 10; const int kGetKeyTimeoutInSeconds = 5 * 60; // 5 minutes. const int kKeyFetchTimeoutInSeconds = 60; // 1 minute. -std::vector StringToBytes(const std::string& string) { - return std::vector(string.begin(), string.end()); -} - -std::vector WidevinePsshFromKeyId( - const std::vector>& key_ids) { - media::WidevinePsshData widevine_pssh_data; - for (const std::vector& 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, std::vector* bytes) { DCHECK(bytes); @@ -128,15 +114,16 @@ bool IsProtectionSchemeValid(FourCC protection_scheme) { } // namespace WidevineKeySource::WidevineKeySource(const std::string& server_url, - bool add_common_pssh) - : key_production_thread_("KeyProductionThread", + int protection_system_flags) + // 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::Unretained(this))), key_fetcher_(new HttpKeyFetcher(kKeyFetchTimeoutInSeconds)), server_url_(server_url), crypto_period_count_(kDefaultCryptoPeriodCount), protection_scheme_(FOURCC_cenc), - add_common_pssh_(add_common_pssh), key_production_started_(false), start_key_production_(base::WaitableEvent::ResetPolicy::AUTOMATIC, 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)) { 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 // from the first available key ids. if (info.system_id() == widevine_system_id) { pssh_data = info.pssh_data(); break; } 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 // PSSH is only used if a Widevine PSSH could not be found. continue; @@ -208,9 +195,10 @@ Status WidevineKeySource::FetchKeys(EmeInitDataType init_data_type, return Status(error::INVALID_ARGUMENT, "No supported PSSHs found."); break; } - case EmeInitDataType::WEBM: - pssh_data = WidevinePsshFromKeyId({init_data}); + case EmeInitDataType::WEBM: { + pssh_data = GenerateWidevinePsshDataFromKeyIds({init_data}); break; + } case EmeInitDataType::WIDEVINE_CLASSIC: if (init_data.size() < sizeof(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); } - // If the flag exists, create a common system ID PSSH box that contains the - // key IDs of all the keys. - if (add_common_pssh_ && !widevine_classic) { - std::set> 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& 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); + if (!widevine_classic) { + if (!UpdateProtectionSystemInfo(&encryption_key_map).ok()) { + return false; } } diff --git a/packager/media/base/widevine_key_source.h b/packager/media/base/widevine_key_source.h index c8e3b92cf0..726f0dad26 100644 --- a/packager/media/base/widevine_key_source.h +++ b/packager/media/base/widevine_key_source.h @@ -18,10 +18,6 @@ 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 KeyFetcher; class RequestSigner; template class ProducerConsumerQueue; @@ -31,7 +27,10 @@ template class ProducerConsumerQueue; class WidevineKeySource : public KeySource { public: /// @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; @@ -72,8 +71,6 @@ class WidevineKeySource : public KeySource { void set_group_id(const std::vector& group_id); private: - typedef std::map> - EncryptionKeyMap; typedef ProducerConsumerQueue> EncryptionKeyQueue; @@ -124,7 +121,6 @@ class WidevineKeySource : public KeySource { const uint32_t crypto_period_count_; FourCC protection_scheme_; base::Lock lock_; - bool add_common_pssh_; bool key_production_started_; base::WaitableEvent start_key_production_; uint32_t first_crypto_period_index_; diff --git a/packager/media/base/widevine_key_source_unittest.cc b/packager/media/base/widevine_key_source_unittest.cc index fa93ade1ae..a8a22afc3d 100644 --- a/packager/media/base/widevine_key_source_unittest.cc +++ b/packager/media/base/widevine_key_source_unittest.cc @@ -13,9 +13,11 @@ #include "packager/base/strings/string_number_conversions.h" #include "packager/base/strings/stringprintf.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/widevine_key_source.h" +#include "packager/media/base/widevine_pssh_generator.h" #include "packager/status_test_util.h" using ::testing::_; @@ -103,7 +105,11 @@ std::string GetMockKeyId(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() { @@ -189,8 +195,13 @@ class WidevineKeySourceTest : public Test { } 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( - new WidevineKeySource(kServerUrl, add_common_pssh_)); + new WidevineKeySource(kServerUrl, protection_system_flags)); widevine_key_source_->set_protection_scheme(protection_scheme_); 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)); EXPECT_EQ(GetMockKey(stream_label), ToString(encryption_key.key)); if (!classic) { - ASSERT_EQ(add_common_pssh_ ? 2u : 1u, - encryption_key.key_system_info.size()); + size_t num_key_system_info = + 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(GetMockPsshData(), 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)); } } + + if (add_playready_pssh_) { + const std::vector 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>& 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 widevine_key_source_; std::vector content_id_; bool add_common_pssh_ = false; + bool add_playready_pssh_ = false; FourCC protection_scheme_ = FOURCC_cenc; private: @@ -300,11 +331,12 @@ TEST_F(WidevineKeySourceTest, NoRetryOnUnknownError) { class WidevineKeySourceParameterizedTest : public WidevineKeySourceTest, - public WithParamInterface> { + public WithParamInterface> { public: WidevineKeySourceParameterizedTest() { 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}"; 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( @@ -532,6 +568,7 @@ TEST_P(WidevineKeySourceParameterizedTest, KeyRotationTest) { INSTANTIATE_TEST_CASE_P(WidevineKeySourceInstance, WidevineKeySourceParameterizedTest, Combine(Bool(), + Bool(), Values(FOURCC_cenc, FOURCC_cbcs, FOURCC_cens, diff --git a/packager/media/base/widevine_pssh_generator.cc b/packager/media/base/widevine_pssh_generator.cc new file mode 100644 index 0000000000..fdc32b7bfe --- /dev/null +++ b/packager/media/base/widevine_pssh_generator.cc @@ -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& WidevinePsshGenerator::SystemId() const { + return system_id_; +} + +base::Optional> +WidevinePsshGenerator::GeneratePsshDataFromKeyIds( + const std::vector>& key_ids) const { + return GenerateWidevinePsshDataFromKeyIds(key_ids); +} + +base::Optional> +WidevinePsshGenerator::GeneratePsshDataFromKeyIdAndKey( + const std::vector& key_id, + const std::vector& key) const { + NOTIMPLEMENTED(); + return base::nullopt; +} +} // namespace media +} // namespace shaka diff --git a/packager/media/base/widevine_pssh_generator.h b/packager/media/base/widevine_pssh_generator.h new file mode 100644 index 0000000000..f6162c163d --- /dev/null +++ b/packager/media/base/widevine_pssh_generator.h @@ -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 + +#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& SystemId() const override; + + base::Optional> GeneratePsshDataFromKeyIds( + const std::vector>& key_ids) const override; + + base::Optional> GeneratePsshDataFromKeyIdAndKey( + const std::vector& key_id, + const std::vector& key) const override; + + std::vector system_id_; +}; + +} // namespace media +} // namespace shaka + +#endif // MEDIA_BASE_WIDEVINE_PSSH_GENERATOR_H_ diff --git a/packager/media/public/crypto_params.h b/packager/media/public/crypto_params.h index 61118283a5..4a97d959b2 100644 --- a/packager/media/public/crypto_params.h +++ b/packager/media/public/crypto_params.h @@ -51,11 +51,6 @@ struct WidevineSigner { struct WidevineEncryptionParams { /// Widevine license / 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. std::vector content_id; /// The name of a stored policy, which specifies DRM content rights. @@ -128,6 +123,20 @@ struct EncryptionParams { PlayreadyEncryptionParams playready; 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. double clear_lead_in_seconds = 0; /// The protection scheme: "cenc", "cens", "cbc1", "cbcs". diff --git a/packager/packager.gyp b/packager/packager.gyp index 261e1bc485..47b8cd1fe5 100644 --- a/packager/packager.gyp +++ b/packager/packager.gyp @@ -81,6 +81,8 @@ 'app/playready_key_encryption_flags.h', 'app/raw_key_encryption_flags.cc', 'app/raw_key_encryption_flags.h', + 'app/protection_system_flags.cc', + 'app/protection_system_flags.h', 'app/retired_flags.cc', 'app/retired_flags.h', 'app/stream_descriptor.cc', diff --git a/packager/status_test_util.h b/packager/status_test_util.h index 2868f6597e..d8e28cd322 100644 --- a/packager/status_test_util.h +++ b/packager/status_test_util.h @@ -13,5 +13,7 @@ #define EXPECT_OK(val) EXPECT_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_