summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--media/base/mock_filters.cc20
-rw-r--r--media/base/mock_filters.h36
-rw-r--r--media/base/stream_parser.h4
-rw-r--r--media/crypto/aes_decryptor.cc82
-rw-r--r--media/crypto/aes_decryptor.h67
-rw-r--r--media/crypto/aes_decryptor_unittest.cc117
-rw-r--r--media/crypto/decryptor_client.h50
-rw-r--r--media/filters/chunk_demuxer.cc8
-rw-r--r--media/filters/chunk_demuxer.h2
-rw-r--r--media/filters/chunk_demuxer_client.h6
-rw-r--r--media/filters/chunk_demuxer_unittest.cc15
-rw-r--r--media/filters/ffmpeg_video_decoder_unittest.cc44
-rw-r--r--media/filters/pipeline_integration_test.cc122
-rw-r--r--media/filters/pipeline_integration_test_base.cc3
-rw-r--r--media/filters/pipeline_integration_test_base.h3
-rw-r--r--media/media.gyp1
-rw-r--r--media/mp4/mp4_stream_parser.cc6
-rw-r--r--media/mp4/mp4_stream_parser.h4
-rw-r--r--media/webm/webm_stream_parser.cc8
-rw-r--r--media/webm/webm_stream_parser.h4
-rw-r--r--webkit/media/webmediaplayer_impl.cc118
-rw-r--r--webkit/media/webmediaplayer_impl.h19
-rw-r--r--webkit/media/webmediaplayer_proxy.cc77
-rw-r--r--webkit/media/webmediaplayer_proxy.h53
24 files changed, 667 insertions, 202 deletions
diff --git a/media/base/mock_filters.cc b/media/base/mock_filters.cc
index a5977a4..b10d42a 100644
--- a/media/base/mock_filters.cc
+++ b/media/base/mock_filters.cc
@@ -41,6 +41,26 @@ MockAudioRenderer::MockAudioRenderer() {}
MockAudioRenderer::~MockAudioRenderer() {}
+MockDecryptorClient::MockDecryptorClient() {}
+
+MockDecryptorClient::~MockDecryptorClient() {}
+
+void MockDecryptorClient::KeyMessage(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> message,
+ int message_length,
+ const std::string& default_url) {
+ KeyMessageMock(key_system, session_id, message.get(), message_length,
+ default_url);
+}
+
+void MockDecryptorClient::NeedKey(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> init_data,
+ int init_data_length) {
+ NeedKeyMock(key_system, session_id, init_data.get(), init_data_length);
+}
+
MockFilterCollection::MockFilterCollection()
: demuxer_(new MockDemuxer()),
video_decoder_(new MockVideoDecoder()),
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index 626392c..cf8fa28 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -26,6 +26,7 @@
#include "media/base/video_decoder_config.h"
#include "media/base/video_frame.h"
#include "media/base/video_renderer.h"
+#include "media/crypto/decryptor_client.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace media {
@@ -209,6 +210,41 @@ class MockAudioRenderer : public AudioRenderer {
DISALLOW_COPY_AND_ASSIGN(MockAudioRenderer);
};
+class MockDecryptorClient : public DecryptorClient {
+ public:
+ MockDecryptorClient();
+ virtual ~MockDecryptorClient();
+
+ MOCK_METHOD2(KeyAdded, void(const std::string&, const std::string&));
+ MOCK_METHOD4(KeyError, void(const std::string&, const std::string&,
+ AesDecryptor::KeyError, int));
+ // TODO(xhwang): This is a workaround of the issue that move-only parameters
+ // are not supported in mocked methods. Remove this when the issue is fixed
+ // (http://code.google.com/p/googletest/issues/detail?id=395) or when we use
+ // std::string instead of scoped_array<uint8> (http://crbug.com/130689).
+ MOCK_METHOD5(KeyMessageMock, void(const std::string& key_system,
+ const std::string& session_id,
+ const uint8* message,
+ int message_length,
+ const std::string& default_url));
+ MOCK_METHOD4(NeedKeyMock, void(const std::string& key_system,
+ const std::string& session_id,
+ const uint8* init_data,
+ int init_data_length));
+ virtual void KeyMessage(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> message,
+ int message_length,
+ const std::string& default_url) OVERRIDE;
+ virtual void NeedKey(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> init_data,
+ int init_data_length) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockDecryptorClient);
+};
+
// FilterFactory that returns canned instances of mock filters. You can set
// expectations on the filters and then pass the collection into a pipeline.
class MockFilterCollection {
diff --git a/media/base/stream_parser.h b/media/base/stream_parser.h
index cdf146d..3ad3cd7 100644
--- a/media/base/stream_parser.h
+++ b/media/base/stream_parser.h
@@ -63,7 +63,7 @@ class MEDIA_EXPORT StreamParser {
// Return value - True indicates that the initialization data is accepted.
// False if something was wrong with the initialization data
// and a parsing error should be signalled.
- typedef base::Callback<bool(scoped_array<uint8>, int)> KeyNeededCB;
+ typedef base::Callback<bool(scoped_array<uint8>, int)> NeedKeyCB;
// Initialize the parser with necessary callbacks. Must be called before any
// data is passed to Parse(). |init_cb| will be called once enough data has
@@ -73,7 +73,7 @@ class MEDIA_EXPORT StreamParser {
const NewConfigCB& config_cb,
const NewBuffersCB& audio_cb,
const NewBuffersCB& video_cb,
- const KeyNeededCB& key_needed_cb,
+ const NeedKeyCB& need_key_cb,
const NewMediaSegmentCB& new_segment_cb) = 0;
// Called when a seek occurs. This flushes the current parser state
diff --git a/media/crypto/aes_decryptor.cc b/media/crypto/aes_decryptor.cc
index cbb75b3..49275fb 100644
--- a/media/crypto/aes_decryptor.cc
+++ b/media/crypto/aes_decryptor.cc
@@ -6,17 +6,21 @@
#include "base/logging.h"
#include "base/stl_util.h"
+#include "base/string_number_conversions.h"
#include "base/string_piece.h"
#include "crypto/encryptor.h"
#include "crypto/symmetric_key.h"
#include "media/base/decoder_buffer.h"
#include "media/base/decrypt_config.h"
+#include "media/crypto/decryptor_client.h"
namespace media {
// TODO(xhwang): Get real IV from frames.
static const char kInitialCounter[] = "0000000000000000";
+uint32 AesDecryptor::next_session_id_ = 1;
+
// Decrypt |input| using |key|.
// Return a DecoderBuffer with the decrypted data if decryption succeeded.
// Return NULL if decryption failed.
@@ -48,35 +52,83 @@ static scoped_refptr<DecoderBuffer> DecryptData(const DecoderBuffer& input,
decrypted_text.size());
}
-AesDecryptor::AesDecryptor() {}
+AesDecryptor::AesDecryptor(DecryptorClient* client)
+ : client_(client) {
+}
AesDecryptor::~AesDecryptor() {
STLDeleteValues(&key_map_);
}
-void AesDecryptor::AddKey(const uint8* key_id, int key_id_size,
- const uint8* key, int key_size) {
- CHECK(key_id && key);
- CHECK_GT(key_id_size, 0);
- CHECK_GT(key_size, 0);
+void AesDecryptor::GenerateKeyRequest(const std::string& key_system,
+ const uint8* init_data,
+ int init_data_length) {
+ std::string session_id_string(base::UintToString(next_session_id_++));
- std::string key_id_string(reinterpret_cast<const char*>(key_id), key_id_size);
- std::string key_string(reinterpret_cast<const char*>(key) , key_size);
+ // For now, just fire the event with the |init_data| as the request.
+ int message_length = init_data_length;
+ scoped_array<uint8> message(new uint8[message_length]);
+ memcpy(message.get(), init_data, message_length);
+
+ client_->KeyMessage(key_system, session_id_string,
+ message.Pass(), message_length, "");
+}
+
+void AesDecryptor::AddKey(const std::string& key_system,
+ const uint8* key,
+ int key_length,
+ const uint8* init_data,
+ int init_data_length,
+ const std::string& session_id) {
+ CHECK(key);
+ CHECK_GT(key_length, 0);
+
+ // TODO(xhwang): Add |session_id| check after we figure out how:
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=16550
+
+ const int kSupportedKeyLength = 16; // 128-bit key.
+ if (key_length != kSupportedKeyLength) {
+ DVLOG(1) << "Invalid key length: " << key_length;
+ client_->KeyError(key_system, session_id, kUnknownError, 0);
+ return;
+ }
+ // TODO(xhwang): Fix the decryptor to accept no |init_data|. See
+ // http://crbug.com/123265. Until then, ensure a non-empty value is passed.
+ static const uint8 kDummyInitData[1] = { 0 };
+ if (!init_data) {
+ init_data = kDummyInitData;
+ init_data_length = arraysize(kDummyInitData);
+ }
+
+ // TODO(xhwang): For now, use |init_data| for key ID. Make this more spec
+ // compliant later (http://crbug.com/123262, http://crbug.com/123265).
+ std::string key_id_string(reinterpret_cast<const char*>(init_data),
+ init_data_length);
+ std::string key_string(reinterpret_cast<const char*>(key) , key_length);
crypto::SymmetricKey* symmetric_key = crypto::SymmetricKey::Import(
crypto::SymmetricKey::AES, key_string);
if (!symmetric_key) {
DVLOG(1) << "Could not import key.";
+ client_->KeyError(key_system, session_id, kUnknownError, 0);
return;
}
- base::AutoLock auto_lock(lock_);
- KeyMap::iterator found = key_map_.find(key_id_string);
- if (found != key_map_.end()) {
- delete found->second;
- key_map_.erase(found);
+ {
+ base::AutoLock auto_lock(key_map_lock_);
+ KeyMap::iterator found = key_map_.find(key_id_string);
+ if (found != key_map_.end()) {
+ delete found->second;
+ key_map_.erase(found);
+ }
+ key_map_[key_id_string] = symmetric_key;
}
- key_map_[key_id_string] = symmetric_key;
+
+ client_->KeyAdded(key_system, session_id);
+}
+
+void AesDecryptor::CancelKeyRequest(const std::string& key_system,
+ const std::string& session_id) {
}
scoped_refptr<DecoderBuffer> AesDecryptor::Decrypt(
@@ -90,7 +142,7 @@ scoped_refptr<DecoderBuffer> AesDecryptor::Decrypt(
crypto::SymmetricKey* key = NULL;
{
- base::AutoLock auto_lock(lock_);
+ 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.";
diff --git a/media/crypto/aes_decryptor.h b/media/crypto/aes_decryptor.h
index d62528f..3471c53 100644
--- a/media/crypto/aes_decryptor.h
+++ b/media/crypto/aes_decryptor.h
@@ -20,23 +20,55 @@ class SymmetricKey;
namespace media {
class DecoderBuffer;
+class DecryptorClient;
// Decrypts AES encrypted buffer into unencrypted buffer.
+// All public methods other than Decrypt() will be called on the renderer
+// thread. Therefore, these calls should be fast and nonblocking, with key
+// events fired asynchronously. Decrypt() will be called on the (video/audio)
+// decoder thread synchronously.
class MEDIA_EXPORT AesDecryptor {
public:
- AesDecryptor();
+ enum KeyError {
+ kUnknownError = 1,
+ kClientError,
+ kServiceError,
+ kOutputError,
+ kHardwareChangeError,
+ kDomainError
+ };
+
+ // The AesDecryptor does not take ownership of the |client|. The |client|
+ // must be valid throughout the lifetime of the AesDecryptor.
+ explicit AesDecryptor(DecryptorClient* client);
~AesDecryptor();
- // Add a |key_id| and |key| pair to the key system. The key is not limited to
- // a decryption key. It can be any data that the key system accepts, such as
- // a license. If multiple calls of this function set different keys for the
- // same |key_id|, the older key will be replaced by the newer key.
- void AddKey(const uint8* key_id, int key_id_size,
- const uint8* key, int key_size);
+ // Generates a key request. The result of this call will be reported via the
+ // client's KeyMessage() or KeyError() methods.
+ void GenerateKeyRequest(const std::string& key_system,
+ const uint8* init_data,
+ int init_data_length);
+
+ // Adds a |key| to the key system. The key is not limited to a decryption key.
+ // It can be any data that the key system accepts, such as a license.
+ // If multiple calls of this function set different keys for the same
+ // |key_id|, the older key will be replaced by the newer key.
+ // The result of this call will be reported via the client's KeyAdded(),
+ // KeyMessage() or KeyError() methods.
+ void AddKey(const std::string& key_system,
+ const uint8* key,
+ int key_length,
+ const uint8* init_data,
+ int init_data_length,
+ const std::string& session_id);
+
+ // Cancels the key request specified by |session_id|.
+ void CancelKeyRequest(const std::string& key_system,
+ const std::string& session_id);
- // Decrypt |input| buffer. The |input| should not be NULL.
- // Return a DecoderBuffer with the decrypted data if decryption succeeded.
- // Return NULL if decryption failed.
+ // Decrypts the |input| buffer, which should not be NULL.
+ // Returns a DecoderBuffer with the decrypted data if decryption succeeded.
+ // Returns NULL if decryption failed.
scoped_refptr<DecoderBuffer> Decrypt(
const scoped_refptr<DecoderBuffer>& input);
@@ -44,8 +76,19 @@ class MEDIA_EXPORT AesDecryptor {
// KeyMap owns the crypto::SymmetricKey* and must delete them when they are
// not needed any more.
typedef base::hash_map<std::string, crypto::SymmetricKey*> KeyMap;
- KeyMap key_map_;
- base::Lock lock_;
+
+ // Since only Decrypt() is called off the renderer thread, we only need to
+ // protect |key_map_|, the only member variable that is shared between
+ // Decrypt() and other methods.
+ KeyMap key_map_; // Protected by the |key_map_lock_|.
+ base::Lock key_map_lock_; // Protects the |key_map_|.
+
+ DecryptorClient* client_;
+
+ // Make session ID unique per renderer by making it static.
+ // TODO(xhwang): Make session ID more strictly defined if needed:
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=16739#c0
+ static uint32 next_session_id_;
DISALLOW_COPY_AND_ASSIGN(AesDecryptor);
};
diff --git a/media/crypto/aes_decryptor_unittest.cc b/media/crypto/aes_decryptor_unittest.cc
index 0b33f61..69cca61 100644
--- a/media/crypto/aes_decryptor_unittest.cc
+++ b/media/crypto/aes_decryptor_unittest.cc
@@ -4,45 +4,93 @@
#include <string>
+#include "base/basictypes.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/gtest/include/gtest/gtest.h"
+using ::testing::_;
+using ::testing::Gt;
+using ::testing::NotNull;
+using ::testing::SaveArg;
+using ::testing::StrNe;
+
namespace media {
-// |kEncryptedData| is encrypted from |kOriginalData| using |kRightKey|, whose
-// length is |kKeySize|. Modifying any of these independently would fail the
-// test.
-static const char kOriginalData[] = "Original data.";
-static const int kEncryptedDataSize = 16;
-static const unsigned char kEncryptedData[] =
- "\x82\x3A\x76\x92\xEC\x7F\xF8\x85\xEC\x23\x52\xFB\x19\xB1\xB9\x09";
-static const int kKeySize = 16;
-static const unsigned char kRightKey[] = "A wonderful key!";
-static const unsigned char kWrongKey[] = "I'm a wrong key.";
-static const int kKeyIdSize = 9;
-static const unsigned char kKeyId1[] = "Key ID 1.";
-static const unsigned char kKeyId2[] = "Key ID 2.";
+static const char kClearKeySystem[] = "org.w3.clearkey";
+static const uint8 kInitData[] = { 0x69, 0x6e, 0x69, 0x74 };
+// |kEncryptedData| is encrypted from |kOriginalData| using |kRightKey|.
+// Modifying any of these independently would fail the test.
+static const uint8 kOriginalData[] = {
+ 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c,
+ 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e
+};
+static const uint8 kEncryptedData[] = {
+ 0x82, 0x3A, 0x76, 0x92, 0xEC, 0x7F, 0xF8, 0x85,
+ 0xEC, 0x23, 0x52, 0xFB, 0x19, 0xB1, 0xB9, 0x09
+};
+static const uint8 kRightKey[] = {
+ 0x41, 0x20, 0x77, 0x6f, 0x6e, 0x64, 0x65, 0x72,
+ 0x66, 0x75, 0x6c, 0x20, 0x6b, 0x65, 0x79, 0x21
+};
+static const uint8 kWrongKey[] = {
+ 0x49, 0x27, 0x6d, 0x20, 0x61, 0x20, 0x77, 0x72,
+ 0x6f, 0x6e, 0x67, 0x20, 0x6b, 0x65, 0x79, 0x2e
+};
+static const uint8 kWrongSizedKey[] = { 0x20, 0x20 };
+static const uint8 kKeyId1[] = {
+ 0x4b, 0x65, 0x79, 0x20, 0x49, 0x44, 0x20, 0x31
+};
+static const uint8 kKeyId2[] = {
+ 0x4b, 0x65, 0x79, 0x20, 0x49, 0x44, 0x20, 0x32
+};
class AesDecryptorTest : public testing::Test {
public:
- AesDecryptorTest() {
- encrypted_data_ = DecoderBuffer::CopyFrom(
- kEncryptedData, kEncryptedDataSize);
+ AesDecryptorTest() : decryptor_(&client_) {
+ encrypted_data_ = DecoderBuffer::CopyFrom(kEncryptedData,
+ arraysize(kEncryptedData));
}
protected:
- void SetKeyIdForEncryptedData(const uint8* key_id, int key_id_size) {
+ void GenerateKeyRequest() {
+ EXPECT_CALL(client_, KeyMessageMock(kClearKeySystem, StrNe(std::string()),
+ NotNull(), Gt(0), ""))
+ .WillOnce(SaveArg<1>(&session_id_string_));
+ decryptor_.GenerateKeyRequest(kClearKeySystem,
+ kInitData, arraysize(kInitData));
+ }
+
+ template <int KeyIdSize, int KeySize>
+ void AddKeyAndExpectToSucceed(const uint8 (&key_id)[KeyIdSize],
+ const uint8 (&key)[KeySize]) {
+ EXPECT_CALL(client_, KeyAdded(kClearKeySystem, session_id_string_));
+ decryptor_.AddKey(kClearKeySystem, key, KeySize, key_id, KeyIdSize,
+ session_id_string_);
+ }
+
+ template <int KeyIdSize, int KeySize>
+ void AddKeyAndExpectToFail(const uint8 (&key_id)[KeyIdSize],
+ const uint8 (&key)[KeySize]) {
+ EXPECT_CALL(client_, KeyError(kClearKeySystem, session_id_string_,
+ AesDecryptor::kUnknownError, 0));
+ decryptor_.AddKey(kClearKeySystem, key, KeySize, key_id, KeyIdSize,
+ session_id_string_);
+ }
+
+ template <int KeyIdSize>
+ void SetKeyIdForEncryptedData(const uint8 (&key_id)[KeyIdSize]) {
encrypted_data_->SetDecryptConfig(
- scoped_ptr<DecryptConfig>(new DecryptConfig(key_id, key_id_size)));
+ scoped_ptr<DecryptConfig>(new DecryptConfig(key_id, KeyIdSize)));
}
void DecryptAndExpectToSucceed() {
scoped_refptr<DecoderBuffer> decrypted =
decryptor_.Decrypt(encrypted_data_);
ASSERT_TRUE(decrypted);
- int data_length = sizeof(kOriginalData) - 1;
+ int data_length = sizeof(kOriginalData);
ASSERT_EQ(data_length, decrypted->GetDataSize());
EXPECT_EQ(0, memcmp(kOriginalData, decrypted->GetData(), data_length));
}
@@ -54,34 +102,45 @@ class AesDecryptorTest : public testing::Test {
}
scoped_refptr<DecoderBuffer> encrypted_data_;
+ MockDecryptorClient client_;
AesDecryptor decryptor_;
+ std::string session_id_string_;
};
TEST_F(AesDecryptorTest, NormalDecryption) {
- decryptor_.AddKey(kKeyId1, kKeyIdSize, kRightKey, kKeySize);
- SetKeyIdForEncryptedData(kKeyId1, kKeyIdSize);
+ GenerateKeyRequest();
+ AddKeyAndExpectToSucceed(kKeyId1, kRightKey);
+ SetKeyIdForEncryptedData(kKeyId1);
ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed());
}
TEST_F(AesDecryptorTest, WrongKey) {
- decryptor_.AddKey(kKeyId1, kKeyIdSize, kWrongKey, kKeySize);
- SetKeyIdForEncryptedData(kKeyId1, kKeyIdSize);
+ GenerateKeyRequest();
+ AddKeyAndExpectToSucceed(kKeyId1, kWrongKey);
+ SetKeyIdForEncryptedData(kKeyId1);
ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail());
}
TEST_F(AesDecryptorTest, MultipleKeys) {
- decryptor_.AddKey(kKeyId1, kKeyIdSize, kRightKey, kKeySize);
- decryptor_.AddKey(kKeyId2, kKeyIdSize, kWrongKey, kKeySize);
- SetKeyIdForEncryptedData(kKeyId1, kKeyIdSize);
+ GenerateKeyRequest();
+ AddKeyAndExpectToSucceed(kKeyId1, kRightKey);
+ AddKeyAndExpectToSucceed(kKeyId2, kWrongKey);
+ SetKeyIdForEncryptedData(kKeyId1);
ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed());
}
TEST_F(AesDecryptorTest, KeyReplacement) {
- SetKeyIdForEncryptedData(kKeyId1, kKeyIdSize);
- decryptor_.AddKey(kKeyId1, kKeyIdSize, kWrongKey, kKeySize);
+ GenerateKeyRequest();
+ SetKeyIdForEncryptedData(kKeyId1);
+ AddKeyAndExpectToSucceed(kKeyId1, kWrongKey);
ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail());
- decryptor_.AddKey(kKeyId1, kKeyIdSize, kRightKey, kKeySize);
+ AddKeyAndExpectToSucceed(kKeyId1, kRightKey);
ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed());
}
+TEST_F(AesDecryptorTest, WrongSizedKey) {
+ GenerateKeyRequest();
+ AddKeyAndExpectToFail(kKeyId1, kWrongSizedKey);
+}
+
} // media
diff --git a/media/crypto/decryptor_client.h b/media/crypto/decryptor_client.h
new file mode 100644
index 0000000..c15a2f0
--- /dev/null
+++ b/media/crypto/decryptor_client.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 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.
+
+#ifndef MEDIA_CRYPTO_DECRYPTOR_CLIENT_H_
+#define MEDIA_CRYPTO_DECRYPTOR_CLIENT_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "media/crypto/aes_decryptor.h"
+
+namespace media {
+
+// Interface used by a decryptor to fire key events.
+// See: http://dvcs.w3.org/hg/html-media/raw-file/tip/encrypted-media/encrypted-media.html#event-summary
+class DecryptorClient {
+ public:
+ // Signals that a key has been added.
+ virtual void KeyAdded(const std::string& key_system,
+ const std::string& session_id) = 0;
+
+ // Signals that a key error occurred. The |system_code| is key
+ // system-dependent. For clear key system, the |system_code| is always zero.
+ virtual void KeyError(const std::string& key_system,
+ const std::string& session_id,
+ AesDecryptor::KeyError error_code,
+ int system_code) = 0;
+
+ // Signals that a key message has been generated.
+ virtual void KeyMessage(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> message,
+ int message_length,
+ const std::string& default_url) = 0;
+
+ // Signals that a key is needed for decryption. |key_system| and |session_id|
+ // can be empty if the key system has not been selected.
+ virtual void NeedKey(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> init_data,
+ int init_data_length) = 0;
+
+ protected:
+ virtual ~DecryptorClient() {}
+};
+
+} // namespace media
+
+#endif // MEDIA_CRYPTO_DECRYPTOR_CLIENT_H_
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index 2cc1bbc..1c87590 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -605,7 +605,7 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id,
has_audio, has_video),
audio_cb,
video_cb,
- base::Bind(&ChunkDemuxer::OnKeyNeeded, base::Unretained(this)),
+ base::Bind(&ChunkDemuxer::OnNeedKey, base::Unretained(this)),
base::Bind(&ChunkDemuxer::OnNewMediaSegment, base::Unretained(this), id));
stream_parser_map_[id] = stream_parser.release();
@@ -1017,9 +1017,9 @@ bool ChunkDemuxer::OnVideoBuffers(const StreamParser::BufferQueue& buffers) {
return video_->Append(buffers);
}
-bool ChunkDemuxer::OnKeyNeeded(scoped_array<uint8> init_data,
- int init_data_size) {
- client_->KeyNeeded(init_data.Pass(), init_data_size);
+bool ChunkDemuxer::OnNeedKey(scoped_array<uint8> init_data,
+ int init_data_size) {
+ client_->DemuxerNeedKey(init_data.Pass(), init_data_size);
return true;
}
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h
index 5945526..a03d50f 100644
--- a/media/filters/chunk_demuxer.h
+++ b/media/filters/chunk_demuxer.h
@@ -117,7 +117,7 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
const VideoDecoderConfig& video_config);
bool OnAudioBuffers(const StreamParser::BufferQueue& buffers);
bool OnVideoBuffers(const StreamParser::BufferQueue& buffers);
- bool OnKeyNeeded(scoped_array<uint8> init_data, int init_data_size);
+ bool OnNeedKey(scoped_array<uint8> init_data, int init_data_size);
void OnNewMediaSegment(const std::string& source_id,
base::TimeDelta start_timestamp);
diff --git a/media/filters/chunk_demuxer_client.h b/media/filters/chunk_demuxer_client.h
index 3fbe6e3..4ac2f78 100644
--- a/media/filters/chunk_demuxer_client.h
+++ b/media/filters/chunk_demuxer_client.h
@@ -25,7 +25,11 @@ class ChunkDemuxerClient {
// A decryption key associated with |init_data| may be needed to decrypt the
// media being demuxed before decoding. Note that the demuxing itself does not
// need decryption.
- virtual void KeyNeeded(scoped_array<uint8> init_data, int init_data_size) = 0;
+ virtual void DemuxerNeedKey(scoped_array<uint8> init_data,
+ int init_data_size) = 0;
+
+ protected:
+ virtual ~ChunkDemuxerClient() {}
};
} // namespace media
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index 941bbcb..d55dff4 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -90,11 +90,12 @@ class MockChunkDemuxerClient : public ChunkDemuxerClient {
MOCK_METHOD1(DemuxerOpened, void(ChunkDemuxer* demuxer));
MOCK_METHOD0(DemuxerClosed, void());
// TODO(xhwang): This is a workaround of the issue that move-only parameters
- // are not supported in mocked methods. Remove this when the issue is fixed.
- // See http://code.google.com/p/googletest/issues/detail?id=395
- MOCK_METHOD2(KeyNeededMock, void(const uint8* init_data, int init_data_size));
- void KeyNeeded(scoped_array<uint8> init_data, int init_data_size) {
- KeyNeededMock(init_data.get(), init_data_size);
+ // are not supported in mocked methods. Remove this when the issue is fixed
+ // (http://code.google.com/p/googletest/issues/detail?id=395) or when we use
+ // std::string instead of scoped_array<uint8> (http://crbug.com/130689).
+ MOCK_METHOD2(NeedKeyMock, void(const uint8* init_data, int init_data_size));
+ void DemuxerNeedKey(scoped_array<uint8> init_data, int init_data_size) {
+ NeedKeyMock(init_data.get(), init_data_size);
}
private:
@@ -590,7 +591,7 @@ TEST_F(ChunkDemuxerTest, TestInit) {
client_.reset(new MockChunkDemuxerClient());
demuxer_ = new ChunkDemuxer(client_.get());
if (has_video && video_content_encoded)
- EXPECT_CALL(*client_, KeyNeededMock(NotNull(), 16));
+ EXPECT_CALL(*client_, NeedKeyMock(NotNull(), 16));
ASSERT_TRUE(InitDemuxer(has_audio, has_video, video_content_encoded));
@@ -896,7 +897,7 @@ TEST_F(ChunkDemuxerTest, TestNetworkErrorEndOfStream) {
// Read() behavior.
class EndOfStreamHelper {
public:
- EndOfStreamHelper(const scoped_refptr<Demuxer> demuxer)
+ explicit EndOfStreamHelper(const scoped_refptr<Demuxer> demuxer)
: demuxer_(demuxer),
audio_read_done_(false),
video_read_done_(false) {
diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc
index 2b4fc94..d2e6908 100644
--- a/media/filters/ffmpeg_video_decoder_unittest.cc
+++ b/media/filters/ffmpeg_video_decoder_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include <deque>
+#include <string>
#include "base/bind.h"
#include "base/message_loop.h"
@@ -24,10 +25,13 @@
using ::testing::_;
using ::testing::AnyNumber;
+using ::testing::Gt;
using ::testing::Invoke;
+using ::testing::NotNull;
using ::testing::ReturnRef;
using ::testing::SaveArg;
using ::testing::StrictMock;
+using ::testing::StrNe;
namespace media {
@@ -37,9 +41,17 @@ static const gfx::Rect kVisibleRect(320, 240);
static const gfx::Size kNaturalSize(522, 288);
static const AVRational kFrameRate = { 100, 1 };
static const AVRational kAspectRatio = { 1, 1 };
-static const unsigned char kRawKey[] = "A wonderful key!";
-static const unsigned char kWrongKey[] = "I'm a wrong key.";
-static const unsigned char kKeyId[] = "A normal key ID.";
+static const char kClearKeySystem[] = "org.w3.clearkey";
+static const uint8 kInitData[] = { 0x69, 0x6e, 0x69, 0x74 };
+static const uint8 kRightKey[] = {
+ 0x41, 0x20, 0x77, 0x6f, 0x6e, 0x64, 0x65, 0x72,
+ 0x66, 0x75, 0x6c, 0x20, 0x6b, 0x65, 0x79, 0x21
+};
+static const uint8 kWrongKey[] = {
+ 0x49, 0x27, 0x6d, 0x20, 0x61, 0x20, 0x77, 0x72,
+ 0x6f, 0x6e, 0x67, 0x20, 0x6b, 0x65, 0x79, 0x2e
+};
+static const uint8 kKeyId[] = { 0x4b, 0x65, 0x79, 0x20, 0x49, 0x44 };
ACTION_P(ReturnBuffer, buffer) {
arg0.Run(buffer);
@@ -48,7 +60,7 @@ ACTION_P(ReturnBuffer, buffer) {
class FFmpegVideoDecoderTest : public testing::Test {
public:
FFmpegVideoDecoderTest()
- : decryptor_(new AesDecryptor()),
+ : decryptor_(new AesDecryptor(&decryptor_client_)),
decoder_(new FFmpegVideoDecoder(base::Bind(&Identity<MessageLoop*>,
&message_loop_))),
demuxer_(new StrictMock<MockDemuxerStream>()),
@@ -202,6 +214,7 @@ class FFmpegVideoDecoderTest : public testing::Test {
scoped_refptr<StrictMock<MockDemuxerStream> > demuxer_;
MockStatisticsCB statistics_cb_;
VideoDecoderConfig config_;
+ MockDecryptorClient decryptor_client_;
VideoDecoder::ReadCB read_cb_;
@@ -375,12 +388,20 @@ TEST_F(FFmpegVideoDecoderTest, DecodeFrame_SmallerHeight) {
TEST_F(FFmpegVideoDecoderTest, DecodeEncryptedFrame_Normal) {
Initialize();
- decryptor_->AddKey(kKeyId, arraysize(kKeyId) - 1,
- kRawKey, arraysize(kRawKey) - 1);
+ std::string sessing_id_string;
+ EXPECT_CALL(decryptor_client_,
+ KeyMessageMock(kClearKeySystem, StrNe(std::string()),
+ NotNull(), Gt(0), ""))
+ .WillOnce(SaveArg<1>(&sessing_id_string));
+ decryptor_->GenerateKeyRequest(kClearKeySystem,
+ kInitData, arraysize(kInitData));
+ EXPECT_CALL(decryptor_client_, KeyAdded(kClearKeySystem, sessing_id_string));
+ decryptor_->AddKey(kClearKeySystem, kRightKey, arraysize(kRightKey),
+ kKeyId, arraysize(kKeyId), sessing_id_string);
// Simulate decoding a single encrypted frame.
encrypted_i_frame_buffer_->SetDecryptConfig(scoped_ptr<DecryptConfig>(
- new DecryptConfig(kKeyId, arraysize(kKeyId) - 1)));
+ new DecryptConfig(kKeyId, arraysize(kKeyId))));
VideoDecoder::DecoderStatus status;
scoped_refptr<VideoFrame> video_frame;
DecodeSingleFrame(encrypted_i_frame_buffer_, &status, &video_frame);
@@ -396,7 +417,7 @@ TEST_F(FFmpegVideoDecoderTest, DecodeEncryptedFrame_NoKey) {
// Simulate decoding a single encrypted frame.
encrypted_i_frame_buffer_->SetDecryptConfig(scoped_ptr<DecryptConfig>(
- new DecryptConfig(kKeyId, arraysize(kKeyId) - 1)));
+ new DecryptConfig(kKeyId, arraysize(kKeyId))));
EXPECT_CALL(*demuxer_, Read(_))
.WillRepeatedly(ReturnBuffer(encrypted_i_frame_buffer_));
@@ -415,11 +436,12 @@ TEST_F(FFmpegVideoDecoderTest, DecodeEncryptedFrame_NoKey) {
// Test decrypting an encrypted frame with a wrong key.
TEST_F(FFmpegVideoDecoderTest, DecodeEncryptedFrame_WrongKey) {
Initialize();
- decryptor_->AddKey(kKeyId, arraysize(kKeyId) - 1,
- kWrongKey, arraysize(kWrongKey) - 1);
+ EXPECT_CALL(decryptor_client_, KeyAdded("", ""));
+ decryptor_->AddKey("", kWrongKey, arraysize(kWrongKey),
+ kKeyId, arraysize(kKeyId), "");
encrypted_i_frame_buffer_->SetDecryptConfig(scoped_ptr<DecryptConfig>(
- new DecryptConfig(kKeyId, arraysize(kKeyId) - 1)));
+ new DecryptConfig(kKeyId, arraysize(kKeyId))));
EXPECT_CALL(*demuxer_, Read(_))
.WillRepeatedly(ReturnBuffer(encrypted_i_frame_buffer_));
diff --git a/media/filters/pipeline_integration_test.cc b/media/filters/pipeline_integration_test.cc
index 2ad7b2a..c0a5b67 100644
--- a/media/filters/pipeline_integration_test.cc
+++ b/media/filters/pipeline_integration_test.cc
@@ -6,16 +6,17 @@
#include "base/bind.h"
#include "media/base/decoder_buffer.h"
+#include "media/base/mock_filters.h"
#include "media/base/test_data_util.h"
+#include "media/crypto/aes_decryptor.h"
+#include "media/crypto/decryptor_client.h"
#include "media/filters/chunk_demuxer_client.h"
namespace media {
-// Key ID of the video track in test file "bear-320x240-encrypted.webm".
-static const unsigned char kKeyId[] =
- "\x11\xa5\x18\x37\xc4\x73\x84\x03\xe5\xe6\x57\xed\x8e\x06\xd9\x7c";
-
-static const char* kSourceId = "SourceId";
+static const char kSourceId[] = "SourceId";
+static const char kClearKeySystem[] = "org.w3.clearkey";
+static const uint8 kInitData[] = { 0x69, 0x6e, 0x69, 0x74 };
// Helper class that emulates calls made on the ChunkDemuxer by the
// Media Source API.
@@ -36,11 +37,8 @@ class MockMediaSource : public ChunkDemuxerClient {
virtual ~MockMediaSource() {}
- void set_decryptor(AesDecryptor* decryptor) {
- decryptor_ = decryptor;
- }
- AesDecryptor* decryptor() const {
- return decryptor_;
+ void set_decryptor_client(DecryptorClient* decryptor_client) {
+ decryptor_client_ = decryptor_client;
}
const std::string& url() const { return url_; }
@@ -95,14 +93,12 @@ class MockMediaSource : public ChunkDemuxerClient {
chunk_demuxer_ = NULL;
}
- virtual void KeyNeeded(scoped_array<uint8> init_data, int init_data_size) {
+ virtual void DemuxerNeedKey(scoped_array<uint8> init_data,
+ int init_data_size) {
DCHECK(init_data.get());
DCHECK_EQ(init_data_size, 16);
- DCHECK(decryptor());
- // In test file bear-320x240-encrypted.webm, the decryption key is equal to
- // |init_data|.
- decryptor()->AddKey(init_data.get(), init_data_size,
- init_data.get(), init_data_size);
+ DCHECK(decryptor_client_);
+ decryptor_client_->NeedKey("", "", init_data.Pass(), init_data_size);
}
private:
@@ -113,22 +109,103 @@ class MockMediaSource : public ChunkDemuxerClient {
bool has_audio_;
bool has_video_;
scoped_refptr<ChunkDemuxer> chunk_demuxer_;
- AesDecryptor* decryptor_;
+ DecryptorClient* decryptor_client_;
+};
+
+class MockDecryptorClientImpl : public DecryptorClient {
+ public:
+ MockDecryptorClientImpl() : decryptor_(this) {}
+
+ AesDecryptor* decryptor() {
+ return &decryptor_;
+ }
+
+ // DecryptorClient implementation.
+ virtual void KeyAdded(const std::string& key_system,
+ const std::string& session_id) {
+ EXPECT_EQ(kClearKeySystem, key_system);
+ EXPECT_FALSE(session_id.empty());
+ }
+
+ virtual void KeyError(const std::string& key_system,
+ const std::string& session_id,
+ AesDecryptor::KeyError error_code,
+ int system_code) {
+ NOTIMPLEMENTED();
+ }
+
+ virtual void KeyMessage(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> message,
+ int message_length,
+ const std::string& default_url) {
+ EXPECT_EQ(kClearKeySystem, key_system);
+ EXPECT_FALSE(session_id.empty());
+ EXPECT_TRUE(message.get());
+ EXPECT_GT(message_length, 0);
+
+ current_key_system_ = key_system;
+ current_session_id_ = session_id;
+ }
+
+ virtual void NeedKey(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> init_data,
+ int init_data_length) {
+ current_key_system_ = key_system;
+ current_session_id_ = session_id;
+
+ // When NeedKey is called from the demuxer, the |key_system| will be empty.
+ // In this case, we need to call GenerateKeyRequest() to initialize a
+ // session (which will call KeyMessage).
+ if (current_key_system_.empty()) {
+ DCHECK(current_session_id_.empty());
+ decryptor_.GenerateKeyRequest(kClearKeySystem,
+ kInitData, arraysize(kInitData));
+ }
+
+ EXPECT_FALSE(current_key_system_.empty());
+ EXPECT_FALSE(current_session_id_.empty());
+ // In test file bear-320x240-encrypted.webm, the decryption key is equal to
+ // |init_data|.
+ decryptor_.AddKey(current_key_system_, init_data.get(), init_data_length,
+ init_data.get(), init_data_length, current_session_id_);
+ }
+
+ private:
+ AesDecryptor decryptor_;
+ std::string current_key_system_;
+ std::string current_session_id_;
};
class PipelineIntegrationTest
: public testing::Test,
public PipelineIntegrationTestBase {
public:
- void StartPipelineWithMediaSource(MockMediaSource& source) {
+ void StartPipelineWithMediaSource(MockMediaSource* source) {
+ pipeline_->Start(
+ CreateFilterCollection(source),
+ base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)),
+ base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)),
+ QuitOnStatusCB(PIPELINE_OK));
+
+ ASSERT_TRUE(decoder_.get());
+
+ message_loop_.Run();
+ }
+
+ void StartPipelineWithEncryptedMedia(
+ MockMediaSource* source,
+ MockDecryptorClientImpl* encrypted_media) {
pipeline_->Start(
- CreateFilterCollection(&source),
+ CreateFilterCollection(source),
base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)),
base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)),
QuitOnStatusCB(PIPELINE_OK));
ASSERT_TRUE(decoder_.get());
- source.set_decryptor(decryptor_.get());
+ decoder_->set_decryptor(encrypted_media->decryptor());
+ source->set_decryptor_client(encrypted_media);
message_loop_.Run();
}
@@ -145,7 +222,7 @@ class PipelineIntegrationTest
bool has_audio,
bool has_video) {
MockMediaSource source(filename, initial_append_size, has_audio, has_video);
- StartPipelineWithMediaSource(source);
+ StartPipelineWithMediaSource(&source);
if (pipeline_status_ != PIPELINE_OK)
return false;
@@ -188,7 +265,8 @@ TEST_F(PipelineIntegrationTest, BasicPlaybackHashed) {
TEST_F(PipelineIntegrationTest, EncryptedPlayback) {
MockMediaSource source("bear-320x240-encrypted.webm", 219726, true, true);
- StartPipelineWithMediaSource(source);
+ MockDecryptorClientImpl encrypted_media;
+ StartPipelineWithEncryptedMedia(&source, &encrypted_media);
source.EndOfStream();
ASSERT_EQ(PIPELINE_OK, pipeline_status_);
diff --git a/media/filters/pipeline_integration_test_base.cc b/media/filters/pipeline_integration_test_base.cc
index 565d3b7..13e596b 100644
--- a/media/filters/pipeline_integration_test_base.cc
+++ b/media/filters/pipeline_integration_test_base.cc
@@ -6,7 +6,6 @@
#include "base/bind.h"
#include "media/base/media_log.h"
-#include "media/crypto/aes_decryptor.h"
#include "media/filters/audio_renderer_impl.h"
#include "media/filters/chunk_demuxer.h"
#include "media/filters/ffmpeg_audio_decoder.h"
@@ -186,7 +185,6 @@ PipelineIntegrationTestBase::CreateFilterCollection(
const scoped_refptr<Demuxer>& demuxer) {
scoped_ptr<FilterCollection> collection(new FilterCollection());
collection->SetDemuxer(demuxer);
- decryptor_.reset(new AesDecryptor());
collection->AddAudioDecoder(new FFmpegAudioDecoder(
base::Bind(&MessageLoopFactory::GetMessageLoop,
base::Unretained(message_loop_factory_.get()),
@@ -195,7 +193,6 @@ PipelineIntegrationTestBase::CreateFilterCollection(
base::Bind(&MessageLoopFactory::GetMessageLoop,
base::Unretained(message_loop_factory_.get()),
"VideoDecoderThread"));
- decoder_->set_decryptor(decryptor_.get());
collection->AddVideoDecoder(decoder_);
// Disable frame dropping if hashing is enabled.
renderer_ = new VideoRendererBase(
diff --git a/media/filters/pipeline_integration_test_base.h b/media/filters/pipeline_integration_test_base.h
index 4fcb33b..1268b39 100644
--- a/media/filters/pipeline_integration_test_base.h
+++ b/media/filters/pipeline_integration_test_base.h
@@ -18,8 +18,6 @@
namespace media {
-class AesDecryptor;
-
// Empty MD5 hash string. Used to verify empty audio or video tracks.
extern const char kNullHash[];
@@ -74,7 +72,6 @@ class PipelineIntegrationTestBase {
bool hashing_enabled_;
scoped_ptr<MessageLoopFactory> message_loop_factory_;
scoped_refptr<Pipeline> pipeline_;
- scoped_ptr<AesDecryptor> decryptor_;
scoped_refptr<FFmpegVideoDecoder> decoder_;
scoped_refptr<VideoRendererBase> renderer_;
scoped_refptr<NullAudioSink> audio_sink_;
diff --git a/media/media.gyp b/media/media.gyp
index 22c6ca0..51e7215 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -196,6 +196,7 @@
'base/video_util.h',
'crypto/aes_decryptor.cc',
'crypto/aes_decryptor.h',
+ 'crypto/decryptor_client.h',
'ffmpeg/ffmpeg_common.cc',
'ffmpeg/ffmpeg_common.h',
'ffmpeg/file_protocol.cc',
diff --git a/media/mp4/mp4_stream_parser.cc b/media/mp4/mp4_stream_parser.cc
index 3d6a190..cf09f49 100644
--- a/media/mp4/mp4_stream_parser.cc
+++ b/media/mp4/mp4_stream_parser.cc
@@ -31,21 +31,21 @@ void MP4StreamParser::Init(const InitCB& init_cb,
const NewConfigCB& config_cb,
const NewBuffersCB& audio_cb,
const NewBuffersCB& video_cb,
- const KeyNeededCB& key_needed_cb,
+ const NeedKeyCB& need_key_cb,
const NewMediaSegmentCB& new_segment_cb) {
DCHECK_EQ(state_, kWaitingForInit);
DCHECK(init_cb_.is_null());
DCHECK(!init_cb.is_null());
DCHECK(!config_cb.is_null());
DCHECK(!audio_cb.is_null() || !video_cb.is_null());
- DCHECK(!key_needed_cb.is_null());
+ DCHECK(!need_key_cb.is_null());
ChangeState(kParsingBoxes);
init_cb_ = init_cb;
config_cb_ = config_cb;
audio_cb_ = audio_cb;
video_cb_ = video_cb;
- key_needed_cb_ = key_needed_cb;
+ need_key_cb_ = need_key_cb;
new_segment_cb_ = new_segment_cb;
}
diff --git a/media/mp4/mp4_stream_parser.h b/media/mp4/mp4_stream_parser.h
index 269b9eb0..5d0d0f9 100644
--- a/media/mp4/mp4_stream_parser.h
+++ b/media/mp4/mp4_stream_parser.h
@@ -28,7 +28,7 @@ class MEDIA_EXPORT MP4StreamParser : public StreamParser {
virtual void Init(const InitCB& init_cb, const NewConfigCB& config_cb,
const NewBuffersCB& audio_cb,
const NewBuffersCB& video_cb,
- const KeyNeededCB& key_needed_cb,
+ const NeedKeyCB& need_key_cb,
const NewMediaSegmentCB& new_segment_cb) OVERRIDE;
virtual void Flush() OVERRIDE;
virtual bool Parse(const uint8* buf, int size) OVERRIDE;
@@ -59,7 +59,7 @@ class MEDIA_EXPORT MP4StreamParser : public StreamParser {
NewConfigCB config_cb_;
NewBuffersCB audio_cb_;
NewBuffersCB video_cb_;
- KeyNeededCB key_needed_cb_;
+ NeedKeyCB need_key_cb_;
NewMediaSegmentCB new_segment_cb_;
OffsetByteQueue queue_;
diff --git a/media/webm/webm_stream_parser.cc b/media/webm/webm_stream_parser.cc
index b063a26..38c5a7e 100644
--- a/media/webm/webm_stream_parser.cc
+++ b/media/webm/webm_stream_parser.cc
@@ -191,14 +191,14 @@ void WebMStreamParser::Init(const InitCB& init_cb,
const NewConfigCB& config_cb,
const NewBuffersCB& audio_cb,
const NewBuffersCB& video_cb,
- const KeyNeededCB& key_needed_cb,
+ const NeedKeyCB& need_key_cb,
const NewMediaSegmentCB& new_segment_cb) {
DCHECK_EQ(state_, kWaitingForInit);
DCHECK(init_cb_.is_null());
DCHECK(!init_cb.is_null());
DCHECK(!config_cb.is_null());
DCHECK(!audio_cb.is_null() || !video_cb.is_null());
- DCHECK(!key_needed_cb.is_null());
+ DCHECK(!need_key_cb.is_null());
DCHECK(!new_segment_cb.is_null());
ChangeState(kParsingHeaders);
@@ -206,7 +206,7 @@ void WebMStreamParser::Init(const InitCB& init_cb,
config_cb_ = config_cb;
audio_cb_ = audio_cb;
video_cb_ = video_cb;
- key_needed_cb_ = key_needed_cb;
+ need_key_cb_ = need_key_cb;
new_segment_cb_ = new_segment_cb;
}
@@ -354,7 +354,7 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) {
CHECK_GT(key_id_size, 0);
scoped_array<uint8> key_id(new uint8[key_id_size]);
memcpy(key_id.get(), tracks_parser.video_encryption_key_id(), key_id_size);
- key_needed_cb_.Run(key_id.Pass(), key_id_size);
+ need_key_cb_.Run(key_id.Pass(), key_id_size);
}
cluster_parser_.reset(new WebMClusterParser(
diff --git a/media/webm/webm_stream_parser.h b/media/webm/webm_stream_parser.h
index 003171e..f555278 100644
--- a/media/webm/webm_stream_parser.h
+++ b/media/webm/webm_stream_parser.h
@@ -25,7 +25,7 @@ class WebMStreamParser : public StreamParser {
virtual void Init(const InitCB& init_cb, const NewConfigCB& config_cb,
const NewBuffersCB& audio_cb,
const NewBuffersCB& video_cb,
- const KeyNeededCB& key_needed_cb,
+ const NeedKeyCB& need_key_cb,
const NewMediaSegmentCB& new_segment_cb) OVERRIDE;
virtual void Flush() OVERRIDE;
virtual bool Parse(const uint8* buf, int size) OVERRIDE;
@@ -64,7 +64,7 @@ class WebMStreamParser : public StreamParser {
NewConfigCB config_cb_;
NewBuffersCB audio_cb_;
NewBuffersCB video_cb_;
- KeyNeededCB key_needed_cb_;
+ NeedKeyCB need_key_cb_;
NewMediaSegmentCB new_segment_cb_;
// True if a new cluster id has been seen, but no audio or video buffers have
diff --git a/webkit/media/webmediaplayer_impl.cc b/webkit/media/webmediaplayer_impl.cc
index bc7631d..588801c 100644
--- a/webkit/media/webmediaplayer_impl.cc
+++ b/webkit/media/webmediaplayer_impl.cc
@@ -6,6 +6,7 @@
#include <limits>
#include <string>
+#include <vector>
#include "base/bind.h"
#include "base/callback.h"
@@ -154,7 +155,7 @@ WebMediaPlayerImpl::WebMediaPlayerImpl(
filter_collection_->AddAudioRenderer(
new media::AudioRendererImpl(new media::NullAudioSink()));
- decryptor_.reset(new media::AesDecryptor());
+ decryptor_.reset(new media::AesDecryptor(proxy_.get()));
}
WebMediaPlayerImpl::~WebMediaPlayerImpl() {
@@ -690,7 +691,7 @@ void WebMediaPlayerImpl::sourceEndOfStream(
DCHECK_EQ(main_loop_, MessageLoop::current());
media::PipelineStatus pipeline_status = media::PIPELINE_OK;
- switch(status) {
+ switch (status) {
case WebMediaPlayer::EndOfStreamStatusNoError:
break;
case WebMediaPlayer::EndOfStreamStatusNetworkError:
@@ -713,36 +714,12 @@ WebMediaPlayerImpl::generateKeyRequest(const WebString& key_system,
if (!IsSupportedKeySystem(key_system))
return WebKit::WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
- // Every request call creates a unique ID.
- // TODO(ddorwin): Move this to the CDM implementations since the CDMs may
- // create their own IDs and since CDMs supporting multiple renderer processes
- // need globally unique IDs.
- // Everything from here until the return should probably be handled by
- // the decryptor - see http://crbug.com/123260.
- static uint32_t next_available_session_id = 1;
- uint32_t session_id = next_available_session_id++;
-
- WebString session_id_string(base::UintToString16(session_id));
-
DVLOG(1) << "generateKeyRequest: " << key_system.utf8().data() << ": "
<< std::string(reinterpret_cast<const char*>(init_data),
- static_cast<size_t>(init_data_length))
- << " [" << session_id_string.utf8().data() << "]";
-
- // TODO(ddorwin): Generate a key request in the decryptor and fire
- // keyMessage when it completes.
- // For now, just fire the event with the init_data as the request.
- const unsigned char* message = init_data;
- unsigned message_length = init_data_length;
-
- MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
- &WebKit::WebMediaPlayerClient::keyMessage,
- base::Unretained(GetClient()),
- key_system,
- session_id_string,
- message,
- message_length));
+ static_cast<size_t>(init_data_length));
+ decryptor_->GenerateKeyRequest(key_system.utf8(),
+ init_data, init_data_length);
return WebKit::WebMediaPlayer::MediaKeyExceptionNoError;
}
@@ -759,7 +736,6 @@ WebKit::WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::addKey(
if (!IsSupportedKeySystem(key_system))
return WebKit::WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
-
DVLOG(1) << "addKey: " << key_system.utf8().data() << ": "
<< std::string(reinterpret_cast<const char*>(key),
static_cast<size_t>(key_length)) << ", "
@@ -767,38 +743,8 @@ WebKit::WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::addKey(
static_cast<size_t>(init_data_length))
<< " [" << session_id.utf8().data() << "]";
- // TODO(ddorwin): Everything from here until the return should probably be
- // handled by the decryptor - see http://crbug.com/123260.
- // Temporarily, fire an error for invalid key length so we can test the error
- // event and fire the keyAdded event in all other cases.
- const unsigned kSupportedKeyLength = 16; // 128-bit key.
- if (key_length != kSupportedKeyLength) {
- DLOG(ERROR) << "addKey: invalid key length: " << key_length;
- MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
- &WebKit::WebMediaPlayerClient::keyError,
- base::Unretained(GetClient()),
- key_system,
- session_id,
- WebKit::WebMediaPlayerClient::MediaKeyErrorCodeUnknown,
- 0));
- } else {
- // TODO(ddorwin): Fix the decryptor to accept no |init_data|. See
- // http://crbug.com/123265. Until then, ensure a non-empty value is passed.
- static const unsigned char kDummyInitData[1] = {0};
- if (!init_data) {
- init_data = kDummyInitData;
- init_data_length = arraysize(kDummyInitData);
- }
-
- decryptor_->AddKey(init_data, init_data_length, key, key_length);
-
- MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
- &WebKit::WebMediaPlayerClient::keyAdded,
- base::Unretained(GetClient()),
- key_system,
- session_id));
- }
-
+ decryptor_->AddKey(key_system.utf8(), key, key_length,
+ init_data, init_data_length, session_id.utf8());
return WebKit::WebMediaPlayer::MediaKeyExceptionNoError;
}
@@ -808,8 +754,7 @@ WebKit::WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::cancelKeyRequest(
if (!IsSupportedKeySystem(key_system))
return WebKit::WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
- // TODO(ddorwin): Cancel the key request in the decryptor.
-
+ decryptor_->CancelKeyRequest(key_system.utf8(), session_id.utf8());
return WebKit::WebMediaPlayer::MediaKeyExceptionNoError;
}
@@ -926,11 +871,50 @@ void WebMediaPlayerImpl::OnDemuxerOpened() {
GetClient()->sourceOpened();
}
-void WebMediaPlayerImpl::OnKeyNeeded(scoped_array<uint8> init_data,
- int init_data_size) {
+void WebMediaPlayerImpl::OnKeyAdded(const std::string& key_system,
+ const std::string& session_id) {
+ DCHECK_EQ(main_loop_, MessageLoop::current());
+
+ GetClient()->keyAdded(WebString::fromUTF8(key_system),
+ WebString::fromUTF8(session_id));
+}
+
+void WebMediaPlayerImpl::OnNeedKey(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> init_data,
+ int init_data_size) {
+ DCHECK_EQ(main_loop_, MessageLoop::current());
+
+ GetClient()->keyNeeded(WebString::fromUTF8(key_system),
+ WebString::fromUTF8(session_id),
+ init_data.get(),
+ init_data_size);
+}
+
+void WebMediaPlayerImpl::OnKeyError(const std::string& key_system,
+ const std::string& session_id,
+ media::AesDecryptor::KeyError error_code,
+ int system_code) {
+ DCHECK_EQ(main_loop_, MessageLoop::current());
+
+ GetClient()->keyError(
+ WebString::fromUTF8(key_system),
+ WebString::fromUTF8(session_id),
+ static_cast<WebKit::WebMediaPlayerClient::MediaKeyErrorCode>(error_code),
+ system_code);
+}
+
+void WebMediaPlayerImpl::OnKeyMessage(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> message,
+ int message_length,
+ const std::string& /* default_url */) {
DCHECK_EQ(main_loop_, MessageLoop::current());
- GetClient()->keyNeeded("", "", init_data.get(), init_data_size);
+ GetClient()->keyMessage(WebString::fromUTF8(key_system),
+ WebString::fromUTF8(session_id),
+ message.get(),
+ message_length);
}
void WebMediaPlayerImpl::SetOpaque(bool opaque) {
diff --git a/webkit/media/webmediaplayer_impl.h b/webkit/media/webmediaplayer_impl.h
index 6ef94b2..30d15f0 100644
--- a/webkit/media/webmediaplayer_impl.h
+++ b/webkit/media/webmediaplayer_impl.h
@@ -48,6 +48,8 @@
#ifndef WEBKIT_MEDIA_WEBMEDIAPLAYER_IMPL_H_
#define WEBKIT_MEDIA_WEBMEDIAPLAYER_IMPL_H_
+#include <string>
+
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
@@ -57,6 +59,7 @@
#include "media/base/filters.h"
#include "media/base/message_loop_factory.h"
#include "media/base/pipeline.h"
+#include "media/crypto/aes_decryptor.h"
#include "skia/ext/platform_canvas.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebAudioSourceProvider.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaPlayer.h"
@@ -213,7 +216,6 @@ class WebMediaPlayerImpl
const WebKit::WebString& key_system,
const WebKit::WebString& session_id);
-
// As we are closing the tab or even the browser, |main_loop_| is destroyed
// even before this object gets destructed, so we need to know when
// |main_loop_| is being destroyed and we can stop posting repaint task
@@ -227,7 +229,20 @@ class WebMediaPlayerImpl
void OnPipelineEnded(media::PipelineStatus status);
void OnPipelineError(media::PipelineStatus error);
void OnDemuxerOpened();
- void OnKeyNeeded(scoped_array<uint8> init_data, int init_data_size);
+ void OnKeyAdded(const std::string& key_system, const std::string& session_id);
+ void OnKeyError(const std::string& key_system,
+ const std::string& session_id,
+ media::AesDecryptor::KeyError error_code,
+ int system_code);
+ void OnKeyMessage(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> message,
+ int message_length,
+ const std::string& default_url);
+ void OnNeedKey(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> init_data,
+ int init_data_size);
void SetOpaque(bool);
private:
diff --git a/webkit/media/webmediaplayer_proxy.cc b/webkit/media/webmediaplayer_proxy.cc
index fdbd544..461f67e 100644
--- a/webkit/media/webmediaplayer_proxy.cc
+++ b/webkit/media/webmediaplayer_proxy.cc
@@ -177,10 +177,10 @@ void WebMediaPlayerProxy::DemuxerClosed() {
&WebMediaPlayerProxy::DemuxerClosedTask, this));
}
-void WebMediaPlayerProxy::KeyNeeded(scoped_array<uint8> init_data,
- int init_data_size) {
+void WebMediaPlayerProxy::DemuxerNeedKey(scoped_array<uint8> init_data,
+ int init_data_size) {
render_loop_->PostTask(FROM_HERE, base::Bind(
- &WebMediaPlayerProxy::KeyNeededTask, this,
+ &WebMediaPlayerProxy::NeedKeyTask, this, "", "",
base::Passed(&init_data), init_data_size));
}
@@ -237,11 +237,76 @@ void WebMediaPlayerProxy::DemuxerClosedTask() {
chunk_demuxer_ = NULL;
}
-void WebMediaPlayerProxy::KeyNeededTask(scoped_array<uint8> init_data,
- int init_data_size) {
+void WebMediaPlayerProxy::KeyAdded(const std::string& key_system,
+ const std::string& session_id) {
+ render_loop_->PostTask(FROM_HERE, base::Bind(
+ &WebMediaPlayerProxy::KeyAddedTask, this, key_system, session_id));
+}
+
+void WebMediaPlayerProxy::KeyError(const std::string& key_system,
+ const std::string& session_id,
+ media::AesDecryptor::KeyError error_code,
+ int system_code) {
+ render_loop_->PostTask(FROM_HERE, base::Bind(
+ &WebMediaPlayerProxy::KeyErrorTask, this, key_system, session_id,
+ error_code, system_code));
+}
+
+void WebMediaPlayerProxy::KeyMessage(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> message,
+ int message_length,
+ const std::string& default_url) {
+ render_loop_->PostTask(FROM_HERE, base::Bind(
+ &WebMediaPlayerProxy::KeyMessageTask, this, key_system, session_id,
+ base::Passed(&message), message_length, default_url));
+}
+
+void WebMediaPlayerProxy::NeedKey(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> init_data,
+ int init_data_size) {
+ render_loop_->PostTask(FROM_HERE, base::Bind(
+ &WebMediaPlayerProxy::NeedKeyTask, this, key_system, session_id,
+ base::Passed(&init_data), init_data_size));
+}
+
+void WebMediaPlayerProxy::KeyAddedTask(const std::string& key_system,
+ const std::string& session_id) {
+ DCHECK(render_loop_->BelongsToCurrentThread());
+ if (webmediaplayer_)
+ webmediaplayer_->OnKeyAdded(key_system, session_id);
+}
+
+void WebMediaPlayerProxy::KeyErrorTask(const std::string& key_system,
+ const std::string& session_id,
+ media::AesDecryptor::KeyError error_code,
+ int system_code) {
+ DCHECK(render_loop_->BelongsToCurrentThread());
+ if (webmediaplayer_)
+ webmediaplayer_->OnKeyError(key_system, session_id,
+ error_code, system_code);
+}
+
+void WebMediaPlayerProxy::KeyMessageTask(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> message,
+ int message_length,
+ const std::string& default_url) {
+ DCHECK(render_loop_->BelongsToCurrentThread());
+ if (webmediaplayer_)
+ webmediaplayer_->OnKeyMessage(key_system, session_id,
+ message.Pass(), message_length, default_url);
+}
+
+void WebMediaPlayerProxy::NeedKeyTask(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> init_data,
+ int init_data_size) {
DCHECK(render_loop_->BelongsToCurrentThread());
if (webmediaplayer_)
- webmediaplayer_->OnKeyNeeded(init_data.Pass(), init_data_size);
+ webmediaplayer_->OnNeedKey(key_system, session_id,
+ init_data.Pass(), init_data_size);
}
} // namespace webkit_media
diff --git a/webkit/media/webmediaplayer_proxy.h b/webkit/media/webmediaplayer_proxy.h
index f958f5d..2416388 100644
--- a/webkit/media/webmediaplayer_proxy.h
+++ b/webkit/media/webmediaplayer_proxy.h
@@ -12,6 +12,7 @@
#include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h"
#include "media/base/pipeline.h"
+#include "media/crypto/decryptor_client.h"
#include "media/filters/chunk_demuxer.h"
#include "media/filters/chunk_demuxer_client.h"
#include "media/filters/ffmpeg_video_decoder.h"
@@ -41,7 +42,8 @@ class WebMediaPlayerImpl;
// the render thread that WebMediaPlayerImpl is running on.
class WebMediaPlayerProxy
: public base::RefCountedThreadSafe<WebMediaPlayerProxy>,
- public media::ChunkDemuxerClient {
+ public media::ChunkDemuxerClient,
+ public media::DecryptorClient {
public:
WebMediaPlayerProxy(const scoped_refptr<base::MessageLoopProxy>& render_loop,
WebMediaPlayerImpl* webmediaplayer);
@@ -89,8 +91,8 @@ class WebMediaPlayerProxy
// ChunkDemuxerClient implementation.
virtual void DemuxerOpened(media::ChunkDemuxer* demuxer) OVERRIDE;
virtual void DemuxerClosed() OVERRIDE;
- virtual void KeyNeeded(scoped_array<uint8> init_data,
- int init_data_size) OVERRIDE;
+ virtual void DemuxerNeedKey(scoped_array<uint8> init_data,
+ int init_data_size) OVERRIDE;
// Methods for Demuxer communication.
void DemuxerStartWaitingForSeek();
@@ -105,9 +107,22 @@ class WebMediaPlayerProxy
void DemuxerEndOfStream(media::PipelineStatus status);
void DemuxerShutdown();
- void DemuxerOpenedTask(const scoped_refptr<media::ChunkDemuxer>& demuxer);
- void DemuxerClosedTask();
- void KeyNeededTask(scoped_array<uint8> init_data, int init_data_size);
+ // DecryptorClient implementation.
+ virtual void KeyAdded(const std::string& key_system,
+ const std::string& session_id) OVERRIDE;
+ virtual void KeyError(const std::string& key_system,
+ const std::string& session_id,
+ media::AesDecryptor::KeyError error_code,
+ int system_code) OVERRIDE;
+ virtual void KeyMessage(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> message,
+ int message_length,
+ const std::string& default_url) OVERRIDE;
+ virtual void NeedKey(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> init_data,
+ int init_data_size) OVERRIDE;
private:
friend class base::RefCountedThreadSafe<WebMediaPlayerProxy>;
@@ -132,6 +147,32 @@ class WebMediaPlayerProxy
// Inform |webmediaplayer_| whether the video content is opaque.
void SetOpaqueTask(bool opaque);
+ void DemuxerOpenedTask(const scoped_refptr<media::ChunkDemuxer>& demuxer);
+ void DemuxerClosedTask();
+
+ // Notify |webmediaplayer_| that a key has been added.
+ void KeyAddedTask(const std::string& key_system,
+ const std::string& session_id);
+
+ // Notify |webmediaplayer_| that a key error occurred.
+ void KeyErrorTask(const std::string& key_system,
+ const std::string& session_id,
+ media::AesDecryptor::KeyError error_code,
+ int system_code);
+
+ // Notify |webmediaplayer_| that a key message has been generated.
+ void KeyMessageTask(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> message,
+ int message_length,
+ const std::string& default_url);
+
+ // Notify |webmediaplayer_| that a key is needed for decryption.
+ void NeedKeyTask(const std::string& key_system,
+ const std::string& session_id,
+ scoped_array<uint8> init_data,
+ int init_data_size);
+
// The render message loop where WebKit lives.
scoped_refptr<base::MessageLoopProxy> render_loop_;
WebMediaPlayerImpl* webmediaplayer_;