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

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: used the stubs script to do the dynamic linking 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
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 class AutoPulseLock {
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
102 // static, pa_context_notify_cb 17 // static, pa_context_notify_cb
103 void PulseAudioOutputStream::ContextNotifyCallback(pa_context* c, 18 void PulseAudioOutputStream::ContextNotifyCallback(pa_context* c,
104 void* p_this) { 19 void* p_this) {
105 PulseAudioOutputStream* stream = static_cast<PulseAudioOutputStream*>(p_this); 20 PulseAudioOutputStream* stream = static_cast<PulseAudioOutputStream*>(p_this);
106 21
107 // Forward unexpected failures to the AudioSourceCallback if available. All 22 // Forward unexpected failures to the AudioSourceCallback if available. All
108 // these variables are only modified under pa_threaded_mainloop_lock() so this 23 // these variables are only modified under pa_threaded_mainloop_lock() so this
109 // should be thread safe. 24 // should be thread safe.
110 if (c && stream->source_callback_ && 25 if (c && stream->source_callback_ &&
111 pa_context_get_state(c) == PA_CONTEXT_FAILED) { 26 pa_context_get_state(c) == PA_CONTEXT_FAILED) {
(...skipping 12 matching lines...) Expand all
124 // should be thread safe. 39 // should be thread safe.
125 if (s && stream->source_callback_ && 40 if (s && stream->source_callback_ &&
126 pa_stream_get_state(s) == PA_STREAM_FAILED) { 41 pa_stream_get_state(s) == PA_STREAM_FAILED) {
127 stream->source_callback_->OnError( 42 stream->source_callback_->OnError(
128 stream, pa_context_errno(stream->pa_context_)); 43 stream, pa_context_errno(stream->pa_context_));
129 } 44 }
130 45
131 pa_threaded_mainloop_signal(stream->pa_mainloop_, 0); 46 pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
132 } 47 }
133 48
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 49 // static, pa_stream_request_cb_t
142 void PulseAudioOutputStream::StreamRequestCallback(pa_stream* s, size_t len, 50 void PulseAudioOutputStream::StreamRequestCallback(pa_stream* s, size_t len,
143 void* p_this) { 51 void* p_this) {
144 // Fulfill write request; must always result in a pa_stream_write() call. 52 // Fulfill write request; must always result in a pa_stream_write() call.
145 static_cast<PulseAudioOutputStream*>(p_this)->FulfillWriteRequest(len); 53 static_cast<PulseAudioOutputStream*>(p_this)->FulfillWriteRequest(len);
146 } 54 }
147 55
148 PulseAudioOutputStream::PulseAudioOutputStream(const AudioParameters& params, 56 PulseAudioOutputStream::PulseAudioOutputStream(const AudioParameters& params,
149 AudioManagerBase* manager) 57 AudioManagerBase* manager)
150 : params_(params), 58 : 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."); 100 RETURN_ON_FAILURE(pa_context_, "Failed to create PulseAudio context.");
193 101
194 // A state callback must be set before calling pa_threaded_mainloop_lock() or 102 // A state callback must be set before calling pa_threaded_mainloop_lock() or
195 // pa_threaded_mainloop_wait() calls may lead to dead lock. 103 // pa_threaded_mainloop_wait() calls may lead to dead lock.
196 pa_context_set_state_callback(pa_context_, &ContextNotifyCallback, this); 104 pa_context_set_state_callback(pa_context_, &ContextNotifyCallback, this);
197 105
198 // Lock the main loop while setting up the context. Failure to do so may lead 106 // 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. 107 // to crashes as the PulseAudio thread tries to run before things are ready.
200 AutoPulseLock auto_lock(pa_mainloop_); 108 AutoPulseLock auto_lock(pa_mainloop_);
201 109
202 RETURN_ON_FAILURE( 110 RETURN_ON_FAILURE(pa_threaded_mainloop_start(pa_mainloop_) == 0,
203 pa_threaded_mainloop_start(pa_mainloop_) == 0, 111 "Failed to start PulseAudio main loop.");
204 "Failed to start PulseAudio main loop.");
205 RETURN_ON_FAILURE( 112 RETURN_ON_FAILURE(
206 pa_context_connect(pa_context_, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) == 0, 113 pa_context_connect(pa_context_, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) == 0,
207 "Failed to connect PulseAudio context."); 114 "Failed to connect PulseAudio context.");
208 115
209 // Wait until |pa_context_| is ready. pa_threaded_mainloop_wait() must be 116 // 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, 117 // called after pa_context_get_state() in case the context is already ready,
211 // otherwise pa_threaded_mainloop_wait() will hang indefinitely. 118 // otherwise pa_threaded_mainloop_wait() will hang indefinitely.
212 while (true) { 119 while (true) {
213 pa_context_state_t context_state = pa_context_get_state(pa_context_); 120 pa_context_state_t context_state = pa_context_get_state(pa_context_);
214 RETURN_ON_FAILURE( 121 RETURN_ON_FAILURE(
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
285 DCHECK(!pa_context_); 192 DCHECK(!pa_context_);
286 return; 193 return;
287 } 194 }
288 195
289 { 196 {
290 AutoPulseLock auto_lock(pa_mainloop_); 197 AutoPulseLock auto_lock(pa_mainloop_);
291 198
292 // Close the stream. 199 // Close the stream.
293 if (pa_stream_) { 200 if (pa_stream_) {
294 // Ensure all samples are played out before shutdown. 201 // Ensure all samples are played out before shutdown.
295 WaitForPulseOperation(pa_stream_flush( 202 pa_operation* operation = pa_stream_flush(
296 pa_stream_, &StreamSuccessCallback, this)); 203 pa_stream_, &StreamSuccessCallback, pa_mainloop_);
204 WaitForOperationCompletion(pa_mainloop_, operation);
297 205
298 // Release PulseAudio structures. 206 // Release PulseAudio structures.
299 pa_stream_disconnect(pa_stream_); 207 pa_stream_disconnect(pa_stream_);
300 pa_stream_set_write_callback(pa_stream_, NULL, NULL); 208 pa_stream_set_write_callback(pa_stream_, NULL, NULL);
301 pa_stream_set_state_callback(pa_stream_, NULL, NULL); 209 pa_stream_set_state_callback(pa_stream_, NULL, NULL);
302 pa_stream_unref(pa_stream_); 210 pa_stream_unref(pa_stream_);
303 pa_stream_ = NULL; 211 pa_stream_ = NULL;
304 } 212 }
305 213
306 if (pa_context_) { 214 if (pa_context_) {
(...skipping 12 matching lines...) Expand all
319 void PulseAudioOutputStream::Close() { 227 void PulseAudioOutputStream::Close() {
320 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 228 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
321 229
322 Reset(); 230 Reset();
323 231
324 // Signal to the manager that we're closed and can be removed. 232 // 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". 233 // This should be the last call in the function as it deletes "this".
326 manager_->ReleaseOutputStream(this); 234 manager_->ReleaseOutputStream(this);
327 } 235 }
328 236
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) { 237 void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) {
343 CHECK_EQ(requested_bytes, static_cast<size_t>(params_.GetBytesPerBuffer())); 238 CHECK_EQ(requested_bytes, static_cast<size_t>(params_.GetBytesPerBuffer()));
344 239
345 int frames_filled = 0; 240 int frames_filled = 0;
346 if (source_callback_) { 241 if (source_callback_) {
242 uint32 hardware_delay = GetHardwareLatencyInBytes(
243 pa_stream_, params_.sample_rate(),
244 params_.GetBytesPerFrame());
347 frames_filled = source_callback_->OnMoreData( 245 frames_filled = source_callback_->OnMoreData(
348 audio_bus_.get(), AudioBuffersState(0, GetHardwareLatencyInBytes())); 246 audio_bus_.get(), AudioBuffersState(0, hardware_delay));
349 } 247 }
350 248
351 // Zero any unfilled data so it plays back as silence. 249 // Zero any unfilled data so it plays back as silence.
352 if (frames_filled < audio_bus_->frames()) { 250 if (frames_filled < audio_bus_->frames()) {
353 audio_bus_->ZeroFramesPartial( 251 audio_bus_->ZeroFramesPartial(
354 frames_filled, audio_bus_->frames() - frames_filled); 252 frames_filled, audio_bus_->frames() - frames_filled);
355 } 253 }
356 254
357 // PulseAudio won't always be able to provide a buffer large enough, so we may 255 // 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. 256 // 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. 297 // Ensure the context and stream are ready.
400 if (pa_context_get_state(pa_context_) != PA_CONTEXT_READY && 298 if (pa_context_get_state(pa_context_) != PA_CONTEXT_READY &&
401 pa_stream_get_state(pa_stream_) != PA_STREAM_READY) { 299 pa_stream_get_state(pa_stream_) != PA_STREAM_READY) {
402 callback->OnError(this, pa_context_errno(pa_context_)); 300 callback->OnError(this, pa_context_errno(pa_context_));
403 return; 301 return;
404 } 302 }
405 303
406 source_callback_ = callback; 304 source_callback_ = callback;
407 305
408 // Uncork (resume) the stream. 306 // Uncork (resume) the stream.
409 WaitForPulseOperation(pa_stream_cork( 307 pa_operation* operation = pa_stream_cork(
410 pa_stream_, 0, &StreamSuccessCallback, this)); 308 pa_stream_, 0, &StreamSuccessCallback, pa_mainloop_);
309 WaitForOperationCompletion(pa_mainloop_, operation);
411 } 310 }
412 311
413 void PulseAudioOutputStream::Stop() { 312 void PulseAudioOutputStream::Stop() {
414 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 313 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
415 314
416 // Cork (pause) the stream. Waiting for the main loop lock will ensure 315 // Cork (pause) the stream. Waiting for the main loop lock will ensure
417 // outstanding callbacks have completed. 316 // outstanding callbacks have completed.
418 AutoPulseLock auto_lock(pa_mainloop_); 317 AutoPulseLock auto_lock(pa_mainloop_);
419 318
420 // Flush the stream prior to cork, doing so after will cause hangs. Write 319 // 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 320 // callbacks are suspended while inside pa_threaded_mainloop_lock() so this
422 // is all thread safe. 321 // is all thread safe.
423 WaitForPulseOperation(pa_stream_flush( 322 pa_operation* operation = pa_stream_flush(
424 pa_stream_, &StreamSuccessCallback, this)); 323 pa_stream_, &StreamSuccessCallback, pa_mainloop_);
324 WaitForOperationCompletion(pa_mainloop_, operation);
425 325
426 WaitForPulseOperation(pa_stream_cork( 326 operation = pa_stream_cork(pa_stream_, 1, &StreamSuccessCallback,
427 pa_stream_, 1, &StreamSuccessCallback, this)); 327 pa_mainloop_);
328 WaitForOperationCompletion(pa_mainloop_, operation);
428 329
429 source_callback_ = NULL; 330 source_callback_ = NULL;
430 } 331 }
431 332
432 void PulseAudioOutputStream::SetVolume(double volume) { 333 void PulseAudioOutputStream::SetVolume(double volume) {
433 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 334 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
434 335
435 volume_ = static_cast<float>(volume); 336 volume_ = static_cast<float>(volume);
436 } 337 }
437 338
438 void PulseAudioOutputStream::GetVolume(double* volume) { 339 void PulseAudioOutputStream::GetVolume(double* volume) {
439 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 340 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
440 341
441 *volume = volume_; 342 *volume = volume_;
442 } 343 }
443 344
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 345 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698