Chromium Code Reviews| Index: media/cdm/ppapi/cdm_wrapper.cc |
| diff --git a/media/cdm/ppapi/cdm_wrapper.cc b/media/cdm/ppapi/cdm_wrapper.cc |
| index 66ae43d4d65917ab4ac609a75ef5fc70f986db19..1edb67eb35845352cac41d50d0dd0c273ebfe528 100644 |
| --- a/media/cdm/ppapi/cdm_wrapper.cc |
| +++ b/media/cdm/ppapi/cdm_wrapper.cc |
| @@ -10,8 +10,10 @@ |
| #include "base/basictypes.h" |
| #include "base/compiler_specific.h" |
| +#include "build/build_config.h" |
| #include "media/cdm/ppapi/api/content_decryption_module.h" |
| #include "media/cdm/ppapi/linked_ptr.h" |
| +#include "ppapi/c/pp_completion_callback.h" |
| #include "ppapi/c/pp_errors.h" |
| #include "ppapi/c/pp_stdint.h" |
| #include "ppapi/c/private/pp_content_decryptor.h" |
| @@ -33,6 +35,11 @@ |
| #include "ppapi/cpp/instance_handle.h" |
| #endif // defined(CHECK_DOCUMENT_URL) |
| +#if defined(OS_CHROMEOS) |
| +#include "ppapi/cpp/private/output_protection_private.h" |
| +#include "ppapi/cpp/private/platform_verification.h" |
| +#endif |
| + |
| namespace { |
| bool IsMainThread() { |
| @@ -456,9 +463,10 @@ VideoFrameImpl::~VideoFrameImpl() { |
| frame_buffer_->Destroy(); |
| } |
| -class AudioFramesImpl : public cdm::AudioFrames { |
| +class AudioFramesImpl : public cdm::AudioFrames_1, |
| + public cdm::AudioFrames_2 { |
| public: |
| - AudioFramesImpl() : buffer_(NULL) {} |
| + AudioFramesImpl() : buffer_(NULL), format_(cdm::kAudioFormatS16) {} |
| virtual ~AudioFramesImpl() { |
| if (buffer_) |
| buffer_->Destroy(); |
| @@ -471,9 +479,16 @@ class AudioFramesImpl : public cdm::AudioFrames { |
| virtual cdm::Buffer* FrameBuffer() OVERRIDE { |
| return buffer_; |
| } |
| + virtual void SetFormat(cdm::AudioFormat format) OVERRIDE { |
| + format_ = format; |
| + } |
| + virtual cdm::AudioFormat Format() const OVERRIDE { |
| + return format_; |
| + } |
| private: |
| PpbBuffer* buffer_; |
| + cdm::AudioFormat format_; |
| DISALLOW_COPY_AND_ASSIGN(AudioFramesImpl); |
| }; |
| @@ -485,7 +500,8 @@ void* GetCdmHost(int host_interface_version, void* user_data); |
| // Content Decryption Module (CDM). |
| class CdmWrapper : public pp::Instance, |
| public pp::ContentDecryptor_Private, |
| - public cdm::Host { |
| + public cdm::Host_1, |
| + public cdm::Host_2 { |
| public: |
| CdmWrapper(PP_Instance instance, pp::Module* module); |
| virtual ~CdmWrapper(); |
| @@ -524,7 +540,7 @@ class CdmWrapper : public pp::Instance, |
| pp::Buffer_Dev encrypted_buffer, |
| const PP_EncryptedBlockInfo& encrypted_block_info) OVERRIDE; |
| - // cdm::Host implementation. |
| + // cdm::Host_1 implementation. |
| virtual cdm::Buffer* Allocate(int32_t capacity) OVERRIDE; |
| virtual void SetTimer(int64_t delay_ms, void* context) OVERRIDE; |
| virtual double GetCurrentWallTimeInSeconds() OVERRIDE; |
| @@ -539,6 +555,15 @@ class CdmWrapper : public pp::Instance, |
| virtual void GetPrivateData(int32_t* instance, |
| GetPrivateInterface* get_interface) OVERRIDE; |
| + // cdm::Host_2 implementation. |
| + virtual bool CanChallengePlatform() OVERRIDE; |
| + virtual void SendPlatformChallenge( |
| + const char* service_id, int32_t service_id_length, |
| + const char* challenge, int32_t challenge_length) OVERRIDE; |
| + virtual void EnableOutputProtection( |
| + uint32_t desired_protection_mask) OVERRIDE; |
| + virtual void QueryOutputProtectionStatus() OVERRIDE; |
| + |
| private: |
| struct SessionInfo { |
| SessionInfo(const std::string& key_system_in, |
| @@ -606,6 +631,35 @@ class CdmWrapper : public pp::Instance, |
| bool IsValidVideoFrame(const LinkedVideoFrame& video_frame); |
| +#if defined(OS_CHROMEOS) |
| + void CanChallengePlatformDone(int32_t result, bool can_challenge_platform); |
| + void SendPlatformChallengeDone(int32_t result); |
| + void EnableProtectionDone(int32_t result); |
| + void QueryOutputProtectionStatusDone(int32_t result); |
| + |
| + pp::OutputProtection_Private output_protection_; |
| + pp::PlatformVerification platform_verification_; |
| + |
| + // |can_challenge_platform_| is currently set asynchronously, return the value |
| + // if we have it, otherwise guess "true" for the most common case... |
| + // TODO(jrummell): This stinks. The value should be delivered via the |
| + // Initialize() method once plumbed. |
| + bool can_challenge_platform_; |
| + |
| + // Since PPAPI doesn't provide handlers for CompletionCallbacks w/ more than |
| + // one output we need to manage our own. These values are only read by |
| + // SendPlatformChallengeDone(). |
| + pp::Var signed_data_output_; |
| + pp::Var signed_data_signature_output_; |
| + pp::Var platform_key_certificate_output_; |
| + bool challenge_in_progress_; |
| + |
| + // Same as above, these are only read by QueryOutputProtectionStatusDone(). |
| + uint32_t link_mask_; |
| + uint32_t output_protection_mask_; |
| + bool query_in_progress_; |
| +#endif |
| + |
| PpbBufferAllocator allocator_; |
| pp::CompletionCallbackFactory<CdmWrapper> callback_factory_; |
| cdm::ContentDecryptionModule* cdm_; |
| @@ -617,9 +671,25 @@ class CdmWrapper : public pp::Instance, |
| CdmWrapper::CdmWrapper(PP_Instance instance, pp::Module* module) |
| : pp::Instance(instance), |
| pp::ContentDecryptor_Private(this), |
| +#if defined(OS_CHROMEOS) |
| + output_protection_(this), |
| + platform_verification_(this), |
| + // Err on the side of the most common case... |
| + can_challenge_platform_(true), |
| + challenge_in_progress_(false), |
| + link_mask_(0), |
|
ddorwin
2013/10/05 21:39:36
output_... ?
DaleCurtis
2013/10/07 22:15:59
Done.
|
| + output_protection_mask_(0), |
| + query_in_progress_(false), |
|
ddorwin
2013/10/05 21:39:36
nit: query is not very specific. query_output_prot
DaleCurtis
2013/10/07 22:15:59
Done.
|
| +#endif |
| allocator_(this), |
| cdm_(NULL) { |
| callback_factory_.Initialize(this); |
| +#if defined(OS_CHROMEOS) |
| + // Preemptively retrieve the platform challenge status. It will not change. |
| + platform_verification_.CanChallengePlatform( |
| + callback_factory_.NewCallbackWithOutput( |
| + &CdmWrapper::CanChallengePlatformDone)); |
| +#endif |
| } |
| CdmWrapper::~CdmWrapper() { |
| @@ -936,7 +1006,7 @@ void CdmWrapper::SendKeyError(const char* session_id, |
| } |
| void CdmWrapper::GetPrivateData(int32_t* instance, |
| - cdm::Host::GetPrivateInterface* get_interface) { |
| + GetPrivateInterface* get_interface) { |
| *instance = pp_instance(); |
| *get_interface = pp::Module::Get()->get_browser_interface(); |
| } |
| @@ -1155,15 +1225,147 @@ bool CdmWrapper::IsValidVideoFrame(const LinkedVideoFrame& video_frame) { |
| return true; |
| } |
| +bool CdmWrapper::CanChallengePlatform() { |
| +#if defined(OS_CHROMEOS) |
| + return can_challenge_platform_; |
| +#else |
| + return false; |
| +#endif |
| +} |
| + |
| +void CdmWrapper::SendPlatformChallenge( |
| + const char* service_id, int32_t service_id_length, |
| + const char* challenge, int32_t challenge_length) { |
| +#if defined(OS_CHROMEOS) |
| + PP_DCHECK(!challenge_in_progress_); |
| + |
| + // Ensure member variables set by the callback are in a clean state. |
|
ddorwin
2013/10/05 21:39:36
Do we need to do the same for output protection?
DaleCurtis
2013/10/07 22:15:59
No, not since the outputs are simple types. We ne
|
| + signed_data_output_ = pp::Var(); |
| + signed_data_signature_output_ = pp::Var(); |
| + platform_key_certificate_output_ = pp::Var(); |
| + |
| + pp::VarArrayBuffer challenge_var(challenge_length); |
| + uint8_t* var_data = static_cast<uint8_t*>(challenge_var.Map()); |
| + memcpy(var_data, challenge, challenge_length); |
| + |
| + std::string service_id_str(service_id, service_id_length); |
| + int32_t result = platform_verification_.ChallengePlatform( |
| + pp::Var(service_id_str), challenge_var, &signed_data_output_, |
| + &signed_data_signature_output_, &platform_key_certificate_output_, |
| + callback_factory_.NewCallback(&CdmWrapper::SendPlatformChallengeDone)); |
| + challenge_var.Unmap(); |
| + if (result == PP_OK_COMPLETIONPENDING) { |
| + challenge_in_progress_ = true; |
| + return; |
| + } |
| + |
| + // Fall through on error and issue an empty OnPlatformChallengeResponse(). |
| + PP_DCHECK(result != PP_OK); |
| +#endif |
| + |
| + cdm::PlatformChallengeResponse response = {}; |
| + cdm_->OnPlatformChallengeResponse(response); |
| +} |
| + |
| +void CdmWrapper::EnableOutputProtection(uint32_t desired_protection_mask) { |
| +#if defined(OS_CHROMEOS) |
| + output_protection_.EnableProtection( |
| + desired_protection_mask, callback_factory_.NewCallback( |
| + &CdmWrapper::EnableProtectionDone)); |
| + |
| + // Errors are ignored since clients must call QueryOutputProtectionStatus() to |
| + // inspect the protection status on a regular basis. |
| + // TODO(dalecurtis): It'd be nice to log a message or non-fatal error here... |
|
ddorwin
2013/10/05 21:39:36
Won't we always get PENDING since we're OOP?
DaleCurtis
2013/10/07 22:15:59
Not if there's an error that prevents the call fro
|
| +#endif |
| +} |
| + |
| +void CdmWrapper::QueryOutputProtectionStatus() { |
| +#if defined(OS_CHROMEOS) |
| + PP_DCHECK(!query_in_progress_); |
| + |
| + link_mask_ = output_protection_mask_ = 0; |
| + int32_t result = output_protection_.QueryStatus( |
| + &link_mask_, &output_protection_mask_, callback_factory_.NewCallback( |
| + &CdmWrapper::QueryOutputProtectionStatusDone)); |
| + if (result == PP_OK_COMPLETIONPENDING) { |
| + query_in_progress_ = true; |
| + return; |
| + } |
| + |
| + // Fall through on error and issue an empty OnQueryOutputProtectionStatus(). |
| + PP_DCHECK(result != PP_OK); |
| +#endif |
| + |
| + cdm_->OnQueryOutputProtectionStatus(0, 0); |
| +} |
| + |
| +#if defined(OS_CHROMEOS) |
| +void CdmWrapper::CanChallengePlatformDone(int32_t result, |
| + bool can_challenge_platform) { |
| + can_challenge_platform_ = (result == PP_OK) ? can_challenge_platform : false; |
| +} |
| + |
| +void CdmWrapper::SendPlatformChallengeDone(int32_t result) { |
| + challenge_in_progress_ = false; |
| + |
| + if (result != PP_OK) { |
| + cdm::PlatformChallengeResponse response = {}; |
| + cdm_->OnPlatformChallengeResponse(response); |
| + return; |
| + } |
| + |
| + pp::VarArrayBuffer signed_data_var(signed_data_output_); |
| + pp::VarArrayBuffer signed_data_signature_var(signed_data_signature_output_); |
| + std::string platform_key_certificate_string = |
| + platform_key_certificate_output_.AsString(); |
| + |
| + cdm::PlatformChallengeResponse response = { |
| + static_cast<uint8_t*>(signed_data_var.Map()), |
| + signed_data_var.ByteLength(), |
| + |
| + static_cast<uint8_t*>(signed_data_signature_var.Map()), |
| + signed_data_signature_var.ByteLength(), |
| + |
| + reinterpret_cast<const uint8_t*>(platform_key_certificate_string.c_str()), |
| + platform_key_certificate_string.length() |
| + }; |
| + cdm_->OnPlatformChallengeResponse(response); |
| + |
| + signed_data_var.Unmap(); |
| + signed_data_signature_var.Unmap(); |
| +} |
| + |
| +void CdmWrapper::EnableProtectionDone(int32_t result) { |
| + // Does nothing since clients must call QueryOutputProtectionStatus() to |
| + // inspect the protection status on a regular basis. |
| + // TODO(dalecurtis): It'd be nice to log a message or non-fatal error here... |
| +} |
| + |
| +void CdmWrapper::QueryOutputProtectionStatusDone(int32_t result) { |
| + query_in_progress_ = false; |
|
ddorwin
2013/10/05 21:39:36
Should we add DCHECK(query_in_progress_); ?
DaleCurtis
2013/10/07 22:15:59
Done.
|
| + |
| + // Return a protection status of none on error. |
| + if (result != PP_OK) |
| + link_mask_ = output_protection_mask_ = 0; |
| + |
| + cdm_->OnQueryOutputProtectionStatus(link_mask_, output_protection_mask_); |
| +} |
| +#endif |
| + |
| void* GetCdmHost(int host_interface_version, void* user_data) { |
| if (!host_interface_version || !user_data) |
| return NULL; |
| - if (host_interface_version != cdm::kHostInterfaceVersion) |
| - return NULL; |
| - |
| CdmWrapper* cdm_wrapper = static_cast<CdmWrapper*>(user_data); |
| - return static_cast<cdm::Host*>(cdm_wrapper); |
| + switch (host_interface_version) { |
| + case cdm::kHostInterfaceVersion_1: |
| + return static_cast<cdm::Host_1*>(cdm_wrapper); |
| + case cdm::kHostInterfaceVersion_2: |
| + return static_cast<cdm::Host_2*>(cdm_wrapper); |
| + default: |
| + PP_NOTREACHED(); |
| + return NULL; |
| + } |
| } |
| // This object is the global object representing this plugin library as long |