summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2014-04-15 22:38:16 +0200
committerwm4 <wm4@nowhere>2014-04-15 22:38:16 +0200
commite2184fcbfb84029666d07fd49b4cab71b835e2e8 (patch)
treef304e9db9be6f2d2c3d7cf235901ba2bd2f5d4ca /audio
parentcad6425c466b29ede88d365eac465563b9c03570 (diff)
downloadmpv-e2184fcbfb84029666d07fd49b4cab71b835e2e8.tar.bz2
mpv-e2184fcbfb84029666d07fd49b4cab71b835e2e8.tar.xz
audio: wake up the core when audio buffer is running low
And also add a function ao_need_data(), which AO drivers can call if their audio buffer runs low. This change intends to make it easier for the playback thread: instead of making the playback thread calculate a timeout at which the audio buffer should be refilled, make the push.c audio thread wakeup the core instead. ao_need_data() is going to be used by ao_pulse, and we need to workaround a stupid situation with pulseaudio causing a deadlock because its callback still holds the internal pulseaudio lock. For AOs that don't call ao_need_data(), the deadline is calculated by the buffer fill status and latency, as before.
Diffstat (limited to 'audio')
-rw-r--r--audio/out/internal.h1
-rw-r--r--audio/out/push.c75
2 files changed, 61 insertions, 15 deletions
diff --git a/audio/out/internal.h b/audio/out/internal.h
index ea655f65f2..4214c6c1d6 100644
--- a/audio/out/internal.h
+++ b/audio/out/internal.h
@@ -121,6 +121,7 @@ struct ao_driver {
// These functions can be called by AOs.
int ao_play_silence(struct ao *ao, int samples);
+void ao_need_data(struct ao *ao);
void ao_wait_drain(struct ao *ao);
int ao_read_data(struct ao *ao, void **data, int samples, int64_t out_time_us);
diff --git a/audio/out/push.c b/audio/out/push.c
index 228fa25ba4..9fb3995d11 100644
--- a/audio/out/push.c
+++ b/audio/out/push.c
@@ -28,6 +28,8 @@
#include "common/msg.h"
#include "common/common.h"
+#include "input/input.h"
+
#include "osdep/threads.h"
#include "compat/atomics.h"
@@ -37,8 +39,13 @@
struct ao_push_state {
pthread_t thread;
pthread_mutex_t lock;
+
+ // uses a separate lock to avoid lock order issues with ao_need_data()
+ pthread_mutex_t wakeup_lock;
pthread_cond_t wakeup;
+ // --- protected by lock
+
struct mp_audio_buffer *buffer;
bool terminate;
@@ -46,8 +53,20 @@ struct ao_push_state {
// Whether the current buffer contains the complete audio.
bool final_chunk;
+
+ // -- protected by wakeup_lock
+ bool need_wakeup;
};
+static void wakeup_playthread(struct ao *ao)
+{
+ struct ao_push_state *p = ao->api_priv;
+ pthread_mutex_lock(&p->wakeup_lock);
+ p->need_wakeup = true;
+ pthread_cond_signal(&p->wakeup);
+ pthread_mutex_unlock(&p->wakeup_lock);
+}
+
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
{
int r = CONTROL_UNKNOWN;
@@ -80,7 +99,7 @@ static void reset(struct ao *ao)
ao->driver->reset(ao);
mp_audio_buffer_clear(p->buffer);
p->playing = false;
- pthread_cond_signal(&p->wakeup);
+ wakeup_playthread(ao);
pthread_mutex_unlock(&p->lock);
}
@@ -91,7 +110,7 @@ static void pause(struct ao *ao)
if (ao->driver->pause)
ao->driver->pause(ao);
p->playing = false;
- pthread_cond_signal(&p->wakeup);
+ wakeup_playthread(ao);
pthread_mutex_unlock(&p->lock);
}
@@ -102,7 +121,7 @@ static void resume(struct ao *ao)
if (ao->driver->resume)
ao->driver->resume(ao);
p->playing = true; // tentatively
- pthread_cond_signal(&p->wakeup);
+ wakeup_playthread(ao);
pthread_mutex_unlock(&p->lock);
}
@@ -160,7 +179,7 @@ static int play(struct ao *ao, void **data, int samples, int flags)
p->final_chunk = !!(flags & AOPLAY_FINAL_CHUNK);
p->playing = true;
- pthread_cond_signal(&p->wakeup);
+ wakeup_playthread(ao);
pthread_mutex_unlock(&p->lock);
return write_samples;
}
@@ -196,8 +215,12 @@ static void *playthread(void *arg)
{
struct ao *ao = arg;
struct ao_push_state *p = ao->api_priv;
- pthread_mutex_lock(&p->lock);
- while (!p->terminate) {
+ while (1) {
+ pthread_mutex_lock(&p->lock);
+ if (p->terminate) {
+ pthread_mutex_unlock(&p->lock);
+ return NULL;
+ }
double timeout = 2.0;
if (p->playing) {
double min_wait = ao->device_buffer / (double)ao->samplerate;
@@ -210,20 +233,28 @@ static void *playthread(void *arg)
if (buffers_full && ao->driver->get_delay) {
float buffered_audio = ao->driver->get_delay(ao);
timeout = buffered_audio - 0.050;
- if (timeout > 0.100) {
- // Keep extra safety margin if the buffers are large
+ // Keep extra safety margin if the buffers are large
+ if (timeout > 0.100)
timeout = MPMAX(timeout - 0.200, 0.100);
- } else {
- timeout = MPMAX(timeout, min_wait);
- }
} else {
- timeout = min_wait;
+ timeout = 0;
}
+ // Half of the buffer played -> wakeup playback thread to get more.
+ if (timeout <= min_wait / 2 + 0.001)
+ mp_input_wakeup(ao->input_ctx);
+ // Avoid wasting CPU - this assumes ao_play_data() usually fills the
+ // audio buffer as far as possible, so even if the device buffer
+ // is not full, we can only wait for the core.
+ timeout = MPMAX(timeout, min_wait);
}
+ pthread_mutex_unlock(&p->lock);
+ pthread_mutex_lock(&p->wakeup_lock);
struct timespec deadline = mpthread_get_deadline(timeout);
- pthread_cond_timedwait(&p->wakeup, &p->lock, &deadline);
+ if (!p->need_wakeup)
+ pthread_cond_timedwait(&p->wakeup, &p->wakeup_lock, &deadline);
+ p->need_wakeup = false;
+ pthread_mutex_unlock(&p->wakeup_lock);
}
- pthread_mutex_unlock(&p->lock);
return NULL;
}
@@ -233,7 +264,7 @@ static void uninit(struct ao *ao)
pthread_mutex_lock(&p->lock);
p->terminate = true;
- pthread_cond_signal(&p->wakeup);
+ wakeup_playthread(ao);
pthread_mutex_unlock(&p->lock);
pthread_join(p->thread, NULL);
@@ -242,6 +273,7 @@ static void uninit(struct ao *ao)
pthread_cond_destroy(&p->wakeup);
pthread_mutex_destroy(&p->lock);
+ pthread_mutex_destroy(&p->wakeup_lock);
}
static int init(struct ao *ao)
@@ -249,6 +281,7 @@ static int init(struct ao *ao)
struct ao_push_state *p = ao->api_priv;
pthread_mutex_init(&p->lock, NULL);
+ pthread_mutex_init(&p->wakeup_lock, NULL);
pthread_cond_init(&p->wakeup, NULL);
p->buffer = mp_audio_buffer_create(ao);
@@ -276,6 +309,7 @@ const struct ao_driver ao_api_push = {
.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);
@@ -290,3 +324,14 @@ int ao_play_silence(struct ao *ao, int samples)
talloc_free(p);
return r;
}
+
+// Notify the core that new data should be sent to the AO. Normally, the core
+// uses a heuristic based on ao_delay() when to refill the buffers, but this
+// can be used to reduce wait times. Can be called from any thread.
+void ao_need_data(struct ao *ao)
+{
+ assert(ao->api == &ao_api_push);
+
+ // wakeup the play thread at least once
+ wakeup_playthread(ao);
+}