Skip to content

Commit 28ce8de

Browse files
committed
mixer+camshow: layout and camshow audio
1 parent d15d668 commit 28ce8de

23 files changed

Lines changed: 767 additions & 319 deletions

File tree

CMakeLists-implied-options.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ if (LWS_WITH_WEBRTC)
196196
set(LWS_WITH_JOSE 1)
197197
set(LWS_WITH_NETWORK 1)
198198
set(LWS_WITH_CLIENT 1)
199+
set(LWS_WITH_ALSA 1)
200+
set(LWS_WITH_OPUS 1)
199201
endif()
200202

201203

include/libwebsockets/lws-alsa.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,30 @@ lws_alsa_enum_controls(struct lws_alsa_ctx *ctx, lws_alsa_control_cb cb, void *u
6363
LWS_VISIBLE LWS_EXTERN int
6464
lws_alsa_set_control(struct lws_alsa_ctx *ctx, uint32_t id, long val);
6565

66+
#if defined(LWS_WITH_OPUS)
67+
68+
struct lws_alsa_opus_capture;
69+
70+
typedef void (*lws_alsa_opus_encoded_cb_t)(void *user_data, const uint8_t *payload, size_t len);
71+
72+
LWS_VISIBLE LWS_EXTERN struct lws_alsa_opus_capture *
73+
lws_alsa_opus_capture_create(const struct lws_alsa_info *info, lws_alsa_opus_encoded_cb_t cb, void *user_data);
74+
75+
LWS_VISIBLE LWS_EXTERN void
76+
lws_alsa_opus_capture_destroy(struct lws_alsa_opus_capture **cap);
77+
78+
LWS_VISIBLE LWS_EXTERN int
79+
lws_alsa_opus_get_fd(struct lws_alsa_opus_capture *cap);
80+
81+
LWS_VISIBLE LWS_EXTERN int
82+
lws_alsa_opus_read(struct lws_alsa_opus_capture *cap);
83+
84+
LWS_VISIBLE LWS_EXTERN int
85+
lws_alsa_opus_send_capabilities(struct lws_alsa_opus_capture *cap, char *buf, size_t max_len);
86+
87+
LWS_VISIBLE LWS_EXTERN int
88+
lws_alsa_opus_set_control(struct lws_alsa_opus_capture *cap, uint32_t id, long val);
89+
90+
#endif
91+
6692
#endif

lib/core-net/CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,16 @@ list(APPEND SOURCES
3636
core-net/wsi.c
3737
core-net/wsi-timeout.c
3838
core-net/adopt.c
39-
core-net/latency.c
4039
core-net/txpacer.c
4140
roles/pipe/ops-pipe.c
4241
)
4342

43+
if (LWS_WITH_LATENCY)
44+
list(APPEND SOURCES
45+
core-net/latency.c
46+
)
47+
endif()
48+
4449
if (LWS_WITH_TRANSPORT_SEQUENCER)
4550
list(APPEND SOURCES
4651
core-net/lws-transport-sequencer.c

lib/core-net/latency.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
#include "private-lib-core.h"
2626

27-
#if defined(LWS_WITH_LATENCY)
2827

2928
LWS_VISIBLE void
3029
lws_latency_cb_start(struct lws_context_per_thread *pt)
@@ -137,4 +136,3 @@ lws_latency_get_json(struct lws_context *context, int tsi, uint64_t since_us,
137136
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "]}");
138137
return count;
139138
}
140-
#endif

lib/media/alsa/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
if (LWS_WITH_ALSA)
22
set(SRCS alsa.c)
3+
if (LWS_WITH_OPUS)
4+
list(APPEND SRCS alsa-opus.c)
5+
endif()
36
add_library(alsa STATIC ${SRCS})
47
target_include_directories(alsa PRIVATE ${LWS_LIB_BUILD_INC_PATHS} ${LWS_LIB_BUILD_INC_PATHS_TEMP})
58
target_link_libraries(alsa websockets asound)

lib/media/alsa/alsa-opus.c

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/*
2+
* libwebsockets - small server side websockets and web server implementation
3+
*
4+
* Copyright (C) 2010 - 2026 Andy Green <andy@warmcat.com>
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to
8+
* deal in the Software without restriction, including without limitation the
9+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10+
* sell copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*/
16+
17+
#include <libwebsockets.h>
18+
19+
#if defined(LWS_WITH_OPUS) && defined(LWS_WITH_ALSA)
20+
21+
#include <opus/opus.h>
22+
#include <string.h>
23+
24+
struct lws_alsa_opus_capture {
25+
struct lws_alsa_ctx *alsa_ctx;
26+
OpusEncoder *opus_enc;
27+
lws_alsa_opus_encoded_cb_t cb;
28+
void *user_data;
29+
30+
uint32_t samples_per_frame;
31+
int16_t *audio_samples;
32+
uint8_t opus_out[512];
33+
};
34+
35+
struct lws_alsa_opus_capture *
36+
lws_alsa_opus_capture_create(const struct lws_alsa_info *info, lws_alsa_opus_encoded_cb_t cb, void *user_data)
37+
{
38+
struct lws_alsa_opus_capture *cap;
39+
int err;
40+
41+
cap = lws_malloc(sizeof(*cap), __func__);
42+
if (!cap)
43+
return NULL;
44+
45+
memset(cap, 0, sizeof(*cap));
46+
47+
cap->alsa_ctx = lws_alsa_create_capture(info);
48+
if (!cap->alsa_ctx) {
49+
lwsl_err("%s: Failed to create ALSA capture\n", __func__);
50+
goto bail;
51+
}
52+
53+
cap->opus_enc = opus_encoder_create((opus_int32)info->rate, (int)info->channels, OPUS_APPLICATION_VOIP, &err);
54+
if (!cap->opus_enc || err != OPUS_OK) {
55+
lwsl_err("%s: Failed to create Opus encoder\n", __func__);
56+
goto bail;
57+
}
58+
59+
cap->samples_per_frame = info->samples_per_frame;
60+
cap->audio_samples = lws_malloc(cap->samples_per_frame * sizeof(int16_t) * info->channels, __func__);
61+
if (!cap->audio_samples)
62+
goto bail;
63+
64+
cap->cb = cb;
65+
cap->user_data = user_data;
66+
67+
return cap;
68+
69+
bail:
70+
lws_alsa_opus_capture_destroy(&cap);
71+
return NULL;
72+
}
73+
74+
void
75+
lws_alsa_opus_capture_destroy(struct lws_alsa_opus_capture **_cap)
76+
{
77+
struct lws_alsa_opus_capture *cap = *_cap;
78+
79+
if (!cap)
80+
return;
81+
82+
if (cap->alsa_ctx)
83+
lws_alsa_destroy(&cap->alsa_ctx);
84+
85+
if (cap->opus_enc)
86+
opus_encoder_destroy(cap->opus_enc);
87+
88+
if (cap->audio_samples)
89+
lws_free(cap->audio_samples);
90+
91+
lws_free(cap);
92+
*_cap = NULL;
93+
}
94+
95+
int
96+
lws_alsa_opus_get_fd(struct lws_alsa_opus_capture *cap)
97+
{
98+
if (!cap || !cap->alsa_ctx)
99+
return -1;
100+
return lws_alsa_get_fd(cap->alsa_ctx);
101+
}
102+
103+
int
104+
lws_alsa_opus_read(struct lws_alsa_opus_capture *cap)
105+
{
106+
int n, opus_len;
107+
108+
if (!cap || !cap->alsa_ctx || !cap->opus_enc)
109+
return -1;
110+
111+
n = lws_alsa_read(cap->alsa_ctx, cap->audio_samples, cap->samples_per_frame);
112+
if (n <= 0)
113+
return 0;
114+
115+
opus_len = opus_encode(cap->opus_enc, cap->audio_samples, n, cap->opus_out, sizeof(cap->opus_out));
116+
if (opus_len > 0 && cap->cb) {
117+
cap->cb(cap->user_data, cap->opus_out, (size_t)opus_len);
118+
}
119+
120+
return 0;
121+
}
122+
123+
struct json_dump_ctx {
124+
char *p;
125+
char *end;
126+
int first;
127+
};
128+
129+
static int alsa_json_control_cb(void *user, const struct lws_alsa_control *c)
130+
{
131+
struct json_dump_ctx *j = (struct json_dump_ctx *)user;
132+
char safe_name[256];
133+
int len;
134+
135+
if (lws_ptr_diff_size_t(j->end, j->p) < 128)
136+
return 1;
137+
138+
if (!j->first)
139+
*j->p++ = ',';
140+
141+
j->first = 0;
142+
143+
lws_json_purify(safe_name, c->name, sizeof(safe_name), &len);
144+
145+
j->p += lws_snprintf(
146+
j->p, lws_ptr_diff_size_t(j->end, j->p),
147+
"{\"id\":%u,\"name\":\"%s\","
148+
"\"min\":%ld,\"max\":%ld,\"step\":%ld,\"val\":%ld}",
149+
c->id, safe_name, c->min, c->max, c->step, c->val);
150+
151+
return 0;
152+
}
153+
154+
int
155+
lws_alsa_opus_send_capabilities(struct lws_alsa_opus_capture *cap, char *buf, size_t max_len)
156+
{
157+
struct json_dump_ctx j;
158+
159+
if (!cap || !cap->alsa_ctx)
160+
return -1;
161+
162+
j.p = buf;
163+
j.end = buf + max_len;
164+
j.first = 1;
165+
166+
j.p += lws_snprintf(j.p, lws_ptr_diff_size_t(j.end, j.p),
167+
"{\"type\":\"capabilities\",\"kind\":\"audio\",\"controls\":[");
168+
169+
lws_alsa_enum_controls(cap->alsa_ctx, alsa_json_control_cb, &j);
170+
171+
j.p += lws_snprintf(j.p, lws_ptr_diff_size_t(j.end, j.p), "]}");
172+
173+
return (int)lws_ptr_diff_size_t(j.p, buf);
174+
}
175+
176+
int
177+
lws_alsa_opus_set_control(struct lws_alsa_opus_capture *cap, uint32_t id, long val)
178+
{
179+
if (!cap || !cap->alsa_ctx)
180+
return -1;
181+
return lws_alsa_set_control(cap->alsa_ctx, id, val);
182+
}
183+
184+
#endif

lib/roles/http/server/interceptor.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,12 +269,13 @@ lws_interceptor_check(struct lws *wsi, const struct lws_protocols *prot)
269269

270270
s = sizeof(buf);
271271
if (lws_jwt_get_http_cookie_validate_jwt(wsi, &ck, buf, &s)) {
272-
lwsl_notice("%s: JWT validation failed for cookie '%s'\n", __func__, vhd->cookie_name);
272+
// lwsl_notice("%s: JWT validation failed for cookie '%s'\n", __func__, vhd->cookie_name);
273273
lws_interceptor_inject_header(wsi, vhd, NULL);
274+
274275
return vhd->always_pass ? 0 : 1;
275276
}
276277

277-
lwsl_notice("%s: JWT validation passed for cookie '%s', payload: %.*s\n", __func__, vhd->cookie_name, (int)s, buf);
278+
// lwsl_notice("%s: JWT validation passed for cookie '%s', payload: %.*s\n", __func__, vhd->cookie_name, (int)s, buf);
278279

279280
cp = lws_json_simple_find(buf, s, "\"sub\":", &claim_len);
280281
if (!cp) {

minimal-examples-lowlevel/raw/minimal-raw-webrtc-camshow/minimal-raw-webrtc-camshow.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ enum {
2525
LWS_SW_NAME,
2626
LWS_SW_URL,
2727
LWS_SW_VIDEO_DEVICE,
28+
LWS_SW_AUDIO_DEVICE,
2829
LWS_SW_WIDTH,
2930
LWS_SW_HELP,
3031
};
@@ -34,12 +35,14 @@ static const struct lws_switches switches[] = {
3435
[LWS_SW_NAME] = { "--name", "Client peer name to identify in mixer" },
3536
[LWS_SW_URL] = { "--url", "WebSockets URL to connect to (default wss://127.0.0.1:7681)" },
3637
[LWS_SW_VIDEO_DEVICE] = { "--video-device", "V4L2 video device path (default /dev/video0)" },
38+
[LWS_SW_AUDIO_DEVICE] = { "--audio-device", "ALSA audio device path (default default)" },
3739
[LWS_SW_WIDTH] = { "--width", "Video capture width in pixels (default 1280)" },
3840
[LWS_SW_HELP] = { "--help", "Show this help information" },
3941
};
4042

4143
static const char *url = "wss://127.0.0.1:7681";
4244
static const char *devs_list = "/dev/video0";
45+
static const char *audio_dev = "default";
4346
static char *devices_copy = NULL;
4447
static const char *client_name;
4548
static uint32_t app_width = 1280;
@@ -122,8 +125,8 @@ static void start_app_attach(struct lws_vhost *vh, const char *logical_name, con
122125
char *p = devices_copy_local, *token;
123126

124127
while ((token = strsep(&p, ","))) {
125-
lwsl_notice("Attaching %s to WebRTC mixer\n", token);
126-
if (cam_ops->attach(vh, url, token, client_name, app_width, app_height, access_token))
128+
lwsl_notice("Attaching %s (audio %s) to WebRTC mixer\n", token, audio_dev);
129+
if (cam_ops->attach(vh, url, token, audio_dev, client_name, app_width, app_height, access_token))
127130
lwsl_err("Failed to queue attach for %s\n", token);
128131
}
129132

@@ -162,7 +165,7 @@ main(int argc, const char **argv)
162165

163166
lws_context_info_defaults(&info, NULL);
164167
lws_cmdline_option_handle_builtin(argc, argv, &info);
165-
lwsl_user("LWS minimal raw webrtc camshow [--url <wss url>] [--video-device <device>] [--name <client name>] [--width <width>] [--height <height>]\n");
168+
lwsl_user("LWS minimal raw webrtc camshow [--url <wss url>] [--video-device <device>] [--audio-device <device>] [--name <client name>] [--width <width>] [--height <height>]\n");
166169
(void)switches;
167170

168171
if ((argc == 1) || lws_cmdline_option(argc, argv, switches[LWS_SW_HELP].sw)) {
@@ -174,6 +177,7 @@ main(int argc, const char **argv)
174177

175178
if ((opt = lws_cmdline_option(argc, argv, switches[LWS_SW_URL].sw))) url = opt;
176179
if ((opt = lws_cmdline_option(argc, argv, switches[LWS_SW_VIDEO_DEVICE].sw))) devs_list = opt;
180+
if ((opt = lws_cmdline_option(argc, argv, switches[LWS_SW_AUDIO_DEVICE].sw))) audio_dev = opt;
177181
if ((opt = lws_cmdline_option(argc, argv, switches[LWS_SW_NAME].sw))) client_name = opt;
178182
if ((opt = lws_cmdline_option(argc, argv, switches[LWS_SW_WIDTH].sw))) app_width = (uint32_t)atoi(opt);
179183
if ((opt = lws_cmdline_option(argc, argv, switches[LWS_SW_HEIGHT].sw))) app_height = (uint32_t)atoi(opt);

0 commit comments

Comments
 (0)