2 GIF file parser for MPlayer
15 #include "stream/stream.h"
20 #include "libvo/fastmemcpy.h"
23 unsigned char *palette
;
30 #define GIF_SIGNATURE (('G' << 16) | ('I' << 8) | 'F')
32 #ifndef HAVE_GIF_TVT_HACK
33 // not supported by certain versions of the library
34 int my_read_gif(GifFileType
*gif
, uint8_t *buf
, int len
) {
35 return stream_read(gif
->UserData
, buf
, len
);
39 static int gif_check_file(demuxer_t
*demuxer
)
41 if (stream_read_int24(demuxer
->stream
) == GIF_SIGNATURE
)
42 return DEMUXER_TYPE_GIF
;
46 static void memcpy_transp_pic(uint8_t *dst
, uint8_t *src
, int w
, int h
,
47 int dstride
, int sstride
, int transp
, uint8_t trans_col
) {
54 if (*src
!= trans_col
)
62 memcpy_pic(dst
, src
, w
, h
, dstride
, sstride
);
65 static int demux_gif_fill_buffer(demuxer_t
*demuxer
, demux_stream_t
*ds
)
67 gif_priv_t
*priv
= demuxer
->priv
;
68 GifFileType
*gif
= priv
->gif
;
69 GifRecordType type
= UNDEFINED_RECORD_TYPE
;
71 demux_packet_t
*dp
= NULL
;
72 ColorMapObject
*effective_map
= NULL
;
76 uint8_t transparent_col
;
78 while (type
!= IMAGE_DESC_RECORD_TYPE
) {
79 if (DGifGetRecordType(gif
, &type
) == GIF_ERROR
) {
83 if (type
== TERMINATE_RECORD_TYPE
)
85 if (type
== SCREEN_DESC_RECORD_TYPE
) {
86 if (DGifGetScreenDesc(gif
) == GIF_ERROR
) {
91 if (type
== EXTENSION_RECORD_TYPE
) {
93 unsigned char *p
= NULL
;
94 if (DGifGetExtension(gif
, &code
, &p
) == GIF_ERROR
) {
100 if (p
[0] == 4) // is the length correct?
102 transparency
= p
[1] & 1;
103 refmode
= (p
[1] >> 2) & 3;
104 // HACK: specification says
105 // > 0 - No disposal specified. The decoder is not required to take any action.
106 // but browsers treat it the same way as
107 // > 1 - Do not dispose. The graphic is to be left in place.
108 // Some broken files rely on this, e.g.
109 // http://samples.mplayerhq.hu/GIF/broken-gif/CLAIRE.GIF
110 if (refmode
== 0) refmode
= 1;
111 frametime
= (p
[3] << 8) | p
[2]; // set the time, centiseconds
112 transparent_col
= p
[4];
114 priv
->current_pts
+= frametime
;
115 } else if ((code
== 0xFE) && (verbose
)) { // comment extension
117 printf("GIF comment: ");
120 char *comments
= p
+ 1;
121 comments
[length
] = 0;
122 printf("%s", comments
);
123 if (DGifGetExtensionNext(gif
, &p
) == GIF_ERROR
) {
131 if (DGifGetExtensionNext(gif
, &p
) == GIF_ERROR
) {
139 if (DGifGetImageDesc(gif
) == GIF_ERROR
) {
144 len
= gif
->Image
.Width
* gif
->Image
.Height
;
145 dp
= new_demux_packet(priv
->w
* priv
->h
);
146 buf
= calloc(gif
->Image
.Width
, gif
->Image
.Height
);
148 fast_memcpy(dp
->buffer
, priv
->refimg
, priv
->w
* priv
->h
);
150 memset(dp
->buffer
, gif
->SBackGroundColor
, priv
->w
* priv
->h
);
152 if (DGifGetLine(gif
, buf
, len
) == GIF_ERROR
) {
157 effective_map
= gif
->Image
.ColorMap
;
158 if (effective_map
== NULL
) effective_map
= gif
->SColorMap
;
162 int cnt
= FFMIN(effective_map
->ColorCount
, 256);
163 int l
= av_clip(gif
->Image
.Left
, 0, priv
->w
);
164 int t
= av_clip(gif
->Image
.Top
, 0, priv
->h
);
165 int w
= av_clip(gif
->Image
.Width
, 0, priv
->w
- l
);
166 int h
= av_clip(gif
->Image
.Height
, 0, priv
->h
- t
);
167 unsigned char *dest
= dp
->buffer
+ priv
->w
* t
+ l
;
170 for (y
= 0; y
< cnt
; y
++) {
171 priv
->palette
[(y
* 4) + 0] = effective_map
->Colors
[y
].Blue
;
172 priv
->palette
[(y
* 4) + 1] = effective_map
->Colors
[y
].Green
;
173 priv
->palette
[(y
* 4) + 2] = effective_map
->Colors
[y
].Red
;
174 priv
->palette
[(y
* 4) + 3] = 0;
177 if (gif
->Image
.Interlace
) {
179 int ih
= (h
- 0 + 7) >> 3;
180 memcpy_transp_pic(dest
, s
, w
, ih
,
181 priv
->w
<< 3, gif
->Image
.Width
,
182 transparency
, transparent_col
);
184 ih
= (h
- 4 + 7) >> 3;
185 memcpy_transp_pic(dest
+ (priv
->w
<< 2), s
, w
, ih
,
186 priv
->w
<< 3, gif
->Image
.Width
,
187 transparency
, transparent_col
);
189 ih
= (h
- 2 + 3) >> 2;
190 memcpy_transp_pic(dest
+ (priv
->w
<< 1), s
, w
, ih
,
191 priv
->w
<< 2, gif
->Image
.Width
,
192 transparency
, transparent_col
);
194 ih
= (h
- 1 + 1) >> 1;
195 memcpy_transp_pic(dest
+ priv
->w
, s
, w
, ih
,
196 priv
->w
<< 1, gif
->Image
.Width
,
197 transparency
, transparent_col
);
199 memcpy_transp_pic(dest
, buf
, w
, h
, priv
->w
, gif
->Image
.Width
,
200 transparency
, transparent_col
);
202 if (refmode
== 1) fast_memcpy(priv
->refimg
, dp
->buffer
, priv
->w
* priv
->h
);
203 if (refmode
== 2 && priv
->useref
) {
204 dest
= priv
->refimg
+ priv
->w
* t
+ l
;
205 memset(buf
, gif
->SBackGroundColor
, len
);
206 memcpy_pic(dest
, buf
, w
, h
, priv
->w
, gif
->Image
.Width
);
208 if (!(refmode
& 2)) priv
->useref
= refmode
& 1;
213 demuxer
->video
->dpos
++;
214 dp
->pts
= ((float)priv
->current_pts
) / 100;
215 dp
->pos
= stream_tell(demuxer
->stream
);
216 ds_add_packet(demuxer
->video
, dp
);
221 static demuxer_t
* demux_open_gif(demuxer_t
* demuxer
)
223 gif_priv_t
*priv
= calloc(1, sizeof(gif_priv_t
));
224 sh_video_t
*sh_video
= NULL
;
225 GifFileType
*gif
= NULL
;
227 priv
->current_pts
= 0;
228 demuxer
->seekable
= 0; // FIXME
230 // go back to the beginning
231 stream_seek(demuxer
->stream
,demuxer
->stream
->start_pos
);
233 #ifdef HAVE_GIF_TVT_HACK
234 // without the TVT functionality of libungif, a hard seek must be
235 // done to the beginning of the file. this is because libgif is
236 // unable to use mplayer's cache, and without this lseek libgif will
237 // not read from the beginning of the file and the command will fail.
238 // with this hack enabled, you will lose the ability to stream a GIF.
239 lseek(demuxer
->stream
->fd
, 0, SEEK_SET
);
240 gif
= DGifOpenFileHandle(demuxer
->stream
->fd
);
242 gif
= DGifOpen(demuxer
->stream
, my_read_gif
);
249 // create a new video stream header
250 sh_video
= new_sh_video(demuxer
, 0);
252 // make sure the demuxer knows about the new video stream header
253 // (even though new_sh_video() ought to take care of it)
254 demuxer
->video
->sh
= sh_video
;
256 // make sure that the video demuxer stream header knows about its
257 // parent video demuxer stream (this is getting wacky), or else
258 // video_read_properties() will choke
259 sh_video
->ds
= demuxer
->video
;
261 sh_video
->format
= mmioFOURCC(8, 'R', 'G', 'B');
263 sh_video
->fps
= 5.0f
;
264 sh_video
->frametime
= 1.0f
/ sh_video
->fps
;
266 sh_video
->bih
= malloc(sizeof(BITMAPINFOHEADER
) + (256 * 4));
267 sh_video
->bih
->biCompression
= sh_video
->format
;
268 sh_video
->bih
->biWidth
= priv
->w
= (uint16_t)gif
->SWidth
;
269 sh_video
->bih
->biHeight
= priv
->h
= (uint16_t)gif
->SHeight
;
270 sh_video
->bih
->biBitCount
= 8;
271 sh_video
->bih
->biPlanes
= 2;
272 priv
->palette
= (unsigned char *)(sh_video
->bih
+ 1);
273 priv
->refimg
= malloc(priv
->w
* priv
->h
);
276 demuxer
->priv
= priv
;
281 static void demux_close_gif(demuxer_t
* demuxer
)
283 gif_priv_t
*priv
= demuxer
->priv
;
285 if (priv
->gif
&& DGifCloseFile(priv
->gif
) == GIF_ERROR
)
292 const demuxer_desc_t demuxer_desc_gif
= {
299 0, // unsafe autodetect
301 demux_gif_fill_buffer
,