From a477481aabaa16f1ed15af456125160bc8face5a Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 9 Mar 2014 00:04:37 +0100 Subject: 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. --- audio/out/internal.h | 59 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 3 deletions(-) (limited to 'audio/out/internal.h') diff --git a/audio/out/internal.h b/audio/out/internal.h index e996f8afeb..98de7e08b3 100644 --- a/audio/out/internal.h +++ b/audio/out/internal.h @@ -19,25 +19,74 @@ #ifndef MP_AO_INTERNAL_H_ #define MP_AO_INTERNAL_H_ +#include + +#include "audio/chmap.h" +#include "audio/chmap_sel.h" + /* global data used by ao.c and ao drivers */ struct ao { int samplerate; struct mp_chmap channels; int format; // one of AF_FORMAT_... - int bps; // bytes per second + int bps; // bytes per second (per plane) int sstride; // size of a sample on each plane // (format_size*num_channels/num_planes) + int num_planes; bool probing; // if true, don't fail loudly on init bool untimed; // don't assume realtime playback bool no_persistent_volume; // the AO does the equivalent of af_volume bool per_application_mixer; // like above, but volume persists (per app) + int device_buffer; // device buffer in samples (guessed by + // common init code if not set by driver) + const struct ao_driver *api; // entrypoints to the wrapper (push.c/pull.c) const struct ao_driver *driver; void *priv; struct encode_lavc_context *encode_lavc_ctx; struct input_ctx *input_ctx; struct mp_log *log; // Using e.g. "[ao/coreaudio]" as prefix + + int buffer; + void *api_priv; }; +extern const struct ao_driver ao_api_push; +extern const struct ao_driver ao_api_pull; + + +/* Note: + * + * In general, there are two types of audio drivers: + * a) push based (the user queues data that should be played) + * b) pull callback based (the audio API calls a callback to get audio) + * + * The ao.c code can handle both. It basically implements two audio paths + * and provides a uniform API for them. If ao_driver->play is NULL, it assumes + * that the driver uses a callback based audio API, otherwise push based. + * + * Requirements: + * a) Most functions (except ->control) must be provided. ->play is called to + * queue audio. ao.c creates a thread to regularly refill audio device + * buffers with ->play, but all driver functions are always called under + * an exclusive lock. + * Mandatory: + * init + * uninit + * reset + * get_space + * play + * get_delay + * pause + * resume + * b) ->play must be NULL. The driver can start the audio API in init(). The + * audio API in turn will start a thread and call a callback provided by the + * driver. That callback calls ao_read_data() to get audio data. Most + * functions are optional and will be emulated if missing (e.g. pausing + * is emulated as silence). ->get_delay and ->get_space are never called. + * Mandatory: + * init + * uninit + */ struct ao_driver { // If true, use with encoding only. bool encode; @@ -49,7 +98,7 @@ struct ao_driver { // device doesn't accept these parameters, you can attempt to negotiate // fallback parameters, and set the ao format fields accordingly. int (*init)(struct ao *ao); - // See ao_control() etc. in ao.c + // 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 (*reset)(struct ao*ao); @@ -65,8 +114,12 @@ struct ao_driver { const struct m_option *options; }; -// These functions can be called by AOs. They don't lock the AO. +// These functions can be called by AOs. + int ao_play_silence(struct ao *ao, int samples); +void ao_wait_drain(struct ao *ao); +int ao_read_data(struct ao *ao, void **data, int samples, int64_t out_time_us); + bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s, struct mp_chmap *map); bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s, -- cgit v1.2.3