summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@sbcglobal.net>2016-03-11 19:34:18 -0500
committerLuke Shumaker <lukeshu@sbcglobal.net>2016-03-11 19:34:18 -0500
commita0a8aaf5170eab149ee0fed4d7846e0df856a2e4 (patch)
tree8e301f0c0279bdd5b592f1c63004b650fab3470f
parent77f1df1a250d958317c930e6f3a36edf42f11d09 (diff)
split into separate executables
-rw-r--r--.gitignore2
-rw-r--r--Makefile8
-rw-r--r--freenect-server--http.c172
-rw-r--r--freenect-server--kinect.c165
-rw-r--r--freenect-server.sh24
-rw-r--r--main.c218
-rw-r--r--main.h41
-rw-r--r--mpjpeg.c (renamed from thread_mpjpeg.c)16
-rw-r--r--mpjpeg.h22
-rw-r--r--thread_http.c129
-rw-r--r--thread_kinect.c122
-rw-r--r--util.c17
-rw-r--r--util.h13
-rw-r--r--wg.c43
-rw-r--r--wg.h19
15 files changed, 427 insertions, 584 deletions
diff --git a/.gitignore b/.gitignore
index 4d63b10..db97f5f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
/freenect-server
+/freenect-server--kinect
+/freenect-server--http
*.o
.*.mk \ No newline at end of file
diff --git a/Makefile b/Makefile
index 39e1290..58da358 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/main.c b/main.c
deleted file mode 100644
index 9af7dd7..0000000
--- a/main.c
+++ /dev/null
@@ -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;
-}
diff --git a/main.h b/main.h
deleted file mode 100644
index 73fa96e..0000000
--- a/main.h
+++ /dev/null
@@ -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);
-}
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..22b6dc6
--- /dev/null
+++ b/util.c
@@ -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;
+}
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..0eb8c19
--- /dev/null
+++ b/util.h
@@ -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);
diff --git a/wg.c b/wg.c
deleted file mode 100644
index 3208aa1..0000000
--- a/wg.c
+++ /dev/null
@@ -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");
- }
-}
diff --git a/wg.h b/wg.h
deleted file mode 100644
index 777e8a3..0000000
--- a/wg.h
+++ /dev/null
@@ -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*);