mirror of https://git.ffmpeg.org/ffmpeg.git
apng: use side data to pass extradata to muxer
This fixes creating apng files, which is broken since commit
5ef1959080
.
Reviewed-by: James Almer <jamrial@gmail.com>
Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
This commit is contained in:
parent
bf14393635
commit
940b8908b9
|
@ -68,6 +68,9 @@ typedef struct PNGEncContext {
|
||||||
// APNG
|
// APNG
|
||||||
uint32_t palette_checksum; // Used to ensure a single unique palette
|
uint32_t palette_checksum; // Used to ensure a single unique palette
|
||||||
uint32_t sequence_number;
|
uint32_t sequence_number;
|
||||||
|
int extra_data_updated;
|
||||||
|
uint8_t *extra_data;
|
||||||
|
int extra_data_size;
|
||||||
|
|
||||||
AVFrame *prev_frame;
|
AVFrame *prev_frame;
|
||||||
AVFrame *last_frame;
|
AVFrame *last_frame;
|
||||||
|
@ -870,15 +873,15 @@ static int encode_apng(AVCodecContext *avctx, AVPacket *pkt,
|
||||||
if (!pict)
|
if (!pict)
|
||||||
return AVERROR(EINVAL);
|
return AVERROR(EINVAL);
|
||||||
|
|
||||||
s->bytestream = avctx->extradata = av_malloc(FF_MIN_BUFFER_SIZE);
|
s->bytestream = s->extra_data = av_malloc(FF_MIN_BUFFER_SIZE);
|
||||||
if (!avctx->extradata)
|
if (!s->extra_data)
|
||||||
return AVERROR(ENOMEM);
|
return AVERROR(ENOMEM);
|
||||||
|
|
||||||
ret = encode_headers(avctx, pict);
|
ret = encode_headers(avctx, pict);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
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);
|
s->last_frame_packet = av_malloc(max_packet_size);
|
||||||
if (!s->last_frame_packet)
|
if (!s->last_frame_packet)
|
||||||
|
@ -917,6 +920,13 @@ static int encode_apng(AVCodecContext *avctx, AVPacket *pkt,
|
||||||
if (s->last_frame) {
|
if (s->last_frame) {
|
||||||
uint8_t* last_fctl_chunk_start = pkt->data;
|
uint8_t* last_fctl_chunk_start = pkt->data;
|
||||||
uint8_t buf[26];
|
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 + 0, s->last_frame_fctl.sequence_number);
|
||||||
AV_WB32(buf + 4, s->last_frame_fctl.width);
|
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->last_frame);
|
||||||
av_frame_free(&s->prev_frame);
|
av_frame_free(&s->prev_frame);
|
||||||
av_freep(&s->last_frame_packet);
|
av_freep(&s->last_frame_packet);
|
||||||
|
av_freep(&s->extra_data);
|
||||||
|
s->extra_data_size = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,9 @@ typedef struct APNGMuxContext {
|
||||||
AVRational prev_delay;
|
AVRational prev_delay;
|
||||||
|
|
||||||
int framerate_warned;
|
int framerate_warned;
|
||||||
|
|
||||||
|
uint8_t *extra_data;
|
||||||
|
int extra_data_size;
|
||||||
} APNGMuxContext;
|
} APNGMuxContext;
|
||||||
|
|
||||||
static uint8_t *apng_find_chunk(uint32_t tag, uint8_t *buf, size_t length)
|
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;
|
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;
|
APNGMuxContext *apng = format_context->priv_data;
|
||||||
AVIOContext *io_context = format_context->pb;
|
AVIOContext *io_context = format_context->pb;
|
||||||
AVStream *codec_stream = format_context->streams[0];
|
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);
|
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) {
|
if (apng->frame_number == 0 && !packet) {
|
||||||
uint8_t *existing_acTL_chunk;
|
uint8_t *existing_acTL_chunk;
|
||||||
uint8_t *existing_fcTL_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");
|
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
|
// 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) {
|
if (existing_acTL_chunk) {
|
||||||
uint8_t *chunk_after_acTL = existing_acTL_chunk + AV_RB32(existing_acTL_chunk) + 12;
|
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, apng->extra_data, existing_acTL_chunk - apng->extra_data);
|
||||||
avio_write(io_context, chunk_after_acTL, codec_par->extradata + codec_par->extradata_size - chunk_after_acTL);
|
avio_write(io_context, chunk_after_acTL, apng->extra_data + apng->extra_data_size - chunk_after_acTL);
|
||||||
} else {
|
} 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
|
// Write frame data without fcTL chunk
|
||||||
|
@ -142,9 +157,9 @@ static void flush_packet(AVFormatContext *format_context, AVPacket *packet)
|
||||||
uint8_t *existing_acTL_chunk;
|
uint8_t *existing_acTL_chunk;
|
||||||
|
|
||||||
// Write normal PNG headers
|
// 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) {
|
if (!existing_acTL_chunk) {
|
||||||
uint8_t buf[8];
|
uint8_t buf[8];
|
||||||
// Write animation control header
|
// Write animation control header
|
||||||
|
@ -195,11 +210,13 @@ static void flush_packet(AVFormatContext *format_context, AVPacket *packet)
|
||||||
av_packet_unref(apng->prev_packet);
|
av_packet_unref(apng->prev_packet);
|
||||||
if (packet)
|
if (packet)
|
||||||
av_copy_packet(apng->prev_packet, packet);
|
av_copy_packet(apng->prev_packet, packet);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int apng_write_packet(AVFormatContext *format_context, AVPacket *packet)
|
static int apng_write_packet(AVFormatContext *format_context, AVPacket *packet)
|
||||||
{
|
{
|
||||||
APNGMuxContext *apng = format_context->priv_data;
|
APNGMuxContext *apng = format_context->priv_data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!apng->prev_packet) {
|
if (!apng->prev_packet) {
|
||||||
apng->prev_packet = av_malloc(sizeof(*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);
|
av_copy_packet(apng->prev_packet, packet);
|
||||||
} else {
|
} else {
|
||||||
flush_packet(format_context, packet);
|
ret = flush_packet(format_context, packet);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -219,10 +238,13 @@ static int apng_write_trailer(AVFormatContext *format_context)
|
||||||
APNGMuxContext *apng = format_context->priv_data;
|
APNGMuxContext *apng = format_context->priv_data;
|
||||||
AVIOContext *io_context = format_context->pb;
|
AVIOContext *io_context = format_context->pb;
|
||||||
uint8_t buf[8];
|
uint8_t buf[8];
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (apng->prev_packet) {
|
if (apng->prev_packet) {
|
||||||
flush_packet(format_context, NULL);
|
ret = flush_packet(format_context, NULL);
|
||||||
av_freep(&apng->prev_packet);
|
av_freep(&apng->prev_packet);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
apng_write_chunk(io_context, MKBETAG('I', 'E', 'N', 'D'), NULL, 0);
|
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);
|
apng_write_chunk(io_context, MKBETAG('a', 'c', 'T', 'L'), buf, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
av_freep(&apng->extra_data);
|
||||||
|
apng->extra_data = 0;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue