Upstream version 8.37.180.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_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   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
131 static std::string GetFileIOTestResultMessage(bool success) {
132   std::string message(kFileIOTestResultHeader);
133   message += success ? '1' : '0';
134   return message;
135 }
136
137 static 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
158 static 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
170 template<typename Type>
171 class ScopedResetter {
172  public:
173   explicit ScopedResetter(Type* object) : object_(object) {}
174   ~ScopedResetter() { object_->Reset(); }
175
176  private:
177   Type* const object_;
178 };
179
180 void 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
187 void DeinitializeCdmModule() {
188 }
189
190 void* 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
216 const char* GetCdmVersion() {
217   return kClearKeyCdmVersion;
218 }
219
220 namespace media {
221
222 ClearKeyCdm::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
239 ClearKeyCdm::~ClearKeyCdm() {}
240
241 void 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.
269 void 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
299 void 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
323 void 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
339 void 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
346 void 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
375 static 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
384 cdm::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
408 cdm::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
432 cdm::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
450 void 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
471 void 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
490 cdm::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
514 cdm::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
552 void ClearKeyCdm::Destroy() {
553   DVLOG(1) << "Destroy()";
554   delete this;
555 }
556
557 void 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
572 cdm::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
603 void ClearKeyCdm::OnPlatformChallengeResponse(
604     const cdm::PlatformChallengeResponse& response) {
605   NOTIMPLEMENTED();
606 }
607
608 void ClearKeyCdm::OnQueryOutputProtectionStatus(
609     uint32_t link_mask, uint32_t output_protection_mask) {
610   NOTIMPLEMENTED();
611 };
612
613 void 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
634 void 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
654 void ClearKeyCdm::OnSessionClosed(const std::string& web_session_id) {
655   host_->OnSessionClosed(web_session_id.data(), web_session_id.length());
656 }
657
658 void 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
667 void 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
689 void 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
706 void ClearKeyCdm::OnSessionReleased(uint32 promise_id,
707                                     const std::string& web_session_id) {
708   host_->OnResolvePromise(promise_id);
709 }
710
711 void 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)
723 int64 ClearKeyCdm::CurrentTimeStampInMicroseconds() const {
724   return output_timestamp_base_in_microseconds_ +
725          base::Time::kMicrosecondsPerSecond *
726          total_samples_generated_  / samples_per_second_;
727 }
728
729 int 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
760 cdm::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
781 void 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
788 void 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