summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2014-03-09 00:49:39 +0100
committerwm4 <wm4@nowhere>2014-03-09 01:27:41 +0100
commite16c91d07ab2acfb83fdeaa6dcfcd25c97666504 (patch)
tree5e5e33af457c4d573a32e15aec95205f4987f9bb
parent2f03dc259960c9cb282e8f371d9f68266afea49c (diff)
downloadmpv-e16c91d07ab2acfb83fdeaa6dcfcd25c97666504.tar.bz2
mpv-e16c91d07ab2acfb83fdeaa6dcfcd25c97666504.tar.xz
audio/out: make draining a separate operation
Until now, this was always conflated with uninit. This was ugly, and also many AOs emulated this manually (or just ignored it). Make draining an explicit operation, so AOs which support it can provide it, and for all others generic code will emulate it. For ao_wasapi, we keep it simple and basically disable the internal draining implementation (maybe it should be restored later). Tested on Linux only.
-rw-r--r--audio/out/ao.c22
-rw-r--r--audio/out/ao.h3
-rw-r--r--audio/out/ao_alsa.c16
-rw-r--r--audio/out/ao_coreaudio.c5
-rw-r--r--audio/out/ao_dsound.c4
-rw-r--r--audio/out/ao_jack.c5
-rw-r--r--audio/out/ao_lavc.c8
-rw-r--r--audio/out/ao_null.c10
-rw-r--r--audio/out/ao_openal.c21
-rw-r--r--audio/out/ao_oss.c21
-rw-r--r--audio/out/ao_pcm.c2
-rw-r--r--audio/out/ao_portaudio.c9
-rw-r--r--audio/out/ao_pulse.c12
-rw-r--r--audio/out/ao_rsound.c8
-rw-r--r--audio/out/ao_sdl.c16
-rw-r--r--audio/out/ao_sndio.c2
-rw-r--r--audio/out/ao_wasapi.c4
-rw-r--r--audio/out/internal.h3
-rw-r--r--audio/out/pull.c4
-rw-r--r--audio/out/push.c18
-rw-r--r--player/loadfile.c5
21 files changed, 116 insertions, 82 deletions
diff --git a/audio/out/ao.c b/audio/out/ao.c
index f1e88d2a10..ee8bc2a254 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -235,11 +235,10 @@ done:
return ao;
}
-// Uninitialize and destroy the AO.
-// cut_audio: if false, block until all remaining audio was played.
-void ao_uninit(struct ao *ao, bool cut_audio)
+// Uninitialize and destroy the AO. Remaining audio must be dropped.
+void ao_uninit(struct ao *ao)
{
- ao->api->uninit(ao, cut_audio);
+ ao->api->uninit(ao);
talloc_free(ao);
}
@@ -315,13 +314,22 @@ void ao_resume(struct ao *ao)
ao->api->resume(ao);
}
-// Wait until the audio buffer is drained. This can be used to emulate draining
-// if native functionality is not available.
-// Only call this on uninit (otherwise, deadlock trouble ahead).
+// Be careful with locking
void ao_wait_drain(struct ao *ao)
{
// This is probably not entirely accurate, but good enough.
mp_sleep_us(ao_get_delay(ao) * 1000000);
+ ao_reset(ao);
+}
+
+// Block until the current audio buffer has played completely.
+void ao_drain(struct ao *ao)
+{
+ if (ao->api->drain) {
+ ao->api->drain(ao);
+ } else {
+ ao_wait_drain(ao);
+ }
}
bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
diff --git a/audio/out/ao.h b/audio/out/ao.h
index 152490aacc..e462fcf4d5 100644
--- a/audio/out/ao.h
+++ b/audio/out/ao.h
@@ -59,7 +59,7 @@ struct ao *ao_init_best(struct mpv_global *global,
struct input_ctx *input_ctx,
struct encode_lavc_context *encode_lavc_ctx,
int samplerate, int format, struct mp_chmap channels);
-void ao_uninit(struct ao *ao, bool cut_audio);
+void ao_uninit(struct ao *ao);
void ao_get_format(struct ao *ao, struct mp_audio *format);
const char *ao_get_name(struct ao *ao);
const char *ao_get_description(struct ao *ao);
@@ -71,5 +71,6 @@ int ao_get_space(struct ao *ao);
void ao_reset(struct ao *ao);
void ao_pause(struct ao *ao);
void ao_resume(struct ao *ao);
+void ao_drain(struct ao *ao);
#endif /* MPLAYER_AUDIO_OUT_H */
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
index d50757bdfd..291c6d7c1c 100644
--- a/audio/out/ao_alsa.c
+++ b/audio/out/ao_alsa.c
@@ -79,7 +79,7 @@ struct priv {
} while (0)
static float get_delay(struct ao *ao);
-static void uninit(struct ao *ao, bool immed);
+static void uninit(struct ao *ao);
/* to set/get/query special features/parameters */
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
@@ -530,22 +530,19 @@ static int init(struct ao *ao)
return 0;
alsa_error:
- uninit(ao, true);
+ uninit(ao);
return -1;
} // end init
/* close audio device */
-static void uninit(struct ao *ao, bool immed)
+static void uninit(struct ao *ao)
{
struct priv *p = ao->priv;
if (p->alsa) {
int err;
- if (!immed)
- snd_pcm_drain(p->alsa);
-
err = snd_pcm_close(p->alsa);
CHECK_ALSA_ERROR("pcm close error");
@@ -556,6 +553,12 @@ alsa_error:
p->alsa = NULL;
}
+static void drain(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+ snd_pcm_drain(p->alsa);
+}
+
static void audio_pause(struct ao *ao)
{
struct priv *p = ao->priv;
@@ -712,6 +715,7 @@ const struct ao_driver audio_out_alsa = {
.pause = audio_pause,
.resume = audio_resume,
.reset = reset,
+ .drain = drain,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {
.cfg_block = 1,
diff --git a/audio/out/ao_coreaudio.c b/audio/out/ao_coreaudio.c
index 36be7c5872..e8b0179d20 100644
--- a/audio/out/ao_coreaudio.c
+++ b/audio/out/ao_coreaudio.c
@@ -626,14 +626,11 @@ static float get_delay(struct ao *ao)
return mp_ring_buffered(p->buffer) / (float)ao->bps;
}
-static void uninit(struct ao *ao, bool immed)
+static void uninit(struct ao *ao)
{
struct priv *p = ao->priv;
OSStatus err = noErr;
- if (!immed)
- mp_sleep_us(get_delay(ao) * 1000000);
-
if (!p->is_digital) {
AudioOutputUnitStop(p->audio_unit);
AudioUnitUninitialize(p->audio_unit);
diff --git a/audio/out/ao_dsound.c b/audio/out/ao_dsound.c
index ce7346fbdb..6ddf080d20 100644
--- a/audio/out/ao_dsound.c
+++ b/audio/out/ao_dsound.c
@@ -546,10 +546,8 @@ static void audio_resume(struct ao *ao)
\brief close audio device
\param immed stop playback immediately
*/
-static void uninit(struct ao *ao, bool immed)
+static void uninit(struct ao *ao)
{
- if (!immed)
- mp_sleep_us(get_delay(ao) * 1000000);
reset(ao);
DestroyBuffer(ao);
diff --git a/audio/out/ao_jack.c b/audio/out/ao_jack.c
index b3cd3fd6ef..6e9b652f2d 100644
--- a/audio/out/ao_jack.c
+++ b/audio/out/ao_jack.c
@@ -207,13 +207,10 @@ err_chmap:
}
// close audio device
-static void uninit(struct ao *ao, bool immed)
+static void uninit(struct ao *ao)
{
struct priv *p = ao->priv;
- if (!immed)
- ao_wait_drain(ao);
-
jack_client_close(p->client);
}
diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c
index afb021f7a0..073ff8ebdb 100644
--- a/audio/out/ao_lavc.c
+++ b/audio/out/ao_lavc.c
@@ -181,7 +181,7 @@ fail:
// close audio device
static int encode(struct ao *ao, double apts, void **data);
-static void uninit(struct ao *ao, bool cut_audio)
+static void uninit(struct ao *ao)
{
struct priv *ac = ao->priv;
struct encode_lavc_context *ectx = ao->encode_lavc_ctx;
@@ -462,6 +462,11 @@ static int play(struct ao *ao, void **data, int samples, int flags)
return bufpos;
}
+static void drain(struct ao *ao)
+{
+ // pretend we support it, so generic code doesn't force a wait
+}
+
const struct ao_driver audio_out_lavc = {
.encode = true,
.description = "audio encoding using libavcodec",
@@ -470,4 +475,5 @@ const struct ao_driver audio_out_lavc = {
.uninit = uninit,
.get_space = get_space,
.play = play,
+ .drain = drain,
};
diff --git a/audio/out/ao_null.c b/audio/out/ao_null.c
index a6b40fdb76..6fff65dcd9 100644
--- a/audio/out/ao_null.c
+++ b/audio/out/ao_null.c
@@ -98,10 +98,15 @@ static int init(struct ao *ao)
}
// close audio device
-static void uninit(struct ao *ao, bool cut_audio)
+static void uninit(struct ao *ao)
+{
+}
+
+static void wait_drain(struct ao *ao)
{
struct priv *priv = ao->priv;
- if (!cut_audio && !priv->paused)
+ drain(ao);
+ if (!priv->paused)
mp_sleep_us(1000000.0 * priv->buffered / ao->samplerate / priv->speed);
}
@@ -185,6 +190,7 @@ const struct ao_driver audio_out_null = {
.get_delay = get_delay,
.pause = pause,
.resume = resume,
+ .drain = wait_drain,
.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 8cc29db689..f2cbe78e94 100644
--- a/audio/out/ao_openal.c
+++ b/audio/out/ao_openal.c
@@ -178,18 +178,10 @@ err_out:
}
// close audio device
-static void uninit(struct ao *ao, bool immed)
+static void uninit(struct ao *ao)
{
ALCcontext *ctx = alcGetCurrentContext();
ALCdevice *dev = alcGetContextsDevice(ctx);
- if (!immed) {
- ALint state;
- alGetSourcei(sources[0], AL_SOURCE_STATE, &state);
- while (state == AL_PLAYING) {
- mp_sleep_us(10000);
- alGetSourcei(sources[0], AL_SOURCE_STATE, &state);
- }
- }
reset(ao);
alcMakeContextCurrent(NULL);
alcDestroyContext(ctx);
@@ -197,6 +189,16 @@ static void uninit(struct ao *ao, bool immed)
ao_data = NULL;
}
+static void drain(struct ao *ao)
+{
+ ALint state;
+ alGetSourcei(sources[0], AL_SOURCE_STATE, &state);
+ while (state == AL_PLAYING) {
+ mp_sleep_us(10000);
+ alGetSourcei(sources[0], AL_SOURCE_STATE, &state);
+ }
+}
+
static void unqueue_buffers(void)
{
ALint p;
@@ -298,6 +300,7 @@ const struct ao_driver audio_out_openal = {
.pause = audio_pause,
.resume = audio_resume,
.reset = reset,
+ .drain = drain,
.priv_size = sizeof(struct priv),
.options = (const struct m_option[]) {
OPT_STRING_VALIDATE("device", cfg_device, 0, validate_device_opt),
diff --git a/audio/out/ao_oss.c b/audio/out/ao_oss.c
index a1bf0feefc..ca7539b590 100644
--- a/audio/out/ao_oss.c
+++ b/audio/out/ao_oss.c
@@ -427,24 +427,28 @@ ac3_retry:
}
// close audio device
-static void uninit(struct ao *ao, bool immed)
+static void uninit(struct ao *ao)
{
struct priv *p = ao->priv;
if (p->audio_fd == -1)
return;
-#ifdef SNDCTL_DSP_SYNC
- // to get the buffer played
- if (!immed)
- ioctl(p->audio_fd, SNDCTL_DSP_SYNC, NULL);
-#endif
#ifdef SNDCTL_DSP_RESET
- if (immed)
- ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL);
+ ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL);
#endif
close(p->audio_fd);
p->audio_fd = -1;
}
+static void drain(struct ao *ao)
+{
+#ifdef SNDCTL_DSP_SYNC
+ struct priv *p = ao->priv;
+ // to get the buffer played
+ if (p->audio_fd != -1)
+ ioctl(p->audio_fd, SNDCTL_DSP_SYNC, NULL);
+#endif
+}
+
#ifndef SNDCTL_DSP_RESET
static void close_device(struct ao *ao)
{
@@ -601,6 +605,7 @@ const struct ao_driver audio_out_oss = {
.pause = audio_pause,
.resume = audio_resume,
.reset = reset,
+ .drain = drain,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {
.audio_fd = -1,
diff --git a/audio/out/ao_pcm.c b/audio/out/ao_pcm.c
index 5824f7ce99..b14c9f4d79 100644
--- a/audio/out/ao_pcm.c
+++ b/audio/out/ao_pcm.c
@@ -166,7 +166,7 @@ static int init(struct ao *ao)
}
// close audio device
-static void uninit(struct ao *ao, bool cut_audio)
+static void uninit(struct ao *ao)
{
struct priv *priv = ao->priv;
diff --git a/audio/out/ao_portaudio.c b/audio/out/ao_portaudio.c
index 135c9224f1..ae8b76e830 100644
--- a/audio/out/ao_portaudio.c
+++ b/audio/out/ao_portaudio.c
@@ -146,16 +146,13 @@ static int stream_callback(const void *input,
return paContinue;
}
-static void uninit(struct ao *ao, bool cut_audio)
+static void uninit(struct ao *ao)
{
struct priv *priv = ao->priv;
if (priv->stream) {
- if (!cut_audio && Pa_IsStreamActive(priv->stream) == 1) {
- ao_wait_drain(ao);
-
+ if (Pa_IsStreamActive(priv->stream) == 1)
CHECK_PA_RET(Pa_StopStream(priv->stream));
- }
CHECK_PA_RET(Pa_CloseStream(priv->stream));
}
@@ -220,7 +217,7 @@ static int init(struct ao *ao)
return 0;
error_exit:
- uninit(ao, true);
+ uninit(ao);
return -1;
}
diff --git a/audio/out/ao_pulse.c b/audio/out/ao_pulse.c
index 12fca17dd6..d08ffcfc53 100644
--- a/audio/out/ao_pulse.c
+++ b/audio/out/ao_pulse.c
@@ -205,13 +205,18 @@ static bool select_chmap(struct ao *ao, pa_channel_map *dst)
chmap_pa_from_mp(dst, &ao->channels);
}
-static void uninit(struct ao *ao, bool cut_audio)
+static void drain(struct ao *ao)
{
struct priv *priv = ao->priv;
- if (priv->stream && !cut_audio) {
+ if (priv->stream) {
pa_threaded_mainloop_lock(priv->mainloop);
waitop(priv, pa_stream_drain(priv->stream, success_cb, ao));
}
+}
+
+static void uninit(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
if (priv->mainloop)
pa_threaded_mainloop_stop(priv->mainloop);
@@ -366,7 +371,7 @@ fail:
if (proplist)
pa_proplist_free(proplist);
- uninit(ao, true);
+ uninit(ao);
return -1;
}
@@ -620,6 +625,7 @@ const struct ao_driver audio_out_pulse = {
.get_delay = get_delay,
.pause = pause,
.resume = resume,
+ .drain = drain,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {
.cfg_buffer = 250,
diff --git a/audio/out/ao_rsound.c b/audio/out/ao_rsound.c
index ef81c621da..75395c5e96 100644
--- a/audio/out/ao_rsound.c
+++ b/audio/out/ao_rsound.c
@@ -128,15 +128,9 @@ static int init(struct ao *ao)
return 0;
}
-static void uninit(struct ao *ao, bool cut_audio)
+static void uninit(struct ao *ao)
{
struct priv *priv = ao->priv;
- /* The API does not provide a direct way to explicitly wait until
- * the last byte has been played server-side as this cannot be
- * guaranteed by backend drivers, so we approximate this behavior.
- */
- if (!cut_audio)
- mp_sleep_us(rsd_delay_ms(priv->rd) * 1000);
rsd_stop(priv->rd);
rsd_free(priv->rd);
diff --git a/audio/out/ao_sdl.c b/audio/out/ao_sdl.c
index acb959b101..662eeca484 100644
--- a/audio/out/ao_sdl.c
+++ b/audio/out/ao_sdl.c
@@ -78,7 +78,7 @@ static void audio_callback(void *userdata, Uint8 *stream, int len)
SDL_UnlockMutex(priv->buffer_mutex);
}
-static void uninit(struct ao *ao, bool cut_audio)
+static void uninit(struct ao *ao)
{
struct priv *priv = ao->priv;
if (!priv)
@@ -134,14 +134,14 @@ static int init(struct ao *ao)
if (SDL_InitSubSystem(SDL_INIT_AUDIO)) {
if (!ao->probing)
MP_ERR(ao, "SDL_Init failed\n");
- uninit(ao, true);
+ uninit(ao);
return -1;
}
struct mp_chmap_sel sel = {0};
mp_chmap_sel_add_waveext_def(&sel);
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) {
- uninit(ao, true);
+ uninit(ao);
return -1;
}
@@ -186,7 +186,7 @@ static int init(struct ao *ao)
if (SDL_OpenAudio(&desired, &obtained)) {
if (!ao->probing)
MP_ERR(ao, "could not open audio: %s\n", SDL_GetError());
- uninit(ao, true);
+ uninit(ao);
return -1;
}
@@ -217,12 +217,12 @@ static int init(struct ao *ao)
default:
if (!ao->probing)
MP_ERR(ao, "could not find matching format\n");
- uninit(ao, true);
+ uninit(ao);
return -1;
}
if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, obtained.channels)) {
- uninit(ao, true);
+ uninit(ao);
return -1;
}
@@ -231,13 +231,13 @@ static int init(struct ao *ao)
priv->buffer_mutex = SDL_CreateMutex();
if (!priv->buffer_mutex) {
MP_ERR(ao, "SDL_CreateMutex failed\n");
- uninit(ao, true);
+ uninit(ao);
return -1;
}
priv->underrun_cond = SDL_CreateCond();
if (!priv->underrun_cond) {
MP_ERR(ao, "SDL_CreateCond failed\n");
- uninit(ao, true);
+ uninit(ao);
return -1;
}
diff --git a/audio/out/ao_sndio.c b/audio/out/ao_sndio.c
index c200b906ab..286f158260 100644
--- a/audio/out/ao_sndio.c
+++ b/audio/out/ao_sndio.c
@@ -216,7 +216,7 @@ error:
/*
* close device
*/
-static void uninit(struct ao *ao, bool immed)
+static void uninit(struct ao *ao)
{
struct priv *p = ao->priv;
diff --git a/audio/out/ao_wasapi.c b/audio/out/ao_wasapi.c
index bb00849adb..a3da9fe388 100644
--- a/audio/out/ao_wasapi.c
+++ b/audio/out/ao_wasapi.c
@@ -1218,11 +1218,11 @@ static int setup_buffers(struct wasapi_state *state)
return !state->ringbuff;
}
-static void uninit(struct ao *ao, bool block)
+static void uninit(struct ao *ao)
{
MP_VERBOSE(ao, "uninit!\n");
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
- state->immed = !block;
+ state->immed = 1;
SetEvent(state->hUninit);
/* wait up to 10 seconds */
if (WaitForSingleObject(state->threadLoop, 10000) == WAIT_TIMEOUT)
diff --git a/audio/out/internal.h b/audio/out/internal.h
index 98de7e08b3..c14cdf3c68 100644
--- a/audio/out/internal.h
+++ b/audio/out/internal.h
@@ -100,13 +100,14 @@ struct ao_driver {
int (*init)(struct ao *ao);
// Optional. See ao_control() etc. in ao.c
int (*control)(struct ao *ao, enum aocontrol cmd, void *arg);
- void (*uninit)(struct ao *ao, bool cut_audio);
+ void (*uninit)(struct ao *ao);
void (*reset)(struct ao*ao);
int (*get_space)(struct ao *ao);
int (*play)(struct ao *ao, void **data, int samples, int flags);
float (*get_delay)(struct ao *ao);
void (*pause)(struct ao *ao);
void (*resume)(struct ao *ao);
+ void (*drain)(struct ao *ao);
// For option parsing (see vo.h)
int priv_size;
diff --git a/audio/out/pull.c b/audio/out/pull.c
index a41ac714ba..01e581925d 100644
--- a/audio/out/pull.c
+++ b/audio/out/pull.c
@@ -190,9 +190,9 @@ static void resume(struct ao *ao)
ao->driver->resume(ao);
}
-static void uninit(struct ao *ao, bool cut_audio)
+static void uninit(struct ao *ao)
{
- ao->driver->uninit(ao, cut_audio);
+ ao->driver->uninit(ao);
}
static int init(struct ao *ao)
diff --git a/audio/out/push.c b/audio/out/push.c
index de093988bd..f0608dd3a4 100644
--- a/audio/out/push.c
+++ b/audio/out/push.c
@@ -106,6 +106,17 @@ static void resume(struct ao *ao)
pthread_mutex_unlock(&p->lock);
}
+static void drain(struct ao *ao)
+{
+ if (ao->driver->drain) {
+ struct ao_push_state *p = ao->api_priv;
+ pthread_mutex_lock(&p->lock);
+ ao->driver->drain(ao);
+ pthread_mutex_unlock(&p->lock);
+ } else {
+ ao_wait_drain(ao);
+ }
+}
static int get_space(struct ao *ao)
{
@@ -202,7 +213,7 @@ static void *playthread(void *arg)
return NULL;
}
-static void uninit(struct ao *ao, bool cut_audio)
+static void uninit(struct ao *ao)
{
struct ao_push_state *p = ao->api_priv;
@@ -213,7 +224,7 @@ static void uninit(struct ao *ao, bool cut_audio)
pthread_join(p->thread, NULL);
- ao->driver->uninit(ao, cut_audio);
+ ao->driver->uninit(ao);
pthread_cond_destroy(&p->wakeup);
pthread_mutex_destroy(&p->lock);
@@ -231,7 +242,7 @@ static int init(struct ao *ao)
&ao->channels, ao->samplerate);
mp_audio_buffer_preallocate_min(p->buffer, ao->buffer);
if (pthread_create(&p->thread, NULL, playthread, ao)) {
- ao->driver->uninit(ao, true);
+ ao->driver->uninit(ao);
return -1;
}
return 0;
@@ -247,6 +258,7 @@ const struct ao_driver ao_api_push = {
.get_delay = get_delay,
.pause = pause,
.resume = resume,
+ .drain = drain,
.priv_size = sizeof(struct ao_push_state),
};
diff --git a/player/loadfile.c b/player/loadfile.c
index 657c950f32..89f893cc25 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -173,10 +173,8 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask)
struct ao *ao = mpctx->ao;
mpctx->initialized_flags &= ~INITIALIZED_AO;
if (ao) {
- bool drain = false;
// Note: with gapless_audio, stop_play is not correctly set
if (opts->gapless_audio || mpctx->stop_play == AT_END_OF_FILE) {
- drain = true;
struct mp_audio data;
mp_audio_buffer_peek(mpctx->ao_buffer, &data);
int samples = mpctx->ao_buffer_playable_samples;
@@ -187,8 +185,9 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask)
if (played < samples)
MP_WARN(mpctx, "Audio output truncated at end.\n");
}
+ ao_drain(ao);
}
- ao_uninit(ao, drain);
+ ao_uninit(ao);
}
mpctx->ao = NULL;
}