clear_key_cdm.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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  DCHECK_EQ(input_buffer.data_offset, 0u);
117  scoped_ptr<media::DecryptConfig> decrypt_config(new media::DecryptConfig(
118      std::string(reinterpret_cast<const char*>(input_buffer.key_id),
119                  input_buffer.key_id_size),
120      std::string(reinterpret_cast<const char*>(input_buffer.iv),
121                  input_buffer.iv_size),
122      subsamples));
123
124  output_buffer->set_decrypt_config(decrypt_config.Pass());
125  output_buffer->set_timestamp(
126      base::TimeDelta::FromMicroseconds(input_buffer.timestamp));
127
128  return output_buffer;
129}
130
131static std::string GetFileIOTestResultMessage(bool success) {
132  std::string message(kFileIOTestResultHeader);
133  message += success ? '1' : '0';
134  return message;
135}
136
137static cdm::Error ConvertException(media::MediaKeys::Exception exception_code) {
138  switch (exception_code) {
139    case media::MediaKeys::NOT_SUPPORTED_ERROR:
140      return cdm::kNotSupportedError;
141    case media::MediaKeys::INVALID_STATE_ERROR:
142      return cdm::kInvalidStateError;
143    case media::MediaKeys::INVALID_ACCESS_ERROR:
144      return cdm::kInvalidAccessError;
145    case media::MediaKeys::QUOTA_EXCEEDED_ERROR:
146      return cdm::kQuotaExceededError;
147    case media::MediaKeys::UNKNOWN_ERROR:
148      return cdm::kUnknownError;
149    case media::MediaKeys::CLIENT_ERROR:
150      return cdm::kClientError;
151    case media::MediaKeys::OUTPUT_ERROR:
152      return cdm::kOutputError;
153  }
154  NOTIMPLEMENTED();
155  return cdm::kUnknownError;
156}
157
158static media::MediaKeys::SessionType ConvertSessionType(
159    cdm::SessionType session_type) {
160  switch (session_type) {
161    case cdm::kPersistent:
162      return media::MediaKeys::PERSISTENT_SESSION;
163    case cdm::kTemporary:
164      return media::MediaKeys::TEMPORARY_SESSION;
165  }
166  NOTIMPLEMENTED();
167  return media::MediaKeys::TEMPORARY_SESSION;
168}
169
170template<typename Type>
171class ScopedResetter {
172 public:
173  explicit ScopedResetter(Type* object) : object_(object) {}
174  ~ScopedResetter() { object_->Reset(); }
175
176 private:
177  Type* const object_;
178};
179
180void INITIALIZE_CDM_MODULE() {
181#if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
182  DVLOG(2) << "FFmpeg libraries initialized: " << g_ffmpeg_lib_initialized;
183  av_register_all();
184#endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
185}
186
187void DeinitializeCdmModule() {
188}
189
190void* CreateCdmInstance(int cdm_interface_version,
191                        const char* key_system, uint32_t key_system_size,
192                        GetCdmHostFunc get_cdm_host_func,
193                        void* user_data) {
194  DVLOG(1) << "CreateCdmInstance()";
195
196  std::string key_system_string(key_system, key_system_size);
197  if (key_system_string != kExternalClearKeyKeySystem &&
198      key_system_string != kExternalClearKeyDecryptOnlyKeySystem &&
199      key_system_string != kExternalClearKeyFileIOTestKeySystem &&
200      key_system_string != kExternalClearKeyCrashKeySystem) {
201    DVLOG(1) << "Unsupported key system:" << key_system_string;
202    return NULL;
203  }
204
205  if (cdm_interface_version != media::ClearKeyCdmInterface::kVersion)
206    return NULL;
207
208  media::ClearKeyCdmHost* host = static_cast<media::ClearKeyCdmHost*>(
209      get_cdm_host_func(media::ClearKeyCdmHost::kVersion, user_data));
210  if (!host)
211    return NULL;
212
213  return new media::ClearKeyCdm(host, key_system_string);
214}
215
216const char* GetCdmVersion() {
217  return kClearKeyCdmVersion;
218}
219
220namespace media {
221
222ClearKeyCdm::ClearKeyCdm(ClearKeyCdmHost* host, const std::string& key_system)
223    : decryptor_(
224        base::Bind(&ClearKeyCdm::OnSessionMessage, base::Unretained(this)),
225        base::Bind(&ClearKeyCdm::OnSessionClosed, base::Unretained(this))),
226      host_(host),
227      key_system_(key_system),
228      timer_delay_ms_(kInitialTimerDelayMs),
229      heartbeat_timer_set_(false) {
230#if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
231  channel_count_ = 0;
232  bits_per_channel_ = 0;
233  samples_per_second_ = 0;
234  output_timestamp_base_in_microseconds_ = kNoTimestamp;
235  total_samples_generated_ = 0;
236#endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
237}
238
239ClearKeyCdm::~ClearKeyCdm() {}
240
241void ClearKeyCdm::CreateSession(uint32 promise_id,
242                                const char* init_data_type,
243                                uint32 init_data_type_size,
244                                const uint8* init_data,
245                                uint32 init_data_size,
246                                cdm::SessionType session_type) {
247  DVLOG(1) << __FUNCTION__;
248
249  scoped_ptr<media::NewSessionCdmPromise> promise(
250      new media::NewSessionCdmPromise(base::Bind(&ClearKeyCdm::OnSessionCreated,
251                                                 base::Unretained(this),
252                                                 promise_id),
253                                      base::Bind(&ClearKeyCdm::OnPromiseFailed,
254                                                 base::Unretained(this),
255                                                 promise_id)));
256  decryptor_.CreateSession(std::string(init_data_type, init_data_type_size),
257                           init_data,
258                           init_data_size,
259                           ConvertSessionType(session_type),
260                           promise.Pass());
261
262  if (key_system_ == kExternalClearKeyFileIOTestKeySystem)
263    StartFileIOTest();
264}
265
266// Loads a emulated stored session. Currently only |kLoadableWebSessionId|
267// (containing a |kLoadableSessionKey| for |kLoadableSessionKeyId|) is
268// supported.
269void ClearKeyCdm::LoadSession(uint32 promise_id,
270                              const char* web_session_id,
271                              uint32_t web_session_id_length) {
272  DVLOG(1) << __FUNCTION__;
273
274  if (std::string(kLoadableWebSessionId) !=
275      std::string(web_session_id, web_session_id_length)) {
276    std::string message("Incorrect session id specified for LoadSession().");
277    host_->OnRejectPromise(promise_id,
278                           cdm::kInvalidAccessError,
279                           0,
280                           message.data(),
281                           message.length());
282    return;
283  }
284
285  scoped_ptr<media::NewSessionCdmPromise> promise(
286      new media::NewSessionCdmPromise(base::Bind(&ClearKeyCdm::OnSessionLoaded,
287                                                 base::Unretained(this),
288                                                 promise_id),
289                                      base::Bind(&ClearKeyCdm::OnPromiseFailed,
290                                                 base::Unretained(this),
291                                                 promise_id)));
292  decryptor_.CreateSession(std::string(kLoadableSessionContentType),
293                           NULL,
294                           0,
295                           MediaKeys::TEMPORARY_SESSION,
296                           promise.Pass());
297}
298
299void ClearKeyCdm::UpdateSession(uint32 promise_id,
300                                const char* web_session_id,
301                                uint32_t web_session_id_size,
302                                const uint8* response,
303                                uint32 response_size) {
304  DVLOG(1) << __FUNCTION__;
305  std::string web_session_str(web_session_id, web_session_id_size);
306
307  scoped_ptr<media::SimpleCdmPromise> promise(new media::SimpleCdmPromise(
308      base::Bind(&ClearKeyCdm::OnSessionUpdated,
309                 base::Unretained(this),
310                 promise_id,
311                 web_session_str),
312      base::Bind(
313          &ClearKeyCdm::OnPromiseFailed, base::Unretained(this), promise_id)));
314  decryptor_.UpdateSession(
315      web_session_str, response, response_size, promise.Pass());
316
317  if (!heartbeat_timer_set_) {
318    ScheduleNextHeartBeat();
319    heartbeat_timer_set_ = true;
320  }
321}
322
323void ClearKeyCdm::ReleaseSession(uint32 promise_id,
324                                 const char* web_session_id,
325                                 uint32_t web_session_id_size) {
326  DVLOG(1) << __FUNCTION__;
327  std::string web_session_str(web_session_id, web_session_id_size);
328
329  scoped_ptr<media::SimpleCdmPromise> promise(new media::SimpleCdmPromise(
330      base::Bind(&ClearKeyCdm::OnSessionReleased,
331                 base::Unretained(this),
332                 promise_id,
333                 web_session_str),
334      base::Bind(
335          &ClearKeyCdm::OnPromiseFailed, base::Unretained(this), promise_id)));
336  decryptor_.ReleaseSession(web_session_str, promise.Pass());
337}
338
339void ClearKeyCdm::SetServerCertificate(uint32 promise_id,
340                                       const uint8_t* server_certificate_data,
341                                       uint32_t server_certificate_data_size) {
342  // ClearKey doesn't use a server certificate.
343  host_->OnResolvePromise(promise_id);
344}
345
346void ClearKeyCdm::TimerExpired(void* context) {
347  if (context == &session_id_for_emulated_loadsession_) {
348    LoadLoadableSession();
349    return;
350  }
351
352  DCHECK(heartbeat_timer_set_);
353  std::string heartbeat_message;
354  if (!next_heartbeat_message_.empty() &&
355      context == &next_heartbeat_message_[0]) {
356    heartbeat_message = next_heartbeat_message_;
357  } else {
358    heartbeat_message = "ERROR: Invalid timer context found!";
359  }
360
361  // This URL is only used for testing the code path for defaultURL.
362  // There is no service at this URL, so applications should ignore it.
363  const char url[] = "http://test.externalclearkey.chromium.org";
364
365  host_->OnSessionMessage(last_session_id_.data(),
366                          last_session_id_.length(),
367                          heartbeat_message.data(),
368                          heartbeat_message.length(),
369                          url,
370                          arraysize(url) - 1);
371
372  ScheduleNextHeartBeat();
373}
374
375static void CopyDecryptResults(
376    media::Decryptor::Status* status_copy,
377    scoped_refptr<media::DecoderBuffer>* buffer_copy,
378    media::Decryptor::Status status,
379    const scoped_refptr<media::DecoderBuffer>& buffer) {
380  *status_copy = status;
381  *buffer_copy = buffer;
382}
383
384cdm::Status ClearKeyCdm::Decrypt(
385    const cdm::InputBuffer& encrypted_buffer,
386    cdm::DecryptedBlock* decrypted_block) {
387  DVLOG(1) << "Decrypt()";
388  DCHECK(encrypted_buffer.data);
389
390  scoped_refptr<media::DecoderBuffer> buffer;
391  cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
392
393  if (status != cdm::kSuccess)
394    return status;
395
396  DCHECK(buffer->data());
397  decrypted_block->SetDecryptedBuffer(
398      host_->Allocate(buffer->data_size()));
399  memcpy(reinterpret_cast<void*>(decrypted_block->DecryptedBuffer()->Data()),
400         buffer->data(),
401         buffer->data_size());
402  decrypted_block->DecryptedBuffer()->SetSize(buffer->data_size());
403  decrypted_block->SetTimestamp(buffer->timestamp().InMicroseconds());
404
405  return cdm::kSuccess;
406}
407
408cdm::Status ClearKeyCdm::InitializeAudioDecoder(
409    const cdm::AudioDecoderConfig& audio_decoder_config) {
410  if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
411    return cdm::kSessionError;
412
413#if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
414  if (!audio_decoder_)
415    audio_decoder_.reset(new media::FFmpegCdmAudioDecoder(host_));
416
417  if (!audio_decoder_->Initialize(audio_decoder_config))
418    return cdm::kSessionError;
419
420  return cdm::kSuccess;
421#elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
422  channel_count_ = audio_decoder_config.channel_count;
423  bits_per_channel_ = audio_decoder_config.bits_per_channel;
424  samples_per_second_ = audio_decoder_config.samples_per_second;
425  return cdm::kSuccess;
426#else
427  NOTIMPLEMENTED();
428  return cdm::kSessionError;
429#endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
430}
431
432cdm::Status ClearKeyCdm::InitializeVideoDecoder(
433    const cdm::VideoDecoderConfig& video_decoder_config) {
434  if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
435    return cdm::kSessionError;
436
437  if (video_decoder_ && video_decoder_->is_initialized()) {
438    DCHECK(!video_decoder_->is_initialized());
439    return cdm::kSessionError;
440  }
441
442  // Any uninitialized decoder will be replaced.
443  video_decoder_ = CreateVideoDecoder(host_, video_decoder_config);
444  if (!video_decoder_)
445    return cdm::kSessionError;
446
447  return cdm::kSuccess;
448}
449
450void ClearKeyCdm::ResetDecoder(cdm::StreamType decoder_type) {
451  DVLOG(1) << "ResetDecoder()";
452#if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
453  switch (decoder_type) {
454    case cdm::kStreamTypeVideo:
455      video_decoder_->Reset();
456      break;
457    case cdm::kStreamTypeAudio:
458      audio_decoder_->Reset();
459      break;
460    default:
461      NOTREACHED() << "ResetDecoder(): invalid cdm::StreamType";
462  }
463#elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
464  if (decoder_type == cdm::kStreamTypeAudio) {
465    output_timestamp_base_in_microseconds_ = kNoTimestamp;
466    total_samples_generated_ = 0;
467  }
468#endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
469}
470
471void ClearKeyCdm::DeinitializeDecoder(cdm::StreamType decoder_type) {
472  DVLOG(1) << "DeinitializeDecoder()";
473  switch (decoder_type) {
474    case cdm::kStreamTypeVideo:
475      video_decoder_->Deinitialize();
476      break;
477    case cdm::kStreamTypeAudio:
478#if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
479      audio_decoder_->Deinitialize();
480#elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
481      output_timestamp_base_in_microseconds_ = kNoTimestamp;
482      total_samples_generated_ = 0;
483#endif
484      break;
485    default:
486      NOTREACHED() << "DeinitializeDecoder(): invalid cdm::StreamType";
487  }
488}
489
490cdm::Status ClearKeyCdm::DecryptAndDecodeFrame(
491    const cdm::InputBuffer& encrypted_buffer,
492    cdm::VideoFrame* decoded_frame) {
493  DVLOG(1) << "DecryptAndDecodeFrame()";
494  TRACE_EVENT0("media", "ClearKeyCdm::DecryptAndDecodeFrame");
495
496  scoped_refptr<media::DecoderBuffer> buffer;
497  cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
498
499  if (status != cdm::kSuccess)
500    return status;
501
502  const uint8_t* data = NULL;
503  int32_t size = 0;
504  int64_t timestamp = 0;
505  if (!buffer->end_of_stream()) {
506    data = buffer->data();
507    size = buffer->data_size();
508    timestamp = encrypted_buffer.timestamp;
509  }
510
511  return video_decoder_->DecodeFrame(data, size, timestamp, decoded_frame);
512}
513
514cdm::Status ClearKeyCdm::DecryptAndDecodeSamples(
515    const cdm::InputBuffer& encrypted_buffer,
516    cdm::AudioFrames* audio_frames) {
517  DVLOG(1) << "DecryptAndDecodeSamples()";
518
519  // Trigger a crash on purpose for testing purpose.
520  if (key_system_ == kExternalClearKeyCrashKeySystem)
521    CHECK(false);
522
523  scoped_refptr<media::DecoderBuffer> buffer;
524  cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
525
526  if (status != cdm::kSuccess)
527    return status;
528
529#if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
530  const uint8_t* data = NULL;
531  int32_t size = 0;
532  int64_t timestamp = 0;
533  if (!buffer->end_of_stream()) {
534    data = buffer->data();
535    size = buffer->data_size();
536    timestamp = encrypted_buffer.timestamp;
537  }
538
539  return audio_decoder_->DecodeBuffer(data, size, timestamp, audio_frames);
540#elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
541  int64 timestamp_in_microseconds = kNoTimestamp;
542  if (!buffer->end_of_stream()) {
543    timestamp_in_microseconds = buffer->GetTimestamp().InMicroseconds();
544    DCHECK(timestamp_in_microseconds != kNoTimestamp);
545  }
546  return GenerateFakeAudioFrames(timestamp_in_microseconds, audio_frames);
547#else
548  return cdm::kSuccess;
549#endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
550}
551
552void ClearKeyCdm::Destroy() {
553  DVLOG(1) << "Destroy()";
554  delete this;
555}
556
557void ClearKeyCdm::ScheduleNextHeartBeat() {
558  // Prepare the next heartbeat message and set timer.
559  std::ostringstream msg_stream;
560  msg_stream << kHeartBeatHeader << " from ClearKey CDM set at time "
561             << host_->GetCurrentTime() << ".";
562  next_heartbeat_message_ = msg_stream.str();
563
564  host_->SetTimer(timer_delay_ms_, &next_heartbeat_message_[0]);
565
566  // Use a smaller timer delay at start-up to facilitate testing. Increase the
567  // timer delay up to a limit to avoid message spam.
568  if (timer_delay_ms_ < kMaxTimerDelayMs)
569    timer_delay_ms_ = std::min(2 * timer_delay_ms_, kMaxTimerDelayMs);
570}
571
572cdm::Status ClearKeyCdm::DecryptToMediaDecoderBuffer(
573    const cdm::InputBuffer& encrypted_buffer,
574    scoped_refptr<media::DecoderBuffer>* decrypted_buffer) {
575  DCHECK(decrypted_buffer);
576  scoped_refptr<media::DecoderBuffer> buffer =
577      CopyDecoderBufferFrom(encrypted_buffer);
578
579  if (buffer->end_of_stream()) {
580    *decrypted_buffer = buffer;
581    return cdm::kSuccess;
582  }
583
584  // Callback is called synchronously, so we can use variables on the stack.
585  media::Decryptor::Status status = media::Decryptor::kError;
586  // The AesDecryptor does not care what the stream type is. Pass kVideo
587  // for both audio and video decryption.
588  decryptor_.Decrypt(
589      media::Decryptor::kVideo,
590      buffer,
591      base::Bind(&CopyDecryptResults, &status, decrypted_buffer));
592
593  if (status == media::Decryptor::kError)
594    return cdm::kDecryptError;
595
596  if (status == media::Decryptor::kNoKey)
597    return cdm::kNoKey;
598
599  DCHECK_EQ(status, media::Decryptor::kSuccess);
600  return cdm::kSuccess;
601}
602
603void ClearKeyCdm::OnPlatformChallengeResponse(
604    const cdm::PlatformChallengeResponse& response) {
605  NOTIMPLEMENTED();
606}
607
608void ClearKeyCdm::OnQueryOutputProtectionStatus(
609    uint32_t link_mask, uint32_t output_protection_mask) {
610  NOTIMPLEMENTED();
611};
612
613void ClearKeyCdm::LoadLoadableSession() {
614  std::string jwk_set = GenerateJWKSet(kLoadableSessionKey,
615                                       sizeof(kLoadableSessionKey),
616                                       kLoadableSessionKeyId,
617                                       sizeof(kLoadableSessionKeyId) - 1);
618  // TODO(xhwang): This triggers OnSessionUpdated(). For prefixed EME support,
619  // this is okay. Check WD EME support.
620  scoped_ptr<media::SimpleCdmPromise> promise(new media::SimpleCdmPromise(
621      base::Bind(&ClearKeyCdm::OnSessionUpdated,
622                 base::Unretained(this),
623                 promise_id_for_emulated_loadsession_,
624                 session_id_for_emulated_loadsession_),
625      base::Bind(&ClearKeyCdm::OnPromiseFailed,
626                 base::Unretained(this),
627                 promise_id_for_emulated_loadsession_)));
628  decryptor_.UpdateSession(session_id_for_emulated_loadsession_,
629                           reinterpret_cast<const uint8*>(jwk_set.data()),
630                           jwk_set.size(),
631                           promise.Pass());
632}
633
634void ClearKeyCdm::OnSessionMessage(const std::string& web_session_id,
635                                   const std::vector<uint8>& message,
636                                   const GURL& destination_url) {
637  DVLOG(1) << "OnSessionMessage: " << message.size();
638
639  // Ignore the message when we are waiting to update the loadable session.
640  if (web_session_id == session_id_for_emulated_loadsession_)
641    return;
642
643  // OnSessionMessage() only called during CreateSession(), so no promise
644  // involved (OnSessionCreated() called to resolve the CreateSession()
645  // promise).
646  host_->OnSessionMessage(web_session_id.data(),
647                          web_session_id.length(),
648                          reinterpret_cast<const char*>(message.data()),
649                          message.size(),
650                          destination_url.spec().data(),
651                          destination_url.spec().size());
652}
653
654void ClearKeyCdm::OnSessionClosed(const std::string& web_session_id) {
655  host_->OnSessionClosed(web_session_id.data(), web_session_id.length());
656}
657
658void ClearKeyCdm::OnSessionCreated(uint32 promise_id,
659                                   const std::string& web_session_id) {
660  // Save the latest session ID for heartbeat and file IO test messages.
661  last_session_id_ = web_session_id;
662
663  host_->OnResolveNewSessionPromise(
664      promise_id, web_session_id.data(), web_session_id.length());
665}
666
667void ClearKeyCdm::OnSessionLoaded(uint32 promise_id,
668                                  const std::string& web_session_id) {
669  // Save the latest session ID for heartbeat and file IO test messages.
670  last_session_id_ = web_session_id;
671
672  // |decryptor_| created some session as |web_session_id|, but going forward
673  // we need to map that to |kLoadableWebSessionId|, as that is what callers
674  // expect.
675  session_id_for_emulated_loadsession_ = web_session_id;
676
677  // Delay LoadLoadableSession() to test the case where Decrypt*() calls are
678  // made before the session is fully loaded.
679  const int64 kDelayToLoadSessionMs = 500;
680
681  // Defer resolving the promise until the session is loaded.
682  promise_id_for_emulated_loadsession_ = promise_id;
683
684  // Use the address of |session_id_for_emulated_loadsession_| as the timer
685  // context so that we can call LoadLoadableSession() when the timer expires.
686  host_->SetTimer(kDelayToLoadSessionMs, &session_id_for_emulated_loadsession_);
687}
688
689void ClearKeyCdm::OnSessionUpdated(uint32 promise_id,
690                                   const std::string& web_session_id) {
691  // OnSessionReady() only called as success for UpdateSession(). However,
692  // UpdateSession() also called to finish loading sessions, so handle
693  // appropriately.
694  if (web_session_id == session_id_for_emulated_loadsession_) {
695    session_id_for_emulated_loadsession_ = std::string();
696    // |promise_id| is the LoadSession() promise, so resolve appropriately.
697    host_->OnResolveNewSessionPromise(
698        promise_id, kLoadableWebSessionId, strlen(kLoadableWebSessionId));
699    host_->OnSessionReady(kLoadableWebSessionId, strlen(kLoadableWebSessionId));
700    return;
701  }
702
703  host_->OnResolvePromise(promise_id);
704}
705
706void ClearKeyCdm::OnSessionReleased(uint32 promise_id,
707                                    const std::string& web_session_id) {
708  host_->OnResolvePromise(promise_id);
709}
710
711void ClearKeyCdm::OnPromiseFailed(uint32 promise_id,
712                                  MediaKeys::Exception exception_code,
713                                  uint32 system_code,
714                                  const std::string& error_message) {
715  host_->OnRejectPromise(promise_id,
716                         ConvertException(exception_code),
717                         system_code,
718                         error_message.data(),
719                         error_message.length());
720}
721
722#if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
723int64 ClearKeyCdm::CurrentTimeStampInMicroseconds() const {
724  return output_timestamp_base_in_microseconds_ +
725         base::Time::kMicrosecondsPerSecond *
726         total_samples_generated_  / samples_per_second_;
727}
728
729int ClearKeyCdm::GenerateFakeAudioFramesFromDuration(
730    int64 duration_in_microseconds,
731    cdm::AudioFrames* audio_frames) const {
732  int64 samples_to_generate = static_cast<double>(samples_per_second_) *
733      duration_in_microseconds / base::Time::kMicrosecondsPerSecond + 0.5;
734  if (samples_to_generate <= 0)
735    return 0;
736
737  int64 bytes_per_sample = channel_count_ * bits_per_channel_ / 8;
738  // |frame_size| must be a multiple of |bytes_per_sample|.
739  int64 frame_size = bytes_per_sample * samples_to_generate;
740
741  int64 timestamp = CurrentTimeStampInMicroseconds();
742
743  const int kHeaderSize = sizeof(timestamp) + sizeof(frame_size);
744  audio_frames->SetFrameBuffer(host_->Allocate(kHeaderSize + frame_size));
745  uint8_t* data = audio_frames->FrameBuffer()->Data();
746
747  memcpy(data, &timestamp, sizeof(timestamp));
748  data += sizeof(timestamp);
749  memcpy(data, &frame_size, sizeof(frame_size));
750  data += sizeof(frame_size);
751  // You won't hear anything because we have all zeros here. But the video
752  // should play just fine!
753  memset(data, 0, frame_size);
754
755  audio_frames->FrameBuffer()->SetSize(kHeaderSize + frame_size);
756
757  return samples_to_generate;
758}
759
760cdm::Status ClearKeyCdm::GenerateFakeAudioFrames(
761    int64 timestamp_in_microseconds,
762    cdm::AudioFrames* audio_frames) {
763  if (timestamp_in_microseconds == kNoTimestamp)
764    return cdm::kNeedMoreData;
765
766  // Return kNeedMoreData for the first frame because duration is unknown.
767  if (output_timestamp_base_in_microseconds_ == kNoTimestamp) {
768    output_timestamp_base_in_microseconds_ = timestamp_in_microseconds;
769    return cdm::kNeedMoreData;
770  }
771
772  int samples_generated = GenerateFakeAudioFramesFromDuration(
773      timestamp_in_microseconds - CurrentTimeStampInMicroseconds(),
774      audio_frames);
775  total_samples_generated_ += samples_generated;
776
777  return samples_generated == 0 ? cdm::kNeedMoreData : cdm::kSuccess;
778}
779#endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
780
781void ClearKeyCdm::StartFileIOTest() {
782  file_io_test_runner_.reset(new FileIOTestRunner(
783      base::Bind(&ClearKeyCdmHost::CreateFileIO, base::Unretained(host_))));
784  file_io_test_runner_->RunAllTests(
785      base::Bind(&ClearKeyCdm::OnFileIOTestComplete, base::Unretained(this)));
786}
787
788void ClearKeyCdm::OnFileIOTestComplete(bool success) {
789  DVLOG(1) << __FUNCTION__ << ": " << success;
790  std::string message = GetFileIOTestResultMessage(success);
791  host_->OnSessionMessage(last_session_id_.data(),
792                          last_session_id_.length(),
793                          message.data(),
794                          message.length(),
795                          NULL,
796                          0);
797  file_io_test_runner_.reset();
798}
799
800}  // namespace media
801