blob: 80f8149e315212f2a135108ccfe3779e9f3beb76 [file] [log] [blame]
// 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/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 scoped_refptr<PepperPluginInstanceImpl>& plugin_instance,
const media::KeyAddedCB& key_added_cb,
const media::KeyErrorCB& key_error_cb,
const media::KeyMessageCB& key_message_cb,
const base::Closure& destroy_plugin_cb) {
ContentDecryptorDelegate* plugin_cdm_delegate =
plugin_instance->GetContentDecryptorDelegate();
if (!plugin_cdm_delegate) {
DVLOG(1) << "PpapiDecryptor: plugin cdm delegate creation failed.";
return scoped_ptr<PpapiDecryptor>();
}
// TODO(jrummell): How do we get the can_challenge_platform value from
// the browser?
const bool can_challenge_platform = false;
plugin_cdm_delegate->Initialize(key_system, can_challenge_platform);
return scoped_ptr<PpapiDecryptor>(new PpapiDecryptor(plugin_instance,
plugin_cdm_delegate,
key_added_cb,
key_error_cb,
key_message_cb,
destroy_plugin_cb));
}
PpapiDecryptor::PpapiDecryptor(
const scoped_refptr<PepperPluginInstanceImpl>& plugin_instance,
ContentDecryptorDelegate* plugin_cdm_delegate,
const media::KeyAddedCB& key_added_cb,
const media::KeyErrorCB& key_error_cb,
const media::KeyMessageCB& key_message_cb,
const base::Closure& destroy_plugin_cb)
: plugin_instance_(plugin_instance),
plugin_cdm_delegate_(plugin_cdm_delegate),
key_added_cb_(key_added_cb),
key_error_cb_(key_error_cb),
key_message_cb_(key_message_cb),
destroy_plugin_cb_(destroy_plugin_cb),
render_loop_proxy_(base::MessageLoopProxy::current()),
weak_ptr_factory_(this),
weak_this_(weak_ptr_factory_.GetWeakPtr()) {
DCHECK(plugin_instance_.get());
plugin_cdm_delegate_->SetKeyEventCallbacks(
base::Bind(&PpapiDecryptor::KeyAdded, weak_this_),
base::Bind(&PpapiDecryptor::KeyError, weak_this_),
base::Bind(&PpapiDecryptor::KeyMessage, weak_this_));
}
PpapiDecryptor::~PpapiDecryptor() {
plugin_cdm_delegate_ = NULL;
plugin_instance_ = NULL;
destroy_plugin_cb_.Run();
}
bool PpapiDecryptor::GenerateKeyRequest(const std::string& type,
const uint8* init_data,
int init_data_length) {
DVLOG(2) << "GenerateKeyRequest()";
DCHECK(render_loop_proxy_->BelongsToCurrentThread());
DCHECK(plugin_cdm_delegate_);
if (!plugin_cdm_delegate_->GenerateKeyRequest(
type, init_data, init_data_length)) {
ReportFailureToCallPlugin(std::string());
return false;
}
return true;
}
void PpapiDecryptor::AddKey(const uint8* key,
int key_length,
const uint8* init_data,
int init_data_length,
const std::string& session_id) {
DVLOG(2) << "AddKey()";
DCHECK(render_loop_proxy_->BelongsToCurrentThread());
if (!plugin_cdm_delegate_->AddKey(
session_id, key, key_length, init_data, init_data_length)) {
ReportFailureToCallPlugin(session_id);
}
if (!new_audio_key_cb_.is_null())
new_audio_key_cb_.Run();
if (!new_video_key_cb_.is_null())
new_video_key_cb_.Run();
}
void PpapiDecryptor::CancelKeyRequest(const std::string& session_id) {
DVLOG(2) << "CancelKeyRequest()";
DCHECK(render_loop_proxy_->BelongsToCurrentThread());
if (!plugin_cdm_delegate_->CancelKeyRequest(session_id))
ReportFailureToCallPlugin(session_id);
}
media::Decryptor* PpapiDecryptor::GetDecryptor() {
#if defined(GOOGLE_TV)
// Google TV only uses PpapiDecrytor as a MediaKeys and does not need the
// Decryptor interface of the PpapiDecryptor.
// Details: If we don't do this GTV will be broken. The reason is that during
// initialization, MediaSourceDelegate tries to use DecryptingDemuxerStream
// to decrypt the stream in the renderer process (for ClearKey support).
// However, for GTV, PpapiDecryptor cannot do decryption at all. By returning
// NULL, DDS init will fail and we fallback to what GTV used to do.
return NULL;
#else
return this;
#endif // defined(GOOGLE_TV)
}
void PpapiDecryptor::RegisterNewKeyCB(StreamType stream_type,
const NewKeyCB& new_key_cb) {
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_this_,
stream_type, encrypted, decrypt_cb));
return;
}
DVLOG(3) << "Decrypt() - stream_type: " << stream_type;
if (!plugin_cdm_delegate_->Decrypt(stream_type, encrypted, decrypt_cb))
decrypt_cb.Run(kError, NULL);
}
void PpapiDecryptor::CancelDecrypt(StreamType stream_type) {
DVLOG(1) << "CancelDecrypt() - stream_type: " << stream_type;
plugin_cdm_delegate_->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_this_, config, init_cb));
return;
}
DVLOG(2) << "InitializeAudioDecoder()";
DCHECK(config.is_encrypted());
DCHECK(config.IsValidConfig());
audio_decoder_init_cb_ = init_cb;
if (!plugin_cdm_delegate_->InitializeAudioDecoder(config, base::Bind(
&PpapiDecryptor::OnDecoderInitialized, weak_this_, 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_this_, config, init_cb));
return;
}
DVLOG(2) << "InitializeVideoDecoder()";
DCHECK(config.is_encrypted());
DCHECK(config.IsValidConfig());
video_decoder_init_cb_ = init_cb;
if (!plugin_cdm_delegate_->InitializeVideoDecoder(config, base::Bind(
&PpapiDecryptor::OnDecoderInitialized, weak_this_, 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_this_,
encrypted, audio_decode_cb));
return;
}
DVLOG(3) << "DecryptAndDecodeAudio()";
if (!plugin_cdm_delegate_->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_this_,
encrypted, video_decode_cb));
return;
}
DVLOG(3) << "DecryptAndDecodeVideo()";
if (!plugin_cdm_delegate_->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_this_, stream_type));
return;
}
DVLOG(2) << "ResetDecoder() - stream_type: " << stream_type;
plugin_cdm_delegate_->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_this_, stream_type));
return;
}
DVLOG(2) << "DeinitializeDecoder() - stream_type: " << stream_type;
plugin_cdm_delegate_->DeinitializeDecoder(stream_type);
}
void PpapiDecryptor::ReportFailureToCallPlugin(const std::string& session_id) {
DVLOG(1) << "Failed to call plugin.";
key_error_cb_.Run(session_id, kUnknownError, 0);
}
void PpapiDecryptor::OnDecoderInitialized(StreamType stream_type,
bool success) {
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::KeyAdded(const std::string& session_id) {
DCHECK(render_loop_proxy_->BelongsToCurrentThread());
key_added_cb_.Run(session_id);
}
void PpapiDecryptor::KeyError(const std::string& session_id,
media::MediaKeys::KeyError error_code,
int system_code) {
DCHECK(render_loop_proxy_->BelongsToCurrentThread());
key_error_cb_.Run(session_id, error_code, system_code);
}
void PpapiDecryptor::KeyMessage(const std::string& session_id,
const std::vector<uint8>& message,
const std::string& default_url) {
DCHECK(render_loop_proxy_->BelongsToCurrentThread());
key_message_cb_.Run(session_id, message, default_url);
}
} // namespace content