[PATCH] Implement -af in ffplay.

Stefano Sabatini stefano.sabatini-lala
Wed Jan 12 00:39:32 CET 2011


Based on a patch by Hemanth.
---
 doc/ffplay.texi |    5 ++
 ffplay.c        |  203 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 208 insertions(+), 0 deletions(-)

diff --git a/doc/ffplay.texi b/doc/ffplay.texi
index c9c38da..41483c0 100644
--- a/doc/ffplay.texi
+++ b/doc/ffplay.texi
@@ -61,6 +61,11 @@ Loops movie playback <number> times. 0 means forever.
 the input video.
 Use the option "-filters" to show all the available filters (including
 also sources and sinks).
+ at item -af @var{filter_graph}
+ at var{filter_graph} is a description of the filter graph to apply to
+the input audio.
+Use the option "-filters" to show all the available filters (including
+sources and sinks).
 
 @end table
 
diff --git a/ffplay.c b/ffplay.c
index 2a2e823..36d2d25 100644
--- a/ffplay.c
+++ b/ffplay.c
@@ -109,6 +109,7 @@ typedef struct VideoPicture {
 
 #if CONFIG_AVFILTER
     AVFilterBufferRef *picref;
+    AVFilterBufferRef *samplesref;
 #endif
 } VideoPicture;
 
@@ -211,6 +212,8 @@ typedef struct VideoState {
 
 #if CONFIG_AVFILTER
     AVFilterContext *out_video_filter;          ///<the last filter in the video chain
+    AVFilterContext *out_audio_filter;          ///<the last filter in the audio chain
+    AVFilterGraph *agraph;
 #endif
 
     float skip_frames;
@@ -220,6 +223,7 @@ typedef struct VideoState {
 
 static void show_help(void);
 static int audio_write_get_buf_size(VideoState *is);
+static int audio_decode_frame(VideoState *is, double *pts_ptr);
 
 /* options specified by the user */
 static AVInputFormat *file_iformat;
@@ -269,6 +273,7 @@ static int framedrop=1;
 static int rdftspeed=20;
 #if CONFIG_AVFILTER
 static char *vfilters = NULL;
+static char *afilters = NULL;
 #endif
 
 /* current context */
@@ -1784,6 +1789,180 @@ static AVFilter input_filter =
                                   { .name = NULL }},
 };
 
+typedef struct {
+    VideoState *is;
+} AudioFilterPriv;
+
+static int input_audio_init(AVFilterContext *ctx, const char *args, void *opaque)
+{
+    AudioFilterPriv *priv = ctx->priv;
+
+    if (!opaque) return AVERROR(EINVAL);
+
+    priv->is = opaque;
+
+    return 0;
+}
+
+static void samples_buf_free(AVFilterBuffer *ptr)
+{
+    av_free(ptr);
+}
+
+static int input_request_samples(AVFilterLink *inlink)
+{
+    AudioFilterPriv *priv = inlink->src->priv;
+    AVFilterBufferRef *samplesref;
+    AVCodecContext *avctx;
+    double pts = 0.0;
+    int buf_size = audio_decode_frame(priv->is, &pts);
+
+    avctx = priv->is->audio_st->codec;
+    if (buf_size <= 0)
+        return AVERROR(EINVAL);
+
+    if (!avctx->channel_layout)
+        avctx->channel_layout = avcodec_guess_channel_layout(avctx->channels, 0, NULL);
+
+    /* FIXME Currently audio streams seem to have no info on planar/packed */
+    samplesref =
+        avfilter_get_audio_buffer_ref_from_buffer(priv->is->audio_buf, buf_size,
+                                                  AV_PERM_WRITE, avctx->sample_fmt, 0,
+                                                  avctx->channel_layout,
+                                                  avctx->channels);
+    samplesref->buf->free = samples_buf_free;
+
+    // AVFilterBufferRef stores pts with timebase 1/samplerate.
+    samplesref->pts                = pts * (double)avctx->sample_rate;
+    samplesref->audio->sample_rate = avctx->sample_rate;
+    avfilter_filter_samples(inlink, samplesref);
+
+    return 0;
+}
+
+static int input_query_audio_formats(AVFilterContext *ctx)
+{
+    AudioFilterPriv *priv = ctx->priv;
+    enum SampleFormat sample_fmts[] = {
+        priv->is->audio_st->codec->sample_fmt, SAMPLE_FMT_NONE
+    };
+
+    avfilter_set_common_formats(ctx, avfilter_make_format_list(sample_fmts));
+    return 0;
+}
+
+static int input_config_audio_props(AVFilterLink *inlink)
+{
+    FilterPriv *priv  = inlink->src->priv;
+    AVCodecContext *avctx = priv->is->audio_st->codec;
+
+    if (!avctx->channel_layout)
+        avctx->channel_layout = avcodec_guess_channel_layout(avctx->channels, 0, NULL);
+    inlink->channel_layout = avctx->channel_layout;
+    inlink->sample_rate = avctx->sample_rate;
+    return 0;
+}
+
+static AVFilter input_audio_filter = {
+    .name      = "ffplay_audio_input",
+
+    .priv_size = sizeof(AudioFilterPriv),
+
+    .init      = input_audio_init,
+
+    .query_formats = input_query_audio_formats,
+
+    .inputs    = (AVFilterPad[]) {{ .name = NULL }},
+    .outputs   = (AVFilterPad[]) {{ .name = "default",
+                                    .type = AVMEDIA_TYPE_AUDIO,
+                                    .request_frame = input_request_samples,
+                                    .config_props  = input_config_audio_props, },
+                                  { .name = NULL }},
+};
+
+static void null_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref) { }
+
+static int output_query_audio_formats(AVFilterContext *ctx)
+{
+    enum SampleFormat sample_fmts[] = { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE };
+
+    avfilter_set_common_formats(ctx, avfilter_make_format_list(sample_fmts));
+    return 0;
+}
+
+static int get_filtered_audio_samples(AVFilterContext *ctx, VideoState *is, double *pts)
+{
+    AVFilterBufferRef *samplesref;
+    AVCodecContext *avctx = is->audio_st->codec;
+    int ret, size;
+
+    if ((ret = avfilter_request_frame(ctx->inputs[0])))
+        return ret;
+    if (!(samplesref = ctx->inputs[0]->cur_buf))
+        return AVERROR(EINVAL);
+    ctx->inputs[0]->cur_buf = NULL;
+    size = samplesref->audio->size;
+
+    // AVFilterBufferRef stores pts with timebase 1/samplerate.
+    *pts = (double)samplesref->pts / (double)avctx->sample_rate;
+
+    is->audio_buf = samplesref->data[0];
+    avfilter_unref_buffer(samplesref);
+
+    return size;
+}
+
+static AVFilter output_audio_filter = {
+    .name      = "ffplay_audio_output",
+
+    .query_formats = output_query_audio_formats,
+
+    .inputs    = (AVFilterPad[]) {{ .name           = "default",
+                                    .type           = AVMEDIA_TYPE_AUDIO,
+                                    .filter_samples = null_filter_samples,
+                                    .min_perms      = AV_PERM_READ, },
+                                  { .name = NULL }},
+    .outputs   = (AVFilterPad[]) {{ .name = NULL }},
+};
+
+static int configure_audio_filters(VideoState *is, const char *afilters)
+{
+    AVFilterContext *asrc_ctx = NULL, *aout_ctx = NULL;
+    int ret;
+
+    is->agraph = avfilter_graph_alloc();
+    if ((ret = avfilter_graph_create_filter(&asrc_ctx, &input_audio_filter, "asrc",
+                                            NULL, is, is->agraph)) < 0)
+        goto fail;
+    if ((ret = avfilter_graph_create_filter(&aout_ctx, &output_audio_filter, "aout",
+                                            NULL, NULL, is->agraph)) < 0)
+        goto fail;
+
+    if (afilters) {
+        AVFilterInOut *inputs  = av_malloc(sizeof(AVFilterInOut));
+        AVFilterInOut *outputs = av_malloc(sizeof(AVFilterInOut));
+
+        *inputs  = (AVFilterInOut){ av_strdup("out"), aout_ctx, 0, NULL };
+        *outputs = (AVFilterInOut){ av_strdup("in" ), asrc_ctx, 0, NULL };
+
+        if ((ret = avfilter_graph_parse(is->agraph, afilters, inputs, outputs, NULL)) < 0)
+            goto fail;
+    } else {
+        if ((ret = avfilter_link(asrc_ctx, 0, aout_ctx, 0)) < 0)
+            goto fail;
+    }
+
+    if ((ret = avfilter_graph_config(is->agraph, NULL)) < 0)
+        goto fail;
+    is->out_audio_filter = aout_ctx;
+    return 0;
+
+fail:
+    avfilter_graph_free(is->agraph);
+    is->agraph = NULL;
+    return ret;
+}
+
 #endif  /* CONFIG_AVFILTER */
 
 static int video_thread(void *arg)
@@ -2095,6 +2274,9 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
             if (data_size <= 0)
                 continue;
 
+#if CONFIG_AVFILTER
+            is->audio_buf = is->audio_buf1;
+#else
             if (dec->sample_fmt != is->audio_src_fmt) {
                 if (is->reformat_ctx)
                     av_audio_convert_free(is->reformat_ctx);
@@ -2126,6 +2308,7 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
             }else{
                 is->audio_buf= is->audio_buf1;
             }
+#endif
 
             /* if no pts, then compute it */
             pts = is->audio_clock;
@@ -2190,7 +2373,11 @@ static void sdl_audio_callback(void *opaque, Uint8 *stream, int len)
 
     while (len > 0) {
         if (is->audio_buf_index >= is->audio_buf_size) {
+#if CONFIG_AVFILTER
+           audio_size = get_filtered_audio_samples(is->out_audio_filter, is, &pts);
+#else
            audio_size = audio_decode_frame(is, &pts);
+#endif
            if (audio_size < 0) {
                 /* if error, just output silence */
                is->audio_buf = is->audio_buf1;
@@ -2222,6 +2409,9 @@ static int stream_component_open(VideoState *is, int stream_index)
     AVCodecContext *avctx;
     AVCodec *codec;
     SDL_AudioSpec wanted_spec, spec;
+#if CONFIG_AVFILTER
+    int ret;
+#endif
 
     if (stream_index < 0 || stream_index >= ic->nb_streams)
         return -1;
@@ -2290,6 +2480,14 @@ static int stream_component_open(VideoState *is, int stream_index)
         is->audio_diff_threshold = 2.0 * SDL_AUDIO_BUFFER_SIZE / avctx->sample_rate;
 
         memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));
+
+#if CONFIG_AVFILTER
+        ret = configure_audio_filters(is, afilters);
+        av_freep(&afilters);
+        if (ret < 0)
+            return ret;
+#endif
+
         packet_queue_init(&is->audioq);
         SDL_PauseAudio(0);
         break;
@@ -2334,6 +2532,10 @@ static void stream_component_close(VideoState *is, int stream_index)
         if (is->reformat_ctx)
             av_audio_convert_free(is->reformat_ctx);
         is->reformat_ctx = NULL;
+#if CONFIG_AVFILTER
+        avfilter_graph_free(is->agraph);
+        av_freep(&(is->agraph));
+#endif
         break;
     case AVMEDIA_TYPE_VIDEO:
         packet_queue_abort(&is->videoq);
@@ -3039,6 +3241,7 @@ static const OptionDef options[] = {
     { "window_title", OPT_STRING | HAS_ARG, {(void*)&window_title}, "set window title", "window title" },
 #if CONFIG_AVFILTER
     { "vf", OPT_STRING | HAS_ARG, {(void*)&vfilters}, "video filters", "filter list" },
+    { "af", OPT_STRING | HAS_ARG, {(void*)&afilters}, "audio filters", "filter list" },
 #endif
     { "rdftspeed", OPT_INT | HAS_ARG| OPT_AUDIO | OPT_EXPERT, {(void*)&rdftspeed}, "rdft speed", "msecs" },
     { "default", OPT_FUNC2 | HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, {(void*)opt_default}, "generic catch all option", "" },
-- 
1.7.2.3


--n8g4imXOkfNTN/H1--



More information about the ffmpeg-devel mailing list