Skip to content

Commit

Permalink
feat(cmd): implemented multicast audio publishing
Browse files Browse the repository at this point in the history
  • Loading branch information
Hamdan Anwar Sayeed authored and s-hamdananwar committed Aug 17, 2024
1 parent 245fd68 commit 269202c
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 0 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,20 @@ lk room join --identity bot \
<room_name>
```
### Publish audio from UDP Multicast
It's possible to publish audio in the Opus codec from a multicast packet stream over a UDP socket. `lk` can act as a multicast listener and publish receiving audio packets. For example, if you want to publish audio packets that are being sent to multicast address `225.8.11.101` and port `9001` on a network connection `en0`,

Run `lk` like this:

```shell
lk room join --identity bot \
--publish-multicast \
--multicast-endpoint 225.8.11.101:9001 \
--multicast-network-name en0 \
<room_name>
```

### Publish streams from your application

Using unix sockets, it's also possible to publish streams from your application. The tracks need to be encoded into
Expand Down
52 changes: 52 additions & 0 deletions cmd/lk/join.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (

"github.com/pion/rtcp"
"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v3/pkg/media"
"github.com/urfave/cli/v3"

provider2 "github.com/livekit/livekit-cli/pkg/provider"
Expand Down Expand Up @@ -270,6 +271,57 @@ func publishFile(room *lksdk.Room,
return err
}

func publishMulticast(room *lksdk.Room,
multicastEndpoint string,
networkName string,
packetDuration int64,
) error {
track, err := lksdk.NewLocalTrack(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus})
if err != nil {
return err
}

_, err = room.LocalParticipant.PublishTrack(track, &lksdk.TrackPublicationOptions{
Source: livekit.TrackSource_MICROPHONE,
})
if err != nil {
return err
}

addr, err := net.ResolveUDPAddr("udp4", multicastEndpoint)
if err != nil {
return err
}
iface, err := net.InterfaceByName(networkName)
if err != nil {
return err
}

conn, err := net.ListenMulticastUDP("udp4", iface, addr)
if err != nil {
return err
}
go publishMulticastPackets(conn, track, packetDuration)
return nil
}

func publishMulticastPackets(conn *net.UDPConn,
track *lksdk.LocalTrack,
packetDuration int64,
) {
buff := make([]byte, 1500)
for {
n, _, err := conn.ReadFromUDP(buff)
if err != nil {
logger.Errorw("Read from UDP error:", err)
}

if err = track.WriteSample(media.Sample{Data: buff[:n], Duration: time.Millisecond * time.Duration(packetDuration)}, &lksdk.SampleWriteOptions{}); err != nil {
logger.Errorw("Write sample error:", err)
}
}
}

func parseSocketFromName(name string) (string, string, string, error) {
// Extract mime type, socket type, and address
// e.g. h264://192.168.0.1:1234 (tcp)
Expand Down
32 changes: 32 additions & 0 deletions cmd/lk/room.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ var (
Name: "publish-demo",
Usage: "Publish demo video as a loop",
},
&cli.BoolFlag{
Name: "publish-multicast",
Usage: "Publish multicast audio in Opus codec",
},
&cli.StringSliceFlag{
Name: "publish",
TakesFile: true,
Expand All @@ -160,6 +164,19 @@ var (
Name: "exit-after-publish",
Usage: "When publishing, exit after file or stream is complete",
},
&cli.StringFlag{
Name: "multicast-endpoint",
Usage: "Multicast `ENDPOINT` to open the UDP socket. Should be of the format <MulticastIpAddress:MulticastPort>",
},
&cli.StringFlag{
Name: "multicast-network-name",
Usage: "`NETWORK NAME` on which the UDP socket should be opened for multicast audio publishing",
},
&cli.IntFlag{
Name: "multiast-packet-duration",
Usage: "Audio packet `DURATION` in milliseconds to be published from multicast",
Value: 20,
},
},
},
{
Expand Down Expand Up @@ -814,6 +831,21 @@ func joinRoom(ctx context.Context, cmd *cli.Command) error {

signal.Notify(done, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)

if cmd.Bool("publish-multicast") {
multicastEndpoint := cmd.String("multicast-endpoint")
if multicastEndpoint == "" {
return fmt.Errorf("no multicast endpoint provided name provided")
}
networkName := cmd.String("network-name")
if networkName == "" {
return fmt.Errorf("no network name provided")
}
packetDuration := cmd.Int("packet-duration")
if err = publishMulticast(room, multicastEndpoint, networkName, packetDuration); err != nil {
return err
}
}

if cmd.Bool("publish-demo") {
if err = publishDemo(room); err != nil {
return err
Expand Down

0 comments on commit 269202c

Please sign in to comment.