summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxegineering <me@xegineering.eu>2024-11-27 21:29:39 +0100
committerxegineering <me@xegineering.eu>2024-11-27 21:38:16 +0100
commit6b7da29eea100b92659a3f1f5df6958ebad544c8 (patch)
tree11684e238ee86109423b564a91f5804bc3bf499e
parentd4cf5ed8e38d1e5d0b8110e8959e002c216f073f (diff)
downloadsoundbox-go-6b7da29eea100b92659a3f1f5df6958ebad544c8.tar
soundbox-go-6b7da29eea100b92659a3f1f5df6958ebad544c8.tar.zst
soundbox-go-6b7da29eea100b92659a3f1f5df6958ebad544c8.zip
Introduce io.Reader-based streamContext()
This prepares the switch to adding more sources than web URLs. Everything providing an io.Reader can then simply use this internal function in the background to avoid code duplication.
-rw-r--r--soundbox/network.go6
-rw-r--r--soundbox/streaming.go50
-rw-r--r--soundbox/url.go45
3 files changed, 57 insertions, 44 deletions
diff --git a/soundbox/network.go b/soundbox/network.go
index 6e7acea..11d9362 100644
--- a/soundbox/network.go
+++ b/soundbox/network.go
@@ -7,6 +7,10 @@ import (
"time"
)
+// streamingPort is the default network port a soundbox is listening to for
+// incoming audio stream data.
+const streamingPort = 5316
+
const dialTimeoutSeconds = 3
// toLinkLocal converts a MAC address to the corresponding IPv6 link-local
@@ -34,7 +38,7 @@ func dialContext(ctx context.Context, ha net.HardwareAddr) (net.Conn, error) {
}
c := make(chan net.Conn)
- dialContext, cancel := context.WithTimeout(ctx, dialTimeoutSeconds * time.Second)
+ dialContext, cancel := context.WithTimeout(ctx, dialTimeoutSeconds*time.Second)
defer cancel()
for _, iface := range ifaces {
go func() {
diff --git a/soundbox/streaming.go b/soundbox/streaming.go
new file mode 100644
index 0000000..5852dcf
--- /dev/null
+++ b/soundbox/streaming.go
@@ -0,0 +1,50 @@
+package soundbox
+
+import (
+ "context"
+ "errors"
+ "io"
+ "net"
+ "time"
+)
+
+const bufferSize = 20
+
+const writeTimeout = 1 * time.Second
+
+func streamContext(ctx context.Context, r io.Reader, 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()
+ }
+ }()
+
+ for {
+ buffer := make([]byte, bufferSize)
+ i, err := r.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 nil
+}
diff --git a/soundbox/url.go b/soundbox/url.go
index 4ad9908..6fed442 100644
--- a/soundbox/url.go
+++ b/soundbox/url.go
@@ -2,39 +2,14 @@ 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",
@@ -47,6 +22,7 @@ func StreamURLContext(ctx context.Context, url string, targets []net.HardwareAdd
"ogg",
"-",
)
+
stdout, err := cmd.StdoutPipe()
if err != nil {
return err
@@ -57,24 +33,7 @@ func StreamURLContext(ctx context.Context, url string, targets []net.HardwareAdd
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
- }
- }
- }
+ streamContext(ctx, stdout, targets)
return cmd.Wait()
}