Implement auto output resampling (if requested)
If there are for example more than one audio outputs and they have different sample rates or channels and such, this will allow automatic conversion of that audio to the request formats/channels/rates (but only if requested).master
parent
860a43c31f
commit
30094a5919
|
@ -24,6 +24,7 @@
|
||||||
#include "../util/platform.h"
|
#include "../util/platform.h"
|
||||||
|
|
||||||
#include "audio-io.h"
|
#include "audio-io.h"
|
||||||
|
#include "audio-resampler.h"
|
||||||
|
|
||||||
/* #define DEBUG_AUDIO */
|
/* #define DEBUG_AUDIO */
|
||||||
|
|
||||||
|
@ -31,10 +32,17 @@
|
||||||
|
|
||||||
struct audio_input {
|
struct audio_input {
|
||||||
struct audio_convert_info conversion;
|
struct audio_convert_info conversion;
|
||||||
|
audio_resampler_t resampler;
|
||||||
|
|
||||||
void (*callback)(void *param, const struct audio_data *data);
|
void (*callback)(void *param, const struct audio_data *data);
|
||||||
void *param;
|
void *param;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline audio_input_free(struct audio_input *input)
|
||||||
|
{
|
||||||
|
audio_resampler_destroy(input->resampler);
|
||||||
|
}
|
||||||
|
|
||||||
struct audio_line {
|
struct audio_line {
|
||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
|
@ -313,6 +321,31 @@ static inline bool mix_audio_line(struct audio_output *audio,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool resample_audio_output(struct audio_input *input,
|
||||||
|
struct audio_data *data)
|
||||||
|
{
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
if (input->resampler) {
|
||||||
|
uint8_t *output[MAX_AV_PLANES];
|
||||||
|
uint32_t frames;
|
||||||
|
uint64_t offset;
|
||||||
|
|
||||||
|
memset(output, 0, sizeof(output));
|
||||||
|
|
||||||
|
success = audio_resampler_resample(input->resampler,
|
||||||
|
output, &frames, &offset,
|
||||||
|
data->data, data->frames);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < MAX_AV_PLANES; i++)
|
||||||
|
data->data[i] = output[i];
|
||||||
|
data->frames = frames;
|
||||||
|
data->timestamp -= offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
static inline void do_audio_output(struct audio_output *audio,
|
static inline void do_audio_output(struct audio_output *audio,
|
||||||
uint64_t timestamp, uint32_t frames)
|
uint64_t timestamp, uint32_t frames)
|
||||||
{
|
{
|
||||||
|
@ -323,12 +356,15 @@ static inline void do_audio_output(struct audio_output *audio,
|
||||||
data.timestamp = timestamp;
|
data.timestamp = timestamp;
|
||||||
data.volume = 1.0f;
|
data.volume = 1.0f;
|
||||||
|
|
||||||
/* TODO: conversion */
|
|
||||||
pthread_mutex_lock(&audio->input_mutex);
|
pthread_mutex_lock(&audio->input_mutex);
|
||||||
|
|
||||||
for (size_t i = 0; i < audio->inputs.num; i++) {
|
for (size_t i = 0; i < audio->inputs.num; i++) {
|
||||||
struct audio_input *input = audio->inputs.array+i;
|
struct audio_input *input = audio->inputs.array+i;
|
||||||
input->callback(input->param, &data);
|
|
||||||
|
if (resample_audio_output(input, &data))
|
||||||
|
input->callback(input->param, &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&audio->input_mutex);
|
pthread_mutex_unlock(&audio->input_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -429,6 +465,30 @@ static size_t audio_get_input_idx(audio_t video,
|
||||||
return DARRAY_INVALID;
|
return DARRAY_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void audio_input_init(struct audio_input *input,
|
||||||
|
struct audio_output *audio)
|
||||||
|
{
|
||||||
|
if (input->conversion.format != audio->info.format ||
|
||||||
|
input->conversion.samples_per_sec != audio->info.samples_per_sec ||
|
||||||
|
input->conversion.speakers != audio->info.speakers) {
|
||||||
|
struct resample_info from = {
|
||||||
|
.format = audio->info.format,
|
||||||
|
.samples_per_sec = audio->info.samples_per_sec,
|
||||||
|
.speakers = audio->info.speakers
|
||||||
|
};
|
||||||
|
|
||||||
|
struct resample_info to = {
|
||||||
|
.format = input->conversion.format,
|
||||||
|
.samples_per_sec = input->conversion.samples_per_sec,
|
||||||
|
.speakers = input->conversion.speakers
|
||||||
|
};
|
||||||
|
|
||||||
|
input->resampler = audio_resampler_create(&to, &from);
|
||||||
|
} else {
|
||||||
|
input->resampler = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void audio_output_connect(audio_t audio,
|
void audio_output_connect(audio_t audio,
|
||||||
struct audio_convert_info *conversion,
|
struct audio_convert_info *conversion,
|
||||||
void (*callback)(void *param, const struct audio_data *data),
|
void (*callback)(void *param, const struct audio_data *data),
|
||||||
|
@ -441,7 +501,6 @@ void audio_output_connect(audio_t audio,
|
||||||
input.callback = callback;
|
input.callback = callback;
|
||||||
input.param = param;
|
input.param = param;
|
||||||
|
|
||||||
/* TODO: conversion */
|
|
||||||
if (conversion) {
|
if (conversion) {
|
||||||
input.conversion = *conversion;
|
input.conversion = *conversion;
|
||||||
} else {
|
} else {
|
||||||
|
@ -451,6 +510,7 @@ void audio_output_connect(audio_t audio,
|
||||||
audio->info.samples_per_sec;
|
audio->info.samples_per_sec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
audio_input_init(&input, audio);
|
||||||
da_push_back(audio->inputs, &input);
|
da_push_back(audio->inputs, &input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,8 +524,10 @@ void audio_output_disconnect(audio_t audio,
|
||||||
pthread_mutex_lock(&audio->input_mutex);
|
pthread_mutex_lock(&audio->input_mutex);
|
||||||
|
|
||||||
size_t idx = audio_get_input_idx(audio, callback, param);
|
size_t idx = audio_get_input_idx(audio, callback, param);
|
||||||
if (idx != DARRAY_INVALID)
|
if (idx != DARRAY_INVALID) {
|
||||||
|
audio_input_free(audio->inputs.array+idx);
|
||||||
da_erase(audio->inputs, idx);
|
da_erase(audio->inputs, idx);
|
||||||
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&audio->input_mutex);
|
pthread_mutex_unlock(&audio->input_mutex);
|
||||||
}
|
}
|
||||||
|
@ -536,9 +598,13 @@ void audio_output_close(audio_t audio)
|
||||||
line = next;
|
line = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < audio->inputs.num; i++)
|
||||||
|
audio_input_free(audio->inputs.array+i);
|
||||||
|
|
||||||
for (size_t i = 0; i < MAX_AV_PLANES; i++)
|
for (size_t i = 0; i < MAX_AV_PLANES; i++)
|
||||||
da_free(audio->mix_buffers[i]);
|
da_free(audio->mix_buffers[i]);
|
||||||
|
|
||||||
|
da_free(audio->inputs);
|
||||||
event_destroy(&audio->stop_event);
|
event_destroy(&audio->stop_event);
|
||||||
pthread_mutex_destroy(&audio->line_mutex);
|
pthread_mutex_destroy(&audio->line_mutex);
|
||||||
bfree(audio);
|
bfree(audio);
|
||||||
|
|
|
@ -576,7 +576,7 @@ static bool ffmpeg_output_start(void *data)
|
||||||
|
|
||||||
struct audio_convert_info aci;
|
struct audio_convert_info aci;
|
||||||
aci.samples_per_sec = SPS_TODO;
|
aci.samples_per_sec = SPS_TODO;
|
||||||
aci.format = AUDIO_FORMAT_FLOAT;
|
aci.format = AUDIO_FORMAT_FLOAT_PLANAR;
|
||||||
aci.speakers = SPEAKERS_STEREO;
|
aci.speakers = SPEAKERS_STEREO;
|
||||||
|
|
||||||
struct video_convert_info vci;
|
struct video_convert_info vci;
|
||||||
|
|
Loading…
Reference in New Issue