From c7c201b00c5275c32745b6628b72ccc2f9d2bd43 Mon Sep 17 00:00:00 2001 From: KongQun Yang Date: Wed, 5 Jul 2017 16:47:55 -0700 Subject: [PATCH] Consolidate EncryptionParam and EncryptionOptions Change-Id: I8a5b5c329d4731341a3184d50bed061aa00cdeda --- packager/app/packager_main.cc | 26 +- packager/app/packager_util.cc | 30 --- packager/app/packager_util.h | 4 - packager/media/crypto/encryption_handler.cc | 48 ++-- packager/media/crypto/encryption_handler.h | 25 +- .../crypto/encryption_handler_unittest.cc | 54 ++-- packager/media/public/crypto_params.h | 211 +++++++++++++++ packager/packager.cc | 66 ++--- packager/packager.h | 248 ++---------------- 9 files changed, 352 insertions(+), 360 deletions(-) create mode 100644 packager/media/public/crypto_params.h diff --git a/packager/app/packager_main.cc b/packager/app/packager_main.cc index 860a1fa473..fa7e034322 100644 --- a/packager/app/packager_main.cc +++ b/packager/app/packager_main.cc @@ -134,6 +134,27 @@ bool GetHlsPlaylistType(const std::string& playlist_type, return true; } +bool GetProtectionScheme(uint32_t* protection_scheme) { + if (FLAGS_protection_scheme == "cenc") { + *protection_scheme = EncryptionParams::kProtectionSchemeCenc; + return true; + } + if (FLAGS_protection_scheme == "cbc1") { + *protection_scheme = EncryptionParams::kProtectionSchemeCbc1; + return true; + } + if (FLAGS_protection_scheme == "cbcs") { + *protection_scheme = EncryptionParams::kProtectionSchemeCbcs; + return true; + } + if (FLAGS_protection_scheme == "cens") { + *protection_scheme = EncryptionParams::kProtectionSchemeCens; + return true; + } + LOG(ERROR) << "Unrecognized protection_scheme " << FLAGS_protection_scheme; + return false; +} + base::Optional GetPackagingParams() { PackagingParams packaging_params; @@ -166,12 +187,13 @@ base::Optional GetPackagingParams() { if (encryption_params.key_provider != KeyProvider::kNone) { encryption_params.clear_lead_in_seconds = FLAGS_clear_lead; - encryption_params.protection_scheme = FLAGS_protection_scheme; + if (!GetProtectionScheme(&encryption_params.protection_scheme)) + return base::nullopt; encryption_params.crypto_period_duration_in_seconds = FLAGS_crypto_period_duration; encryption_params.vp9_subsample_encryption = FLAGS_vp9_subsample_encryption; encryption_params.stream_label_func = std::bind( - &EncryptionParams::DefaultStreamLabelFunction, FLAGS_max_sd_pixels, + &Packager::DefaultStreamLabelFunction, FLAGS_max_sd_pixels, FLAGS_max_hd_pixels, FLAGS_max_uhd1_pixels, std::placeholders::_1); } switch (encryption_params.key_provider) { diff --git a/packager/app/packager_util.cc b/packager/app/packager_util.cc index 99a54f376c..e3c6956ec0 100644 --- a/packager/app/packager_util.cc +++ b/packager/app/packager_util.cc @@ -26,21 +26,6 @@ namespace shaka { namespace media { namespace { -FourCC GetProtectionScheme(const std::string& protection_scheme) { - if (protection_scheme == "cenc") { - return FOURCC_cenc; - } else if (protection_scheme == "cens") { - return FOURCC_cens; - } else if (protection_scheme == "cbc1") { - return FOURCC_cbc1; - } else if (protection_scheme == "cbcs") { - return FOURCC_cbcs; - } else { - LOG(ERROR) << "Unknown protection scheme: " << protection_scheme; - return FOURCC_NULL; - } -} - } // namespace std::unique_ptr CreateSigner(const WidevineSigner& signer) { @@ -198,21 +183,6 @@ ChunkingOptions GetChunkingOptions(const ChunkingParams& chunking_params) { return chunking_options; } -EncryptionOptions GetEncryptionOptions( - const EncryptionParams& encryption_params) { - EncryptionOptions encryption_options; - encryption_options.clear_lead_in_seconds = - encryption_params.clear_lead_in_seconds; - encryption_options.protection_scheme = - GetProtectionScheme(encryption_params.protection_scheme); - encryption_options.crypto_period_duration_in_seconds = - encryption_params.crypto_period_duration_in_seconds; - encryption_options.vp9_subsample_encryption = - encryption_params.vp9_subsample_encryption; - encryption_options.stream_label_func = encryption_params.stream_label_func; - return encryption_options; -} - MuxerOptions GetMuxerOptions(const std::string& temp_dir, const Mp4OutputParams& mp4_params) { MuxerOptions muxer_options; diff --git a/packager/app/packager_util.h b/packager/app/packager_util.h index 5b00865a79..b8e495d960 100644 --- a/packager/app/packager_util.h +++ b/packager/app/packager_util.h @@ -53,10 +53,6 @@ std::unique_ptr CreateDecryptionKeySource( /// @return ChunkingOptions from provided command line options. ChunkingOptions GetChunkingOptions(const ChunkingParams& chunking_params); -/// @return EncryptionOptions from provided command line options. -EncryptionOptions GetEncryptionOptions( - const EncryptionParams& encryption_params); - /// @return MuxerOptions from provided command line options. MuxerOptions GetMuxerOptions(const std::string& temp_dir, const Mp4OutputParams& mp4_params); diff --git a/packager/media/crypto/encryption_handler.cc b/packager/media/crypto/encryption_handler.cc index 750ed8764a..a56b211ef4 100644 --- a/packager/media/crypto/encryption_handler.cc +++ b/packager/media/crypto/encryption_handler.cc @@ -31,7 +31,7 @@ const uint8_t kKeyRotationDefaultKeyId[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; -// Adds one or more subsamples to |*subsamples|. This may add more than one +// Adds one or more subsamples to |*decrypt_config|. This may add more than one // if one of the values overflows the integer in the subsample. void AddSubsample(uint64_t clear_bytes, uint64_t cipher_bytes, @@ -77,15 +77,17 @@ std::string GetStreamLabelForEncryption( } } // namespace -EncryptionHandler::EncryptionHandler( - const EncryptionOptions& encryption_options, - KeySource* key_source) - : encryption_options_(encryption_options), key_source_(key_source) {} +EncryptionHandler::EncryptionHandler(const EncryptionParams& encryption_params, + KeySource* key_source) + : encryption_params_(encryption_params), + protection_scheme_( + static_cast(encryption_params.protection_scheme)), + key_source_(key_source) {} EncryptionHandler::~EncryptionHandler() {} Status EncryptionHandler::InitializeInternal() { - if (!encryption_options_.stream_label_func) { + if (!encryption_params_.stream_label_func) { return Status(error::INVALID_ARGUMENT, "Stream label function not set."); } if (num_input_streams() != 1 || next_output_stream_index() != 1) { @@ -134,17 +136,17 @@ Status EncryptionHandler::ProcessStreamInfo(StreamInfo* stream_info) { } remaining_clear_lead_ = - encryption_options_.clear_lead_in_seconds * stream_info->time_scale(); + encryption_params_.clear_lead_in_seconds * stream_info->time_scale(); crypto_period_duration_ = - encryption_options_.crypto_period_duration_in_seconds * + encryption_params_.crypto_period_duration_in_seconds * stream_info->time_scale(); codec_ = stream_info->codec(); nalu_length_size_ = GetNaluLengthSize(*stream_info); stream_label_ = GetStreamLabelForEncryption( - *stream_info, encryption_options_.stream_label_func); + *stream_info, encryption_params_.stream_label_func); switch (codec_) { case kCodecVP9: - if (encryption_options_.vp9_subsample_encryption) + if (encryption_params_.vp9_subsample_encryption) vpx_parser_.reset(new VP9Parser); break; case kCodecH264: @@ -192,8 +194,7 @@ Status EncryptionHandler::ProcessStreamInfo(StreamInfo* stream_info) { return Status(error::ENCRYPTION_FAILURE, "Failed to create encryptor"); stream_info->set_is_encrypted(true); - stream_info->set_has_clear_lead(encryption_options_.clear_lead_in_seconds > - 0); + stream_info->set_has_clear_lead(encryption_params_.clear_lead_in_seconds > 0); stream_info->set_encryption_config(*encryption_config_); return Status::OK; } @@ -230,10 +231,10 @@ Status EncryptionHandler::ProcessMediaSample(MediaSample* sample) { if (remaining_clear_lead_ > 0) return Status::OK; - std::unique_ptr decrypt_config(new DecryptConfig( - encryption_config_->key_id, encryptor_->iv(), - std::vector(), encryption_options_.protection_scheme, - crypt_byte_block_, skip_byte_block_)); + std::unique_ptr decrypt_config( + new DecryptConfig(encryption_config_->key_id, encryptor_->iv(), + std::vector(), protection_scheme_, + crypt_byte_block_, skip_byte_block_)); bool result = true; if (vpx_parser_) { result = EncryptVpxFrame(vpx_frames, sample, decrypt_config.get()); @@ -262,7 +263,7 @@ Status EncryptionHandler::ProcessMediaSample(MediaSample* sample) { } Status EncryptionHandler::SetupProtectionPattern(StreamType stream_type) { - switch (encryption_options_.protection_scheme) { + switch (protection_scheme_) { case kAppleSampleAesProtectionScheme: { const size_t kH264LeadingClearBytesSize = 32u; const size_t kSmallNalUnitSize = 32u + 16u; @@ -323,7 +324,7 @@ Status EncryptionHandler::SetupProtectionPattern(StreamType stream_type) { bool EncryptionHandler::CreateEncryptor(const EncryptionKey& encryption_key) { std::unique_ptr encryptor; - switch (encryption_options_.protection_scheme) { + switch (protection_scheme_) { case FOURCC_cenc: encryptor.reset(new AesCtrEncryptor); break; @@ -363,8 +364,7 @@ bool EncryptionHandler::CreateEncryptor(const EncryptionKey& encryption_key) { std::vector iv = encryption_key.iv; if (iv.empty()) { - if (!AesCryptor::GenerateRandomIv(encryption_options_.protection_scheme, - &iv)) { + if (!AesCryptor::GenerateRandomIv(protection_scheme_, &iv)) { LOG(ERROR) << "Failed to generate random iv."; return false; } @@ -374,7 +374,7 @@ bool EncryptionHandler::CreateEncryptor(const EncryptionKey& encryption_key) { encryptor_ = std::move(encryptor); encryption_config_.reset(new EncryptionConfig); - encryption_config_->protection_scheme = encryption_options_.protection_scheme; + encryption_config_->protection_scheme = protection_scheme_; encryption_config_->crypt_byte_block = crypt_byte_block_; encryption_config_->skip_byte_block = skip_byte_block_; if (encryptor_->use_constant_iv()) { @@ -467,9 +467,9 @@ bool EncryptionHandler::EncryptNalFrame(MediaSample* sample, // CMAF requires 'cenc' scheme BytesOfProtectedData SHALL be a multiple // of 16 bytes; while 'cbcs' scheme BytesOfProtectedData SHALL start on // the first byte of video data following the slice header. - if (encryption_options_.protection_scheme == FOURCC_cbc1 || - encryption_options_.protection_scheme == FOURCC_cens || - encryption_options_.protection_scheme == FOURCC_cenc) { + if (protection_scheme_ == FOURCC_cbc1 || + protection_scheme_ == FOURCC_cens || + protection_scheme_ == FOURCC_cenc) { const uint16_t misalign_bytes = cipher_bytes % kCencBlockSize; current_clear_bytes += misalign_bytes; cipher_bytes -= misalign_bytes; diff --git a/packager/media/crypto/encryption_handler.h b/packager/media/crypto/encryption_handler.h index e81ee2883b..2cb8afe8d3 100644 --- a/packager/media/crypto/encryption_handler.h +++ b/packager/media/crypto/encryption_handler.h @@ -9,7 +9,7 @@ #include "packager/media/base/key_source.h" #include "packager/media/base/media_handler.h" -#include "packager/packager.h" +#include "packager/media/public/crypto_params.h" namespace shaka { namespace media { @@ -20,27 +20,9 @@ class VPxParser; struct EncryptionKey; struct VPxFrameInfo; -/// This structure defines encryption options. -struct EncryptionOptions { - /// Clear lead duration in seconds. - double clear_lead_in_seconds = 0; - /// The protection scheme: 'cenc', 'cens', 'cbc1', 'cbcs'. - FourCC protection_scheme = FOURCC_cenc; - /// Crypto period duration in seconds. A positive value means key rotation is - /// enabled, the key source must support key rotation in this case. - double crypto_period_duration_in_seconds = 0; - /// Enable/disable subsample encryption for VP9. - bool vp9_subsample_encryption = true; - /// Stream label function used to get the label of the encrypted stream. Must - /// be set. - std::function - stream_label_func; -}; - class EncryptionHandler : public MediaHandler { public: - EncryptionHandler(const EncryptionOptions& encryption_options, + EncryptionHandler(const EncryptionParams& encryption_params, KeySource* key_source); ~EncryptionHandler() override; @@ -76,7 +58,8 @@ class EncryptionHandler : public MediaHandler { void InjectVideoSliceHeaderParserForTesting( std::unique_ptr header_parser); - const EncryptionOptions encryption_options_; + const EncryptionParams encryption_params_; + const FourCC protection_scheme_ = FOURCC_NULL; KeySource* key_source_ = nullptr; std::string stream_label_; // Current encryption config and encryptor. diff --git a/packager/media/crypto/encryption_handler_unittest.cc b/packager/media/crypto/encryption_handler_unittest.cc index 554e21f353..a547cd247d 100644 --- a/packager/media/crypto/encryption_handler_unittest.cc +++ b/packager/media/crypto/encryption_handler_unittest.cc @@ -83,13 +83,13 @@ class MockVideoSliceHeaderParser : public VideoSliceHeaderParser { class EncryptionHandlerTest : public MediaHandlerTestBase { public: - void SetUp() override { SetUpEncryptionHandler(EncryptionOptions()); } + void SetUp() override { SetUpEncryptionHandler(EncryptionParams()); } - void SetUpEncryptionHandler(const EncryptionOptions& encryption_options) { - EncryptionOptions new_encryption_options = encryption_options; - if (!encryption_options.stream_label_func) { + void SetUpEncryptionHandler(const EncryptionParams& encryption_params) { + EncryptionParams new_encryption_params = encryption_params; + if (!encryption_params.stream_label_func) { // Setup default stream label function. - new_encryption_options.stream_label_func = + new_encryption_params.stream_label_func = [](const EncryptionParams::EncryptedStreamAttributes& stream_attributes) { if (stream_attributes.stream_type == @@ -100,7 +100,7 @@ class EncryptionHandlerTest : public MediaHandlerTestBase { }; } encryption_handler_.reset( - new EncryptionHandler(new_encryption_options, &mock_key_source_)); + new EncryptionHandler(new_encryption_params, &mock_key_source_)); SetUpGraph(1 /* one input */, 1 /* one output */, encryption_handler_); } @@ -470,11 +470,11 @@ class EncryptionHandlerEncryptionTest TEST_P(EncryptionHandlerEncryptionTest, ClearLeadWithNoKeyRotation) { const double kClearLeadInSeconds = 1.5 * kSegmentDuration / kTimeScale; - EncryptionOptions encryption_options; - encryption_options.protection_scheme = protection_scheme_; - encryption_options.clear_lead_in_seconds = kClearLeadInSeconds; - encryption_options.vp9_subsample_encryption = vp9_subsample_encryption_; - SetUpEncryptionHandler(encryption_options); + EncryptionParams encryption_params; + encryption_params.protection_scheme = protection_scheme_; + encryption_params.clear_lead_in_seconds = kClearLeadInSeconds; + encryption_params.vp9_subsample_encryption = vp9_subsample_encryption_; + SetUpEncryptionHandler(encryption_params); const EncryptionKey mock_encryption_key = GetMockEncryptionKey(); EXPECT_CALL(mock_key_source_, GetKey(_, _)) @@ -523,13 +523,13 @@ TEST_P(EncryptionHandlerEncryptionTest, ClearLeadWithKeyRotation) { const int kSegmentsPerCryptoPeriod = 2; // 2 segments. const double kCryptoPeriodDurationInSeconds = kSegmentsPerCryptoPeriod * kSegmentDuration / kTimeScale; - EncryptionOptions encryption_options; - encryption_options.protection_scheme = protection_scheme_; - encryption_options.clear_lead_in_seconds = kClearLeadInSeconds; - encryption_options.crypto_period_duration_in_seconds = + EncryptionParams encryption_params; + encryption_params.protection_scheme = protection_scheme_; + encryption_params.clear_lead_in_seconds = kClearLeadInSeconds; + encryption_params.crypto_period_duration_in_seconds = kCryptoPeriodDurationInSeconds; - encryption_options.vp9_subsample_encryption = vp9_subsample_encryption_; - SetUpEncryptionHandler(encryption_options); + encryption_params.vp9_subsample_encryption = vp9_subsample_encryption_; + SetUpEncryptionHandler(encryption_params); ASSERT_OK(Process(GetStreamInfoStreamData(kStreamIndex, codec_, kTimeScale))); EXPECT_THAT(GetOutputStreamDataVector(), @@ -582,10 +582,10 @@ TEST_P(EncryptionHandlerEncryptionTest, ClearLeadWithKeyRotation) { } TEST_P(EncryptionHandlerEncryptionTest, Encrypt) { - EncryptionOptions encryption_options; - encryption_options.protection_scheme = protection_scheme_; - encryption_options.vp9_subsample_encryption = vp9_subsample_encryption_; - SetUpEncryptionHandler(encryption_options); + EncryptionParams encryption_params; + encryption_params.protection_scheme = protection_scheme_; + encryption_params.vp9_subsample_encryption = vp9_subsample_encryption_; + SetUpEncryptionHandler(encryption_params); const EncryptionKey mock_encryption_key = GetMockEncryptionKey(); EXPECT_CALL(mock_key_source_, GetKey(_, _)) @@ -653,15 +653,15 @@ class EncryptionHandlerTrackTypeTest : public EncryptionHandlerTest { TEST_F(EncryptionHandlerTrackTypeTest, AudioTrackType) { EncryptionParams::EncryptedStreamAttributes captured_stream_attributes; - EncryptionOptions encryption_options; - encryption_options.stream_label_func = + EncryptionParams encryption_params; + encryption_params.stream_label_func = [&captured_stream_attributes]( const EncryptionParams::EncryptedStreamAttributes& stream_attributes) { captured_stream_attributes = stream_attributes; return kAudioStreamLabel; }; - SetUpEncryptionHandler(encryption_options); + SetUpEncryptionHandler(encryption_params); EXPECT_CALL(mock_key_source_, GetKey(kAudioStreamLabel, _)) .WillOnce( DoAll(SetArgPointee<1>(GetMockEncryptionKey()), Return(Status::OK))); @@ -672,15 +672,15 @@ TEST_F(EncryptionHandlerTrackTypeTest, AudioTrackType) { TEST_F(EncryptionHandlerTrackTypeTest, VideoTrackType) { EncryptionParams::EncryptedStreamAttributes captured_stream_attributes; - EncryptionOptions encryption_options; - encryption_options.stream_label_func = + EncryptionParams encryption_params; + encryption_params.stream_label_func = [&captured_stream_attributes]( const EncryptionParams::EncryptedStreamAttributes& stream_attributes) { captured_stream_attributes = stream_attributes; return kSdVideoStreamLabel; }; - SetUpEncryptionHandler(encryption_options); + SetUpEncryptionHandler(encryption_params); EXPECT_CALL(mock_key_source_, GetKey(kSdVideoStreamLabel, _)) .WillOnce( DoAll(SetArgPointee<1>(GetMockEncryptionKey()), Return(Status::OK))); diff --git a/packager/media/public/crypto_params.h b/packager/media/public/crypto_params.h new file mode 100644 index 0000000000..21c4d446fd --- /dev/null +++ b/packager/media/public/crypto_params.h @@ -0,0 +1,211 @@ +// 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_PUBLIC_CRYPTO_PARAMS_H_ +#define PACKAGER_MEDIA_PUBLIC_CRYPTO_PARAMS_H_ + +#include +#include +#include +#include + +namespace shaka { + +/// Encryption / decryption key providers. +enum class KeyProvider { + kNone = 0, + kWidevine = 1, + kPlayready = 2, + kRawKey = 3, +}; + +/// Signer credential for Widevine license server. +struct WidevineSigner { + /// Name of the signer / content provider. + std::string signer_name; + + enum class SigningKeyType { + kNone, + kAes, + kRsa, + }; + /// Specifies the signing key type, which determines whether AES or RSA key + /// are used to authenticate the signer. A type of 'kNone' is invalid. + SigningKeyType signing_key_type = SigningKeyType::kNone; + struct { + /// AES signing key. + std::vector key; + /// AES signing IV. + std::vector iv; + } aes; + struct { + /// RSA signing private key. + std::string key; + } rsa; +}; + +/// Widevine encryption parameters. +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. + std::string policy; + /// Signer credential for Widevine license / key server. + WidevineSigner signer; + /// Group identifier, if present licenses will belong to this group. + std::vector group_id; +}; + +/// Playready encryption parameters. +/// Two different modes of playready key acquisition is supported: +/// (1) Fetch from a key server. `key_server_url` and `program_identifier` are +/// required. The presence of other parameters may be necessary depends +/// on server configuration. +/// (2) Provide the raw key directly. Both `key_id` and `key` are required. +/// We are planning to merge this mode with `RawKeyEncryptionParams`. +struct PlayreadyEncryptionParams { + /// Playready license / key server URL. + std::string key_server_url; + /// Playready program identifier. + std::string program_identifier; + /// Absolute path to the Certificate Authority file for the server cert in PEM + /// format. + std::string ca_file; + /// Absolute path to client certificate file. + std::string client_cert_file; + /// Absolute path to the private key file. + std::string client_cert_private_key_file; + /// Password to the private key file. + std::string client_cert_private_key_password; + + // TODO(kqyang): move raw playready key generation to RawKey. + /// Provides a raw Playready KeyId. + std::vector key_id; + /// Provides a raw Playready Key. + std::vector key; +}; + +/// Raw key encryption parameters, i.e. with key parameters provided. +struct RawKeyEncryptionParams { + /// An optional initialization vector. If not provided, a random `iv` will be + /// generated. Note that this parameter should only be used during testing. + std::vector iv; + /// Inject a custom `pssh` or multiple concatenated `psshs`. If not provided, + /// a common system pssh will be generated. + std::vector pssh; + + using StreamLabel = std::string; + struct KeyPair { + std::vector key_id; + std::vector key; + }; + /// Defines the KeyPair for the streams. An empty `StreamLabel` indicates the + /// default `KeyPair`, which applies to all the `StreamLabels` not present in + /// `key_map`. + std::map key_map; +}; + +/// Encryption parameters. +struct EncryptionParams { + /// Specifies the key provider, which determines which key provider is used + /// and which encryption params is valid. 'kNone' means not to encrypt the + /// streams. + KeyProvider key_provider = KeyProvider::kNone; + // Only one of the three fields is valid. + WidevineEncryptionParams widevine; + PlayreadyEncryptionParams playready; + RawKeyEncryptionParams raw_key; + + /// Clear lead duration in seconds. + double clear_lead_in_seconds = 0; + /// The protection scheme: "cenc", "cens", "cbc1", "cbcs". + static constexpr uint32_t kProtectionSchemeCenc = 0x63656E63; + static constexpr uint32_t kProtectionSchemeCbc1 = 0x63626331; + static constexpr uint32_t kProtectionSchemeCens = 0x63656E73; + static constexpr uint32_t kProtectionSchemeCbcs = 0x63626373; + uint32_t protection_scheme = kProtectionSchemeCenc; + /// Crypto period duration in seconds. A positive value means key rotation is + /// enabled, the key provider must support key rotation in this case. + static constexpr double kNoKeyRotation = 0; + double crypto_period_duration_in_seconds = kNoKeyRotation; + /// Enable/disable subsample encryption for VP9. + bool vp9_subsample_encryption = true; + + /// Encrypted stream information that is used to determine stream label. + struct EncryptedStreamAttributes { + enum StreamType { + kUnknown, + kVideo, + kAudio, + }; + + StreamType stream_type = kUnknown; + union OneOf { + OneOf() {} + + struct { + int width = 0; + int height = 0; + float frame_rate = 0; + int bit_depth = 0; + } video; + + struct { + int number_of_channels = 0; + } audio; + } oneof; + }; + /// Stream label function assigns a stream label to the stream to be + /// encrypted. Stream label is used to associate KeyPair with streams. Streams + /// with the same stream label always uses the same keyPair; Streams with + /// different stream label could use the same or different KeyPairs. + /// A default stream label function will be generated if not set. + std::function + stream_label_func; +}; + +/// Widevine decryption parameters. +struct WidevineDecryptionParams { + /// Widevine license / key server URL. + std::string key_server_url; + /// Signer credential for Widevine license / key server. + WidevineSigner signer; +}; + +/// Raw key decryption parameters, i.e. with key parameters provided. +struct RawKeyDecryptionParams { + using StreamLabel = std::string; + struct KeyPair { + std::vector key_id; + std::vector key; + }; + /// Defines the KeyPair for the streams. An empty `StreamLabel` indicates the + /// default `KeyPair`, which applies to all the `StreamLabels` not present in + /// `key_map`. + std::map key_map; +}; + +/// Decryption parameters. +struct DecryptionParams { + /// Specifies the key provider, which determines which key provider is used + /// and which encryption params is valid. 'kNone' means not to decrypt the + /// streams. + KeyProvider key_provider = KeyProvider::kNone; + // Only one of the two fields is valid. + WidevineDecryptionParams widevine; + RawKeyDecryptionParams raw_key; +}; + +} // namespace shaka + +#endif // PACKAGER_MEDIA_PUBLIC_CRYPTO_PARAMS_H_ diff --git a/packager/packager.cc b/packager/packager.cc index 447be1ed54..c674704b01 100644 --- a/packager/packager.cc +++ b/packager/packager.cc @@ -316,7 +316,6 @@ std::shared_ptr CreateOutputMuxer(const MuxerOptions& options, bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors, const PackagingParams& packaging_params, const ChunkingOptions& chunking_options, - const EncryptionOptions& encryption_options, const MuxerOptions& muxer_options, FakeClock* fake_clock, KeySource* encryption_key_source, @@ -472,17 +471,25 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors, Status status; if (encryption_key_source && !stream_iter->skip_encryption) { - auto new_encryption_options = encryption_options; + auto encryption_params = packaging_params.encryption_params; // Use Sample AES in MPEG2TS. // TODO(kqyang): Consider adding a new flag to enable Sample AES as we // will support CENC in TS in the future. if (output_format == CONTAINER_MPEG2TS) { VLOG(1) << "Use Apple Sample AES encryption for MPEG2TS."; - new_encryption_options.protection_scheme = + encryption_params.protection_scheme = kAppleSampleAesProtectionScheme; } + if (!encryption_params.stream_label_func) { + const int kDefaultMaxSdPixels = 768 * 576; + const int kDefaultMaxHdPixels = 1920 * 1080; + const int kDefaultMaxUhd1Pixels = 4096 * 2160; + encryption_params.stream_label_func = std::bind( + &Packager::DefaultStreamLabelFunction, kDefaultMaxSdPixels, + kDefaultMaxHdPixels, kDefaultMaxUhd1Pixels, std::placeholders::_1); + } handlers.emplace_back( - new EncryptionHandler(new_encryption_options, encryption_key_source)); + new EncryptionHandler(encryption_params, encryption_key_source)); } // If trick_play_handler is available, muxer should already be connected to @@ -544,24 +551,6 @@ Status RunRemuxJobs(const std::vector>& jobs) { } // namespace } // namespace media -std::string EncryptionParams::DefaultStreamLabelFunction( - int max_sd_pixels, - int max_hd_pixels, - int max_uhd1_pixels, - const EncryptedStreamAttributes& stream_attributes) { - if (stream_attributes.stream_type == EncryptedStreamAttributes::kAudio) - return "AUDIO"; - if (stream_attributes.stream_type == EncryptedStreamAttributes::kVideo) { - const int pixels = stream_attributes.oneof.video.width * - stream_attributes.oneof.video.height; - if (pixels <= max_sd_pixels) return "SD"; - if (pixels <= max_hd_pixels) return "HD"; - if (pixels <= max_uhd1_pixels) return "UHD1"; - return "UHD2"; - } - return ""; -} - struct Packager::PackagerInternal { media::FakeClock fake_clock; std::unique_ptr encryption_key_source; @@ -596,8 +585,6 @@ Status Packager::Initialize( ChunkingOptions chunking_options = media::GetChunkingOptions(packaging_params.chunking_params); - EncryptionOptions encryption_options = - media::GetEncryptionOptions(packaging_params.encryption_params); MuxerOptions muxer_options = media::GetMuxerOptions( packaging_params.temp_dir, packaging_params.mp4_output_params); @@ -608,11 +595,10 @@ Status Packager::Initialize( // Create encryption key source if needed. if (packaging_params.encryption_params.key_provider != KeyProvider::kNone) { - if (encryption_options.protection_scheme == media::FOURCC_NULL) - return Status(error::INVALID_ARGUMENT, "Invalid protection scheme."); - internal->encryption_key_source = - CreateEncryptionKeySource(encryption_options.protection_scheme, - packaging_params.encryption_params); + internal->encryption_key_source = CreateEncryptionKeySource( + static_cast( + packaging_params.encryption_params.protection_scheme), + packaging_params.encryption_params); if (!internal->encryption_key_source) return Status(error::INVALID_ARGUMENT, "Failed to create key source."); } @@ -651,7 +637,7 @@ Status Packager::Initialize( stream_descriptor_list.insert(descriptor); if (!media::CreateRemuxJobs( stream_descriptor_list, packaging_params, chunking_options, - encryption_options, muxer_options, &internal->fake_clock, + muxer_options, &internal->fake_clock, internal->encryption_key_source.get(), internal->mpd_notifier.get(), internal->hls_notifier.get(), &internal->jobs)) { return Status(error::INVALID_ARGUMENT, "Failed to create remux jobs."); @@ -691,4 +677,24 @@ std::string Packager::GetLibraryVersion() { return GetPackagerVersion(); } +std::string Packager::DefaultStreamLabelFunction( + int max_sd_pixels, + int max_hd_pixels, + int max_uhd1_pixels, + const EncryptionParams::EncryptedStreamAttributes& stream_attributes) { + if (stream_attributes.stream_type == + EncryptionParams::EncryptedStreamAttributes::kAudio) + return "AUDIO"; + if (stream_attributes.stream_type == + EncryptionParams::EncryptedStreamAttributes::kVideo) { + const int pixels = stream_attributes.oneof.video.width * + stream_attributes.oneof.video.height; + if (pixels <= max_sd_pixels) return "SD"; + if (pixels <= max_hd_pixels) return "HD"; + if (pixels <= max_uhd1_pixels) return "UHD1"; + return "UHD2"; + } + return ""; +} + } // namespace shaka diff --git a/packager/packager.h b/packager/packager.h index 06992cf607..974d10e329 100644 --- a/packager/packager.h +++ b/packager/packager.h @@ -7,14 +7,12 @@ #ifndef PACKAGER_PACKAGER_H_ #define PACKAGER_PACKAGER_H_ -#include -#include -#include #include #include #include #include "packager/hls/public/hls_playlist_type.h" +#include "packager/media/public/crypto_params.h" #include "packager/status.h" namespace shaka { @@ -109,225 +107,6 @@ struct HlsParams { double time_shift_buffer_depth = 0; }; -/// Encryption / decryption key providers. -enum class KeyProvider { - kNone = 0, - kWidevine = 1, - kPlayready = 2, - kRawKey = 3, -}; - -/// Signer credential for Widevine license server. -struct WidevineSigner { - /// Name of the signer / content provider. - std::string signer_name; - - enum class SigningKeyType { - kNone, - kAes, - kRsa, - }; - /// Specifies the signing key type, which determines whether AES or RSA key - /// are used to authenticate the signer. A type of 'kNone' is invalid. - SigningKeyType signing_key_type = SigningKeyType::kNone; - struct { - /// AES signing key. - std::vector key; - /// AES signing IV. - std::vector iv; - } aes; - struct { - /// RSA signing private key. - std::string key; - } rsa; -}; - -/// Widevine encryption parameters. -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. - std::string policy; - /// Signer credential for Widevine license / key server. - WidevineSigner signer; - /// Group identifier, if present licenses will belong to this group. - std::vector group_id; -}; - -/// Playready encryption parameters. -/// Two different modes of playready key acquisition is supported: -/// (1) Fetch from a key server. `key_server_url` and `program_identifier` are -/// required. The presence of other parameters may be necessary depends -/// on server configuration. -/// (2) Provide the raw key directly. Both `key_id` and `key` are required. -/// We are planning to merge this mode with `RawKeyEncryptionParams`. -struct PlayreadyEncryptionParams { - /// Playready license / key server URL. - std::string key_server_url; - /// Playready program identifier. - std::string program_identifier; - /// Absolute path to the Certificate Authority file for the server cert in PEM - /// format. - std::string ca_file; - /// Absolute path to client certificate file. - std::string client_cert_file; - /// Absolute path to the private key file. - std::string client_cert_private_key_file; - /// Password to the private key file. - std::string client_cert_private_key_password; - - // TODO(kqyang): move raw playready key generation to RawKey. - /// Provides a raw Playready KeyId. - std::vector key_id; - /// Provides a raw Playready Key. - std::vector key; -}; - -/// Raw key encryption parameters, i.e. with key parameters provided. -struct RawKeyEncryptionParams { - /// An optional initialization vector. If not provided, a random `iv` will be - /// generated. Note that this parameter should only be used during testing. - std::vector iv; - /// Inject a custom `pssh` or multiple concatenated `psshs`. If not provided, - /// a common system pssh will be generated. - std::vector pssh; - - using StreamLabel = std::string; - struct KeyPair { - std::vector key_id; - std::vector key; - }; - /// Defines the KeyPair for the streams. An empty `StreamLabel` indicates the - /// default `KeyPair`, which applies to all the `StreamLabels` not present in - /// `key_map`. - std::map key_map; -}; - -/// Encryption parameters. -struct EncryptionParams { - /// Specifies the key provider, which determines which key provider is used - /// and which encryption params is valid. 'kNone' means not to encrypt the - /// streams. - KeyProvider key_provider = KeyProvider::kNone; - // Only one of the three fields is valid. - WidevineEncryptionParams widevine; - PlayreadyEncryptionParams playready; - RawKeyEncryptionParams raw_key; - - /// Clear lead duration in seconds. - double clear_lead_in_seconds = 0; - /// The protection scheme: "cenc", "cens", "cbc1", "cbcs". - std::string protection_scheme = "cenc"; - /// Crypto period duration in seconds. A positive value means key rotation is - /// enabled, the key provider must support key rotation in this case. - const double kNoKeyRotation = 0; - double crypto_period_duration_in_seconds = 0; - /// Enable/disable subsample encryption for VP9. - bool vp9_subsample_encryption = true; - - /// Encrypted stream information that is used to determine stream label. - struct EncryptedStreamAttributes { - enum StreamType { - kUnknown, - kVideo, - kAudio, - }; - - StreamType stream_type = kUnknown; - union OneOf { - OneOf() {} - - struct { - int width = 0; - int height = 0; - float frame_rate = 0; - int bit_depth = 0; - } video; - - struct { - int number_of_channels = 0; - } audio; - } oneof; - }; - /// Default stream label function implementation. - /// @param max_sd_pixels The threshold to determine whether a video track - /// should be considered as SD. If the max pixels per - /// frame is no higher than max_sd_pixels, i.e. [0, - /// max_sd_pixels], it is SD. - /// @param max_hd_pixels: The threshold to determine whether a video track - /// should be considered as HD. If the max pixels per - /// frame is higher than max_sd_pixels, but no higher - /// than max_hd_pixels, i.e. (max_sd_pixels, - /// max_hd_pixels], it is HD. - /// @param max_uhd1_pixels: The threshold to determine whether a video track - /// should be considered as UHD1. If the max pixels - /// per frame is higher than max_hd_pixels, but no - /// higher than max_uhd1_pixels, i.e. (max_hd_pixels, - /// max_uhd1_pixels], it is UHD1. Otherwise it is - /// UHD2. - /// @param stream_info Encrypted stream info. - /// @return the stream label associated with `stream_info`. Can be "AUDIO", - /// "SD", "HD", "UHD1" or "UHD2". - static SHAKA_EXPORT std::string DefaultStreamLabelFunction( - int max_sd_pixels, - int max_hd_pixels, - int max_uhd1_pixels, - const EncryptedStreamAttributes& stream_attributes); - const int kDefaultMaxSdPixels = 768 * 576; - const int kDefaultMaxHdPixels = 1920 * 1080; - const int kDefaultMaxUhd1Pixels = 4096 * 2160; - /// Stream label function assigns a stream label to the stream to be - /// encrypted. Stream label is used to associate KeyPair with streams. Streams - /// with the same stream label always uses the same keyPair; Streams with - /// different stream label could use the same or different KeyPairs. - std::function - stream_label_func = - std::bind(&EncryptionParams::DefaultStreamLabelFunction, - kDefaultMaxSdPixels, - kDefaultMaxHdPixels, - kDefaultMaxUhd1Pixels, - std::placeholders::_1); -}; - -/// Widevine decryption parameters. -struct WidevineDecryptionParams { - /// Widevine license / key server URL. - std::string key_server_url; - /// Signer credential for Widevine license / key server. - WidevineSigner signer; -}; - -/// Raw key decryption parameters, i.e. with key parameters provided. -struct RawKeyDecryptionParams { - using StreamLabel = std::string; - struct KeyPair { - std::vector key_id; - std::vector key; - }; - /// Defines the KeyPair for the streams. An empty `StreamLabel` indicates the - /// default `KeyPair`, which applies to all the `StreamLabels` not present in - /// `key_map`. - std::map key_map; -}; - -/// Decryption parameters. -struct DecryptionParams { - /// Specifies the key provider, which determines which key provider is used - /// and which encryption params is valid. 'kNone' means not to decrypt the - /// streams. - KeyProvider key_provider = KeyProvider::kNone; - // Only one of the two fields is valid. - WidevineDecryptionParams widevine; - RawKeyDecryptionParams raw_key; -}; - /// Parameters used for testing. struct TestParams { /// Whether to dump input stream info. @@ -440,6 +219,31 @@ class SHAKA_EXPORT Packager { /// @return The version of the library. static std::string GetLibraryVersion(); + /// Default stream label function implementation. + /// @param max_sd_pixels The threshold to determine whether a video track + /// should be considered as SD. If the max pixels per + /// frame is no higher than max_sd_pixels, i.e. [0, + /// max_sd_pixels], it is SD. + /// @param max_hd_pixels The threshold to determine whether a video track + /// should be considered as HD. If the max pixels per + /// frame is higher than max_sd_pixels, but no higher + /// than max_hd_pixels, i.e. (max_sd_pixels, + /// max_hd_pixels], it is HD. + /// @param max_uhd1_pixels The threshold to determine whether a video track + /// should be considered as UHD1. If the max pixels + /// per frame is higher than max_hd_pixels, but no + /// higher than max_uhd1_pixels, i.e. (max_hd_pixels, + /// max_uhd1_pixels], it is UHD1. Otherwise it is + /// UHD2. + /// @param stream_info Encrypted stream info. + /// @return the stream label associated with `stream_info`. Can be "AUDIO", + /// "SD", "HD", "UHD1" or "UHD2". + static std::string DefaultStreamLabelFunction( + int max_sd_pixels, + int max_hd_pixels, + int max_uhd1_pixels, + const EncryptionParams::EncryptedStreamAttributes& stream_attributes); + private: Packager(const Packager&) = delete; Packager& operator=(const Packager&) = delete;