package soundbox /* #cgo pkg-config: libpipewire-0.3 #include "pipewire-binding.h" */ import "C" import ( "bytes" "context" "io" "log" "net" "os/exec" "unsafe" ) var pipewireAudio = make(chan []byte, 5) func s16leDropSilence(input []byte) []byte { output := make([]byte, 0) for i := 0; i < (len(input) - 1); i += 2 { if input[i] == byte(0) && input[i+1] == byte(0) { continue } output = append(output, input[i]) output = append(output, input[i+1]) } return output } func StreamPipewireContext(ctx context.Context, targets []net.HardwareAddr) error { cmd := exec.CommandContext( ctx, "ffmpeg", "-ac", "2", "-ar", "48000", "-f", "s16le", "-channel_layout", "stereo", "-i", "-", "-acodec", "flac", "-f", "ogg", "-", ) stdout, err := cmd.StdoutPipe() if err != nil { return err } stdin, err := cmd.StdinPipe() if err != nil { return err } go C.pw_stdout() 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 = cmd.Start() if err != nil { return err } err = streamContext(ctx, stdout, targets) if err != nil { return err } return cmd.Wait() } //export goHandleData func goHandleData(data *C.int16_t, size C.size_t) { buf := C.GoBytes(unsafe.Pointer(data), C.int(size)) pipewireAudio <- buf }