Home | History | Annotate | Download | only in ppapi
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef MEDIA_CDM_PPAPI_CDM_WRAPPER_H_
      6 #define MEDIA_CDM_PPAPI_CDM_WRAPPER_H_
      7 
      8 #include <map>
      9 #include <queue>
     10 #include <string>
     11 
     12 #include "base/basictypes.h"
     13 #include "media/cdm/ppapi/api/content_decryption_module.h"
     14 #include "media/cdm/ppapi/cdm_helpers.h"
     15 #include "media/cdm/ppapi/supported_cdm_versions.h"
     16 #include "ppapi/cpp/logging.h"
     17 
     18 namespace media {
     19 
     20 // CdmWrapper wraps different versions of ContentDecryptionModule interfaces and
     21 // exposes a common interface to the caller.
     22 //
     23 // The caller should call CdmWrapper::Create() to create a CDM instance.
     24 // CdmWrapper will first try to create a CDM instance that supports the latest
     25 // CDM interface (ContentDecryptionModule). If such an instance cannot be
     26 // created (e.g. an older CDM was loaded), CdmWrapper will try to create a CDM
     27 // that supports an older version of CDM interface (e.g.
     28 // ContentDecryptionModule_*). Internally CdmWrapper converts the CdmWrapper
     29 // calls to corresponding ContentDecryptionModule calls.
     30 //
     31 // Note that CdmWrapper interface always reflects the latest state of content
     32 // decryption related PPAPI APIs (e.g. pp::ContentDecryptor_Private).
     33 //
     34 // Since this file is highly templated and default implementations are short
     35 // (just a shim layer in most cases), everything is done in this header file.
     36 class CdmWrapper {
     37  public:
     38   // CDM_1 and CDM_2 methods AddKey() and CancelKeyRequest() may require
     39   // callbacks to fire. Use this enum to indicate the additional calls required.
     40   // TODO(jrummell): Remove return value once CDM_1 and CDM_2 are no longer
     41   // supported.
     42   enum Result {
     43     NO_ACTION,
     44     CALL_KEY_ADDED,
     45     CALL_KEY_ERROR
     46   };
     47 
     48   static CdmWrapper* Create(const char* key_system,
     49                             uint32_t key_system_size,
     50                             GetCdmHostFunc get_cdm_host_func,
     51                             void* user_data);
     52 
     53   virtual ~CdmWrapper() {};
     54 
     55   virtual void CreateSession(uint32_t session_id,
     56                              const char* type,
     57                              uint32_t type_size,
     58                              const uint8_t* init_data,
     59                              uint32_t init_data_size) = 0;
     60   virtual Result UpdateSession(uint32_t session_id,
     61                                const uint8_t* response,
     62                                uint32_t response_size) = 0;
     63   virtual Result ReleaseSession(uint32_t session_id) = 0;
     64   virtual void TimerExpired(void* context) = 0;
     65   virtual cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer,
     66                               cdm::DecryptedBlock* decrypted_buffer) = 0;
     67   virtual cdm::Status InitializeAudioDecoder(
     68       const cdm::AudioDecoderConfig& audio_decoder_config) = 0;
     69   virtual cdm::Status InitializeVideoDecoder(
     70       const cdm::VideoDecoderConfig& video_decoder_config) = 0;
     71   virtual void DeinitializeDecoder(cdm::StreamType decoder_type) = 0;
     72   virtual void ResetDecoder(cdm::StreamType decoder_type) = 0;
     73   virtual cdm::Status DecryptAndDecodeFrame(
     74       const cdm::InputBuffer& encrypted_buffer,
     75       cdm::VideoFrame* video_frame) = 0;
     76   virtual cdm::Status DecryptAndDecodeSamples(
     77       const cdm::InputBuffer& encrypted_buffer,
     78       cdm::AudioFrames* audio_frames) = 0;
     79   virtual void OnPlatformChallengeResponse(
     80       const cdm::PlatformChallengeResponse& response) = 0;
     81   virtual void OnQueryOutputProtectionStatus(
     82       uint32_t link_mask,
     83       uint32_t output_protection_mask) = 0;
     84 
     85   // ContentDecryptionModule_1 and ContentDecryptionModule_2 interface methods
     86   // AddKey() and CancelKeyRequest() (older versions of UpdateSession() and
     87   // ReleaseSession(), respectively) pass in the web_session_id rather than the
     88   // session_id. As well, Host_1 and Host_2 callbacks SendKeyMessage() and
     89   // SendKeyError() include the web_session_id, but the actual callbacks need
     90   // session_id.
     91   //
     92   // The following functions maintain the session_id <-> web_session_id mapping.
     93   // These can be removed once _1 and _2 interfaces are no longer supported.
     94 
     95   // Determine the corresponding session_id for |web_session_id|.
     96   virtual uint32_t LookupSessionId(const std::string& web_session_id) = 0;
     97 
     98   // Determine the corresponding session_id for |session_id|.
     99   virtual const std::string LookupWebSessionId(uint32_t session_id) = 0;
    100 
    101   // Map between session_id and web_session_id.
    102   // TODO(jrummell): The following can be removed once CDM_1 and CDM_2 are
    103   // no longer supported.
    104   typedef std::map<uint32_t, std::string> SessionMap;
    105   SessionMap session_map_;
    106 
    107   static const uint32_t kInvalidSessionId = 0;
    108 
    109   // As the response from PrefixedGenerateKeyRequest() may be synchronous or
    110   // asynchronous, keep track of the current request during the call to handle
    111   // synchronous responses or errors. If no response received, add this request
    112   // to a queue and assume that the subsequent responses come back in the order
    113   // issued.
    114   // TODO(jrummell): Remove once all supported CDM host interfaces support
    115   // session_id.
    116   uint32_t current_key_request_session_id_;
    117   std::queue<uint32_t> pending_key_request_session_ids_;
    118 
    119  protected:
    120   CdmWrapper() : current_key_request_session_id_(kInvalidSessionId) {}
    121 
    122  private:
    123   DISALLOW_COPY_AND_ASSIGN(CdmWrapper);
    124 };
    125 
    126 // Template class that does the CdmWrapper -> CdmInterface conversion. Default
    127 // implementations are provided. Any methods that need special treatment should
    128 // be specialized.
    129 template <class CdmInterface>
    130 class CdmWrapperImpl : public CdmWrapper {
    131  public:
    132   static CdmWrapper* Create(const char* key_system,
    133                             uint32_t key_system_size,
    134                             GetCdmHostFunc get_cdm_host_func,
    135                             void* user_data) {
    136     void* cdm_instance = ::CreateCdmInstance(
    137         CdmInterface::kVersion, key_system, key_system_size, get_cdm_host_func,
    138         user_data);
    139     if (!cdm_instance)
    140       return NULL;
    141 
    142     return new CdmWrapperImpl<CdmInterface>(
    143         static_cast<CdmInterface*>(cdm_instance));
    144   }
    145 
    146   virtual ~CdmWrapperImpl() {
    147     cdm_->Destroy();
    148   }
    149 
    150   virtual void CreateSession(uint32_t session_id,
    151                              const char* type,
    152                              uint32_t type_size,
    153                              const uint8_t* init_data,
    154                              uint32_t init_data_size) OVERRIDE {
    155     cdm_->CreateSession(session_id, type, type_size, init_data, init_data_size);
    156   }
    157 
    158   virtual Result UpdateSession(uint32_t session_id,
    159                                const uint8_t* response,
    160                                uint32_t response_size) OVERRIDE {
    161     cdm_->UpdateSession(session_id, response, response_size);
    162     return NO_ACTION;
    163   }
    164 
    165   virtual Result ReleaseSession(uint32_t session_id) OVERRIDE {
    166     cdm_->ReleaseSession(session_id);
    167     return NO_ACTION;
    168   }
    169 
    170   virtual void TimerExpired(void* context) OVERRIDE {
    171     cdm_->TimerExpired(context);
    172   }
    173 
    174   virtual cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer,
    175                               cdm::DecryptedBlock* decrypted_buffer) OVERRIDE {
    176     return cdm_->Decrypt(encrypted_buffer, decrypted_buffer);
    177   }
    178 
    179   virtual cdm::Status InitializeAudioDecoder(
    180       const cdm::AudioDecoderConfig& audio_decoder_config) OVERRIDE {
    181     return cdm_->InitializeAudioDecoder(audio_decoder_config);
    182   }
    183 
    184   virtual cdm::Status InitializeVideoDecoder(
    185       const cdm::VideoDecoderConfig& video_decoder_config) OVERRIDE {
    186     return cdm_->InitializeVideoDecoder(video_decoder_config);
    187   }
    188 
    189   virtual void DeinitializeDecoder(cdm::StreamType decoder_type) OVERRIDE {
    190     cdm_->DeinitializeDecoder(decoder_type);
    191   }
    192 
    193   virtual void ResetDecoder(cdm::StreamType decoder_type) OVERRIDE {
    194     cdm_->ResetDecoder(decoder_type);
    195   }
    196 
    197   virtual cdm::Status DecryptAndDecodeFrame(
    198       const cdm::InputBuffer& encrypted_buffer,
    199       cdm::VideoFrame* video_frame) OVERRIDE {
    200     return cdm_->DecryptAndDecodeFrame(encrypted_buffer, video_frame);
    201   }
    202 
    203   virtual cdm::Status DecryptAndDecodeSamples(
    204       const cdm::InputBuffer& encrypted_buffer,
    205       cdm::AudioFrames* audio_frames) OVERRIDE {
    206     return cdm_->DecryptAndDecodeSamples(encrypted_buffer, audio_frames);
    207   }
    208 
    209   virtual void OnPlatformChallengeResponse(
    210       const cdm::PlatformChallengeResponse& response) OVERRIDE {
    211     cdm_->OnPlatformChallengeResponse(response);
    212   }
    213 
    214   virtual void OnQueryOutputProtectionStatus(
    215       uint32_t link_mask,
    216       uint32_t output_protection_mask) OVERRIDE {
    217     cdm_->OnQueryOutputProtectionStatus(link_mask, output_protection_mask);
    218   }
    219 
    220   uint32_t LookupSessionId(const std::string& web_session_id) {
    221     for (SessionMap::iterator it = session_map_.begin();
    222          it != session_map_.end();
    223          ++it) {
    224       if (it->second == web_session_id)
    225         return it->first;
    226     }
    227 
    228     // There is no entry in the map; assume it came from the current
    229     // PrefixedGenerateKeyRequest() call (if possible). If no current request,
    230     // assume it came from the oldest PrefixedGenerateKeyRequest() call.
    231     uint32_t session_id = current_key_request_session_id_;
    232     if (current_key_request_session_id_) {
    233       // Only 1 response is allowed for the current
    234       // PrefixedGenerateKeyRequest().
    235       current_key_request_session_id_ = kInvalidSessionId;
    236     } else {
    237       PP_DCHECK(!pending_key_request_session_ids_.empty());
    238       session_id = pending_key_request_session_ids_.front();
    239       pending_key_request_session_ids_.pop();
    240     }
    241 
    242     // If this is a valid |session_id|, add it to the list. Otherwise, avoid
    243     // adding empty string as a mapping to prevent future calls with an empty
    244     // string from using the wrong session_id.
    245     if (!web_session_id.empty()) {
    246       PP_DCHECK(session_map_.find(session_id) == session_map_.end());
    247       session_map_[session_id] = web_session_id;
    248     }
    249 
    250     return session_id;
    251   }
    252 
    253   const std::string LookupWebSessionId(uint32_t session_id) {
    254     // Session may not exist if error happens during CreateSession().
    255     SessionMap::iterator it = session_map_.find(session_id);
    256     return (it != session_map_.end()) ? it->second : std::string();
    257   }
    258 
    259  private:
    260   CdmWrapperImpl(CdmInterface* cdm) : cdm_(cdm) {
    261     PP_DCHECK(cdm_);
    262   }
    263 
    264   CdmInterface* cdm_;
    265 
    266   DISALLOW_COPY_AND_ASSIGN(CdmWrapperImpl);
    267 };
    268 
    269 // For ContentDecryptionModule_1 and ContentDecryptionModule_2,
    270 // CreateSession(), UpdateSession(), and ReleaseSession() call methods
    271 // are incompatible with ContentDecryptionModule_3. Use the following
    272 // templated functions to handle this.
    273 
    274 template <class CdmInterface>
    275 void PrefixedGenerateKeyRequest(CdmWrapper* wrapper,
    276                                 CdmInterface* cdm,
    277                                 uint32_t session_id,
    278                                 const char* type,
    279                                 uint32_t type_size,
    280                                 const uint8_t* init_data,
    281                                 uint32_t init_data_size) {
    282   // As it is possible for CDMs to reply synchronously during the call to
    283   // GenerateKeyRequest(), keep track of |session_id|.
    284   wrapper->current_key_request_session_id_ = session_id;
    285 
    286   cdm::Status status =
    287       cdm->GenerateKeyRequest(type, type_size, init_data, init_data_size);
    288   PP_DCHECK(status == cdm::kSuccess || status == cdm::kSessionError);
    289   if (status != cdm::kSuccess) {
    290     // If GenerateKeyRequest() failed, no subsequent asynchronous replies
    291     // will be sent. Verify that a response was sent synchronously.
    292     PP_DCHECK(wrapper->current_key_request_session_id_ ==
    293               CdmWrapper::kInvalidSessionId);
    294     wrapper->current_key_request_session_id_ = CdmWrapper::kInvalidSessionId;
    295     return;
    296   }
    297 
    298   if (wrapper->current_key_request_session_id_) {
    299     // If this request is still pending (SendKeyMessage() or SendKeyError()
    300     // not called synchronously), add |session_id| to the end of the queue.
    301     // Without CDM support, it is impossible to match SendKeyMessage()
    302     // (or SendKeyError()) responses to the |session_id|. Doing the best
    303     // we can by keeping track of this in a queue, and assuming the responses
    304     // come back in order.
    305     wrapper->pending_key_request_session_ids_.push(session_id);
    306     wrapper->current_key_request_session_id_ = CdmWrapper::kInvalidSessionId;
    307   }
    308 }
    309 
    310 template <class CdmInterface>
    311 CdmWrapper::Result PrefixedAddKey(CdmWrapper* wrapper,
    312                                   CdmInterface* cdm,
    313                                   uint32_t session_id,
    314                                   const uint8_t* response,
    315                                   uint32_t response_size) {
    316   const std::string web_session_id = wrapper->LookupWebSessionId(session_id);
    317   if (web_session_id.empty()) {
    318     // Possible if UpdateSession() called before CreateSession().
    319     return CdmWrapper::CALL_KEY_ERROR;
    320   }
    321 
    322   // CDM_1 and CDM_2 accept initdata, which is no longer needed.
    323   // In it's place pass in NULL.
    324   cdm::Status status = cdm->AddKey(web_session_id.data(), web_session_id.size(),
    325                                    response, response_size,
    326                                    NULL, 0);
    327   PP_DCHECK(status == cdm::kSuccess || status == cdm::kSessionError);
    328   if (status != cdm::kSuccess) {
    329     // Some CDMs using Host_1/2 don't call keyerror, so send one.
    330     return CdmWrapper::CALL_KEY_ERROR;
    331   }
    332 
    333   return CdmWrapper::CALL_KEY_ADDED;
    334 }
    335 
    336 template <class CdmInterface>
    337 CdmWrapper::Result PrefixedCancelKeyRequest(CdmWrapper* wrapper,
    338                                             CdmInterface* cdm,
    339                                             uint32_t session_id) {
    340   const std::string web_session_id = wrapper->LookupWebSessionId(session_id);
    341   if (web_session_id.empty()) {
    342     // Possible if ReleaseSession() called before CreateSession().
    343     return CdmWrapper::CALL_KEY_ERROR;
    344   }
    345 
    346   wrapper->session_map_.erase(session_id);
    347   cdm::Status status =
    348       cdm->CancelKeyRequest(web_session_id.data(), web_session_id.size());
    349 
    350   PP_DCHECK(status == cdm::kSuccess || status == cdm::kSessionError);
    351   if (status != cdm::kSuccess) {
    352     // Some CDMs using Host_1/2 don't call keyerror, so send one.
    353     return CdmWrapper::CALL_KEY_ERROR;
    354   }
    355 
    356   return CdmWrapper::NO_ACTION;
    357 }
    358 
    359 // Specializations for ContentDecryptionModule_1.
    360 
    361 template <>
    362 void CdmWrapperImpl<cdm::ContentDecryptionModule_1>::CreateSession(
    363     uint32_t session_id,
    364     const char* type,
    365     uint32_t type_size,
    366     const uint8_t* init_data,
    367     uint32_t init_data_size) {
    368   PrefixedGenerateKeyRequest(
    369       this, cdm_, session_id, type, type_size, init_data, init_data_size);
    370 }
    371 
    372 template <>
    373 CdmWrapper::Result CdmWrapperImpl<
    374     cdm::ContentDecryptionModule_1>::UpdateSession(uint32_t session_id,
    375                                                    const uint8_t* response,
    376                                                    uint32_t response_size) {
    377   return PrefixedAddKey(this, cdm_, session_id, response, response_size);
    378 }
    379 
    380 template <>
    381 CdmWrapper::Result CdmWrapperImpl<
    382     cdm::ContentDecryptionModule_1>::ReleaseSession(uint32_t session_id) {
    383   return PrefixedCancelKeyRequest(this, cdm_, session_id);
    384 }
    385 
    386 template <> void CdmWrapperImpl<cdm::ContentDecryptionModule_1>::
    387     OnPlatformChallengeResponse(
    388         const cdm::PlatformChallengeResponse& response) {
    389   PP_NOTREACHED();
    390 }
    391 
    392 template <> void CdmWrapperImpl<cdm::ContentDecryptionModule_1>::
    393     OnQueryOutputProtectionStatus(uint32_t link_mask,
    394                                   uint32_t output_protection_mask) {
    395   PP_NOTREACHED();
    396 }
    397 
    398 template <> cdm::Status CdmWrapperImpl<cdm::ContentDecryptionModule_1>::
    399     DecryptAndDecodeSamples(const cdm::InputBuffer& encrypted_buffer,
    400                             cdm::AudioFrames* audio_frames) {
    401   AudioFramesImpl audio_frames_1;
    402   cdm::Status status =
    403       cdm_->DecryptAndDecodeSamples(encrypted_buffer, &audio_frames_1);
    404   if (status != cdm::kSuccess)
    405     return status;
    406 
    407   audio_frames->SetFrameBuffer(audio_frames_1.PassFrameBuffer());
    408   audio_frames->SetFormat(cdm::kAudioFormatS16);
    409   return cdm::kSuccess;
    410 }
    411 
    412 // Specializations for ContentDecryptionModule_2.
    413 
    414 template <>
    415 void CdmWrapperImpl<cdm::ContentDecryptionModule_2>::CreateSession(
    416     uint32_t session_id,
    417     const char* type,
    418     uint32_t type_size,
    419     const uint8_t* init_data,
    420     uint32_t init_data_size) {
    421   PrefixedGenerateKeyRequest(
    422       this, cdm_, session_id, type, type_size, init_data, init_data_size);
    423 }
    424 
    425 template <>
    426 CdmWrapper::Result CdmWrapperImpl<
    427     cdm::ContentDecryptionModule_2>::UpdateSession(uint32_t session_id,
    428                                                    const uint8_t* response,
    429                                                    uint32_t response_size) {
    430   return PrefixedAddKey(this, cdm_, session_id, response, response_size);
    431 }
    432 
    433 template <>
    434 CdmWrapper::Result CdmWrapperImpl<
    435     cdm::ContentDecryptionModule_2>::ReleaseSession(uint32_t session_id) {
    436   return PrefixedCancelKeyRequest(this, cdm_, session_id);
    437 }
    438 
    439 CdmWrapper* CdmWrapper::Create(const char* key_system,
    440                                uint32_t key_system_size,
    441                                GetCdmHostFunc get_cdm_host_func,
    442                                void* user_data) {
    443   COMPILE_ASSERT(cdm::ContentDecryptionModule::kVersion ==
    444                  cdm::ContentDecryptionModule_3::kVersion,
    445                  update_code_below);
    446 
    447   // Ensure IsSupportedCdmInterfaceVersion matches this implementation.
    448   // Always update this DCHECK when updating this function.
    449   // If this check fails, update this function and DCHECK or update
    450   // IsSupportedCdmInterfaceVersion.
    451   PP_DCHECK(
    452       !IsSupportedCdmInterfaceVersion(
    453           cdm::ContentDecryptionModule::kVersion + 1) &&
    454       IsSupportedCdmInterfaceVersion(cdm::ContentDecryptionModule::kVersion) &&
    455       IsSupportedCdmInterfaceVersion(
    456           cdm::ContentDecryptionModule_2::kVersion) &&
    457       IsSupportedCdmInterfaceVersion(
    458           cdm::ContentDecryptionModule_1::kVersion) &&
    459       !IsSupportedCdmInterfaceVersion(
    460           cdm::ContentDecryptionModule_1::kVersion - 1));
    461 
    462   // Try to create the CDM using the latest CDM interface version.
    463   CdmWrapper* cdm_wrapper =
    464       CdmWrapperImpl<cdm::ContentDecryptionModule>::Create(
    465           key_system, key_system_size, get_cdm_host_func, user_data);
    466   if (cdm_wrapper)
    467     return cdm_wrapper;
    468 
    469   // Try to see if the CDM supports older version(s) of the CDM interface.
    470   cdm_wrapper = CdmWrapperImpl<cdm::ContentDecryptionModule_2>::Create(
    471       key_system, key_system_size, get_cdm_host_func, user_data);
    472   if (cdm_wrapper)
    473     return cdm_wrapper;
    474 
    475   cdm_wrapper = CdmWrapperImpl<cdm::ContentDecryptionModule_1>::Create(
    476       key_system, key_system_size, get_cdm_host_func, user_data);
    477   return cdm_wrapper;
    478 }
    479 
    480 // When updating the CdmAdapter, ensure you've updated the CdmWrapper to contain
    481 // stub implementations for new or modified methods that the older CDM interface
    482 // does not have.
    483 // Also update supported_cdm_versions.h.
    484 COMPILE_ASSERT(cdm::ContentDecryptionModule::kVersion ==
    485                    cdm::ContentDecryptionModule_3::kVersion,
    486                ensure_cdm_wrapper_templates_have_old_version_support);
    487 
    488 }  // namespace media
    489 
    490 #endif  // MEDIA_CDM_PPAPI_CDM_WRAPPER_H_
    491