mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-11-21 10:55:51 +02:00
pngdec: fix and simplify apng reference handling
Current code is very confused and confusing. It uses two different reference frames - "previous" and "last" - when only one is really necessary. It also confuses the two, leading to incorrect output with APNG_DISPOSE_OP_PREVIOUS mode. Fixes #9017.
This commit is contained in:
parent
087359ad85
commit
5663301560
@ -54,7 +54,6 @@ typedef struct PNGDecContext {
|
|||||||
AVCodecContext *avctx;
|
AVCodecContext *avctx;
|
||||||
|
|
||||||
GetByteContext gb;
|
GetByteContext gb;
|
||||||
ThreadFrame previous_picture;
|
|
||||||
ThreadFrame last_picture;
|
ThreadFrame last_picture;
|
||||||
ThreadFrame picture;
|
ThreadFrame picture;
|
||||||
|
|
||||||
@ -711,13 +710,10 @@ static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s,
|
|||||||
s->bpp += byte_depth;
|
s->bpp += byte_depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avctx->codec_id == AV_CODEC_ID_APNG && s->last_dispose_op != APNG_DISPOSE_OP_PREVIOUS) {
|
ff_thread_release_buffer(avctx, &s->picture);
|
||||||
ff_thread_release_buffer(avctx, &s->previous_picture);
|
|
||||||
if ((ret = ff_thread_get_buffer(avctx, &s->previous_picture, AV_GET_BUFFER_FLAG_REF)) < 0)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if ((ret = ff_thread_get_buffer(avctx, &s->picture, AV_GET_BUFFER_FLAG_REF)) < 0)
|
if ((ret = ff_thread_get_buffer(avctx, &s->picture, AV_GET_BUFFER_FLAG_REF)) < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
p->pict_type = AV_PICTURE_TYPE_I;
|
p->pict_type = AV_PICTURE_TYPE_I;
|
||||||
p->key_frame = 1;
|
p->key_frame = 1;
|
||||||
p->interlaced_frame = !!s->interlace_type;
|
p->interlaced_frame = !!s->interlace_type;
|
||||||
@ -1020,7 +1016,7 @@ static int decode_fctl_chunk(AVCodecContext *avctx, PNGDecContext *s,
|
|||||||
return AVERROR_INVALIDDATA;
|
return AVERROR_INVALIDDATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((sequence_number == 0 || !s->previous_picture.f->data[0]) &&
|
if ((sequence_number == 0 || !s->last_picture.f->data[0]) &&
|
||||||
dispose_op == APNG_DISPOSE_OP_PREVIOUS) {
|
dispose_op == APNG_DISPOSE_OP_PREVIOUS) {
|
||||||
// No previous frame to revert to for the first frame
|
// No previous frame to revert to for the first frame
|
||||||
// Spec says to just treat it as a APNG_DISPOSE_OP_BACKGROUND
|
// Spec says to just treat it as a APNG_DISPOSE_OP_BACKGROUND
|
||||||
@ -1088,23 +1084,23 @@ static int handle_p_frame_apng(AVCodecContext *avctx, PNGDecContext *s,
|
|||||||
if (!buffer)
|
if (!buffer)
|
||||||
return AVERROR(ENOMEM);
|
return AVERROR(ENOMEM);
|
||||||
|
|
||||||
|
ff_thread_await_progress(&s->last_picture, INT_MAX, 0);
|
||||||
|
|
||||||
// Do the disposal operation specified by the last frame on the frame
|
// need to reset a rectangle to background:
|
||||||
if (s->last_dispose_op != APNG_DISPOSE_OP_PREVIOUS) {
|
// create a new writable copy
|
||||||
ff_thread_await_progress(&s->last_picture, INT_MAX, 0);
|
if (s->last_dispose_op == APNG_DISPOSE_OP_BACKGROUND) {
|
||||||
memcpy(buffer, s->last_picture.f->data[0], s->image_linesize * s->height);
|
int ret = av_frame_make_writable(s->last_picture.f);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (s->last_dispose_op == APNG_DISPOSE_OP_BACKGROUND)
|
for (y = s->last_y_offset; y < s->last_y_offset + s->last_h; y++) {
|
||||||
for (y = s->last_y_offset; y < s->last_y_offset + s->last_h; ++y)
|
memset(s->last_picture.f->data[0] + s->image_linesize * y +
|
||||||
memset(buffer + s->image_linesize * y + s->bpp * s->last_x_offset, 0, s->bpp * s->last_w);
|
s->bpp * s->last_x_offset, 0, s->bpp * s->last_w);
|
||||||
|
}
|
||||||
memcpy(s->previous_picture.f->data[0], buffer, s->image_linesize * s->height);
|
|
||||||
ff_thread_report_progress(&s->previous_picture, INT_MAX, 0);
|
|
||||||
} else {
|
|
||||||
ff_thread_await_progress(&s->previous_picture, INT_MAX, 0);
|
|
||||||
memcpy(buffer, s->previous_picture.f->data[0], s->image_linesize * s->height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy(buffer, s->last_picture.f->data[0], s->image_linesize * s->height);
|
||||||
|
|
||||||
// Perform blending
|
// Perform blending
|
||||||
if (s->blend_op == APNG_BLEND_OP_SOURCE) {
|
if (s->blend_op == APNG_BLEND_OP_SOURCE) {
|
||||||
for (y = s->y_offset; y < s->y_offset + s->cur_h; ++y) {
|
for (y = s->y_offset; y < s->y_offset + s->cur_h; ++y) {
|
||||||
@ -1448,22 +1444,17 @@ exit_loop:
|
|||||||
if (CONFIG_PNG_DECODER && avctx->codec_id != AV_CODEC_ID_APNG)
|
if (CONFIG_PNG_DECODER && avctx->codec_id != AV_CODEC_ID_APNG)
|
||||||
handle_p_frame_png(s, p);
|
handle_p_frame_png(s, p);
|
||||||
else if (CONFIG_APNG_DECODER &&
|
else if (CONFIG_APNG_DECODER &&
|
||||||
s->previous_picture.f->width == p->width &&
|
|
||||||
s->previous_picture.f->height== p->height &&
|
|
||||||
s->previous_picture.f->format== p->format &&
|
|
||||||
avctx->codec_id == AV_CODEC_ID_APNG &&
|
avctx->codec_id == AV_CODEC_ID_APNG &&
|
||||||
(ret = handle_p_frame_apng(avctx, s, p)) < 0)
|
(ret = handle_p_frame_apng(avctx, s, p)) < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ff_thread_report_progress(&s->picture, INT_MAX, 0);
|
ff_thread_report_progress(&s->picture, INT_MAX, 0);
|
||||||
ff_thread_report_progress(&s->previous_picture, INT_MAX, 0);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
ff_thread_report_progress(&s->picture, INT_MAX, 0);
|
ff_thread_report_progress(&s->picture, INT_MAX, 0);
|
||||||
ff_thread_report_progress(&s->previous_picture, INT_MAX, 0);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1475,14 +1466,10 @@ static int decode_frame_png(AVCodecContext *avctx,
|
|||||||
PNGDecContext *const s = avctx->priv_data;
|
PNGDecContext *const s = avctx->priv_data;
|
||||||
const uint8_t *buf = avpkt->data;
|
const uint8_t *buf = avpkt->data;
|
||||||
int buf_size = avpkt->size;
|
int buf_size = avpkt->size;
|
||||||
AVFrame *p;
|
AVFrame *p = s->picture.f;
|
||||||
int64_t sig;
|
int64_t sig;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ff_thread_release_buffer(avctx, &s->last_picture);
|
|
||||||
FFSWAP(ThreadFrame, s->picture, s->last_picture);
|
|
||||||
p = s->picture.f;
|
|
||||||
|
|
||||||
bytestream2_init(&s->gb, buf, buf_size);
|
bytestream2_init(&s->gb, buf, buf_size);
|
||||||
|
|
||||||
/* check signature */
|
/* check signature */
|
||||||
@ -1519,6 +1506,11 @@ static int decode_frame_png(AVCodecContext *avctx,
|
|||||||
if ((ret = av_frame_ref(data, s->picture.f)) < 0)
|
if ((ret = av_frame_ref(data, s->picture.f)) < 0)
|
||||||
goto the_end;
|
goto the_end;
|
||||||
|
|
||||||
|
if (!(avctx->active_thread_type & FF_THREAD_FRAME)) {
|
||||||
|
ff_thread_release_buffer(avctx, &s->last_picture);
|
||||||
|
FFSWAP(ThreadFrame, s->picture, s->last_picture);
|
||||||
|
}
|
||||||
|
|
||||||
*got_frame = 1;
|
*got_frame = 1;
|
||||||
|
|
||||||
ret = bytestream2_tell(&s->gb);
|
ret = bytestream2_tell(&s->gb);
|
||||||
@ -1536,11 +1528,7 @@ static int decode_frame_apng(AVCodecContext *avctx,
|
|||||||
{
|
{
|
||||||
PNGDecContext *const s = avctx->priv_data;
|
PNGDecContext *const s = avctx->priv_data;
|
||||||
int ret;
|
int ret;
|
||||||
AVFrame *p;
|
AVFrame *p = s->picture.f;
|
||||||
|
|
||||||
ff_thread_release_buffer(avctx, &s->last_picture);
|
|
||||||
FFSWAP(ThreadFrame, s->picture, s->last_picture);
|
|
||||||
p = s->picture.f;
|
|
||||||
|
|
||||||
if (!(s->hdr_state & PNG_IHDR)) {
|
if (!(s->hdr_state & PNG_IHDR)) {
|
||||||
if (!avctx->extradata_size)
|
if (!avctx->extradata_size)
|
||||||
@ -1576,6 +1564,15 @@ static int decode_frame_apng(AVCodecContext *avctx,
|
|||||||
if ((ret = av_frame_ref(data, s->picture.f)) < 0)
|
if ((ret = av_frame_ref(data, s->picture.f)) < 0)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
|
if (!(avctx->active_thread_type & FF_THREAD_FRAME)) {
|
||||||
|
if (s->dispose_op == APNG_DISPOSE_OP_PREVIOUS) {
|
||||||
|
ff_thread_release_buffer(avctx, &s->picture);
|
||||||
|
} else if (s->dispose_op == APNG_DISPOSE_OP_NONE) {
|
||||||
|
ff_thread_release_buffer(avctx, &s->last_picture);
|
||||||
|
FFSWAP(ThreadFrame, s->picture, s->last_picture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
*got_frame = 1;
|
*got_frame = 1;
|
||||||
ret = bytestream2_tell(&s->gb);
|
ret = bytestream2_tell(&s->gb);
|
||||||
|
|
||||||
@ -1590,16 +1587,14 @@ static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src)
|
|||||||
{
|
{
|
||||||
PNGDecContext *psrc = src->priv_data;
|
PNGDecContext *psrc = src->priv_data;
|
||||||
PNGDecContext *pdst = dst->priv_data;
|
PNGDecContext *pdst = dst->priv_data;
|
||||||
|
ThreadFrame *src_frame = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (dst == src)
|
if (dst == src)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ff_thread_release_buffer(dst, &pdst->picture);
|
|
||||||
if (psrc->picture.f->data[0] &&
|
|
||||||
(ret = ff_thread_ref_frame(&pdst->picture, &psrc->picture)) < 0)
|
|
||||||
return ret;
|
|
||||||
if (CONFIG_APNG_DECODER && dst->codec_id == AV_CODEC_ID_APNG) {
|
if (CONFIG_APNG_DECODER && dst->codec_id == AV_CODEC_ID_APNG) {
|
||||||
|
|
||||||
pdst->width = psrc->width;
|
pdst->width = psrc->width;
|
||||||
pdst->height = psrc->height;
|
pdst->height = psrc->height;
|
||||||
pdst->bit_depth = psrc->bit_depth;
|
pdst->bit_depth = psrc->bit_depth;
|
||||||
@ -1619,15 +1614,15 @@ static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src)
|
|||||||
memcpy(pdst->palette, psrc->palette, sizeof(pdst->palette));
|
memcpy(pdst->palette, psrc->palette, sizeof(pdst->palette));
|
||||||
|
|
||||||
pdst->hdr_state |= psrc->hdr_state;
|
pdst->hdr_state |= psrc->hdr_state;
|
||||||
|
}
|
||||||
|
|
||||||
ff_thread_release_buffer(dst, &pdst->last_picture);
|
src_frame = psrc->dispose_op == APNG_DISPOSE_OP_NONE ?
|
||||||
if (psrc->last_picture.f->data[0] &&
|
&psrc->picture : &psrc->last_picture;
|
||||||
(ret = ff_thread_ref_frame(&pdst->last_picture, &psrc->last_picture)) < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ff_thread_release_buffer(dst, &pdst->previous_picture);
|
ff_thread_release_buffer(dst, &pdst->last_picture);
|
||||||
if (psrc->previous_picture.f->data[0] &&
|
if (src_frame && src_frame->f->data[0]) {
|
||||||
(ret = ff_thread_ref_frame(&pdst->previous_picture, &psrc->previous_picture)) < 0)
|
ret = ff_thread_ref_frame(&pdst->last_picture, src_frame);
|
||||||
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1642,11 +1637,9 @@ static av_cold int png_dec_init(AVCodecContext *avctx)
|
|||||||
avctx->color_range = AVCOL_RANGE_JPEG;
|
avctx->color_range = AVCOL_RANGE_JPEG;
|
||||||
|
|
||||||
s->avctx = avctx;
|
s->avctx = avctx;
|
||||||
s->previous_picture.f = av_frame_alloc();
|
|
||||||
s->last_picture.f = av_frame_alloc();
|
s->last_picture.f = av_frame_alloc();
|
||||||
s->picture.f = av_frame_alloc();
|
s->picture.f = av_frame_alloc();
|
||||||
if (!s->previous_picture.f || !s->last_picture.f || !s->picture.f) {
|
if (!s->last_picture.f || !s->picture.f) {
|
||||||
av_frame_free(&s->previous_picture.f);
|
|
||||||
av_frame_free(&s->last_picture.f);
|
av_frame_free(&s->last_picture.f);
|
||||||
av_frame_free(&s->picture.f);
|
av_frame_free(&s->picture.f);
|
||||||
return AVERROR(ENOMEM);
|
return AVERROR(ENOMEM);
|
||||||
@ -1661,8 +1654,6 @@ static av_cold int png_dec_end(AVCodecContext *avctx)
|
|||||||
{
|
{
|
||||||
PNGDecContext *s = avctx->priv_data;
|
PNGDecContext *s = avctx->priv_data;
|
||||||
|
|
||||||
ff_thread_release_buffer(avctx, &s->previous_picture);
|
|
||||||
av_frame_free(&s->previous_picture.f);
|
|
||||||
ff_thread_release_buffer(avctx, &s->last_picture);
|
ff_thread_release_buffer(avctx, &s->last_picture);
|
||||||
av_frame_free(&s->last_picture.f);
|
av_frame_free(&s->last_picture.f);
|
||||||
ff_thread_release_buffer(avctx, &s->picture);
|
ff_thread_release_buffer(avctx, &s->picture);
|
||||||
|
Loading…
Reference in New Issue
Block a user