diff options
-rw-r--r-- | DOCS/man/ao.rst | 2 | ||||
-rw-r--r-- | audio/out/ao.c | 12 | ||||
-rw-r--r-- | audio/out/ao.h | 2 | ||||
-rw-r--r-- | audio/out/ao_alsa.c | 369 | ||||
-rw-r--r-- | audio/out/ao_audiotrack.c | 2 | ||||
-rw-r--r-- | audio/out/ao_audiounit.m | 2 | ||||
-rw-r--r-- | audio/out/ao_coreaudio.c | 2 | ||||
-rw-r--r-- | audio/out/ao_coreaudio_exclusive.c | 2 | ||||
-rw-r--r-- | audio/out/ao_jack.c | 4 | ||||
-rw-r--r-- | audio/out/ao_lavc.c | 57 | ||||
-rw-r--r-- | audio/out/ao_null.c | 89 | ||||
-rw-r--r-- | audio/out/ao_openal.c | 90 | ||||
-rw-r--r-- | audio/out/ao_opensles.c | 2 | ||||
-rw-r--r-- | audio/out/ao_pcm.c | 40 | ||||
-rw-r--r-- | audio/out/ao_pulse.c | 160 | ||||
-rw-r--r-- | audio/out/ao_sdl.c | 4 | ||||
-rw-r--r-- | audio/out/ao_wasapi.c | 2 | ||||
-rw-r--r-- | audio/out/buffer.c | 497 | ||||
-rw-r--r-- | audio/out/internal.h | 118 | ||||
-rw-r--r-- | player/audio.c | 2 |
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 |