blob: 115624461aecb8e87f9946c40ca60ac92d861268 [file] [log] [blame]
James Kuszmaul4a42b182021-01-17 11:32:46 -08001#include "helper/handler.h"
2#include "helper/utils.h"
3#include <rawrtc.h>
4#include <rawrtcc.h>
5#include <rawrtcdc.h>
6#include <re.h>
7#include <stdlib.h> // exit
8#include <unistd.h> // STDIN_FILENO
9
10#define DEBUG_MODULE "peer-connection-app"
11#define DEBUG_LEVEL 7
12#include <re_dbg.h>
13
14enum {
15 TRANSPORT_BUFFER_LENGTH = 1048576, // 1 MiB
16};
17
18// Note: Shadows struct client
19struct peer_connection_client {
20 char* name;
21 char** ice_candidate_types;
22 size_t n_ice_candidate_types;
23 bool offering;
24 struct rawrtc_peer_connection_configuration* configuration;
25 struct rawrtc_peer_connection* connection;
26 struct data_channel_helper* data_channel_negotiated;
27 struct data_channel_helper* data_channel;
28};
29
30static void print_local_description(struct peer_connection_client* const client);
31
32static struct tmr timer = {0};
33
34static void timer_handler(void* arg) {
35 struct data_channel_helper* const channel = arg;
36 struct peer_connection_client* const client = (struct peer_connection_client*) channel->client;
37 struct mbuf* buffer;
38 enum rawrtc_code error;
39
40 // Compose message (16 KiB)
41 buffer = mbuf_alloc(1 << 14);
42 EOE(buffer ? RAWRTC_CODE_SUCCESS : RAWRTC_CODE_NO_MEMORY);
43 EOR(mbuf_fill(buffer, 'M', mbuf_get_space(buffer)));
44 mbuf_set_pos(buffer, 0);
45
46 // Send message
47 DEBUG_PRINTF("(%s) Sending %zu bytes\n", client->name, mbuf_get_left(buffer));
48 error = rawrtc_data_channel_send(channel->channel, buffer, true);
49 if (error) {
50 DEBUG_WARNING("Could not send, reason: %s\n", rawrtc_code_to_str(error));
51 }
52 mem_deref(buffer);
53
54 // Close if offering
55 if (client->offering) {
56 // Close bear-noises
57 DEBUG_PRINTF("(%s) Closing channel\n", client->name, channel->label);
58 EOR(rawrtc_data_channel_close(client->data_channel->channel));
59 }
60}
61
62static void data_channel_open_handler(void* const arg) {
63 struct data_channel_helper* const channel = arg;
64 struct peer_connection_client* const client = (struct peer_connection_client*) channel->client;
65 struct mbuf* buffer;
66 enum rawrtc_code error;
67
68 // Print open event
69 default_data_channel_open_handler(arg);
70
71 // Send data delayed on bear-noises
72 if (str_cmp(channel->label, "bear-noises") == 0) {
73 tmr_start(&timer, 30000, timer_handler, channel);
74 return;
75 }
76
77 // Compose message (8 KiB)
78 buffer = mbuf_alloc(1 << 13);
79 EOE(buffer ? RAWRTC_CODE_SUCCESS : RAWRTC_CODE_NO_MEMORY);
80 EOR(mbuf_fill(buffer, 'M', mbuf_get_space(buffer)));
81 mbuf_set_pos(buffer, 0);
82
83 // Send message
84 DEBUG_PRINTF("(%s) Sending %zu bytes\n", client->name, mbuf_get_left(buffer));
85 error = rawrtc_data_channel_send(channel->channel, buffer, true);
86 if (error) {
87 DEBUG_WARNING("Could not send, reason: %s\n", rawrtc_code_to_str(error));
88 }
89 mem_deref(buffer);
90}
91
92static void negotiation_needed_handler(void* const arg) {
93 struct peer_connection_client* const client = arg;
94
95 // Print negotiation needed
96 default_negotiation_needed_handler(arg);
97
98 // Offering: Create and set local description
99 if (client->offering) {
100 struct rawrtc_peer_connection_description* description;
101 EOE(rawrtc_peer_connection_create_offer(&description, client->connection, false));
102 EOE(rawrtc_peer_connection_set_local_description(client->connection, description));
103 mem_deref(description);
104 }
105}
106
107static void connection_state_change_handler(
108 enum rawrtc_peer_connection_state const state, // read-only
109 void* const arg) {
110 struct peer_connection_client* const client = arg;
111
112 // Print state
113 default_peer_connection_state_change_handler(state, arg);
114
115 // Open? Create new channel
116 // Note: Since this state can switch from 'connected' to 'disconnected' and back again, we
117 // need to make sure we don't re-create data channels unintended.
118 // TODO: Move this once we can create data channels earlier
119 if (!client->data_channel && state == RAWRTC_PEER_CONNECTION_STATE_CONNECTED) {
120 struct rawrtc_data_channel_parameters* channel_parameters;
121 char* const label = client->offering ? "bear-noises" : "lion-noises";
122
123 // Create data channel helper for in-band negotiated data channel
124 data_channel_helper_create(&client->data_channel, (struct client*) client, label);
125
126 // Create data channel parameters
127 EOE(rawrtc_data_channel_parameters_create(
128 &channel_parameters, client->data_channel->label,
129 RAWRTC_DATA_CHANNEL_TYPE_RELIABLE_UNORDERED, 0, NULL, false, 0));
130
131 // Create data channel
132 EOE(rawrtc_peer_connection_create_data_channel(
133 &client->data_channel->channel, client->connection, channel_parameters,
134 data_channel_open_handler, default_data_channel_buffered_amount_low_handler,
135 default_data_channel_error_handler, default_data_channel_close_handler,
136 default_data_channel_message_handler, client->data_channel));
137
138 // Un-reference data channel parameters
139 mem_deref(channel_parameters);
140 }
141}
142
143static void local_candidate_handler(
144 struct rawrtc_peer_connection_ice_candidate* const candidate,
145 char const* const url, // read-only
146 void* const arg) {
147 struct peer_connection_client* const client = arg;
148
149 // Print local candidate
150 default_peer_connection_local_candidate_handler(candidate, url, arg);
151
152 // Print local description (if last candidate)
153 if (!candidate) {
154 print_local_description(client);
155 }
156}
157
158static void client_init(struct peer_connection_client* const client) {
159 struct rawrtc_data_channel_parameters* channel_parameters;
160
161 // Create peer connection
162 EOE(rawrtc_peer_connection_create(
163 &client->connection, client->configuration, negotiation_needed_handler,
164 local_candidate_handler, default_peer_connection_local_candidate_error_handler,
165 default_signaling_state_change_handler, default_ice_transport_state_change_handler,
166 default_ice_gatherer_state_change_handler, connection_state_change_handler,
167 default_data_channel_handler, client));
168
169 // Create data channel helper for pre-negotiated data channel
170 data_channel_helper_create(
171 &client->data_channel_negotiated, (struct client*) client, "cat-noises");
172
173 // Create data channel parameters
174 EOE(rawrtc_data_channel_parameters_create(
175 &channel_parameters, client->data_channel_negotiated->label,
176 RAWRTC_DATA_CHANNEL_TYPE_RELIABLE_ORDERED, 0, NULL, true, 0));
177
178 // Create pre-negotiated data channel
179 EOE(rawrtc_peer_connection_create_data_channel(
180 &client->data_channel_negotiated->channel, client->connection, channel_parameters,
181 data_channel_open_handler, default_data_channel_buffered_amount_low_handler,
182 default_data_channel_error_handler, default_data_channel_close_handler,
183 default_data_channel_message_handler, client->data_channel_negotiated));
184
185 // TODO: Create in-band negotiated data channel
186 // TODO: Return some kind of promise that resolves once the data channel can be created
187
188 // Un-reference data channel parameters
189 mem_deref(channel_parameters);
190}
191
192static void client_stop(struct peer_connection_client* const client) {
193 EOE(rawrtc_peer_connection_close(client->connection));
194
195 // Un-reference & close
196 client->data_channel = mem_deref(client->data_channel);
197 client->data_channel_negotiated = mem_deref(client->data_channel_negotiated);
198 client->connection = mem_deref(client->connection);
199 client->configuration = mem_deref(client->configuration);
200
201 // Stop listening on STDIN
202 fd_close(STDIN_FILENO);
203}
204
205static void parse_remote_description(int flags, void* arg) {
206 struct peer_connection_client* const client = arg;
207 enum rawrtc_code error;
208 bool do_exit = false;
209 struct odict* dict = NULL;
210 char* type_str;
211 char* sdp;
212 enum rawrtc_sdp_type type;
213 struct rawrtc_peer_connection_description* remote_description = NULL;
214 (void) flags;
215
216 // Get dict from JSON
217 error = get_json_stdin(&dict);
218 if (error) {
219 do_exit = error == RAWRTC_CODE_NO_VALUE;
220 goto out;
221 }
222
223 // Decode JSON
224 error |= dict_get_entry(&type_str, dict, "type", ODICT_STRING, true);
225 error |= dict_get_entry(&sdp, dict, "sdp", ODICT_STRING, true);
226 if (error) {
227 DEBUG_WARNING("Invalid remote description\n");
228 goto out;
229 }
230
231 // Convert to description
232 error = rawrtc_str_to_sdp_type(&type, type_str);
233 if (error) {
234 DEBUG_WARNING("Invalid SDP type in remote description: '%s'\n", type_str);
235 goto out;
236 }
237 error = rawrtc_peer_connection_description_create(&remote_description, type, sdp);
238 if (error) {
239 DEBUG_WARNING("Cannot parse remote description: %s\n", rawrtc_code_to_str(error));
240 goto out;
241 }
242
243 // Set remote description
244 DEBUG_INFO("Applying remote description\n");
245 EOE(rawrtc_peer_connection_set_remote_description(client->connection, remote_description));
246
247 // Answering: Create and set local description
248 if (!client->offering) {
249 struct rawrtc_peer_connection_description* local_description;
250 EOE(rawrtc_peer_connection_create_answer(&local_description, client->connection));
251 EOE(rawrtc_peer_connection_set_local_description(client->connection, local_description));
252 mem_deref(local_description);
253 }
254
255out:
256 // Un-reference
257 mem_deref(remote_description);
258 mem_deref(dict);
259
260 // Exit?
261 if (do_exit) {
262 DEBUG_NOTICE("Exiting\n");
263
264 // Stop client & bye
265 tmr_cancel(&timer);
266 re_cancel();
267 }
268}
269
270static void print_local_description(struct peer_connection_client* const client) {
271 struct rawrtc_peer_connection_description* description;
272 enum rawrtc_sdp_type type;
273 char* sdp;
274 struct odict* dict;
275
276 // Get description
277 EOE(rawrtc_peer_connection_get_local_description(&description, client->connection));
278
279 // Get SDP type & the SDP itself
280 EOE(rawrtc_peer_connection_description_get_sdp_type(&type, description));
281 EOE(rawrtc_peer_connection_description_get_sdp(&sdp, description));
282
283 // Create dict & add entries
284 EOR(odict_alloc(&dict, 16));
285 EOR(odict_entry_add(dict, "type", ODICT_STRING, rawrtc_sdp_type_to_str(type)));
286 EOR(odict_entry_add(dict, "sdp", ODICT_STRING, sdp));
287
288 // Print local description as JSON
289 DEBUG_INFO("Local Description:\n%H\n", json_encode_odict, dict);
290
291 // Un-reference
292 mem_deref(dict);
293 mem_deref(sdp);
294 mem_deref(description);
295}
296
297static void exit_with_usage(char* program) {
298 DEBUG_WARNING("Usage: %s <0|1 (offering)> [<ice-candidate-type> ...]", program);
299 exit(1);
300}
301
302int main(int argc, char* argv[argc + 1]) {
303 char** ice_candidate_types = NULL;
304 size_t n_ice_candidate_types = 0;
305 enum rawrtc_ice_role role;
306 struct rawrtc_peer_connection_configuration* configuration;
307 char* const turn_zwuenf_org_urls[] = {"stun:turn.zwuenf.org"};
308 struct peer_connection_client client = {0};
309 (void) client.ice_candidate_types;
310 (void) client.n_ice_candidate_types;
311
312 // Debug
313 dbg_init(DBG_DEBUG, DBG_ALL);
314 DEBUG_PRINTF("Init\n");
315
316 // Initialise
317 EOE(rawrtc_init(true));
318
319 // Check arguments length
320 if (argc < 2) {
321 exit_with_usage(argv[0]);
322 }
323
324 // Get role
325 // Note: We handle it as an ICE role (because that is pretty close)
326 if (get_ice_role(&role, argv[1])) {
327 exit_with_usage(argv[0]);
328 }
329
330 // Get enabled ICE candidate types to be added (optional)
331 if (argc >= 3) {
332 ice_candidate_types = &argv[2];
333 n_ice_candidate_types = (size_t) argc - 2;
334 }
335
336 // Create peer connection configuration
337 EOE(rawrtc_peer_connection_configuration_create(&configuration, RAWRTC_ICE_GATHER_POLICY_ALL));
338
339 // Add ICE servers to configuration
340 EOE(rawrtc_peer_connection_configuration_add_ice_server(
341 configuration, turn_zwuenf_org_urls, ARRAY_SIZE(turn_zwuenf_org_urls), NULL, NULL,
342 RAWRTC_ICE_CREDENTIAL_TYPE_NONE));
343
344 // Set the SCTP transport's buffer length
345 EOE(rawrtc_peer_connection_configuration_set_sctp_buffer_length(
346 configuration, TRANSPORT_BUFFER_LENGTH, TRANSPORT_BUFFER_LENGTH));
347
348 // Set client fields
349 client.name = "A";
350 client.ice_candidate_types = ice_candidate_types;
351 client.n_ice_candidate_types = n_ice_candidate_types;
352 client.configuration = configuration;
353 client.offering = role == RAWRTC_ICE_ROLE_CONTROLLING ? true : false;
354
355 // Setup client
356 client_init(&client);
357
358 // Listen on stdin
359 EOR(fd_listen(STDIN_FILENO, FD_READ, parse_remote_description, &client));
360
361 // Start main loop
362 EOR(re_main(default_signal_handler));
363
364 // Stop client & bye
365 client_stop(&client);
366 before_exit();
367 return 0;
368}