ffmpeg_alsa: add output filter
authoryu.dongliang <18588496441@163.com>
Sat, 15 Apr 2023 04:20:07 +0000 (12:20 +0800)
committeryu.dongliang <18588496441@163.com>
Sat, 15 Apr 2023 04:20:07 +0000 (12:20 +0800)
simp.h
simp_ffmpeg_alsa.c

diff --git a/simp.h b/simp.h
index cf706c6357fc8faa8a72ef20dc1be93d9e6e071e..372d95f910f687ce54f5a85980d03a54fd2c6543 100644 (file)
--- a/simp.h
+++ b/simp.h
@@ -55,11 +55,16 @@ typedef struct {
 
        AVAudioFifo*      afifo;
 
+       AVFilterContext*  obuffersrc_ctx;
+       AVFilterContext*  obuffersink_ctx;
+       AVFilterGraph*    ograph;
+
        int               idx_in;
        int               idx_out;
 
        AVFrame*          iframe;
        AVFrame*          oframe;
+       AVFrame*          fframe;
 
        AVPacket*         ipkt;
        AVPacket*         opkt;
index 214c379d0f99bb0793f9b54f986d53fd9d251ed9..794bf58fab3b569c5787bdc67e9506e3ed6bdba8 100644 (file)
@@ -1,6 +1,95 @@
 #include"simp_ffmpeg.h"
 #include"scf_def.h"
 
+static int _output_filter_init(simp_audio_t* priv)
+{
+    char args[512];
+    int  ret = 0;
+
+    const AVFilter *abuffersrc     = avfilter_get_by_name("abuffer");
+    const AVFilter *abuffersink    = avfilter_get_by_name("abuffersink");
+
+       AVFilterInOut  *outputs        = NULL;
+    AVFilterInOut  *inputs         = NULL;
+
+       const char     *afilters_descr = "[in]aresample=44100[out]";
+
+       priv->ograph = avfilter_graph_alloc();
+       if (!priv->ograph) {
+               ret = -ENOMEM;
+               goto end;
+       }
+
+       snprintf(args, sizeof(args),
+                       "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%#x",
+                       1, 44100, 44100,
+                       av_get_sample_fmt_name(AV_SAMPLE_FMT_FLTP),
+                       AV_CH_LAYOUT_STEREO);
+
+       printf("%s\n", args);
+
+       ret = avfilter_graph_create_filter(&priv->obuffersrc_ctx, abuffersrc, "in", args, NULL, priv->ograph);
+       if (ret < 0)
+               goto end;
+
+       outputs = avfilter_inout_alloc();
+       if (!outputs) {
+               ret = -ENOMEM;
+               goto end;
+       }
+
+       outputs->name       = av_strdup("in");
+       outputs->filter_ctx = priv->obuffersrc_ctx;
+       outputs->pad_idx    = 0;
+       outputs->next       = NULL;
+
+       const enum AVSampleFormat sample_fmts[]  = {priv->codec_out->sample_fmt,     -1};
+       const int                 sample_rates[] = {priv->codec_out->sample_rate,    -1};
+       const int64_t             layouts[]      = {priv->codec_out->channel_layout, -1};
+
+       ret = avfilter_graph_create_filter(&priv->obuffersink_ctx, abuffersink, "out", NULL, NULL, priv->ograph);
+    if (ret < 0)
+        goto end;
+
+       inputs = avfilter_inout_alloc();
+       if (!inputs) {
+               ret = -ENOMEM;
+               goto end;
+       }
+
+    inputs->name       = av_strdup("out");
+    inputs->filter_ctx = priv->obuffersink_ctx;
+    inputs->pad_idx    = 0;
+    inputs->next       = NULL;
+
+       ret = av_opt_set_int_list(priv->obuffersink_ctx, "sample_fmts", sample_fmts, -1, AV_OPT_SEARCH_CHILDREN);
+       if (ret < 0)
+               goto end;
+
+       ret = av_opt_set_int_list(priv->obuffersink_ctx, "channel_layouts", layouts, -1, AV_OPT_SEARCH_CHILDREN);
+       if (ret < 0)
+               goto end;
+
+       ret = av_opt_set_int_list(priv->obuffersink_ctx, "sample_rates", sample_rates, -1, AV_OPT_SEARCH_CHILDREN);
+       if (ret < 0)
+               goto end;
+
+       if ((ret = avfilter_graph_parse_ptr(priv->ograph, afilters_descr, &inputs, &outputs, NULL)) < 0) {
+               scf_loge("\n");
+               goto end;
+       }
+
+       if ((ret = avfilter_graph_config(priv->ograph, NULL)) < 0) {
+               scf_loge("\n");
+               goto end;
+       }
+
+end:
+    avfilter_inout_free(&inputs);
+    avfilter_inout_free(&outputs);
+    return ret;
+}
+
 static int _audio_input_init(simp_audio_t* priv, const char* path)
 {
        AVInputFormat* in = av_find_input_format("alsa");
@@ -117,10 +206,20 @@ static int _audio_output_init(simp_audio_t* priv, const char* path)
                return ret;
        }
 
+       ret = _output_filter_init(priv);
+       if (ret < 0) {
+               scf_loge("output filter init error, ret: %d, %s\n", ret, av_err2str(ret));
+               return ret;
+       }
+
        priv->afifo = av_audio_fifo_alloc(priv->codec_out->sample_fmt, 2, 1024);
        if (!priv->afifo)
                return -ENOMEM;
 
+       priv->fframe = av_frame_alloc();
+       if (!priv->fframe)
+               return -ENOMEM;
+
        priv->opkt = av_packet_alloc();
        if (!priv->opkt)
                return -ENOMEM;
@@ -154,6 +253,9 @@ void simp_audio_free(simp_audio_t* priv)
                if (priv->codec_out)
                        avcodec_close(priv->codec_out);
 
+               if (priv->ograph)
+                       avfilter_graph_free(&priv->ograph);
+
                if (priv->afifo)
                        av_audio_fifo_free(priv->afifo);
 
@@ -163,6 +265,9 @@ void simp_audio_free(simp_audio_t* priv)
                if (priv->oframe)
                        av_frame_free(&priv->oframe);
 
+               if (priv->fframe)
+                       av_frame_free(&priv->fframe);
+
                if (priv->ipkt)
                        av_packet_free(&priv->ipkt);
 
@@ -251,17 +356,31 @@ static int __output_audio(simp_avio_t* io, AVCodecContext* c, AVPacket* pkt, AVF
                scf_list_del(&f->list);
                pthread_mutex_unlock(&io->mutex);
 
-               ret = av_audio_fifo_write(priv->afifo, (void**)f->frame->data, f->frame->nb_samples);
-
                scf_logd("priv->aidx: %d, frame->pts: %ld, frame->nb_samples: %d, afifo->size: %d\n",
                                priv->aidx, f->frame->pts, f->frame->nb_samples, av_audio_fifo_size(priv->afifo));
 
+               ret = av_buffersrc_add_frame_flags(priv->obuffersrc_ctx, f->frame, AV_BUFFERSRC_FLAG_KEEP_REF);
                simp_frame_free(f);
                f = NULL;
-
                if (ret < 0)
                        return ret;
 
+               while (1) {
+                       ret = av_buffersink_get_frame(priv->obuffersink_ctx, priv->fframe);
+
+                       if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+                               break;
+                       } else if (ret < 0) {
+                               scf_loge("av_buffersink_get_frame error, ret: %s\n", av_err2str(ret));
+                               return ret;
+                       }
+
+                       ret = av_audio_fifo_write(priv->afifo, (void**)priv->fframe->data, priv->fframe->nb_samples);
+                       av_frame_unref(priv->fframe);
+                       if (ret < 0)
+                               return ret;
+               }
+
        } else {
                pthread_mutex_unlock(&io->mutex);
        }