From f19ab11f76fa45aa7d1c151472031c3d8ad010bd Mon Sep 17 00:00:00 2001 From: Devaev Maxim Date: Wed, 9 Dec 2020 18:44:12 +0300 Subject: [PATCH] refactoring --- Makefile | 6 +- src/ustreamer/{http => }/blank.c | 6 +- src/ustreamer/{http => }/blank.h | 8 +- src/ustreamer/{http => }/data/blank.jpeg | Bin src/ustreamer/{http => }/data/blank_jpeg.c | 0 src/ustreamer/{http => }/data/blank_jpeg.h | 0 src/ustreamer/{http => }/data/index.html | 0 src/ustreamer/{http => }/data/index_html.c | 0 src/ustreamer/{http => }/data/index_html.h | 2 +- src/ustreamer/encoders/omx/encoder.c | 2 +- src/ustreamer/frame.c | 13 +- src/ustreamer/frame.h | 5 +- src/ustreamer/http/server.c | 132 ++++++--------------- src/ustreamer/http/server.h | 23 ++-- src/ustreamer/options.c | 27 +++-- src/ustreamer/options.h | 9 +- src/ustreamer/stream.c | 80 +++++++++---- src/ustreamer/stream.h | 5 + 18 files changed, 151 insertions(+), 167 deletions(-) rename src/ustreamer/{http => }/blank.c (97%) rename src/ustreamer/{http => }/blank.h (92%) rename src/ustreamer/{http => }/data/blank.jpeg (100%) rename src/ustreamer/{http => }/data/blank_jpeg.c (100%) rename src/ustreamer/{http => }/data/blank_jpeg.h (100%) rename src/ustreamer/{http => }/data/index.html (100%) rename src/ustreamer/{http => }/data/index_html.c (100%) rename src/ustreamer/{http => }/data/index_html.h (97%) diff --git a/Makefile b/Makefile index e0c12cb..e3c4347 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ _PROG_SRCS = $(shell ls \ src/common/*.c \ src/ustreamer/*.c \ src/ustreamer/http/*.c \ - src/ustreamer/http/data/*.c \ + src/ustreamer/data/*.c \ src/ustreamer/encoders/cpu/*.c \ src/ustreamer/encoders/hw/*.c \ ) @@ -90,8 +90,8 @@ uninstall: regen: - tools/make-jpeg-h.py src/ustreamer/http/data/blank.jpeg src/ustreamer/http/data/blank_jpeg.c BLANK - tools/make-html-h.py src/ustreamer/http/data/index.html src/ustreamer/http/data/index_html.c INDEX + tools/make-jpeg-h.py src/ustreamer/data/blank.jpeg src/ustreamer/data/blank_jpeg.c BLANK + tools/make-html-h.py src/ustreamer/data/index.html src/ustreamer/data/index_html.c INDEX $(PROG): $(_PROG_SRCS:%.c=$(BUILD)/%.o) diff --git a/src/ustreamer/http/blank.c b/src/ustreamer/blank.c similarity index 97% rename from src/ustreamer/http/blank.c rename to src/ustreamer/blank.c index 92c024f..3f47a97 100644 --- a/src/ustreamer/http/blank.c +++ b/src/ustreamer/blank.c @@ -36,7 +36,7 @@ static int _jpeg_read_geometry(FILE *fp, unsigned *width, unsigned *height); static void _jpeg_error_handler(j_common_ptr jpeg); -struct frame_t *blank_picture_init(const char *path) { +struct frame_t *blank_frame_init(const char *path) { struct frame_t *blank = NULL; if (path) { @@ -55,7 +55,7 @@ struct frame_t *blank_picture_init(const char *path) { static struct frame_t *_init_internal(void) { struct frame_t *blank; - blank = frame_init(); + blank = frame_init("blank_internal"); frame_set_data(blank, BLANK_JPEG_DATA, BLANK_JPEG_DATA_SIZE); blank->width = BLANK_JPEG_WIDTH; blank->height = BLANK_JPEG_HEIGHT; @@ -66,7 +66,7 @@ static struct frame_t *_init_external(const char *path) { FILE *fp = NULL; struct frame_t *blank; - blank = frame_init(); + blank = frame_init("blank_external"); if ((fp = fopen(path, "rb")) == NULL) { LOG_PERROR("Can't open blank placeholder '%s'", path); diff --git a/src/ustreamer/http/blank.h b/src/ustreamer/blank.h similarity index 92% rename from src/ustreamer/http/blank.h rename to src/ustreamer/blank.h index 204a50c..953500b 100644 --- a/src/ustreamer/http/blank.h +++ b/src/ustreamer/blank.h @@ -28,11 +28,11 @@ #include -#include "../../common/tools.h" -#include "../../common/logging.h" -#include "../frame.h" +#include "../common/tools.h" +#include "../common/logging.h" +#include "frame.h" #include "data/blank_jpeg.h" -struct frame_t *blank_picture_init(const char *path); +struct frame_t *blank_frame_init(const char *path); diff --git a/src/ustreamer/http/data/blank.jpeg b/src/ustreamer/data/blank.jpeg similarity index 100% rename from src/ustreamer/http/data/blank.jpeg rename to src/ustreamer/data/blank.jpeg diff --git a/src/ustreamer/http/data/blank_jpeg.c b/src/ustreamer/data/blank_jpeg.c similarity index 100% rename from src/ustreamer/http/data/blank_jpeg.c rename to src/ustreamer/data/blank_jpeg.c diff --git a/src/ustreamer/http/data/blank_jpeg.h b/src/ustreamer/data/blank_jpeg.h similarity index 100% rename from src/ustreamer/http/data/blank_jpeg.h rename to src/ustreamer/data/blank_jpeg.h diff --git a/src/ustreamer/http/data/index.html b/src/ustreamer/data/index.html similarity index 100% rename from src/ustreamer/http/data/index.html rename to src/ustreamer/data/index.html diff --git a/src/ustreamer/http/data/index_html.c b/src/ustreamer/data/index_html.c similarity index 100% rename from src/ustreamer/http/data/index_html.c rename to src/ustreamer/data/index_html.c diff --git a/src/ustreamer/http/data/index_html.h b/src/ustreamer/data/index_html.h similarity index 97% rename from src/ustreamer/http/data/index_html.h rename to src/ustreamer/data/index_html.h index 9cd2717..ff4fac2 100644 --- a/src/ustreamer/http/data/index_html.h +++ b/src/ustreamer/data/index_html.h @@ -24,7 +24,7 @@ #include -#include "../../../common/config.h" +#include "../../common/config.h" extern const char *const HTML_INDEX_PAGE; diff --git a/src/ustreamer/encoders/omx/encoder.c b/src/ustreamer/encoders/omx/encoder.c index 5c95927..b42b4b9 100644 --- a/src/ustreamer/encoders/omx/encoder.c +++ b/src/ustreamer/encoders/omx/encoder.c @@ -322,7 +322,7 @@ static int _omx_setup_input(struct omx_encoder_t *omx, struct device_t *dev) { portdef.format.image.nSliceHeight = align_size(dev->run->height, 16); portdef.format.image.bFlagErrorConcealment = OMX_FALSE; portdef.format.image.eCompressionFormat = OMX_IMAGE_CodingUnused; - portdef.nBufferSize = frame_get_generous_size(dev->run->width, dev->run->height); + portdef.nBufferSize = ((dev->run->width * dev->run->height) << 1) * 2; # define MAP_FORMAT(_v4l2_format, _omx_format) \ case _v4l2_format: { portdef.format.image.eColorFormat = _omx_format; break; } diff --git a/src/ustreamer/frame.c b/src/ustreamer/frame.c index db6f8e5..45a413f 100644 --- a/src/ustreamer/frame.c +++ b/src/ustreamer/frame.c @@ -23,10 +23,12 @@ #include "frame.h" -struct frame_t *frame_init(void) { +struct frame_t *frame_init(const char *role) { struct frame_t *frame; A_CALLOC(frame, 1); + frame->role = role; + frame_realloc_data(frame, 500 * 1024); return frame; } @@ -37,14 +39,10 @@ void frame_destroy(struct frame_t *frame) { free(frame); } -size_t frame_get_generous_size(unsigned width, unsigned height) { - return ((width * height) << 1) * 2; -} - void frame_realloc_data(struct frame_t *frame, size_t size) { if (frame->allocated < size) { - LOG_DEBUG("Increasing frame %p buffer: %zu -> %zu (+%zu)", - frame, frame->allocated, size, size - frame->allocated); + LOG_DEBUG("Increasing frame buffer '%s': %zu -> %zu (+%zu)", + frame->role, frame->allocated, size, size - frame->allocated); A_REALLOC(frame->data, size); frame->allocated = size; } @@ -69,6 +67,7 @@ void frame_copy(const struct frame_t *src, struct frame_t *dest) { # define COPY(_field) dest->_field = src->_field + // Don't copy the role COPY(used); COPY(width); diff --git a/src/ustreamer/frame.h b/src/ustreamer/frame.h index 10e86a1..162eee4 100644 --- a/src/ustreamer/frame.h +++ b/src/ustreamer/frame.h @@ -33,6 +33,7 @@ struct frame_t { + const char *role; unsigned char *data; size_t used; size_t allocated; @@ -44,11 +45,9 @@ struct frame_t { }; -struct frame_t *frame_init(void); +struct frame_t *frame_init(const char *role); void frame_destroy(struct frame_t *frame); -size_t frame_get_generous_size(unsigned width, unsigned height); - void frame_realloc_data(struct frame_t *frame, size_t size); void frame_set_data(struct frame_t *frame, const unsigned char *data, size_t size); void frame_append_data(struct frame_t *frame, const unsigned char *data, size_t size); diff --git a/src/ustreamer/http/server.c b/src/ustreamer/http/server.c index 9f3ee8c..8bbe134 100644 --- a/src/ustreamer/http/server.c +++ b/src/ustreamer/http/server.c @@ -37,8 +37,7 @@ static void _http_callback_stream_error(struct bufferevent *buf_event, short wha static void _http_exposed_refresh(int fd, short event, void *v_server); static void _http_queue_send_stream(struct http_server_t *server, bool stream_updated, bool frame_updated); -static bool _expose_new_picture_unsafe(struct http_server_t *server); -static bool _expose_blank_picture(struct http_server_t *server); +static bool _expose_new_frame(struct http_server_t *server); static void _format_bufferevent_reason(short what, char *reason); @@ -54,12 +53,11 @@ struct http_server_t *http_server_init(struct stream_t *stream) { struct exposed_t *exposed; A_CALLOC(exposed, 1); - exposed->frame = frame_init(); + exposed->frame = frame_init("http_exposed"); A_CALLOC(run, 1); run->stream = stream; run->exposed = exposed; - run->drop_same_frames_blank = 10; A_CALLOC(server, 1); server->host = "127.0.0.1"; @@ -70,7 +68,6 @@ struct http_server_t *http_server_init(struct stream_t *stream) { server->static_path = ""; server->allow_origin = ""; server->timeout = 10; - server->last_as_blank = -1; server->run = run; assert(!evthread_use_pthreads()); @@ -108,10 +105,6 @@ void http_server_destroy(struct http_server_t *server) { free(RUN(auth_token)); } - if (RUN(blank)) { - frame_destroy(RUN(blank)); - } - frame_destroy(EX(frame)); free(RUN(exposed)); free(server->run); @@ -131,15 +124,10 @@ int http_server_listen(struct http_server_t *server) { assert(!evhttp_set_cb(RUN(http), "/stream", _http_callback_stream, (void *)server)); } - RUN(drop_same_frames_blank) = max_u(server->drop_same_frames, RUN(drop_same_frames_blank)); - RUN(blank) = blank_picture_init(server->blank_path); - - // See _expose_blank_picture() - frame_copy(RUN(blank), EX(frame)); - EX(expose_begin_ts) = get_now_monotonic(); - EX(expose_cmp_ts) = EX(expose_begin_ts); - EX(expose_end_ts) = EX(expose_begin_ts); - // See _http_exposed_refresh() + frame_copy(STREAM(blank), EX(frame)); + EX(expose_begin_ts) = 0; + EX(expose_cmp_ts) = 0; + EX(expose_end_ts) = 0; EX(notify_last_width) = EX(frame->width); EX(notify_last_height) = EX(frame->height); @@ -755,30 +743,18 @@ static void _http_exposed_refresh(UNUSED int fd, UNUSED short what, void *v_serv bool stream_updated = false; bool frame_updated = false; -# define UNLOCK_STREAM { \ - atomic_store(&STREAM(video->updated), false); \ - A_MUTEX_UNLOCK(&STREAM(video->mutex)); \ - } - if (atomic_load(&STREAM(video->updated))) { - LOG_DEBUG("Refreshing HTTP exposed ..."); - A_MUTEX_LOCK(&STREAM(video->mutex)); - if (STREAM(video->online)) { - frame_updated = _expose_new_picture_unsafe(server); - UNLOCK_STREAM; - } else { - UNLOCK_STREAM; - frame_updated = _expose_blank_picture(server); - } + frame_updated = _expose_new_frame(server); stream_updated = true; - } else if (!EX(online)) { - LOG_DEBUG("Refreshing HTTP exposed (BLANK) ..."); - frame_updated = _expose_blank_picture(server); + } else if (EX(expose_end_ts) + 1 < get_now_monotonic()) { + LOG_DEBUG("HTTP: Repeating exposed ..."); + EX(expose_begin_ts) = get_now_monotonic(); + EX(expose_cmp_ts) = EX(expose_begin_ts); + EX(expose_end_ts) = EX(expose_begin_ts); + frame_updated = true; stream_updated = true; } -# undef UNLOCK_STREAM - _http_queue_send_stream(server, stream_updated, frame_updated); if ( @@ -797,87 +773,51 @@ static void _http_exposed_refresh(UNUSED int fd, UNUSED short what, void *v_serv } } -static bool _expose_new_picture_unsafe(struct http_server_t *server) { +static bool _expose_new_frame(struct http_server_t *server) { + bool updated = false; + + A_MUTEX_LOCK(&STREAM(video->mutex)); + + LOG_DEBUG("HTTP: Updating exposed frame (online=%d) ...", STREAM(video->online)); + EX(captured_fps) = STREAM(video->captured_fps); EX(expose_begin_ts) = get_now_monotonic(); - if (server->drop_same_frames) { + if (server->drop_same_frames && STREAM(video->online)) { + bool need_drop = false; + bool maybe_same = false; if ( - EX(online) - && EX(dropped) < server->drop_same_frames - && frame_compare(EX(frame), STREAM(video->frame)) + (need_drop = (EX(dropped) < server->drop_same_frames)) + && (maybe_same = frame_compare(EX(frame), STREAM(video->frame))) ) { EX(expose_cmp_ts) = get_now_monotonic(); EX(expose_end_ts) = EX(expose_cmp_ts); LOG_VERBOSE("HTTP: Dropped same frame number %u; cmp_time=%.06Lf", EX(dropped), EX(expose_cmp_ts) - EX(expose_begin_ts)); EX(dropped) += 1; - return false; // Not updated + goto not_updated; } else { EX(expose_cmp_ts) = get_now_monotonic(); - LOG_VERBOSE("HTTP: Passed same frame check (frames are differ); cmp_time=%.06Lf", - EX(expose_cmp_ts) - EX(expose_begin_ts)); + LOG_VERBOSE("HTTP: Passed same frame check (need_drop=%d, maybe_same=%d); cmp_time=%.06Lf", + need_drop, maybe_same, (EX(expose_cmp_ts) - EX(expose_begin_ts))); } } frame_copy(STREAM(video->frame), EX(frame)); - EX(online) = true; + EX(online) = STREAM(video->online); EX(dropped) = 0; EX(expose_cmp_ts) = EX(expose_begin_ts); EX(expose_end_ts) = get_now_monotonic(); - LOG_VERBOSE("HTTP: Exposed new frame; full exposition time = %.06Lf", - EX(expose_end_ts) - EX(expose_begin_ts)); + LOG_VERBOSE("HTTP: Exposed frame: online=%d, exp_time=%.06Lf", + EX(online), EX(expose_end_ts) - EX(expose_begin_ts)); - return true; // Updated -} - -static bool _expose_blank_picture(struct http_server_t *server) { - EX(expose_begin_ts) = get_now_monotonic(); - EX(expose_cmp_ts) = EX(expose_begin_ts); - -# define EXPOSE_BLANK frame_copy(RUN(blank), EX(frame)) - - if (EX(online)) { // Если переходим из online в offline - if (server->last_as_blank < 0) { // Если last_as_blank выключено, просто покажем картинку - LOG_INFO("HTTP: Changed frame to BLANK"); - EXPOSE_BLANK; - } else if (server->last_as_blank > 0) { // Если нужен таймер - запустим - LOG_INFO("HTTP: Freezing last alive frame for %d seconds", server->last_as_blank); - EX(last_as_blank_ts) = get_now_monotonic(); - } else { // last_as_blank == 0 - показываем последний фрейм вечно - LOG_INFO("HTTP: Freezing last alive frame forever"); - } - goto updated; - } - - if ( // Если уже оффлайн, включена фича last_as_blank с таймером и он запущен - server->last_as_blank > 0 - && EX(last_as_blank_ts) > 0 - && EX(last_as_blank_ts) + server->last_as_blank < EX(expose_begin_ts) - ) { - LOG_INFO("HTTP: Changed last alive frame to BLANK"); - EXPOSE_BLANK; - EX(last_as_blank_ts) = 0; // Останавливаем таймер - goto updated; - } - -# undef EXPOSE_BLANK - - if (EX(dropped) < RUN(drop_same_frames_blank)) { - LOG_PERF("HTTP: Dropped same frame (BLANK) number %u", EX(dropped)); - EX(dropped) += 1; - EX(expose_end_ts) = get_now_monotonic(); - return false; // Not updated - } - - updated: - EX(captured_fps) = 0; - EX(online) = false; - EX(dropped) = 0; - EX(expose_end_ts) = get_now_monotonic(); - return true; // Updated + updated = true; + not_updated: + atomic_store(&STREAM(video->updated), false); + A_MUTEX_UNLOCK(&STREAM(video->mutex)); + return updated; } static void _format_bufferevent_reason(short what, char *reason) { diff --git a/src/ustreamer/http/server.h b/src/ustreamer/http/server.h index f27b3b7..3aa5039 100644 --- a/src/ustreamer/http/server.h +++ b/src/ustreamer/http/server.h @@ -56,6 +56,7 @@ #include "../../common/threading.h" #include "../../common/logging.h" #include "../../common/process.h" +#include "../data/index_html.h" #include "../frame.h" #include "../encoder.h" #include "../stream.h" @@ -68,9 +69,6 @@ #include "base64.h" #include "mime.h" #include "static.h" -#include "blank.h" - -#include "data/index_html.h" struct stream_client_t { @@ -96,14 +94,13 @@ struct stream_client_t { struct exposed_t { struct frame_t *frame; - unsigned captured_fps; - unsigned queued_fps; - bool online; - unsigned dropped; - long double expose_begin_ts; - long double expose_cmp_ts; - long double expose_end_ts; - long double last_as_blank_ts; + unsigned captured_fps; + unsigned queued_fps; + bool online; + unsigned dropped; + long double expose_begin_ts; + long double expose_cmp_ts; + long double expose_end_ts; bool notify_last_online; unsigned notify_last_width; @@ -120,8 +117,6 @@ struct http_server_runtime_t { struct exposed_t *exposed; struct stream_client_t *stream_clients; unsigned stream_clients_count; - struct frame_t *blank; - unsigned drop_same_frames_blank; }; struct http_server_t { @@ -138,8 +133,6 @@ struct http_server_t { char *static_path; char *allow_origin; - char *blank_path; - int last_as_blank; unsigned drop_same_frames; bool slowdown; unsigned fake_width; diff --git a/src/ustreamer/options.c b/src/ustreamer/options.c index fc702be..e3f3489 100644 --- a/src/ustreamer/options.c +++ b/src/ustreamer/options.c @@ -132,6 +132,8 @@ static const struct option _LONG_OPTS[] = { # ifdef WITH_OMX {"glitched-resolutions", required_argument, NULL, _O_GLITCHED_RESOLUTIONS}, # endif + {"blank", required_argument, NULL, _O_BLANK}, + {"last-as-blank", required_argument, NULL, _O_LAST_AS_BLANK}, {"device-timeout", required_argument, NULL, _O_DEVICE_TIMEOUT}, {"device-error-delay", required_argument, NULL, _O_DEVICE_ERROR_DELAY}, @@ -157,8 +159,6 @@ static const struct option _LONG_OPTS[] = { {"user", required_argument, NULL, _O_USER}, {"passwd", required_argument, NULL, _O_PASSWD}, {"static", required_argument, NULL, _O_STATIC}, - {"blank", required_argument, NULL, _O_BLANK}, - {"last-as-blank", required_argument, NULL, _O_LAST_AS_BLANK}, {"drop-same-frames", required_argument, NULL, _O_DROP_SAME_FRAMES}, {"slowdown", no_argument, NULL, _O_SLOWDOWN}, {"allow-origin", required_argument, NULL, _O_ALLOW_ORIGIN}, @@ -231,6 +231,9 @@ struct options_t *options_init(int argc, char *argv[]) { } void options_destroy(struct options_t *options) { + if (options->blank) { + frame_destroy(options->blank); + } for (int index = 0; index < options->argc; ++index) { free(options->argv_copy[index]); } @@ -313,6 +316,7 @@ int options_parse( int short_index; int opt_index; char short_opts[1024] = {0}; + char *blank_path = NULL; # ifdef WITH_SETPROCTITLE char *process_name_prefix = NULL; # endif @@ -354,6 +358,8 @@ int options_parse( } break; # endif + case _O_BLANK: OPT_SET(blank_path, optarg); + case _O_LAST_AS_BLANK: OPT_NUMBER("--last-as-blank", stream->last_as_blank, 0, 86400, 0); case _O_DEVICE_TIMEOUT: OPT_NUMBER("--device-timeout", dev->timeout, 1, 60, 0); case _O_DEVICE_ERROR_DELAY: OPT_NUMBER("--device-error-delay", stream->error_delay, 1, 60, 0); @@ -392,8 +398,6 @@ int options_parse( case _O_USER: OPT_SET(server->user, optarg); case _O_PASSWD: OPT_SET(server->passwd, optarg); case _O_STATIC: OPT_SET(server->static_path, optarg); - case _O_BLANK: OPT_SET(server->blank_path, optarg); - case _O_LAST_AS_BLANK: OPT_NUMBER("--last-as-blank", server->last_as_blank, 0, 86400, 0); case _O_DROP_SAME_FRAMES: OPT_NUMBER("--drop-same-frames", server->drop_same_frames, 0, VIDEO_MAX_FPS, 0); case _O_SLOWDOWN: OPT_SET(server->slowdown, true); case _O_FAKE_RESOLUTION: OPT_RESOLUTION("--fake-resolution", server->fake_width, server->fake_height, false); @@ -443,6 +447,9 @@ int options_parse( } } + options->blank = blank_frame_init(blank_path); + stream->blank = options->blank; + # ifdef WITH_SETPROCTITLE if (process_name_prefix != NULL) { process_set_name_prefix(options->argc, options->argv, process_name_prefix); @@ -618,6 +625,12 @@ static void _help( printf(" -g|--glitched-resolutions ─ Comma-separated list of resolutions that require forced\n"); printf(" encoding on CPU instead of OMX. Default: disabled.\n\n"); # endif + printf(" -k|--blank ─────────────────── Path to JPEG file that will be shown when the device is disconnected\n"); + printf(" during the streaming. Default: black screen 640x480 with 'NO SIGNAL'.\n\n"); + printf(" -K|--last-as-blank ──────────── Show the last frame received from the camera after it was disconnected,\n"); + printf(" but no more than specified time (or endlessly if 0 is specified).\n"); + printf(" If the device has not yet been online, display 'NO SIGNAL' or the image\n"); + printf(" specified by option --blank. Default: disabled.\n\n"); printf(" --device-timeout ────────────── Timeout for device querying. Default: %u.\n\n", dev->timeout); printf(" --device-error-delay ────────── Delay before trying to connect to the device again\n"); printf(" after an error (timeout for example). Default: %u.\n\n", stream->error_delay); @@ -648,12 +661,6 @@ static void _help( printf(" --passwd ───────────── HTTP basic auth passwd. Default: empty.\n\n"); printf(" --static ───────────── Path to dir with static files instead of embedded root index page.\n"); printf(" Symlinks are not supported for security reasons. Default: disabled.\n\n"); - printf(" -k|--blank ────────── Path to JPEG file that will be shown when the device is disconnected\n"); - printf(" during the streaming. Default: black screen 640x480 with 'NO SIGNAL'.\n\n"); - printf(" -K|--last-as-blank ─── Show the last frame received from the camera after it was disconnected,\n"); - printf(" but no more than specified time (or endlessly if 0 is specified).\n"); - printf(" If the device has not yet been online, display 'NO SIGNAL' or the image\n"); - printf(" specified by option --blank. Default: disabled.\n\n"); printf(" -e|--drop-same-frames ── Don't send identical frames to clients, but no more than specified number.\n"); printf(" It can significantly reduce the outgoing traffic, but will increase\n"); printf(" the CPU loading. Don't use this option with analog signal sources\n"); diff --git a/src/ustreamer/options.h b/src/ustreamer/options.h index 7be760b..6aada8a 100644 --- a/src/ustreamer/options.h +++ b/src/ustreamer/options.h @@ -38,7 +38,9 @@ #include "../common/process.h" #include "device.h" +#include "frame.h" #include "encoder.h" +#include "blank.h" #include "stream.h" #include "http/server.h" #ifdef WITH_GPIO @@ -47,9 +49,10 @@ struct options_t { - int argc; - char **argv; - char **argv_copy; + int argc; + char **argv; + char **argv_copy; + struct frame_t *blank; }; diff --git a/src/ustreamer/stream.c b/src/ustreamer/stream.c index 5309f25..077aa13 100644 --- a/src/ustreamer/stream.c +++ b/src/ustreamer/stream.c @@ -30,6 +30,7 @@ struct _worker_t { atomic_bool *workers_stop; long double last_comp_time; + char *frame_role; struct frame_t *frame; pthread_mutex_t has_job_mutex; @@ -70,7 +71,7 @@ struct _workers_pool_t { static struct _workers_pool_t *_stream_init_loop(struct stream_t *stream); static struct _workers_pool_t *_stream_init_one(struct stream_t *stream); -static void _stream_expose_picture(struct stream_t *stream, struct frame_t *frame, unsigned captured_fps); +static void _stream_expose_frame(struct stream_t *stream, struct frame_t *frame, unsigned captured_fps); static struct _workers_pool_t *_workers_pool_init(struct stream_t *stream); static void _workers_pool_destroy(struct _workers_pool_t *pool); @@ -92,11 +93,12 @@ struct stream_t *stream_init(struct device_t *dev, struct encoder_t *encoder) { atomic_init(&proc->slowdown, false); A_CALLOC(video, 1); - video->frame = frame_init(); + video->frame = frame_init("stream_video"); atomic_init(&video->updated, false); A_MUTEX_INIT(&video->mutex); A_CALLOC(stream, 1); + stream->last_as_blank = -1; stream->error_delay = 1; # ifdef WITH_RAWSINK stream->rawsink_name = ""; @@ -140,9 +142,6 @@ void stream_loop(struct stream_t *stream) { LOG_INFO("Capturing ..."); - LOG_DEBUG("Pre-allocating memory for stream frame ..."); - frame_realloc_data(stream->video->frame, frame_get_generous_size(DEV(run->width), DEV(run->height))); - while (!atomic_load(&stream->proc->stop)) { struct _worker_t *ready_wr; @@ -153,7 +152,7 @@ void stream_loop(struct stream_t *stream) { if (!ready_wr->job_failed) { if (ready_wr->job_timely) { - _stream_expose_picture(stream, ready_wr->frame, captured_fps); + _stream_expose_frame(stream, ready_wr->frame, captured_fps); LOG_PERF("##### Encoded frame exposed; worker=%u", ready_wr->number); } else { LOG_PERF("----- Encoded frame dropped; worker=%u", ready_wr->number); @@ -255,11 +254,6 @@ void stream_loop(struct stream_t *stream) { } } - A_MUTEX_LOCK(&stream->video->mutex); - stream->video->online = false; - atomic_store(&stream->video->updated, true); - A_MUTEX_UNLOCK(&stream->video->mutex); - _workers_pool_destroy(pool); device_switch_capturing(stream->dev, false); device_close(stream->dev); @@ -293,6 +287,8 @@ static struct _workers_pool_t *_stream_init_loop(struct stream_t *stream) { LOG_DEBUG("%s: stream->proc->stop=%d", __FUNCTION__, atomic_load(&stream->proc->stop)); while (!atomic_load(&stream->proc->stop)) { + _stream_expose_frame(stream, NULL, 0); + if (access(stream->dev->path, R_OK|W_OK) < 0) { if (access_error != errno) { SEP_INFO('='); @@ -333,16 +329,57 @@ static struct _workers_pool_t *_stream_init_one(struct stream_t *stream) { return NULL; } -static void _stream_expose_picture(struct stream_t *stream, struct frame_t *frame, unsigned captured_fps) { - A_MUTEX_LOCK(&stream->video->mutex); +static void _stream_expose_frame(struct stream_t *stream, struct frame_t *frame, unsigned captured_fps) { +# define VID(_next) stream->video->_next - frame_copy(frame, stream->video->frame); + struct frame_t *new = NULL; - stream->video->online = true; - stream->video->captured_fps = captured_fps; - atomic_store(&stream->video->updated, true); + A_MUTEX_LOCK(&VID(mutex)); - A_MUTEX_UNLOCK(&stream->video->mutex); + if (frame) { + new = frame; + VID(last_as_blank_ts) = 0; // Останавливаем таймер + LOG_DEBUG("Exposed ALIVE video frame"); + + } else { + if (VID(online)) { // Если переходим из online в offline + if (stream->last_as_blank < 0) { // Если last_as_blank выключен, просто покажем старую картинку + new = stream->blank; + LOG_INFO("Changed video frame to BLANK"); + } else if (stream->last_as_blank > 0) { // // Если нужен таймер - запустим + VID(last_as_blank_ts) = get_now_monotonic() + stream->last_as_blank; + LOG_INFO("Freezed last ALIVE video frame for %d seconds", stream->last_as_blank); + } else { // last_as_blank == 0 - показываем последний фрейм вечно + LOG_INFO("Freezed last ALIVE video frame forever"); + } + } else if (stream->last_as_blank < 0) { + new = stream->blank; + LOG_INFO("Changed video frame to BLANK"); + } + + if ( // Если уже оффлайн, включена фича last_as_blank с таймером и он запущен + stream->last_as_blank > 0 + && VID(last_as_blank_ts) != 0 + && VID(last_as_blank_ts) < get_now_monotonic() + ) { + new = stream->blank; + VID(last_as_blank_ts) = 0; // // Останавливаем таймер + LOG_INFO("Changed last ALIVE video frame to BLANK"); + } + } + + if (new) { + frame_copy(new, VID(frame)); + } else if (VID(frame->used) == 0) { // Инициализация + frame_copy(stream->blank, VID(frame)); + frame = NULL; + } + VID(online) = frame; + VID(captured_fps) = captured_fps; + atomic_store(&VID(updated), true); + A_MUTEX_UNLOCK(&VID(mutex)); + +# undef VID } static struct _workers_pool_t *_workers_pool_init(struct stream_t *stream) { @@ -350,7 +387,6 @@ static struct _workers_pool_t *_workers_pool_init(struct stream_t *stream) { # define RUN(_next) stream->dev->run->_next struct _workers_pool_t *pool; - size_t frame_size = frame_get_generous_size(RUN(width), RUN(height)); LOG_INFO("Creating pool with %u workers ...", stream->encoder->run->n_workers); @@ -374,8 +410,9 @@ static struct _workers_pool_t *_workers_pool_init(struct stream_t *stream) { for (unsigned number = 0; number < pool->n_workers; ++number) { # define WR(_next) pool->workers[number]._next - WR(frame) = frame_init(); - frame_realloc_data(WR(frame), frame_size); + A_CALLOC(WR(frame_role), 32); + sprintf(WR(frame_role), "worker_dest_%u", number); + WR(frame) = frame_init(WR(frame_role)); A_MUTEX_INIT(&WR(has_job_mutex)); atomic_init(&WR(has_job), false); @@ -417,6 +454,7 @@ static void _workers_pool_destroy(struct _workers_pool_t *pool) { A_COND_DESTROY(&WR(has_job_cond)); frame_destroy(WR(frame)); + free(WR(frame_role)); # undef WR } diff --git a/src/ustreamer/stream.h b/src/ustreamer/stream.h index 9ec9583..c33b0d3 100644 --- a/src/ustreamer/stream.h +++ b/src/ustreamer/stream.h @@ -35,6 +35,8 @@ #include "../common/threading.h" #include "../common/logging.h" +#include "blank.h" + #include "frame.h" #include "device.h" #include "encoder.h" @@ -56,10 +58,12 @@ struct video_t { bool online; unsigned captured_fps; atomic_bool updated; + long double last_as_blank_ts; pthread_mutex_t mutex; }; struct stream_t { + int last_as_blank; unsigned error_delay; # ifdef WITH_RAWSINK char *rawsink_name; @@ -69,6 +73,7 @@ struct stream_t { struct device_t *dev; struct encoder_t *encoder; + struct frame_t *blank; struct process_t *proc; struct video_t *video;