github.com/pion/webrtc/v3@v3.2.24/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/v3"
    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()+uint64(len(buf)) > 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  			for range time.NewTicker(1000 * time.Millisecond).C {
   117  				bps := float64(atomic.LoadUint64(&totalBytesReceived)*8) / time.Since(since).Seconds()
   118  				log.Printf("Throughput: %.03f Mbps", bps/1024/1024)
   119  			}
   120  		})
   121  
   122  		// Register the OnMessage to handle incoming messages
   123  		dc.OnMessage(func(dcMsg webrtc.DataChannelMessage) {
   124  			n := len(dcMsg.Data)
   125  			atomic.AddUint64(&totalBytesReceived, uint64(n))
   126  		})
   127  	})
   128  
   129  	return pc
   130  }
   131  
   132  func main() {
   133  	offerPC := createOfferer()
   134  	defer func() {
   135  		if err := offerPC.Close(); err != nil {
   136  			fmt.Printf("cannot close offerPC: %v\n", err)
   137  		}
   138  	}()
   139  
   140  	answerPC := createAnswerer()
   141  	defer func() {
   142  		if err := answerPC.Close(); err != nil {
   143  			fmt.Printf("cannot close answerPC: %v\n", err)
   144  		}
   145  	}()
   146  
   147  	// Set ICE Candidate handler. As soon as a PeerConnection has gathered a candidate
   148  	// send it to the other peer
   149  	answerPC.OnICECandidate(func(i *webrtc.ICECandidate) {
   150  		if i != nil {
   151  			check(offerPC.AddICECandidate(i.ToJSON()))
   152  		}
   153  	})
   154  
   155  	// Set ICE Candidate handler. As soon as a PeerConnection has gathered a candidate
   156  	// send it to the other peer
   157  	offerPC.OnICECandidate(func(i *webrtc.ICECandidate) {
   158  		if i != nil {
   159  			check(answerPC.AddICECandidate(i.ToJSON()))
   160  		}
   161  	})
   162  
   163  	// Set the handler for Peer connection state
   164  	// This will notify you when the peer has connected/disconnected
   165  	offerPC.OnConnectionStateChange(func(s webrtc.PeerConnectionState) {
   166  		fmt.Printf("Peer Connection State has changed: %s (offerer)\n", s.String())
   167  
   168  		if s == webrtc.PeerConnectionStateFailed {
   169  			// Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart.
   170  			// Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout.
   171  			// Note that the PeerConnection may come back from PeerConnectionStateDisconnected.
   172  			fmt.Println("Peer Connection has gone to failed exiting")
   173  			os.Exit(0)
   174  		}
   175  	})
   176  
   177  	// Set the handler for Peer connection state
   178  	// This will notify you when the peer has connected/disconnected
   179  	answerPC.OnConnectionStateChange(func(s webrtc.PeerConnectionState) {
   180  		fmt.Printf("Peer Connection State has changed: %s (answerer)\n", s.String())
   181  
   182  		if s == webrtc.PeerConnectionStateFailed {
   183  			// Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart.
   184  			// Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout.
   185  			// Note that the PeerConnection may come back from PeerConnectionStateDisconnected.
   186  			fmt.Println("Peer Connection has gone to failed exiting")
   187  			os.Exit(0)
   188  		}
   189  	})
   190  
   191  	// Now, create an offer
   192  	offer, err := offerPC.CreateOffer(nil)
   193  	check(err)
   194  	check(offerPC.SetLocalDescription(offer))
   195  	desc, err := json.Marshal(offer)
   196  	check(err)
   197  
   198  	setRemoteDescription(answerPC, desc)
   199  
   200  	answer, err := answerPC.CreateAnswer(nil)
   201  	check(err)
   202  	check(answerPC.SetLocalDescription(answer))
   203  	desc2, err := json.Marshal(answer)
   204  	check(err)
   205  
   206  	setRemoteDescription(offerPC, desc2)
   207  
   208  	// Block forever
   209  	select {}
   210  }