1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-08-04 22:03:09 +02:00

avcodec/lcevcdec: don't try to write to output frames directly

The buffer references may not be writable at this point, as the decoder
calls get_buffer2() with the AV_GET_BUFFER_FLAG_REF flag.

Fixes races as reported by tsan, producing correct output regardless of
threading choices.

Signed-off-by: James Almer <jamrial@gmail.com>
This commit is contained in:
James Almer
2025-05-16 16:54:52 -03:00
parent d028cf03b8
commit 8eae65dc5c
3 changed files with 78 additions and 35 deletions

View File

@ -1590,22 +1590,49 @@ static void update_frame_props(AVCodecContext *avctx, AVFrame *frame)
} }
} }
static void attach_post_process_data(AVCodecContext *avctx, AVFrame *frame) static int attach_post_process_data(AVCodecContext *avctx, AVFrame *frame)
{ {
AVCodecInternal *avci = avctx->internal; AVCodecInternal *avci = avctx->internal;
DecodeContext *dc = decode_ctx(avci); DecodeContext *dc = decode_ctx(avci);
if (dc->lcevc_frame) { if (dc->lcevc_frame) {
FrameDecodeData *fdd = frame->private_ref; FrameDecodeData *fdd = frame->private_ref;
FFLCEVCFrame *frame_ctx;
int ret;
fdd->post_process_opaque = av_refstruct_ref(dc->lcevc); frame_ctx = av_mallocz(sizeof(*frame_ctx));
fdd->post_process_opaque_free = ff_lcevc_unref; if (!frame_ctx)
fdd->post_process = ff_lcevc_process; return AVERROR(ENOMEM);
frame_ctx->frame = av_frame_alloc();
if (!frame_ctx->frame) {
av_free(frame_ctx);
return AVERROR(ENOMEM);
}
frame_ctx->lcevc = av_refstruct_ref(dc->lcevc);
frame_ctx->frame->width = frame->width;
frame_ctx->frame->height = frame->height;
frame_ctx->frame->format = frame->format;
frame->width = dc->width; frame->width = dc->width;
frame->height = dc->height; frame->height = dc->height;
ret = avctx->get_buffer2(avctx, frame_ctx->frame, 0);
if (ret < 0) {
ff_lcevc_unref(frame_ctx);
return ret;
}
validate_avframe_allocation(avctx, frame_ctx->frame);
fdd->post_process_opaque = frame_ctx;
fdd->post_process_opaque_free = ff_lcevc_unref;
fdd->post_process = ff_lcevc_process;
} }
dc->lcevc_frame = 0; dc->lcevc_frame = 0;
return 0;
} }
int ff_get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags) int ff_get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags)
@ -1666,7 +1693,9 @@ int ff_get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags)
if (ret < 0) if (ret < 0)
goto fail; goto fail;
attach_post_process_data(avctx, frame); ret = attach_post_process_data(avctx, frame);
if (ret < 0)
goto fail;
end: end:
if (avctx->codec_type == AVMEDIA_TYPE_VIDEO && !override_dimensions && if (avctx->codec_type == AVMEDIA_TYPE_VIDEO && !override_dimensions &&

View File

@ -47,7 +47,7 @@ static LCEVC_ColorFormat map_format(int format)
return LCEVC_ColorFormat_Unknown; return LCEVC_ColorFormat_Unknown;
} }
static int alloc_base_frame(void *logctx, LCEVC_DecoderHandle decoder, static int alloc_base_frame(void *logctx, FFLCEVCContext *lcevc,
const AVFrame *frame, LCEVC_PictureHandle *picture) const AVFrame *frame, LCEVC_PictureHandle *picture)
{ {
LCEVC_PictureDesc desc; LCEVC_PictureDesc desc;
@ -70,22 +70,22 @@ static int alloc_base_frame(void *logctx, LCEVC_DecoderHandle decoder,
desc.sampleAspectRatioDen = frame->sample_aspect_ratio.den; desc.sampleAspectRatioDen = frame->sample_aspect_ratio.den;
/* Allocate LCEVC Picture */ /* Allocate LCEVC Picture */
res = LCEVC_AllocPicture(decoder, &desc, picture); res = LCEVC_AllocPicture(lcevc->decoder, &desc, picture);
if (res != LCEVC_Success) { if (res != LCEVC_Success) {
return AVERROR_EXTERNAL; return AVERROR_EXTERNAL;
} }
res = LCEVC_LockPicture(decoder, *picture, LCEVC_Access_Write, &lock); res = LCEVC_LockPicture(lcevc->decoder, *picture, LCEVC_Access_Write, &lock);
if (res != LCEVC_Success) if (res != LCEVC_Success)
return AVERROR_EXTERNAL; return AVERROR_EXTERNAL;
res = LCEVC_GetPicturePlaneCount(decoder, *picture, &planes); res = LCEVC_GetPicturePlaneCount(lcevc->decoder, *picture, &planes);
if (res != LCEVC_Success) if (res != LCEVC_Success)
return AVERROR_EXTERNAL; return AVERROR_EXTERNAL;
for (unsigned i = 0; i < planes; i++) { for (unsigned i = 0; i < planes; i++) {
LCEVC_PicturePlaneDesc plane; LCEVC_PicturePlaneDesc plane;
res = LCEVC_GetPictureLockPlaneDesc(decoder, lock, i, &plane); res = LCEVC_GetPictureLockPlaneDesc(lcevc->decoder, lock, i, &plane);
if (res != LCEVC_Success) if (res != LCEVC_Success)
return AVERROR_EXTERNAL; return AVERROR_EXTERNAL;
@ -96,43 +96,43 @@ static int alloc_base_frame(void *logctx, LCEVC_DecoderHandle decoder,
av_image_copy2(data, linesizes, frame->data, frame->linesize, av_image_copy2(data, linesizes, frame->data, frame->linesize,
frame->format, frame->width, frame->height); frame->format, frame->width, frame->height);
res = LCEVC_UnlockPicture(decoder, lock); res = LCEVC_UnlockPicture(lcevc->decoder, lock);
if (res != LCEVC_Success) if (res != LCEVC_Success)
return AVERROR_EXTERNAL; return AVERROR_EXTERNAL;
return 0; return 0;
} }
static int alloc_enhanced_frame(void *logctx, LCEVC_DecoderHandle decoder, static int alloc_enhanced_frame(void *logctx, FFLCEVCFrame *frame_ctx,
const AVFrame *frame, LCEVC_PictureHandle *picture) LCEVC_PictureHandle *picture)
{ {
FFLCEVCContext *lcevc = frame_ctx->lcevc;
LCEVC_PictureDesc desc ; LCEVC_PictureDesc desc ;
LCEVC_ColorFormat fmt = map_format(frame->format); LCEVC_ColorFormat fmt = map_format(frame_ctx->frame->format);
LCEVC_PicturePlaneDesc planes[4] = { 0 }; LCEVC_PicturePlaneDesc planes[4] = { 0 };
int width = frame->width * 2 / FFMAX(frame->sample_aspect_ratio.den, 1);
int height = frame->height * 2 / FFMAX(frame->sample_aspect_ratio.num, 1);
LCEVC_ReturnCode res; LCEVC_ReturnCode res;
res = LCEVC_DefaultPictureDesc(&desc, fmt, width, height); res = LCEVC_DefaultPictureDesc(&desc, fmt, frame_ctx->frame->width, frame_ctx->frame->height);
if (res != LCEVC_Success) if (res != LCEVC_Success)
return AVERROR_EXTERNAL; return AVERROR_EXTERNAL;
/* Set plane description */ /* Set plane description */
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
planes[i].firstSample = frame->data[i]; planes[i].firstSample = frame_ctx->frame->data[i];
planes[i].rowByteStride = frame->linesize[i]; planes[i].rowByteStride = frame_ctx->frame->linesize[i];
} }
/* Allocate LCEVC Picture */ /* Allocate LCEVC Picture */
res = LCEVC_AllocPictureExternal(decoder, &desc, NULL, planes, picture); res = LCEVC_AllocPictureExternal(lcevc->decoder, &desc, NULL, planes, picture);
if (res != LCEVC_Success) { if (res != LCEVC_Success) {
return AVERROR_EXTERNAL; return AVERROR_EXTERNAL;
} }
return 0; return 0;
} }
static int lcevc_send_frame(void *logctx, FFLCEVCContext *lcevc, const AVFrame *in) static int lcevc_send_frame(void *logctx, FFLCEVCFrame *frame_ctx, const AVFrame *in)
{ {
FFLCEVCContext *lcevc = frame_ctx->lcevc;
const AVFrameSideData *sd = av_frame_get_side_data(in, AV_FRAME_DATA_LCEVC); const AVFrameSideData *sd = av_frame_get_side_data(in, AV_FRAME_DATA_LCEVC);
LCEVC_PictureHandle picture; LCEVC_PictureHandle picture;
LCEVC_ReturnCode res; LCEVC_ReturnCode res;
@ -145,7 +145,7 @@ static int lcevc_send_frame(void *logctx, FFLCEVCContext *lcevc, const AVFrame *
if (res != LCEVC_Success) if (res != LCEVC_Success)
return AVERROR_EXTERNAL; return AVERROR_EXTERNAL;
ret = alloc_base_frame(logctx, lcevc->decoder, in, &picture); ret = alloc_base_frame(logctx, lcevc, in, &picture);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -154,7 +154,7 @@ static int lcevc_send_frame(void *logctx, FFLCEVCContext *lcevc, const AVFrame *
return AVERROR_EXTERNAL; return AVERROR_EXTERNAL;
memset(&picture, 0, sizeof(picture)); memset(&picture, 0, sizeof(picture));
ret = alloc_enhanced_frame(logctx, lcevc->decoder, in, &picture); ret = alloc_enhanced_frame(logctx, frame_ctx, &picture);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -165,8 +165,9 @@ static int lcevc_send_frame(void *logctx, FFLCEVCContext *lcevc, const AVFrame *
return 0; return 0;
} }
static int generate_output(void *logctx, FFLCEVCContext *lcevc, AVFrame *out) static int generate_output(void *logctx, FFLCEVCFrame *frame_ctx, AVFrame *out)
{ {
FFLCEVCContext *lcevc = frame_ctx->lcevc;
LCEVC_PictureDesc desc; LCEVC_PictureDesc desc;
LCEVC_DecodeInformation info; LCEVC_DecodeInformation info;
LCEVC_PictureHandle picture; LCEVC_PictureHandle picture;
@ -186,6 +187,11 @@ static int generate_output(void *logctx, FFLCEVCContext *lcevc, AVFrame *out)
out->crop_right = desc.cropRight; out->crop_right = desc.cropRight;
out->sample_aspect_ratio.num = desc.sampleAspectRatioNum; out->sample_aspect_ratio.num = desc.sampleAspectRatioNum;
out->sample_aspect_ratio.den = desc.sampleAspectRatioDen; out->sample_aspect_ratio.den = desc.sampleAspectRatioDen;
av_frame_copy_props(frame_ctx->frame, out);
av_frame_unref(out);
av_frame_move_ref(out, frame_ctx->frame);
out->width = desc.width + out->crop_left + out->crop_right; out->width = desc.width + out->crop_left + out->crop_right;
out->height = desc.height + out->crop_top + out->crop_bottom; out->height = desc.height + out->crop_top + out->crop_bottom;
@ -196,13 +202,14 @@ static int generate_output(void *logctx, FFLCEVCContext *lcevc, AVFrame *out)
return 0; return 0;
} }
static int lcevc_receive_frame(void *logctx, FFLCEVCContext *lcevc, AVFrame *out) static int lcevc_receive_frame(void *logctx, FFLCEVCFrame *frame_ctx, AVFrame *out)
{ {
FFLCEVCContext *lcevc = frame_ctx->lcevc;
LCEVC_PictureHandle picture; LCEVC_PictureHandle picture;
LCEVC_ReturnCode res; LCEVC_ReturnCode res;
int ret; int ret;
ret = generate_output(logctx, lcevc, out); ret = generate_output(logctx, frame_ctx, out);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -249,12 +256,7 @@ static int lcevc_init(FFLCEVCContext *lcevc, void *logctx)
#if CONFIG_LIBLCEVC_DEC #if CONFIG_LIBLCEVC_DEC
LCEVC_AccelContextHandle dummy = { 0 }; LCEVC_AccelContextHandle dummy = { 0 };
const int32_t event = LCEVC_Log; const int32_t event = LCEVC_Log;
#endif
if (lcevc->initialized)
return 0;
#if CONFIG_LIBLCEVC_DEC
if (LCEVC_CreateDecoder(&lcevc->decoder, dummy) != LCEVC_Success) { if (LCEVC_CreateDecoder(&lcevc->decoder, dummy) != LCEVC_Success) {
av_log(logctx, AV_LOG_ERROR, "Failed to create LCEVC decoder\n"); av_log(logctx, AV_LOG_ERROR, "Failed to create LCEVC decoder\n");
return AVERROR_EXTERNAL; return AVERROR_EXTERNAL;
@ -279,7 +281,8 @@ static int lcevc_init(FFLCEVCContext *lcevc, void *logctx)
int ff_lcevc_process(void *logctx, AVFrame *frame) int ff_lcevc_process(void *logctx, AVFrame *frame)
{ {
FrameDecodeData *fdd = frame->private_ref; FrameDecodeData *fdd = frame->private_ref;
FFLCEVCContext *lcevc = fdd->post_process_opaque; FFLCEVCFrame *frame_ctx = fdd->post_process_opaque;
FFLCEVCContext *lcevc = frame_ctx->lcevc;
int ret; int ret;
if (!lcevc->initialized) { if (!lcevc->initialized) {
@ -289,11 +292,14 @@ int ff_lcevc_process(void *logctx, AVFrame *frame)
} }
#if CONFIG_LIBLCEVC_DEC #if CONFIG_LIBLCEVC_DEC
ret = lcevc_send_frame(logctx, lcevc, frame); av_assert0(frame_ctx->frame);
ret = lcevc_send_frame(logctx, frame_ctx, frame);
if (ret) if (ret)
return ret < 0 ? ret : 0; return ret < 0 ? ret : 0;
lcevc_receive_frame(logctx, lcevc, frame); lcevc_receive_frame(logctx, frame_ctx, frame);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -317,5 +323,8 @@ int ff_lcevc_alloc(FFLCEVCContext **plcevc)
void ff_lcevc_unref(void *opaque) void ff_lcevc_unref(void *opaque)
{ {
av_refstruct_unref(&opaque); FFLCEVCFrame *lcevc = opaque;
av_refstruct_unref(&lcevc->lcevc);
av_frame_free(&lcevc->frame);
av_free(opaque);
} }

View File

@ -35,6 +35,11 @@ typedef struct FFLCEVCContext {
struct AVFrame; struct AVFrame;
typedef struct FFLCEVCFrame {
FFLCEVCContext *lcevc;
struct AVFrame *frame;
} FFLCEVCFrame;
int ff_lcevc_alloc(FFLCEVCContext **plcevc); int ff_lcevc_alloc(FFLCEVCContext **plcevc);
int ff_lcevc_process(void *logctx, struct AVFrame *frame); int ff_lcevc_process(void *logctx, struct AVFrame *frame);
void ff_lcevc_unref(void *opaque); void ff_lcevc_unref(void *opaque);