Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1094)

Unified Diff: content/browser/speech/speech_recognizer_impl.cc

Issue 594063002: Example use case for AudioSharedBuffer (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Using AudioSharedBuffer instead of AudioInputBuffer Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | content/renderer/media/speech_recognition_audio_source_provider.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/browser/speech/speech_recognizer_impl.cc
diff --git a/content/browser/speech/speech_recognizer_impl.cc b/content/browser/speech/speech_recognizer_impl.cc
index 8824bf0dd92c52061aa2762457ad89dc22e15c67..76b8b305584e3aa53247d5d11a694d711a0806f0 100644
--- a/content/browser/speech/speech_recognizer_impl.cc
+++ b/content/browser/speech/speech_recognizer_impl.cc
@@ -6,11 +6,15 @@
#include "base/basictypes.h"
#include "base/bind.h"
+#include "base/memory/shared_memory.h"
+#include "base/process/process_handle.h"
+#include "base/threading/thread.h"
#include "base/time/time.h"
#include "content/browser/browser_main_loop.h"
#include "content/browser/media/media_internals.h"
#include "content/browser/speech/audio_buffer.h"
#include "content/browser/speech/google_one_shot_remote_engine.h"
+#include "content/public/browser/browser_message_filter.h"
#include "content/public/browser/speech_recognition_event_listener.h"
#include "media/base/audio_converter.h"
#include "net/url_request/url_request_context_getter.h"
@@ -21,6 +25,7 @@
using media::AudioBus;
using media::AudioConverter;
+using media::AudioInputBufferParameters;
using media::AudioInputController;
using media::AudioManager;
using media::AudioParameters;
@@ -41,6 +46,7 @@ class SpeechRecognizerImpl::OnDataConverter
// Converts input audio |data| bus into an AudioChunk where the input format
// is given by |input_parameters_| and the output format by
// |output_parameters_|.
+ // Skips conversion on |audio_converter_| when input and output formats match.
scoped_refptr<AudioChunk> Convert(const AudioBus* data);
private:
@@ -57,6 +63,7 @@ class SpeechRecognizerImpl::OnDataConverter
const AudioParameters input_parameters_;
const AudioParameters output_parameters_;
bool waiting_for_input_;
+ bool need_conversion_;
scoped_ptr<uint8[]> converted_data_;
DISALLOW_COPY_AND_ASSIGN(OnDataConverter);
@@ -124,6 +131,7 @@ SpeechRecognizerImpl::OnDataConverter::OnDataConverter(
waiting_for_input_(false),
converted_data_(new uint8[output_parameters_.GetBytesPerBuffer()]) {
audio_converter_.AddInput(this);
+ need_conversion_ = !(input_parameters_ == output_parameters_);
}
SpeechRecognizerImpl::OnDataConverter::~OnDataConverter() {
@@ -136,10 +144,15 @@ scoped_refptr<AudioChunk> SpeechRecognizerImpl::OnDataConverter::Convert(
const AudioBus* data) {
CHECK_EQ(data->frames(), input_parameters_.frames_per_buffer());
- data->CopyTo(input_bus_.get());
-
- waiting_for_input_ = true;
- audio_converter_.Convert(output_bus_.get());
+ // Do conversion if needed - only when |input_parameters_| differ from
+ // |output_parameters_|.
+ if (need_conversion_) {
+ data->CopyTo(input_bus_.get());
+ waiting_for_input_ = true;
+ audio_converter_.Convert(output_bus_.get());
+ } else {
+ data->CopyTo(output_bus_.get());
+ }
output_bus_->ToInterleaved(
output_bus_->frames(), output_parameters_.bits_per_sample() / 8,
@@ -175,6 +188,8 @@ SpeechRecognizerImpl::SpeechRecognizerImpl(
int session_id,
bool continuous,
bool provisional_results,
+ bool using_audio_track,
+ base::ProcessHandle peer_process_handle,
SpeechRecognitionEngine* engine)
: SpeechRecognizer(listener, session_id),
recognition_engine_(engine),
@@ -183,6 +198,8 @@ SpeechRecognizerImpl::SpeechRecognizerImpl(
media::AudioLogFactory::AUDIO_INPUT_CONTROLLER)),
is_dispatching_event_(false),
provisional_results_(provisional_results),
+ using_audio_track_(using_audio_track),
+ peer_process_handle_(peer_process_handle),
state_(STATE_IDLE) {
DCHECK(recognition_engine_ != NULL);
if (!continuous) {
@@ -214,7 +231,7 @@ SpeechRecognizerImpl::SpeechRecognizerImpl(
// synchronous callbacks.
void SpeechRecognizerImpl::StartRecognition(const std::string& device_id) {
- DCHECK(!device_id.empty());
+ DCHECK(!device_id.empty() ^ using_audio_track_);
device_id_ = device_id;
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
@@ -245,7 +262,8 @@ bool SpeechRecognizerImpl::IsCapturingAudio() const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); // See IsActive().
const bool is_capturing_audio = state_ >= STATE_STARTING &&
state_ <= STATE_RECOGNIZING;
- DCHECK((is_capturing_audio && (audio_controller_.get() != NULL)) ||
+ DCHECK((is_capturing_audio &&
+ (using_audio_track_ || (audio_controller_.get() != NULL))) ||
(!is_capturing_audio && audio_controller_.get() == NULL));
return is_capturing_audio;
}
@@ -263,6 +281,12 @@ SpeechRecognizerImpl::~SpeechRecognizerImpl() {
base::Bind(&KeepAudioControllerRefcountedForDtor, audio_controller_));
audio_log_->OnClosed(0);
}
+
+ if (audio_sync_thread_.get()) {
+ base::ThreadRestrictions::SetIOAllowed(true);
+ audio_sync_socket_->Shutdown();
+ audio_sync_thread_->Stop();
+ }
}
// Invoked in the audio thread.
@@ -482,7 +506,7 @@ void SpeechRecognizerImpl::ProcessAudioPipeline(const AudioChunk& raw_audio) {
endpointer_.ProcessAudio(raw_audio, &rms);
if (route_to_vumeter) {
- DCHECK(route_to_endpointer); // Depends on endpointer due to |rms|.
+ DCHECK(route_to_endpointer); // Depends on endpointer due to |rms|.
UpdateSignalAndNoiseLevels(rms, clip_detected);
}
if (route_to_sr_engine) {
@@ -529,6 +553,15 @@ SpeechRecognizerImpl::StartRecording(const FSMEventArgs&) {
AudioParameters::AUDIO_PCM_LOW_LATENCY, kChannelLayout, kAudioSampleRate,
kNumBitsPerAudioSample, frames_per_buffer);
+ // Check if using audio track as source (audio data processed on renderer).
+ // If |StartRecordingUsingAudioTrack()| fails, we abort the recognition.
+ if (using_audio_track_)
+ return StartRecordingUsingAudioTrack(output_parameters)
+ ? STATE_STARTING
+ : Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO));
+
+ // Get audio data from microphone on the browser.
+
// Audio converter will receive audio based on these parameters as input.
// On Windows we start by verifying that Core Audio is supported. If not,
// the WaveIn API is used and we might as well avoid all audio conversations
@@ -585,6 +618,108 @@ SpeechRecognizerImpl::StartRecording(const FSMEventArgs&) {
return STATE_STARTING;
}
+bool SpeechRecognizerImpl::StartRecordingUsingAudioTrack(
+ const media::AudioParameters& params) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ // Create the shared memory and share with the renderer process.
+ uint32 shared_memory_size = sizeof(uint32) +
+ AudioBus::CalculateMemorySize(params);
+ shared_memory_.reset(new base::SharedMemory());
+ if (!shared_memory_->CreateAndMapAnonymous(shared_memory_size)) {
+ DLOG(ERROR) << "Failed to create shared memory for the speech recognizer";
+ return false;
+ }
+ base::SharedMemoryHandle foreign_memory_handle;
+ if (!shared_memory_->ShareToProcess(base::GetCurrentProcessHandle(),
+ &foreign_memory_handle)) {
+ DLOG(ERROR) << "Failed to map and share the shared memory";
+ return false;
+ }
+ media::AudioSharedBuffer* buffer =
+ static_cast<media::AudioSharedBuffer*>(shared_memory_->memory());
+ audio_track_bus_ = AudioBus::WrapMemory(params, buffer->audio);
+ // Use |buffer->params.size| from shared memory to synchronize by counting
+ // audio buffers when receiving audio data from the renderer.
+ buffer_index_ = &(buffer->buffer_index);
+ *buffer_index_ = 0U;
+ // Reusing the audio converter only to create chunks from the bus.
+ // Same input and output format indicate no additional conversion here.
+ audio_converter_.reset(new OnDataConverter(params, params));
+
+ // The endpointer needs to estimate the environment/background noise before
+ // starting to treat the audio as user input. We wait in the state
+ // ESTIMATING_ENVIRONMENT until such interval has elapsed before switching
+ // to user input mode.
+ endpointer_.SetEnvironmentEstimationMode();
+
+ foreign_sync_socket_.reset(new base::CancelableSyncSocket());
+ audio_sync_socket_.reset(new base::CancelableSyncSocket());
+ if (!base::SyncSocket::CreatePair(audio_sync_socket_.get(),
+ foreign_sync_socket_.get())) {
+ DLOG(ERROR) << "Cannot create SyncSocket pair";
+ return false;
+ }
+
+ base::SyncSocket::TransitDescriptor foreign_socket_descriptor;
+ if (!foreign_sync_socket_->PrepareTransitDescriptor(
+ peer_process_handle_, &foreign_socket_descriptor)) {
+ DLOG(ERROR) << "Cannot prepare syncsocket transit descriptor";
+ return false;
+ }
+
+ // The thread check will be first invoked in |OnAudioTrackData()|.
+ audio_sync_thread_checker_.DetachFromThread();
+ audio_sync_thread_.reset(
+ new base::Thread("SpeechRecognitionAudioTrackThread"));
+ audio_sync_thread_->Start();
+
+ DCHECK_EQ(0U, *buffer_index_);
+ if (!audio_sync_thread_->message_loop_proxy()
+ ->PostTask(FROM_HERE,
+ base::Bind(&SpeechRecognizerImpl::OnAudioTrackData,
+ base::Unretained(this)))) {
+ DLOG(ERROR) << "Cannot post audio sync thread task";
+ return false;
+ }
+
+ listener()->OnAudioTrackReady(session_id(), params, foreign_memory_handle,
+ foreign_socket_descriptor);
+
+ return true;
+}
+
+void SpeechRecognizerImpl::OnAudioTrackData() {
+ DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(audio_sync_thread_checker_.CalledOnValidThread());
+ base::ThreadRestrictions::SetIOAllowed(true);
+
+ // TODO(burnik): We should receive with a timeout here. And when it occurs,
+ // we should stop the recognition assuming there is no more audio data.
+ size_t bytes_received =
+ audio_sync_socket_->Receive(buffer_index_, sizeof(*buffer_index_));
+
+ if (bytes_received != sizeof(*buffer_index_)) {
+ DCHECK_EQ(bytes_received, 0U);
+ return;
+ }
+
+ FSMEventArgs event_args(EVENT_AUDIO_DATA);
+ // Convert audio from native format to fixed format used by WebSpeech.
+ event_args.audio_data = audio_converter_->Convert(audio_track_bus_.get());
+ // Process on browser thread.
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&SpeechRecognizerImpl::DispatchEvent, this, event_args));
+
+ // Notify host that the buffer was consumed via shared memory.
+ (*buffer_index_)++;
+
+ // Post this task for next round of data.
+ audio_sync_thread_->message_loop_proxy()->PostTask(
+ FROM_HERE, base::Bind(&SpeechRecognizerImpl::OnAudioTrackData,
+ base::Unretained(this)));
+}
+
SpeechRecognizerImpl::FSMState
SpeechRecognizerImpl::StartRecognitionEngine(const FSMEventArgs& event_args) {
// This is the first audio packet captured, so the recognition engine is
@@ -635,7 +770,7 @@ SpeechRecognizerImpl::StopCaptureAndWaitForResult(const FSMEventArgs&) {
DCHECK(state_ >= STATE_ESTIMATING_ENVIRONMENT && state_ <= STATE_RECOGNIZING);
DVLOG(1) << "Concluding recognition";
- CloseAudioControllerAsynchronously();
+ if (!using_audio_track_) CloseAudioControllerAsynchronously();
recognition_engine_->AudioChunksEnded();
if (state_ > STATE_WAITING_FOR_SPEECH)
@@ -664,7 +799,7 @@ SpeechRecognizerImpl::AbortWithError(const FSMEventArgs& event_args) {
SpeechRecognizerImpl::FSMState SpeechRecognizerImpl::Abort(
const SpeechRecognitionError& error) {
- if (IsCapturingAudio())
+ if (IsCapturingAudio() && !using_audio_track_)
CloseAudioControllerAsynchronously();
DVLOG(1) << "SpeechRecognizerImpl canceling recognition. ";
@@ -770,11 +905,13 @@ SpeechRecognizerImpl::NotFeasible(const FSMEventArgs& event_args) {
void SpeechRecognizerImpl::CloseAudioControllerAsynchronously() {
DCHECK(IsCapturingAudio());
+ DCHECK(!using_audio_track_);
DVLOG(1) << "SpeechRecognizerImpl closing audio controller.";
// Issues a Close on the audio controller, passing an empty callback. The only
// purpose of such callback is to keep the audio controller refcounted until
// Close has completed (in the audio thread) and automatically destroy it
// afterwards (upon return from OnAudioClosed).
+
audio_controller_->Close(base::Bind(&SpeechRecognizerImpl::OnAudioClosed,
this, audio_controller_));
audio_controller_ = NULL; // The controller is still refcounted by Bind.
« no previous file with comments | « no previous file | content/renderer/media/speech_recognition_audio_source_provider.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698