diff --git a/libavcodec/pngenc.c b/libavcodec/pngenc.c index 00c830e6eb..51ae094770 100644 --- a/libavcodec/pngenc.c +++ b/libavcodec/pngenc.c @@ -68,6 +68,9 @@ typedef struct PNGEncContext { // APNG uint32_t palette_checksum; // Used to ensure a single unique palette uint32_t sequence_number; + int extra_data_updated; + uint8_t *extra_data; + int extra_data_size; AVFrame *prev_frame; AVFrame *last_frame; @@ -870,15 +873,15 @@ static int encode_apng(AVCodecContext *avctx, AVPacket *pkt, if (!pict) return AVERROR(EINVAL); - s->bytestream = avctx->extradata = av_malloc(FF_MIN_BUFFER_SIZE); - if (!avctx->extradata) + s->bytestream = s->extra_data = av_malloc(FF_MIN_BUFFER_SIZE); + if (!s->extra_data) return AVERROR(ENOMEM); ret = encode_headers(avctx, pict); if (ret < 0) return ret; - avctx->extradata_size = s->bytestream - avctx->extradata; + s->extra_data_size = s->bytestream - s->extra_data; s->last_frame_packet = av_malloc(max_packet_size); if (!s->last_frame_packet) @@ -917,6 +920,13 @@ static int encode_apng(AVCodecContext *avctx, AVPacket *pkt, if (s->last_frame) { uint8_t* last_fctl_chunk_start = pkt->data; uint8_t buf[26]; + if (!s->extra_data_updated) { + uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, s->extra_data_size); + if (!side_data) + return AVERROR(ENOMEM); + memcpy(side_data, s->extra_data, s->extra_data_size); + s->extra_data_updated = 1; + } AV_WB32(buf + 0, s->last_frame_fctl.sequence_number); AV_WB32(buf + 4, s->last_frame_fctl.width); @@ -1093,6 +1103,8 @@ static av_cold int png_enc_close(AVCodecContext *avctx) av_frame_free(&s->last_frame); av_frame_free(&s->prev_frame); av_freep(&s->last_frame_packet); + av_freep(&s->extra_data); + s->extra_data_size = 0; return 0; } diff --git a/libavformat/apngenc.c b/libavformat/apngenc.c index e25df2eb5e..e5e8aa998f 100644 --- a/libavformat/apngenc.c +++ b/libavformat/apngenc.c @@ -44,6 +44,9 @@ typedef struct APNGMuxContext { AVRational prev_delay; int framerate_warned; + + uint8_t *extra_data; + int extra_data_size; } APNGMuxContext; static uint8_t *apng_find_chunk(uint32_t tag, uint8_t *buf, size_t length) @@ -101,15 +104,27 @@ static int apng_write_header(AVFormatContext *format_context) return 0; } -static void flush_packet(AVFormatContext *format_context, AVPacket *packet) +static int flush_packet(AVFormatContext *format_context, AVPacket *packet) { APNGMuxContext *apng = format_context->priv_data; AVIOContext *io_context = format_context->pb; AVStream *codec_stream = format_context->streams[0]; - AVCodecParameters *codec_par = codec_stream->codecpar; + uint8_t *side_data = NULL; + int side_data_size = 0; av_assert0(apng->prev_packet); + side_data = av_packet_get_side_data(apng->prev_packet, AV_PKT_DATA_NEW_EXTRADATA, &side_data_size); + + if (side_data_size) { + av_freep(&apng->extra_data); + apng->extra_data = av_mallocz(side_data_size + AV_INPUT_BUFFER_PADDING_SIZE); + if (!apng->extra_data) + return AVERROR(ENOMEM); + apng->extra_data_size = side_data_size; + memcpy(apng->extra_data, side_data, apng->extra_data_size); + } + if (apng->frame_number == 0 && !packet) { uint8_t *existing_acTL_chunk; uint8_t *existing_fcTL_chunk; @@ -117,13 +132,13 @@ static void flush_packet(AVFormatContext *format_context, AVPacket *packet) av_log(format_context, AV_LOG_INFO, "Only a single frame so saving as a normal PNG.\n"); // Write normal PNG headers without acTL chunk - existing_acTL_chunk = apng_find_chunk(MKBETAG('a', 'c', 'T', 'L'), codec_par->extradata, codec_par->extradata_size); + existing_acTL_chunk = apng_find_chunk(MKBETAG('a', 'c', 'T', 'L'), apng->extra_data, apng->extra_data_size); if (existing_acTL_chunk) { uint8_t *chunk_after_acTL = existing_acTL_chunk + AV_RB32(existing_acTL_chunk) + 12; - avio_write(io_context, codec_par->extradata, existing_acTL_chunk - codec_par->extradata); - avio_write(io_context, chunk_after_acTL, codec_par->extradata + codec_par->extradata_size - chunk_after_acTL); + avio_write(io_context, apng->extra_data, existing_acTL_chunk - apng->extra_data); + avio_write(io_context, chunk_after_acTL, apng->extra_data + apng->extra_data_size - chunk_after_acTL); } else { - avio_write(io_context, codec_par->extradata, codec_par->extradata_size); + avio_write(io_context, apng->extra_data, apng->extra_data_size); } // Write frame data without fcTL chunk @@ -142,9 +157,9 @@ static void flush_packet(AVFormatContext *format_context, AVPacket *packet) uint8_t *existing_acTL_chunk; // Write normal PNG headers - avio_write(io_context, codec_par->extradata, codec_par->extradata_size); + avio_write(io_context, apng->extra_data, apng->extra_data_size); - existing_acTL_chunk = apng_find_chunk(MKBETAG('a', 'c', 'T', 'L'), codec_par->extradata, codec_par->extradata_size); + existing_acTL_chunk = apng_find_chunk(MKBETAG('a', 'c', 'T', 'L'), apng->extra_data, apng->extra_data_size); if (!existing_acTL_chunk) { uint8_t buf[8]; // Write animation control header @@ -195,11 +210,13 @@ static void flush_packet(AVFormatContext *format_context, AVPacket *packet) av_packet_unref(apng->prev_packet); if (packet) av_copy_packet(apng->prev_packet, packet); + return 0; } static int apng_write_packet(AVFormatContext *format_context, AVPacket *packet) { APNGMuxContext *apng = format_context->priv_data; + int ret; if (!apng->prev_packet) { apng->prev_packet = av_malloc(sizeof(*apng->prev_packet)); @@ -208,7 +225,9 @@ static int apng_write_packet(AVFormatContext *format_context, AVPacket *packet) av_copy_packet(apng->prev_packet, packet); } else { - flush_packet(format_context, packet); + ret = flush_packet(format_context, packet); + if (ret < 0) + return ret; } return 0; @@ -219,10 +238,13 @@ static int apng_write_trailer(AVFormatContext *format_context) APNGMuxContext *apng = format_context->priv_data; AVIOContext *io_context = format_context->pb; uint8_t buf[8]; + int ret; if (apng->prev_packet) { - flush_packet(format_context, NULL); + ret = flush_packet(format_context, NULL); av_freep(&apng->prev_packet); + if (ret < 0) + return ret; } apng_write_chunk(io_context, MKBETAG('I', 'E', 'N', 'D'), NULL, 0); @@ -235,6 +257,9 @@ static int apng_write_trailer(AVFormatContext *format_context) apng_write_chunk(io_context, MKBETAG('a', 'c', 'T', 'L'), buf, 8); } + av_freep(&apng->extra_data); + apng->extra_data = 0; + return 0; }