mirror of https://git.ffmpeg.org/ffmpeg.git
259 lines
7.8 KiB
C
259 lines
7.8 KiB
C
/*
|
|
* Mpeg video formats-related picture management functions
|
|
*
|
|
* 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 "libavutil/avassert.h"
|
|
#include "libavutil/common.h"
|
|
#include "libavutil/mem.h"
|
|
#include "libavutil/pixdesc.h"
|
|
#include "libavutil/imgutils.h"
|
|
|
|
#include "avcodec.h"
|
|
#include "mpegpicture.h"
|
|
#include "refstruct.h"
|
|
|
|
static void mpv_pic_reset(FFRefStructOpaque unused, void *obj)
|
|
{
|
|
MPVPicture *pic = obj;
|
|
|
|
av_frame_unref(pic->f);
|
|
ff_thread_progress_reset(&pic->progress);
|
|
|
|
ff_refstruct_unref(&pic->hwaccel_picture_private);
|
|
|
|
ff_refstruct_unref(&pic->mbskip_table);
|
|
ff_refstruct_unref(&pic->qscale_table_base);
|
|
ff_refstruct_unref(&pic->mb_type_base);
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
ff_refstruct_unref(&pic->motion_val_base[i]);
|
|
ff_refstruct_unref(&pic->ref_index[i]);
|
|
|
|
pic->motion_val[i] = NULL;
|
|
}
|
|
|
|
pic->mb_type = NULL;
|
|
pic->qscale_table = NULL;
|
|
|
|
pic->mb_stride =
|
|
pic->mb_width =
|
|
pic->mb_height = 0;
|
|
|
|
pic->dummy = 0;
|
|
pic->field_picture = 0;
|
|
pic->b_frame_score = 0;
|
|
pic->reference = 0;
|
|
pic->shared = 0;
|
|
pic->display_picture_number = 0;
|
|
pic->coded_picture_number = 0;
|
|
}
|
|
|
|
static int av_cold mpv_pic_init(FFRefStructOpaque opaque, void *obj)
|
|
{
|
|
MPVPicture *pic = obj;
|
|
int ret, init_progress = (uintptr_t)opaque.nc;
|
|
|
|
ret = ff_thread_progress_init(&pic->progress, init_progress);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
pic->f = av_frame_alloc();
|
|
if (!pic->f)
|
|
return AVERROR(ENOMEM);
|
|
return 0;
|
|
}
|
|
|
|
static void av_cold mpv_pic_free(FFRefStructOpaque unused, void *obj)
|
|
{
|
|
MPVPicture *pic = obj;
|
|
|
|
ff_thread_progress_destroy(&pic->progress);
|
|
av_frame_free(&pic->f);
|
|
}
|
|
|
|
av_cold FFRefStructPool *ff_mpv_alloc_pic_pool(int init_progress)
|
|
{
|
|
return ff_refstruct_pool_alloc_ext(sizeof(MPVPicture),
|
|
FF_REFSTRUCT_POOL_FLAG_FREE_ON_INIT_ERROR,
|
|
(void*)(uintptr_t)init_progress,
|
|
mpv_pic_init, mpv_pic_reset, mpv_pic_free, NULL);
|
|
}
|
|
|
|
void ff_mpv_unref_picture(MPVWorkPicture *pic)
|
|
{
|
|
ff_refstruct_unref(&pic->ptr);
|
|
memset(pic, 0, sizeof(*pic));
|
|
}
|
|
|
|
static void set_workpic_from_pic(MPVWorkPicture *wpic, const MPVPicture *pic)
|
|
{
|
|
for (int i = 0; i < MPV_MAX_PLANES; i++) {
|
|
wpic->data[i] = pic->f->data[i];
|
|
wpic->linesize[i] = pic->f->linesize[i];
|
|
}
|
|
wpic->qscale_table = pic->qscale_table;
|
|
wpic->mb_type = pic->mb_type;
|
|
wpic->mbskip_table = pic->mbskip_table;
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
wpic->motion_val[i] = pic->motion_val[i];
|
|
wpic->ref_index[i] = pic->ref_index[i];
|
|
}
|
|
wpic->reference = pic->reference;
|
|
}
|
|
|
|
void ff_mpv_replace_picture(MPVWorkPicture *dst, const MPVWorkPicture *src)
|
|
{
|
|
av_assert1(dst != src);
|
|
ff_refstruct_replace(&dst->ptr, src->ptr);
|
|
memcpy(dst, src, sizeof(*dst));
|
|
}
|
|
|
|
void ff_mpv_workpic_from_pic(MPVWorkPicture *wpic, MPVPicture *pic)
|
|
{
|
|
ff_refstruct_replace(&wpic->ptr, pic);
|
|
if (!pic) {
|
|
memset(wpic, 0, sizeof(*wpic));
|
|
return;
|
|
}
|
|
set_workpic_from_pic(wpic, pic);
|
|
}
|
|
|
|
int ff_mpv_framesize_alloc(AVCodecContext *avctx,
|
|
ScratchpadContext *sc, int linesize)
|
|
{
|
|
# define EMU_EDGE_HEIGHT (4 * 70)
|
|
int linesizeabs = FFABS(linesize);
|
|
int alloc_size = FFALIGN(linesizeabs + 64, 32);
|
|
|
|
if (linesizeabs <= sc->linesize)
|
|
return 0;
|
|
|
|
if (avctx->hwaccel)
|
|
return 0;
|
|
|
|
if (linesizeabs < 24) {
|
|
av_log(avctx, AV_LOG_ERROR, "Image too small, temporary buffers cannot function\n");
|
|
return AVERROR_PATCHWELCOME;
|
|
}
|
|
|
|
if (av_image_check_size2(alloc_size, EMU_EDGE_HEIGHT, avctx->max_pixels, AV_PIX_FMT_NONE, 0, avctx) < 0)
|
|
return AVERROR(ENOMEM);
|
|
|
|
av_freep(&sc->edge_emu_buffer);
|
|
av_freep(&sc->scratchpad_buf);
|
|
|
|
// edge emu needs blocksize + filter length - 1
|
|
// (= 17x17 for halfpel / 21x21 for H.264)
|
|
// VC-1 computes luma and chroma simultaneously and needs 19X19 + 9x9
|
|
// at uvlinesize. It supports only YUV420 so 24x24 is enough
|
|
// linesize * interlaced * MBsize
|
|
// we also use this buffer for encoding in encode_mb_internal() needig an additional 32 lines
|
|
if (!FF_ALLOCZ_TYPED_ARRAY(sc->edge_emu_buffer, alloc_size * EMU_EDGE_HEIGHT) ||
|
|
!FF_ALLOCZ_TYPED_ARRAY(sc->scratchpad_buf, alloc_size * 4 * 16 * 2)) {
|
|
sc->linesize = 0;
|
|
av_freep(&sc->edge_emu_buffer);
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
sc->linesize = linesizeabs;
|
|
|
|
sc->obmc_scratchpad = sc->scratchpad_buf + 16;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ff_mpv_pic_check_linesize(void *logctx, const AVFrame *f,
|
|
ptrdiff_t *linesizep, ptrdiff_t *uvlinesizep)
|
|
{
|
|
ptrdiff_t linesize = *linesizep, uvlinesize = *uvlinesizep;
|
|
|
|
if ((linesize && linesize != f->linesize[0]) ||
|
|
(uvlinesize && uvlinesize != f->linesize[1])) {
|
|
av_log(logctx, AV_LOG_ERROR, "Stride change unsupported: "
|
|
"linesize=%"PTRDIFF_SPECIFIER"/%d uvlinesize=%"PTRDIFF_SPECIFIER"/%d)\n",
|
|
linesize, f->linesize[0],
|
|
uvlinesize, f->linesize[1]);
|
|
return AVERROR_PATCHWELCOME;
|
|
}
|
|
|
|
if (av_pix_fmt_count_planes(f->format) > 2 &&
|
|
f->linesize[1] != f->linesize[2]) {
|
|
av_log(logctx, AV_LOG_ERROR, "uv stride mismatch unsupported\n");
|
|
return AVERROR_PATCHWELCOME;
|
|
}
|
|
*linesizep = f->linesize[0];
|
|
*uvlinesizep = f->linesize[1];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int alloc_picture_tables(BufferPoolContext *pools, MPVPicture *pic,
|
|
int mb_height)
|
|
{
|
|
#define GET_BUFFER(name, buf_suffix, idx_suffix) do { \
|
|
pic->name ## buf_suffix idx_suffix = ff_refstruct_pool_get(pools->name ## _pool); \
|
|
if (!pic->name ## buf_suffix idx_suffix) \
|
|
return AVERROR(ENOMEM); \
|
|
} while (0)
|
|
GET_BUFFER(qscale_table, _base,);
|
|
GET_BUFFER(mb_type, _base,);
|
|
if (pools->motion_val_pool) {
|
|
if (pools->mbskip_table_pool)
|
|
GET_BUFFER(mbskip_table,,);
|
|
for (int i = 0; i < 2; i++) {
|
|
GET_BUFFER(ref_index,, [i]);
|
|
GET_BUFFER(motion_val, _base, [i]);
|
|
pic->motion_val[i] = pic->motion_val_base[i] + 4;
|
|
}
|
|
}
|
|
#undef GET_BUFFER
|
|
|
|
pic->mb_width = pools->alloc_mb_width;
|
|
pic->mb_height = mb_height;
|
|
pic->mb_stride = pools->alloc_mb_stride;
|
|
|
|
pic->qscale_table = pic->qscale_table_base + 2 * pic->mb_stride + 1;
|
|
pic->mb_type = pic->mb_type_base + 2 * pic->mb_stride + 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ff_mpv_alloc_pic_accessories(AVCodecContext *avctx, MPVWorkPicture *wpic,
|
|
ScratchpadContext *sc,
|
|
BufferPoolContext *pools, int mb_height)
|
|
{
|
|
MPVPicture *pic = wpic->ptr;
|
|
int ret;
|
|
|
|
ret = ff_mpv_framesize_alloc(avctx, sc, pic->f->linesize[0]);
|
|
if (ret < 0)
|
|
goto fail;
|
|
|
|
ret = alloc_picture_tables(pools, pic, mb_height);
|
|
if (ret < 0)
|
|
goto fail;
|
|
|
|
set_workpic_from_pic(wpic, pic);
|
|
|
|
return 0;
|
|
fail:
|
|
av_log(avctx, AV_LOG_ERROR, "Error allocating picture accessories.\n");
|
|
return ret;
|
|
}
|