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"
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"
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().
41 // Temporarily disable possible loss of data warning.
42 MSVC_PUSH_DISABLE_WARNING(4244);
43 #include <libavformat/avformat.h>
47 #if !defined COMPONENT_BUILD
48 static base::AtExitManager g_at_exit_manager
;
51 // Prepare media library.
52 static bool InitializeFFmpegLibraries() {
53 media::InitializeMediaLibrary();
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
),
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';
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
;
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
;
161 return media::MediaKeys::TEMPORARY_SESSION
;
164 static media::EmeInitDataType
ConvertInitDataType(
165 cdm::InitDataType init_data_type
) {
166 switch (init_data_type
) {
168 return media::EmeInitDataType::CENC
;
170 return media::EmeInitDataType::KEYIDS
;
172 return media::EmeInitDataType::WEBM
;
175 return media::EmeInitDataType::UNKNOWN
;
178 cdm::KeyStatus
ConvertKeyStatus(media::CdmKeyInformation::KeyStatus status
) {
180 case media::CdmKeyInformation::KeyStatus::USABLE
:
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
;
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
{
216 explicit ScopedResetter(Type
* object
) : object_(object
) {}
217 ~ScopedResetter() { object_
->Reset(); }
223 void INITIALIZE_CDM_MODULE() {
224 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
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
,
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
;
247 if (cdm_interface_version
!= media::ClearKeyCdmInterface::kVersion
)
250 media::ClearKeyCdmHost
* host
= static_cast<media::ClearKeyCdmHost
*>(
251 get_cdm_host_func(media::ClearKeyCdmHost::kVersion
, user_data
));
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
;
265 ClearKeyCdm::ClearKeyCdm(ClearKeyCdmHost
* host
,
266 const std::string
& key_system
,
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))),
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)
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(
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),
309 base::Bind(&ClearKeyCdm::OnPromiseFailed
,
310 base::Unretained(this),
312 decryptor_
.CreateSessionAndGenerateRequest(
313 ConvertSessionType(session_type
), ConvertInitDataType(init_data_type
),
314 std::vector
<uint8_t>(init_data
, init_data
+ init_data_size
),
317 if (key_system_
== kExternalClearKeyFileIOTestKeySystem
)
321 // Loads a emulated stored session. Currently only |kLoadableSessionId|
322 // (containing a |kLoadableSessionKey| for |kLoadableSessionKeyId|) is
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);
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),
345 base::Bind(&ClearKeyCdm::OnPromiseFailed
,
346 base::Unretained(this),
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),
368 base::Bind(&ClearKeyCdm::OnPromiseFailed
, base::Unretained(this),
370 decryptor_
.UpdateSession(
371 web_session_str
, std::vector
<uint8_t>(response
, response
+ response_size
),
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
<>(
392 &ClearKeyCdm::OnPromiseResolved
, base::Unretained(this), promise_id
),
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_
;
408 // TODO(jrummell): This should be a DCHECK once blink does the proper
410 std::string
message("Not supported for non-persistent sessions.");
411 host_
->OnRejectPromise(promise_id
,
412 cdm::kInvalidAccessError
,
419 scoped_ptr
<media::SimpleCdmPromise
> promise(new media::CdmCallbackPromise
<>(
420 base::Bind(&ClearKeyCdm::OnPromiseResolved
, base::Unretained(this),
422 base::Bind(&ClearKeyCdm::OnPromiseFailed
, base::Unretained(this),
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();
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_
;
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
)
480 DCHECK(buffer
->data());
481 decrypted_block
->SetDecryptedBuffer(
482 host_
->Allocate(buffer
->data_size()));
483 memcpy(reinterpret_cast<void*>(decrypted_block
->DecryptedBuffer()->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)
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
;
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
);
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();
541 case cdm::kStreamTypeAudio
:
542 audio_decoder_
->Reset();
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();
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;
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
)
586 const uint8_t* data
= NULL
;
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
)
607 scoped_refptr
<media::DecoderBuffer
> buffer
;
608 cdm::Status status
= DecryptToMediaDecoderBuffer(encrypted_buffer
, &buffer
);
610 if (status
!= cdm::kSuccess
)
613 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
614 const uint8_t* data
= NULL
;
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
);
632 return cdm::kSuccess
;
633 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
636 void ClearKeyCdm::Destroy() {
637 DVLOG(1) << "Destroy()";
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.
673 media::Decryptor::kVideo
,
675 base::Bind(&CopyDecryptResults
, &status
, decrypted_buffer
));
677 if (status
== media::Decryptor::kError
)
678 return cdm::kDecryptError
;
680 if (status
== media::Decryptor::kNoKey
)
683 DCHECK_EQ(status
, media::Decryptor::kSuccess
);
684 return cdm::kSuccess
;
687 void ClearKeyCdm::OnPlatformChallengeResponse(
688 const cdm::PlatformChallengeResponse
& response
) {
692 void ClearKeyCdm::OnQueryOutputProtectionStatus(
693 cdm::QueryResult result
,
695 uint32_t output_protection_mask
) {
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()),
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_
)
723 // OnSessionMessage() only called during CreateSession(), so no promise
724 // involved (OnSessionCreated() called to resolve the CreateSession()
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
);
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
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
802 // |promise_id_for_emulated_loadsession_| is the LoadSession() promise,
803 // so resolve appropriately.
804 host_
->OnResolveNewSessionPromise(promise_id_for_emulated_loadsession_
,
806 strlen(kLoadableSessionId
));
807 promise_id_for_emulated_loadsession_
= 0;
809 // Generate the KeysChange event now that the session is "loaded" if one
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
,
834 const std::string
& error_message
) {
835 host_
->OnRejectPromise(promise_id
,
836 ConvertException(exception_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)
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
, ×tamp
, 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(),
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();