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