Index: content/renderer/speech_recognition_dispatcher.cc |
diff --git a/content/renderer/speech_recognition_dispatcher.cc b/content/renderer/speech_recognition_dispatcher.cc |
index 178abf4610c286c751047ff46ec252089f2946bb..d176acc25fdfb3ccf81925785ffdd4d1edb97426 100644 |
--- a/content/renderer/speech_recognition_dispatcher.cc |
+++ b/content/renderer/speech_recognition_dispatcher.cc |
@@ -7,7 +7,9 @@ |
#include "base/basictypes.h" |
#include "base/strings/utf_string_conversions.h" |
#include "content/common/speech_recognition_messages.h" |
+#include "content/renderer/media/media_stream_audio_source.h" |
#include "content/renderer/render_view_impl.h" |
+#include "content/renderer/speech_recognition_audio_source_provider.h" |
#include "third_party/WebKit/public/platform/WebString.h" |
#include "third_party/WebKit/public/platform/WebVector.h" |
#include "third_party/WebKit/public/web/WebSpeechGrammar.h" |
@@ -29,6 +31,9 @@ SpeechRecognitionDispatcher::SpeechRecognitionDispatcher( |
RenderViewImpl* render_view) |
: RenderViewObserver(render_view), |
recognizer_client_(NULL), |
+ audio_track_set_(false), |
+ is_allowed_audio_track_(false), |
+ render_loop_(base::MessageLoopProxy::current()), |
next_id_(1) { |
} |
@@ -36,6 +41,7 @@ SpeechRecognitionDispatcher::~SpeechRecognitionDispatcher() { |
} |
void SpeechRecognitionDispatcher::AbortAllRecognitions() { |
+ audio_source_provider_.reset(); |
Send(new SpeechRecognitionHostMsg_AbortAllRequests( |
routing_id())); |
} |
@@ -53,11 +59,39 @@ bool SpeechRecognitionDispatcher::OnMessageReceived( |
IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_Ended, OnRecognitionEnded) |
IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_ResultRetrieved, |
OnResultsRetrieved) |
+ IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_AudioTrackReady, |
+ OnAudioTrackReady) |
+ IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_AudioChunkProcessed, |
+ OnAudioChunkProcessed) |
IPC_MESSAGE_UNHANDLED(handled = false) |
IPC_END_MESSAGE_MAP() |
return handled; |
} |
+void SpeechRecognitionDispatcher::attach( |
+ const blink::WebSpeechRecognitionHandle& handle, |
+ const blink::WebMediaStreamTrack& audio_track, |
+ blink::WebSpeechRecognizerClient* recognizer_client) { |
+ |
+ // Check if track is from an allowed source (microphone only for now) |
+ // TODO(burnik): externalize the policy of allowed track types from dispatcher |
+ DCHECK(audio_track.source().type() == blink::WebMediaStreamSource::TypeAudio); |
+ MediaStreamAudioSource* native_source = |
+ static_cast <MediaStreamAudioSource*>(audio_track.source().extraData()); |
+ StreamDeviceInfo device_info = native_source->device_info(); |
+ is_allowed_audio_track_ = (device_info.device.type == |
+ content::MEDIA_DEVICE_AUDIO_CAPTURE); |
+ |
+ audio_track_ = audio_track; |
+ audio_track_set_ = true; |
+} |
+ |
+void SpeechRecognitionDispatcher::detach( |
+ const blink::WebSpeechRecognitionHandle& handle, |
+ blink::WebSpeechRecognizerClient* recognizer_client) { |
+ audio_track_set_ = false; |
+} |
+ |
void SpeechRecognitionDispatcher::start( |
const WebSpeechRecognitionHandle& handle, |
const WebSpeechRecognitionParams& params, |
@@ -65,6 +99,17 @@ void SpeechRecognitionDispatcher::start( |
DCHECK(!recognizer_client_ || recognizer_client_ == recognizer_client); |
recognizer_client_ = recognizer_client; |
+ // destroy any previous instance not to starve it waiting on chunk ACKs |
+ audio_source_provider_.reset(); |
+ |
+ if (audio_track_set_ && !is_allowed_audio_track_) { |
+ // notify user that the track used is not supported |
+ recognizer_client_->didReceiveError( |
+ handle, |
+ WebString("Provided audioTrack is not supported. Ignoring track."), |
+ WebSpeechRecognizerClient::NotAllowedError); |
+ } |
+ |
SpeechRecognitionHostMsg_StartRequest_Params msg_params; |
for (size_t i = 0; i < params.grammars().size(); ++i) { |
const WebSpeechGrammar& grammar = params.grammars()[i]; |
@@ -78,6 +123,8 @@ void SpeechRecognitionDispatcher::start( |
msg_params.origin_url = params.origin().toString().utf8(); |
msg_params.render_view_id = routing_id(); |
msg_params.request_id = GetOrCreateIDForHandle(handle); |
+ // fall back to default input when the track is not allowed |
+ msg_params.using_audio_track = (audio_track_set_ && is_allowed_audio_track_); |
// The handle mapping will be removed in |OnRecognitionEnd|. |
Send(new SpeechRecognitionHostMsg_StartRequest(msg_params)); |
} |
@@ -85,6 +132,7 @@ void SpeechRecognitionDispatcher::start( |
void SpeechRecognitionDispatcher::stop( |
const WebSpeechRecognitionHandle& handle, |
WebSpeechRecognizerClient* recognizer_client) { |
+ audio_source_provider_.reset(); |
// Ignore a |stop| issued without a matching |start|. |
if (recognizer_client_ != recognizer_client || !HandleExists(handle)) |
return; |
@@ -95,6 +143,7 @@ void SpeechRecognitionDispatcher::stop( |
void SpeechRecognitionDispatcher::abort( |
const WebSpeechRecognitionHandle& handle, |
WebSpeechRecognizerClient* recognizer_client) { |
+ audio_source_provider_.reset(); |
// Ignore an |abort| issued without a matching |start|. |
if (recognizer_client_ != recognizer_client || !HandleExists(handle)) |
return; |
@@ -154,6 +203,7 @@ void SpeechRecognitionDispatcher::OnErrorOccurred( |
recognizer_client_->didReceiveNoMatch(GetHandleFromID(request_id), |
WebSpeechRecognitionResult()); |
} else { |
+ audio_source_provider_.reset(); |
recognizer_client_->didReceiveError( |
GetHandleFromID(request_id), |
WebString(), // TODO(primiano): message? |
@@ -174,6 +224,7 @@ void SpeechRecognitionDispatcher::OnRecognitionEnded(int request_id) { |
// didEnd may call back synchronously to start a new recognition session, |
// and we don't want to delete the handle from the map after that happens. |
handle_map_.erase(request_id); |
+ audio_source_provider_.reset(); |
recognizer_client_->didEnd(handle); |
} |
} |
@@ -211,6 +262,75 @@ void SpeechRecognitionDispatcher::OnResultsRetrieved( |
GetHandleFromID(request_id), final, provisional); |
} |
+void SpeechRecognitionDispatcher::OnAudioError(int request_id){ |
+ // Browser gets notified on render thread |
+ if (!render_loop_->BelongsToCurrentThread()) { |
+ render_loop_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&SpeechRecognitionDispatcher::OnAudioError, |
+ base::Unretained(this), request_id)); |
+ return; |
+ } |
+ audio_source_provider_.reset(); |
+} |
+ |
+void SpeechRecognitionDispatcher::OnAudioTrackReady( |
+ int request_id, |
+ const media::AudioParameters& params, |
+ base::SharedMemoryHandle handle, |
+ uint32 length) { |
+ |
+ // TODO(burnik): Log and DCHECK(!audio_source_provider_). |
+ if (audio_track_.isNull()) { |
+ audio_source_provider_.reset(); |
+ return; |
+ } |
+ |
+ audio_source_provider_.reset( |
+ new SpeechRecognitionAudioSourceProvider( |
+ audio_track_, params, handle, length, |
+ base::Bind(&SpeechRecognitionDispatcher::OnAudioData, |
+ base::Unretained(this), request_id), |
+ base::Bind(&SpeechRecognitionDispatcher::OnAudioError, |
+ base::Unretained(this), request_id))); |
+} |
+ |
+void SpeechRecognitionDispatcher::OnAudioChunkProcessed( |
+ int request_id) { |
+ |
+ // TODO(burnik): Log and DCHECK(!audio_source_provider_). |
+ if (audio_track_.isNull()) |
+ return; |
+ |
+ // discard any message to a destroyed instance |
+ if(!audio_source_provider_.get()) |
+ return; |
+ |
+ audio_source_provider_->NotifyAudioBusConsumed(); |
+} |
+ |
+ |
+// TODO(burnik): Consider using sync_socket |
+void SpeechRecognitionDispatcher::OnAudioData(int request_id) { |
+ // Browser gets notified on render thread |
+ if (!render_loop_->BelongsToCurrentThread()) { |
+ render_loop_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&SpeechRecognitionDispatcher::OnAudioData, |
+ base::Unretained(this), request_id)); |
+ return; |
+ } |
+ // If the handle isn't found in the array, which might happen if the |
+ // recognition has been ended by the browser, delete the |
+ // |audio_source_provider_|. |
+ HandleMap::iterator iter = handle_map_.find(request_id); |
+ if (iter == handle_map_.end()) { |
+ audio_source_provider_.reset(); |
+ return; |
+ } |
+ |
+ Send(new SpeechRecognitionHostMsg_OnAudioTrackData(routing_id(), request_id)); |
+} |
int SpeechRecognitionDispatcher::GetOrCreateIDForHandle( |
const WebSpeechRecognitionHandle& handle) { |