1// +build quic 2 3package main 4 5import ( 6 "flag" 7 "fmt" 8 "time" 9 10 "github.com/pion/quic" 11 "github.com/pion/webrtc/v2" 12 13 "github.com/pion/webrtc/v2/examples/internal/signal" 14) 15 16const messageSize = 15 17 18func main() { 19 isOffer := flag.Bool("offer", false, "Act as the offerer if set") 20 flag.Parse() 21 22 // This example shows off the experimental implementation of webrtc-quic. 23 24 // Everything below is the Pion WebRTC (ORTC) API! Thanks for using it ❤️. 25 26 // Create an API object 27 api := webrtc.NewAPI() 28 29 // Prepare ICE gathering options 30 iceOptions := webrtc.ICEGatherOptions{ 31 ICEServers: []webrtc.ICEServer{ 32 {URLs: []string{"stun:stun.l.google.com:19302"}}, 33 }, 34 } 35 36 // Create the ICE gatherer 37 gatherer, err := api.NewICEGatherer(iceOptions) 38 if err != nil { 39 panic(err) 40 } 41 42 // Construct the ICE transport 43 ice := api.NewICETransport(gatherer) 44 45 // Construct the Quic transport 46 qt, err := api.NewQUICTransport(ice, nil) 47 if err != nil { 48 panic(err) 49 } 50 51 // Handle incoming streams 52 qt.OnBidirectionalStream(func(stream *quic.BidirectionalStream) { 53 fmt.Printf("New stream %d\n", stream.StreamID()) 54 55 // Handle reading from the stream 56 go ReadLoop(stream) 57 58 // Handle writing to the stream 59 go WriteLoop(stream) 60 }) 61 62 // Gather candidates 63 err = gatherer.Gather() 64 if err != nil { 65 panic(err) 66 } 67 68 iceCandidates, err := gatherer.GetLocalCandidates() 69 if err != nil { 70 panic(err) 71 } 72 73 iceParams, err := gatherer.GetLocalParameters() 74 if err != nil { 75 panic(err) 76 } 77 78 quicParams, err := qt.GetLocalParameters() 79 if err != nil { 80 panic(err) 81 } 82 83 s := Signal{ 84 ICECandidates: iceCandidates, 85 ICEParameters: iceParams, 86 QuicParameters: quicParams, 87 } 88 89 // Exchange the information 90 fmt.Println(signal.Encode(s)) 91 remoteSignal := Signal{} 92 signal.Decode(signal.MustReadStdin(), &remoteSignal) 93 94 iceRole := webrtc.ICERoleControlled 95 if *isOffer { 96 iceRole = webrtc.ICERoleControlling 97 } 98 99 err = ice.SetRemoteCandidates(remoteSignal.ICECandidates) 100 if err != nil { 101 panic(err) 102 } 103 104 // Start the ICE transport 105 err = ice.Start(nil, remoteSignal.ICEParameters, &iceRole) 106 if err != nil { 107 panic(err) 108 } 109 110 // Start the Quic transport 111 err = qt.Start(remoteSignal.QuicParameters) 112 if err != nil { 113 panic(err) 114 } 115 116 // Construct the stream as the offerer 117 if *isOffer { 118 var stream *quic.BidirectionalStream 119 stream, err = qt.CreateBidirectionalStream() 120 if err != nil { 121 panic(err) 122 } 123 124 // Handle reading from the stream 125 go ReadLoop(stream) 126 127 // Handle writing to the stream 128 go WriteLoop(stream) 129 } 130 131 select {} 132} 133 134// Signal is used to exchange signaling info. 135// This is not part of the ORTC spec. You are free 136// to exchange this information any way you want. 137type Signal struct { 138 ICECandidates []webrtc.ICECandidate `json:"iceCandidates"` 139 ICEParameters webrtc.ICEParameters `json:"iceParameters"` 140 QuicParameters webrtc.QUICParameters `json:"quicParameters"` 141} 142 143// ReadLoop reads from the stream 144func ReadLoop(s *quic.BidirectionalStream) { 145 for { 146 buffer := make([]byte, messageSize) 147 params, err := s.ReadInto(buffer) 148 if err != nil { 149 panic(err) 150 } 151 152 fmt.Printf("Message from stream '%d': %s\n", s.StreamID(), string(buffer[:params.Amount])) 153 } 154} 155 156// WriteLoop writes to the stream 157func WriteLoop(s *quic.BidirectionalStream) { 158 for range time.NewTicker(5 * time.Second).C { 159 message := signal.RandSeq(messageSize) 160 fmt.Printf("Sending %s \n", message) 161 162 data := quic.StreamWriteParameters{ 163 Data: []byte(message), 164 } 165 err := s.Write(data) 166 if err != nil { 167 panic(err) 168 } 169 } 170} 171