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

PulseAudio output: rewrite

This should fix the deadlocks and memory starvation problems.
(cherry picked from commit ed864e7f920740fe387a06599e50f5c8509c8527)
(cherry picked from commit a759fe73a5b49a241fb733e073e6702abf495ee7)
(cherry picked from commit e5e27586960e28bc6fdcd48b18a663045e9bb58e)
(cherry picked from commit 0462f44e2a133be0b07f365570b91c5f3cd4c38e)
(cherry picked from commit 7a86d4e443908c67470fc8337d37e1b1d68fbbff)

Conflicts:

	modules/audio_output/pulse.c
parent e1fd025f
......@@ -21,16 +21,12 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_aout.h>
#include <vlc_cpu.h>
......@@ -43,36 +39,9 @@
#include <vlc_xlib.h>
#endif
/*****************************************************************************
* aout_sys_t: Pulseaudio output method descriptor
*****************************************************************************
* This structure is part of the audio output thread descriptor.
* It describes the specific properties of an audio device.
*****************************************************************************/
struct aout_sys_t
{
struct pa_stream *stream; /**< PulseAudio playback stream object */
struct pa_context *context; /**< PulseAudio connection context */
struct pa_threaded_mainloop *mainloop; /**< PulseAudio event loop */
size_t buffer_size;
mtime_t start_date;
};
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * );
static void Play ( aout_instance_t * );
static void context_state_cb(pa_context *c, 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 success_cb(pa_stream *s, int sucess, void *userdata);
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin ()
set_shortname( "PulseAudio" )
set_description( N_("Pulseaudio audio output") )
......@@ -84,12 +53,191 @@ vlc_module_begin ()
set_callbacks( Open, Close )
vlc_module_end ()
struct aout_sys_t
{
pa_stream *stream; /**< PulseAudio playback stream object */
pa_context *context; /**< PulseAudio connection context */
pa_threaded_mainloop *mainloop; /**< PulseAudio event loop */
//uint32_t byterate; /**< bytes per second */
};
/* Context helpers */
static void context_state_cb(pa_context *c, void *userdata)
{
pa_threaded_mainloop *mainloop = userdata;
switch (pa_context_get_state(c)) {
case PA_CONTEXT_READY:
case PA_CONTEXT_FAILED:
case PA_CONTEXT_TERMINATED:
pa_threaded_mainloop_signal(mainloop, 0);
default:
break;
}
}
static bool context_wait(pa_threaded_mainloop *mainloop, pa_context *context)
{
pa_context_state_t state;
while ((state = pa_context_get_state(context)) != PA_CONTEXT_READY) {
if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
return -1;
pa_threaded_mainloop_wait(mainloop);
}
return 0;
}
static void error(aout_instance_t *aout, const char *msg, pa_context *context)
{
msg_Err(aout, "%s: %s", msg, pa_strerror(pa_context_errno(context)));
}
/* Stream helpers */
static void stream_state_cb(pa_stream *s, void *userdata)
{
pa_threaded_mainloop *mainloop = userdata;
switch (pa_stream_get_state(s)) {
case PA_STREAM_READY:
case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED:
pa_threaded_mainloop_signal(mainloop, 0);
default:
break;
}
}
static void stream_moved_cb(pa_stream *s, void *userdata)
{
vlc_object_t *obj = userdata;
msg_Dbg(obj, "connected to device %s (%u)",
pa_stream_get_device_name(s),
pa_stream_get_device_index(s));
}
static int stream_wait(pa_threaded_mainloop *mainloop, pa_stream *stream)
{
pa_stream_state_t state;
while ((state = pa_stream_get_state(stream)) != PA_STREAM_READY) {
if (state == PA_STREAM_FAILED || state == PA_STREAM_TERMINATED)
return -1;
pa_threaded_mainloop_wait(mainloop);
}
return 0;
}
/* Memory free callback. The block_t address is in front of the data. */
static void data_free(void *data)
{
block_t **pp = data, *block;
memcpy(&block, pp - 1, sizeof (block));
block_Release(block);
}
static void *data_convert(block_t **pp)
{
block_t *block = *pp;
/* In most cases, there is enough head room, and this is really cheap: */
block = block_Realloc(block, sizeof (block), block->i_buffer);
*pp = block;
if (unlikely(block == NULL))
return NULL;
memcpy(block->p_buffer, &block, sizeof (block));
block->p_buffer += sizeof (block);
block->i_buffer -= sizeof (block);
return block->p_buffer;
}
/*****************************************************************************
* Play: play a sound samples buffer
*****************************************************************************/
static void Play(aout_instance_t *aout)
{
aout_sys_t *sys = aout->output.p_sys;
pa_stream *s = sys->stream;
/* Note: The core already holds the output FIFO lock at this point.
* Therefore we must not under any circumstances (try to) acquire the
* output FIFO lock while the PulseAudio threaded main loop lock is held
* (including from PulseAudio stream callbacks). Otherwise lock inversion
* will take place, and sooner or later a deadlock. */
pa_threaded_mainloop_lock(sys->mainloop);
if (pa_stream_is_corked(sys->stream) > 0) {
pa_operation *op = pa_stream_cork(s, 0, NULL, NULL);
if (op != NULL)
pa_operation_unref(op);
msg_Dbg(aout, "uncorking");
}
#if 0
/* This function should be called by the LibVLC core a header of time,
* but not more than AOUT_MAX_PREPARE. The PulseAudio latency should be
* shorter than that (though it might not be the case with some evil piece
* of audio output hardware). So we need to prepend the buffer with zeroes
* to keep audio and video in sync. */
pa_usec_t latency;
int negative;
if (pa_stream_get_latency(s, &latency, &negative) < 0) {
/* Especially at start of stream, latency may not be known (yet). */
if (pa_context_errno(sys->context) != PA_ERR_NODATA)
error(aout, "cannot determine latency", sys->context);
latency = 0;
}
mtime_t gap = aout_FifoFirstDate(aout, &aout->output.fifo) - mdate()
- latency;
if (gap > AOUT_PTS_TOLERANCE)
msg_Dbg(aout, "buffer too early (%"PRId64" us)", gap);
else if (latency != 0 && gap < -AOUT_PTS_TOLERANCE)
msg_Err(aout, "buffer too late (%"PRId64" us)", -gap);
#endif
#if 0 /* Fault injector to test underrun recovery */
static unsigned u = 0;
if ((++u % 500) == 0) {
msg_Err(aout, "fault injection");
msleep(CLOCK_FREQ*2);
}
#endif
/* This function is called exactly once per block in the output FIFO, so
* this for-loop is not necessary.
* If this function is changed to not always dequeue blocks, be sure to
* limit the queue size to a reasonable limit to avoid huge leaks. */
for (;;) {
block_t *block = aout_FifoPop(aout, &aout->output.fifo);
if (block == NULL)
break;
const void *ptr = data_convert(&block);
if (unlikely(ptr == NULL))
break;
size_t len = block->i_buffer;
//mtime_t pts = block->i_pts, duration = block->i_length;
if (pa_stream_write(s, ptr, len, data_free, 0, PA_SEEK_RELATIVE) < 0)
{
error(aout, "cannot write", sys->context);
block_Release(block);
}
}
pa_threaded_mainloop_unlock(sys->mainloop);
}
/*****************************************************************************
* Open: open the audio device
*****************************************************************************/
static int Open ( vlc_object_t *p_this )
static int Open(vlc_object_t *obj)
{
aout_instance_t *p_aout = (aout_instance_t *)p_this;
aout_instance_t *aout = (aout_instance_t *)obj;
#if !defined(PA_CHECK_VERSION) || !PA_CHECK_VERSION(0,9,22)
if( !vlc_xlib_init( p_this ) )
......@@ -99,20 +247,20 @@ static int Open ( vlc_object_t *p_this )
/* Sample format specification */
struct pa_sample_spec ss;
switch(p_aout->output.output.i_format)
switch(aout->output.output.i_format)
{
case VLC_CODEC_F64B:
p_aout->output.output.i_format = VLC_CODEC_F32B;
aout->output.output.i_format = VLC_CODEC_F32B;
case VLC_CODEC_F32B:
ss.format = PA_SAMPLE_FLOAT32BE;
break;
case VLC_CODEC_F64L:
p_aout->output.output.i_format = VLC_CODEC_F32L;
aout->output.output.i_format = VLC_CODEC_F32L;
case VLC_CODEC_F32L:
ss.format = PA_SAMPLE_FLOAT32LE;
break;
case VLC_CODEC_FI32:
p_aout->output.output.i_format = VLC_CODEC_FL32;
aout->output.output.i_format = VLC_CODEC_FL32;
ss.format = PA_SAMPLE_FLOAT32NE;
break;
case VLC_CODEC_S32B:
......@@ -134,28 +282,28 @@ static int Open ( vlc_object_t *p_this )
ss.format = PA_SAMPLE_S16LE;
break;
case VLC_CODEC_S8:
p_aout->output.output.i_format = VLC_CODEC_U8;
aout->output.output.i_format = VLC_CODEC_U8;
case VLC_CODEC_U8:
ss.format = PA_SAMPLE_U8;
break;
default:
if (HAVE_FPU)
{
p_aout->output.output.i_format = VLC_CODEC_FL32;
aout->output.output.i_format = VLC_CODEC_FL32;
ss.format = PA_SAMPLE_FLOAT32NE;
}
else
{
p_aout->output.output.i_format = VLC_CODEC_S16N;
aout->output.output.i_format = VLC_CODEC_S16N;
ss.format = PA_SAMPLE_S16NE;
}
break;
}
ss.rate = p_aout->output.output.i_rate;
ss.channels = aout_FormatNbChannels(&p_aout->output.output);
ss.rate = aout->output.output.i_rate;
ss.channels = aout_FormatNbChannels(&aout->output.output);
if (!pa_sample_spec_valid(&ss)) {
msg_Err(p_aout, "unsupported sample specification");
msg_Err(aout, "unsupported sample specification");
return VLC_EGENERIC;
}
......@@ -163,292 +311,159 @@ static int Open ( vlc_object_t *p_this )
struct pa_channel_map map;
map.channels = 0;
if (p_aout->output.output.i_physical_channels & AOUT_CHAN_LEFT)
if (aout->output.output.i_physical_channels & AOUT_CHAN_LEFT)
map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_LEFT;
if (p_aout->output.output.i_physical_channels & AOUT_CHAN_RIGHT)
if (aout->output.output.i_physical_channels & AOUT_CHAN_RIGHT)
map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_RIGHT;
if (p_aout->output.output.i_physical_channels & AOUT_CHAN_MIDDLELEFT)
if (aout->output.output.i_physical_channels & AOUT_CHAN_MIDDLELEFT)
map.map[map.channels++] = PA_CHANNEL_POSITION_SIDE_LEFT;
if (p_aout->output.output.i_physical_channels & AOUT_CHAN_MIDDLERIGHT)
if (aout->output.output.i_physical_channels & AOUT_CHAN_MIDDLERIGHT)
map.map[map.channels++] = PA_CHANNEL_POSITION_SIDE_RIGHT;
if (p_aout->output.output.i_physical_channels & AOUT_CHAN_REARLEFT)
if (aout->output.output.i_physical_channels & AOUT_CHAN_REARLEFT)
map.map[map.channels++] = PA_CHANNEL_POSITION_REAR_LEFT;
if (p_aout->output.output.i_physical_channels & AOUT_CHAN_REARRIGHT)
if (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)
if (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 (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)
if (aout->output.output.i_physical_channels & AOUT_CHAN_LFE)
map.map[map.channels++] = PA_CHANNEL_POSITION_LFE;
for (unsigned i = 0; map.channels < ss.channels; i++) {
map.map[map.channels++] = PA_CHANNEL_POSITION_AUX0 + i;
msg_Warn(p_aout, "mapping channel %"PRIu8" to AUX%u", map.channels, i);
msg_Warn(aout, "mapping channel %"PRIu8" to AUX%u", map.channels, i);
}
if (!pa_channel_map_valid(&map)) {
msg_Err(p_aout, "unsupported channel map");
msg_Err(aout, "unsupported channel map");
return VLC_EGENERIC;
} else {
const char *name = pa_channel_map_to_pretty_name(&map);
msg_Dbg(p_aout, "using %s channel map", (name != NULL) ? name : "?");
msg_Dbg(aout, "using %s channel map", (name != NULL) ? name : "?");
}
const pa_stream_flags_t flags = PA_STREAM_INTERPOLATE_TIMING
| PA_STREAM_AUTO_TIMING_UPDATE
| PA_STREAM_ADJUST_LATENCY;
| PA_STREAM_ADJUST_LATENCY
| PA_STREAM_START_CORKED;
const uint32_t byterate = pa_bytes_per_second(&ss);
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;
/* no point in larger buffers on PA side than VLC */
attr.maxlength = byterate * AOUT_MAX_ADVANCE_TIME / CLOCK_FREQ;
attr.tlength = byterate * AOUT_MAX_PREPARE_TIME / CLOCK_FREQ;
attr.prebuf = byterate * AOUT_MIN_PREPARE_TIME / CLOCK_FREQ;
attr.minreq = -1;
attr.fragsize = 0; /* not used for output */
/* Allocate structures */
struct aout_sys_t *sys = malloc(sizeof(*sys));
aout_sys_t *sys = malloc(sizeof(*sys));
if (unlikely(sys == NULL))
return VLC_ENOMEM;
aout->output.p_sys = sys;
sys->context = NULL;
sys->stream = NULL;
sys->start_date = VLC_TS_INVALID;
//sys->byterate = byterate;
sys->mainloop = pa_threaded_mainloop_new();
if (unlikely(sys->mainloop == NULL)) {
/* Allocate threaded main loop */
pa_threaded_mainloop *mainloop = pa_threaded_mainloop_new();
if (unlikely(mainloop == NULL)) {
free(sys);
return VLC_ENOMEM;
}
sys->mainloop = mainloop;
char *ua = var_InheritString(p_aout, "user-agent");
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);
if (pa_threaded_mainloop_start(mainloop) < 0) {
pa_threaded_mainloop_free(mainloop);
free(sys);
return VLC_ENOMEM;
}
p_aout->output.p_sys = sys;
pa_context_set_state_callback(sys->context, context_state_cb, sys);
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)));
pa_threaded_mainloop_lock(mainloop);
/* Connect to PulseAudio server */
char *user_agent = var_InheritString(aout, "user-agent");
pa_context *ctx = pa_context_new(pa_threaded_mainloop_get_api(mainloop),
user_agent);
free(user_agent);
if (unlikely(ctx == NULL))
goto fail;
}
sys->context = ctx;
if (pa_threaded_mainloop_start(sys->mainloop) < 0) {
msg_Err(p_aout, "cannot start main loop");
pa_context_set_state_callback(ctx, context_state_cb, mainloop);
if (pa_context_connect(ctx, NULL, 0, NULL) < 0
|| context_wait(mainloop, ctx)) {
error(aout, "cannot connect to server", ctx);
goto fail;
}
pa_threaded_mainloop_lock(sys->mainloop);
/* Wait until the context is ready */
while (pa_context_get_state(sys->context) != PA_CONTEXT_READY) {
if (pa_context_get_state(sys->context) == PA_CONTEXT_FAILED)
goto connect_fail;
pa_threaded_mainloop_wait(sys->mainloop);
}
/* Create a stream */
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;
}
pa_stream_set_state_callback(sys->stream, stream_state_cb, sys);
pa_stream_set_write_callback(sys->stream, stream_request_cb, p_aout);
if (pa_stream_connect_playback(sys->stream, NULL, &attr,
flags, NULL, NULL) < 0) {
stream_fail:
msg_Err(p_aout, "cannot connect stream: %s",
pa_strerror(pa_context_errno(sys->context)));
goto unlock_and_fail;
/* Create a playback stream */
pa_stream *s = pa_stream_new(ctx, "audio stream", &ss, &map);
if (s == NULL) {
error(aout, "cannot create stream", ctx);
goto fail;
}
sys->stream = s;
pa_stream_set_state_callback(s, stream_state_cb, mainloop);
pa_stream_set_moved_callback(s, stream_moved_cb, aout);
while (pa_stream_get_state(sys->stream) != PA_STREAM_READY) {
if (pa_stream_get_state(sys->stream) == PA_STREAM_FAILED)
goto stream_fail;
pa_threaded_mainloop_wait(sys->mainloop);
if (pa_stream_connect_playback(s, NULL, &attr, flags, NULL, NULL) < 0
|| stream_wait(mainloop, s)) {
error(aout, "cannot connect stream", ctx);
goto fail;
}
stream_moved_cb(s, aout);
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 ");
const struct pa_buffer_attr *pba = pa_stream_get_buffer_attr(sys->stream);
p_aout->output.i_nb_samples = pba->minreq / pa_frame_size(&ss);
/* Set buffersize from pulseaudio defined minrequest */
sys->buffer_size = pba->minreq;
msg_Dbg(p_aout, "using buffer metrics: maxlength=%u, tlength=%u, "
const struct pa_buffer_attr *pba = pa_stream_get_buffer_attr(s);
msg_Dbg(aout, "using buffer metrics: maxlength=%u, tlength=%u, "
"prebuf=%u, minreq=%u",
pba->maxlength, pba->tlength, pba->prebuf, pba->minreq);
{
char sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
msg_Dbg(p_aout, "using sample specification: %s",
pa_sample_spec_snprint(sst, sizeof(sst),
pa_stream_get_sample_spec(sys->stream)));
}
{
char cmt[PA_CHANNEL_MAP_SNPRINT_MAX];
msg_Dbg(p_aout, "using channel map: %s",
pa_channel_map_snprint(cmt, sizeof(cmt),
pa_stream_get_channel_map(sys->stream)));
}
pa_threaded_mainloop_unlock(sys->mainloop);
p_aout->output.pf_play = Play;
aout_VolumeSoftInit(p_aout);
aout->output.i_nb_samples = pba->minreq / pa_frame_size(&ss);
pa_threaded_mainloop_unlock(mainloop);
aout->output.pf_play = Play;
aout_VolumeSoftInit(aout);
return VLC_SUCCESS;
unlock_and_fail:
pa_threaded_mainloop_unlock(sys->mainloop);
fail:
Close(p_this);
pa_threaded_mainloop_unlock(mainloop);
Close(obj);
return VLC_EGENERIC;
}
/*****************************************************************************
* Play: play a sound samples buffer
*****************************************************************************/
static void Play(aout_instance_t * aout)
{
struct aout_sys_t *sys = aout->output.p_sys;
if (likely(sys->start_date != VLC_TS_INVALID))
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
*****************************************************************************/
static void Close (vlc_object_t *obj)
{
aout_instance_t *aout = (aout_instance_t *)obj;
struct aout_sys_t *sys = aout->output.p_sys;
if (sys->stream) {
pa_operation *o;
pa_threaded_mainloop_lock(sys->mainloop);
pa_stream_set_write_callback(sys->stream, NULL, NULL);
o = pa_stream_flush(sys->stream, success_cb, sys);
while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
pa_threaded_mainloop_wait(sys->mainloop);
pa_operation_unref(o);
o = pa_stream_drain(sys->stream, success_cb, sys);
while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
pa_threaded_mainloop_wait(sys->mainloop);
pa_operation_unref(o);
pa_threaded_mainloop_unlock(sys->mainloop);
aout_sys_t *sys = aout->output.p_sys;
pa_threaded_mainloop *mainloop = sys->mainloop;
pa_context *ctx = sys->context;
pa_stream *s = sys->stream;
pa_threaded_mainloop_lock(mainloop);
if (s != NULL) {
pa_operation *op;
op = pa_stream_flush(s, NULL, NULL);
if (op != NULL)
pa_operation_unref(op);
op = pa_stream_drain(s, NULL, NULL);
if (op != NULL)
pa_operation_unref(op);
pa_stream_disconnect(s);
pa_stream_unref(s);
}
pa_threaded_mainloop_stop(sys->mainloop);
if (sys->stream) {
pa_stream_disconnect(sys->stream);
pa_stream_unref(sys->stream);
}
pa_context_disconnect(sys->context);
pa_context_unref(sys->context);
pa_threaded_mainloop_free(sys->mainloop);
if (ctx != NULL)
pa_context_unref(ctx);
pa_threaded_mainloop_unlock(mainloop);
pa_threaded_mainloop_free(mainloop);
free(sys);
}
static void context_state_cb(pa_context *c, void *userdata)
{
struct aout_sys_t *sys = userdata;
switch (pa_context_get_state(c)) {
case PA_CONTEXT_READY:
case PA_CONTEXT_FAILED:
pa_threaded_mainloop_signal(sys->mainloop, 0);
default:
break;
}
}
static void stream_state_cb(pa_stream *s, void *userdata)
{
struct aout_sys_t *sys = userdata;
switch (pa_stream_get_state(s)) {
case PA_STREAM_READY:
case PA_STREAM_FAILED:
pa_threaded_mainloop_signal(sys->mainloop, 0);
default:
break;
}
}
static void stream_request_cb(pa_stream *s, size_t length, void *userdata)
{
aout_instance_t *aout = userdata;
struct aout_sys_t *sys = aout->output.p_sys;
size_t buffer_size = sys->buffer_size;
do {
aout_buffer_t *p_buffer = NULL;
if (sys->start_date != VLC_TS_INVALID) {
pa_usec_t latency;
int negative;
if (pa_stream_get_latency(s, &latency, &negative) < 0){
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;
}
//msg_Dbg(p_aout, "latency=%"PRId64, latency);
mtime_t next_date = mdate() + latency;
if (sys->start_date < next_date + AOUT_PTS_TOLERANCE )
p_buffer = aout_OutputNextBuffer(aout, next_date, 0);
}
if (p_buffer != NULL)
{
pa_stream_write(s, p_buffer->p_buffer, p_buffer->i_buffer, NULL, 0, PA_SEEK_RELATIVE);
length -= p_buffer->i_buffer;
aout_BufferFree( p_buffer );
}
else
{
void *data = pa_xmalloc(length);
memset(data, 0, length);
pa_stream_write(s, data, length, pa_xfree, 0, PA_SEEK_RELATIVE);
length = 0;
}
} while (length > buffer_size);
}
static void success_cb(pa_stream *s, int sucess, void *userdata)
{
struct aout_sys_t *sys = userdata;
(void) s;
(void) sucess;
pa_threaded_mainloop_signal(sys->mainloop, 0);
}
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