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.h | 41 +++++++++ video/decode/lavc_dr1.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++++ video/decode/vd_lavc.c | 26 ++---- 3 files changed, 273 insertions(+), 21 deletions(-) create mode 100644 video/decode/lavc.h create mode 100644 video/decode/lavc_dr1.c (limited to 'video') diff --git a/video/decode/lavc.h b/video/decode/lavc.h new file mode 100644 index 0000000000..bf8a3fc12c --- /dev/null +++ b/video/decode/lavc.h @@ -0,0 +1,41 @@ +#ifndef MPV_LAVC_H +#define MPV_LAVC_H + +#include + +#include "demux/stheader.h" +#include "video/mp_image.h" + +#define MAX_NUM_MPI 50 + +typedef struct ffmpeg_ctx { + AVCodecContext *avctx; + AVFrame *pic; + struct mp_image export_mpi; + struct mp_image hwdec_mpi[MAX_NUM_MPI]; + struct hwdec *hwdec; + enum PixelFormat pix_fmt; + int do_dr1; + int vo_initialized; + int best_csp; + int qp_stat[32]; + double qp_sum; + double inv_qp_sum; + AVRational last_sample_aspect_ratio; + enum AVDiscard skip_frame; + int rawvideo_fmt; + AVCodec *software_fallback; + struct FramePool *dr1_buffer_pool; +} vd_ffmpeg_ctx; + +int mp_codec_get_buffer(AVCodecContext *s, AVFrame *frame); +void mp_codec_release_buffer(AVCodecContext *s, AVFrame *frame); + +struct FrameBuffer; + +void mp_buffer_ref(struct FrameBuffer *buffer); +void mp_buffer_unref(struct FrameBuffer *buffer); + +void mp_buffer_pool_free(struct FramePool **pool); + +#endif 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; +} diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c index 5c25b1fff3..ba015b0ad6 100644 --- a/video/decode/vd_lavc.c +++ b/video/decode/vd_lavc.c @@ -58,32 +58,12 @@ static const vd_info_t info = { }; #include "libavcodec/avcodec.h" +#include "lavc.h" #if AVPALETTE_SIZE > 1024 #error palette too large, adapt libmpcodecs/vf.c:vf_get_image #endif -#define MAX_NUM_MPI 50 - -typedef struct { - AVCodecContext *avctx; - AVFrame *pic; - struct mp_image export_mpi; - struct mp_image hwdec_mpi[MAX_NUM_MPI]; - struct hwdec *hwdec; - enum PixelFormat pix_fmt; - int do_dr1; - int vo_initialized; - int best_csp; - int qp_stat[32]; - double qp_sum; - double inv_qp_sum; - AVRational last_sample_aspect_ratio; - enum AVDiscard skip_frame; - int rawvideo_fmt; - AVCodec *software_fallback; -} vd_ffmpeg_ctx; - #include "core/m_option.h" static int init_avctx(sh_video_t *sh, AVCodec *lavc_codec, struct hwdec *hwdec); @@ -350,6 +330,9 @@ static int init_avctx(sh_video_t *sh, AVCodec *lavc_codec, struct hwdec *hwdec) avctx->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD; } + } else { + avctx->get_buffer = mp_codec_get_buffer; + avctx->release_buffer = mp_codec_release_buffer; } if (avctx->thread_count == 0) { @@ -492,6 +475,7 @@ static void uninit_avctx(sh_video_t *sh) av_freep(&avctx); avcodec_free_frame(&ctx->pic); + mp_buffer_pool_free(&ctx->dr1_buffer_pool); } static void uninit(sh_video_t *sh) -- cgit v1.2.3