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

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

Issue 1703473002: Make AudioOutputDevice restartable and reinitializable (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@new_mixing
Patch Set: Code review (tommi@). Created 4 years, 7 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
« no previous file with comments | « media/audio/audio_device_thread.h ('k') | media/audio/audio_input_device.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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_device_thread.h" 5 #include "media/audio/audio_device_thread.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 #include <stdint.h> 8 #include <stdint.h>
9 9
10 #include <algorithm> 10 #include <algorithm>
11 #include <limits> 11 #include <limits>
12 12
13 #include "base/bind.h" 13 #include "base/bind.h"
14 #include "base/logging.h" 14 #include "base/logging.h"
15 #include "base/macros.h" 15 #include "base/macros.h"
16 #include "base/memory/aligned_memory.h" 16 #include "base/memory/aligned_memory.h"
17 #include "base/message_loop/message_loop.h" 17 #include "base/message_loop/message_loop.h"
18 #include "base/numerics/safe_conversions.h" 18 #include "base/numerics/safe_conversions.h"
19 #include "base/synchronization/condition_variable.h"
19 #include "base/threading/platform_thread.h" 20 #include "base/threading/platform_thread.h"
21 #include "base/threading/thread_checker.h"
20 #include "base/threading/thread_restrictions.h" 22 #include "base/threading/thread_restrictions.h"
21 #include "media/base/audio_bus.h" 23 #include "media/base/audio_bus.h"
22 24
23 using base::PlatformThread; 25 using base::PlatformThread;
24 26
25 namespace media { 27 namespace media {
26 28
27 // The actual worker thread implementation. It's very bare bones and much 29 // The actual worker thread implementation. It's very bare bones and much
28 // simpler than SimpleThread (no synchronization in Start, etc) and supports 30 // simpler than SimpleThread (no synchronization in Start, etc) and supports
29 // joining the thread handle asynchronously via a provided message loop even 31 // joining the thread handle asynchronously via a provided message loop even
30 // after the Thread object itself has been deleted. 32 // after the Thread object itself has been deleted.
31 class AudioDeviceThread::Thread 33 class AudioDeviceThread::Thread
32 : public PlatformThread::Delegate, 34 : public PlatformThread::Delegate,
33 public base::RefCountedThreadSafe<AudioDeviceThread::Thread> { 35 public base::RefCountedThreadSafe<AudioDeviceThread::Thread> {
34 public: 36 public:
35 Thread(AudioDeviceThread::Callback* callback, 37 Thread(const char* thread_name, bool synchronized_buffers);
36 base::SyncSocket::Handle socket, 38
37 const char* thread_name, 39 // Starts the thread and suspends it, which means it will wait in
38 bool synchronized_buffers); 40 // ThreadMain(). Resume() must then be called to actually start running it.
39 41 void StartSuspended();
40 void Start(); 42
41 43 // Shuts down the thread. If |loop_for_join| is non-NULL, the function posts
42 // Stops the thread. If |loop_for_join| is non-NULL, the function posts
43 // a task to join (close) the thread handle later instead of waiting for 44 // a task to join (close) the thread handle later instead of waiting for
44 // the thread. If loop_for_join is NULL, then the function waits 45 // the thread. If loop_for_join is NULL, then the function waits
45 // synchronously for the thread to terminate. 46 // synchronously for the thread to terminate.
47 // Must be called before destruction.
46 void Stop(base::MessageLoop* loop_for_join); 48 void Stop(base::MessageLoop* loop_for_join);
47 49
50 // Resumes the thread and starts running it with the given |callback| and
51 // |socket|.
52 void Resume(AudioDeviceThread::Callback* callback,
53 base::SyncSocket::Handle socket);
54
55 // Suspends the thread, it will wait in ThreadMain(). Shuts down the socket.
56 // It can be resumed again by calling Resume().
57 void Suspend();
58
48 private: 59 private:
60 enum State { STATE_SUSPENDED, STATE_PLAYING, STATE_STOPPED };
DaleCurtis 2016/05/17 17:54:35 Instead of adding STATE_ consider making this an "
Henrik Grunell 2016/05/18 12:21:32 Good points, though can't use DCHECK_EQ etc. with
61
49 friend class base::RefCountedThreadSafe<AudioDeviceThread::Thread>; 62 friend class base::RefCountedThreadSafe<AudioDeviceThread::Thread>;
DaleCurtis 2016/05/17 17:54:35 friend goes right under private:
Henrik Grunell 2016/05/18 12:21:32 Done.
63
50 ~Thread() override; 64 ~Thread() override;
51 65
52 // Overrides from PlatformThread::Delegate. 66 // Overrides from PlatformThread::Delegate.
53 void ThreadMain() override; 67 void ThreadMain() override;
54 68
55 // Runs the loop that reads from the socket. 69 // Runs the loop that reads from the socket, called in ThreadMain().
56 void Run(); 70 void Run();
57 71
58 private: 72 // Ensures that Start(), Resume() and Suspend() are called on the same thread.
59 base::PlatformThreadHandle thread_; 73 // Stop() can be called on any thread.
60 AudioDeviceThread::Callback* callback_; 74 base::ThreadChecker thread_checker_;
61 base::CancelableSyncSocket socket_; 75
62 base::Lock callback_lock_;
63 const char* thread_name_; 76 const char* thread_name_;
64 const bool synchronized_buffers_; 77 const bool synchronized_buffers_;
65 78
79 base::Lock lock_; // Protects everything below.
80 base::ConditionVariable exit_suspended_state_;
81 State state_;
82 AudioDeviceThread::Callback* callback_;
83
84 base::PlatformThreadHandle thread_handle_;
85
86 // Reset in ThreadMain(). Shutdown on another thread.
87 std::unique_ptr<base::CancelableSyncSocket> socket_;
88
89 base::SyncSocket::Handle new_socket_handle_;
90
66 DISALLOW_COPY_AND_ASSIGN(Thread); 91 DISALLOW_COPY_AND_ASSIGN(Thread);
67 }; 92 };
68 93
69 // AudioDeviceThread implementation 94 // AudioDeviceThread implementation
70 95 AudioDeviceThread::AudioDeviceThread() {}
71 AudioDeviceThread::AudioDeviceThread() { 96
72 } 97 AudioDeviceThread::~AudioDeviceThread() {
73 98 DCHECK(!thread_.get());
74 AudioDeviceThread::~AudioDeviceThread() { DCHECK(!thread_.get()); } 99 }
75 100
76 void AudioDeviceThread::Start(AudioDeviceThread::Callback* callback, 101 void AudioDeviceThread::StartSuspended(const char* thread_name,
77 base::SyncSocket::Handle socket, 102 bool synchronized_buffers) {
78 const char* thread_name,
79 bool synchronized_buffers) {
80 base::AutoLock auto_lock(thread_lock_); 103 base::AutoLock auto_lock(thread_lock_);
81 CHECK(!thread_.get()); 104 CHECK(!thread_.get());
82 thread_ = new AudioDeviceThread::Thread( 105 thread_ = new AudioDeviceThread::Thread(thread_name, synchronized_buffers);
83 callback, socket, thread_name, synchronized_buffers); 106 thread_->StartSuspended();
84 thread_->Start();
85 } 107 }
86 108
87 void AudioDeviceThread::Stop(base::MessageLoop* loop_for_join) { 109 void AudioDeviceThread::Stop(base::MessageLoop* loop_for_join) {
88 base::AutoLock auto_lock(thread_lock_); 110 base::AutoLock auto_lock(thread_lock_);
89 if (thread_.get()) { 111 if (thread_.get()) {
90 thread_->Stop(loop_for_join); 112 thread_->Stop(loop_for_join);
91 thread_ = NULL; 113 thread_ = nullptr;
92 } 114 }
93 } 115 }
94 116
117 void AudioDeviceThread::Resume(AudioDeviceThread::Callback* callback,
118 base::SyncSocket::Handle socket) {
119 base::AutoLock auto_lock(thread_lock_);
120 if (thread_.get())
DaleCurtis 2016/05/17 17:54:35 Drop .get() here and elsewhere.
Henrik Grunell 2016/05/18 12:21:32 Done.
121 thread_->Resume(callback, socket);
122 }
123
124 void AudioDeviceThread::Suspend() {
125 base::AutoLock auto_lock(thread_lock_);
126 if (thread_.get())
127 thread_->Suspend();
128 }
129
95 bool AudioDeviceThread::IsStopped() { 130 bool AudioDeviceThread::IsStopped() {
96 base::AutoLock auto_lock(thread_lock_); 131 base::AutoLock auto_lock(thread_lock_);
97 return !thread_.get(); 132 return !thread_.get();
98 } 133 }
99 134
100 // AudioDeviceThread::Thread implementation 135 // AudioDeviceThread::Thread implementation
101 AudioDeviceThread::Thread::Thread(AudioDeviceThread::Callback* callback, 136 AudioDeviceThread::Thread::Thread(const char* thread_name,
102 base::SyncSocket::Handle socket,
103 const char* thread_name,
104 bool synchronized_buffers) 137 bool synchronized_buffers)
105 : thread_(), 138 : thread_name_(thread_name),
106 callback_(callback), 139 synchronized_buffers_(synchronized_buffers),
107 socket_(socket), 140 exit_suspended_state_(&lock_),
108 thread_name_(thread_name), 141 state_(STATE_SUSPENDED),
109 synchronized_buffers_(synchronized_buffers) { 142 callback_(nullptr),
110 } 143 new_socket_handle_(base::CancelableSyncSocket::kInvalidHandle) {}
111 144
112 AudioDeviceThread::Thread::~Thread() { 145 AudioDeviceThread::Thread::~Thread() {
113 DCHECK(thread_.is_null()); 146 DCHECK(thread_handle_.is_null());
114 } 147 DCHECK_EQ(state_, STATE_STOPPED)
115 148 << "Thread must be stopped before destruction";
116 void AudioDeviceThread::Thread::Start() { 149 }
117 base::AutoLock auto_lock(callback_lock_); 150
118 DCHECK(thread_.is_null()); 151 void AudioDeviceThread::Thread::StartSuspended() {
119 // This reference will be released when the thread exists. 152 DCHECK(thread_checker_.CalledOnValidThread());
153
154 base::AutoLock auto_lock(lock_);
155 DCHECK(thread_handle_.is_null()) << "Calling Start() on a started thread?";
156 if (state_ == STATE_STOPPED)
157 return; // Stop has already been executed on another thread.
158
159 // This reference will be released when the thread exits.
DaleCurtis 2016/05/17 17:54:35 We should try to remove this in the future too; pr
Henrik Grunell 2016/05/18 12:21:32 Agree, added TODO.
120 AddRef(); 160 AddRef();
121 161
122 PlatformThread::CreateWithPriority(0, this, &thread_, 162 PlatformThread::CreateWithPriority(0, this, &thread_handle_,
123 base::ThreadPriority::REALTIME_AUDIO); 163 base::ThreadPriority::REALTIME_AUDIO);
124 CHECK(!thread_.is_null()); 164 CHECK(!thread_handle_.is_null());
125 } 165 }
126 166
127 void AudioDeviceThread::Thread::Stop(base::MessageLoop* loop_for_join) { 167 void AudioDeviceThread::Thread::Stop(base::MessageLoop* loop_for_join) {
128 socket_.Shutdown(); 168 // Note: Stop() can be called at any time, even before Start(), because it's
129 169 // allowed to be executed on a different thread.
130 base::PlatformThreadHandle thread = base::PlatformThreadHandle(); 170
131 171 base::PlatformThreadHandle thread_handle_to_join =
132 { // NOLINT 172 base::PlatformThreadHandle();
133 base::AutoLock auto_lock(callback_lock_); 173 {
134 callback_ = NULL; 174 base::AutoLock auto_lock(lock_);
135 std::swap(thread, thread_); 175 DCHECK_NE(state_, STATE_STOPPED) << "Calling Stop() on a stopped thread?";
136 } 176 callback_ = nullptr;
137 177
138 if (!thread.is_null()) { 178 // |socket_| is only set/reset under the |lock_|, so it's safe to
179 // access under that lock here.
180 if (socket_)
181 socket_->Shutdown();
182
183 // Must be done under lock to avoid race with Start().
184 std::swap(thread_handle_to_join, thread_handle_);
185
186 state_ = STATE_STOPPED;
187 exit_suspended_state_.Signal();
188 } // auto_lock(lock_)
189
190 if (!thread_handle_to_join.is_null()) {
139 if (loop_for_join) { 191 if (loop_for_join) {
140 loop_for_join->PostTask(FROM_HERE, 192 loop_for_join->PostTask(FROM_HERE, base::Bind(&base::PlatformThread::Join,
141 base::Bind(&base::PlatformThread::Join, thread)); 193 thread_handle_to_join));
142 } else { 194 } else {
143 base::PlatformThread::Join(thread); 195 base::PlatformThread::Join(thread_handle_to_join);
144 } 196 }
145 } 197 }
146 } 198 }
147 199
200 void AudioDeviceThread::Thread::Resume(AudioDeviceThread::Callback* callback,
201 base::SyncSocket::Handle socket_handle) {
202 DCHECK(thread_checker_.CalledOnValidThread());
203
204 base::AutoLock auto_lock(lock_);
205 if (state_ == STATE_STOPPED)
206 return; // Stop is the last operation to be executed.
207
208 DCHECK_EQ(state_, STATE_SUSPENDED) << "Resume a non-suspended thread?";
209
210 DCHECK_EQ(new_socket_handle_, base::CancelableSyncSocket::kInvalidHandle);
211
212 callback_ = callback;
213 new_socket_handle_ = socket_handle;
214
215 state_ = STATE_PLAYING;
216 exit_suspended_state_.Signal();
217 }
218
219 void AudioDeviceThread::Thread::Suspend() {
220 DCHECK(thread_checker_.CalledOnValidThread());
221
222 base::AutoLock auto_lock(lock_);
223 if (state_ == STATE_STOPPED)
224 return; // Stop is the last operation to be executed.
225
226 DCHECK_EQ(state_, STATE_PLAYING) << "Suspend a suspended thread?";
227 callback_ = nullptr;
228
229 // |socket_| is only set/reset under the |lock_|, so it's safe to
230 // access under that lock here.
231 if (socket_)
232 socket_->Shutdown();
233 state_ = STATE_SUSPENDED;
234 }
235
148 void AudioDeviceThread::Thread::ThreadMain() { 236 void AudioDeviceThread::Thread::ThreadMain() {
149 PlatformThread::SetName(thread_name_); 237 PlatformThread::SetName(thread_name_);
150 238
151 // Singleton access is safe from this thread as long as callback is non-NULL. 239 // Singleton access is safe from this thread as long as callback is non-NULL.
152 // The callback is the only point where the thread calls out to 'unknown' code 240 // The callback is the only point where the thread calls out to 'unknown' code
153 // that might touch singletons and the lifetime of the callback is controlled 241 // that might touch singletons and the lifetime of the callback is controlled
154 // by another thread on which singleton access is OK as well. 242 // by another thread on which singleton access is OK as well.
155 base::ThreadRestrictions::SetSingletonAllowed(true); 243 base::ThreadRestrictions::SetSingletonAllowed(true);
156 244
157 { // NOLINT 245 while (true) {
158 base::AutoLock auto_lock(callback_lock_); 246 {
159 if (callback_) 247 base::AutoLock auto_lock(lock_);
160 callback_->InitializeOnAudioThread(); 248 while (state_ == STATE_SUSPENDED) {
DaleCurtis 2016/05/17 17:54:35 What's the point of doing this versus just restart
Henrik Grunell 2016/05/18 12:21:32 The problem with spinning up a new thread each tim
DaleCurtis 2016/05/18 17:20:43 I feel like overcomplicating our production code t
Henrik Grunell 2016/05/19 13:03:11 The problems is more specifically the components u
DaleCurtis 2016/05/19 20:41:00 I don't think that's a good enough reason to make
Henrik Grunell 2016/05/23 11:41:58 Actually, I have brought up this thread re-use app
161 } 249 // This releases |lock_| while waiting.
250 exit_suspended_state_.Wait();
251 }
162 252
163 Run(); 253 if (state_ == STATE_STOPPED)
254 break;
255
256 // If we are playing but don't have a valid new socket handle, we are here
257 // because the socket receive or send failed for another reason than
258 // Shutdown() being called in Stop() or Suspend(). In that case, quit the
259 // thread.
260 if (new_socket_handle_ == base::CancelableSyncSocket::kInvalidHandle) {
261 LOG(WARNING) << "Audio socket failed, exiting audio thread.";
262 break;
263 }
264
265 // Else reinitialize and resume.
266
267 socket_.reset(new base::CancelableSyncSocket(new_socket_handle_));
268
269 if (callback_)
270 callback_->InitializeOnAudioThread();
271
272 new_socket_handle_ = base::CancelableSyncSocket::kInvalidHandle;
273 } // auto_lock(lock_)
274
275 Run();
276 } // while
164 277
165 // Release the reference for the thread. Note that after this, the Thread 278 // Release the reference for the thread. Note that after this, the Thread
166 // instance will most likely be deleted. 279 // instance will most likely be deleted.
167 Release(); 280 Release();
168 } 281 }
169 282
170 void AudioDeviceThread::Thread::Run() { 283 void AudioDeviceThread::Thread::Run() {
171 uint32_t buffer_index = 0; 284 uint32_t buffer_index = 0;
285
172 while (true) { 286 while (true) {
173 uint32_t pending_data = 0; 287 uint32_t pending_data = 0;
174 size_t bytes_read = socket_.Receive(&pending_data, sizeof(pending_data)); 288 // |socket_| is only modified on this thread, so we don't need a lock to
289 // access it here.
290 size_t bytes_read = socket_->Receive(&pending_data, sizeof(pending_data));
291
175 if (bytes_read != sizeof(pending_data)) 292 if (bytes_read != sizeof(pending_data))
176 break; 293 break;
177 294
178 // std::numeric_limits<uint32_t>::max() is a special signal which is 295 // std::numeric_limits<uint32_t>::max() is a special signal which is
179 // returned after the browser stops the output device in response to a 296 // returned after the browser stops the output device in response to a
180 // renderer side request. 297 // renderer side request.
181 // 298 //
182 // Avoid running Process() for the paused signal, we still need to update 299 // Avoid running Process() for the paused signal, we still need to update
183 // the buffer index if |synchronized_buffers_| is true though. 300 // the buffer index if |synchronized_buffers_| is true though.
184 // 301 //
185 // See comments in AudioOutputController::DoPause() for details on why. 302 // See comments in AudioOutputController::DoPause() for details on why.
186 if (pending_data != std::numeric_limits<uint32_t>::max()) { 303 if (pending_data != std::numeric_limits<uint32_t>::max()) {
187 base::AutoLock auto_lock(callback_lock_); 304 // We can't use an atomic pointer for |callback_|, but need to call on it
305 // under the state lock. This is because after clearing it in Suspend() or
306 // Stop(), the caller may delete the callback object.
307 base::AutoLock auto_lock(lock_);
188 if (callback_) 308 if (callback_)
189 callback_->Process(pending_data); 309 callback_->Process(pending_data);
190 } 310 }
191 311
192 // The usage of |synchronized_buffers_| differs between input and output 312 // The usage of |synchronized_buffers_| differs between input and output
193 // cases. 313 // cases.
194 // Input: 314 // Input:
195 // Let the other end know that we have read data, so that it can verify 315 // Let the other end know that we have read data, so that it can verify
196 // it doesn't overwrite any data before read. The |buffer_index| value is 316 // it doesn't overwrite any data before read. The |buffer_index| value is
197 // not used. For more details, see AudioInputSyncWriter::Write(). 317 // not used. For more details, see AudioInputSyncWriter::Write().
198 // Output: 318 // Output:
199 // Let the other end know which buffer we just filled. The |buffer_index| is 319 // Let the other end know which buffer we just filled. The |buffer_index| is
200 // used to ensure the other end is getting the buffer it expects. For more 320 // used to ensure the other end is getting the buffer it expects. For more
201 // details on how this works see AudioSyncReader::WaitUntilDataIsReady(). 321 // details on how this works see AudioSyncReader::WaitUntilDataIsReady().
202 if (synchronized_buffers_) { 322 if (synchronized_buffers_) {
203 ++buffer_index; 323 ++buffer_index;
204 size_t bytes_sent = socket_.Send(&buffer_index, sizeof(buffer_index)); 324 size_t bytes_sent = socket_->Send(&buffer_index, sizeof(buffer_index));
325
205 if (bytes_sent != sizeof(buffer_index)) 326 if (bytes_sent != sizeof(buffer_index))
206 break; 327 break;
207 } 328 }
208 } 329 } // while
209 } 330 }
210 331
211 // AudioDeviceThread::Callback implementation 332 // AudioDeviceThread::Callback implementation
212 333
213 AudioDeviceThread::Callback::Callback(const AudioParameters& audio_parameters, 334 AudioDeviceThread::Callback::Callback(const AudioParameters& audio_parameters,
214 base::SharedMemoryHandle memory, 335 base::SharedMemoryHandle memory,
215 int memory_length, 336 int memory_length,
216 int total_segments) 337 int total_segments)
217 : audio_parameters_(audio_parameters), 338 : audio_parameters_(audio_parameters),
218 samples_per_ms_(static_cast<double>(audio_parameters.sample_rate()) / 339 samples_per_ms_(static_cast<double>(audio_parameters.sample_rate()) /
(...skipping 14 matching lines...) Expand all
233 } 354 }
234 355
235 AudioDeviceThread::Callback::~Callback() {} 356 AudioDeviceThread::Callback::~Callback() {}
236 357
237 void AudioDeviceThread::Callback::InitializeOnAudioThread() { 358 void AudioDeviceThread::Callback::InitializeOnAudioThread() {
238 MapSharedMemory(); 359 MapSharedMemory();
239 CHECK(shared_memory_.memory()); 360 CHECK(shared_memory_.memory());
240 } 361 }
241 362
242 } // namespace media. 363 } // namespace media.
OLDNEW
« no previous file with comments | « media/audio/audio_device_thread.h ('k') | media/audio/audio_input_device.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698