| 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 "base/threading/thread.h" |
| 10 #include "base/threading/thread_restrictions.h" |
| 11 #include "media/audio/audio_parameters.h" |
| 12 #include "media/base/audio_bus.h" |
| 13 |
| 14 namespace content { |
| 15 |
| 16 SpeechRecognitionAudioReceiver::SpeechRecognitionAudioReceiver( |
| 17 const media::AudioParameters& params, |
| 18 const base::ProcessHandle peer_process_handle, |
| 19 const base::TimeDelta receive_timeout, |
| 20 const OnDataCB& on_data_callback, |
| 21 base::SharedMemoryHandle* foreign_memory_handle, |
| 22 base::SyncSocket::TransitDescriptor* foreign_socket_descriptor) : |
| 23 receive_timeout_(receive_timeout), |
| 24 dropped_packets_(0) { |
| 25 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 26 DCHECK_LT(receive_timeout.InMilliseconds(), 1000); |
| 27 DCHECK(foreign_memory_handle); |
| 28 DCHECK(foreign_socket_descriptor); |
| 29 |
| 30 // Create the shared memory and share with the renderer process. |
| 31 uint32 shared_memory_size = sizeof(media::AudioInputBufferParameters) + |
| 32 media::AudioBus::CalculateMemorySize(params); |
| 33 shared_memory_.reset(new base::SharedMemory()); |
| 34 if (!shared_memory_->CreateAndMapAnonymous(shared_memory_size)) { |
| 35 DLOG(ERROR) << "Failed to create shared memory for the speech recognizer"; |
| 36 shared_memory_.reset(); |
| 37 return; |
| 38 } |
| 39 |
| 40 if (!shared_memory_->ShareToProcess(base::GetCurrentProcessHandle(), |
| 41 foreign_memory_handle)) { |
| 42 DLOG(ERROR) << "Failed to map and share the shared memory"; |
| 43 shared_memory_.reset(); |
| 44 return; |
| 45 } |
| 46 |
| 47 foreign_socket_.reset(new base::CancelableSyncSocket()); |
| 48 audio_socket_.reset(new base::CancelableSyncSocket()); |
| 49 if (!base::SyncSocket::CreatePair(audio_socket_.get(), |
| 50 foreign_socket_.get())) { |
| 51 shared_memory_.reset(); |
| 52 foreign_socket_.reset(); |
| 53 audio_socket_.reset(); |
| 54 DLOG(ERROR) << "Cannot create SyncSocket pair"; |
| 55 return; |
| 56 } |
| 57 |
| 58 if (!foreign_socket_->PrepareTransitDescriptor(peer_process_handle, |
| 59 foreign_socket_descriptor)) { |
| 60 shared_memory_.reset(); |
| 61 foreign_socket_.reset(); |
| 62 audio_socket_.reset(); |
| 63 DLOG(ERROR) << "Cannot prepare syncsocket transit descriptor"; |
| 64 return; |
| 65 } |
| 66 |
| 67 media::AudioInputBuffer* buffer = |
| 68 static_cast<media::AudioInputBuffer*>(shared_memory_->memory()); |
| 69 audio_bus_ = media::AudioBus::WrapMemory(params, buffer->audio); |
| 70 |
| 71 // Use |buffer->params.size| from shared memory to synchronize by counting |
| 72 // audio buffers when receiving from the renderer. |
| 73 buffer_index_ = &(buffer->params.size); |
| 74 *buffer_index_ = 0U; |
| 75 |
| 76 // The thread check will be first invoked in |OnData()|. |
| 77 audio_thread_checker_.DetachFromThread(); |
| 78 audio_thread_.reset(new base::Thread("SpeechRecognitionAudioTrackThread")); |
| 79 audio_thread_->Start(); |
| 80 |
| 81 on_data_cb_ = on_data_callback; |
| 82 |
| 83 listening_ = audio_thread_->task_runner()->PostTask( |
| 84 FROM_HERE, base::Bind(&SpeechRecognitionAudioReceiver::OnData, |
| 85 base::Unretained(this))); |
| 86 DCHECK(listening_); |
| 87 } |
| 88 |
| 89 SpeechRecognitionAudioReceiver::~SpeechRecognitionAudioReceiver() { |
| 90 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 91 DCHECK(audio_thread_.get()); |
| 92 |
| 93 // We need to stop the |audio_thread_| to properly clean up. |
| 94 // We must allow the IO until the task in the thread's message loop completes. |
| 95 // This should execute almost immediately in most of cases since we expect |
| 96 // the sockets were closed and |OnData()| returned without posting a new task. |
| 97 const bool previous_io_allowed = base::ThreadRestrictions::SetIOAllowed(true); |
| 98 |
| 99 // Make sure we close the socket so the thread doesn't block on it. |
| 100 audio_socket_->Shutdown(); |
| 101 audio_thread_->Stop(); |
| 102 base::ThreadRestrictions::SetIOAllowed(previous_io_allowed); |
| 103 } |
| 104 |
| 105 bool SpeechRecognitionAudioReceiver::listening() { |
| 106 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 107 return listening_; |
| 108 } |
| 109 |
| 110 void SpeechRecognitionAudioReceiver::OnData() { |
| 111 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 112 DCHECK(audio_thread_checker_.CalledOnValidThread()); |
| 113 |
| 114 // Maximum of consecutive dropped packets before we abort posting new tasks. |
| 115 // Since |ReceiveWithTimeout()| can return 0U if either the socket was closed |
| 116 // or a timeout occured, this constant is used to handle both situations: |
| 117 // 1) If the renderer is slow to start sending, we allow 2 timeouts. |
| 118 // E.g. if audio chunk is 200 ms and timeout is 400 ms, we allow 800 ms. |
| 119 // 2) If we haven't received any data, we will try to repost the task |
| 120 // 2 times before we abort. |
| 121 // E.g. The socket was closed before we received any data. This happens |
| 122 // if the user refreshes the page (closing the socket) before we get the |
| 123 // the first packet. |
| 124 const uint32 kMaxDroppedPackets = 2; |
| 125 |
| 126 // Receive with timeout not to block indefinitely. |
| 127 size_t bytes_received = audio_socket_->ReceiveWithTimeout( |
| 128 buffer_index_, sizeof(*buffer_index_), receive_timeout_); |
| 129 |
| 130 // Message must be either complete or empty. |
| 131 DCHECK_EQ(0U, bytes_received % sizeof(*buffer_index_)); |
| 132 |
| 133 if (bytes_received == sizeof(*buffer_index_)) { |
| 134 |
| 135 // Any previous issues with the socket are ignored. |
| 136 dropped_packets_ = 0; |
| 137 |
| 138 // Execute the client callback on this |audio_thread_|. |
| 139 on_data_cb_.Run(NULL, audio_bus_.get()); |
| 140 |
| 141 // Notify host via shared memory that the buffer was consumed. |
| 142 ++(*buffer_index_); |
| 143 |
| 144 } else if (++dropped_packets_ > kMaxDroppedPackets) { |
| 145 audio_socket_->Shutdown(); |
| 146 |
| 147 return; |
| 148 } |
| 149 |
| 150 // Post this task again to |audio_thread_| for next round of data. |
| 151 base::MessageLoopProxy::current()->PostTask( |
| 152 FROM_HERE, base::Bind(&SpeechRecognitionAudioReceiver::OnData, |
| 153 base::Unretained(this))); |
| 154 } |
| 155 |
| 156 } // namespace content |
| OLD | NEW |