Home | History | Annotate | Download | only in ppapi
      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/clear_key_cdm.h"
      6 
      7 #include <algorithm>
      8 #include <sstream>
      9 #include <string>
     10 #include <vector>
     11 
     12 #include "base/bind.h"
     13 #include "base/debug/trace_event.h"
     14 #include "base/logging.h"
     15 #include "base/time/time.h"
     16 #include "media/base/decoder_buffer.h"
     17 #include "media/base/decrypt_config.h"
     18 #include "media/cdm/ppapi/cdm_video_decoder.h"
     19 
     20 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
     21 #include "base/basictypes.h"
     22 const int64 kNoTimestamp = kint64min;
     23 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
     24 
     25 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
     26 #include "base/at_exit.h"
     27 #include "base/files/file_path.h"
     28 #include "base/path_service.h"
     29 #include "media/base/media.h"
     30 #include "media/cdm/ppapi/ffmpeg_cdm_audio_decoder.h"
     31 #include "media/cdm/ppapi/ffmpeg_cdm_video_decoder.h"
     32 
     33 // Include FFmpeg avformat.h for av_register_all().
     34 extern "C" {
     35 // Temporarily disable possible loss of data warning.
     36 MSVC_PUSH_DISABLE_WARNING(4244);
     37 #include <libavformat/avformat.h>
     38 MSVC_POP_WARNING();
     39 }  // extern "C"
     40 
     41 // TODO(tomfinegan): When COMPONENT_BUILD is not defined an AtExitManager must
     42 // exist before the call to InitializeFFmpegLibraries(). This should no longer
     43 // be required after http://crbug.com/91970 because we'll be able to get rid of
     44 // InitializeFFmpegLibraries().
     45 #if !defined COMPONENT_BUILD
     46 static base::AtExitManager g_at_exit_manager;
     47 #endif
     48 
     49 // TODO(tomfinegan): InitializeFFmpegLibraries() and |g_cdm_module_initialized|
     50 // are required for running in the sandbox, and should no longer be required
     51 // after http://crbug.com/91970 is fixed.
     52 static bool InitializeFFmpegLibraries() {
     53   base::FilePath file_path;
     54   CHECK(PathService::Get(base::DIR_MODULE, &file_path));
     55   CHECK(media::InitializeMediaLibrary(file_path));
     56   return true;
     57 }
     58 
     59 static bool g_ffmpeg_lib_initialized = InitializeFFmpegLibraries();
     60 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
     61 
     62 const char kClearKeyCdmVersion[] = "0.1.0.1";
     63 const char kExternalClearKeyKeySystem[] = "org.chromium.externalclearkey";
     64 const char kExternalClearKeyDecryptOnlyKeySystem[] =
     65     "org.chromium.externalclearkey.decryptonly";
     66 const int64 kSecondsPerMinute = 60;
     67 const int64 kMsPerSecond = 1000;
     68 const int64 kInitialTimerDelayMs = 200;
     69 const int64 kMaxTimerDelayMs = 1 * kSecondsPerMinute * kMsPerSecond;
     70 // Heart beat message header. If a key message starts with |kHeartBeatHeader|,
     71 // it's a heart beat message. Otherwise, it's a key request.
     72 const char kHeartBeatHeader[] = "HEARTBEAT";
     73 
     74 // Copies |input_buffer| into a media::DecoderBuffer. If the |input_buffer| is
     75 // empty, an empty (end-of-stream) media::DecoderBuffer is returned.
     76 static scoped_refptr<media::DecoderBuffer> CopyDecoderBufferFrom(
     77     const cdm::InputBuffer& input_buffer) {
     78   if (!input_buffer.data) {
     79     DCHECK(!input_buffer.data_size);
     80     return media::DecoderBuffer::CreateEOSBuffer();
     81   }
     82 
     83   // TODO(tomfinegan): Get rid of this copy.
     84   scoped_refptr<media::DecoderBuffer> output_buffer =
     85       media::DecoderBuffer::CopyFrom(input_buffer.data, input_buffer.data_size);
     86 
     87   std::vector<media::SubsampleEntry> subsamples;
     88   for (uint32_t i = 0; i < input_buffer.num_subsamples; ++i) {
     89     media::SubsampleEntry subsample;
     90     subsample.clear_bytes = input_buffer.subsamples[i].clear_bytes;
     91     subsample.cypher_bytes = input_buffer.subsamples[i].cipher_bytes;
     92     subsamples.push_back(subsample);
     93   }
     94 
     95   scoped_ptr<media::DecryptConfig> decrypt_config(new media::DecryptConfig(
     96       std::string(reinterpret_cast<const char*>(input_buffer.key_id),
     97                   input_buffer.key_id_size),
     98       std::string(reinterpret_cast<const char*>(input_buffer.iv),
     99                   input_buffer.iv_size),
    100       input_buffer.data_offset,
    101       subsamples));
    102 
    103   output_buffer->set_decrypt_config(decrypt_config.Pass());
    104   output_buffer->set_timestamp(
    105       base::TimeDelta::FromMicroseconds(input_buffer.timestamp));
    106 
    107   return output_buffer;
    108 }
    109 
    110 template<typename Type>
    111 class ScopedResetter {
    112  public:
    113   explicit ScopedResetter(Type* object) : object_(object) {}
    114   ~ScopedResetter() { object_->Reset(); }
    115 
    116  private:
    117   Type* const object_;
    118 };
    119 
    120 void INITIALIZE_CDM_MODULE() {
    121 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
    122   DVLOG(2) << "FFmpeg libraries initialized: " << g_ffmpeg_lib_initialized;
    123   av_register_all();
    124 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
    125 }
    126 
    127 void DeinitializeCdmModule() {
    128 }
    129 
    130 void* CreateCdmInstance(int cdm_interface_version,
    131                         const char* key_system, uint32_t key_system_size,
    132                         GetCdmHostFunc get_cdm_host_func,
    133                         void* user_data) {
    134   DVLOG(1) << "CreateCdmInstance()";
    135 
    136   std::string key_system_string(key_system, key_system_size);
    137   if (key_system_string != kExternalClearKeyKeySystem &&
    138       key_system_string != kExternalClearKeyDecryptOnlyKeySystem) {
    139     DVLOG(1) << "Unsupported key system:" << key_system_string;
    140     return NULL;
    141   }
    142 
    143   if (cdm_interface_version != media::ClearKeyCdmInterface::kVersion)
    144     return NULL;
    145 
    146   media::ClearKeyCdmHost* host = static_cast<media::ClearKeyCdmHost*>(
    147       get_cdm_host_func(media::ClearKeyCdmHost::kVersion, user_data));
    148   if (!host)
    149     return NULL;
    150 
    151   return new media::ClearKeyCdm(
    152       host, key_system_string == kExternalClearKeyDecryptOnlyKeySystem);
    153 }
    154 
    155 const char* GetCdmVersion() {
    156   return kClearKeyCdmVersion;
    157 }
    158 
    159 namespace media {
    160 
    161 // Since all the calls to AesDecryptor are synchronous, pass a dummy value for
    162 // session_id that is never exposed outside this class.
    163 // TODO(jrummell): Remove usage of this when the CDM interface is updated
    164 // to use session_id.
    165 
    166 ClearKeyCdm::Client::Client()
    167     : status_(kNone), error_code_(MediaKeys::kUnknownError), system_code_(0) {}
    168 
    169 ClearKeyCdm::Client::~Client() {}
    170 
    171 void ClearKeyCdm::Client::Reset() {
    172   status_ = kNone;
    173   web_session_id_.clear();
    174   message_.clear();
    175   destination_url_.clear();
    176   error_code_ = MediaKeys::kUnknownError;
    177   system_code_ = 0;
    178 }
    179 
    180 void ClearKeyCdm::Client::OnSessionCreated(uint32 session_id,
    181                                            const std::string& web_session_id) {
    182   status_ = static_cast<Status>(status_ | kCreated);
    183   web_session_id_ = web_session_id;
    184 }
    185 
    186 void ClearKeyCdm::Client::OnSessionMessage(uint32 session_id,
    187                                            const std::vector<uint8>& message,
    188                                            const std::string& destination_url) {
    189   status_ = static_cast<Status>(status_ | kMessage);
    190   message_ = message;
    191   destination_url_ = destination_url;
    192 }
    193 
    194 void ClearKeyCdm::Client::OnSessionReady(uint32 session_id) {
    195   status_ = static_cast<Status>(status_ | kReady);
    196 }
    197 
    198 void ClearKeyCdm::Client::OnSessionClosed(uint32 session_id) {
    199   status_ = static_cast<Status>(status_ | kClosed);
    200 }
    201 
    202 void ClearKeyCdm::Client::OnSessionError(uint32 session_id,
    203                                          media::MediaKeys::KeyError error_code,
    204                                          int system_code) {
    205   status_ = static_cast<Status>(status_ | kError);
    206   error_code_ = error_code;
    207   system_code_ = system_code;
    208 }
    209 
    210 ClearKeyCdm::ClearKeyCdm(ClearKeyCdmHost* host, bool is_decrypt_only)
    211     : decryptor_(
    212           base::Bind(&Client::OnSessionCreated, base::Unretained(&client_)),
    213           base::Bind(&Client::OnSessionMessage, base::Unretained(&client_)),
    214           base::Bind(&Client::OnSessionReady, base::Unretained(&client_)),
    215           base::Bind(&Client::OnSessionClosed, base::Unretained(&client_)),
    216           base::Bind(&Client::OnSessionError, base::Unretained(&client_))),
    217       host_(host),
    218       is_decrypt_only_(is_decrypt_only),
    219       timer_delay_ms_(kInitialTimerDelayMs),
    220       timer_set_(false) {
    221 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
    222   channel_count_ = 0;
    223   bits_per_channel_ = 0;
    224   samples_per_second_ = 0;
    225   output_timestamp_base_in_microseconds_ = kNoTimestamp;
    226   total_samples_generated_ = 0;
    227 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
    228 }
    229 
    230 ClearKeyCdm::~ClearKeyCdm() {}
    231 
    232 cdm::Status ClearKeyCdm::GenerateKeyRequest(const char* type,
    233                                             uint32_t type_size,
    234                                             const uint8_t* init_data,
    235                                             uint32_t init_data_size) {
    236   DVLOG(1) << "GenerateKeyRequest()";
    237   base::AutoLock auto_lock(client_lock_);
    238   ScopedResetter<Client> auto_resetter(&client_);
    239   decryptor_.CreateSession(MediaKeys::kInvalidSessionId,
    240                            std::string(type, type_size),
    241                            init_data, init_data_size);
    242 
    243   if (client_.status() != (Client::kMessage | Client::kCreated)) {
    244     // Use values returned to client if possible.
    245     host_->SendKeyError(client_.web_session_id().data(),
    246                         client_.web_session_id().size(),
    247                         static_cast<cdm::MediaKeyError>(client_.error_code()),
    248                         client_.system_code());
    249     return cdm::kSessionError;
    250   }
    251 
    252   host_->SendKeyMessage(
    253       client_.web_session_id().data(), client_.web_session_id().size(),
    254       reinterpret_cast<const char*>(&client_.message()[0]),
    255       client_.message().size(),
    256       client_.destination_url().data(), client_.destination_url().size());
    257 
    258   // Only save the latest session ID for heartbeat messages.
    259   heartbeat_session_id_ = client_.web_session_id();
    260 
    261   return cdm::kSuccess;
    262 }
    263 
    264 cdm::Status ClearKeyCdm::AddKey(const char* session_id,
    265                                 uint32_t session_id_size,
    266                                 const uint8_t* key,
    267                                 uint32_t key_size,
    268                                 const uint8_t* key_id,
    269                                 uint32_t key_id_size) {
    270   DVLOG(1) << "AddKey()";
    271   DCHECK(!key_id && !key_id_size);
    272   base::AutoLock auto_lock(client_lock_);
    273   ScopedResetter<Client> auto_resetter(&client_);
    274   decryptor_.UpdateSession(MediaKeys::kInvalidSessionId, key, key_size);
    275 
    276   if (client_.status() != Client::kReady) {
    277     host_->SendKeyError(session_id, session_id_size,
    278                         static_cast<cdm::MediaKeyError>(client_.error_code()),
    279                         client_.system_code());
    280     return cdm::kSessionError;
    281   }
    282 
    283   if (!timer_set_) {
    284     ScheduleNextHeartBeat();
    285     timer_set_ = true;
    286   }
    287 
    288   return cdm::kSuccess;
    289 }
    290 
    291 cdm::Status ClearKeyCdm::CancelKeyRequest(const char* session_id,
    292                                           uint32_t session_id_size) {
    293   DVLOG(1) << "CancelKeyRequest()";
    294   base::AutoLock auto_lock(client_lock_);
    295   ScopedResetter<Client> auto_resetter(&client_);
    296   decryptor_.ReleaseSession(MediaKeys::kInvalidSessionId);
    297 
    298   // No message normally sent by Release(), but if an error occurred,
    299   // report it as a failure.
    300   if (client_.status() == Client::kError) {
    301     host_->SendKeyError(session_id, session_id_size,
    302                         static_cast<cdm::MediaKeyError>(client_.error_code()),
    303                         client_.system_code());
    304     return cdm::kSessionError;
    305   }
    306 
    307   return cdm::kSuccess;
    308 }
    309 
    310 void ClearKeyCdm::TimerExpired(void* context) {
    311   std::string heartbeat_message;
    312   if (!next_heartbeat_message_.empty() &&
    313       context == &next_heartbeat_message_[0]) {
    314     heartbeat_message = next_heartbeat_message_;
    315   } else {
    316     heartbeat_message = "ERROR: Invalid timer context found!";
    317   }
    318 
    319   // This URL is only used for testing the code path for defaultURL.
    320   // There is no service at this URL, so applications should ignore it.
    321   const char url[] = "http://test.externalclearkey.chromium.org";
    322 
    323   host_->SendKeyMessage(
    324       heartbeat_session_id_.data(), heartbeat_session_id_.size(),
    325       heartbeat_message.data(), heartbeat_message.size(),
    326       url, arraysize(url) - 1);
    327 
    328   ScheduleNextHeartBeat();
    329 }
    330 
    331 static void CopyDecryptResults(
    332     media::Decryptor::Status* status_copy,
    333     scoped_refptr<media::DecoderBuffer>* buffer_copy,
    334     media::Decryptor::Status status,
    335     const scoped_refptr<media::DecoderBuffer>& buffer) {
    336   *status_copy = status;
    337   *buffer_copy = buffer;
    338 }
    339 
    340 cdm::Status ClearKeyCdm::Decrypt(
    341     const cdm::InputBuffer& encrypted_buffer,
    342     cdm::DecryptedBlock* decrypted_block) {
    343   DVLOG(1) << "Decrypt()";
    344   DCHECK(encrypted_buffer.data);
    345 
    346   scoped_refptr<media::DecoderBuffer> buffer;
    347   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
    348 
    349   if (status != cdm::kSuccess)
    350     return status;
    351 
    352   DCHECK(buffer->data());
    353   decrypted_block->SetDecryptedBuffer(
    354       host_->Allocate(buffer->data_size()));
    355   memcpy(reinterpret_cast<void*>(decrypted_block->DecryptedBuffer()->Data()),
    356          buffer->data(),
    357          buffer->data_size());
    358   decrypted_block->DecryptedBuffer()->SetSize(buffer->data_size());
    359   decrypted_block->SetTimestamp(buffer->timestamp().InMicroseconds());
    360 
    361   return cdm::kSuccess;
    362 }
    363 
    364 cdm::Status ClearKeyCdm::InitializeAudioDecoder(
    365     const cdm::AudioDecoderConfig& audio_decoder_config) {
    366   if (is_decrypt_only_)
    367     return cdm::kSessionError;
    368 
    369 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
    370   if (!audio_decoder_)
    371     audio_decoder_.reset(new media::FFmpegCdmAudioDecoder(host_));
    372 
    373   if (!audio_decoder_->Initialize(audio_decoder_config))
    374     return cdm::kSessionError;
    375 
    376   return cdm::kSuccess;
    377 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
    378   channel_count_ = audio_decoder_config.channel_count;
    379   bits_per_channel_ = audio_decoder_config.bits_per_channel;
    380   samples_per_second_ = audio_decoder_config.samples_per_second;
    381   return cdm::kSuccess;
    382 #else
    383   NOTIMPLEMENTED();
    384   return cdm::kSessionError;
    385 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
    386 }
    387 
    388 cdm::Status ClearKeyCdm::InitializeVideoDecoder(
    389     const cdm::VideoDecoderConfig& video_decoder_config) {
    390   if (is_decrypt_only_)
    391     return cdm::kSessionError;
    392 
    393   if (video_decoder_ && video_decoder_->is_initialized()) {
    394     DCHECK(!video_decoder_->is_initialized());
    395     return cdm::kSessionError;
    396   }
    397 
    398   // Any uninitialized decoder will be replaced.
    399   video_decoder_ = CreateVideoDecoder(host_, video_decoder_config);
    400   if (!video_decoder_)
    401     return cdm::kSessionError;
    402 
    403   return cdm::kSuccess;
    404 }
    405 
    406 void ClearKeyCdm::ResetDecoder(cdm::StreamType decoder_type) {
    407   DVLOG(1) << "ResetDecoder()";
    408 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
    409   switch (decoder_type) {
    410     case cdm::kStreamTypeVideo:
    411       video_decoder_->Reset();
    412       break;
    413     case cdm::kStreamTypeAudio:
    414       audio_decoder_->Reset();
    415       break;
    416     default:
    417       NOTREACHED() << "ResetDecoder(): invalid cdm::StreamType";
    418   }
    419 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
    420   if (decoder_type == cdm::kStreamTypeAudio) {
    421     output_timestamp_base_in_microseconds_ = kNoTimestamp;
    422     total_samples_generated_ = 0;
    423   }
    424 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
    425 }
    426 
    427 void ClearKeyCdm::DeinitializeDecoder(cdm::StreamType decoder_type) {
    428   DVLOG(1) << "DeinitializeDecoder()";
    429   switch (decoder_type) {
    430     case cdm::kStreamTypeVideo:
    431       video_decoder_->Deinitialize();
    432       break;
    433     case cdm::kStreamTypeAudio:
    434 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
    435       audio_decoder_->Deinitialize();
    436 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
    437       output_timestamp_base_in_microseconds_ = kNoTimestamp;
    438       total_samples_generated_ = 0;
    439 #endif
    440       break;
    441     default:
    442       NOTREACHED() << "DeinitializeDecoder(): invalid cdm::StreamType";
    443   }
    444 }
    445 
    446 cdm::Status ClearKeyCdm::DecryptAndDecodeFrame(
    447     const cdm::InputBuffer& encrypted_buffer,
    448     cdm::VideoFrame* decoded_frame) {
    449   DVLOG(1) << "DecryptAndDecodeFrame()";
    450   TRACE_EVENT0("media", "ClearKeyCdm::DecryptAndDecodeFrame");
    451 
    452   scoped_refptr<media::DecoderBuffer> buffer;
    453   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
    454 
    455   if (status != cdm::kSuccess)
    456     return status;
    457 
    458   const uint8_t* data = NULL;
    459   int32_t size = 0;
    460   int64_t timestamp = 0;
    461   if (!buffer->end_of_stream()) {
    462     data = buffer->data();
    463     size = buffer->data_size();
    464     timestamp = encrypted_buffer.timestamp;
    465   }
    466 
    467   return video_decoder_->DecodeFrame(data, size, timestamp, decoded_frame);
    468 }
    469 
    470 cdm::Status ClearKeyCdm::DecryptAndDecodeSamples(
    471     const cdm::InputBuffer& encrypted_buffer,
    472     cdm::AudioFrames* audio_frames) {
    473   DVLOG(1) << "DecryptAndDecodeSamples()";
    474 
    475   scoped_refptr<media::DecoderBuffer> buffer;
    476   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
    477 
    478   if (status != cdm::kSuccess)
    479     return status;
    480 
    481 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
    482   const uint8_t* data = NULL;
    483   int32_t size = 0;
    484   int64_t timestamp = 0;
    485   if (!buffer->end_of_stream()) {
    486     data = buffer->data();
    487     size = buffer->data_size();
    488     timestamp = encrypted_buffer.timestamp;
    489   }
    490 
    491   return audio_decoder_->DecodeBuffer(data, size, timestamp, audio_frames);
    492 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
    493   int64 timestamp_in_microseconds = kNoTimestamp;
    494   if (!buffer->end_of_stream()) {
    495     timestamp_in_microseconds = buffer->GetTimestamp().InMicroseconds();
    496     DCHECK(timestamp_in_microseconds != kNoTimestamp);
    497   }
    498   return GenerateFakeAudioFrames(timestamp_in_microseconds, audio_frames);
    499 #else
    500   return cdm::kSuccess;
    501 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
    502 }
    503 
    504 void ClearKeyCdm::Destroy() {
    505   DVLOG(1) << "Destroy()";
    506   delete this;
    507 }
    508 
    509 void ClearKeyCdm::ScheduleNextHeartBeat() {
    510   // Prepare the next heartbeat message and set timer.
    511   std::ostringstream msg_stream;
    512   msg_stream << kHeartBeatHeader << " from ClearKey CDM set at time "
    513              << host_->GetCurrentWallTimeInSeconds() << ".";
    514   next_heartbeat_message_ = msg_stream.str();
    515 
    516   host_->SetTimer(timer_delay_ms_, &next_heartbeat_message_[0]);
    517 
    518   // Use a smaller timer delay at start-up to facilitate testing. Increase the
    519   // timer delay up to a limit to avoid message spam.
    520   if (timer_delay_ms_ < kMaxTimerDelayMs)
    521     timer_delay_ms_ = std::min(2 * timer_delay_ms_, kMaxTimerDelayMs);
    522 }
    523 
    524 cdm::Status ClearKeyCdm::DecryptToMediaDecoderBuffer(
    525     const cdm::InputBuffer& encrypted_buffer,
    526     scoped_refptr<media::DecoderBuffer>* decrypted_buffer) {
    527   DCHECK(decrypted_buffer);
    528   scoped_refptr<media::DecoderBuffer> buffer =
    529       CopyDecoderBufferFrom(encrypted_buffer);
    530 
    531   if (buffer->end_of_stream()) {
    532     *decrypted_buffer = buffer;
    533     return cdm::kSuccess;
    534   }
    535 
    536   // Callback is called synchronously, so we can use variables on the stack.
    537   media::Decryptor::Status status = media::Decryptor::kError;
    538   // The AesDecryptor does not care what the stream type is. Pass kVideo
    539   // for both audio and video decryption.
    540   decryptor_.Decrypt(
    541       media::Decryptor::kVideo,
    542       buffer,
    543       base::Bind(&CopyDecryptResults, &status, decrypted_buffer));
    544 
    545   if (status == media::Decryptor::kError)
    546     return cdm::kDecryptError;
    547 
    548   if (status == media::Decryptor::kNoKey)
    549     return cdm::kNoKey;
    550 
    551   DCHECK_EQ(status, media::Decryptor::kSuccess);
    552   return cdm::kSuccess;
    553 }
    554 
    555 void ClearKeyCdm::OnPlatformChallengeResponse(
    556     const cdm::PlatformChallengeResponse& response) {
    557   NOTIMPLEMENTED();
    558 }
    559 
    560 void ClearKeyCdm::OnQueryOutputProtectionStatus(
    561     uint32_t link_mask, uint32_t output_protection_mask) {
    562   NOTIMPLEMENTED();
    563 };
    564 
    565 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
    566 int64 ClearKeyCdm::CurrentTimeStampInMicroseconds() const {
    567   return output_timestamp_base_in_microseconds_ +
    568          base::Time::kMicrosecondsPerSecond *
    569          total_samples_generated_  / samples_per_second_;
    570 }
    571 
    572 int ClearKeyCdm::GenerateFakeAudioFramesFromDuration(
    573     int64 duration_in_microseconds,
    574     cdm::AudioFrames* audio_frames) const {
    575   int64 samples_to_generate = static_cast<double>(samples_per_second_) *
    576       duration_in_microseconds / base::Time::kMicrosecondsPerSecond + 0.5;
    577   if (samples_to_generate <= 0)
    578     return 0;
    579 
    580   int64 bytes_per_sample = channel_count_ * bits_per_channel_ / 8;
    581   // |frame_size| must be a multiple of |bytes_per_sample|.
    582   int64 frame_size = bytes_per_sample * samples_to_generate;
    583 
    584   int64 timestamp = CurrentTimeStampInMicroseconds();
    585 
    586   const int kHeaderSize = sizeof(timestamp) + sizeof(frame_size);
    587   audio_frames->SetFrameBuffer(host_->Allocate(kHeaderSize + frame_size));
    588   uint8_t* data = audio_frames->FrameBuffer()->Data();
    589 
    590   memcpy(data, &timestamp, sizeof(timestamp));
    591   data += sizeof(timestamp);
    592   memcpy(data, &frame_size, sizeof(frame_size));
    593   data += sizeof(frame_size);
    594   // You won't hear anything because we have all zeros here. But the video
    595   // should play just fine!
    596   memset(data, 0, frame_size);
    597 
    598   audio_frames->FrameBuffer()->SetSize(kHeaderSize + frame_size);
    599 
    600   return samples_to_generate;
    601 }
    602 
    603 cdm::Status ClearKeyCdm::GenerateFakeAudioFrames(
    604     int64 timestamp_in_microseconds,
    605     cdm::AudioFrames* audio_frames) {
    606   if (timestamp_in_microseconds == kNoTimestamp)
    607     return cdm::kNeedMoreData;
    608 
    609   // Return kNeedMoreData for the first frame because duration is unknown.
    610   if (output_timestamp_base_in_microseconds_ == kNoTimestamp) {
    611     output_timestamp_base_in_microseconds_ = timestamp_in_microseconds;
    612     return cdm::kNeedMoreData;
    613   }
    614 
    615   int samples_generated = GenerateFakeAudioFramesFromDuration(
    616       timestamp_in_microseconds - CurrentTimeStampInMicroseconds(),
    617       audio_frames);
    618   total_samples_generated_ += samples_generated;
    619 
    620   return samples_generated == 0 ? cdm::kNeedMoreData : cdm::kSuccess;
    621 }
    622 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
    623 
    624 }  // namespace media
    625