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