Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / media / cdm / ppapi / external_clear_key / clear_key_cdm.cc
bloba1f67079aada4b6d04ab8511c160807d6069e0be
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>
11 #include "base/bind.h"
12 #include "base/logging.h"
13 #include "base/stl_util.h"
14 #include "base/time/time.h"
15 #include "base/trace_event/trace_event.h"
16 #include "media/base/cdm_callback_promise.h"
17 #include "media/base/cdm_key_information.h"
18 #include "media/base/decoder_buffer.h"
19 #include "media/base/decrypt_config.h"
20 #include "media/base/key_systems.h"
21 #include "media/cdm/json_web_key.h"
22 #include "media/cdm/ppapi/cdm_file_io_test.h"
23 #include "media/cdm/ppapi/external_clear_key/cdm_video_decoder.h"
24 #include "url/gurl.h"
26 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
27 #include "base/basictypes.h"
28 const int64 kNoTimestamp = kint64min;
29 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
31 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
32 #include "base/at_exit.h"
33 #include "base/files/file_path.h"
34 #include "base/path_service.h"
35 #include "media/base/media.h"
36 #include "media/cdm/ppapi/external_clear_key/ffmpeg_cdm_audio_decoder.h"
37 #include "media/cdm/ppapi/external_clear_key/ffmpeg_cdm_video_decoder.h"
39 // Include FFmpeg avformat.h for av_register_all().
40 extern "C" {
41 // Temporarily disable possible loss of data warning.
42 MSVC_PUSH_DISABLE_WARNING(4244);
43 #include <libavformat/avformat.h>
44 MSVC_POP_WARNING();
45 } // extern "C"
47 #if !defined COMPONENT_BUILD
48 static base::AtExitManager g_at_exit_manager;
49 #endif
51 // Prepare media library.
52 static bool InitializeFFmpegLibraries() {
53 media::InitializeMediaLibrary();
54 return true;
56 static bool g_ffmpeg_lib_initialized = InitializeFFmpegLibraries();
58 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
60 const char kClearKeyCdmVersion[] = "0.1.0.1";
61 const char kExternalClearKeyKeySystem[] = "org.chromium.externalclearkey";
62 const char kExternalClearKeyDecryptOnlyKeySystem[] =
63 "org.chromium.externalclearkey.decryptonly";
64 const char kExternalClearKeyFileIOTestKeySystem[] =
65 "org.chromium.externalclearkey.fileiotest";
66 const char kExternalClearKeyCrashKeySystem[] =
67 "org.chromium.externalclearkey.crash";
69 // Constants for the enumalted session that can be loaded by LoadSession().
70 // These constants need to be in sync with
71 // chrome/test/data/media/encrypted_media_utils.js
72 const char kLoadableSessionId[] = "LoadableSession";
73 const uint8 kLoadableSessionKeyId[] = "0123456789012345";
74 const uint8 kLoadableSessionKey[] =
75 {0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
76 0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c};
78 const int64 kSecondsPerMinute = 60;
79 const int64 kMsPerSecond = 1000;
80 const int64 kInitialTimerDelayMs = 200;
81 const int64 kMaxTimerDelayMs = 1 * kSecondsPerMinute * kMsPerSecond;
82 // Renewal message header. For prefixed EME, if a key message starts with
83 // |kRenewalHeader|, it's a renewal message. Otherwise, it's a key request.
84 // FIXME(jrummell): Remove this once prefixed EME goes away.
85 const char kRenewalHeader[] = "RENEWAL";
86 // CDM file IO test result header.
87 const char kFileIOTestResultHeader[] = "FILEIOTESTRESULT";
89 // Copies |input_buffer| into a media::DecoderBuffer. If the |input_buffer| is
90 // empty, an empty (end-of-stream) media::DecoderBuffer is returned.
91 static scoped_refptr<media::DecoderBuffer> CopyDecoderBufferFrom(
92 const cdm::InputBuffer& input_buffer) {
93 if (!input_buffer.data) {
94 DCHECK(!input_buffer.data_size);
95 return media::DecoderBuffer::CreateEOSBuffer();
98 // TODO(xhwang): Get rid of this copy.
99 scoped_refptr<media::DecoderBuffer> output_buffer =
100 media::DecoderBuffer::CopyFrom(input_buffer.data, input_buffer.data_size);
102 std::vector<media::SubsampleEntry> subsamples;
103 for (uint32_t i = 0; i < input_buffer.num_subsamples; ++i) {
104 subsamples.push_back(
105 media::SubsampleEntry(input_buffer.subsamples[i].clear_bytes,
106 input_buffer.subsamples[i].cipher_bytes));
109 scoped_ptr<media::DecryptConfig> decrypt_config(new media::DecryptConfig(
110 std::string(reinterpret_cast<const char*>(input_buffer.key_id),
111 input_buffer.key_id_size),
112 std::string(reinterpret_cast<const char*>(input_buffer.iv),
113 input_buffer.iv_size),
114 subsamples));
116 output_buffer->set_decrypt_config(decrypt_config.Pass());
117 output_buffer->set_timestamp(
118 base::TimeDelta::FromMicroseconds(input_buffer.timestamp));
120 return output_buffer;
123 static std::string GetFileIOTestResultMessage(bool success) {
124 std::string message(kFileIOTestResultHeader);
125 message += success ? '1' : '0';
126 return message;
129 static cdm::Error ConvertException(media::MediaKeys::Exception exception_code) {
130 switch (exception_code) {
131 case media::MediaKeys::NOT_SUPPORTED_ERROR:
132 return cdm::kNotSupportedError;
133 case media::MediaKeys::INVALID_STATE_ERROR:
134 return cdm::kInvalidStateError;
135 case media::MediaKeys::INVALID_ACCESS_ERROR:
136 return cdm::kInvalidAccessError;
137 case media::MediaKeys::QUOTA_EXCEEDED_ERROR:
138 return cdm::kQuotaExceededError;
139 case media::MediaKeys::UNKNOWN_ERROR:
140 return cdm::kUnknownError;
141 case media::MediaKeys::CLIENT_ERROR:
142 return cdm::kClientError;
143 case media::MediaKeys::OUTPUT_ERROR:
144 return cdm::kOutputError;
146 NOTREACHED();
147 return cdm::kUnknownError;
150 static media::MediaKeys::SessionType ConvertSessionType(
151 cdm::SessionType session_type) {
152 switch (session_type) {
153 case cdm::kTemporary:
154 return media::MediaKeys::TEMPORARY_SESSION;
155 case cdm::kPersistentLicense:
156 return media::MediaKeys::PERSISTENT_LICENSE_SESSION;
157 case cdm::kPersistentKeyRelease:
158 return media::MediaKeys::PERSISTENT_RELEASE_MESSAGE_SESSION;
160 NOTREACHED();
161 return media::MediaKeys::TEMPORARY_SESSION;
164 static media::EmeInitDataType ConvertInitDataType(
165 cdm::InitDataType init_data_type) {
166 switch (init_data_type) {
167 case cdm::kCenc:
168 return media::EmeInitDataType::CENC;
169 case cdm::kKeyIds:
170 return media::EmeInitDataType::KEYIDS;
171 case cdm::kWebM:
172 return media::EmeInitDataType::WEBM;
174 NOTREACHED();
175 return media::EmeInitDataType::UNKNOWN;
178 cdm::KeyStatus ConvertKeyStatus(media::CdmKeyInformation::KeyStatus status) {
179 switch (status) {
180 case media::CdmKeyInformation::KeyStatus::USABLE:
181 return cdm::kUsable;
182 case media::CdmKeyInformation::KeyStatus::INTERNAL_ERROR:
183 return cdm::kInternalError;
184 case media::CdmKeyInformation::KeyStatus::EXPIRED:
185 return cdm::kExpired;
186 case media::CdmKeyInformation::KeyStatus::OUTPUT_NOT_ALLOWED:
187 return cdm::kOutputNotAllowed;
188 case media::CdmKeyInformation::KeyStatus::OUTPUT_DOWNSCALED:
189 return cdm::kOutputDownscaled;
190 case media::CdmKeyInformation::KeyStatus::KEY_STATUS_PENDING:
191 return cdm::kStatusPending;
193 NOTREACHED();
194 return cdm::kInternalError;
197 // Shallow copy all the key information from |keys_info| into |keys_vector|.
198 // |keys_vector| is only valid for the lifetime of |keys_info| because it
199 // contains pointers into the latter.
200 void ConvertCdmKeysInfo(const std::vector<media::CdmKeyInformation*>& keys_info,
201 std::vector<cdm::KeyInformation>* keys_vector) {
202 keys_vector->reserve(keys_info.size());
203 for (const auto& key_info : keys_info) {
204 cdm::KeyInformation key;
205 key.key_id = vector_as_array(&key_info->key_id);
206 key.key_id_size = key_info->key_id.size();
207 key.status = ConvertKeyStatus(key_info->status);
208 key.system_code = key_info->system_code;
209 keys_vector->push_back(key);
213 template<typename Type>
214 class ScopedResetter {
215 public:
216 explicit ScopedResetter(Type* object) : object_(object) {}
217 ~ScopedResetter() { object_->Reset(); }
219 private:
220 Type* const object_;
223 void INITIALIZE_CDM_MODULE() {
224 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
225 av_register_all();
226 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
229 void DeinitializeCdmModule() {
232 void* CreateCdmInstance(int cdm_interface_version,
233 const char* key_system, uint32_t key_system_size,
234 GetCdmHostFunc get_cdm_host_func,
235 void* user_data) {
236 DVLOG(1) << "CreateCdmInstance()";
238 std::string key_system_string(key_system, key_system_size);
239 if (key_system_string != kExternalClearKeyKeySystem &&
240 key_system_string != kExternalClearKeyDecryptOnlyKeySystem &&
241 key_system_string != kExternalClearKeyFileIOTestKeySystem &&
242 key_system_string != kExternalClearKeyCrashKeySystem) {
243 DVLOG(1) << "Unsupported key system:" << key_system_string;
244 return NULL;
247 if (cdm_interface_version != media::ClearKeyCdmInterface::kVersion)
248 return NULL;
250 media::ClearKeyCdmHost* host = static_cast<media::ClearKeyCdmHost*>(
251 get_cdm_host_func(media::ClearKeyCdmHost::kVersion, user_data));
252 if (!host)
253 return NULL;
255 // TODO(jrummell): Obtain the proper origin for this instance.
256 return new media::ClearKeyCdm(host, key_system_string, GURL::EmptyGURL());
259 const char* GetCdmVersion() {
260 return kClearKeyCdmVersion;
263 namespace media {
265 ClearKeyCdm::ClearKeyCdm(ClearKeyCdmHost* host,
266 const std::string& key_system,
267 const GURL& origin)
268 : decryptor_(
269 origin,
270 base::Bind(&ClearKeyCdm::OnSessionMessage, base::Unretained(this)),
271 base::Bind(&ClearKeyCdm::OnSessionClosed, base::Unretained(this)),
272 base::Bind(&ClearKeyCdm::OnSessionKeysChange,
273 base::Unretained(this))),
274 host_(host),
275 key_system_(key_system),
276 has_received_keys_change_event_for_emulated_loadsession_(false),
277 timer_delay_ms_(kInitialTimerDelayMs),
278 renewal_timer_set_(false) {
279 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
280 channel_count_ = 0;
281 bits_per_channel_ = 0;
282 samples_per_second_ = 0;
283 output_timestamp_base_in_microseconds_ = kNoTimestamp;
284 total_samples_generated_ = 0;
285 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
288 ClearKeyCdm::~ClearKeyCdm() {}
290 void ClearKeyCdm::Initialize(bool /* allow_distinctive_identifier */,
291 bool /* allow_persistent_state */) {
292 // Implementation doesn't use distinctive identifier nor save persistent data,
293 // so nothing to do with these values.
296 void ClearKeyCdm::CreateSessionAndGenerateRequest(
297 uint32 promise_id,
298 cdm::SessionType session_type,
299 cdm::InitDataType init_data_type,
300 const uint8* init_data,
301 uint32 init_data_size) {
302 DVLOG(1) << __FUNCTION__;
304 scoped_ptr<media::NewSessionCdmPromise> promise(
305 new media::CdmCallbackPromise<std::string>(
306 base::Bind(&ClearKeyCdm::OnSessionCreated,
307 base::Unretained(this),
308 promise_id),
309 base::Bind(&ClearKeyCdm::OnPromiseFailed,
310 base::Unretained(this),
311 promise_id)));
312 decryptor_.CreateSessionAndGenerateRequest(
313 ConvertSessionType(session_type), ConvertInitDataType(init_data_type),
314 std::vector<uint8_t>(init_data, init_data + init_data_size),
315 promise.Pass());
317 if (key_system_ == kExternalClearKeyFileIOTestKeySystem)
318 StartFileIOTest();
321 // Loads a emulated stored session. Currently only |kLoadableSessionId|
322 // (containing a |kLoadableSessionKey| for |kLoadableSessionKeyId|) is
323 // supported.
324 void ClearKeyCdm::LoadSession(uint32 promise_id,
325 cdm::SessionType session_type,
326 const char* session_id,
327 uint32_t session_id_length) {
328 DVLOG(1) << __FUNCTION__;
329 DCHECK_EQ(session_type, cdm::kPersistentLicense);
331 if (std::string(kLoadableSessionId) !=
332 std::string(session_id, session_id_length)) {
333 host_->OnResolveNewSessionPromise(promise_id, nullptr, 0);
334 return;
337 // Only allowed to successfully load this session once.
338 DCHECK(session_id_for_emulated_loadsession_.empty());
340 scoped_ptr<media::NewSessionCdmPromise> promise(
341 new media::CdmCallbackPromise<std::string>(
342 base::Bind(&ClearKeyCdm::OnSessionLoaded,
343 base::Unretained(this),
344 promise_id),
345 base::Bind(&ClearKeyCdm::OnPromiseFailed,
346 base::Unretained(this),
347 promise_id)));
348 decryptor_.CreateSessionAndGenerateRequest(
349 MediaKeys::TEMPORARY_SESSION, EmeInitDataType::WEBM,
350 std::vector<uint8_t>(), promise.Pass());
353 void ClearKeyCdm::UpdateSession(uint32 promise_id,
354 const char* session_id,
355 uint32_t session_id_length,
356 const uint8* response,
357 uint32 response_size) {
358 DVLOG(1) << __FUNCTION__;
359 std::string web_session_str(session_id, session_id_length);
361 // If updating the loadable session, use the actual session id generated.
362 if (web_session_str == std::string(kLoadableSessionId))
363 web_session_str = session_id_for_emulated_loadsession_;
365 scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
366 base::Bind(&ClearKeyCdm::OnPromiseResolved, base::Unretained(this),
367 promise_id),
368 base::Bind(&ClearKeyCdm::OnPromiseFailed, base::Unretained(this),
369 promise_id)));
370 decryptor_.UpdateSession(
371 web_session_str, std::vector<uint8_t>(response, response + response_size),
372 promise.Pass());
374 if (!renewal_timer_set_) {
375 ScheduleNextRenewal();
376 renewal_timer_set_ = true;
380 void ClearKeyCdm::CloseSession(uint32 promise_id,
381 const char* session_id,
382 uint32_t session_id_length) {
383 DVLOG(1) << __FUNCTION__;
384 std::string web_session_str(session_id, session_id_length);
386 // If closing the loadable session, use the actual session id generated.
387 if (web_session_str == std::string(kLoadableSessionId))
388 web_session_str = session_id_for_emulated_loadsession_;
390 scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
391 base::Bind(
392 &ClearKeyCdm::OnPromiseResolved, base::Unretained(this), promise_id),
393 base::Bind(
394 &ClearKeyCdm::OnPromiseFailed, base::Unretained(this), promise_id)));
395 decryptor_.CloseSession(web_session_str, promise.Pass());
398 void ClearKeyCdm::RemoveSession(uint32 promise_id,
399 const char* session_id,
400 uint32_t session_id_length) {
401 DVLOG(1) << __FUNCTION__;
402 std::string web_session_str(session_id, session_id_length);
404 // RemoveSession only allowed for the loadable session.
405 if (web_session_str == std::string(kLoadableSessionId)) {
406 web_session_str = session_id_for_emulated_loadsession_;
407 } else {
408 // TODO(jrummell): This should be a DCHECK once blink does the proper
409 // checks.
410 std::string message("Not supported for non-persistent sessions.");
411 host_->OnRejectPromise(promise_id,
412 cdm::kInvalidAccessError,
414 message.data(),
415 message.length());
416 return;
419 scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
420 base::Bind(&ClearKeyCdm::OnPromiseResolved, base::Unretained(this),
421 promise_id),
422 base::Bind(&ClearKeyCdm::OnPromiseFailed, base::Unretained(this),
423 promise_id)));
424 decryptor_.RemoveSession(web_session_str, promise.Pass());
427 void ClearKeyCdm::SetServerCertificate(uint32 promise_id,
428 const uint8_t* server_certificate_data,
429 uint32_t server_certificate_data_size) {
430 // ClearKey doesn't use a server certificate.
431 host_->OnResolvePromise(promise_id);
434 void ClearKeyCdm::TimerExpired(void* context) {
435 if (context == &session_id_for_emulated_loadsession_) {
436 LoadLoadableSession();
437 return;
440 DCHECK(renewal_timer_set_);
441 std::string renewal_message;
442 if (!next_renewal_message_.empty() &&
443 context == &next_renewal_message_[0]) {
444 renewal_message = next_renewal_message_;
445 } else {
446 renewal_message = "ERROR: Invalid timer context found!";
449 // This URL is only used for testing the code path for defaultURL.
450 // There is no service at this URL, so applications should ignore it.
451 const char url[] = "http://test.externalclearkey.chromium.org";
453 host_->OnSessionMessage(last_session_id_.data(), last_session_id_.length(),
454 cdm::kLicenseRenewal, renewal_message.data(),
455 renewal_message.length(), url, arraysize(url) - 1);
457 ScheduleNextRenewal();
460 static void CopyDecryptResults(
461 media::Decryptor::Status* status_copy,
462 scoped_refptr<media::DecoderBuffer>* buffer_copy,
463 media::Decryptor::Status status,
464 const scoped_refptr<media::DecoderBuffer>& buffer) {
465 *status_copy = status;
466 *buffer_copy = buffer;
469 cdm::Status ClearKeyCdm::Decrypt(const cdm::InputBuffer& encrypted_buffer,
470 cdm::DecryptedBlock* decrypted_block) {
471 DVLOG(1) << "Decrypt()";
472 DCHECK(encrypted_buffer.data);
474 scoped_refptr<media::DecoderBuffer> buffer;
475 cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
477 if (status != cdm::kSuccess)
478 return status;
480 DCHECK(buffer->data());
481 decrypted_block->SetDecryptedBuffer(
482 host_->Allocate(buffer->data_size()));
483 memcpy(reinterpret_cast<void*>(decrypted_block->DecryptedBuffer()->Data()),
484 buffer->data(),
485 buffer->data_size());
486 decrypted_block->DecryptedBuffer()->SetSize(buffer->data_size());
487 decrypted_block->SetTimestamp(buffer->timestamp().InMicroseconds());
489 return cdm::kSuccess;
492 cdm::Status ClearKeyCdm::InitializeAudioDecoder(
493 const cdm::AudioDecoderConfig& audio_decoder_config) {
494 if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
495 return cdm::kSessionError;
497 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
498 if (!audio_decoder_)
499 audio_decoder_.reset(new media::FFmpegCdmAudioDecoder(host_));
501 if (!audio_decoder_->Initialize(audio_decoder_config))
502 return cdm::kSessionError;
504 return cdm::kSuccess;
505 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
506 channel_count_ = audio_decoder_config.channel_count;
507 bits_per_channel_ = audio_decoder_config.bits_per_channel;
508 samples_per_second_ = audio_decoder_config.samples_per_second;
509 return cdm::kSuccess;
510 #else
511 NOTIMPLEMENTED();
512 return cdm::kSessionError;
513 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
516 cdm::Status ClearKeyCdm::InitializeVideoDecoder(
517 const cdm::VideoDecoderConfig& video_decoder_config) {
518 if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
519 return cdm::kSessionError;
521 if (video_decoder_ && video_decoder_->is_initialized()) {
522 DCHECK(!video_decoder_->is_initialized());
523 return cdm::kSessionError;
526 // Any uninitialized decoder will be replaced.
527 video_decoder_ = CreateVideoDecoder(host_, video_decoder_config);
528 if (!video_decoder_)
529 return cdm::kSessionError;
531 return cdm::kSuccess;
534 void ClearKeyCdm::ResetDecoder(cdm::StreamType decoder_type) {
535 DVLOG(1) << "ResetDecoder()";
536 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
537 switch (decoder_type) {
538 case cdm::kStreamTypeVideo:
539 video_decoder_->Reset();
540 break;
541 case cdm::kStreamTypeAudio:
542 audio_decoder_->Reset();
543 break;
544 default:
545 NOTREACHED() << "ResetDecoder(): invalid cdm::StreamType";
547 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
548 if (decoder_type == cdm::kStreamTypeAudio) {
549 output_timestamp_base_in_microseconds_ = kNoTimestamp;
550 total_samples_generated_ = 0;
552 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
555 void ClearKeyCdm::DeinitializeDecoder(cdm::StreamType decoder_type) {
556 DVLOG(1) << "DeinitializeDecoder()";
557 switch (decoder_type) {
558 case cdm::kStreamTypeVideo:
559 video_decoder_->Deinitialize();
560 break;
561 case cdm::kStreamTypeAudio:
562 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
563 audio_decoder_->Deinitialize();
564 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
565 output_timestamp_base_in_microseconds_ = kNoTimestamp;
566 total_samples_generated_ = 0;
567 #endif
568 break;
569 default:
570 NOTREACHED() << "DeinitializeDecoder(): invalid cdm::StreamType";
574 cdm::Status ClearKeyCdm::DecryptAndDecodeFrame(
575 const cdm::InputBuffer& encrypted_buffer,
576 cdm::VideoFrame* decoded_frame) {
577 DVLOG(1) << "DecryptAndDecodeFrame()";
578 TRACE_EVENT0("media", "ClearKeyCdm::DecryptAndDecodeFrame");
580 scoped_refptr<media::DecoderBuffer> buffer;
581 cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
583 if (status != cdm::kSuccess)
584 return status;
586 const uint8_t* data = NULL;
587 int32_t size = 0;
588 int64_t timestamp = 0;
589 if (!buffer->end_of_stream()) {
590 data = buffer->data();
591 size = buffer->data_size();
592 timestamp = encrypted_buffer.timestamp;
595 return video_decoder_->DecodeFrame(data, size, timestamp, decoded_frame);
598 cdm::Status ClearKeyCdm::DecryptAndDecodeSamples(
599 const cdm::InputBuffer& encrypted_buffer,
600 cdm::AudioFrames* audio_frames) {
601 DVLOG(1) << "DecryptAndDecodeSamples()";
603 // Trigger a crash on purpose for testing purpose.
604 if (key_system_ == kExternalClearKeyCrashKeySystem)
605 CHECK(false);
607 scoped_refptr<media::DecoderBuffer> buffer;
608 cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
610 if (status != cdm::kSuccess)
611 return status;
613 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
614 const uint8_t* data = NULL;
615 int32_t size = 0;
616 int64_t timestamp = 0;
617 if (!buffer->end_of_stream()) {
618 data = buffer->data();
619 size = buffer->data_size();
620 timestamp = encrypted_buffer.timestamp;
623 return audio_decoder_->DecodeBuffer(data, size, timestamp, audio_frames);
624 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
625 int64 timestamp_in_microseconds = kNoTimestamp;
626 if (!buffer->end_of_stream()) {
627 timestamp_in_microseconds = buffer->GetTimestamp().InMicroseconds();
628 DCHECK(timestamp_in_microseconds != kNoTimestamp);
630 return GenerateFakeAudioFrames(timestamp_in_microseconds, audio_frames);
631 #else
632 return cdm::kSuccess;
633 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
636 void ClearKeyCdm::Destroy() {
637 DVLOG(1) << "Destroy()";
638 delete this;
641 void ClearKeyCdm::ScheduleNextRenewal() {
642 // Prepare the next renewal message and set timer.
643 std::ostringstream msg_stream;
644 msg_stream << kRenewalHeader << " from ClearKey CDM set at time "
645 << host_->GetCurrentWallTime() << ".";
646 next_renewal_message_ = msg_stream.str();
648 host_->SetTimer(timer_delay_ms_, &next_renewal_message_[0]);
650 // Use a smaller timer delay at start-up to facilitate testing. Increase the
651 // timer delay up to a limit to avoid message spam.
652 if (timer_delay_ms_ < kMaxTimerDelayMs)
653 timer_delay_ms_ = std::min(2 * timer_delay_ms_, kMaxTimerDelayMs);
656 cdm::Status ClearKeyCdm::DecryptToMediaDecoderBuffer(
657 const cdm::InputBuffer& encrypted_buffer,
658 scoped_refptr<media::DecoderBuffer>* decrypted_buffer) {
659 DCHECK(decrypted_buffer);
660 scoped_refptr<media::DecoderBuffer> buffer =
661 CopyDecoderBufferFrom(encrypted_buffer);
663 if (buffer->end_of_stream()) {
664 *decrypted_buffer = buffer;
665 return cdm::kSuccess;
668 // Callback is called synchronously, so we can use variables on the stack.
669 media::Decryptor::Status status = media::Decryptor::kError;
670 // The AesDecryptor does not care what the stream type is. Pass kVideo
671 // for both audio and video decryption.
672 decryptor_.Decrypt(
673 media::Decryptor::kVideo,
674 buffer,
675 base::Bind(&CopyDecryptResults, &status, decrypted_buffer));
677 if (status == media::Decryptor::kError)
678 return cdm::kDecryptError;
680 if (status == media::Decryptor::kNoKey)
681 return cdm::kNoKey;
683 DCHECK_EQ(status, media::Decryptor::kSuccess);
684 return cdm::kSuccess;
687 void ClearKeyCdm::OnPlatformChallengeResponse(
688 const cdm::PlatformChallengeResponse& response) {
689 NOTIMPLEMENTED();
692 void ClearKeyCdm::OnQueryOutputProtectionStatus(
693 cdm::QueryResult result,
694 uint32_t link_mask,
695 uint32_t output_protection_mask) {
696 NOTIMPLEMENTED();
699 void ClearKeyCdm::LoadLoadableSession() {
700 std::string jwk_set = GenerateJWKSet(kLoadableSessionKey,
701 sizeof(kLoadableSessionKey),
702 kLoadableSessionKeyId,
703 sizeof(kLoadableSessionKeyId) - 1);
704 scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
705 base::Bind(&ClearKeyCdm::OnLoadSessionUpdated, base::Unretained(this)),
706 base::Bind(&ClearKeyCdm::OnPromiseFailed, base::Unretained(this),
707 promise_id_for_emulated_loadsession_)));
708 decryptor_.UpdateSession(session_id_for_emulated_loadsession_,
709 std::vector<uint8_t>(jwk_set.begin(), jwk_set.end()),
710 promise.Pass());
713 void ClearKeyCdm::OnSessionMessage(const std::string& session_id,
714 MediaKeys::MessageType message_type,
715 const std::vector<uint8>& message,
716 const GURL& legacy_destination_url) {
717 DVLOG(1) << "OnSessionMessage: " << message.size();
719 // Ignore the message when we are waiting to update the loadable session.
720 if (session_id == session_id_for_emulated_loadsession_)
721 return;
723 // OnSessionMessage() only called during CreateSession(), so no promise
724 // involved (OnSessionCreated() called to resolve the CreateSession()
725 // promise).
726 host_->OnSessionMessage(session_id.data(), session_id.length(),
727 cdm::kLicenseRequest,
728 reinterpret_cast<const char*>(message.data()),
729 message.size(), legacy_destination_url.spec().data(),
730 legacy_destination_url.spec().size());
733 void ClearKeyCdm::OnSessionKeysChange(const std::string& session_id,
734 bool has_additional_usable_key,
735 CdmKeysInfo keys_info) {
736 DVLOG(1) << "OnSessionKeysChange: " << keys_info.size();
738 std::string new_session_id = session_id;
739 if (new_session_id == session_id_for_emulated_loadsession_) {
740 // Save |keys_info| if the loadable session is still being created. This
741 // event will then be forwarded on in OnLoadSessionUpdated().
742 if (promise_id_for_emulated_loadsession_ != 0) {
743 has_received_keys_change_event_for_emulated_loadsession_ = true;
744 keys_info_for_emulated_loadsession_.swap(keys_info);
745 return;
748 // Loadable session has already been created, so pass this event on,
749 // using the session_id callers expect to see.
750 new_session_id = std::string(kLoadableSessionId);
753 std::vector<cdm::KeyInformation> keys_vector;
754 ConvertCdmKeysInfo(keys_info.get(), &keys_vector);
755 host_->OnSessionKeysChange(new_session_id.data(), new_session_id.length(),
756 has_additional_usable_key,
757 vector_as_array(&keys_vector), keys_vector.size());
760 void ClearKeyCdm::OnSessionClosed(const std::string& session_id) {
761 std::string new_session_id = session_id;
762 if (new_session_id == session_id_for_emulated_loadsession_)
763 new_session_id = std::string(kLoadableSessionId);
764 host_->OnSessionClosed(new_session_id.data(), new_session_id.length());
767 void ClearKeyCdm::OnSessionCreated(uint32 promise_id,
768 const std::string& session_id) {
769 // Save the latest session ID for renewal and file IO test messages.
770 last_session_id_ = session_id;
772 host_->OnResolveNewSessionPromise(promise_id, session_id.data(),
773 session_id.length());
776 void ClearKeyCdm::OnSessionLoaded(uint32 promise_id,
777 const std::string& session_id) {
778 // Save the latest session ID for renewal and file IO test messages.
779 last_session_id_ = session_id;
781 // |decryptor_| created some session as |session_id|, but going forward
782 // we need to map that to |kLoadableSessionId|, as that is what callers
783 // expect.
784 session_id_for_emulated_loadsession_ = session_id;
786 // Delay LoadLoadableSession() to test the case where Decrypt*() calls are
787 // made before the session is fully loaded.
788 const int64 kDelayToLoadSessionMs = 500;
790 // Defer resolving the promise until the session is loaded.
791 promise_id_for_emulated_loadsession_ = promise_id;
793 // Use the address of |session_id_for_emulated_loadsession_| as the timer
794 // context so that we can call LoadLoadableSession() when the timer expires.
795 host_->SetTimer(kDelayToLoadSessionMs, &session_id_for_emulated_loadsession_);
798 void ClearKeyCdm::OnLoadSessionUpdated() {
799 // This method is only called to finish loading sessions, so handle
800 // appropriately.
802 // |promise_id_for_emulated_loadsession_| is the LoadSession() promise,
803 // so resolve appropriately.
804 host_->OnResolveNewSessionPromise(promise_id_for_emulated_loadsession_,
805 kLoadableSessionId,
806 strlen(kLoadableSessionId));
807 promise_id_for_emulated_loadsession_ = 0;
809 // Generate the KeysChange event now that the session is "loaded" if one
810 // was seen.
811 // TODO(jrummell): Once the order of events is fixed in the spec, either
812 // require the keyschange event to have happened, or remove this code.
813 // http://crbug.com/448225
814 if (has_received_keys_change_event_for_emulated_loadsession_) {
815 std::vector<cdm::KeyInformation> keys_vector;
816 CdmKeysInfo keys_info;
817 keys_info.swap(keys_info_for_emulated_loadsession_);
818 has_received_keys_change_event_for_emulated_loadsession_ = false;
819 DCHECK(!keys_vector.empty());
820 ConvertCdmKeysInfo(keys_info.get(), &keys_vector);
821 host_->OnSessionKeysChange(
822 kLoadableSessionId, strlen(kLoadableSessionId), !keys_vector.empty(),
823 vector_as_array(&keys_vector), keys_vector.size());
827 void ClearKeyCdm::OnPromiseResolved(uint32 promise_id) {
828 host_->OnResolvePromise(promise_id);
831 void ClearKeyCdm::OnPromiseFailed(uint32 promise_id,
832 MediaKeys::Exception exception_code,
833 uint32 system_code,
834 const std::string& error_message) {
835 host_->OnRejectPromise(promise_id,
836 ConvertException(exception_code),
837 system_code,
838 error_message.data(),
839 error_message.length());
842 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
843 int64 ClearKeyCdm::CurrentTimeStampInMicroseconds() const {
844 return output_timestamp_base_in_microseconds_ +
845 base::Time::kMicrosecondsPerSecond *
846 total_samples_generated_ / samples_per_second_;
849 int ClearKeyCdm::GenerateFakeAudioFramesFromDuration(
850 int64 duration_in_microseconds,
851 cdm::AudioFrames* audio_frames) const {
852 int64 samples_to_generate = static_cast<double>(samples_per_second_) *
853 duration_in_microseconds / base::Time::kMicrosecondsPerSecond + 0.5;
854 if (samples_to_generate <= 0)
855 return 0;
857 int64 bytes_per_sample = channel_count_ * bits_per_channel_ / 8;
858 // |frame_size| must be a multiple of |bytes_per_sample|.
859 int64 frame_size = bytes_per_sample * samples_to_generate;
861 int64 timestamp = CurrentTimeStampInMicroseconds();
863 const int kHeaderSize = sizeof(timestamp) + sizeof(frame_size);
864 audio_frames->SetFrameBuffer(host_->Allocate(kHeaderSize + frame_size));
865 uint8_t* data = audio_frames->FrameBuffer()->Data();
867 memcpy(data, &timestamp, sizeof(timestamp));
868 data += sizeof(timestamp);
869 memcpy(data, &frame_size, sizeof(frame_size));
870 data += sizeof(frame_size);
871 // You won't hear anything because we have all zeros here. But the video
872 // should play just fine!
873 memset(data, 0, frame_size);
875 audio_frames->FrameBuffer()->SetSize(kHeaderSize + frame_size);
877 return samples_to_generate;
880 cdm::Status ClearKeyCdm::GenerateFakeAudioFrames(
881 int64 timestamp_in_microseconds,
882 cdm::AudioFrames* audio_frames) {
883 if (timestamp_in_microseconds == kNoTimestamp)
884 return cdm::kNeedMoreData;
886 // Return kNeedMoreData for the first frame because duration is unknown.
887 if (output_timestamp_base_in_microseconds_ == kNoTimestamp) {
888 output_timestamp_base_in_microseconds_ = timestamp_in_microseconds;
889 return cdm::kNeedMoreData;
892 int samples_generated = GenerateFakeAudioFramesFromDuration(
893 timestamp_in_microseconds - CurrentTimeStampInMicroseconds(),
894 audio_frames);
895 total_samples_generated_ += samples_generated;
897 return samples_generated == 0 ? cdm::kNeedMoreData : cdm::kSuccess;
899 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
901 void ClearKeyCdm::StartFileIOTest() {
902 file_io_test_runner_.reset(new FileIOTestRunner(
903 base::Bind(&ClearKeyCdmHost::CreateFileIO, base::Unretained(host_))));
904 file_io_test_runner_->RunAllTests(
905 base::Bind(&ClearKeyCdm::OnFileIOTestComplete, base::Unretained(this)));
908 void ClearKeyCdm::OnFileIOTestComplete(bool success) {
909 DVLOG(1) << __FUNCTION__ << ": " << success;
910 std::string message = GetFileIOTestResultMessage(success);
911 host_->OnSessionMessage(last_session_id_.data(), last_session_id_.length(),
912 cdm::kLicenseRequest, message.data(),
913 message.length(), NULL, 0);
914 file_io_test_runner_.reset();
917 } // namespace media