root/content/renderer/media/crypto/ppapi_decryptor.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. Create
  2. weak_ptr_factory_
  3. CreateSession
  4. LoadSession
  5. UpdateSession
  6. ReleaseSession
  7. GetDecryptor
  8. RegisterNewKeyCB
  9. CancelDecrypt
  10. InitializeAudioDecoder
  11. InitializeVideoDecoder
  12. DecryptAndDecodeAudio
  13. DecryptAndDecodeVideo
  14. ResetDecoder
  15. DeinitializeDecoder
  16. ReportFailureToCallPlugin
  17. OnDecoderInitialized
  18. OnSessionCreated
  19. OnSessionMessage
  20. OnSessionReady
  21. OnSessionClosed
  22. OnSessionError
  23. OnFatalPluginError
  24. CdmDelegate

// 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.

#include "content/renderer/media/crypto/ppapi_decryptor.h"

#include <string>

#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "content/renderer/media/crypto/key_systems.h"
#include "content/renderer/pepper/content_decryptor_delegate.h"
#include "content/renderer/pepper/pepper_plugin_instance_impl.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/data_buffer.h"
#include "media/base/decoder_buffer.h"
#include "media/base/video_decoder_config.h"
#include "media/base/video_frame.h"

namespace content {

scoped_ptr<PpapiDecryptor> PpapiDecryptor::Create(
    const std::string& key_system,
    const CreatePepperCdmCB& create_pepper_cdm_cb,
    const media::SessionCreatedCB& session_created_cb,
    const media::SessionMessageCB& session_message_cb,
    const media::SessionReadyCB& session_ready_cb,
    const media::SessionClosedCB& session_closed_cb,
    const media::SessionErrorCB& session_error_cb) {
  std::string plugin_type = GetPepperType(key_system);
  DCHECK(!plugin_type.empty());
  scoped_ptr<PepperCdmWrapper> pepper_cdm_wrapper =
      create_pepper_cdm_cb.Run(plugin_type);
  if (!pepper_cdm_wrapper) {
    DLOG(ERROR) << "Plugin instance creation failed.";
    return scoped_ptr<PpapiDecryptor>();
  }

  return scoped_ptr<PpapiDecryptor>(
      new PpapiDecryptor(key_system,
                         pepper_cdm_wrapper.Pass(),
                         session_created_cb,
                         session_message_cb,
                         session_ready_cb,
                         session_closed_cb,
                         session_error_cb));
}

PpapiDecryptor::PpapiDecryptor(
    const std::string& key_system,
    scoped_ptr<PepperCdmWrapper> pepper_cdm_wrapper,
    const media::SessionCreatedCB& session_created_cb,
    const media::SessionMessageCB& session_message_cb,
    const media::SessionReadyCB& session_ready_cb,
    const media::SessionClosedCB& session_closed_cb,
    const media::SessionErrorCB& session_error_cb)
    : pepper_cdm_wrapper_(pepper_cdm_wrapper.Pass()),
      session_created_cb_(session_created_cb),
      session_message_cb_(session_message_cb),
      session_ready_cb_(session_ready_cb),
      session_closed_cb_(session_closed_cb),
      session_error_cb_(session_error_cb),
      render_loop_proxy_(base::MessageLoopProxy::current()),
      weak_ptr_factory_(this) {
  DCHECK(pepper_cdm_wrapper_.get());
  DCHECK(!session_created_cb_.is_null());
  DCHECK(!session_message_cb_.is_null());
  DCHECK(!session_ready_cb_.is_null());
  DCHECK(!session_closed_cb_.is_null());
  DCHECK(!session_error_cb_.is_null());

  base::WeakPtr<PpapiDecryptor> weak_this = weak_ptr_factory_.GetWeakPtr();
  CdmDelegate()->Initialize(
      key_system,
      base::Bind(&PpapiDecryptor::OnSessionCreated, weak_this),
      base::Bind(&PpapiDecryptor::OnSessionMessage, weak_this),
      base::Bind(&PpapiDecryptor::OnSessionReady, weak_this),
      base::Bind(&PpapiDecryptor::OnSessionClosed, weak_this),
      base::Bind(&PpapiDecryptor::OnSessionError, weak_this),
      base::Bind(&PpapiDecryptor::OnFatalPluginError, weak_this));
}

PpapiDecryptor::~PpapiDecryptor() {
  pepper_cdm_wrapper_.reset();
}

bool PpapiDecryptor::CreateSession(uint32 session_id,
                                   const std::string& content_type,
                                   const uint8* init_data,
                                   int init_data_length) {
  DVLOG(2) << __FUNCTION__;
  DCHECK(render_loop_proxy_->BelongsToCurrentThread());

  if (!CdmDelegate() ||
      !CdmDelegate()->CreateSession(
          session_id, content_type, init_data, init_data_length)) {
    ReportFailureToCallPlugin(session_id);
    return false;
  }

  return true;
}

void PpapiDecryptor::LoadSession(uint32 session_id,
                                 const std::string& web_session_id) {
  DVLOG(2) << __FUNCTION__;
  DCHECK(render_loop_proxy_->BelongsToCurrentThread());

  if (!CdmDelegate()) {
    ReportFailureToCallPlugin(session_id);
    return;
  }

  CdmDelegate()->LoadSession(session_id, web_session_id);
}

void PpapiDecryptor::UpdateSession(uint32 session_id,
                                   const uint8* response,
                                   int response_length) {
  DVLOG(2) << __FUNCTION__;
  DCHECK(render_loop_proxy_->BelongsToCurrentThread());

  if (!CdmDelegate() ||
      !CdmDelegate()->UpdateSession(session_id, response, response_length)) {
    ReportFailureToCallPlugin(session_id);
    return;
  }
}

void PpapiDecryptor::ReleaseSession(uint32 session_id) {
  DVLOG(2) << __FUNCTION__;
  DCHECK(render_loop_proxy_->BelongsToCurrentThread());

  if (!CdmDelegate() || !CdmDelegate()->ReleaseSession(session_id)) {
    ReportFailureToCallPlugin(session_id);
    return;
  }
}

media::Decryptor* PpapiDecryptor::GetDecryptor() {
  return this;
}

void PpapiDecryptor::RegisterNewKeyCB(StreamType stream_type,
                                      const NewKeyCB& new_key_cb) {
  if (!render_loop_proxy_->BelongsToCurrentThread()) {
    render_loop_proxy_->PostTask(FROM_HERE,
                                 base::Bind(&PpapiDecryptor::RegisterNewKeyCB,
                                            weak_ptr_factory_.GetWeakPtr(),
                                            stream_type,
                                            new_key_cb));
    return;
  }

  DVLOG(3) << __FUNCTION__ << " - stream_type: " << stream_type;
  switch (stream_type) {
    case kAudio:
      new_audio_key_cb_ = new_key_cb;
      break;
    case kVideo:
      new_video_key_cb_ = new_key_cb;
      break;
    default:
      NOTREACHED();
  }
}

void PpapiDecryptor::Decrypt(
    StreamType stream_type,
    const scoped_refptr<media::DecoderBuffer>& encrypted,
    const DecryptCB& decrypt_cb) {
  if (!render_loop_proxy_->BelongsToCurrentThread()) {
    render_loop_proxy_->PostTask(FROM_HERE,
                                 base::Bind(&PpapiDecryptor::Decrypt,
                                            weak_ptr_factory_.GetWeakPtr(),
                                            stream_type,
                                            encrypted,
                                            decrypt_cb));
    return;
  }

  DVLOG(3) << __FUNCTION__ << " - stream_type: " << stream_type;
  if (!CdmDelegate() ||
      !CdmDelegate()->Decrypt(stream_type, encrypted, decrypt_cb)) {
    decrypt_cb.Run(kError, NULL);
  }
}

void PpapiDecryptor::CancelDecrypt(StreamType stream_type) {
  if (!render_loop_proxy_->BelongsToCurrentThread()) {
    render_loop_proxy_->PostTask(FROM_HERE,
                                 base::Bind(&PpapiDecryptor::CancelDecrypt,
                                            weak_ptr_factory_.GetWeakPtr(),
                                            stream_type));
    return;
  }

  DVLOG(1) << __FUNCTION__ << " - stream_type: " << stream_type;
  if (CdmDelegate())
    CdmDelegate()->CancelDecrypt(stream_type);
}

void PpapiDecryptor::InitializeAudioDecoder(
      const media::AudioDecoderConfig& config,
      const DecoderInitCB& init_cb) {
  if (!render_loop_proxy_->BelongsToCurrentThread()) {
    render_loop_proxy_->PostTask(
        FROM_HERE,
        base::Bind(&PpapiDecryptor::InitializeAudioDecoder,
                   weak_ptr_factory_.GetWeakPtr(),
                   config,
                   init_cb));
    return;
  }

  DVLOG(2) << __FUNCTION__;
  DCHECK(config.is_encrypted());
  DCHECK(config.IsValidConfig());

  audio_decoder_init_cb_ = init_cb;
  if (!CdmDelegate() || !CdmDelegate()->InitializeAudioDecoder(
                            config,
                            base::Bind(&PpapiDecryptor::OnDecoderInitialized,
                                       weak_ptr_factory_.GetWeakPtr(),
                                       kAudio))) {
    base::ResetAndReturn(&audio_decoder_init_cb_).Run(false);
    return;
  }
}

void PpapiDecryptor::InitializeVideoDecoder(
    const media::VideoDecoderConfig& config,
    const DecoderInitCB& init_cb) {
  if (!render_loop_proxy_->BelongsToCurrentThread()) {
    render_loop_proxy_->PostTask(
        FROM_HERE,
        base::Bind(&PpapiDecryptor::InitializeVideoDecoder,
                   weak_ptr_factory_.GetWeakPtr(),
                   config,
                   init_cb));
    return;
  }

  DVLOG(2) << __FUNCTION__;
  DCHECK(config.is_encrypted());
  DCHECK(config.IsValidConfig());

  video_decoder_init_cb_ = init_cb;
  if (!CdmDelegate() || !CdmDelegate()->InitializeVideoDecoder(
                            config,
                            base::Bind(&PpapiDecryptor::OnDecoderInitialized,
                                       weak_ptr_factory_.GetWeakPtr(),
                                       kVideo))) {
    base::ResetAndReturn(&video_decoder_init_cb_).Run(false);
    return;
  }
}

void PpapiDecryptor::DecryptAndDecodeAudio(
    const scoped_refptr<media::DecoderBuffer>& encrypted,
    const AudioDecodeCB& audio_decode_cb) {
  if (!render_loop_proxy_->BelongsToCurrentThread()) {
    render_loop_proxy_->PostTask(
        FROM_HERE,
        base::Bind(&PpapiDecryptor::DecryptAndDecodeAudio,
                   weak_ptr_factory_.GetWeakPtr(),
                   encrypted,
                   audio_decode_cb));
    return;
  }

  DVLOG(3) << __FUNCTION__;
  if (!CdmDelegate() ||
      !CdmDelegate()->DecryptAndDecodeAudio(encrypted, audio_decode_cb)) {
    audio_decode_cb.Run(kError, AudioBuffers());
  }
}

void PpapiDecryptor::DecryptAndDecodeVideo(
    const scoped_refptr<media::DecoderBuffer>& encrypted,
    const VideoDecodeCB& video_decode_cb) {
  if (!render_loop_proxy_->BelongsToCurrentThread()) {
    render_loop_proxy_->PostTask(
        FROM_HERE,
        base::Bind(&PpapiDecryptor::DecryptAndDecodeVideo,
                   weak_ptr_factory_.GetWeakPtr(),
                   encrypted,
                   video_decode_cb));
    return;
  }

  DVLOG(3) << __FUNCTION__;
  if (!CdmDelegate() ||
      !CdmDelegate()->DecryptAndDecodeVideo(encrypted, video_decode_cb)) {
    video_decode_cb.Run(kError, NULL);
  }
}

void PpapiDecryptor::ResetDecoder(StreamType stream_type) {
  if (!render_loop_proxy_->BelongsToCurrentThread()) {
    render_loop_proxy_->PostTask(FROM_HERE,
                                 base::Bind(&PpapiDecryptor::ResetDecoder,
                                            weak_ptr_factory_.GetWeakPtr(),
                                            stream_type));
    return;
  }

  DVLOG(2) << __FUNCTION__ << " - stream_type: " << stream_type;
  if (CdmDelegate())
    CdmDelegate()->ResetDecoder(stream_type);
}

void PpapiDecryptor::DeinitializeDecoder(StreamType stream_type) {
  if (!render_loop_proxy_->BelongsToCurrentThread()) {
    render_loop_proxy_->PostTask(
        FROM_HERE,
        base::Bind(&PpapiDecryptor::DeinitializeDecoder,
                   weak_ptr_factory_.GetWeakPtr(),
                   stream_type));
    return;
  }

  DVLOG(2) << __FUNCTION__ << " - stream_type: " << stream_type;
  if (CdmDelegate())
    CdmDelegate()->DeinitializeDecoder(stream_type);
}

void PpapiDecryptor::ReportFailureToCallPlugin(uint32 session_id) {
  DCHECK(render_loop_proxy_->BelongsToCurrentThread());
  DVLOG(1) << "Failed to call plugin.";
  session_error_cb_.Run(session_id, kUnknownError, 0);
}

void PpapiDecryptor::OnDecoderInitialized(StreamType stream_type,
                                          bool success) {
  DCHECK(render_loop_proxy_->BelongsToCurrentThread());
  switch (stream_type) {
    case kAudio:
      DCHECK(!audio_decoder_init_cb_.is_null());
      base::ResetAndReturn(&audio_decoder_init_cb_).Run(success);
      break;
    case kVideo:
      DCHECK(!video_decoder_init_cb_.is_null());
      base::ResetAndReturn(&video_decoder_init_cb_).Run(success);
      break;
    default:
      NOTREACHED();
  }
}

void PpapiDecryptor::OnSessionCreated(uint32 session_id,
                                      const std::string& web_session_id) {
  DCHECK(render_loop_proxy_->BelongsToCurrentThread());
  session_created_cb_.Run(session_id, web_session_id);
}

void PpapiDecryptor::OnSessionMessage(uint32 session_id,
                                      const std::vector<uint8>& message,
                                      const std::string& destination_url) {
  DCHECK(render_loop_proxy_->BelongsToCurrentThread());
  session_message_cb_.Run(session_id, message, destination_url);
}

void PpapiDecryptor::OnSessionReady(uint32 session_id) {
  DCHECK(render_loop_proxy_->BelongsToCurrentThread());

  // Based on the spec, we need to resume playback when update() completes
  // successfully, or when a session is successfully loaded. In both cases,
  // the CDM fires OnSessionReady() event. So we choose to call the NewKeyCBs
  // here.
  // TODO(xhwang): Rename OnSessionReady to indicate that the playback may
  // resume successfully (e.g. a new key is available or available again).
  if (!new_audio_key_cb_.is_null())
    new_audio_key_cb_.Run();

  if (!new_video_key_cb_.is_null())
    new_video_key_cb_.Run();

  session_ready_cb_.Run(session_id);
}

void PpapiDecryptor::OnSessionClosed(uint32 session_id) {
  DCHECK(render_loop_proxy_->BelongsToCurrentThread());
  session_closed_cb_.Run(session_id);
}

void PpapiDecryptor::OnSessionError(uint32 session_id,
                                    media::MediaKeys::KeyError error_code,
                                    uint32 system_code) {
  DCHECK(render_loop_proxy_->BelongsToCurrentThread());
  session_error_cb_.Run(session_id, error_code, system_code);
}

void PpapiDecryptor::OnFatalPluginError() {
  DCHECK(render_loop_proxy_->BelongsToCurrentThread());
  pepper_cdm_wrapper_.reset();
}

ContentDecryptorDelegate* PpapiDecryptor::CdmDelegate() {
  DCHECK(render_loop_proxy_->BelongsToCurrentThread());
  return (pepper_cdm_wrapper_) ? pepper_cdm_wrapper_->GetCdmDelegate() : NULL;
}

}  // namespace content

/* [<][>][^][v][top][bottom][index][help] */