package soundbox /* #cgo pkg-config: libpipewire-0.3 #include "pipewire-binding.h" */ import "C" import ( "context" "io" "log" "net" "os/exec" "unsafe" ) 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) 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 } pwc := newPWCapture(ctx) go func() { _, err := io.Copy(stdin, pwc) if err != nil { log.Println("Failed to copy from PipeWire to ffmpeg.") } }() 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)) pwAudio <- buf }