Files
webrtc/examples/ortc/main.go
2023-05-05 11:58:49 -04:00

186 lines
4.2 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/v3"
"github.com/pion/webrtc/v3/examples/internal/signal"
)
func main() {
isOffer := flag.Bool("offer", false, "Act as the offerer if set")
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
err = gatherer.Gather()
if 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,
}
// Exchange the information
fmt.Println(signal.Encode(s))
remoteSignal := Signal{}
signal.Decode(signal.MustReadStdin(), &remoteSignal)
iceRole := webrtc.ICERoleControlled
if *isOffer {
iceRole = webrtc.ICERoleControlling
}
err = ice.SetRemoteCandidates(remoteSignal.ICECandidates)
if err != nil {
panic(err)
}
// Start the ICE transport
err = ice.Start(nil, remoteSignal.ICEParameters, &iceRole)
if err != nil {
panic(err)
}
// Start the DTLS transport
err = dtls.Start(remoteSignal.DTLSParameters)
if err != nil {
panic(err)
}
// Start the SCTP transport
err = sctp.Start(remoteSignal.SCTPCapabilities)
if 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)
err := channel.SendText(message)
if err != nil {
panic(err)
}
}
}
}