mirror of
https://github.com/mpv-player/mpv
synced 2024-12-27 09:32:40 +00:00
0ab3184526
Until recently, ao_lavc and vo_lavc started encoding whenever the core happened to send them data. Since audio and video are not initialized at the same time, and the muxer was not necessarily opened when the first encoder started to produce data, the resulting packets were put into a queue. As soon as the muxer was opened, the queue was flushed. Change this to make the core wait with sending data until all encoders are initialized. This has the advantage that we don't need to queue up the packets.
571 lines
17 KiB
C
571 lines
17 KiB
C
/*
|
|
* This file is part of mpv.
|
|
*
|
|
* mpv is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* mpv is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include <pthread.h>
|
|
#include <inttypes.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
|
|
#include "osdep/io.h"
|
|
|
|
#include "ao.h"
|
|
#include "internal.h"
|
|
#include "audio/format.h"
|
|
|
|
#include "common/msg.h"
|
|
#include "common/common.h"
|
|
|
|
#include "input/input.h"
|
|
|
|
#include "osdep/threads.h"
|
|
#include "osdep/timer.h"
|
|
#include "osdep/atomic.h"
|
|
|
|
#include "audio/audio_buffer.h"
|
|
|
|
struct ao_push_state {
|
|
pthread_t thread;
|
|
pthread_mutex_t lock;
|
|
pthread_cond_t wakeup;
|
|
|
|
// --- protected by lock
|
|
|
|
struct mp_audio_buffer *buffer;
|
|
|
|
uint8_t *silence[MP_NUM_CHANNELS];
|
|
int silence_samples;
|
|
|
|
bool terminate;
|
|
bool wait_on_ao;
|
|
bool still_playing;
|
|
bool need_wakeup;
|
|
bool paused;
|
|
bool initial_unblocked;
|
|
|
|
// Whether the current buffer contains the complete audio.
|
|
bool final_chunk;
|
|
double expected_end_time;
|
|
|
|
int wakeup_pipe[2];
|
|
};
|
|
|
|
// lock must be held
|
|
static void wakeup_playthread(struct ao *ao)
|
|
{
|
|
struct ao_push_state *p = ao->api_priv;
|
|
if (ao->driver->wakeup)
|
|
ao->driver->wakeup(ao);
|
|
p->need_wakeup = true;
|
|
pthread_cond_signal(&p->wakeup);
|
|
}
|
|
|
|
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
|
{
|
|
int r = CONTROL_UNKNOWN;
|
|
if (ao->driver->control) {
|
|
struct ao_push_state *p = ao->api_priv;
|
|
pthread_mutex_lock(&p->lock);
|
|
r = ao->driver->control(ao, cmd, arg);
|
|
pthread_mutex_unlock(&p->lock);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static double unlocked_get_delay(struct ao *ao)
|
|
{
|
|
struct ao_push_state *p = ao->api_priv;
|
|
double driver_delay = 0;
|
|
if (ao->driver->get_delay)
|
|
driver_delay = ao->driver->get_delay(ao);
|
|
return driver_delay + mp_audio_buffer_seconds(p->buffer);
|
|
}
|
|
|
|
static double get_delay(struct ao *ao)
|
|
{
|
|
struct ao_push_state *p = ao->api_priv;
|
|
pthread_mutex_lock(&p->lock);
|
|
double delay = unlocked_get_delay(ao);
|
|
pthread_mutex_unlock(&p->lock);
|
|
return delay;
|
|
}
|
|
|
|
static void reset(struct ao *ao)
|
|
{
|
|
struct ao_push_state *p = ao->api_priv;
|
|
pthread_mutex_lock(&p->lock);
|
|
if (ao->driver->reset)
|
|
ao->driver->reset(ao);
|
|
mp_audio_buffer_clear(p->buffer);
|
|
p->paused = false;
|
|
if (p->still_playing)
|
|
wakeup_playthread(ao);
|
|
p->still_playing = false;
|
|
pthread_mutex_unlock(&p->lock);
|
|
}
|
|
|
|
static void audio_pause(struct ao *ao)
|
|
{
|
|
struct ao_push_state *p = ao->api_priv;
|
|
pthread_mutex_lock(&p->lock);
|
|
if (ao->driver->pause)
|
|
ao->driver->pause(ao);
|
|
p->paused = true;
|
|
wakeup_playthread(ao);
|
|
pthread_mutex_unlock(&p->lock);
|
|
}
|
|
|
|
static void resume(struct ao *ao)
|
|
{
|
|
struct ao_push_state *p = ao->api_priv;
|
|
pthread_mutex_lock(&p->lock);
|
|
if (ao->driver->resume)
|
|
ao->driver->resume(ao);
|
|
p->paused = false;
|
|
p->expected_end_time = 0;
|
|
wakeup_playthread(ao);
|
|
pthread_mutex_unlock(&p->lock);
|
|
}
|
|
|
|
static void drain(struct ao *ao)
|
|
{
|
|
struct ao_push_state *p = ao->api_priv;
|
|
double maxbuffer = ao->buffer / (double)ao->samplerate + 1;
|
|
|
|
MP_VERBOSE(ao, "draining...\n");
|
|
|
|
pthread_mutex_lock(&p->lock);
|
|
if (p->paused)
|
|
goto done;
|
|
|
|
p->final_chunk = true;
|
|
wakeup_playthread(ao);
|
|
|
|
// Wait until everything is done. Since the audio API (especially ALSA)
|
|
// can't be trusted to do this right, and we're hard-blocking here, apply
|
|
// an upper bound timeout.
|
|
struct timespec until = mp_rel_time_to_timespec(maxbuffer);
|
|
while (p->still_playing && mp_audio_buffer_samples(p->buffer) > 0) {
|
|
if (pthread_cond_timedwait(&p->wakeup, &p->lock, &until)) {
|
|
MP_WARN(ao, "Draining is taking too long, aborting.\n");
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (ao->driver->drain) {
|
|
ao->driver->drain(ao);
|
|
} else {
|
|
double time = unlocked_get_delay(ao);
|
|
mp_sleep_us(MPMIN(time, maxbuffer) * 1e6);
|
|
}
|
|
|
|
done:
|
|
pthread_mutex_unlock(&p->lock);
|
|
|
|
reset(ao);
|
|
}
|
|
|
|
static int unlocked_get_space(struct ao *ao)
|
|
{
|
|
struct ao_push_state *p = ao->api_priv;
|
|
int space = mp_audio_buffer_get_write_available(p->buffer);
|
|
if (ao->driver->get_space) {
|
|
int align = af_format_sample_alignment(ao->format);
|
|
// The following code attempts to keep the total buffered audio to
|
|
// ao->buffer in order to improve latency.
|
|
int device_space = ao->driver->get_space(ao);
|
|
int device_buffered = ao->device_buffer - device_space;
|
|
int soft_buffered = mp_audio_buffer_samples(p->buffer);
|
|
// The extra margin helps avoiding too many wakeups if the AO is fully
|
|
// byte based and doesn't do proper chunked processing.
|
|
int min_buffer = ao->buffer + 64;
|
|
int missing = min_buffer - device_buffered - soft_buffered;
|
|
missing = (missing + align - 1) / align * align;
|
|
// But always keep the device's buffer filled as much as we can.
|
|
int device_missing = device_space - soft_buffered;
|
|
missing = MPMAX(missing, device_missing);
|
|
space = MPMIN(space, missing);
|
|
space = MPMAX(0, space);
|
|
}
|
|
return space;
|
|
}
|
|
|
|
static int get_space(struct ao *ao)
|
|
{
|
|
struct ao_push_state *p = ao->api_priv;
|
|
pthread_mutex_lock(&p->lock);
|
|
int space = unlocked_get_space(ao);
|
|
pthread_mutex_unlock(&p->lock);
|
|
return space;
|
|
}
|
|
|
|
static bool get_eof(struct ao *ao)
|
|
{
|
|
struct ao_push_state *p = ao->api_priv;
|
|
pthread_mutex_lock(&p->lock);
|
|
bool eof = !p->still_playing;
|
|
pthread_mutex_unlock(&p->lock);
|
|
return eof;
|
|
}
|
|
|
|
static int play(struct ao *ao, void **data, int samples, int flags)
|
|
{
|
|
struct ao_push_state *p = ao->api_priv;
|
|
|
|
pthread_mutex_lock(&p->lock);
|
|
|
|
int write_samples = mp_audio_buffer_get_write_available(p->buffer);
|
|
write_samples = MPMIN(write_samples, samples);
|
|
|
|
MP_TRACE(ao, "samples=%d flags=%d r=%d\n", samples, flags, write_samples);
|
|
|
|
if (write_samples < samples)
|
|
flags = flags & ~AOPLAY_FINAL_CHUNK;
|
|
bool is_final = flags & AOPLAY_FINAL_CHUNK;
|
|
|
|
mp_audio_buffer_append(p->buffer, data, samples);
|
|
|
|
bool got_data = write_samples > 0 || p->paused || p->final_chunk != is_final;
|
|
|
|
p->final_chunk = is_final;
|
|
p->paused = false;
|
|
if (got_data) {
|
|
p->still_playing = true;
|
|
p->expected_end_time = 0;
|
|
|
|
// If we don't have new data, the decoder thread basically promises it
|
|
// will send new data as soon as it's available.
|
|
wakeup_playthread(ao);
|
|
}
|
|
pthread_mutex_unlock(&p->lock);
|
|
return write_samples;
|
|
}
|
|
|
|
static bool realloc_silence(struct ao *ao, int samples)
|
|
{
|
|
struct ao_push_state *p = ao->api_priv;
|
|
|
|
if (samples <= 0 || !af_fmt_is_pcm(ao->format))
|
|
return false;
|
|
|
|
if (samples > p->silence_samples) {
|
|
talloc_free(p->silence[0]);
|
|
|
|
int bytes = af_fmt_to_bytes(ao->format) * samples * ao->channels.num;
|
|
p->silence[0] = talloc_size(p, bytes);
|
|
for (int n = 1; n < MP_NUM_CHANNELS; n++)
|
|
p->silence[n] = p->silence[0];
|
|
p->silence_samples = samples;
|
|
|
|
af_fill_silence(p->silence[0], bytes, ao->format);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// called locked
|
|
static void ao_play_data(struct ao *ao)
|
|
{
|
|
struct ao_push_state *p = ao->api_priv;
|
|
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");
|
|
uint8_t **planes;
|
|
int samples;
|
|
if (play_silence) {
|
|
planes = p->silence;
|
|
samples = realloc_silence(ao, space) ? space : 0;
|
|
} else {
|
|
mp_audio_buffer_peek(p->buffer, &planes, &samples);
|
|
}
|
|
int max = samples;
|
|
if (samples > space)
|
|
samples = space;
|
|
int flags = 0;
|
|
if (p->final_chunk && samples == max) {
|
|
flags |= AOPLAY_FINAL_CHUNK;
|
|
} else {
|
|
samples = samples / ao->period_size * ao->period_size;
|
|
}
|
|
MP_STATS(ao, "start ao fill");
|
|
ao_post_process_data(ao, (void **)planes, samples);
|
|
int r = 0;
|
|
if (samples)
|
|
r = ao->driver->play(ao, (void **)planes, samples, flags);
|
|
MP_STATS(ao, "end ao fill");
|
|
if (r > samples) {
|
|
MP_ERR(ao, "Audio device returned nonsense value.\n");
|
|
r = samples;
|
|
} else if (r < 0) {
|
|
MP_ERR(ao, "Error writing audio to device.\n");
|
|
} else if (r != samples) {
|
|
MP_ERR(ao, "Audio device returned broken buffer state (sent %d samples, "
|
|
"got %d samples, %d period%s)!\n", 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.
|
|
bool stuck_eof = r <= 0 && space >= max && samples > 0;
|
|
if ((flags & AOPLAY_FINAL_CHUNK) && stuck_eof) {
|
|
MP_ERR(ao, "Audio output driver seems to ignore AOPLAY_FINAL_CHUNK.\n");
|
|
r = max;
|
|
}
|
|
if (!play_silence)
|
|
mp_audio_buffer_skip(p->buffer, r);
|
|
if (r > 0)
|
|
p->expected_end_time = 0;
|
|
// Nothing written, but more input data than space - this must mean the
|
|
// AO's get_space() doesn't do period alignment correctly.
|
|
bool stuck = r == 0 && max >= space && space > 0;
|
|
if (stuck)
|
|
MP_ERR(ao, "Audio output is reporting incorrect buffer status.\n");
|
|
// Wait until space becomes available. Also wait if we actually wrote data,
|
|
// so the AO wakes us up properly if it needs more data.
|
|
p->wait_on_ao = space == 0 || r > 0 || stuck;
|
|
p->still_playing |= r > 0 && !play_silence;
|
|
// If we just filled the AO completely (r == space), don't refill for a
|
|
// while. Prevents wakeup feedback with byte-granular AOs.
|
|
int needed = unlocked_get_space(ao);
|
|
bool more = needed >= (r == space ? ao->device_buffer / 4 : 1) && !stuck &&
|
|
!(flags & AOPLAY_FINAL_CHUNK);
|
|
if (more)
|
|
ao->wakeup_cb(ao->wakeup_ctx); // request more data
|
|
MP_TRACE(ao, "in=%d flags=%d space=%d r=%d wa/pl=%d/%d needed=%d more=%d\n",
|
|
max, flags, space, r, p->wait_on_ao, p->still_playing, needed, more);
|
|
}
|
|
|
|
static void *playthread(void *arg)
|
|
{
|
|
struct ao *ao = arg;
|
|
struct ao_push_state *p = ao->api_priv;
|
|
mpthread_set_name("ao");
|
|
pthread_mutex_lock(&p->lock);
|
|
while (!p->terminate) {
|
|
bool blocked = ao->driver->initially_blocked && !p->initial_unblocked;
|
|
bool playing = (!p->paused || ao->stream_silence) && !blocked;
|
|
if (playing)
|
|
ao_play_data(ao);
|
|
|
|
if (!p->need_wakeup) {
|
|
MP_STATS(ao, "start audio wait");
|
|
if (!p->wait_on_ao || !playing) {
|
|
// Avoid busy waiting, because the audio API will still report
|
|
// that it needs new data, even if we're not ready yet, or if
|
|
// get_space() decides that the amount of audio buffered in the
|
|
// device is enough, and p->buffer can be empty.
|
|
// The most important part is that the decoder is woken up, so
|
|
// that the decoder will wake up us in turn.
|
|
MP_TRACE(ao, "buffer inactive.\n");
|
|
|
|
bool was_playing = p->still_playing;
|
|
double timeout = -1;
|
|
if (p->still_playing && !p->paused && p->final_chunk &&
|
|
!mp_audio_buffer_samples(p->buffer))
|
|
{
|
|
double now = mp_time_sec();
|
|
if (!p->expected_end_time)
|
|
p->expected_end_time = now + unlocked_get_delay(ao);
|
|
if (p->expected_end_time < now) {
|
|
p->still_playing = false;
|
|
} else {
|
|
timeout = p->expected_end_time - now;
|
|
}
|
|
}
|
|
|
|
if (was_playing && !p->still_playing)
|
|
ao->wakeup_cb(ao->wakeup_ctx);
|
|
pthread_cond_signal(&p->wakeup); // for draining
|
|
|
|
if (p->still_playing && timeout > 0) {
|
|
struct timespec ts = mp_rel_time_to_timespec(timeout);
|
|
pthread_cond_timedwait(&p->wakeup, &p->lock, &ts);
|
|
} else {
|
|
pthread_cond_wait(&p->wakeup, &p->lock);
|
|
}
|
|
} else {
|
|
// Wait until the device wants us to write more data to it.
|
|
if (!ao->driver->wait || ao->driver->wait(ao, &p->lock) < 0) {
|
|
// Fallback to guessing.
|
|
double timeout = 0;
|
|
if (ao->driver->get_delay)
|
|
timeout = ao->driver->get_delay(ao);
|
|
timeout *= 0.25; // wake up if 25% played
|
|
if (!p->need_wakeup) {
|
|
struct timespec ts = mp_rel_time_to_timespec(timeout);
|
|
pthread_cond_timedwait(&p->wakeup, &p->lock, &ts);
|
|
}
|
|
}
|
|
}
|
|
MP_STATS(ao, "end audio wait");
|
|
}
|
|
p->need_wakeup = false;
|
|
}
|
|
pthread_mutex_unlock(&p->lock);
|
|
return NULL;
|
|
}
|
|
|
|
static void destroy_no_thread(struct ao *ao)
|
|
{
|
|
struct ao_push_state *p = ao->api_priv;
|
|
|
|
ao->driver->uninit(ao);
|
|
|
|
for (int n = 0; n < 2; n++) {
|
|
int h = p->wakeup_pipe[n];
|
|
if (h >= 0)
|
|
close(h);
|
|
}
|
|
|
|
pthread_cond_destroy(&p->wakeup);
|
|
pthread_mutex_destroy(&p->lock);
|
|
}
|
|
|
|
static void uninit(struct ao *ao)
|
|
{
|
|
struct ao_push_state *p = ao->api_priv;
|
|
|
|
pthread_mutex_lock(&p->lock);
|
|
p->terminate = true;
|
|
wakeup_playthread(ao);
|
|
pthread_mutex_unlock(&p->lock);
|
|
|
|
pthread_join(p->thread, NULL);
|
|
|
|
destroy_no_thread(ao);
|
|
}
|
|
|
|
static int init(struct ao *ao)
|
|
{
|
|
struct ao_push_state *p = ao->api_priv;
|
|
|
|
pthread_mutex_init(&p->lock, NULL);
|
|
pthread_cond_init(&p->wakeup, NULL);
|
|
mp_make_wakeup_pipe(p->wakeup_pipe);
|
|
|
|
if (ao->device_buffer <= 0) {
|
|
MP_FATAL(ao, "Couldn't probe device buffer size.\n");
|
|
goto err;
|
|
}
|
|
|
|
p->buffer = mp_audio_buffer_create(ao);
|
|
mp_audio_buffer_reinit_fmt(p->buffer, ao->format,
|
|
&ao->channels, ao->samplerate);
|
|
mp_audio_buffer_preallocate_min(p->buffer, ao->buffer);
|
|
if (pthread_create(&p->thread, NULL, playthread, ao))
|
|
goto err;
|
|
return 0;
|
|
err:
|
|
destroy_no_thread(ao);
|
|
return -1;
|
|
}
|
|
|
|
const struct ao_driver ao_api_push = {
|
|
.init = init,
|
|
.control = control,
|
|
.uninit = uninit,
|
|
.reset = reset,
|
|
.get_space = get_space,
|
|
.play = play,
|
|
.get_delay = get_delay,
|
|
.pause = audio_pause,
|
|
.resume = resume,
|
|
.drain = drain,
|
|
.get_eof = get_eof,
|
|
.priv_size = sizeof(struct ao_push_state),
|
|
};
|
|
|
|
// Must be called locked.
|
|
int ao_play_silence(struct ao *ao, int samples)
|
|
{
|
|
assert(ao->api == &ao_api_push);
|
|
|
|
struct ao_push_state *p = ao->api_priv;
|
|
|
|
if (!realloc_silence(ao, samples) || !ao->driver->play)
|
|
return 0;
|
|
|
|
return ao->driver->play(ao, (void **)p->silence, samples, 0);
|
|
}
|
|
|
|
void ao_unblock(struct ao *ao)
|
|
{
|
|
if (ao->api == &ao_api_push) {
|
|
struct ao_push_state *p = ao->api_priv;
|
|
pthread_mutex_lock(&p->lock);
|
|
p->need_wakeup = true;
|
|
p->initial_unblocked = true;
|
|
wakeup_playthread(ao);
|
|
pthread_cond_signal(&p->wakeup);
|
|
pthread_mutex_unlock(&p->lock);
|
|
}
|
|
}
|
|
|
|
#ifndef __MINGW32__
|
|
|
|
#include <poll.h>
|
|
|
|
#define MAX_POLL_FDS 20
|
|
|
|
// Call poll() for the given fds. This will extend the given fds with the
|
|
// wakeup pipe, so ao_wakeup_poll() will basically interrupt this function.
|
|
// Unlocks the lock temporarily.
|
|
// Returns <0 on error, 0 on success, 1 if the caller should return immediately.
|
|
int ao_wait_poll(struct ao *ao, struct pollfd *fds, int num_fds,
|
|
pthread_mutex_t *lock)
|
|
{
|
|
struct ao_push_state *p = ao->api_priv;
|
|
assert(ao->api == &ao_api_push);
|
|
assert(&p->lock == lock);
|
|
|
|
if (num_fds >= MAX_POLL_FDS || p->wakeup_pipe[0] < 0)
|
|
return -1;
|
|
|
|
struct pollfd p_fds[MAX_POLL_FDS];
|
|
memcpy(p_fds, fds, num_fds * sizeof(p_fds[0]));
|
|
p_fds[num_fds] = (struct pollfd){
|
|
.fd = p->wakeup_pipe[0],
|
|
.events = POLLIN,
|
|
};
|
|
|
|
pthread_mutex_unlock(&p->lock);
|
|
int r = poll(p_fds, num_fds + 1, -1);
|
|
r = r < 0 ? -errno : 0;
|
|
pthread_mutex_lock(&p->lock);
|
|
|
|
memcpy(fds, p_fds, num_fds * sizeof(fds[0]));
|
|
bool wakeup = false;
|
|
if (p_fds[num_fds].revents & POLLIN) {
|
|
wakeup = true;
|
|
// might "drown" some wakeups, but that's ok for our use-case
|
|
mp_flush_wakeup_pipe(p->wakeup_pipe[0]);
|
|
}
|
|
return (r >= 0 || r == -EINTR) ? wakeup : -1;
|
|
}
|
|
|
|
void ao_wakeup_poll(struct ao *ao)
|
|
{
|
|
assert(ao->api == &ao_api_push);
|
|
struct ao_push_state *p = ao->api_priv;
|
|
|
|
(void)write(p->wakeup_pipe[1], &(char){0}, 1);
|
|
}
|
|
|
|
#endif
|