pulseaudio: Feed audio data in response to write callbacks. Instead of waiting until the entire buffer from the SDL callback is ready to be accepted by PulseAudio, we use pa_stream_set_write_callback and feed some portion of the buffer as callbacks come in asking for more. This lets us remove the halving of the buffer size during device open, and also (hopefully) solves several strange hangs that happen in unusual circumstances. Fixes #4387 Fixes #2262
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
diff --git a/src/audio/pulseaudio/SDL_pulseaudio.c b/src/audio/pulseaudio/SDL_pulseaudio.c
index a969f2b..85e1386 100644
--- a/src/audio/pulseaudio/SDL_pulseaudio.c
+++ b/src/audio/pulseaudio/SDL_pulseaudio.c
@@ -107,8 +107,6 @@ static int (*PULSEAUDIO_pa_stream_connect_record) (pa_stream *, const char *,
static pa_stream_state_t (*PULSEAUDIO_pa_stream_get_state) (const pa_stream *);
static size_t (*PULSEAUDIO_pa_stream_writable_size) (const pa_stream *);
static size_t (*PULSEAUDIO_pa_stream_readable_size) (const pa_stream *);
-static int (*PULSEAUDIO_pa_stream_begin_write) (pa_stream *, void **, size_t*);
-static int (*PULSEAUDIO_pa_stream_cancel_write) (pa_stream *);
static int (*PULSEAUDIO_pa_stream_write) (pa_stream *, const void *, size_t,
pa_free_cb_t, int64_t, pa_seek_mode_t);
static pa_operation * (*PULSEAUDIO_pa_stream_drain) (pa_stream *,
@@ -119,6 +117,7 @@ static pa_operation * (*PULSEAUDIO_pa_stream_flush) (pa_stream *,
pa_stream_success_cb_t, void *);
static int (*PULSEAUDIO_pa_stream_disconnect) (pa_stream *);
static void (*PULSEAUDIO_pa_stream_unref) (pa_stream *);
+static void (*PULSEAUDIO_pa_stream_set_write_callback)(pa_stream *, pa_stream_request_cb_t, void *);
static int load_pulseaudio_syms(void);
@@ -222,8 +221,6 @@ load_pulseaudio_syms(void)
SDL_PULSEAUDIO_SYM(pa_stream_writable_size);
SDL_PULSEAUDIO_SYM(pa_stream_readable_size);
SDL_PULSEAUDIO_SYM(pa_stream_write);
- SDL_PULSEAUDIO_SYM(pa_stream_begin_write);
- SDL_PULSEAUDIO_SYM(pa_stream_cancel_write);
SDL_PULSEAUDIO_SYM(pa_stream_drain);
SDL_PULSEAUDIO_SYM(pa_stream_disconnect);
SDL_PULSEAUDIO_SYM(pa_stream_peek);
@@ -232,6 +229,7 @@ load_pulseaudio_syms(void)
SDL_PULSEAUDIO_SYM(pa_stream_unref);
SDL_PULSEAUDIO_SYM(pa_channel_map_init_auto);
SDL_PULSEAUDIO_SYM(pa_strerror);
+ SDL_PULSEAUDIO_SYM(pa_stream_set_write_callback);
return 0;
}
@@ -359,51 +357,56 @@ ConnectToPulseServer(pa_mainloop **_mainloop, pa_context **_context)
static void
PULSEAUDIO_WaitDevice(_THIS)
{
- struct SDL_PrivateAudioData *h = this->hidden;
+ /* this is a no-op; we wait in PULSEAUDIO_PlayDevice now. */
+}
- while (SDL_AtomicGet(&this->enabled)) {
- if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
- PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
- PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
- SDL_OpenedAudioDeviceDisconnected(this);
- return;
- }
- if (PULSEAUDIO_pa_stream_writable_size(h->stream) >= (h->mixlen/8)) {
- return;
- }
- }
+static void WriteCallback(pa_stream *p, size_t nbytes, void *userdata)
+{
+ struct SDL_PrivateAudioData *h = (struct SDL_PrivateAudioData *) userdata;
+ /*printf("PULSEAUDIO WRITE CALLBACK! nbytes=%u\n", (unsigned int) nbytes);*/
+ h->bytes_requested += nbytes;
}
static void
PULSEAUDIO_PlayDevice(_THIS)
{
- /* Write the audio data */
struct SDL_PrivateAudioData *h = this->hidden;
- if (SDL_AtomicGet(&this->enabled)) {
- if (PULSEAUDIO_pa_stream_write(h->stream, h->pabuf, h->mixlen, NULL, 0LL, PA_SEEK_RELATIVE) < 0) {
+ int available = h->mixlen;
+ int written = 0;
+ int cpy;
+
+ /*printf("PULSEAUDIO PLAYDEVICE START! mixlen=%d\n", available);*/
+
+ while (SDL_AtomicGet(&this->enabled) && (available > 0)) {
+ cpy = SDL_min(h->bytes_requested, available);
+ if (cpy) {
+ if (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf + written, cpy, NULL, 0LL, PA_SEEK_RELATIVE) < 0) {
+ SDL_OpenedAudioDeviceDisconnected(this);
+ return;
+ }
+ /*printf("PULSEAUDIO FEED! nbytes=%u\n", (unsigned int) cpy);*/
+ h->bytes_requested -= cpy;
+ written += cpy;
+ available -= cpy;
+ }
+
+ /* let WriteCallback fire if necessary. */
+ /*printf("PULSEAUDIO ITERATE!\n");*/
+ if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
+ PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
+ PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
SDL_OpenedAudioDeviceDisconnected(this);
+ return;
}
}
+
+ /*printf("PULSEAUDIO PLAYDEVICE END! written=%d\n", written);*/
}
static Uint8 *
PULSEAUDIO_GetDeviceBuf(_THIS)
{
- struct SDL_PrivateAudioData *h = this->hidden;
- size_t nbytes = h->mixlen;
- int ret;
-
- ret = PULSEAUDIO_pa_stream_begin_write(h->stream, &h->pabuf, &nbytes);
-
- if (ret != 0) {
- /* fall back it intermediate buffer */
- h->pabuf = h->mixbuf;
- } else if (nbytes < h->mixlen) {
- PULSEAUDIO_pa_stream_cancel_write(h->stream);
- h->pabuf = h->mixbuf;
- }
-
- return (Uint8 *)h->pabuf;
+ return this->hidden->mixbuf;
}
@@ -604,9 +607,6 @@ PULSEAUDIO_OpenDevice(_THIS, const char *devname)
paspec.format = format;
/* Calculate the final parameters for this audio specification */
-#ifdef PA_STREAM_ADJUST_LATENCY
- this->spec.samples /= 2; /* Mix in smaller chunck to avoid underruns */
-#endif
SDL_CalculateAudioSpec(&this->spec);
/* Allocate mixing buffer */
@@ -623,22 +623,12 @@ PULSEAUDIO_OpenDevice(_THIS, const char *devname)
paspec.rate = this->spec.freq;
/* Reduced prebuffering compared to the defaults. */
-#ifdef PA_STREAM_ADJUST_LATENCY
paattr.fragsize = this->spec.size;
- /* 2x original requested bufsize */
- paattr.tlength = h->mixlen * 4;
+ paattr.tlength = h->mixlen;
paattr.prebuf = -1;
paattr.maxlength = -1;
- /* -1 can lead to pa_stream_writable_size() >= mixlen never being true */
- paattr.minreq = h->mixlen;
- flags = PA_STREAM_ADJUST_LATENCY;
-#else
- paattr.fragsize = this->spec.size;
- paattr.tlength = h->mixlen*2;
- paattr.prebuf = h->mixlen*2;
- paattr.maxlength = h->mixlen*2;
- paattr.minreq = h->mixlen;
-#endif
+ paattr.minreq = -1;
+ flags |= PA_STREAM_ADJUST_LATENCY;
if (ConnectToPulseServer(&h->mainloop, &h->context) < 0) {
return SDL_SetError("Could not connect to PulseAudio server");
@@ -675,6 +665,7 @@ PULSEAUDIO_OpenDevice(_THIS, const char *devname)
if (iscapture) {
rc = PULSEAUDIO_pa_stream_connect_record(h->stream, h->device_name, &paattr, flags);
} else {
+ PULSEAUDIO_pa_stream_set_write_callback(h->stream, WriteCallback, h);
rc = PULSEAUDIO_pa_stream_connect_playback(h->stream, h->device_name, &paattr, flags, NULL, NULL);
}
diff --git a/src/audio/pulseaudio/SDL_pulseaudio.h b/src/audio/pulseaudio/SDL_pulseaudio.h
index a0d0b0c..7ef45fe 100644
--- a/src/audio/pulseaudio/SDL_pulseaudio.h
+++ b/src/audio/pulseaudio/SDL_pulseaudio.h
@@ -43,11 +43,7 @@ struct SDL_PrivateAudioData
Uint8 *mixbuf;
int mixlen;
- /* Pointer to the actual buffer in use in the current
- GetDeviceBuf() -> PlayDevice() iteration.
- Can be either the pointer returned by pa_stream_begin_write()
- or mixbuf */
- void *pabuf;
+ int bytes_requested; /* bytes of data the hardware wants _now_. */
const Uint8 *capturebuf;
int capturelen;