Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(319)

Side by Side Diff: webkit/renderer/media/crypto/ppapi/clear_key_cdm.cc

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

Powered by Google App Engine
This is Rietveld 408576698