00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 
00035 
00036 
00037 
00038 
00039 
00040 
00041 
00042 #include "avformat.h"
00043 
00044 
00045 
00046 #define BITSTREAM_WRITER_LE
00047 
00048 #include "libavcodec/put_bits.h"
00049 
00050 
00051 #define GIF_CHUNKS 100
00052 
00053 
00054 
00055 #define GIF_ADD_APP_HEADER // required to enable looping of animated gif
00056 
00057 typedef struct {
00058     unsigned char r;
00059     unsigned char g;
00060     unsigned char b;
00061 } rgb_triplet;
00062 
00063 
00064 
00065 
00066 
00067 
00068 
00069 
00070 static const rgb_triplet gif_clut[216] = {
00071     { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x33 }, { 0x00, 0x00, 0x66 }, { 0x00, 0x00, 0x99 }, { 0x00, 0x00, 0xcc }, { 0x00, 0x00, 0xff },
00072     { 0x00, 0x33, 0x00 }, { 0x00, 0x33, 0x33 }, { 0x00, 0x33, 0x66 }, { 0x00, 0x33, 0x99 }, { 0x00, 0x33, 0xcc }, { 0x00, 0x33, 0xff },
00073     { 0x00, 0x66, 0x00 }, { 0x00, 0x66, 0x33 }, { 0x00, 0x66, 0x66 }, { 0x00, 0x66, 0x99 }, { 0x00, 0x66, 0xcc }, { 0x00, 0x66, 0xff },
00074     { 0x00, 0x99, 0x00 }, { 0x00, 0x99, 0x33 }, { 0x00, 0x99, 0x66 }, { 0x00, 0x99, 0x99 }, { 0x00, 0x99, 0xcc }, { 0x00, 0x99, 0xff },
00075     { 0x00, 0xcc, 0x00 }, { 0x00, 0xcc, 0x33 }, { 0x00, 0xcc, 0x66 }, { 0x00, 0xcc, 0x99 }, { 0x00, 0xcc, 0xcc }, { 0x00, 0xcc, 0xff },
00076     { 0x00, 0xff, 0x00 }, { 0x00, 0xff, 0x33 }, { 0x00, 0xff, 0x66 }, { 0x00, 0xff, 0x99 }, { 0x00, 0xff, 0xcc }, { 0x00, 0xff, 0xff },
00077     { 0x33, 0x00, 0x00 }, { 0x33, 0x00, 0x33 }, { 0x33, 0x00, 0x66 }, { 0x33, 0x00, 0x99 }, { 0x33, 0x00, 0xcc }, { 0x33, 0x00, 0xff },
00078     { 0x33, 0x33, 0x00 }, { 0x33, 0x33, 0x33 }, { 0x33, 0x33, 0x66 }, { 0x33, 0x33, 0x99 }, { 0x33, 0x33, 0xcc }, { 0x33, 0x33, 0xff },
00079     { 0x33, 0x66, 0x00 }, { 0x33, 0x66, 0x33 }, { 0x33, 0x66, 0x66 }, { 0x33, 0x66, 0x99 }, { 0x33, 0x66, 0xcc }, { 0x33, 0x66, 0xff },
00080     { 0x33, 0x99, 0x00 }, { 0x33, 0x99, 0x33 }, { 0x33, 0x99, 0x66 }, { 0x33, 0x99, 0x99 }, { 0x33, 0x99, 0xcc }, { 0x33, 0x99, 0xff },
00081     { 0x33, 0xcc, 0x00 }, { 0x33, 0xcc, 0x33 }, { 0x33, 0xcc, 0x66 }, { 0x33, 0xcc, 0x99 }, { 0x33, 0xcc, 0xcc }, { 0x33, 0xcc, 0xff },
00082     { 0x33, 0xff, 0x00 }, { 0x33, 0xff, 0x33 }, { 0x33, 0xff, 0x66 }, { 0x33, 0xff, 0x99 }, { 0x33, 0xff, 0xcc }, { 0x33, 0xff, 0xff },
00083     { 0x66, 0x00, 0x00 }, { 0x66, 0x00, 0x33 }, { 0x66, 0x00, 0x66 }, { 0x66, 0x00, 0x99 }, { 0x66, 0x00, 0xcc }, { 0x66, 0x00, 0xff },
00084     { 0x66, 0x33, 0x00 }, { 0x66, 0x33, 0x33 }, { 0x66, 0x33, 0x66 }, { 0x66, 0x33, 0x99 }, { 0x66, 0x33, 0xcc }, { 0x66, 0x33, 0xff },
00085     { 0x66, 0x66, 0x00 }, { 0x66, 0x66, 0x33 }, { 0x66, 0x66, 0x66 }, { 0x66, 0x66, 0x99 }, { 0x66, 0x66, 0xcc }, { 0x66, 0x66, 0xff },
00086     { 0x66, 0x99, 0x00 }, { 0x66, 0x99, 0x33 }, { 0x66, 0x99, 0x66 }, { 0x66, 0x99, 0x99 }, { 0x66, 0x99, 0xcc }, { 0x66, 0x99, 0xff },
00087     { 0x66, 0xcc, 0x00 }, { 0x66, 0xcc, 0x33 }, { 0x66, 0xcc, 0x66 }, { 0x66, 0xcc, 0x99 }, { 0x66, 0xcc, 0xcc }, { 0x66, 0xcc, 0xff },
00088     { 0x66, 0xff, 0x00 }, { 0x66, 0xff, 0x33 }, { 0x66, 0xff, 0x66 }, { 0x66, 0xff, 0x99 }, { 0x66, 0xff, 0xcc }, { 0x66, 0xff, 0xff },
00089     { 0x99, 0x00, 0x00 }, { 0x99, 0x00, 0x33 }, { 0x99, 0x00, 0x66 }, { 0x99, 0x00, 0x99 }, { 0x99, 0x00, 0xcc }, { 0x99, 0x00, 0xff },
00090     { 0x99, 0x33, 0x00 }, { 0x99, 0x33, 0x33 }, { 0x99, 0x33, 0x66 }, { 0x99, 0x33, 0x99 }, { 0x99, 0x33, 0xcc }, { 0x99, 0x33, 0xff },
00091     { 0x99, 0x66, 0x00 }, { 0x99, 0x66, 0x33 }, { 0x99, 0x66, 0x66 }, { 0x99, 0x66, 0x99 }, { 0x99, 0x66, 0xcc }, { 0x99, 0x66, 0xff },
00092     { 0x99, 0x99, 0x00 }, { 0x99, 0x99, 0x33 }, { 0x99, 0x99, 0x66 }, { 0x99, 0x99, 0x99 }, { 0x99, 0x99, 0xcc }, { 0x99, 0x99, 0xff },
00093     { 0x99, 0xcc, 0x00 }, { 0x99, 0xcc, 0x33 }, { 0x99, 0xcc, 0x66 }, { 0x99, 0xcc, 0x99 }, { 0x99, 0xcc, 0xcc }, { 0x99, 0xcc, 0xff },
00094     { 0x99, 0xff, 0x00 }, { 0x99, 0xff, 0x33 }, { 0x99, 0xff, 0x66 }, { 0x99, 0xff, 0x99 }, { 0x99, 0xff, 0xcc }, { 0x99, 0xff, 0xff },
00095     { 0xcc, 0x00, 0x00 }, { 0xcc, 0x00, 0x33 }, { 0xcc, 0x00, 0x66 }, { 0xcc, 0x00, 0x99 }, { 0xcc, 0x00, 0xcc }, { 0xcc, 0x00, 0xff },
00096     { 0xcc, 0x33, 0x00 }, { 0xcc, 0x33, 0x33 }, { 0xcc, 0x33, 0x66 }, { 0xcc, 0x33, 0x99 }, { 0xcc, 0x33, 0xcc }, { 0xcc, 0x33, 0xff },
00097     { 0xcc, 0x66, 0x00 }, { 0xcc, 0x66, 0x33 }, { 0xcc, 0x66, 0x66 }, { 0xcc, 0x66, 0x99 }, { 0xcc, 0x66, 0xcc }, { 0xcc, 0x66, 0xff },
00098     { 0xcc, 0x99, 0x00 }, { 0xcc, 0x99, 0x33 }, { 0xcc, 0x99, 0x66 }, { 0xcc, 0x99, 0x99 }, { 0xcc, 0x99, 0xcc }, { 0xcc, 0x99, 0xff },
00099     { 0xcc, 0xcc, 0x00 }, { 0xcc, 0xcc, 0x33 }, { 0xcc, 0xcc, 0x66 }, { 0xcc, 0xcc, 0x99 }, { 0xcc, 0xcc, 0xcc }, { 0xcc, 0xcc, 0xff },
00100     { 0xcc, 0xff, 0x00 }, { 0xcc, 0xff, 0x33 }, { 0xcc, 0xff, 0x66 }, { 0xcc, 0xff, 0x99 }, { 0xcc, 0xff, 0xcc }, { 0xcc, 0xff, 0xff },
00101     { 0xff, 0x00, 0x00 }, { 0xff, 0x00, 0x33 }, { 0xff, 0x00, 0x66 }, { 0xff, 0x00, 0x99 }, { 0xff, 0x00, 0xcc }, { 0xff, 0x00, 0xff },
00102     { 0xff, 0x33, 0x00 }, { 0xff, 0x33, 0x33 }, { 0xff, 0x33, 0x66 }, { 0xff, 0x33, 0x99 }, { 0xff, 0x33, 0xcc }, { 0xff, 0x33, 0xff },
00103     { 0xff, 0x66, 0x00 }, { 0xff, 0x66, 0x33 }, { 0xff, 0x66, 0x66 }, { 0xff, 0x66, 0x99 }, { 0xff, 0x66, 0xcc }, { 0xff, 0x66, 0xff },
00104     { 0xff, 0x99, 0x00 }, { 0xff, 0x99, 0x33 }, { 0xff, 0x99, 0x66 }, { 0xff, 0x99, 0x99 }, { 0xff, 0x99, 0xcc }, { 0xff, 0x99, 0xff },
00105     { 0xff, 0xcc, 0x00 }, { 0xff, 0xcc, 0x33 }, { 0xff, 0xcc, 0x66 }, { 0xff, 0xcc, 0x99 }, { 0xff, 0xcc, 0xcc }, { 0xff, 0xcc, 0xff },
00106     { 0xff, 0xff, 0x00 }, { 0xff, 0xff, 0x33 }, { 0xff, 0xff, 0x66 }, { 0xff, 0xff, 0x99 }, { 0xff, 0xff, 0xcc }, { 0xff, 0xff, 0xff },
00107 };
00108 
00109 
00110 static int gif_image_write_header(AVIOContext *pb,
00111                                   int width, int height, int loop_count,
00112                                   uint32_t *palette)
00113 {
00114     int i;
00115     unsigned int v;
00116 
00117     avio_write(pb, "GIF", 3);
00118     avio_write(pb, "89a", 3);
00119     avio_wl16(pb, width);
00120     avio_wl16(pb, height);
00121 
00122     avio_w8(pb, 0xf7); 
00123     avio_w8(pb, 0x1f); 
00124     avio_w8(pb, 0);    
00125 
00126     
00127     if (!palette) {
00128         avio_write(pb, (const unsigned char *)gif_clut, 216*3);
00129         for(i=0;i<((256-216)*3);i++)
00130             avio_w8(pb, 0);
00131     } else {
00132         for(i=0;i<256;i++) {
00133             v = palette[i];
00134             avio_w8(pb, (v >> 16) & 0xff);
00135             avio_w8(pb, (v >> 8) & 0xff);
00136             avio_w8(pb, (v) & 0xff);
00137         }
00138     }
00139 
00140         
00141 
00142 
00143 
00144 
00145 
00146 
00147 
00148 
00149 
00150 
00151 
00152 
00153 
00154 
00155 
00156 
00157 
00158 
00159     
00160 #ifdef GIF_ADD_APP_HEADER
00161     if (loop_count >= 0 && loop_count <= 65535) {
00162     avio_w8(pb, 0x21);
00163     avio_w8(pb, 0xff);
00164     avio_w8(pb, 0x0b);
00165         avio_write(pb, "NETSCAPE2.0", sizeof("NETSCAPE2.0") - 1);  
00166         avio_w8(pb, 0x03); 
00167         avio_w8(pb, 0x01); 
00168         avio_wl16(pb, (uint16_t)loop_count);
00169         avio_w8(pb, 0x00); 
00170     }
00171 #endif
00172     return 0;
00173 }
00174 
00175 
00176 static inline unsigned char gif_clut_index(uint8_t r, uint8_t g, uint8_t b)
00177 {
00178     return (((r) / 47) % 6) * 6 * 6 + (((g) / 47) % 6) * 6 + (((b) / 47) % 6);
00179 }
00180 
00181 
00182 static int gif_image_write_image(AVIOContext *pb,
00183                                  int x1, int y1, int width, int height,
00184                                  const uint8_t *buf, int linesize, int pix_fmt)
00185 {
00186     PutBitContext p;
00187     uint8_t buffer[200]; 
00188     int i, left, w, v;
00189     const uint8_t *ptr;
00190     
00191 
00192     avio_w8(pb, 0x2c);
00193     avio_wl16(pb, x1);
00194     avio_wl16(pb, y1);
00195     avio_wl16(pb, width);
00196     avio_wl16(pb, height);
00197     avio_w8(pb, 0x00); 
00198     
00199 
00200     avio_w8(pb, 0x08);
00201 
00202     left= width * height;
00203 
00204     init_put_bits(&p, buffer, 130);
00205 
00206 
00207 
00208 
00209 
00210     ptr = buf;
00211     w = width;
00212     while(left>0) {
00213 
00214         put_bits(&p, 9, 0x0100); 
00215 
00216         for(i=(left<GIF_CHUNKS)?left:GIF_CHUNKS;i;i--) {
00217             if (pix_fmt == PIX_FMT_RGB24) {
00218                 v = gif_clut_index(ptr[0], ptr[1], ptr[2]);
00219                 ptr+=3;
00220             } else {
00221                 v = *ptr++;
00222             }
00223             put_bits(&p, 9, v);
00224             if (--w == 0) {
00225                 w = width;
00226                 buf += linesize;
00227                 ptr = buf;
00228             }
00229         }
00230 
00231         if(left<=GIF_CHUNKS) {
00232             put_bits(&p, 9, 0x101); 
00233             flush_put_bits(&p);
00234         }
00235         if(put_bits_ptr(&p) - p.buf > 0) {
00236             avio_w8(pb, put_bits_ptr(&p) - p.buf); 
00237             avio_write(pb, p.buf, put_bits_ptr(&p) - p.buf); 
00238             p.buf_ptr = p.buf; 
00239         }
00240         left-=GIF_CHUNKS;
00241     }
00242     avio_w8(pb, 0x00); 
00243 
00244     return 0;
00245 }
00246 
00247 typedef struct {
00248     int64_t time, file_time;
00249     uint8_t buffer[100]; 
00250 } GIFContext;
00251 
00252 static int gif_write_header(AVFormatContext *s)
00253 {
00254     GIFContext *gif = s->priv_data;
00255     AVIOContext *pb = s->pb;
00256     AVCodecContext *enc, *video_enc;
00257     int i, width, height, loop_count ;
00258 
00259 
00260 
00261 
00262 
00263     gif->time = 0;
00264     gif->file_time = 0;
00265 
00266     video_enc = NULL;
00267     for(i=0;i<s->nb_streams;i++) {
00268         enc = s->streams[i]->codec;
00269         if (enc->codec_type != AVMEDIA_TYPE_AUDIO)
00270             video_enc = enc;
00271     }
00272 
00273     if (!video_enc) {
00274         av_free(gif);
00275         return -1;
00276     } else {
00277         width = video_enc->width;
00278         height = video_enc->height;
00279         loop_count = s->loop_output;
00280 
00281     }
00282 
00283     if (video_enc->pix_fmt != PIX_FMT_RGB24) {
00284         av_log(s, AV_LOG_ERROR, "ERROR: gif only handles the rgb24 pixel format. Use -pix_fmt rgb24.\n");
00285         return AVERROR(EIO);
00286     }
00287 
00288     gif_image_write_header(pb, width, height, loop_count, NULL);
00289 
00290     avio_flush(s->pb);
00291     return 0;
00292 }
00293 
00294 static int gif_write_video(AVFormatContext *s,
00295                            AVCodecContext *enc, const uint8_t *buf, int size)
00296 {
00297     AVIOContext *pb = s->pb;
00298     int jiffies;
00299 
00300     
00301     avio_w8(pb, 0x21);
00302     avio_w8(pb, 0xf9);
00303     avio_w8(pb, 0x04); 
00304     avio_w8(pb, 0x04); 
00305 
00306     
00307     
00308     
00309     
00310     
00311     jiffies = (70*enc->time_base.num/enc->time_base.den) - 1;
00312 
00313     avio_wl16(pb, jiffies);
00314 
00315     avio_w8(pb, 0x1f); 
00316     avio_w8(pb, 0x00);
00317 
00318     gif_image_write_image(pb, 0, 0, enc->width, enc->height,
00319                           buf, enc->width * 3, PIX_FMT_RGB24);
00320 
00321     avio_flush(s->pb);
00322     return 0;
00323 }
00324 
00325 static int gif_write_packet(AVFormatContext *s, AVPacket *pkt)
00326 {
00327     AVCodecContext *codec = s->streams[pkt->stream_index]->codec;
00328     if (codec->codec_type == AVMEDIA_TYPE_AUDIO)
00329         return 0; 
00330     else
00331         return gif_write_video(s, codec, pkt->data, pkt->size);
00332 }
00333 
00334 static int gif_write_trailer(AVFormatContext *s)
00335 {
00336     AVIOContext *pb = s->pb;
00337 
00338     avio_w8(pb, 0x3b);
00339     avio_flush(s->pb);
00340     return 0;
00341 }
00342 
00343 AVOutputFormat ff_gif_muxer = {
00344     "gif",
00345     NULL_IF_CONFIG_SMALL("GIF Animation"),
00346     "image/gif",
00347     "gif",
00348     sizeof(GIFContext),
00349     CODEC_ID_NONE,
00350     CODEC_ID_RAWVIDEO,
00351     gif_write_header,
00352     gif_write_packet,
00353     gif_write_trailer,
00354 };