- add sources.
[platform/framework/web/crosswalk.git] / src / media / cdm / ppapi / clear_key_cdm.cc
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"
22 const 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().
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"
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
46 static 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.
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;
57 }
58
59 static bool g_ffmpeg_lib_initialized = InitializeFFmpegLibraries();
60 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
61
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";
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.
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();
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
108 template<typename Type>
109 class ScopedResetter {
110  public:
111   explicit ScopedResetter(Type* object) : object_(object) {}
112   ~ScopedResetter() { object_->Reset(); }
113
114  private:
115   Type* const object_;
116 };
117
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
123 }
124
125 void DeinitializeCdmModule() {
126 }
127
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()";
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 != media::CdmInterface::kVersion)
140     return NULL;
141
142   media::CdmHost* host = static_cast<media::CdmHost*>(
143       get_cdm_host_func(media::CdmHost::kVersion, user_data));
144   if (!host)
145     return NULL;
146
147   return new media::ClearKeyCdm(host);
148 }
149
150 const char* GetCdmVersion() {
151   return kClearKeyCdmVersion;
152 }
153
154 namespace media {
155
156 ClearKeyCdm::Client::Client() : status_(kKeyError) {}
157
158 ClearKeyCdm::Client::~Client() {}
159
160 void ClearKeyCdm::Client::Reset() {
161   status_ = kKeyError;
162   session_id_.clear();
163   key_message_.clear();
164   default_url_.clear();
165 }
166
167 void ClearKeyCdm::Client::KeyAdded(const std::string& session_id) {
168   status_ = kKeyAdded;
169   session_id_ = session_id;
170 }
171
172 void ClearKeyCdm::Client::KeyError(const std::string& session_id,
173                                    media::MediaKeys::KeyError error_code,
174                                    int system_code) {
175   status_ = kKeyError;
176   session_id_ = session_id;
177 }
178
179 void ClearKeyCdm::Client::KeyMessage(const std::string& session_id,
180                                      const std::vector<uint8>& message,
181                                      const std::string& default_url) {
182   status_ = kKeyMessage;
183   session_id_ = session_id;
184   key_message_ = message;
185   default_url_ = default_url;
186 }
187
188 ClearKeyCdm::ClearKeyCdm(CdmHost* host)
189     : decryptor_(base::Bind(&Client::KeyAdded, base::Unretained(&client_)),
190                  base::Bind(&Client::KeyError, base::Unretained(&client_)),
191                  base::Bind(&Client::KeyMessage, base::Unretained(&client_))),
192       host_(host),
193       timer_delay_ms_(kInitialTimerDelayMs),
194       timer_set_(false) {
195 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
196   channel_count_ = 0;
197   bits_per_channel_ = 0;
198   samples_per_second_ = 0;
199   output_timestamp_base_in_microseconds_ = kNoTimestamp;
200   total_samples_generated_ = 0;
201 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
202 }
203
204 ClearKeyCdm::~ClearKeyCdm() {}
205
206 cdm::Status ClearKeyCdm::GenerateKeyRequest(const char* type,
207                                             uint32_t type_size,
208                                             const uint8_t* init_data,
209                                             uint32_t init_data_size) {
210   DVLOG(1) << "GenerateKeyRequest()";
211   base::AutoLock auto_lock(client_lock_);
212   ScopedResetter<Client> auto_resetter(&client_);
213   decryptor_.GenerateKeyRequest(std::string(type, type_size),
214                                 init_data, init_data_size);
215
216   if (client_.status() != Client::kKeyMessage) {
217     host_->SendKeyError(NULL, 0, cdm::kUnknownError, 0);
218     return cdm::kSessionError;
219   }
220
221   host_->SendKeyMessage(
222       client_.session_id().data(), client_.session_id().size(),
223       reinterpret_cast<const char*>(&client_.key_message()[0]),
224       client_.key_message().size(),
225       client_.default_url().data(), client_.default_url().size());
226
227   // Only save the latest session ID for heartbeat messages.
228   heartbeat_session_id_ = client_.session_id();
229
230   return cdm::kSuccess;
231 }
232
233 cdm::Status ClearKeyCdm::AddKey(const char* session_id,
234                                 uint32_t session_id_size,
235                                 const uint8_t* key,
236                                 uint32_t key_size,
237                                 const uint8_t* key_id,
238                                 uint32_t key_id_size) {
239   DVLOG(1) << "AddKey()";
240   base::AutoLock auto_lock(client_lock_);
241   ScopedResetter<Client> auto_resetter(&client_);
242   decryptor_.AddKey(key, key_size, key_id, key_id_size,
243                     std::string(session_id, session_id_size));
244
245   if (client_.status() != Client::kKeyAdded)
246     return cdm::kSessionError;
247
248   if (!timer_set_) {
249     ScheduleNextHeartBeat();
250     timer_set_ = true;
251   }
252
253   return cdm::kSuccess;
254 }
255
256 cdm::Status ClearKeyCdm::CancelKeyRequest(const char* session_id,
257                                           uint32_t session_id_size) {
258   DVLOG(1) << "CancelKeyRequest()";
259   base::AutoLock auto_lock(client_lock_);
260   ScopedResetter<Client> auto_resetter(&client_);
261   decryptor_.CancelKeyRequest(std::string(session_id, session_id_size));
262   return cdm::kSuccess;
263 }
264
265 void ClearKeyCdm::TimerExpired(void* context) {
266   std::string heartbeat_message;
267   if (!next_heartbeat_message_.empty() &&
268       context == &next_heartbeat_message_[0]) {
269     heartbeat_message = next_heartbeat_message_;
270   } else {
271     heartbeat_message = "ERROR: Invalid timer context found!";
272   }
273
274   // This URL is only used for testing the code path for defaultURL.
275   // There is no service at this URL, so applications should ignore it.
276   const char url[] = "http://test.externalclearkey.chromium.org";
277
278   host_->SendKeyMessage(
279       heartbeat_session_id_.data(), heartbeat_session_id_.size(),
280       heartbeat_message.data(), heartbeat_message.size(),
281       url, arraysize(url) - 1);
282
283   ScheduleNextHeartBeat();
284 }
285
286 static void CopyDecryptResults(
287     media::Decryptor::Status* status_copy,
288     scoped_refptr<media::DecoderBuffer>* buffer_copy,
289     media::Decryptor::Status status,
290     const scoped_refptr<media::DecoderBuffer>& buffer) {
291   *status_copy = status;
292   *buffer_copy = buffer;
293 }
294
295 cdm::Status ClearKeyCdm::Decrypt(
296     const cdm::InputBuffer& encrypted_buffer,
297     cdm::DecryptedBlock* decrypted_block) {
298   DVLOG(1) << "Decrypt()";
299   DCHECK(encrypted_buffer.data);
300
301   scoped_refptr<media::DecoderBuffer> buffer;
302   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
303
304   if (status != cdm::kSuccess)
305     return status;
306
307   DCHECK(buffer->data());
308   decrypted_block->SetDecryptedBuffer(
309       host_->Allocate(buffer->data_size()));
310   memcpy(reinterpret_cast<void*>(decrypted_block->DecryptedBuffer()->Data()),
311          buffer->data(),
312          buffer->data_size());
313   decrypted_block->DecryptedBuffer()->SetSize(buffer->data_size());
314   decrypted_block->SetTimestamp(buffer->timestamp().InMicroseconds());
315
316   return cdm::kSuccess;
317 }
318
319 cdm::Status ClearKeyCdm::InitializeAudioDecoder(
320     const cdm::AudioDecoderConfig& audio_decoder_config) {
321 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
322   if (!audio_decoder_)
323     audio_decoder_.reset(new media::FFmpegCdmAudioDecoder(host_));
324
325   if (!audio_decoder_->Initialize(audio_decoder_config))
326     return cdm::kSessionError;
327
328   return cdm::kSuccess;
329 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
330   channel_count_ = audio_decoder_config.channel_count;
331   bits_per_channel_ = audio_decoder_config.bits_per_channel;
332   samples_per_second_ = audio_decoder_config.samples_per_second;
333   return cdm::kSuccess;
334 #else
335   NOTIMPLEMENTED();
336   return cdm::kSessionError;
337 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
338 }
339
340 cdm::Status ClearKeyCdm::InitializeVideoDecoder(
341     const cdm::VideoDecoderConfig& video_decoder_config) {
342   if (video_decoder_ && video_decoder_->is_initialized()) {
343     DCHECK(!video_decoder_->is_initialized());
344     return cdm::kSessionError;
345   }
346
347   // Any uninitialized decoder will be replaced.
348   video_decoder_ = CreateVideoDecoder(host_, video_decoder_config);
349   if (!video_decoder_)
350     return cdm::kSessionError;
351
352   return cdm::kSuccess;
353 }
354
355 void ClearKeyCdm::ResetDecoder(cdm::StreamType decoder_type) {
356   DVLOG(1) << "ResetDecoder()";
357 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
358   switch (decoder_type) {
359     case cdm::kStreamTypeVideo:
360       video_decoder_->Reset();
361       break;
362     case cdm::kStreamTypeAudio:
363       audio_decoder_->Reset();
364       break;
365     default:
366       NOTREACHED() << "ResetDecoder(): invalid cdm::StreamType";
367   }
368 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
369   if (decoder_type == cdm::kStreamTypeAudio) {
370     output_timestamp_base_in_microseconds_ = kNoTimestamp;
371     total_samples_generated_ = 0;
372   }
373 #endif  // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
374 }
375
376 void ClearKeyCdm::DeinitializeDecoder(cdm::StreamType decoder_type) {
377   DVLOG(1) << "DeinitializeDecoder()";
378   switch (decoder_type) {
379     case cdm::kStreamTypeVideo:
380       video_decoder_->Deinitialize();
381       break;
382     case cdm::kStreamTypeAudio:
383 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
384       audio_decoder_->Deinitialize();
385 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
386       output_timestamp_base_in_microseconds_ = kNoTimestamp;
387       total_samples_generated_ = 0;
388 #endif
389       break;
390     default:
391       NOTREACHED() << "DeinitializeDecoder(): invalid cdm::StreamType";
392   }
393 }
394
395 cdm::Status ClearKeyCdm::DecryptAndDecodeFrame(
396     const cdm::InputBuffer& encrypted_buffer,
397     cdm::VideoFrame* decoded_frame) {
398   DVLOG(1) << "DecryptAndDecodeFrame()";
399   TRACE_EVENT0("eme", "ClearKeyCdm::DecryptAndDecodeFrame");
400
401   scoped_refptr<media::DecoderBuffer> buffer;
402   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
403
404   if (status != cdm::kSuccess)
405     return status;
406
407   const uint8_t* data = NULL;
408   int32_t size = 0;
409   int64_t timestamp = 0;
410   if (!buffer->end_of_stream()) {
411     data = buffer->data();
412     size = buffer->data_size();
413     timestamp = encrypted_buffer.timestamp;
414   }
415
416   return video_decoder_->DecodeFrame(data, size, timestamp, decoded_frame);
417 }
418
419 cdm::Status ClearKeyCdm::DecryptAndDecodeSamples(
420     const cdm::InputBuffer& encrypted_buffer,
421     cdm::AudioFrames* audio_frames) {
422   DVLOG(1) << "DecryptAndDecodeSamples()";
423
424   scoped_refptr<media::DecoderBuffer> buffer;
425   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
426
427   if (status != cdm::kSuccess)
428     return status;
429
430 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
431   const uint8_t* data = NULL;
432   int32_t size = 0;
433   int64_t timestamp = 0;
434   if (!buffer->end_of_stream()) {
435     data = buffer->data();
436     size = buffer->data_size();
437     timestamp = encrypted_buffer.timestamp;
438   }
439
440   return audio_decoder_->DecodeBuffer(data, size, timestamp, audio_frames);
441 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
442   int64 timestamp_in_microseconds = kNoTimestamp;
443   if (!buffer->end_of_stream()) {
444     timestamp_in_microseconds = buffer->GetTimestamp().InMicroseconds();
445     DCHECK(timestamp_in_microseconds != kNoTimestamp);
446   }
447   return GenerateFakeAudioFrames(timestamp_in_microseconds, audio_frames);
448 #else
449   return cdm::kSuccess;
450 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
451 }
452
453 void ClearKeyCdm::Destroy() {
454   DVLOG(1) << "Destroy()";
455   delete this;
456 }
457
458 void ClearKeyCdm::ScheduleNextHeartBeat() {
459   // Prepare the next heartbeat message and set timer.
460   std::ostringstream msg_stream;
461   msg_stream << kHeartBeatHeader << " from ClearKey CDM set at time "
462              << host_->GetCurrentWallTimeInSeconds() << ".";
463   next_heartbeat_message_ = msg_stream.str();
464
465   host_->SetTimer(timer_delay_ms_, &next_heartbeat_message_[0]);
466
467   // Use a smaller timer delay at start-up to facilitate testing. Increase the
468   // timer delay up to a limit to avoid message spam.
469   if (timer_delay_ms_ < kMaxTimerDelayMs)
470     timer_delay_ms_ = std::min(2 * timer_delay_ms_, kMaxTimerDelayMs);
471 }
472
473 cdm::Status ClearKeyCdm::DecryptToMediaDecoderBuffer(
474     const cdm::InputBuffer& encrypted_buffer,
475     scoped_refptr<media::DecoderBuffer>* decrypted_buffer) {
476   DCHECK(decrypted_buffer);
477   scoped_refptr<media::DecoderBuffer> buffer =
478       CopyDecoderBufferFrom(encrypted_buffer);
479
480   if (buffer->end_of_stream()) {
481     *decrypted_buffer = buffer;
482     return cdm::kSuccess;
483   }
484
485   // Callback is called synchronously, so we can use variables on the stack.
486   media::Decryptor::Status status = media::Decryptor::kError;
487   // The AesDecryptor does not care what the stream type is. Pass kVideo
488   // for both audio and video decryption.
489   decryptor_.Decrypt(
490       media::Decryptor::kVideo,
491       buffer,
492       base::Bind(&CopyDecryptResults, &status, decrypted_buffer));
493
494   if (status == media::Decryptor::kError)
495     return cdm::kDecryptError;
496
497   if (status == media::Decryptor::kNoKey)
498     return cdm::kNoKey;
499
500   DCHECK_EQ(status, media::Decryptor::kSuccess);
501   return cdm::kSuccess;
502 }
503
504 void ClearKeyCdm::OnPlatformChallengeResponse(
505     const cdm::PlatformChallengeResponse& response) {
506   NOTIMPLEMENTED();
507 }
508
509 void ClearKeyCdm::OnQueryOutputProtectionStatus(
510     uint32_t link_mask, uint32_t output_protection_mask) {
511   NOTIMPLEMENTED();
512 };
513
514 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
515 int64 ClearKeyCdm::CurrentTimeStampInMicroseconds() const {
516   return output_timestamp_base_in_microseconds_ +
517          base::Time::kMicrosecondsPerSecond *
518          total_samples_generated_  / samples_per_second_;
519 }
520
521 int ClearKeyCdm::GenerateFakeAudioFramesFromDuration(
522     int64 duration_in_microseconds,
523     cdm::AudioFrames* audio_frames) const {
524   int64 samples_to_generate = static_cast<double>(samples_per_second_) *
525       duration_in_microseconds / base::Time::kMicrosecondsPerSecond + 0.5;
526   if (samples_to_generate <= 0)
527     return 0;
528
529   int64 bytes_per_sample = channel_count_ * bits_per_channel_ / 8;
530   // |frame_size| must be a multiple of |bytes_per_sample|.
531   int64 frame_size = bytes_per_sample * samples_to_generate;
532
533   int64 timestamp = CurrentTimeStampInMicroseconds();
534
535   const int kHeaderSize = sizeof(timestamp) + sizeof(frame_size);
536   audio_frames->SetFrameBuffer(host_->Allocate(kHeaderSize + frame_size));
537   uint8_t* data = audio_frames->FrameBuffer()->Data();
538
539   memcpy(data, &timestamp, sizeof(timestamp));
540   data += sizeof(timestamp);
541   memcpy(data, &frame_size, sizeof(frame_size));
542   data += sizeof(frame_size);
543   // You won't hear anything because we have all zeros here. But the video
544   // should play just fine!
545   memset(data, 0, frame_size);
546
547   audio_frames->FrameBuffer()->SetSize(kHeaderSize + frame_size);
548
549   return samples_to_generate;
550 }
551
552 cdm::Status ClearKeyCdm::GenerateFakeAudioFrames(
553     int64 timestamp_in_microseconds,
554     cdm::AudioFrames* audio_frames) {
555   if (timestamp_in_microseconds == kNoTimestamp)
556     return cdm::kNeedMoreData;
557
558   // Return kNeedMoreData for the first frame because duration is unknown.
559   if (output_timestamp_base_in_microseconds_ == kNoTimestamp) {
560     output_timestamp_base_in_microseconds_ = timestamp_in_microseconds;
561     return cdm::kNeedMoreData;
562   }
563
564   int samples_generated = GenerateFakeAudioFramesFromDuration(
565       timestamp_in_microseconds - CurrentTimeStampInMicroseconds(),
566       audio_frames);
567   total_samples_generated_ += samples_generated;
568
569   return samples_generated == 0 ? cdm::kNeedMoreData : cdm::kSuccess;
570 }
571 #endif  // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
572
573 }  // namespace media