Commit 6d7d2ddd authored by evliu's avatar evliu Committed by Commit Bot

Create Speech On-Device API (SODA) Service

This CL creates a sandboxed service that hosts the Speech On-Device API
(SODA). It contains the components required to launch the service from
the renderer process, but the implementation of the service itself is
stubbed out. The design document for the feature is located at:
go/chrome-live-captions.

Bug: 1045043
Change-Id: I2411865457e1b7864f1b4a327d776932caf3132c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2017563
Commit-Queue: Evan Liu <evliu@google.com>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarDale Curtis <dalecurtis@chromium.org>
Reviewed-by: default avatarKen Rockot <rockot@google.com>
Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Cr-Commit-Position: refs/heads/master@{#747915}
parent 64c3f931
......@@ -10596,6 +10596,11 @@ Please help our engineers fix this problem. Tell us what happened right before y
Also clear browsing data (<ph name="URL">$1<ex>www.google.com</ex></ph>) which may sign you out of Google.com.
</message>
<!-- Speech On-Device API -->
<message name="IDS_UTILITY_PROCESS_SODA_SERVICE_NAME" desc="The name of the service process used to interface with the Speech On-Demand API.">
SODA Service
</message>
<!-- App pause prompt -->
<message name="IDS_APP_PAUSE_PROMPT_TITLE" desc="Titlebar of the app pause prompt window">
App paused
......
......@@ -1715,6 +1715,10 @@ jumbo_static_library("browser") {
"site_isolation/site_details.h",
"site_isolation/site_isolation_policy.cc",
"site_isolation/site_isolation_policy.h",
"soda/soda_service.cc",
"soda/soda_service.h",
"soda/soda_service_factory.cc",
"soda/soda_service_factory.h",
"speech/chrome_speech_recognition_manager_delegate.cc",
"speech/chrome_speech_recognition_manager_delegate.h",
"speech/speech_recognizer.cc",
......
......@@ -21,6 +21,8 @@
#include "chrome/browser/prerender/prerender_contents.h"
#include "chrome/browser/prerender/prerender_processor_impl.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/soda/soda_service.h"
#include "chrome/browser/soda/soda_service_factory.h"
#include "chrome/browser/ssl/insecure_sensitive_input_driver_factory.h"
#include "chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals.mojom.h"
#include "chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.h"
......@@ -49,6 +51,7 @@
#include "content/public/common/content_features.h"
#include "content/public/common/url_constants.h"
#include "extensions/buildflags/buildflags.h"
#include "media/mojo/mojom/soda_service.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "services/image_annotation/public/mojom/image_annotation.mojom.h"
#include "third_party/blink/public/mojom/insecure_input/insecure_input_service.mojom.h"
......@@ -323,6 +326,15 @@ void BindNetworkHintsHandler(
predictors::NetworkHintsHandlerImpl::Create(frame_host, std::move(receiver));
}
void BindSodaContextHandler(
content::RenderFrameHost* frame_host,
mojo::PendingReceiver<media::mojom::SodaContext> receiver) {
SodaServiceFactory::GetForProfile(
Profile::FromBrowserContext(
frame_host->GetProcess()->GetBrowserContext()))
->Create(std::move(receiver));
}
void PopulateChromeFrameBinders(
service_manager::BinderMapWithContext<content::RenderFrameHost*>* map) {
map->Add<image_annotation::mojom::Annotator>(
......@@ -400,6 +412,9 @@ void PopulateChromeFrameBinders(
map->Add<network_hints::mojom::NetworkHintsHandler>(
base::BindRepeating(&BindNetworkHintsHandler));
map->Add<media::mojom::SodaContext>(
base::BindRepeating(&BindSodaContextHandler));
}
void PopulateChromeWebUIFrameBinders(
......
# COMPONENT: Internals>Media
evliu@google.com
beccahughes@chromium.org
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/soda/soda_service.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/service_process_host.h"
namespace soda {
constexpr base::TimeDelta kIdleProcessTimeout = base::TimeDelta::FromSeconds(5);
SodaService::SodaService() = default;
SodaService::~SodaService() = default;
void SodaService::Create(
mojo::PendingReceiver<media::mojom::SodaContext> receiver) {
LaunchIfNotRunning();
soda_service_->BindContext(std::move(receiver));
}
void SodaService::LaunchIfNotRunning() {
if (soda_service_.is_bound())
return;
content::ServiceProcessHost::Launch(
soda_service_.BindNewPipeAndPassReceiver(),
content::ServiceProcessHost::Options()
.WithDisplayName(IDS_UTILITY_PROCESS_SODA_SERVICE_NAME)
.WithSandboxType(service_manager::SandboxType::kSoda)
.Pass());
// Ensure that if the interface is ever disconnected (e.g. the service
// process crashes) or goes idle for a short period of time -- meaning there
// are no in-flight messages and no other interfaces bound through this
// one -- then we will reset |remote|, causing the service process to be
// terminated if it isn't already.
soda_service_.reset_on_disconnect();
soda_service_.reset_on_idle_timeout(kIdleProcessTimeout);
}
} // namespace soda
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_SODA_SODA_SERVICE_H_
#define CHROME_BROWSER_SODA_SODA_SERVICE_H_
#include "components/keyed_service/core/keyed_service.h"
#include "media/mojo/mojom/soda_service.mojom.h"
#include "mojo/public/cpp/bindings/remote.h"
class Profile;
namespace soda {
// Provides a mojo endpoint in the browser that allows the renderer process to
// launch and initialize the sandboxed Speech On-Device API (SODA) service
// process.
class SodaService : public KeyedService {
public:
explicit SodaService();
SodaService(const SodaService&) = delete;
SodaService& operator=(const SodaService&) = delete;
~SodaService() override;
void Create(mojo::PendingReceiver<media::mojom::SodaContext> receiver);
private:
// Launches the SODA service in a sandboxed utility process.
void LaunchIfNotRunning();
// The remote to the SODA service. The browser will not launch a new SODA
// service process if this remote is already bound.
mojo::Remote<media::mojom::SodaService> soda_service_;
};
} // namespace soda
#endif // CHROME_BROWSER_SODA_SODA_SERVICE_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/soda/soda_service_factory.h"
#include "base/no_destructor.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/soda/soda_service.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
// static
soda::SodaService* SodaServiceFactory::GetForProfile(Profile* profile) {
return static_cast<soda::SodaService*>(
GetInstance()->GetServiceForBrowserContext(profile, true));
}
// static
SodaServiceFactory* SodaServiceFactory::GetInstance() {
static base::NoDestructor<SodaServiceFactory> instance;
return instance.get();
}
SodaServiceFactory::SodaServiceFactory()
: BrowserContextKeyedServiceFactory(
"SodaService",
BrowserContextDependencyManager::GetInstance()) {}
SodaServiceFactory::~SodaServiceFactory() = default;
KeyedService* SodaServiceFactory::BuildServiceInstanceFor(
content::BrowserContext* profile) const {
return new soda::SodaService();
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_SODA_SODA_SERVICE_FACTORY_H_
#define CHROME_BROWSER_SODA_SODA_SERVICE_FACTORY_H_
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
class Profile;
namespace base {
template <class T>
class NoDestructor;
} // namespace base
namespace soda {
class SodaService;
} // namespace soda
// Factory to get or create an instance of SodaServiceFactory from
// a Profile.
class SodaServiceFactory : public BrowserContextKeyedServiceFactory {
public:
static soda::SodaService* GetForProfile(Profile* profile);
private:
friend class base::NoDestructor<SodaServiceFactory>;
static SodaServiceFactory* GetInstance();
SodaServiceFactory();
~SodaServiceFactory() override;
// BrowserContextKeyedServiceFactory:
KeyedService* BuildServiceInstanceFor(
content::BrowserContext* profile) const override;
};
#endif // CHROME_BROWSER_SODA_SODA_SERVICE_FACTORY_H_
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
source_set("lib") {
sources = [
"soda_recognizer_impl.cc",
"soda_recognizer_impl.h",
"soda_service_impl.cc",
"soda_service_impl.h",
]
public_deps = [
"//media/mojo/mojom",
"//mojo/public/cpp/bindings",
"//mojo/public/mojom/base",
]
deps = [
"//base",
"//services/service_manager/public/cpp",
]
}
include_rules = [
"+media"
]
# COMPONENT: Internals>Media
evliu@google.com
beccahughes@chromium.org
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/services/soda/soda_recognizer_impl.h"
#include "base/bind.h"
#include "media/base/bind_to_current_loop.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
namespace soda {
SodaRecognizerImpl::~SodaRecognizerImpl() = default;
void SodaRecognizerImpl::Create(
mojo::PendingReceiver<media::mojom::SodaRecognizer> receiver,
mojo::PendingRemote<media::mojom::SodaRecognizerClient> remote) {
mojo::MakeSelfOwnedReceiver(
base::WrapUnique(new SodaRecognizerImpl(std::move(remote))),
std::move(receiver));
}
void SodaRecognizerImpl::OnRecognitionEvent(const std::string& result) {
if (client_remote_.is_bound()) {
client_remote_->OnSodaRecognitionEvent(result);
} else {
NOTREACHED();
}
}
SodaRecognizerImpl::SodaRecognizerImpl(
mojo::PendingRemote<media::mojom::SodaRecognizerClient> remote)
: client_remote_(std::move(remote)) {
recognition_event_callback_ = media::BindToCurrentLoop(base::Bind(
&SodaRecognizerImpl::OnRecognitionEvent, weak_factory_.GetWeakPtr()));
}
} // namespace soda
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_SERVICES_SODA_SODA_RECOGNIZER_IMPL_H_
#define CHROME_SERVICES_SODA_SODA_RECOGNIZER_IMPL_H_
#include "base/memory/weak_ptr.h"
#include "media/mojo/mojom/soda_service.mojom.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
namespace soda {
class SodaClient;
class SodaRecognizerImpl : public media::mojom::SodaRecognizer {
public:
using OnRecognitionEventCallback =
base::RepeatingCallback<void(const std::string& result)>;
~SodaRecognizerImpl() override;
static void Create(
mojo::PendingReceiver<media::mojom::SodaRecognizer> receiver,
mojo::PendingRemote<media::mojom::SodaRecognizerClient> remote);
OnRecognitionEventCallback recognition_event_callback() const {
return recognition_event_callback_;
}
private:
explicit SodaRecognizerImpl(
mojo::PendingRemote<media::mojom::SodaRecognizerClient> remote);
// Return the transcribed audio from the recognition event back to the caller
// via the recognition event client.
void OnRecognitionEvent(const std::string& result);
// The remote endpoint for the mojo pipe used to return transcribed audio from
// the SODA service back to the renderer.
mojo::Remote<media::mojom::SodaRecognizerClient> client_remote_;
// The callback that is eventually executed on a speech recognition event
// which passes the transcribed audio back to the caller via the SODA
// recognition event client remote.
OnRecognitionEventCallback recognition_event_callback_;
base::WeakPtrFactory<SodaRecognizerImpl> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(SodaRecognizerImpl);
};
} // namespace soda
#endif // CHROME_SERVICES_SODA_SODA_RECOGNIZER_IMPL_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/services/soda/soda_service_impl.h"
#include "chrome/services/soda/soda_recognizer_impl.h"
namespace soda {
SodaServiceImpl::SodaServiceImpl(
mojo::PendingReceiver<media::mojom::SodaService> receiver)
: receiver_(this, std::move(receiver)) {}
SodaServiceImpl::~SodaServiceImpl() = default;
void SodaServiceImpl::BindContext(
mojo::PendingReceiver<media::mojom::SodaContext> context) {
soda_contexts_.Add(this, std::move(context));
}
void SodaServiceImpl::BindRecognizer(
mojo::PendingReceiver<media::mojom::SodaRecognizer> receiver,
mojo::PendingRemote<media::mojom::SodaRecognizerClient> client) {
SodaRecognizerImpl::Create(std::move(receiver), std::move(client));
}
} // namespace soda
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_SERVICES_SODA_SODA_SERVICE_IMPL_H_
#define CHROME_SERVICES_SODA_SODA_SERVICE_IMPL_H_
#include "media/mojo/mojom/soda_service.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
namespace soda {
class SodaServiceImpl : public media::mojom::SodaService,
public media::mojom::SodaContext {
public:
explicit SodaServiceImpl(
mojo::PendingReceiver<media::mojom::SodaService> receiver);
~SodaServiceImpl() override;
// media::mojom::SodaService
void BindContext(
mojo::PendingReceiver<media::mojom::SodaContext> context) override;
// media::mojom::SodaContext
void BindRecognizer(
mojo::PendingReceiver<media::mojom::SodaRecognizer> receiver,
mojo::PendingRemote<media::mojom::SodaRecognizerClient> client) override;
private:
mojo::Receiver<media::mojom::SodaService> receiver_;
// The set of receivers used to receive messages from the renderer clients.
mojo::ReceiverSet<media::mojom::SodaContext> soda_contexts_;
DISALLOW_COPY_AND_ASSIGN(SodaServiceImpl);
};
} // namespace soda
#endif // CHROME_SERVICES_SODA_SODA_SERVICE_IMPL_H_
......@@ -35,6 +35,7 @@ static_library("utility") {
"//chrome/common",
"//chrome/common:mojo_bindings",
"//chrome/services/qrcode_generator",
"//chrome/services/soda:lib",
"//components/mirroring/service:mirroring_service",
"//components/paint_preview/buildflags",
"//components/safe_browsing:buildflags",
......
......@@ -15,6 +15,7 @@ include_rules = [
"+chrome/services/qrcode_generator",
"+chrome/services/removable_storage_writer",
"+chrome/services/sharing",
"+chrome/services/soda/soda_service_impl.h",
"+chrome/services/util_win/util_win_impl.h",
"+chrome/services/util_win/public/mojom",
"+chromeos/assistant/buildflags.h",
......
......@@ -10,6 +10,7 @@
#include "base/macros.h"
#include "base/no_destructor.h"
#include "build/build_config.h"
#include "chrome/services/soda/soda_service_impl.h"
#include "components/paint_preview/buildflags/buildflags.h"
#include "components/safe_browsing/buildflags.h"
#include "components/services/patch/file_patcher_impl.h"
......@@ -20,6 +21,7 @@
#include "content/public/utility/utility_thread.h"
#include "device/vr/buildflags/buildflags.h"
#include "extensions/buildflags/buildflags.h"
#include "media/mojo/mojom/soda_service.mojom.h"
#include "mojo/public/cpp/bindings/service_factory.h"
#include "printing/buildflags/buildflags.h"
......@@ -101,6 +103,10 @@ auto RunUnzipper(mojo::PendingReceiver<unzip::mojom::Unzipper> receiver) {
return std::make_unique<unzip::UnzipperImpl>(std::move(receiver));
}
auto RunSodaService(mojo::PendingReceiver<media::mojom::SodaService> receiver) {
return std::make_unique<soda::SodaServiceImpl>(std::move(receiver));
}
#if defined(OS_WIN)
auto RunQuarantineService(
mojo::PendingReceiver<quarantine::mojom::Quarantine> receiver) {
......@@ -242,6 +248,7 @@ mojo::ServiceFactory* GetMainThreadServiceFactory() {
// clang-format off
static base::NoDestructor<mojo::ServiceFactory> factory {
RunFilePatcher,
RunSodaService,
RunUnzipper,
#if !defined(OS_ANDROID)
......
......@@ -34,6 +34,7 @@ mojom("mojom") {
"provision_fetcher.mojom",
"renderer.mojom",
"renderer_extensions.mojom",
"soda_service.mojom",
"video_decode_perf_history.mojom",
"video_decode_stats_recorder.mojom",
"video_decoder.mojom",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
module media.mojom;
import "media/mojo/mojom/media_types.mojom";
// The main interface a client uses to interact with a SODA service
// process. Every renderer can own one or more Remote<SodaContext>,
// with the receiver bound through the BrowserInterfaceBroker.
interface SodaContext {
// Bind the recognizers to the SODA service.
BindRecognizer(pending_receiver<SodaRecognizer> receiver,
pending_remote<SodaRecognizerClient> client);
};
// The main interface to a Speech On-Device API (SODA) service process.
// Used by the browser to issue top-level control requests to the service,
// acquired during process launch.
interface SodaService {
// Bind the SODA context to a new instance of SODA.
BindContext(pending_receiver<SodaContext> context);
};
// The interface used to pass raw audio from the renderer to the SODA
// service. The remote lives in the renderer process and the receiver
// lives in the SODA process.
interface SodaRecognizer {
// TODO(evliu): Fill this in.
};
// The interface used to return speech recognition events from the SODA
// service back to the originating media. The remote lives in the SODA
// process and the receiver lives in the renderer process.
interface SodaRecognizerClient {
// Triggered by SODA on a speech recognition event.
OnSodaRecognitionEvent(string transcription);
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment