3 * Copyright (C) 2003 Joey Parrish
5 * This file is part of MPlayer.
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.
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.
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.
30 #include "stream/stream.h"
35 #include "libvo/fastmemcpy.h"
38 unsigned char *palette
;
45 #define GIF_SIGNATURE (('G' << 16) | ('I' << 8) | 'F')
47 #ifndef CONFIG_GIF_TVT_HACK
48 // not supported by certain versions of the library
49 static int my_read_gif(GifFileType
*gif
, uint8_t *buf
, int len
)
51 return stream_read(gif
->UserData
, buf
, len
);
55 static int gif_check_file(demuxer_t
*demuxer
)
57 if (stream_read_int24(demuxer
->stream
) == GIF_SIGNATURE
)
58 return DEMUXER_TYPE_GIF
;
62 static void memcpy_transp_pic(uint8_t *dst
, uint8_t *src
, int w
, int h
,
63 int dstride
, int sstride
, int transp
, uint8_t trans_col
) {
70 if (*src
!= trans_col
)
78 memcpy_pic(dst
, src
, w
, h
, dstride
, sstride
);
81 static int demux_gif_fill_buffer(demuxer_t
*demuxer
, demux_stream_t
*ds
)
83 gif_priv_t
*priv
= demuxer
->priv
;
84 GifFileType
*gif
= priv
->gif
;
85 GifRecordType type
= UNDEFINED_RECORD_TYPE
;
87 demux_packet_t
*dp
= NULL
;
88 ColorMapObject
*effective_map
= NULL
;
92 uint8_t transparent_col
;
94 while (type
!= IMAGE_DESC_RECORD_TYPE
) {
95 if (DGifGetRecordType(gif
, &type
) == GIF_ERROR
) {
99 if (type
== TERMINATE_RECORD_TYPE
)
101 if (type
== SCREEN_DESC_RECORD_TYPE
) {
102 if (DGifGetScreenDesc(gif
) == GIF_ERROR
) {
107 if (type
== EXTENSION_RECORD_TYPE
) {
109 unsigned char *p
= NULL
;
110 if (DGifGetExtension(gif
, &code
, &p
) == GIF_ERROR
) {
116 if (p
[0] == 4) // is the length correct?
118 transparency
= p
[1] & 1;
119 refmode
= (p
[1] >> 2) & 3;
120 // HACK: specification says
121 // > 0 - No disposal specified. The decoder is not required to take any action.
122 // but browsers treat it the same way as
123 // > 1 - Do not dispose. The graphic is to be left in place.
124 // Some broken files rely on this, e.g.
125 // http://samples.mplayerhq.hu/GIF/broken-gif/CLAIRE.GIF
126 if (refmode
== 0) refmode
= 1;
127 frametime
= (p
[3] << 8) | p
[2]; // set the time, centiseconds
128 transparent_col
= p
[4];
130 priv
->current_pts
+= frametime
;
131 } else if ((code
== 0xFE) && (verbose
)) { // comment extension
133 printf("GIF comment: ");
136 char *comments
= p
+ 1;
137 comments
[length
] = 0;
138 printf("%s", comments
);
139 if (DGifGetExtensionNext(gif
, &p
) == GIF_ERROR
) {
147 if (DGifGetExtensionNext(gif
, &p
) == GIF_ERROR
) {
155 if (DGifGetImageDesc(gif
) == GIF_ERROR
) {
160 len
= gif
->Image
.Width
* gif
->Image
.Height
;
161 dp
= new_demux_packet(priv
->w
* priv
->h
);
162 buf
= calloc(gif
->Image
.Width
, gif
->Image
.Height
);
164 fast_memcpy(dp
->buffer
, priv
->refimg
, priv
->w
* priv
->h
);
166 memset(dp
->buffer
, gif
->SBackGroundColor
, priv
->w
* priv
->h
);
168 if (DGifGetLine(gif
, buf
, len
) == GIF_ERROR
) {
174 effective_map
= gif
->Image
.ColorMap
;
175 if (effective_map
== NULL
) effective_map
= gif
->SColorMap
;
179 int cnt
= FFMIN(effective_map
->ColorCount
, 256);
180 int l
= av_clip(gif
->Image
.Left
, 0, priv
->w
);
181 int t
= av_clip(gif
->Image
.Top
, 0, priv
->h
);
182 int w
= av_clip(gif
->Image
.Width
, 0, priv
->w
- l
);
183 int h
= av_clip(gif
->Image
.Height
, 0, priv
->h
- t
);
184 unsigned char *dest
= dp
->buffer
+ priv
->w
* t
+ l
;
187 for (y
= 0; y
< cnt
; y
++) {
188 priv
->palette
[(y
* 4) + 0] = effective_map
->Colors
[y
].Blue
;
189 priv
->palette
[(y
* 4) + 1] = effective_map
->Colors
[y
].Green
;
190 priv
->palette
[(y
* 4) + 2] = effective_map
->Colors
[y
].Red
;
191 priv
->palette
[(y
* 4) + 3] = 0;
194 if (gif
->Image
.Interlace
) {
196 int ih
= (h
- 0 + 7) >> 3;
197 memcpy_transp_pic(dest
, s
, w
, ih
,
198 priv
->w
<< 3, gif
->Image
.Width
,
199 transparency
, transparent_col
);
201 ih
= (h
- 4 + 7) >> 3;
202 memcpy_transp_pic(dest
+ (priv
->w
<< 2), s
, w
, ih
,
203 priv
->w
<< 3, gif
->Image
.Width
,
204 transparency
, transparent_col
);
206 ih
= (h
- 2 + 3) >> 2;
207 memcpy_transp_pic(dest
+ (priv
->w
<< 1), s
, w
, ih
,
208 priv
->w
<< 2, gif
->Image
.Width
,
209 transparency
, transparent_col
);
211 ih
= (h
- 1 + 1) >> 1;
212 memcpy_transp_pic(dest
+ priv
->w
, s
, w
, ih
,
213 priv
->w
<< 1, gif
->Image
.Width
,
214 transparency
, transparent_col
);
216 memcpy_transp_pic(dest
, buf
, w
, h
, priv
->w
, gif
->Image
.Width
,
217 transparency
, transparent_col
);
219 if (refmode
== 1) fast_memcpy(priv
->refimg
, dp
->buffer
, priv
->w
* priv
->h
);
220 if (refmode
== 2 && priv
->useref
) {
221 dest
= priv
->refimg
+ priv
->w
* t
+ l
;
222 memset(buf
, gif
->SBackGroundColor
, len
);
223 memcpy_pic(dest
, buf
, w
, h
, priv
->w
, gif
->Image
.Width
);
225 if (!(refmode
& 2)) priv
->useref
= refmode
& 1;
230 demuxer
->video
->dpos
++;
231 dp
->pts
= ((float)priv
->current_pts
) / 100;
232 dp
->pos
= stream_tell(demuxer
->stream
);
233 ds_add_packet(demuxer
->video
, dp
);
238 static demuxer_t
* demux_open_gif(demuxer_t
* demuxer
)
240 gif_priv_t
*priv
= calloc(1, sizeof(gif_priv_t
));
241 sh_video_t
*sh_video
= NULL
;
242 GifFileType
*gif
= NULL
;
244 priv
->current_pts
= 0;
245 demuxer
->seekable
= 0; // FIXME
247 // go back to the beginning
248 stream_seek(demuxer
->stream
,demuxer
->stream
->start_pos
);
250 #ifdef CONFIG_GIF_TVT_HACK
251 // without the TVT functionality of libungif, a hard seek must be
252 // done to the beginning of the file. this is because libgif is
253 // unable to use mplayer's cache, and without this lseek libgif will
254 // not read from the beginning of the file and the command will fail.
255 // with this hack enabled, you will lose the ability to stream a GIF.
256 lseek(demuxer
->stream
->fd
, 0, SEEK_SET
);
257 gif
= DGifOpenFileHandle(demuxer
->stream
->fd
);
259 gif
= DGifOpen(demuxer
->stream
, my_read_gif
);
267 // create a new video stream header
268 sh_video
= new_sh_video(demuxer
, 0);
270 // make sure the demuxer knows about the new video stream header
271 // (even though new_sh_video() ought to take care of it)
272 demuxer
->video
->sh
= sh_video
;
274 // make sure that the video demuxer stream header knows about its
275 // parent video demuxer stream (this is getting wacky), or else
276 // video_read_properties() will choke
277 sh_video
->ds
= demuxer
->video
;
279 sh_video
->format
= mmioFOURCC(8, 'R', 'G', 'B');
281 sh_video
->fps
= 5.0f
;
282 sh_video
->frametime
= 1.0f
/ sh_video
->fps
;
284 sh_video
->bih
= malloc(sizeof(*sh_video
->bih
) + (256 * 4));
285 sh_video
->bih
->biCompression
= sh_video
->format
;
286 sh_video
->bih
->biWidth
= priv
->w
= (uint16_t)gif
->SWidth
;
287 sh_video
->bih
->biHeight
= priv
->h
= (uint16_t)gif
->SHeight
;
288 sh_video
->bih
->biBitCount
= 8;
289 sh_video
->bih
->biPlanes
= 2;
290 priv
->palette
= (unsigned char *)(sh_video
->bih
+ 1);
291 priv
->refimg
= malloc(priv
->w
* priv
->h
);
294 demuxer
->priv
= priv
;
299 static void demux_close_gif(demuxer_t
* demuxer
)
301 gif_priv_t
*priv
= demuxer
->priv
;
303 if (priv
->gif
&& DGifCloseFile(priv
->gif
) == GIF_ERROR
)
310 const demuxer_desc_t demuxer_desc_gif
= {
317 0, // unsafe autodetect
319 demux_gif_fill_buffer
,