Compare commits

...

5 Commits
v0.56 ... v0.57

Author SHA1 Message Date
Devaev Maxim
d979209096 Bump version: 0.56 → 0.57 2019-03-16 01:36:14 +03:00
Devaev Maxim
154d8e4c2b updated readme 2019-03-16 01:35:47 +03:00
Devaev Maxim
27d42d2545 --device-persistent -> -n|--persistent 2019-03-16 01:30:55 +03:00
Devaev Maxim
142670c374 refactoring 2019-03-16 01:21:03 +03:00
Devaev Maxim
2a9c4d7e7a C11 atomic types 2019-03-16 01:02:27 +03:00
11 changed files with 108 additions and 93 deletions

View File

@@ -1,7 +1,7 @@
[bumpversion] [bumpversion]
commit = True commit = True
tag = True tag = True
current_version = 0.56 current_version = 0.57
parse = (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?)? parse = (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?)?
serialize = serialize =
{major}.{minor} {major}.{minor}

View File

@@ -7,7 +7,7 @@ CC ?= gcc
# ===== # =====
LIBS = -lm -ljpeg -pthread -levent -levent_pthreads -luuid LIBS = -lm -ljpeg -pthread -levent -levent_pthreads -luuid
override CFLAGS += -c -std=c99 -Wall -Wextra -D_GNU_SOURCE override CFLAGS += -c -std=c11 -Wall -Wextra -D_GNU_SOURCE
SOURCES = $(shell ls src/*.c src/encoders/cpu/*.c src/encoders/hw/*.c) SOURCES = $(shell ls src/*.c src/encoders/cpu/*.c src/encoders/hw/*.c)
PROG = ustreamer PROG = ustreamer

View File

@@ -3,7 +3,7 @@
pkgname=ustreamer pkgname=ustreamer
pkgver=0.56 pkgver=0.57
pkgrel=1 pkgrel=1
pkgdesc="Lightweight and fast MJPG-HTTP streamer" pkgdesc="Lightweight and fast MJPG-HTTP streamer"
url="https://github.com/pi-kvm/ustreamer" url="https://github.com/pi-kvm/ustreamer"

View File

@@ -56,6 +56,8 @@ The recommended way of running µStreamer with [Auvidea B101](https://www.raspbe
$ ./ustreamer \ $ ./ustreamer \
--format=uyvy \ # Device input format --format=uyvy \ # Device input format
--encoder=omx \ # Hardware encoding with OpenMAX --encoder=omx \ # Hardware encoding with OpenMAX
--workers=3 \ # Maximum workers for OpenMAX
--persistent \ # Don't re-initialize device on timeout (for example when HDMI cable was disconnected)
--dv-timings \ # Use DV-timings --dv-timings \ # Use DV-timings
--drop-same-frames=30 # Save that traffic --drop-same-frames=30 # Save that traffic
``` ```

View File

@@ -56,6 +56,8 @@ $ ./ustreamer --help
$ ./ustreamer \ $ ./ustreamer \
--format=uyvy \ # Настройка входного формата устройства --format=uyvy \ # Настройка входного формата устройства
--encoder=omx \ # Использование аппаратного кодирования с помощью OpenMAX --encoder=omx \ # Использование аппаратного кодирования с помощью OpenMAX
--workers=3 \ # Максимум воркеров для OpenMAX
--persistent \ # Не переинициализировать устройство при таймауте (например, когда был отключен HDMI-кабель)
--dv-timings \ # Включение DV-таймингов --dv-timings \ # Включение DV-таймингов
--drop-same-frames=30 # Экономим трафик --drop-same-frames=30 # Экономим трафик
``` ```

View File

@@ -22,4 +22,4 @@
#pragma once #pragma once
#define VERSION "0.56" #define VERSION "0.57"

View File

@@ -42,8 +42,8 @@
#include "encoder.h" #include "encoder.h"
#define INPUT_PORT 340 static const OMX_U32 _INPUT_PORT = 340;
#define OUTPUT_PORT 341 static const OMX_U32 _OUTPUT_PORT = 341;
static int _i_omx = 0; static int _i_omx = 0;
@@ -300,7 +300,7 @@ static int _omx_setup_input(struct omx_encoder_t *omx, struct device_t *dev) {
LOG_DEBUG("Setting up OMX JPEG input port ..."); LOG_DEBUG("Setting up OMX JPEG input port ...");
if (component_get_portdef(&omx->encoder, &portdef, INPUT_PORT) < 0) { if (component_get_portdef(&omx->encoder, &portdef, _INPUT_PORT) < 0) {
LOG_ERROR("... first"); LOG_ERROR("... first");
return -1; return -1;
} }
@@ -338,17 +338,17 @@ static int _omx_setup_input(struct omx_encoder_t *omx, struct device_t *dev) {
return -1; return -1;
} }
if (component_get_portdef(&omx->encoder, &portdef, INPUT_PORT) < 0) { if (component_get_portdef(&omx->encoder, &portdef, _INPUT_PORT) < 0) {
LOG_ERROR("... second"); LOG_ERROR("... second");
return -1; return -1;
} }
if (component_enable_port(&omx->encoder, INPUT_PORT) < 0) { if (component_enable_port(&omx->encoder, _INPUT_PORT) < 0) {
return -1; return -1;
} }
omx->i_input_port_enabled = true; omx->i_input_port_enabled = true;
if ((error = OMX_AllocateBuffer(omx->encoder, &omx->input_buffer, INPUT_PORT, NULL, portdef.nBufferSize)) != OMX_ErrorNone) { if ((error = OMX_AllocateBuffer(omx->encoder, &omx->input_buffer, _INPUT_PORT, NULL, portdef.nBufferSize)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't allocate OMX JPEG input buffer"); LOG_OMX_ERROR(error, "Can't allocate OMX JPEG input buffer");
return -1; return -1;
} }
@@ -361,7 +361,7 @@ static int _omx_setup_output(struct omx_encoder_t *omx, unsigned quality) {
LOG_DEBUG("Setting up OMX JPEG output port ..."); LOG_DEBUG("Setting up OMX JPEG output port ...");
if (component_get_portdef(&omx->encoder, &portdef, OUTPUT_PORT) < 0) { if (component_get_portdef(&omx->encoder, &portdef, _OUTPUT_PORT) < 0) {
LOG_ERROR("... first"); LOG_ERROR("... first");
return -1; return -1;
} }
@@ -374,7 +374,7 @@ static int _omx_setup_output(struct omx_encoder_t *omx, unsigned quality) {
return -1; return -1;
} }
if (component_get_portdef(&omx->encoder, &portdef, OUTPUT_PORT) < 0) { if (component_get_portdef(&omx->encoder, &portdef, _OUTPUT_PORT) < 0) {
LOG_ERROR("... second"); LOG_ERROR("... second");
return -1; return -1;
} }
@@ -395,7 +395,7 @@ static int _omx_setup_output(struct omx_encoder_t *omx, unsigned quality) {
OMX_PARAM_IJGSCALINGTYPE ijg; OMX_PARAM_IJGSCALINGTYPE ijg;
OMX_INIT_STRUCTURE(ijg); OMX_INIT_STRUCTURE(ijg);
ijg.nPortIndex = OUTPUT_PORT; ijg.nPortIndex = _OUTPUT_PORT;
ijg.bEnabled = OMX_TRUE; ijg.bEnabled = OMX_TRUE;
if ((error = OMX_SetParameter(omx->encoder, OMX_IndexParamBrcmEnableIJGTableScaling, &ijg)) != OMX_ErrorNone) { if ((error = OMX_SetParameter(omx->encoder, OMX_IndexParamBrcmEnableIJGTableScaling, &ijg)) != OMX_ErrorNone) {
@@ -408,7 +408,7 @@ static int _omx_setup_output(struct omx_encoder_t *omx, unsigned quality) {
OMX_IMAGE_PARAM_QFACTORTYPE qfactor; OMX_IMAGE_PARAM_QFACTORTYPE qfactor;
OMX_INIT_STRUCTURE(qfactor); OMX_INIT_STRUCTURE(qfactor);
qfactor.nPortIndex = OUTPUT_PORT; qfactor.nPortIndex = _OUTPUT_PORT;
qfactor.nQFactor = quality; qfactor.nQFactor = quality;
if ((error = OMX_SetParameter(omx->encoder, OMX_IndexParamQFactor, &qfactor)) != OMX_ErrorNone) { if ((error = OMX_SetParameter(omx->encoder, OMX_IndexParamQFactor, &qfactor)) != OMX_ErrorNone) {
@@ -417,12 +417,12 @@ static int _omx_setup_output(struct omx_encoder_t *omx, unsigned quality) {
} }
} }
if (component_enable_port(&omx->encoder, OUTPUT_PORT) < 0) { if (component_enable_port(&omx->encoder, _OUTPUT_PORT) < 0) {
return -1; return -1;
} }
omx->i_output_port_enabled = true; omx->i_output_port_enabled = true;
if ((error = OMX_AllocateBuffer(omx->encoder, &omx->output_buffer, OUTPUT_PORT, NULL, portdef.nBufferSize)) != OMX_ErrorNone) { if ((error = OMX_AllocateBuffer(omx->encoder, &omx->output_buffer, _OUTPUT_PORT, NULL, portdef.nBufferSize)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't allocate OMX JPEG output buffer"); LOG_OMX_ERROR(error, "Can't allocate OMX JPEG output buffer");
return -1; return -1;
} }
@@ -434,23 +434,23 @@ static int _omx_encoder_clear_ports(struct omx_encoder_t *omx) {
int retcode = 0; int retcode = 0;
if (omx->i_output_port_enabled) { if (omx->i_output_port_enabled) {
retcode -= component_disable_port(&omx->encoder, OUTPUT_PORT); retcode -= component_disable_port(&omx->encoder, _OUTPUT_PORT);
omx->i_output_port_enabled = false; omx->i_output_port_enabled = false;
} }
if (omx->i_input_port_enabled) { if (omx->i_input_port_enabled) {
retcode -= component_disable_port(&omx->encoder, INPUT_PORT); retcode -= component_disable_port(&omx->encoder, _INPUT_PORT);
omx->i_input_port_enabled = false; omx->i_input_port_enabled = false;
} }
if (omx->input_buffer) { if (omx->input_buffer) {
if ((error = OMX_FreeBuffer(omx->encoder, INPUT_PORT, omx->input_buffer)) != OMX_ErrorNone) { if ((error = OMX_FreeBuffer(omx->encoder, _INPUT_PORT, omx->input_buffer)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't free OMX JPEG input buffer"); LOG_OMX_ERROR(error, "Can't free OMX JPEG input buffer");
// retcode -= 1; // retcode -= 1;
} }
omx->input_buffer = NULL; omx->input_buffer = NULL;
} }
if (omx->output_buffer) { if (omx->output_buffer) {
if ((error = OMX_FreeBuffer(omx->encoder, OUTPUT_PORT, omx->output_buffer)) != OMX_ErrorNone) { if ((error = OMX_FreeBuffer(omx->encoder, _OUTPUT_PORT, omx->output_buffer)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't free OMX JPEG output buffer"); LOG_OMX_ERROR(error, "Can't free OMX JPEG output buffer");
// retcode -= 1; // retcode -= 1;
} }

View File

@@ -22,6 +22,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdatomic.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <unistd.h> #include <unistd.h>
@@ -330,11 +331,15 @@ static void _http_callback_snapshot(struct evhttp_request *request, void *v_serv
ADD_HEADER("Pragma", "no-cache"); ADD_HEADER("Pragma", "no-cache");
ADD_HEADER("Expires", "Mon, 3 Jan 2000 12:34:56 GMT"); ADD_HEADER("Expires", "Mon, 3 Jan 2000 12:34:56 GMT");
# define ADD_TIME_HEADER(_key, _value) \ # define ADD_TIME_HEADER(_key, _value) { \
{ sprintf(header_buf, "%.06Lf", _value); ADD_HEADER(_key, header_buf); } sprintf(header_buf, "%.06Lf", _value); \
ADD_HEADER(_key, header_buf); \
}
# define ADD_UNSIGNED_HEADER(_key, _value) \ # define ADD_UNSIGNED_HEADER(_key, _value) { \
{ sprintf(header_buf, "%u", _value); ADD_HEADER(_key, header_buf); } sprintf(header_buf, "%u", _value); \
ADD_HEADER(_key, header_buf); \
}
ADD_TIME_HEADER("X-Timestamp", get_now_real()); ADD_TIME_HEADER("X-Timestamp", get_now_real());
@@ -471,8 +476,8 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
// по тем же причинам, по которым у нас нет Content-Length. // по тем же причинам, по которым у нас нет Content-Length.
# define ADD_ADVANCE_HEADERS \ # define ADD_ADVANCE_HEADERS \
{ assert(evbuffer_add_printf(buf, \ 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, get_now_real()))
if (client->need_initial) { if (client->need_initial) {
assert(evbuffer_add_printf(buf, assert(evbuffer_add_printf(buf,
@@ -652,10 +657,12 @@ static void _http_exposed_refresh(UNUSED int fd, UNUSED short what, void *v_serv
bool stream_updated = false; bool stream_updated = false;
bool picture_updated = false; bool picture_updated = false;
# define UNLOCK_STREAM \ # define UNLOCK_STREAM { \
{ server->run->stream->updated = false; A_MUTEX_UNLOCK(&server->run->stream->mutex); } atomic_store(&server->run->stream->updated, false); \
A_MUTEX_UNLOCK(&server->run->stream->mutex); \
}
if (server->run->stream->updated) { if (atomic_load(&server->run->stream->updated)) {
LOG_DEBUG("Refreshing HTTP exposed ..."); LOG_DEBUG("Refreshing HTTP exposed ...");
A_MUTEX_LOCK(&server->run->stream->mutex); A_MUTEX_LOCK(&server->run->stream->mutex);
if (server->run->stream->picture.size > 0) { // If online if (server->run->stream->picture.size > 0) { // If online

View File

@@ -41,8 +41,8 @@
#include "http.h" #include "http.h"
static const char _short_opts[] = "d:i:x:y:m:a:f:z:tb:w:q:c:s:p:u:ro:e:h"; static const char _SHORT_OPTS[] = "d:i:x:y:m:a:f:z:ntb:w:q:c:s:p:u:ro:e:h";
static const struct option _long_opts[] = { static const struct option _LONG_OPTS[] = {
{"device", required_argument, NULL, 'd'}, {"device", required_argument, NULL, 'd'},
{"input", required_argument, NULL, 'i'}, {"input", required_argument, NULL, 'i'},
{"width", required_argument, NULL, 'x'}, {"width", required_argument, NULL, 'x'},
@@ -51,14 +51,14 @@ static const struct option _long_opts[] = {
{"tv-standard", required_argument, NULL, 'a'}, {"tv-standard", required_argument, NULL, 'a'},
{"desired-fps", required_argument, NULL, 'f'}, {"desired-fps", required_argument, NULL, 'f'},
{"min-frame-size", required_argument, NULL, 'z'}, {"min-frame-size", required_argument, NULL, 'z'},
{"persistent", no_argument, NULL, 'n'},
{"dv-timings", no_argument, NULL, 't'}, {"dv-timings", no_argument, NULL, 't'},
{"buffers", required_argument, NULL, 'b'}, {"buffers", required_argument, NULL, 'b'},
{"workers", required_argument, NULL, 'w'}, {"workers", required_argument, NULL, 'w'},
{"quality", required_argument, NULL, 'q'}, {"quality", required_argument, NULL, 'q'},
{"encoder", required_argument, NULL, 'c'}, {"encoder", required_argument, NULL, 'c'},
{"device-timeout", required_argument, NULL, 1000}, {"device-timeout", required_argument, NULL, 1000},
{"device-persistent", no_argument, NULL, 1001}, {"device-error-delay", required_argument, NULL, 1001},
{"device-error-delay", required_argument, NULL, 1002},
{"brightness", required_argument, NULL, 2000}, {"brightness", required_argument, NULL, 2000},
{"brightness-auto", no_argument, NULL, 2001}, {"brightness-auto", no_argument, NULL, 2001},
@@ -113,30 +113,30 @@ static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_s
printf("Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com>\n\n"); printf("Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com>\n\n");
printf("Capturing options:\n"); printf("Capturing options:\n");
printf("------------------\n"); printf("------------------\n");
printf(" -d|--device </dev/path> -- Path to V4L2 device. Default: %s.\n\n", dev->path); printf(" -d|--device </dev/path> -- Path to V4L2 device. Default: %s.\n\n", dev->path);
printf(" -i|--input <N> -- Input channel. Default: %u.\n\n", dev->input); printf(" -i|--input <N> -- Input channel. Default: %u.\n\n", dev->input);
printf(" -x|--width <N> -- Initial image width. Default: %u.\n\n", dev->width); printf(" -x|--width <N> -- Initial image width. Default: %u.\n\n", dev->width);
printf(" -y|--height <N> -- Initial image height. Default: %u.\n\n", dev->height); printf(" -y|--height <N> -- Initial image height. Default: %u.\n\n", dev->height);
printf(" -m|--format <fmt> -- Image format.\n"); printf(" -m|--format <fmt> -- Image format.\n");
printf(" Available: %s; default: YUYV.\n\n", FORMATS_STR); printf(" Available: %s; default: YUYV.\n\n", FORMATS_STR);
printf(" -a|--tv-standard <std> -- Force TV standard.\n"); printf(" -a|--tv-standard <std> -- Force TV standard.\n");
printf(" Available: %s; default: disabled.\n\n", STANDARDS_STR); printf(" Available: %s; default: disabled.\n\n", STANDARDS_STR);
printf(" -f|--desired-fps <N> -- Desired FPS. Default: maximum as possible.\n\n"); printf(" -f|--desired-fps <N> -- Desired FPS. Default: maximum as possible.\n\n");
printf(" -z|--min-frame-size <N> -- Drop frames smaller then this limit.\n"); printf(" -z|--min-frame-size <N> -- Drop frames smaller then this limit.\n");
printf(" Useful if the device produces small-sized garbage frames.\n\n"); printf(" Useful if the device produces small-sized garbage frames.\n\n");
printf(" -t|--dv-timings -- Enable DV timings queriyng and events processing.\n"); printf(" -n|--persistent -- Don't re-initialize device on timeout. Default: disabled.\n\n");
printf(" Supports automatic resolution changing. Default: disabled.\n\n"); printf(" -t|--dv-timings -- Enable DV timings queriyng and events processing.\n");
printf(" -b|--buffers <N> -- The number of buffers to receive data from the device.\n"); printf(" Supports automatic resolution changing. Default: disabled.\n\n");
printf(" Each buffer may processed using an intermediate thread.\n"); printf(" -b|--buffers <N> -- The number of buffers to receive data from the device.\n");
printf(" Default: %u (number of CPU cores + 1)\n\n", dev->n_buffers); printf(" Each buffer may processed using an intermediate thread.\n");
printf(" -w|--workers <N> -- The number of compressing threads. Default: %u (== --buffers).\n\n", dev->n_workers); printf(" Default: %u (number of CPU cores + 1)\n\n", dev->n_buffers);
printf(" -q|--quality <N> -- Set quality of JPEG encoding from 1 to 100 (best). Default: %u.\n\n", encoder->quality); printf(" -w|--workers <N> -- The number of compressing threads. Default: %u (== --buffers).\n\n", dev->n_workers);
printf(" -c|--encoder <type> -- Use specified encoder. It may affects to workers number.\n"); printf(" -q|--quality <N> -- Set quality of JPEG encoding from 1 to 100 (best). Default: %u.\n\n", encoder->quality);
printf(" -- Available: %s; default: CPU.\n\n", ENCODER_TYPES_STR); printf(" -c|--encoder <type> -- Use specified encoder. It may affects to workers number.\n");
printf(" --device-timeout <seconds> -- Timeout for device querying. Default: %u\n\n", dev->timeout); printf(" -- Available: %s; default: CPU.\n\n", ENCODER_TYPES_STR);
printf(" --device-persistent -- Don't re-initialize device on timeout. Default: disabled.\n\n"); printf(" --device-timeout <seconds> -- Timeout for device querying. Default: %u\n\n", dev->timeout);
printf(" --device-error-delay <seconds> -- Delay before trying to connect to the device again\n"); printf(" --device-error-delay <seconds> -- Delay before trying to connect to the device again\n");
printf(" after a timeout. Default: %u\n\n", dev->error_delay); printf(" after timeout. Default: %u\n\n", dev->error_delay);
printf("Image control options:\n"); printf("Image control options:\n");
printf("---------------\n"); printf("---------------\n");
printf(" --brightness <N> -- Set brightness. Default: no change.\n\n"); printf(" --brightness <N> -- Set brightness. Default: no change.\n\n");
@@ -232,7 +232,7 @@ static int _parse_options(int argc, char *argv[], struct device_t *dev, struct e
int ch; int ch;
log_level = LOG_LEVEL_INFO; log_level = LOG_LEVEL_INFO;
while ((ch = getopt_long(argc, argv, _short_opts, _long_opts, &index)) >= 0) { while ((ch = getopt_long(argc, argv, _SHORT_OPTS, _LONG_OPTS, &index)) >= 0) {
switch (ch) { switch (ch) {
case 'd': OPT_SET(dev->path, optarg); case 'd': OPT_SET(dev->path, optarg);
case 'i': OPT_UNSIGNED(dev->input, "--input", 0, 128); case 'i': OPT_UNSIGNED(dev->input, "--input", 0, 128);
@@ -245,14 +245,14 @@ static int _parse_options(int argc, char *argv[], struct device_t *dev, struct e
case 'a': OPT_PARSE(dev->standard, device_parse_standard, STANDARD_UNKNOWN, "TV standard"); case 'a': OPT_PARSE(dev->standard, device_parse_standard, STANDARD_UNKNOWN, "TV standard");
case 'f': OPT_UNSIGNED(dev->desired_fps, "--desired-fps", 0, 30); case 'f': OPT_UNSIGNED(dev->desired_fps, "--desired-fps", 0, 30);
case 'z': OPT_UNSIGNED(dev->min_frame_size, "--min-frame-size", 0, 8192); case 'z': OPT_UNSIGNED(dev->min_frame_size, "--min-frame-size", 0, 8192);
case 'n': OPT_SET(dev->persistent, true);
case 't': OPT_SET(dev->dv_timings, true); case 't': OPT_SET(dev->dv_timings, true);
case 'b': OPT_UNSIGNED(dev->n_buffers, "--buffers", 1, 32); case 'b': OPT_UNSIGNED(dev->n_buffers, "--buffers", 1, 32);
case 'w': OPT_UNSIGNED(dev->n_workers, "--workers", 1, 32); case 'w': OPT_UNSIGNED(dev->n_workers, "--workers", 1, 32);
case 'q': OPT_UNSIGNED(encoder->quality, "--quality", 1, 100); case 'q': OPT_UNSIGNED(encoder->quality, "--quality", 1, 100);
case 'c': OPT_PARSE(encoder->type, encoder_parse_type, ENCODER_TYPE_UNKNOWN, "encoder type"); case 'c': OPT_PARSE(encoder->type, encoder_parse_type, ENCODER_TYPE_UNKNOWN, "encoder type");
case 1000: OPT_UNSIGNED(dev->timeout, "--device-timeout", 1, 60); case 1000: OPT_UNSIGNED(dev->timeout, "--device-timeout", 1, 60);
case 1001: OPT_SET(dev->persistent, true); case 1001: OPT_UNSIGNED(dev->error_delay, "--device-error-delay", 1, 60);
case 1002: OPT_UNSIGNED(dev->error_delay, "--device-error-delay", 1, 60);
case 2000: OPT_CTL(brightness); case 2000: OPT_CTL(brightness);
case 2001: OPT_CTL_AUTO(brightness); case 2001: OPT_CTL_AUTO(brightness);

View File

@@ -20,6 +20,7 @@
*****************************************************************************/ *****************************************************************************/
#include <stdatomic.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <time.h> #include <time.h>
@@ -59,10 +60,13 @@ struct stream_t *stream_init(struct device_t *dev, struct encoder_t *encoder) {
struct stream_t *stream; struct stream_t *stream;
A_CALLOC(proc, 1); A_CALLOC(proc, 1);
atomic_init(&proc->stop, false);
atomic_init(&proc->slowdown, false);
A_CALLOC(stream, 1); A_CALLOC(stream, 1);
stream->dev = dev; stream->dev = dev;
stream->encoder = encoder; stream->encoder = encoder;
atomic_init(&stream->updated, false);
A_MUTEX_INIT(&stream->mutex); A_MUTEX_INIT(&stream->mutex);
stream->proc = proc; stream->proc = proc;
return stream; return stream;
@@ -76,11 +80,10 @@ void stream_destroy(struct stream_t *stream) {
void stream_loop(struct stream_t *stream) { void stream_loop(struct stream_t *stream) {
struct workers_pool_t pool; struct workers_pool_t pool;
bool workers_stop;
MEMSET_ZERO(pool); MEMSET_ZERO(pool);
atomic_init(&pool.workers_stop, false);
pool.encoder = stream->encoder; pool.encoder = stream->encoder;
pool.workers_stop = &workers_stop;
LOG_INFO("Using V4L2 device: %s", stream->dev->path); LOG_INFO("Using V4L2 device: %s", stream->dev->path);
LOG_INFO("Using desired FPS: %u", stream->dev->desired_fps); LOG_INFO("Using desired FPS: %u", stream->dev->desired_fps);
@@ -99,7 +102,7 @@ void stream_loop(struct stream_t *stream) {
LOG_INFO("Capturing ..."); LOG_INFO("Capturing ...");
while (!stream->proc->stop) { while (!atomic_load(&stream->proc->stop)) {
int free_worker_number = -1; int free_worker_number = -1;
SEP_DEBUG('-'); SEP_DEBUG('-');
@@ -109,7 +112,7 @@ void stream_loop(struct stream_t *stream) {
A_COND_WAIT_TRUE(pool.free_workers, &pool.free_workers_cond, &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); A_MUTEX_UNLOCK(&pool.free_workers_mutex);
if (oldest_worker && !oldest_worker->has_job && oldest_worker->buf_index >= 0) { if (oldest_worker && !atomic_load(&oldest_worker->has_job) && oldest_worker->buf_index >= 0) {
if (oldest_worker->job_failed) { if (oldest_worker->job_failed) {
break; break;
} }
@@ -123,7 +126,7 @@ void stream_loop(struct stream_t *stream) {
} else { } else {
for (unsigned number = 0; number < stream->dev->n_workers; ++number) { for (unsigned number = 0; number < stream->dev->n_workers; ++number) {
if ( if (
!pool.workers[number].has_job && ( !atomic_load(&pool.workers[number].has_job) && (
free_worker_number == -1 free_worker_number == -1
|| pool.workers[free_worker_number].job_start_time < pool.workers[number].job_start_time || pool.workers[free_worker_number].job_start_time < pool.workers[number].job_start_time
) )
@@ -134,16 +137,16 @@ void stream_loop(struct stream_t *stream) {
} }
assert(free_worker_number >= 0); assert(free_worker_number >= 0);
assert(!pool.workers[free_worker_number].has_job); assert(!atomic_load(&pool.workers[free_worker_number].has_job));
LOG_PERF("----- Raw frame dropped; worker = %u", free_worker_number); LOG_PERF("----- Raw frame dropped; worker = %u", free_worker_number);
} }
if (stream->proc->stop) { if (atomic_load(&stream->proc->stop)) {
break; break;
} }
if (stream->proc->slowdown) { if (atomic_load(&stream->proc->slowdown)) {
usleep(1000000); usleep(1000000);
} }
@@ -252,7 +255,7 @@ void stream_loop(struct stream_t *stream) {
A_MUTEX_LOCK(&FREE_WORKER(has_job_mutex)); A_MUTEX_LOCK(&FREE_WORKER(has_job_mutex));
FREE_WORKER(buf_index) = buf_info.index; FREE_WORKER(buf_index) = buf_info.index;
FREE_WORKER(has_job) = true; atomic_store(&FREE_WORKER(has_job), true);
A_MUTEX_UNLOCK(&FREE_WORKER(has_job_mutex)); A_MUTEX_UNLOCK(&FREE_WORKER(has_job_mutex));
A_COND_SIGNAL(&FREE_WORKER(has_job_cond)); A_COND_SIGNAL(&FREE_WORKER(has_job_cond));
@@ -292,7 +295,7 @@ void stream_loop(struct stream_t *stream) {
free(stream->picture.data); free(stream->picture.data);
stream->width = 0; stream->width = 0;
stream->height = 0; stream->height = 0;
stream->updated = true; atomic_store(&stream->updated, true);
A_MUTEX_UNLOCK(&stream->mutex); A_MUTEX_UNLOCK(&stream->mutex);
} }
@@ -302,11 +305,11 @@ void stream_loop(struct stream_t *stream) {
} }
void stream_loop_break(struct stream_t *stream) { void stream_loop_break(struct stream_t *stream) {
stream->proc->stop = 1; atomic_store(&stream->proc->stop, true);
} }
void stream_switch_slowdown(struct stream_t *stream, bool slowdown) { void stream_switch_slowdown(struct stream_t *stream, bool slowdown) {
stream->proc->slowdown = slowdown; atomic_store(&stream->proc->slowdown, slowdown);
} }
static void _stream_expose_picture(struct stream_t *stream, unsigned buf_index) { static void _stream_expose_picture(struct stream_t *stream, unsigned buf_index) {
@@ -325,7 +328,7 @@ static void _stream_expose_picture(struct stream_t *stream, unsigned buf_index)
stream->width = stream->dev->run->width; stream->width = stream->dev->run->width;
stream->height = stream->dev->run->height; stream->height = stream->dev->run->height;
stream->updated = true; atomic_store(&stream->updated, true);
A_MUTEX_UNLOCK(&stream->mutex); A_MUTEX_UNLOCK(&stream->mutex);
@@ -365,8 +368,8 @@ static long double _stream_get_fluency_delay(struct device_t *dev, struct worker
static int _stream_init_loop(struct stream_t *stream, struct workers_pool_t *pool) { static int _stream_init_loop(struct stream_t *stream, struct workers_pool_t *pool) {
int retval = -1; int retval = -1;
LOG_DEBUG("%s: *stream->proc->stop = %d", __FUNCTION__, stream->proc->stop); LOG_DEBUG("%s: stream->proc->stop = %d", __FUNCTION__, atomic_load(&stream->proc->stop));
while (!stream->proc->stop) { while (!atomic_load(&stream->proc->stop)) {
if ((retval = _stream_init(stream, pool)) < 0) { if ((retval = _stream_init(stream, pool)) < 0) {
LOG_INFO("Sleeping %u seconds before new stream init ...", stream->dev->error_delay); LOG_INFO("Sleeping %u seconds before new stream init ...", stream->dev->error_delay);
sleep(stream->dev->error_delay); sleep(stream->dev->error_delay);
@@ -405,7 +408,7 @@ static int _stream_init(struct stream_t *stream, struct workers_pool_t *pool) {
static void _stream_init_workers(struct stream_t *stream, struct workers_pool_t *pool) { static void _stream_init_workers(struct stream_t *stream, struct workers_pool_t *pool) {
LOG_INFO("Spawning %u workers ...", stream->dev->n_workers); LOG_INFO("Spawning %u workers ...", stream->dev->n_workers);
*pool->workers_stop = false; atomic_store(&pool->workers_stop, false);
A_CALLOC(pool->workers, stream->dev->n_workers); A_CALLOC(pool->workers, stream->dev->n_workers);
A_MUTEX_INIT(&pool->free_workers_mutex); A_MUTEX_INIT(&pool->free_workers_mutex);
@@ -417,11 +420,12 @@ static void _stream_init_workers(struct stream_t *stream, struct workers_pool_t
pool->free_workers += 1; pool->free_workers += 1;
A_MUTEX_INIT(&WORKER(has_job_mutex)); A_MUTEX_INIT(&WORKER(has_job_mutex));
atomic_init(&WORKER(has_job), false);
A_COND_INIT(&WORKER(has_job_cond)); A_COND_INIT(&WORKER(has_job_cond));
WORKER(number) = number; WORKER(number) = number;
WORKER(proc_stop) = (sig_atomic_t *volatile)&(stream->proc->stop); WORKER(proc_stop) = &stream->proc->stop;
WORKER(workers_stop) = pool->workers_stop; WORKER(workers_stop) = &pool->workers_stop;
WORKER(free_workers_mutex) = &pool->free_workers_mutex; WORKER(free_workers_mutex) = &pool->free_workers_mutex;
WORKER(free_workers) = &pool->free_workers; WORKER(free_workers) = &pool->free_workers;
@@ -441,13 +445,13 @@ static void *_stream_worker_thread(void *v_worker) {
LOG_DEBUG("Hello! I am a worker #%u ^_^", worker->number); LOG_DEBUG("Hello! I am a worker #%u ^_^", worker->number);
while (!*worker->proc_stop && !*worker->workers_stop) { while (!atomic_load(worker->proc_stop) && !atomic_load(worker->workers_stop)) {
LOG_DEBUG("Worker %u waiting for a new job ...", worker->number); LOG_DEBUG("Worker %u waiting for a new job ...", worker->number);
A_MUTEX_LOCK(&worker->has_job_mutex); A_MUTEX_LOCK(&worker->has_job_mutex);
A_COND_WAIT_TRUE(worker->has_job, &worker->has_job_cond, &worker->has_job_mutex); A_COND_WAIT_TRUE(atomic_load(&worker->has_job), &worker->has_job_cond, &worker->has_job_mutex);
A_MUTEX_UNLOCK(&worker->has_job_mutex); A_MUTEX_UNLOCK(&worker->has_job_mutex);
if (!*worker->workers_stop) { if (!atomic_load(worker->workers_stop)) {
# define PICTURE(_next) worker->dev->run->pictures[worker->buf_index]._next # define PICTURE(_next) worker->dev->run->pictures[worker->buf_index]._next
LOG_DEBUG("Worker %u compressing JPEG from buffer %u ...", worker->number, worker->buf_index); LOG_DEBUG("Worker %u compressing JPEG from buffer %u ...", worker->number, worker->buf_index);
@@ -460,7 +464,7 @@ static void *_stream_worker_thread(void *v_worker) {
if (_stream_release_buffer(worker->dev, &worker->buf_info) == 0) { if (_stream_release_buffer(worker->dev, &worker->buf_info) == 0) {
worker->job_start_time = PICTURE(encode_begin_time); worker->job_start_time = PICTURE(encode_begin_time);
worker->has_job = false; atomic_store(&worker->has_job, false);
long double last_comp_time = PICTURE(encode_end_time) - worker->job_start_time; long double last_comp_time = PICTURE(encode_end_time) - worker->job_start_time;
@@ -472,7 +476,7 @@ static void *_stream_worker_thread(void *v_worker) {
PICTURE(size), last_comp_time, worker->number, worker->buf_index); PICTURE(size), last_comp_time, worker->number, worker->buf_index);
} else { } else {
worker->job_failed = true; worker->job_failed = true;
worker->has_job = false; atomic_store(&worker->has_job, false);
} }
# undef PICTURE # undef PICTURE
@@ -492,12 +496,12 @@ static void _stream_destroy_workers(struct stream_t *stream, struct workers_pool
if (pool->workers) { if (pool->workers) {
LOG_INFO("Destroying workers ..."); LOG_INFO("Destroying workers ...");
*pool->workers_stop = true; atomic_store(&pool->workers_stop, true);
for (unsigned number = 0; number < stream->dev->n_workers; ++number) { for (unsigned number = 0; number < stream->dev->n_workers; ++number) {
# define WORKER(_next) pool->workers[number]._next # define WORKER(_next) pool->workers[number]._next
A_MUTEX_LOCK(&WORKER(has_job_mutex)); A_MUTEX_LOCK(&WORKER(has_job_mutex));
WORKER(has_job) = true; // Final job: die atomic_store(&WORKER(has_job), true); // Final job: die
A_MUTEX_UNLOCK(&WORKER(has_job_mutex)); A_MUTEX_UNLOCK(&WORKER(has_job_mutex));
A_COND_SIGNAL(&WORKER(has_job_cond)); A_COND_SIGNAL(&WORKER(has_job_cond));

View File

@@ -23,7 +23,7 @@
#pragma once #pragma once
#include <stdbool.h> #include <stdbool.h>
#include <signal.h> #include <stdatomic.h>
#include <pthread.h> #include <pthread.h>
@@ -34,8 +34,8 @@
struct worker_t { struct worker_t {
pthread_t tid; pthread_t tid;
unsigned number; unsigned number;
sig_atomic_t *volatile proc_stop; atomic_bool *proc_stop;
bool *workers_stop; atomic_bool *workers_stop;
pthread_mutex_t last_comp_time_mutex; pthread_mutex_t last_comp_time_mutex;
long double last_comp_time; long double last_comp_time;
@@ -43,7 +43,7 @@ struct worker_t {
pthread_mutex_t has_job_mutex; pthread_mutex_t has_job_mutex;
int buf_index; int buf_index;
struct v4l2_buffer buf_info; struct v4l2_buffer buf_info;
bool has_job; atomic_bool has_job;
bool job_failed; bool job_failed;
long double job_start_time; long double job_start_time;
pthread_cond_t has_job_cond; pthread_cond_t has_job_cond;
@@ -61,7 +61,7 @@ struct worker_t {
struct workers_pool_t { struct workers_pool_t {
struct worker_t *workers; struct worker_t *workers;
bool *workers_stop; atomic_bool workers_stop;
pthread_mutex_t free_workers_mutex; pthread_mutex_t free_workers_mutex;
unsigned free_workers; unsigned free_workers;
@@ -71,8 +71,8 @@ struct workers_pool_t {
}; };
struct process_t { struct process_t {
sig_atomic_t volatile stop; atomic_bool stop;
bool slowdown; atomic_bool slowdown;
}; };
struct stream_t { struct stream_t {
@@ -80,7 +80,7 @@ struct stream_t {
unsigned width; unsigned width;
unsigned height; unsigned height;
unsigned captured_fps; unsigned captured_fps;
bool updated; atomic_bool updated;
pthread_mutex_t mutex; pthread_mutex_t mutex;
struct process_t *proc; struct process_t *proc;