# HG changeset patch # User michael # Date 1119059544 0 # Node ID 80aec794c2eddc6d524c58d4ea0276b2966f8696 # Parent 411b75055a4326c6b8dfcaba04739dda0e9223d9 Animated GIF looping patch by (Todd Kirby // ffmpeg.php gmail com) diff -r 411b75055a43 -r 80aec794c2ed avformat.h --- a/avformat.h Wed Jun 15 20:50:12 2005 +0000 +++ b/avformat.h Sat Jun 18 01:52:24 2005 +0000 @@ -5,7 +5,7 @@ extern "C" { #endif -#define LIBAVFORMAT_BUILD 4626 +#define LIBAVFORMAT_BUILD 4627 #define LIBAVFORMAT_VERSION_INT FFMPEG_VERSION_INT #define LIBAVFORMAT_VERSION FFMPEG_VERSION @@ -327,6 +327,12 @@ int packet_size; int preload; int max_delay; + +#define AVFMT_NOOUTPUTLOOP -1 +#define AVFMT_INFINITEOUTPUTLOOP 0 + /* number of times to loop output in formats that support it */ + int loop_output; + } AVFormatContext; typedef struct AVPacketList { diff -r 411b75055a43 -r 80aec794c2ed gif.c --- a/gif.c Wed Jun 15 20:50:12 2005 +0000 +++ b/gif.c Sat Jun 18 01:52:24 2005 +0000 @@ -43,8 +43,9 @@ /* bitstream minipacket size */ #define GIF_CHUNKS 100 -/* slows down the decoding (and some browsers doesn't like it) */ -/* #define GIF_ADD_APP_HEADER */ +/* slows down the decoding (and some browsers don't like it) */ +/* 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 */ +#define GIF_ADD_APP_HEADER // required to enable looping of animated gif typedef struct { unsigned char r; @@ -169,7 +170,8 @@ /* GIF header */ static int gif_image_write_header(ByteIOContext *pb, - int width, int height, uint32_t *palette) + int width, int height, int loop_count, + uint32_t *palette) { int i; unsigned int v; @@ -197,17 +199,37 @@ } } + /* update: this is the 'NETSCAPE EXTENSION' that allows for looped animated gif + see http://members.aol.com/royalef/gifabout.htm#net-extension + + byte 1 : 33 (hex 0x21) GIF Extension code + byte 2 : 255 (hex 0xFF) Application Extension Label + byte 3 : 11 (hex (0x0B) Length of Application Block + (eleven bytes of data to follow) + bytes 4 to 11 : "NETSCAPE" + bytes 12 to 14 : "2.0" + byte 15 : 3 (hex 0x03) Length of Data Sub-Block + (three bytes of data to follow) + byte 16 : 1 (hex 0x01) + bytes 17 to 18 : 0 to 65535, an unsigned integer in + lo-hi byte format. This indicate the + number of iterations the loop should + be executed. + bytes 19 : 0 (hex 0x00) a Data Sub-block Terminator + */ + /* application extension header */ - /* XXX: not really sure what to put in here... */ #ifdef GIF_ADD_APP_HEADER + if (loop_count >= 0 && loop_count <= 65535) { put_byte(pb, 0x21); put_byte(pb, 0xff); put_byte(pb, 0x0b); - put_tag(pb, "NETSCAPE2.0"); - put_byte(pb, 0x03); - put_byte(pb, 0x01); - put_byte(pb, 0x00); - put_byte(pb, 0x00); + put_tag(pb, "NETSCAPE2.0"); // bytes 4 to 14 + put_byte(pb, 0x03); // byte 15 + put_byte(pb, 0x01); // byte 16 + put_le16(pb, (uint16_t)loop_count); + put_byte(pb, 0x00); // byte 19 + } #endif return 0; } @@ -294,7 +316,7 @@ GIFContext *gif = s->priv_data; ByteIOContext *pb = &s->pb; AVCodecContext *enc, *video_enc; - int i, width, height/*, rate*/; + int i, width, height, loop_count /*, rate*/; /* XXX: do we reject audio streams or just ignore them ? if(s->nb_streams > 1) @@ -316,13 +338,14 @@ } else { width = video_enc->width; height = video_enc->height; + loop_count = s->loop_output; // rate = video_enc->time_base.den; } /* XXX: is it allowed ? seems to work so far... */ video_enc->pix_fmt = PIX_FMT_RGB24; - gif_image_write_header(pb, width, height, NULL); + gif_image_write_header(pb, width, height, loop_count, NULL); put_flush_packet(&s->pb); return 0; @@ -384,7 +407,7 @@ /* better than nothing gif image writer */ int gif_write(ByteIOContext *pb, AVImageInfo *info) { - gif_image_write_header(pb, info->width, info->height, + gif_image_write_header(pb, info->width, info->height, AVFMT_NOOUTPUTLOOP, (uint32_t *)info->pict.data[1]); gif_image_write_image(pb, 0, 0, info->width, info->height, info->pict.data[0], info->pict.linesize[0],