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