vo_gl3: call glFlush() after frame drawing is complete
[mplayer.git] / libvo / vo_jpeg.c
blobefa03810863f013e89b3e6081c193f7d1ed977b1
1 /*
2 * JPEG Renderer for MPlayer
4 * Copyright (C) 2002 by Pontscho <pontscho@makacs.poliod.hu>
5 * Copyright (C) 2003 by Alex
6 * Copyright (C) 2004, 2005 by Ivo van Poorten <ivop@euronet.nl>
8 * This file is part of MPlayer.
10 * MPlayer is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * MPlayer is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 /* ------------------------------------------------------------------------- */
27 /* Global Includes */
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <jpeglib.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <unistd.h>
38 /* ------------------------------------------------------------------------- */
40 /* Local Includes */
42 #include "config.h"
43 #include "subopt-helper.h"
44 #include "mp_msg.h"
45 #include "video_out.h"
46 #include "video_out_internal.h"
47 #include "mplayer.h" /* for exit_player_bad() */
48 #include "osdep/io.h"
50 /* ------------------------------------------------------------------------- */
52 /* Defines */
54 /* Used for temporary buffers to store file- and pathnames */
55 #define BUFLENGTH 512
57 /* ------------------------------------------------------------------------- */
59 /* Info */
61 static const vo_info_t info=
63 "JPEG file",
64 "jpeg",
65 "Zoltan Ponekker (pontscho@makacs.poliod.hu)",
69 const LIBVO_EXTERN (jpeg)
71 /* ------------------------------------------------------------------------- */
73 /* Global Variables */
75 static int image_width;
76 static int image_height;
77 static int image_d_width;
78 static int image_d_height;
80 int jpeg_baseline = 1;
81 int jpeg_progressive_mode = 0;
82 int jpeg_optimize = 100;
83 int jpeg_smooth = 0;
84 int jpeg_quality = 75;
85 int jpeg_dpi = 72; /** Screen resolution = 72 dpi */
86 char *jpeg_outdir = NULL;
87 char *jpeg_subdirs = NULL;
88 int jpeg_maxfiles = 1000;
90 static int framenum = 0;
92 /* ------------------------------------------------------------------------- */
94 /** \brief Create a directory.
96 * This function creates a directory. If it already exists, it tests if
97 * it's a directory and not something else, and if it is, it tests whether
98 * the directory is writable or not.
100 * \param buf Pointer to directory name.
101 * \param verbose Verbose on success. If verbose is non-zero, it will print
102 * a message if it was successful in creating the directory.
104 * \return nothing In case anything fails, the player will exit. If it
105 * returns, everything went well.
108 static void jpeg_mkdir(const char *buf, int verbose) {
109 struct stat stat_p;
111 if ( mkdir(buf, 0755) < 0 ) {
112 switch (errno) { /* use switch in case other errors need to be caught
113 and handled in the future */
114 case EEXIST:
115 if ( mp_stat(buf, &stat_p ) < 0 ) {
116 mp_msg(MSGT_VO, MSGL_ERR, "%s: %s: %s\n", info.short_name,
117 _("This error has occurred"), strerror(errno) );
118 mp_msg(MSGT_VO, MSGL_ERR, "%s: %s %s\n", info.short_name,
119 _("Unable to access"), buf);
120 exit_player_bad(_("Fatal error"));
122 if ( !S_ISDIR(stat_p.st_mode) ) {
123 mp_msg(MSGT_VO, MSGL_ERR, "%s: %s %s\n", info.short_name,
124 buf, _("already exists, but is not a directory."));
125 exit_player_bad(_("Fatal error"));
127 if ( !(stat_p.st_mode & S_IWUSR) ) {
128 mp_msg(MSGT_VO, MSGL_ERR, "%s: %s - %s\n", info.short_name,
129 buf, _("Output directory already exists, but is not writable."));
130 exit_player_bad(_("Fatal error"));
133 mp_msg(MSGT_VO, MSGL_INFO, "%s: %s - %s\n", info.short_name,
134 buf, _("Output directory already exists and is writable."));
135 break;
137 default:
138 mp_msg(MSGT_VO, MSGL_ERR, "%s: %s: %s\n", info.short_name,
139 _("This error has occurred"), strerror(errno) );
140 mp_msg(MSGT_VO, MSGL_ERR, "%s: %s - %s\n", info.short_name,
141 buf, _("Unable to create output directory."));
142 exit_player_bad(_("Fatal error"));
143 } /* end switch */
144 } else if ( verbose ) {
145 mp_msg(MSGT_VO, MSGL_INFO, "%s: %s - %s\n", info.short_name,
146 buf, _("Output directory successfully created."));
147 } /* end if */
150 /* ------------------------------------------------------------------------- */
152 static int config(uint32_t width, uint32_t height, uint32_t d_width,
153 uint32_t d_height, uint32_t flags, char *title,
154 uint32_t format)
156 char buf[BUFLENGTH];
158 /* Create outdir. */
160 snprintf(buf, BUFLENGTH, "%s", jpeg_outdir);
162 jpeg_mkdir(buf, 1); /* This function only returns if creation was
163 successful. If not, the player will exit. */
165 image_height = height;
166 image_width = width;
167 /* Save for JFIF-Header PAR */
168 image_d_width = d_width;
169 image_d_height = d_height;
171 return 0;
174 /* ------------------------------------------------------------------------- */
176 static uint32_t jpeg_write(const char * name, uint8_t * buffer)
178 FILE *outfile;
179 struct jpeg_compress_struct cinfo;
180 struct jpeg_error_mgr jerr;
181 JSAMPROW row_pointer[1];
182 int row_stride;
184 if ( !buffer ) return 1;
185 if ( (outfile = fopen(name, "wb") ) == NULL ) {
186 mp_msg(MSGT_VO, MSGL_ERR, "\n%s: %s\n", info.short_name,
187 _("Unable to create output file."));
188 mp_msg(MSGT_VO, MSGL_ERR, "%s: %s: %s\n",
189 info.short_name, _("This error has occurred"),
190 strerror(errno) );
191 exit_player_bad(_("Fatal error"));
194 cinfo.err = jpeg_std_error(&jerr);
195 jpeg_create_compress(&cinfo);
196 jpeg_stdio_dest(&cinfo, outfile);
198 cinfo.image_width = image_width;
199 cinfo.image_height = image_height;
200 cinfo.input_components = 3;
201 cinfo.in_color_space = JCS_RGB;
203 jpeg_set_defaults(&cinfo);
204 /* Important: Header info must be set AFTER jpeg_set_defaults() */
205 cinfo.write_JFIF_header = TRUE;
206 cinfo.JFIF_major_version = 1;
207 cinfo.JFIF_minor_version = 2;
208 cinfo.density_unit = 1; /* 0=unknown, 1=dpi, 2=dpcm */
209 /* Image DPI is determined by Y_density, so we leave that at
210 jpeg_dpi if possible and crunch X_density instead (PAR > 1) */
211 cinfo.X_density = jpeg_dpi*image_width/image_d_width;
212 cinfo.Y_density = jpeg_dpi*image_height/image_d_height;
213 cinfo.write_Adobe_marker = TRUE;
215 jpeg_set_quality(&cinfo,jpeg_quality, jpeg_baseline);
216 cinfo.optimize_coding = jpeg_optimize;
217 cinfo.smoothing_factor = jpeg_smooth;
219 if ( jpeg_progressive_mode ) {
220 jpeg_simple_progression(&cinfo);
223 jpeg_start_compress(&cinfo, TRUE);
225 row_stride = image_width * 3;
226 while (cinfo.next_scanline < cinfo.image_height) {
227 row_pointer[0] = &buffer[cinfo.next_scanline * row_stride];
228 (void)jpeg_write_scanlines(&cinfo, row_pointer,1);
231 jpeg_finish_compress(&cinfo);
232 fclose(outfile);
233 jpeg_destroy_compress(&cinfo);
235 return 0;
238 /* ------------------------------------------------------------------------- */
240 static int draw_frame(uint8_t *src[])
242 static int framecounter = 0, subdircounter = 0;
243 char buf[BUFLENGTH];
244 static char subdirname[BUFLENGTH] = "";
246 /* Start writing to new subdirectory after a certain amount of frames */
247 if ( framecounter == jpeg_maxfiles ) {
248 framecounter = 0;
251 /* If framecounter is zero (or reset to zero), increment subdirectory
252 * number and create the subdirectory.
253 * If jpeg_subdirs is not set, do nothing and resort to old behaviour. */
254 if ( !framecounter && jpeg_subdirs ) {
255 subdircounter++;
256 snprintf(subdirname, BUFLENGTH, "%s%08d", jpeg_subdirs, subdircounter);
257 snprintf(buf, BUFLENGTH, "%s/%s", jpeg_outdir, subdirname);
258 jpeg_mkdir(buf, 0); /* This function only returns if creation was
259 successful. If not, the player will exit. */
262 framenum++;
264 /* snprintf the full pathname of the outputfile */
265 snprintf(buf, BUFLENGTH, "%s/%s/%08d.jpg", jpeg_outdir, subdirname,
266 framenum);
268 framecounter++;
270 return jpeg_write(buf, src[0]);
273 /* ------------------------------------------------------------------------- */
275 static void draw_osd(void)
279 /* ------------------------------------------------------------------------- */
281 static void flip_page (void)
285 /* ------------------------------------------------------------------------- */
287 static int draw_slice(uint8_t *src[], int stride[], int w, int h,
288 int x, int y)
290 return 0;
293 /* ------------------------------------------------------------------------- */
295 static int query_format(uint32_t format)
297 if (format == IMGFMT_RGB24) {
298 return VFCAP_CSP_SUPPORTED|VFCAP_CSP_SUPPORTED_BY_HW;
301 return 0;
304 /* ------------------------------------------------------------------------- */
306 static void uninit(void)
308 free(jpeg_subdirs);
309 jpeg_subdirs = NULL;
310 free(jpeg_outdir);
311 jpeg_outdir = NULL;
314 /* ------------------------------------------------------------------------- */
316 static void check_events(void)
320 /* ------------------------------------------------------------------------- */
322 /** \brief Validation function for values [0-100]
325 static int int_zero_hundred(void *valp)
327 int *val = valp;
328 return *val >= 0 && *val <= 100;
331 static int preinit(const char *arg)
333 const opt_t subopts[] = {
334 {"progressive", OPT_ARG_BOOL, &jpeg_progressive_mode, NULL},
335 {"baseline", OPT_ARG_BOOL, &jpeg_baseline, NULL},
336 {"optimize", OPT_ARG_INT, &jpeg_optimize,
337 int_zero_hundred},
338 {"smooth", OPT_ARG_INT, &jpeg_smooth,
339 int_zero_hundred},
340 {"quality", OPT_ARG_INT, &jpeg_quality,
341 int_zero_hundred},
342 {"dpi", OPT_ARG_INT, &jpeg_dpi, NULL},
343 {"outdir", OPT_ARG_MSTRZ, &jpeg_outdir, NULL},
344 {"subdirs", OPT_ARG_MSTRZ, &jpeg_subdirs, NULL},
345 {"maxfiles", OPT_ARG_INT, &jpeg_maxfiles, int_pos},
346 {NULL, 0, NULL, NULL}
348 const char *info_message = NULL;
350 mp_msg(MSGT_VO, MSGL_V, "%s: %s\n", info.short_name,
351 "Parsing suboptions.");
353 jpeg_progressive_mode = 0;
354 jpeg_baseline = 1;
355 jpeg_optimize = 100;
356 jpeg_smooth = 0;
357 jpeg_quality = 75;
358 jpeg_maxfiles = 1000;
359 jpeg_outdir = strdup(".");
360 jpeg_subdirs = NULL;
362 if (subopt_parse(arg, subopts) != 0) {
363 return -1;
366 if (jpeg_progressive_mode) info_message = _("Progressive JPEG enabled.");
367 else info_message = _("Progressive JPEG disabled.");
368 mp_msg(MSGT_VO, MSGL_INFO, "%s: %s\n", info.short_name, info_message);
370 if (jpeg_baseline) info_message = _("Baseline JPEG enabled.");
371 else info_message = _("Baseline JPEG disabled.");
372 mp_msg(MSGT_VO, MSGL_INFO, "%s: %s\n", info.short_name, info_message);
374 mp_msg(MSGT_VO, MSGL_V, "%s: optimize --> %d\n", info.short_name,
375 jpeg_optimize);
376 mp_msg(MSGT_VO, MSGL_V, "%s: smooth --> %d\n", info.short_name,
377 jpeg_smooth);
378 mp_msg(MSGT_VO, MSGL_V, "%s: quality --> %d\n", info.short_name,
379 jpeg_quality);
380 mp_msg(MSGT_VO, MSGL_V, "%s: dpi --> %d\n", info.short_name,
381 jpeg_dpi);
382 mp_msg(MSGT_VO, MSGL_V, "%s: outdir --> %s\n", info.short_name,
383 jpeg_outdir);
384 if (jpeg_subdirs) {
385 mp_msg(MSGT_VO, MSGL_V, "%s: subdirs --> %s\n", info.short_name,
386 jpeg_subdirs);
387 mp_msg(MSGT_VO, MSGL_V, "%s: maxfiles --> %d\n", info.short_name,
388 jpeg_maxfiles);
391 mp_msg(MSGT_VO, MSGL_V, "%s: %s\n", info.short_name,
392 "Suboptions parsed OK.");
393 return 0;
396 /* ------------------------------------------------------------------------- */
398 static int control(uint32_t request, void *data)
400 switch (request) {
401 case VOCTRL_QUERY_FORMAT:
402 return query_format(*((uint32_t*)data));
404 return VO_NOTIMPL;
407 /* ------------------------------------------------------------------------- */
409 #undef BUFLENGTH
411 /* ------------------------------------------------------------------------- */