summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/ao.rst2
-rw-r--r--audio/out/ao.c12
-rw-r--r--audio/out/ao.h2
-rw-r--r--audio/out/ao_alsa.c369
-rw-r--r--audio/out/ao_audiotrack.c2
-rw-r--r--audio/out/ao_audiounit.m2
-rw-r--r--audio/out/ao_coreaudio.c2
-rw-r--r--audio/out/ao_coreaudio_exclusive.c2
-rw-r--r--audio/out/ao_jack.c4
-rw-r--r--audio/out/ao_lavc.c57
-rw-r--r--audio/out/ao_null.c89
-rw-r--r--audio/out/ao_openal.c90
-rw-r--r--audio/out/ao_opensles.c2
-rw-r--r--audio/out/ao_pcm.c40
-rw-r--r--audio/out/ao_pulse.c160
-rw-r--r--audio/out/ao_sdl.c4
-rw-r--r--audio/out/ao_wasapi.c2
-rw-r--r--audio/out/buffer.c497
-rw-r--r--audio/out/internal.h118
-rw-r--r--player/audio.c2
20 files changed, 635 insertions, 823 deletions
diff --git a/DOCS/man/ao.rst b/DOCS/man/ao.rst
index 5308a7175b..0b1af44e2b 100644
--- a/DOCS/man/ao.rst
+++ b/DOCS/man/ao.rst
@@ -97,7 +97,7 @@ Available audio output drivers are:
exclusive mode (bypasses the sound server).
``openal``
- OpenAL audio output driver
+ OpenAL audio output driver. This is broken and does not work.
``--openal-num-buffers=<2-128>``
Specify the number of audio buffers to use. Lower values are better for
diff --git a/audio/out/ao.c b/audio/out/ao.c
index 7b301cd2e7..9ac42806e4 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -236,8 +236,10 @@ static struct ao *ao_init(bool probing, struct mpv_global *global,
}
ao->bps = ao->samplerate * ao->sstride;
- if (!ao->device_buffer && ao->driver->get_space)
- ao->device_buffer = ao->driver->get_space(ao);
+ if (ao->device_buffer < 0 && ao->driver->write) {
+ MP_ERR(ao, "Device buffer size not set.\n");
+ goto fail;
+ }
if (ao->device_buffer)
MP_VERBOSE(ao, "device buffer: %d samples.\n", ao->device_buffer);
ao->buffer = MPMAX(ao->device_buffer, ao->def_buffer * ao->samplerate);
@@ -374,12 +376,6 @@ void ao_hotplug_event(struct ao *ao)
ao_add_events(ao, AO_EVENT_HOTPLUG);
}
-// Returns whether this call actually set a new underrun flag.
-bool ao_underrun_event(struct ao *ao)
-{
- return ao_add_events(ao, AO_EVENT_UNDERRUN);
-}
-
bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
struct mp_chmap *map)
{
diff --git a/audio/out/ao.h b/audio/out/ao.h
index da81be103c..0207e5a8bf 100644
--- a/audio/out/ao.h
+++ b/audio/out/ao.h
@@ -39,7 +39,7 @@ enum aocontrol {
// If set, then the queued audio data is the last. Note that after a while, new
// data might be written again, instead of closing the AO.
-#define AOPLAY_FINAL_CHUNK 1
+#define PLAYER_FINAL_CHUNK 1
enum {
AO_EVENT_RELOAD = 1,
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
index e100c0fb12..0f375133e1 100644
--- a/audio/out/ao_alsa.c
+++ b/audio/out/ao_alsa.c
@@ -91,12 +91,9 @@ static const struct m_sub_options ao_alsa_conf = {
struct priv {
snd_pcm_t *alsa;
bool device_lost;
+ bool underrun;
snd_pcm_format_t alsa_fmt;
bool can_pause;
- bool paused;
- bool final_chunk_written;
- snd_pcm_sframes_t prepause_frames;
- double delay_before_pause;
snd_pcm_uframes_t buffersize;
snd_pcm_uframes_t outburst;
@@ -121,34 +118,6 @@ struct priv {
MP_WARN(ao, "%s: %s\n", (message), snd_strerror(err)); \
} while (0)
-// Common code for handling ENODEV, which happens if a device gets "lost", and
-// can't be used anymore. Returns true if alsa_err is not ENODEV.
-static bool check_device_present(struct ao *ao, int alsa_err)
-{
- struct priv *p = ao->priv;
- if (alsa_err != -ENODEV)
- return true;
- if (!p->device_lost) {
- MP_WARN(ao, "Device lost, trying to recover...\n");
- ao_request_reload(ao);
- p->device_lost = true;
- }
- return false;
-}
-
-static void handle_underrun(struct ao *ao)
-{
- struct priv *p = ao->priv;
-
- if (!p->final_chunk_written) {
- ao_underrun_event(ao);
-
- int err = snd_pcm_prepare(p->alsa);
- CHECK_ALSA_ERROR("pcm prepare error");
- alsa_error: ;
- }
-}
-
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
{
struct priv *p = ao->priv;
@@ -865,9 +834,8 @@ static int init_device(struct ao *ao, int mode)
err = snd_pcm_sw_params_get_boundary(alsa_swparams, &boundary);
CHECK_ALSA_ERROR("Unable to get boundary");
- /* start playing when one period has been written */
- err = snd_pcm_sw_params_set_start_threshold
- (p->alsa, alsa_swparams, p->outburst);
+ // Manual trigger; INT_MAX as suggested by ALSA doxygen (they call it MAXINT).
+ err = snd_pcm_sw_params_set_start_threshold(p->alsa, alsa_swparams, INT_MAX);
CHECK_ALSA_ERROR("Unable to set start threshold");
/* play silence when there is an underrun */
@@ -887,6 +855,9 @@ static int init_device(struct ao *ao, int mode)
p->convert.channels = ao->channels.num;
+ err = snd_pcm_prepare(p->alsa);
+ CHECK_ALSA_ERROR("pcm prepare error");
+
return 0;
alsa_error:
@@ -944,258 +915,184 @@ static int init(struct ao *ao)
return r;
}
-static int get_space(struct ao *ao)
+// Function for dealing with playback state. This attempts to recover the ALSA
+// state (bring it into SND_PCM_STATE_{PREPARED,RUNNING,PAUSED}). If state!=NULL,
+// fill it after recovery.
+// Returns true if PCM is in one the expected states.
+static bool recover_and_get_state(struct ao *ao, struct mp_pcm_state *state)
{
struct priv *p = ao->priv;
+ int err;
- // in case of pausing or the device still being configured,
- // just return our buffer size.
- if (p->paused || snd_pcm_state(p->alsa) == SND_PCM_STATE_SETUP)
- return p->buffersize;
-
- snd_pcm_sframes_t space = snd_pcm_avail(p->alsa);
- if (space < 0 && space != -EPIPE) {
- MP_ERR(ao, "Error received from snd_pcm_avail "
- "(%ld, %s with ALSA state %s)!\n",
- space, snd_strerror(space),
- snd_pcm_state_name(snd_pcm_state(p->alsa)));
-
- // request a reload of the AO if device is not present,
- // then error out.
- check_device_present(ao, space);
- goto alsa_error;
- }
- if (space == -EPIPE)
- handle_underrun(ao);
-
- if (space > p->buffersize || space < 0) // Buffer underrun?
- space = p->buffersize;
- return space / p->outburst * p->outburst;
-
-alsa_error:
- return 0;
-}
+ snd_pcm_status_t *st;
+ snd_pcm_status_alloca(&st);
-/* delay in seconds between first and last sample in buffer */
-static double get_delay(struct ao *ao)
-{
- struct priv *p = ao->priv;
- snd_pcm_sframes_t delay;
+ bool state_ok = false;
- if (p->paused)
- return p->delay_before_pause;
+ // Give it a number of chances to recover. This tries to deal with the fact
+ // that the API is asynchronous, and to account for some past cargo-cult
+ // (where things were retried in a loop).
+ for (int n = 0; n < 10; n++) {
+ err = snd_pcm_status(p->alsa, st);
+ CHECK_ALSA_ERROR("snd_pcm_status");
- int err = snd_pcm_delay(p->alsa, &delay);
- if (err < 0) {
- if (err == -EPIPE)
- handle_underrun(ao);
- return 0;
- }
+ snd_pcm_state_t pcmst = snd_pcm_status_get_state(st);
+ if (pcmst == SND_PCM_STATE_PREPARED ||
+ pcmst == SND_PCM_STATE_RUNNING ||
+ pcmst == SND_PCM_STATE_PAUSED)
+ {
+ state_ok = true;
+ break;
+ }
- if (delay < 0) {
- /* underrun - move the application pointer forward to catch up */
- snd_pcm_forward(p->alsa, -delay);
- delay = 0;
- }
- return delay / (double)ao->samplerate;
-}
+ MP_VERBOSE(ao, "attempt %d to recover from state '%s'...\n",
+ n + 1, snd_pcm_state_name(pcmst));
-// For stream-silence mode: replace remaining buffer with silence.
-// Tries to cause an instant buffer underrun.
-static void soft_reset(struct ao *ao)
-{
- struct priv *p = ao->priv;
- snd_pcm_sframes_t frames = snd_pcm_rewindable(p->alsa);
- if (frames > 0 && snd_pcm_state(p->alsa) == SND_PCM_STATE_RUNNING) {
- frames = snd_pcm_rewind(p->alsa, frames);
- if (frames < 0) {
- int err = frames;
- CHECK_ALSA_WARN("pcm rewind error");
+ switch (pcmst) {
+ // Underrun; note and recover. We never use draining,
+ case SND_PCM_STATE_XRUN:
+ case SND_PCM_STATE_DRAINING:
+ p->underrun = true;
+ err = snd_pcm_prepare(p->alsa);
+ CHECK_ALSA_ERROR("pcm prepare error");
+ continue;
+ // Hardware suspend.
+ case SND_PCM_STATE_SUSPENDED:
+ MP_INFO(ao, "PCM in suspend mode, trying to resume.\n");
+ err = snd_pcm_resume(p->alsa);
+ if (err == -EAGAIN) {
+ // Cargo-cult from decades ago, with a cargo cult timeout.
+ MP_INFO(ao, "PCM resume EAGAIN - retrying.\n");
+ sleep(1);
+ continue;
+ }
+ if (err == -ENOSYS) {
+ // As suggested by ALSA doxygen.
+ MP_VERBOSE(ao, "ENOSYS, retrying with snd_pcm_prepare().\n");
+ err = snd_pcm_prepare(p->alsa);
+ }
+ if (err < 0)
+ MP_ERR(ao, "resuming from SUSPENDED: %s\n", snd_strerror(err));
+ continue;
+ // Device lost. OPEN/SETUP are states we never enter after init, so
+ // treat them like DISCONNECTED.
+ case SND_PCM_STATE_DISCONNECTED:
+ case SND_PCM_STATE_OPEN:
+ case SND_PCM_STATE_SETUP:
+ default:
+ if (!p->device_lost) {
+ MP_WARN(ao, "Device lost, trying to recover...\n");
+ ao_request_reload(ao);
+ p->device_lost = true;
+ }
+ return false;
}
}
-}
-static void audio_pause(struct ao *ao)
-{
- struct priv *p = ao->priv;
- int err;
-
- if (p->paused)
- return;
-
- p->delay_before_pause = get_delay(ao);
- p->prepause_frames = p->delay_before_pause * ao->samplerate;
+ if (!state_ok) {
+ MP_ERR(ao, "could not recover\n");
+ return false;
+ }
- if (ao->stream_silence) {
- soft_reset(ao);
- } else if (p->can_pause) {
- if (snd_pcm_state(p->alsa) == SND_PCM_STATE_RUNNING) {
- err = snd_pcm_pause(p->alsa, 1);
- CHECK_ALSA_ERROR("pcm pause error");
- p->prepause_frames = 0;
- }
- } else {
- err = snd_pcm_drop(p->alsa);
- CHECK_ALSA_ERROR("pcm drop error");
+ if (state) {
+ snd_pcm_sframes_t del = snd_pcm_status_get_delay(st);
+ state->delay = MPMAX(del, 0) / (double)ao->samplerate;
+ state->free_samples = snd_pcm_status_get_avail(st);
+ state->free_samples = MPCLAMP(state->free_samples, 0, ao->device_buffer);
+ state->queued_samples = ao->device_buffer - state->free_samples;
+ state->underrun = p->underrun;
}
- p->paused = true;
+ return true;
-alsa_error: ;
+alsa_error:
+ return false;
}
-static void resume_device(struct ao *ao)
+static void audio_get_state(struct ao *ao, struct mp_pcm_state *state)
{
struct priv *p = ao->priv;
- int err;
-
- if (snd_pcm_state(p->alsa) == SND_PCM_STATE_SUSPENDED) {
- MP_INFO(ao, "PCM in suspend mode, trying to resume.\n");
-
- while ((err = snd_pcm_resume(p->alsa)) == -EAGAIN)
- sleep(1);
- }
+ recover_and_get_state(ao, state);
+ p->underrun = false;
}
-static void audio_resume(struct ao *ao)
+static void audio_start(struct ao *ao)
{
struct priv *p = ao->priv;
int err;
- if (!p->paused)
- return;
-
- resume_device(ao);
-
- if (ao->stream_silence) {
- p->paused = false;
- get_delay(ao); // recovers from underrun (as a side-effect)
- } else if (p->can_pause) {
- if (snd_pcm_state(p->alsa) == SND_PCM_STATE_PAUSED) {
- err = snd_pcm_pause(p->alsa, 0);
- CHECK_ALSA_ERROR("pcm resume error");
- }
- } else {
- MP_VERBOSE(ao, "resume not supported by hardware\n");
- err = snd_pcm_prepare(p->alsa);
- CHECK_ALSA_ERROR("pcm prepare error");
- }
+ recover_and_get_state(ao, NULL);
- if (p->prepause_frames)
- ao_play_silence(ao, p->prepause_frames);
+ err = snd_pcm_start(p->alsa);
+ CHECK_ALSA_ERROR("pcm start error");
alsa_error: ;
- p->paused = false;
}
-static void reset(struct ao *ao)
+static void audio_reset(struct ao *ao)
{
struct priv *p = ao->priv;
int err;
- p->paused = false;
- p->prepause_frames = 0;
- p->delay_before_pause = 0;
- p->final_chunk_written = false;
+ err = snd_pcm_drop(p->alsa);
+ CHECK_ALSA_ERROR("pcm drop error");
+ err = snd_pcm_prepare(p->alsa);
+ CHECK_ALSA_ERROR("pcm prepare error");
- if (ao->stream_silence) {
- soft_reset(ao);
- } else {
- err = snd_pcm_drop(p->alsa);
- CHECK_ALSA_ERROR("pcm prepare error");
- err = snd_pcm_prepare(p->alsa);
- CHECK_ALSA_ERROR("pcm prepare error");
- }
+ recover_and_get_state(ao, NULL);
alsa_error: ;
}
-static int play(struct ao *ao, void **data, int samples, int flags)
+static bool audio_set_paused(struct ao *ao, bool paused)
{
struct priv *p = ao->priv;
- snd_pcm_sframes_t res = 0;
- bool final_chunk = flags & AOPLAY_FINAL_CHUNK;
-
- if (!final_chunk)
- samples = samples / p->outburst * p->outburst;
-
- if (samples == 0)
- goto done;
- ao_convert_inplace(&p->convert, data, samples);
+ int err;
- do {
- if (af_fmt_is_planar(ao->format)) {
- res = snd_pcm_writen(p->alsa, data, samples);
- } else {
- res = snd_pcm_writei(p->alsa, data[0], samples);
- }
+ recover_and_get_state(ao, NULL);
- if (res == -EINTR || res == -EAGAIN) { /* retry */
- res = 0;
- } else if (!check_device_present(ao, res)) {
- goto alsa_error;
- } else if (res < 0) {
- if (res == -ESTRPIPE) { /* suspend */
- resume_device(ao);
- } else if (res == -EPIPE) {
- handle_underrun(ao);
- } else {
- MP_ERR(ao, "Write error: %s\n", snd_strerror(res));
- }
- int err = snd_pcm_prepare(p->alsa);
- CHECK_ALSA_ERROR("pcm prepare error");
- res = 0;
- }
- } while (res == 0);
+ if (!p->can_pause)
+ return false;
- p->paused = false;
+ snd_pcm_state_t pcmst = snd_pcm_state(p->alsa);
+ if (pcmst == SND_PCM_STATE_RUNNING && paused) {
+ err = snd_pcm_pause(p->alsa, 1);
+ CHECK_ALSA_ERROR("pcm pause error");
+ } else if (pcmst == SND_PCM_STATE_PAUSED && !paused) {
+ err = snd_pcm_pause(p->alsa, 0);
+ CHECK_ALSA_ERROR("pcm resume error");
+ }
-done:
- p->final_chunk_written = res == samples && final_chunk;
- return res < 0 ? -1 : res;
+ return true;
alsa_error:
- return -1;
+ return false;
}
-#define MAX_POLL_FDS 20
-static int audio_wait(struct ao *ao, pthread_mutex_t *lock)
+static bool audio_write(struct ao *ao, void **data, int samples)
{
struct priv *p = ao->priv;
- int err;
-
- int num_fds = snd_pcm_poll_descriptors_count(p->alsa);
- if (num_fds <= 0 || num_fds >= MAX_POLL_FDS)
- goto alsa_error;
- struct pollfd fds[MAX_POLL_FDS];
- err = snd_pcm_poll_descriptors(p->alsa, fds, num_fds);
- CHECK_ALSA_ERROR("cannot get pollfds");
+ ao_convert_inplace(&p->convert, data, samples);
- while (1) {
- int r = ao_wait_poll(ao, fds, num_fds, lock);
- if (r)
- return r;
+ if (!recover_and_get_state(ao, NULL))
+ return false;
- unsigned short revents;
- err = snd_pcm_poll_descriptors_revents(p->alsa, fds, num_fds, &revents);
- CHECK_ALSA_ERROR("cannot read poll events");
+ snd_pcm_sframes_t err = 0;
+ if (af_fmt_is_planar(ao->format)) {
+ err = snd_pcm_writen(p->alsa, data, samples);
+ } else {
+ err = snd_pcm_writei(p->alsa, data[0], samples);
+ }
- if (revents & POLLERR) {
- snd_pcm_status_t *status;
- snd_pcm_status_alloca(&status);
+ CHECK_ALSA_ERROR("pcm write error");
+ if (err != samples)
+ MP_WARN(ao, "unexpected short write\n");
- err = snd_pcm_status(p->alsa, status);
- check_device_present(ao, err);
- return -1;
- }
- if (revents & POLLOUT)
- return 0;
- }
- return 0;
+ return true;
alsa_error:
- return -1;
+ return false;
}
static bool is_useless_device(char *name)
@@ -1248,16 +1145,12 @@ const struct ao_driver audio_out_alsa = {
.init = init,
.uninit = uninit,
.control = control,
- .get_space = get_space,
- .play = play,
- .get_delay = get_delay,
- .pause = audio_pause,
- .resume = audio_resume,
- .reset = reset,
- .wait = audio_wait,
- .wakeup = ao_wakeup_poll,
+ .get_state = audio_get_state,
+ .write = audio_write,
+ .start = audio_start,
+ .set_pause = audio_set_paused,
+ .reset = audio_reset,
.list_devs = list_devs,
- .reports_underruns = true,
.priv_size = sizeof(struct priv),
.global_opts = &ao_alsa_conf,
};
diff --git a/audio/out/ao_audiotrack.c b/audio/out/ao_audiotrack.c
index b3be357c1b..7a1715d373 100644
--- a/audio/out/ao_audiotrack.c
+++ b/audio/out/ao_audiotrack.c
@@ -706,7 +706,7 @@ const struct ao_driver audio_out_audiotrack = {
.init = init,
.uninit = uninit,
.reset = stop,
- .resume = start,
+ .start = start,
.priv_size = sizeof(struct priv),
.options = (const struct m_option[]) {
{"pcm-float", OPT_FLAG(cfg_pcm_float)},
diff --git a/audio/out/ao_audiounit.m b/audio/out/ao_audiounit.m
index dd77464edb..0dfcb1d82b 100644
--- a/audio/out/ao_audiounit.m
+++ b/audio/out/ao_audiounit.m
@@ -193,6 +193,6 @@ const struct ao_driver audio_out_audiounit = {
.uninit = uninit,
.init = init,
.reset = stop,
- .resume = start,
+ .start = start,
.priv_size = sizeof(struct priv),
};
diff --git a/audio/out/ao_coreaudio.c b/audio/out/ao_coreaudio.c
index 533d102d32..99007ef018 100644
--- a/audio/out/ao_coreaudio.c
+++ b/audio/out/ao_coreaudio.c
@@ -417,7 +417,7 @@ const struct ao_driver audio_out_coreaudio = {
.init = init,
.control = control,
.reset = stop,
- .resume = start,
+ .start = start,
.hotplug_init = hotplug_init,
.hotplug_uninit = hotplug_uninit,
.list_devs = ca_get_device_list,
diff --git a/audio/out/ao_coreaudio_exclusive.c b/audio/out/ao_coreaudio_exclusive.c
index 19721e0951..b1d7c57017 100644
--- a/audio/out/ao_coreaudio_exclusive.c
+++ b/audio/out/ao_coreaudio_exclusive.c
@@ -455,7 +455,7 @@ const struct ao_driver audio_out_coreaudio_exclusive = {
.uninit = uninit,
.init = init,
.reset = audio_pause,
- .resume = audio_resume,
+ .start = audio_resume,
.list_devs = ca_get_device_list,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv){
diff --git a/audio/out/ao_jack.c b/audio/out/ao_jack.c
index 249c314f9d..767f437895 100644
--- a/audio/out/ao_jack.c
+++ b/audio/out/ao_jack.c
@@ -194,7 +194,7 @@ err_port_register:
return -1;
}
-static void resume(struct ao *ao)
+static void start(struct ao *ao)
{
struct priv *p = ao->priv;
if (!p->activated) {
@@ -276,7 +276,7 @@ const struct ao_driver audio_out_jack = {
.name = "jack",
.init = init,
.uninit = uninit,
- .resume = resume,
+ .start = start,
.priv_size = sizeof(struct priv),
.global_opts = &ao_jack_conf,
};
diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c
index ad3865964c..33e82219b0 100644
--- a/audio/out/ao_lavc.c
+++ b/audio/out/ao_lavc.c
@@ -156,7 +156,8 @@ static int init(struct ao *ao)
ao->untimed = true;
- ao->period_size = ac->aframesize * ac->framecount;
+ ao->device_buffer = ac->aframesize * ac->framecount;
+ ao->period_size = ao->device_buffer;
if (ao->channels.num > AV_NUM_DATA_POINTERS)
goto fail;
@@ -188,14 +189,6 @@ static void uninit(struct ao *ao)
}
}
-// return: how many samples can be played without blocking
-static int get_space(struct ao *ao)
-{
- struct priv *ac = ao->priv;
-
- return ac->aframesize * ac->framecount;
-}
-
// must get exactly ac->aframesize amount of data
static void encode(struct ao *ao, double apts, void **data)
{
@@ -249,9 +242,9 @@ static void encode(struct ao *ao, double apts, void **data)
}
}
-// this should round samples down to frame sizes
-// return: number of samples played
-static int play(struct ao *ao, void **data, int samples, int flags)
+// Note: currently relies on samples aligned to period sizes - will not work
+// in the future.
+static bool audio_write(struct ao *ao, void **data, int samples)
{
struct priv *ac = ao->priv;
struct encoder_context *enc = ac->enc;
@@ -271,7 +264,7 @@ static int play(struct ao *ao, void **data, int samples, int flags)
void *tempdata = NULL;
void *padded[MP_NUM_CHANNELS];
- if ((flags & AOPLAY_FINAL_CHUNK) && (samples % ac->aframesize)) {
+ if (samples % ac->aframesize) {
tempdata = talloc_new(NULL);
size_t bytelen = samples * ao->sstride;
size_t extralen = (ac->aframesize - 1) * ao->sstride;
@@ -282,6 +275,7 @@ static int play(struct ao *ao, void **data, int samples, int flags)
}
data = padded;
samples = (bytelen + extralen) / ao->sstride;
+ MP_VERBOSE(ao, "padding final frame with silence\n");
}
double outpts = pts;
@@ -334,15 +328,28 @@ static int play(struct ao *ao, void **data, int samples, int flags)
pthread_mutex_unlock(&ectx->lock);
- if (flags & AOPLAY_FINAL_CHUNK) {
- if (bufpos < orig_samples)
- MP_ERR(ao, "did not write enough data at the end\n");
- } else {
- if (bufpos > orig_samples)
- MP_ERR(ao, "audio buffer overflow (should never happen)\n");
- }
+ return true;
+}
+
+static void get_state(struct ao *ao, struct mp_pcm_state *state)
+{
+ state->free_samples = ao->device_buffer;
+ state->queued_samples = 0;
+ state->delay = 0;
+}
+
+static bool set_pause(struct ao *ao, bool paused)
+{
+ return true; // signal support so common code doesn't write silence
+}
- return taken;
+static void start(struct ao *ao)
+{
+ // we use data immediately
+}
+
+static void reset(struct ao *ao)
+{
}
const struct ao_driver audio_out_lavc = {
@@ -350,12 +357,14 @@ const struct ao_driver audio_out_lavc = {
.description = "audio encoding using libavcodec",
.name = "lavc",
.initially_blocked = true,
- .reports_underruns = true, // not a thing
.priv_size = sizeof(struct priv),
.init = init,
.uninit = uninit,
- .get_space = get_space,
- .play = play,
+ .get_state = get_state,
+ .set_pause = set_pause,
+ .write = audio_write,
+ .start = start,
+ .reset = reset,
};
// vim: sw=4 ts=4 et tw=80
diff --git a/audio/out/ao_null.c b/audio/out/ao_null.c
index 07a0d4748c..107dc7e35a 100644
--- a/audio/out/ao_null.c
+++ b/audio/out/ao_null.c
@@ -40,9 +40,10 @@
struct priv {
bool paused;
double last_time;
- bool playing_final;
float buffered; // samples
int buffersize; // samples
+ bool playing;
+ bool underrun;
int untimed;
float bufferlen; // seconds
@@ -77,8 +78,7 @@ static void drain(struct ao *ao)
if (priv->buffered > 0) {
priv->buffered -= (now - priv->last_time) * ao->samplerate * priv->speed;
if (priv->buffered < 0) {
- if (!priv->playing_final)
- MP_ERR(ao, "buffer underrun\n");
+ priv->underrun = true;
priv->buffered = 0;
}
}
@@ -127,85 +127,81 @@ static void reset(struct ao *ao)
{
struct priv *priv = ao->priv;
priv->buffered = 0;
- priv->playing_final = false;
+ priv->underrun = false;
+ priv->playing = false;
}
-// stop playing, keep buffers (for pause)
-static void pause(struct ao *ao)
+static void start(struct ao *ao)
{
struct priv *priv = ao->priv;
- drain(ao);
- priv->paused = true;
-}
-
-// resume playing, after pause()
-static void resume(struct ao *ao)
-{
- struct priv *priv = ao->priv;
+ if (priv->paused)
+ MP_ERR(ao, "illegal state: start() while paused\n");
drain(ao);
priv->paused = false;
priv->last_time = mp_time_sec();
+ priv->playing = true;
}
-static int get_space(struct ao *ao)
+static bool set_pause(struct ao *ao, bool paused)
{
struct priv *priv = ao->priv;
- drain(ao);
- int samples = priv->buffersize - priv->latency - priv->buffered;
- return samples / priv->outburst * priv->outburst;
+ if (!priv->playing)
+ MP_ERR(ao, "illegal state: set_pause() while not playing\n");
+
+ if (priv->paused != paused) {
+
+ drain(ao);
+ priv->paused = paused;
+ if (!priv->paused)
+ priv->last_time = mp_time_sec();
+ }
+
+ return true;
}
-static int play(struct ao *ao, void **data, int samples, int flags)
+static bool audio_write(struct ao *ao, void **data, int samples)
{
struct priv *priv = ao->priv;
- int accepted;
-
- resume(ao);
if (priv->buffered <= 0)
priv->buffered = priv->latency; // emulate fixed latency
- priv->playing_final = flags & AOPLAY_FINAL_CHUNK;
- if (priv->playing_final) {
- // Last audio chunk - don't round to outburst.
- accepted = MPMIN(priv->buffersize - priv->buffered, samples);
- } else {
- int maxbursts = (priv->buffersize - priv->buffered) / priv->outburst;
- int playbursts = samples / priv->outburst;
- int bursts = playbursts > maxbursts ? maxbursts : playbursts;
- accepted = bursts * priv->outburst;
- }
- priv->buffered += accepted;
- return accepted;
+ priv->buffered += samples;
+ return true;
}
-static double get_delay(struct ao *ao)
+static void get_state(struct ao *ao, struct mp_pcm_state *state)
{
struct priv *priv = ao->priv;
drain(ao);
- // Note how get_delay returns the delay in audio device time (instead of
+ state->free_samples = priv->buffersize - priv->latency - priv->buffered;
+ state->free_samples = state->free_samples / priv->outburst * priv->outburst;
+ state->queued_samples = priv->buffered;
+
+ // Note how get_state returns the delay in audio device time (instead of
// adjusting for speed), since most AOs seem to also do that.
- double delay = priv->buffered;
+ state->delay = priv->buffered;
// Drivers with broken EOF handling usually always report the same device-
// level delay that is additional to the buffer time.
if (priv->broken_eof && priv->buffered < priv->latency)
- delay = priv->latency;
+ state->delay = priv->latency;
- delay /= ao->samplerate;
+ state->delay /= ao->samplerate;
if (priv->broken_delay) { // Report only multiples of outburst
double q = priv->outburst / (double)ao->samplerate;
- if (delay > 0)
- delay = (int)(delay / q) * q;
+ if (state->delay > 0)
+ state->delay = (int)(state->delay / q) * q;
}
- return delay;
+ state->underrun = priv->underrun;
+ priv->underrun = false;
}
#define OPT_BASE_STRUCT struct priv
@@ -216,11 +212,10 @@ const struct ao_driver audio_out_null = {
.init = init,
.uninit = uninit,
.reset = reset,
- .get_space = get_space,
- .play = play,
- .get_delay = get_delay,
- .pause = pause,
- .resume = resume,
+ .get_state = get_state,
+ .set_pause = set_pause,
+ .write = audio_write,
+ .start = start,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {
.bufferlen = 0.2,
diff --git a/audio/out/ao_openal.c b/audio/out/ao_openal.c
index aa20fb8bed..64d957e893 100644
--- a/audio/out/ao_openal.c
+++ b/audio/out/ao_openal.c
@@ -61,8 +61,6 @@ struct priv {
int direct_channels;
};
-static void reset(struct ao *ao);
-
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
{
switch (cmd) {
@@ -177,6 +175,9 @@ static void uninit(struct ao *ao)
static int init(struct ao *ao)
{
+ MP_FATAL(ao, "broken.\n");
+ return -1;
+
float position[3] = {0, 0, 0};
float direction[6] = {0, 0, -1, 0, 1, 0};
ALCdevice *dev = NULL;
@@ -291,67 +292,32 @@ static void unqueue_buffers(struct ao *ao)
}
}
-/**
- * \brief stop playing and empty buffers (for seeking/pause)
- */
static void reset(struct ao *ao)
{
alSourceStop(source);
unqueue_buffers(ao);
}
-/**
- * \brief stop playing, keep buffers (for pause)
- */
-static void audio_pause(struct ao *ao)
-{
- alSourcePause(source);
-}
-
-/**
- * \brief resume playing, after