summaryrefslogtreecommitdiffstats
path: root/audio/out/ao.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2014-03-09 00:04:37 +0100
committerwm4 <wm4@nowhere>2014-03-09 01:27:41 +0100
commita477481aabaa16f1ed15af456125160bc8face5a (patch)
treec99949f3e13e7c7ba498c56edcbcfa433e487cd3 /audio/out/ao.c
parent5ffd6a9e9b7a0d894d7513ad20c24c2727426ecd (diff)
downloadmpv-a477481aabaa16f1ed15af456125160bc8face5a.tar.bz2
mpv-a477481aabaa16f1ed15af456125160bc8face5a.tar.xz
audio/out: feed AOs from a separate thread
This has 2 goals: - Ensure that AOs have always enough data, even if the device buffers are very small. - Reduce complexity in some AOs, which do their own buffering. One disadvantage is that performance is slightly reduced due to more copying. Implementation-wise, we don't change ao.c much, and instead "redirect" the driver's callback to an API wrapper in push.c. Additionally, we add code for dealing with AOs that have a pull API. These AOs usually do their own buffering (jack, coreaudio, portaudio), and adding a thread is basically a waste. The code in pull.c manages a ringbuffer, and allows callback-based AOs to read data directly.
Diffstat (limited to 'audio/out/ao.c')
-rw-r--r--audio/out/ao.c70
1 files changed, 45 insertions, 25 deletions
diff --git a/audio/out/ao.c b/audio/out/ao.c
index 7a973fef2d..f1e88d2a10 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -31,9 +31,14 @@
#include "options/options.h"
#include "options/m_config.h"
+#include "osdep/timer.h"
#include "common/msg.h"
+#include "common/common.h"
#include "common/global.h"
+// Minimum buffer size in seconds.
+#define MIN_BUFFER 0.2
+
extern const struct ao_driver audio_out_oss;
extern const struct ao_driver audio_out_coreaudio;
extern const struct ao_driver audio_out_rsound;
@@ -156,16 +161,36 @@ static struct ao *ao_create(bool probing, struct mpv_global *global,
if (m_config_set_obj_params(config, args) < 0)
goto error;
ao->priv = config->optstruct;
+
char *chmap = mp_chmap_to_str(&ao->channels);
MP_VERBOSE(ao, "requested format: %d Hz, %s channels, %s\n",
ao->samplerate, chmap, af_fmt_to_str(ao->format));
talloc_free(chmap);
+
+ ao->api = ao->driver->play ? &ao_api_push : &ao_api_pull;
+ ao->api_priv = talloc_zero_size(ao, ao->api->priv_size);
+ assert(!ao->api->priv_defaults && !ao->api->options);
+
if (ao->driver->init(ao) < 0)
goto error;
+
ao->sstride = af_fmt2bits(ao->format) / 8;
- if (!af_fmt_is_planar(ao->format))
+ ao->num_planes = 1;
+ if (af_fmt_is_planar(ao->format)) {
+ ao->num_planes = ao->channels.num;
+ } else {
ao->sstride *= ao->channels.num;
+ }
ao->bps = ao->samplerate * ao->sstride;
+
+ if (!ao->device_buffer && ao->driver->get_space)
+ ao->device_buffer = ao->driver->get_space(ao);
+ ao->buffer = MPMAX(ao->device_buffer, MIN_BUFFER * ao->samplerate);
+ MP_VERBOSE(ao, "using soft-buffer of %d samples.\n", ao->buffer);
+
+ if (ao->api->init(ao) < 0)
+ goto error;
+
return ao;
error:
talloc_free(ao);
@@ -214,7 +239,7 @@ done:
// cut_audio: if false, block until all remaining audio was played.
void ao_uninit(struct ao *ao, bool cut_audio)
{
- ao->driver->uninit(ao, cut_audio);
+ ao->api->uninit(ao, cut_audio);
talloc_free(ao);
}
@@ -227,7 +252,7 @@ void ao_uninit(struct ao *ao, bool cut_audio)
// flags: currently AOPLAY_FINAL_CHUNK can be set
int ao_play(struct ao *ao, void **data, int samples, int flags)
{
- return ao->driver->play(ao, data, samples, flags);
+ return ao->api->play(ao, data, samples, flags);
}
int ao_control(struct ao *ao, enum aocontrol cmd, void *arg)
@@ -238,8 +263,8 @@ int ao_control(struct ao *ao, enum aocontrol cmd, void *arg)
case AOCONTROL_HAS_PER_APP_VOLUME:
return !!ao->per_application_mixer;
default:
- if (ao->driver->control)
- return ao->driver->control(ao, cmd, arg);
+ if (ao->api->control)
+ return ao->api->control(ao, cmd, arg);
}
return CONTROL_UNKNOWN;
}
@@ -251,34 +276,34 @@ int ao_control(struct ao *ao, enum aocontrol cmd, void *arg)
// this correctly.
double ao_get_delay(struct ao *ao)
{
- if (!ao->driver->get_delay) {
+ if (!ao->api->get_delay) {
assert(ao->untimed);
return 0;
}
- return ao->driver->get_delay(ao);
+ return ao->api->get_delay(ao);
}
// Return free size of the internal audio buffer. This controls how much audio
// the core should decode and try to queue with ao_play().
int ao_get_space(struct ao *ao)
{
- return ao->driver->get_space(ao);
+ return ao->api->get_space(ao);
}
// Stop playback and empty buffers. Essentially go back to the state after
// ao->init().
void ao_reset(struct ao *ao)
{
- if (ao->driver->reset)
- ao->driver->reset(ao);
+ if (ao->api->reset)
+ ao->api->reset(ao);
}
// Pause playback. Keep the current buffer. ao_get_delay() must return the
// same value as before pausing.
void ao_pause(struct ao *ao)
{
- if (ao->driver->pause)
- ao->driver->pause(ao);
+ if (ao->api->pause)
+ ao->api->pause(ao);
}
// Resume playback. Play the remaining buffer. If the driver doesn't support
@@ -286,22 +311,17 @@ void ao_pause(struct ao *ao)
// the lost audio.
void ao_resume(struct ao *ao)
{
- if (ao->driver->resume)
- ao->driver->resume(ao);
+ if (ao->api->resume)
+ ao->api->resume(ao);
}
-int ao_play_silence(struct ao *ao, int samples)
+// 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).
+void ao_wait_drain(struct ao *ao)
{
- if (samples <= 0 || AF_FORMAT_IS_SPECIAL(ao->format))
- return 0;
- char *p = talloc_size(NULL, samples * ao->sstride);
- af_fill_silence(p, samples * ao->sstride, ao->format);
- void *tmp[MP_NUM_CHANNELS];
- for (int n = 0; n < MP_NUM_CHANNELS; n++)
- tmp[n] = p;
- int r = ao_play(ao, tmp, samples, 0);
- talloc_free(p);
- return r;
+ // This is probably not entirely accurate, but good enough.
+ mp_sleep_us(ao_get_delay(ao) * 1000000);
}
bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,