diff --git a/src/pipewire/pipewire.cc b/src/pipewire/pipewire.cc index f1d58800e..ae6ea171f 100644 --- a/src/pipewire/pipewire.cc +++ b/src/pipewire/pipewire.cc @@ -28,6 +28,7 @@ #include #include +#include #include #if !PW_CHECK_VERSION(0, 3, 33) @@ -103,8 +104,9 @@ class PipeWireOutput : public OutputPlugin int m_aud_format = 0; int m_core_init_seq = 0; - unsigned char * m_buffer = nullptr; - unsigned int m_buffer_at = 0; + timespec m_time {}; + + RingBuf m_buffer; unsigned int m_buffer_size = 0; unsigned int m_frames = 0; unsigned int m_stride = 0; @@ -159,36 +161,61 @@ void PipeWireOutput::pause(bool pause) { pw_thread_loop_lock(m_loop); pw_stream_set_active(m_stream, !pause); + clock_gettime(CLOCK_REALTIME, &m_time); pw_thread_loop_unlock(m_loop); } int PipeWireOutput::get_delay() { - return (m_buffer_at / m_stride + m_frames) * 1000 / m_rate; + int buff_time = (((m_buffer.len() + m_buffer_size) / m_stride) * 1000) / m_rate; + + // Get time difference from last buffer fill + timespec time_now {}; + clock_gettime(CLOCK_REALTIME, &time_now); + + int time_diff = + (((time_now.tv_sec - m_time.tv_sec) * 1000000000) + + (time_now.tv_nsec - m_time.tv_nsec)) / 1000000; + + if (time_diff < 0) + time_diff = 0; + if (time_diff > buff_time) + time_diff = buff_time; + + return buff_time - time_diff; } void PipeWireOutput::drain() { pw_thread_loop_lock(m_loop); - if (m_buffer_at > 0) - pw_thread_loop_timed_wait(m_loop, 2); + + int buflen; + while ((buflen = m_buffer.len()) > 0) + { + pw_thread_loop_timed_wait(m_loop, 1); + if (buflen <= m_buffer.len()) + { + AUDERR("PipeWireOutput: buffer drain lock\n"); + break; + } + } pw_stream_flush(m_stream, true); - pw_thread_loop_timed_wait(m_loop, 2); + pw_thread_loop_timed_wait(m_loop, 1); pw_thread_loop_unlock(m_loop); } void PipeWireOutput::flush() { pw_thread_loop_lock(m_loop); - m_buffer_at = 0; + m_buffer.discard(); pw_thread_loop_unlock(m_loop); pw_stream_flush(m_stream, false); } void PipeWireOutput::period_wait() { - if (m_buffer_at != m_buffer_size) + if (m_buffer.space()) return; pw_thread_loop_lock(m_loop); @@ -200,12 +227,11 @@ int PipeWireOutput::write_audio(const void * data, int length) { pw_thread_loop_lock(m_loop); - auto size = aud::min(m_buffer_size - m_buffer_at, length); - memcpy(m_buffer + m_buffer_at, data, size); - m_buffer_at += size; + length = aud::min(length, m_buffer.space()); + m_buffer.copy_in(static_cast(data), length); pw_thread_loop_unlock(m_loop); - return size; + return length; } void PipeWireOutput::close_audio() @@ -248,11 +274,7 @@ void PipeWireOutput::close_audio() m_loop = nullptr; } - if (m_buffer) - { - delete[] m_buffer; - m_buffer = nullptr; - } + m_buffer.destroy(); } bool PipeWireOutput::open_audio(int format, int rate, int channels, String & error) @@ -267,6 +289,7 @@ bool PipeWireOutput::open_audio(int format, int rate, int channels, String & err return false; } + clock_gettime(CLOCK_REALTIME, &m_time); return true; } @@ -343,10 +366,9 @@ bool PipeWireOutput::init_core() return false; } + m_frames = aud_get_int("output_buffer_size") * m_rate / 1000; m_stride = FMT_SIZEOF(m_aud_format) * m_channels; - m_frames = aud::clamp(64, ceilf(2048 * m_rate / 48000.0f), 8192); - m_buffer_size = m_frames * m_stride; - m_buffer = new unsigned char[m_buffer_size]; + m_buffer.alloc(m_frames * m_stride); return true; } @@ -490,7 +512,9 @@ void PipeWireOutput::on_process(void * data) struct spa_buffer * buf; void * dst; - if (!o->m_buffer_at) + clock_gettime(CLOCK_REALTIME, &o->m_time); + + if (!o->m_buffer.len()) { pw_thread_loop_signal(o->m_loop, false); return; @@ -510,13 +534,12 @@ void PipeWireOutput::on_process(void * data) return; } - auto size = aud::min(buf->datas[0].maxsize, o->m_buffer_at); - memcpy(dst, o->m_buffer, size); - o->m_buffer_at -= size; - memmove(o->m_buffer, o->m_buffer + size, o->m_buffer_at); + auto size = aud::min(buf->datas[0].maxsize, o->m_buffer.len()); + o->m_buffer_size = size; + o->m_buffer.move_out(static_cast(dst), size); b->buffer->datas[0].chunk->offset = 0; - b->buffer->datas[0].chunk->size = o->m_buffer_size; + b->buffer->datas[0].chunk->size = size; b->buffer->datas[0].chunk->stride = o->m_stride; pw_stream_queue_buffer(o->m_stream, b); @@ -570,15 +593,18 @@ void PipeWireOutput::set_channel_map(struct spa_audio_info_raw * info, int chann info->position[8] = SPA_AUDIO_CHANNEL_RC; // Fall through case 8: + case 7: info->position[6] = SPA_AUDIO_CHANNEL_FLC; info->position[7] = SPA_AUDIO_CHANNEL_FRC; // Fall through case 6: + case 5: info->position[4] = SPA_AUDIO_CHANNEL_RL; info->position[5] = SPA_AUDIO_CHANNEL_RR; // Fall through case 4: - info->position[3] = SPA_AUDIO_CHANNEL_LFE; + if (channels != 5 && channels != 7) + info->position[3] = SPA_AUDIO_CHANNEL_LFE; // Fall through case 3: info->position[2] = SPA_AUDIO_CHANNEL_FC;