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
) {
173 effective_map
= gif
->Image
.ColorMap
;
174 if (effective_map
== NULL
) effective_map
= gif
->SColorMap
;
178 int cnt
= FFMIN(effective_map
->ColorCount
, 256);
179 int l
= av_clip(gif
->Image
.Left
, 0, priv
->w
);
180 int t
= av_clip(gif
->Image
.Top
, 0, priv
->h
);
181 int w
= av_clip(gif
->Image
.Width
, 0, priv
->w
- l
);
182 int h
= av_clip(gif
->Image
.Height
, 0, priv
->h
- t
);
183 unsigned char *dest
= dp
->buffer
+ priv
->w
* t
+ l
;
186 for (y
= 0; y
< cnt
; y
++) {
187 priv
->palette
[(y
* 4) + 0] = effective_map
->Colors
[y
].Blue
;
188 priv
->palette
[(y
* 4) + 1] = effective_map
->Colors
[y
].Green
;
189 priv
->palette
[(y
* 4) + 2] = effective_map
->Colors
[y
].Red
;
190 priv
->palette
[(y
* 4) + 3] = 0;
193 if (gif
->Image
.Interlace
) {
195 int ih
= (h
- 0 + 7) >> 3;
196 memcpy_transp_pic(dest
, s
, w
, ih
,
197 priv
->w
<< 3, gif
->Image
.Width
,
198 transparency
, transparent_col
);
200 ih
= (h
- 4 + 7) >> 3;
201 memcpy_transp_pic(dest
+ (priv
->w
<< 2), s
, w
, ih
,
202 priv
->w
<< 3, gif
->Image
.Width
,
203 transparency
, transparent_col
);
205 ih
= (h
- 2 + 3) >> 2;
206 memcpy_transp_pic(dest
+ (priv
->w
<< 1), s
, w
, ih
,
207 priv
->w
<< 2, gif
->Image
.Width
,
208 transparency
, transparent_col
);
210 ih
= (h
- 1 + 1) >> 1;
211 memcpy_transp_pic(dest
+ priv
->w
, s
, w
, ih
,
212 priv
->w
<< 1, gif
->Image
.Width
,
213 transparency
, transparent_col
);
215 memcpy_transp_pic(dest
, buf
, w
, h
, priv
->w
, gif
->Image
.Width
,
216 transparency
, transparent_col
);
218 if (refmode
== 1) fast_memcpy(priv
->refimg
, dp
->buffer
, priv
->w
* priv
->h
);
219 if (refmode
== 2 && priv
->useref
) {
220 dest
= priv
->refimg
+ priv
->w
* t
+ l
;
221 memset(buf
, gif
->SBackGroundColor
, len
);
222 memcpy_pic(dest
, buf
, w
, h
, priv
->w
, gif
->Image
.Width
);
224 if (!(refmode
& 2)) priv
->useref
= refmode
& 1;
229 demuxer
->video
->dpos
++;
230 dp
->pts
= ((float)priv
->current_pts
) / 100;
231 dp
->pos
= stream_tell(demuxer
->stream
);
232 ds_add_packet(demuxer
->video
, dp
);
237 static demuxer_t
* demux_open_gif(demuxer_t
* demuxer
)
239 gif_priv_t
*priv
= calloc(1, sizeof(gif_priv_t
));
240 sh_video_t
*sh_video
= NULL
;
241 GifFileType
*gif
= NULL
;
243 priv
->current_pts
= 0;
244 demuxer
->seekable
= 0; // FIXME
246 // go back to the beginning
247 stream_seek(demuxer
->stream
,demuxer
->stream
->start_pos
);
249 #ifdef CONFIG_GIF_TVT_HACK
250 // without the TVT functionality of libungif, a hard seek must be
251 // done to the beginning of the file. this is because libgif is
252 // unable to use mplayer's cache, and without this lseek libgif will
253 // not read from the beginning of the file and the command will fail.
254 // with this hack enabled, you will lose the ability to stream a GIF.
255 lseek(demuxer
->stream
->fd
, 0, SEEK_SET
);
256 gif
= DGifOpenFileHandle(demuxer
->stream
->fd
);
258 gif
= DGifOpen(demuxer
->stream
, my_read_gif
);
265 // create a new video stream header
266 sh_video
= new_sh_video(demuxer
, 0);
268 // make sure the demuxer knows about the new video stream header
269 // (even though new_sh_video() ought to take care of it)
270 demuxer
->video
->sh
= sh_video
;
272 // make sure that the video demuxer stream header knows about its
273 // parent video demuxer stream (this is getting wacky), or else
274 // video_read_properties() will choke
275 sh_video
->ds
= demuxer
->video
;
277 sh_video
->format
= mmioFOURCC(8, 'R', 'G', 'B');
279 sh_video
->fps
= 5.0f
;
280 sh_video
->frametime
= 1.0f
/ sh_video
->fps
;
282 sh_video
->bih
= malloc(sizeof(BITMAPINFOHEADER
) + (256 * 4));
283 sh_video
->bih
->biCompression
= sh_video
->format
;
284 sh_video
->bih
->biWidth
= priv
->w
= (uint16_t)gif
->SWidth
;
285 sh_video
->bih
->biHeight
= priv
->h
= (uint16_t)gif
->SHeight
;
286 sh_video
->bih
->biBitCount
= 8;
287 sh_video
->bih
->biPlanes
= 2;
288 priv
->palette
= (unsigned char *)(sh_video
->bih
+ 1);
289 priv
->refimg
= malloc(priv
->w
* priv
->h
);
292 demuxer
->priv
= priv
;
297 static void demux_close_gif(demuxer_t
* demuxer
)
299 gif_priv_t
*priv
= demuxer
->priv
;
301 if (priv
->gif
&& DGifCloseFile(priv
->gif
) == GIF_ERROR
)
308 const demuxer_desc_t demuxer_desc_gif
= {
315 0, // unsafe autodetect
317 demux_gif_fill_buffer
,