summaryrefslogtreecommitdiff
path: root/example/main.c
blob: 33ef4bb6e969295bf6614ad96b652f25319b5525 (plain)
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;
}