Skip to content

Commit 269202c

Browse files
Hamdan Anwar Sayeeds-hamdananwar
authored andcommitted
feat(cmd): implemented multicast audio publishing
1 parent 245fd68 commit 269202c

File tree

3 files changed

+98
-0
lines changed

3 files changed

+98
-0
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,20 @@ lk room join --identity bot \
151151
<room_name>
152152
```
153153
154+
### Publish audio from UDP Multicast
155+
156+
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`,
157+
158+
Run `lk` like this:
159+
160+
```shell
161+
lk room join --identity bot \
162+
--publish-multicast \
163+
--multicast-endpoint 225.8.11.101:9001 \
164+
--multicast-network-name en0 \
165+
<room_name>
166+
```
167+
154168
### Publish streams from your application
155169

156170
Using unix sockets, it's also possible to publish streams from your application. The tracks need to be encoded into

cmd/lk/join.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828

2929
"github.com/pion/rtcp"
3030
"github.com/pion/webrtc/v3"
31+
"github.com/pion/webrtc/v3/pkg/media"
3132
"github.com/urfave/cli/v3"
3233

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

274+
func publishMulticast(room *lksdk.Room,
275+
multicastEndpoint string,
276+
networkName string,
277+
packetDuration int64,
278+
) error {
279+
track, err := lksdk.NewLocalTrack(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus})
280+
if err != nil {
281+
return err
282+
}
283+
284+
_, err = room.LocalParticipant.PublishTrack(track, &lksdk.TrackPublicationOptions{
285+
Source: livekit.TrackSource_MICROPHONE,
286+
})
287+
if err != nil {
288+
return err
289+
}
290+
291+
addr, err := net.ResolveUDPAddr("udp4", multicastEndpoint)
292+
if err != nil {
293+
return err
294+
}
295+
iface, err := net.InterfaceByName(networkName)
296+
if err != nil {
297+
return err
298+
}
299+
300+
conn, err := net.ListenMulticastUDP("udp4", iface, addr)
301+
if err != nil {
302+
return err
303+
}
304+
go publishMulticastPackets(conn, track, packetDuration)
305+
return nil
306+
}
307+
308+
func publishMulticastPackets(conn *net.UDPConn,
309+
track *lksdk.LocalTrack,
310+
packetDuration int64,
311+
) {
312+
buff := make([]byte, 1500)
313+
for {
314+
n, _, err := conn.ReadFromUDP(buff)
315+
if err != nil {
316+
logger.Errorw("Read from UDP error:", err)
317+
}
318+
319+
if err = track.WriteSample(media.Sample{Data: buff[:n], Duration: time.Millisecond * time.Duration(packetDuration)}, &lksdk.SampleWriteOptions{}); err != nil {
320+
logger.Errorw("Write sample error:", err)
321+
}
322+
}
323+
}
324+
273325
func parseSocketFromName(name string) (string, string, string, error) {
274326
// Extract mime type, socket type, and address
275327
// e.g. h264://192.168.0.1:1234 (tcp)

cmd/lk/room.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@ var (
145145
Name: "publish-demo",
146146
Usage: "Publish demo video as a loop",
147147
},
148+
&cli.BoolFlag{
149+
Name: "publish-multicast",
150+
Usage: "Publish multicast audio in Opus codec",
151+
},
148152
&cli.StringSliceFlag{
149153
Name: "publish",
150154
TakesFile: true,
@@ -160,6 +164,19 @@ var (
160164
Name: "exit-after-publish",
161165
Usage: "When publishing, exit after file or stream is complete",
162166
},
167+
&cli.StringFlag{
168+
Name: "multicast-endpoint",
169+
Usage: "Multicast `ENDPOINT` to open the UDP socket. Should be of the format <MulticastIpAddress:MulticastPort>",
170+
},
171+
&cli.StringFlag{
172+
Name: "multicast-network-name",
173+
Usage: "`NETWORK NAME` on which the UDP socket should be opened for multicast audio publishing",
174+
},
175+
&cli.IntFlag{
176+
Name: "multiast-packet-duration",
177+
Usage: "Audio packet `DURATION` in milliseconds to be published from multicast",
178+
Value: 20,
179+
},
163180
},
164181
},
165182
{
@@ -814,6 +831,21 @@ func joinRoom(ctx context.Context, cmd *cli.Command) error {
814831

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

834+
if cmd.Bool("publish-multicast") {
835+
multicastEndpoint := cmd.String("multicast-endpoint")
836+
if multicastEndpoint == "" {
837+
return fmt.Errorf("no multicast endpoint provided name provided")
838+
}
839+
networkName := cmd.String("network-name")
840+
if networkName == "" {
841+
return fmt.Errorf("no network name provided")
842+
}
843+
packetDuration := cmd.Int("packet-duration")
844+
if err = publishMulticast(room, multicastEndpoint, networkName, packetDuration); err != nil {
845+
return err
846+
}
847+
}
848+
817849
if cmd.Bool("publish-demo") {
818850
if err = publishDemo(room); err != nil {
819851
return err

0 commit comments

Comments
 (0)