github.com/pion/webrtc/v4@v4.0.1/examples/data-channels-flow-control/main.go (about) 1 // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly> 2 // SPDX-License-Identifier: MIT 3 4 // data-channels-flow-control demonstrates how to use the DataChannel congestion control APIs 5 package main 6 7 import ( 8 "encoding/json" 9 "fmt" 10 "log" 11 "os" 12 "sync/atomic" 13 "time" 14 15 "github.com/pion/webrtc/v4" 16 ) 17 18 const ( 19 bufferedAmountLowThreshold uint64 = 512 * 1024 // 512 KB 20 maxBufferedAmount uint64 = 1024 * 1024 // 1 MB 21 ) 22 23 func check(err error) { 24 if err != nil { 25 panic(err) 26 } 27 } 28 29 func setRemoteDescription(pc *webrtc.PeerConnection, sdp []byte) { 30 var desc webrtc.SessionDescription 31 err := json.Unmarshal(sdp, &desc) 32 check(err) 33 34 // Apply the desc as the remote description 35 err = pc.SetRemoteDescription(desc) 36 check(err) 37 } 38 39 func createOfferer() *webrtc.PeerConnection { 40 // Prepare the configuration 41 config := webrtc.Configuration{ 42 ICEServers: []webrtc.ICEServer{}, 43 } 44 45 // Create a new PeerConnection 46 pc, err := webrtc.NewPeerConnection(config) 47 check(err) 48 49 buf := make([]byte, 1024) 50 51 ordered := false 52 maxRetransmits := uint16(0) 53 54 options := &webrtc.DataChannelInit{ 55 Ordered: &ordered, 56 MaxRetransmits: &maxRetransmits, 57 } 58 59 sendMoreCh := make(chan struct{}, 1) 60 61 // Create a datachannel with label 'data' 62 dc, err := pc.CreateDataChannel("data", options) 63 check(err) 64 65 // Register channel opening handling 66 dc.OnOpen(func() { 67 log.Printf("OnOpen: %s-%d. Start sending a series of 1024-byte packets as fast as it can\n", dc.Label(), dc.ID()) 68 69 for { 70 err2 := dc.Send(buf) 71 check(err2) 72 73 if dc.BufferedAmount() > maxBufferedAmount { 74 // Wait until the bufferedAmount becomes lower than the threshold 75 <-sendMoreCh 76 } 77 } 78 }) 79 80 // Set bufferedAmountLowThreshold so that we can get notified when 81 // we can send more 82 dc.SetBufferedAmountLowThreshold(bufferedAmountLowThreshold) 83 84 // This callback is made when the current bufferedAmount becomes lower than the threshold 85 dc.OnBufferedAmountLow(func() { 86 // Make sure to not block this channel or perform long running operations in this callback 87 // This callback is executed by pion/sctp. If this callback is blocking it will stop operations 88 select { 89 case sendMoreCh <- struct{}{}: 90 default: 91 } 92 }) 93 94 return pc 95 } 96 97 func createAnswerer() *webrtc.PeerConnection { 98 // Prepare the configuration 99 config := webrtc.Configuration{ 100 ICEServers: []webrtc.ICEServer{}, 101 } 102 103 // Create a new PeerConnection 104 pc, err := webrtc.NewPeerConnection(config) 105 check(err) 106 107 pc.OnDataChannel(func(dc *webrtc.DataChannel) { 108 var totalBytesReceived uint64 109 110 // Register channel opening handling 111 dc.OnOpen(func() { 112 log.Printf("OnOpen: %s-%d. Start receiving data", dc.Label(), dc.ID()) 113 since := time.Now() 114 115 // Start printing out the observed throughput 116 ticker := time.NewTicker(1000 * time.Millisecond) 117 defer ticker.Stop() 118 for range ticker.C { 119 bps := float64(atomic.LoadUint64(&totalBytesReceived)*8) / time.Since(since).Seconds() 120 log.Printf("Throughput: %.03f Mbps", bps/1024/1024) 121 } 122 }) 123 124 // Register the OnMessage to handle incoming messages 125 dc.OnMessage(func(dcMsg webrtc.DataChannelMessage) { 126 n := len(dcMsg.Data) 127 atomic.AddUint64(&totalBytesReceived, uint64(n)) 128 }) 129 }) 130 131 return pc 132 } 133 134 func main() { 135 offerPC := createOfferer() 136 defer func() { 137 if err := offerPC.Close(); err != nil { 138 fmt.Printf("cannot close offerPC: %v\n", err) 139 } 140 }() 141 142 answerPC := createAnswerer() 143 defer func() { 144 if err := answerPC.Close(); err != nil { 145 fmt.Printf("cannot close answerPC: %v\n", err) 146 } 147 }() 148 149 // Set ICE Candidate handler. As soon as a PeerConnection has gathered a candidate 150 // send it to the other peer 151 answerPC.OnICECandidate(func(i *webrtc.ICECandidate) { 152 if i != nil { 153 check(offerPC.AddICECandidate(i.ToJSON())) 154 } 155 }) 156 157 // Set ICE Candidate handler. As soon as a PeerConnection has gathered a candidate 158 // send it to the other peer 159 offerPC.OnICECandidate(func(i *webrtc.ICECandidate) { 160 if i != nil { 161 check(answerPC.AddICECandidate(i.ToJSON())) 162 } 163 }) 164 165 // Set the handler for Peer connection state 166 // This will notify you when the peer has connected/disconnected 167 offerPC.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { 168 fmt.Printf("Peer Connection State has changed: %s (offerer)\n", s.String()) 169 170 if s == webrtc.PeerConnectionStateFailed { 171 // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. 172 // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. 173 // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. 174 fmt.Println("Peer Connection has gone to failed exiting") 175 os.Exit(0) 176 } 177 178 if s == webrtc.PeerConnectionStateClosed { 179 // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify 180 fmt.Println("Peer Connection has gone to closed exiting") 181 os.Exit(0) 182 } 183 }) 184 185 // Set the handler for Peer connection state 186 // This will notify you when the peer has connected/disconnected 187 answerPC.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { 188 fmt.Printf("Peer Connection State has changed: %s (answerer)\n", s.String()) 189 190 if s == webrtc.PeerConnectionStateFailed { 191 // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. 192 // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. 193 // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. 194 fmt.Println("Peer Connection has gone to failed exiting") 195 os.Exit(0) 196 } 197 198 if s == webrtc.PeerConnectionStateClosed { 199 // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify 200 fmt.Println("Peer Connection has gone to closed exiting") 201 os.Exit(0) 202 } 203 }) 204 205 // Now, create an offer 206 offer, err := offerPC.CreateOffer(nil) 207 check(err) 208 check(offerPC.SetLocalDescription(offer)) 209 desc, err := json.Marshal(offer) 210 check(err) 211 212 setRemoteDescription(answerPC, desc) 213 214 answer, err := answerPC.CreateAnswer(nil) 215 check(err) 216 check(answerPC.SetLocalDescription(answer)) 217 desc2, err := json.Marshal(answer) 218 check(err) 219 220 setRemoteDescription(offerPC, desc2) 221 222 // Block forever 223 select {} 224 }