webrtc-ruby 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,757 @@
1
+ /*
2
+ * WebRTC Ruby - libdatachannel wrapper
3
+ * Real WebRTC implementation using libdatachannel
4
+ */
5
+
6
+ #include "webrtc_ruby.h"
7
+ #include <rtc/rtc.h>
8
+ #include <stdlib.h>
9
+ #include <string.h>
10
+ #include <stdio.h>
11
+
12
+ /* Internal structures to bridge our API with libdatachannel */
13
+
14
+ struct webrtc_peer_connection {
15
+ int pc_id; /* libdatachannel peer connection id */
16
+ webrtc_ice_candidate_callback ice_candidate_cb;
17
+ void* ice_candidate_user_data;
18
+ webrtc_state_change_callback connection_state_cb;
19
+ void* connection_state_user_data;
20
+ webrtc_data_channel_callback data_channel_cb;
21
+ void* data_channel_user_data;
22
+ char* local_sdp;
23
+ char* local_type;
24
+ char* remote_sdp;
25
+ char* remote_type;
26
+ };
27
+
28
+ struct webrtc_data_channel {
29
+ int dc_id; /* libdatachannel data channel id */
30
+ struct webrtc_peer_connection* pc;
31
+ char* label;
32
+ char* protocol;
33
+ bool ordered;
34
+ int max_retransmits;
35
+ int max_packet_life_time;
36
+ bool negotiated;
37
+ int stream_id;
38
+ webrtc_void_callback open_cb;
39
+ void* open_user_data;
40
+ webrtc_message_callback message_cb;
41
+ void* message_user_data;
42
+ webrtc_void_callback close_cb;
43
+ void* close_user_data;
44
+ };
45
+
46
+ struct webrtc_session_description {
47
+ char* type;
48
+ char* sdp;
49
+ };
50
+
51
+ struct webrtc_ice_candidate {
52
+ char* candidate;
53
+ char* sdp_mid;
54
+ int sdp_mline_index;
55
+ };
56
+
57
+ /* Global state */
58
+ static bool g_initialized = false;
59
+
60
+ /* Callback forwarders */
61
+
62
+ static void on_local_description(int pc, const char* sdp, const char* type, void* ptr) {
63
+ struct webrtc_peer_connection* conn = (struct webrtc_peer_connection*)ptr;
64
+ if (conn) {
65
+ free(conn->local_sdp);
66
+ free(conn->local_type);
67
+ conn->local_sdp = sdp ? strdup(sdp) : NULL;
68
+ conn->local_type = type ? strdup(type) : NULL;
69
+ }
70
+ }
71
+
72
+ static void on_local_candidate(int pc, const char* cand, const char* mid, void* ptr) {
73
+ struct webrtc_peer_connection* conn = (struct webrtc_peer_connection*)ptr;
74
+ if (conn && conn->ice_candidate_cb && cand) {
75
+ conn->ice_candidate_cb(cand, mid, 0, conn->ice_candidate_user_data);
76
+ }
77
+ }
78
+
79
+ static void on_state_change(int pc, rtcState state, void* ptr) {
80
+ struct webrtc_peer_connection* conn = (struct webrtc_peer_connection*)ptr;
81
+ if (conn && conn->connection_state_cb) {
82
+ conn->connection_state_cb((int)state, conn->connection_state_user_data);
83
+ }
84
+ }
85
+
86
+ static void on_data_channel(int pc, int dc, void* ptr) {
87
+ struct webrtc_peer_connection* conn = (struct webrtc_peer_connection*)ptr;
88
+ if (conn && conn->data_channel_cb) {
89
+ /* Create wrapper for incoming data channel */
90
+ struct webrtc_data_channel* channel = calloc(1, sizeof(struct webrtc_data_channel));
91
+ if (channel) {
92
+ channel->dc_id = dc;
93
+ channel->pc = conn;
94
+
95
+ /* Get label */
96
+ char label_buf[256];
97
+ if (rtcGetDataChannelLabel(dc, label_buf, sizeof(label_buf)) > 0) {
98
+ channel->label = strdup(label_buf);
99
+ }
100
+
101
+ /* Get protocol */
102
+ char proto_buf[256];
103
+ if (rtcGetDataChannelProtocol(dc, proto_buf, sizeof(proto_buf)) > 0) {
104
+ channel->protocol = strdup(proto_buf);
105
+ }
106
+
107
+ /* Get reliability */
108
+ rtcReliability reliability;
109
+ if (rtcGetDataChannelReliability(dc, &reliability) == 0) {
110
+ channel->ordered = !reliability.unordered;
111
+ channel->max_retransmits = reliability.maxRetransmits;
112
+ channel->max_packet_life_time = reliability.maxPacketLifeTime;
113
+ }
114
+
115
+ channel->stream_id = rtcGetDataChannelStream(dc);
116
+
117
+ conn->data_channel_cb(channel, conn->data_channel_user_data);
118
+ }
119
+ }
120
+ }
121
+
122
+ static void on_dc_open(int id, void* ptr) {
123
+ struct webrtc_data_channel* dc = (struct webrtc_data_channel*)ptr;
124
+ if (dc && dc->open_cb) {
125
+ dc->open_cb(dc->open_user_data);
126
+ }
127
+ }
128
+
129
+ static void on_dc_message(int id, const char* message, int size, void* ptr) {
130
+ struct webrtc_data_channel* dc = (struct webrtc_data_channel*)ptr;
131
+ if (dc && dc->message_cb) {
132
+ bool is_binary = (size >= 0);
133
+ int actual_size = (size < 0) ? (int)strlen(message) : size;
134
+ dc->message_cb((const uint8_t*)message, (size_t)actual_size, is_binary, dc->message_user_data);
135
+ }
136
+ }
137
+
138
+ static void on_dc_closed(int id, void* ptr) {
139
+ struct webrtc_data_channel* dc = (struct webrtc_data_channel*)ptr;
140
+ if (dc && dc->close_cb) {
141
+ dc->close_cb(dc->close_user_data);
142
+ }
143
+ }
144
+
145
+ /* Public API implementation */
146
+
147
+ int webrtc_init(void) {
148
+ if (g_initialized) {
149
+ return 0;
150
+ }
151
+
152
+ rtcInitLogger(RTC_LOG_WARNING, NULL);
153
+ rtcPreload();
154
+ g_initialized = true;
155
+ return 0;
156
+ }
157
+
158
+ void webrtc_cleanup(void) {
159
+ if (g_initialized) {
160
+ rtcCleanup();
161
+ g_initialized = false;
162
+ }
163
+ }
164
+
165
+ webrtc_peer_connection_t webrtc_peer_connection_create(
166
+ const webrtc_configuration_t* config,
167
+ webrtc_error_t* error) {
168
+
169
+ struct webrtc_peer_connection* pc = calloc(1, sizeof(struct webrtc_peer_connection));
170
+ if (!pc) {
171
+ if (error) {
172
+ error->code = 1;
173
+ error->message = "Failed to allocate memory";
174
+ }
175
+ return NULL;
176
+ }
177
+
178
+ /* Prepare libdatachannel configuration */
179
+ rtcConfiguration rtc_config;
180
+ memset(&rtc_config, 0, sizeof(rtc_config));
181
+
182
+ if (config && config->ice_servers && config->ice_servers_count > 0) {
183
+ rtc_config.iceServers = config->ice_servers;
184
+ rtc_config.iceServersCount = (int)config->ice_servers_count;
185
+ }
186
+
187
+ /* Create peer connection */
188
+ int pc_id = rtcCreatePeerConnection(&rtc_config);
189
+ if (pc_id < 0) {
190
+ free(pc);
191
+ if (error) {
192
+ error->code = 1;
193
+ error->message = "Failed to create peer connection";
194
+ }
195
+ return NULL;
196
+ }
197
+
198
+ pc->pc_id = pc_id;
199
+
200
+ /* Set user pointer for callbacks */
201
+ rtcSetUserPointer(pc_id, pc);
202
+
203
+ /* Set up callbacks */
204
+ rtcSetLocalDescriptionCallback(pc_id, on_local_description);
205
+ rtcSetLocalCandidateCallback(pc_id, on_local_candidate);
206
+ rtcSetStateChangeCallback(pc_id, on_state_change);
207
+ rtcSetDataChannelCallback(pc_id, on_data_channel);
208
+
209
+ if (error) {
210
+ error->code = 0;
211
+ error->message = NULL;
212
+ }
213
+
214
+ return pc;
215
+ }
216
+
217
+ void webrtc_peer_connection_destroy(webrtc_peer_connection_t pc) {
218
+ if (pc) {
219
+ rtcDeletePeerConnection(pc->pc_id);
220
+ free(pc->local_sdp);
221
+ free(pc->local_type);
222
+ free(pc->remote_sdp);
223
+ free(pc->remote_type);
224
+ free(pc);
225
+ }
226
+ }
227
+
228
+ int webrtc_peer_connection_create_offer(
229
+ webrtc_peer_connection_t pc,
230
+ webrtc_session_description_t* out_sdp,
231
+ webrtc_error_t* error) {
232
+
233
+ if (!pc || !out_sdp) {
234
+ if (error) {
235
+ error->code = 1;
236
+ error->message = "Invalid argument";
237
+ }
238
+ return -1;
239
+ }
240
+
241
+ /* Set local description with type "offer" */
242
+ int ret = rtcSetLocalDescription(pc->pc_id, "offer");
243
+ if (ret < 0) {
244
+ if (error) {
245
+ error->code = 1;
246
+ error->message = "Failed to create offer";
247
+ }
248
+ return -1;
249
+ }
250
+
251
+ /* Get the generated SDP */
252
+ char sdp_buf[16384];
253
+ int sdp_len = rtcGetLocalDescription(pc->pc_id, sdp_buf, sizeof(sdp_buf));
254
+ if (sdp_len < 0) {
255
+ if (error) {
256
+ error->code = 1;
257
+ error->message = "Failed to get local description";
258
+ }
259
+ return -1;
260
+ }
261
+
262
+ *out_sdp = webrtc_session_description_create("offer", sdp_buf, error);
263
+ if (!*out_sdp) {
264
+ return -1;
265
+ }
266
+
267
+ if (error) {
268
+ error->code = 0;
269
+ error->message = NULL;
270
+ }
271
+ return 0;
272
+ }
273
+
274
+ int webrtc_peer_connection_create_answer(
275
+ webrtc_peer_connection_t pc,
276
+ webrtc_session_description_t* out_sdp,
277
+ webrtc_error_t* error) {
278
+
279
+ if (!pc || !out_sdp) {
280
+ if (error) {
281
+ error->code = 1;
282
+ error->message = "Invalid argument";
283
+ }
284
+ return -1;
285
+ }
286
+
287
+ /* Set local description with type "answer" */
288
+ int ret = rtcSetLocalDescription(pc->pc_id, "answer");
289
+ if (ret < 0) {
290
+ if (error) {
291
+ error->code = 1;
292
+ error->message = "Failed to create answer";
293
+ }
294
+ return -1;
295
+ }
296
+
297
+ /* Get the generated SDP */
298
+ char sdp_buf[16384];
299
+ int sdp_len = rtcGetLocalDescription(pc->pc_id, sdp_buf, sizeof(sdp_buf));
300
+ if (sdp_len < 0) {
301
+ if (error) {
302
+ error->code = 1;
303
+ error->message = "Failed to get local description";
304
+ }
305
+ return -1;
306
+ }
307
+
308
+ *out_sdp = webrtc_session_description_create("answer", sdp_buf, error);
309
+ if (!*out_sdp) {
310
+ return -1;
311
+ }
312
+
313
+ if (error) {
314
+ error->code = 0;
315
+ error->message = NULL;
316
+ }
317
+ return 0;
318
+ }
319
+
320
+ int webrtc_peer_connection_set_local_description(
321
+ webrtc_peer_connection_t pc,
322
+ webrtc_session_description_t sdp,
323
+ webrtc_error_t* error) {
324
+
325
+ if (!pc || !sdp) {
326
+ if (error) {
327
+ error->code = 1;
328
+ error->message = "Invalid argument";
329
+ }
330
+ return -1;
331
+ }
332
+
333
+ /* Store local description */
334
+ free(pc->local_sdp);
335
+ free(pc->local_type);
336
+ pc->local_sdp = sdp->sdp ? strdup(sdp->sdp) : NULL;
337
+ pc->local_type = sdp->type ? strdup(sdp->type) : NULL;
338
+
339
+ /* libdatachannel handles this through rtcSetLocalDescription in create_offer/answer */
340
+
341
+ if (error) {
342
+ error->code = 0;
343
+ error->message = NULL;
344
+ }
345
+ return 0;
346
+ }
347
+
348
+ int webrtc_peer_connection_set_remote_description(
349
+ webrtc_peer_connection_t pc,
350
+ webrtc_session_description_t sdp,
351
+ webrtc_error_t* error) {
352
+
353
+ if (!pc || !sdp) {
354
+ if (error) {
355
+ error->code = 1;
356
+ error->message = "Invalid argument";
357
+ }
358
+ return -1;
359
+ }
360
+
361
+ const char* sdp_str = sdp->sdp ? sdp->sdp : "";
362
+ const char* type_str = sdp->type ? sdp->type : "offer";
363
+
364
+ int ret = rtcSetRemoteDescription(pc->pc_id, sdp_str, type_str);
365
+ if (ret < 0) {
366
+ if (error) {
367
+ error->code = 1;
368
+ error->message = "Failed to set remote description";
369
+ }
370
+ return -1;
371
+ }
372
+
373
+ /* Store remote description */
374
+ free(pc->remote_sdp);
375
+ free(pc->remote_type);
376
+ pc->remote_sdp = strdup(sdp_str);
377
+ pc->remote_type = strdup(type_str);
378
+
379
+ if (error) {
380
+ error->code = 0;
381
+ error->message = NULL;
382
+ }
383
+ return 0;
384
+ }
385
+
386
+ int webrtc_peer_connection_add_ice_candidate(
387
+ webrtc_peer_connection_t pc,
388
+ webrtc_ice_candidate_t candidate,
389
+ webrtc_error_t* error) {
390
+
391
+ if (!pc) {
392
+ if (error) {
393
+ error->code = 1;
394
+ error->message = "Invalid argument";
395
+ }
396
+ return -1;
397
+ }
398
+
399
+ if (candidate && candidate->candidate) {
400
+ const char* mid = candidate->sdp_mid ? candidate->sdp_mid : "";
401
+ int ret = rtcAddRemoteCandidate(pc->pc_id, candidate->candidate, mid);
402
+ if (ret < 0) {
403
+ if (error) {
404
+ error->code = 1;
405
+ error->message = "Failed to add ICE candidate";
406
+ }
407
+ return -1;
408
+ }
409
+ }
410
+
411
+ if (error) {
412
+ error->code = 0;
413
+ error->message = NULL;
414
+ }
415
+ return 0;
416
+ }
417
+
418
+ void webrtc_peer_connection_on_ice_candidate(
419
+ webrtc_peer_connection_t pc,
420
+ webrtc_ice_candidate_callback callback,
421
+ void* user_data) {
422
+ if (pc) {
423
+ pc->ice_candidate_cb = callback;
424
+ pc->ice_candidate_user_data = user_data;
425
+ }
426
+ }
427
+
428
+ void webrtc_peer_connection_on_connection_state_change(
429
+ webrtc_peer_connection_t pc,
430
+ webrtc_state_change_callback callback,
431
+ void* user_data) {
432
+ if (pc) {
433
+ pc->connection_state_cb = callback;
434
+ pc->connection_state_user_data = user_data;
435
+ }
436
+ }
437
+
438
+ void webrtc_peer_connection_on_data_channel(
439
+ webrtc_peer_connection_t pc,
440
+ webrtc_data_channel_callback callback,
441
+ void* user_data) {
442
+ if (pc) {
443
+ pc->data_channel_cb = callback;
444
+ pc->data_channel_user_data = user_data;
445
+ }
446
+ }
447
+
448
+ int webrtc_peer_connection_get_signaling_state(webrtc_peer_connection_t pc) {
449
+ if (!pc) return -1;
450
+
451
+ char buf[64];
452
+ int ret = rtcGetLocalDescriptionType(pc->pc_id, buf, sizeof(buf));
453
+
454
+ /* Map to our signaling state enum:
455
+ * 0 = stable
456
+ * 1 = have-local-offer
457
+ * 2 = have-remote-offer
458
+ */
459
+ if (ret < 0) {
460
+ return 0; /* stable */
461
+ }
462
+
463
+ if (strcmp(buf, "offer") == 0) {
464
+ return 1; /* have-local-offer */
465
+ }
466
+
467
+ return 0;
468
+ }
469
+
470
+ int webrtc_peer_connection_get_ice_gathering_state(webrtc_peer_connection_t pc) {
471
+ /* libdatachannel doesn't expose this directly in C API */
472
+ /* Return 0 (new) as default */
473
+ return pc ? 0 : -1;
474
+ }
475
+
476
+ int webrtc_peer_connection_get_ice_connection_state(webrtc_peer_connection_t pc) {
477
+ /* libdatachannel doesn't expose this separately */
478
+ return pc ? 0 : -1;
479
+ }
480
+
481
+ int webrtc_peer_connection_get_connection_state(webrtc_peer_connection_t pc) {
482
+ /* libdatachannel doesn't expose state getter in C API */
483
+ /* Return 0 (new) as default */
484
+ return pc ? 0 : -1;
485
+ }
486
+
487
+ webrtc_data_channel_t webrtc_peer_connection_create_data_channel(
488
+ webrtc_peer_connection_t pc,
489
+ const char* label,
490
+ bool ordered,
491
+ int max_retransmits,
492
+ int max_packet_life_time,
493
+ const char* protocol,
494
+ bool negotiated,
495
+ int id,
496
+ webrtc_error_t* error) {
497
+
498
+ if (!pc) {
499
+ if (error) {
500
+ error->code = 1;
501
+ error->message = "Invalid peer connection";
502
+ }
503
+ return NULL;
504
+ }
505
+
506
+ struct webrtc_data_channel* dc = calloc(1, sizeof(struct webrtc_data_channel));
507
+ if (!dc) {
508
+ if (error) {
509
+ error->code = 1;
510
+ error->message = "Failed to allocate memory";
511
+ }
512
+ return NULL;
513
+ }
514
+
515
+ /* Prepare data channel init */
516
+ rtcDataChannelInit init;
517
+ memset(&init, 0, sizeof(init));
518
+ init.reliability.unordered = !ordered;
519
+ init.reliability.unreliable = (max_retransmits >= 0 || max_packet_life_time >= 0);
520
+
521
+ if (max_retransmits >= 0) {
522
+ init.reliability.maxRetransmits = (unsigned int)max_retransmits;
523
+ }
524
+ if (max_packet_life_time >= 0) {
525
+ init.reliability.maxPacketLifeTime = (unsigned int)max_packet_life_time;
526
+ }
527
+
528
+ init.protocol = protocol;
529
+ init.negotiated = negotiated;
530
+
531
+ if (id >= 0) {
532
+ init.manualStream = true;
533
+ init.stream = (uint16_t)id;
534
+ }
535
+
536
+ /* Create data channel */
537
+ int dc_id = rtcCreateDataChannelEx(pc->pc_id, label ? label : "", &init);
538
+ if (dc_id < 0) {
539
+ free(dc);
540
+ if (error) {
541
+ error->code = 1;
542
+ error->message = "Failed to create data channel";
543
+ }
544
+ return NULL;
545
+ }
546
+
547
+ dc->dc_id = dc_id;
548
+ dc->pc = pc;
549
+ dc->label = label ? strdup(label) : NULL;
550
+ dc->protocol = protocol ? strdup(protocol) : NULL;
551
+ dc->ordered = ordered;
552
+ dc->max_retransmits = max_retransmits;
553
+ dc->max_packet_life_time = max_packet_life_time;
554
+ dc->negotiated = negotiated;
555
+ dc->stream_id = rtcGetDataChannelStream(dc_id);
556
+
557
+ /* Set user pointer and callbacks */
558
+ rtcSetUserPointer(dc_id, dc);
559
+ rtcSetOpenCallback(dc_id, on_dc_open);
560
+ rtcSetMessageCallback(dc_id, on_dc_message);
561
+ rtcSetClosedCallback(dc_id, on_dc_closed);
562
+
563
+ if (error) {
564
+ error->code = 0;
565
+ error->message = NULL;
566
+ }
567
+
568
+ return dc;
569
+ }
570
+
571
+ void webrtc_data_channel_destroy(webrtc_data_channel_t dc) {
572
+ if (dc) {
573
+ rtcDeleteDataChannel(dc->dc_id);
574
+ free(dc->label);
575
+ free(dc->protocol);
576
+ free(dc);
577
+ }
578
+ }
579
+
580
+ int webrtc_data_channel_send(webrtc_data_channel_t dc,
581
+ const uint8_t* data,
582
+ size_t length,
583
+ bool is_binary,
584
+ webrtc_error_t* error) {
585
+ if (!dc) {
586
+ if (error) {
587
+ error->code = 1;
588
+ error->message = "Invalid data channel";
589
+ }
590
+ return -1;
591
+ }
592
+
593
+ int ret;
594
+ if (is_binary) {
595
+ ret = rtcSendMessage(dc->dc_id, (const char*)data, (int)length);
596
+ } else {
597
+ /* For text, size should be -1 to indicate null-terminated string */
598
+ ret = rtcSendMessage(dc->dc_id, (const char*)data, -1);
599
+ }
600
+
601
+ if (ret < 0) {
602
+ if (error) {
603
+ error->code = 1;
604
+ error->message = "Failed to send message";
605
+ }
606
+ return -1;
607
+ }
608
+
609
+ if (error) {
610
+ error->code = 0;
611
+ error->message = NULL;
612
+ }
613
+ return 0;
614
+ }
615
+
616
+ void webrtc_data_channel_close(webrtc_data_channel_t dc) {
617
+ if (dc) {
618
+ rtcClose(dc->dc_id);
619
+ }
620
+ }
621
+
622
+ int webrtc_data_channel_get_ready_state(webrtc_data_channel_t dc) {
623
+ if (!dc) return -1;
624
+
625
+ if (rtcIsClosed(dc->dc_id)) {
626
+ return 3; /* closed */
627
+ }
628
+ if (rtcIsOpen(dc->dc_id)) {
629
+ return 1; /* open */
630
+ }
631
+ return 0; /* connecting */
632
+ }
633
+
634
+ const char* webrtc_data_channel_get_label(webrtc_data_channel_t dc) {
635
+ return dc ? dc->label : NULL;
636
+ }
637
+
638
+ size_t webrtc_data_channel_get_buffered_amount(webrtc_data_channel_t dc) {
639
+ if (!dc) return 0;
640
+ int amount = rtcGetBufferedAmount(dc->dc_id);
641
+ return amount >= 0 ? (size_t)amount : 0;
642
+ }
643
+
644
+ void webrtc_data_channel_on_open(webrtc_data_channel_t dc,
645
+ webrtc_void_callback callback,
646
+ void* user_data) {
647
+ if (dc) {
648
+ dc->open_cb = callback;
649
+ dc->open_user_data = user_data;
650
+ }
651
+ }
652
+
653
+ void webrtc_data_channel_on_message(webrtc_data_channel_t dc,
654
+ webrtc_message_callback callback,
655
+ void* user_data) {
656
+ if (dc) {
657
+ dc->message_cb = callback;
658
+ dc->message_user_data = user_data;
659
+ }
660
+ }
661
+
662
+ void webrtc_data_channel_on_close(webrtc_data_channel_t dc,
663
+ webrtc_void_callback callback,
664
+ void* user_data) {
665
+ if (dc) {
666
+ dc->close_cb = callback;
667
+ dc->close_user_data = user_data;
668
+ }
669
+ }
670
+
671
+ webrtc_session_description_t webrtc_session_description_create(
672
+ const char* type,
673
+ const char* sdp,
674
+ webrtc_error_t* error) {
675
+
676
+ struct webrtc_session_description* sd = calloc(1, sizeof(struct webrtc_session_description));
677
+ if (!sd) {
678
+ if (error) {
679
+ error->code = 1;
680
+ error->message = "Failed to allocate memory";
681
+ }
682
+ return NULL;
683
+ }
684
+
685
+ sd->type = type ? strdup(type) : NULL;
686
+ sd->sdp = sdp ? strdup(sdp) : NULL;
687
+
688
+ if (error) {
689
+ error->code = 0;
690
+ error->message = NULL;
691
+ }
692
+
693
+ return sd;
694
+ }
695
+
696
+ void webrtc_session_description_destroy(webrtc_session_description_t sd) {
697
+ if (sd) {
698
+ free(sd->type);
699
+ free(sd->sdp);
700
+ free(sd);
701
+ }
702
+ }
703
+
704
+ const char* webrtc_session_description_get_type(webrtc_session_description_t sd) {
705
+ return sd ? sd->type : NULL;
706
+ }
707
+
708
+ const char* webrtc_session_description_get_sdp(webrtc_session_description_t sd) {
709
+ return sd ? sd->sdp : NULL;
710
+ }
711
+
712
+ webrtc_ice_candidate_t webrtc_ice_candidate_create(
713
+ const char* candidate,
714
+ const char* sdp_mid,
715
+ int sdp_mline_index,
716
+ webrtc_error_t* error) {
717
+
718
+ struct webrtc_ice_candidate* ic = calloc(1, sizeof(struct webrtc_ice_candidate));
719
+ if (!ic) {
720
+ if (error) {
721
+ error->code = 1;
722
+ error->message = "Failed to allocate memory";
723
+ }
724
+ return NULL;
725
+ }
726
+
727
+ ic->candidate = candidate ? strdup(candidate) : NULL;
728
+ ic->sdp_mid = sdp_mid ? strdup(sdp_mid) : NULL;
729
+ ic->sdp_mline_index = sdp_mline_index;
730
+
731
+ if (error) {
732
+ error->code = 0;
733
+ error->message = NULL;
734
+ }
735
+
736
+ return ic;
737
+ }
738
+
739
+ void webrtc_ice_candidate_destroy(webrtc_ice_candidate_t ic) {
740
+ if (ic) {
741
+ free(ic->candidate);
742
+ free(ic->sdp_mid);
743
+ free(ic);
744
+ }
745
+ }
746
+
747
+ const char* webrtc_ice_candidate_get_candidate(webrtc_ice_candidate_t ic) {
748
+ return ic ? ic->candidate : NULL;
749
+ }
750
+
751
+ const char* webrtc_ice_candidate_get_sdp_mid(webrtc_ice_candidate_t ic) {
752
+ return ic ? ic->sdp_mid : NULL;
753
+ }
754
+
755
+ int webrtc_ice_candidate_get_sdp_mline_index(webrtc_ice_candidate_t ic) {
756
+ return ic ? ic->sdp_mline_index : -1;
757
+ }