clear_key_cdm.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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.
4
5#include "media/cdm/ppapi/clear_key_cdm.h"
6
7#include <algorithm>
8#include <sstream>
9#include <string>
10#include <vector>
11
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"
19
20#if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
21#include "base/basictypes.h"
22const int64 kNoTimestamp = kint64min;
23#endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
24
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"
32
33// Include FFmpeg avformat.h for av_register_all().
34extern "C" {
35// Temporarily disable possible loss of data warning.
36MSVC_PUSH_DISABLE_WARNING(4244);
37#include <libavformat/avformat.h>
38MSVC_POP_WARNING();
39}  // extern "C"
40
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
46static base::AtExitManager g_at_exit_manager;
47#endif
48
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.
52static 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;
57}
58
59static bool g_ffmpeg_lib_initialized = InitializeFFmpegLibraries();
60#endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
61
62const char kClearKeyCdmVersion[] = "0.1.0.1";
63const char kExternalClearKeyKeySystem[] = "org.chromium.externalclearkey";
64const int64 kSecondsPerMinute = 60;
65const int64 kMsPerSecond = 1000;
66const int64 kInitialTimerDelayMs = 200;
67const 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.
70const char kHeartBeatHeader[] = "HEARTBEAT";
71
72// Copies |input_buffer| into a media::DecoderBuffer. If the |input_buffer| is
73// empty, an empty (end-of-stream) media::DecoderBuffer is returned.
74static 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();
79  }
80
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);
84
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);
91  }
92
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));
100
101  output_buffer->set_decrypt_config(decrypt_config.Pass());
102  output_buffer->set_timestamp(
103      base::TimeDelta::FromMicroseconds(input_buffer.timestamp));
104
105  return output_buffer;
106}
107
108template<typename Type>
109class ScopedResetter {
110 public:
111  explicit ScopedResetter(Type* object) : object_(object) {}
112  ~ScopedResetter() { object_->Reset(); }
113
114 private:
115  Type* const object_;
116};
117
118void 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
123}
124
125void DeinitializeCdmModule() {
126}
127
128void* 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()";
133
134  if (std::string(key_system, key_system_size) != kExternalClearKeyKeySystem) {
135    DVLOG(1) << "Unsupported key system.";
136    return NULL;
137  }
138
139  if (cdm_interface_version != cdm::ContentDecryptionModule_2::kVersion)
140    return NULL;
141
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;
147
148  return new media::ClearKeyCdm(host);
149}
150
151const char* GetCdmVersion() {
152  return kClearKeyCdmVersion;
153}
154
155namespace media {
156
157ClearKeyCdm::Client::Client() : status_(kKeyError) {}
158
159ClearKeyCdm::Client::~Client() {}
160
161void ClearKeyCdm::Client::Reset() {
162  status_ = kKeyError;
163  session_id_.clear();
164  key_message_.clear();
165  default_url_.clear();
166}
167
168void ClearKeyCdm::Client::KeyAdded(const std::string& session_id) {
169  status_ = kKeyAdded;
170  session_id_ = session_id;
171}
172
173void 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;
178}
179
180void 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;
187}
188
189ClearKeyCdm::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
203}
204
205ClearKeyCdm::~ClearKeyCdm() {}
206
207cdm::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);
216
217  if (client_.status() != Client::kKeyMessage) {
218    host_->SendKeyError(NULL, 0, cdm::kUnknownError, 0);
219    return cdm::kSessionError;
220  }
221
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());
227
228  // Only save the latest session ID for heartbeat messages.
229  heartbeat_session_id_ = client_.session_id();
230
231  return cdm::kSuccess;
232}
233
234cdm::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));
245
246  if (client_.status() != Client::kKeyAdded)
247    return cdm::kSessionError;
248
249  if (!timer_set_) {
250    ScheduleNextHeartBeat();
251    timer_set_ = true;
252  }
253
254  return cdm::kSuccess;
255}
256
257cdm::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;
264}
265
266void 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!";
273  }
274
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";
278
279  host_->SendKeyMessage(
280      heartbeat_session_id_.data(), heartbeat_session_id_.size(),
281      heartbeat_message.data(), heartbeat_message.size(),
282      url, arraysize(url) - 1);
283
284  ScheduleNextHeartBeat();
285}
286
287static 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;
294}
295
296cdm::Status ClearKeyCdm::Decrypt(
297    const cdm::InputBuffer& encrypted_buffer,
298    cdm::DecryptedBlock* decrypted_block) {
299  DVLOG(1) << "Decrypt()";
300  DCHECK(encrypted_buffer.data);
301
302  scoped_refptr<media::DecoderBuffer> buffer;
303  cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
304
305  if (status != cdm::kSuccess)
306    return status;
307
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());
316
317  return cdm::kSuccess;
318}
319
320cdm::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_));
325
326  if (!audio_decoder_->Initialize(audio_decoder_config))
327    return cdm::kSessionError;
328
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
339}
340
341cdm::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;
346  }
347
348  // Any uninitialized decoder will be replaced.
349  video_decoder_ = CreateVideoDecoder(host_, video_decoder_config);
350  if (!video_decoder_)
351    return cdm::kSessionError;
352
353  return cdm::kSuccess;
354}
355
356void 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";
368  }
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;
373  }
374#endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
375}
376
377void 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";
393  }
394}
395
396cdm::Status ClearKeyCdm::DecryptAndDecodeFrame(
397    const cdm::InputBuffer& encrypted_buffer,
398    cdm::VideoFrame* decoded_frame) {
399  DVLOG(1) << "DecryptAndDecodeFrame()";
400  TRACE_EVENT0("eme", "ClearKeyCdm::DecryptAndDecodeFrame");
401
402  scoped_refptr<media::DecoderBuffer> buffer;
403  cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
404
405  if (status != cdm::kSuccess)
406    return status;
407
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;
415  }
416
417  return video_decoder_->DecodeFrame(data, size, timestamp, decoded_frame);
418}
419
420cdm::Status ClearKeyCdm::DecryptAndDecodeSamples(
421    const cdm::InputBuffer& encrypted_buffer,
422    cdm::AudioFrames* audio_frames) {
423  DVLOG(1) << "DecryptAndDecodeSamples()";
424
425  scoped_refptr<media::DecoderBuffer> buffer;
426  cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
427
428  if (status != cdm::kSuccess)
429    return status;
430
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;
439  }
440
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);
447  }
448  return GenerateFakeAudioFrames(timestamp_in_microseconds, audio_frames);
449#else
450  return cdm::kSuccess;
451#endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
452}
453
454void ClearKeyCdm::Destroy() {
455  DVLOG(1) << "Destroy()";
456  delete this;
457}
458
459void 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();
465
466  host_->SetTimer(timer_delay_ms_, &next_heartbeat_message_[0]);
467
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);
472}
473
474cdm::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);
480
481  if (buffer->end_of_stream()) {
482    *decrypted_buffer = buffer;
483    return cdm::kSuccess;
484  }
485
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));
494
495  if (status == media::Decryptor::kError)
496    return cdm::kDecryptError;
497
498  if (status == media::Decryptor::kNoKey)
499    return cdm::kNoKey;
500
501  DCHECK_EQ(status, media::Decryptor::kSuccess);
502  return cdm::kSuccess;
503}
504
505void ClearKeyCdm::OnPlatformChallengeResponse(
506    const cdm::PlatformChallengeResponse& response) {
507  NOTIMPLEMENTED();
508}
509
510void ClearKeyCdm::OnQueryOutputProtectionStatus(
511    uint32_t link_mask, uint32_t output_protection_mask) {
512  NOTIMPLEMENTED();
513};
514
515#if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
516int64 ClearKeyCdm::CurrentTimeStampInMicroseconds() const {
517  return output_timestamp_base_in_microseconds_ +
518         base::Time::kMicrosecondsPerSecond *
519         total_samples_generated_  / samples_per_second_;
520}
521
522int 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;
529
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;
533
534  int64 timestamp = CurrentTimeStampInMicroseconds();
535
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();
539
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);
547
548  audio_frames->FrameBuffer()->SetSize(kHeaderSize + frame_size);
549
550  return samples_to_generate;
551}
552
553cdm::Status ClearKeyCdm::GenerateFakeAudioFrames(
554    int64 timestamp_in_microseconds,
555    cdm::AudioFrames* audio_frames) {
556  if (timestamp_in_microseconds == kNoTimestamp)
557    return cdm::kNeedMoreData;
558
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;
563  }
564
565  int samples_generated = GenerateFakeAudioFramesFromDuration(
566      timestamp_in_microseconds - CurrentTimeStampInMicroseconds(),
567      audio_frames);
568  total_samples_generated_ += samples_generated;
569
570  return samples_generated == 0 ? cdm::kNeedMoreData : cdm::kSuccess;
571}
572#endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
573
574}  // namespace media
575