/* Copyright 2016 Luke Shumaker */ #include #include #include #include #include "mpjpeg.h" #include "util.h" #define STARTS_WITH(str, prefix) strncmp(str, prefix, sizeof(prefix)-1) static char *boundary_line(const char *old) { size_t len = strlen(old); char *new = xrealloc(NULL, len+5); new[0] = '-'; new[1] = '-'; strcpy(&new[2], old); new[2+len+0] = '\r'; new[2+len+1] = '\n'; new[2+len+2] = '\0'; return new; } void mpjpeg_reader(struct mpjpeg_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; while (1) { printf("getting frame\n"); s->back->len = -1; /* read the frame header (MIME headers) */ line_len = getline(&line_buf, &line_cap, stream); if (strcmp(line_buf, boundary) != 0) return; ssize_t framelen = -1; while (strcmp(line_buf, "\r\n") != 0 && line_len >= 0) { line_len = getline(&line_buf, &line_cap, stream); if (STARTS_WITH(line_buf, "Content-Type:") == 0) { printf("line: <%s>\n", line_buf); s->back->len = atoi(&line_buf[strlen("Content-Type:")]); } } if (s->back->len < 0) 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(line_buf, framelen, 1, stream) != 1) return; printf("got frame on fd %d\n", fd); /* swap the frames */ pthread_mutex_lock(&s->frontlock); struct frame *tmp = s->front; s->front = s->back; s->back = tmp; pthread_mutex_unlock(&s->frontlock); } } void mpjpeg_writer(struct mpjpeg_stream *s, int fd, const char *boundary) { struct frame myframe = { 0 }; struct frame *lastframe = NULL; while(1) { /* get the most recent frame (copy front to myframe) */ pthread_mutex_lock(&s->frontlock); if (s->front == lastframe) { /* TODO: use a pthread_cond_t instead of a busy loop */ pthread_mutex_unlock(&s->frontlock); continue; } 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); lastframe = s->front; pthread_mutex_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) return; if (write(fd, myframe.data, myframe.len) < myframe.len) return; } } void init_mpjpeg_stream(struct mpjpeg_stream *s) { ZERO(s->a); ZERO(s->b); s->front = &s->a; s->back = &s->b; pthread_mutex_init(&s->frontlock, NULL); }