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

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

Issue 11098031: Get PulseAudio implementation working. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Comments. Created 8 years, 2 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') | no next file » | 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 "base/bind.h" 7 #include <pulse/pulseaudio.h>
8 #include "base/message_loop.h" 8
9 #include "media/audio/audio_manager_base.h"
9 #include "media/audio/audio_parameters.h" 10 #include "media/audio/audio_parameters.h"
10 #include "media/audio/audio_util.h" 11 #include "media/audio/audio_util.h"
11 #if defined(OS_LINUX)
12 #include "media/audio/linux/audio_manager_linux.h"
13 #elif defined(OS_OPENBSD)
14 #include "media/audio/openbsd/audio_manager_openbsd.h"
15 #endif
16 #include "media/base/data_buffer.h"
17 #include "media/base/seekable_buffer.h"
18 12
19 namespace media { 13 namespace media {
20 14
15 // A helper class that acquires pa_threaded_mainloop_lock() while in scope.
16 class AutoPulseLock {
no longer working on chromium 2012/10/14 19:03:41 How about change the name to ScopedPulseLock?
DaleCurtis 2012/11/06 01:35:07 I like AutoPulseLock for consistently with the exi
17 public:
18 explicit AutoPulseLock(pa_threaded_mainloop* pa_mainloop)
19 : pa_mainloop_(pa_mainloop) {
20 pa_threaded_mainloop_lock(pa_mainloop_);
21 }
22
23 ~AutoPulseLock() {
24 pa_threaded_mainloop_unlock(pa_mainloop_);
25 }
26
27 private:
28 pa_threaded_mainloop* pa_mainloop_;
29 DISALLOW_COPY_AND_ASSIGN(AutoPulseLock);
30 };
31
21 static pa_sample_format_t BitsToPASampleFormat(int bits_per_sample) { 32 static pa_sample_format_t BitsToPASampleFormat(int bits_per_sample) {
no longer working on chromium 2012/10/14 19:03:41 what about extracting these helper class/functions
DaleCurtis 2012/11/06 01:35:07 Lets keep them here for now, when we have an input
22 switch (bits_per_sample) { 33 switch (bits_per_sample) {
23 // Unsupported sample formats shown for reference. I am assuming we want
24 // signed and little endian because that is what we gave to ALSA.
25 case 8: 34 case 8:
26 return PA_SAMPLE_U8; 35 return PA_SAMPLE_U8;
27 // Also 8-bits: PA_SAMPLE_ALAW and PA_SAMPLE_ULAW
28 case 16: 36 case 16:
29 return PA_SAMPLE_S16LE; 37 return PA_SAMPLE_S16LE;
30 // Also 16-bits: PA_SAMPLE_S16BE (big endian).
31 case 24: 38 case 24:
32 return PA_SAMPLE_S24LE; 39 return PA_SAMPLE_S24LE;
33 // Also 24-bits: PA_SAMPLE_S24BE (big endian).
34 // Other cases: PA_SAMPLE_24_32LE (in LSB of 32-bit field, little endian),
35 // and PA_SAMPLE_24_32BE (in LSB of 32-bit field, big endian),
36 case 32: 40 case 32:
37 return PA_SAMPLE_S32LE; 41 return PA_SAMPLE_S32LE;
38 // Also 32-bits: PA_SAMPLE_S32BE (big endian),
39 // PA_SAMPLE_FLOAT32LE (floating point little endian),
40 // and PA_SAMPLE_FLOAT32BE (floating point big endian).
41 default: 42 default:
43 NOTREACHED() << "Invalid bits per sample: " << bits_per_sample;
42 return PA_SAMPLE_INVALID; 44 return PA_SAMPLE_INVALID;
43 } 45 }
44 } 46 }
45 47
46 static pa_channel_position ChromiumToPAChannelPosition(Channels channel) { 48 static pa_channel_position ChromiumToPAChannelPosition(Channels channel) {
47 switch (channel) { 49 switch (channel) {
48 // PulseAudio does not differentiate between left/right and 50 // PulseAudio does not differentiate between left/right and
49 // stereo-left/stereo-right, both translate to front-left/front-right. 51 // stereo-left/stereo-right, both translate to front-left/front-right.
50 case LEFT: 52 case LEFT:
51 case STEREO_LEFT: 53 case STEREO_LEFT:
(...skipping 14 matching lines...) Expand all
66 case RIGHT_OF_CENTER: 68 case RIGHT_OF_CENTER:
67 return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; 69 return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
68 case BACK_CENTER: 70 case BACK_CENTER:
69 return PA_CHANNEL_POSITION_REAR_CENTER; 71 return PA_CHANNEL_POSITION_REAR_CENTER;
70 case SIDE_LEFT: 72 case SIDE_LEFT:
71 return PA_CHANNEL_POSITION_SIDE_LEFT; 73 return PA_CHANNEL_POSITION_SIDE_LEFT;
72 case SIDE_RIGHT: 74 case SIDE_RIGHT:
73 return PA_CHANNEL_POSITION_SIDE_RIGHT; 75 return PA_CHANNEL_POSITION_SIDE_RIGHT;
74 case CHANNELS_MAX: 76 case CHANNELS_MAX:
75 return PA_CHANNEL_POSITION_INVALID; 77 return PA_CHANNEL_POSITION_INVALID;
78 default:
79 NOTREACHED() << "Invalid channel: " << channel;
80 return PA_CHANNEL_POSITION_INVALID;
76 } 81 }
77 NOTREACHED() << "Invalid channel " << channel;
78 return PA_CHANNEL_POSITION_INVALID;
79 } 82 }
80 83
81 static pa_channel_map ChannelLayoutToPAChannelMap( 84 static pa_channel_map ChannelLayoutToPAChannelMap(
82 ChannelLayout channel_layout) { 85 ChannelLayout channel_layout) {
83 // Initialize channel map. 86 // Initialize channel map.
84 pa_channel_map channel_map; 87 pa_channel_map channel_map;
85 pa_channel_map_init(&channel_map); 88 pa_channel_map_init(&channel_map);
86 89
87 channel_map.channels = ChannelLayoutToChannelCount(channel_layout); 90 channel_map.channels = ChannelLayoutToChannelCount(channel_layout);
88 91
DaleCurtis 2012/11/09 01:10:52 Turns out this code was wrong after all...
89 // All channel maps have the same size array of channel positions. 92 // All channel maps have the same size array of channel positions.
90 for (unsigned int channel = 0; channel != CHANNELS_MAX; ++channel) { 93 for (unsigned int channel = 0; channel != CHANNELS_MAX; ++channel) {
91 int channel_position = kChannelOrderings[channel_layout][channel]; 94 int channel_position = kChannelOrderings[channel_layout][channel];
92 if (channel_position > -1) { 95 if (channel_position > -1) {
93 channel_map.map[channel_position] = ChromiumToPAChannelPosition( 96 channel_map.map[channel_position] = ChromiumToPAChannelPosition(
94 static_cast<Channels>(channel)); 97 static_cast<Channels>(channel));
95 } else { 98 } else {
96 // PulseAudio expects unused channels in channel maps to be filled with 99 // PulseAudio expects unused channels in channel maps to be filled with
97 // PA_CHANNEL_POSITION_MONO. 100 // PA_CHANNEL_POSITION_MONO.
98 channel_map.map[channel_position] = PA_CHANNEL_POSITION_MONO; 101 channel_map.map[channel_position] = PA_CHANNEL_POSITION_MONO;
99 } 102 }
100 } 103 }
101 104
102 // Fill in the rest of the unused channels. 105 // Fill in the rest of the unused channels.
103 for (unsigned int channel = CHANNELS_MAX; channel != PA_CHANNELS_MAX; 106 for (unsigned int channel = CHANNELS_MAX; channel != PA_CHANNELS_MAX;
104 ++channel) { 107 ++channel) {
105 channel_map.map[channel] = PA_CHANNEL_POSITION_MONO; 108 channel_map.map[channel] = PA_CHANNEL_POSITION_MONO;
106 } 109 }
107 110
108 return channel_map; 111 return channel_map;
109 } 112 }
110 113
111 static size_t MicrosecondsToBytes( 114 // static, pa_context_notify_cb
112 uint32 microseconds, uint32 sample_rate, size_t bytes_per_frame) { 115 void PulseAudioOutputStream::ContextNotifyCallback(pa_context* c,
113 return microseconds * sample_rate * bytes_per_frame / 116 void* p_this) {
114 base::Time::kMicrosecondsPerSecond; 117 PulseAudioOutputStream* stream = static_cast<PulseAudioOutputStream*>(p_this);
115 } 118
116 119 // Forward unexpected failures to the AudioSourceCallback if available. All
117 // static 120 // these variables are only modified under pa_threaded_mainloop_lock() so this
118 void PulseAudioOutputStream::ContextStateCallback(pa_context* context, 121 // should be thread safe.
119 void* state_addr) { 122 if (c && stream->source_callback_ &&
120 pa_context_state_t* state = static_cast<pa_context_state_t*>(state_addr); 123 pa_context_get_state(c) == PA_CONTEXT_FAILED) {
121 *state = pa_context_get_state(context); 124 stream->source_callback_->OnError(stream, pa_context_errno(c));
122 } 125 }
123 126
124 // static 127 pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
125 void PulseAudioOutputStream::WriteRequestCallback(pa_stream* playback_handle, 128 }
126 size_t length, 129
127 void* stream_addr) { 130 // static, pa_stream_notify_cb
128 PulseAudioOutputStream* stream = 131 void PulseAudioOutputStream::StreamNotifyCallback(pa_stream* s, void* p_this) {
129 reinterpret_cast<PulseAudioOutputStream*>(stream_addr); 132 PulseAudioOutputStream* stream = static_cast<PulseAudioOutputStream*>(p_this);
130 133
131 DCHECK(stream->manager_->GetMessageLoop()->BelongsToCurrentThread()); 134 // Forward unexpected failures to the AudioSourceCallback if available. All
132 135 // these variables are only modified under pa_threaded_mainloop_lock() so this
133 stream->write_callback_handled_ = true; 136 // should be thread safe.
134 137 if (s && stream->source_callback_ &&
135 // Fulfill write request. 138 pa_stream_get_state(s) == PA_STREAM_FAILED) {
136 stream->FulfillWriteRequest(length); 139 stream->source_callback_->OnError(
140 stream, pa_context_errno(stream->pa_context_));
141 }
142
143 pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
144 }
145
146 // static, pa_stream_success_cb_t
147 void PulseAudioOutputStream::StreamSuccessCallback(pa_stream* s, int success,
148 void* p_this) {
149 PulseAudioOutputStream* stream = static_cast<PulseAudioOutputStream*>(p_this);
150 pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
151 }
152
153 // static, pa_stream_request_cb_t
154 void PulseAudioOutputStream::StreamRequestCallback(pa_stream* s, size_t len,
155 void* p_this) {
156 // Fulfill write request; must always result in a pa_stream_write() call.
157 static_cast<PulseAudioOutputStream*>(p_this)->FulfillWriteRequest(len);
137 } 158 }
138 159
139 PulseAudioOutputStream::PulseAudioOutputStream(const AudioParameters& params, 160 PulseAudioOutputStream::PulseAudioOutputStream(const AudioParameters& params,
140 AudioManagerPulse* manager) 161 AudioManagerBase* manager)
141 : channel_layout_(params.channel_layout()), 162 : params_(params),
142 channel_count_(ChannelLayoutToChannelCount(channel_layout_)),
143 sample_format_(BitsToPASampleFormat(params.bits_per_sample())),
144 sample_rate_(params.sample_rate()),
145 bytes_per_frame_(params.GetBytesPerFrame()),
146 manager_(manager), 163 manager_(manager),
147 pa_context_(NULL), 164 pa_context_(NULL),
148 pa_mainloop_(NULL), 165 pa_mainloop_(NULL),
149 playback_handle_(NULL), 166 pa_stream_(NULL),
150 packet_size_(params.GetBytesPerBuffer()),
151 frames_per_packet_(packet_size_ / bytes_per_frame_),
152 client_buffer_(NULL),
153 volume_(1.0f), 167 volume_(1.0f),
154 stream_stopped_(true),
155 write_callback_handled_(false),
156 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
157 source_callback_(NULL) { 168 source_callback_(NULL) {
158 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 169 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
159 170
160 // TODO(slock): Sanity check input values. 171 CHECK(params_.IsValid());
172 audio_bus_ = AudioBus::Create(params_);
161 } 173 }
162 174
163 PulseAudioOutputStream::~PulseAudioOutputStream() { 175 PulseAudioOutputStream::~PulseAudioOutputStream() {
164 // All internal structures should already have been freed in Close(), 176 // All internal structures should already have been freed in Close(), which
165 // which calls AudioManagerPulse::Release which deletes this object. 177 // calls AudioManagerBase::ReleaseOutputStream() which deletes this object.
166 DCHECK(!playback_handle_); 178 DCHECK(!pa_stream_);
167 DCHECK(!pa_context_); 179 DCHECK(!pa_context_);
168 DCHECK(!pa_mainloop_); 180 DCHECK(!pa_mainloop_);
169 } 181 }
170 182
183 // Helper macro for Open() to avoid code spam and string bloat.
184 #define RETURN_ON_FAILURE(expression, message) \
185 if (!(expression)) { \
scherkus (not reviewing) 2012/10/17 22:44:53 "if() {}" is a compound statement and doesn't requ
DaleCurtis 2012/11/06 01:35:07 Sure, but that's pretty redundant here. Done in an
186 if (pa_context_) { \
187 DLOG(ERROR) << message << " Error: " << pa_context_errno(pa_context_); \
188 } else { \
189 DLOG(ERROR) << message; \
190 } \
191 return false; \
192 }
193
171 bool PulseAudioOutputStream::Open() { 194 bool PulseAudioOutputStream::Open() {
172 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 195 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
173 196
174 // TODO(slock): Possibly move most of this to an OpenPlaybackDevice function 197 pa_mainloop_ = pa_threaded_mainloop_new();
175 // in a new class 'pulse_util', like alsa_util. 198 RETURN_ON_FAILURE(pa_mainloop_, "Failed to create PulseAudio main loop.");
176 199
177 // Create a mainloop API and connect to the default server. 200 pa_mainloop_api* pa_mainloop_api = pa_threaded_mainloop_get_api(pa_mainloop_);
178 pa_mainloop_ = pa_mainloop_new();
179 pa_mainloop_api* pa_mainloop_api = pa_mainloop_get_api(pa_mainloop_);
180 pa_context_ = pa_context_new(pa_mainloop_api, "Chromium"); 201 pa_context_ = pa_context_new(pa_mainloop_api, "Chromium");
181 pa_context_state_t pa_context_state = PA_CONTEXT_UNCONNECTED; 202 RETURN_ON_FAILURE(pa_context_, "Failed to create PulseAudio context.");
182 pa_context_connect(pa_context_, NULL, PA_CONTEXT_NOFLAGS, NULL); 203
183 204 // A state callback must be set before calling pa_threaded_mainloop_lock() or
184 // Wait until PulseAudio is ready. 205 // pa_threaded_mainloop_wait() calls may lead to dead lock.
185 pa_context_set_state_callback(pa_context_, &ContextStateCallback, 206 pa_context_set_state_callback(pa_context_, &ContextNotifyCallback, this);
186 &pa_context_state); 207
187 while (pa_context_state != PA_CONTEXT_READY) { 208 // Lock the main loop while setting up the context. Failure to do so may lead
188 pa_mainloop_iterate(pa_mainloop_, 1, NULL); 209 // to crashes as the PulseAudio thread tries to run before things are ready.
189 if (pa_context_state == PA_CONTEXT_FAILED || 210 AutoPulseLock auto_lock(pa_mainloop_);
190 pa_context_state == PA_CONTEXT_TERMINATED) { 211
191 Reset(); 212 RETURN_ON_FAILURE(
192 return false; 213 pa_threaded_mainloop_start(pa_mainloop_) == 0,
193 } 214 "Failed to start PulseAudio main loop.");
215 RETURN_ON_FAILURE(
216 pa_context_connect(pa_context_, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) == 0,
217 "Failed to connect PulseAudio context.");
218
219 // Wait until |pa_context_| is ready. pa_threaded_mainloop_wait() must be
220 // called after pa_context_get_state() in case the context is already ready,
221 // otherwise pa_threaded_mainloop_wait() will hang indefinitely.
222 while (true) {
223 pa_context_state_t context_state = pa_context_get_state(pa_context_);
224 RETURN_ON_FAILURE(
225 PA_CONTEXT_IS_GOOD(context_state), "Invalid PulseAudio context state.");
226 if (context_state == PA_CONTEXT_READY)
227 break;
228 pa_threaded_mainloop_wait(pa_mainloop_);
194 } 229 }
195 230
196 // Set sample specifications. 231 // Set sample specifications.
197 pa_sample_spec pa_sample_specifications; 232 pa_sample_spec pa_sample_specifications;
198 pa_sample_specifications.format = sample_format_; 233 pa_sample_specifications.format = BitsToPASampleFormat(
199 pa_sample_specifications.rate = sample_rate_; 234 params_.bits_per_sample());
200 pa_sample_specifications.channels = channel_count_; 235 pa_sample_specifications.rate = params_.sample_rate();
236 pa_sample_specifications.channels = params_.channels();
201 237
202 // Get channel mapping and open playback stream. 238 // Get channel mapping and open playback stream.
203 pa_channel_map* map = NULL; 239 pa_channel_map* map = NULL;
204 pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap( 240 pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap(
205 channel_layout_); 241 params_.channel_layout());
206 if (source_channel_map.channels != 0) { 242 if (source_channel_map.channels != 0) {
207 // The source data uses a supported channel map so we will use it rather 243 // The source data uses a supported channel map so we will use it rather
208 // than the default channel map (NULL). 244 // than the default channel map (NULL).
209 map = &source_channel_map; 245 map = &source_channel_map;
210 } 246 }
211 playback_handle_ = pa_stream_new(pa_context_, "Playback", 247 pa_stream_ = pa_stream_new(pa_context_, "Playback",
212 &pa_sample_specifications, map); 248 &pa_sample_specifications, map);
no longer working on chromium 2012/10/14 19:03:41 indentation.
DaleCurtis 2012/11/06 01:35:07 Done.
213 249 RETURN_ON_FAILURE(pa_stream_, "Failed to create PulseAudio stream.");
214 // Initialize client buffer. 250 pa_stream_set_state_callback(pa_stream_, &StreamNotifyCallback, this);
215 uint32 output_packet_size = frames_per_packet_ * bytes_per_frame_; 251
216 client_buffer_.reset(new media::SeekableBuffer(0, output_packet_size)); 252 // Even though we start the stream corked below, PulseAudio will issue one
217 253 // stream request after setup. FulfillWriteRequest() must fulfill the write.
218 // Set write callback. 254 // TODO(dalecurtis): Why is this happening? Upstream bug?
no longer working on chromium 2012/10/14 19:03:41 I was hit by this issue too, do you know if it com
DaleCurtis 2012/11/06 01:35:07 pa_stream_connect_playback().
219 pa_stream_set_write_callback(playback_handle_, &WriteRequestCallback, this); 255 pa_stream_set_write_callback(pa_stream_, &StreamRequestCallback, this);
220 256
221 // Set server-side buffer attributes. 257 // Tell pulse audio we only want callbacks of a certain size.
222 // (uint32_t)-1 is the default and recommended value from PulseAudio's
223 // documentation, found at:
224 // http://freedesktop.org/software/pulseaudio/doxygen/structpa__buffer__attr.h tml.
225 pa_buffer_attr pa_buffer_attributes; 258 pa_buffer_attr pa_buffer_attributes;
226 pa_buffer_attributes.maxlength = static_cast<uint32_t>(-1); 259 pa_buffer_attributes.maxlength = params_.GetBytesPerBuffer();
227 pa_buffer_attributes.tlength = output_packet_size; 260 pa_buffer_attributes.minreq = params_.GetBytesPerBuffer();
261 pa_buffer_attributes.tlength = static_cast<uint32_t>(-1);
228 pa_buffer_attributes.prebuf = static_cast<uint32_t>(-1); 262 pa_buffer_attributes.prebuf = static_cast<uint32_t>(-1);
229 pa_buffer_attributes.minreq = static_cast<uint32_t>(-1);
230 pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1); 263 pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1);
231 264
232 // Connect playback stream. 265 // Connect playback stream.
233 pa_stream_connect_playback(playback_handle_, NULL, 266 RETURN_ON_FAILURE(
234 &pa_buffer_attributes, 267 pa_stream_connect_playback(
235 (pa_stream_flags_t) 268 pa_stream_, NULL, &pa_buffer_attributes, PA_STREAM_START_CORKED,
236 (PA_STREAM_INTERPOLATE_TIMING | 269 NULL, NULL) == 0,
237 PA_STREAM_ADJUST_LATENCY | 270 "Failed to connect PulseAudio stream.");
no longer working on chromium 2012/10/14 19:03:41 From internet: The server tries to assure that at
238 PA_STREAM_AUTO_TIMING_UPDATE), 271
239 NULL, NULL); 272 // Wait for the stream to be ready.
240 273 while (true) {
241 if (!playback_handle_) { 274 pa_stream_state_t stream_state = pa_stream_get_state(pa_stream_);
242 Reset(); 275 RETURN_ON_FAILURE(
243 return false; 276 PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state.");
277 if (stream_state == PA_STREAM_READY)
278 break;
279 pa_threaded_mainloop_wait(pa_mainloop_);
244 } 280 }
245 281
246 return true; 282 return true;
247 } 283 }
248 284
285 #undef RETURN_ON_FAILURE
286
249 void PulseAudioOutputStream::Reset() { 287 void PulseAudioOutputStream::Reset() {
250 stream_stopped_ = true; 288 if (!pa_mainloop_) {
251 289 DCHECK(!pa_stream_);
252 // Close the stream. 290 DCHECK(!pa_context_);
253 if (playback_handle_) { 291 return;
254 pa_stream_flush(playback_handle_, NULL, NULL); 292 }
255 pa_stream_disconnect(playback_handle_); 293
256 294 {
257 // Release PulseAudio structures. 295 AutoPulseLock auto_lock(pa_mainloop_);
258 pa_stream_unref(playback_handle_); 296
259 playback_handle_ = NULL; 297 // Close the stream.
260 } 298 if (pa_stream_) {
no longer working on chromium 2012/10/14 19:03:41 Do we need to call pa_stream_set_state_callback to
DaleCurtis 2012/11/06 01:35:07 Done.
261 if (pa_context_) { 299 // Ensure all samples are played out before shutdown.
262 pa_context_unref(pa_context_); 300 WaitForPulseOperation(pa_stream_flush(
263 pa_context_ = NULL; 301 pa_stream_, &StreamSuccessCallback, this));
264 } 302
265 if (pa_mainloop_) { 303 // Release PulseAudio structures.
266 pa_mainloop_free(pa_mainloop_); 304 pa_stream_disconnect(pa_stream_);
267 pa_mainloop_ = NULL; 305 pa_stream_unref(pa_stream_);
268 } 306 pa_stream_ = NULL;
269 307 }
270 // Release internal buffer. 308
271 client_buffer_.reset(); 309 if (pa_context_) {
310 // Clear our state callback.
311 pa_context_set_state_callback(pa_context_, NULL, NULL);
312
313 pa_context_disconnect(pa_context_);
314 pa_context_unref(pa_context_);
315 pa_context_ = NULL;
316 }
317 }
318
319 pa_threaded_mainloop_stop(pa_mainloop_);
320 pa_threaded_mainloop_free(pa_mainloop_);
321 pa_mainloop_ = NULL;
272 } 322 }
273 323
274 void PulseAudioOutputStream::Close() { 324 void PulseAudioOutputStream::Close() {
275 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 325 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
276 326
277 Reset(); 327 Reset();
278 328
279 // Signal to the manager that we're closed and can be removed. 329 // Signal to the manager that we're closed and can be removed.
280 // This should be the last call in the function as it deletes "this". 330 // This should be the last call in the function as it deletes "this".
281 manager_->ReleaseOutputStream(this); 331 manager_->ReleaseOutputStream(this);
282 } 332 }
283 333
284 void PulseAudioOutputStream::WaitForWriteRequest() { 334 int PulseAudioOutputStream::GetHardwareLatencyInBytes() {
285 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 335 int negative = 0;
336 pa_usec_t pa_latency_micros = 0;
337 if (pa_stream_get_latency(pa_stream_, &pa_latency_micros, &negative) != 0 ||
scherkus (not reviewing) 2012/10/17 22:44:53 I meant splitting as in if (pa_stream_get_latency
DaleCurtis 2012/11/06 01:35:07 Done.
338 negative) {
339 return 0;
340 }
286 341
287 if (stream_stopped_) 342 return (pa_latency_micros * params_.sample_rate() *
288 return; 343 params_.GetBytesPerFrame()) / base::Time::kMicrosecondsPerSecond;
289
290 // Iterate the PulseAudio mainloop. If PulseAudio doesn't request a write,
291 // post a task to iterate the mainloop again.
292 write_callback_handled_ = false;
293 pa_mainloop_iterate(pa_mainloop_, 1, NULL);
294 if (!write_callback_handled_) {
295 manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
296 &PulseAudioOutputStream::WaitForWriteRequest,
297 weak_factory_.GetWeakPtr()));
298 }
299 } 344 }
300 345
301 bool PulseAudioOutputStream::BufferPacketFromSource() { 346 void PulseAudioOutputStream::FillBuffer(void* buffer, size_t buffer_size) {
302 uint32 buffer_delay = client_buffer_->forward_bytes(); 347 size_t frames_filled = 0;
303 pa_usec_t pa_latency_micros;
304 int negative;
305 pa_stream_get_latency(playback_handle_, &pa_latency_micros, &negative);
306 uint32 hardware_delay = MicrosecondsToBytes(pa_latency_micros,
307 sample_rate_,
308 bytes_per_frame_);
309 // TODO(slock): Deal with negative latency (negative == 1). This has yet
310 // to happen in practice though.
311 scoped_refptr<media::DataBuffer> packet =
312 new media::DataBuffer(packet_size_);
313 int frames_filled = RunDataCallback(
314 audio_bus_.get(), AudioBuffersState(buffer_delay, hardware_delay));
315 size_t packet_size = frames_filled * bytes_per_frame_;
316 348
317 DCHECK_LE(packet_size, packet_size_); 349 // See comments on pa_stream_set_write_callback() in Open().
318 // Note: If this ever changes to output raw float the data must be clipped and 350 if (source_callback_) {
319 // sanitized since it may come from an untrusted source such as NaCl. 351 frames_filled = source_callback_->OnMoreData(
320 audio_bus_->ToInterleaved( 352 audio_bus_.get(), AudioBuffersState(0, GetHardwareLatencyInBytes()));
321 frames_filled, bytes_per_frame_ / channel_count_, 353 }
322 packet->GetWritableData());
323 354
324 if (packet_size == 0) 355 size_t packet_size = frames_filled * params_.GetBytesPerFrame();
325 return false; 356 CHECK_LE(packet_size, buffer_size);
357 if (packet_size) {
358 // Note: If this ever changes to output raw float the data must be clipped
359 // and sanitized since it may come from an untrusted source such as NaCl.
360 audio_bus_->ToInterleaved(
361 frames_filled, params_.bits_per_sample() / 8, buffer);
326 362
327 media::AdjustVolume(packet->GetWritableData(), 363 media::AdjustVolume(buffer, packet_size, params_.channels(),
328 packet_size, 364 params_.bits_per_sample() / 8, volume_);
329 channel_count_, 365 }
330 bytes_per_frame_ / channel_count_, 366
331 volume_); 367 // Zero out any unfilled data.
332 packet->SetDataSize(packet_size); 368 memset(buffer, 0, buffer_size - packet_size);
333 // Add the packet to the buffer.
334 client_buffer_->Append(packet);
335 return true;
336 } 369 }
337 370
338 void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) { 371 void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) {
339 // If we have enough data to fulfill the request, we can finish the write. 372 void* audio_buffer = NULL;
340 if (stream_stopped_) 373 // Tell pa_stream_begin_write() the buffer size we want.
341 return; 374 size_t bytes_available = requested_bytes;
342 375
343 // Request more data from the source until we can fulfill the request or 376 // Request a buffer from PulseAudio and ensure it's the correct size.
344 // fail to receive anymore data. 377 CHECK_GE(pa_stream_begin_write(
345 bool buffering_successful = true; 378 pa_stream_, &audio_buffer, &bytes_available), 0);
346 size_t forward_bytes = static_cast<size_t>(client_buffer_->forward_bytes()); 379 CHECK_EQ(bytes_available, requested_bytes);
no longer working on chromium 2012/10/14 19:03:41 I am not sure but afraid that we might be hit by t
DaleCurtis 2012/11/09 01:10:52 Done. Now breaks the buffer into whatever chunks p
347 while (forward_bytes < requested_bytes && buffering_successful) { 380 CHECK_EQ(requested_bytes, static_cast<size_t>(params_.GetBytesPerBuffer()));
no longer working on chromium 2012/10/14 19:03:41 if pa can't keep up with the pace of the callbacks
DaleCurtis 2012/11/09 01:10:52 Not so long as we force the buffer size in the pa_
348 buffering_successful = BufferPacketFromSource();
349 }
350 381
351 size_t bytes_written = 0; 382 FillBuffer(audio_buffer, bytes_available);
352 if (client_buffer_->forward_bytes() > 0) { 383 if (pa_stream_write(pa_stream_, audio_buffer, requested_bytes, NULL, 0LL,
353 // Try to fulfill the request by writing as many of the requested bytes to 384 PA_SEEK_RELATIVE) < 0) {
354 // the stream as we can. 385 if (source_callback_)
355 WriteToStream(requested_bytes, &bytes_written); 386 source_callback_->OnError(this, pa_context_errno(pa_context_));
356 }
357
358 if (bytes_written < requested_bytes) {
359 // We weren't able to buffer enough data to fulfill the request. Try to
360 // fulfill the rest of the request later.
361 manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
362 &PulseAudioOutputStream::FulfillWriteRequest,
363 weak_factory_.GetWeakPtr(),
364 requested_bytes - bytes_written));
365 } else {
366 // Continue playback.
367 manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
368 &PulseAudioOutputStream::WaitForWriteRequest,
369 weak_factory_.GetWeakPtr()));
370 }
371 }
372
373 void PulseAudioOutputStream::WriteToStream(size_t bytes_to_write,
374 size_t* bytes_written) {
375 *bytes_written = 0;
376 while (*bytes_written < bytes_to_write) {
377 const uint8* chunk;
378 int chunk_size;
379
380 // Stop writing if there is no more data available.
381 if (!client_buffer_->GetCurrentChunk(&chunk, &chunk_size))
382 break;
383
384 // Write data to stream.
385 pa_stream_write(playback_handle_, chunk, chunk_size,
386 NULL, 0LL, PA_SEEK_RELATIVE);
387 client_buffer_->Seek(chunk_size);
388 *bytes_written += chunk_size;
389 } 387 }
390 } 388 }
391 389
392 void PulseAudioOutputStream::Start(AudioSourceCallback* callback) { 390 void PulseAudioOutputStream::Start(AudioSourceCallback* callback) {
393 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 391 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
394 CHECK(callback); 392 CHECK(callback);
395 DLOG_IF(ERROR, !playback_handle_) 393 CHECK(pa_stream_);
396 << "Open() has not been called successfully"; 394
397 if (!playback_handle_) 395 AutoPulseLock auto_lock(pa_mainloop_);
396
397 // Ensure the context and stream are ready.
398 if (pa_context_get_state(pa_context_) != PA_CONTEXT_READY &&
399 pa_stream_get_state(pa_stream_) != PA_STREAM_READY) {
400 callback->OnError(this, pa_context_errno(pa_context_));
398 return; 401 return;
402 }
399 403
400 source_callback_ = callback; 404 source_callback_ = callback;
405 pa_stream_set_write_callback(pa_stream_, &StreamRequestCallback, this);
401 406
402 // Clear buffer, it might still have data in it. 407 // Uncork (resume) the stream.
403 client_buffer_->Clear(); 408 WaitForPulseOperation(pa_stream_cork(
404 stream_stopped_ = false; 409 pa_stream_, 0, &StreamSuccessCallback, this));
405
406 // Start playback.
407 manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
408 &PulseAudioOutputStream::WaitForWriteRequest,
409 weak_factory_.GetWeakPtr()));
410 } 410 }
411 411
412 void PulseAudioOutputStream::Stop() { 412 void PulseAudioOutputStream::Stop() {
413 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 413 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
414 414
415 stream_stopped_ = true; 415 // Cork (pause) the stream. Waiting for the main loop lock will ensure
416 // outstanding callbacks have completed.
417 AutoPulseLock auto_lock(pa_mainloop_);
418
419 // Flush the stream prior to cork, doing so after will cause hangs. Write
420 // callbacks are suspended while inside pa_threaded_mainloop_lock() so this
421 // is all thread safe.
422 WaitForPulseOperation(pa_stream_flush(
423 pa_stream_, &StreamSuccessCallback, this));
scherkus (not reviewing) 2012/10/17 22:44:53 indent
DaleCurtis 2012/11/09 01:10:52 Done.
424
425 WaitForPulseOperation(pa_stream_cork(
426 pa_stream_, 1, &StreamSuccessCallback, this));
427
428 pa_stream_set_write_callback(pa_stream_, NULL, NULL);
429 source_callback_ = NULL;
416 } 430 }
417 431
418 void PulseAudioOutputStream::SetVolume(double volume) { 432 void PulseAudioOutputStream::SetVolume(double volume) {
419 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 433 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
420 434
421 volume_ = static_cast<float>(volume); 435 volume_ = static_cast<float>(volume);
422 } 436 }
423 437
424 void PulseAudioOutputStream::GetVolume(double* volume) { 438 void PulseAudioOutputStream::GetVolume(double* volume) {
425 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 439 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
426 440
427 *volume = volume_; 441 *volume = volume_;
428 } 442 }
429 443
430 int PulseAudioOutputStream::RunDataCallback( 444 void PulseAudioOutputStream::WaitForPulseOperation(pa_operation* op) {
431 AudioBus* audio_bus, AudioBuffersState buffers_state) { 445 CHECK(op);
432 if (source_callback_) 446 while (pa_operation_get_state(op) == PA_OPERATION_RUNNING)
433 return source_callback_->OnMoreData(audio_bus, buffers_state); 447 pa_threaded_mainloop_wait(pa_mainloop_);
434 448 pa_operation_unref(op);
435 return 0;
436 } 449 }
437 450
438 } // namespace media 451 } // namespace media
OLDNEW
« no previous file with comments | « media/audio/pulse/pulse_output.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698