diff --git a/packager/app/packager_util.cc b/packager/app/packager_util.cc index 3c618354d0..4645d93aa0 100644 --- a/packager/app/packager_util.cc +++ b/packager/app/packager_util.cc @@ -80,7 +80,7 @@ scoped_ptr CreateEncryptionKeySource() { scoped_ptr encryption_key_source; if (FLAGS_enable_widevine_encryption) { scoped_ptr widevine_key_source( - new WidevineKeySource(FLAGS_key_server_url)); + new WidevineKeySource(FLAGS_key_server_url, FLAGS_include_common_pssh)); if (!FLAGS_signer.empty()) { scoped_ptr request_signer(CreateSigner()); if (!request_signer) @@ -111,7 +111,7 @@ scoped_ptr CreateDecryptionKeySource() { scoped_ptr decryption_key_source; if (FLAGS_enable_widevine_decryption) { scoped_ptr widevine_key_source( - new WidevineKeySource(FLAGS_key_server_url)); + new WidevineKeySource(FLAGS_key_server_url, FLAGS_include_common_pssh)); if (!FLAGS_signer.empty()) { scoped_ptr request_signer(CreateSigner()); if (!request_signer) diff --git a/packager/app/widevine_encryption_flags.cc b/packager/app/widevine_encryption_flags.cc index c65c22b38c..b83d94d15f 100644 --- a/packager/app/widevine_encryption_flags.cc +++ b/packager/app/widevine_encryption_flags.cc @@ -23,6 +23,11 @@ DEFINE_bool(enable_widevine_decryption, "Enable decryption with Widevine license server/proxy. User should " "provide either AES signing key (--aes_signing_key, " "--aes_signing_iv) or RSA signing key (--rsa_signing_key_path)."); +DEFINE_bool(include_common_pssh, + false, + "When using Widevine encryption, include an additional v1 PSSH box " + "for the common system ID that includes the key IDs. See: " + "http://goo.gl/PHZDAF"); DEFINE_string(key_server_url, "", "Key server url. Required for encryption and " "decryption"); DEFINE_string(content_id, "", "Content Id (hex)."); @@ -104,6 +109,11 @@ bool ValidateWidevineCryptoFlags() { widevine_encryption_label)) { success = false; } + if (FLAGS_include_common_pssh && !FLAGS_enable_widevine_encryption) { + PrintError("--include_common_pssh is only valid with " + "--enable_widevine_encryption"); + success = false; + } if (FLAGS_max_sd_pixels <= 0) { PrintError("--max_sd_pixels must be positive."); diff --git a/packager/app/widevine_encryption_flags.h b/packager/app/widevine_encryption_flags.h index f6a9b45db3..19658cd8dc 100644 --- a/packager/app/widevine_encryption_flags.h +++ b/packager/app/widevine_encryption_flags.h @@ -13,6 +13,7 @@ DECLARE_bool(enable_widevine_encryption); DECLARE_bool(enable_widevine_decryption); +DECLARE_bool(include_common_pssh); DECLARE_string(key_server_url); DECLARE_string(content_id); DECLARE_string(policy); diff --git a/packager/media/base/fixed_key_source.cc b/packager/media/base/fixed_key_source.cc index 1eeeeaffe2..4edd0133d5 100644 --- a/packager/media/base/fixed_key_source.cc +++ b/packager/media/base/fixed_key_source.cc @@ -12,15 +12,6 @@ namespace edash_packager { namespace media { -namespace { -// Common SystemID defined by EME, which requires Key System implementations -// supporting ISO Common Encryption to support this SystemID and format. -// http://goo.gl/PHZDAF -const uint8_t kCommonSystemId[] = {0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, - 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, - 0x52, 0xe2, 0xfb, 0x4b}; -} // namespace - FixedKeySource::~FixedKeySource() {} Status FixedKeySource::FetchKeys(const std::vector& pssh_box) { diff --git a/packager/media/base/fixed_key_source.h b/packager/media/base/fixed_key_source.h index c1d5499ae6..be7ff7cdb5 100644 --- a/packager/media/base/fixed_key_source.h +++ b/packager/media/base/fixed_key_source.h @@ -16,6 +16,13 @@ namespace edash_packager { namespace media { +// Common SystemID defined by EME, which requires Key System implementations +// supporting ISO Common Encryption to support this SystemID and format. +// http://goo.gl/PHZDAF +const uint8_t kCommonSystemId[] = {0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, + 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, + 0x52, 0xe2, 0xfb, 0x4b}; + /// A key source that uses fixed keys for encryption. class FixedKeySource : public KeySource { public: diff --git a/packager/media/base/widevine_key_source.cc b/packager/media/base/widevine_key_source.cc index b31d9cc335..df8828859a 100644 --- a/packager/media/base/widevine_key_source.cc +++ b/packager/media/base/widevine_key_source.cc @@ -6,12 +6,15 @@ #include "packager/media/base/widevine_key_source.h" +#include + #include "packager/base/base64.h" #include "packager/base/bind.h" #include "packager/base/json/json_reader.h" #include "packager/base/json/json_writer.h" #include "packager/base/memory/ref_counted.h" #include "packager/base/stl_util.h" +#include "packager/media/base/fixed_key_source.h" #include "packager/media/base/http_key_fetcher.h" #include "packager/media/base/producer_consumer_queue.h" #include "packager/media/base/protection_system_specific_info.h" @@ -142,13 +145,15 @@ class WidevineKeySource::RefCountedEncryptionKeyMap DISALLOW_COPY_AND_ASSIGN(RefCountedEncryptionKeyMap); }; -WidevineKeySource::WidevineKeySource(const std::string& server_url) +WidevineKeySource::WidevineKeySource(const std::string& server_url, + bool add_common_pssh) : key_production_thread_("KeyProductionThread", base::Bind(&WidevineKeySource::FetchKeysTask, base::Unretained(this))), key_fetcher_(new HttpKeyFetcher(kKeyFetchTimeoutInSeconds)), server_url_(server_url), crypto_period_count_(kDefaultCryptoPeriodCount), + add_common_pssh_(add_common_pssh), key_production_started_(false), start_key_production_(false, false), first_crypto_period_index_(0) { @@ -572,8 +577,27 @@ bool WidevineKeySource::ExtractEncryptionKey( encryption_key_map[track_type] = encryption_key.release(); } - // NOTE: To support version 1 pssh, update ProtectionSystemSpecificInfo to - // include all key IDs in |encryption_key_map|. + // If the flag exists, create a common system ID PSSH box that contains the + // key IDs of all the keys. + if (add_common_pssh_ && !widevine_classic) { + std::set> key_ids; + for (const EncryptionKeyMap::value_type& pair : encryption_key_map) { + key_ids.insert(pair.second->key_id); + } + + // Create a common system PSSH box. + ProtectionSystemSpecificInfo info; + info.set_system_id(kCommonSystemId, arraysize(kCommonSystemId)); + info.set_pssh_box_version(1); + for (const std::vector& key_id : key_ids) { + info.add_key_id(key_id); + } + + for (const EncryptionKeyMap::value_type& pair : encryption_key_map) { + pair.second->key_system_info.push_back(info); + } + } + DCHECK(!encryption_key_map.empty()); if (!enable_key_rotation) { encryption_key_map_ = encryption_key_map; diff --git a/packager/media/base/widevine_key_source.h b/packager/media/base/widevine_key_source.h index 9d3340da31..5a20c08abe 100644 --- a/packager/media/base/widevine_key_source.h +++ b/packager/media/base/widevine_key_source.h @@ -26,7 +26,7 @@ template class ProducerConsumerQueue; class WidevineKeySource : public KeySource { public: /// @param server_url is the Widevine common encryption server url. - explicit WidevineKeySource(const std::string& server_url); + WidevineKeySource(const std::string& server_url, bool add_common_pssh); ~WidevineKeySource() override; @@ -113,6 +113,7 @@ class WidevineKeySource : public KeySource { const uint32_t crypto_period_count_; base::Lock lock_; + bool add_common_pssh_; bool key_production_started_; base::WaitableEvent start_key_production_; uint32_t first_crypto_period_index_; diff --git a/packager/media/base/widevine_key_source_unittest.cc b/packager/media/base/widevine_key_source_unittest.cc index b6d0c4a9d3..f41db07abb 100644 --- a/packager/media/base/widevine_key_source_unittest.cc +++ b/packager/media/base/widevine_key_source_unittest.cc @@ -7,9 +7,12 @@ #include #include +#include + #include "packager/base/base64.h" #include "packager/base/strings/string_number_conversions.h" #include "packager/base/strings/stringprintf.h" +#include "packager/media/base/fixed_key_source.h" #include "packager/media/base/key_fetcher.h" #include "packager/media/base/request_signer.h" #include "packager/media/base/test/status_test_util.h" @@ -156,7 +159,8 @@ class MockKeyFetcher : public KeyFetcher { DISALLOW_COPY_AND_ASSIGN(MockKeyFetcher); }; -class WidevineKeySourceTest : public ::testing::Test { +class WidevineKeySourceTest : public ::testing::Test, + public ::testing::WithParamInterface { public: WidevineKeySourceTest() : mock_request_signer_(new MockRequestSigner(kSignerName)), @@ -170,7 +174,7 @@ class WidevineKeySourceTest : public ::testing::Test { protected: void CreateWidevineKeySource() { - widevine_key_source_.reset(new WidevineKeySource(kServerUrl)); + widevine_key_source_.reset(new WidevineKeySource(kServerUrl, GetParam())); widevine_key_source_->set_key_fetcher(mock_key_fetcher_.Pass()); } @@ -183,11 +187,30 @@ class WidevineKeySourceTest : public ::testing::Test { &encryption_key)); EXPECT_EQ(GetMockKey(kTrackTypes[i]), ToString(encryption_key.key)); if (!classic) { - ASSERT_EQ(1u, encryption_key.key_system_info.size()); + ASSERT_EQ(GetParam() ? 2u : 1u, encryption_key.key_system_info.size()); EXPECT_EQ(GetMockKeyId(kTrackTypes[i]), ToString(encryption_key.key_id)); EXPECT_EQ(GetMockPsshData(kTrackTypes[i]), ToString(encryption_key.key_system_info[0].pssh_data())); + + if (GetParam()) { + // Each of the keys contains all the key IDs. + const std::vector common_system_id( + kCommonSystemId, kCommonSystemId + arraysize(kCommonSystemId)); + ASSERT_EQ(common_system_id, + encryption_key.key_system_info[1].system_id()); + + const std::vector>& key_ids = + encryption_key.key_system_info[1].key_ids(); + ASSERT_EQ(arraysize(kTrackTypes), key_ids.size()); + for (size_t j = 0; j < arraysize(kTrackTypes); ++j) { + // Because they are stored in a std::set, the order may change. + const std::string key_id_str = GetMockKeyId(kTrackTypes[j]); + const std::vector key_id(key_id_str.begin(), + key_id_str.end()); + EXPECT_THAT(key_ids, testing::Contains(key_id)); + } + } } } } @@ -200,7 +223,7 @@ class WidevineKeySourceTest : public ::testing::Test { DISALLOW_COPY_AND_ASSIGN(WidevineKeySourceTest); }; -TEST_F(WidevineKeySourceTest, GetTrackTypeFromString) { +TEST_P(WidevineKeySourceTest, GetTrackTypeFromString) { EXPECT_EQ(KeySource::TRACK_TYPE_SD, KeySource::GetTrackTypeFromString("SD")); EXPECT_EQ(KeySource::TRACK_TYPE_HD, @@ -211,7 +234,7 @@ TEST_F(WidevineKeySourceTest, GetTrackTypeFromString) { KeySource::GetTrackTypeFromString("FOO")); } -TEST_F(WidevineKeySourceTest, GenerateSignatureFailure) { +TEST_P(WidevineKeySourceTest, GenerateSignatureFailure) { EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _)) .WillOnce(Return(false)); @@ -223,7 +246,7 @@ TEST_F(WidevineKeySourceTest, GenerateSignatureFailure) { // Check whether expected request message and post data was generated and // verify the correct behavior on http failure. -TEST_F(WidevineKeySourceTest, HttpFetchFailure) { +TEST_P(WidevineKeySourceTest, HttpFetchFailure) { std::string expected_message = base::StringPrintf( kExpectedRequestMessageFormat, Base64Encode(kContentId).c_str(), kPolicy); EXPECT_CALL(*mock_request_signer_, @@ -246,7 +269,7 @@ TEST_F(WidevineKeySourceTest, HttpFetchFailure) { widevine_key_source_->FetchKeys(content_id_, kPolicy)); } -TEST_F(WidevineKeySourceTest, LicenseStatusCencOK) { +TEST_P(WidevineKeySourceTest, LicenseStatusCencOK) { std::string mock_response = base::StringPrintf( kHttpResponseFormat, Base64Encode(GenerateMockLicenseResponse()).c_str()); @@ -258,7 +281,7 @@ TEST_F(WidevineKeySourceTest, LicenseStatusCencOK) { VerifyKeys(false); } -TEST_F(WidevineKeySourceTest, LicenseStatusCencNotOK) { +TEST_P(WidevineKeySourceTest, LicenseStatusCencNotOK) { std::string mock_response = base::StringPrintf( kHttpResponseFormat, Base64Encode( GenerateMockClassicLicenseResponse()).c_str()); @@ -272,7 +295,7 @@ TEST_F(WidevineKeySourceTest, LicenseStatusCencNotOK) { .error_code()); } -TEST_F(WidevineKeySourceTest, LicenseStatusCencWithPsshBoxOK) { +TEST_P(WidevineKeySourceTest, LicenseStatusCencWithPsshBoxOK) { std::string expected_message = base::StringPrintf(kExpectedRequestMessageWithPsshFormat, Base64Encode(kRequestPsshData).c_str()); @@ -293,7 +316,7 @@ TEST_F(WidevineKeySourceTest, LicenseStatusCencWithPsshBoxOK) { VerifyKeys(false); } -TEST_F(WidevineKeySourceTest, LicenseStatusCencWithKeyIdsOK) { +TEST_P(WidevineKeySourceTest, LicenseStatusCencWithKeyIdsOK) { std::string expected_pssh_data( kRequestPsshDataFromKeyIds, kRequestPsshDataFromKeyIds + arraysize(kRequestPsshDataFromKeyIds)); @@ -318,7 +341,7 @@ TEST_F(WidevineKeySourceTest, LicenseStatusCencWithKeyIdsOK) { VerifyKeys(false); } -TEST_F(WidevineKeySourceTest, LicenseStatusClassicOK) { +TEST_P(WidevineKeySourceTest, LicenseStatusClassicOK) { std::string expected_message = base::StringPrintf( kExpectedRequestMessageWithAssetIdFormat, kClassicAssetId); EXPECT_CALL(*mock_request_signer_, @@ -337,7 +360,7 @@ TEST_F(WidevineKeySourceTest, LicenseStatusClassicOK) { VerifyKeys(true); } -TEST_F(WidevineKeySourceTest, RetryOnHttpTimeout) { +TEST_P(WidevineKeySourceTest, RetryOnHttpTimeout) { std::string mock_response = base::StringPrintf( kHttpResponseFormat, Base64Encode(GenerateMockLicenseResponse()).c_str()); @@ -351,7 +374,7 @@ TEST_F(WidevineKeySourceTest, RetryOnHttpTimeout) { VerifyKeys(false); } -TEST_F(WidevineKeySourceTest, RetryOnTransientError) { +TEST_P(WidevineKeySourceTest, RetryOnTransientError) { std::string mock_license_status = base::StringPrintf( kLicenseResponseFormat, kLicenseStatusTransientError, ""); std::string mock_response = base::StringPrintf( @@ -371,7 +394,7 @@ TEST_F(WidevineKeySourceTest, RetryOnTransientError) { VerifyKeys(false); } -TEST_F(WidevineKeySourceTest, NoRetryOnUnknownError) { +TEST_P(WidevineKeySourceTest, NoRetryOnUnknownError) { std::string mock_license_status = base::StringPrintf( kLicenseResponseFormat, kLicenseStatusUnknownError, ""); std::string mock_response = base::StringPrintf( @@ -425,7 +448,7 @@ std::string GenerateMockKeyRotationLicenseResponse( } // namespace -TEST_F(WidevineKeySourceTest, KeyRotationTest) { +TEST_P(WidevineKeySourceTest, KeyRotationTest) { const uint32_t kFirstCryptoPeriodIndex = 8; const uint32_t kCryptoPeriodCount = 10; // Array of indexes to be checked. @@ -491,5 +514,9 @@ TEST_F(WidevineKeySourceTest, KeyRotationTest) { EXPECT_EQ(error::INVALID_ARGUMENT, status.error_code()); } +INSTANTIATE_TEST_CASE_P(WidevineKeySourceInstance, + WidevineKeySourceTest, + ::testing::Bool()); + } // namespace media } // namespace edash_packager