summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2012-12-11 18:27:34 +0100
committerwm4 <wm4@nowhere>2013-01-13 17:39:32 +0100
commita8e69707f71f334daa4cfa461d88db9bc8e7fc7c (patch)
treec01b3b8f5cd4a8679f0948e4dca2e519c8e7665b /video
parent58d196c07e4faae0e9e7c03c64029c5c36e03c9a (diff)
downloadmpv-a8e69707f71f334daa4cfa461d88db9bc8e7fc7c.tar.bz2
mpv-a8e69707f71f334daa4cfa461d88db9bc8e7fc7c.tar.xz
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
Diffstat (limited to 'video')
-rw-r--r--video/decode/lavc.h41
-rw-r--r--video/decode/lavc_dr1.c227
-rw-r--r--video/decode/vd_lavc.c26
3 files changed, 273 insertions, 21 deletions
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 <libavcodec/avcodec.h>
+
+#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 <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <math.h>
+
+
+#include <libavcodec/avcodec.h>
+#include <libavutil/avassert.h>
+#include <libavutil/mathematics.h>
+#include <libavutil/imgutils.h>
+#include <libavutil/pixdesc.h>
+
+#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)