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  }