Removed unused VideoCaptureCapability parameters.
[chromium-blink-merge.git] / media / cdm / ppapi / clear_key_cdm.cc
blobd7cd206be69a30a0ba7b5bcfc29ce2b83f263f26
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"
7 #include <algorithm>
8 #include <sstream>
9 #include <string>
10 #include <vector>
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().
34 extern "C" {
35 // Temporarily disable possible loss of data warning.
36 MSVC_PUSH_DISABLE_WARNING(4244);
37 #include <libavformat/avformat.h>
38 MSVC_POP_WARNING();
39 } // extern "C"
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;
47 #endif
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));
56 return true;
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,
99 subsamples));
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 {
110 public:
111 explicit ScopedResetter(Type* object) : object_(object) {}
112 ~ScopedResetter() { object_->Reset(); }
114 private:
115 Type* const object_;
118 void INITIALIZE_CDM_MODULE() {
119 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
120 DVLOG(2) << "FFmpeg libraries initialized: " << g_ffmpeg_lib_initialized;
121 av_register_all();
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.";
136 return NULL;
139 if (cdm_interface_version != cdm::ContentDecryptionModule_2::kVersion)
140 return NULL;
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));
145 if (!host)
146 return NULL;
148 return new media::ClearKeyCdm(host);
151 const char* GetCdmVersion() {
152 return kClearKeyCdmVersion;
155 namespace media {
157 ClearKeyCdm::Client::Client() : status_(kKeyError) {}
159 ClearKeyCdm::Client::~Client() {}
161 void ClearKeyCdm::Client::Reset() {
162 status_ = kKeyError;
163 session_id_.clear();
164 key_message_.clear();
165 default_url_.clear();
168 void ClearKeyCdm::Client::KeyAdded(const std::string& session_id) {
169 status_ = kKeyAdded;
170 session_id_ = session_id;
173 void ClearKeyCdm::Client::KeyError(const std::string& session_id,
174 media::MediaKeys::KeyError error_code,
175 int system_code) {
176 status_ = kKeyError;
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_))),
193 host_(host),
194 timer_delay_ms_(kInitialTimerDelayMs),
195 timer_set_(false) {
196 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
197 channel_count_ = 0;
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,
208 uint32_t type_size,
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,
236 const uint8_t* key,
237 uint32_t key_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;
249 if (!timer_set_) {
250 ScheduleNextHeartBeat();
251 timer_set_ = true;
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_;
271 } else {
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)
306 return status;
308 DCHECK(buffer->data());
309 decrypted_block->SetDecryptedBuffer(
310 host_->Allocate(buffer->data_size()));
311 memcpy(reinterpret_cast<void*>(decrypted_block->DecryptedBuffer()->Data()),
312 buffer->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)
323 if (!audio_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;
335 #else
336 NOTIMPLEMENTED();
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);
350 if (!video_decoder_)
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();
362 break;
363 case cdm::kStreamTypeAudio:
364 audio_decoder_->Reset();
365 break;
366 default:
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();
382 break;
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;
389 #endif
390 break;
391 default:
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)
406 return status;
408 const uint8_t* data = NULL;
409 int32_t size = 0;
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)
429 return status;
431 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
432 const uint8_t* data = NULL;
433 int32_t size = 0;
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);
449 #else
450 return cdm::kSuccess;
451 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
454 void ClearKeyCdm::Destroy() {
455 DVLOG(1) << "Destroy()";
456 delete this;
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.
490 decryptor_.Decrypt(
491 media::Decryptor::kVideo,
492 buffer,
493 base::Bind(&CopyDecryptResults, &status, decrypted_buffer));
495 if (status == media::Decryptor::kError)
496 return cdm::kDecryptError;
498 if (status == media::Decryptor::kNoKey)
499 return cdm::kNoKey;
501 DCHECK_EQ(status, media::Decryptor::kSuccess);
502 return cdm::kSuccess;
505 void ClearKeyCdm::OnPlatformChallengeResponse(
506 const cdm::PlatformChallengeResponse& response) {
507 NOTIMPLEMENTED();
510 void ClearKeyCdm::OnQueryOutputProtectionStatus(
511 uint32_t link_mask, uint32_t output_protection_mask) {
512 NOTIMPLEMENTED();
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)
528 return 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, &timestamp, 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(),
567 audio_frames);
568 total_samples_generated_ += samples_generated;
570 return samples_generated == 0 ? cdm::kNeedMoreData : cdm::kSuccess;
572 #endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
574 } // namespace media