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

Side by Side Diff: media/audio/audio_output_device.cc

Issue 10958004: Prevent AudioDeviceThread from starting if clients have called Stop() (round 2). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 3 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 | Annotate | Revision Log
OLDNEW
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_output_device.h" 5 #include "media/audio/audio_output_device.h"
6 6
7 #include "base/debug/trace_event.h" 7 #include "base/debug/trace_event.h"
8 #include "base/message_loop.h" 8 #include "base/message_loop.h"
9 #include "base/threading/thread_restrictions.h" 9 #include "base/threading/thread_restrictions.h"
10 #include "base/time.h" 10 #include "base/time.h"
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
42 42
43 AudioOutputDevice::AudioOutputDevice( 43 AudioOutputDevice::AudioOutputDevice(
44 AudioOutputIPC* ipc, 44 AudioOutputIPC* ipc,
45 const scoped_refptr<base::MessageLoopProxy>& io_loop) 45 const scoped_refptr<base::MessageLoopProxy>& io_loop)
46 : ScopedLoopObserver(io_loop), 46 : ScopedLoopObserver(io_loop),
47 input_channels_(0), 47 input_channels_(0),
48 callback_(NULL), 48 callback_(NULL),
49 ipc_(ipc), 49 ipc_(ipc),
50 stream_id_(0), 50 stream_id_(0),
51 play_on_start_(true), 51 play_on_start_(true),
52 is_started_(false) { 52 is_started_(false),
53 stopping_(false) {
53 CHECK(ipc_); 54 CHECK(ipc_);
54 } 55 }
55 56
56 void AudioOutputDevice::Initialize(const AudioParameters& params, 57 void AudioOutputDevice::Initialize(const AudioParameters& params,
57 RenderCallback* callback) { 58 RenderCallback* callback) {
58 CHECK_EQ(0, stream_id_) << 59 CHECK_EQ(0, stream_id_) <<
59 "AudioOutputDevice::Initialize() must be called before Start()"; 60 "AudioOutputDevice::Initialize() must be called before Start()";
60 61
61 CHECK(!callback_); // Calling Initialize() twice? 62 CHECK(!callback_); // Calling Initialize() twice?
62 63
(...skipping 20 matching lines...) Expand all
83 DCHECK(callback_) << "Initialize hasn't been called"; 84 DCHECK(callback_) << "Initialize hasn't been called";
84 message_loop()->PostTask(FROM_HERE, 85 message_loop()->PostTask(FROM_HERE,
85 base::Bind(&AudioOutputDevice::CreateStreamOnIOThread, this, 86 base::Bind(&AudioOutputDevice::CreateStreamOnIOThread, this,
86 audio_parameters_, input_channels_)); 87 audio_parameters_, input_channels_));
87 } 88 }
88 89
89 void AudioOutputDevice::Stop() { 90 void AudioOutputDevice::Stop() {
90 { 91 {
91 base::AutoLock auto_lock(audio_thread_lock_); 92 base::AutoLock auto_lock(audio_thread_lock_);
92 audio_thread_.Stop(MessageLoop::current()); 93 audio_thread_.Stop(MessageLoop::current());
94 stopping_ = true;
tommi (sloooow) - chröme 2012/09/20 15:00:22 instead of the variable, you can use audio_thread_
scherkus (not reviewing) 2012/09/20 17:48:28 Not quite... OnStreamCreated() DCHECKs for audio_t
93 } 95 }
94 96
95 message_loop()->PostTask(FROM_HERE, 97 message_loop()->PostTask(FROM_HERE,
96 base::Bind(&AudioOutputDevice::ShutDownOnIOThread, this)); 98 base::Bind(&AudioOutputDevice::ShutDownOnIOThread, this));
97 } 99 }
98 100
99 void AudioOutputDevice::Play() { 101 void AudioOutputDevice::Play() {
100 message_loop()->PostTask(FROM_HERE, 102 message_loop()->PostTask(FROM_HERE,
101 base::Bind(&AudioOutputDevice::PlayOnIOThread, this)); 103 base::Bind(&AudioOutputDevice::PlayOnIOThread, this));
102 } 104 }
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
159 is_started_ = false; 161 is_started_ = false;
160 162
161 if (ipc_) { 163 if (ipc_) {
162 ipc_->CloseStream(stream_id_); 164 ipc_->CloseStream(stream_id_);
163 ipc_->RemoveDelegate(stream_id_); 165 ipc_->RemoveDelegate(stream_id_);
164 } 166 }
165 167
166 stream_id_ = 0; 168 stream_id_ = 0;
167 } 169 }
168 170
169 // We can run into an issue where ShutDownOnIOThread is called right after
170 // OnStreamCreated is called in cases where Start/Stop are called before we
171 // get the OnStreamCreated callback. To handle that corner case, we call
172 // Stop(). In most cases, the thread will already be stopped.
173 // Another situation is when the IO thread goes away before Stop() is called 171 // Another situation is when the IO thread goes away before Stop() is called
174 // in which case, we cannot use the message loop to close the thread handle 172 // in which case, we cannot use the message loop to close the thread handle
175 // and can't not rely on the main thread existing either. 173 // and can't rely on the main thread existing either.
174 base::AutoLock auto_lock_(audio_thread_lock_);
176 base::ThreadRestrictions::ScopedAllowIO allow_io; 175 base::ThreadRestrictions::ScopedAllowIO allow_io;
177 audio_thread_.Stop(NULL); 176 audio_thread_.Stop(NULL);
178 audio_callback_.reset(); 177 audio_callback_.reset();
178 stopping_ = false;
tommi (sloooow) - chröme 2012/09/20 15:00:22 this is one place that's functionally different fr
179 } 179 }
180 180
181 void AudioOutputDevice::SetVolumeOnIOThread(double volume) { 181 void AudioOutputDevice::SetVolumeOnIOThread(double volume) {
182 DCHECK(message_loop()->BelongsToCurrentThread()); 182 DCHECK(message_loop()->BelongsToCurrentThread());
183 if (stream_id_) 183 if (stream_id_)
184 ipc_->SetVolume(stream_id_, volume); 184 ipc_->SetVolume(stream_id_, volume);
185 } 185 }
186 186
187 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegate::State state) { 187 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegate::State state) {
188 DCHECK(message_loop()->BelongsToCurrentThread()); 188 DCHECK(message_loop()->BelongsToCurrentThread());
189 189
190 // Do nothing if the stream has been closed. 190 // Do nothing if the stream has been closed.
191 if (!stream_id_) 191 if (!stream_id_)
192 return; 192 return;
193 193
194 if (state == AudioOutputIPCDelegate::kError) { 194 if (state == AudioOutputIPCDelegate::kError) {
195 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(kError)"; 195 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(kError)";
196 // Don't dereference the callback object if the audio thread 196 // Don't dereference the callback object if the audio thread
197 // is stopped or stopping. That could mean that the callback 197 // is stopped or stopping. That could mean that the callback
198 // object has been deleted. 198 // object has been deleted.
199 // TODO(tommi): Add an explicit contract for clearing the callback 199 // TODO(tommi): Add an explicit contract for clearing the callback
200 // object. Possibly require calling Initialize again or provide 200 // object. Possibly require calling Initialize again or provide
201 // a callback object via Start() and clear it in Stop(). 201 // a callback object via Start() and clear it in Stop().
202 base::AutoLock auto_lock_(audio_thread_lock_);
tommi (sloooow) - chröme 2012/09/20 15:00:22 this isn't needed. IsStopped() does locking intern
scherkus (not reviewing) 2012/09/20 17:48:28 Done.
202 if (!audio_thread_.IsStopped()) 203 if (!audio_thread_.IsStopped())
203 callback_->OnRenderError(); 204 callback_->OnRenderError();
204 } 205 }
205 } 206 }
206 207
207 void AudioOutputDevice::OnStreamCreated( 208 void AudioOutputDevice::OnStreamCreated(
208 base::SharedMemoryHandle handle, 209 base::SharedMemoryHandle handle,
209 base::SyncSocket::Handle socket_handle, 210 base::SyncSocket::Handle socket_handle,
210 int length) { 211 int length) {
211 DCHECK(message_loop()->BelongsToCurrentThread()); 212 DCHECK(message_loop()->BelongsToCurrentThread());
212 #if defined(OS_WIN) 213 #if defined(OS_WIN)
213 DCHECK(handle); 214 DCHECK(handle);
214 DCHECK(socket_handle); 215 DCHECK(socket_handle);
215 #else 216 #else
216 DCHECK_GE(handle.fd, 0); 217 DCHECK_GE(handle.fd, 0);
217 DCHECK_GE(socket_handle, 0); 218 DCHECK_GE(socket_handle, 0);
218 #endif 219 #endif
219 220
220 // We should only get this callback if stream_id_ is valid. If it is not, 221 // We should only get this callback if stream_id_ is valid. If it is not,
221 // the IPC layer should have closed the shared memory and socket handles 222 // the IPC layer should have closed the shared memory and socket handles
222 // for us and not invoked the callback. The basic assertion is that when 223 // for us and not invoked the callback. The basic assertion is that when
223 // stream_id_ is 0 the AudioOutputDevice instance is not registered as a 224 // stream_id_ is 0 the AudioOutputDevice instance is not registered as a
224 // delegate and hence it should not receive callbacks. 225 // delegate and hence it should not receive callbacks.
225 DCHECK(stream_id_); 226 DCHECK(stream_id_);
226 227
228 // Stop() can stop |audio_thread_| before we receive OnStreamCreated().
henrika (OOO until Aug 14) 2012/09/20 07:54:52 Sorry but this sentence is a bit tricky to underst
scherkus (not reviewing) 2012/09/20 17:48:28 Done.
229 // In that case we don't risk starting the thread using |callback_|, which
230 // may have point to freed memory.
tommi (sloooow) - chröme 2012/09/20 15:00:22 If AudioDeviceThread::Stop() has been called, then
scherkus (not reviewing) 2012/09/20 17:48:28 I like all of those ideas but considering this has
231 //
232 // TODO(scherkus): The real fix is to have sane ownership semantics. The fact
henrika (OOO until Aug 14) 2012/09/20 07:54:52 Seems like a very good idea to add a link to crbug
scherkus (not reviewing) 2012/09/20 17:48:28 Done.
233 // that |callback_| (which should own and outlive this object!) can point to
234 // freed memory is a mess. AudioRendererSink should be non-refcounted that
henrika (OOO until Aug 14) 2012/09/20 07:54:52 "that" -> "so that"
scherkus (not reviewing) 2012/09/20 17:48:28 Done.
235 // owners (WebRtcAudioDeviceImpl, AudioRendererImpl, etc...) can Stop() and
236 // delete as they see fit. AudioOutputDevice should internally use WeakPtr
237 // to handle teardown and thread hopping.
227 base::AutoLock auto_lock(audio_thread_lock_); 238 base::AutoLock auto_lock(audio_thread_lock_);
239 if (stopping_)
tommi (sloooow) - chröme 2012/09/20 15:00:22 If we change the Start() vs Initialize() contract,
scherkus (not reviewing) 2012/09/20 17:48:28 I prototyped a change and it appears webrtc can us
240 return;
228 241
229 DCHECK(audio_thread_.IsStopped()); 242 DCHECK(audio_thread_.IsStopped());
230 audio_callback_.reset(new AudioOutputDevice::AudioThreadCallback( 243 audio_callback_.reset(new AudioOutputDevice::AudioThreadCallback(
231 audio_parameters_, input_channels_, handle, length, callback_)); 244 audio_parameters_, input_channels_, handle, length, callback_));
232 audio_thread_.Start(audio_callback_.get(), socket_handle, 245 audio_thread_.Start(audio_callback_.get(), socket_handle,
233 "AudioOutputDevice"); 246 "AudioOutputDevice");
234 247
235 // We handle the case where Play() and/or Pause() may have been called 248 // We handle the case where Play() and/or Pause() may have been called
236 // multiple times before OnStreamCreated() gets called. 249 // multiple times before OnStreamCreated() gets called.
237 is_started_ = true; 250 is_started_ = true;
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after
323 // TODO(dalecurtis): Technically this is not always correct. Due to channel 336 // TODO(dalecurtis): Technically this is not always correct. Due to channel
324 // padding for alignment, there may be more data available than this. We're 337 // padding for alignment, there may be more data available than this. We're
325 // relying on AudioSyncReader::Read() to parse this with that in mind. Rename 338 // relying on AudioSyncReader::Read() to parse this with that in mind. Rename
326 // these methods to Set/GetActualFrameCount(). 339 // these methods to Set/GetActualFrameCount().
327 SetActualDataSizeInBytes( 340 SetActualDataSizeInBytes(
328 &shared_memory_, memory_length_, 341 &shared_memory_, memory_length_,
329 num_frames * sizeof(*output_bus_->channel(0)) * output_bus_->channels()); 342 num_frames * sizeof(*output_bus_->channel(0)) * output_bus_->channels());
330 } 343 }
331 344
332 } // namespace media. 345 } // namespace media.
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698