mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-08-05 02:22:06 +00:00
using us_ prefixes
This commit is contained in:
@@ -23,17 +23,17 @@
|
||||
#include "file.h"
|
||||
|
||||
|
||||
output_file_s *output_file_init(const char *path, bool json) {
|
||||
output_file_s *output;
|
||||
A_CALLOC(output, 1);
|
||||
us_output_file_s *us_output_file_init(const char *path, bool json) {
|
||||
us_output_file_s *output;
|
||||
US_CALLOC(output, 1);
|
||||
|
||||
if (!strcmp(path, "-")) {
|
||||
LOG_INFO("Using output: <stdout>");
|
||||
US_LOG_INFO("Using output: <stdout>");
|
||||
output->fp = stdout;
|
||||
} else {
|
||||
LOG_INFO("Using output: %s", path);
|
||||
US_LOG_INFO("Using output: %s", path);
|
||||
if ((output->fp = fopen(path, "wb")) == NULL) {
|
||||
LOG_PERROR("Can't open output file");
|
||||
US_LOG_PERROR("Can't open output file");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
@@ -42,14 +42,14 @@ output_file_s *output_file_init(const char *path, bool json) {
|
||||
return output;
|
||||
|
||||
error:
|
||||
output_file_destroy(output);
|
||||
us_output_file_destroy(output);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void output_file_write(void *v_output, const frame_s *frame) {
|
||||
output_file_s *output = (output_file_s *)v_output;
|
||||
void us_output_file_write(void *v_output, const us_frame_s *frame) {
|
||||
us_output_file_s *output = (us_output_file_s *)v_output;
|
||||
if (output->json) {
|
||||
base64_encode(frame->data, frame->used, &output->base64_data, &output->base64_allocated);
|
||||
us_base64_encode(frame->data, frame->used, &output->base64_data, &output->base64_allocated);
|
||||
fprintf(output->fp,
|
||||
"{\"size\": %zu, \"width\": %u, \"height\": %u,"
|
||||
" \"format\": %u, \"stride\": %u, \"online\": %u,"
|
||||
@@ -65,14 +65,14 @@ void output_file_write(void *v_output, const frame_s *frame) {
|
||||
fflush(output->fp);
|
||||
}
|
||||
|
||||
void output_file_destroy(void *v_output) {
|
||||
output_file_s *output = (output_file_s *)v_output;
|
||||
void us_output_file_destroy(void *v_output) {
|
||||
us_output_file_s *output = (us_output_file_s *)v_output;
|
||||
if (output->base64_data) {
|
||||
free(output->base64_data);
|
||||
}
|
||||
if (output->fp && output->fp != stdout) {
|
||||
if (fclose(output->fp) < 0) {
|
||||
LOG_PERROR("Can't close output file");
|
||||
US_LOG_PERROR("Can't close output file");
|
||||
}
|
||||
}
|
||||
free(output);
|
||||
|
||||
@@ -41,9 +41,9 @@ typedef struct {
|
||||
FILE *fp;
|
||||
char *base64_data;
|
||||
size_t base64_allocated;
|
||||
} output_file_s;
|
||||
} us_output_file_s;
|
||||
|
||||
|
||||
output_file_s *output_file_init(const char *path, bool json);
|
||||
void output_file_write(void *v_output, const frame_s *frame);
|
||||
void output_file_destroy(void *v_output);
|
||||
us_output_file_s *us_output_file_init(const char *path, bool json);
|
||||
void us_output_file_write(void *v_output, const us_frame_s *frame);
|
||||
void us_output_file_destroy(void *v_output);
|
||||
|
||||
@@ -82,12 +82,12 @@ static const struct option _LONG_OPTS[] = {
|
||||
};
|
||||
|
||||
|
||||
volatile bool global_stop = false;
|
||||
volatile bool _g_stop = false;
|
||||
|
||||
|
||||
typedef struct {
|
||||
void *v_output;
|
||||
void (*write)(void *v_output, const frame_s *frame);
|
||||
void (*write)(void *v_output, const us_frame_s *frame);
|
||||
void (*destroy)(void *v_output);
|
||||
} _output_context_s;
|
||||
|
||||
@@ -104,8 +104,8 @@ static void _help(FILE *fp);
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
LOGGING_INIT;
|
||||
A_THREAD_RENAME("main");
|
||||
US_LOGGING_INIT;
|
||||
US_THREAD_RENAME("main");
|
||||
|
||||
char *sink_name = NULL;
|
||||
unsigned sink_timeout = 1;
|
||||
@@ -140,7 +140,7 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
char short_opts[128];
|
||||
build_short_options(_LONG_OPTS, short_opts, 128);
|
||||
us_build_short_options(_LONG_OPTS, short_opts, 128);
|
||||
|
||||
for (int ch; (ch = getopt_long(argc, argv, short_opts, _LONG_OPTS, NULL)) >= 0;) {
|
||||
switch (ch) {
|
||||
@@ -151,15 +151,15 @@ int main(int argc, char *argv[]) {
|
||||
case _O_COUNT: OPT_NUMBER("--count", count, 0, LLONG_MAX, 0);
|
||||
case _O_INTERVAL: OPT_LDOUBLE("--interval", interval, 0, 60);
|
||||
|
||||
case _O_LOG_LEVEL: OPT_NUMBER("--log-level", us_log_level, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, 0);
|
||||
case _O_PERF: OPT_SET(us_log_level, LOG_LEVEL_PERF);
|
||||
case _O_VERBOSE: OPT_SET(us_log_level, LOG_LEVEL_VERBOSE);
|
||||
case _O_DEBUG: OPT_SET(us_log_level, LOG_LEVEL_DEBUG);
|
||||
case _O_LOG_LEVEL: OPT_NUMBER("--log-level", us_log_level, US_LOG_LEVEL_INFO, US_LOG_LEVEL_DEBUG, 0);
|
||||
case _O_PERF: OPT_SET(us_log_level, US_LOG_LEVEL_PERF);
|
||||
case _O_VERBOSE: OPT_SET(us_log_level, US_LOG_LEVEL_VERBOSE);
|
||||
case _O_DEBUG: OPT_SET(us_log_level, US_LOG_LEVEL_DEBUG);
|
||||
case _O_FORCE_LOG_COLORS: OPT_SET(us_log_colored, true);
|
||||
case _O_NO_LOG_COLORS: OPT_SET(us_log_colored, false);
|
||||
|
||||
case _O_HELP: _help(stdout); return 0;
|
||||
case _O_VERSION: puts(VERSION); return 0;
|
||||
case _O_VERSION: puts(US_VERSION); return 0;
|
||||
|
||||
case 0: break;
|
||||
default: return 1;
|
||||
@@ -178,11 +178,11 @@ int main(int argc, char *argv[]) {
|
||||
_output_context_s ctx = {0};
|
||||
|
||||
if (output_path && output_path[0] != '\0') {
|
||||
if ((ctx.v_output = (void *)output_file_init(output_path, output_json)) == NULL) {
|
||||
if ((ctx.v_output = (void *)us_output_file_init(output_path, output_json)) == NULL) {
|
||||
return 1;
|
||||
}
|
||||
ctx.write = output_file_write;
|
||||
ctx.destroy = output_file_destroy;
|
||||
ctx.write = us_output_file_write;
|
||||
ctx.destroy = us_output_file_destroy;
|
||||
}
|
||||
|
||||
_install_signal_handlers();
|
||||
@@ -196,12 +196,12 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
static void _signal_handler(int signum) {
|
||||
switch (signum) {
|
||||
case SIGTERM: LOG_INFO_NOLOCK("===== Stopping by SIGTERM ====="); break;
|
||||
case SIGINT: LOG_INFO_NOLOCK("===== Stopping by SIGINT ====="); break;
|
||||
case SIGPIPE: LOG_INFO_NOLOCK("===== Stopping by SIGPIPE ====="); break;
|
||||
default: LOG_INFO_NOLOCK("===== Stopping by %d =====", signum); break;
|
||||
case SIGTERM: US_LOG_INFO_NOLOCK("===== Stopping by SIGTERM ====="); break;
|
||||
case SIGINT: US_LOG_INFO_NOLOCK("===== Stopping by SIGINT ====="); break;
|
||||
case SIGPIPE: US_LOG_INFO_NOLOCK("===== Stopping by SIGPIPE ====="); break;
|
||||
default: US_LOG_INFO_NOLOCK("===== Stopping by %d =====", signum); break;
|
||||
}
|
||||
global_stop = true;
|
||||
_g_stop = true;
|
||||
}
|
||||
|
||||
static void _install_signal_handlers(void) {
|
||||
@@ -213,13 +213,13 @@ static void _install_signal_handlers(void) {
|
||||
assert(!sigaddset(&sig_act.sa_mask, SIGTERM));
|
||||
assert(!sigaddset(&sig_act.sa_mask, SIGPIPE));
|
||||
|
||||
LOG_DEBUG("Installing SIGINT handler ...");
|
||||
US_LOG_DEBUG("Installing SIGINT handler ...");
|
||||
assert(!sigaction(SIGINT, &sig_act, NULL));
|
||||
|
||||
LOG_DEBUG("Installing SIGTERM handler ...");
|
||||
US_LOG_DEBUG("Installing SIGTERM handler ...");
|
||||
assert(!sigaction(SIGTERM, &sig_act, NULL));
|
||||
|
||||
LOG_DEBUG("Installing SIGTERM handler ...");
|
||||
US_LOG_DEBUG("Installing SIGTERM handler ...");
|
||||
assert(!sigaction(SIGPIPE, &sig_act, NULL));
|
||||
}
|
||||
|
||||
@@ -234,10 +234,10 @@ static int _dump_sink(
|
||||
|
||||
useconds_t interval_us = interval * 1000000;
|
||||
|
||||
frame_s *frame = frame_init();
|
||||
memsink_s *sink = NULL;
|
||||
us_frame_s *frame = us_frame_init();
|
||||
us_memsink_s *sink = NULL;
|
||||
|
||||
if ((sink = memsink_init("input", sink_name, false, 0, false, 0, sink_timeout)) == NULL) {
|
||||
if ((sink = us_memsink_init("input", sink_name, false, 0, false, 0, sink_timeout)) == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -247,28 +247,28 @@ static int _dump_sink(
|
||||
|
||||
long double last_ts = 0;
|
||||
|
||||
while (!global_stop) {
|
||||
int error = memsink_client_get(sink, frame);
|
||||
while (!_g_stop) {
|
||||
int error = us_memsink_client_get(sink, frame);
|
||||
if (error == 0) {
|
||||
const long double now = get_now_monotonic();
|
||||
const long long now_second = floor_ms(now);
|
||||
const long double now = us_get_now_monotonic();
|
||||
const long long now_second = us_floor_ms(now);
|
||||
|
||||
char fourcc_str[8];
|
||||
LOG_VERBOSE("Frame: size=%zu, res=%ux%u, fourcc=%s, stride=%u, online=%d, key=%d, latency=%.3Lf, diff=%.3Lf",
|
||||
US_LOG_VERBOSE("Frame: size=%zu, res=%ux%u, fourcc=%s, stride=%u, online=%d, key=%d, latency=%.3Lf, diff=%.3Lf",
|
||||
frame->used, frame->width, frame->height,
|
||||
fourcc_to_string(frame->format, fourcc_str, 8),
|
||||
us_fourcc_to_string(frame->format, fourcc_str, 8),
|
||||
frame->stride, frame->online, frame->key,
|
||||
now - frame->grab_ts, (last_ts ? now - last_ts : 0));
|
||||
last_ts = now;
|
||||
|
||||
LOG_DEBUG(" grab_ts=%.3Lf, encode_begin_ts=%.3Lf, encode_end_ts=%.3Lf",
|
||||
US_LOG_DEBUG(" grab_ts=%.3Lf, encode_begin_ts=%.3Lf, encode_end_ts=%.3Lf",
|
||||
frame->grab_ts, frame->encode_begin_ts, frame->encode_end_ts);
|
||||
|
||||
if (now_second != fps_second) {
|
||||
fps = fps_accum;
|
||||
fps_accum = 0;
|
||||
fps_second = now_second;
|
||||
LOG_PERF_FPS("A new second has come; captured_fps=%u", fps);
|
||||
US_LOG_PERF_FPS("A new second has come; captured_fps=%u", fps);
|
||||
}
|
||||
fps_accum += 1;
|
||||
|
||||
@@ -301,11 +301,11 @@ static int _dump_sink(
|
||||
|
||||
ok:
|
||||
if (sink) {
|
||||
memsink_destroy(sink);
|
||||
us_memsink_destroy(sink);
|
||||
}
|
||||
frame_destroy(frame);
|
||||
us_frame_destroy(frame);
|
||||
|
||||
LOG_INFO("Bye-bye");
|
||||
US_LOG_INFO("Bye-bye");
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -313,7 +313,7 @@ static void _help(FILE *fp) {
|
||||
# define SAY(_msg, ...) fprintf(fp, _msg "\n", ##__VA_ARGS__)
|
||||
SAY("\nuStreamer-dump - Dump uStreamer's memory sink to file");
|
||||
SAY("═════════════════════════════════════════════════════");
|
||||
SAY("Version: %s; license: GPLv3", VERSION);
|
||||
SAY("Version: %s; license: GPLv3", US_VERSION);
|
||||
SAY("Copyright (C) 2018-2022 Maxim Devaev <mdevaev@gmail.com>\n");
|
||||
SAY("Example:");
|
||||
SAY("════════");
|
||||
|
||||
@@ -37,11 +37,11 @@ static const char _ENCODING_TABLE[] = {
|
||||
static const unsigned _MOD_TABLE[] = {0, 2, 1};
|
||||
|
||||
|
||||
void base64_encode(const uint8_t *data, size_t size, char **encoded, size_t *allocated) {
|
||||
void us_base64_encode(const uint8_t *data, size_t size, char **encoded, size_t *allocated) {
|
||||
const size_t encoded_size = 4 * ((size + 2) / 3) + 1; // +1 for '\0'
|
||||
|
||||
if (*encoded == NULL || (allocated && *allocated < encoded_size)) {
|
||||
A_REALLOC(*encoded, encoded_size);
|
||||
US_REALLOC(*encoded, encoded_size);
|
||||
if (allocated) {
|
||||
*allocated = encoded_size;
|
||||
}
|
||||
|
||||
@@ -31,4 +31,4 @@
|
||||
#include "tools.h"
|
||||
|
||||
|
||||
void base64_encode(const uint8_t *data, size_t size, char **encoded, size_t *allocated);
|
||||
void us_base64_encode(const uint8_t *data, size_t size, char **encoded, size_t *allocated);
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define VERSION_MAJOR 5
|
||||
#define VERSION_MINOR 16
|
||||
#define US_VERSION_MAJOR 5
|
||||
#define US_VERSION_MINOR 16
|
||||
|
||||
#define MAKE_VERSION2(_major, _minor) #_major "." #_minor
|
||||
#define MAKE_VERSION1(_major, _minor) MAKE_VERSION2(_major, _minor)
|
||||
#define VERSION MAKE_VERSION1(VERSION_MAJOR, VERSION_MINOR)
|
||||
#define US_MAKE_VERSION2(_major, _minor) #_major "." #_minor
|
||||
#define US_MAKE_VERSION1(_major, _minor) US_MAKE_VERSION2(_major, _minor)
|
||||
#define US_VERSION US_MAKE_VERSION1(US_VERSION_MAJOR, US_VERSION_MINOR)
|
||||
|
||||
#define VERSION_U ((unsigned)(VERSION_MAJOR * 1000 + VERSION_MINOR))
|
||||
#define US_VERSION_U ((unsigned)(US_VERSION_MAJOR * 1000 + US_VERSION_MINOR))
|
||||
|
||||
@@ -23,55 +23,55 @@
|
||||
#include "frame.h"
|
||||
|
||||
|
||||
frame_s *frame_init(void) {
|
||||
frame_s *frame;
|
||||
A_CALLOC(frame, 1);
|
||||
frame_realloc_data(frame, 512 * 1024);
|
||||
us_frame_s *us_frame_init(void) {
|
||||
us_frame_s *frame;
|
||||
US_CALLOC(frame, 1);
|
||||
us_frame_realloc_data(frame, 512 * 1024);
|
||||
frame->dma_fd = -1;
|
||||
return frame;
|
||||
}
|
||||
|
||||
void frame_destroy(frame_s *frame) {
|
||||
void us_frame_destroy(us_frame_s *frame) {
|
||||
if (frame->data) {
|
||||
free(frame->data);
|
||||
}
|
||||
free(frame);
|
||||
}
|
||||
|
||||
void frame_realloc_data(frame_s *frame, size_t size) {
|
||||
void us_frame_realloc_data(us_frame_s *frame, size_t size) {
|
||||
if (frame->allocated < size) {
|
||||
A_REALLOC(frame->data, size);
|
||||
US_REALLOC(frame->data, size);
|
||||
frame->allocated = size;
|
||||
}
|
||||
}
|
||||
|
||||
void frame_set_data(frame_s *frame, const uint8_t *data, size_t size) {
|
||||
frame_realloc_data(frame, size);
|
||||
void us_frame_set_data(us_frame_s *frame, const uint8_t *data, size_t size) {
|
||||
us_frame_realloc_data(frame, size);
|
||||
memcpy(frame->data, data, size);
|
||||
frame->used = size;
|
||||
}
|
||||
|
||||
void frame_append_data(frame_s *frame, const uint8_t *data, size_t size) {
|
||||
void us_frame_append_data(us_frame_s *frame, const uint8_t *data, size_t size) {
|
||||
size_t new_used = frame->used + size;
|
||||
frame_realloc_data(frame, new_used);
|
||||
us_frame_realloc_data(frame, new_used);
|
||||
memcpy(frame->data + frame->used, data, size);
|
||||
frame->used = new_used;
|
||||
}
|
||||
|
||||
void frame_copy(const frame_s *src, frame_s *dest) {
|
||||
frame_set_data(dest, src->data, src->used);
|
||||
FRAME_COPY_META(src, dest);
|
||||
void us_frame_copy(const us_frame_s *src, us_frame_s *dest) {
|
||||
us_frame_set_data(dest, src->data, src->used);
|
||||
US_FRAME_COPY_META(src, dest);
|
||||
}
|
||||
|
||||
bool frame_compare(const frame_s *a, const frame_s *b) {
|
||||
bool us_frame_compare(const us_frame_s *a, const us_frame_s *b) {
|
||||
return (
|
||||
a->allocated && b->allocated
|
||||
&& FRAME_COMPARE_META_USED_NOTS(a, b)
|
||||
&& US_FRAME_COMPARE_META_USED_NOTS(a, b)
|
||||
&& !memcmp(a->data, b->data, b->used)
|
||||
);
|
||||
}
|
||||
|
||||
unsigned frame_get_padding(const frame_s *frame) {
|
||||
unsigned us_frame_get_padding(const us_frame_s *frame) {
|
||||
unsigned bytes_per_pixel = 0;
|
||||
switch (frame->format) {
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
@@ -89,7 +89,7 @@ unsigned frame_get_padding(const frame_s *frame) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *fourcc_to_string(unsigned format, char *buf, size_t size) {
|
||||
const char *us_fourcc_to_string(unsigned format, char *buf, size_t size) {
|
||||
assert(size >= 8);
|
||||
buf[0] = format & 0x7F;
|
||||
buf[1] = (format >> 8) & 0x7F;
|
||||
|
||||
@@ -35,84 +35,84 @@
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8_t *data;
|
||||
size_t used;
|
||||
size_t allocated;
|
||||
int dma_fd;
|
||||
uint8_t *data;
|
||||
size_t used;
|
||||
size_t allocated;
|
||||
int dma_fd;
|
||||
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned format;
|
||||
unsigned stride;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned format;
|
||||
unsigned stride;
|
||||
// Stride is a bytesperline in V4L2
|
||||
// https://www.kernel.org/doc/html/v4.14/media/uapi/v4l/pixfmt-v4l2.html
|
||||
// https://medium.com/@oleg.shipitko/what-does-stride-mean-in-image-processing-bba158a72bcd
|
||||
|
||||
bool online;
|
||||
bool key;
|
||||
bool online;
|
||||
bool key;
|
||||
|
||||
long double grab_ts;
|
||||
long double encode_begin_ts;
|
||||
long double encode_end_ts;
|
||||
} frame_s;
|
||||
} us_frame_s;
|
||||
|
||||
|
||||
#define FRAME_COPY_META(_src, _dest) { \
|
||||
_dest->width = _src->width; \
|
||||
_dest->height = _src->height; \
|
||||
_dest->format = _src->format; \
|
||||
_dest->stride = _src->stride; \
|
||||
_dest->online = _src->online; \
|
||||
_dest->key = _src->key; \
|
||||
_dest->grab_ts = _src->grab_ts; \
|
||||
_dest->encode_begin_ts = _src->encode_begin_ts; \
|
||||
_dest->encode_end_ts = _src->encode_end_ts; \
|
||||
#define US_FRAME_COPY_META(x_src, x_dest) { \
|
||||
x_dest->width = x_src->width; \
|
||||
x_dest->height = x_src->height; \
|
||||
x_dest->format = x_src->format; \
|
||||
x_dest->stride = x_src->stride; \
|
||||
x_dest->online = x_src->online; \
|
||||
x_dest->key = x_src->key; \
|
||||
x_dest->grab_ts = x_src->grab_ts; \
|
||||
x_dest->encode_begin_ts = x_src->encode_begin_ts; \
|
||||
x_dest->encode_end_ts = x_src->encode_end_ts; \
|
||||
}
|
||||
|
||||
static inline void frame_copy_meta(const frame_s *src, frame_s *dest) {
|
||||
FRAME_COPY_META(src, dest);
|
||||
static inline void us_frame_copy_meta(const us_frame_s *src, us_frame_s *dest) {
|
||||
US_FRAME_COPY_META(src, dest);
|
||||
}
|
||||
|
||||
#define FRAME_COMPARE_META_USED_NOTS(_a, _b) ( \
|
||||
_a->used == _b->used \
|
||||
&& _a->width == _b->width \
|
||||
&& _a->height == _b->height \
|
||||
&& _a->format == _b->format \
|
||||
&& _a->stride == _b->stride \
|
||||
&& _a->online == _b->online \
|
||||
&& _a->key == _b->key \
|
||||
#define US_FRAME_COMPARE_META_USED_NOTS(x_a, x_b) ( \
|
||||
x_a->used == x_b->used \
|
||||
&& x_a->width == x_b->width \
|
||||
&& x_a->height == x_b->height \
|
||||
&& x_a->format == x_b->format \
|
||||
&& x_a->stride == x_b->stride \
|
||||
&& x_a->online == x_b->online \
|
||||
&& x_a->key == x_b->key \
|
||||
)
|
||||
|
||||
|
||||
static inline void frame_encoding_begin(const frame_s *src, frame_s *dest, unsigned format) {
|
||||
static inline void us_frame_encoding_begin(const us_frame_s *src, us_frame_s *dest, unsigned format) {
|
||||
assert(src->used > 0);
|
||||
frame_copy_meta(src, dest);
|
||||
dest->encode_begin_ts = get_now_monotonic();
|
||||
us_frame_copy_meta(src, dest);
|
||||
dest->encode_begin_ts = us_get_now_monotonic();
|
||||
dest->format = format;
|
||||
dest->stride = 0;
|
||||
dest->used = 0;
|
||||
}
|
||||
|
||||
static inline void frame_encoding_end(frame_s *dest) {
|
||||
static inline void us_frame_encoding_end(us_frame_s *dest) {
|
||||
assert(dest->used > 0);
|
||||
dest->encode_end_ts = get_now_monotonic();
|
||||
dest->encode_end_ts = us_get_now_monotonic();
|
||||
}
|
||||
|
||||
|
||||
frame_s *frame_init(void);
|
||||
void frame_destroy(frame_s *frame);
|
||||
us_frame_s *us_frame_init(void);
|
||||
void us_frame_destroy(us_frame_s *frame);
|
||||
|
||||
void frame_realloc_data(frame_s *frame, size_t size);
|
||||
void frame_set_data(frame_s *frame, const uint8_t *data, size_t size);
|
||||
void frame_append_data(frame_s *frame, const uint8_t *data, size_t size);
|
||||
void us_frame_realloc_data(us_frame_s *frame, size_t size);
|
||||
void us_frame_set_data(us_frame_s *frame, const uint8_t *data, size_t size);
|
||||
void us_frame_append_data(us_frame_s *frame, const uint8_t *data, size_t size);
|
||||
|
||||
void frame_copy(const frame_s *src, frame_s *dest);
|
||||
bool frame_compare(const frame_s *a, const frame_s *b);
|
||||
void us_frame_copy(const us_frame_s *src, us_frame_s *dest);
|
||||
bool us_frame_compare(const us_frame_s *a, const us_frame_s *b);
|
||||
|
||||
unsigned frame_get_padding(const frame_s *frame);
|
||||
unsigned us_frame_get_padding(const us_frame_s *frame);
|
||||
|
||||
const char *fourcc_to_string(unsigned format, char *buf, size_t size);
|
||||
const char *us_fourcc_to_string(unsigned format, char *buf, size_t size);
|
||||
|
||||
static inline bool is_jpeg(unsigned format) {
|
||||
static inline bool us_is_jpeg(unsigned format) {
|
||||
return (format == V4L2_PIX_FMT_JPEG || format == V4L2_PIX_FMT_MJPEG);
|
||||
}
|
||||
|
||||
@@ -25,47 +25,47 @@
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
#define LIST_STRUCT(...) \
|
||||
#define US_LIST_STRUCT(...) \
|
||||
__VA_ARGS__ *prev; \
|
||||
__VA_ARGS__ *next;
|
||||
|
||||
#define LIST_ITERATE(_first, _item, ...) { \
|
||||
for (__typeof__(_first) _item = _first; _item;) { \
|
||||
__typeof__(_first) _next = _item->next; \
|
||||
#define US_LIST_ITERATE(x_first, x_item, ...) { \
|
||||
for (__typeof__(x_first) x_item = x_first; x_item;) { \
|
||||
__typeof__(x_first) m_next = x_item->next; \
|
||||
__VA_ARGS__ \
|
||||
_item = _next; \
|
||||
x_item = m_next; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define LIST_APPEND(_first, _item) { \
|
||||
if (_first == NULL) { \
|
||||
_first = _item; \
|
||||
#define US_LIST_APPEND(x_first, x_item) { \
|
||||
if (x_first == NULL) { \
|
||||
x_first = x_item; \
|
||||
} else { \
|
||||
__typeof__(_first) _last = _first; \
|
||||
for (; _last->next; _last = _last->next); \
|
||||
_item->prev = _last; \
|
||||
_last->next = _item; \
|
||||
__typeof__(x_first) m_last = x_first; \
|
||||
for (; m_last->next; m_last = m_last->next); \
|
||||
x_item->prev = m_last; \
|
||||
m_last->next = x_item; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define LIST_APPEND_C(_first, _item, _count) { \
|
||||
LIST_APPEND(_first, _item); \
|
||||
++(_count); \
|
||||
#define US_LIST_APPEND_C(x_first, x_item, x_count) { \
|
||||
US_LIST_APPEND(x_first, x_item); \
|
||||
++(x_count); \
|
||||
}
|
||||
|
||||
#define LIST_REMOVE(_first, _item) { \
|
||||
if (_item->prev == NULL) { \
|
||||
_first = _item->next; \
|
||||
#define US_LIST_REMOVE(x_first, x_item) { \
|
||||
if (x_item->prev == NULL) { \
|
||||
x_first = x_item->next; \
|
||||
} else { \
|
||||
_item->prev->next = _item->next; \
|
||||
x_item->prev->next = x_item->next; \
|
||||
} \
|
||||
if (_item->next != NULL) { \
|
||||
_item->next->prev = _item->prev; \
|
||||
if (x_item->next != NULL) { \
|
||||
x_item->next->prev = x_item->prev; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define LIST_REMOVE_C(_first, _item, _count) { \
|
||||
LIST_REMOVE(_first, _item); \
|
||||
assert((_count) >= 1); \
|
||||
--(_count); \
|
||||
#define US_LIST_REMOVE_C(x_first, x_item, x_count) { \
|
||||
US_LIST_REMOVE(x_first, x_item); \
|
||||
assert((x_count) >= 1); \
|
||||
--(x_count); \
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "logging.h"
|
||||
|
||||
|
||||
enum log_level_t us_log_level;
|
||||
enum us_log_level_t us_log_level;
|
||||
|
||||
bool us_log_colored;
|
||||
|
||||
|
||||
@@ -37,126 +37,126 @@
|
||||
#include "threading.h"
|
||||
|
||||
|
||||
enum log_level_t {
|
||||
LOG_LEVEL_INFO,
|
||||
LOG_LEVEL_PERF,
|
||||
LOG_LEVEL_VERBOSE,
|
||||
LOG_LEVEL_DEBUG,
|
||||
enum us_log_level_t {
|
||||
US_LOG_LEVEL_INFO,
|
||||
US_LOG_LEVEL_PERF,
|
||||
US_LOG_LEVEL_VERBOSE,
|
||||
US_LOG_LEVEL_DEBUG,
|
||||
};
|
||||
|
||||
|
||||
extern enum log_level_t us_log_level;
|
||||
extern enum us_log_level_t us_log_level;
|
||||
|
||||
extern bool us_log_colored;
|
||||
|
||||
extern pthread_mutex_t us_log_mutex;
|
||||
|
||||
|
||||
#define LOGGING_INIT { \
|
||||
us_log_level = LOG_LEVEL_INFO; \
|
||||
#define US_LOGGING_INIT { \
|
||||
us_log_level = US_LOG_LEVEL_INFO; \
|
||||
us_log_colored = isatty(2); \
|
||||
A_MUTEX_INIT(&us_log_mutex); \
|
||||
US_MUTEX_INIT(&us_log_mutex); \
|
||||
}
|
||||
|
||||
#define LOGGING_DESTROY A_MUTEX_DESTROY(&us_log_mutex)
|
||||
#define US_LOGGING_DESTROY US_MUTEX_DESTROY(&us_log_mutex)
|
||||
|
||||
#define LOGGING_LOCK A_MUTEX_LOCK(&us_log_mutex)
|
||||
#define LOGGING_UNLOCK A_MUTEX_UNLOCK(&us_log_mutex)
|
||||
#define US_LOGGING_LOCK US_MUTEX_LOCK(&us_log_mutex)
|
||||
#define US_LOGGING_UNLOCK US_MUTEX_UNLOCK(&us_log_mutex)
|
||||
|
||||
|
||||
#define COLOR_GRAY "\x1b[30;1m"
|
||||
#define COLOR_RED "\x1b[31;1m"
|
||||
#define COLOR_GREEN "\x1b[32;1m"
|
||||
#define COLOR_YELLOW "\x1b[33;1m"
|
||||
#define COLOR_BLUE "\x1b[34;1m"
|
||||
#define COLOR_CYAN "\x1b[36;1m"
|
||||
#define COLOR_RESET "\x1b[0m"
|
||||
#define US_COLOR_GRAY "\x1b[30;1m"
|
||||
#define US_COLOR_RED "\x1b[31;1m"
|
||||
#define US_COLOR_GREEN "\x1b[32;1m"
|
||||
#define US_COLOR_YELLOW "\x1b[33;1m"
|
||||
#define US_COLOR_BLUE "\x1b[34;1m"
|
||||
#define US_COLOR_CYAN "\x1b[36;1m"
|
||||
#define US_COLOR_RESET "\x1b[0m"
|
||||
|
||||
|
||||
#define SEP_INFO(_ch) { \
|
||||
LOGGING_LOCK; \
|
||||
for (int _i = 0; _i < 80; ++_i) { \
|
||||
fputc(_ch, stderr); \
|
||||
#define US_SEP_INFO(x_ch) { \
|
||||
US_LOGGING_LOCK; \
|
||||
for (int m_count = 0; m_count < 80; ++m_count) { \
|
||||
fputc((x_ch), stderr); \
|
||||
} \
|
||||
fputc('\n', stderr); \
|
||||
fflush(stderr); \
|
||||
LOGGING_UNLOCK; \
|
||||
US_LOGGING_UNLOCK; \
|
||||
}
|
||||
|
||||
#define SEP_DEBUG(_ch) { \
|
||||
if (us_log_level >= LOG_LEVEL_DEBUG) { \
|
||||
SEP_INFO(_ch); \
|
||||
#define US_SEP_DEBUG(x_ch) { \
|
||||
if (us_log_level >= US_LOG_LEVEL_DEBUG) { \
|
||||
US_SEP_INFO(x_ch); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
#define LOG_PRINTF_NOLOCK(_label_color, _label, _msg_color, _msg, ...) { \
|
||||
char _tname_buf[MAX_THREAD_NAME] = {0}; \
|
||||
thread_get_name(_tname_buf); \
|
||||
#define US_LOG_PRINTF_NOLOCK(x_label_color, x_label, x_msg_color, x_msg, ...) { \
|
||||
char m_tname_buf[US_MAX_THREAD_NAME] = {0}; \
|
||||
us_thread_get_name(m_tname_buf); \
|
||||
if (us_log_colored) { \
|
||||
fprintf(stderr, COLOR_GRAY "-- " _label_color _label COLOR_GRAY \
|
||||
" [%.03Lf %9s]" " -- " COLOR_RESET _msg_color _msg COLOR_RESET, \
|
||||
get_now_monotonic(), _tname_buf, ##__VA_ARGS__); \
|
||||
fprintf(stderr, US_COLOR_GRAY "-- " x_label_color x_label US_COLOR_GRAY \
|
||||
" [%.03Lf %9s]" " -- " US_COLOR_RESET x_msg_color x_msg US_COLOR_RESET, \
|
||||
us_get_now_monotonic(), m_tname_buf, ##__VA_ARGS__); \
|
||||
} else { \
|
||||
fprintf(stderr, "-- " _label " [%.03Lf %9s] -- " _msg, \
|
||||
get_now_monotonic(), _tname_buf, ##__VA_ARGS__); \
|
||||
fprintf(stderr, "-- " x_label " [%.03Lf %9s] -- " x_msg, \
|
||||
us_get_now_monotonic(), m_tname_buf, ##__VA_ARGS__); \
|
||||
} \
|
||||
fputc('\n', stderr); \
|
||||
fflush(stderr); \
|
||||
}
|
||||
|
||||
#define LOG_PRINTF(_label_color, _label, _msg_color, _msg, ...) { \
|
||||
LOGGING_LOCK; \
|
||||
LOG_PRINTF_NOLOCK(_label_color, _label, _msg_color, _msg, ##__VA_ARGS__); \
|
||||
LOGGING_UNLOCK; \
|
||||
#define US_LOG_PRINTF(x_label_color, x_label, x_msg_color, x_msg, ...) { \
|
||||
US_LOGGING_LOCK; \
|
||||
US_LOG_PRINTF_NOLOCK(x_label_color, x_label, x_msg_color, x_msg, ##__VA_ARGS__); \
|
||||
US_LOGGING_UNLOCK; \
|
||||
}
|
||||
|
||||
#define LOG_ERROR(_msg, ...) { \
|
||||
LOG_PRINTF(COLOR_RED, "ERROR", COLOR_RED, _msg, ##__VA_ARGS__); \
|
||||
#define US_LOG_ERROR(x_msg, ...) { \
|
||||
US_LOG_PRINTF(US_COLOR_RED, "ERROR", US_COLOR_RED, x_msg, ##__VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define LOG_PERROR(_msg, ...) { \
|
||||
char _perror_buf[1024] = {0}; \
|
||||
char *_perror_ptr = errno_to_string(errno, _perror_buf, 1024); \
|
||||
LOG_ERROR(_msg ": %s", ##__VA_ARGS__, _perror_ptr); \
|
||||
#define US_LOG_PERROR(x_msg, ...) { \
|
||||
char m_perror_buf[1024] = {0}; \
|
||||
char *m_perror_ptr = us_errno_to_string(errno, m_perror_buf, 1024); \
|
||||
US_LOG_ERROR(x_msg ": %s", ##__VA_ARGS__, m_perror_ptr); \
|
||||
}
|
||||
|
||||
#define LOG_INFO(_msg, ...) { \
|
||||
LOG_PRINTF(COLOR_GREEN, "INFO ", "", _msg, ##__VA_ARGS__); \
|
||||
#define US_LOG_INFO(x_msg, ...) { \
|
||||
US_LOG_PRINTF(US_COLOR_GREEN, "INFO ", "", x_msg, ##__VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define LOG_INFO_NOLOCK(_msg, ...) { \
|
||||
LOG_PRINTF_NOLOCK(COLOR_GREEN, "INFO ", "", _msg, ##__VA_ARGS__); \
|
||||
#define US_LOG_INFO_NOLOCK(x_msg, ...) { \
|
||||
US_LOG_PRINTF_NOLOCK(US_COLOR_GREEN, "INFO ", "", x_msg, ##__VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define LOG_PERF(_msg, ...) { \
|
||||
if (us_log_level >= LOG_LEVEL_PERF) { \
|
||||
LOG_PRINTF(COLOR_CYAN, "PERF ", COLOR_CYAN, _msg, ##__VA_ARGS__); \
|
||||
#define US_LOG_PERF(x_msg, ...) { \
|
||||
if (us_log_level >= US_LOG_LEVEL_PERF) { \
|
||||
US_LOG_PRINTF(US_COLOR_CYAN, "PERF ", US_COLOR_CYAN, x_msg, ##__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define LOG_PERF_FPS(_msg, ...) { \
|
||||
if (us_log_level >= LOG_LEVEL_PERF) { \
|
||||
LOG_PRINTF(COLOR_YELLOW, "PERF ", COLOR_YELLOW, _msg, ##__VA_ARGS__); \
|
||||
#define US_LOG_PERF_FPS(x_msg, ...) { \
|
||||
if (us_log_level >= US_LOG_LEVEL_PERF) { \
|
||||
US_LOG_PRINTF(US_COLOR_YELLOW, "PERF ", US_COLOR_YELLOW, x_msg, ##__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define LOG_VERBOSE(_msg, ...) { \
|
||||
if (us_log_level >= LOG_LEVEL_VERBOSE) { \
|
||||
LOG_PRINTF(COLOR_BLUE, "VERB ", COLOR_BLUE, _msg, ##__VA_ARGS__); \
|
||||
#define US_LOG_VERBOSE(x_msg, ...) { \
|
||||
if (us_log_level >= US_LOG_LEVEL_VERBOSE) { \
|
||||
US_LOG_PRINTF(US_COLOR_BLUE, "VERB ", US_COLOR_BLUE, x_msg, ##__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define LOG_VERBOSE_PERROR(_msg, ...) { \
|
||||
if (us_log_level >= LOG_LEVEL_VERBOSE) { \
|
||||
char _perror_buf[1024] = {0}; \
|
||||
char *_perror_ptr = errno_to_string(errno, _perror_buf, 1023); \
|
||||
LOG_PRINTF(COLOR_BLUE, "VERB ", COLOR_BLUE, _msg ": %s", ##__VA_ARGS__, _perror_ptr); \
|
||||
#define US_LOG_VERBOSE_PERROR(x_msg, ...) { \
|
||||
if (us_log_level >= US_LOG_LEVEL_VERBOSE) { \
|
||||
char m_perror_buf[1024] = {0}; \
|
||||
char *m_perror_ptr = us_errno_to_string(errno, m_perror_buf, 1023); \
|
||||
US_LOG_PRINTF(US_COLOR_BLUE, "VERB ", US_COLOR_BLUE, x_msg ": %s", ##__VA_ARGS__, m_perror_ptr); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define LOG_DEBUG(_msg, ...) { \
|
||||
if (us_log_level >= LOG_LEVEL_DEBUG) { \
|
||||
LOG_PRINTF(COLOR_GRAY, "DEBUG", COLOR_GRAY, _msg, ##__VA_ARGS__); \
|
||||
#define US_LOG_DEBUG(x_msg, ...) { \
|
||||
if (us_log_level >= US_LOG_LEVEL_DEBUG) { \
|
||||
US_LOG_PRINTF(US_COLOR_GRAY, "DEBUG", US_COLOR_GRAY, x_msg, ##__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
#include "memsink.h"
|
||||
|
||||
|
||||
memsink_s *memsink_init(
|
||||
us_memsink_s *us_memsink_init(
|
||||
const char *name, const char *obj, bool server,
|
||||
mode_t mode, bool rm, unsigned client_ttl, unsigned timeout) {
|
||||
|
||||
memsink_s *sink;
|
||||
A_CALLOC(sink, 1);
|
||||
us_memsink_s *sink;
|
||||
US_CALLOC(sink, 1);
|
||||
sink->name = name;
|
||||
sink->obj = obj;
|
||||
sink->server = server;
|
||||
@@ -39,54 +39,54 @@ memsink_s *memsink_init(
|
||||
sink->mem = MAP_FAILED;
|
||||
atomic_init(&sink->has_clients, false);
|
||||
|
||||
LOG_INFO("Using %s-sink: %s", name, obj);
|
||||
US_LOG_INFO("Using %s-sink: %s", name, obj);
|
||||
|
||||
mode_t mask = umask(0);
|
||||
sink->fd = shm_open(sink->obj, (server ? O_RDWR | O_CREAT : O_RDWR), mode);
|
||||
umask(mask);
|
||||
if (sink->fd == -1) {
|
||||
umask(mask);
|
||||
LOG_PERROR("%s-sink: Can't open shared memory", name);
|
||||
US_LOG_PERROR("%s-sink: Can't open shared memory", name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (sink->server && ftruncate(sink->fd, sizeof(memsink_shared_s)) < 0) {
|
||||
LOG_PERROR("%s-sink: Can't truncate shared memory", name);
|
||||
if (sink->server && ftruncate(sink->fd, sizeof(us_memsink_shared_s)) < 0) {
|
||||
US_LOG_PERROR("%s-sink: Can't truncate shared memory", name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((sink->mem = memsink_shared_map(sink->fd)) == NULL) {
|
||||
LOG_PERROR("%s-sink: Can't mmap shared memory", name);
|
||||
if ((sink->mem = us_memsink_shared_map(sink->fd)) == NULL) {
|
||||
US_LOG_PERROR("%s-sink: Can't mmap shared memory", name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return sink;
|
||||
|
||||
error:
|
||||
memsink_destroy(sink);
|
||||
us_memsink_destroy(sink);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void memsink_destroy(memsink_s *sink) {
|
||||
void us_memsink_destroy(us_memsink_s *sink) {
|
||||
if (sink->mem != MAP_FAILED) {
|
||||
if (memsink_shared_unmap(sink->mem) < 0) {
|
||||
LOG_PERROR("%s-sink: Can't unmap shared memory", sink->name);
|
||||
if (us_memsink_shared_unmap(sink->mem) < 0) {
|
||||
US_LOG_PERROR("%s-sink: Can't unmap shared memory", sink->name);
|
||||
}
|
||||
}
|
||||
if (sink->fd >= 0) {
|
||||
if (close(sink->fd) < 0) {
|
||||
LOG_PERROR("%s-sink: Can't close shared memory fd", sink->name);
|
||||
US_LOG_PERROR("%s-sink: Can't close shared memory fd", sink->name);
|
||||
}
|
||||
if (sink->rm && shm_unlink(sink->obj) < 0) {
|
||||
if (errno != ENOENT) {
|
||||
LOG_PERROR("%s-sink: Can't remove shared memory", sink->name);
|
||||
US_LOG_PERROR("%s-sink: Can't remove shared memory", sink->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(sink);
|
||||
}
|
||||
|
||||
bool memsink_server_check(memsink_s *sink, const frame_s *frame) {
|
||||
bool us_memsink_server_check(us_memsink_s *sink, const us_frame_s *frame) {
|
||||
// Return true (the need to write to memsink) on any of these conditions:
|
||||
// - EWOULDBLOCK - we have an active client;
|
||||
// - Incorrect magic or version - need to first write;
|
||||
@@ -100,97 +100,97 @@ bool memsink_server_check(memsink_s *sink, const frame_s *frame) {
|
||||
atomic_store(&sink->has_clients, true);
|
||||
return true;
|
||||
}
|
||||
LOG_PERROR("%s-sink: Can't lock memory", sink->name);
|
||||
US_LOG_PERROR("%s-sink: Can't lock memory", sink->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sink->mem->magic != MEMSINK_MAGIC || sink->mem->version != MEMSINK_VERSION) {
|
||||
if (sink->mem->magic != US_MEMSINK_MAGIC || sink->mem->version != US_MEMSINK_VERSION) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool has_clients = (sink->mem->last_client_ts + sink->client_ttl > get_now_monotonic());
|
||||
bool has_clients = (sink->mem->last_client_ts + sink->client_ttl > us_get_now_monotonic());
|
||||
atomic_store(&sink->has_clients, has_clients);
|
||||
|
||||
if (flock(sink->fd, LOCK_UN) < 0) {
|
||||
LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
|
||||
US_LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
|
||||
return false;
|
||||
}
|
||||
return (has_clients || !FRAME_COMPARE_META_USED_NOTS(sink->mem, frame));;
|
||||
return (has_clients || !US_FRAME_COMPARE_META_USED_NOTS(sink->mem, frame));;
|
||||
}
|
||||
|
||||
int memsink_server_put(memsink_s *sink, const frame_s *frame) {
|
||||
int us_memsink_server_put(us_memsink_s *sink, const us_frame_s *frame) {
|
||||
assert(sink->server);
|
||||
|
||||
const long double now = get_now_monotonic();
|
||||
const long double now = us_get_now_monotonic();
|
||||
|
||||
if (frame->used > MEMSINK_MAX_DATA) {
|
||||
LOG_ERROR("%s-sink: Can't put frame: is too big (%zu > %zu)",
|
||||
sink->name, frame->used, MEMSINK_MAX_DATA);
|
||||
if (frame->used > US_MEMSINK_MAX_DATA) {
|
||||
US_LOG_ERROR("%s-sink: Can't put frame: is too big (%zu > %zu)",
|
||||
sink->name, frame->used, US_MEMSINK_MAX_DATA);
|
||||
return 0; // -2
|
||||
}
|
||||
|
||||
if (flock_timedwait_monotonic(sink->fd, 1) == 0) {
|
||||
LOG_VERBOSE("%s-sink: >>>>> Exposing new frame ...", sink->name);
|
||||
if (us_flock_timedwait_monotonic(sink->fd, 1) == 0) {
|
||||
US_LOG_VERBOSE("%s-sink: >>>>> Exposing new frame ...", sink->name);
|
||||
|
||||
sink->last_id = get_now_id();
|
||||
sink->last_id = us_get_now_id();
|
||||
sink->mem->id = sink->last_id;
|
||||
|
||||
memcpy(sink->mem->data, frame->data, frame->used);
|
||||
sink->mem->used = frame->used;
|
||||
FRAME_COPY_META(frame, sink->mem);
|
||||
US_FRAME_COPY_META(frame, sink->mem);
|
||||
|
||||
sink->mem->magic = MEMSINK_MAGIC;
|
||||
sink->mem->version = MEMSINK_VERSION;
|
||||
atomic_store(&sink->has_clients, (sink->mem->last_client_ts + sink->client_ttl > get_now_monotonic()));
|
||||
sink->mem->magic = US_MEMSINK_MAGIC;
|
||||
sink->mem->version = US_MEMSINK_VERSION;
|
||||
atomic_store(&sink->has_clients, (sink->mem->last_client_ts + sink->client_ttl > us_get_now_monotonic()));
|
||||
|
||||
if (flock(sink->fd, LOCK_UN) < 0) {
|
||||
LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
|
||||
US_LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
|
||||
return -1;
|
||||
}
|
||||
LOG_VERBOSE("%s-sink: Exposed new frame; full exposition time = %.3Lf",
|
||||
sink->name, get_now_monotonic() - now);
|
||||
US_LOG_VERBOSE("%s-sink: Exposed new frame; full exposition time = %.3Lf",
|
||||
sink->name, us_get_now_monotonic() - now);
|
||||
|
||||
} else if (errno == EWOULDBLOCK) {
|
||||
LOG_VERBOSE("%s-sink: ===== Shared memory is busy now; frame skipped", sink->name);
|
||||
US_LOG_VERBOSE("%s-sink: ===== Shared memory is busy now; frame skipped", sink->name);
|
||||
|
||||
} else {
|
||||
LOG_PERROR("%s-sink: Can't lock memory", sink->name);
|
||||
US_LOG_PERROR("%s-sink: Can't lock memory", sink->name);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int memsink_client_get(memsink_s *sink, frame_s *frame) { // cppcheck-suppress unusedFunction
|
||||
int us_memsink_client_get(us_memsink_s *sink, us_frame_s *frame) { // cppcheck-suppress unusedFunction
|
||||
assert(!sink->server); // Client only
|
||||
|
||||
if (flock_timedwait_monotonic(sink->fd, sink->timeout) < 0) {
|
||||
if (us_flock_timedwait_monotonic(sink->fd, sink->timeout) < 0) {
|
||||
if (errno == EWOULDBLOCK) {
|
||||
return -2;
|
||||
}
|
||||
LOG_PERROR("%s-sink: Can't lock memory", sink->name);
|
||||
US_LOG_PERROR("%s-sink: Can't lock memory", sink->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int retval = -2; // Not updated
|
||||
if (sink->mem->magic == MEMSINK_MAGIC) {
|
||||
if (sink->mem->version != MEMSINK_VERSION) {
|
||||
LOG_ERROR("%s-sink: Protocol version mismatch: sink=%u, required=%u",
|
||||
sink->name, sink->mem->version, MEMSINK_VERSION);
|
||||
if (sink->mem->magic == US_MEMSINK_MAGIC) {
|
||||
if (sink->mem->version != US_MEMSINK_VERSION) {
|
||||
US_LOG_ERROR("%s-sink: Protocol version mismatch: sink=%u, required=%u",
|
||||
sink->name, sink->mem->version, US_MEMSINK_VERSION);
|
||||
retval = -1;
|
||||
goto done;
|
||||
}
|
||||
if (sink->mem->id != sink->last_id) { // When updated
|
||||
sink->last_id = sink->mem->id;
|
||||
frame_set_data(frame, sink->mem->data, sink->mem->used);
|
||||
FRAME_COPY_META(sink->mem, frame);
|
||||
us_frame_set_data(frame, sink->mem->data, sink->mem->used);
|
||||
US_FRAME_COPY_META(sink->mem, frame);
|
||||
retval = 0;
|
||||
}
|
||||
sink->mem->last_client_ts = get_now_monotonic();
|
||||
sink->mem->last_client_ts = us_get_now_monotonic();
|
||||
}
|
||||
|
||||
done:
|
||||
if (flock(sink->fd, LOCK_UN) < 0) {
|
||||
LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
|
||||
US_LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
|
||||
return -1;
|
||||
}
|
||||
return retval;
|
||||
|
||||
@@ -42,27 +42,27 @@
|
||||
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const char *obj;
|
||||
bool server;
|
||||
bool rm;
|
||||
unsigned client_ttl; // Only for server
|
||||
unsigned timeout;
|
||||
const char *name;
|
||||
const char *obj;
|
||||
bool server;
|
||||
bool rm;
|
||||
unsigned client_ttl; // Only for server
|
||||
unsigned timeout;
|
||||
|
||||
int fd;
|
||||
memsink_shared_s *mem;
|
||||
us_memsink_shared_s *mem;
|
||||
uint64_t last_id;
|
||||
atomic_bool has_clients; // Only for server
|
||||
} memsink_s;
|
||||
} us_memsink_s;
|
||||
|
||||
|
||||
memsink_s *memsink_init(
|
||||
us_memsink_s *us_memsink_init(
|
||||
const char *name, const char *obj, bool server,
|
||||
mode_t mode, bool rm, unsigned client_ttl, unsigned timeout);
|
||||
|
||||
void memsink_destroy(memsink_s *sink);
|
||||
void us_memsink_destroy(us_memsink_s *sink);
|
||||
|
||||
bool memsink_server_check(memsink_s *sink, const frame_s *frame);
|
||||
int memsink_server_put(memsink_s *sink, const frame_s *frame);
|
||||
bool us_memsink_server_check(us_memsink_s *sink, const us_frame_s *frame);
|
||||
int us_memsink_server_put(us_memsink_s *sink, const us_frame_s *frame);
|
||||
|
||||
int memsink_client_get(memsink_s *sink, frame_s *frame);
|
||||
int us_memsink_client_get(us_memsink_s *sink, us_frame_s *frame);
|
||||
|
||||
@@ -29,13 +29,13 @@
|
||||
#include <sys/mman.h>
|
||||
|
||||
|
||||
#define MEMSINK_MAGIC ((uint64_t)0xCAFEBABECAFEBABE)
|
||||
#define MEMSINK_VERSION ((uint32_t)2)
|
||||
#define US_MEMSINK_MAGIC ((uint64_t)0xCAFEBABECAFEBABE)
|
||||
#define US_MEMSINK_VERSION ((uint32_t)2)
|
||||
|
||||
#ifndef CFG_MEMSINK_MAX_DATA
|
||||
# define CFG_MEMSINK_MAX_DATA 33554432
|
||||
#ifndef US_CFG_MEMSINK_MAX_DATA
|
||||
# define US_CFG_MEMSINK_MAX_DATA 33554432
|
||||
#endif
|
||||
#define MEMSINK_MAX_DATA ((size_t)(CFG_MEMSINK_MAX_DATA))
|
||||
#define US_MEMSINK_MAX_DATA ((size_t)(US_CFG_MEMSINK_MAX_DATA))
|
||||
|
||||
|
||||
typedef struct {
|
||||
@@ -58,14 +58,14 @@ typedef struct {
|
||||
|
||||
long double last_client_ts;
|
||||
|
||||
uint8_t data[MEMSINK_MAX_DATA];
|
||||
} memsink_shared_s;
|
||||
uint8_t data[US_MEMSINK_MAX_DATA];
|
||||
} us_memsink_shared_s;
|
||||
|
||||
|
||||
INLINE memsink_shared_s *memsink_shared_map(int fd) {
|
||||
memsink_shared_s *mem = mmap(
|
||||
INLINE us_memsink_shared_s *us_memsink_shared_map(int fd) {
|
||||
us_memsink_shared_s *mem = mmap(
|
||||
NULL,
|
||||
sizeof(memsink_shared_s),
|
||||
sizeof(us_memsink_shared_s),
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
fd,
|
||||
@@ -78,7 +78,7 @@ INLINE memsink_shared_s *memsink_shared_map(int fd) {
|
||||
return mem;
|
||||
}
|
||||
|
||||
INLINE int memsink_shared_unmap(memsink_shared_s *mem) {
|
||||
INLINE int us_memsink_shared_unmap(us_memsink_shared_s *mem) {
|
||||
assert(mem != NULL);
|
||||
return munmap(mem, sizeof(memsink_shared_s));
|
||||
return munmap(mem, sizeof(us_memsink_shared_s));
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "options.h"
|
||||
|
||||
|
||||
void build_short_options(const struct option opts[], char *short_opts, size_t size) {
|
||||
void us_build_short_options(const struct option opts[], char *short_opts, size_t size) {
|
||||
memset(short_opts, 0, size);
|
||||
for (unsigned short_index = 0, opt_index = 0; opts[opt_index].name != NULL; ++opt_index) {
|
||||
assert(short_index < size - 3);
|
||||
|
||||
@@ -30,4 +30,4 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
void build_short_options(const struct option opts[], char *short_opts, size_t size);
|
||||
void us_build_short_options(const struct option opts[], char *short_opts, size_t size);
|
||||
|
||||
@@ -72,7 +72,7 @@ extern char **environ;
|
||||
|
||||
|
||||
#ifdef HAS_PDEATHSIG
|
||||
INLINE int process_track_parent_death(void) {
|
||||
INLINE int us_process_track_parent_death(void) {
|
||||
pid_t parent = getppid();
|
||||
int signum = SIGTERM;
|
||||
# if defined(__linux__)
|
||||
@@ -83,12 +83,12 @@ INLINE int process_track_parent_death(void) {
|
||||
# error WTF?
|
||||
# endif
|
||||
if (retval < 0) {
|
||||
LOG_PERROR("Can't set to receive SIGTERM on parent process death");
|
||||
US_LOG_PERROR("Can't set to receive SIGTERM on parent process death");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (kill(parent, 0) < 0) {
|
||||
LOG_PERROR("The parent process %d is already dead", parent);
|
||||
US_LOG_PERROR("The parent process %d is already dead", parent);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -99,21 +99,21 @@ INLINE int process_track_parent_death(void) {
|
||||
#ifdef WITH_SETPROCTITLE
|
||||
# pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
# pragma GCC diagnostic push
|
||||
INLINE void process_set_name_prefix(int argc, char *argv[], const char *prefix) {
|
||||
INLINE void us_process_set_name_prefix(int argc, char *argv[], const char *prefix) {
|
||||
# pragma GCC diagnostic pop
|
||||
|
||||
char *cmdline = NULL;
|
||||
size_t allocated = 2048;
|
||||
size_t used = 0;
|
||||
|
||||
A_REALLOC(cmdline, allocated);
|
||||
US_REALLOC(cmdline, allocated);
|
||||
cmdline[0] = '\0';
|
||||
|
||||
for (int index = 0; index < argc; ++index) {
|
||||
size_t arg_len = strlen(argv[index]);
|
||||
if (used + arg_len + 16 >= allocated) {
|
||||
allocated += arg_len + 2048;
|
||||
A_REALLOC(cmdline, allocated); // cppcheck-suppress memleakOnRealloc // False-positive (ok with assert)
|
||||
US_REALLOC(cmdline, allocated); // cppcheck-suppress memleakOnRealloc // False-positive (ok with assert)
|
||||
}
|
||||
|
||||
strcat(cmdline, " ");
|
||||
@@ -130,18 +130,18 @@ INLINE void process_set_name_prefix(int argc, char *argv[], const char *prefix)
|
||||
}
|
||||
#endif
|
||||
|
||||
INLINE void process_notify_parent(void) {
|
||||
INLINE void us_process_notify_parent(void) {
|
||||
pid_t parent = getppid();
|
||||
|
||||
if (kill(parent, SIGUSR2) < 0) {
|
||||
LOG_PERROR("Can't send SIGUSR2 to the parent process %d", parent);
|
||||
US_LOG_PERROR("Can't send SIGUSR2 to the parent process %d", parent);
|
||||
}
|
||||
}
|
||||
|
||||
INLINE void process_suicide(void) {
|
||||
INLINE void us_process_suicide(void) {
|
||||
pid_t pid = getpid();
|
||||
|
||||
if (kill(pid, SIGTERM) < 0) {
|
||||
LOG_PERROR("Can't send SIGTERM to own pid %d", pid);
|
||||
US_LOG_PERROR("Can't send SIGTERM to own pid %d", pid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,37 +41,37 @@
|
||||
|
||||
|
||||
#ifdef PTHREAD_MAX_NAMELEN_NP
|
||||
# define MAX_THREAD_NAME ((size_t)(PTHREAD_MAX_NAMELEN_NP))
|
||||
# define US_MAX_THREAD_NAME ((size_t)(PTHREAD_MAX_NAMELEN_NP))
|
||||
#else
|
||||
# define MAX_THREAD_NAME ((size_t)16)
|
||||
# define US_MAX_THREAD_NAME ((size_t)16)
|
||||
#endif
|
||||
|
||||
#define A_THREAD_CREATE(_tid, _func, _arg) assert(!pthread_create(_tid, NULL, _func, _arg))
|
||||
#define A_THREAD_JOIN(_tid) assert(!pthread_join(_tid, NULL))
|
||||
#define US_THREAD_CREATE(x_tid, x_func, x_arg) assert(!pthread_create((x_tid), NULL, (x_func), (x_arg)))
|
||||
#define US_THREAD_JOIN(x_tid) assert(!pthread_join((x_tid), NULL))
|
||||
|
||||
#ifdef WITH_PTHREAD_NP
|
||||
# define A_THREAD_RENAME(_fmt, ...) { \
|
||||
char _new_tname_buf[MAX_THREAD_NAME] = {0}; \
|
||||
assert(snprintf(_new_tname_buf, MAX_THREAD_NAME, _fmt, ##__VA_ARGS__) > 0); \
|
||||
thread_set_name(_new_tname_buf); \
|
||||
# define US_THREAD_RENAME(x_fmt, ...) { \
|
||||
char m_new_tname_buf[US_MAX_THREAD_NAME] = {0}; \
|
||||
assert(snprintf(m_new_tname_buf, US_MAX_THREAD_NAME, (x_fmt), ##__VA_ARGS__) > 0); \
|
||||
us_thread_set_name(m_new_tname_buf); \
|
||||
}
|
||||
#else
|
||||
# define A_THREAD_RENAME(_fmt, ...)
|
||||
# define US_THREAD_RENAME(_fmt, ...)
|
||||
#endif
|
||||
|
||||
#define A_MUTEX_INIT(_mutex) assert(!pthread_mutex_init(_mutex, NULL))
|
||||
#define A_MUTEX_DESTROY(_mutex) assert(!pthread_mutex_destroy(_mutex))
|
||||
#define A_MUTEX_LOCK(_mutex) assert(!pthread_mutex_lock(_mutex))
|
||||
#define A_MUTEX_UNLOCK(_mutex) assert(!pthread_mutex_unlock(_mutex))
|
||||
#define US_MUTEX_INIT(x_mutex) assert(!pthread_mutex_init((x_mutex), NULL))
|
||||
#define US_MUTEX_DESTROY(x_mutex) assert(!pthread_mutex_destroy(x_mutex))
|
||||
#define US_MUTEX_LOCK(x_mutex) assert(!pthread_mutex_lock(x_mutex))
|
||||
#define US_MUTEX_UNLOCK(x_mutex) assert(!pthread_mutex_unlock(x_mutex))
|
||||
|
||||
#define A_COND_INIT(_cond) assert(!pthread_cond_init(_cond, NULL))
|
||||
#define A_COND_DESTROY(_cond) assert(!pthread_cond_destroy(_cond))
|
||||
#define A_COND_SIGNAL(...) assert(!pthread_cond_signal(__VA_ARGS__))
|
||||
#define A_COND_WAIT_TRUE(_var, _cond, _mutex) { while(!(_var)) assert(!pthread_cond_wait(_cond, _mutex)); }
|
||||
#define US_COND_INIT(x_cond) assert(!pthread_cond_init((x_cond), NULL))
|
||||
#define US_COND_DESTROY(x_cond) assert(!pthread_cond_destroy(x_cond))
|
||||
#define US_COND_SIGNAL(...) assert(!pthread_cond_signal(__VA_ARGS__))
|
||||
#define US_COND_WAIT_TRUE(x_var, x_cond, x_mutex) { while(!(x_var)) assert(!pthread_cond_wait((x_cond), (x_mutex))); }
|
||||
|
||||
|
||||
#ifdef WITH_PTHREAD_NP
|
||||
INLINE void thread_set_name(const char *name) {
|
||||
INLINE void us_thread_set_name(const char *name) {
|
||||
# if defined(__linux__)
|
||||
pthread_setname_np(pthread_self(), name);
|
||||
# elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
||||
@@ -79,26 +79,26 @@ INLINE void thread_set_name(const char *name) {
|
||||
# elif defined(__NetBSD__)
|
||||
pthread_setname_np(pthread_self(), "%s", (void *)name);
|
||||
# else
|
||||
# error thread_set_name() not implemented, you can disable it using WITH_PTHREAD_NP=0
|
||||
# error us_thread_set_name() not implemented, you can disable it using WITH_PTHREAD_NP=0
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
INLINE void thread_get_name(char *name) { // Always required for logging
|
||||
INLINE void us_thread_get_name(char *name) { // Always required for logging
|
||||
#ifdef WITH_PTHREAD_NP
|
||||
int retval = -1;
|
||||
# if defined(__linux__) || defined (__NetBSD__)
|
||||
retval = pthread_getname_np(pthread_self(), name, MAX_THREAD_NAME);
|
||||
retval = pthread_getname_np(pthread_self(), name, US_MAX_THREAD_NAME);
|
||||
# elif \
|
||||
(defined(__FreeBSD__) && defined(__FreeBSD_version) && __FreeBSD_version >= 1103500) \
|
||||
|| (defined(__OpenBSD__) && defined(OpenBSD) && OpenBSD >= 201905) \
|
||||
|| defined(__DragonFly__)
|
||||
pthread_get_name_np(pthread_self(), name, MAX_THREAD_NAME);
|
||||
pthread_get_name_np(pthread_self(), name, US_MAX_THREAD_NAME);
|
||||
if (name[0] != '\0') {
|
||||
retval = 0;
|
||||
}
|
||||
# else
|
||||
# error thread_get_name() not implemented, you can disable it using WITH_PTHREAD_NP=0
|
||||
# error us_thread_get_name() not implemented, you can disable it using WITH_PTHREAD_NP=0
|
||||
# endif
|
||||
if (retval < 0) {
|
||||
#endif
|
||||
@@ -117,7 +117,7 @@ INLINE void thread_get_name(char *name) { // Always required for logging
|
||||
pid_t tid = 0; // Makes cppcheck happy
|
||||
# warning gettid() not implemented
|
||||
#endif
|
||||
assert(snprintf(name, MAX_THREAD_NAME, "tid=%d", tid) > 0);
|
||||
assert(snprintf(name, US_MAX_THREAD_NAME, "tid=%d", tid) > 0);
|
||||
|
||||
#ifdef WITH_PTHREAD_NP
|
||||
}
|
||||
|
||||
@@ -53,37 +53,37 @@
|
||||
#define INLINE inline __attribute__((always_inline))
|
||||
#define UNUSED __attribute__((unused))
|
||||
|
||||
#define A_CALLOC(_dest, _nmemb) assert((_dest = calloc(_nmemb, sizeof(*(_dest)))))
|
||||
#define A_REALLOC(_dest, _nmemb) assert((_dest = realloc(_dest, _nmemb * sizeof(*(_dest)))))
|
||||
#define DELETE(_dest, _free) { if (_dest) { _free(_dest); } }
|
||||
#define MEMSET_ZERO(_obj) memset(&(_obj), 0, sizeof(_obj))
|
||||
#define US_CALLOC(x_dest, x_nmemb) assert(((x_dest) = calloc((x_nmemb), sizeof(*(x_dest)))))
|
||||
#define US_REALLOC(x_dest, x_nmemb) assert(((x_dest) = realloc((x_dest), (x_nmemb) * sizeof(*(x_dest)))))
|
||||
#define US_DELETE(x_dest, x_free) { if (x_dest) { x_free(x_dest); } }
|
||||
#define US_MEMSET_ZERO(x_obj) memset(&(x_obj), 0, sizeof(x_obj))
|
||||
|
||||
#define A_ASPRINTF(_dest, _fmt, ...) assert(asprintf(&(_dest), _fmt, ##__VA_ARGS__) >= 0)
|
||||
#define US_ASPRINTF(x_dest, x_fmt, ...) assert(asprintf(&(x_dest), (x_fmt), ##__VA_ARGS__) >= 0)
|
||||
|
||||
#define ARRAY_LEN(_array) (sizeof(_array) / sizeof(_array[0]))
|
||||
#define US_ARRAY_LEN(x_array) (sizeof(x_array) / sizeof((x_array)[0]))
|
||||
|
||||
|
||||
INLINE const char *bool_to_string(bool flag) {
|
||||
INLINE const char *us_bool_to_string(bool flag) {
|
||||
return (flag ? "true" : "false");
|
||||
}
|
||||
|
||||
INLINE size_t align_size(size_t size, size_t to) {
|
||||
INLINE size_t us_align_size(size_t size, size_t to) {
|
||||
return ((size + (to - 1)) & ~(to - 1));
|
||||
}
|
||||
|
||||
INLINE unsigned min_u(unsigned a, unsigned b) {
|
||||
INLINE unsigned us_min_u(unsigned a, unsigned b) {
|
||||
return (a < b ? a : b);
|
||||
}
|
||||
|
||||
INLINE unsigned max_u(unsigned a, unsigned b) {
|
||||
INLINE unsigned us_max_u(unsigned a, unsigned b) {
|
||||
return (a > b ? a : b);
|
||||
}
|
||||
|
||||
INLINE long long floor_ms(long double now) {
|
||||
INLINE long long us_floor_ms(long double now) {
|
||||
return (long long)now - (now < (long long)now); // floor()
|
||||
}
|
||||
|
||||
INLINE uint32_t triple_u32(uint32_t x) {
|
||||
INLINE uint32_t us_triple_u32(uint32_t x) {
|
||||
// https://nullprogram.com/blog/2018/07/31/
|
||||
x ^= x >> 17;
|
||||
x *= UINT32_C(0xED5AD4BB);
|
||||
@@ -95,7 +95,7 @@ INLINE uint32_t triple_u32(uint32_t x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
INLINE void get_now(clockid_t clk_id, time_t *sec, long *msec) {
|
||||
INLINE void us_get_now(clockid_t clk_id, time_t *sec, long *msec) {
|
||||
struct timespec ts;
|
||||
assert(!clock_gettime(clk_id, &ts));
|
||||
*sec = ts.tv_sec;
|
||||
@@ -108,47 +108,47 @@ INLINE void get_now(clockid_t clk_id, time_t *sec, long *msec) {
|
||||
}
|
||||
|
||||
#if defined(CLOCK_MONOTONIC_RAW)
|
||||
# define X_CLOCK_MONOTONIC CLOCK_MONOTONIC_RAW
|
||||
# define _X_CLOCK_MONOTONIC CLOCK_MONOTONIC_RAW
|
||||
#elif defined(CLOCK_MONOTONIC_FAST)
|
||||
# define X_CLOCK_MONOTONIC CLOCK_MONOTONIC_FAST
|
||||
# define _X_CLOCK_MONOTONIC CLOCK_MONOTONIC_FAST
|
||||
#else
|
||||
# define X_CLOCK_MONOTONIC CLOCK_MONOTONIC
|
||||
# define _X_CLOCK_MONOTONIC CLOCK_MONOTONIC
|
||||
#endif
|
||||
|
||||
INLINE long double get_now_monotonic(void) {
|
||||
INLINE long double us_get_now_monotonic(void) {
|
||||
time_t sec;
|
||||
long msec;
|
||||
get_now(X_CLOCK_MONOTONIC, &sec, &msec);
|
||||
us_get_now(_X_CLOCK_MONOTONIC, &sec, &msec);
|
||||
return (long double)sec + ((long double)msec) / 1000;
|
||||
}
|
||||
|
||||
INLINE uint64_t get_now_monotonic_u64(void) {
|
||||
INLINE uint64_t us_get_now_monotonic_u64(void) {
|
||||
struct timespec ts;
|
||||
assert(!clock_gettime(X_CLOCK_MONOTONIC, &ts));
|
||||
assert(!clock_gettime(_X_CLOCK_MONOTONIC, &ts));
|
||||
return (uint64_t)(ts.tv_nsec / 1000) + (uint64_t)ts.tv_sec * 1000000;
|
||||
}
|
||||
|
||||
#undef X_CLOCK_MONOTONIC
|
||||
#undef _X_CLOCK_MONOTONIC
|
||||
|
||||
INLINE uint64_t get_now_id(void) {
|
||||
uint64_t now = get_now_monotonic_u64();
|
||||
return (uint64_t)triple_u32(now) | ((uint64_t)triple_u32(now + 12345) << 32);
|
||||
INLINE uint64_t us_get_now_id(void) {
|
||||
uint64_t now = us_get_now_monotonic_u64();
|
||||
return (uint64_t)us_triple_u32(now) | ((uint64_t)us_triple_u32(now + 12345) << 32);
|
||||
}
|
||||
|
||||
INLINE long double get_now_real(void) {
|
||||
INLINE long double us_get_now_real(void) {
|
||||
time_t sec;
|
||||
long msec;
|
||||
get_now(CLOCK_REALTIME, &sec, &msec);
|
||||
us_get_now(CLOCK_REALTIME, &sec, &msec);
|
||||
return (long double)sec + ((long double)msec) / 1000;
|
||||
}
|
||||
|
||||
INLINE unsigned get_cores_available(void) {
|
||||
INLINE unsigned us_get_cores_available(void) {
|
||||
long cores_sysconf = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
cores_sysconf = (cores_sysconf < 0 ? 0 : cores_sysconf);
|
||||
return max_u(min_u(cores_sysconf, 4), 1);
|
||||
return us_max_u(us_min_u(cores_sysconf, 4), 1);
|
||||
}
|
||||
|
||||
INLINE void ld_to_timespec(long double ld, struct timespec *ts) {
|
||||
INLINE void us_ld_to_timespec(long double ld, struct timespec *ts) {
|
||||
ts->tv_sec = (long)ld;
|
||||
ts->tv_nsec = (ld - ts->tv_sec) * 1000000000L;
|
||||
if (ts->tv_nsec > 999999999L) {
|
||||
@@ -157,17 +157,17 @@ INLINE void ld_to_timespec(long double ld, struct timespec *ts) {
|
||||
}
|
||||
}
|
||||
|
||||
INLINE long double timespec_to_ld(const struct timespec *ts) {
|
||||
INLINE long double us_timespec_to_ld(const struct timespec *ts) {
|
||||
return ts->tv_sec + ((long double)ts->tv_nsec) / 1000000000;
|
||||
}
|
||||
|
||||
INLINE int flock_timedwait_monotonic(int fd, long double timeout) {
|
||||
long double deadline_ts = get_now_monotonic() + timeout;
|
||||
INLINE int us_flock_timedwait_monotonic(int fd, long double timeout) {
|
||||
long double deadline_ts = us_get_now_monotonic() + timeout;
|
||||
int retval = -1;
|
||||
|
||||
while (true) {
|
||||
retval = flock(fd, LOCK_EX | LOCK_NB);
|
||||
if (retval == 0 || errno != EWOULDBLOCK || get_now_monotonic() > deadline_ts) {
|
||||
if (retval == 0 || errno != EWOULDBLOCK || us_get_now_monotonic() > deadline_ts) {
|
||||
break;
|
||||
}
|
||||
if (usleep(1000) < 0) {
|
||||
@@ -177,7 +177,7 @@ INLINE int flock_timedwait_monotonic(int fd, long double timeout) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
INLINE char *errno_to_string(int error, char *buf, size_t size) {
|
||||
INLINE char *us_errno_to_string(int error, char *buf, size_t size) {
|
||||
assert(buf);
|
||||
assert(size > 0);
|
||||
locale_t locale = newlocale(LC_MESSAGES_MASK, "C", NULL);
|
||||
|
||||
@@ -26,15 +26,15 @@
|
||||
typedef struct {
|
||||
struct jpeg_error_mgr mgr; // Default manager
|
||||
jmp_buf jmp;
|
||||
const frame_s *frame;
|
||||
const us_frame_s *frame;
|
||||
} _jpeg_error_manager_s;
|
||||
|
||||
|
||||
static void _jpeg_error_handler(j_common_ptr jpeg);
|
||||
|
||||
|
||||
int unjpeg(const frame_s *src, frame_s *dest, bool decode) {
|
||||
assert(is_jpeg(src->format));
|
||||
int us_unjpeg(const us_frame_s *src, us_frame_s *dest, bool decode) {
|
||||
assert(us_is_jpeg(src->format));
|
||||
|
||||
volatile int retval = 0;
|
||||
|
||||
@@ -57,7 +57,7 @@ int unjpeg(const frame_s *src, frame_s *dest, bool decode) {
|
||||
|
||||
jpeg_start_decompress(&jpeg);
|
||||
|
||||
frame_copy_meta(src, dest);
|
||||
us_frame_copy_meta(src, dest);
|
||||
dest->format = V4L2_PIX_FMT_RGB24;
|
||||
dest->width = jpeg.output_width;
|
||||
dest->height = jpeg.output_height;
|
||||
@@ -68,10 +68,10 @@ int unjpeg(const frame_s *src, frame_s *dest, bool decode) {
|
||||
JSAMPARRAY scanlines;
|
||||
scanlines = (*jpeg.mem->alloc_sarray)((j_common_ptr) &jpeg, JPOOL_IMAGE, dest->stride, 1);
|
||||
|
||||
frame_realloc_data(dest, ((dest->width * dest->height) << 1) * 2);
|
||||
us_frame_realloc_data(dest, ((dest->width * dest->height) << 1) * 2);
|
||||
while (jpeg.output_scanline < jpeg.output_height) {
|
||||
jpeg_read_scanlines(&jpeg, scanlines, 1);
|
||||
frame_append_data(dest, scanlines[0], dest->stride);
|
||||
us_frame_append_data(dest, scanlines[0], dest->stride);
|
||||
}
|
||||
|
||||
jpeg_finish_decompress(&jpeg);
|
||||
@@ -87,6 +87,6 @@ static void _jpeg_error_handler(j_common_ptr jpeg) {
|
||||
char msg[JMSG_LENGTH_MAX];
|
||||
|
||||
(*jpeg_error->mgr.format_message)(jpeg, msg);
|
||||
LOG_ERROR("Can't decompress JPEG: %s", msg);
|
||||
US_LOG_ERROR("Can't decompress JPEG: %s", msg);
|
||||
longjmp(jpeg_error->jmp, -1);
|
||||
}
|
||||
|
||||
@@ -37,4 +37,4 @@
|
||||
#include "frame.h"
|
||||
|
||||
|
||||
int unjpeg(const frame_s *src, frame_s *dest, bool decode);
|
||||
int us_unjpeg(const us_frame_s *src, us_frame_s *dest, bool decode);
|
||||
|
||||
@@ -26,17 +26,15 @@
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "tools.h"
|
||||
|
||||
|
||||
#ifndef CFG_XIOCTL_RETRIES
|
||||
# define CFG_XIOCTL_RETRIES 4
|
||||
#ifndef US_CFG_XIOCTL_RETRIES
|
||||
# define US_CFG_XIOCTL_RETRIES 4
|
||||
#endif
|
||||
#define XIOCTL_RETRIES ((unsigned)(CFG_XIOCTL_RETRIES))
|
||||
#define _XIOCTL_RETRIES ((unsigned)(US_CFG_XIOCTL_RETRIES))
|
||||
|
||||
|
||||
INLINE int xioctl(int fd, int request, void *arg) {
|
||||
int retries = XIOCTL_RETRIES;
|
||||
INLINE int us_xioctl(int fd, int request, void *arg) {
|
||||
int retries = _XIOCTL_RETRIES;
|
||||
int retval = -1;
|
||||
|
||||
do {
|
||||
|
||||
@@ -23,50 +23,50 @@
|
||||
#include "blank.h"
|
||||
|
||||
|
||||
static frame_s *_init_internal(void);
|
||||
static frame_s *_init_external(const char *path);
|
||||
static us_frame_s *_init_internal(void);
|
||||
static us_frame_s *_init_external(const char *path);
|
||||
|
||||
|
||||
frame_s *blank_frame_init(const char *path) {
|
||||
frame_s *blank = NULL;
|
||||
us_frame_s *us_blank_frame_init(const char *path) {
|
||||
us_frame_s *blank = NULL;
|
||||
|
||||
if (path && path[0] != '\0') {
|
||||
blank = _init_external(path);
|
||||
}
|
||||
|
||||
if (blank) {
|
||||
LOG_INFO("Using external blank placeholder: %s", path);
|
||||
US_LOG_INFO("Using external blank placeholder: %s", path);
|
||||
} else {
|
||||
blank = _init_internal();
|
||||
LOG_INFO("Using internal blank placeholder");
|
||||
US_LOG_INFO("Using internal blank placeholder");
|
||||
}
|
||||
return blank;
|
||||
}
|
||||
|
||||
static frame_s *_init_internal(void) {
|
||||
frame_s *blank = frame_init();
|
||||
frame_set_data(blank, BLANK_JPEG_DATA, BLANK_JPEG_DATA_SIZE);
|
||||
blank->width = BLANK_JPEG_WIDTH;
|
||||
blank->height = BLANK_JPEG_HEIGHT;
|
||||
static us_frame_s *_init_internal(void) {
|
||||
us_frame_s *blank = us_frame_init();
|
||||
us_frame_set_data(blank, US_BLANK_JPEG_DATA, US_BLANK_JPEG_DATA_SIZE);
|
||||
blank->width = US_BLANK_JPEG_WIDTH;
|
||||
blank->height = US_BLANK_JPEG_HEIGHT;
|
||||
blank->format = V4L2_PIX_FMT_JPEG;
|
||||
return blank;
|
||||
}
|
||||
|
||||
static frame_s *_init_external(const char *path) {
|
||||
static us_frame_s *_init_external(const char *path) {
|
||||
FILE *fp = NULL;
|
||||
|
||||
frame_s *blank = frame_init();
|
||||
us_frame_s *blank = us_frame_init();
|
||||
blank->format = V4L2_PIX_FMT_JPEG;
|
||||
|
||||
if ((fp = fopen(path, "rb")) == NULL) {
|
||||
LOG_PERROR("Can't open blank placeholder '%s'", path);
|
||||
US_LOG_PERROR("Can't open blank placeholder '%s'", path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
# define CHUNK_SIZE ((size_t)(100 * 1024))
|
||||
while (true) {
|
||||
if (blank->used + CHUNK_SIZE >= blank->allocated) {
|
||||
frame_realloc_data(blank, blank->used + CHUNK_SIZE * 2);
|
||||
us_frame_realloc_data(blank, blank->used + CHUNK_SIZE * 2);
|
||||
}
|
||||
|
||||
size_t readed = fread(blank->data + blank->used, 1, CHUNK_SIZE, fp);
|
||||
@@ -76,26 +76,26 @@ static frame_s *_init_external(const char *path) {
|
||||
if (feof(fp)) {
|
||||
break;
|
||||
} else {
|
||||
LOG_PERROR("Can't read blank placeholder");
|
||||
US_LOG_PERROR("Can't read blank placeholder");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
# undef CHUNK_SIZE
|
||||
|
||||
frame_s *decoded = frame_init();
|
||||
if (unjpeg(blank, decoded, false) < 0) {
|
||||
frame_destroy(decoded);
|
||||
us_frame_s *decoded = us_frame_init();
|
||||
if (us_unjpeg(blank, decoded, false) < 0) {
|
||||
us_frame_destroy(decoded);
|
||||
goto error;
|
||||
}
|
||||
blank->width = decoded->width;
|
||||
blank->height = decoded->height;
|
||||
frame_destroy(decoded);
|
||||
us_frame_destroy(decoded);
|
||||
|
||||
goto ok;
|
||||
|
||||
error:
|
||||
frame_destroy(blank);
|
||||
us_frame_destroy(blank);
|
||||
blank = NULL;
|
||||
|
||||
ok:
|
||||
|
||||
@@ -35,4 +35,4 @@
|
||||
#include "data/blank_jpeg.h"
|
||||
|
||||
|
||||
frame_s *blank_frame_init(const char *path);
|
||||
us_frame_s *us_blank_frame_init(const char *path);
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
#include "blank_jpeg.h"
|
||||
|
||||
|
||||
const unsigned BLANK_JPEG_WIDTH = 640;
|
||||
const unsigned BLANK_JPEG_HEIGHT = 480;
|
||||
const unsigned US_BLANK_JPEG_WIDTH = 640;
|
||||
const unsigned US_BLANK_JPEG_HEIGHT = 480;
|
||||
|
||||
const size_t BLANK_JPEG_DATA_SIZE = 13845;
|
||||
const uint8_t BLANK_JPEG_DATA[] = {
|
||||
const size_t US_BLANK_JPEG_DATA_SIZE = 13845;
|
||||
const uint8_t US_BLANK_JPEG_DATA[] = {
|
||||
0xFF, 0xD8, 0xFF, 0xE1, 0x09, 0x50, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x6E, 0x73, 0x2E, 0x61, 0x64, 0x6F, 0x62,
|
||||
0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x78, 0x61, 0x70, 0x2F, 0x31, 0x2E, 0x30, 0x2F, 0x00, 0x3C, 0x3F, 0x78, 0x70, 0x61,
|
||||
0x63, 0x6B, 0x65, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6E, 0x3D, 0x22, 0xEF, 0xBB, 0xBF, 0x22, 0x20, 0x69, 0x64, 0x3D,
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
extern const unsigned BLANK_JPEG_WIDTH;
|
||||
extern const unsigned BLANK_JPEG_HEIGHT;
|
||||
extern const unsigned US_BLANK_JPEG_WIDTH;
|
||||
extern const unsigned US_BLANK_JPEG_HEIGHT;
|
||||
|
||||
extern const size_t BLANK_JPEG_DATA_SIZE;
|
||||
extern const uint8_t BLANK_JPEG_DATA[];
|
||||
extern const size_t US_BLANK_JPEG_DATA_SIZE;
|
||||
extern const uint8_t US_BLANK_JPEG_DATA[];
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "index_html.h"
|
||||
|
||||
|
||||
const char *const HTML_INDEX_PAGE = " \
|
||||
const char *const US_HTML_INDEX_PAGE = " \
|
||||
<!DOCTYPE html> \
|
||||
\
|
||||
<html> \
|
||||
@@ -33,7 +33,7 @@ const char *const HTML_INDEX_PAGE = " \
|
||||
</head> \
|
||||
\
|
||||
<body> \
|
||||
<h3>µStreamer v" VERSION "</h3> \
|
||||
<h3>µStreamer v" US_VERSION "</h3> \
|
||||
<hr> \
|
||||
<ul> \
|
||||
<li> \
|
||||
|
||||
@@ -27,4 +27,4 @@
|
||||
#include "../../libs/const.h"
|
||||
|
||||
|
||||
extern const char *const HTML_INDEX_PAGE;
|
||||
extern const char *const US_HTML_INDEX_PAGE;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -48,73 +48,73 @@
|
||||
#include "../libs/xioctl.h"
|
||||
|
||||
|
||||
#define VIDEO_MIN_WIDTH ((unsigned)160)
|
||||
#define VIDEO_MAX_WIDTH ((unsigned)10240)
|
||||
#define US_VIDEO_MIN_WIDTH ((unsigned)160)
|
||||
#define US_VIDEO_MAX_WIDTH ((unsigned)10240)
|
||||
|
||||
#define VIDEO_MIN_HEIGHT ((unsigned)120)
|
||||
#define VIDEO_MAX_HEIGHT ((unsigned)4320)
|
||||
#define US_VIDEO_MIN_HEIGHT ((unsigned)120)
|
||||
#define US_VIDEO_MAX_HEIGHT ((unsigned)4320)
|
||||
|
||||
#define VIDEO_MAX_FPS ((unsigned)120)
|
||||
#define US_VIDEO_MAX_FPS ((unsigned)120)
|
||||
|
||||
#define STANDARD_UNKNOWN V4L2_STD_UNKNOWN
|
||||
#define STANDARDS_STR "PAL, NTSC, SECAM"
|
||||
#define US_STANDARD_UNKNOWN V4L2_STD_UNKNOWN
|
||||
#define US_STANDARDS_STR "PAL, NTSC, SECAM"
|
||||
|
||||
#define FORMAT_UNKNOWN -1
|
||||
#define FORMATS_STR "YUYV, UYVY, RGB565, RGB24, MJPEG, JPEG"
|
||||
#define US_FORMAT_UNKNOWN -1
|
||||
#define US_FORMATS_STR "YUYV, UYVY, RGB565, RGB24, MJPEG, JPEG"
|
||||
|
||||
#define IO_METHOD_UNKNOWN -1
|
||||
#define IO_METHODS_STR "MMAP, USERPTR"
|
||||
#define US_IO_METHOD_UNKNOWN -1
|
||||
#define US_IO_METHODS_STR "MMAP, USERPTR"
|
||||
|
||||
|
||||
typedef struct {
|
||||
frame_s raw;
|
||||
us_frame_s raw;
|
||||
struct v4l2_buffer buf;
|
||||
int dma_fd;
|
||||
bool grabbed;
|
||||
} hw_buffer_s;
|
||||
} us_hw_buffer_s;
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned format;
|
||||
unsigned stride;
|
||||
unsigned hw_fps;
|
||||
unsigned jpeg_quality;
|
||||
size_t raw_size;
|
||||
unsigned n_bufs;
|
||||
hw_buffer_s *hw_bufs;
|
||||
bool capturing;
|
||||
bool persistent_timeout_reported;
|
||||
} device_runtime_s;
|
||||
int fd;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned format;
|
||||
unsigned stride;
|
||||
unsigned hw_fps;
|
||||
unsigned jpeg_quality;
|
||||
size_t raw_size;
|
||||
unsigned n_bufs;
|
||||
us_hw_buffer_s *hw_bufs;
|
||||
bool capturing;
|
||||
bool persistent_timeout_reported;
|
||||
} us_device_runtime_s;
|
||||
|
||||
typedef enum {
|
||||
CTL_MODE_NONE = 0,
|
||||
CTL_MODE_VALUE,
|
||||
CTL_MODE_AUTO,
|
||||
CTL_MODE_DEFAULT,
|
||||
} control_mode_e;
|
||||
} us_control_mode_e;
|
||||
|
||||
typedef struct {
|
||||
control_mode_e mode;
|
||||
int value;
|
||||
} control_s;
|
||||
us_control_mode_e mode;
|
||||
int value;
|
||||
} us_control_s;
|
||||
|
||||
typedef struct {
|
||||
control_s brightness;
|
||||
control_s contrast;
|
||||
control_s saturation;
|
||||
control_s hue;
|
||||
control_s gamma;
|
||||
control_s sharpness;
|
||||
control_s backlight_compensation;
|
||||
control_s white_balance;
|
||||
control_s gain;
|
||||
control_s color_effect;
|
||||
control_s rotate;
|
||||
control_s flip_vertical;
|
||||
control_s flip_horizontal;
|
||||
} controls_s;
|
||||
us_control_s brightness;
|
||||
us_control_s contrast;
|
||||
us_control_s saturation;
|
||||
us_control_s hue;
|
||||
us_control_s gamma;
|
||||
us_control_s sharpness;
|
||||
us_control_s backlight_compensation;
|
||||
us_control_s white_balance;
|
||||
us_control_s gain;
|
||||
us_control_s color_effect;
|
||||
us_control_s rotate;
|
||||
us_control_s flip_vertical;
|
||||
us_control_s flip_horizontal;
|
||||
} us_controls_s;
|
||||
|
||||
typedef struct {
|
||||
char *path;
|
||||
@@ -132,25 +132,25 @@ typedef struct {
|
||||
bool persistent;
|
||||
unsigned timeout;
|
||||
|
||||
controls_s ctl;
|
||||
us_controls_s ctl;
|
||||
|
||||
device_runtime_s *run;
|
||||
} device_s;
|
||||
us_device_runtime_s *run;
|
||||
} us_device_s;
|
||||
|
||||
|
||||
device_s *device_init(void);
|
||||
void device_destroy(device_s *dev);
|
||||
us_device_s *us_device_init(void);
|
||||
void us_device_destroy(us_device_s *dev);
|
||||
|
||||
int device_parse_format(const char *str);
|
||||
v4l2_std_id device_parse_standard(const char *str);
|
||||
int device_parse_io_method(const char *str);
|
||||
int us_device_parse_format(const char *str);
|
||||
v4l2_std_id us_device_parse_standard(const char *str);
|
||||
int us_device_parse_io_method(const char *str);
|
||||
|
||||
int device_open(device_s *dev);
|
||||
void device_close(device_s *dev);
|
||||
int us_device_open(us_device_s *dev);
|
||||
void us_device_close(us_device_s *dev);
|
||||
|
||||
int device_export_to_dma(device_s *dev);
|
||||
int device_switch_capturing(device_s *dev, bool enable);
|
||||
int device_select(device_s *dev, bool *has_read, bool *has_write, bool *has_error);
|
||||
int device_grab_buffer(device_s *dev, hw_buffer_s **hw);
|
||||
int device_release_buffer(device_s *dev, hw_buffer_s *hw);
|
||||
int device_consume_event(device_s *dev);
|
||||
int us_device_export_to_dma(us_device_s *dev);
|
||||
int us_device_switch_capturing(us_device_s *dev, bool enable);
|
||||
int us_device_select(us_device_s *dev, bool *has_read, bool *has_write, bool *has_error);
|
||||
int us_device_grab_buffer(us_device_s *dev, us_hw_buffer_s **hw);
|
||||
int us_device_release_buffer(us_device_s *dev, us_hw_buffer_s *hw);
|
||||
int us_device_consume_event(us_device_s *dev);
|
||||
|
||||
@@ -25,67 +25,67 @@
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
const encoder_type_e type;
|
||||
const us_encoder_type_e type;
|
||||
} _ENCODER_TYPES[] = {
|
||||
{"CPU", ENCODER_TYPE_CPU},
|
||||
{"HW", ENCODER_TYPE_HW},
|
||||
{"M2M-VIDEO", ENCODER_TYPE_M2M_VIDEO},
|
||||
{"M2M-IMAGE", ENCODER_TYPE_M2M_IMAGE},
|
||||
{"M2M-MJPEG", ENCODER_TYPE_M2M_VIDEO},
|
||||
{"M2M-JPEG", ENCODER_TYPE_M2M_IMAGE},
|
||||
{"OMX", ENCODER_TYPE_M2M_IMAGE},
|
||||
{"NOOP", ENCODER_TYPE_NOOP},
|
||||
{"CPU", US_ENCODER_TYPE_CPU},
|
||||
{"HW", US_ENCODER_TYPE_HW},
|
||||
{"M2M-VIDEO", US_ENCODER_TYPE_M2M_VIDEO},
|
||||
{"M2M-IMAGE", US_ENCODER_TYPE_M2M_IMAGE},
|
||||
{"M2M-MJPEG", US_ENCODER_TYPE_M2M_VIDEO},
|
||||
{"M2M-JPEG", US_ENCODER_TYPE_M2M_IMAGE},
|
||||
{"OMX", US_ENCODER_TYPE_M2M_IMAGE},
|
||||
{"NOOP", US_ENCODER_TYPE_NOOP},
|
||||
};
|
||||
|
||||
|
||||
static void *_worker_job_init(void *v_enc);
|
||||
static void _worker_job_destroy(void *v_job);
|
||||
static bool _worker_run_job(worker_s *wr);
|
||||
static bool _worker_run_job(us_worker_s *wr);
|
||||
|
||||
|
||||
#define ER(_next) enc->run->_next
|
||||
#define _ER(x_next) enc->run->x_next
|
||||
|
||||
|
||||
encoder_s *encoder_init(void) {
|
||||
encoder_runtime_s *run;
|
||||
A_CALLOC(run, 1);
|
||||
run->type = ENCODER_TYPE_CPU;
|
||||
us_encoder_s *us_encoder_init(void) {
|
||||
us_encoder_runtime_s *run;
|
||||
US_CALLOC(run, 1);
|
||||
run->type = US_ENCODER_TYPE_CPU;
|
||||
run->quality = 80;
|
||||
A_MUTEX_INIT(&run->mutex);
|
||||
US_MUTEX_INIT(&run->mutex);
|
||||
|
||||
encoder_s *enc;
|
||||
A_CALLOC(enc, 1);
|
||||
us_encoder_s *enc;
|
||||
US_CALLOC(enc, 1);
|
||||
enc->type = run->type;
|
||||
enc->n_workers = get_cores_available();
|
||||
enc->n_workers = us_get_cores_available();
|
||||
enc->run = run;
|
||||
return enc;
|
||||
}
|
||||
|
||||
void encoder_destroy(encoder_s *enc) {
|
||||
if (ER(m2ms)) {
|
||||
for (unsigned index = 0; index < ER(n_m2ms); ++index) {
|
||||
if (ER(m2ms[index])) {
|
||||
m2m_encoder_destroy(ER(m2ms[index]));
|
||||
void us_encoder_destroy(us_encoder_s *enc) {
|
||||
if (_ER(m2ms)) {
|
||||
for (unsigned index = 0; index < _ER(n_m2ms); ++index) {
|
||||
if (_ER(m2ms[index])) {
|
||||
us_m2m_encoder_destroy(_ER(m2ms[index]));
|
||||
}
|
||||
}
|
||||
free(ER(m2ms));
|
||||
free(_ER(m2ms));
|
||||
}
|
||||
A_MUTEX_DESTROY(&ER(mutex));
|
||||
US_MUTEX_DESTROY(&_ER(mutex));
|
||||
free(enc->run);
|
||||
free(enc);
|
||||
}
|
||||
|
||||
encoder_type_e encoder_parse_type(const char *str) {
|
||||
for (unsigned index = 0; index < ARRAY_LEN(_ENCODER_TYPES); ++index) {
|
||||
us_encoder_type_e us_encoder_parse_type(const char *str) {
|
||||
for (unsigned index = 0; index < US_ARRAY_LEN(_ENCODER_TYPES); ++index) {
|
||||
if (!strcasecmp(str, _ENCODER_TYPES[index].name)) {
|
||||
return _ENCODER_TYPES[index].type;
|
||||
}
|
||||
}
|
||||
return ENCODER_TYPE_UNKNOWN;
|
||||
return US_ENCODER_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
const char *encoder_type_to_string(encoder_type_e type) {
|
||||
for (unsigned index = 0; index < ARRAY_LEN(_ENCODER_TYPES); ++index) {
|
||||
const char *us_encoder_type_to_string(us_encoder_type_e type) {
|
||||
for (unsigned index = 0; index < US_ARRAY_LEN(_ENCODER_TYPES); ++index) {
|
||||
if (_ENCODER_TYPES[index].type == type) {
|
||||
return _ENCODER_TYPES[index].name;
|
||||
}
|
||||
@@ -93,44 +93,44 @@ const char *encoder_type_to_string(encoder_type_e type) {
|
||||
return _ENCODER_TYPES[0].name;
|
||||
}
|
||||
|
||||
workers_pool_s *encoder_workers_pool_init(encoder_s *enc, device_s *dev) {
|
||||
# define DR(_next) dev->run->_next
|
||||
us_workers_pool_s *us_encoder_workers_pool_init(us_encoder_s *enc, us_device_s *dev) {
|
||||
# define DR(x_next) dev->run->x_next
|
||||
|
||||
encoder_type_e type = (ER(cpu_forced) ? ENCODER_TYPE_CPU : enc->type);
|
||||
us_encoder_type_e type = (_ER(cpu_forced) ? US_ENCODER_TYPE_CPU : enc->type);
|
||||
unsigned quality = dev->jpeg_quality;
|
||||
unsigned n_workers = min_u(enc->n_workers, DR(n_bufs));
|
||||
unsigned n_workers = us_min_u(enc->n_workers, DR(n_bufs));
|
||||
bool cpu_forced = false;
|
||||
|
||||
if (is_jpeg(DR(format)) && type != ENCODER_TYPE_HW) {
|
||||
LOG_INFO("Switching to HW encoder: the input is (M)JPEG ...");
|
||||
type = ENCODER_TYPE_HW;
|
||||
if (us_is_jpeg(DR(format)) && type != US_ENCODER_TYPE_HW) {
|
||||
US_LOG_INFO("Switching to HW encoder: the input is (M)JPEG ...");
|
||||
type = US_ENCODER_TYPE_HW;
|
||||
}
|
||||
|
||||
if (type == ENCODER_TYPE_HW) {
|
||||
if (!is_jpeg(DR(format))) {
|
||||
LOG_INFO("Switching to CPU encoder: the input format is not (M)JPEG ...");
|
||||
if (type == US_ENCODER_TYPE_HW) {
|
||||
if (!us_is_jpeg(DR(format))) {
|
||||
US_LOG_INFO("Switching to CPU encoder: the input format is not (M)JPEG ...");
|
||||
goto use_cpu;
|
||||
}
|
||||
quality = DR(jpeg_quality);
|
||||
n_workers = 1;
|
||||
|
||||
} else if (type == ENCODER_TYPE_M2M_VIDEO || type == ENCODER_TYPE_M2M_IMAGE) {
|
||||
LOG_DEBUG("Preparing M2M-%s encoder ...", (type == ENCODER_TYPE_M2M_VIDEO ? "VIDEO" : "IMAGE"));
|
||||
if (ER(m2ms) == NULL) {
|
||||
A_CALLOC(ER(m2ms), n_workers);
|
||||
} else if (type == US_ENCODER_TYPE_M2M_VIDEO || type == US_ENCODER_TYPE_M2M_IMAGE) {
|
||||
US_LOG_DEBUG("Preparing M2M-%s encoder ...", (type == US_ENCODER_TYPE_M2M_VIDEO ? "VIDEO" : "IMAGE"));
|
||||
if (_ER(m2ms) == NULL) {
|
||||
US_CALLOC(_ER(m2ms), n_workers);
|
||||
}
|
||||
for (; ER(n_m2ms) < n_workers; ++ER(n_m2ms)) {
|
||||
for (; _ER(n_m2ms) < n_workers; ++_ER(n_m2ms)) {
|
||||
// Начинаем с нуля и доинициализируем на следующих заходах при необходимости
|
||||
char name[32];
|
||||
snprintf(name, 32, "JPEG-%u", ER(n_m2ms));
|
||||
if (type == ENCODER_TYPE_M2M_VIDEO) {
|
||||
ER(m2ms[ER(n_m2ms)]) = m2m_mjpeg_encoder_init(name, enc->m2m_path, quality);
|
||||
snprintf(name, 32, "JPEG-%u", _ER(n_m2ms));
|
||||
if (type == US_ENCODER_TYPE_M2M_VIDEO) {
|
||||
_ER(m2ms[_ER(n_m2ms)]) = us_m2m_mjpeg_encoder_init(name, enc->m2m_path, quality);
|
||||
} else {
|
||||
ER(m2ms[ER(n_m2ms)]) = m2m_jpeg_encoder_init(name, enc->m2m_path, quality);
|
||||
_ER(m2ms[_ER(n_m2ms)]) = us_m2m_jpeg_encoder_init(name, enc->m2m_path, quality);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (type == ENCODER_TYPE_NOOP) {
|
||||
} else if (type == US_ENCODER_TYPE_NOOP) {
|
||||
n_workers = 1;
|
||||
quality = 0;
|
||||
}
|
||||
@@ -138,32 +138,32 @@ workers_pool_s *encoder_workers_pool_init(encoder_s *enc, device_s *dev) {
|
||||
goto ok;
|
||||
|
||||
use_cpu:
|
||||
type = ENCODER_TYPE_CPU;
|
||||
type = US_ENCODER_TYPE_CPU;
|
||||
quality = dev->jpeg_quality;
|
||||
|
||||
ok:
|
||||
if (type == ENCODER_TYPE_NOOP) {
|
||||
LOG_INFO("Using JPEG NOOP encoder");
|
||||
if (type == US_ENCODER_TYPE_NOOP) {
|
||||
US_LOG_INFO("Using JPEG NOOP encoder");
|
||||
} else if (quality == 0) {
|
||||
LOG_INFO("Using JPEG quality: encoder default");
|
||||
US_LOG_INFO("Using JPEG quality: encoder default");
|
||||
} else {
|
||||
LOG_INFO("Using JPEG quality: %u%%", quality);
|
||||
US_LOG_INFO("Using JPEG quality: %u%%", quality);
|
||||
}
|
||||
|
||||
A_MUTEX_LOCK(&ER(mutex));
|
||||
ER(type) = type;
|
||||
ER(quality) = quality;
|
||||
US_MUTEX_LOCK(&_ER(mutex));
|
||||
_ER(type) = type;
|
||||
_ER(quality) = quality;
|
||||
if (cpu_forced) {
|
||||
ER(cpu_forced) = true;
|
||||
_ER(cpu_forced) = true;
|
||||
}
|
||||
A_MUTEX_UNLOCK(&ER(mutex));
|
||||
US_MUTEX_UNLOCK(&_ER(mutex));
|
||||
|
||||
long double desired_interval = 0;
|
||||
if (dev->desired_fps > 0 && (dev->desired_fps < dev->run->hw_fps || dev->run->hw_fps == 0)) {
|
||||
desired_interval = (long double)1 / dev->desired_fps;
|
||||
}
|
||||
|
||||
return workers_pool_init(
|
||||
return us_workers_pool_init(
|
||||
"JPEG", "jw", n_workers, desired_interval,
|
||||
_worker_job_init, (void *)enc,
|
||||
_worker_job_destroy,
|
||||
@@ -172,64 +172,61 @@ workers_pool_s *encoder_workers_pool_init(encoder_s *enc, device_s *dev) {
|
||||
# undef DR
|
||||
}
|
||||
|
||||
void encoder_get_runtime_params(encoder_s *enc, encoder_type_e *type, unsigned *quality) {
|
||||
A_MUTEX_LOCK(&ER(mutex));
|
||||
*type = ER(type);
|
||||
*quality = ER(quality);
|
||||
A_MUTEX_UNLOCK(&ER(mutex));
|
||||
void us_encoder_get_runtime_params(us_encoder_s *enc, us_encoder_type_e *type, unsigned *quality) {
|
||||
US_MUTEX_LOCK(&_ER(mutex));
|
||||
*type = _ER(type);
|
||||
*quality = _ER(quality);
|
||||
US_MUTEX_UNLOCK(&_ER(mutex));
|
||||
}
|
||||
|
||||
static void *_worker_job_init(void *v_enc) {
|
||||
encoder_job_s *job;
|
||||
A_CALLOC(job, 1);
|
||||
job->enc = (encoder_s *)v_enc;
|
||||
job->dest = frame_init();
|
||||
us_encoder_job_s *job;
|
||||
US_CALLOC(job, 1);
|
||||
job->enc = (us_encoder_s *)v_enc;
|
||||
job->dest = us_frame_init();
|
||||
return (void *)job;
|
||||
}
|
||||
|
||||
static void _worker_job_destroy(void *v_job) {
|
||||
encoder_job_s *job = (encoder_job_s *)v_job;
|
||||
frame_destroy(job->dest);
|
||||
us_encoder_job_s *job = (us_encoder_job_s *)v_job;
|
||||
us_frame_destroy(job->dest);
|
||||
free(job);
|
||||
}
|
||||
|
||||
#undef ER
|
||||
static bool _worker_run_job(us_worker_s *wr) {
|
||||
us_encoder_job_s *job = (us_encoder_job_s *)wr->job;
|
||||
us_encoder_s *enc = job->enc; // Just for _ER()
|
||||
us_frame_s *src = &job->hw->raw;
|
||||
us_frame_s *dest = job->dest;
|
||||
|
||||
static bool _worker_run_job(worker_s *wr) {
|
||||
encoder_job_s *job = (encoder_job_s *)wr->job;
|
||||
frame_s *src = &job->hw->raw;
|
||||
frame_s *dest = job->dest;
|
||||
assert(_ER(type) != US_ENCODER_TYPE_UNKNOWN);
|
||||
|
||||
# define ER(_next) job->enc->run->_next
|
||||
|
||||
assert(ER(type) != ENCODER_TYPE_UNKNOWN);
|
||||
|
||||
if (ER(type) == ENCODER_TYPE_CPU) {
|
||||
LOG_VERBOSE("Compressing JPEG using CPU: worker=%s, buffer=%u",
|
||||
if (_ER(type) == US_ENCODER_TYPE_CPU) {
|
||||
US_LOG_VERBOSE("Compressing JPEG using CPU: worker=%s, buffer=%u",
|
||||
wr->name, job->hw->buf.index);
|
||||
cpu_encoder_compress(src, dest, ER(quality));
|
||||
us_cpu_encoder_compress(src, dest, _ER(quality));
|
||||
|
||||
} else if (ER(type) == ENCODER_TYPE_HW) {
|
||||
LOG_VERBOSE("Compressing JPEG using HW (just copying): worker=%s, buffer=%u",
|
||||
} else if (_ER(type) == US_ENCODER_TYPE_HW) {
|
||||
US_LOG_VERBOSE("Compressing JPEG using HW (just copying): worker=%s, buffer=%u",
|
||||
wr->name, job->hw->buf.index);
|
||||
hw_encoder_compress(src, dest);
|
||||
us_hw_encoder_compress(src, dest);
|
||||
|
||||
} else if (ER(type) == ENCODER_TYPE_M2M_VIDEO || ER(type) == ENCODER_TYPE_M2M_IMAGE) {
|
||||
LOG_VERBOSE("Compressing JPEG using M2M-%s: worker=%s, buffer=%u",
|
||||
(ER(type) == ENCODER_TYPE_M2M_VIDEO ? "VIDEO" : "IMAGE"), wr->name, job->hw->buf.index);
|
||||
if (m2m_encoder_compress(ER(m2ms[wr->number]), src, dest, false) < 0) {
|
||||
} else if (_ER(type) == US_ENCODER_TYPE_M2M_VIDEO || _ER(type) == US_ENCODER_TYPE_M2M_IMAGE) {
|
||||
US_LOG_VERBOSE("Compressing JPEG using M2M-%s: worker=%s, buffer=%u",
|
||||
(_ER(type) == US_ENCODER_TYPE_M2M_VIDEO ? "VIDEO" : "IMAGE"), wr->name, job->hw->buf.index);
|
||||
if (us_m2m_encoder_compress(_ER(m2ms[wr->number]), src, dest, false) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
} else if (ER(type) == ENCODER_TYPE_NOOP) {
|
||||
LOG_VERBOSE("Compressing JPEG using NOOP (do nothing): worker=%s, buffer=%u",
|
||||
} else if (_ER(type) == US_ENCODER_TYPE_NOOP) {
|
||||
US_LOG_VERBOSE("Compressing JPEG using NOOP (do nothing): worker=%s, buffer=%u",
|
||||
wr->name, job->hw->buf.index);
|
||||
frame_encoding_begin(src, dest, V4L2_PIX_FMT_JPEG);
|
||||
us_frame_encoding_begin(src, dest, V4L2_PIX_FMT_JPEG);
|
||||
usleep(5000); // Просто чтобы работала логика desired_fps
|
||||
dest->encode_end_ts = get_now_monotonic(); // frame_encoding_end()
|
||||
dest->encode_end_ts = us_get_now_monotonic(); // us_frame_encoding_end()
|
||||
}
|
||||
|
||||
LOG_VERBOSE("Compressed new JPEG: size=%zu, time=%0.3Lf, worker=%s, buffer=%u",
|
||||
US_LOG_VERBOSE("Compressed new JPEG: size=%zu, time=%0.3Lf, worker=%s, buffer=%u",
|
||||
job->dest->used,
|
||||
job->dest->encode_end_ts - job->dest->encode_begin_ts,
|
||||
wr->name,
|
||||
@@ -238,12 +235,10 @@ static bool _worker_run_job(worker_s *wr) {
|
||||
return true;
|
||||
|
||||
error:
|
||||
LOG_ERROR("Compression failed: worker=%s, buffer=%u", wr->name, job->hw->buf.index);
|
||||
LOG_ERROR("Error while compressing buffer, falling back to CPU");
|
||||
A_MUTEX_LOCK(&ER(mutex));
|
||||
ER(cpu_forced) = true;
|
||||
A_MUTEX_UNLOCK(&ER(mutex));
|
||||
US_LOG_ERROR("Compression failed: worker=%s, buffer=%u", wr->name, job->hw->buf.index);
|
||||
US_LOG_ERROR("Error while compressing buffer, falling back to CPU");
|
||||
US_MUTEX_LOCK(&_ER(mutex));
|
||||
_ER(cpu_forced) = true;
|
||||
US_MUTEX_UNLOCK(&_ER(mutex));
|
||||
return false;
|
||||
|
||||
# undef ER
|
||||
}
|
||||
|
||||
@@ -46,46 +46,46 @@
|
||||
#define ENCODER_TYPES_STR "CPU, HW, M2M-VIDEO, M2M-IMAGE, NOOP"
|
||||
|
||||
typedef enum {
|
||||
ENCODER_TYPE_UNKNOWN, // Only for encoder_parse_type() and main()
|
||||
ENCODER_TYPE_CPU,
|
||||
ENCODER_TYPE_HW,
|
||||
ENCODER_TYPE_M2M_VIDEO,
|
||||
ENCODER_TYPE_M2M_IMAGE,
|
||||
ENCODER_TYPE_NOOP,
|
||||
} encoder_type_e;
|
||||
US_ENCODER_TYPE_UNKNOWN, // Only for us_encoder_parse_type() and main()
|
||||
US_ENCODER_TYPE_CPU,
|
||||
US_ENCODER_TYPE_HW,
|
||||
US_ENCODER_TYPE_M2M_VIDEO,
|
||||
US_ENCODER_TYPE_M2M_IMAGE,
|
||||
US_ENCODER_TYPE_NOOP,
|
||||
} us_encoder_type_e;
|
||||
|
||||
typedef struct {
|
||||
encoder_type_e type;
|
||||
unsigned quality;
|
||||
bool cpu_forced;
|
||||
pthread_mutex_t mutex;
|
||||
us_encoder_type_e type;
|
||||
unsigned quality;
|
||||
bool cpu_forced;
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
unsigned n_m2ms;
|
||||
m2m_encoder_s **m2ms;
|
||||
} encoder_runtime_s;
|
||||
unsigned n_m2ms;
|
||||
us_m2m_encoder_s **m2ms;
|
||||
} us_encoder_runtime_s;
|
||||
|
||||
typedef struct {
|
||||
encoder_type_e type;
|
||||
unsigned n_workers;
|
||||
char *m2m_path;
|
||||
us_encoder_type_e type;
|
||||
unsigned n_workers;
|
||||
char *m2m_path;
|
||||
|
||||
encoder_runtime_s *run;
|
||||
} encoder_s;
|
||||
us_encoder_runtime_s *run;
|
||||
} us_encoder_s;
|
||||
|
||||
typedef struct {
|
||||
encoder_s *enc;
|
||||
hw_buffer_s *hw;
|
||||
frame_s *dest;
|
||||
} encoder_job_s;
|
||||
us_encoder_s *enc;
|
||||
us_hw_buffer_s *hw;
|
||||
us_frame_s *dest;
|
||||
} us_encoder_job_s;
|
||||
|
||||
|
||||
encoder_s *encoder_init(void);
|
||||
void encoder_destroy(encoder_s *enc);
|
||||
us_encoder_s *us_encoder_init(void);
|
||||
void us_encoder_destroy(us_encoder_s *enc);
|
||||
|
||||
encoder_type_e encoder_parse_type(const char *str);
|
||||
const char *encoder_type_to_string(encoder_type_e type);
|
||||
us_encoder_type_e us_encoder_parse_type(const char *str);
|
||||
const char *us_encoder_type_to_string(us_encoder_type_e type);
|
||||
|
||||
workers_pool_s *encoder_workers_pool_init(encoder_s *enc, device_s *dev);
|
||||
void encoder_get_runtime_params(encoder_s *enc, encoder_type_e *type, unsigned *quality);
|
||||
us_workers_pool_s *us_encoder_workers_pool_init(us_encoder_s *enc, us_device_s *dev);
|
||||
void us_encoder_get_runtime_params(us_encoder_s *enc, us_encoder_type_e *type, unsigned *quality);
|
||||
|
||||
int encoder_compress(encoder_s *enc, unsigned worker_number, frame_s *src, frame_s *dest);
|
||||
int us_encoder_compress(us_encoder_s *enc, unsigned worker_number, us_frame_s *src, us_frame_s *dest);
|
||||
|
||||
@@ -31,26 +31,26 @@
|
||||
typedef struct {
|
||||
struct jpeg_destination_mgr mgr; // Default manager
|
||||
JOCTET *buf; // Start of buffer
|
||||
frame_s *frame;
|
||||
us_frame_s *frame;
|
||||
} _jpeg_dest_manager_s;
|
||||
|
||||
|
||||
static void _jpeg_set_dest_frame(j_compress_ptr jpeg, frame_s *frame);
|
||||
static void _jpeg_set_dest_frame(j_compress_ptr jpeg, us_frame_s *frame);
|
||||
|
||||
static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const frame_s *frame);
|
||||
static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg, const frame_s *frame);
|
||||
static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const frame_s *frame);
|
||||
static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const frame_s *frame);
|
||||
static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
|
||||
static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
|
||||
static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
|
||||
static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
|
||||
|
||||
static void _jpeg_init_destination(j_compress_ptr jpeg);
|
||||
static boolean _jpeg_empty_output_buffer(j_compress_ptr jpeg);
|
||||
static void _jpeg_term_destination(j_compress_ptr jpeg);
|
||||
|
||||
|
||||
void cpu_encoder_compress(const frame_s *src, frame_s *dest, unsigned quality) {
|
||||
void us_cpu_encoder_compress(const us_frame_s *src, us_frame_s *dest, unsigned quality) {
|
||||
// This function based on compress_image_to_jpeg() from mjpg-streamer
|
||||
|
||||
frame_encoding_begin(src, dest, V4L2_PIX_FMT_JPEG);
|
||||
us_frame_encoding_begin(src, dest, V4L2_PIX_FMT_JPEG);
|
||||
|
||||
struct jpeg_compress_struct jpeg;
|
||||
struct jpeg_error_mgr jpeg_error;
|
||||
@@ -70,8 +70,8 @@ void cpu_encoder_compress(const frame_s *src, frame_s *dest, unsigned quality) {
|
||||
|
||||
jpeg_start_compress(&jpeg, TRUE);
|
||||
|
||||
# define WRITE_SCANLINES(_format, _func) \
|
||||
case _format: { _func(&jpeg, src); break; }
|
||||
# define WRITE_SCANLINES(x_format, x_func) \
|
||||
case x_format: { x_func(&jpeg, src); break; }
|
||||
|
||||
switch (src->format) {
|
||||
// https://www.fourcc.org/yuv.php
|
||||
@@ -87,10 +87,10 @@ void cpu_encoder_compress(const frame_s *src, frame_s *dest, unsigned quality) {
|
||||
jpeg_finish_compress(&jpeg);
|
||||
jpeg_destroy_compress(&jpeg);
|
||||
|
||||
frame_encoding_end(dest);
|
||||
us_frame_encoding_end(dest);
|
||||
}
|
||||
|
||||
static void _jpeg_set_dest_frame(j_compress_ptr jpeg, frame_s *frame) {
|
||||
static void _jpeg_set_dest_frame(j_compress_ptr jpeg, us_frame_s *frame) {
|
||||
if (jpeg->dest == NULL) {
|
||||
assert((jpeg->dest = (struct jpeg_destination_mgr *)(*jpeg->mem->alloc_small)(
|
||||
(j_common_ptr) jpeg, JPOOL_PERMANENT, sizeof(_jpeg_dest_manager_s)
|
||||
@@ -111,11 +111,11 @@ static void _jpeg_set_dest_frame(j_compress_ptr jpeg, frame_s *frame) {
|
||||
#define YUV_B(_y, _u, _) (((_y) + (454 * (_u))) >> 8)
|
||||
#define NORM_COMPONENT(_x) (((_x) > 255) ? 255 : (((_x) < 0) ? 0 : (_x)))
|
||||
|
||||
static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const frame_s *frame) {
|
||||
static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
|
||||
uint8_t *line_buf;
|
||||
A_CALLOC(line_buf, frame->width * 3);
|
||||
US_CALLOC(line_buf, frame->width * 3);
|
||||
|
||||
const unsigned padding = frame_get_padding(frame);
|
||||
const unsigned padding = us_frame_get_padding(frame);
|
||||
const uint8_t *data = frame->data;
|
||||
unsigned z = 0;
|
||||
|
||||
@@ -149,11 +149,11 @@ static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const
|
||||
free(line_buf);
|
||||
}
|
||||
|
||||
static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg, const frame_s *frame) {
|
||||
static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
|
||||
uint8_t *line_buf;
|
||||
A_CALLOC(line_buf, frame->width * 3);
|
||||
US_CALLOC(line_buf, frame->width * 3);
|
||||
|
||||
const unsigned padding = frame_get_padding(frame);
|
||||
const unsigned padding = us_frame_get_padding(frame);
|
||||
const uint8_t *data = frame->data;
|
||||
unsigned z = 0;
|
||||
|
||||
@@ -192,11 +192,11 @@ static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg, const
|
||||
#undef YUV_G
|
||||
#undef YUV_R
|
||||
|
||||
static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const frame_s *frame) {
|
||||
static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
|
||||
uint8_t *line_buf;
|
||||
A_CALLOC(line_buf, frame->width * 3);
|
||||
US_CALLOC(line_buf, frame->width * 3);
|
||||
|
||||
const unsigned padding = frame_get_padding(frame);
|
||||
const unsigned padding = us_frame_get_padding(frame);
|
||||
const uint8_t *data = frame->data;
|
||||
|
||||
while (jpeg->next_scanline < frame->height) {
|
||||
@@ -220,8 +220,8 @@ static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, cons
|
||||
free(line_buf);
|
||||
}
|
||||
|
||||
static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const frame_s *frame) {
|
||||
const unsigned padding = frame_get_padding(frame);
|
||||
static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
|
||||
const unsigned padding = us_frame_get_padding(frame);
|
||||
uint8_t *data = frame->data;
|
||||
|
||||
while (jpeg->next_scanline < frame->height) {
|
||||
@@ -251,7 +251,7 @@ static boolean _jpeg_empty_output_buffer(j_compress_ptr jpeg) {
|
||||
|
||||
_jpeg_dest_manager_s *dest = (_jpeg_dest_manager_s *)jpeg->dest;
|
||||
|
||||
frame_append_data(dest->frame, dest->buf, JPEG_OUTPUT_BUFFER_SIZE);
|
||||
us_frame_append_data(dest->frame, dest->buf, JPEG_OUTPUT_BUFFER_SIZE);
|
||||
|
||||
dest->mgr.next_output_byte = dest->buf;
|
||||
dest->mgr.free_in_buffer = JPEG_OUTPUT_BUFFER_SIZE;
|
||||
@@ -267,7 +267,7 @@ static void _jpeg_term_destination(j_compress_ptr jpeg) {
|
||||
size_t final = JPEG_OUTPUT_BUFFER_SIZE - dest->mgr.free_in_buffer;
|
||||
|
||||
// Write any data remaining in the buffer.
|
||||
frame_append_data(dest->frame, dest->buf, final);
|
||||
us_frame_append_data(dest->frame, dest->buf, final);
|
||||
}
|
||||
|
||||
#undef JPEG_OUTPUT_BUFFER_SIZE
|
||||
|
||||
@@ -35,4 +35,4 @@
|
||||
#include "../../../libs/frame.h"
|
||||
|
||||
|
||||
void cpu_encoder_compress(const frame_s *src, frame_s *dest, unsigned quality);
|
||||
void us_cpu_encoder_compress(const us_frame_s *src, us_frame_s *dest, unsigned quality);
|
||||
|
||||
@@ -28,17 +28,17 @@
|
||||
#include "encoder.h"
|
||||
|
||||
|
||||
void _copy_plus_huffman(const frame_s *src, frame_s *dest);
|
||||
void _copy_plus_huffman(const us_frame_s *src, us_frame_s *dest);
|
||||
static bool _is_huffman(const uint8_t *data);
|
||||
|
||||
|
||||
void hw_encoder_compress(const frame_s *src, frame_s *dest) {
|
||||
assert(is_jpeg(src->format));
|
||||
void us_hw_encoder_compress(const us_frame_s *src, us_frame_s *dest) {
|
||||
assert(us_is_jpeg(src->format));
|
||||
_copy_plus_huffman(src, dest);
|
||||
}
|
||||
|
||||
void _copy_plus_huffman(const frame_s *src, frame_s *dest) {
|
||||
frame_encoding_begin(src, dest, V4L2_PIX_FMT_JPEG);
|
||||
void _copy_plus_huffman(const us_frame_s *src, us_frame_s *dest) {
|
||||
us_frame_encoding_begin(src, dest, V4L2_PIX_FMT_JPEG);
|
||||
|
||||
if (!_is_huffman(src->data)) {
|
||||
const uint8_t *src_ptr = src->data;
|
||||
@@ -54,15 +54,15 @@ void _copy_plus_huffman(const frame_s *src, frame_s *dest) {
|
||||
|
||||
const size_t paste = src_ptr - src->data;
|
||||
|
||||
frame_set_data(dest, src->data, paste);
|
||||
frame_append_data(dest, HUFFMAN_TABLE, sizeof(HUFFMAN_TABLE));
|
||||
frame_append_data(dest, src_ptr, src->used - paste);
|
||||
us_frame_set_data(dest, src->data, paste);
|
||||
us_frame_append_data(dest, US_HUFFMAN_TABLE, sizeof(US_HUFFMAN_TABLE));
|
||||
us_frame_append_data(dest, src_ptr, src->used - paste);
|
||||
|
||||
} else {
|
||||
frame_set_data(dest, src->data, src->used);
|
||||
us_frame_set_data(dest, src->data, src->used);
|
||||
}
|
||||
|
||||
frame_encoding_end(dest);
|
||||
us_frame_encoding_end(dest);
|
||||
}
|
||||
|
||||
static bool _is_huffman(const uint8_t *data) {
|
||||
|
||||
@@ -34,4 +34,4 @@
|
||||
#include "huffman.h"
|
||||
|
||||
|
||||
void hw_encoder_compress(const frame_s *src, frame_s *dest);
|
||||
void us_hw_encoder_compress(const us_frame_s *src, us_frame_s *dest);
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
static const uint8_t HUFFMAN_TABLE[] = {
|
||||
static const uint8_t US_HUFFMAN_TABLE[] = {
|
||||
0xFF, 0xC4, 0x01, 0xA2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
|
||||
0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x01, 0x00, 0x03,
|
||||
|
||||
@@ -23,13 +23,13 @@
|
||||
#include "gpio.h"
|
||||
|
||||
|
||||
gpio_s us_gpio = {
|
||||
us_gpio_s us_gpio = {
|
||||
.path = "/dev/gpiochip0",
|
||||
.consumer_prefix = "ustreamer",
|
||||
|
||||
# define MAKE_OUTPUT(_role) { \
|
||||
# define MAKE_OUTPUT(x_role) { \
|
||||
.pin = -1, \
|
||||
.role = _role, \
|
||||
.role = x_role, \
|
||||
.consumer = NULL, \
|
||||
.line = NULL, \
|
||||
.state = false \
|
||||
@@ -46,77 +46,77 @@ gpio_s us_gpio = {
|
||||
};
|
||||
|
||||
|
||||
static void _gpio_output_init(gpio_output_s *output);
|
||||
static void _gpio_output_destroy(gpio_output_s *output);
|
||||
static void _gpio_output_init(us_gpio_output_s *output);
|
||||
static void _gpio_output_destroy(us_gpio_output_s *output);
|
||||
|
||||
|
||||
void gpio_init(void) {
|
||||
void us_gpio_init(void) {
|
||||
assert(us_gpio.chip == NULL);
|
||||
if (
|
||||
us_gpio.prog_running.pin >= 0
|
||||
|| us_gpio.stream_online.pin >= 0
|
||||
|| us_gpio.has_http_clients.pin >= 0
|
||||
) {
|
||||
A_MUTEX_INIT(&us_gpio.mutex);
|
||||
LOG_INFO("GPIO: Using chip device: %s", us_gpio.path);
|
||||
US_MUTEX_INIT(&us_gpio.mutex);
|
||||
US_LOG_INFO("GPIO: Using chip device: %s", us_gpio.path);
|
||||
if ((us_gpio.chip = gpiod_chip_open(us_gpio.path)) != NULL) {
|
||||
_gpio_output_init(&us_gpio.prog_running);
|
||||
_gpio_output_init(&us_gpio.stream_online);
|
||||
_gpio_output_init(&us_gpio.has_http_clients);
|
||||
} else {
|
||||
LOG_PERROR("GPIO: Can't initialize chip device %s", us_gpio.path);
|
||||
US_LOG_PERROR("GPIO: Can't initialize chip device %s", us_gpio.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gpio_destroy(void) {
|
||||
void us_gpio_destroy(void) {
|
||||
_gpio_output_destroy(&us_gpio.prog_running);
|
||||
_gpio_output_destroy(&us_gpio.stream_online);
|
||||
_gpio_output_destroy(&us_gpio.has_http_clients);
|
||||
if (us_gpio.chip) {
|
||||
gpiod_chip_close(us_gpio.chip);
|
||||
us_gpio.chip = NULL;
|
||||
A_MUTEX_DESTROY(&us_gpio.mutex);
|
||||
US_MUTEX_DESTROY(&us_gpio.mutex);
|
||||
}
|
||||
}
|
||||
|
||||
int gpio_inner_set(gpio_output_s *output, bool state) {
|
||||
int us_gpio_inner_set(us_gpio_output_s *output, bool state) {
|
||||
int retval = 0;
|
||||
|
||||
assert(us_gpio.chip);
|
||||
assert(output->line);
|
||||
assert(output->state != state); // Must be checked in macro for the performance
|
||||
A_MUTEX_LOCK(&us_gpio.mutex);
|
||||
US_MUTEX_LOCK(&us_gpio.mutex);
|
||||
|
||||
if (gpiod_line_set_value(output->line, (int)state) < 0) { \
|
||||
LOG_PERROR("GPIO: Can't write value %d to line %s (will be disabled)", state, output->consumer); \
|
||||
US_LOG_PERROR("GPIO: Can't write value %d to line %s (will be disabled)", state, output->consumer); \
|
||||
_gpio_output_destroy(output);
|
||||
retval = -1;
|
||||
}
|
||||
|
||||
A_MUTEX_UNLOCK(&us_gpio.mutex);
|
||||
US_MUTEX_UNLOCK(&us_gpio.mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void _gpio_output_init(gpio_output_s *output) {
|
||||
static void _gpio_output_init(us_gpio_output_s *output) {
|
||||
assert(us_gpio.chip);
|
||||
assert(output->line == NULL);
|
||||
|
||||
A_ASPRINTF(output->consumer, "%s::%s", us_gpio.consumer_prefix, output->role);
|
||||
US_ASPRINTF(output->consumer, "%s::%s", us_gpio.consumer_prefix, output->role);
|
||||
|
||||
if (output->pin >= 0) {
|
||||
if ((output->line = gpiod_chip_get_line(us_gpio.chip, output->pin)) != NULL) {
|
||||
if (gpiod_line_request_output(output->line, output->consumer, 0) < 0) {
|
||||
LOG_PERROR("GPIO: Can't request pin=%d as %s", output->pin, output->consumer);
|
||||
US_LOG_PERROR("GPIO: Can't request pin=%d as %s", output->pin, output->consumer);
|
||||
_gpio_output_destroy(output);
|
||||
}
|
||||
} else {
|
||||
LOG_PERROR("GPIO: Can't get pin=%d as %s", output->pin, output->consumer);
|
||||
US_LOG_PERROR("GPIO: Can't get pin=%d as %s", output->pin, output->consumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _gpio_output_destroy(gpio_output_s *output) {
|
||||
static void _gpio_output_destroy(us_gpio_output_s *output) {
|
||||
if (output->line) {
|
||||
gpiod_line_release(output->line);
|
||||
output->line = NULL;
|
||||
|
||||
@@ -41,46 +41,46 @@ typedef struct {
|
||||
char *consumer;
|
||||
struct gpiod_line *line;
|
||||
bool state;
|
||||
} gpio_output_s;
|
||||
} us_gpio_output_s;
|
||||
|
||||
typedef struct {
|
||||
char *path;
|
||||
char *consumer_prefix;
|
||||
char *path;
|
||||
char *consumer_prefix;
|
||||
|
||||
gpio_output_s prog_running;
|
||||
gpio_output_s stream_online;
|
||||
gpio_output_s has_http_clients;
|
||||
us_gpio_output_s prog_running;
|
||||
us_gpio_output_s stream_online;
|
||||
us_gpio_output_s has_http_clients;
|
||||
|
||||
pthread_mutex_t mutex;
|
||||
struct gpiod_chip *chip;
|
||||
} gpio_s;
|
||||
} us_gpio_s;
|
||||
|
||||
|
||||
extern gpio_s us_gpio;
|
||||
extern us_gpio_s us_gpio;
|
||||
|
||||
|
||||
void gpio_init(void);
|
||||
void gpio_destroy(void);
|
||||
int gpio_inner_set(gpio_output_s *output, bool state);
|
||||
void us_gpio_init(void);
|
||||
void us_gpio_destroy(void);
|
||||
int us_gpio_inner_set(us_gpio_output_s *output, bool state);
|
||||
|
||||
|
||||
#define SET_STATE(_output, _state) { \
|
||||
if (_output.line && _output.state != _state) { \
|
||||
if (!gpio_inner_set(&_output, _state)) { \
|
||||
_output.state = _state; \
|
||||
#define SET_STATE(x_output, x_state) { \
|
||||
if (x_output.line && x_output.state != x_state) { \
|
||||
if (!us_gpio_inner_set(&x_output, x_state)) { \
|
||||
x_output.state = x_state; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
INLINE void gpio_set_prog_running(bool state) {
|
||||
INLINE void us_gpio_set_prog_running(bool state) {
|
||||
SET_STATE(us_gpio.prog_running, state);
|
||||
}
|
||||
|
||||
INLINE void gpio_set_stream_online(bool state) {
|
||||
INLINE void us_gpio_set_stream_online(bool state) {
|
||||
SET_STATE(us_gpio.stream_online, state);
|
||||
}
|
||||
|
||||
INLINE void gpio_set_has_http_clients(bool state) {
|
||||
INLINE void us_gpio_set_has_http_clients(bool state) {
|
||||
SET_STATE(us_gpio.has_http_clients, state);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,42 +23,42 @@
|
||||
#include "stream.h"
|
||||
|
||||
|
||||
h264_stream_s *h264_stream_init(memsink_s *sink, const char *path, unsigned bitrate, unsigned gop) {
|
||||
h264_stream_s *h264;
|
||||
A_CALLOC(h264, 1);
|
||||
us_h264_stream_s *us_h264_stream_init(us_memsink_s *sink, const char *path, unsigned bitrate, unsigned gop) {
|
||||
us_h264_stream_s *h264;
|
||||
US_CALLOC(h264, 1);
|
||||
h264->sink = sink;
|
||||
h264->tmp_src = frame_init();
|
||||
h264->dest = frame_init();
|
||||
h264->tmp_src = us_frame_init();
|
||||
h264->dest = us_frame_init();
|
||||
atomic_init(&h264->online, false);
|
||||
h264->enc = m2m_h264_encoder_init("H264", path, bitrate, gop);
|
||||
h264->enc = us_m2m_h264_encoder_init("H264", path, bitrate, gop);
|
||||
return h264;
|
||||
}
|
||||
|
||||
void h264_stream_destroy(h264_stream_s *h264) {
|
||||
m2m_encoder_destroy(h264->enc);
|
||||
frame_destroy(h264->dest);
|
||||
frame_destroy(h264->tmp_src);
|
||||
void us_h264_stream_destroy(us_h264_stream_s *h264) {
|
||||
us_m2m_encoder_destroy(h264->enc);
|
||||
us_frame_destroy(h264->dest);
|
||||
us_frame_destroy(h264->tmp_src);
|
||||
free(h264);
|
||||
}
|
||||
|
||||
void h264_stream_process(h264_stream_s *h264, const frame_s *frame, bool force_key) {
|
||||
if (!memsink_server_check(h264->sink, frame)) {
|
||||
void us_h264_stream_process(us_h264_stream_s *h264, const us_frame_s *frame, bool force_key) {
|
||||
if (!us_memsink_server_check(h264->sink, frame)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_jpeg(frame->format)) {
|
||||
long double now = get_now_monotonic();
|
||||
LOG_DEBUG("H264: Input frame is JPEG; decoding ...");
|
||||
if (unjpeg(frame, h264->tmp_src, true) < 0) {
|
||||
if (us_is_jpeg(frame->format)) {
|
||||
long double now = us_get_now_monotonic();
|
||||
US_LOG_DEBUG("H264: Input frame is JPEG; decoding ...");
|
||||
if (us_unjpeg(frame, h264->tmp_src, true) < 0) {
|
||||
return;
|
||||
}
|
||||
frame = h264->tmp_src;
|
||||
LOG_VERBOSE("H264: JPEG decoded; time=%.3Lf", get_now_monotonic() - now);
|
||||
US_LOG_VERBOSE("H264: JPEG decoded; time=%.3Lf", us_get_now_monotonic() - now);
|
||||
}
|
||||
|
||||
bool online = false;
|
||||
if (!m2m_encoder_compress(h264->enc, frame, h264->dest, force_key)) {
|
||||
online = !memsink_server_put(h264->sink, h264->dest);
|
||||
if (!us_m2m_encoder_compress(h264->enc, frame, h264->dest, force_key)) {
|
||||
online = !us_memsink_server_put(h264->sink, h264->dest);
|
||||
}
|
||||
atomic_store(&h264->online, online);
|
||||
}
|
||||
|
||||
@@ -35,14 +35,14 @@
|
||||
|
||||
|
||||
typedef struct {
|
||||
memsink_s *sink;
|
||||
frame_s *tmp_src;
|
||||
frame_s *dest;
|
||||
m2m_encoder_s *enc;
|
||||
atomic_bool online;
|
||||
} h264_stream_s;
|
||||
us_memsink_s *sink;
|
||||
us_frame_s *tmp_src;
|
||||
us_frame_s *dest;
|
||||
us_m2m_encoder_s *enc;
|
||||
atomic_bool online;
|
||||
} us_h264_stream_s;
|
||||
|
||||
|
||||
h264_stream_s *h264_stream_init(memsink_s *sink, const char *path, unsigned bitrate, unsigned gop);
|
||||
void h264_stream_destroy(h264_stream_s *h264);
|
||||
void h264_stream_process(h264_stream_s *h264, const frame_s *frame, bool force_key);
|
||||
us_h264_stream_s *us_h264_stream_init(us_memsink_s *sink, const char *path, unsigned bitrate, unsigned gop);
|
||||
void us_h264_stream_destroy(us_h264_stream_s *h264);
|
||||
void us_h264_stream_process(us_h264_stream_s *h264, const us_frame_s *frame, bool force_key);
|
||||
|
||||
@@ -23,25 +23,25 @@
|
||||
#include "bev.h"
|
||||
|
||||
|
||||
char *bufferevent_my_format_reason(short what) {
|
||||
char *us_bufferevent_format_reason(short what) {
|
||||
char *reason;
|
||||
A_CALLOC(reason, 2048);
|
||||
US_CALLOC(reason, 2048);
|
||||
|
||||
char perror_buf[1024] = {0};
|
||||
char *perror_ptr = errno_to_string(EVUTIL_SOCKET_ERROR(), perror_buf, 1024); // evutil_socket_error_to_string() is not thread-safe
|
||||
char *perror_ptr = us_errno_to_string(EVUTIL_SOCKET_ERROR(), perror_buf, 1024); // evutil_socket_error_to_string() is not thread-safe
|
||||
bool first = true;
|
||||
|
||||
strcat(reason, perror_ptr);
|
||||
strcat(reason, " (");
|
||||
|
||||
# define FILL_REASON(_bev, _name) { \
|
||||
if (what & _bev) { \
|
||||
# define FILL_REASON(x_bev, x_name) { \
|
||||
if (what & x_bev) { \
|
||||
if (first) { \
|
||||
first = false; \
|
||||
} else { \
|
||||
strcat(reason, ","); \
|
||||
} \
|
||||
strcat(reason, _name); \
|
||||
strcat(reason, x_name); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
@@ -32,4 +32,4 @@
|
||||
#include "../../libs/logging.h"
|
||||
|
||||
|
||||
char *bufferevent_my_format_reason(short what);
|
||||
char *us_bufferevent_format_reason(short what);
|
||||
|
||||
@@ -46,7 +46,7 @@ static const struct {
|
||||
};
|
||||
|
||||
|
||||
const char *guess_mime_type(const char *path) {
|
||||
const char *us_guess_mime_type(const char *path) {
|
||||
// FIXME: false-positive cppcheck
|
||||
char *dot = strrchr(path, '.'); // cppcheck-suppress ctunullpointer
|
||||
if (dot == NULL || strchr(dot, '/') != NULL) {
|
||||
@@ -54,7 +54,7 @@ const char *guess_mime_type(const char *path) {
|
||||
}
|
||||
|
||||
char *ext = dot + 1;
|
||||
for (unsigned index = 0; index < ARRAY_LEN(_MIME_TYPES); ++index) {
|
||||
for (unsigned index = 0; index < US_ARRAY_LEN(_MIME_TYPES); ++index) {
|
||||
if (!evutil_ascii_strcasecmp(ext, _MIME_TYPES[index].ext)) {
|
||||
return _MIME_TYPES[index].mime;
|
||||
}
|
||||
|
||||
@@ -29,4 +29,4 @@
|
||||
#include "../../libs/tools.h"
|
||||
|
||||
|
||||
const char *guess_mime_type(const char *str);
|
||||
const char *us_guess_mime_type(const char *str);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "path.h"
|
||||
|
||||
|
||||
char *simplify_request_path(const char *str) {
|
||||
char *us_simplify_request_path(const char *str) {
|
||||
// Based on Lighttpd sources:
|
||||
// - https://github.com/lighttpd/lighttpd1.4/blob/b31e7840d5403bc640579135b7004793b9ccd6c0/src/buffer.c#L840
|
||||
// - https://github.com/lighttpd/lighttpd1.4/blob/77c01f981725512653c01cde5ca74c11633dfec4/src/t/test_buffer.c
|
||||
@@ -36,7 +36,7 @@ char *simplify_request_path(const char *str) {
|
||||
char *out;
|
||||
char *slash;
|
||||
|
||||
A_CALLOC(simplified, strlen(str) + 1);
|
||||
US_CALLOC(simplified, strlen(str) + 1);
|
||||
|
||||
if (str[0] == '\0') {
|
||||
simplified[0] = '\0';
|
||||
@@ -112,7 +112,7 @@ char *simplify_request_path(const char *str) {
|
||||
#ifdef TEST_HTTP_PATH
|
||||
|
||||
int test_simplify_request_path(const char *sample, const char *expected) {
|
||||
char *result = simplify_request_path(sample);
|
||||
char *result = us_simplify_request_path(sample);
|
||||
int retval = -!!strcmp(result, expected);
|
||||
|
||||
printf("Testing '%s' -> '%s' ... ", sample, expected);
|
||||
|
||||
@@ -31,4 +31,4 @@
|
||||
#include "../../libs/tools.h"
|
||||
|
||||
|
||||
char *simplify_request_path(const char *str);
|
||||
char *us_simplify_request_path(const char *str);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "server.h"
|
||||
|
||||
|
||||
static int _http_preprocess_request(struct evhttp_request *request, server_s *server);
|
||||
static int _http_preprocess_request(struct evhttp_request *request, us_server_s *server);
|
||||
|
||||
static int _http_check_run_compat_action(struct evhttp_request *request, void *v_server);
|
||||
|
||||
@@ -38,31 +38,32 @@ static void _http_callback_stream_error(struct bufferevent *buf_event, short wha
|
||||
|
||||
static void _http_request_watcher(int fd, short event, void *v_server);
|
||||
static void _http_refresher(int fd, short event, void *v_server);
|
||||
static void _http_queue_send_stream(server_s *server, bool stream_updated, bool frame_updated);
|
||||
static void _http_queue_send_stream(us_server_s *server, bool stream_updated, bool frame_updated);
|
||||
|
||||
static bool _expose_new_frame(server_s *server);
|
||||
static bool _expose_new_frame(us_server_s *server);
|
||||
|
||||
static const char *_http_get_header(struct evhttp_request *request, const char *key);
|
||||
static char *_http_get_client_hostport(struct evhttp_request *request);
|
||||
|
||||
|
||||
#define RUN(_next) server->run->_next
|
||||
#define STREAM(_next) RUN(stream->_next)
|
||||
#define VID(_next) STREAM(run->video->_next)
|
||||
#define EX(_next) RUN(exposed->_next)
|
||||
#define _RUN(x_next) server->run->x_next
|
||||
#define _STREAM(x_next) _RUN(stream->x_next)
|
||||
#define _VID(x_next) _STREAM(run->video->x_next)
|
||||
#define _EX(x_next) _RUN(exposed->x_next)
|
||||
|
||||
|
||||
server_s *server_init(stream_s *stream) {
|
||||
exposed_s *exposed;
|
||||
A_CALLOC(exposed, 1);
|
||||
exposed->frame = frame_init();
|
||||
us_server_s *us_server_init(us_stream_s *stream) {
|
||||
us_exposed_s *exposed;
|
||||
US_CALLOC(exposed, 1);
|
||||
exposed->frame = us_frame_init();
|
||||
|
||||
server_runtime_s *run;
|
||||
A_CALLOC(run, 1);
|
||||
us_server_runtime_s *run;
|
||||
US_CALLOC(run, 1);
|
||||
run->stream = stream;
|
||||
run->exposed = exposed;
|
||||
|
||||
server_s *server;
|
||||
A_CALLOC(server, 1);
|
||||
us_server_s *server;
|
||||
US_CALLOC(server, 1);
|
||||
server->host = "127.0.0.1";
|
||||
server->port = 8080;
|
||||
server->unix_path = "";
|
||||
@@ -80,150 +81,146 @@ server_s *server_init(stream_s *stream) {
|
||||
return server;
|
||||
}
|
||||
|
||||
void server_destroy(server_s *server) {
|
||||
if (RUN(refresher)) {
|
||||
event_del(RUN(refresher));
|
||||
event_free(RUN(refresher));
|
||||
void us_server_destroy(us_server_s *server) {
|
||||
if (_RUN(refresher)) {
|
||||
event_del(_RUN(refresher));
|
||||
event_free(_RUN(refresher));
|
||||
}
|
||||
|
||||
if (RUN(request_watcher)) {
|
||||
event_del(RUN(request_watcher));
|
||||
event_free(RUN(request_watcher));
|
||||
if (_RUN(request_watcher)) {
|
||||
event_del(_RUN(request_watcher));
|
||||
event_free(_RUN(request_watcher));
|
||||
}
|
||||
|
||||
evhttp_free(RUN(http));
|
||||
if (RUN(ext_fd)) {
|
||||
close(RUN(ext_fd));
|
||||
evhttp_free(_RUN(http));
|
||||
if (_RUN(ext_fd)) {
|
||||
close(_RUN(ext_fd));
|
||||
}
|
||||
event_base_free(RUN(base));
|
||||
event_base_free(_RUN(base));
|
||||
|
||||
# if LIBEVENT_VERSION_NUMBER >= 0x02010100
|
||||
libevent_global_shutdown();
|
||||
# endif
|
||||
|
||||
LIST_ITERATE(RUN(stream_clients), client, {
|
||||
US_LIST_ITERATE(_RUN(stream_clients), client, {
|
||||
free(client->key);
|
||||
free(client->hostport);
|
||||
free(client);
|
||||
});
|
||||
|
||||
if (RUN(auth_token)) {
|
||||
free(RUN(auth_token));
|
||||
if (_RUN(auth_token)) {
|
||||
free(_RUN(auth_token));
|
||||
}
|
||||
|
||||
frame_destroy(EX(frame));
|
||||
free(RUN(exposed));
|
||||
us_frame_destroy(_EX(frame));
|
||||
free(_RUN(exposed));
|
||||
free(server->run);
|
||||
free(server);
|
||||
}
|
||||
|
||||
int server_listen(server_s *server) {
|
||||
int us_server_listen(us_server_s *server) {
|
||||
{
|
||||
if (server->static_path[0] != '\0') {
|
||||
LOG_INFO("Enabling HTTP file server: %s", server->static_path);
|
||||
evhttp_set_gencb(RUN(http), _http_callback_static, (void *)server);
|
||||
US_LOG_INFO("Enabling HTTP file server: %s", server->static_path);
|
||||
evhttp_set_gencb(_RUN(http), _http_callback_static, (void *)server);
|
||||
} else {
|
||||
assert(!evhttp_set_cb(RUN(http), "/", _http_callback_root, (void *)server));
|
||||
assert(!evhttp_set_cb(_RUN(http), "/", _http_callback_root, (void *)server));
|
||||
}
|
||||
assert(!evhttp_set_cb(RUN(http), "/state", _http_callback_state, (void *)server));
|
||||
assert(!evhttp_set_cb(RUN(http), "/snapshot", _http_callback_snapshot, (void *)server));
|
||||
assert(!evhttp_set_cb(RUN(http), "/stream", _http_callback_stream, (void *)server));
|
||||
assert(!evhttp_set_cb(_RUN(http), "/state", _http_callback_state, (void *)server));
|
||||
assert(!evhttp_set_cb(_RUN(http), "/snapshot", _http_callback_snapshot, (void *)server));
|
||||
assert(!evhttp_set_cb(_RUN(http), "/stream", _http_callback_stream, (void *)server));
|
||||
}
|
||||
|
||||
frame_copy(STREAM(blank), EX(frame));
|
||||
EX(notify_last_width) = EX(frame->width);
|
||||
EX(notify_last_height) = EX(frame->height);
|
||||
us_frame_copy(_STREAM(blank), _EX(frame));
|
||||
_EX(notify_last_width) = _EX(frame->width);
|
||||
_EX(notify_last_height) = _EX(frame->height);
|
||||
|
||||
if (server->exit_on_no_clients > 0) {
|
||||
RUN(last_request_ts) = get_now_monotonic();
|
||||
_RUN(last_request_ts) = us_get_now_monotonic();
|
||||
struct timeval interval = {0};
|
||||
interval.tv_usec = 100000;
|
||||
assert((RUN(request_watcher) = event_new(RUN(base), -1, EV_PERSIST, _http_request_watcher, server)));
|
||||
assert(!event_add(RUN(request_watcher), &interval));
|
||||
assert((_RUN(request_watcher) = event_new(_RUN(base), -1, EV_PERSIST, _http_request_watcher, server)));
|
||||
assert(!event_add(_RUN(request_watcher), &interval));
|
||||
}
|
||||
|
||||
{
|
||||
struct timeval interval = {0};
|
||||
if (STREAM(dev->desired_fps) > 0) {
|
||||
interval.tv_usec = 1000000 / (STREAM(dev->desired_fps) * 2);
|
||||
if (_STREAM(dev->desired_fps) > 0) {
|
||||
interval.tv_usec = 1000000 / (_STREAM(dev->desired_fps) * 2);
|
||||
} else {
|
||||
interval.tv_usec = 16000; // ~60fps
|
||||
}
|
||||
assert((RUN(refresher) = event_new(RUN(base), -1, EV_PERSIST, _http_refresher, server)));
|
||||
assert(!event_add(RUN(refresher), &interval));
|
||||
assert((_RUN(refresher) = event_new(_RUN(base), -1, EV_PERSIST, _http_refresher, server)));
|
||||
assert(!event_add(_RUN(refresher), &interval));
|
||||
}
|
||||
|
||||
evhttp_set_timeout(RUN(http), server->timeout);
|
||||
evhttp_set_timeout(_RUN(http), server->timeout);
|
||||
|
||||
if (server->user[0] != '\0') {
|
||||
char *encoded_token = NULL;
|
||||
|
||||
char *raw_token;
|
||||
A_ASPRINTF(raw_token, "%s:%s", server->user, server->passwd);
|
||||
base64_encode((uint8_t *)raw_token, strlen(raw_token), &encoded_token, NULL);
|
||||
US_ASPRINTF(raw_token, "%s:%s", server->user, server->passwd);
|
||||
us_base64_encode((uint8_t *)raw_token, strlen(raw_token), &encoded_token, NULL);
|
||||
free(raw_token);
|
||||
|
||||
A_ASPRINTF(RUN(auth_token), "Basic %s", encoded_token);
|
||||
US_ASPRINTF(_RUN(auth_token), "Basic %s", encoded_token);
|
||||
free(encoded_token);
|
||||
|
||||
LOG_INFO("Using HTTP basic auth");
|
||||
US_LOG_INFO("Using HTTP basic auth");
|
||||
}
|
||||
|
||||
if (server->unix_path[0] != '\0') {
|
||||
LOG_DEBUG("Binding HTTP to UNIX socket '%s' ...", server->unix_path);
|
||||
if ((RUN(ext_fd) = evhttp_my_bind_unix(
|
||||
RUN(http),
|
||||
US_LOG_DEBUG("Binding HTTP to UNIX socket '%s' ...", server->unix_path);
|
||||
if ((_RUN(ext_fd) = us_evhttp_bind_unix(
|
||||
_RUN(http),
|
||||
server->unix_path,
|
||||
server->unix_rm,
|
||||
server->unix_mode)) < 0
|
||||
) {
|
||||
return -1;
|
||||
}
|
||||
LOG_INFO("Listening HTTP on UNIX socket '%s'", server->unix_path);
|
||||
US_LOG_INFO("Listening HTTP on UNIX socket '%s'", server->unix_path);
|
||||
|
||||
# ifdef WITH_SYSTEMD
|
||||
} else if (server->systemd) {
|
||||
LOG_DEBUG("Binding HTTP to systemd socket ...");
|
||||
if ((RUN(ext_fd) = evhttp_my_bind_systemd(RUN(http))) < 0) {
|
||||
US_LOG_DEBUG("Binding HTTP to systemd socket ...");
|
||||
if ((_RUN(ext_fd) = us_evhttp_bind_systemd(_RUN(http))) < 0) {
|
||||
return -1;
|
||||
}
|
||||
LOG_INFO("Listening systemd socket ...");
|
||||
US_LOG_INFO("Listening systemd socket ...");
|
||||
# endif
|
||||
|
||||
} else {
|
||||
LOG_DEBUG("Binding HTTP to [%s]:%u ...", server->host, server->port);
|
||||
if (evhttp_bind_socket(RUN(http), server->host, server->port) < 0) {
|
||||
LOG_PERROR("Can't bind HTTP on [%s]:%u", server->host, server->port)
|
||||
US_LOG_DEBUG("Binding HTTP to [%s]:%u ...", server->host, server->port);
|
||||
if (evhttp_bind_socket(_RUN(http), server->host, server->port) < 0) {
|
||||
US_LOG_PERROR("Can't bind HTTP on [%s]:%u", server->host, server->port)
|
||||
return -1;
|
||||
}
|
||||
LOG_INFO("Listening HTTP on [%s]:%u", server->host, server->port);
|
||||
US_LOG_INFO("Listening HTTP on [%s]:%u", server->host, server->port);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void server_loop(server_s *server) {
|
||||
LOG_INFO("Starting HTTP eventloop ...");
|
||||
event_base_dispatch(RUN(base));
|
||||
LOG_INFO("HTTP eventloop stopped");
|
||||
void us_server_loop(us_server_s *server) {
|
||||
US_LOG_INFO("Starting HTTP eventloop ...");
|
||||
event_base_dispatch(_RUN(base));
|
||||
US_LOG_INFO("HTTP eventloop stopped");
|
||||
}
|
||||
|
||||
void server_loop_break(server_s *server) {
|
||||
event_base_loopbreak(RUN(base));
|
||||
void us_server_loop_break(us_server_s *server) {
|
||||
event_base_loopbreak(_RUN(base));
|
||||
}
|
||||
|
||||
#define GET_HEADER(_key) \
|
||||
evhttp_find_header(evhttp_request_get_input_headers(request), _key)
|
||||
#define ADD_HEADER(x_key, x_value) assert(!evhttp_add_header(evhttp_request_get_output_headers(request), x_key, x_value))
|
||||
|
||||
#define ADD_HEADER(_key, _value) \
|
||||
assert(!evhttp_add_header(evhttp_request_get_output_headers(request), _key, _value))
|
||||
|
||||
static int _http_preprocess_request(struct evhttp_request *request, server_s *server) {
|
||||
RUN(last_request_ts) = get_now_monotonic();
|
||||
static int _http_preprocess_request(struct evhttp_request *request, us_server_s *server) {
|
||||
_RUN(last_request_ts) = us_get_now_monotonic();
|
||||
|
||||
if (server->allow_origin[0] != '\0') {
|
||||
const char *cors_headers = GET_HEADER("Access-Control-Request-Headers");
|
||||
const char *cors_method = GET_HEADER("Access-Control-Request-Method");
|
||||
const char *cors_headers = _http_get_header(request, "Access-Control-Request-Headers");
|
||||
const char *cors_method = _http_get_header(request, "Access-Control-Request-Method");
|
||||
|
||||
ADD_HEADER("Access-Control-Allow-Origin", server->allow_origin);
|
||||
ADD_HEADER("Access-Control-Allow-Credentials", "true");
|
||||
@@ -240,10 +237,10 @@ static int _http_preprocess_request(struct evhttp_request *request, server_s *se
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (RUN(auth_token)) {
|
||||
const char *token = GET_HEADER("Authorization");
|
||||
if (_RUN(auth_token)) {
|
||||
const char *token = _http_get_header(request, "Authorization");
|
||||
|
||||
if (token == NULL || strcmp(token, RUN(auth_token)) != 0) {
|
||||
if (token == NULL || strcmp(token, _RUN(auth_token)) != 0) {
|
||||
ADD_HEADER("WWW-Authenticate", "Basic realm=\"Restricted area\"");
|
||||
evhttp_send_reply(request, 401, "Unauthorized", NULL);
|
||||
return -1;
|
||||
@@ -294,7 +291,7 @@ static int _http_check_run_compat_action(struct evhttp_request *request, void *v
|
||||
}
|
||||
|
||||
static void _http_callback_root(struct evhttp_request *request, void *v_server) {
|
||||
server_s *server = (server_s *)v_server;
|
||||
us_server_s *server = (us_server_s *)v_server;
|
||||
|
||||
PREPROCESS_REQUEST;
|
||||
COMPAT_REQUEST;
|
||||
@@ -302,7 +299,7 @@ static void _http_callback_root(struct evhttp_request *request, void *v_server)
|
||||
struct evbuffer *buf;
|
||||
|
||||
assert((buf = evbuffer_new()));
|
||||
assert(evbuffer_add_printf(buf, "%s", HTML_INDEX_PAGE));
|
||||
assert(evbuffer_add_printf(buf, "%s", US_HTML_INDEX_PAGE));
|
||||
ADD_HEADER("Content-Type", "text/html");
|
||||
evhttp_send_reply(request, HTTP_OK, "OK", buf);
|
||||
|
||||
@@ -310,7 +307,7 @@ static void _http_callback_root(struct evhttp_request *request, void *v_server)
|
||||
}
|
||||
|
||||
static void _http_callback_static(struct evhttp_request *request, void *v_server) {
|
||||
server_s *server = (server_s *)v_server;
|
||||
us_server_s *server = (us_server_s *)v_server;
|
||||
|
||||
PREPROCESS_REQUEST;
|
||||
COMPAT_REQUEST;
|
||||
@@ -338,12 +335,12 @@ static void _http_callback_static(struct evhttp_request *request, void *v_server
|
||||
|
||||
assert((buf = evbuffer_new()));
|
||||
|
||||
if ((static_path = find_static_file_path(server->static_path, decoded_path)) == NULL) {
|
||||
if ((static_path = us_find_static_file_path(server->static_path, decoded_path)) == NULL) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
if ((fd = open(static_path, O_RDONLY)) < 0) {
|
||||
LOG_PERROR("HTTP: Can't open found static file %s", static_path);
|
||||
US_LOG_PERROR("HTTP: Can't open found static file %s", static_path);
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
@@ -351,11 +348,11 @@ static void _http_callback_static(struct evhttp_request *request, void *v_server
|
||||
struct stat st;
|
||||
|
||||
if (fstat(fd, &st) < 0) {
|
||||
LOG_PERROR("HTTP: Can't stat() found static file %s", static_path);
|
||||
US_LOG_PERROR("HTTP: Can't stat() found static file %s", static_path);
|
||||
goto not_found;
|
||||
}
|
||||
if (st.st_size > 0 && evbuffer_add_file(buf, fd, 0, st.st_size) < 0) {
|
||||
LOG_ERROR("HTTP: Can't serve static file %s", static_path);
|
||||
US_LOG_ERROR("HTTP: Can't serve static file %s", static_path);
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
@@ -363,7 +360,7 @@ static void _http_callback_static(struct evhttp_request *request, void *v_server
|
||||
// and will close it when finished transferring data
|
||||
fd = -1;
|
||||
|
||||
ADD_HEADER("Content-Type", guess_mime_type(static_path));
|
||||
ADD_HEADER("Content-Type", us_guess_mime_type(static_path));
|
||||
evhttp_send_reply(request, HTTP_OK, "OK", buf);
|
||||
goto cleanup;
|
||||
}
|
||||
@@ -397,13 +394,13 @@ static void _http_callback_static(struct evhttp_request *request, void *v_server
|
||||
#undef COMPAT_REQUEST
|
||||
|
||||
static void _http_callback_state(struct evhttp_request *request, void *v_server) {
|
||||
server_s *server = (server_s *)v_server;
|
||||
us_server_s *server = (us_server_s *)v_server;
|
||||
|
||||
PREPROCESS_REQUEST;
|
||||
|
||||
encoder_type_e enc_type;
|
||||
us_encoder_type_e enc_type;
|
||||
unsigned enc_quality;
|
||||
encoder_get_runtime_params(STREAM(enc), &enc_type, &enc_quality);
|
||||
us_encoder_get_runtime_params(_STREAM(enc), &enc_type, &enc_quality);
|
||||
|
||||
struct evbuffer *buf;
|
||||
assert((buf = evbuffer_new()));
|
||||
@@ -411,32 +408,32 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
|
||||
assert(evbuffer_add_printf(buf,
|
||||
"{\"ok\": true, \"result\": {"
|
||||
" \"encoder\": {\"type\": \"%s\", \"quality\": %u},",
|
||||
encoder_type_to_string(enc_type),
|
||||
us_encoder_type_to_string(enc_type),
|
||||
enc_quality
|
||||
));
|
||||
|
||||
if (STREAM(run->h264)) {
|
||||
if (_STREAM(run->h264)) {
|
||||
assert(evbuffer_add_printf(buf,
|
||||
" \"h264\": {\"bitrate\": %u, \"gop\": %u, \"online\": %s},",
|
||||
STREAM(h264_bitrate),
|
||||
STREAM(h264_gop),
|
||||
bool_to_string(atomic_load(&STREAM(run->h264->online)))
|
||||
_STREAM(h264_bitrate),
|
||||
_STREAM(h264_gop),
|
||||
us_bool_to_string(atomic_load(&_STREAM(run->h264->online)))
|
||||
));
|
||||
}
|
||||
|
||||
if (STREAM(sink) || STREAM(h264_sink)) {
|
||||
if (_STREAM(sink) || _STREAM(h264_sink)) {
|
||||
assert(evbuffer_add_printf(buf, " \"sinks\": {"));
|
||||
if (STREAM(sink)) {
|
||||
if (_STREAM(sink)) {
|
||||
assert(evbuffer_add_printf(buf,
|
||||
"\"jpeg\": {\"has_clients\": %s}",
|
||||
bool_to_string(atomic_load(&STREAM(sink->has_clients)))
|
||||
us_bool_to_string(atomic_load(&_STREAM(sink->has_clients)))
|
||||
));
|
||||
}
|
||||
if (STREAM(h264_sink)) {
|
||||
if (_STREAM(h264_sink)) {
|
||||
assert(evbuffer_add_printf(buf,
|
||||
"%s\"h264\": {\"has_clients\": %s}",
|
||||
(STREAM(sink) ? ", " : ""),
|
||||
bool_to_string(atomic_load(&STREAM(h264_sink->has_clients)))
|
||||
(_STREAM(sink) ? ", " : ""),
|
||||
us_bool_to_string(atomic_load(&_STREAM(h264_sink->has_clients)))
|
||||
));
|
||||
}
|
||||
assert(evbuffer_add_printf(buf, "},"));
|
||||
@@ -446,25 +443,25 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
|
||||
" \"source\": {\"resolution\": {\"width\": %u, \"height\": %u},"
|
||||
" \"online\": %s, \"desired_fps\": %u, \"captured_fps\": %u},"
|
||||
" \"stream\": {\"queued_fps\": %u, \"clients\": %u, \"clients_stat\": {",
|
||||
(server->fake_width ? server->fake_width : EX(frame->width)),
|
||||
(server->fake_height ? server->fake_height : EX(frame->height)),
|
||||
bool_to_string(EX(frame->online)),
|
||||
STREAM(dev->desired_fps),
|
||||
EX(captured_fps),
|
||||
EX(queued_fps),
|
||||
RUN(stream_clients_count)
|
||||
(server->fake_width ? server->fake_width : _EX(frame->width)),
|
||||
(server->fake_height ? server->fake_height : _EX(frame->height)),
|
||||
us_bool_to_string(_EX(frame->online)),
|
||||
_STREAM(dev->desired_fps),
|
||||
_EX(captured_fps),
|
||||
_EX(queued_fps),
|
||||
_RUN(stream_clients_count)
|
||||
));
|
||||
|
||||
LIST_ITERATE(RUN(stream_clients), client, {
|
||||
US_LIST_ITERATE(_RUN(stream_clients), client, {
|
||||
assert(evbuffer_add_printf(buf,
|
||||
"\"%" PRIx64 "\": {\"fps\": %u, \"extra_headers\": %s, \"advance_headers\": %s,"
|
||||
" \"dual_final_frames\": %s, \"zero_data\": %s, \"key\": \"%s\"}%s",
|
||||
client->id,
|
||||
client->fps,
|
||||
bool_to_string(client->extra_headers),
|
||||
bool_to_string(client->advance_headers),
|
||||
bool_to_string(client->dual_final_frames),
|
||||
bool_to_string(client->zero_data),
|
||||
us_bool_to_string(client->extra_headers),
|
||||
us_bool_to_string(client->advance_headers),
|
||||
us_bool_to_string(client->dual_final_frames),
|
||||
us_bool_to_string(client->zero_data),
|
||||
(client->key != NULL ? client->key : "0"),
|
||||
(client->next ? ", " : "")
|
||||
));
|
||||
@@ -478,13 +475,13 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
|
||||
}
|
||||
|
||||
static void _http_callback_snapshot(struct evhttp_request *request, void *v_server) {
|
||||
server_s *server = (server_s *)v_server;
|
||||
us_server_s *server = (us_server_s *)v_server;
|
||||
|
||||
PREPROCESS_REQUEST;
|
||||
|
||||
struct evbuffer *buf;
|
||||
assert((buf = evbuffer_new()));
|
||||
assert(!evbuffer_add(buf, (const void *)EX(frame->data), EX(frame->used)));
|
||||
assert(!evbuffer_add(buf, (const void *)_EX(frame->data), _EX(frame->used)));
|
||||
|
||||
ADD_HEADER("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate, pre-check=0, post-check=0, max-age=0");
|
||||
ADD_HEADER("Pragma", "no-cache");
|
||||
@@ -492,29 +489,29 @@ static void _http_callback_snapshot(struct evhttp_request *request, void *v_serv
|
||||
|
||||
char header_buf[256];
|
||||
|
||||
# define ADD_TIME_HEADER(_key, _value) { \
|
||||
snprintf(header_buf, 255, "%.06Lf", _value); \
|
||||
ADD_HEADER(_key, header_buf); \
|
||||
# define ADD_TIME_HEADER(x_key, x_value) { \
|
||||
snprintf(header_buf, 255, "%.06Lf", x_value); \
|
||||
ADD_HEADER(x_key, header_buf); \
|
||||
}
|
||||
|
||||
# define ADD_UNSIGNED_HEADER(_key, _value) { \
|
||||
snprintf(header_buf, 255, "%u", _value); \
|
||||
ADD_HEADER(_key, header_buf); \
|
||||
# define ADD_UNSIGNED_HEADER(x_key, x_value) { \
|
||||
snprintf(header_buf, 255, "%u", x_value); \
|
||||
ADD_HEADER(x_key, header_buf); \
|
||||
}
|
||||
|
||||
ADD_TIME_HEADER("X-Timestamp", get_now_real());
|
||||
ADD_TIME_HEADER("X-Timestamp", us_get_now_real());
|
||||
|
||||
ADD_HEADER("X-UStreamer-Online", bool_to_string(EX(frame->online)));
|
||||
ADD_UNSIGNED_HEADER("X-UStreamer-Dropped", EX(dropped));
|
||||
ADD_UNSIGNED_HEADER("X-UStreamer-Width", EX(frame->width));
|
||||
ADD_UNSIGNED_HEADER("X-UStreamer-Height", EX(frame->height));
|
||||
ADD_TIME_HEADER("X-UStreamer-Grab-Timestamp", EX(frame->grab_ts));
|
||||
ADD_TIME_HEADER("X-UStreamer-Encode-Begin-Timestamp", EX(frame->encode_begin_ts));
|
||||
ADD_TIME_HEADER("X-UStreamer-Encode-End-Timestamp", EX(frame->encode_end_ts));
|
||||
ADD_TIME_HEADER("X-UStreamer-Expose-Begin-Timestamp", EX(expose_begin_ts));
|
||||
ADD_TIME_HEADER("X-UStreamer-Expose-Cmp-Timestamp", EX(expose_cmp_ts));
|
||||
ADD_TIME_HEADER("X-UStreamer-Expose-End-Timestamp", EX(expose_end_ts));
|
||||
ADD_TIME_HEADER("X-UStreamer-Send-Timestamp", get_now_monotonic());
|
||||
ADD_HEADER("X-UStreamer-Online", us_bool_to_string(_EX(frame->online)));
|
||||
ADD_UNSIGNED_HEADER("X-UStreamer-Dropped", _EX(dropped));
|
||||
ADD_UNSIGNED_HEADER("X-UStreamer-Width", _EX(frame->width));
|
||||
ADD_UNSIGNED_HEADER("X-UStreamer-Height", _EX(frame->height));
|
||||
ADD_TIME_HEADER("X-UStreamer-Grab-Timestamp", _EX(frame->grab_ts));
|
||||
ADD_TIME_HEADER("X-UStreamer-Encode-Begin-Timestamp", _EX(frame->encode_begin_ts));
|
||||
ADD_TIME_HEADER("X-UStreamer-Encode-End-Timestamp", _EX(frame->encode_end_ts));
|
||||
ADD_TIME_HEADER("X-UStreamer-Expose-Begin-Timestamp", _EX(expose_begin_ts));
|
||||
ADD_TIME_HEADER("X-UStreamer-Expose-Cmp-Timestamp", _EX(expose_cmp_ts));
|
||||
ADD_TIME_HEADER("X-UStreamer-Expose-End-Timestamp", _EX(expose_end_ts));
|
||||
ADD_TIME_HEADER("X-UStreamer-Send-Timestamp", us_get_now_monotonic());
|
||||
|
||||
# undef ADD_UNSUGNED_HEADER
|
||||
# undef ADD_TIME_HEADER
|
||||
@@ -534,7 +531,7 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
||||
// https://github.com/libevent/libevent/blob/29cc8386a2f7911eaa9336692a2c5544d8b4734f/http.c#L791
|
||||
// https://github.com/libevent/libevent/blob/29cc8386a2f7911eaa9336692a2c5544d8b4734f/http.c#L1458
|
||||
|
||||
server_s *server = (server_s *)v_server;
|
||||
us_server_s *server = (us_server_s *)v_server;
|
||||
|
||||
PREPROCESS_REQUEST;
|
||||
|
||||
@@ -542,8 +539,8 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
||||
conn = evhttp_request_get_connection(request);
|
||||
|
||||
if (conn) {
|
||||
stream_client_s *client;
|
||||
A_CALLOC(client, 1);
|
||||
us_stream_client_s *client;
|
||||
US_CALLOC(client, 1);
|
||||
client->server = server;
|
||||
client->request = request;
|
||||
client->need_initial = true;
|
||||
@@ -551,7 +548,7 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
||||
|
||||
struct evkeyvalq params;
|
||||
evhttp_parse_query(evhttp_request_get_uri(request), ¶ms);
|
||||
# define PARSE_PARAM(_type, _name) client->_name = uri_get_##_type(¶ms, #_name)
|
||||
# define PARSE_PARAM(x_type, x_name) client->x_name = us_uri_get_##x_type(¶ms, #x_name)
|
||||
PARSE_PARAM(string, key);
|
||||
PARSE_PARAM(true, extra_headers);
|
||||
PARSE_PARAM(true, advance_headers);
|
||||
@@ -561,29 +558,29 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
||||
evhttp_clear_headers(¶ms);
|
||||
|
||||
client->hostport = _http_get_client_hostport(request);
|
||||
client->id = get_now_id();
|
||||
client->id = us_get_now_id();
|
||||
|
||||
LIST_APPEND_C(RUN(stream_clients), client, RUN(stream_clients_count));
|
||||
US_LIST_APPEND_C(_RUN(stream_clients), client, _RUN(stream_clients_count));
|
||||
|
||||
if (RUN(stream_clients_count) == 1) {
|
||||
atomic_store(&VID(has_clients), true);
|
||||
if (_RUN(stream_clients_count) == 1) {
|
||||
atomic_store(&_VID(has_clients), true);
|
||||
# ifdef WITH_GPIO
|
||||
gpio_set_has_http_clients(true);
|
||||
us_gpio_set_has_http_clients(true);
|
||||
# endif
|
||||
}
|
||||
|
||||
LOG_INFO("HTTP: Registered client: %s, id=%" PRIx64 "; clients now: %u",
|
||||
client->hostport, client->id, RUN(stream_clients_count));
|
||||
US_LOG_INFO("HTTP: Registered client: %s, id=%" PRIx64 "; clients now: %u",
|
||||
client->hostport, client->id, _RUN(stream_clients_count));
|
||||
|
||||
struct bufferevent *buf_event = evhttp_connection_get_bufferevent(conn);
|
||||
if (server->tcp_nodelay && !RUN(ext_fd)) {
|
||||
if (server->tcp_nodelay && !_RUN(ext_fd)) {
|
||||
evutil_socket_t fd;
|
||||
int on = 1;
|
||||
|
||||
LOG_DEBUG("HTTP: Setting up TCP_NODELAY to the client %s ...", client->hostport);
|
||||
US_LOG_DEBUG("HTTP: Setting up TCP_NODELAY to the client %s ...", client->hostport);
|
||||
assert((fd = bufferevent_getfd(buf_event)) >= 0);
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on)) != 0) {
|
||||
LOG_PERROR("HTTP: Can't set TCP_NODELAY to the client %s", client->hostport);
|
||||
US_LOG_PERROR("HTTP: Can't set TCP_NODELAY to the client %s", client->hostport);
|
||||
}
|
||||
}
|
||||
bufferevent_setcb(buf_event, NULL, NULL, _http_callback_stream_error, (void *)client);
|
||||
@@ -598,12 +595,11 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
||||
static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_client) {
|
||||
# define BOUNDARY "boundarydonotcross"
|
||||
|
||||
stream_client_s *client = (stream_client_s *)v_client;
|
||||
struct evhttp_request *request = client->request; // for GET_HEADER
|
||||
server_s *server = client->server;
|
||||
us_stream_client_s *client = (us_stream_client_s *)v_client;
|
||||
us_server_s *server = client->server;
|
||||
|
||||
long double now = get_now_monotonic();
|
||||
long long now_second = floor_ms(now);
|
||||
long double now = us_get_now_monotonic();
|
||||
long long now_second = us_floor_ms(now);
|
||||
|
||||
if (now_second != client->fps_accum_second) {
|
||||
client->fps = client->fps_accum;
|
||||
@@ -636,14 +632,14 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
|
||||
|
||||
# define ADD_ADVANCE_HEADERS \
|
||||
assert(evbuffer_add_printf(buf, \
|
||||
"Content-Type: image/jpeg" RN "X-Timestamp: %.06Lf" RN RN, get_now_real()))
|
||||
"Content-Type: image/jpeg" RN "X-Timestamp: %.06Lf" RN RN, us_get_now_real()))
|
||||
|
||||
if (client->need_initial) {
|
||||
assert(evbuffer_add_printf(buf, "HTTP/1.0 200 OK" RN));
|
||||
|
||||
if (client->server->allow_origin[0] != '\0') {
|
||||
const char *cors_headers = GET_HEADER("Access-Control-Request-Headers");
|
||||
const char *cors_method = GET_HEADER("Access-Control-Request-Method");
|
||||
const char *cors_headers = _http_get_header(client->request, "Access-Control-Request-Headers");
|
||||
const char *cors_method = _http_get_header(client->request, "Access-Control-Request-Method");
|
||||
|
||||
assert(evbuffer_add_printf(buf,
|
||||
"Access-Control-Allow-Origin: %s" RN
|
||||
@@ -684,8 +680,8 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
|
||||
"Content-Length: %zu" RN
|
||||
"X-Timestamp: %.06Lf" RN
|
||||
"%s",
|
||||
(!client->zero_data ? EX(frame->used) : 0),
|
||||
get_now_real(),
|
||||
(!client->zero_data ? _EX(frame->used) : 0),
|
||||
us_get_now_real(),
|
||||
(client->extra_headers ? "" : RN)
|
||||
));
|
||||
if (client->extra_headers) {
|
||||
@@ -704,25 +700,25 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
|
||||
"X-UStreamer-Send-Time: %.06Lf" RN
|
||||
"X-UStreamer-Latency: %.06Lf" RN
|
||||
RN,
|
||||
bool_to_string(EX(frame->online)),
|
||||
EX(dropped),
|
||||
EX(frame->width),
|
||||
EX(frame->height),
|
||||
us_bool_to_string(_EX(frame->online)),
|
||||
_EX(dropped),
|
||||
_EX(frame->width),
|
||||
_EX(frame->height),
|
||||
client->fps,
|
||||
EX(frame->grab_ts),
|
||||
EX(frame->encode_begin_ts),
|
||||
EX(frame->encode_end_ts),
|
||||
EX(expose_begin_ts),
|
||||
EX(expose_cmp_ts),
|
||||
EX(expose_end_ts),
|
||||
_EX(frame->grab_ts),
|
||||
_EX(frame->encode_begin_ts),
|
||||
_EX(frame->encode_end_ts),
|
||||
_EX(expose_begin_ts),
|
||||
_EX(expose_cmp_ts),
|
||||
_EX(expose_end_ts),
|
||||
now,
|
||||
now - EX(frame->grab_ts)
|
||||
now - _EX(frame->grab_ts)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if (!client->zero_data) {
|
||||
assert(!evbuffer_add(buf, (void *)EX(frame->data), EX(frame->used)));
|
||||
assert(!evbuffer_add(buf, (void *)_EX(frame->data), _EX(frame->used)));
|
||||
}
|
||||
assert(evbuffer_add_printf(buf, RN "--" BOUNDARY RN));
|
||||
|
||||
@@ -741,21 +737,21 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
|
||||
}
|
||||
|
||||
static void _http_callback_stream_error(UNUSED struct bufferevent *buf_event, UNUSED short what, void *v_client) {
|
||||
stream_client_s *client = (stream_client_s *)v_client;
|
||||
server_s *server = client->server;
|
||||
us_stream_client_s *client = (us_stream_client_s *)v_client;
|
||||
us_server_s *server = client->server;
|
||||
|
||||
LIST_REMOVE_C(RUN(stream_clients), client, RUN(stream_clients_count));
|
||||
US_LIST_REMOVE_C(_RUN(stream_clients), client, _RUN(stream_clients_count));
|
||||
|
||||
if (RUN(stream_clients_count) == 0) {
|
||||
atomic_store(&VID(has_clients), false);
|
||||
if (_RUN(stream_clients_count) == 0) {
|
||||
atomic_store(&_VID(has_clients), false);
|
||||
# ifdef WITH_GPIO
|
||||
gpio_set_has_http_clients(false);
|
||||
us_gpio_set_has_http_clients(false);
|
||||
# endif
|
||||
}
|
||||
|
||||
char *reason = bufferevent_my_format_reason(what);
|
||||
LOG_INFO("HTTP: Disconnected client: %s, id=%" PRIx64 ", %s; clients now: %u",
|
||||
client->hostport, client->id, reason, RUN(stream_clients_count));
|
||||
char *reason = us_bufferevent_format_reason(what);
|
||||
US_LOG_INFO("HTTP: Disconnected client: %s, id=%" PRIx64 ", %s; clients now: %u",
|
||||
client->hostport, client->id, reason, _RUN(stream_clients_count));
|
||||
free(reason);
|
||||
|
||||
struct evhttp_connection *conn = evhttp_request_get_connection(client->request);
|
||||
@@ -768,11 +764,11 @@ static void _http_callback_stream_error(UNUSED struct bufferevent *buf_event, UN
|
||||
free(client);
|
||||
}
|
||||
|
||||
static void _http_queue_send_stream(server_s *server, bool stream_updated, bool frame_updated) {
|
||||
static void _http_queue_send_stream(us_server_s *server, bool stream_updated, bool frame_updated) {
|
||||
bool has_clients = false;
|
||||
bool queued = false;
|
||||
|
||||
LIST_ITERATE(RUN(stream_clients), client, {
|
||||
US_LIST_ITERATE(_RUN(stream_clients), client, {
|
||||
struct evhttp_connection *conn = evhttp_request_get_connection(client->request);
|
||||
if (conn) {
|
||||
// Фикс для бага WebKit. При включенной опции дропа одинаковых фреймов,
|
||||
@@ -808,45 +804,45 @@ static void _http_queue_send_stream(server_s *server, bool stream_updated, bool
|
||||
if (queued) {
|
||||
static unsigned queued_fps_accum = 0;
|
||||
static long long queued_fps_second = 0;
|
||||
long long now = floor_ms(get_now_monotonic());
|
||||
long long now = us_floor_ms(us_get_now_monotonic());
|
||||
if (now != queued_fps_second) {
|
||||
EX(queued_fps) = queued_fps_accum;
|
||||
_EX(queued_fps) = queued_fps_accum;
|
||||
queued_fps_accum = 0;
|
||||
queued_fps_second = now;
|
||||
}
|
||||
queued_fps_accum += 1;
|
||||
} else if (!has_clients) {
|
||||
EX(queued_fps) = 0;
|
||||
_EX(queued_fps) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void _http_request_watcher(UNUSED int fd, UNUSED short what, void *v_server) {
|
||||
server_s *server = (server_s *)v_server;
|
||||
const long double now = get_now_monotonic();
|
||||
us_server_s *server = (us_server_s *)v_server;
|
||||
const long double now = us_get_now_monotonic();
|
||||
|
||||
if (stream_has_clients(RUN(stream))) {
|
||||
RUN(last_request_ts) = now;
|
||||
} else if (RUN(last_request_ts) + server->exit_on_no_clients < now) {
|
||||
LOG_INFO("HTTP: No requests or HTTP/sink clients found in last %u seconds, exiting ...",
|
||||
if (us_stream_has_clients(_RUN(stream))) {
|
||||
_RUN(last_request_ts) = now;
|
||||
} else if (_RUN(last_request_ts) + server->exit_on_no_clients < now) {
|
||||
US_LOG_INFO("HTTP: No requests or HTTP/sink clients found in last %u seconds, exiting ...",
|
||||
server->exit_on_no_clients);
|
||||
process_suicide();
|
||||
RUN(last_request_ts) = now;
|
||||
us_process_suicide();
|
||||
_RUN(last_request_ts) = now;
|
||||
}
|
||||
}
|
||||
|
||||
static void _http_refresher(UNUSED int fd, UNUSED short what, void *v_server) {
|
||||
server_s *server = (server_s *)v_server;
|
||||
us_server_s *server = (us_server_s *)v_server;
|
||||
bool stream_updated = false;
|
||||
bool frame_updated = false;
|
||||
|
||||
if (atomic_load(&VID(updated))) {
|
||||
if (atomic_load(&_VID(updated))) {
|
||||
frame_updated = _expose_new_frame(server);
|
||||
stream_updated = true;
|
||||
} 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);
|
||||
} else if (_EX(expose_end_ts) + 1 < us_get_now_monotonic()) {
|
||||
US_LOG_DEBUG("HTTP: Repeating exposed ...");
|
||||
_EX(expose_begin_ts) = us_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;
|
||||
}
|
||||
@@ -857,68 +853,67 @@ static void _http_refresher(UNUSED int fd, UNUSED short what, void *v_server) {
|
||||
frame_updated
|
||||
&& server->notify_parent
|
||||
&& (
|
||||
EX(notify_last_online) != EX(frame->online)
|
||||
|| EX(notify_last_width) != EX(frame->width)
|
||||
|| EX(notify_last_height) != EX(frame->height)
|
||||
_EX(notify_last_online) != _EX(frame->online)
|
||||
|| _EX(notify_last_width) != _EX(frame->width)
|
||||
|| _EX(notify_last_height) != _EX(frame->height)
|
||||
)
|
||||
) {
|
||||
EX(notify_last_online) = EX(frame->online);
|
||||
EX(notify_last_width) = EX(frame->width);
|
||||
EX(notify_last_height) = EX(frame->height);
|
||||
process_notify_parent();
|
||||
_EX(notify_last_online) = _EX(frame->online);
|
||||
_EX(notify_last_width) = _EX(frame->width);
|
||||
_EX(notify_last_height) = _EX(frame->height);
|
||||
us_process_notify_parent();
|
||||
}
|
||||
}
|
||||
|
||||
static bool _expose_new_frame(server_s *server) {
|
||||
static bool _expose_new_frame(us_server_s *server) {
|
||||
bool updated = false;
|
||||
|
||||
A_MUTEX_LOCK(&VID(mutex));
|
||||
US_MUTEX_LOCK(&_VID(mutex));
|
||||
|
||||
LOG_DEBUG("HTTP: Updating exposed frame (online=%d) ...", VID(frame->online));
|
||||
US_LOG_DEBUG("HTTP: Updating exposed frame (online=%d) ...", _VID(frame->online));
|
||||
|
||||
EX(captured_fps) = VID(captured_fps);
|
||||
EX(expose_begin_ts) = get_now_monotonic();
|
||||
_EX(captured_fps) = _VID(captured_fps);
|
||||
_EX(expose_begin_ts) = us_get_now_monotonic();
|
||||
|
||||
if (server->drop_same_frames && VID(frame->online)) {
|
||||
if (server->drop_same_frames && _VID(frame->online)) {
|
||||
bool need_drop = false;
|
||||
bool maybe_same = false;
|
||||
if (
|
||||
(need_drop = (EX(dropped) < server->drop_same_frames))
|
||||
&& (maybe_same = frame_compare(EX(frame), VID(frame)))
|
||||
(need_drop = (_EX(dropped) < server->drop_same_frames))
|
||||
&& (maybe_same = us_frame_compare(_EX(frame), _VID(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;
|
||||
_EX(expose_cmp_ts) = us_get_now_monotonic();
|
||||
_EX(expose_end_ts) = _EX(expose_cmp_ts);
|
||||
US_LOG_VERBOSE("HTTP: Dropped same frame number %u; cmp_time=%.06Lf",
|
||||
_EX(dropped), _EX(expose_cmp_ts) - _EX(expose_begin_ts));
|
||||
_EX(dropped) += 1;
|
||||
goto not_updated;
|
||||
} else {
|
||||
EX(expose_cmp_ts) = get_now_monotonic();
|
||||
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)));
|
||||
_EX(expose_cmp_ts) = us_get_now_monotonic();
|
||||
US_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(VID(frame), EX(frame));
|
||||
us_frame_copy(_VID(frame), _EX(frame));
|
||||
|
||||
EX(dropped) = 0;
|
||||
EX(expose_cmp_ts) = EX(expose_begin_ts);
|
||||
EX(expose_end_ts) = get_now_monotonic();
|
||||
_EX(dropped) = 0;
|
||||
_EX(expose_cmp_ts) = _EX(expose_begin_ts);
|
||||
_EX(expose_end_ts) = us_get_now_monotonic();
|
||||
|
||||
LOG_VERBOSE("HTTP: Exposed frame: online=%d, exp_time=%.06Lf",
|
||||
EX(frame->online), EX(expose_end_ts) - EX(expose_begin_ts));
|
||||
US_LOG_VERBOSE("HTTP: Exposed frame: online=%d, exp_time=%.06Lf",
|
||||
_EX(frame->online), _EX(expose_end_ts) - _EX(expose_begin_ts));
|
||||
|
||||
updated = true;
|
||||
not_updated:
|
||||
atomic_store(&VID(updated), false);
|
||||
A_MUTEX_UNLOCK(&VID(mutex));
|
||||
atomic_store(&_VID(updated), false);
|
||||
US_MUTEX_UNLOCK(&_VID(mutex));
|
||||
return updated;
|
||||
}
|
||||
|
||||
#undef EX
|
||||
#undef VID
|
||||
#undef STREAM
|
||||
#undef RUN
|
||||
static const char *_http_get_header(struct evhttp_request *request, const char *key) {
|
||||
return evhttp_find_header(evhttp_request_get_input_headers(request), key);
|
||||
}
|
||||
|
||||
static char *_http_get_client_hostport(struct evhttp_request *request) {
|
||||
char *addr = NULL;
|
||||
@@ -930,7 +925,7 @@ static char *_http_get_client_hostport(struct evhttp_request *request) {
|
||||
assert(addr = strdup(peer));
|
||||
}
|
||||
|
||||
const char *xff = GET_HEADER("X-Forwarded-For");
|
||||
const char *xff = _http_get_header(request, "X-Forwarded-For");
|
||||
if (xff) {
|
||||
if (addr) {
|
||||
free(addr);
|
||||
@@ -949,9 +944,7 @@ static char *_http_get_client_hostport(struct evhttp_request *request) {
|
||||
}
|
||||
|
||||
char *hostport;
|
||||
A_ASPRINTF(hostport, "[%s]:%u", addr, port);
|
||||
US_ASPRINTF(hostport, "[%s]:%u", addr, port);
|
||||
free(addr);
|
||||
return hostport;
|
||||
}
|
||||
|
||||
#undef GET_HEADER
|
||||
|
||||
@@ -76,30 +76,30 @@
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct stream_client_sx {
|
||||
struct server_sx *server;
|
||||
struct evhttp_request *request;
|
||||
typedef struct us_stream_client_sx {
|
||||
struct us_server_sx *server;
|
||||
struct evhttp_request *request;
|
||||
|
||||
char *key;
|
||||
bool extra_headers;
|
||||
bool advance_headers;
|
||||
bool dual_final_frames;
|
||||
bool zero_data;
|
||||
char *key;
|
||||
bool extra_headers;
|
||||
bool advance_headers;
|
||||
bool dual_final_frames;
|
||||
bool zero_data;
|
||||
|
||||
char *hostport;
|
||||
uint64_t id;
|
||||
bool need_initial;
|
||||
bool need_first_frame;
|
||||
bool updated_prev;
|
||||
unsigned fps;
|
||||
unsigned fps_accum;
|
||||
long long fps_accum_second;
|
||||
char *hostport;
|
||||
uint64_t id;
|
||||
bool need_initial;
|
||||
bool need_first_frame;
|
||||
bool updated_prev;
|
||||
unsigned fps;
|
||||
unsigned fps_accum;
|
||||
long long fps_accum_second;
|
||||
|
||||
LIST_STRUCT(struct stream_client_sx);
|
||||
} stream_client_s;
|
||||
US_LIST_STRUCT(struct us_stream_client_sx);
|
||||
} us_stream_client_s;
|
||||
|
||||
typedef struct {
|
||||
frame_s *frame;
|
||||
us_frame_s *frame;
|
||||
unsigned captured_fps;
|
||||
unsigned queued_fps;
|
||||
unsigned dropped;
|
||||
@@ -107,10 +107,10 @@ typedef struct {
|
||||
long double expose_cmp_ts;
|
||||
long double expose_end_ts;
|
||||
|
||||
bool notify_last_online;
|
||||
unsigned notify_last_width;
|
||||
unsigned notify_last_height;
|
||||
} exposed_s;
|
||||
bool notify_last_online;
|
||||
unsigned notify_last_width;
|
||||
unsigned notify_last_height;
|
||||
} us_exposed_s;
|
||||
|
||||
typedef struct {
|
||||
struct event_base *base;
|
||||
@@ -123,14 +123,14 @@ typedef struct {
|
||||
long double last_request_ts;
|
||||
|
||||
struct event *refresher;
|
||||
stream_s *stream;
|
||||
exposed_s *exposed;
|
||||
us_stream_s *stream;
|
||||
us_exposed_s *exposed;
|
||||
|
||||
stream_client_s *stream_clients;
|
||||
us_stream_client_s *stream_clients;
|
||||
unsigned stream_clients_count;
|
||||
} server_runtime_s;
|
||||
} us_server_runtime_s;
|
||||
|
||||
typedef struct server_sx {
|
||||
typedef struct us_server_sx {
|
||||
char *host;
|
||||
unsigned port;
|
||||
|
||||
@@ -157,13 +157,13 @@ typedef struct server_sx {
|
||||
bool notify_parent;
|
||||
unsigned exit_on_no_clients;
|
||||
|
||||
server_runtime_s *run;
|
||||
} server_s;
|
||||
us_server_runtime_s *run;
|
||||
} us_server_s;
|
||||
|
||||
|
||||
server_s *server_init(stream_s *stream);
|
||||
void server_destroy(server_s *server);
|
||||
us_server_s *us_server_init(us_stream_s *stream);
|
||||
void us_server_destroy(us_server_s *server);
|
||||
|
||||
int server_listen(server_s *server);
|
||||
void server_loop(server_s *server);
|
||||
void server_loop_break(server_s *server);
|
||||
int us_server_listen(us_server_s *server);
|
||||
void us_server_loop(us_server_s *server);
|
||||
void us_server_loop_break(us_server_s *server);
|
||||
|
||||
@@ -23,29 +23,29 @@
|
||||
#include "static.h"
|
||||
|
||||
|
||||
char *find_static_file_path(const char *root_path, const char *request_path) {
|
||||
char *us_find_static_file_path(const char *root_path, const char *request_path) {
|
||||
char *path = NULL;
|
||||
|
||||
char *simplified_path = simplify_request_path(request_path);
|
||||
char *simplified_path = us_simplify_request_path(request_path);
|
||||
if (simplified_path[0] == '\0') {
|
||||
LOG_VERBOSE("HTTP: Invalid request path %s to static", request_path);
|
||||
US_LOG_VERBOSE("HTTP: Invalid request path %s to static", request_path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
A_CALLOC(path, strlen(root_path) + strlen(simplified_path) + 16); // + reserved for /index.html
|
||||
US_CALLOC(path, strlen(root_path) + strlen(simplified_path) + 16); // + reserved for /index.html
|
||||
sprintf(path, "%s/%s", root_path, simplified_path);
|
||||
|
||||
struct stat st;
|
||||
# define LOAD_STAT { \
|
||||
if (lstat(path, &st) < 0) { \
|
||||
LOG_VERBOSE_PERROR("HTTP: Can't stat() static path %s", path); \
|
||||
US_LOG_VERBOSE_PERROR("HTTP: Can't stat() static path %s", path); \
|
||||
goto error; \
|
||||
} \
|
||||
}
|
||||
|
||||
LOAD_STAT;
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
LOG_VERBOSE("HTTP: Requested static path %s is a directory, trying %s/index.html", path, path);
|
||||
US_LOG_VERBOSE("HTTP: Requested static path %s is a directory, trying %s/index.html", path, path);
|
||||
strcat(path, "/index.html");
|
||||
LOAD_STAT;
|
||||
}
|
||||
@@ -53,12 +53,12 @@ char *find_static_file_path(const char *root_path, const char *request_path) {
|
||||
# undef LOAD_STAT
|
||||
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
LOG_VERBOSE("HTTP: Not a regular file: %s", path);
|
||||
US_LOG_VERBOSE("HTTP: Not a regular file: %s", path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (access(path, R_OK) < 0) {
|
||||
LOG_VERBOSE_PERROR("HTTP: Can't access() R_OK file %s", path);
|
||||
US_LOG_VERBOSE_PERROR("HTTP: Can't access() R_OK file %s", path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,4 +35,4 @@
|
||||
#include "path.h"
|
||||
|
||||
|
||||
char *find_static_file_path(const char *root_path, const char *request_path);
|
||||
char *us_find_static_file_path(const char *root_path, const char *request_path);
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
#include "systemd.h"
|
||||
|
||||
|
||||
evutil_socket_t evhttp_my_bind_systemd(struct evhttp *http) {
|
||||
evutil_socket_t us_evhttp_bind_systemd(struct evhttp *http) {
|
||||
int fds = sd_listen_fds(1);
|
||||
if (fds < 1) {
|
||||
LOG_ERROR("No available systemd sockets");
|
||||
US_LOG_ERROR("No available systemd sockets");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ evutil_socket_t evhttp_my_bind_systemd(struct evhttp *http) {
|
||||
assert(!evutil_make_socket_nonblocking(fd));
|
||||
|
||||
if (evhttp_accept_socket(http, fd) < 0) {
|
||||
LOG_PERROR("Can't evhttp_accept_socket() systemd socket");
|
||||
US_LOG_PERROR("Can't evhttp_accept_socket() systemd socket");
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
|
||||
@@ -34,4 +34,4 @@
|
||||
#include "../../../libs/logging.h"
|
||||
|
||||
|
||||
evutil_socket_t evhttp_my_bind_systemd(struct evhttp *http);
|
||||
evutil_socket_t us_evhttp_bind_systemd(struct evhttp *http);
|
||||
|
||||
@@ -23,13 +23,13 @@
|
||||
#include "unix.h"
|
||||
|
||||
|
||||
evutil_socket_t evhttp_my_bind_unix(struct evhttp *http, const char *path, bool rm, mode_t mode) {
|
||||
evutil_socket_t us_evhttp_bind_unix(struct evhttp *http, const char *path, bool rm, mode_t mode) {
|
||||
struct sockaddr_un addr = {0};
|
||||
|
||||
# define MAX_SUN_PATH (sizeof(addr.sun_path) - 1)
|
||||
|
||||
if (strlen(path) > MAX_SUN_PATH) {
|
||||
LOG_ERROR("UNIX socket path is too long; max=%zu", MAX_SUN_PATH);
|
||||
US_LOG_ERROR("UNIX socket path is too long; max=%zu", MAX_SUN_PATH);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -44,24 +44,24 @@ evutil_socket_t evhttp_my_bind_unix(struct evhttp *http, const char *path, bool
|
||||
|
||||
if (rm && unlink(path) < 0) {
|
||||
if (errno != ENOENT) {
|
||||
LOG_PERROR("Can't remove old UNIX socket '%s'", path);
|
||||
US_LOG_PERROR("Can't remove old UNIX socket '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
|
||||
LOG_PERROR("Can't bind HTTP to UNIX socket '%s'", path);
|
||||
US_LOG_PERROR("Can't bind HTTP to UNIX socket '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
if (mode && chmod(path, mode) < 0) {
|
||||
LOG_PERROR("Can't set permissions %o to UNIX socket '%s'", mode, path);
|
||||
US_LOG_PERROR("Can't set permissions %o to UNIX socket '%s'", mode, path);
|
||||
return -1;
|
||||
}
|
||||
if (listen(fd, 128) < 0) {
|
||||
LOG_PERROR("Can't listen UNIX socket '%s'", path);
|
||||
US_LOG_PERROR("Can't listen UNIX socket '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
if (evhttp_accept_socket(http, fd) < 0) {
|
||||
LOG_PERROR("Can't evhttp_accept_socket() UNIX socket '%s'", path);
|
||||
US_LOG_PERROR("Can't evhttp_accept_socket() UNIX socket '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
|
||||
@@ -39,4 +39,4 @@
|
||||
#include "../../libs/logging.h"
|
||||
|
||||
|
||||
evutil_socket_t evhttp_my_bind_unix(struct evhttp *http, const char *path, bool rm, mode_t mode);
|
||||
evutil_socket_t us_evhttp_bind_unix(struct evhttp *http, const char *path, bool rm, mode_t mode);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "uri.h"
|
||||
|
||||
|
||||
bool uri_get_true(struct evkeyvalq *params, const char *key) {
|
||||
bool us_uri_get_true(struct evkeyvalq *params, const char *key) {
|
||||
const char *value_str = evhttp_find_header(params, key);
|
||||
if (value_str != NULL) {
|
||||
if (
|
||||
@@ -37,7 +37,7 @@ bool uri_get_true(struct evkeyvalq *params, const char *key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char *uri_get_string(struct evkeyvalq *params, const char *key) {
|
||||
char *us_uri_get_string(struct evkeyvalq *params, const char *key) {
|
||||
const char *value_str = evhttp_find_header(params, key);
|
||||
if (value_str != NULL) {
|
||||
return evhttp_encode_uri(value_str);
|
||||
|
||||
@@ -29,5 +29,5 @@
|
||||
#include <event2/keyvalq_struct.h>
|
||||
|
||||
|
||||
bool uri_get_true(struct evkeyvalq *params, const char *key);
|
||||
char *uri_get_string(struct evkeyvalq *params, const char *key);
|
||||
bool us_uri_get_true(struct evkeyvalq *params, const char *key);
|
||||
char *us_uri_get_string(struct evkeyvalq *params, const char *key);
|
||||
|
||||
@@ -23,29 +23,31 @@
|
||||
#include "m2m.h"
|
||||
|
||||
|
||||
static m2m_encoder_s *_m2m_encoder_init(
|
||||
static us_m2m_encoder_s *_m2m_encoder_init(
|
||||
const char *name, const char *path, unsigned output_format,
|
||||
unsigned fps, unsigned bitrate, unsigned gop, unsigned quality, bool allow_dma);
|
||||
|
||||
static void _m2m_encoder_prepare(m2m_encoder_s *enc, const frame_s *frame);
|
||||
static void _m2m_encoder_prepare(us_m2m_encoder_s *enc, const us_frame_s *frame);
|
||||
|
||||
static int _m2m_encoder_init_buffers(
|
||||
m2m_encoder_s *enc, const char *name, enum v4l2_buf_type type,
|
||||
m2m_buffer_s **bufs_ptr, unsigned *n_bufs_ptr, bool dma);
|
||||
us_m2m_encoder_s *enc, const char *name, enum v4l2_buf_type type,
|
||||
us_m2m_buffer_s **bufs_ptr, unsigned *n_bufs_ptr, bool dma);
|
||||
|
||||
static void _m2m_encoder_cleanup(m2m_encoder_s *enc);
|
||||
static void _m2m_encoder_cleanup(us_m2m_encoder_s *enc);
|
||||
|
||||
static int _m2m_encoder_compress_raw(m2m_encoder_s *enc, const frame_s *src, frame_s *dest, bool force_key);
|
||||
static int _m2m_encoder_compress_raw(us_m2m_encoder_s *enc, const us_frame_s *src, us_frame_s *dest, bool force_key);
|
||||
|
||||
|
||||
#define E_LOG_ERROR(_msg, ...) LOG_ERROR("%s: " _msg, enc->name, ##__VA_ARGS__)
|
||||
#define E_LOG_PERROR(_msg, ...) LOG_PERROR("%s: " _msg, enc->name, ##__VA_ARGS__)
|
||||
#define E_LOG_INFO(_msg, ...) LOG_INFO("%s: " _msg, enc->name, ##__VA_ARGS__)
|
||||
#define E_LOG_VERBOSE(_msg, ...) LOG_VERBOSE("%s: " _msg, enc->name, ##__VA_ARGS__)
|
||||
#define E_LOG_DEBUG(_msg, ...) LOG_DEBUG("%s: " _msg, enc->name, ##__VA_ARGS__)
|
||||
#define _E_LOG_ERROR(x_msg, ...) US_LOG_ERROR("%s: " x_msg, enc->name, ##__VA_ARGS__)
|
||||
#define _E_LOG_PERROR(x_msg, ...) US_LOG_PERROR("%s: " x_msg, enc->name, ##__VA_ARGS__)
|
||||
#define _E_LOG_INFO(x_msg, ...) US_LOG_INFO("%s: " x_msg, enc->name, ##__VA_ARGS__)
|
||||
#define _E_LOG_VERBOSE(x_msg, ...) US_LOG_VERBOSE("%s: " x_msg, enc->name, ##__VA_ARGS__)
|
||||
#define _E_LOG_DEBUG(x_msg, ...) US_LOG_DEBUG("%s: " x_msg, enc->name, ##__VA_ARGS__)
|
||||
|
||||
#define _RUN(x_next) enc->run->x_next
|
||||
|
||||
|
||||
m2m_encoder_s *m2m_h264_encoder_init(const char *name, const char *path, unsigned bitrate, unsigned gop) {
|
||||
us_m2m_encoder_s *us_m2m_h264_encoder_init(const char *name, const char *path, unsigned bitrate, unsigned gop) {
|
||||
// FIXME: 30 or 0? https://github.com/6by9/yavta/blob/master/yavta.c#L2100
|
||||
// По логике вещей правильно 0, но почему-то на низких разрешениях типа 640x480
|
||||
// енкодер через несколько секунд перестает производить корректные фреймы.
|
||||
@@ -53,7 +55,7 @@ m2m_encoder_s *m2m_h264_encoder_init(const char *name, const char *path, unsigne
|
||||
return _m2m_encoder_init(name, path, V4L2_PIX_FMT_H264, 30, bitrate, gop, 0, true);
|
||||
}
|
||||
|
||||
m2m_encoder_s *m2m_mjpeg_encoder_init(const char *name, const char *path, unsigned quality) {
|
||||
us_m2m_encoder_s *us_m2m_mjpeg_encoder_init(const char *name, const char *path, unsigned quality) {
|
||||
const double b_min = 25;
|
||||
const double b_max = 20000;
|
||||
const double step = 25;
|
||||
@@ -65,67 +67,65 @@ m2m_encoder_s *m2m_mjpeg_encoder_init(const char *name, const char *path, unsign
|
||||
return _m2m_encoder_init(name, path, V4L2_PIX_FMT_MJPEG, 30, bitrate, 0, 0, true);
|
||||
}
|
||||
|
||||
m2m_encoder_s *m2m_jpeg_encoder_init(const char *name, const char *path, unsigned quality) {
|
||||
us_m2m_encoder_s *us_m2m_jpeg_encoder_init(const char *name, const char *path, unsigned quality) {
|
||||
// FIXME: DMA не работает
|
||||
return _m2m_encoder_init(name, path, V4L2_PIX_FMT_JPEG, 30, 0, 0, quality, false);
|
||||
}
|
||||
|
||||
void m2m_encoder_destroy(m2m_encoder_s *enc) {
|
||||
E_LOG_INFO("Destroying encoder ...");
|
||||
void us_m2m_encoder_destroy(us_m2m_encoder_s *enc) {
|
||||
_E_LOG_INFO("Destroying encoder ...");
|
||||
_m2m_encoder_cleanup(enc);
|
||||
free(enc->path);
|
||||
free(enc->name);
|
||||
free(enc);
|
||||
}
|
||||
|
||||
#define RUN(_next) enc->run->_next
|
||||
|
||||
int m2m_encoder_compress(m2m_encoder_s *enc, const frame_s *src, frame_s *dest, bool force_key) {
|
||||
frame_encoding_begin(src, dest, (enc->output_format == V4L2_PIX_FMT_MJPEG ? V4L2_PIX_FMT_JPEG : enc->output_format));
|
||||
int us_m2m_encoder_compress(us_m2m_encoder_s *enc, const us_frame_s *src, us_frame_s *dest, bool force_key) {
|
||||
us_frame_encoding_begin(src, dest, (enc->output_format == V4L2_PIX_FMT_MJPEG ? V4L2_PIX_FMT_JPEG : enc->output_format));
|
||||
|
||||
if (
|
||||
RUN(width) != src->width
|
||||
|| RUN(height) != src->height
|
||||
|| RUN(input_format) != src->format
|
||||
|| RUN(stride) != src->stride
|
||||
|| RUN(dma) != (enc->allow_dma && src->dma_fd >= 0)
|
||||
_RUN(width) != src->width
|
||||
|| _RUN(height) != src->height
|
||||
|| _RUN(input_format) != src->format
|
||||
|| _RUN(stride) != src->stride
|
||||
|| _RUN(dma) != (enc->allow_dma && src->dma_fd >= 0)
|
||||
) {
|
||||
_m2m_encoder_prepare(enc, src);
|
||||
}
|
||||
if (!RUN(ready)) { // Already prepared but failed
|
||||
if (!_RUN(ready)) { // Already prepared but failed
|
||||
return -1;
|
||||
}
|
||||
|
||||
force_key = (enc->output_format == V4L2_PIX_FMT_H264 && (force_key || RUN(last_online) != src->online));
|
||||
force_key = (enc->output_format == V4L2_PIX_FMT_H264 && (force_key || _RUN(last_online) != src->online));
|
||||
|
||||
if (_m2m_encoder_compress_raw(enc, src, dest, force_key) < 0) {
|
||||
_m2m_encoder_cleanup(enc);
|
||||
E_LOG_ERROR("Encoder destroyed due an error (compress)");
|
||||
_E_LOG_ERROR("Encoder destroyed due an error (compress)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
frame_encoding_end(dest);
|
||||
us_frame_encoding_end(dest);
|
||||
|
||||
E_LOG_VERBOSE("Compressed new frame: size=%zu, time=%0.3Lf, force_key=%d",
|
||||
_E_LOG_VERBOSE("Compressed new frame: size=%zu, time=%0.3Lf, force_key=%d",
|
||||
dest->used, dest->encode_end_ts - dest->encode_begin_ts, force_key);
|
||||
|
||||
RUN(last_online) = src->online;
|
||||
_RUN(last_online) = src->online;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static m2m_encoder_s *_m2m_encoder_init(
|
||||
static us_m2m_encoder_s *_m2m_encoder_init(
|
||||
const char *name, const char *path, unsigned output_format,
|
||||
unsigned fps, unsigned bitrate, unsigned gop, unsigned quality, bool allow_dma) {
|
||||
|
||||
LOG_INFO("%s: Initializing encoder ...", name);
|
||||
US_LOG_INFO("%s: Initializing encoder ...", name);
|
||||
|
||||
m2m_encoder_runtime_s *run;
|
||||
A_CALLOC(run, 1);
|
||||
us_m2m_encoder_runtime_s *run;
|
||||
US_CALLOC(run, 1);
|
||||
run->last_online = -1;
|
||||
run->fd = -1;
|
||||
|
||||
m2m_encoder_s *enc;
|
||||
A_CALLOC(enc, 1);
|
||||
us_m2m_encoder_s *enc;
|
||||
US_CALLOC(enc, 1);
|
||||
assert(enc->name = strdup(name));
|
||||
if (path == NULL) {
|
||||
assert(enc->path = strdup(output_format == V4L2_PIX_FMT_JPEG ? "/dev/video31" : "/dev/video11"));
|
||||
@@ -142,45 +142,45 @@ static m2m_encoder_s *_m2m_encoder_init(
|
||||
return enc;
|
||||
}
|
||||
|
||||
#define E_XIOCTL(_request, _value, _msg, ...) { \
|
||||
if (xioctl(RUN(fd), _request, _value) < 0) { \
|
||||
E_LOG_PERROR(_msg, ##__VA_ARGS__); \
|
||||
#define _E_XIOCTL(x_request, x_value, x_msg, ...) { \
|
||||
if (us_xioctl(_RUN(fd), x_request, x_value) < 0) { \
|
||||
_E_LOG_PERROR(x_msg, ##__VA_ARGS__); \
|
||||
goto error; \
|
||||
} \
|
||||
}
|
||||
|
||||
static void _m2m_encoder_prepare(m2m_encoder_s *enc, const frame_s *frame) {
|
||||
static void _m2m_encoder_prepare(us_m2m_encoder_s *enc, const us_frame_s *frame) {
|
||||
bool dma = (enc->allow_dma && frame->dma_fd >= 0);
|
||||
|
||||
E_LOG_INFO("Configuring encoder: DMA=%d ...", dma);
|
||||
_E_LOG_INFO("Configuring encoder: DMA=%d ...", dma);
|
||||
|
||||
_m2m_encoder_cleanup(enc);
|
||||
|
||||
RUN(width) = frame->width;
|
||||
RUN(height) = frame->height;
|
||||
RUN(input_format) = frame->format;
|
||||
RUN(stride) = frame->stride;
|
||||
RUN(dma) = dma;
|
||||
_RUN(width) = frame->width;
|
||||
_RUN(height) = frame->height;
|
||||
_RUN(input_format) = frame->format;
|
||||
_RUN(stride) = frame->stride;
|
||||
_RUN(dma) = dma;
|
||||
|
||||
if ((RUN(fd) = open(enc->path, O_RDWR)) < 0) {
|
||||
E_LOG_PERROR("Can't open encoder device");
|
||||
if ((_RUN(fd) = open(enc->path, O_RDWR)) < 0) {
|
||||
_E_LOG_PERROR("Can't open encoder device");
|
||||
goto error;
|
||||
}
|
||||
E_LOG_DEBUG("Encoder device fd=%d opened", RUN(fd));
|
||||
_E_LOG_DEBUG("Encoder device fd=%d opened", _RUN(fd));
|
||||
|
||||
# define SET_OPTION(_cid, _value) { \
|
||||
struct v4l2_control _ctl = {0}; \
|
||||
_ctl.id = _cid; \
|
||||
_ctl.value = _value; \
|
||||
E_LOG_DEBUG("Configuring option " #_cid " ..."); \
|
||||
E_XIOCTL(VIDIOC_S_CTRL, &_ctl, "Can't set option " #_cid); \
|
||||
# define SET_OPTION(x_cid, x_value) { \
|
||||
struct v4l2_control m_ctl = {0}; \
|
||||
m_ctl.id = x_cid; \
|
||||
m_ctl.value = x_value; \
|
||||
_E_LOG_DEBUG("Configuring option " #x_cid " ..."); \
|
||||
_E_XIOCTL(VIDIOC_S_CTRL, &m_ctl, "Can't set option " #x_cid); \
|
||||
}
|
||||
|
||||
if (enc->output_format == V4L2_PIX_FMT_H264) {
|
||||
SET_OPTION(V4L2_CID_MPEG_VIDEO_BITRATE, enc->bitrate);
|
||||
SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, enc->gop);
|
||||
SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE);
|
||||
if (RUN(width) * RUN(height) <= 1920 * 1080) { // https://forums.raspberrypi.com/viewtopic.php?t=291447#p1762296
|
||||
if (_RUN(width) * _RUN(height) <= 1920 * 1080) { // https://forums.raspberrypi.com/viewtopic.php?t=291447#p1762296
|
||||
SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_LEVEL, V4L2_MPEG_VIDEO_H264_LEVEL_4_0);
|
||||
} else {
|
||||
SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_LEVEL, V4L2_MPEG_VIDEO_H264_LEVEL_5_1);
|
||||
@@ -199,35 +199,35 @@ static void _m2m_encoder_prepare(m2m_encoder_s *enc, const frame_s *frame) {
|
||||
{
|
||||
struct v4l2_format fmt = {0};
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
|
||||
fmt.fmt.pix_mp.width = RUN(width);
|
||||
fmt.fmt.pix_mp.height = RUN(height);
|
||||
fmt.fmt.pix_mp.pixelformat = RUN(input_format);
|
||||
fmt.fmt.pix_mp.width = _RUN(width);
|
||||
fmt.fmt.pix_mp.height = _RUN(height);
|
||||
fmt.fmt.pix_mp.pixelformat = _RUN(input_format);
|
||||
fmt.fmt.pix_mp.field = V4L2_FIELD_ANY;
|
||||
fmt.fmt.pix_mp.colorspace = V4L2_COLORSPACE_JPEG; // libcamera currently has no means to request the right colour space
|
||||
fmt.fmt.pix_mp.num_planes = 1;
|
||||
// fmt.fmt.pix_mp.plane_fmt[0].bytesperline = RUN(stride);
|
||||
E_LOG_DEBUG("Configuring INPUT format ...");
|
||||
E_XIOCTL(VIDIOC_S_FMT, &fmt, "Can't set INPUT format");
|
||||
// fmt.fmt.pix_mp.plane_fmt[0].bytesperline = _RUN(stride);
|
||||
_E_LOG_DEBUG("Configuring INPUT format ...");
|
||||
_E_XIOCTL(VIDIOC_S_FMT, &fmt, "Can't set INPUT format");
|
||||
}
|
||||
|
||||
{
|
||||
struct v4l2_format fmt = {0};
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
||||
fmt.fmt.pix_mp.width = RUN(width);
|
||||
fmt.fmt.pix_mp.height = RUN(height);
|
||||
fmt.fmt.pix_mp.width = _RUN(width);
|
||||
fmt.fmt.pix_mp.height = _RUN(height);
|
||||
fmt.fmt.pix_mp.pixelformat = enc->output_format;
|
||||
fmt.fmt.pix_mp.field = V4L2_FIELD_ANY;
|
||||
fmt.fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
|
||||
fmt.fmt.pix_mp.num_planes = 1;
|
||||
// fmt.fmt.pix_mp.plane_fmt[0].bytesperline = 0;
|
||||
// fmt.fmt.pix_mp.plane_fmt[0].sizeimage = 512 << 10;
|
||||
E_LOG_DEBUG("Configuring OUTPUT format ...");
|
||||
E_XIOCTL(VIDIOC_S_FMT, &fmt, "Can't set OUTPUT format");
|
||||
_E_LOG_DEBUG("Configuring OUTPUT format ...");
|
||||
_E_XIOCTL(VIDIOC_S_FMT, &fmt, "Can't set OUTPUT format");
|
||||
if (fmt.fmt.pix_mp.pixelformat != enc->output_format) {
|
||||
char fourcc_str[8];
|
||||
E_LOG_ERROR("The OUTPUT format can't be configured as %s",
|
||||
fourcc_to_string(enc->output_format, fourcc_str, 8));
|
||||
E_LOG_ERROR("In case of Raspberry Pi, try to append 'start_x=1' to /boot/config.txt");
|
||||
_E_LOG_ERROR("The OUTPUT format can't be configured as %s",
|
||||
us_fourcc_to_string(enc->output_format, fourcc_str, 8));
|
||||
_E_LOG_ERROR("In case of Raspberry Pi, try to append 'start_x=1' to /boot/config.txt");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
@@ -237,61 +237,61 @@ static void _m2m_encoder_prepare(m2m_encoder_s *enc, const frame_s *frame) {
|
||||
setfps.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
|
||||
setfps.parm.output.timeperframe.numerator = 1;
|
||||
setfps.parm.output.timeperframe.denominator = enc->fps;
|
||||
E_LOG_DEBUG("Configuring INPUT FPS ...");
|
||||
E_XIOCTL(VIDIOC_S_PARM, &setfps, "Can't set INPUT FPS");
|
||||
_E_LOG_DEBUG("Configuring INPUT FPS ...");
|
||||
_E_XIOCTL(VIDIOC_S_PARM, &setfps, "Can't set INPUT FPS");
|
||||
}
|
||||
|
||||
if (_m2m_encoder_init_buffers(enc, (dma ? "INPUT-DMA" : "INPUT"), V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
|
||||
&RUN(input_bufs), &RUN(n_input_bufs), dma) < 0) {
|
||||
&_RUN(input_bufs), &_RUN(n_input_bufs), dma) < 0) {
|
||||
goto error;
|
||||
}
|
||||
if (_m2m_encoder_init_buffers(enc, "OUTPUT", V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
|
||||
&RUN(output_bufs), &RUN(n_output_bufs), false) < 0) {
|
||||
&_RUN(output_bufs), &_RUN(n_output_bufs), false) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
{
|
||||
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
|
||||
E_LOG_DEBUG("Starting INPUT ...");
|
||||
E_XIOCTL(VIDIOC_STREAMON, &type, "Can't start INPUT");
|
||||
_E_LOG_DEBUG("Starting INPUT ...");
|
||||
_E_XIOCTL(VIDIOC_STREAMON, &type, "Can't start INPUT");
|
||||
|
||||
type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
||||
E_LOG_DEBUG("Starting OUTPUT ...");
|
||||
E_XIOCTL(VIDIOC_STREAMON, &type, "Can't start OUTPUT");
|
||||
_E_LOG_DEBUG("Starting OUTPUT ...");
|
||||
_E_XIOCTL(VIDIOC_STREAMON, &type, "Can't start OUTPUT");
|
||||
}
|
||||
|
||||
RUN(ready) = true;
|
||||
E_LOG_DEBUG("Encoder state: *** READY ***");
|
||||
_RUN(ready) = true;
|
||||
_E_LOG_DEBUG("Encoder state: *** READY ***");
|
||||
return;
|
||||
|
||||
error:
|
||||
_m2m_encoder_cleanup(enc);
|
||||
E_LOG_ERROR("Encoder destroyed due an error (prepare)");
|
||||
_E_LOG_ERROR("Encoder destroyed due an error (prepare)");
|
||||
}
|
||||
|
||||
static int _m2m_encoder_init_buffers(
|
||||
m2m_encoder_s *enc, const char *name, enum v4l2_buf_type type,
|
||||
m2m_buffer_s **bufs_ptr, unsigned *n_bufs_ptr, bool dma) {
|
||||
us_m2m_encoder_s *enc, const char *name, enum v4l2_buf_type type,
|
||||
us_m2m_buffer_s **bufs_ptr, unsigned *n_bufs_ptr, bool dma) {
|
||||
|
||||
E_LOG_DEBUG("Initializing %s buffers ...", name);
|
||||
_E_LOG_DEBUG("Initializing %s buffers ...", name);
|
||||
|
||||
struct v4l2_requestbuffers req = {0};
|
||||
req.count = 1;
|
||||
req.type = type;
|
||||
req.memory = (dma ? V4L2_MEMORY_DMABUF : V4L2_MEMORY_MMAP);
|
||||
|
||||
E_LOG_DEBUG("Requesting %u %s buffers ...", req.count, name);
|
||||
E_XIOCTL(VIDIOC_REQBUFS, &req, "Can't request %s buffers", name);
|
||||
_E_LOG_DEBUG("Requesting %u %s buffers ...", req.count, name);
|
||||
_E_XIOCTL(VIDIOC_REQBUFS, &req, "Can't request %s buffers", name);
|
||||
if (req.count < 1) {
|
||||
E_LOG_ERROR("Insufficient %s buffer memory: %u", name, req.count);
|
||||
_E_LOG_ERROR("Insufficient %s buffer memory: %u", name, req.count);
|
||||
goto error;
|
||||
}
|
||||
E_LOG_DEBUG("Got %u %s buffers", req.count, name);
|
||||
_E_LOG_DEBUG("Got %u %s buffers", req.count, name);
|
||||
|
||||
if (dma) {
|
||||
*n_bufs_ptr = req.count;
|
||||
} else {
|
||||
A_CALLOC(*bufs_ptr, req.count);
|
||||
US_CALLOC(*bufs_ptr, req.count);
|
||||
for (*n_bufs_ptr = 0; *n_bufs_ptr < req.count; ++(*n_bufs_ptr)) {
|
||||
struct v4l2_buffer buf = {0};
|
||||
struct v4l2_plane plane = {0};
|
||||
@@ -301,25 +301,25 @@ static int _m2m_encoder_init_buffers(
|
||||
buf.length = 1;
|
||||
buf.m.planes = &plane;
|
||||
|
||||
E_LOG_DEBUG("Querying %s buffer=%u ...", name, *n_bufs_ptr);
|
||||
E_XIOCTL(VIDIOC_QUERYBUF, &buf, "Can't query %s buffer=%u", name, *n_bufs_ptr);
|
||||
_E_LOG_DEBUG("Querying %s buffer=%u ...", name, *n_bufs_ptr);
|
||||
_E_XIOCTL(VIDIOC_QUERYBUF, &buf, "Can't query %s buffer=%u", name, *n_bufs_ptr);
|
||||
|
||||
E_LOG_DEBUG("Mapping %s buffer=%u ...", name, *n_bufs_ptr);
|
||||
_E_LOG_DEBUG("Mapping %s buffer=%u ...", name, *n_bufs_ptr);
|
||||
if (((*bufs_ptr)[*n_bufs_ptr].data = mmap(
|
||||
NULL,
|
||||
plane.length,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
RUN(fd),
|
||||
_RUN(fd),
|
||||
plane.m.mem_offset
|
||||
)) == MAP_FAILED) {
|
||||
E_LOG_PERROR("Can't map %s buffer=%u", name, *n_bufs_ptr);
|
||||
_E_LOG_PERROR("Can't map %s buffer=%u", name, *n_bufs_ptr);
|
||||
goto error;
|
||||
}
|
||||
(*bufs_ptr)[*n_bufs_ptr].allocated = plane.length;
|
||||
|
||||
E_LOG_DEBUG("Queuing %s buffer=%u ...", name, *n_bufs_ptr);
|
||||
E_XIOCTL(VIDIOC_QBUF, &buf, "Can't queue %s buffer=%u", name, *n_bufs_ptr);
|
||||
_E_LOG_DEBUG("Queuing %s buffer=%u ...", name, *n_bufs_ptr);
|
||||
_E_XIOCTL(VIDIOC_QBUF, &buf, "Can't queue %s buffer=%u", name, *n_bufs_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,13 +328,13 @@ static int _m2m_encoder_init_buffers(
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void _m2m_encoder_cleanup(m2m_encoder_s *enc) {
|
||||
if (RUN(ready)) {
|
||||
# define STOP_STREAM(_name, _type) { \
|
||||
enum v4l2_buf_type _type_var = _type; \
|
||||
E_LOG_DEBUG("Stopping %s ...", _name); \
|
||||
if (xioctl(RUN(fd), VIDIOC_STREAMOFF, &_type_var) < 0) { \
|
||||
E_LOG_PERROR("Can't stop %s", _name); \
|
||||
static void _m2m_encoder_cleanup(us_m2m_encoder_s *enc) {
|
||||
if (_RUN(ready)) {
|
||||
# define STOP_STREAM(x_name, x_type) { \
|
||||
enum v4l2_buf_type m_type_var = x_type; \
|
||||
_E_LOG_DEBUG("Stopping %s ...", x_name); \
|
||||
if (us_xioctl(_RUN(fd), VIDIOC_STREAMOFF, &m_type_var) < 0) { \
|
||||
_E_LOG_PERROR("Can't stop %s", x_name); \
|
||||
} \
|
||||
}
|
||||
|
||||
@@ -344,19 +344,19 @@ static void _m2m_encoder_cleanup(m2m_encoder_s *enc) {
|
||||
# undef STOP_STREAM
|
||||
}
|
||||
|
||||
# define DESTROY_BUFFERS(_name, _target) { \
|
||||
if (RUN(_target##_bufs)) { \
|
||||
for (unsigned index = 0; index < RUN(n_##_target##_bufs); ++index) { \
|
||||
if (RUN(_target##_bufs[index].allocated) > 0 && RUN(_target##_bufs[index].data) != MAP_FAILED) { \
|
||||
if (munmap(RUN(_target##_bufs[index].data), RUN(_target##_bufs[index].allocated)) < 0) { \
|
||||
E_LOG_PERROR("Can't unmap %s buffer=%u", #_name, index); \
|
||||
# define DESTROY_BUFFERS(x_name, x_target) { \
|
||||
if (_RUN(x_target##_bufs)) { \
|
||||
for (unsigned m_index = 0; m_index < _RUN(n_##x_target##_bufs); ++m_index) { \
|
||||
if (_RUN(x_target##_bufs[m_index].allocated) > 0 && _RUN(x_target##_bufs[m_index].data) != MAP_FAILED) { \
|
||||
if (munmap(_RUN(x_target##_bufs[m_index].data), _RUN(x_target##_bufs[m_index].allocated)) < 0) { \
|
||||
_E_LOG_PERROR("Can't unmap %s buffer=%u", #x_name, m_index); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
free(RUN(_target##_bufs)); \
|
||||
RUN(_target##_bufs) = NULL; \
|
||||
free(_RUN(x_target##_bufs)); \
|
||||
_RUN(x_target##_bufs) = NULL; \
|
||||
} \
|
||||
RUN(n_##_target##_bufs) = 0; \
|
||||
_RUN(n_##x_target##_bufs) = 0; \
|
||||
}
|
||||
|
||||
DESTROY_BUFFERS("OUTPUT", output);
|
||||
@@ -364,30 +364,30 @@ static void _m2m_encoder_cleanup(m2m_encoder_s *enc) {
|
||||
|
||||
# undef DESTROY_BUFFERS
|
||||
|
||||
if (RUN(fd) >= 0) {
|
||||
if (close(RUN(fd)) < 0) {
|
||||
E_LOG_PERROR("Can't close encoder device");
|
||||
if (_RUN(fd) >= 0) {
|
||||
if (close(_RUN(fd)) < 0) {
|
||||
_E_LOG_PERROR("Can't close encoder device");
|
||||
}
|
||||
RUN(fd) = -1;
|
||||
_RUN(fd) = -1;
|
||||
}
|
||||
|
||||
RUN(last_online) = -1;
|
||||
RUN(ready) = false;
|
||||
_RUN(last_online) = -1;
|
||||
_RUN(ready) = false;
|
||||
|
||||
E_LOG_DEBUG("Encoder state: ~~~ NOT READY ~~~");
|
||||
_E_LOG_DEBUG("Encoder state: ~~~ NOT READY ~~~");
|
||||
}
|
||||
|
||||
static int _m2m_encoder_compress_raw(m2m_encoder_s *enc, const frame_s *src, frame_s *dest, bool force_key) {
|
||||
assert(RUN(ready));
|
||||
static int _m2m_encoder_compress_raw(us_m2m_encoder_s *enc, const us_frame_s *src, us_frame_s *dest, bool force_key) {
|
||||
assert(_RUN(ready));
|
||||
|
||||
E_LOG_DEBUG("Compressing new frame; force_key=%d ...", force_key);
|
||||
_E_LOG_DEBUG("Compressing new frame; force_key=%d ...", force_key);
|
||||
|
||||
if (force_key) {
|
||||
struct v4l2_control ctl = {0};
|
||||
ctl.id = V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME;
|
||||
ctl.value = 1;
|
||||
E_LOG_DEBUG("Forcing keyframe ...")
|
||||
E_XIOCTL(VIDIOC_S_CTRL, &ctl, "Can't force keyframe");
|
||||
_E_LOG_DEBUG("Forcing keyframe ...")
|
||||
_E_XIOCTL(VIDIOC_S_CTRL, &ctl, "Can't force keyframe");
|
||||
}
|
||||
|
||||
struct v4l2_buffer input_buf = {0};
|
||||
@@ -396,25 +396,25 @@ static int _m2m_encoder_compress_raw(m2m_encoder_s *enc, const frame_s *src, fra
|
||||
input_buf.length = 1;
|
||||
input_buf.m.planes = &input_plane;
|
||||
|
||||
if (RUN(dma)) {
|
||||
if (_RUN(dma)) {
|
||||
input_buf.index = 0;
|
||||
input_buf.memory = V4L2_MEMORY_DMABUF;
|
||||
input_buf.field = V4L2_FIELD_NONE;
|
||||
input_plane.m.fd = src->dma_fd;
|
||||
E_LOG_DEBUG("Using INPUT-DMA buffer=%u", input_buf.index);
|
||||
_E_LOG_DEBUG("Using INPUT-DMA buffer=%u", input_buf.index);
|
||||
} else {
|
||||
input_buf.memory = V4L2_MEMORY_MMAP;
|
||||
E_LOG_DEBUG("Grabbing INPUT buffer ...");
|
||||
E_XIOCTL(VIDIOC_DQBUF, &input_buf, "Can't grab INPUT buffer");
|
||||
if (input_buf.index >= RUN(n_input_bufs)) {
|
||||
E_LOG_ERROR("V4L2 error: grabbed invalid INPUT: buffer=%u, n_bufs=%u",
|
||||
input_buf.index, RUN(n_input_bufs));
|
||||
_E_LOG_DEBUG("Grabbing INPUT buffer ...");
|
||||
_E_XIOCTL(VIDIOC_DQBUF, &input_buf, "Can't grab INPUT buffer");
|
||||
if (input_buf.index >= _RUN(n_input_bufs)) {
|
||||
_E_LOG_ERROR("V4L2 error: grabbed invalid INPUT: buffer=%u, n_bufs=%u",
|
||||
input_buf.index, _RUN(n_input_bufs));
|
||||
goto error;
|
||||
}
|
||||
E_LOG_DEBUG("Grabbed INPUT buffer=%u", input_buf.index);
|
||||
_E_LOG_DEBUG("Grabbed INPUT buffer=%u", input_buf.index);
|
||||
}
|
||||
|
||||
uint64_t now = get_now_monotonic_u64();
|
||||
uint64_t now = us_get_now_monotonic_u64();
|
||||
struct timeval ts = {
|
||||
.tv_sec = now / 1000000,
|
||||
.tv_usec = now % 1000000,
|
||||
@@ -424,31 +424,31 @@ static int _m2m_encoder_compress_raw(m2m_encoder_s *enc, const frame_s *src, fra
|
||||
input_buf.timestamp.tv_usec = ts.tv_usec;
|
||||
input_plane.bytesused = src->used;
|
||||
input_plane.length = src->used;
|
||||
if (!RUN(dma)) {
|
||||
memcpy(RUN(input_bufs[input_buf.index].data), src->data, src->used);
|
||||
if (!_RUN(dma)) {
|
||||
memcpy(_RUN(input_bufs[input_buf.index].data), src->data, src->used);
|
||||
}
|
||||
|
||||
const char *input_name = (RUN(dma) ? "INPUT-DMA" : "INPUT");
|
||||
const char *input_name = (_RUN(dma) ? "INPUT-DMA" : "INPUT");
|
||||
|
||||
E_LOG_DEBUG("Sending%s %s buffer ...", (!RUN(dma) ? " (releasing)" : ""), input_name);
|
||||
E_XIOCTL(VIDIOC_QBUF, &input_buf, "Can't send %s buffer", input_name);
|
||||
_E_LOG_DEBUG("Sending%s %s buffer ...", (!_RUN(dma) ? " (releasing)" : ""), input_name);
|
||||
_E_XIOCTL(VIDIOC_QBUF, &input_buf, "Can't send %s buffer", input_name);
|
||||
|
||||
// Для не-DMA отправка буфера по факту являтся освобождением этого буфера
|
||||
bool input_released = !RUN(dma);
|
||||
bool input_released = !_RUN(dma);
|
||||
|
||||
while (true) {
|
||||
struct pollfd enc_poll = {RUN(fd), POLLIN, 0};
|
||||
struct pollfd enc_poll = {_RUN(fd), POLLIN, 0};
|
||||
|
||||
E_LOG_DEBUG("Polling encoder ...");
|
||||
_E_LOG_DEBUG("Polling encoder ...");
|
||||
if (poll(&enc_poll, 1, 1000) < 0 && errno != EINTR) {
|
||||
E_LOG_PERROR("Can't poll encoder");
|
||||
_E_LOG_PERROR("Can't poll encoder");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (enc_poll.revents & POLLIN) {
|
||||
if (!input_released) {
|
||||
E_LOG_DEBUG("Releasing %s buffer=%u ...", input_name, input_buf.index);
|
||||
E_XIOCTL(VIDIOC_DQBUF, &input_buf, "Can't release %s buffer=%u",
|
||||
_E_LOG_DEBUG("Releasing %s buffer=%u ...", input_name, input_buf.index);
|
||||
_E_XIOCTL(VIDIOC_DQBUF, &input_buf, "Can't release %s buffer=%u",
|
||||
input_name, input_buf.index);
|
||||
input_released = true;
|
||||
}
|
||||
@@ -459,23 +459,23 @@ static int _m2m_encoder_compress_raw(m2m_encoder_s *enc, const frame_s *src, fra
|
||||
output_buf.memory = V4L2_MEMORY_MMAP;
|
||||
output_buf.length = 1;
|
||||
output_buf.m.planes = &output_plane;
|
||||
E_LOG_DEBUG("Fetching OUTPUT buffer ...");
|
||||
E_XIOCTL(VIDIOC_DQBUF, &output_buf, "Can't fetch OUTPUT buffer");
|
||||
_E_LOG_DEBUG("Fetching OUTPUT buffer ...");
|
||||
_E_XIOCTL(VIDIOC_DQBUF, &output_buf, "Can't fetch OUTPUT buffer");
|
||||
|
||||
bool done = false;
|
||||
if (ts.tv_sec != output_buf.timestamp.tv_sec || ts.tv_usec != output_buf.timestamp.tv_usec) {
|
||||
// Енкодер первый раз может выдать буфер с мусором и нулевым таймстампом,
|
||||
// так что нужно убедиться, что мы читаем выходной буфер, соответствующий
|
||||
// входному (с тем же таймстампом).
|
||||
E_LOG_DEBUG("Need to retry OUTPUT buffer due timestamp mismatch");
|
||||
_E_LOG_DEBUG("Need to retry OUTPUT buffer due timestamp mismatch");
|
||||
} else {
|
||||
frame_set_data(dest, RUN(output_bufs[output_buf.index].data), output_plane.bytesused);
|
||||
us_frame_set_data(dest, _RUN(output_bufs[output_buf.index].data), output_plane.bytesused);
|
||||
dest->key = output_buf.flags & V4L2_BUF_FLAG_KEYFRAME;
|
||||
done = true;
|
||||
}
|
||||
|
||||
E_LOG_DEBUG("Releasing OUTPUT buffer=%u ...", output_buf.index);
|
||||
E_XIOCTL(VIDIOC_QBUF, &output_buf, "Can't release OUTPUT buffer=%u", output_buf.index);
|
||||
_E_LOG_DEBUG("Releasing OUTPUT buffer=%u ...", output_buf.index);
|
||||
_E_XIOCTL(VIDIOC_QBUF, &output_buf, "Can't release OUTPUT buffer=%u", output_buf.index);
|
||||
|
||||
if (done) {
|
||||
break;
|
||||
@@ -488,12 +488,4 @@ static int _m2m_encoder_compress_raw(m2m_encoder_s *enc, const frame_s *src, fra
|
||||
return -1;
|
||||
}
|
||||
|
||||
#undef E_XIOCTL
|
||||
|
||||
#undef RUN
|
||||
|
||||
#undef E_LOG_DEBUG
|
||||
#undef E_LOG_VERBOSE
|
||||
#undef E_LOG_INFO
|
||||
#undef E_LOG_PERROR
|
||||
#undef E_LOG_ERROR
|
||||
#undef _E_XIOCTL
|
||||
|
||||
@@ -45,24 +45,24 @@
|
||||
typedef struct {
|
||||
uint8_t *data;
|
||||
size_t allocated;
|
||||
} m2m_buffer_s;
|
||||
} us_m2m_buffer_s;
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
m2m_buffer_s *input_bufs;
|
||||
us_m2m_buffer_s *input_bufs;
|
||||
unsigned n_input_bufs;
|
||||
m2m_buffer_s *output_bufs;
|
||||
us_m2m_buffer_s *output_bufs;
|
||||
unsigned n_output_bufs;
|
||||
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned input_format;
|
||||
unsigned stride;
|
||||
bool dma;
|
||||
bool ready;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned input_format;
|
||||
unsigned stride;
|
||||
bool dma;
|
||||
bool ready;
|
||||
|
||||
int last_online;
|
||||
} m2m_encoder_runtime_s;
|
||||
int last_online;
|
||||
} us_m2m_encoder_runtime_s;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
@@ -74,13 +74,13 @@ typedef struct {
|
||||
unsigned quality;
|
||||
bool allow_dma;
|
||||
|
||||
m2m_encoder_runtime_s *run;
|
||||
} m2m_encoder_s;
|
||||
us_m2m_encoder_runtime_s *run;
|
||||
} us_m2m_encoder_s;
|
||||
|
||||
|
||||
m2m_encoder_s *m2m_h264_encoder_init(const char *name, const char *path, unsigned bitrate, unsigned gop);
|
||||
m2m_encoder_s *m2m_mjpeg_encoder_init(const char *name, const char *path, unsigned quality);
|
||||
m2m_encoder_s *m2m_jpeg_encoder_init(const char *name, const char *path, unsigned quality);
|
||||
void m2m_encoder_destroy(m2m_encoder_s *enc);
|
||||
us_m2m_encoder_s *us_m2m_h264_encoder_init(const char *name, const char *path, unsigned bitrate, unsigned gop);
|
||||
us_m2m_encoder_s *us_m2m_mjpeg_encoder_init(const char *name, const char *path, unsigned quality);
|
||||
us_m2m_encoder_s *us_m2m_jpeg_encoder_init(const char *name, const char *path, unsigned quality);
|
||||
void us_m2m_encoder_destroy(us_m2m_encoder_s *enc);
|
||||
|
||||
int m2m_encoder_compress(m2m_encoder_s *enc, const frame_s *src, frame_s *dest, bool force_key);
|
||||
int us_m2m_encoder_compress(us_m2m_encoder_s *enc, const us_frame_s *src, us_frame_s *dest, bool force_key);
|
||||
|
||||
@@ -41,8 +41,8 @@
|
||||
|
||||
|
||||
typedef struct {
|
||||
stream_s *stream;
|
||||
server_s *server;
|
||||
us_stream_s *stream;
|
||||
us_server_s *server;
|
||||
} _main_context_s;
|
||||
|
||||
static _main_context_s *_ctx;
|
||||
@@ -56,27 +56,27 @@ static void _block_thread_signals(void) {
|
||||
}
|
||||
|
||||
static void *_stream_loop_thread(UNUSED void *arg) {
|
||||
A_THREAD_RENAME("stream");
|
||||
US_THREAD_RENAME("stream");
|
||||
_block_thread_signals();
|
||||
stream_loop(_ctx->stream);
|
||||
us_stream_loop(_ctx->stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *_server_loop_thread(UNUSED void *arg) {
|
||||
A_THREAD_RENAME("http");
|
||||
US_THREAD_RENAME("http");
|
||||
_block_thread_signals();
|
||||
server_loop(_ctx->server);
|
||||
us_server_loop(_ctx->server);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _signal_handler(int signum) {
|
||||
switch (signum) {
|
||||
case SIGTERM: LOG_INFO_NOLOCK("===== Stopping by SIGTERM ====="); break;
|
||||
case SIGINT: LOG_INFO_NOLOCK("===== Stopping by SIGINT ====="); break;
|
||||
default: LOG_INFO_NOLOCK("===== Stopping by %d =====", signum); break;
|
||||
case SIGTERM: US_LOG_INFO_NOLOCK("===== Stopping by SIGTERM ====="); break;
|
||||
case SIGINT: US_LOG_INFO_NOLOCK("===== Stopping by SIGINT ====="); break;
|
||||
default: US_LOG_INFO_NOLOCK("===== Stopping by %d =====", signum); break;
|
||||
}
|
||||
stream_loop_break(_ctx->stream);
|
||||
server_loop_break(_ctx->server);
|
||||
us_stream_loop_break(_ctx->stream);
|
||||
us_server_loop_break(_ctx->server);
|
||||
}
|
||||
|
||||
static void _install_signal_handlers(void) {
|
||||
@@ -87,13 +87,13 @@ static void _install_signal_handlers(void) {
|
||||
assert(!sigaddset(&sig_act.sa_mask, SIGINT));
|
||||
assert(!sigaddset(&sig_act.sa_mask, SIGTERM));
|
||||
|
||||
LOG_DEBUG("Installing SIGINT handler ...");
|
||||
US_LOG_DEBUG("Installing SIGINT handler ...");
|
||||
assert(!sigaction(SIGINT, &sig_act, NULL));
|
||||
|
||||
LOG_DEBUG("Installing SIGTERM handler ...");
|
||||
US_LOG_DEBUG("Installing SIGTERM handler ...");
|
||||
assert(!sigaction(SIGTERM, &sig_act, NULL));
|
||||
|
||||
LOG_DEBUG("Ignoring SIGPIPE ...");
|
||||
US_LOG_DEBUG("Ignoring SIGPIPE ...");
|
||||
assert(signal(SIGPIPE, SIG_IGN) != SIG_ERR);
|
||||
}
|
||||
|
||||
@@ -101,18 +101,18 @@ int main(int argc, char *argv[]) {
|
||||
assert(argc >= 0);
|
||||
int exit_code = 0;
|
||||
|
||||
LOGGING_INIT;
|
||||
A_THREAD_RENAME("main");
|
||||
US_LOGGING_INIT;
|
||||
US_THREAD_RENAME("main");
|
||||
|
||||
options_s *options = options_init(argc, argv);
|
||||
device_s *dev = device_init();
|
||||
encoder_s *enc = encoder_init();
|
||||
stream_s *stream = stream_init(dev, enc);
|
||||
server_s *server = server_init(stream);
|
||||
us_options_s *options = us_options_init(argc, argv);
|
||||
us_device_s *dev = us_device_init();
|
||||
us_encoder_s *enc = us_encoder_init();
|
||||
us_stream_s *stream = us_stream_init(dev, enc);
|
||||
us_server_s *server = us_server_init(stream);
|
||||
|
||||
if ((exit_code = options_parse(options, dev, enc, stream, server)) == 0) {
|
||||
# ifdef WITH_GPIO
|
||||
gpio_init();
|
||||
us_gpio_init();
|
||||
# endif
|
||||
|
||||
_install_signal_handlers();
|
||||
@@ -122,34 +122,34 @@ int main(int argc, char *argv[]) {
|
||||
ctx.server = server;
|
||||
_ctx = &ctx;
|
||||
|
||||
if ((exit_code = server_listen(server)) == 0) {
|
||||
if ((exit_code = us_server_listen(server)) == 0) {
|
||||
# ifdef WITH_GPIO
|
||||
gpio_set_prog_running(true);
|
||||
us_gpio_set_prog_running(true);
|
||||
# endif
|
||||
|
||||
pthread_t stream_loop_tid;
|
||||
pthread_t server_loop_tid;
|
||||
A_THREAD_CREATE(&stream_loop_tid, _stream_loop_thread, NULL);
|
||||
A_THREAD_CREATE(&server_loop_tid, _server_loop_thread, NULL);
|
||||
A_THREAD_JOIN(server_loop_tid);
|
||||
A_THREAD_JOIN(stream_loop_tid);
|
||||
US_THREAD_CREATE(&stream_loop_tid, _stream_loop_thread, NULL);
|
||||
US_THREAD_CREATE(&server_loop_tid, _server_loop_thread, NULL);
|
||||
US_THREAD_JOIN(server_loop_tid);
|
||||
US_THREAD_JOIN(stream_loop_tid);
|
||||
}
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
gpio_set_prog_running(false);
|
||||
gpio_destroy();
|
||||
us_gpio_set_prog_running(false);
|
||||
us_gpio_destroy();
|
||||
# endif
|
||||
}
|
||||
|
||||
server_destroy(server);
|
||||
stream_destroy(stream);
|
||||
encoder_destroy(enc);
|
||||
device_destroy(dev);
|
||||
options_destroy(options);
|
||||
us_server_destroy(server);
|
||||
us_stream_destroy(stream);
|
||||
us_encoder_destroy(enc);
|
||||
us_device_destroy(dev);
|
||||
us_options_destroy(options);
|
||||
|
||||
if (exit_code == 0) {
|
||||
LOG_INFO("Bye-bye");
|
||||
US_LOG_INFO("Bye-bye");
|
||||
}
|
||||
LOGGING_DESTROY;
|
||||
US_LOGGING_DESTROY;
|
||||
return (exit_code < 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "options.h"
|
||||
|
||||
|
||||
enum _OPT_VALUES {
|
||||
enum _US_OPT_VALUES {
|
||||
_O_DEVICE = 'd',
|
||||
_O_INPUT = 'i',
|
||||
_O_RESOLUTION = 'r',
|
||||
@@ -85,12 +85,12 @@ enum _OPT_VALUES {
|
||||
_O_TCP_NODELAY,
|
||||
_O_SERVER_TIMEOUT,
|
||||
|
||||
# define ADD_SINK(_prefix) \
|
||||
_O_##_prefix, \
|
||||
_O_##_prefix##_MODE, \
|
||||
_O_##_prefix##_RM, \
|
||||
_O_##_prefix##_CLIENT_TTL, \
|
||||
_O_##_prefix##_TIMEOUT,
|
||||
# define ADD_SINK(x_prefix) \
|
||||
_O_##x_prefix, \
|
||||
_O_##x_prefix##_MODE, \
|
||||
_O_##x_prefix##_RM, \
|
||||
_O_##x_prefix##_CLIENT_TTL, \
|
||||
_O_##x_prefix##_TIMEOUT,
|
||||
ADD_SINK(SINK)
|
||||
ADD_SINK(RAW_SINK)
|
||||
ADD_SINK(H264_SINK)
|
||||
@@ -181,12 +181,12 @@ static const struct option _LONG_OPTS[] = {
|
||||
{"tcp-nodelay", no_argument, NULL, _O_TCP_NODELAY},
|
||||
{"server-timeout", required_argument, NULL, _O_SERVER_TIMEOUT},
|
||||
|
||||
# define ADD_SINK(_opt, _prefix) \
|
||||
{_opt "sink", required_argument, NULL, _O_##_prefix}, \
|
||||
{_opt "sink-mode", required_argument, NULL, _O_##_prefix##_MODE}, \
|
||||
{_opt "sink-rm", no_argument, NULL, _O_##_prefix##_RM}, \
|
||||
{_opt "sink-client-ttl", required_argument, NULL, _O_##_prefix##_CLIENT_TTL}, \
|
||||
{_opt "sink-timeout", required_argument, NULL, _O_##_prefix##_TIMEOUT},
|
||||
# define ADD_SINK(x_opt, x_prefix) \
|
||||
{x_opt "sink", required_argument, NULL, _O_##x_prefix}, \
|
||||
{x_opt "sink-mode", required_argument, NULL, _O_##x_prefix##_MODE}, \
|
||||
{x_opt "sink-rm", no_argument, NULL, _O_##x_prefix##_RM}, \
|
||||
{x_opt "sink-client-ttl", required_argument, NULL, _O_##x_prefix##_CLIENT_TTL}, \
|
||||
{x_opt "sink-timeout", required_argument, NULL, _O_##x_prefix##_TIMEOUT},
|
||||
ADD_SINK("", SINK)
|
||||
ADD_SINK("raw-", RAW_SINK)
|
||||
ADD_SINK("h264-", H264_SINK)
|
||||
@@ -230,36 +230,28 @@ static const struct option _LONG_OPTS[] = {
|
||||
static int _parse_resolution(const char *str, unsigned *width, unsigned *height, bool limited);
|
||||
|
||||
static void _features(void);
|
||||
static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, server_s *server);
|
||||
static void _help(FILE *fp, us_device_s *dev, us_encoder_s *enc, us_stream_s *stream, us_server_s *server);
|
||||
|
||||
|
||||
options_s *options_init(unsigned argc, char *argv[]) {
|
||||
options_s *options;
|
||||
A_CALLOC(options, 1);
|
||||
us_options_s *us_options_init(unsigned argc, char *argv[]) {
|
||||
us_options_s *options;
|
||||
US_CALLOC(options, 1);
|
||||
options->argc = argc;
|
||||
options->argv = argv;
|
||||
|
||||
A_CALLOC(options->argv_copy, argc);
|
||||
US_CALLOC(options->argv_copy, argc);
|
||||
for (unsigned index = 0; index < argc; ++index) {
|
||||
assert(options->argv_copy[index] = strdup(argv[index]));
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
void options_destroy(options_s *options) {
|
||||
# define ADD_SINK(_prefix) { \
|
||||
if (options->_prefix) { \
|
||||
memsink_destroy(options->_prefix); \
|
||||
} \
|
||||
}
|
||||
ADD_SINK(sink);
|
||||
ADD_SINK(raw_sink);
|
||||
ADD_SINK(h264_sink);
|
||||
# undef ADD_SINK
|
||||
void us_options_destroy(us_options_s *options) {
|
||||
US_DELETE(options->sink, us_memsink_destroy);
|
||||
US_DELETE(options->raw_sink, us_memsink_destroy);
|
||||
US_DELETE(options->h264_sink, us_memsink_destroy);
|
||||
|
||||
if (options->blank) {
|
||||
frame_destroy(options->blank);
|
||||
}
|
||||
US_DELETE(options->blank, us_frame_destroy);
|
||||
|
||||
for (unsigned index = 0; index < options->argc; ++index) {
|
||||
free(options->argv_copy[index]);
|
||||
@@ -270,32 +262,32 @@ void options_destroy(options_s *options) {
|
||||
}
|
||||
|
||||
|
||||
int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *stream, server_s *server) {
|
||||
# define OPT_SET(_dest, _value) { \
|
||||
_dest = _value; \
|
||||
int options_parse(us_options_s *options, us_device_s *dev, us_encoder_s *enc, us_stream_s *stream, us_server_s *server) {
|
||||
# define OPT_SET(x_dest, x_value) { \
|
||||
x_dest = x_value; \
|
||||
break; \
|
||||
}
|
||||
|
||||
# define OPT_NUMBER(_name, _dest, _min, _max, _base) { \
|
||||
errno = 0; char *_end = NULL; long long _tmp = strtoll(optarg, &_end, _base); \
|
||||
if (errno || *_end || _tmp < _min || _tmp > _max) { \
|
||||
printf("Invalid value for '%s=%s': min=%lld, max=%lld\n", _name, optarg, (long long)_min, (long long)_max); \
|
||||
# define OPT_NUMBER(x_name, x_dest, x_min, x_max, x_base) { \
|
||||
errno = 0; char *m_end = NULL; long long m_tmp = strtoll(optarg, &m_end, x_base); \
|
||||
if (errno || *m_end || m_tmp < x_min || m_tmp > x_max) { \
|
||||
printf("Invalid value for '%s=%s': min=%lld, max=%lld\n", x_name, optarg, (long long)x_min, (long long)x_max); \
|
||||
return -1; \
|
||||
} \
|
||||
_dest = _tmp; \
|
||||
x_dest = m_tmp; \
|
||||
break; \
|
||||
}
|
||||
|
||||
# define OPT_RESOLUTION(_name, _dest_width, _dest_height, _limited) { \
|
||||
switch (_parse_resolution(optarg, &_dest_width, &_dest_height, _limited)) { \
|
||||
# define OPT_RESOLUTION(x_name, x_dest_width, x_dest_height, x_limited) { \
|
||||
switch (_parse_resolution(optarg, &x_dest_width, &x_dest_height, x_limited)) { \
|
||||
case -1: \
|
||||
printf("Invalid resolution format for '%s=%s'\n", _name, optarg); \
|
||||
printf("Invalid resolution format for '%s=%s'\n", x_name, optarg); \
|
||||
return -1; \
|
||||
case -2: \
|
||||
printf("Invalid width of '%s=%s': min=%u, max=%u\n", _name, optarg, VIDEO_MIN_WIDTH, VIDEO_MAX_WIDTH); \
|
||||
printf("Invalid width of '%s=%s': min=%u, max=%u\n", x_name, optarg, US_VIDEO_MIN_WIDTH, US_VIDEO_MAX_WIDTH); \
|
||||
return -1; \
|
||||
case -3: \
|
||||
printf("Invalid height of '%s=%s': min=%u, max=%u\n", _name, optarg, VIDEO_MIN_HEIGHT, VIDEO_MAX_HEIGHT); \
|
||||
printf("Invalid height of '%s=%s': min=%u, max=%u\n", x_name, optarg, US_VIDEO_MIN_HEIGHT, US_VIDEO_MAX_HEIGHT); \
|
||||
return -1; \
|
||||
case 0: break; \
|
||||
default: assert(0 && "Unknown error"); \
|
||||
@@ -303,48 +295,48 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
|
||||
break; \
|
||||
}
|
||||
|
||||
# define OPT_PARSE(_name, _dest, _func, _invalid, _available) { \
|
||||
if ((_dest = _func(optarg)) == _invalid) { \
|
||||
printf("Unknown " _name ": %s; available: %s\n", optarg, _available); \
|
||||
# define OPT_PARSE(x_name, x_dest, x_func, x_invalid, x_available) { \
|
||||
if ((x_dest = x_func(optarg)) == x_invalid) { \
|
||||
printf("Unknown " x_name ": %s; available: %s\n", optarg, x_available); \
|
||||
return -1; \
|
||||
} \
|
||||
break; \
|
||||
}
|
||||
|
||||
# define OPT_CTL_DEFAULT_NOBREAK(_dest) { \
|
||||
dev->ctl._dest.mode = CTL_MODE_DEFAULT; \
|
||||
# define OPT_CTL_DEFAULT_NOBREAK(x_dest) { \
|
||||
dev->ctl.x_dest.mode = CTL_MODE_DEFAULT; \
|
||||
}
|
||||
|
||||
# define OPT_CTL_MANUAL(_dest) { \
|
||||
# define OPT_CTL_MANUAL(x_dest) { \
|
||||
if (!strcasecmp(optarg, "default")) { \
|
||||
OPT_CTL_DEFAULT_NOBREAK(_dest); \
|
||||
OPT_CTL_DEFAULT_NOBREAK(x_dest); \
|
||||
} else { \
|
||||
dev->ctl._dest.mode = CTL_MODE_VALUE; \
|
||||
OPT_NUMBER("--"#_dest, dev->ctl._dest.value, INT_MIN, INT_MAX, 0); \
|
||||
dev->ctl.x_dest.mode = CTL_MODE_VALUE; \
|
||||
OPT_NUMBER("--"#x_dest, dev->ctl.x_dest.value, INT_MIN, INT_MAX, 0); \
|
||||
} \
|
||||
break; \
|
||||
}
|
||||
|
||||
# define OPT_CTL_AUTO(_dest) { \
|
||||
# define OPT_CTL_AUTO(x_dest) { \
|
||||
if (!strcasecmp(optarg, "default")) { \
|
||||
OPT_CTL_DEFAULT_NOBREAK(_dest); \
|
||||
OPT_CTL_DEFAULT_NOBREAK(x_dest); \
|
||||
} else if (!strcasecmp(optarg, "auto")) { \
|
||||
dev->ctl._dest.mode = CTL_MODE_AUTO; \
|
||||
dev->ctl.x_dest.mode = CTL_MODE_AUTO; \
|
||||
} else { \
|
||||
dev->ctl._dest.mode = CTL_MODE_VALUE; \
|
||||
OPT_NUMBER("--"#_dest, dev->ctl._dest.value, INT_MIN, INT_MAX, 0); \
|
||||
dev->ctl.x_dest.mode = CTL_MODE_VALUE; \
|
||||
OPT_NUMBER("--"#x_dest, dev->ctl.x_dest.value, INT_MIN, INT_MAX, 0); \
|
||||
} \
|
||||
break; \
|
||||
}
|
||||
|
||||
char *blank_path = NULL;
|
||||
|
||||
# define ADD_SINK(_prefix) \
|
||||
char *_prefix##_name = NULL; \
|
||||
mode_t _prefix##_mode = 0660; \
|
||||
bool _prefix##_rm = false; \
|
||||
unsigned _prefix##_client_ttl = 10; \
|
||||
unsigned _prefix##_timeout = 1;
|
||||
# define ADD_SINK(x_prefix) \
|
||||
char *x_prefix##_name = NULL; \
|
||||
mode_t x_prefix##_mode = 0660; \
|
||||
bool x_prefix##_rm = false; \
|
||||
unsigned x_prefix##_client_ttl = 10; \
|
||||
unsigned x_prefix##_timeout = 1;
|
||||
ADD_SINK(sink);
|
||||
ADD_SINK(raw_sink);
|
||||
ADD_SINK(h264_sink);
|
||||
@@ -355,27 +347,27 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
|
||||
# endif
|
||||
|
||||
char short_opts[128];
|
||||
build_short_options(_LONG_OPTS, short_opts, 128);
|
||||
us_build_short_options(_LONG_OPTS, short_opts, 128);
|
||||
|
||||
for (int ch; (ch = getopt_long(options->argc, options->argv_copy, short_opts, _LONG_OPTS, NULL)) >= 0;) {
|
||||
switch (ch) {
|
||||
case _O_DEVICE: OPT_SET(dev->path, optarg);
|
||||
case _O_INPUT: OPT_NUMBER("--input", dev->input, 0, 128, 0);
|
||||
case _O_RESOLUTION: OPT_RESOLUTION("--resolution", dev->width, dev->height, true);
|
||||
case _O_DEVICE: OPT_SET(dev->path, optarg);
|
||||
case _O_INPUT: OPT_NUMBER("--input", dev->input, 0, 128, 0);
|
||||
case _O_RESOLUTION: OPT_RESOLUTION("--resolution", dev->width, dev->height, true);
|
||||
# pragma GCC diagnostic ignored "-Wsign-compare"
|
||||
# pragma GCC diagnostic push
|
||||
case _O_FORMAT: OPT_PARSE("pixel format", dev->format, device_parse_format, FORMAT_UNKNOWN, FORMATS_STR);
|
||||
case _O_FORMAT: OPT_PARSE("pixel format", dev->format, us_device_parse_format, US_FORMAT_UNKNOWN, US_FORMATS_STR);
|
||||
# pragma GCC diagnostic pop
|
||||
case _O_TV_STANDARD: OPT_PARSE("TV standard", dev->standard, device_parse_standard, STANDARD_UNKNOWN, STANDARDS_STR);
|
||||
case _O_IO_METHOD: OPT_PARSE("IO method", dev->io_method, device_parse_io_method, IO_METHOD_UNKNOWN, IO_METHODS_STR);
|
||||
case _O_DESIRED_FPS: OPT_NUMBER("--desired-fps", dev->desired_fps, 0, VIDEO_MAX_FPS, 0);
|
||||
case _O_MIN_FRAME_SIZE: OPT_NUMBER("--min-frame-size", dev->min_frame_size, 1, 8192, 0);
|
||||
case _O_PERSISTENT: OPT_SET(dev->persistent, true);
|
||||
case _O_DV_TIMINGS: OPT_SET(dev->dv_timings, true);
|
||||
case _O_BUFFERS: OPT_NUMBER("--buffers", dev->n_bufs, 1, 32, 0);
|
||||
case _O_WORKERS: OPT_NUMBER("--workers", enc->n_workers, 1, 32, 0);
|
||||
case _O_QUALITY: OPT_NUMBER("--quality", dev->jpeg_quality, 1, 100, 0);
|
||||
case _O_ENCODER: OPT_PARSE("encoder type", enc->type, encoder_parse_type, ENCODER_TYPE_UNKNOWN, ENCODER_TYPES_STR);
|
||||
case _O_TV_STANDARD: OPT_PARSE("TV standard", dev->standard, us_device_parse_standard, US_STANDARD_UNKNOWN, US_STANDARDS_STR);
|
||||
case _O_IO_METHOD: OPT_PARSE("IO method", dev->io_method, us_device_parse_io_method, US_IO_METHOD_UNKNOWN, US_IO_METHODS_STR);
|
||||
case _O_DESIRED_FPS: OPT_NUMBER("--desired-fps", dev->desired_fps, 0, US_VIDEO_MAX_FPS, 0);
|
||||
case _O_MIN_FRAME_SIZE: OPT_NUMBER("--min-frame-size", dev->min_frame_size, 1, 8192, 0);
|
||||
case _O_PERSISTENT: OPT_SET(dev->persistent, true);
|
||||
case _O_DV_TIMINGS: OPT_SET(dev->dv_timings, true);
|
||||
case _O_BUFFERS: OPT_NUMBER("--buffers", dev->n_bufs, 1, 32, 0);
|
||||
case _O_WORKERS: OPT_NUMBER("--workers", enc->n_workers, 1, 32, 0);
|
||||
case _O_QUALITY: OPT_NUMBER("--quality", dev->jpeg_quality, 1, 100, 0);
|
||||
case _O_ENCODER: OPT_PARSE("encoder type", enc->type, us_encoder_parse_type, US_ENCODER_TYPE_UNKNOWN, ENCODER_TYPES_STR);
|
||||
case _O_GLITCHED_RESOLUTIONS: break; // Deprecated
|
||||
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);
|
||||
@@ -409,7 +401,7 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
|
||||
case _O_WHITE_BALANCE: OPT_CTL_AUTO(white_balance);
|
||||
case _O_GAIN: OPT_CTL_AUTO(gain);
|
||||
case _O_COLOR_EFFECT: OPT_CTL_MANUAL(color_effect);
|
||||
case _O_ROTATE: OPT_CTL_MANUAL(rotate);
|
||||
case _O_ROTATE: OPT_CTL_MANUAL(rotate);
|
||||
case _O_FLIP_VERTICAL: OPT_CTL_MANUAL(flip_vertical);
|
||||
case _O_FLIP_HORIZONTAL: OPT_CTL_MANUAL(flip_horizontal);
|
||||
|
||||
@@ -424,24 +416,24 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
|
||||
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_DROP_SAME_FRAMES: OPT_NUMBER("--drop-same-frames", server->drop_same_frames, 0, VIDEO_MAX_FPS, 0);
|
||||
case _O_DROP_SAME_FRAMES: OPT_NUMBER("--drop-same-frames", server->drop_same_frames, 0, US_VIDEO_MAX_FPS, 0);
|
||||
case _O_FAKE_RESOLUTION: OPT_RESOLUTION("--fake-resolution", server->fake_width, server->fake_height, false);
|
||||
case _O_ALLOW_ORIGIN: OPT_SET(server->allow_origin, optarg);
|
||||
case _O_TCP_NODELAY: OPT_SET(server->tcp_nodelay, true);
|
||||
case _O_SERVER_TIMEOUT: OPT_NUMBER("--server-timeout", server->timeout, 1, 60, 0);
|
||||
|
||||
# define ADD_SINK(_opt, _lp, _up) \
|
||||
case _O_##_up: OPT_SET(_lp##_name, optarg); \
|
||||
case _O_##_up##_MODE: OPT_NUMBER("--" #_opt "sink-mode", _lp##_mode, INT_MIN, INT_MAX, 8); \
|
||||
case _O_##_up##_RM: OPT_SET(_lp##_rm, true); \
|
||||
case _O_##_up##_CLIENT_TTL: OPT_NUMBER("--" #_opt "sink-client-ttl", _lp##_client_ttl, 1, 60, 0); \
|
||||
case _O_##_up##_TIMEOUT: OPT_NUMBER("--" #_opt "sink-timeout", _lp##_timeout, 1, 60, 0);
|
||||
# define ADD_SINK(x_opt, x_lp, x_up) \
|
||||
case _O_##x_up: OPT_SET(x_lp##_name, optarg); \
|
||||
case _O_##x_up##_MODE: OPT_NUMBER("--" #x_opt "sink-mode", x_lp##_mode, INT_MIN, INT_MAX, 8); \
|
||||
case _O_##x_up##_RM: OPT_SET(x_lp##_rm, true); \
|
||||
case _O_##x_up##_CLIENT_TTL: OPT_NUMBER("--" #x_opt "sink-client-ttl", x_lp##_client_ttl, 1, 60, 0); \
|
||||
case _O_##x_up##_TIMEOUT: OPT_NUMBER("--" #x_opt "sink-timeout", x_lp##_timeout, 1, 60, 0);
|
||||
ADD_SINK("", sink, SINK)
|
||||
ADD_SINK("raw-", raw_sink, RAW_SINK)
|
||||
ADD_SINK("h264-", h264_sink, H264_SINK)
|
||||
case _O_H264_BITRATE: OPT_NUMBER("--h264-bitrate", stream->h264_bitrate, 25, 20000, 0);
|
||||
case _O_H264_GOP: OPT_NUMBER("--h264-gop", stream->h264_gop, 0, 60, 0);
|
||||
case _O_H264_M2M_DEVICE: OPT_SET(stream->h264_m2m_path, optarg);
|
||||
case _O_H264_BITRATE: OPT_NUMBER("--h264-bitrate", stream->h264_bitrate, 25, 20000, 0);
|
||||
case _O_H264_GOP: OPT_NUMBER("--h264-gop", stream->h264_gop, 0, 60, 0);
|
||||
case _O_H264_M2M_DEVICE: OPT_SET(stream->h264_m2m_path, optarg);
|
||||
# undef ADD_SINK
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
@@ -454,7 +446,7 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
|
||||
|
||||
# ifdef HAS_PDEATHSIG
|
||||
case _O_EXIT_ON_PARENT_DEATH:
|
||||
if (process_track_parent_death() < 0) {
|
||||
if (us_process_track_parent_death() < 0) {
|
||||
return -1;
|
||||
};
|
||||
break;
|
||||
@@ -465,15 +457,15 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
|
||||
# endif
|
||||
case _O_NOTIFY_PARENT: OPT_SET(server->notify_parent, true);
|
||||
|
||||
case _O_LOG_LEVEL: OPT_NUMBER("--log-level", us_log_level, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, 0);
|
||||
case _O_PERF: OPT_SET(us_log_level, LOG_LEVEL_PERF);
|
||||
case _O_VERBOSE: OPT_SET(us_log_level, LOG_LEVEL_VERBOSE);
|
||||
case _O_DEBUG: OPT_SET(us_log_level, LOG_LEVEL_DEBUG);
|
||||
case _O_LOG_LEVEL: OPT_NUMBER("--log-level", us_log_level, US_LOG_LEVEL_INFO, US_LOG_LEVEL_DEBUG, 0);
|
||||
case _O_PERF: OPT_SET(us_log_level, US_LOG_LEVEL_PERF);
|
||||
case _O_VERBOSE: OPT_SET(us_log_level, US_LOG_LEVEL_VERBOSE);
|
||||
case _O_DEBUG: OPT_SET(us_log_level, US_LOG_LEVEL_DEBUG);
|
||||
case _O_FORCE_LOG_COLORS: OPT_SET(us_log_colored, true);
|
||||
case _O_NO_LOG_COLORS: OPT_SET(us_log_colored, false);
|
||||
|
||||
case _O_HELP: _help(stdout, dev, enc, stream, server); return 1;
|
||||
case _O_VERSION: puts(VERSION); return 1;
|
||||
case _O_VERSION: puts(US_VERSION); return 1;
|
||||
case _O_FEATURES: _features(); return 1;
|
||||
|
||||
case 0: break;
|
||||
@@ -481,22 +473,22 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
|
||||
}
|
||||
}
|
||||
|
||||
options->blank = blank_frame_init(blank_path);
|
||||
options->blank = us_blank_frame_init(blank_path);
|
||||
stream->blank = options->blank;
|
||||
|
||||
# define ADD_SINK(_label, _prefix) { \
|
||||
if (_prefix##_name && _prefix##_name[0] != '\0') { \
|
||||
options->_prefix = memsink_init( \
|
||||
_label, \
|
||||
_prefix##_name, \
|
||||
# define ADD_SINK(x_label, x_prefix) { \
|
||||
if (x_prefix##_name && x_prefix##_name[0] != '\0') { \
|
||||
options->x_prefix = us_memsink_init( \
|
||||
x_label, \
|
||||
x_prefix##_name, \
|
||||
true, \
|
||||
_prefix##_mode, \
|
||||
_prefix##_rm, \
|
||||
_prefix##_client_ttl, \
|
||||
_prefix##_timeout \
|
||||
x_prefix##_mode, \
|
||||
x_prefix##_rm, \
|
||||
x_prefix##_client_ttl, \
|
||||
x_prefix##_timeout \
|
||||
); \
|
||||
} \
|
||||
stream->_prefix = options->_prefix; \
|
||||
stream->x_prefix = options->x_prefix; \
|
||||
}
|
||||
ADD_SINK("JPEG", sink);
|
||||
ADD_SINK("RAW", raw_sink);
|
||||
@@ -505,7 +497,7 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
|
||||
|
||||
# ifdef WITH_SETPROCTITLE
|
||||
if (process_name_prefix != NULL) {
|
||||
process_set_name_prefix(options->argc, options->argv, process_name_prefix);
|
||||
us_process_set_name_prefix(options->argc, options->argv, process_name_prefix);
|
||||
}
|
||||
# endif
|
||||
|
||||
@@ -526,10 +518,10 @@ static int _parse_resolution(const char *str, unsigned *width, unsigned *height,
|
||||
return -1;
|
||||
}
|
||||
if (limited) {
|
||||
if (tmp_width < VIDEO_MIN_WIDTH || tmp_width > VIDEO_MAX_WIDTH) {
|
||||
if (tmp_width < US_VIDEO_MIN_WIDTH || tmp_width > US_VIDEO_MAX_WIDTH) {
|
||||
return -2;
|
||||
}
|
||||
if (tmp_height < VIDEO_MIN_HEIGHT || tmp_height > VIDEO_MAX_HEIGHT) {
|
||||
if (tmp_height < US_VIDEO_MIN_HEIGHT || tmp_height > US_VIDEO_MAX_HEIGHT) {
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
@@ -570,11 +562,11 @@ static void _features(void) {
|
||||
# endif
|
||||
}
|
||||
|
||||
static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, server_s *server) {
|
||||
# define SAY(_msg, ...) fprintf(fp, _msg "\n", ##__VA_ARGS__)
|
||||
static void _help(FILE *fp, us_device_s *dev, us_encoder_s *enc, us_stream_s *stream, us_server_s *server) {
|
||||
# define SAY(x_msg, ...) fprintf(fp, x_msg "\n", ##__VA_ARGS__)
|
||||
SAY("\nuStreamer - Lightweight and fast MJPEG-HTTP streamer");
|
||||
SAY("═══════════════════════════════════════════════════");
|
||||
SAY("Version: %s; license: GPLv3", VERSION);
|
||||
SAY("Version: %s; license: GPLv3", US_VERSION);
|
||||
SAY("Copyright (C) 2018-2022 Maxim Devaev <mdevaev@gmail.com>\n");
|
||||
SAY("Capturing options:");
|
||||
SAY("══════════════════");
|
||||
@@ -582,12 +574,12 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser
|
||||
SAY(" -i|--input <N> ────────────────────── Input channel. Default: %u.\n", dev->input);
|
||||
SAY(" -r|--resolution <WxH> ─────────────── Initial image resolution. Default: %ux%u.\n", dev->width, dev->height);
|
||||
SAY(" -m|--format <fmt> ─────────────────── Image format.");
|
||||
SAY(" Available: %s; default: YUYV.\n", FORMATS_STR);
|
||||
SAY(" Available: %s; default: YUYV.\n", US_FORMATS_STR);
|
||||
SAY(" -a|--tv-standard <std> ────────────── Force TV standard.");
|
||||
SAY(" Available: %s; default: disabled.\n", STANDARDS_STR);
|
||||
SAY(" Available: %s; default: disabled.\n", US_STANDARDS_STR);
|
||||
SAY(" -I|--io-method <method> ───────────── Set V4L2 IO method (see kernel documentation).");
|
||||
SAY(" Changing of this parameter may increase the performance. Or not.");
|
||||
SAY(" Available: %s; default: MMAP.\n", IO_METHODS_STR);
|
||||
SAY(" Available: %s; default: MMAP.\n", US_IO_METHODS_STR);
|
||||
SAY(" -f|--desired-fps <N> ──────────────── Desired FPS. Default: maximum possible.\n");
|
||||
SAY(" -z|--min-frame-size <N> ───────────── Drop frames smaller then this limit. Useful if the device");
|
||||
SAY(" produces small-sized garbage frames. Default: %zu bytes.\n", dev->min_frame_size);
|
||||
@@ -666,14 +658,14 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser
|
||||
SAY(" Default: disabled.\n");
|
||||
SAY(" --allow-origin <str> ─────── Set Access-Control-Allow-Origin header. Default: disabled.\n");
|
||||
SAY(" --server-timeout <sec> ───── Timeout for client connections. Default: %u.\n", server->timeout);
|
||||
# define ADD_SINK(_name, _opt) \
|
||||
SAY(_name " sink options:"); \
|
||||
# define ADD_SINK(x_name, x_opt) \
|
||||
SAY(x_name " sink options:"); \
|
||||
SAY("══════════════════"); \
|
||||
SAY(" --" _opt "sink <name> ──────────── Use the shared memory to sink " _name " frames. Default: disabled.\n"); \
|
||||
SAY(" --" _opt "sink-mode <mode> ─────── Set " _name " sink permissions (like 777). Default: 660.\n"); \
|
||||
SAY(" --" _opt "sink-rm ──────────────── Remove shared memory on stop. Default: disabled.\n"); \
|
||||
SAY(" --" _opt "sink-client-ttl <sec> ── Client TTL. Default: 10.\n"); \
|
||||
SAY(" --" _opt "sink-timeout <sec> ───── Timeout for lock. Default: 1.\n");
|
||||
SAY(" --" x_opt "sink <name> ──────────── Use the shared memory to sink " x_name " frames. Default: disabled.\n"); \
|
||||
SAY(" --" x_opt "sink-mode <mode> ─────── Set " x_name " sink permissions (like 777). Default: 660.\n"); \
|
||||
SAY(" --" x_opt "sink-rm ──────────────── Remove shared memory on stop. Default: disabled.\n"); \
|
||||
SAY(" --" x_opt "sink-client-ttl <sec> ── Client TTL. Default: 10.\n"); \
|
||||
SAY(" --" x_opt "sink-timeout <sec> ───── Timeout for lock. Default: 1.\n");
|
||||
ADD_SINK("JPEG", "")
|
||||
ADD_SINK("RAW", "raw-")
|
||||
ADD_SINK("H264", "h264-")
|
||||
|
||||
@@ -50,17 +50,17 @@
|
||||
|
||||
|
||||
typedef struct {
|
||||
unsigned argc;
|
||||
char **argv;
|
||||
char **argv_copy;
|
||||
frame_s *blank;
|
||||
memsink_s *sink;
|
||||
memsink_s *raw_sink;
|
||||
memsink_s *h264_sink;
|
||||
} options_s;
|
||||
unsigned argc;
|
||||
char **argv;
|
||||
char **argv_copy;
|
||||
us_frame_s *blank;
|
||||
us_memsink_s *sink;
|
||||
us_memsink_s *raw_sink;
|
||||
us_memsink_s *h264_sink;
|
||||
} us_options_s;
|
||||
|
||||
|
||||
options_s *options_init(unsigned argc, char *argv[]);
|
||||
void options_destroy(options_s *options);
|
||||
us_options_s *us_options_init(unsigned argc, char *argv[]);
|
||||
void us_options_destroy(us_options_s *options);
|
||||
|
||||
int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *stream, server_s *server);
|
||||
int options_parse(us_options_s *options, us_device_s *dev, us_encoder_s *enc, us_stream_s *stream, us_server_s *server);
|
||||
|
||||
@@ -23,41 +23,41 @@
|
||||
#include "stream.h"
|
||||
|
||||
|
||||
static workers_pool_s *_stream_init_loop(stream_s *stream);
|
||||
static workers_pool_s *_stream_init_one(stream_s *stream);
|
||||
static void _stream_expose_frame(stream_s *stream, frame_s *frame, unsigned captured_fps);
|
||||
static us_workers_pool_s *_stream_init_loop(us_stream_s *stream);
|
||||
static us_workers_pool_s *_stream_init_one(us_stream_s *stream);
|
||||
static void _stream_expose_frame(us_stream_s *stream, us_frame_s *frame, unsigned captured_fps);
|
||||
|
||||
|
||||
#define RUN(_next) stream->run->_next
|
||||
#define _RUN(x_next) stream->run->x_next
|
||||
|
||||
#define SINK_PUT(_sink, _frame) { \
|
||||
if (stream->_sink && memsink_server_check(stream->_sink, _frame)) {\
|
||||
memsink_server_put(stream->_sink, _frame); \
|
||||
#define _SINK_PUT(x_sink, x_frame) { \
|
||||
if (stream->x_sink && us_memsink_server_check(stream->x_sink, x_frame)) {\
|
||||
us_memsink_server_put(stream->x_sink, x_frame); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define H264_PUT(_frame, _force_key) { \
|
||||
if (RUN(h264)) { \
|
||||
h264_stream_process(RUN(h264), _frame, _force_key); \
|
||||
#define _H264_PUT(x_frame, x_force_key) { \
|
||||
if (_RUN(h264)) { \
|
||||
us_h264_stream_process(_RUN(h264), x_frame, x_force_key); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
stream_s *stream_init(device_s *dev, encoder_s *enc) {
|
||||
stream_runtime_s *run;
|
||||
A_CALLOC(run, 1);
|
||||
us_stream_s *us_stream_init(us_device_s *dev, us_encoder_s *enc) {
|
||||
us_stream_runtime_s *run;
|
||||
US_CALLOC(run, 1);
|
||||
atomic_init(&run->stop, false);
|
||||
|
||||
video_s *video;
|
||||
A_CALLOC(video, 1);
|
||||
video->frame = frame_init();
|
||||
us_video_s *video;
|
||||
US_CALLOC(video, 1);
|
||||
video->frame = us_frame_init();
|
||||
atomic_init(&video->updated, false);
|
||||
A_MUTEX_INIT(&video->mutex);
|
||||
US_MUTEX_INIT(&video->mutex);
|
||||
atomic_init(&video->has_clients, false);
|
||||
run->video = video;
|
||||
|
||||
stream_s *stream;
|
||||
A_CALLOC(stream, 1);
|
||||
us_stream_s *stream;
|
||||
US_CALLOC(stream, 1);
|
||||
stream->dev = dev;
|
||||
stream->enc = enc;
|
||||
stream->last_as_blank = -1;
|
||||
@@ -68,42 +68,42 @@ stream_s *stream_init(device_s *dev, encoder_s *enc) {
|
||||
return stream;
|
||||
}
|
||||
|
||||
void stream_destroy(stream_s *stream) {
|
||||
A_MUTEX_DESTROY(&RUN(video->mutex));
|
||||
frame_destroy(RUN(video->frame));
|
||||
free(RUN(video));
|
||||
void us_stream_destroy(us_stream_s *stream) {
|
||||
US_MUTEX_DESTROY(&_RUN(video->mutex));
|
||||
us_frame_destroy(_RUN(video->frame));
|
||||
free(_RUN(video));
|
||||
free(stream->run);
|
||||
free(stream);
|
||||
}
|
||||
|
||||
void stream_loop(stream_s *stream) {
|
||||
void us_stream_loop(us_stream_s *stream) {
|
||||
assert(stream->blank);
|
||||
|
||||
LOG_INFO("Using V4L2 device: %s", stream->dev->path);
|
||||
LOG_INFO("Using desired FPS: %u", stream->dev->desired_fps);
|
||||
US_LOG_INFO("Using V4L2 device: %s", stream->dev->path);
|
||||
US_LOG_INFO("Using desired FPS: %u", stream->dev->desired_fps);
|
||||
|
||||
if (stream->h264_sink) {
|
||||
RUN(h264) = h264_stream_init(stream->h264_sink, stream->h264_m2m_path, stream->h264_bitrate, stream->h264_gop);
|
||||
_RUN(h264) = us_h264_stream_init(stream->h264_sink, stream->h264_m2m_path, stream->h264_bitrate, stream->h264_gop);
|
||||
}
|
||||
|
||||
for (workers_pool_s *pool; (pool = _stream_init_loop(stream)) != NULL;) {
|
||||
for (us_workers_pool_s *pool; (pool = _stream_init_loop(stream)) != NULL;) {
|
||||
long double grab_after = 0;
|
||||
unsigned fluency_passed = 0;
|
||||
unsigned captured_fps = 0;
|
||||
unsigned captured_fps_accum = 0;
|
||||
long long captured_fps_second = 0;
|
||||
|
||||
LOG_INFO("Capturing ...");
|
||||
US_LOG_INFO("Capturing ...");
|
||||
|
||||
while (!atomic_load(&RUN(stop))) {
|
||||
SEP_DEBUG('-');
|
||||
LOG_DEBUG("Waiting for worker ...");
|
||||
while (!atomic_load(&_RUN(stop))) {
|
||||
US_SEP_DEBUG('-');
|
||||
US_LOG_DEBUG("Waiting for worker ...");
|
||||
|
||||
worker_s *ready_wr = workers_pool_wait(pool);
|
||||
encoder_job_s *ready_job = (encoder_job_s *)(ready_wr->job);
|
||||
us_worker_s *ready_wr = us_workers_pool_wait(pool);
|
||||
us_encoder_job_s *ready_job = (us_encoder_job_s *)(ready_wr->job);
|
||||
|
||||
if (ready_job->hw) {
|
||||
if (device_release_buffer(stream->dev, ready_job->hw) < 0) {
|
||||
if (us_device_release_buffer(stream->dev, ready_job->hw) < 0) {
|
||||
ready_wr->job_failed = true;
|
||||
}
|
||||
ready_job->hw = NULL;
|
||||
@@ -111,9 +111,9 @@ void stream_loop(stream_s *stream) {
|
||||
if (!ready_wr->job_failed) {
|
||||
if (ready_wr->job_timely) {
|
||||
_stream_expose_frame(stream, ready_job->dest, captured_fps);
|
||||
LOG_PERF("##### Encoded frame exposed; worker=%s", ready_wr->name);
|
||||
US_LOG_PERF("##### Encoded frame exposed; worker=%s", ready_wr->name);
|
||||
} else {
|
||||
LOG_PERF("----- Encoded frame dropped; worker=%s", ready_wr->name);
|
||||
US_LOG_PERF("----- Encoded frame dropped; worker=%s", ready_wr->name);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
@@ -123,51 +123,51 @@ void stream_loop(stream_s *stream) {
|
||||
bool h264_force_key = false;
|
||||
if (stream->slowdown) {
|
||||
unsigned slc = 0;
|
||||
for (; slc < 10 && !atomic_load(&RUN(stop)) && !stream_has_clients(stream); ++slc) {
|
||||
for (; slc < 10 && !atomic_load(&_RUN(stop)) && !us_stream_has_clients(stream); ++slc) {
|
||||
usleep(100000);
|
||||
++slc;
|
||||
}
|
||||
h264_force_key = (slc == 10);
|
||||
}
|
||||
|
||||
if (atomic_load(&RUN(stop))) {
|
||||
if (atomic_load(&_RUN(stop))) {
|
||||
break;
|
||||
}
|
||||
|
||||
bool has_read;
|
||||
bool has_write;
|
||||
bool has_error;
|
||||
int selected = device_select(stream->dev, &has_read, &has_write, &has_error);
|
||||
int selected = us_device_select(stream->dev, &has_read, &has_write, &has_error);
|
||||
|
||||
if (selected < 0) {
|
||||
if (errno != EINTR) {
|
||||
LOG_PERROR("Mainloop select() error");
|
||||
US_LOG_PERROR("Mainloop select() error");
|
||||
break;
|
||||
}
|
||||
} else if (selected == 0) { // Persistent timeout
|
||||
# ifdef WITH_GPIO
|
||||
gpio_set_stream_online(false);
|
||||
us_gpio_set_stream_online(false);
|
||||
# endif
|
||||
} else {
|
||||
if (has_read) {
|
||||
LOG_DEBUG("Frame is ready");
|
||||
US_LOG_DEBUG("Frame is ready");
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
gpio_set_stream_online(true);
|
||||
us_gpio_set_stream_online(true);
|
||||
# endif
|
||||
|
||||
const long double now = get_now_monotonic();
|
||||
const long long now_second = floor_ms(now);
|
||||
const long double now = us_get_now_monotonic();
|
||||
const long long now_second = us_floor_ms(now);
|
||||
|
||||
hw_buffer_s *hw;
|
||||
int buf_index = device_grab_buffer(stream->dev, &hw);
|
||||
us_hw_buffer_s *hw;
|
||||
int buf_index = us_device_grab_buffer(stream->dev, &hw);
|
||||
|
||||
if (buf_index >= 0) {
|
||||
if (now < grab_after) {
|
||||
fluency_passed += 1;
|
||||
LOG_VERBOSE("Passed %u frames for fluency: now=%.03Lf, grab_after=%.03Lf",
|
||||
US_LOG_VERBOSE("Passed %u frames for fluency: now=%.03Lf, grab_after=%.03Lf",
|
||||
fluency_passed, now, grab_after);
|
||||
if (device_release_buffer(stream->dev, hw) < 0) {
|
||||
if (us_device_release_buffer(stream->dev, hw) < 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -177,20 +177,20 @@ void stream_loop(stream_s *stream) {
|
||||
captured_fps = captured_fps_accum;
|
||||
captured_fps_accum = 0;
|
||||
captured_fps_second = now_second;
|
||||
LOG_PERF_FPS("A new second has come; captured_fps=%u", captured_fps);
|
||||
US_LOG_PERF_FPS("A new second has come; captured_fps=%u", captured_fps);
|
||||
}
|
||||
captured_fps_accum += 1;
|
||||
|
||||
const long double fluency_delay = workers_pool_get_fluency_delay(pool, ready_wr);
|
||||
const long double fluency_delay = us_workers_pool_get_fluency_delay(pool, ready_wr);
|
||||
grab_after = now + fluency_delay;
|
||||
LOG_VERBOSE("Fluency: delay=%.03Lf, grab_after=%.03Lf", fluency_delay, grab_after);
|
||||
US_LOG_VERBOSE("Fluency: delay=%.03Lf, grab_after=%.03Lf", fluency_delay, grab_after);
|
||||
|
||||
ready_job->hw = hw;
|
||||
workers_pool_assign(pool, ready_wr);
|
||||
LOG_DEBUG("Assigned new frame in buffer=%d to worker=%s", buf_index, ready_wr->name);
|
||||
us_workers_pool_assign(pool, ready_wr);
|
||||
US_LOG_DEBUG("Assigned new frame in buffer=%d to worker=%s", buf_index, ready_wr->name);
|
||||
|
||||
SINK_PUT(raw_sink, &hw->raw);
|
||||
H264_PUT(&hw->raw, h264_force_key);
|
||||
_SINK_PUT(raw_sink, &hw->raw);
|
||||
_H264_PUT(&hw->raw, h264_force_key);
|
||||
}
|
||||
} else if (buf_index != -2) { // -2 for broken frame
|
||||
break;
|
||||
@@ -198,72 +198,72 @@ void stream_loop(stream_s *stream) {
|
||||
}
|
||||
|
||||
if (has_write) {
|
||||
LOG_ERROR("Got unexpected writing event, seems device was disconnected");
|
||||
US_LOG_ERROR("Got unexpected writing event, seems device was disconnected");
|
||||
break;
|
||||
}
|
||||
|
||||
if (has_error) {
|
||||
LOG_INFO("Got V4L2 event");
|
||||
if (device_consume_event(stream->dev) < 0) {
|
||||
US_LOG_INFO("Got V4L2 event");
|
||||
if (us_device_consume_event(stream->dev) < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
workers_pool_destroy(pool);
|
||||
device_switch_capturing(stream->dev, false);
|
||||
device_close(stream->dev);
|
||||
us_workers_pool_destroy(pool);
|
||||
us_device_switch_capturing(stream->dev, false);
|
||||
us_device_close(stream->dev);
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
gpio_set_stream_online(false);
|
||||
us_gpio_set_stream_online(false);
|
||||
# endif
|
||||
}
|
||||
|
||||
if (RUN(h264)) {
|
||||
h264_stream_destroy(RUN(h264));
|
||||
if (_RUN(h264)) {
|
||||
us_h264_stream_destroy(_RUN(h264));
|
||||
}
|
||||
}
|
||||
|
||||
void stream_loop_break(stream_s *stream) {
|
||||
atomic_store(&RUN(stop), true);
|
||||
void us_stream_loop_break(us_stream_s *stream) {
|
||||
atomic_store(&_RUN(stop), true);
|
||||
}
|
||||
|
||||
bool stream_has_clients(stream_s *stream) {
|
||||
bool us_stream_has_clients(us_stream_s *stream) {
|
||||
return (
|
||||
atomic_load(&RUN(video->has_clients))
|
||||
atomic_load(&_RUN(video->has_clients))
|
||||
// has_clients синков НЕ обновляются в реальном времени
|
||||
|| (stream->sink != NULL && atomic_load(&stream->sink->has_clients))
|
||||
|| (RUN(h264) != NULL && /*RUN(h264->sink) == NULL ||*/ atomic_load(&RUN(h264->sink->has_clients)))
|
||||
|| (_RUN(h264) != NULL && /*_RUN(h264->sink) == NULL ||*/ atomic_load(&_RUN(h264->sink->has_clients)))
|
||||
);
|
||||
}
|
||||
|
||||
static workers_pool_s *_stream_init_loop(stream_s *stream) {
|
||||
static us_workers_pool_s *_stream_init_loop(us_stream_s *stream) {
|
||||
|
||||
workers_pool_s *pool = NULL;
|
||||
us_workers_pool_s *pool = NULL;
|
||||
int access_error = 0;
|
||||
|
||||
LOG_DEBUG("%s: stream->run->stop=%d", __FUNCTION__, atomic_load(&RUN(stop)));
|
||||
US_LOG_DEBUG("%s: stream->run->stop=%d", __FUNCTION__, atomic_load(&_RUN(stop)));
|
||||
|
||||
while (!atomic_load(&RUN(stop))) {
|
||||
while (!atomic_load(&_RUN(stop))) {
|
||||
_stream_expose_frame(stream, NULL, 0);
|
||||
|
||||
if (access(stream->dev->path, R_OK|W_OK) < 0) {
|
||||
if (access_error != errno) {
|
||||
SEP_INFO('=');
|
||||
LOG_PERROR("Can't access device");
|
||||
LOG_INFO("Waiting for the device access ...");
|
||||
US_SEP_INFO('=');
|
||||
US_LOG_PERROR("Can't access device");
|
||||
US_LOG_INFO("Waiting for the device access ...");
|
||||
access_error = errno;
|
||||
}
|
||||
sleep(stream->error_delay);
|
||||
continue;
|
||||
} else {
|
||||
SEP_INFO('=');
|
||||
US_SEP_INFO('=');
|
||||
access_error = 0;
|
||||
}
|
||||
|
||||
if ((pool = _stream_init_one(stream)) == NULL) {
|
||||
LOG_INFO("Sleeping %u seconds before new stream init ...", stream->error_delay);
|
||||
US_LOG_INFO("Sleeping %u seconds before new stream init ...", stream->error_delay);
|
||||
sleep(stream->error_delay);
|
||||
} else {
|
||||
break;
|
||||
@@ -272,90 +272,86 @@ static workers_pool_s *_stream_init_loop(stream_s *stream) {
|
||||
return pool;
|
||||
}
|
||||
|
||||
static workers_pool_s *_stream_init_one(stream_s *stream) {
|
||||
if (device_open(stream->dev) < 0) {
|
||||
static us_workers_pool_s *_stream_init_one(us_stream_s *stream) {
|
||||
if (us_device_open(stream->dev) < 0) {
|
||||
goto error;
|
||||
}
|
||||
if (
|
||||
stream->enc->type == ENCODER_TYPE_M2M_VIDEO
|
||||
|| stream->enc->type == ENCODER_TYPE_M2M_IMAGE
|
||||
|| (RUN(h264) && !is_jpeg(stream->dev->run->format))
|
||||
stream->enc->type == US_ENCODER_TYPE_M2M_VIDEO
|
||||
|| stream->enc->type == US_ENCODER_TYPE_M2M_IMAGE
|
||||
|| (_RUN(h264) && !us_is_jpeg(stream->dev->run->format))
|
||||
) {
|
||||
device_export_to_dma(stream->dev);
|
||||
us_device_export_to_dma(stream->dev);
|
||||
}
|
||||
if (device_switch_capturing(stream->dev, true) < 0) {
|
||||
if (us_device_switch_capturing(stream->dev, true) < 0) {
|
||||
goto error;
|
||||
}
|
||||
return encoder_workers_pool_init(stream->enc, stream->dev);
|
||||
return us_encoder_workers_pool_init(stream->enc, stream->dev);
|
||||
error:
|
||||
device_close(stream->dev);
|
||||
us_device_close(stream->dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _stream_expose_frame(stream_s *stream, frame_s *frame, unsigned captured_fps) {
|
||||
# define VID(_next) RUN(video->_next)
|
||||
static void _stream_expose_frame(us_stream_s *stream, us_frame_s *frame, unsigned captured_fps) {
|
||||
# define VID(x_next) _RUN(video->x_next)
|
||||
|
||||
frame_s *new = NULL;
|
||||
us_frame_s *new = NULL;
|
||||
|
||||
A_MUTEX_LOCK(&VID(mutex));
|
||||
US_MUTEX_LOCK(&VID(mutex));
|
||||
|
||||
if (frame) {
|
||||
new = frame;
|
||||
RUN(last_as_blank_ts) = 0; // Останавливаем таймер
|
||||
LOG_DEBUG("Exposed ALIVE video frame");
|
||||
_RUN(last_as_blank_ts) = 0; // Останавливаем таймер
|
||||
US_LOG_DEBUG("Exposed ALIVE video frame");
|
||||
|
||||
} else {
|
||||
if (VID(frame->used == 0)) {
|
||||
new = stream->blank; // Инициализация
|
||||
RUN(last_as_blank_ts) = 0;
|
||||
_RUN(last_as_blank_ts) = 0;
|
||||
|
||||
} else if (VID(frame->online)) { // Если переходим из online в offline
|
||||
if (stream->last_as_blank < 0) { // Если last_as_blank выключен, просто покажем старую картинку
|
||||
new = stream->blank;
|
||||
LOG_INFO("Changed video frame to BLANK");
|
||||
US_LOG_INFO("Changed video frame to BLANK");
|
||||
} else if (stream->last_as_blank > 0) { // // Если нужен таймер - запустим
|
||||
RUN(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);
|
||||
_RUN(last_as_blank_ts) = us_get_now_monotonic() + stream->last_as_blank;
|
||||
US_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");
|
||||
US_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");
|
||||
// US_LOG_INFO("Changed video frame to BLANK");
|
||||
}
|
||||
|
||||
if ( // Если уже оффлайн, включена фича last_as_blank с таймером и он запущен
|
||||
stream->last_as_blank > 0
|
||||
&& RUN(last_as_blank_ts) != 0
|
||||
&& RUN(last_as_blank_ts) < get_now_monotonic()
|
||||
&& _RUN(last_as_blank_ts) != 0
|
||||
&& _RUN(last_as_blank_ts) < us_get_now_monotonic()
|
||||
) {
|
||||
new = stream->blank;
|
||||
RUN(last_as_blank_ts) = 0; // // Останавливаем таймер
|
||||
LOG_INFO("Changed last ALIVE video frame to BLANK");
|
||||
_RUN(last_as_blank_ts) = 0; // // Останавливаем таймер
|
||||
US_LOG_INFO("Changed last ALIVE video frame to BLANK");
|
||||
}
|
||||
}
|
||||
|
||||
if (new) {
|
||||
frame_copy(new, VID(frame));
|
||||
us_frame_copy(new, VID(frame));
|
||||
}
|
||||
VID(frame->online) = (bool)frame;
|
||||
VID(captured_fps) = captured_fps;
|
||||
atomic_store(&VID(updated), true);
|
||||
|
||||
A_MUTEX_UNLOCK(&VID(mutex));
|
||||
US_MUTEX_UNLOCK(&VID(mutex));
|
||||
|
||||
new = (frame ? frame : stream->blank);
|
||||
SINK_PUT(sink, new);
|
||||
_SINK_PUT(sink, new);
|
||||
|
||||
if (frame == NULL) {
|
||||
SINK_PUT(raw_sink, stream->blank);
|
||||
H264_PUT(stream->blank, false);
|
||||
_SINK_PUT(raw_sink, stream->blank);
|
||||
_H264_PUT(stream->blank, false);
|
||||
}
|
||||
|
||||
# undef VID
|
||||
}
|
||||
|
||||
#undef H264_PUT
|
||||
#undef SINK_PUT
|
||||
#undef RUN
|
||||
|
||||
@@ -49,48 +49,48 @@
|
||||
|
||||
|
||||
typedef struct {
|
||||
frame_s *frame;
|
||||
us_frame_s *frame;
|
||||
unsigned captured_fps;
|
||||
atomic_bool updated;
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
atomic_bool has_clients; // For slowdown
|
||||
} video_s;
|
||||
} us_video_s;
|
||||
|
||||
typedef struct {
|
||||
video_s *video;
|
||||
long double last_as_blank_ts;
|
||||
us_video_s *video;
|
||||
long double last_as_blank_ts;
|
||||
|
||||
h264_stream_s *h264;
|
||||
us_h264_stream_s *h264;
|
||||
|
||||
atomic_bool stop;
|
||||
} stream_runtime_s;
|
||||
atomic_bool stop;
|
||||
} us_stream_runtime_s;
|
||||
|
||||
typedef struct {
|
||||
device_s *dev;
|
||||
encoder_s *enc;
|
||||
us_device_s *dev;
|
||||
us_encoder_s *enc;
|
||||
|
||||
frame_s *blank;
|
||||
int last_as_blank;
|
||||
bool slowdown;
|
||||
unsigned error_delay;
|
||||
us_frame_s *blank;
|
||||
int last_as_blank;
|
||||
bool slowdown;
|
||||
unsigned error_delay;
|
||||
|
||||
memsink_s *sink;
|
||||
memsink_s *raw_sink;
|
||||
us_memsink_s *sink;
|
||||
us_memsink_s *raw_sink;
|
||||
|
||||
memsink_s *h264_sink;
|
||||
unsigned h264_bitrate;
|
||||
unsigned h264_gop;
|
||||
char *h264_m2m_path;
|
||||
us_memsink_s *h264_sink;
|
||||
unsigned h264_bitrate;
|
||||
unsigned h264_gop;
|
||||
char *h264_m2m_path;
|
||||
|
||||
stream_runtime_s *run;
|
||||
} stream_s;
|
||||
us_stream_runtime_s *run;
|
||||
} us_stream_s;
|
||||
|
||||
|
||||
stream_s *stream_init(device_s *dev, encoder_s *enc);
|
||||
void stream_destroy(stream_s *stream);
|
||||
us_stream_s *us_stream_init(us_device_s *dev, us_encoder_s *enc);
|
||||
void us_stream_destroy(us_stream_s *stream);
|
||||
|
||||
void stream_loop(stream_s *stream);
|
||||
void stream_loop_break(stream_s *stream);
|
||||
void us_stream_loop(us_stream_s *stream);
|
||||
void us_stream_loop_break(us_stream_s *stream);
|
||||
|
||||
bool stream_has_clients(stream_s *stream);
|
||||
bool us_stream_has_clients(us_stream_s *stream);
|
||||
|
||||
@@ -26,16 +26,16 @@
|
||||
static void *_worker_thread(void *v_worker);
|
||||
|
||||
|
||||
workers_pool_s *workers_pool_init(
|
||||
us_workers_pool_s *us_workers_pool_init(
|
||||
const char *name, const char *wr_prefix, unsigned n_workers, long double desired_interval,
|
||||
workers_pool_job_init_f job_init, void *job_init_arg,
|
||||
workers_pool_job_destroy_f job_destroy,
|
||||
workers_pool_run_job_f run_job) {
|
||||
us_workers_pool_job_init_f job_init, void *job_init_arg,
|
||||
us_workers_pool_job_destroy_f job_destroy,
|
||||
us_workers_pool_run_job_f run_job) {
|
||||
|
||||
LOG_INFO("Creating pool %s with %u workers ...", name, n_workers);
|
||||
US_LOG_INFO("Creating pool %s with %u workers ...", name, n_workers);
|
||||
|
||||
workers_pool_s *pool;
|
||||
A_CALLOC(pool, 1);
|
||||
us_workers_pool_s *pool;
|
||||
US_CALLOC(pool, 1);
|
||||
pool->name = name;
|
||||
pool->desired_interval = desired_interval;
|
||||
pool->job_destroy = job_destroy;
|
||||
@@ -44,25 +44,25 @@ workers_pool_s *workers_pool_init(
|
||||
atomic_init(&pool->stop, false);
|
||||
|
||||
pool->n_workers = n_workers;
|
||||
A_CALLOC(pool->workers, pool->n_workers);
|
||||
US_CALLOC(pool->workers, pool->n_workers);
|
||||
|
||||
A_MUTEX_INIT(&pool->free_workers_mutex);
|
||||
A_COND_INIT(&pool->free_workers_cond);
|
||||
US_MUTEX_INIT(&pool->free_workers_mutex);
|
||||
US_COND_INIT(&pool->free_workers_cond);
|
||||
|
||||
for (unsigned number = 0; number < pool->n_workers; ++number) {
|
||||
# define WR(_next) pool->workers[number]._next
|
||||
# define WR(x_next) pool->workers[number].x_next
|
||||
|
||||
WR(number) = number;
|
||||
A_ASPRINTF(WR(name), "%s-%u", wr_prefix, number);
|
||||
US_ASPRINTF(WR(name), "%s-%u", wr_prefix, number);
|
||||
|
||||
A_MUTEX_INIT(&WR(has_job_mutex));
|
||||
US_MUTEX_INIT(&WR(has_job_mutex));
|
||||
atomic_init(&WR(has_job), false);
|
||||
A_COND_INIT(&WR(has_job_cond));
|
||||
US_COND_INIT(&WR(has_job_cond));
|
||||
|
||||
WR(pool) = pool;
|
||||
WR(job) = job_init(job_init_arg);
|
||||
|
||||
A_THREAD_CREATE(&WR(tid), _worker_thread, (void *)&(pool->workers[number]));
|
||||
US_THREAD_CREATE(&WR(tid), _worker_thread, (void *)&(pool->workers[number]));
|
||||
pool->free_workers += 1;
|
||||
|
||||
# undef WR
|
||||
@@ -70,21 +70,21 @@ workers_pool_s *workers_pool_init(
|
||||
return pool;
|
||||
}
|
||||
|
||||
void workers_pool_destroy(workers_pool_s *pool) {
|
||||
LOG_INFO("Destroying workers pool %s ...", pool->name);
|
||||
void us_workers_pool_destroy(us_workers_pool_s *pool) {
|
||||
US_LOG_INFO("Destroying workers pool %s ...", pool->name);
|
||||
|
||||
atomic_store(&pool->stop, true);
|
||||
for (unsigned number = 0; number < pool->n_workers; ++number) {
|
||||
# define WR(_next) pool->workers[number]._next
|
||||
# define WR(x_next) pool->workers[number].x_next
|
||||
|
||||
A_MUTEX_LOCK(&WR(has_job_mutex));
|
||||
US_MUTEX_LOCK(&WR(has_job_mutex));
|
||||
atomic_store(&WR(has_job), true); // Final job: die
|
||||
A_MUTEX_UNLOCK(&WR(has_job_mutex));
|
||||
A_COND_SIGNAL(&WR(has_job_cond));
|
||||
US_MUTEX_UNLOCK(&WR(has_job_mutex));
|
||||
US_COND_SIGNAL(&WR(has_job_cond));
|
||||
|
||||
A_THREAD_JOIN(WR(tid));
|
||||
A_MUTEX_DESTROY(&WR(has_job_mutex));
|
||||
A_COND_DESTROY(&WR(has_job_cond));
|
||||
US_THREAD_JOIN(WR(tid));
|
||||
US_MUTEX_DESTROY(&WR(has_job_mutex));
|
||||
US_COND_DESTROY(&WR(has_job_cond));
|
||||
|
||||
free(WR(name));
|
||||
|
||||
@@ -93,19 +93,19 @@ void workers_pool_destroy(workers_pool_s *pool) {
|
||||
# undef WR
|
||||
}
|
||||
|
||||
A_MUTEX_DESTROY(&pool->free_workers_mutex);
|
||||
A_COND_DESTROY(&pool->free_workers_cond);
|
||||
US_MUTEX_DESTROY(&pool->free_workers_mutex);
|
||||
US_COND_DESTROY(&pool->free_workers_cond);
|
||||
|
||||
free(pool->workers);
|
||||
free(pool);
|
||||
}
|
||||
|
||||
worker_s *workers_pool_wait(workers_pool_s *pool) {
|
||||
worker_s *ready_wr = NULL;
|
||||
us_worker_s *us_workers_pool_wait(us_workers_pool_s *pool) {
|
||||
us_worker_s *ready_wr = NULL;
|
||||
|
||||
A_MUTEX_LOCK(&pool->free_workers_mutex);
|
||||
A_COND_WAIT_TRUE(pool->free_workers, &pool->free_workers_cond, &pool->free_workers_mutex);
|
||||
A_MUTEX_UNLOCK(&pool->free_workers_mutex);
|
||||
US_MUTEX_LOCK(&pool->free_workers_mutex);
|
||||
US_COND_WAIT_TRUE(pool->free_workers, &pool->free_workers_cond, &pool->free_workers_mutex);
|
||||
US_MUTEX_UNLOCK(&pool->free_workers_mutex);
|
||||
|
||||
if (pool->oldest_wr && !atomic_load(&pool->oldest_wr->has_job)) {
|
||||
ready_wr = pool->oldest_wr;
|
||||
@@ -129,7 +129,7 @@ worker_s *workers_pool_wait(workers_pool_s *pool) {
|
||||
return ready_wr;
|
||||
}
|
||||
|
||||
void workers_pool_assign(workers_pool_s *pool, worker_s *ready_wr/*, void *job*/) {
|
||||
void us_workers_pool_assign(us_workers_pool_s *pool, us_worker_s *ready_wr/*, void *job*/) {
|
||||
if (pool->oldest_wr == NULL) {
|
||||
pool->oldest_wr = ready_wr;
|
||||
pool->latest_wr = pool->oldest_wr;
|
||||
@@ -146,21 +146,21 @@ void workers_pool_assign(workers_pool_s *pool, worker_s *ready_wr/*, void *job*/
|
||||
}
|
||||
pool->latest_wr->next_wr = NULL;
|
||||
|
||||
A_MUTEX_LOCK(&ready_wr->has_job_mutex);
|
||||
US_MUTEX_LOCK(&ready_wr->has_job_mutex);
|
||||
//ready_wr->job = job;
|
||||
atomic_store(&ready_wr->has_job, true);
|
||||
A_MUTEX_UNLOCK(&ready_wr->has_job_mutex);
|
||||
A_COND_SIGNAL(&ready_wr->has_job_cond);
|
||||
US_MUTEX_UNLOCK(&ready_wr->has_job_mutex);
|
||||
US_COND_SIGNAL(&ready_wr->has_job_cond);
|
||||
|
||||
A_MUTEX_LOCK(&pool->free_workers_mutex);
|
||||
US_MUTEX_LOCK(&pool->free_workers_mutex);
|
||||
pool->free_workers -= 1;
|
||||
A_MUTEX_UNLOCK(&pool->free_workers_mutex);
|
||||
US_MUTEX_UNLOCK(&pool->free_workers_mutex);
|
||||
}
|
||||
|
||||
long double workers_pool_get_fluency_delay(workers_pool_s *pool, worker_s *ready_wr) {
|
||||
long double us_workers_pool_get_fluency_delay(us_workers_pool_s *pool, us_worker_s *ready_wr) {
|
||||
const long double approx_job_time = pool->approx_job_time * 0.9 + ready_wr->last_job_time * 0.1;
|
||||
|
||||
LOG_VERBOSE("Correcting pool's %s approx_job_time: %.3Lf -> %.3Lf (last_job_time=%.3Lf)",
|
||||
US_LOG_VERBOSE("Correcting pool's %s approx_job_time: %.3Lf -> %.3Lf (last_job_time=%.3Lf)",
|
||||
pool->name, pool->approx_job_time, approx_job_time, ready_wr->last_job_time);
|
||||
|
||||
pool->approx_job_time = approx_job_time;
|
||||
@@ -176,35 +176,35 @@ long double workers_pool_get_fluency_delay(workers_pool_s *pool, worker_s *ready
|
||||
}
|
||||
|
||||
static void *_worker_thread(void *v_worker) {
|
||||
worker_s *wr = (worker_s *)v_worker;
|
||||
us_worker_s *wr = (us_worker_s *)v_worker;
|
||||
|
||||
A_THREAD_RENAME("%s", wr->name);
|
||||
LOG_DEBUG("Hello! I am a worker %s ^_^", wr->name);
|
||||
US_THREAD_RENAME("%s", wr->name);
|
||||
US_LOG_DEBUG("Hello! I am a worker %s ^_^", wr->name);
|
||||
|
||||
while (!atomic_load(&wr->pool->stop)) {
|
||||
LOG_DEBUG("Worker %s waiting for a new job ...", wr->name);
|
||||
US_LOG_DEBUG("Worker %s waiting for a new job ...", wr->name);
|
||||
|
||||
A_MUTEX_LOCK(&wr->has_job_mutex);
|
||||
A_COND_WAIT_TRUE(atomic_load(&wr->has_job), &wr->has_job_cond, &wr->has_job_mutex);
|
||||
A_MUTEX_UNLOCK(&wr->has_job_mutex);
|
||||
US_MUTEX_LOCK(&wr->has_job_mutex);
|
||||
US_COND_WAIT_TRUE(atomic_load(&wr->has_job), &wr->has_job_cond, &wr->has_job_mutex);
|
||||
US_MUTEX_UNLOCK(&wr->has_job_mutex);
|
||||
|
||||
if (!atomic_load(&wr->pool->stop)) {
|
||||
long double job_start_ts = get_now_monotonic();
|
||||
long double job_start_ts = us_get_now_monotonic();
|
||||
wr->job_failed = !wr->pool->run_job(wr);
|
||||
if (!wr->job_failed) {
|
||||
wr->job_start_ts = job_start_ts;
|
||||
wr->last_job_time = get_now_monotonic() - wr->job_start_ts;
|
||||
wr->last_job_time = us_get_now_monotonic() - wr->job_start_ts;
|
||||
}
|
||||
//wr->job = NULL;
|
||||
atomic_store(&wr->has_job, false);
|
||||
}
|
||||
|
||||
A_MUTEX_LOCK(&wr->pool->free_workers_mutex);
|
||||
US_MUTEX_LOCK(&wr->pool->free_workers_mutex);
|
||||
wr->pool->free_workers += 1;
|
||||
A_MUTEX_UNLOCK(&wr->pool->free_workers_mutex);
|
||||
A_COND_SIGNAL(&wr->pool->free_workers_cond);
|
||||
US_MUTEX_UNLOCK(&wr->pool->free_workers_mutex);
|
||||
US_COND_SIGNAL(&wr->pool->free_workers_cond);
|
||||
}
|
||||
|
||||
LOG_DEBUG("Bye-bye (worker %s)", wr->name);
|
||||
US_LOG_DEBUG("Bye-bye (worker %s)", wr->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include "../libs/logging.h"
|
||||
|
||||
|
||||
typedef struct worker_sx {
|
||||
typedef struct us_worker_sx {
|
||||
pthread_t tid;
|
||||
unsigned number;
|
||||
char *name;
|
||||
@@ -49,27 +49,27 @@ typedef struct worker_sx {
|
||||
long double job_start_ts;
|
||||
pthread_cond_t has_job_cond;
|
||||
|
||||
struct worker_sx *prev_wr;
|
||||
struct worker_sx *next_wr;
|
||||
struct us_worker_sx *prev_wr;
|
||||
struct us_worker_sx *next_wr;
|
||||
|
||||
struct workers_pool_sx *pool;
|
||||
} worker_s;
|
||||
struct us_workers_pool_sx *pool;
|
||||
} us_worker_s;
|
||||
|
||||
typedef void *(*workers_pool_job_init_f)(void *arg);
|
||||
typedef void (*workers_pool_job_destroy_f)(void *job);
|
||||
typedef bool (*workers_pool_run_job_f)(worker_s *wr);
|
||||
typedef void *(*us_workers_pool_job_init_f)(void *arg);
|
||||
typedef void (*us_workers_pool_job_destroy_f)(void *job);
|
||||
typedef bool (*us_workers_pool_run_job_f)(us_worker_s *wr);
|
||||
|
||||
typedef struct workers_pool_sx {
|
||||
typedef struct us_workers_pool_sx {
|
||||
const char *name;
|
||||
long double desired_interval;
|
||||
|
||||
workers_pool_job_destroy_f job_destroy;
|
||||
workers_pool_run_job_f run_job;
|
||||
us_workers_pool_job_destroy_f job_destroy;
|
||||
us_workers_pool_run_job_f run_job;
|
||||
|
||||
unsigned n_workers;
|
||||
worker_s *workers;
|
||||
worker_s *oldest_wr;
|
||||
worker_s *latest_wr;
|
||||
us_worker_s *workers;
|
||||
us_worker_s *oldest_wr;
|
||||
us_worker_s *latest_wr;
|
||||
|
||||
long double approx_job_time;
|
||||
|
||||
@@ -78,18 +78,18 @@ typedef struct workers_pool_sx {
|
||||
pthread_cond_t free_workers_cond;
|
||||
|
||||
atomic_bool stop;
|
||||
} workers_pool_s;
|
||||
} us_workers_pool_s;
|
||||
|
||||
|
||||
workers_pool_s *workers_pool_init(
|
||||
us_workers_pool_s *us_workers_pool_init(
|
||||
const char *name, const char *wr_prefix, unsigned n_workers, long double desired_interval,
|
||||
workers_pool_job_init_f job_init, void *job_init_arg,
|
||||
workers_pool_job_destroy_f job_destroy,
|
||||
workers_pool_run_job_f run_job);
|
||||
us_workers_pool_job_init_f job_init, void *job_init_arg,
|
||||
us_workers_pool_job_destroy_f job_destroy,
|
||||
us_workers_pool_run_job_f run_job);
|
||||
|
||||
void workers_pool_destroy(workers_pool_s *pool);
|
||||
void us_workers_pool_destroy(us_workers_pool_s *pool);
|
||||
|
||||
worker_s *workers_pool_wait(workers_pool_s *pool);
|
||||
void workers_pool_assign(workers_pool_s *pool, worker_s *ready_wr/*, void *job*/);
|
||||
us_worker_s *us_workers_pool_wait(us_workers_pool_s *pool);
|
||||
void us_workers_pool_assign(us_workers_pool_s *pool, us_worker_s *ready_wr/*, void *job*/);
|
||||
|
||||
long double workers_pool_get_fluency_delay(workers_pool_s *pool, worker_s *ready_wr);
|
||||
long double us_workers_pool_get_fluency_delay(us_workers_pool_s *pool, us_worker_s *ready_wr);
|
||||
|
||||
Reference in New Issue
Block a user