Skip to content

Commit c4ac67a

Browse files
committed
janus: plug audio devices dynamically
1 parent 472673e commit c4ac67a

File tree

5 files changed

+85
-21
lines changed

5 files changed

+85
-21
lines changed

janus/src/acap.c

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,6 @@ static void *_pcm_thread(void *v_acap);
4747
static void *_encoder_thread(void *v_acap);
4848

4949

50-
bool us_acap_probe(const char *name) {
51-
snd_pcm_t *dev;
52-
int err;
53-
US_JLOG_INFO("acap", "Probing PCM capture ...");
54-
if ((err = snd_pcm_open(&dev, name, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
55-
US_JLOG_PERROR_ALSA(err, "acap", "Can't probe PCM capture");
56-
return false;
57-
}
58-
snd_pcm_close(dev);
59-
US_JLOG_INFO("acap", "PCM capture is available");
60-
return true;
61-
}
62-
6350
us_acap_s *us_acap_init(const char *name, uint pcm_hz) {
6451
us_acap_s *acap;
6552
US_CALLOC(acap, 1);

janus/src/acap.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,6 @@ typedef struct {
5353
} us_acap_s;
5454

5555

56-
bool us_acap_probe(const char *name);
57-
5856
us_acap_s *us_acap_init(const char *name, uint pcm_hz);
5957
void us_acap_destroy(us_acap_s *acap);
6058

janus/src/au.c

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,78 @@
2323
#include "au.h"
2424

2525
#include <stdlib.h>
26+
#include <string.h>
27+
#include <ctype.h>
28+
29+
#include <sys/stat.h>
2630

2731
#include "uslibs/tools.h"
2832

2933

34+
bool us_au_probe(const char *name) {
35+
// This function is very limited. It takes something like:
36+
// hw:0,0 or hw:tc358743,0 or plughw:UAC2Gadget,0
37+
// parses card name (0, tc358743, UAC2Gadget) and checks
38+
// the existence of it in /proc/asound/.
39+
// It's enough for our case.
40+
41+
if (name == NULL) {
42+
return false;
43+
}
44+
45+
if (strchr(name, '/') || strchr(name, '.')) {
46+
return false;
47+
}
48+
49+
const char *begin = strchr(name, ':');
50+
if (begin == NULL) {
51+
return false;
52+
}
53+
begin += 1;
54+
if (*begin == '\0') {
55+
return false;
56+
}
57+
58+
const char *end = strchr(begin, ',');
59+
if (end == NULL) {
60+
return false;
61+
}
62+
if (end - begin < 1) {
63+
return false;
64+
}
65+
66+
char *card = us_strdup(begin);
67+
card[end - begin] = '\0';
68+
69+
bool numeric = true;
70+
for (uz index = 0; card[index] != '\0'; ++index) {
71+
if (!isdigit(card[index])) {
72+
numeric = false;
73+
break;
74+
}
75+
}
76+
77+
char *path;
78+
if (numeric) {
79+
US_ASPRINTF(path, "/proc/asound/card%s", card);
80+
} else {
81+
US_ASPRINTF(path, "/proc/asound/%s", card);
82+
}
83+
84+
bool ok = false;
85+
struct stat st;
86+
if (lstat(path, &st) == 0) {
87+
if (numeric && S_ISDIR(st.st_mode)) {
88+
ok = true;
89+
} else if (!numeric && S_ISLNK(st.st_mode)) {
90+
ok = true;
91+
}
92+
}
93+
free(path);
94+
free(card);
95+
return ok;
96+
}
97+
3098
us_au_pcm_s *us_au_pcm_init(void) {
3199
us_au_pcm_s *pcm;
32100
US_CALLOC(pcm, 1);

janus/src/au.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ typedef struct {
5151
u64 pts;
5252
} us_au_encoded_s;
5353

54+
bool us_au_probe(const char *name);
5455

5556
us_au_pcm_s *us_au_pcm_init(void);
5657
void us_au_pcm_destroy(us_au_pcm_s *pcm);

janus/src/plugin.c

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ static us_janus_client_s *_g_clients = NULL;
6969
static janus_callbacks *_g_gw = NULL;
7070
static us_ring_s *_g_video_ring = NULL;
7171
static us_rtpv_s *_g_rtpv = NULL;
72-
static us_rtpa_s *_g_rtpa = NULL; // Also indicates "audio capture is available"
72+
static us_rtpa_s *_g_rtpa = NULL;
7373

7474
static pthread_t _g_video_rtp_tid;
7575
static atomic_bool _g_video_rtp_tid_created = false;
@@ -250,6 +250,10 @@ static void *_acap_thread(void *arg) {
250250
uint hz = 0;
251251
us_acap_s *acap = NULL;
252252

253+
if (!us_au_probe(_g_config->acap_dev_name)) {
254+
US_ONCE({ US_JLOG_ERROR("acap", "No PCM capture device"); });
255+
goto close_acap;
256+
}
253257
if (_check_tc358743_acap(&hz) < 0) {
254258
goto close_acap;
255259
}
@@ -339,6 +343,11 @@ static void *_aplay_thread(void *arg) {
339343
}
340344

341345
if (dev == NULL) {
346+
if (!us_au_probe(_g_config->aplay_dev_name)) {
347+
US_ONCE({ US_JLOG_ERROR("aplay", "No PCM playback device"); });
348+
goto close_aplay;
349+
}
350+
342351
int err = snd_pcm_open(&dev, _g_config->aplay_dev_name, SND_PCM_STREAM_PLAYBACK, 0);
343352
if (err < 0) {
344353
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) {
424433

425434
US_RING_INIT_WITH_ITEMS(_g_video_ring, 64, us_frame_init);
426435
_g_rtpv = us_rtpv_init(_relay_rtp_clients);
427-
if (_g_config->acap_dev_name != NULL && us_acap_probe(_g_config->acap_dev_name)) {
436+
if (_g_config->acap_dev_name != NULL) {
428437
_g_rtpa = us_rtpa_init(_relay_rtp_clients);
429438
US_THREAD_CREATE(_g_acap_tid, _acap_thread, NULL);
430439
if (_g_config->aplay_dev_name != NULL) {
@@ -602,13 +611,13 @@ static struct janus_plugin_result *_plugin_handle_message(
602611
{
603612
json_t *const obj = json_object_get(params, "audio");
604613
if (obj != NULL && json_is_boolean(obj)) {
605-
with_acap = (_g_rtpa != NULL && json_boolean_value(obj));
614+
with_acap = (us_au_probe(_g_config->acap_dev_name) && json_boolean_value(obj));
606615
}
607616
}
608617
{
609618
json_t *const obj = json_object_get(params, "mic");
610619
if (obj != NULL && json_is_boolean(obj)) {
611-
with_aplay = (_g_config->aplay_dev_name != NULL && with_acap && json_boolean_value(obj));
620+
with_aplay = (us_au_probe(_g_config->aplay_dev_name) && json_boolean_value(obj));
612621
}
613622
}
614623
{
@@ -673,10 +682,11 @@ static struct janus_plugin_result *_plugin_handle_message(
673682

674683
} else if (!strcmp(request_str, "features")) {
675684
const char *const ice_url = getenv("JANUS_USTREAMER_WEB_ICE_URL");
685+
const bool acap_avail = us_au_probe(_g_config->acap_dev_name);
676686
json_t *const features = json_pack(
677687
"{s:b, s:b, s:{s:s?}}",
678-
"audio", (_g_rtpa != NULL),
679-
"mic", (_g_rtpa != NULL && _g_config->aplay_dev_name != NULL),
688+
"audio", acap_avail,
689+
"mic", (acap_avail && us_au_probe(_g_config->aplay_dev_name)),
680690
"ice", "url", (ice_url != NULL ? ice_url : default_ice_url)
681691
);
682692
PUSH_STATUS("features", features, NULL);

0 commit comments

Comments
 (0)