Merge "Add Support for WebM Back" into klp-modular-dev

This commit is contained in:
John "Juce" Bruce
2014-03-31 23:46:50 +00:00
committed by Android (Google) Code Review
16 changed files with 329 additions and 105 deletions

View File

@@ -37,6 +37,7 @@ class CdmEngine : public TimerHandler {
// Construct a valid license request // Construct a valid license request
virtual CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id, virtual CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id,
const CdmKeySetId& key_set_id, const CdmKeySetId& key_set_id,
const std::string& mime_type,
const CdmInitData& init_data, const CdmInitData& init_data,
const CdmLicenseType license_type, const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters, CdmAppParameterMap& app_parameters,

View File

@@ -36,7 +36,8 @@ class CdmSession {
bool VerifySession(const CdmKeySystem& key_system, bool VerifySession(const CdmKeySystem& key_system,
const CdmInitData& init_data); const CdmInitData& init_data);
CdmResponseType GenerateKeyRequest(const CdmInitData& init_data, CdmResponseType GenerateKeyRequest(const std::string& mime_type,
const CdmInitData& init_data,
const CdmLicenseType license_type, const CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters, const CdmAppParameterMap& app_parameters,
CdmKeyMessage* key_request, CdmKeyMessage* key_request,
@@ -109,7 +110,7 @@ class CdmSession {
CdmLicenseType license_type_; CdmLicenseType license_type_;
// license type offline related information // license type offline related information
CdmInitData offline_pssh_data_; CdmInitData offline_init_data_;
CdmKeyMessage offline_key_request_; CdmKeyMessage offline_key_request_;
CdmKeyResponse offline_key_response_; CdmKeyResponse offline_key_response_;
CdmKeyMessage offline_key_renewal_request_; CdmKeyMessage offline_key_renewal_request_;

View File

@@ -27,7 +27,8 @@ class CdmLicense {
bool Init(const std::string& token, CryptoSession* session, bool Init(const std::string& token, CryptoSession* session,
PolicyEngine* policy_engine); PolicyEngine* policy_engine);
bool PrepareKeyRequest(const CdmInitData& pssh_data, bool PrepareKeyRequest(const std::string& mime_type,
const CdmInitData& init_data,
const CdmLicenseType license_type, const CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters, const CdmAppParameterMap& app_parameters,
const CdmSessionId& session_id, const CdmSessionId& session_id,
@@ -42,7 +43,7 @@ class CdmLicense {
bool RestoreOfflineLicense(CdmKeyMessage& license_request, bool RestoreOfflineLicense(CdmKeyMessage& license_request,
CdmKeyResponse& license_response, CdmKeyResponse& license_response,
CdmKeyResponse& license_renewal_response); CdmKeyResponse& license_renewal_response);
bool HasInitData() { return !init_data_.empty(); } bool HasInitData() { return !stored_init_data_.empty(); }
bool IsKeyLoaded(const KeyId& key_id); bool IsKeyLoaded(const KeyId& key_id);
private: private:
@@ -54,12 +55,16 @@ class CdmLicense {
CdmResponseType HandleKeyErrorResponse( CdmResponseType HandleKeyErrorResponse(
const video_widevine_server::sdk::SignedMessage& signed_message); const video_widevine_server::sdk::SignedMessage& signed_message);
template<typename T> bool PrepareContentId(const CdmLicenseType license_type,
const std::string& request_id,
T* content_id);
CryptoSession* session_; CryptoSession* session_;
PolicyEngine* policy_engine_; PolicyEngine* policy_engine_;
std::string server_url_; std::string server_url_;
std::string token_; std::string token_;
std::string service_certificate_; std::string service_certificate_;
std::string init_data_; std::string stored_init_data_;
bool initialized_; bool initialized_;
std::set<KeyId> loaded_keys_; std::set<KeyId> loaded_keys_;

View File

@@ -56,6 +56,8 @@ static const std::string QUERY_VALUE_SECURITY_LEVEL_L2 = "L2";
static const std::string QUERY_VALUE_SECURITY_LEVEL_L3 = "L3"; static const std::string QUERY_VALUE_SECURITY_LEVEL_L3 = "L3";
static const std::string QUERY_VALUE_SECURITY_LEVEL_Unknown = "Unknown"; static const std::string QUERY_VALUE_SECURITY_LEVEL_Unknown = "Unknown";
static const std::string ISO_BMFF_MIME_TYPE = "video/mp4";
static const std::string WEBM_MIME_TYPE = "video/webm";
} // namespace wvcdm } // namespace wvcdm
#endif // CDM_BASE_WV_CDM_CONSTANTS_H_ #endif // CDM_BASE_WV_CDM_CONSTANTS_H_

View File

@@ -124,6 +124,7 @@ CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
CdmResponseType CdmEngine::GenerateKeyRequest( CdmResponseType CdmEngine::GenerateKeyRequest(
const CdmSessionId& session_id, const CdmSessionId& session_id,
const CdmKeySetId& key_set_id, const CdmKeySetId& key_set_id,
const std::string& mime_type,
const CdmInitData& init_data, const CdmInitData& init_data,
const CdmLicenseType license_type, const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters, CdmAppParameterMap& app_parameters,
@@ -179,7 +180,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
} }
} }
sts = iter->second->GenerateKeyRequest(init_data, license_type, sts = iter->second->GenerateKeyRequest(mime_type, init_data, license_type,
app_parameters, key_request, app_parameters, key_request,
server_url); server_url);

View File

@@ -79,7 +79,7 @@ CdmResponseType CdmSession::RestoreOfflineSession(
DeviceFiles::LicenseState license_state; DeviceFiles::LicenseState license_state;
if (!handle.RetrieveLicense(key_set_id, &license_state, &offline_pssh_data_, if (!handle.RetrieveLicense(key_set_id, &license_state, &offline_init_data_,
&offline_key_request_, &offline_key_response_, &offline_key_request_, &offline_key_response_,
&offline_key_renewal_request_, &offline_key_renewal_request_,
&offline_key_renewal_response_, &offline_key_renewal_response_,
@@ -119,9 +119,9 @@ bool CdmSession::VerifySession(const CdmKeySystem& key_system,
} }
CdmResponseType CdmSession::GenerateKeyRequest( CdmResponseType CdmSession::GenerateKeyRequest(
const CdmInitData& init_data, const CdmLicenseType license_type, const std::string& mime_type, const CdmInitData& init_data,
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request, const CdmLicenseType license_type, const CdmAppParameterMap& app_parameters,
std::string* server_url) { CdmKeyMessage* key_request, std::string* server_url) {
if (reinitialize_session_) { if (reinitialize_session_) {
CdmResponseType sts = Init(); CdmResponseType sts = Init();
if (sts != NO_ERROR) { if (sts != NO_ERROR) {
@@ -149,14 +149,18 @@ CdmResponseType CdmSession::GenerateKeyRequest(
? UNKNOWN_ERROR ? UNKNOWN_ERROR
: GenerateRenewalRequest(key_request, server_url); : GenerateRenewalRequest(key_request, server_url);
} else { } else {
if (mime_type != ISO_BMFF_MIME_TYPE && mime_type != WEBM_MIME_TYPE) {
LOGW("CdmSession::GenerateKeyRequest: unknown MIME type");
return KEY_ERROR;
}
if (init_data.empty() && !license_parser_.HasInitData()) { if (init_data.empty() && !license_parser_.HasInitData()) {
LOGW("CdmSession::GenerateKeyRequest: init data absent"); LOGW("CdmSession::GenerateKeyRequest: init data absent");
return KEY_ERROR; return KEY_ERROR;
} }
CdmInitData pssh_data = init_data; CdmInitData extracted_init_data = init_data;
if (Properties::extract_pssh_data()) { if (Properties::extract_pssh_data() && mime_type == ISO_BMFF_MIME_TYPE) {
if (!CdmEngine::ExtractWidevinePssh(init_data, &pssh_data)) { if (!CdmEngine::ExtractWidevinePssh(init_data, &extracted_init_data)) {
return KEY_ERROR; return KEY_ERROR;
} }
} }
@@ -172,14 +176,15 @@ CdmResponseType CdmSession::GenerateKeyRequest(
} }
} }
if (!license_parser_.PrepareKeyRequest(pssh_data, license_type, if (!license_parser_.PrepareKeyRequest(mime_type, extracted_init_data,
app_parameters, session_id_, license_type, app_parameters,
key_request, server_url)) { session_id_, key_request,
server_url)) {
return KEY_ERROR; return KEY_ERROR;
} }
if (license_type_ == kLicenseTypeOffline) { if (license_type_ == kLicenseTypeOffline) {
offline_pssh_data_ = pssh_data; offline_init_data_ = extracted_init_data;
offline_key_request_ = *key_request; offline_key_request_ = *key_request;
offline_release_server_url_ = *server_url; offline_release_server_url_ = *server_url;
} }
@@ -410,7 +415,7 @@ bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
return false; return false;
return handle.StoreLicense( return handle.StoreLicense(
key_set_id_, state, offline_pssh_data_, offline_key_request_, key_set_id_, state, offline_init_data_, offline_key_request_,
offline_key_response_, offline_key_renewal_request_, offline_key_response_, offline_key_renewal_request_,
offline_key_renewal_response_, offline_release_server_url_); offline_key_renewal_response_, offline_release_server_url_);
} }

View File

@@ -84,6 +84,7 @@ using video_widevine_server::sdk::EncryptedClientIdentification;
using video_widevine_server::sdk::LicenseRequest; using video_widevine_server::sdk::LicenseRequest;
using video_widevine_server::sdk::LicenseRequest_ContentIdentification; using video_widevine_server::sdk::LicenseRequest_ContentIdentification;
using video_widevine_server::sdk::LicenseRequest_ContentIdentification_CENC; using video_widevine_server::sdk::LicenseRequest_ContentIdentification_CENC;
using video_widevine_server::sdk::LicenseRequest_ContentIdentification_WebM;
using video_widevine_server::sdk:: using video_widevine_server::sdk::
LicenseRequest_ContentIdentification_ExistingLicense; LicenseRequest_ContentIdentification_ExistingLicense;
using video_widevine_server::sdk::License; using video_widevine_server::sdk::License;
@@ -157,7 +158,8 @@ bool CdmLicense::Init(const std::string& token, CryptoSession* session,
return true; return true;
} }
bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data, bool CdmLicense::PrepareKeyRequest(const std::string& mime_type,
const CdmInitData& init_data,
const CdmLicenseType license_type, const CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters, const CdmAppParameterMap& app_parameters,
const CdmSessionId& session_id, const CdmSessionId& session_id,
@@ -167,7 +169,12 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
LOGE("CdmLicense::PrepareKeyRequest: not initialized"); LOGE("CdmLicense::PrepareKeyRequest: not initialized");
return false; return false;
} }
if (init_data.empty() && init_data_.empty()) { if (mime_type != ISO_BMFF_MIME_TYPE && mime_type != WEBM_MIME_TYPE) {
LOGE("CdmLicense::PrepareKeyRequest: unsupported MIME type %s",
mime_type.c_str());
return false;
}
if (init_data.empty() && stored_init_data_.empty()) {
LOGE("CdmLicense::PrepareKeyRequest: empty init data provided"); LOGE("CdmLicense::PrepareKeyRequest: empty init data provided");
return false; return false;
} }
@@ -192,7 +199,7 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
serialized_service_certificate = service_certificate_; serialized_service_certificate = service_certificate_;
if (privacy_mode_enabled && serialized_service_certificate.empty()) { if (privacy_mode_enabled && serialized_service_certificate.empty()) {
init_data_ = init_data; stored_init_data_ = init_data;
return PrepareServiceCertificateRequest(signed_request, server_url); return PrepareServiceCertificateRequest(signed_request, server_url);
} }
@@ -308,33 +315,44 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
LicenseRequest_ContentIdentification* content_id = LicenseRequest_ContentIdentification* content_id =
license_request.mutable_content_id(); license_request.mutable_content_id();
LicenseRequest_ContentIdentification_CENC* cenc_content_id = if (mime_type == ISO_BMFF_MIME_TYPE) {
content_id->mutable_cenc_id(); LicenseRequest_ContentIdentification_CENC* cenc_content_id =
content_id->mutable_cenc_id();
if (!init_data.empty()) { if (!init_data.empty()) {
cenc_content_id->add_pssh(init_data); cenc_content_id->add_pssh(init_data);
} else if (privacy_mode_enabled && !init_data_.empty()) { } else if (privacy_mode_enabled && !stored_init_data_.empty()) {
cenc_content_id->add_pssh(init_data_); cenc_content_id->add_pssh(stored_init_data_);
} else {
LOGE("CdmLicense::PrepareKeyRequest: ISO-CENC init data not available");
return false;
}
if (!PrepareContentId(license_type, request_id, cenc_content_id)) {
return false;
}
} else if (mime_type == WEBM_MIME_TYPE) {
LicenseRequest_ContentIdentification_WebM* webm_content_id =
content_id->mutable_webm_id();
if (!init_data.empty()) {
webm_content_id->set_header(init_data);
} else if (privacy_mode_enabled && !stored_init_data_.empty()) {
webm_content_id->set_header(stored_init_data_);
} else {
LOGE("CdmLicense::PrepareKeyRequest: WebM init data not available");
return false;
}
if (!PrepareContentId(license_type, request_id, webm_content_id)) {
return false;
}
} else { } else {
LOGD("CdmLicense::PrepareKeyRequest: init data not available"); LOGE("CdmLicense::PrepareKeyRequest: no support for MIME type %s",
mime_type.c_str());
return false; return false;
} }
switch (license_type) {
case kLicenseTypeOffline:
cenc_content_id->set_license_type(video_widevine_server::sdk::OFFLINE);
break;
case kLicenseTypeStreaming:
cenc_content_id->set_license_type(video_widevine_server::sdk::STREAMING);
break;
default:
LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %d",
license_type);
return false;
break;
}
cenc_content_id->set_request_id(request_id);
// TODO(jfore): The time field will be updated once the cdm wrapper // TODO(jfore): The time field will be updated once the cdm wrapper
// has been updated to pass us in the time. // has been updated to pass us in the time.
license_request.set_request_time(0); license_request.set_request_time(0);
@@ -766,6 +784,28 @@ CdmResponseType CdmLicense::HandleKeyErrorResponse(
} }
} }
template<typename T>
bool CdmLicense::PrepareContentId(const CdmLicenseType license_type,
const std::string& request_id,
T* content_id) {
switch (license_type) {
case kLicenseTypeOffline:
content_id->set_license_type(video_widevine_server::sdk::OFFLINE);
break;
case kLicenseTypeStreaming:
content_id->set_license_type(
video_widevine_server::sdk::STREAMING);
break;
default:
LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %d",
license_type);
return false;
}
content_id->set_request_id(request_id);
return true;
}
bool CdmLicense::IsKeyLoaded(const KeyId& key_id) { bool CdmLicense::IsKeyLoaded(const KeyId& key_id) {
return loaded_keys_.find(key_id) != loaded_keys_.end(); return loaded_keys_.find(key_id) != loaded_keys_.end();
} }

View File

@@ -16,6 +16,7 @@
#include "scoped_ptr.h" #include "scoped_ptr.h"
#include "string_conversions.h" #include "string_conversions.h"
#include "url_request.h" #include "url_request.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_types.h" #include "wv_cdm_types.h"
namespace { namespace {
@@ -98,20 +99,22 @@ class WvCdmEngineTest : public testing::Test {
protected: protected:
void GenerateKeyRequest(const std::string& key_system, void GenerateKeyRequest(const std::string& key_system,
const std::string& key_id) { const std::string& key_id,
const std::string& mime_type) {
CdmAppParameterMap app_parameters; CdmAppParameterMap app_parameters;
std::string server_url; std::string server_url;
std::string init_data = key_id; std::string init_data = key_id;
CdmKeySetId key_set_id; CdmKeySetId key_set_id;
// TODO(rfrias): Temporary change till b/9465346 is addressed // TODO(rfrias): Temporary change till b/9465346 is addressed
if (!Properties::extract_pssh_data()) { if (!Properties::extract_pssh_data() || mime_type != ISO_BMFF_MIME_TYPE) {
EXPECT_TRUE(CdmEngine::ExtractWidevinePssh(key_id, &init_data)); EXPECT_TRUE(CdmEngine::ExtractWidevinePssh(key_id, &init_data));
} }
EXPECT_EQ(KEY_MESSAGE, EXPECT_EQ(KEY_MESSAGE,
cdm_engine_->GenerateKeyRequest(session_id_, cdm_engine_->GenerateKeyRequest(session_id_,
key_set_id, key_set_id,
mime_type,
init_data, init_data,
kLicenseTypeStreaming, kLicenseTypeStreaming,
app_parameters, app_parameters,
@@ -164,7 +167,7 @@ class WvCdmEngineTest : public testing::Test {
std::string resp = GetKeyRequestResponse(server_url, std::string resp = GetKeyRequestResponse(server_url,
client_auth); client_auth);
CdmKeySetId key_set_id; CdmKeySetId key_set_id;
EXPECT_EQ(cdm_engine_->AddKey(session_id_, resp, &key_set_id), KEY_ADDED); EXPECT_EQ(KEY_ADDED, cdm_engine_->AddKey(session_id_, resp, &key_set_id));
} }
void VerifyRenewalKeyResponse(const std::string& server_url, void VerifyRenewalKeyResponse(const std::string& server_url,
@@ -172,7 +175,7 @@ class WvCdmEngineTest : public testing::Test {
std::string& init_data){ std::string& init_data){
std::string resp = GetKeyRequestResponse(server_url, std::string resp = GetKeyRequestResponse(server_url,
client_auth); client_auth);
EXPECT_EQ(cdm_engine_->RenewKey(session_id_, resp), wvcdm::KEY_ADDED); EXPECT_EQ(KEY_ADDED, cdm_engine_->RenewKey(session_id_, resp));
} }
scoped_ptr<CdmEngine> cdm_engine_; scoped_ptr<CdmEngine> cdm_engine_;
@@ -190,24 +193,36 @@ TEST(WvCdmProvisioningTest, ProvisioningTest) {
cdm_engine.HandleProvisioningResponse(kValidJsonProvisioningResponse); cdm_engine.HandleProvisioningResponse(kValidJsonProvisioningResponse);
} }
TEST_F(WvCdmEngineTest, BaseMessageTest) { TEST_F(WvCdmEngineTest, BaseIsoBmffMessageTest) {
GenerateKeyRequest(g_key_system, g_key_id); GenerateKeyRequest(g_key_system, g_key_id, "video/mp4");
GetKeyRequestResponse(g_license_server, g_client_auth);
}
// TODO(juce): Set up with correct test data.
TEST_F(WvCdmEngineTest, DISABLED_BaseWebmMessageTest) {
GenerateKeyRequest(g_key_system, g_key_id, "video/webm");
GetKeyRequestResponse(g_license_server, g_client_auth); GetKeyRequestResponse(g_license_server, g_client_auth);
} }
TEST_F(WvCdmEngineTest, WrongMessageTest) { TEST_F(WvCdmEngineTest, WrongMessageTest) {
std::string wrong_message = a2bs_hex(g_wrong_key_id); std::string wrong_message = a2bs_hex(g_wrong_key_id);
GenerateKeyRequest(g_key_system, wrong_message); GenerateKeyRequest(g_key_system, wrong_message, "video/mp4");
GetKeyRequestResponse(g_license_server, g_client_auth); GetKeyRequestResponse(g_license_server, g_client_auth);
} }
TEST_F(WvCdmEngineTest, NormalDecryption) { TEST_F(WvCdmEngineTest, NormalDecryptionIsoBmff) {
GenerateKeyRequest(g_key_system, g_key_id); GenerateKeyRequest(g_key_system, g_key_id, "video/mp4");
VerifyNewKeyResponse(g_license_server, g_client_auth, g_key_id);
}
// TODO(juce): Set up with correct test data.
TEST_F(WvCdmEngineTest, DISABLED_NormalDecryptionWebm) {
GenerateKeyRequest(g_key_system, g_key_id, "video/webm");
VerifyNewKeyResponse(g_license_server, g_client_auth, g_key_id); VerifyNewKeyResponse(g_license_server, g_client_auth, g_key_id);
} }
TEST_F(WvCdmEngineTest, LicenseRenewal) { TEST_F(WvCdmEngineTest, LicenseRenewal) {
GenerateKeyRequest(g_key_system, g_key_id); GenerateKeyRequest(g_key_system, g_key_id, "video/mp4");
VerifyNewKeyResponse(g_license_server, g_client_auth, g_key_id); VerifyNewKeyResponse(g_license_server, g_client_auth, g_key_id);
GenerateRenewalRequest(g_key_system, g_key_id); GenerateRenewalRequest(g_key_system, g_key_id);

View File

@@ -74,12 +74,29 @@ TEST(LicenseTestSession, InitNullSession) {
} }
// TODO(rfrias): Fix or remove test. // TODO(rfrias): Fix or remove test.
TEST_F(LicenseTest, DISABLED_PrepareKeyRequest) { TEST_F(LicenseTest, DISABLED_PrepareIsoBmffKeyRequest) {
std::string signed_request; std::string signed_request;
CdmAppParameterMap app_parameters; CdmAppParameterMap app_parameters;
std::string server_url; std::string server_url;
CdmSessionId session_id; CdmSessionId session_id;
license_.PrepareKeyRequest(a2bs_hex(kInitData), license_.PrepareKeyRequest("video/mp4",
a2bs_hex(kInitData),
kLicenseTypeStreaming,
app_parameters,
session_id,
&signed_request,
&server_url);
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
}
// TODO(rfrias): Fix or remove test.
TEST_F(LicenseTest, DISABLED_PrepareWebmKeyRequest) {
std::string signed_request;
CdmAppParameterMap app_parameters;
std::string server_url;
CdmSessionId session_id;
license_.PrepareKeyRequest("video/webm",
a2bs_hex(kInitData),
kLicenseTypeStreaming, kLicenseTypeStreaming,
app_parameters, app_parameters,
session_id, session_id,
@@ -94,7 +111,8 @@ TEST_F(LicenseTest, DISABLED_HandleKeyResponseValid) {
CdmAppParameterMap app_parameters; CdmAppParameterMap app_parameters;
CdmSessionId session_id; CdmSessionId session_id;
std::string server_url; std::string server_url;
license_.PrepareKeyRequest(a2bs_hex(kInitData), license_.PrepareKeyRequest("video/mp4",
a2bs_hex(kInitData),
kLicenseTypeStreaming, kLicenseTypeStreaming,
app_parameters, app_parameters,
session_id, session_id,
@@ -110,7 +128,8 @@ TEST_F(LicenseTest, DISABLED_HandleKeyResponseInvalid) {
CdmAppParameterMap app_parameters; CdmAppParameterMap app_parameters;
CdmSessionId session_id; CdmSessionId session_id;
std::string server_url; std::string server_url;
license_.PrepareKeyRequest(a2bs_hex(kInitData), license_.PrepareKeyRequest("video/mp4",
a2bs_hex(kInitData),
kLicenseTypeStreaming, kLicenseTypeStreaming,
app_parameters, app_parameters,
session_id, session_id,

View File

@@ -28,6 +28,7 @@ class WvContentDecryptionModule {
// Construct a valid license request. // Construct a valid license request.
virtual CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id, virtual CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id,
const CdmKeySetId& key_set_id, const CdmKeySetId& key_set_id,
const std::string& mime_type,
const CdmInitData& init_data, const CdmInitData& init_data,
const CdmLicenseType license_type, const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters, CdmAppParameterMap& app_parameters,

View File

@@ -38,6 +38,7 @@ CdmResponseType WvContentDecryptionModule::CloseSession(
CdmResponseType WvContentDecryptionModule::GenerateKeyRequest( CdmResponseType WvContentDecryptionModule::GenerateKeyRequest(
const CdmSessionId& session_id, const CdmSessionId& session_id,
const CdmKeySetId& key_set_id, const CdmKeySetId& key_set_id,
const std::string& mime_type,
const CdmInitData& init_data, const CdmInitData& init_data,
const CdmLicenseType license_type, const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters, CdmAppParameterMap& app_parameters,
@@ -49,7 +50,7 @@ CdmResponseType WvContentDecryptionModule::GenerateKeyRequest(
if (sts != NO_ERROR) if (sts != NO_ERROR)
return sts; return sts;
} }
sts = cdm_engine_->GenerateKeyRequest(session_id, key_set_id, sts = cdm_engine_->GenerateKeyRequest(session_id, key_set_id, mime_type,
init_data, license_type, init_data, license_type,
app_parameters, key_request, app_parameters, key_request,
server_url); server_url);

View File

@@ -298,7 +298,8 @@ class WvCdmRequestLicenseTest : public testing::Test {
std::string server_url; std::string server_url;
std::string key_set_id; std::string key_set_id;
EXPECT_EQ(wvcdm::KEY_MESSAGE, EXPECT_EQ(wvcdm::KEY_MESSAGE,
decryptor_.GenerateKeyRequest(session_id_, key_set_id, init_data, decryptor_.GenerateKeyRequest(session_id_, key_set_id,
"video/mp4", init_data,
license_type, app_parameters, license_type, app_parameters,
&key_msg_, &server_url)); &key_msg_, &server_url));
EXPECT_EQ(0u, server_url.size()); EXPECT_EQ(0u, server_url.size());
@@ -312,7 +313,8 @@ class WvCdmRequestLicenseTest : public testing::Test {
std::string init_data; std::string init_data;
wvcdm::CdmAppParameterMap app_parameters; wvcdm::CdmAppParameterMap app_parameters;
EXPECT_EQ(wvcdm::KEY_MESSAGE, EXPECT_EQ(wvcdm::KEY_MESSAGE,
decryptor_.GenerateKeyRequest(session_id_, key_set_id_, init_data, decryptor_.GenerateKeyRequest(session_id_, key_set_id_,
"video/mp4", init_data,
license_type, app_parameters, license_type, app_parameters,
&key_msg_, server_url)); &key_msg_, server_url));
// TODO(edwinwong, rfrias): Add tests cases for when license server url // TODO(edwinwong, rfrias): Add tests cases for when license server url
@@ -326,9 +328,10 @@ class WvCdmRequestLicenseTest : public testing::Test {
wvcdm::CdmAppParameterMap app_parameters; wvcdm::CdmAppParameterMap app_parameters;
std::string server_url; std::string server_url;
EXPECT_EQ(wvcdm::KEY_MESSAGE, EXPECT_EQ(wvcdm::KEY_MESSAGE,
decryptor_.GenerateKeyRequest(session_id, key_set_id, init_data, decryptor_.GenerateKeyRequest(session_id, key_set_id, "video/mp4",
kLicenseTypeRelease, app_parameters, init_data, kLicenseTypeRelease,
&key_msg_, &server_url)); app_parameters, &key_msg_,
&server_url));
} }
// Post a request and extract the drm message from the response // Post a request and extract the drm message from the response
@@ -961,9 +964,10 @@ TEST_F(WvCdmRequestLicenseTest, SecurityLevelPathBackwardCompatibility) {
wvcdm::CdmAppParameterMap app_parameters; wvcdm::CdmAppParameterMap app_parameters;
std::string server_url; std::string server_url;
EXPECT_EQ(wvcdm::NEED_PROVISIONING, EXPECT_EQ(wvcdm::NEED_PROVISIONING,
decryptor_.GenerateKeyRequest(session_id_, key_set_id, g_key_id, decryptor_.GenerateKeyRequest(session_id_, key_set_id, "video/mp4",
kLicenseTypeStreaming, app_parameters, g_key_id, kLicenseTypeStreaming,
&key_msg_, &server_url)); app_parameters, &key_msg_,
&server_url));
EXPECT_EQ(NO_ERROR, EXPECT_EQ(NO_ERROR,
decryptor_.GetProvisioningRequest(&key_msg_, decryptor_.GetProvisioningRequest(&key_msg_,
&provisioning_server_url)); &provisioning_server_url));

View File

@@ -149,22 +149,51 @@ status_t WVDrmPlugin::getKeyRequest(
return android::ERROR_DRM_CANNOT_HANDLE; return android::ERROR_DRM_CANNOT_HANDLE;
} }
// Build PSSH box for PSSH data in initData. string cdmMimeType = mimeType.string();
static const char psshPrefix[] = {
0, 0, 0, 0, // Total size // Provide backwards-compatibility for versions of apps that pass the wrong
'p', 's', 's', 'h', // "PSSH" // MIME type.
0, 0, 0, 0, // Flags - must be zero // TODO(b/13731637) - Remove this.
0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE, // Widevine UUID if (cdmMimeType == "") {
0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED, ALOGW("ALERT: Empty MIME type is supported for backwards-compatibility "
0, 0, 0, 0 // Size of initData "only and may go away in the future. Switch to \"video/mp4\" to "
}; "become compliant.");
CdmInitData psshBox(psshPrefix, sizeof(psshPrefix) / sizeof(uint8_t)); cdmMimeType = "video/mp4";
psshBox.append(reinterpret_cast<const char*>(initData.array()), } else if (cdmMimeType == "video/avc") {
initData.size()); ALOGW("ALERT: MIME type \"video/avc\" is supported for backwards-"
uint32_t* psshBoxSize = reinterpret_cast<uint32_t*>(&psshBox[0]); "compatibility only and may go away in the future. Switch to "
uint32_t* initDataSize = reinterpret_cast<uint32_t*>(&psshBox[28]); "\"video/mp4\" to become compliant.");
*initDataSize = htonl(initData.size()); cdmMimeType = "video/mp4";
*psshBoxSize = htonl(psshBox.size()); }
CdmInitData processedInitData;
if (cdmMimeType == wvcdm::ISO_BMFF_MIME_TYPE) {
// For ISO-BMFF, we need to wrap the init data in a new PSSH header.
static const char psshPrefix[] = {
0, 0, 0, 0, // Total size
'p', 's', 's', 'h', // "PSSH"
0, 0, 0, 0, // Flags - must be zero
0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE, // Widevine UUID
0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED,
0, 0, 0, 0 // Size of initData
};
processedInitData.assign(psshPrefix, sizeof(psshPrefix) / sizeof(char));
processedInitData.append(reinterpret_cast<const char*>(initData.array()),
initData.size());
const size_t kPsshBoxSizeLocation = 0;
const size_t kInitDataSizeLocation =
sizeof(psshPrefix) - sizeof(uint32_t);
uint32_t psshBoxSize = htonl(processedInitData.size());
uint32_t initDataSize = htonl(initData.size());
memcpy(&processedInitData[kPsshBoxSizeLocation], &psshBoxSize,
sizeof(uint32_t));
memcpy(&processedInitData[kInitDataSizeLocation], &initDataSize,
sizeof(uint32_t));
} else {
// For other formats, we can pass the init data through unmodified.
processedInitData.assign(reinterpret_cast<const char*>(initData.array()),
initData.size());
}
CdmAppParameterMap cdmParameters; CdmAppParameterMap cdmParameters;
for (size_t i = 0; i < optionalParameters.size(); ++i) { for (size_t i = 0; i < optionalParameters.size(); ++i) {
@@ -179,11 +208,10 @@ status_t WVDrmPlugin::getKeyRequest(
CdmKeyMessage keyRequest; CdmKeyMessage keyRequest;
string cdmDefaultUrl; string cdmDefaultUrl;
CdmResponseType res = mCDM->GenerateKeyRequest(cdmSessionId, cdmKeySetId, CdmResponseType res = mCDM->GenerateKeyRequest(cdmSessionId, cdmKeySetId,
psshBox, cdmLicenseType, cdmMimeType, processedInitData,
cdmParameters, &keyRequest, cdmLicenseType, cdmParameters,
&cdmDefaultUrl); &keyRequest, &cdmDefaultUrl);
if (isCdmResponseTypeSuccess(res)) { if (isCdmResponseTypeSuccess(res)) {
defaultUrl.clear(); defaultUrl.clear();

View File

@@ -31,8 +31,9 @@ class MockCDM : public WvContentDecryptionModule {
MOCK_METHOD1(CloseSession, CdmResponseType(const CdmSessionId&)); MOCK_METHOD1(CloseSession, CdmResponseType(const CdmSessionId&));
MOCK_METHOD7(GenerateKeyRequest, CdmResponseType(const CdmSessionId&, MOCK_METHOD8(GenerateKeyRequest, CdmResponseType(const CdmSessionId&,
const CdmKeySetId&, const CdmKeySetId&,
const std::string&,
const CdmInitData&, const CdmInitData&,
const CdmLicenseType, const CdmLicenseType,
CdmAppParameterMap&, CdmAppParameterMap&,
@@ -174,7 +175,7 @@ TEST_F(WVDrmPluginTest, ClosesSessions) {
ASSERT_EQ(OK, res); ASSERT_EQ(OK, res);
} }
TEST_F(WVDrmPluginTest, GeneratesKeyRequests) { TEST_F(WVDrmPluginTest, GeneratesIsoBmffKeyRequests) {
StrictMock<MockCDM> cdm; StrictMock<MockCDM> cdm;
StrictMock<MockCrypto> crypto; StrictMock<MockCrypto> crypto;
WVDrmPlugin plugin(&cdm, &crypto); WVDrmPlugin plugin(&cdm, &crypto);
@@ -226,32 +227,33 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) {
cdmParameters["answer"] = "42"; cdmParameters["answer"] = "42";
static const char* kDefaultUrl = "http://google.com/"; static const char* kDefaultUrl = "http://google.com/";
static const char* kMimeType = "video/mp4";
{ {
InSequence calls; InSequence calls;
EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, "", EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, "", kMimeType,
ElementsAreArray(psshBox, kPsshBoxSize), ElementsAreArray(psshBox, kPsshBoxSize),
kLicenseTypeOffline, cdmParameters, _, kLicenseTypeOffline, cdmParameters, _,
_)) _))
.WillOnce(DoAll(SetArgPointee<5>(cdmRequest), .WillOnce(DoAll(SetArgPointee<6>(cdmRequest),
SetArgPointee<6>(kDefaultUrl), SetArgPointee<7>(kDefaultUrl),
Return(wvcdm::KEY_MESSAGE))); Return(wvcdm::KEY_MESSAGE)));
EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, "", EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, "", kMimeType,
ElementsAreArray(psshBox, kPsshBoxSize), ElementsAreArray(psshBox, kPsshBoxSize),
kLicenseTypeStreaming, cdmParameters, _, kLicenseTypeStreaming, cdmParameters, _,
_)) _))
.WillOnce(DoAll(SetArgPointee<5>(cdmRequest), .WillOnce(DoAll(SetArgPointee<6>(cdmRequest),
SetArgPointee<6>(kDefaultUrl), SetArgPointee<7>(kDefaultUrl),
Return(wvcdm::KEY_MESSAGE))); Return(wvcdm::KEY_MESSAGE)));
EXPECT_CALL(cdm, GenerateKeyRequest("", cdmKeySetId, EXPECT_CALL(cdm, GenerateKeyRequest("", cdmKeySetId, kMimeType,
ElementsAreArray(psshBox, kPsshBoxSize), ElementsAreArray(psshBox, kPsshBoxSize),
kLicenseTypeRelease, cdmParameters, _, kLicenseTypeRelease, cdmParameters, _,
_)) _))
.WillOnce(DoAll(SetArgPointee<5>(cdmRequest), .WillOnce(DoAll(SetArgPointee<6>(cdmRequest),
SetArgPointee<6>(kDefaultUrl), SetArgPointee<7>(kDefaultUrl),
Return(wvcdm::KEY_MESSAGE))); Return(wvcdm::KEY_MESSAGE)));
} }
@@ -259,21 +261,118 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) {
String8 defaultUrl; String8 defaultUrl;
status_t res = plugin.getKeyRequest(sessionId, initData, status_t res = plugin.getKeyRequest(sessionId, initData,
String8("video/h264"), String8(kMimeType),
DrmPlugin::kKeyType_Offline, DrmPlugin::kKeyType_Offline,
parameters, request, defaultUrl); parameters, request, defaultUrl);
ASSERT_EQ(OK, res); ASSERT_EQ(OK, res);
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize)); EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
EXPECT_STREQ(kDefaultUrl, defaultUrl.string()); EXPECT_STREQ(kDefaultUrl, defaultUrl.string());
res = plugin.getKeyRequest(sessionId, initData, String8("video/h264"), res = plugin.getKeyRequest(sessionId, initData, String8(kMimeType),
DrmPlugin::kKeyType_Streaming, parameters, DrmPlugin::kKeyType_Streaming, parameters,
request, defaultUrl); request, defaultUrl);
ASSERT_EQ(OK, res); ASSERT_EQ(OK, res);
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize)); EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
EXPECT_STREQ(kDefaultUrl, defaultUrl.string()); EXPECT_STREQ(kDefaultUrl, defaultUrl.string());
res = plugin.getKeyRequest(keySetId, initData, String8("video/h264"), res = plugin.getKeyRequest(keySetId, initData, String8(kMimeType),
DrmPlugin::kKeyType_Release, parameters,
request, defaultUrl);
ASSERT_EQ(OK, res);
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
EXPECT_STREQ(kDefaultUrl, defaultUrl.string());
}
TEST_F(WVDrmPluginTest, GeneratesWebmKeyRequests) {
StrictMock<MockCDM> cdm;
StrictMock<MockCrypto> crypto;
WVDrmPlugin plugin(&cdm, &crypto);
static const size_t kInitDataSize = 128;
uint8_t initDataRaw[kInitDataSize];
static const size_t kRequestSize = 256;
uint8_t requestRaw[kRequestSize];
static const uint32_t kKeySetIdSize = 32;
uint8_t keySetIdRaw[kKeySetIdSize];
FILE* fp = fopen("/dev/urandom", "r");
fread(initDataRaw, sizeof(uint8_t), kInitDataSize, fp);
fread(requestRaw, sizeof(uint8_t), kRequestSize, fp);
fread(keySetIdRaw, sizeof(uint8_t), kKeySetIdSize, fp);
fclose(fp);
memcpy(keySetIdRaw, KEY_SET_ID_PREFIX, sizeof(KEY_SET_ID_PREFIX) - 1);
CdmKeySetId cdmKeySetId(reinterpret_cast<char *>(keySetIdRaw), kKeySetIdSize);
Vector<uint8_t> keySetId;
keySetId.appendArray(keySetIdRaw, kKeySetIdSize);
Vector<uint8_t> initData;
initData.appendArray(initDataRaw, kInitDataSize);
CdmKeyMessage cdmRequest(requestRaw, requestRaw + kRequestSize);
KeyedVector<String8, String8> parameters;
CdmAppParameterMap cdmParameters;
parameters.add(String8("paddingScheme"), String8("BUBBLE WRAP"));
cdmParameters["paddingScheme"] = "BUBBLE WRAP";
parameters.add(String8("favoriteParticle"), String8("higgs boson"));
cdmParameters["favoriteParticle"] = "higgs boson";
parameters.add(String8("answer"), String8("6 * 9"));
cdmParameters["answer"] = "6 * 9";
static const char* kDefaultUrl = "http://google.com/";
static const char* kMimeType = "video/webm";
{
InSequence calls;
EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, "", kMimeType,
ElementsAreArray(initDataRaw,
kInitDataSize),
kLicenseTypeOffline, cdmParameters, _,
_))
.WillOnce(DoAll(SetArgPointee<6>(cdmRequest),
SetArgPointee<7>(kDefaultUrl),
Return(wvcdm::KEY_MESSAGE)));
EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, "", kMimeType,
ElementsAreArray(initDataRaw,
kInitDataSize),
kLicenseTypeStreaming, cdmParameters, _,
_))
.WillOnce(DoAll(SetArgPointee<6>(cdmRequest),
SetArgPointee<7>(kDefaultUrl),
Return(wvcdm::KEY_MESSAGE)));
EXPECT_CALL(cdm, GenerateKeyRequest("", cdmKeySetId, kMimeType,
ElementsAreArray(initDataRaw,
kInitDataSize),
kLicenseTypeRelease, cdmParameters, _,
_))
.WillOnce(DoAll(SetArgPointee<6>(cdmRequest),
SetArgPointee<7>(kDefaultUrl),
Return(wvcdm::KEY_MESSAGE)));
}
Vector<uint8_t> request;
String8 defaultUrl;
status_t res = plugin.getKeyRequest(sessionId, initData,
String8(kMimeType),
DrmPlugin::kKeyType_Offline,
parameters, request, defaultUrl);
ASSERT_EQ(OK, res);
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
EXPECT_STREQ(kDefaultUrl, defaultUrl.string());
res = plugin.getKeyRequest(sessionId, initData, String8(kMimeType),
DrmPlugin::kKeyType_Streaming, parameters,
request, defaultUrl);
ASSERT_EQ(OK, res);
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
EXPECT_STREQ(kDefaultUrl, defaultUrl.string());
res = plugin.getKeyRequest(keySetId, initData, String8(kMimeType),
DrmPlugin::kKeyType_Release, parameters, DrmPlugin::kKeyType_Release, parameters,
request, defaultUrl); request, defaultUrl);
ASSERT_EQ(OK, res); ASSERT_EQ(OK, res);

View File

@@ -9,6 +9,7 @@
#include "WVDrmFactory.h" #include "WVDrmFactory.h"
#include "utils/Errors.h" #include "utils/Errors.h"
#include "wv_cdm_constants.h"
#include "WVCDMSingleton.h" #include "WVCDMSingleton.h"
#include "WVDrmPlugin.h" #include "WVDrmPlugin.h"
#include "WVUUID.h" #include "WVUUID.h"
@@ -24,8 +25,9 @@ bool WVDrmFactory::isCryptoSchemeSupported(const uint8_t uuid[16]) {
} }
bool WVDrmFactory::isContentTypeSupported(const String8 &mimeType) { bool WVDrmFactory::isContentTypeSupported(const String8 &mimeType) {
// For now, only ISO-BMFF is supported, which has MIME-type video/mp4 // Support ISO-BMFF (video/mp4) and WebM (video/webm).
return mimeType == "video/mp4"; return mimeType == wvcdm::ISO_BMFF_MIME_TYPE.c_str() ||
mimeType == wvcdm::WEBM_MIME_TYPE.c_str();
} }
status_t WVDrmFactory::createDrmPlugin(const uint8_t uuid[16], status_t WVDrmFactory::createDrmPlugin(const uint8_t uuid[16],

View File

@@ -46,14 +46,14 @@ TEST(WVDrmFactoryTest, SupportsSupportedContainerFormats) {
EXPECT_TRUE(factory.isContentTypeSupported(String8("video/mp4"))) << EXPECT_TRUE(factory.isContentTypeSupported(String8("video/mp4"))) <<
"WVPluginFactory does not support ISO-BMFF"; "WVPluginFactory does not support ISO-BMFF";
EXPECT_TRUE(factory.isContentTypeSupported(String8("video/webm"))) <<
"WVPluginFactory does not support WebM";
} }
TEST(WVDrmFactoryTest, DoesNotSupportUnsupportedContainerFormats) { TEST(WVDrmFactoryTest, DoesNotSupportUnsupportedContainerFormats) {
WVDrmFactory factory; WVDrmFactory factory;
EXPECT_FALSE(factory.isContentTypeSupported(String8("video/webm"))) <<
"WVPluginFactory incorrectly claims to support Web-M";
// Taken from Encoding.com's list of the most common internet video MIME-types // Taken from Encoding.com's list of the most common internet video MIME-types
EXPECT_FALSE(factory.isContentTypeSupported(String8("video/x-matroska"))) << EXPECT_FALSE(factory.isContentTypeSupported(String8("video/x-matroska"))) <<
"WVPluginFactory incorrectly claims to support Matroska"; "WVPluginFactory incorrectly claims to support Matroska";