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

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

Issue 7473021: PulseAudio Sound Playback on Linux (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: "New runflow per offline with vrk" Created 9 years, 4 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/linux/pulse_output.h ('k') | media/base/media_switches.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/audio/linux/pulse_output.h"
6
7 #include "media/audio/audio_util.h"
8 #include "media/audio/linux/audio_manager_linux.h"
9 #include "media/base/data_buffer.h"
10 #include "media/base/seekable_buffer.h"
11
12 static pa_sample_format_t BitsToFormat(int bits_per_sample) {
13 switch (bits_per_sample) {
14 // Unsupported sample formats shown for reference. I am assuming we want
15 // signed and little endian because that is what we gave to ALSA.
16 case 8:
17 return PA_SAMPLE_U8;
18 // Also 8-bits: PA_SAMPLE_ALAW and PA_SAMPLE_ULAW
19 case 16:
20 return PA_SAMPLE_S16LE;
21 // Also 16-bits: PA_SAMPLE_S16BE (big endian).
22 case 24:
23 return PA_SAMPLE_S24LE;
24 // Also 24-bits: PA_SAMPLE_S24BE (big endian).
25 // Other cases: PA_SAMPLE_24_32LE (in LSB of 32-bit field, little endian),
26 // and PA_SAMPLE_24_32BE (in LSB of 32-bit field, big endian),
27 case 32:
28 return PA_SAMPLE_S32LE;
29 // Also 32-bits: PA_SAMPLE_S32BE (big endian),
30 // PA_SAMPLE_FLOAT32LE (floating point little endian),
31 // and PA_SAMPLE_FLOAT32BE (floating point big endian).
32 default:
33 return PA_SAMPLE_INVALID;
34 }
35 }
36
37 static size_t MicrosecondsToBytes(
38 uint32 microseconds, uint32 sample_rate, size_t bytes_per_frame) {
39 return microseconds * sample_rate * bytes_per_frame /
40 base::Time::kMicrosecondsPerSecond;
41 }
42
43 void PulseAudioOutputStream::ContextStateCallback(pa_context* context,
44 void* state_addr) {
45 pa_context_state_t* state = static_cast<pa_context_state_t*>(state_addr);
46 *state = pa_context_get_state(context);
47 }
48
49 void PulseAudioOutputStream::WriteRequestCallback(
50 pa_stream* playback_handle, size_t length, void* stream_addr) {
51 PulseAudioOutputStream* stream =
52 static_cast<PulseAudioOutputStream*>(stream_addr);
53
54 DCHECK_EQ(stream->message_loop_, MessageLoop::current());
55
56 stream->write_callback_handled_ = true;
57
58 // Fulfill write request.
59 stream->FulfillWriteRequest(length);
60
61 // Continue playback.
62 stream->message_loop_->PostTask(
63 FROM_HERE,
64 stream->method_factory_.NewRunnableMethod(
65 &PulseAudioOutputStream::WaitForWriteRequest));
66 }
67
68 PulseAudioOutputStream::PulseAudioOutputStream(const AudioParameters& params,
69 AudioManagerLinux* manager,
70 MessageLoop* message_loop)
71 : channel_layout_(params.channel_layout),
72 channel_count_(ChannelLayoutToChannelCount(channel_layout_)),
73 sample_format_(BitsToFormat(params.bits_per_sample)),
74 sample_rate_(params.sample_rate),
75 bytes_per_frame_(params.channels * params.bits_per_sample / 8),
76 manager_(manager),
77 pa_context_(NULL),
78 pa_mainloop_(NULL),
79 playback_handle_(NULL),
80 packet_size_(params.GetPacketSize()),
81 frames_per_packet_(packet_size_ / bytes_per_frame_),
82 client_buffer_(NULL),
83 volume_(1.0f),
84 stream_stopped_(true),
85 write_callback_handled_(false),
86 message_loop_(message_loop),
87 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
88 source_callback_(NULL) {
89 DCHECK_EQ(message_loop_, MessageLoop::current());
90
91 DCHECK(manager_);
92
93 // TODO(slock): Sanity check input values.
94 }
95
96 PulseAudioOutputStream::~PulseAudioOutputStream() {
97 // All internal structures should already have been freed in Close(),
98 // which calls AudioManagerLinux::Release which deletes this object.
99 DCHECK(!playback_handle_);
100 DCHECK(!pa_context_);
101 DCHECK(!pa_mainloop_);
102 }
103
104 bool PulseAudioOutputStream::Open() {
105 DCHECK_EQ(message_loop_, MessageLoop::current());
106
107 // TODO(slock): Possibly move most of this to a OpenPlaybackDevice function in
108 // a new class 'pulse_util', like alsa_util.
109
110 // Create a mainloop API and connect to the default server.
111 pa_mainloop_ = pa_mainloop_new();
112 pa_mainloop_api* pa_mainloop_api = pa_mainloop_get_api(pa_mainloop_);
113 pa_context_ = pa_context_new(pa_mainloop_api, "Chromium");
114 pa_context_state_t pa_context_state = PA_CONTEXT_UNCONNECTED;
115 pa_context_connect(pa_context_, NULL, PA_CONTEXT_NOFLAGS, NULL);
116
117 // Wait until PulseAudio is ready.
118 pa_context_set_state_callback(pa_context_, &ContextStateCallback,
119 &pa_context_state);
120 while (pa_context_state != PA_CONTEXT_READY) {
121 pa_mainloop_iterate(pa_mainloop_, 1, NULL);
122 if (pa_context_state == PA_CONTEXT_FAILED ||
123 pa_context_state == PA_CONTEXT_TERMINATED) {
124 stream_stopped_ = true;
125 Reset();
126 return false;
127 }
128 }
129
130 // Set sample specifications and open playback stream.
131 pa_sample_spec pa_sample_specifications;
132 pa_sample_specifications.format = sample_format_;
133 pa_sample_specifications.rate = sample_rate_;
134 pa_sample_specifications.channels = channel_count_;
135 playback_handle_ = pa_stream_new(pa_context_, "Playback",
136 &pa_sample_specifications, NULL);
137
138 // Initialize client buffer.
139 uint32 output_packet_size = frames_per_packet_ * bytes_per_frame_;
140 client_buffer_.reset(new media::SeekableBuffer(0, output_packet_size));
141
142 // Set write callback.
143 pa_stream_set_write_callback(playback_handle_, &WriteRequestCallback,
144 this);
145
146 // Set server-side buffer attributes.
147 // (uint32_t)-1 is the default and recommended value from PulseAudio's
148 // documentation, found at:
149 // http://freedesktop.org/software/pulseaudio/doxygen/structpa__buffer__attr.h tml.
150 pa_buffer_attr pa_buffer_attributes;
151 pa_buffer_attributes.maxlength = static_cast<uint32_t>(-1);
152 pa_buffer_attributes.tlength = output_packet_size;
153 pa_buffer_attributes.prebuf = static_cast<uint32_t>(-1);
154 pa_buffer_attributes.minreq = static_cast<uint32_t>(-1);
155 pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1);
156
157 // Connect playback stream.
158 pa_stream_connect_playback(playback_handle_, NULL,
159 &pa_buffer_attributes,
160 (pa_stream_flags_t)
161 (PA_STREAM_INTERPOLATE_TIMING |
162 PA_STREAM_ADJUST_LATENCY |
163 PA_STREAM_AUTO_TIMING_UPDATE),
164 NULL, NULL);
165
166 if (!playback_handle_) {
167 stream_stopped_ = true;
168 Reset();
169 return false;
170 }
171
172 return true;
173 }
174
175 void PulseAudioOutputStream::Reset() {
176 // Close the stream.
177 if (playback_handle_) {
178 pa_stream_flush(playback_handle_, NULL, NULL);
179 pa_stream_disconnect(playback_handle_);
180
181 // Release PulseAudio structures.
182 pa_stream_unref(playback_handle_);
183 playback_handle_ = NULL;
184 }
185 if (pa_context_) {
186 pa_context_unref(pa_context_);
187 pa_context_ = NULL;
188 }
189 if (pa_mainloop_) {
190 pa_mainloop_free(pa_mainloop_);
191 pa_mainloop_ = NULL;
192 }
193 // |pa_mainloop_api| is freed with |pa_mainloop_|.
194
195 // Release internal buffer.
196 client_buffer_.reset();
197 }
198
199 void PulseAudioOutputStream::Close() {
200 DCHECK_EQ(message_loop_, MessageLoop::current());
201
202 Reset();
203
204 // Signal to the manager that we're closed and can be removed.
205 // This should be the last call in the function as it deletes "this".
206 manager_->ReleaseOutputStream(this);
207 }
208
209 void PulseAudioOutputStream::WaitForWriteRequest() {
210 DCHECK_EQ(message_loop_, MessageLoop::current());
211
212 // Iterate the PulseAudio mainloop. If the stream isn't stopped or PulseAudio
213 // doesn't request a write, post a task to iterate the mainloop again.
214 write_callback_handled_ = false;
215 pa_mainloop_iterate(pa_mainloop_, 1, NULL);
216 if (!write_callback_handled_ && !stream_stopped_) {
217 message_loop_->PostTask(
218 FROM_HERE,
219 method_factory_.NewRunnableMethod(
220 &PulseAudioOutputStream::WaitForWriteRequest));
221 }
222 }
223
224 bool PulseAudioOutputStream::BufferPacketFromSource() {
225 uint32 buffer_delay = client_buffer_->forward_bytes();
226 pa_usec_t pa_latency_micros;
227 int negative;
228 pa_stream_get_latency(playback_handle_, &pa_latency_micros, &negative);
229 uint32 hardware_delay = MicrosecondsToBytes(pa_latency_micros,
230 sample_rate_,
231 bytes_per_frame_);
232 // TODO(slock): Deal with negative latency (negative == 1). This has yet
233 // to happen in practice though.
234 scoped_refptr<media::DataBuffer> packet =
235 new media::DataBuffer(packet_size_);
vrk (LEFT CHROMIUM) 2011/08/17 18:59:23 nit: indentation
slock 2011/08/17 21:43:14 Done.
236 size_t packet_size = RunDataCallback(packet->GetWritableData(),
237 packet->GetBufferSize(),
238 AudioBuffersState(buffer_delay,
239 hardware_delay));
240
241 if (packet_size > 0) {
vrk (LEFT CHROMIUM) 2011/08/17 18:59:23 nit: a little nicer to say if (packet_size == 0)
slock 2011/08/17 21:43:14 Done.
242 // TODO(slock): Swizzling and downmixing.
243 media::AdjustVolume(packet->GetWritableData(),
244 packet_size,
245 channel_count_,
246 bytes_per_frame_ / channel_count_,
247 volume_);
248 packet->SetDataSize(packet_size);
249 // Add the packet to the buffer.
250 client_buffer_->Append(packet);
251 return true;
252 }
253 return false;
254 }
255
256 void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) {
257 // If we have enough data to fulfill the request, we can finish the write.
258 if (!stream_stopped_) {
vrk (LEFT CHROMIUM) 2011/08/17 18:59:23 nit: little cleaner to say if (stream_stopped_)
slock 2011/08/17 21:43:14 Done.
259 if (client_buffer_->forward_bytes() >= requested_bytes) {
vrk (LEFT CHROMIUM) 2011/08/17 18:59:23 Lines 259 - 282 can be simplified like follows: /
slock 2011/08/17 21:43:14 I modified this slightly because it subtly implied
260 WriteToStream(requested_bytes);
261 return;
262 } else {
263 // Request more data from the source until we can fulfill the request or
264 // fail to receive anymore data.
265 while (BufferPacketFromSource()) {
266 if (client_buffer_->forward_bytes() >= requested_bytes) {
267 WriteToStream(requested_bytes);
268 return;
269 }
270 }
271 // We could not fulfill the write request at this time. We will write as
272 // much as we can.
273 WriteToStream(client_buffer_->forward_bytes());
274
275 // Try to fulfill the rest of the request later.
276 message_loop_->PostTask(
277 FROM_HERE,
278 method_factory_.NewRunnableMethod(
279 &PulseAudioOutputStream::FulfillWriteRequest,
280 requested_bytes - client_buffer_->forward_bytes()));
281 }
282 }
283 }
284
285 void PulseAudioOutputStream::WriteToStream(size_t bytes_to_write) {
286 // This while loop will not stall because this function is only called when we
vrk (LEFT CHROMIUM) 2011/08/17 18:59:23 " This while loop will not stall because this func
slock 2011/08/17 21:43:14 Done.
287 // have enough bytes buffered to fulfill the request.
288 size_t bytes_written = 0;
289 while (bytes_written < bytes_to_write) {
290 // Try to get data to write.
291 const uint8* chunk;
292 size_t chunk_size;
293 client_buffer_->GetCurrentChunk(&chunk, &chunk_size);
294
295 pa_stream_write(playback_handle_, chunk, chunk_size,
296 NULL, 0LL, PA_SEEK_RELATIVE);
297 client_buffer_->Seek(chunk_size);
298 bytes_written += chunk_size;
299 }
300 }
301
302 void PulseAudioOutputStream::Start(AudioSourceCallback* callback) {
303 DCHECK_EQ(message_loop_, MessageLoop::current());
304
305 CHECK(callback);
306 source_callback_ = callback;
307
308 // Clear buffer, it might still have data in it.
309 client_buffer_->Clear();
310 stream_stopped_ = false;
311
312 // Start playback.
313 message_loop_->PostTask(
314 FROM_HERE,
315 method_factory_.NewRunnableMethod(
316 &PulseAudioOutputStream::WaitForWriteRequest));
317 }
318
319 void PulseAudioOutputStream::Stop() {
320 DCHECK_EQ(message_loop_, MessageLoop::current());
321
322 stream_stopped_ = true;
323 }
324
325 void PulseAudioOutputStream::SetVolume(double volume) {
326 DCHECK_EQ(message_loop_, MessageLoop::current());
327
328 volume_ = static_cast<float>(volume);
329 }
330
331 void PulseAudioOutputStream::GetVolume(double* volume) {
332 DCHECK_EQ(message_loop_, MessageLoop::current());
333
334 *volume = volume_;
335 }
336
337 uint32 PulseAudioOutputStream::RunDataCallback(
338 uint8* dest, uint32 max_size, AudioBuffersState buffers_state) {
339 if (source_callback_)
340 return source_callback_->OnMoreData(this, dest, max_size, buffers_state);
341
342 return 0;
343 }
OLDNEW
« no previous file with comments | « media/audio/linux/pulse_output.h ('k') | media/base/media_switches.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698