blob: 19504d1bf0abe04663b9f12f285d715356654076 [file] [log] [blame]
Bryant Chandler32ca6382023-08-02 21:52:311// Copyright 2023 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Bryant Chandlerd5890afb2023-09-11 19:12:135#include "components/media_effects/media_effects_service.h"
Bryant Chandler32ca6382023-08-02 21:52:316
Piotr Bialecki7a3cfb22024-07-31 04:53:247#include <memory>
Piotr Bialecki55f635a2025-01-10 20:04:228#include <optional>
Piotr Bialecki7a3cfb22024-07-31 04:53:249
10#include "base/containers/span.h"
11#include "base/files/file_path.h"
12#include "base/files/file_util.h"
13#include "base/files/scoped_temp_dir.h"
14#include "base/files/scoped_temp_file.h"
15#include "base/memory/weak_ptr.h"
16#include "base/observer_list.h"
Bryant Chandler32ca6382023-08-02 21:52:3117#include "base/run_loop.h"
18#include "base/test/test_future.h"
Piotr Bialecki7a3cfb22024-07-31 04:53:2419#include "components/media_effects/media_effects_model_provider.h"
Bryant Chandler2c84a6c2023-09-14 14:23:4320#include "components/user_prefs/test/test_browser_context_with_prefs.h"
Bryant Chandler32ca6382023-08-02 21:52:3121#include "content/public/test/browser_task_environment.h"
Piotr Bialeckic2c709d2024-03-27 21:04:0922#include "mojo/public/cpp/bindings/remote.h"
Piotr Bialecki5a6c9112024-09-20 01:51:5023#include "services/video_effects/public/cpp/video_effects_service_host.h"
Piotr Bialeckic2c709d2024-03-27 21:04:0924#include "services/video_effects/public/mojom/video_effects_processor.mojom.h"
25#include "services/video_effects/public/mojom/video_effects_service.mojom-forward.h"
26#include "services/video_effects/test/fake_video_effects_service.h"
Bryant Chandler32ca6382023-08-02 21:52:3127#include "testing/gtest/include/gtest/gtest.h"
28#include "ui/gfx/geometry/insets_f.h"
29
30namespace {
31
32constexpr char kDeviceId[] = "test_device";
33
Bryant Chandler23896e32024-03-06 17:58:0334media::mojom::VideoEffectsConfigurationPtr GetConfigurationSync(
35 mojo::Remote<media::mojom::VideoEffectsManager>& effects_manager) {
36 base::test::TestFuture<media::mojom::VideoEffectsConfigurationPtr>
Bryant Chandler32ca6382023-08-02 21:52:3137 output_configuration;
38 effects_manager->GetConfiguration(output_configuration.GetCallback());
39 return output_configuration.Take();
40}
41
42void SetFramingSync(
Bryant Chandler23896e32024-03-06 17:58:0343 mojo::Remote<media::mojom::VideoEffectsManager>& effects_manager,
Bryant Chandler32ca6382023-08-02 21:52:3144 float framing_padding_ratio) {
Bryant Chandler23896e32024-03-06 17:58:0345 base::test::TestFuture<media::mojom::SetConfigurationResult> result_future;
Bryant Chandler32ca6382023-08-02 21:52:3146 effects_manager->SetConfiguration(
Bryant Chandler23896e32024-03-06 17:58:0347 media::mojom::VideoEffectsConfiguration::New(
Bryant Chandler32ca6382023-08-02 21:52:3148 nullptr, nullptr,
Bryant Chandler23896e32024-03-06 17:58:0349 media::mojom::Framing::New(gfx::InsetsF{framing_padding_ratio})),
Bryant Chandler32ca6382023-08-02 21:52:3150 result_future.GetCallback());
Bryant Chandler23896e32024-03-06 17:58:0351 EXPECT_EQ(media::mojom::SetConfigurationResult::kOk, result_future.Get());
Bryant Chandler32ca6382023-08-02 21:52:3152}
Piotr Bialecki7a3cfb22024-07-31 04:53:2453
54class FakeModelProvider : public MediaEffectsModelProvider {
55 public:
56 ~FakeModelProvider() override = default;
57
58 // MediaEffectsModelProvider:
59 void AddObserver(Observer* observer) override {
60 observers_.AddObserver(observer);
61 if (model_path_) {
Piotr Bialecki55f635a2025-01-10 20:04:2262 observer->OnBackgroundSegmentationModelUpdated(model_path_);
Piotr Bialecki7a3cfb22024-07-31 04:53:2463 }
64 }
65
66 void RemoveObserver(Observer* observer) override {
67 observers_.RemoveObserver(observer);
68 }
69
70 // Sets the model path and notifies observers about it:
Piotr Bialecki55f635a2025-01-10 20:04:2271 void SetModelPath(std::optional<base::FilePath> model_path) {
Piotr Bialecki7a3cfb22024-07-31 04:53:2472 model_path_ = std::move(model_path);
73 for (auto& observer : observers_) {
Piotr Bialecki55f635a2025-01-10 20:04:2274 observer.OnBackgroundSegmentationModelUpdated(model_path_);
Piotr Bialecki7a3cfb22024-07-31 04:53:2475 }
76 }
77
78 base::WeakPtr<FakeModelProvider> weak_ptr() {
79 return weak_ptr_factory_.GetWeakPtr();
80 }
81
82 private:
83 base::ObserverList<Observer> observers_;
84 std::optional<base::FilePath> model_path_;
85
86 // Must be last:
87 base::WeakPtrFactory<FakeModelProvider> weak_ptr_factory_{this};
88};
89
Bryant Chandler32ca6382023-08-02 21:52:3190} // namespace
91
92class MediaEffectsServiceTest : public testing::Test {
Piotr Bialecki7a3cfb22024-07-31 04:53:2493 public:
94 MediaEffectsServiceTest() {
95 auto model_provider = std::make_unique<FakeModelProvider>();
96 model_provider_ = model_provider->weak_ptr();
97
98 service_.emplace(browser_context_.prefs(), std::move(model_provider));
99 }
100
Bryant Chandler32ca6382023-08-02 21:52:31101 protected:
Bryant Chandler32ca6382023-08-02 21:52:31102 content::BrowserTaskEnvironment task_environment_;
Bryant Chandler2c84a6c2023-09-14 14:23:43103 user_prefs::TestBrowserContextWithPrefs browser_context_;
Piotr Bialecki7a3cfb22024-07-31 04:53:24104 std::optional<MediaEffectsService> service_;
105 base::WeakPtr<FakeModelProvider> model_provider_;
Bryant Chandler32ca6382023-08-02 21:52:31106};
107
108TEST_F(MediaEffectsServiceTest, BindVideoEffectsManager) {
Bryant Chandler23896e32024-03-06 17:58:03109 mojo::Remote<media::mojom::VideoEffectsManager> effects_manager;
Piotr Bialecki7a3cfb22024-07-31 04:53:24110 service_->BindVideoEffectsManager(
Bryant Chandler32ca6382023-08-02 21:52:31111 kDeviceId, effects_manager.BindNewPipeAndPassReceiver());
112
113 EXPECT_TRUE(GetConfigurationSync(effects_manager)->framing.is_null());
114
115 const float kFramingPaddingRatio = 0.2;
116 SetFramingSync(effects_manager, kFramingPaddingRatio);
117
118 auto configuration = GetConfigurationSync(effects_manager);
119 EXPECT_EQ(gfx::InsetsF{kFramingPaddingRatio},
120 configuration->framing->padding_ratios);
121}
122
123TEST_F(MediaEffectsServiceTest,
124 BindVideoEffectsManager_TwoRegistrantsWithSameIdConnectToSameManager) {
Bryant Chandler23896e32024-03-06 17:58:03125 mojo::Remote<media::mojom::VideoEffectsManager> effects_manager1;
Piotr Bialecki7a3cfb22024-07-31 04:53:24126 service_->BindVideoEffectsManager(
Bryant Chandler32ca6382023-08-02 21:52:31127 kDeviceId, effects_manager1.BindNewPipeAndPassReceiver());
128
129 const float kFramingPaddingRatio = 0.234;
130 SetFramingSync(effects_manager1, kFramingPaddingRatio);
131
132 EXPECT_EQ(gfx::InsetsF{kFramingPaddingRatio},
133 GetConfigurationSync(effects_manager1)->framing->padding_ratios);
134
Bryant Chandler23896e32024-03-06 17:58:03135 mojo::Remote<media::mojom::VideoEffectsManager> effects_manager2;
Piotr Bialecki7a3cfb22024-07-31 04:53:24136 service_->BindVideoEffectsManager(
Bryant Chandler32ca6382023-08-02 21:52:31137 kDeviceId, effects_manager2.BindNewPipeAndPassReceiver());
138
139 EXPECT_EQ(gfx::InsetsF{kFramingPaddingRatio},
140 GetConfigurationSync(effects_manager2)->framing->padding_ratios);
141}
142
143TEST_F(
144 MediaEffectsServiceTest,
145 BindVideoEffectsManager_TwoRegistrantsWithDifferentIdConnectToDifferentManager) {
Bryant Chandler23896e32024-03-06 17:58:03146 mojo::Remote<media::mojom::VideoEffectsManager> effects_manager1;
Piotr Bialecki7a3cfb22024-07-31 04:53:24147 service_->BindVideoEffectsManager(
Bryant Chandler32ca6382023-08-02 21:52:31148 "test_device_1", effects_manager1.BindNewPipeAndPassReceiver());
149
150 const float kFramingPaddingRatio = 0.234;
151 SetFramingSync(effects_manager1, kFramingPaddingRatio);
152
153 EXPECT_EQ(gfx::InsetsF{kFramingPaddingRatio},
154 GetConfigurationSync(effects_manager1)->framing->padding_ratios);
155
Bryant Chandler23896e32024-03-06 17:58:03156 mojo::Remote<media::mojom::VideoEffectsManager> effects_manager2;
Piotr Bialecki7a3cfb22024-07-31 04:53:24157 service_->BindVideoEffectsManager(
Bryant Chandler32ca6382023-08-02 21:52:31158 "test_device_2", effects_manager2.BindNewPipeAndPassReceiver());
159
160 // Expect `framing` to be unset because it is a separate instance of
161 // `VideoEffectsManager`.
162 auto framing = std::move(GetConfigurationSync(effects_manager2)->framing);
163 EXPECT_TRUE(framing.is_null());
164}
165
166TEST_F(
167 MediaEffectsServiceTest,
168 OnLastReceiverDisconnected_ErasesTheManagerWhenAllReceiversAreDisconnected) {
Piotr Bialeckic2c709d2024-03-27 21:04:09169 mojo::Remote<video_effects::mojom::VideoEffectsService> service;
170 video_effects::FakeVideoEffectsService fake_effects_service(
171 service.BindNewPipeAndPassReceiver());
Piotr Bialecki5a6c9112024-09-20 01:51:50172 auto service_reset =
173 video_effects::SetVideoEffectsServiceRemoteForTesting(&service);
Piotr Bialeckic2c709d2024-03-27 21:04:09174
Bryant Chandler23896e32024-03-06 17:58:03175 mojo::Remote<media::mojom::VideoEffectsManager> effects_manager1;
Piotr Bialecki7a3cfb22024-07-31 04:53:24176 service_->BindVideoEffectsManager(
Bryant Chandler32ca6382023-08-02 21:52:31177 kDeviceId, effects_manager1.BindNewPipeAndPassReceiver());
Bryant Chandler23896e32024-03-06 17:58:03178 mojo::Remote<media::mojom::VideoEffectsManager> effects_manager2;
Piotr Bialecki7a3cfb22024-07-31 04:53:24179 service_->BindVideoEffectsManager(
Bryant Chandler32ca6382023-08-02 21:52:31180 kDeviceId, effects_manager2.BindNewPipeAndPassReceiver());
181
Piotr Bialeckic2c709d2024-03-27 21:04:09182 auto effects_processor_future =
183 fake_effects_service.GetEffectsProcessorCreationFuture();
184
185 mojo::Remote<video_effects::mojom::VideoEffectsProcessor> effects_processor;
Piotr Bialecki7a3cfb22024-07-31 04:53:24186 service_->BindVideoEffectsProcessor(
Piotr Bialeckic2c709d2024-03-27 21:04:09187 kDeviceId, effects_processor.BindNewPipeAndPassReceiver());
188
Bryant Chandler32ca6382023-08-02 21:52:31189 const float kFramingPaddingRatio = 0.234;
190
191 SetFramingSync(effects_manager1, kFramingPaddingRatio);
192
193 EXPECT_EQ(gfx::InsetsF{kFramingPaddingRatio},
194 GetConfigurationSync(effects_manager1)->framing->padding_ratios);
195
196 EXPECT_EQ(gfx::InsetsF{kFramingPaddingRatio},
197 GetConfigurationSync(effects_manager2)->framing->padding_ratios);
198
Piotr Bialeckic2c709d2024-03-27 21:04:09199 // Wait for the fake effects service to create the processor:
200 EXPECT_TRUE(effects_processor_future->Wait());
201 ASSERT_EQ(fake_effects_service.GetProcessors().size(), 1u);
202 EXPECT_EQ(
203 gfx::InsetsF{kFramingPaddingRatio},
204 GetConfigurationSync(fake_effects_service.GetProcessors()[kDeviceId]
205 ->GetVideoEffectsManager())
206 ->framing->padding_ratios);
207
Bryant Chandler32ca6382023-08-02 21:52:31208 effects_manager1.reset();
209 effects_manager2.reset();
Piotr Bialeckic2c709d2024-03-27 21:04:09210 fake_effects_service.GetProcessors().erase(kDeviceId);
Bryant Chandler32ca6382023-08-02 21:52:31211 // Wait for the reset to complete
212 base::RunLoop().RunUntilIdle();
213
Bryant Chandler23896e32024-03-06 17:58:03214 mojo::Remote<media::mojom::VideoEffectsManager> effects_manager3;
Piotr Bialecki7a3cfb22024-07-31 04:53:24215 service_->BindVideoEffectsManager(
Bryant Chandler32ca6382023-08-02 21:52:31216 kDeviceId, effects_manager3.BindNewPipeAndPassReceiver());
217
218 // Expect `framing` to be unset because it is a new instance of
219 // `VideoEffectsManager`.
220 EXPECT_TRUE(GetConfigurationSync(effects_manager3)->framing.is_null());
221}
Piotr Bialeckic2c709d2024-03-27 21:04:09222
223TEST_F(MediaEffectsServiceTest, BindVideoEffectsProcessor) {
224 // Tests that `MediaEffectsService::BindVideoEffectsProcessor()` works, i.e.
225 // causes the passed in remote to be connected.
226
227 mojo::Remote<video_effects::mojom::VideoEffectsService> service;
228 video_effects::FakeVideoEffectsService fake_effects_service(
229 service.BindNewPipeAndPassReceiver());
Piotr Bialecki5a6c9112024-09-20 01:51:50230 auto service_reset =
231 video_effects::SetVideoEffectsServiceRemoteForTesting(&service);
Piotr Bialeckic2c709d2024-03-27 21:04:09232
233 auto effects_processor_future =
234 fake_effects_service.GetEffectsProcessorCreationFuture();
235
236 mojo::Remote<video_effects::mojom::VideoEffectsProcessor> effects_processor;
Piotr Bialecki7a3cfb22024-07-31 04:53:24237 service_->BindVideoEffectsProcessor(
Piotr Bialeckic2c709d2024-03-27 21:04:09238 kDeviceId, effects_processor.BindNewPipeAndPassReceiver());
239
240 EXPECT_TRUE(effects_processor_future->Wait());
241 EXPECT_TRUE(effects_processor.is_connected());
242 EXPECT_EQ(fake_effects_service.GetProcessors().size(), 1u);
243}
244
245TEST_F(
246 MediaEffectsServiceTest,
247 BindVideoEffectsProcessor_TwoProcessorsWithDifferentIdConnectToDifferentManager) {
248 // Tests that `MediaEffectsService::BindVideoEffectsProcessor()` connects to
249 // a different manager if a different ID was used. This is validated by
250 // checking that the managers return different configurations. We also set a
251 // different config directly via effects manager interface (originating from
252 // a call to `MediaEffectsService::BindVideoEffectsManager()`) so this test
253 // also checks that a correct relationship is established between manager
254 // and processor.
255
256 mojo::Remote<video_effects::mojom::VideoEffectsService> service;
257 video_effects::FakeVideoEffectsService fake_effects_service(
258 service.BindNewPipeAndPassReceiver());
Piotr Bialecki5a6c9112024-09-20 01:51:50259 auto service_reset =
260 video_effects::SetVideoEffectsServiceRemoteForTesting(&service);
Piotr Bialeckic2c709d2024-03-27 21:04:09261
262 mojo::Remote<media::mojom::VideoEffectsManager> effects_manager;
Piotr Bialecki7a3cfb22024-07-31 04:53:24263 service_->BindVideoEffectsManager(
Piotr Bialeckic2c709d2024-03-27 21:04:09264 "test_device_1", effects_manager.BindNewPipeAndPassReceiver());
265
266 constexpr float kFramingPaddingRatio1 = 0.234;
267 SetFramingSync(effects_manager, kFramingPaddingRatio1);
268
269 EXPECT_EQ(gfx::InsetsF{kFramingPaddingRatio1},
270 GetConfigurationSync(effects_manager)->framing->padding_ratios);
271
272 auto effects_processor_future1 =
273 fake_effects_service.GetEffectsProcessorCreationFuture();
274
275 mojo::Remote<video_effects::mojom::VideoEffectsProcessor> effects_processor1;
Piotr Bialecki7a3cfb22024-07-31 04:53:24276 service_->BindVideoEffectsProcessor(
Piotr Bialeckic2c709d2024-03-27 21:04:09277 "test_device_2", effects_processor1.BindNewPipeAndPassReceiver());
278 EXPECT_TRUE(effects_processor_future1->Wait());
279 ASSERT_EQ(fake_effects_service.GetProcessors().size(), 1u);
280
281 constexpr float kFramingPaddingRatio2 = 0.345;
282 SetFramingSync(fake_effects_service.GetProcessors()["test_device_2"]
283 ->GetVideoEffectsManager(),
284 kFramingPaddingRatio2);
285
286 EXPECT_EQ(gfx::InsetsF{kFramingPaddingRatio2},
287 GetConfigurationSync(
288 fake_effects_service.GetProcessors()["test_device_2"]
289 ->GetVideoEffectsManager())
290 ->framing->padding_ratios);
291
292 auto effects_processor_future2 =
293 fake_effects_service.GetEffectsProcessorCreationFuture();
294
295 mojo::Remote<video_effects::mojom::VideoEffectsProcessor> effects_processor2;
Piotr Bialecki7a3cfb22024-07-31 04:53:24296 service_->BindVideoEffectsProcessor(
Piotr Bialeckic2c709d2024-03-27 21:04:09297 "test_device_3", effects_processor2.BindNewPipeAndPassReceiver());
298 EXPECT_TRUE(effects_processor_future2->Wait());
299 ASSERT_EQ(fake_effects_service.GetProcessors().size(), 2u);
300
301 constexpr float kFramingPaddingRatio3 = 0.456;
302 SetFramingSync(fake_effects_service.GetProcessors()["test_device_3"]
303 ->GetVideoEffectsManager(),
304 kFramingPaddingRatio3);
305
306 auto padding2 =
307 std::move(GetConfigurationSync(
308 fake_effects_service.GetProcessors()["test_device_2"]
309 ->GetVideoEffectsManager())
310 ->framing->padding_ratios);
311 auto padding3 =
312 std::move(GetConfigurationSync(
313 fake_effects_service.GetProcessors()["test_device_3"]
314 ->GetVideoEffectsManager())
315 ->framing->padding_ratios);
316
317 EXPECT_NE(padding2, padding3);
318 EXPECT_EQ(gfx::InsetsF{kFramingPaddingRatio2}, padding2);
319 EXPECT_EQ(gfx::InsetsF{kFramingPaddingRatio3}, padding3);
320}
Piotr Bialecki7a3cfb22024-07-31 04:53:24321
322TEST_F(MediaEffectsServiceTest, ModelFileIsOpenedAndSentToVideoEffects) {
323 constexpr char kFirstModelBytes[] = "abcdefgh";
324 constexpr char kSecondModelBytes[] = "ijklmnop";
325
326 mojo::Remote<video_effects::mojom::VideoEffectsService> service;
327 video_effects::FakeVideoEffectsService fake_effects_service(
328 service.BindNewPipeAndPassReceiver());
Piotr Bialecki5a6c9112024-09-20 01:51:50329 auto service_reset =
330 video_effects::SetVideoEffectsServiceRemoteForTesting(&service);
Piotr Bialecki7a3cfb22024-07-31 04:53:24331
332 // Setting the model file path for the first time propagates the model file to
333 // Video Effects Service: Prepare model file:
334 base::ScopedTempFile temporary_model_file_path1;
335 ASSERT_TRUE(temporary_model_file_path1.Create());
336 ASSERT_TRUE(
337 base::WriteFile(temporary_model_file_path1.path(), kFirstModelBytes));
338
339 // Set it on Media Effects Service:
340 auto model_opened_future =
341 fake_effects_service.GetBackgroundSegmentationModelFuture();
342 model_provider_->SetModelPath(temporary_model_file_path1.path());
343
344 auto model_file = model_opened_future->Take();
345 EXPECT_TRUE(model_file.IsValid());
346
347 // Validate that the contents match the contents of the model file:
348 std::string contents(sizeof(kFirstModelBytes), '\0');
Peter Kastingd349e8c2024-12-02 09:49:14349 ASSERT_TRUE(model_file.Read(0, base::as_writable_byte_span(contents)));
Piotr Bialecki7a3cfb22024-07-31 04:53:24350 EXPECT_STREQ(contents.data(), kFirstModelBytes);
351
352 // Setting the model file path for the second time propagates the model file
353 // to Video Effects Service: Prepare model file:
354 base::ScopedTempFile temporary_model_file_path2;
355 ASSERT_TRUE(temporary_model_file_path2.Create());
356 ASSERT_TRUE(
357 base::WriteFile(temporary_model_file_path2.path(), kSecondModelBytes));
358
359 // Set it on Media Effects Service:
360 model_opened_future =
361 fake_effects_service.GetBackgroundSegmentationModelFuture();
362 model_provider_->SetModelPath(temporary_model_file_path2.path());
363
364 model_file = model_opened_future->Take();
365 EXPECT_TRUE(model_file.IsValid());
366
367 // Validate that the contents match the contents of the model file:
368 contents.resize(sizeof(kSecondModelBytes));
Peter Kastingd349e8c2024-12-02 09:49:14369 ASSERT_TRUE(model_file.Read(0, base::as_writable_byte_span(contents)));
Piotr Bialecki7a3cfb22024-07-31 04:53:24370 EXPECT_STREQ(contents.data(), kSecondModelBytes);
371
372 // Setting the model file to a path that doesn't exist does not propagate the
373 // model to Video Effects Service: Set invalid path to the model:
374 base::ScopedTempDir temporary_directory;
375 ASSERT_TRUE(temporary_directory.CreateUniqueTempDir());
376
377 model_opened_future =
378 fake_effects_service.GetBackgroundSegmentationModelFuture();
379 model_provider_->SetModelPath(
380 temporary_directory.GetPath().AppendASCII("should_not_exist.tmp"));
Piotr Bialecki55f635a2025-01-10 20:04:22381
382 model_file = model_opened_future->Take();
383 EXPECT_FALSE(model_file.IsValid());
384
385 model_opened_future =
386 fake_effects_service.GetBackgroundSegmentationModelFuture();
387 model_provider_->SetModelPath(std::nullopt);
388
389 model_file = model_opened_future->Take();
390 EXPECT_FALSE(model_file.IsValid());
Piotr Bialecki7a3cfb22024-07-31 04:53:24391}