C++程序  |  384行  |  14.86 KB

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_CDM_PPAPI_CDM_WRAPPER_H_
#define MEDIA_CDM_PPAPI_CDM_WRAPPER_H_

#include <map>
#include <queue>
#include <string>

#include "base/basictypes.h"
#include "media/cdm/ppapi/api/content_decryption_module.h"
#include "media/cdm/ppapi/cdm_helpers.h"
#include "media/cdm/ppapi/supported_cdm_versions.h"
#include "ppapi/cpp/logging.h"

namespace media {

// CdmWrapper wraps different versions of ContentDecryptionModule interfaces and
// exposes a common interface to the caller.
//
// The caller should call CdmWrapper::Create() to create a CDM instance.
// CdmWrapper will first try to create a CDM instance that supports the latest
// CDM interface (ContentDecryptionModule). If such an instance cannot be
// created (e.g. an older CDM was loaded), CdmWrapper will try to create a CDM
// that supports an older version of CDM interface (e.g.
// ContentDecryptionModule_*). Internally CdmWrapper converts the CdmWrapper
// calls to corresponding ContentDecryptionModule calls.
//
// Note that CdmWrapper interface always reflects the latest state of content
// decryption related PPAPI APIs (e.g. pp::ContentDecryptor_Private).
//
// Since this file is highly templated and default implementations are short
// (just a shim layer in most cases), everything is done in this header file.
class CdmWrapper {
 public:
  static CdmWrapper* Create(const char* key_system,
                            uint32_t key_system_size,
                            GetCdmHostFunc get_cdm_host_func,
                            void* user_data);

  virtual ~CdmWrapper() {};

  virtual void CreateSession(uint32_t promise_id,
                             const char* init_data_type,
                             uint32_t init_data_type_size,
                             const uint8_t* init_data,
                             uint32_t init_data_size,
                             cdm::SessionType session_type) = 0;
  virtual void LoadSession(uint32_t promise_id,
                           const char* web_session_id,
                           uint32_t web_session_id_size) = 0;
  virtual void UpdateSession(uint32_t promise_id,
                             const char* web_session_id,
                             uint32_t web_session_id_size,
                             const uint8_t* response,
                             uint32_t response_size) = 0;
  virtual void ReleaseSession(uint32_t promise_id,
                              const char* web_session_id,
                              uint32_t web_session_id_size) = 0;
  virtual void TimerExpired(void* context) = 0;
  virtual cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer,
                              cdm::DecryptedBlock* decrypted_buffer) = 0;
  virtual cdm::Status InitializeAudioDecoder(
      const cdm::AudioDecoderConfig& audio_decoder_config) = 0;
  virtual cdm::Status InitializeVideoDecoder(
      const cdm::VideoDecoderConfig& video_decoder_config) = 0;
  virtual void DeinitializeDecoder(cdm::StreamType decoder_type) = 0;
  virtual void ResetDecoder(cdm::StreamType decoder_type) = 0;
  virtual cdm::Status DecryptAndDecodeFrame(
      const cdm::InputBuffer& encrypted_buffer,
      cdm::VideoFrame* video_frame) = 0;
  virtual cdm::Status DecryptAndDecodeSamples(
      const cdm::InputBuffer& encrypted_buffer,
      cdm::AudioFrames* audio_frames) = 0;
  virtual void OnPlatformChallengeResponse(
      const cdm::PlatformChallengeResponse& response) = 0;
  virtual void OnQueryOutputProtectionStatus(
      uint32_t link_mask,
      uint32_t output_protection_mask) = 0;

  // Helper function for the cdm::Host_4 methods. Calls to CreateSession(),
  // LoadSession(), UpdateSession(), and ReleaseSession() pass in promise ids,
  // but the CDM interface needs session ids. For create and load, we need to
  // create a new session_id to pass to the CDM. For update and release, we need
  // to look up |web_session_id| and convert it into the existing |session_id|.
  // Since the callbacks don't come through this interface, cdm_adapter needs to
  // create the mapping (and delete it on release).
  // TODO(jrummell): Remove these once Host_4 interface is removed.
  virtual uint32_t LookupPromiseId(uint32_t session_id) = 0;
  virtual void AssignWebSessionId(uint32_t session_id,
                                  const char* web_session_id,
                                  uint32_t web_session_id_size) = 0;
  virtual std::string LookupWebSessionId(uint32_t session_id) = 0;
  virtual void DropWebSessionId(std::string web_session_id) = 0;

 protected:
  CdmWrapper() {}

 private:
  DISALLOW_COPY_AND_ASSIGN(CdmWrapper);
};

// Template class that does the CdmWrapper -> CdmInterface conversion. Default
// implementations are provided. Any methods that need special treatment should
// be specialized.
template <class CdmInterface>
class CdmWrapperImpl : public CdmWrapper {
 public:
  static CdmWrapper* Create(const char* key_system,
                            uint32_t key_system_size,
                            GetCdmHostFunc get_cdm_host_func,
                            void* user_data) {
    void* cdm_instance = ::CreateCdmInstance(
        CdmInterface::kVersion, key_system, key_system_size, get_cdm_host_func,
        user_data);
    if (!cdm_instance)
      return NULL;

    return new CdmWrapperImpl<CdmInterface>(
        static_cast<CdmInterface*>(cdm_instance));
  }

  virtual ~CdmWrapperImpl() {
    cdm_->Destroy();
  }

  virtual void CreateSession(uint32_t promise_id,
                             const char* init_data_type,
                             uint32_t init_data_type_size,
                             const uint8_t* init_data,
                             uint32_t init_data_size,
                             cdm::SessionType session_type) OVERRIDE {
    cdm_->CreateSession(promise_id,
                        init_data_type,
                        init_data_type_size,
                        init_data,
                        init_data_size,
                        session_type);
  }

  virtual void LoadSession(uint32_t promise_id,
                           const char* web_session_id,
                           uint32_t web_session_id_size) OVERRIDE {
    cdm_->LoadSession(promise_id, web_session_id, web_session_id_size);
  }

  virtual void UpdateSession(uint32_t promise_id,
                             const char* web_session_id,
                             uint32_t web_session_id_size,
                             const uint8_t* response,
                             uint32_t response_size) OVERRIDE {
    cdm_->UpdateSession(promise_id,
                        web_session_id,
                        web_session_id_size,
                        response,
                        response_size);
  }

  virtual void ReleaseSession(uint32_t promise_id,
                              const char* web_session_id,
                              uint32_t web_session_id_size) OVERRIDE {
    cdm_->ReleaseSession(promise_id, web_session_id, web_session_id_size);
  }

  virtual void TimerExpired(void* context) OVERRIDE {
    cdm_->TimerExpired(context);
  }

  virtual cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer,
                              cdm::DecryptedBlock* decrypted_buffer) OVERRIDE {
    return cdm_->Decrypt(encrypted_buffer, decrypted_buffer);
  }

  virtual cdm::Status InitializeAudioDecoder(
      const cdm::AudioDecoderConfig& audio_decoder_config) OVERRIDE {
    return cdm_->InitializeAudioDecoder(audio_decoder_config);
  }

  virtual cdm::Status InitializeVideoDecoder(
      const cdm::VideoDecoderConfig& video_decoder_config) OVERRIDE {
    return cdm_->InitializeVideoDecoder(video_decoder_config);
  }

  virtual void DeinitializeDecoder(cdm::StreamType decoder_type) OVERRIDE {
    cdm_->DeinitializeDecoder(decoder_type);
  }

  virtual void ResetDecoder(cdm::StreamType decoder_type) OVERRIDE {
    cdm_->ResetDecoder(decoder_type);
  }

  virtual cdm::Status DecryptAndDecodeFrame(
      const cdm::InputBuffer& encrypted_buffer,
      cdm::VideoFrame* video_frame) OVERRIDE {
    return cdm_->DecryptAndDecodeFrame(encrypted_buffer, video_frame);
  }

  virtual cdm::Status DecryptAndDecodeSamples(
      const cdm::InputBuffer& encrypted_buffer,
      cdm::AudioFrames* audio_frames) OVERRIDE {
    return cdm_->DecryptAndDecodeSamples(encrypted_buffer, audio_frames);
  }

  virtual void OnPlatformChallengeResponse(
      const cdm::PlatformChallengeResponse& response) OVERRIDE {
    cdm_->OnPlatformChallengeResponse(response);
  }

  virtual void OnQueryOutputProtectionStatus(
      uint32_t link_mask,
      uint32_t output_protection_mask) OVERRIDE {
    cdm_->OnQueryOutputProtectionStatus(link_mask, output_protection_mask);
  }

  uint32_t CreateSessionId() {
    return next_session_id_++;
  }

  void RegisterPromise(uint32_t session_id, uint32_t promise_id) {
    PP_DCHECK(promise_to_session_id_map_.find(session_id) ==
              promise_to_session_id_map_.end());
    promise_to_session_id_map_.insert(std::make_pair(session_id, promise_id));
  }

  virtual uint32_t LookupPromiseId(uint32_t session_id) {
    std::map<uint32_t, uint32_t>::iterator it =
        promise_to_session_id_map_.find(session_id);
    if (it == promise_to_session_id_map_.end())
      return 0;
    uint32_t promise_id = it->second;
    promise_to_session_id_map_.erase(it);
    return promise_id;
  }

  virtual void AssignWebSessionId(uint32_t session_id,
                                  const char* web_session_id,
                                  uint32_t web_session_id_size) {
    web_session_to_session_id_map_.insert(std::make_pair(
        std::string(web_session_id, web_session_id_size), session_id));
  }

  uint32_t LookupSessionId(std::string web_session_id) {
    return web_session_to_session_id_map_.find(web_session_id)->second;
  }

  virtual std::string LookupWebSessionId(uint32_t session_id) {
    std::map<std::string, uint32_t>::iterator it;
    for (it = web_session_to_session_id_map_.begin();
         it != web_session_to_session_id_map_.end();
         ++it) {
      if (it->second == session_id)
        return it->first;
    }
    PP_NOTREACHED();
    return std::string();
  }

  virtual void DropWebSessionId(std::string web_session_id) {
    web_session_to_session_id_map_.erase(web_session_id);
  }

 private:
  CdmWrapperImpl(CdmInterface* cdm) : cdm_(cdm), next_session_id_(100) {
    PP_DCHECK(cdm_);
  }

  CdmInterface* cdm_;

  std::map<uint32_t, uint32_t> promise_to_session_id_map_;
  uint32_t next_session_id_;
  std::map<std::string, uint32_t> web_session_to_session_id_map_;

  DISALLOW_COPY_AND_ASSIGN(CdmWrapperImpl);
};

// Overrides for the cdm::Host_4 methods. Calls to CreateSession(),
// LoadSession(), UpdateSession(), and ReleaseSession() pass in promise ids,
// but the CDM interface needs session ids. For create and load, we need to
// create a new session_id to pass to the CDM. For update and release, we need
// to look up |web_session_id| and convert it into the existing |session_id|.
// Since the callbacks don't come through this interface, cdm_adapter needs to
// create the mapping (and delete it on release).
// TODO(jrummell): Remove these once Host_4 interface is removed.

template <>
void CdmWrapperImpl<cdm::ContentDecryptionModule_4>::CreateSession(
    uint32_t promise_id,
    const char* init_data_type,
    uint32_t init_data_type_size,
    const uint8_t* init_data,
    uint32_t init_data_size,
    cdm::SessionType session_type) {
  uint32_t session_id = CreateSessionId();
  RegisterPromise(session_id, promise_id);
  cdm_->CreateSession(session_id,
                      init_data_type,
                      init_data_type_size,
                      init_data,
                      init_data_size);
}

template <>
void CdmWrapperImpl<cdm::ContentDecryptionModule_4>::LoadSession(
    uint32_t promise_id,
    const char* web_session_id,
    uint32_t web_session_id_size) {
  uint32_t session_id = CreateSessionId();
  RegisterPromise(session_id, promise_id);
  cdm_->LoadSession(session_id, web_session_id, web_session_id_size);
}

template <>
void CdmWrapperImpl<cdm::ContentDecryptionModule_4>::UpdateSession(
    uint32_t promise_id,
    const char* web_session_id,
    uint32_t web_session_id_size,
    const uint8_t* response,
    uint32_t response_size) {
  std::string web_session_str(web_session_id, web_session_id_size);
  uint32_t session_id = LookupSessionId(web_session_str);
  RegisterPromise(session_id, promise_id);
  cdm_->UpdateSession(session_id, response, response_size);
}

template <>
void CdmWrapperImpl<cdm::ContentDecryptionModule_4>::ReleaseSession(
    uint32_t promise_id,
    const char* web_session_id,
    uint32_t web_session_id_size) {
  std::string web_session_str(web_session_id, web_session_id_size);
  uint32_t session_id = LookupSessionId(web_session_str);
  RegisterPromise(session_id, promise_id);
  cdm_->ReleaseSession(session_id);
}

CdmWrapper* CdmWrapper::Create(const char* key_system,
                               uint32_t key_system_size,
                               GetCdmHostFunc get_cdm_host_func,
                               void* user_data) {
  COMPILE_ASSERT(cdm::ContentDecryptionModule::kVersion ==
                     cdm::ContentDecryptionModule_5::kVersion,
                 update_code_below);

  // Ensure IsSupportedCdmInterfaceVersion() matches this implementation.
  // Always update this DCHECK when updating this function.
  // If this check fails, update this function and DCHECK or update
  // IsSupportedCdmInterfaceVersion().
  PP_DCHECK(
      !IsSupportedCdmInterfaceVersion(cdm::ContentDecryptionModule::kVersion +
                                      1) &&
      IsSupportedCdmInterfaceVersion(cdm::ContentDecryptionModule::kVersion) &&
      IsSupportedCdmInterfaceVersion(
          cdm::ContentDecryptionModule_4::kVersion) &&
      !IsSupportedCdmInterfaceVersion(cdm::ContentDecryptionModule_4::kVersion -
                                      1));

  // Try to create the CDM using the latest CDM interface version.
  CdmWrapper* cdm_wrapper =
      CdmWrapperImpl<cdm::ContentDecryptionModule>::Create(
          key_system, key_system_size, get_cdm_host_func, user_data);
  if (cdm_wrapper)
    return cdm_wrapper;

  // If |cdm_wrapper| is NULL, try to create the CDM using older supported
  // versions of the CDM interface.
  cdm_wrapper = CdmWrapperImpl<cdm::ContentDecryptionModule_4>::Create(
      key_system, key_system_size, get_cdm_host_func, user_data);
  return cdm_wrapper;
}

// When updating the CdmAdapter, ensure you've updated the CdmWrapper to contain
// stub implementations for new or modified methods that the older CDM interface
// does not have.
// Also update supported_cdm_versions.h.
COMPILE_ASSERT(cdm::ContentDecryptionModule::kVersion ==
                   cdm::ContentDecryptionModule_5::kVersion,
               ensure_cdm_wrapper_templates_have_old_version_support);

}  // namespace media

#endif  // MEDIA_CDM_PPAPI_CDM_WRAPPER_H_