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

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: Introduce SpeechRecognitionAudioReceiver 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.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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698