| 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 |