Home | History | Annotate | Download | only in external_clear_key
      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 "media/cdm/ppapi/external_clear_key/clear_key_cdm.h"
      6 
      7 #include <algorithm>
      8 #include <cstring>
      9 #include <sstream>
     10 #include <string>
     11 #include <vector>
     12 
     13 #include "base/bind.h"
     14 #include "base/debug/trace_event.h"
     15 #include "base/logging.h"
     16 #include "base/time/time.h"
     17 #include "media/base/cdm_promise.h"
     18 #include "media/base/decoder_buffer.h"
     19 #include "media/base/decrypt_config.h"
     20 #include "media/cdm/json_web_key.h"
     21 #include "media/cdm/ppapi/cdm_file_io_test.h"
     22 #include "media/cdm/ppapi/external_clear_key/cdm_video_decoder.h"
     23 
     24 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
     25 #include "base/basictypes.h"
     26 const int64 kNoTimestamp = kint64min;
     27 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
     28 
     29 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
     30 #include "base/at_exit.h"
     31 #include "base/files/file_path.h"
     32 #include "base/path_service.h"
     33 #include "media/base/media.h"
     34 #include "media/cdm/ppapi/external_clear_key/ffmpeg_cdm_audio_decoder.h"
     35 #include "media/cdm/ppapi/external_clear_key/ffmpeg_cdm_video_decoder.h"
     36 
     37 // Include FFmpeg avformat.h for av_register_all().
     38 extern "C" {
     39 // Temporarily disable possible loss of data warning.
     40 MSVC_PUSH_DISABLE_WARNING(4244);
     41 #include <libavformat/avformat.h>
     42 MSVC_POP_WARNING();
     43 }  // extern "C"
     44 
     45 // TODO(tomfinegan): When COMPONENT_BUILD is not defined an AtExitManager must
     46 // exist before the call to InitializeFFmpegLibraries(). This should no longer
     47 // be required after http://crbug.com/91970 because we'll be able to get rid of
     48 // InitializeFFmpegLibraries().
     49 #if !defined COMPONENT_BUILD
     50 static base::AtExitManager g_at_exit_manager;
     51 #endif
     52 
     53 // TODO(tomfinegan): InitializeFFmpegLibraries() and |g_cdm_module_initialized|
     54 // are required for running in the sandbox, and should no longer be required
     55 // after http://crbug.com/91970 is fixed.
     56 static bool InitializeFFmpegLibraries() {
     57   base::FilePath file_path;
     58   CHECK(PathService::Get(base::DIR_MODULE, &file_path));
     59   CHECK(media::InitializeMediaLibrary(file_path));
     60   return true;
     61 }
     62 
     63 static bool g_ffmpeg_lib_initialized = InitializeFFmpegLibraries();
     64 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
     65 
     66 const char kClearKeyCdmVersion[] = "0.1.0.1";
     67 const char kExternalClearKeyKeySystem[] = "org.chromium.externalclearkey";
     68 const char kExternalClearKeyDecryptOnlyKeySystem[] =
     69     "org.chromium.externalclearkey.decryptonly";
     70 const char kExternalClearKeyFileIOTestKeySystem[] =
     71     "org.chromium.externalclearkey.fileiotest";
     72 const char kExternalClearKeyCrashKeySystem[] =
     73     "org.chromium.externalclearkey.crash";
     74 
     75 // Constants for the enumalted session that can be loaded by LoadSession().
     76 // These constants need to be in sync with
     77 // chrome/test/data/media/encrypted_media_utils.js
     78 const char kLoadableWebSessionId[] = "LoadableSession";
     79 const char kLoadableSessionContentType[] = "video/webm";
     80 const uint8 kLoadableSessionKeyId[] = "0123456789012345";
     81 const uint8 kLoadableSessionKey[] =
     82     {0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
     83      0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c};
     84 
     85 const int64 kSecondsPerMinute = 60;
     86 const int64 kMsPerSecond = 1000;
     87 const int64 kInitialTimerDelayMs = 200;
     88 const int64 kMaxTimerDelayMs = 1 * kSecondsPerMinute * kMsPerSecond;
     89 // Heart beat message header. If a key message starts with |kHeartBeatHeader|,
     90 // it's a heart beat message. Otherwise, it's a key request.
     91 const char kHeartBeatHeader[] = "HEARTBEAT";
     92 // CDM file IO test result header.
     93 const char kFileIOTestResultHeader[] = "FILEIOTESTRESULT";
     94 
     95 // Copies |input_buffer| into a media::DecoderBuffer. If the |input_buffer| is
     96 // empty, an empty (end-of-stream) media::DecoderBuffer is returned.
     97 static scoped_refptr<media::DecoderBuffer> CopyDecoderBufferFrom(
     98     const cdm::InputBuffer& input_buffer) {
     99   if (!input_buffer.data) {
    100     DCHECK(!input_buffer.data_size);
    101     return media::DecoderBuffer::CreateEOSBuffer();
    102   }
    103 
    104   // TODO(xhwang): Get rid of this copy.
    105   scoped_refptr<media::DecoderBuffer> output_buffer =
    106       media::DecoderBuffer::CopyFrom(input_buffer.data, input_buffer.data_size);
    107 
    108   std::vector<media::SubsampleEntry> subsamples;
    109   for (uint32_t i = 0; i < input_buffer.num_subsamples; ++i) {
    110     media::SubsampleEntry subsample;
    111     subsample.clear_bytes = input_buffer.subsamples[i].clear_bytes;
    112     subsample.cypher_bytes = input_buffer.subsamples[i].cipher_bytes;
    113     subsamples.push_back(subsample);
    114   }
    115 
    116   scoped_ptr<media::DecryptConfig> decrypt_config(new media::DecryptConfig(
    117       std::string(reinterpret_cast<const char*>(input_buffer.key_id),
    118                   input_buffer.key_id_size),
    119       std::string(reinterpret_cast<const char*>(input_buffer.iv),
    120                   input_buffer.iv_size),
    121       subsamples));
    122 
    123   output_buffer->set_decrypt_config(decrypt_config.Pass());
    124   output_buffer->set_timestamp(
    125       base::TimeDelta::FromMicroseconds(input_buffer.timestamp));
    126 
    127   return output_buffer;
    128 }
    129 
    130 static std::string GetFileIOTestResultMessage(bool success) {
    131   std::string message(kFileIOTestResultHeader);
    132   message += success ? '1' : '0';
    133   return message;
    134 }
    135 
    136 static cdm::Error ConvertException(media::MediaKeys::Exception exception_code) {
    137   switch (exception_code) {
    138     case media::MediaKeys::NOT_SUPPORTED_ERROR:
    139       return cdm::kNotSupportedError;
    140     case media::MediaKeys::INVALID_STATE_ERROR:
    141       return cdm::kInvalidStateError;
    142     case media::MediaKeys::INVALID_ACCESS_ERROR:
    143       return cdm::kInvalidAccessError;
    144     case media::MediaKeys::QUOTA_EXCEEDED_ERROR:
    145       return cdm::kQuotaExceededError;
    146     case media::MediaKeys::UNKNOWN_ERROR:
    147       return cdm::kUnknownError;
    148     case media::MediaKeys::CLIENT_ERROR:
    149       return cdm::kClientError;
    150     case media::MediaKeys::OUTPUT_ERROR:
    151       return cdm::kOutputError;
    152   }
    153   NOTIMPLEMENTED();
    154   return cdm::kUnknownError;
    155 }
    156 
    157 static media::MediaKeys::SessionType ConvertSessionType(
    158     cdm::SessionType session_type) {
    159   switch (session_type) {
    160     case cdm::kPersistent:
    161       return media::MediaKeys::PERSISTENT_SESSION;
    162     case cdm::kTemporary:
    163       return media::MediaKeys::TEMPORARY_SESSION;
    164   }
    165   NOTIMPLEMENTED();
    166   return media::MediaKeys::TEMPORARY_SESSION;
    167 }
    168 
    169 template<typename Type>
    170 class ScopedResetter {
    171  public:
    172   explicit ScopedResetter(Type* object) : object_(object) {}
    173   ~ScopedResetter() { object_->Reset(); }
    174 
    175  private:
    176   Type* const object_;
    177 };
    178 
    179 void INITIALIZE_CDM_MODULE() {
    180 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
    181   DVLOG(2) << "FFmpeg libraries initialized: " << g_ffmpeg_lib_initialized;
    182   av_register_all();
    183 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
    184 }
    185 
    186 void DeinitializeCdmModule() {
    187 }
    188 
    189 void* CreateCdmInstance(int cdm_interface_version,
    190                         const char* key_system, uint32_t key_system_size,
    191                         GetCdmHostFunc get_cdm_host_func,
    192                         void* user_data) {
    193   DVLOG(1) << "CreateCdmInstance()";
    194 
    195   std::string key_system_string(key_system, key_system_size);
    196   if (key_system_string != kExternalClearKeyKeySystem &&
    197       key_system_string != kExternalClearKeyDecryptOnlyKeySystem &&
    198       key_system_string != kExternalClearKeyFileIOTestKeySystem &&
    199       key_system_string != kExternalClearKeyCrashKeySystem) {
    200     DVLOG(1) << "Unsupported key system:" << key_system_string;
    201     return NULL;
    202   }
    203 
    204   if (cdm_interface_version != media::ClearKeyCdmInterface::kVersion)
    205     return NULL;
    206 
    207   media::ClearKeyCdmHost* host = static_cast<media::ClearKeyCdmHost*>(
    208       get_cdm_host_func(media::ClearKeyCdmHost::kVersion, user_data));
    209   if (!host)
    210     return NULL;
    211 
    212   return new media::ClearKeyCdm(host, key_system_string);
    213 }
    214 
    215 const char* GetCdmVersion() {
    216   return kClearKeyCdmVersion;
    217 }
    218 
    219 namespace media {
    220 
    221 ClearKeyCdm::ClearKeyCdm(ClearKeyCdmHost* host, const std::string& key_system)
    222     : decryptor_(
    223           base::Bind(&ClearKeyCdm::OnSessionMessage, base::Unretained(this)),
    224           base::Bind(&ClearKeyCdm::OnSessionClosed, base::Unretained(this)),
    225           base::Bind(&ClearKeyCdm::OnSessionKeysChange,
    226                      base::Unretained(this))),
    227       host_(host),
    228       key_system_(key_system),
    229       timer_delay_ms_(kInitialTimerDelayMs),
    230       heartbeat_timer_set_(false) {
    231 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
    232   channel_count_ = 0;
    233   bits_per_channel_ = 0;
    234   samples_per_second_ = 0;
    235   output_timestamp_base_in_microseconds_ = kNoTimestamp;
    236   total_samples_generated_ = 0;
    237 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
    238 }
    239 
    240 ClearKeyCdm::~ClearKeyCdm() {}
    241 
    242 void ClearKeyCdm::CreateSession(uint32 promise_id,
    243                                 const char* init_data_type,
    244                                 uint32 init_data_type_size,
    245                                 const uint8* init_data,
    246                                 uint32 init_data_size,
    247                                 cdm::SessionType session_type) {
    248   DVLOG(1) << __FUNCTION__;
    249 
    250   scoped_ptr<media::NewSessionCdmPromise> promise(
    251       new media::NewSessionCdmPromise(base::Bind(&ClearKeyCdm::OnSessionCreated,
    252                                                  base::Unretained(this),
    253                                                  promise_id),
    254                                       base::Bind(&ClearKeyCdm::OnPromiseFailed,
    255                                                  base::Unretained(this),
    256                                                  promise_id)));
    257   decryptor_.CreateSession(std::string(init_data_type, init_data_type_size),
    258                            init_data,
    259                            init_data_size,
    260                            ConvertSessionType(session_type),
    261                            promise.Pass());
    262 
    263   if (key_system_ == kExternalClearKeyFileIOTestKeySystem)
    264     StartFileIOTest();
    265 }
    266 
    267 // Loads a emulated stored session. Currently only |kLoadableWebSessionId|
    268 // (containing a |kLoadableSessionKey| for |kLoadableSessionKeyId|) is
    269 // supported.
    270 void ClearKeyCdm::LoadSession(uint32 promise_id,
    271                               const char* web_session_id,
    272                               uint32_t web_session_id_length) {
    273   DVLOG(1) << __FUNCTION__;
    274 
    275   if (std::string(kLoadableWebSessionId) !=
    276       std::string(web_session_id, web_session_id_length)) {
    277     // TODO(jrummell): This should be resolved with undefined, not rejected.
    278     std::string message("Incorrect session id specified for LoadSession().");
    279     host_->OnRejectPromise(promise_id,
    280                            cdm::kInvalidAccessError,
    281                            0,
    282                            message.data(),
    283                            message.length());
    284     return;
    285   }
    286 
    287   scoped_ptr<media::NewSessionCdmPromise> promise(
    288       new media::NewSessionCdmPromise(base::Bind(&ClearKeyCdm::OnSessionLoaded,
    289                                                  base::Unretained(this),
    290                                                  promise_id),
    291                                       base::Bind(&ClearKeyCdm::OnPromiseFailed,
    292                                                  base::Unretained(this),
    293                                                  promise_id)));
    294   decryptor_.CreateSession(std::string(kLoadableSessionContentType),
    295                            NULL,
    296                            0,
    297                            MediaKeys::TEMPORARY_SESSION,
    298                            promise.Pass());
    299 }
    300 
    301 void ClearKeyCdm::UpdateSession(uint32 promise_id,
    302                                 const char* web_session_id,
    303                                 uint32_t web_session_id_length,
    304                                 const uint8* response,
    305                                 uint32 response_size) {
    306   DVLOG(1) << __FUNCTION__;
    307   std::string web_session_str(web_session_id, web_session_id_length);
    308 
    309   scoped_ptr<media::SimpleCdmPromise> promise(new media::SimpleCdmPromise(
    310       base::Bind(&ClearKeyCdm::OnSessionUpdated,
    311                  base::Unretained(this),
    312                  promise_id,
    313                  web_session_str),
    314       base::Bind(
    315           &ClearKeyCdm::OnPromiseFailed, base::Unretained(this), promise_id)));
    316   decryptor_.UpdateSession(
    317       web_session_str, response, response_size, promise.Pass());
    318 
    319   if (!heartbeat_timer_set_) {
    320     ScheduleNextHeartBeat();
    321     heartbeat_timer_set_ = true;
    322   }
    323 }
    324 
    325 void ClearKeyCdm::CloseSession(uint32 promise_id,
    326                                const char* web_session_id,
    327                                uint32_t web_session_id_length) {
    328   DVLOG(1) << __FUNCTION__;
    329   std::string web_session_str(web_session_id, web_session_id_length);
    330 
    331   scoped_ptr<media::SimpleCdmPromise> promise(new media::SimpleCdmPromise(
    332       base::Bind(
    333           &ClearKeyCdm::OnPromiseResolved, base::Unretained(this), promise_id),
    334       base::Bind(
    335           &ClearKeyCdm::OnPromiseFailed, base::Unretained(this), promise_id)));
    336   decryptor_.CloseSession(web_session_str, promise.Pass());
    337 }
    338 
    339 void ClearKeyCdm::RemoveSession(uint32 promise_id,
    340                                 const char* web_session_id,
    341                                 uint32_t web_session_id_length) {
    342   DVLOG(1) << __FUNCTION__;
    343   // RemoveSession only allowed for persistent sessions.
    344   bool is_persistent_session =
    345       std::string(kLoadableWebSessionId) ==
    346       std::string(web_session_id, web_session_id_length);
    347   if (is_persistent_session) {
    348     std::string web_session_str(web_session_id, web_session_id_length);
    349 
    350     scoped_ptr<media::SimpleCdmPromise> promise(
    351         new media::SimpleCdmPromise(base::Bind(&ClearKeyCdm::OnPromiseResolved,
    352                                                base::Unretained(this),
    353                                                promise_id),
    354                                     base::Bind(&ClearKeyCdm::OnPromiseFailed,
    355                                                base::Unretained(this),
    356                                                promise_id)));
    357     decryptor_.RemoveSession(web_session_str, promise.Pass());
    358   } else {
    359     // TODO(jrummell): This should be a DCHECK once blink does the proper
    360     // checks.
    361     std::string message("Not supported for non-persistent sessions.");
    362     host_->OnRejectPromise(promise_id,
    363                            cdm::kInvalidAccessError,
    364                            0,
    365                            message.data(),
    366                            message.length());
    367   }
    368 }
    369 
    370 void ClearKeyCdm::SetServerCertificate(uint32 promise_id,
    371                                        const uint8_t* server_certificate_data,
    372                                        uint32_t server_certificate_data_size) {
    373   // ClearKey doesn't use a server certificate.
    374   host_->OnResolvePromise(promise_id);
    375 }
    376 
    377 void ClearKeyCdm::GetUsableKeyIds(uint32_t promise_id,
    378                                   const char* web_session_id,
    379                                   uint32_t web_session_id_length) {
    380   std::string web_session_str(web_session_id, web_session_id_length);
    381   scoped_ptr<media::KeyIdsPromise> promise(new media::KeyIdsPromise(
    382       base::Bind(&ClearKeyCdm::OnUsableKeyIdsObtained,
    383                  base::Unretained(this),
    384                  promise_id),
    385       base::Bind(
    386           &ClearKeyCdm::OnPromiseFailed, base::Unretained(this), promise_id)));
    387   decryptor_.GetUsableKeyIds(web_session_str, promise.Pass());
    388 }
    389 
    390 void ClearKeyCdm::TimerExpired(void* context) {
    391   if (context == &session_id_for_emulated_loadsession_) {
    392     LoadLoadableSession();
    393     return;
    394   }
    395 
    396   DCHECK(heartbeat_timer_set_);
    397   std::string heartbeat_message;
    398   if (!next_heartbeat_message_.empty() &&
    399       context == &next_heartbeat_message_[0]) {
    400     heartbeat_message = next_heartbeat_message_;
    401   } else {
    402     heartbeat_message = "ERROR: Invalid timer context found!";
    403   }
    404 
    405   // This URL is only used for testing the code path for defaultURL.
    406   // There is no service at this URL, so applications should ignore it.
    407   const char url[] = "http://test.externalclearkey.chromium.org";
    408 
    409   host_->OnSessionMessage(last_session_id_.data(),
    410                           last_session_id_.length(),
    411                           heartbeat_message.data(),
    412                           heartbeat_message.length(),
    413                           url,
    414                           arraysize(url) - 1);
    415 
    416   ScheduleNextHeartBeat();
    417 }
    418 
    419 static void CopyDecryptResults(
    420     media::Decryptor::Status* status_copy,
    421     scoped_refptr<media::DecoderBuffer>* buffer_copy,
    422     media::Decryptor::Status status,
    423     const scoped_refptr<media::DecoderBuffer>& buffer) {
    424   *status_copy = status;
    425   *buffer_copy = buffer;
    426 }
    427 
    428 cdm::Status ClearKeyCdm::Decrypt(const cdm::InputBuffer& encrypted_buffer,
    429                                  cdm::DecryptedBlock* decrypted_block) {
    430   DVLOG(1) << "Decrypt()";
    431   DCHECK(encrypted_buffer.data);
    432 
    433   scoped_refptr<media::DecoderBuffer> buffer;
    434   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
    435 
    436   if (status != cdm::kSuccess)
    437     return status;
    438 
    439   DCHECK(buffer->data());
    440   decrypted_block->SetDecryptedBuffer(
    441       host_->Allocate(buffer->data_size()));
    442   memcpy(reinterpret_cast<void*>(decrypted_block->DecryptedBuffer()->Data()),
    443          buffer->data(),
    444          buffer->data_size());
    445   decrypted_block->DecryptedBuffer()->SetSize(buffer->data_size());
    446   decrypted_block->SetTimestamp(buffer->timestamp().InMicroseconds());
    447 
    448   return cdm::kSuccess;
    449 }
    450 
    451 cdm::Status ClearKeyCdm::InitializeAudioDecoder(
    452     const cdm::AudioDecoderConfig& audio_decoder_config) {
    453   if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
    454     return cdm::kSessionError;
    455 
    456 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
    457   if (!audio_decoder_)
    458     audio_decoder_.reset(new media::FFmpegCdmAudioDecoder(host_));
    459 
    460   if (!audio_decoder_->Initialize(audio_decoder_config))
    461     return cdm::kSessionError;
    462 
    463   return cdm::kSuccess;
    464 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
    465   channel_count_ = audio_decoder_config.channel_count;
    466   bits_per_channel_ = audio_decoder_config.bits_per_channel;
    467   samples_per_second_ = audio_decoder_config.samples_per_second;
    468   return cdm::kSuccess;
    469 #else
    470   NOTIMPLEMENTED();
    471   return cdm::kSessionError;
    472 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
    473 }
    474 
    475 cdm::Status ClearKeyCdm::InitializeVideoDecoder(
    476     const cdm::VideoDecoderConfig& video_decoder_config) {
    477   if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
    478     return cdm::kSessionError;
    479 
    480   if (video_decoder_ && video_decoder_->is_initialized()) {
    481     DCHECK(!video_decoder_->is_initialized());
    482     return cdm::kSessionError;
    483   }
    484 
    485   // Any uninitialized decoder will be replaced.
    486   video_decoder_ = CreateVideoDecoder(host_, video_decoder_config);
    487   if (!video_decoder_)
    488     return cdm::kSessionError;
    489 
    490   return cdm::kSuccess;
    491 }
    492 
    493 void ClearKeyCdm::ResetDecoder(cdm::StreamType decoder_type) {
    494   DVLOG(1) << "ResetDecoder()";
    495 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
    496   switch (decoder_type) {
    497     case cdm::kStreamTypeVideo:
    498       video_decoder_->Reset();
    499       break;
    500     case cdm::kStreamTypeAudio:
    501       audio_decoder_->Reset();
    502       break;
    503     default:
    504       NOTREACHED() << "ResetDecoder(): invalid cdm::StreamType";
    505   }
    506 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
    507   if (decoder_type == cdm::kStreamTypeAudio) {
    508     output_timestamp_base_in_microseconds_ = kNoTimestamp;
    509     total_samples_generated_ = 0;
    510   }
    511 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
    512 }
    513 
    514 void ClearKeyCdm::DeinitializeDecoder(cdm::StreamType decoder_type) {
    515   DVLOG(1) << "DeinitializeDecoder()";
    516   switch (decoder_type) {
    517     case cdm::kStreamTypeVideo:
    518       video_decoder_->Deinitialize();
    519       break;
    520     case cdm::kStreamTypeAudio:
    521 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
    522       audio_decoder_->Deinitialize();
    523 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
    524       output_timestamp_base_in_microseconds_ = kNoTimestamp;
    525       total_samples_generated_ = 0;
    526 #endif
    527       break;
    528     default:
    529       NOTREACHED() << "DeinitializeDecoder(): invalid cdm::StreamType";
    530   }
    531 }
    532 
    533 cdm::Status ClearKeyCdm::DecryptAndDecodeFrame(
    534     const cdm::InputBuffer& encrypted_buffer,
    535     cdm::VideoFrame* decoded_frame) {
    536   DVLOG(1) << "DecryptAndDecodeFrame()";
    537   TRACE_EVENT0("media", "ClearKeyCdm::DecryptAndDecodeFrame");
    538 
    539   scoped_refptr<media::DecoderBuffer> buffer;
    540   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
    541 
    542   if (status != cdm::kSuccess)
    543     return status;
    544 
    545   const uint8_t* data = NULL;
    546   int32_t size = 0;
    547   int64_t timestamp = 0;
    548   if (!buffer->end_of_stream()) {
    549     data = buffer->data();
    550     size = buffer->data_size();
    551     timestamp = encrypted_buffer.timestamp;
    552   }
    553 
    554   return video_decoder_->DecodeFrame(data, size, timestamp, decoded_frame);
    555 }
    556 
    557 cdm::Status ClearKeyCdm::DecryptAndDecodeSamples(
    558     const cdm::InputBuffer& encrypted_buffer,
    559     cdm::AudioFrames* audio_frames) {
    560   DVLOG(1) << "DecryptAndDecodeSamples()";
    561 
    562   // Trigger a crash on purpose for testing purpose.
    563   if (key_system_ == kExternalClearKeyCrashKeySystem)
    564     CHECK(false);
    565 
    566   scoped_refptr<media::DecoderBuffer> buffer;
    567   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
    568 
    569   if (status != cdm::kSuccess)
    570     return status;
    571 
    572 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
    573   const uint8_t* data = NULL;
    574   int32_t size = 0;
    575   int64_t timestamp = 0;
    576   if (!buffer->end_of_stream()) {
    577     data = buffer->data();
    578     size = buffer->data_size();
    579     timestamp = encrypted_buffer.timestamp;
    580   }
    581 
    582   return audio_decoder_->DecodeBuffer(data, size, timestamp, audio_frames);
    583 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
    584   int64 timestamp_in_microseconds = kNoTimestamp;
    585   if (!buffer->end_of_stream()) {
    586     timestamp_in_microseconds = buffer->GetTimestamp().InMicroseconds();
    587     DCHECK(timestamp_in_microseconds != kNoTimestamp);
    588   }
    589   return GenerateFakeAudioFrames(timestamp_in_microseconds, audio_frames);
    590 #else
    591   return cdm::kSuccess;
    592 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
    593 }
    594 
    595 void ClearKeyCdm::Destroy() {
    596   DVLOG(1) << "Destroy()";
    597   delete this;
    598 }
    599 
    600 void ClearKeyCdm::ScheduleNextHeartBeat() {
    601   // Prepare the next heartbeat message and set timer.
    602   std::ostringstream msg_stream;
    603   msg_stream << kHeartBeatHeader << " from ClearKey CDM set at time "
    604              << host_->GetCurrentWallTime() << ".";
    605   next_heartbeat_message_ = msg_stream.str();
    606 
    607   host_->SetTimer(timer_delay_ms_, &next_heartbeat_message_[0]);
    608 
    609   // Use a smaller timer delay at start-up to facilitate testing. Increase the
    610   // timer delay up to a limit to avoid message spam.
    611   if (timer_delay_ms_ < kMaxTimerDelayMs)
    612     timer_delay_ms_ = std::min(2 * timer_delay_ms_, kMaxTimerDelayMs);
    613 }
    614 
    615 cdm::Status ClearKeyCdm::DecryptToMediaDecoderBuffer(
    616     const cdm::InputBuffer& encrypted_buffer,
    617     scoped_refptr<media::DecoderBuffer>* decrypted_buffer) {
    618   DCHECK(decrypted_buffer);
    619   scoped_refptr<media::DecoderBuffer> buffer =
    620       CopyDecoderBufferFrom(encrypted_buffer);
    621 
    622   if (buffer->end_of_stream()) {
    623     *decrypted_buffer = buffer;
    624     return cdm::kSuccess;
    625   }
    626 
    627   // Callback is called synchronously, so we can use variables on the stack.
    628   media::Decryptor::Status status = media::Decryptor::kError;
    629   // The AesDecryptor does not care what the stream type is. Pass kVideo
    630   // for both audio and video decryption.
    631   decryptor_.Decrypt(
    632       media::Decryptor::kVideo,
    633       buffer,
    634       base::Bind(&CopyDecryptResults, &status, decrypted_buffer));
    635 
    636   if (status == media::Decryptor::kError)
    637     return cdm::kDecryptError;
    638 
    639   if (status == media::Decryptor::kNoKey)
    640     return cdm::kNoKey;
    641 
    642   DCHECK_EQ(status, media::Decryptor::kSuccess);
    643   return cdm::kSuccess;
    644 }
    645 
    646 void ClearKeyCdm::OnPlatformChallengeResponse(
    647     const cdm::PlatformChallengeResponse& response) {
    648   NOTIMPLEMENTED();
    649 }
    650 
    651 void ClearKeyCdm::OnQueryOutputProtectionStatus(
    652     uint32_t link_mask, uint32_t output_protection_mask) {
    653   NOTIMPLEMENTED();
    654 };
    655 
    656 void ClearKeyCdm::LoadLoadableSession() {
    657   std::string jwk_set = GenerateJWKSet(kLoadableSessionKey,
    658                                        sizeof(kLoadableSessionKey),
    659                                        kLoadableSessionKeyId,
    660                                        sizeof(kLoadableSessionKeyId) - 1);
    661   // TODO(xhwang): This triggers OnSessionUpdated(). For prefixed EME support,
    662   // this is okay. Check WD EME support.
    663   scoped_ptr<media::SimpleCdmPromise> promise(new media::SimpleCdmPromise(
    664       base::Bind(&ClearKeyCdm::OnSessionUpdated,
    665                  base::Unretained(this),
    666                  promise_id_for_emulated_loadsession_,
    667                  session_id_for_emulated_loadsession_),
    668       base::Bind(&ClearKeyCdm::OnPromiseFailed,
    669                  base::Unretained(this),
    670                  promise_id_for_emulated_loadsession_)));
    671   decryptor_.UpdateSession(session_id_for_emulated_loadsession_,
    672                            reinterpret_cast<const uint8*>(jwk_set.data()),
    673                            jwk_set.size(),
    674                            promise.Pass());
    675 }
    676 
    677 void ClearKeyCdm::OnSessionMessage(const std::string& web_session_id,
    678                                    const std::vector<uint8>& message,
    679                                    const GURL& destination_url) {
    680   DVLOG(1) << "OnSessionMessage: " << message.size();
    681 
    682   // Ignore the message when we are waiting to update the loadable session.
    683   if (web_session_id == session_id_for_emulated_loadsession_)
    684     return;
    685 
    686   // OnSessionMessage() only called during CreateSession(), so no promise
    687   // involved (OnSessionCreated() called to resolve the CreateSession()
    688   // promise).
    689   host_->OnSessionMessage(web_session_id.data(),
    690                           web_session_id.length(),
    691                           reinterpret_cast<const char*>(message.data()),
    692                           message.size(),
    693                           destination_url.spec().data(),
    694                           destination_url.spec().size());
    695 }
    696 
    697 void ClearKeyCdm::OnSessionKeysChange(const std::string& web_session_id,
    698                                       bool has_additional_usable_key) {
    699   host_->OnSessionUsableKeysChange(web_session_id.data(),
    700                                    web_session_id.length(),
    701                                    has_additional_usable_key);
    702 }
    703 
    704 void ClearKeyCdm::OnSessionClosed(const std::string& web_session_id) {
    705   host_->OnSessionClosed(web_session_id.data(), web_session_id.length());
    706 }
    707 
    708 void ClearKeyCdm::OnSessionCreated(uint32 promise_id,
    709                                    const std::string& web_session_id) {
    710   // Save the latest session ID for heartbeat and file IO test messages.
    711   last_session_id_ = web_session_id;
    712 
    713   host_->OnResolveNewSessionPromise(
    714       promise_id, web_session_id.data(), web_session_id.length());
    715 }
    716 
    717 void ClearKeyCdm::OnSessionLoaded(uint32 promise_id,
    718                                   const std::string& web_session_id) {
    719   // Save the latest session ID for heartbeat and file IO test messages.
    720   last_session_id_ = web_session_id;
    721 
    722   // |decryptor_| created some session as |web_session_id|, but going forward
    723   // we need to map that to |kLoadableWebSessionId|, as that is what callers
    724   // expect.
    725   session_id_for_emulated_loadsession_ = web_session_id;
    726 
    727   // Delay LoadLoadableSession() to test the case where Decrypt*() calls are
    728   // made before the session is fully loaded.
    729   const int64 kDelayToLoadSessionMs = 500;
    730 
    731   // Defer resolving the promise until the session is loaded.
    732   promise_id_for_emulated_loadsession_ = promise_id;
    733 
    734   // Use the address of |session_id_for_emulated_loadsession_| as the timer
    735   // context so that we can call LoadLoadableSession() when the timer expires.
    736   host_->SetTimer(kDelayToLoadSessionMs, &session_id_for_emulated_loadsession_);
    737 }
    738 
    739 void ClearKeyCdm::OnSessionUpdated(uint32 promise_id,
    740                                    const std::string& web_session_id) {
    741   // UpdateSession() may be called to finish loading sessions, so handle
    742   // appropriately.
    743   if (web_session_id == session_id_for_emulated_loadsession_) {
    744     session_id_for_emulated_loadsession_ = std::string();
    745     // |promise_id| is the LoadSession() promise, so resolve appropriately.
    746     host_->OnResolveNewSessionPromise(
    747         promise_id, kLoadableWebSessionId, strlen(kLoadableWebSessionId));
    748     return;
    749   }
    750 
    751   host_->OnResolvePromise(promise_id);
    752 }
    753 
    754 void ClearKeyCdm::OnUsableKeyIdsObtained(uint32 promise_id,
    755                                          const KeyIdsVector& key_ids) {
    756   scoped_ptr<cdm::BinaryData[]> result(new cdm::BinaryData[key_ids.size()]);
    757   for (uint32 i = 0; i < key_ids.size(); ++i) {
    758     result[i].data = key_ids[i].data();
    759     result[i].length = key_ids[i].size();
    760   }
    761   host_->OnResolveKeyIdsPromise(promise_id, result.get(), key_ids.size());
    762 }
    763 
    764 void ClearKeyCdm::OnPromiseResolved(uint32 promise_id) {
    765   host_->OnResolvePromise(promise_id);
    766 }
    767 
    768 void ClearKeyCdm::OnPromiseFailed(uint32 promise_id,
    769                                   MediaKeys::Exception exception_code,
    770                                   uint32 system_code,
    771                                   const std::string& error_message) {
    772   host_->OnRejectPromise(promise_id,
    773                          ConvertException(exception_code),
    774                          system_code,
    775                          error_message.data(),
    776                          error_message.length());
    777 }
    778 
    779 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
    780 int64 ClearKeyCdm::CurrentTimeStampInMicroseconds() const {
    781   return output_timestamp_base_in_microseconds_ +
    782          base::Time::kMicrosecondsPerSecond *
    783          total_samples_generated_  / samples_per_second_;
    784 }
    785 
    786 int ClearKeyCdm::GenerateFakeAudioFramesFromDuration(
    787     int64 duration_in_microseconds,
    788     cdm::AudioFrames* audio_frames) const {
    789   int64 samples_to_generate = static_cast<double>(samples_per_second_) *
    790       duration_in_microseconds / base::Time::kMicrosecondsPerSecond + 0.5;
    791   if (samples_to_generate <= 0)
    792     return 0;
    793 
    794   int64 bytes_per_sample = channel_count_ * bits_per_channel_ / 8;
    795   // |frame_size| must be a multiple of |bytes_per_sample|.
    796   int64 frame_size = bytes_per_sample * samples_to_generate;
    797 
    798   int64 timestamp = CurrentTimeStampInMicroseconds();
    799 
    800   const int kHeaderSize = sizeof(timestamp) + sizeof(frame_size);
    801   audio_frames->SetFrameBuffer(host_->Allocate(kHeaderSize + frame_size));
    802   uint8_t* data = audio_frames->FrameBuffer()->Data();
    803 
    804   memcpy(data, &timestamp, sizeof(timestamp));
    805   data += sizeof(timestamp);
    806   memcpy(data, &frame_size, sizeof(frame_size));
    807   data += sizeof(frame_size);
    808   // You won't hear anything because we have all zeros here. But the video
    809   // should play just fine!
    810   memset(data, 0, frame_size);
    811 
    812   audio_frames->FrameBuffer()->SetSize(kHeaderSize + frame_size);
    813 
    814   return samples_to_generate;
    815 }
    816 
    817 cdm::Status ClearKeyCdm::GenerateFakeAudioFrames(
    818     int64 timestamp_in_microseconds,
    819     cdm::AudioFrames* audio_frames) {
    820   if (timestamp_in_microseconds == kNoTimestamp)
    821     return cdm::kNeedMoreData;
    822 
    823   // Return kNeedMoreData for the first frame because duration is unknown.
    824   if (output_timestamp_base_in_microseconds_ == kNoTimestamp) {
    825     output_timestamp_base_in_microseconds_ = timestamp_in_microseconds;
    826     return cdm::kNeedMoreData;
    827   }
    828 
    829   int samples_generated = GenerateFakeAudioFramesFromDuration(
    830       timestamp_in_microseconds - CurrentTimeStampInMicroseconds(),
    831       audio_frames);
    832   total_samples_generated_ += samples_generated;
    833 
    834   return samples_generated == 0 ? cdm::kNeedMoreData : cdm::kSuccess;
    835 }
    836 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
    837 
    838 void ClearKeyCdm::StartFileIOTest() {
    839   file_io_test_runner_.reset(new FileIOTestRunner(
    840       base::Bind(&ClearKeyCdmHost::CreateFileIO, base::Unretained(host_))));
    841   file_io_test_runner_->RunAllTests(
    842       base::Bind(&ClearKeyCdm::OnFileIOTestComplete, base::Unretained(this)));
    843 }
    844 
    845 void ClearKeyCdm::OnFileIOTestComplete(bool success) {
    846   DVLOG(1) << __FUNCTION__ << ": " << success;
    847   std::string message = GetFileIOTestResultMessage(success);
    848   host_->OnSessionMessage(last_session_id_.data(),
    849                           last_session_id_.length(),
    850                           message.data(),
    851                           message.length(),
    852                           NULL,
    853                           0);
    854   file_io_test_runner_.reset();
    855 }
    856 
    857 }  // namespace media
    858