Upstream version 5.34.104.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/decoder_buffer.h"
18 #include "media/base/decrypt_config.h"
19 #include "media/cdm/json_web_key.h"
20 #include "media/cdm/ppapi/cdm_file_io_test.h"
21 #include "media/cdm/ppapi/external_clear_key/cdm_video_decoder.h"
22
23 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
24 #include "base/basictypes.h"
25 const int64 kNoTimestamp = kint64min;
26 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
27
28 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
29 #include "base/at_exit.h"
30 #include "base/files/file_path.h"
31 #include "base/path_service.h"
32 #include "media/base/media.h"
33 #include "media/cdm/ppapi/external_clear_key/ffmpeg_cdm_audio_decoder.h"
34 #include "media/cdm/ppapi/external_clear_key/ffmpeg_cdm_video_decoder.h"
35
36 // Include FFmpeg avformat.h for av_register_all().
37 extern "C" {
38 // Temporarily disable possible loss of data warning.
39 MSVC_PUSH_DISABLE_WARNING(4244);
40 #include <libavformat/avformat.h>
41 MSVC_POP_WARNING();
42 }  // extern "C"
43
44 // TODO(tomfinegan): When COMPONENT_BUILD is not defined an AtExitManager must
45 // exist before the call to InitializeFFmpegLibraries(). This should no longer
46 // be required after http://crbug.com/91970 because we'll be able to get rid of
47 // InitializeFFmpegLibraries().
48 #if !defined COMPONENT_BUILD
49 static base::AtExitManager g_at_exit_manager;
50 #endif
51
52 // TODO(tomfinegan): InitializeFFmpegLibraries() and |g_cdm_module_initialized|
53 // are required for running in the sandbox, and should no longer be required
54 // after http://crbug.com/91970 is fixed.
55 static bool InitializeFFmpegLibraries() {
56   base::FilePath file_path;
57   CHECK(PathService::Get(base::DIR_MODULE, &file_path));
58   CHECK(media::InitializeMediaLibrary(file_path));
59   return true;
60 }
61
62 static bool g_ffmpeg_lib_initialized = InitializeFFmpegLibraries();
63 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
64
65 const char kClearKeyCdmVersion[] = "0.1.0.1";
66 const char kExternalClearKeyKeySystem[] = "org.chromium.externalclearkey";
67 const char kExternalClearKeyDecryptOnlyKeySystem[] =
68     "org.chromium.externalclearkey.decryptonly";
69 const char kExternalClearKeyFileIOTestKeySystem[] =
70     "org.chromium.externalclearkey.fileiotest";
71 const char kExternalClearKeyCrashKeySystem[] =
72     "org.chromium.externalclearkey.crash";
73
74 // Constants for the enumalted session that can be loaded by LoadSession().
75 // These constants need to be in sync with
76 // chrome/test/data/media/encrypted_media_utils.js
77 const char kLoadableWebSessionId[] = "LoadableSession";
78 const char kLoadableSessionContentType[] = "video/webm";
79 const uint8 kLoadableSessionKeyId[] = "0123456789012345";
80 const uint8 kLoadableSessionKey[] =
81     {0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
82      0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c};
83
84 const int64 kSecondsPerMinute = 60;
85 const int64 kMsPerSecond = 1000;
86 const int64 kInitialTimerDelayMs = 200;
87 const int64 kMaxTimerDelayMs = 1 * kSecondsPerMinute * kMsPerSecond;
88 // Heart beat message header. If a key message starts with |kHeartBeatHeader|,
89 // it's a heart beat message. Otherwise, it's a key request.
90 const char kHeartBeatHeader[] = "HEARTBEAT";
91 // CDM file IO test result header.
92 const char kFileIOTestResultHeader[] = "FILEIOTESTRESULT";
93
94 // Copies |input_buffer| into a media::DecoderBuffer. If the |input_buffer| is
95 // empty, an empty (end-of-stream) media::DecoderBuffer is returned.
96 static scoped_refptr<media::DecoderBuffer> CopyDecoderBufferFrom(
97     const cdm::InputBuffer& input_buffer) {
98   if (!input_buffer.data) {
99     DCHECK(!input_buffer.data_size);
100     return media::DecoderBuffer::CreateEOSBuffer();
101   }
102
103   // TODO(xhwang): Get rid of this copy.
104   scoped_refptr<media::DecoderBuffer> output_buffer =
105       media::DecoderBuffer::CopyFrom(input_buffer.data, input_buffer.data_size);
106
107   std::vector<media::SubsampleEntry> subsamples;
108   for (uint32_t i = 0; i < input_buffer.num_subsamples; ++i) {
109     media::SubsampleEntry subsample;
110     subsample.clear_bytes = input_buffer.subsamples[i].clear_bytes;
111     subsample.cypher_bytes = input_buffer.subsamples[i].cipher_bytes;
112     subsamples.push_back(subsample);
113   }
114
115   DCHECK_EQ(input_buffer.data_offset, 0u);
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 template<typename Type>
137 class ScopedResetter {
138  public:
139   explicit ScopedResetter(Type* object) : object_(object) {}
140   ~ScopedResetter() { object_->Reset(); }
141
142  private:
143   Type* const object_;
144 };
145
146 void INITIALIZE_CDM_MODULE() {
147 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
148   DVLOG(2) << "FFmpeg libraries initialized: " << g_ffmpeg_lib_initialized;
149   av_register_all();
150 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
151 }
152
153 void DeinitializeCdmModule() {
154 }
155
156 void* CreateCdmInstance(int cdm_interface_version,
157                         const char* key_system, uint32_t key_system_size,
158                         GetCdmHostFunc get_cdm_host_func,
159                         void* user_data) {
160   DVLOG(1) << "CreateCdmInstance()";
161
162   std::string key_system_string(key_system, key_system_size);
163   if (key_system_string != kExternalClearKeyKeySystem &&
164       key_system_string != kExternalClearKeyDecryptOnlyKeySystem &&
165       key_system_string != kExternalClearKeyFileIOTestKeySystem &&
166       key_system_string != kExternalClearKeyCrashKeySystem) {
167     DVLOG(1) << "Unsupported key system:" << key_system_string;
168     return NULL;
169   }
170
171   if (cdm_interface_version != media::ClearKeyCdmInterface::kVersion)
172     return NULL;
173
174   media::ClearKeyCdmHost* host = static_cast<media::ClearKeyCdmHost*>(
175       get_cdm_host_func(media::ClearKeyCdmHost::kVersion, user_data));
176   if (!host)
177     return NULL;
178
179   return new media::ClearKeyCdm(host, key_system_string);
180 }
181
182 const char* GetCdmVersion() {
183   return kClearKeyCdmVersion;
184 }
185
186 namespace media {
187
188 ClearKeyCdm::ClearKeyCdm(ClearKeyCdmHost* host, const std::string& key_system)
189     : decryptor_(
190           base::Bind(&ClearKeyCdm::OnSessionCreated, base::Unretained(this)),
191           base::Bind(&ClearKeyCdm::OnSessionMessage, base::Unretained(this)),
192           base::Bind(&ClearKeyCdm::OnSessionReady, base::Unretained(this)),
193           base::Bind(&ClearKeyCdm::OnSessionClosed, base::Unretained(this)),
194           base::Bind(&ClearKeyCdm::OnSessionError, base::Unretained(this))),
195       host_(host),
196       key_system_(key_system),
197       last_session_id_(MediaKeys::kInvalidSessionId),
198       session_id_for_emulated_loadsession_(MediaKeys::kInvalidSessionId),
199       timer_delay_ms_(kInitialTimerDelayMs),
200       heartbeat_timer_set_(false) {
201 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
202   channel_count_ = 0;
203   bits_per_channel_ = 0;
204   samples_per_second_ = 0;
205   output_timestamp_base_in_microseconds_ = kNoTimestamp;
206   total_samples_generated_ = 0;
207 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
208 }
209
210 ClearKeyCdm::~ClearKeyCdm() {}
211
212 void ClearKeyCdm::CreateSession(uint32 session_id,
213                                 const char* type,
214                                 uint32 type_size,
215                                 const uint8* init_data,
216                                 uint32 init_data_size) {
217   DVLOG(1) << __FUNCTION__;
218   decryptor_.CreateSession(
219       session_id, std::string(type, type_size), init_data, init_data_size);
220
221   // Save the latest session ID for heartbeat and file IO test messages.
222   last_session_id_ = session_id;
223
224   if (key_system_ == kExternalClearKeyFileIOTestKeySystem)
225     StartFileIOTest();
226 }
227
228 // Loads a emulated stored session. Currently only |kLoadableWebSessionId|
229 // (containing a |kLoadableSessionKey| for |kLoadableSessionKeyId|) is
230 // supported.
231 void ClearKeyCdm::LoadSession(uint32_t session_id,
232                               const char* web_session_id,
233                               uint32_t web_session_id_length) {
234   DVLOG(1) << __FUNCTION__;
235
236   if (std::string(kLoadableWebSessionId) !=
237       std::string(web_session_id, web_session_id_length)) {
238     // TODO(xhwang): Report "NotFoundError" when we support DOMError style.
239     OnSessionError(session_id, MediaKeys::kUnknownError, 0);
240     return;
241   }
242
243   session_id_for_emulated_loadsession_ = session_id;
244
245   decryptor_.CreateSession(session_id, kLoadableSessionContentType, NULL, 0);
246 }
247
248 void ClearKeyCdm::UpdateSession(uint32 session_id,
249                                 const uint8* response,
250                                 uint32 response_size) {
251   DVLOG(1) << __FUNCTION__;
252   decryptor_.UpdateSession(session_id, response, response_size);
253
254   if (!heartbeat_timer_set_) {
255     ScheduleNextHeartBeat();
256     heartbeat_timer_set_ = true;
257   }
258 }
259
260 void ClearKeyCdm::ReleaseSession(uint32 session_id) {
261   DVLOG(1) << __FUNCTION__;
262   decryptor_.ReleaseSession(session_id);
263 }
264
265 void ClearKeyCdm::TimerExpired(void* context) {
266   if (context == &session_id_for_emulated_loadsession_) {
267     LoadLoadableSession();
268     return;
269   }
270
271   DCHECK(heartbeat_timer_set_);
272   std::string heartbeat_message;
273   if (!next_heartbeat_message_.empty() &&
274       context == &next_heartbeat_message_[0]) {
275     heartbeat_message = next_heartbeat_message_;
276   } else {
277     heartbeat_message = "ERROR: Invalid timer context found!";
278   }
279
280   // This URL is only used for testing the code path for defaultURL.
281   // There is no service at this URL, so applications should ignore it.
282   const char url[] = "http://test.externalclearkey.chromium.org";
283
284   host_->OnSessionMessage(last_session_id_,
285                           heartbeat_message.data(),
286                           heartbeat_message.size(),
287                           url,
288                           arraysize(url) - 1);
289
290   ScheduleNextHeartBeat();
291 }
292
293 static void CopyDecryptResults(
294     media::Decryptor::Status* status_copy,
295     scoped_refptr<media::DecoderBuffer>* buffer_copy,
296     media::Decryptor::Status status,
297     const scoped_refptr<media::DecoderBuffer>& buffer) {
298   *status_copy = status;
299   *buffer_copy = buffer;
300 }
301
302 cdm::Status ClearKeyCdm::Decrypt(
303     const cdm::InputBuffer& encrypted_buffer,
304     cdm::DecryptedBlock* decrypted_block) {
305   DVLOG(1) << "Decrypt()";
306   DCHECK(encrypted_buffer.data);
307
308   scoped_refptr<media::DecoderBuffer> buffer;
309   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
310
311   if (status != cdm::kSuccess)
312     return status;
313
314   DCHECK(buffer->data());
315   decrypted_block->SetDecryptedBuffer(
316       host_->Allocate(buffer->data_size()));
317   memcpy(reinterpret_cast<void*>(decrypted_block->DecryptedBuffer()->Data()),
318          buffer->data(),
319          buffer->data_size());
320   decrypted_block->DecryptedBuffer()->SetSize(buffer->data_size());
321   decrypted_block->SetTimestamp(buffer->timestamp().InMicroseconds());
322
323   return cdm::kSuccess;
324 }
325
326 cdm::Status ClearKeyCdm::InitializeAudioDecoder(
327     const cdm::AudioDecoderConfig& audio_decoder_config) {
328   if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
329     return cdm::kSessionError;
330
331 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
332   if (!audio_decoder_)
333     audio_decoder_.reset(new media::FFmpegCdmAudioDecoder(host_));
334
335   if (!audio_decoder_->Initialize(audio_decoder_config))
336     return cdm::kSessionError;
337
338   return cdm::kSuccess;
339 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
340   channel_count_ = audio_decoder_config.channel_count;
341   bits_per_channel_ = audio_decoder_config.bits_per_channel;
342   samples_per_second_ = audio_decoder_config.samples_per_second;
343   return cdm::kSuccess;
344 #else
345   NOTIMPLEMENTED();
346   return cdm::kSessionError;
347 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
348 }
349
350 cdm::Status ClearKeyCdm::InitializeVideoDecoder(
351     const cdm::VideoDecoderConfig& video_decoder_config) {
352   if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
353     return cdm::kSessionError;
354
355   if (video_decoder_ && video_decoder_->is_initialized()) {
356     DCHECK(!video_decoder_->is_initialized());
357     return cdm::kSessionError;
358   }
359
360   // Any uninitialized decoder will be replaced.
361   video_decoder_ = CreateVideoDecoder(host_, video_decoder_config);
362   if (!video_decoder_)
363     return cdm::kSessionError;
364
365   return cdm::kSuccess;
366 }
367
368 void ClearKeyCdm::ResetDecoder(cdm::StreamType decoder_type) {
369   DVLOG(1) << "ResetDecoder()";
370 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
371   switch (decoder_type) {
372     case cdm::kStreamTypeVideo:
373       video_decoder_->Reset();
374       break;
375     case cdm::kStreamTypeAudio:
376       audio_decoder_->Reset();
377       break;
378     default:
379       NOTREACHED() << "ResetDecoder(): invalid cdm::StreamType";
380   }
381 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
382   if (decoder_type == cdm::kStreamTypeAudio) {
383     output_timestamp_base_in_microseconds_ = kNoTimestamp;
384     total_samples_generated_ = 0;
385   }
386 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
387 }
388
389 void ClearKeyCdm::DeinitializeDecoder(cdm::StreamType decoder_type) {
390   DVLOG(1) << "DeinitializeDecoder()";
391   switch (decoder_type) {
392     case cdm::kStreamTypeVideo:
393       video_decoder_->Deinitialize();
394       break;
395     case cdm::kStreamTypeAudio:
396 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
397       audio_decoder_->Deinitialize();
398 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
399       output_timestamp_base_in_microseconds_ = kNoTimestamp;
400       total_samples_generated_ = 0;
401 #endif
402       break;
403     default:
404       NOTREACHED() << "DeinitializeDecoder(): invalid cdm::StreamType";
405   }
406 }
407
408 cdm::Status ClearKeyCdm::DecryptAndDecodeFrame(
409     const cdm::InputBuffer& encrypted_buffer,
410     cdm::VideoFrame* decoded_frame) {
411   DVLOG(1) << "DecryptAndDecodeFrame()";
412   TRACE_EVENT0("media", "ClearKeyCdm::DecryptAndDecodeFrame");
413
414   scoped_refptr<media::DecoderBuffer> buffer;
415   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
416
417   if (status != cdm::kSuccess)
418     return status;
419
420   const uint8_t* data = NULL;
421   int32_t size = 0;
422   int64_t timestamp = 0;
423   if (!buffer->end_of_stream()) {
424     data = buffer->data();
425     size = buffer->data_size();
426     timestamp = encrypted_buffer.timestamp;
427   }
428
429   return video_decoder_->DecodeFrame(data, size, timestamp, decoded_frame);
430 }
431
432 cdm::Status ClearKeyCdm::DecryptAndDecodeSamples(
433     const cdm::InputBuffer& encrypted_buffer,
434     cdm::AudioFrames* audio_frames) {
435   DVLOG(1) << "DecryptAndDecodeSamples()";
436
437   // Trigger a crash on purpose for testing purpose.
438   if (key_system_ == kExternalClearKeyCrashKeySystem)
439     CHECK(false);
440
441   scoped_refptr<media::DecoderBuffer> buffer;
442   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
443
444   if (status != cdm::kSuccess)
445     return status;
446
447 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
448   const uint8_t* data = NULL;
449   int32_t size = 0;
450   int64_t timestamp = 0;
451   if (!buffer->end_of_stream()) {
452     data = buffer->data();
453     size = buffer->data_size();
454     timestamp = encrypted_buffer.timestamp;
455   }
456
457   return audio_decoder_->DecodeBuffer(data, size, timestamp, audio_frames);
458 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
459   int64 timestamp_in_microseconds = kNoTimestamp;
460   if (!buffer->end_of_stream()) {
461     timestamp_in_microseconds = buffer->GetTimestamp().InMicroseconds();
462     DCHECK(timestamp_in_microseconds != kNoTimestamp);
463   }
464   return GenerateFakeAudioFrames(timestamp_in_microseconds, audio_frames);
465 #else
466   return cdm::kSuccess;
467 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
468 }
469
470 void ClearKeyCdm::Destroy() {
471   DVLOG(1) << "Destroy()";
472   delete this;
473 }
474
475 void ClearKeyCdm::ScheduleNextHeartBeat() {
476   // Prepare the next heartbeat message and set timer.
477   std::ostringstream msg_stream;
478   msg_stream << kHeartBeatHeader << " from ClearKey CDM set at time "
479              << host_->GetCurrentWallTimeInSeconds() << ".";
480   next_heartbeat_message_ = msg_stream.str();
481
482   host_->SetTimer(timer_delay_ms_, &next_heartbeat_message_[0]);
483
484   // Use a smaller timer delay at start-up to facilitate testing. Increase the
485   // timer delay up to a limit to avoid message spam.
486   if (timer_delay_ms_ < kMaxTimerDelayMs)
487     timer_delay_ms_ = std::min(2 * timer_delay_ms_, kMaxTimerDelayMs);
488 }
489
490 cdm::Status ClearKeyCdm::DecryptToMediaDecoderBuffer(
491     const cdm::InputBuffer& encrypted_buffer,
492     scoped_refptr<media::DecoderBuffer>* decrypted_buffer) {
493   DCHECK(decrypted_buffer);
494   scoped_refptr<media::DecoderBuffer> buffer =
495       CopyDecoderBufferFrom(encrypted_buffer);
496
497   if (buffer->end_of_stream()) {
498     *decrypted_buffer = buffer;
499     return cdm::kSuccess;
500   }
501
502   // Callback is called synchronously, so we can use variables on the stack.
503   media::Decryptor::Status status = media::Decryptor::kError;
504   // The AesDecryptor does not care what the stream type is. Pass kVideo
505   // for both audio and video decryption.
506   decryptor_.Decrypt(
507       media::Decryptor::kVideo,
508       buffer,
509       base::Bind(&CopyDecryptResults, &status, decrypted_buffer));
510
511   if (status == media::Decryptor::kError)
512     return cdm::kDecryptError;
513
514   if (status == media::Decryptor::kNoKey)
515     return cdm::kNoKey;
516
517   DCHECK_EQ(status, media::Decryptor::kSuccess);
518   return cdm::kSuccess;
519 }
520
521 void ClearKeyCdm::OnPlatformChallengeResponse(
522     const cdm::PlatformChallengeResponse& response) {
523   NOTIMPLEMENTED();
524 }
525
526 void ClearKeyCdm::OnQueryOutputProtectionStatus(
527     uint32_t link_mask, uint32_t output_protection_mask) {
528   NOTIMPLEMENTED();
529 };
530
531 void ClearKeyCdm::LoadLoadableSession() {
532   std::string jwk_set = GenerateJWKSet(kLoadableSessionKey,
533                                        sizeof(kLoadableSessionKey),
534                                        kLoadableSessionKeyId,
535                                        sizeof(kLoadableSessionKeyId) - 1);
536   // TODO(xhwang): This triggers OnSessionUpdated(). For prefixed EME support,
537   // this is okay. Check WD EME support.
538   decryptor_.UpdateSession(session_id_for_emulated_loadsession_,
539                            reinterpret_cast<const uint8*>(jwk_set.data()),
540                            jwk_set.size());
541 }
542
543 void ClearKeyCdm::OnSessionCreated(uint32 session_id,
544                                    const std::string& web_session_id) {
545   std::string new_web_session_id = web_session_id;
546
547   if (session_id == session_id_for_emulated_loadsession_) {
548     // Delay LoadLoadableSession() to test the case where Decrypt*() calls are
549     // made before the session is fully loaded.
550     const int64 kDelayToLoadSessionMs = 500;
551     // Use the address of |session_id_for_emulated_loadsession_| as the timer
552     // context so that we can call LoadLoadableSession() when the timer expires.
553     host_->SetTimer(kDelayToLoadSessionMs,
554                     &session_id_for_emulated_loadsession_);
555     // Defer OnSessionCreated() until the session is loaded.
556     return;
557   }
558
559   host_->OnSessionCreated(
560       session_id, web_session_id.data(), web_session_id.size());
561 }
562
563 void ClearKeyCdm::OnSessionMessage(uint32 session_id,
564                                    const std::vector<uint8>& message,
565                                    const std::string& destination_url) {
566   DVLOG(1) << "OnSessionMessage: " << message.size();
567
568   // Ignore the message when we are waiting to update the loadable session.
569   if (session_id == session_id_for_emulated_loadsession_)
570     return;
571
572   host_->OnSessionMessage(session_id,
573                           reinterpret_cast<const char*>(message.data()),
574                           message.size(),
575                           destination_url.data(),
576                           destination_url.size());
577 }
578
579 void ClearKeyCdm::OnSessionReady(uint32 session_id) {
580   if (session_id == session_id_for_emulated_loadsession_) {
581     session_id_for_emulated_loadsession_ = MediaKeys::kInvalidSessionId;
582     host_->OnSessionCreated(
583         session_id, kLoadableWebSessionId, strlen(kLoadableWebSessionId));
584   }
585
586   host_->OnSessionReady(session_id);
587 }
588
589 void ClearKeyCdm::OnSessionClosed(uint32 session_id) {
590   host_->OnSessionClosed(session_id);
591 }
592
593 void ClearKeyCdm::OnSessionError(uint32 session_id,
594                                  media::MediaKeys::KeyError error_code,
595                                  int system_code) {
596   host_->OnSessionError(
597       session_id, static_cast<cdm::MediaKeyError>(error_code), system_code);
598 }
599
600 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
601 int64 ClearKeyCdm::CurrentTimeStampInMicroseconds() const {
602   return output_timestamp_base_in_microseconds_ +
603          base::Time::kMicrosecondsPerSecond *
604          total_samples_generated_  / samples_per_second_;
605 }
606
607 int ClearKeyCdm::GenerateFakeAudioFramesFromDuration(
608     int64 duration_in_microseconds,
609     cdm::AudioFrames* audio_frames) const {
610   int64 samples_to_generate = static_cast<double>(samples_per_second_) *
611       duration_in_microseconds / base::Time::kMicrosecondsPerSecond + 0.5;
612   if (samples_to_generate <= 0)
613     return 0;
614
615   int64 bytes_per_sample = channel_count_ * bits_per_channel_ / 8;
616   // |frame_size| must be a multiple of |bytes_per_sample|.
617   int64 frame_size = bytes_per_sample * samples_to_generate;
618
619   int64 timestamp = CurrentTimeStampInMicroseconds();
620
621   const int kHeaderSize = sizeof(timestamp) + sizeof(frame_size);
622   audio_frames->SetFrameBuffer(host_->Allocate(kHeaderSize + frame_size));
623   uint8_t* data = audio_frames->FrameBuffer()->Data();
624
625   memcpy(data, &timestamp, sizeof(timestamp));
626   data += sizeof(timestamp);
627   memcpy(data, &frame_size, sizeof(frame_size));
628   data += sizeof(frame_size);
629   // You won't hear anything because we have all zeros here. But the video
630   // should play just fine!
631   memset(data, 0, frame_size);
632
633   audio_frames->FrameBuffer()->SetSize(kHeaderSize + frame_size);
634
635   return samples_to_generate;
636 }
637
638 cdm::Status ClearKeyCdm::GenerateFakeAudioFrames(
639     int64 timestamp_in_microseconds,
640     cdm::AudioFrames* audio_frames) {
641   if (timestamp_in_microseconds == kNoTimestamp)
642     return cdm::kNeedMoreData;
643
644   // Return kNeedMoreData for the first frame because duration is unknown.
645   if (output_timestamp_base_in_microseconds_ == kNoTimestamp) {
646     output_timestamp_base_in_microseconds_ = timestamp_in_microseconds;
647     return cdm::kNeedMoreData;
648   }
649
650   int samples_generated = GenerateFakeAudioFramesFromDuration(
651       timestamp_in_microseconds - CurrentTimeStampInMicroseconds(),
652       audio_frames);
653   total_samples_generated_ += samples_generated;
654
655   return samples_generated == 0 ? cdm::kNeedMoreData : cdm::kSuccess;
656 }
657 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
658
659 void ClearKeyCdm::StartFileIOTest() {
660   file_io_test_runner_.reset(new FileIOTestRunner(
661       base::Bind(&ClearKeyCdmHost::CreateFileIO, base::Unretained(host_))));
662   file_io_test_runner_->RunAllTests(
663       base::Bind(&ClearKeyCdm::OnFileIOTestComplete, base::Unretained(this)));
664 }
665
666 void ClearKeyCdm::OnFileIOTestComplete(bool success) {
667   DVLOG(1) << __FUNCTION__ << ": " << success;
668   std::string message = GetFileIOTestResultMessage(success);
669   host_->OnSessionMessage(
670       last_session_id_, message.data(), message.size(), NULL, 0);
671   file_io_test_runner_.reset();
672 }
673
674 }  // namespace media