mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-12-22 22:01:46 +00:00
[client] audio/pw: flush playback buffers before stopping
This stops the end of the playback from being truncated. It also prevents an audible glitch when playback next starts due to the truncated data being left behind in the ring buffer.
This commit is contained in:
parent
b9c646074d
commit
4c389a9274
1 changed files with 87 additions and 10 deletions
|
@ -29,6 +29,16 @@
|
||||||
#include "common/ringbuffer.h"
|
#include "common/ringbuffer.h"
|
||||||
#include "common/util.h"
|
#include "common/util.h"
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
STREAM_STATE_INACTIVE,
|
||||||
|
STREAM_STATE_ACTIVE,
|
||||||
|
STREAM_STATE_FLUSHING,
|
||||||
|
STREAM_STATE_DRAINING,
|
||||||
|
STREAM_STATE_RESTARTING
|
||||||
|
}
|
||||||
|
StreamState;
|
||||||
|
|
||||||
struct PipeWire
|
struct PipeWire
|
||||||
{
|
{
|
||||||
struct pw_loop * loop;
|
struct pw_loop * loop;
|
||||||
|
@ -43,7 +53,7 @@ struct PipeWire
|
||||||
int stride;
|
int stride;
|
||||||
|
|
||||||
RingBuffer buffer;
|
RingBuffer buffer;
|
||||||
bool active;
|
StreamState state;
|
||||||
}
|
}
|
||||||
playback;
|
playback;
|
||||||
|
|
||||||
|
@ -68,7 +78,17 @@ static void pipewire_onPlaybackProcess(void * userdata)
|
||||||
struct pw_buffer * pbuf;
|
struct pw_buffer * pbuf;
|
||||||
|
|
||||||
if (!ringbuffer_getCount(pw.playback.buffer))
|
if (!ringbuffer_getCount(pw.playback.buffer))
|
||||||
|
{
|
||||||
|
if (pw.playback.state == STREAM_STATE_FLUSHING)
|
||||||
|
{
|
||||||
|
pw_thread_loop_lock(pw.thread);
|
||||||
|
pw_stream_flush(pw.playback.stream, true);
|
||||||
|
pw.playback.state = STREAM_STATE_DRAINING;
|
||||||
|
pw_thread_loop_unlock(pw.thread);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(pbuf = pw_stream_dequeue_buffer(pw.playback.stream)))
|
if (!(pbuf = pw_stream_dequeue_buffer(pw.playback.stream)))
|
||||||
{
|
{
|
||||||
|
@ -93,6 +113,26 @@ static void pipewire_onPlaybackProcess(void * userdata)
|
||||||
pw_stream_queue_buffer(pw.playback.stream, pbuf);
|
pw_stream_queue_buffer(pw.playback.stream, pbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pipewire_onPlaybackDrained(void * userdata)
|
||||||
|
{
|
||||||
|
pw_thread_loop_lock(pw.thread);
|
||||||
|
|
||||||
|
if (pw.playback.state == STREAM_STATE_RESTARTING)
|
||||||
|
{
|
||||||
|
// A play command was received while we were in the middle of stopping;
|
||||||
|
// switch straight back into playing
|
||||||
|
pw_stream_set_active(pw.playback.stream, true);
|
||||||
|
pw.playback.state = STREAM_STATE_ACTIVE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pw_stream_set_active(pw.playback.stream, false);
|
||||||
|
pw.playback.state = STREAM_STATE_INACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
pw_thread_loop_unlock(pw.thread);
|
||||||
|
}
|
||||||
|
|
||||||
static bool pipewire_init(void)
|
static bool pipewire_init(void)
|
||||||
{
|
{
|
||||||
pw_init(NULL, NULL);
|
pw_init(NULL, NULL);
|
||||||
|
@ -138,7 +178,6 @@ static void pipewire_playbackStopStream(void)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pw_thread_loop_lock(pw.thread);
|
pw_thread_loop_lock(pw.thread);
|
||||||
pw_stream_flush(pw.playback.stream, true);
|
|
||||||
pw_stream_destroy(pw.playback.stream);
|
pw_stream_destroy(pw.playback.stream);
|
||||||
pw.playback.stream = NULL;
|
pw.playback.stream = NULL;
|
||||||
pw_thread_loop_unlock(pw.thread);
|
pw_thread_loop_unlock(pw.thread);
|
||||||
|
@ -152,7 +191,8 @@ static void pipewire_playbackStart(int channels, int sampleRate)
|
||||||
static const struct pw_stream_events events =
|
static const struct pw_stream_events events =
|
||||||
{
|
{
|
||||||
.version = PW_VERSION_STREAM_EVENTS,
|
.version = PW_VERSION_STREAM_EVENTS,
|
||||||
.process = pipewire_onPlaybackProcess
|
.process = pipewire_onPlaybackProcess,
|
||||||
|
.drained = pipewire_onPlaybackDrained
|
||||||
};
|
};
|
||||||
|
|
||||||
if (pw.playback.stream &&
|
if (pw.playback.stream &&
|
||||||
|
@ -217,23 +257,61 @@ static void pipewire_playbackPlay(uint8_t * data, size_t size)
|
||||||
|
|
||||||
ringbuffer_append(pw.playback.buffer, data, size / pw.playback.stride);
|
ringbuffer_append(pw.playback.buffer, data, size / pw.playback.stride);
|
||||||
|
|
||||||
if (!pw.playback.active)
|
if (pw.playback.state != STREAM_STATE_ACTIVE &&
|
||||||
|
pw.playback.state != STREAM_STATE_RESTARTING)
|
||||||
{
|
{
|
||||||
pw_thread_loop_lock(pw.thread);
|
pw_thread_loop_lock(pw.thread);
|
||||||
pw_stream_set_active(pw.playback.stream, true);
|
|
||||||
pw.playback.active = true;
|
switch (pw.playback.state) {
|
||||||
|
case STREAM_STATE_INACTIVE:
|
||||||
|
pw_stream_set_active(pw.playback.stream, true);
|
||||||
|
pw.playback.state = STREAM_STATE_ACTIVE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STREAM_STATE_FLUSHING:
|
||||||
|
// We were preparing to stop; just carry on as if nothing happened
|
||||||
|
pw.playback.state = STREAM_STATE_ACTIVE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STREAM_STATE_DRAINING:
|
||||||
|
// We are in the middle of draining the PipeWire buffers; we will need
|
||||||
|
// to reactivate the stream once this has completed
|
||||||
|
pw.playback.state = STREAM_STATE_RESTARTING;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DEBUG_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
pw_thread_loop_unlock(pw.thread);
|
pw_thread_loop_unlock(pw.thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pipewire_playbackStop(void)
|
static void pipewire_playbackStop(void)
|
||||||
{
|
{
|
||||||
if (!pw.playback.active)
|
if (pw.playback.state != STREAM_STATE_ACTIVE &&
|
||||||
|
pw.playback.state != STREAM_STATE_RESTARTING)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pw_thread_loop_lock(pw.thread);
|
pw_thread_loop_lock(pw.thread);
|
||||||
pw_stream_set_active(pw.playback.stream, false);
|
|
||||||
pw.playback.active = false;
|
switch (pw.playback.state)
|
||||||
|
{
|
||||||
|
case STREAM_STATE_ACTIVE:
|
||||||
|
pw.playback.state = STREAM_STATE_FLUSHING;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STREAM_STATE_RESTARTING:
|
||||||
|
// A stop was requested, and then a start while PipeWire was draining, and
|
||||||
|
// now another stop. PipeWire hasn't finished draining yet so just switch
|
||||||
|
// the state back
|
||||||
|
pw.playback.state = STREAM_STATE_DRAINING;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DEBUG_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
pw_thread_loop_unlock(pw.thread);
|
pw_thread_loop_unlock(pw.thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,7 +343,6 @@ static void pipewire_recordStopStream(void)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pw_thread_loop_lock(pw.thread);
|
pw_thread_loop_lock(pw.thread);
|
||||||
pw_stream_flush(pw.record.stream, true);
|
|
||||||
pw_stream_destroy(pw.record.stream);
|
pw_stream_destroy(pw.record.stream);
|
||||||
pw.record.stream = NULL;
|
pw.record.stream = NULL;
|
||||||
pw_thread_loop_unlock(pw.thread);
|
pw_thread_loop_unlock(pw.thread);
|
||||||
|
|
Loading…
Reference in a new issue