Chromium Code Reviews| Index: webkit/media/crypto/ppapi/cdm_wrapper.cc |
| diff --git a/webkit/media/crypto/ppapi/cdm_wrapper.cc b/webkit/media/crypto/ppapi/cdm_wrapper.cc |
| index 8faee29e035df911fece570efbaaa0eeb50b7715..2fdf6808fdc95cedb3fd85b611593691c61f0425 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 blow 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) |
|
ddorwin
2012/08/27 17:33:54
!init_data_ptr
xhwang
2012/08/27 18:07:26
Done.
|
| + 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; |
|
xhwang
2012/08/25 00:04:32
Hello all,
When I look at this code I hate myself
ddorwin
2012/08/27 17:33:54
My thoughts:
The point of CDM.h is to provide a (s
xhwang
2012/08/27 18:07:26
Sounds good. I'll do the helper function later (tr
|
| + |
| + 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 |