| 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 |