188 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * GIF parser
 | |
|  * Copyright (c) 2018 Paul B Mahol
 | |
|  *
 | |
|  * This file is part of FFmpeg.
 | |
|  *
 | |
|  * FFmpeg is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Lesser General Public
 | |
|  * License as published by the Free Software Foundation; either
 | |
|  * version 2.1 of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * FFmpeg is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * Lesser General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Lesser General Public
 | |
|  * License along with FFmpeg; if not, write to the Free Software
 | |
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * @file
 | |
|  * GIF parser
 | |
|  */
 | |
| 
 | |
| #include "libavutil/bswap.h"
 | |
| #include "libavutil/common.h"
 | |
| 
 | |
| #include "gif.h"
 | |
| #include "parser.h"
 | |
| 
 | |
| typedef enum GIFParseStates {
 | |
|     GIF_HEADER = 1,
 | |
|     GIF_EXTENSION,
 | |
|     GIF_EXTENSION_BLOCK,
 | |
|     GIF_IMAGE,
 | |
|     GIF_IMAGE_BLOCK,
 | |
| } gif_states;
 | |
| 
 | |
| typedef struct GIFParseContext {
 | |
|     ParseContext pc;
 | |
|     unsigned found_sig;
 | |
|     int found_start;
 | |
|     int found_end;
 | |
|     int index;
 | |
|     int state;
 | |
|     int gct_flag;
 | |
|     int gct_size;
 | |
|     int block_size;
 | |
|     int etype;
 | |
|     int delay;
 | |
| } GIFParseContext;
 | |
| 
 | |
| static int gif_find_frame_end(GIFParseContext *g, const uint8_t *buf,
 | |
|                               int buf_size, void *logctx)
 | |
| {
 | |
|     int index, next = END_NOT_FOUND;
 | |
| 
 | |
|     for (index = 0; index < buf_size; index++) {
 | |
|         if (!g->state) {
 | |
|             if (!memcmp(buf + index, gif87a_sig, 6) ||
 | |
|                 !memcmp(buf + index, gif89a_sig, 6)) {
 | |
|                 g->state = GIF_HEADER;
 | |
|                 g->found_sig++;
 | |
|             } else if (buf[index] == GIF_EXTENSION_INTRODUCER) {
 | |
|                 g->state = GIF_EXTENSION;
 | |
|                 g->found_start = 1;
 | |
|             } else if (buf[index] == GIF_IMAGE_SEPARATOR) {
 | |
|                 g->state = GIF_IMAGE;
 | |
|             } else if (buf[index] == GIF_TRAILER) {
 | |
|                 g->state = 0;
 | |
|                 g->found_end = 1;
 | |
|                 g->found_sig = 0;
 | |
|             } else {
 | |
|                 g->found_sig = 0;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (g->state == GIF_HEADER) {
 | |
|             if (g->index == 10) {
 | |
|                 g->gct_flag = !!(buf[index] & 0x80);
 | |
|                 g->gct_size = 3 * (1 << ((buf[index] & 0x07) + 1));
 | |
|             }
 | |
|             if (g->index >= 12 + g->gct_flag * g->gct_size) {
 | |
|                 g->state = 0;
 | |
|                 g->index = 0;
 | |
|                 g->gct_flag = 0;
 | |
|                 g->gct_size = 0;
 | |
|                 continue;
 | |
|             }
 | |
|             g->index++;
 | |
|         } else if (g->state == GIF_EXTENSION) {
 | |
|             if (g->found_start && g->found_end && g->found_sig) {
 | |
|                 next = index;
 | |
|                 g->found_start = 0;
 | |
|                 g->found_end = 0;
 | |
|                 g->index = 0;
 | |
|                 g->gct_flag = 0;
 | |
|                 g->gct_size = 0;
 | |
|                 g->state = 0;
 | |
|                 break;
 | |
|             }
 | |
|             if (g->index == 1) {
 | |
|                 g->etype = buf[index];
 | |
|             }
 | |
|             if (g->index >= 2) {
 | |
|                 g->block_size = buf[index];
 | |
|                 g->index = 0;
 | |
|                 g->state = GIF_EXTENSION_BLOCK;
 | |
|                 continue;
 | |
|             }
 | |
|             g->index++;
 | |
|         } else if (g->state == GIF_IMAGE_BLOCK) {
 | |
|             if (!g->index)
 | |
|                 g->block_size = buf[index];
 | |
|             if (g->index >= g->block_size) {
 | |
|                 g->index = 0;
 | |
|                 if (!g->block_size) {
 | |
|                     g->state = 0;
 | |
|                     g->found_end = 1;
 | |
|                 }
 | |
|                 continue;
 | |
|             }
 | |
|             g->index++;
 | |
|         } else if (g->state == GIF_EXTENSION_BLOCK) {
 | |
|             if (g->etype == GIF_GCE_EXT_LABEL) {
 | |
|                 if (g->index == 0)
 | |
|                     g->delay = 0;
 | |
|                 if (g->index >= 1 && g->index <= 2) {
 | |
|                     g->delay |= buf[index] << (8 * (g->index - 1));
 | |
|                 }
 | |
|             }
 | |
|             if (g->index >= g->block_size) {
 | |
|                 g->block_size = buf[index];
 | |
|                 g->index = 0;
 | |
|                 if (!g->block_size)
 | |
|                     g->state = 0;
 | |
|                 continue;
 | |
|             }
 | |
|             g->index++;
 | |
|         } else if (g->state == GIF_IMAGE) {
 | |
|             if (g->index == 8) {
 | |
|                 g->gct_flag = !!(buf[index] & 0x80);
 | |
|                 g->gct_size = 3 * (1 << ((buf[index] & 0x07) + 1));
 | |
|             }
 | |
|             if (g->index >= 10 + g->gct_flag * g->gct_size) {
 | |
|                 g->state = GIF_IMAGE_BLOCK;
 | |
|                 g->index = 0;
 | |
|                 g->gct_flag = 0;
 | |
|                 g->gct_size = 0;
 | |
|                 continue;
 | |
|             }
 | |
|             g->index++;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return next;
 | |
| }
 | |
| 
 | |
| static int gif_parse(AVCodecParserContext *s, AVCodecContext *avctx,
 | |
|                      const uint8_t **poutbuf, int *poutbuf_size,
 | |
|                      const uint8_t *buf, int buf_size)
 | |
| {
 | |
|     GIFParseContext *g = s->priv_data;
 | |
|     int next;
 | |
| 
 | |
|     next = gif_find_frame_end(g, buf, buf_size, avctx);
 | |
|     if (ff_combine_frame(&g->pc, next, &buf, &buf_size) < 0) {
 | |
|         *poutbuf      = NULL;
 | |
|         *poutbuf_size = 0;
 | |
|         return buf_size;
 | |
|     }
 | |
| 
 | |
|     s->duration   = g->delay;
 | |
| 
 | |
|     *poutbuf      = buf;
 | |
|     *poutbuf_size = buf_size;
 | |
|     return next;
 | |
| }
 | |
| 
 | |
| const AVCodecParser ff_gif_parser = {
 | |
|     .codec_ids      = { AV_CODEC_ID_GIF },
 | |
|     .priv_data_size = sizeof(GIFParseContext),
 | |
|     .parser_parse   = gif_parse,
 | |
|     .parser_close   = ff_parse_close,
 | |
| };
 |