1 /*
2  * GIF file parser
3  * Copyright (C) 2003 Joey Parrish
4  *
5  * This file is part of MPlayer.
6  *
7  * MPlayer is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * MPlayer 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
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with MPlayer; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 
26 #include "config.h"
27 
28 #include "mp_msg.h"
29 #include "help_mp.h"
30 
31 #include "stream/stream.h"
32 #include "demuxer.h"
33 #include "stheader.h"
34 
35 #include <gif_lib.h>
36 #include "libvo/fastmemcpy.h"
37 typedef struct {
38   int current_pts;
39   unsigned char *palette;
40   GifFileType *gif;
41   int w, h;
42   int useref;
43   uint8_t *refimg;
44 } gif_priv_t;
45 
46 #define GIF_SIGNATURE (('G' << 24) | ('I' << 16) | ('F' << 8) | '8')
47 
48 #if defined GIFLIB_MAJOR && GIFLIB_MAJOR >= 5
49 #define DGifOpen(a, b) DGifOpen(a, b, NULL)
50 #define DGifOpenFileHandle(a) DGifOpenFileHandle(a, NULL)
51 #define GifError() (gif ? gif->Error : 0)
52 #define GifErrorString() GifErrorString(err)
53 #if defined GIFLIB_MINOR && GIFLIB_MINOR >= 1
54 #define DGifCloseFile(a) DGifCloseFile(a, NULL)
55 #endif
56 #endif
57 
58 /* >= 4.2 prior GIFLIB did not have MAJOR/MINOR defines */
59 #if defined GIFLIB_MAJOR && GIFLIB_MAJOR >= 4
print_gif_error(GifFileType * gif)60 static void print_gif_error(GifFileType *gif)
61 {
62   int err = GifError();
63   const char *err_str = GifErrorString();
64 
65   if (err_str)
66     mp_msg(MSGT_DEMUX, MSGL_ERR, "\n[gif] GIF-LIB error: %s.\n", err_str);
67   else
68     mp_msg(MSGT_DEMUX, MSGL_ERR, "\n[gif] GIF-LIB undefined error %d.\n", err);
69 }
70 #else
print_gif_error(GifFileType * gif)71 static void print_gif_error(GifFileType *gif)
72 {
73   PrintGifError();
74 }
75 #endif
76 
77 #ifndef CONFIG_GIF_TVT_HACK
78 // not supported by certain versions of the library
my_read_gif(GifFileType * gif,uint8_t * buf,int len)79 static int my_read_gif(GifFileType *gif, uint8_t *buf, int len)
80 {
81   return stream_read(gif->UserData, buf, len);
82 }
83 #endif
84 
gif_check_file(demuxer_t * demuxer)85 static int gif_check_file(demuxer_t *demuxer)
86 {
87   if (stream_read_dword(demuxer->stream) == GIF_SIGNATURE) {
88     int sig = stream_read_word(demuxer->stream);
89     if ((((sig & 0xff00) == 0x3700) || ((sig & 0xff00) == 0x3900)) && (sig & 0xff) == 'a')
90       return DEMUXER_TYPE_GIF;
91   }
92   return 0;
93 }
94 
memcpy_transp_pic(uint8_t * dst,uint8_t * src,int w,int h,int dstride,int sstride,int transp,uint8_t trans_col)95 static void memcpy_transp_pic(uint8_t *dst, uint8_t *src, int w, int h,
96                 int dstride, int sstride, int transp, uint8_t trans_col) {
97   if (transp) {
98     dstride -= w;
99     sstride -= w;
100     while (h-- > 0) {
101       int wleft = w;
102       while (wleft-- > 0) {
103         if (*src != trans_col)
104           *dst = *src;
105         dst++; src++;
106       }
107       dst += dstride;
108       src += sstride;
109     }
110   } else
111     memcpy_pic(dst, src, w, h, dstride, sstride);
112 }
113 
demux_gif_fill_buffer(demuxer_t * demuxer,demux_stream_t * ds)114 static int demux_gif_fill_buffer(demuxer_t *demuxer, demux_stream_t *ds)
115 {
116   gif_priv_t *priv = demuxer->priv;
117   GifFileType *gif = priv->gif;
118   GifRecordType type = UNDEFINED_RECORD_TYPE;
119   int len = 0;
120   demux_packet_t *dp = NULL;
121   ColorMapObject *effective_map = NULL;
122   uint8_t *buf = NULL;
123   int refmode = 0;
124   int transparency = 0;
125   uint8_t transparent_col;
126 
127   while (type != IMAGE_DESC_RECORD_TYPE) {
128     if (DGifGetRecordType(gif, &type) == GIF_ERROR) {
129       print_gif_error(priv->gif);
130       return 0; // oops
131     }
132     if (type == TERMINATE_RECORD_TYPE)
133       return 0; // eof
134     if (type == SCREEN_DESC_RECORD_TYPE) {
135       if (DGifGetScreenDesc(gif) == GIF_ERROR) {
136         print_gif_error(priv->gif);
137         return 0; // oops
138       }
139     }
140     if (type == EXTENSION_RECORD_TYPE) {
141       int code;
142       unsigned char *p = NULL;
143       if (DGifGetExtension(gif, &code, &p) == GIF_ERROR) {
144         print_gif_error(priv->gif);
145         return 0; // oops
146       }
147       if (code == 0xF9) {
148         int frametime = 0;
149         if (p && p[0] == 4) // is the length correct?
150         {
151           transparency = p[1] & 1;
152           refmode = (p[1] >> 2) & 3;
153           // HACK: specification says
154           // > 0 - No disposal specified. The decoder is not required to take any action.
155           // but browsers treat it the same way as
156           // > 1 - Do not dispose. The graphic is to be left in place.
157           // Some broken files rely on this, e.g.
158           // http://samples.mplayerhq.hu/GIF/broken-gif/CLAIRE.GIF
159           if (refmode == 0) refmode = 1;
160           frametime = (p[3] << 8) | p[2]; // set the time, centiseconds
161           transparent_col = p[4];
162         }
163         priv->current_pts += frametime;
164       } else if ((code == 0xFE) && (verbose)) { // comment extension
165 	// print iff verbose
166 	printf("GIF comment: ");
167         while (p != NULL) {
168           int length = p[0];
169 	  char *comments = p + 1;
170 	  comments[length] = 0;
171 	  printf("%s", comments);
172           if (DGifGetExtensionNext(gif, &p) == GIF_ERROR) {
173             print_gif_error(priv->gif);
174             return 0; // oops
175           }
176 	}
177 	printf("\n");
178       }
179       while (p != NULL) {
180         if (DGifGetExtensionNext(gif, &p) == GIF_ERROR) {
181           print_gif_error(priv->gif);
182           return 0; // oops
183         }
184       }
185     }
186   }
187 
188   if (DGifGetImageDesc(gif) == GIF_ERROR) {
189     print_gif_error(priv->gif);
190     return 0; // oops
191   }
192 
193   len = gif->Image.Width * gif->Image.Height;
194   dp = new_demux_packet(priv->w * priv->h);
195   buf = calloc(gif->Image.Width, gif->Image.Height);
196   if (priv->useref)
197     fast_memcpy(dp->buffer, priv->refimg, priv->w * priv->h);
198   else
199     memset(dp->buffer, gif->SBackGroundColor, priv->w * priv->h);
200 
201   if (DGifGetLine(gif, buf, len) == GIF_ERROR) {
202     print_gif_error(priv->gif);
203     free(buf);
204     free_demux_packet(dp);
205     return 0; // oops
206   }
207 
208   effective_map = gif->Image.ColorMap;
209   if (effective_map == NULL) effective_map = gif->SColorMap;
210   if (effective_map == NULL) {
211     mp_msg(MSGT_DEMUX, MSGL_ERR, "[demux_gif] No local nor global colormap.\n");
212     free(buf);
213     free_demux_packet(dp);
214     return 0;
215   }
216 
217   {
218     int y;
219     int cnt = FFMIN(effective_map->ColorCount, 256);
220     int l = av_clip(gif->Image.Left, 0, priv->w);
221     int t = av_clip(gif->Image.Top, 0, priv->h);
222     int w = av_clip(gif->Image.Width, 0, priv->w - l);
223     int h = av_clip(gif->Image.Height, 0, priv->h - t);
224     unsigned char *dest = dp->buffer + priv->w * t + l;
225 
226     // copy the palette
227     for (y = 0; y < cnt; y++) {
228       priv->palette[(y * 4) + 0] = effective_map->Colors[y].Blue;
229       priv->palette[(y * 4) + 1] = effective_map->Colors[y].Green;
230       priv->palette[(y * 4) + 2] = effective_map->Colors[y].Red;
231       priv->palette[(y * 4) + 3] = 0;
232     }
233 
234     if (gif->Image.Interlace) {
235       uint8_t *s = buf;
236       int ih = (h - 0 + 7) >> 3;
237       memcpy_transp_pic(dest, s, w, ih,
238                         priv->w << 3, gif->Image.Width,
239                         transparency, transparent_col);
240       s += ih * w;
241       ih = (h - 4 + 7) >> 3;
242       memcpy_transp_pic(dest + (priv->w << 2), s, w, ih,
243                         priv->w << 3, gif->Image.Width,
244                         transparency, transparent_col);
245       s += ih * w;
246       ih = (h - 2 + 3) >> 2;
247       memcpy_transp_pic(dest + (priv->w << 1), s, w, ih,
248                         priv->w << 2, gif->Image.Width,
249                         transparency, transparent_col);
250       s += ih * w;
251       ih = (h - 1 + 1) >> 1;
252       memcpy_transp_pic(dest + priv->w, s, w, ih,
253                         priv->w << 1, gif->Image.Width,
254                         transparency, transparent_col);
255     } else
256       memcpy_transp_pic(dest, buf, w, h, priv->w, gif->Image.Width,
257                         transparency, transparent_col);
258 
259     if (refmode == 1) fast_memcpy(priv->refimg, dp->buffer, priv->w * priv->h);
260     if (refmode == 2 && priv->useref) {
261       dest = priv->refimg + priv->w * t + l;
262       memset(buf, gif->SBackGroundColor, len);
263       memcpy_pic(dest, buf, w, h, priv->w, gif->Image.Width);
264     }
265     if (!(refmode & 2)) priv->useref = refmode & 1;
266   }
267 
268   free(buf);
269 
270   demuxer->video->dpos++;
271   dp->pts = ((float)priv->current_pts) / 100;
272   dp->pos = stream_tell(demuxer->stream);
273   ds_add_packet(demuxer->video, dp);
274 
275   return 1;
276 }
277 
demux_open_gif(demuxer_t * demuxer)278 static demuxer_t* demux_open_gif(demuxer_t* demuxer)
279 {
280   gif_priv_t *priv = calloc(1, sizeof(*priv));
281   sh_video_t *sh_video = NULL;
282   int bih_size = sizeof(*sh_video->bih) + (256 * 4);
283   GifFileType *gif = NULL;
284 
285   priv->current_pts = 0;
286   demuxer->seekable = 0; // FIXME
287 
288   // go back to the beginning
289   stream_seek(demuxer->stream,demuxer->stream->start_pos);
290 
291 #ifdef CONFIG_GIF_TVT_HACK
292   // without the TVT functionality of libungif, a hard seek must be
293   // done to the beginning of the file.  this is because libgif is
294   // unable to use mplayer's cache, and without this lseek libgif will
295   // not read from the beginning of the file and the command will fail.
296   // with this hack enabled, you will lose the ability to stream a GIF.
297   lseek(demuxer->stream->fd, 0, SEEK_SET);
298   gif = DGifOpenFileHandle(demuxer->stream->fd);
299 #else
300   gif = DGifOpen(demuxer->stream, my_read_gif);
301 #endif
302   if (!gif) {
303     print_gif_error(NULL);
304     free(priv);
305     return NULL;
306   }
307 
308   // Validate image size, most code in this demuxer assumes w*h <= INT_MAX
309   if ((int64_t)gif->SWidth * gif->SHeight > INT_MAX) {
310     mp_msg(MSGT_DEMUX, MSGL_ERR,
311            "[demux_gif] Unsupported picture size %dx%d.\n", gif->SWidth,
312            gif->SHeight);
313     if (DGifCloseFile(gif) == GIF_ERROR)
314       print_gif_error(NULL);
315     free(priv);
316     return NULL;
317   }
318 
319   // create a new video stream header
320   sh_video = new_sh_video(demuxer, 0);
321 
322   // make sure the demuxer knows about the new video stream header
323   // (even though new_sh_video() ought to take care of it)
324   demuxer->video->id = 0;
325   demuxer->video->sh = sh_video;
326 
327   sh_video->format = mmioFOURCC(8, 'R', 'G', 'B');
328 
329   sh_video->fps = 5.0f;
330   sh_video->frametime = 1.0f / sh_video->fps;
331 
332   sh_video->bih = calloc(1, bih_size);
333   sh_video->bih->biSize = bih_size;
334   sh_video->bih->biCompression = sh_video->format;
335   sh_video->bih->biWidth = priv->w = (uint16_t)gif->SWidth;
336   sh_video->bih->biHeight = priv->h = (uint16_t)gif->SHeight;
337   sh_video->bih->biBitCount = 8;
338   sh_video->bih->biPlanes = 2;
339   priv->palette = (unsigned char *)(sh_video->bih + 1);
340   priv->refimg = malloc(priv->w * priv->h);
341 
342   priv->gif = gif;
343   demuxer->priv = priv;
344 
345   return demuxer;
346 }
347 
demux_close_gif(demuxer_t * demuxer)348 static void demux_close_gif(demuxer_t* demuxer)
349 {
350   gif_priv_t *priv = demuxer->priv;
351   if (!priv) return;
352   if (priv->gif && DGifCloseFile(priv->gif) == GIF_ERROR)
353     print_gif_error(NULL);
354   free(priv->refimg);
355   free(priv);
356 }
357 
358 
359 const demuxer_desc_t demuxer_desc_gif = {
360   "GIF demuxer",
361   "gif",
362   "GIF",
363   "Joey Parrish",
364   "",
365   DEMUXER_TYPE_GIF,
366   0, // unsafe autodetect
367   gif_check_file,
368   demux_gif_fill_buffer,
369   demux_open_gif,
370   demux_close_gif,
371   NULL,
372   NULL
373 };
374