diff options
author | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-03-11 19:34:18 -0500 |
---|---|---|
committer | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-03-11 19:34:18 -0500 |
commit | a0a8aaf5170eab149ee0fed4d7846e0df856a2e4 (patch) | |
tree | 8e301f0c0279bdd5b592f1c63004b650fab3470f | |
parent | 77f1df1a250d958317c930e6f3a36edf42f11d09 (diff) |
split into separate executables
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 8 | ||||
-rw-r--r-- | freenect-server--http.c | 172 | ||||
-rw-r--r-- | freenect-server--kinect.c | 165 | ||||
-rw-r--r-- | freenect-server.sh | 24 | ||||
-rw-r--r-- | main.c | 218 | ||||
-rw-r--r-- | main.h | 41 | ||||
-rw-r--r-- | mpjpeg.c (renamed from thread_mpjpeg.c) | 16 | ||||
-rw-r--r-- | mpjpeg.h | 22 | ||||
-rw-r--r-- | thread_http.c | 129 | ||||
-rw-r--r-- | thread_kinect.c | 122 | ||||
-rw-r--r-- | util.c | 17 | ||||
-rw-r--r-- | util.h | 13 | ||||
-rw-r--r-- | wg.c | 43 | ||||
-rw-r--r-- | wg.h | 19 |
15 files changed, 427 insertions, 584 deletions
@@ -1,4 +1,6 @@ /freenect-server +/freenect-server--kinect +/freenect-server--http *.o .*.mk
\ No newline at end of file @@ -2,14 +2,14 @@ CFLAGS += -g -std=c99 -Wall -Werror -Wextra -pedantic CPPFLAGS += -std=c99 -Wall -Werror -Wextra -pedantic CPPFLAGS += -D_GNU_SOURCE -all: freenect-server +all: freenect-server freenect-server--kinect freenect-server--http .PHONY: all -freenect-server: main.o thread_kinect.o thread_mpjpeg.o thread_http.o wg.o -lfreenect -lusb-1.0 -lpthread - $(CC) $(LDFLAGS) $^ -o $@ +freenect-server--kinect: util.o -lfreenect -lusb-1.0 +freenect-server--http: util.o mpjpeg.o -lpthread clean: - rm -f -- *.o .*.mk freenect-server + rm -f -- *.o .*.mk freenect-server freenect-server--kinect freenect-server--http .PHONY: clean .DELETE_ON_ERROR: diff --git a/freenect-server--http.c b/freenect-server--http.c new file mode 100644 index 0000000..d966e3b --- /dev/null +++ b/freenect-server--http.c @@ -0,0 +1,172 @@ +#include <error.h> +#include <errno.h> +#include <netdb.h> /* for {get,free}addrinfo() */ +#include <stdlib.h> /* for EXIT_FAILURE */ +#include <sys/socket.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/stat.h> /* for open */ +#include <fcntl.h> /* for open */ + +#include "util.h" +#include "mpjpeg.h" + +struct httpfile { + char name[256]; + struct mpjpeg_stream *stream; +}; + +size_t filec = 0; +struct httpfile *filev = NULL; + +struct reader_thread_args { + struct mpjpeg_stream *stream; + int fd; + const char *boundary; +}; + +void *reader_thread(void *args_anon) { + struct reader_thread_args *args = args_anon; + mpjpeg_reader(args->stream, args->fd, args->boundary); + return NULL; +} + +void start_mpjpeg_reader(struct mpjpeg_stream *s, int fd, const char *boundary) { + struct reader_thread_args *args = xrealloc(NULL, sizeof(struct reader_thread_args)); + args->stream = s; + args->fd = fd; + args->boundary = boundary; + pthread_t thread; + pthread_create(&thread, NULL, reader_thread, args); +} + +void file_add(const char *filename) { + struct mpjpeg_stream *stream = xrealloc(NULL, sizeof(struct mpjpeg_stream)); + init_mpjpeg_stream(stream); + int fd = open(filename, O_RDONLY); + if (fd < 0) + error(EXIT_FAILURE, errno, "opening file: %s", filename); + filev = xrealloc(NULL, (++filec)*sizeof(*filev)); + strncpy(filev[filec-1].name, filename, sizeof(filev[filec-1].name)); + filev[filec-1].stream = stream; + start_mpjpeg_reader(stream, fd, "ffserver" /* FIXME */); +} + +struct mpjpeg_stream *file_get(const char *filename) { + for (size_t i = 0; i < filec; i++) { + if (strncmp(filename, filev[i].name, sizeof(filev[i].name)) == 0) { + return filev[i].stream; + } + } + return NULL; +} + +int tcp4_parse_listen(const char *port) { + int r; + struct addrinfo *addr = NULL; + struct addrinfo hints = { 0 }; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + if ((r = getaddrinfo(NULL, port, &hints, &addr)) != 0) + error(EXIT_FAILURE, r, _("Could not resolve TCP4 port %s"), port); + + int sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); + if (sock < 0) + error(EXIT_FAILURE, errno, _("Could not create a TCP4 socket")); + + int yes = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); + + if (bind(sock, addr->ai_addr, addr->ai_addrlen) < 0) + error(EXIT_FAILURE, errno, _("Could not bind to TCP4 port %s"), port); + + if (listen(sock, 5) < 0) + error(EXIT_FAILURE, errno, _("Could not listen on TCP4 port %s"), port); + + freeaddrinfo(addr); + return sock; +} + +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); + + struct mpjpeg_stream *vidstream = file_get(path); + if (vidstream == NULL) { + dprintf(fd, + "HTTP/1.1 404 Not Found\r\n" + "\r\n"); + return; + } + + const char *boundary = "boundary" /* FIXME */; + dprintf(fd, + "HTTP/1.1 200 OK\r\n" + "Content-Type: multipart/x-mixed-replace;boundary=%s\r\n" + "\r\n", + boundary); + mpjpeg_writer(vidstream, fd, boundary); + close(fd); +} + +void *connection_thread(void *arg_anon) { + int fd = (int)(intptr_t)arg_anon; + connection_handler(fd); + return NULL; +} + +void usage() { + printf("Usage: %s [-h] PORT [FILENAME...]\n", program_invocation_name); +} + +int main(int argc, char *argv[]) { + if (argc < 2) { + dup2(2, 1); + usage(); + return EXIT_FAILURE; + } + if (strcmp(argv[1], "-h") == 0) { + usage(); + return EXIT_SUCCESS; + } + int sock = tcp4_parse_listen(argv[1]); + + for (int i = 2; i < argc; i++) + file_add(argv[i]); + + while (1) { + int conn = accept(sock, NULL, NULL); + if (conn < 0) + continue; + pthread_t thread; + pthread_create(&thread, NULL, connection_thread, (void*)(intptr_t)conn); + } + + return 0; +} diff --git a/freenect-server--kinect.c b/freenect-server--kinect.c new file mode 100644 index 0000000..1c68fe3 --- /dev/null +++ b/freenect-server--kinect.c @@ -0,0 +1,165 @@ +#include <errno.h> +#include <error.h> +#include <stdio.h> +#include <stdlib.h> /* atexit */ +#include <unistd.h> /* dup2 */ + +#include <libfreenect/libfreenect.h> +#include <libusb-1.0/libusb.h> + +#include "util.h" + +FILE *depth_stream = NULL; +FILE *video_stream = NULL; +FILE *accel_stream = NULL; +freenect_context *ctx; + +void dump_ffmpeg_24(FILE *stream, uint32_t timestamp UNUSED, void *data, + size_t data_size) +{ + fwrite(data, data_size, 1, stream); +} + +void dump_ffmpeg_pad16(FILE *stream, uint32_t timestamp UNUSED, void *data_anon, + size_t data_size) +{ + uint16_t* data = data_anon; + uint16_t* end = (void*)&((char*)data_anon)[data_size]; + while (data < end) { + uint32_t z = *data; + fwrite(((char*)(&z)), 3, 1, stream); + data = &data[1]; + } +} + +void handle_accel(freenect_device *dev UNUSED, freenect_raw_tilt_state* data) +{ + printf("handle accel\n"); + double x, y, z; + freenect_get_mks_accel(data, &x, &y, &z); + //fprintf(accel_stream, "x=%f\ty=%f\tz=%f\n", x, y, z); +} + +void handle_depth(freenect_device *dev UNUSED, void *depth, uint32_t timestamp) +{ + printf("handle depth\n"); + dump_ffmpeg_pad16(depth_stream, timestamp, depth, + freenect_find_depth_mode(FREENECT_RESOLUTION_MEDIUM, + FREENECT_DEPTH_11BIT).bytes); +} + +void handle_video(freenect_device *dev, void *rgb, uint32_t timestamp) +{ + printf("handle video\n"); + dump_ffmpeg_24(video_stream, timestamp, rgb, + freenect_get_current_video_mode(dev).bytes); +} + +void print_mode(const char *name, freenect_frame_mode mode) { + /* This is just a courtesy function to let the user know the mode + if it becomes a bother for maintainability just comment out the + code in its body. It will only break if struct entries go missing. + */ + printf("%s Mode: {%d, %d, {%d}, %d, %d, %d, %d, %d, %d, %d}\n", name, + mode.reserved, (int)mode.resolution, (int)mode.video_format, mode.bytes, mode.width, + mode.height, mode.data_bits_per_pixel, mode.padding_bits_per_pixel, + mode.framerate, mode.is_valid); +} + +void cleanup() { + if (ctx) + freenect_shutdown(ctx); + if (video_stream) + fclose(video_stream); + if (depth_stream) + fclose(depth_stream); + if (accel_stream) + fclose(accel_stream); +} + +void usage() { + printf("Usage: %s [-h] [-v video.rgb24] [-d depth.rgb24] [-a accel.txt]\n", program_invocation_name); +} + +int main(int argc, char *argv[]) { + int res = 0; + freenect_device *dev; + + atexit(cleanup); + + for (int i = 1; i < argc; i++) { + if (argv[i][0] == '-' && argv[i][1] != '\0' && argv[i][2] == '\0') { + switch (argv[i][1]) { + case 'h': + usage(); + return EXIT_SUCCESS; + case 'v': + i++; + video_stream = fopen(argv[i], "w"); + break; + case 'd': + i++; + depth_stream = fopen(argv[i], "w"); + break; + case 'a': + i++; + accel_stream = fopen(argv[i], "w"); + break; + default: + dup2(2, 1); + usage(); + return EXIT_FAILURE; + } + } else { + dup2(2, 1); + usage(); + return EXIT_FAILURE; + } + } + + res = freenect_init(&ctx, 0); + if (res) { + error(EXIT_FAILURE, 0, "freenect_init: %s", libusb_strerror(res)); + } + + freenect_device_flags devices = 0; + if (video_stream || depth_stream) + devices |= FREENECT_DEVICE_CAMERA; + if (accel_stream) + devices |= FREENECT_DEVICE_MOTOR; + + freenect_select_subdevices(ctx, devices); + if ((res = freenect_open_device(ctx, &dev, 0))) { + error(EXIT_FAILURE, 0, "freenect_open_device: %s", libusb_strerror(res)); + } + + if (video_stream) { + print_mode("Video", freenect_find_video_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_VIDEO_RGB)); + freenect_set_video_mode(dev, freenect_find_video_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_VIDEO_RGB)); + freenect_start_video(dev); + freenect_set_video_callback(dev, handle_video); + } + + if (depth_stream) { + print_mode("Depth", freenect_find_depth_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_DEPTH_11BIT)); + freenect_set_depth_mode(dev, freenect_find_depth_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_DEPTH_11BIT)); + freenect_start_depth(dev); + freenect_set_depth_callback(dev, handle_depth); + } + + if (accel_stream) { + } + + while (freenect_process_events(ctx) >= 0) { + if (accel_stream) { + freenect_raw_tilt_state* state; + freenect_update_tilt_state(dev); + state = freenect_get_tilt_state(dev); + handle_accel(dev, state); + } + } + + freenect_stop_depth(dev); + freenect_stop_video(dev); + freenect_close_device(dev); +} diff --git a/freenect-server.sh b/freenect-server.sh new file mode 100644 index 0000000..b2f3d7c --- /dev/null +++ b/freenect-server.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +mkfifo video.rgb24 +mkfifo depth.rgb24 +mkfifo video.mjpg +mkfifo depth.mjpg + +pids=() +./freenect-server--kinect -v video.rgb24 -d depth.rgb24 & +pids+=($!) +./freenect-server--http 8090 video.mjpg depth.mjpg & +pids+=($!) +ffmpeg -pix_fmt rgb24 -s 640x480 -f rawvideo -i video.rgb24 -f mpjpeg - > video.mjpg & +pids+=($!) +ffmpeg -pix_fmt rgb24 -s 640x480 -f rawvideo -i depth.rgb24 -f mpjpeg - > depth.mjpg & +pids+=($!) + +cleanup() { + kill -- "${pids[@]}" + rm -f {video,depth}.{rgb24,mjpg} +} +trap cleanup EXIT + +wait @@ -1,218 +0,0 @@ -/* - * This file is part of the OpenKinect Project. http://www.openkinect.org - * - * Copyright (c) 2010 Brandyn White (bwhite@dappervision.com) - * Copyright (c) 2016 Luke Shumaker (lukeshu@sbcglobal.net) - * - * This code is licensed to you under the terms of the Apache License, version - * 2.0, or, at your option, the terms of the GNU General Public License, - * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, - * or the following URLs: - * http://www.apache.org/licenses/LICENSE-2.0 - * http://www.gnu.org/licenses/gpl-2.0.txt - * - * If you redistribute this file in source form, modified or unmodified, you - * may: - * 1) Leave this header intact and distribute it under the same terms, - * accompanying it with the APACHE20 and GPL20 files, or - * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or - * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file - * In all cases you must keep the copyright notice intact and include a copy - * of the CONTRIB file. - * - * Binary distributions must follow the binary distribution requirements of - * either License. - */ - -#include <stdio.h> - -#include <errno.h> -#include <error.h> -#include <pthread.h> -#include <signal.h> -#include <stdlib.h> -#include <unistd.h> -#include <sys/wait.h> - -#include "main.h" -#include "wg.h" - -volatile sig_atomic_t running = 1; -static struct wg wg; - -void * xrealloc(void *ptr, size_t size) -{ - void *ret = realloc(ptr, size); - if (ret == NULL) { - if (ptr==NULL) - error(1, errno, "Could not allocate memory"); - else - error(1, errno, "Could not re-allocate memory"); - } - return ret; -} - -static -void start_ffmpeg(int in, int out) { - switch (fork()) { - case -1: /* error */ - error(1, errno, "fork"); - case 0: /* child */ - dup2(in, 0); - dup2(out, 1); - for (int fd = 3; fd < 30; fd++) - close(fd); - execlp("ffmpeg", - "ffmpeg", - "-pix_fmt", "rgb24", "-s", "640x480", "-f", "rawvideo", "-i", "/dev/stdin", - "-f", "mpjpeg", "-", - NULL); - error(1, errno, "execlp: ffmpeg"); - default: /* parent */ - close(in); - close(out); - } -} - -struct thread_kinect_args { - int video_fd; - int depth_fd; - int accel_fd; -}; -static -void* start_kinect_inner(void *args_anon) { - pthread_setname_np(pthread_self(), "kinect"); - struct thread_kinect_args *args = args_anon; - thread_kinect(args->video_fd, args->depth_fd, args->accel_fd); - wg_sub(&wg, 1); - finish(0); - return NULL; -} -static -void start_kinect(int video_fd, int depth_fd, int accel_fd) { - - static struct thread_kinect_args args; - args.video_fd = video_fd; - args.depth_fd = depth_fd; - args.accel_fd = accel_fd; - - static pthread_t thread; - wg_add(&wg, 1); - pthread_create(&thread, NULL, start_kinect_inner, &args); -} - -struct thread_mpjpeg_args { - struct mpjpeg_stream *s; - int fd; - const char *boundary; -}; -static -void *start_mpjpeg_reader_inner(void *args_anon) { - pthread_setname_np(pthread_self(), "mpjpeg-reader"); - struct thread_mpjpeg_args *args = args_anon; - thread_mpjpeg_reader(args->s, args->fd, args->boundary); - wg_sub(&wg, 1); - return NULL; -} -static -void start_mpjpeg_reader(struct mpjpeg_stream *s, int fd, const char *boundary) { - struct thread_mpjpeg_args *args = xrealloc(NULL, sizeof(struct thread_mpjpeg_args)); - args->s = s; - args->fd = fd; - args->boundary = boundary; - wg_add(&wg, 1); - static pthread_t thread; - pthread_create(&thread, NULL, start_mpjpeg_reader_inner, args); -} - -struct thread_http_args { - struct wg *wg; - struct mpjpeg_stream *video; - struct mpjpeg_stream *depth; -}; -static -void *start_http_inner(void *args_anon) { - pthread_setname_np(pthread_self(), "http-listener"); - struct thread_http_args *args = args_anon; - thread_http_listener(args->wg, args->video, args->depth); - wg_sub(&wg, 1); - finish(0); - return NULL; -} -static -void start_http(struct wg *awg, - struct mpjpeg_stream *video, - struct mpjpeg_stream *depth) { - static struct thread_http_args args; - args.wg = awg; - args.video = video; - args.depth = depth; - - static pthread_t thread; - wg_add(&wg, 1); - pthread_create(&thread, NULL, start_http_inner, &args); -} - -static -void sigchld_handler(int sig UNUSED) -{ - int status; - while (waitpid(-1, &status, WNOHANG) > 0) {} -} - -void finish(int sig) -{ - if (sig != 0) - printf("Caught signal %d\n", sig); - running = 0; - close(httpsock); - kill(getpid(), SIGHUP); -} - -int main(int argc UNUSED, char **argv UNUSED) -{ - int kinect_video_fds[2]; - int kinect_depth_fds[2]; - int kinect_accel_fds[2]; - int mpjpeg_video_fds[2]; - int mpjpeg_depth_fds[2]; - struct mpjpeg_stream mpjpeg_video; - struct mpjpeg_stream mpjpeg_depth; - - struct sigaction act_chld; - sigemptyset (&act_chld.sa_mask); - act_chld.sa_flags = SA_RESTART; - act_chld.sa_handler = sigchld_handler; - if (sigaction(SIGCHLD, &act_chld, 0)) { - error(EXIT_FAILURE, errno, _("Could not set up SIGCHLD handler")); - } - - struct sigaction act_exit; - sigemptyset (&act_exit.sa_mask); - act_exit.sa_flags = 0; - act_exit.sa_handler = finish; - sigaction(SIGINT, &act_exit, 0); - sigaction(SIGQUIT, &act_exit, 0); - sigaction(SIGTERM, &act_exit, 0); - - if (pipe(kinect_video_fds) == -1) error(1, errno, "pipe"); - if (pipe(kinect_depth_fds) == -1) error(1, errno, "pipe"); - if (pipe(kinect_accel_fds) == -1) error(1, errno, "pipe"); - if (pipe(mpjpeg_video_fds) == -1) error(1, errno, "pipe"); - if (pipe(mpjpeg_depth_fds) == -1) error(1, errno, "pipe"); - init_mpjpeg_stream(&mpjpeg_video); - init_mpjpeg_stream(&mpjpeg_depth); - - wg_init(&wg); - start_kinect(kinect_video_fds[1], - kinect_depth_fds[1], - kinect_accel_fds[1]); - start_mpjpeg_reader(&mpjpeg_video, mpjpeg_video_fds[0], "ffserver"); - start_mpjpeg_reader(&mpjpeg_depth, mpjpeg_depth_fds[0], "ffserver"); - start_http(&wg, &mpjpeg_video, &mpjpeg_depth); - /* never call exit() after we've started ffmpeg */ - start_ffmpeg(kinect_video_fds[0], mpjpeg_video_fds[1]); - start_ffmpeg(kinect_depth_fds[0], mpjpeg_depth_fds[1]); - wg_wait(&wg); - return 0; -} @@ -1,41 +0,0 @@ -#pragma once - -#include <signal.h> /* for sig_atomic_t */ -#include <string.h> /* for memset(3) */ - -#define UNUSED __attribute__((__unused__)) -#define ZERO(x) memset(&(x), 0, sizeof(x)) - -#ifndef _ -#define _(str) str -#endif - -extern volatile sig_atomic_t running; -extern int httpsock; - -struct frame { - ssize_t len; - size_t cap; - char *data; -}; - -struct mpjpeg_stream { - struct frame a; - struct frame b; - - struct frame *front; - struct frame *back; - pthread_mutex_t frontlock; -}; - -struct wg; - -void finish(int sig); -void thread_kinect(int video_fd, int depth_fd, int accel_fd); -void thread_http_listen(int fd); -void thread_mpjpeg_reader(struct mpjpeg_stream *s, int fd, const char *boundary); -void thread_mpjpeg_writer(struct mpjpeg_stream *s, int fd, const char *boundary); -void thread_http_listener(struct wg *wg, struct mpjpeg_stream *video, struct mpjpeg_stream *depth); -void init_mpjpeg_stream(); - -void * xrealloc(void *ptr, size_t size); diff --git a/thread_mpjpeg.c b/mpjpeg.c index 410faa0..dd4b96e 100644 --- a/thread_mpjpeg.c +++ b/mpjpeg.c @@ -1,15 +1,17 @@ /* Copyright 2016 Luke Shumaker */ -#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include "main.h" +#include "mpjpeg.h" +#include "util.h" + +#define STARTS_WITH(str, prefix) strncmp(str, prefix, sizeof(prefix)-1) static -char *format_boundary(const char *old) { +char *boundary_line(const char *old) { size_t len = strlen(old); char *new = xrealloc(NULL, len+5); new[0] = '-'; @@ -21,11 +23,9 @@ char *format_boundary(const char *old) { return new; } -#define STARTS_WITH(str, prefix) strncmp(str, prefix, sizeof(prefix)-1) - -void thread_mpjpeg_reader(struct mpjpeg_stream *s, int fd, const char *boundary) { +void mpjpeg_reader(struct mpjpeg_stream *s, int fd, const char *boundary) { FILE *stream = fdopen(fd, "r"); - boundary = format_boundary(boundary); + boundary = boundary_line(boundary); char *line_buf = NULL; size_t line_cap = 0; @@ -67,7 +67,7 @@ void thread_mpjpeg_reader(struct mpjpeg_stream *s, int fd, const char *boundary) } -void thread_mpjpeg_writer(struct mpjpeg_stream *s, int fd, const char *boundary) { +void mpjpeg_writer(struct mpjpeg_stream *s, int fd, const char *boundary) { struct frame myframe = { 0 }; struct frame *lastframe = NULL; while(1) { diff --git a/mpjpeg.h b/mpjpeg.h new file mode 100644 index 0000000..7a51006 --- /dev/null +++ b/mpjpeg.h @@ -0,0 +1,22 @@ +#pragma once + +#include <pthread.h> + +struct frame { + ssize_t len; + size_t cap; + char *data; +}; + +struct mpjpeg_stream { + struct frame a; + struct frame b; + + struct frame *front; + struct frame *back; + pthread_mutex_t frontlock; +}; + +void mpjpeg_reader(struct mpjpeg_stream *s, int fd, const char *boundary); +void mpjpeg_writer(struct mpjpeg_stream *s, int fd, const char *boundary); +void init_mpjpeg_stream(); diff --git a/thread_http.c b/thread_http.c deleted file mode 100644 index 353ad0f..0000000 --- a/thread_http.c +++ /dev/null @@ -1,129 +0,0 @@ -#include <error.h> -#include <errno.h> -#include <netdb.h> /* for {get,free}addrinfo() */ -#include <stdlib.h> /* for EXIT_FAILURE */ -#include <sys/socket.h> -#include <stdio.h> -#include <unistd.h> - -#include "main.h" -#include "wg.h" - -static -int tcp4_parse_listen(const char *port) { - int r; - struct addrinfo *addr = NULL; - struct addrinfo hints = { 0 }; - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE; - - if ((r = getaddrinfo(NULL, port, &hints, &addr)) != 0) - error(EXIT_FAILURE, r, _("Could not resolve TCP4 port %s"), port); - - int sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); - if (sock < 0) - error(EXIT_FAILURE, errno, _("Could not create a TCP4 socket")); - - int yes = 1; - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); - - if (bind(sock, addr->ai_addr, addr->ai_addrlen) < 0) - error(EXIT_FAILURE, errno, _("Could not bind to TCP4 port %s"), port); - - if (listen(sock, 5) < 0) - error(EXIT_FAILURE, errno, _("Could not listen on TCP4 port %s"), port); - - freeaddrinfo(addr); - return sock; -} - -static struct wg *wg; -static struct mpjpeg_stream *video; -static struct mpjpeg_stream *depth; - -static -void thread_http_connection(int fd) { - FILE *stream = fdopen(fd, "r"); - char *line_buf = NULL; - size_t line_cap = 0; - getline(&line_buf, &line_cap, stream); - /* expect line to be "GET /kinect-(video|depth).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) - getline(&line_buf, &line_cap, stream); - - const char *boundary = "boundary"; - if (strcmp(path, "/kinect-video.mjpg") == 0) { - dprintf(fd, - "HTTP/1.1 200 OK\r\n" - "Content-Type: multipart/x-mixed-replace;boundary=%s\r\n" - "\r\n", - boundary); - thread_mpjpeg_writer(video, fd, boundary); - } else if (strcmp(path, "/kinect-depth.mjpg") == 0) { - dprintf(fd, - "HTTP/1.1 200 OK\r\n" - "Content-Type: multipart/x-mixed-replace;boundary=%s\r\n" - "\r\n", - boundary); - thread_mpjpeg_writer(depth, fd, boundary); - } else { - printf("not found url: %s\n", path); - dprintf(fd, - "HTTP/1.1 404 Not Found\r\n" - "\r\n"); - } - close(fd); -} - -static -void *start_http_connection_inner(void *arg_anon) { - pthread_setname_np(pthread_self(), "http-connection"); - int fd = (int)(intptr_t)arg_anon; - thread_http_connection(fd); - wg_sub(wg, 1); - return NULL; -} - -int httpsock = 0; - -static -void start_http_connection(int fd) { - wg_add(wg, 1); - pthread_t thread; - pthread_create(&thread, NULL, start_http_connection_inner, (void*)(intptr_t)fd); -} - -void thread_http_listener(struct wg *awg, - struct mpjpeg_stream *avideo, - struct mpjpeg_stream *adepth) { - wg = awg; - video = avideo; - depth = adepth; - httpsock = tcp4_parse_listen("8090"); - - while (running) { - int conn = accept(httpsock, NULL, NULL); - if (conn < 0) - continue; - start_http_connection(conn); - } - finish(0); -} diff --git a/thread_kinect.c b/thread_kinect.c deleted file mode 100644 index bd70679..0000000 --- a/thread_kinect.c +++ /dev/null @@ -1,122 +0,0 @@ -#include <error.h> -#include <libfreenect/libfreenect.h> -#include <libusb-1.0/libusb.h> -#include <pthread.h> -#include <stdio.h> -#include <unistd.h> - -#include "main.h" - -FILE *depth_stream = NULL; -FILE *video_stream = NULL; -FILE *accel_stream = NULL; - -static -void dump_ffmpeg_24(FILE *stream, uint32_t timestamp UNUSED, void *data, - size_t data_size) -{ - fwrite(data, data_size, 1, stream); -} - -static -void dump_ffmpeg_pad16(FILE *stream, uint32_t timestamp UNUSED, void *data_anon, - size_t data_size) -{ - uint16_t* data = data_anon; - uint16_t* end = (void*)&((char*)data_anon)[data_size]; - while (data < end) { - uint32_t z = *data; - fwrite(((char*)(&z)), 3, 1, stream); - data = &data[1]; - } -} - -static -void handle_accel(freenect_device *dev UNUSED, freenect_raw_tilt_state* data) -{ - double x, y, z; - freenect_get_mks_accel(data, &x, &y, &z); - //fprintf(accel_stream, "x=%f\ty=%f\tz=%f\n", x, y, z); -} - -static -void handle_depth(freenect_device *dev UNUSED, void *depth, uint32_t timestamp) -{ - printf("handle depth\n"); - dump_ffmpeg_pad16(depth_stream, timestamp, depth, - freenect_find_depth_mode(FREENECT_RESOLUTION_MEDIUM, - FREENECT_DEPTH_11BIT).bytes); -} - -static -void handle_video(freenect_device *dev, void *rgb, uint32_t timestamp) -{ - printf("handle depth\n"); - dump_ffmpeg_24(video_stream, timestamp, rgb, - freenect_get_current_video_mode(dev).bytes); -} - -static -void print_mode(const char *name, freenect_frame_mode mode) { - /* This is just a courtesy function to let the user know the mode - if it becomes a bother for maintainability just comment out the - code in its body. It will only break if struct entries go missing. - */ - printf("%s Mode: {%d, %d, {%d}, %d, %d, %d, %d, %d, %d, %d}\n", name, - mode.reserved, (int)mode.resolution, (int)mode.video_format, mode.bytes, mode.width, - mode.height, mode.data_bits_per_pixel, mode.padding_bits_per_pixel, - mode.framerate, mode.is_valid); -} - -void thread_kinect(int video_fd, int depth_fd, int accel_fd) { - int res = 0; - - freenect_context *ctx; - freenect_device *dev; - - pthread_setname_np(pthread_self(), "libusb"); - res = freenect_init(&ctx, 0); - pthread_setname_np(pthread_self(), "kinect"); - if (res) { - error(0, 0, "freenect_init: %s", libusb_strerror(res)); - goto end; - } - - freenect_select_subdevices(ctx, (freenect_device_flags)(FREENECT_DEVICE_CAMERA | FREENECT_DEVICE_MOTOR)); - if ((res = freenect_open_device(ctx, &dev, 0))) { - error(0, 0, "freenect_open_device: %s", libusb_strerror(res)); - goto end; - } - - print_mode("Depth", freenect_find_depth_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_DEPTH_11BIT)); - freenect_set_depth_mode(dev, freenect_find_depth_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_DEPTH_11BIT)); - freenect_start_depth(dev); - - print_mode("Video", freenect_find_video_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_VIDEO_RGB)); - freenect_set_video_mode(dev, freenect_find_video_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_VIDEO_RGB)); - freenect_start_video(dev); - - depth_stream = fdopen(depth_fd, "w"); - video_stream = fdopen(video_fd, "w"); - accel_stream = fdopen(accel_fd, "w"); - - freenect_set_depth_callback(dev, handle_depth); - freenect_set_video_callback(dev, handle_video); - - while (running && freenect_process_events(ctx) >= 0) { - freenect_raw_tilt_state* state; - freenect_update_tilt_state(dev); - state = freenect_get_tilt_state(dev); - handle_accel(dev, state); - } - - freenect_stop_depth(dev); - freenect_stop_video(dev); - freenect_close_device(dev); - - end: - freenect_shutdown(ctx); - close(video_fd); - close(depth_fd); - close(accel_fd); -} @@ -0,0 +1,17 @@ +#include <stdlib.h> +#include <error.h> +#include <errno.h> + +#include "util.h" + +void *xrealloc(void *ptr, size_t size) +{ + void *ret = realloc(ptr, size); + if (ret == NULL) { + if (ptr==NULL) + error(1, errno, "Could not allocate memory"); + else + error(1, errno, "Could not re-allocate memory"); + } + return ret; +} @@ -0,0 +1,13 @@ +#pragma once + +#include <signal.h> /* for sig_atomic_t */ +#include <string.h> /* for memset(3) */ + +#define UNUSED __attribute__((__unused__)) +#define ZERO(x) memset(&(x), 0, sizeof(x)) + +#ifndef _ +#define _(str) str +#endif + +void * xrealloc(void *ptr, size_t size); @@ -1,43 +0,0 @@ -#include <unistd.h> -#include <error.h> -#include <errno.h> - -#include "wg.h" - -/* Thread management tools modeled on https://golang.org/pkg/sync/#WaitGroup */ - -/* pthread_cond_t is overly complicated. Just use a self-pipe. */ - -void wg_init(struct wg *wg) { - wg->count = 0; - pthread_mutex_init(&wg->lock, NULL); - int fds[2]; - if (pipe(fds) != 0) - error(1, errno, "pipe"); - wg->fd_wait = fds[0]; - wg->fd_signal = fds[1]; -} - -void wg_add(struct wg *wg, unsigned int n) { - pthread_mutex_lock(&wg->lock); - wg->count += n; - pthread_mutex_unlock(&wg->lock); -} - -void wg_sub(struct wg *wg, unsigned int n) { - pthread_mutex_lock(&wg->lock); - wg->count -= n; - if (wg->count == 0) - write(wg->fd_signal, " ", 1); - pthread_mutex_unlock(&wg->lock); -} - -void wg_wait(struct wg *wg) { - char b; - retry: - if (read(wg->fd_wait, &b, 1) == -1) { - if (errno == EINTR) - goto retry; - error(1, errno, "wg_wait"); - } -} @@ -1,19 +0,0 @@ -#pragma once - -#include <pthread.h> - -/* Thread management tools modeled on https://golang.org/pkg/sync/#WaitGroup */ - -/* pthread_cond_t is overly complicated. Just use a self-pipe. */ - -struct wg { - int count; - pthread_mutex_t lock; - int fd_wait; - int fd_signal; -}; - -void wg_init(struct wg *); -void wg_add(struct wg *, unsigned int); -void wg_sub(struct wg *, unsigned int); -void wg_wait(struct wg*); |