mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-18 02:55:46 +00:00
refactoring
This commit is contained in:
parent
9039aa8ac5
commit
f19ab11f76
6
Makefile
6
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)
|
||||
|
||||
@ -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);
|
||||
@ -28,11 +28,11 @@
|
||||
|
||||
#include <jpeglib.h>
|
||||
|
||||
#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);
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@ -24,7 +24,7 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "../../../common/config.h"
|
||||
#include "../../common/config.h"
|
||||
|
||||
|
||||
extern const char *const HTML_INDEX_PAGE;
|
||||
@ -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; }
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 <WxH,...> ─ 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> ─────────────────── 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 <sec> ──────────── 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 <sec> ────────────── Timeout for device querying. Default: %u.\n\n", dev->timeout);
|
||||
printf(" --device-error-delay <sec> ────────── 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 <str> ───────────── HTTP basic auth passwd. Default: empty.\n\n");
|
||||
printf(" --static <path> ───────────── 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> ────────── 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 <sec> ─── 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 <N> ── 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");
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user