* more warning cleanups
[ffmpeg.git] / libav / gif.c
1 /*
2  * Animated GIF encoder
3  * Copyright (c) 2000 Gerard Lantau.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20 /*
21  * First version by Francois Revol revol@free.fr
22  *
23  * Features and limitations:
24  * - currently no compression is performed,
25  *   in fact the size of the data is 9/8 the size of the image in 8bpp
26  * - uses only a global standard palette
27  * - tested with IE 5.0, Opera for BeOS, NetPositive (BeOS), and Mozilla (BeOS).
28  *
29  * Reference documents:
30  * http://www.goice.co.jp/member/mo/formats/gif.html
31  * http://astronomy.swin.edu.au/pbourke/dataformats/gif/
32  * http://www.dcs.ed.ac.uk/home/mxr/gfx/2d/GIF89a.txt
33  *
34  * this url claims to have an LZW algorithm not covered by Unisys patent:
35  * http://www.msg.net/utility/whirlgif/gifencod.html
36  * could help reduce the size of the files _a lot_...
37  * some sites mentions an RLE type compression also.
38  */
39
40 #include "avformat.h"
41
42 /* bitstream minipacket size */
43 #define GIF_CHUNKS 100
44
45 /* slows down the decoding (and some browsers doesn't like it) */
46 /* #define GIF_ADD_APP_HEADER */
47
48 typedef struct {
49     unsigned char r;
50     unsigned char g;
51     unsigned char b;
52 } rgb_triplet;
53
54 /* we use the standard 216 color palette */
55
56 /* this script was used to create the palette:
57  * for r in 00 33 66 99 cc ff; do for g in 00 33 66 99 cc ff; do echo -n "    "; for b in 00 33 66 99 cc ff; do 
58  *   echo -n "{ 0x$r, 0x$g, 0x$b }, "; done; echo ""; done; done
59  */
60
61 const rgb_triplet gif_clut[216] = {
62     { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x33 }, { 0x00, 0x00, 0x66 }, { 0x00, 0x00, 0x99 }, { 0x00, 0x00, 0xcc }, { 0x00, 0x00, 0xff },
63     { 0x00, 0x33, 0x00 }, { 0x00, 0x33, 0x33 }, { 0x00, 0x33, 0x66 }, { 0x00, 0x33, 0x99 }, { 0x00, 0x33, 0xcc }, { 0x00, 0x33, 0xff },
64     { 0x00, 0x66, 0x00 }, { 0x00, 0x66, 0x33 }, { 0x00, 0x66, 0x66 }, { 0x00, 0x66, 0x99 }, { 0x00, 0x66, 0xcc }, { 0x00, 0x66, 0xff },
65     { 0x00, 0x99, 0x00 }, { 0x00, 0x99, 0x33 }, { 0x00, 0x99, 0x66 }, { 0x00, 0x99, 0x99 }, { 0x00, 0x99, 0xcc }, { 0x00, 0x99, 0xff },
66     { 0x00, 0xcc, 0x00 }, { 0x00, 0xcc, 0x33 }, { 0x00, 0xcc, 0x66 }, { 0x00, 0xcc, 0x99 }, { 0x00, 0xcc, 0xcc }, { 0x00, 0xcc, 0xff },
67     { 0x00, 0xff, 0x00 }, { 0x00, 0xff, 0x33 }, { 0x00, 0xff, 0x66 }, { 0x00, 0xff, 0x99 }, { 0x00, 0xff, 0xcc }, { 0x00, 0xff, 0xff },
68     { 0x33, 0x00, 0x00 }, { 0x33, 0x00, 0x33 }, { 0x33, 0x00, 0x66 }, { 0x33, 0x00, 0x99 }, { 0x33, 0x00, 0xcc }, { 0x33, 0x00, 0xff },
69     { 0x33, 0x33, 0x00 }, { 0x33, 0x33, 0x33 }, { 0x33, 0x33, 0x66 }, { 0x33, 0x33, 0x99 }, { 0x33, 0x33, 0xcc }, { 0x33, 0x33, 0xff },
70     { 0x33, 0x66, 0x00 }, { 0x33, 0x66, 0x33 }, { 0x33, 0x66, 0x66 }, { 0x33, 0x66, 0x99 }, { 0x33, 0x66, 0xcc }, { 0x33, 0x66, 0xff },
71     { 0x33, 0x99, 0x00 }, { 0x33, 0x99, 0x33 }, { 0x33, 0x99, 0x66 }, { 0x33, 0x99, 0x99 }, { 0x33, 0x99, 0xcc }, { 0x33, 0x99, 0xff },
72     { 0x33, 0xcc, 0x00 }, { 0x33, 0xcc, 0x33 }, { 0x33, 0xcc, 0x66 }, { 0x33, 0xcc, 0x99 }, { 0x33, 0xcc, 0xcc }, { 0x33, 0xcc, 0xff },
73     { 0x33, 0xff, 0x00 }, { 0x33, 0xff, 0x33 }, { 0x33, 0xff, 0x66 }, { 0x33, 0xff, 0x99 }, { 0x33, 0xff, 0xcc }, { 0x33, 0xff, 0xff },
74     { 0x66, 0x00, 0x00 }, { 0x66, 0x00, 0x33 }, { 0x66, 0x00, 0x66 }, { 0x66, 0x00, 0x99 }, { 0x66, 0x00, 0xcc }, { 0x66, 0x00, 0xff },
75     { 0x66, 0x33, 0x00 }, { 0x66, 0x33, 0x33 }, { 0x66, 0x33, 0x66 }, { 0x66, 0x33, 0x99 }, { 0x66, 0x33, 0xcc }, { 0x66, 0x33, 0xff },
76     { 0x66, 0x66, 0x00 }, { 0x66, 0x66, 0x33 }, { 0x66, 0x66, 0x66 }, { 0x66, 0x66, 0x99 }, { 0x66, 0x66, 0xcc }, { 0x66, 0x66, 0xff },
77     { 0x66, 0x99, 0x00 }, { 0x66, 0x99, 0x33 }, { 0x66, 0x99, 0x66 }, { 0x66, 0x99, 0x99 }, { 0x66, 0x99, 0xcc }, { 0x66, 0x99, 0xff },
78     { 0x66, 0xcc, 0x00 }, { 0x66, 0xcc, 0x33 }, { 0x66, 0xcc, 0x66 }, { 0x66, 0xcc, 0x99 }, { 0x66, 0xcc, 0xcc }, { 0x66, 0xcc, 0xff },
79     { 0x66, 0xff, 0x00 }, { 0x66, 0xff, 0x33 }, { 0x66, 0xff, 0x66 }, { 0x66, 0xff, 0x99 }, { 0x66, 0xff, 0xcc }, { 0x66, 0xff, 0xff },
80     { 0x99, 0x00, 0x00 }, { 0x99, 0x00, 0x33 }, { 0x99, 0x00, 0x66 }, { 0x99, 0x00, 0x99 }, { 0x99, 0x00, 0xcc }, { 0x99, 0x00, 0xff },
81     { 0x99, 0x33, 0x00 }, { 0x99, 0x33, 0x33 }, { 0x99, 0x33, 0x66 }, { 0x99, 0x33, 0x99 }, { 0x99, 0x33, 0xcc }, { 0x99, 0x33, 0xff },
82     { 0x99, 0x66, 0x00 }, { 0x99, 0x66, 0x33 }, { 0x99, 0x66, 0x66 }, { 0x99, 0x66, 0x99 }, { 0x99, 0x66, 0xcc }, { 0x99, 0x66, 0xff },
83     { 0x99, 0x99, 0x00 }, { 0x99, 0x99, 0x33 }, { 0x99, 0x99, 0x66 }, { 0x99, 0x99, 0x99 }, { 0x99, 0x99, 0xcc }, { 0x99, 0x99, 0xff },
84     { 0x99, 0xcc, 0x00 }, { 0x99, 0xcc, 0x33 }, { 0x99, 0xcc, 0x66 }, { 0x99, 0xcc, 0x99 }, { 0x99, 0xcc, 0xcc }, { 0x99, 0xcc, 0xff },
85     { 0x99, 0xff, 0x00 }, { 0x99, 0xff, 0x33 }, { 0x99, 0xff, 0x66 }, { 0x99, 0xff, 0x99 }, { 0x99, 0xff, 0xcc }, { 0x99, 0xff, 0xff },
86     { 0xcc, 0x00, 0x00 }, { 0xcc, 0x00, 0x33 }, { 0xcc, 0x00, 0x66 }, { 0xcc, 0x00, 0x99 }, { 0xcc, 0x00, 0xcc }, { 0xcc, 0x00, 0xff },
87     { 0xcc, 0x33, 0x00 }, { 0xcc, 0x33, 0x33 }, { 0xcc, 0x33, 0x66 }, { 0xcc, 0x33, 0x99 }, { 0xcc, 0x33, 0xcc }, { 0xcc, 0x33, 0xff },
88     { 0xcc, 0x66, 0x00 }, { 0xcc, 0x66, 0x33 }, { 0xcc, 0x66, 0x66 }, { 0xcc, 0x66, 0x99 }, { 0xcc, 0x66, 0xcc }, { 0xcc, 0x66, 0xff },
89     { 0xcc, 0x99, 0x00 }, { 0xcc, 0x99, 0x33 }, { 0xcc, 0x99, 0x66 }, { 0xcc, 0x99, 0x99 }, { 0xcc, 0x99, 0xcc }, { 0xcc, 0x99, 0xff },
90     { 0xcc, 0xcc, 0x00 }, { 0xcc, 0xcc, 0x33 }, { 0xcc, 0xcc, 0x66 }, { 0xcc, 0xcc, 0x99 }, { 0xcc, 0xcc, 0xcc }, { 0xcc, 0xcc, 0xff },
91     { 0xcc, 0xff, 0x00 }, { 0xcc, 0xff, 0x33 }, { 0xcc, 0xff, 0x66 }, { 0xcc, 0xff, 0x99 }, { 0xcc, 0xff, 0xcc }, { 0xcc, 0xff, 0xff },
92     { 0xff, 0x00, 0x00 }, { 0xff, 0x00, 0x33 }, { 0xff, 0x00, 0x66 }, { 0xff, 0x00, 0x99 }, { 0xff, 0x00, 0xcc }, { 0xff, 0x00, 0xff },
93     { 0xff, 0x33, 0x00 }, { 0xff, 0x33, 0x33 }, { 0xff, 0x33, 0x66 }, { 0xff, 0x33, 0x99 }, { 0xff, 0x33, 0xcc }, { 0xff, 0x33, 0xff },
94     { 0xff, 0x66, 0x00 }, { 0xff, 0x66, 0x33 }, { 0xff, 0x66, 0x66 }, { 0xff, 0x66, 0x99 }, { 0xff, 0x66, 0xcc }, { 0xff, 0x66, 0xff },
95     { 0xff, 0x99, 0x00 }, { 0xff, 0x99, 0x33 }, { 0xff, 0x99, 0x66 }, { 0xff, 0x99, 0x99 }, { 0xff, 0x99, 0xcc }, { 0xff, 0x99, 0xff },
96     { 0xff, 0xcc, 0x00 }, { 0xff, 0xcc, 0x33 }, { 0xff, 0xcc, 0x66 }, { 0xff, 0xcc, 0x99 }, { 0xff, 0xcc, 0xcc }, { 0xff, 0xcc, 0xff },
97     { 0xff, 0xff, 0x00 }, { 0xff, 0xff, 0x33 }, { 0xff, 0xff, 0x66 }, { 0xff, 0xff, 0x99 }, { 0xff, 0xff, 0xcc }, { 0xff, 0xff, 0xff },
98 };
99
100 /* The GIF format uses reversed order for bitstreams... */
101 /* at least they don't use PDP_ENDIAN :) */
102 /* so we 'extend' PutBitContext. hmmm, OOP :) */
103 /* seems this thing changed slightly since I wrote it... */
104
105 #ifdef ALT_BITSTREAM_WRITER
106 # error no ALT_BITSTREAM_WRITER support for now
107 #endif
108
109 void init_put_bits_rev(PutBitContext *s,
110                    UINT8 *buffer, int buffer_size,
111                    void *opaque,
112                    void (*write_data)(void *, UINT8 *, int))
113 {
114     init_put_bits(s, buffer, buffer_size, opaque, write_data);
115 }
116
117 void put_bits_rev(PutBitContext *s, int n, unsigned int value)
118 {
119     unsigned int bit_buf;
120     int bit_cnt;
121
122 #ifdef STATS
123     st_out_bit_counts[st_current_index] += n;
124 #endif
125     //    printf("put_bits=%d %x\n", n, value);
126     assert(n == 32 || value < (1U << n));
127
128     bit_buf = s->bit_buf;
129     bit_cnt = 32 - s->bit_left; /* XXX:lazyness... was = s->bit_cnt; */
130
131     //    printf("n=%d value=%x cnt=%d buf=%x\n", n, value, bit_cnt, bit_buf);
132     /* XXX: optimize */
133     if (n < (32-bit_cnt)) {
134         bit_buf |= value << (bit_cnt);
135         bit_cnt+=n;
136     } else {
137         bit_buf |= value << (bit_cnt);
138         
139         *s->buf_ptr = bit_buf & 0xff;
140         s->buf_ptr[1] = (bit_buf >> 8) & 0xff;
141         s->buf_ptr[2] = (bit_buf >> 16) & 0xff;
142         s->buf_ptr[3] = (bit_buf >> 24) & 0xff;
143         
144         //printf("bitbuf = %08x\n", bit_buf);
145         s->buf_ptr+=4;
146         if (s->buf_ptr >= s->buf_end)
147             puts("bit buffer overflow !!"); // should never happen ! who got rid of the callback ???
148 //            flush_buffer_rev(s);
149         bit_cnt=bit_cnt + n - 32;
150         if (bit_cnt == 0) {
151             bit_buf = 0;
152         } else {
153             bit_buf = value >> (n - bit_cnt);
154         }
155     }
156
157     s->bit_buf = bit_buf;
158     s->bit_left = 32 - bit_cnt;
159 }
160
161 /* return the number of bits output */
162 INT64 get_bit_count_rev(PutBitContext *s)
163 {
164     return get_bit_count(s);
165 }
166
167 void align_put_bits_rev(PutBitContext *s)
168 {
169     align_put_bits(s);
170 }
171
172 /* pad the end of the output stream with zeros */
173 void flush_put_bits_rev(PutBitContext *s)
174 {
175     while (s->bit_left < 32) {
176         /* XXX: should test end of buffer */
177         *s->buf_ptr++=s->bit_buf & 0xff;
178         s->bit_buf>>=8;
179         s->bit_left+=8;
180     }
181 //    flush_buffer_rev(s);
182     s->bit_left=32;
183     s->bit_buf=0;
184 }
185
186 /* !RevPutBitContext */
187
188 typedef struct {
189     UINT8 buffer[100]; /* data chunks */
190     INT64 time, file_time;
191 } GIFContext;
192
193 static int gif_write_header(AVFormatContext *s)
194 {
195     GIFContext *gif;
196     ByteIOContext *pb = &s->pb;
197     AVCodecContext *enc, *video_enc;
198     int i, width, height, rate;
199
200 /* XXX: do we reject audio streams or just ignore them ?
201     if(s->nb_streams > 1)
202         return -1;
203 */
204
205     gif = malloc(sizeof(GIFContext));
206     if (!gif)
207         return -1;
208     s->priv_data = gif;
209
210     gif->time = 0;
211     gif->file_time = 0;
212
213     video_enc = NULL;
214     for(i=0;i<s->nb_streams;i++) {
215         enc = &s->streams[i]->codec;
216         if (enc->codec_type != CODEC_TYPE_AUDIO)
217             video_enc = enc;
218     }
219
220     if (!video_enc) {
221         free(gif);
222         return -1;
223     } else {
224         width = video_enc->width;
225         height = video_enc->height;
226         rate = video_enc->frame_rate;
227     }
228
229     /* XXX: is it allowed ? seems to work so far... */
230     video_enc->pix_fmt = PIX_FMT_RGB24;
231
232     /* GIF header */
233
234     put_tag(pb, "GIF");
235     put_tag(pb, "89a");
236     put_le16(pb, width);
237     put_le16(pb, height);
238
239     put_byte(pb, 0xf7); /* flags: global clut, 256 entries */
240     put_byte(pb, 0x1f); /* background color index */
241     put_byte(pb, 0); /* aspect ratio */
242
243     /* the global palette */
244
245     put_buffer(pb, (unsigned char *)gif_clut, 216*3);
246     for(i=0;i<((256-216)*3);i++)
247        put_byte(pb, 0);
248
249     /* application extension header */
250     /* XXX: not really sure what to put in here... */
251 #ifdef GIF_ADD_APP_HEADER
252     put_byte(pb, 0x21);
253     put_byte(pb, 0xff);
254     put_byte(pb, 0x0b);
255     put_tag(pb, "NETSCAPE2.0");
256     put_byte(pb, 0x03);
257     put_byte(pb, 0x01);
258     put_byte(pb, 0x00);
259     put_byte(pb, 0x00);
260 #endif
261
262     put_flush_packet(&s->pb);
263     return 0;
264 }
265
266 /* this is maybe slow, but allows for extensions */
267 static inline unsigned char gif_clut_index(rgb_triplet *clut, UINT8 r, UINT8 g, UINT8 b)
268 {
269     return ((((r)/47)%6)*6*6+(((g)/47)%6)*6+(((b)/47)%6));
270 }
271
272 /* chunk writer callback */
273 /* !!! XXX:deprecated
274 static void gif_put_chunk(void *pbctx, UINT8 *buffer, int count)
275 {
276     ByteIOContext *pb = (ByteIOContext *)pbctx;
277     put_byte(pb, (UINT8)count);
278     put_buffer(pb, buffer, count);
279 }
280 */
281
282 static int gif_write_video(AVFormatContext *s, 
283                            AVCodecContext *enc, UINT8 *buf, int size)
284 {
285     ByteIOContext *pb = &s->pb;
286     GIFContext *gif = s->priv_data;
287     int i, left, jiffies;
288     INT64 delay;
289     PutBitContext p;
290     UINT8 buffer[200]; /* 100 * 9 / 8 = 113 */
291
292
293     /* graphic control extension block */
294     put_byte(pb, 0x21);
295     put_byte(pb, 0xf9);
296     put_byte(pb, 0x04); /* block size */
297     put_byte(pb, 0x04); /* flags */
298     
299     /* 1 jiffy is 1/70 s */
300     /* the delay_time field indicates the number of jiffies - 1 */
301     delay = gif->file_time - gif->time;
302
303     /* XXX: should use delay, in order to be more accurate */
304     /* instead of using the same rounded value each time */
305     /* XXX: don't even remember if I really use it for now */
306     jiffies = (70*FRAME_RATE_BASE/enc->frame_rate) - 1;
307
308     put_le16(pb, jiffies);
309
310     put_byte(pb, 0x1f); /* transparent color index */
311     put_byte(pb, 0x00);
312
313     /* image block */
314
315     put_byte(pb, 0x2c);
316     put_le16(pb, 0);
317     put_le16(pb, 0);
318     put_le16(pb, enc->width);
319     put_le16(pb, enc->height);
320     put_byte(pb, 0x00); /* flags */
321     /* no local clut */
322
323     put_byte(pb, 0x08);
324
325     left=size/3;
326
327     /* XXX:deprecated */
328     /*init_put_bits_rev(&p, buffer, sizeof(buf), (void *)pb, gif_put_chunk); *//* mmm found a but in my code: s/sizeof(buf)/150/ */
329
330     init_put_bits_rev(&p, buffer, 130, NULL, NULL);
331
332 /*
333  * the thing here is the bitstream is written as little packets, with a size byte before
334  * but it's still the same bitstream between packets (no flush !)
335  */
336
337     while(left>0) {
338
339         put_bits_rev(&p, 9, 0x0100); /* clear code */
340
341         for(i=0;i<GIF_CHUNKS;i++) {
342             put_bits_rev(&p, 9, gif_clut_index(NULL, *buf, buf[1], buf[2]));
343             buf+=3;
344         }
345
346         if(left<=GIF_CHUNKS) {
347             put_bits_rev(&p, 9, 0x101); /* end of stream */
348             flush_put_bits_rev(&p);
349         }
350         if(pbBufPtr(&p) - p.buf > 0) {
351             put_byte(pb, pbBufPtr(&p) - p.buf); /* byte count of the packet */
352             put_buffer(pb, p.buf, pbBufPtr(&p) - p.buf); /* the actual buffer */
353             p.data_out_size += pbBufPtr(&p) - p.buf;
354             p.buf_ptr = p.buf; /* dequeue the bytes off the bitstream */
355         }
356         if(left<=GIF_CHUNKS) {
357             put_byte(pb, 0x00); /* end of image block */
358         }
359
360         left-=GIF_CHUNKS;
361     }
362
363     put_flush_packet(&s->pb);
364     return 0;
365 }
366
367 static int gif_write_packet(AVFormatContext *s, int stream_index, 
368                            UINT8 *buf, int size, int force_pts)
369 {
370     AVCodecContext *codec = &s->streams[stream_index]->codec;
371     if (codec->codec_type == CODEC_TYPE_AUDIO)
372         return 0; /* just ignore audio */
373     else
374         return gif_write_video(s, codec, buf, size);
375 }
376
377 static int gif_write_trailer(AVFormatContext *s)
378 {
379     GIFContext *gif = s->priv_data;
380     ByteIOContext *pb = &s->pb;
381
382     put_byte(pb, 0x3b);
383     put_flush_packet(&s->pb);
384
385     free(gif);
386     return 0;
387 }
388
389 AVFormat gif_format = {
390     "gif",
391     "GIF Animation",
392     "image/gif",
393     "gif",
394     CODEC_ID_NONE,
395     CODEC_ID_RAWVIDEO,
396     gif_write_header,
397     gif_write_packet,
398     gif_write_trailer,
399
400     NULL, /* read_header */
401     NULL, /* read_packet */
402     NULL, /* read_close */
403 };