diff --git a/http/http.h b/http/http.h index ffc8d22..91ad320 100644 --- a/http/http.h +++ b/http/http.h @@ -38,7 +38,9 @@ int http_server(http_server_options_t *options, http_method_t *methods); void http_index(http_worker_t *worker, FILE *stream); void http_video_html(http_worker_t *worker, FILE *stream); void http_jmuxer_js(http_worker_t *worker, FILE *stream); -void http_404_header(http_worker_t *worker, FILE *stream); +void http_custom_header(FILE *stream, const char *status); +void http_404_header(FILE *stream); +void http_500_header(FILE *stream); void http_404(http_worker_t *worker, FILE *stream); // M-JPEG diff --git a/http/http_h264.c b/http/http_h264.c index 074115f..a7b7f2a 100644 --- a/http/http_h264.c +++ b/http/http_h264.c @@ -25,45 +25,58 @@ bool http_h264_needs_buffer() return buffer_lock_needs_buffer(&http_h264); } -void http_video(http_worker_t *worker, FILE *stream) +typedef struct { + FILE *stream; + bool wrote_header; + bool had_key_frame; + bool requested_key_frame; +} http_video_status_t; + +int http_video_buf_part(buffer_lock_t *buf_lock, buffer_t *buf, int frame, http_video_status_t *status) { - bool had_key_frame = false; - bool requested_key_frame = false; - int counter = 0; - buffer_t *buf = NULL; - fprintf(stream, VIDEO_HEADER); + unsigned char *data = buf->start; - buffer_lock_use(&http_h264, 1); - - while (!feof(stream)) { - buf = buffer_lock_get(&http_h264, 0, &counter); - if (!buf) { - goto error; - } - - unsigned char *data = buf->start; - - if (buf->v4l2_buffer.flags & V4L2_BUF_FLAG_KEYFRAME) { - had_key_frame = true; - E_LOG_DEBUG(buf, "Got key frame (from V4L2)!"); - } else if (buf->used >= 5 && (data[4] & 0x1F) == 0x07) { - had_key_frame = true; - E_LOG_DEBUG(buf, "Got key frame (from buffer)!"); - } - - if (had_key_frame) { - if (!fwrite(buf->start, buf->used, 1, stream)) { - goto error; - } - } else if (!requested_key_frame) { - device_video_force_key(buf->buf_list->device); - requested_key_frame = true; - } - buffer_consumed(buf, "h264-stream"); - buf = NULL; + if (buf->v4l2_buffer.flags & V4L2_BUF_FLAG_KEYFRAME) { + status->had_key_frame = true; + E_LOG_DEBUG(buf, "Got key frame (from V4L2)!"); + } else if (buf->used >= 5 && (data[4] & 0x1F) == 0x07) { + status->had_key_frame = true; + E_LOG_DEBUG(buf, "Got key frame (from buffer)!"); } -error: - buffer_consumed(buf, "error"); - buffer_lock_use(&http_h264, -1); + if (!status->had_key_frame) { + if (!status->requested_key_frame) { + device_video_force_key(buf->buf_list->device); + status->requested_key_frame = true; + } + return 0; + } + + if (!status->wrote_header) { + fprintf(status->stream, VIDEO_HEADER); + } + if (!fwrite(buf->start, buf->used, 1, status->stream)) { + return -1; + } + fflush(status->stream); + return 1; +} + +void http_video(http_worker_t *worker, FILE *stream) +{ + http_video_status_t status = { stream }; + + int n = buffer_lock_write_loop(&http_h264, 0, (buffer_write_fn)http_video_buf_part, &status); + + if (status.wrote_header) { + return; + } + + http_500_header(stream); + + if (n == 0) { + fprintf(stream, "No frames.\n"); + } else if (n < 0) { + fprintf(stream, "Interrupted. Received %d frames", -n); + } } diff --git a/http/http_jpeg.c b/http/http_jpeg.c index 4d75ae5..8950281 100644 --- a/http/http_jpeg.c +++ b/http/http_jpeg.c @@ -39,58 +39,52 @@ void http_jpeg_capture(buffer_t *buf) buffer_lock_capture(&http_jpeg, buf); } -void http_snapshot(http_worker_t *worker, FILE *stream) +int http_snapshot_buf_part(buffer_lock_t *buf_lock, buffer_t *buf, int frame, FILE *stream) { - int counter = 0; - buffer_lock_use(&http_jpeg, 1); - buffer_t *buf = buffer_lock_get(&http_jpeg, 0, &counter); - - if (!buf) { - http_404_header(worker, stream); - fprintf(stream, "No snapshot captured yet.\r\n"); - goto error; - } - fprintf(stream, "HTTP/1.1 200 OK\r\n"); fprintf(stream, "Content-Type: image/jpeg\r\n"); fprintf(stream, "Content-Length: %d\r\n", buf->used); fprintf(stream, "\r\n"); fwrite(buf->start, buf->used, 1, stream); - buffer_consumed(buf, "jpeg-snapshot"); -error: - buffer_lock_use(&http_jpeg, -1); + return 1; +} + +void http_snapshot(http_worker_t *worker, FILE *stream) +{ + int n = buffer_lock_write_loop(&http_jpeg, 1, (buffer_write_fn)http_snapshot_buf_part, stream); + + if (n <= 0) { + http_500_header(stream); + fprintf(stream, "No snapshot captured yet.\r\n"); + } +} + +int http_stream_buf_part(buffer_lock_t *buf_lock, buffer_t *buf, int frame, FILE *stream) +{ + if (!frame && !fprintf(stream, STREAM_HEADER)) { + return -1; + } + if (!fprintf(stream, STREAM_PART, buf->used)) { + return -1; + } + if (!fwrite(buf->start, buf->used, 1, stream)) { + return -1; + } + if (!fprintf(stream, STREAM_BOUNDARY)) { + return -1; + } + + return 1; } void http_stream(http_worker_t *worker, FILE *stream) { - int counter = 0; - buffer_t *buf = NULL; - fprintf(stream, STREAM_HEADER); - buffer_lock_use(&http_jpeg, 1); + int n = buffer_lock_write_loop(&http_jpeg, 0, (buffer_write_fn)http_stream_buf_part, stream); - while (!feof(stream)) { - buf = buffer_lock_get(&http_jpeg, 0, &counter); - - if (!buf) { - fprintf(stream, STREAM_ERROR, -1, "No frames."); - goto error; - } - - if (!fprintf(stream, STREAM_PART, buf->used)) { - goto error; - } - if (!fwrite(buf->start, buf->used, 1, stream)) { - goto error; - } - if (!fprintf(stream, STREAM_BOUNDARY)) { - goto error; - } - - buffer_consumed(buf, "jpeg-stream"); - buf = NULL; + if (n == 0) { + http_500_header(stream); + fprintf(stream, "No frames.\n"); + } else if (n < 0) { + fprintf(stream, "Interrupted. Received %d frames", -n); } - -error: - buffer_consumed(buf, "error"); - buffer_lock_use(&http_jpeg, -1); } diff --git a/http/http_methods.c b/http/http_methods.c index dc207cc..7021ca7 100644 --- a/http/http_methods.c +++ b/http/http_methods.c @@ -12,16 +12,29 @@ void http_index(http_worker_t *worker, FILE *stream) fflush(stream); } -void http_404_header(http_worker_t *worker, FILE *stream) +void http_custom_header(FILE *stream, const char *status) { + fprintf(stream, "HTTP/1.1 %s\r\n", status); + fprintf(stream, "Content-Type: text/plain\r\n"); + fprintf(stream, "\r\n"); +} + +void http_404_header(FILE *stream) +{ + http_custom_header(stream, "404 Not Found"); fprintf(stream, "HTTP/1.1 404 Not Found\r\n"); fprintf(stream, "Content-Type: text/plain\r\n"); fprintf(stream, "\r\n"); } +void http_500_header(FILE *stream) +{ + http_custom_header(stream, "500 Server Error"); +} + void http_404(http_worker_t *worker, FILE *stream) { - http_404_header(worker, stream); + http_404_header(stream); fprintf(stream, "Nothing here?\r\n"); } diff --git a/hw/buffer_lock.c b/hw/buffer_lock.c index 7533fec..0b4bffa 100644 --- a/hw/buffer_lock.c +++ b/hw/buffer_lock.c @@ -77,3 +77,35 @@ ret: pthread_mutex_unlock(&buf_lock->lock); return buf; } + +int buffer_lock_write_loop(buffer_lock_t *buf_lock, int nframes, buffer_write_fn fn, void *data) +{ + int counter = 0; + int frames = 0; + + buffer_lock_use(buf_lock, 1); + + while (nframes == 0 || frames < nframes) { + buffer_t *buf = buffer_lock_get(buf_lock, 0, &counter); + if (!buf) { + goto error; + } + + int ret = fn(buf_lock, buf, frames, data); + buffer_consumed(buf, "write-loop"); + + if (ret > 0) { + frames++; + } else if (ret < 0) { + goto error; + } + } + +ok: + buffer_lock_use(buf_lock, -1); + return frames; + +error: + buffer_lock_use(buf_lock, -1); + return -frames; +} diff --git a/hw/buffer_lock.h b/hw/buffer_lock.h index abc11a1..3c1b2c0 100644 --- a/hw/buffer_lock.h +++ b/hw/buffer_lock.h @@ -25,8 +25,11 @@ typedef struct buffer_lock_s { .timeout_us = (_timeout_ms > DEFAULT_BUFFER_LOCK_TIMEOUT ? _timeout_ms : DEFAULT_BUFFER_LOCK_TIMEOUT) * 1000LL, \ }; +typedef int (*buffer_write_fn)(buffer_lock_t *buf_lock, buffer_t *buf, int frame, void *data); + void buffer_lock_capture(buffer_lock_t *buf_lock, buffer_t *buf); buffer_t *buffer_lock_get(buffer_lock_t *buf_lock, int timeout_ms, int *counter); bool buffer_lock_needs_buffer(buffer_lock_t *buf_lock); void buffer_lock_use(buffer_lock_t *buf_lock, int ref); bool buffer_lock_is_used(buffer_lock_t *buf_lock); +int buffer_lock_write_loop(buffer_lock_t *buf_lock, int nframes, buffer_write_fn fn, void *data);