diff options
author | Chris Hiszpanski <chris.hiszpanski@verkada.com> | 2022-05-21 01:04:33 -0700 |
---|---|---|
committer | Chris Hiszpanski <chris.hiszpanski@verkada.com> | 2022-05-21 02:01:54 -0700 |
commit | 12df87e791ee6e468df31a6c468595b8c124a2b4 (patch) | |
tree | b3eea9989ed5412383ed9912bdaac3fb1412b570 /example/main.c |
Bare bones demo
* ICE candidate parser
* Simple demo.html
* Outline of tinyrtc.h
Diffstat (limited to 'example/main.c')
-rw-r--r-- | example/main.c | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/example/main.c b/example/main.c new file mode 100644 index 0000000..6223898 --- /dev/null +++ b/example/main.c @@ -0,0 +1,112 @@ +// 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 ice_candidate_t parse_ice_candidate(const char *s) +{ + struct 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 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 configuration_t cfg = { + .ice_servers = { + { + .urls = { + "stun:stun.liburtc.org", + }, + } + } + }; + struct peerconn_t* pc = rtc_peer_connection(cfg); + + printf("alert! starting\n"); + + while (true) { + int n; + char *m = NULL; + size_t len = 0; + + + if (n = getline(&m, &len, stdin), -1 == n) { + return -1; + } + + struct ice_candidate_t c = parse_ice_candidate(m); + + on_ice_candidate(c, NULL); + + add_ice_candidate(pc, c); + } + + return 0; +} |