2 MPlayer video driver for animated gif output
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.
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.
56 #include "subopt-helper.h"
57 #include "video_out.h"
58 #include "video_out_internal.h"
61 #define MPLAYER_VERSION 0.90
62 #define VO_GIF_REVISION 6
64 static const vo_info_t info
= {
65 "animated GIF output",
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.
84 // nominal framedrop cycle length in frames
85 static float frame_cycle
;
86 // position in the framedrop cycle
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
)
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"
123 " output=<filename>\n"
124 " Specify the output file. The default is out.gif.\n"
126 " Specify the target framerate. The default is 5.0.\n"
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
);
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
);
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");
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
,
161 // these are control blocks for the gif looping extension.
162 char LB1
[] = "NETSCAPE2.0";
163 char LB2
[] = { 1, 0, 0 };
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.
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");
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");
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.
194 EGifSetGifVersion("89a");
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");
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
);
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");
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");
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");
227 // initialize the delay and framedrop variables.
233 // set the initial width and height info.
234 EGifPutScreenDesc(new_gif
, s_width
, s_height
, 256, 0, reduce_cmap
);
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
);
243 mp_msg(MSGT_VO
, MSGL_DBG2
, "GIF89a: Config finished.\n");
247 // we do not 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
;
262 R
= Ra
; G
= Ga
; B
= Ba
;
263 for (i
= 0; i
< width
* height
; i
++)
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
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");
291 // calculate frame delays and frame skipping
292 ideal_time
+= ideal_delay
;
293 delay
= (int)(ideal_time
- real_time
);
295 frame_adj
+= cycle_pos
;
296 frame_adj
-= frame_cycle
;
299 // set up the delay control block
300 CB
[0] = (char)(delay
>> 8);
301 CB
[1] = (char)(delay
& 0xff);
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
[])
318 static int draw_slice(uint8_t *src
[], int stride
[], int w
, int h
, int x
, int y
)
322 dst
= slice_data
+ (img_width
* y
+ x
) * 3;
324 for (i
= 0; i
< h
; i
++) {
325 memcpy(dst
, frm
, w
* 3);
326 dst
+= (img_width
* 3);
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
;
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
) {
351 static void uninit(void)
353 mp_msg(MSGT_VO
, MSGL_DBG2
, "GIF89a: Uninit entered\n");
355 if (new_gif
!= NULL
) {
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.