blob: 378a9b3b368f0ab563b6ad1d439331ad3d56cb5f
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 | |
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) { |
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 | |
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 | |
219 | av_packet_unref(apng->prev_packet); |
220 | if (packet) |
221 | av_copy_packet(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_malloc(sizeof(*apng->prev_packet)); |
232 | if (!apng->prev_packet) |
233 | return AVERROR(ENOMEM); |
234 | |
235 | av_copy_packet(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 | av_freep(&apng->prev_packet); |
255 | if (ret < 0) |
256 | return ret; |
257 | } |
258 | |
259 | apng_write_chunk(io_context, MKBETAG('I', 'E', 'N', 'D'), NULL, 0); |
260 | |
261 | if (apng->acTL_offset && (io_context->seekable & AVIO_SEEKABLE_NORMAL)) { |
262 | avio_seek(io_context, apng->acTL_offset, SEEK_SET); |
263 | |
264 | AV_WB32(buf, apng->frame_number); |
265 | AV_WB32(buf + 4, apng->plays); |
266 | apng_write_chunk(io_context, MKBETAG('a', 'c', 'T', 'L'), buf, 8); |
267 | } |
268 | |
269 | av_freep(&apng->extra_data); |
270 | apng->extra_data = 0; |
271 | |
272 | return 0; |
273 | } |
274 | |
275 | #define OFFSET(x) offsetof(APNGMuxContext, x) |
276 | #define ENC AV_OPT_FLAG_ENCODING_PARAM |
277 | static const AVOption options[] = { |
278 | { "plays", "Number of times to play the output: 0 - infinite loop, 1 - no loop", OFFSET(plays), |
279 | AV_OPT_TYPE_INT, { .i64 = 1 }, 0, UINT_MAX, ENC }, |
280 | { "final_delay", "Force delay after the last frame", OFFSET(last_delay), |
281 | AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, USHRT_MAX, ENC }, |
282 | { NULL }, |
283 | }; |
284 | |
285 | static const AVClass apng_muxer_class = { |
286 | .class_name = "APNG muxer", |
287 | .item_name = av_default_item_name, |
288 | .version = LIBAVUTIL_VERSION_INT, |
289 | .option = options, |
290 | }; |
291 | |
292 | AVOutputFormat ff_apng_muxer = { |
293 | .name = "apng", |
294 | .long_name = NULL_IF_CONFIG_SMALL("Animated Portable Network Graphics"), |
295 | .mime_type = "image/png", |
296 | .extensions = "apng", |
297 | .priv_data_size = sizeof(APNGMuxContext), |
298 | .audio_codec = AV_CODEC_ID_NONE, |
299 | .video_codec = AV_CODEC_ID_APNG, |
300 | .write_header = apng_write_header, |
301 | .write_packet = apng_write_packet, |
302 | .write_trailer = apng_write_trailer, |
303 | .priv_class = &apng_muxer_class, |
304 | .flags = AVFMT_VARIABLE_FPS, |
305 | }; |
306 |