OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/audio/audio_input_device.h" | 5 #include "media/audio/audio_input_device.h" |
6 | 6 |
| 7 #include "base/basictypes.h" |
7 #include "base/bind.h" | 8 #include "base/bind.h" |
8 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
9 #include "base/threading/thread_restrictions.h" | 10 #include "base/threading/thread_restrictions.h" |
10 #include "base/time.h" | 11 #include "base/time.h" |
11 #include "media/audio/audio_manager_base.h" | 12 #include "media/audio/audio_manager_base.h" |
12 #include "media/base/audio_bus.h" | 13 #include "media/base/audio_bus.h" |
13 | 14 |
14 namespace media { | 15 namespace media { |
15 | 16 |
16 // The number of shared memory buffer segments indicated to browser process | 17 // The number of shared memory buffer segments indicated to browser process |
(...skipping 21 matching lines...) Expand all Loading... |
38 virtual void Process(int pending_data) OVERRIDE; | 39 virtual void Process(int pending_data) OVERRIDE; |
39 | 40 |
40 private: | 41 private: |
41 int current_segment_id_; | 42 int current_segment_id_; |
42 CaptureCallback* capture_callback_; | 43 CaptureCallback* capture_callback_; |
43 scoped_ptr<AudioBus> audio_bus_; | 44 scoped_ptr<AudioBus> audio_bus_; |
44 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); | 45 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); |
45 }; | 46 }; |
46 | 47 |
47 AudioInputDevice::AudioInputDevice( | 48 AudioInputDevice::AudioInputDevice( |
48 AudioInputIPC* ipc, | 49 scoped_ptr<AudioInputIPC> ipc, |
49 const scoped_refptr<base::MessageLoopProxy>& io_loop) | 50 const scoped_refptr<base::MessageLoopProxy>& io_loop) |
50 : ScopedLoopObserver(io_loop), | 51 : ScopedLoopObserver(io_loop), |
51 callback_(NULL), | 52 callback_(NULL), |
52 ipc_(ipc), | 53 ipc_(ipc.Pass()), |
53 stream_id_(0), | 54 state_(IDLE), |
54 session_id_(0), | 55 session_id_(0), |
55 agc_is_enabled_(false) { | 56 agc_is_enabled_(false), |
| 57 stopping_hack_(false) { |
56 CHECK(ipc_); | 58 CHECK(ipc_); |
| 59 |
| 60 // The correctness of the code depends on the relative values assigned in the |
| 61 // State enum. |
| 62 COMPILE_ASSERT(IPC_CLOSED < IDLE, invalid_enum_value_assignment_0); |
| 63 COMPILE_ASSERT(IDLE < CREATING_STREAM, invalid_enum_value_assignment_1); |
| 64 COMPILE_ASSERT(CREATING_STREAM < RECORDING, invalid_enum_value_assignment_2); |
57 } | 65 } |
58 | 66 |
59 void AudioInputDevice::Initialize(const AudioParameters& params, | 67 void AudioInputDevice::Initialize(const AudioParameters& params, |
60 CaptureCallback* callback, | 68 CaptureCallback* callback, |
61 int session_id) { | 69 int session_id) { |
| 70 DCHECK(params.IsValid()); |
62 DCHECK(!callback_); | 71 DCHECK(!callback_); |
63 DCHECK_EQ(0, session_id_); | 72 DCHECK_EQ(0, session_id_); |
64 audio_parameters_ = params; | 73 audio_parameters_ = params; |
65 callback_ = callback; | 74 callback_ = callback; |
66 session_id_ = session_id; | 75 session_id_ = session_id; |
67 } | 76 } |
68 | 77 |
69 void AudioInputDevice::Start() { | 78 void AudioInputDevice::Start() { |
| 79 DCHECK(callback_) << "Initialize hasn't been called"; |
70 DVLOG(1) << "Start()"; | 80 DVLOG(1) << "Start()"; |
71 message_loop()->PostTask(FROM_HERE, | 81 message_loop()->PostTask(FROM_HERE, |
72 base::Bind(&AudioInputDevice::InitializeOnIOThread, this)); | 82 base::Bind(&AudioInputDevice::StartUpOnIOThread, this)); |
73 } | 83 } |
74 | 84 |
75 void AudioInputDevice::Stop() { | 85 void AudioInputDevice::Stop() { |
76 DVLOG(1) << "Stop()"; | 86 DVLOG(1) << "Stop()"; |
77 | 87 |
78 { | 88 { |
79 base::AutoLock auto_lock(audio_thread_lock_); | 89 base::AutoLock auto_lock(audio_thread_lock_); |
80 audio_thread_.Stop(MessageLoop::current()); | 90 audio_thread_.Stop(MessageLoop::current()); |
| 91 stopping_hack_ = true; |
81 } | 92 } |
82 | 93 |
83 message_loop()->PostTask(FROM_HERE, | 94 message_loop()->PostTask(FROM_HERE, |
84 base::Bind(&AudioInputDevice::ShutDownOnIOThread, this)); | 95 base::Bind(&AudioInputDevice::ShutDownOnIOThread, this)); |
85 } | 96 } |
86 | 97 |
87 void AudioInputDevice::SetVolume(double volume) { | 98 void AudioInputDevice::SetVolume(double volume) { |
88 if (volume < 0 || volume > 1.0) { | 99 if (volume < 0 || volume > 1.0) { |
89 DLOG(ERROR) << "Invalid volume value specified"; | 100 DLOG(ERROR) << "Invalid volume value specified"; |
90 return; | 101 return; |
(...skipping 16 matching lines...) Expand all Loading... |
107 int length, | 118 int length, |
108 int total_segments) { | 119 int total_segments) { |
109 DCHECK(message_loop()->BelongsToCurrentThread()); | 120 DCHECK(message_loop()->BelongsToCurrentThread()); |
110 #if defined(OS_WIN) | 121 #if defined(OS_WIN) |
111 DCHECK(handle); | 122 DCHECK(handle); |
112 DCHECK(socket_handle); | 123 DCHECK(socket_handle); |
113 #else | 124 #else |
114 DCHECK_GE(handle.fd, 0); | 125 DCHECK_GE(handle.fd, 0); |
115 DCHECK_GE(socket_handle, 0); | 126 DCHECK_GE(socket_handle, 0); |
116 #endif | 127 #endif |
117 DCHECK(length); | 128 DCHECK_GT(length, 0); |
118 DVLOG(1) << "OnStreamCreated (stream_id=" << stream_id_ << ")"; | |
119 | 129 |
120 // We should only get this callback if stream_id_ is valid. If it is not, | 130 if (state_ != CREATING_STREAM) |
121 // the IPC layer should have closed the shared memory and socket handles | 131 return; |
122 // for us and not invoked the callback. The basic assertion is that when | |
123 // stream_id_ is 0 the AudioInputDevice instance is not registered as a | |
124 // delegate and hence it should not receive callbacks. | |
125 DCHECK(stream_id_); | |
126 | 132 |
127 base::AutoLock auto_lock(audio_thread_lock_); | 133 base::AutoLock auto_lock(audio_thread_lock_); |
| 134 // TODO(miu): See TODO in OnStreamCreated method for AudioOutputDevice. |
| 135 // Interface changes need to be made; likely, after AudioInputDevice is merged |
| 136 // into AudioOutputDevice (http://crbug.com/179597). |
| 137 if (stopping_hack_) |
| 138 return; |
128 | 139 |
129 DCHECK(audio_thread_.IsStopped()); | 140 DCHECK(audio_thread_.IsStopped()); |
130 audio_callback_.reset( | 141 audio_callback_.reset( |
131 new AudioInputDevice::AudioThreadCallback( | 142 new AudioInputDevice::AudioThreadCallback( |
132 audio_parameters_, handle, length, total_segments, callback_)); | 143 audio_parameters_, handle, length, total_segments, callback_)); |
133 audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioInputDevice"); | 144 audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioInputDevice"); |
134 | 145 |
135 MessageLoop::current()->PostTask(FROM_HERE, | 146 state_ = RECORDING; |
136 base::Bind(&AudioInputDevice::StartOnIOThread, this)); | 147 ipc_->RecordStream(); |
137 } | 148 } |
138 | 149 |
139 void AudioInputDevice::OnVolume(double volume) { | 150 void AudioInputDevice::OnVolume(double volume) { |
140 NOTIMPLEMENTED(); | 151 NOTIMPLEMENTED(); |
141 } | 152 } |
142 | 153 |
143 void AudioInputDevice::OnStateChanged( | 154 void AudioInputDevice::OnStateChanged( |
144 AudioInputIPCDelegate::State state) { | 155 AudioInputIPCDelegate::State state) { |
145 DCHECK(message_loop()->BelongsToCurrentThread()); | 156 DCHECK(message_loop()->BelongsToCurrentThread()); |
146 | 157 |
147 // Do nothing if the stream has been closed. | 158 // Do nothing if the stream has been closed. |
148 if (!stream_id_) | 159 if (state_ < CREATING_STREAM) |
149 return; | 160 return; |
150 | 161 |
| 162 // TODO(miu): Clean-up inconsistent and incomplete handling here. |
| 163 // http://crbug.com/180640 |
151 switch (state) { | 164 switch (state) { |
152 case AudioInputIPCDelegate::kStopped: | 165 case AudioInputIPCDelegate::kStopped: |
153 // TODO(xians): Should we just call ShutDownOnIOThread here instead? | 166 ShutDownOnIOThread(); |
154 ipc_->RemoveDelegate(stream_id_); | |
155 | |
156 audio_thread_.Stop(MessageLoop::current()); | |
157 audio_callback_.reset(); | |
158 | |
159 stream_id_ = 0; | |
160 break; | 167 break; |
161 case AudioInputIPCDelegate::kRecording: | 168 case AudioInputIPCDelegate::kRecording: |
162 NOTIMPLEMENTED(); | 169 NOTIMPLEMENTED(); |
163 break; | 170 break; |
164 case AudioInputIPCDelegate::kError: | 171 case AudioInputIPCDelegate::kError: |
165 DLOG(WARNING) << "AudioInputDevice::OnStateChanged(kError)"; | 172 DLOG(WARNING) << "AudioInputDevice::OnStateChanged(kError)"; |
166 // Don't dereference the callback object if the audio thread | 173 // Don't dereference the callback object if the audio thread |
167 // is stopped or stopping. That could mean that the callback | 174 // is stopped or stopping. That could mean that the callback |
168 // object has been deleted. | 175 // object has been deleted. |
169 // TODO(tommi): Add an explicit contract for clearing the callback | 176 // TODO(tommi): Add an explicit contract for clearing the callback |
170 // object. Possibly require calling Initialize again or provide | 177 // object. Possibly require calling Initialize again or provide |
171 // a callback object via Start() and clear it in Stop(). | 178 // a callback object via Start() and clear it in Stop(). |
172 if (!audio_thread_.IsStopped()) | 179 if (!audio_thread_.IsStopped()) |
173 callback_->OnCaptureError(); | 180 callback_->OnCaptureError(); |
174 break; | 181 break; |
175 default: | 182 default: |
176 NOTREACHED(); | 183 NOTREACHED(); |
177 break; | 184 break; |
178 } | 185 } |
179 } | 186 } |
180 | 187 |
181 void AudioInputDevice::OnIPCClosed() { | 188 void AudioInputDevice::OnIPCClosed() { |
182 ipc_ = NULL; | 189 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 190 state_ = IPC_CLOSED; |
| 191 ipc_.reset(); |
183 } | 192 } |
184 | 193 |
185 AudioInputDevice::~AudioInputDevice() { | 194 AudioInputDevice::~AudioInputDevice() { |
186 // TODO(henrika): The current design requires that the user calls | 195 // TODO(henrika): The current design requires that the user calls |
187 // Stop before deleting this class. | 196 // Stop before deleting this class. |
188 CHECK_EQ(0, stream_id_); | 197 DCHECK(audio_thread_.IsStopped()); |
189 } | 198 } |
190 | 199 |
191 void AudioInputDevice::InitializeOnIOThread() { | 200 void AudioInputDevice::StartUpOnIOThread() { |
192 DCHECK(message_loop()->BelongsToCurrentThread()); | 201 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 202 |
193 // Make sure we don't call Start() more than once. | 203 // Make sure we don't call Start() more than once. |
194 DCHECK_EQ(0, stream_id_); | 204 if (state_ != IDLE) |
195 if (stream_id_) | |
196 return; | 205 return; |
197 | 206 |
198 if (session_id_ <= 0) { | 207 if (session_id_ <= 0) { |
199 DLOG(WARNING) << "Invalid session id for the input stream " << session_id_; | 208 DLOG(WARNING) << "Invalid session id for the input stream " << session_id_; |
200 return; | 209 return; |
201 } | 210 } |
202 | 211 |
203 stream_id_ = ipc_->AddDelegate(this); | 212 state_ = CREATING_STREAM; |
204 ipc_->CreateStream(stream_id_, session_id_, audio_parameters_, | 213 ipc_->CreateStream(this, session_id_, audio_parameters_, |
205 agc_is_enabled_, kRequestedSharedMemoryCount); | 214 agc_is_enabled_, kRequestedSharedMemoryCount); |
206 } | 215 } |
207 | 216 |
208 void AudioInputDevice::StartOnIOThread() { | |
209 DCHECK(message_loop()->BelongsToCurrentThread()); | |
210 if (stream_id_) | |
211 ipc_->RecordStream(stream_id_); | |
212 } | |
213 | |
214 void AudioInputDevice::ShutDownOnIOThread() { | 217 void AudioInputDevice::ShutDownOnIOThread() { |
215 DCHECK(message_loop()->BelongsToCurrentThread()); | 218 DCHECK(message_loop()->BelongsToCurrentThread()); |
216 // NOTE: |completion| may be NULL. | |
217 // Make sure we don't call shutdown more than once. | |
218 if (stream_id_) { | |
219 if (ipc_) { | |
220 ipc_->CloseStream(stream_id_); | |
221 ipc_->RemoveDelegate(stream_id_); | |
222 } | |
223 | 219 |
224 stream_id_ = 0; | 220 // Close the stream, if we haven't already. |
| 221 if (state_ >= CREATING_STREAM) { |
| 222 ipc_->CloseStream(); |
| 223 state_ = IDLE; |
225 agc_is_enabled_ = false; | 224 agc_is_enabled_ = false; |
226 } | 225 } |
227 | 226 |
228 // We can run into an issue where ShutDownOnIOThread is called right after | 227 // We can run into an issue where ShutDownOnIOThread is called right after |
229 // OnStreamCreated is called in cases where Start/Stop are called before we | 228 // OnStreamCreated is called in cases where Start/Stop are called before we |
230 // get the OnStreamCreated callback. To handle that corner case, we call | 229 // get the OnStreamCreated callback. To handle that corner case, we call |
231 // Stop(). In most cases, the thread will already be stopped. | 230 // Stop(). In most cases, the thread will already be stopped. |
| 231 // |
232 // Another situation is when the IO thread goes away before Stop() is called | 232 // Another situation is when the IO thread goes away before Stop() is called |
233 // in which case, we cannot use the message loop to close the thread handle | 233 // in which case, we cannot use the message loop to close the thread handle |
234 // and can't not rely on the main thread existing either. | 234 // and can't not rely on the main thread existing either. |
| 235 base::AutoLock auto_lock_(audio_thread_lock_); |
235 base::ThreadRestrictions::ScopedAllowIO allow_io; | 236 base::ThreadRestrictions::ScopedAllowIO allow_io; |
236 audio_thread_.Stop(NULL); | 237 audio_thread_.Stop(NULL); |
237 audio_callback_.reset(); | 238 audio_callback_.reset(); |
238 session_id_ = 0; | 239 stopping_hack_ = false; |
239 } | 240 } |
240 | 241 |
241 void AudioInputDevice::SetVolumeOnIOThread(double volume) { | 242 void AudioInputDevice::SetVolumeOnIOThread(double volume) { |
242 DCHECK(message_loop()->BelongsToCurrentThread()); | 243 DCHECK(message_loop()->BelongsToCurrentThread()); |
243 if (stream_id_) | 244 if (state_ >= CREATING_STREAM) |
244 ipc_->SetVolume(stream_id_, volume); | 245 ipc_->SetVolume(volume); |
245 } | 246 } |
246 | 247 |
247 void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) { | 248 void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) { |
248 DCHECK(message_loop()->BelongsToCurrentThread()); | 249 DCHECK(message_loop()->BelongsToCurrentThread()); |
249 DCHECK_EQ(0, stream_id_) << | 250 |
250 "The AGC state can not be modified while capturing is active."; | 251 if (state_ >= CREATING_STREAM) { |
251 if (stream_id_) | 252 DLOG(WARNING) << "The AGC state can not be modified after starting."; |
252 return; | 253 return; |
| 254 } |
253 | 255 |
254 // We simply store the new AGC setting here. This value will be used when | 256 // We simply store the new AGC setting here. This value will be used when |
255 // a new stream is initialized and by GetAutomaticGainControl(). | 257 // a new stream is initialized and by GetAutomaticGainControl(). |
256 agc_is_enabled_ = enabled; | 258 agc_is_enabled_ = enabled; |
257 } | 259 } |
258 | 260 |
259 void AudioInputDevice::WillDestroyCurrentMessageLoop() { | 261 void AudioInputDevice::WillDestroyCurrentMessageLoop() { |
260 LOG(ERROR) << "IO loop going away before the input device has been stopped"; | 262 LOG(ERROR) << "IO loop going away before the input device has been stopped"; |
261 ShutDownOnIOThread(); | 263 ShutDownOnIOThread(); |
262 } | 264 } |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
304 // with nominal range -1.0 -> +1.0. | 306 // with nominal range -1.0 -> +1.0. |
305 audio_bus_->FromInterleaved(memory, audio_bus_->frames(), bytes_per_sample); | 307 audio_bus_->FromInterleaved(memory, audio_bus_->frames(), bytes_per_sample); |
306 | 308 |
307 // Deliver captured data to the client in floating point format | 309 // Deliver captured data to the client in floating point format |
308 // and update the audio-delay measurement. | 310 // and update the audio-delay measurement. |
309 capture_callback_->Capture(audio_bus_.get(), | 311 capture_callback_->Capture(audio_bus_.get(), |
310 audio_delay_milliseconds, volume); | 312 audio_delay_milliseconds, volume); |
311 } | 313 } |
312 | 314 |
313 } // namespace media | 315 } // namespace media |
OLD | NEW |