| 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..88baa44062b74920ed0ee272db53ec8c29824e7b 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
|
|
|