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.
5 #include "content/renderer/media/crypto/ppapi_decryptor.h"
10 #include "base/callback_helpers.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/message_loop/message_loop_proxy.h"
15 #include "content/renderer/pepper/content_decryptor_delegate.h"
16 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
17 #include "media/base/audio_decoder_config.h"
18 #include "media/base/data_buffer.h"
19 #include "media/base/decoder_buffer.h"
20 #include "media/base/video_decoder_config.h"
21 #include "media/base/video_frame.h"
25 scoped_ptr<PpapiDecryptor> PpapiDecryptor::Create(
26 const std::string& key_system,
27 const scoped_refptr<PepperPluginInstanceImpl>& plugin_instance,
28 const media::SessionCreatedCB& session_created_cb,
29 const media::SessionMessageCB& session_message_cb,
30 const media::SessionReadyCB& session_ready_cb,
31 const media::SessionClosedCB& session_closed_cb,
32 const media::SessionErrorCB& session_error_cb,
33 const base::Closure& destroy_plugin_cb) {
34 ContentDecryptorDelegate* plugin_cdm_delegate =
35 plugin_instance->GetContentDecryptorDelegate();
36 if (!plugin_cdm_delegate) {
37 DVLOG(1) << "PpapiDecryptor: plugin cdm delegate creation failed.";
38 return scoped_ptr<PpapiDecryptor>();
41 return scoped_ptr<PpapiDecryptor>(new PpapiDecryptor(key_system,
52 PpapiDecryptor::PpapiDecryptor(
53 const std::string& key_system,
54 const scoped_refptr<PepperPluginInstanceImpl>& plugin_instance,
55 ContentDecryptorDelegate* plugin_cdm_delegate,
56 const media::SessionCreatedCB& session_created_cb,
57 const media::SessionMessageCB& session_message_cb,
58 const media::SessionReadyCB& session_ready_cb,
59 const media::SessionClosedCB& session_closed_cb,
60 const media::SessionErrorCB& session_error_cb,
61 const base::Closure& destroy_plugin_cb)
62 : plugin_instance_(plugin_instance),
63 plugin_cdm_delegate_(plugin_cdm_delegate),
64 session_created_cb_(session_created_cb),
65 session_message_cb_(session_message_cb),
66 session_ready_cb_(session_ready_cb),
67 session_closed_cb_(session_closed_cb),
68 session_error_cb_(session_error_cb),
69 destroy_plugin_cb_(destroy_plugin_cb),
70 render_loop_proxy_(base::MessageLoopProxy::current()),
71 weak_ptr_factory_(this) {
72 DCHECK(plugin_instance_.get());
73 DCHECK(!session_created_cb_.is_null());
74 DCHECK(!session_message_cb_.is_null());
75 DCHECK(!session_ready_cb_.is_null());
76 DCHECK(!session_closed_cb_.is_null());
77 DCHECK(!session_error_cb_.is_null());
78 DCHECK(!destroy_plugin_cb_.is_null());
80 weak_this_ = weak_ptr_factory_.GetWeakPtr();
82 plugin_cdm_delegate_->Initialize(
84 base::Bind(&PpapiDecryptor::OnSessionCreated, weak_this_),
85 base::Bind(&PpapiDecryptor::OnSessionMessage, weak_this_),
86 base::Bind(&PpapiDecryptor::OnSessionReady, weak_this_),
87 base::Bind(&PpapiDecryptor::OnSessionClosed, weak_this_),
88 base::Bind(&PpapiDecryptor::OnSessionError, weak_this_),
89 base::Bind(&PpapiDecryptor::OnFatalPluginError, weak_this_));
92 PpapiDecryptor::~PpapiDecryptor() {
93 plugin_cdm_delegate_ = NULL;
94 plugin_instance_ = NULL;
95 if (!destroy_plugin_cb_.is_null())
96 base::ResetAndReturn(&destroy_plugin_cb_).Run();
99 bool PpapiDecryptor::CreateSession(uint32 session_id,
100 const std::string& content_type,
101 const uint8* init_data,
102 int init_data_length) {
103 DVLOG(2) << __FUNCTION__;
104 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
106 if (!plugin_cdm_delegate_ ||
107 !plugin_cdm_delegate_->CreateSession(
108 session_id, content_type, init_data, init_data_length)) {
109 ReportFailureToCallPlugin(session_id);
116 void PpapiDecryptor::LoadSession(uint32 session_id,
117 const std::string& web_session_id) {
118 DVLOG(2) << __FUNCTION__;
119 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
121 if (!plugin_cdm_delegate_) {
122 ReportFailureToCallPlugin(session_id);
126 plugin_cdm_delegate_->LoadSession(session_id, web_session_id);
129 void PpapiDecryptor::UpdateSession(uint32 session_id,
130 const uint8* response,
131 int response_length) {
132 DVLOG(2) << __FUNCTION__;
133 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
135 if (!plugin_cdm_delegate_ || !plugin_cdm_delegate_->UpdateSession(
136 session_id, response, response_length)) {
137 ReportFailureToCallPlugin(session_id);
142 void PpapiDecryptor::ReleaseSession(uint32 session_id) {
143 DVLOG(2) << __FUNCTION__;
144 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
146 if (!plugin_cdm_delegate_ ||
147 !plugin_cdm_delegate_->ReleaseSession(session_id)) {
148 ReportFailureToCallPlugin(session_id);
153 media::Decryptor* PpapiDecryptor::GetDecryptor() {
157 void PpapiDecryptor::RegisterNewKeyCB(StreamType stream_type,
158 const NewKeyCB& new_key_cb) {
159 if (!render_loop_proxy_->BelongsToCurrentThread()) {
160 render_loop_proxy_->PostTask(FROM_HERE, base::Bind(
161 &PpapiDecryptor::RegisterNewKeyCB, weak_this_, stream_type,
166 DVLOG(3) << __FUNCTION__ << " - stream_type: " << stream_type;
167 switch (stream_type) {
169 new_audio_key_cb_ = new_key_cb;
172 new_video_key_cb_ = new_key_cb;
179 void PpapiDecryptor::Decrypt(
180 StreamType stream_type,
181 const scoped_refptr<media::DecoderBuffer>& encrypted,
182 const DecryptCB& decrypt_cb) {
183 if (!render_loop_proxy_->BelongsToCurrentThread()) {
184 render_loop_proxy_->PostTask(FROM_HERE, base::Bind(
185 &PpapiDecryptor::Decrypt, weak_this_,
186 stream_type, encrypted, decrypt_cb));
190 DVLOG(3) << __FUNCTION__ << " - stream_type: " << stream_type;
191 if (!plugin_cdm_delegate_ ||
192 !plugin_cdm_delegate_->Decrypt(stream_type, encrypted, decrypt_cb)) {
193 decrypt_cb.Run(kError, NULL);
197 void PpapiDecryptor::CancelDecrypt(StreamType stream_type) {
198 if (!render_loop_proxy_->BelongsToCurrentThread()) {
199 render_loop_proxy_->PostTask(FROM_HERE, base::Bind(
200 &PpapiDecryptor::CancelDecrypt, weak_this_, stream_type));
204 DVLOG(1) << __FUNCTION__ << " - stream_type: " << stream_type;
205 if (plugin_cdm_delegate_)
206 plugin_cdm_delegate_->CancelDecrypt(stream_type);
209 void PpapiDecryptor::InitializeAudioDecoder(
210 const media::AudioDecoderConfig& config,
211 const DecoderInitCB& init_cb) {
212 if (!render_loop_proxy_->BelongsToCurrentThread()) {
213 render_loop_proxy_->PostTask(FROM_HERE, base::Bind(
214 &PpapiDecryptor::InitializeAudioDecoder, weak_this_, config, init_cb));
218 DVLOG(2) << __FUNCTION__;
219 DCHECK(config.is_encrypted());
220 DCHECK(config.IsValidConfig());
222 audio_decoder_init_cb_ = init_cb;
223 if (!plugin_cdm_delegate_ ||
224 !plugin_cdm_delegate_->InitializeAudioDecoder(config, base::Bind(
225 &PpapiDecryptor::OnDecoderInitialized, weak_this_, kAudio))) {
226 base::ResetAndReturn(&audio_decoder_init_cb_).Run(false);
231 void PpapiDecryptor::InitializeVideoDecoder(
232 const media::VideoDecoderConfig& config,
233 const DecoderInitCB& init_cb) {
234 if (!render_loop_proxy_->BelongsToCurrentThread()) {
235 render_loop_proxy_->PostTask(FROM_HERE, base::Bind(
236 &PpapiDecryptor::InitializeVideoDecoder, weak_this_, config, init_cb));
240 DVLOG(2) << __FUNCTION__;
241 DCHECK(config.is_encrypted());
242 DCHECK(config.IsValidConfig());
244 video_decoder_init_cb_ = init_cb;
245 if (!plugin_cdm_delegate_ ||
246 !plugin_cdm_delegate_->InitializeVideoDecoder(config, base::Bind(
247 &PpapiDecryptor::OnDecoderInitialized, weak_this_, kVideo))) {
248 base::ResetAndReturn(&video_decoder_init_cb_).Run(false);
253 void PpapiDecryptor::DecryptAndDecodeAudio(
254 const scoped_refptr<media::DecoderBuffer>& encrypted,
255 const AudioDecodeCB& audio_decode_cb) {
256 if (!render_loop_proxy_->BelongsToCurrentThread()) {
257 render_loop_proxy_->PostTask(FROM_HERE, base::Bind(
258 &PpapiDecryptor::DecryptAndDecodeAudio, weak_this_,
259 encrypted, audio_decode_cb));
263 DVLOG(3) << __FUNCTION__;
264 if (!plugin_cdm_delegate_ || !plugin_cdm_delegate_->DecryptAndDecodeAudio(
265 encrypted, audio_decode_cb)) {
266 audio_decode_cb.Run(kError, AudioBuffers());
270 void PpapiDecryptor::DecryptAndDecodeVideo(
271 const scoped_refptr<media::DecoderBuffer>& encrypted,
272 const VideoDecodeCB& video_decode_cb) {
273 if (!render_loop_proxy_->BelongsToCurrentThread()) {
274 render_loop_proxy_->PostTask(FROM_HERE, base::Bind(
275 &PpapiDecryptor::DecryptAndDecodeVideo, weak_this_,
276 encrypted, video_decode_cb));
280 DVLOG(3) << __FUNCTION__;
281 if (!plugin_cdm_delegate_ || !plugin_cdm_delegate_->DecryptAndDecodeVideo(
282 encrypted, video_decode_cb)) {
283 video_decode_cb.Run(kError, NULL);
287 void PpapiDecryptor::ResetDecoder(StreamType stream_type) {
288 if (!render_loop_proxy_->BelongsToCurrentThread()) {
289 render_loop_proxy_->PostTask(FROM_HERE, base::Bind(
290 &PpapiDecryptor::ResetDecoder, weak_this_, stream_type));
294 DVLOG(2) << __FUNCTION__ << " - stream_type: " << stream_type;
295 if (plugin_cdm_delegate_)
296 plugin_cdm_delegate_->ResetDecoder(stream_type);
299 void PpapiDecryptor::DeinitializeDecoder(StreamType stream_type) {
300 if (!render_loop_proxy_->BelongsToCurrentThread()) {
301 render_loop_proxy_->PostTask(FROM_HERE, base::Bind(
302 &PpapiDecryptor::DeinitializeDecoder, weak_this_, stream_type));
306 DVLOG(2) << __FUNCTION__ << " - stream_type: " << stream_type;
307 if (plugin_cdm_delegate_)
308 plugin_cdm_delegate_->DeinitializeDecoder(stream_type);
311 void PpapiDecryptor::ReportFailureToCallPlugin(uint32 session_id) {
312 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
313 DVLOG(1) << "Failed to call plugin.";
314 session_error_cb_.Run(session_id, kUnknownError, 0);
317 void PpapiDecryptor::OnDecoderInitialized(StreamType stream_type,
319 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
320 switch (stream_type) {
322 DCHECK(!audio_decoder_init_cb_.is_null());
323 base::ResetAndReturn(&audio_decoder_init_cb_).Run(success);
326 DCHECK(!video_decoder_init_cb_.is_null());
327 base::ResetAndReturn(&video_decoder_init_cb_).Run(success);
334 void PpapiDecryptor::OnSessionCreated(uint32 session_id,
335 const std::string& web_session_id) {
336 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
337 session_created_cb_.Run(session_id, web_session_id);
340 void PpapiDecryptor::OnSessionMessage(uint32 session_id,
341 const std::vector<uint8>& message,
342 const std::string& destination_url) {
343 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
344 session_message_cb_.Run(session_id, message, destination_url);
347 void PpapiDecryptor::OnSessionReady(uint32 session_id) {
348 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
350 // Based on the spec, we need to resume playback when update() completes
351 // successfully, or when a session is successfully loaded. In both cases,
352 // the CDM fires OnSessionReady() event. So we choose to call the NewKeyCBs
354 // TODO(xhwang): Rename OnSessionReady to indicate that the playback may
355 // resume successfully (e.g. a new key is available or available again).
356 if (!new_audio_key_cb_.is_null())
357 new_audio_key_cb_.Run();
359 if (!new_video_key_cb_.is_null())
360 new_video_key_cb_.Run();
362 session_ready_cb_.Run(session_id);
365 void PpapiDecryptor::OnSessionClosed(uint32 session_id) {
366 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
367 session_closed_cb_.Run(session_id);
370 void PpapiDecryptor::OnSessionError(uint32 session_id,
371 media::MediaKeys::KeyError error_code,
373 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
374 session_error_cb_.Run(session_id, error_code, system_code);
377 void PpapiDecryptor::OnFatalPluginError() {
378 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
379 DCHECK(plugin_cdm_delegate_);
380 plugin_cdm_delegate_ = NULL;
381 plugin_instance_ = NULL;
382 base::ResetAndReturn(&destroy_plugin_cb_).Run();
385 } // namespace content