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 // This class is needed so that resolving a SessionLoaded() promise triggers
     59 // playback of the stream. It intercepts the resolve() call to invoke an
     60 // additional callback. This is only needed until KeysChange event gets passed
     61 // through Pepper.
     62 class SessionLoadedPromise : public media::NewSessionCdmPromise {
     63  public:
     64   SessionLoadedPromise(scoped_ptr<media::NewSessionCdmPromise> caller_promise,
     65                        base::Closure additional_resolve_cb)
     66       : caller_promise_(caller_promise.Pass()),
     67         additional_resolve_cb_(additional_resolve_cb) {}
     68 
     69   virtual void resolve(const std::string& web_session_id) OVERRIDE {
     70     DCHECK(is_pending_);
     71     is_pending_ = false;
     72     additional_resolve_cb_.Run();
     73     caller_promise_->resolve(web_session_id);
     74   }
     75 
     76   virtual void reject(media::MediaKeys::Exception exception_code,
     77                       uint32 system_code,
     78                       const std::string& error_message) OVERRIDE {
     79     DCHECK(is_pending_);
     80     is_pending_ = false;
     81     caller_promise_->reject(exception_code, system_code, error_message);
     82   }
     83 
     84  protected:
     85   scoped_ptr<media::NewSessionCdmPromise> caller_promise_;
     86   base::Closure additional_resolve_cb_;
     87 };
     88 
     89 scoped_ptr<PpapiDecryptor> PpapiDecryptor::Create(
     90     const std::string& key_system,
     91     const GURL& security_origin,
     92     const CreatePepperCdmCB& create_pepper_cdm_cb,
     93     const media::SessionMessageCB& session_message_cb,
     94     const media::SessionReadyCB& session_ready_cb,
     95     const media::SessionClosedCB& session_closed_cb,
     96     const media::SessionErrorCB& session_error_cb,
     97     const media::SessionKeysChangeCB& session_keys_change_cb,
     98     const media::SessionExpirationUpdateCB& session_expiration_update_cb) {
     99   std::string plugin_type = GetPepperType(key_system);
    100   DCHECK(!plugin_type.empty());
    101   scoped_ptr<PepperCdmWrapper> pepper_cdm_wrapper =
    102       create_pepper_cdm_cb.Run(plugin_type, security_origin);
    103   if (!pepper_cdm_wrapper) {
    104     DLOG(ERROR) << "Plugin instance creation failed.";
    105     return scoped_ptr<PpapiDecryptor>();
    106   }
    107 
    108   return scoped_ptr<PpapiDecryptor>(
    109       new PpapiDecryptor(key_system,
    110                          pepper_cdm_wrapper.Pass(),
    111                          session_message_cb,
    112                          session_ready_cb,
    113                          session_closed_cb,
    114                          session_error_cb,
    115                          session_keys_change_cb,
    116                          session_expiration_update_cb));
    117 }
    118 
    119 PpapiDecryptor::PpapiDecryptor(
    120     const std::string& key_system,
    121     scoped_ptr<PepperCdmWrapper> pepper_cdm_wrapper,
    122     const media::SessionMessageCB& session_message_cb,
    123     const media::SessionReadyCB& session_ready_cb,
    124     const media::SessionClosedCB& session_closed_cb,
    125     const media::SessionErrorCB& session_error_cb,
    126     const media::SessionKeysChangeCB& session_keys_change_cb,
    127     const media::SessionExpirationUpdateCB& session_expiration_update_cb)
    128     : pepper_cdm_wrapper_(pepper_cdm_wrapper.Pass()),
    129       session_message_cb_(session_message_cb),
    130       session_ready_cb_(session_ready_cb),
    131       session_closed_cb_(session_closed_cb),
    132       session_error_cb_(session_error_cb),
    133       session_keys_change_cb_(session_keys_change_cb),
    134       session_expiration_update_cb_(session_expiration_update_cb),
    135       render_loop_proxy_(base::MessageLoopProxy::current()),
    136       weak_ptr_factory_(this) {
    137   DCHECK(pepper_cdm_wrapper_.get());
    138   DCHECK(!session_message_cb_.is_null());
    139   DCHECK(!session_ready_cb_.is_null());
    140   DCHECK(!session_closed_cb_.is_null());
    141   DCHECK(!session_error_cb_.is_null());
    142   DCHECK(!session_keys_change_cb.is_null());
    143   DCHECK(!session_expiration_update_cb.is_null());
    144 
    145   base::WeakPtr<PpapiDecryptor> weak_this = weak_ptr_factory_.GetWeakPtr();
    146   CdmDelegate()->Initialize(
    147       key_system,
    148       base::Bind(&PpapiDecryptor::OnSessionMessage, weak_this),
    149       base::Bind(&PpapiDecryptor::OnSessionReady, weak_this),
    150       base::Bind(&PpapiDecryptor::OnSessionClosed, weak_this),
    151       base::Bind(&PpapiDecryptor::OnSessionError, weak_this),
    152       base::Bind(&PpapiDecryptor::OnSessionKeysChange, weak_this),
    153       base::Bind(&PpapiDecryptor::OnSessionExpirationUpdate, weak_this),
    154       base::Bind(&PpapiDecryptor::OnFatalPluginError, weak_this));
    155 }
    156 
    157 PpapiDecryptor::~PpapiDecryptor() {
    158   pepper_cdm_wrapper_.reset();
    159 }
    160 
    161 void PpapiDecryptor::SetServerCertificate(
    162     const uint8* certificate_data,
    163     int certificate_data_length,
    164     scoped_ptr<media::SimpleCdmPromise> promise) {
    165   DVLOG(2) << __FUNCTION__;
    166   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
    167 
    168   if (!CdmDelegate()) {
    169     promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
    170     return;
    171   }
    172 
    173   CdmDelegate()->SetServerCertificate(
    174       certificate_data, certificate_data_length, promise.Pass());
    175 }
    176 
    177 void PpapiDecryptor::CreateSession(
    178     const std::string& init_data_type,
    179     const uint8* init_data,
    180     int init_data_length,
    181     SessionType session_type,
    182     scoped_ptr<media::NewSessionCdmPromise> promise) {
    183   DVLOG(2) << __FUNCTION__;
    184   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
    185 
    186   if (!CdmDelegate()) {
    187     promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
    188     return;
    189   }
    190 
    191   CdmDelegate()->CreateSession(init_data_type,
    192                                init_data,
    193                                init_data_length,
    194                                session_type,
    195                                promise.Pass());
    196 }
    197 
    198 void PpapiDecryptor::LoadSession(
    199     const std::string& web_session_id,
    200     scoped_ptr<media::NewSessionCdmPromise> promise) {
    201   DVLOG(2) << __FUNCTION__;
    202   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
    203 
    204   if (!CdmDelegate()) {
    205     promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
    206     return;
    207   }
    208 
    209   // TODO(jrummell): Intercepting the promise should not be necessary once
    210   // OnSessionKeysChange() is called in all cases. http://crbug.com/413413.
    211   scoped_ptr<SessionLoadedPromise> session_loaded_promise(
    212       new SessionLoadedPromise(promise.Pass(),
    213                                base::Bind(&PpapiDecryptor::ResumePlayback,
    214                                           weak_ptr_factory_.GetWeakPtr())));
    215 
    216   CdmDelegate()->LoadSession(
    217       web_session_id,
    218       session_loaded_promise.PassAs<media::NewSessionCdmPromise>());
    219 }
    220 
    221 void PpapiDecryptor::UpdateSession(
    222     const std::string& web_session_id,
    223     const uint8* response,
    224     int response_length,
    225     scoped_ptr<media::SimpleCdmPromise> promise) {
    226   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
    227 
    228   if (!CdmDelegate()) {
    229     promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
    230     return;
    231   }
    232 
    233   // TODO(jrummell): Intercepting the promise should not be necessary once
    234   // OnSessionKeysChange() is called in all cases. http://crbug.com/413413.
    235   scoped_ptr<SessionUpdatedPromise> session_updated_promise(
    236       new SessionUpdatedPromise(promise.Pass(),
    237                                 base::Bind(&PpapiDecryptor::ResumePlayback,
    238                                            weak_ptr_factory_.GetWeakPtr())));
    239   CdmDelegate()->UpdateSession(
    240       web_session_id,
    241       response,
    242       response_length,
    243       session_updated_promise.PassAs<media::SimpleCdmPromise>());
    244 }
    245 
    246 void PpapiDecryptor::CloseSession(const std::string& web_session_id,
    247                                   scoped_ptr<media::SimpleCdmPromise> promise) {
    248   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
    249 
    250   if (!CdmDelegate()) {
    251     promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
    252     return;
    253   }
    254 
    255   CdmDelegate()->CloseSession(web_session_id, promise.Pass());
    256 }
    257 
    258 void PpapiDecryptor::RemoveSession(
    259     const std::string& web_session_id,
    260     scoped_ptr<media::SimpleCdmPromise> promise) {
    261   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
    262 
    263   if (!CdmDelegate()) {
    264     promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
    265     return;
    266   }
    267 
    268   CdmDelegate()->RemoveSession(web_session_id, promise.Pass());
    269 }
    270 
    271 void PpapiDecryptor::GetUsableKeyIds(const std::string& web_session_id,
    272                                      scoped_ptr<media::KeyIdsPromise> promise) {
    273   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
    274 
    275   if (!CdmDelegate()) {
    276     promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
    277     return;
    278   }
    279 
    280   CdmDelegate()->GetUsableKeyIds(web_session_id, promise.Pass());
    281 }
    282 
    283 media::Decryptor* PpapiDecryptor::GetDecryptor() {
    284   return this;
    285 }
    286 
    287 void PpapiDecryptor::RegisterNewKeyCB(StreamType stream_type,
    288                                       const NewKeyCB& new_key_cb) {
    289   if (!render_loop_proxy_->BelongsToCurrentThread()) {
    290     render_loop_proxy_->PostTask(FROM_HERE,
    291                                  base::Bind(&PpapiDecryptor::RegisterNewKeyCB,
    292                                             weak_ptr_factory_.GetWeakPtr(),
    293                                             stream_type,
    294                                             new_key_cb));
    295     return;
    296   }
    297 
    298   DVLOG(3) << __FUNCTION__ << " - stream_type: " << stream_type;
    299   switch (stream_type) {
    300     case kAudio:
    301       new_audio_key_cb_ = new_key_cb;
    302       break;
    303     case kVideo:
    304       new_video_key_cb_ = new_key_cb;
    305       break;
    306     default:
    307       NOTREACHED();
    308   }
    309 }
    310 
    311 void PpapiDecryptor::Decrypt(
    312     StreamType stream_type,
    313     const scoped_refptr<media::DecoderBuffer>& encrypted,
    314     const DecryptCB& decrypt_cb) {
    315   if (!render_loop_proxy_->BelongsToCurrentThread()) {
    316     render_loop_proxy_->PostTask(FROM_HERE,
    317                                  base::Bind(&PpapiDecryptor::Decrypt,
    318                                             weak_ptr_factory_.GetWeakPtr(),
    319                                             stream_type,
    320                                             encrypted,
    321                                             decrypt_cb));
    322     return;
    323   }
    324 
    325   DVLOG(3) << __FUNCTION__ << " - stream_type: " << stream_type;
    326   if (!CdmDelegate() ||
    327       !CdmDelegate()->Decrypt(stream_type, encrypted, decrypt_cb)) {
    328     decrypt_cb.Run(kError, NULL);
    329   }
    330 }
    331 
    332 void PpapiDecryptor::CancelDecrypt(StreamType stream_type) {
    333   if (!render_loop_proxy_->BelongsToCurrentThread()) {
    334     render_loop_proxy_->PostTask(FROM_HERE,
    335                                  base::Bind(&PpapiDecryptor::CancelDecrypt,
    336                                             weak_ptr_factory_.GetWeakPtr(),
    337                                             stream_type));
    338     return;
    339   }
    340 
    341   DVLOG(1) << __FUNCTION__ << " - stream_type: " << stream_type;
    342   if (CdmDelegate())
    343     CdmDelegate()->CancelDecrypt(stream_type);
    344 }
    345 
    346 void PpapiDecryptor::InitializeAudioDecoder(
    347       const media::AudioDecoderConfig& config,
    348       const DecoderInitCB& init_cb) {
    349   if (!render_loop_proxy_->BelongsToCurrentThread()) {
    350     render_loop_proxy_->PostTask(
    351         FROM_HERE,
    352         base::Bind(&PpapiDecryptor::InitializeAudioDecoder,
    353                    weak_ptr_factory_.GetWeakPtr(),
    354                    config,
    355                    init_cb));
    356     return;
    357   }
    358 
    359   DVLOG(2) << __FUNCTION__;
    360   DCHECK(config.is_encrypted());
    361   DCHECK(config.IsValidConfig());
    362 
    363   audio_decoder_init_cb_ = init_cb;
    364   if (!CdmDelegate() || !CdmDelegate()->InitializeAudioDecoder(
    365                             config,
    366                             base::Bind(&PpapiDecryptor::OnDecoderInitialized,
    367                                        weak_ptr_factory_.GetWeakPtr(),
    368                                        kAudio))) {
    369     base::ResetAndReturn(&audio_decoder_init_cb_).Run(false);
    370     return;
    371   }
    372 }
    373 
    374 void PpapiDecryptor::InitializeVideoDecoder(
    375     const media::VideoDecoderConfig& config,
    376     const DecoderInitCB& init_cb) {
    377   if (!render_loop_proxy_->BelongsToCurrentThread()) {
    378     render_loop_proxy_->PostTask(
    379         FROM_HERE,
    380         base::Bind(&PpapiDecryptor::InitializeVideoDecoder,
    381                    weak_ptr_factory_.GetWeakPtr(),
    382                    config,
    383                    init_cb));
    384     return;
    385   }
    386 
    387   DVLOG(2) << __FUNCTION__;
    388   DCHECK(config.is_encrypted());
    389   DCHECK(config.IsValidConfig());
    390 
    391   video_decoder_init_cb_ = init_cb;
    392   if (!CdmDelegate() || !CdmDelegate()->InitializeVideoDecoder(
    393                             config,
    394                             base::Bind(&PpapiDecryptor::OnDecoderInitialized,
    395                                        weak_ptr_factory_.GetWeakPtr(),
    396                                        kVideo))) {
    397     base::ResetAndReturn(&video_decoder_init_cb_).Run(false);
    398     return;
    399   }
    400 }
    401 
    402 void PpapiDecryptor::DecryptAndDecodeAudio(
    403     const scoped_refptr<media::DecoderBuffer>& encrypted,
    404     const AudioDecodeCB& audio_decode_cb) {
    405   if (!render_loop_proxy_->BelongsToCurrentThread()) {
    406     render_loop_proxy_->PostTask(
    407         FROM_HERE,
    408         base::Bind(&PpapiDecryptor::DecryptAndDecodeAudio,
    409                    weak_ptr_factory_.GetWeakPtr(),
    410                    encrypted,
    411                    audio_decode_cb));
    412     return;
    413   }
    414 
    415   DVLOG(3) << __FUNCTION__;
    416   if (!CdmDelegate() ||
    417       !CdmDelegate()->DecryptAndDecodeAudio(encrypted, audio_decode_cb)) {
    418     audio_decode_cb.Run(kError, AudioBuffers());
    419   }
    420 }
    421 
    422 void PpapiDecryptor::DecryptAndDecodeVideo(
    423     const scoped_refptr<media::DecoderBuffer>& encrypted,
    424     const VideoDecodeCB& video_decode_cb) {
    425   if (!render_loop_proxy_->BelongsToCurrentThread()) {
    426     render_loop_proxy_->PostTask(
    427         FROM_HERE,
    428         base::Bind(&PpapiDecryptor::DecryptAndDecodeVideo,
    429                    weak_ptr_factory_.GetWeakPtr(),
    430                    encrypted,
    431                    video_decode_cb));
    432     return;
    433   }
    434 
    435   DVLOG(3) << __FUNCTION__;
    436   if (!CdmDelegate() ||
    437       !CdmDelegate()->DecryptAndDecodeVideo(encrypted, video_decode_cb)) {
    438     video_decode_cb.Run(kError, NULL);
    439   }
    440 }
    441 
    442 void PpapiDecryptor::ResetDecoder(StreamType stream_type) {
    443   if (!render_loop_proxy_->BelongsToCurrentThread()) {
    444     render_loop_proxy_->PostTask(FROM_HERE,
    445                                  base::Bind(&PpapiDecryptor::ResetDecoder,
    446                                             weak_ptr_factory_.GetWeakPtr(),
    447                                             stream_type));
    448     return;
    449   }
    450 
    451   DVLOG(2) << __FUNCTION__ << " - stream_type: " << stream_type;
    452   if (CdmDelegate())
    453     CdmDelegate()->ResetDecoder(stream_type);
    454 }
    455 
    456 void PpapiDecryptor::DeinitializeDecoder(StreamType stream_type) {
    457   if (!render_loop_proxy_->BelongsToCurrentThread()) {
    458     render_loop_proxy_->PostTask(
    459         FROM_HERE,
    460         base::Bind(&PpapiDecryptor::DeinitializeDecoder,
    461                    weak_ptr_factory_.GetWeakPtr(),
    462                    stream_type));
    463     return;
    464   }
    465 
    466   DVLOG(2) << __FUNCTION__ << " - stream_type: " << stream_type;
    467   if (CdmDelegate())
    468     CdmDelegate()->DeinitializeDecoder(stream_type);
    469 }
    470 
    471 void PpapiDecryptor::OnDecoderInitialized(StreamType stream_type,
    472                                           bool success) {
    473   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
    474   switch (stream_type) {
    475     case kAudio:
    476       DCHECK(!audio_decoder_init_cb_.is_null());
    477       base::ResetAndReturn(&audio_decoder_init_cb_).Run(success);
    478       break;
    479     case kVideo:
    480       DCHECK(!video_decoder_init_cb_.is_null());
    481       base::ResetAndReturn(&video_decoder_init_cb_).Run(success);
    482       break;
    483     default:
    484       NOTREACHED();
    485   }
    486 }
    487 
    488 void PpapiDecryptor::OnSessionMessage(const std::string& web_session_id,
    489                                       const std::vector<uint8>& message,
    490                                       const GURL& destination_url) {
    491   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
    492   session_message_cb_.Run(web_session_id, message, destination_url);
    493 }
    494 
    495 void PpapiDecryptor::OnSessionKeysChange(const std::string& web_session_id,
    496                                          bool has_additional_usable_key) {
    497   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
    498 
    499   // TODO(jrummell): Handling resume playback should be done in the media
    500   // player, not in the Decryptors. http://crbug.com/413413.
    501   if (has_additional_usable_key)
    502     ResumePlayback();
    503 
    504   session_keys_change_cb_.Run(web_session_id, has_additional_usable_key);
    505 }
    506 
    507 void PpapiDecryptor::OnSessionExpirationUpdate(
    508     const std::string& web_session_id,
    509     const base::Time& new_expiry_time) {
    510   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
    511   session_expiration_update_cb_.Run(web_session_id, new_expiry_time);
    512 }
    513 
    514 void PpapiDecryptor::OnSessionReady(const std::string& web_session_id) {
    515   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
    516 
    517   // TODO(jrummell): Calling ResumePlayback() here should not be necessary once
    518   // OnSessionKeysChange() is called in all cases. http://crbug.com/413413.
    519   ResumePlayback();
    520   session_ready_cb_.Run(web_session_id);
    521 }
    522 
    523 void PpapiDecryptor::OnSessionClosed(const std::string& web_session_id) {
    524   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
    525   session_closed_cb_.Run(web_session_id);
    526 }
    527 
    528 void PpapiDecryptor::OnSessionError(const std::string& web_session_id,
    529                                     MediaKeys::Exception exception_code,
    530                                     uint32 system_code,
    531                                     const std::string& error_description) {
    532   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
    533   session_error_cb_.Run(
    534       web_session_id, exception_code, system_code, error_description);
    535 }
    536 
    537 void PpapiDecryptor::ResumePlayback() {
    538   // Based on the spec, we need to resume playback when update() completes
    539   // successfully, or when a session is successfully loaded (triggered by
    540   // OnSessionReady()). So we choose to call the NewKeyCBs here.
    541   if (!new_audio_key_cb_.is_null())
    542     new_audio_key_cb_.Run();
    543 
    544   if (!new_video_key_cb_.is_null())
    545     new_video_key_cb_.Run();
    546 }
    547 
    548 void PpapiDecryptor::OnFatalPluginError() {
    549   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
    550   pepper_cdm_wrapper_.reset();
    551 }
    552 
    553 ContentDecryptorDelegate* PpapiDecryptor::CdmDelegate() {
    554   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
    555   return (pepper_cdm_wrapper_) ? pepper_cdm_wrapper_->GetCdmDelegate() : NULL;
    556 }
    557 
    558 }  // namespace content
    559