mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-27 04:06:30 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a97f08eac8 | ||
|
|
92ff097d7e | ||
|
|
0eb0370bd3 | ||
|
|
b329dfed1f | ||
|
|
62dc2e5ad5 | ||
|
|
7ea81fa627 | ||
|
|
8ddf77a3d3 | ||
|
|
fde89465ac | ||
|
|
aa1d78a3cd | ||
|
|
6dfe077775 | ||
|
|
dd6dc866a6 | ||
|
|
3bf68884f5 | ||
|
|
e1b2eceea5 | ||
|
|
7ce11fecb6 | ||
|
|
cbc6145977 | ||
|
|
24f7fb797b | ||
|
|
71f1d397bf | ||
|
|
00e83c155e | ||
|
|
1f186a0afe | ||
|
|
3c7075d0d2 | ||
|
|
c1d7bd1595 | ||
|
|
04d1d3d5a6 | ||
|
|
01bc0529e7 | ||
|
|
181231f3ff | ||
|
|
58569f0315 | ||
|
|
5903fcf718 | ||
|
|
1a820b23a5 | ||
|
|
67b767b152 |
@@ -1,14 +1,22 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
current_version = 3.23
|
current_version = 3.26
|
||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?)?
|
parse = (?P<major>\d+)\.(?P<minor>\d+)
|
||||||
serialize =
|
serialize =
|
||||||
{major}.{minor}
|
{major}.{minor}
|
||||||
|
|
||||||
[bumpversion:file:src/libs/config.h]
|
[bumpversion:file:src/libs/config.h]
|
||||||
search = VERSION "{current_version}"
|
parse = (?P<major>\d+)
|
||||||
replace = VERSION "{new_version}"
|
serialize = {major}
|
||||||
|
search = VERSION_MAJOR {current_version}
|
||||||
|
replace = VERSION_MAJOR {new_version}
|
||||||
|
|
||||||
|
[bumpversion:file:./src/libs/config.h]
|
||||||
|
parse = <major>\d+\.(?P<minor>\d+)
|
||||||
|
serialize = {minor}
|
||||||
|
search = VERSION_MINOR {current_version}
|
||||||
|
replace = VERSION_MINOR {new_version}
|
||||||
|
|
||||||
[bumpversion:file:python/setup.py]
|
[bumpversion:file:python/setup.py]
|
||||||
search = version="{current_version}"
|
search = version="{current_version}"
|
||||||
|
|||||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -5,11 +5,12 @@
|
|||||||
/pkg/arch/v*.tar.gz
|
/pkg/arch/v*.tar.gz
|
||||||
/pkg/arch/ustreamer-*.pkg.tar.xz
|
/pkg/arch/ustreamer-*.pkg.tar.xz
|
||||||
/pkg/arch/ustreamer-*.pkg.tar.zst
|
/pkg/arch/ustreamer-*.pkg.tar.zst
|
||||||
/build/
|
/src/build/
|
||||||
|
/src/*.bin
|
||||||
/python/build/
|
/python/build/
|
||||||
/config.mk
|
|
||||||
/vgcore.*
|
|
||||||
/ustreamer
|
/ustreamer
|
||||||
/ustreamer-dump
|
/ustreamer-dump
|
||||||
/*.so
|
/config.mk
|
||||||
|
/vgcore.*
|
||||||
/*.sock
|
/*.sock
|
||||||
|
/*.so
|
||||||
|
|||||||
168
Makefile
168
Makefile
@@ -1,7 +1,5 @@
|
|||||||
-include config.mk
|
-include config.mk
|
||||||
|
|
||||||
USTR ?= ustreamer
|
|
||||||
DUMP ?= ustreamer-dump
|
|
||||||
DESTDIR ?=
|
DESTDIR ?=
|
||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
MANPREFIX ?= $(PREFIX)/share/man
|
MANPREFIX ?= $(PREFIX)/share/man
|
||||||
@@ -14,147 +12,71 @@ LDFLAGS ?=
|
|||||||
RPI_VC_HEADERS ?= /opt/vc/include
|
RPI_VC_HEADERS ?= /opt/vc/include
|
||||||
RPI_VC_LIBS ?= /opt/vc/lib
|
RPI_VC_LIBS ?= /opt/vc/lib
|
||||||
|
|
||||||
BUILD ?= build
|
export
|
||||||
|
|
||||||
LINTERS_IMAGE ?= $(USTR)-linters
|
_LINTERS_IMAGE ?= ustreamer-linters
|
||||||
|
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
_CFLAGS = -MD -c -std=c11 -Wall -Wextra -D_GNU_SOURCE $(CFLAGS)
|
|
||||||
_LDFLAGS = $(LDFLAGS)
|
|
||||||
|
|
||||||
_COMMON_LIBS = -lm -ljpeg -pthread -lrt
|
|
||||||
|
|
||||||
_USTR_LIBS = $(_COMMON_LIBS) -levent -levent_pthreads
|
|
||||||
_USTR_SRCS = $(shell ls \
|
|
||||||
src/libs/*.c \
|
|
||||||
src/ustreamer/*.c \
|
|
||||||
src/ustreamer/http/*.c \
|
|
||||||
src/ustreamer/data/*.c \
|
|
||||||
src/ustreamer/encoders/cpu/*.c \
|
|
||||||
src/ustreamer/encoders/hw/*.c \
|
|
||||||
)
|
|
||||||
|
|
||||||
_DUMP_LIBS = $(_COMMON_LIBS)
|
|
||||||
_DUMP_SRCS = $(shell ls \
|
|
||||||
src/libs/*.c \
|
|
||||||
src/dump/*.c \
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
define optbool
|
define optbool
|
||||||
$(filter $(shell echo $(1) | tr A-Z a-z), yes on 1)
|
$(filter $(shell echo $(1) | tr A-Z a-z), yes on 1)
|
||||||
endef
|
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)
|
|
||||||
_USTR_SRCS += $(shell ls \
|
|
||||||
src/ustreamer/encoders/omx/*.c \
|
|
||||||
src/ustreamer/h264/*.c \
|
|
||||||
)
|
|
||||||
endif
|
|
||||||
|
|
||||||
|
|
||||||
ifneq ($(call optbool,$(WITH_GPIO)),)
|
|
||||||
_USTR_LIBS += -lgpiod
|
|
||||||
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
|
|
||||||
endif
|
|
||||||
|
|
||||||
|
|
||||||
WITH_SETPROCTITLE ?= 1
|
|
||||||
ifneq ($(call optbool,$(WITH_SETPROCTITLE)),)
|
|
||||||
ifeq ($(shell uname -s | tr A-Z a-z),linux)
|
|
||||||
_USTR_LIBS += -lbsd
|
|
||||||
endif
|
|
||||||
override _CFLAGS += -DWITH_SETPROCTITLE
|
|
||||||
endif
|
|
||||||
|
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
all: $(USTR) $(DUMP) python
|
all:
|
||||||
|
+ $(MAKE) apps
|
||||||
|
|
||||||
install: all
|
|
||||||
mkdir -p $(DESTDIR)$(PREFIX)/bin $(DESTDIR)$(MANPREFIX)/man1
|
|
||||||
install -m755 $(USTR) $(DESTDIR)$(PREFIX)/bin/$(USTR)
|
|
||||||
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 -f $(DESTDIR)$(MANPREFIX)/man1/$(USTR).1
|
|
||||||
gzip -f $(DESTDIR)$(MANPREFIX)/man1/$(DUMP).1
|
|
||||||
ifneq ($(call optbool,$(WITH_PYTHON)),)
|
ifneq ($(call optbool,$(WITH_PYTHON)),)
|
||||||
cd python && $(PY) setup.py install --prefix=$(PREFIX) --root=$(if $(DESTDIR),$(DESTDIR),/)
|
+ $(MAKE) python
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
install-strip: install
|
apps:
|
||||||
strip $(DESTDIR)$(PREFIX)/bin/$(USTR)
|
$(MAKE) -C src
|
||||||
strip $(DESTDIR)$(PREFIX)/bin/$(DUMP)
|
@ ln -sf src/ustreamer.bin ustreamer
|
||||||
|
@ ln -sf src/ustreamer-dump.bin ustreamer-dump
|
||||||
|
|
||||||
regen:
|
|
||||||
tools/make-jpeg-h.py src/ustreamer/data/blank.jpeg src/ustreamer/data/blank_jpeg.c BLANK
|
|
||||||
tools/make-html-h.py src/ustreamer/data/index.html src/ustreamer/data/index_html.c INDEX
|
|
||||||
|
|
||||||
|
|
||||||
$(USTR): $(_USTR_SRCS:%.c=$(BUILD)/%.o)
|
|
||||||
# $(info ========================================)
|
|
||||||
$(info == LD $@)
|
|
||||||
@ $(CC) $^ -o $@ $(_LDFLAGS) $(_USTR_LIBS)
|
|
||||||
# $(info :: CC = $(CC))
|
|
||||||
# $(info :: LIBS = $(_USTR_LIBS))
|
|
||||||
# $(info :: CFLAGS = $(_CFLAGS))
|
|
||||||
# $(info :: LDFLAGS = $(_LDFLAGS))
|
|
||||||
|
|
||||||
|
|
||||||
$(DUMP): $(_DUMP_SRCS:%.c=$(BUILD)/%.o)
|
|
||||||
# $(info ========================================)
|
|
||||||
$(info == LD $@)
|
|
||||||
@ $(CC) $^ -o $@ $(_LDFLAGS) $(_DUMP_LIBS)
|
|
||||||
# $(info :: CC = $(CC))
|
|
||||||
# $(info :: LIBS = $(_DUMP_LIBS))
|
|
||||||
# $(info :: CFLAGS = $(_CFLAGS))
|
|
||||||
# $(info :: LDFLAGS = $(_LDFLAGS))
|
|
||||||
|
|
||||||
|
|
||||||
$(BUILD)/%.o: %.c
|
|
||||||
$(info -- CC $<)
|
|
||||||
@ mkdir -p $(dir $@) || true
|
|
||||||
@ $(CC) $< -o $@ $(_CFLAGS)
|
|
||||||
|
|
||||||
|
|
||||||
python:
|
python:
|
||||||
ifneq ($(call optbool,$(WITH_PYTHON)),)
|
$(MAKE) -C python
|
||||||
$(info == PY_BUILD ustreamer-*.so)
|
|
||||||
@ cd python && $(PY) setup.py build
|
|
||||||
@ ln -sf python/build/lib.*/*.so .
|
@ ln -sf python/build/lib.*/*.so .
|
||||||
else
|
|
||||||
@ true
|
|
||||||
|
install: all
|
||||||
|
$(MAKE) -C src install
|
||||||
|
ifneq ($(call optbool,$(WITH_PYTHON)),)
|
||||||
|
$(MAKE) -C python install
|
||||||
endif
|
endif
|
||||||
|
mkdir -p $(DESTDIR)$(MANPREFIX)/man1
|
||||||
|
for man in $(shell ls man); do \
|
||||||
|
install -m644 man/$$man $(DESTDIR)$(MANPREFIX)/man1/$$man; \
|
||||||
|
gzip -f $(DESTDIR)$(MANPREFIX)/man1/$$man; \
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
install-strip: install
|
||||||
|
$(MAKE) -C src install-strip
|
||||||
|
|
||||||
|
|
||||||
|
regen:
|
||||||
|
tools/$(MAKE)-jpeg-h.py src/ustreamer/data/blank.jpeg src/ustreamer/data/blank_jpeg.c BLANK
|
||||||
|
tools/$(MAKE)-html-h.py src/ustreamer/data/index.html src/ustreamer/data/index_html.c INDEX
|
||||||
|
|
||||||
|
|
||||||
release:
|
release:
|
||||||
make clean
|
$(MAKE) clean
|
||||||
make tox
|
$(MAKE) tox
|
||||||
make push
|
$(MAKE) push
|
||||||
make bump V=$(V)
|
$(MAKE) bump V=$(V)
|
||||||
make push
|
$(MAKE) push
|
||||||
make clean
|
$(MAKE) clean
|
||||||
|
|
||||||
|
|
||||||
tox: linters
|
tox: linters
|
||||||
time docker run --rm \
|
time docker run --rm \
|
||||||
--volume `pwd`:/src:ro \
|
--volume `pwd`:/src:ro \
|
||||||
--volume `pwd`/linters:/src/linters:rw \
|
--volume `pwd`/linters:/src/linters:rw \
|
||||||
-t $(LINTERS_IMAGE) bash -c " \
|
-t $(_LINTERS_IMAGE) bash -c " \
|
||||||
cd /src \
|
cd /src \
|
||||||
&& tox -q -c linters/tox.ini $(if $(E),-e $(E),-p auto) \
|
&& tox -q -c linters/tox.ini $(if $(E),-e $(E),-p auto) \
|
||||||
"
|
"
|
||||||
@@ -164,7 +86,7 @@ linters:
|
|||||||
docker build \
|
docker build \
|
||||||
$(if $(call optbool,$(NC)),--no-cache,) \
|
$(if $(call optbool,$(NC)),--no-cache,) \
|
||||||
--rm \
|
--rm \
|
||||||
--tag $(LINTERS_IMAGE) \
|
--tag $(_LINTERS_IMAGE) \
|
||||||
-f linters/Dockerfile linters
|
-f linters/Dockerfile linters
|
||||||
|
|
||||||
|
|
||||||
@@ -180,14 +102,14 @@ push:
|
|||||||
clean-all: linters clean
|
clean-all: linters clean
|
||||||
- docker run --rm \
|
- docker run --rm \
|
||||||
--volume `pwd`:/src \
|
--volume `pwd`:/src \
|
||||||
-it $(LINTERS_IMAGE) bash -c "cd src && rm -rf linters/{.tox,.mypy_cache}"
|
-it $(_LINTERS_IMAGE) bash -c "cd src && rm -rf linters/{.tox,.mypy_cache}"
|
||||||
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf pkg/arch/pkg pkg/arch/src pkg/arch/v*.tar.gz pkg/arch/ustreamer-*.pkg.tar.{xz,zst}
|
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) python/build vgcore.* *.sock *.so
|
rm -f ustreamer ustreamer-dump *.so
|
||||||
|
$(MAKE) -C src clean
|
||||||
|
$(MAKE) -C python clean
|
||||||
|
|
||||||
|
|
||||||
.PHONY: python linters
|
.PHONY: python linters
|
||||||
|
|
||||||
|
|
||||||
_OBJS = $(_USTR_SRCS:%.c=$(BUILD)/%.o) $(_DUMP_SRCS:%.c=$(BUILD)/%.o)
|
|
||||||
-include $(_OBJS:%.o=%.d)
|
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ v4l2 utilities provide the tools to manage USB webcam setting and information. S
|
|||||||
* List of available video devices: `v4l2-ctl --list-devices`.
|
* List of available video devices: `v4l2-ctl --list-devices`.
|
||||||
* List available control settings: `v4l2-ctl -d /dev/video0 --list-ctrls`.
|
* List available control settings: `v4l2-ctl -d /dev/video0 --list-ctrls`.
|
||||||
* List available video formats: `v4l2-ctl -d /dev/video0 --list-formats-ext`.
|
* List available video formats: `v4l2-ctl -d /dev/video0 --list-formats-ext`.
|
||||||
* Read the current setting: `v4l2-ctl d /dev/video0 --get-ctrl=exposure_auto`.
|
* Read the current setting: `v4l2-ctl -d /dev/video0 --get-ctrl=exposure_auto`.
|
||||||
* Change the setting value: `v4l2-ctl d /dev/video0 --set-ctrl=exposure_auto=1`.
|
* Change the setting value: `v4l2-ctl -d /dev/video0 --set-ctrl=exposure_auto=1`.
|
||||||
|
|
||||||
[Here](https://www.kurokesu.com/main/2016/01/16/manual-usb-camera-settings-in-linux/) you can find more examples. Documentation is available in [`man v4l2-ctl`](https://www.mankier.com/1/v4l2-ctl).
|
[Here](https://www.kurokesu.com/main/2016/01/16/manual-usb-camera-settings-in-linux/) you can find more examples. Documentation is available in [`man v4l2-ctl`](https://www.mankier.com/1/v4l2-ctl).
|
||||||
|
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ V4L2 предоставляет ряд официальных утилит дл
|
|||||||
* Вывести список видеоустройств: `v4l2-ctl --list-devices`.
|
* Вывести список видеоустройств: `v4l2-ctl --list-devices`.
|
||||||
* Вывести список доступных контролов устройства: `v4l2-ctl -d /dev/video0 --list-ctrls`.
|
* Вывести список доступных контролов устройства: `v4l2-ctl -d /dev/video0 --list-ctrls`.
|
||||||
* Вывести список доступных форматов видео: `v4l2-ctl -d /dev/video0 --list-formats-ext`.
|
* Вывести список доступных форматов видео: `v4l2-ctl -d /dev/video0 --list-formats-ext`.
|
||||||
* Показать текущее значение контрола: `v4l2-ctl d /dev/video0 --get-ctrl=exposure_auto`.
|
* Показать текущее значение контрола: `v4l2-ctl -d /dev/video0 --get-ctrl=exposure_auto`.
|
||||||
* Изменить значение контрола: `v4l2-ctl d /dev/video0 --set-ctrl=exposure_auto=1`.
|
* Изменить значение контрола: `v4l2-ctl -d /dev/video0 --set-ctrl=exposure_auto=1`.
|
||||||
|
|
||||||
Больше примеров вы можете найти [здесь](https://www.kurokesu.com/main/2016/01/16/manual-usb-camera-settings-in-linux/), а документацию в [`man v4l2-ctl`](https://www.mankier.com/1/v4l2-ctl).
|
Больше примеров вы можете найти [здесь](https://www.kurokesu.com/main/2016/01/16/manual-usb-camera-settings-in-linux/), а документацию в [`man v4l2-ctl`](https://www.mankier.com/1/v4l2-ctl).
|
||||||
|
|
||||||
|
|||||||
3
linters/cppcheck.h
Normal file
3
linters/cppcheck.h
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#define CHAR_BIT 8
|
||||||
|
#define WITH_OMX
|
||||||
|
#define WITH_GPIO
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
[tox]
|
[tox]
|
||||||
envlist = cppcheck-src, cppcheck-python, flake8, pylint, mypy, vulture, htmlhint
|
envlist = cppcheck, flake8, pylint, mypy, vulture, htmlhint
|
||||||
skipsdist = true
|
skipsdist = true
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
basepython = python3.9
|
basepython = python3.9
|
||||||
changedir = /src
|
changedir = /src
|
||||||
|
|
||||||
[testenv:cppcheck-src]
|
[testenv:cppcheck]
|
||||||
whitelist_externals = cppcheck
|
whitelist_externals = cppcheck
|
||||||
commands = cppcheck \
|
commands = cppcheck \
|
||||||
--force \
|
--force \
|
||||||
@@ -18,25 +18,8 @@ commands = cppcheck \
|
|||||||
--suppress=variableScope \
|
--suppress=variableScope \
|
||||||
--inline-suppr \
|
--inline-suppr \
|
||||||
--library=python \
|
--library=python \
|
||||||
-DCHAR_BIT=8 \
|
--include=linters/cppcheck.h \
|
||||||
-DWITH_OMX \
|
src python/ustreamer.c janus/rtp.h janus/rtp.c janus/plugin.c
|
||||||
-DWITH_GPIO \
|
|
||||||
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]
|
[testenv:flake8]
|
||||||
whitelist_externals = bash
|
whitelist_externals = bash
|
||||||
|
|||||||
@@ -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.23" "January 2021"
|
.TH USTREAMER-DUMP 1 "version 3.26" "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.23" "November 2020"
|
.TH USTREAMER 1 "version 3.26" "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
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
|
|
||||||
pkgname=ustreamer
|
pkgname=ustreamer
|
||||||
pkgver=3.23
|
pkgver=3.26
|
||||||
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"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=ustreamer
|
PKG_NAME:=ustreamer
|
||||||
PKG_VERSION:=3.23
|
PKG_VERSION:=3.26
|
||||||
PKG_RELEASE:=1
|
PKG_RELEASE:=1
|
||||||
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
||||||
|
|
||||||
|
|||||||
20
python/Makefile
Normal file
20
python/Makefile
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
-include ../config.mk
|
||||||
|
|
||||||
|
DESTDIR ?=
|
||||||
|
PREFIX ?= /usr/local
|
||||||
|
|
||||||
|
PY ?= python3
|
||||||
|
|
||||||
|
|
||||||
|
# =====
|
||||||
|
all:
|
||||||
|
$(info == PY_BUILD ustreamer-*.so)
|
||||||
|
@ $(PY) setup.py build
|
||||||
|
|
||||||
|
|
||||||
|
install:
|
||||||
|
$(PY) setup.py install --prefix=$(PREFIX) --root=$(if $(DESTDIR),$(DESTDIR),/)
|
||||||
|
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf build
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
from distutils.core import Extension
|
from distutils.core import Extension
|
||||||
from distutils.core import setup
|
from distutils.core import setup
|
||||||
|
|
||||||
@@ -6,7 +8,7 @@ from distutils.core import setup
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
setup(
|
setup(
|
||||||
name="ustreamer",
|
name="ustreamer",
|
||||||
version="3.23",
|
version="3.26",
|
||||||
description="uStreamer tools",
|
description="uStreamer tools",
|
||||||
author="Maxim Devaev",
|
author="Maxim Devaev",
|
||||||
author_email="mdevaev@gmail.com",
|
author_email="mdevaev@gmail.com",
|
||||||
@@ -16,11 +18,8 @@ if __name__ == "__main__":
|
|||||||
"ustreamer",
|
"ustreamer",
|
||||||
libraries=["rt", "m", "pthread"],
|
libraries=["rt", "m", "pthread"],
|
||||||
undef_macros=["NDEBUG"],
|
undef_macros=["NDEBUG"],
|
||||||
sources=["ustreamer.c"],
|
sources=["src/" + name for name in os.listdir("src") if name.endswith(".c")],
|
||||||
depends=[
|
depends=["src/" + name for name in os.listdir("src") if name.endswith(".h")],
|
||||||
"../src/libs/tools.h",
|
|
||||||
"../src/libs/memsinksh.h",
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
1
python/src/frame.c
Symbolic link
1
python/src/frame.c
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../src/libs/frame.c
|
||||||
1
python/src/frame.h
Symbolic link
1
python/src/frame.h
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../src/libs/frame.h
|
||||||
1
python/src/memsinksh.h
Symbolic link
1
python/src/memsinksh.h
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../src/libs/memsinksh.h
|
||||||
1
python/src/tools.h
Symbolic link
1
python/src/tools.h
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../src/libs/tools.h
|
||||||
@@ -13,31 +13,11 @@
|
|||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
|
||||||
#include "../src/libs/tools.h" // Just a header without C-sources
|
#include "tools.h"
|
||||||
#include "../src/libs/memsinksh.h" // No sources again
|
#include "frame.h"
|
||||||
|
#include "memsinksh.h"
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
@@ -49,29 +29,28 @@ typedef struct {
|
|||||||
int fd;
|
int fd;
|
||||||
memsink_shared_s *mem;
|
memsink_shared_s *mem;
|
||||||
|
|
||||||
tmp_frame_s *tmp_frame;
|
uint64_t frame_id;
|
||||||
|
long double frame_ts;
|
||||||
|
frame_s *frame;
|
||||||
} MemsinkObject;
|
} MemsinkObject;
|
||||||
|
|
||||||
|
|
||||||
#define MEM(_next) self->mem->_next
|
#define MEM(_next) self->mem->_next
|
||||||
#define TMP(_next) self->tmp_frame->_next
|
#define FRAME(_next) self->frame->_next
|
||||||
|
|
||||||
|
|
||||||
static void MemsinkObject_destroy_internals(MemsinkObject *self) {
|
static void MemsinkObject_destroy_internals(MemsinkObject *self) {
|
||||||
if (self->mem != NULL) {
|
if (self->mem != NULL) {
|
||||||
munmap(self->mem, sizeof(memsink_shared_s));
|
memsink_shared_unmap(self->mem);
|
||||||
self->mem = NULL;
|
self->mem = NULL;
|
||||||
}
|
}
|
||||||
if (self->fd > 0) {
|
if (self->fd > 0) {
|
||||||
close(self->fd);
|
close(self->fd);
|
||||||
self->fd = -1;
|
self->fd = -1;
|
||||||
}
|
}
|
||||||
if (self->tmp_frame) {
|
if (self->frame) {
|
||||||
if (TMP(data)) {
|
frame_destroy(self->frame);
|
||||||
free(TMP(data));
|
self->frame = NULL;
|
||||||
}
|
|
||||||
free(self->tmp_frame);
|
|
||||||
self->tmp_frame = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,29 +78,15 @@ static int MemsinkObject_init(MemsinkObject *self, PyObject *args, PyObject *kwa
|
|||||||
|
|
||||||
# undef SET_DOUBLE
|
# undef SET_DOUBLE
|
||||||
|
|
||||||
A_CALLOC(self->tmp_frame, 1);
|
self->frame = frame_init();
|
||||||
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);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((self->mem = mmap(
|
if ((self->mem = memsink_shared_map(self->fd)) == NULL) {
|
||||||
NULL,
|
|
||||||
sizeof(memsink_shared_s),
|
|
||||||
PROT_READ | PROT_WRITE,
|
|
||||||
MAP_SHARED,
|
|
||||||
self->fd,
|
|
||||||
0
|
|
||||||
)) == MAP_FAILED) {
|
|
||||||
PyErr_SetFromErrno(PyExc_OSError);
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
self->mem = NULL;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (self->mem == NULL) {
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Memory mapping is NULL"); \
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,24 +142,16 @@ static int wait_frame(MemsinkObject *self) {
|
|||||||
RETURN_OS_ERROR;
|
RETURN_OS_ERROR;
|
||||||
|
|
||||||
} else if (retval == 0) {
|
} else if (retval == 0) {
|
||||||
if (MEM(magic) == MEMSINK_MAGIC && MEM(version) == MEMSINK_VERSION && TMP(id) != MEM(id)) {
|
if (MEM(magic) == MEMSINK_MAGIC && MEM(version) == MEMSINK_VERSION && MEM(id) != self->frame_id) {
|
||||||
if (self->drop_same_frames > 0) {
|
if (self->drop_same_frames > 0) {
|
||||||
# define CMP(_field) (TMP(_field) == MEM(_field))
|
|
||||||
if (
|
if (
|
||||||
CMP(used)
|
FRAME_COMPARE_META_USED_NOTS(self->mem, self->frame)
|
||||||
&& CMP(width)
|
&& (self->frame_ts + self->drop_same_frames > now)
|
||||||
&& CMP(height)
|
&& !memcmp(FRAME(data), MEM(data), MEM(used))
|
||||||
&& 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);
|
self->frame_id = MEM(id);
|
||||||
goto drop;
|
goto drop;
|
||||||
}
|
}
|
||||||
# undef CMP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_BLOCK_THREADS
|
Py_BLOCK_THREADS
|
||||||
@@ -236,30 +193,11 @@ static PyObject *MemsinkObject_wait_frame(MemsinkObject *self, PyObject *Py_UNUS
|
|||||||
default: return NULL;
|
default: return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
# define COPY(_field) TMP(_field) = MEM(_field)
|
frame_set_data(self->frame, MEM(data), MEM(used));
|
||||||
COPY(width);
|
FRAME_COPY_META(self->mem, self->frame);
|
||||||
COPY(height);
|
self->frame_id = MEM(id);
|
||||||
COPY(format);
|
self->frame_ts = get_now_monotonic();
|
||||||
COPY(stride);
|
MEM(last_client_ts) = self->frame_ts;
|
||||||
COPY(online);
|
|
||||||
COPY(key);
|
|
||||||
COPY(grab_ts);
|
|
||||||
COPY(encode_begin_ts);
|
|
||||||
COPY(encode_end_ts);
|
|
||||||
COPY(used);
|
|
||||||
# undef COPY
|
|
||||||
|
|
||||||
if (TMP(allocated) < MEM(used)) {
|
|
||||||
size_t size = MEM(used) + (512 * 1024);
|
|
||||||
A_REALLOC(TMP(data), size);
|
|
||||||
TMP(allocated) = size;
|
|
||||||
}
|
|
||||||
memcpy(TMP(data), MEM(data), MEM(used));
|
|
||||||
TMP(used) = MEM(used);
|
|
||||||
|
|
||||||
TMP(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);
|
||||||
@@ -281,7 +219,7 @@ static PyObject *MemsinkObject_wait_frame(MemsinkObject *self, PyObject *Py_UNUS
|
|||||||
} \
|
} \
|
||||||
Py_DECREF(_tmp); \
|
Py_DECREF(_tmp); \
|
||||||
}
|
}
|
||||||
# define SET_NUMBER(_key, _from, _to) SET_VALUE(#_key, Py##_to##_From##_from(TMP(_key)))
|
# define SET_NUMBER(_key, _from, _to) SET_VALUE(#_key, Py##_to##_From##_from(FRAME(_key)))
|
||||||
|
|
||||||
SET_NUMBER(width, Long, Long);
|
SET_NUMBER(width, Long, Long);
|
||||||
SET_NUMBER(height, Long, Long);
|
SET_NUMBER(height, Long, Long);
|
||||||
@@ -292,7 +230,7 @@ static PyObject *MemsinkObject_wait_frame(MemsinkObject *self, PyObject *Py_UNUS
|
|||||||
SET_NUMBER(grab_ts, Double, Float);
|
SET_NUMBER(grab_ts, Double, Float);
|
||||||
SET_NUMBER(encode_begin_ts, Double, Float);
|
SET_NUMBER(encode_begin_ts, Double, Float);
|
||||||
SET_NUMBER(encode_end_ts, Double, Float);
|
SET_NUMBER(encode_end_ts, Double, Float);
|
||||||
SET_VALUE("data", PyBytes_FromStringAndSize((const char *)TMP(data), TMP(used)));
|
SET_VALUE("data", PyBytes_FromStringAndSize((const char *)FRAME(data), FRAME(used)));
|
||||||
|
|
||||||
# undef SET_NUMBER
|
# undef SET_NUMBER
|
||||||
# undef SET_VALUE
|
# undef SET_VALUE
|
||||||
@@ -376,5 +314,5 @@ PyMODINIT_FUNC PyInit_ustreamer(void) { // cppcheck-suppress unusedFunction
|
|||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef TMP
|
#undef FRAME
|
||||||
#undef MEM
|
#undef MEM
|
||||||
113
src/Makefile
Normal file
113
src/Makefile
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
DESTDIR ?=
|
||||||
|
PREFIX ?= /usr/local
|
||||||
|
|
||||||
|
CC ?= gcc
|
||||||
|
CFLAGS ?= -O3
|
||||||
|
LDFLAGS ?=
|
||||||
|
|
||||||
|
RPI_VC_HEADERS ?= /opt/vc/include
|
||||||
|
RPI_VC_LIBS ?= /opt/vc/lib
|
||||||
|
|
||||||
|
|
||||||
|
# =====
|
||||||
|
_USTR = ustreamer.bin
|
||||||
|
_DUMP = ustreamer-dump.bin
|
||||||
|
|
||||||
|
_CFLAGS = -MD -c -std=c11 -Wall -Wextra -D_GNU_SOURCE $(CFLAGS)
|
||||||
|
_LDFLAGS = $(LDFLAGS)
|
||||||
|
|
||||||
|
_COMMON_LIBS = -lm -ljpeg -pthread -lrt
|
||||||
|
|
||||||
|
_USTR_LIBS = $(_COMMON_LIBS) -levent -levent_pthreads
|
||||||
|
_USTR_SRCS = $(shell ls \
|
||||||
|
libs/*.c \
|
||||||
|
ustreamer/*.c \
|
||||||
|
ustreamer/http/*.c \
|
||||||
|
ustreamer/data/*.c \
|
||||||
|
ustreamer/encoders/cpu/*.c \
|
||||||
|
ustreamer/encoders/hw/*.c \
|
||||||
|
)
|
||||||
|
|
||||||
|
_DUMP_LIBS = $(_COMMON_LIBS)
|
||||||
|
_DUMP_SRCS = $(shell ls \
|
||||||
|
libs/*.c \
|
||||||
|
dump/*.c \
|
||||||
|
)
|
||||||
|
|
||||||
|
_BUILD = build
|
||||||
|
|
||||||
|
|
||||||
|
define optbool
|
||||||
|
$(filter $(shell echo $(1) | tr A-Z a-z), yes on 1)
|
||||||
|
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)
|
||||||
|
_USTR_SRCS += $(shell ls \
|
||||||
|
ustreamer/encoders/omx/*.c \
|
||||||
|
ustreamer/h264/*.c \
|
||||||
|
)
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
ifneq ($(call optbool,$(WITH_GPIO)),)
|
||||||
|
_USTR_LIBS += -lgpiod
|
||||||
|
override _CFLAGS += -DWITH_GPIO
|
||||||
|
_USTR_SRCS += $(shell ls ustreamer/gpio/*.c)
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
WITH_PTHREAD_NP ?= 1
|
||||||
|
ifneq ($(call optbool,$(WITH_PTHREAD_NP)),)
|
||||||
|
override _CFLAGS += -DWITH_PTHREAD_NP
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
WITH_SETPROCTITLE ?= 1
|
||||||
|
ifneq ($(call optbool,$(WITH_SETPROCTITLE)),)
|
||||||
|
ifeq ($(shell uname -s | tr A-Z a-z),linux)
|
||||||
|
_USTR_LIBS += -lbsd
|
||||||
|
endif
|
||||||
|
override _CFLAGS += -DWITH_SETPROCTITLE
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
# =====
|
||||||
|
all: $(_USTR) $(_DUMP)
|
||||||
|
|
||||||
|
|
||||||
|
install: all
|
||||||
|
mkdir -p $(DESTDIR)$(PREFIX)/bin
|
||||||
|
install -m755 $(_USTR) $(DESTDIR)$(PREFIX)/bin/$(subst .bin,,$(_USTR))
|
||||||
|
install -m755 $(_DUMP) $(DESTDIR)$(PREFIX)/bin/$(subst .bin,,$(_DUMP))
|
||||||
|
|
||||||
|
|
||||||
|
install-strip: install
|
||||||
|
strip $(DESTDIR)$(PREFIX)/bin/$(subst .bin,,$(_USTR))
|
||||||
|
strip $(DESTDIR)$(PREFIX)/bin/$(subst .bin,,$(_DUMP))
|
||||||
|
|
||||||
|
|
||||||
|
$(_USTR): $(_USTR_SRCS:%.c=$(_BUILD)/%.o)
|
||||||
|
$(info == LD $@)
|
||||||
|
@ $(CC) $^ -o $@ $(_LDFLAGS) $(_USTR_LIBS)
|
||||||
|
|
||||||
|
|
||||||
|
$(_DUMP): $(_DUMP_SRCS:%.c=$(_BUILD)/%.o)
|
||||||
|
$(info == LD $@)
|
||||||
|
@ $(CC) $^ -o $@ $(_LDFLAGS) $(_DUMP_LIBS)
|
||||||
|
|
||||||
|
|
||||||
|
$(_BUILD)/%.o: %.c
|
||||||
|
$(info -- CC $<)
|
||||||
|
@ mkdir -p $(dir $@) || true
|
||||||
|
@ $(CC) $< -o $@ $(_CFLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(_USTR) $(_DUMP) $(_BUILD)
|
||||||
|
|
||||||
|
|
||||||
|
_OBJS = $(_USTR_SRCS:%.c=$(_BUILD)/%.o) $(_DUMP_SRCS:%.c=$(_BUILD)/%.o)
|
||||||
|
-include $(_OBJS:%.o=%.d)
|
||||||
@@ -128,12 +128,12 @@ int main(int argc, char *argv[]) {
|
|||||||
case _O_OUTPUT: OPT_SET(output_path, optarg);
|
case _O_OUTPUT: OPT_SET(output_path, optarg);
|
||||||
case _O_OUTPUT_JSON: OPT_SET(output_json, true);
|
case _O_OUTPUT_JSON: OPT_SET(output_json, true);
|
||||||
|
|
||||||
case _O_LOG_LEVEL: OPT_NUMBER("--log-level", log_level, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, 0);
|
case _O_LOG_LEVEL: OPT_NUMBER("--log-level", us_log_level, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, 0);
|
||||||
case _O_PERF: OPT_SET(log_level, LOG_LEVEL_PERF);
|
case _O_PERF: OPT_SET(us_log_level, LOG_LEVEL_PERF);
|
||||||
case _O_VERBOSE: OPT_SET(log_level, LOG_LEVEL_VERBOSE);
|
case _O_VERBOSE: OPT_SET(us_log_level, LOG_LEVEL_VERBOSE);
|
||||||
case _O_DEBUG: OPT_SET(log_level, LOG_LEVEL_DEBUG);
|
case _O_DEBUG: OPT_SET(us_log_level, LOG_LEVEL_DEBUG);
|
||||||
case _O_FORCE_LOG_COLORS: OPT_SET(log_colored, true);
|
case _O_FORCE_LOG_COLORS: OPT_SET(us_log_colored, true);
|
||||||
case _O_NO_LOG_COLORS: OPT_SET(log_colored, false);
|
case _O_NO_LOG_COLORS: OPT_SET(us_log_colored, false);
|
||||||
|
|
||||||
case _O_HELP: _help(stdout); return 0;
|
case _O_HELP: _help(stdout); return 0;
|
||||||
case _O_VERSION: puts(VERSION); return 0;
|
case _O_VERSION: puts(VERSION); return 0;
|
||||||
@@ -202,7 +202,7 @@ static void _install_signal_handlers(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int _dump_sink(const char *sink_name, unsigned sink_timeout, _output_context_s *ctx) {
|
static int _dump_sink(const char *sink_name, unsigned sink_timeout, _output_context_s *ctx) {
|
||||||
frame_s *frame = frame_init("input");
|
frame_s *frame = frame_init();
|
||||||
memsink_s *sink = NULL;
|
memsink_s *sink = NULL;
|
||||||
|
|
||||||
if ((sink = memsink_init("input", sink_name, false, 0, false, 0, sink_timeout)) == NULL) {
|
if ((sink = memsink_init("input", sink_name, false, 0, false, 0, sink_timeout)) == NULL) {
|
||||||
@@ -287,7 +287,7 @@ static void _help(FILE *fp) {
|
|||||||
SAY(" --log-level <N> ──── Verbosity level of messages from 0 (info) to 3 (debug).");
|
SAY(" --log-level <N> ──── Verbosity level of messages from 0 (info) to 3 (debug).");
|
||||||
SAY(" Enabling debugging messages can slow down the program.");
|
SAY(" Enabling debugging messages can slow down the program.");
|
||||||
SAY(" Available levels: 0 (info), 1 (performance), 2 (verbose), 3 (debug).");
|
SAY(" Available levels: 0 (info), 1 (performance), 2 (verbose), 3 (debug).");
|
||||||
SAY(" Default: %d.\n", log_level);
|
SAY(" Default: %d.\n", us_log_level);
|
||||||
SAY(" --perf ───────────── Enable performance messages (same as --log-level=1). Default: disabled.\n");
|
SAY(" --perf ───────────── Enable performance messages (same as --log-level=1). Default: disabled.\n");
|
||||||
SAY(" --verbose ────────── Enable verbose messages and lower (same as --log-level=2). Default: disabled.\n");
|
SAY(" --verbose ────────── Enable verbose messages and lower (same as --log-level=2). Default: disabled.\n");
|
||||||
SAY(" --debug ──────────── Enable debug messages and lower (same as --log-level=3). Default: disabled.\n");
|
SAY(" --debug ──────────── Enable debug messages and lower (same as --log-level=3). Default: disabled.\n");
|
||||||
|
|||||||
@@ -22,6 +22,11 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef VERSION
|
#define VERSION_MAJOR 3
|
||||||
# define VERSION "3.23"
|
#define VERSION_MINOR 26
|
||||||
#endif
|
|
||||||
|
#define MAKE_VERSION2(_major, _minor) #_major "." #_minor
|
||||||
|
#define MAKE_VERSION1(_major, _minor) MAKE_VERSION2(_major, _minor)
|
||||||
|
#define VERSION MAKE_VERSION1(VERSION_MAJOR, VERSION_MINOR)
|
||||||
|
|
||||||
|
#define VERSION_U ((unsigned)(VERSION_MAJOR * 1000 + VERSION_MINOR))
|
||||||
|
|||||||
@@ -23,17 +23,14 @@
|
|||||||
#include "frame.h"
|
#include "frame.h"
|
||||||
|
|
||||||
|
|
||||||
frame_s *frame_init(const char *name) {
|
frame_s *frame_init(void) {
|
||||||
frame_s *frame;
|
frame_s *frame;
|
||||||
A_CALLOC(frame, 1);
|
A_CALLOC(frame, 1);
|
||||||
frame->name = name;
|
|
||||||
frame->managed = true;
|
|
||||||
frame_realloc_data(frame, 512 * 1024);
|
frame_realloc_data(frame, 512 * 1024);
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
void frame_destroy(frame_s *frame) {
|
void frame_destroy(frame_s *frame) {
|
||||||
assert(frame->managed);
|
|
||||||
if (frame->data) {
|
if (frame->data) {
|
||||||
free(frame->data);
|
free(frame->data);
|
||||||
}
|
}
|
||||||
@@ -41,68 +38,36 @@ void frame_destroy(frame_s *frame) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void frame_realloc_data(frame_s *frame, size_t size) {
|
void frame_realloc_data(frame_s *frame, size_t size) {
|
||||||
assert(frame->managed);
|
|
||||||
if (frame->allocated < size) {
|
if (frame->allocated < size) {
|
||||||
LOG_DEBUG("Increasing frame buffer '%s': %zu -> %zu (+%zu)",
|
|
||||||
frame->name, frame->allocated, size, size - frame->allocated);
|
|
||||||
A_REALLOC(frame->data, size);
|
A_REALLOC(frame->data, size);
|
||||||
frame->allocated = size;
|
frame->allocated = size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void frame_set_data(frame_s *frame, const uint8_t *data, size_t size) {
|
void frame_set_data(frame_s *frame, const uint8_t *data, size_t size) {
|
||||||
assert(frame->managed);
|
|
||||||
frame_realloc_data(frame, size);
|
frame_realloc_data(frame, size);
|
||||||
memcpy(frame->data, data, size);
|
memcpy(frame->data, data, size);
|
||||||
frame->used = size;
|
frame->used = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void frame_append_data(frame_s *frame, const uint8_t *data, size_t size) {
|
void frame_append_data(frame_s *frame, const uint8_t *data, size_t size) {
|
||||||
assert(frame->managed);
|
|
||||||
size_t new_used = frame->used + size;
|
size_t new_used = frame->used + size;
|
||||||
frame_realloc_data(frame, new_used);
|
frame_realloc_data(frame, new_used);
|
||||||
memcpy(frame->data + frame->used, data, size);
|
memcpy(frame->data + frame->used, data, size);
|
||||||
frame->used = new_used;
|
frame->used = new_used;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define COPY(_field) dest->_field = src->_field
|
|
||||||
|
|
||||||
void frame_copy(const frame_s *src, frame_s *dest) {
|
void frame_copy(const frame_s *src, frame_s *dest) {
|
||||||
assert(dest->managed);
|
|
||||||
frame_set_data(dest, src->data, src->used);
|
frame_set_data(dest, src->data, src->used);
|
||||||
COPY(used);
|
FRAME_COPY_META(src, dest);
|
||||||
frame_copy_meta(src, dest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void frame_copy_meta(const frame_s *src, frame_s *dest) {
|
|
||||||
// Don't copy the name
|
|
||||||
COPY(width);
|
|
||||||
COPY(height);
|
|
||||||
COPY(format);
|
|
||||||
COPY(stride);
|
|
||||||
COPY(online);
|
|
||||||
COPY(key);
|
|
||||||
COPY(grab_ts);
|
|
||||||
COPY(encode_begin_ts);
|
|
||||||
COPY(encode_end_ts);
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef COPY
|
|
||||||
|
|
||||||
bool frame_compare(const frame_s *a, const frame_s *b) {
|
bool frame_compare(const frame_s *a, const frame_s *b) {
|
||||||
# define CMP(_field) (a->_field == b->_field)
|
|
||||||
return (
|
return (
|
||||||
a->allocated && b->allocated
|
a->allocated && b->allocated
|
||||||
&& CMP(used)
|
&& FRAME_COMPARE_META_USED_NOTS(a, b)
|
||||||
&& CMP(width)
|
|
||||||
&& CMP(height)
|
|
||||||
&& CMP(format)
|
|
||||||
&& CMP(stride)
|
|
||||||
&& CMP(online)
|
|
||||||
&& CMP(key)
|
|
||||||
&& !memcmp(a->data, b->data, b->used)
|
&& !memcmp(a->data, b->data, b->used)
|
||||||
);
|
);
|
||||||
# undef CMP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned frame_get_padding(const frame_s *frame) {
|
unsigned frame_get_padding(const frame_s *frame) {
|
||||||
|
|||||||
@@ -32,12 +32,9 @@
|
|||||||
#include <linux/videodev2.h>
|
#include <linux/videodev2.h>
|
||||||
|
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
#include "logging.h"
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *name;
|
|
||||||
|
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
size_t used;
|
size_t used;
|
||||||
size_t allocated;
|
size_t allocated;
|
||||||
@@ -56,12 +53,37 @@ typedef struct {
|
|||||||
long double grab_ts;
|
long double grab_ts;
|
||||||
long double encode_begin_ts;
|
long double encode_begin_ts;
|
||||||
long double encode_end_ts;
|
long double encode_end_ts;
|
||||||
|
|
||||||
bool managed;
|
|
||||||
} frame_s;
|
} frame_s;
|
||||||
|
|
||||||
|
|
||||||
frame_s *frame_init(const char *name);
|
#define FRAME_COPY_META(_src, _dest) { \
|
||||||
|
_dest->width = _src->width; \
|
||||||
|
_dest->height = _src->height; \
|
||||||
|
_dest->format = _src->format; \
|
||||||
|
_dest->stride = _src->stride; \
|
||||||
|
_dest->online = _src->online; \
|
||||||
|
_dest->key = _src->key; \
|
||||||
|
_dest->grab_ts = _src->grab_ts; \
|
||||||
|
_dest->encode_begin_ts = _src->encode_begin_ts; \
|
||||||
|
_dest->encode_end_ts = _src->encode_end_ts; \
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void frame_copy_meta(const frame_s *src, frame_s *dest) {
|
||||||
|
FRAME_COPY_META(src, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FRAME_COMPARE_META_USED_NOTS(_a, _b) ( \
|
||||||
|
_a->used == _b->used \
|
||||||
|
&& _a->width == _b->width \
|
||||||
|
&& _a->height == _b->height \
|
||||||
|
&& _a->format == _b->format \
|
||||||
|
&& _a->stride == _b->stride \
|
||||||
|
&& _a->online == _b->online \
|
||||||
|
&& _a->key == _b->key \
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
frame_s *frame_init(void);
|
||||||
void frame_destroy(frame_s *frame);
|
void frame_destroy(frame_s *frame);
|
||||||
|
|
||||||
void frame_realloc_data(frame_s *frame, size_t size);
|
void frame_realloc_data(frame_s *frame, size_t size);
|
||||||
@@ -69,7 +91,6 @@ void frame_set_data(frame_s *frame, const uint8_t *data, size_t size);
|
|||||||
void frame_append_data(frame_s *frame, const uint8_t *data, size_t size);
|
void frame_append_data(frame_s *frame, const uint8_t *data, size_t size);
|
||||||
|
|
||||||
void frame_copy(const frame_s *src, frame_s *dest);
|
void frame_copy(const frame_s *src, frame_s *dest);
|
||||||
void frame_copy_meta(const frame_s *src, frame_s *dest);
|
|
||||||
bool frame_compare(const frame_s *a, const frame_s *b);
|
bool frame_compare(const frame_s *a, const frame_s *b);
|
||||||
|
|
||||||
unsigned frame_get_padding(const frame_s *frame);
|
unsigned frame_get_padding(const frame_s *frame);
|
||||||
|
|||||||
71
src/libs/list.h
Normal file
71
src/libs/list.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
# #
|
||||||
|
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2021 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# This program is free software: you can redistribute it and/or modify #
|
||||||
|
# it under the terms of the GNU General Public License as published by #
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or #
|
||||||
|
# (at your option) any later version. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, #
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||||
|
# GNU General Public License for more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License #
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define LIST_STRUCT(...) \
|
||||||
|
__VA_ARGS__ *prev; \
|
||||||
|
__VA_ARGS__ *next;
|
||||||
|
|
||||||
|
#define LIST_ITERATE(_first, _item, ...) { \
|
||||||
|
for (__typeof__(_first) _item = _first; _item;) { \
|
||||||
|
__typeof__(_first) _next = _item->next; \
|
||||||
|
__VA_ARGS__ \
|
||||||
|
_item = _next; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LIST_APPEND(_first, _item) { \
|
||||||
|
if (_first == NULL) { \
|
||||||
|
_first = _item; \
|
||||||
|
} else { \
|
||||||
|
__typeof__(_first) _last = _first; \
|
||||||
|
for (; _last->next; _last = _last->next); \
|
||||||
|
_item->prev = _last; \
|
||||||
|
_last->next = _item; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LIST_APPEND_C(_first, _item, _count) { \
|
||||||
|
LIST_APPEND(_first, _item); \
|
||||||
|
++(_count); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LIST_REMOVE(_first, _item) { \
|
||||||
|
if (_item->prev == NULL) { \
|
||||||
|
_first = _item->next; \
|
||||||
|
} else { \
|
||||||
|
_item->prev->next = _item->next; \
|
||||||
|
} \
|
||||||
|
if (_item->next != NULL) { \
|
||||||
|
_item->next->prev = _item->prev; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LIST_REMOVE_C(_first, _item, _count) { \
|
||||||
|
LIST_REMOVE(_first, _item); \
|
||||||
|
assert((_count) >= 1); \
|
||||||
|
--(_count); \
|
||||||
|
}
|
||||||
@@ -23,8 +23,8 @@
|
|||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
|
||||||
|
|
||||||
enum log_level_t log_level;
|
enum log_level_t us_log_level;
|
||||||
|
|
||||||
bool log_colored;
|
bool us_log_colored;
|
||||||
|
|
||||||
pthread_mutex_t log_mutex;
|
pthread_mutex_t us_log_mutex;
|
||||||
|
|||||||
@@ -45,23 +45,23 @@ enum log_level_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
extern enum log_level_t log_level;
|
extern enum log_level_t us_log_level;
|
||||||
|
|
||||||
extern bool log_colored;
|
extern bool us_log_colored;
|
||||||
|
|
||||||
extern pthread_mutex_t log_mutex;
|
extern pthread_mutex_t us_log_mutex;
|
||||||
|
|
||||||
|
|
||||||
#define LOGGING_INIT { \
|
#define LOGGING_INIT { \
|
||||||
log_level = LOG_LEVEL_INFO; \
|
us_log_level = LOG_LEVEL_INFO; \
|
||||||
log_colored = isatty(2); \
|
us_log_colored = isatty(2); \
|
||||||
A_MUTEX_INIT(&log_mutex); \
|
A_MUTEX_INIT(&us_log_mutex); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define LOGGING_DESTROY A_MUTEX_DESTROY(&log_mutex)
|
#define LOGGING_DESTROY A_MUTEX_DESTROY(&us_log_mutex)
|
||||||
|
|
||||||
#define LOGGING_LOCK A_MUTEX_LOCK(&log_mutex)
|
#define LOGGING_LOCK A_MUTEX_LOCK(&us_log_mutex)
|
||||||
#define LOGGING_UNLOCK A_MUTEX_UNLOCK(&log_mutex)
|
#define LOGGING_UNLOCK A_MUTEX_UNLOCK(&us_log_mutex)
|
||||||
|
|
||||||
|
|
||||||
#define COLOR_GRAY "\x1b[30;1m"
|
#define COLOR_GRAY "\x1b[30;1m"
|
||||||
@@ -84,7 +84,7 @@ extern pthread_mutex_t log_mutex;
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define SEP_DEBUG(_ch) { \
|
#define SEP_DEBUG(_ch) { \
|
||||||
if (log_level >= LOG_LEVEL_DEBUG) { \
|
if (us_log_level >= LOG_LEVEL_DEBUG) { \
|
||||||
SEP_INFO(_ch); \
|
SEP_INFO(_ch); \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
@@ -93,7 +93,7 @@ extern pthread_mutex_t log_mutex;
|
|||||||
#define LOG_PRINTF_NOLOCK(_label_color, _label, _msg_color, _msg, ...) { \
|
#define LOG_PRINTF_NOLOCK(_label_color, _label, _msg_color, _msg, ...) { \
|
||||||
char _tname_buf[MAX_THREAD_NAME] = {0}; \
|
char _tname_buf[MAX_THREAD_NAME] = {0}; \
|
||||||
thread_get_name(_tname_buf); \
|
thread_get_name(_tname_buf); \
|
||||||
if (log_colored) { \
|
if (us_log_colored) { \
|
||||||
fprintf(stderr, COLOR_GRAY "-- " _label_color _label COLOR_GRAY \
|
fprintf(stderr, COLOR_GRAY "-- " _label_color _label COLOR_GRAY \
|
||||||
" [%.03Lf %9s]" " -- " COLOR_RESET _msg_color _msg COLOR_RESET, \
|
" [%.03Lf %9s]" " -- " COLOR_RESET _msg_color _msg COLOR_RESET, \
|
||||||
get_now_monotonic(), _tname_buf, ##__VA_ARGS__); \
|
get_now_monotonic(), _tname_buf, ##__VA_ARGS__); \
|
||||||
@@ -130,25 +130,25 @@ extern pthread_mutex_t log_mutex;
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define LOG_PERF(_msg, ...) { \
|
#define LOG_PERF(_msg, ...) { \
|
||||||
if (log_level >= LOG_LEVEL_PERF) { \
|
if (us_log_level >= LOG_LEVEL_PERF) { \
|
||||||
LOG_PRINTF(COLOR_CYAN, "PERF ", COLOR_CYAN, _msg, ##__VA_ARGS__); \
|
LOG_PRINTF(COLOR_CYAN, "PERF ", COLOR_CYAN, _msg, ##__VA_ARGS__); \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define LOG_PERF_FPS(_msg, ...) { \
|
#define LOG_PERF_FPS(_msg, ...) { \
|
||||||
if (log_level >= LOG_LEVEL_PERF) { \
|
if (us_log_level >= LOG_LEVEL_PERF) { \
|
||||||
LOG_PRINTF(COLOR_YELLOW, "PERF ", COLOR_YELLOW, _msg, ##__VA_ARGS__); \
|
LOG_PRINTF(COLOR_YELLOW, "PERF ", COLOR_YELLOW, _msg, ##__VA_ARGS__); \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define LOG_VERBOSE(_msg, ...) { \
|
#define LOG_VERBOSE(_msg, ...) { \
|
||||||
if (log_level >= LOG_LEVEL_VERBOSE) { \
|
if (us_log_level >= LOG_LEVEL_VERBOSE) { \
|
||||||
LOG_PRINTF(COLOR_BLUE, "VERB ", COLOR_BLUE, _msg, ##__VA_ARGS__); \
|
LOG_PRINTF(COLOR_BLUE, "VERB ", COLOR_BLUE, _msg, ##__VA_ARGS__); \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define LOG_VERBOSE_PERROR(_msg, ...) { \
|
#define LOG_VERBOSE_PERROR(_msg, ...) { \
|
||||||
if (log_level >= LOG_LEVEL_VERBOSE) { \
|
if (us_log_level >= LOG_LEVEL_VERBOSE) { \
|
||||||
char _perror_buf[1024] = {0}; \
|
char _perror_buf[1024] = {0}; \
|
||||||
char *_perror_ptr = errno_to_string(errno, _perror_buf, 1023); \
|
char *_perror_ptr = errno_to_string(errno, _perror_buf, 1023); \
|
||||||
LOG_PRINTF(COLOR_BLUE, "VERB ", COLOR_BLUE, _msg ": %s", ##__VA_ARGS__, _perror_ptr); \
|
LOG_PRINTF(COLOR_BLUE, "VERB ", COLOR_BLUE, _msg ": %s", ##__VA_ARGS__, _perror_ptr); \
|
||||||
@@ -156,17 +156,7 @@ extern pthread_mutex_t log_mutex;
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define LOG_DEBUG(_msg, ...) { \
|
#define LOG_DEBUG(_msg, ...) { \
|
||||||
if (log_level >= LOG_LEVEL_DEBUG) { \
|
if (us_log_level >= LOG_LEVEL_DEBUG) { \
|
||||||
LOG_PRINTF(COLOR_GRAY, "DEBUG", COLOR_GRAY, _msg, ##__VA_ARGS__); \
|
LOG_PRINTF(COLOR_GRAY, "DEBUG", COLOR_GRAY, _msg, ##__VA_ARGS__); \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
INLINE char *errno_to_string(int error, char *buf, size_t size) {
|
|
||||||
# if defined(__GLIBC__) && defined(_GNU_SOURCE)
|
|
||||||
return strerror_r(error, buf, size);
|
|
||||||
# else
|
|
||||||
strerror_r(error, buf, size);
|
|
||||||
return buf;
|
|
||||||
# endif
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ memsink_s *memsink_init(
|
|||||||
sink->timeout = timeout;
|
sink->timeout = timeout;
|
||||||
sink->fd = -1;
|
sink->fd = -1;
|
||||||
sink->mem = MAP_FAILED;
|
sink->mem = MAP_FAILED;
|
||||||
|
atomic_init(&sink->has_clients, false);
|
||||||
|
|
||||||
LOG_INFO("Using %s-sink: %s", name, obj);
|
LOG_INFO("Using %s-sink: %s", name, obj);
|
||||||
|
|
||||||
@@ -54,14 +55,7 @@ memsink_s *memsink_init(
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((sink->mem = mmap(
|
if ((sink->mem = memsink_shared_map(sink->fd)) == NULL) {
|
||||||
NULL,
|
|
||||||
sizeof(memsink_shared_s),
|
|
||||||
PROT_READ | PROT_WRITE,
|
|
||||||
MAP_SHARED,
|
|
||||||
sink->fd,
|
|
||||||
0
|
|
||||||
)) == MAP_FAILED) {
|
|
||||||
LOG_PERROR("%s-sink: Can't mmap shared memory", name);
|
LOG_PERROR("%s-sink: Can't mmap shared memory", name);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@@ -75,7 +69,7 @@ memsink_s *memsink_init(
|
|||||||
|
|
||||||
void memsink_destroy(memsink_s *sink) {
|
void memsink_destroy(memsink_s *sink) {
|
||||||
if (sink->mem != MAP_FAILED) {
|
if (sink->mem != MAP_FAILED) {
|
||||||
if (munmap(sink->mem, sizeof(memsink_shared_s)) < 0) {
|
if (memsink_shared_unmap(sink->mem) < 0) {
|
||||||
LOG_PERROR("%s-sink: Can't unmap shared memory", sink->name);
|
LOG_PERROR("%s-sink: Can't unmap shared memory", sink->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,24 +87,22 @@ void memsink_destroy(memsink_s *sink) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool memsink_server_check(memsink_s *sink, const frame_s *frame) {
|
bool memsink_server_check(memsink_s *sink, const frame_s *frame) {
|
||||||
// Возвращает true, если если клиенты ИЛИ изменились метаданные
|
// Возвращает true, если есть клиенты ИЛИ изменились метаданные
|
||||||
|
|
||||||
assert(sink->server);
|
assert(sink->server);
|
||||||
|
|
||||||
if (flock(sink->fd, LOCK_EX | LOCK_NB) < 0) {
|
if (flock(sink->fd, LOCK_EX | LOCK_NB) < 0) {
|
||||||
if (errno == EWOULDBLOCK) {
|
if (errno == EWOULDBLOCK) {
|
||||||
sink->has_clients = true;
|
atomic_store(&sink->has_clients, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
LOG_PERROR("%s-sink: Can't lock memory", sink->name);
|
LOG_PERROR("%s-sink: Can't lock memory", sink->name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
sink->has_clients = (sink->mem->last_client_ts + sink->client_ttl > get_now_monotonic());
|
atomic_store(&sink->has_clients, (sink->mem->last_client_ts + sink->client_ttl > get_now_monotonic()));
|
||||||
|
|
||||||
# define NEQ(_field) (sink->mem->_field != frame->_field)
|
bool retval = (atomic_load(&sink->has_clients) || !FRAME_COMPARE_META_USED_NOTS(sink->mem, frame));
|
||||||
bool retval = (sink->has_clients || NEQ(width) || NEQ(height) || NEQ(format) || NEQ(stride) || NEQ(online) || NEQ(key));
|
|
||||||
# undef NEQ
|
|
||||||
|
|
||||||
if (flock(sink->fd, LOCK_UN) < 0) {
|
if (flock(sink->fd, LOCK_UN) < 0) {
|
||||||
LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
|
LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
|
||||||
@@ -133,24 +125,16 @@ int memsink_server_put(memsink_s *sink, const frame_s *frame) {
|
|||||||
if (flock_timedwait_monotonic(sink->fd, 1) == 0) {
|
if (flock_timedwait_monotonic(sink->fd, 1) == 0) {
|
||||||
LOG_VERBOSE("%s-sink: >>>>> Exposing new frame ...", sink->name);
|
LOG_VERBOSE("%s-sink: >>>>> Exposing new frame ...", sink->name);
|
||||||
|
|
||||||
# define COPY(_field) sink->mem->_field = frame->_field
|
|
||||||
sink->last_id = get_now_id();
|
sink->last_id = get_now_id();
|
||||||
sink->mem->id = sink->last_id;
|
sink->mem->id = sink->last_id;
|
||||||
COPY(used);
|
|
||||||
COPY(width);
|
|
||||||
COPY(height);
|
|
||||||
COPY(format);
|
|
||||||
COPY(stride);
|
|
||||||
COPY(online);
|
|
||||||
COPY(key);
|
|
||||||
COPY(grab_ts);
|
|
||||||
COPY(encode_begin_ts);
|
|
||||||
COPY(encode_end_ts);
|
|
||||||
sink->has_clients = (sink->mem->last_client_ts + sink->client_ttl > get_now_monotonic());
|
|
||||||
memcpy(sink->mem->data, frame->data, frame->used);
|
memcpy(sink->mem->data, frame->data, frame->used);
|
||||||
|
sink->mem->used = frame->used;
|
||||||
|
FRAME_COPY_META(frame, sink->mem);
|
||||||
|
|
||||||
sink->mem->magic = MEMSINK_MAGIC;
|
sink->mem->magic = MEMSINK_MAGIC;
|
||||||
sink->mem->version = MEMSINK_VERSION;
|
sink->mem->version = MEMSINK_VERSION;
|
||||||
# undef COPY
|
atomic_store(&sink->has_clients, (sink->mem->last_client_ts + sink->client_ttl > get_now_monotonic()));
|
||||||
|
|
||||||
if (flock(sink->fd, LOCK_UN) < 0) {
|
if (flock(sink->fd, LOCK_UN) < 0) {
|
||||||
LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
|
LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
|
||||||
@@ -189,19 +173,9 @@ int memsink_client_get(memsink_s *sink, frame_s *frame) { // cppcheck-suppress u
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (sink->mem->id != sink->last_id) { // When updated
|
if (sink->mem->id != sink->last_id) { // When updated
|
||||||
# define COPY(_field) frame->_field = sink->mem->_field
|
|
||||||
sink->last_id = sink->mem->id;
|
sink->last_id = sink->mem->id;
|
||||||
COPY(width);
|
|
||||||
COPY(height);
|
|
||||||
COPY(format);
|
|
||||||
COPY(stride);
|
|
||||||
COPY(online);
|
|
||||||
COPY(key);
|
|
||||||
COPY(grab_ts);
|
|
||||||
COPY(encode_begin_ts);
|
|
||||||
COPY(encode_end_ts);
|
|
||||||
frame_set_data(frame, sink->mem->data, sink->mem->used);
|
frame_set_data(frame, sink->mem->data, sink->mem->used);
|
||||||
# undef COPY
|
FRAME_COPY_META(sink->mem, frame);
|
||||||
retval = 0;
|
retval = 0;
|
||||||
}
|
}
|
||||||
sink->mem->last_client_ts = get_now_monotonic();
|
sink->mem->last_client_ts = get_now_monotonic();
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@@ -51,7 +52,7 @@ typedef struct {
|
|||||||
int fd;
|
int fd;
|
||||||
memsink_shared_s *mem;
|
memsink_shared_s *mem;
|
||||||
uint64_t last_id;
|
uint64_t last_id;
|
||||||
bool has_clients; // Only for server
|
atomic_bool has_clients; // Only for server
|
||||||
} memsink_s;
|
} memsink_s;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
|
||||||
#define MEMSINK_MAGIC ((uint64_t)0xCAFEBABECAFEBABE)
|
#define MEMSINK_MAGIC ((uint64_t)0xCAFEBABECAFEBABE)
|
||||||
@@ -59,3 +60,25 @@ typedef struct {
|
|||||||
|
|
||||||
uint8_t data[MEMSINK_MAX_DATA];
|
uint8_t data[MEMSINK_MAX_DATA];
|
||||||
} memsink_shared_s;
|
} memsink_shared_s;
|
||||||
|
|
||||||
|
|
||||||
|
INLINE memsink_shared_s *memsink_shared_map(int fd) {
|
||||||
|
memsink_shared_s *mem = mmap(
|
||||||
|
NULL,
|
||||||
|
sizeof(memsink_shared_s),
|
||||||
|
PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED,
|
||||||
|
fd,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
if (mem == MAP_FAILED) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
assert(mem != NULL);
|
||||||
|
return mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
INLINE int memsink_shared_unmap(memsink_shared_s *mem) {
|
||||||
|
assert(mem != NULL);
|
||||||
|
return munmap(mem, sizeof(memsink_shared_s));
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,7 +26,9 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <locale.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@@ -35,6 +37,11 @@
|
|||||||
#include <sys/file.h>
|
#include <sys/file.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define RN "\r\n"
|
||||||
|
|
||||||
|
#define INLINE inline __attribute__((always_inline))
|
||||||
|
#define UNUSED __attribute__((unused))
|
||||||
|
|
||||||
#define A_CALLOC(_dest, _nmemb) assert((_dest = calloc(_nmemb, sizeof(*(_dest)))))
|
#define A_CALLOC(_dest, _nmemb) assert((_dest = calloc(_nmemb, sizeof(*(_dest)))))
|
||||||
#define A_REALLOC(_dest, _nmemb) assert((_dest = realloc(_dest, _nmemb * sizeof(*(_dest)))))
|
#define A_REALLOC(_dest, _nmemb) assert((_dest = realloc(_dest, _nmemb * sizeof(*(_dest)))))
|
||||||
#define MEMSET_ZERO(_obj) memset(&(_obj), 0, sizeof(_obj))
|
#define MEMSET_ZERO(_obj) memset(&(_obj), 0, sizeof(_obj))
|
||||||
@@ -43,9 +50,6 @@
|
|||||||
|
|
||||||
#define ARRAY_LEN(_array) (sizeof(_array) / sizeof(_array[0]))
|
#define ARRAY_LEN(_array) (sizeof(_array) / sizeof(_array[0]))
|
||||||
|
|
||||||
#define INLINE inline __attribute__((always_inline))
|
|
||||||
#define UNUSED __attribute__((unused))
|
|
||||||
|
|
||||||
|
|
||||||
INLINE const char *bool_to_string(bool flag) {
|
INLINE const char *bool_to_string(bool flag) {
|
||||||
return (flag ? "true" : "false");
|
return (flag ? "true" : "false");
|
||||||
@@ -143,3 +147,16 @@ INLINE int flock_timedwait_monotonic(int fd, long double timeout) {
|
|||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INLINE char *errno_to_string(int error, char *buf, size_t size) {
|
||||||
|
assert(buf);
|
||||||
|
assert(size > 0);
|
||||||
|
locale_t locale = newlocale(LC_MESSAGES_MASK, "C", NULL);
|
||||||
|
char *str = "!!! newlocale() error !!!";
|
||||||
|
strncpy(buf, (locale ? strerror_l(error, locale) : str), size - 1);
|
||||||
|
buf[size - 1] = '\0';
|
||||||
|
if (locale) {
|
||||||
|
freelocale(locale);
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|||||||
@@ -87,6 +87,6 @@ static void _jpeg_error_handler(j_common_ptr jpeg) {
|
|||||||
char msg[JMSG_LENGTH_MAX];
|
char msg[JMSG_LENGTH_MAX];
|
||||||
|
|
||||||
(*jpeg_error->mgr.format_message)(jpeg, msg);
|
(*jpeg_error->mgr.format_message)(jpeg, msg);
|
||||||
LOG_ERROR("Can't decompress %s JPEG: %s", jpeg_error->frame->name, msg);
|
LOG_ERROR("Can't decompress JPEG: %s", msg);
|
||||||
longjmp(jpeg_error->jmp, -1);
|
longjmp(jpeg_error->jmp, -1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ frame_s *blank_frame_init(const char *path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static frame_s *_init_internal(void) {
|
static frame_s *_init_internal(void) {
|
||||||
frame_s *blank = frame_init("blank_internal");
|
frame_s *blank = frame_init();
|
||||||
frame_set_data(blank, BLANK_JPEG_DATA, BLANK_JPEG_DATA_SIZE);
|
frame_set_data(blank, BLANK_JPEG_DATA, BLANK_JPEG_DATA_SIZE);
|
||||||
blank->width = BLANK_JPEG_WIDTH;
|
blank->width = BLANK_JPEG_WIDTH;
|
||||||
blank->height = BLANK_JPEG_HEIGHT;
|
blank->height = BLANK_JPEG_HEIGHT;
|
||||||
@@ -55,7 +55,7 @@ static frame_s *_init_internal(void) {
|
|||||||
static frame_s *_init_external(const char *path) {
|
static frame_s *_init_external(const char *path) {
|
||||||
FILE *fp = NULL;
|
FILE *fp = NULL;
|
||||||
|
|
||||||
frame_s *blank = frame_init("blank_external");
|
frame_s *blank = frame_init();
|
||||||
blank->format = V4L2_PIX_FMT_JPEG;
|
blank->format = V4L2_PIX_FMT_JPEG;
|
||||||
|
|
||||||
if ((fp = fopen(path, "rb")) == NULL) {
|
if ((fp = fopen(path, "rb")) == NULL) {
|
||||||
@@ -83,7 +83,7 @@ static frame_s *_init_external(const char *path) {
|
|||||||
}
|
}
|
||||||
# undef CHUNK_SIZE
|
# undef CHUNK_SIZE
|
||||||
|
|
||||||
frame_s *decoded = frame_init("blank_external_decoded");
|
frame_s *decoded = frame_init();
|
||||||
if (unjpeg(blank, decoded, false) < 0) {
|
if (unjpeg(blank, decoded, false) < 0) {
|
||||||
frame_destroy(decoded);
|
frame_destroy(decoded);
|
||||||
goto error;
|
goto error;
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ static const struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static void *_worker_job_init(worker_s *wr, void *v_enc);
|
static void *_worker_job_init(void *v_enc);
|
||||||
static void _worker_job_destroy(void *v_job);
|
static void _worker_job_destroy(void *v_job);
|
||||||
static bool _worker_run_job(worker_s *wr);
|
static bool _worker_run_job(worker_s *wr);
|
||||||
|
|
||||||
@@ -212,21 +212,17 @@ void encoder_get_runtime_params(encoder_s *enc, encoder_type_e *type, unsigned *
|
|||||||
A_MUTEX_UNLOCK(&ER(mutex));
|
A_MUTEX_UNLOCK(&ER(mutex));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *_worker_job_init(worker_s *wr, void *v_enc) {
|
static void *_worker_job_init(void *v_enc) {
|
||||||
encoder_job_s *job;
|
encoder_job_s *job;
|
||||||
A_CALLOC(job, 1);
|
A_CALLOC(job, 1);
|
||||||
job->enc = (encoder_s *)v_enc;
|
job->enc = (encoder_s *)v_enc;
|
||||||
|
job->dest = frame_init();
|
||||||
A_ASPRINTF(job->dest_role, "%s_dest", wr->name);
|
|
||||||
job->dest = frame_init(job->dest_role);
|
|
||||||
|
|
||||||
return (void *)job;
|
return (void *)job;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _worker_job_destroy(void *v_job) {
|
static void _worker_job_destroy(void *v_job) {
|
||||||
encoder_job_s *job = (encoder_job_s *)v_job;
|
encoder_job_s *job = (encoder_job_s *)v_job;
|
||||||
frame_destroy(job->dest);
|
frame_destroy(job->dest);
|
||||||
free(job->dest_role);
|
|
||||||
free(job);
|
free(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
#include "gpio.h"
|
#include "gpio.h"
|
||||||
|
|
||||||
|
|
||||||
gpio_s gpio = {
|
gpio_s us_gpio = {
|
||||||
.path = "/dev/gpiochip0",
|
.path = "/dev/gpiochip0",
|
||||||
.consumer_prefix = "ustreamer",
|
.consumer_prefix = "ustreamer",
|
||||||
|
|
||||||
@@ -51,42 +51,42 @@ static void _gpio_output_destroy(gpio_output_s *output);
|
|||||||
|
|
||||||
|
|
||||||
void gpio_init(void) {
|
void gpio_init(void) {
|
||||||
assert(gpio.chip == NULL);
|
assert(us_gpio.chip == NULL);
|
||||||
if (
|
if (
|
||||||
gpio.prog_running.pin >= 0
|
us_gpio.prog_running.pin >= 0
|
||||||
|| gpio.stream_online.pin >= 0
|
|| us_gpio.stream_online.pin >= 0
|
||||||
|| gpio.has_http_clients.pin >= 0
|
|| us_gpio.has_http_clients.pin >= 0
|
||||||
) {
|
) {
|
||||||
A_MUTEX_INIT(&gpio.mutex);
|
A_MUTEX_INIT(&us_gpio.mutex);
|
||||||
LOG_INFO("GPIO: Using chip device: %s", gpio.path);
|
LOG_INFO("GPIO: Using chip device: %s", us_gpio.path);
|
||||||
if ((gpio.chip = gpiod_chip_open(gpio.path)) != NULL) {
|
if ((us_gpio.chip = gpiod_chip_open(us_gpio.path)) != NULL) {
|
||||||
_gpio_output_init(&gpio.prog_running);
|
_gpio_output_init(&us_gpio.prog_running);
|
||||||
_gpio_output_init(&gpio.stream_online);
|
_gpio_output_init(&us_gpio.stream_online);
|
||||||
_gpio_output_init(&gpio.has_http_clients);
|
_gpio_output_init(&us_gpio.has_http_clients);
|
||||||
} else {
|
} else {
|
||||||
LOG_PERROR("GPIO: Can't initialize chip device %s", gpio.path);
|
LOG_PERROR("GPIO: Can't initialize chip device %s", us_gpio.path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void gpio_destroy(void) {
|
void gpio_destroy(void) {
|
||||||
_gpio_output_destroy(&gpio.prog_running);
|
_gpio_output_destroy(&us_gpio.prog_running);
|
||||||
_gpio_output_destroy(&gpio.stream_online);
|
_gpio_output_destroy(&us_gpio.stream_online);
|
||||||
_gpio_output_destroy(&gpio.has_http_clients);
|
_gpio_output_destroy(&us_gpio.has_http_clients);
|
||||||
if (gpio.chip) {
|
if (us_gpio.chip) {
|
||||||
gpiod_chip_close(gpio.chip);
|
gpiod_chip_close(us_gpio.chip);
|
||||||
gpio.chip = NULL;
|
us_gpio.chip = NULL;
|
||||||
A_MUTEX_DESTROY(&gpio.mutex);
|
A_MUTEX_DESTROY(&us_gpio.mutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int gpio_inner_set(gpio_output_s *output, bool state) {
|
int gpio_inner_set(gpio_output_s *output, bool state) {
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
|
|
||||||
assert(gpio.chip);
|
assert(us_gpio.chip);
|
||||||
assert(output->line);
|
assert(output->line);
|
||||||
assert(output->state != state); // Must be checked in macro for the performance
|
assert(output->state != state); // Must be checked in macro for the performance
|
||||||
A_MUTEX_LOCK(&gpio.mutex);
|
A_MUTEX_LOCK(&us_gpio.mutex);
|
||||||
|
|
||||||
if (gpiod_line_set_value(output->line, (int)state) < 0) { \
|
if (gpiod_line_set_value(output->line, (int)state) < 0) { \
|
||||||
LOG_PERROR("GPIO: Can't write value %d to line %s (will be disabled)", state, output->consumer); \
|
LOG_PERROR("GPIO: Can't write value %d to line %s (will be disabled)", state, output->consumer); \
|
||||||
@@ -94,18 +94,18 @@ int gpio_inner_set(gpio_output_s *output, bool state) {
|
|||||||
retval = -1;
|
retval = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
A_MUTEX_UNLOCK(&gpio.mutex);
|
A_MUTEX_UNLOCK(&us_gpio.mutex);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _gpio_output_init(gpio_output_s *output) {
|
static void _gpio_output_init(gpio_output_s *output) {
|
||||||
assert(gpio.chip);
|
assert(us_gpio.chip);
|
||||||
assert(output->line == NULL);
|
assert(output->line == NULL);
|
||||||
|
|
||||||
A_ASPRINTF(output->consumer, "%s::%s", gpio.consumer_prefix, output->role);
|
A_ASPRINTF(output->consumer, "%s::%s", us_gpio.consumer_prefix, output->role);
|
||||||
|
|
||||||
if (output->pin >= 0) {
|
if (output->pin >= 0) {
|
||||||
if ((output->line = gpiod_chip_get_line(gpio.chip, output->pin)) != NULL) {
|
if ((output->line = gpiod_chip_get_line(us_gpio.chip, output->pin)) != NULL) {
|
||||||
if (gpiod_line_request_output(output->line, output->consumer, 0) < 0) {
|
if (gpiod_line_request_output(output->line, output->consumer, 0) < 0) {
|
||||||
LOG_PERROR("GPIO: Can't request pin=%d as %s", output->pin, output->consumer);
|
LOG_PERROR("GPIO: Can't request pin=%d as %s", output->pin, output->consumer);
|
||||||
_gpio_output_destroy(output);
|
_gpio_output_destroy(output);
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ typedef struct {
|
|||||||
} gpio_s;
|
} gpio_s;
|
||||||
|
|
||||||
|
|
||||||
extern gpio_s gpio;
|
extern gpio_s us_gpio;
|
||||||
|
|
||||||
|
|
||||||
void gpio_init(void);
|
void gpio_init(void);
|
||||||
@@ -73,15 +73,15 @@ int gpio_inner_set(gpio_output_s *output, bool state);
|
|||||||
}
|
}
|
||||||
|
|
||||||
INLINE void gpio_set_prog_running(bool state) {
|
INLINE void gpio_set_prog_running(bool state) {
|
||||||
SET_STATE(gpio.prog_running, state);
|
SET_STATE(us_gpio.prog_running, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
INLINE void gpio_set_stream_online(bool state) {
|
INLINE void gpio_set_stream_online(bool state) {
|
||||||
SET_STATE(gpio.stream_online, state);
|
SET_STATE(us_gpio.stream_online, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
INLINE void gpio_set_has_http_clients(bool state) {
|
INLINE void gpio_set_has_http_clients(bool state) {
|
||||||
SET_STATE(gpio.has_http_clients, state);
|
SET_STATE(us_gpio.has_http_clients, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef SET_STATE
|
#undef SET_STATE
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ h264_stream_s *h264_stream_init(memsink_s *sink, unsigned bitrate, unsigned gop)
|
|||||||
h264_stream_s *h264;
|
h264_stream_s *h264;
|
||||||
A_CALLOC(h264, 1);
|
A_CALLOC(h264, 1);
|
||||||
h264->sink = sink;
|
h264->sink = sink;
|
||||||
h264->tmp_src = frame_init("h264_tmp_src");
|
h264->tmp_src = frame_init();
|
||||||
h264->dest = frame_init("h264_dest");
|
h264->dest = frame_init();
|
||||||
atomic_init(&h264->online, false);
|
atomic_init(&h264->online, false);
|
||||||
|
|
||||||
// FIXME: 30 or 0? https://github.com/6by9/yavta/blob/master/yavta.c#L2100
|
// FIXME: 30 or 0? https://github.com/6by9/yavta/blob/master/yavta.c#L2100
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ static char *_http_get_client_hostport(struct evhttp_request *request);
|
|||||||
server_s *server_init(stream_s *stream) {
|
server_s *server_init(stream_s *stream) {
|
||||||
exposed_s *exposed;
|
exposed_s *exposed;
|
||||||
A_CALLOC(exposed, 1);
|
A_CALLOC(exposed, 1);
|
||||||
exposed->frame = frame_init("http_exposed");
|
exposed->frame = frame_init();
|
||||||
|
|
||||||
server_runtime_s *run;
|
server_runtime_s *run;
|
||||||
A_CALLOC(run, 1);
|
A_CALLOC(run, 1);
|
||||||
@@ -93,13 +93,11 @@ void server_destroy(server_s *server) {
|
|||||||
libevent_global_shutdown();
|
libevent_global_shutdown();
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
for (stream_client_s *client = RUN(stream_clients); client != NULL;) {
|
LIST_ITERATE(RUN(stream_clients), client, {
|
||||||
stream_client_s *next = client->next;
|
|
||||||
free(client->key);
|
free(client->key);
|
||||||
free(client->hostport);
|
free(client->hostport);
|
||||||
free(client);
|
free(client);
|
||||||
client = next;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
if (RUN(auth_token)) {
|
if (RUN(auth_token)) {
|
||||||
free(RUN(auth_token));
|
free(RUN(auth_token));
|
||||||
@@ -344,6 +342,7 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
|
|||||||
encoder_type_to_string(enc_type),
|
encoder_type_to_string(enc_type),
|
||||||
enc_quality
|
enc_quality
|
||||||
));
|
));
|
||||||
|
|
||||||
# ifdef WITH_OMX
|
# ifdef WITH_OMX
|
||||||
if (STREAM(run->h264)) {
|
if (STREAM(run->h264)) {
|
||||||
assert(evbuffer_add_printf(buf,
|
assert(evbuffer_add_printf(buf,
|
||||||
@@ -354,6 +353,32 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
if (
|
||||||
|
STREAM(sink)
|
||||||
|
# ifdef WITH_OMX
|
||||||
|
|| STREAM(h264_sink)
|
||||||
|
# endif
|
||||||
|
) {
|
||||||
|
assert(evbuffer_add_printf(buf, " \"sinks\": {"));
|
||||||
|
if (STREAM(sink)) {
|
||||||
|
assert(evbuffer_add_printf(buf,
|
||||||
|
"\"jpeg\": {\"has_clients\": %s}",
|
||||||
|
bool_to_string(atomic_load(&STREAM(sink->has_clients)))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
# ifdef WITH_OMX
|
||||||
|
if (STREAM(h264_sink)) {
|
||||||
|
assert(evbuffer_add_printf(buf,
|
||||||
|
"%s\"h264\": {\"has_clients\": %s}",
|
||||||
|
(STREAM(sink) ? ", " : ""),
|
||||||
|
bool_to_string(atomic_load(&STREAM(h264_sink->has_clients)))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
assert(evbuffer_add_printf(buf, "},"));
|
||||||
|
}
|
||||||
|
|
||||||
assert(evbuffer_add_printf(buf,
|
assert(evbuffer_add_printf(buf,
|
||||||
" \"source\": {\"resolution\": {\"width\": %u, \"height\": %u},"
|
" \"source\": {\"resolution\": {\"width\": %u, \"height\": %u},"
|
||||||
" \"online\": %s, \"desired_fps\": %u, \"captured_fps\": %u},"
|
" \"online\": %s, \"desired_fps\": %u, \"captured_fps\": %u},"
|
||||||
@@ -367,7 +392,7 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
|
|||||||
RUN(stream_clients_count)
|
RUN(stream_clients_count)
|
||||||
));
|
));
|
||||||
|
|
||||||
for (stream_client_s * client = RUN(stream_clients); client != NULL; client = client->next) {
|
LIST_ITERATE(RUN(stream_clients), client, {
|
||||||
assert(evbuffer_add_printf(buf,
|
assert(evbuffer_add_printf(buf,
|
||||||
"\"%" PRIx64 "\": {\"fps\": %u, \"extra_headers\": %s, \"advance_headers\": %s,"
|
"\"%" PRIx64 "\": {\"fps\": %u, \"extra_headers\": %s, \"advance_headers\": %s,"
|
||||||
" \"dual_final_frames\": %s, \"zero_data\": %s}%s",
|
" \"dual_final_frames\": %s, \"zero_data\": %s}%s",
|
||||||
@@ -379,7 +404,7 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
|
|||||||
bool_to_string(client->zero_data),
|
bool_to_string(client->zero_data),
|
||||||
(client->next ? ", " : "")
|
(client->next ? ", " : "")
|
||||||
));
|
));
|
||||||
}
|
});
|
||||||
|
|
||||||
assert(evbuffer_add_printf(buf, "}}}}"));
|
assert(evbuffer_add_printf(buf, "}}}}"));
|
||||||
|
|
||||||
@@ -477,16 +502,7 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
|||||||
client->hostport = _http_get_client_hostport(request);
|
client->hostport = _http_get_client_hostport(request);
|
||||||
client->id = get_now_id();
|
client->id = get_now_id();
|
||||||
|
|
||||||
if (RUN(stream_clients) == NULL) {
|
LIST_APPEND_C(RUN(stream_clients), client, RUN(stream_clients_count));
|
||||||
RUN(stream_clients) = client;
|
|
||||||
} else {
|
|
||||||
stream_client_s *last = RUN(stream_clients);
|
|
||||||
|
|
||||||
for (; last->next != NULL; last = last->next);
|
|
||||||
client->prev = last;
|
|
||||||
last->next = client;
|
|
||||||
}
|
|
||||||
RUN(stream_clients_count) += 1;
|
|
||||||
|
|
||||||
if (RUN(stream_clients_count) == 1) {
|
if (RUN(stream_clients_count) == 1) {
|
||||||
atomic_store(&VID(has_clients), true);
|
atomic_store(&VID(has_clients), true);
|
||||||
@@ -520,7 +536,6 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
|||||||
|
|
||||||
static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_client) {
|
static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_client) {
|
||||||
# define BOUNDARY "boundarydonotcross"
|
# define BOUNDARY "boundarydonotcross"
|
||||||
# define RN "\r\n"
|
|
||||||
|
|
||||||
stream_client_s *client = (stream_client_s *)v_client;
|
stream_client_s *client = (stream_client_s *)v_client;
|
||||||
server_s *server = client->server;
|
server_s *server = client->server;
|
||||||
@@ -645,7 +660,6 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
|
|||||||
bufferevent_enable(buf_event, EV_READ);
|
bufferevent_enable(buf_event, EV_READ);
|
||||||
|
|
||||||
# undef ADD_ADVANCE_HEADERS
|
# undef ADD_ADVANCE_HEADERS
|
||||||
# undef RN
|
|
||||||
# undef BOUNDARY
|
# undef BOUNDARY
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -653,10 +667,7 @@ static void _http_callback_stream_error(UNUSED struct bufferevent *buf_event, UN
|
|||||||
stream_client_s *client = (stream_client_s *)v_client;
|
stream_client_s *client = (stream_client_s *)v_client;
|
||||||
server_s *server = client->server;
|
server_s *server = client->server;
|
||||||
|
|
||||||
char *reason = bufferevent_my_format_reason(what);
|
LIST_REMOVE_C(RUN(stream_clients), client, RUN(stream_clients_count));
|
||||||
|
|
||||||
assert(RUN(stream_clients_count) > 0);
|
|
||||||
RUN(stream_clients_count) -= 1;
|
|
||||||
|
|
||||||
if (RUN(stream_clients_count) == 0) {
|
if (RUN(stream_clients_count) == 0) {
|
||||||
atomic_store(&VID(has_clients), false);
|
atomic_store(&VID(has_clients), false);
|
||||||
@@ -665,34 +676,26 @@ static void _http_callback_stream_error(UNUSED struct bufferevent *buf_event, UN
|
|||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *reason = bufferevent_my_format_reason(what);
|
||||||
LOG_INFO("HTTP: Disconnected client: %s, id=%" PRIx64 ", %s; clients now: %u",
|
LOG_INFO("HTTP: Disconnected client: %s, id=%" PRIx64 ", %s; clients now: %u",
|
||||||
client->hostport, client->id, reason, RUN(stream_clients_count));
|
client->hostport, client->id, reason, RUN(stream_clients_count));
|
||||||
|
free(reason);
|
||||||
|
|
||||||
struct evhttp_connection *conn = evhttp_request_get_connection(client->request);
|
struct evhttp_connection *conn = evhttp_request_get_connection(client->request);
|
||||||
if (conn) {
|
if (conn) {
|
||||||
evhttp_connection_free(conn);
|
evhttp_connection_free(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client->prev == NULL) {
|
|
||||||
RUN(stream_clients) = client->next;
|
|
||||||
} else {
|
|
||||||
client->prev->next = client->next;
|
|
||||||
}
|
|
||||||
if (client->next != NULL) {
|
|
||||||
client->next->prev = client->prev;
|
|
||||||
}
|
|
||||||
free(client->key);
|
free(client->key);
|
||||||
free(client->hostport);
|
free(client->hostport);
|
||||||
free(client);
|
free(client);
|
||||||
|
|
||||||
free(reason);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _http_queue_send_stream(server_s *server, bool stream_updated, bool frame_updated) {
|
static void _http_queue_send_stream(server_s *server, bool stream_updated, bool frame_updated) {
|
||||||
bool has_clients = false;
|
bool has_clients = false;
|
||||||
bool queued = false;
|
bool queued = false;
|
||||||
|
|
||||||
for (stream_client_s *client = RUN(stream_clients); client != NULL; client = client->next) {
|
LIST_ITERATE(RUN(stream_clients), client, {
|
||||||
struct evhttp_connection *conn = evhttp_request_get_connection(client->request);
|
struct evhttp_connection *conn = evhttp_request_get_connection(client->request);
|
||||||
if (conn) {
|
if (conn) {
|
||||||
// Фикс для бага WebKit. При включенной опции дропа одинаковых фреймов,
|
// Фикс для бага WebKit. При включенной опции дропа одинаковых фреймов,
|
||||||
@@ -723,7 +726,7 @@ static void _http_queue_send_stream(server_s *server, bool stream_updated, bool
|
|||||||
|
|
||||||
has_clients = true;
|
has_clients = true;
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
if (queued) {
|
if (queued) {
|
||||||
static unsigned queued_fps_accum = 0;
|
static unsigned queued_fps_accum = 0;
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
#include "../../libs/process.h"
|
#include "../../libs/process.h"
|
||||||
#include "../../libs/frame.h"
|
#include "../../libs/frame.h"
|
||||||
#include "../../libs/base64.h"
|
#include "../../libs/base64.h"
|
||||||
|
#include "../../libs/list.h"
|
||||||
#include "../data/index_html.h"
|
#include "../data/index_html.h"
|
||||||
#include "../encoder.h"
|
#include "../encoder.h"
|
||||||
#include "../stream.h"
|
#include "../stream.h"
|
||||||
@@ -91,8 +92,7 @@ typedef struct stream_client_sx {
|
|||||||
unsigned fps_accum;
|
unsigned fps_accum;
|
||||||
long long fps_accum_second;
|
long long fps_accum_second;
|
||||||
|
|
||||||
struct stream_client_sx *prev;
|
LIST_STRUCT(struct stream_client_sx);
|
||||||
struct stream_client_sx *next;
|
|
||||||
} stream_client_s;
|
} stream_client_s;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@@ -440,11 +440,11 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
|
|||||||
# undef ADD_SINK
|
# undef ADD_SINK
|
||||||
|
|
||||||
# ifdef WITH_GPIO
|
# ifdef WITH_GPIO
|
||||||
case _O_GPIO_DEVICE: OPT_SET(gpio.path, optarg);
|
case _O_GPIO_DEVICE: OPT_SET(us_gpio.path, optarg);
|
||||||
case _O_GPIO_CONSUMER_PREFIX: OPT_SET(gpio.consumer_prefix, optarg);
|
case _O_GPIO_CONSUMER_PREFIX: OPT_SET(us_gpio.consumer_prefix, optarg);
|
||||||
case _O_GPIO_PROG_RUNNING: OPT_NUMBER("--gpio-prog-running", gpio.prog_running.pin, 0, 256, 0);
|
case _O_GPIO_PROG_RUNNING: OPT_NUMBER("--gpio-prog-running", us_gpio.prog_running.pin, 0, 256, 0);
|
||||||
case _O_GPIO_STREAM_ONLINE: OPT_NUMBER("--gpio-stream-online", gpio.stream_online.pin, 0, 256, 0);
|
case _O_GPIO_STREAM_ONLINE: OPT_NUMBER("--gpio-stream-online", us_gpio.stream_online.pin, 0, 256, 0);
|
||||||
case _O_GPIO_HAS_HTTP_CLIENTS: OPT_NUMBER("--gpio-has-http-clients", gpio.has_http_clients.pin, 0, 256, 0);
|
case _O_GPIO_HAS_HTTP_CLIENTS: OPT_NUMBER("--gpio-has-http-clients", us_gpio.has_http_clients.pin, 0, 256, 0);
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# ifdef HAS_PDEATHSIG
|
# ifdef HAS_PDEATHSIG
|
||||||
@@ -459,12 +459,12 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
|
|||||||
# endif
|
# endif
|
||||||
case _O_NOTIFY_PARENT: OPT_SET(server->notify_parent, true);
|
case _O_NOTIFY_PARENT: OPT_SET(server->notify_parent, true);
|
||||||
|
|
||||||
case _O_LOG_LEVEL: OPT_NUMBER("--log-level", log_level, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, 0);
|
case _O_LOG_LEVEL: OPT_NUMBER("--log-level", us_log_level, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, 0);
|
||||||
case _O_PERF: OPT_SET(log_level, LOG_LEVEL_PERF);
|
case _O_PERF: OPT_SET(us_log_level, LOG_LEVEL_PERF);
|
||||||
case _O_VERBOSE: OPT_SET(log_level, LOG_LEVEL_VERBOSE);
|
case _O_VERBOSE: OPT_SET(us_log_level, LOG_LEVEL_VERBOSE);
|
||||||
case _O_DEBUG: OPT_SET(log_level, LOG_LEVEL_DEBUG);
|
case _O_DEBUG: OPT_SET(us_log_level, LOG_LEVEL_DEBUG);
|
||||||
case _O_FORCE_LOG_COLORS: OPT_SET(log_colored, true);
|
case _O_FORCE_LOG_COLORS: OPT_SET(us_log_colored, true);
|
||||||
case _O_NO_LOG_COLORS: OPT_SET(log_colored, false);
|
case _O_NO_LOG_COLORS: OPT_SET(us_log_colored, false);
|
||||||
|
|
||||||
case _O_HELP: _help(stdout, dev, enc, stream, server); return 1;
|
case _O_HELP: _help(stdout, dev, enc, stream, server); return 1;
|
||||||
case _O_VERSION: puts(VERSION); return 1;
|
case _O_VERSION: puts(VERSION); return 1;
|
||||||
@@ -679,8 +679,8 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser
|
|||||||
# ifdef WITH_GPIO
|
# ifdef WITH_GPIO
|
||||||
SAY("GPIO options:");
|
SAY("GPIO options:");
|
||||||
SAY("═════════════");
|
SAY("═════════════");
|
||||||
SAY(" --gpio-device </dev/path> ───── Path to GPIO character device. Default: %s.\n", gpio.path);
|
SAY(" --gpio-device </dev/path> ───── Path to GPIO character device. Default: %s.\n", us_gpio.path);
|
||||||
SAY(" --gpio-consumer-prefix <str> ── Consumer prefix for GPIO outputs. Default: %s.\n", gpio.consumer_prefix);
|
SAY(" --gpio-consumer-prefix <str> ── Consumer prefix for GPIO outputs. Default: %s.\n", us_gpio.consumer_prefix);
|
||||||
SAY(" --gpio-prog-running <pin> ───── Set 1 on GPIO pin while uStreamer is running. Default: disabled.\n");
|
SAY(" --gpio-prog-running <pin> ───── Set 1 on GPIO pin while uStreamer is running. Default: disabled.\n");
|
||||||
SAY(" --gpio-stream-online <pin> ──── Set 1 while streaming. Default: disabled.\n");
|
SAY(" --gpio-stream-online <pin> ──── Set 1 while streaming. Default: disabled.\n");
|
||||||
SAY(" --gpio-has-http-clients <pin> ─ Set 1 while stream has at least one client. Default: disabled.\n");
|
SAY(" --gpio-has-http-clients <pin> ─ Set 1 while stream has at least one client. Default: disabled.\n");
|
||||||
@@ -703,7 +703,7 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser
|
|||||||
SAY(" --log-level <N> ──── Verbosity level of messages from 0 (info) to 3 (debug).");
|
SAY(" --log-level <N> ──── Verbosity level of messages from 0 (info) to 3 (debug).");
|
||||||
SAY(" Enabling debugging messages can slow down the program.");
|
SAY(" Enabling debugging messages can slow down the program.");
|
||||||
SAY(" Available levels: 0 (info), 1 (performance), 2 (verbose), 3 (debug).");
|
SAY(" Available levels: 0 (info), 1 (performance), 2 (verbose), 3 (debug).");
|
||||||
SAY(" Default: %d.\n", log_level);
|
SAY(" Default: %d.\n", us_log_level);
|
||||||
SAY(" --perf ───────────── Enable performance messages (same as --log-level=1). Default: disabled.\n");
|
SAY(" --perf ───────────── Enable performance messages (same as --log-level=1). Default: disabled.\n");
|
||||||
SAY(" --verbose ────────── Enable verbose messages and lower (same as --log-level=2). Default: disabled.\n");
|
SAY(" --verbose ────────── Enable verbose messages and lower (same as --log-level=2). Default: disabled.\n");
|
||||||
SAY(" --debug ──────────── Enable debug messages and lower (same as --log-level=3). Default: disabled.\n");
|
SAY(" --debug ──────────── Enable debug messages and lower (same as --log-level=3). Default: disabled.\n");
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ stream_s *stream_init(device_s *dev, encoder_s *enc) {
|
|||||||
|
|
||||||
video_s *video;
|
video_s *video;
|
||||||
A_CALLOC(video, 1);
|
A_CALLOC(video, 1);
|
||||||
video->frame = frame_init("stream_video");
|
video->frame = frame_init();
|
||||||
atomic_init(&video->updated, false);
|
atomic_init(&video->updated, false);
|
||||||
A_MUTEX_INIT(&video->mutex);
|
A_MUTEX_INIT(&video->mutex);
|
||||||
atomic_init(&video->has_clients, false);
|
atomic_init(&video->has_clients, false);
|
||||||
@@ -136,9 +136,9 @@ void stream_loop(stream_s *stream) {
|
|||||||
&& !atomic_load(&RUN(stop))
|
&& !atomic_load(&RUN(stop))
|
||||||
&& !atomic_load(&RUN(video->has_clients))
|
&& !atomic_load(&RUN(video->has_clients))
|
||||||
// has_clients синков НЕ обновляются в реальном времени
|
// has_clients синков НЕ обновляются в реальном времени
|
||||||
&& (stream->sink == NULL || !stream->sink->has_clients)
|
&& (stream->sink == NULL || !atomic_load(&stream->sink->has_clients))
|
||||||
# ifdef WITH_OMX
|
# ifdef WITH_OMX
|
||||||
&& (RUN(h264) == NULL || /*RUN(h264->sink) == NULL ||*/ !RUN(h264->sink->has_clients))
|
&& (RUN(h264) == NULL || /*RUN(h264->sink) == NULL ||*/ !atomic_load(&RUN(h264->sink->has_clients)))
|
||||||
# endif
|
# endif
|
||||||
) {
|
) {
|
||||||
usleep(100000);
|
usleep(100000);
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ static void *_worker_thread(void *v_worker);
|
|||||||
|
|
||||||
workers_pool_s *workers_pool_init(
|
workers_pool_s *workers_pool_init(
|
||||||
const char *name, const char *wr_prefix, unsigned n_workers, long double desired_interval,
|
const char *name, const char *wr_prefix, unsigned n_workers, long double desired_interval,
|
||||||
void *(*job_init)(worker_s *wr, void *arg), void *job_init_arg,
|
workers_pool_job_init_f job_init, void *job_init_arg,
|
||||||
void (*job_destroy)(void *),
|
workers_pool_job_destroy_f job_destroy,
|
||||||
bool (*run_job)(worker_s *)) {
|
workers_pool_run_job_f run_job) {
|
||||||
|
|
||||||
LOG_INFO("Creating pool %s with %u workers ...", name, n_workers);
|
LOG_INFO("Creating pool %s with %u workers ...", name, n_workers);
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ workers_pool_s *workers_pool_init(
|
|||||||
A_COND_INIT(&WR(has_job_cond));
|
A_COND_INIT(&WR(has_job_cond));
|
||||||
|
|
||||||
WR(pool) = pool;
|
WR(pool) = pool;
|
||||||
WR(job) = job_init(&pool->workers[number], job_init_arg);
|
WR(job) = job_init(job_init_arg);
|
||||||
|
|
||||||
A_THREAD_CREATE(&WR(tid), _worker_thread, (void *)&(pool->workers[number]));
|
A_THREAD_CREATE(&WR(tid), _worker_thread, (void *)&(pool->workers[number]));
|
||||||
pool->free_workers += 1;
|
pool->free_workers += 1;
|
||||||
|
|||||||
@@ -55,12 +55,16 @@ typedef struct worker_sx {
|
|||||||
struct workers_pool_sx *pool;
|
struct workers_pool_sx *pool;
|
||||||
} worker_s;
|
} worker_s;
|
||||||
|
|
||||||
|
typedef void *(*workers_pool_job_init_f)(void *arg);
|
||||||
|
typedef void (*workers_pool_job_destroy_f)(void *job);
|
||||||
|
typedef bool (*workers_pool_run_job_f)(worker_s *wr);
|
||||||
|
|
||||||
typedef struct workers_pool_sx {
|
typedef struct workers_pool_sx {
|
||||||
const char *name;
|
const char *name;
|
||||||
long double desired_interval;
|
long double desired_interval;
|
||||||
|
|
||||||
bool (*run_job)(worker_s *wr);
|
workers_pool_job_destroy_f job_destroy;
|
||||||
void (*job_destroy)(void *job);
|
workers_pool_run_job_f run_job;
|
||||||
|
|
||||||
unsigned n_workers;
|
unsigned n_workers;
|
||||||
worker_s *workers;
|
worker_s *workers;
|
||||||
@@ -79,9 +83,9 @@ typedef struct workers_pool_sx {
|
|||||||
|
|
||||||
workers_pool_s *workers_pool_init(
|
workers_pool_s *workers_pool_init(
|
||||||
const char *name, const char *wr_prefix, unsigned n_workers, long double desired_interval,
|
const char *name, const char *wr_prefix, unsigned n_workers, long double desired_interval,
|
||||||
void *(*job_init)(worker_s *wr, void *arg), void *job_init_arg,
|
workers_pool_job_init_f job_init, void *job_init_arg,
|
||||||
void (*job_destroy)(void *job),
|
workers_pool_job_destroy_f job_destroy,
|
||||||
bool (*run_job)(worker_s *));
|
workers_pool_run_job_f run_job);
|
||||||
|
|
||||||
void workers_pool_destroy(workers_pool_s *pool);
|
void workers_pool_destroy(workers_pool_s *pool);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user