summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--audio/out/ao_portaudio.c160
1 files changed, 13 insertions, 147 deletions
diff --git a/audio/out/ao_portaudio.c b/audio/out/ao_portaudio.c
index c3ba343485..135c9224f1 100644
--- a/audio/out/ao_portaudio.c
+++ b/audio/out/ao_portaudio.c
@@ -20,7 +20,6 @@
#include <string.h>
#include <stdlib.h>
#include <assert.h>
-#include <pthread.h>
#include <libavutil/common.h>
#include <portaudio.h>
@@ -29,22 +28,12 @@
#include "options/m_option.h"
#include "audio/format.h"
#include "common/msg.h"
-#include "misc/ring.h"
+#include "osdep/timer.h"
#include "ao.h"
#include "internal.h"
struct priv {
PaStream *stream;
- int framelen;
-
- pthread_mutex_t ring_mutex;
-
- // following variables are protected by ring_mutex
- struct mp_ring *ring;
- double play_time; // time when last packet returned to PA is on speaker
- // 0 is N/A (0 is not a valid PA time value)
- int play_silence; // play this many bytes of silence, before real data
- bool play_remaining;// play what's left in the buffer, then stop stream
// Options
char *cfg_device;
@@ -81,12 +70,6 @@ static bool check_pa_ret(struct mp_log *log, int ret)
#define CHECK_PA_RET(ret) check_pa_ret(ao->log, (ret))
-static int seconds_to_bytes(struct ao *ao, double seconds)
-{
- return af_fmt_seconds_to_bytes(ao->format, seconds, ao->channels.num,
- ao->samplerate);
-}
-
static int to_int(const char *s, int return_on_error)
{
char *endptr;
@@ -142,11 +125,6 @@ static int validate_device_opt(struct mp_log *log, const m_option_t *opt,
return 0;
}
-static void fill_silence(unsigned char *ptr, int len)
-{
- memset(ptr, 0, len);
-}
-
static int stream_callback(const void *input,
void *output_v,
unsigned long frameCount,
@@ -155,42 +133,17 @@ static int stream_callback(const void *input,
void *userData)
{
struct ao *ao = userData;
- struct priv *priv = ao->priv;
- int res = paContinue;
- unsigned char *output = output_v;
- int len_bytes = frameCount * priv->framelen;
-
- pthread_mutex_lock(&priv->ring_mutex);
// NOTE: PA + ALSA in dmix mode seems to pretend that there is no latency
// (outputBufferDacTime == currentTime)
- priv->play_time = timeInfo->outputBufferDacTime
- + len_bytes / (float)ao->bps;
-
- if (priv->play_silence > 0) {
- int bytes = FFMIN(priv->play_silence, len_bytes);
- fill_silence(output, bytes);
- priv->play_silence -= bytes;
- len_bytes -= bytes;
- output += bytes;
- }
- int read = mp_ring_read(priv->ring, output, len_bytes);
- len_bytes -= read;
- output += read;
-
- if (len_bytes > 0) {
- if (priv->play_remaining) {
- res = paComplete;
- priv->play_remaining = false;
- } else {
- MP_ERR(ao, "Buffer underflow!\n");
- }
- fill_silence(output, len_bytes);
- }
+ double play_time = timeInfo->outputBufferDacTime
+ + frameCount / (float)ao->samplerate;
+ double latency = play_time - timeInfo->currentTime;
+ int64_t end = mp_time_us() + MPMAX(0, latency * 1000000.0);
- pthread_mutex_unlock(&priv->ring_mutex);
+ ao_read_data(ao, &output_v, frameCount, end);
- return res;
+ return paContinue;
}
static void uninit(struct ao *ao, bool cut_audio)
@@ -199,18 +152,13 @@ static void uninit(struct ao *ao, bool cut_audio)
if (priv->stream) {
if (!cut_audio && Pa_IsStreamActive(priv->stream) == 1) {
- pthread_mutex_lock(&priv->ring_mutex);
-
- priv->play_remaining = true;
-
- pthread_mutex_unlock(&priv->ring_mutex);
+ ao_wait_drain(ao);
CHECK_PA_RET(Pa_StopStream(priv->stream));
}
CHECK_PA_RET(Pa_CloseStream(priv->stream));
}
- pthread_mutex_destroy(&priv->ring_mutex);
Pa_Terminate();
}
@@ -221,8 +169,6 @@ static int init(struct ao *ao)
if (!CHECK_PA_RET(Pa_Initialize()))
return -1;
- pthread_mutex_init(&priv->ring_mutex, NULL);
-
int pa_device = Pa_GetDefaultOutputDevice();
if (priv->cfg_device && priv->cfg_device[0])
pa_device = find_device(ao->log, priv->cfg_device);
@@ -261,8 +207,8 @@ static int init(struct ao *ao)
ao->format = fmt->mp_format;
sp.sampleFormat = fmt->pa_format;
- priv->framelen = ao->channels.num * (af_fmt2bits(ao->format) / 8);
- ao->bps = ao->samplerate * priv->framelen;
+ int framelen = ao->channels.num * (af_fmt2bits(ao->format) / 8);
+ ao->bps = ao->samplerate * framelen;
if (!CHECK_PA_RET(Pa_IsFormatSupported(NULL, &sp, ao->samplerate)))
goto error_exit;
@@ -271,8 +217,6 @@ static int init(struct ao *ao)
stream_callback, ao)))
goto error_exit;
- priv->ring = mp_ring_new(priv, seconds_to_bytes(ao, 0.5));
-
return 0;
error_exit:
@@ -280,95 +224,20 @@ error_exit:
return -1;
}
-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[0], samples * ao->sstride);
- if (flags & AOPLAY_FINAL_CHUNK)
- priv->play_remaining = true;
-
- pthread_mutex_unlock(&priv->ring_mutex);
-
- if (Pa_IsStreamStopped(priv->stream) == 1)
- CHECK_PA_RET(Pa_StartStream(priv->stream));
-
- return write_len / ao->sstride;
-}
-
-static int get_space(struct ao *ao)
-{
- struct priv *priv = ao->priv;
-
- pthread_mutex_lock(&priv->ring_mutex);
-
- int free = mp_ring_available(priv->ring);
-
- pthread_mutex_unlock(&priv->ring_mutex);
-
- return free / ao->sstride;
-}
-
-static float get_delay(struct ao *ao)
-{
- struct priv *priv = ao->priv;
-
- double stream_time = Pa_GetStreamTime(priv->stream);
-
- pthread_mutex_lock(&priv->ring_mutex);
-
- float frame_time = priv->play_time ? priv->play_time - stream_time : 0;
- float buffer_latency = (mp_ring_buffered(priv->ring) + priv->play_silence)
- / (float)ao->bps;
-
- pthread_mutex_unlock(&priv->ring_mutex);
-
- return buffer_latency + frame_time;
-}
-
static void reset(struct ao *ao)
{
struct priv *priv = ao->priv;
if (Pa_IsStreamStopped(priv->stream) != 1)
CHECK_PA_RET(Pa_AbortStream(priv->stream));
-
- pthread_mutex_lock(&priv->ring_mutex);
-
- mp_ring_reset(priv->ring);
- priv->play_remaining = false;
- priv->play_time = 0;
- priv->play_silence = 0;
-
- pthread_mutex_unlock(&priv->ring_mutex);
-}
-
-static void pause(struct ao *ao)
-{
- struct priv *priv = ao->priv;
-
- CHECK_PA_RET(Pa_AbortStream(priv->stream));
-
- double stream_time = Pa_GetStreamTime(priv->stream);
-
- pthread_mutex_lock(&priv->ring_mutex);
-
- // When playback resumes, replace the lost audio (due to dropping the
- // portaudio/driver/hardware internal buffers) with silence.
- float frame_time = priv->play_time ? priv->play_time - stream_time : 0;
- priv->play_silence += seconds_to_bytes(ao, FFMAX(frame_time, 0));
- priv->play_time = 0;
-
- pthread_mutex_unlock(&priv->ring_mutex);
}
static void resume(struct ao *ao)
{
struct priv *priv = ao->priv;
- CHECK_PA_RET(Pa_StartStream(priv->stream));
+ if (Pa_IsStreamStopped(priv->stream) == 1)
+ CHECK_PA_RET(Pa_StartStream(priv->stream));
}
#define OPT_BASE_STRUCT struct priv
@@ -379,10 +248,7 @@ const struct ao_driver audio_out_portaudio = {
.init = init,
.uninit = uninit,
.reset = reset,
- .get_space = get_space,
- .play = play,
- .get_delay = get_delay,
- .pause = pause,
+ .pause = reset,
.resume = resume,
.priv_size = sizeof(struct priv),
.options = (const struct m_option[]) {