subreader: fix unsafe sscanf calls with "%["
[mplayer.git] / libvo / vo_gif89a.c
blobb808f8182b5c52feacc595d526d4e5285bff96ee
1 /*
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.
24 /* Notes:
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.
47 #include <gif_lib.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
54 #include "config.h"
55 #include "subopt-helper.h"
56 #include "video_out.h"
57 #include "video_out_internal.h"
58 #include "mp_msg.h"
60 #define MPLAYER_VERSION 0.90
61 #define VO_GIF_REVISION 6
63 static const vo_info_t info = {
64 "animated GIF output",
65 "gif89a",
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.
82 static int real_time;
83 // nominal framedrop cycle length in frames
84 static float frame_cycle;
85 // position in the framedrop cycle
86 static int cycle_pos;
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 const opt_t subopts[] = {
108 {"output", OPT_ARG_MSTRZ, &gif_filename, NULL},
109 {"fps", OPT_ARG_FLOAT, &target_fps, NULL},
110 {NULL, 0, NULL, NULL}
113 static int preinit(const char *arg)
115 target_fps = 0;
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"
121 "\nOptions:\n"
122 " output=<filename>\n"
123 " Specify the output file. The default is out.gif.\n"
124 " fps=<rate>\n"
125 " Specify the target framerate. The default is 5.0.\n"
126 "\n");
127 return -1;
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);
136 } else {
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);
147 } else {
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");
152 return 0;
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,
157 uint32_t format)
159 #ifdef CONFIG_GIF_4
160 // these are control blocks for the gif looping extension.
161 char LB1[] = "NETSCAPE2.0";
162 char LB2[] = { 1, 0, 0 };
163 #endif
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.
169 img_width = s_width;
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");
176 return 0;
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");
185 return 1;
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.
192 #ifdef CONFIG_GIF_4
193 EGifSetGifVersion("89a");
194 #else
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");
197 #endif
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);
202 return 1;
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");
208 return 1;
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");
215 return 1;
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");
223 return 1;
226 // initialize the delay and framedrop variables.
227 ideal_time = 0;
228 real_time = 0;
229 cycle_pos = 0;
230 frame_adj = 0;
232 // set the initial width and height info.
233 EGifPutScreenDesc(new_gif, s_width, s_height, 256, 0, reduce_cmap);
234 #ifdef CONFIG_GIF_4
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);
240 #endif
242 mp_msg(MSGT_VO, MSGL_DBG2, "GIF89a: Config finished.\n");
243 return 0;
246 // we do not draw osd.
247 void draw_osd(void) {}
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;
258 int size = 256;
259 int i;
261 R = Ra; G = Ga; B = Ba;
262 for (i = 0; i < width * height; i++)
264 *R++ = *src++;
265 *G++ = *src++;
266 *B++ = *src++;
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
276 int delay = 0;
277 int ret;
279 cycle_pos++;
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");
287 return;
290 // calculate frame delays and frame skipping
291 ideal_time += ideal_delay;
292 delay = (int)(ideal_time - real_time);
293 real_time += delay;
294 frame_adj += cycle_pos;
295 frame_adj -= frame_cycle;
296 cycle_pos = 0;
298 // set up the delay control block
299 CB[0] = (char)(delay >> 8);
300 CB[1] = (char)(delay & 0xff);
301 CB[2] = 0;
302 CB[3] = 0;
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[])
314 return 1;
317 static int draw_slice(uint8_t *src[], int stride[], int w, int h, int x, int y)
319 uint8_t *dst, *frm;
320 int i;
321 dst = slice_data + (img_width * y + x) * 3;
322 frm = src[0];
323 for (i = 0; i < h; i++) {
324 memcpy(dst, frm, w * 3);
325 dst += (img_width * 3);
326 frm += stride[0];
328 return 0;
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;
335 return 0;
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) {
344 flip_page();
345 return VO_TRUE;
347 return VO_NOTIMPL;
350 static void uninit(void)
352 mp_msg(MSGT_VO, MSGL_DBG2, "GIF89a: Uninit entered\n");
354 if (new_gif != NULL) {
355 char temp[256];
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 free(gif_filename);
366 free(slice_data);
367 free(reduce_data);
368 if (reduce_cmap != NULL) FreeMapObject(reduce_cmap);
370 // set the pointers back to null.
371 new_gif = NULL;
372 gif_filename = NULL;
373 slice_data = NULL;
374 reduce_data = NULL;
375 reduce_cmap = NULL;