[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