blob: 656e4e1b7da39c687e63a63aaa79a1bb1590ddce [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/media_effects/media_effects_service_factory.h"
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/browser/browser_context.h"
#include "media/base/media_switches.h"
#include "services/video_effects/public/cpp/buildflags.h"
#if BUILDFLAG(ENABLE_VIDEO_EFFECTS)
#include "components/media_effects/media_effects_model_provider.h"
#endif
// static
MediaEffectsService* MediaEffectsServiceFactory::GetForBrowserContext(
content::BrowserContext* browser_context) {
return static_cast<MediaEffectsService*>(
GetInstance()->GetServiceForBrowserContext(browser_context, true));
}
// static
MediaEffectsServiceFactory* MediaEffectsServiceFactory::GetInstance() {
static base::NoDestructor<MediaEffectsServiceFactory> instance;
#if BUILDFLAG(ENABLE_VIDEO_EFFECTS)
// Media Effects Service depends on Optimization Guide because it needs to
// subscribe to the model updates.
instance->DependsOn(OptimizationGuideKeyedServiceFactory::GetInstance());
#endif
return instance.get();
}
MediaEffectsServiceFactory::MediaEffectsServiceFactory()
: BrowserContextKeyedServiceFactory(
"MediaEffectsServiceFactory",
BrowserContextDependencyManager::GetInstance()) {}
MediaEffectsServiceFactory::~MediaEffectsServiceFactory() = default;
#if BUILDFLAG(ENABLE_VIDEO_EFFECTS)
class SegmentationModelObserver
: public optimization_guide::OptimizationTargetModelObserver,
public MediaEffectsModelProvider {
public:
explicit SegmentationModelObserver(content::BrowserContext* browser_context) {
// It's safe to store the Optimization Guide service by raw pointer - the
// `SegmentationModelObserver` will be owned by `MediaEffectsService`, and
// we add a dependency between `OptimizationGuideKeyedServiceFactory` &
// `MediaEffectsServiceFactory`, which means that Media Effects Service (and
// by extension this observer) will be destroyed before Optimization Guide.
optimization_guide_ = OptimizationGuideKeyedServiceFactory::GetForProfile(
Profile::FromBrowserContext(browser_context));
if (!optimization_guide_) {
// Optimization Guide can be disabled by a feature, so we should tolerate
// it being null.
return;
}
optimization_guide_->AddObserverForOptimizationTargetModel(
optimization_guide::proto::OptimizationTarget::
OPTIMIZATION_TARGET_CAMERA_BACKGROUND_SEGMENTATION,
std::nullopt, this);
}
~SegmentationModelObserver() override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!optimization_guide_) {
return;
}
optimization_guide_->RemoveObserverForOptimizationTargetModel(
optimization_guide::proto::OptimizationTarget::
OPTIMIZATION_TARGET_CAMERA_BACKGROUND_SEGMENTATION,
this);
}
SegmentationModelObserver(const SegmentationModelObserver& other) = delete;
SegmentationModelObserver& operator=(const SegmentationModelObserver& other) =
delete;
SegmentationModelObserver(SegmentationModelObserver&& other) = delete;
SegmentationModelObserver& operator=(SegmentationModelObserver&& other) =
delete;
// optimization_guide::OptimizationTargetModelObserver:
void OnModelUpdated(
optimization_guide::proto::OptimizationTarget optimization_target,
base::optional_ref<const optimization_guide::ModelInfo> model_info)
override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// We only subscribe to notifications for
// `OPTIMIZATION_TARGET_CAMERA_BACKGROUND_SEGMENTATION`, so we know that the
// updated model info pertains to background segmentation models:
CHECK_EQ(optimization_target,
optimization_guide::proto::OptimizationTarget::
OPTIMIZATION_TARGET_CAMERA_BACKGROUND_SEGMENTATION);
background_segmentation_model_path_ =
model_info.has_value() ? std::optional(model_info->GetModelFilePath())
: std::nullopt;
for (auto& observer : observers_) {
observer.OnBackgroundSegmentationModelUpdated(
background_segmentation_model_path_);
}
}
// MediaEffectsModelProvider:
void AddObserver(MediaEffectsModelProvider::Observer* observer) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// If we already have a model path, let's inform the new observer about it.
if (background_segmentation_model_path_) {
observer->OnBackgroundSegmentationModelUpdated(
*background_segmentation_model_path_);
}
observers_.AddObserver(observer);
}
void RemoveObserver(MediaEffectsModelProvider::Observer* observer) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observers_.RemoveObserver(observer);
}
private:
raw_ptr<OptimizationGuideKeyedService> optimization_guide_;
base::ObserverList<MediaEffectsModelProvider::Observer> observers_;
std::optional<base::FilePath> background_segmentation_model_path_;
SEQUENCE_CHECKER(sequence_checker_);
};
#endif
std::unique_ptr<KeyedService>
MediaEffectsServiceFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* browser_context) const {
CHECK(browser_context);
#if BUILDFLAG(ENABLE_VIDEO_EFFECTS)
std::unique_ptr<SegmentationModelObserver> model_provider;
if (base::FeatureList::IsEnabled(media::kCameraMicEffects)) {
model_provider =
std::make_unique<SegmentationModelObserver>(browser_context);
}
return std::make_unique<MediaEffectsService>(
user_prefs::UserPrefs::Get(browser_context), std::move(model_provider));
#else
return std::make_unique<MediaEffectsService>(
user_prefs::UserPrefs::Get(browser_context));
#endif
}