From 97261b79b20e4e53ef3a5b7a37d1ffb0b5665bcb Mon Sep 17 00:00:00 2001 From: Chris Hiszpanski Date: Sat, 21 May 2022 15:25:35 -0700 Subject: * Added trtc_ prefixes * Split into tinyrtc.c/h * Sending answer * Answer validates --- .gitignore | 2 + Makefile | 13 ++++-- README.md | 5 +-- example/index.js | 7 ++- example/main.c | 32 +++++++------ example/tinyrtc.png | Bin 0 -> 4707 bytes tinyrtc.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tinyrtc.h | 74 ++++++++++++++++++------------ 8 files changed, 208 insertions(+), 51 deletions(-) create mode 100644 example/tinyrtc.png create mode 100644 tinyrtc.c diff --git a/.gitignore b/.gitignore index e383139..258d911 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ example/webrtcd +tinyrtc.o +*.swp diff --git a/Makefile b/Makefile index 4d82061..be9ee9f 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,13 @@ -example/webrtcd: example/main.c tinyrtc.h - $(CC) $(CFLAGS) -I. -o $@ example/main.c +example/webrtcd: example/main.c tinyrtc.o + $(CC) $(CFLAGS) -I. -o $@ example/main.c tinyrtc.o + +tinyrtc.o: tinyrtc.c tinyrtc.h + $(CC) $(CFLAGS) -I. -o $@ -c tinyrtc.c .PHONY: clean clean: - rm -f example/webrtcd + rm -f tinyrtc.o example/webrtcd + +.PHONY: run +run: + websocketd --port=8080 --staticdir=example ./example/webrtcd diff --git a/README.md b/README.md index 095d825..c30f1a0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ -TinyRTC -======= +# ![tinyrtc logo](example/tinyrtc.png) TinyRTC -A header-only implementation of WebRTC for small systems like IP cameras +WebRTC implementation in C for tiny things like IP cameras Build ----- diff --git a/example/index.js b/example/index.js index 35fdbba..a161a10 100644 --- a/example/index.js +++ b/example/index.js @@ -26,10 +26,13 @@ window.onload = function demo() { let d = JSON.parse(event.data); if ('candidate' in d) { if (d.candidate) { - console.log("remote candidate:\n%c%s", "color: blue;", event.data); + console.log("remote candidate:\n%c%s", "color: blue;", d.candidate); } } else if ('sdp' in d) { - console.log("remote answer:\n%c%s", "color: blue;", event.data); + pc.setRemoteDescription(d).catch((e) => { + console.log("local error:\n%c%s", "color: red;", e); + }); + console.log("remote answer:\n%c%s", "color: blue;", d.sdp); } } catch(e) { console.log("remote error:\n%c%s", "color: red;", event.data); diff --git a/example/main.c b/example/main.c index 6223898..33ef4bb 100644 --- a/example/main.c +++ b/example/main.c @@ -17,9 +17,9 @@ static bool matches(const char *json, jsmntok_t *tok, const char *s) { } // Parses ICE candidate from stringified JSON -struct ice_candidate_t parse_ice_candidate(const char *s) +struct trtc_ice_candidate_t parse_ice_candidate(const char *s) { - struct ice_candidate_t c = { 0 }; + struct trtc_ice_candidate_t c = { 0 }; int i, r; jsmn_parser p; @@ -60,7 +60,7 @@ struct ice_candidate_t parse_ice_candidate(const char *s) } // (callback) Called for each discovered local ICE candidate -int on_ice_candidate(const struct ice_candidate_t c, void *arg) { +int on_ice_candidate(const struct trtc_ice_candidate_t c, void *arg) { // send ice candidate over signaling channel printf("{" "\"candidate\":\"%s\"," @@ -78,7 +78,7 @@ int main(int argc, char **argv) setvbuf(stdout, NULL, _IONBF, 0); // create peer connection - struct configuration_t cfg = { + struct trtc_config_t cfg = { .ice_servers = { { .urls = { @@ -87,25 +87,29 @@ int main(int argc, char **argv) } } }; - struct peerconn_t* pc = rtc_peer_connection(cfg); - - printf("alert! starting\n"); + struct trtc_peerconn_t* pc = trtc_peer_connection(cfg); while (true) { int n; - char *m = NULL; - size_t len = 0; + char *msg = NULL; + size_t msglen = 0; - if (n = getline(&m, &len, stdin), -1 == n) { + if (n = getline(&msg, &msglen, stdin), -1 == n) { return -1; } - struct ice_candidate_t c = parse_ice_candidate(m); - - on_ice_candidate(c, NULL); + 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"); + } - add_ice_candidate(pc, c); } return 0; diff --git a/example/tinyrtc.png b/example/tinyrtc.png new file mode 100644 index 0000000..127dd06 Binary files /dev/null and b/example/tinyrtc.png differ diff --git a/tinyrtc.c b/tinyrtc.c new file mode 100644 index 0000000..2092224 --- /dev/null +++ b/tinyrtc.c @@ -0,0 +1,126 @@ +/** + * WebRTC implementation for tiny devices + * + * Copyright (c) 2022 Chris Hiszpanski. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include +#include // snprintf +#include // memset + +#include "tinyrtc.h" + + +/// OPAQUE TYPE DEFINITIONS //////////////////////////////////////////////// + +struct trtc_peerconn_t { + bool active; + + char offer[TRTC_MAX_SDP_SIZE]; + char answer[TRTC_MAX_SDP_SIZE]; + + char l_ice_pwd[TRTC_MAX_ICE_PWD_SIZE]; + char l_ice_ufrag[TRTC_MAX_ICE_UFRAG_SIZE]; +}; + + +/// STATIC ALLOCATIONS ///////////////////////////////////////////////////// + +static struct trtc_peerconn_t peer_connection_pool[TRTC_MAX_PEER_CONNECTIONS]; + + +/// RTC PEER CONNECTION API //////////////////////////////////////////////// + +void rtc_init() { + memset(peer_connection_pool, 0, sizeof(peer_connection_pool)); +} + +struct trtc_peerconn_t* trtc_peer_connection(struct trtc_config_t cfg) { + for (int i = 0; i < TRTC_MAX_PEER_CONNECTIONS; i++) { + if (!peer_connection_pool[i].active) { + memset(&peer_connection_pool[i], 0, sizeof(struct trtc_peerconn_t)); + peer_connection_pool[i].active = true; + return &peer_connection_pool[i]; + } + } + + return NULL; +} + +int trtc_add_ice_candidate(struct trtc_peerconn_t *pc, const struct trtc_ice_candidate_t c) { + return -1; +}; + +const char * trtc_create_answer(struct trtc_peerconn_t *pc) { + unsigned char fp[32]; + + snprintf(pc->l_ice_ufrag, TRTC_MAX_ICE_UFRAG_SIZE, "xxxx"); // XXX + snprintf(pc->l_ice_pwd, TRTC_MAX_ICE_PWD_SIZE, "xxxxxxxxxxxxxxxxxxxxxx"); // XXX + + snprintf(pc->answer, TRTC_MAX_SDP_SIZE, + "v=0\\r\\n" + "o=- 2210401696197537454 2 IN IP4 127.0.0.1\\r\\n" // XXX + "s=-\\r\\n" + "t=0 0\\r\\n" + "a=group:BUNDLE 0\\r\\n" + "a=msid-semantic: WMS\\r\\n" + "m=video 9 UDP/TLS/RTP/SAVPF 98\\r\\n" // XXX + "c=IN IP4 0.0.0.0\\r\\n" + "a=rtcp:9 IN IP4 0.0.0.0\\r\\n" + "a=ice-ufrag:%s\\r\\n" + "a=ice-pwd:%s\\r\\n" + "a=ice-options:trickle\\r\\n" + "a=fingerprint:sha-256 " + "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:" + "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:" + "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:" + "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX\\r\\n" + "a=setup:passive\\r\\n" + "a=mid:0\\r\\n" + "a=sendonly\\r\\n" + "a=rtcp-mux\\r\\n" + "a=rtcp-rsize\\r\\n" + "a=rtpmap:98 H264/90000\\r\\n" + "a=rtcp-fb:98 nack\\r\\n" + "a=rtcp-fb:98 nack pli\\r\\n" + "a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\\r\\n", + pc->l_ice_ufrag, + pc->l_ice_pwd, + fp[ 0], fp[ 1], fp[ 2], fp[ 3], fp[ 4], fp[ 5], fp[ 6], fp[ 7], + fp[ 8], fp[ 9], fp[10], fp[11], fp[12], fp[13], fp[14], fp[15], + fp[16], fp[17], fp[18], fp[19], fp[20], fp[21], fp[22], fp[23], + fp[24], fp[25], fp[26], fp[27], fp[28], fp[29], fp[30], fp[31] + ); + + return pc->answer; +}; + +int trtc_set_local_description(struct trtc_peerconn_t *pc, const char *sdp) { + return -1; +}; + +int trtc_set_remote_description(struct trtc_peerconn_t *pc, const char *sdp) { + return -1; +}; diff --git a/tinyrtc.h b/tinyrtc.h index 1d66b78..205ae1b 100644 --- a/tinyrtc.h +++ b/tinyrtc.h @@ -29,46 +29,62 @@ #ifndef TINYRTC_H #define TINYRTC_H -struct peerconn_t { -}; +#define TRTC_MAX_ICE_CANDIDATE_SIZE 256 +#define TRTC_MAX_ICE_SERVER_CREDENTIAL_SIZE 128 +#define TRTC_MAX_ICE_SERVERS 2 +#define TRTC_MAX_ICE_SERVER_URL_SIZE 128 +#define TRTC_MAX_ICE_SERVER_URLS 3 +#define TRTC_MAX_ICE_SERVER_USERNAME_SIZE 128 +#define TRTC_MAX_ICE_CANDIDATE_USERNAME_FRAGMENT_SIZE 8 +#define TRTC_MAX_ICE_CANDIDATE_SDP_MID_SIZE 32 +#define TRTC_MAX_PEER_CONNECTIONS 5 +#define TRTC_MAX_SDP_SIZE 4096 +#define TRTC_MAX_ICE_UFRAG_SIZE 5 +#define TRTC_MAX_ICE_PWD_SIZE 32 -struct rtc_ice_server_t { - char credential[128]; // XXX only password type supported - char urls[3][128]; - char username[128]; -}; -struct configuration_t { - struct rtc_ice_server_t ice_servers[3]; -}; +/// PRIVATE TYPE DEFINITIONS /////////////////////////////////////////////// -struct ice_candidate_t { - char candidate[256]; - int sdp_mline_index; - char sdp_mid[32]; - char username_fragment[8]; -}; +struct trtc_peerconn_t; -typedef int (on_ice_candidate_t)(const struct ice_candidate_t c, void *arg); -struct peerconn_t* rtc_peer_connection(struct configuration_t cfg) { - return 0; -} +/// PUBLIC TYPE DEFINITIONS //////////////////////////////////////////////// -int add_ice_candidate(struct peerconn_t *pc, const struct ice_candidate_t c) { - return -1; +struct trtc_ice_server_t { + char credential[TRTC_MAX_ICE_SERVER_CREDENTIAL_SIZE]; + char urls[3][TRTC_MAX_ICE_SERVER_URL_SIZE]; + char username[TRTC_MAX_ICE_SERVER_USERNAME_SIZE]; }; -int create_answer(struct peerconn_t *pc) { - return -1; +struct trtc_config_t { + struct trtc_ice_server_t ice_servers[TRTC_MAX_ICE_SERVERS]; }; -int set_local_description(struct peerconn_t *pc, const char *sdp) { - return -1; +struct trtc_ice_candidate_t { + char candidate[TRTC_MAX_ICE_CANDIDATE_SIZE]; + int sdp_mline_index; + char sdp_mid[TRTC_MAX_ICE_CANDIDATE_SDP_MID_SIZE]; + char username_fragment[TRTC_MAX_ICE_CANDIDATE_USERNAME_FRAGMENT_SIZE]; }; -int set_remote_description(struct peerconn_t *pc, const char *sdp) { - return -1; -}; + +/// CALLBACKS ////////////////////////////////////////////////////////////// + +typedef int (trtc_on_ice_candidate_t)(const struct trtc_ice_candidate_t c, void *arg); + + +/// RTC PEER CONNECTION API //////////////////////////////////////////////// + +struct trtc_peerconn_t* trtc_peer_connection(struct trtc_config_t cfg); + +int trtc_add_ice_candidate(struct trtc_peerconn_t *pc, const struct trtc_ice_candidate_t c); + +int trtc_add_track(struct trtc_peerconn_t *pc); + +const char * trtc_create_answer(struct trtc_peerconn_t *pc); + +int trtc_set_local_description(struct trtc_peerconn_t *pc, const char *sdp); + +int trtc_set_remote_description(struct trtc_peerconn_t *pc, const char *sdp); #endif -- cgit v1.2.3