diff --git a/packager/media/base/key_source.cc b/packager/media/base/key_source.cc index a25a9d624f..c62111b72d 100644 --- a/packager/media/base/key_source.cc +++ b/packager/media/base/key_source.cc @@ -11,9 +11,6 @@ #include "packager/media/base/buffer_writer.h" namespace { -const uint8_t kWidevineSystemId[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, - 0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc, - 0xd5, 0x1d, 0x21, 0xed}; // TODO(kqyang): Consider making it configurable. const char kDefaultUUID[] = "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"; const char kDefaultSystemName[] = ""; @@ -33,7 +30,12 @@ Status KeySource::FetchKeys(const std::vector& content_id, return Status::OK; } -Status KeySource::FetchKeys(const std::vector& pssh_data) { +Status KeySource::FetchKeys(const std::vector& pssh_box) { + // Do nothing for fixed key encryption/decryption. + return Status::OK; +} + +Status KeySource::FetchKeys(const std::vector>& key_ids) { // Do nothing for fixed key encryption/decryption. return Status::OK; } diff --git a/packager/media/base/key_source.h b/packager/media/base/key_source.h index 4fe5eb4fa0..48158590e4 100644 --- a/packager/media/base/key_source.h +++ b/packager/media/base/key_source.h @@ -15,6 +15,10 @@ namespace edash_packager { namespace media { +const uint8_t kWidevineSystemId[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, + 0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc, + 0xd5, 0x1d, 0x21, 0xed}; + struct EncryptionKey { EncryptionKey(); ~EncryptionKey(); @@ -47,10 +51,14 @@ class KeySource { 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. + /// @param pssh_box The entire PSSH box for the content to be decrypted /// @return OK on success, an error status otherwise. - virtual Status FetchKeys(const std::vector& pssh_data); + virtual Status FetchKeys(const std::vector& pssh_box); + + /// Fetch keys for CENC from the key server. + /// @param key_ids the key IDs for the keys to fetch from the server. + /// @return OK on success, an error status otherwise. + virtual Status FetchKeys(const std::vector>& key_ids); /// Fetch keys for WVM decryption from the key server. /// @param asset_id is the Widevine Classic asset ID for the content to be diff --git a/packager/media/base/media_base.gyp b/packager/media/base/media_base.gyp index d4a0fbbed2..01ac04dc13 100644 --- a/packager/media/base/media_base.gyp +++ b/packager/media/base/media_base.gyp @@ -83,6 +83,7 @@ 'widevine_key_source.h', ], 'dependencies': [ + 'widevine_pssh_data_proto', '../../base/base.gyp:base', '../../third_party/boringssl/boringssl.gyp:boringssl', '../../third_party/curl/curl.gyp:libcurl', diff --git a/packager/media/base/widevine_key_source.cc b/packager/media/base/widevine_key_source.cc index ea63d761b1..d84f8d7617 100644 --- a/packager/media/base/widevine_key_source.cc +++ b/packager/media/base/widevine_key_source.cc @@ -14,7 +14,9 @@ #include "packager/base/stl_util.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" #include "packager/media/base/request_signer.h" +#include "packager/media/base/widevine_pssh_data.pb.h" #define RCHECK(x) \ do { \ @@ -172,10 +174,49 @@ Status WidevineKeySource::FetchKeys(const std::vector& content_id, return FetchKeysInternal(!kEnableKeyRotation, 0, false); } -Status WidevineKeySource::FetchKeys(const std::vector& pssh_data) { +Status WidevineKeySource::FetchKeys(const std::vector& pssh_box) { + const std::vector widevine_system_id( + kWidevineSystemId, kWidevineSystemId + arraysize(kWidevineSystemId)); + + ProtectionSystemSpecificInfo info; + if (!info.Parse(pssh_box.data(), pssh_box.size())) + return Status(error::PARSER_FAILURE, "Error parsing the PSSH box."); + + if (info.system_id() == widevine_system_id) { + base::AutoLock scoped_lock(lock_); + request_dict_.Clear(); + std::string pssh_data_base64_string; + + BytesToBase64String(info.pssh_data(), &pssh_data_base64_string); + request_dict_.SetString("pssh_data", pssh_data_base64_string); + return FetchKeysInternal(!kEnableKeyRotation, 0, false); + } else if (!info.key_ids().empty()) { + // This is not a Widevine PSSH box. Try making the request for the key-IDs. + // Even if this is a different key-system, it should still work. Either + // the server will not recognize it and return an error, or it will + // recognize it and the key must be correct (or the content is bad). + return FetchKeys(info.key_ids()); + } else { + return Status(error::NOT_FOUND, "No key IDs given in PSSH box."); + } +} + +Status WidevineKeySource::FetchKeys( + const std::vector>& key_ids) { base::AutoLock scoped_lock(lock_); request_dict_.Clear(); std::string pssh_data_base64_string; + + // Generate Widevine PSSH data from the key-IDs. + WidevinePsshData widevine_pssh_data; + for (size_t i = 0; i < key_ids.size(); i++) { + widevine_pssh_data.add_key_id(key_ids[i].data(), key_ids[i].size()); + } + + const std::string serialized_string = widevine_pssh_data.SerializeAsString(); + std::vector pssh_data(serialized_string.begin(), + serialized_string.end()); + BytesToBase64String(pssh_data, &pssh_data_base64_string); request_dict_.SetString("pssh_data", pssh_data_base64_string); return FetchKeysInternal(!kEnableKeyRotation, 0, false); diff --git a/packager/media/base/widevine_key_source.h b/packager/media/base/widevine_key_source.h index 85cb9b6c13..3afdc75914 100644 --- a/packager/media/base/widevine_key_source.h +++ b/packager/media/base/widevine_key_source.h @@ -34,7 +34,8 @@ class WidevineKeySource : public KeySource { /// @{ Status FetchKeys(const std::vector& content_id, const std::string& policy) override; - Status FetchKeys(const std::vector& pssh_data) override; + Status FetchKeys(const std::vector& pssh_box) override; + Status FetchKeys(const std::vector>& key_ids) override; Status FetchKeys(uint32_t asset_id) override; Status GetKey(TrackType track_type, EncryptionKey* key) override; diff --git a/packager/media/base/widevine_key_source_unittest.cc b/packager/media/base/widevine_key_source_unittest.cc index e214134805..e402813bf0 100644 --- a/packager/media/base/widevine_key_source_unittest.cc +++ b/packager/media/base/widevine_key_source_unittest.cc @@ -57,7 +57,15 @@ const char kTrackFormat[] = const char kClassicTrackFormat[] = "{\"type\":\"%s\",\"key\":\"%s\"}"; const char kLicenseResponseFormat[] = "{\"status\":\"%s\",\"tracks\":[%s]}"; const char kHttpResponseFormat[] = "{\"response\":\"%s\"}"; +const uint8_t kRequestPsshBox[] = { + 0, 0, 0, 41, 'p', 's', 's', 'h', 0, 0, 0, + 0, 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8, + 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, 0, 0, 0, 0x09, 'P', + 'S', 'S', 'H', ' ', 'd', 'a', 't', 'a'}; const char kRequestPsshData[] = "PSSH data"; +const uint8_t kRequestPsshDataFromKeyIds[] = {0x12, 0x06, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05}; +const uint8_t kRequestKeyId[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; // 32-bit with leading bit set, to verify that big uint32_t can be handled // correctly. const uint32_t kClassicAssetId = 0x80038cd9; @@ -266,7 +274,7 @@ TEST_F(WidevineKeySourceTest, LicenseStatusCencNotOK) { .error_code()); } -TEST_F(WidevineKeySourceTest, LicenseStatusCencWithPsshDataOK) { +TEST_F(WidevineKeySourceTest, LicenseStatusCencWithPsshBoxOK) { std::string expected_message = base::StringPrintf(kExpectedRequestMessageWithPsshFormat, Base64Encode(kRequestPsshData).c_str()); @@ -281,11 +289,34 @@ TEST_F(WidevineKeySourceTest, LicenseStatusCencWithPsshDataOK) { CreateWidevineKeySource(); widevine_key_source_->set_signer(mock_request_signer_.Pass()); - std::vector pssh_data( - reinterpret_cast(kRequestPsshData), - reinterpret_cast(kRequestPsshData) + - strlen(kRequestPsshData)); - ASSERT_OK(widevine_key_source_->FetchKeys(pssh_data)); + std::vector pssh_box(kRequestPsshBox, + kRequestPsshBox + arraysize(kRequestPsshBox)); + ASSERT_OK(widevine_key_source_->FetchKeys(pssh_box)); + VerifyKeys(false); +} + +TEST_F(WidevineKeySourceTest, LicenseStatusCencWithKeyIdsOK) { + std::string expected_pssh_data( + kRequestPsshDataFromKeyIds, + kRequestPsshDataFromKeyIds + arraysize(kRequestPsshDataFromKeyIds)); + std::string expected_message = + base::StringPrintf(kExpectedRequestMessageWithPsshFormat, + Base64Encode(expected_pssh_data).c_str()); + EXPECT_CALL(*mock_request_signer_, + GenerateSignature(StrEq(expected_message), _)) + .WillOnce(DoAll(SetArgPointee<1>(kMockSignature), Return(true))); + + std::string mock_response = base::StringPrintf( + kHttpResponseFormat, Base64Encode(GenerateMockLicenseResponse()).c_str()); + EXPECT_CALL(*mock_key_fetcher_, FetchKeys(_, _, _)) + .WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK))); + + CreateWidevineKeySource(); + widevine_key_source_->set_signer(mock_request_signer_.Pass()); + std::vector> key_ids; + key_ids.push_back(std::vector( + kRequestKeyId, kRequestKeyId + arraysize(kRequestKeyId))); + ASSERT_OK(widevine_key_source_->FetchKeys(key_ids)); VerifyKeys(false); } diff --git a/packager/media/base/widevine_pssh_data.proto b/packager/media/base/widevine_pssh_data.proto index 62b57f6009..c29f550d34 100644 --- a/packager/media/base/widevine_pssh_data.proto +++ b/packager/media/base/widevine_pssh_data.proto @@ -7,6 +7,7 @@ // This file defines Widevine Pssh Data proto format. syntax = "proto2"; +option optimize_for = LITE_RUNTIME; package edash_packager.media; diff --git a/packager/media/formats/mp4/mp4_media_parser.cc b/packager/media/formats/mp4/mp4_media_parser.cc index 96537949c6..a84b3e57b6 100644 --- a/packager/media/formats/mp4/mp4_media_parser.cc +++ b/packager/media/formats/mp4/mp4_media_parser.cc @@ -17,7 +17,6 @@ #include "packager/media/base/key_source.h" #include "packager/media/base/macros.h" #include "packager/media/base/media_sample.h" -#include "packager/media/base/protection_system_specific_info.h" #include "packager/media/base/video_stream_info.h" #include "packager/media/file/file.h" #include "packager/media/file/file_closer.h" @@ -83,7 +82,6 @@ AudioCodec FourCCToAudioCodec(FourCC fourcc) { } } -const char kWidevineKeySystemId[] = "edef8ba979d64acea3c827dcd51d21ed"; // Default DTS audio number of channels for 5.1 channel layout. const uint8_t kDtsAudioNumChannels = 6; @@ -579,22 +577,23 @@ bool MP4MediaParser::FetchKeysIfNecessary( if (!decryption_key_source_) return true; - // TODO(tinskip): Pass in raw 'pssh' boxes to FetchKeys. This will allow - // supporting multiple keysystems. Move this to KeySource. - std::vector widevine_system_id; - base::HexStringToBytes(kWidevineKeySystemId, &widevine_system_id); + Status status; for (std::vector::const_iterator iter = headers.begin(); iter != headers.end(); ++iter) { - ProtectionSystemSpecificInfo info; - RCHECK(info.Parse(iter->raw_box.data(), iter->raw_box.size())); - if (info.system_id() == widevine_system_id) { - Status status = decryption_key_source_->FetchKeys(info.pssh_data()); - if (!status.ok()) { - LOG(ERROR) << "Error fetching decryption keys: " << status; - return false; - } - return true; + status = decryption_key_source_->FetchKeys(iter->raw_box); + if (!status.ok()) { + // If there is an error, try using the next PSSH box and report if none + // work. + VLOG(1) << "Unable to fetch decryption keys: " << status + << ", trying the next PSSH box"; + continue; } + return true; + } + + if (!status.ok()) { + LOG(ERROR) << "Error fetching decryption keys: " << status; + return false; } LOG(ERROR) << "No viable 'pssh' box found for content decryption."; diff --git a/packager/media/formats/webm/webm.gyp b/packager/media/formats/webm/webm.gyp index 6e03e911c4..cf312be0e4 100644 --- a/packager/media/formats/webm/webm.gyp +++ b/packager/media/formats/webm/webm.gyp @@ -58,7 +58,6 @@ '../../../third_party/boringssl/boringssl.gyp:boringssl', '../../../third_party/libwebm/libwebm.gyp:mkvmuxer', '../../base/media_base.gyp:media_base', - '../../base/media_base.gyp:widevine_pssh_data_proto', '../../filters/filters.gyp:filters' ], }, diff --git a/packager/media/formats/webm/webm_media_parser.cc b/packager/media/formats/webm/webm_media_parser.cc index 08a9bf0927..8288240755 100644 --- a/packager/media/formats/webm/webm_media_parser.cc +++ b/packager/media/formats/webm/webm_media_parser.cc @@ -11,7 +11,6 @@ #include "packager/base/logging.h" #include "packager/media/base/buffer_writer.h" #include "packager/media/base/timestamp.h" -#include "packager/media/base/widevine_pssh_data.pb.h" #include "packager/media/formats/webm/webm_cluster_parser.h" #include "packager/media/formats/webm/webm_constants.h" #include "packager/media/formats/webm/webm_content_encodings.h" @@ -239,20 +238,21 @@ bool WebMMediaParser::FetchKeysIfNecessary( const std::string& video_encryption_key_id) { if (audio_encryption_key_id.empty() && video_encryption_key_id.empty()) return true; - // An error will be returned later if the samples need to be derypted. + // An error will be returned later if the samples need to be decrypted. if (!decryption_key_source_) return true; - // Generate WidevinePsshData from key_id. - WidevinePsshData widevine_pssh_data; - if (!audio_encryption_key_id.empty()) - widevine_pssh_data.add_key_id(audio_encryption_key_id); - if (!video_encryption_key_id.empty()) - widevine_pssh_data.add_key_id(video_encryption_key_id); + std::vector> key_ids; + if (!audio_encryption_key_id.empty()) { + key_ids.push_back(std::vector(audio_encryption_key_id.begin(), + audio_encryption_key_id.end())); + } + if (!video_encryption_key_id.empty()) { + key_ids.push_back(std::vector(video_encryption_key_id.begin(), + video_encryption_key_id.end())); + } - const std::string serialized_string = widevine_pssh_data.SerializeAsString(); - Status status = decryption_key_source_->FetchKeys( - std::vector(serialized_string.begin(), serialized_string.end())); + Status status = decryption_key_source_->FetchKeys(key_ids); if (!status.ok()) { LOG(ERROR) << "Error fetching decryption keys: " << status; return false;