Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[bumpversion]
commit = True
tag = True
current_version = 6.36
current_version = 6.44
parse = (?P<major>\d+)\.(?P<minor>\d+)
serialize =
{major}.{minor}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
|----------|---------------|-------------------|
| Multithreaded JPEG encoding | ✔ | ✘ |
| Hardware image encoding<br>on Raspberry Pi | ✔ | ✘ |
| Behavior when the device<br>is disconnected while streaming | ✔ Shows a black screen<br>with ```NO SIGNAL``` on it<br>until reconnected | ✘ Stops the streaming <sup>1</sup> |
| Behavior when the device<br>is disconnected while streaming | ✔ Shows a black screen<br>with ```NO LIVE VIDEO``` on it<br>until reconnected | ✘ Stops the streaming <sup>1</sup> |
| [DV-timings](https://linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/v4l/dv-timings.html) support -<br>the ability to change resolution<br>on the fly by source signal | ✔ | ☹ Partially yes <sup>1</sup> |
| Option to skip frames when streaming<br>static images by HTTP to save the traffic | ✔ <sup>2</sup> | ✘ |
| Streaming via UNIX domain socket | ✔ | ✘ |
Expand Down
4 changes: 2 additions & 2 deletions janus/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ LDFLAGS ?=
# =====
_PLUGIN = libjanus_ustreamer.so

_CFLAGS = -fPIC -MD -c -std=c17 -Wall -Wextra -D_GNU_SOURCE $(shell $(PKG_CONFIG) --cflags glib-2.0) $(CFLAGS)
_LDFLAGS = -shared -lm -pthread -lrt -ljansson -lopus -lasound -lspeexdsp $(shell $(PKG_CONFIG) --libs glib-2.0) $(LDFLAGS)
_CFLAGS = -fPIC -MD -c -std=c17 -Wall -Wextra -D_GNU_SOURCE $(shell $(PKG_CONFIG) --cflags janus-gateway) $(CFLAGS)
_LDFLAGS = -shared -lm -pthread -lrt -ljansson -lopus -lasound -lspeexdsp $(shell $(PKG_CONFIG) --libs janus-gateway) $(LDFLAGS)

_SRCS = $(shell ls src/uslibs/*.c src/*.c)

Expand Down
13 changes: 0 additions & 13 deletions janus/src/acap.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,6 @@ static void *_pcm_thread(void *v_acap);
static void *_encoder_thread(void *v_acap);


bool us_acap_probe(const char *name) {
snd_pcm_t *dev;
int err;
US_JLOG_INFO("acap", "Probing PCM capture ...");
if ((err = snd_pcm_open(&dev, name, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
US_JLOG_PERROR_ALSA(err, "acap", "Can't probe PCM capture");
return false;
}
snd_pcm_close(dev);
US_JLOG_INFO("acap", "PCM capture is available");
return true;
}

us_acap_s *us_acap_init(const char *name, uint pcm_hz) {
us_acap_s *acap;
US_CALLOC(acap, 1);
Expand Down
2 changes: 0 additions & 2 deletions janus/src/acap.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ typedef struct {
} us_acap_s;


bool us_acap_probe(const char *name);

us_acap_s *us_acap_init(const char *name, uint pcm_hz);
void us_acap_destroy(us_acap_s *acap);

Expand Down
68 changes: 68 additions & 0 deletions janus/src/au.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,78 @@
#include "au.h"

#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <sys/stat.h>

#include "uslibs/tools.h"


bool us_au_probe(const char *name) {
// This function is very limited. It takes something like:
// hw:0,0 or hw:tc358743,0 or plughw:UAC2Gadget,0
// parses card name (0, tc358743, UAC2Gadget) and checks
// the existence of it in /proc/asound/.
// It's enough for our case.

if (name == NULL) {
return false;
}

if (strchr(name, '/') || strchr(name, '.')) {
return false;
}

const char *begin = strchr(name, ':');
if (begin == NULL) {
return false;
}
begin += 1;
if (*begin == '\0') {
return false;
}

const char *end = strchr(begin, ',');
if (end == NULL) {
return false;
}
if (end - begin < 1) {
return false;
}

char *card = us_strdup(begin);
card[end - begin] = '\0';

bool numeric = true;
for (uz index = 0; card[index] != '\0'; ++index) {
if (!isdigit(card[index])) {
numeric = false;
break;
}
}

char *path;
if (numeric) {
US_ASPRINTF(path, "/proc/asound/card%s", card);
} else {
US_ASPRINTF(path, "/proc/asound/%s", card);
}

bool ok = false;
struct stat st;
if (lstat(path, &st) == 0) {
if (numeric && S_ISDIR(st.st_mode)) {
ok = true;
} else if (!numeric && S_ISLNK(st.st_mode)) {
ok = true;
}
}
free(path);
free(card);
return ok;
}

us_au_pcm_s *us_au_pcm_init(void) {
us_au_pcm_s *pcm;
US_CALLOC(pcm, 1);
Expand Down
1 change: 1 addition & 0 deletions janus/src/au.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ typedef struct {
u64 pts;
} us_au_encoded_s;

bool us_au_probe(const char *name);

us_au_pcm_s *us_au_pcm_init(void);
void us_au_pcm_destroy(us_au_pcm_s *pcm);
Expand Down
34 changes: 21 additions & 13 deletions janus/src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,19 @@

#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <janus/config.h>
#include <janus/plugins/plugin.h>

#include "uslibs/types.h"
#include "uslibs/tools.h"

#include "const.h"
#include "logging.h"


static char *_get_value(janus_config *jcfg, const char *section, const char *option);
static uint _get_uint(janus_config *jcfg, const char *section, const char *option, uint def);
// static bool _get_bool(janus_config *jcfg, const char *section, const char *option, bool def);


Expand All @@ -61,20 +62,13 @@ us_config_s *us_config_init(const char *config_dir_path) {
goto error;
}
if ((config->acap_dev_name = _get_value(jcfg, "acap", "device")) != NULL) {
if ((config->tc358743_dev_path = _get_value(jcfg, "acap", "tc358743")) == NULL) {
US_JLOG_INFO("config", "Missing config value: acap.tc358743");
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");
goto error;
}
if ((config->aplay_dev_name = _get_value(jcfg, "aplay", "device")) != NULL) {
char *path = _get_value(jcfg, "aplay", "check");
if (path != NULL) {
if (access(path, F_OK) != 0) {
US_JLOG_INFO("config", "No check file found, aplay will be disabled");
US_DELETE(config->aplay_dev_name, free);
}
US_DELETE(path, free);
}
}
config->aplay_dev_name = _get_value(jcfg, "aplay", "device");
}

goto ok;
Expand Down Expand Up @@ -105,6 +99,20 @@ static char *_get_value(janus_config *jcfg, const char *section, const char *opt
return us_strdup(option_obj->value);
}

static uint _get_uint(janus_config *jcfg, const char *section, const char *option, uint def) {
char *const tmp = _get_value(jcfg, section, option);
uint value = def;
if (tmp != NULL) {
errno = 0;
value = (uint)strtoul(tmp, NULL, 10);
if (errno != 0) {
value = def;
}
free(tmp);
}
return value;
}

/*static bool _get_bool(janus_config *jcfg, const char *section, const char *option, bool def) {
char *const tmp = _get_value(jcfg, section, option);
bool value = def;
Expand Down
4 changes: 4 additions & 0 deletions janus/src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@
#pragma once


#include "uslibs/types.h"


typedef struct {
char *video_sink_name;

char *acap_dev_name;
uint acap_hz;
char *tc358743_dev_path;

char *aplay_dev_name;
Expand Down
37 changes: 27 additions & 10 deletions janus/src/plugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ static us_janus_client_s *_g_clients = NULL;
static janus_callbacks *_g_gw = NULL;
static us_ring_s *_g_video_ring = NULL;
static us_rtpv_s *_g_rtpv = NULL;
static us_rtpa_s *_g_rtpa = NULL; // Also indicates "audio capture is available"
static us_rtpa_s *_g_rtpa = NULL;

static pthread_t _g_video_rtp_tid;
static atomic_bool _g_video_rtp_tid_created = false;
Expand Down Expand Up @@ -214,7 +214,15 @@ static void *_video_sink_thread(void *arg) {
return NULL;
}

static int _check_tc358743_acap(uint *hz) {
static int _get_acap_hz(uint *hz) {
if (_g_config->acap_hz != 0) {
*hz = _g_config->acap_hz;
return 0;
}
if (_g_config->tc358743_dev_path == NULL) {
US_JLOG_ERROR("acap", "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");
Expand All @@ -236,7 +244,6 @@ static void *_acap_thread(void *arg) {
atomic_store(&_g_acap_tid_created, true);

assert(_g_config->acap_dev_name != NULL);
assert(_g_config->tc358743_dev_path != NULL);
assert(_g_rtpa != NULL);

int once = 0;
Expand All @@ -250,7 +257,11 @@ static void *_acap_thread(void *arg) {
uint hz = 0;
us_acap_s *acap = NULL;

if (_check_tc358743_acap(&hz) < 0) {
if (!us_au_probe(_g_config->acap_dev_name)) {
US_ONCE({ US_JLOG_ERROR("acap", "No PCM capture device"); });
goto close_acap;
}
if (_get_acap_hz(&hz) < 0) {
goto close_acap;
}
if (hz == 0) {
Expand All @@ -265,7 +276,7 @@ static void *_acap_thread(void *arg) {
once = 0;

while (!_STOP && _HAS_WATCHERS && _HAS_LISTENERS) {
if (_check_tc358743_acap(&hz) < 0 || acap->pcm_hz != hz) {
if (_get_acap_hz(&hz) < 0 || acap->pcm_hz != hz) {
goto close_acap;
}
uz size = US_RTP_DATAGRAM_SIZE - US_RTP_HEADER_SIZE;
Expand Down Expand Up @@ -339,6 +350,11 @@ static void *_aplay_thread(void *arg) {
}

if (dev == NULL) {
if (!us_au_probe(_g_config->aplay_dev_name)) {
US_ONCE({ US_JLOG_ERROR("aplay", "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"); });
Expand Down Expand Up @@ -424,7 +440,7 @@ static int _plugin_init(janus_callbacks *gw, const char *config_dir_path) {

US_RING_INIT_WITH_ITEMS(_g_video_ring, 64, us_frame_init);
_g_rtpv = us_rtpv_init(_relay_rtp_clients);
if (_g_config->acap_dev_name != NULL && us_acap_probe(_g_config->acap_dev_name)) {
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) {
Expand Down Expand Up @@ -602,13 +618,13 @@ static struct janus_plugin_result *_plugin_handle_message(
{
json_t *const obj = json_object_get(params, "audio");
if (obj != NULL && json_is_boolean(obj)) {
with_acap = (_g_rtpa != NULL && json_boolean_value(obj));
with_acap = (us_au_probe(_g_config->acap_dev_name) && json_boolean_value(obj));
}
}
{
json_t *const obj = json_object_get(params, "mic");
if (obj != NULL && json_is_boolean(obj)) {
with_aplay = (_g_config->aplay_dev_name != NULL && with_acap && json_boolean_value(obj));
with_aplay = (us_au_probe(_g_config->aplay_dev_name) && json_boolean_value(obj));
}
}
{
Expand Down Expand Up @@ -673,10 +689,11 @@ 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", (_g_rtpa != NULL),
"mic", (_g_rtpa != NULL && _g_config->aplay_dev_name != NULL),
"audio", acap_avail,
"mic", (acap_avail && us_au_probe(_g_config->aplay_dev_name)),
"ice", "url", (ice_url != NULL ? ice_url : default_ice_url)
);
PUSH_STATUS("features", features, NULL);
Expand Down
2 changes: 2 additions & 0 deletions janus/src/rtpa.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ char *us_rtpa_make_sdp(us_rtpa_s *rtpa, bool mic) {
"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,
Expand Down
2 changes: 2 additions & 0 deletions janus/src/rtpv.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ char *us_rtpv_make_sdp(us_rtpv_s *rtpv) {
"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
Expand Down
2 changes: 1 addition & 1 deletion man/ustreamer-dump.1
Original file line number Diff line number Diff line change
@@ -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.36" "January 2021"
.TH USTREAMER-DUMP 1 "version 6.44" "January 2021"

.SH NAME
ustreamer-dump \- Dump uStreamer's memory sink to file
Expand Down
5 changes: 4 additions & 1 deletion man/ustreamer.1
Original file line number Diff line number Diff line change
@@ -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.36" "November 2020"
.TH USTREAMER 1 "version 6.44" "November 2020"

.SH NAME
ustreamer \- stream MJPEG video from any V4L2 device to the network
Expand Down Expand Up @@ -276,6 +276,9 @@ Timeout for lock. Default: 1.
.BR \-\-exit\-on\-parent\-death
Exit the program if the parent process is dead. Required \fBWITH_PDEATHSIG\fR feature. Default: disabled.
.TP
.BR \-\-exit\-on\-device\-error
Exit on any device error instead of polling until success. Default: disabled.
.TP
.BR \-\-exit\-on\-no\-clients \fIsec
Exit the program if there have been no stream or sink clients or any HTTP requests in the last N seconds. Default: 0 (disabled).
.TP
Expand Down
2 changes: 1 addition & 1 deletion pkg/arch/PKGBUILD
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@


pkgname=ustreamer
pkgver=6.36
pkgver=6.44
pkgrel=1
pkgdesc="Lightweight and fast MJPEG-HTTP streamer"
url="https://github.com/pikvm/ustreamer"
Expand Down
2 changes: 1 addition & 1 deletion pkg/openwrt/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
include $(TOPDIR)/rules.mk

PKG_NAME:=ustreamer
PKG_VERSION:=6.36
PKG_VERSION:=6.44
PKG_RELEASE:=1
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>

Expand Down
Loading