FFmpeg
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 "internal.h"
26 #include "libavutil/avassert.h"
27 #include "libavutil/crc.h"
28 #include "libavutil/intreadwrite.h"
29 #include "libavutil/log.h"
30 #include "libavutil/opt.h"
31 #include "libavcodec/png.h"
32 #include "libavcodec/apng.h"
33 
34 typedef struct APNGMuxContext {
35  AVClass *class;
36 
37  uint32_t plays;
39 
40  uint64_t acTL_offset;
41  uint32_t frame_number;
42 
45 
47 
51 
52 static uint8_t *apng_find_chunk(uint32_t tag, uint8_t *buf, size_t length)
53 {
54  size_t b;
55  for (b = 0; b < length; b += AV_RB32(buf + b) + 12)
56  if (AV_RB32(&buf[b + 4]) == tag)
57  return &buf[b];
58  return NULL;
59 }
60 
61 static void apng_write_chunk(AVIOContext *io_context, uint32_t tag,
62  uint8_t *buf, size_t length)
63 {
64  const AVCRC *crc_table = av_crc_get_table(AV_CRC_32_IEEE_LE);
65  uint32_t crc = ~0U;
66  uint8_t tagbuf[4];
67 
68  av_assert0(crc_table);
69 
70  avio_wb32(io_context, length);
71  AV_WB32(tagbuf, tag);
72  crc = av_crc(crc_table, crc, tagbuf, 4);
73  avio_wb32(io_context, tag);
74  if (length > 0) {
75  crc = av_crc(crc_table, crc, buf, length);
76  avio_write(io_context, buf, length);
77  }
78  avio_wb32(io_context, ~crc);
79 }
80 
81 static int apng_write_header(AVFormatContext *format_context)
82 {
83  APNGMuxContext *apng = format_context->priv_data;
84  AVCodecParameters *par = format_context->streams[0]->codecpar;
85 
86  if (format_context->nb_streams != 1 ||
87  format_context->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO ||
88  format_context->streams[0]->codecpar->codec_id != AV_CODEC_ID_APNG) {
89  av_log(format_context, AV_LOG_ERROR,
90  "APNG muxer supports only a single video APNG stream.\n");
91  return AVERROR(EINVAL);
92  }
93 
94  if (apng->last_delay.num > USHRT_MAX || apng->last_delay.den > USHRT_MAX) {
95  av_reduce(&apng->last_delay.num, &apng->last_delay.den,
96  apng->last_delay.num, apng->last_delay.den, USHRT_MAX);
97  av_log(format_context, AV_LOG_WARNING,
98  "Last frame delay is too precise. Reducing to %d/%d (%f).\n",
99  apng->last_delay.num, apng->last_delay.den, (double)apng->last_delay.num / apng->last_delay.den);
100  }
101 
102  avio_wb64(format_context->pb, PNGSIG);
103  // Remaining headers are written when they are copied from the encoder
104 
105  if (par->extradata_size) {
107  if (!apng->extra_data)
108  return AVERROR(ENOMEM);
109  apng->extra_data_size = par->extradata_size;
110  memcpy(apng->extra_data, par->extradata, par->extradata_size);
111  }
112 
113  return 0;
114 }
115 
116 static int flush_packet(AVFormatContext *format_context, AVPacket *packet)
117 {
118  APNGMuxContext *apng = format_context->priv_data;
119  AVIOContext *io_context = format_context->pb;
120  AVStream *codec_stream = format_context->streams[0];
121  uint8_t *side_data = NULL;
122  int side_data_size = 0;
123 
124  av_assert0(apng->prev_packet);
125 
126  side_data = av_packet_get_side_data(apng->prev_packet, AV_PKT_DATA_NEW_EXTRADATA, &side_data_size);
127 
128  if (side_data_size) {
129  av_freep(&apng->extra_data);
130  apng->extra_data = av_mallocz(side_data_size + AV_INPUT_BUFFER_PADDING_SIZE);
131  if (!apng->extra_data)
132  return AVERROR(ENOMEM);
133  apng->extra_data_size = side_data_size;
134  memcpy(apng->extra_data, side_data, apng->extra_data_size);
135  }
136 
137  if (apng->frame_number == 0 && !packet) {
138  uint8_t *existing_acTL_chunk;
139  uint8_t *existing_fcTL_chunk;
140 
141  av_log(format_context, AV_LOG_INFO, "Only a single frame so saving as a normal PNG.\n");
142 
143  // Write normal PNG headers without acTL chunk
144  existing_acTL_chunk = apng_find_chunk(MKBETAG('a', 'c', 'T', 'L'), apng->extra_data, apng->extra_data_size);
145  if (existing_acTL_chunk) {
146  uint8_t *chunk_after_acTL = existing_acTL_chunk + AV_RB32(existing_acTL_chunk) + 12;
147  avio_write(io_context, apng->extra_data, existing_acTL_chunk - apng->extra_data);
148  avio_write(io_context, chunk_after_acTL, apng->extra_data + apng->extra_data_size - chunk_after_acTL);
149  } else {
150  avio_write(io_context, apng->extra_data, apng->extra_data_size);
151  }
152 
153  // Write frame data without fcTL chunk
154  existing_fcTL_chunk = apng_find_chunk(MKBETAG('f', 'c', 'T', 'L'), apng->prev_packet->data, apng->prev_packet->size);
155  if (existing_fcTL_chunk) {
156  uint8_t *chunk_after_fcTL = existing_fcTL_chunk + AV_RB32(existing_fcTL_chunk) + 12;
157  avio_write(io_context, apng->prev_packet->data, existing_fcTL_chunk - apng->prev_packet->data);
158  avio_write(io_context, chunk_after_fcTL, apng->prev_packet->data + apng->prev_packet->size - chunk_after_fcTL);
159  } else {
160  avio_write(io_context, apng->prev_packet->data, apng->prev_packet->size);
161  }
162  } else {
163  uint8_t *existing_fcTL_chunk;
164 
165  if (apng->frame_number == 0) {
166  uint8_t *existing_acTL_chunk;
167 
168  // Write normal PNG headers
169  avio_write(io_context, apng->extra_data, apng->extra_data_size);
170 
171  existing_acTL_chunk = apng_find_chunk(MKBETAG('a', 'c', 'T', 'L'), apng->extra_data, apng->extra_data_size);
172  if (!existing_acTL_chunk) {
173  uint8_t buf[8];
174  // Write animation control header
175  apng->acTL_offset = avio_tell(io_context);
176  AV_WB32(buf, UINT_MAX); // number of frames (filled in later)
177  AV_WB32(buf + 4, apng->plays);
178  apng_write_chunk(io_context, MKBETAG('a', 'c', 'T', 'L'), buf, 8);
179  }
180  }
181 
182  existing_fcTL_chunk = apng_find_chunk(MKBETAG('f', 'c', 'T', 'L'), apng->prev_packet->data, apng->prev_packet->size);
183  if (existing_fcTL_chunk) {
184  AVRational delay;
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  if (packet) {
192  int64_t delay_num_raw = (packet->dts - apng->prev_packet->dts) * codec_stream->time_base.num;
193  int64_t delay_den_raw = codec_stream->time_base.den;
194  if (!av_reduce(&delay.num, &delay.den, delay_num_raw, delay_den_raw, USHRT_MAX) &&
195  !apng->framerate_warned) {
196  av_log(format_context, AV_LOG_WARNING,
197  "Frame rate is too high or specified too precisely. Unable to copy losslessly.\n");
198  apng->framerate_warned = 1;
199  }
200  } else if (apng->last_delay.num > 0) {
201  delay = apng->last_delay;
202  } else {
203  delay = apng->prev_delay;
204  }
205 
206  // Update frame control header with new delay
207  AV_WB16(existing_fcTL_chunk + 20, delay.num);
208  AV_WB16(existing_fcTL_chunk + 22, delay.den);
209  AV_WB32(existing_fcTL_chunk + 26, ~av_crc(av_crc_get_table(AV_CRC_32_IEEE_LE), ~0U, existing_fcTL_chunk - 4, 26 + 4));
210  }
211  apng->prev_delay = delay;
212  }
213 
214  // Write frame data
215  avio_write(io_context, apng->prev_packet->data, apng->prev_packet->size);
216  }
217  ++apng->frame_number;
218 
220  if (packet)
221  av_packet_ref(apng->prev_packet, packet);
222  return 0;
223 }
224 
225 static int apng_write_packet(AVFormatContext *format_context, AVPacket *packet)
226 {
227  APNGMuxContext *apng = format_context->priv_data;
228  int ret;
229 
230  if (!apng->prev_packet) {
231  apng->prev_packet = av_packet_alloc();
232  if (!apng->prev_packet)
233  return AVERROR(ENOMEM);
234 
235  av_packet_ref(apng->prev_packet, packet);
236  } else {
237  ret = flush_packet(format_context, packet);
238  if (ret < 0)
239  return ret;
240  }
241 
242  return 0;
243 }
244 
245 static int apng_write_trailer(AVFormatContext *format_context)
246 {
247  APNGMuxContext *apng = format_context->priv_data;
248  AVIOContext *io_context = format_context->pb;
249  uint8_t buf[8];
250  int ret;
251 
252  if (apng->prev_packet) {
253  ret = flush_packet(format_context, NULL);
254  if (ret < 0)
255  return ret;
256  }
257 
258  apng_write_chunk(io_context, MKBETAG('I', 'E', 'N', 'D'), NULL, 0);
259 
260  if (apng->acTL_offset && (io_context->seekable & AVIO_SEEKABLE_NORMAL)) {
261  avio_seek(io_context, apng->acTL_offset, SEEK_SET);
262 
263  AV_WB32(buf, apng->frame_number);
264  AV_WB32(buf + 4, apng->plays);
265  apng_write_chunk(io_context, MKBETAG('a', 'c', 'T', 'L'), buf, 8);
266  }
267 
268  return 0;
269 }
270 
272 {
273  APNGMuxContext *apng = s->priv_data;
274 
275  av_packet_free(&apng->prev_packet);
276  av_freep(&apng->extra_data);
277  apng->extra_data_size = 0;
278 }
279 
280 #define OFFSET(x) offsetof(APNGMuxContext, x)
281 #define ENC AV_OPT_FLAG_ENCODING_PARAM
282 static const AVOption options[] = {
283  { "plays", "Number of times to play the output: 0 - infinite loop, 1 - no loop", OFFSET(plays),
284  AV_OPT_TYPE_INT, { .i64 = 1 }, 0, UINT_MAX, ENC },
285  { "final_delay", "Force delay after the last frame", OFFSET(last_delay),
286  AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, USHRT_MAX, ENC },
287  { NULL },
288 };
289 
290 static const AVClass apng_muxer_class = {
291  .class_name = "APNG muxer",
292  .item_name = av_default_item_name,
293  .version = LIBAVUTIL_VERSION_INT,
294  .option = options,
295 };
296 
298  .name = "apng",
299  .long_name = NULL_IF_CONFIG_SMALL("Animated Portable Network Graphics"),
300  .mime_type = "image/png",
301  .extensions = "apng",
302  .priv_data_size = sizeof(APNGMuxContext),
303  .audio_codec = AV_CODEC_ID_NONE,
304  .video_codec = AV_CODEC_ID_APNG,
308  .deinit = apng_deinit,
309  .priv_class = &apng_muxer_class,
311 };
static void write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int unqueue)
Definition: ffmpeg.c:690
void avio_wb64(AVIOContext *s, uint64_t val)
Definition: aviobuf.c:466
static int apng_write_trailer(AVFormatContext *format_context)
Definition: apngenc.c:245
#define NULL
Definition: coverity.c:32
Bytestream IO Context.
Definition: avio.h:161
AVOption.
Definition: opt.h:246
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:182
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition: avcodec.h:3970
int num
Numerator.
Definition: rational.h:59
static const AVClass apng_muxer_class
Definition: apngenc.c:290
int size
Definition: avcodec.h:1483
int framerate_warned
Definition: apngenc.c:46
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition: aviobuf.c:246
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:191
uint64_t acTL_offset
Definition: apngenc.c:40
void * av_mallocz(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:236
uint64_t_TMPL AV_WL64 unsigned int_TMPL AV_WL32 unsigned int_TMPL AV_WL24 unsigned int_TMPL AV_WL16 uint64_t_TMPL AV_WB64 unsigned int_TMPL AV_WB32 unsigned int_TMPL AV_WB24 unsigned int_TMPL AV_RB16
Definition: bytestream.h:87
This struct describes the properties of an encoded stream.
Definition: avcodec.h:3962
Format I/O context.
Definition: avformat.h:1358
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
Definition: log.h:72
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:37
void av_packet_free(AVPacket **pkt)
Free the packet, if the packet is reference counted, it will be unreferenced first.
Definition: avpacket.c:62
uint8_t
AVOptions.
uint64_t_TMPL AV_WL64 unsigned int_TMPL AV_WL32 unsigned int_TMPL AV_WL24 unsigned int_TMPL AV_WL16 uint64_t_TMPL AV_WB64 unsigned int_TMPL AV_RB32
Definition: bytestream.h:87
AVStream ** streams
A list of all streams in the file.
Definition: avformat.h:1426
Public header for CRC hash function implementation.
static int flush_packet(AVFormatContext *format_context, AVPacket *packet)
Definition: apngenc.c:116
uint8_t * data
Definition: avcodec.h:1482
uint32_t tag
Definition: movenc.c:1531
static void apng_write_chunk(AVIOContext *io_context, uint32_t tag, uint8_t *buf, size_t length)
Definition: apngenc.c:61
static av_always_inline int64_t avio_tell(AVIOContext *s)
ftell() equivalent for AVIOContext.
Definition: avio.h:557
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
void avio_write(AVIOContext *s, const unsigned char *buf, int size)
Definition: aviobuf.c:218
#define AV_WB16(p, v)
Definition: intreadwrite.h:405
#define av_log(a,...)
int av_packet_ref(AVPacket *dst, const AVPacket *src)
Setup a new reference to the data described by a given packet.
Definition: avpacket.c:608
uint8_t * extra_data
Definition: apngenc.c:48
#define U(x)
Definition: vp56_arith.h:37
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
uint8_t * av_packet_get_side_data(const AVPacket *pkt, enum AVPacketSideDataType type, int *size)
Get side information from packet.
Definition: avpacket.c:350
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: internal.h:186
AVOutputFormat ff_apng_muxer
Definition: apngenc.c:297
AVPacket * prev_packet
Definition: apngenc.c:43
enum AVMediaType codec_type
General type of the encoded data.
Definition: avcodec.h:3966
#define PNGSIG
Definition: png.h:47
simple assert() macros that are a bit more flexible than ISO C assert().
GLsizei GLsizei * length
Definition: opengl_enc.c:114
AVRational prev_delay
Definition: apngenc.c:44
int extradata_size
Size of the extradata content in bytes.
Definition: avcodec.h:3988
unsigned int nb_streams
Number of elements in AVFormatContext.streams.
Definition: avformat.h:1414
#define b
Definition: input.c:41
int seekable
A combination of AVIO_SEEKABLE_ flags or 0 when the stream is not seekable.
Definition: avio.h:260
AVRational last_delay
Definition: apngenc.c:38
static int write_trailer(AVFormatContext *s1)
Definition: v4l2enc.c:94
const char * name
Definition: avformat.h:505
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:392
#define s(width, name)
Definition: cbs_vp9.c:257
static uint8_t * apng_find_chunk(uint32_t tag, uint8_t *buf, size_t length)
Definition: apngenc.c:52
#define ENC
Definition: apngenc.c:281
if(ret)
Stream structure.
Definition: avformat.h:881
#define AVIO_SEEKABLE_NORMAL
Seeking works like for a local file.
Definition: avio.h:40
The AV_PKT_DATA_NEW_EXTRADATA is used to notify the codec or the format that the extradata buffer was...
Definition: avcodec.h:1204
#define AV_LOG_INFO
Standard information.
Definition: log.h:187
#define OFFSET(x)
Definition: apngenc.c:280
AVIOContext * pb
I/O context.
Definition: avformat.h:1400
void av_packet_unref(AVPacket *pkt)
Wipe the packet.
Definition: avpacket.c:599
void * buf
Definition: avisynth_c.h:766
Describe the class of an AVClass context structure.
Definition: log.h:67
#define AV_WB32(p, v)
Definition: intreadwrite.h:419
static const AVOption options[]
Definition: apngenc.c:282
Rational number (pair of numerator and denominator).
Definition: rational.h:58
uint32_t frame_number
Definition: apngenc.c:41
static int apng_write_packet(AVFormatContext *format_context, AVPacket *packet)
Definition: apngenc.c:225
int extra_data_size
Definition: apngenc.c:49
#define flags(name, subs,...)
Definition: cbs_av1.c:561
const AVCRC * av_crc_get_table(AVCRCId crc_id)
Get an initialized standard CRC table.
Definition: crc.c:374
Main libavformat public API header.
uint32_t plays
Definition: apngenc.c:37
int den
Denominator.
Definition: rational.h:60
#define MKBETAG(a, b, c, d)
Definition: common.h:367
#define AV_INPUT_BUFFER_PADDING_SIZE
Required number of additionally allocated bytes at the end of the input bitstream for decoding...
Definition: avcodec.h:795
#define AVFMT_VARIABLE_FPS
Format allows variable fps.
Definition: avformat.h:470
void * priv_data
Format private data.
Definition: avformat.h:1386
AVPacket * av_packet_alloc(void)
Allocate an AVPacket and set its fields to default values.
Definition: avpacket.c:51
APNG common header.
static void write_header(FFV1Context *f)
Definition: ffv1enc.c:349
uint8_t * extradata
Extra binary data needed for initializing the decoder, codec-dependent.
Definition: avcodec.h:3984
int64_t dts
Decompression timestamp in AVStream->time_base units; the time at which the packet is decompressed...
Definition: avcodec.h:1481
void avio_wb32(AVIOContext *s, unsigned int val)
Definition: aviobuf.c:380
#define av_freep(p)
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:1028
static void apng_deinit(AVFormatContext *s)
Definition: apngenc.c:271
AVRational time_base
This is the fundamental unit of time (in seconds) in terms of which frame timestamps are represented...
Definition: avformat.h:910
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later.That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another.Frame references ownership and permissions
This structure stores compressed data.
Definition: avcodec.h:1459
uint32_t AVCRC
Definition: crc.h:47
static int apng_write_header(AVFormatContext *format_context)
Definition: apngenc.c:81