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/clear_key_cdm.h"
12 #include "base/bind.h"
13 #include "base/debug/trace_event.h"
14 #include "base/logging.h"
15 #include "base/time/time.h"
16 #include "media/base/decoder_buffer.h"
17 #include "media/base/decrypt_config.h"
18 #include "media/cdm/ppapi/cdm_video_decoder.h"
20 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
21 #include "base/basictypes.h"
22 const int64 kNoTimestamp
= kint64min
;
23 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
25 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
26 #include "base/at_exit.h"
27 #include "base/files/file_path.h"
28 #include "base/path_service.h"
29 #include "media/base/media.h"
30 #include "media/cdm/ppapi/ffmpeg_cdm_audio_decoder.h"
31 #include "media/cdm/ppapi/ffmpeg_cdm_video_decoder.h"
33 // Include FFmpeg avformat.h for av_register_all().
35 // Temporarily disable possible loss of data warning.
36 MSVC_PUSH_DISABLE_WARNING(4244);
37 #include <libavformat/avformat.h>
41 // TODO(tomfinegan): When COMPONENT_BUILD is not defined an AtExitManager must
42 // exist before the call to InitializeFFmpegLibraries(). This should no longer
43 // be required after http://crbug.com/91970 because we'll be able to get rid of
44 // InitializeFFmpegLibraries().
45 #if !defined COMPONENT_BUILD
46 static base::AtExitManager g_at_exit_manager
;
49 // TODO(tomfinegan): InitializeFFmpegLibraries() and |g_cdm_module_initialized|
50 // are required for running in the sandbox, and should no longer be required
51 // after http://crbug.com/91970 is fixed.
52 static bool InitializeFFmpegLibraries() {
53 base::FilePath file_path
;
54 CHECK(PathService::Get(base::DIR_MODULE
, &file_path
));
55 CHECK(media::InitializeMediaLibrary(file_path
));
59 static bool g_ffmpeg_lib_initialized
= InitializeFFmpegLibraries();
60 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
62 const char kClearKeyCdmVersion
[] = "0.1.0.1";
63 const char kExternalClearKeyKeySystem
[] = "org.chromium.externalclearkey";
64 const int64 kSecondsPerMinute
= 60;
65 const int64 kMsPerSecond
= 1000;
66 const int64 kInitialTimerDelayMs
= 200;
67 const int64 kMaxTimerDelayMs
= 1 * kSecondsPerMinute
* kMsPerSecond
;
68 // Heart beat message header. If a key message starts with |kHeartBeatHeader|,
69 // it's a heart beat message. Otherwise, it's a key request.
70 const char kHeartBeatHeader
[] = "HEARTBEAT";
72 // Copies |input_buffer| into a media::DecoderBuffer. If the |input_buffer| is
73 // empty, an empty (end-of-stream) media::DecoderBuffer is returned.
74 static scoped_refptr
<media::DecoderBuffer
> CopyDecoderBufferFrom(
75 const cdm::InputBuffer
& input_buffer
) {
76 if (!input_buffer
.data
) {
77 DCHECK(!input_buffer
.data_size
);
78 return media::DecoderBuffer::CreateEOSBuffer();
81 // TODO(tomfinegan): Get rid of this copy.
82 scoped_refptr
<media::DecoderBuffer
> output_buffer
=
83 media::DecoderBuffer::CopyFrom(input_buffer
.data
, input_buffer
.data_size
);
85 std::vector
<media::SubsampleEntry
> subsamples
;
86 for (uint32_t i
= 0; i
< input_buffer
.num_subsamples
; ++i
) {
87 media::SubsampleEntry subsample
;
88 subsample
.clear_bytes
= input_buffer
.subsamples
[i
].clear_bytes
;
89 subsample
.cypher_bytes
= input_buffer
.subsamples
[i
].cipher_bytes
;
90 subsamples
.push_back(subsample
);
93 scoped_ptr
<media::DecryptConfig
> decrypt_config(new media::DecryptConfig(
94 std::string(reinterpret_cast<const char*>(input_buffer
.key_id
),
95 input_buffer
.key_id_size
),
96 std::string(reinterpret_cast<const char*>(input_buffer
.iv
),
97 input_buffer
.iv_size
),
98 input_buffer
.data_offset
,
101 output_buffer
->set_decrypt_config(decrypt_config
.Pass());
102 output_buffer
->set_timestamp(
103 base::TimeDelta::FromMicroseconds(input_buffer
.timestamp
));
105 return output_buffer
;
108 template<typename Type
>
109 class ScopedResetter
{
111 explicit ScopedResetter(Type
* object
) : object_(object
) {}
112 ~ScopedResetter() { object_
->Reset(); }
118 void INITIALIZE_CDM_MODULE() {
119 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
120 DVLOG(2) << "FFmpeg libraries initialized: " << g_ffmpeg_lib_initialized
;
122 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
125 void DeinitializeCdmModule() {
128 void* CreateCdmInstance(
129 int cdm_interface_version
,
130 const char* key_system
, uint32_t key_system_size
,
131 GetCdmHostFunc get_cdm_host_func
, void* user_data
) {
132 DVLOG(1) << "CreateCdmInstance()";
134 if (std::string(key_system
, key_system_size
) != kExternalClearKeyKeySystem
) {
135 DVLOG(1) << "Unsupported key system.";
139 if (cdm_interface_version
!= cdm::ContentDecryptionModule_2::kVersion
)
142 cdm::ContentDecryptionModule_2::Host
* host
=
143 static_cast<cdm::ContentDecryptionModule_2::Host
*>(get_cdm_host_func(
144 cdm::ContentDecryptionModule_2::Host::kVersion
, user_data
));
148 return new media::ClearKeyCdm(host
);
151 const char* GetCdmVersion() {
152 return kClearKeyCdmVersion
;
157 ClearKeyCdm::Client::Client() : status_(kKeyError
) {}
159 ClearKeyCdm::Client::~Client() {}
161 void ClearKeyCdm::Client::Reset() {
164 key_message_
.clear();
165 default_url_
.clear();
168 void ClearKeyCdm::Client::KeyAdded(const std::string
& session_id
) {
170 session_id_
= session_id
;
173 void ClearKeyCdm::Client::KeyError(const std::string
& session_id
,
174 media::MediaKeys::KeyError error_code
,
177 session_id_
= session_id
;
180 void ClearKeyCdm::Client::KeyMessage(const std::string
& session_id
,
181 const std::vector
<uint8
>& message
,
182 const std::string
& default_url
) {
183 status_
= kKeyMessage
;
184 session_id_
= session_id
;
185 key_message_
= message
;
186 default_url_
= default_url
;
189 ClearKeyCdm::ClearKeyCdm(cdm::Host
* host
)
190 : decryptor_(base::Bind(&Client::KeyAdded
, base::Unretained(&client_
)),
191 base::Bind(&Client::KeyError
, base::Unretained(&client_
)),
192 base::Bind(&Client::KeyMessage
, base::Unretained(&client_
))),
194 timer_delay_ms_(kInitialTimerDelayMs
),
196 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
198 bits_per_channel_
= 0;
199 samples_per_second_
= 0;
200 output_timestamp_base_in_microseconds_
= kNoTimestamp
;
201 total_samples_generated_
= 0;
202 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
205 ClearKeyCdm::~ClearKeyCdm() {}
207 cdm::Status
ClearKeyCdm::GenerateKeyRequest(const char* type
,
209 const uint8_t* init_data
,
210 uint32_t init_data_size
) {
211 DVLOG(1) << "GenerateKeyRequest()";
212 base::AutoLock
auto_lock(client_lock_
);
213 ScopedResetter
<Client
> auto_resetter(&client_
);
214 decryptor_
.GenerateKeyRequest(std::string(type
, type_size
),
215 init_data
, init_data_size
);
217 if (client_
.status() != Client::kKeyMessage
) {
218 host_
->SendKeyError(NULL
, 0, cdm::kUnknownError
, 0);
219 return cdm::kSessionError
;
222 host_
->SendKeyMessage(
223 client_
.session_id().data(), client_
.session_id().size(),
224 reinterpret_cast<const char*>(&client_
.key_message()[0]),
225 client_
.key_message().size(),
226 client_
.default_url().data(), client_
.default_url().size());
228 // Only save the latest session ID for heartbeat messages.
229 heartbeat_session_id_
= client_
.session_id();
231 return cdm::kSuccess
;
234 cdm::Status
ClearKeyCdm::AddKey(const char* session_id
,
235 uint32_t session_id_size
,
238 const uint8_t* key_id
,
239 uint32_t key_id_size
) {
240 DVLOG(1) << "AddKey()";
241 base::AutoLock
auto_lock(client_lock_
);
242 ScopedResetter
<Client
> auto_resetter(&client_
);
243 decryptor_
.AddKey(key
, key_size
, key_id
, key_id_size
,
244 std::string(session_id
, session_id_size
));
246 if (client_
.status() != Client::kKeyAdded
)
247 return cdm::kSessionError
;
250 ScheduleNextHeartBeat();
254 return cdm::kSuccess
;
257 cdm::Status
ClearKeyCdm::CancelKeyRequest(const char* session_id
,
258 uint32_t session_id_size
) {
259 DVLOG(1) << "CancelKeyRequest()";
260 base::AutoLock
auto_lock(client_lock_
);
261 ScopedResetter
<Client
> auto_resetter(&client_
);
262 decryptor_
.CancelKeyRequest(std::string(session_id
, session_id_size
));
263 return cdm::kSuccess
;
266 void ClearKeyCdm::TimerExpired(void* context
) {
267 std::string heartbeat_message
;
268 if (!next_heartbeat_message_
.empty() &&
269 context
== &next_heartbeat_message_
[0]) {
270 heartbeat_message
= next_heartbeat_message_
;
272 heartbeat_message
= "ERROR: Invalid timer context found!";
275 // This URL is only used for testing the code path for defaultURL.
276 // There is no service at this URL, so applications should ignore it.
277 const char url
[] = "http://test.externalclearkey.chromium.org";
279 host_
->SendKeyMessage(
280 heartbeat_session_id_
.data(), heartbeat_session_id_
.size(),
281 heartbeat_message
.data(), heartbeat_message
.size(),
282 url
, arraysize(url
) - 1);
284 ScheduleNextHeartBeat();
287 static void CopyDecryptResults(
288 media::Decryptor::Status
* status_copy
,
289 scoped_refptr
<media::DecoderBuffer
>* buffer_copy
,
290 media::Decryptor::Status status
,
291 const scoped_refptr
<media::DecoderBuffer
>& buffer
) {
292 *status_copy
= status
;
293 *buffer_copy
= buffer
;
296 cdm::Status
ClearKeyCdm::Decrypt(
297 const cdm::InputBuffer
& encrypted_buffer
,
298 cdm::DecryptedBlock
* decrypted_block
) {
299 DVLOG(1) << "Decrypt()";
300 DCHECK(encrypted_buffer
.data
);
302 scoped_refptr
<media::DecoderBuffer
> buffer
;
303 cdm::Status status
= DecryptToMediaDecoderBuffer(encrypted_buffer
, &buffer
);
305 if (status
!= cdm::kSuccess
)
308 DCHECK(buffer
->data());
309 decrypted_block
->SetDecryptedBuffer(
310 host_
->Allocate(buffer
->data_size()));
311 memcpy(reinterpret_cast<void*>(decrypted_block
->DecryptedBuffer()->Data()),
313 buffer
->data_size());
314 decrypted_block
->DecryptedBuffer()->SetSize(buffer
->data_size());
315 decrypted_block
->SetTimestamp(buffer
->timestamp().InMicroseconds());
317 return cdm::kSuccess
;
320 cdm::Status
ClearKeyCdm::InitializeAudioDecoder(
321 const cdm::AudioDecoderConfig
& audio_decoder_config
) {
322 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
324 audio_decoder_
.reset(new media::FFmpegCdmAudioDecoder(host_
));
326 if (!audio_decoder_
->Initialize(audio_decoder_config
))
327 return cdm::kSessionError
;
329 return cdm::kSuccess
;
330 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
331 channel_count_
= audio_decoder_config
.channel_count
;
332 bits_per_channel_
= audio_decoder_config
.bits_per_channel
;
333 samples_per_second_
= audio_decoder_config
.samples_per_second
;
334 return cdm::kSuccess
;
337 return cdm::kSessionError
;
338 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
341 cdm::Status
ClearKeyCdm::InitializeVideoDecoder(
342 const cdm::VideoDecoderConfig
& video_decoder_config
) {
343 if (video_decoder_
&& video_decoder_
->is_initialized()) {
344 DCHECK(!video_decoder_
->is_initialized());
345 return cdm::kSessionError
;
348 // Any uninitialized decoder will be replaced.
349 video_decoder_
= CreateVideoDecoder(host_
, video_decoder_config
);
351 return cdm::kSessionError
;
353 return cdm::kSuccess
;
356 void ClearKeyCdm::ResetDecoder(cdm::StreamType decoder_type
) {
357 DVLOG(1) << "ResetDecoder()";
358 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
359 switch (decoder_type
) {
360 case cdm::kStreamTypeVideo
:
361 video_decoder_
->Reset();
363 case cdm::kStreamTypeAudio
:
364 audio_decoder_
->Reset();
367 NOTREACHED() << "ResetDecoder(): invalid cdm::StreamType";
369 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
370 if (decoder_type
== cdm::kStreamTypeAudio
) {
371 output_timestamp_base_in_microseconds_
= kNoTimestamp
;
372 total_samples_generated_
= 0;
374 #endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
377 void ClearKeyCdm::DeinitializeDecoder(cdm::StreamType decoder_type
) {
378 DVLOG(1) << "DeinitializeDecoder()";
379 switch (decoder_type
) {
380 case cdm::kStreamTypeVideo
:
381 video_decoder_
->Deinitialize();
383 case cdm::kStreamTypeAudio
:
384 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
385 audio_decoder_
->Deinitialize();
386 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
387 output_timestamp_base_in_microseconds_
= kNoTimestamp
;
388 total_samples_generated_
= 0;
392 NOTREACHED() << "DeinitializeDecoder(): invalid cdm::StreamType";
396 cdm::Status
ClearKeyCdm::DecryptAndDecodeFrame(
397 const cdm::InputBuffer
& encrypted_buffer
,
398 cdm::VideoFrame
* decoded_frame
) {
399 DVLOG(1) << "DecryptAndDecodeFrame()";
400 TRACE_EVENT0("eme", "ClearKeyCdm::DecryptAndDecodeFrame");
402 scoped_refptr
<media::DecoderBuffer
> buffer
;
403 cdm::Status status
= DecryptToMediaDecoderBuffer(encrypted_buffer
, &buffer
);
405 if (status
!= cdm::kSuccess
)
408 const uint8_t* data
= NULL
;
410 int64_t timestamp
= 0;
411 if (!buffer
->end_of_stream()) {
412 data
= buffer
->data();
413 size
= buffer
->data_size();
414 timestamp
= encrypted_buffer
.timestamp
;
417 return video_decoder_
->DecodeFrame(data
, size
, timestamp
, decoded_frame
);
420 cdm::Status
ClearKeyCdm::DecryptAndDecodeSamples(
421 const cdm::InputBuffer
& encrypted_buffer
,
422 cdm::AudioFrames
* audio_frames
) {
423 DVLOG(1) << "DecryptAndDecodeSamples()";
425 scoped_refptr
<media::DecoderBuffer
> buffer
;
426 cdm::Status status
= DecryptToMediaDecoderBuffer(encrypted_buffer
, &buffer
);
428 if (status
!= cdm::kSuccess
)
431 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
432 const uint8_t* data
= NULL
;
434 int64_t timestamp
= 0;
435 if (!buffer
->end_of_stream()) {
436 data
= buffer
->data();
437 size
= buffer
->data_size();
438 timestamp
= encrypted_buffer
.timestamp
;
441 return audio_decoder_
->DecodeBuffer(data
, size
, timestamp
, audio_frames
);
442 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
443 int64 timestamp_in_microseconds
= kNoTimestamp
;
444 if (!buffer
->end_of_stream()) {
445 timestamp_in_microseconds
= buffer
->GetTimestamp().InMicroseconds();
446 DCHECK(timestamp_in_microseconds
!= kNoTimestamp
);
448 return GenerateFakeAudioFrames(timestamp_in_microseconds
, audio_frames
);
450 return cdm::kSuccess
;
451 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
454 void ClearKeyCdm::Destroy() {
455 DVLOG(1) << "Destroy()";
459 void ClearKeyCdm::ScheduleNextHeartBeat() {
460 // Prepare the next heartbeat message and set timer.
461 std::ostringstream msg_stream
;
462 msg_stream
<< kHeartBeatHeader
<< " from ClearKey CDM set at time "
463 << host_
->GetCurrentWallTimeInSeconds() << ".";
464 next_heartbeat_message_
= msg_stream
.str();
466 host_
->SetTimer(timer_delay_ms_
, &next_heartbeat_message_
[0]);
468 // Use a smaller timer delay at start-up to facilitate testing. Increase the
469 // timer delay up to a limit to avoid message spam.
470 if (timer_delay_ms_
< kMaxTimerDelayMs
)
471 timer_delay_ms_
= std::min(2 * timer_delay_ms_
, kMaxTimerDelayMs
);
474 cdm::Status
ClearKeyCdm::DecryptToMediaDecoderBuffer(
475 const cdm::InputBuffer
& encrypted_buffer
,
476 scoped_refptr
<media::DecoderBuffer
>* decrypted_buffer
) {
477 DCHECK(decrypted_buffer
);
478 scoped_refptr
<media::DecoderBuffer
> buffer
=
479 CopyDecoderBufferFrom(encrypted_buffer
);
481 if (buffer
->end_of_stream()) {
482 *decrypted_buffer
= buffer
;
483 return cdm::kSuccess
;
486 // Callback is called synchronously, so we can use variables on the stack.
487 media::Decryptor::Status status
= media::Decryptor::kError
;
488 // The AesDecryptor does not care what the stream type is. Pass kVideo
489 // for both audio and video decryption.
491 media::Decryptor::kVideo
,
493 base::Bind(&CopyDecryptResults
, &status
, decrypted_buffer
));
495 if (status
== media::Decryptor::kError
)
496 return cdm::kDecryptError
;
498 if (status
== media::Decryptor::kNoKey
)
501 DCHECK_EQ(status
, media::Decryptor::kSuccess
);
502 return cdm::kSuccess
;
505 void ClearKeyCdm::OnPlatformChallengeResponse(
506 const cdm::PlatformChallengeResponse
& response
) {
510 void ClearKeyCdm::OnQueryOutputProtectionStatus(
511 uint32_t link_mask
, uint32_t output_protection_mask
) {
515 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
516 int64
ClearKeyCdm::CurrentTimeStampInMicroseconds() const {
517 return output_timestamp_base_in_microseconds_
+
518 base::Time::kMicrosecondsPerSecond
*
519 total_samples_generated_
/ samples_per_second_
;
522 int ClearKeyCdm::GenerateFakeAudioFramesFromDuration(
523 int64 duration_in_microseconds
,
524 cdm::AudioFrames
* audio_frames
) const {
525 int64 samples_to_generate
= static_cast<double>(samples_per_second_
) *
526 duration_in_microseconds
/ base::Time::kMicrosecondsPerSecond
+ 0.5;
527 if (samples_to_generate
<= 0)
530 int64 bytes_per_sample
= channel_count_
* bits_per_channel_
/ 8;
531 // |frame_size| must be a multiple of |bytes_per_sample|.
532 int64 frame_size
= bytes_per_sample
* samples_to_generate
;
534 int64 timestamp
= CurrentTimeStampInMicroseconds();
536 const int kHeaderSize
= sizeof(timestamp
) + sizeof(frame_size
);
537 audio_frames
->SetFrameBuffer(host_
->Allocate(kHeaderSize
+ frame_size
));
538 uint8_t* data
= audio_frames
->FrameBuffer()->Data();
540 memcpy(data
, ×tamp
, sizeof(timestamp
));
541 data
+= sizeof(timestamp
);
542 memcpy(data
, &frame_size
, sizeof(frame_size
));
543 data
+= sizeof(frame_size
);
544 // You won't hear anything because we have all zeros here. But the video
545 // should play just fine!
546 memset(data
, 0, frame_size
);
548 audio_frames
->FrameBuffer()->SetSize(kHeaderSize
+ frame_size
);
550 return samples_to_generate
;
553 cdm::Status
ClearKeyCdm::GenerateFakeAudioFrames(
554 int64 timestamp_in_microseconds
,
555 cdm::AudioFrames
* audio_frames
) {
556 if (timestamp_in_microseconds
== kNoTimestamp
)
557 return cdm::kNeedMoreData
;
559 // Return kNeedMoreData for the first frame because duration is unknown.
560 if (output_timestamp_base_in_microseconds_
== kNoTimestamp
) {
561 output_timestamp_base_in_microseconds_
= timestamp_in_microseconds
;
562 return cdm::kNeedMoreData
;
565 int samples_generated
= GenerateFakeAudioFramesFromDuration(
566 timestamp_in_microseconds
- CurrentTimeStampInMicroseconds(),
568 total_samples_generated_
+= samples_generated
;
570 return samples_generated
== 0 ? cdm::kNeedMoreData
: cdm::kSuccess
;
572 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER