Compare commits

...

23 Commits
v1.19 ... v1.22

Author SHA1 Message Date
Devaev Maxim
53feba1248 Bump version: 1.21 → 1.22 2020-08-20 05:15:24 +03:00
Devaev Maxim
119821d5af queued_fps = 0 for no clients 2020-08-19 14:27:52 +03:00
Maxim Devaev
4faabf27ec Merge pull request #33 from pikvm/sem-timeout
Sem timeout
2020-08-19 13:44:23 +03:00
Devaev Maxim
191f6e3c09 non-zero min-frame-size; default = 128 2020-08-19 13:20:22 +03:00
Devaev Maxim
4e51439118 bsd compat 2020-08-18 15:19:09 +03:00
Devaev Maxim
e184e187a2 option --color-effect 2020-08-18 12:15:44 +03:00
Maxim Devaev
592568c9aa Merge pull request #31 from pikvm/flip
Options to flip image
2020-08-17 18:17:01 +03:00
Devaev Maxim
46c5a547a9 options to flip image 2020-08-17 03:49:29 +03:00
Devaev Maxim
3d097a4ffb more logs 2020-08-15 04:51:00 +03:00
Devaev Maxim
00e32c915c fixed uninitialized value 2020-08-15 04:40:55 +03:00
Devaev Maxim
d44c340dce vcos sem timeout 2020-08-15 00:10:07 +03:00
Devaev Maxim
8c18f1dffe Issue #25: fixed freebsd build 2020-08-14 03:38:05 +03:00
Devaev Maxim
c3c386ea5b Bump version: 1.20 → 1.21 2020-08-13 08:59:36 +03:00
Devaev Maxim
fa09992c46 better logging 2020-08-11 06:08:13 +03:00
Devaev Maxim
cefcd0c963 fixed ptr printing 2020-08-11 05:49:03 +03:00
Devaev Maxim
96c806071d Bump version: 1.19 → 1.20 2020-08-11 02:13:16 +03:00
Devaev Maxim
0775b35ef8 option --tcp-nodelay 2020-08-10 08:12:14 +03:00
Maxim Devaev
138d9a74d8 Update README.ru.md 2020-08-01 14:34:15 +03:00
Maxim Devaev
2a668643dc Update README.md 2020-08-01 14:33:52 +03:00
Maxim Devaev
56312cffb5 Update README.md 2020-07-24 13:08:53 +03:00
Maxim Devaev
f553b97dba Update README.md 2020-07-23 22:19:12 +03:00
Maxim Devaev
b619b1e096 Update README.ru.md 2020-07-23 22:18:52 +03:00
Maxim Devaev
06a32fd3ab Update README.md 2020-07-23 22:18:20 +03:00
17 changed files with 104 additions and 36 deletions

View File

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

View File

@@ -4,8 +4,8 @@
[[Русская версия]](README.ru.md)
µStreamer is a lightweight and very quick server to broadcast [MJPG](https://en.wikipedia.org/wiki/Motion_JPEG) video from any V4L2 device to the net. All new browsers have native support of this video format, as well as most video players such as mplayer, VLC etc.
µStreamer is a part of the [Pi-KVM](https://github.com/pikvm) project designed to stream [VGA](https://www.amazon.com/dp/B0126O0RDC) and [HDMI](https://auvidea.com/b101-hdmi-to-csi-2-bridge-15-pin-fpc/) screencast hardware data with the highest resolution and FPS possible.
µStreamer is a lightweight and very quick server to stream [MJPG](https://en.wikipedia.org/wiki/Motion_JPEG) video from any V4L2 device to the net. All new browsers have native support of this video format, as well as most video players such as mplayer, VLC etc.
µStreamer is a part of the [Pi-KVM](https://github.com/pikvm/pikvm) project designed to stream [VGA](https://www.amazon.com/dp/B0126O0RDC) and [HDMI](https://auvidea.com/b101-hdmi-to-csi-2-bridge-15-pin-fpc/) screencast hardware data with the highest resolution and FPS possible.
µStreamer is very similar to [mjpg-streamer](https://github.com/jacksonliam/mjpg-streamer) with ```input_uvc.so``` and ```output_http.so``` plugins, however, there are some major differences. The key ones are:
@@ -13,11 +13,11 @@
|----------|---------------|-------------------|
| Multithreaded JPEG encoding | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Yes | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) No |
| [OpenMAX IL](https://www.khronos.org/openmaxil) hardware acceleration<br>on Raspberry Pi | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Yes | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) No |
| Behavior when the device<br>is disconnected while streaming | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Shows a black screen<br>with ```NO SIGNAL``` on it<br>until reconnected | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) Stops the broadcast <sup>1</sup> |
| Behavior when the device<br>is disconnected while streaming | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Shows a black screen<br>with ```NO SIGNAL``` on it<br>until reconnected | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) Stops the streaming <sup>1</sup> |
| [DV-timings](https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/dv-timings.html) support -<br>the ability to change resolution<br>on the fly by source signal | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Yes | ![#ffaa00](https://placehold.it/15/ffaa00/000000?text=+) Partially yes <sup>1</sup> |
| Option to skip frames when streaming<br>static images by HTTP to save the traffic | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Yes <sup>2</sup> | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) No |
| Streaming via UNIX domain socket | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Yes | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) No |
| Debug logs without recompiling,<br>performance statistics log,<br>access to HTTP broadcast parameters | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Yes | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) No |
| Debug logs without recompiling,<br>performance statistics log,<br>access to HTTP streaming parameters | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Yes | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) No |
| Option to serve files<br>with a built-in HTTP server | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Yes | ![#ffaa00](https://placehold.it/15/ffaa00/000000?text=+) Regular files only |
| Signaling about the stream state to GPIO<br>on Raspberry Pi using [wiringPi](http://wiringpi.com) | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Yes | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) No |
| Access to webcam controls (focus, servos)<br>and settings such as brightness via HTTP | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) No | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Yes |
@@ -25,7 +25,7 @@
Footnotes:
* ```1``` Long before µStreamer, I made a [patch](https://github.com/jacksonliam/mjpg-streamer/pull/164) to add DV-timings support to mjpg-streamer and to keep it from hanging up no device disconnection. Alas, the patch is far from perfect and I can't guarantee it will work every time - mjpg-streamer's source code is very complicated and its structure is hard to understand. With this in mind, along with needing multithreading and JPEG hardware acceleration in the future, I decided to make my own stream server from scratch instead of supporting legacy code.
* ```2``` This feature allows to cut down outgoing traffic several-fold when broadcasting HDMI, but it increases CPU usage a little bit. The idea is that HDMI is a fully digital interface and each captured frame can be identical to the previous one byte-wise. There's no need to broadcast the same image over the net several times a second. With the `--drop-same-frames=20` option enabled, µStreamer will drop all the matching frames (with a limit of 20 in a row). Each new frame is matched with the previous one first by length, then using ```memcmp()```.
* ```2``` This feature allows to cut down outgoing traffic several-fold when streaming HDMI, but it increases CPU usage a little bit. The idea is that HDMI is a fully digital interface and each captured frame can be identical to the previous one byte-wise. There's no need to stream the same image over the net several times a second. With the `--drop-same-frames=20` option enabled, µStreamer will drop all the matching frames (with a limit of 20 in a row). Each new frame is matched with the previous one first by length, then using ```memcmp()```.
-----
# TL;DR
@@ -36,7 +36,7 @@ If you're going to live-stream from your backyard webcam and need to control it,
You'll need ```make```, ```gcc```, ```libevent``` with ```pthreads``` support, ```libjpeg8```/```libjpeg-turbo```, ```libuuid``` and ```libbsd``` (only for Linux).
* Arch: `sudo pacman -S libevent libjpeg-turbo libutil-linux libbsd`.
* Raspbian: `sudo apt install libevent-dev libjpeg8-dev uuid-dev libbsd-dev`.
* Raspbian: `sudo apt install libevent-dev libjpeg8-dev uuid-dev libbsd-dev`. Add `libraspberrypi-dev` for `WITH_OMX=1` and `wiringpi` for `WITH_GPIO=1`.
* Debian: `sudo apt install build-essential libevent-dev libjpeg62-turbo-dev uuid-dev libbsd-dev`.
On Raspberry Pi you can build the program with OpenMAX IL. To do this pass option ```WITH_OMX=1``` to ```make```. To enable GPIO support install [wiringPi](http://wiringpi.com) and pass option ```WITH_GPIO=1```. If the compiler reports about a missing function ```pthread_get_name_np()``` (or similar), add option ```WITH_PTHREAD_NP=0``` (it's enabled by default). For the similar error with ```setproctitle()``` add option ```WITH_SETPROCTITLE=0```.
@@ -53,7 +53,7 @@ FreeBSD port: https://www.freshports.org/multimedia/ustreamer.
-----
# Usage
Without arguments, ```ustreamer``` will try to open ```/dev/video0``` with 640x480 resolution and start broadcasting on ```http://127.0.0.1:8080```. You can override this behavior using parameters ```--device```, ```--host``` and ```--port```. For example, to broadcast to the world, run:
Without arguments, ```ustreamer``` will try to open ```/dev/video0``` with 640x480 resolution and start streaming on ```http://127.0.0.1:8080```. You can override this behavior using parameters ```--device```, ```--host``` and ```--port```. For example, to stream to the world, run:
```
# ./ustreamer --device=/dev/video1 --host=0.0.0.0 --port=80
```

View File

@@ -5,7 +5,7 @@
[[English version]](README.md)
µStreamer - это маленький и очень быстрый сервер, который позволяет организовать трансляцию видео в формате [MJPG](https://en.wikipedia.org/wiki/Motion_JPEG) с любого устройства V4L2 в сеть. Этот формат нативно поддерживается всеми современными браузерами и большинством приложений для просмотра видео (mplayer, VLC и так далее). µStreamer был разработан в рамках проекта [Pi-KVM](https://github.com/pikvm) специально для стриминга с устройств видеозахвата [VGA](https://www.amazon.com/dp/B0126O0RDC) и [HDMI](https://auvidea.com/b101-hdmi-to-csi-2-bridge-15-pin-fpc/) с максимально возможным разрешением и FPS, которые только позволяет железо.
µStreamer - это маленький и очень быстрый сервер, который позволяет организовать трансляцию видео в формате [MJPG](https://en.wikipedia.org/wiki/Motion_JPEG) с любого устройства V4L2 в сеть. Этот формат нативно поддерживается всеми современными браузерами и большинством приложений для просмотра видео (mplayer, VLC и так далее). µStreamer был разработан в рамках проекта [Pi-KVM](https://github.com/pikvm/pikvm) специально для стриминга с устройств видеозахвата [VGA](https://www.amazon.com/dp/B0126O0RDC) и [HDMI](https://auvidea.com/b101-hdmi-to-csi-2-bridge-15-pin-fpc/) с максимально возможным разрешением и FPS, которые только позволяет железо.
Функционально µStreamer очень похож на [mjpg-streamer](https://github.com/jacksonliam/mjpg-streamer) при использовании им плагинов ```input_uvc.so``` и ```output_http.so```, однако имеет ряд серьезных отличий. Основные приведены в этой таблице:
@@ -36,7 +36,7 @@
Для сборки вам понадобятся ```make```, ```gcc```, ```libevent``` с поддержкой ```pthreads```, ```libjpeg8```/```libjpeg-turbo```, ```libuuid``` и ```libbsd``` (только для Linux).
* Arch: `sudo pacman -S libevent libjpeg-turbo libutil-linux libbsd`.
* Raspbian: `sudo apt install libevent-dev libjpeg8-dev uuid-dev libbsd-dev`.
* Raspbian: `sudo apt install libevent-dev libjpeg8-dev uuid-dev libbsd-dev`. Добавьте `libraspberrypi-dev` для сборки с `WITH_OMX=1` и `wiringpi` для `WITH_GPIO=1`.
* Debian: `sudo apt install build-essential libevent-dev libjpeg62-turbo-dev uuid-dev libbsd-dev`.
На Raspberry Pi программу можно собрать с поддержкой OpenMAX IL. Для этого передайте ```make``` параметр ```WITH_OMX=1```. Для включения сборки с поддержкой GPIO установите [wiringPi](http://wiringpi.com) и добавьте параметр ```WITH_GPIO=1```. Если при сборке компилятор ругается на отсутствие функции ```pthread_get_name_np()``` или другой подобной, добавьте параметр ```WITH_PTHREAD_NP=0``` (по умолчанию он включен). При аналогичной ошибке с функцией ```setproctitle()``` добавьте параметр ```WITH_SETPROCTITLE=0```.

View File

@@ -3,7 +3,7 @@
pkgname=ustreamer
pkgver=1.19
pkgver=1.22
pkgrel=1
pkgdesc="Lightweight and fast MJPG-HTTP streamer"
url="https://github.com/pikvm/ustreamer"

View File

@@ -6,7 +6,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=ustreamer
PKG_VERSION:=1.19
PKG_VERSION:=1.22
PKG_RELEASE:=1
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>

View File

@@ -23,5 +23,5 @@
#pragma once
#ifndef VERSION
# define VERSION "1.19"
# define VERSION "1.22"
#endif

View File

@@ -120,6 +120,7 @@ struct device_t *device_init(void) {
dev->standard = V4L2_STD_UNKNOWN;
dev->n_buffers = cores_available + 1;
dev->n_workers = min_u(cores_available, dev->n_buffers);
dev->min_frame_size = 128;
dev->timeout = 1;
dev->error_delay = 1;
dev->io_method = V4L2_MEMORY_MMAP;
@@ -736,6 +737,9 @@ static void _device_apply_controls(struct device_t *dev) {
CONTROL_MANUAL_CID ( V4L2_CID_BACKLIGHT_COMPENSATION, backlight_compensation);
CONTROL_AUTO_CID (V4L2_CID_AUTO_WHITE_BALANCE, V4L2_CID_WHITE_BALANCE_TEMPERATURE, white_balance);
CONTROL_AUTO_CID (V4L2_CID_AUTOGAIN, V4L2_CID_GAIN, gain);
CONTROL_MANUAL_CID ( V4L2_CID_COLORFX, color_effect);
CONTROL_MANUAL_CID ( V4L2_CID_VFLIP, flip_vertical);
CONTROL_MANUAL_CID ( V4L2_CID_HFLIP, flip_horizontal);
# undef CONTROL_AUTO_CID
# undef CONTROL_MANUAL_CID

View File

@@ -90,6 +90,9 @@ struct controls_t {
struct control_t backlight_compensation;
struct control_t white_balance;
struct control_t gain;
struct control_t color_effect;
struct control_t flip_vertical;
struct control_t flip_horizontal;
};
struct device_t {

View File

@@ -201,16 +201,20 @@ int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, uns
#pragma GCC diagnostic pop
assert(encoder->run->type != ENCODER_TYPE_UNKNOWN);
assert(dev->run->hw_buffers[buf_index].used > 0);
dev->run->pictures[buf_index]->encode_begin_ts = get_now_monotonic();
if (encoder->run->type == ENCODER_TYPE_CPU) {
LOG_VERBOSE("Compressing buffer %u using CPU", buf_index);
cpu_encoder_compress_buffer(dev, buf_index, encoder->run->quality);
} else if (encoder->run->type == ENCODER_TYPE_HW) {
LOG_VERBOSE("Compressing buffer %u using HW (just copying)", buf_index);
hw_encoder_compress_buffer(dev, buf_index);
}
# ifdef WITH_OMX
else if (encoder->run->type == ENCODER_TYPE_OMX) {
LOG_VERBOSE("Compressing buffer %u using OMX", buf_index);
if (omx_encoder_compress_buffer(encoder->run->omxs[worker_number], dev, buf_index) < 0) {
goto error;
}

View File

@@ -102,11 +102,11 @@ struct omx_encoder_t *omx_encoder_init(void) {
LOG_INFO("Initializing OMX encoder ...");
if (vcos_semaphore_create(&omx->handler_lock, "handler_lock", 0) != VCOS_SUCCESS) {
if (vcos_semaphore_create(&omx->handler_sem, "handler_sem", 0) != VCOS_SUCCESS) {
LOG_ERROR("Can't create VCOS semaphore");
goto error;
}
omx->i_handler_lock = true;
omx->i_handler_sem = true;
if (_omx_init_component(omx) < 0) {
goto error;
@@ -132,8 +132,8 @@ void omx_encoder_destroy(struct omx_encoder_t *omx) {
_omx_encoder_clear_ports(omx);
component_set_state(&omx->encoder, OMX_StateLoaded);
if (omx->i_handler_lock) {
vcos_semaphore_delete(&omx->handler_lock);
if (omx->i_handler_sem) {
vcos_semaphore_delete(&omx->handler_sem);
}
if (omx->i_encoder) {
@@ -180,6 +180,7 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
# define OUT(_next) omx->output_buffer->_next
OMX_ERRORTYPE error;
VCOS_STATUS_T sem_status;
size_t slice_size = (IN(nAllocLen) < HW_BUFFER(used) ? IN(nAllocLen) : HW_BUFFER(used));
size_t pos = 0;
@@ -236,7 +237,13 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
}
}
vcos_semaphore_wait(&omx->handler_lock);
// vcos_semaphore_wait(&omx->handler_sem);
switch (sem_status = vcos_semaphore_wait_timeout(&omx->handler_sem, 3000)) {
case VCOS_SUCCESS: break;
case VCOS_EAGAIN: LOG_ERROR("Can't wait VCOS semaphore: EAGAIN (timeout)"); return -1;
case VCOS_EINVAL: LOG_ERROR("Can't wait VCOS semaphore: EINTVAL"); return -1;
default: LOG_ERROR("Can't wait VCOS semaphore: %d", sem_status); return -1;
}
}
# undef OUT
@@ -466,7 +473,7 @@ static OMX_ERRORTYPE _omx_event_handler(
if (event == OMX_EventError) {
LOG_ERROR_OMX((OMX_ERRORTYPE)data1, "OMX error event received");
omx->failed = true;
vcos_semaphore_post(&omx->handler_lock);
assert(vcos_semaphore_post(&omx->handler_sem) == VCOS_SUCCESS);
}
return OMX_ErrorNone;
}
@@ -481,7 +488,7 @@ static OMX_ERRORTYPE _omx_input_required_handler(
struct omx_encoder_t *omx = (struct omx_encoder_t *)v_omx;
omx->input_required = true;
vcos_semaphore_post(&omx->handler_lock);
assert(vcos_semaphore_post(&omx->handler_sem) == VCOS_SUCCESS);
return OMX_ErrorNone;
}
@@ -495,6 +502,6 @@ static OMX_ERRORTYPE _omx_output_available_handler(
struct omx_encoder_t *omx = (struct omx_encoder_t *)v_omx;
omx->output_available = true;
vcos_semaphore_post(&omx->handler_lock);
assert(vcos_semaphore_post(&omx->handler_sem) == VCOS_SUCCESS);
return OMX_ErrorNone;
}

View File

@@ -42,9 +42,9 @@ struct omx_encoder_t {
bool input_required;
bool output_available;
bool failed;
VCOS_SEMAPHORE_T handler_lock;
VCOS_SEMAPHORE_T handler_sem;
bool i_handler_lock;
bool i_handler_sem;
bool i_encoder;
bool i_input_port_enabled;
bool i_output_port_enabled;

View File

@@ -31,8 +31,12 @@
#include <fcntl.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <event2/event.h>
#include <event2/thread.h>
@@ -231,6 +235,9 @@ int http_server_listen(struct http_server_t *server) {
return -1;
}
LOG_INFO("Listening HTTP on UNIX socket '%s'", server->unix_path);
if (server->tcp_nodelay) {
LOG_ERROR("TCP_NODELAY flag can't be used with UNIX socket and will be ignored");
}
} else {
LOG_DEBUG("Binding HTTP to [%s]:%u ...", server->host, server->port);
if (evhttp_bind_socket(server->run->http, server->host, server->port) < 0) {
@@ -539,6 +546,16 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
client_addr, client_port, client->id, server->run->stream_clients_count);
buf_event = evhttp_connection_get_bufferevent(conn);
if (server->tcp_nodelay && !server->run->unix_fd) {
evutil_socket_t fd;
int on = 1;
LOG_DEBUG("HTTP: Setting up TCP_NODELAY to the client [%s]:%u ...", client_addr, client_port);
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]:%u", client_addr, client_port);
}
}
bufferevent_setcb(buf_event, NULL, NULL, _http_callback_stream_error, (void *)client);
bufferevent_enable(buf_event, EV_READ);
} else {
@@ -727,6 +744,7 @@ static void _http_queue_send_stream(struct http_server_t *server, bool stream_up
struct evhttp_connection *conn;
struct bufferevent *buf_event;
long long now;
bool has_clients = false;
bool queued = false;
for (struct stream_client_t *client = server->run->stream_clients; client != NULL; client = client->next) {
@@ -757,6 +775,8 @@ static void _http_queue_send_stream(struct http_server_t *server, bool stream_up
} else if (stream_updated) { // Для dual
client->updated_prev = false;
}
has_clients = true;
}
}
@@ -770,6 +790,8 @@ static void _http_queue_send_stream(struct http_server_t *server, bool stream_up
queued_fps_second = now;
}
queued_fps_accum += 1;
} else if (!has_clients) {
server->run->exposed->queued_fps = 0;
}
}

View File

@@ -91,6 +91,7 @@ struct http_server_t {
char *unix_path;
bool unix_rm;
mode_t unix_mode;
bool tcp_nodelay;
unsigned timeout;
char *user;

View File

@@ -92,10 +92,14 @@ enum _OPT_VALUES {
_O_BACKLIGHT_COMPENSATION,
_O_WHITE_BALANCE,
_O_GAIN,
_O_COLOR_EFFECT,
_O_FLIP_VERTICAL,
_O_FLIP_HORIZONTAL,
_O_USER,
_O_PASSWD,
_O_STATIC,
_O_TCP_NODELAY,
_O_SERVER_TIMEOUT,
#ifdef WITH_GPIO
@@ -154,6 +158,9 @@ static const struct option _LONG_OPTS[] = {
{"backlight-compensation", required_argument, NULL, _O_BACKLIGHT_COMPENSATION},
{"white-balance", required_argument, NULL, _O_WHITE_BALANCE},
{"gain", required_argument, NULL, _O_GAIN},
{"color-effect", required_argument, NULL, _O_COLOR_EFFECT},
{"flip-vertical", required_argument, NULL, _O_FLIP_VERTICAL},
{"flip-horizontal", required_argument, NULL, _O_FLIP_HORIZONTAL},
{"host", required_argument, NULL, _O_HOST},
{"port", required_argument, NULL, _O_PORT},
@@ -168,6 +175,7 @@ static const struct option _LONG_OPTS[] = {
{"drop-same-frames", required_argument, NULL, _O_DROP_SAME_FRAMES},
{"slowdown", no_argument, NULL, _O_SLOWDOWN},
{"fake-resolution", required_argument, NULL, _O_FAKE_RESOLUTION},
{"tcp-nodelay", no_argument, NULL, _O_TCP_NODELAY},
{"server-timeout", required_argument, NULL, _O_SERVER_TIMEOUT},
#ifdef WITH_GPIO
@@ -331,7 +339,7 @@ int options_parse(struct options_t *options, struct device_t *dev, struct encode
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, 0, 8192, 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_buffers, 1, 32, 0);
@@ -358,6 +366,9 @@ int options_parse(struct options_t *options, struct device_t *dev, struct encode
OPT_CTL_DEFAULT_NOBREAK(backlight_compensation);
OPT_CTL_DEFAULT_NOBREAK(white_balance);
OPT_CTL_DEFAULT_NOBREAK(gain);
OPT_CTL_DEFAULT_NOBREAK(color_effect);
OPT_CTL_DEFAULT_NOBREAK(flip_vertical);
OPT_CTL_DEFAULT_NOBREAK(flip_horizontal);
break;
case _O_BRIGHTNESS: OPT_CTL_AUTO(brightness);
case _O_CONTRAST: OPT_CTL_MANUAL(contrast);
@@ -368,6 +379,9 @@ int options_parse(struct options_t *options, struct device_t *dev, struct encode
case _O_BACKLIGHT_COMPENSATION: OPT_CTL_MANUAL(backlight_compensation);
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_FLIP_VERTICAL: OPT_CTL_MANUAL(flip_vertical);
case _O_FLIP_HORIZONTAL: OPT_CTL_MANUAL(flip_horizontal);
case _O_HOST: OPT_SET(server->host, optarg);
case _O_PORT: OPT_NUMBER("--port", server->port, 1, 65535, 0);
@@ -382,6 +396,7 @@ int options_parse(struct options_t *options, struct device_t *dev, struct encode
case _O_DROP_SAME_FRAMES: OPT_NUMBER("--drop-same-frames", server->drop_same_frames, 0, VIDEO_MAX_FPS, 0);
case _O_SLOWDOWN: OPT_SET(server->slowdown, true);
case _O_FAKE_RESOLUTION: OPT_RESOLUTION("--fake-resolution", server->fake_width, server->fake_height, false);
case _O_TCP_NODELAY: OPT_SET(server->tcp_nodelay, true);
case _O_SERVER_TIMEOUT: OPT_NUMBER("--server-timeout", server->timeout, 1, 60, 0);
# ifdef WITH_GPIO
@@ -556,7 +571,7 @@ static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_s
printf(" Available: %s; default: MMAP\n\n", IO_METHODS_STR);
printf(" -f|--desired-fps <N> ──────────────── Desired FPS. Default: maximum possible.\n\n");
printf(" -z|--min-frame-size <N> ───────────── Drop frames smaller then this limit. Useful if the device\n");
printf(" produces small-sized garbage frames. Default: disabled.\n\n");
printf(" produces small-sized garbage frames. Default: %zu bytes.\n\n", dev->min_frame_size);
printf(" -n|--persistent ───────────────────── Don't re-initialize device on timeout. Default: disabled.\n\n");
printf(" -t|--dv-timings ───────────────────── Enable DV timings querying and events processing\n");
printf(" to automatic resolution change. Default: disabled.\n\n");
@@ -597,6 +612,9 @@ static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_s
printf(" --backlight-compensation <N|default> ─ Set backlight compensation. Default: no change.\n\n");
printf(" --white-balance <N|auto|default> ───── Set white balance. Default: no change.\n\n");
printf(" --gain <N|auto|default> ────────────── Set gain. Default: no change.\n\n");
printf(" --color-effect <N|default> ─────────── Set color effect. Default: no change.\n\n");
printf(" --flip-vertical <1|0|default> ──────── Set vertical flip. Default: no change.\n\n");
printf(" --flip-horizontal <1|0|default> ────── Set horizontal flip. Default: no change.\n\n");
printf(" Hint: use v4l2-ctl --list-ctrls-menus to query available controls of the device.\n\n");
printf("HTTP server options:\n");
printf("════════════════════\n");
@@ -622,6 +640,8 @@ static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_s
printf(" -l|--slowdown ────────────── Slowdown capturing to 1 FPS or less when no stream clients are connected.\n");
printf(" Useful to reduce CPU consumption. Default: disabled.\n\n");
printf(" -R|--fake-resolution <WxH> ─ Override image resolution for the /state. Default: disabled.\n\n");
printf(" --tcp-nodelay ────────────── Set TCP_NODELAY flag to the client /stream socket. Ignored for --unix.\n");
printf(" Default: disabled.\n\n");
printf(" --server-timeout <sec> ───── Timeout for client connections. Default: %u.\n\n", server->timeout);
#ifdef WITH_GPIO
printf("GPIO options:\n");

View File

@@ -51,7 +51,7 @@ size_t picture_get_generous_size(unsigned width, unsigned height) {
void picture_realloc_data(struct picture_t *picture, size_t size) {
if (picture->allocated < size) {
LOG_DEBUG("Increasing picture 0x%p buffer: %zu -> %zu (+%zu)",
LOG_DEBUG("Increasing picture %p buffer: %zu -> %zu (+%zu)",
picture, picture->allocated, size, size - picture->allocated);
A_REALLOC(picture->data, size);
picture->allocated = size;

View File

@@ -449,23 +449,24 @@ static void *_worker_thread(void *v_worker) {
GPIO_SET_HIGH_AT(workers_busy_at, worker->number);
# endif
if (encoder_compress_buffer(worker->encoder, worker->dev, worker->number, worker->buf_index) < 0) {
worker->job_failed = false;
}
worker->job_failed = (bool)encoder_compress_buffer(worker->encoder, worker->dev, worker->number, worker->buf_index);
if (device_release_buffer(worker->dev, worker->buf_index) == 0) {
worker->job_start_ts = PICTURE(encode_begin_ts);
atomic_store(&worker->has_job, false);
if (!worker->job_failed) {
worker->job_start_ts = PICTURE(encode_begin_ts);
worker->last_comp_time = PICTURE(encode_end_ts) - worker->job_start_ts;
worker->last_comp_time = PICTURE(encode_end_ts) - worker->job_start_ts;
LOG_VERBOSE("Compressed new JPEG: size=%zu, time=%0.3Lf, worker=%u, buffer=%u",
PICTURE(used), worker->last_comp_time, worker->number, worker->buf_index);
LOG_VERBOSE("Compressed new JPEG: size=%zu, time=%0.3Lf, worker=%u, buffer=%u",
PICTURE(used), worker->last_comp_time, worker->number, worker->buf_index);
} else {
LOG_VERBOSE("Compression failed: worker=%u, buffer=%u", worker->number, worker->buf_index);
}
} else {
worker->job_failed = true;
atomic_store(&worker->has_job, false);
}
atomic_store(&worker->has_job, false);
# undef PICTURE
}

View File

@@ -78,7 +78,13 @@ INLINE long double get_now_monotonic(void) {
time_t sec;
long msec;
# if defined(CLOCK_MONOTONIC_RAW)
get_now(CLOCK_MONOTONIC_RAW, &sec, &msec);
# elif defined(CLOCK_MONOTONIC_FAST)
get_now(CLOCK_MONOTONIC_FAST, &sec, &msec);
# else
get_now(CLOCK_MONOTONIC, &sec, &msec);
# endif
return (long double)sec + ((long double)msec) / 1000;
}