[FFmpeg-devel] [PATCH 9/9] ffplay: add -af option

Stefano Sabatini stefasab at gmail.com
Mon Jun 25 23:59:58 CEST 2012


On date Saturday 2012-06-23 18:54:28 +0200, Marton Balint encoded:
> 
> 
> On Fri, 22 Jun 2012, Stefano Sabatini wrote:
> 
> >---
> >doc/ffplay.texi |    6 +++
> >ffplay.c        |  133 +++++++++++++++++++++++++++++++++++++++++++++++++++----
> >2 files changed, 130 insertions(+), 9 deletions(-)
> >
> >diff --git a/doc/ffplay.texi b/doc/ffplay.texi
> >index e2bded7..8ea3b25 100644
> >--- a/doc/ffplay.texi
> >+++ b/doc/ffplay.texi
> >@@ -83,6 +83,12 @@ 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).
> >+
> >@item -i @var{input_file}
> >Read @var{input_file}.
> >@end table
> >diff --git a/ffplay.c b/ffplay.c
> >index cb234f3..c8663f6 100644
> >--- a/ffplay.c
> >+++ b/ffplay.c
> >@@ -110,6 +110,7 @@ typedef struct VideoPicture {
> >
> >#if CONFIG_AVFILTER
> >    AVFilterBufferRef *picref;
> >+    AVFilterBufferRef *samplesref;
> >#endif
> >} VideoPicture;
> >
> >@@ -233,6 +234,10 @@ typedef struct VideoState {
> >    AVFilterGraph *graph;
> >    int use_dr1;
> >    FrameBuffer *buffer_pool;
> >+
> >+    AVFilterContext *in_audio_filter;           ///<the first filter in the audio chain
> >+    AVFilterContext *out_audio_filter;          ///<the last filter in the audio chain
> >+    AVFilterGraph *agraph;
> >#endif
> >
> >    int refresh;
> >@@ -289,6 +294,7 @@ static const char *video_codec_name;
> >static int rdftspeed = 20;
> >#if CONFIG_AVFILTER
> >static char *vfilters = NULL;
> >+static char *afilters = NULL;
> >#endif
> >
> >/* current context */
> >@@ -1634,6 +1640,65 @@ static int configure_video_filters(VideoState *is, const char *vfilters)
> >    return ret;
> >}
> >
> >+static int configure_audio_filters(VideoState *is)
> >+{
> >+    static const enum PixelFormat sample_fmts[] = { AV_SAMPLE_FMT_S16, PIX_FMT_NONE };
> >+    int64_t layouts[] = { 0, PIX_FMT_NONE };
> >+    AVFilterContext *filt_asrc = NULL, *filt_asink = NULL;
> >+    AVCodecContext *avctx = is->audio_st->codec;
> >+    char abuffer_args[256];
> >+    AVABufferSinkParams *abuffersink_params = av_abuffersink_params_alloc();
> >+    int ret;
> >+
> >+    is->agraph = avfilter_graph_alloc();
> >+
> >+    if (!avctx->channel_layout)
> >+        avctx->channel_layout = av_get_default_channel_layout(avctx->channels);
> 
> Is that a good idea? (setting an attribute of AVCodecContext just
> like that). I'd prefer using a temporary variable for the sanitized
> channel layout.
> 
> Fo condition's like this I like to also check if the number of
> channels matches the channel layout description. E.g.:
> 
> if (!channel_layout || nb_channels != av_get_channel_layout_nb_channels(channel_layout))
> 
> I've seen some streams where the layout was messed up.

Uhm OK, although I think this logic should be moved to the sink buffer
(allowing to specify nb_channels in case the layout is not defined,
and failing/or warning if there is nb_channels/layout mismatch).

> >+    layouts[0] = avctx->channel_layout;
> >+
> >+    snprintf(abuffer_args, sizeof(abuffer_args), "sample_rate=%d:sample_fmt=%s:channel_layout=0x%"PRIx64,
> >+             avctx->sample_rate,
> >+             av_get_sample_fmt_name(avctx->sample_fmt),
> >+             avctx->channel_layout);
> >+    ret = avfilter_graph_create_filter(&filt_asrc,
> >+                                       avfilter_get_by_name("abuffer"), "ffplay_abuffer",
> >+                                       abuffer_args, NULL, is->agraph);
> >+    if (ret < 0) goto fail;
> >+
> >+    abuffersink_params->sample_fmts     = sample_fmts;
> >+    abuffersink_params->channel_layouts = layouts;
> 

> Before calling audio_open using these are fine for determining the
> normal output of the audio filters. But once the audio_open is
> called, you should enforce the settings set in
> VideoState->audio_tgt.

So what about calling audio_open() and *then* configuring the
filtergraph? This should be simpler than:

configure_filters()
audio_open()
configure_filters() again

> 
> >+
> >+    ret = avfilter_graph_create_filter(&filt_asink,
> >+                                       avfilter_get_by_name("abuffersink"), "ffplay_abuffersink",
> >+                                       NULL, abuffersink_params, is->agraph);
> >+    if (ret < 0) goto fail;
> >+
> >+    if (afilters) {
> >+        AVFilterInOut *inputs  = av_malloc(sizeof(AVFilterInOut));
> >+        AVFilterInOut *outputs = av_malloc(sizeof(AVFilterInOut));
> >+
> >+        *inputs  = (AVFilterInOut){ av_strdup("out"), filt_asink, 0, NULL };
> >+        *outputs = (AVFilterInOut){ av_strdup("in" ), filt_asrc, 0, NULL };
> >+
> >+        if ((ret = avfilter_graph_parse(is->agraph, afilters, &inputs, &outputs, NULL)) < 0)
> >+            goto fail;
> >+        av_freep(&afilters);
> >+    } else {
> >+        if ((ret = avfilter_link(filt_asrc, 0, filt_asink, 0)) < 0)
> >+            goto fail;
> >+    }
> >+
> >+    if ((ret = avfilter_graph_config(is->agraph, NULL)) < 0)
> >+        goto fail;
> >+    is->in_audio_filter = filt_asrc;
> >+    is->out_audio_filter = filt_asink;
> >+    return 0;
> >+
> >+fail:
> >+    avfilter_graph_free(&is->agraph);
> >+    return ret;
> >+}
> >+
> >#endif  /* CONFIG_AVFILTER */
> >
> >static int video_thread(void *arg)
> >@@ -2058,6 +2123,7 @@ static void sdl_audio_callback(void *opaque, Uint8 *stream, int len)
> >{
> >    VideoState *is = opaque;
> >    int audio_size, len1;
> >+    AVFilterBufferRef av_unused *samplesref;
> >    int bytes_per_sec;
> >    int frame_size = av_samples_get_buffer_size(NULL, is->audio_tgt.channels, 1, is->audio_tgt.fmt, 1);
> >    double pts;
> >@@ -2072,6 +2138,25 @@ static void sdl_audio_callback(void *opaque, Uint8 *stream, int len)
> >               is->audio_buf      = is->silence_buf;
> >               is->audio_buf_size = sizeof(is->silence_buf) / frame_size * frame_size;
> >           } else {
> >+#if CONFIG_AVFILTER
> >+                const AVCodecContext *avctx = is->audio_st->codec;
> >+
> >+                /* inject the buffer into the filter graph
> >+                 * note that AVFilterBufferRef stores pts with timebase 1/samplerate */
> >+                av_buffersrc_add_frame(is->in_audio_filter, is->frame, 0);
> >+
> >+                if (av_buffersink_get_buffer_ref(is->out_audio_filter,
> >+                                                 &samplesref, 0) < 0)
> >+                    return;
> >+
> >+                pts = samplesref->pts / (double)avctx->sample_rate;
> >+
> >+                is->audio_buf = samplesref->data[0];
> >+                audio_size = samplesref->audio->nb_samples *
> >+                    av_get_channel_layout_nb_channels(samplesref->audio->channel_layout) *
> >+                    av_get_bytes_per_sample(samplesref->format);
> >+#endif
> >+
> 
> You should put this into the audio_decode_frame function. For
> example the resampler there only makes sense for the avfilter
> disabled case.
> 
> You should also check for changing audio input format (channel
> number, sample rate, etc), and reconfigure the audio filters if
> necessary.

OK (again this is a long standing issue, auto-reconfiguration should
be handled in the library).

[...]
-- 
FFmpeg = Fundamental and Free MultiPurpose Energized Gymnast


More information about the ffmpeg-devel mailing list