| OLD | NEW |
| (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 |
| OLD | NEW |