mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-27 04:06:30 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb07444b70 | ||
|
|
a8ba2f7364 | ||
|
|
171bcb315a | ||
|
|
911df1af15 | ||
|
|
a165ff4523 | ||
|
|
5cfb3b1e60 | ||
|
|
82b3b78238 | ||
|
|
8a82ff6691 | ||
|
|
2807678551 | ||
|
|
e96e0aa73c | ||
|
|
d06c2619b2 | ||
|
|
6b18455d11 | ||
|
|
217977d9cb | ||
|
|
b9f186e47c | ||
|
|
d7d56f3536 | ||
|
|
2e3c764369 | ||
|
|
7236e53813 | ||
|
|
e7f7350405 | ||
|
|
81f0266a87 | ||
|
|
eb1a2e3695 | ||
|
|
6cfd3739d3 |
@@ -1,7 +1,7 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
current_version = 3.10
|
current_version = 3.16
|
||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?)?
|
parse = (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?)?
|
||||||
serialize =
|
serialize =
|
||||||
{major}.{minor}
|
{major}.{minor}
|
||||||
|
|||||||
@@ -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 echo "Server = http://mirror.yandex.ru/archlinux/\$repo/os/\$arch" > /etc/pacman.d/mirrorlist
|
||||||
|
|
||||||
RUN pacman -Syu --noconfirm \
|
RUN pacman -Syu --noconfirm \
|
||||||
&& pacman -S --needed --noconfirm \
|
&& pacman -S --needed --noconfirm \
|
||||||
base \
|
|
||||||
base-devel \
|
|
||||||
vim \
|
vim \
|
||||||
git \
|
git \
|
||||||
libjpeg \
|
libjpeg \
|
||||||
@@ -17,7 +18,8 @@ RUN pacman -Syu --noconfirm \
|
|||||||
python-tox \
|
python-tox \
|
||||||
cppcheck \
|
cppcheck \
|
||||||
npm \
|
npm \
|
||||||
&& (pacman -Sc --noconfirm || true)
|
&& (pacman -Sc --noconfirm || true) \
|
||||||
|
&& rm -rf /var/cache/pacman/pkg/*
|
||||||
|
|
||||||
RUN npm install htmlhint -g
|
RUN npm install htmlhint -g
|
||||||
|
|
||||||
|
|||||||
@@ -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 3.10" "January 2021"
|
.TH USTREAMER-DUMP 1 "version 3.16" "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 3.10" "November 2020"
|
.TH USTREAMER 1 "version 3.16" "November 2020"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
ustreamer \- stream MJPG video from any V4L2 device to the network
|
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'.
|
Path to JPEG file that will be shown when the device is disconnected during the streaming. Default: black screen 640x480 with 'NO SIGNAL'.
|
||||||
.TP
|
.TP
|
||||||
.BR \-K\ \fIsec ", " \-\-last\-as\-blank\ \fIsec
|
.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
|
.TP
|
||||||
.BR \-l ", " \-\-slowdown
|
.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.
|
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"
|
.SS "Image control options"
|
||||||
.TP
|
.TP
|
||||||
.BR \-\-image\-default
|
.BR \-\-image\-default
|
||||||
Reset all image settings bellow to default. Default: no change.
|
Reset all image settings below to default. Default: no change.
|
||||||
.TP
|
.TP
|
||||||
.BR \-\-brightness\ \fIN ", " \fIauto ", " \fIdefault
|
.BR \-\-brightness\ \fIN ", " \fIauto ", " \fIdefault
|
||||||
Set brightness. Default: no change.
|
Set brightness. Default: no change.
|
||||||
@@ -166,8 +166,8 @@ Bind to this TCP port. Default: 8080.
|
|||||||
.TP
|
.TP
|
||||||
.BR \-U\ \fIpath ", " \-\-unix\ \fIpath
|
.BR \-U\ \fIpath ", " \-\-unix\ \fIpath
|
||||||
Bind to UNIX domain socket. Default: disabled.
|
Bind to UNIX domain socket. Default: disabled.
|
||||||
.tp
|
.TP
|
||||||
.br \-d ", " \-\-unix\-rm
|
.BR \-d ", " \-\-unix\-rm
|
||||||
Try to remove old unix socket file before binding. default: disabled.
|
Try to remove old unix socket file before binding. default: disabled.
|
||||||
.TP
|
.TP
|
||||||
.BR \-M\ \fImode ", " \-\-unix\-mode\ \fImode
|
.BR \-M\ \fImode ", " \-\-unix\-mode\ \fImode
|
||||||
@@ -234,6 +234,12 @@ Client TTL. Default: 10.
|
|||||||
.TP
|
.TP
|
||||||
.BR \-\-h264\-sink\-timeout\ \fIsec
|
.BR \-\-h264\-sink\-timeout\ \fIsec
|
||||||
Timeout for lock. Default: 1.
|
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"
|
.SS "Process options"
|
||||||
.TP
|
.TP
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
|
|
||||||
pkgname=ustreamer
|
pkgname=ustreamer
|
||||||
pkgver=3.10
|
pkgver=3.16
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Lightweight and fast MJPG-HTTP streamer"
|
pkgdesc="Lightweight and fast MJPG-HTTP streamer"
|
||||||
url="https://github.com/pikvm/ustreamer"
|
url="https://github.com/pikvm/ustreamer"
|
||||||
@@ -28,20 +28,21 @@ if [ -e /opt/vc/include/IL/OMX_Core.h ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# LD does not link mmal with this option
|
||||||
|
# This DOESN'T affect setup.py
|
||||||
|
LDFLAGS="${LDFLAGS//--as-needed/}"
|
||||||
|
export LDFLAGS="${LDFLAGS//,,/,}"
|
||||||
|
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
cd "$srcdir"
|
cd "$srcdir"
|
||||||
rm -rf $pkgname-build
|
rm -rf $pkgname-build
|
||||||
cp -r $pkgname $pkgname-build
|
cp -r $pkgname $pkgname-build
|
||||||
cd $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
|
make $_options CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS" $MAKEFLAGS
|
||||||
}
|
}
|
||||||
|
|
||||||
package() {
|
package() {
|
||||||
cd "$srcdir/$pkgname-build"
|
cd "$srcdir/$pkgname-build"
|
||||||
make $_options DESTDIR="$pkgdir" PREFIX=/usr install
|
make $_options CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS" DESTDIR="$pkgdir" PREFIX=/usr install
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=ustreamer
|
PKG_NAME:=ustreamer
|
||||||
PKG_VERSION:=3.10
|
PKG_VERSION:=3.16
|
||||||
PKG_RELEASE:=1
|
PKG_RELEASE:=1
|
||||||
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from distutils.core import setup
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
setup(
|
setup(
|
||||||
name="ustreamer",
|
name="ustreamer",
|
||||||
version="3.10",
|
version="3.16",
|
||||||
description="uStreamer tools",
|
description="uStreamer tools",
|
||||||
author="Maxim Devaev",
|
author="Maxim Devaev",
|
||||||
author_email="mdevaev@gmail.com",
|
author_email="mdevaev@gmail.com",
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/file.h>
|
#include <sys/file.h>
|
||||||
@@ -17,27 +17,51 @@
|
|||||||
#include "../src/libs/memsinksh.h" // No sources again
|
#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;
|
||||||
|
bool key;
|
||||||
|
|
||||||
|
long double grab_ts;
|
||||||
|
long double encode_begin_ts;
|
||||||
|
long double encode_end_ts;
|
||||||
|
} tmp_frame_s;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
|
|
||||||
char *obj;
|
char *obj;
|
||||||
double lock_timeout;
|
double lock_timeout;
|
||||||
double wait_timeout;
|
double wait_timeout;
|
||||||
|
double drop_same_frames;
|
||||||
|
|
||||||
int fd;
|
int fd;
|
||||||
memsink_shared_s *mem;
|
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;
|
} MemsinkObject;
|
||||||
|
|
||||||
|
|
||||||
|
#define MEM(_next) self->mem->_next
|
||||||
|
#define TMP(_next) self->tmp_frame->_next
|
||||||
|
|
||||||
|
|
||||||
static void MemsinkObject_destroy_internals(MemsinkObject *self) {
|
static void MemsinkObject_destroy_internals(MemsinkObject *self) {
|
||||||
if (self->frame != NULL) {
|
if (self->dict_frame != NULL) {
|
||||||
Py_DECREF(self->frame);
|
Py_DECREF(self->dict_frame);
|
||||||
self->frame = NULL;
|
self->dict_frame = NULL;
|
||||||
}
|
}
|
||||||
if (self->mem != NULL) {
|
if (self->mem != NULL) {
|
||||||
munmap(self->mem, sizeof(memsink_shared_s));
|
munmap(self->mem, sizeof(memsink_shared_s));
|
||||||
@@ -47,10 +71,12 @@ static void MemsinkObject_destroy_internals(MemsinkObject *self) {
|
|||||||
close(self->fd);
|
close(self->fd);
|
||||||
self->fd = -1;
|
self->fd = -1;
|
||||||
}
|
}
|
||||||
if (self->tmp_data) {
|
if (self->tmp_frame) {
|
||||||
free(self->tmp_data);
|
if (TMP(data)) {
|
||||||
self->tmp_data = NULL;
|
free(TMP(data));
|
||||||
self->tmp_data_allocated = 0;
|
}
|
||||||
|
free(self->tmp_frame);
|
||||||
|
self->tmp_frame = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,27 +84,29 @@ static int MemsinkObject_init(MemsinkObject *self, PyObject *args, PyObject *kwa
|
|||||||
self->lock_timeout = 1;
|
self->lock_timeout = 1;
|
||||||
self->wait_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(
|
if (!PyArg_ParseTupleAndKeywords(
|
||||||
args, kwargs, "s|dd", kws,
|
args, kwargs, "s|ddd", kws,
|
||||||
&self->obj, &self->lock_timeout, &self->wait_timeout)) {
|
&self->obj, &self->lock_timeout, &self->wait_timeout, &self->drop_same_frames)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
# define SET_TIMEOUT(_timeout) { \
|
# define SET_DOUBLE(_field, _cond) { \
|
||||||
if (self->_timeout <= 0) { \
|
if (!(self->_field _cond)) { \
|
||||||
PyErr_SetString(PyExc_ValueError, #_timeout " must be > 0"); \
|
PyErr_SetString(PyExc_ValueError, #_field " must be " #_cond); \
|
||||||
return -1; \
|
return -1; \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
SET_TIMEOUT(lock_timeout);
|
SET_DOUBLE(lock_timeout, > 0);
|
||||||
SET_TIMEOUT(wait_timeout);
|
SET_DOUBLE(wait_timeout, > 0);
|
||||||
|
SET_DOUBLE(drop_same_frames, >= 0);
|
||||||
|
|
||||||
# undef CHECK_TIMEOUT
|
# undef SET_DOUBLE
|
||||||
|
|
||||||
self->tmp_data_allocated = 512 * 1024;
|
A_CALLOC(self->tmp_frame, 1);
|
||||||
A_REALLOC(self->tmp_data, self->tmp_data_allocated);
|
TMP(allocated) = 512 * 1024;
|
||||||
|
A_REALLOC(TMP(data), TMP(allocated));
|
||||||
|
|
||||||
if ((self->fd = shm_open(self->obj, O_RDWR, 0)) == -1) {
|
if ((self->fd = shm_open(self->obj, O_RDWR, 0)) == -1) {
|
||||||
PyErr_SetFromErrno(PyExc_OSError);
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
@@ -102,7 +130,7 @@ static int MemsinkObject_init(MemsinkObject *self, PyObject *args, PyObject *kwa
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((self->frame = PyDict_New()) == NULL) {
|
if ((self->dict_frame = PyDict_New()) == NULL) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,8 +166,6 @@ static PyObject *MemsinkObject_exit(MemsinkObject *self, PyObject *Py_UNUSED(ign
|
|||||||
return PyObject_CallMethod((PyObject *)self, "close", "");
|
return PyObject_CallMethod((PyObject *)self, "close", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MEM(_next) self->mem->_next
|
|
||||||
|
|
||||||
static int wait_frame(MemsinkObject *self) {
|
static int wait_frame(MemsinkObject *self) {
|
||||||
long double deadline_ts = get_now_monotonic() + self->wait_timeout;
|
long double deadline_ts = get_now_monotonic() + self->wait_timeout;
|
||||||
|
|
||||||
@@ -149,21 +175,48 @@ static int wait_frame(MemsinkObject *self) {
|
|||||||
return -1; \
|
return -1; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long double now;
|
||||||
do {
|
do {
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
|
||||||
int retval = flock_timedwait_monotonic(self->fd, self->lock_timeout);
|
int retval = flock_timedwait_monotonic(self->fd, self->lock_timeout);
|
||||||
|
now = get_now_monotonic();
|
||||||
|
|
||||||
if (retval < 0 && errno != EWOULDBLOCK) {
|
if (retval < 0 && errno != EWOULDBLOCK) {
|
||||||
RETURN_OS_ERROR;
|
RETURN_OS_ERROR;
|
||||||
|
|
||||||
} else if (retval == 0) {
|
} 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)
|
||||||
|
&& CMP(key)
|
||||||
|
&& (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
|
Py_BLOCK_THREADS
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flock(self->fd, LOCK_UN) < 0) {
|
if (flock(self->fd, LOCK_UN) < 0) {
|
||||||
RETURN_OS_ERROR;
|
RETURN_OS_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drop:
|
||||||
|
|
||||||
if (usleep(1000) < 0) {
|
if (usleep(1000) < 0) {
|
||||||
RETURN_OS_ERROR;
|
RETURN_OS_ERROR;
|
||||||
}
|
}
|
||||||
@@ -173,7 +226,7 @@ static int wait_frame(MemsinkObject *self) {
|
|||||||
if (PyErr_CheckSignals() < 0) {
|
if (PyErr_CheckSignals() < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} while (get_now_monotonic() < deadline_ts);
|
} while (now < deadline_ts);
|
||||||
|
|
||||||
# undef RETURN_OS_ERROR
|
# undef RETURN_OS_ERROR
|
||||||
|
|
||||||
@@ -192,65 +245,68 @@ static PyObject *MemsinkObject_wait_frame(MemsinkObject *self, PyObject *Py_UNUS
|
|||||||
default: return NULL;
|
default: return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
# define COPY(_type, _field) _type tmp_##_field = MEM(_field)
|
# define COPY(_field) TMP(_field) = MEM(_field)
|
||||||
COPY(unsigned, width);
|
COPY(width);
|
||||||
COPY(unsigned, height);
|
COPY(height);
|
||||||
COPY(unsigned, format);
|
COPY(format);
|
||||||
COPY(unsigned, stride);
|
COPY(stride);
|
||||||
COPY(bool, online);
|
COPY(online);
|
||||||
COPY(double, grab_ts);
|
COPY(key);
|
||||||
COPY(double, encode_begin_ts);
|
COPY(grab_ts);
|
||||||
COPY(double, encode_end_ts);
|
COPY(encode_begin_ts);
|
||||||
COPY(unsigned, used);
|
COPY(encode_end_ts);
|
||||||
|
COPY(used);
|
||||||
# undef COPY
|
# undef COPY
|
||||||
|
|
||||||
// Временный буффер используется для скорейшего разблокирования синка
|
if (TMP(allocated) < MEM(used)) {
|
||||||
if (self->tmp_data_allocated < MEM(used)) {
|
|
||||||
size_t size = MEM(used) + (512 * 1024);
|
size_t size = MEM(used) + (512 * 1024);
|
||||||
A_REALLOC(self->tmp_data, size);
|
A_REALLOC(TMP(data), size);
|
||||||
self->tmp_data_allocated = 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();
|
TMP(id) = MEM(id);
|
||||||
self->last_id = MEM(id);
|
TMP(ts) = get_now_monotonic();
|
||||||
|
MEM(last_client_ts) = TMP(ts);
|
||||||
|
|
||||||
if (flock(self->fd, LOCK_UN) < 0) {
|
if (flock(self->fd, LOCK_UN) < 0) {
|
||||||
return PyErr_SetFromErrno(PyExc_OSError);
|
return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDict_Clear(self->frame);
|
PyDict_Clear(self->dict_frame);
|
||||||
|
|
||||||
# define SET_VALUE(_key, _maker) { \
|
# define SET_VALUE(_key, _maker) { \
|
||||||
PyObject *_tmp = _maker; \
|
PyObject *_tmp = _maker; \
|
||||||
if (_tmp == NULL) { \
|
if (_tmp == NULL) { \
|
||||||
return NULL; \
|
return NULL; \
|
||||||
} \
|
} \
|
||||||
if (PyDict_SetItemString(self->frame, _key, _tmp) < 0) { \
|
if (PyDict_SetItemString(self->dict_frame, _key, _tmp) < 0) { \
|
||||||
Py_DECREF(_tmp); \
|
Py_DECREF(_tmp); \
|
||||||
return NULL; \
|
return NULL; \
|
||||||
} \
|
} \
|
||||||
Py_DECREF(_tmp); \
|
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_NUMBER(width, Long, Long);
|
||||||
SET_VALUE("height", PyLong_FromLong(tmp_height));
|
SET_NUMBER(height, Long, Long);
|
||||||
SET_VALUE("format", PyLong_FromLong(tmp_format));
|
SET_NUMBER(format, Long, Long);
|
||||||
SET_VALUE("stride", PyLong_FromLong(tmp_stride));
|
SET_NUMBER(stride, Long, Long);
|
||||||
SET_VALUE("online", PyBool_FromLong(tmp_online));
|
SET_NUMBER(online, Long, Bool);
|
||||||
SET_VALUE("grab_ts", PyFloat_FromDouble(tmp_grab_ts));
|
SET_NUMBER(key, Long, Bool);
|
||||||
SET_VALUE("encode_begin_ts", PyFloat_FromDouble(tmp_encode_begin_ts));
|
SET_NUMBER(grab_ts, Double, Float);
|
||||||
SET_VALUE("encode_end_ts", PyFloat_FromDouble(tmp_encode_end_ts));
|
SET_NUMBER(encode_begin_ts, Double, Float);
|
||||||
SET_VALUE("data", PyBytes_FromStringAndSize((const char *)self->tmp_data, tmp_used));
|
SET_NUMBER(encode_end_ts, Double, Float);
|
||||||
|
SET_VALUE("data", PyBytes_FromStringAndSize((const char *)TMP(data), TMP(used)));
|
||||||
|
|
||||||
|
# undef SET_NUMBER
|
||||||
# undef SET_VALUE
|
# undef SET_VALUE
|
||||||
|
|
||||||
Py_INCREF(self->frame);
|
Py_INCREF(self->dict_frame);
|
||||||
return self->frame;
|
return self->dict_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef MEM
|
|
||||||
|
|
||||||
static PyObject *MemsinkObject_is_opened(MemsinkObject *self, PyObject *Py_UNUSED(ignored)) {
|
static PyObject *MemsinkObject_is_opened(MemsinkObject *self, PyObject *Py_UNUSED(ignored)) {
|
||||||
return PyBool_FromLong(self->mem != NULL && self->fd > 0);
|
return PyBool_FromLong(self->mem != NULL && self->fd > 0);
|
||||||
}
|
}
|
||||||
@@ -263,16 +319,18 @@ static PyObject *MemsinkObject_is_opened(MemsinkObject *self, PyObject *Py_UNUSE
|
|||||||
FIELD_GETTER(obj, String, Unicode)
|
FIELD_GETTER(obj, String, Unicode)
|
||||||
FIELD_GETTER(lock_timeout, Double, Float)
|
FIELD_GETTER(lock_timeout, Double, Float)
|
||||||
FIELD_GETTER(wait_timeout, Double, Float)
|
FIELD_GETTER(wait_timeout, Double, Float)
|
||||||
|
FIELD_GETTER(drop_same_frames, Double, Float)
|
||||||
|
|
||||||
#undef FIELD_GETTER
|
#undef FIELD_GETTER
|
||||||
|
|
||||||
static PyMethodDef MemsinkObject_methods[] = {
|
static PyMethodDef MemsinkObject_methods[] = {
|
||||||
# define ADD_METHOD(_meth, _flags) {.ml_name = #_meth, .ml_meth = (PyCFunction)MemsinkObject_##_meth, .ml_flags = (_flags)}
|
# define ADD_METHOD(_name, _method, _flags) \
|
||||||
ADD_METHOD(close, METH_NOARGS),
|
{.ml_name = _name, .ml_meth = (PyCFunction)MemsinkObject_##_method, .ml_flags = (_flags)}
|
||||||
ADD_METHOD(enter, METH_NOARGS),
|
ADD_METHOD("close", close, METH_NOARGS),
|
||||||
ADD_METHOD(exit, METH_VARARGS),
|
ADD_METHOD("__enter__", enter, METH_NOARGS),
|
||||||
ADD_METHOD(wait_frame, METH_NOARGS),
|
ADD_METHOD("__exit__", exit, METH_VARARGS),
|
||||||
ADD_METHOD(is_opened, METH_NOARGS),
|
ADD_METHOD("wait_frame", wait_frame, METH_NOARGS),
|
||||||
|
ADD_METHOD("is_opened", is_opened, METH_NOARGS),
|
||||||
{},
|
{},
|
||||||
# undef ADD_METHOD
|
# undef ADD_METHOD
|
||||||
};
|
};
|
||||||
@@ -282,6 +340,7 @@ static PyGetSetDef MemsinkObject_getsets[] = {
|
|||||||
ADD_GETTER(obj),
|
ADD_GETTER(obj),
|
||||||
ADD_GETTER(lock_timeout),
|
ADD_GETTER(lock_timeout),
|
||||||
ADD_GETTER(wait_timeout),
|
ADD_GETTER(wait_timeout),
|
||||||
|
ADD_GETTER(drop_same_frames),
|
||||||
{},
|
{},
|
||||||
# undef ADD_GETTER
|
# undef ADD_GETTER
|
||||||
};
|
};
|
||||||
@@ -323,3 +382,6 @@ PyMODINIT_FUNC PyInit_ustreamer(void) { // cppcheck-suppress unusedFunction
|
|||||||
|
|
||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef TMP
|
||||||
|
#undef MEM
|
||||||
|
|||||||
@@ -213,6 +213,8 @@ static int _dump_sink(const char *sink_name, unsigned sink_timeout, _output_cont
|
|||||||
unsigned fps_accum = 0;
|
unsigned fps_accum = 0;
|
||||||
long long fps_second = 0;
|
long long fps_second = 0;
|
||||||
|
|
||||||
|
long double last_ts = 0;
|
||||||
|
|
||||||
while (!global_stop) {
|
while (!global_stop) {
|
||||||
int error = memsink_client_get(sink, frame);
|
int error = memsink_client_get(sink, frame);
|
||||||
if (error == 0) {
|
if (error == 0) {
|
||||||
@@ -220,11 +222,12 @@ static int _dump_sink(const char *sink_name, unsigned sink_timeout, _output_cont
|
|||||||
const long long now_second = floor_ms(now);
|
const long long now_second = floor_ms(now);
|
||||||
|
|
||||||
char fourcc_str[8];
|
char fourcc_str[8];
|
||||||
LOG_VERBOSE("Frame: size=%zu, resolution=%ux%u, fourcc=%s, stride=%u, online=%d, latency=%.3Lf",
|
LOG_VERBOSE("Frame: size=%zu, res=%ux%u, fourcc=%s, stride=%u, online=%d, key=%d, latency=%.3Lf, diff=%.3Lf",
|
||||||
frame->used, frame->width, frame->height,
|
frame->used, frame->width, frame->height,
|
||||||
fourcc_to_string(frame->format, fourcc_str, 8),
|
fourcc_to_string(frame->format, fourcc_str, 8),
|
||||||
frame->stride, frame->online,
|
frame->stride, frame->online, frame->key,
|
||||||
now - frame->grab_ts);
|
now - frame->grab_ts, (last_ts ? now - last_ts : 0));
|
||||||
|
last_ts = now;
|
||||||
|
|
||||||
LOG_DEBUG(" grab_ts=%.3Lf, encode_begin_ts=%.3Lf, encode_end_ts=%.3Lf",
|
LOG_DEBUG(" grab_ts=%.3Lf, encode_begin_ts=%.3Lf, encode_end_ts=%.3Lf",
|
||||||
frame->grab_ts, frame->encode_begin_ts, frame->encode_end_ts);
|
frame->grab_ts, frame->encode_begin_ts, frame->encode_end_ts);
|
||||||
|
|||||||
@@ -23,5 +23,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef VERSION
|
#ifndef VERSION
|
||||||
# define VERSION "3.10"
|
# define VERSION "3.16"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ void frame_copy_meta(const frame_s *src, frame_s *dest) {
|
|||||||
COPY(format);
|
COPY(format);
|
||||||
COPY(stride);
|
COPY(stride);
|
||||||
COPY(online);
|
COPY(online);
|
||||||
|
COPY(key);
|
||||||
COPY(grab_ts);
|
COPY(grab_ts);
|
||||||
COPY(encode_begin_ts);
|
COPY(encode_begin_ts);
|
||||||
COPY(encode_end_ts);
|
COPY(encode_end_ts);
|
||||||
@@ -98,6 +99,7 @@ bool frame_compare(const frame_s *a, const frame_s *b) {
|
|||||||
&& CMP(format)
|
&& CMP(format)
|
||||||
&& CMP(stride)
|
&& CMP(stride)
|
||||||
&& CMP(online)
|
&& CMP(online)
|
||||||
|
&& CMP(key)
|
||||||
&& !memcmp(a->data, b->data, b->used)
|
&& !memcmp(a->data, b->data, b->used)
|
||||||
);
|
);
|
||||||
# undef CMP
|
# undef CMP
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ typedef struct {
|
|||||||
// https://medium.com/@oleg.shipitko/what-does-stride-mean-in-image-processing-bba158a72bcd
|
// https://medium.com/@oleg.shipitko/what-does-stride-mean-in-image-processing-bba158a72bcd
|
||||||
|
|
||||||
bool online;
|
bool online;
|
||||||
|
bool key;
|
||||||
|
|
||||||
long double grab_ts;
|
long double grab_ts;
|
||||||
long double encode_begin_ts;
|
long double encode_begin_ts;
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ bool memsink_server_check(memsink_s *sink, const frame_s *frame) {
|
|||||||
sink->has_clients = (sink->mem->last_client_ts + sink->client_ttl > get_now_monotonic());
|
sink->has_clients = (sink->mem->last_client_ts + sink->client_ttl > get_now_monotonic());
|
||||||
|
|
||||||
# define NEQ(_field) (sink->mem->_field != frame->_field)
|
# define NEQ(_field) (sink->mem->_field != frame->_field)
|
||||||
bool retval = (sink->has_clients || NEQ(width) || NEQ(height) || NEQ(format) || NEQ(stride) || NEQ(online));
|
bool retval = (sink->has_clients || NEQ(width) || NEQ(height) || NEQ(format) || NEQ(stride) || NEQ(online) || NEQ(key));
|
||||||
# undef NEQ
|
# undef NEQ
|
||||||
|
|
||||||
if (flock(sink->fd, LOCK_UN) < 0) {
|
if (flock(sink->fd, LOCK_UN) < 0) {
|
||||||
@@ -142,6 +142,7 @@ int memsink_server_put(memsink_s *sink, const frame_s *frame) {
|
|||||||
COPY(format);
|
COPY(format);
|
||||||
COPY(stride);
|
COPY(stride);
|
||||||
COPY(online);
|
COPY(online);
|
||||||
|
COPY(key);
|
||||||
COPY(grab_ts);
|
COPY(grab_ts);
|
||||||
COPY(encode_begin_ts);
|
COPY(encode_begin_ts);
|
||||||
COPY(encode_end_ts);
|
COPY(encode_end_ts);
|
||||||
@@ -195,6 +196,7 @@ int memsink_client_get(memsink_s *sink, frame_s *frame) { // cppcheck-suppress u
|
|||||||
COPY(format);
|
COPY(format);
|
||||||
COPY(stride);
|
COPY(stride);
|
||||||
COPY(online);
|
COPY(online);
|
||||||
|
COPY(key);
|
||||||
COPY(grab_ts);
|
COPY(grab_ts);
|
||||||
COPY(encode_begin_ts);
|
COPY(encode_begin_ts);
|
||||||
COPY(encode_end_ts);
|
COPY(encode_end_ts);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#define MEMSINK_MAGIC ((uint64_t)0xCAFEBABECAFEBABE)
|
#define MEMSINK_MAGIC ((uint64_t)0xCAFEBABECAFEBABE)
|
||||||
#define MEMSINK_VERSION ((uint32_t)1)
|
#define MEMSINK_VERSION ((uint32_t)2)
|
||||||
|
|
||||||
#ifndef CFG_MEMSINK_MAX_DATA
|
#ifndef CFG_MEMSINK_MAX_DATA
|
||||||
# define CFG_MEMSINK_MAX_DATA 33554432
|
# define CFG_MEMSINK_MAX_DATA 33554432
|
||||||
@@ -49,6 +49,7 @@ typedef struct {
|
|||||||
unsigned format;
|
unsigned format;
|
||||||
unsigned stride;
|
unsigned stride;
|
||||||
bool online;
|
bool online;
|
||||||
|
bool key;
|
||||||
|
|
||||||
long double grab_ts;
|
long double grab_ts;
|
||||||
long double encode_begin_ts;
|
long double encode_begin_ts;
|
||||||
|
|||||||
@@ -212,7 +212,8 @@ int h264_encoder_prepare(h264_encoder_s *enc, const frame_s *frame, bool zero_co
|
|||||||
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_PEAK_RATE, enc->bitrate * 1000);
|
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_PEAK_RATE, enc->bitrate * 1000);
|
||||||
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_MIN_QUANT, 16);
|
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_MIN_QUANT, 16);
|
||||||
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_MAX_QUANT, 34);
|
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_MAX_QUANT, 34);
|
||||||
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_FRAME_LIMIT_BITS, 1000000);
|
// Этот параметр с этим значением фризит кодирование изображения из черно-белой консоли
|
||||||
|
// SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_FRAME_LIMIT_BITS, 1000000);
|
||||||
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_H264_AU_DELIMITERS, MMAL_FALSE);
|
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_H264_AU_DELIMITERS, MMAL_FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -347,6 +348,7 @@ static int _h264_encoder_compress_raw(h264_encoder_s *enc, const frame_s *src, i
|
|||||||
}
|
}
|
||||||
|
|
||||||
frame_append_data(dest, out->data, out->length);
|
frame_append_data(dest, out->data, out->length);
|
||||||
|
dest->key = out->flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME;
|
||||||
|
|
||||||
eos = out->flags & MMAL_BUFFER_HEADER_FLAG_EOS;
|
eos = out->flags & MMAL_BUFFER_HEADER_FLAG_EOS;
|
||||||
mmal_buffer_header_release(out);
|
mmal_buffer_header_release(out);
|
||||||
|
|||||||
@@ -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(" -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(" 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(" 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(" -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(" are connected. Useful to reduce CPU consumption. Default: disabled.\n");
|
||||||
SAY(" --device-timeout <sec> ────────────── Timeout for device querying. Default: %u.\n", dev->timeout);
|
SAY(" --device-timeout <sec> ────────────── Timeout for device querying. Default: %u.\n", dev->timeout);
|
||||||
|
|||||||
@@ -25,11 +25,25 @@
|
|||||||
|
|
||||||
static workers_pool_s *_stream_init_loop(stream_s *stream);
|
static workers_pool_s *_stream_init_loop(stream_s *stream);
|
||||||
static workers_pool_s *_stream_init_one(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 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_s *stream_init(device_s *dev, encoder_s *enc) {
|
||||||
stream_runtime_s *run;
|
stream_runtime_s *run;
|
||||||
@@ -194,16 +208,9 @@ void stream_loop(stream_s *stream) {
|
|||||||
workers_pool_assign(pool, ready_wr);
|
workers_pool_assign(pool, ready_wr);
|
||||||
LOG_DEBUG("Assigned new frame in buffer %d to worker %s", buf_index, ready_wr->name);
|
LOG_DEBUG("Assigned new frame in buffer %d to worker %s", buf_index, ready_wr->name);
|
||||||
|
|
||||||
if (stream->raw_sink) {
|
SINK_PUT(raw_sink, &hw->raw);
|
||||||
if (memsink_server_check(stream->raw_sink, &hw->raw)) {
|
|
||||||
memsink_server_put(stream->raw_sink, &hw->raw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# ifdef WITH_OMX
|
# ifdef WITH_OMX
|
||||||
if (RUN(h264)) {
|
H264_PUT(&hw->raw, hw->vcsm_handle, h264_force_key);
|
||||||
h264_stream_process(RUN(h264), &hw->raw, hw->vcsm_handle, h264_force_key);
|
|
||||||
}
|
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
} else if (buf_index != -2) { // -2 for broken frame
|
} 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)));
|
LOG_DEBUG("%s: stream->run->stop=%d", __FUNCTION__, atomic_load(&RUN(stop)));
|
||||||
|
|
||||||
while (!atomic_load(&RUN(stop))) {
|
while (!atomic_load(&RUN(stop))) {
|
||||||
if (_stream_expose_frame(stream, NULL, 0)) {
|
_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
|
|
||||||
}
|
|
||||||
|
|
||||||
if (access(stream->dev->path, R_OK|W_OK) < 0) {
|
if (access(stream->dev->path, R_OK|W_OK) < 0) {
|
||||||
if (access_error != errno) {
|
if (access_error != errno) {
|
||||||
@@ -308,11 +304,10 @@ static workers_pool_s *_stream_init_one(stream_s *stream) {
|
|||||||
return NULL;
|
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)
|
# define VID(_next) RUN(video->_next)
|
||||||
|
|
||||||
frame_s *new = NULL;
|
frame_s *new = NULL;
|
||||||
bool changed = false;
|
|
||||||
|
|
||||||
A_MUTEX_LOCK(&VID(mutex));
|
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");
|
LOG_DEBUG("Exposed ALIVE video frame");
|
||||||
|
|
||||||
} else {
|
} 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 выключен, просто покажем старую картинку
|
if (stream->last_as_blank < 0) { // Если last_as_blank выключен, просто покажем старую картинку
|
||||||
new = stream->blank;
|
new = stream->blank;
|
||||||
LOG_INFO("Changed video frame to 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 - показываем последний фрейм вечно
|
} else { // last_as_blank == 0 - показываем последний фрейм вечно
|
||||||
LOG_INFO("Freezed last ALIVE video frame forever");
|
LOG_INFO("Freezed last ALIVE video frame forever");
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (stream->last_as_blank < 0) {
|
} else if (stream->last_as_blank < 0) {
|
||||||
new = stream->blank;
|
new = stream->blank;
|
||||||
// LOG_INFO("Changed video frame to 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) {
|
if (new) {
|
||||||
frame_copy(new, VID(frame));
|
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;
|
VID(captured_fps) = captured_fps;
|
||||||
atomic_store(&VID(updated), true);
|
atomic_store(&VID(updated), true);
|
||||||
|
|
||||||
A_MUTEX_UNLOCK(&VID(mutex));
|
A_MUTEX_UNLOCK(&VID(mutex));
|
||||||
|
|
||||||
if (changed && stream->sink) {
|
new = (frame ? frame : stream->blank);
|
||||||
if (memsink_server_check(stream->sink, VID(frame))) {
|
SINK_PUT(sink, new);
|
||||||
memsink_server_put(stream->sink, VID(frame));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return changed;
|
if (frame == NULL) {
|
||||||
|
SINK_PUT(raw_sink, stream->blank);
|
||||||
|
# ifdef WITH_OMX
|
||||||
|
H264_PUT(stream->blank, -1, false);
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
# undef VID
|
# undef VID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_OMX
|
||||||
|
# undef H264_PUT
|
||||||
|
#endif
|
||||||
|
#undef SINK_PUT
|
||||||
#undef RUN
|
#undef RUN
|
||||||
|
|||||||
Reference in New Issue
Block a user