| 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..d6fbce31eaa96007d516c75d9469dbfe4be69fa7 100644
|
| --- a/content/browser/speech/speech_recognizer_impl.cc
|
| +++ b/content/browser/speech/speech_recognizer_impl.cc
|
| @@ -6,11 +6,14 @@
|
|
|
| #include "base/basictypes.h"
|
| #include "base/bind.h"
|
| +#include "base/memory/shared_memory.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/browser/speech/speech_recognition_audio_receiver.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"
|
| @@ -41,6 +44,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 +61,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 +129,7 @@ SpeechRecognizerImpl::OnDataConverter::OnDataConverter(
|
| waiting_for_input_(false),
|
| converted_data_(new uint8[output_parameters_.GetBytesPerBuffer()]) {
|
| audio_converter_.AddInput(this);
|
| + need_conversion_ = !input_parameters_.Equals(output_parameters_);
|
| }
|
|
|
| SpeechRecognizerImpl::OnDataConverter::~OnDataConverter() {
|
| @@ -136,10 +142,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 +186,8 @@ SpeechRecognizerImpl::SpeechRecognizerImpl(
|
| int session_id,
|
| bool continuous,
|
| bool provisional_results,
|
| + bool using_audio_receiver,
|
| + base::ProcessHandle peer_process_handle,
|
| SpeechRecognitionEngine* engine)
|
| : SpeechRecognizer(listener, session_id),
|
| recognition_engine_(engine),
|
| @@ -183,6 +196,8 @@ SpeechRecognizerImpl::SpeechRecognizerImpl(
|
| media::AudioLogFactory::AUDIO_INPUT_CONTROLLER)),
|
| is_dispatching_event_(false),
|
| provisional_results_(provisional_results),
|
| + using_audio_receiver_(using_audio_receiver),
|
| + peer_process_handle_(peer_process_handle),
|
| state_(STATE_IDLE) {
|
| DCHECK(recognition_engine_ != NULL);
|
| if (!continuous) {
|
| @@ -214,7 +229,7 @@ SpeechRecognizerImpl::SpeechRecognizerImpl(
|
| // synchronous callbacks.
|
|
|
| void SpeechRecognizerImpl::StartRecognition(const std::string& device_id) {
|
| - DCHECK(!device_id.empty());
|
| + DCHECK(!device_id.empty() ^ using_audio_receiver_);
|
| device_id_ = device_id;
|
|
|
| BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
|
| @@ -245,7 +260,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_receiver_ || (audio_controller_.get() != NULL))) ||
|
| (!is_capturing_audio && audio_controller_.get() == NULL));
|
| return is_capturing_audio;
|
| }
|
| @@ -262,6 +278,7 @@ SpeechRecognizerImpl::~SpeechRecognizerImpl() {
|
| audio_controller_->Close(
|
| base::Bind(&KeepAudioControllerRefcountedForDtor, audio_controller_));
|
| audio_log_->OnClosed(0);
|
| + audio_receiver_.reset();
|
| }
|
| }
|
|
|
| @@ -482,7 +499,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 +546,16 @@ SpeechRecognizerImpl::StartRecording(const FSMEventArgs&) {
|
| AudioParameters::AUDIO_PCM_LOW_LATENCY, kChannelLayout, kAudioSampleRate,
|
| kNumBitsPerAudioSample, frames_per_buffer);
|
|
|
| + // Check if using audio receiver as source (audio data processed on renderer).
|
| + // If |StartRecordingUsingAudioReceiver()| fails, we abort the recognition.
|
| + if (using_audio_receiver_) {
|
| + return StartRecordingUsingAudioReceiver(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 +612,52 @@ SpeechRecognizerImpl::StartRecording(const FSMEventArgs&) {
|
| return STATE_STARTING;
|
| }
|
|
|
| +bool SpeechRecognizerImpl::StartRecordingUsingAudioReceiver(
|
| + const media::AudioParameters& params) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| +
|
| + // 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();
|
| +
|
| + // Allow 100% overhead on receive timeout.
|
| + // This is well adjusted to the allowed latency on the renderer.
|
| + // See |kNumberOfBuffersInFifo| in SpeechRecognitionAudioSink implementation.
|
| + const int64 kReceiveTimeoutMs =
|
| + 2 * recognition_engine_->GetDesiredAudioChunkDurationMs();
|
| +
|
| + // Start receiving audio, incoming data will be posted to |OnAudioTrackData|.
|
| + base::SharedMemoryHandle foreign_memory_handle;
|
| + base::SyncSocket::TransitDescriptor foreign_socket_descriptor;
|
| + audio_receiver_.reset(new SpeechRecognitionAudioReceiver(
|
| + params,
|
| + peer_process_handle_,
|
| + base::TimeDelta::FromMilliseconds(kReceiveTimeoutMs),
|
| + base::Bind(&SpeechRecognizerImpl::OnData, base::Unretained(this)),
|
| + &foreign_memory_handle,
|
| + &foreign_socket_descriptor));
|
| +
|
| + // Check if the receiver successfully initialized and started listening.
|
| + if (!audio_receiver_->listening()) {
|
| + audio_converter_.reset();
|
| + audio_receiver_.reset();
|
| + return false;
|
| + }
|
| +
|
| + listener()->OnSharedAudioBusReady(session_id(),
|
| + params,
|
| + foreign_memory_handle,
|
| + foreign_socket_descriptor);
|
| +
|
| + return true;
|
| +}
|
| +
|
| SpeechRecognizerImpl::FSMState
|
| SpeechRecognizerImpl::StartRecognitionEngine(const FSMEventArgs& event_args) {
|
| // This is the first audio packet captured, so the recognition engine is
|
| @@ -667,6 +740,7 @@ SpeechRecognizerImpl::FSMState SpeechRecognizerImpl::Abort(
|
| if (IsCapturingAudio())
|
| CloseAudioControllerAsynchronously();
|
|
|
| +
|
| DVLOG(1) << "SpeechRecognizerImpl canceling recognition. ";
|
|
|
| // The recognition engine is initialized only after STATE_STARTING.
|
| @@ -770,6 +844,12 @@ SpeechRecognizerImpl::NotFeasible(const FSMEventArgs& event_args) {
|
|
|
| void SpeechRecognizerImpl::CloseAudioControllerAsynchronously() {
|
| DCHECK(IsCapturingAudio());
|
| +
|
| + if (using_audio_receiver_) {
|
| + audio_receiver_.reset();
|
| + return;
|
| + }
|
| +
|
| 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
|
|
|