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.
4
5 #include "content/renderer/media/crypto/ppapi_decryptor.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/callback_helpers.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/message_loop/message_loop_proxy.h"
16 #include "content/renderer/media/crypto/key_systems.h"
17 #include "content/renderer/pepper/content_decryptor_delegate.h"
18 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
19 #include "media/base/audio_decoder_config.h"
20 #include "media/base/cdm_promise.h"
21 #include "media/base/data_buffer.h"
22 #include "media/base/decoder_buffer.h"
23 #include "media/base/video_decoder_config.h"
24 #include "media/base/video_frame.h"
25
26 namespace content {
27
28 // This class is needed so that resolving an Update() promise triggers playback
29 // of the stream. It intercepts the resolve() call to invoke an additional
30 // callback.
31 class SessionUpdatedPromise : public media::SimpleCdmPromise {
32 public:
SessionUpdatedPromise(scoped_ptr<media::SimpleCdmPromise> caller_promise,base::Closure additional_resolve_cb)33 SessionUpdatedPromise(scoped_ptr<media::SimpleCdmPromise> caller_promise,
34 base::Closure additional_resolve_cb)
35 : caller_promise_(caller_promise.Pass()),
36 additional_resolve_cb_(additional_resolve_cb) {}
37
resolve()38 virtual void resolve() OVERRIDE {
39 DCHECK(is_pending_);
40 is_pending_ = false;
41 additional_resolve_cb_.Run();
42 caller_promise_->resolve();
43 }
44
reject(media::MediaKeys::Exception exception_code,uint32 system_code,const std::string & error_message)45 virtual void reject(media::MediaKeys::Exception exception_code,
46 uint32 system_code,
47 const std::string& error_message) OVERRIDE {
48 DCHECK(is_pending_);
49 is_pending_ = false;
50 caller_promise_->reject(exception_code, system_code, error_message);
51 }
52
53 protected:
54 scoped_ptr<media::SimpleCdmPromise> caller_promise_;
55 base::Closure additional_resolve_cb_;
56 };
57
Create(const std::string & key_system,const GURL & security_origin,const CreatePepperCdmCB & create_pepper_cdm_cb,const media::SessionMessageCB & session_message_cb,const media::SessionReadyCB & session_ready_cb,const media::SessionClosedCB & session_closed_cb,const media::SessionErrorCB & session_error_cb)58 scoped_ptr<PpapiDecryptor> PpapiDecryptor::Create(
59 const std::string& key_system,
60 const GURL& security_origin,
61 const CreatePepperCdmCB& create_pepper_cdm_cb,
62 const media::SessionMessageCB& session_message_cb,
63 const media::SessionReadyCB& session_ready_cb,
64 const media::SessionClosedCB& session_closed_cb,
65 const media::SessionErrorCB& session_error_cb) {
66 std::string plugin_type = GetPepperType(key_system);
67 DCHECK(!plugin_type.empty());
68 scoped_ptr<PepperCdmWrapper> pepper_cdm_wrapper =
69 create_pepper_cdm_cb.Run(plugin_type, security_origin);
70 if (!pepper_cdm_wrapper) {
71 DLOG(ERROR) << "Plugin instance creation failed.";
72 return scoped_ptr<PpapiDecryptor>();
73 }
74
75 return scoped_ptr<PpapiDecryptor>(
76 new PpapiDecryptor(key_system,
77 pepper_cdm_wrapper.Pass(),
78 session_message_cb,
79 session_ready_cb,
80 session_closed_cb,
81 session_error_cb));
82 }
83
PpapiDecryptor(const std::string & key_system,scoped_ptr<PepperCdmWrapper> pepper_cdm_wrapper,const media::SessionMessageCB & session_message_cb,const media::SessionReadyCB & session_ready_cb,const media::SessionClosedCB & session_closed_cb,const media::SessionErrorCB & session_error_cb)84 PpapiDecryptor::PpapiDecryptor(
85 const std::string& key_system,
86 scoped_ptr<PepperCdmWrapper> pepper_cdm_wrapper,
87 const media::SessionMessageCB& session_message_cb,
88 const media::SessionReadyCB& session_ready_cb,
89 const media::SessionClosedCB& session_closed_cb,
90 const media::SessionErrorCB& session_error_cb)
91 : pepper_cdm_wrapper_(pepper_cdm_wrapper.Pass()),
92 session_message_cb_(session_message_cb),
93 session_ready_cb_(session_ready_cb),
94 session_closed_cb_(session_closed_cb),
95 session_error_cb_(session_error_cb),
96 render_loop_proxy_(base::MessageLoopProxy::current()),
97 weak_ptr_factory_(this) {
98 DCHECK(pepper_cdm_wrapper_.get());
99 DCHECK(!session_message_cb_.is_null());
100 DCHECK(!session_ready_cb_.is_null());
101 DCHECK(!session_closed_cb_.is_null());
102 DCHECK(!session_error_cb_.is_null());
103
104 base::WeakPtr<PpapiDecryptor> weak_this = weak_ptr_factory_.GetWeakPtr();
105 CdmDelegate()->Initialize(
106 key_system,
107 base::Bind(&PpapiDecryptor::OnSessionMessage, weak_this),
108 base::Bind(&PpapiDecryptor::OnSessionReady, weak_this),
109 base::Bind(&PpapiDecryptor::OnSessionClosed, weak_this),
110 base::Bind(&PpapiDecryptor::OnSessionError, weak_this),
111 base::Bind(&PpapiDecryptor::OnFatalPluginError, weak_this));
112 }
113
~PpapiDecryptor()114 PpapiDecryptor::~PpapiDecryptor() {
115 pepper_cdm_wrapper_.reset();
116 }
117
CreateSession(const std::string & init_data_type,const uint8 * init_data,int init_data_length,SessionType session_type,scoped_ptr<media::NewSessionCdmPromise> promise)118 void PpapiDecryptor::CreateSession(
119 const std::string& init_data_type,
120 const uint8* init_data,
121 int init_data_length,
122 SessionType session_type,
123 scoped_ptr<media::NewSessionCdmPromise> promise) {
124 DVLOG(2) << __FUNCTION__;
125 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
126
127 if (!CdmDelegate()) {
128 promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
129 return;
130 }
131
132 CdmDelegate()->CreateSession(init_data_type,
133 init_data,
134 init_data_length,
135 session_type,
136 promise.Pass());
137 }
138
LoadSession(const std::string & web_session_id,scoped_ptr<media::NewSessionCdmPromise> promise)139 void PpapiDecryptor::LoadSession(
140 const std::string& web_session_id,
141 scoped_ptr<media::NewSessionCdmPromise> promise) {
142 DVLOG(2) << __FUNCTION__;
143 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
144
145 if (!CdmDelegate()) {
146 promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
147 return;
148 }
149
150 CdmDelegate()->LoadSession(web_session_id, promise.Pass());
151 }
152
UpdateSession(const std::string & web_session_id,const uint8 * response,int response_length,scoped_ptr<media::SimpleCdmPromise> promise)153 void PpapiDecryptor::UpdateSession(
154 const std::string& web_session_id,
155 const uint8* response,
156 int response_length,
157 scoped_ptr<media::SimpleCdmPromise> promise) {
158 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
159
160 if (!CdmDelegate()) {
161 promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
162 return;
163 }
164
165 scoped_ptr<SessionUpdatedPromise> session_updated_promise(
166 new SessionUpdatedPromise(promise.Pass(),
167 base::Bind(&PpapiDecryptor::ResumePlayback,
168 weak_ptr_factory_.GetWeakPtr())));
169 CdmDelegate()->UpdateSession(
170 web_session_id,
171 response,
172 response_length,
173 session_updated_promise.PassAs<media::SimpleCdmPromise>());
174 }
175
ReleaseSession(const std::string & web_session_id,scoped_ptr<media::SimpleCdmPromise> promise)176 void PpapiDecryptor::ReleaseSession(
177 const std::string& web_session_id,
178 scoped_ptr<media::SimpleCdmPromise> promise) {
179 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
180
181 if (!CdmDelegate()) {
182 promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
183 return;
184 }
185
186 CdmDelegate()->ReleaseSession(web_session_id, promise.Pass());
187 }
188
GetDecryptor()189 media::Decryptor* PpapiDecryptor::GetDecryptor() {
190 return this;
191 }
192
RegisterNewKeyCB(StreamType stream_type,const NewKeyCB & new_key_cb)193 void PpapiDecryptor::RegisterNewKeyCB(StreamType stream_type,
194 const NewKeyCB& new_key_cb) {
195 if (!render_loop_proxy_->BelongsToCurrentThread()) {
196 render_loop_proxy_->PostTask(FROM_HERE,
197 base::Bind(&PpapiDecryptor::RegisterNewKeyCB,
198 weak_ptr_factory_.GetWeakPtr(),
199 stream_type,
200 new_key_cb));
201 return;
202 }
203
204 DVLOG(3) << __FUNCTION__ << " - stream_type: " << stream_type;
205 switch (stream_type) {
206 case kAudio:
207 new_audio_key_cb_ = new_key_cb;
208 break;
209 case kVideo:
210 new_video_key_cb_ = new_key_cb;
211 break;
212 default:
213 NOTREACHED();
214 }
215 }
216
Decrypt(StreamType stream_type,const scoped_refptr<media::DecoderBuffer> & encrypted,const DecryptCB & decrypt_cb)217 void PpapiDecryptor::Decrypt(
218 StreamType stream_type,
219 const scoped_refptr<media::DecoderBuffer>& encrypted,
220 const DecryptCB& decrypt_cb) {
221 if (!render_loop_proxy_->BelongsToCurrentThread()) {
222 render_loop_proxy_->PostTask(FROM_HERE,
223 base::Bind(&PpapiDecryptor::Decrypt,
224 weak_ptr_factory_.GetWeakPtr(),
225 stream_type,
226 encrypted,
227 decrypt_cb));
228 return;
229 }
230
231 DVLOG(3) << __FUNCTION__ << " - stream_type: " << stream_type;
232 if (!CdmDelegate() ||
233 !CdmDelegate()->Decrypt(stream_type, encrypted, decrypt_cb)) {
234 decrypt_cb.Run(kError, NULL);
235 }
236 }
237
CancelDecrypt(StreamType stream_type)238 void PpapiDecryptor::CancelDecrypt(StreamType stream_type) {
239 if (!render_loop_proxy_->BelongsToCurrentThread()) {
240 render_loop_proxy_->PostTask(FROM_HERE,
241 base::Bind(&PpapiDecryptor::CancelDecrypt,
242 weak_ptr_factory_.GetWeakPtr(),
243 stream_type));
244 return;
245 }
246
247 DVLOG(1) << __FUNCTION__ << " - stream_type: " << stream_type;
248 if (CdmDelegate())
249 CdmDelegate()->CancelDecrypt(stream_type);
250 }
251
InitializeAudioDecoder(const media::AudioDecoderConfig & config,const DecoderInitCB & init_cb)252 void PpapiDecryptor::InitializeAudioDecoder(
253 const media::AudioDecoderConfig& config,
254 const DecoderInitCB& init_cb) {
255 if (!render_loop_proxy_->BelongsToCurrentThread()) {
256 render_loop_proxy_->PostTask(
257 FROM_HERE,
258 base::Bind(&PpapiDecryptor::InitializeAudioDecoder,
259 weak_ptr_factory_.GetWeakPtr(),
260 config,
261 init_cb));
262 return;
263 }
264
265 DVLOG(2) << __FUNCTION__;
266 DCHECK(config.is_encrypted());
267 DCHECK(config.IsValidConfig());
268
269 audio_decoder_init_cb_ = init_cb;
270 if (!CdmDelegate() || !CdmDelegate()->InitializeAudioDecoder(
271 config,
272 base::Bind(&PpapiDecryptor::OnDecoderInitialized,
273 weak_ptr_factory_.GetWeakPtr(),
274 kAudio))) {
275 base::ResetAndReturn(&audio_decoder_init_cb_).Run(false);
276 return;
277 }
278 }
279
InitializeVideoDecoder(const media::VideoDecoderConfig & config,const DecoderInitCB & init_cb)280 void PpapiDecryptor::InitializeVideoDecoder(
281 const media::VideoDecoderConfig& config,
282 const DecoderInitCB& init_cb) {
283 if (!render_loop_proxy_->BelongsToCurrentThread()) {
284 render_loop_proxy_->PostTask(
285 FROM_HERE,
286 base::Bind(&PpapiDecryptor::InitializeVideoDecoder,
287 weak_ptr_factory_.GetWeakPtr(),
288 config,
289 init_cb));
290 return;
291 }
292
293 DVLOG(2) << __FUNCTION__;
294 DCHECK(config.is_encrypted());
295 DCHECK(config.IsValidConfig());
296
297 video_decoder_init_cb_ = init_cb;
298 if (!CdmDelegate() || !CdmDelegate()->InitializeVideoDecoder(
299 config,
300 base::Bind(&PpapiDecryptor::OnDecoderInitialized,
301 weak_ptr_factory_.GetWeakPtr(),
302 kVideo))) {
303 base::ResetAndReturn(&video_decoder_init_cb_).Run(false);
304 return;
305 }
306 }
307
DecryptAndDecodeAudio(const scoped_refptr<media::DecoderBuffer> & encrypted,const AudioDecodeCB & audio_decode_cb)308 void PpapiDecryptor::DecryptAndDecodeAudio(
309 const scoped_refptr<media::DecoderBuffer>& encrypted,
310 const AudioDecodeCB& audio_decode_cb) {
311 if (!render_loop_proxy_->BelongsToCurrentThread()) {
312 render_loop_proxy_->PostTask(
313 FROM_HERE,
314 base::Bind(&PpapiDecryptor::DecryptAndDecodeAudio,
315 weak_ptr_factory_.GetWeakPtr(),
316 encrypted,
317 audio_decode_cb));
318 return;
319 }
320
321 DVLOG(3) << __FUNCTION__;
322 if (!CdmDelegate() ||
323 !CdmDelegate()->DecryptAndDecodeAudio(encrypted, audio_decode_cb)) {
324 audio_decode_cb.Run(kError, AudioBuffers());
325 }
326 }
327
DecryptAndDecodeVideo(const scoped_refptr<media::DecoderBuffer> & encrypted,const VideoDecodeCB & video_decode_cb)328 void PpapiDecryptor::DecryptAndDecodeVideo(
329 const scoped_refptr<media::DecoderBuffer>& encrypted,
330 const VideoDecodeCB& video_decode_cb) {
331 if (!render_loop_proxy_->BelongsToCurrentThread()) {
332 render_loop_proxy_->PostTask(
333 FROM_HERE,
334 base::Bind(&PpapiDecryptor::DecryptAndDecodeVideo,
335 weak_ptr_factory_.GetWeakPtr(),
336 encrypted,
337 video_decode_cb));
338 return;
339 }
340
341 DVLOG(3) << __FUNCTION__;
342 if (!CdmDelegate() ||
343 !CdmDelegate()->DecryptAndDecodeVideo(encrypted, video_decode_cb)) {
344 video_decode_cb.Run(kError, NULL);
345 }
346 }
347
ResetDecoder(StreamType stream_type)348 void PpapiDecryptor::ResetDecoder(StreamType stream_type) {
349 if (!render_loop_proxy_->BelongsToCurrentThread()) {
350 render_loop_proxy_->PostTask(FROM_HERE,
351 base::Bind(&PpapiDecryptor::ResetDecoder,
352 weak_ptr_factory_.GetWeakPtr(),
353 stream_type));
354 return;
355 }
356
357 DVLOG(2) << __FUNCTION__ << " - stream_type: " << stream_type;
358 if (CdmDelegate())
359 CdmDelegate()->ResetDecoder(stream_type);
360 }
361
DeinitializeDecoder(StreamType stream_type)362 void PpapiDecryptor::DeinitializeDecoder(StreamType stream_type) {
363 if (!render_loop_proxy_->BelongsToCurrentThread()) {
364 render_loop_proxy_->PostTask(
365 FROM_HERE,
366 base::Bind(&PpapiDecryptor::DeinitializeDecoder,
367 weak_ptr_factory_.GetWeakPtr(),
368 stream_type));
369 return;
370 }
371
372 DVLOG(2) << __FUNCTION__ << " - stream_type: " << stream_type;
373 if (CdmDelegate())
374 CdmDelegate()->DeinitializeDecoder(stream_type);
375 }
376
OnDecoderInitialized(StreamType stream_type,bool success)377 void PpapiDecryptor::OnDecoderInitialized(StreamType stream_type,
378 bool success) {
379 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
380 switch (stream_type) {
381 case kAudio:
382 DCHECK(!audio_decoder_init_cb_.is_null());
383 base::ResetAndReturn(&audio_decoder_init_cb_).Run(success);
384 break;
385 case kVideo:
386 DCHECK(!video_decoder_init_cb_.is_null());
387 base::ResetAndReturn(&video_decoder_init_cb_).Run(success);
388 break;
389 default:
390 NOTREACHED();
391 }
392 }
393
OnSessionMessage(const std::string & web_session_id,const std::vector<uint8> & message,const GURL & destination_url)394 void PpapiDecryptor::OnSessionMessage(const std::string& web_session_id,
395 const std::vector<uint8>& message,
396 const GURL& destination_url) {
397 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
398 session_message_cb_.Run(web_session_id, message, destination_url);
399 }
400
OnSessionReady(const std::string & web_session_id)401 void PpapiDecryptor::OnSessionReady(const std::string& web_session_id) {
402 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
403
404 ResumePlayback();
405 session_ready_cb_.Run(web_session_id);
406 }
407
OnSessionClosed(const std::string & web_session_id)408 void PpapiDecryptor::OnSessionClosed(const std::string& web_session_id) {
409 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
410 session_closed_cb_.Run(web_session_id);
411 }
412
OnSessionError(const std::string & web_session_id,MediaKeys::Exception exception_code,uint32 system_code,const std::string & error_description)413 void PpapiDecryptor::OnSessionError(const std::string& web_session_id,
414 MediaKeys::Exception exception_code,
415 uint32 system_code,
416 const std::string& error_description) {
417 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
418 session_error_cb_.Run(
419 web_session_id, exception_code, system_code, error_description);
420 }
421
ResumePlayback()422 void PpapiDecryptor::ResumePlayback() {
423 // Based on the spec, we need to resume playback when update() completes
424 // successfully, or when a session is successfully loaded (triggered by
425 // OnSessionReady()). So we choose to call the NewKeyCBs here.
426 if (!new_audio_key_cb_.is_null())
427 new_audio_key_cb_.Run();
428
429 if (!new_video_key_cb_.is_null())
430 new_video_key_cb_.Run();
431 }
432
OnFatalPluginError()433 void PpapiDecryptor::OnFatalPluginError() {
434 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
435 pepper_cdm_wrapper_.reset();
436 }
437
CdmDelegate()438 ContentDecryptorDelegate* PpapiDecryptor::CdmDelegate() {
439 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
440 return (pepper_cdm_wrapper_) ? pepper_cdm_wrapper_->GetCdmDelegate() : NULL;
441 }
442
443 } // namespace content
444