mirror of
https://github.com/pikvm/ustreamer.git
synced 2025-12-23 18:50:00 +00:00
janus: plug audio devices dynamically
This commit is contained in:
parent
472673ea90
commit
c4ac67acba
@ -47,19 +47,6 @@ static void *_pcm_thread(void *v_acap);
|
|||||||
static void *_encoder_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 *us_acap_init(const char *name, uint pcm_hz) {
|
||||||
us_acap_s *acap;
|
us_acap_s *acap;
|
||||||
US_CALLOC(acap, 1);
|
US_CALLOC(acap, 1);
|
||||||
|
|||||||
@ -53,8 +53,6 @@ typedef struct {
|
|||||||
} us_acap_s;
|
} us_acap_s;
|
||||||
|
|
||||||
|
|
||||||
bool us_acap_probe(const char *name);
|
|
||||||
|
|
||||||
us_acap_s *us_acap_init(const char *name, uint pcm_hz);
|
us_acap_s *us_acap_init(const char *name, uint pcm_hz);
|
||||||
void us_acap_destroy(us_acap_s *acap);
|
void us_acap_destroy(us_acap_s *acap);
|
||||||
|
|
||||||
|
|||||||
@ -23,10 +23,78 @@
|
|||||||
#include "au.h"
|
#include "au.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "uslibs/tools.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 *us_au_pcm_init(void) {
|
||||||
us_au_pcm_s *pcm;
|
us_au_pcm_s *pcm;
|
||||||
US_CALLOC(pcm, 1);
|
US_CALLOC(pcm, 1);
|
||||||
|
|||||||
@ -51,6 +51,7 @@ typedef struct {
|
|||||||
u64 pts;
|
u64 pts;
|
||||||
} us_au_encoded_s;
|
} us_au_encoded_s;
|
||||||
|
|
||||||
|
bool us_au_probe(const char *name);
|
||||||
|
|
||||||
us_au_pcm_s *us_au_pcm_init(void);
|
us_au_pcm_s *us_au_pcm_init(void);
|
||||||
void us_au_pcm_destroy(us_au_pcm_s *pcm);
|
void us_au_pcm_destroy(us_au_pcm_s *pcm);
|
||||||
|
|||||||
@ -69,7 +69,7 @@ static us_janus_client_s *_g_clients = NULL;
|
|||||||
static janus_callbacks *_g_gw = NULL;
|
static janus_callbacks *_g_gw = NULL;
|
||||||
static us_ring_s *_g_video_ring = NULL;
|
static us_ring_s *_g_video_ring = NULL;
|
||||||
static us_rtpv_s *_g_rtpv = 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 pthread_t _g_video_rtp_tid;
|
||||||
static atomic_bool _g_video_rtp_tid_created = false;
|
static atomic_bool _g_video_rtp_tid_created = false;
|
||||||
@ -250,6 +250,10 @@ static void *_acap_thread(void *arg) {
|
|||||||
uint hz = 0;
|
uint hz = 0;
|
||||||
us_acap_s *acap = NULL;
|
us_acap_s *acap = NULL;
|
||||||
|
|
||||||
|
if (!us_au_probe(_g_config->acap_dev_name)) {
|
||||||
|
US_ONCE({ US_JLOG_ERROR("acap", "No PCM capture device"); });
|
||||||
|
goto close_acap;
|
||||||
|
}
|
||||||
if (_check_tc358743_acap(&hz) < 0) {
|
if (_check_tc358743_acap(&hz) < 0) {
|
||||||
goto close_acap;
|
goto close_acap;
|
||||||
}
|
}
|
||||||
@ -339,6 +343,11 @@ static void *_aplay_thread(void *arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dev == NULL) {
|
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);
|
int err = snd_pcm_open(&dev, _g_config->aplay_dev_name, SND_PCM_STREAM_PLAYBACK, 0);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
US_ONCE({ US_JLOG_PERROR_ALSA(err, "aplay", "Can't open PCM playback"); });
|
US_ONCE({ US_JLOG_PERROR_ALSA(err, "aplay", "Can't open PCM playback"); });
|
||||||
@ -424,7 +433,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);
|
US_RING_INIT_WITH_ITEMS(_g_video_ring, 64, us_frame_init);
|
||||||
_g_rtpv = us_rtpv_init(_relay_rtp_clients);
|
_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);
|
_g_rtpa = us_rtpa_init(_relay_rtp_clients);
|
||||||
US_THREAD_CREATE(_g_acap_tid, _acap_thread, NULL);
|
US_THREAD_CREATE(_g_acap_tid, _acap_thread, NULL);
|
||||||
if (_g_config->aplay_dev_name != NULL) {
|
if (_g_config->aplay_dev_name != NULL) {
|
||||||
@ -602,13 +611,13 @@ static struct janus_plugin_result *_plugin_handle_message(
|
|||||||
{
|
{
|
||||||
json_t *const obj = json_object_get(params, "audio");
|
json_t *const obj = json_object_get(params, "audio");
|
||||||
if (obj != NULL && json_is_boolean(obj)) {
|
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");
|
json_t *const obj = json_object_get(params, "mic");
|
||||||
if (obj != NULL && json_is_boolean(obj)) {
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@ -673,10 +682,11 @@ static struct janus_plugin_result *_plugin_handle_message(
|
|||||||
|
|
||||||
} else if (!strcmp(request_str, "features")) {
|
} else if (!strcmp(request_str, "features")) {
|
||||||
const char *const ice_url = getenv("JANUS_USTREAMER_WEB_ICE_URL");
|
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(
|
json_t *const features = json_pack(
|
||||||
"{s:b, s:b, s:{s:s?}}",
|
"{s:b, s:b, s:{s:s?}}",
|
||||||
"audio", (_g_rtpa != NULL),
|
"audio", acap_avail,
|
||||||
"mic", (_g_rtpa != NULL && _g_config->aplay_dev_name != NULL),
|
"mic", (acap_avail && us_au_probe(_g_config->aplay_dev_name)),
|
||||||
"ice", "url", (ice_url != NULL ? ice_url : default_ice_url)
|
"ice", "url", (ice_url != NULL ? ice_url : default_ice_url)
|
||||||
);
|
);
|
||||||
PUSH_STATUS("features", features, NULL);
|
PUSH_STATUS("features", features, NULL);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user