1
mirror of https://github.com/mpv-player/mpv synced 2024-07-31 16:29:58 +02:00

ao_pulse, core: make pulse thread wake up core for more data

For ao_pulse, the current latency is not a good indicator of how soon
the AO requires new data to avoid underflow. Add an internal pipe that
can be used to wake up the input loop from select(), and make the
pulseaudio main loop (which runs in a separate thread) use this
mechanism when pulse requests more data. The wakeup signal currently
contains no information about the reason for the wakup, but audio
buffers are always filled when the event loop wakes up.

Also, request a latency of 1 second from the Pulseaudio server. The
default is normally significantly higher. We don't need low latency,
while higher latency helps prevent underflows reduces need for
wakeups.
This commit is contained in:
Uoti Urpala 2012-03-25 22:58:48 +03:00
parent 4fed8ad197
commit a0de4bc500
6 changed files with 57 additions and 13 deletions

View File

@ -622,6 +622,8 @@ struct input_ctx {
struct cmd_queue key_cmd_queue;
struct cmd_queue control_cmd_queue;
int wakeup_pipe[2];
};
@ -1104,6 +1106,13 @@ static int default_cmd_func(int fd, char *buf, int l)
}
}
static int read_wakeup(void *ctx, int fd)
{
char buf[100];
read(fd, buf, sizeof(buf));
return MP_INPUT_NOTHING;
}
static char *find_bind_for_key(const struct cmd_bind *binds, int n, int *keys)
{
@ -1126,7 +1135,7 @@ static char *find_bind_for_key(const struct cmd_bind *binds, int n, int *keys)
}
static struct cmd_bind_section *get_bind_section(struct input_ctx *ictx,
char *section)
char *section)
{
struct cmd_bind_section *bind_section = ictx->cmd_bind_sections;
@ -1731,8 +1740,25 @@ struct input_ctx *mp_input_init(struct input_conf *input_conf)
.ar_delay = input_conf->ar_delay,
.ar_rate = input_conf->ar_rate,
.default_bindings = input_conf->default_bindings,
.wakeup_pipe = {-1, -1},
};
#ifndef __MINGW32__
long ret = pipe(ictx->wakeup_pipe);
for (int i = 0; i < 2 && ret >= 0; i++) {
ret = fcntl(ictx->wakeup_pipe[i], F_GETFL);
if (ret < 0)
break;
ret = fcntl(ictx->wakeup_pipe[i], F_SETFL, ret | O_NONBLOCK);
}
if (ret < 0)
mp_msg(MSGT_INPUT, MSGL_ERR,
"Failed to initialize wakeup pipe: %s\n", strerror(errno));
else
mp_input_add_key_fd(ictx, ictx->wakeup_pipe[0], true, read_wakeup,
NULL, NULL);
#endif
char *file;
char *config_file = input_conf->config_file;
file = config_file[0] != '/' ? get_path(config_file) : config_file;
@ -1830,17 +1856,17 @@ void mp_input_uninit(struct input_ctx *ictx)
if (!ictx)
return;
unsigned int i;
for (i = 0; i < ictx->num_key_fd; i++) {
for (int i = 0; i < ictx->num_key_fd; i++) {
if (ictx->key_fds[i].close_func)
ictx->key_fds[i].close_func(ictx->key_fds[i].fd);
}
for (i = 0; i < ictx->num_cmd_fd; i++) {
for (int i = 0; i < ictx->num_cmd_fd; i++) {
if (ictx->cmd_fds[i].close_func)
ictx->cmd_fds[i].close_func(ictx->cmd_fds[i].fd);
}
for (int i = 0; i < 2; i++)
if (ictx->wakeup_pipe[i] != -1)
close(ictx->wakeup_pipe[i]);
talloc_free(ictx);
}
@ -1890,6 +1916,12 @@ static int print_cmd_list(m_option_t *cfg)
exit(0);
}
void mp_input_wakeup(struct input_ctx *ictx)
{
if (ictx->wakeup_pipe[1] >= 0)
write(ictx->wakeup_pipe[1], &(char){0}, 1);
}
/**
* \param time time to wait for an interruption in milliseconds
*/

View File

@ -272,6 +272,9 @@ void mp_input_uninit(struct input_ctx *ictx);
struct m_config;
void mp_input_register_options(struct m_config *cfg);
// Wake up sleeping input loop from another thread.
void mp_input_wakeup(struct input_ctx *ictx);
// Interruptible usleep: (used by libmpdemux)
int mp_input_check_interrupt(struct input_ctx *ictx, int time);

View File

@ -30,6 +30,7 @@
#include "libaf/af_format.h"
#include "mp_msg.h"
#include "audio_out.h"
#include "input/input.h"
#define PULSE_CLIENT_NAME "mplayer2"
@ -84,6 +85,7 @@ static void stream_request_cb(pa_stream *s, size_t length, void *userdata)
{
struct ao *ao = userdata;
struct priv *priv = ao->priv;
mp_input_wakeup(ao->input_ctx);
pa_threaded_mainloop_signal(priv->mainloop, 0);
}
@ -263,8 +265,14 @@ static int init(struct ao *ao, char *params)
pa_stream_set_write_callback(priv->stream, stream_request_cb, ao);
pa_stream_set_latency_update_callback(priv->stream,
stream_latency_update_cb, ao);
if (pa_stream_connect_playback(priv->stream, sink, NULL,
pa_buffer_attr bufattr = {
.maxlength = -1,
.tlength = pa_usec_to_bytes(1000000, &ss),
.prebuf = -1,
.minreq = -1,
.fragsize = -1,
};
if (pa_stream_connect_playback(priv->stream, sink, &bufattr,
PA_STREAM_INTERPOLATE_TIMING
| PA_STREAM_AUTO_TIMING_UPDATE, NULL,
NULL) < 0)

View File

@ -136,10 +136,11 @@ void list_audio_out(void)
mp_msg(MSGT_GLOBAL, MSGL_INFO,"\n");
}
struct ao *ao_create(void)
struct ao *ao_create(struct MPOpts *opts, struct input_ctx *input)
{
struct ao *r = talloc(NULL, struct ao);
*r = (struct ao){.outburst = OUTBURST, .buffersize = -1};
*r = (struct ao){.outburst = OUTBURST, .buffersize = -1,
.opts = opts, .input_ctx = input };
return r;
}

View File

@ -81,6 +81,7 @@ struct ao {
const struct ao_driver *driver;
void *priv;
struct MPOpts *opts;
struct input_ctx *input_ctx;
};
extern char *ao_subdevice;
@ -109,7 +110,7 @@ typedef struct ao_control_vol {
float right;
} ao_control_vol_t;
struct ao *ao_create(void);
struct ao *ao_create(struct MPOpts *opts, struct input_ctx *input);
void ao_init(struct ao *ao, char **ao_list);
void ao_uninit(struct ao *ao, bool cut_audio);
int ao_play(struct ao *ao, void *data, int len, int flags);

View File

@ -1775,8 +1775,7 @@ void reinit_audio_chain(struct MPContext *mpctx)
current_module = "af_preinit";
if (!(mpctx->initialized_flags & INITIALIZED_AO)) {
mpctx->initialized_flags |= INITIALIZED_AO;
mpctx->ao = ao_create();
mpctx->ao->opts = opts;
mpctx->ao = ao_create(opts, mpctx->input);
mpctx->ao->samplerate = force_srate;
mpctx->ao->format = opts->audio_output_format;
}