summaryrefslogtreecommitdiffstats
path: root/media/cdm
diff options
context:
space:
mode:
authorjrummell@chromium.org <jrummell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-14 01:34:24 +0000
committerjrummell@chromium.org <jrummell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-14 01:34:24 +0000
commitdc45e793ab9b1d06b54225416c6cdbe2bc2d45c6 (patch)
tree9f51f7b2ed902e8445ced95e157f86f0931e0eb0 /media/cdm
parentf60a627cf5ce3c2234c1c66def822b58f686d7e2 (diff)
downloadchromium_src-dc45e793ab9b1d06b54225416c6cdbe2bc2d45c6.zip
chromium_src-dc45e793ab9b1d06b54225416c6cdbe2bc2d45c6.tar.gz
chromium_src-dc45e793ab9b1d06b54225416c6cdbe2bc2d45c6.tar.bz2
Add support for multiple MediaKey sessions
The draft EME spec supports multiple instances of MediaKey objects, each having 0 or more sessions (MediaKeySession). This change adds a way to uniquely identify each instance of MediaKeySession so that they can all coexist, and adds an additional callback SetSessionId() which is called whenever a new session is actually created by a CDM. Currently there is no way to correlate a created session with the original request. Change is to add a unique reference_id for each MediaKeySession, and use that to identify the session, rather that session_id (although session_id will still be available as a property via MediaKeySession). With session_id not being passed around, the new callback SetSessionId() is used when a session ID is assigned by the CDM. There are also changes to the way WebMediaPlayer works with prefixed EME to support the new internal interfaces. Not done is changes to the CDMs to support the reference_id. Currently it is handled by having the CDM wrapper keep track of a reference_id -> session_id mapping. BUG=308704 TEST=encrypted-media layout tests and browser_tests for encrypted media pass Review URL: https://codereview.chromium.org/26687006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@235008 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/cdm')
-rw-r--r--media/cdm/aes_decryptor.cc34
-rw-r--r--media/cdm/aes_decryptor.h17
-rw-r--r--media/cdm/aes_decryptor_unittest.cc75
-rw-r--r--media/cdm/ppapi/cdm_adapter.cc137
-rw-r--r--media/cdm/ppapi/cdm_adapter.h42
-rw-r--r--media/cdm/ppapi/cdm_wrapper.h207
-rw-r--r--media/cdm/ppapi/clear_key_cdm.cc74
-rw-r--r--media/cdm/ppapi/clear_key_cdm.h21
8 files changed, 413 insertions, 194 deletions
diff --git a/media/cdm/aes_decryptor.cc b/media/cdm/aes_decryptor.cc
index fe840ff3..7c5857c 100644
--- a/media/cdm/aes_decryptor.cc
+++ b/media/cdm/aes_decryptor.cc
@@ -259,17 +259,19 @@ static scoped_refptr<DecoderBuffer> DecryptData(const DecoderBuffer& input,
AesDecryptor::AesDecryptor(const KeyAddedCB& key_added_cb,
const KeyErrorCB& key_error_cb,
- const KeyMessageCB& key_message_cb)
+ const KeyMessageCB& key_message_cb,
+ const SetSessionIdCB& set_session_id_cb)
: key_added_cb_(key_added_cb),
key_error_cb_(key_error_cb),
- key_message_cb_(key_message_cb) {
-}
+ key_message_cb_(key_message_cb),
+ set_session_id_cb_(set_session_id_cb) {}
AesDecryptor::~AesDecryptor() {
STLDeleteValues(&key_map_);
}
-bool AesDecryptor::GenerateKeyRequest(const std::string& type,
+bool AesDecryptor::GenerateKeyRequest(uint32 reference_id,
+ const std::string& type,
const uint8* init_data,
int init_data_length) {
std::string session_id_string(base::UintToString(next_session_id_++));
@@ -280,15 +282,16 @@ bool AesDecryptor::GenerateKeyRequest(const std::string& type,
if (init_data && init_data_length)
message.assign(init_data, init_data + init_data_length);
- key_message_cb_.Run(session_id_string, message, std::string());
+ set_session_id_cb_.Run(reference_id, session_id_string);
+ key_message_cb_.Run(reference_id, message, std::string());
return true;
}
-void AesDecryptor::AddKey(const uint8* key,
+void AesDecryptor::AddKey(uint32 reference_id,
+ const uint8* key,
int key_length,
const uint8* init_data,
- int init_data_length,
- const std::string& session_id) {
+ int init_data_length) {
CHECK(key);
CHECK_GT(key_length, 0);
@@ -301,9 +304,6 @@ void AesDecryptor::AddKey(const uint8* key,
// key and |init_data| is the key id), if |key| is not valid JSON, then
// attempt to process it as a raw key.
- // TODO(xhwang): Add |session_id| check after we figure out how:
- // https://www.w3.org/Bugs/Public/show_bug.cgi?id=16550
-
std::string key_string(reinterpret_cast<const char*>(key), key_length);
JWKKeys jwk_keys;
if (ExtractJWKKeys(key_string, &jwk_keys)) {
@@ -313,12 +313,12 @@ void AesDecryptor::AddKey(const uint8* key,
// Make sure that at least one key was extracted.
if (jwk_keys.empty()) {
- key_error_cb_.Run(session_id, MediaKeys::kUnknownError, 0);
+ key_error_cb_.Run(reference_id, MediaKeys::kUnknownError, 0);
return;
}
for (JWKKeys::iterator it = jwk_keys.begin() ; it != jwk_keys.end(); ++it) {
if (!AddDecryptionKey(it->first, it->second)) {
- key_error_cb_.Run(session_id, MediaKeys::kUnknownError, 0);
+ key_error_cb_.Run(reference_id, MediaKeys::kUnknownError, 0);
return;
}
}
@@ -329,7 +329,7 @@ void AesDecryptor::AddKey(const uint8* key,
if (key_string.length() !=
static_cast<size_t>(DecryptConfig::kDecryptionKeySize)) {
DVLOG(1) << "Invalid key length: " << key_string.length();
- key_error_cb_.Run(session_id, MediaKeys::kUnknownError, 0);
+ key_error_cb_.Run(reference_id, MediaKeys::kUnknownError, 0);
return;
}
@@ -347,7 +347,7 @@ void AesDecryptor::AddKey(const uint8* key,
init_data_length);
if (!AddDecryptionKey(key_id_string, key_string)) {
// Error logged in AddDecryptionKey()
- key_error_cb_.Run(session_id, MediaKeys::kUnknownError, 0);
+ key_error_cb_.Run(reference_id, MediaKeys::kUnknownError, 0);
return;
}
}
@@ -358,10 +358,10 @@ void AesDecryptor::AddKey(const uint8* key,
if (!new_video_key_cb_.is_null())
new_video_key_cb_.Run();
- key_added_cb_.Run(session_id);
+ key_added_cb_.Run(reference_id);
}
-void AesDecryptor::CancelKeyRequest(const std::string& session_id) {
+void AesDecryptor::CancelKeyRequest(uint32 reference_id) {
}
Decryptor* AesDecryptor::GetDecryptor() {
diff --git a/media/cdm/aes_decryptor.h b/media/cdm/aes_decryptor.h
index 3ab4bc0..d1f9341 100644
--- a/media/cdm/aes_decryptor.h
+++ b/media/cdm/aes_decryptor.h
@@ -29,17 +29,21 @@ class MEDIA_EXPORT AesDecryptor : public MediaKeys, public Decryptor {
public:
AesDecryptor(const KeyAddedCB& key_added_cb,
const KeyErrorCB& key_error_cb,
- const KeyMessageCB& key_message_cb);
+ const KeyMessageCB& key_message_cb,
+ const SetSessionIdCB& set_session_id_cb);
virtual ~AesDecryptor();
// MediaKeys implementation.
- virtual bool GenerateKeyRequest(const std::string& type,
+ virtual bool GenerateKeyRequest(uint32 reference_id,
+ const std::string& type,
const uint8* init_data,
int init_data_length) OVERRIDE;
- virtual void AddKey(const uint8* key, int key_length,
- const uint8* init_data, int init_data_length,
- const std::string& session_id) OVERRIDE;
- virtual void CancelKeyRequest(const std::string& session_id) OVERRIDE;
+ virtual void AddKey(uint32 reference_id,
+ const uint8* key,
+ int key_length,
+ const uint8* init_data,
+ int init_data_length) OVERRIDE;
+ virtual void CancelKeyRequest(uint32 reference_id) OVERRIDE;
virtual Decryptor* GetDecryptor() OVERRIDE;
// Decryptor implementation.
@@ -99,6 +103,7 @@ class MEDIA_EXPORT AesDecryptor : public MediaKeys, public Decryptor {
KeyAddedCB key_added_cb_;
KeyErrorCB key_error_cb_;
KeyMessageCB key_message_cb_;
+ SetSessionIdCB set_session_id_cb_;
// KeyMap owns the DecryptionKey* and must delete them when they are
// not needed any more.
diff --git a/media/cdm/aes_decryptor_unittest.cc b/media/cdm/aes_decryptor_unittest.cc
index 3adc861..b52a6195 100644
--- a/media/cdm/aes_decryptor_unittest.cc
+++ b/media/cdm/aes_decryptor_unittest.cc
@@ -192,7 +192,8 @@ class AesDecryptorTest : public testing::Test {
: decryptor_(
base::Bind(&AesDecryptorTest::KeyAdded, base::Unretained(this)),
base::Bind(&AesDecryptorTest::KeyError, base::Unretained(this)),
- base::Bind(&AesDecryptorTest::KeyMessage, base::Unretained(this))),
+ base::Bind(&AesDecryptorTest::KeyMessage, base::Unretained(this)),
+ base::Bind(&AesDecryptorTest::SetSession, base::Unretained(this))),
decrypt_cb_(base::Bind(&AesDecryptorTest::BufferDecrypted,
base::Unretained(this))),
original_data_(kOriginalData, kOriginalData + kOriginalDataSize),
@@ -210,11 +211,12 @@ class AesDecryptorTest : public testing::Test {
protected:
void GenerateKeyRequest(const std::vector<uint8>& key_id) {
+ reference_id_ = 6;
DCHECK(!key_id.empty());
- EXPECT_CALL(*this, KeyMessage(StrNe(std::string()), key_id, ""))
- .WillOnce(SaveArg<0>(&session_id_string_));
+ EXPECT_CALL(*this, SetSession(reference_id_, StrNe(std::string())));
+ EXPECT_CALL(*this, KeyMessage(reference_id_, key_id, ""));
EXPECT_TRUE(decryptor_.GenerateKeyRequest(
- std::string(), &key_id[0], key_id.size()));
+ reference_id_, std::string(), &key_id[0], key_id.size()));
}
enum AddKeyExpectation {
@@ -230,33 +232,33 @@ class AesDecryptorTest : public testing::Test {
DCHECK(!key.empty());
if (result == KEY_ADDED) {
- EXPECT_CALL(*this, KeyAdded(session_id_string_));
+ EXPECT_CALL(*this, KeyAdded(reference_id_));
} else if (result == KEY_ERROR) {
- EXPECT_CALL(*this, KeyError(session_id_string_,
- MediaKeys::kUnknownError, 0));
+ EXPECT_CALL(*this, KeyError(reference_id_, MediaKeys::kUnknownError, 0));
} else {
NOTREACHED();
}
- decryptor_.AddKey(&key[0], key.size(), &key_id[0], key_id.size(),
- session_id_string_);
+ decryptor_.AddKey(
+ reference_id_, &key[0], key.size(), &key_id[0], key_id.size());
}
void AddKeyAndExpect(const std::string& key, AddKeyExpectation result) {
DCHECK(!key.empty());
if (result == KEY_ADDED) {
- EXPECT_CALL(*this, KeyAdded(session_id_string_));
+ EXPECT_CALL(*this, KeyAdded(reference_id_));
} else if (result == KEY_ERROR) {
- EXPECT_CALL(*this,
- KeyError(session_id_string_, MediaKeys::kUnknownError, 0));
+ EXPECT_CALL(*this, KeyError(reference_id_, MediaKeys::kUnknownError, 0));
} else {
NOTREACHED();
}
- decryptor_.AddKey(reinterpret_cast<const uint8*>(key.c_str()), key.length(),
- NULL, 0,
- session_id_string_);
+ decryptor_.AddKey(reference_id_,
+ reinterpret_cast<const uint8*>(key.c_str()),
+ key.length(),
+ NULL,
+ 0);
}
MOCK_METHOD2(BufferDecrypted, void(Decryptor::Status,
@@ -307,15 +309,17 @@ class AesDecryptorTest : public testing::Test {
}
}
- MOCK_METHOD1(KeyAdded, void(const std::string&));
- MOCK_METHOD3(KeyError, void(const std::string&,
- MediaKeys::KeyError, int));
- MOCK_METHOD3(KeyMessage, void(const std::string& session_id,
- const std::vector<uint8>& message,
- const std::string& default_url));
+ MOCK_METHOD1(KeyAdded, void(uint32 reference_id));
+ MOCK_METHOD3(KeyError, void(uint32 reference_id, MediaKeys::KeyError, int));
+ MOCK_METHOD3(KeyMessage,
+ void(uint32 reference_id,
+ const std::vector<uint8>& message,
+ const std::string& default_url));
+ MOCK_METHOD2(SetSession,
+ void(uint32 reference_id, const std::string& session_id));
AesDecryptor decryptor_;
- std::string session_id_string_;
+ uint32 reference_id_;
AesDecryptor::DecryptCB decrypt_cb_;
// Constants for testing.
@@ -329,8 +333,31 @@ class AesDecryptorTest : public testing::Test {
};
TEST_F(AesDecryptorTest, GenerateKeyRequestWithNullInitData) {
- EXPECT_CALL(*this, KeyMessage(StrNe(std::string()), IsEmpty(), ""));
- EXPECT_TRUE(decryptor_.GenerateKeyRequest(std::string(), NULL, 0));
+ reference_id_ = 8;
+ EXPECT_CALL(*this, KeyMessage(reference_id_, IsEmpty(), ""));
+ EXPECT_CALL(*this, SetSession(reference_id_, StrNe(std::string())));
+ EXPECT_TRUE(
+ decryptor_.GenerateKeyRequest(reference_id_, std::string(), NULL, 0));
+}
+
+TEST_F(AesDecryptorTest, MultipleGenerateKeyRequest) {
+ uint32 reference_id1 = 10;
+ EXPECT_CALL(*this, KeyMessage(reference_id1, IsEmpty(), ""));
+ EXPECT_CALL(*this, SetSession(reference_id1, StrNe(std::string())));
+ EXPECT_TRUE(
+ decryptor_.GenerateKeyRequest(reference_id1, std::string(), NULL, 0));
+
+ uint32 reference_id2 = 11;
+ EXPECT_CALL(*this, KeyMessage(reference_id2, IsEmpty(), ""));
+ EXPECT_CALL(*this, SetSession(reference_id2, StrNe(std::string())));
+ EXPECT_TRUE(
+ decryptor_.GenerateKeyRequest(reference_id2, std::string(), NULL, 0));
+
+ uint32 reference_id3 = 23;
+ EXPECT_CALL(*this, KeyMessage(reference_id3, IsEmpty(), ""));
+ EXPECT_CALL(*this, SetSession(reference_id3, StrNe(std::string())));
+ EXPECT_TRUE(
+ decryptor_.GenerateKeyRequest(reference_id3, std::string(), NULL, 0));
}
TEST_F(AesDecryptorTest, NormalDecryption) {
diff --git a/media/cdm/ppapi/cdm_adapter.cc b/media/cdm/ppapi/cdm_adapter.cc
index f8adb03..1da9d15 100644
--- a/media/cdm/ppapi/cdm_adapter.cc
+++ b/media/cdm/ppapi/cdm_adapter.cc
@@ -236,8 +236,7 @@ bool CdmAdapter::CreateCdmInstance(const std::string& key_system) {
// No KeyErrors should be reported in this function because they cannot be
// bubbled up in the WD EME API. Those errors will be reported during session
// creation (aka GenerateKeyRequest).
-void CdmAdapter::Initialize(const std::string& key_system,
- bool can_challenge_platform) {
+void CdmAdapter::Initialize(const std::string& key_system) {
PP_DCHECK(!key_system.empty());
PP_DCHECK(key_system_.empty() || (key_system_ == key_system && cdm_));
@@ -248,12 +247,13 @@ void CdmAdapter::Initialize(const std::string& key_system,
key_system_ = key_system;
}
-void CdmAdapter::GenerateKeyRequest(const std::string& type,
+void CdmAdapter::GenerateKeyRequest(uint32_t reference_id,
+ const std::string& type,
pp::VarArrayBuffer init_data) {
// Initialize() doesn't report an error, so GenerateKeyRequest() can be called
// even if Initialize() failed.
if (!cdm_) {
- SendUnknownKeyError(key_system_, std::string());
+ SendUnknownKeyError(reference_id);
return;
}
@@ -267,22 +267,20 @@ void CdmAdapter::GenerateKeyRequest(const std::string& type,
PP_DCHECK(0 < url_components.host.len);
#endif // defined(CHECK_DOCUMENT_URL)
- cdm::Status status = cdm_->GenerateKeyRequest(
- type.data(), type.size(),
- static_cast<const uint8_t*>(init_data.Map()),
- init_data.ByteLength());
- PP_DCHECK(status == cdm::kSuccess || status == cdm::kSessionError);
- if (status != cdm::kSuccess)
- SendUnknownKeyError(key_system_, std::string());
+ cdm_->GenerateKeyRequest(reference_id,
+ type.data(),
+ type.size(),
+ static_cast<const uint8_t*>(init_data.Map()),
+ init_data.ByteLength());
}
-void CdmAdapter::AddKey(const std::string& session_id,
+void CdmAdapter::AddKey(uint32_t reference_id,
pp::VarArrayBuffer key,
pp::VarArrayBuffer init_data) {
// TODO(jrummell): In EME WD, AddKey() can only be called on valid sessions.
// We should be able to DCHECK(cdm_) when addressing http://crbug.com/249976.
if (!cdm_) {
- SendUnknownKeyError(key_system_, session_id);
+ SendUnknownKeyError(reference_id);
return;
}
@@ -292,36 +290,43 @@ void CdmAdapter::AddKey(const std::string& session_id,
const uint32_t init_data_size = init_data.ByteLength();
PP_DCHECK(!init_data_ptr == !init_data_size);
- if (!key_ptr || !key_size) {
- SendUnknownKeyError(key_system_, session_id);
+ if (!key_ptr || key_size <= 0) {
+ SendUnknownKeyError(reference_id);
return;
}
-
- cdm::Status status = cdm_->AddKey(session_id.data(), session_id.size(),
- key_ptr, key_size,
- init_data_ptr, init_data_size);
- PP_DCHECK(status == cdm::kSuccess || status == cdm::kSessionError);
- if (status != cdm::kSuccess) {
- SendUnknownKeyError(key_system_, session_id);
- return;
+ CdmWrapper::Result result = cdm_->AddKey(
+ reference_id, key_ptr, key_size, init_data_ptr, init_data_size);
+ switch (result) {
+ case CdmWrapper::NO_ACTION:
+ break;
+ case CdmWrapper::CALL_KEY_ADDED:
+ SendKeyAdded(reference_id);
+ break;
+ case CdmWrapper::CALL_KEY_ERROR:
+ SendUnknownKeyError(reference_id);
+ break;
}
-
- SendKeyAdded(key_system_, session_id);
}
-void CdmAdapter::CancelKeyRequest(const std::string& session_id) {
+void CdmAdapter::CancelKeyRequest(uint32_t reference_id) {
// TODO(jrummell): In EME WD, AddKey() can only be called on valid sessions.
// We should be able to DCHECK(cdm_) when addressing http://crbug.com/249976.
if (!cdm_) {
- SendUnknownKeyError(key_system_, session_id);
+ SendUnknownKeyError(reference_id);
return;
}
- cdm::Status status = cdm_->CancelKeyRequest(session_id.data(),
- session_id.size());
- PP_DCHECK(status == cdm::kSuccess || status == cdm::kSessionError);
- if (status != cdm::kSuccess)
- SendUnknownKeyError(key_system_, session_id);
+ CdmWrapper::Result result = cdm_->CancelKeyRequest(reference_id);
+ switch (result) {
+ case CdmWrapper::NO_ACTION:
+ break;
+ case CdmWrapper::CALL_KEY_ADDED:
+ PP_NOTREACHED();
+ break;
+ case CdmWrapper::CALL_KEY_ERROR:
+ SendUnknownKeyError(reference_id);
+ break;
+ }
}
// Note: In the following decryption/decoding related functions, errors are NOT
@@ -527,10 +532,17 @@ void CdmAdapter::SendKeyMessage(
const char* message, uint32_t message_length,
const char* default_url, uint32_t default_url_length) {
PP_DCHECK(!key_system_.empty());
+
+ std::string session_id_str(session_id, session_id_length);
+ PP_DCHECK(!session_id_str.empty());
+ uint32_t reference_id = cdm_->DetermineReferenceId(session_id_str);
+
+ PostOnMain(callback_factory_.NewCallback(
+ &CdmAdapter::SetSessionId, reference_id, session_id_str));
+
PostOnMain(callback_factory_.NewCallback(
&CdmAdapter::KeyMessage,
- SessionInfo(key_system_,
- std::string(session_id, session_id_length)),
+ reference_id,
std::vector<uint8>(message, message + message_length),
std::string(default_url, default_url_length)));
}
@@ -539,10 +551,10 @@ void CdmAdapter::SendKeyError(const char* session_id,
uint32_t session_id_length,
cdm::MediaKeyError error_code,
uint32_t system_code) {
- SendKeyErrorInternal(key_system_,
- std::string(session_id, session_id_length),
- error_code,
- system_code);
+ std::string session_id_str(session_id, session_id_length);
+ uint32_t reference_id = cdm_->DetermineReferenceId(session_id_str);
+
+ SendKeyErrorInternal(reference_id, error_code, system_code);
}
void CdmAdapter::GetPrivateData(int32_t* instance,
@@ -551,41 +563,32 @@ void CdmAdapter::GetPrivateData(int32_t* instance,
*get_interface = pp::Module::Get()->get_browser_interface();
}
-void CdmAdapter::SendUnknownKeyError(const std::string& key_system,
- const std::string& session_id) {
- SendKeyErrorInternal(key_system, session_id, cdm::kUnknownError, 0);
+void CdmAdapter::SendUnknownKeyError(uint32_t reference_id) {
+ SendKeyErrorInternal(reference_id, cdm::kUnknownError, 0);
}
-void CdmAdapter::SendKeyAdded(const std::string& key_system,
- const std::string& session_id) {
- PostOnMain(callback_factory_.NewCallback(
- &CdmAdapter::KeyAdded,
- SessionInfo(key_system_, session_id)));
+void CdmAdapter::SendKeyAdded(uint32_t reference_id) {
+ PostOnMain(
+ callback_factory_.NewCallback(&CdmAdapter::KeyAdded, reference_id));
}
-void CdmAdapter::SendKeyErrorInternal(const std::string& key_system,
- const std::string& session_id,
+void CdmAdapter::SendKeyErrorInternal(uint32_t reference_id,
cdm::MediaKeyError error_code,
uint32_t system_code) {
- PostOnMain(callback_factory_.NewCallback(&CdmAdapter::KeyError,
- SessionInfo(key_system_, session_id),
- error_code,
- system_code));
+ PostOnMain(callback_factory_.NewCallback(
+ &CdmAdapter::KeyError, reference_id, error_code, system_code));
}
-void CdmAdapter::KeyAdded(int32_t result, const SessionInfo& session_info) {
+void CdmAdapter::KeyAdded(int32_t result, uint32_t reference_id) {
PP_DCHECK(result == PP_OK);
- PP_DCHECK(!session_info.key_system.empty());
- pp::ContentDecryptor_Private::KeyAdded(session_info.key_system,
- session_info.session_id);
+ pp::ContentDecryptor_Private::KeyAdded(reference_id);
}
void CdmAdapter::KeyMessage(int32_t result,
- const SessionInfo& session_info,
+ uint32_t reference_id,
const std::vector<uint8>& message,
const std::string& default_url) {
PP_DCHECK(result == PP_OK);
- PP_DCHECK(!session_info.key_system.empty());
pp::VarArrayBuffer message_array_buffer(message.size());
if (message.size() > 0) {
@@ -593,18 +596,24 @@ void CdmAdapter::KeyMessage(int32_t result,
}
pp::ContentDecryptor_Private::KeyMessage(
- session_info.key_system, session_info.session_id,
- message_array_buffer, default_url);
+ reference_id,
+ message_array_buffer,
+ default_url);
}
void CdmAdapter::KeyError(int32_t result,
- const SessionInfo& session_info,
+ uint32_t reference_id,
cdm::MediaKeyError error_code,
uint32_t system_code) {
PP_DCHECK(result == PP_OK);
- pp::ContentDecryptor_Private::KeyError(
- session_info.key_system, session_info.session_id,
- error_code, system_code);
+ pp::ContentDecryptor_Private::KeyError(reference_id, error_code, system_code);
+}
+
+void CdmAdapter::SetSessionId(int32_t result,
+ uint32_t reference_id,
+ const std::string& session_id) {
+ PP_DCHECK(result == PP_OK);
+ pp::ContentDecryptor_Private::SetSessionId(reference_id, session_id);
}
void CdmAdapter::DeliverBlock(int32_t result,
diff --git a/media/cdm/ppapi/cdm_adapter.h b/media/cdm/ppapi/cdm_adapter.h
index 441dfe9..0542c5d 100644
--- a/media/cdm/ppapi/cdm_adapter.h
+++ b/media/cdm/ppapi/cdm_adapter.h
@@ -51,15 +51,14 @@ class CdmAdapter : public pp::Instance,
// PPP_ContentDecryptor_Private implementation.
// Note: Results of calls to these methods must be reported through the
// PPB_ContentDecryptor_Private interface.
- // TODO(jrummell): |can_challenge_platform| should be removed.
- virtual void Initialize(const std::string& key_system,
- bool can_challenge_platform) OVERRIDE;
- virtual void GenerateKeyRequest(const std::string& type,
+ virtual void Initialize(const std::string& key_system) OVERRIDE;
+ virtual void GenerateKeyRequest(uint32_t reference_id,
+ const std::string& type,
pp::VarArrayBuffer init_data) OVERRIDE;
- virtual void AddKey(const std::string& session_id,
+ virtual void AddKey(uint32_t reference_id,
pp::VarArrayBuffer key,
pp::VarArrayBuffer init_data) OVERRIDE;
- virtual void CancelKeyRequest(const std::string& session_id) OVERRIDE;
+ virtual void CancelKeyRequest(uint32_t reference_id) OVERRIDE;
virtual void Decrypt(
pp::Buffer_Dev encrypted_buffer,
const PP_EncryptedBlockInfo& encrypted_block_info) OVERRIDE;
@@ -105,45 +104,33 @@ class CdmAdapter : public pp::Instance,
cdm::Status decoder_status) OVERRIDE;
private:
- struct SessionInfo {
- SessionInfo(const std::string& key_system_in,
- const std::string& session_id_in)
- : key_system(key_system_in),
- session_id(session_id_in) {}
- const std::string key_system;
- const std::string session_id;
- };
-
typedef linked_ptr<DecryptedBlockImpl> LinkedDecryptedBlock;
typedef linked_ptr<VideoFrameImpl> LinkedVideoFrame;
typedef linked_ptr<AudioFramesImpl> LinkedAudioFrames;
bool CreateCdmInstance(const std::string& key_system);
- void SendUnknownKeyError(const std::string& key_system,
- const std::string& session_id);
-
- void SendKeyAdded(const std::string& key_system,
- const std::string& session_id);
-
- // TODO(jrummell): Drop the |key_system| parameter.
- void SendKeyErrorInternal(const std::string& key_system,
- const std::string& session_id,
+ void SendUnknownKeyError(uint32_t reference_id);
+ void SendKeyAdded(uint32_t reference_id);
+ void SendKeyErrorInternal(uint32_t reference_id,
cdm::MediaKeyError error_code,
uint32_t system_code);
// <code>PPB_ContentDecryptor_Private</code> dispatchers. These are passed to
// <code>callback_factory_</code> to ensure that calls into
// <code>PPP_ContentDecryptor_Private</code> are asynchronous.
- void KeyAdded(int32_t result, const SessionInfo& session_info);
+ void KeyAdded(int32_t result, uint32_t reference_id);
void KeyMessage(int32_t result,
- const SessionInfo& session_info,
+ uint32_t reference_id,
const std::vector<uint8>& message,
const std::string& default_url);
void KeyError(int32_t result,
- const SessionInfo& session_info,
+ uint32_t reference_id,
cdm::MediaKeyError error_code,
uint32_t system_code);
+ void SetSessionId(int32_t result,
+ uint32_t reference_id,
+ const std::string& session_id);
void DeliverBlock(int32_t result,
const cdm::Status& status,
const LinkedDecryptedBlock& decrypted_block,
@@ -172,7 +159,6 @@ class CdmAdapter : public pp::Instance,
bool IsValidVideoFrame(const LinkedVideoFrame& video_frame);
-
#if defined(OS_CHROMEOS)
void SendPlatformChallengeDone(int32_t result);
void EnableProtectionDone(int32_t result);
diff --git a/media/cdm/ppapi/cdm_wrapper.h b/media/cdm/ppapi/cdm_wrapper.h
index 166d89c..8d3003a 100644
--- a/media/cdm/ppapi/cdm_wrapper.h
+++ b/media/cdm/ppapi/cdm_wrapper.h
@@ -5,6 +5,10 @@
#ifndef MEDIA_CDM_PPAPI_CDM_WRAPPER_H_
#define MEDIA_CDM_PPAPI_CDM_WRAPPER_H_
+#include <map>
+#include <queue>
+#include <string>
+
#include "base/basictypes.h"
#include "media/cdm/ppapi/api/content_decryption_module.h"
#include "media/cdm/ppapi/cdm_helpers.h"
@@ -31,6 +35,16 @@ namespace media {
// (just a shim layer in most cases), everything is done in this header file.
class CdmWrapper {
public:
+ // CDM_1 and CDM_2 methods AddKey() and CancelKeyRequest() may require
+ // callbacks to fire. Use this enum to indicate the additional calls required.
+ // TODO(jrummell): Remove return value once CDM_1 and CDM_2 are no longer
+ // supported.
+ enum Result {
+ NO_ACTION,
+ CALL_KEY_ADDED,
+ CALL_KEY_ERROR
+ };
+
static CdmWrapper* Create(const char* key_system,
uint32_t key_system_size,
GetCdmHostFunc get_cdm_host_func,
@@ -38,18 +52,17 @@ class CdmWrapper {
virtual ~CdmWrapper() {};
- virtual cdm::Status GenerateKeyRequest(const char* type,
- uint32_t type_size,
- const uint8_t* init_data,
- uint32_t init_data_size) = 0;
- virtual cdm::Status AddKey(const char* session_id,
- uint32_t session_id_size,
- const uint8_t* key,
- uint32_t key_size,
- const uint8_t* key_id,
- uint32_t key_id_size) = 0;
- virtual cdm::Status CancelKeyRequest(const char* session_id,
- uint32_t session_id_size) = 0;
+ virtual void GenerateKeyRequest(uint32_t reference_id,
+ const char* type,
+ uint32_t type_size,
+ const uint8_t* init_data,
+ uint32_t init_data_size) = 0;
+ virtual Result AddKey(uint32_t reference_id,
+ const uint8_t* key,
+ uint32_t key_size,
+ const uint8_t* key_id,
+ uint32_t key_id_size) = 0;
+ virtual Result CancelKeyRequest(uint32_t reference_id) = 0;
virtual void TimerExpired(void* context) = 0;
virtual cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_buffer) = 0;
@@ -71,8 +84,39 @@ class CdmWrapper {
uint32_t link_mask,
uint32_t output_protection_mask) = 0;
+ // ContentDecryptionModule_1 and ContentDecryptionModule_2 interface methods
+ // AddKey() and CancelKeyRequest() (older versions of Update() and Close(),
+ // respectively) pass in the session_id rather than the reference_id. As well,
+ // Host_1 and Host_2 callbacks SendKeyMessage() and SendKeyError() include the
+ // session ID, but the actual callbacks need the reference ID.
+ //
+ // The following functions maintain the reference_id <-> session_id mapping.
+ // These can be removed once _1 and _2 interfaces are no longer supported.
+
+ // Determine the corresponding reference_id for |session_id|.
+ virtual uint32_t DetermineReferenceId(const std::string& session_id) = 0;
+
+ // Determine the corresponding session_id for |reference_id|.
+ virtual const std::string LookupSessionId(uint32_t reference_id) = 0;
+
protected:
- CdmWrapper() {};
+ typedef std::map<uint32_t, std::string> SessionMap;
+ static const uint32_t kInvalidReferenceId = 0;
+
+ CdmWrapper() : current_key_request_reference_id_(kInvalidReferenceId) {}
+
+ // Map between session_id and reference_id.
+ SessionMap session_map_;
+
+ // As the response from GenerateKeyRequest() may be synchronous or
+ // asynchronous, keep track of the current request during the call to handle
+ // synchronous responses or errors. If no response received, add this request
+ // to a queue and assume that the subsequent responses come back in the order
+ // issued.
+ // TODO(jrummell): Remove once all supported CDM host interfaces support
+ // reference_id.
+ uint32_t current_key_request_reference_id_;
+ std::queue<uint32_t> pending_key_request_reference_ids_;
private:
DISALLOW_COPY_AND_ASSIGN(CdmWrapper);
@@ -102,26 +146,92 @@ class CdmWrapperImpl : public CdmWrapper {
cdm_->Destroy();
}
- virtual cdm::Status GenerateKeyRequest(const char* type,
- uint32_t type_size,
- const uint8_t* init_data,
- uint32_t init_data_size) OVERRIDE {
- return cdm_->GenerateKeyRequest(type, type_size, init_data, init_data_size);
+ // TODO(jrummell): In CDM_3 all key callbacks will use reference_id, so there
+ // is no need to keep track of the current/pending request IDs. As well, the
+ // definition for AddKey() and CancelKeyRequest() require the CDM to always
+ // send a response (success or error), so the callbacks are not required.
+ // Simplify the following 3 routines when CDM_3 is supported.
+
+ virtual void GenerateKeyRequest(uint32_t reference_id,
+ const char* type,
+ uint32_t type_size,
+ const uint8_t* init_data,
+ uint32_t init_data_size) OVERRIDE {
+ // As it is possible for CDMs to reply synchronously during the call to
+ // GenerateKeyRequest(), keep track of |reference_id|.
+ current_key_request_reference_id_ = reference_id;
+
+ cdm::Status status =
+ cdm_->GenerateKeyRequest(type, type_size, init_data, init_data_size);
+ PP_DCHECK(status == cdm::kSuccess || status == cdm::kSessionError);
+ if (status != cdm::kSuccess) {
+ // If GenerateKeyRequest() failed, no subsequent asynchronous replies
+ // will be sent. Verify that a response was sent synchronously.
+ PP_DCHECK(current_key_request_reference_id_ == kInvalidReferenceId);
+ current_key_request_reference_id_ = kInvalidReferenceId;
+ return;
+ }
+
+ if (current_key_request_reference_id_) {
+ // If this request is still pending (SendKeyMessage() or SendKeyError()
+ // not called synchronously), add |reference_id| to the end of the queue.
+ // Without CDM support, it is impossible to match SendKeyMessage()
+ // (or SendKeyError()) responses to the |reference_id|. Doing the best
+ // we can by keeping track of this in a queue, and assuming the responses
+ // come back in order.
+ pending_key_request_reference_ids_.push(reference_id);
+ current_key_request_reference_id_ = kInvalidReferenceId;
+ }
}
- virtual cdm::Status AddKey(const char* session_id,
- uint32_t session_id_size,
- const uint8_t* key,
- uint32_t key_size,
- const uint8_t* key_id,
- uint32_t key_id_size) OVERRIDE {
- return cdm_->AddKey(
- session_id, session_id_size, key, key_size, key_id, key_id_size);
+ virtual Result AddKey(uint32_t reference_id,
+ const uint8_t* key,
+ uint32_t key_size,
+ const uint8_t* key_id,
+ uint32_t key_id_size) OVERRIDE {
+ const std::string session_id = LookupSessionId(reference_id);
+ if (session_id.empty()) {
+ // Possible if AddKey() called before GenerateKeyRequest().
+ return CALL_KEY_ERROR;
+ }
+
+ cdm::Status status = cdm_->AddKey(session_id.data(),
+ session_id.size(),
+ key,
+ key_size,
+ key_id,
+ key_id_size);
+ PP_DCHECK(status == cdm::kSuccess || status == cdm::kSessionError);
+ if (status != cdm::kSuccess) {
+ // http://crbug.com/310345: CDMs should send a KeyError message if they
+ // return a failure, so no need to do it twice. Remove this once all CDMs
+ // have been updated.
+ return CALL_KEY_ERROR;
+ }
+
+ return CALL_KEY_ADDED;
}
- virtual cdm::Status CancelKeyRequest(const char* session_id,
- uint32_t session_id_size) OVERRIDE {
- return cdm_->CancelKeyRequest(session_id, session_id_size);
+ virtual Result CancelKeyRequest(uint32_t reference_id) OVERRIDE {
+ const std::string session_id = LookupSessionId(reference_id);
+ if (session_id.empty()) {
+ // Possible if CancelKeyRequest() called before GenerateKeyRequest().
+ return CALL_KEY_ERROR;
+ }
+
+ session_map_.erase(reference_id);
+ cdm::Status status =
+ cdm_->CancelKeyRequest(session_id.data(), session_id.size());
+
+ PP_DCHECK(status == cdm::kSuccess || status == cdm::kSessionError);
+ if (status != cdm::kSuccess) {
+ // http://crbug.com/310345: CDMs should send a KeyError message if they
+ // return a failure, so no need to do it twice. Remove this once all CDMs
+ // have been updated.
+ return CALL_KEY_ERROR;
+ }
+
+ return NO_ACTION;
}
virtual void TimerExpired(void* context) OVERRIDE {
@@ -174,6 +284,45 @@ class CdmWrapperImpl : public CdmWrapper {
cdm_->OnQueryOutputProtectionStatus(link_mask, output_protection_mask);
}
+ uint32_t DetermineReferenceId(const std::string& session_id) {
+ for (SessionMap::iterator it = session_map_.begin();
+ it != session_map_.end();
+ ++it) {
+ if (it->second == session_id)
+ return it->first;
+ }
+
+ // There is no entry in the map; assume it came from the current
+ // GenerateKeyRequest() call (if possible). If no current request,
+ // assume it came from the oldest GenerateKeyRequest() call.
+ uint32_t reference_id = current_key_request_reference_id_;
+ if (current_key_request_reference_id_) {
+ // Only 1 response is allowed for the current GenerateKeyRequest().
+ current_key_request_reference_id_ = kInvalidReferenceId;
+ } else {
+ PP_DCHECK(!pending_key_request_reference_ids_.empty());
+ reference_id = pending_key_request_reference_ids_.front();
+ pending_key_request_reference_ids_.pop();
+ }
+
+ // If this is a valid |session_id|, add it to the list. Otherwise, avoid
+ // adding empty string as a mapping to prevent future calls with an empty
+ // string from using the wrong reference_id.
+ if (!session_id.empty()) {
+ PP_DCHECK(session_map_.find(reference_id) == session_map_.end());
+ PP_DCHECK(!session_id.empty());
+ session_map_[reference_id] = session_id;
+ }
+
+ return reference_id;
+ }
+
+ const std::string LookupSessionId(uint32_t reference_id) {
+ // Session may not exist if error happens during GenerateKeyRequest().
+ SessionMap::iterator it = session_map_.find(reference_id);
+ return (it != session_map_.end()) ? it->second : std::string();
+ }
+
private:
CdmWrapperImpl(CdmInterface* cdm) : cdm_(cdm) {
PP_DCHECK(cdm_);
diff --git a/media/cdm/ppapi/clear_key_cdm.cc b/media/cdm/ppapi/clear_key_cdm.cc
index 279e1ce..3728eff 100644
--- a/media/cdm/ppapi/clear_key_cdm.cc
+++ b/media/cdm/ppapi/clear_key_cdm.cc
@@ -153,42 +153,56 @@ const char* GetCdmVersion() {
namespace media {
-ClearKeyCdm::Client::Client() : status_(kKeyError) {}
+// Since all the calls to AesDecryptor are synchronous, pass a dummy value for
+// reference_id that is never exposed outside this class.
+// TODO(jrummell): Remove usage of this when the CDM interface is updated
+// to use reference_id.
+
+ClearKeyCdm::Client::Client()
+ : status_(kNone), error_code_(MediaKeys::kUnknownError), system_code_(0) {}
ClearKeyCdm::Client::~Client() {}
void ClearKeyCdm::Client::Reset() {
- status_ = kKeyError;
+ status_ = kNone;
session_id_.clear();
key_message_.clear();
default_url_.clear();
+ error_code_ = MediaKeys::kUnknownError;
+ system_code_ = 0;
}
-void ClearKeyCdm::Client::KeyAdded(const std::string& session_id) {
- status_ = kKeyAdded;
- session_id_ = session_id;
+void ClearKeyCdm::Client::KeyAdded(uint32 reference_id) {
+ status_ = static_cast<Status>(status_ | kKeyAdded);
}
-void ClearKeyCdm::Client::KeyError(const std::string& session_id,
+void ClearKeyCdm::Client::KeyError(uint32 reference_id,
media::MediaKeys::KeyError error_code,
int system_code) {
- status_ = kKeyError;
- session_id_ = session_id;
+ status_ = static_cast<Status>(status_ | kKeyError);
+ error_code_ = error_code;
+ system_code_ = system_code;
}
-void ClearKeyCdm::Client::KeyMessage(const std::string& session_id,
+void ClearKeyCdm::Client::KeyMessage(uint32 reference_id,
const std::vector<uint8>& message,
const std::string& default_url) {
- status_ = kKeyMessage;
- session_id_ = session_id;
+ status_ = static_cast<Status>(status_ | kKeyMessage);
key_message_ = message;
default_url_ = default_url;
}
+void ClearKeyCdm::Client::SetSessionId(uint32 reference_id,
+ const std::string& session_id) {
+ status_ = static_cast<Status>(status_ | kSetSessionId);
+ session_id_ = session_id;
+}
+
ClearKeyCdm::ClearKeyCdm(ClearKeyCdmHost* host)
: decryptor_(base::Bind(&Client::KeyAdded, base::Unretained(&client_)),
base::Bind(&Client::KeyError, base::Unretained(&client_)),
- base::Bind(&Client::KeyMessage, base::Unretained(&client_))),
+ base::Bind(&Client::KeyMessage, base::Unretained(&client_)),
+ base::Bind(&Client::SetSessionId, base::Unretained(&client_))),
host_(host),
timer_delay_ms_(kInitialTimerDelayMs),
timer_set_(false) {
@@ -210,11 +224,16 @@ cdm::Status ClearKeyCdm::GenerateKeyRequest(const char* type,
DVLOG(1) << "GenerateKeyRequest()";
base::AutoLock auto_lock(client_lock_);
ScopedResetter<Client> auto_resetter(&client_);
- decryptor_.GenerateKeyRequest(std::string(type, type_size),
+ decryptor_.GenerateKeyRequest(MediaKeys::kInvalidReferenceId,
+ std::string(type, type_size),
init_data, init_data_size);
- if (client_.status() != Client::kKeyMessage) {
- host_->SendKeyError(NULL, 0, cdm::kUnknownError, 0);
+ if (client_.status() != (Client::kKeyMessage | Client::kSetSessionId)) {
+ // Use values returned to client if possible.
+ host_->SendKeyError(client_.session_id().data(),
+ client_.session_id().size(),
+ static_cast<cdm::MediaKeyError>(client_.error_code()),
+ client_.system_code());
return cdm::kSessionError;
}
@@ -239,11 +258,16 @@ cdm::Status ClearKeyCdm::AddKey(const char* session_id,
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)
+ decryptor_.AddKey(MediaKeys::kInvalidReferenceId,
+ key, key_size,
+ key_id, key_id_size);
+
+ if (client_.status() != Client::kKeyAdded) {
+ host_->SendKeyError(session_id, session_id_size,
+ static_cast<cdm::MediaKeyError>(client_.error_code()),
+ client_.system_code());
return cdm::kSessionError;
+ }
if (!timer_set_) {
ScheduleNextHeartBeat();
@@ -258,7 +282,17 @@ cdm::Status ClearKeyCdm::CancelKeyRequest(const char* session_id,
DVLOG(1) << "CancelKeyRequest()";
base::AutoLock auto_lock(client_lock_);
ScopedResetter<Client> auto_resetter(&client_);
- decryptor_.CancelKeyRequest(std::string(session_id, session_id_size));
+ decryptor_.CancelKeyRequest(MediaKeys::kInvalidReferenceId);
+
+ // No message normally sent by CancelKeyRequest(), but if an error occurred,
+ // report it as a failure.
+ if (client_.status() == Client::kKeyError) {
+ host_->SendKeyError(session_id, session_id_size,
+ static_cast<cdm::MediaKeyError>(client_.error_code()),
+ client_.system_code());
+ return cdm::kSessionError;
+ }
+
return cdm::kSuccess;
}
diff --git a/media/cdm/ppapi/clear_key_cdm.h b/media/cdm/ppapi/clear_key_cdm.h
index 1da325d..c8d0703 100644
--- a/media/cdm/ppapi/clear_key_cdm.h
+++ b/media/cdm/ppapi/clear_key_cdm.h
@@ -71,10 +71,14 @@ class ClearKeyCdm : public ClearKeyCdmInterface {
// this Client class as well. Investigate this possibility.
class Client {
public:
+ // TODO(jrummell): Remove bitmask and rename kNone to kInvalid once CDM
+ // interface supports reference_id passing completely.
enum Status {
- kKeyAdded,
- kKeyError,
- kKeyMessage
+ kNone = 0,
+ kKeyAdded = 1 << 0,
+ kKeyError = 1 << 1,
+ kKeyMessage = 1 << 2,
+ kSetSessionId = 1 << 3
};
Client();
@@ -84,23 +88,28 @@ class ClearKeyCdm : public ClearKeyCdmInterface {
const std::string& session_id() { return session_id_; }
const std::vector<uint8>& key_message() { return key_message_; }
const std::string& default_url() { return default_url_; }
+ MediaKeys::KeyError error_code() { return error_code_; }
+ int system_code() { return system_code_; }
// Resets the Client to a clean state.
void Reset();
- void KeyAdded(const std::string& session_id);
- void KeyError(const std::string& session_id,
+ void KeyAdded(uint32 reference_id);
+ void KeyError(uint32 reference_id,
MediaKeys::KeyError error_code,
int system_code);
- void KeyMessage(const std::string& session_id,
+ void KeyMessage(uint32 reference_id,
const std::vector<uint8>& message,
const std::string& default_url);
+ void SetSessionId(uint32 reference_id, const std::string& session_id);
private:
Status status_;
std::string session_id_;
std::vector<uint8> key_message_;
std::string default_url_;
+ MediaKeys::KeyError error_code_;
+ int system_code_;
};
// Prepares next heartbeat message and sets a timer for it.