1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
// Simple example of a mock IP camera. Answers WebRTC offer and streams H.264.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "jsmn.h"
#include "tinyrtc.h"
// Helper function for parsing JSON
static bool matches(const char *json, jsmntok_t *tok, const char *s) {
if (JSMN_STRING != tok->type) return false;
if (strlen(s) != tok->end - tok->start) return false;
return 0 == strncmp(json + tok->start, s, tok->end - tok->start);
}
// Parses ICE candidate from stringified JSON
struct trtc_ice_candidate_t parse_ice_candidate(const char *s)
{
struct trtc_ice_candidate_t c = { 0 };
int i, r;
jsmn_parser p;
jsmntok_t t[16];
jsmn_init(&p);
r = jsmn_parse(&p, s, strlen(s), t, sizeof(t) / sizeof(t[0]));
if (r < 0) {
printf("%s:%d: failed to parse json", __func__, __LINE__);
return c;
}
if (r < 1 || t[0].type != JSMN_OBJECT) {
printf("%s:%d: object expected", __func__, __LINE__);
return c;
}
for (i = 1; i < r; i++) {
if (matches(s, &t[i], "candidate")) {
strncpy(c.candidate, s + t[i + 1].start,
t[i + 1].end - t[i + 1].start);
i++;
} else if (matches(s, &t[i], "sdpMid")) {
strncpy(c.sdp_mid, s + t[i + 1].start,
t[i + 1].end - t[i + 1].start);
i++;
} else if (matches(s, &t[i], "sdpMLineIndex")) {
c.sdp_mline_index = atoi(s + t[i + 1].start);
i++;
} else if (matches(s, &t[i], "usernameFragment")) {
strncpy(c.username_fragment, s + t[i + 1].start,
t[i + 1].end - t[i + 1].start);
i++;
}
}
return c;
}
// (callback) Called for each discovered local ICE candidate
int on_ice_candidate(const struct trtc_ice_candidate_t c, void *arg) {
// send ice candidate over signaling channel
printf("{"
"\"candidate\":\"%s\","
"\"sdpMLineIndex\":%d,"
"\"sdpMid\":\"%s\","
"\"usernameFragment\":null"
"}\n", c.candidate, c.sdp_mline_index, c.sdp_mid);
return 0;
}
int main(int argc, char **argv)
{
// disable stdout buffering to ensure signaling messages send immediately
setvbuf(stdout, NULL, _IONBF, 0);
// create peer connection
struct trtc_config_t cfg = {
.ice_servers = {
{
.urls = {
"stun:stun.liburtc.org",
},
}
}
};
struct trtc_peerconn_t* pc = trtc_peer_connection(cfg);
while (true) {
int n;
char *msg = NULL;
size_t msglen = 0;
if (n = getline(&msg, &msglen, stdin), -1 == n) {
return -1;
}
if (strstr(msg, "\"sdp\"")) {
// got offer. send answer.
printf("{\"sdp\":\"%s\",\"type\":\"answer\"}\n", trtc_create_answer(pc));
} else if (strstr(msg, "\"candidate\"")) {
struct trtc_ice_candidate_t c = parse_ice_candidate(msg);
on_ice_candidate(c, NULL);
trtc_add_ice_candidate(pc, c);
} else {
printf("unrecognized signaling message\n");
}
}
return 0;
}
|