Chromium Code Reviews| 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, | |
|
no longer working on chromium
2014/10/17 12:28:10
fix the indentation.
burnik
2014/10/23 13:56:36
Done.
| |
| 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 dropped_packets_(0), | |
| 52 weak_ptr_factory_(this), | |
| 53 listening_(false) { | |
| 54 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 55 DCHECK(task_runner); | |
| 56 } | |
| 57 | |
| 58 SpeechRecognitionAudioReceiver::~SpeechRecognitionAudioReceiver() { | |
| 59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 60 | |
| 61 // Make sure no new tasks are getting posted to the |task_runner_| and that we | |
| 62 // close the socket since the receiver is going away. | |
| 63 Stop(); | |
| 64 } | |
| 65 | |
| 66 bool SpeechRecognitionAudioReceiver::Initialize( | |
| 67 const base::ProcessHandle peer_process_handle, | |
| 68 base::SharedMemoryHandle* foreign_memory_handle, | |
| 69 base::SyncSocket::TransitDescriptor* foreign_socket_descriptor) { | |
| 70 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 71 DCHECK(foreign_memory_handle); | |
| 72 DCHECK(foreign_socket_descriptor); | |
| 73 DCHECK(!IsListening()); | |
| 74 | |
| 75 // All clean up on failure will be done by the destructor. | |
| 76 const size_t kSharedMemorySize = sizeof(media::AudioInputBufferParameters) + | |
| 77 media::AudioBus::CalculateMemorySize(audio_params_); | |
| 78 shared_memory_.reset(new base::SharedMemory()); | |
| 79 if (!shared_memory_->CreateAndMapAnonymous(kSharedMemorySize)) { | |
| 80 DLOG(ERROR) << "Failed to create shared memory for the speech recognizer"; | |
| 81 return false; | |
| 82 } | |
| 83 memset(shared_memory_->memory(), 0, kSharedMemorySize); | |
| 84 | |
| 85 if (!shared_memory_->ShareToProcess(base::GetCurrentProcessHandle(), | |
| 86 foreign_memory_handle)) { | |
| 87 DLOG(ERROR) << "Failed to map and share the shared memory"; | |
| 88 return false; | |
| 89 } | |
| 90 | |
| 91 foreign_socket_.reset(new base::CancelableSyncSocket()); | |
| 92 receiving_socket_.reset(new base::CancelableSyncSocket()); | |
| 93 if (!base::SyncSocket::CreatePair(receiving_socket_.get(), | |
| 94 foreign_socket_.get())) { | |
| 95 DLOG(ERROR) << "Cannot create SyncSocket pair"; | |
| 96 return false; | |
| 97 } | |
| 98 | |
| 99 if (!foreign_socket_->PrepareTransitDescriptor(peer_process_handle, | |
| 100 foreign_socket_descriptor)) { | |
| 101 DLOG(ERROR) << "Cannot prepare syncsocket transit descriptor"; | |
| 102 return false; | |
| 103 } | |
| 104 | |
| 105 media::AudioInputBuffer* audio_buffer = | |
| 106 static_cast<media::AudioInputBuffer*>(shared_memory_->memory()); | |
| 107 audio_bus_ = media::AudioBus::WrapMemory(audio_params_, audio_buffer->audio); | |
| 108 audio_bus_->Zero(); | |
| 109 | |
| 110 // Use |buffer->params.size| from shared memory to synchronize by counting | |
| 111 // audio buffers when receiving from the renderer. | |
| 112 audio_buffer->params.size = 0; | |
| 113 | |
| 114 thread_checker_.DetachFromThread(); | |
| 115 | |
| 116 return true; | |
| 117 } | |
| 118 | |
| 119 void SpeechRecognitionAudioReceiver::Listen() { | |
| 120 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 121 if (IsListening()) | |
| 122 return; | |
| 123 | |
| 124 // It will take at least 2 * |audio_chunk_duration_| ms for the producer to | |
| 125 // send the first packet, therefore we delay the task. We also allow a 100% | |
| 126 // time overhead on the socket receive timeout because the first packet | |
| 127 // usually arrives late (depending on the IPC and the renderer). | |
| 128 task_runner_->PostDelayedTask( | |
| 129 FROM_HERE, | |
| 130 base::Bind(&SpeechRecognitionAudioReceiver::Receive, | |
| 131 weak_ptr_factory_.GetWeakPtr(), | |
| 132 audio_chunk_duration_), | |
| 133 2 * audio_chunk_duration_); | |
| 134 | |
| 135 SetListening(true); | |
| 136 } | |
| 137 | |
| 138 void SpeechRecognitionAudioReceiver::Stop() { | |
|
no longer working on chromium
2014/10/17 12:28:10
you need to invalidate the weak pointer in this me
burnik
2014/10/23 13:56:36
Moved away from the weak ptr factory.
| |
| 139 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 140 if (!IsListening()) | |
| 141 return; | |
| 142 | |
| 143 SetListening(false); | |
| 144 | |
| 145 // We close the socket so the task runner thread doesn't block on it. | |
| 146 receiving_socket_->Shutdown(); | |
| 147 } | |
| 148 | |
| 149 void SpeechRecognitionAudioReceiver::Receive( | |
| 150 const base::TimeDelta& receive_timeout) { | |
| 151 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 152 DCHECK_LT(receive_timeout.InMilliseconds(), 1000); | |
| 153 | |
| 154 // Maximum of consecutive dropped packets. When reached we trigger an error | |
| 155 // and stop reposting the task for |Receive()|. | |
| 156 static const uint32 kMaxDroppedPackets = 2; | |
| 157 static const size_t kSize = sizeof(media::AudioInputBufferParameters().size); | |
|
no longer working on chromium
2014/10/17 12:28:10
use sizeof(GetAudioBuffer()->params.size)
burnik
2014/10/23 13:56:36
Done.
| |
| 158 | |
| 159 // Receive with timeout not to block indefinitely. Ideally we block here for | |
| 160 // no time at all for all packets after the first one. | |
| 161 size_t bytes_received = receiving_socket_->ReceiveWithTimeout( | |
| 162 &(GetAudioBuffer()->params.size), kSize, receive_timeout); | |
| 163 DCHECK_EQ(0U, bytes_received % kSize); | |
| 164 | |
| 165 // Register time to account for any delay before we repost the task. | |
| 166 const base::TimeTicks last_receive_time = base::TimeTicks::Now(); | |
| 167 | |
| 168 if (bytes_received == kSize) { | |
| 169 // Any previous issues with the socket are ignored. | |
| 170 dropped_packets_ = 0; | |
| 171 | |
| 172 // Execute the client callback on this |task_runner_|'s thread. | |
| 173 event_handler_->OnData(NULL, audio_bus_.get()); | |
| 174 | |
| 175 // Notify host via shared memory that the buffer was consumed. | |
| 176 GetAudioBuffer()->params.size++; | |
| 177 } else { | |
| 178 // If Stop() was called we stop reposting the task. | |
| 179 if (!IsListening()) | |
| 180 return; | |
| 181 | |
| 182 // Socket issue: either a timeout occurred or socket is closed. | |
| 183 if (++dropped_packets_ >= kMaxDroppedPackets) { | |
| 184 DLOG(ERROR) << "Dropped max packets"; | |
| 185 receiving_socket_->Shutdown(); | |
| 186 | |
| 187 // We have passed the limit of consecutive dropped packets so we trigger | |
| 188 // the error event. | |
| 189 event_handler_->OnError( | |
| 190 NULL, media::AudioInputController::UNKNOWN_ERROR); | |
| 191 | |
| 192 return; | |
| 193 } | |
| 194 } | |
| 195 | |
| 196 const base::TimeDelta time_delta = base::TimeTicks::Now() - last_receive_time; | |
|
no longer working on chromium
2014/10/17 12:28:10
I am a bit confused here, what does this time_delt
burnik
2014/10/23 13:56:36
Removed timing.
| |
| 197 DCHECK_LE(time_delta.InMilliseconds(), | |
| 198 audio_chunk_duration_.InMilliseconds()); | |
| 199 | |
| 200 // Repost the task with allowing 50% delay on the socket receive. | |
| 201 base::MessageLoopProxy::current()->PostDelayedTask( | |
| 202 FROM_HERE, | |
| 203 base::Bind(&SpeechRecognitionAudioReceiver::Receive, | |
| 204 weak_ptr_factory_.GetWeakPtr(), | |
| 205 audio_chunk_duration_ / 2), | |
| 206 audio_chunk_duration_ - time_delta); | |
| 207 } | |
| 208 | |
| 209 media::AudioInputBuffer* SpeechRecognitionAudioReceiver::GetAudioBuffer() { | |
| 210 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 211 DCHECK_GE(shared_memory_->mapped_size(), shared_memory_->requested_size()); | |
| 212 return static_cast<media::AudioInputBuffer*>(shared_memory_->memory()); | |
| 213 } | |
| 214 | |
| 215 bool SpeechRecognitionAudioReceiver::IsListening() { | |
| 216 base::AutoLock lock(listening_lock_); | |
| 217 return listening_; | |
| 218 } | |
| 219 | |
| 220 void SpeechRecognitionAudioReceiver::SetListening(const bool listening) { | |
| 221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 222 base::AutoLock lock(listening_lock_); | |
| 223 listening_ = listening; | |
| 224 } | |
| 225 | |
| 226 } // namespace content | |
| OLD | NEW |