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

Side by Side Diff: media/audio/pulse/pulse_output.cc

Issue 10952024: Adding pulseaudio input support to chrome (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebased and addressed Dale's final comments. Created 7 years, 10 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
« no previous file with comments | « media/audio/pulse/pulse_output.h ('k') | media/audio/pulse/pulse_stub_header.fragment » ('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/pulse/pulse_output.h" 5 #include "media/audio/pulse/pulse_output.h"
6 6
7 #include <pulse/pulseaudio.h> 7 #include <pulse/pulseaudio.h>
8 8
9 #include "base/message_loop.h" 9 #include "base/message_loop.h"
10 #include "media/audio/audio_manager_base.h" 10 #include "media/audio/audio_manager_base.h"
11 #include "media/audio/audio_parameters.h" 11 #include "media/audio/audio_parameters.h"
12 #include "media/audio/audio_util.h" 12 #include "media/audio/audio_util.h"
13 #include "media/audio/pulse/pulse_util.h"
13 14
14 namespace media { 15 namespace media {
15 16
16 // A helper class that acquires pa_threaded_mainloop_lock() while in scope. 17 using pulse::AutoPulseLock;
17 class AutoPulseLock { 18 using pulse::WaitForOperationCompletion;
18 public:
19 explicit AutoPulseLock(pa_threaded_mainloop* pa_mainloop)
20 : pa_mainloop_(pa_mainloop) {
21 pa_threaded_mainloop_lock(pa_mainloop_);
22 }
23
24 ~AutoPulseLock() {
25 pa_threaded_mainloop_unlock(pa_mainloop_);
26 }
27
28 private:
29 pa_threaded_mainloop* pa_mainloop_;
30
31 DISALLOW_COPY_AND_ASSIGN(AutoPulseLock);
32 };
33
34 static pa_sample_format_t BitsToPASampleFormat(int bits_per_sample) {
35 switch (bits_per_sample) {
36 case 8:
37 return PA_SAMPLE_U8;
38 case 16:
39 return PA_SAMPLE_S16LE;
40 case 24:
41 return PA_SAMPLE_S24LE;
42 case 32:
43 return PA_SAMPLE_S32LE;
44 default:
45 NOTREACHED() << "Invalid bits per sample: " << bits_per_sample;
46 return PA_SAMPLE_INVALID;
47 }
48 }
49
50 static pa_channel_position ChromiumToPAChannelPosition(Channels channel) {
51 switch (channel) {
52 // PulseAudio does not differentiate between left/right and
53 // stereo-left/stereo-right, both translate to front-left/front-right.
54 case LEFT:
55 return PA_CHANNEL_POSITION_FRONT_LEFT;
56 case RIGHT:
57 return PA_CHANNEL_POSITION_FRONT_RIGHT;
58 case CENTER:
59 return PA_CHANNEL_POSITION_FRONT_CENTER;
60 case LFE:
61 return PA_CHANNEL_POSITION_LFE;
62 case BACK_LEFT:
63 return PA_CHANNEL_POSITION_REAR_LEFT;
64 case BACK_RIGHT:
65 return PA_CHANNEL_POSITION_REAR_RIGHT;
66 case LEFT_OF_CENTER:
67 return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
68 case RIGHT_OF_CENTER:
69 return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
70 case BACK_CENTER:
71 return PA_CHANNEL_POSITION_REAR_CENTER;
72 case SIDE_LEFT:
73 return PA_CHANNEL_POSITION_SIDE_LEFT;
74 case SIDE_RIGHT:
75 return PA_CHANNEL_POSITION_SIDE_RIGHT;
76 case CHANNELS_MAX:
77 return PA_CHANNEL_POSITION_INVALID;
78 default:
79 NOTREACHED() << "Invalid channel: " << channel;
80 return PA_CHANNEL_POSITION_INVALID;
81 }
82 }
83
84 static pa_channel_map ChannelLayoutToPAChannelMap(
85 ChannelLayout channel_layout) {
86 pa_channel_map channel_map;
87 pa_channel_map_init(&channel_map);
88
89 channel_map.channels = ChannelLayoutToChannelCount(channel_layout);
90 for (Channels ch = LEFT; ch < CHANNELS_MAX;
91 ch = static_cast<Channels>(ch + 1)) {
92 int channel_index = ChannelOrder(channel_layout, ch);
93 if (channel_index < 0)
94 continue;
95
96 channel_map.map[channel_index] = ChromiumToPAChannelPosition(ch);
97 }
98
99 return channel_map;
100 }
101 19
102 // static, pa_context_notify_cb 20 // static, pa_context_notify_cb
103 void PulseAudioOutputStream::ContextNotifyCallback(pa_context* c, 21 void PulseAudioOutputStream::ContextNotifyCallback(pa_context* c,
104 void* p_this) { 22 void* p_this) {
105 PulseAudioOutputStream* stream = static_cast<PulseAudioOutputStream*>(p_this); 23 PulseAudioOutputStream* stream = static_cast<PulseAudioOutputStream*>(p_this);
106 24
107 // Forward unexpected failures to the AudioSourceCallback if available. All 25 // Forward unexpected failures to the AudioSourceCallback if available. All
108 // these variables are only modified under pa_threaded_mainloop_lock() so this 26 // these variables are only modified under pa_threaded_mainloop_lock() so this
109 // should be thread safe. 27 // should be thread safe.
110 if (c && stream->source_callback_ && 28 if (c && stream->source_callback_ &&
(...skipping 13 matching lines...) Expand all
124 // should be thread safe. 42 // should be thread safe.
125 if (s && stream->source_callback_ && 43 if (s && stream->source_callback_ &&
126 pa_stream_get_state(s) == PA_STREAM_FAILED) { 44 pa_stream_get_state(s) == PA_STREAM_FAILED) {
127 stream->source_callback_->OnError( 45 stream->source_callback_->OnError(
128 stream, pa_context_errno(stream->pa_context_)); 46 stream, pa_context_errno(stream->pa_context_));
129 } 47 }
130 48
131 pa_threaded_mainloop_signal(stream->pa_mainloop_, 0); 49 pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
132 } 50 }
133 51
134 // static, pa_stream_success_cb_t
135 void PulseAudioOutputStream::StreamSuccessCallback(pa_stream* s, int success,
136 void* p_this) {
137 PulseAudioOutputStream* stream = static_cast<PulseAudioOutputStream*>(p_this);
138 pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
139 }
140
141 // static, pa_stream_request_cb_t 52 // static, pa_stream_request_cb_t
142 void PulseAudioOutputStream::StreamRequestCallback(pa_stream* s, size_t len, 53 void PulseAudioOutputStream::StreamRequestCallback(pa_stream* s, size_t len,
143 void* p_this) { 54 void* p_this) {
144 // Fulfill write request; must always result in a pa_stream_write() call. 55 // Fulfill write request; must always result in a pa_stream_write() call.
145 static_cast<PulseAudioOutputStream*>(p_this)->FulfillWriteRequest(len); 56 static_cast<PulseAudioOutputStream*>(p_this)->FulfillWriteRequest(len);
146 } 57 }
147 58
148 PulseAudioOutputStream::PulseAudioOutputStream(const AudioParameters& params, 59 PulseAudioOutputStream::PulseAudioOutputStream(const AudioParameters& params,
149 AudioManagerBase* manager) 60 AudioManagerBase* manager)
150 : params_(params), 61 : params_(params),
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
192 RETURN_ON_FAILURE(pa_context_, "Failed to create PulseAudio context."); 103 RETURN_ON_FAILURE(pa_context_, "Failed to create PulseAudio context.");
193 104
194 // A state callback must be set before calling pa_threaded_mainloop_lock() or 105 // A state callback must be set before calling pa_threaded_mainloop_lock() or
195 // pa_threaded_mainloop_wait() calls may lead to dead lock. 106 // pa_threaded_mainloop_wait() calls may lead to dead lock.
196 pa_context_set_state_callback(pa_context_, &ContextNotifyCallback, this); 107 pa_context_set_state_callback(pa_context_, &ContextNotifyCallback, this);
197 108
198 // Lock the main loop while setting up the context. Failure to do so may lead 109 // Lock the main loop while setting up the context. Failure to do so may lead
199 // to crashes as the PulseAudio thread tries to run before things are ready. 110 // to crashes as the PulseAudio thread tries to run before things are ready.
200 AutoPulseLock auto_lock(pa_mainloop_); 111 AutoPulseLock auto_lock(pa_mainloop_);
201 112
202 RETURN_ON_FAILURE( 113 RETURN_ON_FAILURE(pa_threaded_mainloop_start(pa_mainloop_) == 0,
203 pa_threaded_mainloop_start(pa_mainloop_) == 0, 114 "Failed to start PulseAudio main loop.");
204 "Failed to start PulseAudio main loop.");
205 RETURN_ON_FAILURE( 115 RETURN_ON_FAILURE(
206 pa_context_connect(pa_context_, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) == 0, 116 pa_context_connect(pa_context_, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) == 0,
207 "Failed to connect PulseAudio context."); 117 "Failed to connect PulseAudio context.");
208 118
209 // Wait until |pa_context_| is ready. pa_threaded_mainloop_wait() must be 119 // Wait until |pa_context_| is ready. pa_threaded_mainloop_wait() must be
210 // called after pa_context_get_state() in case the context is already ready, 120 // called after pa_context_get_state() in case the context is already ready,
211 // otherwise pa_threaded_mainloop_wait() will hang indefinitely. 121 // otherwise pa_threaded_mainloop_wait() will hang indefinitely.
212 while (true) { 122 while (true) {
213 pa_context_state_t context_state = pa_context_get_state(pa_context_); 123 pa_context_state_t context_state = pa_context_get_state(pa_context_);
214 RETURN_ON_FAILURE( 124 RETURN_ON_FAILURE(
215 PA_CONTEXT_IS_GOOD(context_state), "Invalid PulseAudio context state."); 125 PA_CONTEXT_IS_GOOD(context_state), "Invalid PulseAudio context state.");
216 if (context_state == PA_CONTEXT_READY) 126 if (context_state == PA_CONTEXT_READY)
217 break; 127 break;
218 pa_threaded_mainloop_wait(pa_mainloop_); 128 pa_threaded_mainloop_wait(pa_mainloop_);
219 } 129 }
220 130
221 // Set sample specifications. 131 // Set sample specifications.
222 pa_sample_spec pa_sample_specifications; 132 pa_sample_spec pa_sample_specifications;
223 pa_sample_specifications.format = BitsToPASampleFormat( 133 pa_sample_specifications.format = pulse::BitsToPASampleFormat(
224 params_.bits_per_sample()); 134 params_.bits_per_sample());
225 pa_sample_specifications.rate = params_.sample_rate(); 135 pa_sample_specifications.rate = params_.sample_rate();
226 pa_sample_specifications.channels = params_.channels(); 136 pa_sample_specifications.channels = params_.channels();
227 137
228 // Get channel mapping and open playback stream. 138 // Get channel mapping and open playback stream.
229 pa_channel_map* map = NULL; 139 pa_channel_map* map = NULL;
230 pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap( 140 pa_channel_map source_channel_map = pulse::ChannelLayoutToPAChannelMap(
231 params_.channel_layout()); 141 params_.channel_layout());
232 if (source_channel_map.channels != 0) { 142 if (source_channel_map.channels != 0) {
233 // The source data uses a supported channel map so we will use it rather 143 // The source data uses a supported channel map so we will use it rather
234 // than the default channel map (NULL). 144 // than the default channel map (NULL).
235 map = &source_channel_map; 145 map = &source_channel_map;
236 } 146 }
237 pa_stream_ = pa_stream_new( 147 pa_stream_ = pa_stream_new(
238 pa_context_, "Playback", &pa_sample_specifications, map); 148 pa_context_, "Playback", &pa_sample_specifications, map);
239 RETURN_ON_FAILURE(pa_stream_, "Failed to create PulseAudio stream."); 149 RETURN_ON_FAILURE(pa_stream_, "Failed to create PulseAudio stream.");
240 pa_stream_set_state_callback(pa_stream_, &StreamNotifyCallback, this); 150 pa_stream_set_state_callback(pa_stream_, &StreamNotifyCallback, this);
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
285 DCHECK(!pa_context_); 195 DCHECK(!pa_context_);
286 return; 196 return;
287 } 197 }
288 198
289 { 199 {
290 AutoPulseLock auto_lock(pa_mainloop_); 200 AutoPulseLock auto_lock(pa_mainloop_);
291 201
292 // Close the stream. 202 // Close the stream.
293 if (pa_stream_) { 203 if (pa_stream_) {
294 // Ensure all samples are played out before shutdown. 204 // Ensure all samples are played out before shutdown.
295 WaitForPulseOperation(pa_stream_flush( 205 pa_operation* operation = pa_stream_flush(
296 pa_stream_, &StreamSuccessCallback, this)); 206 pa_stream_, &pulse::StreamSuccessCallback, pa_mainloop_);
207 WaitForOperationCompletion(pa_mainloop_, operation);
297 208
298 // Release PulseAudio structures. 209 // Release PulseAudio structures.
299 pa_stream_disconnect(pa_stream_); 210 pa_stream_disconnect(pa_stream_);
300 pa_stream_set_write_callback(pa_stream_, NULL, NULL); 211 pa_stream_set_write_callback(pa_stream_, NULL, NULL);
301 pa_stream_set_state_callback(pa_stream_, NULL, NULL); 212 pa_stream_set_state_callback(pa_stream_, NULL, NULL);
302 pa_stream_unref(pa_stream_); 213 pa_stream_unref(pa_stream_);
303 pa_stream_ = NULL; 214 pa_stream_ = NULL;
304 } 215 }
305 216
306 if (pa_context_) { 217 if (pa_context_) {
(...skipping 12 matching lines...) Expand all
319 void PulseAudioOutputStream::Close() { 230 void PulseAudioOutputStream::Close() {
320 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 231 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
321 232
322 Reset(); 233 Reset();
323 234
324 // Signal to the manager that we're closed and can be removed. 235 // Signal to the manager that we're closed and can be removed.
325 // This should be the last call in the function as it deletes "this". 236 // This should be the last call in the function as it deletes "this".
326 manager_->ReleaseOutputStream(this); 237 manager_->ReleaseOutputStream(this);
327 } 238 }
328 239
329 int PulseAudioOutputStream::GetHardwareLatencyInBytes() {
330 int negative = 0;
331 pa_usec_t pa_latency_micros = 0;
332 if (pa_stream_get_latency(pa_stream_, &pa_latency_micros, &negative) != 0)
333 return 0;
334
335 if (negative)
336 return 0;
337
338 return (pa_latency_micros * params_.sample_rate() *
339 params_.GetBytesPerFrame()) / base::Time::kMicrosecondsPerSecond;
340 }
341
342 void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) { 240 void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) {
343 CHECK_EQ(requested_bytes, static_cast<size_t>(params_.GetBytesPerBuffer())); 241 CHECK_EQ(requested_bytes, static_cast<size_t>(params_.GetBytesPerBuffer()));
344 242
345 int frames_filled = 0; 243 int frames_filled = 0;
346 if (source_callback_) { 244 if (source_callback_) {
245 uint32 hardware_delay = pulse::GetHardwareLatencyInBytes(
246 pa_stream_, params_.sample_rate(),
247 params_.GetBytesPerFrame());
347 frames_filled = source_callback_->OnMoreData( 248 frames_filled = source_callback_->OnMoreData(
348 audio_bus_.get(), AudioBuffersState(0, GetHardwareLatencyInBytes())); 249 audio_bus_.get(), AudioBuffersState(0, hardware_delay));
349 } 250 }
350 251
351 // Zero any unfilled data so it plays back as silence. 252 // Zero any unfilled data so it plays back as silence.
352 if (frames_filled < audio_bus_->frames()) { 253 if (frames_filled < audio_bus_->frames()) {
353 audio_bus_->ZeroFramesPartial( 254 audio_bus_->ZeroFramesPartial(
354 frames_filled, audio_bus_->frames() - frames_filled); 255 frames_filled, audio_bus_->frames() - frames_filled);
355 } 256 }
356 257
357 // PulseAudio won't always be able to provide a buffer large enough, so we may 258 // PulseAudio won't always be able to provide a buffer large enough, so we may
358 // need to request multiple buffers and fill them individually. 259 // need to request multiple buffers and fill them individually.
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
399 // Ensure the context and stream are ready. 300 // Ensure the context and stream are ready.
400 if (pa_context_get_state(pa_context_) != PA_CONTEXT_READY && 301 if (pa_context_get_state(pa_context_) != PA_CONTEXT_READY &&
401 pa_stream_get_state(pa_stream_) != PA_STREAM_READY) { 302 pa_stream_get_state(pa_stream_) != PA_STREAM_READY) {
402 callback->OnError(this, pa_context_errno(pa_context_)); 303 callback->OnError(this, pa_context_errno(pa_context_));
403 return; 304 return;
404 } 305 }
405 306
406 source_callback_ = callback; 307 source_callback_ = callback;
407 308
408 // Uncork (resume) the stream. 309 // Uncork (resume) the stream.
409 WaitForPulseOperation(pa_stream_cork( 310 pa_operation* operation = pa_stream_cork(
410 pa_stream_, 0, &StreamSuccessCallback, this)); 311 pa_stream_, 0, &pulse::StreamSuccessCallback, pa_mainloop_);
312 WaitForOperationCompletion(pa_mainloop_, operation);
411 } 313 }
412 314
413 void PulseAudioOutputStream::Stop() { 315 void PulseAudioOutputStream::Stop() {
414 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 316 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
415 317
416 // Cork (pause) the stream. Waiting for the main loop lock will ensure 318 // Cork (pause) the stream. Waiting for the main loop lock will ensure
417 // outstanding callbacks have completed. 319 // outstanding callbacks have completed.
418 AutoPulseLock auto_lock(pa_mainloop_); 320 AutoPulseLock auto_lock(pa_mainloop_);
419 321
420 // Flush the stream prior to cork, doing so after will cause hangs. Write 322 // Flush the stream prior to cork, doing so after will cause hangs. Write
421 // callbacks are suspended while inside pa_threaded_mainloop_lock() so this 323 // callbacks are suspended while inside pa_threaded_mainloop_lock() so this
422 // is all thread safe. 324 // is all thread safe.
423 WaitForPulseOperation(pa_stream_flush( 325 pa_operation* operation = pa_stream_flush(
424 pa_stream_, &StreamSuccessCallback, this)); 326 pa_stream_, &pulse::StreamSuccessCallback, pa_mainloop_);
327 WaitForOperationCompletion(pa_mainloop_, operation);
425 328
426 WaitForPulseOperation(pa_stream_cork( 329 operation = pa_stream_cork(pa_stream_, 1, &pulse::StreamSuccessCallback,
427 pa_stream_, 1, &StreamSuccessCallback, this)); 330 pa_mainloop_);
331 WaitForOperationCompletion(pa_mainloop_, operation);
428 332
429 source_callback_ = NULL; 333 source_callback_ = NULL;
430 } 334 }
431 335
432 void PulseAudioOutputStream::SetVolume(double volume) { 336 void PulseAudioOutputStream::SetVolume(double volume) {
433 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 337 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
434 338
435 volume_ = static_cast<float>(volume); 339 volume_ = static_cast<float>(volume);
436 } 340 }
437 341
438 void PulseAudioOutputStream::GetVolume(double* volume) { 342 void PulseAudioOutputStream::GetVolume(double* volume) {
439 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 343 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
440 344
441 *volume = volume_; 345 *volume = volume_;
442 } 346 }
443 347
444 void PulseAudioOutputStream::WaitForPulseOperation(pa_operation* op) {
445 CHECK(op);
446 while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) {
447 pa_threaded_mainloop_wait(pa_mainloop_);
448 }
449 pa_operation_unref(op);
450 }
451
452 } // namespace media 348 } // namespace media
OLDNEW
« no previous file with comments | « media/audio/pulse/pulse_output.h ('k') | media/audio/pulse/pulse_stub_header.fragment » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698