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