Compare commits

...

24 Commits
v6.30 ... v6.37

Author SHA1 Message Date
Maxim Devaev
0e521ad0c6 Bump version: 6.36 → 6.37 2025-05-27 19:42:34 +03:00
Maxim Devaev
620a0ec847 Fixed #290: improved blank diagnostics 2025-05-27 19:30:07 +03:00
Maxim Devaev
7a1d4816ed frametext: more improvements 2025-05-26 22:34:19 +03:00
Maxim Devaev
aec8431024 verbose on-screen error messages 2025-05-26 20:06:06 +03:00
Maxim Devaev
5b18e29555 frametext: improved proportions 2025-05-26 20:04:35 +03:00
Maxim Devaev
2717248581 Bump version: 6.35 → 6.36 2025-03-27 04:38:28 +02:00
Maxim Devaev
afd305e87d v4p: fix for some DOS device 2025-03-27 04:36:35 +02:00
Maxim Devaev
e3d8132237 fixed --format-swap-rgb 2025-03-27 04:33:17 +02:00
Maxim Devaev
1f32e875c3 openwrt: +libatomic 2025-03-09 06:45:37 +02:00
Maxim Devaev
2e88fb9294 Bump version: 6.34 → 6.35 2025-03-08 20:16:11 +02:00
Maxim Devaev
d68f8e6d86 added missing formats 2025-03-08 20:14:17 +02:00
gudvinr
b380beba6d Add GREY pixelformat (#171)
Fixes #170

Monochrome cameras send only Y component of YUV image
2025-03-08 20:01:49 +02:00
Maxim Devaev
3a06a484ce Bump version: 6.33 → 6.34 2025-03-05 17:34:18 +02:00
Maxim Devaev
0307d3bdb6 Issue #287: Don't add -latomic on FreeBSD 2025-03-05 17:32:01 +02:00
Maxim Devaev
f2dd9c3c5a pikvm/ustreamer#306: Added ifdef for linux 2025-02-28 22:24:31 +02:00
Maxim Devaev
4e3f873f0d Added pkg-config to README 2025-02-27 22:21:23 +02:00
Maxim Devaev
029440cf82 Bump version: 6.32 → 6.33 2025-02-24 18:47:04 +02:00
Maxim Devaev
df74f5cf18 janus: added default ICE url 2025-02-24 18:41:40 +02:00
Maxim Devaev
97494c3531 janus: replaces STUN variables to ICE_URL 2025-02-24 18:21:46 +02:00
Maxim Devaev
71544880d1 janus: changed env prefix 2025-02-24 17:16:24 +02:00
Maxim Devaev
83127e58ff Bump version: 6.31 → 6.32 2025-02-24 05:19:22 +02:00
Maxim Devaev
604a8f7cb4 janus: STUN env 2025-02-24 05:17:32 +02:00
Maxim Devaev
602c1747d5 Bump version: 6.30 → 6.31 2025-02-08 15:46:31 +02:00
Maxim Devaev
a2b8b35070 improved build system 2025-02-08 15:44:40 +02:00
27 changed files with 256 additions and 105 deletions

View File

@@ -1,7 +1,7 @@
[bumpversion]
commit = True
tag = True
current_version = 6.30
current_version = 6.37
parse = (?P<major>\d+)\.(?P<minor>\d+)
serialize =
{major}.{minor}

View File

@@ -1,4 +1,3 @@
include lib.mk
-include config.mk
@@ -24,6 +23,18 @@ WITH_PTHREAD_NP ?= 1
WITH_SETPROCTITLE ?= 1
WITH_PDEATHSIG ?= 1
define optbool
$(filter $(shell echo $(1) | tr A-Z a-z), yes on 1)
endef
MK_WITH_PYTHON = $(call optbool,$(WITH_PYTHON))
MK_WITH_JANUS = $(call optbool,$(WITH_JANUS))
MK_WITH_V4P = $(call optbool,$(WITH_V4P))
MK_WITH_GPIO = $(call optbool,$(WITH_GPIO))
MK_WITH_SYSTEMD = $(call optbool,$(WITH_SYSTEMD))
MK_WITH_PTHREAD_NP = $(call optbool,$(WITH_PTHREAD_NP))
MK_WITH_SETPROCTITLE = $(call optbool,$(WITH_SETPROCTITLE))
MK_WITH_PDEATHSIG = $(call optbool,$(WITH_PDEATHSIG))
export
_LINTERS_IMAGE ?= ustreamer-linters
@@ -44,10 +55,10 @@ endif
# =====
all:
+ $(MAKE) apps
ifneq ($(call optbool,$(WITH_PYTHON)),)
ifneq ($(MK_WITH_PYTHON),)
+ $(MAKE) python
endif
ifneq ($(call optbool,$(WITH_JANUS)),)
ifneq ($(MK_WITH_JANUS),)
+ $(MAKE) janus
endif
@@ -71,10 +82,10 @@ janus:
install: all
$(MAKE) -C src install
ifneq ($(call optbool,$(WITH_PYTHON)),)
ifneq ($(MK_WITH_PYTHON),)
$(MAKE) -C python install
endif
ifneq ($(call optbool,$(WITH_JANUS)),)
ifneq ($(MK_WITH_JANUS),)
$(MAKE) -C janus install
endif
mkdir -p $(R_DESTDIR)$(MANPREFIX)/man1

View File

@@ -11,7 +11,7 @@
|----------|---------------|-------------------|
| Multithreaded JPEG encoding | ✔ | ✘ |
| Hardware image encoding<br>on Raspberry Pi | ✔ | ✘ |
| Behavior when the device<br>is disconnected while streaming | ✔ Shows a black screen<br>with ```NO SIGNAL``` on it<br>until reconnected | ✘ Stops the streaming <sup>1</sup> |
| Behavior when the device<br>is disconnected while streaming | ✔ Shows a black screen<br>with ```NO LIVE VIDEO``` on it<br>until reconnected | ✘ Stops the streaming <sup>1</sup> |
| [DV-timings](https://linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/v4l/dv-timings.html) support -<br>the ability to change resolution<br>on the fly by source signal | ✔ | ☹ Partially yes <sup>1</sup> |
| Option to skip frames when streaming<br>static images by HTTP to save the traffic | ✔ <sup>2</sup> | ✘ |
| Streaming via UNIX domain socket | ✔ | ✘ |
@@ -44,7 +44,7 @@ You need to download the µStreamer onto your system and build it from the sourc
* FreeBSD port: https://www.freshports.org/multimedia/ustreamer.
### Preconditions
You'll need ```make```, ```gcc```, ```libevent``` with ```pthreads``` support, ```libjpeg9```/```libjpeg-turbo``` and ```libbsd``` (only for Linux).
You'll need ```make```, ```gcc```, ```pkg-config```, ```libevent``` with ```pthreads``` support, ```libjpeg9```/```libjpeg-turbo``` and ```libbsd``` (only for Linux).
* Arch: `sudo pacman -S libevent libjpeg-turbo libutil-linux libbsd`.
* Raspberry OS Bullseye: `sudo apt install libevent-dev libjpeg62-turbo libbsd-dev`. Add `libgpiod-dev` for `WITH_GPIO=1` and `libsystemd-dev` for `WITH_SYSTEMD=1` and `libasound2-dev libspeex-dev libspeexdsp-dev libopus-dev` for `WITH_JANUS=1`.

View File

@@ -1,7 +1,3 @@
include ../lib.mk
# =====
R_DESTDIR ?=
PREFIX ?= /usr/local
@@ -22,7 +18,12 @@ _SRCS = $(shell ls src/uslibs/*.c src/*.c)
_BUILD = build
ifneq ($(call optbool,$(WITH_PTHREAD_NP)),)
# =====
ifneq ($(shell sh -c 'uname 2>/dev/null || echo Unknown'),FreeBSD)
override _LDFLAGS += -latomic
endif
ifneq ($(MK_WITH_PTHREAD_NP),)
override _CFLAGS += -DWITH_PTHREAD_NP
endif

View File

@@ -59,6 +59,9 @@
#include "memsinkfd.h"
#include "config.h"
static const char *const default_ice_url = "stun:stun.l.google.com:19302";
static us_config_s *_g_config = NULL;
static const useconds_t _g_watchers_polling = 100000;
@@ -669,10 +672,12 @@ static struct janus_plugin_result *_plugin_handle_message(
}
} else if (!strcmp(request_str, "features")) {
const char *const ice_url = getenv("JANUS_USTREAMER_WEB_ICE_URL");
json_t *const features = json_pack(
"{sbsb}",
"{s:b, s:b, s:{s:s?}}",
"audio", (_g_rtpa != NULL),
"mic", (_g_rtpa != NULL && _g_config->aplay_dev_name != NULL)
"mic", (_g_rtpa != NULL && _g_config->aplay_dev_name != NULL),
"ice", "url", (ice_url != NULL ? ice_url : default_ice_url)
);
PUSH_STATUS("features", features, NULL);
json_decref(features);

3
lib.mk
View File

@@ -1,3 +0,0 @@
define optbool
$(filter $(shell echo $(1) | tr A-Z a-z), yes on 1)
endef

View File

@@ -1,6 +1,6 @@
.\" Manpage for ustreamer-dump.
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
.TH USTREAMER-DUMP 1 "version 6.30" "January 2021"
.TH USTREAMER-DUMP 1 "version 6.37" "January 2021"
.SH NAME
ustreamer-dump \- Dump uStreamer's memory sink to file

View File

@@ -1,6 +1,6 @@
.\" Manpage for ustreamer.
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
.TH USTREAMER 1 "version 6.30" "November 2020"
.TH USTREAMER 1 "version 6.37" "November 2020"
.SH NAME
ustreamer \- stream MJPEG video from any V4L2 device to the network
@@ -52,7 +52,7 @@ Initial image resolution. Default: 640x480.
.TP
.BR \-m\ \fIfmt ", " \-\-format\ \fIfmt
Image format.
Available: YUYV, YVYU, UYVY, YUV420, YVU420, RGB565, RGB24, JPEG; default: YUYV.
Available: YUYV, YVYU, UYVY, YUV420, YVU420, RGB565, RGB24, GREY, MJPEG, JPEG; default: YUYV.
.TP
.BR \-a\ \fIstd ", " \-\-tv\-standard\ \fIstd
Force TV standard.

View File

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

View File

@@ -6,7 +6,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=ustreamer
PKG_VERSION:=6.30
PKG_VERSION:=6.37
PKG_RELEASE:=1
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
@@ -25,7 +25,7 @@ define Package/ustreamer
SECTION:=multimedia
CATEGORY:=Multimedia
TITLE:=uStreamer
DEPENDS:=+libpthread +libjpeg +libv4l +libbsd +libevent2 +libevent2-core +libevent2-extra +libevent2-pthreads
DEPENDS:=+libatomic +libpthread +libjpeg +libv4l +libbsd +libevent2 +libevent2-core +libevent2-extra +libevent2-pthreads
URL:=https://github.com/pikvm/ustreamer
endef

View File

@@ -1,5 +1,3 @@
-include ../config.mk
R_DESTDIR ?=
PREFIX ?= /usr/local

View File

@@ -16,20 +16,12 @@ def _find_sources() -> list[str]:
def _find_flags() -> dict[str, bool]:
return {
key: bool(int(value))
key[3:]: (value.strip().lower() in ["true", "on", "1"])
for (key, value) in sorted(os.environ.items())
if key.startswith("WITH_")
if key.startswith("MK_WITH_")
}
def _make_d_flags(flags: dict[str, bool]) -> list[str]:
return [
f"-D{key}"
for (key, value) in flags.items()
if value
]
def _make_d_features(flags: dict[str, bool]) -> str:
features = " ".join([
f"{key}={int(value)}"
@@ -42,7 +34,7 @@ def main() -> None:
flags = _find_flags()
setup(
name="ustreamer",
version="6.30",
version="6.37",
description="uStreamer tools",
author="Maxim Devaev",
author_email="mdevaev@gmail.com",
@@ -54,7 +46,6 @@ def main() -> None:
extra_compile_args=[
"-std=c17", "-D_GNU_SOURCE",
_make_d_features(flags),
*_make_d_flags(flags),
],
undef_macros=["NDEBUG"],
sources=_find_sources(),

View File

@@ -1,7 +1,3 @@
include ../lib.mk
# =====
R_DESTDIR ?=
PREFIX ?= /usr/local
@@ -18,9 +14,9 @@ _V4P = ustreamer-v4p.bin
_CFLAGS = -MD -c -std=c17 -Wall -Wextra -D_GNU_SOURCE $(CFLAGS)
_USTR_LDFLAGS = $(LDFLAGS) -lm -ljpeg -pthread -lrt -latomic -levent -levent_pthreads
_DUMP_LDFLAGS = $(LDFLAGS) -lm -ljpeg -pthread -lrt -latomic
_V4P_LDFLAGS = $(LDFLAGS) -lm -ljpeg -pthread -lrt -latomic
_USTR_LDFLAGS = $(LDFLAGS) -lm -ljpeg -pthread -lrt -levent -levent_pthreads
_DUMP_LDFLAGS = $(LDFLAGS) -lm -ljpeg -pthread -lrt
_V4P_LDFLAGS = $(LDFLAGS) -lm -ljpeg -pthread -lrt
_USTR_SRCS = $(shell ls \
libs/*.c \
@@ -50,45 +46,51 @@ _OBJS = $(_USTR_SRCS:%.c=$(_BUILD)/%.o) $(_DUMP_SRCS:%.c=$(_BUILD)/%.o)
# =====
ifneq ($(call optbool,$(WITH_PYTHON)),)
override _CFLAGS += -DWITH_PYTHON
ifneq ($(shell sh -c 'uname 2>/dev/null || echo Unknown'),FreeBSD)
override _USTR_LDFLAGS += -latomic
override _DUMP_LDFLAGS += -latomic
override _V4P_LDFLAGS += -latomic
endif
ifneq ($(call optbool,$(WITH_JANUS)),)
override _CFLAGS += -DWITH_JANUS
ifneq ($(MK_WITH_PYTHON),)
override _CFLAGS += -DMK_WITH_PYTHON
endif
ifneq ($(call optbool,$(WITH_GPIO)),)
override _CFLAGS += -DWITH_GPIO $(shell $(PKG_CONFIG) --atleast-version=2 libgpiod 2> /dev/null && echo -DHAVE_GPIOD2)
ifneq ($(MK_WITH_JANUS),)
override _CFLAGS += -DMK_WITH_JANUS
endif
ifneq ($(MK_WITH_GPIO),)
override _CFLAGS += -DMK_WITH_GPIO -DWITH_GPIO $(shell $(PKG_CONFIG) --atleast-version=2 libgpiod 2> /dev/null && echo -DHAVE_GPIOD2)
override _USTR_LDFLAGS += -lgpiod
override _USTR_SRCS += $(shell ls ustreamer/gpio/*.c)
endif
ifneq ($(call optbool,$(WITH_SYSTEMD)),)
override _CFLAGS += -DWITH_SYSTEMD
ifneq ($(MK_WITH_SYSTEMD),)
override _CFLAGS += -DMK_WITH_SYSTEMD -DWITH_SYSTEMD
override _USTR_LDFLAGS += -lsystemd
override _USTR_SRCS += $(shell ls ustreamer/http/systemd/*.c)
endif
ifneq ($(call optbool,$(WITH_PTHREAD_NP)),)
override _CFLAGS += -DWITH_PTHREAD_NP
ifneq ($(MK_WITH_PTHREAD_NP),)
override _CFLAGS += -DMK_WITH_PTHREAD_NP -DWITH_PTHREAD_NP
endif
ifneq ($(call optbool,$(WITH_SETPROCTITLE)),)
override _CFLAGS += -DWITH_SETPROCTITLE
ifneq ($(MK_WITH_SETPROCTITLE),)
override _CFLAGS += -DMK_WITH_SETPROCTITLE -DWITH_SETPROCTITLE
ifeq ($(shell uname -s | tr A-Z a-z),linux)
override _USTR_LDFLAGS += -lbsd
endif
endif
ifneq ($(call optbool,$(WITH_PDEATHSIG)),)
override _CFLAGS += -DWITH_PDEATHSIG
ifneq ($(MK_WITH_PDEATHSIG),)
override _CFLAGS += -DMK_WITH_PDEATHSIG -DWITH_PDEATHSIG
endif
ifneq ($(call optbool,$(WITH_V4P)),)
ifneq ($(MK_WITH_V4P),)
override _TARGETS += $(_V4P)
override _OBJS += $(_V4P_SRCS:%.c=$(_BUILD)/%.o)
override _CFLAGS += -DWITH_V4P $(shell $(PKG_CONFIG) --cflags libdrm)
override _CFLAGS += -DMK_WITH_V4P -DWITH_V4P $(shell $(PKG_CONFIG) --cflags libdrm)
override _V4P_LDFLAGS += $(shell $(PKG_CONFIG) --libs libdrm)
override _USTR_SRCS += $(shell ls libs/drm/*.c)
override _USTR_LDFLAGS += $(shell $(PKG_CONFIG) --libs libdrm)

View File

@@ -48,6 +48,7 @@
#include "threading.h"
#include "frame.h"
#include "xioctl.h"
#include "tc358743.h"
static const struct {
@@ -69,6 +70,7 @@ static const struct {
{"UYVY", V4L2_PIX_FMT_UYVY},
{"YUV420", V4L2_PIX_FMT_YUV420},
{"YVU420", V4L2_PIX_FMT_YVU420},
{"GREY", V4L2_PIX_FMT_GREY},
{"RGB565", V4L2_PIX_FMT_RGB565},
{"RGB24", V4L2_PIX_FMT_RGB24},
{"BGR24", V4L2_PIX_FMT_BGR24},
@@ -192,12 +194,18 @@ int us_capture_open(us_capture_s *cap) {
_LOG_DEBUG("Capture device fd=%d opened", run->fd);
if (cap->dv_timings && cap->persistent) {
struct v4l2_control ctl = {.id = V4L2_CID_DV_RX_POWER_PRESENT};
if (!us_xioctl(run->fd, VIDIOC_G_CTRL, &ctl)) {
if (!ctl.value) {
goto error_no_cable;
}
}
_LOG_DEBUG("Probing DV-timings or QuerySTD ...");
if (_capture_open_dv_timings(cap, false) < 0) {
US_ONCE_FOR(run->open_error_once, __LINE__, {
_LOG_ERROR("No signal from source");
});
goto error_no_signal;
switch (_capture_open_dv_timings(cap, false)) {
case 0: break;
case US_ERROR_NO_SIGNAL: goto error_no_signal;
case US_ERROR_NO_SYNC: goto error_no_sync;
default: goto error;
}
}
@@ -215,6 +223,15 @@ int us_capture_open(us_capture_s *cap) {
if (_capture_open_format(cap, true) < 0) {
goto error;
}
if (cap->dv_timings && cap->persistent) {
struct v4l2_control ctl = {.id = TC358743_CID_LANES_ENOUGH};
if (!us_xioctl(run->fd, VIDIOC_G_CTRL, &ctl)) {
if (!ctl.value) {
_LOG_ERROR("Not enough lanes, hardware can't handle this signal");
goto error_no_lanes;
}
}
}
_capture_open_hw_fps(cap);
_capture_open_jpeg_quality(cap);
if (_capture_open_io_method(cap) < 0) {
@@ -247,9 +264,23 @@ error_no_device:
us_capture_close(cap);
return US_ERROR_NO_DEVICE;
error_no_signal:
error_no_cable:
us_capture_close(cap);
return US_ERROR_NO_DATA;
return US_ERROR_NO_CABLE;
error_no_signal:
US_ONCE_FOR(run->open_error_once, __LINE__, { _LOG_ERROR("No signal from source"); });
us_capture_close(cap);
return US_ERROR_NO_SIGNAL;
error_no_sync:
US_ONCE_FOR(run->open_error_once, __LINE__, { _LOG_ERROR("No sync on signal"); });
us_capture_close(cap);
return US_ERROR_NO_SYNC;
error_no_lanes:
us_capture_close(cap);
return US_ERROR_NO_LANES;
error:
run->open_error_once = 0;
@@ -619,6 +650,10 @@ static int _capture_open_dv_timings(us_capture_s *cap, bool apply) {
// TC358743 errors here (see in the kernel: drivers/media/i2c/tc358743.c):
// - ENOLINK: No valid signal (SYS_STATUS & MASK_S_TMDS)
// - ENOLCK: No sync on signal (SYS_STATUS & MASK_S_SYNC)
switch (errno) {
case ENOLINK: return US_ERROR_NO_SIGNAL;
case ENOLCK: return US_ERROR_NO_SYNC;
}
dv_errno = errno;
goto querystd;
} else if (!apply) {

View File

@@ -39,7 +39,7 @@
#define US_VIDEO_MAX_FPS ((uint)120)
#define US_STANDARDS_STR "PAL, NTSC, SECAM"
#define US_FORMATS_STR "YUYV, YVYU, UYVY, YUV420, YVU420, RGB565, RGB24, BGR24, MJPEG, JPEG"
#define US_FORMATS_STR "YUYV, YVYU, UYVY, YUV420, YVU420, RGB565, RGB24, BGR24, GREY, MJPEG, JPEG"
#define US_IO_METHODS_STR "MMAP, USERPTR"

View File

@@ -26,7 +26,7 @@
#define US_VERSION_MAJOR 6
#define US_VERSION_MINOR 30
#define US_VERSION_MINOR 37
#define US_MAKE_VERSION2(_major, _minor) #_major "." #_minor
#define US_MAKE_VERSION1(_major, _minor) US_MAKE_VERSION2(_major, _minor)

View File

@@ -28,7 +28,9 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#ifdef __linux__
# include <sys/sysmacros.h>
#endif
#include <linux/videodev2.h>
@@ -376,7 +378,7 @@ int us_drm_expose_stub(us_drm_s *drm, us_drm_stub_e stub, const us_capture_s *ca
DRAW_MSG("=== PiKVM ===\n \n< UNSUPPORTED CAPTURE FORMAT >");
break;
case US_DRM_STUB_NO_SIGNAL:
DRAW_MSG("=== PiKVM ===\n \n< NO SIGNAL >");
DRAW_MSG("=== PiKVM ===\n \n< NO LIVE VIDEO >");
break;
case US_DRM_STUB_BUSY:
DRAW_MSG("=== PiKVM ===\n \n< ONLINE IS ACTIVE >");
@@ -664,6 +666,15 @@ static drmModeModeInfo *_find_best_mode(drmModeConnector *conn, uint width, uint
continue; // Discard interlaced
}
const float mode_hz = _get_refresh_rate(mode);
if (width == 640 && height == 416 && mode->hdisplay == 640 && mode->vdisplay == 480) {
// A special case for some ancient DOS device with VGA converter.
// @CapnKirk in Discord
if (hz > 0 && mode_hz < hz) {
best = mode;
best->vdisplay = 416;
break;
}
}
if (mode->hdisplay == width && mode->vdisplay == height) {
best = mode; // Any mode with exact resolution
if (hz > 0 && mode_hz == hz) {

View File

@@ -24,4 +24,8 @@
#define US_ERROR_COMMON -1
#define US_ERROR_NO_DEVICE -2
#define US_ERROR_NO_DATA -3
#define US_ERROR_NO_CABLE -3
#define US_ERROR_NO_SIGNAL -4
#define US_ERROR_NO_SYNC -5
#define US_ERROR_NO_LANES -6
#define US_ERROR_NO_DATA -7

View File

@@ -84,6 +84,7 @@ uint us_frame_get_padding(const us_frame_s *frame) {
switch (frame->format) {
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_GREY:
bytes_per_pixel = 1;
break;

View File

@@ -121,9 +121,16 @@ void us_frametext_draw(us_frametext_s *ft, const char *text, uint width, uint he
if (block_width == 0 || block_height == 0) {
goto empty;
}
uint scale_x = frame->width / block_width / 2;
uint scale_y = frame->height / block_height / 3;
if (scale_x < scale_y / 1.5) {
// Ширина текста должна быть от 75%, до половины экрана, в зависимости от длины
const float div_x = US_MAX(US_MIN((100 / block_width * 2), 2.0), 1.5);
// Высоту тоже отрегулировать как-нибудь
const float div_y = US_MAX(US_MIN((70 / block_height * 2), 2.0), 1.5);
uint scale_x = frame->width / block_width / div_x;
uint scale_y = frame->height / block_height / div_y;
if (scale_x < scale_y / 1.5) { // Keep proportions
scale_y = scale_x * 1.5;
} else if (scale_y < scale_x * 1.5) {
scale_x = scale_y / 1.5;

View File

@@ -33,17 +33,6 @@
#include "xioctl.h"
#ifndef V4L2_CID_USER_TC358743_BASE
# define V4L2_CID_USER_TC358743_BASE (V4L2_CID_USER_BASE + 0x1080)
#endif
#ifndef TC358743_CID_AUDIO_PRESENT
# define TC358743_CID_AUDIO_PRESENT (V4L2_CID_USER_TC358743_BASE + 1)
#endif
#ifndef TC358743_CID_AUDIO_SAMPLING_RATE
# define TC358743_CID_AUDIO_SAMPLING_RATE (V4L2_CID_USER_TC358743_BASE + 0)
#endif
int us_tc358743_xioctl_get_audio_hz(int fd, uint *audio_hz) {
*audio_hz = 0;

View File

@@ -22,7 +22,26 @@
#pragma once
#include <linux/v4l2-controls.h>
#include "types.h"
#ifndef V4L2_CID_USER_TC358743_BASE
# define V4L2_CID_USER_TC358743_BASE (V4L2_CID_USER_BASE + 0x1080)
#endif
#ifndef TC358743_CID_AUDIO_SAMPLING_RATE
# define TC358743_CID_AUDIO_SAMPLING_RATE (V4L2_CID_USER_TC358743_BASE + 0)
#endif
#ifndef TC358743_CID_AUDIO_PRESENT
# define TC358743_CID_AUDIO_PRESENT (V4L2_CID_USER_TC358743_BASE + 1)
#endif
#ifndef TC358743_CID_LANES_ENOUGH
# define TC358743_CID_LANES_ENOUGH (V4L2_CID_USER_TC358743_BASE + 2)
#endif
int us_tc358743_xioctl_get_audio_hz(int fd, uint *audio_hz);

View File

@@ -36,7 +36,7 @@ us_blank_s *us_blank_init(void) {
blank->ft = us_frametext_init();
blank->raw = blank->ft->frame;
blank->jpeg = us_frame_init();
us_blank_draw(blank, "< NO SIGNAL >", 640, 480);
us_blank_draw(blank, "< NO LIVE VIDEO >", 640, 480);
return blank;
}

View File

@@ -39,6 +39,7 @@ static void _jpeg_set_dest_frame(j_compress_ptr jpeg, us_frame_s *frame);
static void _jpeg_write_scanlines_yuv(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
static void _jpeg_write_scanlines_yuv_planar(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
static void _jpeg_write_scanlines_grey(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
#ifndef JCS_EXTENSIONS
@@ -75,6 +76,10 @@ void us_cpu_encoder_compress(const us_frame_s *src, us_frame_s *dest, uint quali
case V4L2_PIX_FMT_YVU420:
jpeg.in_color_space = JCS_YCbCr;
break;
case V4L2_PIX_FMT_GREY:
jpeg.input_components = 1;
jpeg.in_color_space = JCS_GRAYSCALE;
break;
# ifdef JCS_EXTENSIONS
case V4L2_PIX_FMT_BGR24:
jpeg.in_color_space = JCS_EXT_BGR;
@@ -102,6 +107,10 @@ void us_cpu_encoder_compress(const us_frame_s *src, us_frame_s *dest, uint quali
case V4L2_PIX_FMT_YVU420:
_jpeg_write_scanlines_yuv_planar(&jpeg, src);
break;
case V4L2_PIX_FMT_GREY:
_jpeg_write_scanlines_grey(&jpeg, src);
break;
case V4L2_PIX_FMT_RGB565:
_jpeg_write_scanlines_rgb565(&jpeg, src);
@@ -249,6 +258,30 @@ static void _jpeg_write_scanlines_yuv_planar(struct jpeg_compress_struct *jpeg,
free(line_buf);
}
static void _jpeg_write_scanlines_grey(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
u8 *line_buf;
US_CALLOC(line_buf, frame->width);
const uint padding = us_frame_get_padding(frame);
const u8 *data = frame->data;
while (jpeg->next_scanline < frame->height) {
u8 *ptr = line_buf;
for (uint x = 0; x < frame->width; ++x) {
ptr[0] = data[x];
ptr += 1;
}
data += frame->width + padding;
JSAMPROW scanlines[1] = {line_buf};
jpeg_write_scanlines(jpeg, scanlines, 1);
}
free(line_buf);
}
static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
u8 *line_buf;
US_CALLOC(line_buf, frame->width * 3);

View File

@@ -880,7 +880,7 @@ static void _http_send_snapshot(us_server_s *server) {
if (!captured_meta.online) {
if (blank == NULL) {
blank = us_blank_init();
us_blank_draw(blank, "< NO SIGNAL >", captured_meta.width, captured_meta.height);
us_blank_draw(blank, "< NO LIVE VIDEO >", captured_meta.width, captured_meta.height);
}
frame = blank->jpeg;
}

View File

@@ -138,7 +138,7 @@ static const struct option _LONG_OPTS[] = {
{"input", required_argument, NULL, _O_INPUT},
{"resolution", required_argument, NULL, _O_RESOLUTION},
{"format", required_argument, NULL, _O_FORMAT},
{"format-swap-rgb", required_argument, NULL, _O_FORMAT_SWAP_RGB},
{"format-swap-rgb", no_argument, NULL, _O_FORMAT_SWAP_RGB},
{"tv-standard", required_argument, NULL, _O_TV_STANDARD},
{"io-method", required_argument, NULL, _O_IO_METHOD},
{"desired-fps", required_argument, NULL, _O_DESIRED_FPS},
@@ -581,49 +581,49 @@ static int _check_instance_id(const char *str) {
}
static void _features(void) {
# ifdef WITH_PYTHON
# ifdef MK_WITH_PYTHON
puts("+ WITH_PYTHON");
# else
puts("- WITH_PYTHON");
# endif
# ifdef WITH_JANUS
# ifdef MK_WITH_JANUS
puts("+ WITH_JANUS");
# else
puts("- WITH_JANUS");
# endif
# ifdef WITH_V4P
# ifdef MK_WITH_V4P
puts("+ WITH_V4P");
# else
puts("- WITH_V4P");
# endif
# ifdef WITH_GPIO
# ifdef MK_WITH_GPIO
puts("+ WITH_GPIO");
# else
puts("- WITH_GPIO");
# endif
# ifdef WITH_SYSTEMD
# ifdef MK_WITH_SYSTEMD
puts("+ WITH_SYSTEMD");
# else
puts("- WITH_SYSTEMD");
# endif
# ifdef WITH_PTHREAD_NP
# ifdef MK_WITH_PTHREAD_NP
puts("+ WITH_PTHREAD_NP");
# else
puts("- WITH_PTHREAD_NP");
# endif
# ifdef WITH_SETPROCTITLE
# ifdef MK_WITH_SETPROCTITLE
puts("+ WITH_SETPROCTITLE");
# else
puts("- WITH_SETPROCTITLE");
# endif
# ifdef WITH_PDEATHSIG
# ifdef MK_WITH_PDEATHSIG
puts("+ WITH_PDEATHSIG");
# else
puts("- WITH_PDEATHSIG");

View File

@@ -129,7 +129,7 @@ us_stream_s *us_stream_init(us_capture_s *cap, us_encoder_s *enc) {
void us_stream_update_blank(us_stream_s *stream, const us_capture_s *cap) {
us_stream_runtime_s *const run = stream->run;
us_blank_draw(run->blank, "< NO SIGNAL >", cap->width, cap->height);
us_blank_draw(run->blank, "< NO LIVE VIDEO >", cap->width, cap->height);
us_fpsi_frame_to_meta(run->blank->raw, &run->notify_meta); // Initial "unchanged" meta
_stream_update_captured_fpsi(stream, run->blank->raw, false);
}
@@ -529,6 +529,8 @@ static int _stream_init_loop(us_stream_s *stream) {
int once = 0;
while (!atomic_load(&stream->run->stop)) {
const char *blank_reason = "< NO LIVE VIDEO >";
# ifdef WITH_GPIO
us_gpio_set_stream_online(false);
# endif
@@ -554,16 +556,61 @@ static int _stream_init_loop(us_stream_s *stream) {
switch (us_capture_open(stream->cap)) {
case 0: break;
case US_ERROR_NO_DEVICE:
case US_ERROR_NO_DATA:
US_ONCE({ US_LOG_INFO("Waiting for the capture device ..."); });
goto offline_and_retry;
blank_reason = (
"< NO CAPTURE DEVICE >\n \n"
" Possible reasons: \n \n"
" - Device unplugged \n \n"
" - Bad config \n \n"
" - Malfunction "
);
goto silent_error;
case US_ERROR_NO_CABLE:
blank_reason = (
"< NO VIDEO SOURCE >\n \n"
" Possible reasons: \n \n"
" - Source is off \n \n"
" - Cable problems "
);
goto silent_error;
case US_ERROR_NO_SIGNAL:
blank_reason = (
"< NO SIGNAL DETECTED >\n \n"
" Possible reasons: \n \n"
" - Video suspended \n \n"
" - Cable problems "
);
goto silent_error;
case US_ERROR_NO_SYNC:
blank_reason = (
"< NO SYNC WITH SIGNAL >\n \n"
" Possible reasons: \n \n"
" - Source is crazy \n \n"
" - Cable problems "
);
goto silent_error;
case US_ERROR_NO_LANES:
blank_reason = (
"< UNSUPPORTED SIGNAL TIMINGS >\n \n"
" Possible reasons: \n \n"
" - Too high frequency \n \n"
" - Source ignores EDID \n \n"
" - Invalid EDID "
);
goto verbose_error;
default:
once = 0;
goto offline_and_retry;
goto verbose_error;
}
us_encoder_open(stream->enc, stream->cap);
return 0;
silent_error:
US_ONCE({ US_LOG_INFO("Waiting for the capture device ..."); });
goto offline_and_retry;
verbose_error:
once = 0;
goto offline_and_retry;
offline_and_retry:
for (uint count = 0; count < stream->error_delay * 10; ++count) {
if (atomic_load(&run->stop)) {
@@ -577,7 +624,7 @@ static int _stream_init_loop(us_stream_s *stream) {
width = stream->cap->width;
height = stream->cap->height;
}
us_blank_draw(run->blank, "< NO SIGNAL >", width, height);
us_blank_draw(run->blank, blank_reason, width, height);
_stream_update_captured_fpsi(stream, run->blank->raw, false);
_stream_expose_jpeg(stream, run->blank->jpeg);