Commit 24edaa7f authored by michael's avatar michael

Animated GIF looping patch by (Todd Kirby // ffmpeg.php gmail com)


git-svn-id: file:///var/local/repositories/ffmpeg/trunk@4383 9553f0bf-9b14-0410-a0b8-cfaf0461ba5b
parent 43fd36b9
...@@ -498,6 +498,9 @@ read input at native frame rate. Mainly used to simulate a grab device. ...@@ -498,6 +498,9 @@ read input at native frame rate. Mainly used to simulate a grab device.
@item -loop @item -loop
loop over the input stream. Currently it works only for image loop over the input stream. Currently it works only for image
streams. This option is used for ffserver automatic testing. 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 @end table
@node FFmpeg formula evaluator @node FFmpeg formula evaluator
......
...@@ -209,6 +209,7 @@ static int frame_skip_factor= 0; ...@@ -209,6 +209,7 @@ static int frame_skip_factor= 0;
static int frame_skip_exp= 0; static int frame_skip_exp= 0;
static int frame_skip_cmp= FF_CMP_DCTMAX; static int frame_skip_cmp= FF_CMP_DCTMAX;
extern int loop_input; /* currently a hack */ extern int loop_input; /* currently a hack */
static int loop_output = AVFMT_NOOUTPUTLOOP;
static int gray_only = 0; static int gray_only = 0;
static int gop_size = 12; static int gop_size = 12;
...@@ -3689,6 +3690,7 @@ static void opt_output_file(const char *filename) ...@@ -3689,6 +3690,7 @@ static void opt_output_file(const char *filename)
oc->mux_rate= mux_rate; oc->mux_rate= mux_rate;
oc->preload= (int)(mux_preload*AV_TIME_BASE); oc->preload= (int)(mux_preload*AV_TIME_BASE);
oc->max_delay= (int)(mux_max_delay*AV_TIME_BASE); oc->max_delay= (int)(mux_max_delay*AV_TIME_BASE);
oc->loop_output = loop_output;
/* reset some options */ /* reset some options */
file_oformat = NULL; file_oformat = NULL;
...@@ -4177,6 +4179,7 @@ const OptionDef options[] = { ...@@ -4177,6 +4179,7 @@ const OptionDef options[] = {
{ "bitexact", OPT_EXPERT, {(void*)opt_bitexact}, "only use bit exact algorithms (for codec testing)" }, { "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", "" }, { "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", 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" }, { "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" }, { "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" }, { "threads", HAS_ARG | OPT_EXPERT, {(void*)opt_thread_count}, "thread count", "count" },
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
extern "C" { extern "C" {
#endif #endif
#define LIBAVFORMAT_BUILD 4626 #define LIBAVFORMAT_BUILD 4627
#define LIBAVFORMAT_VERSION_INT FFMPEG_VERSION_INT #define LIBAVFORMAT_VERSION_INT FFMPEG_VERSION_INT
#define LIBAVFORMAT_VERSION FFMPEG_VERSION #define LIBAVFORMAT_VERSION FFMPEG_VERSION
...@@ -327,6 +327,12 @@ typedef struct AVFormatContext { ...@@ -327,6 +327,12 @@ typedef struct AVFormatContext {
int packet_size; int packet_size;
int preload; int preload;
int max_delay; 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; } AVFormatContext;
typedef struct AVPacketList { typedef struct AVPacketList {
......
...@@ -43,8 +43,9 @@ ...@@ -43,8 +43,9 @@
/* bitstream minipacket size */ /* bitstream minipacket size */
#define GIF_CHUNKS 100 #define GIF_CHUNKS 100
/* slows down the decoding (and some browsers doesn't like it) */ /* slows down the decoding (and some browsers don't like it) */
/* #define GIF_ADD_APP_HEADER */ /* 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 { typedef struct {
unsigned char r; unsigned char r;
...@@ -169,7 +170,8 @@ static void gif_flush_put_bits_rev(PutBitContext *s) ...@@ -169,7 +170,8 @@ static void gif_flush_put_bits_rev(PutBitContext *s)
/* GIF header */ /* GIF header */
static int gif_image_write_header(ByteIOContext *pb, 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; int i;
unsigned int v; unsigned int v;
...@@ -197,17 +199,37 @@ static int gif_image_write_header(ByteIOContext *pb, ...@@ -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 */ /* application extension header */
/* XXX: not really sure what to put in here... */
#ifdef GIF_ADD_APP_HEADER #ifdef GIF_ADD_APP_HEADER
if (loop_count >= 0 && loop_count <= 65535) {
put_byte(pb, 0x21); put_byte(pb, 0x21);
put_byte(pb, 0xff); put_byte(pb, 0xff);
put_byte(pb, 0x0b); put_byte(pb, 0x0b);
put_tag(pb, "NETSCAPE2.0"); put_tag(pb, "NETSCAPE2.0"); // bytes 4 to 14
put_byte(pb, 0x03); put_byte(pb, 0x03); // byte 15
put_byte(pb, 0x01); put_byte(pb, 0x01); // byte 16
put_byte(pb, 0x00); put_le16(pb, (uint16_t)loop_count);
put_byte(pb, 0x00); put_byte(pb, 0x00); // byte 19
}
#endif #endif
return 0; return 0;
} }
...@@ -294,7 +316,7 @@ static int gif_write_header(AVFormatContext *s) ...@@ -294,7 +316,7 @@ static int gif_write_header(AVFormatContext *s)
GIFContext *gif = s->priv_data; GIFContext *gif = s->priv_data;
ByteIOContext *pb = &s->pb; ByteIOContext *pb = &s->pb;
AVCodecContext *enc, *video_enc; 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 ? /* XXX: do we reject audio streams or just ignore them ?
if(s->nb_streams > 1) if(s->nb_streams > 1)
...@@ -316,13 +338,14 @@ static int gif_write_header(AVFormatContext *s) ...@@ -316,13 +338,14 @@ static int gif_write_header(AVFormatContext *s)
} else { } else {
width = video_enc->width; width = video_enc->width;
height = video_enc->height; height = video_enc->height;
loop_count = s->loop_output;
// rate = video_enc->time_base.den; // rate = video_enc->time_base.den;
} }
/* XXX: is it allowed ? seems to work so far... */ /* XXX: is it allowed ? seems to work so far... */
video_enc->pix_fmt = PIX_FMT_RGB24; 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); put_flush_packet(&s->pb);
return 0; return 0;
...@@ -384,7 +407,7 @@ static int gif_write_trailer(AVFormatContext *s) ...@@ -384,7 +407,7 @@ static int gif_write_trailer(AVFormatContext *s)
/* better than nothing gif image writer */ /* better than nothing gif image writer */
int gif_write(ByteIOContext *pb, AVImageInfo *info) 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]); (uint32_t *)info->pict.data[1]);
gif_image_write_image(pb, 0, 0, info->width, info->height, gif_image_write_image(pb, 0, 0, info->width, info->height,
info->pict.data[0], info->pict.linesize[0], info->pict.data[0], info->pict.linesize[0],
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment