]> git.sesse.net Git - ffmpeg/blob - libavformat/apngenc.c
avformat/brstm: support little-endian s16 PCM
[ffmpeg] / libavformat / apngenc.c
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 } APNGMuxContext;
48
49 static uint8_t *apng_find_chunk(uint32_t tag, uint8_t *buf, size_t length)
50 {
51     size_t b;
52     for (b = 0; b < length; b += AV_RB32(buf + b) + 12)
53         if (AV_RB32(&buf[b + 4]) == tag)
54             return &buf[b];
55     return NULL;
56 }
57
58 static void apng_write_chunk(AVIOContext *io_context, uint32_t tag,
59                              uint8_t *buf, size_t length)
60 {
61     const AVCRC *crc_table = av_crc_get_table(AV_CRC_32_IEEE_LE);
62     uint32_t crc = ~0U;
63     uint8_t tagbuf[4];
64
65     av_assert0(crc_table);
66
67     avio_wb32(io_context, length);
68     AV_WB32(tagbuf, tag);
69     crc = av_crc(crc_table, crc, tagbuf, 4);
70     avio_wb32(io_context, tag);
71     if (length > 0) {
72         crc = av_crc(crc_table, crc, buf, length);
73         avio_write(io_context, buf, length);
74     }
75     avio_wb32(io_context, ~crc);
76 }
77
78 static int apng_write_header(AVFormatContext *format_context)
79 {
80     APNGMuxContext *apng = format_context->priv_data;
81
82     if (format_context->nb_streams != 1 ||
83         format_context->streams[0]->codec->codec_type != AVMEDIA_TYPE_VIDEO ||
84         format_context->streams[0]->codec->codec_id   != AV_CODEC_ID_APNG) {
85         av_log(format_context, AV_LOG_ERROR,
86                "APNG muxer supports only a single video APNG stream.\n");
87         return AVERROR(EINVAL);
88     }
89
90     if (apng->last_delay.num > USHRT_MAX || apng->last_delay.den > USHRT_MAX) {
91         av_reduce(&apng->last_delay.num, &apng->last_delay.den,
92                   apng->last_delay.num, apng->last_delay.den, USHRT_MAX);
93         av_log(format_context, AV_LOG_WARNING,
94                "Last frame delay is too precise. Reducing to %d/%d (%f).\n",
95                apng->last_delay.num, apng->last_delay.den, (double)apng->last_delay.num / apng->last_delay.den);
96     }
97
98     avio_wb64(format_context->pb, PNGSIG);
99     // Remaining headers are written when they are copied from the encoder
100
101     return 0;
102 }
103
104 static void flush_packet(AVFormatContext *format_context, AVPacket *packet)
105 {
106     APNGMuxContext *apng = format_context->priv_data;
107     AVIOContext *io_context = format_context->pb;
108     AVStream *codec_stream = format_context->streams[0];
109     AVCodecContext *codec_context = codec_stream->codec;
110
111     av_assert0(apng->prev_packet);
112
113     if (apng->frame_number == 0 && !packet) {
114         uint8_t *existing_acTL_chunk;
115         uint8_t *existing_fcTL_chunk;
116
117         av_log(format_context, AV_LOG_INFO, "Only a single frame so saving as a normal PNG.\n");
118
119         // Write normal PNG headers without acTL chunk
120         existing_acTL_chunk = apng_find_chunk(MKBETAG('a', 'c', 'T', 'L'), codec_context->extradata, codec_context->extradata_size);
121         if (existing_acTL_chunk) {
122             uint8_t *chunk_after_acTL = existing_acTL_chunk + AV_RB32(existing_acTL_chunk) + 12;
123             avio_write(io_context, codec_context->extradata, existing_acTL_chunk - codec_context->extradata);
124             avio_write(io_context, chunk_after_acTL, codec_context->extradata + codec_context->extradata_size - chunk_after_acTL);
125         } else {
126             avio_write(io_context, codec_context->extradata, codec_context->extradata_size);
127         }
128
129         // Write frame data without fcTL chunk
130         existing_fcTL_chunk = apng_find_chunk(MKBETAG('f', 'c', 'T', 'L'), apng->prev_packet->data, apng->prev_packet->size);
131         if (existing_fcTL_chunk) {
132             uint8_t *chunk_after_fcTL = existing_fcTL_chunk + AV_RB32(existing_fcTL_chunk) + 12;
133             avio_write(io_context, apng->prev_packet->data, existing_fcTL_chunk - apng->prev_packet->data);
134             avio_write(io_context, chunk_after_fcTL, apng->prev_packet->data + apng->prev_packet->size - chunk_after_fcTL);
135         } else {
136             avio_write(io_context, apng->prev_packet->data, apng->prev_packet->size);
137         }
138     } else {
139         uint8_t *existing_fcTL_chunk;
140
141         if (apng->frame_number == 0) {
142             uint8_t *existing_acTL_chunk;
143
144             // Write normal PNG headers
145             avio_write(io_context, codec_context->extradata, codec_context->extradata_size);
146
147             existing_acTL_chunk = apng_find_chunk(MKBETAG('a', 'c', 'T', 'L'), codec_context->extradata, codec_context->extradata_size);
148             if (!existing_acTL_chunk) {
149                 uint8_t buf[8];
150                 // Write animation control header
151                 apng->acTL_offset = avio_tell(io_context);
152                 AV_WB32(buf, UINT_MAX); // number of frames (filled in later)
153                 AV_WB32(buf + 4, apng->plays);
154                 apng_write_chunk(io_context, MKBETAG('a', 'c', 'T', 'L'), buf, 8);
155             }
156         }
157
158         existing_fcTL_chunk = apng_find_chunk(MKBETAG('f', 'c', 'T', 'L'), apng->prev_packet->data, apng->prev_packet->size);
159         if (existing_fcTL_chunk) {
160             AVRational delay;
161
162             existing_fcTL_chunk += 8;
163             delay.num = AV_RB16(existing_fcTL_chunk + 20);
164             delay.den = AV_RB16(existing_fcTL_chunk + 22);
165
166             if (delay.num == 0 && delay.den == 0) {
167                 if (packet) {
168                     int64_t delay_num_raw = (packet->dts - apng->prev_packet->dts) * codec_stream->time_base.num;
169                     int64_t delay_den_raw = codec_stream->time_base.den;
170                     if (!av_reduce(&delay.num, &delay.den, delay_num_raw, delay_den_raw, USHRT_MAX) &&
171                         !apng->framerate_warned) {
172                         av_log(format_context, AV_LOG_WARNING,
173                                "Frame rate is too high or specified too precisely. Unable to copy losslessly.\n");
174                         apng->framerate_warned = 1;
175                     }
176                 } else if (apng->last_delay.den > 0) {
177                     delay = apng->last_delay;
178                 } else {
179                     delay = apng->prev_delay;
180                 }
181
182                 // Update frame control header with new delay
183                 AV_WB16(existing_fcTL_chunk + 20, delay.num);
184                 AV_WB16(existing_fcTL_chunk + 22, delay.den);
185                 AV_WB32(existing_fcTL_chunk + 26, ~av_crc(av_crc_get_table(AV_CRC_32_IEEE_LE), ~0U, existing_fcTL_chunk - 4, 26 + 4));
186             }
187             apng->prev_delay = delay;
188         }
189
190         // Write frame data
191         avio_write(io_context, apng->prev_packet->data, apng->prev_packet->size);
192     }
193     ++apng->frame_number;
194
195     av_free_packet(apng->prev_packet);
196     if (packet)
197         av_copy_packet(apng->prev_packet, packet);
198 }
199
200 static int apng_write_packet(AVFormatContext *format_context, AVPacket *packet)
201 {
202     APNGMuxContext *apng = format_context->priv_data;
203
204     if (!apng->prev_packet) {
205         apng->prev_packet = av_malloc(sizeof(*apng->prev_packet));
206         if (!apng->prev_packet)
207             return AVERROR(ENOMEM);
208
209         av_copy_packet(apng->prev_packet, packet);
210     } else {
211         flush_packet(format_context, packet);
212     }
213
214     return 0;
215 }
216
217 static int apng_write_trailer(AVFormatContext *format_context)
218 {
219     APNGMuxContext *apng = format_context->priv_data;
220     AVIOContext *io_context = format_context->pb;
221     uint8_t buf[8];
222
223     if (apng->prev_packet) {
224         flush_packet(format_context, NULL);
225         av_freep(&apng->prev_packet);
226     }
227
228     apng_write_chunk(io_context, MKBETAG('I', 'E', 'N', 'D'), NULL, 0);
229
230     if (apng->acTL_offset && io_context->seekable) {
231         avio_seek(io_context, apng->acTL_offset, SEEK_SET);
232
233         AV_WB32(buf, apng->frame_number);
234         AV_WB32(buf + 4, apng->plays);
235         apng_write_chunk(io_context, MKBETAG('a', 'c', 'T', 'L'), buf, 8);
236     }
237
238     return 0;
239 }
240
241 #define OFFSET(x) offsetof(APNGMuxContext, x)
242 #define ENC AV_OPT_FLAG_ENCODING_PARAM
243 static const AVOption options[] = {
244     { "plays", "Number of times to play the output: 0 - infinite loop, 1 - no loop", OFFSET(plays),
245       AV_OPT_TYPE_INT, { .i64 = 1 }, 0, UINT_MAX, ENC },
246     { "final_delay", "Force delay after the last frame", OFFSET(last_delay),
247       AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, USHRT_MAX, ENC },
248     { NULL },
249 };
250
251 static const AVClass apng_muxer_class = {
252     .class_name = "APNG muxer",
253     .item_name  = av_default_item_name,
254     .version    = LIBAVUTIL_VERSION_INT,
255     .option     = options,
256 };
257
258 AVOutputFormat ff_apng_muxer = {
259     .name           = "apng",
260     .long_name      = NULL_IF_CONFIG_SMALL("Animated Portable Network Graphics"),
261     .mime_type      = "image/png",
262     .extensions     = "apng",
263     .priv_data_size = sizeof(APNGMuxContext),
264     .audio_codec    = AV_CODEC_ID_NONE,
265     .video_codec    = AV_CODEC_ID_APNG,
266     .write_header   = apng_write_header,
267     .write_packet   = apng_write_packet,
268     .write_trailer  = apng_write_trailer,
269     .priv_class     = &apng_muxer_class,
270     .flags          = AVFMT_VARIABLE_FPS,
271 };