mirror of https://github.com/mpv-player/mpv
audio/out: require AO drivers to report period size and correct buffer
Before this change, AOs could have internal alignment, and play() would not consume the trailing data if the size passed to it is not aligned. Change this to require AOs to report their alignment (via period_size), and make sure to always send aligned data. The buffer reported by get_space() now always has to be correct and reliable. If play() does not consume all data provided (which is bounded by get_space()), an error is printed. This is preparation for potential further AO changes. I casually checked alsa/lavc/null/pcm, the other AOs might or might not work.
This commit is contained in:
parent
72ef74dab5
commit
037c37519b
|
@ -194,6 +194,8 @@ static struct ao *ao_init(bool probing, struct mpv_global *global,
|
|||
|
||||
ao->stream_silence = flags & AO_INIT_STREAM_SILENCE;
|
||||
|
||||
ao->period_size = 1;
|
||||
|
||||
int r = ao->driver->init(ao);
|
||||
if (r < 0) {
|
||||
// Silly exception for coreaudio spdif redirection
|
||||
|
@ -209,6 +211,11 @@ static struct ao *ao_init(bool probing, struct mpv_global *global,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (ao->period_size < 1) {
|
||||
MP_ERR(ao, "Invalid period size set.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ao->sstride = af_fmt_to_bytes(ao->format);
|
||||
ao->num_planes = 1;
|
||||
if (af_fmt_is_planar(ao->format)) {
|
||||
|
|
|
@ -840,6 +840,7 @@ static int init_device(struct ao *ao, int mode)
|
|||
MP_VERBOSE(ao, "period size: %d samples\n", (int)p->outburst);
|
||||
|
||||
ao->device_buffer = p->buffersize;
|
||||
ao->period_size = p->outburst;
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -160,6 +160,8 @@ static int init(struct ao *ao)
|
|||
|
||||
ao->untimed = true;
|
||||
|
||||
ao->period_size = ac->aframesize * ac->framecount;
|
||||
|
||||
if (ao->channels.num > AV_NUM_DATA_POINTERS)
|
||||
goto fail;
|
||||
|
||||
|
@ -203,7 +205,7 @@ static void uninit(struct ao *ao)
|
|||
ac->shutdown = true;
|
||||
}
|
||||
|
||||
// return: how many bytes can be played without blocking
|
||||
// return: how many samples can be played without blocking
|
||||
static int get_space(struct ao *ao)
|
||||
{
|
||||
struct priv *ac = ao->priv;
|
||||
|
|
|
@ -108,6 +108,8 @@ static int init(struct ao *ao)
|
|||
|
||||
priv->last_time = mp_time_sec();
|
||||
|
||||
ao->period_size = priv->outburst;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -214,6 +214,7 @@ static int init(struct ao *ao)
|
|||
}
|
||||
|
||||
p->chunk_size = CHUNK_SAMPLES * af_fmt_to_bytes(ao->format);
|
||||
ao->period_size = CHUNK_SAMPLES;
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
|
|
|
@ -394,7 +394,8 @@ static int reopen_device(struct ao *ao, bool allow_format_changes)
|
|||
}
|
||||
}
|
||||
|
||||
p->outburst -= p->outburst % (channels.num * af_fmt_to_bytes(format)); // round down
|
||||
ao->period_size = channels.num * af_fmt_to_bytes(format);
|
||||
p->outburst -= p->outburst % ao->period_size; // round down
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -195,6 +195,8 @@ static int init(struct ao *ao)
|
|||
if (!p->pfd)
|
||||
goto error;
|
||||
|
||||
ao->period_size = p->par.round;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
|
|
|
@ -48,6 +48,14 @@ struct ao {
|
|||
int init_flags; // AO_INIT_* flags
|
||||
bool stream_silence; // if audio inactive, just play silence
|
||||
|
||||
// Set by the driver on init. This is typically the period size, and the
|
||||
// smallest unit the driver will accept in one piece (although if
|
||||
// AOPLAY_FINAL_CHUNK is set, the driver must accept everything).
|
||||
// This value is in complete samples (i.e. 1 for stereo means 1 sample
|
||||
// for both channels each).
|
||||
// Used for push based API only.
|
||||
int period_size;
|
||||
|
||||
// The device as selected by the user, usually using ao_device_desc.name
|
||||
// from an entry from the list returned by driver->list_devices. If the
|
||||
// default device should be used, this is set to NULL.
|
||||
|
|
|
@ -285,6 +285,8 @@ static void ao_play_data(struct ao *ao)
|
|||
int space = ao->driver->get_space(ao);
|
||||
bool play_silence = p->paused || (ao->stream_silence && !p->still_playing);
|
||||
space = MPMAX(space, 0);
|
||||
if (space % ao->period_size)
|
||||
MP_ERR(ao, "Audio device reports unaligned available buffer size.\n");
|
||||
struct mp_audio data;
|
||||
if (play_silence) {
|
||||
ao_get_silence(ao, &data, space);
|
||||
|
@ -295,16 +297,25 @@ static void ao_play_data(struct ao *ao)
|
|||
if (data.samples > space)
|
||||
data.samples = space;
|
||||
int flags = 0;
|
||||
if (p->final_chunk && data.samples == max)
|
||||
if (p->final_chunk && data.samples == max) {
|
||||
flags |= AOPLAY_FINAL_CHUNK;
|
||||
} else {
|
||||
data.samples = data.samples / ao->period_size * ao->period_size;
|
||||
}
|
||||
MP_STATS(ao, "start ao fill");
|
||||
int r = 0;
|
||||
if (data.samples)
|
||||
r = ao->driver->play(ao, data.planes, data.samples, flags);
|
||||
MP_STATS(ao, "end ao fill");
|
||||
if (r > data.samples) {
|
||||
MP_WARN(ao, "Audio device returned non-sense value.\n");
|
||||
MP_ERR(ao, "Audio device returned non-sense value.\n");
|
||||
r = data.samples;
|
||||
} else if (r < 0) {
|
||||
MP_ERR(ao, "Error writing audio to device.\n");
|
||||
} else if (r != data.samples) {
|
||||
MP_ERR(ao, "Audio device returned broken buffer state (sent %d samples, "
|
||||
"got %d samples, %d period%s)!\n", data.samples, r,
|
||||
ao->period_size, flags & AOPLAY_FINAL_CHUNK ? " final" : "");
|
||||
}
|
||||
r = MPMAX(r, 0);
|
||||
// Probably can't copy the rest of the buffer due to period alignment.
|
||||
|
|
Loading…
Reference in New Issue