diff options
author | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-15 04:42:43 +0000 |
---|---|---|
committer | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-15 04:42:43 +0000 |
commit | ff41739978fb88dc0334f8bf2546e6ee8eaf5a0e (patch) | |
tree | 6df3c69f6d788bc95c80109cee09f1b3881b7391 /media | |
parent | ec2e2f697e94600cb16b53f2b9c88664526a4982 (diff) | |
download | chromium_src-ff41739978fb88dc0334f8bf2546e6ee8eaf5a0e.zip chromium_src-ff41739978fb88dc0334f8bf2546e6ee8eaf5a0e.tar.gz chromium_src-ff41739978fb88dc0334f8bf2546e6ee8eaf5a0e.tar.bz2 |
Change AesDecryptor::Decrypt to be an asynchronous call.
BUG=125401
TEST=media_tests, media layout tests.
Review URL: https://chromiumcodereview.appspot.com/10561014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@146748 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/base/decryptor.h | 25 | ||||
-rw-r--r-- | media/crypto/aes_decryptor.cc | 28 | ||||
-rw-r--r-- | media/crypto/aes_decryptor.h | 4 | ||||
-rw-r--r-- | media/crypto/aes_decryptor_unittest.cc | 24 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer_unittest.cc | 1 | ||||
-rw-r--r-- | media/filters/ffmpeg_video_decoder.cc | 78 | ||||
-rw-r--r-- | media/filters/ffmpeg_video_decoder.h | 23 |
7 files changed, 135 insertions, 48 deletions
diff --git a/media/base/decryptor.h b/media/base/decryptor.h index 924f3ac..442c3c6 100644 --- a/media/base/decryptor.h +++ b/media/base/decryptor.h @@ -8,6 +8,7 @@ #include <string> #include "base/basictypes.h" +#include "base/callback.h" #include "base/memory/ref_counted.h" #include "media/base/media_export.h" @@ -31,6 +32,11 @@ class MEDIA_EXPORT Decryptor { kDomainError }; + enum DecryptStatus { + kSuccess, // Decryption successfully completed. Decrypted buffer ready. + kError // Error occurred during decryption. + }; + Decryptor() {} virtual ~Decryptor() {} @@ -54,11 +60,20 @@ class MEDIA_EXPORT Decryptor { virtual void CancelKeyRequest(const std::string& key_system, const std::string& session_id) = 0; - // Decrypts the |input| buffer, which should not be NULL. - // Returns a DecoderBuffer with the decrypted data if decryption succeeded. - // Returns NULL if decryption failed. - virtual scoped_refptr<DecoderBuffer> Decrypt( - const scoped_refptr<DecoderBuffer>& input) = 0; + // Decrypts the |encrypted| buffer. The decrypt status and decrypted buffer + // are returned via the provided callback |decrypt_cb|. The |encrypted| buffer + // must not be NULL. + // + // Note that the callback maybe called synchronously or asynchronously. + // + // If the returned status is kSuccess, the |encrypted| buffer is successfully + // decrypted and the decrypted buffer is ready to be read. + // If the returned status is kError, unexpected error has occurred. In this + // case the decrypted buffer must be NULL. + typedef base::Callback<void(DecryptStatus, + const scoped_refptr<DecoderBuffer>&)> DecryptCB; + virtual void Decrypt(const scoped_refptr<DecoderBuffer>& encrypted, + const DecryptCB& decrypt_cb) = 0; private: DISALLOW_COPY_AND_ASSIGN(Decryptor); diff --git a/media/crypto/aes_decryptor.cc b/media/crypto/aes_decryptor.cc index 129bc33..f677f0d 100644 --- a/media/crypto/aes_decryptor.cc +++ b/media/crypto/aes_decryptor.cc @@ -131,8 +131,8 @@ void AesDecryptor::CancelKeyRequest(const std::string& key_system, const std::string& session_id) { } -scoped_refptr<DecoderBuffer> AesDecryptor::Decrypt( - const scoped_refptr<DecoderBuffer>& encrypted) { +void AesDecryptor::Decrypt(const scoped_refptr<DecoderBuffer>& encrypted, + const DecryptCB& decrypt_cb) { CHECK(encrypted->GetDecryptConfig()); const uint8* key_id = encrypted->GetDecryptConfig()->key_id(); const int key_id_size = encrypted->GetDecryptConfig()->key_id_size(); @@ -144,21 +144,27 @@ scoped_refptr<DecoderBuffer> AesDecryptor::Decrypt( { base::AutoLock auto_lock(key_map_lock_); KeyMap::const_iterator found = key_map_.find(key_id_string); - if (found == key_map_.end()) { - DVLOG(1) << "Could not find a matching key for given key ID."; - return NULL; - } - key = found->second; + if (found != key_map_.end()) + key = found->second; + } + + if (!key) { + DVLOG(1) << "Could not find a matching key for given key ID."; + decrypt_cb.Run(kError, NULL); + return; } scoped_refptr<DecoderBuffer> decrypted = DecryptData(*encrypted, key); - if (decrypted) { - decrypted->SetTimestamp(encrypted->GetTimestamp()); - decrypted->SetDuration(encrypted->GetDuration()); + if (!decrypted) { + DVLOG(1) << "Decryption failed."; + decrypt_cb.Run(kError, NULL); + return; } - return decrypted; + decrypted->SetTimestamp(encrypted->GetTimestamp()); + decrypted->SetDuration(encrypted->GetDuration()); + decrypt_cb.Run(kSuccess, decrypted); } } // namespace media diff --git a/media/crypto/aes_decryptor.h b/media/crypto/aes_decryptor.h index 7201052..6d0bf47 100644 --- a/media/crypto/aes_decryptor.h +++ b/media/crypto/aes_decryptor.h @@ -42,8 +42,8 @@ class MEDIA_EXPORT AesDecryptor : public Decryptor { const std::string& session_id) OVERRIDE; virtual void CancelKeyRequest(const std::string& key_system, const std::string& session_id) OVERRIDE; - virtual scoped_refptr<DecoderBuffer> Decrypt( - const scoped_refptr<DecoderBuffer>& input) OVERRIDE; + virtual void Decrypt(const scoped_refptr<DecoderBuffer>& encrypted, + const DecryptCB& decrypt_cb) OVERRIDE; private: // KeyMap owns the crypto::SymmetricKey* and must delete them when they are diff --git a/media/crypto/aes_decryptor_unittest.cc b/media/crypto/aes_decryptor_unittest.cc index 814bd6c..3d9d983 100644 --- a/media/crypto/aes_decryptor_unittest.cc +++ b/media/crypto/aes_decryptor_unittest.cc @@ -5,14 +5,17 @@ #include <string> #include "base/basictypes.h" +#include "base/bind.h" #include "media/base/decoder_buffer.h" #include "media/base/decrypt_config.h" #include "media/base/mock_filters.h" #include "media/crypto/aes_decryptor.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::_; using ::testing::Gt; +using ::testing::IsNull; using ::testing::NotNull; using ::testing::SaveArg; using ::testing::StrNe; @@ -49,7 +52,10 @@ static const uint8 kKeyId2[] = { class AesDecryptorTest : public testing::Test { public: - AesDecryptorTest() : decryptor_(&client_) { + AesDecryptorTest() + : decryptor_(&client_), + decrypt_cb_(base::Bind(&AesDecryptorTest::BufferDecrypted, + base::Unretained(this))) { encrypted_data_ = DecoderBuffer::CopyFrom(kEncryptedData, arraysize(kEncryptedData)); } @@ -86,9 +92,15 @@ class AesDecryptorTest : public testing::Test { scoped_ptr<DecryptConfig>(new DecryptConfig(key_id, KeyIdSize))); } + MOCK_METHOD2(BufferDecrypted, void(Decryptor::DecryptStatus, + const scoped_refptr<DecoderBuffer>&)); + void DecryptAndExpectToSucceed() { - scoped_refptr<DecoderBuffer> decrypted = - decryptor_.Decrypt(encrypted_data_); + scoped_refptr<DecoderBuffer> decrypted; + EXPECT_CALL(*this, BufferDecrypted(AesDecryptor::kSuccess, NotNull())) + .WillOnce(SaveArg<1>(&decrypted)); + + decryptor_.Decrypt(encrypted_data_, decrypt_cb_); ASSERT_TRUE(decrypted); int data_length = sizeof(kOriginalData); ASSERT_EQ(data_length, decrypted->GetDataSize()); @@ -96,15 +108,15 @@ class AesDecryptorTest : public testing::Test { } void DecryptAndExpectToFail() { - scoped_refptr<DecoderBuffer> decrypted = - decryptor_.Decrypt(encrypted_data_); - EXPECT_FALSE(decrypted); + EXPECT_CALL(*this, BufferDecrypted(AesDecryptor::kError, IsNull())); + decryptor_.Decrypt(encrypted_data_, decrypt_cb_); } scoped_refptr<DecoderBuffer> encrypted_data_; MockDecryptorClient client_; AesDecryptor decryptor_; std::string session_id_string_; + AesDecryptor::DecryptCB decrypt_cb_; }; TEST_F(AesDecryptorTest, NormalDecryption) { diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc index bfb31a2..cb492f5 100644 --- a/media/filters/ffmpeg_demuxer_unittest.cc +++ b/media/filters/ffmpeg_demuxer_unittest.cc @@ -24,7 +24,6 @@ using ::testing::AnyNumber; using ::testing::DoAll; using ::testing::InSequence; using ::testing::Invoke; -using ::testing::NotNull; using ::testing::Return; using ::testing::SaveArg; using ::testing::SetArgPointee; diff --git a/media/filters/ffmpeg_video_decoder.cc b/media/filters/ffmpeg_video_decoder.cc index 7e681f8..ed41fc0 100644 --- a/media/filters/ffmpeg_video_decoder.cc +++ b/media/filters/ffmpeg_video_decoder.cc @@ -4,13 +4,15 @@ #include "media/filters/ffmpeg_video_decoder.h" +#include <algorithm> +#include <string> + #include "base/bind.h" #include "base/callback_helpers.h" #include "base/command_line.h" #include "base/message_loop.h" #include "base/string_number_conversions.h" #include "media/base/decoder_buffer.h" -#include "media/base/decryptor.h" #include "media/base/demuxer_stream.h" #include "media/base/limits.h" #include "media/base/media_switches.h" @@ -275,18 +277,19 @@ void FFmpegVideoDecoder::ReadFromDemuxerStream() { DCHECK_NE(state_, kDecodeFinished); DCHECK(!read_cb_.is_null()); - demuxer_stream_->Read(base::Bind(&FFmpegVideoDecoder::DecodeBuffer, this)); + demuxer_stream_->Read(base::Bind(&FFmpegVideoDecoder::DecryptOrDecodeBuffer, + this)); } -void FFmpegVideoDecoder::DecodeBuffer( +void FFmpegVideoDecoder::DecryptOrDecodeBuffer( const scoped_refptr<DecoderBuffer>& buffer) { // TODO(scherkus): fix FFmpegDemuxerStream::Read() to not execute our read // callback on the same execution stack so we can get rid of forced task post. message_loop_->PostTask(FROM_HERE, base::Bind( - &FFmpegVideoDecoder::DoDecodeBuffer, this, buffer)); + &FFmpegVideoDecoder::DoDecryptOrDecodeBuffer, this, buffer)); } -void FFmpegVideoDecoder::DoDecodeBuffer( +void FFmpegVideoDecoder::DoDecryptOrDecodeBuffer( const scoped_refptr<DecoderBuffer>& buffer) { DCHECK_EQ(MessageLoop::current(), message_loop_); DCHECK_NE(state_, kUninitialized); @@ -304,6 +307,58 @@ void FFmpegVideoDecoder::DoDecodeBuffer( return; } + if (buffer->GetDecryptConfig() && buffer->GetDataSize()) { + decryptor_->Decrypt(buffer, + base::Bind(&FFmpegVideoDecoder::BufferDecrypted, this)); + return; + } + + DecodeBuffer(buffer); +} + +void FFmpegVideoDecoder::BufferDecrypted( + Decryptor::DecryptStatus decrypt_status, + const scoped_refptr<DecoderBuffer>& buffer) { + message_loop_->PostTask(FROM_HERE, base::Bind( + &FFmpegVideoDecoder::DoBufferDecrypted, this, decrypt_status, buffer)); +} + +void FFmpegVideoDecoder::DoBufferDecrypted( + Decryptor::DecryptStatus decrypt_status, + const scoped_refptr<DecoderBuffer>& buffer) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + DCHECK_NE(state_, kUninitialized); + DCHECK_NE(state_, kDecodeFinished); + DCHECK(!read_cb_.is_null()); + + if (!reset_cb_.is_null()) { + DeliverFrame(NULL); + DoReset(); + return; + } + + if (decrypt_status == Decryptor::kError) { + state_ = kDecodeFinished; + base::ResetAndReturn(&read_cb_).Run(kDecryptError, NULL); + return; + } + + DCHECK_EQ(Decryptor::kSuccess, decrypt_status); + DCHECK(buffer); + DCHECK(buffer->GetDataSize()); + DCHECK(!buffer->GetDecryptConfig()); + DecodeBuffer(buffer); +} + +void FFmpegVideoDecoder::DecodeBuffer( + const scoped_refptr<DecoderBuffer>& buffer) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + DCHECK_NE(state_, kUninitialized); + DCHECK_NE(state_, kDecodeFinished); + DCHECK(reset_cb_.is_null()); + DCHECK(!read_cb_.is_null()); + DCHECK(buffer); + // During decode, because reads are issued asynchronously, it is possible to // receive multiple end of stream buffers since each read is acked. When the // first end of stream buffer is read, FFmpeg may still have frames queued @@ -334,19 +389,8 @@ void FFmpegVideoDecoder::DoDecodeBuffer( state_ = kFlushCodec; } - scoped_refptr<DecoderBuffer> unencrypted_buffer = buffer; - if (buffer->GetDecryptConfig() && buffer->GetDataSize()) { - unencrypted_buffer = decryptor_->Decrypt(buffer); - - if (!unencrypted_buffer || !unencrypted_buffer->GetDataSize()) { - state_ = kDecodeFinished; - base::ResetAndReturn(&read_cb_).Run(kDecryptError, NULL); - return; - } - } - scoped_refptr<VideoFrame> video_frame; - if (!Decode(unencrypted_buffer, &video_frame)) { + if (!Decode(buffer, &video_frame)) { state_ = kDecodeFinished; base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); return; diff --git a/media/filters/ffmpeg_video_decoder.h b/media/filters/ffmpeg_video_decoder.h index 3bfcf3f..0f07755 100644 --- a/media/filters/ffmpeg_video_decoder.h +++ b/media/filters/ffmpeg_video_decoder.h @@ -5,10 +5,9 @@ #ifndef MEDIA_FILTERS_FFMPEG_VIDEO_DECODER_H_ #define MEDIA_FILTERS_FFMPEG_VIDEO_DECODER_H_ -#include <deque> - #include "base/callback.h" #include "base/memory/ref_counted.h" +#include "media/base/decryptor.h" #include "media/base/video_decoder.h" class MessageLoop; @@ -19,7 +18,6 @@ struct AVFrame; namespace media { class DecoderBuffer; -class Decryptor; class MEDIA_EXPORT FFmpegVideoDecoder : public VideoDecoder { public: @@ -59,10 +57,23 @@ class MEDIA_EXPORT FFmpegVideoDecoder : public VideoDecoder { // Reads from the demuxer stream with corresponding callback method. void ReadFromDemuxerStream(); - void DecodeBuffer(const scoped_refptr<DecoderBuffer>& buffer); + void DecryptOrDecodeBuffer(const scoped_refptr<DecoderBuffer>& buffer); + + // Carries out the buffer processing operation scheduled by + // DecryptOrDecodeBuffer(). + void DoDecryptOrDecodeBuffer(const scoped_refptr<DecoderBuffer>& buffer); - // Carries out the decoding operation scheduled by DecodeBuffer(). - void DoDecodeBuffer(const scoped_refptr<DecoderBuffer>& buffer); + // Callback called by the decryptor to deliver decrypted data buffer and + // reporting decrypt status. This callback could be called synchronously or + // asynchronously. + void BufferDecrypted(Decryptor::DecryptStatus decrypt_status, + const scoped_refptr<DecoderBuffer>& buffer); + + // Carries out the operation scheduled by BufferDecrypted(). + void DoBufferDecrypted(Decryptor::DecryptStatus decrypt_status, + const scoped_refptr<DecoderBuffer>& buffer); + + void DecodeBuffer(const scoped_refptr<DecoderBuffer>& buffer); bool Decode(const scoped_refptr<DecoderBuffer>& buffer, scoped_refptr<VideoFrame>* video_frame); |