Home | History | Annotate | Download | only in crypto
      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:
     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 
     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 
     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 
    114 PpapiDecryptor::~PpapiDecryptor() {
    115   pepper_cdm_wrapper_.reset();
    116 }
    117 
    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 
    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 
    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 
    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 
    189 media::Decryptor* PpapiDecryptor::GetDecryptor() {
    190   return this;
    191 }
    192 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    433 void PpapiDecryptor::OnFatalPluginError() {
    434   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
    435   pepper_cdm_wrapper_.reset();
    436 }
    437 
    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