Compare commits

...

14 Commits
v3.6 ... v3.7

Author SHA1 Message Date
Devaev Maxim
40abe73391 Bump version: 3.6 → 3.7 2021-01-21 11:51:34 +03:00
Devaev Maxim
e4f1ef654f lint fix 2021-01-21 11:34:24 +03:00
Devaev Maxim
fa6afb96ce check usleep() retval 2021-01-21 11:22:04 +03:00
Devaev Maxim
c3f98b34f2 python builddeps 2021-01-21 09:27:29 +03:00
Devaev Maxim
b7b3e8e87d python: handle signals 2021-01-21 09:18:09 +03:00
Devaev Maxim
94383a2d54 moved python module 2021-01-21 07:19:10 +03:00
Devaev Maxim
184a4879eb gzip force 2021-01-21 05:23:22 +03:00
Devaev Maxim
bf48908c59 fixed another segfault 2021-01-21 05:22:35 +03:00
Devaev Maxim
66afbccf21 fixed segfault 2021-01-21 03:01:50 +03:00
Devaev Maxim
97dbe59aea handle usleep error 2021-01-21 02:56:43 +03:00
Devaev Maxim
d874fdeaec gitignore 2021-01-20 16:06:42 +03:00
Devaev Maxim
97e3938d56 trying to eliminate memory leaks 2021-01-20 16:06:32 +03:00
Devaev Maxim
87c7e8063f fixed shm umask 2021-01-20 15:17:13 +03:00
Devaev Maxim
fe5beb0114 fixed python destdir 2021-01-20 14:24:12 +03:00
14 changed files with 157 additions and 90 deletions

View File

@@ -1,7 +1,7 @@
[bumpversion]
commit = True
tag = True
current_version = 3.6
current_version = 3.7
parse = (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?)?
serialize =
{major}.{minor}
@@ -10,7 +10,7 @@ serialize =
search = VERSION "{current_version}"
replace = VERSION "{new_version}"
[bumpversion:file:src/python/setup.py]
[bumpversion:file:python/setup.py]
search = version="{current_version}"
replace = version="{new_version}"

2
.gitignore vendored
View File

@@ -6,8 +6,10 @@
/pkg/arch/ustreamer-*.pkg.tar.xz
/pkg/arch/ustreamer-*.pkg.tar.zst
/build/
/python/build/
/config.mk
/vgcore.*
/ustreamer
/ustreamer-dump
/*.so
/*.sock

View File

@@ -7,6 +7,7 @@ PREFIX ?= /usr/local
MANPREFIX ?= $(PREFIX)/share/man
CC ?= gcc
PY ?= python3
CFLAGS ?= -O3 -MD
LDFLAGS ?=
@@ -87,10 +88,10 @@ install: $(USTR) $(DUMP)
install -m755 $(DUMP) $(DESTDIR)$(PREFIX)/bin/$(DUMP)
install -m644 man/$(USTR).1 $(DESTDIR)$(MANPREFIX)/man1/$(USTR).1
install -m644 man/$(DUMP).1 $(DESTDIR)$(MANPREFIX)/man1/$(DUMP).1
gzip $(DESTDIR)$(MANPREFIX)/man1/$(USTR).1
gzip $(DESTDIR)$(MANPREFIX)/man1/$(DUMP).1
gzip -f $(DESTDIR)$(MANPREFIX)/man1/$(USTR).1
gzip -f $(DESTDIR)$(MANPREFIX)/man1/$(DUMP).1
ifneq ($(call optbool,$(WITH_PYTHON)),)
cd src/python && python3 setup.py install --prefix=$(PREFIX) --root=$(DESTDIR)
cd python && $(PY) setup.py install --prefix=$(PREFIX) --root=$(if $(DESTDIR),$(DESTDIR),/)
endif
@@ -132,8 +133,9 @@ $(BUILD)/%.o: %.c
python:
ifneq ($(call optbool,$(WITH_PYTHON)),)
cd src/python && python3 setup.py build
ln -sf src/python/build/lib.*/*.so .
$(info == PY_BUILD ustreamer-*.so)
@ cd python && $(PY) setup.py build
@ ln -sf python/build/lib.*/*.so .
else
@ true
endif
@@ -180,7 +182,7 @@ clean-all: linters clean
-it $(LINTERS_IMAGE) bash -c "cd src && rm -rf linters/{.tox,.mypy_cache}"
clean:
rm -rf pkg/arch/pkg pkg/arch/src pkg/arch/v*.tar.gz pkg/arch/ustreamer-*.pkg.tar.{xz,zst}
rm -rf $(USTR) $(DUMP) $(BUILD) src/python/build vgcore.* *.sock *.so
rm -rf $(USTR) $(DUMP) $(BUILD) python/build vgcore.* *.sock *.so
.PHONY: python linters

View File

@@ -1,12 +1,12 @@
[tox]
envlist = cppcheck, flake8, pylint, mypy, vulture, htmlhint
envlist = cppcheck-src, cppcheck-python, flake8, pylint, mypy, vulture, htmlhint
skipsdist = true
[testenv]
basepython = python3.9
changedir = /src
[testenv:cppcheck]
[testenv:cppcheck-src]
whitelist_externals = cppcheck
commands = cppcheck \
--force \
@@ -17,33 +17,49 @@ commands = cppcheck \
--suppress=assignmentInAssert \
--suppress=variableScope \
--inline-suppr \
--library=python \
-DCHAR_BIT=8 \
-DWITH_OMX \
-DWITH_GPIO \
src
src python
[testenv:cppcheck-python]
whitelist_externals = cppcheck
commands = cppcheck \
--force \
--std=c11 \
--error-exitcode=1 \
--quiet \
--enable=warning,unusedFunction,portability,performance,style \
--suppress=assignmentInAssert \
--suppress=variableScope \
--inline-suppr \
--library=python \
-DCHAR_BIT=8 \
python
[testenv:flake8]
whitelist_externals = bash
commands = bash -c 'flake8 --config=linters/flake8.ini tools/*.py' src/python/*.py
commands = bash -c 'flake8 --config=linters/flake8.ini tools/*.py' python/*.py
deps =
flake8
flake8-quotes
[testenv:pylint]
whitelist_externals = bash
commands = bash -c 'pylint --rcfile=linters/pylint.ini --output-format=colorized --reports=no tools/*.py src/python/*.py'
commands = bash -c 'pylint --rcfile=linters/pylint.ini --output-format=colorized --reports=no tools/*.py python/*.py'
deps =
pylint
[testenv:mypy]
whitelist_externals = bash
commands = bash -c 'mypy --config-file=linters/mypy.ini tools/*.py src/python/*.py'
commands = bash -c 'mypy --config-file=linters/mypy.ini tools/*.py python/*.py'
deps =
mypy
[testenv:vulture]
whitelist_externals = bash
commands = bash -c 'vulture tools/*.py src/python/*.py'
commands = bash -c 'vulture tools/*.py python/*.py'
deps =
vulture

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.6" "January 2021"
.TH USTREAMER-DUMP 1 "version 3.7" "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.6" "November 2020"
.TH USTREAMER 1 "version 3.7" "November 2020"
.SH NAME
ustreamer \- stream MJPG video from any V4L2 device to the network

View File

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

View File

@@ -6,7 +6,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=ustreamer
PKG_VERSION:=3.6
PKG_VERSION:=3.7
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.6",
version="3.7",
description="uStreamer tools",
author="Maxim Devaev",
author_email="mdevaev@gmail.com",
@@ -17,6 +17,10 @@ if __name__ == "__main__":
libraries=["rt", "m", "pthread"],
undef_macros=["NDEBUG"],
sources=["ustreamer.c"],
depends=[
"../src/libs/tools.h",
"../src/libs/memsinksh.h",
],
),
],
)

View File

@@ -13,8 +13,8 @@
#include <Python.h>
#include "../libs/tools.h" // Just a header without C-sources
#include "../libs/memsinksh.h" // No sources again
#include "../src/libs/tools.h" // Just a header without C-sources
#include "../src/libs/memsinksh.h" // No sources again
typedef struct {
@@ -29,10 +29,16 @@ typedef struct {
uint8_t *tmp_data;
size_t tmp_data_allocated;
uint64_t last_id;
PyObject *frame; // PyDict
} MemsinkObject;
static void MemsinkObject_destroy_internals(MemsinkObject *self) {
if (self->frame != NULL) {
Py_DECREF(self->frame);
self->frame = NULL;
}
if (self->mem != NULL) {
munmap(self->mem, sizeof(memsink_shared_s));
self->mem = NULL;
@@ -96,6 +102,10 @@ static int MemsinkObject_init(MemsinkObject *self, PyObject *args, PyObject *kwa
goto error;
}
if ((self->frame = PyDict_New()) == NULL) {
goto error;
}
return 0;
error:
@@ -128,53 +138,61 @@ 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;
# define RETURN_OS_ERROR { \
Py_BLOCK_THREADS \
PyErr_SetFromErrno(PyExc_OSError); \
return -1; \
}
do {
Py_BEGIN_ALLOW_THREADS
int retval = flock_timedwait_monotonic(self->fd, self->lock_timeout);
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) {
Py_BLOCK_THREADS
return 0;
}
if (flock(self->fd, LOCK_UN) < 0) {
RETURN_OS_ERROR;
}
}
if (usleep(1000) < 0) {
RETURN_OS_ERROR;
}
Py_END_ALLOW_THREADS
if (PyErr_CheckSignals() < 0) {
return -1;
}
} while (get_now_monotonic() < deadline_ts);
# undef RETURN_OS_ERROR
return -2;
}
static PyObject *MemsinkObject_wait_frame(MemsinkObject *self, PyObject *Py_UNUSED(ignored)) {
if (self->mem == NULL || self->fd <= 0) {
PyErr_SetString(PyExc_RuntimeError, "Closed");
return NULL;
}
bool found = false;
bool failed = false;
Py_BEGIN_ALLOW_THREADS
long double deadline_ts = get_now_monotonic() + self->wait_timeout;
do {
int retval = flock_timedwait_monotonic(self->fd, self->lock_timeout);
if (retval < 0 && errno != EWOULDBLOCK) {
failed = true;
break;
} else if (retval == 0) {
if (
self->mem->magic == MEMSINK_MAGIC
&& self->mem->version == MEMSINK_VERSION
&& self->mem->id != self->last_id
) {
found = true;
break;
}
if (flock(self->fd, LOCK_UN) < 0) {
failed = true;
break;
}
}
errno = 0;
usleep(1000);
if (errno == EINTR) {
failed = true;
break;
}
} while (get_now_monotonic() < deadline_ts);
Py_END_ALLOW_THREADS
if (failed) {
return PyErr_SetFromErrno(PyExc_OSError);
}
if (!found) {
Py_RETURN_NONE;
switch (wait_frame(self)) {
case 0: break;
case -2: Py_RETURN_NONE;
default: return NULL;
}
# define COPY(_type, _field) _type tmp_##_field = self->mem->_field
# define COPY(_type, _field) _type tmp_##_field = MEM(_field)
COPY(unsigned, width);
COPY(unsigned, height);
COPY(unsigned, format);
@@ -187,35 +205,52 @@ static PyObject *MemsinkObject_wait_frame(MemsinkObject *self, PyObject *Py_UNUS
# undef COPY
// Временный буффер используется для скорейшего разблокирования синка
if (self->tmp_data_allocated < self->mem->used) {
size_t size = self->mem->used + (512 * 1024);
if (self->tmp_data_allocated < MEM(used)) {
size_t size = MEM(used) + (512 * 1024);
A_REALLOC(self->tmp_data, size);
self->tmp_data_allocated = size;
}
memcpy(self->tmp_data, self->mem->data, self->mem->used);
memcpy(self->tmp_data, MEM(data), MEM(used));
self->mem->last_client_ts = get_now_monotonic();
self->last_id = self->mem->id;
MEM(last_client_ts) = get_now_monotonic();
self->last_id = MEM(id);
if (flock(self->fd, LOCK_UN) < 0) {
return PyErr_SetFromErrno(PyExc_OSError);
}
PyObject *frame = PyDict_New();
# define SET_VALUE(_field, _from, _to) PyDict_SetItemString(frame, #_field, Py##_to##_From##_from(tmp_##_field))
SET_VALUE(width, Long, Long);
SET_VALUE(height, Long, Long);
SET_VALUE(format, Long, Long);
SET_VALUE(stride, Long, Long);
SET_VALUE(online, Long, Bool);
SET_VALUE(grab_ts, Double, Float);
SET_VALUE(encode_begin_ts, Double, Float);
SET_VALUE(encode_end_ts, Double, Float);
PyDict_Clear(self->frame);
# define SET_VALUE(_key, _maker) { \
PyObject *_tmp = _maker; \
if (_tmp == NULL) { \
return NULL; \
} \
if (PyDict_SetItemString(self->frame, _key, _tmp) < 0) { \
Py_DECREF(_tmp); \
return NULL; \
} \
Py_DECREF(_tmp); \
}
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));
# undef SET_VALUE
PyDict_SetItemString(frame, "data", PyBytes_FromStringAndSize((const char *)self->tmp_data, tmp_used));
return frame;
Py_INCREF(self->frame);
return self->frame;
}
#undef MEM
static PyObject *MemsinkObject_is_opened(MemsinkObject *self, PyObject *Py_UNUSED(ignored)) {
return PyBool_FromLong(self->mem != NULL && self->fd > 0);
}

View File

@@ -23,5 +23,5 @@
#pragma once
#ifndef VERSION
# define VERSION "3.6"
# define VERSION "3.7"
#endif

View File

@@ -40,7 +40,11 @@ memsink_s *memsink_init(
LOG_INFO("Using %s-sink: %s", name, obj);
if ((sink->fd = shm_open(sink->obj, (server ? O_RDWR | O_CREAT : O_RDWR), mode)) == -1) {
mode_t mask = umask(0);
sink->fd = shm_open(sink->obj, (server ? O_RDWR | O_CREAT : O_RDWR), mode);
umask(mask);
if (sink->fd == -1) {
umask(mask);
LOG_PERROR("%s-sink: Can't open shared memory", name);
goto error;
}

View File

@@ -135,7 +135,9 @@ INLINE int flock_timedwait_monotonic(int fd, long double timeout) {
if (retval == 0 || errno != EWOULDBLOCK || get_now_monotonic() > deadline_ts) {
break;
}
usleep(1000);
if (usleep(1000) < 0) {
break;
}
}
return retval;
}

View File

@@ -221,18 +221,20 @@ static int _vcos_semwait(VCOS_SEMAPHORE_T *sem) {
if (sem_status == VCOS_SUCCESS) {
return 0;
} else if (sem_status != VCOS_EAGAIN || get_now_monotonic() > deadline_ts) {
goto error;
break;
}
if (usleep(1000) < 0) {
break;
}
usleep(1000);
}
error:
switch (sem_status) {
case VCOS_EAGAIN: LOG_ERROR("Can't wait VCOS semaphore: EAGAIN (timeout)"); break;
case VCOS_EINVAL: LOG_ERROR("Can't wait VCOS semaphore: EINVAL"); break;
default: LOG_ERROR("Can't wait VCOS semaphore: %d", sem_status); break;
}
return -1;
switch (sem_status) {
case VCOS_EAGAIN: LOG_ERROR("Can't wait VCOS semaphore: EAGAIN (timeout)"); break;
case VCOS_EINVAL: LOG_ERROR("Can't wait VCOS semaphore: EINVAL"); break;
default: LOG_ERROR("Can't wait VCOS semaphore: %d", sem_status); break;
}
return -1;
# else
return (vcos_semaphore_wait(sem) == VCOS_SUCCESS ? 0 : -1);
# endif