#include "mix.h" #include #include #include #include #include #include #include #include #include "types.h" #include "log.h" #include "output.h" struct mix_s { // format params int clockrate; int channels; AVFilterGraph *graph; AVFilterContext *src_ctxs[2]; AVFilterContext *amix_ctx; AVFilterContext *sink_ctx; unsigned int next_idx; AVAudioResampleContext *avresample; AVFrame *swr_frame; int swr_buffers; }; static void mix_shutdown(mix_t *mix) { if (mix->amix_ctx) avfilter_free(mix->amix_ctx); mix->amix_ctx = NULL; if (mix->sink_ctx) avfilter_free(mix->sink_ctx); mix->sink_ctx = NULL; for (int i = 0; i < G_N_ELEMENTS(mix->src_ctxs); i++) { if (mix->src_ctxs[i]) avfilter_free(mix->src_ctxs[i]); mix->src_ctxs[i] = NULL; } avfilter_graph_free(&mix->graph); } void mix_destroy(mix_t *mix) { mix_shutdown(mix); g_slice_free1(sizeof(*mix), mix); } unsigned int mix_get_index(mix_t *mix) { return mix->next_idx++; } int mix_config(mix_t *mix, unsigned int clockrate, unsigned int channels) { 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; format_mismatch: mix_shutdown(mix); // copy params mix->clockrate = clockrate; mix->channels = channels; // filter graph err = "failed to alloc filter graph"; mix->graph = avfilter_graph_alloc(); if (!mix->graph) goto err; // amix err = "no amix filter available"; AVFilter *flt = avfilter_get_by_name("amix"); if (!flt) goto err; snprintf(args, sizeof(args), "inputs=%lu", (unsigned long) G_N_ELEMENTS(mix->src_ctxs)); err = "failed to create amix filter context"; if (avfilter_graph_create_filter(&mix->amix_ctx, flt, NULL, args, NULL, mix->graph)) goto err; // inputs err = "no abuffer filter available"; flt = avfilter_get_by_name("abuffer"); if (!flt) goto err; for (int i = 0; i < G_N_ELEMENTS(mix->src_ctxs); i++) { dbg("init input ctx %i", i); 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)); err = "failed to create abuffer filter context"; if (avfilter_graph_create_filter(&mix->src_ctxs[i], flt, NULL, args, NULL, mix->graph)) goto err; err = "failed to link abuffer to amix"; if (avfilter_link(mix->src_ctxs[i], 0, mix->amix_ctx, i)) goto err; } // sink err = "no abuffersink filter available"; flt = avfilter_get_by_name("abuffersink"); if (!flt) goto err; err = "failed to create abuffersink filter context"; if (avfilter_graph_create_filter(&mix->sink_ctx, flt, NULL, NULL, NULL, mix->graph)) goto err; err = "failed to link amix to abuffersink"; if (avfilter_link(mix->amix_ctx, 0, mix->sink_ctx, 0)) goto err; // finish up err = "failed to configure filter chain"; if (avfilter_graph_config(mix->graph, NULL)) goto err; return 0; err: mix_shutdown(mix); ilog(LOG_ERR, "Failed to initialize mixer: %s", err); return -1; } mix_t *mix_new() { mix_t *mix = g_slice_alloc0(sizeof(*mix)); mix->clockrate = -1; mix->channels = -1; return mix; } 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; } int mix_add(mix_t *mix, AVFrame *frame, unsigned int idx, output_t *output) { const char *err; err = "index out of range"; if (idx >= G_N_ELEMENTS(mix->src_ctxs)) goto err; err = "mixer not initialized"; if (!mix->src_ctxs[idx]) goto err; err = "failed to add frame to mixer"; if (av_buffersrc_add_frame(mix->src_ctxs[idx], frame)) goto err; while (1) { int ret = av_buffersink_get_frame(mix->sink_ctx, frame); err = "failed to get frame from mixer"; if (ret < 0) { if (ret == AVERROR(EAGAIN)) break; else goto err; } frame = mix_resample_frame(mix, frame); if (output_add(output, frame)) return -1; } return 0; err: ilog(LOG_ERR, "Failed to add frame to mixer: %s", err); return -1; }