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/bitstream.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(ByteIOContext *pb,
00111                                   int width, int height, int loop_count,
00112                                   uint32_t *palette)
00113 {
00114     int i;
00115     unsigned int v;
00116 
00117     put_tag(pb, "GIF");
00118     put_tag(pb, "89a");
00119     put_le16(pb, width);
00120     put_le16(pb, height);
00121 
00122     put_byte(pb, 0xf7); 
00123     put_byte(pb, 0x1f); 
00124     put_byte(pb, 0); 
00125 
00126     
00127     if (!palette) {
00128         put_buffer(pb, (const unsigned char *)gif_clut, 216*3);
00129         for(i=0;i<((256-216)*3);i++)
00130             put_byte(pb, 0);
00131     } else {
00132         for(i=0;i<256;i++) {
00133             v = palette[i];
00134             put_byte(pb, (v >> 16) & 0xff);
00135             put_byte(pb, (v >> 8) & 0xff);
00136             put_byte(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     put_byte(pb, 0x21);
00163     put_byte(pb, 0xff);
00164     put_byte(pb, 0x0b);
00165         put_tag(pb, "NETSCAPE2.0");  
00166         put_byte(pb, 0x03); 
00167         put_byte(pb, 0x01); 
00168         put_le16(pb, (uint16_t)loop_count);
00169         put_byte(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(ByteIOContext *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     put_byte(pb, 0x2c);
00193     put_le16(pb, x1);
00194     put_le16(pb, y1);
00195     put_le16(pb, width);
00196     put_le16(pb, height);
00197     put_byte(pb, 0x00); 
00198     
00199 
00200     put_byte(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(pbBufPtr(&p) - p.buf > 0) {
00236             put_byte(pb, pbBufPtr(&p) - p.buf); 
00237             put_buffer(pb, p.buf, pbBufPtr(&p) - p.buf); 
00238             p.buf_ptr = p.buf; 
00239         }
00240         left-=GIF_CHUNKS;
00241     }
00242     put_byte(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     ByteIOContext *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 != CODEC_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     put_flush_packet(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     ByteIOContext *pb = s->pb;
00298     GIFContext *gif = s->priv_data;
00299     int jiffies;
00300     int64_t delay;
00301 
00302     
00303     put_byte(pb, 0x21);
00304     put_byte(pb, 0xf9);
00305     put_byte(pb, 0x04); 
00306     put_byte(pb, 0x04); 
00307 
00308     
00309     
00310     delay = gif->file_time - gif->time;
00311 
00312     
00313     
00314     
00315     jiffies = (70*enc->time_base.num/enc->time_base.den) - 1;
00316 
00317     put_le16(pb, jiffies);
00318 
00319     put_byte(pb, 0x1f); 
00320     put_byte(pb, 0x00);
00321 
00322     gif_image_write_image(pb, 0, 0, enc->width, enc->height,
00323                           buf, enc->width * 3, PIX_FMT_RGB24);
00324 
00325     put_flush_packet(s->pb);
00326     return 0;
00327 }
00328 
00329 static int gif_write_packet(AVFormatContext *s, AVPacket *pkt)
00330 {
00331     AVCodecContext *codec = s->streams[pkt->stream_index]->codec;
00332     if (codec->codec_type == CODEC_TYPE_AUDIO)
00333         return 0; 
00334     else
00335         return gif_write_video(s, codec, pkt->data, pkt->size);
00336 }
00337 
00338 static int gif_write_trailer(AVFormatContext *s)
00339 {
00340     ByteIOContext *pb = s->pb;
00341 
00342     put_byte(pb, 0x3b);
00343     put_flush_packet(s->pb);
00344     return 0;
00345 }
00346 
00347 AVOutputFormat gif_muxer = {
00348     "gif",
00349     NULL_IF_CONFIG_SMALL("GIF Animation"),
00350     "image/gif",
00351     "gif",
00352     sizeof(GIFContext),
00353     CODEC_ID_NONE,
00354     CODEC_ID_RAWVIDEO,
00355     gif_write_header,
00356     gif_write_packet,
00357     gif_write_trailer,
00358 };