Compare commits

...

22 Commits
v0.80 ... v1.4

Author SHA1 Message Date
Devaev Maxim
b02b0f910c Bump version: 1.3 → 1.4 2019-09-12 03:04:29 +03:00
Devaev Maxim
bfbb5dd29d show option choices on fail 2019-09-12 03:04:06 +03:00
Devaev Maxim
f3339f0502 fixed short options 2019-09-12 03:00:13 +03:00
Devaev Maxim
5d270b0029 improved help and messages 2019-09-12 02:45:02 +03:00
Devaev Maxim
dd5a5a079a Bump version: 1.2 → 1.3 2019-09-10 18:38:58 +03:00
Devaev Maxim
a3fcb01b7b renamed log color options 2019-09-10 18:38:49 +03:00
Devaev Maxim
afe5b91f63 Bump version: 1.1 → 1.2 2019-09-10 18:23:00 +03:00
Devaev Maxim
330641ee9f removed legacy --with/--height (with fake-) 2019-09-06 23:28:31 +03:00
Devaev Maxim
d8ec5169e7 refactoring 2019-09-06 22:44:26 +03:00
Devaev Maxim
93d5be6ffc color logging 2019-09-06 22:26:15 +03:00
Devaev Maxim
2dbaf08eb0 refactoring 2019-09-06 02:00:09 +03:00
Devaev Maxim
9a216153dc enum log_level 2019-09-06 01:48:53 +03:00
Devaev Maxim
090ed174af enum for short opts 2019-09-05 22:07:01 +03:00
Devaev Maxim
4292c8a2d1 moved options parser to separate file 2019-09-05 22:06:37 +03:00
Devaev Maxim
856dab30bc refactoring 2019-09-05 16:25:37 +03:00
Devaev Maxim
16d5c81c22 Bump version: 1.0 → 1.1 2019-09-01 22:43:13 +03:00
Devaev Maxim
58e3a77a79 changed upstream url 2019-09-01 22:42:58 +03:00
Devaev Maxim
16105db7a0 refactoring 2019-07-27 18:22:08 +03:00
Devaev Maxim
6e307b1ef4 Bump version: 0.81 → 1.0 2019-07-13 07:15:28 +03:00
Devaev Maxim
554491ff19 messages refactoring 2019-07-13 07:14:33 +03:00
Devaev Maxim
fcd70c3166 Bump version: 0.80 → 0.81 2019-07-13 01:06:35 +03:00
Devaev Maxim
aaed14e9de wider fps and resolution range 2019-07-13 01:06:21 +03:00
23 changed files with 704 additions and 522 deletions

View File

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

View File

@@ -24,15 +24,15 @@ endef
ifneq ($(call optbool,$(WITH_OMX)),)
LIBS += -lbcm_host -lvcos -lopenmaxil -L$(RPI_VC_LIBS)
override CFLAGS += -DWITH_OMX -DOMX_SKIP64BIT -I$(RPI_VC_HEADERS)
SOURCES += $(shell ls src/encoders/omx/*.c)
LIBS += -lbcm_host -lvcos -lopenmaxil -L$(RPI_VC_LIBS)
override CFLAGS += -DWITH_OMX -DOMX_SKIP64BIT -I$(RPI_VC_HEADERS)
SOURCES += $(shell ls src/encoders/omx/*.c)
endif
ifneq ($(call optbool,$(WITH_GPIO)),)
LIBS += -lwiringPi
override CFLAGS += -DWITH_GPIO
LIBS += -lwiringPi
override CFLAGS += -DWITH_GPIO
endif
@@ -58,7 +58,7 @@ regen:
$(PROG): $(SOURCES:.c=.o)
$(info -- LINKING $@)
$(info -- LD $@)
@ $(CC) $(SOURCES:.c=.o) -o $@ $(LDFLAGS) $(LIBS)
$(info ===== Build complete =====)
$(info == CC = $(CC))

View File

@@ -2,7 +2,7 @@
[[Русская версия]](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/pi-kvm) 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 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 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:
@@ -35,7 +35,7 @@ You'll need ```make```, ```gcc```, ```libevent``` with ```pthreads``` support,
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```.
```
$ git clone --depth=1 https://github.com/pi-kvm/ustreamer
$ git clone --depth=1 https://github.com/pikvm/ustreamer
$ cd ustreamer
$ make
$ ./ustreamer --help

View File

@@ -2,7 +2,7 @@
[[English version]](README.md)
µStreamer - это маленький и очень быстрый сервер, который позволяет организовать трансляцию видео в формате [MJPG](https://en.wikipedia.org/wiki/Motion_JPEG) с любого устройства V4L2 в сеть. Этот формат нативно поддерживается всеми современными браузерами и большинством приложений для просмотра видео (mplayer, VLC и так далее). µStreamer был разработан в рамках проекта [Pi-KVM](https://github.com/pi-kvm) специально для стриминга с устройств видеозахвата [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) специально для стриминга с устройств видеозахвата [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```, однако имеет ряд серьезных отличий. Основные приведены в этой таблице:
@@ -35,7 +35,7 @@
На Raspberry Pi программу можно собрать с поддержкой OpenMAX IL. Для этого передайте ```make``` параметр ```WITH_OMX=1```. Для включения сборки с поддержкой GPIO установите [wiringPi](http://wiringpi.com) и добавьте параметр ```WITH_GPIO=1```.
```
$ git clone --depth=1 https://github.com/pi-kvm/ustreamer
$ git clone --depth=1 https://github.com/pikvm/ustreamer
$ cd ustreamer
$ make
$ ./ustreamer --help

View File

@@ -3,17 +3,17 @@
pkgname=ustreamer
pkgver=0.80
pkgver=1.4
pkgrel=1
pkgdesc="Lightweight and fast MJPG-HTTP streamer"
url="https://github.com/pi-kvm/ustreamer"
url="https://github.com/pikvm/ustreamer"
license=(GPL)
arch=(i686 x86_64 armv6h armv7h)
depends=(libjpeg libevent libutil-linux)
# optional: raspberrypi-firmware for OMX JPEG encoder
# optional: raspberrypi-firmware for OMX encoder
# optional: wiringpi for GPIO support
makedepends=(gcc make)
source=(${pkgname}::"git+https://github.com/pi-kvm/ustreamer#commit=v${pkgver}")
source=(${pkgname}::"git+https://github.com/pikvm/ustreamer#commit=v${pkgver}")
md5sums=(SKIP)

View File

@@ -6,8 +6,8 @@ EAPI=7
inherit git-r3
DESCRIPTION="uStreamer - Lightweight and fast MJPG-HTTP streamer"
HOMEPAGE="https://github.com/pi-kvm/ustreamer"
EGIT_REPO_URI="https://github.com/pi-kvm/ustreamer.git"
HOMEPAGE="https://github.com/pikvm/ustreamer"
EGIT_REPO_URI="https://github.com/pikvm/ustreamer.git"
LICENSE="GPL-3"
SLOT="0"

View File

@@ -6,12 +6,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=ustreamer
PKG_VERSION:=0.80
PKG_VERSION:=1.4
PKG_RELEASE:=1
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/pi-kvm/ustreamer.git
PKG_SOURCE_URL:=https://github.com/pikvm/ustreamer.git
PKG_SOURCE_VERSION:=v$(PKG_VERSION)
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
@@ -26,7 +26,7 @@ define Package/ustreamer
CATEGORY:=Multimedia
TITLE:=uStreamer
DEPENDS:=+libpthread +libjpeg +libv4l +libuuid +libevent2 +libevent2-core +libevent2-extra +libevent2-pthreads
URL:=https://github.com/pi-kvm/ustreamer
URL:=https://github.com/pikvm/ustreamer
endef
define Package/ustreamer/description

View File

@@ -23,5 +23,5 @@
#pragma once
#ifndef VERSION
# define VERSION "0.80"
# define VERSION "1.4"
#endif

View File

@@ -28,11 +28,13 @@
#include <linux/videodev2.h>
#define VIDEO_MIN_WIDTH 320
#define VIDEO_MAX_WIDTH 1920
#define VIDEO_MIN_WIDTH 160
#define VIDEO_MAX_WIDTH 10240
#define VIDEO_MIN_HEIGHT 180
#define VIDEO_MAX_HEIGHT 1200
#define VIDEO_MIN_HEIGHT 120
#define VIDEO_MAX_HEIGHT 4320
#define VIDEO_MAX_FPS 120
#define STANDARD_UNKNOWN V4L2_STD_UNKNOWN
#define STANDARDS_STR "PAL, NTSC, SECAM"

View File

@@ -108,13 +108,13 @@ void encoder_prepare(struct encoder_t *encoder, struct device_t *dev) {
bool cpu_forced = false;
if ((dev->run->format == V4L2_PIX_FMT_MJPEG || dev->run->format == V4L2_PIX_FMT_JPEG) && type != ENCODER_TYPE_HW) {
LOG_INFO("Switching to HW JPEG encoder because the input format is (M)JPEG");
LOG_INFO("Switching to HW encoder because the input format is (M)JPEG");
type = ENCODER_TYPE_HW;
}
if (type == ENCODER_TYPE_HW) {
if (dev->run->format != V4L2_PIX_FMT_MJPEG && dev->run->format != V4L2_PIX_FMT_JPEG) {
LOG_INFO("Switching to CPU JPEG encoder because the input format is not (M)JPEG");
LOG_INFO("Switching to CPU encoder because the input format is not (M)JPEG");
goto use_cpu;
}
@@ -131,16 +131,16 @@ void encoder_prepare(struct encoder_t *encoder, struct device_t *dev) {
encoder->glitched_resolutions[index][0] == dev->run->width
&& encoder->glitched_resolutions[index][1] == dev->run->height
) {
LOG_INFO("Switching to CPU JPEG encoder the resolution %ux%u marked as glitchy for OMX",
LOG_INFO("Switching to CPU encoder the resolution %ux%u marked as glitchy for OMX",
dev->run->width, dev->run->height);
goto use_cpu;
}
}
LOG_DEBUG("Preparing OMX JPEG encoder ...");
LOG_DEBUG("Preparing OMX encoder ...");
if (dev->run->n_workers > OMX_MAX_ENCODERS) {
LOG_INFO("OMX JPEG encoder sets limit for worker threads: %u", OMX_MAX_ENCODERS);
LOG_INFO("OMX encoder sets limit for worker threads: %u", OMX_MAX_ENCODERS);
dev->run->n_workers = OMX_MAX_ENCODERS;
}
@@ -151,14 +151,14 @@ void encoder_prepare(struct encoder_t *encoder, struct device_t *dev) {
// Начинаем с нуля и доинициализируем на следующих заходах при необходимости
for (; encoder->run->n_omxs < dev->run->n_workers; ++encoder->run->n_omxs) {
if ((encoder->run->omxs[encoder->run->n_omxs] = omx_encoder_init()) == NULL) {
LOG_ERROR("Can't initialize OMX JPEG encoder, falling back to CPU");
LOG_ERROR("Can't initialize OMX encoder, falling back to CPU");
goto force_cpu;
}
}
for (unsigned index = 0; index < encoder->run->n_omxs; ++index) {
if (omx_encoder_prepare(encoder->run->omxs[index], dev, quality) < 0) {
LOG_ERROR("Can't prepare OMX JPEG encoder, falling back to CPU");
LOG_ERROR("Can't prepare OMX encoder, falling back to CPU");
goto force_cpu;
}
}

View File

@@ -100,7 +100,7 @@ void cpu_encoder_compress_buffer(struct device_t *dev, unsigned index, unsigned
WRITE_SCANLINES(V4L2_PIX_FMT_UYVY, _jpeg_write_scanlines_uyvy);
WRITE_SCANLINES(V4L2_PIX_FMT_RGB565, _jpeg_write_scanlines_rgb565);
WRITE_SCANLINES(V4L2_PIX_FMT_RGB24, _jpeg_write_scanlines_rgb24);
default: assert(0 && "Unsupported input format for CPU JPEG encoder");
default: assert(0 && "Unsupported input format for CPU encoder");
}
# undef WRITE_SCANLINES

View File

@@ -51,12 +51,12 @@ int hw_encoder_prepare(struct device_t *dev, unsigned quality) {
MEMSET_ZERO(comp);
if (xioctl(dev->run->fd, VIDIOC_G_JPEGCOMP, &comp) < 0) {
LOG_ERROR("Can't query HW JPEG encoder params and set quality (unsupported)");
LOG_ERROR("Device does not support setting of HW encoding quality parameters");
return -1;
}
comp.quality = quality;
if (xioctl(dev->run->fd, VIDIOC_S_JPEGCOMP, &comp) < 0) {
LOG_ERROR("Can't set HW JPEG encoder quality (unsopported)");
LOG_ERROR("Unable to change MJPG quality for JPEG source with HW pass-through encoder");
return -1;
}
return 0;
@@ -64,7 +64,7 @@ int hw_encoder_prepare(struct device_t *dev, unsigned quality) {
void hw_encoder_compress_buffer(struct device_t *dev, unsigned index) {
if (dev->run->format != V4L2_PIX_FMT_MJPEG && dev->run->format != V4L2_PIX_FMT_JPEG) {
assert(0 && "Unsupported input format for HW JPEG encoder");
assert(0 && "Unsupported input format for HW encoder");
}
# define PICTURE(_next) dev->run->pictures[index]._next

View File

@@ -41,7 +41,7 @@ int component_enable_port(OMX_HANDLETYPE *component, OMX_U32 port) {
LOG_DEBUG("Enabling OMX port %u ...", port);
if ((error = OMX_SendCommand(*component, OMX_CommandPortEnable, port, NULL)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't enable OMX port %u", port);
LOG_ERROR_OMX(error, "Can't enable OMX port %u", port);
return -1;
}
return _component_wait_port_changed(component, port, OMX_TRUE);
@@ -52,7 +52,7 @@ int component_disable_port(OMX_HANDLETYPE *component, OMX_U32 port) {
LOG_DEBUG("Disabling OMX port %u ...", port);
if ((error = OMX_SendCommand(*component, OMX_CommandPortDisable, port, NULL)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't disable OMX port %u", port);
LOG_ERROR_OMX(error, "Can't disable OMX port %u", port);
return -1;
}
return _component_wait_port_changed(component, port, OMX_FALSE);
@@ -66,7 +66,7 @@ int component_get_portdef(OMX_HANDLETYPE *component, OMX_PARAM_PORTDEFINITIONTYP
LOG_DEBUG("Fetching OMX port %u definition ...", port);
if ((error = OMX_GetParameter(*component, OMX_IndexParamPortDefinition, portdef)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't get OMX port %u definition", port);
LOG_ERROR_OMX(error, "Can't get OMX port %u definition", port);
return -1;
}
return 0;
@@ -77,7 +77,7 @@ int component_set_portdef(OMX_HANDLETYPE *component, OMX_PARAM_PORTDEFINITIONTYP
LOG_DEBUG("Writing OMX port %u definition ...", portdef->nPortIndex);
if ((error = OMX_SetParameter(*component, OMX_IndexParamPortDefinition, portdef)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't set OMX port %u definition", portdef->nPortIndex);
LOG_ERROR_OMX(error, "Can't set OMX port %u definition", portdef->nPortIndex);
return -1;
}
return 0;
@@ -96,7 +96,7 @@ int component_set_state(OMX_HANDLETYPE *component, OMX_STATETYPE state) {
return _component_wait_state_changed(component, state);
} else if (error == OMX_ErrorInsufficientResources && retries) {
// Иногда железо не инициализируется, хз почему, просто ретраим, со второй попытки сработает
LOG_OMX_ERROR(error, "Can't switch OMX component state to %s, need to retry", state_str);
LOG_ERROR_OMX(error, "Can't switch OMX component state to %s, need to retry", state_str);
retries -= 1;
usleep(8000);
} else {
@@ -104,7 +104,7 @@ int component_set_state(OMX_HANDLETYPE *component, OMX_STATETYPE state) {
}
} while (retries);
LOG_OMX_ERROR(error, "Can't switch OMX component state to %s", state_str);
LOG_ERROR_OMX(error, "Can't switch OMX component state to %s", state_str);
return -1;
}
@@ -119,7 +119,7 @@ static int _component_wait_port_changed(OMX_HANDLETYPE *component, OMX_U32 port,
do {
if ((error = OMX_GetParameter(*component, OMX_IndexParamPortDefinition, &portdef)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't get OMX port %u definition for waiting", port);
LOG_ERROR_OMX(error, "Can't get OMX port %u definition for waiting", port);
return -1;
}
@@ -141,7 +141,7 @@ static int _component_wait_state_changed(OMX_HANDLETYPE *component, OMX_STATETYP
do {
if ((error = OMX_GetState(*component, &state)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Failed to get OMX component state");
LOG_ERROR_OMX(error, "Failed to get OMX component state");
return -1;
}

View File

@@ -93,13 +93,13 @@ struct omx_encoder_t *omx_encoder_init(void) {
LOG_INFO("Initializing OMX ...");
if ((error = OMX_Init()) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't initialize OMX");
LOG_ERROR_OMX(error, "Can't initialize OMX");
goto error;
}
}
_i_omx += 1;
LOG_INFO("Initializing OMX JPEG encoder ...");
LOG_INFO("Initializing OMX encoder ...");
if (vcos_semaphore_create(&omx->handler_lock, "handler_lock", 0) != VCOS_SUCCESS) {
LOG_ERROR("Can't create VCOS semaphore");
@@ -125,7 +125,7 @@ struct omx_encoder_t *omx_encoder_init(void) {
void omx_encoder_destroy(struct omx_encoder_t *omx) {
OMX_ERRORTYPE error;
LOG_INFO("Destroying OMX JPEG encoder ...");
LOG_INFO("Destroying OMX encoder ...");
component_set_state(&omx->encoder, OMX_StateIdle);
_omx_encoder_clear_ports(omx);
@@ -137,7 +137,7 @@ void omx_encoder_destroy(struct omx_encoder_t *omx) {
if (omx->i_encoder) {
if ((error = OMX_FreeHandle(omx->encoder)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't free OMX.broadcom.image_encode");
LOG_ERROR_OMX(error, "Can't free OMX.broadcom.image_encode");
}
}
@@ -184,7 +184,7 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
size_t pos = 0;
if ((error = OMX_FillThisBuffer(omx->encoder, omx->output_buffer)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Failed to request filling of the output buffer on encoder");
LOG_ERROR_OMX(error, "Failed to request filling of the output buffer on encoder");
return -1;
}
@@ -210,7 +210,7 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
}
if ((error = OMX_FillThisBuffer(omx->encoder, omx->output_buffer)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Failed to request filling of the output buffer on encoder");
LOG_ERROR_OMX(error, "Failed to request filling of the output buffer on encoder");
return -1;
}
}
@@ -233,7 +233,7 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
}
if ((error = OMX_EmptyThisBuffer(omx->encoder, omx->input_buffer)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Failed to request emptying of the input buffer on encoder");
LOG_ERROR_OMX(error, "Failed to request emptying of the input buffer on encoder");
return -1;
}
}
@@ -259,7 +259,7 @@ static int _omx_init_component(struct omx_encoder_t *omx) {
LOG_DEBUG("Initializing OMX.broadcom.image_encode ...");
if ((error = OMX_GetHandle(&omx->encoder, "OMX.broadcom.image_encode", omx, &callbacks)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't initialize OMX.broadcom.image_encode");
LOG_ERROR_OMX(error, "Can't initialize OMX.broadcom.image_encode");
return -1;
}
omx->i_encoder = true;
@@ -276,13 +276,13 @@ static int _omx_init_disable_ports(struct omx_encoder_t *omx) {
OMX_INIT_STRUCTURE(ports);
if ((error = OMX_GetParameter(omx->encoder, OMX_IndexParamImageInit, &ports)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't OMX_GetParameter(OMX_IndexParamImageInit)");
LOG_ERROR_OMX(error, "Can't OMX_GetParameter(OMX_IndexParamImageInit)");
return -1;
}
for (unsigned index = 0; index < 4; ++index) {
if ((error = OMX_GetParameter(omx->encoder, types[index], &ports)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't OMX_GetParameter(types[%u])", index);
LOG_ERROR_OMX(error, "Can't OMX_GetParameter(types[%u])", index);
return -1;
}
for (OMX_U32 port = ports.nStartPortNumber; port < ports.nStartPortNumber + ports.nPorts; ++port) {
@@ -329,7 +329,7 @@ static int _omx_setup_input(struct omx_encoder_t *omx, struct device_t *dev) {
// FIXME: RGB24 не работает нормально, нижняя половина экрана зеленая.
// FIXME: Китайский EasyCap тоже не работает, мусор на экране.
// Вероятно обе проблемы вызваны некорректной реализацией OMX на пае.
default: assert(0 && "Unsupported input format for OMX JPEG encoder");
default: assert(0 && "Unsupported input format for OMX encoder");
}
# undef MAP_FORMAT
@@ -349,7 +349,7 @@ static int _omx_setup_input(struct omx_encoder_t *omx, struct device_t *dev) {
omx->i_input_port_enabled = true;
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_ERROR_OMX(error, "Can't allocate OMX JPEG input buffer");
return -1;
}
return 0;
@@ -386,7 +386,7 @@ static int _omx_setup_output(struct omx_encoder_t *omx, unsigned quality) {
exif.bEnabled = OMX_FALSE;
if ((error = OMX_SetParameter(omx->encoder, OMX_IndexParamBrcmDisableEXIF, &exif)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't disable EXIF on OMX JPEG");
LOG_ERROR_OMX(error, "Can't disable EXIF on OMX JPEG");
return -1;
}
}
@@ -399,7 +399,7 @@ static int _omx_setup_output(struct omx_encoder_t *omx, unsigned quality) {
ijg.bEnabled = OMX_TRUE;
if ((error = OMX_SetParameter(omx->encoder, OMX_IndexParamBrcmEnableIJGTableScaling, &ijg)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't set OMX JPEG IJG settings");
LOG_ERROR_OMX(error, "Can't set OMX JPEG IJG settings");
return -1;
}
}
@@ -412,7 +412,7 @@ static int _omx_setup_output(struct omx_encoder_t *omx, unsigned quality) {
qfactor.nQFactor = quality;
if ((error = OMX_SetParameter(omx->encoder, OMX_IndexParamQFactor, &qfactor)) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't set OMX JPEG quality");
LOG_ERROR_OMX(error, "Can't set OMX JPEG quality");
return -1;
}
}
@@ -423,7 +423,7 @@ static int _omx_setup_output(struct omx_encoder_t *omx, unsigned quality) {
omx->i_output_port_enabled = true;
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_ERROR_OMX(error, "Can't allocate OMX JPEG output buffer");
return -1;
}
return 0;
@@ -444,14 +444,14 @@ static int _omx_encoder_clear_ports(struct omx_encoder_t *omx) {
if (omx->input_buffer) {
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_ERROR_OMX(error, "Can't free OMX JPEG input buffer");
// retcode -= 1;
}
omx->input_buffer = NULL;
}
if (omx->output_buffer) {
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_ERROR_OMX(error, "Can't free OMX JPEG output buffer");
// retcode -= 1;
}
omx->output_buffer = NULL;
@@ -469,7 +469,7 @@ static OMX_ERRORTYPE _omx_event_handler(
struct omx_encoder_t *omx = (struct omx_encoder_t *)v_omx;
if (event == OMX_EventError) {
LOG_OMX_ERROR((OMX_ERRORTYPE)data1, "OMX error event received");
LOG_ERROR_OMX((OMX_ERRORTYPE)data1, "OMX error event received");
omx->failed = true;
vcos_semaphore_post(&omx->handler_lock);
}

View File

@@ -22,23 +22,15 @@
#pragma once
#include <stdio.h>
#include <sys/syscall.h>
#include <IL/OMX_IVCommon.h>
#include <IL/OMX_Core.h>
#include <IL/OMX_Image.h>
#include "../../logging.h"
#include "../../tools.h"
#define LOG_OMX_ERROR(_error, _msg, ...) { \
LOGGING_LOCK; \
printf("-- ERROR [%.03Lf tid=%ld] -- " _msg ": %s\n", get_now_monotonic(), \
syscall(SYS_gettid), ##__VA_ARGS__, omx_error_to_string(_error)); \
LOGGING_UNLOCK; \
#define LOG_ERROR_OMX(_error, _msg, ...) { \
LOG_ERROR(_msg ": %s", ##__VA_ARGS__, omx_error_to_string(_error)); \
}

View File

@@ -54,6 +54,6 @@
</ul>
<br>
<hr>
<a href="https://github.com/pi-kvm/ustreamer">Sources &amp; docs</a>
<a href="https://github.com/pikvm/ustreamer">Sources &amp; docs</a>
</body>
</html>

View File

@@ -82,7 +82,7 @@ const char HTML_INDEX_PAGE[] = " \
</ul> \
<br> \
<hr> \
<a href=\"https://github.com/pi-kvm/ustreamer\">Sources &amp; docs</a> \
<a href=\"https://github.com/pikvm/ustreamer\">Sources &amp; docs</a> \
</body> \
</html> \
";

View File

@@ -895,7 +895,7 @@ static bool _expose_new_picture_unsafe(struct http_server_t *server) {
EXPOSED(expose_cmp_time) = EXPOSED(expose_begin_time);
EXPOSED(expose_end_time) = get_now_monotonic();
LOG_VERBOSE("HTTP: exposed new frame; full exposition time = %.06Lf",
LOG_VERBOSE("HTTP: exposed new frame; full exposition time = %.06Lf",
EXPOSED(expose_end_time) - EXPOSED(expose_begin_time));
# undef EXPOSED

View File

@@ -23,8 +23,10 @@
#pragma once
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <assert.h>
@@ -34,18 +36,21 @@
#include "tools.h"
unsigned log_level;
enum {
LOG_LEVEL_INFO,
LOG_LEVEL_PERF,
LOG_LEVEL_VERBOSE,
LOG_LEVEL_DEBUG,
} log_level;
bool log_colored;
pthread_mutex_t log_mutex;
#define LOG_LEVEL_INFO 0
#define LOG_LEVEL_PERF 1
#define LOG_LEVEL_VERBOSE 2
#define LOG_LEVEL_DEBUG 3
#define LOGGING_INIT { \
log_level = LOG_LEVEL_INFO; \
log_colored = isatty(1); \
assert(!pthread_mutex_init(&log_mutex, NULL)); \
}
@@ -55,6 +60,15 @@ pthread_mutex_t log_mutex;
#define LOGGING_UNLOCK assert(!pthread_mutex_unlock(&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 SEP_INFO(_ch) { \
LOGGING_LOCK; \
for (int _i = 0; _i < 80; ++_i) { \
@@ -71,57 +85,64 @@ pthread_mutex_t log_mutex;
} \
}
#define LOG_PRINTF_NOLOCK(_label, _msg, ...) { \
printf("-- " _label " [%.03Lf tid=%d] -- " _msg "\n", get_now_monotonic(), get_thread_id(), ##__VA_ARGS__); \
#define LOG_PRINTF_NOLOCK(_label_color, _label, _msg_color, _msg, ...) { \
if (log_colored) { \
printf(COLOR_GRAY "-- " _label_color _label COLOR_GRAY " [%.03Lf tid=%d]" " -- " COLOR_RESET _msg_color _msg COLOR_RESET, \
get_now_monotonic(), get_thread_id(), ##__VA_ARGS__); \
} else { \
printf("-- " _label " [%.03Lf tid=%d] -- " _msg, \
get_now_monotonic(), get_thread_id(), ##__VA_ARGS__); \
} \
putchar('\n'); \
fflush(stdout); \
}
#define LOG_ERROR(_msg, ...) { \
#define LOG_PRINTF(_label_color, _label, _msg_color, _msg, ...) { \
LOGGING_LOCK; \
LOG_PRINTF_NOLOCK("ERROR", _msg, ##__VA_ARGS__); \
LOG_PRINTF_NOLOCK(_label_color, _label, _msg_color, _msg, ##__VA_ARGS__); \
LOGGING_UNLOCK; \
}
#define LOG_ERROR(_msg, ...) { \
LOG_PRINTF(COLOR_RED, "ERROR", COLOR_RED, _msg, ##__VA_ARGS__); \
}
#define LOG_PERROR(_msg, ...) { \
char _buf[1024] = ""; \
char *_ptr = errno_to_string(_buf, 1024); \
LOGGING_LOCK; \
printf("-- ERROR [%.03Lf tid=%d] -- " _msg ": %s\n", get_now_monotonic(), get_thread_id(), ##__VA_ARGS__, _ptr); \
fflush(stdout); \
LOGGING_UNLOCK; \
LOG_ERROR(_msg ": %s", ##__VA_ARGS__, _ptr); \
}
#define LOG_INFO(_msg, ...) { \
LOGGING_LOCK; \
LOG_PRINTF_NOLOCK("INFO ", _msg, ##__VA_ARGS__); \
LOGGING_UNLOCK; \
LOG_PRINTF(COLOR_GREEN, "INFO ", "", _msg, ##__VA_ARGS__); \
}
#define LOG_INFO_NOLOCK(_msg, ...) { \
LOG_PRINTF_NOLOCK("INFO ", _msg, ##__VA_ARGS__); \
LOG_PRINTF_NOLOCK(COLOR_GREEN, "INFO ", "", _msg, ##__VA_ARGS__); \
}
#define LOG_PERF(_msg, ...) { \
if (log_level >= LOG_LEVEL_PERF) { \
LOGGING_LOCK; \
LOG_PRINTF_NOLOCK("PERF ", _msg, ##__VA_ARGS__); \
LOGGING_UNLOCK; \
LOG_PRINTF(COLOR_CYAN, "PERF ", COLOR_CYAN, _msg, ##__VA_ARGS__); \
} \
}
#define LOG_PERF_FPS(_msg, ...) { \
if (log_level >= LOG_LEVEL_PERF) { \
LOG_PRINTF(COLOR_YELLOW, "PERF ", COLOR_YELLOW, _msg, ##__VA_ARGS__); \
} \
}
#define LOG_VERBOSE(_msg, ...) { \
if (log_level >= LOG_LEVEL_VERBOSE) { \
LOGGING_LOCK; \
LOG_PRINTF_NOLOCK("VERB ", _msg, ##__VA_ARGS__); \
LOGGING_UNLOCK; \
LOG_PRINTF(COLOR_BLUE, "VERB ", COLOR_BLUE, _msg, ##__VA_ARGS__); \
} \
}
#define LOG_DEBUG(_msg, ...) { \
if (log_level >= LOG_LEVEL_DEBUG) { \
LOGGING_LOCK; \
LOG_PRINTF_NOLOCK("DEBUG", _msg, ##__VA_ARGS__); \
LOGGING_UNLOCK; \
LOG_PRINTF(COLOR_GRAY, "DEBUG", COLOR_GRAY, _msg, ##__VA_ARGS__); \
} \
}

View File

@@ -28,15 +28,13 @@
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <limits.h>
#include <signal.h>
#include <getopt.h>
#include <pthread.h>
#include "config.h"
#include "tools.h"
#include "logging.h"
#include "options.h"
#include "device.h"
#include "encoder.h"
#include "stream.h"
@@ -46,422 +44,12 @@
#endif
static const char _SHORT_OPTS[] = "d:i:r:x:y:m:a:f:z:ntb:w:q:c:s:p:U:DM:k:e:lR:hv"
#ifdef WITH_OMX
"g:"
#endif
;
static const struct option _LONG_OPTS[] = {
{"device", required_argument, NULL, 'd'},
{"input", required_argument, NULL, 'i'},
{"resolution", required_argument, NULL, 'r'},
{"width", required_argument, NULL, 'x'},
{"height", required_argument, NULL, 'y'},
{"format", required_argument, NULL, 'm'},
{"tv-standard", required_argument, NULL, 'a'},
{"desired-fps", required_argument, NULL, 'f'},
{"min-frame-size", required_argument, NULL, 'z'},
{"persistent", no_argument, NULL, 'n'},
{"dv-timings", no_argument, NULL, 't'},
{"buffers", required_argument, NULL, 'b'},
{"workers", required_argument, NULL, 'w'},
{"quality", required_argument, NULL, 'q'},
{"encoder", required_argument, NULL, 'c'},
# ifdef WITH_OMX
{"glitched-resolutions", required_argument, NULL, 'g'},
# endif
{"device-timeout", required_argument, NULL, 1000},
{"device-error-delay", required_argument, NULL, 1001},
{"brightness", required_argument, NULL, 2000},
{"brightness-auto", no_argument, NULL, 2001},
{"contrast", required_argument, NULL, 2002},
{"saturation", required_argument, NULL, 2003},
{"hue", required_argument, NULL, 2004},
{"hue-auto", no_argument, NULL, 2005},
{"gamma", required_argument, NULL, 2006},
{"sharpness", required_argument, NULL, 2007},
{"backlight-compensation", required_argument, NULL, 2008},
{"white-balance", required_argument, NULL, 2009},
{"white-balance-auto", no_argument, NULL, 2010},
{"gain", required_argument, NULL, 2011},
{"gain-auto", no_argument, NULL, 2012},
{"host", required_argument, NULL, 's'},
{"port", required_argument, NULL, 'p'},
{"unix", required_argument, NULL, 'U'},
{"unix-rm", no_argument, NULL, 'D'},
{"unix-mode", required_argument, NULL, 'M'},
{"user", required_argument, NULL, 3000},
{"passwd", required_argument, NULL, 3001},
{"static", required_argument, NULL, 3002},
{"blank", required_argument, NULL, 'k'},
{"drop-same-frames", required_argument, NULL, 'e'},
{"slowdown", no_argument, NULL, 'l'},
{"fake-resolution", required_argument, NULL, 'R'},
{"fake-width", required_argument, NULL, 3003},
{"fake-height", required_argument, NULL, 3004},
{"server-timeout", required_argument, NULL, 3005},
#ifdef WITH_GPIO
{"gpio-prog-running", required_argument, NULL, 4000},
{"gpio-stream-online", required_argument, NULL, 4001},
{"gpio-has-http-clients", required_argument, NULL, 4002},
{"gpio-workers-busy-at", required_argument, NULL, 4003},
#endif
{"perf", no_argument, NULL, 5000},
{"verbose", no_argument, NULL, 5001},
{"debug", no_argument, NULL, 5002},
{"log-level", required_argument, NULL, 5010},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
{NULL, 0, NULL, 0},
};
static void _version(bool nl) {
printf(VERSION);
# ifdef WITH_OMX
printf(" + OMX");
# endif
# ifdef WITH_GPIO
printf(" + GPIO");
# endif
if (nl) {
putchar('\n');
}
}
static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_server_t *server) {
printf("\nuStreamer - Lightweight and fast MJPG-HTTP streamer\n");
printf("═══════════════════════════════════════════════════\n\n");
printf("Version: ");
_version(false);
printf("; license: GPLv3\n");
printf("Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com>\n\n");
printf("Capturing options:\n");
printf("══════════════════\n");
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(" -r|--resolution <WxH> ─────────────── Initial image resolution. Default: %ux%u.\n\n", dev->width, dev->height);
printf(" -m|--format <fmt> ─────────────────── Image format.\n");
printf(" Available: %s; default: YUYV.\n\n", FORMATS_STR);
printf(" -a|--tv-standard <std> ────────────── Force TV standard.\n");
printf(" Available: %s; default: disabled.\n\n", STANDARDS_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(" -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");
printf(" -b|--buffers <N> ──────────────────── The number of buffers to receive data from the device.\n");
printf(" Each buffer may processed using an independent thread.\n");
printf(" Default: %u (the number of CPU cores (but not more than 4) + 1).\n\n", dev->n_buffers);
printf(" -w|--workers <N> ──────────────────── The number of worker threads but not more than buffers.\n");
printf(" Default: %u (the number of CPU cores (but not more than 4)).\n\n", dev->n_workers);
printf(" -q|--quality <N> ──────────────────── Set quality of JPEG encoding from 1 to 100 (best). Default: %u.\n\n", encoder->quality);
printf(" -c|--encoder <type> ───────────────── Use specified encoder. It may affect the number of workers.\n\n");
# ifdef WITH_OMX
printf(" -g|--glitched-resolutions <WxH,...> ─ Comma-separated list of resolutions that require forced\n");
# endif
printf(" encoding on CPU instead of OMX. Default: disabled.\n");
printf(" Available: %s; default: CPU.\n\n", ENCODER_TYPES_STR);
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(" after an error (timeout for example). Default: %u.\n\n", dev->error_delay);
printf("Image control options:\n");
printf("══════════════════════\n");
printf(" --brightness <N> ───────────── Set brightness. Default: no change.\n\n");
printf(" --brightness-auto ──────────── Enable automatic brightness control. Default: no change.\n\n");
printf(" --contrast <N> ─────────────── Set contrast. Default: no change.\n\n");
printf(" --saturation <N> ───────────── Set saturation. Default: no change.\n\n");
printf(" --hue <N> ──────────────────── Set hue. Default: no change.\n\n");
printf(" --hue-auto ─────────────────── Enable automatic hue control. Default: no change.\n\n");
printf(" --gamma <N> ────────────────── Set gamma. Default: no change.\n\n");
printf(" --sharpness <N> ────────────── Set sharpness. Default: no change.\n\n");
printf(" --backlight-compensation <N> ─ Set backlight compensation. Default: no change.\n\n");
printf(" --white-balance <N> ────────── Set white balance. Default: no change.\n\n");
printf(" --white-balance-auto ───────── Enable automatic white balance control. Default: no change.\n\n");
printf(" --gain <N> ─────────────────── Set gain. Default: no change.\n\n");
printf(" --gain-auto ────────────────── Enable automatic gain control. Default: no change.\n\n");
printf("HTTP server options:\n");
printf("════════════════════\n");
printf(" -s|--host <address> ──────── Listen on Hostname or IP. Default: %s.\n\n", server->host);
printf(" -p|--port <N> ────────────── Bind to this TCP port. Default: %u.\n\n", server->port);
printf(" -U|--unix <path> ─────────── Bind to UNIX domain socket. Default: disabled.\n\n");
printf(" -D|--unix-rm ─────────────── Try to remove old UNIX socket file before binding. Default: disabled.\n\n");
printf(" -M|--unix-mode <mode> ────── Set UNIX socket file permissions (like 777). Default: disabled.\n\n");
printf(" --user <name> ────────────── HTTP basic auth user. Default: disabled.\n\n");
printf(" --passwd <str> ───────────── HTTP basic auth passwd. Default: empty.\n\n");
printf(" --static <path> ───────────── Path to dir with static files instead of embedded root index page.\n");
printf(" Symlinks are not supported for security reasons. Default: disabled.\n\n");
printf(" -k|--blank <path> ─────────── Path to JPEG file that will be shown when the device is disconnected\n");
printf(" during the streaming. Default: black screen 640x480 with 'NO SIGNAL'.\n\n");
printf(" -e|--drop-same-frames <N> ── Don't send identical frames to clients, but no more than specified number.\n");
printf(" It can significantly reduce the outgoing traffic, but will increase\n");
printf(" the CPU loading. Don't use this option with analog signal sources\n");
printf(" or webcams, it's useless. Default: disabled.\n\n");
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 state. Default: disabled.\n\n");
printf(" --server-timeout <seconds> ─ Timeout for client connections. Default: %u.\n\n", server->timeout);
#ifdef WITH_GPIO
printf("GPIO options:\n");
printf("═════════════\n");
printf(" --gpio-prog-running <pin> ───── Set 1 on GPIO pin while uStreamer is running. Default: disabled.\n\n");
printf(" --gpio-stream-online <pin> ──── Set 1 while streaming. Default: disabled\n\n");
printf(" --gpio-has-http-clients <pin> ─ Set 1 while stream has at least one client. Default: disabled.\n\n");
printf(" --gpio-workers-busy-at <pin> ── Set 1 on (pin + N) while worker with number N has a job.\n");
printf(" The worker's numbering starts from 0. Default: disabled\n\n");
#endif
printf("Misc options:\n");
printf("═════════════\n");
printf(" --log-level <N> ─ Verbosity level of messages from 0 (info) to 3 (debug).\n");
printf(" Enabling debugging messages can slow down the program.\n");
printf(" Available levels: 0 (info), 1 (performance), 2 (verbose), 3 (debug).\n");
printf(" Default: %u.\n\n", log_level);
printf(" --perf ────────── Enable performance messages (same as --log-level=1). Default: disabled.\n\n");
printf(" --verbose ─────── Enable verbose messages and lower (same as --log-level=2). Default: disabled.\n\n");
printf(" --debug ───────── Enable debug messages and lower (same as --log-level=3). Default: disabled.\n\n");
printf(" -h|--help ─────── Print this text and exit.\n\n");
printf(" -v|--version ──── Print version and exit.\n\n");
}
static int _parse_resolution(const char *str, unsigned *width, unsigned *height, bool limited) {
unsigned tmp_width;
unsigned tmp_height;
if (sscanf(str, "%ux%u", &tmp_width, &tmp_height) != 2) {
return -1;
}
if (limited) {
if (tmp_width < VIDEO_MIN_WIDTH || tmp_width > VIDEO_MAX_WIDTH) {
return -2;
}
if (tmp_height < VIDEO_MIN_HEIGHT || tmp_height > VIDEO_MAX_HEIGHT) {
return -3;
}
}
*width = tmp_width;
*height = tmp_height;
return 0;
}
#ifdef WITH_OMX
static int _parse_glitched_resolutions(const char *str, struct encoder_t *encoder) {
char *str_copy;
char *ptr;
unsigned count = 0;
unsigned width;
unsigned height;
assert((str_copy = strdup(str)) != NULL);
ptr = strtok(str_copy, ",;:\n\t ");
while (ptr != NULL) {
if (count >= MAX_GLITCHED_RESOLUTIONS) {
printf("Too big '--glitched-resolutions' list: maxlen=%u\n", MAX_GLITCHED_RESOLUTIONS);
goto error;
}
switch (_parse_resolution(ptr, &width, &height, true)) {
case -1:
printf("Invalid resolution format of '%s' in '--glitched-resolutions=%s\n", ptr, str_copy);
goto error;
case -2:
printf("Invalid width of '%s' in '--glitched-resolutions=%s: min=%u, max=%u\n",
ptr, str_copy, VIDEO_MIN_WIDTH, VIDEO_MIN_HEIGHT);
goto error;
case -3:
printf("Invalid width of '%s' in '--glitched-resolutions=%s: min=%u, max=%u\n",
ptr, str_copy, VIDEO_MIN_WIDTH, VIDEO_MIN_HEIGHT);
goto error;
case 0: break;
default: assert(0 && "Unknown error");
}
encoder->glitched_resolutions[count][0] = width;
encoder->glitched_resolutions[count][1] = height;
count += 1;
ptr = strtok(NULL, ",;:\n\t ");
}
encoder->n_glitched_resolutions = count;
free(str_copy);
return 0;
error:
free(str_copy);
return -1;
}
#endif
static int _parse_options(int argc, char *argv[], struct device_t *dev, struct encoder_t *encoder, struct http_server_t *server) {
# define OPT_SET(_dest, _value) { \
_dest = _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=%u, max=%u\n", _name, optarg, _min, _max); \
return -1; \
} \
_dest = _tmp; \
break; \
}
# define OPT_RESOLUTION(_name, _dest_width, _dest_height, _limited) { \
switch (_parse_resolution(optarg, &_dest_width, &_dest_height, _limited)) { \
case -1: \
printf("Invalid resolution format for '%s=%s'\n", _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); \
return -1; \
case -3: \
printf("Invalid height of '%s=%s': min=%u, max=%u\n", _name, optarg, VIDEO_MIN_HEIGHT, VIDEO_MAX_HEIGHT); \
return -1; \
case 0: break; \
default: assert(0 && "Unknown error"); \
} \
break; \
}
# define OPT_RESOLUTION_OBSOLETE(_name, _replace, _dest, _min, _max) { \
printf("\n=== WARNING! The option '%s' is obsolete; use '%s' instead it ===\n\n", _name, _replace); \
OPT_NUMBER(_name, _dest, _min, _max, 0); \
}
# ifdef WITH_OMX
# define OPT_GLITCHED_RESOLUTIONS { \
if (_parse_glitched_resolutions(optarg, encoder) < 0) { \
return -1; \
} \
break; \
}
# endif
# define OPT_PARSE(_name, _dest, _func, _invalid) { \
if ((_dest = _func(optarg)) == _invalid) { \
printf("Unknown " _name ": %s\n", optarg); \
return -1; \
} \
break; \
}
# define OPT_CTL(_dest) { \
dev->ctl._dest.value_set = true; \
dev->ctl._dest.auto_set = false; \
OPT_NUMBER("--"#_dest, dev->ctl._dest.value, INT_MIN, INT_MAX, 0); \
break; \
}
# define OPT_CTL_AUTO(_dest) { \
dev->ctl._dest.value_set = false; \
dev->ctl._dest.auto_set = true; \
break; \
}
int index;
int ch;
log_level = LOG_LEVEL_INFO;
while ((ch = getopt_long(argc, argv, _SHORT_OPTS, _LONG_OPTS, &index)) >= 0) {
switch (ch) {
case 'd': OPT_SET(dev->path, optarg);
case 'i': OPT_NUMBER("--input", dev->input, 0, 128, 0);
case 'r': OPT_RESOLUTION("--resolution", dev->width, dev->height, true);
case 'x': OPT_RESOLUTION_OBSOLETE("--width", "--resolution", dev->width, VIDEO_MIN_WIDTH, VIDEO_MAX_WIDTH);
case 'y': OPT_RESOLUTION_OBSOLETE("--height", "--resolution", dev->height, VIDEO_MIN_HEIGHT, VIDEO_MAX_HEIGHT);
# pragma GCC diagnostic ignored "-Wsign-compare"
# pragma GCC diagnostic push
case 'm': OPT_PARSE("pixel format", dev->format, device_parse_format, FORMAT_UNKNOWN);
# pragma GCC diagnostic pop
case 'a': OPT_PARSE("TV standard", dev->standard, device_parse_standard, STANDARD_UNKNOWN);
case 'f': OPT_NUMBER("--desired-fps", dev->desired_fps, 0, 30, 0);
case 'z': OPT_NUMBER("--min-frame-size", dev->min_frame_size, 0, 8192, 0);
case 'n': OPT_SET(dev->persistent, true);
case 't': OPT_SET(dev->dv_timings, true);
case 'b': OPT_NUMBER("--buffers", dev->n_buffers, 1, 32, 0);
case 'w': OPT_NUMBER("--workers", dev->n_workers, 1, 32, 0);
case 'q': OPT_NUMBER("--quality", encoder->quality, 1, 100, 0);
case 'c': OPT_PARSE("encoder type", encoder->type, encoder_parse_type, ENCODER_TYPE_UNKNOWN);
# ifdef WITH_OMX
case 'g': OPT_GLITCHED_RESOLUTIONS;
# endif
case 1000: OPT_NUMBER("--device-timeout", dev->timeout, 1, 60, 0);
case 1001: OPT_NUMBER("--device-error-delay", dev->error_delay, 1, 60, 0);
case 2000: OPT_CTL(brightness);
case 2001: OPT_CTL_AUTO(brightness);
case 2002: OPT_CTL(contrast);
case 2003: OPT_CTL(saturation);
case 2004: OPT_CTL(hue);
case 2005: OPT_CTL_AUTO(hue);
case 2006: OPT_CTL(gamma);
case 2007: OPT_CTL(sharpness);
case 2008: OPT_CTL(backlight_compensation);
case 2009: OPT_CTL(white_balance);
case 2010: OPT_CTL_AUTO(white_balance);
case 2011: OPT_CTL(gain);
case 2012: OPT_CTL_AUTO(gain);
case 's': OPT_SET(server->host, optarg);
case 'p': OPT_NUMBER("--port", server->port, 1, 65535, 0);
case 'U': OPT_SET(server->unix_path, optarg);
case 'D': OPT_SET(server->unix_rm, true);
case 'M': OPT_NUMBER("--unix-mode", server->unix_mode, INT_MIN, INT_MAX, 8);
case 3000: OPT_SET(server->user, optarg);
case 3001: OPT_SET(server->passwd, optarg);
case 3002: OPT_SET(server->static_path, optarg);
case 'k': OPT_SET(server->blank_path, optarg);
case 'e': OPT_NUMBER("--drop-same-frames", server->drop_same_frames, 0, 30, 0);
case 'l': OPT_SET(server->slowdown, true);
case 'R': OPT_RESOLUTION("--fake-resolution", server->fake_width, server->fake_height, false);
case 3003: OPT_RESOLUTION_OBSOLETE("--fake-width", "--fake-resolution", server->fake_width, 0, UINT_MAX);
case 3004: OPT_RESOLUTION_OBSOLETE("--fake-height", "--fake-resolution", server->fake_height, 0, UINT_MAX);
case 3005: OPT_NUMBER("--server-timeout", server->timeout, 1, 60, 0);
# ifdef WITH_GPIO
case 4000: OPT_NUMBER("--gpio-prog-running", gpio_pin_prog_running, 0, 256, 0);
case 4001: OPT_NUMBER("--gpio-stream-online", gpio_pin_stream_online, 0, 256, 0);
case 4002: OPT_NUMBER("--gpio-has-http-clients", gpio_pin_has_http_clients, 0, 256, 0);
case 4003: OPT_NUMBER("--gpio-workers-busy-at", gpio_pin_workers_busy_at, 0, 256, 0);
# endif
case 5000: OPT_SET(log_level, LOG_LEVEL_PERF);
case 5001: OPT_SET(log_level, LOG_LEVEL_VERBOSE);
case 5002: OPT_SET(log_level, LOG_LEVEL_DEBUG);
case 5010: OPT_NUMBER("--log-level", log_level, 0, 3, 0);
case 'h': _help(dev, encoder, server); return 1;
case 'v': _version(true); return 1;
case 0: break;
default: _help(dev, encoder, server); return -1;
}
}
# undef OPT_CTL_AUTO
# undef OPT_CTL
# undef OPT_PARSE
# ifdef WITH_OMX
# undef OPT_GLITCHED_RESOLUTIONS
# endif
# undef OPT_RESOLUTION_OBSOLETE
# undef OPT_RESOLUTION
# undef OPT_NUMBER
# undef OPT_SET
return 0;
}
struct main_context_t {
struct _main_context_t {
struct stream_t *stream;
struct http_server_t *server;
};
static struct main_context_t *_ctx;
static struct _main_context_t *_ctx;
static void _block_thread_signals(void) {
sigset_t mask;
@@ -526,7 +114,7 @@ int main(int argc, char *argv[]) {
stream = stream_init(dev, encoder);
server = http_server_init(stream);
if ((exit_code = _parse_options(argc, argv, dev, encoder, server)) == 0) {
if ((exit_code = parse_options(argc, argv, dev, encoder, server)) == 0) {
# ifdef WITH_GPIO
GPIO_INIT_PINOUT;
# endif
@@ -535,7 +123,7 @@ int main(int argc, char *argv[]) {
pthread_t stream_loop_tid;
pthread_t server_loop_tid;
struct main_context_t ctx;
struct _main_context_t ctx;
ctx.stream = stream;
ctx.server = server;

549
src/options.c Normal file
View File

@@ -0,0 +1,549 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
# #
*****************************************************************************/
#include "options.h"
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <signal.h>
#include <getopt.h>
#include <assert.h>
#include "config.h"
#include "tools.h"
#include "logging.h"
#include "device.h"
#include "encoder.h"
#include "stream.h"
#include "http/server.h"
#ifdef WITH_GPIO
# include "gpio.h"
#endif
enum _OPT_VALUES {
_O_DEVICE = 'd',
_O_INPUT = 'i',
_O_RESOLUTION = 'r',
_O_FORMAT = 'm',
_O_TV_STANDARD = 'a',
_O_DESIRED_FPS = 'f',
_O_MIN_FRAME_SIZE = 'z',
_O_PERSISTENT = 'n',
_O_DV_TIMINGS = 't',
_O_BUFFERS = 'b',
_O_WORKERS = 'w',
_O_QUALITY = 'q',
_O_ENCODER = 'c',
#ifdef WITH_OMX
_O_GLITCHED_RESOLUTIONS = 'g',
#endif
_O_HOST = 's',
_O_PORT = 'p',
_O_UNIX = 'U',
_O_UNIX_RM = 'D',
_O_UNIX_MODE = 'M',
_O_BLANK = 'k',
_O_DROP_SAME_FRAMES = 'e',
_O_SLOWDOWN = 'l',
_O_FAKE_RESOLUTION = 'R',
_O_HELP = 'h',
_O_VERSION = 'v',
// Longs only
_O_DEVICE_TIMEOUT = 10000,
_O_DEVICE_ERROR_DELAY,
_O_BRIGHTNESS,
_O_BRIGHTNESS_AUTO,
_O_CONTRAST,
_O_SATURATION,
_O_HUE,
_O_HUE_AUTO,
_O_GAMMA,
_O_SHARPNESS,
_O_BACKLIGHT_COMPENSATION,
_O_WHITE_BALANCE,
_O_WHITE_BALANCE_AUTO,
_O_GAIN,
_O_GAIN_AUTO,
_O_USER,
_O_PASSWD,
_O_STATIC,
_O_SERVER_TIMEOUT,
#ifdef WITH_GPIO
_O_GPIO_PROG_RUNNING,
_O_GPIO_STREAM_ONLINE,
_O_GPIO_HAS_HTTP_CLIENTS,
_O_GPIO_WORKERS_BUSY_AT,
#endif
_O_LOG_LEVEL,
_O_PERF,
_O_VERBOSE,
_O_DEBUG,
_O_FORCE_LOG_COLORS,
_O_NO_LOG_COLORS,
};
static const struct option _LONG_OPTS[] = {
{"device", required_argument, NULL, _O_DEVICE},
{"input", required_argument, NULL, _O_INPUT},
{"resolution", required_argument, NULL, _O_RESOLUTION},
{"format", required_argument, NULL, _O_FORMAT},
{"tv-standard", required_argument, NULL, _O_TV_STANDARD},
{"desired-fps", required_argument, NULL, _O_DESIRED_FPS},
{"min-frame-size", required_argument, NULL, _O_MIN_FRAME_SIZE},
{"persistent", no_argument, NULL, _O_PERSISTENT},
{"dv-timings", no_argument, NULL, _O_DV_TIMINGS},
{"buffers", required_argument, NULL, _O_BUFFERS},
{"workers", required_argument, NULL, _O_WORKERS},
{"quality", required_argument, NULL, _O_QUALITY},
{"encoder", required_argument, NULL, _O_ENCODER},
# ifdef WITH_OMX
{"glitched-resolutions", required_argument, NULL, _O_GLITCHED_RESOLUTIONS},
# endif
{"device-timeout", required_argument, NULL, _O_DEVICE_TIMEOUT},
{"device-error-delay", required_argument, NULL, _O_DEVICE_ERROR_DELAY},
{"brightness", required_argument, NULL, _O_BRIGHTNESS},
{"brightness-auto", no_argument, NULL, _O_BRIGHTNESS_AUTO},
{"contrast", required_argument, NULL, _O_CONTRAST},
{"saturation", required_argument, NULL, _O_SATURATION},
{"hue", required_argument, NULL, _O_HUE},
{"hue-auto", no_argument, NULL, _O_HUE_AUTO},
{"gamma", required_argument, NULL, _O_GAMMA},
{"sharpness", required_argument, NULL, _O_SHARPNESS},
{"backlight-compensation", required_argument, NULL, _O_BACKLIGHT_COMPENSATION},
{"white-balance", required_argument, NULL, _O_WHITE_BALANCE},
{"white-balance-auto", no_argument, NULL, _O_WHITE_BALANCE_AUTO},
{"gain", required_argument, NULL, _O_GAIN},
{"gain-auto", no_argument, NULL, _O_GAIN_AUTO},
{"host", required_argument, NULL, _O_HOST},
{"port", required_argument, NULL, _O_PORT},
{"unix", required_argument, NULL, _O_UNIX},
{"unix-rm", no_argument, NULL, _O_UNIX_RM},
{"unix-mode", required_argument, NULL, _O_UNIX_MODE},
{"user", required_argument, NULL, _O_USER},
{"passwd", required_argument, NULL, _O_PASSWD},
{"static", required_argument, NULL, _O_STATIC},
{"blank", required_argument, NULL, _O_BLANK},
{"drop-same-frames", required_argument, NULL, _O_DROP_SAME_FRAMES},
{"slowdown", no_argument, NULL, _O_SLOWDOWN},
{"fake-resolution", required_argument, NULL, _O_FAKE_RESOLUTION},
{"server-timeout", required_argument, NULL, _O_SERVER_TIMEOUT},
#ifdef WITH_GPIO
{"gpio-prog-running", required_argument, NULL, _O_GPIO_PROG_RUNNING},
{"gpio-stream-online", required_argument, NULL, _O_GPIO_STREAM_ONLINE},
{"gpio-has-http-clients", required_argument, NULL, _O_GPIO_HAS_HTTP_CLIENTS},
{"gpio-workers-busy-at", required_argument, NULL, _O_GPIO_WORKERS_BUSY_AT},
#endif
{"log-level", required_argument, NULL, _O_LOG_LEVEL},
{"perf", no_argument, NULL, _O_PERF},
{"verbose", no_argument, NULL, _O_VERBOSE},
{"debug", no_argument, NULL, _O_DEBUG},
{"force-log-colors", no_argument, NULL, _O_FORCE_LOG_COLORS},
{"no-log-colors", no_argument, NULL, _O_NO_LOG_COLORS},
{"help", no_argument, NULL, _O_HELP},
{"version", no_argument, NULL, _O_VERSION},
{NULL, 0, NULL, 0},
};
static int _parse_resolution(const char *str, unsigned *width, unsigned *height, bool limited);
#ifdef WITH_OMX
static int _parse_glitched_resolutions(const char *str, struct encoder_t *encoder);
#endif
static void _version(bool nl);
static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_server_t *server);
int parse_options(int argc, char *argv[], struct device_t *dev, struct encoder_t *encoder, struct http_server_t *server) {
# define OPT_SET(_dest, _value) { \
_dest = _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=%u, max=%u\n", _name, optarg, _min, _max); \
return -1; \
} \
_dest = _tmp; \
break; \
}
# define OPT_RESOLUTION(_name, _dest_width, _dest_height, _limited) { \
switch (_parse_resolution(optarg, &_dest_width, &_dest_height, _limited)) { \
case -1: \
printf("Invalid resolution format for '%s=%s'\n", _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); \
return -1; \
case -3: \
printf("Invalid height of '%s=%s': min=%u, max=%u\n", _name, optarg, VIDEO_MIN_HEIGHT, VIDEO_MAX_HEIGHT); \
return -1; \
case 0: break; \
default: assert(0 && "Unknown error"); \
} \
break; \
}
# ifdef WITH_OMX
# define OPT_GLITCHED_RESOLUTIONS { \
if (_parse_glitched_resolutions(optarg, encoder) < 0) { \
return -1; \
} \
break; \
}
# endif
# define OPT_PARSE(_name, _dest, _func, _invalid, _available) { \
if ((_dest = _func(optarg)) == _invalid) { \
printf("Unknown " _name ": %s; available: %s\n", optarg, _available); \
return -1; \
} \
break; \
}
# define OPT_CTL(_dest) { \
dev->ctl._dest.value_set = true; \
dev->ctl._dest.auto_set = false; \
OPT_NUMBER("--"#_dest, dev->ctl._dest.value, INT_MIN, INT_MAX, 0); \
break; \
}
# define OPT_CTL_AUTO(_dest) { \
dev->ctl._dest.value_set = false; \
dev->ctl._dest.auto_set = true; \
break; \
}
int ch;
int short_index;
int opt_index;
char short_opts[1024] = {0};
for (short_index = 0, opt_index = 0; _LONG_OPTS[opt_index].name != NULL; ++opt_index) {
if (isalpha(_LONG_OPTS[opt_index].val)) {
short_opts[short_index] = _LONG_OPTS[opt_index].val;
++short_index;
if (_LONG_OPTS[opt_index].has_arg == required_argument) {
short_opts[short_index] = ':';
++short_index;
}
}
}
while ((ch = getopt_long(argc, argv, 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);
# 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);
# pragma GCC diagnostic pop
case _O_TV_STANDARD: OPT_PARSE("TV standard", dev->standard, device_parse_standard, STANDARD_UNKNOWN, STANDARDS_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_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);
case _O_WORKERS: OPT_NUMBER("--workers", dev->n_workers, 1, 32, 0);
case _O_QUALITY: OPT_NUMBER("--quality", encoder->quality, 1, 100, 0);
case _O_ENCODER: OPT_PARSE("encoder type", encoder->type, encoder_parse_type, ENCODER_TYPE_UNKNOWN, ENCODER_TYPES_STR);
# ifdef WITH_OMX
case _O_GLITCHED_RESOLUTIONS: OPT_GLITCHED_RESOLUTIONS;
# endif
case _O_DEVICE_TIMEOUT: OPT_NUMBER("--device-timeout", dev->timeout, 1, 60, 0);
case _O_DEVICE_ERROR_DELAY: OPT_NUMBER("--device-error-delay", dev->error_delay, 1, 60, 0);
case _O_BRIGHTNESS: OPT_CTL(brightness);
case _O_BRIGHTNESS_AUTO: OPT_CTL_AUTO(brightness);
case _O_CONTRAST: OPT_CTL(contrast);
case _O_SATURATION: OPT_CTL(saturation);
case _O_HUE: OPT_CTL(hue);
case _O_HUE_AUTO: OPT_CTL_AUTO(hue);
case _O_GAMMA: OPT_CTL(gamma);
case _O_SHARPNESS: OPT_CTL(sharpness);
case _O_BACKLIGHT_COMPENSATION: OPT_CTL(backlight_compensation);
case _O_WHITE_BALANCE: OPT_CTL(white_balance);
case _O_WHITE_BALANCE_AUTO: OPT_CTL_AUTO(white_balance);
case _O_GAIN: OPT_CTL(gain);
case _O_GAIN_AUTO: OPT_CTL_AUTO(gain);
case _O_HOST: OPT_SET(server->host, optarg);
case _O_PORT: OPT_NUMBER("--port", server->port, 1, 65535, 0);
case _O_UNIX: OPT_SET(server->unix_path, optarg);
case _O_UNIX_RM: OPT_SET(server->unix_rm, true);
case _O_UNIX_MODE: OPT_NUMBER("--unix-mode", server->unix_mode, INT_MIN, INT_MAX, 8);
case _O_USER: OPT_SET(server->user, optarg);
case _O_PASSWD: OPT_SET(server->passwd, optarg);
case _O_STATIC: OPT_SET(server->static_path, optarg);
case _O_BLANK: OPT_SET(server->blank_path, optarg);
case _O_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_SERVER_TIMEOUT: OPT_NUMBER("--server-timeout", server->timeout, 1, 60, 0);
# ifdef WITH_GPIO
case _O_GPIO_PROG_RUNNING: OPT_NUMBER("--gpio-prog-running", gpio_pin_prog_running, 0, 256, 0);
case _O_GPIO_STREAM_ONLINE: OPT_NUMBER("--gpio-stream-online", gpio_pin_stream_online, 0, 256, 0);
case _O_GPIO_HAS_HTTP_CLIENTS: OPT_NUMBER("--gpio-has-http-clients", gpio_pin_has_http_clients, 0, 256, 0);
case _O_GPIO_WORKERS_BUSY_AT: OPT_NUMBER("--gpio-workers-busy-at", gpio_pin_workers_busy_at, 0, 256, 0);
# endif
case _O_LOG_LEVEL: OPT_NUMBER("--log-level", log_level, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, 0);
case _O_PERF: OPT_SET(log_level, LOG_LEVEL_PERF);
case _O_VERBOSE: OPT_SET(log_level, LOG_LEVEL_VERBOSE);
case _O_DEBUG: OPT_SET(log_level, LOG_LEVEL_DEBUG);
case _O_FORCE_LOG_COLORS: OPT_SET(log_colored, true);
case _O_NO_LOG_COLORS: OPT_SET(log_colored, false);
case _O_HELP: _help(dev, encoder, server); return 1;
case _O_VERSION: _version(true); return 1;
case 0: break;
default: _help(dev, encoder, server); return -1;
}
}
# undef OPT_CTL_AUTO
# undef OPT_CTL
# undef OPT_PARSE
# ifdef WITH_OMX
# undef OPT_GLITCHED_RESOLUTIONS
# endif
# undef OPT_RESOLUTION
# undef OPT_NUMBER
# undef OPT_SET
return 0;
}
static int _parse_resolution(const char *str, unsigned *width, unsigned *height, bool limited) {
unsigned tmp_width;
unsigned tmp_height;
if (sscanf(str, "%ux%u", &tmp_width, &tmp_height) != 2) {
return -1;
}
if (limited) {
if (tmp_width < VIDEO_MIN_WIDTH || tmp_width > VIDEO_MAX_WIDTH) {
return -2;
}
if (tmp_height < VIDEO_MIN_HEIGHT || tmp_height > VIDEO_MAX_HEIGHT) {
return -3;
}
}
*width = tmp_width;
*height = tmp_height;
return 0;
}
#ifdef WITH_OMX
static int _parse_glitched_resolutions(const char *str, struct encoder_t *encoder) {
char *str_copy;
char *ptr;
unsigned count = 0;
unsigned width;
unsigned height;
assert((str_copy = strdup(str)) != NULL);
ptr = strtok(str_copy, ",;:\n\t ");
while (ptr != NULL) {
if (count >= MAX_GLITCHED_RESOLUTIONS) {
printf("Too big '--glitched-resolutions' list: maxlen=%u\n", MAX_GLITCHED_RESOLUTIONS);
goto error;
}
switch (_parse_resolution(ptr, &width, &height, true)) {
case -1:
printf("Invalid resolution format of '%s' in '--glitched-resolutions=%s\n", ptr, str_copy);
goto error;
case -2:
printf("Invalid width of '%s' in '--glitched-resolutions=%s: min=%u, max=%u\n",
ptr, str_copy, VIDEO_MIN_WIDTH, VIDEO_MIN_HEIGHT);
goto error;
case -3:
printf("Invalid width of '%s' in '--glitched-resolutions=%s: min=%u, max=%u\n",
ptr, str_copy, VIDEO_MIN_WIDTH, VIDEO_MIN_HEIGHT);
goto error;
case 0: break;
default: assert(0 && "Unknown error");
}
encoder->glitched_resolutions[count][0] = width;
encoder->glitched_resolutions[count][1] = height;
count += 1;
ptr = strtok(NULL, ",;:\n\t ");
}
encoder->n_glitched_resolutions = count;
free(str_copy);
return 0;
error:
free(str_copy);
return -1;
}
#endif
static void _version(bool nl) {
printf(VERSION);
# ifdef WITH_OMX
printf(" + OMX");
# endif
# ifdef WITH_GPIO
printf(" + GPIO");
# endif
if (nl) {
putchar('\n');
}
}
static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_server_t *server) {
printf("\nuStreamer - Lightweight and fast MJPG-HTTP streamer\n");
printf("═══════════════════════════════════════════════════\n\n");
printf("Version: ");
_version(false);
printf("; license: GPLv3\n");
printf("Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com>\n\n");
printf("Capturing options:\n");
printf("══════════════════\n");
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(" -r|--resolution <WxH> ─────────────── Initial image resolution. Default: %ux%u.\n\n", dev->width, dev->height);
printf(" -m|--format <fmt> ─────────────────── Image format.\n");
printf(" Available: %s; default: YUYV.\n\n", FORMATS_STR);
printf(" -a|--tv-standard <std> ────────────── Force TV standard.\n");
printf(" Available: %s; default: disabled.\n\n", STANDARDS_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(" -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");
printf(" -b|--buffers <N> ──────────────────── The number of buffers to receive data from the device.\n");
printf(" Each buffer may processed using an independent thread.\n");
printf(" Default: %u (the number of CPU cores (but not more than 4) + 1).\n\n", dev->n_buffers);
printf(" -w|--workers <N> ──────────────────── The number of worker threads but not more than buffers.\n");
printf(" Default: %u (the number of CPU cores (but not more than 4)).\n\n", dev->n_workers);
printf(" -q|--quality <N> ──────────────────── Set quality of JPEG encoding from 1 to 100 (best). Default: %u.\n", encoder->quality);
printf(" Note: If HW encoding is used (JPEG source format selected),\n");
printf(" this parameter attempts to configure the camera\n");
printf(" or capture device hardware's internal encoder.\n");
printf(" It does not re-encode MJPG to MJPG to change the quality level\n");
printf(" for sources that already output MJPG.\n\n");
printf(" -c|--encoder <type> ───────────────── Use specified encoder. It may affect the number of workers.\n");
printf(" Available:\n");
printf(" * CPU ─ Software MJPG encoding (default);\n");
# ifdef WITH_OMX
printf(" * OMX ─ GPU hardware accelerated MJPG encoding with OpenMax;\n");
# endif
printf(" * HW ── Use pre-encoded MJPG frames directly from camera hardware.\n\n");
# ifdef WITH_OMX
printf(" -g|--glitched-resolutions <WxH,...> ─ Comma-separated list of resolutions that require forced\n");
printf(" encoding on CPU instead of OMX. Default: disabled.\n\n");
# endif
printf(" --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(" after an error (timeout for example). Default: %u.\n\n", dev->error_delay);
printf("Image control options:\n");
printf("══════════════════════\n");
printf(" --brightness <N> ───────────── Set brightness. Default: no change.\n\n");
printf(" --brightness-auto ──────────── Enable automatic brightness control. Default: no change.\n\n");
printf(" --contrast <N> ─────────────── Set contrast. Default: no change.\n\n");
printf(" --saturation <N> ───────────── Set saturation. Default: no change.\n\n");
printf(" --hue <N> ──────────────────── Set hue. Default: no change.\n\n");
printf(" --hue-auto ─────────────────── Enable automatic hue control. Default: no change.\n\n");
printf(" --gamma <N> ────────────────── Set gamma. Default: no change.\n\n");
printf(" --sharpness <N> ────────────── Set sharpness. Default: no change.\n\n");
printf(" --backlight-compensation <N> ─ Set backlight compensation. Default: no change.\n\n");
printf(" --white-balance <N> ────────── Set white balance. Default: no change.\n\n");
printf(" --white-balance-auto ───────── Enable automatic white balance control. Default: no change.\n\n");
printf(" --gain <N> ─────────────────── Set gain. Default: no change.\n\n");
printf(" --gain-auto ────────────────── Enable automatic gain control. Default: no change.\n\n");
printf("HTTP server options:\n");
printf("════════════════════\n");
printf(" -s|--host <address> ──────── Listen on Hostname or IP. Default: %s.\n\n", server->host);
printf(" -p|--port <N> ────────────── Bind to this TCP port. Default: %u.\n\n", server->port);
printf(" -U|--unix <path> ─────────── Bind to UNIX domain socket. Default: disabled.\n\n");
printf(" -D|--unix-rm ─────────────── Try to remove old UNIX socket file before binding. Default: disabled.\n\n");
printf(" -M|--unix-mode <mode> ────── Set UNIX socket file permissions (like 777). Default: disabled.\n\n");
printf(" --user <name> ────────────── HTTP basic auth user. Default: disabled.\n\n");
printf(" --passwd <str> ───────────── HTTP basic auth passwd. Default: empty.\n\n");
printf(" --static <path> ───────────── Path to dir with static files instead of embedded root index page.\n");
printf(" Symlinks are not supported for security reasons. Default: disabled.\n\n");
printf(" -k|--blank <path> ─────────── Path to JPEG file that will be shown when the device is disconnected\n");
printf(" during the streaming. Default: black screen 640x480 with 'NO SIGNAL'.\n\n");
printf(" -e|--drop-same-frames <N> ── Don't send identical frames to clients, but no more than specified number.\n");
printf(" It can significantly reduce the outgoing traffic, but will increase\n");
printf(" the CPU loading. Don't use this option with analog signal sources\n");
printf(" or webcams, it's useless. Default: disabled.\n\n");
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 state. Default: disabled.\n\n");
printf(" --server-timeout <seconds> ─ Timeout for client connections. Default: %u.\n\n", server->timeout);
#ifdef WITH_GPIO
printf("GPIO options:\n");
printf("═════════════\n");
printf(" --gpio-prog-running <pin> ───── Set 1 on GPIO pin while uStreamer is running. Default: disabled.\n\n");
printf(" --gpio-stream-online <pin> ──── Set 1 while streaming. Default: disabled\n\n");
printf(" --gpio-has-http-clients <pin> ─ Set 1 while stream has at least one client. Default: disabled.\n\n");
printf(" --gpio-workers-busy-at <pin> ── Set 1 on (pin + N) while worker with number N has a job.\n");
printf(" The worker's numbering starts from 0. Default: disabled\n\n");
#endif
printf("Logging options:\n");
printf("════════════════\n");
printf(" --log-level <N> ──── Verbosity level of messages from 0 (info) to 3 (debug).\n");
printf(" Enabling debugging messages can slow down the program.\n");
printf(" Available levels: 0 (info), 1 (performance), 2 (verbose), 3 (debug).\n");
printf(" Default: %u.\n\n", log_level);
printf(" --perf ───────────── Enable performance messages (same as --log-level=1). Default: disabled.\n\n");
printf(" --verbose ────────── Enable verbose messages and lower (same as --log-level=2). Default: disabled.\n\n");
printf(" --debug ──────────── Enable debug messages and lower (same as --log-level=3). Default: disabled.\n\n");
printf(" --force-log-colors ─ Force color logging. Default: colored if stdout is a TTY.\n\n");
printf(" --no-log-colors ──── Disable color logging. Default: ditto.\n\n");
printf("Help options:\n");
printf("═════════════\n");
printf(" -h|--help ─────── Print this text and exit.\n\n");
printf(" -v|--version ──── Print version and exit.\n\n");
}

30
src/options.h Normal file
View File

@@ -0,0 +1,30 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
# #
*****************************************************************************/
#pragma once
#include "device.h"
#include "encoder.h"
#include "http/server.h"
int parse_options(int argc, char *argv[], struct device_t *dev, struct encoder_t *encoder, struct http_server_t *server);

View File

@@ -240,7 +240,7 @@ void stream_loop(struct stream_t *stream) {
captured_fps = captured_fps_accum;
captured_fps_accum = 0;
captured_fps_second = now_second;
LOG_PERF("A new second has come; captured_fps=%u", captured_fps);
LOG_PERF_FPS("A new second has come; captured_fps=%u", captured_fps);
}
captured_fps_accum += 1;