blob: ab95dd22e5a27acfa3add3056c62439ef31d8113 [file]
// Copyright 2020 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/services/speech/speech_recognition_service_impl.h"
#include <string>
#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/memory/weak_ptr.h"
#include "chrome/services/speech/audio_source_fetcher_impl.h"
#include "chrome/services/speech/soda_speech_recognizer_impl.h"
#include "chrome/services/speech/speech_recognition_recognizer_impl.h"
#include "media/mojo/mojom/speech_recognition.mojom.h"
#include "media/mojo/mojom/speech_recognition_service.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
namespace speech {
namespace {
constexpr char kInvalidSpeechRecogntionOptions[] =
"Invalid SpeechRecognitionOptions provided";
} // namespace
SpeechRecognitionServiceImpl::SpeechRecognitionServiceImpl(
mojo::PendingReceiver<media::mojom::SpeechRecognitionService> receiver)
: receiver_(this, std::move(receiver)) {}
SpeechRecognitionServiceImpl::~SpeechRecognitionServiceImpl() = default;
void SpeechRecognitionServiceImpl::BindAudioSourceSpeechRecognitionContext(
mojo::PendingReceiver<media::mojom::AudioSourceSpeechRecognitionContext>
context) {
// TODO(b/249867435): Make this function create mojo::ReportBadMessage because
// this method shouldn't be called on platforms other than ChromeOS. ChromeOS
// clients should use CrosSpeechRecognitionService.
audio_source_speech_recognition_contexts_.Add(this, std::move(context));
}
void SpeechRecognitionServiceImpl::BindSpeechRecognitionContext(
mojo::PendingReceiver<media::mojom::SpeechRecognitionContext> context) {
speech_recognition_contexts_.Add(this, std::move(context));
}
void SpeechRecognitionServiceImpl::SetSodaPaths(
const base::FilePath& binary_path,
const base::flat_map<std::string, base::FilePath>& config_paths,
const std::string& default_live_caption_language) {
binary_path_ = binary_path;
config_paths_ = config_paths;
default_live_caption_language_ = default_live_caption_language;
}
void SpeechRecognitionServiceImpl::SetSodaParams(
const bool mask_offensive_words) {
mask_offensive_words_ = mask_offensive_words;
}
void SpeechRecognitionServiceImpl::SetSodaConfigPaths(
const base::flat_map<std::string, base::FilePath>& config_paths) {
config_paths_ = config_paths;
for (Observer& observer : observers_) {
observer.OnLanguagePackInstalled(config_paths_);
}
}
void SpeechRecognitionServiceImpl::BindRecognizer(
mojo::PendingReceiver<media::mojom::SpeechRecognitionRecognizer> receiver,
mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient> client,
media::mojom::SpeechRecognitionOptionsPtr options,
BindRecognizerCallback callback) {
if (CreateRecognizer(std::move(receiver), std::move(client),
std::move(options))) {
std::move(callback).Run(
SpeechRecognitionRecognizerImpl::IsMultichannelSupported());
}
}
void SpeechRecognitionServiceImpl::BindWebSpeechRecognizer(
mojo::PendingReceiver<media::mojom::SpeechRecognitionSession>
session_receiver,
mojo::PendingRemote<media::mojom::SpeechRecognitionSessionClient>
session_client,
mojo::PendingReceiver<media::mojom::SpeechRecognitionAudioForwarder>
audio_forwarder,
int channel_count,
int sample_rate,
media::mojom::SpeechRecognitionOptionsPtr options,
bool continuous) {
mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizer>
speech_recognition_recognizer;
mojo::PendingReceiver<media::mojom::SpeechRecognitionRecognizerClient>
speech_recognition_recognizer_client;
if (CreateRecognizer(
speech_recognition_recognizer.InitWithNewPipeAndPassReceiver(),
speech_recognition_recognizer_client.InitWithNewPipeAndPassRemote(),
std::move(options))) {
mojo::MakeSelfOwnedReceiver(
std::make_unique<SodaSpeechRecognizerImpl>(
continuous, sample_rate, std::move(speech_recognition_recognizer),
std::move(speech_recognition_recognizer_client),
std::move(session_client), std::move(audio_forwarder)),
std::move(session_receiver));
}
}
void SpeechRecognitionServiceImpl::BindAudioSourceFetcher(
mojo::PendingReceiver<media::mojom::AudioSourceFetcher> fetcher_receiver,
mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient> client,
media::mojom::SpeechRecognitionOptionsPtr options,
BindRecognizerCallback callback) {
const bool is_server_based = options->is_server_based;
// TODO(b/249867435): Remove SpeechRecognitionServiceImpl's implementation of
// AudioSourceSpeechRecognitionContext should be removed. AudioSourceFetcher
// is only ever used from ChromeOS and it should be accessed
// from CrosSpeechRecognitionService.
if (is_server_based) {
mojo::ReportBadMessage(kInvalidSpeechRecogntionOptions);
return;
}
// Destroy the speech recognition service if the SODA files haven't been
// downloaded yet.
if (!FilePathsExist()) {
speech_recognition_contexts_.Clear();
receiver_.reset();
return;
}
auto recognizer = std::make_unique<SpeechRecognitionRecognizerImpl>(
std::move(client), std::move(options), binary_path_, config_paths_,
default_live_caption_language_, mask_offensive_words_,
weak_factory_.GetWeakPtr());
// On Chrome OS, CrosSpeechRecognitionRecognizerImpl will create its own
// CrosSodaClient.
#if !BUILDFLAG(IS_CHROMEOS)
recognizer->CreateSodaClient(binary_path_);
#endif
const bool is_multi_channel_supported =
SpeechRecognitionRecognizerImpl::IsMultichannelSupported();
AudioSourceFetcherImpl::Create(std::move(fetcher_receiver),
std::move(recognizer),
is_multi_channel_supported, is_server_based);
std::move(callback).Run(is_multi_channel_supported);
}
void SpeechRecognitionServiceImpl::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void SpeechRecognitionServiceImpl::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
bool SpeechRecognitionServiceImpl::FilePathsExist() {
if (!base::PathExists(binary_path_))
return false;
for (const auto& config : config_paths_) {
if (!base::PathExists(config.second))
return false;
}
return true;
}
bool SpeechRecognitionServiceImpl::CreateRecognizer(
mojo::PendingReceiver<media::mojom::SpeechRecognitionRecognizer> receiver,
mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient> client,
media::mojom::SpeechRecognitionOptionsPtr options) {
// This is currently only used by Live Caption and server side
// speech recognition is not available to it.
if (options->is_server_based ||
options->recognizer_client_type !=
media::mojom::RecognizerClientType::kLiveCaption) {
mojo::ReportBadMessage(kInvalidSpeechRecogntionOptions);
return false;
}
// Destroy the speech recognition service if the SODA files haven't been
// downloaded yet.
if (!FilePathsExist()) {
speech_recognition_contexts_.Clear();
receiver_.reset();
return false;
}
auto language = options->language.has_value()
? options->language.value()
: default_live_caption_language_;
SpeechRecognitionRecognizerImpl::Create(
std::move(receiver), std::move(client), std::move(options), binary_path_,
config_paths_, language, mask_offensive_words_,
weak_factory_.GetWeakPtr());
return true;
}
} // namespace speech