UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
jack.c
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2012-2024 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#include <jack/jack.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42
44#include "audio/types.h"
45#include "audio/utils.h"
46#include "debug.h"
47#include "host.h"
48#include "jack_common.h"
49#include "lib_common.h"
50#include "utils/audio_buffer.h"
51#include "utils/macros.h"
52#include "utils/ring_buffer.h"
53
54#define MOD_NAME "[JACK playback] "
55
56enum {
58 MAX_LEN_MS = 1000,
60};
61
64
67 jack_client_t *client;
68 jack_port_t *output_port[MAX_PORTS];
71 float *converted;
72
74 void *data; // audio buffer
76 char *tmp;
77
78 long int first_channel;
79};
80
81static int jack_samplerate_changed_callback(jack_nframes_t nframes, void *arg);
82static int jack_process_callback(jack_nframes_t nframes, void *arg);
83
84static int jack_samplerate_changed_callback(jack_nframes_t nframes, void *arg)
85{
86 struct state_jack_playback *s = (struct state_jack_playback *) arg;
87
88 if (s->jack_sample_rate != 0) {
89 bug_msg(LOG_LEVEL_WARNING, "JACK sample rate changed from %d to %d. "
90 "Runtime change is not supported in UG and will likely "
91 "cause malfunctioning. ",
92 s->jack_sample_rate, nframes);
93 } else {
94 log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Sample rate changed to: %d\n",
95 nframes);
96 }
97
98 s->jack_sample_rate = nframes;
99
100 return 0;
101}
102
103static int jack_process_callback(jack_nframes_t nframes, void *arg)
104{
105 struct state_jack_playback *s = (struct state_jack_playback *) arg;
106 int len;
107 int req_len = s->desc.ch_count * nframes * sizeof(float);
108 int nframes_available = nframes;
109
110 len = s->buffer_fns->read(s->data, s->tmp, req_len);
111 if (len != req_len) {
112 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Buffer underflow detected.\n");
113 nframes_available = len / s->desc.ch_count / sizeof(float);
114 }
115
116 for (int i = 0; i < s->desc.ch_count; ++i) {
117 jack_default_audio_sample_t *out =
118 s->libjack->port_get_buffer (s->output_port[i], nframes_available);
119 assert(out != NULL);
120 demux_channel((char *) out, s->tmp, sizeof(float), len, s->desc.ch_count, i);
121 }
122
123 return 0;
124}
125
126static void audio_play_jack_probe(struct device_info **available_devices, int *count, void (**deleter)(void *))
127{
128 *deleter = free;
129 *available_devices = audio_jack_probe(PACKAGE_STRING, JackPortIsInput, count);
130}
131
132static void audio_play_jack_help(const char *client_name)
133{
134 int count = 0;
135 int i = 0;
136 struct device_info *available_devices = audio_jack_probe(client_name, JackPortIsInput, &count);
137 if(!available_devices)
138 return;
139
140 printf("Usage:\n");
141 printf("\t-r jack[:first_channel=<f>][:name=<n>][:<device>]\n");
142 printf("\twhere\n");
143 printf("\t\t<f> - index of first channel to capture (default: 0)\n");
144 printf("\t\t<n> - name of the JACK client (default: %s)\n", PACKAGE_NAME);
145 printf("\n");
146
147 printf("Available devices:\n");
148 for(i = 0; i < count; i++){
149 printf("\t%s\n", available_devices[i].name);
150 }
151 free(available_devices);
152}
153
154static void * audio_play_jack_init(const char *cfg)
155{
156 struct state_jack_playback *s;
157 const char **ports;
158 jack_status_t status;
159 char *client_name;
160 const char *source_name = "";
161
162 client_name = alloca(MAX(strlen(PACKAGE_NAME), strlen(cfg)) + 1);
163 strcpy(client_name, PACKAGE_NAME);
164
165 s = calloc(1, sizeof(struct state_jack_playback));
166 if(!s) {
167 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to allocate memory.\n");
168 return NULL;
169 }
170
171 s->libjack = open_libjack();
172 if (s->libjack == NULL) {
173 free(s);
174 return NULL;
175 }
176
177 char *dup = strdup(cfg);
178 assert(dup != NULL);
179 char *tmp = dup, *item, *save_ptr;
180 while ((item = strtok_r(tmp, ":", &save_ptr)) != NULL) {
181 if (strcmp(item, "help") == 0) {
182 audio_play_jack_help(client_name);
183 free(s);
184 free(dup);
185 return INIT_NOERR;
186 } else if (strstr(item, "first_channel=") == item) {
187 char *endptr;
188 char *val = item + strlen("first_channel=");
189 errno = 0;
190 s->first_channel = strtol(val, &endptr, 0);
191 if (errno == ERANGE || *endptr != '\0' || s->first_channel < 0) {
192 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Wrong value '%s'.\n", val);
193 goto error;
194 }
195 } else if (strstr(item, "name=") == item) {
196 strcpy(client_name, item + strlen("name="));
197 } else { // the rest is the device name
198 source_name = cfg + (item - dup);
199 break;
200 }
201 tmp = NULL;
202 }
203 free(dup);
204 dup = NULL;
205
206 s->jack_ports_pattern = strdup(source_name);
207
208 s->client = s->libjack->client_open(client_name, JackNullOption, &status);
209 if(status & JackFailure) {
210 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Opening JACK client failed.\n");
211 goto error;
212 }
213
215 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Registering callback problem.\n");
216 goto release_client;
217 }
218
219
220 if (s->libjack->set_process_callback(s->client, jack_process_callback, (void *) s) != 0) {
221 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Process callback registration problem.\n");
222 goto release_client;
223 }
224
226 log_msg(LOG_LEVEL_INFO, "JACK sample rate: %d\n", s->jack_sample_rate);
227
228
229 ports = s->libjack->get_ports(s->client, s->jack_ports_pattern, NULL, JackPortIsInput);
230 if(ports == NULL) {
231 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to input ports matching %s.\n", s->jack_ports_pattern);
232 goto release_client;
233 }
234
235 s->jack_ports_count = 0;
236 while(ports[s->jack_ports_count]) s->jack_ports_count++;
237 free(ports);
238
239 {
240 char name[30];
241 int i;
242
243 for(i = 0; i < MAX_PORTS; ++i) {
244 snprintf(name, 30, "playback_%02u", i);
245 s->output_port[i] = s->libjack->port_register (s->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
246 }
247 }
248
249
250 return s;
251
252release_client:
254error:
256 free(dup);
257 free(s);
258 return NULL;
259}
260
261static bool audio_play_jack_query_format(struct state_jack_playback *s, void *data, size_t *len)
262{
263 struct audio_desc desc;
264 if (*len < sizeof desc) {
265 return false;
266 } else {
267 memcpy(&desc, data, sizeof desc);
268 }
269
270 if (s->jack_sample_rate <= 0) {
271 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Wrong JACK sample rate detected: %d!",
273 return false;
274 }
275 desc = (struct audio_desc){4, s->jack_sample_rate, MIN(s->jack_ports_count, desc.ch_count), AC_PCM};
276
277 memcpy(data, &desc, sizeof desc);
278 *len = sizeof desc;
279 return true;
280}
281
282static bool audio_play_jack_ctl(void *state, int request, void *data, size_t *len)
283{
284 struct state_jack_playback *s = (struct state_jack_playback *) state;
285
286 switch (request) {
289 default:
290 return false;
291 }
292}
293
295{
296 struct state_jack_playback *s = (struct state_jack_playback *) state;
297 const char **ports;
298 int i;
299
300 assert(desc.bps == 4 && desc.sample_rate == s->jack_sample_rate && desc.codec == AC_PCM);
301
302 s->libjack->deactivate(s->client);
303
304 ports = s->libjack->get_ports(s->client, s->jack_ports_pattern, NULL, JackPortIsInput);
305 if(ports == NULL) {
306 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to input ports matching %s.\n", s->jack_ports_pattern);
307 return false;
308 }
309
310 if(desc.ch_count > s->jack_ports_count) {
311 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Warning: received %d audio channels, JACK can process only %d.", desc.ch_count, s->jack_ports_count);
312 }
313
314 if (s->buffer_fns) {
315 s->buffer_fns->destroy(s->data);
316 s->buffer_fns = NULL;
317 s->data = NULL;
318 }
319
320 {
321 int buf_len_ms = DEFAULT_AUDIO_BUF_LEN_MS;
322 if (get_commandline_param("low-latency-audio")) {
323 buf_len_ms = 5;
324 }
325 if (get_commandline_param("audio-buffer-len")) {
326 buf_len_ms = atoi(get_commandline_param("audio-buffer-len"));
327 assert(buf_len_ms > 0 && buf_len_ms < MAX_LEN_MS);
328 }
329 if (get_commandline_param("audio-disable-adaptive-buffer")) {
330 int buf_len = desc.bps * desc.ch_count * (desc.sample_rate * buf_len_ms / 1000);
331 s->data = ring_buffer_init(buf_len);
333 } else {
334 s->data = audio_buffer_init(desc.sample_rate, desc.bps, desc.ch_count, buf_len_ms);
336 }
337 }
338
339 /* for all channels previously connected */
340 for(i = 0; i < desc.ch_count; ++i) {
341 s->libjack->disconnect(s->client, s->libjack->port_name (s->output_port[i]), ports[i]);
342 log_msg(LOG_LEVEL_INFO, MOD_NAME "Port %d: %s\n", i, ports[i]);
343 }
344 free(s->tmp);
345 free(s->converted);
346 s->desc.bps = desc.bps;
347 s->desc.ch_count = desc.ch_count;
348 s->desc.sample_rate = desc.sample_rate;
349
350 s->max_channel_len = (desc.sample_rate / 1000) * MAX_LEN_MS * sizeof(float);
351 s->tmp = malloc(s->max_channel_len);
352 s->converted = malloc(desc.ch_count * s->max_channel_len);
353
354 if (s->libjack->activate(s->client)) {
355 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot activate client.\n");
356 return false;
357 }
358
359 for(i = 0; i < desc.ch_count; ++i) {
360 if (s->libjack->connect (s->client, s->libjack->port_name (s->output_port[i]), ports[i])) {
361 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot connect output port: %d.\n", i);
362 return false;
363 }
364 }
365 free(ports);
366
367 return true;
368}
369
370static void audio_play_jack_put_frame(void *state, const struct audio_frame *frame)
371{
372 struct state_jack_playback *s = (struct state_jack_playback *) state;
373 assert(frame->bps == 4);
374 int len = frame->data_len;
375
376 if (len >= s->max_channel_len * frame->ch_count) {
377 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Long frame: %d!\n", frame->data_len);
378 len = s->max_channel_len * frame->ch_count;
379 }
380
381 int2float((char *) s->converted, frame->data, len);
382 s->buffer_fns->write(s->data, (char *) s->converted, len);
383}
384
385static void audio_play_jack_done(void *state)
386{
387 struct state_jack_playback *s = (struct state_jack_playback *) state;
388
390 free(s->tmp);
391 free(s->converted);
392 free(s->jack_ports_pattern);
393 if (s->buffer_fns) {
394 s->buffer_fns->destroy(s->data);
395 }
396
398
399 free(s);
400}
401
410
412
413/* vim: set expandtab sw=8: */
void ** state
int void * val
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
struct audio_buffer_api audio_buffer_fns
struct audio_buffer * audio_buffer_init(int sample_rate, int bps, int ch_count, int suggested_latency_ms)
#define AUDIO_PLAYBACK_CTL_QUERY_FORMAT
#define AUDIO_PLAYBACK_ABI_VERSION
static int jack_process_callback(jack_nframes_t nframes, void *arg)
Definition jack.c:89
static int jack_samplerate_changed_callback(jack_nframes_t nframes, void *arg)
Definition jack.c:80
#define MOD_NAME
Definition jack.c:54
@ MAX_PORTS
Definition jack.c:57
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
void bug_msg(int level, const char *format,...)
prints the error with the advice to report the bug
Definition debug.cpp:379
#define LOG_LEVEL_NOTICE
information that may be interesting
Definition debug.h:61
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
int count
Definition hd-rum.c:44
const char * get_commandline_param(const char *key)
Definition host.cpp:894
#define INIT_NOERR
Definition host.h:73
static struct device_info * audio_jack_probe(const char *client_name, unsigned long port_flags, int *count)
static void close_libjack(struct libjack_connection *s)
static struct libjack_connection * open_libjack(void)
@ 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
#define MIN(a, b)
Definition macros.h:63
#define MAX(a, b)
Definition macros.h:65
static bool audio_play_jack_ctl(void *state, int request, void *data, size_t *len)
Definition jack.c:282
static void audio_play_jack_done(void *state)
Definition jack.c:385
static void audio_play_jack_probe(struct device_info **available_devices, int *count, void(**deleter)(void *))
Definition jack.c:126
static void audio_play_jack_help(const char *client_name)
Definition jack.c:132
static const struct audio_playback_info aplay_jack_info
Definition jack.c:402
static bool audio_play_jack_reconfigure(void *state, struct audio_desc desc)
Definition jack.c:294
static void * audio_play_jack_init(const char *cfg)
Definition jack.c:154
static void audio_play_jack_put_frame(void *state, const struct audio_frame *frame)
Definition jack.c:370
@ MAX_LEN_MS
Definition jack.c:58
@ MAX_PORTS
Definition jack.c:59
@ DEFAULT_AUDIO_BUF_LEN_MS
Definition jack.c:57
static bool audio_play_jack_query_format(struct state_jack_playback *s, void *data, size_t *len)
Definition jack.c:261
struct audio_buffer_api ring_buffer_fns
struct ring_buffer * ring_buffer_init(int size)
const char * name
Definition settings.cpp:193
int(* read)(void *buf, char *out, int max_len)
void(* write)(void *buf, const char *in, int len)
void(* destroy)(void *buf)
int sample_rate
Definition types.h:78
int ch_count
Definition types.h:79
int bps
Definition types.h:77
struct used to comunicate with audio capture/playback device
Definition types.h:99
Definition hd-rum.c:27
jack_client_close_t client_close
jack_port_register_t port_register
jack_set_sample_rate_callback_t set_sample_rate_callback
jack_get_sample_rate_t get_sample_rate
jack_connect_t connect
jack_client_open_t client_open
jack_port_name_t port_name
jack_deactivate_t deactivate
jack_port_get_buffer_t port_get_buffer
jack_activate_t activate
jack_get_ports_t get_ports
jack_disconnect_t disconnect
jack_set_process_callback_t set_process_callback
jack_port_t * output_port[MAX_PORTS]
Definition jack.c:68
struct audio_buffer_api * buffer_fns
Definition jack.c:75
jack_client_t * client
Definition jack.c:67
struct libjack_connection * libjack
Definition jack.c:63
int max_channel_len
maximal length of channel data that is processed at once
Definition jack.c:70
int jack_ports_count
Definition jack.c:73
int jack_sample_rate
Definition jack.c:66
long int first_channel
Definition jack.c:78
char * tmp
temporary buffer used to demux data
Definition jack.c:76
struct audio_desc desc
Definition jack.c:69
float * converted
temporery buffer for int2float (put_frame)
Definition jack.c:71
char * jack_ports_pattern
Definition jack.c:65
void demux_channel(char *out, char *in, int bps, int in_len, int in_stream_channels, int pos_in_stream)
Definition utils.cpp:334
void int2float(char *out, const char *in, int len)
Definition utils.cpp:496