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