mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-19 08:16:31 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69cc45a2a0 | ||
|
|
ece96b5834 | ||
|
|
383ed7530b | ||
|
|
7f620c758f | ||
|
|
fb50eea526 | ||
|
|
63f757a9da | ||
|
|
4ec02d46b9 | ||
|
|
527afb66df | ||
|
|
5502758a7e | ||
|
|
faa1776407 | ||
|
|
3d7fb8c8dd | ||
|
|
b5f814d71e | ||
|
|
6eafd4156a | ||
|
|
df1e4eaa06 | ||
|
|
d2bef81b03 | ||
|
|
7b3dffd072 | ||
|
|
1a6e9998fb | ||
|
|
9ab9561803 |
@@ -1,7 +1,7 @@
|
||||
[bumpversion]
|
||||
commit = True
|
||||
tag = True
|
||||
current_version = 5.17
|
||||
current_version = 5.22
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)
|
||||
serialize =
|
||||
{major}.{minor}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[[Русская версия]](README.ru.md)
|
||||
|
||||
µStreamer is a lightweight and very quick server to stream [MJPEG](https://en.wikipedia.org/wiki/Motion_JPEG) video from any V4L2 device to the net. All new browsers have native support of this video format, as well as most video players such as mplayer, VLC etc.
|
||||
µStreamer is a part of the [Pi-KVM](https://github.com/pikvm/pikvm) project designed to stream [VGA](https://www.amazon.com/dp/B0126O0RDC) and [HDMI](https://auvidea.com/b101-hdmi-to-csi-2-bridge-15-pin-fpc/) screencast hardware data with the highest resolution and FPS possible.
|
||||
µStreamer is a part of the [PiKVM](https://github.com/pikvm/pikvm) project designed to stream [VGA](https://www.amazon.com/dp/B0126O0RDC) and [HDMI](https://auvidea.com/b101-hdmi-to-csi-2-bridge-15-pin-fpc/) screencast hardware data with the highest resolution and FPS possible.
|
||||
|
||||
µStreamer is very similar to [mjpg-streamer](https://github.com/jacksonliam/mjpg-streamer) with ```input_uvc.so``` and ```output_http.so``` plugins, however, there are some major differences. The key ones are:
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[[English version]](README.md)
|
||||
|
||||
|
||||
µStreamer - это маленький и очень быстрый сервер, который позволяет организовать трансляцию видео в формате [MJPEG](https://en.wikipedia.org/wiki/Motion_JPEG) с любого устройства V4L2 в сеть. Этот формат нативно поддерживается всеми современными браузерами и большинством приложений для просмотра видео (mplayer, VLC и так далее). µStreamer был разработан в рамках проекта [Pi-KVM](https://github.com/pikvm/pikvm) специально для стриминга с устройств видеозахвата [VGA](https://www.amazon.com/dp/B0126O0RDC) и [HDMI](https://auvidea.com/b101-hdmi-to-csi-2-bridge-15-pin-fpc/) с максимально возможным разрешением и FPS, которые только позволяет железо.
|
||||
µStreamer - это маленький и очень быстрый сервер, который позволяет организовать трансляцию видео в формате [MJPEG](https://en.wikipedia.org/wiki/Motion_JPEG) с любого устройства V4L2 в сеть. Этот формат нативно поддерживается всеми современными браузерами и большинством приложений для просмотра видео (mplayer, VLC и так далее). µStreamer был разработан в рамках проекта [PiKVM](https://github.com/pikvm/pikvm) специально для стриминга с устройств видеозахвата [VGA](https://www.amazon.com/dp/B0126O0RDC) и [HDMI](https://auvidea.com/b101-hdmi-to-csi-2-bridge-15-pin-fpc/) с максимально возможным разрешением и FPS, которые только позволяет железо.
|
||||
|
||||
Функционально µStreamer очень похож на [mjpg-streamer](https://github.com/jacksonliam/mjpg-streamer) при использовании им плагинов ```input_uvc.so``` и ```output_http.so```, однако имеет ряд серьезных отличий. Основные приведены в этой таблице:
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#define US_JLOG_ERROR(x_prefix, x_msg, ...) JANUS_LOG(LOG_ERR, "== %s/%-9s -- " x_msg "\n", US_PLUGIN_NAME, x_prefix, ##__VA_ARGS__)
|
||||
|
||||
#define US_JLOG_PERROR(x_prefix, x_msg, ...) { \
|
||||
char m_perror_buf[1024] = {0}; \
|
||||
char *m_perror_ptr = us_errno_to_string(errno, m_perror_buf, 1023); \
|
||||
JANUS_LOG(LOG_ERR, "[%s/%-9s] " x_msg ": %s\n", US_PLUGIN_NAME, x_prefix, ##__VA_ARGS__, m_perror_ptr); \
|
||||
char *const m_perror_str = us_errno_to_string(errno); \
|
||||
JANUS_LOG(LOG_ERR, "[%s/%-9s] " x_msg ": %s\n", US_PLUGIN_NAME, x_prefix, ##__VA_ARGS__, m_perror_str); \
|
||||
free(m_perror_str); \
|
||||
}
|
||||
|
||||
@@ -40,6 +40,9 @@ us_queue_s *us_queue_init(unsigned capacity) {
|
||||
}
|
||||
|
||||
void us_queue_destroy(us_queue_s *queue) {
|
||||
US_COND_DESTROY(queue->empty_cond);
|
||||
US_COND_DESTROY(queue->full_cond);
|
||||
US_MUTEX_DESTROY(queue->mutex);
|
||||
free(queue->items);
|
||||
free(queue);
|
||||
}
|
||||
@@ -73,7 +76,7 @@ int us_queue_put(us_queue_s *queue, void *item, long double timeout) {
|
||||
++queue->in;
|
||||
queue->in %= queue->capacity;
|
||||
US_MUTEX_UNLOCK(queue->mutex);
|
||||
assert(!pthread_cond_broadcast(&queue->empty_cond));
|
||||
US_COND_BROADCAST(queue->empty_cond);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -85,7 +88,7 @@ int us_queue_get(us_queue_s *queue, void **item, long double timeout) {
|
||||
++queue->out;
|
||||
queue->out %= queue->capacity;
|
||||
US_MUTEX_UNLOCK(queue->mutex);
|
||||
assert(!pthread_cond_broadcast(&queue->full_cond));
|
||||
US_COND_BROADCAST(queue->full_cond);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ FROM archlinux/archlinux:base-devel
|
||||
RUN mkdir -p /etc/pacman.d/hooks \
|
||||
&& ln -s /dev/null /etc/pacman.d/hooks/30-systemd-tmpfiles.hook
|
||||
|
||||
RUN echo "Server = http://mirror.yandex.ru/archlinux/\$repo/os/\$arch" > /etc/pacman.d/mirrorlist
|
||||
RUN echo 'Server = https://mirror.rackspace.com/archlinux/$repo/os/$arch' > /etc/pacman.d/mirrorlist
|
||||
|
||||
RUN pacman -Syu --noconfirm \
|
||||
&& pacman -S --needed --noconfirm \
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Manpage for ustreamer-dump.
|
||||
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
|
||||
.TH USTREAMER-DUMP 1 "version 5.17" "January 2021"
|
||||
.TH USTREAMER-DUMP 1 "version 5.22" "January 2021"
|
||||
|
||||
.SH NAME
|
||||
ustreamer-dump \- Dump uStreamer's memory sink to file
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Manpage for ustreamer.
|
||||
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
|
||||
.TH USTREAMER 1 "version 5.17" "November 2020"
|
||||
.TH USTREAMER 1 "version 5.22" "November 2020"
|
||||
|
||||
.SH NAME
|
||||
ustreamer \- stream MJPEG video from any V4L2 device to the network
|
||||
@@ -10,7 +10,7 @@ ustreamer \- stream MJPEG video from any V4L2 device to the network
|
||||
.RI [OPTIONS]
|
||||
|
||||
.SH DESCRIPTION
|
||||
µStreamer (\fBustreamer\fP) is a lightweight and very quick server to stream MJPEG video from any V4L2 device to the network. All new browsers have native support of this video format, as well as most video players such as mplayer, VLC etc. µStreamer is a part of the Pi-KVM project designed to stream VGA and HDMI screencast hardware data with the highest resolution and FPS possible.
|
||||
µStreamer (\fBustreamer\fP) is a lightweight and very quick server to stream MJPEG video from any V4L2 device to the network. All new browsers have native support of this video format, as well as most video players such as mplayer, VLC etc. µStreamer is a part of the PiKVM project designed to stream VGA and HDMI screencast hardware data with the highest resolution and FPS possible.
|
||||
|
||||
.SH USAGE
|
||||
Without arguments, \fBustreamer\fR will try to open \fB/dev/video0\fR with 640x480 resolution and start streaming on \fBhttp://127\.0\.0\.1:8080\fR\. You can override this behavior using parameters \fB\-\-device\fR, \fB\-\-host\fR and \fB\-\-port\fR\. For example, to stream to the world, run: \fBustreamer --device=/dev/video1 --host=0.0.0.0 --port=80\fR
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
|
||||
pkgname=ustreamer
|
||||
pkgver=5.17
|
||||
pkgver=5.22
|
||||
pkgrel=1
|
||||
pkgdesc="Lightweight and fast MJPEG-HTTP streamer"
|
||||
url="https://github.com/pikvm/ustreamer"
|
||||
@@ -22,8 +22,8 @@ if [ -e /usr/bin/python3 ]; then
|
||||
makedepends+=(python-setuptools)
|
||||
fi
|
||||
if [ -e /usr/include/janus/plugins/plugin.h ];then
|
||||
depends+=(janus-gateway-pikvm alsa-lib opus)
|
||||
makedepends+=(janus-gateway-pikvm alsa-lib opus)
|
||||
depends+=(janus-gateway alsa-lib opus)
|
||||
makedepends+=(janus-gateway alsa-lib opus)
|
||||
_options="$_options WITH_JANUS=1"
|
||||
fi
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=ustreamer
|
||||
PKG_VERSION:=5.17
|
||||
PKG_VERSION:=5.22
|
||||
PKG_RELEASE:=1
|
||||
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import os
|
||||
|
||||
from typing import List
|
||||
|
||||
from setuptools import Extension
|
||||
from setuptools import setup
|
||||
|
||||
|
||||
# =====
|
||||
def _find_sources(suffix: str) -> List[str]:
|
||||
sources: List[str] = []
|
||||
def _find_sources(suffix: str) -> list[str]:
|
||||
sources: list[str] = []
|
||||
for (root_path, _, names) in os.walk("src"):
|
||||
for name in names:
|
||||
if name.endswith(suffix):
|
||||
@@ -19,7 +17,7 @@ def _find_sources(suffix: str) -> List[str]:
|
||||
if __name__ == "__main__":
|
||||
setup(
|
||||
name="ustreamer",
|
||||
version="5.17",
|
||||
version="5.22",
|
||||
description="uStreamer tools",
|
||||
author="Maxim Devaev",
|
||||
author_email="mdevaev@gmail.com",
|
||||
|
||||
@@ -151,12 +151,12 @@ int main(int argc, char *argv[]) {
|
||||
case _O_COUNT: OPT_NUMBER("--count", count, 0, LLONG_MAX, 0);
|
||||
case _O_INTERVAL: OPT_LDOUBLE("--interval", interval, 0, 60);
|
||||
|
||||
case _O_LOG_LEVEL: OPT_NUMBER("--log-level", us_log_level, US_LOG_LEVEL_INFO, US_LOG_LEVEL_DEBUG, 0);
|
||||
case _O_PERF: OPT_SET(us_log_level, US_LOG_LEVEL_PERF);
|
||||
case _O_VERBOSE: OPT_SET(us_log_level, US_LOG_LEVEL_VERBOSE);
|
||||
case _O_DEBUG: OPT_SET(us_log_level, US_LOG_LEVEL_DEBUG);
|
||||
case _O_FORCE_LOG_COLORS: OPT_SET(us_log_colored, true);
|
||||
case _O_NO_LOG_COLORS: OPT_SET(us_log_colored, false);
|
||||
case _O_LOG_LEVEL: OPT_NUMBER("--log-level", us_g_log_level, US_LOG_LEVEL_INFO, US_LOG_LEVEL_DEBUG, 0);
|
||||
case _O_PERF: OPT_SET(us_g_log_level, US_LOG_LEVEL_PERF);
|
||||
case _O_VERBOSE: OPT_SET(us_g_log_level, US_LOG_LEVEL_VERBOSE);
|
||||
case _O_DEBUG: OPT_SET(us_g_log_level, US_LOG_LEVEL_DEBUG);
|
||||
case _O_FORCE_LOG_COLORS: OPT_SET(us_g_log_colored, true);
|
||||
case _O_NO_LOG_COLORS: OPT_SET(us_g_log_colored, false);
|
||||
|
||||
case _O_HELP: _help(stdout); return 0;
|
||||
case _O_VERSION: puts(US_VERSION); return 0;
|
||||
@@ -195,12 +195,9 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
|
||||
static void _signal_handler(int signum) {
|
||||
switch (signum) {
|
||||
case SIGTERM: US_LOG_INFO_NOLOCK("===== Stopping by SIGTERM ====="); break;
|
||||
case SIGINT: US_LOG_INFO_NOLOCK("===== Stopping by SIGINT ====="); break;
|
||||
case SIGPIPE: US_LOG_INFO_NOLOCK("===== Stopping by SIGPIPE ====="); break;
|
||||
default: US_LOG_INFO_NOLOCK("===== Stopping by %d =====", signum); break;
|
||||
}
|
||||
char *const name = us_signum_to_string(signum);
|
||||
US_LOG_INFO_NOLOCK("===== Stopping by %s =====", name);
|
||||
free(name);
|
||||
_g_stop = true;
|
||||
}
|
||||
|
||||
@@ -330,7 +327,7 @@ static void _help(FILE *fp) {
|
||||
SAY(" --log-level <N> ──── Verbosity level of messages from 0 (info) to 3 (debug).");
|
||||
SAY(" Enabling debugging messages can slow down the program.");
|
||||
SAY(" Available levels: 0 (info), 1 (performance), 2 (verbose), 3 (debug).");
|
||||
SAY(" Default: %d.\n", us_log_level);
|
||||
SAY(" Default: %d.\n", us_g_log_level);
|
||||
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(" --debug ──────────── Enable debug messages and lower (same as --log-level=3). Default: disabled.\n");
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#pragma once
|
||||
|
||||
#define US_VERSION_MAJOR 5
|
||||
#define US_VERSION_MINOR 17
|
||||
#define US_VERSION_MINOR 22
|
||||
|
||||
#define US_MAKE_VERSION2(_major, _minor) #_major "." #_minor
|
||||
#define US_MAKE_VERSION1(_major, _minor) US_MAKE_VERSION2(_major, _minor)
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
#include "logging.h"
|
||||
|
||||
|
||||
enum us_log_level_t us_log_level;
|
||||
enum us_log_level_t us_g_log_level;
|
||||
|
||||
bool us_log_colored;
|
||||
bool us_g_log_colored;
|
||||
|
||||
pthread_mutex_t us_log_mutex;
|
||||
pthread_mutex_t us_g_log_mutex;
|
||||
|
||||
@@ -45,23 +45,23 @@ enum us_log_level_t {
|
||||
};
|
||||
|
||||
|
||||
extern enum us_log_level_t us_log_level;
|
||||
extern enum us_log_level_t us_g_log_level;
|
||||
|
||||
extern bool us_log_colored;
|
||||
extern bool us_g_log_colored;
|
||||
|
||||
extern pthread_mutex_t us_log_mutex;
|
||||
extern pthread_mutex_t us_g_log_mutex;
|
||||
|
||||
|
||||
#define US_LOGGING_INIT { \
|
||||
us_log_level = US_LOG_LEVEL_INFO; \
|
||||
us_log_colored = isatty(2); \
|
||||
US_MUTEX_INIT(us_log_mutex); \
|
||||
us_g_log_level = US_LOG_LEVEL_INFO; \
|
||||
us_g_log_colored = isatty(2); \
|
||||
US_MUTEX_INIT(us_g_log_mutex); \
|
||||
}
|
||||
|
||||
#define US_LOGGING_DESTROY US_MUTEX_DESTROY(us_log_mutex)
|
||||
#define US_LOGGING_DESTROY US_MUTEX_DESTROY(us_g_log_mutex)
|
||||
|
||||
#define US_LOGGING_LOCK US_MUTEX_LOCK(us_log_mutex)
|
||||
#define US_LOGGING_UNLOCK US_MUTEX_UNLOCK(us_log_mutex)
|
||||
#define US_LOGGING_LOCK US_MUTEX_LOCK(us_g_log_mutex)
|
||||
#define US_LOGGING_UNLOCK US_MUTEX_UNLOCK(us_g_log_mutex)
|
||||
|
||||
|
||||
#define US_COLOR_GRAY "\x1b[30;1m"
|
||||
@@ -84,7 +84,7 @@ extern pthread_mutex_t us_log_mutex;
|
||||
}
|
||||
|
||||
#define US_SEP_DEBUG(x_ch) { \
|
||||
if (us_log_level >= US_LOG_LEVEL_DEBUG) { \
|
||||
if (us_g_log_level >= US_LOG_LEVEL_DEBUG) { \
|
||||
US_SEP_INFO(x_ch); \
|
||||
} \
|
||||
}
|
||||
@@ -93,7 +93,7 @@ extern pthread_mutex_t us_log_mutex;
|
||||
#define US_LOG_PRINTF_NOLOCK(x_label_color, x_label, x_msg_color, x_msg, ...) { \
|
||||
char m_tname_buf[US_MAX_THREAD_NAME] = {0}; \
|
||||
us_thread_get_name(m_tname_buf); \
|
||||
if (us_log_colored) { \
|
||||
if (us_g_log_colored) { \
|
||||
fprintf(stderr, US_COLOR_GRAY "-- " x_label_color x_label US_COLOR_GRAY \
|
||||
" [%.03Lf %9s]" " -- " US_COLOR_RESET x_msg_color x_msg US_COLOR_RESET, \
|
||||
us_get_now_monotonic(), m_tname_buf, ##__VA_ARGS__); \
|
||||
@@ -116,9 +116,9 @@ extern pthread_mutex_t us_log_mutex;
|
||||
}
|
||||
|
||||
#define US_LOG_PERROR(x_msg, ...) { \
|
||||
char m_perror_buf[1024] = {0}; \
|
||||
char *m_perror_ptr = us_errno_to_string(errno, m_perror_buf, 1024); \
|
||||
US_LOG_ERROR(x_msg ": %s", ##__VA_ARGS__, m_perror_ptr); \
|
||||
char *const m_perror_str = us_errno_to_string(errno); \
|
||||
US_LOG_ERROR(x_msg ": %s", ##__VA_ARGS__, m_perror_str); \
|
||||
free(m_perror_str); \
|
||||
}
|
||||
|
||||
#define US_LOG_INFO(x_msg, ...) { \
|
||||
@@ -130,33 +130,33 @@ extern pthread_mutex_t us_log_mutex;
|
||||
}
|
||||
|
||||
#define US_LOG_PERF(x_msg, ...) { \
|
||||
if (us_log_level >= US_LOG_LEVEL_PERF) { \
|
||||
if (us_g_log_level >= US_LOG_LEVEL_PERF) { \
|
||||
US_LOG_PRINTF(US_COLOR_CYAN, "PERF ", US_COLOR_CYAN, x_msg, ##__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define US_LOG_PERF_FPS(x_msg, ...) { \
|
||||
if (us_log_level >= US_LOG_LEVEL_PERF) { \
|
||||
if (us_g_log_level >= US_LOG_LEVEL_PERF) { \
|
||||
US_LOG_PRINTF(US_COLOR_YELLOW, "PERF ", US_COLOR_YELLOW, x_msg, ##__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define US_LOG_VERBOSE(x_msg, ...) { \
|
||||
if (us_log_level >= US_LOG_LEVEL_VERBOSE) { \
|
||||
if (us_g_log_level >= US_LOG_LEVEL_VERBOSE) { \
|
||||
US_LOG_PRINTF(US_COLOR_BLUE, "VERB ", US_COLOR_BLUE, x_msg, ##__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define US_LOG_VERBOSE_PERROR(x_msg, ...) { \
|
||||
if (us_log_level >= US_LOG_LEVEL_VERBOSE) { \
|
||||
char m_perror_buf[1024] = {0}; \
|
||||
char *m_perror_ptr = us_errno_to_string(errno, m_perror_buf, 1023); \
|
||||
US_LOG_PRINTF(US_COLOR_BLUE, "VERB ", US_COLOR_BLUE, x_msg ": %s", ##__VA_ARGS__, m_perror_ptr); \
|
||||
if (us_g_log_level >= US_LOG_LEVEL_VERBOSE) { \
|
||||
char *m_perror_str = us_errno_to_string(errno); \
|
||||
US_LOG_PRINTF(US_COLOR_BLUE, "VERB ", US_COLOR_BLUE, x_msg ": %s", ##__VA_ARGS__, m_perror_str); \
|
||||
free(m_perror_str); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define US_LOG_DEBUG(x_msg, ...) { \
|
||||
if (us_log_level >= US_LOG_LEVEL_DEBUG) { \
|
||||
if (us_g_log_level >= US_LOG_LEVEL_DEBUG) { \
|
||||
US_LOG_PRINTF(US_COLOR_GRAY, "DEBUG", US_COLOR_GRAY, x_msg, ##__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
@@ -67,7 +67,8 @@
|
||||
#define US_COND_INIT(x_cond) assert(!pthread_cond_init(&(x_cond), NULL))
|
||||
#define US_COND_DESTROY(x_cond) assert(!pthread_cond_destroy(&(x_cond)))
|
||||
#define US_COND_SIGNAL(x_cond) assert(!pthread_cond_signal(&(x_cond)))
|
||||
#define US_COND_WAIT_TRUE(x_var, x_cond, x_mutex) { while(!(x_var)) assert(!pthread_cond_wait(&(x_cond), &(x_mutex))); }
|
||||
#define US_COND_BROADCAST(x_cond) assert(!pthread_cond_broadcast(&(x_cond)))
|
||||
#define US_COND_WAIT_FOR(x_var, x_cond, x_mutex) { while(!(x_var)) assert(!pthread_cond_wait(&(x_cond), &(x_mutex))); }
|
||||
|
||||
|
||||
#ifdef WITH_PTHREAD_NP
|
||||
|
||||
@@ -38,6 +38,12 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/file.h>
|
||||
|
||||
#if defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 32
|
||||
# define HAS_SIGABBREV_NP
|
||||
#else
|
||||
# include <signal.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef NDEBUG
|
||||
# error WTF dude? Asserts are good things!
|
||||
@@ -181,15 +187,34 @@ INLINE int us_flock_timedwait_monotonic(int fd, long double timeout) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
INLINE char *us_errno_to_string(int error, char *buf, size_t size) {
|
||||
assert(buf != NULL);
|
||||
assert(size > 0);
|
||||
INLINE char *us_errno_to_string(int error) {
|
||||
locale_t locale = newlocale(LC_MESSAGES_MASK, "C", NULL);
|
||||
const char *str = "!!! newlocale() error !!!";
|
||||
strncpy(buf, (locale ? strerror_l(error, locale) : str), size - 1);
|
||||
buf[size - 1] = '\0';
|
||||
char *buf;
|
||||
if (locale) {
|
||||
buf = us_strdup(strerror_l(error, locale));
|
||||
freelocale(locale);
|
||||
} else {
|
||||
buf = us_strdup("!!! newlocale() error !!!");
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
INLINE char *us_signum_to_string(int signum) {
|
||||
# ifdef HAS_SIGABBREV_NP
|
||||
const char *const name = sigabbrev_np(signum);
|
||||
# else
|
||||
const char *const name = (
|
||||
signum == SIGTERM ? "TERM" :
|
||||
signum == SIGINT ? "INT" :
|
||||
signum == SIGPIPE ? "PIPE" :
|
||||
NULL
|
||||
);
|
||||
# endif
|
||||
char *buf;
|
||||
if (name != NULL) {
|
||||
US_ASPRINTF(buf, "SIG%s", name);
|
||||
} else {
|
||||
US_ASPRINTF(buf, "SIG[%d]", signum);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "gpio.h"
|
||||
|
||||
|
||||
us_gpio_s us_gpio = {
|
||||
us_gpio_s us_g_gpio = {
|
||||
.path = "/dev/gpiochip0",
|
||||
.consumer_prefix = "ustreamer",
|
||||
|
||||
@@ -51,42 +51,42 @@ static void _gpio_output_destroy(us_gpio_output_s *output);
|
||||
|
||||
|
||||
void us_gpio_init(void) {
|
||||
assert(us_gpio.chip == NULL);
|
||||
assert(us_g_gpio.chip == NULL);
|
||||
if (
|
||||
us_gpio.prog_running.pin >= 0
|
||||
|| us_gpio.stream_online.pin >= 0
|
||||
|| us_gpio.has_http_clients.pin >= 0
|
||||
us_g_gpio.prog_running.pin >= 0
|
||||
|| us_g_gpio.stream_online.pin >= 0
|
||||
|| us_g_gpio.has_http_clients.pin >= 0
|
||||
) {
|
||||
US_MUTEX_INIT(us_gpio.mutex);
|
||||
US_LOG_INFO("GPIO: Using chip device: %s", us_gpio.path);
|
||||
if ((us_gpio.chip = gpiod_chip_open(us_gpio.path)) != NULL) {
|
||||
_gpio_output_init(&us_gpio.prog_running);
|
||||
_gpio_output_init(&us_gpio.stream_online);
|
||||
_gpio_output_init(&us_gpio.has_http_clients);
|
||||
US_MUTEX_INIT(us_g_gpio.mutex);
|
||||
US_LOG_INFO("GPIO: Using chip device: %s", us_g_gpio.path);
|
||||
if ((us_g_gpio.chip = gpiod_chip_open(us_g_gpio.path)) != NULL) {
|
||||
_gpio_output_init(&us_g_gpio.prog_running);
|
||||
_gpio_output_init(&us_g_gpio.stream_online);
|
||||
_gpio_output_init(&us_g_gpio.has_http_clients);
|
||||
} else {
|
||||
US_LOG_PERROR("GPIO: Can't initialize chip device %s", us_gpio.path);
|
||||
US_LOG_PERROR("GPIO: Can't initialize chip device %s", us_g_gpio.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void us_gpio_destroy(void) {
|
||||
_gpio_output_destroy(&us_gpio.prog_running);
|
||||
_gpio_output_destroy(&us_gpio.stream_online);
|
||||
_gpio_output_destroy(&us_gpio.has_http_clients);
|
||||
if (us_gpio.chip != NULL) {
|
||||
gpiod_chip_close(us_gpio.chip);
|
||||
us_gpio.chip = NULL;
|
||||
US_MUTEX_DESTROY(us_gpio.mutex);
|
||||
_gpio_output_destroy(&us_g_gpio.prog_running);
|
||||
_gpio_output_destroy(&us_g_gpio.stream_online);
|
||||
_gpio_output_destroy(&us_g_gpio.has_http_clients);
|
||||
if (us_g_gpio.chip != NULL) {
|
||||
gpiod_chip_close(us_g_gpio.chip);
|
||||
us_g_gpio.chip = NULL;
|
||||
US_MUTEX_DESTROY(us_g_gpio.mutex);
|
||||
}
|
||||
}
|
||||
|
||||
int us_gpio_inner_set(us_gpio_output_s *output, bool state) {
|
||||
int retval = 0;
|
||||
|
||||
assert(us_gpio.chip != NULL);
|
||||
assert(us_g_gpio.chip != NULL);
|
||||
assert(output->line != NULL);
|
||||
assert(output->state != state); // Must be checked in macro for the performance
|
||||
US_MUTEX_LOCK(us_gpio.mutex);
|
||||
US_MUTEX_LOCK(us_g_gpio.mutex);
|
||||
|
||||
if (gpiod_line_set_value(output->line, (int)state) < 0) { \
|
||||
US_LOG_PERROR("GPIO: Can't write value %d to line %s (will be disabled)", state, output->consumer); \
|
||||
@@ -94,18 +94,18 @@ int us_gpio_inner_set(us_gpio_output_s *output, bool state) {
|
||||
retval = -1;
|
||||
}
|
||||
|
||||
US_MUTEX_UNLOCK(us_gpio.mutex);
|
||||
US_MUTEX_UNLOCK(us_g_gpio.mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void _gpio_output_init(us_gpio_output_s *output) {
|
||||
assert(us_gpio.chip != NULL);
|
||||
assert(us_g_gpio.chip != NULL);
|
||||
assert(output->line == NULL);
|
||||
|
||||
US_ASPRINTF(output->consumer, "%s::%s", us_gpio.consumer_prefix, output->role);
|
||||
US_ASPRINTF(output->consumer, "%s::%s", us_g_gpio.consumer_prefix, output->role);
|
||||
|
||||
if (output->pin >= 0) {
|
||||
if ((output->line = gpiod_chip_get_line(us_gpio.chip, output->pin)) != NULL) {
|
||||
if ((output->line = gpiod_chip_get_line(us_g_gpio.chip, output->pin)) != NULL) {
|
||||
if (gpiod_line_request_output(output->line, output->consumer, 0) < 0) {
|
||||
US_LOG_PERROR("GPIO: Can't request pin=%d as %s", output->pin, output->consumer);
|
||||
_gpio_output_destroy(output);
|
||||
|
||||
@@ -56,7 +56,7 @@ typedef struct {
|
||||
} us_gpio_s;
|
||||
|
||||
|
||||
extern us_gpio_s us_gpio;
|
||||
extern us_gpio_s us_g_gpio;
|
||||
|
||||
|
||||
void us_gpio_init(void);
|
||||
@@ -73,15 +73,15 @@ int us_gpio_inner_set(us_gpio_output_s *output, bool state);
|
||||
}
|
||||
|
||||
INLINE void us_gpio_set_prog_running(bool state) {
|
||||
SET_STATE(us_gpio.prog_running, state);
|
||||
SET_STATE(us_g_gpio.prog_running, state);
|
||||
}
|
||||
|
||||
INLINE void us_gpio_set_stream_online(bool state) {
|
||||
SET_STATE(us_gpio.stream_online, state);
|
||||
SET_STATE(us_g_gpio.stream_online, state);
|
||||
}
|
||||
|
||||
INLINE void us_gpio_set_has_http_clients(bool state) {
|
||||
SET_STATE(us_gpio.has_http_clients, state);
|
||||
SET_STATE(us_g_gpio.has_http_clients, state);
|
||||
}
|
||||
|
||||
#undef SET_STATE
|
||||
|
||||
@@ -28,11 +28,11 @@ char *us_bufferevent_format_reason(short what) {
|
||||
US_CALLOC(reason, 2048);
|
||||
|
||||
// evutil_socket_error_to_string() is not thread-safe
|
||||
char perror_buf[1024] = {0};
|
||||
const char *perror_ptr = us_errno_to_string(EVUTIL_SOCKET_ERROR(), perror_buf, 1024);
|
||||
char *const perror_str = us_errno_to_string(EVUTIL_SOCKET_ERROR());
|
||||
bool first = true;
|
||||
|
||||
strcat(reason, perror_ptr);
|
||||
strcat(reason, perror_str);
|
||||
free(perror_str);
|
||||
strcat(reason, " (");
|
||||
|
||||
# define FILL_REASON(x_bev, x_name) { \
|
||||
|
||||
@@ -186,7 +186,7 @@ static void _m2m_encoder_prepare(us_m2m_encoder_s *enc, const us_frame_s *frame)
|
||||
SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_LEVEL, V4L2_MPEG_VIDEO_H264_LEVEL_5_1);
|
||||
}
|
||||
SET_OPTION(V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER, 1);
|
||||
SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 16);
|
||||
SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 20);
|
||||
SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 32);
|
||||
} else if (enc->output_format == V4L2_PIX_FMT_MJPEG) {
|
||||
SET_OPTION(V4L2_CID_MPEG_VIDEO_BITRATE, enc->bitrate);
|
||||
|
||||
@@ -40,12 +40,9 @@
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
us_stream_s *stream;
|
||||
us_server_s *server;
|
||||
} _main_context_s;
|
||||
static us_stream_s *_g_stream = NULL;
|
||||
static us_server_s *_g_server = NULL;
|
||||
|
||||
static _main_context_s *_ctx;
|
||||
|
||||
static void _block_thread_signals(void) {
|
||||
sigset_t mask;
|
||||
@@ -58,25 +55,23 @@ static void _block_thread_signals(void) {
|
||||
static void *_stream_loop_thread(UNUSED void *arg) {
|
||||
US_THREAD_RENAME("stream");
|
||||
_block_thread_signals();
|
||||
us_stream_loop(_ctx->stream);
|
||||
us_stream_loop(_g_stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *_server_loop_thread(UNUSED void *arg) {
|
||||
US_THREAD_RENAME("http");
|
||||
_block_thread_signals();
|
||||
us_server_loop(_ctx->server);
|
||||
us_server_loop(_g_server);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _signal_handler(int signum) {
|
||||
switch (signum) {
|
||||
case SIGTERM: US_LOG_INFO_NOLOCK("===== Stopping by SIGTERM ====="); break;
|
||||
case SIGINT: US_LOG_INFO_NOLOCK("===== Stopping by SIGINT ====="); break;
|
||||
default: US_LOG_INFO_NOLOCK("===== Stopping by %d =====", signum); break;
|
||||
}
|
||||
us_stream_loop_break(_ctx->stream);
|
||||
us_server_loop_break(_ctx->server);
|
||||
char *const name = us_signum_to_string(signum);
|
||||
US_LOG_INFO_NOLOCK("===== Stopping by %s =====", name);
|
||||
free(name);
|
||||
us_stream_loop_break(_g_stream);
|
||||
us_server_loop_break(_g_server);
|
||||
}
|
||||
|
||||
static void _install_signal_handlers(void) {
|
||||
@@ -107,22 +102,17 @@ int main(int argc, char *argv[]) {
|
||||
us_options_s *options = us_options_init(argc, argv);
|
||||
us_device_s *dev = us_device_init();
|
||||
us_encoder_s *enc = us_encoder_init();
|
||||
us_stream_s *stream = us_stream_init(dev, enc);
|
||||
us_server_s *server = us_server_init(stream);
|
||||
_g_stream = us_stream_init(dev, enc);
|
||||
_g_server = us_server_init(_g_stream);
|
||||
|
||||
if ((exit_code = options_parse(options, dev, enc, stream, server)) == 0) {
|
||||
if ((exit_code = options_parse(options, dev, enc, _g_stream, _g_server)) == 0) {
|
||||
# ifdef WITH_GPIO
|
||||
us_gpio_init();
|
||||
# endif
|
||||
|
||||
_install_signal_handlers();
|
||||
|
||||
_main_context_s ctx;
|
||||
ctx.stream = stream;
|
||||
ctx.server = server;
|
||||
_ctx = &ctx;
|
||||
|
||||
if ((exit_code = us_server_listen(server)) == 0) {
|
||||
if ((exit_code = us_server_listen(_g_server)) == 0) {
|
||||
# ifdef WITH_GPIO
|
||||
us_gpio_set_prog_running(true);
|
||||
# endif
|
||||
@@ -141,8 +131,8 @@ int main(int argc, char *argv[]) {
|
||||
# endif
|
||||
}
|
||||
|
||||
us_server_destroy(server);
|
||||
us_stream_destroy(stream);
|
||||
us_server_destroy(_g_server);
|
||||
us_stream_destroy(_g_stream);
|
||||
us_encoder_destroy(enc);
|
||||
us_device_destroy(dev);
|
||||
us_options_destroy(options);
|
||||
|
||||
@@ -437,11 +437,11 @@ int options_parse(us_options_s *options, us_device_s *dev, us_encoder_s *enc, us
|
||||
# undef ADD_SINK
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
case _O_GPIO_DEVICE: OPT_SET(us_gpio.path, optarg);
|
||||
case _O_GPIO_CONSUMER_PREFIX: OPT_SET(us_gpio.consumer_prefix, optarg);
|
||||
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", us_gpio.stream_online.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);
|
||||
case _O_GPIO_DEVICE: OPT_SET(us_g_gpio.path, optarg);
|
||||
case _O_GPIO_CONSUMER_PREFIX: OPT_SET(us_g_gpio.consumer_prefix, optarg);
|
||||
case _O_GPIO_PROG_RUNNING: OPT_NUMBER("--gpio-prog-running", us_g_gpio.prog_running.pin, 0, 256, 0);
|
||||
case _O_GPIO_STREAM_ONLINE: OPT_NUMBER("--gpio-stream-online", us_g_gpio.stream_online.pin, 0, 256, 0);
|
||||
case _O_GPIO_HAS_HTTP_CLIENTS: OPT_NUMBER("--gpio-has-http-clients", us_g_gpio.has_http_clients.pin, 0, 256, 0);
|
||||
# endif
|
||||
|
||||
# ifdef HAS_PDEATHSIG
|
||||
@@ -457,12 +457,12 @@ int options_parse(us_options_s *options, us_device_s *dev, us_encoder_s *enc, us
|
||||
# endif
|
||||
case _O_NOTIFY_PARENT: OPT_SET(server->notify_parent, true);
|
||||
|
||||
case _O_LOG_LEVEL: OPT_NUMBER("--log-level", us_log_level, US_LOG_LEVEL_INFO, US_LOG_LEVEL_DEBUG, 0);
|
||||
case _O_PERF: OPT_SET(us_log_level, US_LOG_LEVEL_PERF);
|
||||
case _O_VERBOSE: OPT_SET(us_log_level, US_LOG_LEVEL_VERBOSE);
|
||||
case _O_DEBUG: OPT_SET(us_log_level, US_LOG_LEVEL_DEBUG);
|
||||
case _O_FORCE_LOG_COLORS: OPT_SET(us_log_colored, true);
|
||||
case _O_NO_LOG_COLORS: OPT_SET(us_log_colored, false);
|
||||
case _O_LOG_LEVEL: OPT_NUMBER("--log-level", us_g_log_level, US_LOG_LEVEL_INFO, US_LOG_LEVEL_DEBUG, 0);
|
||||
case _O_PERF: OPT_SET(us_g_log_level, US_LOG_LEVEL_PERF);
|
||||
case _O_VERBOSE: OPT_SET(us_g_log_level, US_LOG_LEVEL_VERBOSE);
|
||||
case _O_DEBUG: OPT_SET(us_g_log_level, US_LOG_LEVEL_DEBUG);
|
||||
case _O_FORCE_LOG_COLORS: OPT_SET(us_g_log_colored, true);
|
||||
case _O_NO_LOG_COLORS: OPT_SET(us_g_log_colored, false);
|
||||
|
||||
case _O_HELP: _help(stdout, dev, enc, stream, server); return 1;
|
||||
case _O_VERSION: puts(US_VERSION); return 1;
|
||||
@@ -676,8 +676,8 @@ static void _help(FILE *fp, us_device_s *dev, us_encoder_s *enc, us_stream_s *st
|
||||
# ifdef WITH_GPIO
|
||||
SAY("GPIO options:");
|
||||
SAY("═════════════");
|
||||
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", us_gpio.consumer_prefix);
|
||||
SAY(" --gpio-device </dev/path> ───── Path to GPIO character device. Default: %s.\n", us_g_gpio.path);
|
||||
SAY(" --gpio-consumer-prefix <str> ── Consumer prefix for GPIO outputs. Default: %s.\n", us_g_gpio.consumer_prefix);
|
||||
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-has-http-clients <pin> ─ Set 1 while stream has at least one client. Default: disabled.\n");
|
||||
@@ -702,7 +702,7 @@ static void _help(FILE *fp, us_device_s *dev, us_encoder_s *enc, us_stream_s *st
|
||||
SAY(" --log-level <N> ──── Verbosity level of messages from 0 (info) to 3 (debug).");
|
||||
SAY(" Enabling debugging messages can slow down the program.");
|
||||
SAY(" Available levels: 0 (info), 1 (performance), 2 (verbose), 3 (debug).");
|
||||
SAY(" Default: %d.\n", us_log_level);
|
||||
SAY(" Default: %d.\n", us_g_log_level);
|
||||
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(" --debug ──────────── Enable debug messages and lower (same as --log-level=3). Default: disabled.\n");
|
||||
|
||||
@@ -104,7 +104,7 @@ us_worker_s *us_workers_pool_wait(us_workers_pool_s *pool) {
|
||||
us_worker_s *ready_wr = NULL;
|
||||
|
||||
US_MUTEX_LOCK(pool->free_workers_mutex);
|
||||
US_COND_WAIT_TRUE(pool->free_workers, pool->free_workers_cond, pool->free_workers_mutex);
|
||||
US_COND_WAIT_FOR(pool->free_workers, pool->free_workers_cond, pool->free_workers_mutex);
|
||||
US_MUTEX_UNLOCK(pool->free_workers_mutex);
|
||||
|
||||
if (pool->oldest_wr && !atomic_load(&pool->oldest_wr->has_job)) {
|
||||
@@ -185,7 +185,7 @@ static void *_worker_thread(void *v_worker) {
|
||||
US_LOG_DEBUG("Worker %s waiting for a new job ...", wr->name);
|
||||
|
||||
US_MUTEX_LOCK(wr->has_job_mutex);
|
||||
US_COND_WAIT_TRUE(atomic_load(&wr->has_job), wr->has_job_cond, wr->has_job_mutex);
|
||||
US_COND_WAIT_FOR(atomic_load(&wr->has_job), wr->has_job_cond, wr->has_job_mutex);
|
||||
US_MUTEX_UNLOCK(wr->has_job_mutex);
|
||||
|
||||
if (!atomic_load(&wr->pool->stop)) {
|
||||
|
||||
@@ -26,13 +26,11 @@ import os
|
||||
import io
|
||||
import struct
|
||||
|
||||
from typing import Tuple
|
||||
|
||||
import common
|
||||
|
||||
|
||||
# =====
|
||||
def _get_jpeg_size(data: bytes) -> Tuple[int, int]:
|
||||
def _get_jpeg_size(data: bytes) -> tuple[int, int]:
|
||||
# https://sheep.horse/2013/9/finding_the_dimensions_of_a_jpeg_file_in_python.html
|
||||
|
||||
stream = io.BytesIO(data)
|
||||
|
||||
Reference in New Issue
Block a user