Fix crash during early exit
[mplayer.git] / libvo / vo_gif89a.c
blob58da54e8cb7a204d3c0fefef372a0974337f5a46
1 /*
2 MPlayer video driver for animated gif output
4 (C) 2002
6 Written by Joey Parrish <joey@nicewarrior.org>
7 Based on vo_directfb2.c
9 This library 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 of the License, or (at your option) any later version.
14 This library 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
20 License along with this library; if not, write to the
21 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 Boston, MA 02110-1301 USA.
25 /* Notes:
26 * when setting output framerate, frames will be ignored as needed
27 * to achieve the desired rate. no frames will be duplicated.
29 * output framerate can be specified as a float
30 * value now, instead of just an int.
32 * adjustments will be made to both the frame drop cycle and the
33 * delay per frame to achieve the desired output framerate.
35 * time values are in centiseconds, because that's
36 * what the gif spec uses for it's delay values.
38 * preinit looks for arguments in one of the following formats (in this order):
39 * fps:filename -- sets the framerate (float) and output file
40 * fps -- sets the framerate (float), default file out.gif
41 * filename -- defaults to 5 fps, sets output file
42 * (none) -- defaults to 5 fps, output file out.gif
44 * trying to put the filename before the framerate will result in the
45 * entire argument being interpretted as the filename.
48 #include <gif_lib.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
55 #include "config.h"
56 #include "subopt-helper.h"
57 #include "video_out.h"
58 #include "video_out_internal.h"
59 #include "mp_msg.h"
61 #define MPLAYER_VERSION 0.90
62 #define VO_GIF_REVISION 6
64 static const vo_info_t info = {
65 "animated GIF output",
66 "gif89a",
67 "Joey Parrish joey@nicewarrior.org",
71 const LIBVO_EXTERN(gif89a)
74 // how many frames per second we are aiming for during output.
75 static float target_fps;
76 // default value for output fps.
77 static const float default_fps = 5.00;
78 // the ideal gif delay per frame.
79 static float ideal_delay;
80 // the ideal time thus far.
81 static float ideal_time;
82 // actual time thus far.
83 static int real_time;
84 // nominal framedrop cycle length in frames
85 static float frame_cycle;
86 // position in the framedrop cycle
87 static int cycle_pos;
88 // adjustment of the framedrop cycle
89 static float frame_adj;
91 // the output width and height
92 static uint32_t img_width;
93 static uint32_t img_height;
94 // image data for slice rendering
95 static uint8_t *slice_data = NULL;
96 // reduced image data for flip_page
97 static uint8_t *reduce_data = NULL;
98 // reduced color map for flip_page
99 static ColorMapObject *reduce_cmap = NULL;
101 // a pointer to the gif structure
102 static GifFileType *new_gif = NULL;
103 // a string to contain the filename of the output gif
104 static char *gif_filename = NULL;
105 // the default output filename
106 #define DEFAULT_FILE "out.gif"
108 static opt_t subopts[] = {
109 {"output", OPT_ARG_MSTRZ, &gif_filename, NULL, 0},
110 {"fps", OPT_ARG_FLOAT, &target_fps, NULL, 0},
111 {NULL, 0, NULL, NULL, 0}
114 static int preinit(const char *arg)
116 target_fps = 0;
118 if (subopt_parse(arg, subopts) != 0) {
119 mp_msg(MSGT_VO, MSGL_FATAL,
120 "\n-vo gif89a command line help:\n"
121 "Example: mplayer -vo gif89a:output=file.gif:fps=4.9\n"
122 "\nOptions:\n"
123 " output=<filename>\n"
124 " Specify the output file. The default is out.gif.\n"
125 " fps=<rate>\n"
126 " Specify the target framerate. The default is 5.0.\n"
127 "\n");
128 return -1;
131 if (target_fps > vo_fps)
132 target_fps = vo_fps; // i will not duplicate frames.
134 if (target_fps <= 0) {
135 target_fps = default_fps;
136 mp_msg(MSGT_VO, MSGL_V, "GIF89a: default, %.2f fps\n", target_fps);
137 } else {
138 mp_msg(MSGT_VO, MSGL_V, "GIF89a: output fps forced to %.2f\n", target_fps);
141 ideal_delay = 100 / target_fps; // in centiseconds
142 frame_cycle = vo_fps / target_fps;
143 // we make one output frame every (frame_cycle) frames, on average.
145 if (gif_filename == NULL) {
146 gif_filename = strdup(DEFAULT_FILE);
147 mp_msg(MSGT_VO, MSGL_V, "GIF89a: default, file \"%s\"\n", gif_filename);
148 } else {
149 mp_msg(MSGT_VO, MSGL_V, "GIF89a: file forced to \"%s\"\n", gif_filename);
152 mp_msg(MSGT_VO, MSGL_DBG2, "GIF89a: Preinit OK\n");
153 return 0;
156 static int config(uint32_t s_width, uint32_t s_height, uint32_t d_width,
157 uint32_t d_height, uint32_t flags, char *title,
158 uint32_t format)
160 #ifdef HAVE_GIF_4
161 // these are control blocks for the gif looping extension.
162 char LB1[] = "NETSCAPE2.0";
163 char LB2[] = { 1, 0, 0 };
164 #endif
166 mp_msg(MSGT_VO, MSGL_DBG2, "GIF89a: Config entered [%dx%d]\n", s_width,s_height);
167 mp_msg(MSGT_VO, MSGL_DBG2, "GIF89a: With requested format: %s\n", vo_format_name(format));
169 // save these for later.
170 img_width = s_width;
171 img_height = s_height;
173 // multiple configs without uninit are not allowed.
174 // this is because config opens a new gif file.
175 if (vo_config_count > 0) {
176 mp_msg(MSGT_VO, MSGL_V, "GIF89a: Reconfigure attempted.\n");
177 return 0;
179 // reconfigure need not be a fatal error, so return 0.
180 // multiple configs without uninit will result in two
181 // movies concatenated in one gif file. the output
182 // gif will have the dimensions of the first movie.
184 if (format != IMGFMT_RGB24) {
185 mp_msg(MSGT_VO, MSGL_ERR, "GIF89a: Error - given unsupported colorspace.\n");
186 return 1;
189 // the EGifSetGifVersion line causes segfaults in certain
190 // earlier versions of libungif. i don't know exactly which,
191 // but certainly in all those before v4. if you have problems,
192 // you need to upgrade your gif library.
193 #ifdef HAVE_GIF_4
194 EGifSetGifVersion("89a");
195 #else
196 mp_msg(MSGT_VO, MSGL_ERR, "GIF89a: Your version of libungif needs to be upgraded.\n");
197 mp_msg(MSGT_VO, MSGL_ERR, "GIF89a: Some functionality has been disabled.\n");
198 #endif
200 new_gif = EGifOpenFileName(gif_filename, 0);
201 if (new_gif == NULL) {
202 mp_msg(MSGT_VO, MSGL_ERR, "GIF89a: error opening file \"%s\" for output.\n", gif_filename);
203 return 1;
206 slice_data = malloc(img_width * img_height * 3);
207 if (slice_data == NULL) {
208 mp_msg(MSGT_VO, MSGL_ERR, "GIF89a: malloc failed.\n");
209 return 1;
212 reduce_data = malloc(img_width * img_height);
213 if (reduce_data == NULL) {
214 free(slice_data); slice_data = NULL;
215 mp_msg(MSGT_VO, MSGL_ERR, "GIF89a: malloc failed.\n");
216 return 1;
219 reduce_cmap = MakeMapObject(256, NULL);
220 if (reduce_cmap == NULL) {
221 free(slice_data); slice_data = NULL;
222 free(reduce_data); reduce_data = NULL;
223 mp_msg(MSGT_VO, MSGL_ERR, "GIF89a: malloc failed.\n");
224 return 1;
227 // initialize the delay and framedrop variables.
228 ideal_time = 0;
229 real_time = 0;
230 cycle_pos = 0;
231 frame_adj = 0;
233 // set the initial width and height info.
234 EGifPutScreenDesc(new_gif, s_width, s_height, 256, 0, reduce_cmap);
235 #ifdef HAVE_GIF_4
236 // version 3 of libungif does not support multiple control blocks.
237 // looping requires multiple control blocks.
238 // therefore, looping is only enabled for v4 and up.
239 EGifPutExtensionFirst(new_gif, 0xFF, 11, LB1);
240 EGifPutExtensionLast(new_gif, 0, 3, LB2);
241 #endif
243 mp_msg(MSGT_VO, MSGL_DBG2, "GIF89a: Config finished.\n");
244 return 0;
247 // we do not draw osd.
248 void draw_osd() {}
250 // we do not handle events.
251 static void check_events(void) {}
253 static int gif_reduce(int width, int height, uint8_t *src, uint8_t *dst, GifColorType *colors)
255 unsigned char Ra[width * height];
256 unsigned char Ga[width * height];
257 unsigned char Ba[width * height];
258 unsigned char *R, *G, *B;
259 int size = 256;
260 int i;
262 R = Ra; G = Ga; B = Ba;
263 for (i = 0; i < width * height; i++)
265 *R++ = *src++;
266 *G++ = *src++;
267 *B++ = *src++;
270 R = Ra; G = Ga; B = Ba;
271 return QuantizeBuffer(width, height, &size, R, G, B, dst, colors);
274 static void flip_page(void)
276 char CB[4]; // control block
277 int delay = 0;
278 int ret;
280 cycle_pos++;
281 if (cycle_pos < frame_cycle - frame_adj)
282 return; // we are skipping this frame
284 // quantize the image
285 ret = gif_reduce(img_width, img_height, slice_data, reduce_data, reduce_cmap->Colors);
286 if (ret == GIF_ERROR) {
287 mp_msg(MSGT_VO, MSGL_ERR, "GIF89a: Quantize failed.\n");
288 return;
291 // calculate frame delays and frame skipping
292 ideal_time += ideal_delay;
293 delay = (int)(ideal_time - real_time);
294 real_time += delay;
295 frame_adj += cycle_pos;
296 frame_adj -= frame_cycle;
297 cycle_pos = 0;
299 // set up the delay control block
300 CB[0] = (char)(delay >> 8);
301 CB[1] = (char)(delay & 0xff);
302 CB[2] = 0;
303 CB[3] = 0;
305 // put the control block with delay info
306 EGifPutExtension(new_gif, 0xF9, 0x04, CB);
307 // put the image description
308 EGifPutImageDesc(new_gif, 0, 0, img_width, img_height, 0, reduce_cmap);
309 // put the image itself
310 EGifPutLine(new_gif, reduce_data, img_width * img_height);
313 static int draw_frame(uint8_t *src[])
315 return 1;
318 static int draw_slice(uint8_t *src[], int stride[], int w, int h, int x, int y)
320 uint8_t *dst, *frm;
321 int i;
322 dst = slice_data + (img_width * y + x) * 3;
323 frm = src[0];
324 for (i = 0; i < h; i++) {
325 memcpy(dst, frm, w * 3);
326 dst += (img_width * 3);
327 frm += stride[0];
329 return 0;
332 static int query_format(uint32_t format)
334 if (format == IMGFMT_RGB24)
335 return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_TIMER | VFCAP_ACCEPT_STRIDE;
336 return 0;
339 static int control(uint32_t request, void *data)
341 if (request == VOCTRL_QUERY_FORMAT) {
342 return query_format(*((uint32_t*)data));
344 if (request == VOCTRL_DUPLICATE_FRAME) {
345 flip_page();
346 return VO_TRUE;
348 return VO_NOTIMPL;
351 static void uninit(void)
353 mp_msg(MSGT_VO, MSGL_DBG2, "GIF89a: Uninit entered\n");
355 if (new_gif != NULL) {
356 char temp[256];
357 // comment the gif and close it
358 snprintf(temp, 256, "MPlayer gif output v%2.2f-%d (c) %s\r\n",
359 MPLAYER_VERSION, VO_GIF_REVISION,
360 "joey@nicewarrior.org");
361 EGifPutComment(new_gif, temp);
362 EGifCloseFile(new_gif); // also frees gif storage space.
365 // free our allocated ram
366 if (gif_filename != NULL) free(gif_filename);
367 if (slice_data != NULL) free(slice_data);
368 if (reduce_data != NULL) free(reduce_data);
369 if (reduce_cmap != NULL) FreeMapObject(reduce_cmap);
371 // set the pointers back to null.
372 new_gif = NULL;
373 gif_filename = NULL;
374 slice_data = NULL;
375 reduce_data = NULL;
376 reduce_cmap = NULL;