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/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/message_loop/message_loop_proxy.h"
13 #include "content/renderer/pepper/content_decryptor_delegate.h"
14 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
15 #include "media/base/audio_decoder_config.h"
16 #include "media/base/cdm_key_information.h"
17 #include "media/base/data_buffer.h"
18 #include "media/base/decoder_buffer.h"
19 #include "media/base/key_systems.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 GURL
& security_origin
,
28 const CreatePepperCdmCB
& create_pepper_cdm_cb
,
29 const media::SessionMessageCB
& session_message_cb
,
30 const media::SessionClosedCB
& session_closed_cb
,
31 const media::SessionErrorCB
& session_error_cb
,
32 const media::SessionKeysChangeCB
& session_keys_change_cb
,
33 const media::SessionExpirationUpdateCB
& session_expiration_update_cb
) {
34 std::string plugin_type
= media::GetPepperType(key_system
);
35 DCHECK(!plugin_type
.empty());
36 scoped_ptr
<PepperCdmWrapper
> pepper_cdm_wrapper
=
37 create_pepper_cdm_cb
.Run(plugin_type
, security_origin
);
38 if (!pepper_cdm_wrapper
) {
39 DLOG(ERROR
) << "Plugin instance creation failed.";
40 return scoped_ptr
<PpapiDecryptor
>();
43 return scoped_ptr
<PpapiDecryptor
>(
44 new PpapiDecryptor(key_system
,
45 pepper_cdm_wrapper
.Pass(),
49 session_keys_change_cb
,
50 session_expiration_update_cb
));
53 PpapiDecryptor::PpapiDecryptor(
54 const std::string
& key_system
,
55 scoped_ptr
<PepperCdmWrapper
> pepper_cdm_wrapper
,
56 const media::SessionMessageCB
& session_message_cb
,
57 const media::SessionClosedCB
& session_closed_cb
,
58 const media::SessionErrorCB
& session_error_cb
,
59 const media::SessionKeysChangeCB
& session_keys_change_cb
,
60 const media::SessionExpirationUpdateCB
& session_expiration_update_cb
)
61 : pepper_cdm_wrapper_(pepper_cdm_wrapper
.Pass()),
62 session_message_cb_(session_message_cb
),
63 session_closed_cb_(session_closed_cb
),
64 session_error_cb_(session_error_cb
),
65 session_keys_change_cb_(session_keys_change_cb
),
66 session_expiration_update_cb_(session_expiration_update_cb
),
67 render_loop_proxy_(base::MessageLoopProxy::current()),
68 weak_ptr_factory_(this) {
69 DCHECK(pepper_cdm_wrapper_
.get());
70 DCHECK(!session_message_cb_
.is_null());
71 DCHECK(!session_closed_cb_
.is_null());
72 DCHECK(!session_error_cb_
.is_null());
73 DCHECK(!session_keys_change_cb
.is_null());
74 DCHECK(!session_expiration_update_cb
.is_null());
76 base::WeakPtr
<PpapiDecryptor
> weak_this
= weak_ptr_factory_
.GetWeakPtr();
77 CdmDelegate()->Initialize(
79 base::Bind(&PpapiDecryptor::OnSessionMessage
, weak_this
),
80 base::Bind(&PpapiDecryptor::OnSessionClosed
, weak_this
),
81 base::Bind(&PpapiDecryptor::OnSessionError
, weak_this
),
82 base::Bind(&PpapiDecryptor::OnSessionKeysChange
, weak_this
),
83 base::Bind(&PpapiDecryptor::OnSessionExpirationUpdate
, weak_this
),
84 base::Bind(&PpapiDecryptor::OnFatalPluginError
, weak_this
));
87 PpapiDecryptor::~PpapiDecryptor() {
88 pepper_cdm_wrapper_
.reset();
91 void PpapiDecryptor::SetServerCertificate(
92 const uint8
* certificate_data
,
93 int certificate_data_length
,
94 scoped_ptr
<media::SimpleCdmPromise
> promise
) {
95 DVLOG(2) << __FUNCTION__
;
96 DCHECK(render_loop_proxy_
->BelongsToCurrentThread());
99 promise
->reject(INVALID_STATE_ERROR
, 0, "CdmDelegate() does not exist.");
103 CdmDelegate()->SetServerCertificate(
104 certificate_data
, certificate_data_length
, promise
.Pass());
107 void PpapiDecryptor::CreateSessionAndGenerateRequest(
108 SessionType session_type
,
109 const std::string
& init_data_type
,
110 const uint8
* init_data
,
111 int init_data_length
,
112 scoped_ptr
<media::NewSessionCdmPromise
> promise
) {
113 DVLOG(2) << __FUNCTION__
;
114 DCHECK(render_loop_proxy_
->BelongsToCurrentThread());
116 if (!CdmDelegate()) {
117 promise
->reject(INVALID_STATE_ERROR
, 0, "CdmDelegate() does not exist.");
121 CdmDelegate()->CreateSessionAndGenerateRequest(session_type
, init_data_type
,
122 init_data
, init_data_length
,
126 void PpapiDecryptor::LoadSession(
127 SessionType session_type
,
128 const std::string
& session_id
,
129 scoped_ptr
<media::NewSessionCdmPromise
> promise
) {
130 DVLOG(2) << __FUNCTION__
;
131 DCHECK(render_loop_proxy_
->BelongsToCurrentThread());
133 if (!CdmDelegate()) {
134 promise
->reject(INVALID_STATE_ERROR
, 0, "CdmDelegate() does not exist.");
137 CdmDelegate()->LoadSession(session_type
, session_id
, promise
.Pass());
140 void PpapiDecryptor::UpdateSession(
141 const std::string
& session_id
,
142 const uint8
* response
,
144 scoped_ptr
<media::SimpleCdmPromise
> promise
) {
145 DCHECK(render_loop_proxy_
->BelongsToCurrentThread());
147 if (!CdmDelegate()) {
148 promise
->reject(INVALID_STATE_ERROR
, 0, "CdmDelegate() does not exist.");
151 CdmDelegate()->UpdateSession(session_id
, response
, response_length
,
155 void PpapiDecryptor::CloseSession(const std::string
& session_id
,
156 scoped_ptr
<media::SimpleCdmPromise
> promise
) {
157 DCHECK(render_loop_proxy_
->BelongsToCurrentThread());
159 if (!CdmDelegate()) {
160 promise
->reject(INVALID_STATE_ERROR
, 0, "CdmDelegate() does not exist.");
164 CdmDelegate()->CloseSession(session_id
, promise
.Pass());
167 void PpapiDecryptor::RemoveSession(
168 const std::string
& session_id
,
169 scoped_ptr
<media::SimpleCdmPromise
> promise
) {
170 DCHECK(render_loop_proxy_
->BelongsToCurrentThread());
172 if (!CdmDelegate()) {
173 promise
->reject(INVALID_STATE_ERROR
, 0, "CdmDelegate() does not exist.");
177 CdmDelegate()->RemoveSession(session_id
, promise
.Pass());
180 media::CdmContext
* PpapiDecryptor::GetCdmContext() {
184 media::Decryptor
* PpapiDecryptor::GetDecryptor() {
188 void PpapiDecryptor::RegisterNewKeyCB(StreamType stream_type
,
189 const NewKeyCB
& new_key_cb
) {
190 if (!render_loop_proxy_
->BelongsToCurrentThread()) {
191 render_loop_proxy_
->PostTask(FROM_HERE
,
192 base::Bind(&PpapiDecryptor::RegisterNewKeyCB
,
193 weak_ptr_factory_
.GetWeakPtr(),
199 DVLOG(3) << __FUNCTION__
<< " - stream_type: " << stream_type
;
200 switch (stream_type
) {
202 new_audio_key_cb_
= new_key_cb
;
205 new_video_key_cb_
= new_key_cb
;
212 void PpapiDecryptor::Decrypt(
213 StreamType stream_type
,
214 const scoped_refptr
<media::DecoderBuffer
>& encrypted
,
215 const DecryptCB
& decrypt_cb
) {
216 if (!render_loop_proxy_
->BelongsToCurrentThread()) {
217 render_loop_proxy_
->PostTask(FROM_HERE
,
218 base::Bind(&PpapiDecryptor::Decrypt
,
219 weak_ptr_factory_
.GetWeakPtr(),
226 DVLOG(3) << __FUNCTION__
<< " - stream_type: " << stream_type
;
227 if (!CdmDelegate() ||
228 !CdmDelegate()->Decrypt(stream_type
, encrypted
, decrypt_cb
)) {
229 decrypt_cb
.Run(kError
, NULL
);
233 void PpapiDecryptor::CancelDecrypt(StreamType stream_type
) {
234 if (!render_loop_proxy_
->BelongsToCurrentThread()) {
235 render_loop_proxy_
->PostTask(FROM_HERE
,
236 base::Bind(&PpapiDecryptor::CancelDecrypt
,
237 weak_ptr_factory_
.GetWeakPtr(),
242 DVLOG(1) << __FUNCTION__
<< " - stream_type: " << stream_type
;
244 CdmDelegate()->CancelDecrypt(stream_type
);
247 void PpapiDecryptor::InitializeAudioDecoder(
248 const media::AudioDecoderConfig
& config
,
249 const DecoderInitCB
& init_cb
) {
250 if (!render_loop_proxy_
->BelongsToCurrentThread()) {
251 render_loop_proxy_
->PostTask(
253 base::Bind(&PpapiDecryptor::InitializeAudioDecoder
,
254 weak_ptr_factory_
.GetWeakPtr(),
260 DVLOG(2) << __FUNCTION__
;
261 DCHECK(config
.is_encrypted());
262 DCHECK(config
.IsValidConfig());
264 audio_decoder_init_cb_
= init_cb
;
265 if (!CdmDelegate() || !CdmDelegate()->InitializeAudioDecoder(
267 base::Bind(&PpapiDecryptor::OnDecoderInitialized
,
268 weak_ptr_factory_
.GetWeakPtr(),
270 base::ResetAndReturn(&audio_decoder_init_cb_
).Run(false);
275 void PpapiDecryptor::InitializeVideoDecoder(
276 const media::VideoDecoderConfig
& config
,
277 const DecoderInitCB
& init_cb
) {
278 if (!render_loop_proxy_
->BelongsToCurrentThread()) {
279 render_loop_proxy_
->PostTask(
281 base::Bind(&PpapiDecryptor::InitializeVideoDecoder
,
282 weak_ptr_factory_
.GetWeakPtr(),
288 DVLOG(2) << __FUNCTION__
;
289 DCHECK(config
.is_encrypted());
290 DCHECK(config
.IsValidConfig());
292 video_decoder_init_cb_
= init_cb
;
293 if (!CdmDelegate() || !CdmDelegate()->InitializeVideoDecoder(
295 base::Bind(&PpapiDecryptor::OnDecoderInitialized
,
296 weak_ptr_factory_
.GetWeakPtr(),
298 base::ResetAndReturn(&video_decoder_init_cb_
).Run(false);
303 void PpapiDecryptor::DecryptAndDecodeAudio(
304 const scoped_refptr
<media::DecoderBuffer
>& encrypted
,
305 const AudioDecodeCB
& audio_decode_cb
) {
306 if (!render_loop_proxy_
->BelongsToCurrentThread()) {
307 render_loop_proxy_
->PostTask(
309 base::Bind(&PpapiDecryptor::DecryptAndDecodeAudio
,
310 weak_ptr_factory_
.GetWeakPtr(),
316 DVLOG(3) << __FUNCTION__
;
317 if (!CdmDelegate() ||
318 !CdmDelegate()->DecryptAndDecodeAudio(encrypted
, audio_decode_cb
)) {
319 audio_decode_cb
.Run(kError
, AudioFrames());
323 void PpapiDecryptor::DecryptAndDecodeVideo(
324 const scoped_refptr
<media::DecoderBuffer
>& encrypted
,
325 const VideoDecodeCB
& video_decode_cb
) {
326 if (!render_loop_proxy_
->BelongsToCurrentThread()) {
327 render_loop_proxy_
->PostTask(
329 base::Bind(&PpapiDecryptor::DecryptAndDecodeVideo
,
330 weak_ptr_factory_
.GetWeakPtr(),
336 DVLOG(3) << __FUNCTION__
;
337 if (!CdmDelegate() ||
338 !CdmDelegate()->DecryptAndDecodeVideo(encrypted
, video_decode_cb
)) {
339 video_decode_cb
.Run(kError
, NULL
);
343 void PpapiDecryptor::ResetDecoder(StreamType stream_type
) {
344 if (!render_loop_proxy_
->BelongsToCurrentThread()) {
345 render_loop_proxy_
->PostTask(FROM_HERE
,
346 base::Bind(&PpapiDecryptor::ResetDecoder
,
347 weak_ptr_factory_
.GetWeakPtr(),
352 DVLOG(2) << __FUNCTION__
<< " - stream_type: " << stream_type
;
354 CdmDelegate()->ResetDecoder(stream_type
);
357 void PpapiDecryptor::DeinitializeDecoder(StreamType stream_type
) {
358 if (!render_loop_proxy_
->BelongsToCurrentThread()) {
359 render_loop_proxy_
->PostTask(
361 base::Bind(&PpapiDecryptor::DeinitializeDecoder
,
362 weak_ptr_factory_
.GetWeakPtr(),
367 DVLOG(2) << __FUNCTION__
<< " - stream_type: " << stream_type
;
369 CdmDelegate()->DeinitializeDecoder(stream_type
);
372 void PpapiDecryptor::OnDecoderInitialized(StreamType stream_type
,
374 DCHECK(render_loop_proxy_
->BelongsToCurrentThread());
375 switch (stream_type
) {
377 DCHECK(!audio_decoder_init_cb_
.is_null());
378 base::ResetAndReturn(&audio_decoder_init_cb_
).Run(success
);
381 DCHECK(!video_decoder_init_cb_
.is_null());
382 base::ResetAndReturn(&video_decoder_init_cb_
).Run(success
);
389 void PpapiDecryptor::OnSessionMessage(const std::string
& session_id
,
390 MessageType message_type
,
391 const std::vector
<uint8
>& message
,
392 const GURL
& legacy_destination_url
) {
393 DCHECK(render_loop_proxy_
->BelongsToCurrentThread());
394 session_message_cb_
.Run(session_id
, message_type
, message
,
395 legacy_destination_url
);
398 void PpapiDecryptor::OnSessionKeysChange(const std::string
& session_id
,
399 bool has_additional_usable_key
,
400 media::CdmKeysInfo keys_info
) {
401 DCHECK(render_loop_proxy_
->BelongsToCurrentThread());
403 // TODO(jrummell): Handling resume playback should be done in the media
404 // player, not in the Decryptors. http://crbug.com/413413.
405 if (has_additional_usable_key
)
406 AttemptToResumePlayback();
408 session_keys_change_cb_
.Run(session_id
, has_additional_usable_key
,
412 void PpapiDecryptor::OnSessionExpirationUpdate(
413 const std::string
& session_id
,
414 const base::Time
& new_expiry_time
) {
415 DCHECK(render_loop_proxy_
->BelongsToCurrentThread());
416 session_expiration_update_cb_
.Run(session_id
, new_expiry_time
);
419 void PpapiDecryptor::OnSessionClosed(const std::string
& session_id
) {
420 DCHECK(render_loop_proxy_
->BelongsToCurrentThread());
421 session_closed_cb_
.Run(session_id
);
424 void PpapiDecryptor::OnSessionError(const std::string
& session_id
,
425 MediaKeys::Exception exception_code
,
427 const std::string
& error_description
) {
428 DCHECK(render_loop_proxy_
->BelongsToCurrentThread());
429 session_error_cb_
.Run(session_id
, exception_code
, system_code
,
433 void PpapiDecryptor::AttemptToResumePlayback() {
434 if (!new_audio_key_cb_
.is_null())
435 new_audio_key_cb_
.Run();
437 if (!new_video_key_cb_
.is_null())
438 new_video_key_cb_
.Run();
441 void PpapiDecryptor::OnFatalPluginError() {
442 DCHECK(render_loop_proxy_
->BelongsToCurrentThread());
443 pepper_cdm_wrapper_
.reset();
446 ContentDecryptorDelegate
* PpapiDecryptor::CdmDelegate() {
447 DCHECK(render_loop_proxy_
->BelongsToCurrentThread());
448 return (pepper_cdm_wrapper_
) ? pepper_cdm_wrapper_
->GetCdmDelegate() : NULL
;
451 } // namespace content