FFmpeg
Loading...
Searching...
No Matches
apngenc.c
Go to the documentation of this file.
1/*
2 * APNG muxer
3 * Copyright (c) 2015 Donny Yang
4 *
5 * first version by Donny Yang <work@kota.moe>
6 *
7 * This file is part of FFmpeg.
8 *
9 * FFmpeg is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * FFmpeg is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with FFmpeg; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24#include "avformat.h"
25#include "mux.h"
26#include "libavutil/avassert.h"
27#include "libavutil/crc.h"
29#include "libavutil/log.h"
30#include "libavutil/mem.h"
31#include "libavutil/opt.h"
32#include "libavcodec/apng.h"
33#include "libavcodec/png.h"
34
52
53static const uint8_t *apng_find_chunk(uint32_t tag, const uint8_t *buf,
54 size_t length)
55{
56 size_t b;
57 for (b = 0; AV_RB32(buf + b) + 12ULL <= length - b; b += AV_RB32(buf + b) + 12ULL)
58 if (AV_RB32(&buf[b + 4]) == tag)
59 return &buf[b];
60 return NULL;
61}
62
63static void apng_write_chunk(AVIOContext *io_context, uint32_t tag,
64 uint8_t *buf, size_t length)
65{
66 const AVCRC *crc_table = av_crc_get_table(AV_CRC_32_IEEE_LE);
67 uint32_t crc = ~0U;
68 uint8_t tagbuf[4];
69
70 av_assert0(crc_table);
71
72 avio_wb32(io_context, length);
73 AV_WB32(tagbuf, tag);
74 crc = av_crc(crc_table, crc, tagbuf, 4);
75 avio_wb32(io_context, tag);
76 if (length > 0) {
77 crc = av_crc(crc_table, crc, buf, length);
78 avio_write(io_context, buf, length);
79 }
80 avio_wb32(io_context, ~crc);
81}
82
83static int apng_write_header(AVFormatContext *format_context)
84{
85 APNGMuxContext *apng = format_context->priv_data;
86 AVCodecParameters *par = format_context->streams[0]->codecpar;
87
88 if (apng->last_delay.num > UINT16_MAX || apng->last_delay.den > UINT16_MAX) {
89 av_reduce(&apng->last_delay.num, &apng->last_delay.den,
90 apng->last_delay.num, apng->last_delay.den, UINT16_MAX);
91 av_log(format_context, AV_LOG_WARNING,
92 "Last frame delay is too precise. Reducing to %d/%d (%f).\n",
93 apng->last_delay.num, apng->last_delay.den, (double)apng->last_delay.num / apng->last_delay.den);
94 }
95
96 avio_wb64(format_context->pb, PNGSIG);
97 // Remaining headers are written when they are copied from the encoder
98
99 if (par->extradata_size) {
101 if (!apng->extra_data)
102 return AVERROR(ENOMEM);
103 apng->extra_data_size = par->extradata_size;
104 memcpy(apng->extra_data, par->extradata, par->extradata_size);
105 }
106
107 return 0;
108}
109
110static int flush_packet(AVFormatContext *format_context, AVPacket *packet)
111{
112 APNGMuxContext *apng = format_context->priv_data;
113 AVIOContext *io_context = format_context->pb;
114 AVStream *codec_stream = format_context->streams[0];
115 uint8_t *side_data = NULL;
116 size_t side_data_size;
117
118 av_assert0(apng->prev_packet);
119
120 side_data = av_packet_get_side_data(apng->prev_packet, AV_PKT_DATA_NEW_EXTRADATA, &side_data_size);
121
122 if (side_data_size) {
123 av_freep(&apng->extra_data);
124 apng->extra_data = av_mallocz(side_data_size + AV_INPUT_BUFFER_PADDING_SIZE);
125 if (!apng->extra_data)
126 return AVERROR(ENOMEM);
127 apng->extra_data_size = side_data_size;
128 memcpy(apng->extra_data, side_data, apng->extra_data_size);
129 }
130
131 if (apng->frame_number == 0 && !packet) {
132 const uint8_t *existing_acTL_chunk;
133 const uint8_t *existing_fcTL_chunk;
134
135 av_log(format_context, AV_LOG_INFO, "Only a single frame so saving as a normal PNG.\n");
136
137 // Write normal PNG headers without acTL chunk
138 existing_acTL_chunk = apng_find_chunk(MKBETAG('a', 'c', 'T', 'L'), apng->extra_data, apng->extra_data_size);
139 if (existing_acTL_chunk) {
140 const uint8_t *chunk_after_acTL = existing_acTL_chunk + AV_RB32(existing_acTL_chunk) + 12;
141 avio_write(io_context, apng->extra_data, existing_acTL_chunk - apng->extra_data);
142 avio_write(io_context, chunk_after_acTL, apng->extra_data + apng->extra_data_size - chunk_after_acTL);
143 } else {
144 avio_write(io_context, apng->extra_data, apng->extra_data_size);
145 }
146
147 // Write frame data without fcTL chunk
148 existing_fcTL_chunk = apng_find_chunk(MKBETAG('f', 'c', 'T', 'L'), apng->prev_packet->data, apng->prev_packet->size);
149 if (existing_fcTL_chunk) {
150 const uint8_t *chunk_after_fcTL = existing_fcTL_chunk + AV_RB32(existing_fcTL_chunk) + 12;
151 avio_write(io_context, apng->prev_packet->data, existing_fcTL_chunk - apng->prev_packet->data);
152 avio_write(io_context, chunk_after_fcTL, apng->prev_packet->data + apng->prev_packet->size - chunk_after_fcTL);
153 } else {
154 avio_write(io_context, apng->prev_packet->data, apng->prev_packet->size);
155 }
156 } else {
157 const uint8_t *data, *data_end;
158 const uint8_t *existing_fcTL_chunk;
159
160 if (apng->frame_number == 0) {
161 const uint8_t *existing_acTL_chunk;
162
163 // Write normal PNG headers
164 avio_write(io_context, apng->extra_data, apng->extra_data_size);
165
166 existing_acTL_chunk = apng_find_chunk(MKBETAG('a', 'c', 'T', 'L'), apng->extra_data, apng->extra_data_size);
167 if (!existing_acTL_chunk) {
168 uint8_t buf[8];
169 // Write animation control header
170 apng->acTL_offset = avio_tell(io_context);
171 AV_WB32(buf, UINT_MAX); // number of frames (filled in later)
172 AV_WB32(buf + 4, apng->plays);
173 apng_write_chunk(io_context, MKBETAG('a', 'c', 'T', 'L'), buf, 8);
174 }
175 }
176
177 data = apng->prev_packet->data;
178 data_end = data + apng->prev_packet->size;
179 existing_fcTL_chunk = apng_find_chunk(MKBETAG('f', 'c', 'T', 'L'), apng->prev_packet->data, apng->prev_packet->size);
180 if (existing_fcTL_chunk) {
181 AVRational delay;
182
183 if (AV_RB32(existing_fcTL_chunk) != APNG_FCTL_CHUNK_SIZE)
184 return AVERROR_INVALIDDATA;
185
186 existing_fcTL_chunk += 8;
187 delay.num = AV_RB16(existing_fcTL_chunk + 20);
188 delay.den = AV_RB16(existing_fcTL_chunk + 22);
189
190 if (delay.num == 0 && delay.den == 0) {
191 uint8_t new_fcTL_chunk[APNG_FCTL_CHUNK_SIZE];
192
193 if (packet) {
194 int64_t delay_num_raw = (packet->dts - apng->prev_packet->dts) * codec_stream->time_base.num;
195 int64_t delay_den_raw = codec_stream->time_base.den;
196 if (!av_reduce(&delay.num, &delay.den, delay_num_raw, delay_den_raw, UINT16_MAX) &&
197 !apng->framerate_warned) {
198 av_log(format_context, AV_LOG_WARNING,
199 "Frame rate is too high or specified too precisely. Unable to copy losslessly.\n");
200 apng->framerate_warned = 1;
201 }
202 } else if (apng->last_delay.num > 0) {
203 delay = apng->last_delay;
204 } else {
205 delay = apng->prev_delay;
206 }
207
208 avio_write(io_context, data, (existing_fcTL_chunk - 8) - data);
209 data = existing_fcTL_chunk + APNG_FCTL_CHUNK_SIZE + 4 /* CRC-32 */;
210 // Update frame control header with new delay
211 memcpy(new_fcTL_chunk, existing_fcTL_chunk, sizeof(new_fcTL_chunk));
212 AV_WB16(new_fcTL_chunk + 20, delay.num);
213 AV_WB16(new_fcTL_chunk + 22, delay.den);
214 apng_write_chunk(io_context, MKBETAG('f', 'c', 'T', 'L'),
215 new_fcTL_chunk, sizeof(new_fcTL_chunk));
216 }
217 apng->prev_delay = delay;
218 }
219
220 // Write frame data
221 avio_write(io_context, data, data_end - data);
222 }
223 ++apng->frame_number;
224
226 if (packet)
227 av_packet_ref(apng->prev_packet, packet);
228 return 0;
229}
230
231static int apng_write_packet(AVFormatContext *format_context, AVPacket *packet)
232{
233 APNGMuxContext *apng = format_context->priv_data;
234 int ret;
235
236 if (!apng->prev_packet) {
238 if (!apng->prev_packet)
239 return AVERROR(ENOMEM);
240
241 av_packet_ref(apng->prev_packet, packet);
242 } else {
243 ret = flush_packet(format_context, packet);
244 if (ret < 0)
245 return ret;
246 }
247
248 return 0;
249}
250
251static int apng_write_trailer(AVFormatContext *format_context)
252{
253 APNGMuxContext *apng = format_context->priv_data;
254 AVIOContext *io_context = format_context->pb;
255 uint8_t buf[8];
256 int ret;
257
258 if (apng->prev_packet) {
259 ret = flush_packet(format_context, NULL);
260 if (ret < 0)
261 return ret;
262 }
263
264 apng_write_chunk(io_context, MKBETAG('I', 'E', 'N', 'D'), NULL, 0);
265
266 if (apng->acTL_offset && (io_context->seekable & AVIO_SEEKABLE_NORMAL)) {
267 avio_seek(io_context, apng->acTL_offset, SEEK_SET);
268
269 AV_WB32(buf, apng->frame_number);
270 AV_WB32(buf + 4, apng->plays);
271 apng_write_chunk(io_context, MKBETAG('a', 'c', 'T', 'L'), buf, 8);
272 }
273
274 return 0;
275}
276
278{
279 APNGMuxContext *apng = s->priv_data;
280
282 av_freep(&apng->extra_data);
283 apng->extra_data_size = 0;
284}
285
286#define OFFSET(x) offsetof(APNGMuxContext, x)
287#define ENC AV_OPT_FLAG_ENCODING_PARAM
288static const AVOption options[] = {
289 { "plays", "Number of times to play the output: 0 - infinite loop, 1 - no loop", OFFSET(plays),
290 AV_OPT_TYPE_INT, { .i64 = 1 }, 0, UINT16_MAX, ENC },
291 { "final_delay", "Force delay after the last frame", OFFSET(last_delay),
292 AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, UINT16_MAX, ENC },
293 { NULL },
294};
295
296static const AVClass apng_muxer_class = {
297 .class_name = "APNG muxer",
298 .item_name = av_default_item_name,
299 .version = LIBAVUTIL_VERSION_INT,
300 .option = options,
301};
302
304 .p.name = "apng",
305 .p.long_name = NULL_IF_CONFIG_SMALL("Animated Portable Network Graphics"),
306 .p.mime_type = "image/png",
307 .p.extensions = "apng",
308 .priv_data_size = sizeof(APNGMuxContext),
309 .p.audio_codec = AV_CODEC_ID_NONE,
310 .p.video_codec = AV_CODEC_ID_APNG,
311 .p.subtitle_codec = AV_CODEC_ID_NONE,
312 .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH |
314 .write_header = apng_write_header,
315 .write_packet = apng_write_packet,
316 .write_trailer = apng_write_trailer,
317 .deinit = apng_deinit,
318 .p.priv_class = &apng_muxer_class,
319 .p.flags = AVFMT_VARIABLE_FPS,
320};
const FFOutputFormat ff_apng_muxer
Definition apngenc.c:303
APNG common header.
#define APNG_FCTL_CHUNK_SIZE
Definition apng.h:42
static void apng_deinit(AVFormatContext *s)
Definition apngenc.c:277
static int apng_write_trailer(AVFormatContext *format_context)
Definition apngenc.c:251
static int apng_write_packet(AVFormatContext *format_context, AVPacket *packet)
Definition apngenc.c:231
static const uint8_t * apng_find_chunk(uint32_t tag, const uint8_t *buf, size_t length)
Definition apngenc.c:53
static const AVClass apng_muxer_class
Definition apngenc.c:296
static int apng_write_header(AVFormatContext *format_context)
Definition apngenc.c:83
#define OFFSET(x)
Definition apngenc.c:286
static void apng_write_chunk(AVIOContext *io_context, uint32_t tag, uint8_t *buf, size_t length)
Definition apngenc.c:63
static int flush_packet(AVFormatContext *format_context, AVPacket *packet)
Definition apngenc.c:110
#define U(x)
Definition vpx_arith.h:37
simple assert() macros that are a bit more flexible than ISO C assert().
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition avassert.h:42
Main libavformat public API header.
#define AVFMT_VARIABLE_FPS
Format allows variable fps.
Definition avformat.h:501
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition aviobuf.c:236
#define AVIO_SEEKABLE_NORMAL
Seeking works like for a local file.
Definition avio.h:41
void avio_wb32(AVIOContext *s, unsigned int val)
Definition aviobuf.c:368
static av_always_inline int64_t avio_tell(AVIOContext *s)
ftell() equivalent for AVIOContext.
Definition avio.h:494
void avio_wb64(AVIOContext *s, uint64_t val)
Definition aviobuf.c:434
void avio_write(AVIOContext *s, const unsigned char *buf, int size)
Definition aviobuf.c:206
#define ENC
Definition caca.c:198
#define s(width, name)
Definition cbs_vp9.c:198
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
Public header for CRC hash function implementation.
@ AV_OPT_TYPE_RATIONAL
Underlying C type is AVRational.
Definition opt.h:279
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
@ AV_CODEC_ID_APNG
Definition codec_id.h:260
@ AV_CODEC_ID_NONE
Definition codec_id.h:48
#define AV_INPUT_BUFFER_PADDING_SIZE
Required number of additionally allocated bytes at the end of the input bitstream for decoding.
Definition defs.h:40
@ AV_PKT_DATA_NEW_EXTRADATA
The AV_PKT_DATA_NEW_EXTRADATA is used to notify the codec or the format that the extradata buffer was...
Definition packet.h:56
void av_packet_free(AVPacket **pkt)
Free the packet, if the packet is reference counted, it will be unreferenced first.
Definition packet.c:74
void av_packet_unref(AVPacket *pkt)
Wipe the packet.
Definition packet.c:434
uint8_t * av_packet_get_side_data(const AVPacket *pkt, enum AVPacketSideDataType type, size_t *size)
Get side information from packet.
Definition packet.c:252
AVPacket * av_packet_alloc(void)
Allocate an AVPacket and set its fields to default values.
Definition packet.c:63
int av_packet_ref(AVPacket *dst, const AVPacket *src)
Setup a new reference to the data described by a given packet.
Definition packet.c:442
const AVCRC * av_crc_get_table(AVCRCId crc_id)
Get an initialized standard CRC table.
Definition crc.c:389
uint32_t AVCRC
Definition crc.h:46
uint32_t av_crc(const AVCRC *ctx, uint32_t crc, const uint8_t *buffer, size_t length)
Calculate the CRC of a block.
Definition crc.c:421
@ AV_CRC_32_IEEE_LE
Definition crc.h:53
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition error.h:61
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition log.h:216
#define AV_LOG_INFO
Standard information.
Definition log.h:221
const char * av_default_item_name(void *ptr)
Return the context name.
Definition log.c:241
int av_reduce(int *dst_num, int *dst_den, int64_t num, int64_t den, int64_t max)
Reduce a fraction.
Definition rational.c:35
#define LIBAVUTIL_VERSION_INT
Definition version.h:85
#define b
Definition input.c:43
#define AV_WB32(p, v)
#define AV_RB32(p)
#define AV_WB16(p, v)
#define AV_RB16(p)
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
#define MKBETAG(a, b, c, d)
Definition macros.h:56
Memory handling functions.
uint32_t tag
Definition movenc.c:2073
#define FF_OFMT_FLAG_MAX_ONE_OF_EACH
If this flag is set, it indicates that for each codec type whose corresponding default codec (i....
Definition mux.h:50
#define FF_OFMT_FLAG_ONLY_DEFAULT_CODECS
If this flag is set, then the only permitted audio/video/subtitle codec ids are AVOutputFormat....
Definition mux.h:59
const char data[16]
Definition mxf.c:149
AVOptions.
#define PNGSIG
Definition png.h:49
int extra_data_size
Definition apngenc.c:50
uint32_t plays
Definition apngenc.c:38
int framerate_warned
Definition apngenc.c:47
AVRational last_delay
Definition apngenc.c:39
AVPacket * prev_packet
Definition apngenc.c:44
uint32_t frame_number
Definition apngenc.c:42
uint8_t * extra_data
Definition apngenc.c:49
uint64_t acTL_offset
Definition apngenc.c:41
AVRational prev_delay
Definition apngenc.c:45
Describe the class of an AVClass context structure.
Definition log.h:76
This struct describes the properties of an encoded stream.
Definition codec_par.h:49
int extradata_size
Size of the extradata content in bytes.
Definition codec_par.h:75
uint8_t * extradata
Extra binary data needed for initializing the decoder, codec-dependent.
Definition codec_par.h:71
Format I/O context.
Definition avformat.h:1333
AVIOContext * pb
I/O context.
Definition avformat.h:1375
void * priv_data
Format private data.
Definition avformat.h:1361
AVStream ** streams
A list of all streams in the file.
Definition avformat.h:1401
Bytestream IO Context.
Definition avio.h:160
int seekable
A combination of AVIO_SEEKABLE_ flags or 0 when the stream is not seekable.
Definition avio.h:261
AVOption.
Definition opt.h:428
This structure stores compressed data.
Definition packet.h:580
int size
Definition packet.h:604
int64_t dts
Decompression timestamp in AVStream->time_base units; the time at which the packet is decompressed.
Definition packet.h:602
uint8_t * data
Definition packet.h:603
Rational number (pair of numerator and denominator).
Definition rational.h:58
int num
Numerator.
Definition rational.h:59
int den
Denominator.
Definition rational.h:60
Stream structure.
Definition avformat.h:766
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition avformat.h:789
AVRational time_base
This is the fundamental unit of time (in seconds) in terms of which frame timestamps are represented.
Definition avformat.h:805
#define av_mallocz(s)
#define av_freep(p)
#define av_log(a,...)