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

Marton Balint cus at passwd.hu
Sat Jun 23 18:54:28 CEST 2012



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.

> +    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.

> +
> +    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.

>                if (is->show_mode != SHOW_MODE_VIDEO)
>                    update_sample_display(is, (int16_t *)is->audio_buf, audio_size);
>                is->audio_buf_size = audio_size;
> @@ -2155,6 +2240,10 @@ static int stream_component_open(VideoState *is, int stream_index)
>     AVCodec *codec;
>     AVDictionary *opts;
>     AVDictionaryEntry *t = NULL;
> +    int av_unused ret;
> +    int sample_rate, nb_channels;
> +    int64_t channel_layout;
> +    int audio_hw_buf_size;
>
>     if (stream_index < 0 || stream_index >= ic->nb_streams)
>         return -1;
> @@ -2199,15 +2288,6 @@ static int stream_component_open(VideoState *is, int stream_index)
>         return AVERROR_OPTION_NOT_FOUND;
>     }
>
> -    /* prepare audio output */
> -    if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) {
> -        int audio_hw_buf_size = audio_open(is, avctx->channel_layout, avctx->channels, avctx->sample_rate, &is->audio_src);
> -        if (audio_hw_buf_size < 0)
> -            return -1;
> -        is->audio_hw_buf_size = audio_hw_buf_size;
> -        is->audio_tgt = is->audio_src;
> -    }
> -
>     ic->streams[stream_index]->discard = AVDISCARD_DEFAULT;
>     switch (avctx->codec_type) {
>     case AVMEDIA_TYPE_AUDIO:
> @@ -2225,6 +2305,37 @@ static int stream_component_open(VideoState *is, int stream_index)
>
>         memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));
>         memset(&is->audio_pkt_temp, 0, sizeof(is->audio_pkt_temp));
> +
> +
> +       if (avctx->sample_rate <= 0 || avctx->channels <= 0){
> +           fprintf(stderr, "Invalid sample rate or channel count\n");
> +           return AVERROR(EINVAL);
> +        }
> +
> +#if CONFIG_AVFILTER
> +        if ((ret = configure_audio_filters(is)) < 0)
> +            return ret;
> +#endif
> +
> +        if (CONFIG_AVFILTER &&
> +            is->out_audio_filter && is->out_audio_filter->inputs[0]) {
> +            AVFilterLink *link = is->out_audio_filter->inputs[0];
> +            sample_rate = link->sample_rate;
> +            nb_channels = av_get_channel_layout_nb_channels(link->channel_layout);
> +            channel_layout = link->channel_layout;
> +        } else {
> +            sample_rate = avctx->sample_rate;
> +            nb_channels = avctx->channels;
> +            channel_layout = avctx->channel_layout;
> +        }
> +
> +        /* prepare audio output */
> +        audio_hw_buf_size = audio_open(is, channel_layout, nb_channels, sample_rate, &is->audio_src);
> +        if (audio_hw_buf_size < 0)
> +            return -1;
> +        is->audio_hw_buf_size = audio_hw_buf_size;
> +        is->audio_tgt = is->audio_src;
> +

Now that audio_open is done, you achieved the real settings the hardware 
supports in is->audio_tgt. So you should do a filter reconfiguration here 
with those properties.

>         packet_queue_start(&is->audioq);
>         SDL_PauseAudio(0);
>         break;
> @@ -2277,6 +2388,9 @@ static void stream_component_close(VideoState *is, int stream_index)
>             is->rdft = NULL;
>             is->rdft_bits = 0;
>         }
> +#if CONFIG_AVFILTER
> +        avfilter_graph_free(&is->agraph);
> +#endif
>         break;
>     case AVMEDIA_TYPE_VIDEO:
>         packet_queue_abort(&is->videoq);
> @@ -3002,6 +3116,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" },
>     { "showmode", HAS_ARG, {(void*)opt_show_mode}, "select show mode (0 = video, 1 = waves, 2 = RDFT)", "mode" },
> -- 
> 1.7.5.4

Regards,
Marton


More information about the ffmpeg-devel mailing list