From fb81332e803da17b944dbff658fb2ee292118a4d Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 2 Jan 2017 13:41:13 -0500 Subject: [PATCH] handle multiple different sample types fixes #306 --- recording-daemon/Makefile | 2 +- recording-daemon/decoder.c | 128 ++++++++++------------------------ recording-daemon/mix.c | 132 +++++++----------------------------- recording-daemon/mix.h | 2 +- recording-daemon/output.c | 55 +++++++++------ recording-daemon/output.h | 2 +- recording-daemon/resample.c | 104 ++++++++++++++++++++++++++++ recording-daemon/resample.h | 13 ++++ recording-daemon/types.h | 34 ++++++++++ 9 files changed, 248 insertions(+), 224 deletions(-) create mode 100644 recording-daemon/resample.c create mode 100644 recording-daemon/resample.h diff --git a/recording-daemon/Makefile b/recording-daemon/Makefile index 9b9827da3..4266a54c7 100644 --- a/recording-daemon/Makefile +++ b/recording-daemon/Makefile @@ -23,7 +23,7 @@ LDFLAGS+= `pkg-config --libs libavfilter` include ../lib/lib.Makefile SRCS= epoll.c garbage.c inotify.c main.c metafile.c stream.c recaux.c packet.c \ - decoder.c output.c mix.c + decoder.c output.c mix.c resample.c LIBSRCS= loglib.c auxlib.c rtplib.c OBJS= $(SRCS:.c=.o) $(LIBSRCS:.c=.o) diff --git a/recording-daemon/decoder.c b/recording-daemon/decoder.c index 89d1fe979..291a4d7d1 100644 --- a/recording-daemon/decoder.c +++ b/recording-daemon/decoder.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -13,17 +14,15 @@ #include "str.h" #include "output.h" #include "mix.h" +#include "resample.h" struct decoder_s { - // format params - int channels; - int in_clockrate; - int out_clockrate; + format_t in_format, + out_format; - AVAudioResampleContext *avresample; - AVFrame *swr_frame; - int swr_buffers; + resample_t mix_resample, + output_resample; AVCodecContext *avcctx; AVPacket avpkt; @@ -126,9 +125,14 @@ decoder_t *decoder_new(const char *payload_str) { decoder_t *ret = g_slice_alloc0(sizeof(*ret)); - ret->channels = channels; - ret->in_clockrate = clockrate; - ret->out_clockrate = resample_audio ? : clockrate; + format_init(&ret->in_format); + ret->in_format.channels = channels; + ret->in_format.clockrate = clockrate; + // output defaults to same as input + ret->out_format = ret->in_format; + if (resample_audio) + ret->out_format.clockrate = resample_audio; + // sample format to be determined later when decoded frames arrive AVCodec *codec = NULL; if (def->avcodec_name) @@ -151,6 +155,9 @@ decoder_t *decoder_new(const char *payload_str) { if (i) goto err; + for (const enum AVSampleFormat *sfmt = codec->sample_fmts; sfmt && *sfmt != -1; sfmt++) + dbg("supported sample format for input codec %s: %s", codec->name, av_get_sample_fmt_name(*sfmt)); + av_init_packet(&ret->avpkt); ret->frame = av_frame_alloc(); err = "failed to alloc av frame"; @@ -171,100 +178,37 @@ err: } -static AVFrame *decoder_resample_frame(decoder_t *dec) { - const char *err; - - if (dec->in_clockrate == dec->out_clockrate) - return dec->frame; - - if (!dec->avresample) { - dec->avresample = avresample_alloc_context(); - err = "failed to alloc resample context"; - if (!dec->avresample) - goto err; - - av_opt_set_int(dec->avresample, "in_channel_layout", - av_get_default_channel_layout(dec->channels), 0); - av_opt_set_int(dec->avresample, "in_sample_fmt", - AV_SAMPLE_FMT_S16, 0); - av_opt_set_int(dec->avresample, "in_sample_rate", - dec->in_clockrate, 0); - av_opt_set_int(dec->avresample, "out_channel_layout", - av_get_default_channel_layout(dec->channels), 0); - av_opt_set_int(dec->avresample, "out_sample_fmt", - AV_SAMPLE_FMT_S16, 0); - av_opt_set_int(dec->avresample, "out_sample_rate", - dec->out_clockrate, 0); - // av_opt_set_int(dec->avresample, "internal_sample_fmt", AV_SAMPLE_FMT_FLTP, 0); // ? - - err = "failed to init resample context"; - if (avresample_open(dec->avresample) < 0) - goto err; - } - - // get a large enough buffer for resampled audio - this should be enough so we don't - // have to loop - int dst_samples = avresample_available(dec->avresample) + - av_rescale_rnd(avresample_get_delay(dec->avresample) + dec->frame->nb_samples, - dec->out_clockrate, dec->in_clockrate, AV_ROUND_UP); - if (!dec->swr_frame || dec->swr_buffers < dst_samples) { - av_frame_free(&dec->swr_frame); - dec->swr_frame = av_frame_alloc(); - err = "failed to alloc resampling frame"; - if (!dec->swr_frame) - goto err; - av_frame_copy_props(dec->swr_frame, dec->frame); - dec->swr_frame->format = dec->frame->format; - dec->swr_frame->channel_layout = dec->frame->channel_layout; - dec->swr_frame->nb_samples = dst_samples; - dec->swr_frame->sample_rate = dec->out_clockrate; - err = "failed to get resample buffers"; - if (av_frame_get_buffer(dec->swr_frame, 0) < 0) - goto err; - dec->swr_buffers = dst_samples; - } - - dec->swr_frame->nb_samples = dst_samples; - int ret_samples = avresample_convert(dec->avresample, dec->swr_frame->extended_data, - dec->swr_frame->linesize[0], dst_samples, - dec->frame->extended_data, - dec->frame->linesize[0], dec->frame->nb_samples); - err = "failed to resample audio"; - if (ret_samples < 0) - goto err; - - dec->swr_frame->nb_samples = ret_samples; - dec->swr_frame->pts = av_rescale(dec->frame->pts, dec->out_clockrate, dec->in_clockrate); - return dec->swr_frame; - -err: - ilog(LOG_ERR, "Error resampling: %s", err); - return NULL; -} - - static int decoder_got_frame(decoder_t *dec, output_t *output, metafile_t *metafile) { - // do we need to resample? - AVFrame *dec_frame = decoder_resample_frame(dec); - if (!dec_frame) - return -1; + // determine and save sample type + if (G_UNLIKELY(dec->in_format.format == -1)) + dec->in_format.format = dec->out_format.format = dec->frame->format; // handle mix output pthread_mutex_lock(&metafile->mix_lock); if (metafile->mix_out) { if (G_UNLIKELY(dec->mixer_idx == (unsigned int) -1)) dec->mixer_idx = mix_get_index(metafile->mix); - output_config(metafile->mix_out, dec->out_clockrate, dec->channels); - mix_config(metafile->mix, dec->out_clockrate, dec->channels); + format_t actual_format; + output_config(metafile->mix_out, &dec->out_format, &actual_format); + mix_config(metafile->mix, &actual_format); + AVFrame *dec_frame = resample_frame(&dec->mix_resample, dec->frame, &actual_format); + if (!dec_frame) { + pthread_mutex_unlock(&metafile->mix_lock); + return -1; + } AVFrame *clone = av_frame_clone(dec_frame); - clone->pts = dec_frame->pts; if (mix_add(metafile->mix, clone, dec->mixer_idx, metafile->mix_out)) ilog(LOG_ERR, "Failed to add decoded packet to mixed output"); } pthread_mutex_unlock(&metafile->mix_lock); if (output) { - output_config(output, dec->out_clockrate, dec->channels); + // XXX might be a second resampling to same format + format_t actual_format; + output_config(output, &dec->out_format, &actual_format); + AVFrame *dec_frame = resample_frame(&dec->output_resample, dec->frame, &actual_format); + if (!dec_frame) + return -1; if (output_add(output, dec_frame)) ilog(LOG_ERR, "Failed to add decoded packet to individual output"); } @@ -388,7 +332,7 @@ void decoder_close(decoder_t *dec) { av_free(dec->avcctx); #endif av_frame_free(&dec->frame); - av_frame_free(&dec->swr_frame); - avresample_free(&dec->avresample); + resample_shutdown(&dec->mix_resample); + resample_shutdown(&dec->output_resample); g_slice_free1(sizeof(*dec), dec); } diff --git a/recording-daemon/mix.c b/recording-daemon/mix.c index 7eb281c92..9e91e4949 100644 --- a/recording-daemon/mix.c +++ b/recording-daemon/mix.c @@ -11,15 +11,14 @@ #include "types.h" #include "log.h" #include "output.h" +#include "resample.h" #define NUM_INPUTS 4 struct mix_s { - // format params - int clockrate; - int channels; + format_t format; AVFilterGraph *graph; AVFilterContext *src_ctxs[NUM_INPUTS]; @@ -30,9 +29,8 @@ struct mix_s { unsigned int next_idx; AVFrame *sink_frame; - AVAudioResampleContext *avresample; - AVFrame *swr_frame; - int swr_buffers; + resample_t resample; + uint64_t out_pts; // starting at zero AVFrame *silence_frame; @@ -54,8 +52,10 @@ static void mix_shutdown(mix_t *mix) { mix->src_ctxs[i] = NULL; } - avresample_free(&mix->avresample); + resample_shutdown(&mix->resample); avfilter_graph_free(&mix->graph); + + format_init(&mix->format); } @@ -64,7 +64,6 @@ void mix_destroy(mix_t *mix) { return; mix_shutdown(mix); av_frame_free(&mix->sink_frame); - av_frame_free(&mix->swr_frame); av_frame_free(&mix->silence_frame); g_slice_free1(sizeof(*mix), mix); } @@ -75,25 +74,16 @@ unsigned int mix_get_index(mix_t *mix) { } -int mix_config(mix_t *mix, unsigned int clockrate, unsigned int channels) { +int mix_config(mix_t *mix, const format_t *format) { const char *err; char args[512]; - // anything to do? - if (G_UNLIKELY(mix->clockrate != clockrate)) - goto format_mismatch; - if (G_UNLIKELY(mix->channels != channels)) - goto format_mismatch; - - // all good - return 0; + if (format_eq(format, &mix->format)) + return 0; -format_mismatch: mix_shutdown(mix); - // copy params - mix->clockrate = clockrate; - mix->channels = channels; + mix->format = *format; // filter graph err = "failed to alloc filter graph"; @@ -123,9 +113,9 @@ format_mismatch: snprintf(args, sizeof(args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:" "channel_layout=0x%" PRIx64, - 1, mix->clockrate, mix->clockrate, - av_get_sample_fmt_name(AV_SAMPLE_FMT_S16), - av_get_default_channel_layout(mix->channels)); + 1, mix->format.clockrate, mix->format.clockrate, + av_get_sample_fmt_name(mix->format.format), + av_get_default_channel_layout(mix->format.channels)); err = "failed to create abuffer filter context"; if (avfilter_graph_create_filter(&mix->src_ctxs[i], flt, NULL, args, NULL, mix->graph)) @@ -166,8 +156,7 @@ err: mix_t *mix_new() { mix_t *mix = g_slice_alloc0(sizeof(*mix)); - mix->clockrate = -1; - mix->channels = -1; + format_init(&mix->format); mix->sink_frame = av_frame_alloc(); for (int i = 0; i < NUM_INPUTS; i++) @@ -177,94 +166,24 @@ mix_t *mix_new() { } -static AVFrame *mix_resample_frame(mix_t *mix, AVFrame *frame) { - const char *err; - - if (frame->format == AV_SAMPLE_FMT_S16) - return frame; - - if (!mix->avresample) { - mix->avresample = avresample_alloc_context(); - err = "failed to alloc resample context"; - if (!mix->avresample) - goto err; - - av_opt_set_int(mix->avresample, "in_channel_layout", - av_get_default_channel_layout(mix->channels), 0); - av_opt_set_int(mix->avresample, "in_sample_fmt", - frame->format, 0); - av_opt_set_int(mix->avresample, "in_sample_rate", - mix->clockrate, 0); - av_opt_set_int(mix->avresample, "out_channel_layout", - av_get_default_channel_layout(mix->channels), 0); - av_opt_set_int(mix->avresample, "out_sample_fmt", - AV_SAMPLE_FMT_S16, 0); - av_opt_set_int(mix->avresample, "out_sample_rate", - mix->clockrate, 0); - // av_opt_set_int(dec->avresample, "internal_sample_fmt", AV_SAMPLE_FMT_FLTP, 0); // ? - - err = "failed to init resample context"; - if (avresample_open(mix->avresample) < 0) - goto err; - } - - // get a large enough buffer for resampled audio - this should be enough so we don't - // have to loop - int dst_samples = avresample_available(mix->avresample) + - av_rescale_rnd(avresample_get_delay(mix->avresample) + frame->nb_samples, - mix->clockrate, mix->clockrate, AV_ROUND_UP); - if (!mix->swr_frame || mix->swr_buffers < dst_samples) { - av_frame_free(&mix->swr_frame); - mix->swr_frame = av_frame_alloc(); - err = "failed to alloc resampling frame"; - if (!mix->swr_frame) - goto err; - av_frame_copy_props(mix->swr_frame, frame); - mix->swr_frame->format = frame->format; - mix->swr_frame->channel_layout = frame->channel_layout; - mix->swr_frame->nb_samples = dst_samples; - mix->swr_frame->sample_rate = mix->clockrate; - err = "failed to get resample buffers"; - if (av_frame_get_buffer(mix->swr_frame, 0) < 0) - goto err; - mix->swr_buffers = dst_samples; - } - - mix->swr_frame->nb_samples = dst_samples; - int ret_samples = avresample_convert(mix->avresample, mix->swr_frame->extended_data, - mix->swr_frame->linesize[0], dst_samples, - frame->extended_data, - frame->linesize[0], frame->nb_samples); - err = "failed to resample audio"; - if (ret_samples < 0) - goto err; - - mix->swr_frame->nb_samples = ret_samples; - mix->swr_frame->pts = av_rescale(frame->pts, mix->clockrate, mix->clockrate); - return mix->swr_frame; - -err: - ilog(LOG_ERR, "Error resampling: %s", err); - return NULL; -} - - static void mix_silence_fill_idx_upto(mix_t *mix, unsigned int idx, uint64_t upto) { - unsigned int silence_samples = mix->clockrate / 100; + unsigned int silence_samples = mix->format.clockrate / 100; while (mix->in_pts[idx] < upto) { if (G_UNLIKELY(!mix->silence_frame)) { mix->silence_frame = av_frame_alloc(); - mix->silence_frame->format = AV_SAMPLE_FMT_S16; + mix->silence_frame->format = mix->format.format; mix->silence_frame->channel_layout = - av_get_default_channel_layout(mix->channels); + av_get_default_channel_layout(mix->format.channels); mix->silence_frame->nb_samples = silence_samples; - mix->silence_frame->sample_rate = mix->clockrate; + mix->silence_frame->sample_rate = mix->format.clockrate; if (av_frame_get_buffer(mix->silence_frame, 0) < 0) { ilog(LOG_ERR, "Failed to get silence frame buffers"); return; } - memset(mix->silence_frame->extended_data[0], 0, mix->silence_frame->linesize[0]); + int planes = av_sample_fmt_is_planar(mix->silence_frame->format) ? mix->format.channels : 1; + for (int i = 0; i < planes; i++) + memset(mix->silence_frame->extended_data[i], 0, mix->silence_frame->linesize[0]); } dbg("pushing silence frame into stream %i (%lli < %llu)", idx, @@ -282,19 +201,18 @@ static void mix_silence_fill_idx_upto(mix_t *mix, unsigned int idx, uint64_t upt static void mix_silence_fill(mix_t *mix) { - if (mix->out_pts < mix->clockrate) + if (mix->out_pts < mix->format.clockrate) return; for (int i = 0; i < NUM_INPUTS; i++) { // check the pts of each input and give them max 1 second of delay. // if they fall behind too much, fill input with silence. otherwise // output stalls and won't produce media - mix_silence_fill_idx_upto(mix, i, mix->out_pts - mix->clockrate); + mix_silence_fill_idx_upto(mix, i, mix->out_pts - mix->format.clockrate); } } -// frees the frame passed to it int mix_add(mix_t *mix, AVFrame *frame, unsigned int idx, output_t *output) { const char *err; @@ -347,7 +265,7 @@ int mix_add(mix_t *mix, AVFrame *frame, unsigned int idx, output_t *output) { else goto err; } - frame = mix_resample_frame(mix, mix->sink_frame); + frame = resample_frame(&mix->resample, mix->sink_frame, &mix->format); ret = output_add(output, frame); diff --git a/recording-daemon/mix.h b/recording-daemon/mix.h index e631cf73f..104561302 100644 --- a/recording-daemon/mix.h +++ b/recording-daemon/mix.h @@ -8,7 +8,7 @@ mix_t *mix_new(void); void mix_destroy(mix_t *mix); -int mix_config(mix_t *, unsigned int clockrate, unsigned int channels); +int mix_config(mix_t *, const format_t *format); int mix_add(mix_t *mix, AVFrame *frame, unsigned int idx, output_t *output); unsigned int mix_get_index(mix_t *); diff --git a/recording-daemon/output.c b/recording-daemon/output.c index 3d092a773..7eea31e1a 100644 --- a/recording-daemon/output.c +++ b/recording-daemon/output.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -13,9 +14,8 @@ struct output_s { char filename[PATH_MAX]; - // format params - int clockrate; - int channels; + format_t requested_format, + actual_format; AVCodecContext *avcctx; AVFormatContext *fmtctx; @@ -150,30 +150,22 @@ int output_add(output_t *output, AVFrame *frame) { output_t *output_new(const char *filename) { output_t *ret = g_slice_alloc0(sizeof(*ret)); g_strlcpy(ret->filename, filename, sizeof(ret->filename)); - ret->clockrate = -1; - ret->channels = -1; + format_init(&ret->requested_format); + format_init(&ret->actual_format); return ret; } -int output_config(output_t *output, unsigned int clockrate, unsigned int channels) { +int output_config(output_t *output, const format_t *requested_format, format_t *actual_format) { const char *err; // anything to do? - if (G_UNLIKELY(output->clockrate != clockrate)) - goto format_mismatch; - if (G_UNLIKELY(output->channels != channels)) - goto format_mismatch; + if (G_LIKELY(format_eq(requested_format, &output->requested_format))) + goto done; - // all good - return 0; - -format_mismatch: output_shutdown(output); - // copy params - output->clockrate = clockrate; - output->channels = channels; + output->requested_format = *requested_format; err = "failed to alloc format context"; output->fmtctx = avformat_alloc_context(); @@ -201,11 +193,25 @@ format_mismatch: output->avcctx = output->avst->codec; #endif - output->avcctx->channels = output->channels; - output->avcctx->channel_layout = av_get_default_channel_layout(output->channels); - output->avcctx->sample_rate = output->clockrate; - output->avcctx->sample_fmt = AV_SAMPLE_FMT_S16; - output->avcctx->time_base = (AVRational){1,output->clockrate}; + // copy all format params + output->actual_format = output->requested_format; + + // determine sample format to use + output->actual_format.format = -1; + for (const enum AVSampleFormat *sfmt = codec->sample_fmts; sfmt && *sfmt != -1; sfmt++) { + dbg("supported sample format for output codec %s: %s", codec->name, av_get_sample_fmt_name(*sfmt)); + if (*sfmt == requested_format->format) + output->actual_format.format = *sfmt; + } + if (output->actual_format.format == -1 && codec->sample_fmts) + output->actual_format.format = codec->sample_fmts[0]; + dbg("using output sample format %s for codec %s", av_get_sample_fmt_name(output->actual_format.format), codec->name); + + output->avcctx->channels = output->actual_format.channels; + output->avcctx->channel_layout = av_get_default_channel_layout(output->actual_format.channels); + output->avcctx->sample_rate = output->actual_format.clockrate; + output->avcctx->sample_fmt = output->actual_format.format; + output->avcctx->time_base = (AVRational){1,output->actual_format.clockrate}; output->avcctx->bit_rate = mp3_bitrate; output->avst->time_base = output->avcctx->time_base; @@ -255,6 +261,8 @@ got_fn: output->fifo = av_audio_fifo_alloc(output->avcctx->sample_fmt, output->avcctx->channels, output->frame->nb_samples); +done: + *actual_format = output->actual_format; return 0; err: @@ -286,6 +294,9 @@ static void output_shutdown(output_t *output) { output->fifo_pts = 0; output->mux_dts = 0; + + format_init(&output->requested_format); + format_init(&output->actual_format); } diff --git a/recording-daemon/output.h b/recording-daemon/output.h index 184e8be7d..44dd34268 100644 --- a/recording-daemon/output.h +++ b/recording-daemon/output.h @@ -13,7 +13,7 @@ void output_init(const char *format); output_t *output_new(const char *filename); void output_close(output_t *); -int output_config(output_t *output, unsigned int clockrate, unsigned int channels); +int output_config(output_t *output, const format_t *requested_format, format_t *actual_format); int output_add(output_t *output, AVFrame *frame); diff --git a/recording-daemon/resample.c b/recording-daemon/resample.c new file mode 100644 index 000000000..ea66d15bc --- /dev/null +++ b/recording-daemon/resample.c @@ -0,0 +1,104 @@ +#include "resample.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "log.h" +#include "types.h" + + + + +AVFrame *resample_frame(resample_t *resample, AVFrame *frame, const format_t *to_format) { + const char *err; + + uint64_t to_channel_layout = av_get_default_channel_layout(to_format->channels); + if (frame->format != to_format->format) + goto resample; + if (frame->sample_rate != to_format->clockrate) + goto resample; + if (frame->channel_layout != to_channel_layout) + goto resample; + + return frame; + +resample: + + if (G_UNLIKELY(!resample->avresample)) { + resample->avresample = avresample_alloc_context(); + err = "failed to alloc resample context"; + if (!resample->avresample) + goto err; + + av_opt_set_int(resample->avresample, "in_channel_layout", + frame->channel_layout, 0); + av_opt_set_int(resample->avresample, "in_sample_fmt", + frame->format, 0); + av_opt_set_int(resample->avresample, "in_sample_rate", + frame->sample_rate, 0); + av_opt_set_int(resample->avresample, "out_channel_layout", + to_channel_layout, 0); + av_opt_set_int(resample->avresample, "out_sample_fmt", + to_format->format, 0); + av_opt_set_int(resample->avresample, "out_sample_rate", + to_format->clockrate, 0); + // av_opt_set_int(dec->avresample, "internal_sample_fmt", AV_SAMPLE_FMT_FLTP, 0); // ? + + err = "failed to init resample context"; + if (avresample_open(resample->avresample) < 0) + goto err; + } + + // get a large enough buffer for resampled audio - this should be enough so we don't + // have to loop + int dst_samples = avresample_available(resample->avresample) + + av_rescale_rnd(avresample_get_delay(resample->avresample) + frame->nb_samples, + to_format->clockrate, frame->sample_rate, AV_ROUND_UP); + if (G_UNLIKELY(!resample->swr_frame || resample->swr_buffers < dst_samples)) { + av_frame_free(&resample->swr_frame); + dbg("allocating resampling frame for %i/%i/%i", to_format->format, (int) to_channel_layout, + to_format->clockrate); + resample->swr_frame = av_frame_alloc(); + err = "failed to alloc resampling frame"; + if (!resample->swr_frame) + goto err; + av_frame_copy_props(resample->swr_frame, frame); + resample->swr_frame->format = to_format->format; + resample->swr_frame->channel_layout = to_channel_layout; + resample->swr_frame->nb_samples = dst_samples; + resample->swr_frame->sample_rate = to_format->clockrate; + err = "failed to get resample buffers"; + if (av_frame_get_buffer(resample->swr_frame, 0) < 0) + goto err; + resample->swr_buffers = dst_samples; + } + + resample->swr_frame->nb_samples = dst_samples; + int ret_samples = avresample_convert(resample->avresample, resample->swr_frame->extended_data, + resample->swr_frame->linesize[0], dst_samples, + frame->extended_data, + frame->linesize[0], frame->nb_samples); + err = "failed to resample audio"; + if (ret_samples < 0) + goto err; + + resample->swr_frame->nb_samples = ret_samples; + resample->swr_frame->pts = av_rescale(frame->pts, to_format->clockrate, frame->sample_rate); + return resample->swr_frame; + +err: + ilog(LOG_ERR, "Error resampling: %s", err); + return NULL; +} + + +void resample_shutdown(resample_t *resample) { + av_frame_free(&resample->swr_frame); + avresample_free(&resample->avresample); +} diff --git a/recording-daemon/resample.h b/recording-daemon/resample.h new file mode 100644 index 000000000..ba8f155d0 --- /dev/null +++ b/recording-daemon/resample.h @@ -0,0 +1,13 @@ +#ifndef _RESAMPLE_H_ +#define _RESAMPLE_H_ + + +#include "types.h" +#include + + +AVFrame *resample_frame(resample_t *resample, AVFrame *frame, const format_t *to_format); +void resample_shutdown(resample_t *resample); + + +#endif diff --git a/recording-daemon/types.h b/recording-daemon/types.h index b1b63d243..f87be6df9 100644 --- a/recording-daemon/types.h +++ b/recording-daemon/types.h @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include "str.h" @@ -24,6 +26,8 @@ struct output_s; typedef struct output_s output_t; struct mix_s; typedef struct mix_s mix_t; +struct resample_s; +typedef struct resample_s resample_t; typedef void handler_func(handler_t *); @@ -93,4 +97,34 @@ struct metafile_s { }; +struct resample_s { + AVAudioResampleContext *avresample; + AVFrame *swr_frame; + int swr_buffers; +}; + + +struct format_s { + int clockrate; + int channels; + int format; // enum AVSampleFormat +}; +typedef struct format_s format_t; + +INLINE int format_eq(const format_t *a, const format_t *b) { + if (G_UNLIKELY(a->clockrate != b->clockrate)) + return 0; + if (G_UNLIKELY(a->channels != b->channels)) + return 0; + if (G_UNLIKELY(a->format != b->format)) + return 0; + return 1; +} +INLINE void format_init(format_t *f) { + f->clockrate = -1; + f->channels = -1; + f->format = -1; +} + + #endif