From 2745c093dca7105672fd540f9d060a0ef1a1ce69 Mon Sep 17 00:00:00 2001 From: Chris Hiszpanski Date: Thu, 29 Apr 2021 01:30:50 -0700 Subject: Adds signaling to demo Adds simple HTTP/1.0 signaling to demo. Demo long-polls demo server (demo.liburtc.org) for an offer and posts an answer. Similarily, long-polls and posts candidates. --- examples/.gitignore | 2 +- examples/Makefile.am | 14 +-- examples/callee.c | 62 ------------- examples/demo.c | 250 +++++++++++++++++++++++++++++++++++++++++++++++++++ examples/demo.html | 95 +++++++++++++++++++- src/urtc.c | 10 +-- src/urtc.h | 12 +-- 7 files changed, 360 insertions(+), 85 deletions(-) delete mode 100644 examples/callee.c create mode 100644 examples/demo.c diff --git a/examples/.gitignore b/examples/.gitignore index b04600a..1549b67 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -1 +1 @@ -sandbox +demo diff --git a/examples/Makefile.am b/examples/Makefile.am index f2e28b1..945cc7f 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -1,11 +1,5 @@ # Builds a minimal example program -#bin_PROGRAMS = example -#example_CFLAGS = -I$(top_srcdir)/src/include $(LWS_CFLAGS) -#example_LDADD = $(top_builddir)/src/liburtc.la $(LWS_LIBS) -#example_SOURCES = server.c - -# Builds a minimal example program -bin_PROGRAMS = sandbox -sandbox_CFLAGS = -I$(top_srcdir)/src -sandbox_LDADD = $(top_builddir)/src/liburtc.la -sandbox_SOURCES = sandbox.c +bin_PROGRAMS = demo +demo_CFLAGS = -I$(top_srcdir)/src +demo_LDADD = $(top_builddir)/src/liburtc.la +demo_SOURCES = demo.c diff --git a/examples/callee.c b/examples/callee.c deleted file mode 100644 index 4fb7ddf..0000000 --- a/examples/callee.c +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Simple callee example (web browser is caller). Sends video to caller upon - * incoming calls. - * - * Copyright (c) 2019-2021 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 - -#include "urtc.h" - - -int main() { - sigset_t ss; - int signal; - - const char *stun[] = { - "stun.liburtc.org", - NULL - }; - - sigemptyset(&ss); - sigaddset(&ss, SIGINT); - sigaddset(&ss, SIGTERM); - sigaddset(&ss, SIGQUIT); - - // create a new peer connection (no actual network communication yet) - urtc_peerconn_t *pc = urtc_peerconn_create(NULL); - - // connect to signaling service - - sigwait(&ss, &signal); - - urtc_peerconn_destroy(pc); - - return 0; -} - -/* vim: set expandtab ts=8 sw=4 tw=0 : */ diff --git a/examples/demo.c b/examples/demo.c new file mode 100644 index 0000000..34b85de --- /dev/null +++ b/examples/demo.c @@ -0,0 +1,250 @@ +/** + * Simple callee example (web browser is caller). Sends video to caller upon + * incoming calls. + */ + +#include // errno +#include +#include +#include +#include // printf +#include +#include + +#include +#include +#include + +#include "urtc.h" + +// STUN servers +const char *stun[] = { + "stun.liburtc.org", + NULL +}; + +static int http_connect(const char *hostname) { + int err, fd; + struct addrinfo *res, *res0; + + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_protocol = IPPROTO_TCP + }; + + // resolve hostname to IPv4/IPv6 address + if (err = getaddrinfo(hostname, "http", &hints, &res0), err) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(errno)); + return -1; + } + + for (res = res0; res; res = res->ai_next) { + // attempt to open socket + fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (-1 == fd) { + continue; // failed. onto the next result. + } + + // attempt to connect socket + if (-1 == connect(fd, res->ai_addr, res->ai_addrlen)) { + close(fd); + fd = -1; + continue; // failed. onto the next result. + } + + break; // success. return. + } + + // free resolved addresses + freeaddrinfo(res0); + + return fd; +} + +static int http_post( + const char *hostname, + const char *path, + const char *body +) { + const char *tmpl = + "POST %s HTTP/1.0\r\n" + "Host: %s\r\n" + "Content-Length: %d\r\n" + "Content-Type: text/plain\r\n" + "\r\n"; + + int fd, rv = -1; + char req[128]; + + if (fd = http_connect(hostname), -1 == fd) { + return -1; + } + + // construct request + if (-1 == snprintf(req, sizeof(req), tmpl, path, hostname, strlen(body))) { + fprintf(stderr, "bad request template\n"); + goto _unwind_socket; + } + + // send request + if (rv = send(fd, req, strlen(req), 0), -1 == rv) { + perror("send"); + goto _unwind_socket; + } + + // send request body + if (rv = send(fd, body, strlen(body), 0), -1 == rv) { + perror("send"); + goto _unwind_socket; + } + +_unwind_socket: + shutdown(fd, SHUT_RDWR); + close(fd); + +_done: + return rv; +} + +/** + * HTTP GET request + * + * Resolves simple signaling server hostname to IPv4/IPv6 address and connects + * to its HTTP port. + * + * \param[out] response Buffer into which response is written. + * \param[in] size Max size of response. + * \param hostname Server hostname (e.g. "demo.liburtc.org") + * \param path HTTP path (e.g. "/offer") + * + * \return Socket file descriptor, or -1 on error. Caller must close file + * descriptor. + */ +static int get( + char *response, + size_t size, + const char *hostname, + const char *path +) { + const char *tmpl = + "GET %s HTTP/1.0\r\n" + "Host: %s\r\n" + "\r\n"; + + int err, fd, rv = -1; + char req[128]; + + if (fd = http_connect(hostname), -1 == fd) { + return -1; + } + + // construct request + if (-1 == snprintf(req, sizeof(req), tmpl, path, hostname)) { + fprintf(stderr, "bad request template\n"); + goto _unwind_socket; + } + + // send request + if (rv = send(fd, req, strlen(req), 0), -1 == rv) { + perror("send"); + goto _unwind_socket; + } + + // get response + if (rv = recv(fd, response, size, 0), -1 == rv) { + perror("recv"); + goto _unwind_socket; + } + +_unwind_socket: + shutdown(fd, SHUT_RDWR); + close(fd); + +_done: + return rv; +} + + +/** + * (callback) Handles incoming ICE candidates. + * + * \param cand[in] ICE candidate. + * \param arg[in] User argument. + */ +void handle_ice_candidate(const char *cand, void *arg) { + urtc_peerconn_t *pc = (urtc_peerconn_t *)arg; + + urtc_add_ice_candidate(pc, cand); +} + +static int longpoll(char *response, size_t size, const char *path) { + int n; + + while (true) { + n = get(response, size, "demo.liburtc.org", path); + if (-1 == n || 0 == n) { + perror("get"); + continue; + } + + // blindly strip http headers + char *body = strstr(response, "\r\n\r\n"); + strcpy(response, body + 4); + + // ignore empty responses (i.e. long-poll timeout) + if (0 == strlen(response)) { + continue; + } + + break; + } + + return n; +} + +char offer[4096]; +char answer[4096]; + +int main(int argc, char **argv) { + int n; + + // create a new peer connection (no actual network communication yet) + urtc_peerconn_t *pc = urtc_peerconn_create(stun); + + // TODO add tracks here + + // get offer + n = longpoll(offer, sizeof(offer), "/offer"); + fprintf(stderr, "%s\n", offer); + + if (urtc_set_remote_description(pc, offer)) { + fprintf(stderr, "error: set remote description\n"); + } + if (urtc_create_answer(pc, answer, sizeof(answer))) { + fprintf(stderr, "error: create answer\n"); + } + + http_post("demo.liburtc.org", "/answer", answer); + fprintf(stderr, "answer:\n%s\n", answer); + //urtc_set_local_description(pc, answer); + + + // block until signal + { + int signal; + sigset_t ss; + sigemptyset(&ss); + sigaddset(&ss, SIGINT); + sigaddset(&ss, SIGTERM); + sigaddset(&ss, SIGQUIT); + sigwait(&ss, &signal); + } + + // clean up + urtc_peerconn_destroy(pc); + + return 0; +} + +/* vim: set expandtab ts=4 sw=4 tw=0 : */ diff --git a/examples/demo.html b/examples/demo.html index 03e07b9..0be9df8 100644 --- a/examples/demo.html +++ b/examples/demo.html @@ -4,12 +4,105 @@ liburtc demo +

liburtc demo

+
+

+ In this demo, this web browser is the caller and liburtc is the callee. +

+ +

+ This browser creates an offer and relays it to liburtc via a simple + signaling server (HTTP/1.0 long-polling). liburtc replies with an + answer, again via the signaling service. +

+ +
+ + diff --git a/src/urtc.c b/src/urtc.c index 8cc6ff5..f3ffa99 100644 --- a/src/urtc.c +++ b/src/urtc.c @@ -425,20 +425,20 @@ int urtc_add_ice_candidate(struct peerconn *pc, const char *cand) { return -URTC_ERR_NOT_IMPLEMENTED; } -int urtc_create_answer(struct peerconn *pc, char **answer) { - return -URTC_ERR_NOT_IMPLEMENTED; +int urtc_create_answer(struct peerconn *pc, char *answer, size_t size) { + return sdp_serialize(answer, size, &pc->ldesc); } -int urtc_create_offer(struct peerconn *pc, char **offer) { +int urtc_create_offer(struct peerconn *pc, char *offer, size_t size) { return -URTC_ERR_NOT_IMPLEMENTED; } int urtc_set_remote_description(struct peerconn *pc, const char *desc) { - return -URTC_ERR_NOT_IMPLEMENTED; + return sdp_parse(&pc->rdesc, desc); } int urtc_set_local_description(struct peerconn *pc, const char *desc) { - return -URTC_ERR_NOT_IMPLEMENTED; + return sdp_parse(&pc->ldesc, desc); } void urtc_peerconn_destroy(struct peerconn *pc) { diff --git a/src/urtc.h b/src/urtc.h index 7760ca8..eae4e6b 100644 --- a/src/urtc.h +++ b/src/urtc.h @@ -151,12 +151,12 @@ int urtc_add_ice_candidate(urtc_peerconn_t *pc, const char *cand); * Akin to `createAnswer` method of `RTCPeerConnection` in WebRTC JS API. * * \param pc Peer connection - * \param answer Pointer to string pointer pointing to generated answer. - * Memory internally managed (caller need not and must not free). + * \param answer Destination pointer for generated answer. + * \param size Bytes available at destination. * * \return 0 on success, negative on error. */ -int urtc_create_answer(urtc_peerconn_t *pc, char **answer); +int urtc_create_answer(urtc_peerconn_t *pc, char *answer, size_t size); /** * Creates a local description for an offering peer connection @@ -170,12 +170,12 @@ int urtc_create_answer(urtc_peerconn_t *pc, char **answer); * Akin to `createOffer` method of `RTCPeerConnection` in WebRTC JS API. * * \param pc Peer connection - * \param offer Pointer to string pointer pointing to generated offer. - * Memory interally managed (caller need no and must not free). + * \param offer Destination pointer for generated offer. + * \param size Bytes available at destination. * * \return 0 on success, negative on error. */ -int urtc_create_offer(urtc_peerconn_t *pc, char **offer); +int urtc_create_offer(urtc_peerconn_t *pc, char *offer, size_t size); /** * Sets local description for peer connection -- cgit v1.2.3