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..933876f5cbe2bf04f340af4ea2f0b4db5a145281 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,18 @@ |
| #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 needs to be consistent with media::Decryptor::KeyError! |
|
Tom Finegan
2012/08/22 16:54:31
s/needs to/must/
dmichael (off chromium)
2012/08/23 18:11:29
Should these be defined in the API? If they were,
xhwang
2012/08/24 22:01:59
Done.
xhwang
2012/08/24 22:01:59
Good point, added a TODO to add a PP_MediaKeyError
|
| +enum KeyError { |
|
dmichael (off chromium)
2012/08/23 18:11:29
How about a different name, like KeyErrorCode, to
xhwang
2012/08/24 22:01:59
Renamed to MediaKeyError to be consistent to the s
|
| + kUnknownError = 1, |
| + kClientError, |
| + kServiceError, |
| + kOutputError, |
| + kHardwareChangeError, |
| + kDomainError |
| }; |
| bool IsMainThread() { |
| @@ -58,14 +50,19 @@ 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 |
| virtual bool GenerateKeyRequest(const std::string& key_system, |
| @@ -77,163 +74,226 @@ 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); |
| + 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); |
|
ddorwin
2012/08/22 18:25:54
result is never used. Do we need it?
xhwang
2012/08/24 22:01:59
Even if we don't use the result, I think this is r
|
| + void KeyMessage(int32_t result, const cdm::KeyMessage& key_message); |
| + void KeyError(int32_t result, const std::string& session_id); |
|
ddorwin
2012/08/22 18:25:54
Should have the two error values as parameters too
xhwang
2012/08/24 22:01:59
For now we really don't need it. If we need it in
|
| + void DeliverBlock(int32_t result, |
| + const cdm::Status& status, |
| + const 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_) { |
| + CallOnMain(callback_factory_.NewCallback(&CdmWrapper::KeyError, |
| + std::string())); |
|
dmichael (off chromium)
2012/08/23 18:11:29
You don't have to CallOnMainThread for this, do yo
xhwang
2012/08/24 22:01:59
See reply below.
|
| + return true; |
| + } |
| + } |
| - 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, |
|
ddorwin
2012/08/22 18:25:54
We need to pass cdm::Status to the error. Eventual
dmichael (off chromium)
2012/08/23 18:11:29
CallOnMain isn't necessary here either (it's proba
xhwang
2012/08/24 22:01:59
As per offline discussion with dmichael@:
If runn
xhwang
2012/08/24 22:01:59
Agreed, added a TODO.
|
| + std::string())); |
|
Tom Finegan
2012/08/22 16:54:31
Seems kind of unfriendly to pass an empty string b
ddorwin
2012/08/22 18:25:54
it's the session ID, which doesn't exist yet.
|
| + return true; |
| + } |
| + |
| + 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)); |
| + PP_DCHECK(key.Map() && key.ByteLength() > 0); |
| + PP_DCHECK(init_data.Map() && init_data.ByteLength() > 0); |
|
dmichael (off chromium)
2012/08/23 18:11:29
Please don't do Map() inside of a DCHECK. It can h
|
| + PP_DCHECK(cdm_); |
| + |
| + cdm::Status status = cdm_->AddKey( |
| + session_id.data(), |
| + session_id.size(), |
|
Tom Finegan
2012/08/22 16:54:31
I still think we should get rid of the size param
ddorwin
2012/08/22 18:25:54
This is consistent with the other parameters, and
dmichael (off chromium)
2012/08/23 18:11:29
It also makes it easier to use stuff like memcpy o
|
| + reinterpret_cast<const uint8_t*>(key.Map()), |
| + key.ByteLength(), |
| + reinterpret_cast<const uint8_t*>(init_data.Map()), |
| + init_data.ByteLength()); |
| + |
| + 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()); |
|
dmichael (off chromium)
2012/08/23 18:11:29
Could maybe be one line?
|
| - 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, |
|
dmichael (off chromium)
2012/08/23 18:11:29
could be one line? (esp. since probably doesn't ne
|
| + session_id)); |
| + return false; |
| + } |
| - CallOnMain(callback_factory_.NewCallback(&CDMWrapper::KeyMessage, |
| - decryptor_message)); |
| + // TODO(xhwang): Need to do anything here? Spec says TBD. |
|
ddorwin
2012/08/22 18:25:54
I think this TBD belongs in the CDMs, not here. Th
xhwang
2012/08/24 22:01:59
Done.
|
| 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); |
|
Tom Finegan
2012/08/22 16:54:31
I think we should check this status here, and retu
ddorwin
2012/08/22 18:25:54
I've changed my mind (since our conversation) abou
xhwang
2012/08/24 22:01:59
The reason I add the comments in the IDL is to avo
xhwang
2012/08/24 22:01:59
As per the spec: 5.1. Encrypted Block Encountered:
|
| + |
| + 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; |
| +} |
| + |
|
Tom Finegan
2012/08/22 16:54:31
A comment here that this returns a resource with a
xhwang
2012/08/24 22:01:59
Done.
ddorwin
2012/08/27 17:33:54
Where is it?
xhwang
2012/08/27 18:07:26
http://codereview.chromium.org/10876014/diff/1006/
|
| +PP_Resource CdmWrapper::MakeBufferResource(const uint8_t* data, |
| + uint32_t data_size) { |
|
dmichael (off chromium)
2012/08/23 18:11:29
nit: looks like data_size could fit on the previou
xhwang
2012/08/24 22:01:59
Unfortunately it doesn't fit :(
|
| + if (!data || data_size == 0) |
| 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, |
| + const 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 safe as the memory is allocated in |
|
ddorwin
2012/08/22 18:25:54
"not _always_ safe"
It works for now...
xhwang
2012/08/24 22:01:59
Done.
|
| + // another object. |
|
Tom Finegan
2012/08/22 16:54:31
s/object/object that runs in a different thread/ ?
ddorwin
2012/08/22 18:25:54
It's really a different shared object. I'm sure th
xhwang
2012/08/24 22:01:59
Done.
|
| + delete [] key_message.session_id; |
| + delete [] key_message.message; |
| + delete [] key_message.default_url; |
|
dmichael (off chromium)
2012/08/23 18:11:29
Can you make cdm::KeyMessage smarter or something?
xhwang
2012/08/24 22:01:59
We have a plan which is not trivial. The above TOD
|
| } |
| -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); |
| +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, |
| + const cdm::OutputBuffer& output_buffer, |
|
ddorwin
2012/08/22 18:25:54
can't be const if we're deleting a member.
xhwang
2012/08/24 22:01:59
Done.
|
| + 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; |
| + |
| + if (status == cdm::kSuccess) |
|
ddorwin
2012/08/22 18:25:54
switch statement? esp. useful when we have more er
xhwang
2012/08/24 22:01:59
Done.
|
| + decrypted_block_info.result = PP_DECRYPTRESULT_SUCCESS; |
| + else if (status == cdm::kErrorNoKey) |
| + decrypted_block_info.result = PP_DECRYPTRESULT_DECRYPT_NOKEY; |
| + else // if (status == cdm::kErrorUnknown) |
| + decrypted_block_info.result = PP_DECRYPTRESULT_DECRYPT_ERROR; |
| + |
| + pp::ContentDecryptor_Private::DeliverBlock(decrypted_buffer, |
| + decrypted_block_info); |
| + |
| + // TODO(xhwang): Fix this. This is not safe as the memory is allocated in |
| + // another object. |
| + delete [] output_buffer.data; |
|
ddorwin
2012/08/22 18:25:54
How do we guarantee output_buffer is not being use
xhwang
2012/08/24 22:01:59
I am not sure what's the problem? The output_buffe
ddorwin
2012/08/27 17:33:54
That answers my question. Would be nice if we coul
|
| } |
| // This object is the global object representing this plugin library as long |
| @@ -244,15 +304,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 |