From a8e69707f71f334daa4cfa461d88db9bc8e7fc7c Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 11 Dec 2012 18:27:34 +0100 Subject: vd_lavc: add DR1 support Replace libavcodec's native buffer allocation with code taken from ffplay/ffmpeg's libavfilter support. The code in lavc_dr1.c is directly copied from cmdutils.c. Note that this is quite arcane code, which contains some workarounds for decoder bugs and the like. This is not really a maintainance burden, since fixes from ffmpeg can be directly applied to the code in lavc_dr1.c. It's unknown why libavcodec doesn't provide such a function directly. avcodec_default_get_buffer() can't be reused for various reasons. There's some hope that the work known as The Evil Plan [1] will make custom get_buffer implementations unneeded. The DR1 support as of this commit does nothing. A future commit will use it to implement ref-counting for mp_image (similar to how AVFrame will be ref-counted with The Evil Plan.) [1] http://lists.libav.org/pipermail/libav-devel/2012-December/039781.html --- video/decode/lavc_dr1.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 video/decode/lavc_dr1.c (limited to 'video/decode/lavc_dr1.c') diff --git a/video/decode/lavc_dr1.c b/video/decode/lavc_dr1.c new file mode 100644 index 0000000000..1049b6105a --- /dev/null +++ b/video/decode/lavc_dr1.c @@ -0,0 +1,227 @@ +/* + * Various utilities for command line tools + * Copyright (c) 2000-2003 Fabrice Bellard + * + * 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 + */ + +#include +#include +#include +#include + + +#include +#include +#include +#include +#include + +#include "lavc.h" + +typedef struct FramePool { + struct FrameBuffer *list; + // used to deal with frames that live past the time the pool should live + int dead; + int refcount; // number of allocated buffers (not in list) +} FramePool; + +typedef struct FrameBuffer { + uint8_t *base[4]; + uint8_t *data[4]; + int linesize[4]; + + int h, w; + int pix_fmt; + + int refcount; + struct FramePool *pool; + struct FrameBuffer *next; +} FrameBuffer; + + +static int alloc_buffer(FramePool *pool, AVCodecContext *s) +{ + const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[s->pix_fmt]; + FrameBuffer *buf; + int i, ret; + int pixel_size; + int h_chroma_shift, v_chroma_shift; + int edge = 32; // XXX should be avcodec_get_edge_width(), but that fails on svq1 + int w = s->width, h = s->height; + + if (!desc) + return AVERROR(EINVAL); + pixel_size = desc->comp[0].step_minus1 + 1; + + buf = av_mallocz(sizeof(*buf)); + if (!buf) + return AVERROR(ENOMEM); + + avcodec_align_dimensions(s, &w, &h); + + if (!(s->flags & CODEC_FLAG_EMU_EDGE)) { + w += 2*edge; + h += 2*edge; + } + + if ((ret = av_image_alloc(buf->base, buf->linesize, w, h, + s->pix_fmt, 32)) < 0) { + av_freep(&buf); + av_log(s, AV_LOG_ERROR, "alloc_buffer: av_image_alloc() failed\n"); + return ret; + } + /* XXX this shouldn't be needed, but some tests break without this line + * those decoders are buggy and need to be fixed. + * the following tests fail: + * cdgraphics, ansi, aasc, fraps-v1, qtrle-1bit + */ + memset(buf->base[0], 128, ret); + + avcodec_get_chroma_sub_sample(s->pix_fmt, &h_chroma_shift, &v_chroma_shift); + for (i = 0; i < FF_ARRAY_ELEMS(buf->data); i++) { + const int h_shift = i==0 ? 0 : h_chroma_shift; + const int v_shift = i==0 ? 0 : v_chroma_shift; + if ((s->flags & CODEC_FLAG_EMU_EDGE) || !buf->linesize[i] || !buf->base[i]) + buf->data[i] = buf->base[i]; + else + buf->data[i] = buf->base[i] + + FFALIGN((buf->linesize[i]*edge >> v_shift) + + (pixel_size*edge >> h_shift), 32); + } + buf->w = s->width; + buf->h = s->height; + buf->pix_fmt = s->pix_fmt; + buf->pool = pool; + + buf->next = pool->list; + pool->list = buf; + return 0; +} + +int mp_codec_get_buffer(AVCodecContext *s, AVFrame *frame) +{ + sh_video_t *sh = s->opaque; + struct ffmpeg_ctx *ctx = sh->context; + + if (!ctx->dr1_buffer_pool) { + ctx->dr1_buffer_pool = av_mallocz(sizeof(*ctx->dr1_buffer_pool)); + if (!ctx->dr1_buffer_pool) + return AVERROR(ENOMEM); + } + + FramePool *pool = ctx->dr1_buffer_pool; + FrameBuffer *buf; + int ret, i; + + if(av_image_check_size(s->width, s->height, 0, s) || s->pix_fmt<0) { + av_log(s, AV_LOG_ERROR, "codec_get_buffer: image parameters invalid\n"); + return -1; + } + + if (!pool->list && (ret = alloc_buffer(pool, s)) < 0) + return ret; + + buf = pool->list; + if (buf->w != s->width || buf->h != s->height || buf->pix_fmt != s->pix_fmt) { + pool->list = buf->next; + av_freep(&buf->base[0]); + av_free(buf); + if ((ret = alloc_buffer(pool, s)) < 0) + return ret; + buf = pool->list; + } + av_assert0(!buf->refcount); + buf->refcount++; + + pool->list = buf->next; + pool->refcount++; + + frame->opaque = buf; + frame->type = FF_BUFFER_TYPE_USER; + frame->extended_data = frame->data; + + for (i = 0; i < FF_ARRAY_ELEMS(buf->data); i++) { + frame->base[i] = buf->base[i]; // XXX h264.c uses base though it shouldn't + frame->data[i] = buf->data[i]; + frame->linesize[i] = buf->linesize[i]; + } + + return 0; +} + +void mp_buffer_ref(struct FrameBuffer *buf) +{ + buf->refcount++; +} + +void mp_buffer_unref(struct FrameBuffer *buf) +{ + FramePool *pool = buf->pool; + + av_assert0(pool->refcount > 0); + av_assert0(buf->refcount > 0); + buf->refcount--; + if (!buf->refcount) { + FrameBuffer *tmp; + for(tmp= pool->list; tmp; tmp= tmp->next) + av_assert1(tmp != buf); + + buf->next = pool->list; + pool->list = buf; + pool->refcount--; + } + + if (pool->dead && pool->refcount == 0) + mp_buffer_pool_free(&pool); +} + +void mp_codec_release_buffer(AVCodecContext *s, AVFrame *frame) +{ + FrameBuffer *buf = frame->opaque; + int i; + + if(frame->type!=FF_BUFFER_TYPE_USER) { + avcodec_default_release_buffer(s, frame); + return; + } + + for (i = 0; i < FF_ARRAY_ELEMS(frame->data); i++) + frame->data[i] = NULL; + + mp_buffer_unref(buf); +} + +void mp_buffer_pool_free(struct FramePool **p_pool) +{ + struct FramePool *pool = *p_pool; + if (!pool) + return; + + while (pool->list) { + FrameBuffer *buf = pool->list; + pool->list = buf->next; + av_assert0(buf->refcount == 0); + av_freep(&buf->base[0]); + av_free(buf); + } + pool->dead = 1; + if (pool->refcount == 0) + av_free(pool); + + *p_pool = NULL; +} -- cgit v1.2.3