Skip to content

Commit f14fe1e

Browse files
pipewire: Revise plugin code. Closes: #1401
- Use RingBuf from audcore - Respect output_buffer_size setting - Improve get_delay() and drain() functions - Add support for 5 and 7 channels Co-authored-by: Thomas Lange <[email protected]>
1 parent d306f1a commit f14fe1e

File tree

1 file changed

+68
-29
lines changed

1 file changed

+68
-29
lines changed

src/pipewire/pipewire.cc

+68-29
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,28 @@
2828

2929
#include <libaudcore/i18n.h>
3030
#include <libaudcore/plugin.h>
31+
#include <libaudcore/ringbuf.h>
3132
#include <libaudcore/runtime.h>
3233

3334
#if !PW_CHECK_VERSION(0, 3, 33)
34-
#define PW_KEY_NODE_RATE "node.rate"
35+
#define PW_KEY_NODE_RATE "node.rate"
36+
#endif
37+
38+
#if !PW_CHECK_VERSION(0, 3, 50)
39+
static inline int pw_stream_get_time_n(struct pw_stream * stream,
40+
struct pw_time * time, size_t size)
41+
{
42+
return pw_stream_get_time(stream, time);
43+
}
44+
#endif
45+
46+
#if !PW_CHECK_VERSION(1, 0, 4)
47+
static uint64_t pw_stream_get_nsec(struct pw_stream * stream)
48+
{
49+
struct timespec ts;
50+
clock_gettime(CLOCK_MONOTONIC, &ts);
51+
return SPA_TIMESPEC_TO_NSEC(&ts);
52+
}
3553
#endif
3654

3755
class PipeWireOutput : public OutputPlugin
@@ -103,9 +121,8 @@ class PipeWireOutput : public OutputPlugin
103121
int m_aud_format = 0;
104122
int m_core_init_seq = 0;
105123

106-
unsigned char * m_buffer = nullptr;
107-
unsigned int m_buffer_at = 0;
108-
unsigned int m_buffer_size = 0;
124+
RingBuf<unsigned char> m_buffer;
125+
unsigned int m_pw_buffer_size = 0;
109126
unsigned int m_frames = 0;
110127
unsigned int m_stride = 0;
111128
unsigned int m_rate = 0;
@@ -164,31 +181,57 @@ void PipeWireOutput::pause(bool pause)
164181

165182
int PipeWireOutput::get_delay()
166183
{
167-
return (m_buffer_at / m_stride + m_frames) * 1000 / m_rate;
184+
int buff_time = ((m_buffer.len() / m_stride) * 1000) / m_rate;
185+
int pw_buff_time = ((m_pw_buffer_size / m_stride) * 1000) / m_rate;
186+
int time_diff = 0;
187+
int add_delay = 0;
188+
189+
// Get time difference from updated time snapshot of the stream
190+
struct pw_time time;
191+
if (pw_stream_get_time_n(m_stream, &time, sizeof time) == 0)
192+
{
193+
time_diff = (pw_stream_get_nsec(m_stream) - time.now) / SPA_NSEC_PER_MSEC;
194+
time_diff = aud::clamp(time_diff, 0, pw_buff_time);
195+
add_delay = (time.buffered + time.queued) * 1000 / m_rate;
196+
197+
if (time.rate.denom > 0)
198+
add_delay += time.delay * 1000 * time.rate.num / time.rate.denom;
199+
}
200+
201+
return buff_time + pw_buff_time - time_diff + add_delay;
168202
}
169203

170204
void PipeWireOutput::drain()
171205
{
172206
pw_thread_loop_lock(m_loop);
173-
if (m_buffer_at > 0)
174-
pw_thread_loop_timed_wait(m_loop, 2);
207+
208+
int buflen;
209+
while ((buflen = m_buffer.len()) > 0)
210+
{
211+
pw_thread_loop_timed_wait(m_loop, 1);
212+
if (buflen <= m_buffer.len())
213+
{
214+
AUDERR("PipeWireOutput: buffer drain lock\n");
215+
break;
216+
}
217+
}
175218

176219
pw_stream_flush(m_stream, true);
177-
pw_thread_loop_timed_wait(m_loop, 2);
220+
pw_thread_loop_timed_wait(m_loop, 1); // trigger on_drained() callback
178221
pw_thread_loop_unlock(m_loop);
179222
}
180223

181224
void PipeWireOutput::flush()
182225
{
183226
pw_thread_loop_lock(m_loop);
184-
m_buffer_at = 0;
227+
m_buffer.discard();
185228
pw_thread_loop_unlock(m_loop);
186229
pw_stream_flush(m_stream, false);
187230
}
188231

189232
void PipeWireOutput::period_wait()
190233
{
191-
if (m_buffer_at != m_buffer_size)
234+
if (m_buffer.space())
192235
return;
193236

194237
pw_thread_loop_lock(m_loop);
@@ -200,12 +243,11 @@ int PipeWireOutput::write_audio(const void * data, int length)
200243
{
201244
pw_thread_loop_lock(m_loop);
202245

203-
auto size = aud::min<size_t>(m_buffer_size - m_buffer_at, length);
204-
memcpy(m_buffer + m_buffer_at, data, size);
205-
m_buffer_at += size;
246+
length = aud::min(length, m_buffer.space());
247+
m_buffer.copy_in(static_cast<const unsigned char *>(data), length);
206248

207249
pw_thread_loop_unlock(m_loop);
208-
return size;
250+
return length;
209251
}
210252

211253
void PipeWireOutput::close_audio()
@@ -248,11 +290,7 @@ void PipeWireOutput::close_audio()
248290
m_loop = nullptr;
249291
}
250292

251-
if (m_buffer)
252-
{
253-
delete[] m_buffer;
254-
m_buffer = nullptr;
255-
}
293+
m_buffer.destroy();
256294
}
257295

258296
bool PipeWireOutput::open_audio(int format, int rate, int channels, String & error)
@@ -343,10 +381,9 @@ bool PipeWireOutput::init_core()
343381
return false;
344382
}
345383

384+
m_frames = aud_get_int("output_buffer_size") * m_rate / 1000;
346385
m_stride = FMT_SIZEOF(m_aud_format) * m_channels;
347-
m_frames = aud::clamp<int>(64, ceilf(2048 * m_rate / 48000.0f), 8192);
348-
m_buffer_size = m_frames * m_stride;
349-
m_buffer = new unsigned char[m_buffer_size];
386+
m_buffer.alloc(m_frames * m_stride);
350387

351388
return true;
352389
}
@@ -490,7 +527,7 @@ void PipeWireOutput::on_process(void * data)
490527
struct spa_buffer * buf;
491528
void * dst;
492529

493-
if (!o->m_buffer_at)
530+
if (!o->m_buffer.len())
494531
{
495532
pw_thread_loop_signal(o->m_loop, false);
496533
return;
@@ -510,13 +547,12 @@ void PipeWireOutput::on_process(void * data)
510547
return;
511548
}
512549

513-
auto size = aud::min<uint32_t>(buf->datas[0].maxsize, o->m_buffer_at);
514-
memcpy(dst, o->m_buffer, size);
515-
o->m_buffer_at -= size;
516-
memmove(o->m_buffer, o->m_buffer + size, o->m_buffer_at);
550+
auto size = aud::min<uint32_t>(buf->datas[0].maxsize, o->m_buffer.len());
551+
o->m_pw_buffer_size = size;
552+
o->m_buffer.move_out(static_cast<unsigned char *>(dst), size);
517553

518554
b->buffer->datas[0].chunk->offset = 0;
519-
b->buffer->datas[0].chunk->size = o->m_buffer_size;
555+
b->buffer->datas[0].chunk->size = size;
520556
b->buffer->datas[0].chunk->stride = o->m_stride;
521557

522558
pw_stream_queue_buffer(o->m_stream, b);
@@ -570,15 +606,18 @@ void PipeWireOutput::set_channel_map(struct spa_audio_info_raw * info, int chann
570606
info->position[8] = SPA_AUDIO_CHANNEL_RC;
571607
// Fall through
572608
case 8:
609+
case 7:
573610
info->position[6] = SPA_AUDIO_CHANNEL_FLC;
574611
info->position[7] = SPA_AUDIO_CHANNEL_FRC;
575612
// Fall through
576613
case 6:
614+
case 5:
577615
info->position[4] = SPA_AUDIO_CHANNEL_RL;
578616
info->position[5] = SPA_AUDIO_CHANNEL_RR;
579617
// Fall through
580618
case 4:
581-
info->position[3] = SPA_AUDIO_CHANNEL_LFE;
619+
if (channels != 5 && channels != 7)
620+
info->position[3] = SPA_AUDIO_CHANNEL_LFE;
582621
// Fall through
583622
case 3:
584623
info->position[2] = SPA_AUDIO_CHANNEL_FC;

0 commit comments

Comments
 (0)