github.com/pion/webrtc/v4@v4.0.1/examples/reflect/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  // reflect demonstrates how with one PeerConnection you can send video to Pion and have the packets sent back
     8  package main
     9  
    10  import (
    11  	"bufio"
    12  	"encoding/base64"
    13  	"encoding/json"
    14  	"errors"
    15  	"fmt"
    16  	"io"
    17  	"os"
    18  	"strings"
    19  
    20  	"github.com/pion/interceptor"
    21  	"github.com/pion/interceptor/pkg/intervalpli"
    22  	"github.com/pion/webrtc/v4"
    23  )
    24  
    25  // nolint:gocognit
    26  func main() {
    27  	// Everything below is the Pion WebRTC API! Thanks for using it ❤️.
    28  
    29  	// Create a MediaEngine object to configure the supported codec
    30  	m := &webrtc.MediaEngine{}
    31  
    32  	// Setup the codecs you want to use.
    33  	// We'll use a VP8 and Opus but you can also define your own
    34  	if err := m.RegisterCodec(webrtc.RTPCodecParameters{
    35  		RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil},
    36  		PayloadType:        96,
    37  	}, webrtc.RTPCodecTypeVideo); err != nil {
    38  		panic(err)
    39  	}
    40  
    41  	// Create a InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline.
    42  	// This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection`
    43  	// this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry
    44  	// for each PeerConnection.
    45  	i := &interceptor.Registry{}
    46  
    47  	// Use the default set of Interceptors
    48  	if err := webrtc.RegisterDefaultInterceptors(m, i); err != nil {
    49  		panic(err)
    50  	}
    51  
    52  	// Register a intervalpli factory
    53  	// This interceptor sends a PLI every 3 seconds. A PLI causes a video keyframe to be generated by the sender.
    54  	// This makes our video seekable and more error resilent, but at a cost of lower picture quality and higher bitrates
    55  	// A real world application should process incoming RTCP packets from viewers and forward them to senders
    56  	intervalPliFactory, err := intervalpli.NewReceiverInterceptor()
    57  	if err != nil {
    58  		panic(err)
    59  	}
    60  	i.Add(intervalPliFactory)
    61  
    62  	// Create the API object with the MediaEngine
    63  	api := webrtc.NewAPI(webrtc.WithMediaEngine(m), webrtc.WithInterceptorRegistry(i))
    64  
    65  	// Prepare the configuration
    66  	config := webrtc.Configuration{
    67  		ICEServers: []webrtc.ICEServer{
    68  			{
    69  				URLs: []string{"stun:stun.l.google.com:19302"},
    70  			},
    71  		},
    72  	}
    73  	// Create a new RTCPeerConnection
    74  	peerConnection, err := api.NewPeerConnection(config)
    75  	if err != nil {
    76  		panic(err)
    77  	}
    78  	defer func() {
    79  		if cErr := peerConnection.Close(); cErr != nil {
    80  			fmt.Printf("cannot close peerConnection: %v\n", cErr)
    81  		}
    82  	}()
    83  
    84  	// Create Track that we send video back to browser on
    85  	outputTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion")
    86  	if err != nil {
    87  		panic(err)
    88  	}
    89  
    90  	// Add this newly created track to the PeerConnection
    91  	rtpSender, err := peerConnection.AddTrack(outputTrack)
    92  	if err != nil {
    93  		panic(err)
    94  	}
    95  
    96  	// Read incoming RTCP packets
    97  	// Before these packets are returned they are processed by interceptors. For things
    98  	// like NACK this needs to be called.
    99  	go func() {
   100  		rtcpBuf := make([]byte, 1500)
   101  		for {
   102  			if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil {
   103  				return
   104  			}
   105  		}
   106  	}()
   107  
   108  	// Wait for the offer to be pasted
   109  	offer := webrtc.SessionDescription{}
   110  	decode(readUntilNewline(), &offer)
   111  
   112  	// Set the remote SessionDescription
   113  	err = peerConnection.SetRemoteDescription(offer)
   114  	if err != nil {
   115  		panic(err)
   116  	}
   117  
   118  	// Set a handler for when a new remote track starts, this handler copies inbound RTP packets,
   119  	// replaces the SSRC and sends them back
   120  	peerConnection.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) { //nolint: revive
   121  		fmt.Printf("Track has started, of type %d: %s \n", track.PayloadType(), track.Codec().MimeType)
   122  		for {
   123  			// Read RTP packets being sent to Pion
   124  			rtp, _, readErr := track.ReadRTP()
   125  			if readErr != nil {
   126  				panic(readErr)
   127  			}
   128  
   129  			if writeErr := outputTrack.WriteRTP(rtp); writeErr != nil {
   130  				panic(writeErr)
   131  			}
   132  		}
   133  	})
   134  
   135  	// Set the handler for Peer connection state
   136  	// This will notify you when the peer has connected/disconnected
   137  	peerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) {
   138  		fmt.Printf("Peer Connection State has changed: %s\n", s.String())
   139  
   140  		if s == webrtc.PeerConnectionStateFailed {
   141  			// Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart.
   142  			// Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout.
   143  			// Note that the PeerConnection may come back from PeerConnectionStateDisconnected.
   144  			fmt.Println("Peer Connection has gone to failed exiting")
   145  			os.Exit(0)
   146  		}
   147  
   148  		if s == webrtc.PeerConnectionStateClosed {
   149  			// PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify
   150  			fmt.Println("Peer Connection has gone to closed exiting")
   151  			os.Exit(0)
   152  		}
   153  	})
   154  
   155  	// Create an answer
   156  	answer, err := peerConnection.CreateAnswer(nil)
   157  	if err != nil {
   158  		panic(err)
   159  	}
   160  
   161  	// Create channel that is blocked until ICE Gathering is complete
   162  	gatherComplete := webrtc.GatheringCompletePromise(peerConnection)
   163  
   164  	// Sets the LocalDescription, and starts our UDP listeners
   165  	if err = peerConnection.SetLocalDescription(answer); err != nil {
   166  		panic(err)
   167  	}
   168  
   169  	// Block until ICE Gathering is complete, disabling trickle ICE
   170  	// we do this because we only can exchange one signaling message
   171  	// in a production application you should exchange ICE Candidates via OnICECandidate
   172  	<-gatherComplete
   173  
   174  	// Output the answer in base64 so we can paste it in browser
   175  	fmt.Println(encode(peerConnection.LocalDescription()))
   176  
   177  	// Block forever
   178  	select {}
   179  }
   180  
   181  // Read from stdin until we get a newline
   182  func readUntilNewline() (in string) {
   183  	var err error
   184  
   185  	r := bufio.NewReader(os.Stdin)
   186  	for {
   187  		in, err = r.ReadString('\n')
   188  		if err != nil && !errors.Is(err, io.EOF) {
   189  			panic(err)
   190  		}
   191  
   192  		if in = strings.TrimSpace(in); len(in) > 0 {
   193  			break
   194  		}
   195  	}
   196  
   197  	fmt.Println("")
   198  	return
   199  }
   200  
   201  // JSON encode + base64 a SessionDescription
   202  func encode(obj *webrtc.SessionDescription) string {
   203  	b, err := json.Marshal(obj)
   204  	if err != nil {
   205  		panic(err)
   206  	}
   207  
   208  	return base64.StdEncoding.EncodeToString(b)
   209  }
   210  
   211  // Decode a base64 and unmarshal JSON into a SessionDescription
   212  func decode(in string, obj *webrtc.SessionDescription) {
   213  	b, err := base64.StdEncoding.DecodeString(in)
   214  	if err != nil {
   215  		panic(err)
   216  	}
   217  
   218  	if err = json.Unmarshal(b, obj); err != nil {
   219  		panic(err)
   220  	}
   221  }