mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-28 12:46:32 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de8cb85605 | ||
|
|
000be92a0b | ||
|
|
f2779f7b44 | ||
|
|
dcddfddf56 | ||
|
|
793f24c48e |
@@ -1,7 +1,7 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
current_version = 6.12
|
current_version = 6.13
|
||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)
|
parse = (?P<major>\d+)\.(?P<minor>\d+)
|
||||||
serialize =
|
serialize =
|
||||||
{major}.{minor}
|
{major}.{minor}
|
||||||
|
|||||||
6
Makefile
6
Makefile
@@ -16,6 +16,12 @@ export
|
|||||||
_LINTERS_IMAGE ?= ustreamer-linters
|
_LINTERS_IMAGE ?= ustreamer-linters
|
||||||
|
|
||||||
|
|
||||||
|
# =====
|
||||||
|
ifeq (, $(shell which pkg-config))
|
||||||
|
$(error "No pkg-config found in $(PATH)")
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
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)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
| Compatibility with mjpg-streamer's API | ✔ | :) |
|
| Compatibility with mjpg-streamer's API | ✔ | :) |
|
||||||
|
|
||||||
Footnotes:
|
Footnotes:
|
||||||
* ```1``` Long before µStreamer, I made a [patch](https://github.com/jacksonliam/mjpg-streamer/pull/164) to add DV-timings support to mjpg-streamer and to keep it from hanging up no device disconnection. Alas, the patch is far from perfect and I can't guarantee it will work every time - mjpg-streamer's source code is very complicated and its structure is hard to understand. With this in mind, along with needing multithreading and JPEG hardware acceleration in the future, I decided to make my own stream server from scratch instead of supporting legacy code.
|
* ```1``` Long before µStreamer, I made a [patch](https://github.com/jacksonliam/mjpg-streamer/pull/164) to add DV-timings support to mjpg-streamer and to keep it from hanging up on device disconnection. Alas, the patch is far from perfect and I can't guarantee it will work every time - mjpg-streamer's source code is very complicated and its structure is hard to understand. With this in mind, along with needing multithreading and JPEG hardware acceleration in the future, I decided to make my own stream server from scratch instead of supporting legacy code.
|
||||||
|
|
||||||
* ```2``` This feature allows to cut down outgoing traffic several-fold when streaming HDMI, but it increases CPU usage a little bit. The idea is that HDMI is a fully digital interface and each captured frame can be identical to the previous one byte-wise. There's no need to stream the same image over the net several times a second. With the `--drop-same-frames=20` option enabled, µStreamer will drop all the matching frames (with a limit of 20 in a row). Each new frame is matched with the previous one first by length, then using ```memcmp()```.
|
* ```2``` This feature allows to cut down outgoing traffic several-fold when streaming HDMI, but it increases CPU usage a little bit. The idea is that HDMI is a fully digital interface and each captured frame can be identical to the previous one byte-wise. There's no need to stream the same image over the net several times a second. With the `--drop-same-frames=20` option enabled, µStreamer will drop all the matching frames (with a limit of 20 in a row). Each new frame is matched with the previous one first by length, then using ```memcmp()```.
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ commands = cppcheck \
|
|||||||
--std=c17 \
|
--std=c17 \
|
||||||
--error-exitcode=1 \
|
--error-exitcode=1 \
|
||||||
--quiet \
|
--quiet \
|
||||||
|
--check-level=exhaustive \
|
||||||
--enable=warning,portability,performance,style \
|
--enable=warning,portability,performance,style \
|
||||||
--suppress=assignmentInAssert \
|
--suppress=assignmentInAssert \
|
||||||
--suppress=variableScope \
|
--suppress=variableScope \
|
||||||
|
|||||||
@@ -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 6.12" "January 2021"
|
.TH USTREAMER-DUMP 1 "version 6.13" "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 6.12" "November 2020"
|
.TH USTREAMER 1 "version 6.13" "November 2020"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
ustreamer \- stream MJPEG video from any V4L2 device to the network
|
ustreamer \- stream MJPEG video from any V4L2 device to the network
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
|
|
||||||
pkgname=ustreamer
|
pkgname=ustreamer
|
||||||
pkgver=6.12
|
pkgver=6.13
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Lightweight and fast MJPEG-HTTP streamer"
|
pkgdesc="Lightweight and fast MJPEG-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:=6.12
|
PKG_VERSION:=6.13
|
||||||
PKG_RELEASE:=1
|
PKG_RELEASE:=1
|
||||||
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ def _find_sources(suffix: str) -> list[str]:
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
setup(
|
setup(
|
||||||
name="ustreamer",
|
name="ustreamer",
|
||||||
version="6.12",
|
version="6.13",
|
||||||
description="uStreamer tools",
|
description="uStreamer tools",
|
||||||
author="Maxim Devaev",
|
author="Maxim Devaev",
|
||||||
author_email="mdevaev@gmail.com",
|
author_email="mdevaev@gmail.com",
|
||||||
|
|||||||
@@ -111,9 +111,9 @@ int main(int argc, char *argv[]) {
|
|||||||
US_LOGGING_INIT;
|
US_LOGGING_INIT;
|
||||||
US_THREAD_RENAME("main");
|
US_THREAD_RENAME("main");
|
||||||
|
|
||||||
char *sink_name = NULL;
|
const char *sink_name = NULL;
|
||||||
unsigned sink_timeout = 1;
|
unsigned sink_timeout = 1;
|
||||||
char *output_path = NULL;
|
const char *output_path = NULL;
|
||||||
bool output_json = false;
|
bool output_json = false;
|
||||||
long long count = 0;
|
long long count = 0;
|
||||||
long double interval = 0;
|
long double interval = 0;
|
||||||
|
|||||||
@@ -83,9 +83,9 @@ static const struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static int _capture_wait_buffer(us_capture_s *cap);
|
static int _capture_wait_buffer(us_capture_s *cap);
|
||||||
static int _capture_consume_event(us_capture_s *cap);
|
static int _capture_consume_event(const us_capture_s *cap);
|
||||||
static void _v4l2_buffer_copy(const struct v4l2_buffer *src, struct v4l2_buffer *dest);
|
static void _v4l2_buffer_copy(const struct v4l2_buffer *src, struct v4l2_buffer *dest);
|
||||||
static bool _capture_is_buffer_valid(us_capture_s *cap, const struct v4l2_buffer *buf, const u8 *data);
|
static bool _capture_is_buffer_valid(const us_capture_s *cap, const struct v4l2_buffer *buf, const u8 *data);
|
||||||
static int _capture_open_check_cap(us_capture_s *cap);
|
static int _capture_open_check_cap(us_capture_s *cap);
|
||||||
static int _capture_open_dv_timings(us_capture_s *cap, bool apply);
|
static int _capture_open_dv_timings(us_capture_s *cap, bool apply);
|
||||||
static int _capture_open_format(us_capture_s *cap, bool first);
|
static int _capture_open_format(us_capture_s *cap, bool first);
|
||||||
@@ -98,12 +98,12 @@ static int _capture_open_queue_buffers(us_capture_s *cap);
|
|||||||
static int _capture_open_export_to_dma(us_capture_s *cap);
|
static int _capture_open_export_to_dma(us_capture_s *cap);
|
||||||
static int _capture_apply_resolution(us_capture_s *cap, uint width, uint height, float hz);
|
static int _capture_apply_resolution(us_capture_s *cap, uint width, uint height, float hz);
|
||||||
|
|
||||||
static void _capture_apply_controls(us_capture_s *cap);
|
static void _capture_apply_controls(const us_capture_s *cap);
|
||||||
static int _capture_query_control(
|
static int _capture_query_control(
|
||||||
us_capture_s *cap, struct v4l2_queryctrl *query,
|
const us_capture_s *cap, struct v4l2_queryctrl *query,
|
||||||
const char *name, uint cid, bool quiet);
|
const char *name, uint cid, bool quiet);
|
||||||
static void _capture_set_control(
|
static void _capture_set_control(
|
||||||
us_capture_s *cap, const struct v4l2_queryctrl *query,
|
const us_capture_s *cap, const struct v4l2_queryctrl *query,
|
||||||
const char *name, uint cid, int value, bool quiet);
|
const char *name, uint cid, int value, bool quiet);
|
||||||
|
|
||||||
static const char *_format_to_string_nullable(uint format);
|
static const char *_format_to_string_nullable(uint format);
|
||||||
@@ -421,7 +421,7 @@ int us_capture_hwbuf_grab(us_capture_s *cap, us_capture_hwbuf_s **hw) {
|
|||||||
return buf.index;
|
return buf.index;
|
||||||
}
|
}
|
||||||
|
|
||||||
int us_capture_hwbuf_release(us_capture_s *cap, us_capture_hwbuf_s *hw) {
|
int us_capture_hwbuf_release(const us_capture_s *cap, us_capture_hwbuf_s *hw) {
|
||||||
assert(atomic_load(&hw->refs) == 0);
|
assert(atomic_load(&hw->refs) == 0);
|
||||||
const uint index = hw->buf.index;
|
const uint index = hw->buf.index;
|
||||||
_LOG_DEBUG("Releasing HW buffer=%u ...", index);
|
_LOG_DEBUG("Releasing HW buffer=%u ...", index);
|
||||||
@@ -486,7 +486,7 @@ int _capture_wait_buffer(us_capture_s *cap) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _capture_consume_event(us_capture_s *cap) {
|
static int _capture_consume_event(const us_capture_s *cap) {
|
||||||
struct v4l2_event event;
|
struct v4l2_event event;
|
||||||
if (us_xioctl(cap->run->fd, VIDIOC_DQEVENT, &event) < 0) {
|
if (us_xioctl(cap->run->fd, VIDIOC_DQEVENT, &event) < 0) {
|
||||||
_LOG_PERROR("Can't consume V4L2 event");
|
_LOG_PERROR("Can't consume V4L2 event");
|
||||||
@@ -513,7 +513,7 @@ static void _v4l2_buffer_copy(const struct v4l2_buffer *src, struct v4l2_buffer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _capture_is_buffer_valid(us_capture_s *cap, const struct v4l2_buffer *buf, const u8 *data) {
|
bool _capture_is_buffer_valid(const us_capture_s *cap, const struct v4l2_buffer *buf, const u8 *data) {
|
||||||
// Workaround for broken, corrupted frames:
|
// Workaround for broken, corrupted frames:
|
||||||
// Under low light conditions corrupted frames may get captured.
|
// Under low light conditions corrupted frames may get captured.
|
||||||
// The good thing is such frames are quite small compared to the regular frames.
|
// The good thing is such frames are quite small compared to the regular frames.
|
||||||
@@ -737,7 +737,7 @@ static int _capture_open_format(us_capture_s *cap, bool first) {
|
|||||||
_format_to_string_supported(cap->format),
|
_format_to_string_supported(cap->format),
|
||||||
_format_to_string_supported(FMT(pixelformat)));
|
_format_to_string_supported(FMT(pixelformat)));
|
||||||
|
|
||||||
char *format_str;
|
const char *format_str;
|
||||||
if ((format_str = (char*)_format_to_string_nullable(FMT(pixelformat))) != NULL) {
|
if ((format_str = (char*)_format_to_string_nullable(FMT(pixelformat))) != NULL) {
|
||||||
_LOG_INFO("Falling back to format=%s", format_str);
|
_LOG_INFO("Falling back to format=%s", format_str);
|
||||||
} else {
|
} else {
|
||||||
@@ -1037,7 +1037,7 @@ static int _capture_apply_resolution(us_capture_s *cap, uint width, uint height,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _capture_apply_controls(us_capture_s *cap) {
|
static void _capture_apply_controls(const us_capture_s *cap) {
|
||||||
# define SET_CID_VALUE(x_cid, x_field, x_value, x_quiet) { \
|
# define SET_CID_VALUE(x_cid, x_field, x_value, x_quiet) { \
|
||||||
struct v4l2_queryctrl m_query; \
|
struct v4l2_queryctrl m_query; \
|
||||||
if (_capture_query_control(cap, &m_query, #x_field, x_cid, x_quiet) == 0) { \
|
if (_capture_query_control(cap, &m_query, #x_field, x_cid, x_quiet) == 0) { \
|
||||||
@@ -1094,7 +1094,7 @@ static void _capture_apply_controls(us_capture_s *cap) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int _capture_query_control(
|
static int _capture_query_control(
|
||||||
us_capture_s *cap, struct v4l2_queryctrl *query,
|
const us_capture_s *cap, struct v4l2_queryctrl *query,
|
||||||
const char *name, uint cid, bool quiet) {
|
const char *name, uint cid, bool quiet) {
|
||||||
|
|
||||||
// cppcheck-suppress redundantPointerOp
|
// cppcheck-suppress redundantPointerOp
|
||||||
@@ -1111,7 +1111,7 @@ static int _capture_query_control(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void _capture_set_control(
|
static void _capture_set_control(
|
||||||
us_capture_s *cap, const struct v4l2_queryctrl *query,
|
const us_capture_s *cap, const struct v4l2_queryctrl *query,
|
||||||
const char *name, uint cid, int value, bool quiet) {
|
const char *name, uint cid, int value, bool quiet) {
|
||||||
|
|
||||||
if (value < query->minimum || value > query->maximum || value % query->step != 0) {
|
if (value < query->minimum || value > query->maximum || value % query->step != 0) {
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ int us_capture_open(us_capture_s *cap);
|
|||||||
void us_capture_close(us_capture_s *cap);
|
void us_capture_close(us_capture_s *cap);
|
||||||
|
|
||||||
int us_capture_hwbuf_grab(us_capture_s *cap, us_capture_hwbuf_s **hw);
|
int us_capture_hwbuf_grab(us_capture_s *cap, us_capture_hwbuf_s **hw);
|
||||||
int us_capture_hwbuf_release(us_capture_s *cap, us_capture_hwbuf_s *hw);
|
int us_capture_hwbuf_release(const us_capture_s *cap, us_capture_hwbuf_s *hw);
|
||||||
|
|
||||||
void us_capture_hwbuf_incref(us_capture_hwbuf_s *hw);
|
void us_capture_hwbuf_incref(us_capture_hwbuf_s *hw);
|
||||||
void us_capture_hwbuf_decref(us_capture_hwbuf_s *hw);
|
void us_capture_hwbuf_decref(us_capture_hwbuf_s *hw);
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#define US_VERSION_MAJOR 6
|
#define US_VERSION_MAJOR 6
|
||||||
#define US_VERSION_MINOR 12
|
#define US_VERSION_MINOR 13
|
||||||
|
|
||||||
#define US_MAKE_VERSION2(_major, _minor) #_major "." #_minor
|
#define US_MAKE_VERSION2(_major, _minor) #_major "." #_minor
|
||||||
#define US_MAKE_VERSION1(_major, _minor) US_MAKE_VERSION2(_major, _minor)
|
#define US_MAKE_VERSION1(_major, _minor) US_MAKE_VERSION2(_major, _minor)
|
||||||
|
|||||||
@@ -614,7 +614,7 @@ static int _drm_find_sink(us_drm_s *drm, uint width, uint height, float hz) {
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
drmModeModeInfo *best;
|
const drmModeModeInfo *best;
|
||||||
if ((best = _find_best_mode(conn, width, height, hz)) == NULL) {
|
if ((best = _find_best_mode(conn, width, height, hz)) == NULL) {
|
||||||
_LOG_ERROR("Can't find any appropriate display modes");
|
_LOG_ERROR("Can't find any appropriate display modes");
|
||||||
drmModeFreeConnector(conn);
|
drmModeFreeConnector(conn);
|
||||||
|
|||||||
@@ -113,7 +113,9 @@ INLINE void us_thread_get_name(char *name) { // Always required for logging
|
|||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
const pid_t tid = syscall(SYS_gettid);
|
const pid_t tid = syscall(SYS_gettid);
|
||||||
#elif defined(__FreeBSD__)
|
#elif defined(__FreeBSD__)
|
||||||
const pid_t tid = syscall(SYS_thr_self);
|
long id;
|
||||||
|
assert(!syscall(SYS_thr_self, &id));
|
||||||
|
const pid_t tid = id;
|
||||||
#elif defined(__OpenBSD__)
|
#elif defined(__OpenBSD__)
|
||||||
const pid_t tid = syscall(SYS_getthrid);
|
const pid_t tid = syscall(SYS_getthrid);
|
||||||
#elif defined(__NetBSD__)
|
#elif defined(__NetBSD__)
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ char *us_simplify_request_path(const char *str) {
|
|||||||
char pre1; // The one before
|
char pre1; // The one before
|
||||||
char pre2; // The one before that
|
char pre2; // The one before that
|
||||||
char *simplified;
|
char *simplified;
|
||||||
char *start;
|
const char *start;
|
||||||
char *out;
|
char *out;
|
||||||
char *slash;
|
char *slash;
|
||||||
|
|
||||||
|
|||||||
@@ -353,7 +353,7 @@ int options_parse(us_options_s *options, us_capture_s *cap, us_encoder_s *enc, u
|
|||||||
}
|
}
|
||||||
|
|
||||||
# define ADD_SINK(x_prefix) \
|
# define ADD_SINK(x_prefix) \
|
||||||
char *x_prefix##_name = NULL; \
|
const char *x_prefix##_name = NULL; \
|
||||||
mode_t x_prefix##_mode = 0660; \
|
mode_t x_prefix##_mode = 0660; \
|
||||||
bool x_prefix##_rm = false; \
|
bool x_prefix##_rm = false; \
|
||||||
unsigned x_prefix##_client_ttl = 10; \
|
unsigned x_prefix##_client_ttl = 10; \
|
||||||
@@ -364,7 +364,7 @@ int options_parse(us_options_s *options, us_capture_s *cap, us_encoder_s *enc, u
|
|||||||
# undef ADD_SINK
|
# undef ADD_SINK
|
||||||
|
|
||||||
# ifdef WITH_SETPROCTITLE
|
# ifdef WITH_SETPROCTITLE
|
||||||
char *process_name_prefix = NULL;
|
const char *process_name_prefix = NULL;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
char short_opts[128];
|
char short_opts[128];
|
||||||
|
|||||||
Reference in New Issue
Block a user