diff options
author | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-03-15 17:47:59 -0400 |
---|---|---|
committer | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-03-15 17:47:59 -0400 |
commit | 116c393eb5a770988bf1abf0be8f24d0e29748b1 (patch) | |
tree | d28786975421e96dba2d276d61188002394346b3 | |
parent | 9853745525fc736a4a0323588437b77120b1fcb6 (diff) |
http server: don't assume multipart/x-mixed-replace streams are JPEGs
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | freenect-server.sh | 2 | ||||
-rw-r--r-- | mpjpeg.h | 22 | ||||
-rw-r--r-- | multipart-replace-http-server.c (renamed from freenect-server--http.c) | 24 | ||||
-rw-r--r-- | multipart-replace.c (renamed from mpjpeg.c) | 81 | ||||
-rw-r--r-- | multipart-replace.h | 22 |
7 files changed, 88 insertions, 71 deletions
@@ -1,6 +1,6 @@ /freenect-server /freenect-server--kinect -/freenect-server--http +/multipart-replace-http-server *.o .*.mk
\ No newline at end of file @@ -4,14 +4,14 @@ CFLAGS += -g -std=c99 -Wall -Werror -Wextra -pedantic CPPFLAGS += -std=c99 -Wall -Werror -Wextra -pedantic CPPFLAGS += -D_GNU_SOURCE -all: freenect-server freenect-server--kinect freenect-server--http +all: freenect-server freenect-server--kinect multipart-replace-http-server .PHONY: all freenect-server--kinect: util.o -lfreenect -lusb-1.0 -freenect-server--http: util.o mpjpeg.o -lpthread +multipart-replace-http-server: util.o multipart-replace.o -lpthread clean: - rm -f -- *.o .*.mk freenect-server freenect-server--kinect freenect-server--http + rm -f -- *.o .*.mk freenect-server freenect-server--kinect multipart-replace-http-server .PHONY: clean .DELETE_ON_ERROR: diff --git a/freenect-server.sh b/freenect-server.sh index 27d3fd3..721243c 100644 --- a/freenect-server.sh +++ b/freenect-server.sh @@ -11,7 +11,7 @@ mkfifo $t/video.mjpg mkfifo $t/depth.mjpg ( freenect-server--kinect -v $t/video.rgb24 -d $t/depth.rgb24; echo "EXITED: freenect-server--kinect: $?") & pids+=($!) -( freenect-server--http 5800 $t/video.mjpg $t/depth.mjpg; echo "EXITED: freenect-server--http: $?") & pids+=($!) +( multipart-replace-http-server 5800 $t/video.mjpg $t/depth.mjpg; echo "EXITED: freenect-server--http: $?") & pids+=($!) rm -f {depth,video}.{avi,mjpg} ( ffmpeg -loglevel warning -pixel_format rgb24 -s 640x480 -f rawvideo -i $t/video.rgb24 -q:v 1 -f mpjpeg - > $t/video.mjpg; echo "EXITED: ffmpeg video: $?") & pids+=($!) ( ffmpeg -loglevel warning -pixel_format rgb24 -s 640x480 -f rawvideo -i $t/depth.rgb24 -q:v 1 -f mpjpeg - > $t/depth.mjpg; echo "EXITED: ffmpeg depth: $?") & pids+=($!) diff --git a/mpjpeg.h b/mpjpeg.h deleted file mode 100644 index f850adc..0000000 --- a/mpjpeg.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Copyright 2016 Luke Shumaker */ - -#pragma once - -#include <pthread.h> - -struct frame { - ssize_t len; - size_t cap; - char *data; -}; - -struct mpjpeg_stream { - struct frame a, b; - struct frame *front, *back; - pthread_rwlock_t frontlock; - long framecount; -}; - -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/freenect-server--http.c b/multipart-replace-http-server.c index cda7ae1..c3e6219 100644 --- a/freenect-server--http.c +++ b/multipart-replace-http-server.c @@ -11,30 +11,30 @@ #include <fcntl.h> /* for open */ #include "util.h" -#include "mpjpeg.h" +#include "multipart-replace.h" struct httpfile { char name[256]; - struct mpjpeg_stream *stream; + struct multipart_replace_stream *stream; }; size_t filec = 0; struct httpfile *filev = NULL; struct reader_thread_args { - struct mpjpeg_stream *stream; + struct multipart_replace_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); - error(EXIT_FAILURE, 0, "mpjpeg stream ended"); + multipart_replace_reader(args->stream, args->fd, args->boundary); + error(EXIT_FAILURE, 0, "multipart_replace stream ended"); return NULL; } -void start_mpjpeg_reader(struct mpjpeg_stream *s, int fd, const char *boundary) { +void start_multipart_replace_reader(struct multipart_replace_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; @@ -44,8 +44,8 @@ void start_mpjpeg_reader(struct mpjpeg_stream *s, int fd, const char *boundary) } void file_add(const char *filename) { - struct mpjpeg_stream *stream = xrealloc(NULL, sizeof(struct mpjpeg_stream)); - init_mpjpeg_stream(stream); + struct multipart_replace_stream *stream = xrealloc(NULL, sizeof(struct multipart_replace_stream)); + init_multipart_replace_stream(stream); int fd = open(filename, O_RDONLY); if (fd < 0) error(EXIT_FAILURE, errno, "opening file: %s", filename); @@ -56,10 +56,10 @@ void file_add(const char *filename) { strncpy(filev[filec-1].name, shortname, sizeof(filev[filec-1].name)); error(0, 0, "added file #%zd: %s", filec-1, filev[filec-1].name); filev[filec-1].stream = stream; - start_mpjpeg_reader(stream, fd, "ffserver" /* FIXME */); + start_multipart_replace_reader(stream, fd, "ffserver" /* FIXME */); } -struct mpjpeg_stream *file_get(const char *filename) { +struct multipart_replace_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; @@ -124,7 +124,7 @@ void connection_handler(int fd) { 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); + struct multipart_replace_stream *vidstream = file_get(path); if (vidstream == NULL) { error(0, 0, "404 %s", path); dprintf(fd, @@ -140,7 +140,7 @@ void connection_handler(int fd) { "Content-Type: multipart/x-mixed-replace;boundary=%s\r\n" "\r\n", boundary); - mpjpeg_writer(vidstream, fd, boundary); + multipart_replace_writer(vidstream, fd, boundary); close(fd); } diff --git a/mpjpeg.c b/multipart-replace.c index 87ca2a4..30ae7bb 100644 --- a/mpjpeg.c +++ b/multipart-replace.c @@ -7,7 +7,7 @@ #include <string.h> #include <unistd.h> -#include "mpjpeg.h" +#include "multipart-replace.h" #include "util.h" static @@ -38,44 +38,53 @@ ssize_t safe_atoi(char *str) { return atoi(str); } -void mpjpeg_reader(struct mpjpeg_stream *s, int fd, const char *boundary) { +#define rgetline() do { if ((line_len = getline(&line_buf, &line_cap, stream)) < 0) { error(0, 0, "source hung up"); return; } } while(0) +void multipart_replace_reader(struct multipart_replace_stream *s, int fd, const char *boundary) { FILE *stream = fdopen(fd, "r"); boundary = boundary_line(boundary); char *line_buf = NULL; size_t line_cap = 0; ssize_t line_len = 0; + ssize_t content_length = -1; while (1) { - s->back->len = -1; + content_length = -1; /* scan for the first non-empty line */ do { - line_len = getline(&line_buf, &line_cap, stream); - } while (line_len >= 0 && strcmp(line_buf, "\r\n") == 0); + rgetline(); + } while (strcmp(line_buf, "\r\n") == 0); /* make sure it matches the boundary separator */ if (strcmp(line_buf, boundary) != 0) { error(0, 0, "line does not match boundary: \"%s\"", line_buf); return; } /* read the frame header (MIME headers) */ - while (strcmp(line_buf, "\r\n") != 0 && line_len >= 0) { - line_len = getline(&line_buf, &line_cap, stream); + do { + rgetline(); + /* append the line to the frame contents */ + if ((ssize_t)s->back->cap < s->back->len + line_len) + s->back->buf = xrealloc(s->back->buf, s->back->cap = s->back->len + line_len); + memcpy(&s->back->buf[s->back->len], line_buf, line_len); + s->back->len += line_len; + /* parse the Content-length (if applicable) */ if (strncasecmp(line_buf, "Content-length:", strlen("Content-length:")) == 0) { - s->back->len = safe_atoi(&line_buf[strlen("Content-length:")]); + content_length = safe_atoi(&line_buf[strlen("Content-length:")]); } - } - if (s->back->len < 0) { + } while (strcmp(line_buf, "\r\n") != 0); + /* make sure that it included a Content-length header */ + if (content_length < 0) { error(0, 0, "did not get frame length"); return; } - - /* read the frame contents (JPEG) */ - if (s->back->cap < (size_t)s->back->len) - s->back->data = xrealloc(s->back->data, s->back->cap = s->back->len); - if (fread(s->back->data, s->back->len, 1, stream) != 1) { + /* read the frame contents */ + if ((ssize_t)s->back->cap < s->back->len + content_length) + s->back->buf = xrealloc(s->back->buf, s->back->cap = s->back->len + content_length); + if (fread(s->back->buf, s->back->len, 1, stream) != 1) { error(0, ferror(stream), "fread(%zd)", s->back->len); return; } + s->back->len += content_length; /* swap the frames */ pthread_rwlock_wrlock(&s->frontlock); @@ -85,41 +94,49 @@ void mpjpeg_reader(struct mpjpeg_stream *s, int fd, const char *boundary) { s->framecount++; pthread_rwlock_unlock(&s->frontlock); } - } -void mpjpeg_writer(struct mpjpeg_stream *s, int fd, const char *boundary) { +void multipart_replace_writer(struct multipart_replace_stream *s, int fd, const char *boundary) { struct frame myframe = { 0 }; long lastframe = 0; - pthread_rwlock_rdlock(&s->frontlock); + boundary = boundary_line(boundary); + size_t boundary_len = strlen(boundary); + while(1) { - /* get the most recent frame (copy front to myframe) */ + /* poll until there's a new frame */ + pthread_rwlock_rdlock(&s->frontlock); + while (s->framecount == lastframe) { + pthread_rwlock_unlock(&s->frontlock); + usleep(30000); /* a bit over 30 FPS */ + pthread_rwlock_rdlock(&s->frontlock); + } + + /* get the most recent frame (copy `s->front` to `myframe`) */ if (myframe.cap < (size_t)s->front->len) - myframe.data = xrealloc(myframe.data, myframe.cap = s->front->len); - memcpy(myframe.data, s->front->data, myframe.len = s->front->len); + myframe.buf = xrealloc(myframe.buf, myframe.cap = s->front->len); + memcpy(myframe.buf, s->front->buf, myframe.len = s->front->len); lastframe = s->framecount; + pthread_rwlock_unlock(&s->frontlock); + /* send the frame to the client */ - if (dprintf(fd, "--%s\r\nContent-Type: image/jpeg\r\nContent-Length: %zd\r\n\r\n", boundary, myframe.len) < 0) { - error(0, errno, "dprintf"); + if (write(fd, boundary, boundary_len) < (ssize_t)boundary_len) { + error(0, errno, "write"); return; } - if (write(fd, myframe.data, myframe.len) < myframe.len) { + if (write(fd, myframe.buf, myframe.len) < myframe.len) { error(0, errno, "write"); return; } - - /* poll until there's a new frame */ - pthread_rwlock_rdlock(&s->frontlock); - while (s->framecount == lastframe) { - pthread_rwlock_unlock(&s->frontlock); - usleep(30000); /* a bit over 30 FPS */ - pthread_rwlock_rdlock(&s->frontlock); + /* send a blank line for pleasantness */ + if (write(fd, "\r\n", 2) < 2) { + error(0, errno, "write"); + return; } } } -void init_mpjpeg_stream(struct mpjpeg_stream *s) { +void init_multipart_replace_stream(struct multipart_replace_stream *s) { ZERO(s->a); ZERO(s->b); s->front = &s->a; diff --git a/multipart-replace.h b/multipart-replace.h new file mode 100644 index 0000000..126f6fa --- /dev/null +++ b/multipart-replace.h @@ -0,0 +1,22 @@ +/* Copyright 2016 Luke Shumaker */ + +#pragma once + +#include <pthread.h> + +struct frame { + ssize_t len; + size_t cap; + char *buf; +}; + +struct multipart_replace_stream { + struct frame a, b; + struct frame *front, *back; + pthread_rwlock_t frontlock; + long framecount; +}; + +void multipart_replace_reader(struct multipart_replace_stream *s, int fd, const char *boundary); +void multipart_replace_writer(struct multipart_replace_stream *s, int fd, const char *boundary); +void init_multipart_replace_stream(); |