UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
mixer.cpp
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2016-2023 CESNET z.s.p.o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38
39#ifdef HAVE_CONFIG_H
40#include "config.h"
41#include "config_unix.h"
42#include "config_win32.h"
43#endif
44
45#include "audio/audio_capture.h"
47#include "audio/codec.h"
48#include "audio/types.h"
49#include "debug.h"
50#include "lib_common.h"
51#include "module.h"
52#include "rtp/rtp.h"
53#include "transmit.h"
54#include "utils/audio_buffer.h"
55#include "utils/thread.h"
56#include <chrono>
57#include <iostream>
58#include <map>
59#include <mutex>
60#include <thread>
61#include <vector>
62
63#define SAMPLE_RATE 48000
64#define BPS 2
65#define CHANNELS 1
66#define FRAMES_PER_SEC 25
67static_assert(SAMPLE_RATE % FRAMES_PER_SEC == 0, "Sample rate not divisible by frames per sec!");
68#define SAMPLES_PER_FRAME (SAMPLE_RATE / FRAMES_PER_SEC)
69
70#define PARTICIPANT_TIMEOUT_S 60
71typedef int16_t sample_type_source;
72typedef int32_t sample_type_mixed;
73static_assert(sizeof(sample_type_source) == BPS, "sample_type source doesn't match BPS");
74static_assert(sizeof(sample_type_mixed) > sizeof(sample_type_source), "sample_type_mixed is not wider than sample_type_source");
75
76using namespace std;
77using namespace std::chrono;
78
80public:
81 bool operator() (const sockaddr_storage & x, const sockaddr_storage & y) const {
82 if (x.ss_family != y.ss_family) {
83 return x.ss_family < y.ss_family;
84 }
85
86 if (x.ss_family == AF_INET) {
87 const auto &sin_x =
88 reinterpret_cast<const sockaddr_in &>(x);
89 const auto &sin_y =
90 reinterpret_cast<const sockaddr_in &>(y);
91
92 if (sin_x.sin_addr.s_addr != sin_y.sin_addr.s_addr) {
93 return sin_x.sin_addr.s_addr < sin_y.sin_addr.s_addr;
94 }
95 return sin_x.sin_port < sin_y.sin_port;
96 } else if (x.ss_family == AF_INET6) {
97 const auto &sin_x =
98 reinterpret_cast<const sockaddr_in6 &>(x);
99 const auto &sin_y =
100 reinterpret_cast<const sockaddr_in6 &>(y);
101
102 for (int i = 0; i < 16; ++i) {
103 if (sin_x.sin6_addr.s6_addr[i] != sin_y.sin6_addr.s6_addr[i]) {
104 return sin_x.sin6_addr.s6_addr[i] < sin_y.sin6_addr.s6_addr[i];
105 }
106 }
107
108 return sin_x.sin6_port < sin_y.sin6_port;
109 }
110 abort();
111 }
112};
113
114static void mixer_dummy_rtp_callback(struct rtp *session [[gnu::unused]], rtp_event * e [[gnu::unused]]) {
115}
116
118 am_participant(struct socket_udp_local *l, struct sockaddr_storage *ss, string const & audio_codec) {
119 assert(l != nullptr && ss != nullptr);
120 m_buffer = audio_buffer_init(SAMPLE_RATE, BPS, CHANNELS, get_commandline_param("low-latency-audio") ? 50 : 5);
121 assert(m_buffer != NULL);
122 struct sockaddr *sa = (struct sockaddr *) ss;
123 assert(ss->ss_family == AF_INET || ss->ss_family == AF_INET6);
124 socklen_t len = ss->ss_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
125
126 m_network_device = rtp_init_with_udp_socket(l, sa, len, mixer_dummy_rtp_callback);
127 assert(m_network_device != NULL);
128 m_tx_session = tx_init(NULL, 1500, TX_MEDIA_AUDIO, NULL, NULL, RATE_UNLIMITED);
129 assert(m_tx_session != NULL);
130
131 m_audio_coder = audio_codec_init_cfg(audio_codec.c_str(), AUDIO_CODER);
132 if (!m_audio_coder) {
133 LOG(LOG_LEVEL_ERROR) << "Audio coder init failed!\n";
134 throw 1;
135 }
136 }
152 m_audio_coder = std::move(other.m_audio_coder);
153 m_buffer = std::move(other.m_buffer);
154 m_network_device = std::move(other.m_network_device);
155 m_tx_session = std::move(other.m_tx_session);
156 last_seen = std::move(other.last_seen);
157 other.m_audio_coder = nullptr;
158 other.m_buffer = nullptr;
159 other.m_tx_session = nullptr;
160 other.m_network_device = nullptr;
161 return *this;
162 }
164 *this = std::move(other);
165 }
170 chrono::steady_clock::time_point last_seen;
171};
172
173template<typename source_t, typename intermediate_t>
175public:
176 virtual ~generic_mix_algo() {}
177 virtual intermediate_t add_to_mix(intermediate_t dst, source_t sample) {
178 return dst + sample;
179 }
180
181 virtual intermediate_t get_mixed_without_source_sample(intermediate_t mix, source_t source_sample) {
182 return mix - source_sample;
183 }
184
185 virtual intermediate_t normalize(intermediate_t sample) = 0;
186};
187
194template<typename source_t, typename intermediate_t>
195class linear_mix_algo : public generic_mix_algo<source_t, intermediate_t> {
196public:
197 intermediate_t normalize(intermediate_t sample) override {
198 // clamp the value since linear mixer doesn't normalize values
199 return min<intermediate_t>(max<intermediate_t>(sample, numeric_limits<source_t>::min()), numeric_limits<source_t>::max());
200 }
201};
202
210template<typename source_t, typename intermediate_t>
211class logarithmic_mix_algo : public generic_mix_algo<source_t, intermediate_t> {
212public:
213 static constexpr double t = 0.5;
214 static constexpr double alpha = 5.71144;
215 intermediate_t normalize(intermediate_t sample) override {
216 if (sample >= numeric_limits<source_t>::min() / 2 &&
217 sample <= numeric_limits<source_t>::max() / 2) {
218 return sample;
219 } else {
220 double sample_norm = (double) sample / numeric_limits<source_t>::max();
221 double ret = sample_norm / fabs(sample_norm) * (t + (1.0 - t) * log(1.0 + alpha * (fabs(sample_norm) - t) / (2 - t)) / log(1.0 + alpha)) * numeric_limits<source_t>::max();
222 return ret;
223 }
224 }
225};
226
227struct state_audio_mixer final {
228 state_audio_mixer(const char *cfg) {
229 if (cfg) {
230 shared_ptr<char> tmp(strdup(cfg), free);
231 char *item, *save_ptr;
232 char *copy = tmp.get();
233
234 while ((item = strtok_r(copy, ":", &save_ptr))) {
235 if (strncmp(item, "codec=", strlen("codec=")) == 0) {
236 audio_codec = item + strlen("codec=");
237 } else if (strncmp(item, "algo=", strlen("algo=")) == 0) {
238 string algo = item + strlen("algo=");
239 if (algo == "linear") {
241 } else if (algo == "logarithmic") {
243 } else {
244 LOG(LOG_LEVEL_ERROR) << "Unknown mixing algorithm: " << algo << "\n";
245 throw 1;
246 }
247 } else {
248 LOG(LOG_LEVEL_ERROR) << "Unknown option: " << item << "\n";
249 throw 1;
250 }
251 copy = nullptr;
252 }
253 }
254
255 struct audio_codec_state *audio_coder =
257 if (!audio_coder) {
258 LOG(LOG_LEVEL_ERROR) << "Audio coder init failed!\n";
259 throw 1;
260 } else {
261 audio_codec_done(audio_coder);
262 }
263
264 thread_id = thread(&state_audio_mixer::worker, this);
265 }
267 thread_id.join();
268 }
269 bool should_exit = false;
272 void worker();
273
274 map<sockaddr_storage, am_participant, sockaddr_storage_less> participants;
276
278 string audio_codec{"PCM"};
279private:
280 thread thread_id;
281 unique_ptr<generic_mix_algo<sample_type_source, sample_type_mixed>> mixing_algorithm{new linear_mix_algo<sample_type_source, sample_type_mixed>()};
282};
283
285{
286 set_thread_name(__func__);
287 chrono::steady_clock::time_point next_frame_time = chrono::steady_clock::now();
288
289 static_assert(SAMPLES_PER_FRAME * 1000ll % SAMPLE_RATE == 0, "Sample rate is not evenly divisible by number of samples in frame");
290 const chrono::milliseconds interval(SAMPLES_PER_FRAME*1000ll/SAMPLE_RATE);
291
292 while (!should_exit) {
293 this_thread::sleep_until(next_frame_time);
294 next_frame_time += interval;
295 auto now = chrono::steady_clock::now();
296
297 // check if we didn't overslept much
298 if (next_frame_time < now) {
299 LOG(LOG_LEVEL_WARNING) << "[Audio mixer] Next frame time in past! Setting to now.\n";
300 next_frame_time = now;
301 }
302
303 unique_lock<mutex> plk(participants_lock);
304 // check timeouts
305 for (auto it = participants.cbegin(); it != participants.cend(); )
306 {
307 if (duration_cast<seconds>(now - it->second.last_seen).count() > PARTICIPANT_TIMEOUT_S) {
308 it = participants.erase(it);
309 } else {
310 ++it;
311 }
312 }
313
314 size_t data_len_source = SAMPLES_PER_FRAME * sizeof(sample_type_source) * CHANNELS;
315 vector<sample_type_mixed> mixed(SAMPLES_PER_FRAME * CHANNELS);
316 vector<audio_frame2> participant_frames(participants.size());
317 int participant_index = 0;
318
319 // mix all together
320 for (auto & p : participants) {
321 participant_frames[participant_index].init(CHANNELS, AC_PCM, BPS, SAMPLE_RATE);
322 static_assert(CHANNELS == 1, "Currently only one channel is implemented here.");
323 participant_frames[participant_index].resize(0, data_len_source);
324 char *particip_data = participant_frames[participant_index].get_data(0);
325 int ret = audio_buffer_read(p.second.m_buffer, particip_data, data_len_source);
326 memset(particip_data, 0, data_len_source - ret);
327
328 sample_type_source *src = (sample_type_source *)(void *) particip_data;
329 auto dst = mixed.begin();
330 for (int i = 0; i < SAMPLES_PER_FRAME * CHANNELS; ++i) {
331 *dst = mixing_algorithm->add_to_mix(*dst, *src);
332 ++dst;
333 ++src;
334 }
335 participant_index++;
336 }
337
338 // substract each source signal from the mix coming to that participant
339 for (auto & pb : participant_frames) {
340 auto mix = mixed.begin();
341 sample_type_source *part = (sample_type_source *)(void *) pb.get_data(0);
342 for (int i = 0; i < SAMPLES_PER_FRAME * CHANNELS; ++i) {
343 *part = mixing_algorithm->normalize(mixing_algorithm->get_mixed_without_source_sample(*mix, *part));
344 ++mix;
345 ++part;
346 }
347 }
348
349 // send
350 participant_index = 0;
351 for (auto & p : participants) {
352 audio_frame2 *uncompressed = &participant_frames[participant_index];
353 while (audio_frame2 compressed = audio_codec_compress(p.second.m_audio_coder, uncompressed)) {
354 audio_tx_send(p.second.m_tx_session, p.second.m_network_device, &compressed);
355 uncompressed = nullptr;
356 }
357
358 participant_index++;
359 }
360 plk.unlock();
361 }
362}
363
365{
366 printf("Usage:\n"
367 "\t%s -r mixer[:codec=<codec>][:algo={linear|logarithmic}]\n"
368 "\n"
369 "<codec>\n"
370 "\taudio codec to use\n"
371 "linear\n"
372 "\tlinear sum of signals (with clamping)\n"
373 "logarithmic\n"
374 "\tlinear sum of signals to threshold, above threshold logarithmic dynamic range compression is used\n"
375 "\n"
376 "Notes:\n"
377 "1)\tYou do not need to specify audio participants explicitly,\n"
378 "\t" PACKAGE_NAME " simply sends the the stream back to the host\n"
379 "\tthat is sending to mixer. Therefore it is necessary that the\n"
380 "\tparticipant uses single " PACKAGE_NAME " for both sending and\n"
381 "\treceiving audio.\n"
382 "2)\tUses default port for receiving, therefore if you want to use it\n"
383 "\ton machine that is a part of the conference, you should use something like:\n"
384 "\t\t%s -s <your_capture> -P 5004:5004:5010:5006\n"
385 "\tfor the " PACKAGE_NAME " instance that is part of the conference (not mixer!)\n",
386 uv_argv[0], uv_argv[0]);
387}
388
389static void audio_play_mixer_probe(struct device_info **available_devices, int *count, void (**deleter)(void *))
390{
391 *deleter = free;
392 *available_devices = NULL;
393 *count = 0;
394}
395
396static void * audio_play_mixer_init(const char *cfg)
397{
398 if (strcmp(cfg, "help") == 0) {
400 return INIT_NOERR;
401 }
402 try {
403 return new state_audio_mixer{cfg};
404 } catch (...) {
405 return nullptr;
406 }
407}
408
409static void audio_play_mixer_put_frame(void *state, const struct audio_frame *frame)
410{
411 struct state_audio_mixer *s = (struct state_audio_mixer *) state;
412
413 unique_lock<mutex> lk(s->participants_lock);
414
415 auto ss = *(struct sockaddr_storage *) frame->network_source;
416
417 if (s->participants.find(ss) == s->participants.end()) {
418 s->participants.emplace(ss, am_participant{s->recv_socket, &ss, s->audio_codec});
419 }
420
421 audio_buffer_write(s->participants.at(ss).m_buffer, frame->data, frame->data_len);
422 s->participants.at(ss).last_seen = chrono::steady_clock::now();
423}
424
425static void audio_play_mixer_done(void *state)
426{
427 struct state_audio_mixer *s = (struct state_audio_mixer *) state;
428 s->should_exit = true;
429
430 delete s;
431}
432
433static bool audio_play_mixer_ctl(void *state, int request, void *data, size_t *len)
434{
435 struct state_audio_mixer *s = (struct state_audio_mixer *) state;
436
437 switch (request) {
439 if (*len >= sizeof(struct audio_desc)) {
441 memcpy(data, &desc, sizeof desc);
442 *len = sizeof desc;
443 return true;
444 } else {
445 return false;
446 }
448 if (*len >= sizeof(bool)) {
449 *(bool *) data = true;
450 *len = sizeof(bool);
451 return true;
452 } else {
453 return false;
454 }
456 if (*len != sizeof(struct rtp *)) {
457 return false;
458 } else {
459 s->recv_socket = rtp_get_udp_local_socket(*(struct rtp **) data);
460 assert(s->recv_socket != NULL);
461 return true;
462 }
463 default:
464 return false;
465 }
466}
467
468static bool audio_play_mixer_reconfigure(void *state [[gnu::unused]], struct audio_desc desc)
469{
471 assert(desc == requested);
472 return true;
473}
474
483
485
void ** state
struct video_desc desc
struct video_frame * frame
int void size_t * len
int void(** deleter)(void *))
This file contains definition of some common audio types.
@ AC_PCM
Definition types.h:53
void audio_buffer_destroy(struct audio_buffer *buf)
struct audio_buffer * audio_buffer_init(int sample_rate, int bps, int ch_count, int suggested_latency_ms)
int audio_buffer_read(struct audio_buffer *buf, char *out, int max_len)
void audio_buffer_write(struct audio_buffer *buf, const char *in, int len)
#define AUDIO_PLAYBACK_CTL_QUERY_FORMAT
#define AUDIO_PLAYBACK_ABI_VERSION
#define AUDIO_PLAYBACK_CTL_MULTIPLE_STREAMS
#define AUDIO_PLAYBACK_PUT_NETWORK_DEVICE
virtual intermediate_t normalize(intermediate_t sample)=0
virtual intermediate_t get_mixed_without_source_sample(intermediate_t mix, source_t source_sample)
Definition mixer.cpp:181
virtual intermediate_t add_to_mix(intermediate_t dst, source_t sample)
Definition mixer.cpp:177
virtual ~generic_mix_algo()
Definition mixer.cpp:176
intermediate_t normalize(intermediate_t sample) override
Definition mixer.cpp:197
intermediate_t normalize(intermediate_t sample) override
Definition mixer.cpp:215
static constexpr double alpha
Definition mixer.cpp:214
static constexpr double t
Definition mixer.cpp:213
bool operator()(const sockaddr_storage &x, const sockaddr_storage &y) const
Definition mixer.cpp:81
struct audio_codec_state * audio_codec_init_cfg(const char *audio_codec_cfg, audio_codec_direction_t direction)
Definition codec.cpp:165
audio_frame2 audio_codec_compress(struct audio_codec_state *s, const audio_frame2 *frame)
Definition codec.cpp:245
void audio_codec_done(struct audio_codec_state *s)
Definition codec.cpp:372
@ AUDIO_CODER
Definition codec.h:46
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG(level)
Definition debug.h:313
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
void module_done(struct module *module_data)
Definition module.c:93
#define CAST_MODULE(x)
Definition module.h:197
int count
Definition hd-rum.c:44
const char * get_commandline_param(const char *key)
Definition host.cpp:894
char ** uv_argv
Definition host.cpp:174
#define INIT_NOERR
Definition host.h:73
@ LIBRARY_CLASS_AUDIO_PLAYBACK
Definition lib_common.h:77
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
static void audio_play_mixer_help()
Definition mixer.cpp:364
#define CHANNELS
Definition mixer.cpp:65
#define PARTICIPANT_TIMEOUT_S
Definition mixer.cpp:70
static void audio_play_mixer_done(void *state)
Definition mixer.cpp:425
#define SAMPLE_RATE
Definition mixer.cpp:63
static void audio_play_mixer_put_frame(void *state, const struct audio_frame *frame)
Definition mixer.cpp:409
static bool audio_play_mixer_reconfigure(void *state, struct audio_desc desc)
Definition mixer.cpp:468
static void * audio_play_mixer_init(const char *cfg)
Definition mixer.cpp:396
#define SAMPLES_PER_FRAME
Definition mixer.cpp:68
static const struct audio_playback_info aplay_mixer_info
Definition mixer.cpp:475
static bool audio_play_mixer_ctl(void *state, int request, void *data, size_t *len)
Definition mixer.cpp:433
static void mixer_dummy_rtp_callback(struct rtp *session, rtp_event *e)
Definition mixer.cpp:114
static void audio_play_mixer_probe(struct device_info **available_devices, int *count, void(**deleter)(void *))
Definition mixer.cpp:389
#define FRAMES_PER_SEC
Definition mixer.cpp:66
#define BPS
Definition mixer.cpp:64
int16_t sample_type_source
Definition mixer.cpp:71
int32_t sample_type_mixed
Definition mixer.cpp:72
rtp_t rtp_init_with_udp_socket(struct socket_udp_local *l, struct sockaddr *sa, socklen_t len, rtp_callback callback)
Definition rtp.c:1175
#define min(a, b)
Definition rtp.c:99
struct socket_udp_local * rtp_get_udp_local_socket(struct rtp *session)
Definition rtp.c:4037
void rtp_done(struct rtp *session)
Definition rtp.c:3676
struct rtp * m_network_device
Definition mixer.cpp:168
struct audio_buffer * m_buffer
Definition mixer.cpp:167
am_participant(am_participant &&other)
Definition mixer.cpp:163
struct tx * m_tx_session
Definition mixer.cpp:169
am_participant(struct socket_udp_local *l, struct sockaddr_storage *ss, string const &audio_codec)
Definition mixer.cpp:118
chrono::steady_clock::time_point last_seen
Definition mixer.cpp:170
am_participant & operator=(am_participant &&other)
Definition mixer.cpp:151
struct audio_codec_state * m_audio_coder
Definition mixer.cpp:166
struct used to comunicate with audio capture/playback device
Definition types.h:99
Definition hd-rum.c:27
Definition rtp.c:274
struct socket_udp_local * recv_socket
Definition mixer.cpp:277
unique_ptr< generic_mix_algo< sample_type_source, sample_type_mixed > > mixing_algorithm
Definition mixer.cpp:281
mutex participants_lock
Definition mixer.cpp:275
map< sockaddr_storage, am_participant, sockaddr_storage_less > participants
Definition mixer.cpp:274
string audio_codec
Definition mixer.cpp:278
state_audio_mixer & operator=(state_audio_mixer const &)=delete
state_audio_mixer(const char *cfg)
Definition mixer.cpp:228
state_audio_mixer(state_audio_mixer const &)=delete
void set_thread_name(const char *name)
Definition thread.c:72
void audio_tx_send(struct tx *tx, struct rtp *rtp_session, const audio_frame2 *buffer)
Definition transmit.cpp:799
struct tx * tx_init(struct module *parent, unsigned mtu, enum tx_media_type media_type, const char *fec, const char *encryption, long long int bitrate)
Definition transmit.cpp:201