Skip to content

Commit

Permalink
media: CdmFactory creates CDM (MediaKeys) asynchronously.
Browse files Browse the repository at this point in the history
This CL fixes the EME stack down to CdmFactory. The real aync CDM creation
will be fixed in a separate CL.

For unprefixed EME, since it's promise based, async creation of CDM fits easily.

For prefixed EME, GenerateKeyRequest() can be called multiple times without
waiting for the real CDM to be created/loaded. ProxyDecryptor is modified to
handle this case.

BUG=469003
TEST=All existing tests pass.

Review URL: https://codereview.chromium.org/1070853004

Cr-Commit-Position: refs/heads/master@{#324942}
  • Loading branch information
xhwang-chromium authored and Commit bot committed Apr 13, 2015
1 parent 4750c60 commit 9bd8c73
Show file tree
Hide file tree
Showing 17 changed files with 361 additions and 242 deletions.
67 changes: 34 additions & 33 deletions content/renderer/media/android/webmediaplayer_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1527,48 +1527,35 @@ WebMediaPlayerAndroid::GenerateKeyRequestInternal(
if (!IsKeySystemSupported(key_system))
return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;

// We do not support run-time switching between key systems for now.
if (current_key_system_.empty()) {
if (!proxy_decryptor_) {
proxy_decryptor_.reset(new media::ProxyDecryptor(
media_permission_,
base::Bind(&WebMediaPlayerAndroid::OnKeyAdded,
weak_factory_.GetWeakPtr()),
base::Bind(&WebMediaPlayerAndroid::OnKeyError,
weak_factory_.GetWeakPtr()),
base::Bind(&WebMediaPlayerAndroid::OnKeyMessage,
weak_factory_.GetWeakPtr())));
}
if (!proxy_decryptor_) {
DCHECK(current_key_system_.empty());
proxy_decryptor_.reset(new media::ProxyDecryptor(
media_permission_, base::Bind(&WebMediaPlayerAndroid::OnKeyAdded,
weak_factory_.GetWeakPtr()),
base::Bind(&WebMediaPlayerAndroid::OnKeyError,
weak_factory_.GetWeakPtr()),
base::Bind(&WebMediaPlayerAndroid::OnKeyMessage,
weak_factory_.GetWeakPtr())));

GURL security_origin(frame_->document().securityOrigin().toString());
if (!proxy_decryptor_->InitializeCDM(cdm_factory_, key_system,
security_origin)) {
return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
}

// Set the CDM onto the media player.
cdm_context_ = proxy_decryptor_->GetCdmContext();

if (is_player_initialized_)
SetCdmInternal(base::Bind(&media::IgnoreCdmAttached));

proxy_decryptor_->CreateCdm(
cdm_factory_, key_system, security_origin,
base::Bind(&WebMediaPlayerAndroid::OnCdmContextReady,
weak_factory_.GetWeakPtr()));
current_key_system_ = key_system;
} else if (key_system != current_key_system_) {
return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
}

// We do not support run-time switching between key systems for now.
DCHECK(!current_key_system_.empty());
if (key_system != current_key_system_)
return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;

media::EmeInitDataType init_data_type = init_data_type_;
if (init_data_type == media::EmeInitDataType::UNKNOWN)
init_data_type = GuessInitDataType(init_data, init_data_length);

// TODO(xhwang): We assume all streams are from the same container (thus have
// the same "type") for now. In the future, the "type" should be passed down
// from the application.
if (!proxy_decryptor_->GenerateKeyRequest(
init_data_type, init_data, init_data_length)) {
current_key_system_.clear();
return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
}
proxy_decryptor_->GenerateKeyRequest(init_data_type, init_data,
init_data_length);

return WebMediaPlayer::MediaKeyExceptionNoError;
}
Expand Down Expand Up @@ -1772,6 +1759,20 @@ void WebMediaPlayerAndroid::OnWaitingForDecryptionKey() {
client_->didResumePlaybackBlockedForKey();
}

void WebMediaPlayerAndroid::OnCdmContextReady(media::CdmContext* cdm_context) {
DCHECK(!cdm_context_);

if (!cdm_context) {
LOG(ERROR) << "CdmContext not available (e.g. CDM creation failed).";
return;
}

cdm_context_ = cdm_context;

if (is_player_initialized_)
SetCdmInternal(base::Bind(&media::IgnoreCdmAttached));
}

void WebMediaPlayerAndroid::SetCdmInternal(
const media::CdmAttachedCB& cdm_attached_cb) {
DCHECK(cdm_context_ && is_player_initialized_);
Expand Down
3 changes: 3 additions & 0 deletions content/renderer/media/android/webmediaplayer_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,9 @@ class WebMediaPlayerAndroid : public blink::WebMediaPlayer,
MediaKeyException CancelKeyRequestInternal(const std::string& key_system,
const std::string& session_id);

// Called when |cdm_context| is ready.
void OnCdmContextReady(media::CdmContext* cdm_context);

// Sets the CDM. Should only be called when |is_player_initialized_| is true
// and a new non-null |cdm_context_| is available. Fires |cdm_attached_cb_|
// with the result after the CDM is attached.
Expand Down
55 changes: 32 additions & 23 deletions content/renderer/media/crypto/render_cdm_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

#include "content/renderer/media/crypto/render_cdm_factory.h"

#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_loop_proxy.h"
#include "media/base/key_systems.h"
#include "media/cdm/aes_decryptor.h"
#include "url/gurl.h"

#if defined(ENABLE_PEPPER_CDMS)
#include "content/renderer/media/crypto/ppapi_decryptor.h"
#elif defined(ENABLE_BROWSER_CDMS)
Expand Down Expand Up @@ -37,7 +39,7 @@ RenderCdmFactory::~RenderCdmFactory() {
DCHECK(thread_checker_.CalledOnValidThread());
}

scoped_ptr<media::MediaKeys> RenderCdmFactory::Create(
void RenderCdmFactory::Create(
const std::string& key_system,
bool allow_distinctive_identifier,
bool allow_persistent_state,
Expand All @@ -46,38 +48,45 @@ scoped_ptr<media::MediaKeys> RenderCdmFactory::Create(
const media::SessionClosedCB& session_closed_cb,
const media::LegacySessionErrorCB& legacy_session_error_cb,
const media::SessionKeysChangeCB& session_keys_change_cb,
const media::SessionExpirationUpdateCB& session_expiration_update_cb) {
const media::SessionExpirationUpdateCB& session_expiration_update_cb,
const CdmCreatedCB& cdm_created_cb) {
DCHECK(thread_checker_.CalledOnValidThread());

if (!security_origin.is_valid())
return nullptr;
if (!security_origin.is_valid()) {
base::MessageLoopProxy::current()->PostTask(
FROM_HERE, base::Bind(cdm_created_cb, nullptr));
return;
}

scoped_ptr<media::MediaKeys> cdm;

if (media::CanUseAesDecryptor(key_system)) {
// TODO(sandersd): Currently the prefixed API always allows distinctive
// identifiers and persistent state. Once that changes we can sanity check
// here that neither is allowed for AesDecryptor, since it does not support
// them and should never be configured that way. http://crbug.com/455271
return scoped_ptr<media::MediaKeys>(
new media::AesDecryptor(security_origin, session_message_cb,
session_closed_cb, session_keys_change_cb));
}

cdm.reset(new media::AesDecryptor(security_origin, session_message_cb,
session_closed_cb,
session_keys_change_cb));
} else {
#if defined(ENABLE_PEPPER_CDMS)
return scoped_ptr<media::MediaKeys>(PpapiDecryptor::Create(
key_system, allow_distinctive_identifier, allow_persistent_state,
security_origin, create_pepper_cdm_cb_, session_message_cb,
session_closed_cb, legacy_session_error_cb, session_keys_change_cb,
session_expiration_update_cb));
cdm = PpapiDecryptor::Create(
key_system, allow_distinctive_identifier, allow_persistent_state,
security_origin, create_pepper_cdm_cb_, session_message_cb,
session_closed_cb, legacy_session_error_cb, session_keys_change_cb,
session_expiration_update_cb);
#elif defined(ENABLE_BROWSER_CDMS)
DCHECK(allow_distinctive_identifier);
DCHECK(allow_persistent_state);
return scoped_ptr<media::MediaKeys>(ProxyMediaKeys::Create(
key_system, security_origin, manager_, session_message_cb,
session_closed_cb, legacy_session_error_cb, session_keys_change_cb,
session_expiration_update_cb));
#else
return nullptr;
DCHECK(allow_distinctive_identifier);
DCHECK(allow_persistent_state);
cdm = ProxyMediaKeys::Create(
key_system, security_origin, manager_, session_message_cb,
session_closed_cb, legacy_session_error_cb, session_keys_change_cb,
session_expiration_update_cb);
#endif // defined(ENABLE_PEPPER_CDMS)
}

base::MessageLoopProxy::current()->PostTask(
FROM_HERE, base::Bind(cdm_created_cb, base::Passed(&cdm)));
}

} // namespace content
7 changes: 4 additions & 3 deletions content/renderer/media/crypto/render_cdm_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ class RenderCdmFactory : public media::CdmFactory, public RenderFrameObserver {

~RenderCdmFactory() override;

scoped_ptr<media::MediaKeys> Create(
// CdmFactory implementation.
void Create(
const std::string& key_system,
bool allow_distinctive_identifier,
bool allow_persistent_state,
Expand All @@ -48,8 +49,8 @@ class RenderCdmFactory : public media::CdmFactory, public RenderFrameObserver {
const media::SessionClosedCB& session_closed_cb,
const media::LegacySessionErrorCB& legacy_session_error_cb,
const media::SessionKeysChangeCB& session_keys_change_cb,
const media::SessionExpirationUpdateCB& session_expiration_update_cb)
override;
const media::SessionExpirationUpdateCB& session_expiration_update_cb,
const CdmCreatedCB& cdm_created_cb) override;

private:
#if defined(ENABLE_PEPPER_CDMS)
Expand Down
9 changes: 7 additions & 2 deletions media/base/cdm_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ namespace media {

class MEDIA_EXPORT CdmFactory {
public:
using CdmCreatedCB = base::Callback<void(scoped_ptr<MediaKeys>)>;

CdmFactory();
virtual ~CdmFactory();

virtual scoped_ptr<MediaKeys> Create(
// Creates a CDM for |key_system| and returns it through |cdm_created_cb|
// asynchronously.
virtual void Create(
const std::string& key_system,
bool allow_distinctive_identifier,
bool allow_persistent_state,
Expand All @@ -29,7 +33,8 @@ class MEDIA_EXPORT CdmFactory {
const SessionClosedCB& session_closed_cb,
const LegacySessionErrorCB& legacy_session_error_cb,
const SessionKeysChangeCB& session_keys_change_cb,
const SessionExpirationUpdateCB& session_expiration_update_cb) = 0;
const SessionExpirationUpdateCB& session_expiration_update_cb,
const CdmCreatedCB& cdm_created_cb) = 0;

private:
DISALLOW_COPY_AND_ASSIGN(CdmFactory);
Expand Down
67 changes: 44 additions & 23 deletions media/blink/cdm_session_adapter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "media/base/cdm_promise.h"
#include "media/base/key_systems.h"
#include "media/base/media_keys.h"
#include "media/blink/webcontentdecryptionmodule_impl.h"
#include "media/blink/webcontentdecryptionmodulesession_impl.h"
#include "url/gurl.h"

Expand All @@ -26,33 +27,34 @@ CdmSessionAdapter::CdmSessionAdapter() : weak_ptr_factory_(this) {

CdmSessionAdapter::~CdmSessionAdapter() {}

bool CdmSessionAdapter::Initialize(CdmFactory* cdm_factory,
const std::string& key_system,
bool allow_distinctive_identifier,
bool allow_persistent_state,
const GURL& security_origin) {
key_system_ = key_system;
key_system_uma_prefix_ =
kMediaEME + GetKeySystemNameForUMA(key_system) + kDot;

void CdmSessionAdapter::CreateCdm(
CdmFactory* cdm_factory,
const std::string& key_system,
bool allow_distinctive_identifier,
bool allow_persistent_state,
const GURL& security_origin,
blink::WebContentDecryptionModuleResult result) {
// Note: WebContentDecryptionModuleImpl::Create() calls this method without
// holding a reference to the CdmSessionAdapter. Bind OnCdmCreated() with
// |this| instead of |weak_this| to prevent |this| from being desctructed.
base::WeakPtr<CdmSessionAdapter> weak_this = weak_ptr_factory_.GetWeakPtr();
media_keys_ = cdm_factory->Create(
cdm_factory->Create(
key_system, allow_distinctive_identifier, allow_persistent_state,
security_origin,
base::Bind(&CdmSessionAdapter::OnSessionMessage, weak_this),
base::Bind(&CdmSessionAdapter::OnSessionClosed, weak_this),
base::Bind(&CdmSessionAdapter::OnLegacySessionError, weak_this),
base::Bind(&CdmSessionAdapter::OnSessionKeysChange, weak_this),
base::Bind(&CdmSessionAdapter::OnSessionExpirationUpdate, weak_this));
return media_keys_.get() != nullptr;
base::Bind(&CdmSessionAdapter::OnSessionExpirationUpdate, weak_this),
base::Bind(&CdmSessionAdapter::OnCdmCreated, this, key_system, result));
}

void CdmSessionAdapter::SetServerCertificate(
const uint8* server_certificate,
int server_certificate_length,
scoped_ptr<SimpleCdmPromise> promise) {
media_keys_->SetServerCertificate(
server_certificate, server_certificate_length, promise.Pass());
cdm_->SetServerCertificate(server_certificate, server_certificate_length,
promise.Pass());
}

WebContentDecryptionModuleSessionImpl* CdmSessionAdapter::CreateSession() {
Expand Down Expand Up @@ -81,37 +83,35 @@ void CdmSessionAdapter::InitializeNewSession(
int init_data_length,
MediaKeys::SessionType session_type,
scoped_ptr<NewSessionCdmPromise> promise) {
media_keys_->CreateSessionAndGenerateRequest(session_type, init_data_type,
init_data, init_data_length,
promise.Pass());
cdm_->CreateSessionAndGenerateRequest(session_type, init_data_type, init_data,
init_data_length, promise.Pass());
}

void CdmSessionAdapter::LoadSession(MediaKeys::SessionType session_type,
const std::string& session_id,
scoped_ptr<NewSessionCdmPromise> promise) {
media_keys_->LoadSession(session_type, session_id, promise.Pass());
cdm_->LoadSession(session_type, session_id, promise.Pass());
}

void CdmSessionAdapter::UpdateSession(const std::string& session_id,
const uint8* response,
int response_length,
scoped_ptr<SimpleCdmPromise> promise) {
media_keys_->UpdateSession(session_id, response, response_length,
promise.Pass());
cdm_->UpdateSession(session_id, response, response_length, promise.Pass());
}

void CdmSessionAdapter::CloseSession(const std::string& session_id,
scoped_ptr<SimpleCdmPromise> promise) {
media_keys_->CloseSession(session_id, promise.Pass());
cdm_->CloseSession(session_id, promise.Pass());
}

void CdmSessionAdapter::RemoveSession(const std::string& session_id,
scoped_ptr<SimpleCdmPromise> promise) {
media_keys_->RemoveSession(session_id, promise.Pass());
cdm_->RemoveSession(session_id, promise.Pass());
}

CdmContext* CdmSessionAdapter::GetCdmContext() {
return media_keys_->GetCdmContext();
return cdm_->GetCdmContext();
}

const std::string& CdmSessionAdapter::GetKeySystem() const {
Expand All @@ -122,6 +122,27 @@ const std::string& CdmSessionAdapter::GetKeySystemUMAPrefix() const {
return key_system_uma_prefix_;
}

void CdmSessionAdapter::OnCdmCreated(
const std::string& key_system,
blink::WebContentDecryptionModuleResult result,
scoped_ptr<MediaKeys> cdm) {
DVLOG(2) << __FUNCTION__;
if (!cdm) {
result.completeWithError(
blink::WebContentDecryptionModuleExceptionNotSupportedError, 0,
"Failed to create the CDM instance.");
return;
}

key_system_ = key_system;
key_system_uma_prefix_ =
kMediaEME + GetKeySystemNameForUMA(key_system) + kDot;
cdm_ = cdm.Pass();

result.completeWithContentDecryptionModule(
new WebContentDecryptionModuleImpl(this));
}

void CdmSessionAdapter::OnSessionMessage(
const std::string& session_id,
MediaKeys::MessageType message_type,
Expand Down
Loading

0 comments on commit 9bd8c73

Please sign in to comment.