github.com/pion/webrtc/v3@v3.2.24/examples/broadcast/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  // broadcast demonstrates how to broadcast a video to many peers, while only requiring the broadcaster to upload once.
     8  package main
     9  
    10  import (
    11  	"errors"
    12  	"fmt"
    13  	"io"
    14  
    15  	"github.com/pion/interceptor"
    16  	"github.com/pion/interceptor/pkg/intervalpli"
    17  	"github.com/pion/webrtc/v3"
    18  	"github.com/pion/webrtc/v3/examples/internal/signal"
    19  )
    20  
    21  func main() { // nolint:gocognit
    22  	sdpChan := signal.HTTPSDPServer()
    23  
    24  	// Everything below is the Pion WebRTC API, thanks for using it ❤️.
    25  	offer := webrtc.SessionDescription{}
    26  	signal.Decode(<-sdpChan, &offer)
    27  	fmt.Println("")
    28  
    29  	peerConnectionConfig := webrtc.Configuration{
    30  		ICEServers: []webrtc.ICEServer{
    31  			{
    32  				URLs: []string{"stun:stun.l.google.com:19302"},
    33  			},
    34  		},
    35  	}
    36  
    37  	m := &webrtc.MediaEngine{}
    38  	if err := m.RegisterDefaultCodecs(); err != nil {
    39  		panic(err)
    40  	}
    41  
    42  	// Create a InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline.
    43  	// This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection`
    44  	// this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry
    45  	// for each PeerConnection.
    46  	i := &interceptor.Registry{}
    47  
    48  	// Use the default set of Interceptors
    49  	if err := webrtc.RegisterDefaultInterceptors(m, i); err != nil {
    50  		panic(err)
    51  	}
    52  
    53  	// Register a intervalpli factory
    54  	// This interceptor sends a PLI every 3 seconds. A PLI causes a video keyframe to be generated by the sender.
    55  	// This makes our video seekable and more error resilent, but at a cost of lower picture quality and higher bitrates
    56  	// A real world application should process incoming RTCP packets from viewers and forward them to senders
    57  	intervalPliFactory, err := intervalpli.NewReceiverInterceptor()
    58  	if err != nil {
    59  		panic(err)
    60  	}
    61  	i.Add(intervalPliFactory)
    62  
    63  	// Create a new RTCPeerConnection
    64  	peerConnection, err := webrtc.NewAPI(webrtc.WithMediaEngine(m), webrtc.WithInterceptorRegistry(i)).NewPeerConnection(peerConnectionConfig)
    65  	if err != nil {
    66  		panic(err)
    67  	}
    68  	defer func() {
    69  		if cErr := peerConnection.Close(); cErr != nil {
    70  			fmt.Printf("cannot close peerConnection: %v\n", cErr)
    71  		}
    72  	}()
    73  
    74  	// Allow us to receive 1 video track
    75  	if _, err = peerConnection.AddTransceiverFromKind(webrtc.RTPCodecTypeVideo); err != nil {
    76  		panic(err)
    77  	}
    78  
    79  	localTrackChan := make(chan *webrtc.TrackLocalStaticRTP)
    80  	// Set a handler for when a new remote track starts, this just distributes all our packets
    81  	// to connected peers
    82  	peerConnection.OnTrack(func(remoteTrack *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) {
    83  		// Create a local track, all our SFU clients will be fed via this track
    84  		localTrack, newTrackErr := webrtc.NewTrackLocalStaticRTP(remoteTrack.Codec().RTPCodecCapability, "video", "pion")
    85  		if newTrackErr != nil {
    86  			panic(newTrackErr)
    87  		}
    88  		localTrackChan <- localTrack
    89  
    90  		rtpBuf := make([]byte, 1400)
    91  		for {
    92  			i, _, readErr := remoteTrack.Read(rtpBuf)
    93  			if readErr != nil {
    94  				panic(readErr)
    95  			}
    96  
    97  			// ErrClosedPipe means we don't have any subscribers, this is ok if no peers have connected yet
    98  			if _, err = localTrack.Write(rtpBuf[:i]); err != nil && !errors.Is(err, io.ErrClosedPipe) {
    99  				panic(err)
   100  			}
   101  		}
   102  	})
   103  
   104  	// Set the remote SessionDescription
   105  	err = peerConnection.SetRemoteDescription(offer)
   106  	if err != nil {
   107  		panic(err)
   108  	}
   109  
   110  	// Create answer
   111  	answer, err := peerConnection.CreateAnswer(nil)
   112  	if err != nil {
   113  		panic(err)
   114  	}
   115  
   116  	// Create channel that is blocked until ICE Gathering is complete
   117  	gatherComplete := webrtc.GatheringCompletePromise(peerConnection)
   118  
   119  	// Sets the LocalDescription, and starts our UDP listeners
   120  	err = peerConnection.SetLocalDescription(answer)
   121  	if err != nil {
   122  		panic(err)
   123  	}
   124  
   125  	// Block until ICE Gathering is complete, disabling trickle ICE
   126  	// we do this because we only can exchange one signaling message
   127  	// in a production application you should exchange ICE Candidates via OnICECandidate
   128  	<-gatherComplete
   129  
   130  	// Get the LocalDescription and take it to base64 so we can paste in browser
   131  	fmt.Println(signal.Encode(*peerConnection.LocalDescription()))
   132  
   133  	localTrack := <-localTrackChan
   134  	for {
   135  		fmt.Println("")
   136  		fmt.Println("Curl an base64 SDP to start sendonly peer connection")
   137  
   138  		recvOnlyOffer := webrtc.SessionDescription{}
   139  		signal.Decode(<-sdpChan, &recvOnlyOffer)
   140  
   141  		// Create a new PeerConnection
   142  		peerConnection, err := webrtc.NewPeerConnection(peerConnectionConfig)
   143  		if err != nil {
   144  			panic(err)
   145  		}
   146  
   147  		rtpSender, err := peerConnection.AddTrack(localTrack)
   148  		if err != nil {
   149  			panic(err)
   150  		}
   151  
   152  		// Read incoming RTCP packets
   153  		// Before these packets are returned they are processed by interceptors. For things
   154  		// like NACK this needs to be called.
   155  		go func() {
   156  			rtcpBuf := make([]byte, 1500)
   157  			for {
   158  				if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil {
   159  					return
   160  				}
   161  			}
   162  		}()
   163  
   164  		// Set the remote SessionDescription
   165  		err = peerConnection.SetRemoteDescription(recvOnlyOffer)
   166  		if err != nil {
   167  			panic(err)
   168  		}
   169  
   170  		// Create answer
   171  		answer, err := peerConnection.CreateAnswer(nil)
   172  		if err != nil {
   173  			panic(err)
   174  		}
   175  
   176  		// Create channel that is blocked until ICE Gathering is complete
   177  		gatherComplete = webrtc.GatheringCompletePromise(peerConnection)
   178  
   179  		// Sets the LocalDescription, and starts our UDP listeners
   180  		err = peerConnection.SetLocalDescription(answer)
   181  		if err != nil {
   182  			panic(err)
   183  		}
   184  
   185  		// Block until ICE Gathering is complete, disabling trickle ICE
   186  		// we do this because we only can exchange one signaling message
   187  		// in a production application you should exchange ICE Candidates via OnICECandidate
   188  		<-gatherComplete
   189  
   190  		// Get the LocalDescription and take it to base64 so we can paste in browser
   191  		fmt.Println(signal.Encode(*peerConnection.LocalDescription()))
   192  	}
   193  }