From 8108551a5c4c4c9696582c73d000f94b12cd95a5 Mon Sep 17 00:00:00 2001 From: Todd Kirby Date: Sat, 18 Jun 2005 01:52:24 +0000 Subject: [PATCH] Animated GIF looping patch by (Todd Kirby // ffmpeg.php gmail com) Originally committed as revision 4383 to svn://svn.ffmpeg.org/ffmpeg/trunk --- doc/ffmpeg-doc.texi | 3 +++ ffmpeg.c | 3 +++ libavformat/avformat.h | 8 ++++++- libavformat/gif.c | 47 +++++++++++++++++++++++++++++++----------- 4 files changed, 48 insertions(+), 13 deletions(-) diff --git a/doc/ffmpeg-doc.texi b/doc/ffmpeg-doc.texi index c0771eb8d1..31e73ef6d5 100644 --- a/doc/ffmpeg-doc.texi +++ b/doc/ffmpeg-doc.texi @@ -498,6 +498,9 @@ read input at native frame rate. Mainly used to simulate a grab device. @item -loop loop over the input stream. Currently it works only for image streams. This option is used for ffserver automatic testing. +@item -loop_output number_of_times +Repeatedly loop output for formats that support looping such as animated gif +(Zero will loop the output infinitely) @end table @node FFmpeg formula evaluator diff --git a/ffmpeg.c b/ffmpeg.c index 4b78ff5802..a8551323ad 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -209,6 +209,7 @@ static int frame_skip_factor= 0; static int frame_skip_exp= 0; static int frame_skip_cmp= FF_CMP_DCTMAX; extern int loop_input; /* currently a hack */ +static int loop_output = AVFMT_NOOUTPUTLOOP; static int gray_only = 0; static int gop_size = 12; @@ -3689,6 +3690,7 @@ static void opt_output_file(const char *filename) oc->mux_rate= mux_rate; oc->preload= (int)(mux_preload*AV_TIME_BASE); oc->max_delay= (int)(mux_max_delay*AV_TIME_BASE); + oc->loop_output = loop_output; /* reset some options */ file_oformat = NULL; @@ -4177,6 +4179,7 @@ const OptionDef options[] = { { "bitexact", OPT_EXPERT, {(void*)opt_bitexact}, "only use bit exact algorithms (for codec testing)" }, { "re", OPT_BOOL | OPT_EXPERT, {(void*)&rate_emu}, "read input at native frame rate", "" }, { "loop", OPT_BOOL | OPT_EXPERT, {(void*)&loop_input}, "loop (current only works with images)" }, + { "loop_output", HAS_ARG | OPT_INT | OPT_EXPERT, {(void*)&loop_output}, "number of times to loop output in formats that support looping (0 loops forever)", "" }, { "v", HAS_ARG, {(void*)opt_verbose}, "control amount of logging", "verbose" }, { "target", HAS_ARG, {(void*)opt_target}, "specify target file type (\"vcd\", \"svcd\", \"dvd\", \"dv\", \"pal-vcd\", \"ntsc-svcd\", ...)", "type" }, { "threads", HAS_ARG | OPT_EXPERT, {(void*)opt_thread_count}, "thread count", "count" }, diff --git a/libavformat/avformat.h b/libavformat/avformat.h index f81a4f37fa..705ff008b9 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -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 @@ typedef struct AVFormatContext { 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 --git a/libavformat/gif.c b/libavformat/gif.c index 0cb9b9a1ee..1248139d79 100644 --- a/libavformat/gif.c +++ b/libavformat/gif.c @@ -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 @@ static void gif_flush_put_bits_rev(PutBitContext *s) /* 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 @@ static int gif_image_write_header(ByteIOContext *pb, } } + /* 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 @@ static int gif_write_header(AVFormatContext *s) 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 @@ static int gif_write_header(AVFormatContext *s) } 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 @@ static int gif_write_trailer(AVFormatContext *s) /* 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],