1/* 2 * Animated GIF muxer 3 * Copyright (c) 2000 Fabrice Bellard 4 * 5 * This file is part of Libav. 6 * 7 * Libav is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * Libav is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with Libav; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22/* 23 * First version by Francois Revol revol@free.fr 24 * 25 * Features and limitations: 26 * - currently no compression is performed, 27 * in fact the size of the data is 9/8 the size of the image in 8bpp 28 * - uses only a global standard palette 29 * - tested with IE 5.0, Opera for BeOS, NetPositive (BeOS), and Mozilla (BeOS). 30 * 31 * Reference documents: 32 * http://www.goice.co.jp/member/mo/formats/gif.html 33 * http://astronomy.swin.edu.au/pbourke/dataformats/gif/ 34 * http://www.dcs.ed.ac.uk/home/mxr/gfx/2d/GIF89a.txt 35 * 36 * this url claims to have an LZW algorithm not covered by Unisys patent: 37 * http://www.msg.net/utility/whirlgif/gifencod.html 38 * could help reduce the size of the files _a lot_... 39 * some sites mentions an RLE type compression also. 40 */ 41 42#include "avformat.h" 43#include "libavutil/log.h" 44#include "libavutil/opt.h" 45 46/* The GIF format uses reversed order for bitstreams... */ 47/* at least they don't use PDP_ENDIAN :) */ 48#define BITSTREAM_WRITER_LE 49 50#include "libavcodec/put_bits.h" 51 52/* bitstream minipacket size */ 53#define GIF_CHUNKS 100 54 55/* slows down the decoding (and some browsers don't like it) */ 56/* update on the 'some browsers don't like it issue from above: this was probably due to missing 'Data Sub-block Terminator' (byte 19) in the app_header */ 57#define GIF_ADD_APP_HEADER // required to enable looping of animated gif 58 59typedef struct { 60 unsigned char r; 61 unsigned char g; 62 unsigned char b; 63} rgb_triplet; 64 65/* we use the standard 216 color palette */ 66 67/* this script was used to create the palette: 68 * 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 69 * echo -n "{ 0x$r, 0x$g, 0x$b }, "; done; echo ""; done; done 70 */ 71 72static const rgb_triplet gif_clut[216] = { 73 { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x33 }, { 0x00, 0x00, 0x66 }, { 0x00, 0x00, 0x99 }, { 0x00, 0x00, 0xcc }, { 0x00, 0x00, 0xff }, 74 { 0x00, 0x33, 0x00 }, { 0x00, 0x33, 0x33 }, { 0x00, 0x33, 0x66 }, { 0x00, 0x33, 0x99 }, { 0x00, 0x33, 0xcc }, { 0x00, 0x33, 0xff }, 75 { 0x00, 0x66, 0x00 }, { 0x00, 0x66, 0x33 }, { 0x00, 0x66, 0x66 }, { 0x00, 0x66, 0x99 }, { 0x00, 0x66, 0xcc }, { 0x00, 0x66, 0xff }, 76 { 0x00, 0x99, 0x00 }, { 0x00, 0x99, 0x33 }, { 0x00, 0x99, 0x66 }, { 0x00, 0x99, 0x99 }, { 0x00, 0x99, 0xcc }, { 0x00, 0x99, 0xff }, 77 { 0x00, 0xcc, 0x00 }, { 0x00, 0xcc, 0x33 }, { 0x00, 0xcc, 0x66 }, { 0x00, 0xcc, 0x99 }, { 0x00, 0xcc, 0xcc }, { 0x00, 0xcc, 0xff }, 78 { 0x00, 0xff, 0x00 }, { 0x00, 0xff, 0x33 }, { 0x00, 0xff, 0x66 }, { 0x00, 0xff, 0x99 }, { 0x00, 0xff, 0xcc }, { 0x00, 0xff, 0xff }, 79 { 0x33, 0x00, 0x00 }, { 0x33, 0x00, 0x33 }, { 0x33, 0x00, 0x66 }, { 0x33, 0x00, 0x99 }, { 0x33, 0x00, 0xcc }, { 0x33, 0x00, 0xff }, 80 { 0x33, 0x33, 0x00 }, { 0x33, 0x33, 0x33 }, { 0x33, 0x33, 0x66 }, { 0x33, 0x33, 0x99 }, { 0x33, 0x33, 0xcc }, { 0x33, 0x33, 0xff }, 81 { 0x33, 0x66, 0x00 }, { 0x33, 0x66, 0x33 }, { 0x33, 0x66, 0x66 }, { 0x33, 0x66, 0x99 }, { 0x33, 0x66, 0xcc }, { 0x33, 0x66, 0xff }, 82 { 0x33, 0x99, 0x00 }, { 0x33, 0x99, 0x33 }, { 0x33, 0x99, 0x66 }, { 0x33, 0x99, 0x99 }, { 0x33, 0x99, 0xcc }, { 0x33, 0x99, 0xff }, 83 { 0x33, 0xcc, 0x00 }, { 0x33, 0xcc, 0x33 }, { 0x33, 0xcc, 0x66 }, { 0x33, 0xcc, 0x99 }, { 0x33, 0xcc, 0xcc }, { 0x33, 0xcc, 0xff }, 84 { 0x33, 0xff, 0x00 }, { 0x33, 0xff, 0x33 }, { 0x33, 0xff, 0x66 }, { 0x33, 0xff, 0x99 }, { 0x33, 0xff, 0xcc }, { 0x33, 0xff, 0xff }, 85 { 0x66, 0x00, 0x00 }, { 0x66, 0x00, 0x33 }, { 0x66, 0x00, 0x66 }, { 0x66, 0x00, 0x99 }, { 0x66, 0x00, 0xcc }, { 0x66, 0x00, 0xff }, 86 { 0x66, 0x33, 0x00 }, { 0x66, 0x33, 0x33 }, { 0x66, 0x33, 0x66 }, { 0x66, 0x33, 0x99 }, { 0x66, 0x33, 0xcc }, { 0x66, 0x33, 0xff }, 87 { 0x66, 0x66, 0x00 }, { 0x66, 0x66, 0x33 }, { 0x66, 0x66, 0x66 }, { 0x66, 0x66, 0x99 }, { 0x66, 0x66, 0xcc }, { 0x66, 0x66, 0xff }, 88 { 0x66, 0x99, 0x00 }, { 0x66, 0x99, 0x33 }, { 0x66, 0x99, 0x66 }, { 0x66, 0x99, 0x99 }, { 0x66, 0x99, 0xcc }, { 0x66, 0x99, 0xff }, 89 { 0x66, 0xcc, 0x00 }, { 0x66, 0xcc, 0x33 }, { 0x66, 0xcc, 0x66 }, { 0x66, 0xcc, 0x99 }, { 0x66, 0xcc, 0xcc }, { 0x66, 0xcc, 0xff }, 90 { 0x66, 0xff, 0x00 }, { 0x66, 0xff, 0x33 }, { 0x66, 0xff, 0x66 }, { 0x66, 0xff, 0x99 }, { 0x66, 0xff, 0xcc }, { 0x66, 0xff, 0xff }, 91 { 0x99, 0x00, 0x00 }, { 0x99, 0x00, 0x33 }, { 0x99, 0x00, 0x66 }, { 0x99, 0x00, 0x99 }, { 0x99, 0x00, 0xcc }, { 0x99, 0x00, 0xff }, 92 { 0x99, 0x33, 0x00 }, { 0x99, 0x33, 0x33 }, { 0x99, 0x33, 0x66 }, { 0x99, 0x33, 0x99 }, { 0x99, 0x33, 0xcc }, { 0x99, 0x33, 0xff }, 93 { 0x99, 0x66, 0x00 }, { 0x99, 0x66, 0x33 }, { 0x99, 0x66, 0x66 }, { 0x99, 0x66, 0x99 }, { 0x99, 0x66, 0xcc }, { 0x99, 0x66, 0xff }, 94 { 0x99, 0x99, 0x00 }, { 0x99, 0x99, 0x33 }, { 0x99, 0x99, 0x66 }, { 0x99, 0x99, 0x99 }, { 0x99, 0x99, 0xcc }, { 0x99, 0x99, 0xff }, 95 { 0x99, 0xcc, 0x00 }, { 0x99, 0xcc, 0x33 }, { 0x99, 0xcc, 0x66 }, { 0x99, 0xcc, 0x99 }, { 0x99, 0xcc, 0xcc }, { 0x99, 0xcc, 0xff }, 96 { 0x99, 0xff, 0x00 }, { 0x99, 0xff, 0x33 }, { 0x99, 0xff, 0x66 }, { 0x99, 0xff, 0x99 }, { 0x99, 0xff, 0xcc }, { 0x99, 0xff, 0xff }, 97 { 0xcc, 0x00, 0x00 }, { 0xcc, 0x00, 0x33 }, { 0xcc, 0x00, 0x66 }, { 0xcc, 0x00, 0x99 }, { 0xcc, 0x00, 0xcc }, { 0xcc, 0x00, 0xff }, 98 { 0xcc, 0x33, 0x00 }, { 0xcc, 0x33, 0x33 }, { 0xcc, 0x33, 0x66 }, { 0xcc, 0x33, 0x99 }, { 0xcc, 0x33, 0xcc }, { 0xcc, 0x33, 0xff }, 99 { 0xcc, 0x66, 0x00 }, { 0xcc, 0x66, 0x33 }, { 0xcc, 0x66, 0x66 }, { 0xcc, 0x66, 0x99 }, { 0xcc, 0x66, 0xcc }, { 0xcc, 0x66, 0xff }, 100 { 0xcc, 0x99, 0x00 }, { 0xcc, 0x99, 0x33 }, { 0xcc, 0x99, 0x66 }, { 0xcc, 0x99, 0x99 }, { 0xcc, 0x99, 0xcc }, { 0xcc, 0x99, 0xff }, 101 { 0xcc, 0xcc, 0x00 }, { 0xcc, 0xcc, 0x33 }, { 0xcc, 0xcc, 0x66 }, { 0xcc, 0xcc, 0x99 }, { 0xcc, 0xcc, 0xcc }, { 0xcc, 0xcc, 0xff }, 102 { 0xcc, 0xff, 0x00 }, { 0xcc, 0xff, 0x33 }, { 0xcc, 0xff, 0x66 }, { 0xcc, 0xff, 0x99 }, { 0xcc, 0xff, 0xcc }, { 0xcc, 0xff, 0xff }, 103 { 0xff, 0x00, 0x00 }, { 0xff, 0x00, 0x33 }, { 0xff, 0x00, 0x66 }, { 0xff, 0x00, 0x99 }, { 0xff, 0x00, 0xcc }, { 0xff, 0x00, 0xff }, 104 { 0xff, 0x33, 0x00 }, { 0xff, 0x33, 0x33 }, { 0xff, 0x33, 0x66 }, { 0xff, 0x33, 0x99 }, { 0xff, 0x33, 0xcc }, { 0xff, 0x33, 0xff }, 105 { 0xff, 0x66, 0x00 }, { 0xff, 0x66, 0x33 }, { 0xff, 0x66, 0x66 }, { 0xff, 0x66, 0x99 }, { 0xff, 0x66, 0xcc }, { 0xff, 0x66, 0xff }, 106 { 0xff, 0x99, 0x00 }, { 0xff, 0x99, 0x33 }, { 0xff, 0x99, 0x66 }, { 0xff, 0x99, 0x99 }, { 0xff, 0x99, 0xcc }, { 0xff, 0x99, 0xff }, 107 { 0xff, 0xcc, 0x00 }, { 0xff, 0xcc, 0x33 }, { 0xff, 0xcc, 0x66 }, { 0xff, 0xcc, 0x99 }, { 0xff, 0xcc, 0xcc }, { 0xff, 0xcc, 0xff }, 108 { 0xff, 0xff, 0x00 }, { 0xff, 0xff, 0x33 }, { 0xff, 0xff, 0x66 }, { 0xff, 0xff, 0x99 }, { 0xff, 0xff, 0xcc }, { 0xff, 0xff, 0xff }, 109}; 110 111/* GIF header */ 112static int gif_image_write_header(AVIOContext *pb, 113 int width, int height, int loop_count, 114 uint32_t *palette) 115{ 116 int i; 117 unsigned int v; 118 119 avio_write(pb, "GIF", 3); 120 avio_write(pb, "89a", 3); 121 avio_wl16(pb, width); 122 avio_wl16(pb, height); 123 124 avio_w8(pb, 0xf7); /* flags: global clut, 256 entries */ 125 avio_w8(pb, 0x1f); /* background color index */ 126 avio_w8(pb, 0); /* aspect ratio */ 127 128 /* the global palette */ 129 if (!palette) { 130 avio_write(pb, (const unsigned char *)gif_clut, 216*3); 131 for(i=0;i<((256-216)*3);i++) 132 avio_w8(pb, 0); 133 } else { 134 for(i=0;i<256;i++) { 135 v = palette[i]; 136 avio_w8(pb, (v >> 16) & 0xff); 137 avio_w8(pb, (v >> 8) & 0xff); 138 avio_w8(pb, (v) & 0xff); 139 } 140 } 141 142 /* update: this is the 'NETSCAPE EXTENSION' that allows for looped animated gif 143 see http://members.aol.com/royalef/gifabout.htm#net-extension 144 145 byte 1 : 33 (hex 0x21) GIF Extension code 146 byte 2 : 255 (hex 0xFF) Application Extension Label 147 byte 3 : 11 (hex (0x0B) Length of Application Block 148 (eleven bytes of data to follow) 149 bytes 4 to 11 : "NETSCAPE" 150 bytes 12 to 14 : "2.0" 151 byte 15 : 3 (hex 0x03) Length of Data Sub-Block 152 (three bytes of data to follow) 153 byte 16 : 1 (hex 0x01) 154 bytes 17 to 18 : 0 to 65535, an unsigned integer in 155 lo-hi byte format. This indicate the 156 number of iterations the loop should 157 be executed. 158 bytes 19 : 0 (hex 0x00) a Data Sub-block Terminator 159 */ 160 161 /* application extension header */ 162#ifdef GIF_ADD_APP_HEADER 163 if (loop_count >= 0 && loop_count <= 65535) { 164 avio_w8(pb, 0x21); 165 avio_w8(pb, 0xff); 166 avio_w8(pb, 0x0b); 167 avio_write(pb, "NETSCAPE2.0", sizeof("NETSCAPE2.0") - 1); // bytes 4 to 14 168 avio_w8(pb, 0x03); // byte 15 169 avio_w8(pb, 0x01); // byte 16 170 avio_wl16(pb, (uint16_t)loop_count); 171 avio_w8(pb, 0x00); // byte 19 172 } 173#endif 174 return 0; 175} 176 177/* this is maybe slow, but allows for extensions */ 178static inline unsigned char gif_clut_index(uint8_t r, uint8_t g, uint8_t b) 179{ 180 return (((r) / 47) % 6) * 6 * 6 + (((g) / 47) % 6) * 6 + (((b) / 47) % 6); 181} 182 183 184static int gif_image_write_image(AVIOContext *pb, 185 int x1, int y1, int width, int height, 186 const uint8_t *buf, int linesize, int pix_fmt) 187{ 188 PutBitContext p; 189 uint8_t buffer[200]; /* 100 * 9 / 8 = 113 */ 190 int i, left, w, v; 191 const uint8_t *ptr; 192 /* image block */ 193 194 avio_w8(pb, 0x2c); 195 avio_wl16(pb, x1); 196 avio_wl16(pb, y1); 197 avio_wl16(pb, width); 198 avio_wl16(pb, height); 199 avio_w8(pb, 0x00); /* flags */ 200 /* no local clut */ 201 202 avio_w8(pb, 0x08); 203 204 left= width * height; 205 206 init_put_bits(&p, buffer, 130); 207 208/* 209 * the thing here is the bitstream is written as little packets, with a size byte before 210 * but it's still the same bitstream between packets (no flush !) 211 */ 212 ptr = buf; 213 w = width; 214 while(left>0) { 215 216 put_bits(&p, 9, 0x0100); /* clear code */ 217 218 for(i=(left<GIF_CHUNKS)?left:GIF_CHUNKS;i;i--) { 219 if (pix_fmt == PIX_FMT_RGB24) { 220 v = gif_clut_index(ptr[0], ptr[1], ptr[2]); 221 ptr+=3; 222 } else { 223 v = *ptr++; 224 } 225 put_bits(&p, 9, v); 226 if (--w == 0) { 227 w = width; 228 buf += linesize; 229 ptr = buf; 230 } 231 } 232 233 if(left<=GIF_CHUNKS) { 234 put_bits(&p, 9, 0x101); /* end of stream */ 235 flush_put_bits(&p); 236 } 237 if(put_bits_ptr(&p) - p.buf > 0) { 238 avio_w8(pb, put_bits_ptr(&p) - p.buf); /* byte count of the packet */ 239 avio_write(pb, p.buf, put_bits_ptr(&p) - p.buf); /* the actual buffer */ 240 p.buf_ptr = p.buf; /* dequeue the bytes off the bitstream */ 241 } 242 left-=GIF_CHUNKS; 243 } 244 avio_w8(pb, 0x00); /* end of image block */ 245 246 return 0; 247} 248 249typedef struct { 250 AVClass *class; /** Class for private options. */ 251 int64_t time, file_time; 252 uint8_t buffer[100]; /* data chunks */ 253 int loop; 254} GIFContext; 255 256static int gif_write_header(AVFormatContext *s) 257{ 258 GIFContext *gif = s->priv_data; 259 AVIOContext *pb = s->pb; 260 AVCodecContext *enc, *video_enc; 261 int i, width, height /*, rate*/; 262 263/* XXX: do we reject audio streams or just ignore them ? 264 if(s->nb_streams > 1) 265 return -1; 266*/ 267 gif->time = 0; 268 gif->file_time = 0; 269 270 video_enc = NULL; 271 for(i=0;i<s->nb_streams;i++) { 272 enc = s->streams[i]->codec; 273 if (enc->codec_type != AVMEDIA_TYPE_AUDIO) 274 video_enc = enc; 275 } 276 277 if (!video_enc) { 278 av_free(gif); 279 return -1; 280 } else { 281 width = video_enc->width; 282 height = video_enc->height; 283// rate = video_enc->time_base.den; 284 } 285 286 if (video_enc->pix_fmt != PIX_FMT_RGB24) { 287 av_log(s, AV_LOG_ERROR, "ERROR: gif only handles the rgb24 pixel format. Use -pix_fmt rgb24.\n"); 288 return AVERROR(EIO); 289 } 290 291#if FF_API_LOOP_OUTPUT 292 if (s->loop_output) 293 gif->loop = s->loop_output; 294#endif 295 296 gif_image_write_header(pb, width, height, gif->loop, NULL); 297 298 avio_flush(s->pb); 299 return 0; 300} 301 302static int gif_write_video(AVFormatContext *s, 303 AVCodecContext *enc, const uint8_t *buf, int size) 304{ 305 AVIOContext *pb = s->pb; 306 int jiffies; 307 308 /* graphic control extension block */ 309 avio_w8(pb, 0x21); 310 avio_w8(pb, 0xf9); 311 avio_w8(pb, 0x04); /* block size */ 312 avio_w8(pb, 0x04); /* flags */ 313 314 /* 1 jiffy is 1/70 s */ 315 /* the delay_time field indicates the number of jiffies - 1 */ 316 /* XXX: should use delay, in order to be more accurate */ 317 /* instead of using the same rounded value each time */ 318 /* XXX: don't even remember if I really use it for now */ 319 jiffies = (70*enc->time_base.num/enc->time_base.den) - 1; 320 321 avio_wl16(pb, jiffies); 322 323 avio_w8(pb, 0x1f); /* transparent color index */ 324 avio_w8(pb, 0x00); 325 326 gif_image_write_image(pb, 0, 0, enc->width, enc->height, 327 buf, enc->width * 3, PIX_FMT_RGB24); 328 329 avio_flush(s->pb); 330 return 0; 331} 332 333static int gif_write_packet(AVFormatContext *s, AVPacket *pkt) 334{ 335 AVCodecContext *codec = s->streams[pkt->stream_index]->codec; 336 if (codec->codec_type == AVMEDIA_TYPE_AUDIO) 337 return 0; /* just ignore audio */ 338 else 339 return gif_write_video(s, codec, pkt->data, pkt->size); 340} 341 342static int gif_write_trailer(AVFormatContext *s) 343{ 344 AVIOContext *pb = s->pb; 345 346 avio_w8(pb, 0x3b); 347 avio_flush(s->pb); 348 return 0; 349} 350 351#define OFFSET(x) offsetof(GIFContext, x) 352#define ENC AV_OPT_FLAG_ENCODING_PARAM 353static const AVOption options[] = { 354 { "loop", "Number of times to loop the output.", OFFSET(loop), AV_OPT_TYPE_INT, {0}, 0, 65535, ENC }, 355 { NULL }, 356}; 357 358static const AVClass gif_muxer_class = { 359 .class_name = "GIF muxer", 360 .item_name = av_default_item_name, 361 .version = LIBAVUTIL_VERSION_INT, 362 .option = options, 363}; 364 365AVOutputFormat ff_gif_muxer = { 366 .name = "gif", 367 .long_name = NULL_IF_CONFIG_SMALL("GIF Animation"), 368 .mime_type = "image/gif", 369 .extensions = "gif", 370 .priv_data_size = sizeof(GIFContext), 371 .audio_codec = CODEC_ID_NONE, 372 .video_codec = CODEC_ID_RAWVIDEO, 373 .write_header = gif_write_header, 374 .write_packet = gif_write_packet, 375 .write_trailer = gif_write_trailer, 376 .priv_class = &gif_muxer_class, 377}; 378