using us_ prefixes

This commit is contained in:
Maxim Devaev
2022-07-19 11:02:36 +03:00
parent e3293d6887
commit cbee3adb2e
90 changed files with 2297 additions and 2338 deletions

View File

@@ -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);

View File

@@ -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);

View File

@@ -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("════════");

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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))

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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); \
}

View File

@@ -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;

View File

@@ -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__); \
} \
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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));
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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:

View File

@@ -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);

View File

@@ -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,

View File

@@ -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[];

View File

@@ -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>&micro;Streamer v" VERSION "</h3> \
<h3>&micro;Streamer v" US_VERSION "</h3> \
<hr> \
<ul> \
<li> \

View File

@@ -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

View File

@@ -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);

View File

@@ -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
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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,

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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); \
} \
}

View File

@@ -32,4 +32,4 @@
#include "../../libs/logging.h"
char *bufferevent_my_format_reason(short what);
char *us_bufferevent_format_reason(short what);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -31,4 +31,4 @@
#include "../../libs/tools.h"
char *simplify_request_path(const char *str);
char *us_simplify_request_path(const char *str);

View File

@@ -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), &params);
# define PARSE_PARAM(_type, _name) client->_name = uri_get_##_type(&params, #_name)
# define PARSE_PARAM(x_type, x_name) client->x_name = us_uri_get_##x_type(&params, #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(&params);
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

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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-")

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);