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

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

Powered by Google App Engine
This is Rietveld 408576698