1package main 2 3import ( 4 "fmt" 5 "math/rand" 6 "os" 7 "time" 8 9 "github.com/pion/webrtc/v2" 10 "github.com/pion/webrtc/v2/pkg/media" 11 "github.com/pion/webrtc/v2/pkg/media/ivfreader" 12 13 "github.com/pion/webrtc/v2/examples/internal/signal" 14) 15 16func main() { 17 // Wait for the offer to be pasted 18 offer := webrtc.SessionDescription{} 19 signal.Decode(signal.MustReadStdin(), &offer) 20 21 // We make our own mediaEngine so we can place the sender's codecs in it. This because we must use the 22 // dynamic media type from the sender in our answer. This is not required if we are the offerer 23 mediaEngine := webrtc.MediaEngine{} 24 err := mediaEngine.PopulateFromSDP(offer) 25 if err != nil { 26 panic(err) 27 } 28 29 // Search for VP8 Payload type. If the offer doesn't support VP8 exit since 30 // since they won't be able to decode anything we send them 31 var payloadType uint8 32 for _, videoCodec := range mediaEngine.GetCodecsByKind(webrtc.RTPCodecTypeVideo) { 33 if videoCodec.Name == "VP8" { 34 payloadType = videoCodec.PayloadType 35 break 36 } 37 } 38 if payloadType == 0 { 39 panic("Remote peer does not support VP8") 40 } 41 42 // Create a new RTCPeerConnection 43 api := webrtc.NewAPI(webrtc.WithMediaEngine(mediaEngine)) 44 peerConnection, err := api.NewPeerConnection(webrtc.Configuration{ 45 ICEServers: []webrtc.ICEServer{ 46 { 47 URLs: []string{"stun:stun.l.google.com:19302"}, 48 }, 49 }, 50 }) 51 if err != nil { 52 panic(err) 53 } 54 55 // Create a video track 56 videoTrack, err := peerConnection.NewTrack(payloadType, rand.Uint32(), "video", "pion") 57 if err != nil { 58 panic(err) 59 } 60 if _, err = peerConnection.AddTrack(videoTrack); err != nil { 61 panic(err) 62 } 63 64 go func() { 65 // Open a IVF file and start reading using our IVFReader 66 file, ivfErr := os.Open("output.ivf") 67 if ivfErr != nil { 68 panic(ivfErr) 69 } 70 71 ivf, header, ivfErr := ivfreader.NewWith(file) 72 if ivfErr != nil { 73 panic(ivfErr) 74 } 75 76 // 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. 77 // This isn't required since the video is timestamped, but we will such much higher loss if we send all at once. 78 sleepTime := time.Millisecond * time.Duration((float32(header.TimebaseNumerator)/float32(header.TimebaseDenominator))*1000) 79 for { 80 frame, _, ivfErr := ivf.ParseNextFrame() 81 if ivfErr != nil { 82 panic(ivfErr) 83 } 84 85 time.Sleep(sleepTime) 86 if ivfErr = videoTrack.WriteSample(media.Sample{Data: frame, Samples: 90000}); ivfErr != nil { 87 panic(ivfErr) 88 } 89 } 90 }() 91 92 // Set the handler for ICE connection state 93 // This will notify you when the peer has connected/disconnected 94 peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) { 95 fmt.Printf("Connection State has changed %s \n", connectionState.String()) 96 }) 97 98 // Set the remote SessionDescription 99 if err = peerConnection.SetRemoteDescription(offer); err != nil { 100 panic(err) 101 } 102 103 // Create answer 104 answer, err := peerConnection.CreateAnswer(nil) 105 if err != nil { 106 panic(err) 107 } 108 109 // Sets the LocalDescription, and starts our UDP listeners 110 if err = peerConnection.SetLocalDescription(answer); err != nil { 111 panic(err) 112 } 113 114 // Output the answer in base64 so we can paste it in browser 115 fmt.Println(signal.Encode(answer)) 116 117 // Block forever 118 select {} 119} 120