github.com/pion/webrtc/v4@v4.0.1/examples/rtp-to-webrtc/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 // rtp-to-webrtc demonstrates how to consume a RTP stream video UDP, and then send to a WebRTC client. 8 package main 9 10 import ( 11 "bufio" 12 "encoding/base64" 13 "encoding/json" 14 "errors" 15 "fmt" 16 "io" 17 "net" 18 "os" 19 "strings" 20 21 "github.com/pion/webrtc/v4" 22 ) 23 24 func main() { 25 peerConnection, err := webrtc.NewPeerConnection(webrtc.Configuration{ 26 ICEServers: []webrtc.ICEServer{ 27 { 28 URLs: []string{"stun:stun.l.google.com:19302"}, 29 }, 30 }, 31 }) 32 if err != nil { 33 panic(err) 34 } 35 36 // Open a UDP Listener for RTP Packets on port 5004 37 listener, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 5004}) 38 if err != nil { 39 panic(err) 40 } 41 42 // Increase the UDP receive buffer size 43 // Default UDP buffer sizes vary on different operating systems 44 bufferSize := 300000 // 300KB 45 err = listener.SetReadBuffer(bufferSize) 46 if err != nil { 47 panic(err) 48 } 49 50 defer func() { 51 if err = listener.Close(); err != nil { 52 panic(err) 53 } 54 }() 55 56 // Create a video track 57 videoTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion") 58 if err != nil { 59 panic(err) 60 } 61 rtpSender, err := peerConnection.AddTrack(videoTrack) 62 if err != nil { 63 panic(err) 64 } 65 66 // Read incoming RTCP packets 67 // Before these packets are returned they are processed by interceptors. For things 68 // like NACK this needs to be called. 69 go func() { 70 rtcpBuf := make([]byte, 1500) 71 for { 72 if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil { 73 return 74 } 75 } 76 }() 77 78 // Set the handler for ICE connection state 79 // This will notify you when the peer has connected/disconnected 80 peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) { 81 fmt.Printf("Connection State has changed %s \n", connectionState.String()) 82 83 if connectionState == webrtc.ICEConnectionStateFailed { 84 if closeErr := peerConnection.Close(); closeErr != nil { 85 panic(closeErr) 86 } 87 } 88 }) 89 90 // Wait for the offer to be pasted 91 offer := webrtc.SessionDescription{} 92 decode(readUntilNewline(), &offer) 93 94 // Set the remote SessionDescription 95 if err = peerConnection.SetRemoteDescription(offer); err != nil { 96 panic(err) 97 } 98 99 // Create answer 100 answer, err := peerConnection.CreateAnswer(nil) 101 if err != nil { 102 panic(err) 103 } 104 105 // Create channel that is blocked until ICE Gathering is complete 106 gatherComplete := webrtc.GatheringCompletePromise(peerConnection) 107 108 // Sets the LocalDescription, and starts our UDP listeners 109 if err = peerConnection.SetLocalDescription(answer); err != nil { 110 panic(err) 111 } 112 113 // Block until ICE Gathering is complete, disabling trickle ICE 114 // we do this because we only can exchange one signaling message 115 // in a production application you should exchange ICE Candidates via OnICECandidate 116 <-gatherComplete 117 118 // Output the answer in base64 so we can paste it in browser 119 fmt.Println(encode(peerConnection.LocalDescription())) 120 121 // Read RTP packets forever and send them to the WebRTC Client 122 inboundRTPPacket := make([]byte, 1600) // UDP MTU 123 for { 124 n, _, err := listener.ReadFrom(inboundRTPPacket) 125 if err != nil { 126 panic(fmt.Sprintf("error during read: %s", err)) 127 } 128 129 if _, err = videoTrack.Write(inboundRTPPacket[:n]); err != nil { 130 if errors.Is(err, io.ErrClosedPipe) { 131 // The peerConnection has been closed. 132 return 133 } 134 135 panic(err) 136 } 137 } 138 } 139 140 // Read from stdin until we get a newline 141 func readUntilNewline() (in string) { 142 var err error 143 144 r := bufio.NewReader(os.Stdin) 145 for { 146 in, err = r.ReadString('\n') 147 if err != nil && !errors.Is(err, io.EOF) { 148 panic(err) 149 } 150 151 if in = strings.TrimSpace(in); len(in) > 0 { 152 break 153 } 154 } 155 156 fmt.Println("") 157 return 158 } 159 160 // JSON encode + base64 a SessionDescription 161 func encode(obj *webrtc.SessionDescription) string { 162 b, err := json.Marshal(obj) 163 if err != nil { 164 panic(err) 165 } 166 167 return base64.StdEncoding.EncodeToString(b) 168 } 169 170 // Decode a base64 and unmarshal JSON into a SessionDescription 171 func decode(in string, obj *webrtc.SessionDescription) { 172 b, err := base64.StdEncoding.DecodeString(in) 173 if err != nil { 174 panic(err) 175 } 176 177 if err = json.Unmarshal(b, obj); err != nil { 178 panic(err) 179 } 180 }