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 static 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 static const char kClearKeyCdmVersion[] = "0.1.0.1";
     63 static const char kExternalClearKey[] = "org.chromium.externalclearkey";
     64 static const int64 kSecondsPerMinute = 60;
     65 static const int64 kMsPerSecond = 1000;
     66 static const int64 kInitialTimerDelayMs = 200;
     67 static const int64 kMaxTimerDelayMs = 1 * kSecondsPerMinute * kMsPerSecond;
     68 // Heart beat message header. If a key message starts with |kHeartBeatHeader|,
     69 // it's a heart beat message. Otherwise, it's a key request.
     70 static const char kHeartBeatHeader[] = "HEARTBEAT";
     71 
     72 // Copies |input_buffer| into a media::DecoderBuffer. If the |input_buffer| is
     73 // empty, an empty (end-of-stream) media::DecoderBuffer is returned.
     74 static scoped_refptr<media::DecoderBuffer> CopyDecoderBufferFrom(
     75     const cdm::InputBuffer& input_buffer) {
     76   if (!input_buffer.data) {
     77     DCHECK_EQ(input_buffer.data_size, 0);
     78     return media::DecoderBuffer::CreateEOSBuffer();
     79   }
     80 
     81   // TODO(tomfinegan): Get rid of this copy.
     82   scoped_refptr<media::DecoderBuffer> output_buffer =
     83       media::DecoderBuffer::CopyFrom(input_buffer.data, input_buffer.data_size);
     84 
     85   std::vector<media::SubsampleEntry> subsamples;
     86   for (int32_t i = 0; i < input_buffer.num_subsamples; ++i) {
     87     media::SubsampleEntry subsample;
     88     subsample.clear_bytes = input_buffer.subsamples[i].clear_bytes;
     89     subsample.cypher_bytes = input_buffer.subsamples[i].cipher_bytes;
     90     subsamples.push_back(subsample);
     91   }
     92 
     93   scoped_ptr<media::DecryptConfig> decrypt_config(new media::DecryptConfig(
     94       std::string(reinterpret_cast<const char*>(input_buffer.key_id),
     95                   input_buffer.key_id_size),
     96       std::string(reinterpret_cast<const char*>(input_buffer.iv),
     97                   input_buffer.iv_size),
     98       input_buffer.data_offset,
     99       subsamples));
    100 
    101   output_buffer->set_decrypt_config(decrypt_config.Pass());
    102   output_buffer->set_timestamp(
    103       base::TimeDelta::FromMicroseconds(input_buffer.timestamp));
    104 
    105   return output_buffer;
    106 }
    107 
    108 template<typename Type>
    109 class ScopedResetter {
    110  public:
    111   explicit ScopedResetter(Type* object) : object_(object) {}
    112   ~ScopedResetter() { object_->Reset(); }
    113 
    114  private:
    115   Type* const object_;
    116 };
    117 
    118 void INITIALIZE_CDM_MODULE() {
    119 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
    120   DVLOG(2) << "FFmpeg libraries initialized: " << g_ffmpeg_lib_initialized;
    121   av_register_all();
    122 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
    123 }
    124 
    125 void DeinitializeCdmModule() {
    126 }
    127 
    128 void* CreateCdmInstance(
    129     int cdm_interface_version,
    130     const char* key_system, int key_system_size,
    131     GetCdmHostFunc get_cdm_host_func, void* user_data) {
    132   DVLOG(1) << "CreateCdmInstance()";
    133 
    134   if (cdm_interface_version != cdm::kCdmInterfaceVersion)
    135     return NULL;
    136 
    137   cdm::Host* host = static_cast<cdm::Host*>(
    138       get_cdm_host_func(cdm::kHostInterfaceVersion, user_data));
    139   if (!host)
    140     return NULL;
    141 
    142   return static_cast<cdm::ContentDecryptionModule*>(
    143       new media::ClearKeyCdm(host));
    144 }
    145 
    146 const char* GetCdmVersion() {
    147   return kClearKeyCdmVersion;
    148 }
    149 
    150 namespace media {
    151 
    152 ClearKeyCdm::Client::Client() : status_(kKeyError) {}
    153 
    154 ClearKeyCdm::Client::~Client() {}
    155 
    156 void ClearKeyCdm::Client::Reset() {
    157   status_ = kKeyError;
    158   session_id_.clear();
    159   key_message_.clear();
    160   default_url_.clear();
    161 }
    162 
    163 void ClearKeyCdm::Client::KeyAdded(const std::string& session_id) {
    164   status_ = kKeyAdded;
    165   session_id_ = session_id;
    166 }
    167 
    168 void ClearKeyCdm::Client::KeyError(const std::string& session_id,
    169                                    media::MediaKeys::KeyError error_code,
    170                                    int system_code) {
    171   status_ = kKeyError;
    172   session_id_ = session_id;
    173 }
    174 
    175 void ClearKeyCdm::Client::KeyMessage(const std::string& session_id,
    176                                      const std::vector<uint8>& message,
    177                                      const std::string& default_url) {
    178   status_ = kKeyMessage;
    179   session_id_ = session_id;
    180   key_message_ = message;
    181   default_url_ = default_url;
    182 }
    183 
    184 ClearKeyCdm::ClearKeyCdm(cdm::Host* host)
    185     : decryptor_(base::Bind(&Client::KeyAdded, base::Unretained(&client_)),
    186                  base::Bind(&Client::KeyError, base::Unretained(&client_)),
    187                  base::Bind(&Client::KeyMessage, base::Unretained(&client_))),
    188       host_(host),
    189       timer_delay_ms_(kInitialTimerDelayMs),
    190       timer_set_(false) {
    191 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
    192   channel_count_ = 0;
    193   bits_per_channel_ = 0;
    194   samples_per_second_ = 0;
    195   output_timestamp_base_in_microseconds_ = kNoTimestamp;
    196   total_samples_generated_ = 0;
    197 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
    198 }
    199 
    200 ClearKeyCdm::~ClearKeyCdm() {}
    201 
    202 cdm::Status ClearKeyCdm::GenerateKeyRequest(const char* type, int type_size,
    203                                             const uint8_t* init_data,
    204                                             int init_data_size) {
    205   DVLOG(1) << "GenerateKeyRequest()";
    206   base::AutoLock auto_lock(client_lock_);
    207   ScopedResetter<Client> auto_resetter(&client_);
    208   decryptor_.GenerateKeyRequest(std::string(type, type_size),
    209                                 init_data, init_data_size);
    210 
    211   if (client_.status() != Client::kKeyMessage) {
    212     host_->SendKeyError(NULL, 0, cdm::kUnknownError, 0);
    213     return cdm::kSessionError;
    214   }
    215 
    216   host_->SendKeyMessage(
    217       client_.session_id().data(), client_.session_id().size(),
    218       reinterpret_cast<const char*>(&client_.key_message()[0]),
    219       client_.key_message().size(),
    220       client_.default_url().data(), client_.default_url().size());
    221 
    222   // Only save the latest session ID for heartbeat messages.
    223   heartbeat_session_id_ = client_.session_id();
    224 
    225   return cdm::kSuccess;
    226 }
    227 
    228 cdm::Status ClearKeyCdm::AddKey(const char* session_id,
    229                                 int session_id_size,
    230                                 const uint8_t* key,
    231                                 int key_size,
    232                                 const uint8_t* key_id,
    233                                 int key_id_size) {
    234   DVLOG(1) << "AddKey()";
    235   base::AutoLock auto_lock(client_lock_);
    236   ScopedResetter<Client> auto_resetter(&client_);
    237   decryptor_.AddKey(key, key_size, key_id, key_id_size,
    238                     std::string(session_id, session_id_size));
    239 
    240   if (client_.status() != Client::kKeyAdded)
    241     return cdm::kSessionError;
    242 
    243   if (!timer_set_) {
    244     ScheduleNextHeartBeat();
    245     timer_set_ = true;
    246   }
    247 
    248   return cdm::kSuccess;
    249 }
    250 
    251 cdm::Status ClearKeyCdm::CancelKeyRequest(const char* session_id,
    252                                           int session_id_size) {
    253   DVLOG(1) << "CancelKeyRequest()";
    254   base::AutoLock auto_lock(client_lock_);
    255   ScopedResetter<Client> auto_resetter(&client_);
    256   decryptor_.CancelKeyRequest(std::string(session_id, session_id_size));
    257   return cdm::kSuccess;
    258 }
    259 
    260 void ClearKeyCdm::TimerExpired(void* context) {
    261   std::string heartbeat_message;
    262   if (!next_heartbeat_message_.empty() &&
    263       context == &next_heartbeat_message_[0]) {
    264     heartbeat_message = next_heartbeat_message_;
    265   } else {
    266     heartbeat_message = "ERROR: Invalid timer context found!";
    267   }
    268 
    269   // This URL is only used for testing the code path for defaultURL.
    270   // There is no service at this URL, so applications should ignore it.
    271   const char url[] = "http://test.externalclearkey.chromium.org";
    272 
    273   host_->SendKeyMessage(
    274       heartbeat_session_id_.data(), heartbeat_session_id_.size(),
    275       heartbeat_message.data(), heartbeat_message.size(),
    276       url, arraysize(url) - 1);
    277 
    278   ScheduleNextHeartBeat();
    279 }
    280 
    281 static void CopyDecryptResults(
    282     media::Decryptor::Status* status_copy,
    283     scoped_refptr<media::DecoderBuffer>* buffer_copy,
    284     media::Decryptor::Status status,
    285     const scoped_refptr<media::DecoderBuffer>& buffer) {
    286   *status_copy = status;
    287   *buffer_copy = buffer;
    288 }
    289 
    290 cdm::Status ClearKeyCdm::Decrypt(
    291     const cdm::InputBuffer& encrypted_buffer,
    292     cdm::DecryptedBlock* decrypted_block) {
    293   DVLOG(1) << "Decrypt()";
    294   DCHECK(encrypted_buffer.data);
    295 
    296   scoped_refptr<media::DecoderBuffer> buffer;
    297   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
    298 
    299   if (status != cdm::kSuccess)
    300     return status;
    301 
    302   DCHECK(buffer->data());
    303   decrypted_block->SetDecryptedBuffer(
    304       host_->Allocate(buffer->data_size()));
    305   memcpy(reinterpret_cast<void*>(decrypted_block->DecryptedBuffer()->Data()),
    306          buffer->data(),
    307          buffer->data_size());
    308   decrypted_block->DecryptedBuffer()->SetSize(buffer->data_size());
    309   decrypted_block->SetTimestamp(buffer->timestamp().InMicroseconds());
    310 
    311   return cdm::kSuccess;
    312 }
    313 
    314 cdm::Status ClearKeyCdm::InitializeAudioDecoder(
    315     const cdm::AudioDecoderConfig& audio_decoder_config) {
    316 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
    317   if (!audio_decoder_)
    318     audio_decoder_.reset(new media::FFmpegCdmAudioDecoder(host_));
    319 
    320   if (!audio_decoder_->Initialize(audio_decoder_config))
    321     return cdm::kSessionError;
    322 
    323   return cdm::kSuccess;
    324 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
    325   channel_count_ = audio_decoder_config.channel_count;
    326   bits_per_channel_ = audio_decoder_config.bits_per_channel;
    327   samples_per_second_ = audio_decoder_config.samples_per_second;
    328   return cdm::kSuccess;
    329 #else
    330   NOTIMPLEMENTED();
    331   return cdm::kSessionError;
    332 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
    333 }
    334 
    335 cdm::Status ClearKeyCdm::InitializeVideoDecoder(
    336     const cdm::VideoDecoderConfig& video_decoder_config) {
    337   if (video_decoder_ && video_decoder_->is_initialized()) {
    338     DCHECK(!video_decoder_->is_initialized());
    339     return cdm::kSessionError;
    340   }
    341 
    342   // Any uninitialized decoder will be replaced.
    343   video_decoder_ = CreateVideoDecoder(host_, video_decoder_config);
    344   if (!video_decoder_)
    345     return cdm::kSessionError;
    346 
    347   return cdm::kSuccess;
    348 }
    349 
    350 void ClearKeyCdm::ResetDecoder(cdm::StreamType decoder_type) {
    351   DVLOG(1) << "ResetDecoder()";
    352 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
    353   switch (decoder_type) {
    354     case cdm::kStreamTypeVideo:
    355       video_decoder_->Reset();
    356       break;
    357     case cdm::kStreamTypeAudio:
    358       audio_decoder_->Reset();
    359       break;
    360     default:
    361       NOTREACHED() << "ResetDecoder(): invalid cdm::StreamType";
    362   }
    363 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
    364   if (decoder_type == cdm::kStreamTypeAudio) {
    365     output_timestamp_base_in_microseconds_ = kNoTimestamp;
    366     total_samples_generated_ = 0;
    367   }
    368 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
    369 }
    370 
    371 void ClearKeyCdm::DeinitializeDecoder(cdm::StreamType decoder_type) {
    372   DVLOG(1) << "DeinitializeDecoder()";
    373   switch (decoder_type) {
    374     case cdm::kStreamTypeVideo:
    375       video_decoder_->Deinitialize();
    376       break;
    377     case cdm::kStreamTypeAudio:
    378 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
    379       audio_decoder_->Deinitialize();
    380 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
    381       output_timestamp_base_in_microseconds_ = kNoTimestamp;
    382       total_samples_generated_ = 0;
    383 #endif
    384       break;
    385     default:
    386       NOTREACHED() << "DeinitializeDecoder(): invalid cdm::StreamType";
    387   }
    388 }
    389 
    390 cdm::Status ClearKeyCdm::DecryptAndDecodeFrame(
    391     const cdm::InputBuffer& encrypted_buffer,
    392     cdm::VideoFrame* decoded_frame) {
    393   DVLOG(1) << "DecryptAndDecodeFrame()";
    394   TRACE_EVENT0("eme", "ClearKeyCdm::DecryptAndDecodeFrame");
    395 
    396   scoped_refptr<media::DecoderBuffer> buffer;
    397   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
    398 
    399   if (status != cdm::kSuccess)
    400     return status;
    401 
    402   const uint8_t* data = NULL;
    403   int32_t size = 0;
    404   int64_t timestamp = 0;
    405   if (!buffer->end_of_stream()) {
    406     data = buffer->data();
    407     size = buffer->data_size();
    408     timestamp = encrypted_buffer.timestamp;
    409   }
    410 
    411   return video_decoder_->DecodeFrame(data, size, timestamp, decoded_frame);
    412 }
    413 
    414 cdm::Status ClearKeyCdm::DecryptAndDecodeSamples(
    415     const cdm::InputBuffer& encrypted_buffer,
    416     cdm::AudioFrames* audio_frames) {
    417   DVLOG(1) << "DecryptAndDecodeSamples()";
    418 
    419   scoped_refptr<media::DecoderBuffer> buffer;
    420   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
    421 
    422   if (status != cdm::kSuccess)
    423     return status;
    424 
    425 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
    426   const uint8_t* data = NULL;
    427   int32_t size = 0;
    428   int64_t timestamp = 0;
    429   if (!buffer->end_of_stream()) {
    430     data = buffer->data();
    431     size = buffer->data_size();
    432     timestamp = encrypted_buffer.timestamp;
    433   }
    434 
    435   return audio_decoder_->DecodeBuffer(data, size, timestamp, audio_frames);
    436 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
    437   int64 timestamp_in_microseconds = kNoTimestamp;
    438   if (!buffer->end_of_stream()) {
    439     timestamp_in_microseconds = buffer->GetTimestamp().InMicroseconds();
    440     DCHECK(timestamp_in_microseconds != kNoTimestamp);
    441   }
    442   return GenerateFakeAudioFrames(timestamp_in_microseconds, audio_frames);
    443 #else
    444   return cdm::kSuccess;
    445 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
    446 }
    447 
    448 void ClearKeyCdm::Destroy() {
    449   DVLOG(1) << "Destroy()";
    450   delete this;
    451 }
    452 
    453 void ClearKeyCdm::ScheduleNextHeartBeat() {
    454   // Prepare the next heartbeat message and set timer.
    455   std::ostringstream msg_stream;
    456   msg_stream << kHeartBeatHeader << " from ClearKey CDM set at time "
    457              << host_->GetCurrentWallTimeInSeconds() << ".";
    458   next_heartbeat_message_ = msg_stream.str();
    459 
    460   host_->SetTimer(timer_delay_ms_, &next_heartbeat_message_[0]);
    461 
    462   // Use a smaller timer delay at start-up to facilitate testing. Increase the
    463   // timer delay up to a limit to avoid message spam.
    464   if (timer_delay_ms_ < kMaxTimerDelayMs)
    465     timer_delay_ms_ = std::min(2 * timer_delay_ms_, kMaxTimerDelayMs);
    466 }
    467 
    468 cdm::Status ClearKeyCdm::DecryptToMediaDecoderBuffer(
    469     const cdm::InputBuffer& encrypted_buffer,
    470     scoped_refptr<media::DecoderBuffer>* decrypted_buffer) {
    471   DCHECK(decrypted_buffer);
    472   scoped_refptr<media::DecoderBuffer> buffer =
    473       CopyDecoderBufferFrom(encrypted_buffer);
    474 
    475   if (buffer->end_of_stream()) {
    476     *decrypted_buffer = buffer;
    477     return cdm::kSuccess;
    478   }
    479 
    480   // Callback is called synchronously, so we can use variables on the stack.
    481   media::Decryptor::Status status = media::Decryptor::kError;
    482   // The AesDecryptor does not care what the stream type is. Pass kVideo
    483   // for both audio and video decryption.
    484   decryptor_.Decrypt(
    485       media::Decryptor::kVideo,
    486       buffer,
    487       base::Bind(&CopyDecryptResults, &status, decrypted_buffer));
    488 
    489   if (status == media::Decryptor::kError)
    490     return cdm::kDecryptError;
    491 
    492   if (status == media::Decryptor::kNoKey)
    493     return cdm::kNoKey;
    494 
    495   DCHECK_EQ(status, media::Decryptor::kSuccess);
    496   return cdm::kSuccess;
    497 }
    498 
    499 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
    500 int64 ClearKeyCdm::CurrentTimeStampInMicroseconds() const {
    501   return output_timestamp_base_in_microseconds_ +
    502          base::Time::kMicrosecondsPerSecond *
    503          total_samples_generated_  / samples_per_second_;
    504 }
    505 
    506 int ClearKeyCdm::GenerateFakeAudioFramesFromDuration(
    507     int64 duration_in_microseconds,
    508     cdm::AudioFrames* audio_frames) const {
    509   int64 samples_to_generate = static_cast<double>(samples_per_second_) *
    510       duration_in_microseconds / base::Time::kMicrosecondsPerSecond + 0.5;
    511   if (samples_to_generate <= 0)
    512     return 0;
    513 
    514   int64 bytes_per_sample = channel_count_ * bits_per_channel_ / 8;
    515   // |frame_size| must be a multiple of |bytes_per_sample|.
    516   int64 frame_size = bytes_per_sample * samples_to_generate;
    517 
    518   int64 timestamp = CurrentTimeStampInMicroseconds();
    519 
    520   const int kHeaderSize = sizeof(timestamp) + sizeof(frame_size);
    521   audio_frames->SetFrameBuffer(host_->Allocate(kHeaderSize + frame_size));
    522   uint8_t* data = audio_frames->FrameBuffer()->Data();
    523 
    524   memcpy(data, &timestamp, sizeof(timestamp));
    525   data += sizeof(timestamp);
    526   memcpy(data, &frame_size, sizeof(frame_size));
    527   data += sizeof(frame_size);
    528   // You won't hear anything because we have all zeros here. But the video
    529   // should play just fine!
    530   memset(data, 0, frame_size);
    531 
    532   audio_frames->FrameBuffer()->SetSize(kHeaderSize + frame_size);
    533 
    534   return samples_to_generate;
    535 }
    536 
    537 cdm::Status ClearKeyCdm::GenerateFakeAudioFrames(
    538     int64 timestamp_in_microseconds,
    539     cdm::AudioFrames* audio_frames) {
    540   if (timestamp_in_microseconds == kNoTimestamp)
    541     return cdm::kNeedMoreData;
    542 
    543   // Return kNeedMoreData for the first frame because duration is unknown.
    544   if (output_timestamp_base_in_microseconds_ == kNoTimestamp) {
    545     output_timestamp_base_in_microseconds_ = timestamp_in_microseconds;
    546     return cdm::kNeedMoreData;
    547   }
    548 
    549   int samples_generated = GenerateFakeAudioFramesFromDuration(
    550       timestamp_in_microseconds - CurrentTimeStampInMicroseconds(),
    551       audio_frames);
    552   total_samples_generated_ += samples_generated;
    553 
    554   return samples_generated == 0 ? cdm::kNeedMoreData : cdm::kSuccess;
    555 }
    556 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
    557 
    558 }  // namespace media
    559