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"
26const 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().
38extern "C" {
39// Temporarily disable possible loss of data warning.
40MSVC_PUSH_DISABLE_WARNING(4244);
41#include <libavformat/avformat.h>
42MSVC_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
50static 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.
56static 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
63static bool g_ffmpeg_lib_initialized = InitializeFFmpegLibraries();
64#endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
65
66const char kClearKeyCdmVersion[] = "0.1.0.1";
67const char kExternalClearKeyKeySystem[] = "org.chromium.externalclearkey";
68const char kExternalClearKeyDecryptOnlyKeySystem[] =
69    "org.chromium.externalclearkey.decryptonly";
70const char kExternalClearKeyFileIOTestKeySystem[] =
71    "org.chromium.externalclearkey.fileiotest";
72const 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
78const char kLoadableWebSessionId[] = "LoadableSession";
79const char kLoadableSessionContentType[] = "video/webm";
80const uint8 kLoadableSessionKeyId[] = "0123456789012345";
81const uint8 kLoadableSessionKey[] =
82    {0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
83     0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c};
84
85const int64 kSecondsPerMinute = 60;
86const int64 kMsPerSecond = 1000;
87const int64 kInitialTimerDelayMs = 200;
88const 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.
91const char kHeartBeatHeader[] = "HEARTBEAT";
92// CDM file IO test result header.
93const 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.
97static 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
130static std::string GetFileIOTestResultMessage(bool success) {
131  std::string message(kFileIOTestResultHeader);
132  message += success ? '1' : '0';
133  return message;
134}
135
136static 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
157static 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
169template<typename Type>
170class ScopedResetter {
171 public:
172  explicit ScopedResetter(Type* object) : object_(object) {}
173  ~ScopedResetter() { object_->Reset(); }
174
175 private:
176  Type* const object_;
177};
178
179void 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
186void DeinitializeCdmModule() {
187}
188
189void* 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
215const char* GetCdmVersion() {
216  return kClearKeyCdmVersion;
217}
218
219namespace media {
220
221ClearKeyCdm::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
240ClearKeyCdm::~ClearKeyCdm() {}
241
242void 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.
270void 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
301void 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
325void 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
339void 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
370void 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
377void 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
390void 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
419static 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
428cdm::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
451cdm::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
475cdm::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
493void 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
514void 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
533cdm::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
557cdm::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
595void ClearKeyCdm::Destroy() {
596  DVLOG(1) << "Destroy()";
597  delete this;
598}
599
600void 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
615cdm::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
646void ClearKeyCdm::OnPlatformChallengeResponse(
647    const cdm::PlatformChallengeResponse& response) {
648  NOTIMPLEMENTED();
649}
650
651void ClearKeyCdm::OnQueryOutputProtectionStatus(
652    uint32_t link_mask, uint32_t output_protection_mask) {
653  NOTIMPLEMENTED();
654};
655
656void 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
677void 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
697void 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
704void ClearKeyCdm::OnSessionClosed(const std::string& web_session_id) {
705  host_->OnSessionClosed(web_session_id.data(), web_session_id.length());
706}
707
708void 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
717void 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
739void 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
754void 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
764void ClearKeyCdm::OnPromiseResolved(uint32 promise_id) {
765  host_->OnResolvePromise(promise_id);
766}
767
768void 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)
780int64 ClearKeyCdm::CurrentTimeStampInMicroseconds() const {
781  return output_timestamp_base_in_microseconds_ +
782         base::Time::kMicrosecondsPerSecond *
783         total_samples_generated_  / samples_per_second_;
784}
785
786int 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
817cdm::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
838void 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
845void 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