Index: webkit/renderer/media/crypto/ppapi/clear_key_cdm.cc |
diff --git a/webkit/renderer/media/crypto/ppapi/clear_key_cdm.cc b/webkit/renderer/media/crypto/ppapi/clear_key_cdm.cc |
deleted file mode 100644 |
index 7f35fd818609ebb5a8511fa36f670431e5567ac7..0000000000000000000000000000000000000000 |
--- a/webkit/renderer/media/crypto/ppapi/clear_key_cdm.cc |
+++ /dev/null |
@@ -1,558 +0,0 @@ |
-// Copyright 2013 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "webkit/renderer/media/crypto/ppapi/clear_key_cdm.h" |
- |
-#include <algorithm> |
-#include <sstream> |
-#include <string> |
-#include <vector> |
- |
-#include "base/bind.h" |
-#include "base/debug/trace_event.h" |
-#include "base/logging.h" |
-#include "base/time/time.h" |
-#include "media/base/decoder_buffer.h" |
-#include "media/base/decrypt_config.h" |
-#include "webkit/renderer/media/crypto/ppapi/cdm_video_decoder.h" |
- |
-#if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER) |
-#include "base/basictypes.h" |
-static const int64 kNoTimestamp = kint64min; |
-#endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER |
- |
-#if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER) |
-#include "base/at_exit.h" |
-#include "base/files/file_path.h" |
-#include "base/path_service.h" |
-#include "media/base/media.h" |
-#include "webkit/renderer/media/crypto/ppapi/ffmpeg_cdm_audio_decoder.h" |
-#include "webkit/renderer/media/crypto/ppapi/ffmpeg_cdm_video_decoder.h" |
- |
-// Include FFmpeg avformat.h for av_register_all(). |
-extern "C" { |
-// Temporarily disable possible loss of data warning. |
-MSVC_PUSH_DISABLE_WARNING(4244); |
-#include <libavformat/avformat.h> |
-MSVC_POP_WARNING(); |
-} // extern "C" |
- |
-// TODO(tomfinegan): When COMPONENT_BUILD is not defined an AtExitManager must |
-// exist before the call to InitializeFFmpegLibraries(). This should no longer |
-// be required after http://crbug.com/91970 because we'll be able to get rid of |
-// InitializeFFmpegLibraries(). |
-#if !defined COMPONENT_BUILD |
-static base::AtExitManager g_at_exit_manager; |
-#endif |
- |
-// TODO(tomfinegan): InitializeFFmpegLibraries() and |g_cdm_module_initialized| |
-// are required for running in the sandbox, and should no longer be required |
-// after http://crbug.com/91970 is fixed. |
-static bool InitializeFFmpegLibraries() { |
- base::FilePath file_path; |
- CHECK(PathService::Get(base::DIR_MODULE, &file_path)); |
- CHECK(media::InitializeMediaLibrary(file_path)); |
- return true; |
-} |
- |
-static bool g_ffmpeg_lib_initialized = InitializeFFmpegLibraries(); |
-#endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER |
- |
-static const char kClearKeyCdmVersion[] = "0.1.0.1"; |
-static const char kExternalClearKey[] = "org.chromium.externalclearkey"; |
-static const int64 kSecondsPerMinute = 60; |
-static const int64 kMsPerSecond = 1000; |
-static const int64 kInitialTimerDelayMs = 200; |
-static const int64 kMaxTimerDelayMs = 1 * kSecondsPerMinute * kMsPerSecond; |
-// Heart beat message header. If a key message starts with |kHeartBeatHeader|, |
-// it's a heart beat message. Otherwise, it's a key request. |
-static const char kHeartBeatHeader[] = "HEARTBEAT"; |
- |
-// Copies |input_buffer| into a media::DecoderBuffer. If the |input_buffer| is |
-// empty, an empty (end-of-stream) media::DecoderBuffer is returned. |
-static scoped_refptr<media::DecoderBuffer> CopyDecoderBufferFrom( |
- const cdm::InputBuffer& input_buffer) { |
- if (!input_buffer.data) { |
- DCHECK_EQ(input_buffer.data_size, 0); |
- return media::DecoderBuffer::CreateEOSBuffer(); |
- } |
- |
- // TODO(tomfinegan): Get rid of this copy. |
- scoped_refptr<media::DecoderBuffer> output_buffer = |
- media::DecoderBuffer::CopyFrom(input_buffer.data, input_buffer.data_size); |
- |
- std::vector<media::SubsampleEntry> subsamples; |
- for (int32_t i = 0; i < input_buffer.num_subsamples; ++i) { |
- media::SubsampleEntry subsample; |
- subsample.clear_bytes = input_buffer.subsamples[i].clear_bytes; |
- subsample.cypher_bytes = input_buffer.subsamples[i].cipher_bytes; |
- subsamples.push_back(subsample); |
- } |
- |
- scoped_ptr<media::DecryptConfig> decrypt_config(new media::DecryptConfig( |
- std::string(reinterpret_cast<const char*>(input_buffer.key_id), |
- input_buffer.key_id_size), |
- std::string(reinterpret_cast<const char*>(input_buffer.iv), |
- input_buffer.iv_size), |
- input_buffer.data_offset, |
- subsamples)); |
- |
- output_buffer->set_decrypt_config(decrypt_config.Pass()); |
- output_buffer->set_timestamp( |
- base::TimeDelta::FromMicroseconds(input_buffer.timestamp)); |
- |
- return output_buffer; |
-} |
- |
-template<typename Type> |
-class ScopedResetter { |
- public: |
- explicit ScopedResetter(Type* object) : object_(object) {} |
- ~ScopedResetter() { object_->Reset(); } |
- |
- private: |
- Type* const object_; |
-}; |
- |
-void INITIALIZE_CDM_MODULE() { |
-#if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER) |
- DVLOG(2) << "FFmpeg libraries initialized: " << g_ffmpeg_lib_initialized; |
- av_register_all(); |
-#endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER |
-} |
- |
-void DeinitializeCdmModule() { |
-} |
- |
-void* CreateCdmInstance( |
- int cdm_interface_version, |
- const char* key_system, int key_system_size, |
- GetCdmHostFunc get_cdm_host_func, void* user_data) { |
- DVLOG(1) << "CreateCdmInstance()"; |
- |
- if (cdm_interface_version != cdm::kCdmInterfaceVersion) |
- return NULL; |
- |
- cdm::Host* host = static_cast<cdm::Host*>( |
- get_cdm_host_func(cdm::kHostInterfaceVersion, user_data)); |
- if (!host) |
- return NULL; |
- |
- return static_cast<cdm::ContentDecryptionModule*>( |
- new webkit_media::ClearKeyCdm(host)); |
-} |
- |
-const char* GetCdmVersion() { |
- return kClearKeyCdmVersion; |
-} |
- |
-namespace webkit_media { |
- |
-ClearKeyCdm::Client::Client() : status_(kKeyError) {} |
- |
-ClearKeyCdm::Client::~Client() {} |
- |
-void ClearKeyCdm::Client::Reset() { |
- status_ = kKeyError; |
- session_id_.clear(); |
- key_message_.clear(); |
- default_url_.clear(); |
-} |
- |
-void ClearKeyCdm::Client::KeyAdded(const std::string& session_id) { |
- status_ = kKeyAdded; |
- session_id_ = session_id; |
-} |
- |
-void ClearKeyCdm::Client::KeyError(const std::string& session_id, |
- media::MediaKeys::KeyError error_code, |
- int system_code) { |
- status_ = kKeyError; |
- session_id_ = session_id; |
-} |
- |
-void ClearKeyCdm::Client::KeyMessage(const std::string& session_id, |
- const std::vector<uint8>& message, |
- const std::string& default_url) { |
- status_ = kKeyMessage; |
- session_id_ = session_id; |
- key_message_ = message; |
- default_url_ = default_url; |
-} |
- |
-ClearKeyCdm::ClearKeyCdm(cdm::Host* host) |
- : decryptor_(base::Bind(&Client::KeyAdded, base::Unretained(&client_)), |
- base::Bind(&Client::KeyError, base::Unretained(&client_)), |
- base::Bind(&Client::KeyMessage, base::Unretained(&client_))), |
- host_(host), |
- timer_delay_ms_(kInitialTimerDelayMs), |
- timer_set_(false) { |
-#if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER) |
- channel_count_ = 0; |
- bits_per_channel_ = 0; |
- samples_per_second_ = 0; |
- output_timestamp_base_in_microseconds_ = kNoTimestamp; |
- total_samples_generated_ = 0; |
-#endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER |
-} |
- |
-ClearKeyCdm::~ClearKeyCdm() {} |
- |
-cdm::Status ClearKeyCdm::GenerateKeyRequest(const char* type, int type_size, |
- const uint8_t* init_data, |
- int init_data_size) { |
- DVLOG(1) << "GenerateKeyRequest()"; |
- base::AutoLock auto_lock(client_lock_); |
- ScopedResetter<Client> auto_resetter(&client_); |
- decryptor_.GenerateKeyRequest(std::string(type, type_size), |
- init_data, init_data_size); |
- |
- if (client_.status() != Client::kKeyMessage) { |
- host_->SendKeyError(NULL, 0, cdm::kUnknownError, 0); |
- return cdm::kSessionError; |
- } |
- |
- host_->SendKeyMessage( |
- client_.session_id().data(), client_.session_id().size(), |
- reinterpret_cast<const char*>(&client_.key_message()[0]), |
- client_.key_message().size(), |
- client_.default_url().data(), client_.default_url().size()); |
- |
- // Only save the latest session ID for heartbeat messages. |
- heartbeat_session_id_ = client_.session_id(); |
- |
- return cdm::kSuccess; |
-} |
- |
-cdm::Status ClearKeyCdm::AddKey(const char* session_id, |
- int session_id_size, |
- const uint8_t* key, |
- int key_size, |
- const uint8_t* key_id, |
- int key_id_size) { |
- DVLOG(1) << "AddKey()"; |
- base::AutoLock auto_lock(client_lock_); |
- ScopedResetter<Client> auto_resetter(&client_); |
- decryptor_.AddKey(key, key_size, key_id, key_id_size, |
- std::string(session_id, session_id_size)); |
- |
- if (client_.status() != Client::kKeyAdded) |
- return cdm::kSessionError; |
- |
- if (!timer_set_) { |
- ScheduleNextHeartBeat(); |
- timer_set_ = true; |
- } |
- |
- return cdm::kSuccess; |
-} |
- |
-cdm::Status ClearKeyCdm::CancelKeyRequest(const char* session_id, |
- int session_id_size) { |
- DVLOG(1) << "CancelKeyRequest()"; |
- base::AutoLock auto_lock(client_lock_); |
- ScopedResetter<Client> auto_resetter(&client_); |
- decryptor_.CancelKeyRequest(std::string(session_id, session_id_size)); |
- return cdm::kSuccess; |
-} |
- |
-void ClearKeyCdm::TimerExpired(void* context) { |
- std::string heartbeat_message; |
- if (!next_heartbeat_message_.empty() && |
- context == &next_heartbeat_message_[0]) { |
- heartbeat_message = next_heartbeat_message_; |
- } else { |
- heartbeat_message = "ERROR: Invalid timer context found!"; |
- } |
- |
- // This URL is only used for testing the code path for defaultURL. |
- // There is no service at this URL, so applications should ignore it. |
- const char url[] = "http://test.externalclearkey.chromium.org"; |
- |
- host_->SendKeyMessage( |
- heartbeat_session_id_.data(), heartbeat_session_id_.size(), |
- heartbeat_message.data(), heartbeat_message.size(), |
- url, arraysize(url) - 1); |
- |
- ScheduleNextHeartBeat(); |
-} |
- |
-static void CopyDecryptResults( |
- media::Decryptor::Status* status_copy, |
- scoped_refptr<media::DecoderBuffer>* buffer_copy, |
- media::Decryptor::Status status, |
- const scoped_refptr<media::DecoderBuffer>& buffer) { |
- *status_copy = status; |
- *buffer_copy = buffer; |
-} |
- |
-cdm::Status ClearKeyCdm::Decrypt( |
- const cdm::InputBuffer& encrypted_buffer, |
- cdm::DecryptedBlock* decrypted_block) { |
- DVLOG(1) << "Decrypt()"; |
- DCHECK(encrypted_buffer.data); |
- |
- scoped_refptr<media::DecoderBuffer> buffer; |
- cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer); |
- |
- if (status != cdm::kSuccess) |
- return status; |
- |
- DCHECK(buffer->data()); |
- decrypted_block->SetDecryptedBuffer( |
- host_->Allocate(buffer->data_size())); |
- memcpy(reinterpret_cast<void*>(decrypted_block->DecryptedBuffer()->Data()), |
- buffer->data(), |
- buffer->data_size()); |
- decrypted_block->DecryptedBuffer()->SetSize(buffer->data_size()); |
- decrypted_block->SetTimestamp(buffer->timestamp().InMicroseconds()); |
- |
- return cdm::kSuccess; |
-} |
- |
-cdm::Status ClearKeyCdm::InitializeAudioDecoder( |
- const cdm::AudioDecoderConfig& audio_decoder_config) { |
-#if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER) |
- if (!audio_decoder_) |
- audio_decoder_.reset(new webkit_media::FFmpegCdmAudioDecoder(host_)); |
- |
- if (!audio_decoder_->Initialize(audio_decoder_config)) |
- return cdm::kSessionError; |
- |
- return cdm::kSuccess; |
-#elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER) |
- channel_count_ = audio_decoder_config.channel_count; |
- bits_per_channel_ = audio_decoder_config.bits_per_channel; |
- samples_per_second_ = audio_decoder_config.samples_per_second; |
- return cdm::kSuccess; |
-#else |
- NOTIMPLEMENTED(); |
- return cdm::kSessionError; |
-#endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER |
-} |
- |
-cdm::Status ClearKeyCdm::InitializeVideoDecoder( |
- const cdm::VideoDecoderConfig& video_decoder_config) { |
- if (video_decoder_ && video_decoder_->is_initialized()) { |
- DCHECK(!video_decoder_->is_initialized()); |
- return cdm::kSessionError; |
- } |
- |
- // Any uninitialized decoder will be replaced. |
- video_decoder_ = CreateVideoDecoder(host_, video_decoder_config); |
- if (!video_decoder_) |
- return cdm::kSessionError; |
- |
- return cdm::kSuccess; |
-} |
- |
-void ClearKeyCdm::ResetDecoder(cdm::StreamType decoder_type) { |
- DVLOG(1) << "ResetDecoder()"; |
-#if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER) |
- switch (decoder_type) { |
- case cdm::kStreamTypeVideo: |
- video_decoder_->Reset(); |
- break; |
- case cdm::kStreamTypeAudio: |
- audio_decoder_->Reset(); |
- break; |
- default: |
- NOTREACHED() << "ResetDecoder(): invalid cdm::StreamType"; |
- } |
-#elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER) |
- if (decoder_type == cdm::kStreamTypeAudio) { |
- output_timestamp_base_in_microseconds_ = kNoTimestamp; |
- total_samples_generated_ = 0; |
- } |
-#endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER |
-} |
- |
-void ClearKeyCdm::DeinitializeDecoder(cdm::StreamType decoder_type) { |
- DVLOG(1) << "DeinitializeDecoder()"; |
- switch (decoder_type) { |
- case cdm::kStreamTypeVideo: |
- video_decoder_->Deinitialize(); |
- break; |
- case cdm::kStreamTypeAudio: |
-#if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER) |
- audio_decoder_->Deinitialize(); |
-#elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER) |
- output_timestamp_base_in_microseconds_ = kNoTimestamp; |
- total_samples_generated_ = 0; |
-#endif |
- break; |
- default: |
- NOTREACHED() << "DeinitializeDecoder(): invalid cdm::StreamType"; |
- } |
-} |
- |
-cdm::Status ClearKeyCdm::DecryptAndDecodeFrame( |
- const cdm::InputBuffer& encrypted_buffer, |
- cdm::VideoFrame* decoded_frame) { |
- DVLOG(1) << "DecryptAndDecodeFrame()"; |
- TRACE_EVENT0("eme", "ClearKeyCdm::DecryptAndDecodeFrame"); |
- |
- scoped_refptr<media::DecoderBuffer> buffer; |
- cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer); |
- |
- if (status != cdm::kSuccess) |
- return status; |
- |
- const uint8_t* data = NULL; |
- int32_t size = 0; |
- int64_t timestamp = 0; |
- if (!buffer->end_of_stream()) { |
- data = buffer->data(); |
- size = buffer->data_size(); |
- timestamp = encrypted_buffer.timestamp; |
- } |
- |
- return video_decoder_->DecodeFrame(data, size, timestamp, decoded_frame); |
-} |
- |
-cdm::Status ClearKeyCdm::DecryptAndDecodeSamples( |
- const cdm::InputBuffer& encrypted_buffer, |
- cdm::AudioFrames* audio_frames) { |
- DVLOG(1) << "DecryptAndDecodeSamples()"; |
- |
- scoped_refptr<media::DecoderBuffer> buffer; |
- cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer); |
- |
- if (status != cdm::kSuccess) |
- return status; |
- |
-#if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER) |
- const uint8_t* data = NULL; |
- int32_t size = 0; |
- int64_t timestamp = 0; |
- if (!buffer->end_of_stream()) { |
- data = buffer->data(); |
- size = buffer->data_size(); |
- timestamp = encrypted_buffer.timestamp; |
- } |
- |
- return audio_decoder_->DecodeBuffer(data, size, timestamp, audio_frames); |
-#elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER) |
- int64 timestamp_in_microseconds = kNoTimestamp; |
- if (!buffer->end_of_stream()) { |
- timestamp_in_microseconds = buffer->GetTimestamp().InMicroseconds(); |
- DCHECK(timestamp_in_microseconds != kNoTimestamp); |
- } |
- return GenerateFakeAudioFrames(timestamp_in_microseconds, audio_frames); |
-#else |
- return cdm::kSuccess; |
-#endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER |
-} |
- |
-void ClearKeyCdm::Destroy() { |
- DVLOG(1) << "Destroy()"; |
- delete this; |
-} |
- |
-void ClearKeyCdm::ScheduleNextHeartBeat() { |
- // Prepare the next heartbeat message and set timer. |
- std::ostringstream msg_stream; |
- msg_stream << kHeartBeatHeader << " from ClearKey CDM set at time " |
- << host_->GetCurrentWallTimeInSeconds() << "."; |
- next_heartbeat_message_ = msg_stream.str(); |
- |
- host_->SetTimer(timer_delay_ms_, &next_heartbeat_message_[0]); |
- |
- // Use a smaller timer delay at start-up to facilitate testing. Increase the |
- // timer delay up to a limit to avoid message spam. |
- if (timer_delay_ms_ < kMaxTimerDelayMs) |
- timer_delay_ms_ = std::min(2 * timer_delay_ms_, kMaxTimerDelayMs); |
-} |
- |
-cdm::Status ClearKeyCdm::DecryptToMediaDecoderBuffer( |
- const cdm::InputBuffer& encrypted_buffer, |
- scoped_refptr<media::DecoderBuffer>* decrypted_buffer) { |
- DCHECK(decrypted_buffer); |
- scoped_refptr<media::DecoderBuffer> buffer = |
- CopyDecoderBufferFrom(encrypted_buffer); |
- |
- if (buffer->end_of_stream()) { |
- *decrypted_buffer = buffer; |
- return cdm::kSuccess; |
- } |
- |
- // Callback is called synchronously, so we can use variables on the stack. |
- media::Decryptor::Status status = media::Decryptor::kError; |
- // The AesDecryptor does not care what the stream type is. Pass kVideo |
- // for both audio and video decryption. |
- decryptor_.Decrypt( |
- media::Decryptor::kVideo, |
- buffer, |
- base::Bind(&CopyDecryptResults, &status, decrypted_buffer)); |
- |
- if (status == media::Decryptor::kError) |
- return cdm::kDecryptError; |
- |
- if (status == media::Decryptor::kNoKey) |
- return cdm::kNoKey; |
- |
- DCHECK_EQ(status, media::Decryptor::kSuccess); |
- return cdm::kSuccess; |
-} |
- |
-#if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER) |
-int64 ClearKeyCdm::CurrentTimeStampInMicroseconds() const { |
- return output_timestamp_base_in_microseconds_ + |
- base::Time::kMicrosecondsPerSecond * |
- total_samples_generated_ / samples_per_second_; |
-} |
- |
-int ClearKeyCdm::GenerateFakeAudioFramesFromDuration( |
- int64 duration_in_microseconds, |
- cdm::AudioFrames* audio_frames) const { |
- int64 samples_to_generate = static_cast<double>(samples_per_second_) * |
- duration_in_microseconds / base::Time::kMicrosecondsPerSecond + 0.5; |
- if (samples_to_generate <= 0) |
- return 0; |
- |
- int64 bytes_per_sample = channel_count_ * bits_per_channel_ / 8; |
- // |frame_size| must be a multiple of |bytes_per_sample|. |
- int64 frame_size = bytes_per_sample * samples_to_generate; |
- |
- int64 timestamp = CurrentTimeStampInMicroseconds(); |
- |
- const int kHeaderSize = sizeof(timestamp) + sizeof(frame_size); |
- audio_frames->SetFrameBuffer(host_->Allocate(kHeaderSize + frame_size)); |
- uint8_t* data = audio_frames->FrameBuffer()->Data(); |
- |
- memcpy(data, ×tamp, sizeof(timestamp)); |
- data += sizeof(timestamp); |
- memcpy(data, &frame_size, sizeof(frame_size)); |
- data += sizeof(frame_size); |
- // You won't hear anything because we have all zeros here. But the video |
- // should play just fine! |
- memset(data, 0, frame_size); |
- |
- audio_frames->FrameBuffer()->SetSize(kHeaderSize + frame_size); |
- |
- return samples_to_generate; |
-} |
- |
-cdm::Status ClearKeyCdm::GenerateFakeAudioFrames( |
- int64 timestamp_in_microseconds, |
- cdm::AudioFrames* audio_frames) { |
- if (timestamp_in_microseconds == kNoTimestamp) |
- return cdm::kNeedMoreData; |
- |
- // Return kNeedMoreData for the first frame because duration is unknown. |
- if (output_timestamp_base_in_microseconds_ == kNoTimestamp) { |
- output_timestamp_base_in_microseconds_ = timestamp_in_microseconds; |
- return cdm::kNeedMoreData; |
- } |
- |
- int samples_generated = GenerateFakeAudioFramesFromDuration( |
- timestamp_in_microseconds - CurrentTimeStampInMicroseconds(), |
- audio_frames); |
- total_samples_generated_ += samples_generated; |
- |
- return samples_generated == 0 ? cdm::kNeedMoreData : cdm::kSuccess; |
-} |
-#endif // CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER |
- |
-} // namespace webkit_media |