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"
22const 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().
34extern "C" {
35// Temporarily disable possible loss of data warning.
36MSVC_PUSH_DISABLE_WARNING(4244);
37#include <libavformat/avformat.h>
38MSVC_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
46static 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.
52static 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
59static bool g_ffmpeg_lib_initialized = InitializeFFmpegLibraries();
60#endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
61
62const char kClearKeyCdmVersion[] = "0.1.0.1";
63const char kExternalClearKeyKeySystem[] = "org.chromium.externalclearkey";
64const char kExternalClearKeyDecryptOnlyKeySystem[] =
65    "org.chromium.externalclearkey.decryptonly";
66const int64 kSecondsPerMinute = 60;
67const int64 kMsPerSecond = 1000;
68const int64 kInitialTimerDelayMs = 200;
69const 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.
72const 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.
76static 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
110template<typename Type>
111class ScopedResetter {
112 public:
113  explicit ScopedResetter(Type* object) : object_(object) {}
114  ~ScopedResetter() { object_->Reset(); }
115
116 private:
117  Type* const object_;
118};
119
120void 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
127void DeinitializeCdmModule() {
128}
129
130void* 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
155const char* GetCdmVersion() {
156  return kClearKeyCdmVersion;
157}
158
159namespace 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
166ClearKeyCdm::Client::Client()
167    : status_(kNone), error_code_(MediaKeys::kUnknownError), system_code_(0) {}
168
169ClearKeyCdm::Client::~Client() {}
170
171void 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
180void 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
186void 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
194void ClearKeyCdm::Client::OnSessionReady(uint32 session_id) {
195  status_ = static_cast<Status>(status_ | kReady);
196}
197
198void ClearKeyCdm::Client::OnSessionClosed(uint32 session_id) {
199  status_ = static_cast<Status>(status_ | kClosed);
200}
201
202void 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
210ClearKeyCdm::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
230ClearKeyCdm::~ClearKeyCdm() {}
231
232cdm::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
264cdm::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
291cdm::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
310void 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
331static 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
340cdm::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
364cdm::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
388cdm::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
406void 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
427void 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
446cdm::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
470cdm::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
504void ClearKeyCdm::Destroy() {
505  DVLOG(1) << "Destroy()";
506  delete this;
507}
508
509void 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
524cdm::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
555void ClearKeyCdm::OnPlatformChallengeResponse(
556    const cdm::PlatformChallengeResponse& response) {
557  NOTIMPLEMENTED();
558}
559
560void 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)
566int64 ClearKeyCdm::CurrentTimeStampInMicroseconds() const {
567  return output_timestamp_base_in_microseconds_ +
568         base::Time::kMicrosecondsPerSecond *
569         total_samples_generated_  / samples_per_second_;
570}
571
572int 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
603cdm::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