/* * 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 . */ #include #include #include #include #include #include #include #include "ao.h" #include "internal.h" #include "audio/aframe.h" #include "audio/format.h" #include "common/msg.h" #include "common/common.h" #include "input/input.h" #include "osdep/io.h" #include "osdep/timer.h" #include "osdep/threads.h" #include "osdep/atomic.h" #include "misc/ring.h" struct buffer_state { // Buffer and AO pthread_mutex_t lock; pthread_cond_t wakeup; // Playthread sleep pthread_mutex_t pt_lock; pthread_cond_t pt_wakeup; // Access from AO driver's thread only. char *convert_buffer; // --- protected by lock struct mp_ring *buffers[MP_NUM_CHANNELS]; bool streaming; // AO streaming active bool playing; // logically playing audio from buffer bool paused; // logically paused; implies playing=true bool final_chunk; // if buffer contains EOF int64_t end_time_us; // absolute output time of last played sample int64_t underflow; // number of samples missing since last check bool initial_unblocked; // "Push" AOs only (AOs with driver->write). bool still_playing; bool hw_paused; // driver->set_pause() was used successfully bool recover_pause; // non-hw_paused: needs to recover delay bool draining; bool had_underrun; bool ao_wait_low_buffer; struct mp_pcm_state prepause_state; pthread_t thread; // thread shoveling data to AO bool thread_valid; // thread is running struct mp_aframe *temp_buf; // --- protected by pt_lock bool need_wakeup; bool terminate; // exit thread }; static void *playthread(void *arg); void ao_wakeup_playthread(struct ao *ao) { struct buffer_state *p = ao->buffer_state; pthread_mutex_lock(&p->pt_lock); p->need_wakeup = true; pthread_cond_broadcast(&p->pt_wakeup); pthread_mutex_unlock(&p->pt_lock); } // called locked static void get_dev_state(struct ao *ao, struct mp_pcm_state *state) { struct buffer_state *p = ao->buffer_state; if (p->paused) { *state = p->prepause_state; return; } *state = (struct mp_pcm_state){ .free_samples = -1, .queued_samples = -1, .delay = -1, .underrun = false, }; ao->driver->get_state(ao, state); if (state->underrun) { p->had_underrun = true; if (p->draining) { MP_VERBOSE(ao, "underrun signaled for audio end\n"); p->still_playing = false; pthread_cond_broadcast(&p->wakeup); } else { ao_add_events(ao, AO_EVENT_UNDERRUN); } } } static int unlocked_get_space(struct ao *ao) { struct buffer_state *p = ao->buffer_state; int space = mp_ring_available(p->buffers[0]) / ao->sstride; // The following code attempts to keep the total buffered audio at // ao->buffer in order to improve latency. if (ao->driver->write) { struct mp_pcm_state state; get_dev_state(ao, &state); int align = af_format_sample_alignment(ao->format); int device_space = MPMAX(state.free_samples, 0); int device_buffered = ao->device_buffer - device_space; int soft_buffered = mp_ring_size(p->buffers[0]) / ao->sstride - space; // 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; } int ao_get_space(struct ao *ao) { struct buffer_state *p = ao->buffer_state; pthread_mutex_lock(&p->lock); int space = unlocked_get_space(ao); pthread_mutex_unlock(&p->lock); return space; } int ao_play(struct ao *ao, void **data, int samples, int flags) { struct buffer_state *p = ao->buffer_state; pthread_mutex_lock(&p->lock); int write_samples = mp_ring_available(p->buffers[0]) / ao->sstride; write_samples = MPMIN(write_samples, samples); int write_bytes = write_samples * ao->sstride; for (int n = 0; n < ao->num_planes; n++) { int r = mp_ring_write(p->buffers[n], data[n], write_bytes); assert(r == write_bytes); } p->paused = false; p->final_chunk = write_samples == samples && (flags & PLAYER_FINAL_CHUNK); if (p->underflow) MP_DBG(ao, "Audio underrun by %lld samples.\n", (long long)p->underflow); p->underflow = 0; if (write_samples) { p->playing = true; p->still_playing = true; p->draining = false; if (!ao->driver->write && !p->streaming) { p->streaming = true; ao->driver->start(ao); } } pthread_mutex_unlock(&p->lock); if (write_samples) ao_wakeup_playthread(ao); return write_samples; } // Read the given amount of samples in the user-provided data buffer. Returns // the number of samples copied. If there is not enough data (buffer underrun // or EOF), return the number of samples that could be copied, and fill the // rest of the user-provided buffer with silence. // This basically assumes that the audio device doesn't care about underruns. // If this is called in paused mode, it will always return 0. // The caller should set out_time_us to the expected delay until the last sample // reaches the speakers, in microseconds, using mp_time_us() as reference. int ao_read_data(struct ao *ao, void **data, int samples, int64_t out_time_us) { struct buffer_state *p = ao->buffer_state; int full_bytes = samples * ao->sstride; bool need_wakeup = false; int bytes = 0; pthread_mutex_lock(&p->lock); if (!p->playing || p->paused) goto end; int buffered_bytes = mp_ring_buffered(p->buffers[0]); bytes = MPMIN(buffered_bytes, full_bytes); if (full_bytes > bytes && !p->final_chunk) { p->underflow += (full_bytes - bytes) / ao->sstride; ao_add_events(ao, AO_EVENT_UNDERRUN); } if (bytes > 0) p->end_time_us = out_time_us; for (int n = 0; n < ao->num_planes; n++) mp_ring_read(p->buffers[n], data[n], bytes); // Half of the buffer played -> request more. need_wakeup = buffered_bytes - bytes <= mp_ring_size(p->buffers[0]) / 2; end: pthread_mutex_unlock(&p->lock); if (need_wakeup) ao->wakeup_cb(ao->wakeup_ctx); // pad with silence (underflow/paused/eof) for (int n = 0; n < ao->num_planes; n++) af_fill_silence((char *)data[n] + bytes, full_bytes - bytes, ao->format); ao_post_process_data(ao, data, samples); return bytes / ao->sstride; } // Same as ao_read_data(), but convert data according to *fmt. // fmt->src_fmt and fmt->channels must be the same as the AO parameters. int ao_read_data_converted(struct ao *ao, struct ao_convert_fmt *fmt, void **data, int samples, int64_t out_time_us) { struct buffer_state *p = ao->buffer_state; void *ndata[MP_NUM_CHANNELS] = {0}; if (!ao_need_conversion(fmt)) return ao_read_data(ao, data, samples, out_time_us); assert(ao->format == fmt->src_fmt); assert(ao->channels.num == fmt->channels); bool planar = af_fmt_is_planar(fmt->src_fmt); int planes = planar ? fmt->channels : 1; int plane_samples = samples * (planar ? 1: fmt->channels); int src_plane_size = plane_samples * af_fmt_to_bytes(fmt->src_fmt); int dst_plane_size = plane_samples * fmt->dst_bits / 8; int needed = src_plane_size * planes; if (needed > talloc_get_size(p->convert_buffer) || !p->convert_buffer) { talloc_free(p->convert_buffer); p->convert_buffer = talloc_size(NULL, needed); } for (int n = 0; n < planes; n++) ndata[n] = p->convert_buffer + n * src_plane_size; int res = ao_read_data(ao, ndata, samples, out_time_us); ao_convert_inplace(fmt, ndata, samples); for (int n = 0; n < planes; n++) memcpy(data[n], ndata[n], dst_plane_size); return res; } int ao_control(struct ao *ao, enum aocontrol cmd, void *arg) { struct buffer_state *p = ao->buffer_state; int r = CONTROL_UNKNOWN; if (ao->driver->control) { 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 buffer_state *p = ao->buffer_state; double driver_delay = 0; if (ao->driver->write) { struct mp_pcm_state state; get_dev_state(ao, &state); driver_delay = state.delay; } else { int64_t end = p->end_time_us; int64_t now = mp_time_us(); driver_delay += MPMAX(0, (end - now) / (1000.0 * 1000.0)); } return mp_ring_buffered(p->buffers[0]) / (double)ao->bps + driver_delay; } double ao_get_delay(struct ao *ao) { struct buffer_state *p = ao->buffer_state; pthread_mutex_lock(&p->lock); double delay = unlocked_get_delay(ao); pthread_mutex_unlock(&p->lock); return delay; } void ao_reset(struct ao *ao) { struct buffer_state *p = ao->buffer_state; bool wakeup = false; pthread_mutex_lock(&p->lock); for (int n = 0; n < ao->num_planes; n++) mp_ring_reset(p->buffers[n]); if (!ao->stream_silence && ao->driver->reset) { ao->driver->reset(ao); // assumes the audio callback thread is stopped p->streaming = false; } p->paused = false; p->playing = false; p->recover_pause = false; p->hw_paused = false; wakeup = p->still_playing || p->draining; p->draining = false; p->still_playing = false; p->end_time_us = 0; atomic_fetch_and(&ao->events_, ~(unsigned int)AO_EVENT_UNDERRUN); pthread_mutex_unlock(&p->lock); if (wakeup) ao_wakeup_playthread(ao); } void ao_pause(struct ao *ao) { struct buffer_state *p = ao->buffer_state; bool wakeup = false; pthread_mutex_lock(&p->lock); if (p->playing && !p->paused) { if (p->streaming && !ao->stream_silence) { if (ao->driver->write) { if (!p->recover_pause) get_dev_state(ao, &p->prepause_state); if (ao->driver->set_pause && ao->driver->set_pause(ao, true)) { p->hw_paused = true; } else { ao->driver->reset(ao); p->streaming = false; } } else if (ao->driver->reset) { ao->driver->reset(ao); p->streaming = false; } } p->paused = true; wakeup = true; } pthread_mutex_unlock(&p->lock); if (wakeup) ao_wakeup_playthread(ao); } void ao_resume(struct ao *ao) { struct buffer_state *p = ao->buffer_state; bool wakeup = false; pthread_mutex_lock(&p->lock); if (p->playing && p->paused) { if (ao->driver->write) { if (p->streaming && p->hw_paused) { ao->driver->set_pause(ao, false); } else { p->recover_pause = true; } p->hw_paused = false; } else { if (!p->streaming) ao->driver->start(ao); p->streaming = true; } p->paused = false; wakeup = true; } pthread_mutex_unlock(&p->lock); if (wakeup) ao_wakeup_playthread(ao); } bool ao_eof_reached(struct ao *ao) { struct buffer_state *p = ao->buffer_state; pthread_mutex_lock(&p->lock); bool eof = !p->playing; if (ao->driver->write) { eof |= !p->still_playing; } else { // For simplicity, ignore the latency. Otherwise, we would have to run // an extra thread to time it. eof |= mp_ring_buffered(p->buffers[0]) == 0; } pthread_mutex_unlock(&p->lock); return eof; } // Block until the current audio buffer has played completely. void ao_drain(struct ao *ao) { struct buffer_state *p = ao->buffer_state; pthread_mutex_lock(&p->lock); p->final_chunk = true; while (!p->paused && p->still_playing && !p->had_underrun) { if (ao->driver->write) { if (p->draining) { // Wait for EOF signal from AO. pthread_cond_wait(&p->wakeup, &p->lock); } else { p->draining = true; MP_VERBOSE(ao, "waiting for draining...\n"); pthread_mutex_unlock(&p->lock); ao_wakeup_playthread(ao); pthread_mutex_lock(&p->lock); } } else { double left = mp_ring_buffered(p->buffers[0]) / (double)ao->bps * 1e6; pthread_mutex_unlock(&p->lock); if (left > 0) { // Wait for lower bound. mp_sleep_us(left); // And then poll for actual end. No other way. // Limit to arbitrary ~250ms max. waiting for robustness. int64_t max = mp_time_us() + 250000; while (mp_time_us() < max && !ao_eof_reached(ao)) mp_sleep_us(1); } else { p->still_playing = false; } pthread_mutex_lock(&p->lock); } } pthread_mutex_unlock(&p->lock); ao_reset(ao); } void ao_uninit(struct ao *ao) { struct buffer_state *p = ao->buffer_state; if (p->thread_valid) { pthread_mutex_lock(&p->pt_lock); p->terminate = true; pthread_cond_broadcast(&p->pt_wakeup); pthread_mutex_unlock(&p->pt_lock); pthread_join(p->thread, NULL); p->thread_valid = false; } if (ao->driver_initialized) ao->driver->uninit(ao); talloc_free(p->convert_buffer); talloc_free(p->temp_buf); pthread_cond_destroy(&p->wakeup); pthread_mutex_destroy(&p->lock); pthread_cond_destroy(&p->pt_wakeup); pthread_mutex_destroy(&p->pt_lock); talloc_free(ao); } void init_buffer_pre(struct ao *ao) { ao->buffer_state = talloc_zero(ao, struct buffer_state); } bool init_buffer_post(struct ao *ao) { struct buffer_state *p = ao->buffer_state; assert(ao->driver->start); if (ao->driver->write) { assert(ao->driver->reset); assert(ao->driver->get_state); } for (int n = 0; n < ao->num_planes; n++) p->buffers[n] = mp_ring_new(ao, ao->buffer * ao->sstride); mpthread_mutex_init_recursive(&p->lock); pthread_cond_init(&p->wakeup, NULL); pthread_mutex_init(&p->pt_lock, NULL); pthread_cond_init(&p->pt_wakeup, NULL); if (ao->driver->write) { p->thread_valid = true; if (pthread_create(&p->thread, NULL, playthread, ao)) { p->thread_valid = false; return false; } } else { if (ao->stream_silence) { ao->driver->start(ao); p->streaming = true; } } return true; } static bool realloc_buf(struct ao *ao, int samples) { struct buffer_state *p = ao->buffer_state; samples = MPMAX(1, samples); if (!p->temp_buf || samples > mp_aframe_get_size(p->temp_buf)) { TA_FREEP(&p->temp_buf); p->temp_buf = mp_aframe_create(); if (!mp_aframe_set_format(p->temp_buf, ao->format) || !mp_aframe_set_chmap(p->temp_buf, &ao->channels) || !mp_aframe_set_rate(p->temp_buf, ao->samplerate) || !mp_aframe_alloc_data(p->temp_buf, samples)) { TA_FREEP(&p->temp_buf); return false; } } return true; } // called locked static void ao_play_data(struct ao *ao) { struct buffer_state *p = ao->buffer_state; if (p->had_underrun) { MP_VERBOSE(ao, "recover underrun\n"); ao->driver->reset(ao); p->streaming = false; p->had_underrun = false; } struct mp_pcm_state state; get_dev_state(ao, &state); // Round free space to period sizes to reduce number of write() calls. int space = state.free_samples / ao->period_size * ao->period_size; bool play_silence = p->paused || (ao->stream_silence && !p->still_playing); space = MPMAX(space, 0); if (!realloc_buf(ao, space)) { MP_ERR(ao, "Failed to allocate buffer.\n"); return; } void **planes = (void **)mp_aframe_get_data_rw(p->temp_buf); assert(planes); int samples = mp_ring_buffered(p->buffers[0]) / ao->sstride; if (samples > space) samples = space; if (play_silence) samples = space; if (p->recover_pause) { samples = MPCLAMP(p->prepause_state.delay * ao->samplerate, 0, space); p->recover_pause = false; mp_aframe_set_silence(p->temp_buf, 0, space); } else { samples = ao_read_data(ao, planes, samples, 0); } if (play_silence) samples = space; // ao_read_data() sets remainder to silent bool is_eof = p->final_chunk && samples < space; bool ok = true; int written = 0; if (samples) { p->draining = is_eof; MP_STATS(ao, "start ao fill"); ok = ao->driver->write(ao, planes, samples); MP_STATS(ao, "end ao fill"); } if (!ok) MP_ERR(ao, "Error writing audio to device.\n"); if (samples > 0 && ok) { written = samples; if (!p->streaming) { MP_VERBOSE(ao, "starting AO\n"); ao->driver->start(ao); p->streaming = true; } } if (p->draining && p->still_playing && ao->untimed) { p->still_playing = false; pthread_cond_broadcast(&p->wakeup); } // 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->ao_wait_low_buffer = space == 0 || written > 0 || p->draining; p->still_playing |= samples > 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 >= (written == space ? ao->device_buffer / 4 : 1) && !p->final_chunk; if (more) ao->wakeup_cb(ao->wakeup_ctx); // request more data MP_TRACE(ao, "in=%d eof=%d space=%d r=%d wa/pl/dr=%d/%d/%d needed=%d more=%d\n", samples, is_eof, space, written, p->ao_wait_low_buffer, p->still_playing, p->draining, needed, more); } static void *playthread(void *arg) { struct ao *ao = arg; struct buffer_state *p = ao->buffer_state; mpthread_set_name("ao"); while (1) { pthread_mutex_lock(&p->lock); bool blocked = ao->driver->initially_blocked && !p->initial_unblocked; bool playing = !p->paused && (p->playing || ao->stream_silence); if (playing && !blocked) ao_play_data(ao); // Wait until the device wants us to write more data to it. // Fallback to guessing. double timeout = INFINITY; if (p->ao_wait_low_buffer) { struct mp_pcm_state state; get_dev_state(ao, &state); timeout = state.delay * 0.25; // wake up if 25% played p->ao_wait_low_buffer = false; // If the AO doesn't tell us, we need to guess. if (p->draining) timeout = MPMAX(timeout, 0.1); } pthread_mutex_unlock(&p->lock); pthread_mutex_lock(&p->pt_lock); if (p->terminate) { pthread_mutex_unlock(&p->pt_lock); break; } if (!p->need_wakeup) { MP_STATS(ao, "start audio wait"); struct timespec ts = mp_rel_time_to_timespec(timeout); pthread_cond_timedwait(&p->pt_wakeup, &p->pt_lock, &ts); MP_STATS(ao, "end audio wait"); } p->need_wakeup = false; pthread_mutex_unlock(&p->pt_lock); } return NULL; } void ao_unblock(struct ao *ao) { if (ao->driver->write) { struct buffer_state *p = ao->buffer_state; pthread_mutex_lock(&p->lock); p->initial_unblocked = true; pthread_mutex_unlock(&p->lock); ao_wakeup_playthread(ao); } }