github.com/pion/webrtc/v3@v3.2.24/examples/insertable-streams/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  // insertable-streams demonstrates how to use insertable streams with Pion
     8  package main
     9  
    10  import (
    11  	"context"
    12  	"errors"
    13  	"fmt"
    14  	"io"
    15  	"os"
    16  	"time"
    17  
    18  	"github.com/pion/webrtc/v3"
    19  	"github.com/pion/webrtc/v3/examples/internal/signal"
    20  	"github.com/pion/webrtc/v3/pkg/media"
    21  	"github.com/pion/webrtc/v3/pkg/media/ivfreader"
    22  )
    23  
    24  const cipherKey = 0xAA
    25  
    26  // nolint:gocognit
    27  func main() {
    28  	peerConnection, err := webrtc.NewPeerConnection(webrtc.Configuration{
    29  		ICEServers: []webrtc.ICEServer{
    30  			{
    31  				URLs: []string{"stun:stun.l.google.com:19302"},
    32  			},
    33  		},
    34  	})
    35  	if err != nil {
    36  		panic(err)
    37  	}
    38  	defer func() {
    39  		if cErr := peerConnection.Close(); cErr != nil {
    40  			fmt.Printf("cannot close peerConnection: %v\n", cErr)
    41  		}
    42  	}()
    43  
    44  	// Create a video track
    45  	videoTrack, err := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion")
    46  	if err != nil {
    47  		panic(err)
    48  	}
    49  	rtpSender, err := peerConnection.AddTrack(videoTrack)
    50  	if err != nil {
    51  		panic(err)
    52  	}
    53  
    54  	// Read incoming RTCP packets
    55  	// Before these packets are returned they are processed by interceptors. For things
    56  	// like NACK this needs to be called.
    57  	go func() {
    58  		rtcpBuf := make([]byte, 1500)
    59  		for {
    60  			if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil {
    61  				return
    62  			}
    63  		}
    64  	}()
    65  
    66  	iceConnectedCtx, iceConnectedCtxCancel := context.WithCancel(context.Background())
    67  	go func() {
    68  		// Open a IVF file and start reading using our IVFReader
    69  		file, ivfErr := os.Open("output.ivf")
    70  		if ivfErr != nil {
    71  			panic(ivfErr)
    72  		}
    73  
    74  		ivf, header, ivfErr := ivfreader.NewWith(file)
    75  		if ivfErr != nil {
    76  			panic(ivfErr)
    77  		}
    78  
    79  		// Wait for connection established
    80  		<-iceConnectedCtx.Done()
    81  
    82  		// Send our video file frame at a time. Pace our sending so we send it at the same speed it should be played back as.
    83  		// This isn't required since the video is timestamped, but we will such much higher loss if we send all at once.
    84  		sleepTime := time.Millisecond * time.Duration((float32(header.TimebaseNumerator)/float32(header.TimebaseDenominator))*1000)
    85  		for {
    86  			frame, _, ivfErr := ivf.ParseNextFrame()
    87  			if errors.Is(ivfErr, io.EOF) {
    88  				fmt.Printf("All frames parsed and sent")
    89  				os.Exit(0)
    90  			}
    91  
    92  			if ivfErr != nil {
    93  				panic(ivfErr)
    94  			}
    95  
    96  			// Encrypt video using XOR Cipher
    97  			for i := range frame {
    98  				frame[i] ^= cipherKey
    99  			}
   100  
   101  			time.Sleep(sleepTime)
   102  			if ivfErr = videoTrack.WriteSample(media.Sample{Data: frame, Duration: time.Second}); ivfErr != nil {
   103  				panic(ivfErr)
   104  			}
   105  		}
   106  	}()
   107  
   108  	// Set the handler for ICE connection state
   109  	// This will notify you when the peer has connected/disconnected
   110  	peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
   111  		fmt.Printf("Connection State has changed %s \n", connectionState.String())
   112  		if connectionState == webrtc.ICEConnectionStateConnected {
   113  			iceConnectedCtxCancel()
   114  		}
   115  	})
   116  
   117  	// Set the handler for Peer connection state
   118  	// This will notify you when the peer has connected/disconnected
   119  	peerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) {
   120  		fmt.Printf("Peer Connection State has changed: %s\n", s.String())
   121  
   122  		if s == webrtc.PeerConnectionStateFailed {
   123  			// Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart.
   124  			// Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout.
   125  			// Note that the PeerConnection may come back from PeerConnectionStateDisconnected.
   126  			fmt.Println("Peer Connection has gone to failed exiting")
   127  			os.Exit(0)
   128  		}
   129  	})
   130  
   131  	// Wait for the offer to be pasted
   132  	offer := webrtc.SessionDescription{}
   133  	signal.Decode(signal.MustReadStdin(), &offer)
   134  
   135  	// Set the remote SessionDescription
   136  	if err = peerConnection.SetRemoteDescription(offer); err != nil {
   137  		panic(err)
   138  	}
   139  
   140  	// Create answer
   141  	answer, err := peerConnection.CreateAnswer(nil)
   142  	if err != nil {
   143  		panic(err)
   144  	}
   145  
   146  	// Create channel that is blocked until ICE Gathering is complete
   147  	gatherComplete := webrtc.GatheringCompletePromise(peerConnection)
   148  
   149  	// Sets the LocalDescription, and starts our UDP listeners
   150  	if err = peerConnection.SetLocalDescription(answer); err != nil {
   151  		panic(err)
   152  	}
   153  
   154  	// Block until ICE Gathering is complete, disabling trickle ICE
   155  	// we do this because we only can exchange one signaling message
   156  	// in a production application you should exchange ICE Candidates via OnICECandidate
   157  	<-gatherComplete
   158  
   159  	// Output the answer in base64 so we can paste it in browser
   160  	fmt.Println(signal.Encode(*peerConnection.LocalDescription()))
   161  
   162  	// Block forever
   163  	select {}
   164  }