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 }