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.
5 #include "media/cdm/ppapi/external_clear_key/clear_key_cdm.h"
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"
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
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"
36 // Include FFmpeg avformat.h for av_register_all().
38 // Temporarily disable possible loss of data warning.
39 MSVC_PUSH_DISABLE_WARNING(4244);
40 #include <libavformat/avformat.h>
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;
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));
62 static bool g_ffmpeg_lib_initialized = InitializeFFmpegLibraries();
63 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
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";
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};
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";
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();
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);
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);
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),
123 output_buffer->set_decrypt_config(decrypt_config.Pass());
124 output_buffer->set_timestamp(
125 base::TimeDelta::FromMicroseconds(input_buffer.timestamp));
127 return output_buffer;
130 static std::string GetFileIOTestResultMessage(bool success) {
131 std::string message(kFileIOTestResultHeader);
132 message += success ? '1' : '0';
136 template<typename Type>
137 class ScopedResetter {
139 explicit ScopedResetter(Type* object) : object_(object) {}
140 ~ScopedResetter() { object_->Reset(); }
146 void INITIALIZE_CDM_MODULE() {
147 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
148 DVLOG(2) << "FFmpeg libraries initialized: " << g_ffmpeg_lib_initialized;
150 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
153 void DeinitializeCdmModule() {
156 void* CreateCdmInstance(int cdm_interface_version,
157 const char* key_system, uint32_t key_system_size,
158 GetCdmHostFunc get_cdm_host_func,
160 DVLOG(1) << "CreateCdmInstance()";
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;
171 if (cdm_interface_version != media::ClearKeyCdmInterface::kVersion)
174 media::ClearKeyCdmHost* host = static_cast<media::ClearKeyCdmHost*>(
175 get_cdm_host_func(media::ClearKeyCdmHost::kVersion, user_data));
179 return new media::ClearKeyCdm(host, key_system_string);
182 const char* GetCdmVersion() {
183 return kClearKeyCdmVersion;
188 ClearKeyCdm::ClearKeyCdm(ClearKeyCdmHost* host, const std::string& key_system)
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))),
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)
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
210 ClearKeyCdm::~ClearKeyCdm() {}
212 void ClearKeyCdm::CreateSession(uint32 session_id,
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);
221 // Save the latest session ID for heartbeat and file IO test messages.
222 last_session_id_ = session_id;
224 if (key_system_ == kExternalClearKeyFileIOTestKeySystem)
228 // Loads a emulated stored session. Currently only |kLoadableWebSessionId|
229 // (containing a |kLoadableSessionKey| for |kLoadableSessionKeyId|) is
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__;
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);
243 session_id_for_emulated_loadsession_ = session_id;
245 decryptor_.CreateSession(session_id, kLoadableSessionContentType, NULL, 0);
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);
254 if (!heartbeat_timer_set_) {
255 ScheduleNextHeartBeat();
256 heartbeat_timer_set_ = true;
260 void ClearKeyCdm::ReleaseSession(uint32 session_id) {
261 DVLOG(1) << __FUNCTION__;
262 decryptor_.ReleaseSession(session_id);
265 void ClearKeyCdm::TimerExpired(void* context) {
266 if (context == &session_id_for_emulated_loadsession_) {
267 LoadLoadableSession();
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_;
277 heartbeat_message = "ERROR: Invalid timer context found!";
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";
284 host_->OnSessionMessage(last_session_id_,
285 heartbeat_message.data(),
286 heartbeat_message.size(),
290 ScheduleNextHeartBeat();
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;
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);
308 scoped_refptr<media::DecoderBuffer> buffer;
309 cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
311 if (status != cdm::kSuccess)
314 DCHECK(buffer->data());
315 decrypted_block->SetDecryptedBuffer(
316 host_->Allocate(buffer->data_size()));
317 memcpy(reinterpret_cast<void*>(decrypted_block->DecryptedBuffer()->Data()),
319 buffer->data_size());
320 decrypted_block->DecryptedBuffer()->SetSize(buffer->data_size());
321 decrypted_block->SetTimestamp(buffer->timestamp().InMicroseconds());
323 return cdm::kSuccess;
326 cdm::Status ClearKeyCdm::InitializeAudioDecoder(
327 const cdm::AudioDecoderConfig& audio_decoder_config) {
328 if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
329 return cdm::kSessionError;
331 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
333 audio_decoder_.reset(new media::FFmpegCdmAudioDecoder(host_));
335 if (!audio_decoder_->Initialize(audio_decoder_config))
336 return cdm::kSessionError;
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;
346 return cdm::kSessionError;
347 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
350 cdm::Status ClearKeyCdm::InitializeVideoDecoder(
351 const cdm::VideoDecoderConfig& video_decoder_config) {
352 if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
353 return cdm::kSessionError;
355 if (video_decoder_ && video_decoder_->is_initialized()) {
356 DCHECK(!video_decoder_->is_initialized());
357 return cdm::kSessionError;
360 // Any uninitialized decoder will be replaced.
361 video_decoder_ = CreateVideoDecoder(host_, video_decoder_config);
363 return cdm::kSessionError;
365 return cdm::kSuccess;
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();
375 case cdm::kStreamTypeAudio:
376 audio_decoder_->Reset();
379 NOTREACHED() << "ResetDecoder(): invalid cdm::StreamType";
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;
386 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
389 void ClearKeyCdm::DeinitializeDecoder(cdm::StreamType decoder_type) {
390 DVLOG(1) << "DeinitializeDecoder()";
391 switch (decoder_type) {
392 case cdm::kStreamTypeVideo:
393 video_decoder_->Deinitialize();
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;
404 NOTREACHED() << "DeinitializeDecoder(): invalid cdm::StreamType";
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");
414 scoped_refptr<media::DecoderBuffer> buffer;
415 cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
417 if (status != cdm::kSuccess)
420 const uint8_t* data = NULL;
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;
429 return video_decoder_->DecodeFrame(data, size, timestamp, decoded_frame);
432 cdm::Status ClearKeyCdm::DecryptAndDecodeSamples(
433 const cdm::InputBuffer& encrypted_buffer,
434 cdm::AudioFrames* audio_frames) {
435 DVLOG(1) << "DecryptAndDecodeSamples()";
437 // Trigger a crash on purpose for testing purpose.
438 if (key_system_ == kExternalClearKeyCrashKeySystem)
441 scoped_refptr<media::DecoderBuffer> buffer;
442 cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
444 if (status != cdm::kSuccess)
447 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
448 const uint8_t* data = NULL;
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;
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);
464 return GenerateFakeAudioFrames(timestamp_in_microseconds, audio_frames);
466 return cdm::kSuccess;
467 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
470 void ClearKeyCdm::Destroy() {
471 DVLOG(1) << "Destroy()";
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();
482 host_->SetTimer(timer_delay_ms_, &next_heartbeat_message_[0]);
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);
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);
497 if (buffer->end_of_stream()) {
498 *decrypted_buffer = buffer;
499 return cdm::kSuccess;
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.
507 media::Decryptor::kVideo,
509 base::Bind(&CopyDecryptResults, &status, decrypted_buffer));
511 if (status == media::Decryptor::kError)
512 return cdm::kDecryptError;
514 if (status == media::Decryptor::kNoKey)
517 DCHECK_EQ(status, media::Decryptor::kSuccess);
518 return cdm::kSuccess;
521 void ClearKeyCdm::OnPlatformChallengeResponse(
522 const cdm::PlatformChallengeResponse& response) {
526 void ClearKeyCdm::OnQueryOutputProtectionStatus(
527 uint32_t link_mask, uint32_t output_protection_mask) {
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()),
543 void ClearKeyCdm::OnSessionCreated(uint32 session_id,
544 const std::string& web_session_id) {
545 std::string new_web_session_id = web_session_id;
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.
559 host_->OnSessionCreated(
560 session_id, web_session_id.data(), web_session_id.size());
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();
568 // Ignore the message when we are waiting to update the loadable session.
569 if (session_id == session_id_for_emulated_loadsession_)
572 host_->OnSessionMessage(session_id,
573 reinterpret_cast<const char*>(message.data()),
575 destination_url.data(),
576 destination_url.size());
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));
586 host_->OnSessionReady(session_id);
589 void ClearKeyCdm::OnSessionClosed(uint32 session_id) {
590 host_->OnSessionClosed(session_id);
593 void ClearKeyCdm::OnSessionError(uint32 session_id,
594 media::MediaKeys::KeyError error_code,
596 host_->OnSessionError(
597 session_id, static_cast<cdm::MediaKeyError>(error_code), system_code);
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_;
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)
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;
619 int64 timestamp = CurrentTimeStampInMicroseconds();
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();
625 memcpy(data, ×tamp, 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);
633 audio_frames->FrameBuffer()->SetSize(kHeaderSize + frame_size);
635 return samples_to_generate;
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;
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;
650 int samples_generated = GenerateFakeAudioFramesFromDuration(
651 timestamp_in_microseconds - CurrentTimeStampInMicroseconds(),
653 total_samples_generated_ += samples_generated;
655 return samples_generated == 0 ? cdm::kNeedMoreData : cdm::kSuccess;
657 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
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)));
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();