mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 02:37:10 +00:00 
			
		
		
		
	app_jack: Support audio with a sampling rate higher than 8kHz
This patch enables the jack-audiohook to cope with dynamic sampling rates from and to Asterisk. Information from the channel is taken to derive the channel's sampling rate, suiting SLINxx format and frame->datalen. There are stil a few limitations after this patch: * Required information is taken from the channel during initialization as the audiohook does not provide this information. Audiohook.internal_sampl_rate(...) is set later, but no callback is available to inform app_jack. * Frame.datalen is computed using "rate / 50" assuming a ptime of 20ms. There is no internal API available to determine datalen for a SLINxx. * Ringbuffer size is now dynamic depending on the value of frame.datalen (see above) and the number of frames, which are in RINGBUFFER_FRAME_CAPACITY, that need to fit. Review: https://reviewboard.asterisk.org/r/3618 Note that the patch being committed here is based on the patch posted on ASTERISK-23836. However, Matthis Schmieder also provided a patch to enable this functionality, and that patch is noted below. ASTERISK-20696 #close Reported by: Matthis Schmieder patches: app_jack.patch uploaded by Matthis Schmieder (License 6445) ASTERISK-23836 #close Reported by: Dennis Guse patches: patch-app_jack.c uploaded by Dennis Guse (License 6513) git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@417360 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
		| @@ -61,7 +61,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | ||||
|  | ||||
| #define RESAMPLE_QUALITY 1 | ||||
|  | ||||
| #define RINGBUFFER_SIZE 16384 | ||||
| /* The number of frames the ringbuffers can store. The actual size is RINGBUFFER_FRAME_CAPACITY * jack_data->frame_datalen */ | ||||
| #define RINGBUFFER_FRAME_CAPACITY 100 | ||||
|  | ||||
| /*! \brief Common options between the Jack() app and JACK_HOOK() function */ | ||||
| #define COMMON_OPTIONS \ | ||||
| @@ -128,6 +129,9 @@ struct jack_data { | ||||
| 	jack_port_t *output_port; | ||||
| 	jack_ringbuffer_t *input_rb; | ||||
| 	jack_ringbuffer_t *output_rb; | ||||
| 	enum ast_format_id audiohook_format_id; | ||||
| 	unsigned int audiohook_rate; | ||||
| 	unsigned int frame_datalen; | ||||
| 	void *output_resampler; | ||||
| 	double output_resample_factor; | ||||
| 	void *input_resampler; | ||||
| @@ -201,10 +205,8 @@ static int alloc_resampler(struct jack_data *jack_data, int input) | ||||
|  | ||||
| 	jack_srate = jack_get_sample_rate(jack_data->client); | ||||
|  | ||||
| 	/* XXX Hard coded 8 kHz */ | ||||
|  | ||||
| 	to_srate = input ? 8000.0 : jack_srate; | ||||
| 	from_srate = input ? jack_srate : 8000.0; | ||||
| 	to_srate = input ? jack_data->audiohook_rate : jack_srate; | ||||
| 	from_srate = input ? jack_srate : jack_data->audiohook_rate; | ||||
|  | ||||
| 	resample_factor = input ? &jack_data->input_resample_factor : | ||||
| 		&jack_data->output_resample_factor; | ||||
| @@ -289,7 +291,7 @@ static void handle_input(void *buf, jack_nframes_t nframes, | ||||
|  | ||||
| 	res = jack_ringbuffer_write(jack_data->input_rb, (const char *) s_buf, write_len); | ||||
| 	if (res != write_len) { | ||||
| 		ast_debug(2, "Tried to write %d bytes to the ringbuffer, but only wrote %d\n", | ||||
| 		ast_log(LOG_WARNING, "Tried to write %d bytes to the ringbuffer, but only wrote %d\n", | ||||
| 			(int) sizeof(s_buf), (int) res); | ||||
| 	} | ||||
| } | ||||
| @@ -392,6 +394,28 @@ static int init_jack_data(struct ast_channel *chan, struct jack_data *jack_data) | ||||
| 	jack_status_t status = 0; | ||||
| 	jack_options_t jack_options = JackNullOption; | ||||
|  | ||||
| 	struct ast_format format_slin; | ||||
| 	unsigned int channel_rate; | ||||
|  | ||||
| 	unsigned int ringbuffer_size; | ||||
|  | ||||
| 	/* Deducing audiohook sample rate from channel format | ||||
| 	   ATTENTION: Might be problematic, if channel has different sampling than used by audiohook! | ||||
| 	*/ | ||||
| 	channel_rate = ast_format_rate(ast_channel_readformat(chan)); | ||||
| 	jack_data->audiohook_format_id = ast_format_slin_by_rate(channel_rate); | ||||
|  | ||||
| 	ast_format_set(&format_slin, jack_data->audiohook_format_id, 0); | ||||
| 	jack_data->audiohook_rate = ast_format_rate(&format_slin); | ||||
|  | ||||
| 	/* Guessing frame->datalen assuming a ptime of 20ms */ | ||||
| 	jack_data->frame_datalen = jack_data->audiohook_rate / 50; | ||||
|  | ||||
| 	ringbuffer_size = jack_data->frame_datalen * RINGBUFFER_FRAME_CAPACITY; | ||||
|  | ||||
| 	ast_debug(1, "Audiohook parameters: slin-format:%d, rate:%d, frame-len:%d, ringbuffer_size: %d\n", | ||||
| 	    jack_data->audiohook_format_id, jack_data->audiohook_rate, jack_data->frame_datalen, ringbuffer_size); | ||||
|  | ||||
| 	if (!ast_strlen_zero(jack_data->client_name)) { | ||||
| 		client_name = jack_data->client_name; | ||||
| 	} else { | ||||
| @@ -400,10 +424,10 @@ static int init_jack_data(struct ast_channel *chan, struct jack_data *jack_data) | ||||
| 		ast_channel_unlock(chan); | ||||
| 	} | ||||
|  | ||||
| 	if (!(jack_data->output_rb = jack_ringbuffer_create(RINGBUFFER_SIZE))) | ||||
| 	if (!(jack_data->output_rb = jack_ringbuffer_create(ringbuffer_size))) | ||||
| 		return -1; | ||||
|  | ||||
| 	if (!(jack_data->input_rb = jack_ringbuffer_create(RINGBUFFER_SIZE))) | ||||
| 	if (!(jack_data->input_rb = jack_ringbuffer_create(ringbuffer_size))) | ||||
| 		return -1; | ||||
|  | ||||
| 	if (jack_data->no_start_server) | ||||
| @@ -573,10 +597,9 @@ static int queue_voice_frame(struct jack_data *jack_data, struct ast_frame *f) | ||||
|  | ||||
| 	res = jack_ringbuffer_write(jack_data->output_rb, (const char *) f_buf, f_buf_used * sizeof(float)); | ||||
| 	if (res != (f_buf_used * sizeof(float))) { | ||||
| 		ast_debug(2, "Tried to write %d bytes to the ringbuffer, but only wrote %d\n", | ||||
| 		ast_log(LOG_WARNING, "Tried to write %d bytes to the ringbuffer, but only wrote %d\n", | ||||
| 			(int) (f_buf_used * sizeof(float)), (int) res); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @@ -602,7 +625,7 @@ static int queue_voice_frame(struct jack_data *jack_data, struct ast_frame *f) | ||||
| static void handle_jack_audio(struct ast_channel *chan, struct jack_data *jack_data, | ||||
| 	struct ast_frame *out_frame) | ||||
| { | ||||
| 	short buf[160]; | ||||
| 	short buf[jack_data->frame_datalen]; | ||||
| 	struct ast_frame f = { | ||||
| 		.frametype = AST_FRAME_VOICE, | ||||
| 		.src = "JACK", | ||||
| @@ -610,7 +633,7 @@ static void handle_jack_audio(struct ast_channel *chan, struct jack_data *jack_d | ||||
| 		.datalen = sizeof(buf), | ||||
| 		.samples = ARRAY_LEN(buf), | ||||
| 	}; | ||||
| 	ast_format_set(&f.subclass.format, AST_FORMAT_SLINEAR, 0); | ||||
| 	ast_format_set(&f.subclass.format, jack_data->audiohook_format_id, 0); | ||||
|  | ||||
| 	for (;;) { | ||||
| 		size_t res, read_len; | ||||
| @@ -755,12 +778,12 @@ static int jack_exec(struct ast_channel *chan, const char *data) | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR)) { | ||||
| 	if (ast_set_read_format_by_id(chan, jack_data->audiohook_format_id)) { | ||||
| 		destroy_jack_data(jack_data); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR)) { | ||||
| 	if (ast_set_write_format_by_id(chan, jack_data->audiohook_format_id)) { | ||||
| 		destroy_jack_data(jack_data); | ||||
| 		return -1; | ||||
| 	} | ||||
| @@ -826,12 +849,6 @@ static int jack_hook_callback(struct ast_audiohook *audiohook, struct ast_channe | ||||
| 	if (frame->frametype != AST_FRAME_VOICE) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (frame->subclass.format.id != AST_FORMAT_SLINEAR) { | ||||
| 		ast_log(LOG_WARNING, "Expected frame in SLINEAR for the audiohook, but got format %s\n", | ||||
| 			ast_getformatname(&frame->subclass.format)); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	ast_channel_lock(chan); | ||||
|  | ||||
| 	if (!(datastore = ast_channel_datastore_find(chan, &jack_hook_ds_info, NULL))) { | ||||
| @@ -842,6 +859,13 @@ static int jack_hook_callback(struct ast_audiohook *audiohook, struct ast_channe | ||||
|  | ||||
| 	jack_data = datastore->data; | ||||
|  | ||||
| 	if (frame->subclass.format.id != jack_data->audiohook_format_id) { | ||||
| 		ast_log(LOG_WARNING, "Expected frame in SLINEAR with id %d for the audiohook, but got format %s\n", | ||||
| 		    jack_data->audiohook_format_id, ast_getformatname(&frame->subclass.format)); | ||||
| 		ast_channel_unlock(chan); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	queue_voice_frame(jack_data, frame); | ||||
|  | ||||
| 	handle_jack_audio(chan, jack_data, frame); | ||||
| @@ -888,7 +912,7 @@ static int enable_jack_hook(struct ast_channel *chan, char *data) | ||||
| 		goto return_error; | ||||
|  | ||||
| 	jack_data->has_audiohook = 1; | ||||
| 	ast_audiohook_init(&jack_data->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "JACK_HOOK", 0); | ||||
| 	ast_audiohook_init(&jack_data->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "JACK_HOOK", AST_AUDIOHOOK_MANIPULATE_ALL_RATES); | ||||
| 	jack_data->audiohook.manipulate_callback = jack_hook_callback; | ||||
|  | ||||
| 	datastore->data = jack_data; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user