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
26namespace 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.
31class SessionUpdatedPromise : public media::SimpleCdmPromise {
32 public:
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
38  virtual void resolve() OVERRIDE {
39    DCHECK(is_pending_);
40    is_pending_ = false;
41    additional_resolve_cb_.Run();
42    caller_promise_->resolve();
43  }
44
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
58scoped_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
84PpapiDecryptor::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
114PpapiDecryptor::~PpapiDecryptor() {
115  pepper_cdm_wrapper_.reset();
116}
117
118void 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
139void 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
153void 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
176void 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
189media::Decryptor* PpapiDecryptor::GetDecryptor() {
190  return this;
191}
192
193void 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
217void 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
238void 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
252void 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
280void 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
308void 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
328void 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
348void 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
362void 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
377void 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
394void 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
401void 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
408void PpapiDecryptor::OnSessionClosed(const std::string& web_session_id) {
409  DCHECK(render_loop_proxy_->BelongsToCurrentThread());
410  session_closed_cb_.Run(web_session_id);
411}
412
413void 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
422void 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
433void PpapiDecryptor::OnFatalPluginError() {
434  DCHECK(render_loop_proxy_->BelongsToCurrentThread());
435  pepper_cdm_wrapper_.reset();
436}
437
438ContentDecryptorDelegate* PpapiDecryptor::CdmDelegate() {
439  DCHECK(render_loop_proxy_->BelongsToCurrentThread());
440  return (pepper_cdm_wrapper_) ? pepper_cdm_wrapper_->GetCdmDelegate() : NULL;
441}
442
443}  // namespace content
444