diff --git a/avconv.c b/avconv.c index 73f3bc3763..33758c12b7 100644 --- a/avconv.c +++ b/avconv.c @@ -50,6 +50,7 @@ # include "libavfilter/avfilter.h" # include "libavfilter/avfiltergraph.h" # include "libavfilter/buffersrc.h" +# include "libavfilter/buffersink.h" # include "libavfilter/vsrc_buffer.h" #if HAVE_SYS_RESOURCE_H @@ -582,14 +583,25 @@ static void filter_release_buffer(AVFilterBuffer *fb) unref_buffer(buf->ist, buf); } -static const enum PixelFormat *choose_pixel_fmts(OutputStream *ost) +static char *choose_pixel_fmts(OutputStream *ost) { if (ost->st->codec->pix_fmt != PIX_FMT_NONE) { - ost->pix_fmts[0] = ost->st->codec->pix_fmt; - return ost->pix_fmts; - } else if (ost->enc->pix_fmts) - return ost->enc->pix_fmts; - else + return av_strdup(av_get_pix_fmt_name(ost->st->codec->pix_fmt)); + } else if (ost->enc->pix_fmts) { + const enum PixelFormat *p; + AVIOContext *s = NULL; + uint8_t *ret; + int len; + + if (avio_open_dyn_buf(&s) < 0) + exit_program(1); + + for (p = ost->enc->pix_fmts; *p != PIX_FMT_NONE; p++) + avio_printf(s, "%s:", av_get_pix_fmt_name(*p)); + len = avio_close_dyn_buf(s, &ret); + ret[len - 1] = 0; + return ret; + } else return NULL; } @@ -597,9 +609,9 @@ static int configure_video_filters(FilterGraph *fg) { InputStream *ist = fg->inputs[0]->ist; OutputStream *ost = fg->outputs[0]->ost; - AVFilterContext *last_filter, *filter; + AVFilterContext *in_filter, *out_filter, *filter; AVCodecContext *codec = ost->st->codec; - SinkContext sink_ctx = { .pix_fmts = choose_pixel_fmts(ost) }; + char *pix_fmts; AVRational sample_aspect_ratio; char args[255]; int ret; @@ -621,11 +633,13 @@ static int configure_video_filters(FilterGraph *fg) "src", args, NULL, fg->graph); if (ret < 0) return ret; - ret = avfilter_graph_create_filter(&fg->outputs[0]->filter, &sink, - "out", NULL, &sink_ctx, fg->graph); + ret = avfilter_graph_create_filter(&fg->outputs[0]->filter, + avfilter_get_by_name("buffersink"), + "out", NULL, NULL, fg->graph); if (ret < 0) return ret; - last_filter = fg->inputs[0]->filter; + in_filter = fg->inputs[0]->filter; + out_filter = fg->outputs[0]->filter; if (codec->width || codec->height) { snprintf(args, 255, "%d:%d:flags=0x%X", @@ -635,9 +649,22 @@ static int configure_video_filters(FilterGraph *fg) if ((ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("scale"), NULL, args, NULL, fg->graph)) < 0) return ret; - if ((ret = avfilter_link(last_filter, 0, filter, 0)) < 0) + if ((ret = avfilter_link(in_filter, 0, filter, 0)) < 0) return ret; - last_filter = filter; + in_filter = filter; + } + + if ((pix_fmts = choose_pixel_fmts(ost))) { + if ((ret = avfilter_graph_create_filter(&filter, + avfilter_get_by_name("format"), + "format", pix_fmts, NULL, + fg->graph)) < 0) + return ret; + if ((ret = avfilter_link(filter, 0, out_filter, 0)) < 0) + return ret; + + out_filter = filter; + av_freep(&pix_fmts); } snprintf(args, sizeof(args), "flags=0x%X", (unsigned)ost->sws_flags); @@ -648,19 +675,19 @@ static int configure_video_filters(FilterGraph *fg) AVFilterInOut *inputs = avfilter_inout_alloc(); outputs->name = av_strdup("in"); - outputs->filter_ctx = last_filter; + outputs->filter_ctx = in_filter; outputs->pad_idx = 0; outputs->next = NULL; inputs->name = av_strdup("out"); - inputs->filter_ctx = fg->outputs[0]->filter; + inputs->filter_ctx = out_filter; inputs->pad_idx = 0; inputs->next = NULL; if ((ret = avfilter_graph_parse(fg->graph, ost->avfilter, inputs, outputs, NULL)) < 0) return ret; } else { - if ((ret = avfilter_link(last_filter, 0, fg->outputs[0]->filter, 0)) < 0) + if ((ret = avfilter_link(in_filter, 0, out_filter, 0)) < 0) return ret; } @@ -776,33 +803,52 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) static int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) { - SinkContext sink_ctx; + char *pix_fmts; AVCodecContext *codec = ofilter->ost->st->codec; AVFilterContext *last_filter = out->filter_ctx; int pad_idx = out->pad_idx; int ret; - sink_ctx.pix_fmts = choose_pixel_fmts(ofilter->ost); - ret = avfilter_graph_create_filter(&ofilter->filter, &sink, - "out", NULL, &sink_ctx, fg->graph); + ret = avfilter_graph_create_filter(&ofilter->filter, + avfilter_get_by_name("buffersink"), + "out", NULL, pix_fmts, fg->graph); if (ret < 0) return ret; if (codec->width || codec->height) { char args[255]; + AVFilterContext *filter; + snprintf(args, sizeof(args), "%d:%d:flags=0x%X", codec->width, codec->height, (unsigned)ofilter->ost->sws_flags); - if ((ret = avfilter_graph_create_filter(&last_filter, avfilter_get_by_name("scale"), + if ((ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("scale"), NULL, args, NULL, fg->graph)) < 0) return ret; - if ((ret = avfilter_link(out->filter_ctx, out->pad_idx, last_filter, 0)) < 0) + if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0) return ret; + + last_filter = filter; pad_idx = 0; } + if ((pix_fmts = choose_pixel_fmts(ofilter->ost))) { + AVFilterContext *filter; + if ((ret = avfilter_graph_create_filter(&filter, + avfilter_get_by_name("format"), + "format", pix_fmts, NULL, + fg->graph)) < 0) + return ret; + if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0) + return ret; + + last_filter = filter; + pad_idx = 0; + av_freep(&pix_fmts); + } + if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0) return ret; @@ -1801,7 +1847,7 @@ static int poll_filters(void) { AVFilterBufferRef *picref; AVFrame *filtered_frame = NULL; - int i, frame_size, ret; + int i, frame_size; for (i = 0; i < nb_output_streams; i++) { OutputStream *ost = output_streams[i]; @@ -1816,13 +1862,11 @@ static int poll_filters(void) avcodec_get_frame_defaults(ost->filtered_frame); filtered_frame = ost->filtered_frame; - while (avfilter_poll_frame(ost->filter->filter->inputs[0])) { - AVRational ist_pts_tb; - if ((ret = get_filtered_video_frame(ost->filter->filter, - filtered_frame, &picref, - &ist_pts_tb)) < 0) - return ret; - filtered_frame->pts = av_rescale_q(picref->pts, ist_pts_tb, AV_TIME_BASE_Q); + while (av_buffersink_read(ost->filter->filter, &picref) >= 0) { + avfilter_copy_buf_props(filtered_frame, picref); + filtered_frame->pts = av_rescale_q(picref->pts, + ost->filter->filter->inputs[0]->time_base, + AV_TIME_BASE_Q); if (of->start_time && filtered_frame->pts < of->start_time) return 0; diff --git a/avplay.c b/avplay.c index 9bd83f381e..d291ba384a 100644 --- a/avplay.c +++ b/avplay.c @@ -41,6 +41,7 @@ #if CONFIG_AVFILTER # include "libavfilter/avfilter.h" # include "libavfilter/avfiltergraph.h" +# include "libavfilter/buffersink.h" #endif #include "cmdutils.h" @@ -1708,21 +1709,28 @@ static AVFilter input_filter = static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const char *vfilters) { - static const enum PixelFormat pix_fmts[] = { PIX_FMT_YUV420P, PIX_FMT_NONE }; char sws_flags_str[128]; int ret; - SinkContext sink_ctx = { .pix_fmts = pix_fmts }; - AVFilterContext *filt_src = NULL, *filt_out = NULL; + AVFilterContext *filt_src = NULL, *filt_out = NULL, *filt_format; snprintf(sws_flags_str, sizeof(sws_flags_str), "flags=%d", sws_flags); graph->scale_sws_opts = av_strdup(sws_flags_str); if ((ret = avfilter_graph_create_filter(&filt_src, &input_filter, "src", NULL, is, graph)) < 0) return ret; - if ((ret = avfilter_graph_create_filter(&filt_out, &sink, "out", - NULL, &sink_ctx, graph)) < 0) + if ((ret = avfilter_graph_create_filter(&filt_out, + avfilter_get_by_name("buffersink"), + "out", NULL, NULL, graph)) < 0) return ret; + if ((ret = avfilter_graph_create_filter(&filt_format, + avfilter_get_by_name("format"), + "format", "yuv420p", NULL, graph)) < 0) + return ret; + if ((ret = avfilter_link(filt_format, 0, filt_out, 0)) < 0) + return ret; + + if (vfilters) { AVFilterInOut *outputs = avfilter_inout_alloc(); AVFilterInOut *inputs = avfilter_inout_alloc(); @@ -1733,14 +1741,14 @@ static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const c outputs->next = NULL; inputs->name = av_strdup("out"); - inputs->filter_ctx = filt_out; + inputs->filter_ctx = filt_format; inputs->pad_idx = 0; inputs->next = NULL; if ((ret = avfilter_graph_parse(graph, vfilters, inputs, outputs, NULL)) < 0) return ret; } else { - if ((ret = avfilter_link(filt_src, 0, filt_out, 0)) < 0) + if ((ret = avfilter_link(filt_src, 0, filt_format, 0)) < 0) return ret; } @@ -1796,11 +1804,16 @@ static int video_thread(void *arg) last_w = is->video_st->codec->width; last_h = is->video_st->codec->height; } - ret = get_filtered_video_frame(filt_out, frame, &picref, &tb); + ret = av_buffersink_read(filt_out, &picref); if (picref) { + avfilter_copy_buf_props(frame, picref); + pts_int = picref->pts; + tb = filt_out->inputs[0]->time_base; pos = picref->pos; frame->opaque = picref; + + ret = 1; } if (ret >= 0 && av_cmp_q(tb, is->video_st->time_base)) { diff --git a/cmdutils.c b/cmdutils.c index 6d2e97f694..3cd11ca241 100644 --- a/cmdutils.c +++ b/cmdutils.c @@ -1021,74 +1021,6 @@ AVDictionary **setup_find_stream_info_opts(AVFormatContext *s, return opts; } -#if CONFIG_AVFILTER - -static int sink_init(AVFilterContext *ctx, const char *args, void *opaque) -{ - SinkContext *priv = ctx->priv; - - if (!opaque) - return AVERROR(EINVAL); - *priv = *(SinkContext *)opaque; - - return 0; -} - -static void null_end_frame(AVFilterLink *inlink) { } - -static int sink_query_formats(AVFilterContext *ctx) -{ - SinkContext *priv = ctx->priv; - - if (priv->pix_fmts) - avfilter_set_common_formats(ctx, avfilter_make_format_list(priv->pix_fmts)); - else - avfilter_default_query_formats(ctx); - return 0; -} - -AVFilter sink = { - .name = "sink", - .priv_size = sizeof(SinkContext), - .init = sink_init, - - .query_formats = sink_query_formats, - - .inputs = (AVFilterPad[]) {{ .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .end_frame = null_end_frame, - .min_perms = AV_PERM_READ, }, - { .name = NULL }}, - .outputs = (AVFilterPad[]) {{ .name = NULL }}, -}; - -int get_filtered_video_frame(AVFilterContext *ctx, AVFrame *frame, - AVFilterBufferRef **picref_ptr, AVRational *tb) -{ - int ret; - AVFilterBufferRef *picref; - - if ((ret = avfilter_request_frame(ctx->inputs[0])) < 0) - return ret; - if (!(picref = ctx->inputs[0]->cur_buf)) - return AVERROR(ENOENT); - *picref_ptr = picref; - ctx->inputs[0]->cur_buf = NULL; - *tb = ctx->inputs[0]->time_base; - - memcpy(frame->data, picref->data, sizeof(frame->data)); - memcpy(frame->linesize, picref->linesize, sizeof(frame->linesize)); - frame->interlaced_frame = picref->video->interlaced; - frame->top_field_first = picref->video->top_field_first; - frame->key_frame = picref->video->key_frame; - frame->pict_type = picref->video->pict_type; - frame->sample_aspect_ratio = picref->video->pixel_aspect; - - return 1; -} - -#endif /* CONFIG_AVFILTER */ - void *grow_array(void *array, int elem_size, int *size, int new_size) { if (new_size >= INT_MAX / elem_size) { diff --git a/cmdutils.h b/cmdutils.h index 792254cf6c..6fff47ddeb 100644 --- a/cmdutils.h +++ b/cmdutils.h @@ -367,21 +367,6 @@ int64_t guess_correct_pts(PtsCorrectionContext *ctx, int64_t pts, int64_t dts); FILE *get_preset_file(char *filename, size_t filename_size, const char *preset_name, int is_path, const char *codec_name); -typedef struct { - const enum PixelFormat *pix_fmts; -} SinkContext; - -extern AVFilter sink; - -/** - * Extract a frame from sink. - * - * @return a negative error in case of failure, 1 if one frame has - * been extracted successfully. - */ -int get_filtered_video_frame(AVFilterContext *sink, AVFrame *frame, - AVFilterBufferRef **picref, AVRational *pts_tb); - /** * Do all the necessary cleanup and abort. * This function is implemented in the avtools, not cmdutils. diff --git a/configure b/configure index 67cba5a795..4b1e55169f 100755 --- a/configure +++ b/configure @@ -1540,7 +1540,7 @@ avfilter_deps="swscale" avformat_deps="avcodec" # programs -avconv_deps="avcodec avfilter avformat avresample swscale" +avconv_deps="avcodec avfilter avformat avresample swscale format_filter" avplay_deps="avcodec avformat swscale sdl" avplay_select="rdft" avprobe_deps="avcodec avformat" diff --git a/doc/filters.texi b/doc/filters.texi index c5a56f49f6..dbcc86a384 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -2078,6 +2078,14 @@ will generate a video with a duration of 5.3 seconds, with size Below is a description of the currently available video sinks. +@section buffersink + +Buffer video frames, and make them available to the end of the filter +graph. + +This sink is intended for a programmatic use through the interface defined in +@file{libavfilter/buffersink.h}. + @section nullsink Null video sink, do absolutely nothing with the input video. It is diff --git a/libavfilter/Makefile b/libavfilter/Makefile index ae858397df..e786b6d2fe 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -4,6 +4,7 @@ FFLIBS-$(CONFIG_MOVIE_FILTER) += avformat avcodec HEADERS = avfilter.h \ avfiltergraph.h \ + buffersink.h \ buffersrc.h \ version.h \ vsrc_buffer.h \ @@ -11,6 +12,7 @@ HEADERS = avfilter.h \ OBJS = allfilters.o \ avfilter.o \ avfiltergraph.o \ + buffersink.o \ defaults.o \ drawutils.o \ formats.o \ diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 198e152cb0..f887002b66 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -94,6 +94,10 @@ void avfilter_register_all(void) extern AVFilter avfilter_vsrc_buffer; avfilter_register(&avfilter_vsrc_buffer); } + { + extern AVFilter avfilter_vsink_buffer; + avfilter_register(&avfilter_vsink_buffer); + } { extern AVFilter avfilter_vf_scale; avfilter_register(&avfilter_vf_scale); diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c new file mode 100644 index 0000000000..e4cbe3be42 --- /dev/null +++ b/libavfilter/buffersink.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2011 Stefano Sabatini + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * buffer sink + */ + +#include "libavutil/fifo.h" + +#include "avfilter.h" +#include "buffersink.h" + +typedef struct { + AVFifoBuffer *fifo; ///< FIFO buffer of video frame references +} BufferSinkContext; + +#define FIFO_INIT_SIZE 8 + +static av_cold void uninit(AVFilterContext *ctx) +{ + BufferSinkContext *sink = ctx->priv; + + while (sink->fifo && av_fifo_size(sink->fifo)) { + AVFilterBufferRef *buf; + av_fifo_generic_read(sink->fifo, &buf, sizeof(buf), NULL); + avfilter_unref_buffer(buf); + } + av_fifo_free(sink->fifo); +} + +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) +{ + BufferSinkContext *sink = ctx->priv; + + if (!(sink->fifo = av_fifo_alloc(FIFO_INIT_SIZE*sizeof(AVFilterBufferRef*)))) { + av_log(ctx, AV_LOG_ERROR, "Failed to allocate fifo\n"); + return AVERROR(ENOMEM); + } + + return 0; +} + +static void end_frame(AVFilterLink *link) +{ + AVFilterContext *ctx = link->dst; + BufferSinkContext *sink = ctx->priv; + + if (av_fifo_space(sink->fifo) < sizeof(AVFilterBufferRef *) && + (av_fifo_realloc2(sink->fifo, av_fifo_size(sink->fifo) * 2) < 0)) { + av_log(ctx, AV_LOG_ERROR, "Error reallocating the FIFO.\n"); + return; + } + + av_fifo_generic_write(sink->fifo, &link->cur_buf, sizeof(link->cur_buf), NULL); + link->cur_buf = NULL; +} + +int av_buffersink_read(AVFilterContext *ctx, AVFilterBufferRef **buf) +{ + BufferSinkContext *sink = ctx->priv; + AVFilterLink *link = ctx->inputs[0]; + int ret; + + if (!buf) { + if (av_fifo_size(sink->fifo)) + return av_fifo_size(sink->fifo)/sizeof(*buf); + else + return avfilter_poll_frame(ctx->inputs[0]); + } + + if (!av_fifo_size(sink->fifo) && + (ret = avfilter_request_frame(link)) < 0) + return ret; + + if (!av_fifo_size(sink->fifo)) + return AVERROR(EINVAL); + + av_fifo_generic_read(sink->fifo, buf, sizeof(*buf), NULL); + + return 0; +} + +AVFilter avfilter_vsink_buffer = { + .name = "buffersink", + .description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them available to the end of the filter graph."), + .priv_size = sizeof(BufferSinkContext), + .init = init, + .uninit = uninit, + + .inputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .end_frame = end_frame, + .min_perms = AV_PERM_READ, }, + { .name = NULL }}, + .outputs = (AVFilterPad[]) {{ .name = NULL }}, +}; diff --git a/libavfilter/buffersink.h b/libavfilter/buffersink.h new file mode 100644 index 0000000000..e579b9ad03 --- /dev/null +++ b/libavfilter/buffersink.h @@ -0,0 +1,43 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFILTER_BUFFERSINK_H +#define AVFILTER_BUFFERSINK_H + +/** + * @file + * memory buffer sink API + */ + +#include "avfilter.h" + +/** + * Get a buffer with filtered data from sink and put it in buf. + * + * @param sink pointer to a context of a buffersink AVFilter. + * @param buf pointer to the buffer will be written here if buf is non-NULL. buf + * must be freed by the caller using avfilter_unref_buffer(). + * Buf may also be NULL to query whether a buffer is ready to be + * output. + * + * @return >= 0 in case of success, a negative AVERROR code in case of + * failure. + */ +int av_buffersink_read(AVFilterContext *sink, AVFilterBufferRef **buf); + +#endif /* AVFILTER_BUFFERSINK_H */