diff options
author | xegineering <me@xegineering.eu> | 2024-12-11 20:03:29 +0100 |
---|---|---|
committer | xegineering <me@xegineering.eu> | 2024-12-15 12:36:51 +0100 |
commit | ca5ebc8a795114a72f4410f05b2270f24ead1d60 (patch) | |
tree | 5ca901ebc8365909e4fd2ec11a4cf1ff114f149d /soundbox | |
parent | 62d3045df3283872628b0b8b8a8caeef1c226dfa (diff) | |
download | soundbox-go-ca5ebc8a795114a72f4410f05b2270f24ead1d60.tar soundbox-go-ca5ebc8a795114a72f4410f05b2270f24ead1d60.tar.zst soundbox-go-ca5ebc8a795114a72f4410f05b2270f24ead1d60.zip |
pipewire: Implement capture tear-down
This closes the PipeWire process properly to not leak memory and remove
the PipeWire capture node of soundbox as soon as the context passed to
StreamPipewireContext() is closed.
Diffstat (limited to 'soundbox')
-rw-r--r-- | soundbox/pipewire-binding.c | 48 | ||||
-rw-r--r-- | soundbox/pipewire-binding.h | 5 | ||||
-rw-r--r-- | soundbox/pipewire.go | 52 |
3 files changed, 78 insertions, 27 deletions
diff --git a/soundbox/pipewire-binding.c b/soundbox/pipewire-binding.c index bd23ff0..0a31843 100644 --- a/soundbox/pipewire-binding.c +++ b/soundbox/pipewire-binding.c @@ -2,6 +2,7 @@ #include <spa/param/audio/format-utils.h> #include "pipewire-binding.h" +#include "pipewire/main-loop.h" #define SAMPLING_RATE 48000 @@ -11,15 +12,21 @@ #define STRIDE sizeof(int16_t) * CHANNELS +struct pw_go_capture { + struct pw_main_loop *loop; + struct pw_stream *stream; +}; + + static void on_process(void *userdata) { - struct pw_stream *stream = *(struct pw_stream **)userdata; + struct pw_go_capture *capture = (struct pw_go_capture *)userdata; struct pw_buffer *pw_buf; struct spa_buffer *spa_buf; int n_frames; int16_t *src; - if ((pw_buf = pw_stream_dequeue_buffer(stream)) == NULL) { + if ((pw_buf = pw_stream_dequeue_buffer(capture->stream)) == NULL) { return; } @@ -41,7 +48,7 @@ static void on_process(void *userdata) spa_buf->datas[0].chunk->stride = STRIDE; spa_buf->datas[0].chunk->size = n_frames * STRIDE; - pw_stream_queue_buffer(stream, pw_buf); + pw_stream_queue_buffer(capture->stream, pw_buf); } @@ -51,15 +58,14 @@ static const struct pw_stream_events stream_events = { }; -void pw_stdout(void) +void *pw_go_capture_init(void) { pw_init(NULL, NULL); - struct pw_main_loop *loop = pw_main_loop_new(NULL); - - struct pw_stream *stream = NULL; - stream = pw_stream_new_simple( - pw_main_loop_get_loop(loop), + struct pw_go_capture *capture = malloc(sizeof(struct pw_go_capture)); + capture->loop = pw_main_loop_new(NULL); + capture->stream = pw_stream_new_simple( + pw_main_loop_get_loop(capture->loop), NODE_NAME, pw_properties_new( PW_KEY_MEDIA_TYPE, "Audio", @@ -69,7 +75,7 @@ void pw_stdout(void) NULL ), &stream_events, - &stream + capture ); uint8_t buffer[1024]; @@ -87,7 +93,7 @@ void pw_stdout(void) }; pw_stream_connect( - stream, + capture->stream, PW_DIRECTION_INPUT, PW_ID_ANY, PW_STREAM_FLAG_MAP_BUFFERS | @@ -96,9 +102,23 @@ void pw_stdout(void) sizeof(params) / sizeof(params[0]) ); - pw_main_loop_run(loop); + return (void *)capture; +} + - pw_stream_destroy(stream); - pw_main_loop_destroy(loop); +void pw_go_capture_run(void *cdata) +{ + struct pw_go_capture *capture = cdata; + pw_main_loop_run(capture->loop); + pw_stream_destroy(capture->stream); + pw_main_loop_destroy(capture->loop); + free(capture); pw_deinit(); } + + +void pw_go_capture_deinit(void *cdata) +{ + struct pw_go_capture *capture = cdata; + pw_main_loop_quit(capture->loop); +} diff --git a/soundbox/pipewire-binding.h b/soundbox/pipewire-binding.h index d25a0b9..58ee93a 100644 --- a/soundbox/pipewire-binding.h +++ b/soundbox/pipewire-binding.h @@ -5,6 +5,9 @@ #include <stddef.h> extern void goHandleData(int16_t *, size_t); -void pw_stdout(void); + +void *pw_go_capture_init(void); +void pw_go_capture_run(void *); +void pw_go_capture_deinit(void *); #endif // !PIPEWIRE_BINDING_H diff --git a/soundbox/pipewire.go b/soundbox/pipewire.go index 00bbe49..476d2e4 100644 --- a/soundbox/pipewire.go +++ b/soundbox/pipewire.go @@ -7,7 +7,6 @@ package soundbox import "C" import ( - "bytes" "context" "io" "log" @@ -16,7 +15,41 @@ import ( "unsafe" ) -var pipewireAudio = make(chan []byte, 5) +type pwCapture struct { + cdata unsafe.Pointer +} + +var pwAudio chan []byte + +func newPWCapture(ctx context.Context) pwCapture { + pwc := pwCapture{} + + pwAudio = make(chan []byte, 5) + pwc.cdata = unsafe.Pointer(C.pw_go_capture_init()) // TODO pass &pwc.audio here + go C.pw_go_capture_run(pwc.cdata) + + go func() { + <-ctx.Done() + C.pw_go_capture_deinit(pwc.cdata) + }() + + return pwc +} + +func (pwc pwCapture) Read(p []byte) (int, error) { + select { + case chunk, ok := <-pwAudio: + if ok { + noSilence := s16leDropSilence(chunk) + i := copy(p, noSilence) + return i, nil + } else { + return 0, io.EOF + } + default: + return 0, nil + } +} func s16leDropSilence(input []byte) []byte { output := make([]byte, 0) @@ -61,16 +94,11 @@ func StreamPipewireContext(ctx context.Context, targets []net.HardwareAddr) erro return err } - go C.pw_stdout() - + pwc := newPWCapture(ctx) go func() { - for buffer := range pipewireAudio { - tempReader := bytes.NewReader(s16leDropSilence(buffer)) - _, err := io.Copy(stdin, tempReader) - if err != nil { - log.Println("Failed to copy from PipeWire to ffmpeg.") - break - } + _, err := io.Copy(stdin, pwc) + if err != nil { + log.Println("Failed to copy from PipeWire to ffmpeg.") } }() @@ -90,5 +118,5 @@ func StreamPipewireContext(ctx context.Context, targets []net.HardwareAddr) erro //export goHandleData func goHandleData(data *C.int16_t, size C.size_t) { buf := C.GoBytes(unsafe.Pointer(data), C.int(size)) - pipewireAudio <- buf + pwAudio <- buf } |