/src/ffmpeg/libavcodec/gif_parser.c
Line | Count | Source |
1 | | /* |
2 | | * GIF parser |
3 | | * Copyright (c) 2018 Paul B Mahol |
4 | | * |
5 | | * This file is part of FFmpeg. |
6 | | * |
7 | | * FFmpeg is free software; you can redistribute it and/or |
8 | | * modify it under the terms of the GNU Lesser General Public |
9 | | * License as published by the Free Software Foundation; either |
10 | | * version 2.1 of the License, or (at your option) any later version. |
11 | | * |
12 | | * FFmpeg is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | | * Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General Public |
18 | | * License along with FFmpeg; if not, write to the Free Software |
19 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | | */ |
21 | | |
22 | | /** |
23 | | * @file |
24 | | * GIF parser |
25 | | */ |
26 | | |
27 | | #include "gif.h" |
28 | | #include "parser.h" |
29 | | #include "parser_internal.h" |
30 | | |
31 | | typedef enum GIFParseStates { |
32 | | GIF_HEADER = 1, |
33 | | GIF_EXTENSION, |
34 | | GIF_EXTENSION_BLOCK, |
35 | | GIF_IMAGE, |
36 | | GIF_IMAGE_BLOCK, |
37 | | } gif_states; |
38 | | |
39 | | typedef struct GIFParseContext { |
40 | | ParseContext pc; |
41 | | unsigned found_sig; |
42 | | int found_start; |
43 | | int found_end; |
44 | | int index; |
45 | | int state; |
46 | | int gct_flag; |
47 | | int gct_size; |
48 | | int block_size; |
49 | | int etype; |
50 | | int delay; |
51 | | int keyframe; |
52 | | } GIFParseContext; |
53 | | |
54 | | static int gif_find_frame_end(GIFParseContext *g, const uint8_t *buf, |
55 | | int buf_size, void *logctx) |
56 | 91.5k | { |
57 | 91.5k | ParseContext *pc = &g->pc; |
58 | 91.5k | int index, next = END_NOT_FOUND; |
59 | | |
60 | 41.8M | for (index = 0; index < buf_size; index++) { |
61 | 41.7M | if (!g->state) { |
62 | 22.4M | if (!memcmp(buf + index, gif87a_sig, 6) || |
63 | 22.4M | !memcmp(buf + index, gif89a_sig, 6)) { |
64 | 19.8k | g->state = GIF_HEADER; |
65 | 19.8k | g->found_sig++; |
66 | 19.8k | g->keyframe = 1; |
67 | 22.4M | } else if (buf[index] == GIF_EXTENSION_INTRODUCER) { |
68 | 43.5k | g->state = GIF_EXTENSION; |
69 | 43.5k | g->found_start = pc->frame_start_found = 1; |
70 | 22.3M | } else if (buf[index] == GIF_IMAGE_SEPARATOR) { |
71 | 49.3k | if (g->state != GIF_EXTENSION_BLOCK && g->found_start && |
72 | 47.2k | g->found_end && g->found_sig) { |
73 | 26.8k | next = index; |
74 | 26.8k | g->found_start = pc->frame_start_found = 1; |
75 | 26.8k | g->found_end = 0; |
76 | 26.8k | g->index = 0; |
77 | 26.8k | g->gct_flag = 0; |
78 | 26.8k | g->gct_size = 0; |
79 | 26.8k | g->state = GIF_IMAGE; |
80 | 26.8k | break; |
81 | 26.8k | } |
82 | 22.5k | g->state = GIF_IMAGE; |
83 | 22.3M | } else if (buf[index] == GIF_TRAILER) { |
84 | 154k | g->state = 0; |
85 | 154k | g->found_end = 1; |
86 | 154k | g->found_sig = 0; |
87 | 22.1M | } else { |
88 | 22.1M | g->found_sig = 0; |
89 | 22.1M | } |
90 | 22.4M | } |
91 | | |
92 | 41.7M | if (g->state == GIF_HEADER) { |
93 | 311k | if (g->index == 10) { |
94 | 19.8k | g->gct_flag = !!(buf[index] & 0x80); |
95 | 19.8k | g->gct_size = 3 * (1 << ((buf[index] & 0x07) + 1)); |
96 | 19.8k | } |
97 | 311k | if (g->index >= 12 + g->gct_flag * g->gct_size) { |
98 | 19.8k | g->state = 0; |
99 | 19.8k | g->index = 0; |
100 | 19.8k | g->gct_flag = 0; |
101 | 19.8k | g->gct_size = 0; |
102 | 19.8k | continue; |
103 | 19.8k | } |
104 | 291k | g->index++; |
105 | 41.4M | } else if (g->state == GIF_EXTENSION) { |
106 | 118k | if (g->found_start && g->found_end && g->found_sig) { |
107 | 6.22k | next = index; |
108 | 6.22k | g->found_start = pc->frame_start_found = 0; |
109 | 6.22k | g->found_end = 0; |
110 | 6.22k | g->index = 0; |
111 | 6.22k | g->gct_flag = 0; |
112 | 6.22k | g->gct_size = 0; |
113 | 6.22k | g->state = 0; |
114 | 6.22k | break; |
115 | 6.22k | } |
116 | 112k | if (g->index == 1) { |
117 | 37.3k | g->etype = buf[index]; |
118 | 37.3k | } |
119 | 112k | if (g->index >= 2) { |
120 | 37.3k | g->block_size = buf[index]; |
121 | 37.3k | g->index = 0; |
122 | 37.3k | g->state = GIF_EXTENSION_BLOCK; |
123 | 37.3k | continue; |
124 | 37.3k | } |
125 | 74.6k | g->index++; |
126 | 41.3M | } else if (g->state == GIF_IMAGE_BLOCK) { |
127 | 9.85M | if (!g->index) |
128 | 228k | g->block_size = buf[index]; |
129 | 9.85M | if (g->index >= g->block_size) { |
130 | 228k | g->index = 0; |
131 | 228k | if (!g->block_size) { |
132 | 49.0k | g->state = 0; |
133 | 49.0k | g->found_end = 1; |
134 | 49.0k | } |
135 | 228k | continue; |
136 | 228k | } |
137 | 9.62M | g->index++; |
138 | 31.4M | } else if (g->state == GIF_EXTENSION_BLOCK) { |
139 | 7.79M | if (g->etype == GIF_GCE_EXT_LABEL) { |
140 | 474k | if (g->index == 0) |
141 | 32.3k | g->delay = 0; |
142 | 474k | if (g->index >= 1 && g->index <= 2) { |
143 | 42.8k | g->delay |= buf[index] << (8 * (g->index - 1)); |
144 | 42.8k | } |
145 | 474k | } |
146 | 7.79M | if (g->index >= g->block_size) { |
147 | 207k | g->block_size = buf[index]; |
148 | 207k | g->index = 0; |
149 | 207k | if (!g->block_size) |
150 | 37.0k | g->state = 0; |
151 | 207k | continue; |
152 | 207k | } |
153 | 7.58M | g->index++; |
154 | 23.6M | } else if (g->state == GIF_IMAGE) { |
155 | 1.34M | if (g->index == 9) { |
156 | 49.2k | g->gct_flag = !!(buf[index] & 0x80); |
157 | 49.2k | g->gct_size = 3 * (1 << ((buf[index] & 0x07) + 1)); |
158 | 49.2k | } |
159 | 1.34M | if (g->index >= 10 + g->gct_flag * g->gct_size) { |
160 | 49.2k | g->state = GIF_IMAGE_BLOCK; |
161 | 49.2k | g->index = 0; |
162 | 49.2k | g->gct_flag = 0; |
163 | 49.2k | g->gct_size = 0; |
164 | 49.2k | continue; |
165 | 49.2k | } |
166 | 1.29M | g->index++; |
167 | 1.29M | } |
168 | 41.7M | } |
169 | | |
170 | 91.5k | return next; |
171 | 91.5k | } |
172 | | |
173 | | static int gif_parse(AVCodecParserContext *s, AVCodecContext *avctx, |
174 | | const uint8_t **poutbuf, int *poutbuf_size, |
175 | | const uint8_t *buf, int buf_size) |
176 | 91.5k | { |
177 | 91.5k | GIFParseContext *g = s->priv_data; |
178 | 91.5k | int next; |
179 | | |
180 | 91.5k | *poutbuf_size = 0; |
181 | 91.5k | *poutbuf = NULL; |
182 | | |
183 | 91.5k | if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) { |
184 | 0 | next = buf_size; |
185 | 91.5k | } else { |
186 | 91.5k | next = gif_find_frame_end(g, buf, buf_size, avctx); |
187 | 91.5k | if (ff_combine_frame(&g->pc, next, &buf, &buf_size) < 0) { |
188 | 57.6k | *poutbuf = NULL; |
189 | 57.6k | *poutbuf_size = 0; |
190 | 57.6k | return buf_size; |
191 | 57.6k | } |
192 | 91.5k | } |
193 | | |
194 | 33.8k | s->duration = g->delay ? g->delay : 10; |
195 | 33.8k | s->key_frame = g->keyframe; |
196 | 33.8k | s->pict_type = g->keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; |
197 | 33.8k | g->keyframe = 0; |
198 | | |
199 | 33.8k | *poutbuf = buf; |
200 | 33.8k | *poutbuf_size = buf_size; |
201 | 33.8k | return next; |
202 | 91.5k | } |
203 | | |
204 | | const FFCodecParser ff_gif_parser = { |
205 | | PARSER_CODEC_LIST(AV_CODEC_ID_GIF), |
206 | | .priv_data_size = sizeof(GIFParseContext), |
207 | | .parse = gif_parse, |
208 | | .close = ff_parse_close, |
209 | | }; |