Files
webrtc/examples/ortc/main.go
Sean DuBois e409a1d9cc Add ORTC Media examples
Resolves #379
2023-09-17 00:11:53 -04:00

187 lines
4.4 KiB
Go

// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build !js
// +build !js
// ortc demonstrates Pion WebRTC's ORTC capabilities.
package main
import (
"flag"
"fmt"
"time"
"github.com/pion/webrtc/v4"
"github.com/pion/webrtc/v4/examples/internal/signal"
)
func main() {
isOffer := flag.Bool("offer", false, "Act as the offerer if set")
port := flag.Int("port", 8080, "http server port")
flag.Parse()
// Everything below is the Pion WebRTC (ORTC) API! Thanks for using it ❤️.
// Prepare ICE gathering options
iceOptions := webrtc.ICEGatherOptions{
ICEServers: []webrtc.ICEServer{
{URLs: []string{"stun:stun.l.google.com:19302"}},
},
}
// Create an API object
api := webrtc.NewAPI()
// Create the ICE gatherer
gatherer, err := api.NewICEGatherer(iceOptions)
if err != nil {
panic(err)
}
// Construct the ICE transport
ice := api.NewICETransport(gatherer)
// Construct the DTLS transport
dtls, err := api.NewDTLSTransport(ice, nil)
if err != nil {
panic(err)
}
// Construct the SCTP transport
sctp := api.NewSCTPTransport(dtls)
// Handle incoming data channels
sctp.OnDataChannel(func(channel *webrtc.DataChannel) {
fmt.Printf("New DataChannel %s %d\n", channel.Label(), channel.ID())
// Register the handlers
channel.OnOpen(handleOnOpen(channel))
channel.OnMessage(func(msg webrtc.DataChannelMessage) {
fmt.Printf("Message from DataChannel '%s': '%s'\n", channel.Label(), string(msg.Data))
})
})
gatherFinished := make(chan struct{})
gatherer.OnLocalCandidate(func(i *webrtc.ICECandidate) {
if i == nil {
close(gatherFinished)
}
})
// Gather candidates
if err = gatherer.Gather(); err != nil {
panic(err)
}
<-gatherFinished
iceCandidates, err := gatherer.GetLocalCandidates()
if err != nil {
panic(err)
}
iceParams, err := gatherer.GetLocalParameters()
if err != nil {
panic(err)
}
dtlsParams, err := dtls.GetLocalParameters()
if err != nil {
panic(err)
}
sctpCapabilities := sctp.GetCapabilities()
s := Signal{
ICECandidates: iceCandidates,
ICEParameters: iceParams,
DTLSParameters: dtlsParams,
SCTPCapabilities: sctpCapabilities,
}
iceRole := webrtc.ICERoleControlled
// Exchange the information
fmt.Println(signal.Encode(s))
remoteSignal := Signal{}
if *isOffer {
signalingChan := signal.HTTPSDPServer(*port)
signal.Decode(<-signalingChan, &remoteSignal)
iceRole = webrtc.ICERoleControlling
} else {
signal.Decode(signal.MustReadStdin(), &remoteSignal)
}
if err = ice.SetRemoteCandidates(remoteSignal.ICECandidates); err != nil {
panic(err)
}
// Start the ICE transport
err = ice.Start(nil, remoteSignal.ICEParameters, &iceRole)
if err != nil {
panic(err)
}
// Start the DTLS transport
if err = dtls.Start(remoteSignal.DTLSParameters); err != nil {
panic(err)
}
// Start the SCTP transport
if err = sctp.Start(remoteSignal.SCTPCapabilities); err != nil {
panic(err)
}
// Construct the data channel as the offerer
if *isOffer {
var id uint16 = 1
dcParams := &webrtc.DataChannelParameters{
Label: "Foo",
ID: &id,
}
var channel *webrtc.DataChannel
channel, err = api.NewDataChannel(sctp, dcParams)
if err != nil {
panic(err)
}
// Register the handlers
// channel.OnOpen(handleOnOpen(channel)) // TODO: OnOpen on handle ChannelAck
go handleOnOpen(channel)() // Temporary alternative
channel.OnMessage(func(msg webrtc.DataChannelMessage) {
fmt.Printf("Message from DataChannel '%s': '%s'\n", channel.Label(), string(msg.Data))
})
}
select {}
}
// Signal is used to exchange signaling info.
// This is not part of the ORTC spec. You are free
// to exchange this information any way you want.
type Signal struct {
ICECandidates []webrtc.ICECandidate `json:"iceCandidates"`
ICEParameters webrtc.ICEParameters `json:"iceParameters"`
DTLSParameters webrtc.DTLSParameters `json:"dtlsParameters"`
SCTPCapabilities webrtc.SCTPCapabilities `json:"sctpCapabilities"`
}
func handleOnOpen(channel *webrtc.DataChannel) func() {
return func() {
fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", channel.Label(), channel.ID())
for range time.NewTicker(5 * time.Second).C {
message := signal.RandSeq(15)
fmt.Printf("Sending '%s' \n", message)
if err := channel.SendText(message); err != nil {
panic(err)
}
}
}
}