package main import ( "fmt" "time" "github.com/pions/rtcp" "github.com/pions/webrtc" "github.com/pions/webrtc/examples/internal/signal" ) var peerConnectionConfig = webrtc.Configuration{ ICEServers: []webrtc.ICEServer{ { URLs: []string{"stun:stun.l.google.com:19302"}, }, }, } const ( rtcpPLIInterval = time.Second * 3 ) func main() { sdpChan := signal.HTTPSDPServer() // Everything below is the pion-WebRTC API, thanks for using it ❤️. // Create a MediaEngine object to configure the supported codec m := webrtc.MediaEngine{} // Setup the codecs you want to use. // Only support VP8, this makes our proxying code simpler m.RegisterCodec(webrtc.NewRTPVP8Codec(webrtc.DefaultPayloadTypeVP8, 90000)) // Create the API object with the MediaEngine api := webrtc.NewAPI(webrtc.WithMediaEngine(m)) offer := webrtc.SessionDescription{} signal.Decode(<-sdpChan, &offer) fmt.Println("") // Create a new RTCPeerConnection peerConnection, err := api.NewPeerConnection(peerConnectionConfig) if err != nil { panic(err) } localTrackChan := make(chan *webrtc.Track) // Set a handler for when a new remote track starts, this just distributes all our packets // to connected peers peerConnection.OnTrack(func(remoteTrack *webrtc.Track, receiver *webrtc.RTPReceiver) { // Send a PLI on an interval so that the publisher is pushing a keyframe every rtcpPLIInterval // This can be less wasteful by processing incoming RTCP events, then we would emit a NACK/PLI when a viewer requests it go func() { ticker := time.NewTicker(rtcpPLIInterval) for range ticker.C { if err := peerConnection.SendRTCP(&rtcp.PictureLossIndication{MediaSSRC: remoteTrack.SSRC()}); err != nil { fmt.Println(err) } } }() // Create a local track, all our SFU clients will be fed via this track localTrack, err := peerConnection.NewTrack(remoteTrack.PayloadType(), remoteTrack.SSRC(), "video", "pion") if err != nil { panic(err) } localTrackChan <- localTrack rtpBuf := make([]byte, 1400) for { i, err := remoteTrack.Read(rtpBuf) if err != nil { panic(err) } if _, err = localTrack.Write(rtpBuf[:i]); err != nil { panic(err) } } }) // Set the remote SessionDescription err = peerConnection.SetRemoteDescription(offer) if err != nil { panic(err) } // Create answer answer, err := peerConnection.CreateAnswer(nil) if err != nil { panic(err) } // Sets the LocalDescription, and starts our UDP listeners err = peerConnection.SetLocalDescription(answer) if err != nil { panic(err) } // Get the LocalDescription and take it to base64 so we can paste in browser fmt.Println(signal.Encode(answer)) localTrack := <-localTrackChan for { fmt.Println("") fmt.Println("Curl an base64 SDP to start sendonly peer connection") recvOnlyOffer := webrtc.SessionDescription{} signal.Decode(<-sdpChan, &recvOnlyOffer) // Create a new PeerConnection peerConnection, err := api.NewPeerConnection(peerConnectionConfig) if err != nil { panic(err) } _, err = peerConnection.AddTrack(localTrack) if err != nil { panic(err) } // Set the remote SessionDescription err = peerConnection.SetRemoteDescription(recvOnlyOffer) if err != nil { panic(err) } // Create answer answer, err := peerConnection.CreateAnswer(nil) if err != nil { panic(err) } // Sets the LocalDescription, and starts our UDP listeners err = peerConnection.SetLocalDescription(answer) if err != nil { panic(err) } // Get the LocalDescription and take it to base64 so we can paste in browser fmt.Println(signal.Encode(answer)) } }