output/pipewire: call pw_stream_flush() only if really draining
If draining was not requested, generate silence instead if there is no data in the ring buffer. The problem is that pw_stream_flush() appears to disable the stream permanently, even though there is no state_changed callback - the stream state remains at PW_STREAM_STATE_STREAMING, but the stream is defunct. I have no idea why and I havn't found any documentation about it. Closes https://github.com/MusicPlayerDaemon/MPD/issues/1219
This commit is contained in:
parent
d33aa01000
commit
8a243e6e28
|
@ -22,6 +22,11 @@
|
||||||
#include "../OutputAPI.hxx"
|
#include "../OutputAPI.hxx"
|
||||||
#include "../Error.hxx"
|
#include "../Error.hxx"
|
||||||
#include "mixer/plugins/PipeWireMixerPlugin.hxx"
|
#include "mixer/plugins/PipeWireMixerPlugin.hxx"
|
||||||
|
#include "pcm/Silence.hxx"
|
||||||
|
#include "util/Domain.hxx"
|
||||||
|
#include "util/ScopeExit.hxx"
|
||||||
|
#include "util/WritableBuffer.hxx"
|
||||||
|
#include "Log.hxx"
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
|
@ -43,6 +48,8 @@
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
static constexpr Domain pipewire_output_domain("pipewire_output");
|
||||||
|
|
||||||
class PipeWireOutput final : AudioOutput {
|
class PipeWireOutput final : AudioOutput {
|
||||||
const char *const name;
|
const char *const name;
|
||||||
|
|
||||||
|
@ -60,6 +67,11 @@ class PipeWireOutput final : AudioOutput {
|
||||||
|
|
||||||
float volume = 1.0;
|
float volume = 1.0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The active sample format, needed for PcmSilence().
|
||||||
|
*/
|
||||||
|
SampleFormat sample_format;
|
||||||
|
|
||||||
bool disconnected;
|
bool disconnected;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -72,6 +84,14 @@ class PipeWireOutput final : AudioOutput {
|
||||||
|
|
||||||
bool interrupted;
|
bool interrupted;
|
||||||
bool paused;
|
bool paused;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Has Drain() been called? This causes Process() to invoke
|
||||||
|
* pw_stream_flush() to drain PipeWire as soon as the
|
||||||
|
* #ring_buffer has been drained.
|
||||||
|
*/
|
||||||
|
bool drain_requested;
|
||||||
|
|
||||||
bool drained;
|
bool drained;
|
||||||
|
|
||||||
explicit PipeWireOutput(const ConfigBlock &block);
|
explicit PipeWireOutput(const ConfigBlock &block);
|
||||||
|
@ -313,6 +333,7 @@ PipeWireOutput::Open(AudioFormat &audio_format)
|
||||||
disconnected = false;
|
disconnected = false;
|
||||||
restore_volume = true;
|
restore_volume = true;
|
||||||
paused = false;
|
paused = false;
|
||||||
|
drain_requested = false;
|
||||||
drained = true;
|
drained = true;
|
||||||
|
|
||||||
auto props = pw_properties_new(PW_KEY_MEDIA_TYPE, "Audio",
|
auto props = pw_properties_new(PW_KEY_MEDIA_TYPE, "Audio",
|
||||||
|
@ -335,6 +356,7 @@ PipeWireOutput::Open(AudioFormat &audio_format)
|
||||||
auto raw = ToPipeWireAudioFormat(audio_format);
|
auto raw = ToPipeWireAudioFormat(audio_format);
|
||||||
|
|
||||||
frame_size = audio_format.GetFrameSize();
|
frame_size = audio_format.GetFrameSize();
|
||||||
|
sample_format = audio_format.format;
|
||||||
interrupted = false;
|
interrupted = false;
|
||||||
|
|
||||||
/* allocate a ring buffer of 1 second */
|
/* allocate a ring buffer of 1 second */
|
||||||
|
@ -409,10 +431,17 @@ PipeWireOutput::Process() noexcept
|
||||||
|
|
||||||
size_t nbytes = ring_buffer->pop(dest, max_size);
|
size_t nbytes = ring_buffer->pop(dest, max_size);
|
||||||
if (nbytes == 0) {
|
if (nbytes == 0) {
|
||||||
|
if (drain_requested) {
|
||||||
pw_stream_flush(stream, true);
|
pw_stream_flush(stream, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* buffer underrun: generate some silence */
|
||||||
|
PcmSilence({dest, max_size}, sample_format);
|
||||||
|
|
||||||
|
LogWarning(pipewire_output_domain, "Decoder is too slow; playing silence to avoid xrun");
|
||||||
|
}
|
||||||
|
|
||||||
buf->datas[0].chunk->offset = 0;
|
buf->datas[0].chunk->offset = 0;
|
||||||
buf->datas[0].chunk->stride = frame_size;
|
buf->datas[0].chunk->stride = frame_size;
|
||||||
buf->datas[0].chunk->size = nbytes;
|
buf->datas[0].chunk->size = nbytes;
|
||||||
|
@ -454,6 +483,9 @@ PipeWireOutput::Drain()
|
||||||
{
|
{
|
||||||
const PipeWire::ThreadLoopLock lock(thread_loop);
|
const PipeWire::ThreadLoopLock lock(thread_loop);
|
||||||
|
|
||||||
|
drain_requested = true;
|
||||||
|
AtScopeExit(this) { drain_requested = false; };
|
||||||
|
|
||||||
while (!drained && !interrupted) {
|
while (!drained && !interrupted) {
|
||||||
CheckThrowError();
|
CheckThrowError();
|
||||||
pw_thread_loop_wait(thread_loop);
|
pw_thread_loop_wait(thread_loop);
|
||||||
|
|
Loading…
Reference in New Issue