Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / media / cdm / ppapi / external_clear_key / clear_key_cdm.cc
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_callback_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"
26 const 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().
38 extern "C" {
39 // Temporarily disable possible loss of data warning.
40 MSVC_PUSH_DISABLE_WARNING(4244);
41 #include <libavformat/avformat.h>
42 MSVC_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
50 static 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.
56 static 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
63 static bool g_ffmpeg_lib_initialized = InitializeFFmpegLibraries();
64 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
65
66 const char kClearKeyCdmVersion[] = "0.1.0.1";
67 const char kExternalClearKeyKeySystem[] = "org.chromium.externalclearkey";
68 const char kExternalClearKeyDecryptOnlyKeySystem[] =
69     "org.chromium.externalclearkey.decryptonly";
70 const char kExternalClearKeyFileIOTestKeySystem[] =
71     "org.chromium.externalclearkey.fileiotest";
72 const 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
78 const char kLoadableWebSessionId[] = "LoadableSession";
79 const char kLoadableSessionContentType[] = "video/webm";
80 const uint8 kLoadableSessionKeyId[] = "0123456789012345";
81 const uint8 kLoadableSessionKey[] =
82     {0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
83      0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c};
84
85 const int64 kSecondsPerMinute = 60;
86 const int64 kMsPerSecond = 1000;
87 const int64 kInitialTimerDelayMs = 200;
88 const 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.
91 const char kHeartBeatHeader[] = "HEARTBEAT";
92 // CDM file IO test result header.
93 const 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.
97 static 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
130 static std::string GetFileIOTestResultMessage(bool success) {
131   std::string message(kFileIOTestResultHeader);
132   message += success ? '1' : '0';
133   return message;
134 }
135
136 static 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
157 static 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
169 template<typename Type>
170 class ScopedResetter {
171  public:
172   explicit ScopedResetter(Type* object) : object_(object) {}
173   ~ScopedResetter() { object_->Reset(); }
174
175  private:
176   Type* const object_;
177 };
178
179 void 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
186 void DeinitializeCdmModule() {
187 }
188
189 void* 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
215 const char* GetCdmVersion() {
216   return kClearKeyCdmVersion;
217 }
218
219 namespace media {
220
221 ClearKeyCdm::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
240 ClearKeyCdm::~ClearKeyCdm() {}
241
242 void 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::CdmCallbackPromise<std::string>(
252           base::Bind(&ClearKeyCdm::OnSessionCreated,
253                      base::Unretained(this),
254                      promise_id),
255           base::Bind(&ClearKeyCdm::OnPromiseFailed,
256                      base::Unretained(this),
257                      promise_id)));
258   decryptor_.CreateSession(std::string(init_data_type, init_data_type_size),
259                            init_data,
260                            init_data_size,
261                            ConvertSessionType(session_type),
262                            promise.Pass());
263
264   if (key_system_ == kExternalClearKeyFileIOTestKeySystem)
265     StartFileIOTest();
266 }
267
268 // Loads a emulated stored session. Currently only |kLoadableWebSessionId|
269 // (containing a |kLoadableSessionKey| for |kLoadableSessionKeyId|) is
270 // supported.
271 void ClearKeyCdm::LoadSession(uint32 promise_id,
272                               const char* web_session_id,
273                               uint32_t web_session_id_length) {
274   DVLOG(1) << __FUNCTION__;
275
276   if (std::string(kLoadableWebSessionId) !=
277       std::string(web_session_id, web_session_id_length)) {
278     // TODO(jrummell): This should be resolved with undefined, not rejected.
279     std::string message("Incorrect session id specified for LoadSession().");
280     host_->OnRejectPromise(promise_id,
281                            cdm::kInvalidAccessError,
282                            0,
283                            message.data(),
284                            message.length());
285     return;
286   }
287
288   scoped_ptr<media::NewSessionCdmPromise> promise(
289       new media::CdmCallbackPromise<std::string>(
290           base::Bind(&ClearKeyCdm::OnSessionLoaded,
291                      base::Unretained(this),
292                      promise_id),
293           base::Bind(&ClearKeyCdm::OnPromiseFailed,
294                      base::Unretained(this),
295                      promise_id)));
296   decryptor_.CreateSession(std::string(kLoadableSessionContentType),
297                            NULL,
298                            0,
299                            MediaKeys::TEMPORARY_SESSION,
300                            promise.Pass());
301 }
302
303 void ClearKeyCdm::UpdateSession(uint32 promise_id,
304                                 const char* web_session_id,
305                                 uint32_t web_session_id_length,
306                                 const uint8* response,
307                                 uint32 response_size) {
308   DVLOG(1) << __FUNCTION__;
309   std::string web_session_str(web_session_id, web_session_id_length);
310
311   scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
312       base::Bind(&ClearKeyCdm::OnSessionUpdated,
313                  base::Unretained(this),
314                  promise_id,
315                  web_session_str),
316       base::Bind(
317           &ClearKeyCdm::OnPromiseFailed, base::Unretained(this), promise_id)));
318   decryptor_.UpdateSession(
319       web_session_str, response, response_size, promise.Pass());
320
321   if (!heartbeat_timer_set_) {
322     ScheduleNextHeartBeat();
323     heartbeat_timer_set_ = true;
324   }
325 }
326
327 void ClearKeyCdm::CloseSession(uint32 promise_id,
328                                const char* web_session_id,
329                                uint32_t web_session_id_length) {
330   DVLOG(1) << __FUNCTION__;
331   std::string web_session_str(web_session_id, web_session_id_length);
332
333   scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
334       base::Bind(
335           &ClearKeyCdm::OnPromiseResolved, base::Unretained(this), promise_id),
336       base::Bind(
337           &ClearKeyCdm::OnPromiseFailed, base::Unretained(this), promise_id)));
338   decryptor_.CloseSession(web_session_str, promise.Pass());
339 }
340
341 void ClearKeyCdm::RemoveSession(uint32 promise_id,
342                                 const char* web_session_id,
343                                 uint32_t web_session_id_length) {
344   DVLOG(1) << __FUNCTION__;
345   // RemoveSession only allowed for persistent sessions.
346   bool is_persistent_session =
347       std::string(kLoadableWebSessionId) ==
348       std::string(web_session_id, web_session_id_length);
349   if (is_persistent_session) {
350     std::string web_session_str(web_session_id, web_session_id_length);
351
352     scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
353         base::Bind(&ClearKeyCdm::OnPromiseResolved,
354                    base::Unretained(this),
355                    promise_id),
356         base::Bind(&ClearKeyCdm::OnPromiseFailed,
357                    base::Unretained(this),
358                    promise_id)));
359     decryptor_.RemoveSession(web_session_str, promise.Pass());
360   } else {
361     // TODO(jrummell): This should be a DCHECK once blink does the proper
362     // checks.
363     std::string message("Not supported for non-persistent sessions.");
364     host_->OnRejectPromise(promise_id,
365                            cdm::kInvalidAccessError,
366                            0,
367                            message.data(),
368                            message.length());
369   }
370 }
371
372 void ClearKeyCdm::SetServerCertificate(uint32 promise_id,
373                                        const uint8_t* server_certificate_data,
374                                        uint32_t server_certificate_data_size) {
375   // ClearKey doesn't use a server certificate.
376   host_->OnResolvePromise(promise_id);
377 }
378
379 void ClearKeyCdm::GetUsableKeyIds(uint32_t promise_id,
380                                   const char* web_session_id,
381                                   uint32_t web_session_id_length) {
382   std::string web_session_str(web_session_id, web_session_id_length);
383   scoped_ptr<media::KeyIdsPromise> promise(
384       new media::CdmCallbackPromise<KeyIdsVector>(
385           base::Bind(&ClearKeyCdm::OnUsableKeyIdsObtained,
386                      base::Unretained(this),
387                      promise_id),
388           base::Bind(&ClearKeyCdm::OnPromiseFailed,
389                      base::Unretained(this),
390                      promise_id)));
391   decryptor_.GetUsableKeyIds(web_session_str, promise.Pass());
392 }
393
394 void ClearKeyCdm::TimerExpired(void* context) {
395   if (context == &session_id_for_emulated_loadsession_) {
396     LoadLoadableSession();
397     return;
398   }
399
400   DCHECK(heartbeat_timer_set_);
401   std::string heartbeat_message;
402   if (!next_heartbeat_message_.empty() &&
403       context == &next_heartbeat_message_[0]) {
404     heartbeat_message = next_heartbeat_message_;
405   } else {
406     heartbeat_message = "ERROR: Invalid timer context found!";
407   }
408
409   // This URL is only used for testing the code path for defaultURL.
410   // There is no service at this URL, so applications should ignore it.
411   const char url[] = "http://test.externalclearkey.chromium.org";
412
413   host_->OnSessionMessage(last_session_id_.data(),
414                           last_session_id_.length(),
415                           heartbeat_message.data(),
416                           heartbeat_message.length(),
417                           url,
418                           arraysize(url) - 1);
419
420   ScheduleNextHeartBeat();
421 }
422
423 static void CopyDecryptResults(
424     media::Decryptor::Status* status_copy,
425     scoped_refptr<media::DecoderBuffer>* buffer_copy,
426     media::Decryptor::Status status,
427     const scoped_refptr<media::DecoderBuffer>& buffer) {
428   *status_copy = status;
429   *buffer_copy = buffer;
430 }
431
432 cdm::Status ClearKeyCdm::Decrypt(const cdm::InputBuffer& encrypted_buffer,
433                                  cdm::DecryptedBlock* decrypted_block) {
434   DVLOG(1) << "Decrypt()";
435   DCHECK(encrypted_buffer.data);
436
437   scoped_refptr<media::DecoderBuffer> buffer;
438   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
439
440   if (status != cdm::kSuccess)
441     return status;
442
443   DCHECK(buffer->data());
444   decrypted_block->SetDecryptedBuffer(
445       host_->Allocate(buffer->data_size()));
446   memcpy(reinterpret_cast<void*>(decrypted_block->DecryptedBuffer()->Data()),
447          buffer->data(),
448          buffer->data_size());
449   decrypted_block->DecryptedBuffer()->SetSize(buffer->data_size());
450   decrypted_block->SetTimestamp(buffer->timestamp().InMicroseconds());
451
452   return cdm::kSuccess;
453 }
454
455 cdm::Status ClearKeyCdm::InitializeAudioDecoder(
456     const cdm::AudioDecoderConfig& audio_decoder_config) {
457   if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
458     return cdm::kSessionError;
459
460 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
461   if (!audio_decoder_)
462     audio_decoder_.reset(new media::FFmpegCdmAudioDecoder(host_));
463
464   if (!audio_decoder_->Initialize(audio_decoder_config))
465     return cdm::kSessionError;
466
467   return cdm::kSuccess;
468 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
469   channel_count_ = audio_decoder_config.channel_count;
470   bits_per_channel_ = audio_decoder_config.bits_per_channel;
471   samples_per_second_ = audio_decoder_config.samples_per_second;
472   return cdm::kSuccess;
473 #else
474   NOTIMPLEMENTED();
475   return cdm::kSessionError;
476 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
477 }
478
479 cdm::Status ClearKeyCdm::InitializeVideoDecoder(
480     const cdm::VideoDecoderConfig& video_decoder_config) {
481   if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
482     return cdm::kSessionError;
483
484   if (video_decoder_ && video_decoder_->is_initialized()) {
485     DCHECK(!video_decoder_->is_initialized());
486     return cdm::kSessionError;
487   }
488
489   // Any uninitialized decoder will be replaced.
490   video_decoder_ = CreateVideoDecoder(host_, video_decoder_config);
491   if (!video_decoder_)
492     return cdm::kSessionError;
493
494   return cdm::kSuccess;
495 }
496
497 void ClearKeyCdm::ResetDecoder(cdm::StreamType decoder_type) {
498   DVLOG(1) << "ResetDecoder()";
499 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
500   switch (decoder_type) {
501     case cdm::kStreamTypeVideo:
502       video_decoder_->Reset();
503       break;
504     case cdm::kStreamTypeAudio:
505       audio_decoder_->Reset();
506       break;
507     default:
508       NOTREACHED() << "ResetDecoder(): invalid cdm::StreamType";
509   }
510 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
511   if (decoder_type == cdm::kStreamTypeAudio) {
512     output_timestamp_base_in_microseconds_ = kNoTimestamp;
513     total_samples_generated_ = 0;
514   }
515 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
516 }
517
518 void ClearKeyCdm::DeinitializeDecoder(cdm::StreamType decoder_type) {
519   DVLOG(1) << "DeinitializeDecoder()";
520   switch (decoder_type) {
521     case cdm::kStreamTypeVideo:
522       video_decoder_->Deinitialize();
523       break;
524     case cdm::kStreamTypeAudio:
525 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
526       audio_decoder_->Deinitialize();
527 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
528       output_timestamp_base_in_microseconds_ = kNoTimestamp;
529       total_samples_generated_ = 0;
530 #endif
531       break;
532     default:
533       NOTREACHED() << "DeinitializeDecoder(): invalid cdm::StreamType";
534   }
535 }
536
537 cdm::Status ClearKeyCdm::DecryptAndDecodeFrame(
538     const cdm::InputBuffer& encrypted_buffer,
539     cdm::VideoFrame* decoded_frame) {
540   DVLOG(1) << "DecryptAndDecodeFrame()";
541   TRACE_EVENT0("media", "ClearKeyCdm::DecryptAndDecodeFrame");
542
543   scoped_refptr<media::DecoderBuffer> buffer;
544   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
545
546   if (status != cdm::kSuccess)
547     return status;
548
549   const uint8_t* data = NULL;
550   int32_t size = 0;
551   int64_t timestamp = 0;
552   if (!buffer->end_of_stream()) {
553     data = buffer->data();
554     size = buffer->data_size();
555     timestamp = encrypted_buffer.timestamp;
556   }
557
558   return video_decoder_->DecodeFrame(data, size, timestamp, decoded_frame);
559 }
560
561 cdm::Status ClearKeyCdm::DecryptAndDecodeSamples(
562     const cdm::InputBuffer& encrypted_buffer,
563     cdm::AudioFrames* audio_frames) {
564   DVLOG(1) << "DecryptAndDecodeSamples()";
565
566   // Trigger a crash on purpose for testing purpose.
567   if (key_system_ == kExternalClearKeyCrashKeySystem)
568     CHECK(false);
569
570   scoped_refptr<media::DecoderBuffer> buffer;
571   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
572
573   if (status != cdm::kSuccess)
574     return status;
575
576 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
577   const uint8_t* data = NULL;
578   int32_t size = 0;
579   int64_t timestamp = 0;
580   if (!buffer->end_of_stream()) {
581     data = buffer->data();
582     size = buffer->data_size();
583     timestamp = encrypted_buffer.timestamp;
584   }
585
586   return audio_decoder_->DecodeBuffer(data, size, timestamp, audio_frames);
587 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
588   int64 timestamp_in_microseconds = kNoTimestamp;
589   if (!buffer->end_of_stream()) {
590     timestamp_in_microseconds = buffer->GetTimestamp().InMicroseconds();
591     DCHECK(timestamp_in_microseconds != kNoTimestamp);
592   }
593   return GenerateFakeAudioFrames(timestamp_in_microseconds, audio_frames);
594 #else
595   return cdm::kSuccess;
596 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
597 }
598
599 void ClearKeyCdm::Destroy() {
600   DVLOG(1) << "Destroy()";
601   delete this;
602 }
603
604 void ClearKeyCdm::ScheduleNextHeartBeat() {
605   // Prepare the next heartbeat message and set timer.
606   std::ostringstream msg_stream;
607   msg_stream << kHeartBeatHeader << " from ClearKey CDM set at time "
608              << host_->GetCurrentWallTime() << ".";
609   next_heartbeat_message_ = msg_stream.str();
610
611   host_->SetTimer(timer_delay_ms_, &next_heartbeat_message_[0]);
612
613   // Use a smaller timer delay at start-up to facilitate testing. Increase the
614   // timer delay up to a limit to avoid message spam.
615   if (timer_delay_ms_ < kMaxTimerDelayMs)
616     timer_delay_ms_ = std::min(2 * timer_delay_ms_, kMaxTimerDelayMs);
617 }
618
619 cdm::Status ClearKeyCdm::DecryptToMediaDecoderBuffer(
620     const cdm::InputBuffer& encrypted_buffer,
621     scoped_refptr<media::DecoderBuffer>* decrypted_buffer) {
622   DCHECK(decrypted_buffer);
623   scoped_refptr<media::DecoderBuffer> buffer =
624       CopyDecoderBufferFrom(encrypted_buffer);
625
626   if (buffer->end_of_stream()) {
627     *decrypted_buffer = buffer;
628     return cdm::kSuccess;
629   }
630
631   // Callback is called synchronously, so we can use variables on the stack.
632   media::Decryptor::Status status = media::Decryptor::kError;
633   // The AesDecryptor does not care what the stream type is. Pass kVideo
634   // for both audio and video decryption.
635   decryptor_.Decrypt(
636       media::Decryptor::kVideo,
637       buffer,
638       base::Bind(&CopyDecryptResults, &status, decrypted_buffer));
639
640   if (status == media::Decryptor::kError)
641     return cdm::kDecryptError;
642
643   if (status == media::Decryptor::kNoKey)
644     return cdm::kNoKey;
645
646   DCHECK_EQ(status, media::Decryptor::kSuccess);
647   return cdm::kSuccess;
648 }
649
650 void ClearKeyCdm::OnPlatformChallengeResponse(
651     const cdm::PlatformChallengeResponse& response) {
652   NOTIMPLEMENTED();
653 }
654
655 void ClearKeyCdm::OnQueryOutputProtectionStatus(
656     uint32_t link_mask, uint32_t output_protection_mask) {
657   NOTIMPLEMENTED();
658 };
659
660 void ClearKeyCdm::LoadLoadableSession() {
661   std::string jwk_set = GenerateJWKSet(kLoadableSessionKey,
662                                        sizeof(kLoadableSessionKey),
663                                        kLoadableSessionKeyId,
664                                        sizeof(kLoadableSessionKeyId) - 1);
665   // TODO(xhwang): This triggers OnSessionUpdated(). For prefixed EME support,
666   // this is okay. Check WD EME support.
667   scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
668       base::Bind(&ClearKeyCdm::OnSessionUpdated,
669                  base::Unretained(this),
670                  promise_id_for_emulated_loadsession_,
671                  session_id_for_emulated_loadsession_),
672       base::Bind(&ClearKeyCdm::OnPromiseFailed,
673                  base::Unretained(this),
674                  promise_id_for_emulated_loadsession_)));
675   decryptor_.UpdateSession(session_id_for_emulated_loadsession_,
676                            reinterpret_cast<const uint8*>(jwk_set.data()),
677                            jwk_set.size(),
678                            promise.Pass());
679 }
680
681 void ClearKeyCdm::OnSessionMessage(const std::string& web_session_id,
682                                    const std::vector<uint8>& message,
683                                    const GURL& destination_url) {
684   DVLOG(1) << "OnSessionMessage: " << message.size();
685
686   // Ignore the message when we are waiting to update the loadable session.
687   if (web_session_id == session_id_for_emulated_loadsession_)
688     return;
689
690   // OnSessionMessage() only called during CreateSession(), so no promise
691   // involved (OnSessionCreated() called to resolve the CreateSession()
692   // promise).
693   host_->OnSessionMessage(web_session_id.data(),
694                           web_session_id.length(),
695                           reinterpret_cast<const char*>(message.data()),
696                           message.size(),
697                           destination_url.spec().data(),
698                           destination_url.spec().size());
699 }
700
701 void ClearKeyCdm::OnSessionKeysChange(const std::string& web_session_id,
702                                       bool has_additional_usable_key) {
703   // Ignore the message when we are waiting to update the loadable session.
704   if (web_session_id == session_id_for_emulated_loadsession_)
705     return;
706
707   host_->OnSessionUsableKeysChange(web_session_id.data(),
708                                    web_session_id.length(),
709                                    has_additional_usable_key);
710 }
711
712 void ClearKeyCdm::OnSessionClosed(const std::string& web_session_id) {
713   host_->OnSessionClosed(web_session_id.data(), web_session_id.length());
714 }
715
716 void ClearKeyCdm::OnSessionCreated(uint32 promise_id,
717                                    const std::string& web_session_id) {
718   // Save the latest session ID for heartbeat and file IO test messages.
719   last_session_id_ = web_session_id;
720
721   host_->OnResolveNewSessionPromise(
722       promise_id, web_session_id.data(), web_session_id.length());
723 }
724
725 void ClearKeyCdm::OnSessionLoaded(uint32 promise_id,
726                                   const std::string& web_session_id) {
727   // Save the latest session ID for heartbeat and file IO test messages.
728   last_session_id_ = web_session_id;
729
730   // |decryptor_| created some session as |web_session_id|, but going forward
731   // we need to map that to |kLoadableWebSessionId|, as that is what callers
732   // expect.
733   session_id_for_emulated_loadsession_ = web_session_id;
734
735   // Delay LoadLoadableSession() to test the case where Decrypt*() calls are
736   // made before the session is fully loaded.
737   const int64 kDelayToLoadSessionMs = 500;
738
739   // Defer resolving the promise until the session is loaded.
740   promise_id_for_emulated_loadsession_ = promise_id;
741
742   // Use the address of |session_id_for_emulated_loadsession_| as the timer
743   // context so that we can call LoadLoadableSession() when the timer expires.
744   host_->SetTimer(kDelayToLoadSessionMs, &session_id_for_emulated_loadsession_);
745 }
746
747 void ClearKeyCdm::OnSessionUpdated(uint32 promise_id,
748                                    const std::string& web_session_id) {
749   // UpdateSession() may be called to finish loading sessions, so handle
750   // appropriately.
751   if (web_session_id == session_id_for_emulated_loadsession_) {
752     session_id_for_emulated_loadsession_ = std::string();
753     // |promise_id| is the LoadSession() promise, so resolve appropriately.
754     host_->OnResolveNewSessionPromise(
755         promise_id, kLoadableWebSessionId, strlen(kLoadableWebSessionId));
756     // Generate the UsableKeys event now that the session is "loaded".
757     host_->OnSessionUsableKeysChange(
758         kLoadableWebSessionId, strlen(kLoadableWebSessionId), true);
759     return;
760   }
761
762   host_->OnResolvePromise(promise_id);
763 }
764
765 void ClearKeyCdm::OnUsableKeyIdsObtained(uint32 promise_id,
766                                          const KeyIdsVector& key_ids) {
767   scoped_ptr<cdm::BinaryData[]> result(new cdm::BinaryData[key_ids.size()]);
768   for (uint32 i = 0; i < key_ids.size(); ++i) {
769     result[i].data = key_ids[i].data();
770     result[i].length = key_ids[i].size();
771   }
772   host_->OnResolveKeyIdsPromise(promise_id, result.get(), key_ids.size());
773 }
774
775 void ClearKeyCdm::OnPromiseResolved(uint32 promise_id) {
776   host_->OnResolvePromise(promise_id);
777 }
778
779 void ClearKeyCdm::OnPromiseFailed(uint32 promise_id,
780                                   MediaKeys::Exception exception_code,
781                                   uint32 system_code,
782                                   const std::string& error_message) {
783   host_->OnRejectPromise(promise_id,
784                          ConvertException(exception_code),
785                          system_code,
786                          error_message.data(),
787                          error_message.length());
788 }
789
790 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
791 int64 ClearKeyCdm::CurrentTimeStampInMicroseconds() const {
792   return output_timestamp_base_in_microseconds_ +
793          base::Time::kMicrosecondsPerSecond *
794          total_samples_generated_  / samples_per_second_;
795 }
796
797 int ClearKeyCdm::GenerateFakeAudioFramesFromDuration(
798     int64 duration_in_microseconds,
799     cdm::AudioFrames* audio_frames) const {
800   int64 samples_to_generate = static_cast<double>(samples_per_second_) *
801       duration_in_microseconds / base::Time::kMicrosecondsPerSecond + 0.5;
802   if (samples_to_generate <= 0)
803     return 0;
804
805   int64 bytes_per_sample = channel_count_ * bits_per_channel_ / 8;
806   // |frame_size| must be a multiple of |bytes_per_sample|.
807   int64 frame_size = bytes_per_sample * samples_to_generate;
808
809   int64 timestamp = CurrentTimeStampInMicroseconds();
810
811   const int kHeaderSize = sizeof(timestamp) + sizeof(frame_size);
812   audio_frames->SetFrameBuffer(host_->Allocate(kHeaderSize + frame_size));
813   uint8_t* data = audio_frames->FrameBuffer()->Data();
814
815   memcpy(data, &timestamp, sizeof(timestamp));
816   data += sizeof(timestamp);
817   memcpy(data, &frame_size, sizeof(frame_size));
818   data += sizeof(frame_size);
819   // You won't hear anything because we have all zeros here. But the video
820   // should play just fine!
821   memset(data, 0, frame_size);
822
823   audio_frames->FrameBuffer()->SetSize(kHeaderSize + frame_size);
824
825   return samples_to_generate;
826 }
827
828 cdm::Status ClearKeyCdm::GenerateFakeAudioFrames(
829     int64 timestamp_in_microseconds,
830     cdm::AudioFrames* audio_frames) {
831   if (timestamp_in_microseconds == kNoTimestamp)
832     return cdm::kNeedMoreData;
833
834   // Return kNeedMoreData for the first frame because duration is unknown.
835   if (output_timestamp_base_in_microseconds_ == kNoTimestamp) {
836     output_timestamp_base_in_microseconds_ = timestamp_in_microseconds;
837     return cdm::kNeedMoreData;
838   }
839
840   int samples_generated = GenerateFakeAudioFramesFromDuration(
841       timestamp_in_microseconds - CurrentTimeStampInMicroseconds(),
842       audio_frames);
843   total_samples_generated_ += samples_generated;
844
845   return samples_generated == 0 ? cdm::kNeedMoreData : cdm::kSuccess;
846 }
847 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
848
849 void ClearKeyCdm::StartFileIOTest() {
850   file_io_test_runner_.reset(new FileIOTestRunner(
851       base::Bind(&ClearKeyCdmHost::CreateFileIO, base::Unretained(host_))));
852   file_io_test_runner_->RunAllTests(
853       base::Bind(&ClearKeyCdm::OnFileIOTestComplete, base::Unretained(this)));
854 }
855
856 void ClearKeyCdm::OnFileIOTestComplete(bool success) {
857   DVLOG(1) << __FUNCTION__ << ": " << success;
858   std::string message = GetFileIOTestResultMessage(success);
859   host_->OnSessionMessage(last_session_id_.data(),
860                           last_session_id_.length(),
861                           message.data(),
862                           message.length(),
863                           NULL,
864                           0);
865   file_io_test_runner_.reset();
866 }
867
868 }  // namespace media