Commit e1fd025f authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

PulseAudio: preserve audio format, fix some races, simplify and cleanup

 - preserve sample format from VLC, if possible,
 - preserve physical channels mapping,
 - remove a useless callback,
 - fix small race conditions with pa_*_wait() on startup,
 - cosmetic changes.
(cherry picked from commit 176927134d1d82836f7a545cf5cc3a19a842a8d6)
(cherry picked from commit a6274616f21886a1b025c62ac0bca30b165d3db8)
(cherry picked from commit 90ef491bb4fa4b93b4bc620f656828ad47f0d8a7)

Conflicts:

	modules/audio_output/pulse.c
parent 53543a4d
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* pulse.c : Pulseaudio output plugin for vlc * pulse.c : Pulseaudio output plugin for vlc
***************************************************************************** *****************************************************************************
* Copyright (C) 2008 the VideoLAN team * Copyright (C) 2008 the VideoLAN team
* Copyright (C) 2009-2011 Rémi Denis-Courmont
* *
* Authors: Martin Hamrle <hamrle @ post . cz> * Authors: Martin Hamrle <hamrle @ post . cz>
* *
...@@ -42,8 +43,6 @@ ...@@ -42,8 +43,6 @@
#include <vlc_xlib.h> #include <vlc_xlib.h>
#endif #endif
#include <assert.h>
/***************************************************************************** /*****************************************************************************
* aout_sys_t: Pulseaudio output method descriptor * aout_sys_t: Pulseaudio output method descriptor
***************************************************************************** *****************************************************************************
...@@ -52,37 +51,13 @@ ...@@ -52,37 +51,13 @@
*****************************************************************************/ *****************************************************************************/
struct aout_sys_t struct aout_sys_t
{ {
/** PulseAudio playback stream object */ struct pa_stream *stream; /**< PulseAudio playback stream object */
struct pa_stream *stream; struct pa_context *context; /**< PulseAudio connection context */
struct pa_threaded_mainloop *mainloop; /**< PulseAudio event loop */
/** PulseAudio connection context */
struct pa_context *context;
/** Main event loop object */
struct pa_threaded_mainloop *mainloop;
int started;
size_t buffer_size; size_t buffer_size;
mtime_t start_date; mtime_t start_date;
}; };
#if 0
#define PULSE_DEBUG( ...) \
msg_Dbg( p_aout, __VA_ARGS__ )
#else
#define PULSE_DEBUG( ...) \
(void) 0
#endif
#define CHECK_DEAD_GOTO(label) do { \
if (!p_sys->context || pa_context_get_state(p_sys->context) != PA_CONTEXT_READY || \
!p_sys->stream || pa_stream_get_state(p_sys->stream) != PA_STREAM_READY) { \
msg_Err(p_aout, "Connection died: %s", p_sys->context ? pa_strerror(pa_context_errno(p_sys->context)) : "NULL"); \
goto label; \
} \
} while(0);
/***************************************************************************** /*****************************************************************************
* Local prototypes * Local prototypes
*****************************************************************************/ *****************************************************************************/
...@@ -93,9 +68,7 @@ static void Play ( aout_instance_t * ); ...@@ -93,9 +68,7 @@ static void Play ( aout_instance_t * );
static void context_state_cb(pa_context *c, void *userdata); static void context_state_cb(pa_context *c, void *userdata);
static void stream_state_cb(pa_stream *s, void * userdata); static void stream_state_cb(pa_stream *s, void * userdata);
static void stream_request_cb(pa_stream *s, size_t length, void *userdata); static void stream_request_cb(pa_stream *s, size_t length, void *userdata);
static void stream_latency_update_cb(pa_stream *s, void *userdata);
static void success_cb(pa_stream *s, int sucess, void *userdata); static void success_cb(pa_stream *s, int sucess, void *userdata);
static void uninit(aout_instance_t *p_aout);
/***************************************************************************** /*****************************************************************************
* Module descriptor * Module descriptor
...@@ -117,420 +90,365 @@ vlc_module_end () ...@@ -117,420 +90,365 @@ vlc_module_end ()
static int Open ( vlc_object_t *p_this ) static int Open ( vlc_object_t *p_this )
{ {
aout_instance_t *p_aout = (aout_instance_t *)p_this; aout_instance_t *p_aout = (aout_instance_t *)p_this;
struct aout_sys_t * p_sys;
struct pa_sample_spec ss;
const struct pa_buffer_attr *buffer_attr;
struct pa_buffer_attr a;
struct pa_channel_map map;
char * p_client_name;
#if !defined(PA_CHECK_VERSION) || !PA_CHECK_VERSION(0,9,22) #if !defined(PA_CHECK_VERSION) || !PA_CHECK_VERSION(0,9,22)
if( !vlc_xlib_init( p_this ) ) if( !vlc_xlib_init( p_this ) )
return VLC_EGENERIC; return VLC_EGENERIC;
#endif #endif
/* Allocate structures */ /* Sample format specification */
p_aout->output.p_sys = p_sys = calloc( 1, sizeof( aout_sys_t ) ); struct pa_sample_spec ss;
if( p_sys == NULL )
return VLC_ENOMEM;
PULSE_DEBUG( "Pulse start initialization");
ss.channels = aout_FormatNbChannels( &p_aout->output.output ); /* Get the input stream channel count */
/* Setup the pulse audio stream based on the input stream count */ switch(p_aout->output.output.i_format)
switch(ss.channels)
{ {
case 8: case VLC_CODEC_F64B:
p_aout->output.output.i_physical_channels p_aout->output.output.i_format = VLC_CODEC_F32B;
= AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER case VLC_CODEC_F32B:
| AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT ss.format = PA_SAMPLE_FLOAT32BE;
| AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
| AOUT_CHAN_LFE;
break; break;
case 6: case VLC_CODEC_F64L:
p_aout->output.output.i_physical_channels p_aout->output.output.i_format = VLC_CODEC_F32L;
= AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER case VLC_CODEC_F32L:
| AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT ss.format = PA_SAMPLE_FLOAT32LE;
| AOUT_CHAN_LFE;
break; break;
case VLC_CODEC_FI32:
case 4: p_aout->output.output.i_format = VLC_CODEC_FL32;
p_aout->output.output.i_physical_channels ss.format = PA_SAMPLE_FLOAT32NE;
= AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
| AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
break; break;
case VLC_CODEC_S32B:
case 3: ss.format = PA_SAMPLE_S32BE;
p_aout->output.output.i_physical_channels
= AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER;
break; break;
case VLC_CODEC_S32L:
case 2: ss.format = PA_SAMPLE_S32LE;
p_aout->output.output.i_physical_channels
= AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
break; break;
case VLC_CODEC_S24B:
case 1: ss.format = PA_SAMPLE_S24BE;
p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER; break;
case VLC_CODEC_S24L:
ss.format = PA_SAMPLE_S24LE;
break;
case VLC_CODEC_S16B:
ss.format = PA_SAMPLE_S16BE;
break;
case VLC_CODEC_S16L:
ss.format = PA_SAMPLE_S16LE;
break;
case VLC_CODEC_S8:
p_aout->output.output.i_format = VLC_CODEC_U8;
case VLC_CODEC_U8:
ss.format = PA_SAMPLE_U8;
break; break;
default: default:
msg_Err(p_aout,"Invalid number of channels"); if (HAVE_FPU)
goto fail; {
p_aout->output.output.i_format = VLC_CODEC_FL32;
ss.format = PA_SAMPLE_FLOAT32NE;
}
else
{
p_aout->output.output.i_format = VLC_CODEC_S16N;
ss.format = PA_SAMPLE_S16NE;
}
break;
} }
/* Add a quick command line info message */
msg_Dbg(p_aout, "%d audio channels", ss.channels);
ss.rate = p_aout->output.output.i_rate; ss.rate = p_aout->output.output.i_rate;
if (HAVE_FPU) ss.channels = aout_FormatNbChannels(&p_aout->output.output);
{
ss.format = PA_SAMPLE_FLOAT32NE;
p_aout->output.output.i_format = VLC_CODEC_FL32;
}
else
{
ss.format = PA_SAMPLE_S16NE;
p_aout->output.output.i_format = VLC_CODEC_S16N;
}
if (!pa_sample_spec_valid(&ss)) { if (!pa_sample_spec_valid(&ss)) {
msg_Err(p_aout,"Invalid sample spec"); msg_Err(p_aout, "unsupported sample specification");
goto fail; return VLC_EGENERIC;
} }
/* Reduce overall latency to 100mS to reduce audible clicks /* Channel mapping (order defined in vlc_aout.h) */
* Also pulse minreq and internal buffers are now 100mS which reduces resampling struct pa_channel_map map;
* but still shouldn't drop samples with some usb sound cards map.channels = 0;
*/
a.tlength = pa_bytes_per_second(&ss)/10; if (p_aout->output.output.i_physical_channels & AOUT_CHAN_LEFT)
a.maxlength = a.tlength * 2; map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_LEFT;
a.prebuf = -1; if (p_aout->output.output.i_physical_channels & AOUT_CHAN_RIGHT)
a.minreq = -1; map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_RIGHT;
if (p_aout->output.output.i_physical_channels & AOUT_CHAN_MIDDLELEFT)
/* Initialise the speaker map setup above */ map.map[map.channels++] = PA_CHANNEL_POSITION_SIDE_LEFT;
pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA); if (p_aout->output.output.i_physical_channels & AOUT_CHAN_MIDDLERIGHT)
map.map[map.channels++] = PA_CHANNEL_POSITION_SIDE_RIGHT;
if (!(p_sys->mainloop = pa_threaded_mainloop_new())) { if (p_aout->output.output.i_physical_channels & AOUT_CHAN_REARLEFT)
msg_Err(p_aout, "Failed to allocate main loop"); map.map[map.channels++] = PA_CHANNEL_POSITION_REAR_LEFT;
goto fail; if (p_aout->output.output.i_physical_channels & AOUT_CHAN_REARRIGHT)
map.map[map.channels++] = PA_CHANNEL_POSITION_REAR_RIGHT;
if (p_aout->output.output.i_physical_channels & AOUT_CHAN_REARCENTER)
map.map[map.channels++] = PA_CHANNEL_POSITION_REAR_CENTER;
if (p_aout->output.output.i_physical_channels & AOUT_CHAN_CENTER)
{
if (ss.channels == 1)
map.map[map.channels++] = PA_CHANNEL_POSITION_MONO;
else
map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_CENTER;
} }
if (p_aout->output.output.i_physical_channels & AOUT_CHAN_LFE)
map.map[map.channels++] = PA_CHANNEL_POSITION_LFE;
if ((p_client_name = var_InheritString(p_aout, "user-agent")) == NULL) { for (unsigned i = 0; map.channels < ss.channels; i++) {
msg_Err(p_aout, "No user-agent string available."); map.map[map.channels++] = PA_CHANNEL_POSITION_AUX0 + i;
goto fail; msg_Warn(p_aout, "mapping channel %"PRIu8" to AUX%u", map.channels, i);
} }
p_sys->context = pa_context_new(pa_threaded_mainloop_get_api(p_sys->mainloop), p_client_name); if (!pa_channel_map_valid(&map)) {
free(p_client_name); msg_Err(p_aout, "unsupported channel map");
if(!p_sys->context) return VLC_EGENERIC;
{ } else {
msg_Err(p_aout, "Failed to allocate context"); const char *name = pa_channel_map_to_pretty_name(&map);
goto fail; msg_Dbg(p_aout, "using %s channel map", (name != NULL) ? name : "?");
} }
pa_context_set_state_callback(p_sys->context, context_state_cb, p_aout); const pa_stream_flags_t flags = PA_STREAM_INTERPOLATE_TIMING
| PA_STREAM_AUTO_TIMING_UPDATE
| PA_STREAM_ADJUST_LATENCY;
PULSE_DEBUG( "Pulse before context connect"); struct pa_buffer_attr attr;
/* Reduce overall latency to 100mS to reduce audible clicks
* Also minreq and internal buffers are now 100ms to reduce resampling.
* But it still should not drop samples even with USB sound cards. */
attr.tlength = pa_bytes_per_second(&ss)/10;
attr.maxlength = attr.tlength * 2;
attr.prebuf = -1;
attr.minreq = -1;
attr.fragsize = 0; /* not used for output */
if (pa_context_connect(p_sys->context, NULL, 0, NULL) < 0) { /* Allocate structures */
msg_Err(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context))); struct aout_sys_t *sys = malloc(sizeof(*sys));
goto fail; if (unlikely(sys == NULL))
return VLC_ENOMEM;
sys->stream = NULL;
sys->start_date = VLC_TS_INVALID;
sys->mainloop = pa_threaded_mainloop_new();
if (unlikely(sys->mainloop == NULL)) {
free(sys);
return VLC_ENOMEM;
} }
PULSE_DEBUG( "Pulse after context connect"); char *ua = var_InheritString(p_aout, "user-agent");
pa_threaded_mainloop_lock(p_sys->mainloop); sys->context = pa_context_new(
pa_threaded_mainloop_get_api(sys->mainloop), ua);
free(ua);
if (unlikely(sys->context == NULL)) {
pa_threaded_mainloop_free(sys->mainloop);
free(sys);
return VLC_ENOMEM;
}
if (pa_threaded_mainloop_start(p_sys->mainloop) < 0) { p_aout->output.p_sys = sys;
msg_Err(p_aout, "Failed to start main loop"); pa_context_set_state_callback(sys->context, context_state_cb, sys);
goto unlock_and_fail; if (pa_context_connect(sys->context, NULL, 0, NULL) < 0) {
connect_fail:
msg_Err(p_aout, "cannot connect to server: %s",
pa_strerror(pa_context_errno(sys->context)));
goto fail;
} }
msg_Dbg(p_aout, "Pulse mainloop started"); if (pa_threaded_mainloop_start(sys->mainloop) < 0) {
msg_Err(p_aout, "cannot start main loop");
goto fail;
}
pa_threaded_mainloop_lock(sys->mainloop);
/* Wait until the context is ready */ /* Wait until the context is ready */
pa_threaded_mainloop_wait(p_sys->mainloop); while (pa_context_get_state(sys->context) != PA_CONTEXT_READY) {
if (pa_context_get_state(sys->context) == PA_CONTEXT_FAILED)
if (pa_context_get_state(p_sys->context) != PA_CONTEXT_READY) { goto connect_fail;
msg_Dbg(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context))); pa_threaded_mainloop_wait(sys->mainloop);
goto unlock_and_fail;
} }
if (!(p_sys->stream = pa_stream_new(p_sys->context, "audio stream", &ss, &map))) { /* Create a stream */
msg_Err(p_aout, "Failed to create stream: %s", pa_strerror(pa_context_errno(p_sys->context))); sys->stream = pa_stream_new(sys->context, "audio stream", &ss, &map);
if (sys->stream == NULL) {
msg_Err(p_aout, "cannot create stream: %s",
pa_strerror(pa_context_errno(sys->context)));
goto unlock_and_fail; goto unlock_and_fail;
} }
pa_stream_set_state_callback(sys->stream, stream_state_cb, sys);
PULSE_DEBUG( "Pulse after new stream"); pa_stream_set_write_callback(sys->stream, stream_request_cb, p_aout);
if (pa_stream_connect_playback(sys->stream, NULL, &attr,
pa_stream_set_state_callback(p_sys->stream, stream_state_cb, p_aout); flags, NULL, NULL) < 0) {
pa_stream_set_write_callback(p_sys->stream, stream_request_cb, p_aout); stream_fail:
pa_stream_set_latency_update_callback(p_sys->stream, stream_latency_update_cb, p_aout); msg_Err(p_aout, "cannot connect stream: %s",
pa_strerror(pa_context_errno(sys->context)));
if (pa_stream_connect_playback(p_sys->stream, NULL, &a, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0) {
msg_Err(p_aout, "Failed to connect stream: %s", pa_strerror(pa_context_errno(p_sys->context)));
goto unlock_and_fail; goto unlock_and_fail;
} }
PULSE_DEBUG("Pulse stream connect"); while (pa_stream_get_state(sys->stream) != PA_STREAM_READY) {
if (pa_stream_get_state(sys->stream) == PA_STREAM_FAILED)
/* Wait until the stream is ready */ goto stream_fail;
pa_threaded_mainloop_wait(p_sys->mainloop); pa_threaded_mainloop_wait(sys->mainloop);
msg_Dbg(p_aout,"Pulse stream connected");
if (pa_stream_get_state(p_sys->stream) != PA_STREAM_READY) {
msg_Err(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context)));
goto unlock_and_fail;
} }
msg_Dbg(p_aout, "Connected to device %s (%u, %ssuspended).",
pa_stream_get_device_name(sys->stream),
pa_stream_get_device_index(sys->stream),
pa_stream_is_suspended(sys->stream) ? "" : "not ");
PULSE_DEBUG("Pulse after stream get status"); const struct pa_buffer_attr *pba = pa_stream_get_buffer_attr(sys->stream);
pa_threaded_mainloop_unlock(p_sys->mainloop);
buffer_attr = pa_stream_get_buffer_attr(p_sys->stream);
p_aout->output.i_nb_samples = buffer_attr->minreq / pa_frame_size(&ss);
p_aout->output.i_nb_samples = pba->minreq / pa_frame_size(&ss);
/* Set buffersize from pulseaudio defined minrequest */ /* Set buffersize from pulseaudio defined minrequest */
p_sys->buffer_size = buffer_attr->minreq; sys->buffer_size = pba->minreq;
p_aout->output.pf_play = Play; msg_Dbg(p_aout, "using buffer metrics: maxlength=%u, tlength=%u, "
aout_VolumeSoftInit(p_aout); "prebuf=%u, minreq=%u",
msg_Dbg(p_aout, "Pulse initialized successfully"); pba->maxlength, pba->tlength, pba->prebuf, pba->minreq);
{ {
char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX]; char sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
msg_Dbg(p_aout, "using sample specification: %s",
msg_Dbg(p_aout, "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u", buffer_attr->maxlength, buffer_attr->tlength, buffer_attr->prebuf, buffer_attr->minreq); pa_sample_spec_snprint(sst, sizeof(sst),
msg_Dbg(p_aout, "Using sample spec '%s', channel map '%s'.", pa_stream_get_sample_spec(sys->stream)));
pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(p_sys->stream)), }
pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(p_sys->stream))); {
char cmt[PA_CHANNEL_MAP_SNPRINT_MAX];
msg_Dbg(p_aout, "Connected to device %s (%u, %ssuspended).", msg_Dbg(p_aout, "using channel map: %s",
pa_stream_get_device_name(p_sys->stream), pa_channel_map_snprint(cmt, sizeof(cmt),
pa_stream_get_device_index(p_sys->stream), pa_stream_get_channel_map(sys->stream)));
pa_stream_is_suspended(p_sys->stream) ? "" : "not ");
} }
pa_threaded_mainloop_unlock(sys->mainloop);
p_aout->output.pf_play = Play;
aout_VolumeSoftInit(p_aout);
return VLC_SUCCESS; return VLC_SUCCESS;
unlock_and_fail: unlock_and_fail:
msg_Dbg(p_aout, "Pulse initialization unlock and fail"); pa_threaded_mainloop_unlock(sys->mainloop);
if (p_sys->mainloop)
pa_threaded_mainloop_unlock(p_sys->mainloop);
fail: fail:
msg_Dbg(p_aout, "Pulse initialization failed"); Close(p_this);
uninit(p_aout);
return VLC_EGENERIC; return VLC_EGENERIC;
} }
/***************************************************************************** /*****************************************************************************
* Play: play a sound samples buffer * Play: play a sound samples buffer
*****************************************************************************/ *****************************************************************************/
static void Play( aout_instance_t * p_aout ) static void Play(aout_instance_t * aout)
{ {
struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys; struct aout_sys_t *sys = aout->output.p_sys;
if(!p_sys->started){
msg_Dbg(p_aout, "Pulse stream started");
pa_threaded_mainloop_lock(p_sys->mainloop);
p_sys->start_date =
aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
p_sys->started = 1;
pa_threaded_mainloop_signal(p_sys->mainloop, 0); if (likely(sys->start_date != VLC_TS_INVALID))
pa_threaded_mainloop_unlock(p_sys->mainloop); return;
} pa_threaded_mainloop_lock(sys->mainloop);
sys->start_date = aout_FifoFirstDate(aout, &aout->output.fifo);
pa_threaded_mainloop_unlock(sys->mainloop);
} }
/***************************************************************************** /*****************************************************************************
* Close: close the audio device * Close: close the audio device
*****************************************************************************/ *****************************************************************************/
static void Close ( vlc_object_t *p_this ) static void Close (vlc_object_t *obj)
{ {
aout_instance_t *p_aout = (aout_instance_t *)p_this; aout_instance_t *aout = (aout_instance_t *)obj;
struct aout_sys_t * p_sys = p_aout->output.p_sys; struct aout_sys_t *sys = aout->output.p_sys;
msg_Dbg(p_aout, "Pulse Close");
if(p_sys->stream){
pa_threaded_mainloop_lock(p_sys->mainloop);
pa_stream_set_write_callback(p_sys->stream, NULL, NULL);
if (sys->stream) {
pa_operation *o; pa_operation *o;
o = pa_stream_flush(p_sys->stream, success_cb, p_aout); pa_threaded_mainloop_lock(sys->mainloop);
if (likely(o != NULL)) { pa_stream_set_write_callback(sys->stream, NULL, NULL);
while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
pa_threaded_mainloop_wait(p_sys->mainloop);
pa_operation_unref(o);
}
o = pa_stream_drain(p_sys->stream, success_cb, p_aout); o = pa_stream_flush(sys->stream, success_cb, sys);
if (likely(o != NULL)) { while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) pa_threaded_mainloop_wait(sys->mainloop);
pa_threaded_mainloop_wait(p_sys->mainloop); pa_operation_unref(o);
pa_operation_unref(o);
}
pa_threaded_mainloop_unlock(p_sys->mainloop); o = pa_stream_drain(sys->stream, success_cb, sys);
} while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
uninit(p_aout); pa_threaded_mainloop_wait(sys->mainloop);
} pa_operation_unref(o);
static void uninit(aout_instance_t *p_aout){ pa_threaded_mainloop_unlock(sys->mainloop);
struct aout_sys_t * p_sys = p_aout->output.p_sys;
if (p_sys->mainloop)
pa_threaded_mainloop_stop(p_sys->mainloop);
if (p_sys->stream) {
pa_stream_disconnect(p_sys->stream);
pa_stream_unref(p_sys->stream);
p_sys->stream = NULL;
} }
if (p_sys->context) { pa_threaded_mainloop_stop(sys->mainloop);
pa_context_disconnect(p_sys->context); if (sys->stream) {
pa_context_unref(p_sys->context); pa_stream_disconnect(sys->stream);
p_sys->context = NULL; pa_stream_unref(sys->stream);
} }
pa_context_disconnect(sys->context);
if (p_sys->mainloop) { pa_context_unref(sys->context);
pa_threaded_mainloop_free(p_sys->mainloop); pa_threaded_mainloop_free(sys->mainloop);
p_sys->mainloop = NULL; free(sys);
}
free(p_sys);
p_aout->output.p_sys = NULL;
} }
static void context_state_cb(pa_context *c, void *userdata) { static void context_state_cb(pa_context *c, void *userdata)
aout_instance_t *p_aout = (aout_instance_t *)userdata; {
struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys; struct aout_sys_t *sys = userdata;
assert(c);
PULSE_DEBUG( "Pulse context state changed");
switch (pa_context_get_state(c)) { switch (pa_context_get_state(c)) {
case PA_CONTEXT_READY: case PA_CONTEXT_READY:
case PA_CONTEXT_TERMINATED:
case PA_CONTEXT_FAILED: case PA_CONTEXT_FAILED:
PULSE_DEBUG( "Pulse context state changed signal"); pa_threaded_mainloop_signal(sys->mainloop, 0);
pa_threaded_mainloop_signal(p_sys->mainloop, 0); default:
break;
case PA_CONTEXT_UNCONNECTED:
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
PULSE_DEBUG( "Pulse context state changed no signal");
break; break;
} }
} }
static void stream_state_cb(pa_stream *s, void * userdata) { static void stream_state_cb(pa_stream *s, void *userdata)
aout_instance_t *p_aout = (aout_instance_t *)userdata; {
struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys; struct aout_sys_t *sys = userdata;
assert(s);
PULSE_DEBUG( "Pulse stream state changed");
switch (pa_stream_get_state(s)) { switch (pa_stream_get_state(s)) {
case PA_STREAM_READY: case PA_STREAM_READY:
case PA_STREAM_FAILED: case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED: pa_threaded_mainloop_signal(sys->mainloop, 0);
pa_threaded_mainloop_signal(p_sys->mainloop, 0); default:
break;
case PA_STREAM_UNCONNECTED:
case PA_STREAM_CREATING:
break; break;
} }
} }
static void stream_request_cb(pa_stream *s, size_t length, void *userdata) { static void stream_request_cb(pa_stream *s, size_t length, void *userdata)
VLC_UNUSED( s ); {
aout_instance_t *p_aout = (aout_instance_t *)userdata; aout_instance_t *aout = userdata;
struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys; struct aout_sys_t *sys = aout->output.p_sys;
mtime_t next_date; size_t buffer_size = sys->buffer_size;
assert(s);
assert(p_sys);
size_t buffer_size = p_sys->buffer_size;
PULSE_DEBUG( "Pulse stream request %d", length); do {
aout_buffer_t *p_buffer = NULL;
do{ if (sys->start_date != VLC_TS_INVALID) {
aout_buffer_t * p_buffer = NULL;
if(p_sys->started){
pa_usec_t latency; pa_usec_t latency;
int negative; int negative;
if(pa_stream_get_latency(p_sys->stream, &latency, &negative)<0){
if (pa_context_errno(p_sys->context) != PA_ERR_NODATA) { if (pa_stream_get_latency(s, &latency, &negative) < 0){
msg_Err(p_aout, "pa_stream_get_latency() failed: %s", pa_strerror(pa_context_errno(p_sys->context))); if (pa_context_errno(sys->context) != PA_ERR_NODATA) {
msg_Err(aout, "cannot determine latency: %s",
pa_strerror(pa_context_errno(sys->context)));
} }
latency = 0; latency = 0;
} }
PULSE_DEBUG( "Pulse stream request latency=%"PRId64"", latency); //msg_Dbg(p_aout, "latency=%"PRId64, latency);
next_date = mdate() + latency; mtime_t next_date = mdate() + latency;
if(p_sys->start_date < next_date + AOUT_PTS_TOLERANCE ){ if (sys->start_date < next_date + AOUT_PTS_TOLERANCE )
p_buffer = aout_OutputNextBuffer( p_aout, next_date, 0); p_buffer = aout_OutputNextBuffer(aout, next_date, 0);
}
} }
if ( p_buffer != NULL ) if (p_buffer != NULL)
{ {
PULSE_DEBUG( "Pulse stream request write buffer %d", p_buffer->i_buffer); pa_stream_write(s, p_buffer->p_buffer, p_buffer->i_buffer, NULL, 0, PA_SEEK_RELATIVE);
pa_stream_write(p_sys->stream, p_buffer->p_buffer, p_buffer->i_buffer, NULL, 0, PA_SEEK_RELATIVE);
length -= p_buffer->i_buffer; length -= p_buffer->i_buffer;
aout_BufferFree( p_buffer ); aout_BufferFree( p_buffer );
} }
else else
{ {
PULSE_DEBUG( "Pulse stream request write zeroes");
void *data = pa_xmalloc(length); void *data = pa_xmalloc(length);
memset(data, 0, length); memset(data, 0, length);
pa_stream_write(p_sys->stream, data, length, pa_xfree, 0, PA_SEEK_RELATIVE); pa_stream_write(s, data, length, pa_xfree, 0, PA_SEEK_RELATIVE);
length = 0; length = 0;
} }
}while(length > buffer_size); } while (length > buffer_size);
pa_threaded_mainloop_signal(p_sys->mainloop, 0);
}
static void stream_latency_update_cb(pa_stream *s, void *userdata) {
VLC_UNUSED( s );
aout_instance_t *p_aout = (aout_instance_t *)userdata;
struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
assert(s);
PULSE_DEBUG( "Pulse stream latency update");
pa_threaded_mainloop_signal(p_sys->mainloop, 0);
} }
static void success_cb(pa_stream *s, int sucess, void *userdata) static void success_cb(pa_stream *s, int sucess, void *userdata)
{ {
VLC_UNUSED( s ); struct aout_sys_t *sys = userdata;
aout_instance_t *p_aout = (aout_instance_t *)userdata;
struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
VLC_UNUSED(sucess); (void) s;
(void) sucess;
assert(s); pa_threaded_mainloop_signal(sys->mainloop, 0);
pa_threaded_mainloop_signal(p_sys->mainloop, 0);
} }
#undef PULSE_DEBUG
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment