diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-08-24 12:15:48 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-08-28 13:30:04 +0000 |
commit | b014812705fc80bff0a5c120dfcef88f349816dc (patch) | |
tree | 25a2e2d9fa285f1add86aa333389a839f81a39ae /chromium/media/mojo/services | |
parent | 9f4560b1027ae06fdb497023cdcaf91b8511fa74 (diff) | |
download | qtwebengine-chromium-b014812705fc80bff0a5c120dfcef88f349816dc.tar.gz |
BASELINE: Update Chromium to 68.0.3440.125
Change-Id: I23f19369e01f688e496f5bf179abb521ad73874f
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/media/mojo/services')
51 files changed, 962 insertions, 710 deletions
diff --git a/chromium/media/mojo/services/BUILD.gn b/chromium/media/mojo/services/BUILD.gn index 4d87502453f..938b4c6a003 100644 --- a/chromium/media/mojo/services/BUILD.gn +++ b/chromium/media/mojo/services/BUILD.gn @@ -32,8 +32,6 @@ component("services") { "mojo_audio_decoder_service.h", "mojo_audio_input_stream.cc", "mojo_audio_input_stream.h", - "mojo_audio_input_stream_observer.cc", - "mojo_audio_input_stream_observer.h", "mojo_audio_output_stream.cc", "mojo_audio_output_stream.h", "mojo_audio_output_stream_provider.cc", @@ -103,7 +101,6 @@ component("services") { "//media/gpu/ipc/service", "//media/mojo/common", "//media/mojo/common:mojo_shared_buffer_video_frame", - "//mojo/common", "//services/metrics/public/cpp:metrics_cpp", "//services/metrics/public/cpp:ukm_builders", "//services/service_manager/public/mojom", @@ -133,7 +130,11 @@ component("services") { "mojo_cdm_proxy_service.cc", "mojo_cdm_proxy_service.h", ] - deps += [ "//media/cdm:cdm_api" ] + deps += [ + "//media/cdm:cdm_api", + "//media/cdm:cdm_paths", + "//media/cdm/library_cdm/clear_key_cdm:clear_key_cdm_proxy", + ] # TODO(xhwang): Ideally media should not worry about sandbox. Find a way to # remove this dependency. @@ -161,7 +162,6 @@ source_set("unit_tests") { sources = [ "deferred_destroy_strong_binding_set_unittest.cc", "media_metrics_provider_unittest.cc", - "mojo_audio_input_stream_observer_unittest.cc", "mojo_audio_input_stream_unittest.cc", "mojo_audio_output_stream_provider_unittest.cc", "mojo_audio_output_stream_unittest.cc", @@ -238,6 +238,7 @@ service_test("media_service_unittests") { ":services", "//base", "//media:test_support", + "//media/cdm:cdm_paths", "//media/mojo/clients", "//media/mojo/common", "//media/mojo/interfaces", diff --git a/chromium/media/mojo/services/OWNERS b/chromium/media/mojo/services/OWNERS index fce6c7aef0b..da9d4b5c016 100644 --- a/chromium/media/mojo/services/OWNERS +++ b/chromium/media/mojo/services/OWNERS @@ -15,3 +15,6 @@ per-file test_manifest.json=file://ipc/SECURITY_OWNERS per-file pipeline_apptest_manifest.json=set noparent per-file pipeline_apptest_manifest.json=file://ipc/SECURITY_OWNERS + +per-file mojo_audio_output*=file://media/audio/OWNERS +per-file mojo_audio_input*=file://media/audio/OWNERS diff --git a/chromium/media/mojo/services/cdm_manifest.json b/chromium/media/mojo/services/cdm_manifest.json index 96a06dd68f2..78ea3fccf42 100644 --- a/chromium/media/mojo/services/cdm_manifest.json +++ b/chromium/media/mojo/services/cdm_manifest.json @@ -5,7 +5,7 @@ "interface_provider_specs": { "service_manager:connector": { "provides": { - "media:cdm": [ "media::mojom::CdmService" ] + "media:cdm": [ "media.mojom.CdmService" ] }, "requires": { "*": [ "app" ] diff --git a/chromium/media/mojo/services/cdm_service.cc b/chromium/media/mojo/services/cdm_service.cc index 678476e763b..1fdc540dd75 100644 --- a/chromium/media/mojo/services/cdm_service.cc +++ b/chromium/media/mojo/services/cdm_service.cc @@ -22,44 +22,49 @@ namespace media { namespace { +using service_manager::ServiceContextRef; + constexpr base::TimeDelta kServiceContextRefReleaseDelay = base::TimeDelta::FromSeconds(5); -void DeleteServiceContextRef(service_manager::ServiceContextRef* ref) { +void DeleteServiceContextRef(ServiceContextRef* ref) { delete ref; } // Starting a new process and loading the library CDM could be expensive. This -// class helps delay the release of service_manager::ServiceContextRef by +// class helps delay the release of ServiceContextRef by // |kServiceContextRefReleaseDelay|, which will ultimately delay CdmService // destruction by the same delay as well. This helps reduce the chance of // destroying the CdmService and immediately creates it (in another process) in // cases like navigation, which could cause long service connection delays. -class DelayedReleaseServiceContextRef - : public service_manager::ServiceContextRef { +class DelayedReleaseServiceContextRef : public ServiceContextRef { public: - explicit DelayedReleaseServiceContextRef( - std::unique_ptr<service_manager::ServiceContextRef> ref) + DelayedReleaseServiceContextRef(std::unique_ptr<ServiceContextRef> ref, + base::TimeDelta delay) : ref_(std::move(ref)), - task_runner_(base::ThreadTaskRunnerHandle::Get()) {} + delay_(delay), + task_runner_(base::ThreadTaskRunnerHandle::Get()) { + DCHECK_GT(delay_, base::TimeDelta()); + } ~DelayedReleaseServiceContextRef() override { service_manager::ServiceContextRef* ref_ptr = ref_.release(); if (!task_runner_->PostNonNestableDelayedTask( FROM_HERE, base::BindOnce(&DeleteServiceContextRef, ref_ptr), - kServiceContextRefReleaseDelay)) { + delay_)) { DeleteServiceContextRef(ref_ptr); } } - // service_manager::ServiceContextRef implementation. + // ServiceContextRef implementation. std::unique_ptr<ServiceContextRef> Clone() override { NOTIMPLEMENTED(); return nullptr; } private: - std::unique_ptr<service_manager::ServiceContextRef> ref_; + std::unique_ptr<ServiceContextRef> ref_; + base::TimeDelta delay_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; DISALLOW_COPY_AND_ASSIGN(DelayedReleaseServiceContextRef); @@ -85,10 +90,9 @@ class DelayedReleaseServiceContextRef // details. class CdmFactoryImpl : public DeferredDestroy<mojom::CdmFactory> { public: - CdmFactoryImpl( - CdmService::Client* client, - service_manager::mojom::InterfaceProviderPtr interfaces, - std::unique_ptr<service_manager::ServiceContextRef> service_context_ref) + CdmFactoryImpl(CdmService::Client* client, + service_manager::mojom::InterfaceProviderPtr interfaces, + std::unique_ptr<ServiceContextRef> service_context_ref) : client_(client), interfaces_(std::move(interfaces)), service_context_ref_(std::move(service_context_ref)) { @@ -147,7 +151,7 @@ class CdmFactoryImpl : public DeferredDestroy<mojom::CdmFactory> { CdmService::Client* client_; service_manager::mojom::InterfaceProviderPtr interfaces_; mojo::StrongBindingSet<mojom::ContentDecryptionModule> cdm_bindings_; - std::unique_ptr<service_manager::ServiceContextRef> service_context_ref_; + std::unique_ptr<ServiceContextRef> service_context_ref_; std::unique_ptr<media::CdmFactory> cdm_factory_; base::OnceClosure destroy_cb_; @@ -157,7 +161,8 @@ class CdmFactoryImpl : public DeferredDestroy<mojom::CdmFactory> { } // namespace CdmService::CdmService(std::unique_ptr<Client> client) - : client_(std::move(client)) { + : client_(std::move(client)), + service_release_delay_(kServiceContextRefReleaseDelay) { DVLOG(1) << __func__; DCHECK(client_); registry_.AddInterface<mojom::CdmService>( @@ -260,10 +265,10 @@ void CdmService::CreateCdmFactory( if (!client_) return; - std::unique_ptr<service_manager::ServiceContextRef> service_context_ref = - is_delayed_service_release_enabled + std::unique_ptr<ServiceContextRef> service_context_ref = + service_release_delay_ > base::TimeDelta() ? std::make_unique<DelayedReleaseServiceContextRef>( - ref_factory_->CreateRef()) + ref_factory_->CreateRef(), service_release_delay_) : ref_factory_->CreateRef(); cdm_factory_bindings_.AddBinding( diff --git a/chromium/media/mojo/services/cdm_service.h b/chromium/media/mojo/services/cdm_service.h index 22f418c4bfe..67771567730 100644 --- a/chromium/media/mojo/services/cdm_service.h +++ b/chromium/media/mojo/services/cdm_service.h @@ -53,8 +53,10 @@ class MEDIA_MOJO_EXPORT CdmService : public service_manager::Service, explicit CdmService(std::unique_ptr<Client> client); ~CdmService() final; - void DisableDelayedServiceReleaseForTesting() { - is_delayed_service_release_enabled = false; + // By default CdmService release is delayed. Overrides the delay with |delay|. + // If |delay| is 0, delayed service release will be disabled. + void SetServiceReleaseDelayForTesting(base::TimeDelta delay) { + service_release_delay_ = delay; } size_t BoundCdmFactorySizeForTesting() const { @@ -92,7 +94,7 @@ class MEDIA_MOJO_EXPORT CdmService : public service_manager::Service, DeferredDestroyStrongBindingSet<mojom::CdmFactory> cdm_factory_bindings_; service_manager::BinderRegistry registry_; mojo::BindingSet<mojom::CdmService> bindings_; - bool is_delayed_service_release_enabled = true; + base::TimeDelta service_release_delay_; }; } // namespace media diff --git a/chromium/media/mojo/services/cdm_service_unittest.cc b/chromium/media/mojo/services/cdm_service_unittest.cc index b0d39879bc4..90a9e99f5d2 100644 --- a/chromium/media/mojo/services/cdm_service_unittest.cc +++ b/chromium/media/mojo/services/cdm_service_unittest.cc @@ -25,13 +25,18 @@ #include "url/gurl.h" #include "url/origin.h" -using testing::Invoke; -using testing::InvokeWithoutArgs; - namespace media { namespace { +using testing::_; +using testing::Invoke; +using testing::InvokeWithoutArgs; + +MATCHER_P(MatchesResult, success, "") { + return arg->success == success; +} + const char kClearKeyKeySystem[] = "org.w3.clearkey"; const char kInvalidKeySystem[] = "invalid.key.system"; const char kSecurityOrigin[] = "https://foo.com"; @@ -87,12 +92,16 @@ class ServiceTestClient : public service_manager::test::ServiceTestClient, std::make_unique<CdmService>(std::move(mock_cdm_service_client)); cdm_service_ = cdm_service.get(); - // Delayed service release involves a posted delayed task which will not - // block *.RunUntilIdle() and hence cause a memory leak in the test. - cdm_service_->DisableDelayedServiceReleaseForTesting(); + cdm_service_->SetServiceReleaseDelayForTesting(service_release_delay_); service_context_ = std::make_unique<service_manager::ServiceContext>( std::move(cdm_service), std::move(request)); + service_context_->SetQuitClosure(base::BindRepeating( + &ServiceTestClient::DestroyService, base::Unretained(this))); + } + + void SetServiceReleaseDelay(base::TimeDelta delay) { + service_release_delay_ = delay; } void DestroyService() { service_context_.reset(); } @@ -108,6 +117,11 @@ class ServiceTestClient : public service_manager::test::ServiceTestClient, service_factory_bindings_.AddBinding(this, std::move(request)); } + // Delayed service release involves a posted delayed task which will not + // block *.RunUntilIdle() and hence cause a memory leak in the test. So by + // default use a zero value delay to disable the delay. + base::TimeDelta service_release_delay_; + service_manager::BinderRegistry registry_; mojo::BindingSet<service_manager::mojom::ServiceFactory> service_factory_bindings_; @@ -116,22 +130,20 @@ class ServiceTestClient : public service_manager::test::ServiceTestClient, MockCdmServiceClient* mock_cdm_service_client_ = nullptr; }; -} // namespace - class CdmServiceTest : public service_manager::test::ServiceTest { public: CdmServiceTest() : ServiceTest("cdm_service_unittest") {} ~CdmServiceTest() override {} + MOCK_METHOD0(CdmServiceConnectionClosed, void()); MOCK_METHOD0(CdmFactoryConnectionClosed, void()); MOCK_METHOD0(CdmConnectionClosed, void()); - // service_manager::test::ServiceTest: - void SetUp() override { - ServiceTest::SetUp(); - + void Initialize() { connector()->BindInterface(media::mojom::kCdmServiceName, &cdm_service_ptr_); + cdm_service_ptr_.set_connection_error_handler(base::BindRepeating( + &CdmServiceTest::CdmServiceConnectionClosed, base::Unretained(this))); service_manager::mojom::InterfaceProviderPtr interfaces; auto provider = std::make_unique<MediaInterfaceProvider>( @@ -146,21 +158,22 @@ class CdmServiceTest : public service_manager::test::ServiceTest { &CdmServiceTest::CdmFactoryConnectionClosed, base::Unretained(this))); } - // MOCK_METHOD* doesn't support move-only types. Work around this by having - // an extra method. - MOCK_METHOD1(OnCdmInitializedInternal, void(bool result)); - void OnCdmInitialized(mojom::CdmPromiseResultPtr result, - int cdm_id, - mojom::DecryptorPtr decryptor) { - OnCdmInitializedInternal(result->success); + void InitializeWithServiceReleaseDelay(base::TimeDelta delay) { + service_test_client_->SetServiceReleaseDelay(delay); + Initialize(); } + MOCK_METHOD3(OnCdmInitialized, + void(mojom::CdmPromiseResultPtr result, + int cdm_id, + mojom::DecryptorPtr decryptor)); + void InitializeCdm(const std::string& key_system, bool expected_result) { base::RunLoop run_loop; cdm_factory_ptr_->CreateCdm(key_system, mojo::MakeRequest(&cdm_ptr_)); cdm_ptr_.set_connection_error_handler(base::BindRepeating( &CdmServiceTest::CdmConnectionClosed, base::Unretained(this))); - EXPECT_CALL(*this, OnCdmInitializedInternal(expected_result)) + EXPECT_CALL(*this, OnCdmInitialized(MatchesResult(expected_result), _, _)) .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); cdm_ptr_->Initialize(key_system, url::Origin::Create(GURL(kSecurityOrigin)), CdmConfig(), @@ -169,6 +182,7 @@ class CdmServiceTest : public service_manager::test::ServiceTest { run_loop.Run(); } + // service_manager::test::ServiceTest implementation. std::unique_ptr<service_manager::Service> CreateService() override { auto service_test_client = std::make_unique<ServiceTestClient>(this); service_test_client_ = service_test_client.get(); @@ -184,14 +198,17 @@ class CdmServiceTest : public service_manager::test::ServiceTest { DISALLOW_COPY_AND_ASSIGN(CdmServiceTest); }; +} // namespace + TEST_F(CdmServiceTest, LoadCdm) { - base::FilePath cdm_path(FILE_PATH_LITERAL("dummy path")); + Initialize(); // Even with a dummy path where the CDM cannot be loaded, EnsureSandboxed() // should still be called to ensure the process is sandboxed. EXPECT_CALL(*service_test_client_->mock_cdm_service_client(), EnsureSandboxed()); + base::FilePath cdm_path(FILE_PATH_LITERAL("dummy path")); #if defined(OS_MACOSX) // Token provider will not be used since the path is a dummy path. cdm_service_ptr_->LoadCdm(cdm_path, nullptr); @@ -203,22 +220,26 @@ TEST_F(CdmServiceTest, LoadCdm) { } TEST_F(CdmServiceTest, InitializeCdm_Success) { + Initialize(); InitializeCdm(kClearKeyKeySystem, true); } TEST_F(CdmServiceTest, InitializeCdm_InvalidKeySystem) { + Initialize(); InitializeCdm(kInvalidKeySystem, false); } TEST_F(CdmServiceTest, DestroyAndRecreateCdm) { + Initialize(); InitializeCdm(kClearKeyKeySystem, true); cdm_ptr_.reset(); InitializeCdm(kClearKeyKeySystem, true); } // CdmFactory connection error will NOT destroy CDMs. Instead, it will only be -// destroyed after |cdm_| is reset. +// destroyed after |cdm_ptr_| is reset. TEST_F(CdmServiceTest, DestroyCdmFactory) { + Initialize(); auto* service = service_test_client_->cdm_service(); InitializeCdm(kClearKeyKeySystem, true); @@ -236,13 +257,39 @@ TEST_F(CdmServiceTest, DestroyCdmFactory) { EXPECT_EQ(service->UnboundCdmFactorySizeForTesting(), 0u); } +// Same as DestroyCdmFactory test, but do not disable delayed service release. +// TODO(xhwang): Use ScopedTaskEnvironment::MainThreadType::MOCK_TIME and +// ScopedTaskEnvironment::FastForwardBy() so we don't have to really wait for +// the delay in the test. But currently FastForwardBy() doesn't support delayed +// task yet. +TEST_F(CdmServiceTest, DestroyCdmFactory_DelayedServiceRelease) { + constexpr base::TimeDelta kServiceContextRefReleaseDelay = + base::TimeDelta::FromSeconds(1); + InitializeWithServiceReleaseDelay(kServiceContextRefReleaseDelay); + + InitializeCdm(kClearKeyKeySystem, true); + cdm_factory_ptr_.reset(); + base::RunLoop().RunUntilIdle(); + + base::RunLoop run_loop; + auto start_time = base::Time::Now(); + cdm_ptr_.reset(); + EXPECT_CALL(*this, CdmServiceConnectionClosed()) + .WillOnce(Invoke(&run_loop, &base::RunLoop::Quit)); + run_loop.Run(); + auto time_passed = base::Time::Now() - start_time; + EXPECT_GE(time_passed, kServiceContextRefReleaseDelay); +} + // Destroy service will destroy the CdmFactory and all CDMs. TEST_F(CdmServiceTest, DestroyCdmService) { + Initialize(); InitializeCdm(kClearKeyKeySystem, true); base::RunLoop run_loop; // Ideally we should not care about order, and should only quit the loop when // both connections are closed. + EXPECT_CALL(*this, CdmServiceConnectionClosed()); EXPECT_CALL(*this, CdmFactoryConnectionClosed()); EXPECT_CALL(*this, CdmConnectionClosed()) .WillOnce(Invoke(&run_loop, &base::RunLoop::Quit)); diff --git a/chromium/media/mojo/services/cdm_service_unittest_manifest.json b/chromium/media/mojo/services/cdm_service_unittest_manifest.json index 1d35efbb1a7..9440910d412 100644 --- a/chromium/media/mojo/services/cdm_service_unittest_manifest.json +++ b/chromium/media/mojo/services/cdm_service_unittest_manifest.json @@ -5,7 +5,7 @@ "service_manager:connector": { "provides": { "service_manager:service_factory": [ - "service_manager::mojom::ServiceFactory" + "service_manager.mojom.ServiceFactory" ] }, "requires": { diff --git a/chromium/media/mojo/services/gpu_mojo_media_client.cc b/chromium/media/mojo/services/gpu_mojo_media_client.cc index fb112f24420..dd6d7ba0912 100644 --- a/chromium/media/mojo/services/gpu_mojo_media_client.cc +++ b/chromium/media/mojo/services/gpu_mojo_media_client.cc @@ -7,13 +7,16 @@ #include <utility> #include "base/bind.h" +#include "base/feature_list.h" #include "build/build_config.h" #include "gpu/ipc/service/gpu_channel.h" #include "media/base/audio_decoder.h" #include "media/base/cdm_factory.h" +#include "media/base/media_switches.h" #include "media/base/video_decoder.h" #include "media/gpu/buildflags.h" #include "media/gpu/ipc/service/media_gpu_channel_manager.h" +#include "media/gpu/ipc/service/vda_video_decoder.h" #if defined(OS_ANDROID) #include "base/memory/ptr_util.h" @@ -61,7 +64,7 @@ std::unique_ptr<MediaDrmStorage> CreateMediaDrmStorage( } #endif // defined(OS_ANDROID) -#if defined(OS_ANDROID) || defined(OS_WIN) +#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN) gpu::CommandBufferStub* GetCommandBufferStub( base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager, base::UnguessableToken channel_token, @@ -82,11 +85,13 @@ gpu::CommandBufferStub* GetCommandBufferStub( GpuMojoMediaClient::GpuMojoMediaClient( const gpu::GpuPreferences& gpu_preferences, + const gpu::GpuDriverBugWorkarounds& gpu_workarounds, scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager, AndroidOverlayMojoFactoryCB android_overlay_factory_cb, CdmProxyFactoryCB cdm_proxy_factory_cb) : gpu_preferences_(gpu_preferences), + gpu_workarounds_(gpu_workarounds), gpu_task_runner_(std::move(gpu_task_runner)), media_gpu_channel_manager_(std::move(media_gpu_channel_manager)), android_overlay_factory_cb_(std::move(android_overlay_factory_cb)), @@ -109,7 +114,8 @@ std::unique_ptr<VideoDecoder> GpuMojoMediaClient::CreateVideoDecoder( scoped_refptr<base::SingleThreadTaskRunner> task_runner, MediaLog* media_log, mojom::CommandBufferIdPtr command_buffer_id, - RequestOverlayInfoCB request_overlay_info_cb) { + RequestOverlayInfoCB request_overlay_info_cb, + const gfx::ColorSpace& target_color_space) { // Both MCVD and D3D11 VideoDecoders need a command buffer. if (!command_buffer_id) return nullptr; @@ -126,9 +132,19 @@ std::unique_ptr<VideoDecoder> GpuMojoMediaClient::CreateVideoDecoder( android_overlay_factory_cb_, std::move(request_overlay_info_cb), std::make_unique<VideoFrameFactoryImpl>(gpu_task_runner_, std::move(get_stub_cb))); -#elif defined(OS_WIN) - return std::make_unique<D3D11VideoDecoder>( - gpu_task_runner_, +#elif defined(OS_MACOSX) || defined(OS_WIN) +#if defined(OS_WIN) + if (base::FeatureList::IsEnabled(kD3D11VideoDecoder)) { + return D3D11VideoDecoder::Create( + gpu_task_runner_, gpu_preferences_, gpu_workarounds_, + base::BindRepeating(&GetCommandBufferStub, media_gpu_channel_manager_, + command_buffer_id->channel_token, + command_buffer_id->route_id)); + } +#endif // defined(OS_WIN) + return VdaVideoDecoder::Create( + task_runner, gpu_task_runner_, media_log, target_color_space, + gpu_preferences_, gpu_workarounds_, base::BindRepeating(&GetCommandBufferStub, media_gpu_channel_manager_, command_buffer_id->channel_token, command_buffer_id->route_id)); diff --git a/chromium/media/mojo/services/gpu_mojo_media_client.h b/chromium/media/mojo/services/gpu_mojo_media_client.h index eda075db512..37acd5ab568 100644 --- a/chromium/media/mojo/services/gpu_mojo_media_client.h +++ b/chromium/media/mojo/services/gpu_mojo_media_client.h @@ -12,6 +12,7 @@ #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" #include "gpu/command_buffer/service/gpu_preferences.h" +#include "gpu/config/gpu_driver_bug_workarounds.h" #include "media/base/android_overlay_mojo_factory.h" #include "media/cdm/cdm_proxy.h" #include "media/mojo/services/mojo_media_client.h" @@ -28,6 +29,7 @@ class GpuMojoMediaClient : public MojoMediaClient { // CdmProxy is not supported on the platform. GpuMojoMediaClient( const gpu::GpuPreferences& gpu_preferences, + const gpu::GpuDriverBugWorkarounds& gpu_workarounds, scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager, AndroidOverlayMojoFactoryCB android_overlay_factory_cb, @@ -42,7 +44,8 @@ class GpuMojoMediaClient : public MojoMediaClient { scoped_refptr<base::SingleThreadTaskRunner> task_runner, MediaLog* media_log, mojom::CommandBufferIdPtr command_buffer_id, - RequestOverlayInfoCB request_overlay_info_cb) final; + RequestOverlayInfoCB request_overlay_info_cb, + const gfx::ColorSpace& target_color_space) final; std::unique_ptr<CdmFactory> CreateCdmFactory( service_manager::mojom::InterfaceProvider* interface_provider) final; #if BUILDFLAG(ENABLE_LIBRARY_CDMS) @@ -51,6 +54,7 @@ class GpuMojoMediaClient : public MojoMediaClient { private: gpu::GpuPreferences gpu_preferences_; + gpu::GpuDriverBugWorkarounds gpu_workarounds_; scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_; base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager_; AndroidOverlayMojoFactoryCB android_overlay_factory_cb_; diff --git a/chromium/media/mojo/services/interface_factory_impl.cc b/chromium/media/mojo/services/interface_factory_impl.cc index 760568207a6..14df0447a7d 100644 --- a/chromium/media/mojo/services/interface_factory_impl.cc +++ b/chromium/media/mojo/services/interface_factory_impl.cc @@ -11,6 +11,7 @@ #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "media/base/media_log.h" +#include "media/mojo/services/mojo_decryptor_service.h" #include "media/mojo/services/mojo_media_client.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "services/service_manager/public/mojom/interface_provider.mojom.h" @@ -25,9 +26,7 @@ #if BUILDFLAG(ENABLE_MOJO_RENDERER) #include "base/bind_helpers.h" -#include "media/base/audio_renderer_sink.h" -#include "media/base/renderer_factory.h" -#include "media/base/video_renderer_sink.h" +#include "media/base/renderer.h" #include "media/mojo/services/mojo_renderer_service.h" #endif // BUILDFLAG(ENABLE_MOJO_RENDERER) @@ -58,6 +57,8 @@ InterfaceFactoryImpl::InterfaceFactoryImpl( mojo_media_client_(mojo_media_client) { DVLOG(1) << __func__; DCHECK(mojo_media_client_); + + SetBindingConnectionErrorHandler(); } InterfaceFactoryImpl::~InterfaceFactoryImpl() { @@ -68,6 +69,7 @@ InterfaceFactoryImpl::~InterfaceFactoryImpl() { void InterfaceFactoryImpl::CreateAudioDecoder( mojo::InterfaceRequest<mojom::AudioDecoder> request) { + DVLOG(2) << __func__; #if BUILDFLAG(ENABLE_MOJO_AUDIO_DECODER) scoped_refptr<base::SingleThreadTaskRunner> task_runner( base::ThreadTaskRunnerHandle::Get()); @@ -88,6 +90,7 @@ void InterfaceFactoryImpl::CreateAudioDecoder( void InterfaceFactoryImpl::CreateVideoDecoder( mojom::VideoDecoderRequest request) { + DVLOG(2) << __func__; #if BUILDFLAG(ENABLE_MOJO_VIDEO_DECODER) video_decoder_bindings_.AddBinding( std::make_unique<MojoVideoDecoderService>(mojo_media_client_, @@ -100,11 +103,8 @@ void InterfaceFactoryImpl::CreateRenderer( media::mojom::HostedRendererType type, const std::string& type_specific_id, mojo::InterfaceRequest<mojom::Renderer> request) { + DVLOG(2) << __func__; #if BUILDFLAG(ENABLE_MOJO_RENDERER) - RendererFactory* renderer_factory = GetRendererFactory(); - if (!renderer_factory) - return; - // Creation requests for non default renderers should have already been // handled by now, in a different layer. if (type != media::mojom::HostedRendererType::kDefault) { @@ -112,17 +112,11 @@ void InterfaceFactoryImpl::CreateRenderer( return; } - scoped_refptr<base::SingleThreadTaskRunner> task_runner( - base::ThreadTaskRunnerHandle::Get()); - auto audio_sink = - mojo_media_client_->CreateAudioRendererSink(type_specific_id); - - auto video_sink = mojo_media_client_->CreateVideoRendererSink(task_runner); - // TODO(hubbe): Find out if gfx::ColorSpace() is correct for the - // target_color_space. - auto renderer = renderer_factory->CreateRenderer( - task_runner, task_runner, audio_sink.get(), video_sink.get(), - RequestOverlayInfoCB(), gfx::ColorSpace()); + // For HostedRendererType::kDefault type, |type_specific_id| represents an + // audio device ID. See interface_factory.mojom. + const std::string& audio_device_id = type_specific_id; + auto renderer = mojo_media_client_->CreateRenderer( + base::ThreadTaskRunnerHandle::Get(), media_log_, audio_device_id); if (!renderer) { DLOG(ERROR) << "Renderer creation failed."; return; @@ -130,8 +124,8 @@ void InterfaceFactoryImpl::CreateRenderer( std::unique_ptr<MojoRendererService> mojo_renderer_service = std::make_unique<MojoRendererService>( - &cdm_service_context_, std::move(audio_sink), std::move(video_sink), - std::move(renderer), MojoRendererService::InitiateSurfaceRequestCB()); + &cdm_service_context_, std::move(renderer), + MojoRendererService::InitiateSurfaceRequestCB()); MojoRendererService* mojo_renderer_service_ptr = mojo_renderer_service.get(); @@ -150,6 +144,7 @@ void InterfaceFactoryImpl::CreateRenderer( void InterfaceFactoryImpl::CreateCdm( const std::string& /* key_system */, mojo::InterfaceRequest<mojom::ContentDecryptionModule> request) { + DVLOG(2) << __func__; #if BUILDFLAG(ENABLE_MOJO_CDM) CdmFactory* cdm_factory = GetCdmFactory(); if (!cdm_factory) @@ -161,8 +156,23 @@ void InterfaceFactoryImpl::CreateCdm( #endif // BUILDFLAG(ENABLE_MOJO_CDM) } +void InterfaceFactoryImpl::CreateDecryptor(int cdm_id, + mojom::DecryptorRequest request) { + DVLOG(2) << __func__; + auto mojo_decryptor_service = + MojoDecryptorService::Create(cdm_id, &cdm_service_context_); + if (!mojo_decryptor_service) { + DLOG(ERROR) << "MojoDecryptorService creation failed."; + return; + } + + decryptor_bindings_.AddBinding(std::move(mojo_decryptor_service), + std::move(request)); +} + void InterfaceFactoryImpl::CreateCdmProxy(const std::string& cdm_guid, mojom::CdmProxyRequest request) { + DVLOG(2) << __func__; #if BUILDFLAG(ENABLE_LIBRARY_CDMS) if (!base::IsValidGUID(cdm_guid)) { DLOG(ERROR) << "Invalid CDM GUID: " << cdm_guid; @@ -182,17 +192,83 @@ void InterfaceFactoryImpl::CreateCdmProxy(const std::string& cdm_guid, #endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) } +void InterfaceFactoryImpl::OnDestroyPending(base::OnceClosure destroy_cb) { + DVLOG(1) << __func__; + destroy_cb_ = std::move(destroy_cb); + if (IsEmpty()) + std::move(destroy_cb_).Run(); + // else the callback will be called when IsEmpty() becomes true. +} + +bool InterfaceFactoryImpl::IsEmpty() { +#if BUILDFLAG(ENABLE_MOJO_AUDIO_DECODER) + if (!audio_decoder_bindings_.empty()) + return false; +#endif // BUILDFLAG(ENABLE_MOJO_AUDIO_DECODER) + +#if BUILDFLAG(ENABLE_MOJO_VIDEO_DECODER) + if (!video_decoder_bindings_.empty()) + return false; +#endif // BUILDFLAG(ENABLE_MOJO_VIDEO_DECODER) + #if BUILDFLAG(ENABLE_MOJO_RENDERER) -RendererFactory* InterfaceFactoryImpl::GetRendererFactory() { - if (!renderer_factory_) { - renderer_factory_ = mojo_media_client_->CreateRendererFactory(media_log_); - LOG_IF(ERROR, !renderer_factory_) << "RendererFactory not available."; - } - return renderer_factory_.get(); + if (!renderer_bindings_.empty()) + return false; +#endif // BUILDFLAG(ENABLE_MOJO_RENDERER) + +#if BUILDFLAG(ENABLE_MOJO_CDM) + if (!cdm_bindings_.empty()) + return false; +#endif // BUILDFLAG(ENABLE_MOJO_CDM) + +#if BUILDFLAG(ENABLE_LIBRARY_CDMS) + if (!cdm_proxy_bindings_.empty()) + return false; +#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) + + if (!decryptor_bindings_.empty()) + return false; + + return true; } + +void InterfaceFactoryImpl::SetBindingConnectionErrorHandler() { + // base::Unretained is safe because all bindings are owned by |this|. If + // |this| is destructed, the bindings will be destructed as well and the + // connection error handler should never be called. + auto connection_error_cb = base::BindRepeating( + &InterfaceFactoryImpl::OnBindingConnectionError, base::Unretained(this)); + +#if BUILDFLAG(ENABLE_MOJO_AUDIO_DECODER) + audio_decoder_bindings_.set_connection_error_handler(connection_error_cb); +#endif // BUILDFLAG(ENABLE_MOJO_AUDIO_DECODER) + +#if BUILDFLAG(ENABLE_MOJO_VIDEO_DECODER) + video_decoder_bindings_.set_connection_error_handler(connection_error_cb); +#endif // BUILDFLAG(ENABLE_MOJO_VIDEO_DECODER) + +#if BUILDFLAG(ENABLE_MOJO_RENDERER) + renderer_bindings_.set_connection_error_handler(connection_error_cb); #endif // BUILDFLAG(ENABLE_MOJO_RENDERER) #if BUILDFLAG(ENABLE_MOJO_CDM) + cdm_bindings_.set_connection_error_handler(connection_error_cb); +#endif // BUILDFLAG(ENABLE_MOJO_CDM) + +#if BUILDFLAG(ENABLE_LIBRARY_CDMS) + cdm_proxy_bindings_.set_connection_error_handler(connection_error_cb); +#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) + + decryptor_bindings_.set_connection_error_handler(connection_error_cb); +} + +void InterfaceFactoryImpl::OnBindingConnectionError() { + DVLOG(2) << __func__; + if (destroy_cb_ && IsEmpty()) + std::move(destroy_cb_).Run(); +} + +#if BUILDFLAG(ENABLE_MOJO_CDM) CdmFactory* InterfaceFactoryImpl::GetCdmFactory() { if (!cdm_factory_) { cdm_factory_ = mojo_media_client_->CreateCdmFactory(interfaces_.get()); diff --git a/chromium/media/mojo/services/interface_factory_impl.h b/chromium/media/mojo/services/interface_factory_impl.h index 7797ca1b415..486fdaea204 100644 --- a/chromium/media/mojo/services/interface_factory_impl.h +++ b/chromium/media/mojo/services/interface_factory_impl.h @@ -10,6 +10,7 @@ #include "base/macros.h" #include "media/mojo/buildflags.h" #include "media/mojo/interfaces/interface_factory.mojom.h" +#include "media/mojo/services/deferred_destroy_strong_binding_set.h" #include "media/mojo/services/mojo_cdm_service_context.h" #include "mojo/public/cpp/bindings/strong_binding_set.h" #include "services/service_manager/public/cpp/connector.h" @@ -20,9 +21,8 @@ namespace media { class CdmFactory; class MediaLog; class MojoMediaClient; -class RendererFactory; -class InterfaceFactoryImpl : public mojom::InterfaceFactory { +class InterfaceFactoryImpl : public DeferredDestroy<mojom::InterfaceFactory> { public: InterfaceFactoryImpl( service_manager::mojom::InterfaceProviderPtr interfaces, @@ -39,13 +39,20 @@ class InterfaceFactoryImpl : public mojom::InterfaceFactory { mojom::RendererRequest request) final; void CreateCdm(const std::string& key_system, mojom::ContentDecryptionModuleRequest request) final; + void CreateDecryptor(int cdm_id, mojom::DecryptorRequest request) final; void CreateCdmProxy(const std::string& cdm_guid, mojom::CdmProxyRequest request) final; + // DeferredDestroy<mojom::InterfaceFactory> implemenation. + void OnDestroyPending(base::OnceClosure destroy_cb) final; + private: -#if BUILDFLAG(ENABLE_MOJO_RENDERER) - RendererFactory* GetRendererFactory(); -#endif // BUILDFLAG(ENABLE_MOJO_RENDERER) + // Returns true when there is no media component (audio/video decoder, + // renderer, cdm and cdm proxy) bindings exist. + bool IsEmpty(); + + void SetBindingConnectionErrorHandler(); + void OnBindingConnectionError(); #if BUILDFLAG(ENABLE_MOJO_CDM) CdmFactory* GetCdmFactory(); @@ -66,7 +73,6 @@ class InterfaceFactoryImpl : public mojom::InterfaceFactory { #if BUILDFLAG(ENABLE_MOJO_RENDERER) MediaLog* media_log_; - std::unique_ptr<RendererFactory> renderer_factory_; mojo::StrongBindingSet<mojom::Renderer> renderer_bindings_; #endif // BUILDFLAG(ENABLE_MOJO_RENDERER) @@ -80,8 +86,11 @@ class InterfaceFactoryImpl : public mojom::InterfaceFactory { mojo::StrongBindingSet<mojom::CdmProxy> cdm_proxy_bindings_; #endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) + mojo::StrongBindingSet<mojom::Decryptor> decryptor_bindings_; + std::unique_ptr<service_manager::ServiceContextRef> connection_ref_; MojoMediaClient* mojo_media_client_; + base::OnceClosure destroy_cb_; DISALLOW_COPY_AND_ASSIGN(InterfaceFactoryImpl); }; diff --git a/chromium/media/mojo/services/media_manifest.json b/chromium/media/mojo/services/media_manifest.json index 64c6194d6bf..ca7971ecb25 100644 --- a/chromium/media/mojo/services/media_manifest.json +++ b/chromium/media/mojo/services/media_manifest.json @@ -4,7 +4,7 @@ "interface_provider_specs": { "service_manager:connector": { "provides": { - "media:media": [ "media::mojom::MediaService" ] + "media:media": [ "media.mojom.MediaService" ] }, "requires": { "*": [ "app" ] diff --git a/chromium/media/mojo/services/media_resource_shim.cc b/chromium/media/mojo/services/media_resource_shim.cc index ace3f01b4ab..184f2a35022 100644 --- a/chromium/media/mojo/services/media_resource_shim.cc +++ b/chromium/media/mojo/services/media_resource_shim.cc @@ -39,10 +39,6 @@ std::vector<DemuxerStream*> MediaResourceShim::GetAllStreams() { return result; } -void MediaResourceShim::SetStreamStatusChangeCB( - const StreamStatusChangeCB& cb) { -} - void MediaResourceShim::OnStreamReady() { if (++streams_ready_ == streams_.size()) base::ResetAndReturn(&demuxer_ready_cb_).Run(); diff --git a/chromium/media/mojo/services/media_resource_shim.h b/chromium/media/mojo/services/media_resource_shim.h index fe54ef7c648..fab9eecd5d5 100644 --- a/chromium/media/mojo/services/media_resource_shim.h +++ b/chromium/media/mojo/services/media_resource_shim.h @@ -27,7 +27,6 @@ class MediaResourceShim : public MediaResource { // MediaResource interface. std::vector<DemuxerStream*> GetAllStreams() override; - void SetStreamStatusChangeCB(const StreamStatusChangeCB& cb) override; private: // Called as each mojom::DemuxerStream becomes ready. Once all streams diff --git a/chromium/media/mojo/services/media_service.h b/chromium/media/mojo/services/media_service.h index 5219eb2a56e..0e4dc375009 100644 --- a/chromium/media/mojo/services/media_service.h +++ b/chromium/media/mojo/services/media_service.h @@ -11,9 +11,9 @@ #include "media/base/media_log.h" #include "media/mojo/interfaces/interface_factory.mojom.h" #include "media/mojo/interfaces/media_service.mojom.h" +#include "media/mojo/services/deferred_destroy_strong_binding_set.h" #include "media/mojo/services/media_mojo_export.h" #include "mojo/public/cpp/bindings/binding_set.h" -#include "mojo/public/cpp/bindings/strong_binding_set.h" #include "services/service_manager/public/cpp/binder_registry.h" #include "services/service_manager/public/cpp/service.h" #include "services/service_manager/public/cpp/service_context.h" @@ -56,7 +56,8 @@ class MEDIA_MOJO_EXPORT MediaService : public service_manager::Service, // Note: Since |&media_log_| is passed to bindings, the bindings must be // destructed first. - mojo::StrongBindingSet<mojom::InterfaceFactory> interface_factory_bindings_; + DeferredDestroyStrongBindingSet<mojom::InterfaceFactory> + interface_factory_bindings_; service_manager::BinderRegistry registry_; mojo::BindingSet<mojom::MediaService> bindings_; diff --git a/chromium/media/mojo/services/media_service_factory.cc b/chromium/media/mojo/services/media_service_factory.cc index be65dd05d12..b789dfd4819 100644 --- a/chromium/media/mojo/services/media_service_factory.cc +++ b/chromium/media/mojo/services/media_service_factory.cc @@ -31,14 +31,15 @@ std::unique_ptr<service_manager::Service> CreateMediaService() { std::unique_ptr<service_manager::Service> CreateGpuMediaService( const gpu::GpuPreferences& gpu_preferences, + const gpu::GpuDriverBugWorkarounds& gpu_workarounds, scoped_refptr<base::SingleThreadTaskRunner> task_runner, base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager, AndroidOverlayMojoFactoryCB android_overlay_factory_cb, CdmProxyFactoryCB cdm_proxy_factory_cb) { return std::unique_ptr<service_manager::Service>( new MediaService(std::make_unique<GpuMojoMediaClient>( - gpu_preferences, task_runner, media_gpu_channel_manager, - std::move(android_overlay_factory_cb), + gpu_preferences, gpu_workarounds, task_runner, + media_gpu_channel_manager, std::move(android_overlay_factory_cb), std::move(cdm_proxy_factory_cb)))); } diff --git a/chromium/media/mojo/services/media_service_factory.h b/chromium/media/mojo/services/media_service_factory.h index 2c0e7ad644b..8dd062587e7 100644 --- a/chromium/media/mojo/services/media_service_factory.h +++ b/chromium/media/mojo/services/media_service_factory.h @@ -11,6 +11,7 @@ #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" #include "gpu/command_buffer/service/gpu_preferences.h" +#include "gpu/config/gpu_driver_bug_workarounds.h" #include "media/base/android_overlay_mojo_factory.h" #include "media/cdm/cdm_proxy.h" #include "media/mojo/services/media_mojo_export.h" @@ -34,6 +35,7 @@ CreateMediaService(); std::unique_ptr<service_manager::Service> MEDIA_MOJO_EXPORT CreateGpuMediaService( const gpu::GpuPreferences& gpu_preferences, + const gpu::GpuDriverBugWorkarounds& gpu_workarounds, scoped_refptr<base::SingleThreadTaskRunner> task_runner, base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager, AndroidOverlayMojoFactoryCB android_overlay_factory_cb, diff --git a/chromium/media/mojo/services/media_service_unittest.cc b/chromium/media/mojo/services/media_service_unittest.cc index 3685165b8c0..4897ac8790c 100644 --- a/chromium/media/mojo/services/media_service_unittest.cc +++ b/chromium/media/mojo/services/media_service_unittest.cc @@ -11,11 +11,13 @@ #include "base/callback.h" #include "base/macros.h" #include "base/run_loop.h" +#include "base/task_scheduler/post_task.h" #include "build/build_config.h" #include "media/base/cdm_config.h" #include "media/base/mock_filters.h" #include "media/base/test_helpers.h" #include "media/mojo/buildflags.h" +#include "media/mojo/clients/mojo_decryptor.h" #include "media/mojo/clients/mojo_demuxer_stream_impl.h" #include "media/mojo/common/media_type_converters.h" #include "media/mojo/interfaces/constants.mojom.h" @@ -32,20 +34,54 @@ #include "url/gurl.h" #include "url/origin.h" +#if BUILDFLAG(ENABLE_LIBRARY_CDMS) +#include "media/cdm/cdm_paths.h" // nogncheck +#include "media/mojo/interfaces/cdm_proxy.mojom.h" +#endif + +namespace media { + +namespace { + +using testing::_; +using testing::DoAll; using testing::Invoke; using testing::InvokeWithoutArgs; using testing::NiceMock; +using testing::SaveArg; using testing::StrictMock; +using testing::WithArg; -namespace media { -namespace { +MATCHER_P(MatchesResult, success, "") { + return arg->success == success; +} #if BUILDFLAG(ENABLE_MOJO_CDM) && !defined(OS_ANDROID) const char kClearKeyKeySystem[] = "org.w3.clearkey"; const char kInvalidKeySystem[] = "invalid.key.system"; #endif -const char kSecurityOrigin[] = "http://foo.com"; +const char kSecurityOrigin[] = "https://foo.com"; + +// Returns a trivial encrypted DecoderBuffer. +scoped_refptr<DecoderBuffer> CreateEncryptedBuffer() { + scoped_refptr<DecoderBuffer> encrypted_buffer(new DecoderBuffer(100)); + encrypted_buffer->set_decrypt_config( + DecryptConfig::CreateCencConfig("dummy_key_id", "0123456789ABCDEF", {})); + return encrypted_buffer; +} + +class MockCdmProxyClient : public mojom::CdmProxyClient { + public: + MockCdmProxyClient() = default; + ~MockCdmProxyClient() override = default; + + // mojom::CdmProxyClient implementation. + MOCK_METHOD0(NotifyHardwareReset, void()); + + private: + DISALLOW_COPY_AND_ASSIGN(MockCdmProxyClient); +}; class MockRendererClient : public mojom::RendererClient { public: @@ -73,10 +109,20 @@ class MockRendererClient : public mojom::RendererClient { DISALLOW_COPY_AND_ASSIGN(MockRendererClient); }; +ACTION_P(QuitLoop, run_loop) { + base::PostTask(FROM_HERE, run_loop->QuitClosure()); +} + +// Tests MediaService built into a standalone mojo service binary (see +// ServiceMain() in main.cc) where MediaService uses TestMojoMediaClient. +// TestMojoMediaClient supports CDM creation using DefaultCdmFactory (only +// supports Clear Key key system), and Renderer creation using +// DefaultRendererFactory that always create media::RendererImpl. class MediaServiceTest : public service_manager::test::ServiceTest { public: MediaServiceTest() : ServiceTest("media_service_unittests"), + cdm_proxy_client_binding_(&cdm_proxy_client_), renderer_client_binding_(&renderer_client_), video_stream_(DemuxerStream::VIDEO) {} ~MediaServiceTest() override = default; @@ -84,44 +130,100 @@ class MediaServiceTest : public service_manager::test::ServiceTest { void SetUp() override { ServiceTest::SetUp(); - media::mojom::MediaServicePtr media_service; - connector()->BindInterface(media::mojom::kMediaServiceName, &media_service); - - service_manager::mojom::InterfaceProviderPtr interfaces; + service_manager::mojom::InterfaceProviderPtr host_interfaces; auto provider = std::make_unique<MediaInterfaceProvider>( - mojo::MakeRequest(&interfaces)); - media_service->CreateInterfaceFactory( - mojo::MakeRequest(&interface_factory_), std::move(interfaces)); - - run_loop_.reset(new base::RunLoop()); + mojo::MakeRequest(&host_interfaces)); + + connector()->BindInterface(mojom::kMediaServiceName, &media_service_); + media_service_.set_connection_error_handler( + base::BindRepeating(&MediaServiceTest::MediaServiceConnectionClosed, + base::Unretained(this))); + media_service_->CreateInterfaceFactory( + mojo::MakeRequest(&interface_factory_), std::move(host_interfaces)); } - // MOCK_METHOD* doesn't support move only types. Work around this by having - // an extra method. - MOCK_METHOD2(OnCdmInitializedInternal, void(bool result, int cdm_id)); - void OnCdmInitialized(mojom::CdmPromiseResultPtr result, - int cdm_id, - mojom::DecryptorPtr decryptor) { - OnCdmInitializedInternal(result->success, cdm_id); - } + MOCK_METHOD3(OnCdmInitialized, + void(mojom::CdmPromiseResultPtr result, + int cdm_id, + mojom::DecryptorPtr decryptor)); + MOCK_METHOD0(OnCdmConnectionError, void()); - void InitializeCdm(const std::string& key_system, - bool expected_result, - int cdm_id) { + // Returns the CDM ID associated with the CDM. + int InitializeCdm(const std::string& key_system, bool expected_result) { + base::RunLoop run_loop; interface_factory_->CreateCdm(key_system, mojo::MakeRequest(&cdm_)); + cdm_.set_connection_error_handler(base::BindRepeating( + &MediaServiceTest::OnCdmConnectionError, base::Unretained(this))); + + int cdm_id = CdmContext::kInvalidCdmId; - EXPECT_CALL(*this, OnCdmInitializedInternal(expected_result, cdm_id)) - .WillOnce(InvokeWithoutArgs(run_loop_.get(), &base::RunLoop::Quit)); + // The last parameter mojom::DecryptorPtr is move-only and not supported by + // DoAll. Hence use WithArg to only extract the "int cdm_id" out and then + // call DoAll. + EXPECT_CALL(*this, OnCdmInitialized(MatchesResult(expected_result), _, _)) + .WillOnce(WithArg<1>(DoAll(SaveArg<0>(&cdm_id), QuitLoop(&run_loop)))); cdm_->Initialize(key_system, url::Origin::Create(GURL(kSecurityOrigin)), CdmConfig(), - base::Bind(&MediaServiceTest::OnCdmInitialized, - base::Unretained(this))); + base::BindOnce(&MediaServiceTest::OnCdmInitialized, + base::Unretained(this))); + run_loop.Run(); + return cdm_id; + } + + MOCK_METHOD4(OnCdmProxyInitialized, + void(CdmProxy::Status status, + CdmProxy::Protocol protocol, + uint32_t crypto_session_id, + int cdm_id)); + + // Returns the CDM ID associated with the CdmProxy. + int InitializeCdmProxy(const std::string& cdm_guid) { + base::RunLoop run_loop; + interface_factory_->CreateCdmProxy(cdm_guid, + mojo::MakeRequest(&cdm_proxy_)); + + mojom::CdmProxyClientAssociatedPtrInfo client_ptr_info; + cdm_proxy_client_binding_.Bind(mojo::MakeRequest(&client_ptr_info)); + int cdm_id = CdmContext::kInvalidCdmId; + + EXPECT_CALL(*this, OnCdmProxyInitialized(CdmProxy::Status::kOk, _, _, _)) + .WillOnce(DoAll(SaveArg<3>(&cdm_id), QuitLoop(&run_loop))); + cdm_proxy_->Initialize( + std::move(client_ptr_info), + base::BindOnce(&MediaServiceTest::OnCdmProxyInitialized, + base::Unretained(this))); + run_loop.Run(); + return cdm_id; + } + + MOCK_METHOD2(OnDecrypted, + void(Decryptor::Status, scoped_refptr<DecoderBuffer>)); + + void CreateDecryptor(int cdm_id, bool expected_result) { + base::RunLoop run_loop; + mojom::DecryptorPtr decryptor_ptr; + interface_factory_->CreateDecryptor(cdm_id, + mojo::MakeRequest(&decryptor_ptr)); + MojoDecryptor mojo_decryptor(std::move(decryptor_ptr)); + + // In the success case, there's no decryption key to decrypt the buffer so + // we would expect no-key. + auto expected_status = + expected_result ? Decryptor::kNoKey : Decryptor::kError; + + EXPECT_CALL(*this, OnDecrypted(expected_status, _)) + .WillOnce(QuitLoop(&run_loop)); + mojo_decryptor.Decrypt(Decryptor::kVideo, CreateEncryptedBuffer(), + base::BindRepeating(&MediaServiceTest::OnDecrypted, + base::Unretained(this))); + run_loop.Run(); } MOCK_METHOD1(OnRendererInitialized, void(bool)); void InitializeRenderer(const VideoDecoderConfig& video_config, bool expected_result) { + base::RunLoop run_loop; interface_factory_->CreateRenderer( media::mojom::HostedRendererType::kDefault, std::string(), mojo::MakeRequest(&renderer_)); @@ -135,25 +237,31 @@ class MediaServiceTest : public service_manager::test::ServiceTest { mojom::RendererClientAssociatedPtrInfo client_ptr_info; renderer_client_binding_.Bind(mojo::MakeRequest(&client_ptr_info)); - EXPECT_CALL(*this, OnRendererInitialized(expected_result)) - .WillOnce(InvokeWithoutArgs(run_loop_.get(), &base::RunLoop::Quit)); std::vector<mojom::DemuxerStreamPtrInfo> streams; streams.push_back(std::move(video_stream_proxy_info)); - renderer_->Initialize(std::move(client_ptr_info), std::move(streams), - base::nullopt, base::nullopt, - base::Bind(&MediaServiceTest::OnRendererInitialized, - base::Unretained(this))); + + EXPECT_CALL(*this, OnRendererInitialized(expected_result)) + .WillOnce(QuitLoop(&run_loop)); + renderer_->Initialize( + std::move(client_ptr_info), std::move(streams), base::nullopt, + base::nullopt, + base::BindOnce(&MediaServiceTest::OnRendererInitialized, + base::Unretained(this))); + run_loop.Run(); } - MOCK_METHOD0(ConnectionClosed, void()); + MOCK_METHOD0(MediaServiceConnectionClosed, void()); protected: - std::unique_ptr<base::RunLoop> run_loop_; - + mojom::MediaServicePtr media_service_; mojom::InterfaceFactoryPtr interface_factory_; mojom::ContentDecryptionModulePtr cdm_; + mojom::CdmProxyPtr cdm_proxy_; mojom::RendererPtr renderer_; + NiceMock<MockCdmProxyClient> cdm_proxy_client_; + mojo::AssociatedBinding<mojom::CdmProxyClient> cdm_proxy_client_binding_; + NiceMock<MockRendererClient> renderer_client_; mojo::AssociatedBinding<mojom::RendererClient> renderer_client_binding_; @@ -168,47 +276,148 @@ class MediaServiceTest : public service_manager::test::ServiceTest { // Note: base::RunLoop::RunUntilIdle() does not work well in these tests because // even when the loop is idle, we may still have pending events in the pipe. +// - If you have an InterfacePtr hosted by the service in the service process, +// you can use InterfacePtr::FlushForTesting(). Note that this doesn't drain +// the task runner in the test process and doesn't cover all negative cases. +// - If you expect a callback on an InterfacePtr call or connection error, use +// base::RunLoop::Run() and QuitLoop(). // TODO(crbug.com/829233): Enable these tests on Android. #if BUILDFLAG(ENABLE_MOJO_CDM) && !defined(OS_ANDROID) TEST_F(MediaServiceTest, InitializeCdm_Success) { - InitializeCdm(kClearKeyKeySystem, true, 1); - run_loop_->Run(); + InitializeCdm(kClearKeyKeySystem, true); } TEST_F(MediaServiceTest, InitializeCdm_InvalidKeySystem) { - InitializeCdm(kInvalidKeySystem, false, 0); - run_loop_->Run(); + InitializeCdm(kInvalidKeySystem, false); +} + +TEST_F(MediaServiceTest, Decryptor_WithCdm) { + int cdm_id = InitializeCdm(kClearKeyKeySystem, true); + CreateDecryptor(cdm_id, true); } #endif // BUILDFLAG(ENABLE_MOJO_CDM) && !defined(OS_ANDROID) #if BUILDFLAG(ENABLE_MOJO_RENDERER) TEST_F(MediaServiceTest, InitializeRenderer) { InitializeRenderer(TestVideoConfig::Normal(), true); - run_loop_->Run(); } #endif // BUILDFLAG(ENABLE_MOJO_RENDERER) +#if BUILDFLAG(ENABLE_LIBRARY_CDMS) +TEST_F(MediaServiceTest, CdmProxy) { + InitializeCdmProxy(kClearKeyCdmGuid); +} + +TEST_F(MediaServiceTest, Decryptor_WithCdmProxy) { + int cdm_id = InitializeCdmProxy(kClearKeyCdmGuid); + CreateDecryptor(cdm_id, true); +} + +TEST_F(MediaServiceTest, Decryptor_WrongCdmId) { + int cdm_id = InitializeCdmProxy(kClearKeyCdmGuid); + CreateDecryptor(cdm_id + 1, false); +} + +TEST_F(MediaServiceTest, DeferredDestruction_CdmProxy) { + InitializeCdmProxy(kClearKeyCdmGuid); + + // Disconnecting InterfaceFactory should not terminate the MediaService since + // there is still a CdmProxy hosted. + interface_factory_.reset(); + cdm_proxy_.FlushForTesting(); + + // Disconnecting CdmProxy will now terminate the MediaService. + base::RunLoop run_loop; + EXPECT_CALL(*this, MediaServiceConnectionClosed()) + .WillOnce(QuitLoop(&run_loop)); + cdm_proxy_.reset(); + run_loop.Run(); +} +#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) + +TEST_F(MediaServiceTest, Decryptor_WithoutCdmOrCdmProxy) { + // Creating decryptor without creating CDM or CdmProxy. + CreateDecryptor(1, false); +} + +TEST_F(MediaServiceTest, Lifetime_DestroyMediaService) { + // Disconnecting |media_service_| doesn't terminate MediaService + // since |interface_factory_| is still alive. This is ensured here since + // MediaServiceConnectionClosed() is not called. + EXPECT_CALL(*this, MediaServiceConnectionClosed()).Times(0); + media_service_.reset(); + interface_factory_.FlushForTesting(); +} + +TEST_F(MediaServiceTest, Lifetime_DestroyInterfaceFactory) { + // Disconnecting InterfaceFactory will now terminate the MediaService since + // there's no media components hosted. + base::RunLoop run_loop; + EXPECT_CALL(*this, MediaServiceConnectionClosed()) + .WillOnce(QuitLoop(&run_loop)); + interface_factory_.reset(); + run_loop.Run(); +} + +#if (BUILDFLAG(ENABLE_MOJO_CDM) && !defined(OS_ANDROID)) || \ + BUILDFLAG(ENABLE_MOJO_RENDERER) +// MediaService stays alive as long as there are InterfaceFactory impls, which +// are then deferred destroyed until no media components (e.g. CDM or Renderer) +// are hosted. TEST_F(MediaServiceTest, Lifetime) { - // The lifetime of the media service is controlled by the number of - // live InterfaceFactory impls, not MediaService impls, so this pipe should - // be closed when the last InterfaceFactory is destroyed. - media::mojom::MediaServicePtr media_service; - connector()->BindInterface(media::mojom::kMediaServiceName, &media_service); - media_service.set_connection_error_handler( - base::Bind(&MediaServiceTest::ConnectionClosed, base::Unretained(this))); - - // Disconnecting CDM and Renderer services doesn't terminate the app. +#if BUILDFLAG(ENABLE_MOJO_CDM) && !defined(OS_ANDROID) + InitializeCdm(kClearKeyKeySystem, true); +#endif + +#if BUILDFLAG(ENABLE_MOJO_RENDERER) + InitializeRenderer(TestVideoConfig::Normal(), true); +#endif + + // Disconnecting CDM and Renderer services doesn't terminate MediaService + // since |interface_factory_| is still alive. cdm_.reset(); renderer_.reset(); + interface_factory_.FlushForTesting(); - // Disconnecting InterfaceFactory service should terminate the app, which will - // close the connection. - EXPECT_CALL(*this, ConnectionClosed()) - .WillOnce(Invoke(run_loop_.get(), &base::RunLoop::Quit)); + // Disconnecting InterfaceFactory will now terminate the MediaService. + base::RunLoop run_loop; + EXPECT_CALL(*this, MediaServiceConnectionClosed()) + .WillOnce(QuitLoop(&run_loop)); interface_factory_.reset(); + run_loop.Run(); +} + +TEST_F(MediaServiceTest, DeferredDestruction) { +#if BUILDFLAG(ENABLE_MOJO_CDM) && !defined(OS_ANDROID) + InitializeCdm(kClearKeyKeySystem, true); +#endif - run_loop_->Run(); +#if BUILDFLAG(ENABLE_MOJO_RENDERER) + InitializeRenderer(TestVideoConfig::Normal(), true); +#endif + + ASSERT_TRUE(cdm_ || renderer_); + + // Disconnecting InterfaceFactory should not terminate the MediaService since + // there are still media components (CDM or Renderer) hosted. + interface_factory_.reset(); + if (cdm_) + cdm_.FlushForTesting(); + else if (renderer_) + renderer_.FlushForTesting(); + else + NOTREACHED(); + + // Disconnecting CDM and Renderer will now terminate the MediaService. + base::RunLoop run_loop; + EXPECT_CALL(*this, MediaServiceConnectionClosed()) + .WillOnce(QuitLoop(&run_loop)); + cdm_.reset(); + renderer_.reset(); + run_loop.Run(); } +#endif // (BUILDFLAG(ENABLE_MOJO_CDM) && !defined(OS_ANDROID)) || + // BUILDFLAG(ENABLE_MOJO_RENDERER) } // namespace media diff --git a/chromium/media/mojo/services/mojo_audio_decoder_service.cc b/chromium/media/mojo/services/mojo_audio_decoder_service.cc index 5f50351e72d..15cae7c515f 100644 --- a/chromium/media/mojo/services/mojo_audio_decoder_service.cc +++ b/chromium/media/mojo/services/mojo_audio_decoder_service.cc @@ -57,7 +57,7 @@ void MojoAudioDecoderService::Initialize(const AudioDecoderConfig& config, base::Bind(&MojoAudioDecoderService::OnInitialized, weak_this_, base::Passed(&callback)), base::Bind(&MojoAudioDecoderService::OnAudioBufferReady, weak_this_), - media::AudioDecoder::WaitingForDecryptionKeyCB()); + base::NullCallback()); } void MojoAudioDecoderService::SetDataSource( diff --git a/chromium/media/mojo/services/mojo_audio_input_stream.cc b/chromium/media/mojo/services/mojo_audio_input_stream.cc index 7592b7589f4..3762dbb608c 100644 --- a/chromium/media/mojo/services/mojo_audio_input_stream.cc +++ b/chromium/media/mojo/services/mojo_audio_input_stream.cc @@ -8,7 +8,7 @@ #include <utility> #include "base/callback_helpers.h" -#include "base/memory/shared_memory.h" +#include "base/memory/read_only_shared_memory_region.h" #include "base/sync_socket.h" #include "mojo/public/cpp/system/platform_handle.h" @@ -49,6 +49,12 @@ MojoAudioInputStream::~MojoAudioInputStream() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } +void MojoAudioInputStream::SetOutputDeviceForAec( + const std::string& raw_output_device_id) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + delegate_->OnSetOutputDeviceForAec(raw_output_device_id); +} + void MojoAudioInputStream::Record() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); delegate_->OnRecordStream(); @@ -67,24 +73,20 @@ void MojoAudioInputStream::SetVolume(double volume) { void MojoAudioInputStream::OnStreamCreated( int stream_id, - const base::SharedMemory* shared_memory, + base::ReadOnlySharedMemoryRegion shared_memory_region, std::unique_ptr<base::CancelableSyncSocket> foreign_socket, bool initially_muted) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(stream_created_callback_); - DCHECK(shared_memory); DCHECK(foreign_socket); - base::SharedMemoryHandle foreign_memory_handle = - shared_memory->GetReadOnlyHandle(); - if (!base::SharedMemory::IsHandleValid(foreign_memory_handle)) { + if (!shared_memory_region.IsValid()) { OnStreamError(/*not used*/ 0); return; } - mojo::ScopedSharedBufferHandle buffer_handle = mojo::WrapSharedMemoryHandle( - foreign_memory_handle, shared_memory->requested_size(), - mojo::UnwrappedSharedMemoryHandleProtection::kReadOnly); + mojo::ScopedSharedBufferHandle buffer_handle = + mojo::WrapReadOnlySharedMemoryRegion(std::move(shared_memory_region)); mojo::ScopedHandle socket_handle = mojo::WrapPlatformFile(foreign_socket->Release()); diff --git a/chromium/media/mojo/services/mojo_audio_input_stream.h b/chromium/media/mojo/services/mojo_audio_input_stream.h index d28f1e1c27e..26c4af1ef56 100644 --- a/chromium/media/mojo/services/mojo_audio_input_stream.h +++ b/chromium/media/mojo/services/mojo_audio_input_stream.h @@ -42,6 +42,8 @@ class MEDIA_MOJO_EXPORT MojoAudioInputStream ~MojoAudioInputStream() override; + void SetOutputDeviceForAec(const std::string& raw_output_device_id); + private: // mojom::AudioInputStream implementation. void Record() override; @@ -50,7 +52,7 @@ class MEDIA_MOJO_EXPORT MojoAudioInputStream // AudioInputDelegate::EventHandler implementation. void OnStreamCreated( int stream_id, - const base::SharedMemory* shared_memory, + base::ReadOnlySharedMemoryRegion shared_memory_region, std::unique_ptr<base::CancelableSyncSocket> foreign_socket, bool initially_muted) override; void OnMuted(int stream_id, bool is_muted) override; diff --git a/chromium/media/mojo/services/mojo_audio_input_stream_observer.cc b/chromium/media/mojo/services/mojo_audio_input_stream_observer.cc deleted file mode 100644 index 8f93afeadb4..00000000000 --- a/chromium/media/mojo/services/mojo_audio_input_stream_observer.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/mojo/services/mojo_audio_input_stream_observer.h" - -#include <utility> - -namespace media { - -MojoAudioInputStreamObserver::MojoAudioInputStreamObserver( - mojom::AudioInputStreamObserverRequest request, - base::OnceClosure recording_started_callback, - base::OnceClosure connection_error_callback) - : binding_(this, std::move(request)), - recording_started_callback_(std::move(recording_started_callback)) { - DCHECK(recording_started_callback_); - binding_.set_connection_error_handler(std::move(connection_error_callback)); -} - -MojoAudioInputStreamObserver::~MojoAudioInputStreamObserver() { - DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); -} - -void MojoAudioInputStreamObserver::DidStartRecording() { - DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); - DCHECK(recording_started_callback_); - std::move(recording_started_callback_).Run(); -} - -} // namespace media diff --git a/chromium/media/mojo/services/mojo_audio_input_stream_observer.h b/chromium/media/mojo/services/mojo_audio_input_stream_observer.h deleted file mode 100644 index c85f73df712..00000000000 --- a/chromium/media/mojo/services/mojo_audio_input_stream_observer.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_MOJO_SERVICES_MOJO_AUDIO_INPUT_STREAM_OBSERVER_H_ -#define MEDIA_MOJO_SERVICES_MOJO_AUDIO_INPUT_STREAM_OBSERVER_H_ - -#include "media/mojo/interfaces/audio_input_stream.mojom.h" -#include "media/mojo/services/media_mojo_export.h" -#include "mojo/public/cpp/bindings/binding.h" - -namespace media { - -class MEDIA_MOJO_EXPORT MojoAudioInputStreamObserver - : public mojom::AudioInputStreamObserver { - public: - MojoAudioInputStreamObserver(mojom::AudioInputStreamObserverRequest request, - base::OnceClosure recording_started_callback, - base::OnceClosure connection_error_callback); - ~MojoAudioInputStreamObserver() override; - - void DidStartRecording() override; - - private: - mojo::Binding<AudioInputStreamObserver> binding_; - base::OnceClosure recording_started_callback_; - - SEQUENCE_CHECKER(owning_sequence_); - - DISALLOW_COPY_AND_ASSIGN(MojoAudioInputStreamObserver); -}; - -} // namespace media - -#endif // MEDIA_MOJO_SERVICES_MOJO_AUDIO_INPUT_STREAM_OBSERVER_H_ diff --git a/chromium/media/mojo/services/mojo_audio_input_stream_observer_unittest.cc b/chromium/media/mojo/services/mojo_audio_input_stream_observer_unittest.cc deleted file mode 100644 index 287eeb3e99a..00000000000 --- a/chromium/media/mojo/services/mojo_audio_input_stream_observer_unittest.cc +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/mojo/services/mojo_audio_input_stream_observer.h" - -#include <memory> -#include <utility> - -#include "base/run_loop.h" -#include "base/test/scoped_task_environment.h" -#include "mojo/public/cpp/bindings/associated_binding.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace media { - -class MojoAudioInputStreamObserverTest : public testing::Test { - public: - MojoAudioInputStreamObserverTest() {} - ~MojoAudioInputStreamObserverTest() {} - - std::unique_ptr<MojoAudioInputStreamObserver> CreateObserver( - media::mojom::AudioInputStreamObserverRequest request) { - return std::make_unique<MojoAudioInputStreamObserver>( - std::move(request), - base::BindOnce( - &MojoAudioInputStreamObserverTest::RecordingStartedCallback, - base::Unretained(this)), - base::BindOnce( - &MojoAudioInputStreamObserverTest::BindingConnectionError, - base::Unretained(this))); - } - - MOCK_METHOD0(RecordingStartedCallback, void()); - MOCK_METHOD0(BindingConnectionError, void()); - - private: - base::test::ScopedTaskEnvironment scoped_task_env_; - - DISALLOW_COPY_AND_ASSIGN(MojoAudioInputStreamObserverTest); -}; - -TEST_F(MojoAudioInputStreamObserverTest, DidStartRecording) { - media::mojom::AudioInputStreamObserverPtr observer_ptr; - std::unique_ptr<MojoAudioInputStreamObserver> observer = - CreateObserver(mojo::MakeRequest(&observer_ptr)); - - EXPECT_CALL(*this, RecordingStartedCallback()); - observer_ptr->DidStartRecording(); - base::RunLoop().RunUntilIdle(); -} - -TEST_F(MojoAudioInputStreamObserverTest, BindingConnectionError) { - media::mojom::AudioInputStreamObserverPtr observer_ptr; - std::unique_ptr<MojoAudioInputStreamObserver> observer = - CreateObserver(mojo::MakeRequest(&observer_ptr)); - - EXPECT_CALL(*this, BindingConnectionError()); - observer_ptr.reset(); - base::RunLoop().RunUntilIdle(); -} - -} // namespace media diff --git a/chromium/media/mojo/services/mojo_audio_input_stream_unittest.cc b/chromium/media/mojo/services/mojo_audio_input_stream_unittest.cc index 5271b9227e1..a193f4fc665 100644 --- a/chromium/media/mojo/services/mojo_audio_input_stream_unittest.cc +++ b/chromium/media/mojo/services/mojo_audio_input_stream_unittest.cc @@ -6,7 +6,7 @@ #include <utility> -#include "base/memory/shared_memory.h" +#include "base/memory/read_only_shared_memory_region.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/sync_socket.h" @@ -59,11 +59,12 @@ class TestCancelableSyncSocket : public base::CancelableSyncSocket { class MockDelegate : public AudioInputDelegate { public: MockDelegate() = default; - ~MockDelegate() = default; + ~MockDelegate() override = default; MOCK_METHOD0(GetStreamId, int()); MOCK_METHOD0(OnRecordStream, void()); MOCK_METHOD1(OnSetVolume, void(double)); + MOCK_METHOD1(OnSetOutputDeviceForAec, void(const std::string&)); }; class MockDelegateFactory { @@ -105,17 +106,9 @@ class MockClient : public mojom::AudioInputStreamClient { socket_ = std::make_unique<base::CancelableSyncSocket>(fd); EXPECT_NE(socket_->handle(), base::CancelableSyncSocket::kInvalidHandle); - size_t memory_length; - base::SharedMemoryHandle shmem_handle; - mojo::UnwrappedSharedMemoryHandleProtection protection; - EXPECT_EQ(mojo::UnwrapSharedMemoryHandle( - std::move(data_pipe->shared_memory), &shmem_handle, - &memory_length, &protection), - MOJO_RESULT_OK); - EXPECT_EQ(protection, - mojo::UnwrappedSharedMemoryHandleProtection::kReadOnly); - buffer_ = std::make_unique<base::SharedMemory>(shmem_handle, - true /* read_only */); + region_ = mojo::UnwrapReadOnlySharedMemoryRegion( + std::move(data_pipe->shared_memory)); + EXPECT_TRUE(region_.IsValid()); GotNotification(initially_muted); } @@ -127,7 +120,7 @@ class MockClient : public mojom::AudioInputStreamClient { MOCK_METHOD0(OnError, void()); private: - std::unique_ptr<base::SharedMemory> buffer_; + base::ReadOnlySharedMemoryRegion region_; std::unique_ptr<base::CancelableSyncSocket> socket_; DISALLOW_COPY_AND_ASSIGN(MockClient); @@ -171,10 +164,8 @@ class MojoAudioInputStreamTest : public Test { base::WrapUnique(delegate_)); EXPECT_TRUE( base::CancelableSyncSocket::CreatePair(&local_, foreign_socket_.get())); - base::SharedMemoryCreateOptions shmem_options; - shmem_options.size = kShmemSize; - shmem_options.share_read_only = true; - EXPECT_TRUE(mem_.Create(shmem_options)); + mem_ = base::ReadOnlySharedMemoryRegion::Create(kShmemSize).region; + EXPECT_TRUE(mem_.IsValid()); EXPECT_CALL(mock_delegate_factory_, MockCreateDelegate(NotNull())) .WillOnce(SaveArg<0>(&delegate_event_handler_)); } @@ -182,7 +173,7 @@ class MojoAudioInputStreamTest : public Test { base::MessageLoop loop_; base::CancelableSyncSocket local_; std::unique_ptr<TestCancelableSyncSocket> foreign_socket_; - base::SharedMemory mem_; + base::ReadOnlySharedMemoryRegion mem_; StrictMock<MockDelegate>* delegate_ = nullptr; AudioInputDelegate::EventHandler* delegate_event_handler_ = nullptr; StrictMock<MockDelegateFactory> mock_delegate_factory_; @@ -232,8 +223,9 @@ TEST_F(MojoAudioInputStreamTest, DestructWithCallPending_Safe) { ASSERT_NE(nullptr, delegate_event_handler_); foreign_socket_->ExpectOwnershipTransfer(); - delegate_event_handler_->OnStreamCreated( - kStreamId, &mem_, std::move(foreign_socket_), kInitiallyNotMuted); + delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_), + std::move(foreign_socket_), + kInitiallyNotMuted); audio_input_ptr->Record(); impl_.reset(); base::RunLoop().RunUntilIdle(); @@ -247,8 +239,9 @@ TEST_F(MojoAudioInputStreamTest, Created_NotifiesClient) { ASSERT_NE(nullptr, delegate_event_handler_); foreign_socket_->ExpectOwnershipTransfer(); - delegate_event_handler_->OnStreamCreated( - kStreamId, &mem_, std::move(foreign_socket_), kInitiallyNotMuted); + delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_), + std::move(foreign_socket_), + kInitiallyNotMuted); base::RunLoop().RunUntilIdle(); } @@ -294,8 +287,9 @@ TEST_F(MojoAudioInputStreamTest, DelegateErrorAfterCreated_PropagatesError) { ASSERT_NE(nullptr, delegate_event_handler_); foreign_socket_->ExpectOwnershipTransfer(); - delegate_event_handler_->OnStreamCreated( - kStreamId, &mem_, std::move(foreign_socket_), kInitiallyNotMuted); + delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_), + std::move(foreign_socket_), + kInitiallyNotMuted); delegate_event_handler_->OnStreamError(kStreamId); base::RunLoop().RunUntilIdle(); diff --git a/chromium/media/mojo/services/mojo_audio_output_stream.cc b/chromium/media/mojo/services/mojo_audio_output_stream.cc index 79e52418c6a..5cdfb6ca5fd 100644 --- a/chromium/media/mojo/services/mojo_audio_output_stream.cc +++ b/chromium/media/mojo/services/mojo_audio_output_stream.cc @@ -15,29 +15,20 @@ namespace media { MojoAudioOutputStream::MojoAudioOutputStream( - mojom::AudioOutputStreamRequest request, - mojom::AudioOutputStreamClientPtr client, CreateDelegateCallback create_delegate_callback, StreamCreatedCallback stream_created_callback, - base::OnceClosure deleter_callback) + DeleterCallback deleter_callback) : stream_created_callback_(std::move(stream_created_callback)), deleter_callback_(std::move(deleter_callback)), - binding_(this, std::move(request)), - client_(std::move(client)), + binding_(this), weak_factory_(this) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(stream_created_callback_); DCHECK(deleter_callback_); - // |this| owns |binding_|, so unretained is safe. - binding_.set_connection_error_handler( - base::BindOnce(&MojoAudioOutputStream::OnError, base::Unretained(this))); - client_.set_connection_error_handler( - base::BindOnce(&MojoAudioOutputStream::OnError, base::Unretained(this))); delegate_ = std::move(create_delegate_callback).Run(this); if (!delegate_) { // Failed to initialize the stream. We cannot call |deleter_callback_| yet, // since construction isn't done. - binding_.Close(); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&MojoAudioOutputStream::OnStreamError, @@ -72,44 +63,46 @@ void MojoAudioOutputStream::SetVolume(double volume) { void MojoAudioOutputStream::OnStreamCreated( int stream_id, - const base::SharedMemory* shared_memory, + base::UnsafeSharedMemoryRegion shared_memory_region, std::unique_ptr<base::CancelableSyncSocket> foreign_socket) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(stream_created_callback_); - DCHECK(shared_memory); DCHECK(foreign_socket); - base::SharedMemoryHandle foreign_memory_handle = - base::SharedMemory::DuplicateHandle(shared_memory->handle()); - if (!base::SharedMemory::IsHandleValid(foreign_memory_handle)) { + if (!shared_memory_region.IsValid()) { OnStreamError(/*not used*/ 0); return; } - mojo::ScopedSharedBufferHandle buffer_handle = mojo::WrapSharedMemoryHandle( - foreign_memory_handle, shared_memory->requested_size(), - mojo::UnwrappedSharedMemoryHandleProtection::kReadWrite); + mojo::ScopedSharedBufferHandle buffer_handle = + mojo::WrapUnsafeSharedMemoryRegion(std::move(shared_memory_region)); mojo::ScopedHandle socket_handle = mojo::WrapPlatformFile(foreign_socket->Release()); DCHECK(buffer_handle.is_valid()); DCHECK(socket_handle.is_valid()); - base::ResetAndReturn(&stream_created_callback_) - .Run( - {base::in_place, std::move(buffer_handle), std::move(socket_handle)}); + mojom::AudioOutputStreamPtr stream; + binding_.Bind(mojo::MakeRequest(&stream)); + // |this| owns |binding_| so unretained is safe. + binding_.set_connection_error_handler(base::BindOnce( + &MojoAudioOutputStream::StreamConnectionLost, base::Unretained(this))); + + std::move(stream_created_callback_) + .Run(std::move(stream), {base::in_place, std::move(buffer_handle), + std::move(socket_handle)}); } void MojoAudioOutputStream::OnStreamError(int stream_id) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - client_->OnError(); - OnError(); + DCHECK(deleter_callback_); + std::move(deleter_callback_).Run(/*had_error*/ true); // Deletes |this|. } -void MojoAudioOutputStream::OnError() { +void MojoAudioOutputStream::StreamConnectionLost() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(deleter_callback_); - std::move(deleter_callback_).Run(); // Deletes |this|. + std::move(deleter_callback_).Run(/*had_error*/ false); // Deletes |this|. } } // namespace media diff --git a/chromium/media/mojo/services/mojo_audio_output_stream.h b/chromium/media/mojo/services/mojo_audio_output_stream.h index 217052605cf..31c1fcf5eef 100644 --- a/chromium/media/mojo/services/mojo_audio_output_stream.h +++ b/chromium/media/mojo/services/mojo_audio_output_stream.h @@ -23,21 +23,22 @@ class MEDIA_MOJO_EXPORT MojoAudioOutputStream public AudioOutputDelegate::EventHandler { public: using StreamCreatedCallback = - mojom::AudioOutputStreamProvider::AcquireCallback; + base::OnceCallback<void(mojom::AudioOutputStreamPtr, + media::mojom::AudioDataPipePtr)>; using CreateDelegateCallback = base::OnceCallback<std::unique_ptr<AudioOutputDelegate>( AudioOutputDelegate::EventHandler*)>; + using DeleterCallback = base::OnceCallback<void(bool)>; // |create_delegate_callback| is used to obtain an AudioOutputDelegate for the // stream in the constructor. |stream_created_callback| is called when the // stream has been initialized. |deleter_callback| is called when this class - // should be removed (stream ended/error). |deleter_callback| is required to - // destroy |this| synchronously. - MojoAudioOutputStream(mojom::AudioOutputStreamRequest request, - mojom::AudioOutputStreamClientPtr client, - CreateDelegateCallback create_delegate_callback, + // should be removed (stream ended/error). Its argument indicates if an error + // was encountered (false indicates that the remote end closed the stream). + // |deleter_callback| is required to destroy |this| synchronously. + MojoAudioOutputStream(CreateDelegateCallback create_delegate_callback, StreamCreatedCallback stream_created_callback, - base::OnceClosure deleter_callback); + DeleterCallback deleter_callback); ~MojoAudioOutputStream() override; @@ -50,19 +51,17 @@ class MEDIA_MOJO_EXPORT MojoAudioOutputStream // AudioOutputDelegate::EventHandler implementation. void OnStreamCreated( int stream_id, - const base::SharedMemory* shared_memory, + base::UnsafeSharedMemoryRegion shared_memory_region, std::unique_ptr<base::CancelableSyncSocket> foreign_socket) override; void OnStreamError(int stream_id) override; - // Closes connection to client and notifies owner. - void OnError(); + void StreamConnectionLost(); SEQUENCE_CHECKER(sequence_checker_); StreamCreatedCallback stream_created_callback_; - base::OnceClosure deleter_callback_; + DeleterCallback deleter_callback_; mojo::Binding<AudioOutputStream> binding_; - mojom::AudioOutputStreamClientPtr client_; std::unique_ptr<AudioOutputDelegate> delegate_; base::WeakPtrFactory<MojoAudioOutputStream> weak_factory_; diff --git a/chromium/media/mojo/services/mojo_audio_output_stream_provider.cc b/chromium/media/mojo/services/mojo_audio_output_stream_provider.cc index 32e7b7fc790..1a661e8099b 100644 --- a/chromium/media/mojo/services/mojo_audio_output_stream_provider.cc +++ b/chromium/media/mojo/services/mojo_audio_output_stream_provider.cc @@ -23,8 +23,9 @@ MojoAudioOutputStreamProvider::MojoAudioOutputStreamProvider( observer_binding_(observer_.get()) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // Unretained is safe since |this| owns |binding_|. - binding_.set_connection_error_handler(base::Bind( - &MojoAudioOutputStreamProvider::OnError, base::Unretained(this))); + binding_.set_connection_error_handler( + base::BindOnce(&MojoAudioOutputStreamProvider::CleanUp, + base::Unretained(this), /*had_error*/ false)); DCHECK(create_delegate_callback_); DCHECK(deleter_callback_); } @@ -34,10 +35,8 @@ MojoAudioOutputStreamProvider::~MojoAudioOutputStreamProvider() { } void MojoAudioOutputStreamProvider::Acquire( - mojom::AudioOutputStreamRequest stream_request, - mojom::AudioOutputStreamClientPtr client, const AudioParameters& params, - AcquireCallback callback) { + mojom::AudioOutputStreamProviderClientPtr provider_client) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); #if !defined(OS_ANDROID) if (params.IsBitstreamFormat()) { @@ -53,29 +52,32 @@ void MojoAudioOutputStreamProvider::Acquire( return; } + provider_client_ = std::move(provider_client); + mojom::AudioOutputStreamObserverPtr observer_ptr; observer_binding_.Bind(mojo::MakeRequest(&observer_ptr)); // Unretained is safe since |this| owns |audio_output_|. - audio_output_.emplace(std::move(stream_request), std::move(client), - base::BindOnce(std::move(create_delegate_callback_), - params, std::move(observer_ptr)), - std::move(callback), - base::BindOnce(&MojoAudioOutputStreamProvider::OnError, - base::Unretained(this))); + audio_output_.emplace( + base::BindOnce(std::move(create_delegate_callback_), params, + std::move(observer_ptr)), + base::BindOnce(&mojom::AudioOutputStreamProviderClient::Created, + base::Unretained(provider_client_.get())), + base::BindOnce(&MojoAudioOutputStreamProvider::CleanUp, + base::Unretained(this))); } -void MojoAudioOutputStreamProvider::OnError() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // Deletes |this|: +void MojoAudioOutputStreamProvider::CleanUp(bool had_error) { + if (had_error) { + provider_client_.ResetWithReason( + static_cast<uint32_t>(media::mojom::AudioOutputStreamObserver:: + DisconnectReason::kPlatformError), + std::string()); + } std::move(deleter_callback_).Run(this); } void MojoAudioOutputStreamProvider::BadMessage(const std::string& error) { mojo::ReportBadMessage(error); - if (binding_.is_bound()) - binding_.Unbind(); - if (observer_binding_.is_bound()) - observer_binding_.Unbind(); std::move(deleter_callback_).Run(this); // deletes |this|. } diff --git a/chromium/media/mojo/services/mojo_audio_output_stream_provider.h b/chromium/media/mojo/services/mojo_audio_output_stream_provider.h index f642571b1a0..0d580f31b98 100644 --- a/chromium/media/mojo/services/mojo_audio_output_stream_provider.h +++ b/chromium/media/mojo/services/mojo_audio_output_stream_provider.h @@ -42,25 +42,25 @@ class MEDIA_MOJO_EXPORT MojoAudioOutputStreamProvider private: // mojom::AudioOutputStreamProvider implementation. - void Acquire(mojom::AudioOutputStreamRequest stream_request, - mojom::AudioOutputStreamClientPtr client, - const AudioParameters& params, - AcquireCallback acquire_callback) override; + void Acquire( + const AudioParameters& params, + mojom::AudioOutputStreamProviderClientPtr provider_client) override; // Called when |audio_output_| had an error. - void OnError(); + void CleanUp(bool had_error); // Closes mojo connections, reports a bad message, and self-destructs. void BadMessage(const std::string& error); SEQUENCE_CHECKER(sequence_checker_); - base::Optional<MojoAudioOutputStream> audio_output_; mojo::Binding<AudioOutputStreamProvider> binding_; CreateDelegateCallback create_delegate_callback_; DeleterCallback deleter_callback_; std::unique_ptr<mojom::AudioOutputStreamObserver> observer_; mojo::Binding<mojom::AudioOutputStreamObserver> observer_binding_; + base::Optional<MojoAudioOutputStream> audio_output_; + mojom::AudioOutputStreamProviderClientPtr provider_client_; DISALLOW_COPY_AND_ASSIGN(MojoAudioOutputStreamProvider); }; diff --git a/chromium/media/mojo/services/mojo_audio_output_stream_provider_unittest.cc b/chromium/media/mojo/services/mojo_audio_output_stream_provider_unittest.cc index 49edfa94372..e559976433e 100644 --- a/chromium/media/mojo/services/mojo_audio_output_stream_provider_unittest.cc +++ b/chromium/media/mojo/services/mojo_audio_output_stream_provider_unittest.cc @@ -31,8 +31,6 @@ using testing::StrictMock; using MockDeleter = base::MockCallback< base::OnceCallback<void(mojom::AudioOutputStreamProvider*)>>; -void FakeAcquireCallback(mojom::AudioDataPipePtr data_pipe) {} - class FakeObserver : public mojom::AudioOutputStreamObserver { public: FakeObserver() = default; @@ -83,26 +81,22 @@ TEST(MojoAudioOutputStreamProviderTest, AcquireTwice_BadMessage) { mojo::MakeRequest(&provider_ptr), base::BindOnce(&CreateFakeDelegate), deleter.Get(), std::make_unique<FakeObserver>()); - mojom::AudioOutputStreamPtr stream_1; - mojom::AudioOutputStreamClientPtr client_1; - mojom::AudioOutputStreamClientRequest client_request_1 = - mojo::MakeRequest(&client_1); - - mojom::AudioOutputStreamPtr stream_2; - mojom::AudioOutputStreamClientPtr client_2; - mojom::AudioOutputStreamClientRequest client_request_2 = - mojo::MakeRequest(&client_2); - provider_ptr->Acquire(mojo::MakeRequest(&stream_1), std::move(client_1), - media::AudioParameters::UnavailableDeviceParams(), - base::BindOnce(&FakeAcquireCallback)); - provider_ptr->Acquire(mojo::MakeRequest(&stream_2), std::move(client_2), - media::AudioParameters::UnavailableDeviceParams(), - base::BindOnce(&FakeAcquireCallback)); + mojom::AudioOutputStreamProviderClientPtr client_1; + mojo::MakeRequest(&client_1); + provider_ptr->Acquire(media::AudioParameters::UnavailableDeviceParams(), + std::move(client_1)); + + mojom::AudioOutputStreamProviderClientPtr client_2; + mojo::MakeRequest(&client_2); + provider_ptr->Acquire(media::AudioParameters::UnavailableDeviceParams(), + std::move(client_2)); EXPECT_CALL(deleter, Run(provider)).WillOnce(DeleteArg<0>()); base::RunLoop().RunUntilIdle(); EXPECT_TRUE(got_bad_message); Mock::VerifyAndClear(&deleter); + + mojo::edk::SetDefaultProcessErrorCallback(mojo::edk::ProcessErrorCallback()); } TEST(MojoAudioOutputStreamProviderTest, @@ -124,12 +118,9 @@ TEST(MojoAudioOutputStreamProviderTest, mojo::MakeRequest(&provider_ptr), base::BindOnce(&CreateFakeDelegate), deleter.Get(), std::make_unique<FakeObserver>()); - mojom::AudioOutputStreamPtr stream; - mojom::AudioOutputStreamClientPtr client; - mojom::AudioOutputStreamClientRequest client_request = - mojo::MakeRequest(&client); - provider_ptr->Acquire(mojo::MakeRequest(&stream), std::move(client), params, - base::BindOnce(&FakeAcquireCallback)); + mojom::AudioOutputStreamProviderClientPtr client; + mojo::MakeRequest(&client); + provider_ptr->Acquire(params, std::move(client)); #if defined(OS_ANDROID) base::RunLoop().RunUntilIdle(); @@ -144,6 +135,7 @@ TEST(MojoAudioOutputStreamProviderTest, EXPECT_TRUE(got_bad_message); Mock::VerifyAndClear(&deleter); #endif + mojo::edk::SetDefaultProcessErrorCallback(mojo::edk::ProcessErrorCallback()); } } // namespace media diff --git a/chromium/media/mojo/services/mojo_audio_output_stream_unittest.cc b/chromium/media/mojo/services/mojo_audio_output_stream_unittest.cc index efd161972c2..508e845d35f 100644 --- a/chromium/media/mojo/services/mojo_audio_output_stream_unittest.cc +++ b/chromium/media/mojo/services/mojo_audio_output_stream_unittest.cc @@ -11,6 +11,7 @@ #include "base/run_loop.h" #include "base/sync_socket.h" #include "media/audio/audio_output_controller.h" +#include "mojo/public/cpp/system/message_pipe.h" #include "mojo/public/cpp/system/platform_handle.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -57,7 +58,7 @@ class TestCancelableSyncSocket : public base::CancelableSyncSocket { class MockDelegate : public AudioOutputDelegate { public: MockDelegate() = default; - ~MockDelegate() = default; + ~MockDelegate() override = default; MOCK_METHOD0(GetStreamId, int()); MOCK_METHOD0(OnPlayStream, void()); @@ -88,14 +89,14 @@ class MockDelegateFactory { class MockDeleter { public: - MOCK_METHOD0(Finished, void()); + MOCK_METHOD1(Finished, void(bool)); }; -class MockClient : public mojom::AudioOutputStreamClient { +class MockClient { public: MockClient() = default; - void Initialized(mojom::AudioDataPipePtr data_pipe) { + void Initialize(mojom::AudioDataPipePtr data_pipe) { ASSERT_TRUE(data_pipe->shared_memory.is_valid()); ASSERT_TRUE(data_pipe->socket.is_valid()); @@ -121,8 +122,6 @@ class MockClient : public mojom::AudioOutputStreamClient { MOCK_METHOD0(GotNotification, void()); - MOCK_METHOD0(OnError, void()); - private: std::unique_ptr<base::SharedMemory> buffer_; std::unique_ptr<base::CancelableSyncSocket> socket_; @@ -133,9 +132,9 @@ std::unique_ptr<AudioOutputDelegate> CreateNoDelegate( return nullptr; } -void NotCalled(mojom::AudioDataPipePtr data_pipe) { - EXPECT_TRUE(false) << "The StreamCreated callback was called despite the " - "test expecting it not to."; +void NotCalled(mojom::AudioOutputStreamPtr, mojom::AudioDataPipePtr) { + ADD_FAILURE() << "The StreamCreated callback was called despite the test " + "expecting it not to."; } } // namespace @@ -143,30 +142,38 @@ void NotCalled(mojom::AudioDataPipePtr data_pipe) { class MojoAudioOutputStreamTest : public Test { public: MojoAudioOutputStreamTest() - : foreign_socket_(std::make_unique<TestCancelableSyncSocket>()), - client_binding_(&client_, mojo::MakeRequest(&client_ptr_)) {} + : foreign_socket_(std::make_unique<TestCancelableSyncSocket>()) {} AudioOutputStreamPtr CreateAudioOutput() { - AudioOutputStreamPtr p; + mojom::AudioOutputStreamPtr p; + pending_stream_request_ = mojo::MakeRequest(&p); ExpectDelegateCreation(); impl_ = std::make_unique<MojoAudioOutputStream>( - mojo::MakeRequest(&p), std::move(client_ptr_), base::BindOnce(&MockDelegateFactory::CreateDelegate, base::Unretained(&mock_delegate_factory_)), - base::BindOnce(&MockClient::Initialized, base::Unretained(&client_)), + base::BindOnce(&MojoAudioOutputStreamTest::CreatedStream, + base::Unretained(this)), base::BindOnce(&MockDeleter::Finished, base::Unretained(&deleter_))); - EXPECT_TRUE(p.is_bound()); return p; } protected: + void CreatedStream(mojom::AudioOutputStreamPtr stream, + mojom::AudioDataPipePtr data_pipe) { + EXPECT_EQ(mojo::FuseMessagePipes(pending_stream_request_.PassMessagePipe(), + stream.PassInterface().PassHandle()), + MOJO_RESULT_OK); + client_.Initialize(std::move(data_pipe)); + } + void ExpectDelegateCreation() { delegate_ = new StrictMock<MockDelegate>(); mock_delegate_factory_.PrepareDelegateForCreation( base::WrapUnique(delegate_)); EXPECT_TRUE( base::CancelableSyncSocket::CreatePair(&local_, foreign_socket_.get())); - EXPECT_TRUE(mem_.CreateAnonymous(kShmemSize)); + mem_ = base::UnsafeSharedMemoryRegion::Create(kShmemSize); + EXPECT_TRUE(mem_.IsValid()); EXPECT_CALL(mock_delegate_factory_, MockCreateDelegate(NotNull())) .WillOnce(SaveArg<0>(&delegate_event_handler_)); } @@ -174,51 +181,57 @@ class MojoAudioOutputStreamTest : public Test { base::MessageLoop loop_; base::CancelableSyncSocket local_; std::unique_ptr<TestCancelableSyncSocket> foreign_socket_; - base::SharedMemory mem_; + base::UnsafeSharedMemoryRegion mem_; StrictMock<MockDelegate>* delegate_ = nullptr; AudioOutputDelegate::EventHandler* delegate_event_handler_ = nullptr; StrictMock<MockDelegateFactory> mock_delegate_factory_; StrictMock<MockDeleter> deleter_; StrictMock<MockClient> client_; - media::mojom::AudioOutputStreamClientPtr client_ptr_; - mojo::Binding<media::mojom::AudioOutputStreamClient> client_binding_; + mojom::AudioOutputStreamRequest pending_stream_request_; std::unique_ptr<MojoAudioOutputStream> impl_; }; TEST_F(MojoAudioOutputStreamTest, NoDelegate_SignalsError) { - bool deleter_called = false; - EXPECT_CALL(client_, OnError()).Times(1); mojom::AudioOutputStreamPtr stream_ptr; MojoAudioOutputStream stream( - mojo::MakeRequest(&stream_ptr), std::move(client_ptr_), base::BindOnce(&CreateNoDelegate), base::BindOnce(&NotCalled), - base::BindOnce([](bool* p) { *p = true; }, &deleter_called)); - EXPECT_FALSE(deleter_called) - << "Stream shouldn't call the deleter from its constructor."; + base::BindOnce(&MockDeleter::Finished, base::Unretained(&deleter_))); + EXPECT_CALL(deleter_, Finished(true)); base::RunLoop().RunUntilIdle(); - EXPECT_TRUE(deleter_called); } TEST_F(MojoAudioOutputStreamTest, Play_Plays) { AudioOutputStreamPtr audio_output_ptr = CreateAudioOutput(); + + EXPECT_CALL(client_, GotNotification()); EXPECT_CALL(*delegate_, OnPlayStream()); + delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_), + std::move(foreign_socket_)); audio_output_ptr->Play(); base::RunLoop().RunUntilIdle(); } TEST_F(MojoAudioOutputStreamTest, Pause_Pauses) { AudioOutputStreamPtr audio_output_ptr = CreateAudioOutput(); + + EXPECT_CALL(client_, GotNotification()); EXPECT_CALL(*delegate_, OnPauseStream()); + delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_), + std::move(foreign_socket_)); audio_output_ptr->Pause(); base::RunLoop().RunUntilIdle(); } TEST_F(MojoAudioOutputStreamTest, SetVolume_SetsVolume) { AudioOutputStreamPtr audio_output_ptr = CreateAudioOutput(); + + EXPECT_CALL(client_, GotNotification()); EXPECT_CALL(*delegate_, OnSetVolume(kNewVolume)); + delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_), + std::move(foreign_socket_)); audio_output_ptr->SetVolume(kNewVolume); base::RunLoop().RunUntilIdle(); } @@ -230,7 +243,7 @@ TEST_F(MojoAudioOutputStreamTest, DestructWithCallPending_Safe) { ASSERT_NE(nullptr, delegate_event_handler_); foreign_socket_->ExpectOwnershipTransfer(); - delegate_event_handler_->OnStreamCreated(kStreamId, &mem_, + delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_), std::move(foreign_socket_)); audio_output_ptr->Play(); impl_.reset(); @@ -245,7 +258,7 @@ TEST_F(MojoAudioOutputStreamTest, Created_NotifiesClient) { ASSERT_NE(nullptr, delegate_event_handler_); foreign_socket_->ExpectOwnershipTransfer(); - delegate_event_handler_->OnStreamCreated(kStreamId, &mem_, + delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_), std::move(foreign_socket_)); base::RunLoop().RunUntilIdle(); @@ -253,9 +266,11 @@ TEST_F(MojoAudioOutputStreamTest, Created_NotifiesClient) { TEST_F(MojoAudioOutputStreamTest, SetVolumeTooLarge_Error) { AudioOutputStreamPtr audio_output_ptr = CreateAudioOutput(); - EXPECT_CALL(deleter_, Finished()); - EXPECT_CALL(client_, OnError()).Times(1); + EXPECT_CALL(deleter_, Finished(true)); + EXPECT_CALL(client_, GotNotification()); + delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_), + std::move(foreign_socket_)); audio_output_ptr->SetVolume(15); base::RunLoop().RunUntilIdle(); Mock::VerifyAndClear(&deleter_); @@ -263,9 +278,11 @@ TEST_F(MojoAudioOutputStreamTest, SetVolumeTooLarge_Error) { TEST_F(MojoAudioOutputStreamTest, SetVolumeNegative_Error) { AudioOutputStreamPtr audio_output_ptr = CreateAudioOutput(); - EXPECT_CALL(deleter_, Finished()); - EXPECT_CALL(client_, OnError()).Times(1); + EXPECT_CALL(deleter_, Finished(true)); + EXPECT_CALL(client_, GotNotification()); + delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_), + std::move(foreign_socket_)); audio_output_ptr->SetVolume(-0.5); base::RunLoop().RunUntilIdle(); Mock::VerifyAndClear(&deleter_); @@ -273,8 +290,7 @@ TEST_F(MojoAudioOutputStreamTest, SetVolumeNegative_Error) { TEST_F(MojoAudioOutputStreamTest, DelegateErrorBeforeCreated_PropagatesError) { AudioOutputStreamPtr audio_output_ptr = CreateAudioOutput(); - EXPECT_CALL(deleter_, Finished()); - EXPECT_CALL(client_, OnError()).Times(1); + EXPECT_CALL(deleter_, Finished(true)); ASSERT_NE(nullptr, delegate_event_handler_); delegate_event_handler_->OnStreamError(kStreamId); @@ -286,13 +302,12 @@ TEST_F(MojoAudioOutputStreamTest, DelegateErrorBeforeCreated_PropagatesError) { TEST_F(MojoAudioOutputStreamTest, DelegateErrorAfterCreated_PropagatesError) { AudioOutputStreamPtr audio_output_ptr = CreateAudioOutput(); EXPECT_CALL(client_, GotNotification()); - EXPECT_CALL(deleter_, Finished()); - EXPECT_CALL(client_, OnError()).Times(1); + EXPECT_CALL(deleter_, Finished(true)); base::RunLoop().RunUntilIdle(); ASSERT_NE(nullptr, delegate_event_handler_); foreign_socket_->ExpectOwnershipTransfer(); - delegate_event_handler_->OnStreamCreated(kStreamId, &mem_, + delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_), std::move(foreign_socket_)); delegate_event_handler_->OnStreamError(kStreamId); @@ -300,9 +315,14 @@ TEST_F(MojoAudioOutputStreamTest, DelegateErrorAfterCreated_PropagatesError) { Mock::VerifyAndClear(&deleter_); } -TEST_F(MojoAudioOutputStreamTest, RemoteEndGone_Error) { +TEST_F(MojoAudioOutputStreamTest, RemoteEndGone_CallsDeleter) { AudioOutputStreamPtr audio_output_ptr = CreateAudioOutput(); - EXPECT_CALL(deleter_, Finished()); + + EXPECT_CALL(client_, GotNotification()); + EXPECT_CALL(deleter_, Finished(false)); + + delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_), + std::move(foreign_socket_)); audio_output_ptr.reset(); base::RunLoop().RunUntilIdle(); Mock::VerifyAndClear(&deleter_); diff --git a/chromium/media/mojo/services/mojo_cdm_service.cc b/chromium/media/mojo/services/mojo_cdm_service.cc index 5c39b204fe2..31bb3e7fa5e 100644 --- a/chromium/media/mojo/services/mojo_cdm_service.cc +++ b/chromium/media/mojo/services/mojo_cdm_service.cc @@ -166,12 +166,17 @@ void MojoCdmService::OnCdmCreated( // If |cdm| has a decryptor, create the MojoDecryptorService // and pass the connection back to the client. - mojom::DecryptorPtr decryptor_service; + mojom::DecryptorPtr decryptor_ptr; CdmContext* const cdm_context = cdm_->GetCdmContext(); if (cdm_context && cdm_context->GetDecryptor()) { - decryptor_.reset(new MojoDecryptorService( - cdm_context->GetDecryptor(), MakeRequest(&decryptor_service), - base::Bind(&MojoCdmService::OnDecryptorConnectionError, weak_this_))); + // 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_binding_ = std::make_unique<mojo::Binding<mojom::Decryptor>>( + decryptor_.get(), MakeRequest(&decryptor_ptr)); + decryptor_binding_->set_connection_error_handler(base::BindOnce( + &MojoCdmService::OnDecryptorConnectionError, weak_this_)); } // If the |context_| is not null, we should support connecting the |cdm| with @@ -184,7 +189,7 @@ void MojoCdmService::OnCdmCreated( cdm_promise_result->success = true; std::move(callback).Run(std::move(cdm_promise_result), cdm_id, - std::move(decryptor_service)); + std::move(decryptor_ptr)); } void MojoCdmService::OnSessionMessage(const std::string& session_id, diff --git a/chromium/media/mojo/services/mojo_cdm_service.h b/chromium/media/mojo/services/mojo_cdm_service.h index 051d46dd18b..716620b9e6c 100644 --- a/chromium/media/mojo/services/mojo_cdm_service.h +++ b/chromium/media/mojo/services/mojo_cdm_service.h @@ -21,6 +21,7 @@ #include "media/mojo/services/mojo_cdm_promise.h" #include "media/mojo/services/mojo_cdm_service_context.h" #include "media/mojo/services/mojo_decryptor_service.h" +#include "mojo/public/cpp/bindings/binding.h" namespace media { @@ -106,6 +107,7 @@ class MEDIA_MOJO_EXPORT MojoCdmService : public mojom::ContentDecryptionModule { // MojoDecryptorService is passed the Decryptor from |cdm_|, so // |decryptor_| must not outlive |cdm_|. std::unique_ptr<MojoDecryptorService> decryptor_; + std::unique_ptr<mojo::Binding<mojom::Decryptor>> decryptor_binding_; // Set to a valid CDM ID if the |cdm_| is successfully created. int cdm_id_; diff --git a/chromium/media/mojo/services/mojo_cdm_service_context.cc b/chromium/media/mojo/services/mojo_cdm_service_context.cc index f7d62ccd6a0..49e71b5e111 100644 --- a/chromium/media/mojo/services/mojo_cdm_service_context.cc +++ b/chromium/media/mojo/services/mojo_cdm_service_context.cc @@ -38,13 +38,14 @@ class CdmProxyContextRef : public CdmContextRef, public CdmContext { private: // CdmContext implementation. - CdmProxyContext* GetCdmProxyContext() final { + Decryptor* GetDecryptor() final { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return cdm_context_ ? cdm_context_->GetDecryptor() : nullptr; + } - if (!cdm_context_) - return nullptr; - - return cdm_context_->GetCdmProxyContext(); + CdmProxyContext* GetCdmProxyContext() final { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return cdm_context_ ? cdm_context_->GetCdmProxyContext() : nullptr; } base::WeakPtr<CdmContext> cdm_context_; diff --git a/chromium/media/mojo/services/mojo_decryptor_service.cc b/chromium/media/mojo/services/mojo_decryptor_service.cc index 99307ad60fb..8a08b90f305 100644 --- a/chromium/media/mojo/services/mojo_decryptor_service.cc +++ b/chromium/media/mojo/services/mojo_decryptor_service.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/numerics/safe_conversions.h" #include "media/base/audio_decoder_config.h" +#include "media/base/cdm_context.h" #include "media/base/decoder_buffer.h" #include "media/base/decryptor.h" #include "media/base/video_decoder_config.h" @@ -17,6 +18,7 @@ #include "media/mojo/common/mojo_decoder_buffer_converter.h" #include "media/mojo/common/mojo_shared_buffer_video_frame.h" #include "media/mojo/interfaces/demuxer_stream.mojom.h" +#include "media/mojo/services/mojo_cdm_service_context.h" #include "mojo/public/cpp/bindings/strong_binding.h" namespace media { @@ -45,17 +47,40 @@ class FrameResourceReleaserImpl final : public mojom::FrameResourceReleaser { } // namespace +// static +std::unique_ptr<MojoDecryptorService> MojoDecryptorService::Create( + int cdm_id, + MojoCdmServiceContext* mojo_cdm_service_context) { + auto cdm_context_ref = mojo_cdm_service_context->GetCdmContextRef(cdm_id); + if (!cdm_context_ref) { + DVLOG(1) << "CdmContextRef not found for CDM ID: " << cdm_id; + return nullptr; + } + + auto* cdm_context = cdm_context_ref->GetCdmContext(); + DCHECK(cdm_context); + + auto* decryptor = cdm_context->GetDecryptor(); + if (!decryptor) { + DVLOG(1) << "CdmContext does not support Decryptor"; + return nullptr; + } + + return std::make_unique<MojoDecryptorService>(decryptor, + std::move(cdm_context_ref)); +} + MojoDecryptorService::MojoDecryptorService( media::Decryptor* decryptor, - mojo::InterfaceRequest<mojom::Decryptor> request, - const base::Closure& error_handler) - : binding_(this, std::move(request)), - decryptor_(decryptor), + std::unique_ptr<CdmContextRef> cdm_context_ref) + : decryptor_(decryptor), + cdm_context_ref_(std::move(cdm_context_ref)), weak_factory_(this) { DVLOG(1) << __func__; DCHECK(decryptor_); + // |cdm_context_ref_| could be null, in which case the owner of |this| will + // make sure |decryptor_| is always valid. weak_this_ = weak_factory_.GetWeakPtr(); - binding_.set_connection_error_handler(error_handler); } MojoDecryptorService::~MojoDecryptorService() { diff --git a/chromium/media/mojo/services/mojo_decryptor_service.h b/chromium/media/mojo/services/mojo_decryptor_service.h index 879456b1f35..17e82580997 100644 --- a/chromium/media/mojo/services/mojo_decryptor_service.h +++ b/chromium/media/mojo/services/mojo_decryptor_service.h @@ -13,30 +13,34 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" +#include "media/base/cdm_context.h" #include "media/base/decryptor.h" #include "media/mojo/interfaces/decryptor.mojom.h" #include "media/mojo/services/media_mojo_export.h" -#include "mojo/public/cpp/bindings/binding.h" namespace media { class DecoderBuffer; +class MojoCdmServiceContext; class MojoDecoderBufferReader; class MojoDecoderBufferWriter; -// A mojom::Decryptor implementation. This object is owned by the creator, -// and uses a weak binding across the mojo interface. +// A mojom::Decryptor implementation that proxies decryptor calls to a +// media::Decryptor. class MEDIA_MOJO_EXPORT MojoDecryptorService : public mojom::Decryptor { public: using StreamType = media::Decryptor::StreamType; using Status = media::Decryptor::Status; - // Constructs a MojoDecryptorService and binds it to the |request|. - // |error_handler| will be called if a connection error occurs. - // Caller must ensure that |decryptor| outlives |this|. + static std::unique_ptr<MojoDecryptorService> Create( + int cdm_id, + MojoCdmServiceContext* mojo_cdm_service_context); + + // If |cdm_context_ref| is null, caller must ensure that |decryptor| outlives + // |this|. Otherwise, |decryptor| is guaranteed to be valid as long as + // |cdm_context_ref| is held. MojoDecryptorService(media::Decryptor* decryptor, - mojo::InterfaceRequest<mojom::Decryptor> request, - const base::Closure& error_handler); + std::unique_ptr<CdmContextRef> cdm_context_ref); ~MojoDecryptorService() final; @@ -93,9 +97,6 @@ class MEDIA_MOJO_EXPORT MojoDecryptorService : public mojom::Decryptor { // Returns audio/video buffer reader according to the |stream_type|. MojoDecoderBufferReader* GetBufferReader(StreamType stream_type) const; - // A weak binding is used to connect to the MojoDecryptor. - mojo::Binding<mojom::Decryptor> binding_; - // Helper classes to receive encrypted DecoderBuffer from the client. std::unique_ptr<MojoDecoderBufferReader> audio_buffer_reader_; std::unique_ptr<MojoDecoderBufferReader> video_buffer_reader_; @@ -106,6 +107,10 @@ class MEDIA_MOJO_EXPORT MojoDecryptorService : public mojom::Decryptor { media::Decryptor* decryptor_; + // Holds the CdmContextRef to keep the CdmContext alive for the lifetime of + // the |decryptor_|. + std::unique_ptr<CdmContextRef> cdm_context_ref_; + base::WeakPtr<MojoDecryptorService> weak_this_; base::WeakPtrFactory<MojoDecryptorService> weak_factory_; diff --git a/chromium/media/mojo/services/mojo_jpeg_decode_accelerator_service_unittest.cc b/chromium/media/mojo/services/mojo_jpeg_decode_accelerator_service_unittest.cc index 4e7fc0d33f2..d12bf86edf3 100644 --- a/chromium/media/mojo/services/mojo_jpeg_decode_accelerator_service_unittest.cc +++ b/chromium/media/mojo/services/mojo_jpeg_decode_accelerator_service_unittest.cc @@ -78,7 +78,7 @@ TEST_F(MojoJpegDecodeAcceleratorServiceTest, InitializeAndDecode) { kArbitraryBitstreamBufferId, base::SharedMemory::DuplicateHandle(shm.handle()), kInputBufferSizeInBytes); - bitstream_buffer.SetDecryptConfig(DecryptConfig(kKeyId, kIv, subsamples)); + bitstream_buffer.SetDecryptionSettings(kKeyId, kIv, subsamples); jpeg_decoder->Decode( bitstream_buffer, kDummyFrameCodedSize, std::move(output_frame_handle), diff --git a/chromium/media/mojo/services/mojo_jpeg_encode_accelerator_service.cc b/chromium/media/mojo/services/mojo_jpeg_encode_accelerator_service.cc index 25b67848d06..41ec4400eba 100644 --- a/chromium/media/mojo/services/mojo_jpeg_encode_accelerator_service.cc +++ b/chromium/media/mojo/services/mojo_jpeg_encode_accelerator_service.cc @@ -145,8 +145,11 @@ void MojoJpegEncodeAcceleratorService::EncodeWithFD( media::BitstreamBuffer output_buffer(buffer_id, output_shm_handle, output_buffer_size); - media::BitstreamBuffer exif_buffer(buffer_id, exif_shm_handle, - exif_buffer_size); + std::unique_ptr<media::BitstreamBuffer> exif_buffer; + if (exif_buffer_size > 0) { + exif_buffer = std::make_unique<media::BitstreamBuffer>( + buffer_id, exif_shm_handle, exif_buffer_size); + } gfx::Size coded_size(coded_size_width, coded_size_height); if (encode_cb_map_.find(buffer_id) != encode_cb_map_.end()) { @@ -187,7 +190,7 @@ void MojoJpegEncodeAcceleratorService::EncodeWithFD( base::Passed(&input_shm))); DCHECK(accelerator_); - accelerator_->Encode(frame, kJpegQuality, &exif_buffer, output_buffer); + accelerator_->Encode(frame, kJpegQuality, exif_buffer.get(), output_buffer); #else NOTREACHED(); #endif diff --git a/chromium/media/mojo/services/mojo_media_client.cc b/chromium/media/mojo/services/mojo_media_client.cc index 1bdf6e16971..83ef569ff33 100644 --- a/chromium/media/mojo/services/mojo_media_client.cc +++ b/chromium/media/mojo/services/mojo_media_client.cc @@ -6,12 +6,10 @@ #include "base/single_thread_task_runner.h" #include "media/base/audio_decoder.h" -#include "media/base/audio_renderer_sink.h" #include "media/base/cdm_factory.h" #include "media/base/media_log.h" -#include "media/base/renderer_factory.h" +#include "media/base/renderer.h" #include "media/base/video_decoder.h" -#include "media/base/video_renderer_sink.h" #if BUILDFLAG(ENABLE_LIBRARY_CDMS) #include "media/cdm/cdm_proxy.h" @@ -34,25 +32,18 @@ std::unique_ptr<VideoDecoder> MojoMediaClient::CreateVideoDecoder( scoped_refptr<base::SingleThreadTaskRunner> task_runner, MediaLog* media_log, mojom::CommandBufferIdPtr command_buffer_id, - RequestOverlayInfoCB request_overlay_info_cb) { + RequestOverlayInfoCB request_overlay_info_cb, + const gfx::ColorSpace& target_color_space) { return nullptr; } -scoped_refptr<AudioRendererSink> MojoMediaClient::CreateAudioRendererSink( +std::unique_ptr<Renderer> MojoMediaClient::CreateRenderer( + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + MediaLog* media_log, const std::string& audio_device_id) { return nullptr; } -std::unique_ptr<VideoRendererSink> MojoMediaClient::CreateVideoRendererSink( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) { - return nullptr; -} - -std::unique_ptr<RendererFactory> MojoMediaClient::CreateRendererFactory( - MediaLog* media_log) { - return nullptr; -} - std::unique_ptr<CdmFactory> MojoMediaClient::CreateCdmFactory( service_manager::mojom::InterfaceProvider* host_interfaces) { return nullptr; diff --git a/chromium/media/mojo/services/mojo_media_client.h b/chromium/media/mojo/services/mojo_media_client.h index 9e84464a48d..47c8b9712d6 100644 --- a/chromium/media/mojo/services/mojo_media_client.h +++ b/chromium/media/mojo/services/mojo_media_client.h @@ -18,25 +18,27 @@ namespace base { class SingleThreadTaskRunner; -} +} // namespace base + +namespace gfx { +class ColorSpace; +} // namespace gfx namespace service_manager { class Connector; namespace mojom { class InterfaceProvider; -} +} // namespace mojom } // namespace service_manager namespace media { class AudioDecoder; -class AudioRendererSink; class CdmFactory; class CdmProxy; class MediaLog; -class RendererFactory; +class Renderer; class VideoDecoder; -class VideoRendererSink; class MEDIA_MOJO_EXPORT MojoMediaClient { public: @@ -56,22 +58,16 @@ class MEDIA_MOJO_EXPORT MojoMediaClient { scoped_refptr<base::SingleThreadTaskRunner> task_runner, MediaLog* media_log, mojom::CommandBufferIdPtr command_buffer_id, - RequestOverlayInfoCB request_overlay_info_cb); + RequestOverlayInfoCB request_overlay_info_cb, + const gfx::ColorSpace& target_color_space); - // Returns the output sink used for rendering audio on |audio_device_id|. - // May be null if the RendererFactory doesn't need an audio sink. - virtual scoped_refptr<AudioRendererSink> CreateAudioRendererSink( + // Returns the Renderer to be used by MojoRendererService. + // TODO(hubbe): Find out whether we should pass in |target_color_space| here. + virtual std::unique_ptr<Renderer> CreateRenderer( + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + MediaLog* media_log, const std::string& audio_device_id); - // Returns the output sink used for rendering video. - // May be null if the RendererFactory doesn't need a video sink. - virtual std::unique_ptr<VideoRendererSink> CreateVideoRendererSink( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); - - // Returns the RendererFactory to be used by MojoRendererService. - virtual std::unique_ptr<RendererFactory> CreateRendererFactory( - MediaLog* media_log); - // Returns the CdmFactory to be used by MojoCdmService. |host_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. diff --git a/chromium/media/mojo/services/mojo_media_log.cc b/chromium/media/mojo/services/mojo_media_log.cc index f13f13ea963..30941d4bab8 100644 --- a/chromium/media/mojo/services/mojo_media_log.cc +++ b/chromium/media/mojo/services/mojo_media_log.cc @@ -8,9 +8,8 @@ namespace media { -// TODO(sandersd): Do we need to respond to the channel closing? MojoMediaLog::MojoMediaLog( - mojo::AssociatedInterfacePtr<mojom::MediaLog> remote_media_log) + scoped_refptr<mojom::ThreadSafeMediaLogAssociatedPtr> remote_media_log) : remote_media_log_(std::move(remote_media_log)) { DVLOG(1) << __func__; } @@ -22,7 +21,7 @@ MojoMediaLog::~MojoMediaLog() { void MojoMediaLog::AddEvent(std::unique_ptr<MediaLogEvent> event) { DVLOG(1) << __func__; DCHECK(event); - remote_media_log_->AddEvent(*event); + (**remote_media_log_).AddEvent(*event); } } // namespace media diff --git a/chromium/media/mojo/services/mojo_media_log.h b/chromium/media/mojo/services/mojo_media_log.h index bf46c381486..78e526f359a 100644 --- a/chromium/media/mojo/services/mojo_media_log.h +++ b/chromium/media/mojo/services/mojo_media_log.h @@ -8,9 +8,9 @@ #include <memory> #include "base/macros.h" +#include "base/memory/scoped_refptr.h" #include "media/base/media_log.h" #include "media/mojo/interfaces/media_log.mojom.h" -#include "mojo/public/cpp/bindings/associated_interface_ptr.h" namespace media { @@ -18,14 +18,14 @@ class MojoMediaLog final : public MediaLog { public: // TODO(sandersd): Template on Ptr type to support non-associated. explicit MojoMediaLog( - mojo::AssociatedInterfacePtr<mojom::MediaLog> remote_media_log); + scoped_refptr<mojom::ThreadSafeMediaLogAssociatedPtr> remote_media_log); ~MojoMediaLog() final; // MediaLog implementation. void AddEvent(std::unique_ptr<MediaLogEvent> event) override; private: - mojo::AssociatedInterfacePtr<mojom::MediaLog> remote_media_log_; + scoped_refptr<mojom::ThreadSafeMediaLogAssociatedPtr> remote_media_log_; DISALLOW_COPY_AND_ASSIGN(MojoMediaLog); }; diff --git a/chromium/media/mojo/services/mojo_renderer_service.cc b/chromium/media/mojo/services/mojo_renderer_service.cc index 7097a77d07a..f385149ccb6 100644 --- a/chromium/media/mojo/services/mojo_renderer_service.cc +++ b/chromium/media/mojo/services/mojo_renderer_service.cc @@ -9,11 +9,9 @@ #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/optional.h" -#include "media/base/audio_renderer_sink.h" #include "media/base/cdm_context.h" #include "media/base/media_url_demuxer.h" #include "media/base/renderer.h" -#include "media/base/video_renderer_sink.h" #include "media/mojo/common/media_type_converters.h" #include "media/mojo/services/media_resource_shim.h" #include "media/mojo/services/mojo_cdm_service_context.h" @@ -36,14 +34,12 @@ const int kTimeUpdateIntervalMs = 50; // static mojo::StrongBindingPtr<mojom::Renderer> MojoRendererService::Create( MojoCdmServiceContext* mojo_cdm_service_context, - scoped_refptr<AudioRendererSink> audio_sink, - std::unique_ptr<VideoRendererSink> video_sink, std::unique_ptr<media::Renderer> renderer, const InitiateSurfaceRequestCB& initiate_surface_request_cb, mojo::InterfaceRequest<mojom::Renderer> request) { - MojoRendererService* service = new MojoRendererService( - mojo_cdm_service_context, std::move(audio_sink), std::move(video_sink), - std::move(renderer), initiate_surface_request_cb); + MojoRendererService* service = + new MojoRendererService(mojo_cdm_service_context, std::move(renderer), + initiate_surface_request_cb); mojo::StrongBindingPtr<mojom::Renderer> binding = mojo::MakeStrongBinding<mojom::Renderer>(base::WrapUnique(service), @@ -54,27 +50,13 @@ mojo::StrongBindingPtr<mojom::Renderer> MojoRendererService::Create( return binding; } -// static -mojo::StrongBindingPtr<mojom::Renderer> MojoRendererService::Create( - std::unique_ptr<media::Renderer> renderer, - const InitiateSurfaceRequestCB& initiate_surface_request_cb, - mojo::InterfaceRequest<mojom::Renderer> request) { - return MojoRendererService::Create( - nullptr, nullptr, nullptr, std::move(renderer), - initiate_surface_request_cb, std::move(request)); -} - MojoRendererService::MojoRendererService( MojoCdmServiceContext* mojo_cdm_service_context, - scoped_refptr<AudioRendererSink> audio_sink, - std::unique_ptr<VideoRendererSink> video_sink, std::unique_ptr<media::Renderer> renderer, InitiateSurfaceRequestCB initiate_surface_request_cb) : mojo_cdm_service_context_(mojo_cdm_service_context), state_(STATE_UNINITIALIZED), playback_rate_(0), - audio_sink_(std::move(audio_sink)), - video_sink_(std::move(video_sink)), renderer_(std::move(renderer)), initiate_surface_request_cb_(initiate_surface_request_cb), weak_factory_(this) { @@ -281,9 +263,8 @@ void MojoRendererService::OnFlushCompleted(FlushCallback callback) { std::move(callback).Run(); } -void MojoRendererService::OnCdmAttached( - base::OnceCallback<void(bool)> callback, - bool success) { +void MojoRendererService::OnCdmAttached(base::OnceCallback<void(bool)> callback, + bool success) { DVLOG(1) << __func__ << "(" << success << ")"; if (!success) diff --git a/chromium/media/mojo/services/mojo_renderer_service.h b/chromium/media/mojo/services/mojo_renderer_service.h index 8303d772c7b..49a192a55d3 100644 --- a/chromium/media/mojo/services/mojo_renderer_service.h +++ b/chromium/media/mojo/services/mojo_renderer_service.h @@ -25,12 +25,10 @@ namespace media { -class AudioRendererSink; class CdmContextRef; class MediaResourceShim; class MojoCdmServiceContext; class Renderer; -class VideoRendererSink; // A mojom::Renderer implementation that use a media::Renderer to render // media streams. @@ -43,21 +41,6 @@ class MEDIA_MOJO_EXPORT MojoRendererService : public mojom::Renderer, // which is safely accessible via the returned StrongBindingPtr. static mojo::StrongBindingPtr<mojom::Renderer> Create( MojoCdmServiceContext* mojo_cdm_service_context, - scoped_refptr<AudioRendererSink> audio_sink, - std::unique_ptr<VideoRendererSink> video_sink, - std::unique_ptr<media::Renderer> renderer, - const InitiateSurfaceRequestCB& initiate_surface_request_cb, - mojo::InterfaceRequest<mojom::Renderer> request); - - // Helper function to bind MojoRendererService with a StrongBinding, - // which is safely accessible via the returned StrongBindingPtr. - // NOTE: Some media::Renderers don't need Audio/VideoRendererSinks, and don't - // support encrypted content. For example, MediaPlayerRenderer instead uses a - // StreamTextureWrapper, and FlingingRenderer does not need to render any - // video on the local device. This function serves the same purpose as the one - // above, but without forcing classes to define the forward declared - // AudioRendererSink, VideoRendererSink and MojoCdmServiceContext. - static mojo::StrongBindingPtr<mojom::Renderer> Create( std::unique_ptr<media::Renderer> renderer, const InitiateSurfaceRequestCB& initiate_surface_request_cb, mojo::InterfaceRequest<mojom::Renderer> request); @@ -65,8 +48,6 @@ class MEDIA_MOJO_EXPORT MojoRendererService : public mojom::Renderer, // |mojo_cdm_service_context| can be used to find the CDM to support // encrypted media. If null, encrypted media is not supported. MojoRendererService(MojoCdmServiceContext* mojo_cdm_service_context, - scoped_refptr<AudioRendererSink> audio_sink, - std::unique_ptr<VideoRendererSink> video_sink, std::unique_ptr<media::Renderer> renderer, InitiateSurfaceRequestCB initiate_surface_request_cb); @@ -150,14 +131,8 @@ class MEDIA_MOJO_EXPORT MojoRendererService : public mojom::Renderer, // the |renderer_|. std::unique_ptr<CdmContextRef> cdm_context_ref_; - // Audio and Video sinks. - // May be null if underlying |renderer_| does not use them. - scoped_refptr<AudioRendererSink> audio_sink_; - std::unique_ptr<VideoRendererSink> video_sink_; - // Note: Destroy |renderer_| first to avoid access violation into other - // members, e.g. |media_resource_|, |cdm_|, |audio_sink_|, and - // |video_sink_|. + // members, e.g. |media_resource_| and |cdm_|. // Must use "media::" because "Renderer" is ambiguous. std::unique_ptr<media::Renderer> renderer_; diff --git a/chromium/media/mojo/services/mojo_video_decoder_service.cc b/chromium/media/mojo/services/mojo_video_decoder_service.cc index a68e7701ffb..ccfd012bcbc 100644 --- a/chromium/media/mojo/services/mojo_video_decoder_service.cc +++ b/chromium/media/mojo/services/mojo_video_decoder_service.cc @@ -8,6 +8,7 @@ #include "base/bind_helpers.h" #include "base/logging.h" #include "base/macros.h" +#include "base/metrics/histogram_macros.h" #include "base/optional.h" #include "base/threading/thread_task_runner_handle.h" #include "media/base/cdm_context.h" @@ -29,6 +30,13 @@ namespace media { namespace { +// Number of active (Decode() was called at least once) +// MojoVideoDecoderService instances that are alive. +// +// Since MojoVideoDecoderService is constructed only by the MediaFactory, +// this will only ever be accessed from a single thread. +static int32_t g_num_active_mvd_instances = 0; + class StaticSyncTokenClient : public VideoFrame::SyncTokenClient { public: explicit StaticSyncTokenClient(const gpu::SyncToken& sync_token) @@ -66,7 +74,7 @@ class VideoFrameHandleReleaserImpl final // VideoFrame. base::UnguessableToken RegisterVideoFrame(scoped_refptr<VideoFrame> frame) { base::UnguessableToken token = base::UnguessableToken::Create(); - DVLOG(2) << __func__ << " => " << token.ToString(); + DVLOG(3) << __func__ << " => " << token.ToString(); video_frames_[token] = std::move(frame); return token; } @@ -74,7 +82,7 @@ class VideoFrameHandleReleaserImpl final // mojom::MojoVideoFrameHandleReleaser implementation void ReleaseVideoFrame(const base::UnguessableToken& release_token, const gpu::SyncToken& release_sync_token) final { - DVLOG(2) << __func__ << "(" << release_token.ToString() << ")"; + DVLOG(3) << __func__ << "(" << release_token.ToString() << ")"; auto it = video_frames_.find(release_token); if (it == video_frames_.end()) { mojo::ReportBadMessage("Unknown |release_token|."); @@ -98,14 +106,17 @@ MojoVideoDecoderService::MojoVideoDecoderService( : mojo_media_client_(mojo_media_client), mojo_cdm_service_context_(mojo_cdm_service_context), weak_factory_(this) { - DVLOG(3) << __func__; + DVLOG(1) << __func__; DCHECK(mojo_media_client_); DCHECK(mojo_cdm_service_context_); weak_this_ = weak_factory_.GetWeakPtr(); } MojoVideoDecoderService::~MojoVideoDecoderService() { - DVLOG(3) << __func__; + DVLOG(1) << __func__; + + if (is_active_instance_) + g_num_active_mvd_instances--; } void MojoVideoDecoderService::Construct( @@ -113,7 +124,8 @@ void MojoVideoDecoderService::Construct( mojom::MediaLogAssociatedPtrInfo media_log, mojom::VideoFrameHandleReleaserRequest video_frame_handle_releaser, mojo::ScopedDataPipeConsumerHandle decoder_buffer_pipe, - mojom::CommandBufferIdPtr command_buffer_id) { + mojom::CommandBufferIdPtr command_buffer_id, + const gfx::ColorSpace& target_color_space) { DVLOG(1) << __func__; if (decoder_) { @@ -124,9 +136,9 @@ void MojoVideoDecoderService::Construct( client_.Bind(std::move(client)); - mojom::MediaLogAssociatedPtr media_log_ptr; - media_log_ptr.Bind(std::move(media_log)); - media_log_ = std::make_unique<MojoMediaLog>(std::move(media_log_ptr)); + media_log_ = std::make_unique<MojoMediaLog>( + mojom::ThreadSafeMediaLogAssociatedPtr::Create( + std::move(media_log), base::ThreadTaskRunnerHandle::Get())); video_frame_handle_releaser_ = mojo::MakeStrongBinding(std::make_unique<VideoFrameHandleReleaserImpl>(), @@ -139,14 +151,16 @@ void MojoVideoDecoderService::Construct( base::ThreadTaskRunnerHandle::Get(), media_log_.get(), std::move(command_buffer_id), base::Bind(&MojoVideoDecoderService::OnDecoderRequestedOverlayInfo, - weak_this_)); + weak_this_), + target_color_space); } void MojoVideoDecoderService::Initialize(const VideoDecoderConfig& config, bool low_delay, int32_t cdm_id, InitializeCallback callback) { - DVLOG(1) << __func__; + DVLOG(1) << __func__ << " config = " << config.AsHumanReadableString() + << ", cdm_id = " << cdm_id; if (!decoder_) { std::move(callback).Run(false, false, 1); @@ -155,7 +169,7 @@ void MojoVideoDecoderService::Initialize(const VideoDecoderConfig& config, // Get CdmContext from cdm_id if the stream is encrypted. CdmContext* cdm_context = nullptr; - if (config.is_encrypted()) { + if (cdm_id != CdmContext::kInvalidCdmId) { cdm_context_ref_ = mojo_cdm_service_context_->GetCdmContextRef(cdm_id); if (!cdm_context_ref_) { DVLOG(1) << "CdmContextRef not found for CDM id: " << cdm_id; @@ -173,40 +187,46 @@ void MojoVideoDecoderService::Initialize(const VideoDecoderConfig& config, base::Passed(&callback)), base::BindRepeating(&MojoVideoDecoderService::OnDecoderOutput, weak_this_), - media::VideoDecoder::WaitingForDecryptionKeyCB()); + base::NullCallback()); } void MojoVideoDecoderService::Decode(mojom::DecoderBufferPtr buffer, DecodeCallback callback) { - DVLOG(2) << __func__ << " pts=" << buffer->timestamp.InMilliseconds(); + DVLOG(3) << __func__ << " pts=" << buffer->timestamp.InMilliseconds(); if (!decoder_) { std::move(callback).Run(DecodeStatus::DECODE_ERROR); return; } + if (!is_active_instance_) { + is_active_instance_ = true; + g_num_active_mvd_instances++; + UMA_HISTOGRAM_EXACT_LINEAR("Media.MojoVideoDecoder.ActiveInstances", + g_num_active_mvd_instances, 64); + } + mojo_decoder_buffer_reader_->ReadDecoderBuffer( std::move(buffer), base::BindOnce(&MojoVideoDecoderService::OnReaderRead, weak_this_, std::move(callback))); } void MojoVideoDecoderService::Reset(ResetCallback callback) { - DVLOG(1) << __func__; + DVLOG(2) << __func__; if (!decoder_) { std::move(callback).Run(); return; } - // Flush the reader so that pending decodes will be dispatches first. + // Flush the reader so that pending decodes will be dispatched first. mojo_decoder_buffer_reader_->Flush( base::Bind(&MojoVideoDecoderService::OnReaderFlushed, weak_this_, base::Passed(&callback))); } -void MojoVideoDecoderService::OnDecoderInitialized( - InitializeCallback callback, - bool success) { +void MojoVideoDecoderService::OnDecoderInitialized(InitializeCallback callback, + bool success) { DVLOG(1) << __func__; DCHECK(decoder_); @@ -239,21 +259,26 @@ void MojoVideoDecoderService::OnReaderFlushed(ResetCallback callback) { void MojoVideoDecoderService::OnDecoderDecoded(DecodeCallback callback, DecodeStatus status) { - DVLOG(2) << __func__; + DVLOG(3) << __func__; std::move(callback).Run(status); } void MojoVideoDecoderService::OnDecoderReset(ResetCallback callback) { - DVLOG(1) << __func__; + DVLOG(2) << __func__; std::move(callback).Run(); } void MojoVideoDecoderService::OnDecoderOutput( const scoped_refptr<VideoFrame>& frame) { - DVLOG(2) << __func__; + DVLOG(3) << __func__; DCHECK(client_); DCHECK(decoder_); + // All MojoVideoDecoder-based decoders are hardware decoders. If you're the + // first to implement an out-of-process decoder that is not power efficent, + // you can remove this DCHECK. + DCHECK(frame->metadata()->IsTrue(VideoFrameMetadata::POWER_EFFICIENT)); + base::Optional<base::UnguessableToken> release_token; if (frame->HasReleaseMailboxCB() && video_frame_handle_releaser_) { // |video_frame_handle_releaser_| is explicitly constructed with a diff --git a/chromium/media/mojo/services/mojo_video_decoder_service.h b/chromium/media/mojo/services/mojo_video_decoder_service.h index 739fff0b124..05aa82518f6 100644 --- a/chromium/media/mojo/services/mojo_video_decoder_service.h +++ b/chromium/media/mojo/services/mojo_video_decoder_service.h @@ -46,7 +46,8 @@ class MEDIA_MOJO_EXPORT MojoVideoDecoderService final mojom::MediaLogAssociatedPtrInfo media_log, mojom::VideoFrameHandleReleaserRequest video_frame_handle_releaser, mojo::ScopedDataPipeConsumerHandle decoder_buffer_pipe, - mojom::CommandBufferIdPtr command_buffer_id) final; + mojom::CommandBufferIdPtr command_buffer_id, + const gfx::ColorSpace& target_color_space) final; void Initialize(const VideoDecoderConfig& config, bool low_delay, int32_t cdm_id, @@ -75,6 +76,9 @@ class MEDIA_MOJO_EXPORT MojoVideoDecoderService final bool restart_for_transitions, const ProvideOverlayInfoCB& provide_overlay_info_cb); + // Whether this instance is active (Decode() was called at least once). + bool is_active_instance_ = false; + // Decoder factory. MojoMediaClient* mojo_media_client_; diff --git a/chromium/media/mojo/services/test_mojo_media_client.cc b/chromium/media/mojo/services/test_mojo_media_client.cc index d5def90093f..6c3ce3d4427 100644 --- a/chromium/media/mojo/services/test_mojo_media_client.cc +++ b/chromium/media/mojo/services/test_mojo_media_client.cc @@ -6,7 +6,6 @@ #include <memory> -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/threading/thread_task_runner_handle.h" #include "media/audio/audio_device_description.h" @@ -19,9 +18,16 @@ #include "media/base/null_video_sink.h" #include "media/base/renderer_factory.h" #include "media/cdm/default_cdm_factory.h" +#include "media/renderers/default_decoder_factory.h" #include "media/renderers/default_renderer_factory.h" #include "media/video/gpu_video_accelerator_factories.h" +#if BUILDFLAG(ENABLE_LIBRARY_CDMS) +#include "media/cdm/cdm_paths.h" // nogncheck +#include "media/cdm/cdm_proxy.h" // nogncheck +#include "media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.h" // nogncheck +#endif + namespace media { TestMojoMediaClient::TestMojoMediaClient() = default; @@ -50,23 +56,40 @@ void TestMojoMediaClient::Initialize( } } -scoped_refptr<AudioRendererSink> TestMojoMediaClient::CreateAudioRendererSink( +std::unique_ptr<Renderer> TestMojoMediaClient::CreateRenderer( + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + MediaLog* media_log, const std::string& /* audio_device_id */) { - return new AudioOutputStreamSink(); -} + // If called the first time, do one time initialization. + if (!decoder_factory_) { + decoder_factory_.reset(new media::DefaultDecoderFactory(nullptr)); + } -std::unique_ptr<VideoRendererSink> TestMojoMediaClient::CreateVideoRendererSink( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) { - return std::make_unique<NullVideoSink>( + if (!renderer_factory_) { + renderer_factory_ = std::make_unique<DefaultRendererFactory>( + media_log, decoder_factory_.get(), + DefaultRendererFactory::GetGpuFactoriesCB()); + } + + // We cannot share AudioOutputStreamSink or NullVideoSink among different + // RendererImpls. Thus create one for each Renderer creation. + auto audio_sink = base::MakeRefCounted<AudioOutputStreamSink>(); + auto video_sink = std::make_unique<NullVideoSink>( false, base::TimeDelta::FromSecondsD(1.0 / 60), NullVideoSink::NewFrameCB(), task_runner); -} + auto* video_sink_ptr = video_sink.get(); -std::unique_ptr<RendererFactory> TestMojoMediaClient::CreateRendererFactory( - MediaLog* media_log) { - return std::make_unique<DefaultRendererFactory>( - media_log, nullptr, DefaultRendererFactory::GetGpuFactoriesCB()); -} + // Hold created sinks since DefaultRendererFactory only takes raw pointers to + // the sinks. We are not cleaning up them even after a created Renderer is + // destroyed. But this is fine since this class is only used for tests. + audio_sinks_.push_back(audio_sink); + video_sinks_.push_back(std::move(video_sink)); + + return renderer_factory_->CreateRenderer( + task_runner, task_runner, audio_sink.get(), video_sink_ptr, + RequestOverlayInfoCB(), gfx::ColorSpace()); + +} // namespace media std::unique_ptr<CdmFactory> TestMojoMediaClient::CreateCdmFactory( service_manager::mojom::InterfaceProvider* /* host_interfaces */) { @@ -74,4 +97,15 @@ std::unique_ptr<CdmFactory> TestMojoMediaClient::CreateCdmFactory( return std::make_unique<DefaultCdmFactory>(); } +#if BUILDFLAG(ENABLE_LIBRARY_CDMS) +std::unique_ptr<CdmProxy> TestMojoMediaClient::CreateCdmProxy( + const std::string& cdm_guid) { + DVLOG(1) << __func__ << ": cdm_guid = " << cdm_guid; + if (cdm_guid == kClearKeyCdmGuid) + return std::make_unique<ClearKeyCdmProxy>(); + + return nullptr; +} +#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) + } // namespace media diff --git a/chromium/media/mojo/services/test_mojo_media_client.h b/chromium/media/mojo/services/test_mojo_media_client.h index f593ed8ded2..d5adbeb790e 100644 --- a/chromium/media/mojo/services/test_mojo_media_client.h +++ b/chromium/media/mojo/services/test_mojo_media_client.h @@ -6,24 +6,22 @@ #define MEDIA_MOJO_SERVICES_TEST_MOJO_MEDIA_CLIENT_H_ #include <memory> +#include <vector> #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "media/media_buildflags.h" #include "media/mojo/services/mojo_media_client.h" -namespace base { -class SingleThreadTaskRunner; -} - namespace media { class AudioManager; class AudioRendererSink; -class MediaLog; +class DecoderFactory; class RendererFactory; class VideoRendererSink; -// Default MojoMediaClient for MediaService. +// Test MojoMediaClient for MediaService. class TestMojoMediaClient : public MojoMediaClient { public: TestMojoMediaClient(); @@ -31,17 +29,22 @@ class TestMojoMediaClient : public MojoMediaClient { // MojoMediaClient implementation. void Initialize(service_manager::Connector* connector) final; - scoped_refptr<AudioRendererSink> CreateAudioRendererSink( + std::unique_ptr<Renderer> CreateRenderer( + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + MediaLog* media_log, const std::string& audio_device_id) final; - std::unique_ptr<VideoRendererSink> CreateVideoRendererSink( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) final; - std::unique_ptr<RendererFactory> CreateRendererFactory( - MediaLog* media_log) final; std::unique_ptr<CdmFactory> CreateCdmFactory( service_manager::mojom::InterfaceProvider* /* host_interfaces */) final; +#if BUILDFLAG(ENABLE_LIBRARY_CDMS) + std::unique_ptr<CdmProxy> CreateCdmProxy(const std::string& cdm_guid) final; +#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) private: std::unique_ptr<AudioManager> audio_manager_; + std::unique_ptr<DecoderFactory> decoder_factory_; + std::unique_ptr<RendererFactory> renderer_factory_; + std::vector<scoped_refptr<AudioRendererSink>> audio_sinks_; + std::vector<std::unique_ptr<VideoRendererSink>> video_sinks_; DISALLOW_COPY_AND_ASSIGN(TestMojoMediaClient); }; diff --git a/chromium/media/mojo/services/watch_time_recorder.cc b/chromium/media/mojo/services/watch_time_recorder.cc index 80816bd8c15..e34b0e3aeb9 100644 --- a/chromium/media/mojo/services/watch_time_recorder.cc +++ b/chromium/media/mojo/services/watch_time_recorder.cc @@ -85,74 +85,6 @@ static VideoDecoderName ConvertVideoDecoderNameToEnum(const std::string& name) { return VideoDecoderName::kUnknown; } -static bool ShouldReportToUma(WatchTimeKey key) { - switch (key) { - // These keys are not currently reported to UMA, but are used for UKM metric - // calculations. To report them in the future just add the keys to report to - // the lower list and add histograms.xml entries for them. - case WatchTimeKey::kVideoAll: - case WatchTimeKey::kVideoMse: - case WatchTimeKey::kVideoEme: - case WatchTimeKey::kVideoSrc: - case WatchTimeKey::kVideoBattery: - case WatchTimeKey::kVideoAc: - case WatchTimeKey::kVideoDisplayFullscreen: - case WatchTimeKey::kVideoDisplayInline: - case WatchTimeKey::kVideoDisplayPictureInPicture: - case WatchTimeKey::kVideoEmbeddedExperience: - case WatchTimeKey::kVideoNativeControlsOn: - case WatchTimeKey::kVideoNativeControlsOff: - case WatchTimeKey::kVideoBackgroundAll: - case WatchTimeKey::kVideoBackgroundMse: - case WatchTimeKey::kVideoBackgroundEme: - case WatchTimeKey::kVideoBackgroundSrc: - case WatchTimeKey::kVideoBackgroundBattery: - case WatchTimeKey::kVideoBackgroundAc: - case WatchTimeKey::kVideoBackgroundEmbeddedExperience: - return false; - - case WatchTimeKey::kAudioAll: - case WatchTimeKey::kAudioMse: - case WatchTimeKey::kAudioEme: - case WatchTimeKey::kAudioSrc: - case WatchTimeKey::kAudioBattery: - case WatchTimeKey::kAudioAc: - case WatchTimeKey::kAudioEmbeddedExperience: - case WatchTimeKey::kAudioNativeControlsOn: - case WatchTimeKey::kAudioNativeControlsOff: - case WatchTimeKey::kAudioBackgroundAll: - case WatchTimeKey::kAudioBackgroundMse: - case WatchTimeKey::kAudioBackgroundEme: - case WatchTimeKey::kAudioBackgroundSrc: - case WatchTimeKey::kAudioBackgroundBattery: - case WatchTimeKey::kAudioBackgroundAc: - case WatchTimeKey::kAudioBackgroundEmbeddedExperience: - case WatchTimeKey::kAudioVideoAll: - case WatchTimeKey::kAudioVideoMse: - case WatchTimeKey::kAudioVideoEme: - case WatchTimeKey::kAudioVideoSrc: - case WatchTimeKey::kAudioVideoBattery: - case WatchTimeKey::kAudioVideoAc: - case WatchTimeKey::kAudioVideoDisplayFullscreen: - case WatchTimeKey::kAudioVideoDisplayInline: - case WatchTimeKey::kAudioVideoDisplayPictureInPicture: - case WatchTimeKey::kAudioVideoEmbeddedExperience: - case WatchTimeKey::kAudioVideoNativeControlsOn: - case WatchTimeKey::kAudioVideoNativeControlsOff: - case WatchTimeKey::kAudioVideoBackgroundAll: - case WatchTimeKey::kAudioVideoBackgroundMse: - case WatchTimeKey::kAudioVideoBackgroundEme: - case WatchTimeKey::kAudioVideoBackgroundSrc: - case WatchTimeKey::kAudioVideoBackgroundBattery: - case WatchTimeKey::kAudioVideoBackgroundAc: - case WatchTimeKey::kAudioVideoBackgroundEmbeddedExperience: - return true; - } - - NOTREACHED(); - return false; -} - static void RecordWatchTimeInternal( base::StringPiece key, base::TimeDelta value, @@ -236,9 +168,10 @@ void WatchTimeRecorder::FinalizeWatchTime( // Report only certain keys to UMA and only if they have at met the minimum // watch time requirement. Otherwise, for SRC/MSE/EME keys, log them to the // discard metric. - if (ShouldReportToUma(kv.first)) { + base::StringPiece key_str = ConvertWatchTimeKeyToStringForUma(kv.first); + if (!key_str.empty()) { if (kv.second >= kMinimumElapsedWatchTime) { - RecordWatchTimeInternal(WatchTimeKeyToString(kv.first), kv.second); + RecordWatchTimeInternal(key_str, kv.second); } else if (kv.second > base::TimeDelta()) { auto it = std::find_if(extended_metrics_keys_.begin(), extended_metrics_keys_.end(), @@ -264,7 +197,7 @@ void WatchTimeRecorder::FinalizeWatchTime( // Check for watch times entries that have corresponding MTBR entries and // report the MTBR value using watch_time / |underflow_count|. Do this only // for foreground reporters since we only have UMA keys for foreground. - if (!properties_->is_background) { + if (!properties_->is_background && !properties_->is_muted) { for (auto& mapping : extended_metrics_keys_) { auto it = watch_time_info_.find(mapping.watch_time_key); if (it == watch_time_info_.end() || it->second < kMinimumElapsedWatchTime) @@ -308,11 +241,6 @@ void WatchTimeRecorder::UpdateUnderflowCount(int32_t count) { underflow_count_ = count; } -// static -bool WatchTimeRecorder::ShouldReportUmaForTesting(WatchTimeKey key) { - return ShouldReportToUma(key); -} - void WatchTimeRecorder::RecordUkmPlaybackData() { // UKM may be unavailable in content_shell or other non-chrome/ builds; it // may also be unavailable if browser shutdown has started; so this may be a @@ -329,6 +257,7 @@ void WatchTimeRecorder::RecordUkmPlaybackData() { builder.SetIsTopFrame(is_top_frame_); builder.SetIsBackground(properties_->is_background); + builder.SetIsMuted(properties_->is_muted); builder.SetPlayerID(player_id_); bool recorded_all_metric = false; @@ -336,6 +265,7 @@ void WatchTimeRecorder::RecordUkmPlaybackData() { if (kv.first == WatchTimeKey::kAudioAll || kv.first == WatchTimeKey::kAudioBackgroundAll || kv.first == WatchTimeKey::kAudioVideoAll || + kv.first == WatchTimeKey::kAudioVideoMutedAll || kv.first == WatchTimeKey::kAudioVideoBackgroundAll || kv.first == WatchTimeKey::kVideoAll || kv.first == WatchTimeKey::kVideoBackgroundAll) { @@ -351,6 +281,7 @@ void WatchTimeRecorder::RecordUkmPlaybackData() { } else if (kv.first == WatchTimeKey::kAudioAc || kv.first == WatchTimeKey::kAudioBackgroundAc || kv.first == WatchTimeKey::kAudioVideoAc || + kv.first == WatchTimeKey::kAudioVideoMutedAc || kv.first == WatchTimeKey::kAudioVideoBackgroundAc || kv.first == WatchTimeKey::kVideoAc || kv.first == WatchTimeKey::kVideoBackgroundAc) { @@ -358,25 +289,32 @@ void WatchTimeRecorder::RecordUkmPlaybackData() { } else if (kv.first == WatchTimeKey::kAudioBattery || kv.first == WatchTimeKey::kAudioBackgroundBattery || kv.first == WatchTimeKey::kAudioVideoBattery || + kv.first == WatchTimeKey::kAudioVideoMutedBattery || kv.first == WatchTimeKey::kAudioVideoBackgroundBattery || kv.first == WatchTimeKey::kVideoBattery || kv.first == WatchTimeKey::kVideoBackgroundBattery) { builder.SetWatchTime_Battery(kv.second.InMilliseconds()); } else if (kv.first == WatchTimeKey::kAudioNativeControlsOn || kv.first == WatchTimeKey::kAudioVideoNativeControlsOn || + kv.first == WatchTimeKey::kAudioVideoMutedNativeControlsOn || kv.first == WatchTimeKey::kVideoNativeControlsOn) { builder.SetWatchTime_NativeControlsOn(kv.second.InMilliseconds()); } else if (kv.first == WatchTimeKey::kAudioNativeControlsOff || kv.first == WatchTimeKey::kAudioVideoNativeControlsOff || + kv.first == WatchTimeKey::kAudioVideoMutedNativeControlsOff || kv.first == WatchTimeKey::kVideoNativeControlsOff) { builder.SetWatchTime_NativeControlsOff(kv.second.InMilliseconds()); } else if (kv.first == WatchTimeKey::kAudioVideoDisplayFullscreen || + kv.first == WatchTimeKey::kAudioVideoMutedDisplayFullscreen || kv.first == WatchTimeKey::kVideoDisplayFullscreen) { builder.SetWatchTime_DisplayFullscreen(kv.second.InMilliseconds()); } else if (kv.first == WatchTimeKey::kAudioVideoDisplayInline || + kv.first == WatchTimeKey::kAudioVideoMutedDisplayInline || kv.first == WatchTimeKey::kVideoDisplayInline) { builder.SetWatchTime_DisplayInline(kv.second.InMilliseconds()); } else if (kv.first == WatchTimeKey::kAudioVideoDisplayPictureInPicture || + kv.first == + WatchTimeKey::kAudioVideoMutedDisplayPictureInPicture || kv.first == WatchTimeKey::kVideoDisplayPictureInPicture) { builder.SetWatchTime_DisplayPictureInPicture(kv.second.InMilliseconds()); } diff --git a/chromium/media/mojo/services/watch_time_recorder.h b/chromium/media/mojo/services/watch_time_recorder.h index ccc4330299e..82a7b670594 100644 --- a/chromium/media/mojo/services/watch_time_recorder.h +++ b/chromium/media/mojo/services/watch_time_recorder.h @@ -40,9 +40,6 @@ class MEDIA_MOJO_EXPORT WatchTimeRecorder : public mojom::WatchTimeRecorder { void UpdateUnderflowCount(int32_t count) override; - // Test helper method for determining if keys are not reported to UMA. - static bool ShouldReportUmaForTesting(WatchTimeKey key); - private: // Records a UKM event based on |aggregate_watch_time_info_|; only recorded // with a complete finalize (destruction or empty FinalizeWatchTime call). diff --git a/chromium/media/mojo/services/watch_time_recorder_unittest.cc b/chromium/media/mojo/services/watch_time_recorder_unittest.cc index def3bacb95e..a9e2e0a428f 100644 --- a/chromium/media/mojo/services/watch_time_recorder_unittest.cc +++ b/chromium/media/mojo/services/watch_time_recorder_unittest.cc @@ -11,6 +11,7 @@ #include "base/hash.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "base/strings/string_number_conversions.h" #include "base/test/histogram_tester.h" #include "base/test/test_message_loop.h" #include "base/threading/thread_task_runner_handle.h" @@ -69,7 +70,7 @@ class WatchTimeRecorderTest : public testing::Test { bool is_encrypted) { Initialize(mojom::PlaybackProperties::New( kUnknownAudioCodec, kUnknownVideoCodec, has_audio, has_video, false, - is_mse, is_encrypted, false, gfx::Size(800, 600))); + false, is_mse, is_encrypted, false, gfx::Size(800, 600))); } void ExpectWatchTime(const std::vector<base::StringPiece>& keys, @@ -77,7 +78,9 @@ class WatchTimeRecorderTest : public testing::Test { for (int i = 0; i <= static_cast<int>(WatchTimeKey::kWatchTimeKeyMax); ++i) { const base::StringPiece test_key = - WatchTimeKeyToString(static_cast<WatchTimeKey>(i)); + ConvertWatchTimeKeyToStringForUma(static_cast<WatchTimeKey>(i)); + if (test_key.empty()) + continue; auto it = std::find(keys.begin(), keys.end(), test_key); if (it == keys.end()) { histogram_tester_->ExpectTotalCount(test_key.as_string(), 0); @@ -161,8 +164,13 @@ TEST_F(WatchTimeRecorderTest, TestBasicReporting) { for (int i = 0; i <= static_cast<int>(WatchTimeKey::kWatchTimeKeyMax); ++i) { const WatchTimeKey key = static_cast<WatchTimeKey>(i); - SCOPED_TRACE(WatchTimeKeyToString(key)); + auto key_str = ConvertWatchTimeKeyToStringForUma(key); + SCOPED_TRACE(key_str.empty() ? base::NumberToString(i) + : key_str.as_string()); + + // Values for |is_background| and |is_muted| don't matter in this test since + // they don't prevent the muted or background keys from being recorded. Initialize(true, false, true, true); wtr_->RecordWatchTime(WatchTimeKey::kWatchTimeKeyMax, kWatchTime1); wtr_->RecordWatchTime(key, kWatchTime1); @@ -176,10 +184,8 @@ TEST_F(WatchTimeRecorderTest, TestBasicReporting) { wtr_->FinalizeWatchTime({key}); base::RunLoop().RunUntilIdle(); - if (WatchTimeRecorder::ShouldReportUmaForTesting(key)) { - const base::StringPiece key_str = WatchTimeKeyToString(key); + if (!key_str.empty()) ExpectWatchTime({key_str}, kWatchTime2); - } // These keys are only reported for a full finalize. ExpectMtbrTime({}, base::TimeDelta()); @@ -199,6 +205,7 @@ TEST_F(WatchTimeRecorderTest, TestBasicReporting) { case WatchTimeKey::kAudioBackgroundAll: case WatchTimeKey::kAudioVideoAll: case WatchTimeKey::kAudioVideoBackgroundAll: + case WatchTimeKey::kAudioVideoMutedAll: case WatchTimeKey::kVideoAll: case WatchTimeKey::kVideoBackgroundAll: ExpectUkmWatchTime({UkmEntry::kWatchTimeName}, kWatchTime2); @@ -217,6 +224,10 @@ TEST_F(WatchTimeRecorderTest, TestBasicReporting) { case WatchTimeKey::kAudioVideoEme: case WatchTimeKey::kAudioVideoSrc: case WatchTimeKey::kAudioVideoEmbeddedExperience: + case WatchTimeKey::kAudioVideoMutedMse: + case WatchTimeKey::kAudioVideoMutedEme: + case WatchTimeKey::kAudioVideoMutedSrc: + case WatchTimeKey::kAudioVideoMutedEmbeddedExperience: case WatchTimeKey::kAudioVideoBackgroundMse: case WatchTimeKey::kAudioVideoBackgroundEme: case WatchTimeKey::kAudioVideoBackgroundSrc: @@ -236,6 +247,7 @@ TEST_F(WatchTimeRecorderTest, TestBasicReporting) { case WatchTimeKey::kAudioBattery: case WatchTimeKey::kAudioBackgroundBattery: case WatchTimeKey::kAudioVideoBattery: + case WatchTimeKey::kAudioVideoMutedBattery: case WatchTimeKey::kAudioVideoBackgroundBattery: case WatchTimeKey::kVideoBattery: case WatchTimeKey::kVideoBackgroundBattery: @@ -247,24 +259,28 @@ TEST_F(WatchTimeRecorderTest, TestBasicReporting) { case WatchTimeKey::kAudioBackgroundAc: case WatchTimeKey::kAudioVideoAc: case WatchTimeKey::kAudioVideoBackgroundAc: + case WatchTimeKey::kAudioVideoMutedAc: case WatchTimeKey::kVideoAc: case WatchTimeKey::kVideoBackgroundAc: ExpectUkmWatchTime({UkmEntry::kWatchTime_ACName}, kWatchTime2); break; case WatchTimeKey::kAudioVideoDisplayFullscreen: + case WatchTimeKey::kAudioVideoMutedDisplayFullscreen: case WatchTimeKey::kVideoDisplayFullscreen: ExpectUkmWatchTime({UkmEntry::kWatchTime_DisplayFullscreenName}, kWatchTime2); break; case WatchTimeKey::kAudioVideoDisplayInline: + case WatchTimeKey::kAudioVideoMutedDisplayInline: case WatchTimeKey::kVideoDisplayInline: ExpectUkmWatchTime({UkmEntry::kWatchTime_DisplayInlineName}, kWatchTime2); break; case WatchTimeKey::kAudioVideoDisplayPictureInPicture: + case WatchTimeKey::kAudioVideoMutedDisplayPictureInPicture: case WatchTimeKey::kVideoDisplayPictureInPicture: ExpectUkmWatchTime({UkmEntry::kWatchTime_DisplayPictureInPictureName}, kWatchTime2); @@ -272,6 +288,7 @@ TEST_F(WatchTimeRecorderTest, TestBasicReporting) { case WatchTimeKey::kAudioNativeControlsOn: case WatchTimeKey::kAudioVideoNativeControlsOn: + case WatchTimeKey::kAudioVideoMutedNativeControlsOn: case WatchTimeKey::kVideoNativeControlsOn: ExpectUkmWatchTime({UkmEntry::kWatchTime_NativeControlsOnName}, kWatchTime2); @@ -279,6 +296,7 @@ TEST_F(WatchTimeRecorderTest, TestBasicReporting) { case WatchTimeKey::kAudioNativeControlsOff: case WatchTimeKey::kAudioVideoNativeControlsOff: + case WatchTimeKey::kAudioVideoMutedNativeControlsOff: case WatchTimeKey::kVideoNativeControlsOff: ExpectUkmWatchTime({UkmEntry::kWatchTime_NativeControlsOffName}, kWatchTime2); @@ -365,9 +383,9 @@ TEST_F(WatchTimeRecorderTest, TestDiscardMetrics) { EXPECT_TRUE(test_recorder_->EntryHasMetric(entry, name)); TEST_F(WatchTimeRecorderTest, TestFinalizeNoDuplication) { - mojom::PlaybackPropertiesPtr properties = - mojom::PlaybackProperties::New(kCodecAAC, kCodecH264, true, true, false, - false, false, false, gfx::Size(800, 600)); + mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New( + kCodecAAC, kCodecH264, true, true, false, false, false, false, false, + gfx::Size(800, 600)); Initialize(properties.Clone()); // Verify that UKM is reported along with the watch time. @@ -400,6 +418,7 @@ TEST_F(WatchTimeRecorderTest, TestFinalizeNoDuplication) { test_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestOrigin)); EXPECT_UKM(UkmEntry::kIsBackgroundName, properties->is_background); + EXPECT_UKM(UkmEntry::kIsMutedName, properties->is_muted); EXPECT_UKM(UkmEntry::kAudioCodecName, properties->audio_codec); EXPECT_UKM(UkmEntry::kVideoCodecName, properties->video_codec); EXPECT_UKM(UkmEntry::kHasAudioName, properties->has_audio); @@ -430,9 +449,9 @@ TEST_F(WatchTimeRecorderTest, TestFinalizeNoDuplication) { } TEST_F(WatchTimeRecorderTest, FinalizeWithoutWatchTime) { - mojom::PlaybackPropertiesPtr properties = - mojom::PlaybackProperties::New(kCodecAAC, kCodecH264, true, true, false, - false, false, false, gfx::Size(800, 600)); + mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New( + kCodecAAC, kCodecH264, true, true, false, false, false, false, false, + gfx::Size(800, 600)); Initialize(properties.Clone()); // Finalize everything. UKM is only recorded at destruction, so this should do @@ -462,6 +481,7 @@ TEST_F(WatchTimeRecorderTest, FinalizeWithoutWatchTime) { test_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestOrigin)); EXPECT_UKM(UkmEntry::kIsBackgroundName, properties->is_background); + EXPECT_UKM(UkmEntry::kIsMutedName, properties->is_muted); EXPECT_UKM(UkmEntry::kAudioCodecName, properties->audio_codec); EXPECT_UKM(UkmEntry::kVideoCodecName, properties->video_codec); EXPECT_UKM(UkmEntry::kHasAudioName, properties->has_audio); @@ -492,9 +512,9 @@ TEST_F(WatchTimeRecorderTest, FinalizeWithoutWatchTime) { } TEST_F(WatchTimeRecorderTest, BasicUkmAudioVideo) { - mojom::PlaybackPropertiesPtr properties = - mojom::PlaybackProperties::New(kCodecAAC, kCodecH264, true, true, false, - false, false, false, gfx::Size(800, 600)); + mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New( + kCodecAAC, kCodecH264, true, true, false, false, false, false, false, + gfx::Size(800, 600)); Initialize(properties.Clone()); constexpr base::TimeDelta kWatchTime = base::TimeDelta::FromSeconds(4); @@ -509,6 +529,7 @@ TEST_F(WatchTimeRecorderTest, BasicUkmAudioVideo) { EXPECT_UKM(UkmEntry::kWatchTimeName, kWatchTime.InMilliseconds()); EXPECT_UKM(UkmEntry::kIsBackgroundName, properties->is_background); + EXPECT_UKM(UkmEntry::kIsMutedName, properties->is_muted); EXPECT_UKM(UkmEntry::kAudioCodecName, properties->audio_codec); EXPECT_UKM(UkmEntry::kVideoCodecName, properties->video_codec); EXPECT_UKM(UkmEntry::kHasAudioName, properties->has_audio); @@ -538,9 +559,9 @@ TEST_F(WatchTimeRecorderTest, BasicUkmAudioVideo) { } TEST_F(WatchTimeRecorderTest, BasicUkmAudioVideoWithExtras) { - mojom::PlaybackPropertiesPtr properties = - mojom::PlaybackProperties::New(kCodecOpus, kCodecVP9, true, true, false, - true, true, false, gfx::Size(800, 600)); + mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New( + kCodecOpus, kCodecVP9, true, true, false, false, true, true, false, + gfx::Size(800, 600)); Initialize(properties.Clone()); constexpr base::TimeDelta kWatchTime = base::TimeDelta::FromSeconds(54); @@ -601,6 +622,7 @@ TEST_F(WatchTimeRecorderTest, BasicUkmAudioVideoWithExtras) { EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 5); EXPECT_UKM(UkmEntry::kIsBackgroundName, properties->is_background); + EXPECT_UKM(UkmEntry::kIsMutedName, properties->is_muted); EXPECT_UKM(UkmEntry::kAudioCodecName, properties->audio_codec); EXPECT_UKM(UkmEntry::kVideoCodecName, properties->video_codec); EXPECT_UKM(UkmEntry::kHasAudioName, properties->has_audio); @@ -617,10 +639,10 @@ TEST_F(WatchTimeRecorderTest, BasicUkmAudioVideoWithExtras) { } } -TEST_F(WatchTimeRecorderTest, BasicUkmAudioVideoBackground) { - mojom::PlaybackPropertiesPtr properties = - mojom::PlaybackProperties::New(kCodecAAC, kCodecH264, true, true, true, - false, false, false, gfx::Size(800, 600)); +TEST_F(WatchTimeRecorderTest, BasicUkmAudioVideoBackgroundMuted) { + mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New( + kCodecAAC, kCodecH264, true, true, true, true, false, false, false, + gfx::Size(800, 600)); Initialize(properties.Clone()); constexpr base::TimeDelta kWatchTime = base::TimeDelta::FromSeconds(54); @@ -635,6 +657,7 @@ TEST_F(WatchTimeRecorderTest, BasicUkmAudioVideoBackground) { EXPECT_UKM(UkmEntry::kWatchTimeName, kWatchTime.InMilliseconds()); EXPECT_UKM(UkmEntry::kIsBackgroundName, properties->is_background); + EXPECT_UKM(UkmEntry::kIsMutedName, properties->is_muted); EXPECT_UKM(UkmEntry::kAudioCodecName, properties->audio_codec); EXPECT_UKM(UkmEntry::kVideoCodecName, properties->video_codec); EXPECT_UKM(UkmEntry::kHasAudioName, properties->has_audio); |