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

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 buffering scheme, 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* userdata) {
45 pa_context_state_t* state = static_cast<pa_context_state_t*>(userdata);
46 *state = pa_context_get_state(context);
47 }
48
49 void PulseAudioOutputStream::StreamWriteRequestCallback(
50 pa_stream* playback_handle, size_t length, void* userdata) {
51 PulseAudioOutputStream* stream =
52 static_cast<PulseAudioOutputStream*>(userdata);
53
54 DCHECK_EQ(stream->message_loop_, MessageLoop::current());
55
56 stream->write_callback_handled_ = true;
57
58 stream->Write(length);
59
60 // Continue playback.
61 stream->message_loop_->PostTask(
62 FROM_HERE,
63 stream->method_factory_.NewRunnableMethod(
64 &PulseAudioOutputStream::WaitForWriteTask));
65 }
66
67 PulseAudioOutputStream::PulseAudioOutputStream(const AudioParameters& params,
68 AudioManagerLinux* manager,
69 MessageLoop* message_loop)
70 : channel_layout_(params.channel_layout),
71 channel_count_(ChannelLayoutToChannelCount(channel_layout_)),
72 sample_format_(BitsToFormat(params.bits_per_sample)),
73 sample_rate_(params.sample_rate),
74 bytes_per_frame_(params.channels * params.bits_per_sample / 8),
75 manager_(manager),
76 pa_context_(NULL),
77 pa_mainloop_(NULL),
78 playback_handle_(NULL),
79 packet_size_(params.GetPacketSize()),
80 frames_per_packet_(packet_size_ / bytes_per_frame_),
81 client_buffer_(NULL),
82 source_exhausted_(false),
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_, &StreamWriteRequestCallback,
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::WaitForWriteTask() {
210 DCHECK_EQ(message_loop_, MessageLoop::current());
211
212 // Iterate the PulseAudio mainloop until the WriteCallback is called or the
213 // stream is stopped. The PulseAudio mainloop will call the WriteCallback to
214 // request more data when the server-side buffer needs more data to write to
215 // the audio sink. WriteCallback moves data from the |client_buffer_| to the
216 // server-side buffer. If the |client_buffer_| doesn't have enough data for
217 // the request, BufferPacketInClient is called to move data from the source
218 // into |client_buffer_|.
219 // WARNING: This blocks in PulseAudio until a WriteCallback occurs.
220 // TODO(slock): Fix this.
221 write_callback_handled_ = false;
222 while (!write_callback_handled_ && !stream_stopped_ && !source_exhausted_)
223 pa_mainloop_iterate(pa_mainloop_, 1, NULL);
224 }
225
226 void PulseAudioOutputStream::BufferPacket() {
227 uint32 buffer_delay = client_buffer_->forward_bytes();
228 pa_usec_t pa_latency_micros;
229 int negative;
230 pa_stream_get_latency(playback_handle_, &pa_latency_micros, &negative);
231 uint32 hardware_delay = MicrosecondsToBytes(pa_latency_micros,
232 sample_rate_,
233 bytes_per_frame_);
234 // TODO(slock): Deal with negative latency (negative == 1). This has yet
235 // to happen in practice though.
236 scoped_refptr<media::DataBuffer> packet =
237 new media::DataBuffer(packet_size_);
238 size_t packet_size = RunDataCallback(packet->GetWritableData(),
239 packet->GetBufferSize(),
240 AudioBuffersState(buffer_delay,
241 hardware_delay));
242
243 // TODO(slock): Swizzling and downmixing.
244
245 media::AdjustVolume(packet->GetWritableData(),
vrk (LEFT CHROMIUM) 2011/08/16 18:19:24 I would think AdjustVolume should only be called i
slock 2011/08/16 23:18:58 Done.
246 packet_size,
247 channel_count_,
248 bytes_per_frame_ / channel_count_,
249 volume_);
250
251 if (packet_size > 0) {
252 packet->SetDataSize(packet_size);
253 // Add the packet to the buffer.
254 client_buffer_->Append(packet);
255 }
256 }
257
258 void PulseAudioOutputStream::Write(size_t requested_bytes) {
259 // If we don't have enough data buffered, get more from the source.
260 while (client_buffer_->forward_bytes() < requested_bytes)
vrk (LEFT CHROMIUM) 2011/08/16 18:19:24 Fix the stalling here if you don't have enough dat
slock 2011/08/16 23:18:58 Done.
261 BufferPacket();
262
263 size_t bytes_written = 0;
264 while (bytes_written < requested_bytes) {
265 // If we need more data from the source to complete the request.
266
267 // Get data to write.
268 const uint8* current_chunk;
269 size_t current_chunk_size;
270 client_buffer_->GetCurrentChunk(&current_chunk, &current_chunk_size);
271
272 if (current_chunk_size > 0) {
273 pa_stream_write(playback_handle_, current_chunk, current_chunk_size,
274 NULL, 0LL, PA_SEEK_RELATIVE);
275 client_buffer_->Seek(current_chunk_size);
276 bytes_written += current_chunk_size;
277 }
278 }
279 }
280
281 void PulseAudioOutputStream::Start(AudioSourceCallback* callback) {
282 DCHECK_EQ(message_loop_, MessageLoop::current());
283
284 CHECK(callback);
285 source_callback_ = callback;
286
287 // Clear buffer, it might still have data in it.
288 client_buffer_->Clear();
289 stream_stopped_ = false;
290 source_exhausted_ = false;
291
292 // Start playback.
293 message_loop_->PostTask(
294 FROM_HERE,
295 method_factory_.NewRunnableMethod(
296 &PulseAudioOutputStream::WaitForWriteTask));
297 }
298
299 void PulseAudioOutputStream::Stop() {
300 DCHECK_EQ(message_loop_, MessageLoop::current());
301
302 // Effect will not be instantaneous as the PulseAudio server buffer drains.
303 // TODO(slock): Immediate stopping.
304 stream_stopped_ = true;
305 }
306
307 void PulseAudioOutputStream::SetVolume(double volume) {
308 DCHECK_EQ(message_loop_, MessageLoop::current());
309
310 volume_ = static_cast<float>(volume);
311 }
312
313 void PulseAudioOutputStream::GetVolume(double* volume) {
314 DCHECK_EQ(message_loop_, MessageLoop::current());
315
316 *volume = volume_;
317 }
318
319 uint32 PulseAudioOutputStream::RunDataCallback(
320 uint8* dest, uint32 max_size, AudioBuffersState buffers_state) {
321 if (source_callback_)
322 return source_callback_->OnMoreData(this, dest, max_size, buffers_state);
323
324 return 0;
325 }
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