Compare commits

...

20 Commits
v3.8 ... v3.14

Author SHA1 Message Date
Devaev Maxim
5cfb3b1e60 Bump version: 3.13 → 3.14 2021-01-28 22:16:44 +03:00
Devaev Maxim
82b3b78238 ignore drop-same-frames for sinks 2021-01-27 11:30:35 +03:00
Devaev Maxim
8a82ff6691 using archlinux/archlinux:base-devel 2021-01-25 11:58:52 +03:00
Devaev Maxim
2807678551 Bump version: 3.12 → 3.13 2021-01-25 03:26:26 +03:00
Devaev Maxim
e96e0aa73c workaround for unreasonable rebuilding in package() 2021-01-25 03:21:31 +03:00
Devaev Maxim
d06c2619b2 added missing options to man 2021-01-24 12:26:43 +03:00
Devaev Maxim
6b18455d11 systemd-tmpfiles hangs 2021-01-24 12:21:08 +03:00
Maxim Devaev
217977d9cb Merge pull request #87 from reedy/patch-1
Fix casing of macros
2021-01-22 05:22:26 +03:00
Maxim Devaev
b9f186e47c Merge pull request #88 from reedy/patch-2
Fix below typo
2021-01-22 05:21:44 +03:00
Sam Reed
d7d56f3536 Fix below typo 2021-01-22 01:45:40 +00:00
Sam Reed
2e3c764369 Fix casing of macros 2021-01-22 01:43:50 +00:00
Devaev Maxim
7236e53813 Bump version: 3.11 → 3.12 2021-01-22 02:00:35 +03:00
Devaev Maxim
e7f7350405 fix 2021-01-21 23:49:37 +03:00
Devaev Maxim
81f0266a87 drop_same_frames in python 2021-01-21 23:37:06 +03:00
Devaev Maxim
eb1a2e3695 Bump version: 3.10 → 3.11 2021-01-21 18:55:17 +03:00
Devaev Maxim
6cfd3739d3 fixed python context manager 2021-01-21 18:54:39 +03:00
Devaev Maxim
77b8386de7 Bump version: 3.9 → 3.10 2021-01-21 16:59:47 +03:00
Devaev Maxim
a002bd3427 Makefile deps fix 2021-01-21 16:59:10 +03:00
Devaev Maxim
202b907430 Bump version: 3.8 → 3.9 2021-01-21 12:47:40 +03:00
Devaev Maxim
d9f4aba953 fixed build 2021-01-21 12:47:05 +03:00
12 changed files with 210 additions and 139 deletions

View File

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

View File

@@ -8,7 +8,7 @@ MANPREFIX ?= $(PREFIX)/share/man
CC ?= gcc
PY ?= python3
CFLAGS ?= -O3 -MD
CFLAGS ?= -O3
LDFLAGS ?=
RPI_VC_HEADERS ?= /opt/vc/include
@@ -20,7 +20,8 @@ LINTERS_IMAGE ?= $(USTR)-linters
# =====
override CFLAGS += -c -std=c11 -Wall -Wextra -D_GNU_SOURCE
_CFLAGS = -MD -c -std=c11 -Wall -Wextra -D_GNU_SOURCE $(CFLAGS)
_LDFLAGS = $(LDFLAGS)
_COMMON_LIBS = -lm -ljpeg -pthread -lrt
@@ -48,7 +49,7 @@ endef
ifneq ($(call optbool,$(WITH_OMX)),)
_USTR_LIBS += -lbcm_host -lvcos -lvcsm -lopenmaxil -lmmal -lmmal_core -lmmal_util -lmmal_vc_client -lmmal_components -L$(RPI_VC_LIBS)
override CFLAGS += -DWITH_OMX -DOMX_SKIP64BIT -I$(RPI_VC_HEADERS)
override _CFLAGS += -DWITH_OMX -DOMX_SKIP64BIT -I$(RPI_VC_HEADERS)
_USTR_SRCS += $(shell ls \
src/ustreamer/encoders/omx/*.c \
src/ustreamer/h264/*.c \
@@ -58,14 +59,14 @@ endif
ifneq ($(call optbool,$(WITH_GPIO)),)
_USTR_LIBS += -lgpiod
override CFLAGS += -DWITH_GPIO
override _CFLAGS += -DWITH_GPIO
_USTR_SRCS += $(shell ls src/ustreamer/gpio/*.c)
endif
WITH_PTHREAD_NP ?= 1
ifneq ($(call optbool,$(WITH_PTHREAD_NP)),)
override CFLAGS += -DWITH_PTHREAD_NP
override _CFLAGS += -DWITH_PTHREAD_NP
endif
@@ -74,7 +75,7 @@ ifneq ($(call optbool,$(WITH_SETPROCTITLE)),)
ifeq ($(shell uname -s | tr A-Z a-z),linux)
_USTR_LIBS += -lbsd
endif
override CFLAGS += -DWITH_SETPROCTITLE
override _CFLAGS += -DWITH_SETPROCTITLE
endif
@@ -82,7 +83,7 @@ endif
all: $(USTR) $(DUMP) python
install: $(USTR) $(DUMP)
install: all
mkdir -p $(DESTDIR)$(PREFIX)/bin $(DESTDIR)$(MANPREFIX)/man1
install -m755 $(USTR) $(DESTDIR)$(PREFIX)/bin/$(USTR)
install -m755 $(DUMP) $(DESTDIR)$(PREFIX)/bin/$(DUMP)
@@ -108,27 +109,27 @@ regen:
$(USTR): $(_USTR_SRCS:%.c=$(BUILD)/%.o)
# $(info ========================================)
$(info == LD $@)
@ $(CC) $^ -o $@ $(LDFLAGS) $(_USTR_LIBS)
@ $(CC) $^ -o $@ $(_LDFLAGS) $(_USTR_LIBS)
# $(info :: CC = $(CC))
# $(info :: LIBS = $(_USTR_LIBS))
# $(info :: CFLAGS = $(CFLAGS))
# $(info :: LDFLAGS = $(LDFLAGS))
# $(info :: CFLAGS = $(_CFLAGS))
# $(info :: LDFLAGS = $(_LDFLAGS))
$(DUMP): $(_DUMP_SRCS:%.c=$(BUILD)/%.o)
# $(info ========================================)
$(info == LD $@)
@ $(CC) $^ -o $@ $(LDFLAGS) $(_DUMP_LIBS)
@ $(CC) $^ -o $@ $(_LDFLAGS) $(_DUMP_LIBS)
# $(info :: CC = $(CC))
# $(info :: LIBS = $(_DUMP_LIBS))
# $(info :: CFLAGS = $(CFLAGS))
# $(info :: LDFLAGS = $(LDFLAGS))
# $(info :: CFLAGS = $(_CFLAGS))
# $(info :: LDFLAGS = $(_LDFLAGS))
$(BUILD)/%.o: %.c
$(info -- CC $<)
@ mkdir -p $(dir $@) || true
@ $(CC) $< -o $@ $(CFLAGS)
@ $(CC) $< -o $@ $(_CFLAGS)
python:

View File

@@ -1,11 +1,12 @@
FROM archlinux/base
FROM archlinux/archlinux:base-devel
RUN mkdir -p /etc/pacman.d/hooks \
&& ln -s /dev/null /etc/pacman.d/hooks/30-systemd-tmpfiles.hook
RUN echo "Server = http://mirror.yandex.ru/archlinux/\$repo/os/\$arch" > /etc/pacman.d/mirrorlist
RUN pacman -Syu --noconfirm \
&& pacman -S --needed --noconfirm \
base \
base-devel \
vim \
git \
libjpeg \
@@ -17,7 +18,8 @@ RUN pacman -Syu --noconfirm \
python-tox \
cppcheck \
npm \
&& (pacman -Sc --noconfirm || true)
&& (pacman -Sc --noconfirm || true) \
&& rm -rf /var/cache/pacman/pkg/*
RUN npm install htmlhint -g

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 3.8" "January 2021"
.TH USTREAMER-DUMP 1 "version 3.14" "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 3.8" "November 2020"
.TH USTREAMER 1 "version 3.14" "November 2020"
.SH NAME
ustreamer \- stream MJPG video from any V4L2 device to the network
@@ -104,7 +104,7 @@ It doesn't do anything. Still here for compatibility. Required \fBWITH_OMX\fR fe
Path to JPEG file that will be shown when the device is disconnected during the streaming. Default: black screen 640x480 with 'NO SIGNAL'.
.TP
.BR \-K\ \fIsec ", " \-\-last\-as\-blank\ \fIsec
Show the last frame received from the camera after it was disconnected, but no more than specified time (or endlessly if 0 is specified). If the device has not yet been online, display 'NO SIGNAL' or the image specified by option \-\-blank. Default: disabled.
Show the last frame received from the camera after it was disconnected, but no more than specified time (or endlessly if 0 is specified). If the device has not yet been online, display 'NO SIGNAL' or the image specified by option \-\-blank. Note: currently this option has no effect on memory sinks. Default: disabled.
.TP
.BR \-l ", " \-\-slowdown
Slowdown capturing to 1 FPS or less when no stream or sink clients are connected. Useful to reduce CPU consumption. Default: disabled.
@@ -118,7 +118,7 @@ Delay before trying to connect to the device again after an error (timeout for e
.SS "Image control options"
.TP
.BR \-\-image\-default
Reset all image settings bellow to default. Default: no change.
Reset all image settings below to default. Default: no change.
.TP
.BR \-\-brightness\ \fIN ", " \fIauto ", " \fIdefault
Set brightness. Default: no change.
@@ -166,8 +166,8 @@ Bind to this TCP port. Default: 8080.
.TP
.BR \-U\ \fIpath ", " \-\-unix\ \fIpath
Bind to UNIX domain socket. Default: disabled.
.tp
.br \-d ", " \-\-unix\-rm
.TP
.BR \-d ", " \-\-unix\-rm
Try to remove old unix socket file before binding. default: disabled.
.TP
.BR \-M\ \fImode ", " \-\-unix\-mode\ \fImode
@@ -234,6 +234,12 @@ Client TTL. Default: 10.
.TP
.BR \-\-h264\-sink\-timeout\ \fIsec
Timeout for lock. Default: 1.
.TP
.BR \-\-h264\-bitrate\ \fIkbps
H264 bitrate in Kbps. Default: 5000.
.TP
.BR \-\-h264\-gop\ \fIN
Intarval between keyframes. Default: 30.
.SS "Process options"
.TP

View File

@@ -3,7 +3,7 @@
pkgname=ustreamer
pkgver=3.8
pkgver=3.14
pkgrel=1
pkgdesc="Lightweight and fast MJPG-HTTP streamer"
url="https://github.com/pikvm/ustreamer"
@@ -28,20 +28,21 @@ if [ -e /opt/vc/include/IL/OMX_Core.h ]; then
fi
# LD does not link mmal with this option
# This DOESN'T affect setup.py
LDFLAGS="${LDFLAGS//--as-needed/}"
export LDFLAGS="${LDFLAGS//,,/,}"
build() {
cd "$srcdir"
rm -rf $pkgname-build
cp -r $pkgname $pkgname-build
cd $pkgname-build
# LD does not link mmal with this option
LDFLAGS="${LDFLAGS//--as-needed/}"
LDFLAGS="${LDFLAGS//,,/,}"
make $_options CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS" $MAKEFLAGS
}
package() {
cd "$srcdir/$pkgname-build"
make DESTDIR="$pkgdir" PREFIX=/usr install
make $_options CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS" DESTDIR="$pkgdir" PREFIX=/usr install
}

View File

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

View File

@@ -6,7 +6,7 @@ from distutils.core import setup
if __name__ == "__main__":
setup(
name="ustreamer",
version="3.8",
version="3.14",
description="uStreamer tools",
author="Maxim Devaev",
author_email="mdevaev@gmail.com",

View File

@@ -2,9 +2,9 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/file.h>
@@ -17,27 +17,50 @@
#include "../src/libs/memsinksh.h" // No sources again
typedef struct {
uint64_t id;
long double ts;
uint8_t *data;
size_t used;
size_t allocated;
unsigned width;
unsigned height;
unsigned format;
unsigned stride;
bool online;
long double grab_ts;
long double encode_begin_ts;
long double encode_end_ts;
} tmp_frame_s;
typedef struct {
PyObject_HEAD
char *obj;
double lock_timeout;
double wait_timeout;
double drop_same_frames;
int fd;
memsink_shared_s *mem;
uint8_t *tmp_data;
size_t tmp_data_allocated;
uint64_t last_id;
PyObject *frame; // PyDict
tmp_frame_s *tmp_frame;
PyObject *dict_frame; // PyDict
} MemsinkObject;
#define MEM(_next) self->mem->_next
#define TMP(_next) self->tmp_frame->_next
static void MemsinkObject_destroy_internals(MemsinkObject *self) {
if (self->frame != NULL) {
Py_DECREF(self->frame);
self->frame = NULL;
if (self->dict_frame != NULL) {
Py_DECREF(self->dict_frame);
self->dict_frame = NULL;
}
if (self->mem != NULL) {
munmap(self->mem, sizeof(memsink_shared_s));
@@ -47,10 +70,12 @@ static void MemsinkObject_destroy_internals(MemsinkObject *self) {
close(self->fd);
self->fd = -1;
}
if (self->tmp_data) {
free(self->tmp_data);
self->tmp_data = NULL;
self->tmp_data_allocated = 0;
if (self->tmp_frame) {
if (TMP(data)) {
free(TMP(data));
}
free(self->tmp_frame);
self->tmp_frame = NULL;
}
}
@@ -58,27 +83,29 @@ static int MemsinkObject_init(MemsinkObject *self, PyObject *args, PyObject *kwa
self->lock_timeout = 1;
self->wait_timeout = 1;
static char *kws[] = {"obj", "lock_timeout", "wait_timeout", NULL};
static char *kws[] = {"obj", "lock_timeout", "wait_timeout", "drop_same_frames", NULL};
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "s|dd", kws,
&self->obj, &self->lock_timeout, &self->wait_timeout)) {
args, kwargs, "s|ddd", kws,
&self->obj, &self->lock_timeout, &self->wait_timeout, &self->drop_same_frames)) {
return -1;
}
# define SET_TIMEOUT(_timeout) { \
if (self->_timeout <= 0) { \
PyErr_SetString(PyExc_ValueError, #_timeout " must be > 0"); \
# define SET_DOUBLE(_field, _cond) { \
if (!(self->_field _cond)) { \
PyErr_SetString(PyExc_ValueError, #_field " must be " #_cond); \
return -1; \
} \
}
SET_TIMEOUT(lock_timeout);
SET_TIMEOUT(wait_timeout);
SET_DOUBLE(lock_timeout, > 0);
SET_DOUBLE(wait_timeout, > 0);
SET_DOUBLE(drop_same_frames, >= 0);
# undef CHECK_TIMEOUT
# undef SET_DOUBLE
self->tmp_data_allocated = 512 * 1024;
A_REALLOC(self->tmp_data, self->tmp_data_allocated);
A_CALLOC(self->tmp_frame, 1);
TMP(allocated) = 512 * 1024;
A_REALLOC(TMP(data), TMP(allocated));
if ((self->fd = shm_open(self->obj, O_RDWR, 0)) == -1) {
PyErr_SetFromErrno(PyExc_OSError);
@@ -102,7 +129,7 @@ static int MemsinkObject_init(MemsinkObject *self, PyObject *args, PyObject *kwa
goto error;
}
if ((self->frame = PyDict_New()) == NULL) {
if ((self->dict_frame = PyDict_New()) == NULL) {
goto error;
}
@@ -138,8 +165,6 @@ static PyObject *MemsinkObject_exit(MemsinkObject *self, PyObject *Py_UNUSED(ign
return PyObject_CallMethod((PyObject *)self, "close", "");
}
#define MEM(_next) self->mem->_next
static int wait_frame(MemsinkObject *self) {
long double deadline_ts = get_now_monotonic() + self->wait_timeout;
@@ -149,21 +174,47 @@ static int wait_frame(MemsinkObject *self) {
return -1; \
}
long double now;
do {
Py_BEGIN_ALLOW_THREADS
int retval = flock_timedwait_monotonic(self->fd, self->lock_timeout);
now = get_now_monotonic();
if (retval < 0 && errno != EWOULDBLOCK) {
RETURN_OS_ERROR;
} else if (retval == 0) {
if (MEM(magic) == MEMSINK_MAGIC && MEM(version) == MEMSINK_VERSION && MEM(id) != self->last_id) {
if (MEM(magic) == MEMSINK_MAGIC && MEM(version) == MEMSINK_VERSION && TMP(id) != MEM(id)) {
if (self->drop_same_frames > 0) {
# define CMP(_field) (TMP(_field) == MEM(_field))
if (
CMP(used)
&& CMP(width)
&& CMP(height)
&& CMP(format)
&& CMP(stride)
&& CMP(online)
&& (TMP(ts) + self->drop_same_frames > now)
&& !memcmp(TMP(data), MEM(data), MEM(used))
) {
TMP(id) = MEM(id);
goto drop;
}
# undef CMP
}
Py_BLOCK_THREADS
return 0;
}
if (flock(self->fd, LOCK_UN) < 0) {
RETURN_OS_ERROR;
}
}
drop:
if (usleep(1000) < 0) {
RETURN_OS_ERROR;
}
@@ -173,7 +224,7 @@ static int wait_frame(MemsinkObject *self) {
if (PyErr_CheckSignals() < 0) {
return -1;
}
} while (get_now_monotonic() < deadline_ts);
} while (now < deadline_ts);
# undef RETURN_OS_ERROR
@@ -192,65 +243,66 @@ static PyObject *MemsinkObject_wait_frame(MemsinkObject *self, PyObject *Py_UNUS
default: return NULL;
}
# define COPY(_type, _field) _type tmp_##_field = MEM(_field)
COPY(unsigned, width);
COPY(unsigned, height);
COPY(unsigned, format);
COPY(unsigned, stride);
COPY(bool, online);
COPY(double, grab_ts);
COPY(double, encode_begin_ts);
COPY(double, encode_end_ts);
COPY(unsigned, used);
# define COPY(_field) TMP(_field) = MEM(_field)
COPY(width);
COPY(height);
COPY(format);
COPY(stride);
COPY(online);
COPY(grab_ts);
COPY(encode_begin_ts);
COPY(encode_end_ts);
COPY(used);
# undef COPY
// Временный буффер используется для скорейшего разблокирования синка
if (self->tmp_data_allocated < MEM(used)) {
if (TMP(allocated) < MEM(used)) {
size_t size = MEM(used) + (512 * 1024);
A_REALLOC(self->tmp_data, size);
self->tmp_data_allocated = size;
A_REALLOC(TMP(data), size);
TMP(allocated) = size;
}
memcpy(self->tmp_data, MEM(data), MEM(used));
memcpy(TMP(data), MEM(data), MEM(used));
TMP(used) = MEM(used);
MEM(last_client_ts) = get_now_monotonic();
self->last_id = MEM(id);
TMP(id) = MEM(id);
TMP(ts) = get_now_monotonic();
MEM(last_client_ts) = TMP(ts);
if (flock(self->fd, LOCK_UN) < 0) {
return PyErr_SetFromErrno(PyExc_OSError);
}
PyDict_Clear(self->frame);
PyDict_Clear(self->dict_frame);
# define SET_VALUE(_key, _maker) { \
PyObject *_tmp = _maker; \
if (_tmp == NULL) { \
return NULL; \
} \
if (PyDict_SetItemString(self->frame, _key, _tmp) < 0) { \
if (PyDict_SetItemString(self->dict_frame, _key, _tmp) < 0) { \
Py_DECREF(_tmp); \
return NULL; \
} \
Py_DECREF(_tmp); \
}
# define SET_NUMBER(_key, _from, _to) SET_VALUE(#_key, Py##_to##_From##_from(TMP(_key)))
SET_VALUE("width", PyLong_FromLong(tmp_width));
SET_VALUE("height", PyLong_FromLong(tmp_height));
SET_VALUE("format", PyLong_FromLong(tmp_format));
SET_VALUE("stride", PyLong_FromLong(tmp_stride));
SET_VALUE("online", PyBool_FromLong(tmp_online));
SET_VALUE("grab_ts", PyFloat_FromDouble(tmp_grab_ts));
SET_VALUE("encode_begin_ts", PyFloat_FromDouble(tmp_encode_begin_ts));
SET_VALUE("encode_end_ts", PyFloat_FromDouble(tmp_encode_end_ts));
SET_VALUE("data", PyBytes_FromStringAndSize((const char *)self->tmp_data, tmp_used));
SET_NUMBER(width, Long, Long);
SET_NUMBER(height, Long, Long);
SET_NUMBER(format, Long, Long);
SET_NUMBER(stride, Long, Long);
SET_NUMBER(online, Long, Bool);
SET_NUMBER(grab_ts, Double, Float);
SET_NUMBER(encode_begin_ts, Double, Float);
SET_NUMBER(encode_end_ts, Double, Float);
SET_VALUE("data", PyBytes_FromStringAndSize((const char *)TMP(data), TMP(used)));
# undef SET_NUMBER
# undef SET_VALUE
Py_INCREF(self->frame);
return self->frame;
Py_INCREF(self->dict_frame);
return self->dict_frame;
}
#undef MEM
static PyObject *MemsinkObject_is_opened(MemsinkObject *self, PyObject *Py_UNUSED(ignored)) {
return PyBool_FromLong(self->mem != NULL && self->fd > 0);
}
@@ -263,16 +315,18 @@ static PyObject *MemsinkObject_is_opened(MemsinkObject *self, PyObject *Py_UNUSE
FIELD_GETTER(obj, String, Unicode)
FIELD_GETTER(lock_timeout, Double, Float)
FIELD_GETTER(wait_timeout, Double, Float)
FIELD_GETTER(drop_same_frames, Double, Float)
#undef FIELD_GETTER
static PyMethodDef MemsinkObject_methods[] = {
# define ADD_METHOD(_meth, _flags) {.ml_name = #_meth, .ml_meth = (PyCFunction)MemsinkObject_##_meth, .ml_flags = (_flags)}
ADD_METHOD(close, METH_NOARGS),
ADD_METHOD(enter, METH_NOARGS),
ADD_METHOD(exit, METH_VARARGS),
ADD_METHOD(wait_frame, METH_NOARGS),
ADD_METHOD(is_opened, METH_NOARGS),
# define ADD_METHOD(_name, _method, _flags) \
{.ml_name = _name, .ml_meth = (PyCFunction)MemsinkObject_##_method, .ml_flags = (_flags)}
ADD_METHOD("close", close, METH_NOARGS),
ADD_METHOD("__enter__", enter, METH_NOARGS),
ADD_METHOD("__exit__", exit, METH_VARARGS),
ADD_METHOD("wait_frame", wait_frame, METH_NOARGS),
ADD_METHOD("is_opened", is_opened, METH_NOARGS),
{},
# undef ADD_METHOD
};
@@ -282,6 +336,7 @@ static PyGetSetDef MemsinkObject_getsets[] = {
ADD_GETTER(obj),
ADD_GETTER(lock_timeout),
ADD_GETTER(wait_timeout),
ADD_GETTER(drop_same_frames),
{},
# undef ADD_GETTER
};
@@ -323,3 +378,6 @@ PyMODINIT_FUNC PyInit_ustreamer(void) { // cppcheck-suppress unusedFunction
return module;
}
#undef TMP
#undef MEM

View File

@@ -23,5 +23,5 @@
#pragma once
#ifndef VERSION
# define VERSION "3.8"
# define VERSION "3.14"
#endif

View File

@@ -617,7 +617,8 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser
SAY(" -K|--last-as-blank <sec> ──────────── Show the last frame received from the camera after it was disconnected,");
SAY(" but no more than specified time (or endlessly if 0 is specified).");
SAY(" If the device has not yet been online, display 'NO SIGNAL' or the image");
SAY(" specified by option --blank. Default: disabled.\n");
SAY(" specified by option --blank. Default: disabled.");
SAY(" Note: currently this option has no effect on memory sinks.\n");
SAY(" -l|--slowdown ─────────────────────── Slowdown capturing to 1 FPS or less when no stream or sink clients");
SAY(" are connected. Useful to reduce CPU consumption. Default: disabled.\n");
SAY(" --device-timeout <sec> ────────────── Timeout for device querying. Default: %u.\n", dev->timeout);

View File

@@ -25,11 +25,25 @@
static workers_pool_s *_stream_init_loop(stream_s *stream);
static workers_pool_s *_stream_init_one(stream_s *stream);
static bool _stream_expose_frame(stream_s *stream, frame_s *frame, unsigned captured_fps);
static void _stream_expose_frame(stream_s *stream, frame_s *frame, unsigned captured_fps);
#define RUN(_next) stream->run->_next
#define SINK_PUT(_sink, _frame) { \
if (stream->_sink && memsink_server_check(stream->_sink, _frame)) {\
memsink_server_put(stream->_sink, _frame); \
} \
}
#ifdef WITH_OMX
# define H264_PUT(_frame, _vcsm_handle, _force_key) { \
if (RUN(h264)) { \
h264_stream_process(RUN(h264), _frame, _vcsm_handle, _force_key); \
} \
}
#endif
stream_s *stream_init(device_s *dev, encoder_s *enc) {
stream_runtime_s *run;
@@ -194,16 +208,9 @@ void stream_loop(stream_s *stream) {
workers_pool_assign(pool, ready_wr);
LOG_DEBUG("Assigned new frame in buffer %d to worker %s", buf_index, ready_wr->name);
if (stream->raw_sink) {
if (memsink_server_check(stream->raw_sink, &hw->raw)) {
memsink_server_put(stream->raw_sink, &hw->raw);
}
}
SINK_PUT(raw_sink, &hw->raw);
# ifdef WITH_OMX
if (RUN(h264)) {
h264_stream_process(RUN(h264), &hw->raw, hw->vcsm_handle, h264_force_key);
}
H264_PUT(&hw->raw, hw->vcsm_handle, h264_force_key);
# endif
}
} else if (buf_index != -2) { // -2 for broken frame
@@ -253,18 +260,7 @@ static workers_pool_s *_stream_init_loop(stream_s *stream) {
LOG_DEBUG("%s: stream->run->stop=%d", __FUNCTION__, atomic_load(&RUN(stop)));
while (!atomic_load(&RUN(stop))) {
if (_stream_expose_frame(stream, NULL, 0)) {
if (stream->raw_sink) {
if (memsink_server_check(stream->raw_sink, stream->blank)) {
memsink_server_put(stream->raw_sink, stream->blank);
}
}
# ifdef WITH_OMX
if (RUN(h264)) {
h264_stream_process(RUN(h264), stream->blank, -1, false);
}
# endif
}
_stream_expose_frame(stream, NULL, 0);
if (access(stream->dev->path, R_OK|W_OK) < 0) {
if (access_error != errno) {
@@ -308,11 +304,10 @@ static workers_pool_s *_stream_init_one(stream_s *stream) {
return NULL;
}
static bool _stream_expose_frame(stream_s *stream, frame_s *frame, unsigned captured_fps) {
static void _stream_expose_frame(stream_s *stream, frame_s *frame, unsigned captured_fps) {
# define VID(_next) RUN(video->_next)
frame_s *new = NULL;
bool changed = false;
A_MUTEX_LOCK(&VID(mutex));
@@ -322,7 +317,11 @@ static bool _stream_expose_frame(stream_s *stream, frame_s *frame, unsigned capt
LOG_DEBUG("Exposed ALIVE video frame");
} else {
if (VID(frame->online)) { // Если переходим из online в offline
if (VID(frame->used == 0)) {
new = stream->blank; // Инициализация
RUN(last_as_blank_ts) = 0;
} else if (VID(frame->online)) { // Если переходим из online в offline
if (stream->last_as_blank < 0) { // Если last_as_blank выключен, просто покажем старую картинку
new = stream->blank;
LOG_INFO("Changed video frame to BLANK");
@@ -332,6 +331,7 @@ static bool _stream_expose_frame(stream_s *stream, frame_s *frame, unsigned capt
} else { // last_as_blank == 0 - показываем последний фрейм вечно
LOG_INFO("Freezed last ALIVE video frame forever");
}
} else if (stream->last_as_blank < 0) {
new = stream->blank;
// LOG_INFO("Changed video frame to BLANK");
@@ -350,26 +350,28 @@ static bool _stream_expose_frame(stream_s *stream, frame_s *frame, unsigned capt
if (new) {
frame_copy(new, VID(frame));
changed = true;
} else if (VID(frame->used) == 0) { // Инициализация
frame_copy(stream->blank, VID(frame));
frame = NULL;
changed = true;
}
VID(frame->online) = frame;
VID(frame->online) = (bool)frame;
VID(captured_fps) = captured_fps;
atomic_store(&VID(updated), true);
A_MUTEX_UNLOCK(&VID(mutex));
if (changed && stream->sink) {
if (memsink_server_check(stream->sink, VID(frame))) {
memsink_server_put(stream->sink, VID(frame));
}
}
new = (frame ? frame : stream->blank);
SINK_PUT(sink, new);
return changed;
if (frame == NULL) {
SINK_PUT(raw_sink, stream->blank);
# ifdef WITH_OMX
H264_PUT(stream->blank, -1, false);
# endif
}
# undef VID
}
#ifdef WITH_OMX
# undef H264_PUT
#endif
#undef SINK_PUT
#undef RUN