From 0c59d40ae06b680de366663d77b4124947813367 Mon Sep 17 00:00:00 2001 From: Matthieu Bouron Date: Fri, 11 Dec 2015 13:32:47 +0100 Subject: [PATCH] lavfi: use a video frame pool for each link of the filtergraph --- libavfilter/Makefile | 1 + libavfilter/avfilter.c | 1 + libavfilter/avfilter.h | 5 ++ libavfilter/framepool.c | 189 ++++++++++++++++++++++++++++++++++++++++ libavfilter/framepool.h | 84 ++++++++++++++++++ libavfilter/internal.h | 1 + libavfilter/video.c | 42 ++++++--- 7 files changed, 309 insertions(+), 14 deletions(-) create mode 100644 libavfilter/framepool.c create mode 100644 libavfilter/framepool.h diff --git a/libavfilter/Makefile b/libavfilter/Makefile index d7a3f612bb..dea012aa93 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -17,6 +17,7 @@ OBJS = allfilters.o \ drawutils.o \ fifo.o \ formats.o \ + framepool.o \ graphdump.o \ graphparser.o \ opencl_allkernels.o \ diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index c5c30448dc..5d7bc09b86 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -168,6 +168,7 @@ void avfilter_link_free(AVFilterLink **link) return; av_frame_free(&(*link)->partial_buf); + ff_video_frame_pool_uninit((FFVideoFramePool**)&(*link)->video_frame_pool); av_freep(link); } diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index 7aac3cf934..dca02945f6 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -509,6 +509,11 @@ struct AVFilterLink { * Number of past frames sent through the link. */ int64_t frame_count; + + /** + * A pointer to a FFVideoFramePool struct. + */ + void *video_frame_pool; }; /** diff --git a/libavfilter/framepool.c b/libavfilter/framepool.c new file mode 100644 index 0000000000..ff3a4f7444 --- /dev/null +++ b/libavfilter/framepool.c @@ -0,0 +1,189 @@ +/* + * This file is part of FFmpeg. + * + * Copyright (c) 2015 Matthieu Bouron + * + * 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 + */ + +#include "framepool.h" +#include "libavutil/avassert.h" +#include "libavutil/buffer.h" +#include "libavutil/frame.h" +#include "libavutil/imgutils.h" +#include "libavutil/mem.h" +#include "libavutil/pixfmt.h" + +struct FFVideoFramePool { + + int width; + int height; + int format; + int align; + int linesize[4]; + AVBufferPool *pools[4]; + +}; + +FFVideoFramePool *ff_video_frame_pool_init(AVBufferRef* (*alloc)(int size), + int width, + int height, + enum AVPixelFormat format, + int align) +{ + int i, ret; + FFVideoFramePool *pool; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(format); + + if (!desc) + return NULL; + + pool = av_mallocz(sizeof(FFVideoFramePool)); + if (!pool) + return NULL; + + pool->width = width; + pool->height = height; + pool->format = format; + pool->align = align; + + if ((ret = av_image_check_size(width, height, 0, NULL)) < 0) { + goto fail; + } + + if (!pool->linesize[0]) { + for(i = 1; i <= align; i += i) { + ret = av_image_fill_linesizes(pool->linesize, pool->format, + FFALIGN(pool->width, i)); + if (ret < 0) { + goto fail; + } + if (!(pool->linesize[0] & (pool->align - 1))) + break; + } + + for (i = 0; i < 4 && pool->linesize[i]; i++) { + pool->linesize[i] = FFALIGN(pool->linesize[i], pool->align); + } + } + + for (i = 0; i < 4 && pool->linesize[i]; i++) { + int h = FFALIGN(pool->height, 32); + if (i == 1 || i == 2) + h = FF_CEIL_RSHIFT(h, desc->log2_chroma_h); + + pool->pools[i] = av_buffer_pool_init(pool->linesize[i] * h + 16 + 16 - 1, + alloc); + if (!pool->pools[i]) + goto fail; + } + + if (desc->flags & AV_PIX_FMT_FLAG_PAL || + desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL) { + pool->pools[1] = av_buffer_pool_init(AVPALETTE_SIZE, alloc); + if (!pool->pools[1]) + goto fail; + } + + return pool; + +fail: + ff_video_frame_pool_uninit(&pool); + return NULL; +} + +int ff_video_frame_pool_get_config(FFVideoFramePool *pool, + int *width, + int *height, + enum AVPixelFormat *format, + int *align) +{ + if (!pool) + return AVERROR(EINVAL); + + *width = pool->width; + *height = pool->height; + *format = pool->format; + *align = pool->align; + + return 0; +} + + +AVFrame *ff_video_frame_pool_get(FFVideoFramePool *pool) +{ + int i; + AVFrame *frame; + const AVPixFmtDescriptor *desc; + + frame = av_frame_alloc(); + if (!frame) { + return NULL; + } + + desc = av_pix_fmt_desc_get(pool->format); + if (!desc) { + goto fail; + } + + frame->width = pool->width; + frame->height = pool->height; + frame->format = pool->format; + + for (i = 0; i < 4; i++) { + frame->linesize[i] = pool->linesize[i]; + if (!pool->pools[i]) + break; + + frame->buf[i] = av_buffer_pool_get(pool->pools[i]); + if (!frame->buf[i]) { + goto fail; + } + + frame->data[i] = frame->buf[i]->data; + } + + if (desc->flags & AV_PIX_FMT_FLAG_PAL || + desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL) { + enum AVPixelFormat format = + pool->format == AV_PIX_FMT_PAL8 ? AV_PIX_FMT_BGR8 : pool->format; + + av_assert0(frame->data[1] != NULL); + if (avpriv_set_systematic_pal2((uint32_t *)frame->data[1], format) < 0) { + goto fail; + } + } + + frame->extended_data = frame->data; + + return frame; +fail: + av_frame_free(&frame); + return NULL; +} + +void ff_video_frame_pool_uninit(FFVideoFramePool **pool) +{ + int i; + + if (!pool || !*pool) + return; + + for (i = 0; i < 4; i++) { + av_buffer_pool_uninit(&(*pool)->pools[i]); + } + + av_freep(pool); +} diff --git a/libavfilter/framepool.h b/libavfilter/framepool.h new file mode 100644 index 0000000000..bb6bb10f27 --- /dev/null +++ b/libavfilter/framepool.h @@ -0,0 +1,84 @@ +/* + * This file is part of FFmpeg. + * + * Copyright (c) 2015 Matthieu Bouron + * + * 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 AVUTIL_FRAME_POOL_H +#define AVUTIL_FRAME_POOL_H + +#include "libavutil/buffer.h" +#include "libavutil/frame.h" + +/** + * Video frame pool. This structure is opaque and not meant to be accessed + * directly. It is allocated with ff_video_frame_pool_init() and freed with + * ff_video_frame_pool_uninit(). + */ +typedef struct FFVideoFramePool FFVideoFramePool; + +/** + * Allocate and initialize a video frame pool. + * + * @param alloc a function that will be used to allocate new frame buffers when + * the pool is empty. May be NULL, then the default allocator will be used + * (av_buffer_alloc()). + * @param width width of each frame in this pool + * @param height height of each frame in this pool + * @param format format of each frame in this pool + * @param align buffers alignement of each frame in this pool + * @return newly created video frame pool on success, NULL on error. + */ +FFVideoFramePool *ff_video_frame_pool_init(AVBufferRef* (*alloc)(int size), + int width, + int height, + enum AVPixelFormat format, + int align); + +/** + * Deallocate the video frame pool. It is safe to call this function while + * some of the allocated video frame are still in use. + * + * @param pool pointer to the video frame pool to be freed. It will be set to NULL. + */ +void ff_video_frame_pool_uninit(FFVideoFramePool **pool); + +/** + * Get the video frame pool configuration. + * + * @param width width of each frame in this pool + * @param height height of each frame in this pool + * @param format format of each frame in this pool + * @param align buffers alignement of each frame in this pool + * @return 0 on success, a negative AVERROR otherwise. + */ +int ff_video_frame_pool_get_config(FFVideoFramePool *pool, + int *width, + int *height, + enum AVPixelFormat *format, + int *align); + +/** + * Allocate a new AVFrame, reussing old buffers from the pool when available. + * This function may be called simultaneously from multiple threads. + * + * @return a new AVFrame on success, NULL on error. + */ +AVFrame *ff_video_frame_pool_get(FFVideoFramePool *pool); + + +#endif /* AVUTIL_FRAME_POOL_H */ diff --git a/libavfilter/internal.h b/libavfilter/internal.h index 1cc6bf3b10..6ae1535281 100644 --- a/libavfilter/internal.h +++ b/libavfilter/internal.h @@ -28,6 +28,7 @@ #include "avfilter.h" #include "avfiltergraph.h" #include "formats.h" +#include "framepool.h" #include "thread.h" #include "version.h" #include "video.h" diff --git a/libavfilter/video.c b/libavfilter/video.c index 0274fc18f7..2744be6360 100644 --- a/libavfilter/video.c +++ b/libavfilter/video.c @@ -32,31 +32,45 @@ #include "internal.h" #include "video.h" +#define BUFFER_ALIGN 32 + + AVFrame *ff_null_get_video_buffer(AVFilterLink *link, int w, int h) { return ff_get_video_buffer(link->dst->outputs[0], w, h); } -/* TODO: set the buffer's priv member to a context structure for the whole - * filter chain. This will allow for a buffer pool instead of the constant - * alloc & free cycle currently implemented. */ AVFrame *ff_default_get_video_buffer(AVFilterLink *link, int w, int h) { - AVFrame *frame = av_frame_alloc(); - int ret; + int pool_width = 0; + int pool_height = 0; + int pool_align = 0; + enum AVPixelFormat pool_format = AV_PIX_FMT_NONE; - if (!frame) - return NULL; + if (!link->video_frame_pool) { + link->video_frame_pool = ff_video_frame_pool_init(av_buffer_allocz, w, h, + link->format, BUFFER_ALIGN); + if (!link->video_frame_pool) + return NULL; + } else { + if (ff_video_frame_pool_get_config(link->video_frame_pool, + &pool_width, &pool_height, + &pool_format, &pool_align) < 0) { + return NULL; + } - frame->width = w; - frame->height = h; - frame->format = link->format; + if (pool_width != w || pool_height != h || + pool_format != link->format || pool_align != BUFFER_ALIGN) { - ret = av_frame_get_buffer(frame, 32); - if (ret < 0) - av_frame_free(&frame); + ff_video_frame_pool_uninit((FFVideoFramePool **)&link->video_frame_pool); + link->video_frame_pool = ff_video_frame_pool_init(av_buffer_allocz, w, h, + link->format, BUFFER_ALIGN); + if (!link->video_frame_pool) + return NULL; + } + } - return frame; + return ff_video_frame_pool_get(link->video_frame_pool); } AVFrame *ff_get_video_buffer(AVFilterLink *link, int w, int h)