github.com/pion/webrtc/v3@v3.2.24/examples/reflect/main.go (about) 1 // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly> 2 // SPDX-License-Identifier: MIT 3 4 //go:build !js 5 // +build !js 6 7 // reflect demonstrates how with one PeerConnection you can send video to Pion and have the packets sent back 8 package main 9 10 import ( 11 "fmt" 12 "os" 13 14 "github.com/pion/interceptor" 15 "github.com/pion/interceptor/pkg/intervalpli" 16 "github.com/pion/webrtc/v3" 17 "github.com/pion/webrtc/v3/examples/internal/signal" 18 ) 19 20 // nolint:gocognit 21 func main() { 22 // Everything below is the Pion WebRTC API! Thanks for using it ❤️. 23 24 // Create a MediaEngine object to configure the supported codec 25 m := &webrtc.MediaEngine{} 26 27 // Setup the codecs you want to use. 28 // We'll use a VP8 and Opus but you can also define your own 29 if err := m.RegisterCodec(webrtc.RTPCodecParameters{ 30 RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, 31 PayloadType: 96, 32 }, webrtc.RTPCodecTypeVideo); err != nil { 33 panic(err) 34 } 35 36 // Create a InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline. 37 // This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection` 38 // this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry 39 // for each PeerConnection. 40 i := &interceptor.Registry{} 41 42 // Use the default set of Interceptors 43 if err := webrtc.RegisterDefaultInterceptors(m, i); err != nil { 44 panic(err) 45 } 46 47 // Register a intervalpli factory 48 // This interceptor sends a PLI every 3 seconds. A PLI causes a video keyframe to be generated by the sender. 49 // This makes our video seekable and more error resilent, but at a cost of lower picture quality and higher bitrates 50 // A real world application should process incoming RTCP packets from viewers and forward them to senders 51 intervalPliFactory, err := intervalpli.NewReceiverInterceptor() 52 if err != nil { 53 panic(err) 54 } 55 i.Add(intervalPliFactory) 56 57 // Create the API object with the MediaEngine 58 api := webrtc.NewAPI(webrtc.WithMediaEngine(m), webrtc.WithInterceptorRegistry(i)) 59 60 // Prepare the configuration 61 config := webrtc.Configuration{ 62 ICEServers: []webrtc.ICEServer{ 63 { 64 URLs: []string{"stun:stun.l.google.com:19302"}, 65 }, 66 }, 67 } 68 // Create a new RTCPeerConnection 69 peerConnection, err := api.NewPeerConnection(config) 70 if err != nil { 71 panic(err) 72 } 73 defer func() { 74 if cErr := peerConnection.Close(); cErr != nil { 75 fmt.Printf("cannot close peerConnection: %v\n", cErr) 76 } 77 }() 78 79 // Create Track that we send video back to browser on 80 outputTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion") 81 if err != nil { 82 panic(err) 83 } 84 85 // Add this newly created track to the PeerConnection 86 rtpSender, err := peerConnection.AddTrack(outputTrack) 87 if err != nil { 88 panic(err) 89 } 90 91 // Read incoming RTCP packets 92 // Before these packets are returned they are processed by interceptors. For things 93 // like NACK this needs to be called. 94 go func() { 95 rtcpBuf := make([]byte, 1500) 96 for { 97 if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil { 98 return 99 } 100 } 101 }() 102 103 // Wait for the offer to be pasted 104 offer := webrtc.SessionDescription{} 105 signal.Decode(signal.MustReadStdin(), &offer) 106 107 // Set the remote SessionDescription 108 err = peerConnection.SetRemoteDescription(offer) 109 if err != nil { 110 panic(err) 111 } 112 113 // Set a handler for when a new remote track starts, this handler copies inbound RTP packets, 114 // replaces the SSRC and sends them back 115 peerConnection.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) { 116 fmt.Printf("Track has started, of type %d: %s \n", track.PayloadType(), track.Codec().MimeType) 117 for { 118 // Read RTP packets being sent to Pion 119 rtp, _, readErr := track.ReadRTP() 120 if readErr != nil { 121 panic(readErr) 122 } 123 124 if writeErr := outputTrack.WriteRTP(rtp); writeErr != nil { 125 panic(writeErr) 126 } 127 } 128 }) 129 130 // Set the handler for Peer connection state 131 // This will notify you when the peer has connected/disconnected 132 peerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { 133 fmt.Printf("Peer Connection State has changed: %s\n", s.String()) 134 135 if s == webrtc.PeerConnectionStateFailed { 136 // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. 137 // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. 138 // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. 139 fmt.Println("Peer Connection has gone to failed exiting") 140 os.Exit(0) 141 } 142 }) 143 144 // Create an answer 145 answer, err := peerConnection.CreateAnswer(nil) 146 if err != nil { 147 panic(err) 148 } 149 150 // Create channel that is blocked until ICE Gathering is complete 151 gatherComplete := webrtc.GatheringCompletePromise(peerConnection) 152 153 // Sets the LocalDescription, and starts our UDP listeners 154 if err = peerConnection.SetLocalDescription(answer); err != nil { 155 panic(err) 156 } 157 158 // Block until ICE Gathering is complete, disabling trickle ICE 159 // we do this because we only can exchange one signaling message 160 // in a production application you should exchange ICE Candidates via OnICECandidate 161 <-gatherComplete 162 163 // Output the answer in base64 so we can paste it in browser 164 fmt.Println(signal.Encode(*peerConnection.LocalDescription())) 165 166 // Block forever 167 select {} 168 }