#define AL_LOG_SECTION "ff_resampler" #include #include #include #include "resampler.h" static bool ff_resampler_init(struct camu_resampler *resamp, struct camu_resampler_format *fmt) { struct camu_ff_resampler *av = (struct camu_ff_resampler *)resamp; av->resample_context = NULL; al_assert(fmt->resampler_needed); camu_audio_format_copy(&av->fmt, &fmt->req); SwrContext *swr = swr_alloc(); #ifndef CAMU_OLD_FFMPEG av_opt_set_chlayout(swr, "in_chlayout", &fmt->in.channel_layout, 0); av_opt_set_chlayout(swr, "out_chlayout", &fmt->req.channel_layout, 0); #else av_opt_set_channel_layout(swr, "in_channel_layout", fmt->in.channel_layout, 0); av_opt_set_channel_layout(swr, "out_channel_layout", fmt->req.channel_layout, 0); #endif av_opt_set_int(swr, "in_sample_rate", fmt->in.sample_rate, 0); av_opt_set_int(swr, "out_sample_rate", fmt->req.sample_rate, 0); av_opt_set_sample_fmt(swr, "in_sample_fmt", (enum AVSampleFormat)fmt->in.format, 0); av_opt_set_sample_fmt(swr, "out_sample_fmt", (enum AVSampleFormat)fmt->req.format, 0); #ifdef CAMU_HAVE_SOXR // Slower but more accurate resampler. av_opt_set_int(swr, "resampler", SWR_ENGINE_SOXR, 0); log_info("Using SoX resampler."); #endif s32 ret = swr_init(swr); if (ret != 0) { swr_free(&swr); log_error("Failed to init resampler context (%s).", av_err2str(ret)); return false; } av->resample_context = swr; return true; } static inline void free_sample_data(struct camu_ff_resampler *av) { if (av->data[0]) av_freep(&av->data[0]); } static inline void alloc_sample_data(struct camu_ff_resampler *av, s32 sample_count) { if (av->sample_count >= sample_count) return; free_sample_data(av); s32 nb_channels = av->fmt.channel_count; av_samples_alloc(av->data, NULL, nb_channels, sample_count, (enum AVSampleFormat)av->fmt.format, 32); av->sample_count = sample_count; } static s32 ff_resampler_convert(struct camu_resampler *resamp, const u8 **in_data, s32 in_samples) { struct camu_ff_resampler *av = (struct camu_ff_resampler *)resamp; al_assert(in_samples > 0); s32 out_resample_count = swr_get_out_samples(av->resample_context, in_samples); alloc_sample_data(av, out_resample_count); s32 resample_count = swr_convert(av->resample_context, av->data, out_resample_count, in_data, in_samples); if (resample_count < 0) { log_error("Failed to convert samples (%d).", resample_count); } return resample_count; } static s32 ff_resampler_flush(struct camu_resampler *resamp) { struct camu_ff_resampler *av = (struct camu_ff_resampler *)resamp; s32 resample_count = swr_convert(av->resample_context, av->data, av->sample_count, NULL, 0); if (swr_init(av->resample_context) != 0) { swr_free(&av->resample_context); log_error("Failed to re-init resampler context after flush."); return -1; } return resample_count; } static u8 **ff_resampler_get_data(struct camu_resampler *resamp) { struct camu_ff_resampler *av = (struct camu_ff_resampler *)resamp; return (u8 **)av->data; } static void ff_resampler_free(struct camu_resampler **resamp) { struct camu_ff_resampler *av = (struct camu_ff_resampler *)*resamp; if (av->resample_context) { swr_close(av->resample_context); swr_free(&av->resample_context); } free_sample_data(av); al_free(av); *resamp = NULL; } struct camu_resampler *camu_ff_resampler_create(void) { struct camu_ff_resampler *av = al_alloc_object(struct camu_ff_resampler); av->resamp.init = ff_resampler_init; av->resamp.convert = ff_resampler_convert; av->resamp.flush = ff_resampler_flush; av->resamp.get_data = ff_resampler_get_data; av->resamp.free = ff_resampler_free; return (struct camu_resampler *)av; }