mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-27 20:26:31 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b4bcd09fe | ||
|
|
245cd94a4c | ||
|
|
8a24a0b129 | ||
|
|
b362c1fa50 | ||
|
|
0414fa5a51 | ||
|
|
191dd16d7b | ||
|
|
31a03928c9 | ||
|
|
185a8b6773 | ||
|
|
b7501cfbda | ||
|
|
1dc90dad7a | ||
|
|
45a4d148f5 | ||
|
|
93ab12b54b | ||
|
|
fb07444b70 | ||
|
|
a8ba2f7364 | ||
|
|
171bcb315a | ||
|
|
911df1af15 | ||
|
|
a165ff4523 |
@@ -1,7 +1,7 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
current_version = 3.14
|
current_version = 3.17
|
||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?)?
|
parse = (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?)?
|
||||||
serialize =
|
serialize =
|
||||||
{major}.{minor}
|
{major}.{minor}
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -39,7 +39,7 @@ You'll need ```make```, ```gcc```, ```libevent``` with ```pthreads``` support,
|
|||||||
* Arch: `sudo pacman -S libevent libjpeg-turbo libutil-linux libbsd`.
|
* Arch: `sudo pacman -S libevent libjpeg-turbo libutil-linux libbsd`.
|
||||||
* Raspbian: `sudo apt install libevent-dev libjpeg8-dev uuid-dev libbsd-dev`. Add `libraspberrypi-dev` for `WITH_OMX=1` and `libgpiod` for `WITH_GPIO=1`.
|
* Raspbian: `sudo apt install libevent-dev libjpeg8-dev uuid-dev libbsd-dev`. Add `libraspberrypi-dev` for `WITH_OMX=1` and `libgpiod` for `WITH_GPIO=1`.
|
||||||
* Debian: `sudo apt install build-essential libevent-dev libjpeg62-turbo-dev uuid-dev libbsd-dev`.
|
* Debian: `sudo apt install build-essential libevent-dev libjpeg62-turbo-dev uuid-dev libbsd-dev`.
|
||||||
* Ubuntu 20.04 x86_64: `sudo apt install build-essential libevent-dev libjpeg62-dev uuid-dev libbsd-dev make gcc libjpeg8 libjpeg-turbo8 libuuid1 libbsd0`.
|
* Ubuntu 20.04 x86_64: `sudo apt install build-essential libevent-dev libjpeg-dev libjpeg62-dev uuid-dev libbsd-dev make gcc libjpeg8 libjpeg-turbo8 libuuid1 libbsd0`.
|
||||||
|
|
||||||
On Raspberry Pi you can build the program with OpenMAX IL. To do this pass option ```WITH_OMX=1``` to ```make```. To enable GPIO support install [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) and pass option ```WITH_GPIO=1```. If the compiler reports about a missing function ```pthread_get_name_np()``` (or similar), add option ```WITH_PTHREAD_NP=0``` (it's enabled by default). For the similar error with ```setproctitle()``` add option ```WITH_SETPROCTITLE=0```.
|
On Raspberry Pi you can build the program with OpenMAX IL. To do this pass option ```WITH_OMX=1``` to ```make```. To enable GPIO support install [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) and pass option ```WITH_GPIO=1```. If the compiler reports about a missing function ```pthread_get_name_np()``` (or similar), add option ```WITH_PTHREAD_NP=0``` (it's enabled by default). For the similar error with ```setproctitle()``` add option ```WITH_SETPROCTITLE=0```.
|
||||||
|
|
||||||
@@ -77,6 +77,22 @@ $ ./ustreamer \
|
|||||||
|
|
||||||
You can always view the full list of options with ```ustreamer --help```.
|
You can always view the full list of options with ```ustreamer --help```.
|
||||||
|
|
||||||
|
-----
|
||||||
|
# Raspberry Pi Camera Example
|
||||||
|
Example usage for the Raspberry Pi v1 camera:
|
||||||
|
```bash
|
||||||
|
$ sudo modprobe bcm2835-v4l2
|
||||||
|
$ ./ustreamer --host :: -m jpeg --device-timeout=5 --buffers=3 -r 2592x1944
|
||||||
|
```
|
||||||
|
|
||||||
|
:exclamation: Please note that newer camera models have a different maximum resolution. You can see the supported resolutions at the [PiCamera documentation](https://picamera.readthedocs.io/en/release-1.13/fov.html#sensor-modes).
|
||||||
|
|
||||||
|
:exclamation: If you get a poor framerate, it could be that the camera is switched to photo mode, which produces a low framerate (but a higher quality picture). This is because `bcm2835-v4l2` switches to photo mode at resolutions higher than `1280x720`. To work around this, pass the `max_video_width` and `max_video_height` module parameters like so:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ modprobe bcm2835-v4l2 max_video_width=2592 max_video_height=1944
|
||||||
|
```
|
||||||
|
|
||||||
-----
|
-----
|
||||||
# See also
|
# See also
|
||||||
* [Running uStreamer via systemd service](https://github.com/pikvm/ustreamer/issues/16).
|
* [Running uStreamer via systemd service](https://github.com/pikvm/ustreamer/issues/16).
|
||||||
|
|||||||
18
README.ru.md
18
README.ru.md
@@ -39,7 +39,7 @@
|
|||||||
* Arch: `sudo pacman -S libevent libjpeg-turbo libutil-linux libbsd`.
|
* Arch: `sudo pacman -S libevent libjpeg-turbo libutil-linux libbsd`.
|
||||||
* Raspbian: `sudo apt install libevent-dev libjpeg8-dev uuid-dev libbsd-dev`. Добавьте `libraspberrypi-dev` для сборки с `WITH_OMX=1` и `libgpiod` для `WITH_GPIO=1`.
|
* Raspbian: `sudo apt install libevent-dev libjpeg8-dev uuid-dev libbsd-dev`. Добавьте `libraspberrypi-dev` для сборки с `WITH_OMX=1` и `libgpiod` для `WITH_GPIO=1`.
|
||||||
* Debian: `sudo apt install build-essential libevent-dev libjpeg62-turbo-dev uuid-dev libbsd-dev`.
|
* Debian: `sudo apt install build-essential libevent-dev libjpeg62-turbo-dev uuid-dev libbsd-dev`.
|
||||||
* Ubuntu 20.04 x86_64: `sudo apt install build-essential libevent-dev libjpeg62-dev uuid-dev libbsd-dev make gcc libjpeg8 libjpeg-turbo8 libuuid1 libbsd0`.
|
* Ubuntu 20.04 x86_64: `sudo apt install build-essential libevent-dev libjpeg-dev libjpeg62-dev uuid-dev libbsd-dev make gcc libjpeg8 libjpeg-turbo8 libuuid1 libbsd0`.
|
||||||
|
|
||||||
На Raspberry Pi программу можно собрать с поддержкой OpenMAX IL. Для этого передайте ```make``` параметр ```WITH_OMX=1```. Для включения сборки с поддержкой GPIO установите [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) и добавьте параметр ```WITH_GPIO=1```. Если при сборке компилятор ругается на отсутствие функции ```pthread_get_name_np()``` или другой подобной, добавьте параметр ```WITH_PTHREAD_NP=0``` (по умолчанию он включен). При аналогичной ошибке с функцией ```setproctitle()``` добавьте параметр ```WITH_SETPROCTITLE=0```.
|
На Raspberry Pi программу можно собрать с поддержкой OpenMAX IL. Для этого передайте ```make``` параметр ```WITH_OMX=1```. Для включения сборки с поддержкой GPIO установите [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) и добавьте параметр ```WITH_GPIO=1```. Если при сборке компилятор ругается на отсутствие функции ```pthread_get_name_np()``` или другой подобной, добавьте параметр ```WITH_PTHREAD_NP=0``` (по умолчанию он включен). При аналогичной ошибке с функцией ```setproctitle()``` добавьте параметр ```WITH_SETPROCTITLE=0```.
|
||||||
|
|
||||||
@@ -77,6 +77,22 @@ $ ./ustreamer \
|
|||||||
|
|
||||||
За полным списком опций обращайтесь ко встроенной справке: ```ustreamer --help```.
|
За полным списком опций обращайтесь ко встроенной справке: ```ustreamer --help```.
|
||||||
|
|
||||||
|
-----
|
||||||
|
# Камера Raspberry Pi
|
||||||
|
Пример использования камеры Raspberry Pi v1:
|
||||||
|
```bash
|
||||||
|
$ sudo modprobe bcm2835-v4l2
|
||||||
|
$ ./ustreamer --host :: -m jpeg --device-timeout=5 --buffers=3 -r 2592x1944
|
||||||
|
```
|
||||||
|
|
||||||
|
:exclamation: Обратите внимание что боле новые модели камеры имеют другое максимальное разрешение. Список поддерживаемых разрешений можно найти в [документации PiCamera](https://picamera.readthedocs.io/en/release-1.13/fov.html#sensor-modes).
|
||||||
|
|
||||||
|
:exclamation: Если камера выдает низкий фреймрейт, возможно что она работает в фото-режиме, где производит более низкий фпс, но более качественную кратинку. Это происходит потому что `bcm2835-v4l2` переключает камеру в фото-режим на разрешениях выше `1280x720`. Чтобы обойти это, передайте параметры `max_video_width` и `max_video_height` при загрузке модуля, например:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ modprobe bcm2835-v4l2 max_video_width=2592 max_video_height=1944
|
||||||
|
```
|
||||||
|
|
||||||
-----
|
-----
|
||||||
# Смотрите также
|
# Смотрите также
|
||||||
* [Запуск с помощью systemd-сервиса](https://github.com/pikvm/ustreamer/issues/16).
|
* [Запуск с помощью systemd-сервиса](https://github.com/pikvm/ustreamer/issues/16).
|
||||||
|
|||||||
@@ -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.14" "January 2021"
|
.TH USTREAMER-DUMP 1 "version 3.17" "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.14" "November 2020"
|
.TH USTREAMER 1 "version 3.17" "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.14
|
pkgver=3.17
|
||||||
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.14
|
PKG_VERSION:=3.17
|
||||||
PKG_RELEASE:=1
|
PKG_RELEASE:=1
|
||||||
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from distutils.core import setup
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
setup(
|
setup(
|
||||||
name="ustreamer",
|
name="ustreamer",
|
||||||
version="3.14",
|
version="3.17",
|
||||||
description="uStreamer tools",
|
description="uStreamer tools",
|
||||||
author="Maxim Devaev",
|
author="Maxim Devaev",
|
||||||
author_email="mdevaev@gmail.com",
|
author_email="mdevaev@gmail.com",
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ typedef struct {
|
|||||||
unsigned stride;
|
unsigned stride;
|
||||||
|
|
||||||
bool online;
|
bool online;
|
||||||
|
bool key;
|
||||||
|
|
||||||
long double grab_ts;
|
long double grab_ts;
|
||||||
long double encode_begin_ts;
|
long double encode_begin_ts;
|
||||||
@@ -195,6 +196,7 @@ static int wait_frame(MemsinkObject *self) {
|
|||||||
&& CMP(format)
|
&& CMP(format)
|
||||||
&& CMP(stride)
|
&& CMP(stride)
|
||||||
&& CMP(online)
|
&& CMP(online)
|
||||||
|
&& CMP(key)
|
||||||
&& (TMP(ts) + self->drop_same_frames > now)
|
&& (TMP(ts) + self->drop_same_frames > now)
|
||||||
&& !memcmp(TMP(data), MEM(data), MEM(used))
|
&& !memcmp(TMP(data), MEM(data), MEM(used))
|
||||||
) {
|
) {
|
||||||
@@ -249,6 +251,7 @@ static PyObject *MemsinkObject_wait_frame(MemsinkObject *self, PyObject *Py_UNUS
|
|||||||
COPY(format);
|
COPY(format);
|
||||||
COPY(stride);
|
COPY(stride);
|
||||||
COPY(online);
|
COPY(online);
|
||||||
|
COPY(key);
|
||||||
COPY(grab_ts);
|
COPY(grab_ts);
|
||||||
COPY(encode_begin_ts);
|
COPY(encode_begin_ts);
|
||||||
COPY(encode_end_ts);
|
COPY(encode_end_ts);
|
||||||
@@ -291,6 +294,7 @@ static PyObject *MemsinkObject_wait_frame(MemsinkObject *self, PyObject *Py_UNUS
|
|||||||
SET_NUMBER(format, Long, Long);
|
SET_NUMBER(format, Long, Long);
|
||||||
SET_NUMBER(stride, Long, Long);
|
SET_NUMBER(stride, Long, Long);
|
||||||
SET_NUMBER(online, Long, Bool);
|
SET_NUMBER(online, Long, Bool);
|
||||||
|
SET_NUMBER(key, Long, Bool);
|
||||||
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);
|
||||||
|
|||||||
@@ -213,6 +213,8 @@ static int _dump_sink(const char *sink_name, unsigned sink_timeout, _output_cont
|
|||||||
unsigned fps_accum = 0;
|
unsigned fps_accum = 0;
|
||||||
long long fps_second = 0;
|
long long fps_second = 0;
|
||||||
|
|
||||||
|
long double last_ts = 0;
|
||||||
|
|
||||||
while (!global_stop) {
|
while (!global_stop) {
|
||||||
int error = memsink_client_get(sink, frame);
|
int error = memsink_client_get(sink, frame);
|
||||||
if (error == 0) {
|
if (error == 0) {
|
||||||
@@ -220,11 +222,12 @@ static int _dump_sink(const char *sink_name, unsigned sink_timeout, _output_cont
|
|||||||
const long long now_second = floor_ms(now);
|
const long long now_second = floor_ms(now);
|
||||||
|
|
||||||
char fourcc_str[8];
|
char fourcc_str[8];
|
||||||
LOG_VERBOSE("Frame: size=%zu, resolution=%ux%u, fourcc=%s, stride=%u, online=%d, latency=%.3Lf",
|
LOG_VERBOSE("Frame: size=%zu, res=%ux%u, fourcc=%s, stride=%u, online=%d, key=%d, latency=%.3Lf, diff=%.3Lf",
|
||||||
frame->used, frame->width, frame->height,
|
frame->used, frame->width, frame->height,
|
||||||
fourcc_to_string(frame->format, fourcc_str, 8),
|
fourcc_to_string(frame->format, fourcc_str, 8),
|
||||||
frame->stride, frame->online,
|
frame->stride, frame->online, frame->key,
|
||||||
now - frame->grab_ts);
|
now - frame->grab_ts, (last_ts ? now - last_ts : 0));
|
||||||
|
last_ts = now;
|
||||||
|
|
||||||
LOG_DEBUG(" grab_ts=%.3Lf, encode_begin_ts=%.3Lf, encode_end_ts=%.3Lf",
|
LOG_DEBUG(" grab_ts=%.3Lf, encode_begin_ts=%.3Lf, encode_end_ts=%.3Lf",
|
||||||
frame->grab_ts, frame->encode_begin_ts, frame->encode_end_ts);
|
frame->grab_ts, frame->encode_begin_ts, frame->encode_end_ts);
|
||||||
|
|||||||
@@ -23,5 +23,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef VERSION
|
#ifndef VERSION
|
||||||
# define VERSION "3.14"
|
# define VERSION "3.17"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ void frame_copy_meta(const frame_s *src, frame_s *dest) {
|
|||||||
COPY(format);
|
COPY(format);
|
||||||
COPY(stride);
|
COPY(stride);
|
||||||
COPY(online);
|
COPY(online);
|
||||||
|
COPY(key);
|
||||||
COPY(grab_ts);
|
COPY(grab_ts);
|
||||||
COPY(encode_begin_ts);
|
COPY(encode_begin_ts);
|
||||||
COPY(encode_end_ts);
|
COPY(encode_end_ts);
|
||||||
@@ -98,6 +99,7 @@ bool frame_compare(const frame_s *a, const frame_s *b) {
|
|||||||
&& CMP(format)
|
&& CMP(format)
|
||||||
&& CMP(stride)
|
&& CMP(stride)
|
||||||
&& CMP(online)
|
&& CMP(online)
|
||||||
|
&& CMP(key)
|
||||||
&& !memcmp(a->data, b->data, b->used)
|
&& !memcmp(a->data, b->data, b->used)
|
||||||
);
|
);
|
||||||
# undef CMP
|
# undef CMP
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ typedef struct {
|
|||||||
// https://medium.com/@oleg.shipitko/what-does-stride-mean-in-image-processing-bba158a72bcd
|
// https://medium.com/@oleg.shipitko/what-does-stride-mean-in-image-processing-bba158a72bcd
|
||||||
|
|
||||||
bool online;
|
bool online;
|
||||||
|
bool key;
|
||||||
|
|
||||||
long double grab_ts;
|
long double grab_ts;
|
||||||
long double encode_begin_ts;
|
long double encode_begin_ts;
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ bool memsink_server_check(memsink_s *sink, const frame_s *frame) {
|
|||||||
sink->has_clients = (sink->mem->last_client_ts + sink->client_ttl > get_now_monotonic());
|
sink->has_clients = (sink->mem->last_client_ts + sink->client_ttl > get_now_monotonic());
|
||||||
|
|
||||||
# define NEQ(_field) (sink->mem->_field != frame->_field)
|
# define NEQ(_field) (sink->mem->_field != frame->_field)
|
||||||
bool retval = (sink->has_clients || NEQ(width) || NEQ(height) || NEQ(format) || NEQ(stride) || NEQ(online));
|
bool retval = (sink->has_clients || NEQ(width) || NEQ(height) || NEQ(format) || NEQ(stride) || NEQ(online) || NEQ(key));
|
||||||
# undef NEQ
|
# undef NEQ
|
||||||
|
|
||||||
if (flock(sink->fd, LOCK_UN) < 0) {
|
if (flock(sink->fd, LOCK_UN) < 0) {
|
||||||
@@ -142,6 +142,7 @@ int memsink_server_put(memsink_s *sink, const frame_s *frame) {
|
|||||||
COPY(format);
|
COPY(format);
|
||||||
COPY(stride);
|
COPY(stride);
|
||||||
COPY(online);
|
COPY(online);
|
||||||
|
COPY(key);
|
||||||
COPY(grab_ts);
|
COPY(grab_ts);
|
||||||
COPY(encode_begin_ts);
|
COPY(encode_begin_ts);
|
||||||
COPY(encode_end_ts);
|
COPY(encode_end_ts);
|
||||||
@@ -195,6 +196,7 @@ int memsink_client_get(memsink_s *sink, frame_s *frame) { // cppcheck-suppress u
|
|||||||
COPY(format);
|
COPY(format);
|
||||||
COPY(stride);
|
COPY(stride);
|
||||||
COPY(online);
|
COPY(online);
|
||||||
|
COPY(key);
|
||||||
COPY(grab_ts);
|
COPY(grab_ts);
|
||||||
COPY(encode_begin_ts);
|
COPY(encode_begin_ts);
|
||||||
COPY(encode_end_ts);
|
COPY(encode_end_ts);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#define MEMSINK_MAGIC ((uint64_t)0xCAFEBABECAFEBABE)
|
#define MEMSINK_MAGIC ((uint64_t)0xCAFEBABECAFEBABE)
|
||||||
#define MEMSINK_VERSION ((uint32_t)1)
|
#define MEMSINK_VERSION ((uint32_t)2)
|
||||||
|
|
||||||
#ifndef CFG_MEMSINK_MAX_DATA
|
#ifndef CFG_MEMSINK_MAX_DATA
|
||||||
# define CFG_MEMSINK_MAX_DATA 33554432
|
# define CFG_MEMSINK_MAX_DATA 33554432
|
||||||
@@ -49,6 +49,7 @@ typedef struct {
|
|||||||
unsigned format;
|
unsigned format;
|
||||||
unsigned stride;
|
unsigned stride;
|
||||||
bool online;
|
bool online;
|
||||||
|
bool key;
|
||||||
|
|
||||||
long double grab_ts;
|
long double grab_ts;
|
||||||
long double encode_begin_ts;
|
long double encode_begin_ts;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ static const struct {
|
|||||||
{"UYVY", V4L2_PIX_FMT_UYVY},
|
{"UYVY", V4L2_PIX_FMT_UYVY},
|
||||||
{"RGB565", V4L2_PIX_FMT_RGB565},
|
{"RGB565", V4L2_PIX_FMT_RGB565},
|
||||||
{"RGB24", V4L2_PIX_FMT_RGB24},
|
{"RGB24", V4L2_PIX_FMT_RGB24},
|
||||||
{"JPEG", V4L2_PIX_FMT_MJPEG},
|
{"MJPEG", V4L2_PIX_FMT_MJPEG},
|
||||||
{"JPEG", V4L2_PIX_FMT_JPEG},
|
{"JPEG", V4L2_PIX_FMT_JPEG},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@
|
|||||||
#define STANDARDS_STR "PAL, NTSC, SECAM"
|
#define STANDARDS_STR "PAL, NTSC, SECAM"
|
||||||
|
|
||||||
#define FORMAT_UNKNOWN -1
|
#define FORMAT_UNKNOWN -1
|
||||||
#define FORMATS_STR "YUYV, UYVY, RGB565, RGB24, JPEG"
|
#define FORMATS_STR "YUYV, UYVY, RGB565, RGB24, MJPEG, JPEG"
|
||||||
|
|
||||||
#define IO_METHOD_UNKNOWN -1
|
#define IO_METHOD_UNKNOWN -1
|
||||||
#define IO_METHODS_STR "MMAP, USERPTR"
|
#define IO_METHODS_STR "MMAP, USERPTR"
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ static const OMX_U32 _INPUT_PORT = 340;
|
|||||||
static const OMX_U32 _OUTPUT_PORT = 341;
|
static const OMX_U32 _OUTPUT_PORT = 341;
|
||||||
|
|
||||||
|
|
||||||
static int _vcos_semwait(VCOS_SEMAPHORE_T *sem);
|
|
||||||
static int _omx_init_component(omx_encoder_s *omx);
|
static int _omx_init_component(omx_encoder_s *omx);
|
||||||
static int _omx_init_disable_ports(omx_encoder_s *omx);
|
static int _omx_init_disable_ports(omx_encoder_s *omx);
|
||||||
static int _omx_setup_input(omx_encoder_s *omx, const frame_s *frame);
|
static int _omx_setup_input(omx_encoder_s *omx, const frame_s *frame);
|
||||||
@@ -194,7 +193,7 @@ int omx_encoder_compress(omx_encoder_s *omx, const frame_s *src, frame_s *dest)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_vcos_semwait(&omx->handler_sem) != 0) {
|
if (vcos_my_semwait("", &omx->handler_sem, 1) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -204,42 +203,6 @@ int omx_encoder_compress(omx_encoder_s *omx, const frame_s *src, frame_s *dest)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _vcos_semwait(VCOS_SEMAPHORE_T *sem) {
|
|
||||||
// vcos_semaphore_wait() can wait infinite
|
|
||||||
// vcos_semaphore_wait_timeout() is broken by design:
|
|
||||||
// - https://github.com/pikvm/ustreamer/issues/56
|
|
||||||
// - https://github.com/raspberrypi/userland/issues/658
|
|
||||||
// CFG_OMX_SEMWAIT_TIMEOUT is ugly busyloop
|
|
||||||
// Три стула.
|
|
||||||
|
|
||||||
# ifdef CFG_OMX_SEMWAIT_TIMEOUT
|
|
||||||
long double deadline_ts = get_now_monotonic() + (long double)CFG_OMX_SEMWAIT_TIMEOUT; // Seconds
|
|
||||||
VCOS_STATUS_T sem_status;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
sem_status = vcos_semaphore_trywait(sem);
|
|
||||||
if (sem_status == VCOS_SUCCESS) {
|
|
||||||
return 0;
|
|
||||||
} else if (sem_status != VCOS_EAGAIN || get_now_monotonic() > deadline_ts) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (usleep(1000) < 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _omx_init_component(omx_encoder_s *omx) {
|
static int _omx_init_component(omx_encoder_s *omx) {
|
||||||
OMX_ERRORTYPE error;
|
OMX_ERRORTYPE error;
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
#include "../../../libs/logging.h"
|
#include "../../../libs/logging.h"
|
||||||
#include "../../../libs/frame.h"
|
#include "../../../libs/frame.h"
|
||||||
|
|
||||||
|
#include "vcos.h"
|
||||||
#include "formatters.h"
|
#include "formatters.h"
|
||||||
#include "component.h"
|
#include "component.h"
|
||||||
|
|
||||||
|
|||||||
55
src/ustreamer/encoders/omx/vcos.c
Normal file
55
src/ustreamer/encoders/omx/vcos.c
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
# #
|
||||||
|
# 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/>. #
|
||||||
|
# #
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#include "vcos.h"
|
||||||
|
|
||||||
|
|
||||||
|
int vcos_my_semwait(const char *prefix, VCOS_SEMAPHORE_T *sem, long double timeout) {
|
||||||
|
// vcos_semaphore_wait() can wait infinite
|
||||||
|
// vcos_semaphore_wait_timeout() is broken by design:
|
||||||
|
// - https://github.com/pikvm/ustreamer/issues/56
|
||||||
|
// - https://github.com/raspberrypi/userland/issues/658
|
||||||
|
// - The current approach is an ugly busyloop
|
||||||
|
// Три стула.
|
||||||
|
|
||||||
|
long double deadline_ts = get_now_monotonic() + timeout;
|
||||||
|
VCOS_STATUS_T sem_status;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
sem_status = vcos_semaphore_trywait(sem);
|
||||||
|
if (sem_status == VCOS_SUCCESS) {
|
||||||
|
return 0;
|
||||||
|
} else if (sem_status != VCOS_EAGAIN || get_now_monotonic() > deadline_ts) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (usleep(1000) < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (sem_status) {
|
||||||
|
case VCOS_EAGAIN: LOG_ERROR("%sCan't wait VCOS semaphore: EAGAIN (timeout)", prefix); break;
|
||||||
|
case VCOS_EINVAL: LOG_ERROR("%sCan't wait VCOS semaphore: EINVAL", prefix); break;
|
||||||
|
default: LOG_ERROR("%sCan't wait VCOS semaphore: %d", prefix, sem_status); break;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
31
src/ustreamer/encoders/omx/vcos.h
Normal file
31
src/ustreamer/encoders/omx/vcos.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
# #
|
||||||
|
# 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 <interface/vcos/vcos_semaphore.h>
|
||||||
|
|
||||||
|
#include "../../../libs/tools.h"
|
||||||
|
#include "../../../libs/logging.h"
|
||||||
|
|
||||||
|
|
||||||
|
int vcos_my_semwait(const char *prefix, VCOS_SEMAPHORE_T *sem, long double timeout);
|
||||||
@@ -212,7 +212,8 @@ int h264_encoder_prepare(h264_encoder_s *enc, const frame_s *frame, bool zero_co
|
|||||||
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_PEAK_RATE, enc->bitrate * 1000);
|
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_PEAK_RATE, enc->bitrate * 1000);
|
||||||
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_MIN_QUANT, 16);
|
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_MIN_QUANT, 16);
|
||||||
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_MAX_QUANT, 34);
|
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_MAX_QUANT, 34);
|
||||||
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_FRAME_LIMIT_BITS, 1000000);
|
// Этот параметр с этим значением фризит кодирование изображения из черно-белой консоли
|
||||||
|
// SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_FRAME_LIMIT_BITS, 1000000);
|
||||||
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_H264_AU_DELIMITERS, MMAL_FALSE);
|
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_H264_AU_DELIMITERS, MMAL_FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +225,7 @@ int h264_encoder_prepare(h264_encoder_s *enc, const frame_s *frame, bool zero_co
|
|||||||
|
|
||||||
error:
|
error:
|
||||||
_h264_encoder_cleanup(enc);
|
_h264_encoder_cleanup(enc);
|
||||||
LOG_ERROR("H264: Encoder disabled due error (prepare)");
|
LOG_ERROR("H264: Encoder destroyed due an error (prepare)");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
# undef ENABLE_PORT
|
# undef ENABLE_PORT
|
||||||
@@ -275,7 +276,7 @@ int h264_encoder_compress(h264_encoder_s *enc, const frame_s *src, int src_vcsm_
|
|||||||
|
|
||||||
if (_h264_encoder_compress_raw(enc, src, src_vcsm_handle, dest, force_key) < 0) {
|
if (_h264_encoder_compress_raw(enc, src, src_vcsm_handle, dest, force_key) < 0) {
|
||||||
_h264_encoder_cleanup(enc);
|
_h264_encoder_cleanup(enc);
|
||||||
LOG_ERROR("H264: Encoder disabled due error (compress)");
|
LOG_ERROR("H264: Encoder destroyed due an error (compress)");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,7 +340,9 @@ static int _h264_encoder_compress_raw(h264_encoder_s *enc, const frame_s *src, i
|
|||||||
|
|
||||||
error = mmal_wrapper_buffer_get_full(enc->output_port, &out, 0);
|
error = mmal_wrapper_buffer_get_full(enc->output_port, &out, 0);
|
||||||
if (error == MMAL_EAGAIN) {
|
if (error == MMAL_EAGAIN) {
|
||||||
vcos_semaphore_wait(&enc->handler_sem);
|
if (vcos_my_semwait("H264: ", &enc->handler_sem, 1) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
} else if (error != MMAL_SUCCESS) {
|
} else if (error != MMAL_SUCCESS) {
|
||||||
LOG_ERROR_MMAL(error, "H264: Can't get MMAL output buffer");
|
LOG_ERROR_MMAL(error, "H264: Can't get MMAL output buffer");
|
||||||
@@ -347,6 +350,7 @@ static int _h264_encoder_compress_raw(h264_encoder_s *enc, const frame_s *src, i
|
|||||||
}
|
}
|
||||||
|
|
||||||
frame_append_data(dest, out->data, out->length);
|
frame_append_data(dest, out->data, out->length);
|
||||||
|
dest->key = out->flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME;
|
||||||
|
|
||||||
eos = out->flags & MMAL_BUFFER_HEADER_FLAG_EOS;
|
eos = out->flags & MMAL_BUFFER_HEADER_FLAG_EOS;
|
||||||
mmal_buffer_header_release(out);
|
mmal_buffer_header_release(out);
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
#include "../../libs/tools.h"
|
#include "../../libs/tools.h"
|
||||||
#include "../../libs/logging.h"
|
#include "../../libs/logging.h"
|
||||||
#include "../../libs/frame.h"
|
#include "../../libs/frame.h"
|
||||||
|
#include "../encoders/omx/vcos.h"
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user