From 7eea20df86f4174067363f6bc7c74a47fad17b7a Mon Sep 17 00:00:00 2001 From: "John \"Juce\" Bruce" Date: Wed, 2 Apr 2014 15:39:12 -0700 Subject: [PATCH] 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 --- libwvdrmengine/cdm/Android.mk | 1 + libwvdrmengine/cdm/core/include/cdm_engine.h | 22 ++-- libwvdrmengine/cdm/core/include/cdm_session.h | 6 +- .../cdm/core/include/initialization_data.h | 47 +++++++ libwvdrmengine/cdm/core/include/license.h | 4 +- .../cdm/core/include/wv_cdm_constants.h | 6 +- libwvdrmengine/cdm/core/src/cdm_engine.cpp | 97 +------------- libwvdrmengine/cdm/core/src/cdm_session.cpp | 31 ++--- .../cdm/core/src/initialization_data.cpp | 121 ++++++++++++++++++ libwvdrmengine/cdm/core/src/license.cpp | 29 ++--- .../cdm/core/test/cdm_engine_test.cpp | 56 ++++---- .../cdm/core/test/license_unittest.cpp | 21 +-- .../include/wv_content_decryption_module.h | 5 +- .../cdm/src/wv_content_decryption_module.cpp | 12 +- libwvdrmengine/include/WVDrmFactory.h | 2 +- libwvdrmengine/mediadrm/include/WVDrmPlugin.h | 2 +- libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp | 15 ++- libwvdrmengine/src/WVDrmFactory.cpp | 8 +- .../test/unit/WVDrmFactory_test.cpp | 10 +- 19 files changed, 295 insertions(+), 200 deletions(-) create mode 100644 libwvdrmengine/cdm/core/include/initialization_data.h create mode 100644 libwvdrmengine/cdm/core/src/initialization_data.cpp diff --git a/libwvdrmengine/cdm/Android.mk b/libwvdrmengine/cdm/Android.mk index 237366e9..84e9d3c7 100644 --- a/libwvdrmengine/cdm/Android.mk +++ b/libwvdrmengine/cdm/Android.mk @@ -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 \ diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index dc5b7825..a9d79b1f 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -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,14 +36,14 @@ class CdmEngine : public TimerHandler { // License related methods // Construct a valid license request - virtual CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id, - const CdmKeySetId& key_set_id, - const std::string& mime_type, - const CdmInitData& init_data, - const CdmLicenseType license_type, - CdmAppParameterMap& app_parameters, - CdmKeyMessage* key_request, - std::string* server_url); + virtual CdmResponseType GenerateKeyRequest( + const CdmSessionId& session_id, + const CdmKeySetId& key_set_id, + const InitializationData& init_data, + const CdmLicenseType license_type, + CdmAppParameterMap& app_parameters, + CdmKeyMessage* key_request, + std::string* server_url); // Accept license response and extract key info. virtual CdmResponseType AddKey(const CdmSessionId& session_id, @@ -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 diff --git a/libwvdrmengine/cdm/core/include/cdm_session.h b/libwvdrmengine/cdm/core/include/cdm_session.h index ac57f844..fa06a7e3 100644 --- a/libwvdrmengine/cdm/core/include/cdm_session.h +++ b/libwvdrmengine/cdm/core/include/cdm_session.h @@ -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, diff --git a/libwvdrmengine/cdm/core/include/initialization_data.h b/libwvdrmengine/cdm/core/include/initialization_data.h new file mode 100644 index 00000000..5d3a16f7 --- /dev/null +++ b/libwvdrmengine/cdm/core/include/initialization_data.h @@ -0,0 +1,47 @@ +// Copyright 2014 Google Inc. All Rights Reserved. + +#ifndef CORE_INCLUDE_INITIALIZATION_DATA_H_ +#define CORE_INCLUDE_INITIALIZATION_DATA_H_ + +#include + +#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_ diff --git a/libwvdrmengine/cdm/core/include/license.h b/libwvdrmengine/cdm/core/include/license.h index f888c9a0..5f94521e 100644 --- a/libwvdrmengine/cdm/core/include/license.h +++ b/libwvdrmengine/cdm/core/include/license.h @@ -5,6 +5,7 @@ #include +#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, diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h index 58cbdf38..f4874ebd 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h @@ -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_ diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index dd61c4ab..3514a192 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -5,7 +5,6 @@ #include #include -#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(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 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 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); diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index b88358c0..6dc3ba2d 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -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; } diff --git a/libwvdrmengine/cdm/core/src/initialization_data.cpp b/libwvdrmengine/cdm/core/src/initialization_data.cpp new file mode 100644 index 00000000..dc34e8af --- /dev/null +++ b/libwvdrmengine/cdm/core/src/initialization_data.cpp @@ -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(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 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 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 diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index 3085a070..ee9662cf 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -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; } diff --git a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp index 286c2658..ee6ae1ef 100644 --- a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp @@ -3,6 +3,8 @@ #include #include +#include + #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 diff --git a/libwvdrmengine/cdm/core/test/license_unittest.cpp b/libwvdrmengine/cdm/core/test/license_unittest.cpp index dbdae3a7..55326873 100644 --- a/libwvdrmengine/cdm/core/test/license_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/license_unittest.cpp @@ -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, diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index 9c85b2b2..53ead0ed 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -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, diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index 8ed59bb2..251e3f7f 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -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); diff --git a/libwvdrmengine/include/WVDrmFactory.h b/libwvdrmengine/include/WVDrmFactory.h index 7f09e417..b284b94f 100644 --- a/libwvdrmengine/include/WVDrmFactory.h +++ b/libwvdrmengine/include/WVDrmFactory.h @@ -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); diff --git a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h index 69be54ac..85e34055 100644 --- a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h +++ b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h @@ -53,7 +53,7 @@ class WVDrmPlugin : public android::DrmPlugin, virtual status_t getKeyRequest( const Vector& scope, const Vector& initData, - const String8& mimeType, + const String8& initDataType, KeyType keyType, const KeyedVector& optionalParameters, Vector& request, diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index 993f7770..c3db9119 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -128,7 +128,7 @@ status_t WVDrmPlugin::closeSession(const Vector& sessionId) { status_t WVDrmPlugin::getKeyRequest( const Vector& scope, const Vector& initData, - const String8& mimeType, + const String8& initDataType, KeyType keyType, const KeyedVector& optionalParameters, Vector& 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 diff --git a/libwvdrmengine/src/WVDrmFactory.cpp b/libwvdrmengine/src/WVDrmFactory.cpp index 50979e7d..b78b0892 100644 --- a/libwvdrmengine/src/WVDrmFactory.cpp +++ b/libwvdrmengine/src/WVDrmFactory.cpp @@ -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], diff --git a/libwvdrmengine/test/unit/WVDrmFactory_test.cpp b/libwvdrmengine/test/unit/WVDrmFactory_test.cpp index d6cfd1fa..e258f0e8 100644 --- a/libwvdrmengine/test/unit/WVDrmFactory_test.cpp +++ b/libwvdrmengine/test/unit/WVDrmFactory_test.cpp @@ -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) {