• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
38     AVRational last_delay;
39 
40     uint64_t acTL_offset;
41     uint32_t frame_number;
42 
43     AVPacket *prev_packet;
44     AVRational prev_delay;
45 
46     int framerate_warned;
47 
48     uint8_t *extra_data;
49     int extra_data_size;
50 } APNGMuxContext;
51 
apng_find_chunk(uint32_t tag,uint8_t * buf,size_t length)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 
apng_write_chunk(AVIOContext * io_context,uint32_t tag,uint8_t * buf,size_t length)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 
apng_write_header(AVFormatContext * format_context)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) {
106         apng->extra_data = av_mallocz(par->extradata_size + AV_INPUT_BUFFER_PADDING_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 
flush_packet(AVFormatContext * format_context,AVPacket * packet)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     buffer_size_t side_data_size;
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 
219     av_packet_unref(apng->prev_packet);
220     if (packet)
221         av_packet_ref(apng->prev_packet, packet);
222     return 0;
223 }
224 
apng_write_packet(AVFormatContext * format_context,AVPacket * packet)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 
apng_write_trailer(AVFormatContext * format_context)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 
apng_deinit(AVFormatContext * s)271 static void apng_deinit(AVFormatContext *s)
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 
297 AVOutputFormat ff_apng_muxer = {
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,
305     .write_header   = apng_write_header,
306     .write_packet   = apng_write_packet,
307     .write_trailer  = apng_write_trailer,
308     .deinit         = apng_deinit,
309     .priv_class     = &apng_muxer_class,
310     .flags          = AVFMT_VARIABLE_FPS,
311 };
312