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