mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-28 04:36:33 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c848756d53 | ||
|
|
2a8aaabe48 | ||
|
|
239db92a85 | ||
|
|
740e09c70d | ||
|
|
e030479aae | ||
|
|
4db730abd9 | ||
|
|
79020143c7 | ||
|
|
1f96925181 | ||
|
|
74dc1dc146 | ||
|
|
6f8e8205b3 | ||
|
|
5f932d862b | ||
|
|
590a73f9ec | ||
|
|
79bbafdc98 | ||
|
|
fcecc12229 | ||
|
|
f79a663839 | ||
|
|
3e228c1fb8 | ||
|
|
53ec87b416 | ||
|
|
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.18
|
||||||
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 (__not_found__,$(shell which pkg-config 2>/dev/null || echo "__not_found__"))
|
||||||
|
$(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()```.
|
||||||
|
|
||||||
|
|||||||
@@ -406,17 +406,13 @@ static void _plugin_hangup_media(janus_plugin_session *session) { _set_transmit(
|
|||||||
static struct janus_plugin_result *_plugin_handle_message(
|
static struct janus_plugin_result *_plugin_handle_message(
|
||||||
janus_plugin_session *session, char *transaction, json_t *msg, json_t *jsep) {
|
janus_plugin_session *session, char *transaction, json_t *msg, json_t *jsep) {
|
||||||
|
|
||||||
assert(transaction != NULL);
|
janus_plugin_result_type result_type = JANUS_PLUGIN_OK;
|
||||||
|
char *result_msg = NULL;
|
||||||
# define FREE_MSG_JSEP { \
|
|
||||||
US_DELETE(msg, json_decref); \
|
|
||||||
US_DELETE(jsep, json_decref); \
|
|
||||||
}
|
|
||||||
|
|
||||||
if (session == NULL || msg == NULL) {
|
if (session == NULL || msg == NULL) {
|
||||||
free(transaction);
|
result_type = JANUS_PLUGIN_ERROR;
|
||||||
FREE_MSG_JSEP;
|
result_msg = (msg ? "No session" : "No message");
|
||||||
return janus_plugin_result_new(JANUS_PLUGIN_ERROR, (msg ? "No session" : "No message"), NULL);
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
# define PUSH_ERROR(x_error, x_reason) { \
|
# define PUSH_ERROR(x_error, x_reason) { \
|
||||||
@@ -425,20 +421,20 @@ static struct janus_plugin_result *_plugin_handle_message(
|
|||||||
json_object_set_new(m_event, "ustreamer", json_string("event")); \
|
json_object_set_new(m_event, "ustreamer", json_string("event")); \
|
||||||
json_object_set_new(m_event, "error_code", json_integer(x_error)); \
|
json_object_set_new(m_event, "error_code", json_integer(x_error)); \
|
||||||
json_object_set_new(m_event, "error", json_string(x_reason)); \
|
json_object_set_new(m_event, "error", json_string(x_reason)); \
|
||||||
_g_gw->push_event(session, create(), transaction, m_event, NULL); \
|
_g_gw->push_event(session, create(), NULL, m_event, NULL); \
|
||||||
json_decref(m_event); \
|
json_decref(m_event); \
|
||||||
}
|
}
|
||||||
|
|
||||||
json_t *const request = json_object_get(msg, "request");
|
json_t *const request = json_object_get(msg, "request");
|
||||||
if (request == NULL) {
|
if (request == NULL) {
|
||||||
PUSH_ERROR(400, "Request missing");
|
PUSH_ERROR(400, "Request missing");
|
||||||
goto ok_wait;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *const request_str = json_string_value(request);
|
const char *const request_str = json_string_value(request);
|
||||||
if (request_str == NULL) {
|
if (request_str == NULL) {
|
||||||
PUSH_ERROR(400, "Request not a string");
|
PUSH_ERROR(400, "Request not a string");
|
||||||
goto ok_wait;
|
goto done;
|
||||||
}
|
}
|
||||||
// US_JLOG_INFO("main", "Message: %s", request_str);
|
// US_JLOG_INFO("main", "Message: %s", request_str);
|
||||||
|
|
||||||
@@ -448,10 +444,10 @@ static struct janus_plugin_result *_plugin_handle_message(
|
|||||||
json_t *const m_result = json_object(); \
|
json_t *const m_result = json_object(); \
|
||||||
json_object_set_new(m_result, "status", json_string(x_status)); \
|
json_object_set_new(m_result, "status", json_string(x_status)); \
|
||||||
if (x_payload != NULL) { \
|
if (x_payload != NULL) { \
|
||||||
json_object_set_new(m_result, x_status, x_payload); \
|
json_object_set(m_result, x_status, x_payload); \
|
||||||
} \
|
} \
|
||||||
json_object_set_new(m_event, "result", m_result); \
|
json_object_set_new(m_event, "result", m_result); \
|
||||||
_g_gw->push_event(session, create(), transaction, m_event, x_jsep); \
|
_g_gw->push_event(session, create(), NULL, m_event, x_jsep); \
|
||||||
json_decref(m_event); \
|
json_decref(m_event); \
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -542,13 +538,17 @@ static struct janus_plugin_result *_plugin_handle_message(
|
|||||||
PUSH_ERROR(405, "Not implemented");
|
PUSH_ERROR(405, "Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
ok_wait:
|
done:
|
||||||
FREE_MSG_JSEP;
|
US_DELETE(transaction, free);
|
||||||
return janus_plugin_result_new(JANUS_PLUGIN_OK_WAIT, NULL, NULL);
|
US_DELETE(msg, json_decref);
|
||||||
|
US_DELETE(jsep, json_decref);
|
||||||
|
|
||||||
|
return janus_plugin_result_new(
|
||||||
|
result_type, result_msg,
|
||||||
|
(result_type == JANUS_PLUGIN_OK ? json_pack("{sb}", "ok", 1) : NULL));
|
||||||
|
|
||||||
# undef PUSH_STATUS
|
# undef PUSH_STATUS
|
||||||
# undef PUSH_ERROR
|
# undef PUSH_ERROR
|
||||||
# undef FREE_MSG_JSEP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _plugin_incoming_rtcp(janus_plugin_session *handle, janus_plugin_rtcp *packet) {
|
static void _plugin_incoming_rtcp(janus_plugin_session *handle, janus_plugin_rtcp *packet) {
|
||||||
|
|||||||
@@ -50,13 +50,13 @@ char *us_rtpa_make_sdp(us_rtpa_s *rtpa) {
|
|||||||
"m=audio 1 RTP/SAVPF %u" RN
|
"m=audio 1 RTP/SAVPF %u" RN
|
||||||
"c=IN IP4 0.0.0.0" RN
|
"c=IN IP4 0.0.0.0" RN
|
||||||
"a=rtpmap:%u OPUS/48000/2" RN
|
"a=rtpmap:%u OPUS/48000/2" RN
|
||||||
// "a=fmtp:%u useinbandfec=1" RN
|
"a=fmtp:%u sprop-stereo=1" RN // useinbandfec=1
|
||||||
"a=rtcp-fb:%u nack" RN
|
"a=rtcp-fb:%u nack" RN
|
||||||
"a=rtcp-fb:%u nack pli" RN
|
"a=rtcp-fb:%u nack pli" RN
|
||||||
"a=rtcp-fb:%u goog-remb" RN
|
"a=rtcp-fb:%u goog-remb" RN
|
||||||
"a=ssrc:%" PRIu32 " cname:ustreamer" RN
|
"a=ssrc:%" PRIu32 " cname:ustreamer" RN
|
||||||
"a=sendonly" RN,
|
"a=sendonly" RN,
|
||||||
pl, pl, pl, pl, pl, // pl,
|
pl, pl, pl, pl, pl, pl,
|
||||||
rtpa->rtp->ssrc
|
rtpa->rtp->ssrc
|
||||||
);
|
);
|
||||||
return sdp;
|
return sdp;
|
||||||
|
|||||||
@@ -13,8 +13,10 @@ 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=assertWithSideEffect \
|
||||||
--suppress=variableScope \
|
--suppress=variableScope \
|
||||||
--inline-suppr \
|
--inline-suppr \
|
||||||
--library=python \
|
--library=python \
|
||||||
|
|||||||
@@ -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.18" "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.18" "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
|
||||||
@@ -68,6 +68,9 @@ Desired FPS. Default: maximum possible.
|
|||||||
.BR \-z\ \fIN ", " \-\-min\-frame\-size\ \fIN
|
.BR \-z\ \fIN ", " \-\-min\-frame\-size\ \fIN
|
||||||
Drop frames smaller then this limit. Useful if the device produces small\-sized garbage frames. Default: 128 bytes.
|
Drop frames smaller then this limit. Useful if the device produces small\-sized garbage frames. Default: 128 bytes.
|
||||||
.TP
|
.TP
|
||||||
|
.BR \-T ", " \-\-allow\-truncated\-frames
|
||||||
|
Allows to handle truncated frames. Useful if the device produces incorrect but still acceptable frames. Default: disabled.
|
||||||
|
.TP
|
||||||
.BR \-n ", " \-\-persistent
|
.BR \-n ", " \-\-persistent
|
||||||
Suppress repetitive signal source errors. Default: disabled.
|
Suppress repetitive signal source errors. Default: disabled.
|
||||||
.TP
|
.TP
|
||||||
|
|||||||
@@ -3,14 +3,14 @@
|
|||||||
|
|
||||||
|
|
||||||
pkgname=ustreamer
|
pkgname=ustreamer
|
||||||
pkgver=6.12
|
pkgver=6.18
|
||||||
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"
|
||||||
license=(GPL)
|
license=(GPL)
|
||||||
arch=(i686 x86_64 armv6h armv7h aarch64)
|
arch=(i686 x86_64 armv6h armv7h aarch64)
|
||||||
depends=(libjpeg libevent libbsd libgpiod systemd)
|
depends=(libjpeg libevent libbsd libgpiod systemd)
|
||||||
makedepends=(gcc make systemd)
|
makedepends=(gcc make pkgconf systemd)
|
||||||
source=(${pkgname}::"git+https://github.com/pikvm/ustreamer#commit=v${pkgver}")
|
source=(${pkgname}::"git+https://github.com/pikvm/ustreamer#commit=v${pkgver}")
|
||||||
md5sums=(SKIP)
|
md5sums=(SKIP)
|
||||||
|
|
||||||
|
|||||||
@@ -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.18
|
||||||
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.18",
|
||||||
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.
|
||||||
@@ -544,8 +544,11 @@ bool _capture_is_buffer_valid(us_capture_s *cap, const struct v4l2_buffer *buf,
|
|||||||
const u8 *const eoi_ptr = end_ptr - 2;
|
const u8 *const eoi_ptr = end_ptr - 2;
|
||||||
const u16 eoi_marker = (((u16)(eoi_ptr[0]) << 8) | eoi_ptr[1]);
|
const u16 eoi_marker = (((u16)(eoi_ptr[0]) << 8) | eoi_ptr[1]);
|
||||||
if (eoi_marker != 0xFFD9 && eoi_marker != 0xD900 && eoi_marker != 0x0000) {
|
if (eoi_marker != 0xFFD9 && eoi_marker != 0xD900 && eoi_marker != 0x0000) {
|
||||||
_LOG_DEBUG("Discarding truncated JPEG frame: eoi_marker=0x%04x, bytesused=%u", eoi_marker, buf->bytesused);
|
if (!cap->allow_truncated_frames) {
|
||||||
return false;
|
_LOG_DEBUG("Discarding truncated JPEG frame: eoi_marker=0x%04x, bytesused=%u", eoi_marker, buf->bytesused);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_LOG_DEBUG("Got truncated JPEG frame: eoi_marker=0x%04x, bytesused=%u", eoi_marker, buf->bytesused);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -737,7 +740,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 +1040,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 +1097,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 +1114,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) {
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ typedef struct {
|
|||||||
bool dma_required;
|
bool dma_required;
|
||||||
uint desired_fps;
|
uint desired_fps;
|
||||||
uz min_frame_size;
|
uz min_frame_size;
|
||||||
|
bool allow_truncated_frames;
|
||||||
bool persistent;
|
bool persistent;
|
||||||
uint timeout;
|
uint timeout;
|
||||||
us_controls_s ctl;
|
us_controls_s ctl;
|
||||||
@@ -133,7 +134,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 18
|
||||||
|
|
||||||
#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;
|
||||||
|
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ int main(int argc, char *argv[]) {
|
|||||||
_g_server = us_server_init(_g_stream);
|
_g_server = us_server_init(_g_stream);
|
||||||
|
|
||||||
if ((exit_code = options_parse(options, cap, enc, _g_stream, _g_server)) == 0) {
|
if ((exit_code = options_parse(options, cap, enc, _g_stream, _g_server)) == 0) {
|
||||||
|
us_stream_update_blank(_g_stream, cap);
|
||||||
# ifdef WITH_GPIO
|
# ifdef WITH_GPIO
|
||||||
us_gpio_init();
|
us_gpio_init();
|
||||||
# endif
|
# endif
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ enum _US_OPT_VALUES {
|
|||||||
_O_IO_METHOD = 'I',
|
_O_IO_METHOD = 'I',
|
||||||
_O_DESIRED_FPS = 'f',
|
_O_DESIRED_FPS = 'f',
|
||||||
_O_MIN_FRAME_SIZE = 'z',
|
_O_MIN_FRAME_SIZE = 'z',
|
||||||
|
_O_ALLOW_TRUNCATED_FRAMES = 'T',
|
||||||
_O_PERSISTENT = 'n',
|
_O_PERSISTENT = 'n',
|
||||||
_O_DV_TIMINGS = 't',
|
_O_DV_TIMINGS = 't',
|
||||||
_O_BUFFERS = 'b',
|
_O_BUFFERS = 'b',
|
||||||
@@ -142,6 +143,7 @@ static const struct option _LONG_OPTS[] = {
|
|||||||
{"io-method", required_argument, NULL, _O_IO_METHOD},
|
{"io-method", required_argument, NULL, _O_IO_METHOD},
|
||||||
{"desired-fps", required_argument, NULL, _O_DESIRED_FPS},
|
{"desired-fps", required_argument, NULL, _O_DESIRED_FPS},
|
||||||
{"min-frame-size", required_argument, NULL, _O_MIN_FRAME_SIZE},
|
{"min-frame-size", required_argument, NULL, _O_MIN_FRAME_SIZE},
|
||||||
|
{"allow-truncated-frames", no_argument, NULL, _O_ALLOW_TRUNCATED_FRAMES},
|
||||||
{"persistent", no_argument, NULL, _O_PERSISTENT},
|
{"persistent", no_argument, NULL, _O_PERSISTENT},
|
||||||
{"dv-timings", no_argument, NULL, _O_DV_TIMINGS},
|
{"dv-timings", no_argument, NULL, _O_DV_TIMINGS},
|
||||||
{"buffers", required_argument, NULL, _O_BUFFERS},
|
{"buffers", required_argument, NULL, _O_BUFFERS},
|
||||||
@@ -353,7 +355,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 +366,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];
|
||||||
@@ -384,6 +386,7 @@ int options_parse(us_options_s *options, us_capture_s *cap, us_encoder_s *enc, u
|
|||||||
case _O_IO_METHOD: OPT_PARSE_ENUM("IO method", cap->io_method, us_capture_parse_io_method, US_IO_METHODS_STR);
|
case _O_IO_METHOD: OPT_PARSE_ENUM("IO method", cap->io_method, us_capture_parse_io_method, US_IO_METHODS_STR);
|
||||||
case _O_DESIRED_FPS: OPT_NUMBER("--desired-fps", cap->desired_fps, 0, US_VIDEO_MAX_FPS, 0);
|
case _O_DESIRED_FPS: OPT_NUMBER("--desired-fps", cap->desired_fps, 0, US_VIDEO_MAX_FPS, 0);
|
||||||
case _O_MIN_FRAME_SIZE: OPT_NUMBER("--min-frame-size", cap->min_frame_size, 1, 8192, 0);
|
case _O_MIN_FRAME_SIZE: OPT_NUMBER("--min-frame-size", cap->min_frame_size, 1, 8192, 0);
|
||||||
|
case _O_ALLOW_TRUNCATED_FRAMES: OPT_SET(cap->allow_truncated_frames, true);
|
||||||
case _O_PERSISTENT: OPT_SET(cap->persistent, true);
|
case _O_PERSISTENT: OPT_SET(cap->persistent, true);
|
||||||
case _O_DV_TIMINGS: OPT_SET(cap->dv_timings, true);
|
case _O_DV_TIMINGS: OPT_SET(cap->dv_timings, true);
|
||||||
case _O_BUFFERS: OPT_NUMBER("--buffers", cap->n_bufs, 1, 32, 0);
|
case _O_BUFFERS: OPT_NUMBER("--buffers", cap->n_bufs, 1, 32, 0);
|
||||||
@@ -632,6 +635,8 @@ static void _help(FILE *fp, const us_capture_s *cap, const us_encoder_s *enc, co
|
|||||||
SAY(" -f|--desired-fps <N> ──────────────── Desired FPS. Default: maximum possible.\n");
|
SAY(" -f|--desired-fps <N> ──────────────── Desired FPS. Default: maximum possible.\n");
|
||||||
SAY(" -z|--min-frame-size <N> ───────────── Drop frames smaller then this limit. Useful if the device");
|
SAY(" -z|--min-frame-size <N> ───────────── Drop frames smaller then this limit. Useful if the device");
|
||||||
SAY(" produces small-sized garbage frames. Default: %zu bytes.\n", cap->min_frame_size);
|
SAY(" produces small-sized garbage frames. Default: %zu bytes.\n", cap->min_frame_size);
|
||||||
|
SAY(" -T|--allow-truncated-frames ───────── Allows to handle truncated frames. Useful if the device");
|
||||||
|
SAY(" produces incorrect but still acceptable frames. Default: disabled.\n");
|
||||||
SAY(" -n|--persistent ───────────────────── Don't re-initialize device on timeout. Default: disabled.\n");
|
SAY(" -n|--persistent ───────────────────── Don't re-initialize device on timeout. Default: disabled.\n");
|
||||||
SAY(" -t|--dv-timings ───────────────────── Enable DV-timings querying and events processing");
|
SAY(" -t|--dv-timings ───────────────────── Enable DV-timings querying and events processing");
|
||||||
SAY(" to automatic resolution change. Default: disabled.\n");
|
SAY(" to automatic resolution change. Default: disabled.\n");
|
||||||
|
|||||||
@@ -129,6 +129,10 @@ us_stream_s *us_stream_init(us_capture_s *cap, us_encoder_s *enc) {
|
|||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void us_stream_update_blank(us_stream_s *stream, const us_capture_s *cap) {
|
||||||
|
us_blank_draw(stream->run->blank, "< NO SIGNAL >", cap->width, cap->height);
|
||||||
|
}
|
||||||
|
|
||||||
void us_stream_destroy(us_stream_s *stream) {
|
void us_stream_destroy(us_stream_s *stream) {
|
||||||
us_fpsi_destroy(stream->run->http->captured_fpsi);
|
us_fpsi_destroy(stream->run->http->captured_fpsi);
|
||||||
US_RING_DELETE_WITH_ITEMS(stream->run->http->jpeg_ring, us_frame_destroy);
|
US_RING_DELETE_WITH_ITEMS(stream->run->http->jpeg_ring, us_frame_destroy);
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ typedef struct {
|
|||||||
|
|
||||||
|
|
||||||
us_stream_s *us_stream_init(us_capture_s *cap, us_encoder_s *enc);
|
us_stream_s *us_stream_init(us_capture_s *cap, us_encoder_s *enc);
|
||||||
|
void us_stream_update_blank(us_stream_s *stream, const us_capture_s *cap);
|
||||||
void us_stream_destroy(us_stream_s *stream);
|
void us_stream_destroy(us_stream_s *stream);
|
||||||
|
|
||||||
void us_stream_loop(us_stream_s *stream);
|
void us_stream_loop(us_stream_s *stream);
|
||||||
|
|||||||
Reference in New Issue
Block a user