diff options
Diffstat (limited to 'soundbox')
-rw-r--r-- | soundbox/interfaces.go | 2 | ||||
-rw-r--r-- | soundbox/stream.go | 84 |
2 files changed, 69 insertions, 17 deletions
diff --git a/soundbox/interfaces.go b/soundbox/interfaces.go index 2854b1a..04839c7 100644 --- a/soundbox/interfaces.go +++ b/soundbox/interfaces.go @@ -17,7 +17,7 @@ func getInterface() (net.Interface, error) { } for _, iface := range all { - if iface.Flags & net.FlagUp == 0 { + if iface.Flags&net.FlagUp == 0 { continue } addresses, err := iface.Addrs() diff --git a/soundbox/stream.go b/soundbox/stream.go index 4b08840..0d6fe5f 100644 --- a/soundbox/stream.go +++ b/soundbox/stream.go @@ -2,15 +2,22 @@ package soundbox import ( "context" + "errors" "fmt" - "os/exec" + "io" "net" + "os/exec" + "time" ) // streamingPort is the default network port a soundbox is listening to for // incoming audio stream data. const streamingPort = 5316 +const bufferSize = 20 + +const writeTimeout = 1 * time.Second + // StreamURLContext streams audio from a given URL to one or multiple soundbox // devices. The devices are referenced via their MAC addresses given by the // targets argument. The ctx argument is passed to cancel the streaming. @@ -20,29 +27,74 @@ func StreamURLContext(ctx context.Context, url string, targets []net.HardwareAdd return err } + ips := make([]net.IP, 0) + for _, target := range targets { + ip, err := toLinkLocal(target) + if err != nil { + return err + } + ips = append(ips, ip) + } + + conns := make([]net.Conn, 0) + for _, ip := range ips { + var d net.Dialer + conn, err := d.DialContext( + ctx, + "tcp6", + fmt.Sprintf("[%s%%%s]:%d", ip, iface.Name, streamingPort), + ) + if err != nil { + return err + } + conns = append(conns, conn) + } + defer func() { + for _, conn := range conns { + conn.Close() + } + }() + args := []string{ "-re", "-i", url, + "-acodec", + "flac", + "-f", + "ogg", + "-", } - for _, target := range targets { - ip, err := toLinkLocal(target) + cmd := exec.CommandContext(ctx, "ffmpeg", args...) + stdout, err := cmd.StdoutPipe() + if err != nil { + return err + } + + err = cmd.Start() + if err != nil { + return err + } + + for { + buffer := make([]byte, bufferSize) + i, err := stdout.Read(buffer) if err != nil { - return err + if errors.Is(err, io.EOF) { + break + } else { + return err + } + } + for _, conn := range conns { + conn.SetDeadline(time.Now().Add(writeTimeout)) + _, err = conn.Write(buffer[:i]) + if err != nil { + return err + } } - - args = append(args, "-acodec") - args = append(args, "flac") - args = append(args, "-f") - args = append(args, "ogg") - args = append(args, fmt.Sprintf( - "tcp://[%s%%%s]:%d", - ip, - iface.Name, - streamingPort, - )) } - return exec.CommandContext(ctx, "ffmpeg", args...).Run() + return cmd.Wait() } |