]> git.sesse.net Git - vlc/blob - modules/audio_output/pulse.c
DirectSound: correct include order
[vlc] / modules / audio_output / pulse.c
1 /*****************************************************************************
2  * pulse.c : Pulseaudio output plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2008 the VideoLAN team
5  * Copyright (C) 2009-2011 RĂ©mi Denis-Courmont
6  *
7  * Authors: Martin Hamrle <hamrle @ post . cz>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <vlc_common.h>
29 #include <vlc_plugin.h>
30 #include <vlc_aout.h>
31 #include <vlc_cpu.h>
32
33 #include <pulse/pulseaudio.h>
34
35 static int  Open        ( vlc_object_t * );
36 static void Close       ( vlc_object_t * );
37
38 vlc_module_begin ()
39     set_shortname( "PulseAudio" )
40     set_description( N_("Pulseaudio audio output") )
41     set_capability( "audio output", 160 )
42     set_category( CAT_AUDIO )
43     set_subcategory( SUBCAT_AUDIO_AOUT )
44     add_shortcut( "pulseaudio", "pa" )
45     set_callbacks( Open, Close )
46 vlc_module_end ()
47
48 /* TODO: single static mainloop */
49
50 /* NOTE:
51  * Be careful what you do when the PulseAudio mainloop is held, which is to say
52  * within PulseAudio callbacks, or after pa_threaded_mainloop_lock().
53  * In particular, a VLC variable callback cannot be triggered nor deleted with
54  * the PulseAudio mainloop lock held, if the callback acquires the lock. */
55
56 struct aout_sys_t
57 {
58     pa_stream *stream; /**< PulseAudio playback stream object */
59     pa_context *context; /**< PulseAudio connection context */
60     pa_threaded_mainloop *mainloop; /**< PulseAudio event loop */
61     pa_volume_t base_volume; /**< 0dB reference volume */
62     pa_cvolume cvolume; /**< actual sink input volume */
63     //uint32_t byterate; /**< bytes per second */
64 };
65
66 /* Context helpers */
67 static void context_state_cb(pa_context *c, void *userdata)
68 {
69     pa_threaded_mainloop *mainloop = userdata;
70
71     switch (pa_context_get_state(c)) {
72         case PA_CONTEXT_READY:
73         case PA_CONTEXT_FAILED:
74         case PA_CONTEXT_TERMINATED:
75             pa_threaded_mainloop_signal(mainloop, 0);
76         default:
77             break;
78     }
79 }
80
81 static bool context_wait(pa_threaded_mainloop *mainloop, pa_context *context)
82 {
83     pa_context_state_t state;
84
85     while ((state = pa_context_get_state(context)) != PA_CONTEXT_READY) {
86         if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
87             return -1;
88         pa_threaded_mainloop_wait(mainloop);
89     }
90     return 0;
91 }
92
93 static void error(aout_instance_t *aout, const char *msg, pa_context *context)
94 {
95     msg_Err(aout, "%s: %s", msg, pa_strerror(pa_context_errno(context)));
96 }
97
98 /* Sink */
99 static void sink_list_cb(pa_context *c, const pa_sink_info *i, int eol,
100                          void *userdata)
101 {
102     aout_instance_t *aout = userdata;
103     vlc_value_t val, text;
104
105     if (eol)
106         return;
107     (void) c;
108
109     msg_Dbg(aout, "listing sink %s (%"PRIu32"): %s", i->name, i->index,
110             i->description);
111     val.i_int = i->index;
112     text.psz_string = (char *)i->description;
113     var_Change(aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text);
114 }
115
116 static void sink_info_cb(pa_context *c, const pa_sink_info *i, int eol,
117                          void *userdata)
118 {
119     aout_instance_t *aout = userdata;
120     aout_sys_t *sys = aout->output.p_sys;
121
122     if (eol)
123         return;
124     (void) c;
125
126     /* PulseAudio flat volume NORM / 100% / 0dB corresponds to no software
127      * amplification and maximum hardware amplification.
128      * VLC maps DEFAULT / 100% to no gain at all (software/hardware).
129      * Thus we need to use the sink base_volume as a multiplier,
130      * if and only if flat volume is active for our current sink. */
131     if (i->flags & PA_SINK_FLAT_VOLUME)
132         sys->base_volume = i->base_volume;
133     else
134         sys->base_volume = PA_VOLUME_NORM;
135     msg_Dbg(aout, "base volume: %f", pa_sw_volume_to_linear(sys->base_volume));
136 }
137
138 /* Stream helpers */
139 static void stream_state_cb(pa_stream *s, void *userdata)
140 {
141     pa_threaded_mainloop *mainloop = userdata;
142
143     switch (pa_stream_get_state(s)) {
144         case PA_STREAM_READY:
145         case PA_STREAM_FAILED:
146         case PA_STREAM_TERMINATED:
147             pa_threaded_mainloop_signal(mainloop, 0);
148         default:
149             break;
150     }
151 }
152
153 static void stream_moved_cb(pa_stream *s, void *userdata)
154 {
155     aout_instance_t *aout = userdata;
156     aout_sys_t *sys = aout->output.p_sys;
157     pa_operation *op;
158     uint32_t idx = pa_stream_get_device_index(s);
159
160     msg_Dbg(aout, "connected to sink %"PRIu32": %s", idx,
161                   pa_stream_get_device_name(s));
162     op = pa_context_get_sink_info_by_index(sys->context, idx,
163                                            sink_info_cb, aout);
164     if (likely(op != NULL))
165         pa_operation_unref(op);
166
167     /* Update the variable if someone else moved our stream */
168     var_Change(aout, "audio-device", VLC_VAR_SETVALUE,
169                &(vlc_value_t){ .i_int = idx }, NULL);
170 }
171
172 static void stream_overflow_cb(pa_stream *s, void *userdata)
173 {
174     aout_instance_t *aout = userdata;
175
176     msg_Err(aout, "overflow");
177     (void) s;
178 }
179
180 static void stream_started_cb(pa_stream *s, void *userdata)
181 {
182     aout_instance_t *aout = userdata;
183
184     msg_Dbg(aout, "started");
185     (void) s;
186 }
187
188 static void stream_suspended_cb(pa_stream *s, void *userdata)
189 {
190     aout_instance_t *aout = userdata;
191
192     msg_Dbg(aout, "suspended");
193     (void) s;
194 }
195
196 static void stream_underflow_cb(pa_stream *s, void *userdata)
197 {
198     aout_instance_t *aout = userdata;
199
200     msg_Dbg(aout, "underflow");
201     (void) s;
202 }
203
204 static int stream_wait(pa_threaded_mainloop *mainloop, pa_stream *stream)
205 {
206     pa_stream_state_t state;
207
208     while ((state = pa_stream_get_state(stream)) != PA_STREAM_READY) {
209         if (state == PA_STREAM_FAILED || state == PA_STREAM_TERMINATED)
210             return -1;
211         pa_threaded_mainloop_wait(mainloop);
212     }
213     return 0;
214 }
215
216 /* Memory free callback. The block_t address is in front of the data. */
217 static void data_free(void *data)
218 {
219     block_t **pp = data, *block;
220
221     memcpy(&block, pp - 1, sizeof (block));
222     block_Release(block);
223 }
224
225 static void *data_convert(block_t **pp)
226 {
227     block_t *block = *pp;
228     /* In most cases, there is enough head room, and this is really cheap: */
229     block = block_Realloc(block, sizeof (block), block->i_buffer);
230     *pp = block;
231     if (unlikely(block == NULL))
232         return NULL;
233
234     memcpy(block->p_buffer, &block, sizeof (block));
235     block->p_buffer += sizeof (block);
236     block->i_buffer -= sizeof (block);
237     return block->p_buffer;
238 }
239
240 /*****************************************************************************
241  * Play: play a sound samples buffer
242  *****************************************************************************/
243 static void Play(aout_instance_t *aout)
244 {
245     aout_sys_t *sys = aout->output.p_sys;
246     pa_stream *s = sys->stream;
247
248     /* Note: The core already holds the output FIFO lock at this point.
249      * Therefore we must not under any circumstances (try to) acquire the
250      * output FIFO lock while the PulseAudio threaded main loop lock is held
251      * (including from PulseAudio stream callbacks). Otherwise lock inversion
252      * will take place, and sooner or later a deadlock. */
253     pa_threaded_mainloop_lock(sys->mainloop);
254
255     if (pa_stream_is_corked(sys->stream) > 0) {
256         pa_operation *op = pa_stream_cork(s, 0, NULL, NULL);
257         if (op != NULL)
258             pa_operation_unref(op);
259         msg_Dbg(aout, "uncorking");
260     }
261
262 #if 0
263     /* This function should be called by the LibVLC core a header of time,
264      * but not more than AOUT_MAX_PREPARE. The PulseAudio latency should be
265      * shorter than that (though it might not be the case with some evil piece
266      * of audio output hardware). So we may need to trigger playback early,
267      * (that is to say, short cut the PulseAudio prebuffering). Otherwise,
268      * audio and video may be out of synchronization. */
269     pa_usec_t latency;
270     int negative;
271     if (pa_stream_get_latency(s, &latency, &negative) < 0) {
272         /* Especially at start of stream, latency may not be known (yet). */
273         if (pa_context_errno(sys->context) != PA_ERR_NODATA)
274             error(aout, "cannot determine latency", sys->context);
275     } else {
276         mtime_t gap = aout_FifoFirstDate(aout, &aout->output.fifo) - mdate()
277                 - latency;
278
279         if (gap > AOUT_PTS_TOLERANCE)
280             msg_Dbg(aout, "buffer too early (%"PRId64" us)", gap);
281         else if (gap < -AOUT_PTS_TOLERANCE)
282             msg_Err(aout, "buffer too late (%"PRId64" us)", -gap);
283     }
284 #endif
285 #if 0 /* Fault injector to test underrun recovery */
286     static unsigned u = 0;
287     if ((++u % 500) == 0) {
288         msg_Err(aout, "fault injection");
289         msleep(CLOCK_FREQ*2);
290     }
291 #endif
292
293     /* This function is called exactly once per block in the output FIFO, so
294      * this for-loop is not necessary.
295      * If this function is changed to not always dequeue blocks, be sure to
296      * limit the queue size to a reasonable limit to avoid huge leaks. */
297     for (;;) {
298         block_t *block = aout_FifoPop(aout, &aout->output.fifo);
299         if (block == NULL)
300             break;
301
302         const void *ptr = data_convert(&block);
303         if (unlikely(ptr == NULL))
304             break;
305
306         size_t len = block->i_buffer;
307         //mtime_t pts = block->i_pts, duration = block->i_length;
308
309         if (pa_stream_write(s, ptr, len, data_free, 0, PA_SEEK_RELATIVE) < 0)
310         {
311             error(aout, "cannot write", sys->context);
312             block_Release(block);
313         }
314     }
315
316     pa_threaded_mainloop_unlock(sys->mainloop);
317 }
318
319 static int VolumeSet(aout_instance_t *aout, audio_volume_t vol, bool mute)
320 {
321     aout_sys_t *sys = aout->output.p_sys;
322     pa_threaded_mainloop *mainloop = sys->mainloop;
323     pa_operation *op;
324
325     uint32_t idx = pa_stream_get_index(sys->stream);
326     pa_volume_t volume = pa_sw_volume_from_linear(vol / (float)AOUT_VOLUME_DEFAULT);
327     pa_cvolume cvolume;
328
329     /* TODO: do not ruin the channel balance (if set outside VLC) */
330     /* TODO: notify UI about volume changes by other PulseAudio clients */
331     pa_cvolume_set(&sys->cvolume, sys->cvolume.channels, volume);
332     pa_sw_cvolume_multiply_scalar(&cvolume, &sys->cvolume, sys->base_volume);
333     assert(pa_cvolume_valid(&cvolume));
334
335     pa_threaded_mainloop_lock(mainloop);
336     op = pa_context_set_sink_input_volume(sys->context, idx, &cvolume, NULL, NULL);
337     if (likely(op != NULL))
338         pa_operation_unref(op);
339     op = pa_context_set_sink_input_mute(sys->context, idx, mute, NULL, NULL);
340     if (likely(op != NULL))
341         pa_operation_unref(op);
342     pa_threaded_mainloop_unlock(mainloop);
343
344     return 0;
345 }
346
347 static int StreamMove(vlc_object_t *obj, const char *varname, vlc_value_t old,
348                       vlc_value_t val, void *userdata)
349 {
350     aout_instance_t *aout = (aout_instance_t *)obj;
351     aout_sys_t *sys = aout->output.p_sys;
352     pa_stream *s = userdata;
353     pa_operation *op;
354     uint32_t idx = pa_stream_get_index(s);
355     uint32_t sink_idx = val.i_int;
356
357     (void) varname; (void) old;
358
359     pa_threaded_mainloop_lock(sys->mainloop);
360     op = pa_context_move_sink_input_by_index(sys->context, idx, sink_idx,
361                                              NULL, NULL);
362     if (likely(op != NULL)) {
363         pa_operation_unref(op);
364         msg_Dbg(aout, "moving to sink %"PRIu32, sink_idx);
365     } else
366         error(aout, "cannot move sink", sys->context);
367     pa_threaded_mainloop_unlock(sys->mainloop);
368
369     return (op != NULL) ? VLC_SUCCESS : VLC_EGENERIC;
370 }
371
372
373 /*****************************************************************************
374  * Open: open the audio device
375  *****************************************************************************/
376 static int Open(vlc_object_t *obj)
377 {
378     aout_instance_t *aout = (aout_instance_t *)obj;
379     pa_operation *op;
380
381     /* Sample format specification */
382     struct pa_sample_spec ss;
383
384     switch(aout->output.output.i_format)
385     {
386         case VLC_CODEC_F64B:
387             aout->output.output.i_format = VLC_CODEC_F32B;
388         case VLC_CODEC_F32B:
389             ss.format = PA_SAMPLE_FLOAT32BE;
390             break;
391         case VLC_CODEC_F64L:
392             aout->output.output.i_format = VLC_CODEC_F32L;
393         case VLC_CODEC_F32L:
394             ss.format = PA_SAMPLE_FLOAT32LE;
395             break;
396         case VLC_CODEC_FI32:
397             aout->output.output.i_format = VLC_CODEC_FL32;
398             ss.format = PA_SAMPLE_FLOAT32NE;
399             break;
400         case VLC_CODEC_S32B:
401             ss.format = PA_SAMPLE_S32BE;
402             break;
403         case VLC_CODEC_S32L:
404             ss.format = PA_SAMPLE_S32LE;
405             break;
406         case VLC_CODEC_S24B:
407             ss.format = PA_SAMPLE_S24BE;
408             break;
409         case VLC_CODEC_S24L:
410             ss.format = PA_SAMPLE_S24LE;
411             break;
412         case VLC_CODEC_S16B:
413             ss.format = PA_SAMPLE_S16BE;
414             break;
415         case VLC_CODEC_S16L:
416             ss.format = PA_SAMPLE_S16LE;
417             break;
418         case VLC_CODEC_S8:
419             aout->output.output.i_format = VLC_CODEC_U8;
420         case VLC_CODEC_U8:
421             ss.format = PA_SAMPLE_U8;
422             break;
423         default:
424             if (HAVE_FPU)
425             {
426                 aout->output.output.i_format = VLC_CODEC_FL32;
427                 ss.format = PA_SAMPLE_FLOAT32NE;
428             }
429             else
430             {
431                 aout->output.output.i_format = VLC_CODEC_S16N;
432                 ss.format = PA_SAMPLE_S16NE;
433             }
434             break;
435     }
436
437     ss.rate = aout->output.output.i_rate;
438     ss.channels = aout_FormatNbChannels(&aout->output.output);
439     if (!pa_sample_spec_valid(&ss)) {
440         msg_Err(aout, "unsupported sample specification");
441         return VLC_EGENERIC;
442     }
443
444     /* Channel mapping (order defined in vlc_aout.h) */
445     struct pa_channel_map map;
446     map.channels = 0;
447
448     if (aout->output.output.i_physical_channels & AOUT_CHAN_LEFT)
449         map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_LEFT;
450     if (aout->output.output.i_physical_channels & AOUT_CHAN_RIGHT)
451         map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_RIGHT;
452     if (aout->output.output.i_physical_channels & AOUT_CHAN_MIDDLELEFT)
453         map.map[map.channels++] = PA_CHANNEL_POSITION_SIDE_LEFT;
454     if (aout->output.output.i_physical_channels & AOUT_CHAN_MIDDLERIGHT)
455         map.map[map.channels++] = PA_CHANNEL_POSITION_SIDE_RIGHT;
456     if (aout->output.output.i_physical_channels & AOUT_CHAN_REARLEFT)
457         map.map[map.channels++] = PA_CHANNEL_POSITION_REAR_LEFT;
458     if (aout->output.output.i_physical_channels & AOUT_CHAN_REARRIGHT)
459         map.map[map.channels++] = PA_CHANNEL_POSITION_REAR_RIGHT;
460     if (aout->output.output.i_physical_channels & AOUT_CHAN_REARCENTER)
461         map.map[map.channels++] = PA_CHANNEL_POSITION_REAR_CENTER;
462     if (aout->output.output.i_physical_channels & AOUT_CHAN_CENTER)
463     {
464         if (ss.channels == 1)
465             map.map[map.channels++] = PA_CHANNEL_POSITION_MONO;
466         else
467             map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_CENTER;
468     }
469     if (aout->output.output.i_physical_channels & AOUT_CHAN_LFE)
470         map.map[map.channels++] = PA_CHANNEL_POSITION_LFE;
471
472     for (unsigned i = 0; map.channels < ss.channels; i++) {
473         map.map[map.channels++] = PA_CHANNEL_POSITION_AUX0 + i;
474         msg_Warn(aout, "mapping channel %"PRIu8" to AUX%u", map.channels, i);
475     }
476
477     if (!pa_channel_map_valid(&map)) {
478         msg_Err(aout, "unsupported channel map");
479         return VLC_EGENERIC;
480     } else {
481         const char *name = pa_channel_map_to_pretty_name(&map);
482         msg_Dbg(aout, "using %s channel map", (name != NULL) ? name : "?");
483     }
484
485     /* Stream parameters */
486     const pa_stream_flags_t flags = PA_STREAM_INTERPOLATE_TIMING
487                                   | PA_STREAM_AUTO_TIMING_UPDATE
488                                   | PA_STREAM_ADJUST_LATENCY
489                                   | PA_STREAM_START_CORKED;
490
491     const uint32_t byterate = pa_bytes_per_second(&ss);
492     struct pa_buffer_attr attr;
493     /* no point in larger buffers on PA side than VLC */
494     attr.maxlength = -1;
495     attr.tlength = byterate * AOUT_MAX_ADVANCE_TIME / CLOCK_FREQ;
496     attr.prebuf = byterate * AOUT_MAX_PREPARE_TIME / CLOCK_FREQ;
497     attr.minreq = -1;
498     attr.fragsize = 0; /* not used for output */
499
500     /* Allocate structures */
501     aout_sys_t *sys = malloc(sizeof(*sys));
502     if (unlikely(sys == NULL))
503         return VLC_ENOMEM;
504     aout->output.p_sys = sys;
505     sys->context = NULL;
506     sys->stream = NULL;
507     //sys->byterate = byterate;
508
509     /* Channel volume */
510     sys->base_volume = PA_VOLUME_NORM;
511     pa_cvolume_set(&sys->cvolume, ss.channels, PA_VOLUME_NORM);
512
513     /* Allocate threaded main loop */
514     pa_threaded_mainloop *mainloop = pa_threaded_mainloop_new();
515     if (unlikely(mainloop == NULL)) {
516         free(sys);
517         return VLC_ENOMEM;
518     }
519     sys->mainloop = mainloop;
520
521     if (pa_threaded_mainloop_start(mainloop) < 0) {
522         pa_threaded_mainloop_free(mainloop);
523         free(sys);
524         return VLC_ENOMEM;
525     }
526     pa_threaded_mainloop_lock(mainloop);
527
528     /* Connect to PulseAudio server */
529     char *user_agent = var_InheritString(aout, "user-agent");
530     pa_context *ctx = pa_context_new(pa_threaded_mainloop_get_api(mainloop),
531                                      user_agent);
532     free(user_agent);
533     if (unlikely(ctx == NULL))
534         goto fail;
535     sys->context = ctx;
536
537     pa_context_set_state_callback(ctx, context_state_cb, mainloop);
538     if (pa_context_connect(ctx, NULL, 0, NULL) < 0
539      || context_wait(mainloop, ctx)) {
540         error(aout, "cannot connect to server", ctx);
541         goto fail;
542     }
543
544     /* Create a playback stream */
545     pa_stream *s = pa_stream_new(ctx, "audio stream", &ss, &map);
546     if (s == NULL) {
547         error(aout, "cannot create stream", ctx);
548         goto fail;
549     }
550     sys->stream = s;
551     pa_stream_set_state_callback(s, stream_state_cb, mainloop);
552     pa_stream_set_moved_callback(s, stream_moved_cb, aout);
553     pa_stream_set_overflow_callback(s, stream_overflow_cb, aout);
554     pa_stream_set_started_callback(s, stream_started_cb, aout);
555     pa_stream_set_suspended_callback(s, stream_suspended_cb, aout);
556     pa_stream_set_underflow_callback(s, stream_underflow_cb, aout);
557
558     if (pa_stream_connect_playback(s, NULL, &attr, flags, NULL, NULL) < 0
559      || stream_wait(mainloop, s)) {
560         error(aout, "cannot connect stream", ctx);
561         goto fail;
562     }
563
564     const struct pa_buffer_attr *pba = pa_stream_get_buffer_attr(s);
565     msg_Dbg(aout, "using buffer metrics: maxlength=%u, tlength=%u, "
566             "prebuf=%u, minreq=%u",
567             pba->maxlength, pba->tlength, pba->prebuf, pba->minreq);
568
569     aout->output.i_nb_samples = pba->minreq / pa_frame_size(&ss);
570
571     var_Create(aout, "audio-device", VLC_VAR_INTEGER|VLC_VAR_HASCHOICE);
572     var_Change(aout, "audio-device", VLC_VAR_SETTEXT,
573                &(vlc_value_t){ .psz_string = (char *)_("Audio device") },
574                NULL);
575     var_AddCallback (aout, "audio-device", StreamMove, s);
576     op = pa_context_get_sink_info_list(ctx, sink_list_cb, aout);
577     /* We may need to wait for completion... once LibVLC supports this */
578     if (op != NULL)
579         pa_operation_unref(op);
580     stream_moved_cb(s, aout);
581     pa_threaded_mainloop_unlock(mainloop);
582
583     aout->output.pf_play = Play;
584     aout->output.pf_volume_set = VolumeSet;
585     return VLC_SUCCESS;
586
587 fail:
588     pa_threaded_mainloop_unlock(mainloop);
589     Close(obj);
590     return VLC_EGENERIC;
591 }
592
593 /*****************************************************************************
594  * Close: close the audio device
595  *****************************************************************************/
596 static void Close (vlc_object_t *obj)
597 {
598     aout_instance_t *aout = (aout_instance_t *)obj;
599     aout_sys_t *sys = aout->output.p_sys;
600     pa_threaded_mainloop *mainloop = sys->mainloop;
601     pa_context *ctx = sys->context;
602     pa_stream *s = sys->stream;
603
604     if (s != NULL) {
605         /* The callback takes mainloop lock, so it CANNOT be held here! */
606         var_DelCallback (aout, "audio-device", StreamMove, s);
607         var_Destroy (aout, "audio-device");
608     }
609
610     pa_threaded_mainloop_lock(mainloop);
611     if (s != NULL) {
612         pa_operation *op;
613
614         op = pa_stream_flush(s, NULL, NULL);
615         if (op != NULL)
616             pa_operation_unref(op);
617         op = pa_stream_drain(s, NULL, NULL);
618         if (op != NULL)
619             pa_operation_unref(op);
620         pa_stream_disconnect(s);
621         pa_stream_unref(s);
622     }
623     if (ctx != NULL)
624         pa_context_unref(ctx);
625     pa_threaded_mainloop_unlock(mainloop);
626     pa_threaded_mainloop_free(mainloop);
627     free(sys);
628 }