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

Side by Side Diff: content/browser/speech/speech_recognition_audio_receiver.cc

Issue 549373003: Binding media stream audio track to speech recognition [browser] Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Audio Receiver unit test Created 6 years, 2 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/speech/speech_recognition_audio_receiver.h"
6
7 #include "base/bind.h"
8 #include "base/process/process_handle.h"
9 #include "base/threading/thread_restrictions.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "media/audio/audio_parameters.h"
12 #include "media/base/audio_bus.h"
13
14 namespace content {
15
16 // static
17 SpeechRecognitionAudioReceiver* SpeechRecognitionAudioReceiver::Create(
no longer working on chromium 2014/10/15 12:14:28 use scoped_ptr<SpeechRecognitionAudioReceiver>
burnik 2014/10/15 14:52:27 Done.
18 const media::AudioParameters& audio_params,
19 const base::ProcessHandle peer_process_handle,
20 media::AudioInputController::EventHandler* event_handler,
21 base::SingleThreadTaskRunner* task_runner,
22 base::SharedMemoryHandle* foreign_memory_handle,
23 base::SyncSocket::TransitDescriptor* foreign_socket_descriptor) {
24 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
25 DCHECK(event_handler);
26
27 scoped_ptr<SpeechRecognitionAudioReceiver> receiver(
28 new SpeechRecognitionAudioReceiver(audio_params,
29 event_handler,
30 task_runner));
31
32 if (receiver->Initialize(peer_process_handle,
33 foreign_memory_handle,
34 foreign_socket_descriptor)) {
35 return receiver.release();
36 } else {
37 return NULL;
38 }
39 }
40
41 SpeechRecognitionAudioReceiver::SpeechRecognitionAudioReceiver(
42 const media::AudioParameters& audio_params,
43 media::AudioInputController::EventHandler* event_handler,
44 base::SingleThreadTaskRunner* task_runner) :
45 audio_params_(audio_params),
46 task_runner_(task_runner),
47 event_handler_(event_handler),
48 dropped_packets_(0),
49 weak_ptr_factory_(this),
50 listening_(false) {
51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
52 }
53
54 SpeechRecognitionAudioReceiver::~SpeechRecognitionAudioReceiver() {
55 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
56
57 // Make sure we don't issue an OnError event since the client is
58 // deleting the object.
59 set_listening(false);
60
61 // We close the socket so the task runner thread doesn't block on it.
62 receiving_socket_->Shutdown();
63
64 // No audio thread indicates the client provided a TaskRunner.
65 if (!audio_thread_.get())
66 return;
67
68 // We need to stop the |audio_thread_| to properly clean up.
69 // We must allow the IO until the task in the thread's message loop completes.
70 // This should execute almost immediately in most of cases since we expect
71 // the sockets were closed and |Receive()| returned without reposting a task.
72 const bool previous_io_allowed = base::ThreadRestrictions::SetIOAllowed(true);
73 audio_thread_->Stop();
74 base::ThreadRestrictions::SetIOAllowed(previous_io_allowed);
75 }
76
77 bool SpeechRecognitionAudioReceiver::Initialize(
78 const base::ProcessHandle peer_process_handle,
79 base::SharedMemoryHandle* foreign_memory_handle,
80 base::SyncSocket::TransitDescriptor* foreign_socket_descriptor) {
81 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
82 DCHECK(foreign_memory_handle);
83 DCHECK(foreign_socket_descriptor);
84 DCHECK(!listening());
85
86 // All clean up on failure will be done by the destructor.
87 const size_t kSharedMemorySize = sizeof(media::AudioInputBufferParameters) +
88 media::AudioBus::CalculateMemorySize(audio_params_);
89 shared_memory_.reset(new base::SharedMemory());
90 if (!shared_memory_->CreateAndMapAnonymous(kSharedMemorySize)) {
91 DLOG(ERROR) << "Failed to create shared memory for the speech recognizer";
92 return false;
93 }
94 memset(shared_memory_->memory(), 0, kSharedMemorySize);
95
96 if (!shared_memory_->ShareToProcess(base::GetCurrentProcessHandle(),
97 foreign_memory_handle)) {
98 DLOG(ERROR) << "Failed to map and share the shared memory";
99 return false;
100 }
101
102 foreign_socket_.reset(new base::CancelableSyncSocket());
103 receiving_socket_.reset(new base::CancelableSyncSocket());
104 if (!base::SyncSocket::CreatePair(receiving_socket_.get(),
105 foreign_socket_.get())) {
106 DLOG(ERROR) << "Cannot create SyncSocket pair";
107 return false;
108 }
109
110 if (!foreign_socket_->PrepareTransitDescriptor(peer_process_handle,
111 foreign_socket_descriptor)) {
112 DLOG(ERROR) << "Cannot prepare syncsocket transit descriptor";
113 return false;
114 }
115
116 media::AudioInputBuffer* audio_buffer =
117 static_cast<media::AudioInputBuffer*>(shared_memory_->memory());
118 audio_bus_ = media::AudioBus::WrapMemory(audio_params_, audio_buffer->audio);
119 audio_bus_->Zero();
120
121 // Use |buffer->params.size| from shared memory to synchronize by counting
122 // audio buffers when receiving from the renderer.
123 audio_buffer->params.size = 0;
124
125 audio_thread_checker_.DetachFromThread();
126
127 // Check if we need a new thread for the task runner.
128 if (!task_runner_) {
129 audio_thread_.reset(new base::Thread("AudioReceiverThread"));
130 audio_thread_->Start();
131 task_runner_ = audio_thread_->task_runner().get();
132 }
133
134 return true;
135 }
136
137 void SpeechRecognitionAudioReceiver::Listen() {
138 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
139 if (listening())
140 return;
141
142 task_runner_->PostTask(FROM_HERE,
143 base::Bind(&SpeechRecognitionAudioReceiver::Receive,
144 weak_ptr_factory_.GetWeakPtr()));
145 set_listening(true);
146 }
147
148 void SpeechRecognitionAudioReceiver::Receive() {
149 DCHECK(audio_thread_checker_.CalledOnValidThread());
150 static const int kAudioChunkLengthMs =
151 (audio_params_.frames_per_buffer() * 1000) / audio_params_.sample_rate();
152
153 // Allow near 100% overhead on receive timeout.
154 // This is well adjusted to the allowed latency on the renderer.
155 // See |kNumberOfBuffersInFifo| in SpeechRecognitionAudioSink implementation.
156 static const base::TimeDelta kReceiveTimeout =
157 base::TimeDelta::FromMilliseconds(kAudioChunkLengthMs * 2);
158 DCHECK_LT(kReceiveTimeout.InMilliseconds(), 1000);
159
160 // Maximum of consecutive dropped packets. When reached we trigger an error
161 // and stop reposting the task for |Receive()|.
162 static const uint32 kMaxDroppedPackets = 2;
163 static const size_t kSize = sizeof(media::AudioInputBufferParameters().size);
164
165 // Receive with timeout not to block indefinitely. Ideally we block here for
166 // |kAudioChunkLengthMs| as long as the socket is open.
167 size_t bytes_received = receiving_socket_->ReceiveWithTimeout(
no longer working on chromium 2014/10/15 12:14:28 I think the best solution here is to use a short t
burnik 2014/10/15 14:52:27 Yeah, I've tried that approach. The delay seems no
168 &(GetAudioBuffer()->params.size), kSize, kReceiveTimeout);
169 DCHECK_EQ(0U, bytes_received % kSize);
170
171 if (bytes_received == kSize) {
172 // Any previous issues with the socket are ignored.
173 dropped_packets_ = 0;
174
175 // Execute the client callback on this |task_runner_|'s thread.
176 event_handler_->OnData(NULL, audio_bus_.get());
177
178 // Notify host via shared memory that the buffer was consumed.
179 GetAudioBuffer()->params.size++;
180 } else {
181 // If destructor was called we stop posting tasks to the thread.
182 if (!listening())
183 return;
184
185 // Socket issue: either a timeout occurred or socket is closed.
186 if (++dropped_packets_ >= kMaxDroppedPackets) {
187 DLOG(ERROR) << "Dropped max packets";
188 receiving_socket_->Shutdown();
189
190 // We have passed the limit of consecutive dropped packets
191 // so we trigger the error event.
192 event_handler_->OnError(
193 NULL, media::AudioInputController::UNKNOWN_ERROR);
194
195 return;
196 }
197 }
198
199 base::MessageLoopProxy::current()->PostTask(
200 FROM_HERE,
201 base::Bind(&SpeechRecognitionAudioReceiver::Receive,
202 weak_ptr_factory_.GetWeakPtr()));
203 }
204
205 media::AudioInputBuffer* SpeechRecognitionAudioReceiver::GetAudioBuffer() {
206 DCHECK(audio_thread_checker_.CalledOnValidThread());
207 DCHECK_GE(shared_memory_->mapped_size(), shared_memory_->requested_size());
208 return static_cast<media::AudioInputBuffer*>(shared_memory_->memory());
209 }
210
211 bool SpeechRecognitionAudioReceiver::listening() {
212 base::AutoLock lock(listening_lock_);
213 return listening_;
214 }
215
216 void SpeechRecognitionAudioReceiver::set_listening(const bool listening) {
217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
218 base::AutoLock lock(listening_lock_);
219 listening_ = listening;
220 }
221
222 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698