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 "base/trace_event/trace_event.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/cdm_initialized_promise.h"
19 #include "media/base/cdm_key_information.h"
20 #include "media/base/data_buffer.h"
21 #include "media/base/decoder_buffer.h"
22 #include "media/base/key_systems.h"
23 #include "media/base/video_decoder_config.h"
24 #include "media/base/video_frame.h"
28 void PpapiDecryptor::Create(
29 const std::string
& key_system
,
30 const GURL
& security_origin
,
31 bool allow_distinctive_identifier
,
32 bool allow_persistent_state
,
33 const CreatePepperCdmCB
& create_pepper_cdm_cb
,
34 const media::SessionMessageCB
& session_message_cb
,
35 const media::SessionClosedCB
& session_closed_cb
,
36 const media::LegacySessionErrorCB
& legacy_session_error_cb
,
37 const media::SessionKeysChangeCB
& session_keys_change_cb
,
38 const media::SessionExpirationUpdateCB
& session_expiration_update_cb
,
39 const media::CdmCreatedCB
& cdm_created_cb
) {
40 std::string plugin_type
= media::GetPepperType(key_system
);
41 DCHECK(!plugin_type
.empty());
43 scoped_ptr
<PepperCdmWrapper
> pepper_cdm_wrapper
;
45 TRACE_EVENT0("media", "PpapiDecryptor::CreatePepperCDM");
46 pepper_cdm_wrapper
= create_pepper_cdm_cb
.Run(plugin_type
, security_origin
);
49 if (!pepper_cdm_wrapper
) {
51 "Unable to create the CDM for the key system " + key_system
+ ".";
52 DLOG(ERROR
) << message
;
53 base::ThreadTaskRunnerHandle::Get()->PostTask(
54 FROM_HERE
, base::Bind(cdm_created_cb
, nullptr, message
));
58 scoped_ptr
<PpapiDecryptor
> ppapi_decryptor(
59 new PpapiDecryptor(pepper_cdm_wrapper
.Pass(), session_message_cb
,
60 session_closed_cb
, legacy_session_error_cb
,
61 session_keys_change_cb
, session_expiration_update_cb
));
63 // PpapiDecryptor ownership passed to the promise, but keep a copy in order
64 // to call InitializeCdm().
65 PpapiDecryptor
* ppapi_decryptor_copy
= ppapi_decryptor
.get();
66 scoped_ptr
<media::CdmInitializedPromise
> promise(
67 new media::CdmInitializedPromise(cdm_created_cb
, ppapi_decryptor
.Pass()));
68 ppapi_decryptor_copy
->InitializeCdm(key_system
, allow_distinctive_identifier
,
69 allow_persistent_state
, promise
.Pass());
72 PpapiDecryptor::PpapiDecryptor(
73 scoped_ptr
<PepperCdmWrapper
> pepper_cdm_wrapper
,
74 const media::SessionMessageCB
& session_message_cb
,
75 const media::SessionClosedCB
& session_closed_cb
,
76 const media::LegacySessionErrorCB
& legacy_session_error_cb
,
77 const media::SessionKeysChangeCB
& session_keys_change_cb
,
78 const media::SessionExpirationUpdateCB
& session_expiration_update_cb
)
79 : pepper_cdm_wrapper_(pepper_cdm_wrapper
.Pass()),
80 session_message_cb_(session_message_cb
),
81 session_closed_cb_(session_closed_cb
),
82 legacy_session_error_cb_(legacy_session_error_cb
),
83 session_keys_change_cb_(session_keys_change_cb
),
84 session_expiration_update_cb_(session_expiration_update_cb
),
85 render_task_runner_(base::ThreadTaskRunnerHandle::Get()),
86 weak_ptr_factory_(this) {
87 DCHECK(pepper_cdm_wrapper_
.get());
88 DCHECK(!session_message_cb_
.is_null());
89 DCHECK(!session_closed_cb_
.is_null());
90 DCHECK(!legacy_session_error_cb_
.is_null());
91 DCHECK(!session_keys_change_cb
.is_null());
92 DCHECK(!session_expiration_update_cb
.is_null());
95 PpapiDecryptor::~PpapiDecryptor() {
96 pepper_cdm_wrapper_
.reset();
99 void PpapiDecryptor::InitializeCdm(
100 const std::string
& key_system
,
101 bool allow_distinctive_identifier
,
102 bool allow_persistent_state
,
103 scoped_ptr
<media::SimpleCdmPromise
> promise
) {
104 base::WeakPtr
<PpapiDecryptor
> weak_this
= weak_ptr_factory_
.GetWeakPtr();
105 CdmDelegate()->Initialize(
106 key_system
, allow_distinctive_identifier
, allow_persistent_state
,
107 base::Bind(&PpapiDecryptor::OnSessionMessage
, weak_this
),
108 base::Bind(&PpapiDecryptor::OnSessionClosed
, weak_this
),
109 base::Bind(&PpapiDecryptor::OnLegacySessionError
, weak_this
),
110 base::Bind(&PpapiDecryptor::OnSessionKeysChange
, weak_this
),
111 base::Bind(&PpapiDecryptor::OnSessionExpirationUpdate
, weak_this
),
112 base::Bind(&PpapiDecryptor::OnFatalPluginError
, weak_this
),
116 void PpapiDecryptor::SetServerCertificate(
117 const std::vector
<uint8_t>& certificate
,
118 scoped_ptr
<media::SimpleCdmPromise
> promise
) {
119 DVLOG(2) << __FUNCTION__
;
120 DCHECK(render_task_runner_
->BelongsToCurrentThread());
122 if (!CdmDelegate()) {
123 promise
->reject(INVALID_STATE_ERROR
, 0, "CDM has failed.");
127 CdmDelegate()->SetServerCertificate(certificate
, promise
.Pass());
130 void PpapiDecryptor::CreateSessionAndGenerateRequest(
131 SessionType session_type
,
132 media::EmeInitDataType init_data_type
,
133 const std::vector
<uint8_t>& init_data
,
134 scoped_ptr
<media::NewSessionCdmPromise
> promise
) {
135 DVLOG(2) << __FUNCTION__
;
136 DCHECK(render_task_runner_
->BelongsToCurrentThread());
138 if (!CdmDelegate()) {
139 promise
->reject(INVALID_STATE_ERROR
, 0, "CDM has failed.");
143 CdmDelegate()->CreateSessionAndGenerateRequest(session_type
, init_data_type
,
144 init_data
, promise
.Pass());
147 void PpapiDecryptor::LoadSession(
148 SessionType session_type
,
149 const std::string
& session_id
,
150 scoped_ptr
<media::NewSessionCdmPromise
> promise
) {
151 DVLOG(2) << __FUNCTION__
;
152 DCHECK(render_task_runner_
->BelongsToCurrentThread());
154 if (!CdmDelegate()) {
155 promise
->reject(INVALID_STATE_ERROR
, 0, "CDM has failed.");
158 CdmDelegate()->LoadSession(session_type
, session_id
, promise
.Pass());
161 void PpapiDecryptor::UpdateSession(
162 const std::string
& session_id
,
163 const std::vector
<uint8_t>& response
,
164 scoped_ptr
<media::SimpleCdmPromise
> promise
) {
165 DCHECK(render_task_runner_
->BelongsToCurrentThread());
167 if (!CdmDelegate()) {
168 promise
->reject(INVALID_STATE_ERROR
, 0, "CDM has failed.");
171 CdmDelegate()->UpdateSession(session_id
, response
, promise
.Pass());
174 void PpapiDecryptor::CloseSession(const std::string
& session_id
,
175 scoped_ptr
<media::SimpleCdmPromise
> promise
) {
176 DCHECK(render_task_runner_
->BelongsToCurrentThread());
178 if (!CdmDelegate()) {
179 promise
->reject(INVALID_STATE_ERROR
, 0, "CDM has failed.");
183 CdmDelegate()->CloseSession(session_id
, promise
.Pass());
186 void PpapiDecryptor::RemoveSession(
187 const std::string
& session_id
,
188 scoped_ptr
<media::SimpleCdmPromise
> promise
) {
189 DCHECK(render_task_runner_
->BelongsToCurrentThread());
191 if (!CdmDelegate()) {
192 promise
->reject(INVALID_STATE_ERROR
, 0, "CDM has failed.");
196 CdmDelegate()->RemoveSession(session_id
, promise
.Pass());
199 media::CdmContext
* PpapiDecryptor::GetCdmContext() {
203 media::Decryptor
* PpapiDecryptor::GetDecryptor() {
207 int PpapiDecryptor::GetCdmId() const {
208 return kInvalidCdmId
;
211 void PpapiDecryptor::RegisterNewKeyCB(StreamType stream_type
,
212 const NewKeyCB
& new_key_cb
) {
213 if (!render_task_runner_
->BelongsToCurrentThread()) {
214 render_task_runner_
->PostTask(
216 base::Bind(&PpapiDecryptor::RegisterNewKeyCB
,
217 weak_ptr_factory_
.GetWeakPtr(), stream_type
, new_key_cb
));
221 DVLOG(3) << __FUNCTION__
<< " - stream_type: " << stream_type
;
222 switch (stream_type
) {
224 new_audio_key_cb_
= new_key_cb
;
227 new_video_key_cb_
= new_key_cb
;
234 void PpapiDecryptor::Decrypt(
235 StreamType stream_type
,
236 const scoped_refptr
<media::DecoderBuffer
>& encrypted
,
237 const DecryptCB
& decrypt_cb
) {
238 if (!render_task_runner_
->BelongsToCurrentThread()) {
239 render_task_runner_
->PostTask(
241 base::Bind(&PpapiDecryptor::Decrypt
, weak_ptr_factory_
.GetWeakPtr(),
242 stream_type
, encrypted
, decrypt_cb
));
246 DVLOG(3) << __FUNCTION__
<< " - stream_type: " << stream_type
;
247 if (!CdmDelegate() ||
248 !CdmDelegate()->Decrypt(stream_type
, encrypted
, decrypt_cb
)) {
249 decrypt_cb
.Run(kError
, NULL
);
253 void PpapiDecryptor::CancelDecrypt(StreamType stream_type
) {
254 if (!render_task_runner_
->BelongsToCurrentThread()) {
255 render_task_runner_
->PostTask(
256 FROM_HERE
, base::Bind(&PpapiDecryptor::CancelDecrypt
,
257 weak_ptr_factory_
.GetWeakPtr(), stream_type
));
261 DVLOG(1) << __FUNCTION__
<< " - stream_type: " << stream_type
;
263 CdmDelegate()->CancelDecrypt(stream_type
);
266 void PpapiDecryptor::InitializeAudioDecoder(
267 const media::AudioDecoderConfig
& config
,
268 const DecoderInitCB
& init_cb
) {
269 if (!render_task_runner_
->BelongsToCurrentThread()) {
270 render_task_runner_
->PostTask(
271 FROM_HERE
, base::Bind(&PpapiDecryptor::InitializeAudioDecoder
,
272 weak_ptr_factory_
.GetWeakPtr(), config
, init_cb
));
276 DVLOG(2) << __FUNCTION__
;
277 DCHECK(config
.is_encrypted());
278 DCHECK(config
.IsValidConfig());
280 audio_decoder_init_cb_
= init_cb
;
281 if (!CdmDelegate() || !CdmDelegate()->InitializeAudioDecoder(
283 base::Bind(&PpapiDecryptor::OnDecoderInitialized
,
284 weak_ptr_factory_
.GetWeakPtr(),
286 base::ResetAndReturn(&audio_decoder_init_cb_
).Run(false);
291 void PpapiDecryptor::InitializeVideoDecoder(
292 const media::VideoDecoderConfig
& config
,
293 const DecoderInitCB
& init_cb
) {
294 if (!render_task_runner_
->BelongsToCurrentThread()) {
295 render_task_runner_
->PostTask(
296 FROM_HERE
, base::Bind(&PpapiDecryptor::InitializeVideoDecoder
,
297 weak_ptr_factory_
.GetWeakPtr(), config
, init_cb
));
301 DVLOG(2) << __FUNCTION__
;
302 DCHECK(config
.is_encrypted());
303 DCHECK(config
.IsValidConfig());
305 video_decoder_init_cb_
= init_cb
;
306 if (!CdmDelegate() || !CdmDelegate()->InitializeVideoDecoder(
308 base::Bind(&PpapiDecryptor::OnDecoderInitialized
,
309 weak_ptr_factory_
.GetWeakPtr(),
311 base::ResetAndReturn(&video_decoder_init_cb_
).Run(false);
316 void PpapiDecryptor::DecryptAndDecodeAudio(
317 const scoped_refptr
<media::DecoderBuffer
>& encrypted
,
318 const AudioDecodeCB
& audio_decode_cb
) {
319 if (!render_task_runner_
->BelongsToCurrentThread()) {
320 render_task_runner_
->PostTask(
322 base::Bind(&PpapiDecryptor::DecryptAndDecodeAudio
,
323 weak_ptr_factory_
.GetWeakPtr(), encrypted
, audio_decode_cb
));
327 DVLOG(3) << __FUNCTION__
;
328 if (!CdmDelegate() ||
329 !CdmDelegate()->DecryptAndDecodeAudio(encrypted
, audio_decode_cb
)) {
330 audio_decode_cb
.Run(kError
, AudioFrames());
334 void PpapiDecryptor::DecryptAndDecodeVideo(
335 const scoped_refptr
<media::DecoderBuffer
>& encrypted
,
336 const VideoDecodeCB
& video_decode_cb
) {
337 if (!render_task_runner_
->BelongsToCurrentThread()) {
338 render_task_runner_
->PostTask(
340 base::Bind(&PpapiDecryptor::DecryptAndDecodeVideo
,
341 weak_ptr_factory_
.GetWeakPtr(), encrypted
, video_decode_cb
));
345 DVLOG(3) << __FUNCTION__
;
346 if (!CdmDelegate() ||
347 !CdmDelegate()->DecryptAndDecodeVideo(encrypted
, video_decode_cb
)) {
348 video_decode_cb
.Run(kError
, NULL
);
352 void PpapiDecryptor::ResetDecoder(StreamType stream_type
) {
353 if (!render_task_runner_
->BelongsToCurrentThread()) {
354 render_task_runner_
->PostTask(
355 FROM_HERE
, base::Bind(&PpapiDecryptor::ResetDecoder
,
356 weak_ptr_factory_
.GetWeakPtr(), stream_type
));
360 DVLOG(2) << __FUNCTION__
<< " - stream_type: " << stream_type
;
362 CdmDelegate()->ResetDecoder(stream_type
);
365 void PpapiDecryptor::DeinitializeDecoder(StreamType stream_type
) {
366 if (!render_task_runner_
->BelongsToCurrentThread()) {
367 render_task_runner_
->PostTask(
368 FROM_HERE
, base::Bind(&PpapiDecryptor::DeinitializeDecoder
,
369 weak_ptr_factory_
.GetWeakPtr(), stream_type
));
373 DVLOG(2) << __FUNCTION__
<< " - stream_type: " << stream_type
;
375 CdmDelegate()->DeinitializeDecoder(stream_type
);
378 void PpapiDecryptor::OnDecoderInitialized(StreamType stream_type
,
380 DCHECK(render_task_runner_
->BelongsToCurrentThread());
381 switch (stream_type
) {
383 DCHECK(!audio_decoder_init_cb_
.is_null());
384 base::ResetAndReturn(&audio_decoder_init_cb_
).Run(success
);
387 DCHECK(!video_decoder_init_cb_
.is_null());
388 base::ResetAndReturn(&video_decoder_init_cb_
).Run(success
);
395 void PpapiDecryptor::OnSessionMessage(const std::string
& session_id
,
396 MessageType message_type
,
397 const std::vector
<uint8_t>& message
,
398 const GURL
& legacy_destination_url
) {
399 DCHECK(render_task_runner_
->BelongsToCurrentThread());
400 session_message_cb_
.Run(session_id
, message_type
, message
,
401 legacy_destination_url
);
404 void PpapiDecryptor::OnSessionKeysChange(const std::string
& session_id
,
405 bool has_additional_usable_key
,
406 media::CdmKeysInfo keys_info
) {
407 DCHECK(render_task_runner_
->BelongsToCurrentThread());
409 // TODO(jrummell): Handling resume playback should be done in the media
410 // player, not in the Decryptors. http://crbug.com/413413.
411 if (has_additional_usable_key
)
412 AttemptToResumePlayback();
414 session_keys_change_cb_
.Run(session_id
, has_additional_usable_key
,
418 void PpapiDecryptor::OnSessionExpirationUpdate(
419 const std::string
& session_id
,
420 const base::Time
& new_expiry_time
) {
421 DCHECK(render_task_runner_
->BelongsToCurrentThread());
422 session_expiration_update_cb_
.Run(session_id
, new_expiry_time
);
425 void PpapiDecryptor::OnSessionClosed(const std::string
& session_id
) {
426 DCHECK(render_task_runner_
->BelongsToCurrentThread());
427 session_closed_cb_
.Run(session_id
);
430 void PpapiDecryptor::OnLegacySessionError(
431 const std::string
& session_id
,
432 MediaKeys::Exception exception_code
,
433 uint32_t system_code
,
434 const std::string
& error_description
) {
435 DCHECK(render_task_runner_
->BelongsToCurrentThread());
436 legacy_session_error_cb_
.Run(session_id
, exception_code
, system_code
,
440 void PpapiDecryptor::AttemptToResumePlayback() {
441 if (!new_audio_key_cb_
.is_null())
442 new_audio_key_cb_
.Run();
444 if (!new_video_key_cb_
.is_null())
445 new_video_key_cb_
.Run();
448 void PpapiDecryptor::OnFatalPluginError() {
449 DCHECK(render_task_runner_
->BelongsToCurrentThread());
450 pepper_cdm_wrapper_
.reset();
453 ContentDecryptorDelegate
* PpapiDecryptor::CdmDelegate() {
454 DCHECK(render_task_runner_
->BelongsToCurrentThread());
455 return (pepper_cdm_wrapper_
) ? pepper_cdm_wrapper_
->GetCdmDelegate() : NULL
;
458 } // namespace content