Add Support for Audio MIME Types

The EME spec technically requires CDMs to treat audio/mp4 and
video/mp4 equivalently, as well as audio/webm and video/webm. We had
only been accepting video/mp4 and video/webm up until now.

This change also centralizes handling of init data types in the shared
CDM code instead of having it spread across multiple places in the
codebase.

(This is a merge of https://widevine-internal-review.googlesource.com/9532/
from the Widevine CDM repo.)

Bug: 13564917
Change-Id: Ib8bdfb2b003ffb00e8f0559561335abb3c5778b0
This commit is contained in:
John "Juce" Bruce
2014-04-02 15:39:12 -07:00
parent b920034e3b
commit 7eea20df86
19 changed files with 295 additions and 200 deletions

View File

@@ -28,6 +28,7 @@ LOCAL_SRC_FILES := \
$(CORE_SRC_DIR)/certificate_provisioning.cpp \
$(CORE_SRC_DIR)/crypto_session.cpp \
$(CORE_SRC_DIR)/device_files.cpp \
$(CORE_SRC_DIR)/initialization_data.cpp \
$(CORE_SRC_DIR)/license.cpp \
$(CORE_SRC_DIR)/oemcrypto_adapter_dynamic.cpp \
$(CORE_SRC_DIR)/policy_engine.cpp \

View File

@@ -4,6 +4,7 @@
#define CDM_BASE_CDM_ENGINE_H_
#include "certificate_provisioning.h"
#include "initialization_data.h"
#include "oemcrypto_adapter.h"
#include "timer.h"
#include "wv_cdm_types.h"
@@ -35,10 +36,10 @@ class CdmEngine : public TimerHandler {
// License related methods
// 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 std::string& mime_type,
const CdmInitData& init_data,
const InitializationData& init_data,
const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters,
CdmKeyMessage* key_request,
@@ -110,11 +111,6 @@ class CdmEngine : public TimerHandler {
virtual bool DetachEventListener(const CdmSessionId& session_id,
WvCdmEventListener* listener);
// Parse a blob of multiple concatenated PSSH atoms to extract the first
// widevine pssh
static bool ExtractWidevinePssh(const CdmInitData& init_data,
CdmInitData* output);
private:
// private methods
// Cancel all sessions

View File

@@ -7,6 +7,7 @@
#include "crypto_session.h"
#include "device_files.h"
#include "initialization_data.h"
#include "license.h"
#include "oemcrypto_adapter.h"
#include "policy_engine.h"
@@ -34,10 +35,9 @@ class CdmSession {
const CdmSessionId& session_id() { return session_id_; }
bool VerifySession(const CdmKeySystem& key_system,
const CdmInitData& init_data);
const InitializationData& init_data);
CdmResponseType GenerateKeyRequest(const std::string& mime_type,
const CdmInitData& init_data,
CdmResponseType GenerateKeyRequest(const InitializationData& init_data,
const CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters,
CdmKeyMessage* key_request,

View File

@@ -0,0 +1,47 @@
// Copyright 2014 Google Inc. All Rights Reserved.
#ifndef CORE_INCLUDE_INITIALIZATION_DATA_H_
#define CORE_INCLUDE_INITIALIZATION_DATA_H_
#include <string>
#include "wv_cdm_types.h"
namespace wvcdm {
class WvCdmEngineTest;
class InitializationData {
public:
InitializationData(const std::string& type,
const CdmInitData& data = CdmInitData());
bool is_supported() const { return is_cenc_ || is_webm_; }
bool is_cenc() const { return is_cenc_; }
bool is_webm() const { return is_webm_; }
bool IsEmpty() const { return data_.empty(); }
const std::string& type() const { return type_; }
const CdmInitData& data() const { return data_; }
private:
friend WvCdmEngineTest;
// Parse a blob of multiple concatenated PSSH atoms to extract the first
// Widevine PSSH.
// TODO(juce): Make this non-static and remove the friend above once the unit
// test is rewritten to not need this.
static bool ExtractWidevinePssh(const CdmInitData& init_data,
CdmInitData* output);
std::string type_;
CdmInitData data_;
bool is_cenc_;
bool is_webm_;
CORE_DISALLOW_COPY_AND_ASSIGN(InitializationData);
};
} // namespace wvcdm
#endif // CORE_INCLUDE_INITIALIZATION_DATA_H_

View File

@@ -5,6 +5,7 @@
#include <set>
#include "initialization_data.h"
#include "wv_cdm_types.h"
namespace video_widevine_server {
@@ -27,8 +28,7 @@ class CdmLicense {
bool Init(const std::string& token, CryptoSession* session,
PolicyEngine* policy_engine);
bool PrepareKeyRequest(const std::string& mime_type,
const CdmInitData& init_data,
bool PrepareKeyRequest(const InitializationData& init_data,
const CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters,
const CdmSessionId& session_id,

View File

@@ -56,8 +56,10 @@ 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_Unknown = "Unknown";
static const std::string ISO_BMFF_MIME_TYPE = "video/mp4";
static const std::string WEBM_MIME_TYPE = "video/webm";
static const std::string ISO_BMFF_VIDEO_MIME_TYPE = "video/mp4";
static const std::string ISO_BMFF_AUDIO_MIME_TYPE = "audio/mp4";
static const std::string WEBM_VIDEO_MIME_TYPE = "video/webm";
static const std::string WEBM_AUDIO_MIME_TYPE = "audio/webm";
} // namespace wvcdm
#endif // CDM_BASE_WV_CDM_CONSTANTS_H_

View File

@@ -5,7 +5,6 @@
#include <iostream>
#include <sstream>
#include "buffer_reader.h"
#include "cdm_session.h"
#include "license_protocol.pb.h"
#include "log.h"
@@ -124,8 +123,7 @@ CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
CdmResponseType CdmEngine::GenerateKeyRequest(
const CdmSessionId& session_id,
const CdmKeySetId& key_set_id,
const std::string& mime_type,
const CdmInitData& init_data,
const InitializationData& init_data,
const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters,
CdmKeyMessage* key_request,
@@ -180,7 +178,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
}
}
sts = iter->second->GenerateKeyRequest(mime_type, init_data, license_type,
sts = iter->second->GenerateKeyRequest(init_data, license_type,
app_parameters, key_request,
server_url);
@@ -611,97 +609,6 @@ bool CdmEngine::CancelSessions() {
return true;
}
// Parse a blob of multiple concatenated PSSH atoms to extract the first
// widevine pssh
// TODO(kqyang): temporary workaround - remove after b/7928472 is resolved
bool CdmEngine::ExtractWidevinePssh(
const CdmInitData& init_data, CdmInitData* output) {
BufferReader reader(
reinterpret_cast<const uint8_t*>(init_data.data()), init_data.length());
// TODO(kqyang): Extracted from an actual init_data;
// Need to find out where it comes from.
static const uint8_t kWidevineSystemId[] = {
0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED,
};
// one PSSH blob consists of:
// 4 byte size of the PSSH atom, inclusive
// "pssh"
// 4 byte flags, value 0
// 16 byte system id
// 4 byte size of PSSH data, exclusive
while (1) {
// size of PSSH atom, used for skipping
uint32_t size;
if (!reader.Read4(&size)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH atom size");
return false;
}
// "pssh"
std::vector<uint8_t> pssh;
if (!reader.ReadVec(&pssh, 4)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH literal");
return false;
}
if (memcmp(&pssh[0], "pssh", 4)) {
LOGW("CdmEngine::ExtractWidevinePssh: PSSH literal not present");
return false;
}
// flags
uint32_t flags;
if (!reader.Read4(&flags)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH flags");
return false;
}
if (flags != 0) {
LOGW("CdmEngine::ExtractWidevinePssh: PSSH flags not zero");
return false;
}
// system id
std::vector<uint8_t> system_id;
if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read system ID");
return false;
}
if (memcmp(&system_id[0], kWidevineSystemId,
sizeof(kWidevineSystemId))) {
// skip the remaining contents of the atom,
// after size field, atom name, flags and system id
if (!reader.SkipBytes(
size - 4 - 4 - 4 - sizeof(kWidevineSystemId))) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to rest of PSSH atom");
return false;
}
continue;
}
// size of PSSH box
uint32_t pssh_length;
if (!reader.Read4(&pssh_length)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH box size");
return false;
}
output->clear();
if (!reader.ReadString(output, pssh_length)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH");
return false;
}
return true;
}
// we did not find a matching record
return false;
}
void CdmEngine::EnablePolicyTimer() {
if (!policy_timer_.IsRunning())
policy_timer_.Start(this, kCdmPolicyTimerDurationSeconds);

View File

@@ -112,16 +112,16 @@ CdmResponseType CdmSession::RestoreOfflineSession(
}
bool CdmSession::VerifySession(const CdmKeySystem& key_system,
const CdmInitData& init_data) {
const InitializationData& init_data) {
// TODO(gmorgan): Compare key_system and init_data with value received
// during session startup - they should be the same.
return true;
}
CdmResponseType CdmSession::GenerateKeyRequest(
const std::string& mime_type, const CdmInitData& init_data,
const CdmLicenseType license_type, const CdmAppParameterMap& app_parameters,
CdmKeyMessage* key_request, std::string* server_url) {
const InitializationData& init_data, const CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
std::string* server_url) {
if (reinitialize_session_) {
CdmResponseType sts = Init();
if (sts != NO_ERROR) {
@@ -149,22 +149,16 @@ CdmResponseType CdmSession::GenerateKeyRequest(
? UNKNOWN_ERROR
: GenerateRenewalRequest(key_request, server_url);
} else {
if (mime_type != ISO_BMFF_MIME_TYPE && mime_type != WEBM_MIME_TYPE) {
LOGW("CdmSession::GenerateKeyRequest: unknown MIME type");
if (!init_data.is_supported()) {
LOGW("CdmSession::GenerateKeyRequest: unsupported init data type (%s)",
init_data.type().c_str());
return KEY_ERROR;
}
if (init_data.empty() && !license_parser_.HasInitData()) {
if (init_data.IsEmpty() && !license_parser_.HasInitData()) {
LOGW("CdmSession::GenerateKeyRequest: init data absent");
return KEY_ERROR;
}
CdmInitData extracted_init_data = init_data;
if (Properties::extract_pssh_data() && mime_type == ISO_BMFF_MIME_TYPE) {
if (!CdmEngine::ExtractWidevinePssh(init_data, &extracted_init_data)) {
return KEY_ERROR;
}
}
if (Properties::use_certificates_as_identification()) {
if (is_certificate_loaded_ ||
crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) {
@@ -176,15 +170,14 @@ CdmResponseType CdmSession::GenerateKeyRequest(
}
}
if (!license_parser_.PrepareKeyRequest(mime_type, extracted_init_data,
license_type, app_parameters,
session_id_, key_request,
server_url)) {
if (!license_parser_.PrepareKeyRequest(init_data, license_type,
app_parameters, session_id_,
key_request, server_url)) {
return KEY_ERROR;
}
if (license_type_ == kLicenseTypeOffline) {
offline_init_data_ = extracted_init_data;
offline_init_data_ = init_data.data();
offline_key_request_ = *key_request;
offline_release_server_url_ = *server_url;
}

View File

@@ -0,0 +1,121 @@
// Copyright 2014 Google Inc. All Rights Reserved.
#include "initialization_data.h"
#include "buffer_reader.h"
#include "log.h"
#include "properties.h"
#include "wv_cdm_constants.h"
namespace wvcdm {
InitializationData::InitializationData(const std::string& type,
const CdmInitData& data)
: type_(type), is_cenc_(false), is_webm_(false) {
if (type == ISO_BMFF_VIDEO_MIME_TYPE || type == ISO_BMFF_AUDIO_MIME_TYPE) {
is_cenc_ = true;
} else if (type == WEBM_VIDEO_MIME_TYPE || type == WEBM_AUDIO_MIME_TYPE) {
is_webm_ = true;
}
if (is_supported()) {
if (Properties::extract_pssh_data() && is_cenc()) {
ExtractWidevinePssh(data, &data_);
} else {
data_ = data;
}
}
}
// Parse a blob of multiple concatenated PSSH atoms to extract the first
// Widevine PSSH.
// TODO(kqyang): temporary workaround - remove after b/7928472 is resolved
bool InitializationData::ExtractWidevinePssh(
const CdmInitData& init_data, CdmInitData* output) {
BufferReader reader(
reinterpret_cast<const uint8_t*>(init_data.data()), init_data.length());
// TODO(kqyang): Extracted from an actual init_data;
// Need to find out where it comes from.
static const uint8_t kWidevineSystemId[] = {
0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED,
};
// one PSSH blob consists of:
// 4 byte size of the PSSH atom, inclusive
// "pssh"
// 4 byte flags, value 0
// 16 byte system id
// 4 byte size of PSSH data, exclusive
while (1) {
// size of PSSH atom, used for skipping
uint32_t size;
if (!reader.Read4(&size)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH atom size");
return false;
}
// "pssh"
std::vector<uint8_t> pssh;
if (!reader.ReadVec(&pssh, 4)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH literal");
return false;
}
if (memcmp(&pssh[0], "pssh", 4)) {
LOGW("CdmEngine::ExtractWidevinePssh: PSSH literal not present");
return false;
}
// flags
uint32_t flags;
if (!reader.Read4(&flags)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH flags");
return false;
}
if (flags != 0) {
LOGW("CdmEngine::ExtractWidevinePssh: PSSH flags not zero");
return false;
}
// system id
std::vector<uint8_t> system_id;
if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read system ID");
return false;
}
if (memcmp(&system_id[0], kWidevineSystemId,
sizeof(kWidevineSystemId))) {
// skip the remaining contents of the atom,
// after size field, atom name, flags and system id
if (!reader.SkipBytes(
size - 4 - 4 - 4 - sizeof(kWidevineSystemId))) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to rest of PSSH atom");
return false;
}
continue;
}
// size of PSSH box
uint32_t pssh_length;
if (!reader.Read4(&pssh_length)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH box size");
return false;
}
output->clear();
if (!reader.ReadString(output, pssh_length)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH");
return false;
}
return true;
}
// we did not find a matching record
return false;
}
} // namespace wvcdm

View File

@@ -158,8 +158,7 @@ bool CdmLicense::Init(const std::string& token, CryptoSession* session,
return true;
}
bool CdmLicense::PrepareKeyRequest(const std::string& mime_type,
const CdmInitData& init_data,
bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data,
const CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters,
const CdmSessionId& session_id,
@@ -169,12 +168,12 @@ bool CdmLicense::PrepareKeyRequest(const std::string& mime_type,
LOGE("CdmLicense::PrepareKeyRequest: not initialized");
return false;
}
if (mime_type != ISO_BMFF_MIME_TYPE && mime_type != WEBM_MIME_TYPE) {
LOGE("CdmLicense::PrepareKeyRequest: unsupported MIME type %s",
mime_type.c_str());
if (!init_data.is_supported()) {
LOGE("CdmLicense::PrepareKeyRequest: unsupported init data type (%s)",
init_data.type().c_str());
return false;
}
if (init_data.empty() && stored_init_data_.empty()) {
if (init_data.IsEmpty() && stored_init_data_.empty()) {
LOGE("CdmLicense::PrepareKeyRequest: empty init data provided");
return false;
}
@@ -199,7 +198,7 @@ bool CdmLicense::PrepareKeyRequest(const std::string& mime_type,
serialized_service_certificate = service_certificate_;
if (privacy_mode_enabled && serialized_service_certificate.empty()) {
stored_init_data_ = init_data;
stored_init_data_ = init_data.data();
return PrepareServiceCertificateRequest(signed_request, server_url);
}
@@ -315,12 +314,12 @@ bool CdmLicense::PrepareKeyRequest(const std::string& mime_type,
LicenseRequest_ContentIdentification* content_id =
license_request.mutable_content_id();
if (mime_type == ISO_BMFF_MIME_TYPE) {
if (init_data.is_cenc()) {
LicenseRequest_ContentIdentification_CENC* cenc_content_id =
content_id->mutable_cenc_id();
if (!init_data.empty()) {
cenc_content_id->add_pssh(init_data);
if (!init_data.IsEmpty()) {
cenc_content_id->add_pssh(init_data.data());
} else if (privacy_mode_enabled && !stored_init_data_.empty()) {
cenc_content_id->add_pssh(stored_init_data_);
} else {
@@ -331,12 +330,12 @@ bool CdmLicense::PrepareKeyRequest(const std::string& mime_type,
if (!PrepareContentId(license_type, request_id, cenc_content_id)) {
return false;
}
} else if (mime_type == WEBM_MIME_TYPE) {
} else if (init_data.is_webm()) {
LicenseRequest_ContentIdentification_WebM* webm_content_id =
content_id->mutable_webm_id();
if (!init_data.empty()) {
webm_content_id->set_header(init_data);
if (!init_data.IsEmpty()) {
webm_content_id->set_header(init_data.data());
} else if (privacy_mode_enabled && !stored_init_data_.empty()) {
webm_content_id->set_header(stored_init_data_);
} else {
@@ -348,8 +347,8 @@ bool CdmLicense::PrepareKeyRequest(const std::string& mime_type,
return false;
}
} else {
LOGE("CdmLicense::PrepareKeyRequest: no support for MIME type %s",
mime_type.c_str());
LOGE("CdmLicense::PrepareKeyRequest: no support for init data type (%s)",
init_data.type().c_str());
return false;
}

View File

@@ -3,6 +3,8 @@
#include <errno.h>
#include <getopt.h>
#include <string>
#if defined(CHROMIUM_BUILD)
#include "base/at_exit.h"
#include "base/message_loop.h"
@@ -10,6 +12,7 @@
#include "cdm_engine.h"
#include "config_test_env.h"
#include "gtest/gtest.h"
#include "initialization_data.h"
#include "license_request.h"
#include "log.h"
#include "properties.h"
@@ -82,6 +85,10 @@ static wvcdm::CdmProvisioningResponse kValidJsonProvisioningResponse =
"gZYLHnFiQLZekZUbUUlWY_CwU9Cv0UtxqQ6Oa835_Ug8_n1BwX6BPbmbcWe2Y19laSnDWg4JBNl"
"F2CyP9N75jPtW9rVfjUSqKEPOwaIgwzNDkyMjM3NDcAAAA=\","
"\"signature\": \"r-LpoZcbbr2KtoPaFnuWTVBh4Gup1k8vn0ClW2qm32A=\"}}";
const std::string kCencMimeType = "video/mp4";
const std::string kWebmMimeType = "video/webm";
} // namespace
namespace wvcdm {
@@ -98,23 +105,26 @@ class WvCdmEngineTest : public testing::Test {
}
protected:
void GenerateKeyRequest(const std::string& key_system,
const std::string& key_id,
const std::string& mime_type) {
void GenerateKeyRequest(const std::string& key_id,
const std::string& init_data_type_string) {
CdmAppParameterMap app_parameters;
std::string server_url;
std::string init_data = key_id;
CdmKeySetId key_set_id;
// TODO(rfrias): Temporary change till b/9465346 is addressed
if (!Properties::extract_pssh_data() || mime_type != ISO_BMFF_MIME_TYPE) {
EXPECT_TRUE(CdmEngine::ExtractWidevinePssh(key_id, &init_data));
CdmInitData extracted_init_data = key_id;
InitializationData init_data_type(init_data_type_string);
if (!Properties::extract_pssh_data() || !init_data_type.is_cenc()) {
EXPECT_TRUE(InitializationData::ExtractWidevinePssh(
key_id,
&extracted_init_data));
}
InitializationData init_data(init_data_type_string, extracted_init_data);
EXPECT_EQ(KEY_MESSAGE,
cdm_engine_->GenerateKeyRequest(session_id_,
key_set_id,
mime_type,
init_data,
kLicenseTypeStreaming,
app_parameters,
@@ -122,8 +132,7 @@ class WvCdmEngineTest : public testing::Test {
&server_url));
}
void GenerateRenewalRequest(const std::string& key_system,
const std::string& init_data) {
void GenerateRenewalRequest() {
EXPECT_EQ(KEY_MESSAGE,
cdm_engine_->GenerateRenewalRequest(session_id_,
&key_msg_,
@@ -162,8 +171,7 @@ class WvCdmEngineTest : public testing::Test {
}
void VerifyNewKeyResponse(const std::string& server_url,
const std::string& client_auth,
std::string& init_data){
const std::string& client_auth){
std::string resp = GetKeyRequestResponse(server_url,
client_auth);
CdmKeySetId key_set_id;
@@ -171,8 +179,7 @@ class WvCdmEngineTest : public testing::Test {
}
void VerifyRenewalKeyResponse(const std::string& server_url,
const std::string& client_auth,
std::string& init_data){
const std::string& client_auth){
std::string resp = GetKeyRequestResponse(server_url,
client_auth);
EXPECT_EQ(KEY_ADDED, cdm_engine_->RenewKey(session_id_, resp));
@@ -199,41 +206,40 @@ TEST(WvCdmProvisioningTest, ProvisioningTest) {
}
TEST_F(WvCdmEngineTest, BaseIsoBmffMessageTest) {
GenerateKeyRequest(g_key_system, g_key_id, "video/mp4");
GenerateKeyRequest(g_key_id, kCencMimeType);
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");
GenerateKeyRequest(g_key_id, kWebmMimeType);
GetKeyRequestResponse(g_license_server, g_client_auth);
}
TEST_F(WvCdmEngineTest, WrongMessageTest) {
std::string wrong_message = a2bs_hex(g_wrong_key_id);
GenerateKeyRequest(g_key_system, wrong_message, "video/mp4");
GenerateKeyRequest(wrong_message, kCencMimeType);
GetKeyRequestResponse(g_license_server, g_client_auth);
}
TEST_F(WvCdmEngineTest, NormalDecryptionIsoBmff) {
GenerateKeyRequest(g_key_system, g_key_id, "video/mp4");
VerifyNewKeyResponse(g_license_server, g_client_auth, g_key_id);
GenerateKeyRequest(g_key_id, kCencMimeType);
VerifyNewKeyResponse(g_license_server, g_client_auth);
}
// 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);
GenerateKeyRequest(g_key_id, kWebmMimeType);
VerifyNewKeyResponse(g_license_server, g_client_auth);
}
TEST_F(WvCdmEngineTest, LicenseRenewal) {
GenerateKeyRequest(g_key_system, g_key_id, "video/mp4");
VerifyNewKeyResponse(g_license_server, g_client_auth, g_key_id);
GenerateKeyRequest(g_key_id, kCencMimeType);
VerifyNewKeyResponse(g_license_server, g_client_auth);
GenerateRenewalRequest(g_key_system, g_key_id);
GenerateRenewalRequest();
VerifyRenewalKeyResponse(server_url_.empty() ? g_license_server : server_url_,
g_client_auth,
g_key_id);
g_client_auth);
}
} // namespace wvcdm

View File

@@ -3,6 +3,7 @@
#include "crypto_session.h"
#include "license.h"
#include "gtest/gtest.h"
#include "initialization_data.h"
#include "policy_engine.h"
#include "string_conversions.h"
@@ -41,6 +42,10 @@ static const char* kInvalidResponse =
"2E4A47A24C06AC1B1A2061F21836A04E558BEE0244EF41C165F60CF23C580275"
"3175D48BAF1C6CA5759F200220A2BCCA86051A203FD4671075D9DEC6486A9317"
"70669993306831EDD57D77F34EFEB467470BA364";
const std::string kCencMimeType = "video/mp4";
const std::string kWebmMimeType = "video/webm";
}
namespace wvcdm {
@@ -79,8 +84,8 @@ TEST_F(LicenseTest, DISABLED_PrepareIsoBmffKeyRequest) {
CdmAppParameterMap app_parameters;
std::string server_url;
CdmSessionId session_id;
license_.PrepareKeyRequest("video/mp4",
a2bs_hex(kInitData),
InitializationData init_data(kCencMimeType, a2bs_hex(kInitData));
license_.PrepareKeyRequest(init_data,
kLicenseTypeStreaming,
app_parameters,
session_id,
@@ -95,8 +100,8 @@ TEST_F(LicenseTest, DISABLED_PrepareWebmKeyRequest) {
CdmAppParameterMap app_parameters;
std::string server_url;
CdmSessionId session_id;
license_.PrepareKeyRequest("video/webm",
a2bs_hex(kInitData),
InitializationData init_data(kWebmMimeType, a2bs_hex(kInitData));
license_.PrepareKeyRequest(init_data,
kLicenseTypeStreaming,
app_parameters,
session_id,
@@ -111,8 +116,8 @@ TEST_F(LicenseTest, DISABLED_HandleKeyResponseValid) {
CdmAppParameterMap app_parameters;
CdmSessionId session_id;
std::string server_url;
license_.PrepareKeyRequest("video/mp4",
a2bs_hex(kInitData),
InitializationData init_data(kCencMimeType, a2bs_hex(kInitData));
license_.PrepareKeyRequest(init_data,
kLicenseTypeStreaming,
app_parameters,
session_id,
@@ -128,8 +133,8 @@ TEST_F(LicenseTest, DISABLED_HandleKeyResponseInvalid) {
CdmAppParameterMap app_parameters;
CdmSessionId session_id;
std::string server_url;
license_.PrepareKeyRequest("video/mp4",
a2bs_hex(kInitData),
InitializationData init_data(kCencMimeType, a2bs_hex(kInitData));
license_.PrepareKeyRequest(init_data,
kLicenseTypeStreaming,
app_parameters,
session_id,

View File

@@ -18,6 +18,9 @@ class WvContentDecryptionModule {
WvContentDecryptionModule();
virtual ~WvContentDecryptionModule();
// Static methods
static bool SupportsInitDataType(const std::string& type);
// Session related methods
virtual CdmResponseType OpenSession(
const CdmKeySystem& key_system,
@@ -28,7 +31,7 @@ class WvContentDecryptionModule {
// Construct a valid license request.
virtual CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id,
const CdmKeySetId& key_set_id,
const std::string& mime_type,
const std::string& init_data_type,
const CdmInitData& init_data,
const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters,

View File

@@ -6,6 +6,7 @@
#include "cdm_client_property_set.h"
#include "cdm_engine.h"
#include "initialization_data.h"
#include "log.h"
#include "properties.h"
#include "wv_cdm_constants.h"
@@ -18,6 +19,10 @@ WvContentDecryptionModule::WvContentDecryptionModule()
WvContentDecryptionModule::~WvContentDecryptionModule() {}
bool WvContentDecryptionModule::SupportsInitDataType(const std::string& type) {
return InitializationData(type).is_supported();
}
CdmResponseType WvContentDecryptionModule::OpenSession(
const CdmKeySystem& key_system,
CdmClientPropertySet* property_set,
@@ -38,7 +43,7 @@ CdmResponseType WvContentDecryptionModule::CloseSession(
CdmResponseType WvContentDecryptionModule::GenerateKeyRequest(
const CdmSessionId& session_id,
const CdmKeySetId& key_set_id,
const std::string& mime_type,
const std::string& init_data_type,
const CdmInitData& init_data,
const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters,
@@ -50,8 +55,9 @@ CdmResponseType WvContentDecryptionModule::GenerateKeyRequest(
if (sts != NO_ERROR)
return sts;
}
sts = cdm_engine_->GenerateKeyRequest(session_id, key_set_id, mime_type,
init_data, license_type,
InitializationData initialization_data(init_data_type, init_data);
sts = cdm_engine_->GenerateKeyRequest(session_id, key_set_id,
initialization_data, license_type,
app_parameters, key_request,
server_url);

View File

@@ -19,7 +19,7 @@ class WVDrmFactory : public android::DrmFactory {
virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]);
virtual bool isContentTypeSupported(const android::String8 &mimeType);
virtual bool isContentTypeSupported(const android::String8 &initDataType);
virtual android::status_t createDrmPlugin(const uint8_t uuid[16],
android::DrmPlugin** plugin);

View File

@@ -53,7 +53,7 @@ class WVDrmPlugin : public android::DrmPlugin,
virtual status_t getKeyRequest(
const Vector<uint8_t>& scope,
const Vector<uint8_t>& initData,
const String8& mimeType,
const String8& initDataType,
KeyType keyType,
const KeyedVector<String8, String8>& optionalParameters,
Vector<uint8_t>& request,

View File

@@ -128,7 +128,7 @@ status_t WVDrmPlugin::closeSession(const Vector<uint8_t>& sessionId) {
status_t WVDrmPlugin::getKeyRequest(
const Vector<uint8_t>& scope,
const Vector<uint8_t>& initData,
const String8& mimeType,
const String8& initDataType,
KeyType keyType,
const KeyedVector<String8, String8>& optionalParameters,
Vector<uint8_t>& request,
@@ -149,17 +149,20 @@ status_t WVDrmPlugin::getKeyRequest(
return android::ERROR_DRM_CANNOT_HANDLE;
}
string cdmMimeType = mimeType.string();
string cdmMimeType = initDataType.string();
// Provide backwards-compatibility for apps that pass non-EME-compatible MIME
// types.
if (cdmMimeType != wvcdm::ISO_BMFF_MIME_TYPE &&
cdmMimeType != wvcdm::WEBM_MIME_TYPE) {
cdmMimeType = wvcdm::ISO_BMFF_MIME_TYPE;
if (cdmMimeType != wvcdm::ISO_BMFF_VIDEO_MIME_TYPE &&
cdmMimeType != wvcdm::ISO_BMFF_AUDIO_MIME_TYPE &&
cdmMimeType != wvcdm::WEBM_VIDEO_MIME_TYPE &&
cdmMimeType != wvcdm::WEBM_AUDIO_MIME_TYPE) {
cdmMimeType = wvcdm::ISO_BMFF_VIDEO_MIME_TYPE;
}
CdmInitData processedInitData;
if (cdmMimeType == wvcdm::ISO_BMFF_MIME_TYPE) {
if (cdmMimeType == wvcdm::ISO_BMFF_VIDEO_MIME_TYPE ||
cdmMimeType == wvcdm::ISO_BMFF_AUDIO_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

View File

@@ -11,6 +11,7 @@
#include "utils/Errors.h"
#include "wv_cdm_constants.h"
#include "WVCDMSingleton.h"
#include "wv_content_decryption_module.h"
#include "WVDrmPlugin.h"
#include "WVUUID.h"
@@ -24,10 +25,9 @@ bool WVDrmFactory::isCryptoSchemeSupported(const uint8_t uuid[16]) {
return isWidevineUUID(uuid);
}
bool WVDrmFactory::isContentTypeSupported(const String8 &mimeType) {
// Support ISO-BMFF (video/mp4) and WebM (video/webm).
return mimeType == wvcdm::ISO_BMFF_MIME_TYPE.c_str() ||
mimeType == wvcdm::WEBM_MIME_TYPE.c_str();
bool WVDrmFactory::isContentTypeSupported(const String8 &initDataType) {
return wvcdm::WvContentDecryptionModule::SupportsInitDataType(
initDataType.string());
}
status_t WVDrmFactory::createDrmPlugin(const uint8_t uuid[16],

View File

@@ -45,10 +45,16 @@ TEST(WVDrmFactoryTest, SupportsSupportedContainerFormats) {
WVDrmFactory factory;
EXPECT_TRUE(factory.isContentTypeSupported(String8("video/mp4"))) <<
"WVPluginFactory does not support ISO-BMFF";
"WVPluginFactory does not support ISO-BMFF video";
EXPECT_TRUE(factory.isContentTypeSupported(String8("audio/mp4"))) <<
"WVPluginFactory does not support ISO-BMFF audio";
EXPECT_TRUE(factory.isContentTypeSupported(String8("video/webm"))) <<
"WVPluginFactory does not support WebM";
"WVPluginFactory does not support WebM video";
EXPECT_TRUE(factory.isContentTypeSupported(String8("audio/webm"))) <<
"WVPluginFactory does not support WebM audio";
}
TEST(WVDrmFactoryTest, DoesNotSupportUnsupportedContainerFormats) {