1
0
mirror of https://github.com/mpv-player/mpv synced 2024-12-12 18:06:18 +00:00
mpv/video/decode/lavc_dr1.c

250 lines
6.9 KiB
C
Raw Normal View History

/*
* 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 used_by_decoder, needed_by_decoder;
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;
buf->used_by_decoder = buf->needed_by_decoder = 1;
if (frame->buffer_hints & FF_BUFFER_HINTS_VALID) {
buf->needed_by_decoder =
(frame->buffer_hints & FF_BUFFER_HINTS_PRESERVE) ||
(frame->buffer_hints & FF_BUFFER_HINTS_REUSABLE);
} else {
buf->needed_by_decoder = !!frame->reference;
}
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);
}
bool mp_buffer_is_unique(struct FrameBuffer *buf)
{
int refcount = buf->refcount;
// Decoder has a reference, but doesn't want to use it. (ffmpeg has no good
// way of transferring frame ownership to the user.)
if (buf->used_by_decoder && !buf->needed_by_decoder)
refcount--;
return refcount == 1;
}
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;
}
buf->used_by_decoder = buf->needed_by_decoder = 0;
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;
}