diff options
Diffstat (limited to 'chromium/media/mojo')
63 files changed, 1064 insertions, 618 deletions
diff --git a/chromium/media/mojo/clients/mojo_audio_decoder.cc b/chromium/media/mojo/clients/mojo_audio_decoder.cc index 2bf1b1a59b9..76259add30c 100644 --- a/chromium/media/mojo/clients/mojo_audio_decoder.cc +++ b/chromium/media/mojo/clients/mojo_audio_decoder.cc @@ -51,10 +51,6 @@ AudioDecoderType MojoAudioDecoder::GetDecoderType() const { return decoder_type_; } -std::string MojoAudioDecoder::GetDisplayName() const { - return "MojoAudioDecoder"; -} - void MojoAudioDecoder::FailInit(InitCB init_cb, Status err) { task_runner_->PostTask(FROM_HERE, base::BindOnce(std::move(init_cb), std::move(err))); diff --git a/chromium/media/mojo/clients/mojo_audio_decoder.h b/chromium/media/mojo/clients/mojo_audio_decoder.h index fbb85ab5f72..04a40615daa 100644 --- a/chromium/media/mojo/clients/mojo_audio_decoder.h +++ b/chromium/media/mojo/clients/mojo_audio_decoder.h @@ -36,7 +36,6 @@ class MojoAudioDecoder final : public AudioDecoder, // Decoder implementation bool IsPlatformDecoder() const final; bool SupportsDecryption() const final; - std::string GetDisplayName() const override; AudioDecoderType GetDecoderType() const override; // AudioDecoder implementation. diff --git a/chromium/media/mojo/clients/mojo_audio_decoder_unittest.cc b/chromium/media/mojo/clients/mojo_audio_decoder_unittest.cc index daba06f1470..8f4ad6ca715 100644 --- a/chromium/media/mojo/clients/mojo_audio_decoder_unittest.cc +++ b/chromium/media/mojo/clients/mojo_audio_decoder_unittest.cc @@ -65,9 +65,9 @@ class MojoAudioDecoderTest : public ::testing::Test { base::BindOnce(&MojoAudioDecoderTest::ConnectToService, base::Unretained(this), remote_audio_decoder.InitWithNewPipeAndPassReceiver())); - mojo_audio_decoder_.reset( - new MojoAudioDecoder(task_environment_.GetMainThreadTaskRunner(), - std::move(remote_audio_decoder))); + mojo_audio_decoder_ = std::make_unique<MojoAudioDecoder>( + task_environment_.GetMainThreadTaskRunner(), + std::move(remote_audio_decoder)); } ~MojoAudioDecoderTest() override { @@ -91,13 +91,13 @@ class MojoAudioDecoderTest : public ::testing::Test { void RunLoop() { DVLOG(1) << __func__; - run_loop_.reset(new base::RunLoop()); + run_loop_ = std::make_unique<base::RunLoop>(); run_loop_->Run(); } void RunLoopUntilIdle() { DVLOG(1) << __func__; - run_loop_.reset(new base::RunLoop()); + run_loop_ = std::make_unique<base::RunLoop>(); run_loop_->RunUntilIdle(); } diff --git a/chromium/media/mojo/clients/mojo_cdm.cc b/chromium/media/mojo/clients/mojo_cdm.cc index 403c19f040f..f0098346345 100644 --- a/chromium/media/mojo/clients/mojo_cdm.cc +++ b/chromium/media/mojo/clients/mojo_cdm.cc @@ -36,21 +36,24 @@ void RecordConnectionError(bool connection_error_happened) { } // namespace MojoCdm::MojoCdm(mojo::Remote<mojom::ContentDecryptionModule> remote_cdm, - const base::Optional<base::UnguessableToken>& cdm_id, - mojo::PendingRemote<mojom::Decryptor> decryptor_remote, + media::mojom::CdmContextPtr cdm_context, 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)), - cdm_id_(cdm_id), - decryptor_remote_(std::move(decryptor_remote)), + cdm_id_(cdm_context->cdm_id), + decryptor_remote_(std::move(cdm_context->decryptor)), +#if defined(OS_WIN) + requires_media_foundation_renderer_( + cdm_context->requires_media_foundation_renderer), +#endif // defined(OS_WIN) session_message_cb_(session_message_cb), session_closed_cb_(session_closed_cb), session_keys_change_cb_(session_keys_change_cb), session_expiration_update_cb_(session_expiration_update_cb) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DCHECK(cdm_id); + DCHECK(cdm_id_); DVLOG(2) << __func__ << " cdm_id: " << CdmContext::CdmIdToString(base::OptionalOrNullptr(cdm_id_)); DCHECK(session_message_cb_); @@ -58,12 +61,6 @@ MojoCdm::MojoCdm(mojo::Remote<mojom::ContentDecryptionModule> remote_cdm, DCHECK(session_keys_change_cb_); DCHECK(session_expiration_update_cb_); -#if defined(OS_WIN) - // TODO(xhwang): Need a way to implement RequiresMediaFoundationRenderer(). - // The plan is to pass back this info when we create the CDM, e.g. in the - // `cdm_created_cb` of `MojoCdmFactory::Create()`. -#endif // defined(OS_WIN) - remote_cdm_->SetClient(client_receiver_.BindNewEndpointAndPassRemote()); // Report a false event here as a baseline. @@ -273,17 +270,17 @@ Decryptor* MojoCdm::GetDecryptor() { base::Optional<base::UnguessableToken> MojoCdm::GetCdmId() const { // Can be called on a different thread. base::AutoLock auto_lock(lock_); - DVLOG(2) << __func__ << ": cdm_id = " + DVLOG(2) << __func__ << ": cdm_id=" << CdmContext::CdmIdToString(base::OptionalOrNullptr(cdm_id_)); return cdm_id_; } #if defined(OS_WIN) bool MojoCdm::RequiresMediaFoundationRenderer() { - DVLOG(2) << __func__ << " this:" << this - << " is_mf_renderer_content_:" << is_mf_renderer_content_; - - return is_mf_renderer_content_; + base::AutoLock auto_lock(lock_); + DVLOG(2) << __func__ << ": requires_media_foundation_renderer_=" + << requires_media_foundation_renderer_; + return requires_media_foundation_renderer_; } #endif // defined(OS_WIN) diff --git a/chromium/media/mojo/clients/mojo_cdm.h b/chromium/media/mojo/clients/mojo_cdm.h index b0f88e5c01c..ca886cc99cf 100644 --- a/chromium/media/mojo/clients/mojo_cdm.h +++ b/chromium/media/mojo/clients/mojo_cdm.h @@ -44,9 +44,9 @@ class MojoCdm final : public ContentDecryptionModule, public: using MessageType = CdmMessageType; + // All parameters must be non-null. MojoCdm(mojo::Remote<mojom::ContentDecryptionModule> remote_cdm, - const base::Optional<base::UnguessableToken>& cdm_id, - mojo::PendingRemote<mojom::Decryptor> decryptor_remote, + media::mojom::CdmContextPtr cdm_context, const SessionMessageCB& session_message_cb, const SessionClosedCB& session_closed_cb, const SessionKeysChangeCB& session_keys_change_cb, @@ -117,26 +117,31 @@ class MojoCdm final : public ContentDecryptionModule, mojo::AssociatedReceiver<ContentDecryptionModuleClient> client_receiver_{ this}; - // Protects |cdm_id_|, |decryptor_remote_|, |decryptor_| and - // |decryptor_task_runner_| which could be accessed from other threads. - // See CdmContext implementation above. + // Protects |cdm_id_|, |decryptor_remote_|, |decryptor_|, + // |decryptor_task_runner_| and |requires_media_foundation_renderer_|, which + // could be accessed from other threads. See CdmContext implementation above. mutable base::Lock lock_; // CDM ID of the remote CDM. Set after initialization is completed. Must not // be invalid if initialization succeeded. - base::Optional<base::UnguessableToken> cdm_id_; + base::Optional<base::UnguessableToken> cdm_id_ GUARDED_BY(lock_); // The mojo::PendingRemote<mojom::Decryptor> exposed by the remote CDM. Set // after initialization is completed and cleared after |decryptor_| is // created. May be invalid after initialization if the CDM doesn't support a // Decryptor. - mojo::PendingRemote<mojom::Decryptor> decryptor_remote_; + mojo::PendingRemote<mojom::Decryptor> decryptor_remote_ GUARDED_BY(lock_); // Decryptor based on |decryptor_remote_|, lazily created in // GetDecryptor(). Since GetDecryptor() can be called on a different thread, // use |decryptor_task_runner_| to bind |decryptor_| to that thread. - std::unique_ptr<MojoDecryptor> decryptor_; - scoped_refptr<base::SingleThreadTaskRunner> decryptor_task_runner_; + std::unique_ptr<MojoDecryptor> decryptor_ GUARDED_BY(lock_); + scoped_refptr<base::SingleThreadTaskRunner> decryptor_task_runner_ + GUARDED_BY(lock_); + +#if defined(OS_WIN) + bool requires_media_foundation_renderer_ GUARDED_BY(lock_) = false; +#endif // defined(OS_WIN) // Callbacks for firing session events. SessionMessageCB session_message_cb_; @@ -152,11 +157,6 @@ class MojoCdm final : public ContentDecryptionModule, CallbackRegistry<EventCB::RunType> event_callbacks_; -#if defined(OS_WIN) - // The current content is for MediaFoundationRenderer or not. - bool is_mf_renderer_content_ = false; -#endif // defined(OS_WIN) - // This must be the last member. base::WeakPtrFactory<MojoCdm> weak_factory_{this}; diff --git a/chromium/media/mojo/clients/mojo_cdm_factory.cc b/chromium/media/mojo/clients/mojo_cdm_factory.cc index 7d95271465a..8e4732a3e9c 100644 --- a/chromium/media/mojo/clients/mojo_cdm_factory.cc +++ b/chromium/media/mojo/clients/mojo_cdm_factory.cc @@ -15,6 +15,7 @@ #include "media/base/key_systems.h" #include "media/cdm/aes_decryptor.h" #include "media/mojo/clients/mojo_cdm.h" +#include "media/mojo/mojom/content_decryption_module.mojom.h" #include "media/mojo/mojom/interface_factory.mojom.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/remote.h" @@ -30,21 +31,20 @@ void OnCdmCreated( const SessionExpirationUpdateCB& session_expiration_update_cb, CdmCreatedCB cdm_created_cb, mojo::PendingRemote<mojom::ContentDecryptionModule> cdm_remote, - const base::Optional<base::UnguessableToken>& cdm_id, - mojo::PendingRemote<mojom::Decryptor> decryptor, + media::mojom::CdmContextPtr cdm_context, const std::string& error_message) { // Convert from a PendingRemote to Remote so we can verify that it is // connected, this will also check if |cdm_remote| is null. mojo::Remote<mojom::ContentDecryptionModule> remote(std::move(cdm_remote)); - if (!remote || !remote.is_connected()) { + if (!remote || !remote.is_connected() || !cdm_context) { std::move(cdm_created_cb).Run(nullptr, error_message); return; } std::move(cdm_created_cb) .Run(base::MakeRefCounted<MojoCdm>( - std::move(remote), cdm_id, std::move(decryptor), - session_message_cb, session_closed_cb, session_keys_change_cb, + std::move(remote), std::move(cdm_context), session_message_cb, + session_closed_cb, session_keys_change_cb, session_expiration_update_cb), ""); } diff --git a/chromium/media/mojo/clients/mojo_cdm_unittest.cc b/chromium/media/mojo/clients/mojo_cdm_unittest.cc index 08e24fb4722..3eb54feb6ed 100644 --- a/chromium/media/mojo/clients/mojo_cdm_unittest.cc +++ b/chromium/media/mojo/clients/mojo_cdm_unittest.cc @@ -28,7 +28,9 @@ using ::testing::_; using ::testing::DoAll; using ::testing::Invoke; +using ::testing::Return; using ::testing::ReturnNull; +using ::testing::ReturnPointee; using ::testing::StrictMock; using ::testing::WithArg; using ::testing::WithArgs; @@ -68,10 +70,17 @@ class MojoCdmTest : public ::testing::Test { }; MojoCdmTest() = default; - ~MojoCdmTest() override = default; void Initialize(ExpectedResult expected_result) { + EXPECT_CALL(*remote_cdm_, GetCdmContext()) + .WillRepeatedly(Return(&cdm_context_)); + EXPECT_CALL(cdm_context_, GetDecryptor()).WillRepeatedly(ReturnNull()); +#if defined(OS_WIN) + EXPECT_CALL(cdm_context_, RequiresMediaFoundationRenderer()) + .WillRepeatedly(ReturnPointee(&requires_media_foundation_renderer_)); +#endif + if (expected_result == CONNECTION_ERROR_DURING) { // Create() will be successful, so provide a callback that will break // the connection before returning the CDM. @@ -79,18 +88,17 @@ class MojoCdmTest : public ::testing::Test { &MojoCdmTest::ForceConnectionError, base::Unretained(this))); } - MojoCdmService::Create( - &cdm_factory_, &mojo_cdm_service_context_, kClearKeyKeySystem, - CdmConfig(), - base::BindOnce(&MojoCdmTest::OnCdmServiceCreated, + mojo_cdm_service_ = + std::make_unique<MojoCdmService>(&mojo_cdm_service_context_); + mojo_cdm_service_->Initialize( + &cdm_factory_, kClearKeyKeySystem, CdmConfig(), + base::BindOnce(&MojoCdmTest::OnCdmServiceInitialized, base::Unretained(this), expected_result)); } - void OnCdmServiceCreated(ExpectedResult expected_result, - std::unique_ptr<MojoCdmService> cdm_service, - mojo::PendingRemote<mojom::Decryptor> decryptor, - const std::string& error_message) { - mojo_cdm_service_ = std::move(cdm_service); + void OnCdmServiceInitialized(ExpectedResult expected_result, + mojom::CdmContextPtr cdm_context, + const std::string& error_message) { cdm_receiver_ = std::make_unique<mojo::Receiver<mojom::ContentDecryptionModule>>( mojo_cdm_service_.get()); @@ -103,8 +111,7 @@ class MojoCdmTest : public ::testing::Test { mojo::Remote<mojom::ContentDecryptionModule> cdm_remote( cdm_receiver_->BindNewPipeAndPassRemote()); mojo_cdm_ = base::MakeRefCounted<MojoCdm>( - std::move(cdm_remote), mojo_cdm_service_->cdm_id(), - std::move(decryptor), + std::move(cdm_remote), std::move(cdm_context), base::BindRepeating(&MockCdmClient::OnSessionMessage, base::Unretained(&cdm_client_)), base::BindRepeating(&MockCdmClient::OnSessionClosed, @@ -113,7 +120,6 @@ class MojoCdmTest : public ::testing::Test { base::Unretained(&cdm_client_)), base::BindRepeating(&MockCdmClient::OnSessionExpirationUpdate, base::Unretained(&cdm_client_))); - remote_cdm_ = cdm_factory_.GetCreatedCdm(); EXPECT_EQ(kClearKeyKeySystem, remote_cdm_->GetKeySystem()); base::RunLoop().RunUntilIdle(); } @@ -349,8 +355,9 @@ class MojoCdmTest : public ::testing::Test { base::TestMessageLoop message_loop_; // |remote_cdm_| represents the CDM at the end of the mojo message pipe. - MockCdm* remote_cdm_; - MockCdmFactory cdm_factory_; + scoped_refptr<MockCdm> remote_cdm_{new MockCdm()}; + MockCdmFactory cdm_factory_{remote_cdm_}; + MockCdmContext cdm_context_; MojoCdmServiceContext mojo_cdm_service_context_; StrictMock<MockCdmClient> cdm_client_; @@ -363,6 +370,10 @@ class MojoCdmTest : public ::testing::Test { std::unique_ptr<mojo::Receiver<mojom::ContentDecryptionModule>> cdm_receiver_; scoped_refptr<ContentDecryptionModule> mojo_cdm_; +#if defined(OS_WIN) + bool requires_media_foundation_renderer_ = false; +#endif + private: DISALLOW_COPY_AND_ASSIGN(MojoCdmTest); }; @@ -596,9 +607,6 @@ TEST_F(MojoCdmTest, SessionKeysChangeCB_Success) { base::RunLoop().RunUntilIdle(); } -// TODO(xhwang): Refactor MockCdmFactory to mock CdmFactory::Create() so that we -// can set expectations and default actions on the created MockCdm, e.g. return -// a non-null Decryptor to test the HasDecryptor case. TEST_F(MojoCdmTest, NoDecryptor) { Initialize(SUCCESS); auto* cdm_context = mojo_cdm_->GetCdmContext(); @@ -607,4 +615,22 @@ TEST_F(MojoCdmTest, NoDecryptor) { EXPECT_FALSE(decryptor); } +#if defined(OS_WIN) +TEST_F(MojoCdmTest, RequiresMediaFoundationRenderer) { + requires_media_foundation_renderer_ = true; + Initialize(SUCCESS); + auto* cdm_context = mojo_cdm_->GetCdmContext(); + EXPECT_TRUE(cdm_context) << "All CDMs should support CdmContext"; + EXPECT_TRUE(cdm_context->RequiresMediaFoundationRenderer()); +} + +TEST_F(MojoCdmTest, NotRequireMediaFoundationRenderer) { + requires_media_foundation_renderer_ = false; + Initialize(SUCCESS); + auto* cdm_context = mojo_cdm_->GetCdmContext(); + EXPECT_TRUE(cdm_context) << "All CDMs should support CdmContext"; + EXPECT_FALSE(cdm_context->RequiresMediaFoundationRenderer()); +} +#endif + } // namespace media diff --git a/chromium/media/mojo/clients/mojo_decryptor_unittest.cc b/chromium/media/mojo/clients/mojo_decryptor_unittest.cc index fa9638f6245..bd1d63a3408 100644 --- a/chromium/media/mojo/clients/mojo_decryptor_unittest.cc +++ b/chromium/media/mojo/clients/mojo_decryptor_unittest.cc @@ -42,10 +42,10 @@ class MojoDecryptorTest : public ::testing::Test { void SetWriterCapacity(uint32_t capacity) { writer_capacity_ = capacity; } void Initialize() { - decryptor_.reset(new StrictMock<MockDecryptor>()); + decryptor_ = std::make_unique<StrictMock<MockDecryptor>>(); - mojo_decryptor_service_.reset( - new MojoDecryptorService(decryptor_.get(), nullptr)); + mojo_decryptor_service_ = + std::make_unique<MojoDecryptorService>(decryptor_.get(), nullptr); receiver_ = std::make_unique<mojo::Receiver<mojom::Decryptor>>( mojo_decryptor_service_.get()); diff --git a/chromium/media/mojo/clients/mojo_renderer_unittest.cc b/chromium/media/mojo/clients/mojo_renderer_unittest.cc index 0211d5ccee2..831017c1355 100644 --- a/chromium/media/mojo/clients/mojo_renderer_unittest.cc +++ b/chromium/media/mojo/clients/mojo_renderer_unittest.cc @@ -4,6 +4,8 @@ #include <stdint.h> +#include <memory> + #include "base/bind.h" #include "base/macros.h" #include "base/memory/ptr_util.h" @@ -72,10 +74,10 @@ class MojoRendererTest : public ::testing::Test { &mojo_cdm_service_context_, std::move(mock_renderer), remote_renderer_remote.InitWithNewPipeAndPassReceiver()); - mojo_renderer_.reset( - new MojoRenderer(message_loop_.task_runner(), - std::unique_ptr<VideoOverlayFactory>(nullptr), nullptr, - std::move(remote_renderer_remote))); + mojo_renderer_ = std::make_unique<MojoRenderer>( + message_loop_.task_runner(), + std::unique_ptr<VideoOverlayFactory>(nullptr), nullptr, + std::move(remote_renderer_remote)); // CreateAudioStream() and CreateVideoStream() overrides expectations for // expected non-NULL streams. @@ -168,19 +170,17 @@ class MojoRendererTest : public ::testing::Test { base::RunLoop().RunUntilIdle(); } - void OnCdmServiceCreated(std::unique_ptr<MojoCdmService> cdm_service, - mojo::PendingRemote<mojom::Decryptor> decryptor, - const std::string& error_message) { - EXPECT_TRUE(!!cdm_service); - cdm_context_.set_cdm_id(base::OptionalOrNullptr(cdm_service->cdm_id())); - mojo_cdm_service_ = std::move(cdm_service); + void OnCdmServiceInitialized(mojom::CdmContextPtr cdm_context, + const std::string& error_message) { + cdm_context_.set_cdm_id(cdm_context->cdm_id); } void CreateCdm() { - MojoCdmService::Create( - &cdm_factory_, &mojo_cdm_service_context_, kClearKeyKeySystem, - CdmConfig(), - base::BindOnce(&MojoRendererTest::OnCdmServiceCreated, + mojo_cdm_service_ = + std::make_unique<MojoCdmService>(&mojo_cdm_service_context_); + mojo_cdm_service_->Initialize( + &cdm_factory_, kClearKeyKeySystem, CdmConfig(), + base::BindOnce(&MojoRendererTest::OnCdmServiceInitialized, base::Unretained(this))); base::RunLoop().RunUntilIdle(); } @@ -304,8 +304,7 @@ TEST_F(MojoRendererTest, SetCdm_InvalidCdmId) { TEST_F(MojoRendererTest, SetCdm_NonExistCdmId) { Initialize(); - auto cdm_id = base::UnguessableToken::Create(); - cdm_context_.set_cdm_id(&cdm_id); + cdm_context_.set_cdm_id(base::UnguessableToken::Create()); SetCdmAndExpect(false); } diff --git a/chromium/media/mojo/clients/mojo_video_decoder.cc b/chromium/media/mojo/clients/mojo_video_decoder.cc index a093a377cd6..23c3bc5d7c1 100644 --- a/chromium/media/mojo/clients/mojo_video_decoder.cc +++ b/chromium/media/mojo/clients/mojo_video_decoder.cc @@ -147,10 +147,6 @@ VideoDecoderType MojoVideoDecoder::GetDecoderType() const { return decoder_type_; } -std::string MojoVideoDecoder::GetDisplayName() const { - return "MojoVideoDecoder"; -} - void MojoVideoDecoder::FailInit(InitCB init_cb, Status err) { task_runner_->PostTask(FROM_HERE, base::BindOnce(std::move(init_cb), std::move(err))); diff --git a/chromium/media/mojo/clients/mojo_video_decoder.h b/chromium/media/mojo/clients/mojo_video_decoder.h index b5fdef8375d..1d85f48837d 100644 --- a/chromium/media/mojo/clients/mojo_video_decoder.h +++ b/chromium/media/mojo/clients/mojo_video_decoder.h @@ -60,7 +60,6 @@ class MojoVideoDecoder final : public VideoDecoder, // Decoder implementation bool IsPlatformDecoder() const final; bool SupportsDecryption() const final; - std::string GetDisplayName() const override; VideoDecoderType GetDecoderType() const final; // VideoDecoder implementation. diff --git a/chromium/media/mojo/clients/mojo_video_encode_accelerator.cc b/chromium/media/mojo/clients/mojo_video_encode_accelerator.cc index 2fdfdef3ba4..192c2c31ca0 100644 --- a/chromium/media/mojo/clients/mojo_video_encode_accelerator.cc +++ b/chromium/media/mojo/clients/mojo_video_encode_accelerator.cc @@ -134,18 +134,7 @@ void MojoVideoEncodeAccelerator::Encode(scoped_refptr<VideoFrame> frame, DCHECK_EQ(num_planes, frame->layout().num_planes()); DCHECK(vea_.is_bound()); -#if defined(OS_LINUX) || defined(OS_CHROMEOS) - // TODO(crbug.com/1003197): Remove this once we stop supporting STORAGE_DMABUF - // in VideoEncodeAccelerator. - if (frame->storage_type() == VideoFrame::STORAGE_DMABUFS) { - DCHECK(frame->HasDmaBufs()); - vea_->Encode( - frame, force_keyframe, - base::BindOnce(base::DoNothing::Once<scoped_refptr<VideoFrame>>(), - frame)); - return; - } -#endif + // GPU memory path: Pass-through. if (frame->storage_type() == VideoFrame::STORAGE_GPU_MEMORY_BUFFER) { vea_->Encode( frame, force_keyframe, @@ -154,62 +143,26 @@ void MojoVideoEncodeAccelerator::Encode(scoped_refptr<VideoFrame> frame, return; } - if ((frame->format() != PIXEL_FORMAT_I420 && - frame->format() != PIXEL_FORMAT_NV12) || - VideoFrame::STORAGE_SHMEM != frame->storage_type() || - !frame->shm_region()->IsValid()) { - DLOG(ERROR) << "Unexpected video frame buffer"; - return; - } - - // Oftentimes |frame|'s underlying planes will be aligned and not tightly - // packed, so don't use VideoFrame::AllocationSize(). - const size_t allocation_size = frame->shm_region()->GetSize(); - - // A MojoSharedBufferVideoFrame is created with an owned writable handle. As - // the handle in |frame| is not owned, a new region must be created and - // |frame| copied into it. - mojo::ScopedSharedBufferHandle dst_handle = - mojo::SharedBufferHandle::Create(allocation_size); - if (!dst_handle->is_valid()) { - DLOG(ERROR) << "Can't create new frame backing memory"; - return; - } - mojo::ScopedSharedBufferMapping dst_mapping = - dst_handle->Map(allocation_size); - if (!dst_mapping) { - DLOG(ERROR) << "Can't map new frame backing memory"; + // Mappable memory path: Copy buffer to shared memory. + if (frame->format() != PIXEL_FORMAT_I420 && + frame->format() != PIXEL_FORMAT_NV12) { + DLOG(ERROR) << "Unexpected pixel format: " + << VideoPixelFormatToString(frame->format()); return; } - DCHECK(frame->shm_region()); - base::WritableSharedMemoryMapping src_mapping = frame->shm_region()->Map(); - if (!src_mapping.IsValid()) { - DLOG(ERROR) << "Can't map src frame backing memory"; + if (frame->storage_type() != VideoFrame::STORAGE_SHMEM && + frame->storage_type() != VideoFrame::STORAGE_UNOWNED_MEMORY) { + DLOG(ERROR) << "Unexpected storage type: " + << VideoFrame::StorageTypeToString(frame->storage_type()); return; } - memcpy(dst_mapping.get(), src_mapping.memory(), allocation_size); - std::vector<uint32_t> offsets(num_planes); - std::vector<int32_t> strides(num_planes); - for (size_t i = 0; i < num_planes; ++i) { - offsets[i] = frame->data(i) - frame->data(0); - strides[i] = frame->stride(i); - } - - // Temporary Mojo VideoFrame to allow for marshalling. scoped_refptr<MojoSharedBufferVideoFrame> mojo_frame = - MojoSharedBufferVideoFrame::Create( - frame->format(), frame->coded_size(), frame->visible_rect(), - frame->natural_size(), std::move(dst_handle), allocation_size, - std::move(offsets), std::move(strides), frame->timestamp()); + MojoSharedBufferVideoFrame::CreateFromYUVFrame(*frame); if (!mojo_frame) { - DLOG(ERROR) << "Failed creating MojoSharedBufferVideoFrame"; + DLOG(ERROR) << "Failed creating MojoSharedBufferVideoFrame from YUV"; return; } - - // Encode() is synchronous: clients will assume full ownership of |frame| when - // this gets destroyed and probably recycle its shared_memory_handle(): keep - // the former alive until the remote end is actually finished. vea_->Encode( std::move(mojo_frame), force_keyframe, base::BindOnce(base::DoNothing::Once<scoped_refptr<VideoFrame>>(), diff --git a/chromium/media/mojo/clients/win/media_foundation_renderer_client.cc b/chromium/media/mojo/clients/win/media_foundation_renderer_client.cc index 3bb5f2d15a7..66ef3880041 100644 --- a/chromium/media/mojo/clients/win/media_foundation_renderer_client.cc +++ b/chromium/media/mojo/clients/win/media_foundation_renderer_client.cc @@ -84,19 +84,20 @@ void MediaFoundationRendererClient::OnConnectionError() { void MediaFoundationRendererClient::OnRemoteRendererInitialized( PipelineStatus status) { DVLOG_FUNC(1) << "status=" << status; - DCHECK(media_task_runner_->BelongsToCurrentThread()); + DCHECK(!init_cb_.is_null()); + if (status != media::PipelineStatus::PIPELINE_OK) { - DCHECK(!init_cb_.is_null()); std::move(init_cb_).Run(status); return; } if (has_video_) { // TODO(frankli): Add code to init DCOMPTextureWrapper. - } else { - std::move(init_cb_).Run(status); + NOTIMPLEMENTED() << "Video compositing not implemented yet"; } + + std::move(init_cb_).Run(status); } void MediaFoundationRendererClient::OnDCOMPSurfaceHandleCreated(bool success) { @@ -385,7 +386,7 @@ void MediaFoundationRendererClient::OnVideoFrameRateChange( scoped_refptr<media::VideoFrame> MediaFoundationRendererClient::Render( base::TimeTicks deadline_min, base::TimeTicks deadline_max, - bool background_rendering) { + RenderingMode mode) { // Returns no video frame as it is rendered independently by Windows Direct // Composition. return nullptr; diff --git a/chromium/media/mojo/clients/win/media_foundation_renderer_client.h b/chromium/media/mojo/clients/win/media_foundation_renderer_client.h index 7ba6d844dea..f9afedaa4f7 100644 --- a/chromium/media/mojo/clients/win/media_foundation_renderer_client.h +++ b/chromium/media/mojo/clients/win/media_foundation_renderer_client.h @@ -80,9 +80,10 @@ class MediaFoundationRendererClient void OnVideoFrameRateChange(base::Optional<int>) override; // media::VideoRendererSink::RenderCallback implementation. - scoped_refptr<media::VideoFrame> Render(base::TimeTicks deadline_min, - base::TimeTicks deadline_max, - bool background_rendering) override; + scoped_refptr<media::VideoFrame> Render( + base::TimeTicks deadline_min, + base::TimeTicks deadline_max, + RenderingMode rendering_mode) override; void OnFrameDropped() override; base::TimeDelta GetPreferredRenderInterval() override; diff --git a/chromium/media/mojo/common/BUILD.gn b/chromium/media/mojo/common/BUILD.gn index 384e9946cd4..8d33e8dc97c 100644 --- a/chromium/media/mojo/common/BUILD.gn +++ b/chromium/media/mojo/common/BUILD.gn @@ -4,6 +4,8 @@ source_set("common") { sources = [ + "audio_data_s16_converter.cc", + "audio_data_s16_converter.h", "media_type_converters.cc", "media_type_converters.h", "mojo_data_pipe_read_write.cc", diff --git a/chromium/media/mojo/common/audio_data_s16_converter.cc b/chromium/media/mojo/common/audio_data_s16_converter.cc new file mode 100644 index 00000000000..add32e1e055 --- /dev/null +++ b/chromium/media/mojo/common/audio_data_s16_converter.cc @@ -0,0 +1,120 @@ +// Copyright 2021 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/mojo/common/audio_data_s16_converter.h" + +#include <memory> + +#include "media/base/audio_buffer.h" +#include "media/base/audio_bus.h" +#include "media/base/audio_timestamp_helper.h" +#include "media/base/channel_mixer.h" +#include "media/mojo/mojom/media_types.mojom.h" + +namespace media { + +AudioDataS16Converter::AudioDataS16Converter() = default; +AudioDataS16Converter::~AudioDataS16Converter() = default; + +mojom::AudioDataS16Ptr AudioDataS16Converter::ConvertToAudioDataS16( + scoped_refptr<AudioBuffer> buffer, + bool is_multichannel_supported) { + DCHECK_GT(buffer->frame_count(), 0); + DCHECK_GT(buffer->channel_count(), 0); + DCHECK_GT(buffer->sample_rate(), 0); + + // If the audio is already in the interleaved signed int 16 format, directly + // assign it to the buffer, unless it is multichannel when multichannel is + // not supported. + if (buffer->sample_format() == SampleFormat::kSampleFormatS16 && + (buffer->channel_count() == 1 || is_multichannel_supported)) { + auto signed_buffer = mojom::AudioDataS16::New(); + signed_buffer->channel_count = buffer->channel_count(); + signed_buffer->frame_count = buffer->frame_count(); + signed_buffer->sample_rate = buffer->sample_rate(); + int16_t* audio_data = reinterpret_cast<int16_t*>(buffer->channel_data()[0]); + signed_buffer->data.assign( + audio_data, + audio_data + buffer->frame_count() * buffer->channel_count()); + return signed_buffer; + } + + CopyBufferToTempAudioBus(*buffer); + return ConvertAudioBusToAudioDataS16Internal( + *temp_audio_bus_, buffer->sample_rate(), buffer->channel_layout(), + is_multichannel_supported); +} + +mojom::AudioDataS16Ptr AudioDataS16Converter::ConvertToAudioDataS16( + std::unique_ptr<AudioBus> audio_bus, + int sample_rate, + ChannelLayout channel_layout, + bool is_multichannel_supported) { + DCHECK_GT(audio_bus->frames(), 0); + DCHECK_GT(audio_bus->channels(), 0); + return ConvertAudioBusToAudioDataS16Internal( + *audio_bus, sample_rate, channel_layout, is_multichannel_supported); +} + +mojom::AudioDataS16Ptr +AudioDataS16Converter::ConvertAudioBusToAudioDataS16Internal( + const AudioBus& audio_bus, + int sample_rate, + ChannelLayout channel_layout, + bool is_multichannel_supported) { + auto signed_buffer = mojom::AudioDataS16::New(); + signed_buffer->channel_count = audio_bus.channels(); + signed_buffer->frame_count = audio_bus.frames(); + signed_buffer->sample_rate = sample_rate; + + // If multichannel audio is not supported, mix the channels into a monaural + // channel before converting it. + if (audio_bus.channels() > 1 && !is_multichannel_supported) { + signed_buffer->channel_count = 1; + ResetChannelMixerIfNeeded(audio_bus.frames(), channel_layout); + signed_buffer->data.resize(audio_bus.frames()); + + channel_mixer_->Transform(&audio_bus, monaural_audio_bus_.get()); + monaural_audio_bus_->ToInterleaved<SignedInt16SampleTypeTraits>( + monaural_audio_bus_->frames(), &signed_buffer->data[0]); + + return signed_buffer; + } + + signed_buffer->data.resize(audio_bus.frames() * audio_bus.channels()); + audio_bus.ToInterleaved<SignedInt16SampleTypeTraits>(audio_bus.frames(), + &signed_buffer->data[0]); + + return signed_buffer; +} + +void AudioDataS16Converter::CopyBufferToTempAudioBus( + const AudioBuffer& buffer) { + if (!temp_audio_bus_ || + buffer.channel_count() != temp_audio_bus_->channels() || + buffer.frame_count() != temp_audio_bus_->frames()) { + temp_audio_bus_ = + AudioBus::Create(buffer.channel_count(), buffer.frame_count()); + } + + buffer.ReadFrames(buffer.frame_count(), + /* source_frame_offset */ 0, /* dest_frame_offset */ 0, + temp_audio_bus_.get()); +} + +void AudioDataS16Converter::ResetChannelMixerIfNeeded( + int frame_count, + ChannelLayout channel_layout) { + if (!monaural_audio_bus_ || frame_count != monaural_audio_bus_->frames()) { + monaural_audio_bus_ = AudioBus::Create(1 /* channels */, frame_count); + } + + if (channel_layout != channel_layout_) { + channel_layout_ = channel_layout; + channel_mixer_ = + std::make_unique<ChannelMixer>(channel_layout, CHANNEL_LAYOUT_MONO); + } +} + +} // namespace media
\ No newline at end of file diff --git a/chromium/media/mojo/common/audio_data_s16_converter.h b/chromium/media/mojo/common/audio_data_s16_converter.h new file mode 100644 index 00000000000..ba18c40605b --- /dev/null +++ b/chromium/media/mojo/common/audio_data_s16_converter.h @@ -0,0 +1,67 @@ +// Copyright 2021 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_MOJO_COMMON_AUDIO_DATA_S16_CONVERTER_H_ +#define MEDIA_MOJO_COMMON_AUDIO_DATA_S16_CONVERTER_H_ + +#include <memory> + +#include "media/base/audio_buffer.h" +#include "media/base/audio_bus.h" +#include "media/mojo/mojom/media_types.mojom.h" + +namespace media { + +class ChannelMixer; + +// Converts AudioBuffer or AudioBus into mojom::AudioDataS16. +class AudioDataS16Converter { + public: + AudioDataS16Converter(); + virtual ~AudioDataS16Converter(); + AudioDataS16Converter(const AudioDataS16Converter&) = delete; + AudioDataS16Converter& operator=(const AudioDataS16Converter&) = delete; + + mojom::AudioDataS16Ptr ConvertToAudioDataS16( + scoped_refptr<AudioBuffer> buffer, + bool is_multichannel_supported); + + mojom::AudioDataS16Ptr ConvertToAudioDataS16( + std::unique_ptr<AudioBus> audio_bus, + int sample_rate, + ChannelLayout channel_layout, + bool is_multichannel_supported); + + private: + mojom::AudioDataS16Ptr ConvertAudioBusToAudioDataS16Internal( + const AudioBus& audio_bus, + int sample_rate, + ChannelLayout channel_layout, + bool is_multichannel_supported); + + // Recreates the temporary audio bus if the frame count or channel count + // changed and reads the frames from the buffer into the temporary audio bus. + void CopyBufferToTempAudioBus(const AudioBuffer& buffer); + + // Resets the temporary monaural audio bus and the channel mixer used to + // combine multiple audio channels. + void ResetChannelMixerIfNeeded(int frame_count, ChannelLayout channel_layout); + + // The temporary audio bus used to convert the raw audio to the appropriate + // format. + std::unique_ptr<AudioBus> temp_audio_bus_; + + // The temporary audio bus used to mix multichannel audio into a single + // channel. + std::unique_ptr<AudioBus> monaural_audio_bus_; + + std::unique_ptr<ChannelMixer> channel_mixer_; + + // The layout used to instantiate the channel mixer. + ChannelLayout channel_layout_ = ChannelLayout::CHANNEL_LAYOUT_NONE; +}; + +} // namespace media + +#endif // MEDIA_MOJO_COMMON_AUDIO_DATA_S16_CONVERTER_H_ diff --git a/chromium/media/mojo/common/mojo_shared_buffer_video_frame.cc b/chromium/media/mojo/common/mojo_shared_buffer_video_frame.cc index 649be420285..fab1dc1b489 100644 --- a/chromium/media/mojo/common/mojo_shared_buffer_video_frame.cc +++ b/chromium/media/mojo/common/mojo_shared_buffer_video_frame.cc @@ -79,7 +79,7 @@ MojoSharedBufferVideoFrame::CreateDefaultForTesting( } scoped_refptr<MojoSharedBufferVideoFrame> -MojoSharedBufferVideoFrame::CreateFromYUVFrame(const VideoFrame& frame) { +MojoSharedBufferVideoFrame::CreateFromYUVFrame(VideoFrame& frame) { size_t num_planes = VideoFrame::NumPlanes(frame.format()); DCHECK_LE(num_planes, 3u); DCHECK_GE(num_planes, 2u); @@ -99,6 +99,11 @@ MojoSharedBufferVideoFrame::CreateFromYUVFrame(const VideoFrame& frame) { mojo::ScopedSharedBufferHandle handle = mojo::SharedBufferHandle::Create(aggregate_size); + if (!handle->is_valid()) { + DLOG(ERROR) << "Can't create new frame backing memory"; + return nullptr; + } + mojo::ScopedSharedBufferMapping dst_mapping = handle->Map(aggregate_size); // The data from |frame| may not be consecutive between planes. Copy data into // a shared memory buffer which is tightly packed. Padding inside each planes @@ -111,7 +116,21 @@ MojoSharedBufferVideoFrame::CreateFromYUVFrame(const VideoFrame& frame) { frame.timestamp()); CHECK(!!mojo_frame); - // Copy plane data. + // If the source memory region is a shared memory region we must map it too. + base::WritableSharedMemoryMapping src_mapping; + if (frame.storage_type() == VideoFrame::STORAGE_SHMEM) { + if (!frame.shm_region()->IsValid()) { + DLOG(ERROR) << "Invalid source shared memory region"; + return nullptr; + } + src_mapping = frame.shm_region()->Map(); + if (!src_mapping.IsValid()) { + DLOG(ERROR) << "Can't map source shared memory region"; + return nullptr; + } + } + + // Copy plane data while mappings are in scope. for (size_t i = 0; i < num_planes; ++i) { memcpy(mojo_frame->shared_buffer_data() + offsets[i], static_cast<const void*>(frame.data(i)), sizes[i]); diff --git a/chromium/media/mojo/common/mojo_shared_buffer_video_frame.h b/chromium/media/mojo/common/mojo_shared_buffer_video_frame.h index 5bb63c9255d..5a852080ce6 100644 --- a/chromium/media/mojo/common/mojo_shared_buffer_video_frame.h +++ b/chromium/media/mojo/common/mojo_shared_buffer_video_frame.h @@ -47,7 +47,7 @@ class MojoSharedBufferVideoFrame : public VideoFrame { // Internally the data from in-memory YUV frame will be copied to a // consecutive block in shared memory. Will return null on failure. static scoped_refptr<MojoSharedBufferVideoFrame> CreateFromYUVFrame( - const VideoFrame& frame); + VideoFrame& frame); // Creates a MojoSharedBufferVideoFrame that uses the memory in |handle|. // This will take ownership of |handle|, so the caller can no longer use it. diff --git a/chromium/media/mojo/mojom/BUILD.gn b/chromium/media/mojo/mojom/BUILD.gn index 44e9f738a81..341e26de457 100644 --- a/chromium/media/mojo/mojom/BUILD.gn +++ b/chromium/media/mojo/mojom/BUILD.gn @@ -17,7 +17,7 @@ mojom("mojom") { "audio_logging.mojom", "audio_output_stream.mojom", "audio_parameters.mojom", - "cdm_infobar_service.mojom", + "audio_stream_factory.mojom", "cdm_service.mojom", "cdm_storage.mojom", "content_decryption_module.mojom", @@ -60,6 +60,10 @@ mojom("mojom") { sources += [ "cast_application_media_info_manager.mojom" ] } + if (is_win) { + sources += [ "media_foundation_service.mojom" ] + } + public_deps = [ "//gpu/ipc/common:interfaces", "//media/learning/mojo/public/mojom", @@ -415,10 +419,6 @@ mojom("mojom") { { types = [ { - mojom = "media.mojom.ScalingSettings" - cpp = "::media::ScalingSettings" - }, - { mojom = "media.mojom.ResolutionBitrateLimit" cpp = "::media::ResolutionBitrateLimit" }, diff --git a/chromium/media/mojo/mojom/audio_stream_factory.mojom b/chromium/media/mojo/mojom/audio_stream_factory.mojom new file mode 100644 index 00000000000..0b1e61f23f1 --- /dev/null +++ b/chromium/media/mojo/mojom/audio_stream_factory.mojom @@ -0,0 +1,100 @@ +// 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. + +module media.mojom; + +import "media/mojo/mojom/audio_data_pipe.mojom"; +import "media/mojo/mojom/audio_input_stream.mojom"; +import "media/mojo/mojom/audio_logging.mojom"; +import "media/mojo/mojom/audio_output_stream.mojom"; +import "media/mojo/mojom/audio_parameters.mojom"; +import "mojo/public/mojom/base/shared_memory.mojom"; +import "mojo/public/mojom/base/unguessable_token.mojom"; + +// Mutes a group of AudioOutputStreams while at least one binding to an instance +// exists. Once the last binding is dropped, all streams in the group are +// un-muted. +interface LocalMuter {}; + +// This interface is exposed by the audio service to allow trusted clients +// (like the browser process) to create streams. Note that while the factory +// interface itself is only for trusted clients, the created streams and data +// pipes may be forwarded to untrusted clients. +// +// The client must keep the connection to the factory while streams are +// running. +interface AudioStreamFactory { + // Creates an AudioInputStream and returns the AudioDataPipe it writes data to + // and a bool indicating whether the stream is initially muted. |data_pipe| is + // null, |initially_muted| is false and |stream_id| is empty in case stream + // creation failed. + // |device_id| is either the |unique_id| field from an AudioDeviceDescription + // obtained from the audio.mojom.SystemInfo interface, or "default". + // |shared_memory_count| indicates how many buffer segments can the input + // stream client read at once, to avoid data overwriting. |enable_agc| is used + // for enabling automatic gain control. |key_press_count_buffer| is an + // optional readonly shared memory handle for reading the current key press + // count, updated by browser process in media::UserInputMonitor + // implementation. + CreateInputStream( + pending_receiver<media.mojom.AudioInputStream> stream, + pending_remote<media.mojom.AudioInputStreamClient> client, + pending_remote<media.mojom.AudioInputStreamObserver>? observer, + pending_remote<media.mojom.AudioLog>? log, + string device_id, media.mojom.AudioParameters params, + uint32 shared_memory_count, bool enable_agc, + mojo_base.mojom.ReadOnlySharedMemoryRegion? key_press_count_buffer) + => (media.mojom.ReadOnlyAudioDataPipe? data_pipe, bool initially_muted, + mojo_base.mojom.UnguessableToken? stream_id); + + // Associates an output device with an input stream, so that the input knows + // which output device to cancel echo from. |input_stream_id| is the id + // returned when the stream was created. |output_device_id| is a raw device + // id. In case either of the parameters are invalid, the operation will + // silently fail. + AssociateInputAndOutputForAec( + mojo_base.mojom.UnguessableToken input_stream_id, + string output_device_id); + + // Creates an AudioOutputStream and returns the AudioDataPipe it reads data + // from. |data_pipe| is null in case stream creation failed. |device_id| is + // either the |unique_id| field from an AudioDeviceDescription obtained from + // the audio.mojom.SystemInfo interface, or "default". The stream |group_id| + // is used for muting streams or capturing them for loopback. + CreateOutputStream( + pending_receiver<media.mojom.AudioOutputStream> stream, + pending_associated_remote<media.mojom.AudioOutputStreamObserver>? observer, + pending_remote<media.mojom.AudioLog>? log, + string device_id, media.mojom.AudioParameters params, + mojo_base.mojom.UnguessableToken group_id) + => (media.mojom.ReadWriteAudioDataPipe? data_pipe); + + // Binds the request to the LocalMuter associated with the given |group_id|. + // While one or more bindings to a group's LocalMuter exists, all local audio + // playout for the streams in that group is muted. + // + // It is the responsibility of the client to bind to a muter before creating + // any output streams that should be started muted. Likewise, if existing + // output streams must remain muted until they are shut down, the binding to + // the muter must not be closed until after all other streams' binding. (This + // is the reason for the associated request argument.) + BindMuter(pending_associated_receiver<LocalMuter> receiver, + mojo_base.mojom.UnguessableToken group_id); + + // Creates an AudioInputStream that provides the result of looping-back and + // mixing-together all current and future AudioOutputStreams tagged with the + // given |group_id|. The loopback re-mixes audio, if necessary, so that the + // resulting data stream format matches the specified |params|. All other args + // and the result are as described in CreateInputStream() above, except for + // |stream_id|. Loopback streams have no ID, since they cannot be used in + // echo cancellation. + CreateLoopbackStream( + pending_receiver<media.mojom.AudioInputStream> receiver, + pending_remote<media.mojom.AudioInputStreamClient> client, + pending_remote<media.mojom.AudioInputStreamObserver> observer, + media.mojom.AudioParameters params, + uint32 shared_memory_count, + mojo_base.mojom.UnguessableToken group_id) + => (media.mojom.ReadOnlyAudioDataPipe? data_pipe); +}; diff --git a/chromium/media/mojo/mojom/cdm_infobar_service.mojom b/chromium/media/mojo/mojom/cdm_infobar_service.mojom deleted file mode 100644 index 4da7c551c7c..00000000000 --- a/chromium/media/mojo/mojom/cdm_infobar_service.mojom +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2020 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. - -module media.mojom; - -// Service to trigger info bar for CDM related events. -interface CdmInfobarService { - // Notifies that the platform is not supported. - NotifyUnsupportedPlatform(); -};
\ No newline at end of file diff --git a/chromium/media/mojo/mojom/content_decryption_module.mojom b/chromium/media/mojo/mojom/content_decryption_module.mojom index 8181ceeeed2..c0e8e2510d3 100644 --- a/chromium/media/mojo/mojom/content_decryption_module.mojom +++ b/chromium/media/mojo/mojom/content_decryption_module.mojom @@ -132,6 +132,19 @@ interface ContentDecryptionModuleClient { OnSessionExpirationUpdate(string session_id, double new_expiry_time_sec); }; +// Context associated with the remote CDM, mostly to populate media::CdmContext. +struct CdmContext { + // An ID that can be used to locate the CDM at the remote side. + mojo_base.mojom.UnguessableToken cdm_id; + + // The remote Decryptor if the CDM implementation provides one. + pending_remote<Decryptor>? decryptor; + + // Whether MediaFoundationRenderer is required by the CDM. + [EnableIf=is_win] + bool requires_media_foundation_renderer; +}; + // Factory interface used for creating ContentDecryptionModule instances. interface CdmFactory { // Creates a CDM based on the |key_system| and |cdm_config| provided. A @@ -141,14 +154,11 @@ interface CdmFactory { // untrusted process (e.g. renderer), so the implementation must fully // validate |key_system| before creating the CDM. |cdm_config| specifies other // properties of the CDM which may influence creation. Upon failure, the - // returned |cdm| and |decryptor| will be null, |cdm_id| will be zero and - // |error_message| will specify the error reason. Upon success, |cdm| will be - // valid, |cdm_id| will be non-zero and will later be used to locate the CDM - // at the remote side and |error_message| will be empty. |decryptor| will be - // the remote Decryptor if the CDM implementation provides one. + // returned |cdm| and |cdm_context| will be null, and |error_message| will + // specify the error reason. Upon success, |cdm| and |cdm_context| will be + // valid, and |error_message| will be empty. CreateCdm(string key_system, CdmConfig cdm_config) => (pending_remote<ContentDecryptionModule>? cdm, - mojo_base.mojom.UnguessableToken? cdm_id, - pending_remote<Decryptor>? decryptor, + CdmContext? cdm_context, string error_message); }; diff --git a/chromium/media/mojo/mojom/interface_factory.mojom b/chromium/media/mojo/mojom/interface_factory.mojom index 09e7328dd7c..cf43b8cd012 100644 --- a/chromium/media/mojo/mojom/interface_factory.mojom +++ b/chromium/media/mojo/mojom/interface_factory.mojom @@ -70,14 +70,11 @@ interface InterfaceFactory { // untrusted process (e.g. renderer), so the implementation must fully // validate |key_system| before creating the CDM. |cdm_config| specifies other // properties of the CDM which may influence creation. Upon failure, the - // returned |cdm| and |decryptor| will be null, |cdm_id| will be zero and - // |error_message| will specify the error reason. Upon success, |cdm| will be - // valid, |cdm_id| will be non-zero and will later be used to locate the CDM - // at the remote side and |error_message| will be empty. |decryptor| will be - // the remote Decryptor if the CDM implementation provides one. + // returned |cdm| and |cdm_context| will be null, and |error_message| will + // specify the error reason. Upon success, |cdm| and |cdm_context| will be + // valid, and |error_message| will be empty. CreateCdm(string key_system, CdmConfig cdm_config) => (pending_remote<ContentDecryptionModule>? cdm, - mojo_base.mojom.UnguessableToken? cdm_id, - pending_remote<Decryptor>? decryptor, + CdmContext? cdm_context, string error_message); }; diff --git a/chromium/media/mojo/mojom/key_system_support.mojom b/chromium/media/mojo/mojom/key_system_support.mojom index 69d608ca7d5..ce6267224bd 100644 --- a/chromium/media/mojo/mojom/key_system_support.mojom +++ b/chromium/media/mojo/mojom/key_system_support.mojom @@ -13,7 +13,6 @@ import "media/mojo/mojom/media_types.mojom"; struct KeySystemCapability { // Software secure codecs and encryption schemes supported by the CDM. array<VideoCodec> video_codecs; - bool supports_vp9_profile2; array<EncryptionScheme> encryption_schemes; // Hardware secure codecs and encryption schemes supported by the CDM. diff --git a/chromium/media/mojo/mojom/media_foundation_service.mojom b/chromium/media/mojo/mojom/media_foundation_service.mojom new file mode 100644 index 00000000000..e87f7b7944d --- /dev/null +++ b/chromium/media/mojo/mojom/media_foundation_service.mojom @@ -0,0 +1,35 @@ +// Copyright 2021 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. + +module media.mojom; + +import "media/mojo/mojom/frame_interface_factory.mojom"; +import "media/mojo/mojom/interface_factory.mojom"; +import "media/mojo/mojom/key_system_support.mojom"; +import "mojo/public/mojom/base/file_path.mojom"; + +// A service to provide Windows MediaFoundation-based InterfaceFactory and +// KeySystemCapability. The service runs in the MediaFoundationService process, +// which is a specialized utility process with SandboxType::kMediaFoundationCdm. +// The process is not sandboxed after launch. `Initialize()` must be called to +// preload the CDM if needed, after which the sandbox will be sealed. The +// service is always connected from the browser process. The remote +// `InterfaceFactory` lives in the renderer process. +interface MediaFoundationService { + // Must be called immediately after the `MediaFoundationService` is connected, + // before any other method can be called. Must seal the sandbox before return. + // If `cdm_path` is not empty, preloads the CDM into the process. + Initialize(mojo_base.mojom.FilePath cdm_path); + + // Queries the capabilities of the MediaFoundation-based CDM. + IsKeySystemSupported(string key_system) + => (bool is_supported, KeySystemCapability? key_system_capability); + + // Requests an InterfaceFactory. `frame_interfaces` can optionally be used to + // provide interfaces hosted by the caller to the remote InterfaceFactory + // implementation. + CreateInterfaceFactory( + pending_receiver<InterfaceFactory> factory, + pending_remote<FrameInterfaceFactory> frame_interfaces); +}; diff --git a/chromium/media/mojo/mojom/media_player.mojom b/chromium/media/mojo/mojom/media_player.mojom index 727d298ed49..1a7fe6cb761 100644 --- a/chromium/media/mojo/mojom/media_player.mojom +++ b/chromium/media/mojo/mojom/media_player.mojom @@ -11,12 +11,6 @@ import "ui/gfx/geometry/mojom/geometry.mojom"; // Implemented by HTMLMediaElement in the renderer process. interface MediaPlayer { - // Sends |observer| to the renderer process so that it can be established a - // communication channel with the implementor of MediaPlayerObserver, and - // allows multiple observers for more observers like PictureInPictureService. - AddMediaPlayerObserver( - pending_associated_remote<MediaPlayerObserver> observer); - // Requests the media player to start or resume media playback. RequestPlay(); @@ -29,14 +23,33 @@ interface MediaPlayer { // Requests the media player to move backward the media playback position. RequestSeekBackward(mojo_base.mojom.TimeDelta seek_time); + // Requests the media player to move to a specific time. + RequestSeekTo(mojo_base.mojom.TimeDelta seek_time); + // Requests the media player to enter the Picture-in-Picture mode. RequestEnterPictureInPicture(); // Requests the media player to exit the Picture-in-Picture mode. RequestExitPictureInPicture(); + // Set the volume multiplier to control audio ducking. + // Output volume should be set to |player_volume| * |multiplier|. The range + // of |multiplier| is [0, 1], where 1 indicates normal (non-ducked) playback. + SetVolumeMultiplier(double multiplier); + + // Set the player as the persistent video. Persistent video should hide its + // controls and go fullscreen. + SetPersistentState(bool persistent); + + // Notify the player that it is now eligible to start recording power + // measurements if |state| is true, else it is no longer eligible. + SetPowerExperimentState(bool enabled); + // Set the media player sink id to |sink_id|. SetAudioSinkId(string sink_id); + + // Suspends the media player when the host frame is closed. + SuspendForFrameClosed(); }; // Implemented by MediaWebContentsObserver::MediaPlayerObserverHostImpl in the @@ -94,6 +107,9 @@ interface MediaPlayerHost { // document owning the HTMLMediaElement that a new MediaPlayer is available, // passing a pending remote (i.e. |player_remote|) that will be used in the // browser process to establish a channel with the HTMLMediaElement. + // |observer| starts observing the MediaPlayer as soon as the player is + // created. OnMediaPlayerAdded(pending_associated_remote<MediaPlayer> player_remote, + pending_associated_receiver<MediaPlayerObserver> observer, int32 player_id); }; diff --git a/chromium/media/mojo/mojom/media_types.mojom b/chromium/media/mojo/mojom/media_types.mojom index 1ae7c91c0a8..1db4788ed02 100644 --- a/chromium/media/mojo/mojom/media_types.mojom +++ b/chromium/media/mojo/mojom/media_types.mojom @@ -372,7 +372,6 @@ struct VideoFrame { union VideoFrameData { EosVideoFrameData eos_data; SharedBufferVideoFrameData shared_buffer_data; - DmabufVideoFrameData dmabuf_data; GpuMemoryBufferVideoFrameData gpu_memory_buffer_data; MailboxVideoFrameData mailbox_data; }; @@ -393,12 +392,6 @@ struct SharedBufferVideoFrameData { array<uint32> offsets; }; -// This defines video frame data stored in dmabuf. -struct DmabufVideoFrameData { - // Size depends on media::VideoFrame::NumPlanes with frame format. - array<handle<platform>> dmabuf_fds; -}; - struct GpuMemoryBufferVideoFrameData { gfx.mojom.GpuMemoryBufferHandle gpu_memory_buffer_handle; array<gpu.mojom.MailboxHolder, 4> mailbox_holder; diff --git a/chromium/media/mojo/mojom/speech_recognition_service.mojom b/chromium/media/mojo/mojom/speech_recognition_service.mojom index 0c8b14579ca..ab4d55221b5 100644 --- a/chromium/media/mojo/mojom/speech_recognition_service.mojom +++ b/chromium/media/mojo/mojom/speech_recognition_service.mojom @@ -4,21 +4,42 @@ module media.mojom; +import "media/mojo/mojom/audio_parameters.mojom"; +import "media/mojo/mojom/audio_stream_factory.mojom"; import "media/mojo/mojom/media_types.mojom"; import "mojo/public/mojom/base/file_path.mojom"; import "mojo/public/mojom/base/time.mojom"; import "services/network/public/mojom/url_loader_factory.mojom"; +// Corresponds to the LangIdEvent.ConfidenceInterval defined in +// http://google3/speech/soda/public/soda_event.proto. +enum ConfidenceLevel { + kUnknown, + kNotConfident, + kConfident, + kHighlyConfident, +}; + // The main interface a client uses to interact with a speech recognition -// service process. Every renderer can own one or more +// service process. In Live Caption, every renderer can own one or more // Remote<SpeechRecognitionContext>, with the receiver bound through the -// BrowserInterfaceBroker. Returns a flag indicating whether multichannel -// audio is supported by the speech recognition service. +// BrowserInterfaceBroker. In Chrome OS features like Dictation and Projector, +// every OnDeviceSpeechRecognizer can own a Remote<SpeechRecognitionContext>. interface SpeechRecognitionContext { - // Bind the recognizers to the speech recognition service. + // Bind the recognizers to the speech recognition service. Returns a flag + // indicating whether multichannel audio is supported by the speech + // recognition service. BindRecognizer(pending_receiver<SpeechRecognitionRecognizer> receiver, pending_remote<SpeechRecognitionRecognizerClient> client) => (bool is_multichannel_supported); + + // Prepares microphone audio to be captured from within the + // SpeechRecognitionService process, with results passed back to the + // SpeechRecognitionRecognizerClient. + BindAudioSourceFetcher( + pending_receiver<AudioSourceFetcher> fetcher_receiver, + pending_remote<SpeechRecognitionRecognizerClient> client) + => (bool is_multichannel_supported); }; // The main interface to a speech secognition service process. @@ -43,6 +64,19 @@ interface SpeechRecognitionService { pending_remote<SpeechRecognitionServiceClient> client); }; +// The interface used to start and stop fetching audio from the microphone +// for speech recognition. +interface AudioSourceFetcher { + // Begin fetching audio. Results will be returned using the + // Remote<SpeechRecognitionRecognizerClient> which was passed in + // BindAudioSourceFetcher. + Start(pending_remote<AudioStreamFactory> factory, string device_id, + media.mojom.AudioParameters audio_parameters); + + // Stops audio fetching. + Stop(); +}; + // The interface used to send messages from the speech recognition service // back to the consumer of the service. interface SpeechRecognitionServiceClient { @@ -68,6 +102,10 @@ interface SpeechRecognitionRecognizer { // Notify the speech recognition recognizer that audio was received by the // renderer after the caption bubble was closed. AudioReceivedAfterBubbleClosed(mojo_base.mojom.TimeDelta duration); + + // Notify the speech recognition recognizer that the language changed. Takes + // in the locale string (e.g. "en-US"). + OnLanguageChanged(string language); }; // The interface used to return speech recognition events from the speech @@ -76,6 +114,12 @@ interface SpeechRecognitionRecognizer { interface SpeechRecognitionRecognizerClient { // Triggered by speech recognition process on a speech recognition event. OnSpeechRecognitionRecognitionEvent(SpeechRecognitionResult result); + + // Triggered by an error within the speech recognition service. + OnSpeechRecognitionError(); + + // Triggered by speech recognition process on a language identification event. + OnLanguageIdentificationEvent(LanguageIdentificationEvent event); }; // A speech recognition result created by the speech service and passed to the @@ -89,13 +133,27 @@ struct SpeechRecognitionResult { bool is_final; }; +// A language identification event created by the speech recognition service +// and passed to the browser and renderer. +struct LanguageIdentificationEvent { + // The locale of the language with the highest confidence. + string language; + + // The confidence interval. + ConfidenceLevel confidence_level; +}; + // The interface used to notify the speech recognition client of events // triggered by the browser. The remote lives in the browser process and the // receiver lives in the renderer process. -interface SpeechRecognitionAvailabilityObserver { +interface SpeechRecognitionBrowserObserver { // Notify the speech recognition client when speech recognition availability // changes. SpeechRecognitionAvailabilityChanged(bool is_speech_recognition_available); + + // Notify the speech recognition client when the speech recognition language + // changes. + SpeechRecognitionLanguageChanged(string language); }; // This interface between the speech recognition client and the browser. @@ -103,6 +161,6 @@ interface SpeechRecognitionAvailabilityObserver { // browser process. interface SpeechRecognitionClientBrowserInterface { // Bind the speech recognition availability observer. - BindSpeechRecognitionAvailabilityObserver( - pending_remote<SpeechRecognitionAvailabilityObserver> observer); + BindSpeechRecognitionBrowserObserver( + pending_remote<SpeechRecognitionBrowserObserver> observer); }; diff --git a/chromium/media/mojo/mojom/video_encode_accelerator_mojom_traits_unittest.cc b/chromium/media/mojo/mojom/video_encode_accelerator_mojom_traits_unittest.cc index 475dfee84d3..15d59d1d76a 100644 --- a/chromium/media/mojo/mojom/video_encode_accelerator_mojom_traits_unittest.cc +++ b/chromium/media/mojo/mojom/video_encode_accelerator_mojom_traits_unittest.cc @@ -15,8 +15,6 @@ namespace media { TEST(VideoEncoderInfoStructTraitTest, RoundTrip) { ::media::VideoEncoderInfo input; input.implementation_name = "FakeVideoEncodeAccelerator"; - // Scaling settings. - input.scaling_settings = ::media::ScalingSettings(12, 123); // FPS allocation. for (size_t i = 0; i < ::media::VideoEncoderInfo::kMaxSpatialLayers; ++i) input.fps_allocation[i] = {5, 5, 10}; diff --git a/chromium/media/mojo/mojom/video_encoder_info.mojom b/chromium/media/mojo/mojom/video_encoder_info.mojom index a299ca7f84e..d0d15e386de 100644 --- a/chromium/media/mojo/mojom/video_encoder_info.mojom +++ b/chromium/media/mojo/mojom/video_encoder_info.mojom @@ -6,11 +6,6 @@ module media.mojom; import "ui/gfx/geometry/mojom/geometry.mojom"; -struct ScalingSettings { - uint8 min_qp; - uint8 max_qp; -}; - struct ResolutionBitrateLimit { gfx.mojom.Size frame_size; int32 min_start_bitrate_bps; @@ -26,7 +21,6 @@ struct VideoEncoderInfo { bool is_hardware_accelerated; bool supports_simulcast; - ScalingSettings? scaling_settings; // This array size is equal to media::VideoEncoderInfo::kMaxSpatialLayers. array<array<uint8>, 5> fps_allocation; array<ResolutionBitrateLimit> resolution_bitrate_limits; diff --git a/chromium/media/mojo/mojom/video_encoder_info_mojom_traits.cc b/chromium/media/mojo/mojom/video_encoder_info_mojom_traits.cc index 32262cc67fd..f30ce429b0e 100644 --- a/chromium/media/mojo/mojom/video_encoder_info_mojom_traits.cc +++ b/chromium/media/mojo/mojom/video_encoder_info_mojom_traits.cc @@ -7,16 +7,6 @@ namespace mojo { // static -bool StructTraits< - media::mojom::ScalingSettingsDataView, - media::ScalingSettings>::Read(media::mojom::ScalingSettingsDataView data, - media::ScalingSettings* out) { - out->min_qp = data.min_qp(); - out->max_qp = data.max_qp(); - return true; -} - -// static bool StructTraits<media::mojom::ResolutionBitrateLimitDataView, media::ResolutionBitrateLimit>:: Read(media::mojom::ResolutionBitrateLimitDataView data, @@ -42,9 +32,6 @@ bool StructTraits< if (!data.ReadImplementationName(&out->implementation_name)) return false; - if (!data.ReadScalingSettings(&out->scaling_settings)) - return false; - base::span<std::vector<uint8_t>> fps_allocation(out->fps_allocation); if (!data.ReadFpsAllocation(&fps_allocation)) return false; diff --git a/chromium/media/mojo/mojom/video_encoder_info_mojom_traits.h b/chromium/media/mojo/mojom/video_encoder_info_mojom_traits.h index 30698bf4b56..39e911f8ca2 100644 --- a/chromium/media/mojo/mojom/video_encoder_info_mojom_traits.h +++ b/chromium/media/mojo/mojom/video_encoder_info_mojom_traits.h @@ -13,21 +13,6 @@ namespace mojo { template <> -class StructTraits<media::mojom::ScalingSettingsDataView, - media::ScalingSettings> { - public: - static int32_t min_qp(const media::ScalingSettings& scaling_settings) { - return scaling_settings.min_qp; - } - static int32_t max_qp(const media::ScalingSettings& scaling_settings) { - return scaling_settings.max_qp; - } - - static bool Read(media::mojom::ScalingSettingsDataView data, - media::ScalingSettings* out); -}; - -template <> class StructTraits<media::mojom::ResolutionBitrateLimitDataView, media::ResolutionBitrateLimit> { public: @@ -75,10 +60,6 @@ class StructTraits<media::mojom::VideoEncoderInfoDataView, const media::VideoEncoderInfo& video_encoder_info) { return video_encoder_info.supports_simulcast; } - static const base::Optional<media::ScalingSettings>& scaling_settings( - const media::VideoEncoderInfo& video_encoder_info) { - return video_encoder_info.scaling_settings; - } static base::span<const std::vector<uint8_t>, media::VideoEncoderInfo::kMaxSpatialLayers> fps_allocation(const media::VideoEncoderInfo& video_encoder_info) { diff --git a/chromium/media/mojo/mojom/video_frame_mojom_traits.cc b/chromium/media/mojo/mojom/video_frame_mojom_traits.cc index eac5560cc57..08ad8fc66ef 100644 --- a/chromium/media/mojo/mojom/video_frame_mojom_traits.cc +++ b/chromium/media/mojo/mojom/video_frame_mojom_traits.cc @@ -63,23 +63,6 @@ media::mojom::VideoFrameDataPtr MakeVideoFrameData( std::move(offsets))); } -#if defined(OS_LINUX) || defined(OS_CHROMEOS) - if (input->storage_type() == media::VideoFrame::STORAGE_DMABUFS) { - std::vector<mojo::PlatformHandle> dmabuf_fds; - - const size_t num_planes = media::VideoFrame::NumPlanes(input->format()); - dmabuf_fds.reserve(num_planes); - for (size_t i = 0; i < num_planes; i++) { - const int dmabuf_fd = HANDLE_EINTR(dup(input->DmabufFds()[i].get())); - dmabuf_fds.emplace_back(base::ScopedFD(dmabuf_fd)); - DCHECK(dmabuf_fds.back().is_valid()); - } - - return media::mojom::VideoFrameData::NewDmabufData( - media::mojom::DmabufVideoFrameData::New(std::move(dmabuf_fds))); - } -#endif - std::vector<gpu::MailboxHolder> mailbox_holder(media::VideoFrame::kMaxPlanes); DCHECK_LE(input->NumTextures(), mailbox_holder.size()); // STORAGE_GPU_MEMORY_BUFFER may carry meaningful or dummy mailboxes, @@ -167,45 +150,6 @@ bool StructTraits<media::mojom::VideoFrameDataView, shared_buffer_data.TakeFrameData(), shared_buffer_data.frame_data_size(), std::move(offsets), std::move(strides), timestamp); -#if defined(OS_LINUX) || defined(OS_CHROMEOS) - } else if (data.is_dmabuf_data()) { - media::mojom::DmabufVideoFrameDataDataView dmabuf_data; - data.GetDmabufDataDataView(&dmabuf_data); - - std::vector<mojo::PlatformHandle> dmabuf_fds_data; - if (!dmabuf_data.ReadDmabufFds(&dmabuf_fds_data)) - return false; - - const size_t num_planes = media::VideoFrame::NumPlanes(format); - std::vector<int> strides = - media::VideoFrame::ComputeStrides(format, coded_size); - if (num_planes != strides.size()) - return false; - if (num_planes != dmabuf_fds_data.size()) - return false; - - std::vector<media::ColorPlaneLayout> planes(num_planes); - for (size_t i = 0; i < num_planes; i++) { - planes[i].stride = strides[i]; - planes[i].offset = 0; - planes[i].size = static_cast<size_t>( - media::VideoFrame::PlaneSize(format, i, coded_size).GetArea()); - } - - auto layout = media::VideoFrameLayout::CreateWithPlanes(format, coded_size, - std::move(planes)); - if (!layout) - return false; - - std::vector<base::ScopedFD> dmabuf_fds; - dmabuf_fds.reserve(num_planes); - for (size_t i = 0; i < num_planes; i++) { - dmabuf_fds.push_back(dmabuf_fds_data[i].TakeFD()); - DCHECK(dmabuf_fds.back().is_valid()); - } - frame = media::VideoFrame::WrapExternalDmabufs( - *layout, visible_rect, natural_size, std::move(dmabuf_fds), timestamp); -#endif } else if (data.is_gpu_memory_buffer_data()) { media::mojom::GpuMemoryBufferVideoFrameDataDataView gpu_memory_buffer_data; data.GetGpuMemoryBufferDataDataView(&gpu_memory_buffer_data); diff --git a/chromium/media/mojo/mojom/video_frame_mojom_traits_unittest.cc b/chromium/media/mojo/mojom/video_frame_mojom_traits_unittest.cc index 1ac03ed08eb..d00e80d8693 100644 --- a/chromium/media/mojo/mojom/video_frame_mojom_traits_unittest.cc +++ b/chromium/media/mojo/mojom/video_frame_mojom_traits_unittest.cc @@ -24,11 +24,6 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" -#if defined(OS_LINUX) || defined(OS_CHROMEOS) -#include <fcntl.h> -#include <sys/stat.h> -#endif - namespace media { namespace { @@ -102,41 +97,6 @@ TEST_F(VideoFrameStructTraitsTest, MojoSharedBufferVideoFrame) { } } -#if defined(OS_LINUX) || defined(OS_CHROMEOS) -TEST_F(VideoFrameStructTraitsTest, DmabufVideoFrame) { - const size_t num_planes = media::VideoFrame::NumPlanes(PIXEL_FORMAT_NV12); - std::vector<int> strides = {1280, 1280}; - std::vector<size_t> sizes = {1280 * 720, 1280 * 720 / 2}; - auto layout = media::VideoFrameLayout::CreateWithPlanes( - PIXEL_FORMAT_NV12, gfx::Size(1280, 720), - {media::ColorPlaneLayout(strides[0], 0, sizes[0]), - media::ColorPlaneLayout(strides[1], 0, sizes[1])}); - - // DMABUF needs device to create, use file fd instead. - std::vector<int> fake_fds = {open("/dev/null", O_RDWR), - open("/dev/zero", O_RDWR)}; - std::vector<base::ScopedFD> dmabuf_fds; - dmabuf_fds.reserve(num_planes); - for (size_t i = 0; i < num_planes; i++) - dmabuf_fds.emplace_back(fake_fds[i]); - - scoped_refptr<VideoFrame> frame = VideoFrame::WrapExternalDmabufs( - *layout, gfx::Rect(0, 0, 1280, 720), gfx::Size(1280, 720), - std::move(dmabuf_fds), base::TimeDelta::FromSeconds(100)); - - ASSERT_TRUE(RoundTrip(&frame)); - ASSERT_TRUE(frame); - EXPECT_FALSE(frame->metadata().end_of_stream); - EXPECT_EQ(frame->format(), PIXEL_FORMAT_NV12); - EXPECT_EQ(frame->coded_size(), gfx::Size(1280, 720)); - EXPECT_EQ(frame->visible_rect(), gfx::Rect(0, 0, 1280, 720)); - EXPECT_EQ(frame->natural_size(), gfx::Size(1280, 720)); - EXPECT_EQ(frame->timestamp(), base::TimeDelta::FromSeconds(100)); - ASSERT_TRUE(frame->HasDmaBufs()); - ASSERT_EQ(frame->storage_type(), VideoFrame::STORAGE_DMABUFS); -} -#endif - TEST_F(VideoFrameStructTraitsTest, MailboxVideoFrame) { gpu::Mailbox mailbox = gpu::Mailbox::Generate(); gpu::MailboxHolder mailbox_holder[VideoFrame::kMaxPlanes]; diff --git a/chromium/media/mojo/services/BUILD.gn b/chromium/media/mojo/services/BUILD.gn index 5565b1ffdb8..3a87515b49c 100644 --- a/chromium/media/mojo/services/BUILD.gn +++ b/chromium/media/mojo/services/BUILD.gn @@ -141,6 +141,7 @@ component("services") { deps += [ "//sandbox" ] } } + if (is_chromeos_ash) { deps += [ "//chromeos/components/cdm_factory_daemon:cdm_factory_daemon_gpu" ] @@ -152,6 +153,8 @@ component("services") { "media_foundation_mojo_media_client.h", "media_foundation_renderer_wrapper.cc", "media_foundation_renderer_wrapper.h", + "media_foundation_service.cc", + "media_foundation_service.h", ] deps += [ "//media/base/win:media_foundation_util" ] } diff --git a/chromium/media/mojo/services/cdm_service.cc b/chromium/media/mojo/services/cdm_service.cc index 025dffbded5..9e1babd8287 100644 --- a/chromium/media/mojo/services/cdm_service.cc +++ b/chromium/media/mojo/services/cdm_service.cc @@ -66,16 +66,22 @@ class CdmFactoryImpl final : public DeferredDestroy<mojom::CdmFactory> { auto* cdm_factory = GetCdmFactory(); if (!cdm_factory) { - std::move(callback).Run(mojo::NullRemote(), base::nullopt, - mojo::NullRemote(), + std::move(callback).Run(mojo::NullRemote(), nullptr, "CDM Factory creation failed"); return; } - MojoCdmService::Create( - cdm_factory, &cdm_service_context_, key_system, cdm_config, - base::BindOnce(&CdmFactoryImpl::OnCdmServiceCreated, - weak_ptr_factory_.GetWeakPtr(), std::move(callback))); + auto mojo_cdm_service = + std::make_unique<MojoCdmService>(&cdm_service_context_); + auto* raw_mojo_cdm_service = mojo_cdm_service.get(); + DCHECK(!pending_mojo_cdm_services_.count(raw_mojo_cdm_service)); + pending_mojo_cdm_services_[raw_mojo_cdm_service] = + std::move(mojo_cdm_service); + raw_mojo_cdm_service->Initialize( + cdm_factory, key_system, cdm_config, + base::BindOnce(&CdmFactoryImpl::OnCdmServiceInitialized, + weak_ptr_factory_.GetWeakPtr(), raw_mojo_cdm_service, + std::move(callback))); } // DeferredDestroy<mojom::CdmFactory> implemenation. @@ -100,22 +106,27 @@ class CdmFactoryImpl final : public DeferredDestroy<mojom::CdmFactory> { std::move(destroy_cb_).Run(); } - void OnCdmServiceCreated(CreateCdmCallback callback, - std::unique_ptr<MojoCdmService> cdm_service, - mojo::PendingRemote<mojom::Decryptor> decryptor, - const std::string& error_message) { - if (!cdm_service) { - std::move(callback).Run(mojo::NullRemote(), base::nullopt, - mojo::NullRemote(), error_message); + void OnCdmServiceInitialized(MojoCdmService* raw_mojo_cdm_service, + CreateCdmCallback callback, + mojom::CdmContextPtr cdm_context, + const std::string& error_message) { + DCHECK(raw_mojo_cdm_service); + + // Remove pending MojoCdmService from the mapping in all cases. + DCHECK(pending_mojo_cdm_services_.count(raw_mojo_cdm_service)); + auto mojo_cdm_service = + std::move(pending_mojo_cdm_services_[raw_mojo_cdm_service]); + pending_mojo_cdm_services_.erase(raw_mojo_cdm_service); + + if (!cdm_context) { + std::move(callback).Run(mojo::NullRemote(), nullptr, error_message); return; } - auto cdm_id = cdm_service->cdm_id(); mojo::PendingRemote<mojom::ContentDecryptionModule> remote; - cdm_receivers_.Add(std::move(cdm_service), + cdm_receivers_.Add(std::move(mojo_cdm_service), remote.InitWithNewPipeAndPassReceiver()); - std::move(callback).Run(std::move(remote), cdm_id, std::move(decryptor), - ""); + std::move(callback).Run(std::move(remote), std::move(cdm_context), ""); } // Must be declared before the receivers below because the bound objects might @@ -129,6 +140,10 @@ class CdmFactoryImpl final : public DeferredDestroy<mojom::CdmFactory> { std::unique_ptr<media::CdmFactory> cdm_factory_; base::OnceClosure destroy_cb_; + // MojoCdmServices pending initialization. + std::map<MojoCdmService*, std::unique_ptr<MojoCdmService>> + pending_mojo_cdm_services_; + // NOTE: Weak pointers must be invalidated before all other member variables. base::WeakPtrFactory<CdmFactoryImpl> weak_ptr_factory_{this}; diff --git a/chromium/media/mojo/services/cdm_service_unittest.cc b/chromium/media/mojo/services/cdm_service_unittest.cc index 3386e888e04..6c43549c92c 100644 --- a/chromium/media/mojo/services/cdm_service_unittest.cc +++ b/chromium/media/mojo/services/cdm_service_unittest.cc @@ -10,6 +10,7 @@ #include "base/test/task_environment.h" #include "base/unguessable_token.h" #include "build/build_config.h" +#include "media/base/mock_filters.h" #include "media/cdm/default_cdm_factory.h" #include "media/media_buildflags.h" #include "media/mojo/services/cdm_service.h" @@ -26,17 +27,46 @@ namespace { using testing::_; using testing::Invoke; using testing::InvokeWithoutArgs; +using testing::NiceMock; +using testing::Return; MATCHER_P(MatchesResult, success, "") { return arg->success == success; } -const char kClearKeyKeySystem[] = "org.w3.clearkey"; -const char kInvalidKeySystem[] = "invalid.key.system"; +// MockCdmFactory treats any non-empty key system as valid and the empty key +// system as invalid. +const char kValidKeySystem[] = "org.w3.clearkey"; +const char kInvalidKeySystem[] = ""; + +// Needed since MockCdmServiceClient needs to return unique_ptr of CdmFactory. +class CdmFactoryWrapper : public CdmFactory { + public: + explicit CdmFactoryWrapper(CdmFactory* cdm_factory) + : cdm_factory_(cdm_factory) {} + + // CdmFactory implementation. + void Create(const std::string& key_system, + const CdmConfig& cdm_config, + const SessionMessageCB& session_message_cb, + const SessionClosedCB& session_closed_cb, + const SessionKeysChangeCB& session_keys_change_cb, + const SessionExpirationUpdateCB& session_expiration_update_cb, + CdmCreatedCB cdm_created_cb) override { + cdm_factory_->Create(key_system, cdm_config, session_message_cb, + session_closed_cb, session_keys_change_cb, + session_expiration_update_cb, + std::move(cdm_created_cb)); + } + + private: + CdmFactory* const cdm_factory_; +}; class MockCdmServiceClient : public media::CdmService::Client { public: - MockCdmServiceClient() = default; + explicit MockCdmServiceClient(CdmFactory* cdm_factory) + : cdm_factory_(cdm_factory) {} ~MockCdmServiceClient() override = default; // media::CdmService::Client implementation. @@ -44,12 +74,15 @@ class MockCdmServiceClient : public media::CdmService::Client { std::unique_ptr<media::CdmFactory> CreateCdmFactory( mojom::FrameInterfaceFactory* frame_interfaces) override { - return std::make_unique<media::DefaultCdmFactory>(); + return std::make_unique<CdmFactoryWrapper>(cdm_factory_); } #if BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION) void AddCdmHostFilePaths(std::vector<media::CdmHostFilePath>*) override {} #endif // BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION) + + private: + CdmFactory* const cdm_factory_; }; class CdmServiceTest : public testing::Test { @@ -62,7 +95,11 @@ class CdmServiceTest : public testing::Test { MOCK_METHOD0(CdmConnectionClosed, void()); void Initialize() { - auto mock_cdm_service_client = std::make_unique<MockCdmServiceClient>(); + EXPECT_CALL(*mock_cdm_, GetCdmContext()) + .WillRepeatedly(Return(&cdm_context_)); + + auto mock_cdm_service_client = + std::make_unique<MockCdmServiceClient>(&mock_cdm_factory_); mock_cdm_service_client_ = mock_cdm_service_client.get(); service_ = std::make_unique<CdmService>( @@ -106,17 +143,21 @@ class CdmServiceTest : public testing::Test { mojo::Remote<mojom::CdmFactory> cdm_factory_remote_; mojo::Remote<mojom::ContentDecryptionModule> cdm_remote_; + // MojoCdmService will always create/use `mock_cdm_factory_` and `mock_cdm_`, + // so it's easier to set expectations on them. + scoped_refptr<MockCdm> mock_cdm_{new MockCdm()}; + MockCdmFactory mock_cdm_factory_{mock_cdm_}; + NiceMock<MockCdmContext> cdm_context_; + private: void OnCdmCreated(bool expected_result, mojo::PendingRemote<mojom::ContentDecryptionModule> remote, - const base::Optional<base::UnguessableToken>& cdm_id, - mojo::PendingRemote<mojom::Decryptor> decryptor, + media::mojom::CdmContextPtr cdm_context, const std::string& error_message) { if (!expected_result) { EXPECT_FALSE(remote); - EXPECT_FALSE(decryptor); + EXPECT_FALSE(cdm_context); EXPECT_TRUE(!error_message.empty()); - EXPECT_FALSE(cdm_id); return; } EXPECT_TRUE(remote); @@ -153,7 +194,7 @@ TEST_F(CdmServiceTest, LoadCdm) { TEST_F(CdmServiceTest, InitializeCdm_Success) { Initialize(); - InitializeCdm(kClearKeyKeySystem, true); + InitializeCdm(kValidKeySystem, true); } TEST_F(CdmServiceTest, InitializeCdm_InvalidKeySystem) { @@ -163,9 +204,9 @@ TEST_F(CdmServiceTest, InitializeCdm_InvalidKeySystem) { TEST_F(CdmServiceTest, DestroyAndRecreateCdm) { Initialize(); - InitializeCdm(kClearKeyKeySystem, true); + InitializeCdm(kValidKeySystem, true); cdm_remote_.reset(); - InitializeCdm(kClearKeyKeySystem, true); + InitializeCdm(kValidKeySystem, true); } // CdmFactory disconnection will cause the service to idle. @@ -173,7 +214,7 @@ TEST_F(CdmServiceTest, DestroyCdmFactory) { Initialize(); auto* service = cdm_service(); - InitializeCdm(kClearKeyKeySystem, true); + InitializeCdm(kValidKeySystem, true); EXPECT_EQ(service->BoundCdmFactorySizeForTesting(), 1u); EXPECT_EQ(service->UnboundCdmFactorySizeForTesting(), 0u); @@ -185,9 +226,9 @@ TEST_F(CdmServiceTest, DestroyCdmFactory) { } // Destroy service will destroy the CdmFactory and all CDMs. -TEST_F(CdmServiceTest, DestroyCdmService) { +TEST_F(CdmServiceTest, DestroyCdmService_AfterCdmCreation) { Initialize(); - InitializeCdm(kClearKeyKeySystem, true); + InitializeCdm(kValidKeySystem, true); base::RunLoop run_loop; // Ideally we should not care about order, and should only quit the loop when @@ -199,4 +240,17 @@ TEST_F(CdmServiceTest, DestroyCdmService) { run_loop.Run(); } +// Before the CDM is fully created, CdmService has been destroyed. We should +// fail gracefully instead of a crash. See crbug.com/1190319. +TEST_F(CdmServiceTest, DestroyCdmService_DuringCdmCreation) { + base::RunLoop run_loop; + EXPECT_CALL(*this, CdmFactoryConnectionClosed()) + .WillOnce(Invoke(&run_loop, &base::RunLoop::Quit)); + mock_cdm_factory_.SetBeforeCreationCB(base::BindRepeating( + &CdmServiceTest::DestroyService, base::Unretained(this))); + Initialize(); + InitializeCdm(kValidKeySystem, true); + run_loop.Run(); +} + } // namespace media diff --git a/chromium/media/mojo/services/gpu_mojo_media_client.cc b/chromium/media/mojo/services/gpu_mojo_media_client.cc index f11336e1a78..49eb8786b5f 100644 --- a/chromium/media/mojo/services/gpu_mojo_media_client.cc +++ b/chromium/media/mojo/services/gpu_mojo_media_client.cc @@ -54,8 +54,8 @@ #if defined(OS_ANDROID) #include "media/mojo/services/android_mojo_util.h" -using media::android_mojo_util::CreateProvisionFetcher; using media::android_mojo_util::CreateMediaDrmStorage; +using media::android_mojo_util::CreateProvisionFetcher; #endif // defined(OS_ANDROID) #if BUILDFLAG(IS_CHROMEOS_ASH) @@ -109,7 +109,7 @@ D3D11VideoDecoder::GetD3D11DeviceCB GetD3D11DeviceCallback() { // use. bool ShouldUseChromeOSDirectVideoDecoder( const gpu::GpuPreferences& gpu_preferences) { -#if BUILDFLAG(IS_CHROMEOS_ASH) +#if defined(OS_CHROMEOS) return gpu_preferences.enable_chromeos_direct_video_decoder; #else return false; @@ -313,6 +313,10 @@ std::unique_ptr<VideoDecoder> GpuMojoMediaClient::CreateVideoDecoder( if (!d3d11_supported_configs_) GetSupportedVideoDecoderConfigs(); + const bool enable_hdr = + gl::DirectCompositionSurfaceWin::IsHDRSupported() || + base::FeatureList::IsEnabled(kD3D11VideoDecoderForceEnableHDR); + video_decoder = D3D11VideoDecoder::Create( gpu_task_runner_, media_log->Clone(), gpu_preferences_, gpu_workarounds_, @@ -320,11 +324,10 @@ std::unique_ptr<VideoDecoder> GpuMojoMediaClient::CreateVideoDecoder( media_gpu_channel_manager_, command_buffer_id->channel_token, command_buffer_id->route_id), - GetD3D11DeviceCallback(), *d3d11_supported_configs_, - gl::DirectCompositionSurfaceWin::IsHDRSupported()); + GetD3D11DeviceCallback(), *d3d11_supported_configs_, enable_hdr); } #endif // defined(OS_WIN) - break; + break; }; // switch // |video_decoder| may be null if we don't support |implementation|. diff --git a/chromium/media/mojo/services/interface_factory_impl.cc b/chromium/media/mojo/services/interface_factory_impl.cc index fbfbbd24cbc..093389972db 100644 --- a/chromium/media/mojo/services/interface_factory_impl.cc +++ b/chromium/media/mojo/services/interface_factory_impl.cc @@ -23,22 +23,18 @@ #include "media/mojo/services/mojo_video_decoder_service.h" #endif // BUILDFLAG(ENABLE_MOJO_VIDEO_DECODER) -#if BUILDFLAG(ENABLE_MOJO_RENDERER) || BUILDFLAG(ENABLE_CAST_RENDERER) +#if BUILDFLAG(ENABLE_MOJO_RENDERER) || BUILDFLAG(ENABLE_CAST_RENDERER) || \ + defined(OS_WIN) #include "base/callback_helpers.h" #include "media/base/renderer.h" #include "media/mojo/services/mojo_renderer_service.h" -#endif // BUILDFLAG(ENABLE_MOJO_RENDERER) || BUILDFLAG(ENABLE_CAST_RENDERER) +#endif #if BUILDFLAG(ENABLE_MOJO_CDM) #include "media/base/cdm_factory.h" #include "media/mojo/services/mojo_cdm_service.h" #endif // BUILDFLAG(ENABLE_MOJO_CDM) -#if defined(OS_WIN) -#include "media/mojo/services/media_foundation_renderer_wrapper.h" -#include "media/mojo/services/mojo_renderer_service.h" -#endif // defined(OS_WIN) - namespace media { InterfaceFactoryImpl::InterfaceFactoryImpl( @@ -102,12 +98,7 @@ void InterfaceFactoryImpl::CreateDefaultRenderer( return; } - std::unique_ptr<MojoRendererService> mojo_renderer_service = - std::make_unique<MojoRendererService>(&cdm_service_context_, - std::move(renderer)); - - renderer_receivers_.Add(std::move(mojo_renderer_service), - std::move(receiver)); + AddRenderer(std::move(renderer), std::move(receiver)); #endif // BUILDFLAG(ENABLE_MOJO_RENDERER) } @@ -124,12 +115,7 @@ void InterfaceFactoryImpl::CreateCastRenderer( return; } - std::unique_ptr<MojoRendererService> mojo_renderer_service = - std::make_unique<MojoRendererService>(&cdm_service_context_, - std::move(renderer)); - - renderer_receivers_.Add(std::move(mojo_renderer_service), - std::move(receiver)); + AddRenderer(std::move(renderer), std::move(receiver)); } #endif @@ -157,13 +143,16 @@ void InterfaceFactoryImpl::CreateMediaFoundationRenderer( mojo::PendingReceiver<media::mojom::Renderer> receiver, mojo::PendingReceiver<media::mojom::MediaFoundationRendererExtension> renderer_extension_receiver) { - DVLOG(1) << __func__ << ": this=" << this; - - scoped_refptr<base::SingleThreadTaskRunner> task_runner = - base::ThreadTaskRunnerHandle::Get(); - CreateMediaFoundationRendererOnTaskRunner( - std::move(task_runner), std::move(receiver), + DVLOG(2) << __func__; + auto renderer = mojo_media_client_->CreateMediaFoundationRenderer( + base::ThreadTaskRunnerHandle::Get(), std::move(renderer_extension_receiver)); + if (!renderer) { + DLOG(ERROR) << "MediaFoundationRenderer creation failed."; + return; + } + + AddRenderer(std::move(renderer), std::move(receiver)); } #endif // defined (OS_WIN) @@ -174,17 +163,24 @@ void InterfaceFactoryImpl::CreateCdm(const std::string& key_system, #if BUILDFLAG(ENABLE_MOJO_CDM) CdmFactory* cdm_factory = GetCdmFactory(); if (!cdm_factory) { - std::move(callback).Run(mojo::NullRemote(), base::nullopt, - mojo::NullRemote(), "CDM Factory creation failed"); + std::move(callback).Run(mojo::NullRemote(), nullptr, + "CDM Factory creation failed"); return; } - MojoCdmService::Create( - cdm_factory, &cdm_service_context_, key_system, cdm_config, - base::BindOnce(&InterfaceFactoryImpl::OnCdmServiceCreated, - weak_ptr_factory_.GetWeakPtr(), std::move(callback))); + auto mojo_cdm_service = + std::make_unique<MojoCdmService>(&cdm_service_context_); + auto* raw_mojo_cdm_service = mojo_cdm_service.get(); + DCHECK(!pending_mojo_cdm_services_.count(raw_mojo_cdm_service)); + pending_mojo_cdm_services_[raw_mojo_cdm_service] = + std::move(mojo_cdm_service); + raw_mojo_cdm_service->Initialize( + cdm_factory, key_system, cdm_config, + base::BindOnce(&InterfaceFactoryImpl::OnCdmServiceInitialized, + weak_ptr_factory_.GetWeakPtr(), raw_mojo_cdm_service, + std::move(callback))); #else // BUILDFLAG(ENABLE_MOJO_CDM) - std::move(callback).Run(mojo::NullRemote(), base::nullopt, mojo::NullRemote(), + std::move(callback).Run(mojo::NullRemote(), nullptr, "Mojo CDM not supported"); #endif } @@ -256,6 +252,18 @@ void InterfaceFactoryImpl::OnReceiverDisconnect() { std::move(destroy_cb_).Run(); } +#if BUILDFLAG(ENABLE_MOJO_RENDERER) || BUILDFLAG(ENABLE_CAST_RENDERER) || \ + defined(OS_WIN) +void InterfaceFactoryImpl::AddRenderer( + std::unique_ptr<media::Renderer> renderer, + mojo::PendingReceiver<mojom::Renderer> receiver) { + auto mojo_renderer_service = std::make_unique<MojoRendererService>( + &cdm_service_context_, std::move(renderer)); + renderer_receivers_.Add(std::move(mojo_renderer_service), + std::move(receiver)); +} +#endif + #if BUILDFLAG(ENABLE_MOJO_CDM) CdmFactory* InterfaceFactoryImpl::GetCdmFactory() { if (!cdm_factory_) { @@ -266,53 +274,30 @@ CdmFactory* InterfaceFactoryImpl::GetCdmFactory() { return cdm_factory_.get(); } -void InterfaceFactoryImpl::OnCdmServiceCreated( +void InterfaceFactoryImpl::OnCdmServiceInitialized( + MojoCdmService* raw_mojo_cdm_service, CreateCdmCallback callback, - std::unique_ptr<MojoCdmService> cdm_service, - mojo::PendingRemote<mojom::Decryptor> decryptor, + mojom::CdmContextPtr cdm_context, const std::string& error_message) { - if (!cdm_service) { - std::move(callback).Run(mojo::NullRemote(), base::nullopt, - mojo::NullRemote(), error_message); + DCHECK(raw_mojo_cdm_service); + + // Remove pending MojoCdmService from the mapping in all cases. + DCHECK(pending_mojo_cdm_services_.count(raw_mojo_cdm_service)); + auto mojo_cdm_service = + std::move(pending_mojo_cdm_services_[raw_mojo_cdm_service]); + pending_mojo_cdm_services_.erase(raw_mojo_cdm_service); + + if (!cdm_context) { + std::move(callback).Run(mojo::NullRemote(), nullptr, error_message); return; } - auto cdm_id = cdm_service->cdm_id(); mojo::PendingRemote<mojom::ContentDecryptionModule> remote; - cdm_receivers_.Add(std::move(cdm_service), + cdm_receivers_.Add(std::move(mojo_cdm_service), remote.InitWithNewPipeAndPassReceiver()); - std::move(callback).Run(std::move(remote), cdm_id, std::move(decryptor), ""); + std::move(callback).Run(std::move(remote), std::move(cdm_context), ""); } #endif // BUILDFLAG(ENABLE_MOJO_CDM) -#if defined(OS_WIN) -void InterfaceFactoryImpl::CreateMediaFoundationRendererOnTaskRunner( - scoped_refptr<base::SingleThreadTaskRunner> task_runner, - mojo::PendingReceiver<media::mojom::Renderer> receiver, - mojo::PendingReceiver<media::mojom::MediaFoundationRendererExtension> - renderer_extension_receiver) { - DVLOG(1) << __func__ << ": this=" << this; - - if (!task_runner->RunsTasksInCurrentSequence()) { - task_runner->PostTask( - FROM_HERE, - base::BindOnce( - &InterfaceFactoryImpl::CreateMediaFoundationRendererOnTaskRunner, - base::Unretained(this), task_runner, std::move(receiver), - std::move(renderer_extension_receiver))); - return; - } - - DVLOG(1) << __func__ << ": this=" << this; - - auto renderer = std::make_unique<media::MediaFoundationRendererWrapper>( - /*muted=*/false, std::move(task_runner), - std::move(renderer_extension_receiver)); - - media::MojoRendererService::Create(&cdm_service_context_, std::move(renderer), - std::move(receiver)); -} -#endif // defined(OS_WIN) - } // namespace media diff --git a/chromium/media/mojo/services/interface_factory_impl.h b/chromium/media/mojo/services/interface_factory_impl.h index be55cc55dd4..d238edb589c 100644 --- a/chromium/media/mojo/services/interface_factory_impl.h +++ b/chromium/media/mojo/services/interface_factory_impl.h @@ -33,6 +33,7 @@ namespace media { class CdmFactory; class MojoMediaClient; +class Renderer; class InterfaceFactoryImpl final : public DeferredDestroy<mojom::InterfaceFactory> { @@ -53,7 +54,7 @@ class InterfaceFactoryImpl final #if BUILDFLAG(ENABLE_CAST_RENDERER) void CreateCastRenderer( const base::UnguessableToken& overlay_plane_id, - mojo::PendingReceiver<media::mojom::Renderer> receiver) final; + mojo::PendingReceiver<mojom::Renderer> receiver) final; #endif #if defined(OS_ANDROID) void CreateMediaPlayerRenderer( @@ -70,8 +71,8 @@ class InterfaceFactoryImpl final #endif // defined(OS_ANDROID) #if defined(OS_WIN) void CreateMediaFoundationRenderer( - mojo::PendingReceiver<media::mojom::Renderer> receiver, - mojo::PendingReceiver<media::mojom::MediaFoundationRendererExtension> + mojo::PendingReceiver<mojom::Renderer> receiver, + mojo::PendingReceiver<mojom::MediaFoundationRendererExtension> renderer_extension_receiver) final; #endif // defined(OS_WIN) @@ -90,22 +91,22 @@ class InterfaceFactoryImpl final void SetReceiverDisconnectHandler(); void OnReceiverDisconnect(); +#if BUILDFLAG(ENABLE_MOJO_RENDERER) || BUILDFLAG(ENABLE_CAST_RENDERER) || \ + defined(OS_WIN) + // Creates MojoRendererService for `renderer`, bind it to `receiver` and add + // them to `renderer_receivers_`. + void AddRenderer(std::unique_ptr<media::Renderer> renderer, + mojo::PendingReceiver<mojom::Renderer> receiver); +#endif + #if BUILDFLAG(ENABLE_MOJO_CDM) CdmFactory* GetCdmFactory(); - void OnCdmServiceCreated(CreateCdmCallback callback, - std::unique_ptr<MojoCdmService> cdm_service, - mojo::PendingRemote<mojom::Decryptor> decryptor, - const std::string& error_message); + void OnCdmServiceInitialized(MojoCdmService* raw_mojo_cdm_service, + CreateCdmCallback callback, + mojom::CdmContextPtr cdm_context, + const std::string& error_message); #endif // BUILDFLAG(ENABLE_MOJO_CDM) -#if defined(OS_WIN) - void CreateMediaFoundationRendererOnTaskRunner( - scoped_refptr<base::SingleThreadTaskRunner> task_runner, - mojo::PendingReceiver<media::mojom::Renderer> receiver, - mojo::PendingReceiver<media::mojom::MediaFoundationRendererExtension> - renderer_extension_receiver); -#endif // defined(OS_WIN) - // Must be declared before the receivers below because the bound objects might // take a raw pointer of |cdm_service_context_| and assume it's always // available. @@ -119,15 +120,20 @@ class InterfaceFactoryImpl final mojo::UniqueReceiverSet<mojom::VideoDecoder> video_decoder_receivers_; #endif // BUILDFLAG(ENABLE_MOJO_VIDEO_DECODER) -#if BUILDFLAG(ENABLE_MOJO_RENDERER) || BUILDFLAG(ENABLE_CAST_RENDERER) +#if BUILDFLAG(ENABLE_MOJO_RENDERER) || BUILDFLAG(ENABLE_CAST_RENDERER) || \ + defined(OS_WIN) // TODO(xhwang): Use MojoMediaLog for Renderer. NullMediaLog media_log_; mojo::UniqueReceiverSet<mojom::Renderer> renderer_receivers_; -#endif // BUILDFLAG(ENABLE_MOJO_RENDERER) || BUILDFLAG(ENABLE_CAST_RENDERER) +#endif #if BUILDFLAG(ENABLE_MOJO_CDM) std::unique_ptr<CdmFactory> cdm_factory_; mojo::UniqueReceiverSet<mojom::ContentDecryptionModule> cdm_receivers_; + + // MojoCdmServices pending initialization. + std::map<MojoCdmService*, std::unique_ptr<MojoCdmService>> + pending_mojo_cdm_services_; #endif // BUILDFLAG(ENABLE_MOJO_CDM) mojo::Remote<mojom::FrameInterfaceFactory> frame_interfaces_; diff --git a/chromium/media/mojo/services/media_foundation_mojo_media_client.cc b/chromium/media/mojo/services/media_foundation_mojo_media_client.cc index aa427c267db..27de9b14c14 100644 --- a/chromium/media/mojo/services/media_foundation_mojo_media_client.cc +++ b/chromium/media/mojo/services/media_foundation_mojo_media_client.cc @@ -4,22 +4,12 @@ #include "media/mojo/services/media_foundation_mojo_media_client.h" -#include "media/base/audio_decoder.h" #include "media/base/win/mf_helpers.h" -#include "media/cdm/cdm_adapter_factory.h" -#include "media/mojo/services/mojo_cdm_helper.h" +#include "media/cdm/win/media_foundation_cdm_factory.h" +#include "media/mojo/services/media_foundation_renderer_wrapper.h" namespace media { -namespace { - -std::unique_ptr<media::CdmAuxiliaryHelper> CreateCdmHelper( - mojom::FrameInterfaceFactory* frame_interfaces) { - return std::make_unique<media::MojoCdmHelper>(frame_interfaces); -} - -} // namespace - MediaFoundationMojoMediaClient::MediaFoundationMojoMediaClient() { DVLOG_FUNC(1); } @@ -28,17 +18,21 @@ MediaFoundationMojoMediaClient::~MediaFoundationMojoMediaClient() { DVLOG_FUNC(1); } -// MojoMediaClient overrides. +std::unique_ptr<Renderer> +MediaFoundationMojoMediaClient::CreateMediaFoundationRenderer( + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + mojo::PendingReceiver<mojom::MediaFoundationRendererExtension> + renderer_extension_receiver) { + DVLOG_FUNC(1); + return std::make_unique<MediaFoundationRendererWrapper>( + /*muted=*/false, std::move(task_runner), + std::move(renderer_extension_receiver)); +} -std::unique_ptr<media::CdmFactory> -MediaFoundationMojoMediaClient::CreateCdmFactory( +std::unique_ptr<CdmFactory> MediaFoundationMojoMediaClient::CreateCdmFactory( mojom::FrameInterfaceFactory* frame_interfaces) { DVLOG_FUNC(1); - - // TODO(frankli): consider to use MediaFoundationCdmFactory instead of - // CdmAdapterFactory. - return std::make_unique<media::CdmAdapterFactory>( - base::BindRepeating(&CreateCdmHelper, frame_interfaces)); + return std::make_unique<MediaFoundationCdmFactory>(); } } // namespace media diff --git a/chromium/media/mojo/services/media_foundation_mojo_media_client.h b/chromium/media/mojo/services/media_foundation_mojo_media_client.h index e476f0be1b8..45286fb39b1 100644 --- a/chromium/media/mojo/services/media_foundation_mojo_media_client.h +++ b/chromium/media/mojo/services/media_foundation_mojo_media_client.h @@ -1,4 +1,4 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. +// Copyright 2021 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. @@ -11,16 +11,20 @@ namespace media { -// This class is the |mojo_media_client| parameter to create -// media::MediaService. The MediaService itself is running in the mf_cdm utility -// process to host MediaFoundationRenderer/Cdm. -class MediaFoundationMojoMediaClient : public media::MojoMediaClient { +// MediaFoundation-specific MojoMediaClient implementation for +// MediaFoundationService running in the "Media Foundation Service" utility +// process hosting MediaFoundationRenderer and MediaFoundationCdm. +class MediaFoundationMojoMediaClient : public MojoMediaClient { public: MediaFoundationMojoMediaClient(); ~MediaFoundationMojoMediaClient() final; // MojoMediaClient implementation. - std::unique_ptr<media::CdmFactory> CreateCdmFactory( + std::unique_ptr<Renderer> CreateMediaFoundationRenderer( + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + mojo::PendingReceiver<mojom::MediaFoundationRendererExtension> + renderer_extension_receiver) final; + std::unique_ptr<CdmFactory> CreateCdmFactory( mojom::FrameInterfaceFactory* frame_interfaces) final; private: diff --git a/chromium/media/mojo/services/media_foundation_service.cc b/chromium/media/mojo/services/media_foundation_service.cc new file mode 100644 index 00000000000..892cbd96381 --- /dev/null +++ b/chromium/media/mojo/services/media_foundation_service.cc @@ -0,0 +1,48 @@ +// Copyright 2021 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/mojo/services/media_foundation_service.h" + +#include <memory> + +#include "base/bind.h" +#include "base/check.h" +#include "media/media_buildflags.h" +#include "media/mojo/mojom/interface_factory.mojom.h" +#include "media/mojo/services/interface_factory_impl.h" + +namespace media { + +MediaFoundationService::MediaFoundationService( + mojo::PendingReceiver<mojom::MediaFoundationService> receiver) + : receiver_(this, std::move(receiver)) { + mojo_media_client_.Initialize(); +} + +MediaFoundationService::~MediaFoundationService() = default; + +void MediaFoundationService::Initialize(const base::FilePath& cdm_path) { + // TODO(xhwang): Support loading MediaFoundation CDM and activating + // IMFContentDecryptionModuleFactory. + NOTIMPLEMENTED(); +} + +void MediaFoundationService::IsKeySystemSupported( + const std::string& key_system, + IsKeySystemSupportedCallback callback) { + // TODO(crbug.com/1115687): Implement MediaFoundation-based key system support + // query. + NOTIMPLEMENTED(); +} + +void MediaFoundationService::CreateInterfaceFactory( + mojo::PendingReceiver<mojom::InterfaceFactory> receiver, + mojo::PendingRemote<mojom::FrameInterfaceFactory> frame_interfaces) { + interface_factory_receivers_.Add( + std::make_unique<InterfaceFactoryImpl>(std::move(frame_interfaces), + &mojo_media_client_), + std::move(receiver)); +} + +} // namespace media diff --git a/chromium/media/mojo/services/media_foundation_service.h b/chromium/media/mojo/services/media_foundation_service.h new file mode 100644 index 00000000000..ad25361a377 --- /dev/null +++ b/chromium/media/mojo/services/media_foundation_service.h @@ -0,0 +1,48 @@ +// Copyright 2021 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_MOJO_SERVICES_MEDIA_FOUNDATION_SERVICE_H_ +#define MEDIA_MOJO_SERVICES_MEDIA_FOUNDATION_SERVICE_H_ + +#include "build/build_config.h" +#include "media/mojo/mojom/frame_interface_factory.mojom.h" +#include "media/mojo/mojom/interface_factory.mojom.h" +#include "media/mojo/mojom/media_foundation_service.mojom.h" +#include "media/mojo/services/media_foundation_mojo_media_client.h" +#include "media/mojo/services/media_mojo_export.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/unique_receiver_set.h" + +namespace media { + +// This class is similar to media::CdmService and media::MediaService, with +// extra support for CDM preloading and key system support query. +class MEDIA_MOJO_EXPORT MediaFoundationService final + : public mojom::MediaFoundationService { + public: + explicit MediaFoundationService( + mojo::PendingReceiver<mojom::MediaFoundationService> receiver); + MediaFoundationService(const MediaFoundationService&) = delete; + MediaFoundationService operator=(const MediaFoundationService&) = delete; + ~MediaFoundationService() final; + + private: + // mojom::MediaFoundationService implementation: + void Initialize(const base::FilePath& cdm_path) final; + void IsKeySystemSupported(const std::string& key_system, + IsKeySystemSupportedCallback callback) final; + void CreateInterfaceFactory( + mojo::PendingReceiver<mojom::InterfaceFactory> receiver, + mojo::PendingRemote<mojom::FrameInterfaceFactory> frame_interfaces) final; + + mojo::Receiver<mojom::MediaFoundationService> receiver_; + MediaFoundationMojoMediaClient mojo_media_client_; + mojo::UniqueReceiverSet<mojom::InterfaceFactory> interface_factory_receivers_; +}; + +} // namespace media + +#endif // MEDIA_MOJO_SERVICES_MEDIA_FOUNDATION_SERVICE_H_ diff --git a/chromium/media/mojo/services/media_metrics_provider_unittest.cc b/chromium/media/mojo/services/media_metrics_provider_unittest.cc index d22d954904e..972f79d7dee 100644 --- a/chromium/media/mojo/services/media_metrics_provider_unittest.cc +++ b/chromium/media/mojo/services/media_metrics_provider_unittest.cc @@ -68,7 +68,7 @@ class MediaMetricsProviderTest : public testing::Test { void ResetMetricRecorders() { // Ensure cleared global before attempting to create a new TestUkmReporter. test_recorder_.reset(); - test_recorder_.reset(new ukm::TestAutoSetUkmRecorder()); + test_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>(); } protected: diff --git a/chromium/media/mojo/services/media_service_factory.cc b/chromium/media/mojo/services/media_service_factory.cc index 27ebebc83e6..cf221050482 100644 --- a/chromium/media/mojo/services/media_service_factory.cc +++ b/chromium/media/mojo/services/media_service_factory.cc @@ -7,7 +7,6 @@ #include <memory> #include "base/notreached.h" -#include "build/build_config.h" #include "media/mojo/buildflags.h" #include "media/mojo/services/gpu_mojo_media_client.h" #include "media/mojo/services/media_service.h" @@ -28,11 +27,6 @@ std::unique_ptr<MediaService> CreateMediaService( #if defined(OS_ANDROID) return std::make_unique<MediaService>( std::make_unique<AndroidMojoMediaClient>(), std::move(receiver)); -#elif defined(OS_WIN) - DVLOG(1) << "Create MediaService with MediaFoundationMojoMediaClient"; - return std::make_unique<MediaService>( - std::make_unique<media::MediaFoundationMojoMediaClient>(), - std::move(receiver)); #else NOTREACHED() << "No MediaService implementation available."; return nullptr; diff --git a/chromium/media/mojo/services/media_service_factory.h b/chromium/media/mojo/services/media_service_factory.h index d0052bf2acf..7b613d7c803 100644 --- a/chromium/media/mojo/services/media_service_factory.h +++ b/chromium/media/mojo/services/media_service_factory.h @@ -10,6 +10,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" +#include "build/build_config.h" #include "gpu/config/gpu_driver_bug_workarounds.h" #include "gpu/config/gpu_feature_info.h" #include "gpu/config/gpu_preferences.h" diff --git a/chromium/media/mojo/services/media_service_unittest.cc b/chromium/media/mojo/services/media_service_unittest.cc index 9f8855a991e..ac1cba2aa34 100644 --- a/chromium/media/mojo/services/media_service_unittest.cc +++ b/chromium/media/mojo/services/media_service_unittest.cc @@ -138,8 +138,8 @@ class MediaServiceTest : public testing::Test { video_stream_.set_video_decoder_config(video_config); mojo::PendingRemote<mojom::DemuxerStream> video_stream_proxy; - mojo_video_stream_.reset(new MojoDemuxerStreamImpl( - &video_stream_, video_stream_proxy.InitWithNewPipeAndPassReceiver())); + mojo_video_stream_ = std::make_unique<MojoDemuxerStreamImpl>( + &video_stream_, video_stream_proxy.InitWithNewPipeAndPassReceiver()); mojo::PendingAssociatedRemote<mojom::RendererClient> client_remote; renderer_client_receiver_.Bind( @@ -162,14 +162,12 @@ class MediaServiceTest : public testing::Test { protected: void OnCdmCreated(bool expected_result, mojo::PendingRemote<mojom::ContentDecryptionModule> remote, - const base::Optional<base::UnguessableToken>& cdm_id, - mojo::PendingRemote<mojom::Decryptor> decryptor, + media::mojom::CdmContextPtr cdm_context, const std::string& error_message) { if (!expected_result) { EXPECT_FALSE(remote); - EXPECT_FALSE(decryptor); + EXPECT_FALSE(cdm_context); EXPECT_TRUE(!error_message.empty()); - EXPECT_FALSE(cdm_id); return; } EXPECT_TRUE(remote); diff --git a/chromium/media/mojo/services/mojo_audio_decoder_service.cc b/chromium/media/mojo/services/mojo_audio_decoder_service.cc index 589bce46a40..90f071bb723 100644 --- a/chromium/media/mojo/services/mojo_audio_decoder_service.cc +++ b/chromium/media/mojo/services/mojo_audio_decoder_service.cc @@ -4,6 +4,7 @@ #include "media/mojo/services/mojo_audio_decoder_service.h" +#include <memory> #include <utility> #include "base/bind.h" @@ -83,8 +84,8 @@ void MojoAudioDecoderService::SetDataSource( mojo::ScopedDataPipeConsumerHandle receive_pipe) { DVLOG(1) << __func__; - mojo_decoder_buffer_reader_.reset( - new MojoDecoderBufferReader(std::move(receive_pipe))); + mojo_decoder_buffer_reader_ = + std::make_unique<MojoDecoderBufferReader>(std::move(receive_pipe)); } void MojoAudioDecoderService::Decode(mojom::DecoderBufferPtr buffer, @@ -110,8 +111,9 @@ void MojoAudioDecoderService::OnInitialized(InitializeCallback callback, if (!status.is_ok()) { // Do not call decoder_->NeedsBitstreamConversion() if init failed. - std::move(callback).Run(std::move(status), false, - AudioDecoderType::kUnknown); + std::move(callback).Run( + std::move(status), false, + decoder_ ? decoder_->GetDecoderType() : AudioDecoderType::kUnknown); return; } diff --git a/chromium/media/mojo/services/mojo_cdm_service.cc b/chromium/media/mojo/services/mojo_cdm_service.cc index 8352f58df35..785f2c96289 100644 --- a/chromium/media/mojo/services/mojo_cdm_service.cc +++ b/chromium/media/mojo/services/mojo_cdm_service.cc @@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/macros.h" #include "base/synchronization/lock.h" +#include "build/build_config.h" #include "media/base/cdm_config.h" #include "media/base/cdm_context.h" #include "media/base/cdm_factory.h" @@ -32,46 +33,39 @@ using NewSessionMojoCdmPromise = MojoCdmPromise<void(mojom::CdmPromiseResultPtr, const std::string&), std::string>; -// static -void MojoCdmService::Create(CdmFactory* cdm_factory, - MojoCdmServiceContext* context, - const std::string& key_system, - const CdmConfig& cdm_config, - CdmServiceCreatedCB callback) { - std::unique_ptr<MojoCdmService> mojo_cdm_service( - new MojoCdmService(cdm_factory, context)); - auto weak_this = mojo_cdm_service->weak_factory_.GetWeakPtr(); - cdm_factory->Create( - key_system, cdm_config, - base::BindRepeating(&MojoCdmService::OnSessionMessage, weak_this), - base::BindRepeating(&MojoCdmService::OnSessionClosed, weak_this), - base::BindRepeating(&MojoCdmService::OnSessionKeysChange, weak_this), - base::BindRepeating(&MojoCdmService::OnSessionExpirationUpdate, - weak_this), - base::BindOnce(&MojoCdmService::OnCdmCreated, weak_this, - std::move(mojo_cdm_service), - mojo::WrapCallbackWithDefaultInvokeIfNotRun( - std::move(callback), nullptr, mojo::NullRemote(), - "Mojo CDM Service creation failure"))); -} - -MojoCdmService::MojoCdmService(CdmFactory* cdm_factory, - MojoCdmServiceContext* context) - : cdm_factory_(cdm_factory), context_(context) { +MojoCdmService::MojoCdmService(MojoCdmServiceContext* context) + : context_(context) { DVLOG(1) << __func__; - DCHECK(cdm_factory_); - // |context_| can be null. + DCHECK(context_); } MojoCdmService::~MojoCdmService() { DVLOG(1) << __func__; - if (!context_ || !cdm_id_) + if (!cdm_id_) return; context_->UnregisterCdm(cdm_id_.value()); } +void MojoCdmService::Initialize(CdmFactory* cdm_factory, + const std::string& key_system, + const CdmConfig& cdm_config, + InitializeCB init_cb) { + auto weak_this = weak_factory_.GetWeakPtr(); + cdm_factory->Create( + key_system, cdm_config, + base::BindRepeating(&MojoCdmService::OnSessionMessage, weak_this), + base::BindRepeating(&MojoCdmService::OnSessionClosed, weak_this), + base::BindRepeating(&MojoCdmService::OnSessionKeysChange, weak_this), + base::BindRepeating(&MojoCdmService::OnSessionExpirationUpdate, + weak_this), + base::BindOnce(&MojoCdmService::OnCdmCreated, weak_this, + mojo::WrapCallbackWithDefaultInvokeIfNotRun( + std::move(init_cb), nullptr, + "Mojo CDM Service creation aborted"))); +} + void MojoCdmService::SetClient( mojo::PendingAssociatedRemote<mojom::ContentDecryptionModuleClient> client) { @@ -143,51 +137,58 @@ scoped_refptr<ContentDecryptionModule> MojoCdmService::GetCdm() { } void MojoCdmService::OnCdmCreated( - std::unique_ptr<MojoCdmService> mojo_cdm_service, - CdmServiceCreatedCB callback, + InitializeCB init_cb, const scoped_refptr<::media::ContentDecryptionModule>& cdm, const std::string& error_message) { - DVLOG(2) << __func__ << ": error_message=" << error_message; - // TODO(xhwang): This should not happen when KeySystemInfo is properly // populated. See http://crbug.com/469366 if (!cdm) { // Make sure the error string is non-empty on failure. - std::move(callback).Run( - nullptr, mojo::NullRemote(), - error_message.empty() ? "CDM initialization failed" : error_message); + auto non_empty_error_message = + error_message.empty() ? "CDM creation failed" : error_message; + DVLOG(1) << __func__ << ": " << non_empty_error_message; + std::move(init_cb).Run(nullptr, non_empty_error_message); return; } cdm_ = cdm; + cdm_id_ = context_->RegisterCdm(this); + DVLOG(1) << __func__ << ": CDM successfully registered with ID " + << CdmContext::CdmIdToString(base::OptionalOrNullptr(cdm_id_)); - if (context_) { - cdm_id_ = context_->RegisterCdm(this); - DVLOG(1) << __func__ << ": CDM successfully registered with ID " - << CdmContext::CdmIdToString(base::OptionalOrNullptr(cdm_id_)); - } + auto mojo_cdm_context = mojom::CdmContext::New(); + mojo_cdm_context->cdm_id = cdm_id(); - // If |cdm| has a decryptor, create the MojoDecryptorService - // and pass the connection back to the client. - mojo::PendingRemote<mojom::Decryptor> decryptor_remote; CdmContext* const cdm_context = cdm_->GetCdmContext(); - if (cdm_context && cdm_context->GetDecryptor()) { - DVLOG(2) << __func__ << ": CDM supports Decryptor."; - // Both |cdm_| and |decryptor_| are owned by |this|, so we don't need to - // pass in a CdmContextRef. - decryptor_.reset( - new MojoDecryptorService(cdm_context->GetDecryptor(), nullptr)); - decryptor_receiver_ = std::make_unique<mojo::Receiver<mojom::Decryptor>>( - decryptor_.get(), decryptor_remote.InitWithNewPipeAndPassReceiver()); - // base::Unretained is safe because |decryptor_receiver_| is owned by - // |this|. If |this| is destructed, |decryptor_receiver_| will be destructed - // as well and the error handler should never be called. - decryptor_receiver_->set_disconnect_handler(base::BindOnce( - &MojoCdmService::OnDecryptorConnectionError, base::Unretained(this))); + if (cdm_context) { + // If |cdm| has a decryptor, create the MojoDecryptorService + // and pass the connection back to the client. + if (cdm_context->GetDecryptor()) { + DVLOG(2) << __func__ << ": CDM supports Decryptor."; + mojo::PendingRemote<mojom::Decryptor> decryptor_remote; + // Both |cdm_| and |decryptor_| are owned by |this|, so we don't need to + // pass in a CdmContextRef. + decryptor_ = std::make_unique<MojoDecryptorService>( + cdm_context->GetDecryptor(), /*cdm_context_ref=*/nullptr); + decryptor_receiver_ = std::make_unique<mojo::Receiver<mojom::Decryptor>>( + decryptor_.get(), decryptor_remote.InitWithNewPipeAndPassReceiver()); + // base::Unretained is safe because |decryptor_receiver_| is owned by + // |this|. If |this| is destructed, |decryptor_receiver_| will be + // destructed as well and the error handler should never be called. + // The disconnection can happen due to race conditions during render + // process teardown or crash. + decryptor_receiver_->set_disconnect_handler(base::BindOnce( + &MojoCdmService::OnDecryptorConnectionError, base::Unretained(this))); + mojo_cdm_context->decryptor = std::move(decryptor_remote); + } + +#if defined(OS_WIN) + mojo_cdm_context->requires_media_foundation_renderer = + cdm_context->RequiresMediaFoundationRenderer(); +#endif // defined(OS_WIN) } - std::move(callback).Run(std::move(mojo_cdm_service), - std::move(decryptor_remote), ""); + std::move(init_cb).Run(std::move(mojo_cdm_context), ""); } void MojoCdmService::OnSessionMessage(const std::string& session_id, @@ -230,7 +231,8 @@ void MojoCdmService::OnDecryptorConnectionError() { DVLOG(2) << __func__; // MojoDecryptorService has lost connectivity to it's client, so it can be - // freed. + // freed. This could happen due to render process teardown or crash. No need + // for recovery. decryptor_.reset(); } diff --git a/chromium/media/mojo/services/mojo_cdm_service.h b/chromium/media/mojo/services/mojo_cdm_service.h index ec27caa040d..18195fb1c73 100644 --- a/chromium/media/mojo/services/mojo_cdm_service.h +++ b/chromium/media/mojo/services/mojo_cdm_service.h @@ -35,25 +35,23 @@ class CdmFactory; class MEDIA_MOJO_EXPORT MojoCdmService final : public mojom::ContentDecryptionModule { public: - using CdmServiceCreatedCB = - base::OnceCallback<void(std::unique_ptr<MojoCdmService> mojo_cdm_service, - mojo::PendingRemote<mojom::Decryptor> decryptor, + // Callback for Initialize(). Non-null `cdm_context` indicates success. Null + // `cdm_context` indicates failure and the `error_message` provides a reason. + using InitializeCB = + base::OnceCallback<void(mojom::CdmContextPtr cdm_context, const std::string& error_message)>; - // Creates a MojoCdmService. Callback will have |mojo_cdm_service| be non-null - // on success, on failure it will be null and the |error_message| will - // indicate a reason. - // - |cdm_factory| is used to create CDM instances. Must not be null. - // - |context| is used to keep track of all CDM instances such that we can - // connect the CDM with a media player (e.g. decoder). Can be null if the - // CDM does not need to be connected with any media player in this process. - static void Create(CdmFactory* cdm_factory, - MojoCdmServiceContext* context, - const std::string& key_system, - const CdmConfig& cdm_config, - CdmServiceCreatedCB callback); - + explicit MojoCdmService(MojoCdmServiceContext* context); ~MojoCdmService() final; + + // Initialize the MojoCdmService, including creating the real CDM using the + // `cdm_factory`, which must not be null. The MojoCdmService should NOT be + // used before the `init_cb` is returned. + void Initialize(CdmFactory* cdm_factory, + const std::string& key_system, + const CdmConfig& cdm_config, + InitializeCB init_cb); + // mojom::ContentDecryptionModule implementation. void SetClient( mojo::PendingAssociatedRemote<mojom::ContentDecryptionModuleClient> @@ -82,14 +80,11 @@ class MEDIA_MOJO_EXPORT MojoCdmService final scoped_refptr<::media::ContentDecryptionModule> GetCdm(); // Gets the remote ID of the CDM this is holding. - base::Optional<base::UnguessableToken> cdm_id() const { return cdm_id_; } + base::UnguessableToken cdm_id() const { return cdm_id_.value(); } private: - MojoCdmService(CdmFactory* cdm_factory, MojoCdmServiceContext* context); - // Callback for CdmFactory::Create(). - void OnCdmCreated(std::unique_ptr<MojoCdmService> mojo_cdm_service, - CdmServiceCreatedCB callback, + void OnCdmCreated(InitializeCB callback, const scoped_refptr<::media::ContentDecryptionModule>& cdm, const std::string& error_message); @@ -107,8 +102,7 @@ class MEDIA_MOJO_EXPORT MojoCdmService final // Callback for when |decryptor_| loses connectivity. void OnDecryptorConnectionError(); - CdmFactory* cdm_factory_; - MojoCdmServiceContext* const context_ = nullptr; + MojoCdmServiceContext* const context_; scoped_refptr<::media::ContentDecryptionModule> cdm_; // MojoDecryptorService is passed the Decryptor from |cdm_|, so diff --git a/chromium/media/mojo/services/mojo_decryptor_service.cc b/chromium/media/mojo/services/mojo_decryptor_service.cc index d113b425667..96b757d0697 100644 --- a/chromium/media/mojo/services/mojo_decryptor_service.cc +++ b/chromium/media/mojo/services/mojo_decryptor_service.cc @@ -4,6 +4,7 @@ #include "media/mojo/services/mojo_decryptor_service.h" +#include <memory> #include <utility> #include "base/bind.h" @@ -78,14 +79,14 @@ void MojoDecryptorService::Initialize( } has_initialize_been_called_ = true; - audio_buffer_reader_.reset( - new MojoDecoderBufferReader(std::move(audio_pipe))); - video_buffer_reader_.reset( - new MojoDecoderBufferReader(std::move(video_pipe))); - decrypt_buffer_reader_.reset( - new MojoDecoderBufferReader(std::move(decrypt_pipe))); - decrypted_buffer_writer_.reset( - new MojoDecoderBufferWriter(std::move(decrypted_pipe))); + audio_buffer_reader_ = + std::make_unique<MojoDecoderBufferReader>(std::move(audio_pipe)); + video_buffer_reader_ = + std::make_unique<MojoDecoderBufferReader>(std::move(video_pipe)); + decrypt_buffer_reader_ = + std::make_unique<MojoDecoderBufferReader>(std::move(decrypt_pipe)); + decrypted_buffer_writer_ = + std::make_unique<MojoDecoderBufferWriter>(std::move(decrypted_pipe)); } void MojoDecryptorService::Decrypt(StreamType stream_type, diff --git a/chromium/media/mojo/services/mojo_demuxer_stream_adapter.cc b/chromium/media/mojo/services/mojo_demuxer_stream_adapter.cc index accafab8e28..d9f40c7eefc 100644 --- a/chromium/media/mojo/services/mojo_demuxer_stream_adapter.cc +++ b/chromium/media/mojo/services/mojo_demuxer_stream_adapter.cc @@ -5,6 +5,8 @@ #include "media/mojo/services/mojo_demuxer_stream_adapter.h" #include <stdint.h> + +#include <memory> #include <utility> #include "base/bind.h" @@ -76,8 +78,8 @@ void MojoDemuxerStreamAdapter::OnStreamReady( type_ = type; - mojo_decoder_buffer_reader_.reset( - new MojoDecoderBufferReader(std::move(consumer_handle))); + mojo_decoder_buffer_reader_ = + std::make_unique<MojoDecoderBufferReader>(std::move(consumer_handle)); UpdateConfig(std::move(audio_config), std::move(video_config)); diff --git a/chromium/media/mojo/services/mojo_media_client.cc b/chromium/media/mojo/services/mojo_media_client.cc index b9f53ad90c5..70303624d74 100644 --- a/chromium/media/mojo/services/mojo_media_client.cc +++ b/chromium/media/mojo/services/mojo_media_client.cc @@ -57,6 +57,15 @@ std::unique_ptr<Renderer> MojoMediaClient::CreateCastRenderer( } #endif // BUILDFLAG(ENABLE_CAST_RENDERER) +#if defined(OS_WIN) +std::unique_ptr<Renderer> MojoMediaClient::CreateMediaFoundationRenderer( + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + mojo::PendingReceiver<mojom::MediaFoundationRendererExtension> + renderer_extension_receiver) { + return nullptr; +} +#endif // defined(OS_WIN) + std::unique_ptr<CdmFactory> MojoMediaClient::CreateCdmFactory( mojom::FrameInterfaceFactory* frame_interfaces) { return nullptr; diff --git a/chromium/media/mojo/services/mojo_media_client.h b/chromium/media/mojo/services/mojo_media_client.h index 8d0ff976a04..cba23037f32 100644 --- a/chromium/media/mojo/services/mojo_media_client.h +++ b/chromium/media/mojo/services/mojo_media_client.h @@ -17,6 +17,7 @@ #include "media/media_buildflags.h" #include "media/mojo/buildflags.h" #include "media/mojo/mojom/frame_interface_factory.mojom.h" +#include "media/mojo/mojom/renderer_extensions.mojom.h" #include "media/mojo/mojom/video_decoder.mojom.h" #include "media/mojo/services/media_mojo_export.h" @@ -91,9 +92,16 @@ class MEDIA_MOJO_EXPORT MojoMediaClient { const base::UnguessableToken& overlay_plane_id); #endif // BUILDFLAG(ENABLE_CAST_RENDERER) - // Returns the CdmFactory to be used by MojoCdmService. |frame_interfaces| can - // be used to request interfaces provided remotely by the host. It may be a - // nullptr if the host chose not to bind the InterfacePtr. +#if defined(OS_WIN) + virtual std::unique_ptr<Renderer> CreateMediaFoundationRenderer( + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + mojo::PendingReceiver<mojom::MediaFoundationRendererExtension> + renderer_extension_receiver); +#endif // defined(OS_WIN) + + // Returns the CdmFactory to be used by MojoCdmService. |frame_interfaces| + // can be used to request interfaces provided remotely by the host. It may + // be a nullptr if the host chose not to bind the InterfacePtr. virtual std::unique_ptr<CdmFactory> CreateCdmFactory( mojom::FrameInterfaceFactory* frame_interfaces); diff --git a/chromium/media/mojo/services/mojo_renderer_service.cc b/chromium/media/mojo/services/mojo_renderer_service.cc index 102932c7b5e..5fbf6cf0e4c 100644 --- a/chromium/media/mojo/services/mojo_renderer_service.cc +++ b/chromium/media/mojo/services/mojo_renderer_service.cc @@ -4,6 +4,7 @@ #include "media/mojo/services/mojo_renderer_service.h" +#include <memory> #include <utility> #include "base/bind.h" @@ -73,10 +74,10 @@ void MojoRendererService::Initialize( } DCHECK(!media_url_params->media_url.is_empty()); - media_resource_.reset(new MediaUrlDemuxer( + media_resource_ = std::make_unique<MediaUrlDemuxer>( nullptr, media_url_params->media_url, media_url_params->site_for_cookies, media_url_params->top_frame_origin, media_url_params->allow_credentials, - media_url_params->is_hls)); + media_url_params->is_hls); renderer_->Initialize( media_resource_.get(), this, base::BindOnce(&MojoRendererService::OnRendererInitializeDone, weak_this_, diff --git a/chromium/media/mojo/services/mojo_video_decoder_service.cc b/chromium/media/mojo/services/mojo_video_decoder_service.cc index 450d27ecd64..19e7d34f60a 100644 --- a/chromium/media/mojo/services/mojo_video_decoder_service.cc +++ b/chromium/media/mojo/services/mojo_video_decoder_service.cc @@ -4,15 +4,19 @@ #include "media/mojo/services/mojo_video_decoder_service.h" +#include <memory> #include <utility> #include "base/bind.h" #include "base/callback_helpers.h" #include "base/logging.h" #include "base/macros.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/optional.h" +#include "base/strings/stringprintf.h" #include "base/threading/thread_task_runner_handle.h" +#include "base/timer/elapsed_timer.h" #include "media/base/decoder_buffer.h" #include "media/base/simple_sync_token_client.h" #include "media/base/video_decoder.h" @@ -43,6 +47,18 @@ const char kInitializeTraceName[] = "MojoVideoDecoderService::Initialize"; const char kDecodeTraceName[] = "MojoVideoDecoderService::Decode"; const char kResetTraceName[] = "MojoVideoDecoderService::Reset"; +void RecordTimingHistogram(VideoDecoderImplementation impl, + const char* method, + base::TimeDelta elapsed) { + base::UmaHistogramTimes( + base::StringPrintf("Media.MojoVideoDecoderServiceTiming.%s.%s", + impl == VideoDecoderImplementation::kDefault + ? "Default" + : "Alternate", + method), + elapsed); +} + } // namespace class VideoFrameHandleReleaserImpl final @@ -99,6 +115,7 @@ MojoVideoDecoderService::MojoVideoDecoderService( MojoVideoDecoderService::~MojoVideoDecoderService() { DVLOG(1) << __func__; + base::ElapsedTimer elapsed; if (init_cb_) { OnDecoderInitialized( @@ -111,6 +128,14 @@ MojoVideoDecoderService::~MojoVideoDecoderService() { if (is_active_instance_) g_num_active_mvd_instances--; + + // Destruct the VideoDecoder here so its destruction duration is included by + // the histogram timer below. + weak_factory_.InvalidateWeakPtrs(); + decoder_.reset(); + + if (implementation_) + RecordTimingHistogram(*implementation_, "Destruct", elapsed.Elapsed()); } void MojoVideoDecoderService::GetSupportedConfigs( @@ -139,6 +164,9 @@ void MojoVideoDecoderService::Construct( return; } + base::ElapsedTimer elapsed; + implementation_ = implementation; + client_.Bind(std::move(client)); scoped_refptr<base::SingleThreadTaskRunner> task_runner = @@ -151,8 +179,8 @@ void MojoVideoDecoderService::Construct( std::make_unique<VideoFrameHandleReleaserImpl>(), std::move(video_frame_handle_releaser_receiver)); - mojo_decoder_buffer_reader_.reset( - new MojoDecoderBufferReader(std::move(decoder_buffer_pipe))); + mojo_decoder_buffer_reader_ = + std::make_unique<MojoDecoderBufferReader>(std::move(decoder_buffer_pipe)); decoder_ = mojo_media_client_->CreateVideoDecoder( task_runner, media_log_.get(), std::move(command_buffer_id), @@ -160,6 +188,8 @@ void MojoVideoDecoderService::Construct( base::BindRepeating( &MojoVideoDecoderService::OnDecoderRequestedOverlayInfo, weak_this_), target_color_space); + + RecordTimingHistogram(*implementation_, "Construct", elapsed.Elapsed()); } void MojoVideoDecoderService::Initialize( @@ -281,7 +311,9 @@ void MojoVideoDecoderService::OnDecoderInitialized(Status status) { status.code()); if (!status.is_ok()) { - std::move(init_cb_).Run(status, false, 1, VideoDecoderType::kUnknown); + std::move(init_cb_).Run( + status, false, 1, + decoder_ ? decoder_->GetDecoderType() : VideoDecoderType::kUnknown); return; } std::move(init_cb_).Run(status, decoder_->NeedsBitstreamConversion(), diff --git a/chromium/media/mojo/services/mojo_video_decoder_service.h b/chromium/media/mojo/services/mojo_video_decoder_service.h index da0ff47e90f..d04d177d3bf 100644 --- a/chromium/media/mojo/services/mojo_video_decoder_service.h +++ b/chromium/media/mojo/services/mojo_video_decoder_service.h @@ -86,6 +86,9 @@ class MEDIA_MOJO_EXPORT MojoVideoDecoderService final bool restart_for_transitions, ProvideOverlayInfoCB provide_overlay_info_cb); + // Implementation value provided at the time of Construct(). + base::Optional<VideoDecoderImplementation> implementation_; + // Whether this instance is active (Decode() was called at least once). bool is_active_instance_ = false; diff --git a/chromium/media/mojo/services/test_mojo_media_client.cc b/chromium/media/mojo/services/test_mojo_media_client.cc index 7d51f3db006..8a9a71f4d29 100644 --- a/chromium/media/mojo/services/test_mojo_media_client.cc +++ b/chromium/media/mojo/services/test_mojo_media_client.cc @@ -6,6 +6,7 @@ #include <memory> +#include "base/callback_helpers.h" #include "base/run_loop.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" @@ -56,7 +57,7 @@ std::unique_ptr<Renderer> TestMojoMediaClient::CreateRenderer( const std::string& /* audio_device_id */) { // If called the first time, do one time initialization. if (!decoder_factory_) { - decoder_factory_.reset(new media::DefaultDecoderFactory(nullptr)); + decoder_factory_ = std::make_unique<media::DefaultDecoderFactory>(nullptr); } if (!renderer_factory_) { diff --git a/chromium/media/mojo/services/video_decode_perf_history_unittest.cc b/chromium/media/mojo/services/video_decode_perf_history_unittest.cc index a475f85b295..e6eeddb2364 100644 --- a/chromium/media/mojo/services/video_decode_perf_history_unittest.cc +++ b/chromium/media/mojo/services/video_decode_perf_history_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include <map> +#include <memory> #include <string> #include "base/bind.h" @@ -302,7 +303,7 @@ class VideoDecodePerfHistoryTest : public testing::Test { past_is_efficient); // Zero it out to make verification readable. if (!old_stats) - old_stats.reset(new DecodeStatsEntry(0, 0, 0)); + old_stats = std::make_unique<DecodeStatsEntry>(0, 0, 0); EXPECT_UKM(UkmEntry::kPerf_PastVideoFramesDecodedName, old_stats->frames_decoded); EXPECT_UKM(UkmEntry::kPerf_PastVideoFramesDroppedName, diff --git a/chromium/media/mojo/services/video_decode_stats_recorder_unittest.cc b/chromium/media/mojo/services/video_decode_stats_recorder_unittest.cc index 7c63a2d65b9..8e3831b5d12 100644 --- a/chromium/media/mojo/services/video_decode_stats_recorder_unittest.cc +++ b/chromium/media/mojo/services/video_decode_stats_recorder_unittest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <memory> #include <string> #include "base/bind.h" @@ -57,10 +58,10 @@ class VideoDecodeStatsRecorderTest : public ::testing::Test { learning::FeatureValue origin, bool is_top_frame, uint64_t player_id) { - recorder_.reset(new VideoDecodeStatsRecorder( + recorder_ = std::make_unique<VideoDecodeStatsRecorder>( base::BindRepeating(&VideoDecodeStatsRecorderTest::PrintSavePerfRecord, base::Unretained(this)), - source_id, origin, is_top_frame, player_id)); + source_id, origin, is_top_frame, player_id); } void PrintSavePerfRecord(ukm::SourceId source_id, diff --git a/chromium/media/mojo/services/watch_time_recorder_unittest.cc b/chromium/media/mojo/services/watch_time_recorder_unittest.cc index 82597c9dd1d..08467f09fef 100644 --- a/chromium/media/mojo/services/watch_time_recorder_unittest.cc +++ b/chromium/media/mojo/services/watch_time_recorder_unittest.cc @@ -171,10 +171,10 @@ class WatchTimeRecorderTest : public testing::Test { } void ResetMetricRecorders() { - histogram_tester_.reset(new base::HistogramTester()); + histogram_tester_ = std::make_unique<base::HistogramTester>(); // Ensure cleared global before attempting to create a new TestUkmReporter. test_recorder_.reset(); - test_recorder_.reset(new ukm::TestAutoSetUkmRecorder()); + test_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>(); test_recorder_->UpdateSourceURL(source_id_, GURL(kTestOrigin)); } |