Revert of ui: Clean up damaged rects and clear them after painting. (patchset #2...
[chromium-blink-merge.git] / media / cdm / ppapi / external_clear_key / clear_key_cdm.cc
blob725ee88f137d27cf6e741a92e88576e959a8cdd2
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.
5 #include "media/cdm/ppapi/external_clear_key/clear_key_cdm.h"
7 #include <algorithm>
8 #include <cstring>
9 #include <sstream>
10 #include <string>
11 #include <vector>
13 #include "base/bind.h"
14 #include "base/logging.h"
15 #include "base/stl_util.h"
16 #include "base/time/time.h"
17 #include "base/trace_event/trace_event.h"
18 #include "media/base/cdm_callback_promise.h"
19 #include "media/base/cdm_key_information.h"
20 #include "media/base/decoder_buffer.h"
21 #include "media/base/decrypt_config.h"
22 #include "media/base/key_systems.h"
23 #include "media/cdm/json_web_key.h"
24 #include "media/cdm/ppapi/cdm_file_io_test.h"
25 #include "media/cdm/ppapi/external_clear_key/cdm_video_decoder.h"
26 #include "url/gurl.h"
28 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
29 #include "base/basictypes.h"
30 const int64 kNoTimestamp = kint64min;
31 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
33 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
34 #include "base/at_exit.h"
35 #include "base/files/file_path.h"
36 #include "base/path_service.h"
37 #include "media/base/media.h"
38 #include "media/cdm/ppapi/external_clear_key/ffmpeg_cdm_audio_decoder.h"
39 #include "media/cdm/ppapi/external_clear_key/ffmpeg_cdm_video_decoder.h"
41 // Include FFmpeg avformat.h for av_register_all().
42 extern "C" {
43 // Temporarily disable possible loss of data warning.
44 MSVC_PUSH_DISABLE_WARNING(4244);
45 #include <libavformat/avformat.h>
46 MSVC_POP_WARNING();
47 } // extern "C"
49 // TODO(tomfinegan): When COMPONENT_BUILD is not defined an AtExitManager must
50 // exist before the call to InitializeFFmpegLibraries(). This should no longer
51 // be required after http://crbug.com/91970 because we'll be able to get rid of
52 // InitializeFFmpegLibraries().
53 #if !defined COMPONENT_BUILD
54 static base::AtExitManager g_at_exit_manager;
55 #endif
57 // TODO(tomfinegan): InitializeFFmpegLibraries() and |g_cdm_module_initialized|
58 // are required for running in the sandbox, and should no longer be required
59 // after http://crbug.com/91970 is fixed.
60 static bool InitializeFFmpegLibraries() {
61 base::FilePath file_path;
62 CHECK(PathService::Get(base::DIR_MODULE, &file_path));
63 CHECK(media::InitializeMediaLibrary(file_path));
64 return true;
67 static bool g_ffmpeg_lib_initialized = InitializeFFmpegLibraries();
68 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
70 const char kClearKeyCdmVersion[] = "0.1.0.1";
71 const char kExternalClearKeyKeySystem[] = "org.chromium.externalclearkey";
72 const char kExternalClearKeyDecryptOnlyKeySystem[] =
73 "org.chromium.externalclearkey.decryptonly";
74 const char kExternalClearKeyFileIOTestKeySystem[] =
75 "org.chromium.externalclearkey.fileiotest";
76 const char kExternalClearKeyCrashKeySystem[] =
77 "org.chromium.externalclearkey.crash";
79 // Constants for the enumalted session that can be loaded by LoadSession().
80 // These constants need to be in sync with
81 // chrome/test/data/media/encrypted_media_utils.js
82 const char kLoadableSessionId[] = "LoadableSession";
83 const uint8 kLoadableSessionKeyId[] = "0123456789012345";
84 const uint8 kLoadableSessionKey[] =
85 {0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
86 0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c};
88 const int64 kSecondsPerMinute = 60;
89 const int64 kMsPerSecond = 1000;
90 const int64 kInitialTimerDelayMs = 200;
91 const int64 kMaxTimerDelayMs = 1 * kSecondsPerMinute * kMsPerSecond;
92 // Renewal message header. For prefixed EME, if a key message starts with
93 // |kRenewalHeader|, it's a renewal message. Otherwise, it's a key request.
94 // FIXME(jrummell): Remove this once prefixed EME goes away.
95 const char kRenewalHeader[] = "RENEWAL";
96 // CDM file IO test result header.
97 const char kFileIOTestResultHeader[] = "FILEIOTESTRESULT";
99 // Copies |input_buffer| into a media::DecoderBuffer. If the |input_buffer| is
100 // empty, an empty (end-of-stream) media::DecoderBuffer is returned.
101 static scoped_refptr<media::DecoderBuffer> CopyDecoderBufferFrom(
102 const cdm::InputBuffer& input_buffer) {
103 if (!input_buffer.data) {
104 DCHECK(!input_buffer.data_size);
105 return media::DecoderBuffer::CreateEOSBuffer();
108 // TODO(xhwang): Get rid of this copy.
109 scoped_refptr<media::DecoderBuffer> output_buffer =
110 media::DecoderBuffer::CopyFrom(input_buffer.data, input_buffer.data_size);
112 std::vector<media::SubsampleEntry> subsamples;
113 for (uint32_t i = 0; i < input_buffer.num_subsamples; ++i) {
114 subsamples.push_back(
115 media::SubsampleEntry(input_buffer.subsamples[i].clear_bytes,
116 input_buffer.subsamples[i].cipher_bytes));
119 scoped_ptr<media::DecryptConfig> decrypt_config(new media::DecryptConfig(
120 std::string(reinterpret_cast<const char*>(input_buffer.key_id),
121 input_buffer.key_id_size),
122 std::string(reinterpret_cast<const char*>(input_buffer.iv),
123 input_buffer.iv_size),
124 subsamples));
126 output_buffer->set_decrypt_config(decrypt_config.Pass());
127 output_buffer->set_timestamp(
128 base::TimeDelta::FromMicroseconds(input_buffer.timestamp));
130 return output_buffer;
133 static std::string GetFileIOTestResultMessage(bool success) {
134 std::string message(kFileIOTestResultHeader);
135 message += success ? '1' : '0';
136 return message;
139 static cdm::Error ConvertException(media::MediaKeys::Exception exception_code) {
140 switch (exception_code) {
141 case media::MediaKeys::NOT_SUPPORTED_ERROR:
142 return cdm::kNotSupportedError;
143 case media::MediaKeys::INVALID_STATE_ERROR:
144 return cdm::kInvalidStateError;
145 case media::MediaKeys::INVALID_ACCESS_ERROR:
146 return cdm::kInvalidAccessError;
147 case media::MediaKeys::QUOTA_EXCEEDED_ERROR:
148 return cdm::kQuotaExceededError;
149 case media::MediaKeys::UNKNOWN_ERROR:
150 return cdm::kUnknownError;
151 case media::MediaKeys::CLIENT_ERROR:
152 return cdm::kClientError;
153 case media::MediaKeys::OUTPUT_ERROR:
154 return cdm::kOutputError;
156 NOTREACHED();
157 return cdm::kUnknownError;
160 static media::MediaKeys::SessionType ConvertSessionType(
161 cdm::SessionType session_type) {
162 switch (session_type) {
163 case cdm::kTemporary:
164 return media::MediaKeys::TEMPORARY_SESSION;
165 case cdm::kPersistentLicense:
166 return media::MediaKeys::PERSISTENT_LICENSE_SESSION;
167 case cdm::kPersistentKeyRelease:
168 return media::MediaKeys::PERSISTENT_RELEASE_MESSAGE_SESSION;
170 NOTREACHED();
171 return media::MediaKeys::TEMPORARY_SESSION;
174 static media::EmeInitDataType ConvertInitDataType(
175 cdm::InitDataType init_data_type) {
176 switch (init_data_type) {
177 case cdm::kCenc:
178 return media::EmeInitDataType::CENC;
179 case cdm::kKeyIds:
180 return media::EmeInitDataType::KEYIDS;
181 case cdm::kWebM:
182 return media::EmeInitDataType::WEBM;
184 NOTREACHED();
185 return media::EmeInitDataType::UNKNOWN;
188 cdm::KeyStatus ConvertKeyStatus(media::CdmKeyInformation::KeyStatus status) {
189 switch (status) {
190 case media::CdmKeyInformation::KeyStatus::USABLE:
191 return cdm::kUsable;
192 case media::CdmKeyInformation::KeyStatus::INTERNAL_ERROR:
193 return cdm::kInternalError;
194 case media::CdmKeyInformation::KeyStatus::EXPIRED:
195 return cdm::kExpired;
196 case media::CdmKeyInformation::KeyStatus::OUTPUT_NOT_ALLOWED:
197 return cdm::kOutputNotAllowed;
198 case media::CdmKeyInformation::KeyStatus::OUTPUT_DOWNSCALED:
199 return cdm::kOutputDownscaled;
200 case media::CdmKeyInformation::KeyStatus::KEY_STATUS_PENDING:
201 return cdm::kStatusPending;
203 NOTREACHED();
204 return cdm::kInternalError;
207 // Shallow copy all the key information from |keys_info| into |keys_vector|.
208 // |keys_vector| is only valid for the lifetime of |keys_info| because it
209 // contains pointers into the latter.
210 void ConvertCdmKeysInfo(const std::vector<media::CdmKeyInformation*>& keys_info,
211 std::vector<cdm::KeyInformation>* keys_vector) {
212 keys_vector->reserve(keys_info.size());
213 for (const auto& key_info : keys_info) {
214 cdm::KeyInformation key;
215 key.key_id = vector_as_array(&key_info->key_id);
216 key.key_id_size = key_info->key_id.size();
217 key.status = ConvertKeyStatus(key_info->status);
218 key.system_code = key_info->system_code;
219 keys_vector->push_back(key);
223 template<typename Type>
224 class ScopedResetter {
225 public:
226 explicit ScopedResetter(Type* object) : object_(object) {}
227 ~ScopedResetter() { object_->Reset(); }
229 private:
230 Type* const object_;
233 void INITIALIZE_CDM_MODULE() {
234 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
235 DVLOG(2) << "FFmpeg libraries initialized: " << g_ffmpeg_lib_initialized;
236 av_register_all();
237 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
240 void DeinitializeCdmModule() {
243 void* CreateCdmInstance(int cdm_interface_version,
244 const char* key_system, uint32_t key_system_size,
245 GetCdmHostFunc get_cdm_host_func,
246 void* user_data) {
247 DVLOG(1) << "CreateCdmInstance()";
249 std::string key_system_string(key_system, key_system_size);
250 if (key_system_string != kExternalClearKeyKeySystem &&
251 key_system_string != kExternalClearKeyDecryptOnlyKeySystem &&
252 key_system_string != kExternalClearKeyFileIOTestKeySystem &&
253 key_system_string != kExternalClearKeyCrashKeySystem) {
254 DVLOG(1) << "Unsupported key system:" << key_system_string;
255 return NULL;
258 if (cdm_interface_version != media::ClearKeyCdmInterface::kVersion)
259 return NULL;
261 media::ClearKeyCdmHost* host = static_cast<media::ClearKeyCdmHost*>(
262 get_cdm_host_func(media::ClearKeyCdmHost::kVersion, user_data));
263 if (!host)
264 return NULL;
266 // TODO(jrummell): Obtain the proper origin for this instance.
267 return new media::ClearKeyCdm(host, key_system_string, GURL::EmptyGURL());
270 const char* GetCdmVersion() {
271 return kClearKeyCdmVersion;
274 namespace media {
276 ClearKeyCdm::ClearKeyCdm(ClearKeyCdmHost* host,
277 const std::string& key_system,
278 const GURL& origin)
279 : decryptor_(
280 origin,
281 base::Bind(&ClearKeyCdm::OnSessionMessage, base::Unretained(this)),
282 base::Bind(&ClearKeyCdm::OnSessionClosed, base::Unretained(this)),
283 base::Bind(&ClearKeyCdm::OnSessionKeysChange,
284 base::Unretained(this))),
285 host_(host),
286 key_system_(key_system),
287 has_received_keys_change_event_for_emulated_loadsession_(false),
288 timer_delay_ms_(kInitialTimerDelayMs),
289 renewal_timer_set_(false) {
290 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
291 channel_count_ = 0;
292 bits_per_channel_ = 0;
293 samples_per_second_ = 0;
294 output_timestamp_base_in_microseconds_ = kNoTimestamp;
295 total_samples_generated_ = 0;
296 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
299 ClearKeyCdm::~ClearKeyCdm() {}
301 void ClearKeyCdm::Initialize(bool /* allow_distinctive_identifier */,
302 bool /* allow_persistent_state */) {
303 // Implementation doesn't use distinctive identifier nor save persistent data,
304 // so nothing to do with these values.
307 void ClearKeyCdm::CreateSessionAndGenerateRequest(
308 uint32 promise_id,
309 cdm::SessionType session_type,
310 cdm::InitDataType init_data_type,
311 const uint8* init_data,
312 uint32 init_data_size) {
313 DVLOG(1) << __FUNCTION__;
315 scoped_ptr<media::NewSessionCdmPromise> promise(
316 new media::CdmCallbackPromise<std::string>(
317 base::Bind(&ClearKeyCdm::OnSessionCreated,
318 base::Unretained(this),
319 promise_id),
320 base::Bind(&ClearKeyCdm::OnPromiseFailed,
321 base::Unretained(this),
322 promise_id)));
323 decryptor_.CreateSessionAndGenerateRequest(
324 ConvertSessionType(session_type), ConvertInitDataType(init_data_type),
325 init_data, init_data_size, promise.Pass());
327 if (key_system_ == kExternalClearKeyFileIOTestKeySystem)
328 StartFileIOTest();
331 // Loads a emulated stored session. Currently only |kLoadableSessionId|
332 // (containing a |kLoadableSessionKey| for |kLoadableSessionKeyId|) is
333 // supported.
334 void ClearKeyCdm::LoadSession(uint32 promise_id,
335 cdm::SessionType session_type,
336 const char* session_id,
337 uint32_t session_id_length) {
338 DVLOG(1) << __FUNCTION__;
339 DCHECK_EQ(session_type, cdm::kPersistentLicense);
341 if (std::string(kLoadableSessionId) !=
342 std::string(session_id, session_id_length)) {
343 // TODO(jrummell): This should be resolved with undefined, not rejected.
344 std::string message("Incorrect session id specified for LoadSession().");
345 host_->OnRejectPromise(promise_id,
346 cdm::kInvalidAccessError,
348 message.data(),
349 message.length());
350 return;
353 // Only allowed to successfully load this session once.
354 DCHECK(session_id_for_emulated_loadsession_.empty());
356 scoped_ptr<media::NewSessionCdmPromise> promise(
357 new media::CdmCallbackPromise<std::string>(
358 base::Bind(&ClearKeyCdm::OnSessionLoaded,
359 base::Unretained(this),
360 promise_id),
361 base::Bind(&ClearKeyCdm::OnPromiseFailed,
362 base::Unretained(this),
363 promise_id)));
364 decryptor_.CreateSessionAndGenerateRequest(MediaKeys::TEMPORARY_SESSION,
365 EmeInitDataType::WEBM, NULL, 0,
366 promise.Pass());
369 void ClearKeyCdm::UpdateSession(uint32 promise_id,
370 const char* session_id,
371 uint32_t session_id_length,
372 const uint8* response,
373 uint32 response_size) {
374 DVLOG(1) << __FUNCTION__;
375 std::string web_session_str(session_id, session_id_length);
377 // If updating the loadable session, use the actual session id generated.
378 if (web_session_str == std::string(kLoadableSessionId))
379 web_session_str = session_id_for_emulated_loadsession_;
381 scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
382 base::Bind(&ClearKeyCdm::OnPromiseResolved, base::Unretained(this),
383 promise_id),
384 base::Bind(&ClearKeyCdm::OnPromiseFailed, base::Unretained(this),
385 promise_id)));
386 decryptor_.UpdateSession(
387 web_session_str, response, response_size, promise.Pass());
389 if (!renewal_timer_set_) {
390 ScheduleNextRenewal();
391 renewal_timer_set_ = true;
395 void ClearKeyCdm::CloseSession(uint32 promise_id,
396 const char* session_id,
397 uint32_t session_id_length) {
398 DVLOG(1) << __FUNCTION__;
399 std::string web_session_str(session_id, session_id_length);
401 // If closing the loadable session, use the actual session id generated.
402 if (web_session_str == std::string(kLoadableSessionId))
403 web_session_str = session_id_for_emulated_loadsession_;
405 scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
406 base::Bind(
407 &ClearKeyCdm::OnPromiseResolved, base::Unretained(this), promise_id),
408 base::Bind(
409 &ClearKeyCdm::OnPromiseFailed, base::Unretained(this), promise_id)));
410 decryptor_.CloseSession(web_session_str, promise.Pass());
413 void ClearKeyCdm::RemoveSession(uint32 promise_id,
414 const char* session_id,
415 uint32_t session_id_length) {
416 DVLOG(1) << __FUNCTION__;
417 std::string web_session_str(session_id, session_id_length);
419 // RemoveSession only allowed for the loadable session.
420 if (web_session_str == std::string(kLoadableSessionId)) {
421 web_session_str = session_id_for_emulated_loadsession_;
422 } else {
423 // TODO(jrummell): This should be a DCHECK once blink does the proper
424 // checks.
425 std::string message("Not supported for non-persistent sessions.");
426 host_->OnRejectPromise(promise_id,
427 cdm::kInvalidAccessError,
429 message.data(),
430 message.length());
431 return;
434 scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
435 base::Bind(&ClearKeyCdm::OnPromiseResolved, base::Unretained(this),
436 promise_id),
437 base::Bind(&ClearKeyCdm::OnPromiseFailed, base::Unretained(this),
438 promise_id)));
439 decryptor_.RemoveSession(web_session_str, promise.Pass());
442 void ClearKeyCdm::SetServerCertificate(uint32 promise_id,
443 const uint8_t* server_certificate_data,
444 uint32_t server_certificate_data_size) {
445 // ClearKey doesn't use a server certificate.
446 host_->OnResolvePromise(promise_id);
449 void ClearKeyCdm::TimerExpired(void* context) {
450 if (context == &session_id_for_emulated_loadsession_) {
451 LoadLoadableSession();
452 return;
455 DCHECK(renewal_timer_set_);
456 std::string renewal_message;
457 if (!next_renewal_message_.empty() &&
458 context == &next_renewal_message_[0]) {
459 renewal_message = next_renewal_message_;
460 } else {
461 renewal_message = "ERROR: Invalid timer context found!";
464 // This URL is only used for testing the code path for defaultURL.
465 // There is no service at this URL, so applications should ignore it.
466 const char url[] = "http://test.externalclearkey.chromium.org";
468 host_->OnSessionMessage(last_session_id_.data(), last_session_id_.length(),
469 cdm::kLicenseRenewal, renewal_message.data(),
470 renewal_message.length(), url, arraysize(url) - 1);
472 ScheduleNextRenewal();
475 static void CopyDecryptResults(
476 media::Decryptor::Status* status_copy,
477 scoped_refptr<media::DecoderBuffer>* buffer_copy,
478 media::Decryptor::Status status,
479 const scoped_refptr<media::DecoderBuffer>& buffer) {
480 *status_copy = status;
481 *buffer_copy = buffer;
484 cdm::Status ClearKeyCdm::Decrypt(const cdm::InputBuffer& encrypted_buffer,
485 cdm::DecryptedBlock* decrypted_block) {
486 DVLOG(1) << "Decrypt()";
487 DCHECK(encrypted_buffer.data);
489 scoped_refptr<media::DecoderBuffer> buffer;
490 cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
492 if (status != cdm::kSuccess)
493 return status;
495 DCHECK(buffer->data());
496 decrypted_block->SetDecryptedBuffer(
497 host_->Allocate(buffer->data_size()));
498 memcpy(reinterpret_cast<void*>(decrypted_block->DecryptedBuffer()->Data()),
499 buffer->data(),
500 buffer->data_size());
501 decrypted_block->DecryptedBuffer()->SetSize(buffer->data_size());
502 decrypted_block->SetTimestamp(buffer->timestamp().InMicroseconds());
504 return cdm::kSuccess;
507 cdm::Status ClearKeyCdm::InitializeAudioDecoder(
508 const cdm::AudioDecoderConfig& audio_decoder_config) {
509 if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
510 return cdm::kSessionError;
512 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
513 if (!audio_decoder_)
514 audio_decoder_.reset(new media::FFmpegCdmAudioDecoder(host_));
516 if (!audio_decoder_->Initialize(audio_decoder_config))
517 return cdm::kSessionError;
519 return cdm::kSuccess;
520 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
521 channel_count_ = audio_decoder_config.channel_count;
522 bits_per_channel_ = audio_decoder_config.bits_per_channel;
523 samples_per_second_ = audio_decoder_config.samples_per_second;
524 return cdm::kSuccess;
525 #else
526 NOTIMPLEMENTED();
527 return cdm::kSessionError;
528 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
531 cdm::Status ClearKeyCdm::InitializeVideoDecoder(
532 const cdm::VideoDecoderConfig& video_decoder_config) {
533 if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
534 return cdm::kSessionError;
536 if (video_decoder_ && video_decoder_->is_initialized()) {
537 DCHECK(!video_decoder_->is_initialized());
538 return cdm::kSessionError;
541 // Any uninitialized decoder will be replaced.
542 video_decoder_ = CreateVideoDecoder(host_, video_decoder_config);
543 if (!video_decoder_)
544 return cdm::kSessionError;
546 return cdm::kSuccess;
549 void ClearKeyCdm::ResetDecoder(cdm::StreamType decoder_type) {
550 DVLOG(1) << "ResetDecoder()";
551 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
552 switch (decoder_type) {
553 case cdm::kStreamTypeVideo:
554 video_decoder_->Reset();
555 break;
556 case cdm::kStreamTypeAudio:
557 audio_decoder_->Reset();
558 break;
559 default:
560 NOTREACHED() << "ResetDecoder(): invalid cdm::StreamType";
562 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
563 if (decoder_type == cdm::kStreamTypeAudio) {
564 output_timestamp_base_in_microseconds_ = kNoTimestamp;
565 total_samples_generated_ = 0;
567 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
570 void ClearKeyCdm::DeinitializeDecoder(cdm::StreamType decoder_type) {
571 DVLOG(1) << "DeinitializeDecoder()";
572 switch (decoder_type) {
573 case cdm::kStreamTypeVideo:
574 video_decoder_->Deinitialize();
575 break;
576 case cdm::kStreamTypeAudio:
577 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
578 audio_decoder_->Deinitialize();
579 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
580 output_timestamp_base_in_microseconds_ = kNoTimestamp;
581 total_samples_generated_ = 0;
582 #endif
583 break;
584 default:
585 NOTREACHED() << "DeinitializeDecoder(): invalid cdm::StreamType";
589 cdm::Status ClearKeyCdm::DecryptAndDecodeFrame(
590 const cdm::InputBuffer& encrypted_buffer,
591 cdm::VideoFrame* decoded_frame) {
592 DVLOG(1) << "DecryptAndDecodeFrame()";
593 TRACE_EVENT0("media", "ClearKeyCdm::DecryptAndDecodeFrame");
595 scoped_refptr<media::DecoderBuffer> buffer;
596 cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
598 if (status != cdm::kSuccess)
599 return status;
601 const uint8_t* data = NULL;
602 int32_t size = 0;
603 int64_t timestamp = 0;
604 if (!buffer->end_of_stream()) {
605 data = buffer->data();
606 size = buffer->data_size();
607 timestamp = encrypted_buffer.timestamp;
610 return video_decoder_->DecodeFrame(data, size, timestamp, decoded_frame);
613 cdm::Status ClearKeyCdm::DecryptAndDecodeSamples(
614 const cdm::InputBuffer& encrypted_buffer,
615 cdm::AudioFrames* audio_frames) {
616 DVLOG(1) << "DecryptAndDecodeSamples()";
618 // Trigger a crash on purpose for testing purpose.
619 if (key_system_ == kExternalClearKeyCrashKeySystem)
620 CHECK(false);
622 scoped_refptr<media::DecoderBuffer> buffer;
623 cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
625 if (status != cdm::kSuccess)
626 return status;
628 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
629 const uint8_t* data = NULL;
630 int32_t size = 0;
631 int64_t timestamp = 0;
632 if (!buffer->end_of_stream()) {
633 data = buffer->data();
634 size = buffer->data_size();
635 timestamp = encrypted_buffer.timestamp;
638 return audio_decoder_->DecodeBuffer(data, size, timestamp, audio_frames);
639 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
640 int64 timestamp_in_microseconds = kNoTimestamp;
641 if (!buffer->end_of_stream()) {
642 timestamp_in_microseconds = buffer->GetTimestamp().InMicroseconds();
643 DCHECK(timestamp_in_microseconds != kNoTimestamp);
645 return GenerateFakeAudioFrames(timestamp_in_microseconds, audio_frames);
646 #else
647 return cdm::kSuccess;
648 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
651 void ClearKeyCdm::Destroy() {
652 DVLOG(1) << "Destroy()";
653 delete this;
656 void ClearKeyCdm::ScheduleNextRenewal() {
657 // Prepare the next renewal message and set timer.
658 std::ostringstream msg_stream;
659 msg_stream << kRenewalHeader << " from ClearKey CDM set at time "
660 << host_->GetCurrentWallTime() << ".";
661 next_renewal_message_ = msg_stream.str();
663 host_->SetTimer(timer_delay_ms_, &next_renewal_message_[0]);
665 // Use a smaller timer delay at start-up to facilitate testing. Increase the
666 // timer delay up to a limit to avoid message spam.
667 if (timer_delay_ms_ < kMaxTimerDelayMs)
668 timer_delay_ms_ = std::min(2 * timer_delay_ms_, kMaxTimerDelayMs);
671 cdm::Status ClearKeyCdm::DecryptToMediaDecoderBuffer(
672 const cdm::InputBuffer& encrypted_buffer,
673 scoped_refptr<media::DecoderBuffer>* decrypted_buffer) {
674 DCHECK(decrypted_buffer);
675 scoped_refptr<media::DecoderBuffer> buffer =
676 CopyDecoderBufferFrom(encrypted_buffer);
678 if (buffer->end_of_stream()) {
679 *decrypted_buffer = buffer;
680 return cdm::kSuccess;
683 // Callback is called synchronously, so we can use variables on the stack.
684 media::Decryptor::Status status = media::Decryptor::kError;
685 // The AesDecryptor does not care what the stream type is. Pass kVideo
686 // for both audio and video decryption.
687 decryptor_.Decrypt(
688 media::Decryptor::kVideo,
689 buffer,
690 base::Bind(&CopyDecryptResults, &status, decrypted_buffer));
692 if (status == media::Decryptor::kError)
693 return cdm::kDecryptError;
695 if (status == media::Decryptor::kNoKey)
696 return cdm::kNoKey;
698 DCHECK_EQ(status, media::Decryptor::kSuccess);
699 return cdm::kSuccess;
702 void ClearKeyCdm::OnPlatformChallengeResponse(
703 const cdm::PlatformChallengeResponse& response) {
704 NOTIMPLEMENTED();
707 void ClearKeyCdm::OnQueryOutputProtectionStatus(
708 cdm::QueryResult result,
709 uint32_t link_mask,
710 uint32_t output_protection_mask) {
711 NOTIMPLEMENTED();
714 void ClearKeyCdm::LoadLoadableSession() {
715 std::string jwk_set = GenerateJWKSet(kLoadableSessionKey,
716 sizeof(kLoadableSessionKey),
717 kLoadableSessionKeyId,
718 sizeof(kLoadableSessionKeyId) - 1);
719 scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
720 base::Bind(&ClearKeyCdm::OnLoadSessionUpdated, base::Unretained(this)),
721 base::Bind(&ClearKeyCdm::OnPromiseFailed, base::Unretained(this),
722 promise_id_for_emulated_loadsession_)));
723 decryptor_.UpdateSession(session_id_for_emulated_loadsession_,
724 reinterpret_cast<const uint8*>(jwk_set.data()),
725 jwk_set.size(),
726 promise.Pass());
729 void ClearKeyCdm::OnSessionMessage(const std::string& session_id,
730 MediaKeys::MessageType message_type,
731 const std::vector<uint8>& message,
732 const GURL& legacy_destination_url) {
733 DVLOG(1) << "OnSessionMessage: " << message.size();
735 // Ignore the message when we are waiting to update the loadable session.
736 if (session_id == session_id_for_emulated_loadsession_)
737 return;
739 // OnSessionMessage() only called during CreateSession(), so no promise
740 // involved (OnSessionCreated() called to resolve the CreateSession()
741 // promise).
742 host_->OnSessionMessage(session_id.data(), session_id.length(),
743 cdm::kLicenseRequest,
744 reinterpret_cast<const char*>(message.data()),
745 message.size(), legacy_destination_url.spec().data(),
746 legacy_destination_url.spec().size());
749 void ClearKeyCdm::OnSessionKeysChange(const std::string& session_id,
750 bool has_additional_usable_key,
751 CdmKeysInfo keys_info) {
752 DVLOG(1) << "OnSessionKeysChange: " << keys_info.size();
754 std::string new_session_id = session_id;
755 if (new_session_id == session_id_for_emulated_loadsession_) {
756 // Save |keys_info| if the loadable session is still being created. This
757 // event will then be forwarded on in OnLoadSessionUpdated().
758 if (promise_id_for_emulated_loadsession_ != 0) {
759 has_received_keys_change_event_for_emulated_loadsession_ = true;
760 keys_info_for_emulated_loadsession_.swap(keys_info);
761 return;
764 // Loadable session has already been created, so pass this event on,
765 // using the session_id callers expect to see.
766 new_session_id = std::string(kLoadableSessionId);
769 std::vector<cdm::KeyInformation> keys_vector;
770 ConvertCdmKeysInfo(keys_info.get(), &keys_vector);
771 host_->OnSessionKeysChange(new_session_id.data(), new_session_id.length(),
772 has_additional_usable_key,
773 vector_as_array(&keys_vector), keys_vector.size());
776 void ClearKeyCdm::OnSessionClosed(const std::string& session_id) {
777 std::string new_session_id = session_id;
778 if (new_session_id == session_id_for_emulated_loadsession_)
779 new_session_id = std::string(kLoadableSessionId);
780 host_->OnSessionClosed(new_session_id.data(), new_session_id.length());
783 void ClearKeyCdm::OnSessionCreated(uint32 promise_id,
784 const std::string& session_id) {
785 // Save the latest session ID for renewal and file IO test messages.
786 last_session_id_ = session_id;
788 host_->OnResolveNewSessionPromise(promise_id, session_id.data(),
789 session_id.length());
792 void ClearKeyCdm::OnSessionLoaded(uint32 promise_id,
793 const std::string& session_id) {
794 // Save the latest session ID for renewal and file IO test messages.
795 last_session_id_ = session_id;
797 // |decryptor_| created some session as |session_id|, but going forward
798 // we need to map that to |kLoadableSessionId|, as that is what callers
799 // expect.
800 session_id_for_emulated_loadsession_ = session_id;
802 // Delay LoadLoadableSession() to test the case where Decrypt*() calls are
803 // made before the session is fully loaded.
804 const int64 kDelayToLoadSessionMs = 500;
806 // Defer resolving the promise until the session is loaded.
807 promise_id_for_emulated_loadsession_ = promise_id;
809 // Use the address of |session_id_for_emulated_loadsession_| as the timer
810 // context so that we can call LoadLoadableSession() when the timer expires.
811 host_->SetTimer(kDelayToLoadSessionMs, &session_id_for_emulated_loadsession_);
814 void ClearKeyCdm::OnLoadSessionUpdated() {
815 // This method is only called to finish loading sessions, so handle
816 // appropriately.
818 // |promise_id_for_emulated_loadsession_| is the LoadSession() promise,
819 // so resolve appropriately.
820 host_->OnResolveNewSessionPromise(promise_id_for_emulated_loadsession_,
821 kLoadableSessionId,
822 strlen(kLoadableSessionId));
823 promise_id_for_emulated_loadsession_ = 0;
825 // Generate the KeysChange event now that the session is "loaded" if one
826 // was seen.
827 // TODO(jrummell): Once the order of events is fixed in the spec, either
828 // require the keyschange event to have happened, or remove this code.
829 // http://crbug.com/448225
830 if (has_received_keys_change_event_for_emulated_loadsession_) {
831 std::vector<cdm::KeyInformation> keys_vector;
832 CdmKeysInfo keys_info;
833 keys_info.swap(keys_info_for_emulated_loadsession_);
834 has_received_keys_change_event_for_emulated_loadsession_ = false;
835 DCHECK(!keys_vector.empty());
836 ConvertCdmKeysInfo(keys_info.get(), &keys_vector);
837 host_->OnSessionKeysChange(
838 kLoadableSessionId, strlen(kLoadableSessionId), !keys_vector.empty(),
839 vector_as_array(&keys_vector), keys_vector.size());
843 void ClearKeyCdm::OnPromiseResolved(uint32 promise_id) {
844 host_->OnResolvePromise(promise_id);
847 void ClearKeyCdm::OnPromiseFailed(uint32 promise_id,
848 MediaKeys::Exception exception_code,
849 uint32 system_code,
850 const std::string& error_message) {
851 host_->OnRejectPromise(promise_id,
852 ConvertException(exception_code),
853 system_code,
854 error_message.data(),
855 error_message.length());
858 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
859 int64 ClearKeyCdm::CurrentTimeStampInMicroseconds() const {
860 return output_timestamp_base_in_microseconds_ +
861 base::Time::kMicrosecondsPerSecond *
862 total_samples_generated_ / samples_per_second_;
865 int ClearKeyCdm::GenerateFakeAudioFramesFromDuration(
866 int64 duration_in_microseconds,
867 cdm::AudioFrames* audio_frames) const {
868 int64 samples_to_generate = static_cast<double>(samples_per_second_) *
869 duration_in_microseconds / base::Time::kMicrosecondsPerSecond + 0.5;
870 if (samples_to_generate <= 0)
871 return 0;
873 int64 bytes_per_sample = channel_count_ * bits_per_channel_ / 8;
874 // |frame_size| must be a multiple of |bytes_per_sample|.
875 int64 frame_size = bytes_per_sample * samples_to_generate;
877 int64 timestamp = CurrentTimeStampInMicroseconds();
879 const int kHeaderSize = sizeof(timestamp) + sizeof(frame_size);
880 audio_frames->SetFrameBuffer(host_->Allocate(kHeaderSize + frame_size));
881 uint8_t* data = audio_frames->FrameBuffer()->Data();
883 memcpy(data, &timestamp, sizeof(timestamp));
884 data += sizeof(timestamp);
885 memcpy(data, &frame_size, sizeof(frame_size));
886 data += sizeof(frame_size);
887 // You won't hear anything because we have all zeros here. But the video
888 // should play just fine!
889 memset(data, 0, frame_size);
891 audio_frames->FrameBuffer()->SetSize(kHeaderSize + frame_size);
893 return samples_to_generate;
896 cdm::Status ClearKeyCdm::GenerateFakeAudioFrames(
897 int64 timestamp_in_microseconds,
898 cdm::AudioFrames* audio_frames) {
899 if (timestamp_in_microseconds == kNoTimestamp)
900 return cdm::kNeedMoreData;
902 // Return kNeedMoreData for the first frame because duration is unknown.
903 if (output_timestamp_base_in_microseconds_ == kNoTimestamp) {
904 output_timestamp_base_in_microseconds_ = timestamp_in_microseconds;
905 return cdm::kNeedMoreData;
908 int samples_generated = GenerateFakeAudioFramesFromDuration(
909 timestamp_in_microseconds - CurrentTimeStampInMicroseconds(),
910 audio_frames);
911 total_samples_generated_ += samples_generated;
913 return samples_generated == 0 ? cdm::kNeedMoreData : cdm::kSuccess;
915 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
917 void ClearKeyCdm::StartFileIOTest() {
918 file_io_test_runner_.reset(new FileIOTestRunner(
919 base::Bind(&ClearKeyCdmHost::CreateFileIO, base::Unretained(host_))));
920 file_io_test_runner_->RunAllTests(
921 base::Bind(&ClearKeyCdm::OnFileIOTestComplete, base::Unretained(this)));
924 void ClearKeyCdm::OnFileIOTestComplete(bool success) {
925 DVLOG(1) << __FUNCTION__ << ": " << success;
926 std::string message = GetFileIOTestResultMessage(success);
927 host_->OnSessionMessage(last_session_id_.data(), last_session_id_.length(),
928 cdm::kLicenseRequest, message.data(),
929 message.length(), NULL, 0);
930 file_io_test_runner_.reset();
933 } // namespace media