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   static CdmWrapper* Create(const char* key_system,
     39                             uint32_t key_system_size,
     40                             GetCdmHostFunc get_cdm_host_func,
     41                             void* user_data);
     42 
     43   virtual ~CdmWrapper() {};
     44 
     45   // TODO(jrummell): Remove return value when CDM4/5 are removed.
     46   virtual bool SetServerCertificate(uint32_t promise_id,
     47                                     const uint8_t* server_certificate_data,
     48                                     uint32_t server_certificate_data_size) = 0;
     49   virtual void CreateSession(uint32_t promise_id,
     50                              const char* init_data_type,
     51                              uint32_t init_data_type_size,
     52                              const uint8_t* init_data,
     53                              uint32_t init_data_size,
     54                              cdm::SessionType session_type) = 0;
     55   virtual void LoadSession(uint32_t promise_id,
     56                            const char* web_session_id,
     57                            uint32_t web_session_id_size) = 0;
     58   virtual void UpdateSession(uint32_t promise_id,
     59                              const char* web_session_id,
     60                              uint32_t web_session_id_size,
     61                              const uint8_t* response,
     62                              uint32_t response_size) = 0;
     63   // TODO(jrummell): Remove return value when CDM4/5 are removed.
     64   virtual bool CloseSession(uint32_t promise_id,
     65                             const char* web_session_id,
     66                             uint32_t web_session_id_size) = 0;
     67   virtual void RemoveSession(uint32_t promise_id,
     68                              const char* web_session_id,
     69                              uint32_t web_session_id_size) = 0;
     70   // TODO(jrummell): Remove return value when CDM4/5 are removed.
     71   virtual bool GetUsableKeyIds(uint32_t promise_id,
     72                                const char* web_session_id,
     73                                uint32_t web_session_id_size) = 0;
     74   virtual void TimerExpired(void* context) = 0;
     75   virtual cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer,
     76                               cdm::DecryptedBlock* decrypted_buffer) = 0;
     77   virtual cdm::Status InitializeAudioDecoder(
     78       const cdm::AudioDecoderConfig& audio_decoder_config) = 0;
     79   virtual cdm::Status InitializeVideoDecoder(
     80       const cdm::VideoDecoderConfig& video_decoder_config) = 0;
     81   virtual void DeinitializeDecoder(cdm::StreamType decoder_type) = 0;
     82   virtual void ResetDecoder(cdm::StreamType decoder_type) = 0;
     83   virtual cdm::Status DecryptAndDecodeFrame(
     84       const cdm::InputBuffer& encrypted_buffer,
     85       cdm::VideoFrame* video_frame) = 0;
     86   virtual cdm::Status DecryptAndDecodeSamples(
     87       const cdm::InputBuffer& encrypted_buffer,
     88       cdm::AudioFrames* audio_frames) = 0;
     89   virtual void OnPlatformChallengeResponse(
     90       const cdm::PlatformChallengeResponse& response) = 0;
     91   virtual void OnQueryOutputProtectionStatus(
     92       uint32_t link_mask,
     93       uint32_t output_protection_mask) = 0;
     94 
     95   // Helper function for the cdm::Host_4 methods. Calls to CreateSession(),
     96   // LoadSession(), UpdateSession(), and ReleaseSession() pass in promise ids,
     97   // but the CDM interface needs session ids. For create and load, we need to
     98   // create a new session_id to pass to the CDM. For update and release, we need
     99   // to look up |web_session_id| and convert it into the existing |session_id|.
    100   // Since the callbacks don't come through this interface, cdm_adapter needs to
    101   // create the mapping (and delete it on release).
    102   // TODO(jrummell): Remove these once Host_4 interface is removed.
    103   virtual uint32_t LookupPromiseId(uint32_t session_id) = 0;
    104   virtual void AssignWebSessionId(uint32_t session_id,
    105                                   const char* web_session_id,
    106                                   uint32_t web_session_id_size) = 0;
    107   virtual std::string LookupWebSessionId(uint32_t session_id) = 0;
    108   virtual void DropWebSessionId(std::string web_session_id) = 0;
    109 
    110   // Helper functions for the cdm::Host_4 methods.
    111   // CDMs using cdm::Host_6 will call OnSessionUsableKeys() as necessary when
    112   // resolving LoadSession() and UpdateSession(). This needs to be simulated
    113   // for the older CDMs. These must not be called for cdm::Host_6 and later.
    114   // TODO(jrummell): Remove these once Host_4 interface is removed.
    115 
    116   // Query whether a SessionUsableKeys event is necessary for the specified
    117   // |promise_id|. Returns true if needed and |web_session_id| is updated,
    118   // otherwise returns false.
    119   virtual bool SessionUsableKeysEventNeeded(uint32_t promise_id,
    120                                             std::string* web_session_id) = 0;
    121 
    122   // Used to indicate that a SessionUsableKeys event is required for the
    123   // specified |promise_id| and associated |web_session_id|.
    124   virtual void SetSessionUsableKeysEventNeeded(
    125       uint32_t promise_id,
    126       const char* web_session_id,
    127       uint32_t web_session_id_size) = 0;
    128 
    129   // cdm::Host_6 introduces InputBuffer_2 (aka InputBuffer). cdm::Host_4
    130   // methods still use InputBuffer_1, so this helper function
    131   // converts InputBuffer_2 to InputBuffer_1.
    132   // TODO(jrummell): Remove these once Host_4 interfaces is removed.
    133   virtual void ConvertInputBuffer(const cdm::InputBuffer& v2,
    134                                   cdm::InputBuffer_1* v1) = 0;
    135 
    136   // Prior to CDM_6, |init_data_type| was a content type. This helper convererts
    137   // an |init_data_type| to a content type.
    138   // TODO(sandersd): Remove once Host_4 interface is removed.
    139   virtual std::string ConvertInitDataTypeToContentType(
    140       const std::string& init_data_type) const = 0;
    141 
    142  protected:
    143   CdmWrapper() {}
    144 
    145  private:
    146   DISALLOW_COPY_AND_ASSIGN(CdmWrapper);
    147 };
    148 
    149 // Template class that does the CdmWrapper -> CdmInterface conversion. Default
    150 // implementations are provided. Any methods that need special treatment should
    151 // be specialized.
    152 template <class CdmInterface>
    153 class CdmWrapperImpl : public CdmWrapper {
    154  public:
    155   static CdmWrapper* Create(const char* key_system,
    156                             uint32_t key_system_size,
    157                             GetCdmHostFunc get_cdm_host_func,
    158                             void* user_data) {
    159     void* cdm_instance = ::CreateCdmInstance(
    160         CdmInterface::kVersion, key_system, key_system_size, get_cdm_host_func,
    161         user_data);
    162     if (!cdm_instance)
    163       return NULL;
    164 
    165     return new CdmWrapperImpl<CdmInterface>(
    166         static_cast<CdmInterface*>(cdm_instance));
    167   }
    168 
    169   virtual ~CdmWrapperImpl() {
    170     cdm_->Destroy();
    171   }
    172 
    173   // Returns true if |data| is prefixed with |header| and has data after the
    174   // |header|.
    175   bool HasHeader(const uint8* data,
    176                  int data_length,
    177                  const std::string& header) {
    178     return static_cast<size_t>(data_length) > header.length() &&
    179            std::equal(data, data + header.length(), header.begin());
    180   }
    181 
    182   virtual bool SetServerCertificate(
    183       uint32_t promise_id,
    184       const uint8_t* server_certificate_data,
    185       uint32_t server_certificate_data_size) OVERRIDE {
    186     cdm_->SetServerCertificate(
    187         promise_id, server_certificate_data, server_certificate_data_size);
    188     return true;
    189   }
    190 
    191   virtual void CreateSession(uint32_t promise_id,
    192                              const char* init_data_type,
    193                              uint32_t init_data_type_size,
    194                              const uint8_t* init_data,
    195                              uint32_t init_data_size,
    196                              cdm::SessionType session_type) OVERRIDE {
    197     // TODO(jrummell): Remove this code once |session_type| is passed through
    198     // Pepper. When removing, add the header back in for CDM4.
    199     PP_DCHECK(session_type == cdm::kTemporary);
    200     const char kPersistentSessionHeader[] = "PERSISTENT|";
    201     if (HasHeader(init_data, init_data_size, kPersistentSessionHeader)) {
    202       cdm_->CreateSession(promise_id,
    203                           init_data_type,
    204                           init_data_type_size,
    205                           init_data + strlen(kPersistentSessionHeader),
    206                           init_data_size - strlen(kPersistentSessionHeader),
    207                           cdm::kPersistent);
    208       return;
    209     }
    210 
    211     cdm_->CreateSession(promise_id,
    212                         init_data_type,
    213                         init_data_type_size,
    214                         init_data,
    215                         init_data_size,
    216                         session_type);
    217   }
    218 
    219   virtual void LoadSession(uint32_t promise_id,
    220                            const char* web_session_id,
    221                            uint32_t web_session_id_size) OVERRIDE {
    222     cdm_->LoadSession(promise_id, web_session_id, web_session_id_size);
    223   }
    224 
    225   virtual void UpdateSession(uint32_t promise_id,
    226                              const char* web_session_id,
    227                              uint32_t web_session_id_size,
    228                              const uint8_t* response,
    229                              uint32_t response_size) OVERRIDE {
    230     cdm_->UpdateSession(promise_id,
    231                         web_session_id,
    232                         web_session_id_size,
    233                         response,
    234                         response_size);
    235   }
    236 
    237   virtual bool CloseSession(uint32_t promise_id,
    238                             const char* web_session_id,
    239                             uint32_t web_session_id_size) OVERRIDE {
    240     cdm_->CloseSession(promise_id, web_session_id, web_session_id_size);
    241     return true;
    242   }
    243 
    244   virtual void RemoveSession(uint32_t promise_id,
    245                              const char* web_session_id,
    246                              uint32_t web_session_id_size) OVERRIDE {
    247     cdm_->RemoveSession(promise_id, web_session_id, web_session_id_size);
    248   }
    249 
    250   virtual bool GetUsableKeyIds(uint32_t promise_id,
    251                                const char* web_session_id,
    252                                uint32_t web_session_id_size) OVERRIDE {
    253     cdm_->GetUsableKeyIds(promise_id, web_session_id, web_session_id_size);
    254     return true;
    255   }
    256 
    257   virtual void TimerExpired(void* context) OVERRIDE {
    258     cdm_->TimerExpired(context);
    259   }
    260 
    261   virtual cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer,
    262                               cdm::DecryptedBlock* decrypted_buffer) OVERRIDE {
    263     return cdm_->Decrypt(encrypted_buffer, decrypted_buffer);
    264   }
    265 
    266   virtual cdm::Status InitializeAudioDecoder(
    267       const cdm::AudioDecoderConfig& audio_decoder_config) OVERRIDE {
    268     return cdm_->InitializeAudioDecoder(audio_decoder_config);
    269   }
    270 
    271   virtual cdm::Status InitializeVideoDecoder(
    272       const cdm::VideoDecoderConfig& video_decoder_config) OVERRIDE {
    273     return cdm_->InitializeVideoDecoder(video_decoder_config);
    274   }
    275 
    276   virtual void DeinitializeDecoder(cdm::StreamType decoder_type) OVERRIDE {
    277     cdm_->DeinitializeDecoder(decoder_type);
    278   }
    279 
    280   virtual void ResetDecoder(cdm::StreamType decoder_type) OVERRIDE {
    281     cdm_->ResetDecoder(decoder_type);
    282   }
    283 
    284   virtual cdm::Status DecryptAndDecodeFrame(
    285       const cdm::InputBuffer& encrypted_buffer,
    286       cdm::VideoFrame* video_frame) OVERRIDE {
    287     return cdm_->DecryptAndDecodeFrame(encrypted_buffer, video_frame);
    288   }
    289 
    290   virtual cdm::Status DecryptAndDecodeSamples(
    291       const cdm::InputBuffer& encrypted_buffer,
    292       cdm::AudioFrames* audio_frames) OVERRIDE {
    293     return cdm_->DecryptAndDecodeSamples(encrypted_buffer, audio_frames);
    294   }
    295 
    296   virtual void OnPlatformChallengeResponse(
    297       const cdm::PlatformChallengeResponse& response) OVERRIDE {
    298     cdm_->OnPlatformChallengeResponse(response);
    299   }
    300 
    301   virtual void OnQueryOutputProtectionStatus(
    302       uint32_t link_mask,
    303       uint32_t output_protection_mask) OVERRIDE {
    304     cdm_->OnQueryOutputProtectionStatus(link_mask, output_protection_mask);
    305   }
    306 
    307   uint32_t CreateSessionId() {
    308     return next_session_id_++;
    309   }
    310 
    311   void RegisterPromise(uint32_t session_id, uint32_t promise_id) {
    312     PP_DCHECK(promise_to_session_id_map_.find(session_id) ==
    313               promise_to_session_id_map_.end());
    314     promise_to_session_id_map_.insert(std::make_pair(session_id, promise_id));
    315   }
    316 
    317   virtual uint32_t LookupPromiseId(uint32_t session_id) {
    318     std::map<uint32_t, uint32_t>::iterator it =
    319         promise_to_session_id_map_.find(session_id);
    320     if (it == promise_to_session_id_map_.end())
    321       return 0;
    322     uint32_t promise_id = it->second;
    323     promise_to_session_id_map_.erase(it);
    324     return promise_id;
    325   }
    326 
    327   virtual void AssignWebSessionId(uint32_t session_id,
    328                                   const char* web_session_id,
    329                                   uint32_t web_session_id_size) {
    330     web_session_to_session_id_map_.insert(std::make_pair(
    331         std::string(web_session_id, web_session_id_size), session_id));
    332   }
    333 
    334   uint32_t LookupSessionId(std::string web_session_id) {
    335     return web_session_to_session_id_map_.find(web_session_id)->second;
    336   }
    337 
    338   virtual std::string LookupWebSessionId(uint32_t session_id) {
    339     std::map<std::string, uint32_t>::iterator it;
    340     for (it = web_session_to_session_id_map_.begin();
    341          it != web_session_to_session_id_map_.end();
    342          ++it) {
    343       if (it->second == session_id)
    344         return it->first;
    345     }
    346     PP_NOTREACHED();
    347     return std::string();
    348   }
    349 
    350   virtual void DropWebSessionId(std::string web_session_id) {
    351     web_session_to_session_id_map_.erase(web_session_id);
    352   }
    353 
    354   virtual bool SessionUsableKeysEventNeeded(uint32_t promise_id,
    355                                             std::string* web_session_id) {
    356     std::map<uint32_t, std::string>::iterator it =
    357         promises_needing_usable_keys_event_.find(promise_id);
    358     if (it == promises_needing_usable_keys_event_.end())
    359       return false;
    360     web_session_id->swap(it->second);
    361     promises_needing_usable_keys_event_.erase(it);
    362     return true;
    363   }
    364 
    365   virtual void SetSessionUsableKeysEventNeeded(uint32_t promise_id,
    366                                                const char* web_session_id,
    367                                                uint32_t web_session_id_size) {
    368     promises_needing_usable_keys_event_.insert(std::make_pair(
    369         promise_id, std::string(web_session_id, web_session_id_size)));
    370   }
    371 
    372   virtual void ConvertInputBuffer(const cdm::InputBuffer& v2,
    373                                   cdm::InputBuffer_1* v1) {
    374     v1->data = v2.data;
    375     v1->data_size = v2.data_size;
    376     v1->data_offset = 0;
    377     v1->key_id = v2.key_id;
    378     v1->key_id_size = v2.key_id_size;
    379     v1->iv = v2.iv;
    380     v1->iv_size = v2.iv_size;
    381     v1->subsamples = v2.subsamples;
    382     v1->num_subsamples = v2.num_subsamples;
    383     v1->timestamp = v2.timestamp;
    384   }
    385 
    386   virtual std::string ConvertInitDataTypeToContentType(
    387       const std::string& init_data_type) const {
    388     if (init_data_type == "cenc")
    389       return "video/mp4";
    390     if (init_data_type == "webm")
    391       return "video/webm";
    392     return init_data_type;
    393   }
    394 
    395  private:
    396   CdmWrapperImpl(CdmInterface* cdm) : cdm_(cdm), next_session_id_(100) {
    397     PP_DCHECK(cdm_);
    398   }
    399 
    400   CdmInterface* cdm_;
    401 
    402   std::map<uint32_t, uint32_t> promise_to_session_id_map_;
    403   uint32_t next_session_id_;
    404   std::map<std::string, uint32_t> web_session_to_session_id_map_;
    405 
    406   std::map<uint32_t, std::string> promises_needing_usable_keys_event_;
    407 
    408   DISALLOW_COPY_AND_ASSIGN(CdmWrapperImpl);
    409 };
    410 
    411 // Overrides for the cdm::Host_4 methods. Calls to CreateSession(),
    412 // LoadSession(), UpdateSession(), and ReleaseSession() pass in promise ids,
    413 // but the CDM interface needs session ids. For create and load, we need to
    414 // create a new session_id to pass to the CDM. For update and release, we need
    415 // to look up |web_session_id| and convert it into the existing |session_id|.
    416 // Since the callbacks don't come through this interface, cdm_adapter needs to
    417 // create the mapping (and delete it on release). Finally, for create, we need
    418 // to translate |init_data_type| to a MIME type.
    419 // TODO(jrummell): Remove these once Host_4 interface is removed.
    420 
    421 template <>
    422 bool CdmWrapperImpl<cdm::ContentDecryptionModule_4>::SetServerCertificate(
    423     uint32_t promise_id,
    424     const uint8_t* server_certificate_data,
    425     uint32_t server_certificate_data_size) {
    426   return false;
    427 }
    428 
    429 template <>
    430 void CdmWrapperImpl<cdm::ContentDecryptionModule_4>::CreateSession(
    431     uint32_t promise_id,
    432     const char* init_data_type,
    433     uint32_t init_data_type_size,
    434     const uint8_t* init_data,
    435     uint32_t init_data_size,
    436     cdm::SessionType session_type) {
    437   uint32_t session_id = CreateSessionId();
    438   RegisterPromise(session_id, promise_id);
    439   std::string converted_init_data_type = ConvertInitDataTypeToContentType(
    440       std::string(init_data_type, init_data_type_size));
    441   cdm_->CreateSession(session_id,
    442                       converted_init_data_type.data(),
    443                       converted_init_data_type.length(),
    444                       init_data,
    445                       init_data_size);
    446 }
    447 
    448 template <>
    449 void CdmWrapperImpl<cdm::ContentDecryptionModule_4>::LoadSession(
    450     uint32_t promise_id,
    451     const char* web_session_id,
    452     uint32_t web_session_id_size) {
    453   uint32_t session_id = CreateSessionId();
    454   RegisterPromise(session_id, promise_id);
    455   // As CDM_4 doesn't support OnSessionUsableKeysChange(), make sure to generate
    456   // one when the promise is resolved. This may be overly aggressive.
    457   SetSessionUsableKeysEventNeeded(
    458       promise_id, web_session_id, web_session_id_size);
    459   cdm_->LoadSession(session_id, web_session_id, web_session_id_size);
    460 }
    461 
    462 template <>
    463 void CdmWrapperImpl<cdm::ContentDecryptionModule_4>::UpdateSession(
    464     uint32_t promise_id,
    465     const char* web_session_id,
    466     uint32_t web_session_id_size,
    467     const uint8_t* response,
    468     uint32_t response_size) {
    469   std::string web_session_str(web_session_id, web_session_id_size);
    470   uint32_t session_id = LookupSessionId(web_session_str);
    471   RegisterPromise(session_id, promise_id);
    472   // As CDM_4 doesn't support OnSessionUsableKeysChange(), make sure to generate
    473   // one when the promise is resolved. This may be overly aggressive.
    474   SetSessionUsableKeysEventNeeded(
    475       promise_id, web_session_id, web_session_id_size);
    476   cdm_->UpdateSession(session_id, response, response_size);
    477 }
    478 
    479 template <>
    480 bool CdmWrapperImpl<cdm::ContentDecryptionModule_4>::CloseSession(
    481     uint32_t promise_id,
    482     const char* web_session_id,
    483     uint32_t web_session_id_size) {
    484   return false;
    485 }
    486 
    487 template <>
    488 void CdmWrapperImpl<cdm::ContentDecryptionModule_4>::RemoveSession(
    489     uint32_t promise_id,
    490     const char* web_session_id,
    491     uint32_t web_session_id_size) {
    492   std::string web_session_str(web_session_id, web_session_id_size);
    493   uint32_t session_id = LookupSessionId(web_session_str);
    494   RegisterPromise(session_id, promise_id);
    495   cdm_->ReleaseSession(session_id);
    496 }
    497 
    498 template <>
    499 bool CdmWrapperImpl<cdm::ContentDecryptionModule_4>::GetUsableKeyIds(
    500     uint32_t promise_id,
    501     const char* web_session_id,
    502     uint32_t web_session_id_size) {
    503   return false;
    504 }
    505 
    506 template <>
    507 cdm::Status CdmWrapperImpl<cdm::ContentDecryptionModule_4>::Decrypt(
    508     const cdm::InputBuffer& encrypted_buffer,
    509     cdm::DecryptedBlock* decrypted_buffer) {
    510   cdm::InputBuffer_1 buffer;
    511   ConvertInputBuffer(encrypted_buffer, &buffer);
    512   return cdm_->Decrypt(buffer, decrypted_buffer);
    513 }
    514 
    515 template <>
    516 cdm::Status
    517 CdmWrapperImpl<cdm::ContentDecryptionModule_4>::DecryptAndDecodeFrame(
    518     const cdm::InputBuffer& encrypted_buffer,
    519     cdm::VideoFrame* video_frame) {
    520   cdm::InputBuffer_1 buffer;
    521   ConvertInputBuffer(encrypted_buffer, &buffer);
    522   return cdm_->DecryptAndDecodeFrame(buffer, video_frame);
    523 }
    524 
    525 template <>
    526 cdm::Status
    527 CdmWrapperImpl<cdm::ContentDecryptionModule_4>::DecryptAndDecodeSamples(
    528     const cdm::InputBuffer& encrypted_buffer,
    529     cdm::AudioFrames* audio_frames) {
    530   cdm::InputBuffer_1 buffer;
    531   ConvertInputBuffer(encrypted_buffer, &buffer);
    532   return cdm_->DecryptAndDecodeSamples(buffer, audio_frames);
    533 }
    534 
    535 CdmWrapper* CdmWrapper::Create(const char* key_system,
    536                                uint32_t key_system_size,
    537                                GetCdmHostFunc get_cdm_host_func,
    538                                void* user_data) {
    539   COMPILE_ASSERT(cdm::ContentDecryptionModule::kVersion ==
    540                      cdm::ContentDecryptionModule_6::kVersion,
    541                  update_code_below);
    542 
    543   // Ensure IsSupportedCdmInterfaceVersion() matches this implementation.
    544   // Always update this DCHECK when updating this function.
    545   // If this check fails, update this function and DCHECK or update
    546   // IsSupportedCdmInterfaceVersion().
    547   PP_DCHECK(
    548       !IsSupportedCdmInterfaceVersion(cdm::ContentDecryptionModule::kVersion +
    549                                       1) &&
    550       IsSupportedCdmInterfaceVersion(cdm::ContentDecryptionModule::kVersion) &&
    551       IsSupportedCdmInterfaceVersion(
    552           cdm::ContentDecryptionModule_4::kVersion) &&
    553       !IsSupportedCdmInterfaceVersion(cdm::ContentDecryptionModule_4::kVersion -
    554                                       1));
    555 
    556   // Try to create the CDM using the latest CDM interface version.
    557   CdmWrapper* cdm_wrapper =
    558       CdmWrapperImpl<cdm::ContentDecryptionModule>::Create(
    559           key_system, key_system_size, get_cdm_host_func, user_data);
    560   if (cdm_wrapper)
    561     return cdm_wrapper;
    562 
    563   // If |cdm_wrapper| is NULL, try to create the CDM using older supported
    564   // versions of the CDM interface.
    565   cdm_wrapper = CdmWrapperImpl<cdm::ContentDecryptionModule_4>::Create(
    566       key_system, key_system_size, get_cdm_host_func, user_data);
    567   return cdm_wrapper;
    568 }
    569 
    570 // When updating the CdmAdapter, ensure you've updated the CdmWrapper to contain
    571 // stub implementations for new or modified methods that the older CDM interface
    572 // does not have.
    573 // Also update supported_cdm_versions.h.
    574 COMPILE_ASSERT(cdm::ContentDecryptionModule::kVersion ==
    575                    cdm::ContentDecryptionModule_6::kVersion,
    576                ensure_cdm_wrapper_templates_have_old_version_support);
    577 
    578 }  // namespace media
    579 
    580 #endif  // MEDIA_CDM_PPAPI_CDM_WRAPPER_H_
    581