2 * MPlayer video driver for animated GIF output
4 * copyright (C) 2002 Joey Parrish <joey@nicewarrior.org>
5 * based on vo_directfb2.c
7 * This file is part of MPlayer.
9 * MPlayer is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * MPlayer is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with MPlayer; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * when setting output framerate, frames will be ignored as needed
26 * to achieve the desired rate. no frames will be duplicated.
28 * output framerate can be specified as a float
29 * value now, instead of just an int.
31 * adjustments will be made to both the frame drop cycle and the
32 * delay per frame to achieve the desired output framerate.
34 * time values are in centiseconds, because that's
35 * what the gif spec uses for it's delay values.
37 * preinit looks for arguments in one of the following formats (in this order):
38 * fps:filename -- sets the framerate (float) and output file
39 * fps -- sets the framerate (float), default file out.gif
40 * filename -- defaults to 5 fps, sets output file
41 * (none) -- defaults to 5 fps, output file out.gif
43 * trying to put the filename before the framerate will result in the
44 * entire argument being interpretted as the filename.
55 #include "subopt-helper.h"
56 #include "video_out.h"
57 #include "video_out_internal.h"
60 #define MPLAYER_VERSION 0.90
61 #define VO_GIF_REVISION 6
63 static const vo_info_t info
= {
64 "animated GIF output",
66 "Joey Parrish joey@nicewarrior.org",
70 const LIBVO_EXTERN(gif89a
)
73 // how many frames per second we are aiming for during output.
74 static float target_fps
;
75 // default value for output fps.
76 static const float default_fps
= 5.00;
77 // the ideal gif delay per frame.
78 static float ideal_delay
;
79 // the ideal time thus far.
80 static float ideal_time
;
81 // actual time thus far.
83 // nominal framedrop cycle length in frames
84 static float frame_cycle
;
85 // position in the framedrop cycle
87 // adjustment of the framedrop cycle
88 static float frame_adj
;
90 // the output width and height
91 static uint32_t img_width
;
92 static uint32_t img_height
;
93 // image data for slice rendering
94 static uint8_t *slice_data
= NULL
;
95 // reduced image data for flip_page
96 static uint8_t *reduce_data
= NULL
;
97 // reduced color map for flip_page
98 static ColorMapObject
*reduce_cmap
= NULL
;
100 // a pointer to the gif structure
101 static GifFileType
*new_gif
= NULL
;
102 // a string to contain the filename of the output gif
103 static char *gif_filename
= NULL
;
104 // the default output filename
105 #define DEFAULT_FILE "out.gif"
107 static opt_t subopts
[] = {
108 {"output", OPT_ARG_MSTRZ
, &gif_filename
, NULL
, 0},
109 {"fps", OPT_ARG_FLOAT
, &target_fps
, NULL
, 0},
110 {NULL
, 0, NULL
, NULL
, 0}
113 static int preinit(const char *arg
)
117 if (subopt_parse(arg
, subopts
) != 0) {
118 mp_msg(MSGT_VO
, MSGL_FATAL
,
119 "\n-vo gif89a command line help:\n"
120 "Example: mplayer -vo gif89a:output=file.gif:fps=4.9\n"
122 " output=<filename>\n"
123 " Specify the output file. The default is out.gif.\n"
125 " Specify the target framerate. The default is 5.0.\n"
130 if (target_fps
> vo_fps
)
131 target_fps
= vo_fps
; // i will not duplicate frames.
133 if (target_fps
<= 0) {
134 target_fps
= default_fps
;
135 mp_msg(MSGT_VO
, MSGL_V
, "GIF89a: default, %.2f fps\n", target_fps
);
137 mp_msg(MSGT_VO
, MSGL_V
, "GIF89a: output fps forced to %.2f\n", target_fps
);
140 ideal_delay
= 100 / target_fps
; // in centiseconds
141 frame_cycle
= vo_fps
/ target_fps
;
142 // we make one output frame every (frame_cycle) frames, on average.
144 if (gif_filename
== NULL
) {
145 gif_filename
= strdup(DEFAULT_FILE
);
146 mp_msg(MSGT_VO
, MSGL_V
, "GIF89a: default, file \"%s\"\n", gif_filename
);
148 mp_msg(MSGT_VO
, MSGL_V
, "GIF89a: file forced to \"%s\"\n", gif_filename
);
151 mp_msg(MSGT_VO
, MSGL_DBG2
, "GIF89a: Preinit OK\n");
155 static int config(uint32_t s_width
, uint32_t s_height
, uint32_t d_width
,
156 uint32_t d_height
, uint32_t flags
, char *title
,
160 // these are control blocks for the gif looping extension.
161 char LB1
[] = "NETSCAPE2.0";
162 char LB2
[] = { 1, 0, 0 };
165 mp_msg(MSGT_VO
, MSGL_DBG2
, "GIF89a: Config entered [%dx%d]\n", s_width
,s_height
);
166 mp_msg(MSGT_VO
, MSGL_DBG2
, "GIF89a: With requested format: %s\n", vo_format_name(format
));
168 // save these for later.
170 img_height
= s_height
;
172 // multiple configs without uninit are not allowed.
173 // this is because config opens a new gif file.
174 if (vo_config_count
> 0) {
175 mp_msg(MSGT_VO
, MSGL_V
, "GIF89a: Reconfigure attempted.\n");
178 // reconfigure need not be a fatal error, so return 0.
179 // multiple configs without uninit will result in two
180 // movies concatenated in one gif file. the output
181 // gif will have the dimensions of the first movie.
183 if (format
!= IMGFMT_RGB24
) {
184 mp_msg(MSGT_VO
, MSGL_ERR
, "GIF89a: Error - given unsupported colorspace.\n");
188 // the EGifSetGifVersion line causes segfaults in certain
189 // earlier versions of libungif. i don't know exactly which,
190 // but certainly in all those before v4. if you have problems,
191 // you need to upgrade your gif library.
193 EGifSetGifVersion("89a");
195 mp_msg(MSGT_VO
, MSGL_ERR
, "GIF89a: Your version of libungif needs to be upgraded.\n");
196 mp_msg(MSGT_VO
, MSGL_ERR
, "GIF89a: Some functionality has been disabled.\n");
199 new_gif
= EGifOpenFileName(gif_filename
, 0);
200 if (new_gif
== NULL
) {
201 mp_msg(MSGT_VO
, MSGL_ERR
, "GIF89a: error opening file \"%s\" for output.\n", gif_filename
);
205 slice_data
= malloc(img_width
* img_height
* 3);
206 if (slice_data
== NULL
) {
207 mp_msg(MSGT_VO
, MSGL_ERR
, "GIF89a: malloc failed.\n");
211 reduce_data
= malloc(img_width
* img_height
);
212 if (reduce_data
== NULL
) {
213 free(slice_data
); slice_data
= NULL
;
214 mp_msg(MSGT_VO
, MSGL_ERR
, "GIF89a: malloc failed.\n");
218 reduce_cmap
= MakeMapObject(256, NULL
);
219 if (reduce_cmap
== NULL
) {
220 free(slice_data
); slice_data
= NULL
;
221 free(reduce_data
); reduce_data
= NULL
;
222 mp_msg(MSGT_VO
, MSGL_ERR
, "GIF89a: malloc failed.\n");
226 // initialize the delay and framedrop variables.
232 // set the initial width and height info.
233 EGifPutScreenDesc(new_gif
, s_width
, s_height
, 256, 0, reduce_cmap
);
235 // version 3 of libungif does not support multiple control blocks.
236 // looping requires multiple control blocks.
237 // therefore, looping is only enabled for v4 and up.
238 EGifPutExtensionFirst(new_gif
, 0xFF, 11, LB1
);
239 EGifPutExtensionLast(new_gif
, 0, 3, LB2
);
242 mp_msg(MSGT_VO
, MSGL_DBG2
, "GIF89a: Config finished.\n");
246 // we do not draw osd.
249 // we do not handle events.
250 static void check_events(void) {}
252 static int gif_reduce(int width
, int height
, uint8_t *src
, uint8_t *dst
, GifColorType
*colors
)
254 unsigned char Ra
[width
* height
];
255 unsigned char Ga
[width
* height
];
256 unsigned char Ba
[width
* height
];
257 unsigned char *R
, *G
, *B
;
261 R
= Ra
; G
= Ga
; B
= Ba
;
262 for (i
= 0; i
< width
* height
; i
++)
269 R
= Ra
; G
= Ga
; B
= Ba
;
270 return QuantizeBuffer(width
, height
, &size
, R
, G
, B
, dst
, colors
);
273 static void flip_page(void)
275 char CB
[4]; // control block
280 if (cycle_pos
< frame_cycle
- frame_adj
)
281 return; // we are skipping this frame
283 // quantize the image
284 ret
= gif_reduce(img_width
, img_height
, slice_data
, reduce_data
, reduce_cmap
->Colors
);
285 if (ret
== GIF_ERROR
) {
286 mp_msg(MSGT_VO
, MSGL_ERR
, "GIF89a: Quantize failed.\n");
290 // calculate frame delays and frame skipping
291 ideal_time
+= ideal_delay
;
292 delay
= (int)(ideal_time
- real_time
);
294 frame_adj
+= cycle_pos
;
295 frame_adj
-= frame_cycle
;
298 // set up the delay control block
299 CB
[0] = (char)(delay
>> 8);
300 CB
[1] = (char)(delay
& 0xff);
304 // put the control block with delay info
305 EGifPutExtension(new_gif
, 0xF9, 0x04, CB
);
306 // put the image description
307 EGifPutImageDesc(new_gif
, 0, 0, img_width
, img_height
, 0, reduce_cmap
);
308 // put the image itself
309 EGifPutLine(new_gif
, reduce_data
, img_width
* img_height
);
312 static int draw_frame(uint8_t *src
[])
317 static int draw_slice(uint8_t *src
[], int stride
[], int w
, int h
, int x
, int y
)
321 dst
= slice_data
+ (img_width
* y
+ x
) * 3;
323 for (i
= 0; i
< h
; i
++) {
324 memcpy(dst
, frm
, w
* 3);
325 dst
+= (img_width
* 3);
331 static int query_format(uint32_t format
)
333 if (format
== IMGFMT_RGB24
)
334 return VFCAP_CSP_SUPPORTED
| VFCAP_CSP_SUPPORTED_BY_HW
| VFCAP_TIMER
| VFCAP_ACCEPT_STRIDE
;
338 static int control(uint32_t request
, void *data
, ...)
340 if (request
== VOCTRL_QUERY_FORMAT
) {
341 return query_format(*((uint32_t*)data
));
343 if (request
== VOCTRL_DUPLICATE_FRAME
) {
350 static void uninit(void)
352 mp_msg(MSGT_VO
, MSGL_DBG2
, "GIF89a: Uninit entered\n");
354 if (new_gif
!= NULL
) {
356 // comment the gif and close it
357 snprintf(temp
, 256, "MPlayer gif output v%2.2f-%d (c) %s\r\n",
358 MPLAYER_VERSION
, VO_GIF_REVISION
,
359 "joey@nicewarrior.org");
360 EGifPutComment(new_gif
, temp
);
361 EGifCloseFile(new_gif
); // also frees gif storage space.
364 // free our allocated ram
365 if (gif_filename
!= NULL
) free(gif_filename
);
366 if (slice_data
!= NULL
) free(slice_data
);
367 if (reduce_data
!= NULL
) free(reduce_data
);
368 if (reduce_cmap
!= NULL
) FreeMapObject(reduce_cmap
);
370 // set the pointers back to null.