From 65e558b19c749a0bebb6f6cc42958b73f404408d Mon Sep 17 00:00:00 2001 From: Thomas Inskip Date: Wed, 20 Aug 2014 16:51:15 -0700 Subject: [PATCH] KeySource changes to support media decryption. Renamed EncryptionKeySource and WidevineEncryptionKeySource to KeySource and WidevineKeySource respectively. Replaced Initialize method with FetchKeys. Added unit tests. content_id parameter is now binary. Specified in command line with hex string. Change-Id: I8010ca62d036cefb3a7c105e0eefee25bbf06d61 --- README.md | 23 +-- app/packager_main.cc | 14 +- app/packager_util.cc | 59 ++++-- app/packager_util.h | 15 +- app/widevine_encryption_flags.cc | 68 +++++-- app/widevine_encryption_flags.h | 1 + ...encryption_key_source.cc => key_source.cc} | 56 ++++-- .../{encryption_key_source.h => key_source.h} | 50 ++++- media/base/media_base.gyp | 10 +- media/base/muxer.cc | 8 +- media/base/muxer.h | 20 +- ...n_key_source.cc => widevine_key_source.cc} | 179 +++++++++++------ ...ion_key_source.h => widevine_key_source.h} | 56 +++--- ...est.cc => widevine_key_source_unittest.cc} | 189 ++++++++++++------ media/formats/mp4/encrypting_fragmenter.cc | 2 +- media/formats/mp4/key_rotation_fragmenter.cc | 4 +- media/formats/mp4/key_rotation_fragmenter.h | 10 +- media/formats/mp4/mp4_muxer.cc | 2 +- media/formats/mp4/segmenter.cc | 14 +- media/formats/mp4/segmenter.h | 4 +- media/test/packager_test.cc | 18 +- 21 files changed, 526 insertions(+), 276 deletions(-) rename media/base/{encryption_key_source.cc => key_source.cc} (70%) rename media/base/{encryption_key_source.h => key_source.h} (52%) rename media/base/{widevine_encryption_key_source.cc => widevine_key_source.cc} (73%) rename media/base/{widevine_encryption_key_source.h => widevine_key_source.h} (70%) rename media/base/{widevine_encryption_key_source_unittest.cc => widevine_key_source_unittest.cc} (67%) diff --git a/README.md b/README.md index c644160436..246bcc6a4f 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Demuxer is responsible for extracting elementary stream samples from a multimedi Demuxer reads from source through the File interface. A concrete LocalFile class is already implemented. The users may also implement their own File class if they want to read/write using a different kinds of protocol, e.g. network storage, http etc. -Muxer is responsible for taking elementary stream samples and producing media segments. An optional EncryptionKeySource can be provided to Muxer to generate encrypted outputs. Muxer writes to output using the same File interface as Demuxer. +Muxer is responsible for taking elementary stream samples and producing media segments. An optional KeySource can be provided to Muxer to generate encrypted outputs. Muxer writes to output using the same File interface as Demuxer. Demuxer and Muxer are connected using MediaStream. MediaStream wraps the elementary streams and is responsible for the interaction between Demuxer and Muxer. A demuxer can transmits multiple MediaStreams; similarly, A muxer is able to accept and mux multiple MediaStreams, not necessarily from the same Demuxer. @@ -203,17 +203,17 @@ muxer_options.temp_dir = …; muxer_options.bandwidth = 0; ``` -##Creating EncryptionKeySource## +##Creating KeySource## ```C++ -// An EncryptionKeySource is optional. The stream won’t be encrypted if an -// EncryptionKeySource is not provided. +// A KeySource is optional. The stream won’t be encrypted if an +// KeySource is not provided. ``` -###WidevineEncryptionKeySource### +###WidevineKeySource### ```C++ -// Users may use WidevineEncryptionKeySource to fetch keys from Widevine +// Users may use WidevineKeySource to fetch keys from Widevine // common encryption server. // A request signer is required to sign the common encryption request. @@ -221,12 +221,11 @@ scoped_ptr signer( RsaRequestSigner::CreateSigner(signer, pkcs1_rsa_private_key)); if (!signer) { … } -scoped_ptr widevine_encryption_key_source( - new WidevineEncryptionKeySource( - key_server_url, content_id, track_type, policy, signer.Pass())); +scoped_ptr widevine_encryption_key_source( + new WidevineKeySource(key_server_url, signer.Pass())); -// Intialize widevine encryption key source. -status = widevine_encryption_key_source->Initialize(); +// Grab keys for the content. +status = widevine_encryption_key_source->FetchKeys(content_id, policy)); if (!status.ok()) { … } // Set encryption key source to muxer. @@ -236,7 +235,7 @@ if (!status.ok()) { … } // |clear_lead| specifies clear lead duration in seconds. // |crypto_period_duration| if not zero, enable key rotation with specified // crypto period. -muxer->SetEncryptionKeySource( +muxer->SetKeySource( widevine_encryption_key_source.get(), max_sd_pixels, clear_lead, crypto_period_duration); ``` diff --git a/app/packager_main.cc b/app/packager_main.cc index 03bc438097..a2a65b755c 100644 --- a/app/packager_main.cc +++ b/app/packager_main.cc @@ -19,7 +19,7 @@ #include "base/strings/stringprintf.h" #include "base/threading/simple_thread.h" #include "media/base/demuxer.h" -#include "media/base/encryption_key_source.h" +#include "media/base/key_source.h" #include "media/base/muxer_options.h" #include "media/base/muxer_util.h" #include "media/event/mpd_notify_muxer_listener.h" @@ -96,7 +96,7 @@ class RemuxJob : public base::SimpleThread { bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors, const MuxerOptions& muxer_options, - EncryptionKeySource* key_source, + KeySource* key_source, MpdNotifier* mpd_notifier, std::vector* muxer_listeners, std::vector* remux_jobs) { @@ -142,10 +142,10 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors, scoped_ptr muxer(new mp4::MP4Muxer(stream_muxer_options)); if (key_source) { - muxer->SetEncryptionKeySource(key_source, - FLAGS_max_sd_pixels, - FLAGS_clear_lead, - FLAGS_crypto_period_duration); + muxer->SetKeySource(key_source, + FLAGS_max_sd_pixels, + FLAGS_clear_lead, + FLAGS_crypto_period_duration); } scoped_ptr muxer_listener; @@ -239,7 +239,7 @@ bool RunPackager(const StreamDescriptorList& stream_descriptors) { return false; // Create encryption key source if needed. - scoped_ptr encryption_key_source; + scoped_ptr encryption_key_source; if (FLAGS_enable_widevine_encryption || FLAGS_enable_fixed_key_encryption) { encryption_key_source = CreateEncryptionKeySource(); if (!encryption_key_source) diff --git a/app/packager_util.cc b/app/packager_util.cc index 02505f39f0..149814f56d 100644 --- a/app/packager_util.cc +++ b/app/packager_util.cc @@ -18,7 +18,7 @@ #include "media/base/muxer_options.h" #include "media/base/request_signer.h" #include "media/base/stream_info.h" -#include "media/base/widevine_encryption_key_source.h" +#include "media/base/widevine_key_source.h" #include "media/file/file.h" #include "mpd/base/mpd_builder.h" @@ -34,13 +34,9 @@ void DumpStreamInfo(const std::vector& streams) { printf("Stream [%zu] %s\n", i, streams[i]->info()->ToString().c_str()); } -// Create and initialize encryptor source. -scoped_ptr CreateEncryptionKeySource() { - scoped_ptr encryption_key_source; - if (FLAGS_enable_widevine_encryption) { - scoped_ptr signer; - DCHECK(!FLAGS_aes_signing_key.empty() || - !FLAGS_rsa_signing_key_path.empty()); +scoped_ptr CreateSigner() { + scoped_ptr signer; + if (FLAGS_enable_widevine_encryption || FLAGS_enable_widevine_decryption) { if (!FLAGS_aes_signing_key.empty()) { signer.reset( AesRequestSigner::CreateSigner(FLAGS_signer, FLAGS_aes_signing_key, @@ -49,7 +45,7 @@ scoped_ptr CreateEncryptionKeySource() { LOG(ERROR) << "Cannot create an AES signer object from '" << FLAGS_aes_signing_key << "':'" << FLAGS_aes_signing_iv << "'."; - return scoped_ptr(); + return scoped_ptr(); } } else if (!FLAGS_rsa_signing_key_path.empty()) { std::string rsa_private_key; @@ -57,38 +53,57 @@ scoped_ptr CreateEncryptionKeySource() { &rsa_private_key)) { LOG(ERROR) << "Failed to read from '" << FLAGS_rsa_signing_key_path << "'."; - return scoped_ptr(); + return scoped_ptr(); } - signer.reset( RsaRequestSigner::CreateSigner(FLAGS_signer, rsa_private_key)); if (!signer) { LOG(ERROR) << "Cannot create a RSA signer object from '" << FLAGS_rsa_signing_key_path << "'."; - return scoped_ptr(); + return scoped_ptr(); } } + } + return signer.Pass(); +} - scoped_ptr widevine_encryption_key_source( - new WidevineEncryptionKeySource( - FLAGS_key_server_url, - FLAGS_content_id, - FLAGS_policy, - signer.Pass())); - Status status = widevine_encryption_key_source->Initialize(); +scoped_ptr CreateEncryptionKeySource() { + scoped_ptr encryption_key_source; + if (FLAGS_enable_widevine_encryption) { + scoped_ptr signer(CreateSigner()); + std::vector content_id; + if (!base::HexStringToBytes(FLAGS_content_id, &content_id)) { + LOG(ERROR) << "Invalid content_id hex string specified."; + return scoped_ptr(); + } + scoped_ptr widevine_encryption_key_source( + new WidevineKeySource(FLAGS_key_server_url, + signer.Pass())); + Status status = widevine_encryption_key_source->FetchKeys(content_id, + FLAGS_policy); if (!status.ok()) { - LOG(ERROR) << "Widevine encryption key source failed to initialize: " + LOG(ERROR) << "Widevine encryption key source failed to fetch keys: " << status.ToString(); - return scoped_ptr(); + return scoped_ptr(); } encryption_key_source = widevine_encryption_key_source.Pass(); } else if (FLAGS_enable_fixed_key_encryption) { - encryption_key_source = EncryptionKeySource::CreateFromHexStrings( + encryption_key_source = KeySource::CreateFromHexStrings( FLAGS_key_id, FLAGS_key, FLAGS_pssh, ""); } return encryption_key_source.Pass(); } +scoped_ptr CreateDecryptionKeySource() { + scoped_ptr decryption_key_source; + if (FLAGS_enable_widevine_decryption) { + scoped_ptr signer(CreateSigner()); + decryption_key_source.reset(new WidevineKeySource(FLAGS_key_server_url, + signer.Pass())); + } + return decryption_key_source.Pass(); +} + bool AssignFlagsFromProfile() { bool single_segment = FLAGS_single_segment; if (FLAGS_profile == "on-demand") { diff --git a/app/packager_util.h b/app/packager_util.h index 9bea418483..623b23d63e 100644 --- a/app/packager_util.h +++ b/app/packager_util.h @@ -23,7 +23,7 @@ struct MpdOptions; namespace media { -class EncryptionKeySource; +class KeySource; class MediaInfo; class MediaStream; class Muxer; @@ -32,10 +32,17 @@ struct MuxerOptions; /// Print all the stream info for the provided strings to standard output. void DumpStreamInfo(const std::vector& streams); -/// Create EncryptionKeySource based on provided command line options. -/// @return A scoped_ptr containig a new EncryptionKeySource, or NULL if +/// Create KeySource based on provided command line options for content +/// encryption. Also fetches keys. +/// @return A scoped_ptr containing a new KeySource, or NULL if /// encryption is not required. -scoped_ptr CreateEncryptionKeySource(); +scoped_ptr CreateEncryptionKeySource(); + +/// Create KeySource based on provided command line options for content +/// decryption. Does not fetch keys. +/// @return A scoped_ptr containing a new KeySource, or NULL if decryption +/// is not required. +scoped_ptr CreateDecryptionKeySource(); /// Set flags according to profile. bool AssignFlagsFromProfile(); diff --git a/app/widevine_encryption_flags.cc b/app/widevine_encryption_flags.cc index bdb2aa1ed3..d7d5d7790e 100644 --- a/app/widevine_encryption_flags.cc +++ b/app/widevine_encryption_flags.cc @@ -8,6 +8,7 @@ #include "app/widevine_encryption_flags.h" +#include "base/logging.h" #include "base/strings/string_number_conversions.h" DEFINE_bool(enable_widevine_encryption, @@ -15,8 +16,14 @@ DEFINE_bool(enable_widevine_encryption, "Enable encryption 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_string(key_server_url, "", "Key server url."); -DEFINE_string(content_id, "", "Content Id."); +DEFINE_bool(enable_widevine_decryption, + false, + "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_string(key_server_url, "", "Key server url. Required for encryption and " + "decryption"); +DEFINE_string(content_id, "", "Content Id (hex)."); DEFINE_string(policy, "", "The name of a stored policy, which specifies DRM content " @@ -44,9 +51,44 @@ DEFINE_int32(crypto_period_duration, namespace { -static bool IsNotEmptyWithWidevineEncryption(const char* flag_name, - const std::string& flag_value) { - return FLAGS_enable_widevine_encryption ? !flag_value.empty() : true; +static bool VerifyEncryptionAndDecryptionParams(const char* flag_name, + const std::string& flag_value) { + DCHECK(flag_name); + + if (FLAGS_enable_widevine_encryption) { + if (flag_value.empty()) { + fprintf(stderr, + "ERROR: %s required if enable_widevine_encryption is true\n", + flag_name); + return false; + } + } else if (FLAGS_enable_widevine_decryption) { + const std::string flag_name_str = flag_name; + if (flag_name_str == "key_server_url") { + if (flag_value.empty()) { + fprintf(stderr, + "ERROR: %s required if --enable_widevine_decryption is true\n", + flag_name); + return false; + } + } else { + if (!flag_value.empty()) { + fprintf(stderr, "ERROR: %s should only be specified if " + "--enable_widevine_decryption is true\n", flag_name); + return false; + } + } + } else { + if (!flag_value.empty()) { + const std::string flag_name_str = flag_name; + fprintf(stderr, "ERROR: %s should only be specified if %s" + " is true\n", flag_name, flag_name_str == "key_server_url" ? + "--enable_widevine_encryption or --enable_widevine_decryption" : + "--enable_widevine_encryption"); + return false; + } + } + return true; } static bool IsPositive(const char* flag_name, int flag_value) { @@ -61,20 +103,20 @@ static bool VerifyAesRsaKey(const char* flag_name, if (flag_name_str == "aes_signing_iv") { if (!FLAGS_aes_signing_key.empty() && flag_value.empty()) { fprintf(stderr, - "ERROR: --aes_signing_iv is required for --aes_signing_key.\n"); + "ERROR: --aes_signing_iv is required for --aes_signing_key.\n"); return false; } } else if (flag_name_str == "rsa_signing_key_path") { if (FLAGS_aes_signing_key.empty() && flag_value.empty()) { fprintf(stderr, - "ERROR: --aes_signing_key or --rsa_signing_key_path is " - "required.\n"); + "ERROR: --aes_signing_key or --rsa_signing_key_path is " + "required.\n"); return false; } if (!FLAGS_aes_signing_key.empty() && !flag_value.empty()) { fprintf(stderr, - "ERROR: --aes_signing_key and --rsa_signing_key_path are " - "exclusive.\n"); + "ERROR: --aes_signing_key and --rsa_signing_key_path are " + "exclusive.\n"); return false; } } @@ -83,15 +125,15 @@ static bool VerifyAesRsaKey(const char* flag_name, bool dummy_key_server_url_validator = google::RegisterFlagValidator(&FLAGS_key_server_url, - &IsNotEmptyWithWidevineEncryption); + &VerifyEncryptionAndDecryptionParams); bool dummy_content_id_validator = google::RegisterFlagValidator(&FLAGS_content_id, - &IsNotEmptyWithWidevineEncryption); + &VerifyEncryptionAndDecryptionParams); bool dummy_track_type_validator = google::RegisterFlagValidator(&FLAGS_max_sd_pixels, &IsPositive); bool dummy_signer_validator = google::RegisterFlagValidator(&FLAGS_signer, - &IsNotEmptyWithWidevineEncryption); + &VerifyEncryptionAndDecryptionParams); bool dummy_aes_iv_validator = google::RegisterFlagValidator(&FLAGS_aes_signing_iv, &VerifyAesRsaKey); diff --git a/app/widevine_encryption_flags.h b/app/widevine_encryption_flags.h index 264d393a00..249eebb7c8 100644 --- a/app/widevine_encryption_flags.h +++ b/app/widevine_encryption_flags.h @@ -12,6 +12,7 @@ #include DECLARE_bool(enable_widevine_encryption); +DECLARE_bool(enable_widevine_decryption); DECLARE_string(key_server_url); DECLARE_string(content_id); DECLARE_string(policy); diff --git a/media/base/encryption_key_source.cc b/media/base/key_source.cc similarity index 70% rename from media/base/encryption_key_source.cc rename to media/base/key_source.cc index d9dd33cd12..a61868c71f 100644 --- a/media/base/encryption_key_source.cc +++ b/media/base/key_source.cc @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -#include "media/base/encryption_key_source.h" +#include "media/base/key_source.h" #include "base/strings/string_number_conversions.h" #include "media/base/aes_encryptor.h" @@ -21,23 +21,42 @@ namespace media { EncryptionKey::EncryptionKey() {} EncryptionKey::~EncryptionKey() {} -EncryptionKeySource::~EncryptionKeySource() {} +KeySource::~KeySource() {} -Status EncryptionKeySource::GetKey(TrackType track_type, EncryptionKey* key) { +Status KeySource::FetchKeys(const std::vector& content_id, + const std::string& policy) { + NOTREACHED(); + return Status::OK; +} + +Status KeySource::FetchKeys(const std::vector& pssh_data) { + NOTREACHED(); + return Status::OK; +} + +Status KeySource::GetKey(TrackType track_type, EncryptionKey* key) { DCHECK(key); DCHECK(encryption_key_); *key = *encryption_key_; return Status::OK; } -Status EncryptionKeySource::GetCryptoPeriodKey(uint32 crypto_period_index, - TrackType track_type, - EncryptionKey* key) { +Status KeySource::GetKey(const std::vector& key_id, + EncryptionKey* key) { + DCHECK(key); + DCHECK(encryption_key_); + *key = *encryption_key_; + return Status::OK; +} + +Status KeySource::GetCryptoPeriodKey(uint32 crypto_period_index, + TrackType track_type, + EncryptionKey* key) { NOTIMPLEMENTED(); return Status(error::UNIMPLEMENTED, ""); } -scoped_ptr EncryptionKeySource::CreateFromHexStrings( +scoped_ptr KeySource::CreateFromHexStrings( const std::string& key_id_hex, const std::string& key_hex, const std::string& pssh_data_hex, @@ -46,33 +65,33 @@ scoped_ptr EncryptionKeySource::CreateFromHexStrings( if (!base::HexStringToBytes(key_id_hex, &encryption_key->key_id)) { LOG(ERROR) << "Cannot parse key_id_hex " << key_id_hex; - return scoped_ptr(); + return scoped_ptr(); } if (!base::HexStringToBytes(key_hex, &encryption_key->key)) { LOG(ERROR) << "Cannot parse key_hex " << key_hex; - return scoped_ptr(); + return scoped_ptr(); } std::vector pssh_data; if (!base::HexStringToBytes(pssh_data_hex, &pssh_data)) { LOG(ERROR) << "Cannot parse pssh_hex " << pssh_data_hex; - return scoped_ptr(); + return scoped_ptr(); } if (!iv_hex.empty()) { if (!base::HexStringToBytes(iv_hex, &encryption_key->iv)) { LOG(ERROR) << "Cannot parse iv_hex " << iv_hex; - return scoped_ptr(); + return scoped_ptr(); } } encryption_key->pssh = PsshBoxFromPsshData(pssh_data); - return scoped_ptr( - new EncryptionKeySource(encryption_key.Pass())); + return scoped_ptr( + new KeySource(encryption_key.Pass())); } -EncryptionKeySource::TrackType EncryptionKeySource::GetTrackTypeFromString( +KeySource::TrackType KeySource::GetTrackTypeFromString( const std::string& track_type_string) { if (track_type_string == "SD") return TRACK_TYPE_SD; @@ -84,7 +103,7 @@ EncryptionKeySource::TrackType EncryptionKeySource::GetTrackTypeFromString( return TRACK_TYPE_UNKNOWN; } -std::string EncryptionKeySource::TrackTypeToString(TrackType track_type) { +std::string KeySource::TrackTypeToString(TrackType track_type) { switch (track_type) { case TRACK_TYPE_SD: return "SD"; @@ -98,7 +117,7 @@ std::string EncryptionKeySource::TrackTypeToString(TrackType track_type) { } } -std::vector EncryptionKeySource::PsshBoxFromPsshData( +std::vector KeySource::PsshBoxFromPsshData( const std::vector& pssh_data) { const uint8 kPsshFourCC[] = {'p', 's', 's', 'h'}; const uint32 kVersionAndFlags = 0; @@ -118,9 +137,8 @@ std::vector EncryptionKeySource::PsshBoxFromPsshData( return std::vector(writer.Buffer(), writer.Buffer() + writer.Size()); } -EncryptionKeySource::EncryptionKeySource() {} -EncryptionKeySource::EncryptionKeySource( - scoped_ptr encryption_key) +KeySource::KeySource() {} +KeySource::KeySource(scoped_ptr encryption_key) : encryption_key_(encryption_key.Pass()) { DCHECK(encryption_key_); } diff --git a/media/base/encryption_key_source.h b/media/base/key_source.h similarity index 52% rename from media/base/encryption_key_source.h rename to media/base/key_source.h index b41870edc9..08d8c1fc8d 100644 --- a/media/base/encryption_key_source.h +++ b/media/base/key_source.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 MEDIA_BASE_ENCRYPTION_KEY_SOURCE_H_ -#define MEDIA_BASE_ENCRYPTION_KEY_SOURCE_H_ +#ifndef MEDIA_BASE_KEY_SOURCE_H_ +#define MEDIA_BASE_KEY_SOURCE_H_ #include @@ -24,8 +24,8 @@ struct EncryptionKey { std::vector iv; }; -/// EncryptionKeySource is responsible for encryption key acquisition. -class EncryptionKeySource { +/// KeySource is responsible for encryption key acquisition. +class KeySource { public: enum TrackType { TRACK_TYPE_UNKNOWN = 0, @@ -35,19 +35,47 @@ class EncryptionKeySource { NUM_VALID_TRACK_TYPES = 3 }; - virtual ~EncryptionKeySource(); + virtual ~KeySource(); + + /// Fetch keys for CENC from the key server. + /// @param content_id the unique id identify the content. + /// @param policy specifies the DRM content rights. + /// @return OK on success, an error status otherwise. + virtual Status FetchKeys(const std::vector& content_id, + const std::string& policy); + + /// Fetch keys for CENC from the key server. + /// @param pssh_data is the Data portion of the PSSH box for the content + /// to be decrypted. + /// @return OK on success, an error status otherwise. + virtual Status FetchKeys(const std::vector& pssh_data); /// Get encryption key of the specified track type. + /// @param track_type is the type of track for which retrieving the key. + /// @param key is a pointer to the EncryptionKey which will hold the retrieved + /// key. Owner retains ownership, and may not be NULL. /// @return OK on success, an error status otherwise. virtual Status GetKey(TrackType track_type, EncryptionKey* key); + /// Get the encryption key specified by the CENC key ID. + /// @param key_id is the unique identifier for the key being retreived. + /// @param key is a pointer to the EncryptionKey which will hold the retrieved + /// key. Owner retains ownership, and may not be NULL. + /// @return OK on success, or an error status otherwise. + virtual Status GetKey(const std::vector& key_id, EncryptionKey* key); + /// Get encryption key of the specified track type at the specified index. + /// @param crypto_period_index is the sequence number of the key rotation + /// period for which the key is being retrieved. + /// @param track_type is the type of track for which retrieving the key. + /// @param key is a pointer to the EncryptionKey which will hold the retrieved + /// key. Owner retains ownership, and may not be NULL. /// @return OK on success, an error status otherwise. virtual Status GetCryptoPeriodKey(uint32 crypto_period_index, TrackType track_type, EncryptionKey* key); - /// Create EncryptionKeySource object from hex strings. + /// Create KeySource object from hex strings. /// @param key_id_hex is the key id in hex string. /// @param key_hex is the key in hex string. /// @param pssh_data_hex is the pssh_data in hex string. @@ -55,7 +83,7 @@ class EncryptionKeySource { /// generated IV with the default length will be used. /// Note: GetKey on the created key source will always return the same key /// for all track types. - static scoped_ptr CreateFromHexStrings( + static scoped_ptr CreateFromHexStrings( const std::string& key_id_hex, const std::string& key_hex, const std::string& pssh_data_hex, @@ -68,7 +96,7 @@ class EncryptionKeySource { static std::string TrackTypeToString(TrackType track_type); protected: - EncryptionKeySource(); + KeySource(); /// @return the raw bytes of the pssh box with system ID and box header /// included. @@ -76,13 +104,13 @@ class EncryptionKeySource { const std::vector& pssh_data); private: - explicit EncryptionKeySource(scoped_ptr encryption_key); + explicit KeySource(scoped_ptr encryption_key); scoped_ptr encryption_key_; - DISALLOW_COPY_AND_ASSIGN(EncryptionKeySource); + DISALLOW_COPY_AND_ASSIGN(KeySource); }; } // namespace media -#endif // MEDIA_BASE_ENCRYPTION_KEY_SOURCE_H_ +#endif // MEDIA_BASE_KEY_SOURCE_H_ diff --git a/media/base/media_base.gyp b/media/base/media_base.gyp index 730ec0fc06..e913d5cc1b 100644 --- a/media/base/media_base.gyp +++ b/media/base/media_base.gyp @@ -36,10 +36,10 @@ 'decrypt_config.cc', 'decrypt_config.h', 'decryptor_source.h', - 'encryption_key_source.cc', - 'encryption_key_source.h', 'http_fetcher.cc', 'http_fetcher.h', + 'key_source.cc', + 'key_source.h', 'limits.h', 'media_parser.h', 'media_sample.cc', @@ -67,8 +67,8 @@ 'timestamp.h', 'video_stream_info.cc', 'video_stream_info.h', - 'widevine_encryption_key_source.cc', - 'widevine_encryption_key_source.h', + 'widevine_key_source.cc', + 'widevine_key_source.h', ], 'dependencies': [ '../../base/base.gyp:base', @@ -98,7 +98,7 @@ 'status_test_util.h', 'status_test_util_unittest.cc', 'status_unittest.cc', - 'widevine_encryption_key_source_unittest.cc', + 'widevine_key_source_unittest.cc', ], 'dependencies': [ '../../testing/gtest.gyp:gtest', diff --git a/media/base/muxer.cc b/media/base/muxer.cc index 4edf50117c..cdd50f0e06 100644 --- a/media/base/muxer.cc +++ b/media/base/muxer.cc @@ -23,10 +23,10 @@ Muxer::Muxer(const MuxerOptions& options) Muxer::~Muxer() {} -void Muxer::SetEncryptionKeySource(EncryptionKeySource* encryption_key_source, - uint32 max_sd_pixels, - double clear_lead_in_seconds, - double crypto_period_duration_in_seconds) { +void Muxer::SetKeySource(KeySource* encryption_key_source, + uint32 max_sd_pixels, + double clear_lead_in_seconds, + double crypto_period_duration_in_seconds) { DCHECK(encryption_key_source); encryption_key_source_ = encryption_key_source; max_sd_pixels_ = max_sd_pixels; diff --git a/media/base/muxer.h b/media/base/muxer.h index fe46fb712a..c5724e7637 100644 --- a/media/base/muxer.h +++ b/media/base/muxer.h @@ -22,7 +22,7 @@ class Clock; namespace media { -class EncryptionKeySource; +class KeySource; class MediaSample; class MediaStream; @@ -31,7 +31,7 @@ class MuxerListener; } /// Muxer is responsible for taking elementary stream samples and producing -/// media containers. An optional EncryptionKeySource can be provided to Muxer +/// media containers. An optional KeySource can be provided to Muxer /// to generate encrypted outputs. class Muxer { public: @@ -39,8 +39,8 @@ class Muxer { virtual ~Muxer(); /// Set encryption key source. - /// @param encryption_key_source points to the encryption key source to be - /// injected. Should not be NULL. + /// @param encryption_key_source points to the encryption key source. The + /// caller retains ownership, and should not be NULL. /// @param max_sd_pixels specifies the threshold to determine whether a video /// track should be considered as SD or HD. If the track has more /// pixels per frame than max_sd_pixels, it is HD, SD otherwise. @@ -48,10 +48,10 @@ class Muxer { /// @param crypto_period_duration_in_seconds specifies crypto period duration /// in seconds. A positive value means key rotation is enabled, the /// key source must support key rotation in this case. - void SetEncryptionKeySource(EncryptionKeySource* encryption_key_source, - uint32 max_sd_pixels, - double clear_lead_in_seconds, - double crypto_period_duration_in_seconds); + void SetKeySource(KeySource* encryption_key_source, + uint32 max_sd_pixels, + double clear_lead_in_seconds, + double crypto_period_duration_in_seconds); /// Add video/audio stream. void AddStream(MediaStream* stream); @@ -77,7 +77,7 @@ class Muxer { protected: const MuxerOptions& options() const { return options_; } - EncryptionKeySource* encryption_key_source() { + KeySource* encryption_key_source() { return encryption_key_source_; } uint32 max_sd_pixels() const { return max_sd_pixels_; } @@ -108,7 +108,7 @@ class Muxer { MuxerOptions options_; bool initialized_; std::vector streams_; - EncryptionKeySource* encryption_key_source_; + KeySource* encryption_key_source_; uint32 max_sd_pixels_; double clear_lead_in_seconds_; double crypto_period_duration_in_seconds_; diff --git a/media/base/widevine_encryption_key_source.cc b/media/base/widevine_key_source.cc similarity index 73% rename from media/base/widevine_encryption_key_source.cc rename to media/base/widevine_key_source.cc index 4a6ed3cf24..fc16473311 100644 --- a/media/base/widevine_encryption_key_source.cc +++ b/media/base/widevine_key_source.cc @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -#include "media/base/widevine_encryption_key_source.h" +#include "media/base/widevine_key_source.h" #include "base/base64.h" #include "base/bind.h" @@ -12,7 +12,6 @@ #include "base/json/json_writer.h" #include "base/memory/ref_counted.h" #include "base/stl_util.h" -#include "base/values.h" #include "media/base/http_fetcher.h" #include "media/base/producer_consumer_queue.h" #include "media/base/request_signer.h" @@ -55,27 +54,36 @@ bool Base64StringToBytes(const std::string& base64_string, return true; } -bool GetKeyAndKeyId(const base::DictionaryValue& track_dict, - std::vector* key, - std::vector* key_id) { - DCHECK(key); - DCHECK(key_id); +void BytesToBase64String(const std::vector& bytes, + std::string* base64_string) { + DCHECK(base64_string); + base::Base64Encode(base::StringPiece(reinterpret_cast + (bytes.data()), bytes.size()), + base64_string); +} +bool GetKeyFromTrack(const base::DictionaryValue& track_dict, + std::vector* key) { + DCHECK(key); std::string key_base64_string; RCHECK(track_dict.GetString("key", &key_base64_string)); VLOG(2) << "Key:" << key_base64_string; RCHECK(Base64StringToBytes(key_base64_string, key)); + return true; +} +bool GetKeyIdFromTrack(const base::DictionaryValue& track_dict, + std::vector* key_id) { + DCHECK(key_id); std::string key_id_base64_string; RCHECK(track_dict.GetString("key_id", &key_id_base64_string)); VLOG(2) << "Keyid:" << key_id_base64_string; RCHECK(Base64StringToBytes(key_id_base64_string, key_id)); - return true; } -bool GetPsshData(const base::DictionaryValue& track_dict, - std::vector* pssh_data) { +bool GetPsshDataFromTrack(const base::DictionaryValue& track_dict, + std::vector* pssh_data) { DCHECK(pssh_data); const base::ListValue* pssh_list; @@ -105,7 +113,7 @@ bool GetPsshData(const base::DictionaryValue& track_dict, namespace media { // A ref counted wrapper for EncryptionKeyMap. -class WidevineEncryptionKeySource::RefCountedEncryptionKeyMap +class WidevineKeySource::RefCountedEncryptionKeyMap : public base::RefCountedThreadSafe { public: explicit RefCountedEncryptionKeyMap(EncryptionKeyMap* encryption_key_map) { @@ -113,7 +121,7 @@ class WidevineEncryptionKeySource::RefCountedEncryptionKeyMap encryption_key_map_.swap(*encryption_key_map); } - std::map& map() { + std::map& map() { return encryption_key_map_; } @@ -127,15 +135,11 @@ class WidevineEncryptionKeySource::RefCountedEncryptionKeyMap DISALLOW_COPY_AND_ASSIGN(RefCountedEncryptionKeyMap); }; -WidevineEncryptionKeySource::WidevineEncryptionKeySource( +WidevineKeySource::WidevineKeySource( const std::string& server_url, - const std::string& content_id, - const std::string& policy, scoped_ptr signer) : http_fetcher_(new SimpleHttpFetcher(kHttpTimeoutInSeconds)), server_url_(server_url), - content_id_(content_id), - policy_(policy), signer_(signer.Pass()), crypto_period_count_(kDefaultCryptoPeriodCount), key_production_started_(false), @@ -143,12 +147,12 @@ WidevineEncryptionKeySource::WidevineEncryptionKeySource( first_crypto_period_index_(0), key_production_thread_( "KeyProductionThread", - base::Bind(&WidevineEncryptionKeySource::FetchKeysTask, + base::Bind(&WidevineKeySource::FetchKeysTask, base::Unretained(this))) { DCHECK(signer_); } -WidevineEncryptionKeySource::~WidevineEncryptionKeySource() { +WidevineKeySource::~WidevineKeySource() { if (key_pool_) key_pool_->Stop(); if (key_production_thread_.HasBeenStarted()) { @@ -159,17 +163,47 @@ WidevineEncryptionKeySource::~WidevineEncryptionKeySource() { } } -Status WidevineEncryptionKeySource::Initialize() { +Status WidevineKeySource::FetchKeys(const std::vector& content_id, + const std::string& policy) { + base::AutoLock scoped_lock(lock_); + request_dict_.Clear(); + std::string content_id_base64_string; + BytesToBase64String(content_id, &content_id_base64_string); + request_dict_.SetString("content_id", content_id_base64_string); + request_dict_.SetString("policy", policy); + return FetchKeysCommon(false); +} + +Status WidevineKeySource::FetchKeys( + const std::vector& pssh_data) { + base::AutoLock scoped_lock(lock_); + request_dict_.Clear(); + std::string pssh_data_base64_string; + BytesToBase64String(pssh_data, &pssh_data_base64_string); + request_dict_.SetString("pssh_data", pssh_data_base64_string); + return FetchKeysCommon(false); +} + +Status WidevineKeySource::FetchKeys(uint32 asset_id) { + base::AutoLock scoped_lock(lock_); + request_dict_.Clear(); + request_dict_.SetInteger("asset_id", asset_id); + return FetchKeysCommon(true); +} + +Status WidevineKeySource::FetchKeysCommon(bool widevine_classic) { + // TODO(tinskip): Make this method callable multiple times to fetch + // different keys. DCHECK(!key_production_thread_.HasBeenStarted()); key_production_thread_.Start(); // Perform a fetch request to find out if the key source is healthy. // It also stores the keys fetched for consumption later. - return FetchKeys(!kEnableKeyRotation, 0); + return FetchKeysInternal(!kEnableKeyRotation, 0, widevine_classic); } -Status WidevineEncryptionKeySource::GetKey(TrackType track_type, - EncryptionKey* key) { +Status WidevineKeySource::GetKey(TrackType track_type, + EncryptionKey* key) { DCHECK(key); if (encryption_key_map_.find(track_type) == encryption_key_map_.end()) { return Status(error::INTERNAL_ERROR, @@ -179,7 +213,23 @@ Status WidevineEncryptionKeySource::GetKey(TrackType track_type, return Status::OK; } -Status WidevineEncryptionKeySource::GetCryptoPeriodKey( +Status WidevineKeySource::GetKey(const std::vector& key_id, + EncryptionKey* key) { + DCHECK(key); + for (std::map::iterator iter = + encryption_key_map_.begin(); + iter != encryption_key_map_.end(); + ++iter) { + if (iter->second->key_id == key_id) { + *key = *iter->second; + return Status::OK; + } + } + return Status(error::INTERNAL_ERROR, + "Cannot find key with specified key ID"); +} + +Status WidevineKeySource::GetCryptoPeriodKey( uint32 crypto_period_index, TrackType track_type, EncryptionKey* key) { @@ -201,12 +251,12 @@ Status WidevineEncryptionKeySource::GetCryptoPeriodKey( return GetKeyInternal(crypto_period_index, track_type, key); } -void WidevineEncryptionKeySource::set_http_fetcher( +void WidevineKeySource::set_http_fetcher( scoped_ptr http_fetcher) { http_fetcher_ = http_fetcher.Pass(); } -Status WidevineEncryptionKeySource::GetKeyInternal( +Status WidevineKeySource::GetKeyInternal( uint32 crypto_period_index, TrackType track_type, EncryptionKey* key) { @@ -236,25 +286,31 @@ Status WidevineEncryptionKeySource::GetKeyInternal( return Status::OK; } -void WidevineEncryptionKeySource::FetchKeysTask() { +void WidevineKeySource::FetchKeysTask() { // Wait until key production is signaled. start_key_production_.Wait(); if (!key_pool_ || key_pool_->Stopped()) return; - Status status = FetchKeys(kEnableKeyRotation, first_crypto_period_index_); + Status status = FetchKeysInternal(kEnableKeyRotation, + first_crypto_period_index_, + false); while (status.ok()) { first_crypto_period_index_ += crypto_period_count_; - status = FetchKeys(kEnableKeyRotation, first_crypto_period_index_); + status = FetchKeysInternal(kEnableKeyRotation, + first_crypto_period_index_, + false); } common_encryption_request_status_ = status; key_pool_->Stop(); } -Status WidevineEncryptionKeySource::FetchKeys( - bool enable_key_rotation, uint32 first_crypto_period_index) { +Status WidevineKeySource::FetchKeysInternal(bool enable_key_rotation, + uint32 first_crypto_period_index, + bool widevine_classic) { std::string request; - FillRequest(content_id_, enable_key_rotation, first_crypto_period_index, + FillRequest(enable_key_rotation, + first_crypto_period_index, &request); std::string message; @@ -280,7 +336,10 @@ Status WidevineEncryptionKeySource::FetchKeys( } bool transient_error = false; - if (ExtractEncryptionKey(enable_key_rotation, response, &transient_error)) + if (ExtractEncryptionKey(enable_key_rotation, + widevine_classic, + response, + &transient_error)) return Status::OK; if (!transient_error) { @@ -303,18 +362,11 @@ Status WidevineEncryptionKeySource::FetchKeys( "Failed to recover from server internal error."); } -void WidevineEncryptionKeySource::FillRequest(const std::string& content_id, - bool enable_key_rotation, - uint32 first_crypto_period_index, - std::string* request) { +void WidevineKeySource::FillRequest(bool enable_key_rotation, + uint32 first_crypto_period_index, + std::string* request) { DCHECK(request); - - std::string content_id_base64_string; - base::Base64Encode(content_id, &content_id_base64_string); - - base::DictionaryValue request_dict; - request_dict.SetString("content_id", content_id_base64_string); - request_dict.SetString("policy", policy_); + DCHECK(!request_dict_.empty()); // Build tracks. base::ListValue* tracks = new base::ListValue(); @@ -329,25 +381,25 @@ void WidevineEncryptionKeySource::FillRequest(const std::string& content_id, track_audio->SetString("type", "AUDIO"); tracks->Append(track_audio); - request_dict.Set("tracks", tracks); + request_dict_.Set("tracks", tracks); // Build DRM types. base::ListValue* drm_types = new base::ListValue(); drm_types->AppendString("WIDEVINE"); - request_dict.Set("drm_types", drm_types); + request_dict_.Set("drm_types", drm_types); // Build key rotation fields. if (enable_key_rotation) { - request_dict.SetInteger("first_crypto_period_index", + request_dict_.SetInteger("first_crypto_period_index", first_crypto_period_index); - request_dict.SetInteger("crypto_period_count", crypto_period_count_); + request_dict_.SetInteger("crypto_period_count", crypto_period_count_); } - base::JSONWriter::Write(&request_dict, request); + base::JSONWriter::Write(&request_dict_, request); } -Status WidevineEncryptionKeySource::SignRequest(const std::string& request, - std::string* signed_request) { +Status WidevineKeySource::SignRequest(const std::string& request, + std::string* signed_request) { DCHECK(signed_request); // Sign the request. @@ -371,7 +423,7 @@ Status WidevineEncryptionKeySource::SignRequest(const std::string& request, return Status::OK; } -bool WidevineEncryptionKeySource::DecodeResponse( +bool WidevineKeySource::DecodeResponse( const std::string& raw_response, std::string* response) { DCHECK(response); @@ -391,8 +443,9 @@ bool WidevineEncryptionKeySource::DecodeResponse( return true; } -bool WidevineEncryptionKeySource::ExtractEncryptionKey( +bool WidevineKeySource::ExtractEncryptionKey( bool enable_key_rotation, + bool widevine_classic, const std::string& response, bool* transient_error) { DCHECK(transient_error); @@ -453,12 +506,20 @@ bool WidevineEncryptionKeySource::ExtractEncryptionKey( RCHECK(encryption_key_map.find(track_type) == encryption_key_map.end()); scoped_ptr encryption_key(new EncryptionKey()); - std::vector pssh_data; - if (!GetKeyAndKeyId( - *track_dict, &encryption_key->key, &encryption_key->key_id) || - !GetPsshData(*track_dict, &pssh_data)) + + if (!GetKeyFromTrack(*track_dict, &encryption_key->key)) return false; - encryption_key->pssh = PsshBoxFromPsshData(pssh_data); + + // Get key ID and PSSH data for CENC content only. + if (!widevine_classic) { + if (!GetKeyIdFromTrack(*track_dict, &encryption_key->key_id)) + return false; + + std::vector pssh_data; + if (!GetPsshDataFromTrack(*track_dict, &pssh_data)) + return false; + encryption_key->pssh = PsshBoxFromPsshData(pssh_data); + } encryption_key_map[track_type] = encryption_key.release(); } @@ -470,7 +531,7 @@ bool WidevineEncryptionKeySource::ExtractEncryptionKey( return PushToKeyPool(&encryption_key_map); } -bool WidevineEncryptionKeySource::PushToKeyPool( +bool WidevineKeySource::PushToKeyPool( EncryptionKeyMap* encryption_key_map) { DCHECK(key_pool_); DCHECK(encryption_key_map); diff --git a/media/base/widevine_encryption_key_source.h b/media/base/widevine_key_source.h similarity index 70% rename from media/base/widevine_encryption_key_source.h rename to media/base/widevine_key_source.h index 8c0f4b2677..d7fd5161fa 100644 --- a/media/base/widevine_encryption_key_source.h +++ b/media/base/widevine_key_source.h @@ -4,44 +4,43 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -#ifndef MEDIA_BASE_WIDEVINE_ENCRYPTION_KEY_SOURCE_H_ -#define MEDIA_BASE_WIDEVINE_ENCRYPTION_KEY_SOURCE_H_ +#ifndef MEDIA_BASE_WIDEVINE_KEY_SOURCE_H_ +#define MEDIA_BASE_WIDEVINE_KEY_SOURCE_H_ #include -#include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "base/synchronization/waitable_event.h" +#include "base/values.h" #include "media/base/closure_thread.h" -#include "media/base/encryption_key_source.h" +#include "media/base/key_source.h" namespace media { class HttpFetcher; class RequestSigner; template class ProducerConsumerQueue; -/// WidevineEncryptionKeySource talks to the Widevine encryption service to +/// WidevineKeySource talks to the Widevine encryption service to /// acquire the encryption keys. -class WidevineEncryptionKeySource : public EncryptionKeySource { +class WidevineKeySource : public KeySource { public: /// @param server_url is the Widevine common encryption server url. - /// @param content_id the unique id identify the content to be encrypted. - /// @param policy specifies the DRM content rights. /// @param signer signs the request message. It should not be NULL. - WidevineEncryptionKeySource(const std::string& server_url, - const std::string& content_id, - const std::string& policy, - scoped_ptr signer); - virtual ~WidevineEncryptionKeySource(); + WidevineKeySource(const std::string& server_url, + scoped_ptr signer); - /// Initialize the key source. Must be called before calling GetKey or - /// GetCryptoPeriodKey. - /// @return OK on success, an error status otherwise. - Status Initialize(); + virtual ~WidevineKeySource(); - /// @name EncryptionKeySource implementation overrides. + /// @name KeySource implementation overrides. /// @{ + virtual Status FetchKeys(const std::vector& content_id, + const std::string& policy) OVERRIDE; + virtual Status FetchKeys(const std::vector& pssh_data) OVERRIDE; + Status FetchKeys(uint32 asset_id); + virtual Status GetKey(TrackType track_type, EncryptionKey* key) OVERRIDE; + virtual Status GetKey(const std::vector& key_id, + EncryptionKey* key) OVERRIDE; virtual Status GetCryptoPeriodKey(uint32 crypto_period_index, TrackType track_type, EncryptionKey* key) OVERRIDE; @@ -62,16 +61,20 @@ class WidevineEncryptionKeySource : public EncryptionKeySource { TrackType track_type, EncryptionKey* key); + // Common implementation of FetchKeys methods above. + Status FetchKeysCommon(bool widevine_classic); + // The closure task to fetch keys repeatedly. void FetchKeysTask(); // Fetch keys from server. - Status FetchKeys(bool enable_key_rotation, uint32 first_crypto_period_index); + Status FetchKeysInternal(bool enable_key_rotation, + uint32 first_crypto_period_index, + bool widevine_classic); // Fill |request| with necessary fields for Widevine encryption request. // |request| should not be NULL. - void FillRequest(const std::string& content_id, - bool enable_key_rotation, + void FillRequest(bool enable_key_rotation, uint32 first_crypto_period_index, std::string* request); // Sign and properly format |request|. @@ -85,7 +88,9 @@ class WidevineEncryptionKeySource : public EncryptionKeySource { // failure is because of a transient error from the server. |transient_error| // should not be NULL. bool ExtractEncryptionKey(bool enable_key_rotation, - const std::string& response, bool* transient_error); + bool widevine_classic, + const std::string& response, + bool* transient_error); // Push the keys to the key pool. bool PushToKeyPool(EncryptionKeyMap* encryption_key_map); @@ -94,9 +99,8 @@ class WidevineEncryptionKeySource : public EncryptionKeySource { // Can be overridden using set_http_fetcher for testing or other purposes. scoped_ptr http_fetcher_; std::string server_url_; - std::string content_id_; - std::string policy_; scoped_ptr signer_; + base::DictionaryValue request_dict_; const uint32 crypto_period_count_; base::Lock lock_; @@ -108,9 +112,9 @@ class WidevineEncryptionKeySource : public EncryptionKeySource { EncryptionKeyMap encryption_key_map_; // For non key rotation request. Status common_encryption_request_status_; - DISALLOW_COPY_AND_ASSIGN(WidevineEncryptionKeySource); + DISALLOW_COPY_AND_ASSIGN(WidevineKeySource); }; } // namespace media -#endif // MEDIA_BASE_WIDEVINE_ENCRYPTION_KEY_SOURCE_H_ +#endif // MEDIA_BASE_WIDEVINE_KEY_SOURCE_H_ diff --git a/media/base/widevine_encryption_key_source_unittest.cc b/media/base/widevine_key_source_unittest.cc similarity index 67% rename from media/base/widevine_encryption_key_source_unittest.cc rename to media/base/widevine_key_source_unittest.cc index 0435b997d2..1b5a0cc7d3 100644 --- a/media/base/widevine_encryption_key_source_unittest.cc +++ b/media/base/widevine_key_source_unittest.cc @@ -13,7 +13,7 @@ #include "media/base/http_fetcher.h" #include "media/base/request_signer.h" #include "media/base/status_test_util.h" -#include "media/base/widevine_encryption_key_source.h" +#include "media/base/widevine_key_source.h" namespace { const char kServerUrl[] = "http://www.foo.com/getcontentkey"; @@ -25,9 +25,9 @@ const char kMockSignature[] = "MockSignature"; // The license service may return an error indicating a transient error has // just happened in the server, or other types of errors. -// WidevineEncryptionKeySource will perform a number of retries on transient +// WidevineKeySource will perform a number of retries on transient // errors; -// WidevineEncryptionKeySource does not know about other errors and retries are +// WidevineKeySource does not know about other errors and retries are // not performed. const char kLicenseStatusTransientError[] = "INTERNAL_ERROR"; const char kLicenseStatusUnknownError[] = "UNKNOWN_ERROR"; @@ -40,8 +40,11 @@ const char kExpectedSignedMessageFormat[] = const char kTrackFormat[] = "{\"type\":\"%s\",\"key_id\":\"%s\",\"key\":" "\"%s\",\"pssh\":[{\"drm_type\":\"WIDEVINE\",\"data\":\"%s\"}]}"; +const char kClassicTrackFormat[] = "{\"type\":\"%s\",\"key\":\"%s\"}"; const char kLicenseResponseFormat[] = "{\"status\":\"%s\",\"tracks\":[%s]}"; const char kHttpResponseFormat[] = "{\"response\":\"%s\"}"; +const char kRequestPsshData[] = "PSSH data"; +const uint32 kClassicAssetId = 1234; std::string Base64Encode(const std::string& input) { std::string output; @@ -81,6 +84,20 @@ std::string GenerateMockLicenseResponse() { return base::StringPrintf(kLicenseResponseFormat, "OK", tracks.c_str()); } +std::string GenerateMockClassicLicenseResponse() { + const std::string kTrackTypes[] = {"SD", "HD", "AUDIO"}; + std::string tracks; + for (size_t i = 0; i < 3; ++i) { + if (!tracks.empty()) + tracks += ","; + tracks += base::StringPrintf( + kClassicTrackFormat, + kTrackTypes[i].c_str(), + Base64Encode(GetMockKey(kTrackTypes[i])).c_str()); + } + return base::StringPrintf(kLicenseResponseFormat, "OK", tracks.c_str()); +} + std::string GetPsshDataFromPsshBox(const std::string& pssh_box) { const size_t kPsshDataOffset = 32u; DCHECK_LT(kPsshDataOffset, pssh_box.size()); @@ -125,68 +142,75 @@ class MockHttpFetcher : public HttpFetcher { DISALLOW_COPY_AND_ASSIGN(MockHttpFetcher); }; -class WidevineEncryptionKeySourceTest : public ::testing::Test { +class WidevineKeySourceTest : public ::testing::Test { public: - WidevineEncryptionKeySourceTest() + WidevineKeySourceTest() : mock_request_signer_(new MockRequestSigner(kSignerName)), mock_http_fetcher_(new MockHttpFetcher()) {} + void SetUp() OVERRIDE { + content_id_.assign(reinterpret_cast(kContentId), + reinterpret_cast(kContentId) + + strlen(kContentId)); + } + protected: - void CreateWidevineEncryptionKeySource() { - widevine_encryption_key_source_.reset(new WidevineEncryptionKeySource( + void CreateWidevineKeySource() { + widevine_key_source_.reset(new WidevineKeySource( kServerUrl, - kContentId, - kPolicy, mock_request_signer_.PassAs())); - widevine_encryption_key_source_->set_http_fetcher( + widevine_key_source_->set_http_fetcher( mock_http_fetcher_.PassAs()); } - void VerifyKeys() { + void VerifyKeys(bool classic) { EncryptionKey encryption_key; const std::string kTrackTypes[] = {"SD", "HD", "AUDIO"}; for (size_t i = 0; i < arraysize(kTrackTypes); ++i) { - ASSERT_OK(widevine_encryption_key_source_->GetKey( - EncryptionKeySource::GetTrackTypeFromString(kTrackTypes[i]), + ASSERT_OK(widevine_key_source_->GetKey( + KeySource::GetTrackTypeFromString(kTrackTypes[i]), &encryption_key)); - EXPECT_EQ(GetMockKeyId(kTrackTypes[i]), ToString(encryption_key.key_id)); EXPECT_EQ(GetMockKey(kTrackTypes[i]), ToString(encryption_key.key)); - EXPECT_EQ(GetMockPsshData(kTrackTypes[i]), - GetPsshDataFromPsshBox(ToString(encryption_key.pssh))); + if (!classic) { + EXPECT_EQ(GetMockKeyId(kTrackTypes[i]), + ToString(encryption_key.key_id)); + EXPECT_EQ(GetMockPsshData(kTrackTypes[i]), + GetPsshDataFromPsshBox(ToString(encryption_key.pssh))); + } } } - scoped_ptr mock_request_signer_; scoped_ptr mock_http_fetcher_; - scoped_ptr widevine_encryption_key_source_; + scoped_ptr widevine_key_source_; + std::vector content_id_; private: - DISALLOW_COPY_AND_ASSIGN(WidevineEncryptionKeySourceTest); + DISALLOW_COPY_AND_ASSIGN(WidevineKeySourceTest); }; -TEST_F(WidevineEncryptionKeySourceTest, GetTrackTypeFromString) { - EXPECT_EQ(EncryptionKeySource::TRACK_TYPE_SD, - EncryptionKeySource::GetTrackTypeFromString("SD")); - EXPECT_EQ(EncryptionKeySource::TRACK_TYPE_HD, - EncryptionKeySource::GetTrackTypeFromString("HD")); - EXPECT_EQ(EncryptionKeySource::TRACK_TYPE_AUDIO, - EncryptionKeySource::GetTrackTypeFromString("AUDIO")); - EXPECT_EQ(EncryptionKeySource::TRACK_TYPE_UNKNOWN, - EncryptionKeySource::GetTrackTypeFromString("FOO")); +TEST_F(WidevineKeySourceTest, GetTrackTypeFromString) { + EXPECT_EQ(KeySource::TRACK_TYPE_SD, + KeySource::GetTrackTypeFromString("SD")); + EXPECT_EQ(KeySource::TRACK_TYPE_HD, + KeySource::GetTrackTypeFromString("HD")); + EXPECT_EQ(KeySource::TRACK_TYPE_AUDIO, + KeySource::GetTrackTypeFromString("AUDIO")); + EXPECT_EQ(KeySource::TRACK_TYPE_UNKNOWN, + KeySource::GetTrackTypeFromString("FOO")); } -TEST_F(WidevineEncryptionKeySourceTest, GenerateSignatureFailure) { +TEST_F(WidevineKeySourceTest, GenerateSignatureFailure) { EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _)) .WillOnce(Return(false)); - CreateWidevineEncryptionKeySource(); + CreateWidevineKeySource(); ASSERT_EQ(Status(error::INTERNAL_ERROR, "Signature generation failed."), - widevine_encryption_key_source_->Initialize()); + widevine_key_source_->FetchKeys(content_id_, kPolicy)); } // Check whether expected request message and post data was generated and // verify the correct behavior on http failure. -TEST_F(WidevineEncryptionKeySourceTest, HttpPostFailure) { +TEST_F(WidevineKeySourceTest, HttpPostFailure) { std::string expected_message = base::StringPrintf( kExpectedRequestMessageFormat, Base64Encode(kContentId).c_str(), kPolicy); EXPECT_CALL(*mock_request_signer_, GenerateSignature(expected_message, _)) @@ -201,12 +225,12 @@ TEST_F(WidevineEncryptionKeySourceTest, HttpPostFailure) { EXPECT_CALL(*mock_http_fetcher_, Post(kServerUrl, expected_post_data, _)) .WillOnce(Return(kMockStatus)); - CreateWidevineEncryptionKeySource(); + CreateWidevineKeySource(); ASSERT_EQ(kMockStatus, - widevine_encryption_key_source_->Initialize()); + widevine_key_source_->FetchKeys(content_id_, kPolicy)); } -TEST_F(WidevineEncryptionKeySourceTest, LicenseStatusOK) { +TEST_F(WidevineKeySourceTest, LicenseStatusCencOK) { EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _)) .WillOnce(Return(true)); @@ -216,12 +240,63 @@ TEST_F(WidevineEncryptionKeySourceTest, LicenseStatusOK) { EXPECT_CALL(*mock_http_fetcher_, Post(_, _, _)) .WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK))); - CreateWidevineEncryptionKeySource(); - ASSERT_OK(widevine_encryption_key_source_->Initialize()); - VerifyKeys(); + CreateWidevineKeySource(); + ASSERT_OK(widevine_key_source_->FetchKeys(content_id_, kPolicy)); + VerifyKeys(false); } -TEST_F(WidevineEncryptionKeySourceTest, RetryOnHttpTimeout) { +TEST_F(WidevineKeySourceTest, LicenseStatusCencNotOK) { + EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _)) + .WillOnce(Return(true)); + + std::string mock_response = base::StringPrintf( + kHttpResponseFormat, Base64Encode( + GenerateMockClassicLicenseResponse()).c_str()); + + EXPECT_CALL(*mock_http_fetcher_, Post(_, _, _)) + .WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK))); + + CreateWidevineKeySource(); + ASSERT_EQ(error::SERVER_ERROR, + widevine_key_source_->FetchKeys(content_id_, kPolicy) + .error_code()); +} + +TEST_F(WidevineKeySourceTest, LicenseStatusCencWithPsshDataOK) { + EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _)) + .WillOnce(Return(true)); + + std::string mock_response = base::StringPrintf( + kHttpResponseFormat, Base64Encode(GenerateMockLicenseResponse()).c_str()); + + EXPECT_CALL(*mock_http_fetcher_, Post(_, _, _)) + .WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK))); + + CreateWidevineKeySource(); + std::vector pssh_data( + reinterpret_cast(kRequestPsshData), + reinterpret_cast(kRequestPsshData) + strlen(kContentId)); + ASSERT_OK(widevine_key_source_->FetchKeys(pssh_data)); + VerifyKeys(false); +} + +TEST_F(WidevineKeySourceTest, LicenseStatusClassicOK) { + EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _)) + .WillOnce(Return(true)); + + std::string mock_response = base::StringPrintf( + kHttpResponseFormat, Base64Encode( + GenerateMockClassicLicenseResponse()).c_str()); + + EXPECT_CALL(*mock_http_fetcher_, Post(_, _, _)) + .WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK))); + + CreateWidevineKeySource(); + ASSERT_OK(widevine_key_source_->FetchKeys(kClassicAssetId)); + VerifyKeys(true); +} + +TEST_F(WidevineKeySourceTest, RetryOnHttpTimeout) { EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _)) .WillOnce(Return(true)); @@ -233,12 +308,12 @@ TEST_F(WidevineEncryptionKeySourceTest, RetryOnHttpTimeout) { .WillOnce(Return(Status(error::TIME_OUT, ""))) .WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK))); - CreateWidevineEncryptionKeySource(); - ASSERT_OK(widevine_encryption_key_source_->Initialize()); - VerifyKeys(); + CreateWidevineKeySource(); + ASSERT_OK(widevine_key_source_->FetchKeys(content_id_, kPolicy)); + VerifyKeys(false); } -TEST_F(WidevineEncryptionKeySourceTest, RetryOnTransientError) { +TEST_F(WidevineKeySourceTest, RetryOnTransientError) { EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _)) .WillOnce(Return(true)); @@ -256,12 +331,12 @@ TEST_F(WidevineEncryptionKeySourceTest, RetryOnTransientError) { .WillOnce(DoAll(SetArgPointee<2>(expected_retried_response), Return(Status::OK))); - CreateWidevineEncryptionKeySource(); - ASSERT_OK(widevine_encryption_key_source_->Initialize()); - VerifyKeys(); + CreateWidevineKeySource(); + ASSERT_OK(widevine_key_source_->FetchKeys(content_id_, kPolicy)); + VerifyKeys(false); } -TEST_F(WidevineEncryptionKeySourceTest, NoRetryOnUnknownError) { +TEST_F(WidevineKeySourceTest, NoRetryOnUnknownError) { EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _)) .WillOnce(Return(true)); @@ -273,9 +348,9 @@ TEST_F(WidevineEncryptionKeySourceTest, NoRetryOnUnknownError) { EXPECT_CALL(*mock_http_fetcher_, Post(_, _, _)) .WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK))); - CreateWidevineEncryptionKeySource(); + CreateWidevineKeySource(); ASSERT_EQ(error::SERVER_ERROR, - widevine_encryption_key_source_->Initialize().error_code()); + widevine_key_source_->FetchKeys(content_id_, kPolicy).error_code()); } namespace { @@ -316,7 +391,7 @@ std::string GenerateMockKeyRotationLicenseResponse( } // namespace -TEST_F(WidevineEncryptionKeySourceTest, KeyRotationTest) { +TEST_F(WidevineKeySourceTest, KeyRotationTest) { const uint32 kFirstCryptoPeriodIndex = 8; const uint32 kCryptoPeriodCount = 10; // Array of indexes to be checked. @@ -328,7 +403,7 @@ TEST_F(WidevineEncryptionKeySourceTest, KeyRotationTest) { // Generate expectations in sequence. InSequence dummy; - // Expecting a non-key rotation enabled request on Initialize(). + // Expecting a non-key rotation enabled request on FetchKeys(). EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _)) .WillOnce(Return(true)); std::string mock_response = base::StringPrintf( @@ -357,16 +432,16 @@ TEST_F(WidevineEncryptionKeySourceTest, KeyRotationTest) { .WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK))); } - CreateWidevineEncryptionKeySource(); - ASSERT_OK(widevine_encryption_key_source_->Initialize()); + CreateWidevineKeySource(); + ASSERT_OK(widevine_key_source_->FetchKeys(content_id_, kPolicy)); EncryptionKey encryption_key; for (size_t i = 0; i < arraysize(kCryptoPeriodIndexes); ++i) { const std::string kTrackTypes[] = {"SD", "HD", "AUDIO"}; for (size_t j = 0; j < 3; ++j) { - ASSERT_OK(widevine_encryption_key_source_->GetCryptoPeriodKey( + ASSERT_OK(widevine_key_source_->GetCryptoPeriodKey( kCryptoPeriodIndexes[i], - EncryptionKeySource::GetTrackTypeFromString(kTrackTypes[j]), + KeySource::GetTrackTypeFromString(kTrackTypes[j]), &encryption_key)); EXPECT_EQ(GetMockKey(kTrackTypes[j], kCryptoPeriodIndexes[i]), ToString(encryption_key.key)); @@ -374,9 +449,9 @@ TEST_F(WidevineEncryptionKeySourceTest, KeyRotationTest) { } // The old crypto period indexes should have been garbage collected. - Status status = widevine_encryption_key_source_->GetCryptoPeriodKey( + Status status = widevine_key_source_->GetCryptoPeriodKey( kFirstCryptoPeriodIndex, - EncryptionKeySource::TRACK_TYPE_SD, + KeySource::TRACK_TYPE_SD, &encryption_key); EXPECT_EQ(error::INVALID_ARGUMENT, status.error_code()); } diff --git a/media/formats/mp4/encrypting_fragmenter.cc b/media/formats/mp4/encrypting_fragmenter.cc index 3e283ca5ba..c6e0615871 100644 --- a/media/formats/mp4/encrypting_fragmenter.cc +++ b/media/formats/mp4/encrypting_fragmenter.cc @@ -8,7 +8,7 @@ #include "media/base/aes_encryptor.h" #include "media/base/buffer_reader.h" -#include "media/base/encryption_key_source.h" +#include "media/base/key_source.h" #include "media/base/media_sample.h" #include "media/formats/mp4/box_definitions.h" #include "media/formats/mp4/cenc.h" diff --git a/media/formats/mp4/key_rotation_fragmenter.cc b/media/formats/mp4/key_rotation_fragmenter.cc index dfb603a1e1..c1c6ce5482 100644 --- a/media/formats/mp4/key_rotation_fragmenter.cc +++ b/media/formats/mp4/key_rotation_fragmenter.cc @@ -15,8 +15,8 @@ namespace mp4 { KeyRotationFragmenter::KeyRotationFragmenter( MovieFragment* moof, TrackFragment* traf, - EncryptionKeySource* encryption_key_source, - EncryptionKeySource::TrackType track_type, + KeySource* encryption_key_source, + KeySource::TrackType track_type, int64 crypto_period_duration, int64 clear_time, uint8 nalu_length_size) diff --git a/media/formats/mp4/key_rotation_fragmenter.h b/media/formats/mp4/key_rotation_fragmenter.h index c29907aaef..be14910a21 100644 --- a/media/formats/mp4/key_rotation_fragmenter.h +++ b/media/formats/mp4/key_rotation_fragmenter.h @@ -7,7 +7,7 @@ #ifndef MEDIA_FORMATS_MP4_KEY_ROTATION_FRAGMENTER_H_ #define MEDIA_FORMATS_MP4_KEY_ROTATION_FRAGMENTER_H_ -#include "media/base/encryption_key_source.h" +#include "media/base/key_source.h" #include "media/formats/mp4/encrypting_fragmenter.h" namespace media { @@ -33,8 +33,8 @@ class KeyRotationFragmenter : public EncryptingFragmenter { /// encryption. KeyRotationFragmenter(MovieFragment* moof, TrackFragment* traf, - EncryptionKeySource* encryption_key_source, - EncryptionKeySource::TrackType track_type, + KeySource* encryption_key_source, + KeySource::TrackType track_type, int64 crypto_period_duration, int64 clear_time, uint8 nalu_length_size); @@ -50,8 +50,8 @@ class KeyRotationFragmenter : public EncryptingFragmenter { private: MovieFragment* moof_; - EncryptionKeySource* encryption_key_source_; - EncryptionKeySource::TrackType track_type_; + KeySource* encryption_key_source_; + KeySource::TrackType track_type_; const int64 crypto_period_duration_; size_t prev_crypto_period_index_; diff --git a/media/formats/mp4/mp4_muxer.cc b/media/formats/mp4/mp4_muxer.cc index 8c6cf4f754..3d2cdb690c 100644 --- a/media/formats/mp4/mp4_muxer.cc +++ b/media/formats/mp4/mp4_muxer.cc @@ -10,7 +10,7 @@ #include "base/time/time.h" #include "media/base/aes_encryptor.h" #include "media/base/audio_stream_info.h" -#include "media/base/encryption_key_source.h" +#include "media/base/key_source.h" #include "media/base/media_sample.h" #include "media/base/media_stream.h" #include "media/base/video_stream_info.h" diff --git a/media/formats/mp4/segmenter.cc b/media/formats/mp4/segmenter.cc index c225714bae..1bafd359ea 100644 --- a/media/formats/mp4/segmenter.cc +++ b/media/formats/mp4/segmenter.cc @@ -10,7 +10,7 @@ #include "base/stl_util.h" #include "media/base/buffer_writer.h" -#include "media/base/encryption_key_source.h" +#include "media/base/key_source.h" #include "media/base/media_sample.h" #include "media/base/media_stream.h" #include "media/base/muxer_options.h" @@ -94,17 +94,17 @@ uint8 GetNaluLengthSize(const StreamInfo& stream_info) { return video_stream_info.nalu_length_size(); } -EncryptionKeySource::TrackType GetTrackTypeForEncryption( +KeySource::TrackType GetTrackTypeForEncryption( const StreamInfo& stream_info, uint32 max_sd_pixels) { if (stream_info.stream_type() == kStreamAudio) - return EncryptionKeySource::TRACK_TYPE_AUDIO; + return KeySource::TRACK_TYPE_AUDIO; DCHECK_EQ(kStreamVideo, stream_info.stream_type()); const VideoStreamInfo& video_stream_info = static_cast(stream_info); uint32 pixels = video_stream_info.width() * video_stream_info.height(); - return (pixels > max_sd_pixels) ? EncryptionKeySource::TRACK_TYPE_HD - : EncryptionKeySource::TRACK_TYPE_SD; + return (pixels > max_sd_pixels) ? KeySource::TRACK_TYPE_HD + : KeySource::TRACK_TYPE_SD; } } // namespace @@ -126,7 +126,7 @@ Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); } Status Segmenter::Initialize(const std::vector& streams, event::MuxerListener* muxer_listener, - EncryptionKeySource* encryption_key_source, + KeySource* encryption_key_source, uint32 max_sd_pixels, double clear_lead_in_seconds, double crypto_period_duration_in_seconds) { @@ -151,7 +151,7 @@ Status Segmenter::Initialize(const std::vector& streams, } uint8 nalu_length_size = GetNaluLengthSize(*streams[i]->info()); - EncryptionKeySource::TrackType track_type = + KeySource::TrackType track_type = GetTrackTypeForEncryption(*streams[i]->info(), max_sd_pixels); SampleDescription& description = moov_->tracks[i].media.information.sample_table.description; diff --git a/media/formats/mp4/segmenter.h b/media/formats/mp4/segmenter.h index 04e41b5d7f..d4eac1b66e 100644 --- a/media/formats/mp4/segmenter.h +++ b/media/formats/mp4/segmenter.h @@ -19,7 +19,7 @@ namespace media { struct MuxerOptions; class BufferWriter; -class EncryptionKeySource; +class KeySource; class MediaSample; class MediaStream; @@ -64,7 +64,7 @@ class Segmenter { /// @return OK on success, an error status otherwise. Status Initialize(const std::vector& streams, event::MuxerListener* muxer_listener, - EncryptionKeySource* encryption_key_source, + KeySource* encryption_key_source, uint32 max_sd_pixels, double clear_lead_in_seconds, double crypto_period_duration_in_seconds); diff --git a/media/test/packager_test.cc b/media/test/packager_test.cc index 691596bf44..335b699122 100644 --- a/media/test/packager_test.cc +++ b/media/test/packager_test.cc @@ -11,7 +11,7 @@ #include "base/strings/stringprintf.h" #include "base/time/clock.h" #include "media/base/demuxer.h" -#include "media/base/encryption_key_source.h" +#include "media/base/key_source.h" #include "media/base/media_stream.h" #include "media/base/muxer.h" #include "media/base/status_test_util.h" @@ -151,8 +151,8 @@ void PackagerTestBasic::Remux(const std::string& input, Demuxer demuxer(GetFullPath(input), decryptor_source_); ASSERT_OK(demuxer.Initialize()); - scoped_ptr encryption_key_source( - EncryptionKeySource::CreateFromHexStrings( + scoped_ptr encryption_key_source( + KeySource::CreateFromHexStrings( kKeyIdHex, kKeyHex, kPsshHex, "")); DCHECK(encryption_key_source); @@ -165,10 +165,10 @@ void PackagerTestBasic::Remux(const std::string& input, muxer_video->AddStream(FindFirstVideoStream(demuxer.streams())); if (enable_encryption) { - muxer_video->SetEncryptionKeySource(encryption_key_source.get(), - EncryptionKeySource::TRACK_TYPE_SD, - kClearLeadInSeconds, - kCryptoDurationInSeconds); + muxer_video->SetKeySource(encryption_key_source.get(), + KeySource::TRACK_TYPE_SD, + kClearLeadInSeconds, + kCryptoDurationInSeconds); } } @@ -181,8 +181,8 @@ void PackagerTestBasic::Remux(const std::string& input, muxer_audio->AddStream(FindFirstAudioStream(demuxer.streams())); if (enable_encryption) { - muxer_audio->SetEncryptionKeySource(encryption_key_source.get(), - EncryptionKeySource::TRACK_TYPE_SD, + muxer_audio->SetKeySource(encryption_key_source.get(), + KeySource::TRACK_TYPE_SD, kClearLeadInSeconds, kCryptoDurationInSeconds); }