diff options
Diffstat (limited to 'soundbox/url.go')
-rw-r--r-- | soundbox/url.go | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/soundbox/url.go b/soundbox/url.go new file mode 100644 index 0000000..4ad9908 --- /dev/null +++ b/soundbox/url.go @@ -0,0 +1,80 @@ +package soundbox + +import ( + "context" + "errors" + "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. +func StreamURLContext(ctx context.Context, url string, targets []net.HardwareAddr) error { + conns := make([]net.Conn, 0) + for _, target := range targets { + conn, err := dialContext(ctx, target) + if err != nil { + return err + } + conns = append(conns, conn) + } + defer func() { + for _, conn := range conns { + conn.Close() + } + }() + + cmd := exec.CommandContext( + ctx, + "ffmpeg", + "-re", + "-i", + url, + "-acodec", + "flac", + "-f", + "ogg", + "-", + ) + 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 { + 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 + } + } + } + + return cmd.Wait() +} |