Line | Branch | Exec | 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 | 44654 | static int gif_find_frame_end(GIFParseContext *g, const uint8_t *buf, | |
55 | int buf_size, void *logctx) | ||
56 | { | ||
57 | 44654 | ParseContext *pc = &g->pc; | |
58 | 44654 | int index, next = END_NOT_FOUND; | |
59 | |||
60 |
2/2✓ Branch 0 taken 43809551 times.
✓ Branch 1 taken 42865 times.
|
43852416 | for (index = 0; index < buf_size; index++) { |
61 |
2/2✓ Branch 0 taken 5596 times.
✓ Branch 1 taken 43803955 times.
|
43809551 | if (!g->state) { |
62 |
1/2✓ Branch 0 taken 5596 times.
✗ Branch 1 not taken.
|
5596 | if (!memcmp(buf + index, gif87a_sig, 6) || |
63 |
2/2✓ Branch 0 taken 50 times.
✓ Branch 1 taken 5546 times.
|
5596 | !memcmp(buf + index, gif89a_sig, 6)) { |
64 | 50 | g->state = GIF_HEADER; | |
65 | 50 | g->found_sig++; | |
66 | 50 | g->keyframe = 1; | |
67 |
2/2✓ Branch 0 taken 3674 times.
✓ Branch 1 taken 1872 times.
|
5546 | } else if (buf[index] == GIF_EXTENSION_INTRODUCER) { |
68 | 3674 | g->state = GIF_EXTENSION; | |
69 | 3674 | g->found_start = pc->frame_start_found = 1; | |
70 |
2/2✓ Branch 0 taken 1839 times.
✓ Branch 1 taken 33 times.
|
1872 | } else if (buf[index] == GIF_IMAGE_SEPARATOR) { |
71 |
2/4✓ Branch 0 taken 1839 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1839 times.
✗ Branch 3 not taken.
|
1839 | if (g->state != GIF_EXTENSION_BLOCK && g->found_start && |
72 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 1839 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
1839 | g->found_end && g->found_sig) { |
73 | ✗ | next = index; | |
74 | ✗ | g->found_start = pc->frame_start_found = 1; | |
75 | ✗ | g->found_end = 0; | |
76 | ✗ | g->index = 0; | |
77 | ✗ | g->gct_flag = 0; | |
78 | ✗ | g->gct_size = 0; | |
79 | ✗ | g->state = GIF_IMAGE; | |
80 | ✗ | break; | |
81 | } | ||
82 | 1839 | g->state = GIF_IMAGE; | |
83 |
1/2✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
|
33 | } else if (buf[index] == GIF_TRAILER) { |
84 | 33 | g->state = 0; | |
85 | 33 | g->found_end = 1; | |
86 | 33 | g->found_sig = 0; | |
87 | } else { | ||
88 | ✗ | g->found_sig = 0; | |
89 | } | ||
90 | } | ||
91 | |||
92 |
2/2✓ Branch 0 taken 33098 times.
✓ Branch 1 taken 43776453 times.
|
43809551 | if (g->state == GIF_HEADER) { |
93 |
2/2✓ Branch 0 taken 50 times.
✓ Branch 1 taken 33048 times.
|
33098 | if (g->index == 10) { |
94 | 50 | g->gct_flag = !!(buf[index] & 0x80); | |
95 | 50 | g->gct_size = 3 * (1 << ((buf[index] & 0x07) + 1)); | |
96 | } | ||
97 |
2/2✓ Branch 0 taken 50 times.
✓ Branch 1 taken 33048 times.
|
33098 | if (g->index >= 12 + g->gct_flag * g->gct_size) { |
98 | 50 | g->state = 0; | |
99 | 50 | g->index = 0; | |
100 | 50 | g->gct_flag = 0; | |
101 | 50 | g->gct_size = 0; | |
102 | 50 | continue; | |
103 | } | ||
104 | 33048 | g->index++; | |
105 |
2/2✓ Branch 0 taken 7444 times.
✓ Branch 1 taken 43769009 times.
|
43776453 | } else if (g->state == GIF_EXTENSION) { |
106 |
4/6✓ Branch 0 taken 7444 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1789 times.
✓ Branch 3 taken 5655 times.
✓ Branch 4 taken 1789 times.
✗ Branch 5 not taken.
|
7444 | if (g->found_start && g->found_end && g->found_sig) { |
107 | 1789 | next = index; | |
108 | 1789 | g->found_start = pc->frame_start_found = 0; | |
109 | 1789 | g->found_end = 0; | |
110 | 1789 | g->index = 0; | |
111 | 1789 | g->gct_flag = 0; | |
112 | 1789 | g->gct_size = 0; | |
113 | 1789 | g->state = 0; | |
114 | 1789 | break; | |
115 | } | ||
116 |
2/2✓ Branch 0 taken 1885 times.
✓ Branch 1 taken 3770 times.
|
5655 | if (g->index == 1) { |
117 | 1885 | g->etype = buf[index]; | |
118 | } | ||
119 |
2/2✓ Branch 0 taken 1885 times.
✓ Branch 1 taken 3770 times.
|
5655 | if (g->index >= 2) { |
120 | 1885 | g->block_size = buf[index]; | |
121 | 1885 | g->index = 0; | |
122 | 1885 | g->state = GIF_EXTENSION_BLOCK; | |
123 | 1885 | continue; | |
124 | } | ||
125 | 3770 | g->index++; | |
126 |
2/2✓ Branch 0 taken 42843797 times.
✓ Branch 1 taken 925212 times.
|
43769009 | } else if (g->state == GIF_IMAGE_BLOCK) { |
127 |
2/2✓ Branch 0 taken 170506 times.
✓ Branch 1 taken 42673291 times.
|
42843797 | if (!g->index) |
128 | 170506 | g->block_size = buf[index]; | |
129 |
2/2✓ Branch 0 taken 170489 times.
✓ Branch 1 taken 42673308 times.
|
42843797 | if (g->index >= g->block_size) { |
130 | 170489 | g->index = 0; | |
131 |
2/2✓ Branch 0 taken 1822 times.
✓ Branch 1 taken 168667 times.
|
170489 | if (!g->block_size) { |
132 | 1822 | g->state = 0; | |
133 | 1822 | g->found_end = 1; | |
134 | } | ||
135 | 170489 | continue; | |
136 | } | ||
137 | 42673308 | g->index++; | |
138 |
2/2✓ Branch 0 taken 10998 times.
✓ Branch 1 taken 914214 times.
|
925212 | } else if (g->state == GIF_EXTENSION_BLOCK) { |
139 |
2/2✓ Branch 0 taken 9195 times.
✓ Branch 1 taken 1803 times.
|
10998 | if (g->etype == GIF_GCE_EXT_LABEL) { |
140 |
2/2✓ Branch 0 taken 1839 times.
✓ Branch 1 taken 7356 times.
|
9195 | if (g->index == 0) |
141 | 1839 | g->delay = 0; | |
142 |
4/4✓ Branch 0 taken 7356 times.
✓ Branch 1 taken 1839 times.
✓ Branch 2 taken 3678 times.
✓ Branch 3 taken 3678 times.
|
9195 | if (g->index >= 1 && g->index <= 2) { |
143 | 3678 | g->delay |= buf[index] << (8 * (g->index - 1)); | |
144 | } | ||
145 | } | ||
146 |
2/2✓ Branch 0 taken 1940 times.
✓ Branch 1 taken 9058 times.
|
10998 | if (g->index >= g->block_size) { |
147 | 1940 | g->block_size = buf[index]; | |
148 | 1940 | g->index = 0; | |
149 |
2/2✓ Branch 0 taken 1885 times.
✓ Branch 1 taken 55 times.
|
1940 | if (!g->block_size) |
150 | 1885 | g->state = 0; | |
151 | 1940 | continue; | |
152 | } | ||
153 | 9058 | g->index++; | |
154 |
2/2✓ Branch 0 taken 914181 times.
✓ Branch 1 taken 33 times.
|
914214 | } else if (g->state == GIF_IMAGE) { |
155 |
2/2✓ Branch 0 taken 1839 times.
✓ Branch 1 taken 912342 times.
|
914181 | if (g->index == 9) { |
156 | 1839 | g->gct_flag = !!(buf[index] & 0x80); | |
157 | 1839 | g->gct_size = 3 * (1 << ((buf[index] & 0x07) + 1)); | |
158 | } | ||
159 |
2/2✓ Branch 0 taken 1839 times.
✓ Branch 1 taken 912342 times.
|
914181 | if (g->index >= 10 + g->gct_flag * g->gct_size) { |
160 | 1839 | g->state = GIF_IMAGE_BLOCK; | |
161 | 1839 | g->index = 0; | |
162 | 1839 | g->gct_flag = 0; | |
163 | 1839 | g->gct_size = 0; | |
164 | 1839 | continue; | |
165 | } | ||
166 | 912342 | g->index++; | |
167 | } | ||
168 | } | ||
169 | |||
170 | 44654 | return next; | |
171 | } | ||
172 | |||
173 | 44654 | 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 | { | ||
177 | 44654 | GIFParseContext *g = s->priv_data; | |
178 | int next; | ||
179 | |||
180 | 44654 | *poutbuf_size = 0; | |
181 | 44654 | *poutbuf = NULL; | |
182 | |||
183 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 44654 times.
|
44654 | if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) { |
184 | ✗ | next = buf_size; | |
185 | } else { | ||
186 | 44654 | next = gif_find_frame_end(g, buf, buf_size, avctx); | |
187 |
2/2✓ Branch 1 taken 42799 times.
✓ Branch 2 taken 1855 times.
|
44654 | if (ff_combine_frame(&g->pc, next, &buf, &buf_size) < 0) { |
188 | 42799 | *poutbuf = NULL; | |
189 | 42799 | *poutbuf_size = 0; | |
190 | 42799 | return buf_size; | |
191 | } | ||
192 | } | ||
193 | |||
194 |
2/2✓ Branch 0 taken 637 times.
✓ Branch 1 taken 1218 times.
|
1855 | s->duration = g->delay ? g->delay : 10; |
195 | 1855 | s->key_frame = g->keyframe; | |
196 |
2/2✓ Branch 0 taken 50 times.
✓ Branch 1 taken 1805 times.
|
1855 | s->pict_type = g->keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; |
197 | 1855 | g->keyframe = 0; | |
198 | |||
199 | 1855 | *poutbuf = buf; | |
200 | 1855 | *poutbuf_size = buf_size; | |
201 | 1855 | return next; | |
202 | } | ||
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 | }; | ||
210 |