mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-27 12:16:31 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
913cdac7a6 | ||
|
|
777697dc1e | ||
|
|
5f437b9a35 | ||
|
|
b089f896da | ||
|
|
0e521ad0c6 | ||
|
|
620a0ec847 | ||
|
|
7a1d4816ed | ||
|
|
aec8431024 | ||
|
|
5b18e29555 | ||
|
|
2717248581 | ||
|
|
afd305e87d | ||
|
|
e3d8132237 | ||
|
|
1f32e875c3 | ||
|
|
2e88fb9294 | ||
|
|
d68f8e6d86 | ||
|
|
b380beba6d | ||
|
|
3a06a484ce | ||
|
|
0307d3bdb6 | ||
|
|
f2dd9c3c5a | ||
|
|
4e3f873f0d | ||
|
|
029440cf82 | ||
|
|
df74f5cf18 | ||
|
|
97494c3531 | ||
|
|
71544880d1 |
@@ -1,7 +1,7 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
current_version = 6.32
|
current_version = 6.39
|
||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)
|
parse = (?P<major>\d+)\.(?P<minor>\d+)
|
||||||
serialize =
|
serialize =
|
||||||
{major}.{minor}
|
{major}.{minor}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|----------|---------------|-------------------|
|
|----------|---------------|-------------------|
|
||||||
| Multithreaded JPEG encoding | ✔ | ✘ |
|
| Multithreaded JPEG encoding | ✔ | ✘ |
|
||||||
| Hardware image encoding<br>on Raspberry Pi | ✔ | ✘ |
|
| 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> |
|
| [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> | ✘ |
|
| Option to skip frames when streaming<br>static images by HTTP to save the traffic | ✔ <sup>2</sup> | ✘ |
|
||||||
| Streaming via UNIX domain socket | ✔ | ✘ |
|
| 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.
|
* FreeBSD port: https://www.freshports.org/multimedia/ustreamer.
|
||||||
|
|
||||||
### Preconditions
|
### 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`.
|
* 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`.
|
* 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`.
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ _SRCS = $(shell ls src/uslibs/*.c src/*.c)
|
|||||||
_BUILD = build
|
_BUILD = build
|
||||||
|
|
||||||
|
|
||||||
|
# =====
|
||||||
|
ifneq ($(shell sh -c 'uname 2>/dev/null || echo Unknown'),FreeBSD)
|
||||||
|
override _LDFLAGS += -latomic
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq ($(MK_WITH_PTHREAD_NP),)
|
ifneq ($(MK_WITH_PTHREAD_NP),)
|
||||||
override _CFLAGS += -DWITH_PTHREAD_NP
|
override _CFLAGS += -DWITH_PTHREAD_NP
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -60,6 +60,8 @@
|
|||||||
#include "config.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 us_config_s *_g_config = NULL;
|
||||||
static const useconds_t _g_watchers_polling = 100000;
|
static const useconds_t _g_watchers_polling = 100000;
|
||||||
|
|
||||||
@@ -670,15 +672,12 @@ static struct janus_plugin_result *_plugin_handle_message(
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if (!strcmp(request_str, "features")) {
|
} else if (!strcmp(request_str, "features")) {
|
||||||
const char *const stun_host = getenv("JANUS_USTREAMER_STUN_HOST");
|
const char *const ice_url = getenv("JANUS_USTREAMER_WEB_ICE_URL");
|
||||||
const char *const stun_port = getenv("JANUS_USTREAMER_STUN_PORT");
|
|
||||||
json_t *const features = json_pack(
|
json_t *const features = json_pack(
|
||||||
"{s:b, s:b, s:{s:s, s:i}}",
|
"{s:b, s:b, s:{s:s?}}",
|
||||||
"audio", (_g_rtpa != NULL),
|
"audio", (_g_rtpa != NULL),
|
||||||
"mic", (_g_rtpa != NULL && _g_config->aplay_dev_name != NULL),
|
"mic", (_g_rtpa != NULL && _g_config->aplay_dev_name != NULL),
|
||||||
"stun",
|
"ice", "url", (ice_url != NULL ? ice_url : default_ice_url)
|
||||||
"host", (stun_host != NULL ? stun_host : ""),
|
|
||||||
"port", (stun_port != NULL ? atoi(stun_port) : 0)
|
|
||||||
);
|
);
|
||||||
PUSH_STATUS("features", features, NULL);
|
PUSH_STATUS("features", features, NULL);
|
||||||
json_decref(features);
|
json_decref(features);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" Manpage for ustreamer-dump.
|
.\" Manpage for ustreamer-dump.
|
||||||
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
|
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
|
||||||
.TH USTREAMER-DUMP 1 "version 6.32" "January 2021"
|
.TH USTREAMER-DUMP 1 "version 6.39" "January 2021"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
ustreamer-dump \- Dump uStreamer's memory sink to file
|
ustreamer-dump \- Dump uStreamer's memory sink to file
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" Manpage for ustreamer.
|
.\" Manpage for ustreamer.
|
||||||
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
|
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
|
||||||
.TH USTREAMER 1 "version 6.32" "November 2020"
|
.TH USTREAMER 1 "version 6.39" "November 2020"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
ustreamer \- stream MJPEG video from any V4L2 device to the network
|
ustreamer \- stream MJPEG video from any V4L2 device to the network
|
||||||
@@ -52,7 +52,7 @@ Initial image resolution. Default: 640x480.
|
|||||||
.TP
|
.TP
|
||||||
.BR \-m\ \fIfmt ", " \-\-format\ \fIfmt
|
.BR \-m\ \fIfmt ", " \-\-format\ \fIfmt
|
||||||
Image format.
|
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
|
.TP
|
||||||
.BR \-a\ \fIstd ", " \-\-tv\-standard\ \fIstd
|
.BR \-a\ \fIstd ", " \-\-tv\-standard\ \fIstd
|
||||||
Force TV standard.
|
Force TV standard.
|
||||||
@@ -276,6 +276,9 @@ Timeout for lock. Default: 1.
|
|||||||
.BR \-\-exit\-on\-parent\-death
|
.BR \-\-exit\-on\-parent\-death
|
||||||
Exit the program if the parent process is dead. Required \fBWITH_PDEATHSIG\fR feature. Default: disabled.
|
Exit the program if the parent process is dead. Required \fBWITH_PDEATHSIG\fR feature. Default: disabled.
|
||||||
.TP
|
.TP
|
||||||
|
.BR \-\-exit\-on\-device\-error
|
||||||
|
Exit on any device error instead of polling until success. Default: disabled.
|
||||||
|
.TP
|
||||||
.BR \-\-exit\-on\-no\-clients \fIsec
|
.BR \-\-exit\-on\-no\-clients \fIsec
|
||||||
Exit the program if there have been no stream or sink clients or any HTTP requests in the last N seconds. Default: 0 (disabled).
|
Exit the program if there have been no stream or sink clients or any HTTP requests in the last N seconds. Default: 0 (disabled).
|
||||||
.TP
|
.TP
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
|
|
||||||
pkgname=ustreamer
|
pkgname=ustreamer
|
||||||
pkgver=6.32
|
pkgver=6.39
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Lightweight and fast MJPEG-HTTP streamer"
|
pkgdesc="Lightweight and fast MJPEG-HTTP streamer"
|
||||||
url="https://github.com/pikvm/ustreamer"
|
url="https://github.com/pikvm/ustreamer"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=ustreamer
|
PKG_NAME:=ustreamer
|
||||||
PKG_VERSION:=6.32
|
PKG_VERSION:=6.39
|
||||||
PKG_RELEASE:=1
|
PKG_RELEASE:=1
|
||||||
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ define Package/ustreamer
|
|||||||
SECTION:=multimedia
|
SECTION:=multimedia
|
||||||
CATEGORY:=Multimedia
|
CATEGORY:=Multimedia
|
||||||
TITLE:=uStreamer
|
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
|
URL:=https://github.com/pikvm/ustreamer
|
||||||
endef
|
endef
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ def main() -> None:
|
|||||||
flags = _find_flags()
|
flags = _find_flags()
|
||||||
setup(
|
setup(
|
||||||
name="ustreamer",
|
name="ustreamer",
|
||||||
version="6.32",
|
version="6.39",
|
||||||
description="uStreamer tools",
|
description="uStreamer tools",
|
||||||
author="Maxim Devaev",
|
author="Maxim Devaev",
|
||||||
author_email="mdevaev@gmail.com",
|
author_email="mdevaev@gmail.com",
|
||||||
|
|||||||
12
src/Makefile
12
src/Makefile
@@ -14,9 +14,9 @@ _V4P = ustreamer-v4p.bin
|
|||||||
|
|
||||||
_CFLAGS = -MD -c -std=c17 -Wall -Wextra -D_GNU_SOURCE $(CFLAGS)
|
_CFLAGS = -MD -c -std=c17 -Wall -Wextra -D_GNU_SOURCE $(CFLAGS)
|
||||||
|
|
||||||
_USTR_LDFLAGS = $(LDFLAGS) -lm -ljpeg -pthread -lrt -latomic -levent -levent_pthreads
|
_USTR_LDFLAGS = $(LDFLAGS) -lm -ljpeg -pthread -lrt -levent -levent_pthreads
|
||||||
_DUMP_LDFLAGS = $(LDFLAGS) -lm -ljpeg -pthread -lrt -latomic
|
_DUMP_LDFLAGS = $(LDFLAGS) -lm -ljpeg -pthread -lrt
|
||||||
_V4P_LDFLAGS = $(LDFLAGS) -lm -ljpeg -pthread -lrt -latomic
|
_V4P_LDFLAGS = $(LDFLAGS) -lm -ljpeg -pthread -lrt
|
||||||
|
|
||||||
_USTR_SRCS = $(shell ls \
|
_USTR_SRCS = $(shell ls \
|
||||||
libs/*.c \
|
libs/*.c \
|
||||||
@@ -46,6 +46,12 @@ _OBJS = $(_USTR_SRCS:%.c=$(_BUILD)/%.o) $(_DUMP_SRCS:%.c=$(_BUILD)/%.o)
|
|||||||
|
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
|
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 ($(MK_WITH_PYTHON),)
|
ifneq ($(MK_WITH_PYTHON),)
|
||||||
override _CFLAGS += -DMK_WITH_PYTHON
|
override _CFLAGS += -DMK_WITH_PYTHON
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -48,6 +48,7 @@
|
|||||||
#include "threading.h"
|
#include "threading.h"
|
||||||
#include "frame.h"
|
#include "frame.h"
|
||||||
#include "xioctl.h"
|
#include "xioctl.h"
|
||||||
|
#include "tc358743.h"
|
||||||
|
|
||||||
|
|
||||||
static const struct {
|
static const struct {
|
||||||
@@ -69,6 +70,7 @@ static const struct {
|
|||||||
{"UYVY", V4L2_PIX_FMT_UYVY},
|
{"UYVY", V4L2_PIX_FMT_UYVY},
|
||||||
{"YUV420", V4L2_PIX_FMT_YUV420},
|
{"YUV420", V4L2_PIX_FMT_YUV420},
|
||||||
{"YVU420", V4L2_PIX_FMT_YVU420},
|
{"YVU420", V4L2_PIX_FMT_YVU420},
|
||||||
|
{"GREY", V4L2_PIX_FMT_GREY},
|
||||||
{"RGB565", V4L2_PIX_FMT_RGB565},
|
{"RGB565", V4L2_PIX_FMT_RGB565},
|
||||||
{"RGB24", V4L2_PIX_FMT_RGB24},
|
{"RGB24", V4L2_PIX_FMT_RGB24},
|
||||||
{"BGR24", V4L2_PIX_FMT_BGR24},
|
{"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);
|
_LOG_DEBUG("Capture device fd=%d opened", run->fd);
|
||||||
|
|
||||||
if (cap->dv_timings && cap->persistent) {
|
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 ...");
|
_LOG_DEBUG("Probing DV-timings or QuerySTD ...");
|
||||||
if (_capture_open_dv_timings(cap, false) < 0) {
|
switch (_capture_open_dv_timings(cap, false)) {
|
||||||
US_ONCE_FOR(run->open_error_once, __LINE__, {
|
case 0: break;
|
||||||
_LOG_ERROR("No signal from source");
|
case US_ERROR_NO_SIGNAL: goto error_no_signal;
|
||||||
});
|
case US_ERROR_NO_SYNC: goto error_no_sync;
|
||||||
goto error_no_signal;
|
default: goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,6 +223,15 @@ int us_capture_open(us_capture_s *cap) {
|
|||||||
if (_capture_open_format(cap, true) < 0) {
|
if (_capture_open_format(cap, true) < 0) {
|
||||||
goto error;
|
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_hw_fps(cap);
|
||||||
_capture_open_jpeg_quality(cap);
|
_capture_open_jpeg_quality(cap);
|
||||||
if (_capture_open_io_method(cap) < 0) {
|
if (_capture_open_io_method(cap) < 0) {
|
||||||
@@ -247,9 +264,23 @@ error_no_device:
|
|||||||
us_capture_close(cap);
|
us_capture_close(cap);
|
||||||
return US_ERROR_NO_DEVICE;
|
return US_ERROR_NO_DEVICE;
|
||||||
|
|
||||||
error_no_signal:
|
error_no_cable:
|
||||||
us_capture_close(cap);
|
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:
|
error:
|
||||||
run->open_error_once = 0;
|
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):
|
// TC358743 errors here (see in the kernel: drivers/media/i2c/tc358743.c):
|
||||||
// - ENOLINK: No valid signal (SYS_STATUS & MASK_S_TMDS)
|
// - ENOLINK: No valid signal (SYS_STATUS & MASK_S_TMDS)
|
||||||
// - ENOLCK: No sync on signal (SYS_STATUS & MASK_S_SYNC)
|
// - 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;
|
dv_errno = errno;
|
||||||
goto querystd;
|
goto querystd;
|
||||||
} else if (!apply) {
|
} else if (!apply) {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
#define US_VIDEO_MAX_FPS ((uint)120)
|
#define US_VIDEO_MAX_FPS ((uint)120)
|
||||||
|
|
||||||
#define US_STANDARDS_STR "PAL, NTSC, SECAM"
|
#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"
|
#define US_IO_METHODS_STR "MMAP, USERPTR"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#define US_VERSION_MAJOR 6
|
#define US_VERSION_MAJOR 6
|
||||||
#define US_VERSION_MINOR 32
|
#define US_VERSION_MINOR 39
|
||||||
|
|
||||||
#define US_MAKE_VERSION2(_major, _minor) #_major "." #_minor
|
#define US_MAKE_VERSION2(_major, _minor) #_major "." #_minor
|
||||||
#define US_MAKE_VERSION1(_major, _minor) US_MAKE_VERSION2(_major, _minor)
|
#define US_MAKE_VERSION1(_major, _minor) US_MAKE_VERSION2(_major, _minor)
|
||||||
|
|||||||
@@ -28,7 +28,9 @@
|
|||||||
|
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/sysmacros.h>
|
#ifdef __linux__
|
||||||
|
# include <sys/sysmacros.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <linux/videodev2.h>
|
#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 >");
|
DRAW_MSG("=== PiKVM ===\n \n< UNSUPPORTED CAPTURE FORMAT >");
|
||||||
break;
|
break;
|
||||||
case US_DRM_STUB_NO_SIGNAL:
|
case US_DRM_STUB_NO_SIGNAL:
|
||||||
DRAW_MSG("=== PiKVM ===\n \n< NO SIGNAL >");
|
DRAW_MSG("=== PiKVM ===\n \n< NO LIVE VIDEO >");
|
||||||
break;
|
break;
|
||||||
case US_DRM_STUB_BUSY:
|
case US_DRM_STUB_BUSY:
|
||||||
DRAW_MSG("=== PiKVM ===\n \n< ONLINE IS ACTIVE >");
|
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
|
continue; // Discard interlaced
|
||||||
}
|
}
|
||||||
const float mode_hz = _get_refresh_rate(mode);
|
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) {
|
if (mode->hdisplay == width && mode->vdisplay == height) {
|
||||||
best = mode; // Any mode with exact resolution
|
best = mode; // Any mode with exact resolution
|
||||||
if (hz > 0 && mode_hz == hz) {
|
if (hz > 0 && mode_hz == hz) {
|
||||||
|
|||||||
@@ -24,4 +24,8 @@
|
|||||||
|
|
||||||
#define US_ERROR_COMMON -1
|
#define US_ERROR_COMMON -1
|
||||||
#define US_ERROR_NO_DEVICE -2
|
#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
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ uint us_frame_get_padding(const us_frame_s *frame) {
|
|||||||
switch (frame->format) {
|
switch (frame->format) {
|
||||||
case V4L2_PIX_FMT_YUV420:
|
case V4L2_PIX_FMT_YUV420:
|
||||||
case V4L2_PIX_FMT_YVU420:
|
case V4L2_PIX_FMT_YVU420:
|
||||||
|
case V4L2_PIX_FMT_GREY:
|
||||||
bytes_per_pixel = 1;
|
bytes_per_pixel = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
if (block_width == 0 || block_height == 0) {
|
||||||
goto empty;
|
goto empty;
|
||||||
}
|
}
|
||||||
uint scale_x = frame->width / block_width / 2;
|
|
||||||
uint scale_y = frame->height / block_height / 3;
|
// Ширина текста должна быть от 75%, до половины экрана, в зависимости от длины
|
||||||
if (scale_x < scale_y / 1.5) {
|
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;
|
scale_y = scale_x * 1.5;
|
||||||
} else if (scale_y < scale_x * 1.5) {
|
} else if (scale_y < scale_x * 1.5) {
|
||||||
scale_x = scale_y / 1.5;
|
scale_x = scale_y / 1.5;
|
||||||
|
|||||||
@@ -33,17 +33,6 @@
|
|||||||
#include "xioctl.h"
|
#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) {
|
int us_tc358743_xioctl_get_audio_hz(int fd, uint *audio_hz) {
|
||||||
*audio_hz = 0;
|
*audio_hz = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,26 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <linux/v4l2-controls.h>
|
||||||
|
|
||||||
#include "types.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);
|
int us_tc358743_xioctl_get_audio_hz(int fd, uint *audio_hz);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ us_blank_s *us_blank_init(void) {
|
|||||||
blank->ft = us_frametext_init();
|
blank->ft = us_frametext_init();
|
||||||
blank->raw = blank->ft->frame;
|
blank->raw = blank->ft->frame;
|
||||||
blank->jpeg = us_frame_init();
|
blank->jpeg = us_frame_init();
|
||||||
us_blank_draw(blank, "< NO SIGNAL >", 640, 480);
|
us_blank_draw(blank, "< NO LIVE VIDEO >", 640, 480);
|
||||||
return blank;
|
return blank;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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(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_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_rgb565(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
|
||||||
static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
|
static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
|
||||||
#ifndef JCS_EXTENSIONS
|
#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:
|
case V4L2_PIX_FMT_YVU420:
|
||||||
jpeg.in_color_space = JCS_YCbCr;
|
jpeg.in_color_space = JCS_YCbCr;
|
||||||
break;
|
break;
|
||||||
|
case V4L2_PIX_FMT_GREY:
|
||||||
|
jpeg.input_components = 1;
|
||||||
|
jpeg.in_color_space = JCS_GRAYSCALE;
|
||||||
|
break;
|
||||||
# ifdef JCS_EXTENSIONS
|
# ifdef JCS_EXTENSIONS
|
||||||
case V4L2_PIX_FMT_BGR24:
|
case V4L2_PIX_FMT_BGR24:
|
||||||
jpeg.in_color_space = JCS_EXT_BGR;
|
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:
|
case V4L2_PIX_FMT_YVU420:
|
||||||
_jpeg_write_scanlines_yuv_planar(&jpeg, src);
|
_jpeg_write_scanlines_yuv_planar(&jpeg, src);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case V4L2_PIX_FMT_GREY:
|
||||||
|
_jpeg_write_scanlines_grey(&jpeg, src);
|
||||||
|
break;
|
||||||
|
|
||||||
case V4L2_PIX_FMT_RGB565:
|
case V4L2_PIX_FMT_RGB565:
|
||||||
_jpeg_write_scanlines_rgb565(&jpeg, src);
|
_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);
|
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) {
|
static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
|
||||||
u8 *line_buf;
|
u8 *line_buf;
|
||||||
US_CALLOC(line_buf, frame->width * 3);
|
US_CALLOC(line_buf, frame->width * 3);
|
||||||
|
|||||||
@@ -880,7 +880,7 @@ static void _http_send_snapshot(us_server_s *server) {
|
|||||||
if (!captured_meta.online) {
|
if (!captured_meta.online) {
|
||||||
if (blank == NULL) {
|
if (blank == NULL) {
|
||||||
blank = us_blank_init();
|
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;
|
frame = blank->jpeg;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ enum _US_OPT_VALUES {
|
|||||||
# ifdef WITH_PDEATHSIG
|
# ifdef WITH_PDEATHSIG
|
||||||
_O_EXIT_ON_PARENT_DEATH,
|
_O_EXIT_ON_PARENT_DEATH,
|
||||||
# endif
|
# endif
|
||||||
|
_O_EXIT_ON_DEVICE_ERROR,
|
||||||
_O_EXIT_ON_NO_CLIENTS,
|
_O_EXIT_ON_NO_CLIENTS,
|
||||||
# ifdef WITH_SETPROCTITLE
|
# ifdef WITH_SETPROCTITLE
|
||||||
_O_PROCESS_NAME_PREFIX,
|
_O_PROCESS_NAME_PREFIX,
|
||||||
@@ -138,7 +139,7 @@ static const struct option _LONG_OPTS[] = {
|
|||||||
{"input", required_argument, NULL, _O_INPUT},
|
{"input", required_argument, NULL, _O_INPUT},
|
||||||
{"resolution", required_argument, NULL, _O_RESOLUTION},
|
{"resolution", required_argument, NULL, _O_RESOLUTION},
|
||||||
{"format", required_argument, NULL, _O_FORMAT},
|
{"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},
|
{"tv-standard", required_argument, NULL, _O_TV_STANDARD},
|
||||||
{"io-method", required_argument, NULL, _O_IO_METHOD},
|
{"io-method", required_argument, NULL, _O_IO_METHOD},
|
||||||
{"desired-fps", required_argument, NULL, _O_DESIRED_FPS},
|
{"desired-fps", required_argument, NULL, _O_DESIRED_FPS},
|
||||||
@@ -227,6 +228,7 @@ static const struct option _LONG_OPTS[] = {
|
|||||||
# ifdef WITH_PDEATHSIG
|
# ifdef WITH_PDEATHSIG
|
||||||
{"exit-on-parent-death", no_argument, NULL, _O_EXIT_ON_PARENT_DEATH},
|
{"exit-on-parent-death", no_argument, NULL, _O_EXIT_ON_PARENT_DEATH},
|
||||||
# endif
|
# endif
|
||||||
|
{"exit-on-device-error", no_argument, NULL, _O_EXIT_ON_DEVICE_ERROR},
|
||||||
{"exit-on-no-clients", required_argument, NULL, _O_EXIT_ON_NO_CLIENTS},
|
{"exit-on-no-clients", required_argument, NULL, _O_EXIT_ON_NO_CLIENTS},
|
||||||
# ifdef WITH_SETPROCTITLE
|
# ifdef WITH_SETPROCTITLE
|
||||||
{"process-name-prefix", required_argument, NULL, _O_PROCESS_NAME_PREFIX},
|
{"process-name-prefix", required_argument, NULL, _O_PROCESS_NAME_PREFIX},
|
||||||
@@ -490,6 +492,7 @@ int options_parse(us_options_s *options, us_capture_s *cap, us_encoder_s *enc, u
|
|||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
# endif
|
# endif
|
||||||
|
case _O_EXIT_ON_DEVICE_ERROR: OPT_SET(stream->exit_on_device_error, true);
|
||||||
case _O_EXIT_ON_NO_CLIENTS: OPT_NUMBER("--exit-on-no-clients", stream->exit_on_no_clients, 0, 86400, 0);
|
case _O_EXIT_ON_NO_CLIENTS: OPT_NUMBER("--exit-on-no-clients", stream->exit_on_no_clients, 0, 86400, 0);
|
||||||
# ifdef WITH_SETPROCTITLE
|
# ifdef WITH_SETPROCTITLE
|
||||||
case _O_PROCESS_NAME_PREFIX: OPT_SET(process_name_prefix, optarg);
|
case _O_PROCESS_NAME_PREFIX: OPT_SET(process_name_prefix, optarg);
|
||||||
|
|||||||
@@ -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) {
|
void us_stream_update_blank(us_stream_s *stream, const us_capture_s *cap) {
|
||||||
us_stream_runtime_s *const run = stream->run;
|
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
|
us_fpsi_frame_to_meta(run->blank->raw, &run->notify_meta); // Initial "unchanged" meta
|
||||||
_stream_update_captured_fpsi(stream, run->blank->raw, false);
|
_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;
|
int once = 0;
|
||||||
while (!atomic_load(&stream->run->stop)) {
|
while (!atomic_load(&stream->run->stop)) {
|
||||||
|
const char *blank_reason = "< NO LIVE VIDEO >";
|
||||||
|
|
||||||
# ifdef WITH_GPIO
|
# ifdef WITH_GPIO
|
||||||
us_gpio_set_stream_online(false);
|
us_gpio_set_stream_online(false);
|
||||||
# endif
|
# endif
|
||||||
@@ -554,17 +556,68 @@ static int _stream_init_loop(us_stream_s *stream) {
|
|||||||
switch (us_capture_open(stream->cap)) {
|
switch (us_capture_open(stream->cap)) {
|
||||||
case 0: break;
|
case 0: break;
|
||||||
case US_ERROR_NO_DEVICE:
|
case US_ERROR_NO_DEVICE:
|
||||||
case US_ERROR_NO_DATA:
|
blank_reason = (
|
||||||
US_ONCE({ US_LOG_INFO("Waiting for the capture device ..."); });
|
"< NO CAPTURE DEVICE >\n \n"
|
||||||
goto offline_and_retry;
|
" 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:
|
default:
|
||||||
once = 0;
|
goto verbose_error;
|
||||||
goto offline_and_retry;
|
|
||||||
}
|
}
|
||||||
us_encoder_open(stream->enc, stream->cap);
|
us_encoder_open(stream->enc, stream->cap);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
silent_error:
|
||||||
|
if (!stream->exit_on_device_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:
|
offline_and_retry:
|
||||||
|
if (stream->exit_on_device_error) {
|
||||||
|
US_LOG_INFO("Device error, exiting ...");
|
||||||
|
us_process_suicide();
|
||||||
|
}
|
||||||
for (uint count = 0; count < stream->error_delay * 10; ++count) {
|
for (uint count = 0; count < stream->error_delay * 10; ++count) {
|
||||||
if (atomic_load(&run->stop)) {
|
if (atomic_load(&run->stop)) {
|
||||||
break;
|
break;
|
||||||
@@ -577,7 +630,7 @@ static int _stream_init_loop(us_stream_s *stream) {
|
|||||||
width = stream->cap->width;
|
width = stream->cap->width;
|
||||||
height = stream->cap->height;
|
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_update_captured_fpsi(stream, run->blank->raw, false);
|
||||||
_stream_expose_jpeg(stream, run->blank->jpeg);
|
_stream_expose_jpeg(stream, run->blank->jpeg);
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ typedef struct {
|
|||||||
bool notify_parent;
|
bool notify_parent;
|
||||||
bool slowdown;
|
bool slowdown;
|
||||||
uint error_delay;
|
uint error_delay;
|
||||||
|
bool exit_on_device_error;
|
||||||
uint exit_on_no_clients;
|
uint exit_on_no_clients;
|
||||||
|
|
||||||
us_memsink_s *jpeg_sink;
|
us_memsink_s *jpeg_sink;
|
||||||
|
|||||||
Reference in New Issue
Block a user