Commit a80616b7 authored by Xiaohan Wang's avatar Xiaohan Wang Committed by Commit Bot

media: Support NewKeyCB registration in CdmContext

All players/decoders handling encrypted media should implement the logic
to suspend playback when the decryption key is not available, and then
resume playback when a new usable decryption key is added. Hence,
there's a common need for the players/decoders to be notified when such
an event (new usable key) happens.

Since CdmContext is the common interface bridging players/decoders and
the CDM, this CL adds RegisterNewKeyCB() to CdmContext.

To facilitate the implementation of RegisterNewKeyCB() in various CDMs
and CdmProxies, CallbackRegistry is also introduced, which can manage
callback registration, notification and unregistration. Note that
there's base::CallbackList which is similar, but that class is not as
simple and is not thread-safe.

This CL also updates D3D11CdmProxy to use CallbackRegistry to manage
new key callbacks. D3D11VideoDecoderImpl is also updated to be able to
register new key callbacks.

Bug: 821288
Test: Covered by existing tests.
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel
Change-Id: I0380f934efb63d3b3c893d909e0d78f5f8e082f4
Reviewed-on: https://chromium-review.googlesource.com/1014245
Commit-Queue: Xiaohan Wang <xhwang@chromium.org>
Reviewed-by: default avatarFrank Liberato <liberato@chromium.org>
Cr-Commit-Position: refs/heads/master@{#551468}
parent 7951767c
......@@ -81,6 +81,7 @@ source_set("base") {
"byte_queue.cc",
"byte_queue.h",
"callback_holder.h",
"callback_registry.h",
"cdm_callback_promise.cc",
"cdm_callback_promise.h",
"cdm_config.h",
......@@ -456,6 +457,7 @@ source_set("unit_tests") {
"bind_to_current_loop_unittest.cc",
"bit_reader_unittest.cc",
"callback_holder_unittest.cc",
"callback_registry_unittest.cc",
"channel_mixer_unittest.cc",
"channel_mixing_matrix_unittest.cc",
"container_names_unittest.cc",
......
// Copyright 2018 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_BASE_CALLBACK_REGISTRY_H_
#define MEDIA_BASE_CALLBACK_REGISTRY_H_
#include <stdint.h>
#include <map>
#include <memory>
#include "base/callback.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/synchronization/lock.h"
#include "media/base/bind_to_current_loop.h"
namespace media {
// A class that keeps a callback registered. The callback will be unregistered
// upon destruction of this object.
class CallbackRegistration {
public:
CallbackRegistration() = default;
virtual ~CallbackRegistration() = default;
private:
DISALLOW_COPY_AND_ASSIGN(CallbackRegistration);
};
template <typename Sig>
class CallbackRegistry;
// A helper class that can register, unregister callbacks, and notify registered
// callbacks. This class is thread safe: all methods can be called on any
// thread. The CallbackRegistry must outlive all CallbackRegistrations returned
// by Register().
// TODO(xhwang): This class is similar to base::CallbackList, but is simpler,
// and provides thread safty. Consider merging these two.
template <typename... Args>
class CallbackRegistry<void(Args...)> {
public:
using CallbackType = base::RepeatingCallback<void(Args...)>;
CallbackRegistry() = default;
~CallbackRegistry() = default;
std::unique_ptr<CallbackRegistration> Register(CallbackType cb)
WARN_UNUSED_RESULT {
base::AutoLock lock(lock_);
DCHECK(cb);
uint32_t registration_id = ++next_registration_id_;
DVLOG(1) << __func__ << ": registration_id = " << registration_id;
// Use BindToCurrentLoop so that the callbacks are always posted to the
// thread where Register() is called. Also, this helps avoid reentrancy
// and deadlock issues, e.g. Register() is called in one of the callbacks.
callbacks_[registration_id] = BindToCurrentLoop(std::move(cb));
return std::make_unique<RegistrationImpl>(this, registration_id);
}
void Notify(Args&&... args) {
DVLOG(1) << __func__;
base::AutoLock lock(lock_);
for (auto const& entry : callbacks_)
entry.second.Run(std::forward<Args>(args)...);
}
private:
class RegistrationImpl : public CallbackRegistration {
public:
RegistrationImpl(CallbackRegistry<void(Args...)>* registry,
uint32_t registration_id)
: registry_(registry), registration_id_(registration_id) {}
~RegistrationImpl() override { registry_->Unregister(registration_id_); }
private:
CallbackRegistry<void(Args...)>* registry_ = nullptr;
uint32_t registration_id_ = 0;
DISALLOW_COPY_AND_ASSIGN(RegistrationImpl);
};
void Unregister(uint32_t registration_id) {
DVLOG(1) << __func__ << ": registration_id = " << registration_id;
base::AutoLock lock(lock_);
size_t num_callbacks_removed = callbacks_.erase(registration_id);
DCHECK_EQ(num_callbacks_removed, 1u);
}
base::Lock lock_;
uint32_t next_registration_id_ = 0;
std::map<uint32_t, CallbackType> callbacks_;
DISALLOW_COPY_AND_ASSIGN(CallbackRegistry);
};
using ClosureRegistry = CallbackRegistry<void()>;
} // namespace media
#endif // MEDIA_BASE_CALLBACK_REGISTRY_H_
// Copyright 2018 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.
#include "media/base/callback_registry.h"
#include "base/callback.h"
#include "base/macros.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
using ::testing::_;
using ::testing::Invoke;
using ::testing::IsNull;
class CallbackRegistryTest : public testing::Test {
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_;
};
TEST_F(CallbackRegistryTest, RegisterWithNoParam) {
ClosureRegistry registry;
base::MockCallback<base::RepeatingCallback<void()>> callback;
auto registration = registry.Register(callback.Get());
EXPECT_TRUE(registration);
EXPECT_CALL(callback, Run());
registry.Notify();
scoped_task_environment_.RunUntilIdle();
}
TEST_F(CallbackRegistryTest, RegisterWithOneParam) {
CallbackRegistry<void(int)> registry;
base::MockCallback<base::RepeatingCallback<void(int)>> callback;
auto registration = registry.Register(callback.Get());
EXPECT_TRUE(registration);
EXPECT_CALL(callback, Run(1));
registry.Notify(1);
scoped_task_environment_.RunUntilIdle();
}
TEST_F(CallbackRegistryTest, RegisterWithTwoParams) {
CallbackRegistry<void(int, int)> registry;
base::MockCallback<base::RepeatingCallback<void(int, int)>> callback;
auto registration = registry.Register(callback.Get());
EXPECT_TRUE(registration);
EXPECT_CALL(callback, Run(1, 2));
registry.Notify(1, 2);
scoped_task_environment_.RunUntilIdle();
}
TEST_F(CallbackRegistryTest, RegisterWithMoveOnlyParam) {
CallbackRegistry<void(std::unique_ptr<int>)> registry;
base::MockCallback<base::RepeatingCallback<void(std::unique_ptr<int>)>>
callback;
auto registration = registry.Register(callback.Get());
EXPECT_TRUE(registration);
EXPECT_CALL(callback, Run(_));
registry.Notify(std::make_unique<int>(1));
scoped_task_environment_.RunUntilIdle();
}
TEST_F(CallbackRegistryTest, RegisterWithPointerParam) {
CallbackRegistry<void(int*)> registry;
base::MockCallback<base::RepeatingCallback<void(int*)>> callback;
auto registration = registry.Register(callback.Get());
EXPECT_TRUE(registration);
EXPECT_CALL(callback, Run(IsNull()));
registry.Notify(nullptr);
scoped_task_environment_.RunUntilIdle();
}
TEST_F(CallbackRegistryTest, RegisterWithReferenceParam) {
CallbackRegistry<void(const int&)> registry;
base::MockCallback<base::RepeatingCallback<void(const int&)>> callback;
auto registration = registry.Register(callback.Get());
EXPECT_TRUE(registration);
int i = 1;
EXPECT_CALL(callback, Run(i));
registry.Notify(i);
scoped_task_environment_.RunUntilIdle();
}
TEST_F(CallbackRegistryTest, RegisterAfterNotify) {
ClosureRegistry registry;
base::MockCallback<base::RepeatingClosure> callback_1;
auto registration_1 = registry.Register(callback_1.Get());
EXPECT_TRUE(registration_1);
EXPECT_CALL(callback_1, Run());
registry.Notify();
scoped_task_environment_.RunUntilIdle();
base::MockCallback<base::RepeatingClosure> callback_2;
auto registration_2 = registry.Register(callback_2.Get());
EXPECT_TRUE(registration_2);
EXPECT_CALL(callback_1, Run());
EXPECT_CALL(callback_2, Run());
registry.Notify();
scoped_task_environment_.RunUntilIdle();
}
TEST_F(CallbackRegistryTest, EmptyRegistry) {
ClosureRegistry registry;
registry.Notify();
}
TEST_F(CallbackRegistryTest, UnregisterCallback) {
ClosureRegistry registry;
base::MockCallback<base::RepeatingClosure> callback_1;
base::MockCallback<base::RepeatingClosure> callback_2;
auto registration_1 = registry.Register(callback_1.Get());
auto registration_2 = registry.Register(callback_2.Get());
EXPECT_TRUE(registration_1);
EXPECT_TRUE(registration_2);
EXPECT_CALL(callback_1, Run());
EXPECT_CALL(callback_2, Run());
registry.Notify();
scoped_task_environment_.RunUntilIdle();
registration_1.reset();
EXPECT_CALL(callback_2, Run());
registry.Notify();
scoped_task_environment_.RunUntilIdle();
registration_2.reset();
registry.Notify();
scoped_task_environment_.RunUntilIdle();
}
TEST_F(CallbackRegistryTest, RegisterDuringNotification) {
ClosureRegistry registry;
base::MockCallback<base::RepeatingClosure> callback_1;
base::MockCallback<base::RepeatingClosure> callback_2;
auto registration_1 = registry.Register(callback_1.Get());
std::unique_ptr<CallbackRegistration> registration_2;
EXPECT_TRUE(registration_1);
// Register callback_2 during callback_1's notification run.
EXPECT_CALL(callback_1, Run()).WillOnce(Invoke([&]() {
registration_2 = registry.Register(callback_2.Get());
}));
registry.Notify();
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(registration_2);
EXPECT_CALL(callback_1, Run());
EXPECT_CALL(callback_2, Run());
registry.Notify();
scoped_task_environment_.RunUntilIdle();
}
} // namespace
} // namespace media
......@@ -4,12 +4,19 @@
#include "media/base/cdm_context.h"
#include "media/base/callback_registry.h"
namespace media {
CdmContext::CdmContext() = default;
CdmContext::~CdmContext() = default;
std::unique_ptr<CallbackRegistration> CdmContext::RegisterNewKeyCB(
base::RepeatingClosure new_key_cb) {
return nullptr;
}
Decryptor* CdmContext::GetDecryptor() {
return nullptr;
}
......
......@@ -13,6 +13,7 @@
namespace media {
class CallbackRegistration;
class CdmProxyContext;
class Decryptor;
class MediaCryptoContext;
......@@ -35,6 +36,20 @@ class MEDIA_EXPORT CdmContext {
virtual ~CdmContext();
// Registers a callback which will be called when an additional usable key is
// available in the CDM. Can be called multiple times to register multiple
// callbacks, all of which will be called when a new usable key is available.
// Lifetime: The caller should keep the returned CallbackRegistration object
// to keep the callback registered. The callback will be unregistered upon the
// destruction of the returned CallbackRegistration object. The returned
// CallbackRegistration object can be destructed on any thread.
// Thread Model: Can be called on any thread. The registered callback will
// always be called on the thread where RegisterNewKeyCB() is called.
// TODO(xhwang): We are not using base::CallbackList because it is not thread-
// safe. Consider refactoring base::CallbackList to avoid code duplication.
virtual std::unique_ptr<CallbackRegistration> RegisterNewKeyCB(
base::RepeatingClosure new_key_cb);
// Gets the Decryptor object associated with the CDM. Returns nullptr if the
// CDM does not support a Decryptor (i.e. platform-based CDMs where decryption
// occurs implicitly along with decoding).
......
......@@ -18,6 +18,7 @@
#include "crypto/encryptor.h"
#include "crypto/symmetric_key.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/callback_registry.h"
#include "media/base/cdm_promise.h"
#include "media/base/decoder_buffer.h"
#include "media/base/decrypt_config.h"
......@@ -147,10 +148,7 @@ void AesDecryptor::SessionIdDecryptionKeyMap::Erase(
key_list_.erase(position);
}
enum ClearBytesBufferSel {
kSrcContainsClearBytes,
kDstContainsClearBytes
};
enum ClearBytesBufferSel { kSrcContainsClearBytes, kDstContainsClearBytes };
static void CopySubsamples(const std::vector<SubsampleEntry>& subsamples,
const ClearBytesBufferSel sel,
......@@ -554,6 +552,12 @@ CdmContext* AesDecryptor::GetCdmContext() {
return this;
}
std::unique_ptr<CallbackRegistration> AesDecryptor::RegisterNewKeyCB(
base::RepeatingClosure new_key_cb) {
NOTIMPLEMENTED();
return nullptr;
}
Decryptor* AesDecryptor::GetDecryptor() {
return this;
}
......@@ -585,8 +589,8 @@ void AesDecryptor::Decrypt(StreamType stream_type,
scoped_refptr<DecoderBuffer> decrypted;
if (!encrypted->decrypt_config()->is_encrypted()) {
decrypted = DecoderBuffer::CopyFrom(encrypted->data(),
encrypted->data_size());
decrypted =
DecoderBuffer::CopyFrom(encrypted->data(), encrypted->data_size());
} else {
const std::string& key_id = encrypted->decrypt_config()->key_id();
base::AutoLock auto_lock(key_map_lock_);
......@@ -757,8 +761,7 @@ CdmKeysInfo AesDecryptor::GenerateKeysInfoList(
}
AesDecryptor::DecryptionKey::DecryptionKey(const std::string& secret)
: secret_(secret) {
}
: secret_(secret) {}
AesDecryptor::DecryptionKey::~DecryptionKey() = default;
......
......@@ -61,6 +61,8 @@ class MEDIA_EXPORT AesDecryptor : public ContentDecryptionModule,
CdmContext* GetCdmContext() override;
// CdmContext implementation.
std::unique_ptr<CallbackRegistration> RegisterNewKeyCB(
base::RepeatingClosure new_key_cb) override;
Decryptor* GetDecryptor() override;
int GetCdmId() const override;
......@@ -180,7 +182,7 @@ class MEDIA_EXPORT AesDecryptor : public ContentDecryptionModule,
// 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.
KeyIdToSessionKeysMap key_map_; // Protected by |key_map_lock_|.
KeyIdToSessionKeysMap key_map_; // Protected by |key_map_lock_|.
mutable base::Lock key_map_lock_; // Protects the |key_map_|.
// Keeps track of current open sessions and their type. Although publicly
......
......@@ -19,6 +19,7 @@
#include "base/trace_event/trace_event.h"
#include "components/crash/core/common/crash_key.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/callback_registry.h"
#include "media/base/cdm_initialized_promise.h"
#include "media/base/cdm_key_information.h"
#include "media/base/channel_layout.h"
......@@ -684,6 +685,12 @@ CdmContext* CdmAdapter::GetCdmContext() {
return this;
}
std::unique_ptr<CallbackRegistration> CdmAdapter::RegisterNewKeyCB(
base::RepeatingClosure new_key_cb) {
NOTIMPLEMENTED();
return nullptr;
}
Decryptor* CdmAdapter::GetDecryptor() {
DCHECK(task_runner_->BelongsToCurrentThread());
return this;
......
......@@ -86,6 +86,8 @@ class MEDIA_EXPORT CdmAdapter : public ContentDecryptionModule,
CdmContext* GetCdmContext() final;
// CdmContext implementation.
std::unique_ptr<CallbackRegistration> RegisterNewKeyCB(
base::RepeatingClosure new_key_cb) final;
Decryptor* GetDecryptor() final;
int GetCdmId() const final;
......
......@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "base/logging.h"
#include "media/base/callback_registry.h"
#include "media/base/cdm_context.h"
#include "media/base/cdm_proxy_context.h"
......@@ -105,11 +106,18 @@ class D3D11CdmContext : public CdmContext {
}
// CdmContext implementation.
std::unique_ptr<CallbackRegistration> RegisterNewKeyCB(
base::RepeatingClosure new_key_cb) override {
return new_key_callbacks_.Register(std::move(new_key_cb));
}
CdmProxyContext* GetCdmProxyContext() override { return &cdm_proxy_context_; }
private:
D3D11CdmProxyContext cdm_proxy_context_;
// TODO(rkuroiwa): Call Notify() when new usable key is available.
ClosureRegistry new_key_callbacks_;
base::WeakPtrFactory<D3D11CdmContext> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(D3D11CdmContext);
......
......@@ -10,6 +10,7 @@
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/cdm_context.h"
#include "media/base/decoder_buffer.h"
#include "media/base/video_decoder_config.h"
#include "media/base/video_frame.h"
......@@ -160,6 +161,13 @@ void D3D11VideoDecoderImpl::Initialize(
std::make_unique<H264Decoder>(std::make_unique<D3D11H264Accelerator>(
this, video_decoder, video_device_, video_context_));
// |cdm_context| could be null for clear playback.
if (cdm_context) {
new_key_callback_registration_ =
cdm_context->RegisterNewKeyCB(base::BindRepeating(
&D3D11VideoDecoderImpl::NotifyNewKey, weak_factory_.GetWeakPtr()));
}
state_ = State::kRunning;
std::move(init_cb_).Run(true);
}
......@@ -370,6 +378,11 @@ base::WeakPtr<D3D11VideoDecoderImpl> D3D11VideoDecoderImpl::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
void D3D11VideoDecoderImpl::NotifyNewKey() {
// TODO(rkuroiwa): Implement this method.
NOTIMPLEMENTED();
}
void D3D11VideoDecoderImpl::NotifyError(const char* reason) {
state_ = State::kError;
DLOG(ERROR) << reason;
......
......@@ -16,6 +16,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "gpu/ipc/service/command_buffer_stub.h"
#include "media/base/callback_registry.h"
#include "media/base/video_decoder.h"
#include "media/gpu/gles2_decoder_helper.h"
#include "media/gpu/media_gpu_export.h"
......@@ -27,7 +28,7 @@ namespace media {
class MEDIA_GPU_EXPORT D3D11VideoDecoderImpl : public VideoDecoder,
public D3D11VideoDecoderClient {
public:
D3D11VideoDecoderImpl(
explicit D3D11VideoDecoderImpl(
base::RepeatingCallback<gpu::CommandBufferStub*()> get_stub_cb);
~D3D11VideoDecoderImpl() override;
......@@ -72,6 +73,9 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoderImpl : public VideoDecoder,
void OnMailboxReleased(scoped_refptr<D3D11PictureBuffer> buffer,
const gpu::SyncToken& sync_token);
// Callback to notify that new usable key is available.
void NotifyNewKey();
// Enter the kError state. This will fail any pending |init_cb_| and / or
// pending decode as well.
void NotifyError(const char* reason);
......@@ -104,6 +108,9 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoderImpl : public VideoDecoder,
State state_ = State::kInitializing;
// Callback registration to keep the new key callback registered.
std::unique_ptr<CallbackRegistration> new_key_callback_registration_;
base::WeakPtrFactory<D3D11VideoDecoderImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(D3D11VideoDecoderImpl);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment