Include protection_scheme in Widevine CENC request

Widevine license server added support for protection_schemes recently;
If CENC request includes protection_scheme in the CENC request, server
will return with a PSSH with protection_scheme filled in.

Change-Id: I6dcac498b5e039503d6ac0f6e057737f7c53efaf
This commit is contained in:
Kongqun Yang 2017-04-05 13:53:58 -07:00 committed by KongQun Yang
parent d9e7e2f1d0
commit 5fc6e540f3
6 changed files with 154 additions and 94 deletions

View File

@ -496,7 +496,8 @@ bool RunPackager(const StreamDescriptorList& stream_descriptors) {
FLAGS_enable_playready_encryption) { FLAGS_enable_playready_encryption) {
if (encryption_options.protection_scheme == FOURCC_NULL) if (encryption_options.protection_scheme == FOURCC_NULL)
return false; return false;
encryption_key_source = CreateEncryptionKeySource(); encryption_key_source =
CreateEncryptionKeySource(encryption_options.protection_scheme);
if (!encryption_key_source) if (!encryption_key_source)
return false; return false;
} }

View File

@ -88,11 +88,12 @@ std::unique_ptr<RequestSigner> CreateSigner() {
return signer; return signer;
} }
std::unique_ptr<KeySource> CreateEncryptionKeySource() { std::unique_ptr<KeySource> CreateEncryptionKeySource(FourCC protection_scheme) {
std::unique_ptr<KeySource> encryption_key_source; std::unique_ptr<KeySource> encryption_key_source;
if (FLAGS_enable_widevine_encryption) { if (FLAGS_enable_widevine_encryption) {
std::unique_ptr<WidevineKeySource> widevine_key_source( std::unique_ptr<WidevineKeySource> widevine_key_source(
new WidevineKeySource(FLAGS_key_server_url, FLAGS_include_common_pssh)); new WidevineKeySource(FLAGS_key_server_url, FLAGS_include_common_pssh));
widevine_key_source->set_protection_scheme(protection_scheme);
if (!FLAGS_signer.empty()) { if (!FLAGS_signer.empty()) {
std::unique_ptr<RequestSigner> request_signer(CreateSigner()); std::unique_ptr<RequestSigner> request_signer(CreateSigner());
if (!request_signer) if (!request_signer)

View File

@ -13,6 +13,8 @@
#include <memory> #include <memory>
#include "packager/media/base/fourccs.h"
DECLARE_bool(dump_stream_info); DECLARE_bool(dump_stream_info);
namespace shaka { namespace shaka {
@ -30,9 +32,11 @@ struct MuxerOptions;
/// Create KeySource based on provided command line options for content /// Create KeySource based on provided command line options for content
/// encryption. Also fetches keys. /// encryption. Also fetches keys.
/// @param protection_scheme specifies the protection scheme to be used for
/// encryption.
/// @return A std::unique_ptr containing a new KeySource, or nullptr if /// @return A std::unique_ptr containing a new KeySource, or nullptr if
/// encryption is not required. /// encryption is not required.
std::unique_ptr<KeySource> CreateEncryptionKeySource(); std::unique_ptr<KeySource> CreateEncryptionKeySource(FourCC protection_scheme);
/// Create KeySource based on provided command line options for content /// Create KeySource based on provided command line options for content
/// decryption. Does not fetch keys. /// decryption. Does not fetch keys.

View File

@ -12,6 +12,7 @@
#include "packager/base/bind.h" #include "packager/base/bind.h"
#include "packager/base/json/json_reader.h" #include "packager/base/json/json_reader.h"
#include "packager/base/json/json_writer.h" #include "packager/base/json/json_writer.h"
#include "packager/base/strings/string_number_conversions.h"
#include "packager/media/base/fixed_key_source.h" #include "packager/media/base/fixed_key_source.h"
#include "packager/media/base/http_key_fetcher.h" #include "packager/media/base/http_key_fetcher.h"
#include "packager/media/base/network_util.h" #include "packager/media/base/network_util.h"
@ -22,6 +23,7 @@
#include "packager/media/base/widevine_pssh_data.pb.h" #include "packager/media/base/widevine_pssh_data.pb.h"
namespace shaka { namespace shaka {
namespace media {
namespace { namespace {
const bool kEnableKeyRotation = true; const bool kEnableKeyRotation = true;
@ -118,9 +120,12 @@ bool GetPsshDataFromTrack(const base::DictionaryValue& track_dict,
return true; return true;
} }
} // namespace bool IsProtectionSchemeValid(FourCC protection_scheme) {
return protection_scheme == FOURCC_cenc || protection_scheme == FOURCC_cbcs ||
protection_scheme == FOURCC_cbc1 || protection_scheme == FOURCC_cens;
}
namespace media { } // namespace
WidevineKeySource::WidevineKeySource(const std::string& server_url, WidevineKeySource::WidevineKeySource(const std::string& server_url,
bool add_common_pssh) bool add_common_pssh)
@ -130,6 +135,7 @@ WidevineKeySource::WidevineKeySource(const std::string& server_url,
key_fetcher_(new HttpKeyFetcher(kKeyFetchTimeoutInSeconds)), key_fetcher_(new HttpKeyFetcher(kKeyFetchTimeoutInSeconds)),
server_url_(server_url), server_url_(server_url),
crypto_period_count_(kDefaultCryptoPeriodCount), crypto_period_count_(kDefaultCryptoPeriodCount),
protection_scheme_(FOURCC_cenc),
add_common_pssh_(add_common_pssh), add_common_pssh_(add_common_pssh),
key_production_started_(false), key_production_started_(false),
start_key_production_(base::WaitableEvent::ResetPolicy::AUTOMATIC, start_key_production_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
@ -157,6 +163,18 @@ Status WidevineKeySource::FetchKeys(const std::vector<uint8_t>& content_id,
BytesToBase64String(content_id, &content_id_base64_string); BytesToBase64String(content_id, &content_id_base64_string);
request_dict_.SetString("content_id", content_id_base64_string); request_dict_.SetString("content_id", content_id_base64_string);
request_dict_.SetString("policy", policy); request_dict_.SetString("policy", policy);
FourCC protection_scheme = protection_scheme_;
// Treat sample aes as a variant of cbcs.
if (protection_scheme == kAppleSampleAesProtectionScheme)
protection_scheme = FOURCC_cbcs;
if (IsProtectionSchemeValid(protection_scheme)) {
request_dict_.SetInteger("protection_scheme", protection_scheme);
} else {
LOG(WARNING) << "Ignore unrecognized protection scheme "
<< FourCCToString(protection_scheme);
}
return FetchKeysInternal(!kEnableKeyRotation, 0, false); return FetchKeysInternal(!kEnableKeyRotation, 0, false);
} }

View File

@ -12,6 +12,7 @@
#include "packager/base/synchronization/waitable_event.h" #include "packager/base/synchronization/waitable_event.h"
#include "packager/base/values.h" #include "packager/base/values.h"
#include "packager/media/base/closure_thread.h" #include "packager/media/base/closure_thread.h"
#include "packager/media/base/fourccs.h"
#include "packager/media/base/key_source.h" #include "packager/media/base/key_source.h"
namespace shaka { namespace shaka {
@ -53,6 +54,11 @@ class WidevineKeySource : public KeySource {
Status FetchKeys(const std::vector<uint8_t>& content_id, Status FetchKeys(const std::vector<uint8_t>& content_id,
const std::string& policy); const std::string& policy);
/// Set the protection scheme for the key source.
void set_protection_scheme(FourCC protection_scheme) {
protection_scheme_ = protection_scheme;
}
/// Set signer for the key source. /// Set signer for the key source.
/// @param signer signs the request message. /// @param signer signs the request message.
void set_signer(std::unique_ptr<RequestSigner> signer); void set_signer(std::unique_ptr<RequestSigner> signer);
@ -61,9 +67,6 @@ class WidevineKeySource : public KeySource {
/// @param key_fetcher points to the @b KeyFetcher object to be injected. /// @param key_fetcher points to the @b KeyFetcher object to be injected.
void set_key_fetcher(std::unique_ptr<KeyFetcher> key_fetcher); void set_key_fetcher(std::unique_ptr<KeyFetcher> key_fetcher);
protected:
ClosureThread key_production_thread_;
private: private:
typedef std::map<TrackType, std::unique_ptr<EncryptionKey>> EncryptionKeyMap; typedef std::map<TrackType, std::unique_ptr<EncryptionKey>> EncryptionKeyMap;
typedef ProducerConsumerQueue<std::shared_ptr<EncryptionKeyMap>> typedef ProducerConsumerQueue<std::shared_ptr<EncryptionKeyMap>>
@ -104,6 +107,7 @@ class WidevineKeySource : public KeySource {
// Push the keys to the key pool. // Push the keys to the key pool.
bool PushToKeyPool(EncryptionKeyMap* encryption_key_map); bool PushToKeyPool(EncryptionKeyMap* encryption_key_map);
ClosureThread key_production_thread_;
// The fetcher object used to fetch keys from the license service. // The fetcher object used to fetch keys from the license service.
// It is initialized to a default fetcher on class initialization. // It is initialized to a default fetcher on class initialization.
// Can be overridden using set_key_fetcher for testing or other purposes. // Can be overridden using set_key_fetcher for testing or other purposes.
@ -113,6 +117,7 @@ class WidevineKeySource : public KeySource {
base::DictionaryValue request_dict_; base::DictionaryValue request_dict_;
const uint32_t crypto_period_count_; const uint32_t crypto_period_count_;
FourCC protection_scheme_;
base::Lock lock_; base::Lock lock_;
bool add_common_pssh_; bool add_common_pssh_;
bool key_production_started_; bool key_production_started_;

View File

@ -19,13 +19,19 @@
#include "packager/media/base/widevine_key_source.h" #include "packager/media/base/widevine_key_source.h"
using ::testing::_; using ::testing::_;
using ::testing::Bool;
using ::testing::Combine;
using ::testing::DoAll; using ::testing::DoAll;
using ::testing::InSequence; using ::testing::InSequence;
using ::testing::Return; using ::testing::Return;
using ::testing::SetArgPointee; using ::testing::SetArgPointee;
using ::testing::StrEq; using ::testing::StrEq;
using ::testing::Test;
using ::testing::Values;
using ::testing::WithParamInterface;
namespace shaka { namespace shaka {
namespace media {
namespace { namespace {
const char kServerUrl[] = "http://www.foo.com/getcontentkey"; const char kServerUrl[] = "http://www.foo.com/getcontentkey";
const char kContentId[] = "ContentFoo"; const char kContentId[] = "ContentFoo";
@ -45,6 +51,7 @@ const char kLicenseStatusUnknownError[] = "UNKNOWN_ERROR";
const char kExpectedRequestMessageFormat[] = const char kExpectedRequestMessageFormat[] =
"{\"content_id\":\"%s\",\"drm_types\":[\"WIDEVINE\"],\"policy\":\"%s\"," "{\"content_id\":\"%s\",\"drm_types\":[\"WIDEVINE\"],\"policy\":\"%s\","
"\"protection_scheme\":%d,"
"\"tracks\":[{\"type\":\"SD\"},{\"type\":\"HD\"},{\"type\":\"UHD1\"}," "\"tracks\":[{\"type\":\"SD\"},{\"type\":\"HD\"},{\"type\":\"UHD1\"},"
"{\"type\":\"UHD2\"},{\"type\":\"AUDIO\"}]}"; "{\"type\":\"UHD2\"},{\"type\":\"AUDIO\"}]}";
const char kExpectedRequestMessageWithAssetIdFormat[] = const char kExpectedRequestMessageWithAssetIdFormat[] =
@ -64,11 +71,12 @@ const char kClassicTrackFormat[] = "{\"type\":\"%s\",\"key\":\"%s\"}";
const char kLicenseResponseFormat[] = "{\"status\":\"%s\",\"tracks\":[%s]}"; const char kLicenseResponseFormat[] = "{\"status\":\"%s\",\"tracks\":[%s]}";
const char kHttpResponseFormat[] = "{\"response\":\"%s\"}"; const char kHttpResponseFormat[] = "{\"response\":\"%s\"}";
const uint8_t kRequestPsshBox[] = { const uint8_t kRequestPsshBox[] = {
0, 0, 0, 41, 'p', 's', 's', 'h', 0, 0, 0, 0, 0, 0, 44, 'p', 's', 's', 'h', 0, 0, 0,
0, 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8, 0, 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8,
0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, 0, 0, 0, 0x09, 'P', 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, 0, 0, 0, 12, 0x22,
'S', 'S', 'H', ' ', 'd', 'a', 't', 'a'}; 0x0a, 'C', 'o', 'n', 't', 'e', 'n', 't', 'F', 'o', 'o'};
const char kRequestPsshData[] = "PSSH data"; const char kRequestPsshData[] = {0x22, 0x0a, 'C', 'o', 'n', 't', 'e',
'n', 't', 'F', 'o', 'o', '\0'};
const uint8_t kRequestPsshDataFromKeyIds[] = {0x12, 0x06, 0x00, 0x01, const uint8_t kRequestPsshDataFromKeyIds[] = {0x12, 0x06, 0x00, 0x01,
0x02, 0x03, 0x04, 0x05}; 0x02, 0x03, 0x04, 0x05};
const uint8_t kRequestKeyId[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; const uint8_t kRequestKeyId[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
@ -98,8 +106,8 @@ std::string GetMockKey(const std::string& track_type) {
return "MockKey" + track_type; return "MockKey" + track_type;
} }
std::string GetMockPsshData(const std::string& track_type) { std::string GetMockPsshData() {
return "MockPsshData" + track_type; return kRequestPsshData;
} }
std::string GenerateMockLicenseResponse() { std::string GenerateMockLicenseResponse() {
@ -108,12 +116,11 @@ std::string GenerateMockLicenseResponse() {
for (size_t i = 0; i < 5; ++i) { for (size_t i = 0; i < 5; ++i) {
if (!tracks.empty()) if (!tracks.empty())
tracks += ","; tracks += ",";
tracks += base::StringPrintf( tracks +=
kTrackFormat, base::StringPrintf(kTrackFormat, kTrackTypes[i].c_str(),
kTrackTypes[i].c_str(), Base64Encode(GetMockKeyId(kTrackTypes[i])).c_str(),
Base64Encode(GetMockKeyId(kTrackTypes[i])).c_str(), Base64Encode(GetMockKey(kTrackTypes[i])).c_str(),
Base64Encode(GetMockKey(kTrackTypes[i])).c_str(), Base64Encode(GetMockPsshData()).c_str());
Base64Encode(GetMockPsshData(kTrackTypes[i])).c_str());
} }
return base::StringPrintf(kLicenseResponseFormat, "OK", tracks.c_str()); return base::StringPrintf(kLicenseResponseFormat, "OK", tracks.c_str());
} }
@ -134,8 +141,6 @@ std::string GenerateMockClassicLicenseResponse() {
} // namespace } // namespace
namespace media {
class MockRequestSigner : public RequestSigner { class MockRequestSigner : public RequestSigner {
public: public:
explicit MockRequestSigner(const std::string& signer_name) explicit MockRequestSigner(const std::string& signer_name)
@ -163,8 +168,7 @@ class MockKeyFetcher : public KeyFetcher {
DISALLOW_COPY_AND_ASSIGN(MockKeyFetcher); DISALLOW_COPY_AND_ASSIGN(MockKeyFetcher);
}; };
class WidevineKeySourceTest : public ::testing::Test, class WidevineKeySourceTest : public Test {
public ::testing::WithParamInterface<bool> {
public: public:
WidevineKeySourceTest() WidevineKeySourceTest()
: mock_request_signer_(new MockRequestSigner(kSignerName)), : mock_request_signer_(new MockRequestSigner(kSignerName)),
@ -177,8 +181,17 @@ class WidevineKeySourceTest : public ::testing::Test,
} }
protected: protected:
FourCC GetExpectedProtectionScheme() {
// Apple SAMPLE-AES is considered as a variation of cbcs.
if (protection_scheme_ == kAppleSampleAesProtectionScheme)
return FOURCC_cbcs;
return protection_scheme_;
}
void CreateWidevineKeySource() { void CreateWidevineKeySource() {
widevine_key_source_.reset(new WidevineKeySource(kServerUrl, GetParam())); widevine_key_source_.reset(
new WidevineKeySource(kServerUrl, add_common_pssh_));
widevine_key_source_->set_protection_scheme(protection_scheme_);
widevine_key_source_->set_key_fetcher(std::move(mock_key_fetcher_)); widevine_key_source_->set_key_fetcher(std::move(mock_key_fetcher_));
} }
@ -191,13 +204,14 @@ class WidevineKeySourceTest : public ::testing::Test,
&encryption_key)); &encryption_key));
EXPECT_EQ(GetMockKey(kTrackTypes[i]), ToString(encryption_key.key)); EXPECT_EQ(GetMockKey(kTrackTypes[i]), ToString(encryption_key.key));
if (!classic) { if (!classic) {
ASSERT_EQ(GetParam() ? 2u : 1u, encryption_key.key_system_info.size()); ASSERT_EQ(add_common_pssh_ ? 2u : 1u,
encryption_key.key_system_info.size());
EXPECT_EQ(GetMockKeyId(kTrackTypes[i]), EXPECT_EQ(GetMockKeyId(kTrackTypes[i]),
ToString(encryption_key.key_id)); ToString(encryption_key.key_id));
EXPECT_EQ(GetMockPsshData(kTrackTypes[i]), EXPECT_EQ(GetMockPsshData(),
ToString(encryption_key.key_system_info[0].pssh_data())); ToString(encryption_key.key_system_info[0].pssh_data()));
if (GetParam()) { if (add_common_pssh_) {
// Each of the keys contains all the key IDs. // Each of the keys contains all the key IDs.
const std::vector<uint8_t> common_system_id( const std::vector<uint8_t> common_system_id(
kCommonSystemId, kCommonSystemId + arraysize(kCommonSystemId)); kCommonSystemId, kCommonSystemId + arraysize(kCommonSystemId));
@ -222,12 +236,14 @@ class WidevineKeySourceTest : public ::testing::Test,
std::unique_ptr<MockKeyFetcher> mock_key_fetcher_; std::unique_ptr<MockKeyFetcher> mock_key_fetcher_;
std::unique_ptr<WidevineKeySource> widevine_key_source_; std::unique_ptr<WidevineKeySource> widevine_key_source_;
std::vector<uint8_t> content_id_; std::vector<uint8_t> content_id_;
bool add_common_pssh_ = false;
FourCC protection_scheme_ = FOURCC_cenc;
private: private:
DISALLOW_COPY_AND_ASSIGN(WidevineKeySourceTest); DISALLOW_COPY_AND_ASSIGN(WidevineKeySourceTest);
}; };
TEST_P(WidevineKeySourceTest, GetTrackTypeFromString) { TEST_F(WidevineKeySourceTest, GetTrackTypeFromString) {
EXPECT_EQ(KeySource::TRACK_TYPE_SD, EXPECT_EQ(KeySource::TRACK_TYPE_SD,
KeySource::GetTrackTypeFromString("SD")); KeySource::GetTrackTypeFromString("SD"));
EXPECT_EQ(KeySource::TRACK_TYPE_HD, EXPECT_EQ(KeySource::TRACK_TYPE_HD,
@ -242,7 +258,7 @@ TEST_P(WidevineKeySourceTest, GetTrackTypeFromString) {
KeySource::GetTrackTypeFromString("FOO")); KeySource::GetTrackTypeFromString("FOO"));
} }
TEST_P(WidevineKeySourceTest, GenerateSignatureFailure) { TEST_F(WidevineKeySourceTest, GenerateSignatureFailure) {
EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _)) EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _))
.WillOnce(Return(false)); .WillOnce(Return(false));
@ -252,11 +268,70 @@ TEST_P(WidevineKeySourceTest, GenerateSignatureFailure) {
widevine_key_source_->FetchKeys(content_id_, kPolicy)); widevine_key_source_->FetchKeys(content_id_, kPolicy));
} }
TEST_F(WidevineKeySourceTest, RetryOnHttpTimeout) {
std::string mock_response = base::StringPrintf(
kHttpResponseFormat, Base64Encode(GenerateMockLicenseResponse()).c_str());
// Retry is expected on HTTP timeout.
EXPECT_CALL(*mock_key_fetcher_, FetchKeys(_, _, _))
.WillOnce(Return(Status(error::TIME_OUT, "")))
.WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK)));
CreateWidevineKeySource();
ASSERT_OK(widevine_key_source_->FetchKeys(content_id_, kPolicy));
VerifyKeys(false);
}
TEST_F(WidevineKeySourceTest, RetryOnTransientError) {
std::string mock_license_status = base::StringPrintf(
kLicenseResponseFormat, kLicenseStatusTransientError, "");
std::string mock_response = base::StringPrintf(
kHttpResponseFormat, Base64Encode(mock_license_status).c_str());
std::string expected_retried_response = base::StringPrintf(
kHttpResponseFormat, Base64Encode(GenerateMockLicenseResponse()).c_str());
// Retry is expected on transient error.
EXPECT_CALL(*mock_key_fetcher_, FetchKeys(_, _, _))
.WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK)))
.WillOnce(DoAll(SetArgPointee<2>(expected_retried_response),
Return(Status::OK)));
CreateWidevineKeySource();
ASSERT_OK(widevine_key_source_->FetchKeys(content_id_, kPolicy));
VerifyKeys(false);
}
TEST_F(WidevineKeySourceTest, NoRetryOnUnknownError) {
std::string mock_license_status = base::StringPrintf(
kLicenseResponseFormat, kLicenseStatusUnknownError, "");
std::string mock_response = base::StringPrintf(
kHttpResponseFormat, Base64Encode(mock_license_status).c_str());
EXPECT_CALL(*mock_key_fetcher_, FetchKeys(_, _, _))
.WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK)));
CreateWidevineKeySource();
ASSERT_EQ(error::SERVER_ERROR,
widevine_key_source_->FetchKeys(content_id_, kPolicy).error_code());
}
class WidevineKeySourceParameterizedTest
: public WidevineKeySourceTest,
public WithParamInterface<std::tr1::tuple<bool, FourCC>> {
public:
WidevineKeySourceParameterizedTest() {
add_common_pssh_ = std::tr1::get<0>(GetParam());
protection_scheme_ = std::tr1::get<1>(GetParam());
}
};
// Check whether expected request message and post data was generated and // Check whether expected request message and post data was generated and
// verify the correct behavior on http failure. // verify the correct behavior on http failure.
TEST_P(WidevineKeySourceTest, HttpFetchFailure) { TEST_P(WidevineKeySourceParameterizedTest, HttpFetchFailure) {
std::string expected_message = base::StringPrintf( std::string expected_message = base::StringPrintf(
kExpectedRequestMessageFormat, Base64Encode(kContentId).c_str(), kPolicy); kExpectedRequestMessageFormat, Base64Encode(kContentId).c_str(), kPolicy,
GetExpectedProtectionScheme());
EXPECT_CALL(*mock_request_signer_, EXPECT_CALL(*mock_request_signer_,
GenerateSignature(StrEq(expected_message), _)) GenerateSignature(StrEq(expected_message), _))
.WillOnce(DoAll(SetArgPointee<1>(kMockSignature), Return(true))); .WillOnce(DoAll(SetArgPointee<1>(kMockSignature), Return(true)));
@ -277,7 +352,7 @@ TEST_P(WidevineKeySourceTest, HttpFetchFailure) {
widevine_key_source_->FetchKeys(content_id_, kPolicy)); widevine_key_source_->FetchKeys(content_id_, kPolicy));
} }
TEST_P(WidevineKeySourceTest, LicenseStatusCencOK) { TEST_P(WidevineKeySourceParameterizedTest, LicenseStatusCencOK) {
std::string mock_response = base::StringPrintf( std::string mock_response = base::StringPrintf(
kHttpResponseFormat, Base64Encode(GenerateMockLicenseResponse()).c_str()); kHttpResponseFormat, Base64Encode(GenerateMockLicenseResponse()).c_str());
@ -289,7 +364,7 @@ TEST_P(WidevineKeySourceTest, LicenseStatusCencOK) {
VerifyKeys(false); VerifyKeys(false);
} }
TEST_P(WidevineKeySourceTest, LicenseStatusCencNotOK) { TEST_P(WidevineKeySourceParameterizedTest, LicenseStatusCencNotOK) {
std::string mock_response = base::StringPrintf( std::string mock_response = base::StringPrintf(
kHttpResponseFormat, Base64Encode( kHttpResponseFormat, Base64Encode(
GenerateMockClassicLicenseResponse()).c_str()); GenerateMockClassicLicenseResponse()).c_str());
@ -303,7 +378,7 @@ TEST_P(WidevineKeySourceTest, LicenseStatusCencNotOK) {
.error_code()); .error_code());
} }
TEST_P(WidevineKeySourceTest, LicenseStatusCencWithPsshBoxOK) { TEST_P(WidevineKeySourceParameterizedTest, LicenseStatusCencWithPsshBoxOK) {
std::string expected_message = std::string expected_message =
base::StringPrintf(kExpectedRequestMessageWithPsshFormat, base::StringPrintf(kExpectedRequestMessageWithPsshFormat,
Base64Encode(kRequestPsshData).c_str()); Base64Encode(kRequestPsshData).c_str());
@ -324,7 +399,7 @@ TEST_P(WidevineKeySourceTest, LicenseStatusCencWithPsshBoxOK) {
VerifyKeys(false); VerifyKeys(false);
} }
TEST_P(WidevineKeySourceTest, LicenseStatusCencWithKeyIdOK) { TEST_P(WidevineKeySourceParameterizedTest, LicenseStatusCencWithKeyIdsOK) {
std::string expected_pssh_data( std::string expected_pssh_data(
kRequestPsshDataFromKeyIds, kRequestPsshDataFromKeyIds,
kRequestPsshDataFromKeyIds + arraysize(kRequestPsshDataFromKeyIds)); kRequestPsshDataFromKeyIds + arraysize(kRequestPsshDataFromKeyIds));
@ -348,7 +423,7 @@ TEST_P(WidevineKeySourceTest, LicenseStatusCencWithKeyIdOK) {
VerifyKeys(false); VerifyKeys(false);
} }
TEST_P(WidevineKeySourceTest, LicenseStatusClassicOK) { TEST_P(WidevineKeySourceParameterizedTest, LicenseStatusClassicOK) {
std::string expected_message = base::StringPrintf( std::string expected_message = base::StringPrintf(
kExpectedRequestMessageWithAssetIdFormat, kClassicAssetId); kExpectedRequestMessageWithAssetIdFormat, kClassicAssetId);
EXPECT_CALL(*mock_request_signer_, EXPECT_CALL(*mock_request_signer_,
@ -370,59 +445,12 @@ TEST_P(WidevineKeySourceTest, LicenseStatusClassicOK) {
VerifyKeys(true); VerifyKeys(true);
} }
TEST_P(WidevineKeySourceTest, RetryOnHttpTimeout) {
std::string mock_response = base::StringPrintf(
kHttpResponseFormat, Base64Encode(GenerateMockLicenseResponse()).c_str());
// Retry is expected on HTTP timeout.
EXPECT_CALL(*mock_key_fetcher_, FetchKeys(_, _, _))
.WillOnce(Return(Status(error::TIME_OUT, "")))
.WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK)));
CreateWidevineKeySource();
ASSERT_OK(widevine_key_source_->FetchKeys(content_id_, kPolicy));
VerifyKeys(false);
}
TEST_P(WidevineKeySourceTest, RetryOnTransientError) {
std::string mock_license_status = base::StringPrintf(
kLicenseResponseFormat, kLicenseStatusTransientError, "");
std::string mock_response = base::StringPrintf(
kHttpResponseFormat, Base64Encode(mock_license_status).c_str());
std::string expected_retried_response = base::StringPrintf(
kHttpResponseFormat, Base64Encode(GenerateMockLicenseResponse()).c_str());
// Retry is expected on transient error.
EXPECT_CALL(*mock_key_fetcher_, FetchKeys(_, _, _))
.WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK)))
.WillOnce(DoAll(SetArgPointee<2>(expected_retried_response),
Return(Status::OK)));
CreateWidevineKeySource();
ASSERT_OK(widevine_key_source_->FetchKeys(content_id_, kPolicy));
VerifyKeys(false);
}
TEST_P(WidevineKeySourceTest, NoRetryOnUnknownError) {
std::string mock_license_status = base::StringPrintf(
kLicenseResponseFormat, kLicenseStatusUnknownError, "");
std::string mock_response = base::StringPrintf(
kHttpResponseFormat, Base64Encode(mock_license_status).c_str());
EXPECT_CALL(*mock_key_fetcher_, FetchKeys(_, _, _))
.WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK)));
CreateWidevineKeySource();
ASSERT_EQ(error::SERVER_ERROR,
widevine_key_source_->FetchKeys(content_id_, kPolicy).error_code());
}
namespace { namespace {
const char kCryptoPeriodRequestMessageFormat[] = const char kCryptoPeriodRequestMessageFormat[] =
"{\"content_id\":\"%s\",\"crypto_period_count\":%u,\"drm_types\":[" "{\"content_id\":\"%s\",\"crypto_period_count\":%u,\"drm_types\":["
"\"WIDEVINE\"],\"first_crypto_period_index\":%u,\"policy\":\"%s\"," "\"WIDEVINE\"],\"first_crypto_period_index\":%u,\"policy\":\"%s\","
"\"protection_scheme\":%d,"
"\"tracks\":[{\"type\":\"SD\"},{\"type\":\"HD\"},{\"type\":\"UHD1\"}," "\"tracks\":[{\"type\":\"SD\"},{\"type\":\"HD\"},{\"type\":\"UHD1\"},"
"{\"type\":\"UHD2\"},{\"type\":\"AUDIO\"}]}"; "{\"type\":\"UHD2\"},{\"type\":\"AUDIO\"}]}";
@ -459,7 +487,7 @@ std::string GenerateMockKeyRotationLicenseResponse(
} // namespace } // namespace
TEST_P(WidevineKeySourceTest, KeyRotationTest) { TEST_P(WidevineKeySourceParameterizedTest, KeyRotationTest) {
const uint32_t kFirstCryptoPeriodIndex = 8; const uint32_t kFirstCryptoPeriodIndex = 8;
const uint32_t kCryptoPeriodCount = 10; const uint32_t kCryptoPeriodCount = 10;
// Array of indexes to be checked. // Array of indexes to be checked.
@ -482,12 +510,10 @@ TEST_P(WidevineKeySourceTest, KeyRotationTest) {
for (uint32_t i = 0; i < kCryptoIterations; ++i) { for (uint32_t i = 0; i < kCryptoIterations; ++i) {
uint32_t first_crypto_period_index = uint32_t first_crypto_period_index =
kFirstCryptoPeriodIndex - 1 + i * kCryptoPeriodCount; kFirstCryptoPeriodIndex - 1 + i * kCryptoPeriodCount;
std::string expected_message = std::string expected_message = base::StringPrintf(
base::StringPrintf(kCryptoPeriodRequestMessageFormat, kCryptoPeriodRequestMessageFormat, Base64Encode(kContentId).c_str(),
Base64Encode(kContentId).c_str(), kCryptoPeriodCount, first_crypto_period_index, kPolicy,
kCryptoPeriodCount, GetExpectedProtectionScheme());
first_crypto_period_index,
kPolicy);
EXPECT_CALL(*mock_request_signer_, GenerateSignature(expected_message, _)) EXPECT_CALL(*mock_request_signer_, GenerateSignature(expected_message, _))
.WillOnce(DoAll(SetArgPointee<1>(kMockSignature), Return(true))); .WillOnce(DoAll(SetArgPointee<1>(kMockSignature), Return(true)));
@ -526,8 +552,13 @@ TEST_P(WidevineKeySourceTest, KeyRotationTest) {
} }
INSTANTIATE_TEST_CASE_P(WidevineKeySourceInstance, INSTANTIATE_TEST_CASE_P(WidevineKeySourceInstance,
WidevineKeySourceTest, WidevineKeySourceParameterizedTest,
::testing::Bool()); Combine(Bool(),
Values(FOURCC_cenc,
FOURCC_cbcs,
FOURCC_cens,
FOURCC_cbc1,
kAppleSampleAesProtectionScheme)));
} // namespace media } // namespace media
} // namespace shaka } // namespace shaka