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

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 ready for review. 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,
DaleCurtis 2013/01/30 02:54:30 Should we try to collapse these into pulse util?
no longer working on chromium 2013/02/12 17:35:59 Not sure how we can, since it accesses the stream-
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) {
112 stream->source_callback_->OnError(stream, pa_context_errno(c)); 27 stream->source_callback_->OnError(stream, pa_context_errno(c));
113 } 28 }
(...skipping 10 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 135 matching lines...) Expand 10 before | Expand all | Expand 10 after
286 return; 194 return;
287 } 195 }
288 196
289 { 197 {
290 AutoPulseLock auto_lock(pa_mainloop_); 198 AutoPulseLock auto_lock(pa_mainloop_);
291 199
292 // Close the stream. 200 // Close the stream.
293 if (pa_stream_) { 201 if (pa_stream_) {
294 // Ensure all samples are played out before shutdown. 202 // Ensure all samples are played out before shutdown.
295 WaitForPulseOperation(pa_stream_flush( 203 WaitForPulseOperation(pa_stream_flush(
296 pa_stream_, &StreamSuccessCallback, this)); 204 pa_stream_, &StreamSuccessCallback, pa_mainloop_));
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 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
400 if (pa_context_get_state(pa_context_) != PA_CONTEXT_READY && 308 if (pa_context_get_state(pa_context_) != PA_CONTEXT_READY &&
401 pa_stream_get_state(pa_stream_) != PA_STREAM_READY) { 309 pa_stream_get_state(pa_stream_) != PA_STREAM_READY) {
402 callback->OnError(this, pa_context_errno(pa_context_)); 310 callback->OnError(this, pa_context_errno(pa_context_));
403 return; 311 return;
404 } 312 }
405 313
406 source_callback_ = callback; 314 source_callback_ = callback;
407 315
408 // Uncork (resume) the stream. 316 // Uncork (resume) the stream.
409 WaitForPulseOperation(pa_stream_cork( 317 WaitForPulseOperation(pa_stream_cork(
410 pa_stream_, 0, &StreamSuccessCallback, this)); 318 pa_stream_, 0, &StreamSuccessCallback, pa_mainloop_));
411 } 319 }
412 320
413 void PulseAudioOutputStream::Stop() { 321 void PulseAudioOutputStream::Stop() {
414 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 322 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
415 323
416 // Cork (pause) the stream. Waiting for the main loop lock will ensure 324 // Cork (pause) the stream. Waiting for the main loop lock will ensure
417 // outstanding callbacks have completed. 325 // outstanding callbacks have completed.
418 AutoPulseLock auto_lock(pa_mainloop_); 326 AutoPulseLock auto_lock(pa_mainloop_);
419 327
420 // Flush the stream prior to cork, doing so after will cause hangs. Write 328 // 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 329 // callbacks are suspended while inside pa_threaded_mainloop_lock() so this
422 // is all thread safe. 330 // is all thread safe.
423 WaitForPulseOperation(pa_stream_flush( 331 WaitForPulseOperation(pa_stream_flush(
424 pa_stream_, &StreamSuccessCallback, this)); 332 pa_stream_, &StreamSuccessCallback, pa_mainloop_));
425 333
426 WaitForPulseOperation(pa_stream_cork( 334 WaitForPulseOperation(pa_stream_cork(
427 pa_stream_, 1, &StreamSuccessCallback, this)); 335 pa_stream_, 1, &StreamSuccessCallback, pa_mainloop_));
428 336
429 source_callback_ = NULL; 337 source_callback_ = NULL;
430 } 338 }
431 339
432 void PulseAudioOutputStream::SetVolume(double volume) { 340 void PulseAudioOutputStream::SetVolume(double volume) {
433 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 341 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
434 342
435 volume_ = static_cast<float>(volume); 343 volume_ = static_cast<float>(volume);
436 } 344 }
437 345
438 void PulseAudioOutputStream::GetVolume(double* volume) { 346 void PulseAudioOutputStream::GetVolume(double* volume) {
439 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 347 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
440 348
441 *volume = volume_; 349 *volume = volume_;
442 } 350 }
443 351
444 void PulseAudioOutputStream::WaitForPulseOperation(pa_operation* op) { 352 void PulseAudioOutputStream::WaitForPulseOperation(pa_operation* op) {
DaleCurtis 2013/01/30 02:54:30 Move to PulseUtil?
no longer working on chromium 2013/02/12 17:35:59 Done.
445 CHECK(op); 353 CHECK(op);
446 while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) { 354 while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) {
447 pa_threaded_mainloop_wait(pa_mainloop_); 355 pa_threaded_mainloop_wait(pa_mainloop_);
448 } 356 }
449 pa_operation_unref(op); 357 pa_operation_unref(op);
450 } 358 }
451 359
452 } // namespace media 360 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698