package main import ( "flag" "fmt" "time" "github.com/pions/webrtc" "github.com/pions/webrtc/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)) }) }) // Gather candidates err = gatherer.Gather() if err != nil { panic(err) } iceCandidates, err := gatherer.GetLocalCandidates() if err != nil { panic(err) } iceParams, err := gatherer.GetLocalParameters() if err != nil { panic(err) } dtlsParams := dtls.GetLocalParameters() 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 { dcParams := &webrtc.DataChannelParameters{ Label: "Foo", ID: 1, } 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) } } } }