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