• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • Examples
  • File List
  • Globals

libavformat/gif.c

Go to the documentation of this file.
00001 /*
00002  * Animated GIF muxer
00003  * Copyright (c) 2000 Fabrice Bellard
00004  *
00005  * This file is part of Libav.
00006  *
00007  * Libav is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * Libav is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with Libav; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00020  */
00021 
00022 /*
00023  * First version by Francois Revol revol@free.fr
00024  *
00025  * Features and limitations:
00026  * - currently no compression is performed,
00027  *   in fact the size of the data is 9/8 the size of the image in 8bpp
00028  * - uses only a global standard palette
00029  * - tested with IE 5.0, Opera for BeOS, NetPositive (BeOS), and Mozilla (BeOS).
00030  *
00031  * Reference documents:
00032  * http://www.goice.co.jp/member/mo/formats/gif.html
00033  * http://astronomy.swin.edu.au/pbourke/dataformats/gif/
00034  * http://www.dcs.ed.ac.uk/home/mxr/gfx/2d/GIF89a.txt
00035  *
00036  * this url claims to have an LZW algorithm not covered by Unisys patent:
00037  * http://www.msg.net/utility/whirlgif/gifencod.html
00038  * could help reduce the size of the files _a lot_...
00039  * some sites mentions an RLE type compression also.
00040  */
00041 
00042 #include "avformat.h"
00043 #include "libavutil/log.h"
00044 #include "libavutil/opt.h"
00045 
00046 /* The GIF format uses reversed order for bitstreams... */
00047 /* at least they don't use PDP_ENDIAN :) */
00048 #define BITSTREAM_WRITER_LE
00049 
00050 #include "libavcodec/put_bits.h"
00051 
00052 /* bitstream minipacket size */
00053 #define GIF_CHUNKS 100
00054 
00055 /* slows down the decoding (and some browsers don't like it) */
00056 /* 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 */
00057 #define GIF_ADD_APP_HEADER // required to enable looping of animated gif
00058 
00059 typedef struct {
00060     unsigned char r;
00061     unsigned char g;
00062     unsigned char b;
00063 } rgb_triplet;
00064 
00065 /* we use the standard 216 color palette */
00066 
00067 /* this script was used to create the palette:
00068  * 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
00069  *   echo -n "{ 0x$r, 0x$g, 0x$b }, "; done; echo ""; done; done
00070  */
00071 
00072 static const rgb_triplet gif_clut[216] = {
00073     { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x33 }, { 0x00, 0x00, 0x66 }, { 0x00, 0x00, 0x99 }, { 0x00, 0x00, 0xcc }, { 0x00, 0x00, 0xff },
00074     { 0x00, 0x33, 0x00 }, { 0x00, 0x33, 0x33 }, { 0x00, 0x33, 0x66 }, { 0x00, 0x33, 0x99 }, { 0x00, 0x33, 0xcc }, { 0x00, 0x33, 0xff },
00075     { 0x00, 0x66, 0x00 }, { 0x00, 0x66, 0x33 }, { 0x00, 0x66, 0x66 }, { 0x00, 0x66, 0x99 }, { 0x00, 0x66, 0xcc }, { 0x00, 0x66, 0xff },
00076     { 0x00, 0x99, 0x00 }, { 0x00, 0x99, 0x33 }, { 0x00, 0x99, 0x66 }, { 0x00, 0x99, 0x99 }, { 0x00, 0x99, 0xcc }, { 0x00, 0x99, 0xff },
00077     { 0x00, 0xcc, 0x00 }, { 0x00, 0xcc, 0x33 }, { 0x00, 0xcc, 0x66 }, { 0x00, 0xcc, 0x99 }, { 0x00, 0xcc, 0xcc }, { 0x00, 0xcc, 0xff },
00078     { 0x00, 0xff, 0x00 }, { 0x00, 0xff, 0x33 }, { 0x00, 0xff, 0x66 }, { 0x00, 0xff, 0x99 }, { 0x00, 0xff, 0xcc }, { 0x00, 0xff, 0xff },
00079     { 0x33, 0x00, 0x00 }, { 0x33, 0x00, 0x33 }, { 0x33, 0x00, 0x66 }, { 0x33, 0x00, 0x99 }, { 0x33, 0x00, 0xcc }, { 0x33, 0x00, 0xff },
00080     { 0x33, 0x33, 0x00 }, { 0x33, 0x33, 0x33 }, { 0x33, 0x33, 0x66 }, { 0x33, 0x33, 0x99 }, { 0x33, 0x33, 0xcc }, { 0x33, 0x33, 0xff },
00081     { 0x33, 0x66, 0x00 }, { 0x33, 0x66, 0x33 }, { 0x33, 0x66, 0x66 }, { 0x33, 0x66, 0x99 }, { 0x33, 0x66, 0xcc }, { 0x33, 0x66, 0xff },
00082     { 0x33, 0x99, 0x00 }, { 0x33, 0x99, 0x33 }, { 0x33, 0x99, 0x66 }, { 0x33, 0x99, 0x99 }, { 0x33, 0x99, 0xcc }, { 0x33, 0x99, 0xff },
00083     { 0x33, 0xcc, 0x00 }, { 0x33, 0xcc, 0x33 }, { 0x33, 0xcc, 0x66 }, { 0x33, 0xcc, 0x99 }, { 0x33, 0xcc, 0xcc }, { 0x33, 0xcc, 0xff },
00084     { 0x33, 0xff, 0x00 }, { 0x33, 0xff, 0x33 }, { 0x33, 0xff, 0x66 }, { 0x33, 0xff, 0x99 }, { 0x33, 0xff, 0xcc }, { 0x33, 0xff, 0xff },
00085     { 0x66, 0x00, 0x00 }, { 0x66, 0x00, 0x33 }, { 0x66, 0x00, 0x66 }, { 0x66, 0x00, 0x99 }, { 0x66, 0x00, 0xcc }, { 0x66, 0x00, 0xff },
00086     { 0x66, 0x33, 0x00 }, { 0x66, 0x33, 0x33 }, { 0x66, 0x33, 0x66 }, { 0x66, 0x33, 0x99 }, { 0x66, 0x33, 0xcc }, { 0x66, 0x33, 0xff },
00087     { 0x66, 0x66, 0x00 }, { 0x66, 0x66, 0x33 }, { 0x66, 0x66, 0x66 }, { 0x66, 0x66, 0x99 }, { 0x66, 0x66, 0xcc }, { 0x66, 0x66, 0xff },
00088     { 0x66, 0x99, 0x00 }, { 0x66, 0x99, 0x33 }, { 0x66, 0x99, 0x66 }, { 0x66, 0x99, 0x99 }, { 0x66, 0x99, 0xcc }, { 0x66, 0x99, 0xff },
00089     { 0x66, 0xcc, 0x00 }, { 0x66, 0xcc, 0x33 }, { 0x66, 0xcc, 0x66 }, { 0x66, 0xcc, 0x99 }, { 0x66, 0xcc, 0xcc }, { 0x66, 0xcc, 0xff },
00090     { 0x66, 0xff, 0x00 }, { 0x66, 0xff, 0x33 }, { 0x66, 0xff, 0x66 }, { 0x66, 0xff, 0x99 }, { 0x66, 0xff, 0xcc }, { 0x66, 0xff, 0xff },
00091     { 0x99, 0x00, 0x00 }, { 0x99, 0x00, 0x33 }, { 0x99, 0x00, 0x66 }, { 0x99, 0x00, 0x99 }, { 0x99, 0x00, 0xcc }, { 0x99, 0x00, 0xff },
00092     { 0x99, 0x33, 0x00 }, { 0x99, 0x33, 0x33 }, { 0x99, 0x33, 0x66 }, { 0x99, 0x33, 0x99 }, { 0x99, 0x33, 0xcc }, { 0x99, 0x33, 0xff },
00093     { 0x99, 0x66, 0x00 }, { 0x99, 0x66, 0x33 }, { 0x99, 0x66, 0x66 }, { 0x99, 0x66, 0x99 }, { 0x99, 0x66, 0xcc }, { 0x99, 0x66, 0xff },
00094     { 0x99, 0x99, 0x00 }, { 0x99, 0x99, 0x33 }, { 0x99, 0x99, 0x66 }, { 0x99, 0x99, 0x99 }, { 0x99, 0x99, 0xcc }, { 0x99, 0x99, 0xff },
00095     { 0x99, 0xcc, 0x00 }, { 0x99, 0xcc, 0x33 }, { 0x99, 0xcc, 0x66 }, { 0x99, 0xcc, 0x99 }, { 0x99, 0xcc, 0xcc }, { 0x99, 0xcc, 0xff },
00096     { 0x99, 0xff, 0x00 }, { 0x99, 0xff, 0x33 }, { 0x99, 0xff, 0x66 }, { 0x99, 0xff, 0x99 }, { 0x99, 0xff, 0xcc }, { 0x99, 0xff, 0xff },
00097     { 0xcc, 0x00, 0x00 }, { 0xcc, 0x00, 0x33 }, { 0xcc, 0x00, 0x66 }, { 0xcc, 0x00, 0x99 }, { 0xcc, 0x00, 0xcc }, { 0xcc, 0x00, 0xff },
00098     { 0xcc, 0x33, 0x00 }, { 0xcc, 0x33, 0x33 }, { 0xcc, 0x33, 0x66 }, { 0xcc, 0x33, 0x99 }, { 0xcc, 0x33, 0xcc }, { 0xcc, 0x33, 0xff },
00099     { 0xcc, 0x66, 0x00 }, { 0xcc, 0x66, 0x33 }, { 0xcc, 0x66, 0x66 }, { 0xcc, 0x66, 0x99 }, { 0xcc, 0x66, 0xcc }, { 0xcc, 0x66, 0xff },
00100     { 0xcc, 0x99, 0x00 }, { 0xcc, 0x99, 0x33 }, { 0xcc, 0x99, 0x66 }, { 0xcc, 0x99, 0x99 }, { 0xcc, 0x99, 0xcc }, { 0xcc, 0x99, 0xff },
00101     { 0xcc, 0xcc, 0x00 }, { 0xcc, 0xcc, 0x33 }, { 0xcc, 0xcc, 0x66 }, { 0xcc, 0xcc, 0x99 }, { 0xcc, 0xcc, 0xcc }, { 0xcc, 0xcc, 0xff },
00102     { 0xcc, 0xff, 0x00 }, { 0xcc, 0xff, 0x33 }, { 0xcc, 0xff, 0x66 }, { 0xcc, 0xff, 0x99 }, { 0xcc, 0xff, 0xcc }, { 0xcc, 0xff, 0xff },
00103     { 0xff, 0x00, 0x00 }, { 0xff, 0x00, 0x33 }, { 0xff, 0x00, 0x66 }, { 0xff, 0x00, 0x99 }, { 0xff, 0x00, 0xcc }, { 0xff, 0x00, 0xff },
00104     { 0xff, 0x33, 0x00 }, { 0xff, 0x33, 0x33 }, { 0xff, 0x33, 0x66 }, { 0xff, 0x33, 0x99 }, { 0xff, 0x33, 0xcc }, { 0xff, 0x33, 0xff },
00105     { 0xff, 0x66, 0x00 }, { 0xff, 0x66, 0x33 }, { 0xff, 0x66, 0x66 }, { 0xff, 0x66, 0x99 }, { 0xff, 0x66, 0xcc }, { 0xff, 0x66, 0xff },
00106     { 0xff, 0x99, 0x00 }, { 0xff, 0x99, 0x33 }, { 0xff, 0x99, 0x66 }, { 0xff, 0x99, 0x99 }, { 0xff, 0x99, 0xcc }, { 0xff, 0x99, 0xff },
00107     { 0xff, 0xcc, 0x00 }, { 0xff, 0xcc, 0x33 }, { 0xff, 0xcc, 0x66 }, { 0xff, 0xcc, 0x99 }, { 0xff, 0xcc, 0xcc }, { 0xff, 0xcc, 0xff },
00108     { 0xff, 0xff, 0x00 }, { 0xff, 0xff, 0x33 }, { 0xff, 0xff, 0x66 }, { 0xff, 0xff, 0x99 }, { 0xff, 0xff, 0xcc }, { 0xff, 0xff, 0xff },
00109 };
00110 
00111 /* GIF header */
00112 static int gif_image_write_header(AVIOContext *pb,
00113                                   int width, int height, int loop_count,
00114                                   uint32_t *palette)
00115 {
00116     int i;
00117     unsigned int v;
00118 
00119     avio_write(pb, "GIF", 3);
00120     avio_write(pb, "89a", 3);
00121     avio_wl16(pb, width);
00122     avio_wl16(pb, height);
00123 
00124     avio_w8(pb, 0xf7); /* flags: global clut, 256 entries */
00125     avio_w8(pb, 0x1f); /* background color index */
00126     avio_w8(pb, 0);    /* aspect ratio */
00127 
00128     /* the global palette */
00129     if (!palette) {
00130         avio_write(pb, (const unsigned char *)gif_clut, 216*3);
00131         for(i=0;i<((256-216)*3);i++)
00132             avio_w8(pb, 0);
00133     } else {
00134         for(i=0;i<256;i++) {
00135             v = palette[i];
00136             avio_w8(pb, (v >> 16) & 0xff);
00137             avio_w8(pb, (v >> 8) & 0xff);
00138             avio_w8(pb, (v) & 0xff);
00139         }
00140     }
00141 
00142         /*        update: this is the 'NETSCAPE EXTENSION' that allows for looped animated gif
00143                 see http://members.aol.com/royalef/gifabout.htm#net-extension
00144 
00145                 byte   1       : 33 (hex 0x21) GIF Extension code
00146                 byte   2       : 255 (hex 0xFF) Application Extension Label
00147                 byte   3       : 11 (hex (0x0B) Length of Application Block
00148                                          (eleven bytes of data to follow)
00149                 bytes  4 to 11 : "NETSCAPE"
00150                 bytes 12 to 14 : "2.0"
00151                 byte  15       : 3 (hex 0x03) Length of Data Sub-Block
00152                                          (three bytes of data to follow)
00153                 byte  16       : 1 (hex 0x01)
00154                 bytes 17 to 18 : 0 to 65535, an unsigned integer in
00155                                          lo-hi byte format. This indicate the
00156                                          number of iterations the loop should
00157                                          be executed.
00158                 bytes 19       : 0 (hex 0x00) a Data Sub-block Terminator
00159         */
00160 
00161     /* application extension header */
00162 #ifdef GIF_ADD_APP_HEADER
00163     if (loop_count >= 0 && loop_count <= 65535) {
00164     avio_w8(pb, 0x21);
00165     avio_w8(pb, 0xff);
00166     avio_w8(pb, 0x0b);
00167         avio_write(pb, "NETSCAPE2.0", sizeof("NETSCAPE2.0") - 1);  // bytes 4 to 14
00168         avio_w8(pb, 0x03); // byte 15
00169         avio_w8(pb, 0x01); // byte 16
00170         avio_wl16(pb, (uint16_t)loop_count);
00171         avio_w8(pb, 0x00); // byte 19
00172     }
00173 #endif
00174     return 0;
00175 }
00176 
00177 /* this is maybe slow, but allows for extensions */
00178 static inline unsigned char gif_clut_index(uint8_t r, uint8_t g, uint8_t b)
00179 {
00180     return (((r) / 47) % 6) * 6 * 6 + (((g) / 47) % 6) * 6 + (((b) / 47) % 6);
00181 }
00182 
00183 
00184 static int gif_image_write_image(AVIOContext *pb,
00185                                  int x1, int y1, int width, int height,
00186                                  const uint8_t *buf, int linesize, int pix_fmt)
00187 {
00188     PutBitContext p;
00189     uint8_t buffer[200]; /* 100 * 9 / 8 = 113 */
00190     int i, left, w, v;
00191     const uint8_t *ptr;
00192     /* image block */
00193 
00194     avio_w8(pb, 0x2c);
00195     avio_wl16(pb, x1);
00196     avio_wl16(pb, y1);
00197     avio_wl16(pb, width);
00198     avio_wl16(pb, height);
00199     avio_w8(pb, 0x00); /* flags */
00200     /* no local clut */
00201 
00202     avio_w8(pb, 0x08);
00203 
00204     left= width * height;
00205 
00206     init_put_bits(&p, buffer, 130);
00207 
00208 /*
00209  * the thing here is the bitstream is written as little packets, with a size byte before
00210  * but it's still the same bitstream between packets (no flush !)
00211  */
00212     ptr = buf;
00213     w = width;
00214     while(left>0) {
00215 
00216         put_bits(&p, 9, 0x0100); /* clear code */
00217 
00218         for(i=(left<GIF_CHUNKS)?left:GIF_CHUNKS;i;i--) {
00219             if (pix_fmt == PIX_FMT_RGB24) {
00220                 v = gif_clut_index(ptr[0], ptr[1], ptr[2]);
00221                 ptr+=3;
00222             } else {
00223                 v = *ptr++;
00224             }
00225             put_bits(&p, 9, v);
00226             if (--w == 0) {
00227                 w = width;
00228                 buf += linesize;
00229                 ptr = buf;
00230             }
00231         }
00232 
00233         if(left<=GIF_CHUNKS) {
00234             put_bits(&p, 9, 0x101); /* end of stream */
00235             flush_put_bits(&p);
00236         }
00237         if(put_bits_ptr(&p) - p.buf > 0) {
00238             avio_w8(pb, put_bits_ptr(&p) - p.buf); /* byte count of the packet */
00239             avio_write(pb, p.buf, put_bits_ptr(&p) - p.buf); /* the actual buffer */
00240             p.buf_ptr = p.buf; /* dequeue the bytes off the bitstream */
00241         }
00242         left-=GIF_CHUNKS;
00243     }
00244     avio_w8(pb, 0x00); /* end of image block */
00245 
00246     return 0;
00247 }
00248 
00249 typedef struct {
00250     AVClass *class;         
00251     int64_t time, file_time;
00252     uint8_t buffer[100]; /* data chunks */
00253     int loop;
00254 } GIFContext;
00255 
00256 static int gif_write_header(AVFormatContext *s)
00257 {
00258     GIFContext *gif = s->priv_data;
00259     AVIOContext *pb = s->pb;
00260     AVCodecContext *enc, *video_enc;
00261     int i, width, height /*, rate*/;
00262 
00263 /* XXX: do we reject audio streams or just ignore them ?
00264     if(s->nb_streams > 1)
00265         return -1;
00266 */
00267     gif->time = 0;
00268     gif->file_time = 0;
00269 
00270     video_enc = NULL;
00271     for(i=0;i<s->nb_streams;i++) {
00272         enc = s->streams[i]->codec;
00273         if (enc->codec_type != AVMEDIA_TYPE_AUDIO)
00274             video_enc = enc;
00275     }
00276 
00277     if (!video_enc) {
00278         av_free(gif);
00279         return -1;
00280     } else {
00281         width = video_enc->width;
00282         height = video_enc->height;
00283 //        rate = video_enc->time_base.den;
00284     }
00285 
00286     if (video_enc->pix_fmt != PIX_FMT_RGB24) {
00287         av_log(s, AV_LOG_ERROR, "ERROR: gif only handles the rgb24 pixel format. Use -pix_fmt rgb24.\n");
00288         return AVERROR(EIO);
00289     }
00290 
00291 #if FF_API_LOOP_OUTPUT
00292     if (s->loop_output)
00293         gif->loop = s->loop_output;
00294 #endif
00295 
00296     gif_image_write_header(pb, width, height, gif->loop, NULL);
00297 
00298     avio_flush(s->pb);
00299     return 0;
00300 }
00301 
00302 static int gif_write_video(AVFormatContext *s,
00303                            AVCodecContext *enc, const uint8_t *buf, int size)
00304 {
00305     AVIOContext *pb = s->pb;
00306     int jiffies;
00307 
00308     /* graphic control extension block */
00309     avio_w8(pb, 0x21);
00310     avio_w8(pb, 0xf9);
00311     avio_w8(pb, 0x04); /* block size */
00312     avio_w8(pb, 0x04); /* flags */
00313 
00314     /* 1 jiffy is 1/70 s */
00315     /* the delay_time field indicates the number of jiffies - 1 */
00316     /* XXX: should use delay, in order to be more accurate */
00317     /* instead of using the same rounded value each time */
00318     /* XXX: don't even remember if I really use it for now */
00319     jiffies = (70*enc->time_base.num/enc->time_base.den) - 1;
00320 
00321     avio_wl16(pb, jiffies);
00322 
00323     avio_w8(pb, 0x1f); /* transparent color index */
00324     avio_w8(pb, 0x00);
00325 
00326     gif_image_write_image(pb, 0, 0, enc->width, enc->height,
00327                           buf, enc->width * 3, PIX_FMT_RGB24);
00328 
00329     avio_flush(s->pb);
00330     return 0;
00331 }
00332 
00333 static int gif_write_packet(AVFormatContext *s, AVPacket *pkt)
00334 {
00335     AVCodecContext *codec = s->streams[pkt->stream_index]->codec;
00336     if (codec->codec_type == AVMEDIA_TYPE_AUDIO)
00337         return 0; /* just ignore audio */
00338     else
00339         return gif_write_video(s, codec, pkt->data, pkt->size);
00340 }
00341 
00342 static int gif_write_trailer(AVFormatContext *s)
00343 {
00344     AVIOContext *pb = s->pb;
00345 
00346     avio_w8(pb, 0x3b);
00347     avio_flush(s->pb);
00348     return 0;
00349 }
00350 
00351 #define OFFSET(x) offsetof(GIFContext, x)
00352 #define ENC AV_OPT_FLAG_ENCODING_PARAM
00353 static const AVOption options[] = {
00354     { "loop", "Number of times to loop the output.", OFFSET(loop), AV_OPT_TYPE_INT, {0}, 0, 65535, ENC },
00355     { NULL },
00356 };
00357 
00358 static const AVClass gif_muxer_class = {
00359     .class_name = "GIF muxer",
00360     .item_name  = av_default_item_name,
00361     .version    = LIBAVUTIL_VERSION_INT,
00362     .option     = options,
00363 };
00364 
00365 AVOutputFormat ff_gif_muxer = {
00366     .name              = "gif",
00367     .long_name         = NULL_IF_CONFIG_SMALL("GIF Animation"),
00368     .mime_type         = "image/gif",
00369     .extensions        = "gif",
00370     .priv_data_size    = sizeof(GIFContext),
00371     .audio_codec       = CODEC_ID_NONE,
00372     .video_codec       = CODEC_ID_RAWVIDEO,
00373     .write_header      = gif_write_header,
00374     .write_packet      = gif_write_packet,
00375     .write_trailer     = gif_write_trailer,
00376     .priv_class = &gif_muxer_class,
00377 };
Generated on Thu Jul 11 2013 15:38:19 for Libav by doxygen 1.7.1