Skip to content

Commit

Permalink
media: Support Decryptor hosted by CdmProxy
Browse files Browse the repository at this point in the history
- In CdmAdapter, do not expose a Decryptor is CdmProxy is requested.
- In MojoCdm, support CDM ID based Decryptor in MojoCdm.
- Implement ClearKeyCdmProxy::SetKey() to load a license in its
  AesDecryptor so it can perform decryption.
- Update ECKEncryptedMediaTest.CdmProxy test to cover playback using
  ClearKeyCdmProxy to do decryption.

TBR: rkuroiwa@chromium.org
Bug: 806018
Test: ECKEncryptedMediaTest.CdmProxy covers the new path
Change-Id: Icd1cbddb8535f9b2ef5b20c3b1535d8c41dbb19c
Reviewed-on: https://chromium-review.googlesource.com/1071073
Commit-Queue: Xiaohan Wang <xhwang@chromium.org>
Reviewed-by: John Rummell <jrummell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#562537}
  • Loading branch information
xhwang-chromium authored and Commit Bot committed May 29, 2018
1 parent 72a97a3 commit d908b6e
Show file tree
Hide file tree
Showing 15 changed files with 118 additions and 38 deletions.
6 changes: 4 additions & 2 deletions chrome/browser/media/encrypted_media_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -855,8 +855,10 @@ IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, CdmProxy) {
return;
}

TestNonPlaybackCases(kExternalClearKeyCdmProxyTestKeySystem,
kUnitTestSuccess);
// ClearKeyCdmProxy only supports decrypt-only.
RunSimpleEncryptedMediaTest("bear-a_enc-a.webm", kWebMVorbisAudioOnly,
kExternalClearKeyCdmProxyTestKeySystem,
SrcType::MSE);
}

#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS)
2 changes: 2 additions & 0 deletions media/cdm/aes_decryptor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,8 @@ void AesDecryptor::RegisterNewKeyCB(StreamType stream_type,
void AesDecryptor::Decrypt(StreamType stream_type,
scoped_refptr<DecoderBuffer> encrypted,
const DecryptCB& decrypt_cb) {
DVLOG(3) << __func__ << ": " << encrypted->AsHumanReadableString();

if (!encrypted->decrypt_config()) {
// If there is no DecryptConfig, then the data is unencrypted so return it
// immediately.
Expand Down
2 changes: 2 additions & 0 deletions media/cdm/aes_decryptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ class MEDIA_EXPORT AesDecryptor : public ContentDecryptionModule,
void DeinitializeDecoder(StreamType stream_type) override;

private:
// Testing classes that needs to manipulate internal states for testing.
friend class ClearKeyPersistentSessionCdm;
friend class ClearKeyCdmProxy;

// Internally this class supports persistent license type sessions so that
// it can be used by ClearKeyPersistentSessionCdm. The following methods
Expand Down
5 changes: 4 additions & 1 deletion media/cdm/cdm_adapter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,10 @@ Decryptor* CdmAdapter::GetDecryptor() {

// When using HW secure codecs, we cannot and should not use the CDM instance
// to do decrypt and/or decode. Instead, we should use the CdmProxy.
if (cdm_config_.use_hw_secure_codecs)
// TODO(xhwang): Fix External Clear Key key system to be able to set
// |use_hw_secure_codecs| so that we don't have to check both.
// TODO(xhwang): Update this logic to support transcryption.
if (cdm_config_.use_hw_secure_codecs || cdm_proxy_created_)
return nullptr;

return this;
Expand Down
5 changes: 5 additions & 0 deletions media/cdm/library_cdm/clear_key_cdm/cdm_proxy_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ void CdmProxyTest::Run(CompletionCB completion_cb) {
cdm_proxy_->Initialize();
}

void CdmProxyTest::SetKey(const std::vector<uint8_t>& response) {
cdm_proxy_->SetKey(crypto_session_id_, nullptr, 0, response.data(),
response.size());
}

void CdmProxyTest::OnTestComplete(bool success) {
DVLOG(1) << __func__ << ": success = " << success;
std::move(completion_cb_).Run(success);
Expand Down
3 changes: 3 additions & 0 deletions media/cdm/library_cdm/clear_key_cdm/cdm_proxy_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class CdmProxyTest : public cdm::CdmProxyClient {
// Runs the test and returns the test result through |completion_cb|.
void Run(CompletionCB completion_cb);

void SetKey(const std::vector<uint8_t>& response);

private:
void OnTestComplete(bool success);

Expand All @@ -41,6 +43,7 @@ class CdmProxyTest : public cdm::CdmProxyClient {
CdmHostProxy* const cdm_host_proxy_ = nullptr;
CompletionCB completion_cb_;
cdm::CdmProxy* cdm_proxy_ = nullptr;
uint32_t crypto_session_id_ = 0u;

DISALLOW_COPY_AND_ASSIGN(CdmProxyTest);
};
Expand Down
34 changes: 17 additions & 17 deletions media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -483,8 +483,6 @@ void ClearKeyCdm::CreateSessionAndGenerateRequest(
ReportVerifyCdmHostTestResult();
} else if (key_system_ == kExternalClearKeyStorageIdTestKeySystem) {
StartStorageIdTest();
} else if (key_system_ == kExternalClearKeyCdmProxyTestKeySystem) {
ReportCdmProxyTestResult();
}
}

Expand Down Expand Up @@ -514,16 +512,19 @@ void ClearKeyCdm::UpdateSession(uint32_t promise_id,
uint32_t response_size) {
DVLOG(1) << __func__;
std::string web_session_str(session_id, session_id_length);
std::vector<uint8_t> response_vector(response, response + response_size);

std::unique_ptr<media::SimpleCdmPromise> promise(
new media::CdmCallbackPromise<>(
base::Bind(&ClearKeyCdm::OnUpdateSuccess, base::Unretained(this),
promise_id, web_session_str),
base::Bind(&ClearKeyCdm::OnPromiseFailed, base::Unretained(this),
promise_id)));
cdm_->UpdateSession(web_session_str,
std::vector<uint8_t>(response, response + response_size),
std::move(promise));
cdm_->UpdateSession(web_session_str, response_vector, std::move(promise));

// Also push the license to CdmProxy
if (cdm_proxy_test_)
cdm_proxy_test_->SetKey(response_vector);
}

void ClearKeyCdm::OnUpdateSuccess(uint32_t promise_id,
Expand Down Expand Up @@ -649,6 +650,10 @@ cdm::Status ClearKeyCdm::Decrypt(const cdm::InputBuffer_2& encrypted_buffer,
DVLOG(1) << __func__;
DCHECK(encrypted_buffer.data);

// When CdmProxy is used, the CDM cannot do any decryption or decoding.
if (key_system_ == kExternalClearKeyCdmProxyTestKeySystem)
return cdm::kDecryptError;

scoped_refptr<DecoderBuffer> buffer;
cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);

Expand Down Expand Up @@ -683,8 +688,10 @@ cdm::Status ClearKeyCdm::InitializeAudioDecoder(

cdm::Status ClearKeyCdm::InitializeAudioDecoder(
const cdm::AudioDecoderConfig_2& audio_decoder_config) {
if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem ||
key_system_ == kExternalClearKeyCdmProxyTestKeySystem) {
return cdm::kInitializationError;
}

#if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
if (!audio_decoder_)
Expand Down Expand Up @@ -715,8 +722,10 @@ cdm::Status ClearKeyCdm::InitializeVideoDecoder(

cdm::Status ClearKeyCdm::InitializeVideoDecoder(
const cdm::VideoDecoderConfig_2& video_decoder_config) {
if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem ||
key_system_ == kExternalClearKeyCdmProxyTestKeySystem) {
return cdm::kInitializationError;
}

if (video_decoder_ && video_decoder_->is_initialized()) {
DCHECK(!video_decoder_->is_initialized());
Expand Down Expand Up @@ -1084,16 +1093,7 @@ void ClearKeyCdm::OnCdmProxyTestComplete(bool success) {
DVLOG(1) << __func__;
DCHECK(cdm_proxy_test_);

cdm_proxy_test_.reset();
has_cdm_proxy_test_passed_ = success;

// Ignore test result here. It will be reported in ReportCdmProxyTestResult().
cdm_host_proxy_->OnInitialized(true);
}

void ClearKeyCdm::ReportCdmProxyTestResult() {
// StartCdmProxyTest() should have already been called and finished.
OnUnitTestComplete(has_cdm_proxy_test_passed_);
cdm_host_proxy_->OnInitialized(success);
}

} // namespace media
2 changes: 0 additions & 2 deletions media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@ class ClearKeyCdm : public cdm::ContentDecryptionModule_9,

void StartCdmProxyTest();
void OnCdmProxyTestComplete(bool success);
void ReportCdmProxyTestResult();

int host_interface_version_ = 0;

Expand Down Expand Up @@ -194,7 +193,6 @@ class ClearKeyCdm : public cdm::ContentDecryptionModule_9,
bool is_running_output_protection_test_ = false;
bool is_running_platform_verification_test_ = false;
bool is_running_storage_id_test_ = false;
bool has_cdm_proxy_test_passed_ = false;

DISALLOW_COPY_AND_ASSIGN(ClearKeyCdm);
};
Expand Down
50 changes: 44 additions & 6 deletions media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,26 @@

namespace media {

namespace {

constexpr char kDummySessionId[] = "dummy session id";

class IgnoreResponsePromise : public SimpleCdmPromise {
public:
IgnoreResponsePromise() = default;
~IgnoreResponsePromise() override = default;

// SimpleCdmPromise implementation.
void resolve() final { MarkPromiseSettled(); }
void reject(CdmPromise::Exception exception_code,
uint32_t system_code,
const std::string& error_message) final {
MarkPromiseSettled();
}
};

} // namespace

ClearKeyCdmProxy::ClearKeyCdmProxy() : weak_factory_(this) {}

ClearKeyCdmProxy::~ClearKeyCdmProxy() {}
Expand Down Expand Up @@ -67,21 +87,39 @@ void ClearKeyCdmProxy::CreateMediaCryptoSession(

void ClearKeyCdmProxy::SetKey(uint32_t crypto_session_id,
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key_blob) {}
const std::vector<uint8_t>& key_blob) {
DVLOG(1) << __func__;

if (!aes_decryptor_)
CreateDecryptor();

aes_decryptor_->UpdateSession(kDummySessionId, key_blob,
std::make_unique<IgnoreResponsePromise>());
}

void ClearKeyCdmProxy::RemoveKey(uint32_t crypto_session_id,
const std::vector<uint8_t>& key_id) {}

Decryptor* ClearKeyCdmProxy::GetDecryptor() {
DVLOG(1) << __func__;

if (!aes_decryptor_) {
aes_decryptor_ = base::MakeRefCounted<AesDecryptor>(
base::DoNothing(), base::DoNothing(), base::DoNothing(),
base::DoNothing());
}
if (!aes_decryptor_)
CreateDecryptor();

return aes_decryptor_.get();
}

void ClearKeyCdmProxy::CreateDecryptor() {
DVLOG(1) << __func__;
DCHECK(!aes_decryptor_);

aes_decryptor_ =
base::MakeRefCounted<AesDecryptor>(base::DoNothing(), base::DoNothing(),
base::DoNothing(), base::DoNothing());

// Also create a dummy session to be used for SetKey().
aes_decryptor_->CreateSession(kDummySessionId,
CdmSessionType::TEMPORARY_SESSION);
}

} // namespace media
2 changes: 2 additions & 0 deletions media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class ClearKeyCdmProxy : public CdmProxy, public CdmContext {
Decryptor* GetDecryptor() final;

private:
void CreateDecryptor();

scoped_refptr<AesDecryptor> aes_decryptor_;

base::WeakPtrFactory<ClearKeyCdmProxy> weak_factory_;
Expand Down
29 changes: 23 additions & 6 deletions media/mojo/clients/mojo_cdm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "media/mojo/clients/mojo_decryptor.h"
#include "media/mojo/common/media_type_converters.h"
#include "media/mojo/interfaces/decryptor.mojom.h"
#include "media/mojo/interfaces/interface_factory.mojom.h"
#include "services/service_manager/public/cpp/connect.h"
#include "services/service_manager/public/mojom/interface_provider.mojom.h"
#include "url/origin.h"
Expand All @@ -41,14 +42,15 @@ void MojoCdm::Create(
const url::Origin& security_origin,
const CdmConfig& cdm_config,
mojom::ContentDecryptionModulePtr remote_cdm,
mojom::InterfaceFactory* interface_factory,
const SessionMessageCB& session_message_cb,
const SessionClosedCB& session_closed_cb,
const SessionKeysChangeCB& session_keys_change_cb,
const SessionExpirationUpdateCB& session_expiration_update_cb,
const CdmCreatedCB& cdm_created_cb) {
scoped_refptr<MojoCdm> mojo_cdm(
new MojoCdm(std::move(remote_cdm), session_message_cb, session_closed_cb,
session_keys_change_cb, session_expiration_update_cb));
scoped_refptr<MojoCdm> mojo_cdm(new MojoCdm(
std::move(remote_cdm), interface_factory, session_message_cb,
session_closed_cb, session_keys_change_cb, session_expiration_update_cb));

// |mojo_cdm| ownership is passed to the promise.
std::unique_ptr<CdmInitializedPromise> promise(
Expand All @@ -59,11 +61,13 @@ void MojoCdm::Create(
}

MojoCdm::MojoCdm(mojom::ContentDecryptionModulePtr remote_cdm,
mojom::InterfaceFactory* interface_factory,
const SessionMessageCB& session_message_cb,
const SessionClosedCB& session_closed_cb,
const SessionKeysChangeCB& session_keys_change_cb,
const SessionExpirationUpdateCB& session_expiration_update_cb)
: remote_cdm_(std::move(remote_cdm)),
interface_factory_(interface_factory),
client_binding_(this),
task_runner_(base::ThreadTaskRunnerHandle::Get()),
cdm_id_(CdmContext::kInvalidCdmId),
Expand Down Expand Up @@ -303,14 +307,27 @@ Decryptor* MojoCdm::GetDecryptor() {
decryptor_task_runner_ = base::ThreadTaskRunnerHandle::Get();
DCHECK(decryptor_task_runner_->BelongsToCurrentThread());

if (decryptor_)
return decryptor_.get();

mojom::DecryptorPtr decryptor_ptr;

// Can be called on a different thread.
if (decryptor_ptr_info_.is_valid()) {
DCHECK(!decryptor_);
mojom::DecryptorPtr decryptor_ptr;
DVLOG(1) << __func__ << ": Using Decryptor exposed by the CDM directly";
decryptor_ptr.Bind(std::move(decryptor_ptr_info_));
decryptor_.reset(new MojoDecryptor(std::move(decryptor_ptr)));
} else if (interface_factory_ && cdm_id_ != CdmContext::kInvalidCdmId) {
// TODO(xhwang): Pass back info on whether Decryptor is supported by the
// remote CDM.
DVLOG(1) << __func__ << ": Using Decryptor associated with CDM ID "
<< cdm_id_ << ", typically hosted by CdmProxy in MediaService";
interface_factory_->CreateDecryptor(cdm_id_,
mojo::MakeRequest(&decryptor_ptr));
}

if (decryptor_ptr)
decryptor_.reset(new MojoDecryptor(std::move(decryptor_ptr)));

return decryptor_.get();
}

Expand Down
7 changes: 7 additions & 0 deletions media/mojo/clients/mojo_cdm.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ class Origin;

namespace media {

namespace mojom {
class InterfaceFactory;
}

class MojoDecryptor;

// A ContentDecryptionModule that proxies to a mojom::ContentDecryptionModule.
Expand All @@ -49,6 +53,7 @@ class MojoCdm : public ContentDecryptionModule,
const url::Origin& security_origin,
const CdmConfig& cdm_config,
mojom::ContentDecryptionModulePtr remote_cdm,
mojom::InterfaceFactory* interface_factory,
const SessionMessageCB& session_message_cb,
const SessionClosedCB& session_closed_cb,
const SessionKeysChangeCB& session_keys_change_cb,
Expand Down Expand Up @@ -84,6 +89,7 @@ class MojoCdm : public ContentDecryptionModule,

private:
MojoCdm(mojom::ContentDecryptionModulePtr remote_cdm,
mojom::InterfaceFactory* interface_factory,
const SessionMessageCB& session_message_cb,
const SessionClosedCB& session_closed_cb,
const SessionKeysChangeCB& session_keys_change_cb,
Expand Down Expand Up @@ -132,6 +138,7 @@ class MojoCdm : public ContentDecryptionModule,
THREAD_CHECKER(thread_checker_);

mojom::ContentDecryptionModulePtr remote_cdm_;
mojom::InterfaceFactory* interface_factory_;
mojo::Binding<ContentDecryptionModuleClient> client_binding_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;

Expand Down
5 changes: 3 additions & 2 deletions media/mojo/clients/mojo_cdm_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ void MojoCdmFactory::Create(
interface_factory_->CreateCdm(key_system, mojo::MakeRequest(&cdm_ptr));

MojoCdm::Create(key_system, security_origin, cdm_config, std::move(cdm_ptr),
session_message_cb, session_closed_cb, session_keys_change_cb,
session_expiration_update_cb, cdm_created_cb);
interface_factory_, session_message_cb, session_closed_cb,
session_keys_change_cb, session_expiration_update_cb,
cdm_created_cb);
}

} // namespace media
2 changes: 1 addition & 1 deletion media/mojo/clients/mojo_cdm_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class MojoCdmTest : public ::testing::Test {
}

MojoCdm::Create(key_system, url::Origin::Create(GURL(kTestSecurityOrigin)),
CdmConfig(), std::move(remote_cdm),
CdmConfig(), std::move(remote_cdm), nullptr,
base::Bind(&MockCdmClient::OnSessionMessage,
base::Unretained(&cdm_client_)),
base::Bind(&MockCdmClient::OnSessionClosed,
Expand Down
2 changes: 1 addition & 1 deletion media/test/data/eme_player_js/player_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ PlayerUtils.createPlayer = function(video, testConfig) {
switch (keySystem) {
case WIDEVINE_KEYSYSTEM:
return WidevinePlayer;
case CDM_PROXY_TEST_KEYSYSTEM:
case CLEARKEY:
case EXTERNAL_CLEARKEY:
case MESSAGE_TYPE_TEST_KEYSYSTEM:
Expand All @@ -296,7 +297,6 @@ PlayerUtils.createPlayer = function(video, testConfig) {
case PLATFORM_VERIFICATION_TEST_KEYSYSTEM:
case VERIFY_HOST_FILES_TEST_KEYSYSTEM:
case STORAGE_ID_TEST_KEYSYSTEM:
case CDM_PROXY_TEST_KEYSYSTEM:
return UnitTestPlayer;
default:
Utils.timeLog(keySystem + ' is not a known key system');
Expand Down

0 comments on commit d908b6e

Please sign in to comment.