ao_pulse: allow disabling timing bug workarounds

Add an option that enables using native PulseAudio auto-updated timing
information, instead of the manual calculations added in mplayer2 times.
You can use --ao=pulse:no-latency-hacks to enable the new code. The code
is almost the same as the code that was removed with commit de435ed5,
but I didn't readd some bits I didn't understand. Likewise, the option
will disable the code added with that commit.

In my tests this seemed to work well, though the A/V sync display looks
funny when seeking.

The default is still the old behavior.

See issue #959.
This commit is contained in:
wm4 2014-07-26 23:19:14 +02:00
parent 77d9e4b8a9
commit bc6359313f
2 changed files with 44 additions and 3 deletions

View File

@ -149,6 +149,12 @@ Available audio output drivers are:
value makes the audio stream react faster, e.g. to playback speed
changes. Default: 250.
``latency-hacks=<yes|no>``
Enable hacks to workaround PulseAudio timing bugs (default: yes). If
enabled, mpv will do elaborate latency calculations on its own. If
disabled, it will use PulseAudio automatically updated timing
information. Disabling this might help with e.g. networked audio.
``portaudio``
PortAudio audio output driver. This works on all platforms, and has
extensive MS Windows support.

View File

@ -63,6 +63,7 @@ struct priv {
char *cfg_host;
char *cfg_sink;
int cfg_buffer;
int cfg_latency_hacks;
};
#define GENERIC_ERR_MSG(str) \
@ -365,8 +366,13 @@ static int init(struct ao *ao)
.minreq = -1,
.fragsize = -1,
};
int flags = PA_STREAM_NOT_MONOTONIC;
if (!priv->cfg_latency_hacks)
flags |= PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE;
if (pa_stream_connect_playback(priv->stream, sink, &bufattr,
PA_STREAM_NOT_MONOTONIC, NULL, NULL) < 0)
flags, NULL, NULL) < 0)
goto unlock_and_fail;
/* Wait until the stream is ready */
@ -463,8 +469,7 @@ static int get_space(struct ao *ao)
return space / ao->sstride;
}
// Return the current latency in seconds
static float get_delay(struct ao *ao)
static float get_delay_hackfixed(struct ao *ao)
{
/* This code basically does what pa_stream_get_latency() _should_
* do, but doesn't due to multiple known bugs in PulseAudio (at
@ -519,6 +524,34 @@ static float get_delay(struct ao *ao)
return latency / 1e6;
}
static float get_delay_pulse(struct ao *ao)
{
struct priv *priv = ao->priv;
pa_usec_t latency = (pa_usec_t) -1;
pa_threaded_mainloop_lock(priv->mainloop);
while (pa_stream_get_latency(priv->stream, &latency, NULL) < 0) {
if (pa_context_errno(priv->context) != PA_ERR_NODATA) {
GENERIC_ERR_MSG("pa_stream_get_latency() failed");
break;
}
/* Wait until latency data is available again */
pa_threaded_mainloop_wait(priv->mainloop);
}
pa_threaded_mainloop_unlock(priv->mainloop);
return latency == (pa_usec_t) -1 ? 0 : latency / 1000000.0;
}
// Return the current latency in seconds
static float get_delay(struct ao *ao)
{
struct priv *priv = ao->priv;
if (priv->cfg_latency_hacks) {
return get_delay_hackfixed(ao);
} else {
return get_delay_pulse(ao);
}
}
/* A callback function that is called when the
* pa_context_get_sink_input_info() operation completes. Saves the
* volume field of the specified structure to the global variable volume.
@ -648,11 +681,13 @@ const struct ao_driver audio_out_pulse = {
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {
.cfg_buffer = 250,
.cfg_latency_hacks = 1,
},
.options = (const struct m_option[]) {
OPT_STRING("host", cfg_host, 0),
OPT_STRING("sink", cfg_sink, 0),
OPT_CHOICE_OR_INT("buffer", cfg_buffer, 0, 1, 2000, ({"native", -1})),
OPT_FLAG("latency-hacks", cfg_latency_hacks, 0),
{0}
},
};