github.com/pion/webrtc/v3@v3.2.24/examples/swap-tracks/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 // swap-tracks demonstrates how to swap multiple incoming tracks on a single outgoing track. 8 package main 9 10 import ( 11 "context" 12 "errors" 13 "fmt" 14 "io" 15 "time" 16 17 "github.com/pion/rtcp" 18 "github.com/pion/rtp" 19 "github.com/pion/webrtc/v3" 20 "github.com/pion/webrtc/v3/examples/internal/signal" 21 ) 22 23 func main() { // nolint:gocognit 24 // Everything below is the Pion WebRTC API! Thanks for using it ❤️. 25 26 // Prepare the configuration 27 config := webrtc.Configuration{ 28 ICEServers: []webrtc.ICEServer{ 29 { 30 URLs: []string{"stun:stun.l.google.com:19302"}, 31 }, 32 }, 33 } 34 // Create a new RTCPeerConnection 35 peerConnection, err := webrtc.NewPeerConnection(config) 36 if err != nil { 37 panic(err) 38 } 39 defer func() { 40 if cErr := peerConnection.Close(); cErr != nil { 41 fmt.Printf("cannot close peerConnection: %v\n", cErr) 42 } 43 }() 44 45 // Create Track that we send video back to browser on 46 outputTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion") 47 if err != nil { 48 panic(err) 49 } 50 51 // Add this newly created track to the PeerConnection 52 rtpSender, err := peerConnection.AddTrack(outputTrack) 53 if err != nil { 54 panic(err) 55 } 56 57 // Read incoming RTCP packets 58 // Before these packets are returned they are processed by interceptors. For things 59 // like NACK this needs to be called. 60 go func() { 61 rtcpBuf := make([]byte, 1500) 62 for { 63 if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil { 64 return 65 } 66 } 67 }() 68 69 // Wait for the offer to be pasted 70 offer := webrtc.SessionDescription{} 71 signal.Decode(signal.MustReadStdin(), &offer) 72 73 // Set the remote SessionDescription 74 err = peerConnection.SetRemoteDescription(offer) 75 if err != nil { 76 panic(err) 77 } 78 79 // Which track is currently being handled 80 currTrack := 0 81 // The total number of tracks 82 trackCount := 0 83 // The channel of packets with a bit of buffer 84 packets := make(chan *rtp.Packet, 60) 85 86 // Set a handler for when a new remote track starts 87 peerConnection.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) { 88 fmt.Printf("Track has started, of type %d: %s \n", track.PayloadType(), track.Codec().MimeType) 89 trackNum := trackCount 90 trackCount++ 91 // The last timestamp so that we can change the packet to only be the delta 92 var lastTimestamp uint32 93 94 // Whether this track is the one currently sending to the channel (on change 95 // of this we send a PLI to have the entire picture updated) 96 var isCurrTrack bool 97 for { 98 // Read RTP packets being sent to Pion 99 rtp, _, readErr := track.ReadRTP() 100 if readErr != nil { 101 panic(readErr) 102 } 103 104 // Change the timestamp to only be the delta 105 oldTimestamp := rtp.Timestamp 106 if lastTimestamp == 0 { 107 rtp.Timestamp = 0 108 } else { 109 rtp.Timestamp -= lastTimestamp 110 } 111 lastTimestamp = oldTimestamp 112 113 // Check if this is the current track 114 if currTrack == trackNum { 115 // If just switched to this track, send PLI to get picture refresh 116 if !isCurrTrack { 117 isCurrTrack = true 118 if writeErr := peerConnection.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: uint32(track.SSRC())}}); writeErr != nil { 119 fmt.Println(writeErr) 120 } 121 } 122 packets <- rtp 123 } else { 124 isCurrTrack = false 125 } 126 } 127 }) 128 129 ctx, done := context.WithCancel(context.Background()) 130 131 // Set the handler for Peer connection state 132 // This will notify you when the peer has connected/disconnected 133 peerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { 134 fmt.Printf("Peer Connection State has changed: %s\n", s.String()) 135 136 if s == webrtc.PeerConnectionStateFailed { 137 // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. 138 // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. 139 // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. 140 done() 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 err = peerConnection.SetLocalDescription(answer) 155 if err != nil { 156 panic(err) 157 } 158 159 // Block until ICE Gathering is complete, disabling trickle ICE 160 // we do this because we only can exchange one signaling message 161 // in a production application you should exchange ICE Candidates via OnICECandidate 162 <-gatherComplete 163 164 fmt.Println(signal.Encode(*peerConnection.LocalDescription())) 165 166 // Asynchronously take all packets in the channel and write them out to our 167 // track 168 go func() { 169 var currTimestamp uint32 170 for i := uint16(0); ; i++ { 171 packet := <-packets 172 // Timestamp on the packet is really a diff, so add it to current 173 currTimestamp += packet.Timestamp 174 packet.Timestamp = currTimestamp 175 // Keep an increasing sequence number 176 packet.SequenceNumber = i 177 // Write out the packet, ignoring closed pipe if nobody is listening 178 if err := outputTrack.WriteRTP(packet); err != nil { 179 if errors.Is(err, io.ErrClosedPipe) { 180 // The peerConnection has been closed. 181 return 182 } 183 184 panic(err) 185 } 186 } 187 }() 188 189 // Wait for connection, then rotate the track every 5s 190 fmt.Printf("Waiting for connection\n") 191 for { 192 select { 193 case <-ctx.Done(): 194 return 195 default: 196 } 197 198 // We haven't gotten any tracks yet 199 if trackCount == 0 { 200 continue 201 } 202 203 fmt.Printf("Waiting 5 seconds then changing...\n") 204 time.Sleep(5 * time.Second) 205 if currTrack == trackCount-1 { 206 currTrack = 0 207 } else { 208 currTrack++ 209 } 210 fmt.Printf("Switched to track #%v\n", currTrack+1) 211 } 212 }