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

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: Wait with no timeout Created 6 years, 1 month 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 "content/public/browser/browser_thread.h"
10 #include "media/audio/audio_parameters.h"
11 #include "media/base/audio_bus.h"
12
13 namespace content {
14
15 // static
16 scoped_ptr<SpeechRecognitionAudioReceiver>
17 SpeechRecognitionAudioReceiver::Create(
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 receiver.reset();
36 }
37
38 return receiver.Pass();
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 audio_chunk_duration_(base::TimeDelta::FromMilliseconds(
47 (audio_params_.frames_per_buffer() * 1000) /
48 audio_params_.sample_rate())),
49 task_runner_(task_runner),
50 event_handler_(event_handler),
51 listening_(false) {
52 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
53 DCHECK(task_runner);
54 }
55
56 SpeechRecognitionAudioReceiver::~SpeechRecognitionAudioReceiver() {
57 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
58 // Make sure Stop() was called before we destroy the object.
59 DCHECK(!IsListening());
60 }
61
62 bool SpeechRecognitionAudioReceiver::Initialize(
63 const base::ProcessHandle peer_process_handle,
64 base::SharedMemoryHandle* foreign_memory_handle,
65 base::SyncSocket::TransitDescriptor* foreign_socket_descriptor) {
66 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
67 DCHECK(foreign_memory_handle);
68 DCHECK(foreign_socket_descriptor);
69 DCHECK(!IsListening());
70
71 // All clean up on failure will be done by the destructor.
72 const size_t kSharedMemorySize = sizeof(media::AudioInputBufferParameters) +
73 media::AudioBus::CalculateMemorySize(audio_params_);
74 shared_memory_.reset(new base::SharedMemory());
75 if (!shared_memory_->CreateAndMapAnonymous(kSharedMemorySize)) {
76 DLOG(ERROR) << "Failed to create shared memory for the speech recognizer";
77 return false;
78 }
79 memset(shared_memory_->memory(), 0, kSharedMemorySize);
80
81 if (!shared_memory_->ShareToProcess(base::GetCurrentProcessHandle(),
82 foreign_memory_handle)) {
83 DLOG(ERROR) << "Failed to map and share the shared memory";
84 return false;
85 }
86
87 foreign_socket_.reset(new base::CancelableSyncSocket());
88 receiving_socket_.reset(new base::CancelableSyncSocket());
89 if (!base::SyncSocket::CreatePair(receiving_socket_.get(),
90 foreign_socket_.get())) {
91 DLOG(ERROR) << "Cannot create SyncSocket pair";
92 return false;
93 }
94
95 if (!foreign_socket_->PrepareTransitDescriptor(peer_process_handle,
96 foreign_socket_descriptor)) {
97 DLOG(ERROR) << "Cannot prepare syncsocket transit descriptor";
98 return false;
99 }
100
101 media::AudioInputBuffer* audio_buffer =
102 static_cast<media::AudioInputBuffer*>(shared_memory_->memory());
103 audio_bus_ = media::AudioBus::WrapMemory(audio_params_, audio_buffer->audio);
104 audio_bus_->Zero();
105
106 // Use |audio_buffer->params.size| from shared memory to synchronize by
107 // counting audio buffers when receiving from the renderer.
108 audio_buffer->params.size = 0;
109 thread_checker_.DetachFromThread();
110
111 return true;
112 }
113
114 void SpeechRecognitionAudioReceiver::StartListening() {
115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
116 if (IsListening())
117 return;
118 start_event_.reset(new base::WaitableEvent(false, false));
119 task_runner_->PostTask(
120 FROM_HERE,
121 base::Bind(&SpeechRecognitionAudioReceiver::Receive,
122 base::Unretained(this)));
123 start_event_->Wait();
124 start_event_.reset();
125 SetListening(true);
126 }
127
128 void SpeechRecognitionAudioReceiver::Stop() {
129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
130 if (!IsListening())
131 return;
132
133 SetListening(false);
134
135 // We close the socket so the task runner thread doesn't block on it.
136 receiving_socket_->Shutdown();
137 }
138
139 void SpeechRecognitionAudioReceiver::Receive() {
140 DCHECK(thread_checker_.CalledOnValidThread());
141
142 // We use the audio buffer wrapping the shared memory to read audio data and
143 // write the buffer index via |audio_buffer->params.size| to synchronize.
144 media::AudioInputBuffer* audio_buffer = GetAudioBuffer();
145
146 // Maximum of consecutive dropped packets. When reached we trigger an error
147 // and stop the thread loop.
148 static const uint32 kMaxDroppedPackets = 2;
149 static const size_t kSize = sizeof(audio_buffer->params.size);
150 uint32 dropped_packets = 0U;
151
152 // The first packet will arrive late, so we use a longer first timeout.
153 base::TimeDelta receive_timeout = base::TimeDelta::FromMilliseconds(900);
154
155 // Signal the event handler that listening has started.
156 event_handler_->OnRecording(NULL);
157
158 // Signal the start event for thread synchronization.
159 DCHECK(start_event_.get());
160 start_event_->Signal();
161
162 while (true) {
163 size_t bytes_received = receiving_socket_->ReceiveWithTimeout(
164 &(audio_buffer->params.size), kSize, receive_timeout);
165 DCHECK_EQ(0U, bytes_received % kSize);
166
167 if (bytes_received == kSize) {
168 // Any previous issues with the socket are ignored.
169 dropped_packets = 0;
170
171 // Execute the client callback on this |task_runner_|'s thread.
172 event_handler_->OnData(NULL, audio_bus_.get());
173
174 // Notify host via shared memory that the buffer was consumed.
175 ++(audio_buffer->params.size);
176 } else {
177 // If Stop() was called we stop the loop.
178 if (!IsListening())
179 return;
180
181 // Socket issue: either a timeout occurred or socket is closed.
182 if (++dropped_packets >= kMaxDroppedPackets) {
183 DLOG(ERROR) << "Dropped max packets";
184 receiving_socket_->Shutdown();
185
186 // We have passed the limit of consecutive dropped packets so we trigger
187 // the error event.
188 event_handler_->OnError(
189 NULL, media::AudioInputController::UNKNOWN_ERROR);
190
191 return;
192 }
193 }
194
195 // All packets after first one use a shorter timeout.
196 receive_timeout = audio_chunk_duration_ * 2;
197 }
198 }
199
200 media::AudioInputBuffer* SpeechRecognitionAudioReceiver::GetAudioBuffer() {
201 DCHECK(thread_checker_.CalledOnValidThread());
202 DCHECK_GE(shared_memory_->mapped_size(), shared_memory_->requested_size());
203 return static_cast<media::AudioInputBuffer*>(shared_memory_->memory());
204 }
205
206 bool SpeechRecognitionAudioReceiver::IsListening() {
207 base::AutoLock lock(listening_lock_);
208 return listening_;
209 }
210
211 void SpeechRecognitionAudioReceiver::SetListening(const bool listening) {
212 base::AutoLock lock(listening_lock_);
213 listening_ = listening;
214 }
215
216 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698