audio/out: prepare for non-interleaved audio

This comes with two internal AO API changes:

1. ao_driver.play now can take non-interleaved audio. For this purpose,
the data pointer is changed to void **data, where data[0] corresponds to
the pointer in the old API. Also, the len argument as well as the return
value are now in samples, not bytes. "Sample" in this context means the
unit of the smallest possible audio frame, i.e. sample_size * channels.

2. ao_driver.get_space now returns samples instead of bytes. (Similar to
the play function.)

Change all AOs to use the new API.

The AO API as exposed to the rest of the player still uses the old API.
It's emulated in ao.c. This is purely to split the commits changing all
AOs and the commits adding actual support for outputting N-I audio.
This commit is contained in:
wm4 2013-11-10 23:24:21 +01:00
parent d115fb3b0e
commit 380fc765e4
17 changed files with 92 additions and 85 deletions

View File

@ -159,7 +159,10 @@ static struct ao *ao_create(bool probing, struct mpv_global *global,
talloc_free(chmap);
if (ao->driver->init(ao) < 0)
goto error;
ao->bps = ao->channels.num * ao->samplerate * af_fmt2bits(ao->format) / 8;
ao->sstride = af_fmt2bits(ao->format) / 8;
if (!af_fmt_is_planar(ao->format))
ao->sstride *= ao->channels.num;
ao->bps = ao->samplerate * ao->sstride;
return ao;
error:
talloc_free(ao);
@ -208,7 +211,8 @@ void ao_uninit(struct ao *ao, bool cut_audio)
int ao_play(struct ao *ao, void *data, int len, int flags)
{
return ao->driver->play(ao, data, len, flags);
int r = ao->driver->play(ao, &data, len / ao->sstride, flags);
return r < 0 ? r : r * ao->sstride;
}
int ao_control(struct ao *ao, enum aocontrol cmd, void *arg)
@ -229,7 +233,7 @@ double ao_get_delay(struct ao *ao)
int ao_get_space(struct ao *ao)
{
return ao->driver->get_space(ao);
return ao->driver->get_space(ao) * ao->sstride;
}
void ao_reset(struct ao *ao)
@ -254,10 +258,9 @@ int ao_play_silence(struct ao *ao, int samples)
{
if (samples <= 0 || AF_FORMAT_IS_SPECIAL(ao->format))
return 0;
int s = ao->channels.num * (af_fmt2bits(ao->format) / 8);
char *p = talloc_size(NULL, samples * s);
af_fill_silence(p, samples * s, ao->format);
int r = ao_play(ao, p, samples * s, 0);
char *p = talloc_size(NULL, samples * ao->sstride);
af_fill_silence(p, samples * ao->sstride, ao->format);
int r = ao_play(ao, p, samples * ao->sstride, 0);
talloc_free(p);
return r;
}

View File

@ -56,7 +56,7 @@ struct ao_driver {
void (*uninit)(struct ao *ao, bool cut_audio);
void (*reset)(struct ao*ao);
int (*get_space)(struct ao *ao);
int (*play)(struct ao *ao, void *data, int len, int flags);
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);
@ -73,6 +73,8 @@ struct ao {
struct mp_chmap channels;
int format;
int bps; // bytes per second
int sstride; // size of a sample on each plane
// (format_size*num_channels/num_planes)
double pts; // some mplayer.c state (why is this here?)
struct bstr buffer;
int buffer_playable_size; // part of the part of the buffer the AO hasn't

View File

@ -636,18 +636,12 @@ static void reset(struct ao *ao)
alsa_error: ;
}
/*
plays 'len' bytes of 'data'
returns: number of bytes played
modified last at 29.06.02 by jp
thanxs for marius <marius@rospot.com> for giving us the light ;)
*/
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *p = ao->priv;
int num_frames;
snd_pcm_sframes_t res = 0;
int len = samples * p->bytes_per_sample;
if (!(flags & AOPLAY_FINAL_CHUNK))
len = len / p->outburst * p->outburst;
num_frames = len / p->bytes_per_sample;
@ -661,7 +655,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
return 0;
do {
res = snd_pcm_writei(p->alsa, data, num_frames);
res = snd_pcm_writei(p->alsa, data[0], num_frames);
if (res == -EINTR) {
/* nothing to do */
@ -680,7 +674,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
}
} while (res == 0);
return res < 0 ? -1 : res * p->bytes_per_sample;
return res < 0 ? -1 : res;
alsa_error:
return -1;
@ -701,7 +695,7 @@ static int get_space(struct ao *ao)
unsigned space = snd_pcm_status_get_avail(status) * p->bytes_per_sample;
if (space > p->buffersize) // Buffer underrun?
space = p->buffersize;
return space;
return space / p->bytes_per_sample;
alsa_error:
return 0;

View File

@ -578,10 +578,12 @@ coreaudio_error:
return CONTROL_ERROR;
}
static int play(struct ao *ao, void *output_samples, int num_bytes, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *p = ao->priv;
struct priv_d *d = p->digital;
void *output_samples = data[0];
int num_bytes = samples * ao->sstride;
// Check whether we need to reset the digital output stream.
if (p->is_digital && d->stream_asbd_changed) {
@ -599,7 +601,7 @@ static int play(struct ao *ao, void *output_samples, int num_bytes, int flags)
int wrote = mp_ring_write(p->buffer, output_samples, num_bytes);
audio_resume(ao);
return wrote;
return wrote / ao->sstride;
}
static void reset(struct ao *ao)
@ -612,7 +614,7 @@ static void reset(struct ao *ao)
static int get_space(struct ao *ao)
{
struct priv *p = ao->priv;
return mp_ring_available(p->buffer);
return mp_ring_available(p->buffer) / ao->sstride;
}
static float get_delay(struct ao *ao)

View File

@ -596,7 +596,7 @@ static int get_space(struct ao *ao)
int space = check_free_buffer_size(ao);
if (space < p->min_free_space)
return 0;
return space - p->min_free_space;
return (space - p->min_free_space) / ao->sstride;
}
/**
@ -606,9 +606,10 @@ static int get_space(struct ao *ao)
\param flags currently unused
\return number of played bytes
*/
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *p = ao->priv;
int len = samples * ao->sstride;
int space = check_free_buffer_size(ao);
if (space < len)
@ -616,7 +617,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
if (!(flags & AOPLAY_FINAL_CHUNK))
len = (len / p->outburst) * p->outburst;
return write_buffer(ao, data, len);
return write_buffer(ao, data[0], len) / ao->sstride;
}
/**

View File

@ -319,19 +319,20 @@ static void audio_resume(struct ao *ao)
static int get_space(struct ao *ao)
{
struct priv *p = ao->priv;
return mp_ring_available(p->ring);
return mp_ring_available(p->ring) / ao->sstride;
}
/**
* \brief write data into buffer and reset underrun flag
*/
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *p = ao->priv;
int len = samples * ao->sstride;
if (!(flags & AOPLAY_FINAL_CHUNK))
len -= len % p->outburst;
p->underrun = 0;
return mp_ring_write(p->ring, data, len);
return mp_ring_write(p->ring, data[0], len) / ao->sstride;
}
#define OPT_BASE_STRUCT struct priv

View File

@ -296,8 +296,6 @@ static void fill_with_padding(void *buf, int cnt, int sz, const void *padding)
}
// close audio device
static int encode(struct ao *ao, double apts, void *data);
static int play(struct ao *ao, void *data, int len, int flags);
static void uninit(struct ao *ao, bool cut_audio)
{
struct encode_lavc_context *ectx = ao->encode_lavc_ctx;
@ -315,7 +313,7 @@ static int get_space(struct ao *ao)
{
struct priv *ac = ao->priv;
return ac->aframesize * ac->sample_size * ao->channels.num * ac->framecount;
return ac->aframesize * ac->framecount;
}
// must get exactly ac->aframesize amount of data
@ -444,10 +442,10 @@ static int encode(struct ao *ao, double apts, void *data)
return packet.size;
}
// plays 'len' bytes of 'data'
// plays 'samples' samples of 'ni_data[0]'
// it should round it down to frame sizes
// return: number of bytes played
static int play(struct ao *ao, void *data, int len, int flags)
// return: number of samples played
static int play(struct ao *ao, void **ni_data, int samples, int flags)
{
struct priv *ac = ao->priv;
struct encode_lavc_context *ectx = ao->encode_lavc_ctx;
@ -457,6 +455,8 @@ static int play(struct ao *ao, void *data, int len, int flags)
double nextpts;
double pts = ao->pts;
double outpts;
void *data = ni_data[0];
int len = samples * ao->sstride;
int bytelen = len;
len /= ac->sample_size * ao->channels.num;
@ -477,8 +477,9 @@ static int play(struct ao *ao, void *data, int len, int flags)
extralen / ac->sample_size,
ac->sample_size, ac->sample_padding);
// No danger of recursion, because AOPLAY_FINAL_CHUNK not set
written = play(ao, paddingbuf, bytelen + extralen, 0);
if (written < bytelen) {
written =
play(ao, &paddingbuf, (bytelen + extralen) / ao->sstride, 0);
if (written * ao->sstride < bytelen) {
MP_ERR(ao, "did not write enough data at the end\n");
}
talloc_free(paddingbuf);
@ -492,7 +493,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
while (encode(ao, outpts, NULL) > 0) ;
return FFMIN(written, bytelen);
return (FFMIN(written, bytelen)) / ao->sstride;
}
if (pts == MP_NOPTS_VALUE) {
@ -559,7 +560,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
if (ac->offset_left <= -len) {
// skip whole frame
ac->offset_left += len;
return len * ac->sample_size * ao->channels.num;
return len;
} else {
// skip part of this frame, buffer/encode the rest
bufpos -= ac->offset_left;
@ -632,7 +633,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
ectx->next_in_pts = nextpts;
}
return bufpos * ac->sample_size * ao->channels.num;
return bufpos;
}
const struct ao_driver audio_out_lavc = {

View File

@ -96,18 +96,19 @@ static int get_space(struct ao *ao)
struct priv *priv = ao->priv;
drain(ao);
return priv->buffersize - priv->buffered_bytes;
return (priv->buffersize - priv->buffered_bytes) / ao->sstride;
}
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *priv = ao->priv;
int len = samples * ao->sstride;
int maxbursts = (priv->buffersize - priv->buffered_bytes) / priv->outburst;
int playbursts = len / priv->outburst;
int bursts = playbursts > maxbursts ? maxbursts : playbursts;
priv->buffered_bytes += bursts * priv->outburst;
return bursts * priv->outburst;
return (bursts * priv->outburst) / ao->sstride;
}
static float get_delay(struct ao *ao)

View File

@ -251,18 +251,19 @@ static int get_space(struct ao *ao)
queued = NUM_BUF - queued - 3;
if (queued < 0)
return 0;
return queued * CHUNK_SIZE * ao->channels.num;
return (queued * CHUNK_SIZE * ao->channels.num) / ao->sstride;
}
/**
* \brief write data into buffer and reset underrun flag
*/
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
ALint state;
int i, j, k;
int ch;
int16_t *d = data;
int16_t *d = data[0];
int len = samples * ao->sstride;
len /= ao->channels.num * CHUNK_SIZE;
for (i = 0; i < len; i++) {
for (ch = 0; ch < ao->channels.num; ch++) {
@ -278,7 +279,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
alGetSourcei(sources[0], AL_SOURCE_STATE, &state);
if (state != AL_PLAYING) // checked here in case of an underrun
alSourcePlayv(ao->channels.num, sources);
return len * ao->channels.num * CHUNK_SIZE;
return len * ao->channels.num * CHUNK_SIZE / ao->sstride;
}
static float get_delay(struct ao *ao)

View File

@ -457,7 +457,7 @@ static int get_space(struct ao *ao)
if (ioctl(p->audio_fd, SNDCTL_DSP_GETOSPACE, &p->zz) != -1) {
// calculate exact buffer space:
playsize = p->zz.fragments * p->zz.fragsize;
return playsize;
return playsize / ao->sstride;
}
#endif
@ -475,14 +475,14 @@ static int get_space(struct ao *ao)
}
#endif
return p->outburst;
return p->outburst / ao->sstride;
}
// stop playing, keep buffers (for pause)
static void audio_pause(struct ao *ao)
{
struct priv *p = ao->priv;
p->prepause_space = get_space(ao);
p->prepause_space = get_space(ao) * ao->sstride;
#ifdef SNDCTL_DSP_RESET
ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL);
#else
@ -493,17 +493,18 @@ static void audio_pause(struct ao *ao)
// plays 'len' bytes of 'data'
// it should round it down to outburst*n
// return: number of bytes played
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *p = ao->priv;
int len = samples * ao->sstride;
if (len == 0)
return len;
if (len > p->outburst || !(flags & AOPLAY_FINAL_CHUNK)) {
len /= p->outburst;
len *= p->outburst;
}
len = write(p->audio_fd, data, len);
return len;
len = write(p->audio_fd, data[0], len);
return len / ao->sstride;
}
// resume playing, after audio_pause()
@ -513,8 +514,7 @@ static void audio_resume(struct ao *ao)
#ifndef SNDCTL_DSP_RESET
reset(ao);
#endif
int fillframes = (get_space(ao) - p->prepause_space) /
(af_fmt2bits(ao->format) / 8 * ao->channels.num);
int fillframes = get_space(ao) - p->prepause_space / ao->sstride;
if (fillframes > 0)
ao_play_silence(ao, fillframes);
}

View File

@ -196,13 +196,14 @@ static int get_space(struct ao *ao)
return 65536;
}
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *priv = ao->priv;
int len = samples * ao->sstride;
fwrite(data, len, 1, priv->fp);
fwrite(data[0], len, 1, priv->fp);
priv->data_length += len;
return len;
return len / ao->sstride;
}
#define OPT_BASE_STRUCT struct priv

View File

@ -280,13 +280,13 @@ error_exit:
return -1;
}
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *priv = ao->priv;
pthread_mutex_lock(&priv->ring_mutex);
int write_len = mp_ring_write(priv->ring, data, len);
int write_len = mp_ring_write(priv->ring, data[0], samples * ao->sstride);
if (flags & AOPLAY_FINAL_CHUNK)
priv->play_remaining = true;
@ -295,7 +295,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
if (Pa_IsStreamStopped(priv->stream) == 1)
check_pa_ret(Pa_StartStream(priv->stream));
return write_len;
return write_len / ao->sstride;
}
static int get_space(struct ao *ao)
@ -308,7 +308,7 @@ static int get_space(struct ao *ao)
pthread_mutex_unlock(&priv->ring_mutex);
return free;
return free / ao->sstride;
}
static float get_delay(struct ao *ao)

View File

@ -377,14 +377,14 @@ static void cork(struct ao *ao, bool pause)
}
// Play the specified data to the pulseaudio server
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *priv = ao->priv;
pa_threaded_mainloop_lock(priv->mainloop);
if (pa_stream_write(priv->stream, data, len, NULL, 0,
if (pa_stream_write(priv->stream, data[0], samples * ao->sstride, NULL, 0,
PA_SEEK_RELATIVE) < 0) {
GENERIC_ERR_MSG("pa_stream_write() failed");
len = -1;
samples = -1;
}
if (flags & AOPLAY_FINAL_CHUNK) {
// Force start in case the stream was too short for prebuf
@ -392,7 +392,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
pa_operation_unref(op);
}
pa_threaded_mainloop_unlock(priv->mainloop);
return len;
return samples;
}
// Reset the audio stream, i.e. flush the playback buffer on the server side
@ -427,14 +427,14 @@ static void resume(struct ao *ao)
cork(ao, false);
}
// Return number of bytes that may be written to the server without blocking
// Return number of samples that may be written to the server without blocking
static int get_space(struct ao *ao)
{
struct priv *priv = ao->priv;
pa_threaded_mainloop_lock(priv->mainloop);
size_t space = pa_stream_writable_size(priv->stream);
pa_threaded_mainloop_unlock(priv->mainloop);
return space;
return space / ao->sstride;
}
// Return the current latency in seconds

View File

@ -163,13 +163,13 @@ static void audio_resume(struct ao *ao)
static int get_space(struct ao *ao)
{
struct priv *priv = ao->priv;
return rsd_get_avail(priv->rd);
return rsd_get_avail(priv->rd) / ao->sstride;
}
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *priv = ao->priv;
return rsd_write(priv->rd, data, len);
return rsd_write(priv->rd, data[0], samples * ao->sstride) / ao->sstride;
}
static float get_delay(struct ao *ao)

View File

@ -261,7 +261,7 @@ static int get_space(struct ao *ao)
SDL_LockMutex(priv->buffer_mutex);
int space = av_fifo_space(priv->buffer);
SDL_UnlockMutex(priv->buffer_mutex);
return space;
return space / ao->sstride;
}
static void pause(struct ao *ao)
@ -292,20 +292,21 @@ static void resume(struct ao *ao)
do_resume(ao);
}
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *priv = ao->priv;
int len = samples * ao->sstride;
SDL_LockMutex(priv->buffer_mutex);
int free = av_fifo_space(priv->buffer);
if (len > free) len = free;
av_fifo_generic_write(priv->buffer, data, len, NULL);
av_fifo_generic_write(priv->buffer, data[0], len, NULL);
SDL_CondSignal(priv->underrun_cond);
SDL_UnlockMutex(priv->buffer_mutex);
if (priv->unpause) {
priv->unpause = 0;
do_resume(ao);
}
return len;
return len / ao->sstride;
}
static float get_delay(struct ao *ao)

View File

@ -242,16 +242,16 @@ static void reset(struct ao *ao)
/*
* play given number of bytes until sio_write() blocks
*/
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *p = ao->priv;
int n;
n = sio_write(p->hdl, data, len);
n = sio_write(p->hdl, data[0], samples * ao->sstride);
p->delay += n;
if (flags & AOPLAY_FINAL_CHUNK)
reset(ao);
return n;
return n / ao->sstride;
}
/*
@ -271,7 +271,7 @@ static int get_space(struct ao *ao)
; /* nothing */
sio_revents(p->hdl, p->pfd);
return p->par.bufsz * p->par.pchan * p->par.bps - p->delay;
return (p->par.bufsz * p->par.pchan * p->par.bps - p->delay) / ao->sstride;
}
/*

View File

@ -1197,7 +1197,7 @@ static int get_space(struct ao *ao)
if (!ao || !ao->priv)
return -1;
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
return mp_ring_available(state->ringbuff);
return mp_ring_available(state->ringbuff) / ao->sstride;
}
static void reset_buffers(struct wasapi_state *state)
@ -1339,25 +1339,24 @@ static void reset(struct ao *ao)
reset_buffers(state);
}
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
int ret = 0;
if (!ao || !ao->priv)
return ret;
return 0;
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
if (WaitForSingleObject(state->fatal_error, 0) == WAIT_OBJECT_0) {
/* something bad happened */
return ret;
return 0;
}
ret = mp_ring_write(state->ringbuff, data, len);
int ret = mp_ring_write(state->ringbuff, data[0], samples * ao->sstride);
if (!state->is_playing) {
/* start playing */
state->is_playing = 1;
SetEvent(state->hPlay);
}
return ret;
return ret / ao->sstride;
}
static float get_delay(struct ao *ao)