diff options
author | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-03-21 02:41:40 -0400 |
---|---|---|
committer | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-03-21 02:41:40 -0400 |
commit | 51495e7579a1fcbdbfda868109c2128c2e933f9f (patch) | |
tree | a1a57ab349240947d9a7645d08de1294ec6114d3 | |
parent | adbfb27a90dacca1cdea40521d8614f61fe3f7cb (diff) |
stuff
-rw-r--r-- | freenect-server-http.service | 14 | ||||
-rw-r--r-- | freenect-server.c | 53 | ||||
-rw-r--r-- | freenect-server.service | 5 | ||||
-rw-r--r-- | freenect-server@.socket | 4 | ||||
-rw-r--r-- | multipart-replace-http-server.c | 185 | ||||
-rw-r--r-- | multipart-replace.c | 2 | ||||
-rw-r--r-- | util.c | 55 | ||||
-rw-r--r-- | util.h | 5 |
8 files changed, 205 insertions, 118 deletions
diff --git a/freenect-server-http.service b/freenect-server-http.service new file mode 100644 index 0000000..f8ae608 --- /dev/null +++ b/freenect-server-http.service @@ -0,0 +1,14 @@ +[Unit] +Description=Kinect HTTP media streamer +After=network.target +Requires=freenect-server-http.socket +After=freenect-server@accel.mjson.socket freenect-server@depth.mjpg.socket freenect-server@video.mjpg.socket + +[Service] +Type=simple +User=alarm +ExecStart=/home/alarm/freenect-server/multipart-replace-http-server fd systemd /run/freenect-server/video.mjpg /run/freenect-server/depth.mjpg /run/freenect-server/accel.mjson + +[Install] +WantedBy=multi-user.target +Also=freenect-server.socket diff --git a/freenect-server.c b/freenect-server.c index 37e2a08..5089bdd 100644 --- a/freenect-server.c +++ b/freenect-server.c @@ -125,7 +125,7 @@ void handle_accel(freenect_device *dev UNUSED, freenect_raw_tilt_state* data) { uint8_t depth_rgb[640*480*3]; void handle_depth(freenect_device *dev, void *depth, uint32_t timestamp UNUSED) { uint32_t size = freenect_get_current_depth_mode(dev).bytes; - if (size != 460*480*2) + if (size != 640*480*2) error(EXIT_FAILURE, 0, "handle_depth: expected 640*480*2 byte frame, but got %"PRId32, size); /* scale the 11-bit values into 8-bit values */ uint16_t *depth_grey = depth; @@ -137,7 +137,7 @@ void handle_depth(freenect_device *dev, void *depth, uint32_t timestamp UNUSED) void handle_video(freenect_device *dev, void *rgb, uint32_t timestamp UNUSED) { uint32_t size = freenect_get_current_video_mode(dev).bytes; - if (size != 460*480*3) + if (size != 640*480*3) error(EXIT_FAILURE, 0, "handle_video: expected 640*480*3 byte frame, but got %"PRId32, size); /* write the image */ write_imageframe(video_stream, rgb); @@ -163,10 +163,28 @@ void cleanup() { fclose(depth_stream); if (accel_stream) fclose(accel_stream); + sleep(5); /* work around systemd bug dropping log messages */ +} + +FILE *xfopen(const char *path, const char *mode) { + FILE *stream = fopen(path, mode); + if (stream == NULL) + error(EXIT_FAILURE, errno, "fopen: %s", path); + return stream; +} + +FILE *xfdopen(const char *path, const char *mode) { + int fd = get_fd(path); + if (fd < 0) + error(EXIT_FAILURE, -fd, "get_fd: %s", path); + FILE *stream = fdopen(fd, mode); + if (stream == NULL) + error(EXIT_FAILURE, errno, "fdopen: %s (%d)", path, fd); + return stream; } void usage() { - printf("Usage: %s [-h] [-v video.rgb24] [-d depth.rgb24] [-a accel.txt]\n", program_invocation_name); + printf("Usage: %s [-h] [-v video.mjpg|-V video_fd] [-d depth.mjpg|-D depth_fd] [-a accel.mjson|-A accel_fd]\n", program_invocation_name); } int main(int argc, char *argv[]) { @@ -183,21 +201,27 @@ int main(int argc, char *argv[]) { return EXIT_SUCCESS; case 'v': i++; - video_stream = fopen(argv[i], "w"); - if (video_stream == NULL) - error(EXIT_FAILURE, errno, "fopen: %s", argv[i]); + video_stream = xfopen(argv[i], "w"); break; case 'd': i++; - depth_stream = fopen(argv[i], "w"); - if (video_stream == NULL) - error(EXIT_FAILURE, errno, "fopen: %s", argv[i]); + depth_stream = xfopen(argv[i], "w"); break; case 'a': i++; - accel_stream = fopen(argv[i], "w"); - if (video_stream == NULL) - error(EXIT_FAILURE, errno, "fopen: %s", argv[i]); + accel_stream = xfopen(argv[i], "w"); + break; + case 'V': + i++; + video_stream = xfdopen(argv[i], "w"); + break; + case 'D': + i++; + depth_stream = xfdopen(argv[i], "w"); + break; + case 'A': + i++; + accel_stream = xfdopen(argv[i], "w"); break; default: dup2(2, 1); @@ -210,6 +234,11 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } } + if (!(video_stream || depth_stream || accel_stream)) { + dup2(2, 1); + usage(); + return EXIT_FAILURE; + } res = freenect_init(&ctx, 0); if (res) { diff --git a/freenect-server.service b/freenect-server.service index fbc2035..75177ff 100644 --- a/freenect-server.service +++ b/freenect-server.service @@ -1,13 +1,12 @@ [Unit] Description=Kinect media streamer backend After=network.target -Requires=freenect-server@accel.socket freenect-server@depth.socket freenect-server@video.socket +Requires=freenect-server@accel.mjson.socket freenect-server@depth.mjpg.socket freenect-server@video.mjpg.socket [Service] Type=simple User=alarm -ExecStart=/home/alarm/freenect-server/freenect-server fd systemd -Restart=always +ExecStart=/home/alarm/freenect-server/freenect-server -V systemd:freenect-server@video.mjpg.socket -D systemd:freenect-server@depth.mjpg.socket -A systemd:freenect-server@accel.mjson.socket [Install] WantedBy=multi-user.target diff --git a/freenect-server@.socket b/freenect-server@.socket index 841f2fd..13bcc4c 100644 --- a/freenect-server@.socket +++ b/freenect-server@.socket @@ -5,8 +5,8 @@ After=network.target [Socket] SocketUser=alarm SocketGroup=alarm -ListenFIFO=/run/freenect-server/Ii.fifo -FileDescriptorName=%I +ListenFIFO=/run/freenect-server/%I +#FileDescriptorName=%I Service=freenect-server.service [Install] diff --git a/multipart-replace-http-server.c b/multipart-replace-http-server.c index 195ad29..785bfe3 100644 --- a/multipart-replace-http-server.c +++ b/multipart-replace-http-server.c @@ -1,6 +1,5 @@ /* Copyright 2016 Luke Shumaker */ -#include <ctype.h> /* for isdigit */ #include <errno.h> #include <error.h> #include <fcntl.h> /* for open */ @@ -31,6 +30,7 @@ struct reader_thread_args { }; void *reader_thread(void *args_anon) { + pthread_setname_np(pthread_self(), "reader"); struct reader_thread_args *args = args_anon; multipart_replace_reader(args->stream, args->fd, args->boundary); error(EXIT_FAILURE, 0, "multipart_replace stream ended"); @@ -74,29 +74,95 @@ struct multipart_replace_stream *file_get(const char *filename) { return NULL; } +void connection_handler(int fd) { + FILE *netstream = fdopen(fd, "r"); + char *line_buf = NULL; + size_t line_cap = 0; + ssize_t line_len = 0; + + line_len = getline(&line_buf, &line_cap, netstream); + /* expect line to be "GET /path.mjpg HTTP/1.1" */ + if (strncmp(line_buf, "GET ", 4) != 0) { + dprintf(fd, + "HTTP/1.1 405 Method Not Allowed\r\n" + "Allow: GET\r\n" + "\r\n"); + close(fd); + return; + } + char *version = &line_buf[4]; + char *path = strsep(&version, " "); + if (strcmp(version, "HTTP/1.1\r\n") != 0 && strcmp(version, "HTTP/1.0\r\n")) { + close(fd); + return; + } + path = strdupa(path); + + /* run through the rest of the headers */ + while (strcmp(line_buf, "\r\n") != 0 && line_len >= 0) + line_len = getline(&line_buf, &line_cap, netstream); + + if (strcmp(path, "/") == 0) { + error(0, 0, "200 %s", path); + dprintf(fd, + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=utf-8\r\n" + "\r\n" + "<!DOCTYPE html>\n" + "<html>\n" + "<head>\n" + " <title>multipart/x-mixed-replace HTTP server</title>\n" + "</head>\n" + "<body>\n" + " <ul>\n" + ); + for (size_t i = 0; i < filec; i++) + dprintf(fd, " <li><a href=\"%s\">%s</a></li>\n", filev[i].name, filev[i].name); + dprintf(fd, + " </ul>\n" + "</body>\n" + "</html>\n"); + close(fd); + return; + } + struct multipart_replace_stream *vidstream = file_get(path); + if (vidstream == NULL) { + error(0, 0, "404 %s", path); + dprintf(fd, + "HTTP/1.1 404 Not Found\r\n" + "\r\n"); + return; + } + + const char *boundary = "boundary" /* FIXME */; + error(0, 0, "200 %s", path); + dprintf(fd, + "HTTP/1.1 200 OK\r\n" + "Content-Type: multipart/x-mixed-replace;boundary=%s\r\n" + "\r\n", + boundary); + multipart_replace_writer(vidstream, fd, boundary); + close(fd); +} + +void *connection_thread(void *arg_anon) { + pthread_setname_np(pthread_self(), "connection"); + int fd = (int)(intptr_t)arg_anon; + error(0, 0, "Connection %d opened", fd); + connection_handler(fd); + error(0, 0, "Connection %d closed", fd); + return NULL; +} + + /* same error codes values as -getaddrinfo(); */ int sockstream_listen(const char *type, const char *addr) { int sock; if (strcmp(type, "fd") == 0) { - if (strcmp(addr, "stdin") == 0) { - sock = 0; - } else if (strcmp(addr, "stdout") == 0) { - sock = 1; - } else if (strcmp(addr, "stderr") == 0) { - sock = 2; - } else if (strcmp(addr, "systemd") == 0) { - sock = 3; /* <systemd/sd-daemon.h>:SD_LISTEN_FDS_START */ - } else { - for (size_t i = 0; addr[i] != '\0'; i++) - if (!isdigit(addr[i])) { - errno = EINVAL; - return -EAI_SYSTEM; - } - if (addr[0] == '\0') { - errno = EINVAL; - return -EAI_SYSTEM; - } - sock = atoi(addr); + sock = get_fd(addr); + if (sock < 0) { + errno = -sock; + return -EAI_SYSTEM; } /* make sure it's a socket */ struct stat st; @@ -195,85 +261,6 @@ int sockstream_listen(const char *type, const char *addr) { } } -void connection_handler(int fd) { - FILE *netstream = fdopen(fd, "r"); - char *line_buf = NULL; - size_t line_cap = 0; - ssize_t line_len = 0; - - line_len = getline(&line_buf, &line_cap, netstream); - /* expect line to be "GET /path.mjpg HTTP/1.1" */ - if (strncmp(line_buf, "GET ", 4) != 0) { - dprintf(fd, - "HTTP/1.1 405 Method Not Allowed\r\n" - "Allow: GET\r\n" - "\r\n"); - close(fd); - return; - } - char *version = &line_buf[4]; - char *path = strsep(&version, " "); - if (strcmp(version, "HTTP/1.1\r\n") != 0 && strcmp(version, "HTTP/1.0\r\n")) { - close(fd); - return; - } - path = strdupa(path); - - /* run through the rest of the headers */ - while (strcmp(line_buf, "\r\n") != 0 && line_len >= 0) - line_len = getline(&line_buf, &line_cap, netstream); - - if (strcmp(path, "/") == 0) { - error(0, 0, "200 %s", path); - dprintf(fd, - "HTTP/1.1 200 OK\r\n" - "Content-Type: text/html; charset=utf-8\r\n" - "\r\n" - "<!DOCTYPE html>\n" - "<html>\n" - "<head>\n" - " <title>multipart/x-mixed-replace HTTP server</title>\n" - "</head>\n" - "<body>\n" - " <ul>\n" - ); - for (size_t i = 0; i < filec; i++) - dprintf(fd, " <li><a href=\"%s\">%s</a></li>\n", filev[i].name, filev[i].name); - dprintf(fd, - " </ul>\n" - "</body>\n" - "</html>\n"); - close(fd); - return; - } - struct multipart_replace_stream *vidstream = file_get(path); - if (vidstream == NULL) { - error(0, 0, "404 %s", path); - dprintf(fd, - "HTTP/1.1 404 Not Found\r\n" - "\r\n"); - return; - } - - const char *boundary = "boundary" /* FIXME */; - error(0, 0, "200 %s", path); - dprintf(fd, - "HTTP/1.1 200 OK\r\n" - "Content-Type: multipart/x-mixed-replace;boundary=%s\r\n" - "\r\n", - boundary); - multipart_replace_writer(vidstream, fd, boundary); - close(fd); -} - -void *connection_thread(void *arg_anon) { - int fd = (int)(intptr_t)arg_anon; - error(0, 0, "Connection %d opened", fd); - connection_handler(fd); - error(0, 0, "Connection %d closed", fd); - return NULL; -} - void usage() { printf("Usage: %s [-h] ADDRTYPE ADDR [FILENAME...]\n", program_invocation_name); printf("Multiplex several multipart/x-mixed-replace streams over HTTP.\n" diff --git a/multipart-replace.c b/multipart-replace.c index 5f0ca4e..24a783e 100644 --- a/multipart-replace.c +++ b/multipart-replace.c @@ -108,7 +108,7 @@ void multipart_replace_writer(struct multipart_replace_stream *s, int fd, const long lastframe = 0; boundary = boundary_line(boundary); size_t boundary_len = strlen(boundary); - + error(0, 0, "boundary: <%s>", boundary); while(1) { /* poll until there's a new frame */ pthread_rwlock_rdlock(&s->frontlock); @@ -1,5 +1,6 @@ /* Copyright 2016 Luke Shumaker */ +#include <ctype.h> /* for isdigit */ #include <stdlib.h> #include <error.h> #include <errno.h> @@ -17,3 +18,57 @@ void *xrealloc(void *ptr, size_t size) } return ret; } + +bool is_numeric(const char *str) { + for (size_t i = 0; str[i] != '\0'; i++) + if (!isdigit(str[i])) + return false; + if (str[0] == '\0') + return false; + return true; +} + +int get_fd(const char *addr) { + int sock; + if (strcmp(addr, "stdin") == 0) { + sock = 0; + } else if (strcmp(addr, "stdout") == 0) { + sock = 1; + } else if (strcmp(addr, "stderr") == 0) { + sock = 2; + } else if (strncmp(addr, "systemd", strlen("systemd")) == 0) { + sock = 3; /* <systemd/sd-daemon.h>:SD_LISTEN_FDS_START */ + addr = &addr[strlen("systemd")]; + switch (addr[0]) { + case '\0': + /* do nothing */ + break; + case ':': + addr = &addr[1]; + if (is_numeric(addr)) { + sock += atoi(addr); + } else { + const char *e = getenv("LISTEN_FDNAMES"); + if (e == NULL) + return -ENOTCONN; + error(0, 0, "LISTEN_FDNAMES=\"%s\"", e); + char *names = strdupa(e); + char *name = NULL; + int i = -1; + do { + name = strsep(&names, ":"); + error(0, 0, "name=\"%s\"", name); + i++; + } while (name != NULL && strcmp(name, addr) != 0); + if (name == NULL) + return -ENOENT; + sock += i; + } + } + } else { + if (!is_numeric(addr)) + return -EINVAL; + sock = atoi(addr); + } + return sock; +} @@ -4,6 +4,7 @@ #include <signal.h> /* for sig_atomic_t */ #include <string.h> /* for memset(3) */ +#include <stdbool.h> #define UNUSED __attribute__((__unused__)) #define ZERO(x) memset(&(x), 0, sizeof(x)) @@ -12,4 +13,6 @@ #define _(str) str #endif -void * xrealloc(void *ptr, size_t size); +void *xrealloc(void *ptr, size_t size); +bool is_numeric(const char *str); +int get_fd(const char *addr); |