2014-03-08 23:04:37 +00:00
|
|
|
/*
|
|
|
|
* This file is part of mpv.
|
|
|
|
*
|
|
|
|
* mpv is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <inttypes.h>
|
2014-05-29 21:57:11 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
2014-03-08 23:04:37 +00:00
|
|
|
#include <assert.h>
|
|
|
|
|
2014-05-29 21:57:11 +00:00
|
|
|
#include "osdep/io.h"
|
|
|
|
|
2014-03-08 23:04:37 +00:00
|
|
|
#include "ao.h"
|
|
|
|
#include "internal.h"
|
|
|
|
#include "audio/format.h"
|
|
|
|
|
|
|
|
#include "common/msg.h"
|
|
|
|
#include "common/common.h"
|
|
|
|
|
2014-04-15 20:38:16 +00:00
|
|
|
#include "input/input.h"
|
|
|
|
|
2014-03-08 23:04:37 +00:00
|
|
|
#include "osdep/threads.h"
|
2014-04-17 20:50:49 +00:00
|
|
|
#include "osdep/timer.h"
|
2014-03-08 23:04:37 +00:00
|
|
|
#include "compat/atomics.h"
|
|
|
|
|
|
|
|
#include "audio/audio.h"
|
|
|
|
#include "audio/audio_buffer.h"
|
|
|
|
|
|
|
|
struct ao_push_state {
|
|
|
|
pthread_t thread;
|
|
|
|
pthread_mutex_t lock;
|
|
|
|
pthread_cond_t wakeup;
|
2014-05-30 00:14:45 +00:00
|
|
|
pthread_cond_t wakeup_drain;
|
2014-03-08 23:04:37 +00:00
|
|
|
|
2014-04-15 20:38:16 +00:00
|
|
|
// --- protected by lock
|
|
|
|
|
2014-03-08 23:04:37 +00:00
|
|
|
struct mp_audio_buffer *buffer;
|
|
|
|
|
|
|
|
bool terminate;
|
2014-05-30 00:14:45 +00:00
|
|
|
bool drain;
|
2014-05-29 21:56:48 +00:00
|
|
|
bool buffers_full;
|
|
|
|
bool avoid_ao_wait;
|
|
|
|
bool need_wakeup;
|
|
|
|
bool requested_data;
|
|
|
|
bool paused;
|
2014-03-08 23:04:37 +00:00
|
|
|
|
|
|
|
// Whether the current buffer contains the complete audio.
|
|
|
|
bool final_chunk;
|
2014-04-17 20:50:49 +00:00
|
|
|
double expected_end_time;
|
2014-05-29 21:57:11 +00:00
|
|
|
|
|
|
|
int wakeup_pipe[2];
|
2014-03-08 23:04:37 +00:00
|
|
|
};
|
|
|
|
|
2014-05-29 21:56:48 +00:00
|
|
|
// lock must be held
|
2014-04-15 20:38:16 +00:00
|
|
|
static void wakeup_playthread(struct ao *ao)
|
|
|
|
{
|
|
|
|
struct ao_push_state *p = ao->api_priv;
|
2014-05-29 21:56:48 +00:00
|
|
|
if (ao->driver->wakeup)
|
|
|
|
ao->driver->wakeup(ao);
|
2014-04-15 20:38:16 +00:00
|
|
|
p->need_wakeup = true;
|
|
|
|
pthread_cond_signal(&p->wakeup);
|
|
|
|
}
|
|
|
|
|
2014-03-08 23:04:37 +00:00
|
|
|
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 float get_delay(struct ao *ao)
|
|
|
|
{
|
|
|
|
struct ao_push_state *p = ao->api_priv;
|
|
|
|
pthread_mutex_lock(&p->lock);
|
|
|
|
double driver_delay = 0;
|
|
|
|
if (ao->driver->get_delay)
|
|
|
|
driver_delay = ao->driver->get_delay(ao);
|
|
|
|
double delay = driver_delay + mp_audio_buffer_seconds(p->buffer);
|
|
|
|
pthread_mutex_unlock(&p->lock);
|
2014-04-17 20:50:49 +00:00
|
|
|
if (delay >= AO_EOF_DELAY && p->expected_end_time) {
|
|
|
|
if (mp_time_sec() > p->expected_end_time) {
|
|
|
|
MP_ERR(ao, "Audio device EOF reporting is broken!\n");
|
|
|
|
MP_ERR(ao, "Please report this problem.\n");
|
|
|
|
delay = 0;
|
|
|
|
}
|
|
|
|
}
|
2014-03-08 23:04:37 +00:00
|
|
|
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);
|
2014-05-29 21:56:48 +00:00
|
|
|
p->paused = false;
|
2014-04-15 20:38:16 +00:00
|
|
|
wakeup_playthread(ao);
|
2014-03-08 23:04:37 +00:00
|
|
|
pthread_mutex_unlock(&p->lock);
|
|
|
|
}
|
|
|
|
|
2014-05-29 21:57:11 +00:00
|
|
|
static void audio_pause(struct ao *ao)
|
2014-03-08 23:04:37 +00:00
|
|
|
{
|
|
|
|
struct ao_push_state *p = ao->api_priv;
|
|
|
|
pthread_mutex_lock(&p->lock);
|
|
|
|
if (ao->driver->pause)
|
|
|
|
ao->driver->pause(ao);
|
2014-05-29 21:56:48 +00:00
|
|
|
p->paused = true;
|
2014-04-15 20:38:16 +00:00
|
|
|
wakeup_playthread(ao);
|
2014-03-08 23:04:37 +00:00
|
|
|
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);
|
2014-05-29 21:56:48 +00:00
|
|
|
p->paused = false;
|
2014-04-17 20:50:49 +00:00
|
|
|
p->expected_end_time = 0;
|
2014-04-15 20:38:16 +00:00
|
|
|
wakeup_playthread(ao);
|
2014-03-08 23:04:37 +00:00
|
|
|
pthread_mutex_unlock(&p->lock);
|
|
|
|
}
|
|
|
|
|
2014-03-08 23:49:39 +00:00
|
|
|
static void drain(struct ao *ao)
|
|
|
|
{
|
2014-05-30 00:14:45 +00:00
|
|
|
struct ao_push_state *p = ao->api_priv;
|
|
|
|
|
|
|
|
pthread_mutex_lock(&p->lock);
|
2014-07-13 18:06:33 +00:00
|
|
|
if (p->paused) {
|
|
|
|
pthread_mutex_unlock(&p->lock);
|
|
|
|
return;
|
|
|
|
}
|
2014-05-30 00:14:45 +00:00
|
|
|
p->final_chunk = true;
|
|
|
|
p->drain = true;
|
|
|
|
wakeup_playthread(ao);
|
|
|
|
while (p->drain)
|
|
|
|
pthread_cond_wait(&p->wakeup_drain, &p->lock);
|
|
|
|
pthread_mutex_unlock(&p->lock);
|
|
|
|
|
|
|
|
if (!ao->driver->drain)
|
2014-07-13 18:06:33 +00:00
|
|
|
mp_sleep_us(get_delay(ao) * 1000000);
|
|
|
|
reset(ao);
|
2014-03-08 23:49:39 +00:00
|
|
|
}
|
2014-03-08 23:04:37 +00:00
|
|
|
|
2014-05-11 18:51:49 +00:00
|
|
|
static int unlocked_get_space(struct ao *ao)
|
2014-03-08 23:04:37 +00:00
|
|
|
{
|
|
|
|
struct ao_push_state *p = ao->api_priv;
|
audio/out: reduce amount of audio buffering
Since the addition of the AO feed thread, 200ms of latency (MIN_BUFFER)
was added to all push-based AOs. This is not so nice, because even AOs
with relatively small buffering (e.g. ao_alsa on my system with ~170ms
of buffer size), the additional latency becomes noticable when e.g.
toggling mute with softvol.
Fix this by trying to keep not only 200ms minimum buffer, but also 200ms
maximum buffer. In other words, never buffer beyond 200ms in total. Do
this by estimating the AO's buffer fill status using get_space and the
initially known AO buffer size (the get_space return value on
initialization, before any audio was played). We limit the maximum
amount of data written to the soft buffer so that soft buffer size and
audio buffer size equal to 200ms (MIN_BUFFER).
To avoid weird problems with weird AOs, we buffer beyond MIN_BUFFER if
the AO's get_space requests more data than that, and as long as the soft
buffer is large enough.
Note that this is just a hack to improve the latency. When the audio
chain gains the ability to refilter data, this won't be needed anymore,
and instead we can introduce some sort of buffer replacement function in
order to update data in the soft buffer.
2014-03-10 00:13:40 +00:00
|
|
|
int space = mp_audio_buffer_get_write_available(p->buffer);
|
|
|
|
if (ao->driver->get_space) {
|
|
|
|
// The following code attempts to keep the total buffered audio to
|
2014-05-30 21:56:10 +00:00
|
|
|
// MIN_BUFFER/2+device_buffer in order to improve latency.
|
audio/out: reduce amount of audio buffering
Since the addition of the AO feed thread, 200ms of latency (MIN_BUFFER)
was added to all push-based AOs. This is not so nice, because even AOs
with relatively small buffering (e.g. ao_alsa on my system with ~170ms
of buffer size), the additional latency becomes noticable when e.g.
toggling mute with softvol.
Fix this by trying to keep not only 200ms minimum buffer, but also 200ms
maximum buffer. In other words, never buffer beyond 200ms in total. Do
this by estimating the AO's buffer fill status using get_space and the
initially known AO buffer size (the get_space return value on
initialization, before any audio was played). We limit the maximum
amount of data written to the soft buffer so that soft buffer size and
audio buffer size equal to 200ms (MIN_BUFFER).
To avoid weird problems with weird AOs, we buffer beyond MIN_BUFFER if
the AO's get_space requests more data than that, and as long as the soft
buffer is large enough.
Note that this is just a hack to improve the latency. When the audio
chain gains the ability to refilter data, this won't be needed anymore,
and instead we can introduce some sort of buffer replacement function in
order to update data in the soft buffer.
2014-03-10 00:13:40 +00:00
|
|
|
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);
|
2014-05-30 21:56:10 +00:00
|
|
|
int min_buffer = MIN_BUFFER / 2 * ao->samplerate + ao->device_buffer;
|
|
|
|
int total_buffer = device_buffered + soft_buffered;
|
|
|
|
int missing = min_buffer - total_buffer;
|
audio/out: reduce amount of audio buffering
Since the addition of the AO feed thread, 200ms of latency (MIN_BUFFER)
was added to all push-based AOs. This is not so nice, because even AOs
with relatively small buffering (e.g. ao_alsa on my system with ~170ms
of buffer size), the additional latency becomes noticable when e.g.
toggling mute with softvol.
Fix this by trying to keep not only 200ms minimum buffer, but also 200ms
maximum buffer. In other words, never buffer beyond 200ms in total. Do
this by estimating the AO's buffer fill status using get_space and the
initially known AO buffer size (the get_space return value on
initialization, before any audio was played). We limit the maximum
amount of data written to the soft buffer so that soft buffer size and
audio buffer size equal to 200ms (MIN_BUFFER).
To avoid weird problems with weird AOs, we buffer beyond MIN_BUFFER if
the AO's get_space requests more data than that, and as long as the soft
buffer is large enough.
Note that this is just a hack to improve the latency. When the audio
chain gains the ability to refilter data, this won't be needed anymore,
and instead we can introduce some sort of buffer replacement function in
order to update data in the soft buffer.
2014-03-10 00:13:40 +00:00
|
|
|
space = MPMIN(space, missing);
|
|
|
|
space = MPMAX(0, space);
|
|
|
|
}
|
2014-05-11 18:51:49 +00:00
|
|
|
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);
|
2014-03-08 23:04:37 +00:00
|
|
|
pthread_mutex_unlock(&p->lock);
|
audio/out: reduce amount of audio buffering
Since the addition of the AO feed thread, 200ms of latency (MIN_BUFFER)
was added to all push-based AOs. This is not so nice, because even AOs
with relatively small buffering (e.g. ao_alsa on my system with ~170ms
of buffer size), the additional latency becomes noticable when e.g.
toggling mute with softvol.
Fix this by trying to keep not only 200ms minimum buffer, but also 200ms
maximum buffer. In other words, never buffer beyond 200ms in total. Do
this by estimating the AO's buffer fill status using get_space and the
initially known AO buffer size (the get_space return value on
initialization, before any audio was played). We limit the maximum
amount of data written to the soft buffer so that soft buffer size and
audio buffer size equal to 200ms (MIN_BUFFER).
To avoid weird problems with weird AOs, we buffer beyond MIN_BUFFER if
the AO's get_space requests more data than that, and as long as the soft
buffer is large enough.
Note that this is just a hack to improve the latency. When the audio
chain gains the ability to refilter data, this won't be needed anymore,
and instead we can introduce some sort of buffer replacement function in
order to update data in the soft buffer.
2014-03-10 00:13:40 +00:00
|
|
|
return space;
|
2014-03-08 23:04:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2014-05-29 21:56:48 +00:00
|
|
|
if (write_samples < samples)
|
|
|
|
flags = flags & ~AOPLAY_FINAL_CHUNK;
|
|
|
|
bool is_final = flags & AOPLAY_FINAL_CHUNK;
|
|
|
|
|
2014-03-08 23:04:37 +00:00
|
|
|
struct mp_audio audio;
|
|
|
|
mp_audio_buffer_get_format(p->buffer, &audio);
|
|
|
|
for (int n = 0; n < ao->num_planes; n++)
|
|
|
|
audio.planes[n] = data[n];
|
|
|
|
audio.samples = write_samples;
|
|
|
|
mp_audio_buffer_append(p->buffer, &audio);
|
|
|
|
|
2014-05-29 21:56:48 +00:00
|
|
|
bool got_data = write_samples > 0 || p->paused || p->final_chunk != is_final;
|
2014-03-08 23:04:37 +00:00
|
|
|
|
2014-05-29 21:56:48 +00:00
|
|
|
p->expected_end_time = 0;
|
|
|
|
p->final_chunk = is_final;
|
|
|
|
p->paused = false;
|
|
|
|
|
|
|
|
// If we don't have new data, the decoder thread basically promises it
|
|
|
|
// will send new data as soon as it's available.
|
|
|
|
if (got_data) {
|
|
|
|
p->requested_data = false;
|
|
|
|
wakeup_playthread(ao);
|
|
|
|
}
|
2014-03-08 23:04:37 +00:00
|
|
|
pthread_mutex_unlock(&p->lock);
|
|
|
|
return write_samples;
|
|
|
|
}
|
|
|
|
|
|
|
|
// called locked
|
2014-05-29 21:56:48 +00:00
|
|
|
static void ao_play_data(struct ao *ao)
|
2014-03-08 23:04:37 +00:00
|
|
|
{
|
|
|
|
struct ao_push_state *p = ao->api_priv;
|
|
|
|
struct mp_audio data;
|
|
|
|
mp_audio_buffer_peek(p->buffer, &data);
|
|
|
|
int max = data.samples;
|
2014-05-29 21:56:48 +00:00
|
|
|
int space = ao->driver->get_space(ao);
|
|
|
|
space = MPMAX(space, 0);
|
2014-03-08 23:04:37 +00:00
|
|
|
if (data.samples > space)
|
|
|
|
data.samples = space;
|
|
|
|
int flags = 0;
|
|
|
|
if (p->final_chunk && data.samples == max)
|
|
|
|
flags |= AOPLAY_FINAL_CHUNK;
|
2014-05-29 21:56:48 +00:00
|
|
|
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");
|
2014-03-08 23:04:37 +00:00
|
|
|
if (r > data.samples) {
|
2014-05-11 14:26:23 +00:00
|
|
|
MP_WARN(ao, "Audio device returned non-sense value.\n");
|
2014-03-08 23:04:37 +00:00
|
|
|
r = data.samples;
|
|
|
|
}
|
2014-05-30 00:14:38 +00:00
|
|
|
r = MPMAX(r, 0);
|
|
|
|
// Probably can't copy the rest of the buffer due to period alignment.
|
|
|
|
bool stuck = r <= 0 && space >= max && data.samples > 0;
|
|
|
|
if ((flags & AOPLAY_FINAL_CHUNK) && stuck) {
|
|
|
|
MP_ERR(ao, "Audio output driver seems to ignore AOPLAY_FINAL_CHUNK.\n");
|
|
|
|
r = max;
|
|
|
|
}
|
|
|
|
mp_audio_buffer_skip(p->buffer, r);
|
2014-04-17 20:50:49 +00:00
|
|
|
if (p->final_chunk && mp_audio_buffer_samples(p->buffer) == 0) {
|
|
|
|
p->expected_end_time = mp_time_sec() + AO_EOF_DELAY + 0.25; // + margin
|
|
|
|
if (ao->driver->get_delay)
|
|
|
|
p->expected_end_time += ao->driver->get_delay(ao);
|
|
|
|
}
|
2014-05-29 21:56:48 +00:00
|
|
|
// In both cases, we have to account for space!=0, but the AO not accepting
|
|
|
|
// any new data (due to rounding to period boundaries).
|
|
|
|
p->buffers_full = max >= space && r <= 0;
|
2014-05-30 00:14:38 +00:00
|
|
|
p->avoid_ao_wait = (max == 0 && space > 0) || p->paused || stuck;
|
2014-06-11 22:30:48 +00:00
|
|
|
MP_TRACE(ao, "in=%d, space=%d r=%d flags=%d aw=%d full=%d f=%d\n", max,
|
|
|
|
space, r, flags, p->avoid_ao_wait, p->buffers_full, p->final_chunk);
|
2014-05-29 21:56:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Estimate when the AO needs data again.
|
|
|
|
static double ao_estimate_timeout(struct ao *ao)
|
|
|
|
{
|
|
|
|
struct ao_push_state *p = ao->api_priv;
|
|
|
|
double timeout = 0;
|
|
|
|
if (p->buffers_full && ao->driver->get_delay) {
|
|
|
|
timeout = ao->driver->get_delay(ao) - 0.050;
|
|
|
|
// Keep extra safety margin if the buffers are large
|
|
|
|
if (timeout > 0.100)
|
|
|
|
timeout = MPMAX(timeout - 0.200, 0.100);
|
|
|
|
}
|
|
|
|
return MPMAX(timeout, ao->device_buffer * 0.75 / ao->samplerate);
|
2014-03-08 23:04:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void *playthread(void *arg)
|
|
|
|
{
|
|
|
|
struct ao *ao = arg;
|
|
|
|
struct ao_push_state *p = ao->api_priv;
|
2014-05-29 21:56:48 +00:00
|
|
|
pthread_mutex_lock(&p->lock);
|
|
|
|
while (!p->terminate) {
|
2014-06-03 13:57:47 +00:00
|
|
|
if (!p->paused)
|
|
|
|
ao_play_data(ao);
|
2014-05-29 21:56:48 +00:00
|
|
|
|
|
|
|
// Request new data from decoder if buffer goes below "full".
|
|
|
|
// Allow a small margin of missing data for AOs that use timeouts.
|
|
|
|
double margin = ao->driver->wait ? 0 : ao->device_buffer / 8;
|
|
|
|
if (!p->buffers_full && unlocked_get_space(ao) > margin) {
|
|
|
|
if (!p->requested_data)
|
|
|
|
mp_input_wakeup(ao->input_ctx);
|
|
|
|
p->requested_data = true;
|
2014-04-15 20:38:16 +00:00
|
|
|
}
|
2014-05-29 21:56:48 +00:00
|
|
|
|
2014-06-11 22:30:21 +00:00
|
|
|
if (p->drain && (p->avoid_ao_wait || p->paused)) {
|
2014-05-30 00:14:45 +00:00
|
|
|
if (ao->driver->drain)
|
|
|
|
ao->driver->drain(ao);
|
|
|
|
p->drain = false;
|
|
|
|
pthread_cond_signal(&p->wakeup_drain);
|
|
|
|
}
|
|
|
|
|
2014-05-29 21:56:48 +00:00
|
|
|
if (!p->need_wakeup) {
|
|
|
|
MP_STATS(ao, "start audio wait");
|
2014-06-03 13:57:47 +00:00
|
|
|
if (p->avoid_ao_wait || p->paused) {
|
2014-05-29 21:56:48 +00:00
|
|
|
// 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");
|
|
|
|
mp_input_wakeup(ao->input_ctx);
|
|
|
|
pthread_cond_wait(&p->wakeup, &p->lock);
|
2014-03-08 23:04:37 +00:00
|
|
|
} else {
|
2014-05-29 21:56:48 +00:00
|
|
|
if (!ao->driver->wait || ao->driver->wait(ao, &p->lock) < 0) {
|
|
|
|
// Fallback to guessing.
|
|
|
|
double timeout = ao_estimate_timeout(ao);
|
|
|
|
mpthread_cond_timedwait_rel(&p->wakeup, &p->lock, timeout);
|
|
|
|
}
|
2014-03-08 23:04:37 +00:00
|
|
|
}
|
2014-05-29 21:56:48 +00:00
|
|
|
MP_STATS(ao, "end audio wait");
|
2014-03-08 23:04:37 +00:00
|
|
|
}
|
2014-04-15 20:38:16 +00:00
|
|
|
p->need_wakeup = false;
|
2014-03-08 23:04:37 +00:00
|
|
|
}
|
2014-05-29 21:56:48 +00:00
|
|
|
pthread_mutex_unlock(&p->lock);
|
2014-03-08 23:04:37 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-03-08 23:49:39 +00:00
|
|
|
static void uninit(struct ao *ao)
|
2014-03-08 23:04:37 +00:00
|
|
|
{
|
|
|
|
struct ao_push_state *p = ao->api_priv;
|
|
|
|
|
|
|
|
pthread_mutex_lock(&p->lock);
|
|
|
|
p->terminate = true;
|
2014-04-15 20:38:16 +00:00
|
|
|
wakeup_playthread(ao);
|
2014-03-08 23:04:37 +00:00
|
|
|
pthread_mutex_unlock(&p->lock);
|
|
|
|
|
|
|
|
pthread_join(p->thread, NULL);
|
|
|
|
|
2014-03-08 23:49:39 +00:00
|
|
|
ao->driver->uninit(ao);
|
2014-03-08 23:04:37 +00:00
|
|
|
|
2014-05-29 21:57:11 +00:00
|
|
|
for (int n = 0; n < 2; n++)
|
|
|
|
close(p->wakeup_pipe[n]);
|
|
|
|
|
2014-03-08 23:04:37 +00:00
|
|
|
pthread_cond_destroy(&p->wakeup);
|
2014-05-30 00:14:45 +00:00
|
|
|
pthread_cond_destroy(&p->wakeup_drain);
|
2014-03-08 23:04:37 +00:00
|
|
|
pthread_mutex_destroy(&p->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2014-05-30 00:14:45 +00:00
|
|
|
pthread_cond_init(&p->wakeup_drain, NULL);
|
2014-07-26 18:26:27 +00:00
|
|
|
mp_make_wakeup_pipe(p->wakeup_pipe);
|
2014-03-08 23:04:37 +00:00
|
|
|
|
|
|
|
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);
|
2014-07-25 12:30:59 +00:00
|
|
|
if (pthread_create(&p->thread, NULL, playthread, ao))
|
|
|
|
goto err;
|
2014-03-08 23:04:37 +00:00
|
|
|
return 0;
|
2014-07-25 12:30:59 +00:00
|
|
|
err:
|
|
|
|
ao->driver->uninit(ao);
|
|
|
|
return -1;
|
2014-03-08 23:04:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2014-05-29 21:57:11 +00:00
|
|
|
.pause = audio_pause,
|
2014-03-08 23:04:37 +00:00
|
|
|
.resume = resume,
|
2014-03-08 23:49:39 +00:00
|
|
|
.drain = drain,
|
2014-03-08 23:04:37 +00:00
|
|
|
.priv_size = sizeof(struct ao_push_state),
|
|
|
|
};
|
|
|
|
|
2014-04-15 20:38:16 +00:00
|
|
|
// Must be called locked.
|
2014-03-08 23:04:37 +00:00
|
|
|
int ao_play_silence(struct ao *ao, int samples)
|
|
|
|
{
|
|
|
|
assert(ao->api == &ao_api_push);
|
|
|
|
if (samples <= 0 || AF_FORMAT_IS_SPECIAL(ao->format) || !ao->driver->play)
|
|
|
|
return 0;
|
|
|
|
char *p = talloc_size(NULL, samples * ao->sstride);
|
|
|
|
af_fill_silence(p, samples * ao->sstride, ao->format);
|
|
|
|
void *tmp[MP_NUM_CHANNELS];
|
|
|
|
for (int n = 0; n < MP_NUM_CHANNELS; n++)
|
|
|
|
tmp[n] = p;
|
|
|
|
int r = ao->driver->play(ao, tmp, samples, 0);
|
|
|
|
talloc_free(p);
|
|
|
|
return r;
|
|
|
|
}
|
2014-05-29 21:57:11 +00:00
|
|
|
|
|
|
|
#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.
|
2014-05-30 21:54:11 +00:00
|
|
|
// Returns <0 on error, 0 on success, 1 if the caller should return immediately.
|
2014-05-29 21:57:11 +00:00
|
|
|
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]));
|
2014-05-30 21:54:11 +00:00
|
|
|
bool wakeup = false;
|
2014-05-29 21:57:11 +00:00
|
|
|
if (p_fds[num_fds].revents & POLLIN) {
|
2014-05-30 21:54:11 +00:00
|
|
|
wakeup = true;
|
2014-05-29 21:57:11 +00:00
|
|
|
// flush the wakeup pipe contents - might "drown" some wakeups, but
|
|
|
|
// that's ok for our use-case
|
|
|
|
char buf[100];
|
|
|
|
read(p->wakeup_pipe[0], buf, sizeof(buf));
|
|
|
|
}
|
2014-05-30 21:54:11 +00:00
|
|
|
return (r >= 0 || r == -EINTR) ? wakeup : -1;
|
2014-05-29 21:57:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ao_wakeup_poll(struct ao *ao)
|
|
|
|
{
|
|
|
|
assert(ao->api == &ao_api_push);
|
|
|
|
struct ao_push_state *p = ao->api_priv;
|
|
|
|
|
|
|
|
write(p->wakeup_pipe[1], &(char){0}, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|