diff --git a/Changelog b/Changelog index 7745fa0938..80ed401ab9 100644 --- a/Changelog +++ b/Changelog @@ -17,6 +17,7 @@ version 0.7: - 4:4:4 H.264 decoding support - 10-bit H.264 optimizations for x86 - lut, lutrgb, and lutyuv filters added +- buffersink libavfilter sink added version 0.7_beta2: diff --git a/cmdutils.c b/cmdutils.c index 3b9cfaa3d9..c3c5c0efa9 100644 --- a/cmdutils.c +++ b/cmdutils.c @@ -962,71 +962,3 @@ FILE *get_preset_file(char *filename, size_t filename_size, return f; } - -#if CONFIG_AVFILTER - -static int ffsink_init(AVFilterContext *ctx, const char *args, void *opaque) -{ - FFSinkContext *priv = ctx->priv; - - if (!opaque) - return AVERROR(EINVAL); - *priv = *(FFSinkContext *)opaque; - - return 0; -} - -static void null_end_frame(AVFilterLink *inlink) { } - -static int ffsink_query_formats(AVFilterContext *ctx) -{ - FFSinkContext *priv = ctx->priv; - enum PixelFormat pix_fmts[] = { priv->pix_fmt, PIX_FMT_NONE }; - - avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts)); - return 0; -} - -AVFilter ffsink = { - .name = "ffsink", - .priv_size = sizeof(FFSinkContext), - .init = ffsink_init, - - .query_formats = ffsink_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; - *picref_ptr = NULL; - - 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->pkt_pos = picref->pos; - 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->sample_aspect_ratio; - - return 1; -} - -#endif /* CONFIG_AVFILTER */ diff --git a/cmdutils.h b/cmdutils.h index c0c3ce07d9..e001ab9201 100644 --- a/cmdutils.h +++ b/cmdutils.h @@ -260,19 +260,4 @@ int read_file(const char *filename, char **bufptr, size_t *size); FILE *get_preset_file(char *filename, size_t filename_size, const char *preset_name, int is_path, const char *codec_name); -typedef struct { - enum PixelFormat pix_fmt; -} FFSinkContext; - -extern AVFilter ffsink; - -/** - * 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); - #endif /* FFMPEG_CMDUTILS_H */ diff --git a/doc/APIchanges b/doc/APIchanges index e07951fe05..4db7ffeddf 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -13,6 +13,9 @@ libavutil: 2011-04-18 API changes, most recent first: +2011-06-19 - xxxxxxx - lavfi 2.21.0 - vsink_buffer.h + Add video sink buffer and vsink_buffer.h public header. + 2011-06-12 - xxxxxxx - lavfi 2.18.0 - avcodec.h Add avfilter_get_video_buffer_ref_from_frame() function in libavfilter/avcodec.h. diff --git a/doc/filters.texi b/doc/filters.texi index 7a0b2693ee..eb31714486 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -1977,6 +1977,19 @@ frei0r_src=200x200:10:partik0l=1234 [overlay]; [in][overlay] overlay 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 mainly intended for a programmatic use, in particular +through the interface defined in @file{libavfilter/vsink_buffer.h}. + +It does not require a string parameter in input, but you need to +specify a pointer to a list of supported pixel formats terminated by +-1 in the opaque parameter provided to @code{avfilter_init_filter} +when initializing this sink. + @section nullsink Null video sink, do absolutely nothing with the input video. It is diff --git a/ffmpeg.c b/ffmpeg.c index 2921a06ddf..0b1b1bee8d 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -51,6 +51,7 @@ # include "libavfilter/avcodec.h" # include "libavfilter/avfilter.h" # include "libavfilter/avfiltergraph.h" +# include "libavfilter/vsink_buffer.h" # include "libavfilter/vsrc_buffer.h" #endif @@ -363,7 +364,7 @@ static int configure_video_filters(AVInputStream *ist, AVOutputStream *ost) /** filter graph containing all filters including input & output */ AVCodecContext *codec = ost->st->codec; AVCodecContext *icodec = ist->st->codec; - FFSinkContext ffsink_ctx = { .pix_fmt = codec->pix_fmt }; + enum PixelFormat pix_fmts[] = { codec->pix_fmt, PIX_FMT_NONE }; AVRational sample_aspect_ratio; char args[255]; int ret; @@ -383,8 +384,8 @@ static int configure_video_filters(AVInputStream *ist, AVOutputStream *ost) "src", args, NULL, ost->graph); if (ret < 0) return ret; - ret = avfilter_graph_create_filter(&ost->output_video_filter, &ffsink, - "out", NULL, &ffsink_ctx, ost->graph); + ret = avfilter_graph_create_filter(&ost->output_video_filter, avfilter_get_by_name("buffersink"), + "out", NULL, pix_fmts, ost->graph); if (ret < 0) return ret; last_filter = ost->input_video_filter; @@ -1708,12 +1709,15 @@ static int output_packet(AVInputStream *ist, int ist_index, frame_available = ist->st->codec->codec_type != AVMEDIA_TYPE_VIDEO || !ost->output_video_filter || avfilter_poll_frame(ost->output_video_filter->inputs[0]); while (frame_available) { - AVRational ist_pts_tb; - if (ist->st->codec->codec_type == AVMEDIA_TYPE_VIDEO && ost->output_video_filter) - if (get_filtered_video_frame(ost->output_video_filter, &picture, &ost->picref, &ist_pts_tb) < 0) + if (ist->st->codec->codec_type == AVMEDIA_TYPE_VIDEO && ost->output_video_filter) { + AVRational ist_pts_tb = ost->output_video_filter->inputs[0]->time_base; + if (av_vsink_buffer_get_video_buffer_ref(ost->output_video_filter, &ost->picref, 0) < 0) goto cont; - if (ost->picref) - ist->pts = av_rescale_q(ost->picref->pts, ist_pts_tb, AV_TIME_BASE_Q); + if (ost->picref) { + avfilter_fill_frame_from_video_buffer_ref(&picture, ost->picref); + ist->pts = av_rescale_q(ost->picref->pts, ist_pts_tb, AV_TIME_BASE_Q); + } + } #endif os = output_files[ost->file_index]; diff --git a/ffplay.c b/ffplay.c index 31a683216f..548fda627a 100644 --- a/ffplay.c +++ b/ffplay.c @@ -41,6 +41,7 @@ # include "libavfilter/avcodec.h" # include "libavfilter/avfilter.h" # include "libavfilter/avfiltergraph.h" +# include "libavfilter/vsink_buffer.h" #endif #include @@ -1682,7 +1683,7 @@ static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const c { char sws_flags_str[128]; int ret; - FFSinkContext ffsink_ctx = { .pix_fmt = PIX_FMT_YUV420P }; + enum PixelFormat pix_fmts[] = { PIX_FMT_YUV420P, PIX_FMT_NONE }; AVFilterContext *filt_src = NULL, *filt_out = NULL; snprintf(sws_flags_str, sizeof(sws_flags_str), "flags=%d", sws_flags); graph->scale_sws_opts = av_strdup(sws_flags_str); @@ -1690,8 +1691,8 @@ static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const c if ((ret = avfilter_graph_create_filter(&filt_src, &input_filter, "src", NULL, is, graph)) < 0) goto the_end; - if ((ret = avfilter_graph_create_filter(&filt_out, &ffsink, "out", - NULL, &ffsink_ctx, graph)) < 0) + if ((ret = avfilter_graph_create_filter(&filt_out, avfilter_get_by_name("buffersink"), "out", + NULL, pix_fmts, graph)) < 0) goto the_end; if(vfilters) { @@ -1748,13 +1749,14 @@ static int video_thread(void *arg) AVPacket pkt; #else AVFilterBufferRef *picref; - AVRational tb; + AVRational tb = filt_out->inputs[0]->time_base; #endif while (is->paused && !is->videoq.abort_request) SDL_Delay(10); #if CONFIG_AVFILTER - ret = get_filtered_video_frame(filt_out, frame, &picref, &tb); + ret = av_vsink_buffer_get_video_buffer_ref(filt_out, &picref, 0); if (picref) { + avfilter_fill_frame_from_video_buffer_ref(frame, picref); pts_int = picref->pts; pos = picref->pos; frame->opaque = picref; @@ -1776,7 +1778,7 @@ static int video_thread(void *arg) if (ret < 0) goto the_end; - if (!ret) + if (!picref) continue; pts = pts_int*av_q2d(is->video_st->time_base); diff --git a/libavfilter/Makefile b/libavfilter/Makefile index c594573798..84a7ac3394 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -6,7 +6,7 @@ FFLIBS-$(CONFIG_MOVIE_FILTER) += avformat avcodec FFLIBS-$(CONFIG_SCALE_FILTER) += swscale FFLIBS-$(CONFIG_MP_FILTER) += avcodec -HEADERS = avcodec.h avfilter.h avfiltergraph.h vsrc_buffer.h +HEADERS = avcodec.h avfilter.h avfiltergraph.h vsink_buffer.hvsrc_buffer.h OBJS = allfilters.o \ avfilter.o \ @@ -69,6 +69,7 @@ OBJS-$(CONFIG_FREI0R_SRC_FILTER) += vf_frei0r.o OBJS-$(CONFIG_MOVIE_FILTER) += vsrc_movie.o OBJS-$(CONFIG_NULLSRC_FILTER) += vsrc_nullsrc.o +OBJS-$(CONFIG_BUFFERSINK_FILTER) += vsink_buffer.o OBJS-$(CONFIG_NULLSINK_FILTER) += vsink_nullsink.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 2983f6bd8c..42047ecbe8 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -85,5 +85,6 @@ void avfilter_register_all(void) REGISTER_FILTER (MOVIE, movie, vsrc); REGISTER_FILTER (NULLSRC, nullsrc, vsrc); + REGISTER_FILTER (BUFFER, buffersink, vsink); REGISTER_FILTER (NULLSINK, nullsink, vsink); } diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index bcfc4c6df9..5ac70399ac 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -26,7 +26,7 @@ #include "libavutil/samplefmt.h" #define LIBAVFILTER_VERSION_MAJOR 2 -#define LIBAVFILTER_VERSION_MINOR 20 +#define LIBAVFILTER_VERSION_MINOR 21 #define LIBAVFILTER_VERSION_MICRO 0 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ diff --git a/libavfilter/vsink_buffer.c b/libavfilter/vsink_buffer.c new file mode 100644 index 0000000000..c0824606a9 --- /dev/null +++ b/libavfilter/vsink_buffer.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2011 Stefano Sabatini + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * buffer video sink + */ + +#include "avfilter.h" +#include "vsink_buffer.h" + +typedef struct { + AVFilterBufferRef *picref; ///< cached picref + enum PixelFormat *pix_fmts; ///< accepted pixel formats, must be terminated with -1 +} BufferSinkContext; + +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) +{ + BufferSinkContext *buf = ctx->priv; + + if (!opaque) { + av_log(ctx, AV_LOG_ERROR, "No opaque field provided, which is required.\n"); + return AVERROR(EINVAL); + } + + buf->pix_fmts = opaque; + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + BufferSinkContext *buf = ctx->priv; + + if (buf->picref) + avfilter_unref_buffer(buf->picref); + buf->picref = NULL; +} + +static void end_frame(AVFilterLink *inlink) +{ + BufferSinkContext *buf = inlink->dst->priv; + + if (buf->picref) /* drop the last cached frame */ + avfilter_unref_buffer(buf->picref); + buf->picref = inlink->cur_buf; +} + +static int query_formats(AVFilterContext *ctx) +{ + BufferSinkContext *buf = ctx->priv; + + avfilter_set_common_formats(ctx, avfilter_make_format_list(buf->pix_fmts)); + return 0; +} + +int av_vsink_buffer_get_video_buffer_ref(AVFilterContext *ctx, + AVFilterBufferRef **picref, int flags) +{ + BufferSinkContext *buf = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + int ret; + *picref = NULL; + + /* no picref available, fetch it from the filterchain */ + if (!buf->picref) { + if ((ret = avfilter_request_frame(inlink)) < 0) + return ret; + } + + if (!buf->picref) + return AVERROR(EINVAL); + + *picref = buf->picref; + if (!(flags & AV_VSINK_BUF_FLAG_PEEK)) + buf->picref = NULL; + + return 0; +} + +AVFilter avfilter_vsink_buffersink = { + .name = "buffersink", + .priv_size = sizeof(BufferSinkContext), + .init = init, + .uninit = uninit, + + .query_formats = query_formats, + + .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/vsink_buffer.h b/libavfilter/vsink_buffer.h new file mode 100644 index 0000000000..88b4c1d258 --- /dev/null +++ b/libavfilter/vsink_buffer.h @@ -0,0 +1,47 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFILTER_VSINK_BUFFER_H +#define AVFILTER_VSINK_BUFFER_H + +/** + * @file + * memory buffer sink API for video + */ + +#include "avfilter.h" + +/** + * Tell av_vsink_buffer_get_video_buffer_ref() to read the picref, but not + * remove it from the buffer. This is useful if you need only to read + * the picref, without to fetch it. + */ +#define AV_VSINK_BUF_FLAG_PEEK 1 + +/** + * Get a video buffer data from buffer_sink and put it in picref. + * + * @param buffer_sink pointer to a buffer sink context + * @param flags a combination of AV_VSINK_BUF_FLAG_* flags + * @return >= 0 in case of success, a negative AVERROR code in case of + * failure + */ +int av_vsink_buffer_get_video_buffer_ref(AVFilterContext *buffer_sink, + AVFilterBufferRef **picref, int flags); + +#endif /* AVFILTER_VSINK_BUFFER_H */