diff options
Diffstat (limited to 'webkit/media/crypto/ppapi/cdm_wrapper.cc')
-rw-r--r-- | webkit/media/crypto/ppapi/cdm_wrapper.cc | 345 |
1 files changed, 216 insertions, 129 deletions
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 |