@ -44,6 +44,13 @@ typedef struct GIFDemuxContext {
*/
int min_delay ;
int default_delay ;
/**
* loop options
*/
int total_iter ;
int iter_count ;
int ignore_loop ;
} GIFDemuxContext ;
/**
@ -156,6 +163,17 @@ static int gif_read_ext(AVFormatContext *s)
/* skip the rest of the Graphic Control Extension block */
if ( ( ret = avio_skip ( pb , sb_size - 3 ) ) < 0 )
return ret ;
} else if ( ext_label = = GIF_APP_EXT_LABEL ) {
uint8_t netscape_ext [ sizeof ( NETSCAPE_EXT_STR ) - 1 + 2 ] ;
if ( ( sb_size = avio_r8 ( pb ) ) ! = strlen ( NETSCAPE_EXT_STR ) )
return 0 ;
ret = avio_read ( pb , netscape_ext , sizeof ( netscape_ext ) ) ;
if ( ret < sizeof ( netscape_ext ) )
return ret ;
gdc - > total_iter = avio_rl16 ( pb ) ;
if ( gdc - > total_iter = = 0 )
gdc - > total_iter = - 1 ;
}
if ( ( ret = gif_skip_subblocks ( pb ) ) < 0 )
@ -268,9 +286,12 @@ resync:
}
}
if ( ret > = 0 & & ! frame_parsed ) {
if ( ( ret > = 0 & & ! frame_parsed ) | | ret = = AVERROR_EOF ) {
/* This might happen when there is no image block
* between extension blocks and GIF_TRAILER or EOF */
if ( ! gdc - > ignore_loop & & ( block_label = = GIF_TRAILER | | url_feof ( pb ) )
& & ( gdc - > total_iter < 0 | | + + gdc - > iter_count < gdc - > total_iter ) )
return avio_seek ( pb , 0 , SEEK_SET ) ;
return AVERROR_EOF ;
} else
return ret ;
@ -279,6 +300,7 @@ resync:
static const AVOption options [ ] = {
{ " min_delay " , " minimum valid delay between frames (in hundredths of second) " , offsetof ( GIFDemuxContext , min_delay ) , AV_OPT_TYPE_INT , { . i64 = GIF_MIN_DELAY } , 0 , 100 * 60 , AV_OPT_FLAG_DECODING_PARAM } ,
{ " default_delay " , " default delay between frames (in hundredths of second) " , offsetof ( GIFDemuxContext , default_delay ) , AV_OPT_TYPE_INT , { . i64 = GIF_DEFAULT_DELAY } , 0 , 100 * 60 , AV_OPT_FLAG_DECODING_PARAM } ,
{ " ignore_loop " , " ignore loop setting (netscape extension) " , offsetof ( GIFDemuxContext , ignore_loop ) , AV_OPT_TYPE_INT , { . i64 = 1 } , 0 , 1 , AV_OPT_FLAG_DECODING_PARAM } ,
{ NULL } ,
} ;