summaryrefslogtreecommitdiff
path: root/example/main.c
diff options
context:
space:
mode:
authorChris Hiszpanski <chris.hiszpanski@verkada.com>2022-05-21 01:04:33 -0700
committerChris Hiszpanski <chris.hiszpanski@verkada.com>2022-05-21 02:01:54 -0700
commit12df87e791ee6e468df31a6c468595b8c124a2b4 (patch)
treeb3eea9989ed5412383ed9912bdaac3fb1412b570 /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.c112
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;
+}