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/location.h"
11 #include "base/logging.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "content/renderer/pepper/content_decryptor_delegate.h"
15 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
16 #include "media/base/audio_decoder_config.h"
17 #include "media/base/cdm_initialized_promise.h"
18 #include "media/base/cdm_key_information.h"
19 #include "media/base/data_buffer.h"
20 #include "media/base/decoder_buffer.h"
21 #include "media/base/key_systems.h"
22 #include "media/base/video_decoder_config.h"
23 #include "media/base/video_frame.h"
27 void PpapiDecryptor::Create(
28 const std::string
& key_system
,
29 const GURL
& security_origin
,
30 bool allow_distinctive_identifier
,
31 bool allow_persistent_state
,
32 const CreatePepperCdmCB
& create_pepper_cdm_cb
,
33 const media::SessionMessageCB
& session_message_cb
,
34 const media::SessionClosedCB
& session_closed_cb
,
35 const media::LegacySessionErrorCB
& legacy_session_error_cb
,
36 const media::SessionKeysChangeCB
& session_keys_change_cb
,
37 const media::SessionExpirationUpdateCB
& session_expiration_update_cb
,
38 const media::CdmCreatedCB
& cdm_created_cb
) {
39 std::string plugin_type
= media::GetPepperType(key_system
);
40 DCHECK(!plugin_type
.empty());
41 scoped_ptr
<PepperCdmWrapper
> pepper_cdm_wrapper
=
42 create_pepper_cdm_cb
.Run(plugin_type
, security_origin
);
43 if (!pepper_cdm_wrapper
) {
45 "Unable to create the CDM for the key system " + key_system
+ ".";
46 DLOG(ERROR
) << message
;
47 base::ThreadTaskRunnerHandle::Get()->PostTask(
48 FROM_HERE
, base::Bind(cdm_created_cb
, nullptr, message
));
52 scoped_ptr
<PpapiDecryptor
> ppapi_decryptor(
53 new PpapiDecryptor(pepper_cdm_wrapper
.Pass(), session_message_cb
,
54 session_closed_cb
, legacy_session_error_cb
,
55 session_keys_change_cb
, session_expiration_update_cb
));
57 // PpapiDecryptor ownership passed to the promise, but keep a copy in order
58 // to call InitializeCdm().
59 PpapiDecryptor
* ppapi_decryptor_copy
= ppapi_decryptor
.get();
60 scoped_ptr
<media::CdmInitializedPromise
> promise(
61 new media::CdmInitializedPromise(cdm_created_cb
, ppapi_decryptor
.Pass()));
62 ppapi_decryptor_copy
->InitializeCdm(key_system
, allow_distinctive_identifier
,
63 allow_persistent_state
, promise
.Pass());
66 PpapiDecryptor::PpapiDecryptor(
67 scoped_ptr
<PepperCdmWrapper
> pepper_cdm_wrapper
,
68 const media::SessionMessageCB
& session_message_cb
,
69 const media::SessionClosedCB
& session_closed_cb
,
70 const media::LegacySessionErrorCB
& legacy_session_error_cb
,
71 const media::SessionKeysChangeCB
& session_keys_change_cb
,
72 const media::SessionExpirationUpdateCB
& session_expiration_update_cb
)
73 : pepper_cdm_wrapper_(pepper_cdm_wrapper
.Pass()),
74 session_message_cb_(session_message_cb
),
75 session_closed_cb_(session_closed_cb
),
76 legacy_session_error_cb_(legacy_session_error_cb
),
77 session_keys_change_cb_(session_keys_change_cb
),
78 session_expiration_update_cb_(session_expiration_update_cb
),
79 render_task_runner_(base::ThreadTaskRunnerHandle::Get()),
80 weak_ptr_factory_(this) {
81 DCHECK(pepper_cdm_wrapper_
.get());
82 DCHECK(!session_message_cb_
.is_null());
83 DCHECK(!session_closed_cb_
.is_null());
84 DCHECK(!legacy_session_error_cb_
.is_null());
85 DCHECK(!session_keys_change_cb
.is_null());
86 DCHECK(!session_expiration_update_cb
.is_null());
89 PpapiDecryptor::~PpapiDecryptor() {
90 pepper_cdm_wrapper_
.reset();
93 void PpapiDecryptor::InitializeCdm(
94 const std::string
& key_system
,
95 bool allow_distinctive_identifier
,
96 bool allow_persistent_state
,
97 scoped_ptr
<media::SimpleCdmPromise
> promise
) {
98 base::WeakPtr
<PpapiDecryptor
> weak_this
= weak_ptr_factory_
.GetWeakPtr();
99 CdmDelegate()->Initialize(
100 key_system
, allow_distinctive_identifier
, allow_persistent_state
,
101 base::Bind(&PpapiDecryptor::OnSessionMessage
, weak_this
),
102 base::Bind(&PpapiDecryptor::OnSessionClosed
, weak_this
),
103 base::Bind(&PpapiDecryptor::OnLegacySessionError
, weak_this
),
104 base::Bind(&PpapiDecryptor::OnSessionKeysChange
, weak_this
),
105 base::Bind(&PpapiDecryptor::OnSessionExpirationUpdate
, weak_this
),
106 base::Bind(&PpapiDecryptor::OnFatalPluginError
, weak_this
),
110 void PpapiDecryptor::SetServerCertificate(
111 const std::vector
<uint8_t>& certificate
,
112 scoped_ptr
<media::SimpleCdmPromise
> promise
) {
113 DVLOG(2) << __FUNCTION__
;
114 DCHECK(render_task_runner_
->BelongsToCurrentThread());
116 if (!CdmDelegate()) {
117 promise
->reject(INVALID_STATE_ERROR
, 0, "CDM has failed.");
121 CdmDelegate()->SetServerCertificate(certificate
, promise
.Pass());
124 void PpapiDecryptor::CreateSessionAndGenerateRequest(
125 SessionType session_type
,
126 media::EmeInitDataType init_data_type
,
127 const std::vector
<uint8_t>& init_data
,
128 scoped_ptr
<media::NewSessionCdmPromise
> promise
) {
129 DVLOG(2) << __FUNCTION__
;
130 DCHECK(render_task_runner_
->BelongsToCurrentThread());
132 if (!CdmDelegate()) {
133 promise
->reject(INVALID_STATE_ERROR
, 0, "CDM has failed.");
137 CdmDelegate()->CreateSessionAndGenerateRequest(session_type
, init_data_type
,
138 init_data
, promise
.Pass());
141 void PpapiDecryptor::LoadSession(
142 SessionType session_type
,
143 const std::string
& session_id
,
144 scoped_ptr
<media::NewSessionCdmPromise
> promise
) {
145 DVLOG(2) << __FUNCTION__
;
146 DCHECK(render_task_runner_
->BelongsToCurrentThread());
148 if (!CdmDelegate()) {
149 promise
->reject(INVALID_STATE_ERROR
, 0, "CDM has failed.");
152 CdmDelegate()->LoadSession(session_type
, session_id
, promise
.Pass());
155 void PpapiDecryptor::UpdateSession(
156 const std::string
& session_id
,
157 const std::vector
<uint8_t>& response
,
158 scoped_ptr
<media::SimpleCdmPromise
> promise
) {
159 DCHECK(render_task_runner_
->BelongsToCurrentThread());
161 if (!CdmDelegate()) {
162 promise
->reject(INVALID_STATE_ERROR
, 0, "CDM has failed.");
165 CdmDelegate()->UpdateSession(session_id
, response
, promise
.Pass());
168 void PpapiDecryptor::CloseSession(const std::string
& session_id
,
169 scoped_ptr
<media::SimpleCdmPromise
> promise
) {
170 DCHECK(render_task_runner_
->BelongsToCurrentThread());
172 if (!CdmDelegate()) {
173 promise
->reject(INVALID_STATE_ERROR
, 0, "CDM has failed.");
177 CdmDelegate()->CloseSession(session_id
, promise
.Pass());
180 void PpapiDecryptor::RemoveSession(
181 const std::string
& session_id
,
182 scoped_ptr
<media::SimpleCdmPromise
> promise
) {
183 DCHECK(render_task_runner_
->BelongsToCurrentThread());
185 if (!CdmDelegate()) {
186 promise
->reject(INVALID_STATE_ERROR
, 0, "CDM has failed.");
190 CdmDelegate()->RemoveSession(session_id
, promise
.Pass());
193 media::CdmContext
* PpapiDecryptor::GetCdmContext() {
197 media::Decryptor
* PpapiDecryptor::GetDecryptor() {
201 int PpapiDecryptor::GetCdmId() const {
202 return kInvalidCdmId
;
205 void PpapiDecryptor::RegisterNewKeyCB(StreamType stream_type
,
206 const NewKeyCB
& new_key_cb
) {
207 if (!render_task_runner_
->BelongsToCurrentThread()) {
208 render_task_runner_
->PostTask(
210 base::Bind(&PpapiDecryptor::RegisterNewKeyCB
,
211 weak_ptr_factory_
.GetWeakPtr(), stream_type
, new_key_cb
));
215 DVLOG(3) << __FUNCTION__
<< " - stream_type: " << stream_type
;
216 switch (stream_type
) {
218 new_audio_key_cb_
= new_key_cb
;
221 new_video_key_cb_
= new_key_cb
;
228 void PpapiDecryptor::Decrypt(
229 StreamType stream_type
,
230 const scoped_refptr
<media::DecoderBuffer
>& encrypted
,
231 const DecryptCB
& decrypt_cb
) {
232 if (!render_task_runner_
->BelongsToCurrentThread()) {
233 render_task_runner_
->PostTask(
235 base::Bind(&PpapiDecryptor::Decrypt
, weak_ptr_factory_
.GetWeakPtr(),
236 stream_type
, encrypted
, decrypt_cb
));
240 DVLOG(3) << __FUNCTION__
<< " - stream_type: " << stream_type
;
241 if (!CdmDelegate() ||
242 !CdmDelegate()->Decrypt(stream_type
, encrypted
, decrypt_cb
)) {
243 decrypt_cb
.Run(kError
, NULL
);
247 void PpapiDecryptor::CancelDecrypt(StreamType stream_type
) {
248 if (!render_task_runner_
->BelongsToCurrentThread()) {
249 render_task_runner_
->PostTask(
250 FROM_HERE
, base::Bind(&PpapiDecryptor::CancelDecrypt
,
251 weak_ptr_factory_
.GetWeakPtr(), stream_type
));
255 DVLOG(1) << __FUNCTION__
<< " - stream_type: " << stream_type
;
257 CdmDelegate()->CancelDecrypt(stream_type
);
260 void PpapiDecryptor::InitializeAudioDecoder(
261 const media::AudioDecoderConfig
& config
,
262 const DecoderInitCB
& init_cb
) {
263 if (!render_task_runner_
->BelongsToCurrentThread()) {
264 render_task_runner_
->PostTask(
265 FROM_HERE
, base::Bind(&PpapiDecryptor::InitializeAudioDecoder
,
266 weak_ptr_factory_
.GetWeakPtr(), config
, init_cb
));
270 DVLOG(2) << __FUNCTION__
;
271 DCHECK(config
.is_encrypted());
272 DCHECK(config
.IsValidConfig());
274 audio_decoder_init_cb_
= init_cb
;
275 if (!CdmDelegate() || !CdmDelegate()->InitializeAudioDecoder(
277 base::Bind(&PpapiDecryptor::OnDecoderInitialized
,
278 weak_ptr_factory_
.GetWeakPtr(),
280 base::ResetAndReturn(&audio_decoder_init_cb_
).Run(false);
285 void PpapiDecryptor::InitializeVideoDecoder(
286 const media::VideoDecoderConfig
& config
,
287 const DecoderInitCB
& init_cb
) {
288 if (!render_task_runner_
->BelongsToCurrentThread()) {
289 render_task_runner_
->PostTask(
290 FROM_HERE
, base::Bind(&PpapiDecryptor::InitializeVideoDecoder
,
291 weak_ptr_factory_
.GetWeakPtr(), config
, init_cb
));
295 DVLOG(2) << __FUNCTION__
;
296 DCHECK(config
.is_encrypted());
297 DCHECK(config
.IsValidConfig());
299 video_decoder_init_cb_
= init_cb
;
300 if (!CdmDelegate() || !CdmDelegate()->InitializeVideoDecoder(
302 base::Bind(&PpapiDecryptor::OnDecoderInitialized
,
303 weak_ptr_factory_
.GetWeakPtr(),
305 base::ResetAndReturn(&video_decoder_init_cb_
).Run(false);
310 void PpapiDecryptor::DecryptAndDecodeAudio(
311 const scoped_refptr
<media::DecoderBuffer
>& encrypted
,
312 const AudioDecodeCB
& audio_decode_cb
) {
313 if (!render_task_runner_
->BelongsToCurrentThread()) {
314 render_task_runner_
->PostTask(
316 base::Bind(&PpapiDecryptor::DecryptAndDecodeAudio
,
317 weak_ptr_factory_
.GetWeakPtr(), encrypted
, audio_decode_cb
));
321 DVLOG(3) << __FUNCTION__
;
322 if (!CdmDelegate() ||
323 !CdmDelegate()->DecryptAndDecodeAudio(encrypted
, audio_decode_cb
)) {
324 audio_decode_cb
.Run(kError
, AudioFrames());
328 void PpapiDecryptor::DecryptAndDecodeVideo(
329 const scoped_refptr
<media::DecoderBuffer
>& encrypted
,
330 const VideoDecodeCB
& video_decode_cb
) {
331 if (!render_task_runner_
->BelongsToCurrentThread()) {
332 render_task_runner_
->PostTask(
334 base::Bind(&PpapiDecryptor::DecryptAndDecodeVideo
,
335 weak_ptr_factory_
.GetWeakPtr(), encrypted
, video_decode_cb
));
339 DVLOG(3) << __FUNCTION__
;
340 if (!CdmDelegate() ||
341 !CdmDelegate()->DecryptAndDecodeVideo(encrypted
, video_decode_cb
)) {
342 video_decode_cb
.Run(kError
, NULL
);
346 void PpapiDecryptor::ResetDecoder(StreamType stream_type
) {
347 if (!render_task_runner_
->BelongsToCurrentThread()) {
348 render_task_runner_
->PostTask(
349 FROM_HERE
, base::Bind(&PpapiDecryptor::ResetDecoder
,
350 weak_ptr_factory_
.GetWeakPtr(), stream_type
));
354 DVLOG(2) << __FUNCTION__
<< " - stream_type: " << stream_type
;
356 CdmDelegate()->ResetDecoder(stream_type
);
359 void PpapiDecryptor::DeinitializeDecoder(StreamType stream_type
) {
360 if (!render_task_runner_
->BelongsToCurrentThread()) {
361 render_task_runner_
->PostTask(
362 FROM_HERE
, base::Bind(&PpapiDecryptor::DeinitializeDecoder
,
363 weak_ptr_factory_
.GetWeakPtr(), stream_type
));
367 DVLOG(2) << __FUNCTION__
<< " - stream_type: " << stream_type
;
369 CdmDelegate()->DeinitializeDecoder(stream_type
);
372 void PpapiDecryptor::OnDecoderInitialized(StreamType stream_type
,
374 DCHECK(render_task_runner_
->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_t>& message
,
392 const GURL
& legacy_destination_url
) {
393 DCHECK(render_task_runner_
->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_task_runner_
->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_task_runner_
->BelongsToCurrentThread());
416 session_expiration_update_cb_
.Run(session_id
, new_expiry_time
);
419 void PpapiDecryptor::OnSessionClosed(const std::string
& session_id
) {
420 DCHECK(render_task_runner_
->BelongsToCurrentThread());
421 session_closed_cb_
.Run(session_id
);
424 void PpapiDecryptor::OnLegacySessionError(
425 const std::string
& session_id
,
426 MediaKeys::Exception exception_code
,
427 uint32_t system_code
,
428 const std::string
& error_description
) {
429 DCHECK(render_task_runner_
->BelongsToCurrentThread());
430 legacy_session_error_cb_
.Run(session_id
, exception_code
, system_code
,
434 void PpapiDecryptor::AttemptToResumePlayback() {
435 if (!new_audio_key_cb_
.is_null())
436 new_audio_key_cb_
.Run();
438 if (!new_video_key_cb_
.is_null())
439 new_video_key_cb_
.Run();
442 void PpapiDecryptor::OnFatalPluginError() {
443 DCHECK(render_task_runner_
->BelongsToCurrentThread());
444 pepper_cdm_wrapper_
.reset();
447 ContentDecryptorDelegate
* PpapiDecryptor::CdmDelegate() {
448 DCHECK(render_task_runner_
->BelongsToCurrentThread());
449 return (pepper_cdm_wrapper_
) ? pepper_cdm_wrapper_
->GetCdmDelegate() : NULL
;
452 } // namespace content