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