|
|
@@ -44,6 +44,13 @@ typedef struct GIFDemuxContext { |
|
|
*/ |
|
|
*/ |
|
|
int min_delay; |
|
|
int min_delay; |
|
|
int default_delay; |
|
|
int default_delay; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* loop options |
|
|
|
|
|
*/ |
|
|
|
|
|
int total_iter; |
|
|
|
|
|
int iter_count; |
|
|
|
|
|
int ignore_loop; |
|
|
} GIFDemuxContext; |
|
|
} GIFDemuxContext; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@@ -156,6 +163,17 @@ static int gif_read_ext(AVFormatContext *s) |
|
|
/* skip the rest of the Graphic Control Extension block */ |
|
|
/* skip the rest of the Graphic Control Extension block */ |
|
|
if ((ret = avio_skip(pb, sb_size - 3)) < 0 ) |
|
|
if ((ret = avio_skip(pb, sb_size - 3)) < 0 ) |
|
|
return ret; |
|
|
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) |
|
|
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 |
|
|
/* This might happen when there is no image block |
|
|
* between extension blocks and GIF_TRAILER or EOF */ |
|
|
* 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; |
|
|
return AVERROR_EOF; |
|
|
} else |
|
|
} else |
|
|
return ret; |
|
|
return ret; |
|
|
@@ -279,6 +300,7 @@ resync: |
|
|
static const AVOption options[] = { |
|
|
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 }, |
|
|
{ "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 }, |
|
|
{ "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 }, |
|
|
{ NULL }, |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|