mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-11-04 20:14:24 +00:00
Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdea0d214b | ||
|
|
72e2c6dabe | ||
|
|
686460fa29 | ||
|
|
54c1c77dfb | ||
|
|
0e0aef7b80 | ||
|
|
31b98f1d59 | ||
|
|
9734c83bd4 | ||
|
|
b88abdcae4 | ||
|
|
6fca17cf52 | ||
|
|
5fe579956e | ||
|
|
52dfa38a8f | ||
|
|
8b76b59393 | ||
|
|
92878e22ab | ||
|
|
4dc0f73e1b | ||
|
|
9fe42b038a | ||
|
|
3750a22091 | ||
|
|
ef59c52a83 | ||
|
|
f296bb57e7 | ||
|
|
f7166b7c41 | ||
|
|
b35200345e | ||
|
|
5bd7c9c5ef | ||
|
|
fc35b1cd87 | ||
|
|
92d9828894 | ||
|
|
e7008c6086 | ||
|
|
d0672e15b9 | ||
|
|
04aa4c166c | ||
|
|
0fbbc27d8c | ||
|
|
b02bb85d8b | ||
|
|
80dce276ed | ||
|
|
e869536a11 | ||
|
|
b0acf0fa4d | ||
|
|
f32e2569d2 | ||
|
|
6d63f8e5d2 | ||
|
|
2dac4db9fc | ||
|
|
23dd2f9e66 | ||
|
|
fdfd4225d0 | ||
|
|
9424b655eb | ||
|
|
f94fe25845 | ||
|
|
bdcbaae8b3 | ||
|
|
52a2326426 | ||
|
|
fa15d9693f | ||
|
|
88460b72e1 | ||
|
|
8c69c77481 | ||
|
|
5331ae14aa | ||
|
|
0127dcf018 | ||
|
|
aa58b1b002 | ||
|
|
a05eab71a8 | ||
|
|
e013356cf0 | ||
|
|
c730981827 | ||
|
|
3bb1ed3ef3 | ||
|
|
1cda22bfd2 | ||
|
|
29ee20e864 | ||
|
|
5e13f4cd58 | ||
|
|
39c1916f61 | ||
|
|
614e83771b | ||
|
|
15a9e28ac6 | ||
|
|
a1ae02de5d | ||
|
|
214708549d | ||
|
|
1be4521664 | ||
|
|
94752dde75 | ||
|
|
b933b7b407 | ||
|
|
61f44b5f97 | ||
|
|
8fef0408b6 | ||
|
|
62028be064 | ||
|
|
3b7592bb31 | ||
|
|
8adca998e9 | ||
|
|
6ac5a5f065 |
@@ -1,7 +1,7 @@
|
||||
[bumpversion]
|
||||
commit = True
|
||||
tag = True
|
||||
current_version = 6.47
|
||||
current_version = 6.61
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)
|
||||
serialize =
|
||||
{major}.{minor}
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,6 +6,7 @@
|
||||
/python/dist/
|
||||
/python/ustreamer.egg-info/
|
||||
/python/root/
|
||||
/python/build/
|
||||
/janus/build/
|
||||
/ustreamer
|
||||
/ustreamer-*
|
||||
|
||||
2
Makefile
2
Makefile
@@ -72,7 +72,7 @@ apps:
|
||||
|
||||
python:
|
||||
$(MAKE) -C python
|
||||
$(ECHO) ln -sf python/root/usr/lib/python*/site-packages/*.so .
|
||||
$(ECHO) ln -sf python/build/lib.linux-*/*.so -t .
|
||||
|
||||
|
||||
janus:
|
||||
|
||||
11
README.md
11
README.md
@@ -35,13 +35,14 @@ If you're going to live-stream from your backyard webcam and need to control it,
|
||||
# Installation
|
||||
|
||||
## Building
|
||||
You need to download the µStreamer onto your system and build it from the sources.
|
||||
You need to download the µStreamer onto your system and build it from the sources, or use a package:
|
||||
|
||||
* AUR has a package for Arch Linux: https://aur.archlinux.org/packages/ustreamer.
|
||||
* Fedora: https://src.fedoraproject.org/rpms/ustreamer.
|
||||
* Ubuntu: https://packages.ubuntu.com/jammy/ustreamer.
|
||||
* Arch Linux: https://aur.archlinux.org/packages/ustreamer
|
||||
* Fedora: https://src.fedoraproject.org/rpms/ustreamer
|
||||
* Ubuntu: https://packages.ubuntu.com/jammy/ustreamer
|
||||
* Debian: https://packages.debian.org/sid/ustreamer
|
||||
* FreeBSD port: https://www.freshports.org/multimedia/ustreamer.
|
||||
* OpenWRT: https://github.com/openwrt/packages/tree/master/multimedia/ustreamer
|
||||
* FreeBSD port: https://www.freshports.org/multimedia/ustreamer
|
||||
|
||||
### Preconditions
|
||||
You'll need ```make```, ```gcc```, ```pkg-config```, ```libevent``` with ```pthreads``` support, ```libjpeg9```/```libjpeg-turbo``` and ```libbsd``` (only for Linux).
|
||||
|
||||
@@ -19,7 +19,7 @@ _BUILD = build
|
||||
|
||||
|
||||
# =====
|
||||
ifneq ($(shell sh -c 'uname 2>/dev/null || echo Unknown'),FreeBSD)
|
||||
ifeq ($(findstring bsd,$(shell $(CC) -dumpmachine)),)
|
||||
override _LDFLAGS += -latomic
|
||||
endif
|
||||
|
||||
|
||||
@@ -34,13 +34,13 @@
|
||||
#include "uslibs/types.h"
|
||||
#include "uslibs/errors.h"
|
||||
#include "uslibs/tools.h"
|
||||
#include "uslibs/threading.h"
|
||||
#include "uslibs/logging.h"
|
||||
#include "uslibs/array.h"
|
||||
#include "uslibs/ring.h"
|
||||
#include "uslibs/threading.h"
|
||||
|
||||
#include "rtp.h"
|
||||
#include "au.h"
|
||||
#include "logging.h"
|
||||
|
||||
|
||||
static void *_pcm_thread(void *v_acap);
|
||||
@@ -60,14 +60,14 @@ us_acap_s *us_acap_init(const char *name, uint pcm_hz) {
|
||||
{
|
||||
if ((err = snd_pcm_open(&acap->dev, name, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
|
||||
acap->dev = NULL;
|
||||
US_JLOG_PERROR_ALSA(err, "acap", "Can't open PCM capture");
|
||||
US_LOG_PERROR_ALSA(err, "Can't open PCM capture");
|
||||
goto error;
|
||||
}
|
||||
assert(!snd_pcm_hw_params_malloc(&acap->dev_params));
|
||||
|
||||
# define SET_PARAM(_msg, _func, ...) { \
|
||||
if ((err = _func(acap->dev, acap->dev_params, ##__VA_ARGS__)) < 0) { \
|
||||
US_JLOG_PERROR_ALSA(err, "acap", _msg); \
|
||||
US_LOG_PERROR_ALSA(err, _msg); \
|
||||
goto error; \
|
||||
} \
|
||||
}
|
||||
@@ -78,7 +78,7 @@ us_acap_s *us_acap_init(const char *name, uint pcm_hz) {
|
||||
SET_PARAM("Can't set PCM sampling format", snd_pcm_hw_params_set_format, SND_PCM_FORMAT_S16_LE);
|
||||
SET_PARAM("Can't set PCM sampling rate", snd_pcm_hw_params_set_rate_near, &acap->pcm_hz, 0);
|
||||
if (acap->pcm_hz < US_AU_MIN_PCM_HZ || acap->pcm_hz > US_AU_MAX_PCM_HZ) {
|
||||
US_JLOG_ERROR("acap", "Unsupported PCM freq: %u; should be: %u <= F <= %u",
|
||||
US_LOG_ERROR("Unsupported PCM freq: %u; should be: %u <= F <= %u",
|
||||
acap->pcm_hz, US_AU_MIN_PCM_HZ, US_AU_MAX_PCM_HZ);
|
||||
goto error;
|
||||
}
|
||||
@@ -93,7 +93,7 @@ us_acap_s *us_acap_init(const char *name, uint pcm_hz) {
|
||||
acap->res = speex_resampler_init(US_RTP_OPUS_CH, acap->pcm_hz, US_RTP_OPUS_HZ, SPEEX_RESAMPLER_QUALITY_DESKTOP, &err);
|
||||
if (err < 0) {
|
||||
acap->res = NULL;
|
||||
US_JLOG_PERROR_RES(err, "acap", "Can't create resampler");
|
||||
US_LOG_PERROR_RES(err, "Can't create resampler");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,7 @@ us_acap_s *us_acap_init(const char *name, uint pcm_hz) {
|
||||
// OPUS_SET_INBAND_FEC(1), OPUS_SET_PACKET_LOSS_PERC(10): see rtpa.c
|
||||
}
|
||||
|
||||
US_JLOG_INFO("acap", "Capture configured on %uHz; capturing ...", acap->pcm_hz);
|
||||
US_LOG_INFO("Capture configured on %uHz; capturing ...", acap->pcm_hz);
|
||||
acap->tids_created = true;
|
||||
US_THREAD_CREATE(acap->enc_tid, _encoder_thread, acap);
|
||||
US_THREAD_CREATE(acap->pcm_tid, _pcm_thread, acap);
|
||||
@@ -135,7 +135,7 @@ void us_acap_destroy(us_acap_s *acap) {
|
||||
US_RING_DELETE_WITH_ITEMS(acap->enc_ring, us_au_encoded_destroy);
|
||||
US_RING_DELETE_WITH_ITEMS(acap->pcm_ring, us_au_pcm_destroy);
|
||||
if (acap->tids_created) {
|
||||
US_JLOG_INFO("acap", "Capture closed");
|
||||
US_LOG_INFO("Capture closed");
|
||||
}
|
||||
free(acap);
|
||||
}
|
||||
@@ -161,7 +161,7 @@ int us_acap_get_encoded(us_acap_s *acap, u8 *data, uz *size, u64 *pts) {
|
||||
}
|
||||
|
||||
static void *_pcm_thread(void *v_acap) {
|
||||
US_THREAD_SETTLE("us_ac_pcm");
|
||||
US_THREAD_SETTLE("us_acap_pcm");
|
||||
|
||||
us_acap_s *const acap = v_acap;
|
||||
u8 in[US_AU_MAX_BUF8];
|
||||
@@ -169,10 +169,10 @@ static void *_pcm_thread(void *v_acap) {
|
||||
while (!atomic_load(&acap->stop)) {
|
||||
const int frames = snd_pcm_readi(acap->dev, in, acap->pcm_frames);
|
||||
if (frames < 0) {
|
||||
US_JLOG_PERROR_ALSA(frames, "acap", "Fatal: Can't capture PCM frames");
|
||||
US_LOG_PERROR_ALSA(frames, "Fatal: Can't capture PCM frames");
|
||||
break;
|
||||
} else if (frames < (int)acap->pcm_frames) {
|
||||
US_JLOG_ERROR("acap", "Fatal: Too few PCM frames captured");
|
||||
US_LOG_ERROR("Fatal: Too few PCM frames captured");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ static void *_pcm_thread(void *v_acap) {
|
||||
memcpy(out->data, in, acap->pcm_size);
|
||||
us_ring_producer_release(acap->pcm_ring, ri);
|
||||
} else {
|
||||
US_JLOG_ERROR("acap", "PCM ring is full");
|
||||
US_LOG_ERROR("PCM ring is full");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ static void *_pcm_thread(void *v_acap) {
|
||||
}
|
||||
|
||||
static void *_encoder_thread(void *v_acap) {
|
||||
US_THREAD_SETTLE("us_ac_enc");
|
||||
US_THREAD_SETTLE("us_acap_enc");
|
||||
|
||||
us_acap_s *const acap = v_acap;
|
||||
s16 in_res[US_AU_MAX_BUF16];
|
||||
@@ -217,7 +217,7 @@ static void *_encoder_thread(void *v_acap) {
|
||||
|
||||
const int out_ri = us_ring_producer_acquire(acap->enc_ring, 0);
|
||||
if (out_ri < 0) {
|
||||
US_JLOG_ERROR("acap", "OPUS encoder queue is full");
|
||||
US_LOG_ERROR("OPUS encoder queue is full");
|
||||
us_ring_consumer_release(acap->pcm_ring, in_ri);
|
||||
continue;
|
||||
}
|
||||
@@ -233,7 +233,7 @@ static void *_encoder_thread(void *v_acap) {
|
||||
acap->pts += US_AU_HZ_TO_FRAMES(US_RTP_OPUS_HZ);
|
||||
} else {
|
||||
out->used = 0;
|
||||
US_JLOG_PERROR_OPUS(size, "acap", "Fatal: Can't encode PCM frame to OPUS");
|
||||
US_LOG_PERROR_OPUS(size, "Fatal: Can't encode PCM frame to OPUS");
|
||||
}
|
||||
us_ring_producer_release(acap->enc_ring, out_ri);
|
||||
}
|
||||
|
||||
@@ -35,11 +35,11 @@
|
||||
#include "uslibs/types.h"
|
||||
#include "uslibs/tools.h"
|
||||
#include "uslibs/threading.h"
|
||||
#include "uslibs/logging.h"
|
||||
#include "uslibs/array.h"
|
||||
#include "uslibs/list.h"
|
||||
#include "uslibs/ring.h"
|
||||
|
||||
#include "logging.h"
|
||||
#include "au.h"
|
||||
#include "rtp.h"
|
||||
|
||||
@@ -55,6 +55,9 @@ us_janus_client_s *us_janus_client_init(janus_callbacks *gw, janus_plugin_sessio
|
||||
US_CALLOC(client, 1);
|
||||
client->gw = gw;
|
||||
client->session = session;
|
||||
client->video_ssrc = us_get_ssrc();
|
||||
client->audio_ssrc = us_get_ssrc();
|
||||
|
||||
atomic_init(&client->transmit, false);
|
||||
atomic_init(&client->transmit_acap, false);
|
||||
atomic_init(&client->transmit_aplay, false);
|
||||
@@ -99,7 +102,7 @@ void us_janus_client_send(us_janus_client_s *client, const us_rtp_s *rtp) {
|
||||
us_ring_s *const ring = (rtp->video ? client->video_ring : client->acap_ring);
|
||||
const int ri = us_ring_producer_acquire(ring, 0);
|
||||
if (ri < 0) {
|
||||
US_JLOG_ERROR("client", "Session %p %s ring is full",
|
||||
US_LOG_ERROR("Session %p %s ring is full",
|
||||
client->session, (rtp->video ? "video" : "acap"));
|
||||
return;
|
||||
}
|
||||
@@ -139,7 +142,7 @@ void us_janus_client_recv(us_janus_client_s *client, janus_plugin_rtp *packet) {
|
||||
us_ring_s *const ring = client->aplay_enc_ring;
|
||||
const int ri = us_ring_producer_acquire(ring, 0);
|
||||
if (ri < 0) {
|
||||
// US_JLOG_ERROR("client", "Session %p aplay ring is full", client->session);
|
||||
// US_LOG_ERROR("Session %p aplay ring is full", client->session);
|
||||
return;
|
||||
}
|
||||
us_au_encoded_s *enc = ring->items[ri];
|
||||
@@ -154,12 +157,12 @@ void us_janus_client_recv(us_janus_client_s *client, janus_plugin_rtp *packet) {
|
||||
}
|
||||
|
||||
static void *_video_thread(void *v_client) {
|
||||
US_THREAD_SETTLE("us_cx_vid");
|
||||
US_THREAD_SETTLE("us_cx_vcap");
|
||||
return _video_or_acap_thread(v_client, true);
|
||||
}
|
||||
|
||||
static void *_acap_thread(void *v_client) {
|
||||
US_THREAD_SETTLE("us_cx_ac");
|
||||
US_THREAD_SETTLE("us_cx_acap");
|
||||
return _video_or_acap_thread(v_client, false);
|
||||
}
|
||||
|
||||
@@ -181,6 +184,8 @@ static void *_video_or_acap_thread(void *v_client, bool video) {
|
||||
atomic_load(&client->transmit)
|
||||
&& (video || atomic_load(&client->transmit_acap))
|
||||
) {
|
||||
us_rtp_write_ssrc(&rtp, (rtp.video ? client->video_ssrc : client->audio_ssrc));
|
||||
|
||||
janus_plugin_rtp packet = {
|
||||
.video = rtp.video,
|
||||
.buffer = (char*)rtp.datagram,
|
||||
@@ -193,18 +198,24 @@ static void *_video_or_acap_thread(void *v_client, bool video) {
|
||||
};
|
||||
janus_plugin_rtp_extensions_reset(&packet.extensions);
|
||||
|
||||
if (rtp.zero_playout_delay) {
|
||||
// https://github.com/pikvm/pikvm/issues/784
|
||||
packet.extensions.min_delay = 0;
|
||||
packet.extensions.max_delay = 0;
|
||||
} else {
|
||||
// Эти дефолты используются в Chrome/Safari/Firefox.
|
||||
// Работает всё одинаково, потому что у них общая кодовая база WebRTC.
|
||||
packet.extensions.min_delay = 0;
|
||||
packet.extensions.max_delay = 1000; // == 10s, i.e. 10ms granularity
|
||||
if (rtp.first_of_frame) {
|
||||
if (rtp.zero_playout_delay) {
|
||||
// https://github.com/pikvm/pikvm/issues/784
|
||||
packet.extensions.min_delay = 0;
|
||||
packet.extensions.max_delay = 0;
|
||||
} else {
|
||||
// Эти дефолты используются в Chrome/Safari/Firefox.
|
||||
// Работает всё одинаково, потому что у них общая кодовая база WebRTC.
|
||||
packet.extensions.min_delay = 0;
|
||||
packet.extensions.max_delay = 1000; // == 10s, i.e. 10ms granularity
|
||||
}
|
||||
}
|
||||
|
||||
if (rtp.video) {
|
||||
if (rtp.video && rtp.first_of_frame) {
|
||||
packet.extensions.abs_capture_ts = rtp.grab_ntp_ts;
|
||||
}
|
||||
|
||||
if (rtp.video && rtp.last_of_frame) {
|
||||
uint video_orient = atomic_load(&client->video_orient);
|
||||
if (video_orient != 0) {
|
||||
// The extension rotates the video clockwise, but want it counterclockwise.
|
||||
@@ -225,7 +236,7 @@ static void *_video_or_acap_thread(void *v_client, bool video) {
|
||||
}
|
||||
|
||||
static void *_aplay_thread(void *v_client) {
|
||||
US_THREAD_SETTLE("us_cx_ap");
|
||||
US_THREAD_SETTLE("us_cx_aplay");
|
||||
|
||||
us_janus_client_s *const client = v_client;
|
||||
|
||||
@@ -247,7 +258,7 @@ static void *_aplay_thread(void *v_client) {
|
||||
|
||||
const int out_ri = us_ring_producer_acquire(client->aplay_pcm_ring, 0);
|
||||
if (out_ri < 0) {
|
||||
US_JLOG_ERROR("aplay", "OPUS decoder queue is full");
|
||||
US_LOG_ERROR("OPUS decoder queue is full");
|
||||
us_ring_consumer_release(client->aplay_enc_ring, in_ri);
|
||||
continue;
|
||||
}
|
||||
@@ -260,7 +271,7 @@ static void *_aplay_thread(void *v_client) {
|
||||
out->frames = frames;
|
||||
} else {
|
||||
out->frames = 0;
|
||||
US_JLOG_PERROR_OPUS(frames, "aplay", "Fatal: Can't decode OPUS to PCM frame");
|
||||
US_LOG_PERROR_OPUS(frames, "Fatal: Can't decode OPUS to PCM frame");
|
||||
}
|
||||
us_ring_producer_release(client->aplay_pcm_ring, out_ri);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
typedef struct {
|
||||
janus_callbacks *gw;
|
||||
janus_plugin_session *session;
|
||||
u32 video_ssrc;
|
||||
u32 audio_ssrc;
|
||||
|
||||
atomic_bool transmit;
|
||||
atomic_bool transmit_acap;
|
||||
atomic_bool transmit_aplay;
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
|
||||
#include "uslibs/types.h"
|
||||
#include "uslibs/tools.h"
|
||||
#include "uslibs/logging.h"
|
||||
|
||||
#include "const.h"
|
||||
#include "logging.h"
|
||||
|
||||
|
||||
static char *_get_value(janus_config *jcfg, const char *section, const char *option);
|
||||
@@ -48,28 +48,29 @@ us_config_s *us_config_init(const char *config_dir_path) {
|
||||
janus_config *jcfg = NULL;
|
||||
|
||||
US_ASPRINTF(config_file_path, "%s/%s.jcfg", config_dir_path, US_PLUGIN_PACKAGE);
|
||||
US_JLOG_INFO("config", "Reading config file '%s' ...", config_file_path);
|
||||
US_LOG_INFO("Reading config file '%s' ...", config_file_path);
|
||||
|
||||
jcfg = janus_config_parse(config_file_path);
|
||||
if (jcfg == NULL) {
|
||||
US_JLOG_ERROR("config", "Can't read config");
|
||||
US_LOG_ERROR("Can't read config");
|
||||
goto error;
|
||||
}
|
||||
janus_config_print(jcfg);
|
||||
|
||||
if ((config->video_sink_name = _get_value(jcfg, "video", "sink")) == NULL) {
|
||||
US_JLOG_ERROR("config", "Missing config value: video.sink");
|
||||
US_LOG_ERROR("Missing config value: video.sink");
|
||||
goto error;
|
||||
}
|
||||
if ((config->acap_dev_name = _get_value(jcfg, "acap", "device")) != NULL) {
|
||||
config->acap_hz = _get_uint(jcfg, "acap", "sampling_rate", 0);
|
||||
config->tc358743_dev_path = _get_value(jcfg, "acap", "tc358743");
|
||||
if (config->acap_hz == 0 && config->tc358743_dev_path == NULL) {
|
||||
US_JLOG_ERROR("config", "Either acap.sampling_rate or acap.tc358743 required");
|
||||
US_LOG_ERROR("Either acap.sampling_rate or acap.tc358743 required");
|
||||
goto error;
|
||||
}
|
||||
config->aplay_dev_name = _get_value(jcfg, "aplay", "device");
|
||||
}
|
||||
config->aplay_dev_name = _get_value(jcfg, "aplay", "device");
|
||||
config->vplay_sink_name = _get_value(jcfg, "vplay", "sink");
|
||||
|
||||
goto ok;
|
||||
|
||||
@@ -87,6 +88,7 @@ void us_config_destroy(us_config_s *config) {
|
||||
US_DELETE(config->acap_dev_name, free);
|
||||
US_DELETE(config->tc358743_dev_path, free);
|
||||
US_DELETE(config->aplay_dev_name, free);
|
||||
US_DELETE(config->vplay_sink_name, free);
|
||||
free(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ typedef struct {
|
||||
char *tc358743_dev_path;
|
||||
|
||||
char *aplay_dev_name;
|
||||
char *vplay_sink_name;
|
||||
} us_config_s;
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
@@ -43,20 +42,20 @@
|
||||
#include "uslibs/errors.h"
|
||||
#include "uslibs/tools.h"
|
||||
#include "uslibs/threading.h"
|
||||
#include "uslibs/logging.h"
|
||||
#include "uslibs/list.h"
|
||||
#include "uslibs/ring.h"
|
||||
#include "uslibs/memsinksh.h"
|
||||
#include "uslibs/memsink.h"
|
||||
#include "uslibs/tc358743.h"
|
||||
|
||||
#include "const.h"
|
||||
#include "logging.h"
|
||||
#include "client.h"
|
||||
#include "au.h"
|
||||
#include "acap.h"
|
||||
#include "rtp.h"
|
||||
#include "rtpv.h"
|
||||
#include "rtpa.h"
|
||||
#include "memsinkfd.h"
|
||||
#include "sdp.h"
|
||||
#include "config.h"
|
||||
|
||||
|
||||
@@ -136,81 +135,53 @@ static void *_video_rtp_thread(void *arg) {
|
||||
|
||||
static void *_video_sink_thread(void *arg) {
|
||||
(void)arg;
|
||||
US_THREAD_SETTLE("us_p_vsink");
|
||||
US_THREAD_SETTLE("us_p_vcap");
|
||||
atomic_store(&_g_video_sink_tid_created, true);
|
||||
|
||||
us_frame_s *drop = us_frame_init();
|
||||
u64 frame_id = 0;
|
||||
us_frame_s *tmp = us_frame_init();
|
||||
int once = 0;
|
||||
|
||||
while (!_STOP) {
|
||||
if (!_HAS_WATCHERS) {
|
||||
US_ONCE({ US_JLOG_INFO("video", "No active watchers, memsink disconnected"); });
|
||||
US_ONCE({ US_LOG_INFO("No active watchers, memsink disconnected"); });
|
||||
usleep(_g_watchers_polling);
|
||||
continue;
|
||||
}
|
||||
|
||||
int fd = -1;
|
||||
us_memsink_shared_s *mem = NULL;
|
||||
|
||||
const uz data_size = us_memsink_calculate_size(_g_config->video_sink_name);
|
||||
if (data_size == 0) {
|
||||
US_ONCE({ US_JLOG_ERROR("video", "Invalid memsink object suffix"); });
|
||||
goto close_memsink;
|
||||
}
|
||||
|
||||
if ((fd = shm_open(_g_config->video_sink_name, O_RDWR, 0)) <= 0) {
|
||||
US_ONCE({ US_JLOG_PERROR("video", "Can't open memsink"); });
|
||||
goto close_memsink;
|
||||
}
|
||||
|
||||
if ((mem = us_memsink_shared_map(fd, data_size)) == NULL) {
|
||||
US_ONCE({ US_JLOG_PERROR("video", "Can't map memsink"); });
|
||||
us_memsink_s *sink = us_memsink_init_opened("vcap", _g_config->video_sink_name, false, 0, false, 0, 1);
|
||||
if (sink == NULL) {
|
||||
goto close_memsink;
|
||||
}
|
||||
|
||||
once = 0;
|
||||
|
||||
US_JLOG_INFO("video", "Memsink opened; reading frames ...");
|
||||
US_LOG_INFO("Memsink opened; reading frames ...");
|
||||
while (!_STOP && _HAS_WATCHERS) {
|
||||
const int waited = us_memsink_fd_wait_frame(fd, mem, frame_id);
|
||||
if (waited == 0) {
|
||||
const int got = us_memsink_client_get(sink, tmp, NULL, atomic_load(&_g_key_required));
|
||||
if (got == 0) {
|
||||
const int ri = us_ring_producer_acquire(_g_video_ring, 0);
|
||||
us_frame_s *frame;
|
||||
if (ri >= 0) {
|
||||
frame = _g_video_ring->items[ri];
|
||||
} else {
|
||||
US_ONCE({ US_JLOG_PERROR("video", "Video ring is full"); });
|
||||
frame = drop;
|
||||
}
|
||||
|
||||
const int got = us_memsink_fd_get_frame(fd, mem, frame, &frame_id, atomic_load(&_g_key_required));
|
||||
if (ri >= 0) {
|
||||
us_frame_s *dest = _g_video_ring->items[ri];
|
||||
us_frame_copy(tmp, dest);
|
||||
us_ring_producer_release(_g_video_ring, ri);
|
||||
if (tmp->key) {
|
||||
atomic_store(&_g_key_required, false);
|
||||
}
|
||||
} else {
|
||||
US_ONCE({ US_LOG_ERROR("Video ring is full"); });
|
||||
}
|
||||
if (got < 0) {
|
||||
goto close_memsink;
|
||||
}
|
||||
|
||||
if (ri >= 0 && frame->key) {
|
||||
atomic_store(&_g_key_required, false);
|
||||
}
|
||||
} else if (waited != US_ERROR_NO_DATA) {
|
||||
} else if (got != US_ERROR_NO_DATA) {
|
||||
goto close_memsink;
|
||||
}
|
||||
}
|
||||
|
||||
close_memsink:
|
||||
if (mem != NULL) {
|
||||
us_memsink_shared_unmap(mem, data_size);
|
||||
mem = NULL;
|
||||
}
|
||||
US_CLOSE_FD(fd);
|
||||
US_JLOG_INFO("video", "Memsink closed");
|
||||
US_DELETE(sink, us_memsink_destroy);
|
||||
US_LOG_INFO("Memsink closed");
|
||||
sleep(1); // error_delay
|
||||
}
|
||||
|
||||
us_frame_destroy(drop);
|
||||
us_frame_destroy(tmp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -220,17 +191,17 @@ static int _get_acap_hz(uint *hz) {
|
||||
return 0;
|
||||
}
|
||||
if (_g_config->tc358743_dev_path == NULL) {
|
||||
US_JLOG_ERROR("acap", "No configured sampling rate");
|
||||
US_LOG_ERROR("No configured sampling rate");
|
||||
return -1;
|
||||
}
|
||||
int fd;
|
||||
if ((fd = open(_g_config->tc358743_dev_path, O_RDWR)) < 0) {
|
||||
US_JLOG_PERROR("acap", "Can't open TC358743 V4L2 device");
|
||||
US_LOG_PERROR("Can't open TC358743 V4L2 device");
|
||||
return -1;
|
||||
}
|
||||
const int checked = us_tc358743_xioctl_get_audio_hz(fd, hz);
|
||||
if (checked < 0) {
|
||||
US_JLOG_PERROR("acap", "Can't check TC358743 audio state (%d)", checked);
|
||||
US_LOG_PERROR("Can't check TC358743 audio state (%d)", checked);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
@@ -240,7 +211,7 @@ static int _get_acap_hz(uint *hz) {
|
||||
|
||||
static void *_acap_thread(void *arg) {
|
||||
(void)arg;
|
||||
US_THREAD_SETTLE("us_p_ac");
|
||||
US_THREAD_SETTLE("us_p_acap");
|
||||
atomic_store(&_g_acap_tid_created, true);
|
||||
|
||||
assert(_g_config->acap_dev_name != NULL);
|
||||
@@ -258,17 +229,17 @@ static void *_acap_thread(void *arg) {
|
||||
us_acap_s *acap = NULL;
|
||||
|
||||
if (!us_au_probe(_g_config->acap_dev_name)) {
|
||||
US_ONCE({ US_JLOG_ERROR("acap", "No PCM capture device"); });
|
||||
US_ONCE({ US_LOG_ERROR("No PCM capture device"); });
|
||||
goto close_acap;
|
||||
}
|
||||
if (_get_acap_hz(&hz) < 0) {
|
||||
goto close_acap;
|
||||
}
|
||||
if (hz == 0) {
|
||||
US_ONCE({ US_JLOG_INFO("acap", "No audio presented from the host"); });
|
||||
US_ONCE({ US_LOG_INFO("No audio presented from the host"); });
|
||||
goto close_acap;
|
||||
}
|
||||
US_ONCE({ US_JLOG_INFO("acap", "Detected host audio"); });
|
||||
US_ONCE({ US_LOG_INFO("Detected host audio"); });
|
||||
if ((acap = us_acap_init(_g_config->acap_dev_name, hz)) == NULL) {
|
||||
goto close_acap;
|
||||
}
|
||||
@@ -279,7 +250,7 @@ static void *_acap_thread(void *arg) {
|
||||
if (_get_acap_hz(&hz) < 0 || acap->pcm_hz != hz) {
|
||||
goto close_acap;
|
||||
}
|
||||
uz size = US_RTP_DATAGRAM_SIZE - US_RTP_HEADER_SIZE;
|
||||
uz size = US_RTP_TOTAL_SIZE - US_RTP_HEADER_SIZE;
|
||||
u8 data[size];
|
||||
u64 pts;
|
||||
const int result = us_acap_get_encoded(acap, data, &size, &pts);
|
||||
@@ -301,7 +272,7 @@ static void *_acap_thread(void *arg) {
|
||||
|
||||
static void *_aplay_thread(void *arg) {
|
||||
(void)arg;
|
||||
US_THREAD_SETTLE("us_p_ap");
|
||||
US_THREAD_SETTLE("us_p_aplay");
|
||||
atomic_store(&_g_aplay_tid_created, true);
|
||||
|
||||
assert(_g_config->aplay_dev_name != NULL);
|
||||
@@ -330,10 +301,10 @@ static void *_aplay_thread(void *arg) {
|
||||
}
|
||||
} while (skip && !_STOP);
|
||||
us_au_pcm_mix(&mixed, &last);
|
||||
// US_JLOG_INFO("++++++", "mixed %p", client);
|
||||
// US_LOG_INFO("++++++ mixed %p", client);
|
||||
});
|
||||
_UNLOCK_APLAY;
|
||||
// US_JLOG_INFO("++++++", "--------------");
|
||||
// US_LOG_INFO("++++++ --------------");
|
||||
|
||||
if (skip) {
|
||||
static uint skipped = 0;
|
||||
@@ -345,19 +316,19 @@ static void *_aplay_thread(void *arg) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!_HAS_WATCHERS || !_HAS_LISTENERS || !_HAS_SPEAKERS) {
|
||||
if (!_HAS_WATCHERS || !_HAS_SPEAKERS) {
|
||||
goto close_aplay;
|
||||
}
|
||||
|
||||
if (dev == NULL) {
|
||||
if (!us_au_probe(_g_config->aplay_dev_name)) {
|
||||
US_ONCE({ US_JLOG_ERROR("aplay", "No PCM playback device"); });
|
||||
US_ONCE({ US_LOG_ERROR("No PCM playback device"); });
|
||||
goto close_aplay;
|
||||
}
|
||||
|
||||
int err = snd_pcm_open(&dev, _g_config->aplay_dev_name, SND_PCM_STREAM_PLAYBACK, 0);
|
||||
if (err < 0) {
|
||||
US_ONCE({ US_JLOG_PERROR_ALSA(err, "aplay", "Can't open PCM playback"); });
|
||||
US_ONCE({ US_LOG_PERROR_ALSA(err, "Can't open PCM playback"); });
|
||||
goto close_aplay;
|
||||
}
|
||||
|
||||
@@ -365,11 +336,11 @@ static void *_aplay_thread(void *arg) {
|
||||
US_RTP_OPUS_CH, US_RTP_OPUS_HZ, 1 /* soft resample */, 50000 /* 50000 = 0.05sec */
|
||||
);
|
||||
if (err < 0) {
|
||||
US_ONCE({ US_JLOG_PERROR_ALSA(err, "aplay", "Can't configure PCM playback"); });
|
||||
US_ONCE({ US_LOG_PERROR_ALSA(err, "Can't configure PCM playback"); });
|
||||
goto close_aplay;
|
||||
}
|
||||
|
||||
US_JLOG_INFO("aplay", "Playback opened, playing ...");
|
||||
US_LOG_INFO("Playback opened, playing ...");
|
||||
once = 0;
|
||||
}
|
||||
|
||||
@@ -379,20 +350,20 @@ static void *_aplay_thread(void *arg) {
|
||||
frames = snd_pcm_recover(dev, frames, 1);
|
||||
} else {
|
||||
if (once != 0) {
|
||||
US_JLOG_INFO("aplay", "Playing resumed (snd_pcm_writei) ...");
|
||||
US_LOG_INFO("Playing resumed (snd_pcm_writei) ...");
|
||||
}
|
||||
once = 0;
|
||||
skip = false;
|
||||
}
|
||||
if (frames < 0) {
|
||||
US_ONCE({ US_JLOG_PERROR_ALSA(frames, "aplay", "Can't play to PCM playback"); });
|
||||
US_ONCE({ US_LOG_PERROR_ALSA(frames, "Can't play to PCM playback"); });
|
||||
if (frames == -ENODEV) {
|
||||
goto close_aplay;
|
||||
}
|
||||
skip = true;
|
||||
} else {
|
||||
if (once != 0) {
|
||||
US_JLOG_INFO("aplay", "Playing resumed (snd_pcm_recover) ...");
|
||||
US_LOG_INFO("Playing resumed (snd_pcm_recover) ...");
|
||||
}
|
||||
once = 0;
|
||||
skip = false;
|
||||
@@ -403,7 +374,7 @@ static void *_aplay_thread(void *arg) {
|
||||
close_aplay:
|
||||
if (dev != NULL) {
|
||||
US_DELETE(dev, snd_pcm_close);
|
||||
US_JLOG_INFO("aplay", "Playback closed");
|
||||
US_LOG_INFO("Playback closed");
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
@@ -430,7 +401,9 @@ static int _plugin_init(janus_callbacks *gw, const char *config_dir_path) {
|
||||
// sysctl -w net.core.rmem_max=1000000
|
||||
// sysctl -w net.core.wmem_max=1000000
|
||||
|
||||
US_JLOG_INFO("main", "Initializing PiKVM uStreamer plugin %s ...", US_VERSION);
|
||||
US_LOGGING_INIT;
|
||||
|
||||
US_LOG_INFO("Initializing PiKVM uStreamer plugin %s ...", US_VERSION);
|
||||
if (gw == NULL || config_dir_path == NULL || ((_g_config = us_config_init(config_dir_path)) == NULL)) {
|
||||
return -1;
|
||||
}
|
||||
@@ -443,9 +416,9 @@ static int _plugin_init(janus_callbacks *gw, const char *config_dir_path) {
|
||||
if (_g_config->acap_dev_name != NULL) {
|
||||
_g_rtpa = us_rtpa_init(_relay_rtp_clients);
|
||||
US_THREAD_CREATE(_g_acap_tid, _acap_thread, NULL);
|
||||
if (_g_config->aplay_dev_name != NULL) {
|
||||
US_THREAD_CREATE(_g_aplay_tid, _aplay_thread, NULL);
|
||||
}
|
||||
}
|
||||
if (_g_config->aplay_dev_name != NULL) {
|
||||
US_THREAD_CREATE(_g_aplay_tid, _aplay_thread, NULL);
|
||||
}
|
||||
US_THREAD_CREATE(_g_video_rtp_tid, _video_rtp_thread, NULL);
|
||||
US_THREAD_CREATE(_g_video_sink_tid, _video_sink_thread, NULL);
|
||||
@@ -455,7 +428,7 @@ static int _plugin_init(janus_callbacks *gw, const char *config_dir_path) {
|
||||
}
|
||||
|
||||
static void _plugin_destroy(void) {
|
||||
US_JLOG_INFO("main", "Destroying plugin ...");
|
||||
US_LOG_INFO("Destroying plugin ...");
|
||||
|
||||
atomic_store(&_g_stop, true);
|
||||
# define JOIN(_tid) { if (atomic_load(&_tid##_created)) { US_THREAD_JOIN(_tid); } }
|
||||
@@ -475,12 +448,14 @@ static void _plugin_destroy(void) {
|
||||
US_DELETE(_g_rtpa, us_rtpa_destroy);
|
||||
US_DELETE(_g_rtpv, us_rtpv_destroy);
|
||||
US_DELETE(_g_config, us_config_destroy);
|
||||
|
||||
US_LOGGING_DESTROY;
|
||||
}
|
||||
|
||||
static void _plugin_create_session(janus_plugin_session *session, int *err) {
|
||||
_IF_DISABLED({ *err = -1; return; });
|
||||
_LOCK_ALL;
|
||||
US_JLOG_INFO("main", "Creating session %p ...", session);
|
||||
US_LOG_INFO("Creating session %p ...", session);
|
||||
us_janus_client_s *const client = us_janus_client_init(_g_gw, session);
|
||||
US_LIST_APPEND(_g_clients, client);
|
||||
atomic_store(&_g_has_watchers, true);
|
||||
@@ -496,7 +471,7 @@ static void _plugin_destroy_session(janus_plugin_session* session, int *err) {
|
||||
bool has_speakers = false;
|
||||
US_LIST_ITERATE(_g_clients, client, {
|
||||
if (client->session == session) {
|
||||
US_JLOG_INFO("main", "Removing session %p ...", session);
|
||||
US_LOG_INFO("Removing session %p ...", session);
|
||||
US_LIST_REMOVE(_g_clients, client);
|
||||
us_janus_client_destroy(client);
|
||||
found = true;
|
||||
@@ -507,7 +482,7 @@ static void _plugin_destroy_session(janus_plugin_session* session, int *err) {
|
||||
}
|
||||
});
|
||||
if (!found) {
|
||||
US_JLOG_WARN("main", "No session %p", session);
|
||||
US_LOG_ERROR("No session %p", session);
|
||||
*err = -2;
|
||||
}
|
||||
atomic_store(&_g_has_watchers, has_watchers);
|
||||
@@ -539,13 +514,13 @@ static void _set_transmit(janus_plugin_session *session, const char *msg, bool t
|
||||
US_LIST_ITERATE(_g_clients, client, {
|
||||
if (client->session == session) {
|
||||
atomic_store(&client->transmit, transmit);
|
||||
// US_JLOG_INFO("main", "%s session %p", msg, session);
|
||||
// US_LOG_INFO("%s session %p", msg, session);
|
||||
found = true;
|
||||
}
|
||||
has_watchers = (has_watchers || atomic_load(&client->transmit));
|
||||
});
|
||||
if (!found) {
|
||||
US_JLOG_WARN("main", "No session %p", session);
|
||||
US_LOG_ERROR("No session %p", session);
|
||||
}
|
||||
atomic_store(&_g_has_watchers, has_watchers);
|
||||
_UNLOCK_ALL;
|
||||
@@ -567,7 +542,7 @@ static struct janus_plugin_result *_plugin_handle_message(
|
||||
}
|
||||
|
||||
# define PUSH_ERROR(x_error, x_reason) { \
|
||||
/*US_JLOG_ERROR("main", "Message error in session %p: %s", session, x_reason);*/ \
|
||||
/*US_LOG_ERROR("Message error in session %p: %s", session, x_reason);*/ \
|
||||
json_t *m_event = json_object(); \
|
||||
json_object_set_new(m_event, "ustreamer", json_string("event")); \
|
||||
json_object_set_new(m_event, "error_code", json_integer(x_error)); \
|
||||
@@ -587,7 +562,7 @@ static struct janus_plugin_result *_plugin_handle_message(
|
||||
PUSH_ERROR(400, "Request not a string");
|
||||
goto done;
|
||||
}
|
||||
// US_JLOG_INFO("main", "Message: %s", request_str);
|
||||
// US_LOG_INFO("Message: %s", request_str);
|
||||
|
||||
# define PUSH_STATUS(x_status, x_payload, x_jsep) { \
|
||||
json_t *const m_event = json_object(); \
|
||||
@@ -612,21 +587,20 @@ static struct janus_plugin_result *_plugin_handle_message(
|
||||
uint video_orient = 0;
|
||||
bool with_acap = false;
|
||||
bool with_aplay = false;
|
||||
bool with_vplay = false;
|
||||
{
|
||||
json_t *const params = json_object_get(msg, "params");
|
||||
if (params != NULL) {
|
||||
{
|
||||
json_t *const obj = json_object_get(params, "audio");
|
||||
if (obj != NULL && json_is_boolean(obj)) {
|
||||
with_acap = (us_au_probe(_g_config->acap_dev_name) && json_boolean_value(obj));
|
||||
# define READ_BOOL(x_target, x_key, x_cond) { \
|
||||
json_t *const m_obj = json_object_get(params, x_key); \
|
||||
if (m_obj != NULL && json_is_boolean(m_obj)) { \
|
||||
x_target = (json_boolean_value(m_obj) && (x_cond)); \
|
||||
} \
|
||||
}
|
||||
}
|
||||
{
|
||||
json_t *const obj = json_object_get(params, "mic");
|
||||
if (obj != NULL && json_is_boolean(obj)) {
|
||||
with_aplay = (us_au_probe(_g_config->aplay_dev_name) && json_boolean_value(obj));
|
||||
}
|
||||
}
|
||||
READ_BOOL(with_acap, "audio", us_au_probe(_g_config->acap_dev_name));
|
||||
READ_BOOL(with_aplay, "mic", us_au_probe(_g_config->aplay_dev_name));
|
||||
READ_BOOL(with_vplay, "camera", (_g_config->vplay_sink_name != NULL));
|
||||
# undef READ_BOOL
|
||||
{
|
||||
json_t *const obj = json_object_get(params, "orientation");
|
||||
if (obj != NULL && json_is_integer(obj)) {
|
||||
@@ -640,41 +614,23 @@ static struct janus_plugin_result *_plugin_handle_message(
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
char *sdp;
|
||||
char *const video_sdp = us_rtpv_make_sdp(_g_rtpv);
|
||||
char *const audio_sdp = (with_acap ? us_rtpa_make_sdp(_g_rtpa, with_aplay) : us_strdup(""));
|
||||
US_ASPRINTF(sdp,
|
||||
"v=0" RN
|
||||
"o=- %" PRIu64 " 1 IN IP4 0.0.0.0" RN
|
||||
"s=PiKVM uStreamer" RN
|
||||
"t=0 0" RN
|
||||
"%s%s",
|
||||
us_get_now_id() >> 1,
|
||||
# if JANUS_PLUGIN_API_VERSION >= 100
|
||||
// Place video SDP before audio SDP so that the video and audio streams
|
||||
// have predictable indices, even if audio is not available.
|
||||
// See also client.c.
|
||||
video_sdp, audio_sdp
|
||||
# else
|
||||
// For versions of Janus prior to 1.x, place the audio SDP first.
|
||||
audio_sdp, video_sdp
|
||||
# endif
|
||||
);
|
||||
json_t *const offer_jsep = json_pack("{ssss}", "type", "offer", "sdp", sdp);
|
||||
PUSH_STATUS("started", NULL, offer_jsep);
|
||||
json_decref(offer_jsep);
|
||||
free(audio_sdp);
|
||||
free(video_sdp);
|
||||
free(sdp);
|
||||
}
|
||||
|
||||
{
|
||||
_LOCK_ALL;
|
||||
bool has_listeners = false;
|
||||
bool has_speakers = false;
|
||||
US_LIST_ITERATE(_g_clients, client, {
|
||||
if (client->session == session) {
|
||||
char *const sdp = us_sdp_create(
|
||||
client->video_ssrc,
|
||||
client->audio_ssrc,
|
||||
with_acap,
|
||||
with_aplay,
|
||||
with_vplay);
|
||||
json_t *const offer_jsep = json_pack("{ssss}", "type", "offer", "sdp", sdp);
|
||||
PUSH_STATUS("started", NULL, offer_jsep);
|
||||
json_decref(offer_jsep);
|
||||
free(sdp);
|
||||
|
||||
atomic_store(&client->transmit_acap, with_acap);
|
||||
atomic_store(&client->transmit_aplay, with_aplay);
|
||||
atomic_store(&client->video_orient, video_orient);
|
||||
@@ -689,18 +645,17 @@ static struct janus_plugin_result *_plugin_handle_message(
|
||||
|
||||
} else if (!strcmp(request_str, "features")) {
|
||||
const char *const ice_url = getenv("JANUS_USTREAMER_WEB_ICE_URL");
|
||||
const bool acap_avail = us_au_probe(_g_config->acap_dev_name);
|
||||
json_t *const features = json_pack(
|
||||
"{s:b, s:b, s:{s:s?}}",
|
||||
"audio", acap_avail,
|
||||
"mic", (acap_avail && us_au_probe(_g_config->aplay_dev_name)),
|
||||
"audio", us_au_probe(_g_config->acap_dev_name),
|
||||
"mic", us_au_probe(_g_config->aplay_dev_name),
|
||||
"ice", "url", (ice_url != NULL ? ice_url : default_ice_url)
|
||||
);
|
||||
PUSH_STATUS("features", features, NULL);
|
||||
json_decref(features);
|
||||
|
||||
} else if (!strcmp(request_str, "key_required")) {
|
||||
// US_JLOG_INFO("main", "Got key_required message");
|
||||
// US_LOG_INFO("Got key_required message");
|
||||
atomic_store(&_g_key_required, true);
|
||||
|
||||
} else {
|
||||
@@ -740,8 +695,10 @@ static void _plugin_incoming_rtcp(janus_plugin_session *session, janus_plugin_rt
|
||||
if (session == NULL || packet == NULL || !packet->video) {
|
||||
return; // Accept only valid video
|
||||
}
|
||||
if (janus_rtcp_has_pli(packet->buffer, packet->length)) {
|
||||
// US_JLOG_INFO("main", "Got video PLI");
|
||||
if (
|
||||
janus_rtcp_has_pli(packet->buffer, packet->length)
|
||||
|| janus_rtcp_has_fir(packet->buffer, packet->length)
|
||||
) {
|
||||
atomic_store(&_g_key_required, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,22 +44,27 @@ void us_rtp_destroy(us_rtp_s *rtp) {
|
||||
void us_rtp_assign(us_rtp_s *rtp, uint payload, bool video) {
|
||||
rtp->payload = payload;
|
||||
rtp->video = video;
|
||||
rtp->ssrc = us_triple_u32(us_get_now_monotonic_u64());
|
||||
}
|
||||
|
||||
void us_rtp_write_header(us_rtp_s *rtp, u32 pts, bool marked) {
|
||||
#define WRITE_BE_U32(x_offset, x_value) \
|
||||
*((u32*)(rtp->datagram + x_offset)) = __builtin_bswap32(x_value)
|
||||
|
||||
void us_rtp_write_header(us_rtp_s *rtp, u32 pts, bool last_header) {
|
||||
u32 word0 = 0x80000000;
|
||||
if (marked) {
|
||||
if (last_header) {
|
||||
word0 |= 1 << 23;
|
||||
}
|
||||
word0 |= (rtp->payload & 0x7F) << 16;
|
||||
word0 |= rtp->seq;
|
||||
++rtp->seq;
|
||||
|
||||
# define WRITE_BE_U32(x_offset, x_value) \
|
||||
*((u32*)(rtp->datagram + x_offset)) = __builtin_bswap32(x_value)
|
||||
WRITE_BE_U32(0, word0);
|
||||
WRITE_BE_U32(4, pts);
|
||||
WRITE_BE_U32(8, rtp->ssrc);
|
||||
# undef WRITE_BE_U32
|
||||
WRITE_BE_U32(8, 0); // SSRC should be written separately for each client
|
||||
}
|
||||
|
||||
void us_rtp_write_ssrc(us_rtp_s *rtp, u32 ssrc) {
|
||||
WRITE_BE_U32(8, ssrc);
|
||||
}
|
||||
|
||||
#undef WRITE_BE_U32
|
||||
|
||||
@@ -23,12 +23,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "uslibs/types.h"
|
||||
#include "uslibs/tools.h"
|
||||
|
||||
|
||||
// https://stackoverflow.com/questions/47635545/why-webrtc-chose-rtp-max-packet-size-to-1200-bytes
|
||||
#define US_RTP_DATAGRAM_SIZE 1200
|
||||
// Max RTP size for WebRTC is 1200 bytes:
|
||||
// - https://stackoverflow.com/questions/47635545/why-webrtc-chose-rtp-max-packet-size-to-1200-bytes
|
||||
// But(!) Tailscale has 1280 MTU. So to fit it required to subtract:
|
||||
// 1. possible RTP extensions (see sdp.c)
|
||||
// 2. additional SRTP fields (>= 10 bytes)
|
||||
// 3. UDP header (8 bytes)
|
||||
// 4. IPv6 header (40 bytes)
|
||||
// Finally it looks like below value should be good enought for all cases
|
||||
#define US_RTP_TOTAL_SIZE 1150
|
||||
#define US_RTP_HEADER_SIZE 12
|
||||
#define US_RTP_PAYLOAD_SIZE (US_RTP_DATAGRAM_SIZE - US_RTP_HEADER_SIZE)
|
||||
#define US_RTP_PAYLOAD_SIZE (US_RTP_TOTAL_SIZE - US_RTP_HEADER_SIZE)
|
||||
|
||||
#define US_RTP_H264_PAYLOAD 96
|
||||
#define US_RTP_OPUS_PAYLOAD 111
|
||||
@@ -40,19 +48,27 @@
|
||||
typedef struct {
|
||||
uint payload;
|
||||
bool video;
|
||||
u32 ssrc;
|
||||
|
||||
u16 seq;
|
||||
u8 datagram[US_RTP_DATAGRAM_SIZE];
|
||||
u8 datagram[US_RTP_TOTAL_SIZE];
|
||||
uz used;
|
||||
|
||||
bool first_of_frame;
|
||||
bool last_of_frame;
|
||||
bool zero_playout_delay;
|
||||
u64 grab_ntp_ts;
|
||||
} us_rtp_s;
|
||||
|
||||
typedef void (*us_rtp_callback_f)(const us_rtp_s *rtp);
|
||||
|
||||
|
||||
static inline u32 us_get_ssrc(void) {
|
||||
return us_triple_u32(us_get_now_monotonic_u64());
|
||||
}
|
||||
|
||||
us_rtp_s *us_rtp_init(void);
|
||||
void us_rtp_destroy(us_rtp_s *rtp);
|
||||
|
||||
void us_rtp_assign(us_rtp_s *rtp, uint payload, bool video);
|
||||
void us_rtp_write_header(us_rtp_s *rtp, u32 pts, bool marked);
|
||||
void us_rtp_write_header(us_rtp_s *rtp, u32 pts, bool last_header);
|
||||
void us_rtp_write_ssrc(us_rtp_s *rtp, u32 ssrc);
|
||||
|
||||
@@ -23,11 +23,12 @@
|
||||
#include "rtpa.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "uslibs/types.h"
|
||||
#include "uslibs/tools.h"
|
||||
|
||||
#include "rtp.h"
|
||||
|
||||
|
||||
us_rtpa_s *us_rtpa_init(us_rtp_callback_f callback) {
|
||||
us_rtpa_s *rtpa;
|
||||
@@ -43,32 +44,8 @@ void us_rtpa_destroy(us_rtpa_s *rtpa) {
|
||||
free(rtpa);
|
||||
}
|
||||
|
||||
char *us_rtpa_make_sdp(us_rtpa_s *rtpa, bool mic) {
|
||||
const uint pl = rtpa->rtp->payload;
|
||||
char *sdp;
|
||||
US_ASPRINTF(sdp,
|
||||
"m=audio 1 RTP/SAVPF %u" RN
|
||||
"c=IN IP4 0.0.0.0" RN
|
||||
"a=rtpmap:%u OPUS/%u/%u" RN
|
||||
"a=fmtp:%u sprop-stereo=1" RN // useinbandfec=1
|
||||
"a=rtcp-fb:%u nack" RN
|
||||
"a=rtcp-fb:%u nack pli" RN
|
||||
"a=rtcp-fb:%u goog-remb" RN
|
||||
"a=mid:a" RN
|
||||
"a=msid:audio a" RN
|
||||
"a=ssrc:%" PRIu32 " cname:ustreamer" RN
|
||||
"a=%s" RN,
|
||||
pl, pl,
|
||||
US_RTP_OPUS_HZ, US_RTP_OPUS_CH,
|
||||
pl, pl, pl, pl,
|
||||
rtpa->rtp->ssrc,
|
||||
(mic ? "sendrecv" : "sendonly")
|
||||
);
|
||||
return sdp;
|
||||
}
|
||||
|
||||
void us_rtpa_wrap(us_rtpa_s *rtpa, const u8 *data, uz size, u32 pts) {
|
||||
if (size + US_RTP_HEADER_SIZE <= US_RTP_DATAGRAM_SIZE) {
|
||||
if (size + US_RTP_HEADER_SIZE <= US_RTP_TOTAL_SIZE) {
|
||||
us_rtp_write_header(rtpa->rtp, pts, false);
|
||||
memcpy(rtpa->rtp->datagram + US_RTP_HEADER_SIZE, data, size);
|
||||
rtpa->rtp->used = size + US_RTP_HEADER_SIZE;
|
||||
|
||||
@@ -36,5 +36,4 @@ typedef struct {
|
||||
us_rtpa_s *us_rtpa_init(us_rtp_callback_f callback);
|
||||
void us_rtpa_destroy(us_rtpa_s *rtpa);
|
||||
|
||||
char *us_rtpa_make_sdp(us_rtpa_s *rtpa, bool mic);
|
||||
void us_rtpa_wrap(us_rtpa_s *rtpa, const u8 *data, uz size, u32 pts);
|
||||
|
||||
196
janus/src/rtpv.c
196
janus/src/rtpv.c
@@ -26,8 +26,6 @@
|
||||
#include "rtpv.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
@@ -35,10 +33,15 @@
|
||||
#include "uslibs/tools.h"
|
||||
#include "uslibs/frame.h"
|
||||
|
||||
#include "rtp.h"
|
||||
|
||||
void _rtpv_process_nalu(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool marked);
|
||||
|
||||
static sz _find_annexb(const u8 *data, uz size);
|
||||
static bool _is_nalu_small(uz size);
|
||||
|
||||
static void _rtpv_process_nalu(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool first_nalu, bool last_nalu);
|
||||
static void _rtpv_process_nalu_small(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool first_nalu, bool last_nalu);
|
||||
static void _rtpv_process_nalu_big(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool first_nalu, bool last_nalu);
|
||||
|
||||
|
||||
us_rtpv_s *us_rtpv_init(us_rtp_callback_f callback) {
|
||||
@@ -55,85 +58,112 @@ void us_rtpv_destroy(us_rtpv_s *rtpv) {
|
||||
free(rtpv);
|
||||
}
|
||||
|
||||
char *us_rtpv_make_sdp(us_rtpv_s *rtpv) {
|
||||
// https://tools.ietf.org/html/rfc6184
|
||||
// https://github.com/meetecho/janus-gateway/issues/2443
|
||||
const uint pl = rtpv->rtp->payload;
|
||||
char *sdp;
|
||||
US_ASPRINTF(sdp,
|
||||
"m=video 1 RTP/SAVPF %u" RN
|
||||
"c=IN IP4 0.0.0.0" RN
|
||||
"a=rtpmap:%u H264/90000" RN
|
||||
"a=fmtp:%u profile-level-id=42E01F" RN
|
||||
"a=fmtp:%u packetization-mode=1" RN
|
||||
"a=rtcp-fb:%u nack" RN
|
||||
"a=rtcp-fb:%u nack pli" RN
|
||||
"a=rtcp-fb:%u goog-remb" RN
|
||||
"a=mid:v" RN
|
||||
"a=msid:video v" RN
|
||||
"a=ssrc:%" PRIu32 " cname:ustreamer" RN
|
||||
"a=extmap:1 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay" RN
|
||||
"a=extmap:2 urn:3gpp:video-orientation" RN
|
||||
"a=sendonly" RN,
|
||||
pl, pl, pl, pl,
|
||||
pl, pl, pl,
|
||||
rtpv->rtp->ssrc
|
||||
);
|
||||
return sdp;
|
||||
}
|
||||
|
||||
#define _PRE 3 // Annex B prefix length
|
||||
|
||||
void us_rtpv_wrap(us_rtpv_s *rtpv, const us_frame_s *frame, bool zero_playout_delay) {
|
||||
// There is a complicated logic here but everything works as it should:
|
||||
// - https://github.com/pikvm/ustreamer/issues/115#issuecomment-893071775
|
||||
US_A(frame->format == V4L2_PIX_FMT_H264);
|
||||
|
||||
assert(frame->format == V4L2_PIX_FMT_H264);
|
||||
|
||||
rtpv->rtp->zero_playout_delay = zero_playout_delay;
|
||||
|
||||
const u32 pts = us_get_now_monotonic_u64() * 9 / 100; // PTS units are in 90 kHz
|
||||
sz last_offset = -_PRE;
|
||||
|
||||
while (true) { // Find and iterate by nalus
|
||||
const uz next_start = last_offset + _PRE;
|
||||
sz offset = _find_annexb(frame->data + next_start, frame->used - next_start);
|
||||
if (offset < 0) {
|
||||
break;
|
||||
}
|
||||
offset += next_start;
|
||||
|
||||
if (last_offset >= 0) {
|
||||
const u8 *const data = frame->data + last_offset + _PRE;
|
||||
uz size = offset - last_offset - _PRE;
|
||||
if (data[size - 1] == 0) { // Check for extra 00
|
||||
--size;
|
||||
}
|
||||
_rtpv_process_nalu(rtpv, data, size, pts, false);
|
||||
}
|
||||
|
||||
last_offset = offset;
|
||||
if (frame->used <= _PRE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (last_offset >= 0) {
|
||||
const u8 *const data = frame->data + last_offset + _PRE;
|
||||
uz size = frame->used - last_offset - _PRE;
|
||||
_rtpv_process_nalu(rtpv, data, size, pts, true);
|
||||
rtpv->rtp->zero_playout_delay = zero_playout_delay;
|
||||
rtpv->rtp->grab_ntp_ts = us_get_now_ntp() - us_ld_to_ntp(us_get_now_monotonic() - frame->grab_begin_ts);
|
||||
|
||||
const u32 pts = us_get_now_monotonic_u64() * 9 / 100; // PTS units are in 90 kHz
|
||||
|
||||
// Bytestream can be like:
|
||||
// [00-]00-00-01 NALU [00-]00-00-01 NALU
|
||||
// ... with optional 00 prefix for _PRE.
|
||||
// We need to iterate by NALUs only and throw away separators.
|
||||
|
||||
const sz offset_to_first = _find_annexb(frame->data, frame->used);
|
||||
if (offset_to_first < 0) {
|
||||
return;
|
||||
}
|
||||
uz begin = offset_to_first + _PRE;
|
||||
|
||||
bool first_nalu = true;
|
||||
while (begin < frame->used) { // Find and iterate by NALUs
|
||||
const u8 *const data = frame->data + begin;
|
||||
const sz offset_to_next = _find_annexb(data, frame->used - begin);
|
||||
|
||||
if (offset_to_next >= 0) { // Process NALUs between prefixes
|
||||
uz size = offset_to_next;
|
||||
if (size > 1 && data[size - 1] == 0) { // Check for extra trailing zero
|
||||
--size;
|
||||
}
|
||||
if (size > 1) { // Skip too short NALUs
|
||||
_rtpv_process_nalu(rtpv, data, size, pts, first_nalu, false);
|
||||
first_nalu = false;
|
||||
}
|
||||
begin += offset_to_next + _PRE;
|
||||
|
||||
} else { // Process the tail
|
||||
const uz size = frame->used - begin;
|
||||
if (size > 1) {
|
||||
_rtpv_process_nalu(rtpv, data, size, pts, first_nalu, true);
|
||||
first_nalu = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _rtpv_process_nalu(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool marked) {
|
||||
static sz _find_annexb(const u8 *data, uz size) {
|
||||
// Parses buffer for 00 00 01 start codes
|
||||
if (size >= _PRE) {
|
||||
for (uz i = 0; i <= size - _PRE; ++i) {
|
||||
if (
|
||||
data[i] == 0
|
||||
&& data[i + 1] == 0
|
||||
&& data[i + 2] == 1
|
||||
) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#undef _PRE
|
||||
|
||||
static bool _is_nalu_small(uz size) {
|
||||
return (size + US_RTP_HEADER_SIZE <= US_RTP_TOTAL_SIZE);
|
||||
}
|
||||
|
||||
static void _rtpv_process_nalu(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool first_nalu, bool last_nalu) {
|
||||
US_A(size > 1);
|
||||
if (_is_nalu_small(size)) {
|
||||
_rtpv_process_nalu_small(rtpv, data, size, pts, first_nalu, last_nalu);
|
||||
} else {
|
||||
_rtpv_process_nalu_big(rtpv, data, size, pts, first_nalu, last_nalu);
|
||||
}
|
||||
}
|
||||
|
||||
static void _rtpv_process_nalu_small(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool first_nalu, bool last_nalu) {
|
||||
US_A(size > 1);
|
||||
US_A(_is_nalu_small(size));
|
||||
|
||||
us_rtp_s *rtp = rtpv->rtp;
|
||||
u8 *dg = rtp->datagram;
|
||||
|
||||
us_rtp_write_header(rtp, pts, last_nalu);
|
||||
memcpy(dg + US_RTP_HEADER_SIZE, data, size);
|
||||
rtp->used = size + US_RTP_HEADER_SIZE;
|
||||
rtp->first_of_frame = first_nalu;
|
||||
rtp->last_of_frame = last_nalu;
|
||||
rtpv->callback(rtp);
|
||||
}
|
||||
|
||||
static void _rtpv_process_nalu_big(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool first_nalu, bool last_nalu) {
|
||||
US_A(size > 1);
|
||||
US_A(!_is_nalu_small(size));
|
||||
|
||||
const uint ref_idc = (data[0] >> 5) & 3;
|
||||
const uint type = data[0] & 0x1F;
|
||||
u8 *dg = rtpv->rtp->datagram;
|
||||
|
||||
if (size + US_RTP_HEADER_SIZE <= US_RTP_DATAGRAM_SIZE) {
|
||||
us_rtp_write_header(rtpv->rtp, pts, marked);
|
||||
memcpy(dg + US_RTP_HEADER_SIZE, data, size);
|
||||
rtpv->rtp->used = size + US_RTP_HEADER_SIZE;
|
||||
rtpv->callback(rtpv->rtp);
|
||||
return;
|
||||
}
|
||||
us_rtp_s *rtp = rtpv->rtp;
|
||||
u8 *dg = rtp->datagram;
|
||||
|
||||
const uz fu_overhead = US_RTP_HEADER_SIZE + 2; // FU-A overhead
|
||||
|
||||
@@ -142,13 +172,13 @@ void _rtpv_process_nalu(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool
|
||||
|
||||
bool first = true;
|
||||
while (remaining > 0) {
|
||||
sz frag_size = US_RTP_DATAGRAM_SIZE - fu_overhead;
|
||||
sz frag_size = US_RTP_TOTAL_SIZE - fu_overhead;
|
||||
const bool last = (remaining <= frag_size);
|
||||
if (last) {
|
||||
frag_size = remaining;
|
||||
}
|
||||
|
||||
us_rtp_write_header(rtpv->rtp, pts, (marked && last));
|
||||
us_rtp_write_header(rtp, pts, (last_nalu && last));
|
||||
|
||||
dg[US_RTP_HEADER_SIZE] = 28 | (ref_idc << 5);
|
||||
|
||||
@@ -162,25 +192,13 @@ void _rtpv_process_nalu(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool
|
||||
dg[US_RTP_HEADER_SIZE + 1] = fu;
|
||||
|
||||
memcpy(dg + fu_overhead, src, frag_size);
|
||||
rtpv->rtp->used = fu_overhead + frag_size;
|
||||
rtpv->callback(rtpv->rtp);
|
||||
rtp->used = fu_overhead + frag_size;
|
||||
rtp->first_of_frame = (first_nalu && first);
|
||||
rtp->last_of_frame = (last_nalu && last);
|
||||
rtpv->callback(rtp);
|
||||
|
||||
src += frag_size;
|
||||
remaining -= frag_size;
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
static sz _find_annexb(const u8 *data, uz size) {
|
||||
// Parses buffer for 00 00 01 start codes
|
||||
if (size >= _PRE) {
|
||||
for (uz index = 0; index <= size - _PRE; ++index) {
|
||||
if (data[index] == 0 && data[index + 1] == 0 && data[index + 2] == 1) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#undef _PRE
|
||||
|
||||
@@ -37,5 +37,4 @@ typedef struct {
|
||||
us_rtpv_s *us_rtpv_init(us_rtp_callback_f callback);
|
||||
void us_rtpv_destroy(us_rtpv_s *rtpv);
|
||||
|
||||
char *us_rtpv_make_sdp(us_rtpv_s *rtpv);
|
||||
void us_rtpv_wrap(us_rtpv_s *rtpv, const us_frame_s *frame, bool zero_playout_delay);
|
||||
|
||||
119
janus/src/sdp.c
Normal file
119
janus/src/sdp.c
Normal file
@@ -0,0 +1,119 @@
|
||||
/*****************************************************************************
|
||||
# #
|
||||
# uStreamer - Lightweight and fast MJPEG-HTTP streamer. #
|
||||
# #
|
||||
# Copyright (C) 2018-2024 Maxim Devaev <mdevaev@gmail.com> #
|
||||
# #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#include "sdp.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <janus/plugins/plugin.h>
|
||||
|
||||
#include "uslibs/types.h"
|
||||
#include "uslibs/tools.h"
|
||||
|
||||
#include "rtp.h"
|
||||
#include "rtpv.h"
|
||||
#include "rtpa.h"
|
||||
|
||||
|
||||
char *us_sdp_create(u32 video_ssrc, u32 audio_ssrc, bool acap, bool aplay, bool vplay) {
|
||||
char *video_sdp;
|
||||
{
|
||||
// https://tools.ietf.org/html/rfc6184
|
||||
// https://github.com/meetecho/janus-gateway/issues/2443
|
||||
const uint pl = US_RTP_H264_PAYLOAD;
|
||||
US_ASPRINTF(
|
||||
video_sdp,
|
||||
"m=video 1 RTP/SAVPF %u" RN
|
||||
"c=IN IP4 0.0.0.0" RN
|
||||
"a=rtpmap:%u H264/90000" RN
|
||||
"a=fmtp:%u profile-level-id=42E01F;packetization-mode=1" RN
|
||||
"a=rtcp-fb:%u ccm fir" RN
|
||||
"a=rtcp-fb:%u nack" RN
|
||||
"a=rtcp-fb:%u nack pli" RN
|
||||
"a=rtcp-fb:%u goog-remb" RN
|
||||
"a=mid:v" RN
|
||||
"a=msid:video v" RN
|
||||
"a=ssrc:%" PRIu32 " cname:ustreamer" RN
|
||||
"a=extmap:1/sendonly urn:3gpp:video-orientation" RN
|
||||
"a=extmap:2/sendonly http://www.webrtc.org/experiments/rtp-hdrext/playout-delay" RN
|
||||
"a=extmap:3/sendonly http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time" RN
|
||||
"a=%s" RN,
|
||||
pl, pl, pl, pl, pl, pl, pl,
|
||||
video_ssrc,
|
||||
(vplay ? "sendrecv" : "sendonly"));
|
||||
}
|
||||
|
||||
char *audio_sdp;
|
||||
if (acap || aplay) {
|
||||
const uint pl = US_RTP_OPUS_PAYLOAD;
|
||||
|
||||
char *dir = "";
|
||||
if (acap && aplay) {
|
||||
dir = "sendrecv";
|
||||
} else if (acap && !aplay) {
|
||||
dir = "sendonly";
|
||||
} else if (!acap && aplay) {
|
||||
dir = "recvonly";
|
||||
}
|
||||
|
||||
US_ASPRINTF(
|
||||
audio_sdp,
|
||||
"m=audio 1 RTP/SAVPF %u" RN
|
||||
"c=IN IP4 0.0.0.0" RN
|
||||
"a=rtpmap:%u OPUS/%u/%u" RN
|
||||
"a=fmtp:%u sprop-stereo=1" RN // useinbandfec=1
|
||||
"a=mid:a" RN
|
||||
"a=msid:audio a" RN
|
||||
"a=ssrc:%" PRIu32 " cname:ustreamer" RN
|
||||
"a=%s" RN,
|
||||
pl, pl,
|
||||
US_RTP_OPUS_HZ, US_RTP_OPUS_CH,
|
||||
pl,
|
||||
audio_ssrc,
|
||||
dir);
|
||||
} else {
|
||||
audio_sdp = us_strdup("");
|
||||
}
|
||||
|
||||
char *sdp;
|
||||
US_ASPRINTF(sdp,
|
||||
"v=0" RN
|
||||
"o=- %" PRIu64 " 1 IN IP4 0.0.0.0" RN
|
||||
"s=PiKVM uStreamer" RN
|
||||
"t=0 0" RN
|
||||
"%s%s",
|
||||
us_get_now_id() >> 1,
|
||||
# if JANUS_PLUGIN_API_VERSION >= 100
|
||||
// Place video SDP before audio SDP so that the video and audio streams
|
||||
// have predictable indices, even if audio is not available.
|
||||
// See also client.c.
|
||||
video_sdp, audio_sdp
|
||||
# else
|
||||
// For versions of Janus prior to 1.x, place the audio SDP first.
|
||||
audio_sdp, video_sdp
|
||||
# endif
|
||||
);
|
||||
|
||||
free(audio_sdp);
|
||||
free(video_sdp);
|
||||
return sdp;
|
||||
}
|
||||
@@ -23,9 +23,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "uslibs/types.h"
|
||||
#include "uslibs/frame.h"
|
||||
#include "uslibs/memsinksh.h"
|
||||
|
||||
|
||||
int us_memsink_fd_wait_frame(int fd, us_memsink_shared_s *mem, u64 last_id);
|
||||
int us_memsink_fd_get_frame(int fd, us_memsink_shared_s *mem, us_frame_s *frame, u64 *frame_id, bool key_required);
|
||||
char *us_sdp_create(u32 video_ssrc, u32 audio_ssrc, bool acap, bool aplay, bool vplay);
|
||||
1
janus/src/uslibs/logging.c
Symbolic link
1
janus/src/uslibs/logging.c
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../src/libs/logging.c
|
||||
1
janus/src/uslibs/logging.h
Symbolic link
1
janus/src/uslibs/logging.h
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../src/libs/logging.h
|
||||
@@ -22,22 +22,11 @@
|
||||
|
||||
#include <janus/plugins/plugin.h>
|
||||
|
||||
#include "uslibs/tools.h"
|
||||
|
||||
#include "const.h"
|
||||
#include "threading.h"
|
||||
|
||||
|
||||
#define US_JLOG_INFO(x_prefix, x_msg, ...) JANUS_LOG(LOG_INFO, "== %s/%-9s -- " x_msg "\n", US_PLUGIN_NAME, x_prefix, ##__VA_ARGS__)
|
||||
#define US_JLOG_WARN(x_prefix, x_msg, ...) JANUS_LOG(LOG_WARN, "== %s/%-9s -- " x_msg "\n", US_PLUGIN_NAME, x_prefix, ##__VA_ARGS__)
|
||||
#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 *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); \
|
||||
#define US_LOG_PRINTF_NOLOCK(x_label_color, x_label, x_msg_color, x_msg, ...) { \
|
||||
char m_tname_buf[US_THREAD_NAME_SIZE] = {0}; \
|
||||
us_thread_get_name(m_tname_buf); \
|
||||
JANUS_LOG(LOG_INFO, "== ustreamer/%-15s " x_label " -- " x_msg "\n", m_tname_buf, ##__VA_ARGS__); \
|
||||
}
|
||||
|
||||
// We don't include alsa, speex and opus headers here
|
||||
#define US_JLOG_PERROR_ALSA(_err, _prefix, _msg, ...) US_JLOG_ERROR(_prefix, _msg ": %s", ##__VA_ARGS__, snd_strerror(_err))
|
||||
#define US_JLOG_PERROR_RES(_err, _prefix, _msg, ...) US_JLOG_ERROR(_prefix, _msg ": %s", ##__VA_ARGS__, speex_resampler_strerror(_err))
|
||||
#define US_JLOG_PERROR_OPUS(_err, _prefix, _msg, ...) US_JLOG_ERROR(_prefix, _msg ": %s", ##__VA_ARGS__, opus_strerror(_err))
|
||||
1
janus/src/uslibs/memsink.c
Symbolic link
1
janus/src/uslibs/memsink.c
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../src/libs/memsink.c
|
||||
1
janus/src/uslibs/memsink.h
Symbolic link
1
janus/src/uslibs/memsink.h
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../src/libs/memsink.h
|
||||
@@ -9,7 +9,7 @@ RUN pacman -Syu --noconfirm \
|
||||
&& pacman -S --needed --noconfirm \
|
||||
vim \
|
||||
git \
|
||||
libjpeg \
|
||||
libjpeg-turbo \
|
||||
libevent \
|
||||
libutil-linux \
|
||||
libbsd \
|
||||
|
||||
@@ -15,8 +15,6 @@ commands = cppcheck \
|
||||
--quiet \
|
||||
--check-level=exhaustive \
|
||||
--enable=warning,portability,performance,style \
|
||||
--suppress=assignmentInAssert \
|
||||
--suppress=assertWithSideEffect \
|
||||
--suppress=variableScope \
|
||||
--inline-suppr \
|
||||
--library=python \
|
||||
@@ -39,7 +37,7 @@ deps =
|
||||
|
||||
[testenv:mypy]
|
||||
allowlist_externals = bash
|
||||
commands = bash -c 'mypy --config-file=linters/mypy.ini tools/*.py python/*.py'
|
||||
commands = bash -c 'mypy --config-file=linters/mypy.ini --cache-dir=/dev/null tools/*.py python/*.py'
|
||||
deps =
|
||||
mypy
|
||||
|
||||
|
||||
@@ -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 6.47" "January 2021"
|
||||
.TH USTREAMER-DUMP 1 "version 6.61" "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 6.47" "November 2020"
|
||||
.TH USTREAMER 1 "version 6.61" "November 2020"
|
||||
|
||||
.SH NAME
|
||||
ustreamer \- stream MJPEG video from any V4L2 device to the network
|
||||
@@ -23,7 +23,7 @@ For example, the recommended way of running µStreamer with TC358743-based captu
|
||||
.RS
|
||||
\fB\-\-format=uyvy \e\fR # Device input format
|
||||
.nf
|
||||
\fB\-\-encoder=m2m-image \e\fR # Hardware encoding with V4L2 M2M intraface
|
||||
\fB\-\-encoder=m2m-image \e\fR # Hardware encoding with V4L2 M2M interface
|
||||
.nf
|
||||
\fB\-\-workers=3 \e\fR # Maximum workers for V4L2 encoder
|
||||
.nf
|
||||
@@ -66,7 +66,7 @@ Available: MMAP, USERPTR; default: MMAP.
|
||||
Desired FPS. Default: maximum possible.
|
||||
.TP
|
||||
.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 than this limit. Useful if the device produces small\-sized garbage frames. Default: 128 bytes.
|
||||
.TP
|
||||
.BR \-T ", " \-\-allow\-truncated\-frames
|
||||
Allows to handle truncated frames. Useful if the device produces incorrect but still acceptable frames. Default: disabled.
|
||||
@@ -78,7 +78,7 @@ Suppress repetitive signal source errors. Default: disabled.
|
||||
Enable DV-timings querying and events processing to automatic resolution change. Default: disabled.
|
||||
.TP
|
||||
.BR \-b\ \fIN ", " \-\-buffers\ \fIN
|
||||
The number of buffers to receive data from the device. Each buffer may processed using an independent thread.
|
||||
The number of buffers to receive data from the device. Each buffer may be processed using an independent thread.
|
||||
Default: 2 (the number of CPU cores (but not more than 4) + 1).
|
||||
.TP
|
||||
.BR \-w\ \fIN ", " \-\-workers\ \fIN
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
|
||||
|
||||
pkgname=ustreamer
|
||||
pkgver=6.47
|
||||
pkgver=6.61
|
||||
pkgrel=1
|
||||
pkgdesc="Lightweight and fast MJPEG-HTTP streamer"
|
||||
url="https://github.com/pikvm/ustreamer"
|
||||
license=(GPL)
|
||||
arch=(i686 x86_64 armv6h armv7h aarch64)
|
||||
depends=(libjpeg libevent libbsd libgpiod systemd)
|
||||
depends=(libjpeg-turbo libevent libbsd libgpiod systemd)
|
||||
makedepends=(gcc make pkgconf systemd)
|
||||
source=(${pkgname}::"git+https://github.com/pikvm/ustreamer#commit=v${pkgver}")
|
||||
md5sums=(SKIP)
|
||||
@@ -19,7 +19,7 @@ _options="WITH_GPIO=1 WITH_SYSTEMD=1"
|
||||
if [ -e /usr/bin/python3 ]; then
|
||||
_options="$_options WITH_PYTHON=1"
|
||||
depends+=("python>=3.14" "python<3.15")
|
||||
makedepends+=(python-setuptools python-pip python-build python-wheel)
|
||||
makedepends+=(python-setuptools python-pip python-build python-wheel python-installer)
|
||||
fi
|
||||
if [ -e /usr/include/janus/plugins/plugin.h ];then
|
||||
depends+=(janus-gateway alsa-lib opus)
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
# This package is just an example. For OpenWRT it is recommended to use upstream package:
|
||||
# - https://github.com/openwrt/packages/tree/master/multimedia/ustreamer
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=ustreamer
|
||||
PKG_VERSION:=6.47
|
||||
PKG_VERSION:=6.61
|
||||
PKG_RELEASE:=1
|
||||
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
||||
|
||||
|
||||
@@ -9,13 +9,12 @@ all: root
|
||||
root: $(shell find src -type f,l) setup.py
|
||||
$(info == PY_BUILD ustreamer-*.so)
|
||||
rm -rf root
|
||||
$(ECHO) $(PY) -m build --skip-dependency-check --no-isolation
|
||||
$(ECHO) $(PY) -m pip install dist/*.whl --ignore-installed --root=./root
|
||||
$(ECHO) $(PY) -m build --wheel --skip-dependency-check --no-isolation
|
||||
|
||||
|
||||
install:
|
||||
$(PY) -m pip install dist/*.whl --ignore-installed --prefix=$(PREFIX) --root=$(if $(R_DESTDIR),$(R_DESTDIR),/)
|
||||
$(PY) -m installer --prefix=$(PREFIX) $(if $(R_DESTDIR),--destdir=$(R_DESTDIR),) dist/*.whl
|
||||
|
||||
|
||||
clean:
|
||||
rm -rf root dist ustreamer.egg-info
|
||||
rm -rf root dist build ustreamer.egg-info
|
||||
|
||||
@@ -34,7 +34,7 @@ def main() -> None:
|
||||
flags = _find_flags()
|
||||
setup(
|
||||
name="ustreamer",
|
||||
version="6.47",
|
||||
version="6.61",
|
||||
description="uStreamer tools",
|
||||
author="Maxim Devaev",
|
||||
author_email="mdevaev@gmail.com",
|
||||
|
||||
@@ -46,7 +46,7 @@ _OBJS = $(_USTR_SRCS:%.c=$(_BUILD)/%.o) $(_DUMP_SRCS:%.c=$(_BUILD)/%.o)
|
||||
|
||||
|
||||
# =====
|
||||
ifneq ($(shell sh -c 'uname 2>/dev/null || echo Unknown'),FreeBSD)
|
||||
ifeq ($(findstring bsd,$(shell $(CC) -dumpmachine)),)
|
||||
override _USTR_LDFLAGS += -latomic
|
||||
override _DUMP_LDFLAGS += -latomic
|
||||
override _V4P_LDFLAGS += -latomic
|
||||
@@ -78,7 +78,7 @@ endif
|
||||
|
||||
ifneq ($(MK_WITH_SETPROCTITLE),)
|
||||
override _CFLAGS += -DMK_WITH_SETPROCTITLE -DWITH_SETPROCTITLE
|
||||
ifeq ($(shell uname -s | tr A-Z a-z),linux)
|
||||
ifneq ($(findstring linux,$(shell $(CC) -dumpmachine)),)
|
||||
override _USTR_LDFLAGS += -lbsd
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <float.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "../libs/const.h"
|
||||
#include "../libs/errors.h"
|
||||
|
||||
@@ -22,14 +22,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include "tools.h"
|
||||
|
||||
|
||||
#define US_ARRAY_LEN(x_array) (sizeof(x_array) / sizeof((x_array)[0]))
|
||||
|
||||
#define US_ARRAY_ITERATE(x_array, x_start, x_item_ptr, ...) { \
|
||||
const int m_len = US_ARRAY_LEN(x_array); \
|
||||
assert(x_start <= m_len); \
|
||||
US_A(x_start <= m_len); \
|
||||
for (int m_i = x_start; m_i < m_len; ++m_i) { \
|
||||
__typeof__((x_array)[0]) *const x_item_ptr = &x_array[m_i]; \
|
||||
__VA_ARGS__ \
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/select.h>
|
||||
#include <sys/mman.h>
|
||||
@@ -471,7 +470,7 @@ int us_capture_hwbuf_grab(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);
|
||||
US_A(atomic_load(&hw->refs) == 0);
|
||||
const uint i = hw->buf.index;
|
||||
_LOG_DEBUG("Releasing HW buffer=%u ...", i);
|
||||
if (us_xioctl(cap->run->fd, VIDIOC_QBUF, &hw->buf) < 0) {
|
||||
@@ -556,7 +555,7 @@ static void _v4l2_buffer_copy(const struct v4l2_buffer *src, struct v4l2_buffer
|
||||
struct v4l2_plane *dest_planes = dest->m.planes;
|
||||
memcpy(dest, src, sizeof(struct v4l2_buffer));
|
||||
if (src->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
|
||||
assert(dest_planes);
|
||||
US_A(dest_planes);
|
||||
dest->m.planes = dest_planes;
|
||||
memcpy(dest->m.planes, src->m.planes, sizeof(struct v4l2_plane) * VIDEO_MAX_PLANES);
|
||||
}
|
||||
@@ -911,7 +910,7 @@ static int _capture_open_io_method(us_capture_s *cap) {
|
||||
switch (cap->io_method) {
|
||||
case V4L2_MEMORY_MMAP: return _capture_open_io_method_mmap(cap);
|
||||
case V4L2_MEMORY_USERPTR: return _capture_open_io_method_userptr(cap);
|
||||
default: assert(0 && "Unsupported IO method");
|
||||
default: US_RAISE("Unsupported IO method");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -972,7 +971,7 @@ static int _capture_open_io_method_mmap(us_capture_s *cap) {
|
||||
_LOG_PERROR("Can't map device buffer=%u", run->n_bufs);
|
||||
return -1;
|
||||
}
|
||||
assert(hw->raw.data != NULL);
|
||||
US_A(hw->raw.data != NULL);
|
||||
hw->raw.allocated = buf_size;
|
||||
|
||||
if (run->capture_mplane) {
|
||||
@@ -1014,7 +1013,7 @@ static int _capture_open_io_method_userptr(us_capture_s *cap) {
|
||||
|
||||
for (run->n_bufs = 0; run->n_bufs < req.count; ++run->n_bufs) {
|
||||
us_capture_hwbuf_s *hw = &run->bufs[run->n_bufs];
|
||||
assert((hw->raw.data = aligned_alloc(page_size, buf_size)) != NULL);
|
||||
US_A((hw->raw.data = aligned_alloc(page_size, buf_size)) != NULL);
|
||||
memset(hw->raw.data, 0, buf_size);
|
||||
hw->raw.allocated = buf_size;
|
||||
if (run->capture_mplane) {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
|
||||
#define US_VERSION_MAJOR 6
|
||||
#define US_VERSION_MINOR 47
|
||||
#define US_VERSION_MINOR 61
|
||||
|
||||
#define US_MAKE_VERSION2(_major, _minor) #_major "." #_minor
|
||||
#define US_MAKE_VERSION1(_major, _minor) US_MAKE_VERSION2(_major, _minor)
|
||||
|
||||
@@ -56,7 +56,7 @@ static int _drm_find_sink(us_drm_s *drm, uint width, uint height, float hz);
|
||||
|
||||
static drmModeModeInfo *_find_best_mode(drmModeConnector *conn, uint width, uint height, float hz);
|
||||
static u32 _find_dpms(int fd, drmModeConnector *conn);
|
||||
static u32 _find_crtc(int fd, drmModeRes *res, drmModeConnector *conn, u32 *taken_crtcs);
|
||||
static u32 _find_crtc(int fd, const drmModeRes *res, drmModeConnector *conn, u32 *taken_crtcs);
|
||||
static const char *_connector_type_to_string(u32 type);
|
||||
static float _get_refresh_rate(const drmModeModeInfo *mode);
|
||||
|
||||
@@ -99,7 +99,7 @@ void us_drm_destroy(us_drm_s *drm) {
|
||||
int us_drm_open(us_drm_s *drm, const us_capture_s *cap) {
|
||||
us_drm_runtime_s *const run = drm->run;
|
||||
|
||||
assert(run->fd < 0);
|
||||
US_A(run->fd < 0);
|
||||
|
||||
switch (_drm_check_status(drm)) {
|
||||
case 0: break;
|
||||
@@ -200,7 +200,7 @@ void us_drm_close(us_drm_s *drm) {
|
||||
if (run->exposing_dma_fd >= 0) {
|
||||
// Нужно подождать, пока dma_fd не освободится, прежде чем прерывать процесс.
|
||||
// Просто на всякий случай.
|
||||
assert(run->fd >= 0);
|
||||
US_A(run->fd >= 0);
|
||||
us_drm_wait_for_vsync(drm);
|
||||
run->exposing_dma_fd = -1;
|
||||
}
|
||||
@@ -258,8 +258,8 @@ void us_drm_close(us_drm_s *drm) {
|
||||
int us_drm_ensure_no_signal(us_drm_s *drm) {
|
||||
us_drm_runtime_s *const run = drm->run;
|
||||
|
||||
assert(run->fd >= 0);
|
||||
assert(run->opened > 0);
|
||||
US_A(run->fd >= 0);
|
||||
US_A(run->opened > 0);
|
||||
|
||||
const ldf now_ts = us_get_now_monotonic();
|
||||
if (run->blank_at_ts == 0) {
|
||||
@@ -284,7 +284,7 @@ int us_drm_ensure_no_signal(us_drm_s *drm) {
|
||||
}
|
||||
|
||||
int us_drm_dpms_power_off(us_drm_s *drm) {
|
||||
assert(drm->run->fd >= 0);
|
||||
US_A(drm->run->fd >= 0);
|
||||
switch (_drm_check_status(drm)) {
|
||||
case 0: break;
|
||||
case US_ERROR_NO_DEVICE: return 0; // Unplugged, nice
|
||||
@@ -300,7 +300,7 @@ int us_drm_dpms_power_off(us_drm_s *drm) {
|
||||
int us_drm_wait_for_vsync(us_drm_s *drm) {
|
||||
us_drm_runtime_s *const run = drm->run;
|
||||
|
||||
assert(run->fd >= 0);
|
||||
US_A(run->fd >= 0);
|
||||
run->blank_at_ts = 0;
|
||||
|
||||
switch (_drm_check_status(drm)) {
|
||||
@@ -355,8 +355,8 @@ static void _drm_vsync_callback(int fd, uint n_frame, uint sec, uint usec, void
|
||||
int us_drm_expose_stub(us_drm_s *drm, us_drm_stub_e stub, const us_capture_s *cap) {
|
||||
us_drm_runtime_s *const run = drm->run;
|
||||
|
||||
assert(run->fd >= 0);
|
||||
assert(run->opened > 0);
|
||||
US_A(run->fd >= 0);
|
||||
US_A(run->opened > 0);
|
||||
run->blank_at_ts = 0;
|
||||
|
||||
switch (_drm_check_status(drm)) {
|
||||
@@ -369,7 +369,7 @@ int us_drm_expose_stub(us_drm_s *drm, us_drm_stub_e stub, const us_capture_s *ca
|
||||
# define DRAW_MSG(x_msg) us_frametext_draw(run->ft, (x_msg), run->mode.hdisplay, run->mode.vdisplay)
|
||||
switch (stub) {
|
||||
case US_DRM_STUB_BAD_RESOLUTION: {
|
||||
assert(cap != NULL);
|
||||
US_A(cap != NULL);
|
||||
char msg[1024];
|
||||
US_SNPRINTF(msg, 1023,
|
||||
"=== PiKVM ==="
|
||||
@@ -420,8 +420,8 @@ int us_drm_expose_dma(us_drm_s *drm, const us_capture_hwbuf_s *hw) {
|
||||
us_drm_runtime_s *const run = drm->run;
|
||||
us_drm_buffer_s *const buf = &run->bufs[hw->buf.index];
|
||||
|
||||
assert(run->fd >= 0);
|
||||
assert(run->opened == 0);
|
||||
US_A(run->fd >= 0);
|
||||
US_A(run->opened == 0);
|
||||
run->blank_at_ts = 0;
|
||||
|
||||
switch (_drm_check_status(drm)) {
|
||||
@@ -706,8 +706,8 @@ static drmModeModeInfo *_find_best_mode(drmModeConnector *conn, uint width, uint
|
||||
if (best == NULL) {
|
||||
best = (conn->count_modes > 0 ? &conn->modes[0] : NULL);
|
||||
}
|
||||
assert(best == NULL || best->hdisplay > 0);
|
||||
assert(best == NULL || best->vdisplay > 0);
|
||||
US_A(best == NULL || best->hdisplay > 0);
|
||||
US_A(best == NULL || best->vdisplay > 0);
|
||||
return best;
|
||||
}
|
||||
|
||||
@@ -726,7 +726,7 @@ static u32 _find_dpms(int fd, drmModeConnector *conn) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 _find_crtc(int fd, drmModeRes *res, drmModeConnector *conn, u32 *taken_crtcs) {
|
||||
static u32 _find_crtc(int fd, const drmModeRes *res, drmModeConnector *conn, u32 *taken_crtcs) {
|
||||
for (int ei = 0; ei < conn->count_encoders; ++ei) {
|
||||
drmModeEncoder *enc = drmModeGetEncoder(fd, conn->encoders[ei]);
|
||||
if (enc == NULL) {
|
||||
|
||||
@@ -56,9 +56,9 @@ void us_fpsi_frame_to_meta(const us_frame_s *frame, us_fpsi_meta_s *meta) {
|
||||
|
||||
void us_fpsi_update(us_fpsi_s *fpsi, bool bump, const us_fpsi_meta_s *meta) {
|
||||
if (meta != NULL) {
|
||||
assert(fpsi->with_meta);
|
||||
US_A(fpsi->with_meta);
|
||||
} else {
|
||||
assert(!fpsi->with_meta);
|
||||
US_A(!fpsi->with_meta);
|
||||
}
|
||||
|
||||
const sll now_ts = us_floor_ms(us_get_now_monotonic());
|
||||
@@ -68,7 +68,7 @@ void us_fpsi_update(us_fpsi_s *fpsi, bool bump, const us_fpsi_meta_s *meta) {
|
||||
// Fast mutex-less store method
|
||||
ull state = (ull)fpsi->accum & 0xFFFF;
|
||||
if (fpsi->with_meta) {
|
||||
assert(meta != NULL);
|
||||
US_A(meta != NULL);
|
||||
state |= (ull)(meta->width & 0xFFFF) << 16;
|
||||
state |= (ull)(meta->height & 0xFFFF) << 32;
|
||||
state |= (ull)(meta->online ? 1 : 0) << 48;
|
||||
@@ -84,7 +84,7 @@ void us_fpsi_update(us_fpsi_s *fpsi, bool bump, const us_fpsi_meta_s *meta) {
|
||||
|
||||
uint us_fpsi_get(us_fpsi_s *fpsi, us_fpsi_meta_s *meta) {
|
||||
if (meta != NULL) {
|
||||
assert(fpsi->with_meta);
|
||||
US_A(fpsi->with_meta);
|
||||
}
|
||||
|
||||
// Между чтением инфы и времени может быть гонка,
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
@@ -107,7 +106,7 @@ uint us_frame_get_padding(const us_frame_s *frame) {
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0 && "Unknown format");
|
||||
US_RAISE("Unknown format");
|
||||
}
|
||||
if (bytes_per_pixel > 0 && frame->stride > frame->width) {
|
||||
return (frame->stride - frame->width * bytes_per_pixel);
|
||||
@@ -120,7 +119,7 @@ bool us_is_jpeg(uint format) {
|
||||
}
|
||||
|
||||
const char *us_fourcc_to_string(uint format, char *buf, uz size) {
|
||||
assert(size >= 8);
|
||||
US_A(size >= 8);
|
||||
buf[0] = format & 0x7F;
|
||||
buf[1] = (format >> 8) & 0x7F;
|
||||
buf[2] = (format >> 16) & 0x7F;
|
||||
|
||||
@@ -84,7 +84,7 @@ typedef struct {
|
||||
|
||||
|
||||
static inline void us_frame_encoding_begin(const us_frame_s *src, us_frame_s *dest, uint format) {
|
||||
assert(src->used > 0);
|
||||
US_A(src->used > 0);
|
||||
US_FRAME_COPY_META(src, dest);
|
||||
dest->encode_begin_ts = us_get_now_monotonic();
|
||||
dest->format = format;
|
||||
@@ -93,7 +93,7 @@ static inline void us_frame_encoding_begin(const us_frame_s *src, us_frame_s *de
|
||||
}
|
||||
|
||||
static inline void us_frame_encoding_end(us_frame_s *dest) {
|
||||
assert(dest->used > 0);
|
||||
US_A(dest->used > 0);
|
||||
dest->encode_end_ts = us_get_now_monotonic();
|
||||
}
|
||||
|
||||
|
||||
@@ -84,8 +84,8 @@ To access the nth pixel in a row, right-shift by n.
|
||||
*/
|
||||
|
||||
void us_frametext_draw(us_frametext_s *ft, const char *text, uint width, uint height) {
|
||||
assert(width > 0);
|
||||
assert(height > 0);
|
||||
US_A(width > 0);
|
||||
US_A(height > 0);
|
||||
|
||||
us_frame_s *const frame = ft->frame;
|
||||
|
||||
@@ -112,7 +112,7 @@ void us_frametext_draw(us_frametext_s *ft, const char *text, uint width, uint he
|
||||
}
|
||||
|
||||
char *str = us_strdup(text);
|
||||
char *line;
|
||||
const char *line;
|
||||
char *rest;
|
||||
|
||||
uint block_width = 0;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include "tools.h"
|
||||
|
||||
|
||||
#define US_LIST_DECLARE \
|
||||
@@ -71,6 +71,6 @@
|
||||
|
||||
#define US_LIST_REMOVE_C(x_first, x_item, x_count) { \
|
||||
US_LIST_REMOVE(x_first, x_item); \
|
||||
assert((x_count) >= 1); \
|
||||
US_A((x_count) >= 1); \
|
||||
--(x_count); \
|
||||
}
|
||||
|
||||
@@ -28,13 +28,13 @@
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "tools.h"
|
||||
#include "threading.h"
|
||||
#include "logging_base.h"
|
||||
|
||||
|
||||
enum us_log_level_t {
|
||||
@@ -73,37 +73,7 @@ extern pthread_mutex_t us_g_log_mutex;
|
||||
#define US_COLOR_RESET "\x1b[0m"
|
||||
|
||||
|
||||
#define US_SEP_INFO(x_ch) { \
|
||||
US_LOGGING_LOCK; \
|
||||
for (int m_i = 0; m_i < 80; ++m_i) { \
|
||||
fputc((x_ch), stderr); \
|
||||
} \
|
||||
fputc('\n', stderr); \
|
||||
fflush(stderr); \
|
||||
US_LOGGING_UNLOCK; \
|
||||
}
|
||||
|
||||
#define US_SEP_DEBUG(x_ch) { \
|
||||
if (us_g_log_level >= US_LOG_LEVEL_DEBUG) { \
|
||||
US_SEP_INFO(x_ch); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
#define US_LOG_PRINTF_NOLOCK(x_label_color, x_label, x_msg_color, x_msg, ...) { \
|
||||
char m_tname_buf[US_THREAD_NAME_SIZE] = {0}; \
|
||||
us_thread_get_name(m_tname_buf); \
|
||||
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__); \
|
||||
} else { \
|
||||
fprintf(stderr, "-- " x_label " [%.03Lf %9s] -- " x_msg, \
|
||||
us_get_now_monotonic(), m_tname_buf, ##__VA_ARGS__); \
|
||||
} \
|
||||
fputc('\n', stderr); \
|
||||
fflush(stderr); \
|
||||
}
|
||||
// XXX: See the definition of US_LOG_PRINTF_NOLOCK() in logging_base.h
|
||||
|
||||
#define US_LOG_PRINTF(x_label_color, x_label, x_msg_color, x_msg, ...) { \
|
||||
US_LOGGING_LOCK; \
|
||||
@@ -111,6 +81,10 @@ extern pthread_mutex_t us_g_log_mutex;
|
||||
US_LOGGING_UNLOCK; \
|
||||
}
|
||||
|
||||
#define US_LOG_SEP_INFO { \
|
||||
US_LOG_PRINTF(US_COLOR_GREEN, "INFO ", US_COLOR_GREEN, "============================================="); \
|
||||
}
|
||||
|
||||
#define US_LOG_ERROR(x_msg, ...) { \
|
||||
US_LOG_PRINTF(US_COLOR_RED, "ERROR", US_COLOR_RED, x_msg, ##__VA_ARGS__); \
|
||||
}
|
||||
@@ -121,6 +95,11 @@ extern pthread_mutex_t us_g_log_mutex;
|
||||
free(m_perror_str); \
|
||||
}
|
||||
|
||||
// We don't include alsa, speex and opus headers here
|
||||
#define US_LOG_PERROR_ALSA(x_err, x_msg, ...) US_LOG_ERROR(x_msg ": %s", ##__VA_ARGS__, snd_strerror(x_err))
|
||||
#define US_LOG_PERROR_RES(x_err, x_msg, ...) US_LOG_ERROR(x_msg ": %s", ##__VA_ARGS__, speex_resampler_strerror(x_err))
|
||||
#define US_LOG_PERROR_OPUS(x_err, x_msg, ...) US_LOG_ERROR(x_msg ": %s", ##__VA_ARGS__, opus_strerror(x_err))
|
||||
|
||||
#define US_LOG_INFO(x_msg, ...) { \
|
||||
US_LOG_PRINTF(US_COLOR_GREEN, "INFO ", "", x_msg, ##__VA_ARGS__); \
|
||||
}
|
||||
|
||||
@@ -20,61 +20,29 @@
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#include "memsinkfd.h"
|
||||
#pragma once
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "uslibs/types.h"
|
||||
#include "uslibs/errors.h"
|
||||
#include "uslibs/tools.h"
|
||||
#include "uslibs/frame.h"
|
||||
#include "uslibs/memsinksh.h"
|
||||
|
||||
#include "logging.h"
|
||||
#include "types.h"
|
||||
#include "tools.h"
|
||||
#include "threading.h"
|
||||
|
||||
|
||||
int us_memsink_fd_wait_frame(int fd, us_memsink_shared_s *mem, u64 last_id) {
|
||||
const ldf deadline_ts = us_get_now_monotonic() + 1; // wait_timeout
|
||||
ldf now_ts;
|
||||
do {
|
||||
const int result = us_flock_timedwait_monotonic(fd, 1); // lock_timeout
|
||||
now_ts = us_get_now_monotonic();
|
||||
if (result < 0 && errno != EWOULDBLOCK) {
|
||||
US_JLOG_PERROR("video", "Can't lock memsink");
|
||||
return -1;
|
||||
} else if (result == 0) {
|
||||
if (mem->magic == US_MEMSINK_MAGIC && mem->version == US_MEMSINK_VERSION && mem->id != last_id) {
|
||||
return 0;
|
||||
}
|
||||
if (flock(fd, LOCK_UN) < 0) {
|
||||
US_JLOG_PERROR("video", "Can't unlock memsink");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
usleep(1000); // lock_polling
|
||||
} while (now_ts < deadline_ts);
|
||||
return US_ERROR_NO_DATA;
|
||||
}
|
||||
|
||||
int us_memsink_fd_get_frame(int fd, us_memsink_shared_s *mem, us_frame_s *frame, u64 *frame_id, bool key_required) {
|
||||
us_frame_set_data(frame, us_memsink_get_data(mem), mem->used);
|
||||
US_FRAME_COPY_META(mem, frame);
|
||||
*frame_id = mem->id;
|
||||
mem->last_client_ts = us_get_now_monotonic();
|
||||
if (key_required) {
|
||||
mem->key_requested = true;
|
||||
#define US_LOG_PRINTF_NOLOCK(x_label_color, x_label, x_msg_color, x_msg, ...) { \
|
||||
char m_tname_buf[US_THREAD_NAME_SIZE] = {0}; \
|
||||
us_thread_get_name(m_tname_buf); \
|
||||
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__); \
|
||||
} else { \
|
||||
fprintf(stderr, "-- " x_label " [%.03Lf %9s] -- " x_msg, \
|
||||
us_get_now_monotonic(), m_tname_buf, ##__VA_ARGS__); \
|
||||
} \
|
||||
fputc('\n', stderr); \
|
||||
fflush(stderr); \
|
||||
}
|
||||
|
||||
bool retval = 0;
|
||||
if (frame->format != V4L2_PIX_FMT_H264) {
|
||||
US_JLOG_ERROR("video", "Got non-H264 frame from memsink");
|
||||
retval = -1;
|
||||
}
|
||||
if (flock(fd, LOCK_UN) < 0) {
|
||||
US_JLOG_PERROR("video", "Can't unlock memsink");
|
||||
retval = -1;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -110,7 +109,7 @@ bool us_memsink_server_check(us_memsink_s *sink, const us_frame_s *frame) {
|
||||
// Если frame == NULL, то только проверяем наличие клиентов
|
||||
// или необходимость инициализировать память.
|
||||
|
||||
assert(sink->server);
|
||||
US_A(sink->server);
|
||||
|
||||
if (sink->mem->magic != US_MEMSINK_MAGIC || sink->mem->version != US_MEMSINK_VERSION) {
|
||||
// Если регион памяти не был инициализирован, то нужно что-то туда положить.
|
||||
@@ -162,7 +161,7 @@ bool us_memsink_server_check(us_memsink_s *sink, const us_frame_s *frame) {
|
||||
}
|
||||
|
||||
int us_memsink_server_put(us_memsink_s *sink, const us_frame_s *frame, bool *key_requested) {
|
||||
assert(sink->server);
|
||||
US_A(sink->server);
|
||||
|
||||
const ldf now = us_get_now_monotonic();
|
||||
|
||||
@@ -210,7 +209,7 @@ int us_memsink_server_put(us_memsink_s *sink, const us_frame_s *frame, bool *key
|
||||
}
|
||||
|
||||
int us_memsink_client_get(us_memsink_s *sink, us_frame_s *frame, bool *key_requested, bool key_required) {
|
||||
assert(!sink->server); // Client only
|
||||
US_A(!sink->server); // Client only
|
||||
|
||||
if (us_flock_timedwait_monotonic(sink->fd, sink->timeout) < 0) {
|
||||
if (errno == EWOULDBLOCK) {
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "tools.h"
|
||||
|
||||
|
||||
us_memsink_shared_s *us_memsink_shared_map(int fd, uz data_size) {
|
||||
@@ -40,12 +40,12 @@ us_memsink_shared_s *us_memsink_shared_map(int fd, uz data_size) {
|
||||
if (mem == MAP_FAILED) {
|
||||
return NULL;
|
||||
}
|
||||
assert(mem != NULL);
|
||||
US_A(mem != NULL);
|
||||
return mem;
|
||||
}
|
||||
|
||||
int us_memsink_shared_unmap(us_memsink_shared_s *mem, uz data_size) {
|
||||
assert(mem != NULL);
|
||||
US_A(mem != NULL);
|
||||
return munmap(mem, sizeof(us_memsink_shared_s) + data_size);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ uz us_memsink_calculate_size(const char *obj) {
|
||||
} else if (!strcasecmp(ptr, "h264")) {
|
||||
return 2 * 1024 * 1024;
|
||||
} else if (!strcasecmp(ptr, "raw")) {
|
||||
return 1920 * 1200 * 3; // RGB
|
||||
return 3840 * 2160 * 3; // RGB
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
|
||||
#define US_MEMSINK_MAGIC ((u64)0xCAFEBABECAFEBABE)
|
||||
#define US_MEMSINK_VERSION ((u32)7)
|
||||
#define US_MEMSINK_VERSION ((u32)8)
|
||||
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -25,15 +25,15 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "tools.h"
|
||||
|
||||
|
||||
void us_build_short_options(const struct option opts[], char *short_opts, uz size) {
|
||||
memset(short_opts, 0, size);
|
||||
for (uint short_i = 0, opt_i = 0; opts[opt_i].name != NULL; ++opt_i) {
|
||||
assert(short_i < size - 3);
|
||||
US_A(short_i < size - 3);
|
||||
if (isalpha(opts[opt_i].val)) {
|
||||
short_opts[short_i] = opts[opt_i].val;
|
||||
++short_i;
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
@@ -41,11 +40,11 @@ us_queue_s *us_queue_init(uint capacity) {
|
||||
US_MUTEX_INIT(q->mutex);
|
||||
|
||||
pthread_condattr_t attrs;
|
||||
assert(!pthread_condattr_init(&attrs));
|
||||
assert(!pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC));
|
||||
assert(!pthread_cond_init(&q->full_cond, &attrs));
|
||||
assert(!pthread_cond_init(&q->empty_cond, &attrs));
|
||||
assert(!pthread_condattr_destroy(&attrs));
|
||||
US_A(!pthread_condattr_init(&attrs));
|
||||
US_A(!pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC));
|
||||
US_A(!pthread_cond_init(&q->full_cond, &attrs));
|
||||
US_A(!pthread_cond_init(&q->empty_cond, &attrs));
|
||||
US_A(!pthread_condattr_destroy(&attrs));
|
||||
return q;
|
||||
}
|
||||
|
||||
@@ -59,7 +58,7 @@ void us_queue_destroy(us_queue_s *q) {
|
||||
|
||||
#define _WAIT_OR_UNLOCK(x_var, x_cond) { \
|
||||
struct timespec m_ts; \
|
||||
assert(!clock_gettime(CLOCK_MONOTONIC, &m_ts)); \
|
||||
US_A(!clock_gettime(CLOCK_MONOTONIC, &m_ts)); \
|
||||
us_ld_to_timespec(us_timespec_to_ld(&m_ts) + timeout, &m_ts); \
|
||||
while (x_var) { \
|
||||
const int err = pthread_cond_timedwait(&(x_cond), &q->mutex, &m_ts); \
|
||||
@@ -67,7 +66,7 @@ void us_queue_destroy(us_queue_s *q) {
|
||||
US_MUTEX_UNLOCK(q->mutex); \
|
||||
return -1; \
|
||||
} \
|
||||
assert(!err); \
|
||||
US_A(!err); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "ring.h"
|
||||
|
||||
#include "types.h"
|
||||
@@ -43,7 +41,7 @@ us_ring_s *us_ring_init(uint capacity) {
|
||||
ring->consumer = us_queue_init(capacity);
|
||||
for (uint ri = 0; ri < capacity; ++ri) {
|
||||
ring->places[ri] = ri; // XXX: Just to avoid casting between pointer and uint
|
||||
assert(!us_queue_put(ring->producer, (void*)(ring->places + ri), 0));
|
||||
US_A(!us_queue_put(ring->producer, (void*)(ring->places + ri), 0));
|
||||
}
|
||||
return ring;
|
||||
}
|
||||
@@ -82,5 +80,5 @@ int _acquire(us_ring_s *ring, us_queue_s *q, ldf timeout) {
|
||||
}
|
||||
|
||||
void _release(us_ring_s *ring, us_queue_s *q, uint ri) {
|
||||
assert(!us_queue_put(q, (void*)(ring->places + ri), 0));
|
||||
US_A(!us_queue_put(q, (void*)(ring->places + ri), 0));
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 32
|
||||
# define HAS_SIGABBREV_NP
|
||||
@@ -58,25 +57,25 @@ char *us_signum_to_string(int signum) {
|
||||
void us_install_signals_handler(us_signal_handler_f handler, bool ignore_sigpipe) {
|
||||
struct sigaction sig_act = {0};
|
||||
|
||||
assert(!sigemptyset(&sig_act.sa_mask));
|
||||
US_A(!sigemptyset(&sig_act.sa_mask));
|
||||
sig_act.sa_handler = handler;
|
||||
assert(!sigaddset(&sig_act.sa_mask, SIGINT));
|
||||
assert(!sigaddset(&sig_act.sa_mask, SIGTERM));
|
||||
US_A(!sigaddset(&sig_act.sa_mask, SIGINT));
|
||||
US_A(!sigaddset(&sig_act.sa_mask, SIGTERM));
|
||||
if (!ignore_sigpipe) {
|
||||
assert(!sigaddset(&sig_act.sa_mask, SIGPIPE));
|
||||
US_A(!sigaddset(&sig_act.sa_mask, SIGPIPE));
|
||||
}
|
||||
|
||||
US_LOG_DEBUG("Installing SIGINT handler ...");
|
||||
assert(!sigaction(SIGINT, &sig_act, NULL));
|
||||
US_A(!sigaction(SIGINT, &sig_act, NULL));
|
||||
|
||||
US_LOG_DEBUG("Installing SIGTERM handler ...");
|
||||
assert(!sigaction(SIGTERM, &sig_act, NULL));
|
||||
US_A(!sigaction(SIGTERM, &sig_act, NULL));
|
||||
|
||||
if (!ignore_sigpipe) {
|
||||
US_LOG_DEBUG("Installing SIGPIPE handler ...");
|
||||
assert(!sigaction(SIGPIPE, &sig_act, NULL));
|
||||
US_A(!sigaction(SIGPIPE, &sig_act, NULL));
|
||||
} else {
|
||||
US_LOG_DEBUG("Ignoring SIGPIPE ...");
|
||||
assert(signal(SIGPIPE, SIG_IGN) != SIG_ERR);
|
||||
US_A(signal(SIGPIPE, SIG_IGN) != SIG_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/syscall.h>
|
||||
|
||||
@@ -47,8 +46,8 @@
|
||||
# define US_THREAD_NAME_SIZE ((uz)16)
|
||||
#endif
|
||||
|
||||
#define US_THREAD_CREATE(x_tid, x_func, x_arg) assert(!pthread_create(&(x_tid), NULL, (x_func), (x_arg)))
|
||||
#define US_THREAD_JOIN(x_tid) assert(!pthread_join((x_tid), NULL))
|
||||
#define US_THREAD_CREATE(x_tid, x_func, x_arg) US_A(!pthread_create(&(x_tid), NULL, (x_func), (x_arg)))
|
||||
#define US_THREAD_JOIN(x_tid) US_A(!pthread_join((x_tid), NULL))
|
||||
|
||||
#ifdef WITH_PTHREAD_NP
|
||||
# define US_THREAD_RENAME(x_fmt, ...) { \
|
||||
@@ -65,16 +64,16 @@
|
||||
us_thread_block_signals(); \
|
||||
}
|
||||
|
||||
#define US_MUTEX_INIT(x_mutex) assert(!pthread_mutex_init(&(x_mutex), NULL))
|
||||
#define US_MUTEX_DESTROY(x_mutex) assert(!pthread_mutex_destroy(&(x_mutex)))
|
||||
#define US_MUTEX_LOCK(x_mutex) assert(!pthread_mutex_lock(&(x_mutex)))
|
||||
#define US_MUTEX_UNLOCK(x_mutex) assert(!pthread_mutex_unlock(&(x_mutex)))
|
||||
#define US_MUTEX_INIT(x_mutex) US_A(!pthread_mutex_init(&(x_mutex), NULL))
|
||||
#define US_MUTEX_DESTROY(x_mutex) US_A(!pthread_mutex_destroy(&(x_mutex)))
|
||||
#define US_MUTEX_LOCK(x_mutex) US_A(!pthread_mutex_lock(&(x_mutex)))
|
||||
#define US_MUTEX_UNLOCK(x_mutex) US_A(!pthread_mutex_unlock(&(x_mutex)))
|
||||
|
||||
#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_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))); }
|
||||
#define US_COND_INIT(x_cond) US_A(!pthread_cond_init(&(x_cond), NULL))
|
||||
#define US_COND_DESTROY(x_cond) US_A(!pthread_cond_destroy(&(x_cond)))
|
||||
#define US_COND_SIGNAL(x_cond) US_A(!pthread_cond_signal(&(x_cond)))
|
||||
#define US_COND_BROADCAST(x_cond) US_A(!pthread_cond_broadcast(&(x_cond)))
|
||||
#define US_COND_WAIT_FOR(x_var, x_cond, x_mutex) { while(!(x_var)) US_A(!pthread_cond_wait(&(x_cond), &(x_mutex))); }
|
||||
|
||||
|
||||
#ifdef WITH_PTHREAD_NP
|
||||
@@ -95,12 +94,12 @@ INLINE void us_thread_get_name(char *name) { // Always required for logging
|
||||
#ifdef WITH_PTHREAD_NP
|
||||
int retval = -1;
|
||||
# if defined(__linux__) || defined (__NetBSD__)
|
||||
retval = pthread_getname_np(pthread_self(), name, US_THREAD_NAME_SIZE - 1);
|
||||
retval = pthread_getname_np(pthread_self(), name, US_THREAD_NAME_SIZE); // Always null-terminated
|
||||
# elif \
|
||||
(defined(__FreeBSD__) && defined(__FreeBSD_version) && __FreeBSD_version >= 1103500) \
|
||||
|| (defined(__OpenBSD__) && defined(OpenBSD) && OpenBSD >= 201905) \
|
||||
|| defined(__DragonFly__)
|
||||
pthread_get_name_np(pthread_self(), name, US_THREAD_NAME_SIZE - 1);
|
||||
pthread_get_name_np(pthread_self(), name, US_THREAD_NAME_SIZE); // Also null-terminated
|
||||
if (name[0] != '\0') {
|
||||
retval = 0;
|
||||
}
|
||||
@@ -114,7 +113,7 @@ INLINE void us_thread_get_name(char *name) { // Always required for logging
|
||||
const pid_t tid = syscall(SYS_gettid);
|
||||
#elif defined(__FreeBSD__)
|
||||
long id;
|
||||
assert(!syscall(SYS_thr_self, &id));
|
||||
US_A(!syscall(SYS_thr_self, &id));
|
||||
const pid_t tid = id;
|
||||
#elif defined(__OpenBSD__)
|
||||
const pid_t tid = syscall(SYS_getthrid);
|
||||
@@ -135,8 +134,8 @@ INLINE void us_thread_get_name(char *name) { // Always required for logging
|
||||
|
||||
INLINE void us_thread_block_signals(void) {
|
||||
sigset_t mask;
|
||||
assert(!sigemptyset(&mask));
|
||||
assert(!sigaddset(&mask, SIGINT));
|
||||
assert(!sigaddset(&mask, SIGTERM));
|
||||
assert(!pthread_sigmask(SIG_BLOCK, &mask, NULL));
|
||||
US_A(!sigemptyset(&mask));
|
||||
US_A(!sigaddset(&mask, SIGINT));
|
||||
US_A(!sigaddset(&mask, SIGTERM));
|
||||
US_A(!pthread_sigmask(SIG_BLOCK, &mask, NULL));
|
||||
}
|
||||
|
||||
@@ -51,14 +51,17 @@
|
||||
|
||||
#define INLINE inline __attribute__((always_inline))
|
||||
|
||||
#define US_CALLOC(x_dest, x_nmemb) assert(((x_dest) = calloc((x_nmemb), sizeof(*(x_dest)))) != NULL)
|
||||
#define US_REALLOC(x_dest, x_nmemb) assert(((x_dest) = realloc((x_dest), (x_nmemb) * sizeof(*(x_dest)))) != NULL)
|
||||
#define US_A(x_arg) { const bool m_ar = (x_arg); assert(m_ar && #x_arg); }
|
||||
#define US_RAISE(x_msg) assert(0 && x_msg)
|
||||
|
||||
#define US_CALLOC(x_dest, x_nmemb) US_A(((x_dest) = calloc((x_nmemb), sizeof(*(x_dest)))) != NULL)
|
||||
#define US_REALLOC(x_dest, x_nmemb) US_A(((x_dest) = realloc((x_dest), (x_nmemb) * sizeof(*(x_dest)))) != NULL)
|
||||
#define US_DELETE(x_dest, x_free) { if (x_dest) { x_free(x_dest); x_dest = NULL; } }
|
||||
#define US_CLOSE_FD(x_dest) { if (x_dest >= 0) { close(x_dest); x_dest = -1; } }
|
||||
#define US_MEMSET_ZERO(x_obj) memset(&(x_obj), 0, sizeof(x_obj))
|
||||
|
||||
#define US_SNPRINTF(x_dest, x_size, x_fmt, ...) assert(snprintf((x_dest), (x_size), (x_fmt), ##__VA_ARGS__) > 0)
|
||||
#define US_ASPRINTF(x_dest, x_fmt, ...) assert(asprintf(&(x_dest), (x_fmt), ##__VA_ARGS__) > 0)
|
||||
#define US_SNPRINTF(x_dest, x_size, x_fmt, ...) US_A(snprintf((x_dest), (x_size), (x_fmt), ##__VA_ARGS__) > 0)
|
||||
#define US_ASPRINTF(x_dest, x_fmt, ...) US_A(asprintf(&(x_dest), (x_fmt), ##__VA_ARGS__) > 0)
|
||||
|
||||
#define US_MIN(x_a, x_b) ({ \
|
||||
__typeof__(x_a) m_a = (x_a); \
|
||||
@@ -85,7 +88,7 @@
|
||||
|
||||
INLINE char *us_strdup(const char *str) {
|
||||
char *const new = strdup(str);
|
||||
assert(new != NULL);
|
||||
US_A(new != NULL);
|
||||
return new;
|
||||
}
|
||||
|
||||
@@ -115,7 +118,7 @@ INLINE u32 us_triple_u32(u32 x) {
|
||||
|
||||
INLINE void us_get_now(clockid_t clk_id, time_t *sec, long *msec) {
|
||||
struct timespec ts;
|
||||
assert(!clock_gettime(clk_id, &ts));
|
||||
US_A(!clock_gettime(clk_id, &ts));
|
||||
*sec = ts.tv_sec;
|
||||
*msec = round(ts.tv_nsec / 1.0e6);
|
||||
|
||||
@@ -134,7 +137,7 @@ INLINE ldf us_get_now_monotonic(void) {
|
||||
|
||||
INLINE u64 us_get_now_monotonic_u64(void) {
|
||||
struct timespec ts;
|
||||
assert(!clock_gettime(CLOCK_MONOTONIC, &ts));
|
||||
US_A(!clock_gettime(CLOCK_MONOTONIC, &ts));
|
||||
return (u64)(ts.tv_nsec / 1000) + (u64)ts.tv_sec * 1000000;
|
||||
}
|
||||
|
||||
@@ -150,12 +153,6 @@ INLINE ldf us_get_now_real(void) {
|
||||
return (ldf)sec + ((ldf)msec) / 1000;
|
||||
}
|
||||
|
||||
INLINE uint us_get_cores_available(void) {
|
||||
long cores_sysconf = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
cores_sysconf = (cores_sysconf < 0 ? 0 : cores_sysconf);
|
||||
return US_MAX(US_MIN(cores_sysconf, 4), 1);
|
||||
}
|
||||
|
||||
INLINE void us_ld_to_timespec(ldf ld, struct timespec *ts) {
|
||||
ts->tv_sec = (long)ld;
|
||||
ts->tv_nsec = (ld - ts->tv_sec) * 1000000000L;
|
||||
@@ -169,6 +166,28 @@ INLINE ldf us_timespec_to_ld(const struct timespec *ts) {
|
||||
return ts->tv_sec + ((ldf)ts->tv_nsec) / 1000000000;
|
||||
}
|
||||
|
||||
#define NTP_UNIX_TIME_DIFF 2208988800u // Difference between Unix time and NTP time in seconds (1970 - 1900)
|
||||
#define NTP_TICKS_IN_SECOND 4294967296u // Ticks per second in NTP time
|
||||
|
||||
INLINE u64 us_get_now_ntp(void) {
|
||||
struct timespec ts;
|
||||
US_A(!clock_gettime(CLOCK_REALTIME, &ts));
|
||||
return (((u64)ts.tv_sec + NTP_UNIX_TIME_DIFF) << 32) + ((u64)ts.tv_nsec / 1000 * NTP_TICKS_IN_SECOND) / 1000000;
|
||||
}
|
||||
|
||||
INLINE u64 us_ld_to_ntp(ldf ld) {
|
||||
return (ld > 0 ? ld * NTP_TICKS_IN_SECOND : 0);
|
||||
}
|
||||
|
||||
#undef NTP_TICKS_IN_SECOND
|
||||
#undef NTP_UNIX_TIME_DIFF
|
||||
|
||||
INLINE uint us_get_cores_available(void) {
|
||||
long cores_sysconf = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
cores_sysconf = (cores_sysconf < 0 ? 0 : cores_sysconf);
|
||||
return US_MAX(US_MIN(cores_sysconf, 4), 1);
|
||||
}
|
||||
|
||||
INLINE int us_flock_timedwait_monotonic(int fd, ldf timeout) {
|
||||
const ldf deadline_ts = us_get_now_monotonic() + timeout;
|
||||
int retval = -1;
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <setjmp.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <jpeglib.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "tools.h"
|
||||
#include "logging.h"
|
||||
#include "frame.h"
|
||||
|
||||
@@ -45,7 +45,7 @@ static void _jpeg_error_handler(j_common_ptr jpeg);
|
||||
|
||||
|
||||
int us_unjpeg(const us_frame_s *src, us_frame_s *dest, bool decode) {
|
||||
assert(us_is_jpeg(src->format));
|
||||
US_A(us_is_jpeg(src->format));
|
||||
|
||||
volatile int retval = 0;
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
@@ -113,7 +112,7 @@ void us_encoder_open(us_encoder_s *enc, us_capture_s *cap) {
|
||||
us_encoder_runtime_s *const run = enc->run;
|
||||
us_capture_runtime_s *const cr = cap->run;
|
||||
|
||||
assert(run->pool == NULL);
|
||||
US_A(run->pool == NULL);
|
||||
|
||||
us_encoder_type_e type = enc->type;
|
||||
uint quality = cap->jpeg_quality;
|
||||
@@ -176,7 +175,7 @@ void us_encoder_open(us_encoder_s *enc, us_capture_s *cap) {
|
||||
}
|
||||
|
||||
void us_encoder_close(us_encoder_s *enc) {
|
||||
assert(enc->run->pool != NULL);
|
||||
US_A(enc->run->pool != NULL);
|
||||
US_DELETE(enc->run->pool, us_workers_pool_destroy);
|
||||
}
|
||||
|
||||
@@ -226,7 +225,7 @@ static bool _worker_run_job(us_worker_s *wr) {
|
||||
}
|
||||
|
||||
} else {
|
||||
assert(0 && "Unknown encoder type");
|
||||
US_RAISE("Unknown encoder type");
|
||||
}
|
||||
|
||||
US_LOG_VERBOSE("Compressed new JPEG: size=%zu, time=%0.3Lf, worker=%s, buffer=%u",
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <jpeglib.h>
|
||||
|
||||
@@ -140,7 +139,8 @@ void us_cpu_encoder_compress(const us_frame_s *src, us_frame_s *dest, uint quali
|
||||
_jpeg_write_scanlines_bgr24(&jpeg, src);
|
||||
# endif
|
||||
break;
|
||||
default: assert(0 && "Unsupported input format for CPU encoder"); return;
|
||||
default:
|
||||
US_RAISE("Unsupported input format for CPU encoder");
|
||||
}
|
||||
|
||||
jpeg_finish_compress(&jpeg);
|
||||
@@ -151,7 +151,7 @@ void us_cpu_encoder_compress(const us_frame_s *src, us_frame_s *dest, uint quali
|
||||
|
||||
static void _jpeg_set_dest_frame(j_compress_ptr jpeg, us_frame_s *frame) {
|
||||
if (jpeg->dest == NULL) {
|
||||
assert((jpeg->dest = (struct jpeg_destination_mgr*)(*jpeg->mem->alloc_small)(
|
||||
US_A((jpeg->dest = (struct jpeg_destination_mgr*)(*jpeg->mem->alloc_small)(
|
||||
(j_common_ptr) jpeg, JPOOL_PERMANENT, sizeof(_jpeg_dest_manager_s)
|
||||
)) != NULL);
|
||||
}
|
||||
@@ -192,8 +192,7 @@ static void _jpeg_write_scanlines_yuv(struct jpeg_compress_struct *jpeg, const u
|
||||
u = data[0];
|
||||
v = data[2];
|
||||
} else {
|
||||
assert(0 && "Unsupported pixel format");
|
||||
return; // Makes linter happy
|
||||
US_RAISE("Unsupported pixel format");
|
||||
}
|
||||
|
||||
ptr[0] = y;
|
||||
@@ -217,15 +216,11 @@ static void _jpeg_write_scanlines_yuv_planar(struct jpeg_compress_struct *jpeg,
|
||||
US_CALLOC(line_buf, frame->width * 3);
|
||||
|
||||
const uint padding = us_frame_get_padding(frame);
|
||||
const uint image_size = frame->width * frame->height;
|
||||
const uint chroma_array_size = (frame->used - image_size) / 2;
|
||||
const uint chroma_matrix_order = (image_size / chroma_array_size) == 16 ? 4 : 2;
|
||||
const uint luma_array_size = (frame->width + padding) * frame->height;
|
||||
const uint chroma_array_size = (frame->used - luma_array_size) >> 1;
|
||||
const u8 *data = frame->data;
|
||||
const u8 *chroma1_data = frame->data + image_size;
|
||||
const u8 *chroma2_data = frame->data + image_size + chroma_array_size;
|
||||
|
||||
//US_LOG_DEBUG("Planar data: Image Size %u, Chroma Array Size %u, Chroma Matrix Order %u",
|
||||
// image_size, chroma_array_size, chroma_matrix_order);
|
||||
const u8 *chroma1_data = frame->data + luma_array_size;
|
||||
const u8 *chroma2_data = frame->data + luma_array_size + chroma_array_size;
|
||||
|
||||
while (jpeg->next_scanline < frame->height) {
|
||||
u8 *ptr = line_buf;
|
||||
@@ -235,7 +230,7 @@ static void _jpeg_write_scanlines_yuv_planar(struct jpeg_compress_struct *jpeg,
|
||||
u8 y = data[x];
|
||||
u8 u;
|
||||
u8 v;
|
||||
uint chroma_position = x / chroma_matrix_order;
|
||||
uint chroma_position = x >> 1;
|
||||
|
||||
switch (frame->format) {
|
||||
case V4L2_PIX_FMT_YUV420:
|
||||
@@ -247,8 +242,7 @@ static void _jpeg_write_scanlines_yuv_planar(struct jpeg_compress_struct *jpeg,
|
||||
v = chroma1_data[chroma_position];
|
||||
break;
|
||||
default:
|
||||
assert(0 && "Unsupported pixel format");
|
||||
return; // Makes linter happy
|
||||
US_RAISE("Unsupported pixel format");
|
||||
}
|
||||
|
||||
ptr[0] = y;
|
||||
@@ -259,9 +253,9 @@ static void _jpeg_write_scanlines_yuv_planar(struct jpeg_compress_struct *jpeg,
|
||||
|
||||
data += frame->width + padding;
|
||||
|
||||
if (jpeg->next_scanline > 0 && jpeg->next_scanline % chroma_matrix_order == 0) {
|
||||
chroma1_data += (frame->width + padding) / chroma_matrix_order;
|
||||
chroma2_data += (frame->width + padding) / chroma_matrix_order;
|
||||
if (jpeg->next_scanline > 0 && jpeg->next_scanline % 2 == 0) {
|
||||
chroma1_data += (frame->width + padding) >> 1;
|
||||
chroma2_data += (frame->width + padding) >> 1;
|
||||
}
|
||||
|
||||
JSAMPROW scanlines[1] = {line_buf};
|
||||
@@ -371,7 +365,7 @@ static void _jpeg_init_destination(j_compress_ptr jpeg) {
|
||||
_jpeg_dest_manager_s *const dest = (_jpeg_dest_manager_s*)jpeg->dest;
|
||||
|
||||
// Allocate the output buffer - it will be released when done with image
|
||||
assert((dest->buf = (JOCTET*)(*jpeg->mem->alloc_small)(
|
||||
US_A((dest->buf = (JOCTET*)(*jpeg->mem->alloc_small)(
|
||||
(j_common_ptr) jpeg, JPOOL_IMAGE, JPEG_OUTPUT_BUFFER_SIZE * sizeof(JOCTET)
|
||||
)) != NULL);
|
||||
|
||||
|
||||
@@ -28,11 +28,11 @@
|
||||
#include "encoder.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include "../../../libs/types.h"
|
||||
#include "../../../libs/tools.h"
|
||||
#include "../../../libs/frame.h"
|
||||
|
||||
#include "huffman.h"
|
||||
@@ -43,7 +43,7 @@ static bool _is_huffman(const u8 *data);
|
||||
|
||||
|
||||
void us_hw_encoder_compress(const us_frame_s *src, us_frame_s *dest) {
|
||||
assert(us_is_jpeg(src->format));
|
||||
US_A(us_is_jpeg(src->format));
|
||||
_copy_plus_huffman(src, dest);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ static void _gpio_output_destroy(us_gpio_output_s *out);
|
||||
|
||||
void us_gpio_init(void) {
|
||||
# ifndef HAVE_GPIOD2
|
||||
assert(us_g_gpio.chip == NULL);
|
||||
US_A(us_g_gpio.chip == NULL);
|
||||
# endif
|
||||
if (
|
||||
us_g_gpio.prog_running.pin >= 0
|
||||
@@ -96,10 +96,10 @@ int us_gpio_inner_set(us_gpio_output_s *out, bool on) {
|
||||
int retval = 0;
|
||||
|
||||
# ifndef HAVE_GPIOD2
|
||||
assert(us_g_gpio.chip != NULL);
|
||||
US_A(us_g_gpio.chip != NULL);
|
||||
# endif
|
||||
assert(out->line != NULL);
|
||||
assert(out->on != on); // Must be checked in macro for the performance
|
||||
US_A(out->line != NULL);
|
||||
US_A(out->on != on); // Must be checked in macro for the performance
|
||||
US_MUTEX_LOCK(us_g_gpio.mutex);
|
||||
|
||||
# ifdef HAVE_GPIOD2
|
||||
@@ -117,24 +117,24 @@ int us_gpio_inner_set(us_gpio_output_s *out, bool on) {
|
||||
}
|
||||
|
||||
static void _gpio_output_init(us_gpio_output_s *out, struct gpiod_chip *chip) {
|
||||
assert(out->line == NULL);
|
||||
US_A(out->line == NULL);
|
||||
|
||||
US_ASPRINTF(out->consumer, "%s::%s", us_g_gpio.consumer_prefix, out->role);
|
||||
|
||||
if (out->pin >= 0) {
|
||||
# ifdef HAVE_GPIOD2
|
||||
struct gpiod_line_settings *line_settings;
|
||||
assert(line_settings = gpiod_line_settings_new());
|
||||
assert(!gpiod_line_settings_set_direction(line_settings, GPIOD_LINE_DIRECTION_OUTPUT));
|
||||
assert(!gpiod_line_settings_set_output_value(line_settings, false));
|
||||
US_A(line_settings = gpiod_line_settings_new());
|
||||
US_A(!gpiod_line_settings_set_direction(line_settings, GPIOD_LINE_DIRECTION_OUTPUT));
|
||||
US_A(!gpiod_line_settings_set_output_value(line_settings, false));
|
||||
|
||||
struct gpiod_line_config *line_config;
|
||||
assert(line_config = gpiod_line_config_new());
|
||||
US_A(line_config = gpiod_line_config_new());
|
||||
const unsigned offset = out->pin;
|
||||
assert(!gpiod_line_config_add_line_settings(line_config, &offset, 1, line_settings));
|
||||
US_A(!gpiod_line_config_add_line_settings(line_config, &offset, 1, line_settings));
|
||||
|
||||
struct gpiod_request_config *req_config;
|
||||
assert(req_config = gpiod_request_config_new());
|
||||
US_A(req_config = gpiod_request_config_new());
|
||||
gpiod_request_config_set_consumer(req_config, out->consumer);
|
||||
|
||||
if ((out->line = gpiod_chip_request_lines(chip, req_config, line_config)) == NULL) {
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <gpiod.h>
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -102,12 +101,12 @@ static bool _expose_frame(us_server_s *server, const us_frame_s *frame);
|
||||
#define _LOG_VERBOSE(x_msg, ...) US_LOG_VERBOSE("HTTP: " x_msg, ##__VA_ARGS__)
|
||||
#define _LOG_DEBUG(x_msg, ...) US_LOG_DEBUG("HTTP: " x_msg, ##__VA_ARGS__)
|
||||
|
||||
#define _A_EVBUFFER_NEW(x_buf) assert((x_buf = evbuffer_new()) != NULL)
|
||||
#define _A_EVBUFFER_ADD(x_buf, x_data, x_size) assert(!evbuffer_add(x_buf, x_data, x_size))
|
||||
#define _A_EVBUFFER_ADD_PRINTF(x_buf, x_fmt, ...) assert(evbuffer_add_printf(x_buf, x_fmt, ##__VA_ARGS__) >= 0)
|
||||
#define _A_EVBUFFER_NEW(x_buf) US_A((x_buf = evbuffer_new()) != NULL)
|
||||
#define _A_EVBUFFER_ADD(x_buf, x_data, x_size) US_A(!evbuffer_add(x_buf, x_data, x_size))
|
||||
#define _A_EVBUFFER_ADD_PRINTF(x_buf, x_fmt, ...) US_A(evbuffer_add_printf(x_buf, x_fmt, ##__VA_ARGS__) >= 0)
|
||||
|
||||
#define _A_ADD_HEADER(x_req, x_key, x_value) \
|
||||
assert(!evhttp_add_header(evhttp_request_get_output_headers(x_req), x_key, x_value))
|
||||
US_A(!evhttp_add_header(evhttp_request_get_output_headers(x_req), x_key, x_value))
|
||||
|
||||
|
||||
us_server_s *us_server_init(us_stream_s *stream) {
|
||||
@@ -135,9 +134,9 @@ us_server_s *us_server_init(us_stream_s *stream) {
|
||||
server->stream = stream;
|
||||
server->run = run;
|
||||
|
||||
assert(!evthread_use_pthreads());
|
||||
assert((run->base = event_base_new()) != NULL);
|
||||
assert((run->http = evhttp_new(run->base)) != NULL);
|
||||
US_A(!evthread_use_pthreads());
|
||||
US_A((run->base = event_base_new()) != NULL);
|
||||
US_A((run->http = evhttp_new(run->base)) != NULL);
|
||||
evhttp_set_allowed_methods(run->http, EVHTTP_REQ_GET|EVHTTP_REQ_HEAD|EVHTTP_REQ_OPTIONS);
|
||||
return server;
|
||||
}
|
||||
@@ -188,17 +187,17 @@ int us_server_listen(us_server_s *server) {
|
||||
_LOG_INFO("Enabling the file server: %s", server->static_path);
|
||||
evhttp_set_gencb(run->http, _http_callback_static, (void*)server);
|
||||
} else {
|
||||
assert(!evhttp_set_cb(run->http, "/", _http_callback_root, (void*)server));
|
||||
assert(!evhttp_set_cb(run->http, "/favicon.ico", _http_callback_favicon, (void*)server));
|
||||
US_A(!evhttp_set_cb(run->http, "/", _http_callback_root, (void*)server));
|
||||
US_A(!evhttp_set_cb(run->http, "/favicon.ico", _http_callback_favicon, (void*)server));
|
||||
}
|
||||
assert(!evhttp_set_cb(run->http, "/state", _http_callback_state, (void*)server));
|
||||
assert(!evhttp_set_cb(run->http, "/snapshot", _http_callback_snapshot, (void*)server));
|
||||
assert(!evhttp_set_cb(run->http, "/stream", _http_callback_stream, (void*)server));
|
||||
US_A(!evhttp_set_cb(run->http, "/state", _http_callback_state, (void*)server));
|
||||
US_A(!evhttp_set_cb(run->http, "/snapshot", _http_callback_snapshot, (void*)server));
|
||||
US_A(!evhttp_set_cb(run->http, "/stream", _http_callback_stream, (void*)server));
|
||||
}
|
||||
|
||||
us_frame_copy(stream->run->blank->jpeg, ex->frame);
|
||||
|
||||
assert((run->refresher = event_new(run->base, -1, 0, _http_refresher, server)) != NULL);
|
||||
US_A((run->refresher = event_new(run->base, -1, 0, _http_refresher, server)) != NULL);
|
||||
stream->run->http->jpeg_refresher = run->refresher;
|
||||
|
||||
evhttp_set_timeout(run->http, server->timeout);
|
||||
@@ -612,7 +611,7 @@ static void _http_callback_stream(struct evhttp_request *req, void *v_server) {
|
||||
if (server->tcp_nodelay && run->ext_fd >= 0) {
|
||||
_LOG_DEBUG("Setting up TCP_NODELAY to the client %s ...", client->hostport);
|
||||
const evutil_socket_t fd = bufferevent_getfd(buf_event);
|
||||
assert(fd >= 0);
|
||||
US_A(fd >= 0);
|
||||
int on = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) != 0) {
|
||||
_LOG_PERROR("Can't set TCP_NODELAY to the client %s", client->hostport);
|
||||
@@ -703,7 +702,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
|
||||
ADD_ADVANCE_HEADERS;
|
||||
}
|
||||
|
||||
assert(!bufferevent_write_buffer(buf_event, buf));
|
||||
US_A(!bufferevent_write_buffer(buf_event, buf));
|
||||
client->need_initial = false;
|
||||
}
|
||||
|
||||
@@ -763,7 +762,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
|
||||
ADD_ADVANCE_HEADERS;
|
||||
}
|
||||
|
||||
assert(!bufferevent_write_buffer(buf_event, buf));
|
||||
US_A(!bufferevent_write_buffer(buf_event, buf));
|
||||
evbuffer_free(buf);
|
||||
|
||||
bufferevent_setcb(buf_event, NULL, NULL, _http_callback_stream_error, (void*)client);
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
@@ -46,7 +45,7 @@ char *us_find_static_file_path(const char *root_path, const char *req_path) {
|
||||
}
|
||||
|
||||
US_CALLOC(path, strlen(root_path) + strlen(simplified_path) + 16); // + reserved for /index.html
|
||||
assert(sprintf(path, "%s/%s", root_path, simplified_path) > 0);
|
||||
US_A(sprintf(path, "%s/%s", root_path, simplified_path) > 0);
|
||||
|
||||
struct stat st;
|
||||
# define LOAD_STAT { \
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "systemd.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <event2/http.h>
|
||||
#include <event2/util.h>
|
||||
@@ -47,7 +46,7 @@ evutil_socket_t us_evhttp_bind_systemd(struct evhttp *http) {
|
||||
}
|
||||
fd = SD_LISTEN_FDS_START;
|
||||
|
||||
assert(!evutil_make_socket_nonblocking(fd));
|
||||
US_A(!evutil_make_socket_nonblocking(fd));
|
||||
|
||||
if (evhttp_accept_socket(http, fd) < 0) {
|
||||
US_LOG_PERROR("HTTP: Can't evhttp_accept_socket() systemd socket");
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
@@ -54,8 +53,8 @@ evutil_socket_t us_evhttp_bind_unix(struct evhttp *http, const char *path, bool
|
||||
addr.sun_family = AF_UNIX;
|
||||
|
||||
const evutil_socket_t fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
assert(fd >= 0);
|
||||
assert(!evutil_make_socket_nonblocking(fd));
|
||||
US_A(fd >= 0);
|
||||
US_A(!evutil_make_socket_nonblocking(fd));
|
||||
|
||||
if (rm && unlink(path) < 0) {
|
||||
if (errno != ENOENT) {
|
||||
@@ -99,7 +98,7 @@ char *us_evhttp_get_hostport(struct evhttp_request *req) {
|
||||
const char *xff = us_evhttp_get_header(req, "X-Forwarded-For");
|
||||
if (xff != NULL) {
|
||||
US_DELETE(addr, free);
|
||||
assert((addr = strndup(xff, 1024)) != NULL);
|
||||
US_A((addr = strndup(xff, 1024)) != NULL);
|
||||
for (uint i = 0; addr[i]; ++i) {
|
||||
if (addr[i] == ',') {
|
||||
addr[i] = '\0';
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
@@ -89,7 +88,7 @@ us_m2m_encoder_s *us_m2m_mjpeg_encoder_init(const char *name, const char *path,
|
||||
double bitrate = log10(quality) * (b_max - b_min) / 2 + b_min;
|
||||
bitrate = step * round(bitrate / step);
|
||||
bitrate *= 1000; // From Kbps
|
||||
assert(bitrate > 0);
|
||||
US_A(bitrate > 0);
|
||||
return _m2m_encoder_init(name, path, V4L2_PIX_FMT_MJPEG, bitrate, 0, 0, true, false);
|
||||
}
|
||||
|
||||
@@ -417,7 +416,7 @@ static int _m2m_encoder_init_buffers(
|
||||
_LOG_PERROR("Can't map %s buffer=%u", name, *n_bufs_ptr);
|
||||
goto error;
|
||||
}
|
||||
assert((*bufs_ptr)[*n_bufs_ptr].data != NULL);
|
||||
US_A((*bufs_ptr)[*n_bufs_ptr].data != NULL);
|
||||
(*bufs_ptr)[*n_bufs_ptr].allocated = plane.length;
|
||||
|
||||
_LOG_DEBUG("Queuing %s buffer=%u ...", name, *n_bufs_ptr);
|
||||
@@ -492,7 +491,7 @@ static int _m2m_encoder_compress_raw(
|
||||
) {
|
||||
us_m2m_encoder_runtime_s *const run = enc->run;
|
||||
|
||||
assert(run->ready);
|
||||
US_A(run->ready);
|
||||
|
||||
if (force_key) {
|
||||
struct v4l2_control ctl = {0};
|
||||
|
||||
@@ -46,10 +46,10 @@ static us_server_s *_g_server = NULL;
|
||||
|
||||
static void _block_thread_signals(void) {
|
||||
sigset_t mask;
|
||||
assert(!sigemptyset(&mask));
|
||||
assert(!sigaddset(&mask, SIGINT));
|
||||
assert(!sigaddset(&mask, SIGTERM));
|
||||
assert(!pthread_sigmask(SIG_BLOCK, &mask, NULL));
|
||||
US_A(!sigemptyset(&mask));
|
||||
US_A(!sigaddset(&mask, SIGINT));
|
||||
US_A(!sigaddset(&mask, SIGTERM));
|
||||
US_A(!pthread_sigmask(SIG_BLOCK, &mask, NULL));
|
||||
}
|
||||
|
||||
static void *_stream_loop_thread(void *arg) {
|
||||
@@ -77,7 +77,7 @@ static void _signal_handler(int signum) {
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
assert(argc >= 0);
|
||||
US_A(argc >= 0);
|
||||
int exit_code = 0;
|
||||
|
||||
US_LOGGING_INIT;
|
||||
|
||||
@@ -30,10 +30,10 @@
|
||||
#include <limits.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "../libs/types.h"
|
||||
#include "../libs/const.h"
|
||||
#include "../libs/tools.h"
|
||||
#include "../libs/logging.h"
|
||||
#include "../libs/process.h"
|
||||
#include "../libs/frame.h"
|
||||
@@ -300,10 +300,11 @@ us_options_s *us_options_init(uint argc, char *argv[]) {
|
||||
opts->argc = argc;
|
||||
opts->argv = argv;
|
||||
|
||||
US_CALLOC(opts->argv_copy, argc);
|
||||
US_CALLOC(opts->argv_copy, argc + 1);
|
||||
for (uint i = 0; i < argc; ++i) {
|
||||
opts->argv_copy[i] = us_strdup(argv[i]);
|
||||
}
|
||||
opts->argv_copy[argc] = NULL;
|
||||
return opts;
|
||||
}
|
||||
|
||||
@@ -358,7 +359,7 @@ int us_options_parse(
|
||||
printf("Invalid height of '%s=%s': min=%u, max=%u\n", x_name, optarg, US_VIDEO_MIN_HEIGHT, US_VIDEO_MAX_HEIGHT); \
|
||||
return -1; \
|
||||
case 0: break; \
|
||||
default: assert(0 && "Unknown error"); \
|
||||
default: US_RAISE("Unknown error"); \
|
||||
} \
|
||||
break; \
|
||||
}
|
||||
@@ -704,7 +705,7 @@ static void _help(
|
||||
SAY(" Changing of this parameter may increase the performance. Or not.");
|
||||
SAY(" Available: %s; default: MMAP.\n", US_IO_METHODS_STR);
|
||||
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 than this limit. Useful if the device");
|
||||
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");
|
||||
@@ -712,7 +713,7 @@ static void _help(
|
||||
SAY(" -t|--dv-timings ───────────────────── Enable DV-timings querying and events processing");
|
||||
SAY(" to automatic resolution change. Default: disabled.\n");
|
||||
SAY(" -b|--buffers <N> ──────────────────── The number of buffers to receive data from the device.");
|
||||
SAY(" Each buffer may processed using an independent thread.");
|
||||
SAY(" Each buffer may be processed using an independent thread.");
|
||||
SAY(" Default: %u (the number of CPU cores (but not more than 4) + 1).\n", cap->n_bufs);
|
||||
SAY(" -w|--workers <N> ──────────────────── The number of worker threads but not more than buffers.");
|
||||
SAY(" Default: %u (the number of CPU cores (but not more than 4)).\n", enc->n_workers);
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <pthread.h>
|
||||
@@ -199,7 +198,7 @@ void us_stream_loop(us_stream_s *stream) {
|
||||
CREATE_WORKER((stream->raw_sink != NULL), raw_ctx, _raw_thread, 2);
|
||||
CREATE_WORKER((stream->h264_sink != NULL), h264_ctx, _h264_thread, cap->run->n_bufs);
|
||||
# ifdef WITH_V4P
|
||||
CREATE_WORKER((stream->drm != NULL), drm_ctx, _drm_thread, cap->run->n_bufs); // cppcheck-suppress assertWithSideEffect
|
||||
CREATE_WORKER((stream->drm != NULL), drm_ctx, _drm_thread, cap->run->n_bufs);
|
||||
# endif
|
||||
# undef CREATE_WORKER
|
||||
|
||||
@@ -273,7 +272,7 @@ void us_stream_loop(us_stream_s *stream) {
|
||||
us_capture_close(cap);
|
||||
|
||||
if (!atomic_load(&run->stop)) {
|
||||
US_SEP_INFO('=');
|
||||
US_LOG_SEP_INFO;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,7 +527,7 @@ static us_capture_hwbuf_s *_get_latest_hw(us_queue_s *q) {
|
||||
}
|
||||
while (!us_queue_is_empty(q)) { // Берем только самый свежий кадр
|
||||
us_capture_hwbuf_decref(hw);
|
||||
assert(!us_queue_get(q, (void**)&hw, 0));
|
||||
US_A(!us_queue_get(q, (void**)&hw, 0));
|
||||
}
|
||||
return hw;
|
||||
}
|
||||
@@ -771,7 +770,7 @@ static void _stream_check_suicide(us_stream_s *stream) {
|
||||
return;
|
||||
}
|
||||
|
||||
const atomic_ullong *last_req_ts = &stream->run->http->last_req_ts;
|
||||
atomic_ullong *last_req_ts = &stream->run->http->last_req_ts;
|
||||
const ldf now_ts = us_get_now_monotonic();
|
||||
|
||||
if (_stream_has_any_clients_cached(stream)) {
|
||||
|
||||
@@ -122,7 +122,7 @@ us_worker_s *us_workers_pool_wait(us_workers_pool_s *pool) {
|
||||
found = wr;
|
||||
}
|
||||
});
|
||||
assert(found != NULL);
|
||||
US_A(found != NULL);
|
||||
US_LIST_REMOVE(pool->workers, found);
|
||||
US_LIST_APPEND(pool->workers, found); // Перемещаем в конец списка
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
@@ -245,11 +244,11 @@ static void _main_loop(void) {
|
||||
static void *_follower_thread(void *v_unix_follow) { // cppcheck-suppress constParameterCallback
|
||||
US_THREAD_SETTLE("follower");
|
||||
const char *path = v_unix_follow;
|
||||
assert(path != NULL);
|
||||
US_A(path != NULL);
|
||||
|
||||
while (!atomic_load(&_g_stop)) {
|
||||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
assert(fd >= 0);
|
||||
US_A(fd >= 0);
|
||||
|
||||
struct sockaddr_un addr = {0};
|
||||
strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
|
||||
|
||||
Reference in New Issue
Block a user