diff options
author | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-28 18:57:40 +0000 |
---|---|---|
committer | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-28 18:57:40 +0000 |
commit | d341e33493ca212af3fa5947afa5fcfaef76503e (patch) | |
tree | cd05fbde518ad841003327b04c7b45c6d33d31eb /webkit/media | |
parent | a224c54fa4fc25f8f51c285b510cd850a2c48472 (diff) | |
download | chromium_src-d341e33493ca212af3fa5947afa5fcfaef76503e.zip chromium_src-d341e33493ca212af3fa5947afa5fcfaef76503e.tar.gz chromium_src-d341e33493ca212af3fa5947afa5fcfaef76503e.tar.bz2 |
Hook up CDM calls in CdmWrapper.
Updated pp::ContentDecryptor_Private methods in CdmWrapper to call into the CDM. Also updated the event firing through pp::ContentDecryptor_Private interface in CdmWrapper.
BUG=138139
TEST=none
Review URL: https://chromiumcodereview.appspot.com/10876014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@153704 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/media')
-rw-r--r-- | webkit/media/crypto/key_systems.cc | 2 | ||||
-rw-r--r-- | webkit/media/crypto/ppapi/cdm_wrapper.cc | 345 | ||||
-rw-r--r-- | webkit/media/crypto/ppapi/content_decryption_module.h | 44 |
3 files changed, 255 insertions, 136 deletions
diff --git a/webkit/media/crypto/key_systems.cc b/webkit/media/crypto/key_systems.cc index 11a307c..b85c9c4 100644 --- a/webkit/media/crypto/key_systems.cc +++ b/webkit/media/crypto/key_systems.cc @@ -48,7 +48,7 @@ supported_format_key_system_combinations[] = { static const KeySystemPluginTypePair key_system_to_plugin_type_mapping[] = { // TODO(xhwang): Update this with the real plugin name. - { kExternalClearKeyKeySystem, "application/x-ppapi-example" } + { kExternalClearKeyKeySystem, "application/x-ppapi-clearkey-cdm" } }; static bool IsSupportedKeySystemWithContainerAndCodec( diff --git a/webkit/media/crypto/ppapi/cdm_wrapper.cc b/webkit/media/crypto/ppapi/cdm_wrapper.cc index 8faee29..88baa44 100644 --- a/webkit/media/crypto/ppapi/cdm_wrapper.cc +++ b/webkit/media/crypto/ppapi/cdm_wrapper.cc @@ -2,7 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <cstring> // For std::memcpy. +#include <cstring> // For memcpy. +#include <vector> #include "base/compiler_specific.h" // For OVERRIDE. #include "ppapi/c/pp_errors.h" @@ -20,27 +21,21 @@ #include "ppapi/cpp/dev/buffer_dev.h" #include "ppapi/cpp/private/content_decryptor_private.h" #include "ppapi/utility/completion_callback_factory.h" +#include "webkit/media/crypto/ppapi/content_decryption_module.h" namespace { -struct DecryptorMessage { - DecryptorMessage() : media_error(0), system_code(0) {} - std::string key_system; - std::string session_id; - std::string default_url; - std::string message_data; - int32_t media_error; - int32_t system_code; -}; - -struct DecryptedBlock { - DecryptedBlock() { - std::memset(reinterpret_cast<void*>(&decrypted_block_info), - 0, - sizeof(decrypted_block_info)); - } - std::string decrypted_data; - PP_DecryptedBlockInfo decrypted_block_info; +// This must be consistent with MediaKeyError defined in the spec: +// http://goo.gl/rbdnR +// TODO(xhwang): Add PP_MediaKeyError enum to avoid later static_cast in +// PluginInstance. +enum MediaKeyError { + kUnknownError = 1, + kClientError, + kServiceError, + kOutputError, + kHardwareChangeError, + kDomainError }; bool IsMainThread() { @@ -58,16 +53,25 @@ void CallOnMain(pp::CompletionCallback cb) { } // namespace +namespace webkit_media { // A wrapper class for abstracting away PPAPI interaction and threading for a // Content Decryption Module (CDM). -class CDMWrapper : public pp::Instance, +class CdmWrapper : public pp::Instance, public pp::ContentDecryptor_Private { public: - CDMWrapper(PP_Instance instance, pp::Module* module); - virtual ~CDMWrapper() {} + CdmWrapper(PP_Instance instance, pp::Module* module); + virtual ~CdmWrapper(); + + virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) { + return true; + } // PPP_ContentDecryptor_Private methods + // Note: As per comments in PPP_ContentDecryptor_Private, these calls should + // return false if the call was not forwarded to the CDM and should return + // true otherwise. Once the call reaches the CDM, the call result/status + // should be reported through the PPB_ContentDecryptor_Private interface. virtual bool GenerateKeyRequest(const std::string& key_system, pp::VarArrayBuffer init_data) OVERRIDE; virtual bool AddKey(const std::string& session_id, @@ -77,163 +81,244 @@ class CDMWrapper : public pp::Instance, virtual bool Decrypt( pp::Buffer_Dev encrypted_buffer, const PP_EncryptedBlockInfo& encrypted_block_info) OVERRIDE; - virtual bool DecryptAndDecode( pp::Buffer_Dev encrypted_buffer, - const PP_EncryptedBlockInfo& encrypted_block_info) OVERRIDE { - return false; - } - - virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) { - return true; - } + const PP_EncryptedBlockInfo& encrypted_block_info) OVERRIDE; private: - PP_Resource StringToBufferResource(const std::string& str); + // Creates a PP_Resource containing a PPB_Buffer_Impl, copies |data| into the + // buffer resource, and returns it. Returns a an invalid PP_Resource with an + // ID of 0 on failure. Upon success, the returned Buffer resource has a + // reference count of 1. + PP_Resource MakeBufferResource(const uint8_t* data, uint32_t data_size); // <code>PPB_ContentDecryptor_Private</code> dispatchers. These are passed to // <code>callback_factory_</code> to ensure that calls into // <code>PPP_ContentDecryptor_Private</code> are asynchronous. - void NeedKey(int32_t result, const DecryptorMessage& decryptor_message); - void KeyAdded(int32_t result, const DecryptorMessage& decryptor_message); - void KeyMessage(int32_t result, const DecryptorMessage& decryptor_message); - void KeyError(int32_t result, const DecryptorMessage& decryptor_message); - void DeliverBlock(int32_t result, const DecryptedBlock& decrypted_block); - - pp::CompletionCallbackFactory<CDMWrapper> callback_factory_; + void KeyAdded(int32_t result, const std::string& session_id); + void KeyMessage(int32_t result, cdm::KeyMessage& key_message); + void KeyError(int32_t result, const std::string& session_id); + void DeliverBlock(int32_t result, + const cdm::Status& status, + cdm::OutputBuffer& output_buffer, + const PP_DecryptTrackingInfo& tracking_info); + + pp::CompletionCallbackFactory<CdmWrapper> callback_factory_; + cdm::ContentDecryptionModule* cdm_; + std::string key_system_; }; -CDMWrapper::CDMWrapper(PP_Instance instance, - pp::Module* module) +CdmWrapper::CdmWrapper(PP_Instance instance, pp::Module* module) : pp::Instance(instance), - pp::ContentDecryptor_Private(this) { + pp::ContentDecryptor_Private(this), + cdm_(NULL) { callback_factory_.Initialize(this); } -bool CDMWrapper::GenerateKeyRequest(const std::string& key_system, +CdmWrapper::~CdmWrapper() { + if (cdm_) + DestroyCdmInstance(cdm_); +} + +bool CdmWrapper::GenerateKeyRequest(const std::string& key_system, pp::VarArrayBuffer init_data) { - PP_DCHECK(!key_system.empty() && init_data.ByteLength()); + PP_DCHECK(!key_system.empty()); + + if (!cdm_) { + cdm_ = CreateCdmInstance(); + if (!cdm_) + return false; + } - DecryptorMessage decryptor_message; - decryptor_message.key_system = key_system; - decryptor_message.session_id = "0"; - decryptor_message.default_url = "http://www.google.com"; - decryptor_message.message_data = "GenerateKeyRequest"; + cdm::KeyMessage key_request; + cdm::Status status = cdm_->GenerateKeyRequest( + reinterpret_cast<const uint8_t*>(init_data.Map()), + init_data.ByteLength(), + &key_request); + + if (status != cdm::kSuccess || + !key_request.message || + key_request.message_size == 0) { + CallOnMain(callback_factory_.NewCallback(&CdmWrapper::KeyError, + std::string())); + return true; + } + + // TODO(xhwang): Remove unnecessary CallOnMain calls here and below once we + // only support out-of-process. + // If running out-of-process, PPB calls will always behave asynchronously + // since IPC is involved. In that case, if we are already on main thread, + // we don't need to use CallOnMain to help us call PPB call on main thread, + // or to help call PPB asynchronously. + key_system_ = key_system; + CallOnMain(callback_factory_.NewCallback(&CdmWrapper::KeyMessage, + key_request)); - CallOnMain(callback_factory_.NewCallback(&CDMWrapper::KeyMessage, - decryptor_message)); return true; } -bool CDMWrapper::AddKey(const std::string& session_id, +bool CdmWrapper::AddKey(const std::string& session_id, pp::VarArrayBuffer key, pp::VarArrayBuffer init_data) { - const std::string key_string(reinterpret_cast<char*>(key.Map()), - key.ByteLength()); - const std::string init_data_string(reinterpret_cast<char*>(init_data.Map()), - init_data.ByteLength()); - - PP_DCHECK(!session_id.empty() && !key_string.empty()); - - DecryptorMessage decryptor_message; - decryptor_message.key_system = "AddKey"; - decryptor_message.session_id = "0"; - decryptor_message.default_url = "http://www.google.com"; - decryptor_message.message_data = "AddKey"; - CallOnMain(callback_factory_.NewCallback(&CDMWrapper::KeyAdded, - decryptor_message)); + const uint8_t* key_ptr = reinterpret_cast<const uint8_t*>(key.Map()); + int key_size = key.ByteLength(); + const uint8_t* init_data_ptr = + reinterpret_cast<const uint8_t*>(init_data.Map()); + int init_data_size = init_data.ByteLength(); + + if (!key_ptr || key_size <= 0 || !init_data_ptr || init_data_size <= 0) + return false; + + PP_DCHECK(cdm_); + cdm::Status status = cdm_->AddKey(session_id.data(), session_id.size(), + key_ptr, key_size, + init_data_ptr, init_data_size); + + if (status != cdm::kSuccess) { + CallOnMain(callback_factory_.NewCallback(&CdmWrapper::KeyError, + session_id)); + return true; + } + + CallOnMain(callback_factory_.NewCallback(&CdmWrapper::KeyAdded, session_id)); return true; } -bool CDMWrapper::CancelKeyRequest(const std::string& session_id) { - // TODO(tomfinegan): cancel pending key request in CDM. +bool CdmWrapper::CancelKeyRequest(const std::string& session_id) { + PP_DCHECK(cdm_); - PP_DCHECK(!session_id.empty()); + cdm::Status status = cdm_->CancelKeyRequest(session_id.data(), + session_id.size()); - DecryptorMessage decryptor_message; - decryptor_message.key_system = "CancelKeyRequest"; - decryptor_message.session_id = "0"; - decryptor_message.default_url = "http://www.google.com"; - decryptor_message.message_data = "CancelKeyRequest"; + if (status != cdm::kSuccess) { + CallOnMain(callback_factory_.NewCallback(&CdmWrapper::KeyError, + session_id)); + return true; + } - CallOnMain(callback_factory_.NewCallback(&CDMWrapper::KeyMessage, - decryptor_message)); return true; } -bool CDMWrapper::Decrypt(pp::Buffer_Dev encrypted_buffer, +bool CdmWrapper::Decrypt(pp::Buffer_Dev encrypted_buffer, const PP_EncryptedBlockInfo& encrypted_block_info) { PP_DCHECK(!encrypted_buffer.is_null()); + PP_DCHECK(cdm_); + + // TODO(xhwang): Simplify the following data conversion. + cdm::InputBuffer input_buffer; + input_buffer.data = reinterpret_cast<uint8_t*>(encrypted_buffer.data()); + input_buffer.data_size = encrypted_buffer.size(); + input_buffer.data_offset = encrypted_block_info.data_offset; + input_buffer.key_id = encrypted_block_info.key_id; + input_buffer.key_id_size = encrypted_block_info.key_id_size; + input_buffer.iv = encrypted_block_info.iv; + input_buffer.iv_size = encrypted_block_info.iv_size; + input_buffer.checksum = encrypted_block_info.checksum; + input_buffer.checksum_size = encrypted_block_info.checksum_size; + input_buffer.num_subsamples = encrypted_block_info.num_subsamples; + std::vector<cdm::SubsampleEntry> subsamples; + for (uint32_t i = 0; i < encrypted_block_info.num_subsamples; ++i) { + subsamples.push_back(cdm::SubsampleEntry( + encrypted_block_info.subsamples[i].clear_bytes, + encrypted_block_info.subsamples[i].cipher_bytes)); + } + input_buffer.subsamples = &subsamples[0]; + input_buffer.timestamp = encrypted_block_info.tracking_info.timestamp; + + cdm::OutputBuffer output_buffer; + cdm::Status status = cdm_->Decrypt(input_buffer, &output_buffer); + + CallOnMain(callback_factory_.NewCallback( + &CdmWrapper::DeliverBlock, + status, + output_buffer, + encrypted_block_info.tracking_info)); - DecryptedBlock decrypted_block; - decrypted_block.decrypted_data = "Pretend I'm decrypted data!"; - decrypted_block.decrypted_block_info.result = PP_DECRYPTRESULT_SUCCESS; - decrypted_block.decrypted_block_info.tracking_info = - encrypted_block_info.tracking_info; - - // TODO(tomfinegan): This would end up copying a lot of data in the real - // implementation if we continue passing std::strings around. It *might* not - // be such a big deal w/a real CDM. We may be able to simply pass a pointer - // into the CDM. Otherwise we could look into using std::tr1::shared_ptr - // instead of passing a giant std::string filled with encrypted data. - CallOnMain(callback_factory_.NewCallback(&CDMWrapper::DeliverBlock, - decrypted_block)); return true; } -PP_Resource CDMWrapper::StringToBufferResource(const std::string& str) { - if (str.empty()) +bool CdmWrapper::DecryptAndDecode( + pp::Buffer_Dev encrypted_buffer, + const PP_EncryptedBlockInfo& encrypted_block_info) { + return false; +} + +PP_Resource CdmWrapper::MakeBufferResource(const uint8_t* data, + uint32_t data_size) { + if (!data || !data_size) return 0; - pp::Buffer_Dev buffer(this, str.size()); + pp::Buffer_Dev buffer(this, data_size); if (!buffer.data()) return 0; - std::memcpy(buffer.data(), str.data(), str.size()); + memcpy(buffer.data(), data, data_size); + return buffer.detach(); } -void CDMWrapper::NeedKey(int32_t result, - const DecryptorMessage& decryptor_message) { - const std::string& message_data = decryptor_message.message_data; - pp::VarArrayBuffer init_data(message_data.size()); - std::memcpy(init_data.Map(), message_data.data(), message_data.size()); - pp::ContentDecryptor_Private::NeedKey(decryptor_message.key_system, - decryptor_message.session_id, - init_data); +void CdmWrapper::KeyAdded(int32_t result, const std::string& session_id) { + pp::ContentDecryptor_Private::KeyAdded(key_system_, session_id); } -void CDMWrapper::KeyAdded(int32_t result, - const DecryptorMessage& decryptor_message) { - pp::ContentDecryptor_Private::KeyAdded(decryptor_message.key_system, - decryptor_message.session_id); +void CdmWrapper::KeyMessage(int32_t result, + cdm::KeyMessage& key_message) { + pp::Buffer_Dev message_buffer(MakeBufferResource(key_message.message, + key_message.message_size)); + pp::ContentDecryptor_Private::KeyMessage( + key_system_, + std::string(key_message.session_id, key_message.session_id_size), + message_buffer, + std::string(key_message.default_url, key_message.default_url_size)); + + // TODO(xhwang): Fix this. This is not always safe as the memory is allocated + // in another shared object. + delete [] key_message.session_id; + key_message.session_id = NULL; + delete [] key_message.message; + key_message.message = NULL; + delete [] key_message.default_url; + key_message.default_url = NULL; } -void CDMWrapper::KeyMessage(int32_t result, - const DecryptorMessage& decryptor_message) { - pp::Buffer_Dev message_buffer( - StringToBufferResource(decryptor_message.message_data)); - pp::ContentDecryptor_Private::KeyMessage(decryptor_message.key_system, - decryptor_message.session_id, - message_buffer, - decryptor_message.default_url); +// TODO(xhwang): Support MediaKeyError (see spec: http://goo.gl/rbdnR) in CDM +// interface and in this function. +void CdmWrapper::KeyError(int32_t result, const std::string& session_id) { + pp::ContentDecryptor_Private::KeyError(key_system_, + session_id, + kUnknownError, + 0); } -void CDMWrapper::KeyError(int32_t result, - const DecryptorMessage& decryptor_message) { - pp::ContentDecryptor_Private::KeyError(decryptor_message.key_system, - decryptor_message.session_id, - decryptor_message.media_error, - decryptor_message.system_code); -} +void CdmWrapper::DeliverBlock(int32_t result, + const cdm::Status& status, + cdm::OutputBuffer& output_buffer, + const PP_DecryptTrackingInfo& tracking_info) { + pp::Buffer_Dev decrypted_buffer(MakeBufferResource(output_buffer.data, + output_buffer.data_size)); -void CDMWrapper::DeliverBlock(int32_t result, - const DecryptedBlock& decrypted_block) { - pp::Buffer_Dev decrypted_buffer( - StringToBufferResource(decrypted_block.decrypted_data)); - pp::ContentDecryptor_Private::DeliverBlock( - decrypted_buffer, - decrypted_block.decrypted_block_info); + PP_DecryptedBlockInfo decrypted_block_info; + decrypted_block_info.tracking_info.request_id = tracking_info.request_id; + decrypted_block_info.tracking_info.timestamp = output_buffer.timestamp; + + switch (status) { + case cdm::kSuccess: + decrypted_block_info.result = PP_DECRYPTRESULT_SUCCESS; + break; + case cdm::kErrorNoKey: + decrypted_block_info.result = PP_DECRYPTRESULT_DECRYPT_NOKEY; + break; + default: + decrypted_block_info.result = PP_DECRYPTRESULT_DECRYPT_ERROR; + } + + pp::ContentDecryptor_Private::DeliverBlock(decrypted_buffer, + decrypted_block_info); + + // TODO(xhwang): Fix this. This is not always safe as the memory is allocated + // in another shared object. + delete [] output_buffer.data; + output_buffer.data = NULL; } // This object is the global object representing this plugin library as long @@ -244,15 +329,17 @@ class MyModule : public pp::Module { virtual ~MyModule() {} virtual pp::Instance* CreateInstance(PP_Instance instance) { - return new CDMWrapper(instance, this); + return new CdmWrapper(instance, this); } }; +} // namespace webkit_media + namespace pp { // Factory function for your specialization of the Module object. Module* CreateModule() { - return new MyModule(); + return new webkit_media::MyModule(); } } // namespace pp diff --git a/webkit/media/crypto/ppapi/content_decryption_module.h b/webkit/media/crypto/ppapi/content_decryption_module.h index 0023d49..cb21258 100644 --- a/webkit/media/crypto/ppapi/content_decryption_module.h +++ b/webkit/media/crypto/ppapi/content_decryption_module.h @@ -33,7 +33,17 @@ enum Status { kErrorNoKey }; +// TODO(xhwang): Use int32_t instead of uint32_t for sizes here and below and +// update checks to include <0. struct KeyMessage { + KeyMessage() + : session_id(NULL), + session_id_size(0), + message(NULL), + message_size(0), + default_url(NULL), + default_url_size(0) {} + char* session_id; uint32_t session_id_size; uint8_t* message; @@ -65,33 +75,55 @@ struct KeyMessage { // // TODO(xhwang): Add checks to make sure these structs have fixed layout. struct SubsampleEntry { + SubsampleEntry(uint32_t clear_bytes, uint32_t cipher_bytes) + : clear_bytes(clear_bytes), cipher_bytes(cipher_bytes) {} + uint32_t clear_bytes; uint32_t cipher_bytes; }; struct InputBuffer { - uint8_t* data; // Pointer to the beginning of the input data. + InputBuffer() + : data(NULL), + data_size(0), + data_offset(0), + key_id(NULL), + key_id_size(0), + iv(NULL), + iv_size(0), + checksum(NULL), + checksum_size(0), + subsamples(NULL), + num_subsamples(0), + timestamp(0) {} + + const uint8_t* data; // Pointer to the beginning of the input data. uint32_t data_size; // Size (in bytes) of |data|. uint32_t data_offset; // Number of bytes to be discarded before decryption. - uint8_t* key_id; // Key ID to identify the decryption key. + const uint8_t* key_id; // Key ID to identify the decryption key. uint32_t key_id_size; // Size (in bytes) of |key_id|. - uint8_t* iv; // Initialization vector. + const uint8_t* iv; // Initialization vector. uint32_t iv_size; // Size (in bytes) of |iv|. - uint8_t* checksum; + const uint8_t* checksum; uint32_t checksum_size; // Size (in bytes) of the |checksum|. - struct SubsampleEntry* subsamples; + const struct SubsampleEntry* subsamples; uint32_t num_subsamples; // Number of subsamples in |subsamples|. int64_t timestamp; // Presentation timestamp in microseconds. }; struct OutputBuffer { - uint8_t* data; // Pointer to the beginning of the output data. + OutputBuffer() + : data(NULL), + data_size(0), + timestamp(0) {} + + const uint8_t* data; // Pointer to the beginning of the output data. uint32_t data_size; // Size (in bytes) of |data|. int64_t timestamp; // Presentation timestamp in microseconds. |