diff --git a/src/device.c b/src/device.c index bfae0d0..d527b78 100644 --- a/src/device.c +++ b/src/device.c @@ -133,28 +133,6 @@ v4l2_std_id device_parse_standard(const char *str) { return STANDARD_UNKNOWN; } -void device_copy_picture(const struct picture_t *src, struct picture_t *dest) { -# define COPY(_field) dest->_field = src->_field - - if (dest->allocated < src->allocated) { - A_REALLOC(dest->data, src->allocated); - COPY(allocated); - } - - memcpy(dest->data, src->data, src->used); - - COPY(used); - - COPY(width); - COPY(height); - - COPY(grab_time); - COPY(encode_begin_time); - COPY(encode_end_time); - -# undef COPY -} - int device_open(struct device_t *dev) { if ((dev->run->fd = open(dev->path, O_RDWR|O_NONBLOCK)) < 0) { LOG_PERROR("Can't open device"); @@ -196,9 +174,8 @@ void device_close(struct device_t *dev) { if (dev->run->pictures) { LOG_DEBUG("Releasing picture buffers ..."); - for (unsigned index = 0; index < dev->run->n_buffers && dev->run->pictures[index].data; ++index) { - free(dev->run->pictures[index].data); - dev->run->pictures[index].data = NULL; + for (unsigned index = 0; index < dev->run->n_buffers; ++index) { + picture_destroy(dev->run->pictures[index]); } free(dev->run->pictures); dev->run->pictures = NULL; @@ -305,7 +282,7 @@ int device_grab_buffer(struct device_t *dev) { dev->run->hw_buffers[buf_info.index].used = buf_info.bytesused; memcpy(&dev->run->hw_buffers[buf_info.index].buf_info, &buf_info, sizeof(struct v4l2_buffer)); - dev->run->pictures[buf_info.index].grab_time = get_now_monotonic(); + dev->run->pictures[buf_info.index]->grab_time = get_now_monotonic(); return buf_info.index; } @@ -600,14 +577,15 @@ static int _device_open_queue_buffers(struct device_t *dev) { } static void _device_open_alloc_picbufs(struct device_t *dev) { + size_t picture_size = picture_get_generous_size(dev->run->width, dev->run->height); + LOG_DEBUG("Allocating picture buffers ..."); A_CALLOC(dev->run->pictures, dev->run->n_buffers); - dev->run->max_raw_image_size = ((dev->run->width * dev->run->height) << 1) * 2; for (unsigned index = 0; index < dev->run->n_buffers; ++index) { - LOG_DEBUG("Allocating picture buffer %u sized %zu bytes... ", index, dev->run->max_raw_image_size); - A_CALLOC(dev->run->pictures[index].data, dev->run->max_raw_image_size); - dev->run->pictures[index].allocated = dev->run->max_raw_image_size; + dev->run->pictures[index] = picture_init(); + LOG_DEBUG("Pre-allocating picture buffer %u sized %zu bytes... ", index, picture_size); + picture_realloc_data(dev->run->pictures[index], picture_size); } } diff --git a/src/device.h b/src/device.h index 17289b8..6a6b2db 100644 --- a/src/device.h +++ b/src/device.h @@ -27,6 +27,8 @@ #include +#include "picture.h" + #define VIDEO_MIN_WIDTH 160 #define VIDEO_MAX_WIDTH 10240 @@ -50,17 +52,6 @@ struct hw_buffer_t { struct v4l2_buffer buf_info; }; -struct picture_t { - unsigned char *data; - size_t used; - size_t allocated; - unsigned width; - unsigned height; - long double grab_time; - long double encode_begin_time; - long double encode_end_time; -}; - struct device_runtime_t { int fd; unsigned width; @@ -69,8 +60,7 @@ struct device_runtime_t { unsigned n_buffers; unsigned n_workers; struct hw_buffer_t *hw_buffers; - struct picture_t *pictures; - size_t max_raw_image_size; + struct picture_t **pictures; bool capturing; }; @@ -120,8 +110,6 @@ void device_destroy(struct device_t *dev); int device_parse_format(const char *str); v4l2_std_id device_parse_standard(const char *str); -void device_copy_picture(const struct picture_t *src, struct picture_t *dest); - int device_open(struct device_t *dev); void device_close(struct device_t *dev); diff --git a/src/encoder.c b/src/encoder.c index 9e3af8a..0a57200 100644 --- a/src/encoder.c +++ b/src/encoder.c @@ -200,7 +200,7 @@ int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, uns assert(encoder->run->type != ENCODER_TYPE_UNKNOWN); - dev->run->pictures[buf_index].encode_begin_time = get_now_monotonic(); + dev->run->pictures[buf_index]->encode_begin_time = get_now_monotonic(); if (encoder->run->type == ENCODER_TYPE_CPU) { cpu_encoder_compress_buffer(dev, buf_index, encoder->run->quality); @@ -215,10 +215,10 @@ int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, uns } # endif - dev->run->pictures[buf_index].encode_end_time = get_now_monotonic(); + dev->run->pictures[buf_index]->encode_end_time = get_now_monotonic(); - dev->run->pictures[buf_index].width = dev->run->width; - dev->run->pictures[buf_index].height = dev->run->height; + dev->run->pictures[buf_index]->width = dev->run->width; + dev->run->pictures[buf_index]->height = dev->run->height; return 0; diff --git a/src/encoders/cpu/encoder.c b/src/encoders/cpu/encoder.c index 8987210..7902488 100644 --- a/src/encoders/cpu/encoder.c +++ b/src/encoders/cpu/encoder.c @@ -36,6 +36,7 @@ #include #include "../../tools.h" +#include "../../picture.h" #include "../../device.h" @@ -43,7 +44,6 @@ struct _jpeg_dest_manager_t { struct jpeg_destination_mgr mgr; // Default manager JOCTET *buffer; // Start of buffer struct picture_t *picture; - unsigned char *picture_data_cursor; }; @@ -79,7 +79,7 @@ void cpu_encoder_compress_buffer(struct device_t *dev, unsigned index, unsigned jpeg.err = jpeg_std_error(&jpeg_error); jpeg_create_compress(&jpeg); - _jpeg_set_picture(&jpeg, &dev->run->pictures[index]); + _jpeg_set_picture(&jpeg, dev->run->pictures[index]); jpeg.image_width = dev->run->width; jpeg.image_height = dev->run->height; @@ -108,7 +108,7 @@ void cpu_encoder_compress_buffer(struct device_t *dev, unsigned index, unsigned jpeg_finish_compress(&jpeg); jpeg_destroy_compress(&jpeg); - assert(dev->run->pictures[index].used > 0); + assert(dev->run->pictures[index]->used > 0); } static void _jpeg_set_picture(j_compress_ptr jpeg, struct picture_t *picture) { @@ -125,7 +125,6 @@ static void _jpeg_set_picture(j_compress_ptr jpeg, struct picture_t *picture) { dest->mgr.empty_output_buffer = _jpeg_empty_output_buffer; dest->mgr.term_destination = _jpeg_term_destination; dest->picture = picture; - dest->picture_data_cursor = picture->data; picture->used = 0; } @@ -277,13 +276,8 @@ static boolean _jpeg_empty_output_buffer(j_compress_ptr jpeg) { // Called whenever local jpeg buffer fills up struct _jpeg_dest_manager_t *dest = (struct _jpeg_dest_manager_t *)jpeg->dest; - size_t new_used = dest->picture->used + JPEG_OUTPUT_BUFFER_SIZE; - assert(new_used <= dest->picture->allocated); - - memcpy(dest->picture_data_cursor, dest->buffer, JPEG_OUTPUT_BUFFER_SIZE); - dest->picture_data_cursor += JPEG_OUTPUT_BUFFER_SIZE; - dest->picture->used = new_used; + picture_append_data(dest->picture, dest->buffer, JPEG_OUTPUT_BUFFER_SIZE); dest->mgr.next_output_byte = dest->buffer; dest->mgr.free_in_buffer = JPEG_OUTPUT_BUFFER_SIZE; @@ -293,18 +287,13 @@ static boolean _jpeg_empty_output_buffer(j_compress_ptr jpeg) { static void _jpeg_term_destination(j_compress_ptr jpeg) { // Called by jpeg_finish_compress after all data has been written. - // Usually needs to flush buffer + // Usually needs to flush buffer. struct _jpeg_dest_manager_t *dest = (struct _jpeg_dest_manager_t *)jpeg->dest; size_t final = JPEG_OUTPUT_BUFFER_SIZE - dest->mgr.free_in_buffer; - size_t new_used = dest->picture->used + final; - assert(new_used <= dest->picture->allocated); - - // Write any data remaining in the buffer - memcpy(dest->picture_data_cursor, dest->buffer, final); - dest->picture_data_cursor += final; - dest->picture->used = new_used; + // Write any data remaining in the buffer. + picture_append_data(dest->picture, dest->buffer, final); } #undef JPEG_OUTPUT_BUFFER_SIZE diff --git a/src/encoders/hw/encoder.c b/src/encoders/hw/encoder.c index 57310c5..b22b334 100644 --- a/src/encoders/hw/encoder.c +++ b/src/encoders/hw/encoder.c @@ -36,13 +36,14 @@ #include "../../tools.h" #include "../../logging.h" #include "../../xioctl.h" +#include "../../picture.h" #include "../../device.h" #include "huffman.h" +void _copy_plus_huffman(const struct hw_buffer_t *src, struct picture_t *dest); static bool _is_huffman(const unsigned char *data); -static size_t _memcpy_with_huffman(unsigned char *dest, const unsigned char *src, size_t size); int hw_encoder_prepare(struct device_t *dev, unsigned quality) { @@ -66,15 +67,30 @@ void hw_encoder_compress_buffer(struct device_t *dev, unsigned index) { if (dev->run->format != V4L2_PIX_FMT_MJPEG && dev->run->format != V4L2_PIX_FMT_JPEG) { assert(0 && "Unsupported input format for HW encoder"); } + _copy_plus_huffman(&dev->run->hw_buffers[index], dev->run->pictures[index]); +} -# define PICTURE(_next) dev->run->pictures[index]._next -# define HW_BUFFER(_next) dev->run->hw_buffers[index]._next +void _copy_plus_huffman(const struct hw_buffer_t *src, struct picture_t *dest) { + if (!_is_huffman(src->data)) { + const unsigned char *src_ptr = src->data; + const unsigned char *src_end = src->data + src->used; + size_t paste; - assert(PICTURE(allocated) >= HW_BUFFER(used) + sizeof(HUFFMAN_TABLE)); - PICTURE(used) = _memcpy_with_huffman(PICTURE(data), HW_BUFFER(data), HW_BUFFER(used)); + while ((((src_ptr[0] << 8) | src_ptr[1]) != 0xFFC0) && (src_ptr < src_end)) { + src_ptr += 1; + } + if (src_ptr >= src_end) { + dest->used = 0; // Error + return; + } + paste = src_ptr - src->data; -# undef HW_BUFFER -# undef PICTURE + picture_set_data(dest, src->data, paste); + picture_append_data(dest, HUFFMAN_TABLE, sizeof(HUFFMAN_TABLE)); + picture_append_data(dest, src_ptr, src->used - paste); + } else { + picture_set_data(dest, src->data, src->used); + } } static bool _is_huffman(const unsigned char *data) { @@ -91,27 +107,3 @@ static bool _is_huffman(const unsigned char *data) { } return false; } - -static size_t _memcpy_with_huffman(unsigned char *dest, const unsigned char *src, size_t size) { - if (!_is_huffman(src)) { - const unsigned char *src_ptr = src; - const unsigned char *src_end = src + size; - size_t paste; - - while ((((src_ptr[0] << 8) | src_ptr[1]) != 0xFFC0) && (src_ptr < src_end)) { - src_ptr += 1; - } - if (src_ptr >= src_end) { - return 0; - } - paste = src_ptr - src; - - memcpy(dest, src, paste); - memcpy(dest + paste, HUFFMAN_TABLE, sizeof(HUFFMAN_TABLE)); - memcpy(dest + paste + sizeof(HUFFMAN_TABLE), src_ptr, size - paste); - return (size + sizeof(HUFFMAN_TABLE)); - } else { - memcpy(dest, src, size); - return size; - } -} diff --git a/src/encoders/omx/encoder.c b/src/encoders/omx/encoder.c index f1b7d82..7485519 100644 --- a/src/encoders/omx/encoder.c +++ b/src/encoders/omx/encoder.c @@ -37,6 +37,7 @@ #include "../../logging.h" #include "../../tools.h" +#include "../../picture.h" #include "../../device.h" #include "formatters.h" @@ -174,7 +175,6 @@ int omx_encoder_prepare(struct omx_encoder_t *omx, struct device_t *dev, unsigne } int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, unsigned index) { -# define PICTURE(_next) dev->run->pictures[index]._next # define HW_BUFFER(_next) dev->run->hw_buffers[index]._next # define IN(_next) omx->input_buffer->_next # define OUT(_next) omx->output_buffer->_next @@ -188,7 +188,7 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, return -1; } - PICTURE(used) = 0; + dev->run->pictures[index]->used = 0; omx->output_available = false; omx->input_required = true; @@ -200,9 +200,7 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, if (omx->output_available) { omx->output_available = false; - assert(PICTURE(used) + OUT(nFilledLen) <= PICTURE(allocated)); - memcpy(PICTURE(data) + PICTURE(used), OUT(pBuffer) + OUT(nOffset), OUT(nFilledLen)); - PICTURE(used) += OUT(nFilledLen); + picture_append_data(dev->run->pictures[index], OUT(pBuffer) + OUT(nOffset), OUT(nFilledLen)); if (OUT(nFlags) & OMX_BUFFERFLAG_ENDOFFRAME) { OUT(nFlags) = 0; @@ -244,7 +242,6 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, # undef OUT # undef IN # undef HW_BUFFER -# undef PICTURE return 0; } @@ -313,7 +310,7 @@ static int _omx_setup_input(struct omx_encoder_t *omx, struct device_t *dev) { # undef ALIGN_HEIGHT portdef.format.image.bFlagErrorConcealment = OMX_FALSE; portdef.format.image.eCompressionFormat = OMX_IMAGE_CodingUnused; - portdef.nBufferSize = dev->run->max_raw_image_size; + portdef.nBufferSize = picture_get_generous_size(dev->run->width, dev->run->height); # define MAP_FORMAT(_v4l2_format, _omx_format) \ case _v4l2_format: { portdef.format.image.eColorFormat = _omx_format; break; } diff --git a/src/http/blank.c b/src/http/blank.c index 6db3f75..0bc223a 100644 --- a/src/http/blank.c +++ b/src/http/blank.c @@ -31,6 +31,7 @@ #include "../tools.h" #include "../logging.h" +#include "../picture.h" #include "../device.h" #include "data/blank_jpeg.h" @@ -42,55 +43,44 @@ struct _jpeg_error_manager_t { }; -static struct picture_t *_blank_init_internal(void); -static struct picture_t *_blank_init_external(const char *path); +static struct picture_t *_init_internal(void); +static struct picture_t *_init_external(const char *path); + static int _jpeg_read_geometry(FILE *fp, unsigned *width, unsigned *height); static void _jpeg_error_handler(j_common_ptr jpeg); -struct picture_t *blank_init(const char *path) { +struct picture_t *blank_picture_init(const char *path) { struct picture_t *blank = NULL; if (path) { - blank = _blank_init_external(path); + blank = _init_external(path); } if (blank) { LOG_INFO("Using external blank placeholder: %s", path); } else { - blank = _blank_init_internal(); + blank = _init_internal(); LOG_INFO("Using internal blank placeholder"); } return blank; } -void blank_destroy(struct picture_t *blank) { - free(blank->data); - free(blank); -} - -static struct picture_t *_blank_init_internal(void) { +static struct picture_t *_init_internal(void) { struct picture_t *blank; - A_CALLOC(blank, 1); - - A_CALLOC(blank->data, ARRAY_LEN(BLANK_JPEG_DATA)); - memcpy(blank->data, BLANK_JPEG_DATA, ARRAY_LEN(BLANK_JPEG_DATA)); - - blank->used = ARRAY_LEN(BLANK_JPEG_DATA); - blank->allocated = ARRAY_LEN(BLANK_JPEG_DATA); - + blank = picture_init(); + picture_set_data(blank, BLANK_JPEG_DATA, ARRAY_LEN(BLANK_JPEG_DATA)); blank->width = BLANK_JPEG_WIDTH; blank->height = BLANK_JPEG_HEIGHT; - return blank; } -static struct picture_t *_blank_init_external(const char *path) { +static struct picture_t *_init_external(const char *path) { FILE *fp = NULL; struct picture_t *blank; - A_CALLOC(blank, 1); + blank = picture_init(); if ((fp = fopen(path, "rb")) == NULL) { LOG_PERROR("Can't open blank placeholder '%s'", path); @@ -109,8 +99,7 @@ static struct picture_t *_blank_init_external(const char *path) { # define CHUNK_SIZE (100 * 1024) while (true) { if (blank->used + CHUNK_SIZE >= blank->allocated) { - blank->allocated = blank->used + CHUNK_SIZE * 2; - A_REALLOC(blank->data, blank->allocated); + picture_realloc_data(blank, blank->used + CHUNK_SIZE * 2); } size_t readed = fread(blank->data + blank->used, 1, CHUNK_SIZE, fp); @@ -128,8 +117,7 @@ static struct picture_t *_blank_init_external(const char *path) { # undef CHUNK_SIZE error: - free(blank->data); - free(blank); + picture_destroy(blank); blank = NULL; ok: diff --git a/src/http/blank.h b/src/http/blank.h index 71fd4c6..8ff5895 100644 --- a/src/http/blank.h +++ b/src/http/blank.h @@ -24,8 +24,7 @@ #include -#include "../device.h" +#include "../picture.h" -struct picture_t *blank_init(const char *path); -void blank_destroy(struct picture_t *blank); +struct picture_t *blank_picture_init(const char *path); diff --git a/src/http/server.c b/src/http/server.c index cd57b24..d3e14e5 100644 --- a/src/http/server.c +++ b/src/http/server.c @@ -51,6 +51,7 @@ #include "../tools.h" #include "../logging.h" +#include "../picture.h" #include "../encoder.h" #include "../stream.h" #ifdef WITH_GPIO @@ -91,6 +92,7 @@ struct http_server_t *http_server_init(struct stream_t *stream) { struct exposed_t *exposed; A_CALLOC(exposed, 1); + exposed->picture = picture_init(); A_CALLOC(run, 1); run->stream = stream; @@ -143,10 +145,10 @@ void http_server_destroy(struct http_server_t *server) { } if (server->run->blank) { - blank_destroy(server->run->blank); + picture_destroy(server->run->blank); } - free(server->run->exposed->picture.data); + picture_destroy(server->run->exposed->picture); free(server->run->exposed); free(server->run); free(server); @@ -165,7 +167,7 @@ int http_server_listen(struct http_server_t *server) { } server->run->drop_same_frames_blank = max_u(server->drop_same_frames, server->run->drop_same_frames_blank); - server->run->blank = blank_init(server->blank_path); + server->run->blank = blank_picture_init(server->blank_path); _expose_blank_picture(server); { @@ -436,8 +438,8 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server) " \"stream\": {\"queued_fps\": %u, \"clients\": %u, \"clients_stat\": {", encoder_type_to_string(encoder_run_type), encoder_run_quality, - (server->fake_width ? server->fake_width : server->run->exposed->picture.width), - (server->fake_height ? server->fake_height : server->run->exposed->picture.height), + (server->fake_width ? server->fake_width : server->run->exposed->picture->width), + (server->fake_height ? server->fake_height : server->run->exposed->picture->height), bool_to_string(server->run->exposed->online), server->run->stream->dev->desired_fps, server->run->exposed->captured_fps, @@ -476,7 +478,7 @@ static void _http_callback_snapshot(struct evhttp_request *request, void *v_serv # define EXPOSED(_next) server->run->exposed->_next assert((buf = evbuffer_new())); - assert(!evbuffer_add(buf, (const void *)EXPOSED(picture.data), EXPOSED(picture.used))); + assert(!evbuffer_add(buf, (const void *)EXPOSED(picture->data), EXPOSED(picture->used))); ADD_HEADER("Access-Control-Allow-Origin:", "*"); ADD_HEADER("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate, pre-check=0, post-check=0, max-age=0"); @@ -497,11 +499,11 @@ static void _http_callback_snapshot(struct evhttp_request *request, void *v_serv ADD_HEADER("X-UStreamer-Online", bool_to_string(EXPOSED(online))); ADD_UNSIGNED_HEADER("X-UStreamer-Dropped", EXPOSED(dropped)); - ADD_UNSIGNED_HEADER("X-UStreamer-Width", EXPOSED(picture.width)); - ADD_UNSIGNED_HEADER("X-UStreamer-Height", EXPOSED(picture.height)); - ADD_TIME_HEADER("X-UStreamer-Grab-Time", EXPOSED(picture.grab_time)); - ADD_TIME_HEADER("X-UStreamer-Encode-Begin-Time", EXPOSED(picture.encode_begin_time)); - ADD_TIME_HEADER("X-UStreamer-Encode-End-Time", EXPOSED(picture.encode_end_time)); + ADD_UNSIGNED_HEADER("X-UStreamer-Width", EXPOSED(picture->width)); + ADD_UNSIGNED_HEADER("X-UStreamer-Height", EXPOSED(picture->height)); + ADD_TIME_HEADER("X-UStreamer-Grab-Time", EXPOSED(picture->grab_time)); + ADD_TIME_HEADER("X-UStreamer-Encode-Begin-Time", EXPOSED(picture->encode_begin_time)); + ADD_TIME_HEADER("X-UStreamer-Encode-End-Time", EXPOSED(picture->encode_end_time)); ADD_TIME_HEADER("X-UStreamer-Expose-Begin-Time", EXPOSED(expose_begin_time)); ADD_TIME_HEADER("X-UStreamer-Expose-Cmp-Time", EXPOSED(expose_cmp_time)); ADD_TIME_HEADER("X-UStreamer-Expose-End-Time", EXPOSED(expose_end_time)); @@ -663,7 +665,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c "Content-Length: %zu" RN "X-Timestamp: %.06Lf" RN "%s", - EXPOSED(picture.used), + EXPOSED(picture->used), get_now_real(), (client->extra_headers ? "" : RN) )); @@ -684,12 +686,12 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c RN, bool_to_string(EXPOSED(online)), EXPOSED(dropped), - EXPOSED(picture.width), - EXPOSED(picture.height), + EXPOSED(picture->width), + EXPOSED(picture->height), client->fps, - EXPOSED(picture.grab_time), - EXPOSED(picture.encode_begin_time), - EXPOSED(picture.encode_end_time), + EXPOSED(picture->grab_time), + EXPOSED(picture->encode_begin_time), + EXPOSED(picture->encode_end_time), EXPOSED(expose_begin_time), EXPOSED(expose_cmp_time), EXPOSED(expose_end_time), @@ -698,7 +700,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c } } - assert(!evbuffer_add(buf, (void *)EXPOSED(picture.data), EXPOSED(picture.used))); + assert(!evbuffer_add(buf, (void *)EXPOSED(picture->data), EXPOSED(picture->used))); assert(evbuffer_add_printf(buf, RN "--" BOUNDARY RN)); if (client->advance_headers) { @@ -853,8 +855,8 @@ static bool _expose_new_picture_unsafe(struct http_server_t *server) { if ( EXPOSED(online) && EXPOSED(dropped) < server->drop_same_frames - && EXPOSED(picture.used) == STREAM(picture.used) - && !memcmp(EXPOSED(picture.data), STREAM(picture.data), STREAM(picture.used)) + && EXPOSED(picture->used) == STREAM(picture->used) + && !memcmp(EXPOSED(picture->data), STREAM(picture->data), STREAM(picture->used)) ) { EXPOSED(expose_cmp_time) = get_now_monotonic(); EXPOSED(expose_end_time) = EXPOSED(expose_cmp_time); @@ -869,7 +871,7 @@ static bool _expose_new_picture_unsafe(struct http_server_t *server) { } } - device_copy_picture(&STREAM(picture), &EXPOSED(picture)); + picture_copy(STREAM(picture), EXPOSED(picture)); EXPOSED(online) = true; EXPOSED(dropped) = 0; @@ -890,9 +892,8 @@ static bool _expose_blank_picture(struct http_server_t *server) { EXPOSED(expose_begin_time) = get_now_monotonic(); EXPOSED(expose_cmp_time) = EXPOSED(expose_begin_time); - if (EXPOSED(online) || EXPOSED(picture.used) == 0) { - device_copy_picture(server->run->blank, &EXPOSED(picture)); - + if (EXPOSED(online) || EXPOSED(picture->used) == 0) { + picture_copy(server->run->blank, EXPOSED(picture)); EXPOSED(captured_fps) = 0; EXPOSED(online) = false; goto updated; diff --git a/src/http/server.h b/src/http/server.h index 0147a56..4ba71f1 100644 --- a/src/http/server.h +++ b/src/http/server.h @@ -31,6 +31,7 @@ #include #include "../tools.h" +#include "../picture.h" #include "../stream.h" #include "blank.h" @@ -58,7 +59,7 @@ struct stream_client_t { }; struct exposed_t { - struct picture_t picture; + struct picture_t *picture; unsigned captured_fps; unsigned queued_fps; bool online; diff --git a/src/picture.c b/src/picture.c new file mode 100644 index 0000000..ab8130a --- /dev/null +++ b/src/picture.c @@ -0,0 +1,91 @@ +/***************************************************************************** +# # +# uStreamer - Lightweight and fast MJPG-HTTP streamer. # +# # +# Copyright (C) 2018 Maxim Devaev # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +*****************************************************************************/ + + +#include "picture.h" + +#include +#include +#include + +#include "tools.h" +#include "logging.h" + + +struct picture_t *picture_init(void) { + struct picture_t *picture; + + A_CALLOC(picture, 1); + return picture; +} + +void picture_destroy(struct picture_t *picture) { + if (picture->data) { + free(picture->data); + } + free(picture); +} + +size_t picture_get_generous_size(unsigned width, unsigned height) { + return ((width * height) << 1) * 2; +} + +void picture_realloc_data(struct picture_t *picture, size_t size) { + if (picture->allocated < size) { + LOG_DEBUG("Increasing picture 0x%p buffer: %zu -> %zu (+%zu)", + picture, picture->allocated, size, size - picture->allocated); + A_REALLOC(picture->data, size); + picture->allocated = size; + } +} + +void picture_set_data(struct picture_t *picture, const unsigned char *data, size_t size) { + picture_realloc_data(picture, size); + memcpy(picture->data, data, size); + picture->used = size; +} + +void picture_append_data(struct picture_t *picture, const unsigned char *data, size_t size) { + size_t new_used = picture->used + size; + + picture_realloc_data(picture, new_used); + memcpy(picture->data + picture->used, data, size); + picture->used = new_used; +} + +void picture_copy(const struct picture_t *src, struct picture_t *dest) { + assert(src->allocated); + + picture_set_data(dest, src->data, src->allocated); + +# define COPY(_field) dest->_field = src->_field + + COPY(used); + + COPY(width); + COPY(height); + + COPY(grab_time); + COPY(encode_begin_time); + COPY(encode_end_time); + +# undef COPY +} diff --git a/src/picture.h b/src/picture.h new file mode 100644 index 0000000..2627725 --- /dev/null +++ b/src/picture.h @@ -0,0 +1,49 @@ +/***************************************************************************** +# # +# uStreamer - Lightweight and fast MJPG-HTTP streamer. # +# # +# Copyright (C) 2018 Maxim Devaev # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +*****************************************************************************/ + + +#pragma once + +#include + + +struct picture_t { + unsigned char *data; + size_t used; + size_t allocated; + unsigned width; + unsigned height; + long double grab_time; + long double encode_begin_time; + long double encode_end_time; +}; + + +struct picture_t *picture_init(void); +void picture_destroy(struct picture_t *picture); + +size_t picture_get_generous_size(unsigned width, unsigned height); + +void picture_realloc_data(struct picture_t *picture, size_t size); +void picture_set_data(struct picture_t *picture, const unsigned char *data, size_t size); +void picture_append_data(struct picture_t *picture, const unsigned char *data, size_t size); + +void picture_copy(const struct picture_t *src, struct picture_t *dest); diff --git a/src/stream.c b/src/stream.c index 04261cf..7e2ce3c 100644 --- a/src/stream.c +++ b/src/stream.c @@ -36,6 +36,7 @@ #include "tools.h" #include "logging.h" #include "xioctl.h" +#include "picture.h" #include "device.h" #include "encoder.h" #ifdef WITH_GPIO @@ -111,6 +112,7 @@ struct stream_t *stream_init(struct device_t *dev, struct encoder_t *encoder) { atomic_init(&proc->slowdown, false); A_CALLOC(stream, 1); + stream->picture = picture_init(); stream->dev = dev; stream->encoder = encoder; atomic_init(&stream->updated, false); @@ -121,9 +123,7 @@ struct stream_t *stream_init(struct device_t *dev, struct encoder_t *encoder) { void stream_destroy(struct stream_t *stream) { A_MUTEX_DESTROY(&stream->mutex); - if (stream->picture.data) { - free(stream->picture.data); - } + picture_destroy(stream->picture); free(stream->proc); free(stream); } @@ -144,6 +144,9 @@ void stream_loop(struct stream_t *stream) { LOG_INFO("Capturing ..."); + LOG_DEBUG("Pre-allocating memory for stream picture ..."); + picture_realloc_data(stream->picture, picture_get_generous_size(stream->dev->run->width, stream->dev->run->height)); + while (!atomic_load(&stream->proc->stop)) { struct _worker_t *ready_worker; @@ -335,19 +338,15 @@ static struct _workers_pool_t *_stream_init(struct stream_t *stream) { } static void _stream_expose_picture(struct stream_t *stream, unsigned buf_index, unsigned captured_fps) { -# define PICTURE(_next) stream->dev->run->pictures[buf_index]._next - A_MUTEX_LOCK(&stream->mutex); - device_copy_picture(&stream->dev->run->pictures[buf_index], &stream->picture); + picture_copy(stream->dev->run->pictures[buf_index], stream->picture); stream->online = true; stream->captured_fps = captured_fps; atomic_store(&stream->updated, true); A_MUTEX_UNLOCK(&stream->mutex); - -# undef PICTURE } static struct _workers_pool_t *_workers_pool_init(struct stream_t *stream) { @@ -443,7 +442,7 @@ static void *_worker_thread(void *v_worker) { A_MUTEX_UNLOCK(&worker->has_job_mutex); if (!atomic_load(worker->workers_stop)) { -# define PICTURE(_next) worker->dev->run->pictures[worker->buf_index]._next +# define PICTURE(_next) worker->dev->run->pictures[worker->buf_index]->_next LOG_DEBUG("Worker %u compressing JPEG from buffer %u ...", worker->number, worker->buf_index); diff --git a/src/stream.h b/src/stream.h index 405db1f..5880ba2 100644 --- a/src/stream.h +++ b/src/stream.h @@ -24,6 +24,7 @@ #include +#include "picture.h" #include "device.h" #include "encoder.h" @@ -34,7 +35,7 @@ struct process_t { }; struct stream_t { - struct picture_t picture; + struct picture_t *picture; bool online; unsigned captured_fps; atomic_bool updated;