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)/certificate_provisioning.cpp \
$(CORE_SRC_DIR)/crypto_session.cpp \ $(CORE_SRC_DIR)/crypto_session.cpp \
$(CORE_SRC_DIR)/device_files.cpp \ $(CORE_SRC_DIR)/device_files.cpp \
$(CORE_SRC_DIR)/initialization_data.cpp \
$(CORE_SRC_DIR)/license.cpp \ $(CORE_SRC_DIR)/license.cpp \
$(CORE_SRC_DIR)/oemcrypto_adapter_dynamic.cpp \ $(CORE_SRC_DIR)/oemcrypto_adapter_dynamic.cpp \
$(CORE_SRC_DIR)/policy_engine.cpp \ $(CORE_SRC_DIR)/policy_engine.cpp \

View File

@@ -4,6 +4,7 @@
#define CDM_BASE_CDM_ENGINE_H_ #define CDM_BASE_CDM_ENGINE_H_
#include "certificate_provisioning.h" #include "certificate_provisioning.h"
#include "initialization_data.h"
#include "oemcrypto_adapter.h" #include "oemcrypto_adapter.h"
#include "timer.h" #include "timer.h"
#include "wv_cdm_types.h" #include "wv_cdm_types.h"
@@ -35,14 +36,14 @@ class CdmEngine : public TimerHandler {
// License related methods // License related methods
// Construct a valid license request // Construct a valid license request
virtual CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id, virtual CdmResponseType GenerateKeyRequest(
const CdmKeySetId& key_set_id, const CdmSessionId& session_id,
const std::string& mime_type, const CdmKeySetId& key_set_id,
const CdmInitData& init_data, const InitializationData& init_data,
const CdmLicenseType license_type, const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters, CdmAppParameterMap& app_parameters,
CdmKeyMessage* key_request, CdmKeyMessage* key_request,
std::string* server_url); std::string* server_url);
// Accept license response and extract key info. // Accept license response and extract key info.
virtual CdmResponseType AddKey(const CdmSessionId& session_id, virtual CdmResponseType AddKey(const CdmSessionId& session_id,
@@ -110,11 +111,6 @@ class CdmEngine : public TimerHandler {
virtual bool DetachEventListener(const CdmSessionId& session_id, virtual bool DetachEventListener(const CdmSessionId& session_id,
WvCdmEventListener* listener); 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:
// private methods // private methods
// Cancel all sessions // Cancel all sessions

View File

@@ -7,6 +7,7 @@
#include "crypto_session.h" #include "crypto_session.h"
#include "device_files.h" #include "device_files.h"
#include "initialization_data.h"
#include "license.h" #include "license.h"
#include "oemcrypto_adapter.h" #include "oemcrypto_adapter.h"
#include "policy_engine.h" #include "policy_engine.h"
@@ -34,10 +35,9 @@ class CdmSession {
const CdmSessionId& session_id() { return session_id_; } const CdmSessionId& session_id() { return session_id_; }
bool VerifySession(const CdmKeySystem& key_system, bool VerifySession(const CdmKeySystem& key_system,
const CdmInitData& init_data); const InitializationData& init_data);
CdmResponseType GenerateKeyRequest(const std::string& mime_type, CdmResponseType GenerateKeyRequest(const InitializationData& init_data,
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,

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 <set>
#include "initialization_data.h"
#include "wv_cdm_types.h" #include "wv_cdm_types.h"
namespace video_widevine_server { namespace video_widevine_server {
@@ -27,8 +28,7 @@ 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 std::string& mime_type, bool PrepareKeyRequest(const InitializationData& init_data,
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,

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_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 ISO_BMFF_VIDEO_MIME_TYPE = "video/mp4";
static const std::string WEBM_MIME_TYPE = "video/webm"; 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 } // namespace wvcdm
#endif // CDM_BASE_WV_CDM_CONSTANTS_H_ #endif // CDM_BASE_WV_CDM_CONSTANTS_H_

View File

@@ -5,7 +5,6 @@
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include "buffer_reader.h"
#include "cdm_session.h" #include "cdm_session.h"
#include "license_protocol.pb.h" #include "license_protocol.pb.h"
#include "log.h" #include "log.h"
@@ -124,8 +123,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 InitializationData& init_data,
const CdmInitData& init_data,
const CdmLicenseType license_type, const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters, CdmAppParameterMap& app_parameters,
CdmKeyMessage* key_request, 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, app_parameters, key_request,
server_url); server_url);
@@ -611,97 +609,6 @@ bool CdmEngine::CancelSessions() {
return true; 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() { void CdmEngine::EnablePolicyTimer() {
if (!policy_timer_.IsRunning()) if (!policy_timer_.IsRunning())
policy_timer_.Start(this, kCdmPolicyTimerDurationSeconds); policy_timer_.Start(this, kCdmPolicyTimerDurationSeconds);

View File

@@ -112,16 +112,16 @@ CdmResponseType CdmSession::RestoreOfflineSession(
} }
bool CdmSession::VerifySession(const CdmKeySystem& key_system, 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 // TODO(gmorgan): Compare key_system and init_data with value received
// during session startup - they should be the same. // during session startup - they should be the same.
return true; return true;
} }
CdmResponseType CdmSession::GenerateKeyRequest( CdmResponseType CdmSession::GenerateKeyRequest(
const std::string& mime_type, const CdmInitData& init_data, const InitializationData& init_data, const CdmLicenseType license_type,
const CdmLicenseType license_type, const CdmAppParameterMap& app_parameters, const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
CdmKeyMessage* key_request, std::string* server_url) { 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,22 +149,16 @@ 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) { if (!init_data.is_supported()) {
LOGW("CdmSession::GenerateKeyRequest: unknown MIME type"); LOGW("CdmSession::GenerateKeyRequest: unsupported init data type (%s)",
init_data.type().c_str());
return KEY_ERROR; return KEY_ERROR;
} }
if (init_data.empty() && !license_parser_.HasInitData()) { if (init_data.IsEmpty() && !license_parser_.HasInitData()) {
LOGW("CdmSession::GenerateKeyRequest: init data absent"); LOGW("CdmSession::GenerateKeyRequest: init data absent");
return KEY_ERROR; 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 (Properties::use_certificates_as_identification()) {
if (is_certificate_loaded_ || if (is_certificate_loaded_ ||
crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) { crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) {
@@ -176,15 +170,14 @@ CdmResponseType CdmSession::GenerateKeyRequest(
} }
} }
if (!license_parser_.PrepareKeyRequest(mime_type, extracted_init_data, if (!license_parser_.PrepareKeyRequest(init_data, license_type,
license_type, app_parameters, app_parameters, session_id_,
session_id_, key_request, key_request, server_url)) {
server_url)) {
return KEY_ERROR; return KEY_ERROR;
} }
if (license_type_ == kLicenseTypeOffline) { if (license_type_ == kLicenseTypeOffline) {
offline_init_data_ = extracted_init_data; offline_init_data_ = init_data.data();
offline_key_request_ = *key_request; offline_key_request_ = *key_request;
offline_release_server_url_ = *server_url; 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; return true;
} }
bool CdmLicense::PrepareKeyRequest(const std::string& mime_type, bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data,
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,
@@ -169,12 +168,12 @@ bool CdmLicense::PrepareKeyRequest(const std::string& mime_type,
LOGE("CdmLicense::PrepareKeyRequest: not initialized"); LOGE("CdmLicense::PrepareKeyRequest: not initialized");
return false; return false;
} }
if (mime_type != ISO_BMFF_MIME_TYPE && mime_type != WEBM_MIME_TYPE) { if (!init_data.is_supported()) {
LOGE("CdmLicense::PrepareKeyRequest: unsupported MIME type %s", LOGE("CdmLicense::PrepareKeyRequest: unsupported init data type (%s)",
mime_type.c_str()); init_data.type().c_str());
return false; 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"); LOGE("CdmLicense::PrepareKeyRequest: empty init data provided");
return false; return false;
} }
@@ -199,7 +198,7 @@ bool CdmLicense::PrepareKeyRequest(const std::string& mime_type,
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()) {
stored_init_data_ = init_data; stored_init_data_ = init_data.data();
return PrepareServiceCertificateRequest(signed_request, server_url); return PrepareServiceCertificateRequest(signed_request, server_url);
} }
@@ -315,12 +314,12 @@ bool CdmLicense::PrepareKeyRequest(const std::string& mime_type,
LicenseRequest_ContentIdentification* content_id = LicenseRequest_ContentIdentification* content_id =
license_request.mutable_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 = LicenseRequest_ContentIdentification_CENC* cenc_content_id =
content_id->mutable_cenc_id(); content_id->mutable_cenc_id();
if (!init_data.empty()) { if (!init_data.IsEmpty()) {
cenc_content_id->add_pssh(init_data); cenc_content_id->add_pssh(init_data.data());
} else if (privacy_mode_enabled && !stored_init_data_.empty()) { } else if (privacy_mode_enabled && !stored_init_data_.empty()) {
cenc_content_id->add_pssh(stored_init_data_); cenc_content_id->add_pssh(stored_init_data_);
} else { } else {
@@ -331,12 +330,12 @@ bool CdmLicense::PrepareKeyRequest(const std::string& mime_type,
if (!PrepareContentId(license_type, request_id, cenc_content_id)) { if (!PrepareContentId(license_type, request_id, cenc_content_id)) {
return false; return false;
} }
} else if (mime_type == WEBM_MIME_TYPE) { } else if (init_data.is_webm()) {
LicenseRequest_ContentIdentification_WebM* webm_content_id = LicenseRequest_ContentIdentification_WebM* webm_content_id =
content_id->mutable_webm_id(); content_id->mutable_webm_id();
if (!init_data.empty()) { if (!init_data.IsEmpty()) {
webm_content_id->set_header(init_data); webm_content_id->set_header(init_data.data());
} else if (privacy_mode_enabled && !stored_init_data_.empty()) { } else if (privacy_mode_enabled && !stored_init_data_.empty()) {
webm_content_id->set_header(stored_init_data_); webm_content_id->set_header(stored_init_data_);
} else { } else {
@@ -348,8 +347,8 @@ bool CdmLicense::PrepareKeyRequest(const std::string& mime_type,
return false; return false;
} }
} else { } else {
LOGE("CdmLicense::PrepareKeyRequest: no support for MIME type %s", LOGE("CdmLicense::PrepareKeyRequest: no support for init data type (%s)",
mime_type.c_str()); init_data.type().c_str());
return false; return false;
} }

View File

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

View File

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

View File

@@ -18,6 +18,9 @@ class WvContentDecryptionModule {
WvContentDecryptionModule(); WvContentDecryptionModule();
virtual ~WvContentDecryptionModule(); virtual ~WvContentDecryptionModule();
// Static methods
static bool SupportsInitDataType(const std::string& type);
// Session related methods // Session related methods
virtual CdmResponseType OpenSession( virtual CdmResponseType OpenSession(
const CdmKeySystem& key_system, const CdmKeySystem& key_system,
@@ -28,7 +31,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 std::string& init_data_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

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

View File

@@ -19,7 +19,7 @@ class WVDrmFactory : public android::DrmFactory {
virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]); 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], virtual android::status_t createDrmPlugin(const uint8_t uuid[16],
android::DrmPlugin** plugin); android::DrmPlugin** plugin);

View File

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

View File

@@ -128,7 +128,7 @@ status_t WVDrmPlugin::closeSession(const Vector<uint8_t>& sessionId) {
status_t WVDrmPlugin::getKeyRequest( status_t WVDrmPlugin::getKeyRequest(
const Vector<uint8_t>& scope, const Vector<uint8_t>& scope,
const Vector<uint8_t>& initData, const Vector<uint8_t>& initData,
const String8& mimeType, const String8& initDataType,
KeyType keyType, KeyType keyType,
const KeyedVector<String8, String8>& optionalParameters, const KeyedVector<String8, String8>& optionalParameters,
Vector<uint8_t>& request, Vector<uint8_t>& request,
@@ -149,17 +149,20 @@ status_t WVDrmPlugin::getKeyRequest(
return android::ERROR_DRM_CANNOT_HANDLE; 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 // Provide backwards-compatibility for apps that pass non-EME-compatible MIME
// types. // types.
if (cdmMimeType != wvcdm::ISO_BMFF_MIME_TYPE && if (cdmMimeType != wvcdm::ISO_BMFF_VIDEO_MIME_TYPE &&
cdmMimeType != wvcdm::WEBM_MIME_TYPE) { cdmMimeType != wvcdm::ISO_BMFF_AUDIO_MIME_TYPE &&
cdmMimeType = wvcdm::ISO_BMFF_MIME_TYPE; cdmMimeType != wvcdm::WEBM_VIDEO_MIME_TYPE &&
cdmMimeType != wvcdm::WEBM_AUDIO_MIME_TYPE) {
cdmMimeType = wvcdm::ISO_BMFF_VIDEO_MIME_TYPE;
} }
CdmInitData processedInitData; 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. // For ISO-BMFF, we need to wrap the init data in a new PSSH header.
static const char psshPrefix[] = { static const char psshPrefix[] = {
0, 0, 0, 0, // Total size 0, 0, 0, 0, // Total size

View File

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

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