Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / media / cdm / ppapi / external_clear_key / clear_key_cdm.cc
blobfbe3829a814147390c6841394381ce6414572bfa
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 // TODO(jrummell): Remove kOutputNotAllowed, add kOutputRestricted to CDM
180 // interface. http://crbug.com/507791.
181 switch (status) {
182 case media::CdmKeyInformation::KeyStatus::USABLE:
183 return cdm::kUsable;
184 case media::CdmKeyInformation::KeyStatus::INTERNAL_ERROR:
185 return cdm::kInternalError;
186 case media::CdmKeyInformation::KeyStatus::EXPIRED:
187 return cdm::kExpired;
188 case media::CdmKeyInformation::KeyStatus::OUTPUT_RESTRICTED:
189 return cdm::kOutputNotAllowed;
190 case media::CdmKeyInformation::KeyStatus::OUTPUT_DOWNSCALED:
191 return cdm::kOutputDownscaled;
192 case media::CdmKeyInformation::KeyStatus::KEY_STATUS_PENDING:
193 return cdm::kStatusPending;
195 NOTREACHED();
196 return cdm::kInternalError;
199 // Shallow copy all the key information from |keys_info| into |keys_vector|.
200 // |keys_vector| is only valid for the lifetime of |keys_info| because it
201 // contains pointers into the latter.
202 void ConvertCdmKeysInfo(const std::vector<media::CdmKeyInformation*>& keys_info,
203 std::vector<cdm::KeyInformation>* keys_vector) {
204 keys_vector->reserve(keys_info.size());
205 for (const auto& key_info : keys_info) {
206 cdm::KeyInformation key;
207 key.key_id = vector_as_array(&key_info->key_id);
208 key.key_id_size = key_info->key_id.size();
209 key.status = ConvertKeyStatus(key_info->status);
210 key.system_code = key_info->system_code;
211 keys_vector->push_back(key);
215 template<typename Type>
216 class ScopedResetter {
217 public:
218 explicit ScopedResetter(Type* object) : object_(object) {}
219 ~ScopedResetter() { object_->Reset(); }
221 private:
222 Type* const object_;
225 void INITIALIZE_CDM_MODULE() {
226 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
227 av_register_all();
228 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
231 void DeinitializeCdmModule() {
234 void* CreateCdmInstance(int cdm_interface_version,
235 const char* key_system, uint32_t key_system_size,
236 GetCdmHostFunc get_cdm_host_func,
237 void* user_data) {
238 DVLOG(1) << "CreateCdmInstance()";
240 std::string key_system_string(key_system, key_system_size);
241 if (key_system_string != kExternalClearKeyKeySystem &&
242 key_system_string != kExternalClearKeyDecryptOnlyKeySystem &&
243 key_system_string != kExternalClearKeyFileIOTestKeySystem &&
244 key_system_string != kExternalClearKeyCrashKeySystem) {
245 DVLOG(1) << "Unsupported key system:" << key_system_string;
246 return NULL;
249 if (cdm_interface_version != media::ClearKeyCdmInterface::kVersion)
250 return NULL;
252 media::ClearKeyCdmHost* host = static_cast<media::ClearKeyCdmHost*>(
253 get_cdm_host_func(media::ClearKeyCdmHost::kVersion, user_data));
254 if (!host)
255 return NULL;
257 // TODO(jrummell): Obtain the proper origin for this instance.
258 return new media::ClearKeyCdm(host, key_system_string, GURL::EmptyGURL());
261 const char* GetCdmVersion() {
262 return kClearKeyCdmVersion;
265 namespace media {
267 ClearKeyCdm::ClearKeyCdm(ClearKeyCdmHost* host,
268 const std::string& key_system,
269 const GURL& origin)
270 : decryptor_(
271 origin,
272 base::Bind(&ClearKeyCdm::OnSessionMessage, base::Unretained(this)),
273 base::Bind(&ClearKeyCdm::OnSessionClosed, base::Unretained(this)),
274 base::Bind(&ClearKeyCdm::OnSessionKeysChange,
275 base::Unretained(this))),
276 host_(host),
277 key_system_(key_system),
278 has_received_keys_change_event_for_emulated_loadsession_(false),
279 timer_delay_ms_(kInitialTimerDelayMs),
280 renewal_timer_set_(false) {
281 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
282 channel_count_ = 0;
283 bits_per_channel_ = 0;
284 samples_per_second_ = 0;
285 output_timestamp_base_in_microseconds_ = kNoTimestamp;
286 total_samples_generated_ = 0;
287 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
290 ClearKeyCdm::~ClearKeyCdm() {}
292 void ClearKeyCdm::Initialize(bool /* allow_distinctive_identifier */,
293 bool /* allow_persistent_state */) {
294 // Implementation doesn't use distinctive identifier nor save persistent data,
295 // so nothing to do with these values.
298 void ClearKeyCdm::CreateSessionAndGenerateRequest(
299 uint32 promise_id,
300 cdm::SessionType session_type,
301 cdm::InitDataType init_data_type,
302 const uint8* init_data,
303 uint32 init_data_size) {
304 DVLOG(1) << __FUNCTION__;
306 scoped_ptr<media::NewSessionCdmPromise> promise(
307 new media::CdmCallbackPromise<std::string>(
308 base::Bind(&ClearKeyCdm::OnSessionCreated,
309 base::Unretained(this),
310 promise_id),
311 base::Bind(&ClearKeyCdm::OnPromiseFailed,
312 base::Unretained(this),
313 promise_id)));
314 decryptor_.CreateSessionAndGenerateRequest(
315 ConvertSessionType(session_type), ConvertInitDataType(init_data_type),
316 std::vector<uint8_t>(init_data, init_data + init_data_size),
317 promise.Pass());
319 if (key_system_ == kExternalClearKeyFileIOTestKeySystem)
320 StartFileIOTest();
323 // Loads a emulated stored session. Currently only |kLoadableSessionId|
324 // (containing a |kLoadableSessionKey| for |kLoadableSessionKeyId|) is
325 // supported.
326 void ClearKeyCdm::LoadSession(uint32 promise_id,
327 cdm::SessionType session_type,
328 const char* session_id,
329 uint32_t session_id_length) {
330 DVLOG(1) << __FUNCTION__;
331 DCHECK_EQ(session_type, cdm::kPersistentLicense);
333 if (std::string(kLoadableSessionId) !=
334 std::string(session_id, session_id_length)) {
335 host_->OnResolveNewSessionPromise(promise_id, nullptr, 0);
336 return;
339 // Only allowed to successfully load this session once.
340 DCHECK(session_id_for_emulated_loadsession_.empty());
342 scoped_ptr<media::NewSessionCdmPromise> promise(
343 new media::CdmCallbackPromise<std::string>(
344 base::Bind(&ClearKeyCdm::OnSessionLoaded,
345 base::Unretained(this),
346 promise_id),
347 base::Bind(&ClearKeyCdm::OnPromiseFailed,
348 base::Unretained(this),
349 promise_id)));
350 decryptor_.CreateSessionAndGenerateRequest(
351 MediaKeys::TEMPORARY_SESSION, EmeInitDataType::WEBM,
352 std::vector<uint8_t>(), promise.Pass());
355 void ClearKeyCdm::UpdateSession(uint32 promise_id,
356 const char* session_id,
357 uint32_t session_id_length,
358 const uint8* response,
359 uint32 response_size) {
360 DVLOG(1) << __FUNCTION__;
361 std::string web_session_str(session_id, session_id_length);
363 // If updating the loadable session, use the actual session id generated.
364 if (web_session_str == std::string(kLoadableSessionId))
365 web_session_str = session_id_for_emulated_loadsession_;
367 scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
368 base::Bind(&ClearKeyCdm::OnPromiseResolved, base::Unretained(this),
369 promise_id),
370 base::Bind(&ClearKeyCdm::OnPromiseFailed, base::Unretained(this),
371 promise_id)));
372 decryptor_.UpdateSession(
373 web_session_str, std::vector<uint8_t>(response, response + response_size),
374 promise.Pass());
376 if (!renewal_timer_set_) {
377 ScheduleNextRenewal();
378 renewal_timer_set_ = true;
382 void ClearKeyCdm::CloseSession(uint32 promise_id,
383 const char* session_id,
384 uint32_t session_id_length) {
385 DVLOG(1) << __FUNCTION__;
386 std::string web_session_str(session_id, session_id_length);
388 // If closing the loadable session, use the actual session id generated.
389 if (web_session_str == std::string(kLoadableSessionId))
390 web_session_str = session_id_for_emulated_loadsession_;
392 scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
393 base::Bind(
394 &ClearKeyCdm::OnPromiseResolved, base::Unretained(this), promise_id),
395 base::Bind(
396 &ClearKeyCdm::OnPromiseFailed, base::Unretained(this), promise_id)));
397 decryptor_.CloseSession(web_session_str, promise.Pass());
400 void ClearKeyCdm::RemoveSession(uint32 promise_id,
401 const char* session_id,
402 uint32_t session_id_length) {
403 DVLOG(1) << __FUNCTION__;
404 std::string web_session_str(session_id, session_id_length);
406 // RemoveSession only allowed for the loadable session.
407 if (web_session_str == std::string(kLoadableSessionId)) {
408 web_session_str = session_id_for_emulated_loadsession_;
409 } else {
410 // TODO(jrummell): This should be a DCHECK once blink does the proper
411 // checks.
412 std::string message("Not supported for non-persistent sessions.");
413 host_->OnRejectPromise(promise_id,
414 cdm::kInvalidAccessError,
416 message.data(),
417 message.length());
418 return;
421 scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
422 base::Bind(&ClearKeyCdm::OnPromiseResolved, base::Unretained(this),
423 promise_id),
424 base::Bind(&ClearKeyCdm::OnPromiseFailed, base::Unretained(this),
425 promise_id)));
426 decryptor_.RemoveSession(web_session_str, promise.Pass());
429 void ClearKeyCdm::SetServerCertificate(uint32 promise_id,
430 const uint8_t* server_certificate_data,
431 uint32_t server_certificate_data_size) {
432 // ClearKey doesn't use a server certificate.
433 host_->OnResolvePromise(promise_id);
436 void ClearKeyCdm::TimerExpired(void* context) {
437 if (context == &session_id_for_emulated_loadsession_) {
438 LoadLoadableSession();
439 return;
442 DCHECK(renewal_timer_set_);
443 std::string renewal_message;
444 if (!next_renewal_message_.empty() &&
445 context == &next_renewal_message_[0]) {
446 renewal_message = next_renewal_message_;
447 } else {
448 renewal_message = "ERROR: Invalid timer context found!";
451 // This URL is only used for testing the code path for defaultURL.
452 // There is no service at this URL, so applications should ignore it.
453 const char url[] = "http://test.externalclearkey.chromium.org";
455 host_->OnSessionMessage(last_session_id_.data(), last_session_id_.length(),
456 cdm::kLicenseRenewal, renewal_message.data(),
457 renewal_message.length(), url, arraysize(url) - 1);
459 ScheduleNextRenewal();
462 static void CopyDecryptResults(
463 media::Decryptor::Status* status_copy,
464 scoped_refptr<media::DecoderBuffer>* buffer_copy,
465 media::Decryptor::Status status,
466 const scoped_refptr<media::DecoderBuffer>& buffer) {
467 *status_copy = status;
468 *buffer_copy = buffer;
471 cdm::Status ClearKeyCdm::Decrypt(const cdm::InputBuffer& encrypted_buffer,
472 cdm::DecryptedBlock* decrypted_block) {
473 DVLOG(1) << "Decrypt()";
474 DCHECK(encrypted_buffer.data);
476 scoped_refptr<media::DecoderBuffer> buffer;
477 cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
479 if (status != cdm::kSuccess)
480 return status;
482 DCHECK(buffer->data());
483 decrypted_block->SetDecryptedBuffer(
484 host_->Allocate(buffer->data_size()));
485 memcpy(reinterpret_cast<void*>(decrypted_block->DecryptedBuffer()->Data()),
486 buffer->data(),
487 buffer->data_size());
488 decrypted_block->DecryptedBuffer()->SetSize(buffer->data_size());
489 decrypted_block->SetTimestamp(buffer->timestamp().InMicroseconds());
491 return cdm::kSuccess;
494 cdm::Status ClearKeyCdm::InitializeAudioDecoder(
495 const cdm::AudioDecoderConfig& audio_decoder_config) {
496 if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
497 return cdm::kSessionError;
499 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
500 if (!audio_decoder_)
501 audio_decoder_.reset(new media::FFmpegCdmAudioDecoder(host_));
503 if (!audio_decoder_->Initialize(audio_decoder_config))
504 return cdm::kSessionError;
506 return cdm::kSuccess;
507 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
508 channel_count_ = audio_decoder_config.channel_count;
509 bits_per_channel_ = audio_decoder_config.bits_per_channel;
510 samples_per_second_ = audio_decoder_config.samples_per_second;
511 return cdm::kSuccess;
512 #else
513 NOTIMPLEMENTED();
514 return cdm::kSessionError;
515 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
518 cdm::Status ClearKeyCdm::InitializeVideoDecoder(
519 const cdm::VideoDecoderConfig& video_decoder_config) {
520 if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
521 return cdm::kSessionError;
523 if (video_decoder_ && video_decoder_->is_initialized()) {
524 DCHECK(!video_decoder_->is_initialized());
525 return cdm::kSessionError;
528 // Any uninitialized decoder will be replaced.
529 video_decoder_ = CreateVideoDecoder(host_, video_decoder_config);
530 if (!video_decoder_)
531 return cdm::kSessionError;
533 return cdm::kSuccess;
536 void ClearKeyCdm::ResetDecoder(cdm::StreamType decoder_type) {
537 DVLOG(1) << "ResetDecoder()";
538 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
539 switch (decoder_type) {
540 case cdm::kStreamTypeVideo:
541 video_decoder_->Reset();
542 break;
543 case cdm::kStreamTypeAudio:
544 audio_decoder_->Reset();
545 break;
546 default:
547 NOTREACHED() << "ResetDecoder(): invalid cdm::StreamType";
549 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
550 if (decoder_type == cdm::kStreamTypeAudio) {
551 output_timestamp_base_in_microseconds_ = kNoTimestamp;
552 total_samples_generated_ = 0;
554 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
557 void ClearKeyCdm::DeinitializeDecoder(cdm::StreamType decoder_type) {
558 DVLOG(1) << "DeinitializeDecoder()";
559 switch (decoder_type) {
560 case cdm::kStreamTypeVideo:
561 video_decoder_->Deinitialize();
562 break;
563 case cdm::kStreamTypeAudio:
564 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
565 audio_decoder_->Deinitialize();
566 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
567 output_timestamp_base_in_microseconds_ = kNoTimestamp;
568 total_samples_generated_ = 0;
569 #endif
570 break;
571 default:
572 NOTREACHED() << "DeinitializeDecoder(): invalid cdm::StreamType";
576 cdm::Status ClearKeyCdm::DecryptAndDecodeFrame(
577 const cdm::InputBuffer& encrypted_buffer,
578 cdm::VideoFrame* decoded_frame) {
579 DVLOG(1) << "DecryptAndDecodeFrame()";
580 TRACE_EVENT0("media", "ClearKeyCdm::DecryptAndDecodeFrame");
582 scoped_refptr<media::DecoderBuffer> buffer;
583 cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
585 if (status != cdm::kSuccess)
586 return status;
588 const uint8_t* data = NULL;
589 int32_t size = 0;
590 int64_t timestamp = 0;
591 if (!buffer->end_of_stream()) {
592 data = buffer->data();
593 size = buffer->data_size();
594 timestamp = encrypted_buffer.timestamp;
597 return video_decoder_->DecodeFrame(data, size, timestamp, decoded_frame);
600 cdm::Status ClearKeyCdm::DecryptAndDecodeSamples(
601 const cdm::InputBuffer& encrypted_buffer,
602 cdm::AudioFrames* audio_frames) {
603 DVLOG(1) << "DecryptAndDecodeSamples()";
605 // Trigger a crash on purpose for testing purpose.
606 if (key_system_ == kExternalClearKeyCrashKeySystem)
607 CHECK(false);
609 scoped_refptr<media::DecoderBuffer> buffer;
610 cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
612 if (status != cdm::kSuccess)
613 return status;
615 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
616 const uint8_t* data = NULL;
617 int32_t size = 0;
618 int64_t timestamp = 0;
619 if (!buffer->end_of_stream()) {
620 data = buffer->data();
621 size = buffer->data_size();
622 timestamp = encrypted_buffer.timestamp;
625 return audio_decoder_->DecodeBuffer(data, size, timestamp, audio_frames);
626 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
627 int64 timestamp_in_microseconds = kNoTimestamp;
628 if (!buffer->end_of_stream()) {
629 timestamp_in_microseconds = buffer->GetTimestamp().InMicroseconds();
630 DCHECK(timestamp_in_microseconds != kNoTimestamp);
632 return GenerateFakeAudioFrames(timestamp_in_microseconds, audio_frames);
633 #else
634 return cdm::kSuccess;
635 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
638 void ClearKeyCdm::Destroy() {
639 DVLOG(1) << "Destroy()";
640 delete this;
643 void ClearKeyCdm::ScheduleNextRenewal() {
644 // Prepare the next renewal message and set timer.
645 std::ostringstream msg_stream;
646 msg_stream << kRenewalHeader << " from ClearKey CDM set at time "
647 << host_->GetCurrentWallTime() << ".";
648 next_renewal_message_ = msg_stream.str();
650 host_->SetTimer(timer_delay_ms_, &next_renewal_message_[0]);
652 // Use a smaller timer delay at start-up to facilitate testing. Increase the
653 // timer delay up to a limit to avoid message spam.
654 if (timer_delay_ms_ < kMaxTimerDelayMs)
655 timer_delay_ms_ = std::min(2 * timer_delay_ms_, kMaxTimerDelayMs);
658 cdm::Status ClearKeyCdm::DecryptToMediaDecoderBuffer(
659 const cdm::InputBuffer& encrypted_buffer,
660 scoped_refptr<media::DecoderBuffer>* decrypted_buffer) {
661 DCHECK(decrypted_buffer);
662 scoped_refptr<media::DecoderBuffer> buffer =
663 CopyDecoderBufferFrom(encrypted_buffer);
665 if (buffer->end_of_stream()) {
666 *decrypted_buffer = buffer;
667 return cdm::kSuccess;
670 // Callback is called synchronously, so we can use variables on the stack.
671 media::Decryptor::Status status = media::Decryptor::kError;
672 // The AesDecryptor does not care what the stream type is. Pass kVideo
673 // for both audio and video decryption.
674 decryptor_.Decrypt(
675 media::Decryptor::kVideo,
676 buffer,
677 base::Bind(&CopyDecryptResults, &status, decrypted_buffer));
679 if (status == media::Decryptor::kError)
680 return cdm::kDecryptError;
682 if (status == media::Decryptor::kNoKey)
683 return cdm::kNoKey;
685 DCHECK_EQ(status, media::Decryptor::kSuccess);
686 return cdm::kSuccess;
689 void ClearKeyCdm::OnPlatformChallengeResponse(
690 const cdm::PlatformChallengeResponse& response) {
691 NOTIMPLEMENTED();
694 void ClearKeyCdm::OnQueryOutputProtectionStatus(
695 cdm::QueryResult result,
696 uint32_t link_mask,
697 uint32_t output_protection_mask) {
698 NOTIMPLEMENTED();
701 void ClearKeyCdm::LoadLoadableSession() {
702 std::string jwk_set = GenerateJWKSet(kLoadableSessionKey,
703 sizeof(kLoadableSessionKey),
704 kLoadableSessionKeyId,
705 sizeof(kLoadableSessionKeyId) - 1);
706 scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
707 base::Bind(&ClearKeyCdm::OnLoadSessionUpdated, base::Unretained(this)),
708 base::Bind(&ClearKeyCdm::OnPromiseFailed, base::Unretained(this),
709 promise_id_for_emulated_loadsession_)));
710 decryptor_.UpdateSession(session_id_for_emulated_loadsession_,
711 std::vector<uint8_t>(jwk_set.begin(), jwk_set.end()),
712 promise.Pass());
715 void ClearKeyCdm::OnSessionMessage(const std::string& session_id,
716 MediaKeys::MessageType message_type,
717 const std::vector<uint8>& message,
718 const GURL& legacy_destination_url) {
719 DVLOG(1) << "OnSessionMessage: " << message.size();
721 // Ignore the message when we are waiting to update the loadable session.
722 if (session_id == session_id_for_emulated_loadsession_)
723 return;
725 // OnSessionMessage() only called during CreateSession(), so no promise
726 // involved (OnSessionCreated() called to resolve the CreateSession()
727 // promise).
728 host_->OnSessionMessage(session_id.data(), session_id.length(),
729 cdm::kLicenseRequest,
730 reinterpret_cast<const char*>(message.data()),
731 message.size(), legacy_destination_url.spec().data(),
732 legacy_destination_url.spec().size());
735 void ClearKeyCdm::OnSessionKeysChange(const std::string& session_id,
736 bool has_additional_usable_key,
737 CdmKeysInfo keys_info) {
738 DVLOG(1) << "OnSessionKeysChange: " << keys_info.size();
740 std::string new_session_id = session_id;
741 if (new_session_id == session_id_for_emulated_loadsession_) {
742 // Save |keys_info| if the loadable session is still being created. This
743 // event will then be forwarded on in OnLoadSessionUpdated().
744 if (promise_id_for_emulated_loadsession_ != 0) {
745 has_received_keys_change_event_for_emulated_loadsession_ = true;
746 keys_info_for_emulated_loadsession_.swap(keys_info);
747 return;
750 // Loadable session has already been created, so pass this event on,
751 // using the session_id callers expect to see.
752 new_session_id = std::string(kLoadableSessionId);
755 std::vector<cdm::KeyInformation> keys_vector;
756 ConvertCdmKeysInfo(keys_info.get(), &keys_vector);
757 host_->OnSessionKeysChange(new_session_id.data(), new_session_id.length(),
758 has_additional_usable_key,
759 vector_as_array(&keys_vector), keys_vector.size());
762 void ClearKeyCdm::OnSessionClosed(const std::string& session_id) {
763 std::string new_session_id = session_id;
764 if (new_session_id == session_id_for_emulated_loadsession_)
765 new_session_id = std::string(kLoadableSessionId);
766 host_->OnSessionClosed(new_session_id.data(), new_session_id.length());
769 void ClearKeyCdm::OnSessionCreated(uint32 promise_id,
770 const std::string& session_id) {
771 // Save the latest session ID for renewal and file IO test messages.
772 last_session_id_ = session_id;
774 host_->OnResolveNewSessionPromise(promise_id, session_id.data(),
775 session_id.length());
778 void ClearKeyCdm::OnSessionLoaded(uint32 promise_id,
779 const std::string& session_id) {
780 // Save the latest session ID for renewal and file IO test messages.
781 last_session_id_ = session_id;
783 // |decryptor_| created some session as |session_id|, but going forward
784 // we need to map that to |kLoadableSessionId|, as that is what callers
785 // expect.
786 session_id_for_emulated_loadsession_ = session_id;
788 // Delay LoadLoadableSession() to test the case where Decrypt*() calls are
789 // made before the session is fully loaded.
790 const int64 kDelayToLoadSessionMs = 500;
792 // Defer resolving the promise until the session is loaded.
793 promise_id_for_emulated_loadsession_ = promise_id;
795 // Use the address of |session_id_for_emulated_loadsession_| as the timer
796 // context so that we can call LoadLoadableSession() when the timer expires.
797 host_->SetTimer(kDelayToLoadSessionMs, &session_id_for_emulated_loadsession_);
800 void ClearKeyCdm::OnLoadSessionUpdated() {
801 // This method is only called to finish loading sessions, so handle
802 // appropriately.
804 // |promise_id_for_emulated_loadsession_| is the LoadSession() promise,
805 // so resolve appropriately.
806 host_->OnResolveNewSessionPromise(promise_id_for_emulated_loadsession_,
807 kLoadableSessionId,
808 strlen(kLoadableSessionId));
809 promise_id_for_emulated_loadsession_ = 0;
811 // Generate the KeysChange event now that the session is "loaded" if one
812 // was seen.
813 // TODO(jrummell): Once the order of events is fixed in the spec, either
814 // require the keyschange event to have happened, or remove this code.
815 // http://crbug.com/448225
816 if (has_received_keys_change_event_for_emulated_loadsession_) {
817 std::vector<cdm::KeyInformation> keys_vector;
818 CdmKeysInfo keys_info;
819 keys_info.swap(keys_info_for_emulated_loadsession_);
820 has_received_keys_change_event_for_emulated_loadsession_ = false;
821 DCHECK(!keys_vector.empty());
822 ConvertCdmKeysInfo(keys_info.get(), &keys_vector);
823 host_->OnSessionKeysChange(
824 kLoadableSessionId, strlen(kLoadableSessionId), !keys_vector.empty(),
825 vector_as_array(&keys_vector), keys_vector.size());
829 void ClearKeyCdm::OnPromiseResolved(uint32 promise_id) {
830 host_->OnResolvePromise(promise_id);
833 void ClearKeyCdm::OnPromiseFailed(uint32 promise_id,
834 MediaKeys::Exception exception_code,
835 uint32 system_code,
836 const std::string& error_message) {
837 host_->OnRejectPromise(promise_id,
838 ConvertException(exception_code),
839 system_code,
840 error_message.data(),
841 error_message.length());
844 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
845 int64 ClearKeyCdm::CurrentTimeStampInMicroseconds() const {
846 return output_timestamp_base_in_microseconds_ +
847 base::Time::kMicrosecondsPerSecond *
848 total_samples_generated_ / samples_per_second_;
851 int ClearKeyCdm::GenerateFakeAudioFramesFromDuration(
852 int64 duration_in_microseconds,
853 cdm::AudioFrames* audio_frames) const {
854 int64 samples_to_generate = static_cast<double>(samples_per_second_) *
855 duration_in_microseconds / base::Time::kMicrosecondsPerSecond + 0.5;
856 if (samples_to_generate <= 0)
857 return 0;
859 int64 bytes_per_sample = channel_count_ * bits_per_channel_ / 8;
860 // |frame_size| must be a multiple of |bytes_per_sample|.
861 int64 frame_size = bytes_per_sample * samples_to_generate;
863 int64 timestamp = CurrentTimeStampInMicroseconds();
865 const int kHeaderSize = sizeof(timestamp) + sizeof(frame_size);
866 audio_frames->SetFrameBuffer(host_->Allocate(kHeaderSize + frame_size));
867 uint8_t* data = audio_frames->FrameBuffer()->Data();
869 memcpy(data, &timestamp, sizeof(timestamp));
870 data += sizeof(timestamp);
871 memcpy(data, &frame_size, sizeof(frame_size));
872 data += sizeof(frame_size);
873 // You won't hear anything because we have all zeros here. But the video
874 // should play just fine!
875 memset(data, 0, frame_size);
877 audio_frames->FrameBuffer()->SetSize(kHeaderSize + frame_size);
879 return samples_to_generate;
882 cdm::Status ClearKeyCdm::GenerateFakeAudioFrames(
883 int64 timestamp_in_microseconds,
884 cdm::AudioFrames* audio_frames) {
885 if (timestamp_in_microseconds == kNoTimestamp)
886 return cdm::kNeedMoreData;
888 // Return kNeedMoreData for the first frame because duration is unknown.
889 if (output_timestamp_base_in_microseconds_ == kNoTimestamp) {
890 output_timestamp_base_in_microseconds_ = timestamp_in_microseconds;
891 return cdm::kNeedMoreData;
894 int samples_generated = GenerateFakeAudioFramesFromDuration(
895 timestamp_in_microseconds - CurrentTimeStampInMicroseconds(),
896 audio_frames);
897 total_samples_generated_ += samples_generated;
899 return samples_generated == 0 ? cdm::kNeedMoreData : cdm::kSuccess;
901 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
903 void ClearKeyCdm::StartFileIOTest() {
904 file_io_test_runner_.reset(new FileIOTestRunner(
905 base::Bind(&ClearKeyCdmHost::CreateFileIO, base::Unretained(host_))));
906 file_io_test_runner_->RunAllTests(
907 base::Bind(&ClearKeyCdm::OnFileIOTestComplete, base::Unretained(this)));
910 void ClearKeyCdm::OnFileIOTestComplete(bool success) {
911 DVLOG(1) << __FUNCTION__ << ": " << success;
912 std::string message = GetFileIOTestResultMessage(success);
913 host_->OnSessionMessage(last_session_id_.data(), last_session_id_.length(),
914 cdm::kLicenseRequest, message.data(),
915 message.length(), NULL, 0);
916 file_io_test_runner_.reset();
919 } // namespace media