FFmpeg/libavcodec/mpegpicture.c
Andreas Rheinhardt a05eebee99 avcodec/mpegpicture: Avoid MotionEstContext in ff_mpeg_framesize_alloc()
Only set the ScratchpadContext and let the users that need it
(i.e. encoders) set the MotionEstContext stuff themselves.
Also add an explicit pointer to ScratchpadContext to point
to the allocated buffer so that none of the other scratchpad
pointers is singled out as being used for the allocations.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2024-06-12 11:54:57 +02:00

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;
}