mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-03-01 05:06:32 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5aebc1231 | ||
|
|
5f4f46bbe6 | ||
|
|
61a2fe6546 | ||
|
|
98499b6604 | ||
|
|
f52d090f9b | ||
|
|
ebb3df46b9 | ||
|
|
8fd6659cf1 | ||
|
|
13ce0bbc63 | ||
|
|
0add4cc25f | ||
|
|
96d84b33bd | ||
|
|
f2dfe7641e |
@@ -1,7 +1,7 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
current_version = 1.17
|
current_version = 1.18
|
||||||
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}
|
||||||
@@ -17,4 +17,3 @@ replace = pkgver={new_version}
|
|||||||
[bumpversion:file:pkg/openwrt/Makefile]
|
[bumpversion:file:pkg/openwrt/Makefile]
|
||||||
search = PKG_VERSION:={current_version}
|
search = PKG_VERSION:={current_version}
|
||||||
replace = PKG_VERSION:={new_version}
|
replace = PKG_VERSION:={new_version}
|
||||||
|
|
||||||
|
|||||||
4
.github/FUNDING.yml
vendored
Normal file
4
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
patreon: pikvm
|
||||||
|
custom: https://www.paypal.me/mdevaev
|
||||||
@@ -36,7 +36,7 @@ If you're going to live-stream from your backyard webcam and need to control it,
|
|||||||
You'll need ```make```, ```gcc```, ```libevent``` with ```pthreads``` support, ```libjpeg8```/```libjpeg-turbo```, ```libuuid``` and ```libbsd``` (only for Linux).
|
You'll need ```make```, ```gcc```, ```libevent``` with ```pthreads``` support, ```libjpeg8```/```libjpeg-turbo```, ```libuuid``` and ```libbsd``` (only for Linux).
|
||||||
|
|
||||||
* 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 libevent-pthreads-2.1-6-dev libjpeg8-dev uuid-dev libbsd-dev`.
|
* Raspbian: `sudo apt install libevent-dev libjpeg8-dev uuid-dev libbsd-dev`.
|
||||||
* 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`.
|
||||||
|
|
||||||
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 [wiringPi](http://wiringpi.com) 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 [wiringPi](http://wiringpi.com) 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```.
|
||||||
@@ -72,8 +72,9 @@ $ ./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```.
|
||||||
|
|
||||||
-----
|
-----
|
||||||
# Tips
|
# 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).
|
||||||
|
* [uStreamer Ansible Role](https://github.com/mtlynch/ansible-role-ustreamer): Use [Ansible](https://docs.ansible.com/ansible/latest/index.html) to compile uStreamer and install it as a systemd service automatically.
|
||||||
|
|
||||||
-----
|
-----
|
||||||
# License
|
# License
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
Для сборки вам понадобятся ```make```, ```gcc```, ```libevent``` с поддержкой ```pthreads```, ```libjpeg8```/```libjpeg-turbo```, ```libuuid``` и ```libbsd``` (только для Linux).
|
Для сборки вам понадобятся ```make```, ```gcc```, ```libevent``` с поддержкой ```pthreads```, ```libjpeg8```/```libjpeg-turbo```, ```libuuid``` и ```libbsd``` (только для Linux).
|
||||||
|
|
||||||
* 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 libevent-pthreads-2.1-6-dev libjpeg8-dev uuid-dev libbsd-dev`.
|
* Raspbian: `sudo apt install libevent-dev libjpeg8-dev uuid-dev libbsd-dev`.
|
||||||
* 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`.
|
||||||
|
|
||||||
На Raspberry Pi программу можно собрать с поддержкой OpenMAX IL. Для этого передайте ```make``` параметр ```WITH_OMX=1```. Для включения сборки с поддержкой GPIO установите [wiringPi](http://wiringpi.com) и добавьте параметр ```WITH_GPIO=1```. Если при сборке компилятор ругается на отсутствие функции ```pthread_get_name_np()``` или другой подобной, добавьте параметр ```WITH_PTHREAD_NP=0``` (по умолчанию он включен). При аналогичной ошибке с функцией ```setproctitle()``` добавьте параметр ```WITH_SETPROCTITLE=0```.
|
На Raspberry Pi программу можно собрать с поддержкой OpenMAX IL. Для этого передайте ```make``` параметр ```WITH_OMX=1```. Для включения сборки с поддержкой GPIO установите [wiringPi](http://wiringpi.com) и добавьте параметр ```WITH_GPIO=1```. Если при сборке компилятор ругается на отсутствие функции ```pthread_get_name_np()``` или другой подобной, добавьте параметр ```WITH_PTHREAD_NP=0``` (по умолчанию он включен). При аналогичной ошибке с функцией ```setproctitle()``` добавьте параметр ```WITH_SETPROCTITLE=0```.
|
||||||
@@ -72,8 +72,9 @@ $ ./ustreamer \
|
|||||||
За полным списком опций обращайтесь ко встроенной справке: ```ustreamer --help```.
|
За полным списком опций обращайтесь ко встроенной справке: ```ustreamer --help```.
|
||||||
|
|
||||||
-----
|
-----
|
||||||
# Советы
|
# Смотрите также
|
||||||
* [Запуск с помощью systemd-сервиса](https://github.com/pikvm/ustreamer/issues/16)
|
* [Запуск с помощью systemd-сервиса](https://github.com/pikvm/ustreamer/issues/16).
|
||||||
|
* [uStreamer Ansible Role](https://github.com/mtlynch/ansible-role-ustreamer): Использование [Ansible](https://docs.ansible.com/ansible/latest/index.html) для сборки и установки стримера как systemd-сервиса.
|
||||||
|
|
||||||
-----
|
-----
|
||||||
# Лицензия
|
# Лицензия
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ commands = cppcheck \
|
|||||||
[testenv:flake8]
|
[testenv:flake8]
|
||||||
whitelist_externals = bash
|
whitelist_externals = bash
|
||||||
commands = bash -c 'flake8 --config=linters/flake8.ini tools/*.py'
|
commands = bash -c 'flake8 --config=linters/flake8.ini tools/*.py'
|
||||||
# FIXME: pyflakes from master to support walrus
|
|
||||||
deps =
|
deps =
|
||||||
git+https://github.com/PyCQA/pyflakes@1911c20#egg=pyflakes
|
|
||||||
flake8
|
flake8
|
||||||
flake8-quotes
|
flake8-quotes
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
|
|
||||||
pkgname=ustreamer
|
pkgname=ustreamer
|
||||||
pkgver=1.17
|
pkgver=1.18
|
||||||
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:=1.17
|
PKG_VERSION:=1.18
|
||||||
PKG_RELEASE:=1
|
PKG_RELEASE:=1
|
||||||
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
||||||
|
|
||||||
|
|||||||
@@ -23,5 +23,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef VERSION
|
#ifndef VERSION
|
||||||
# define VERSION "1.17"
|
# define VERSION "1.18"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -305,9 +305,7 @@ static int _omx_setup_input(struct omx_encoder_t *omx, struct device_t *dev) {
|
|||||||
portdef.format.image.nFrameWidth = dev->run->width;
|
portdef.format.image.nFrameWidth = dev->run->width;
|
||||||
portdef.format.image.nFrameHeight = dev->run->height;
|
portdef.format.image.nFrameHeight = dev->run->height;
|
||||||
portdef.format.image.nStride = 0;
|
portdef.format.image.nStride = 0;
|
||||||
# define ALIGN_HEIGHT(_x, _y) (((_x) + ((_y) - 1)) & ~((_y) - 1))
|
portdef.format.image.nSliceHeight = align_size(dev->run->height, 16);
|
||||||
portdef.format.image.nSliceHeight = ALIGN_HEIGHT(dev->run->height, 16);
|
|
||||||
# undef ALIGN_HEIGHT
|
|
||||||
portdef.format.image.bFlagErrorConcealment = OMX_FALSE;
|
portdef.format.image.bFlagErrorConcealment = OMX_FALSE;
|
||||||
portdef.format.image.eCompressionFormat = OMX_IMAGE_CodingUnused;
|
portdef.format.image.eCompressionFormat = OMX_IMAGE_CodingUnused;
|
||||||
portdef.nBufferSize = picture_get_generous_size(dev->run->width, dev->run->height);
|
portdef.nBufferSize = picture_get_generous_size(dev->run->width, dev->run->height);
|
||||||
|
|||||||
@@ -67,15 +67,38 @@ const char *omx_error_to_string(OMX_ERRORTYPE error) {
|
|||||||
CASE_TO_STRING(OMX_ErrorPortUnresponsiveDuringDeallocation);
|
CASE_TO_STRING(OMX_ErrorPortUnresponsiveDuringDeallocation);
|
||||||
CASE_TO_STRING(OMX_ErrorPortUnresponsiveDuringStop);
|
CASE_TO_STRING(OMX_ErrorPortUnresponsiveDuringStop);
|
||||||
CASE_TO_STRING(OMX_ErrorIncorrectStateTransition);
|
CASE_TO_STRING(OMX_ErrorIncorrectStateTransition);
|
||||||
|
CASE_TO_STRING(OMX_ErrorIncorrectStateOperation);
|
||||||
|
CASE_TO_STRING(OMX_ErrorUnsupportedSetting);
|
||||||
|
CASE_TO_STRING(OMX_ErrorUnsupportedIndex);
|
||||||
|
CASE_TO_STRING(OMX_ErrorBadPortIndex);
|
||||||
|
CASE_TO_STRING(OMX_ErrorPortUnpopulated);
|
||||||
|
CASE_TO_STRING(OMX_ErrorComponentSuspended);
|
||||||
|
CASE_TO_STRING(OMX_ErrorDynamicResourcesUnavailable);
|
||||||
|
CASE_TO_STRING(OMX_ErrorMbErrorsInFrame);
|
||||||
|
CASE_TO_STRING(OMX_ErrorFormatNotDetected);
|
||||||
|
CASE_TO_STRING(OMX_ErrorContentPipeOpenFailed);
|
||||||
|
CASE_TO_STRING(OMX_ErrorContentPipeCreationFailed);
|
||||||
|
CASE_TO_STRING(OMX_ErrorSeperateTablesUsed);
|
||||||
|
CASE_TO_STRING(OMX_ErrorTunnelingUnsupported);
|
||||||
|
CASE_TO_STRING(OMX_ErrorKhronosExtensions);
|
||||||
|
CASE_TO_STRING(OMX_ErrorVendorStartUnused);
|
||||||
|
CASE_TO_STRING(OMX_ErrorDiskFull);
|
||||||
|
CASE_TO_STRING(OMX_ErrorMaxFileSize);
|
||||||
|
CASE_TO_STRING(OMX_ErrorDrmUnauthorised);
|
||||||
|
CASE_TO_STRING(OMX_ErrorDrmExpired);
|
||||||
|
CASE_TO_STRING(OMX_ErrorDrmGeneral);
|
||||||
default: return "Unknown OMX error";
|
default: return "Unknown OMX error";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *omx_state_to_string(OMX_STATETYPE state) {
|
const char *omx_state_to_string(OMX_STATETYPE state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
|
CASE_TO_STRING(OMX_StateInvalid);
|
||||||
CASE_TO_STRING(OMX_StateLoaded);
|
CASE_TO_STRING(OMX_StateLoaded);
|
||||||
CASE_TO_STRING(OMX_StateIdle);
|
CASE_TO_STRING(OMX_StateIdle);
|
||||||
CASE_TO_STRING(OMX_StateExecuting);
|
CASE_TO_STRING(OMX_StateExecuting);
|
||||||
|
CASE_TO_STRING(OMX_StatePause);
|
||||||
|
CASE_TO_STRING(OMX_StateWaitForResources);
|
||||||
// cppcheck-suppress constArgument
|
// cppcheck-suppress constArgument
|
||||||
// cppcheck-suppress knownArgument
|
// cppcheck-suppress knownArgument
|
||||||
CASE_ASSERT("Unsupported OMX state", state);
|
CASE_ASSERT("Unsupported OMX state", state);
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ static void _http_queue_send_stream(struct http_server_t *server, bool stream_up
|
|||||||
static bool _expose_new_picture_unsafe(struct http_server_t *server);
|
static bool _expose_new_picture_unsafe(struct http_server_t *server);
|
||||||
static bool _expose_blank_picture(struct http_server_t *server);
|
static bool _expose_blank_picture(struct http_server_t *server);
|
||||||
|
|
||||||
|
static void _format_bufferevent_reason(short what, char *reason);
|
||||||
|
|
||||||
|
|
||||||
struct http_server_t *http_server_init(struct stream_t *stream) {
|
struct http_server_t *http_server_init(struct stream_t *stream) {
|
||||||
struct http_server_runtime_t *run;
|
struct http_server_runtime_t *run;
|
||||||
@@ -533,7 +535,7 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
|||||||
}
|
}
|
||||||
|
|
||||||
evhttp_connection_get_peer(conn, &client_addr, &client_port);
|
evhttp_connection_get_peer(conn, &client_addr, &client_port);
|
||||||
LOG_INFO("HTTP: Registered the new stream client: [%s]:%u, id=%s; clients now: %u",
|
LOG_INFO("HTTP: Registered client: [%s]:%u, id=%s; clients now: %u",
|
||||||
client_addr, client_port, client->id, server->run->stream_clients_count);
|
client_addr, client_port, client->id, server->run->stream_clients_count);
|
||||||
|
|
||||||
buf_event = evhttp_connection_get_bufferevent(conn);
|
buf_event = evhttp_connection_get_bufferevent(conn);
|
||||||
@@ -677,6 +679,9 @@ static void _http_callback_stream_error(UNUSED struct bufferevent *buf_event, UN
|
|||||||
struct evhttp_connection *conn;
|
struct evhttp_connection *conn;
|
||||||
char *client_addr = "???";
|
char *client_addr = "???";
|
||||||
unsigned short client_port = 0;
|
unsigned short client_port = 0;
|
||||||
|
char reason[2048] = {0};
|
||||||
|
|
||||||
|
_format_bufferevent_reason(what, reason);
|
||||||
|
|
||||||
# define RUN(_next) client->server->run->_next
|
# define RUN(_next) client->server->run->_next
|
||||||
|
|
||||||
@@ -697,8 +702,9 @@ static void _http_callback_stream_error(UNUSED struct bufferevent *buf_event, UN
|
|||||||
if (conn) {
|
if (conn) {
|
||||||
evhttp_connection_get_peer(conn, &client_addr, &client_port);
|
evhttp_connection_get_peer(conn, &client_addr, &client_port);
|
||||||
}
|
}
|
||||||
LOG_INFO("HTTP: Disconnected the stream client: [%s]:%u; clients now: %u",
|
|
||||||
client_addr, client_port, RUN(stream_clients_count));
|
LOG_INFO("HTTP: Disconnected client: [%s]:%u, id=%s, %s; clients now: %u",
|
||||||
|
client_addr, client_port, client->id, reason, RUN(stream_clients_count));
|
||||||
if (conn) {
|
if (conn) {
|
||||||
evhttp_connection_free(conn);
|
evhttp_connection_free(conn);
|
||||||
}
|
}
|
||||||
@@ -910,3 +916,33 @@ static bool _expose_blank_picture(struct http_server_t *server) {
|
|||||||
|
|
||||||
# undef EXPOSED
|
# undef EXPOSED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _format_bufferevent_reason(short what, char *reason) {
|
||||||
|
char perror_buf[1024] = {0};
|
||||||
|
char *perror_ptr = errno_to_string(EVUTIL_SOCKET_ERROR(), perror_buf, 1024); // evutil_socket_error_to_string() is not thread-sage
|
||||||
|
bool first = true;
|
||||||
|
|
||||||
|
strcat(reason, perror_ptr);
|
||||||
|
strcat(reason, " (");
|
||||||
|
|
||||||
|
# define FILL_REASON(_bev, _name) { \
|
||||||
|
if (what & _bev) { \
|
||||||
|
if (first) { \
|
||||||
|
first = false; \
|
||||||
|
} else { \
|
||||||
|
strcat(reason, ","); \
|
||||||
|
} \
|
||||||
|
strcat(reason, _name); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
FILL_REASON(BEV_EVENT_READING, "reading");
|
||||||
|
FILL_REASON(BEV_EVENT_WRITING, "writing");
|
||||||
|
FILL_REASON(BEV_EVENT_ERROR, "error");
|
||||||
|
FILL_REASON(BEV_EVENT_TIMEOUT, "timeout");
|
||||||
|
FILL_REASON(BEV_EVENT_EOF, "eof"); // cppcheck-suppress unreadVariable
|
||||||
|
|
||||||
|
# undef FILL_REASON
|
||||||
|
|
||||||
|
strcat(reason, ")");
|
||||||
|
}
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ extern pthread_mutex_t log_mutex;
|
|||||||
|
|
||||||
#define LOG_PERROR(_msg, ...) { \
|
#define LOG_PERROR(_msg, ...) { \
|
||||||
char _perror_buf[1024] = {0}; \
|
char _perror_buf[1024] = {0}; \
|
||||||
char *_perror_ptr = errno_to_string(_perror_buf, 1024); \
|
char *_perror_ptr = errno_to_string(errno, _perror_buf, 1024); \
|
||||||
LOG_ERROR(_msg ": %s", ##__VA_ARGS__, _perror_ptr); \
|
LOG_ERROR(_msg ": %s", ##__VA_ARGS__, _perror_ptr); \
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ extern pthread_mutex_t log_mutex;
|
|||||||
#define LOG_VERBOSE_PERROR(_msg, ...) { \
|
#define LOG_VERBOSE_PERROR(_msg, ...) { \
|
||||||
if (log_level >= LOG_LEVEL_VERBOSE) { \
|
if (log_level >= LOG_LEVEL_VERBOSE) { \
|
||||||
char _perror_buf[1024] = {0}; \
|
char _perror_buf[1024] = {0}; \
|
||||||
char *_perror_ptr = errno_to_string(_perror_buf, 1024); \
|
char *_perror_ptr = errno_to_string(errno, _perror_buf, 1024); \
|
||||||
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); \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
@@ -161,11 +161,11 @@ extern pthread_mutex_t log_mutex;
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
INLINE char *errno_to_string(char *buf, size_t size) {
|
INLINE char *errno_to_string(int error, char *buf, size_t size) {
|
||||||
# if defined(__GLIBC__) && defined(_GNU_SOURCE)
|
# if defined(__GLIBC__) && defined(_GNU_SOURCE)
|
||||||
return strerror_r(errno, buf, size);
|
return strerror_r(error, buf, size);
|
||||||
# else
|
# else
|
||||||
strerror_r(errno, buf, size);
|
strerror_r(error, buf, size);
|
||||||
return buf;
|
return buf;
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ INLINE char *bool_to_string(bool flag) {
|
|||||||
return (flag ? "true" : "false");
|
return (flag ? "true" : "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INLINE size_t align_size(size_t size, size_t to) {
|
||||||
|
return ((size + (to - 1)) & ~(to - 1));
|
||||||
|
}
|
||||||
|
|
||||||
INLINE unsigned min_u(unsigned a, unsigned b) {
|
INLINE unsigned min_u(unsigned a, unsigned b) {
|
||||||
return (a < b ? a : b);
|
return (a < b ? a : b);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user