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