1 /*****************************************************************************
2 * crop.c : Crop video plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2002 VideoLAN
5 * $Id: crop.c,v 1.3 2002/06/11 09:44:21 gbazin Exp $
7 * Authors: Samuel Hocevar <sam@zoy.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program 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
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
28 #include <stdlib.h> /* malloc(), free() */
34 #include "filter_common.h"
36 /*****************************************************************************
37 * Capabilities defined in the other files.
38 *****************************************************************************/
39 static void vout_getfunctions( function_list_t * p_function_list );
41 /*****************************************************************************
42 * Build configuration tree.
43 *****************************************************************************/
44 #define GEOMETRY_TEXT N_("Crop geometry")
45 #define GEOMETRY_LONGTEXT N_("Set the geometry of the zone to crop")
47 #define AUTOCROP_TEXT N_("Automatic cropping")
48 #define AUTOCROP_LONGTEXT N_("Activate automatic black border cropping")
51 ADD_CATEGORY_HINT( N_("Miscellaneous"), NULL )
52 ADD_STRING ( "crop-geometry", NULL, NULL, GEOMETRY_TEXT, GEOMETRY_LONGTEXT )
53 ADD_BOOL ( "autocrop", 0, NULL, AUTOCROP_TEXT, AUTOCROP_LONGTEXT )
57 SET_DESCRIPTION( _("image crop video module") )
58 /* Capability score set to 0 because we don't want to be spawned
59 * as a video output unless explicitly requested to */
60 ADD_CAPABILITY( VOUT_FILTER, 0 )
61 ADD_SHORTCUT( "crop" )
65 vout_getfunctions( &p_module->p_functions->vout );
68 MODULE_DEACTIVATE_START
69 MODULE_DEACTIVATE_STOP
71 /*****************************************************************************
72 * vout_sys_t: Crop video output method descriptor
73 *****************************************************************************
74 * This structure is part of the video output thread descriptor.
75 * It describes the Crop specific properties of an output thread.
76 *****************************************************************************/
79 vout_thread_t *p_vout;
81 unsigned int i_x, i_y;
82 unsigned int i_width, i_height, i_aspect;
84 vlc_bool_t b_autocrop;
86 /* Autocrop specific variables */
87 unsigned int i_lastchange;
91 /*****************************************************************************
93 *****************************************************************************/
94 static int vout_Create ( vout_thread_t * );
95 static int vout_Init ( vout_thread_t * );
96 static void vout_End ( vout_thread_t * );
97 static void vout_Destroy ( vout_thread_t * );
98 static int vout_Manage ( vout_thread_t * );
99 static void vout_Render ( vout_thread_t *, picture_t * );
100 static void vout_Display ( vout_thread_t *, picture_t * );
102 static void UpdateStats ( vout_thread_t *, picture_t * );
104 /*****************************************************************************
105 * Functions exported as capabilities. They are declared as static so that
106 * we don't pollute the namespace too much.
107 *****************************************************************************/
108 static void vout_getfunctions( function_list_t * p_function_list )
110 p_function_list->functions.vout.pf_create = vout_Create;
111 p_function_list->functions.vout.pf_init = vout_Init;
112 p_function_list->functions.vout.pf_end = vout_End;
113 p_function_list->functions.vout.pf_destroy = vout_Destroy;
114 p_function_list->functions.vout.pf_manage = vout_Manage;
115 p_function_list->functions.vout.pf_render = vout_Render;
116 p_function_list->functions.vout.pf_display = vout_Display;
119 /*****************************************************************************
120 * vout_Create: allocates Crop video thread output method
121 *****************************************************************************
122 * This function allocates and initializes a Crop vout method.
123 *****************************************************************************/
124 static int vout_Create( vout_thread_t *p_vout )
126 /* Allocate structure */
127 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
128 if( p_vout->p_sys == NULL )
130 msg_Err( p_vout, "out of memory" );
137 /*****************************************************************************
138 * vout_Init: initialize Crop video thread output method
139 *****************************************************************************/
140 static int vout_Init( vout_thread_t *p_vout )
146 I_OUTPUTPICTURES = 0;
148 p_vout->p_sys->i_lastchange = 0;
149 p_vout->p_sys->b_changed = 0;
151 /* Initialize the output structure */
152 p_vout->output.i_chroma = p_vout->render.i_chroma;
153 p_vout->output.i_width = p_vout->render.i_width;
154 p_vout->output.i_height = p_vout->render.i_height;
155 p_vout->output.i_aspect = p_vout->render.i_aspect;
157 /* Shall we use autocrop ? */
158 p_vout->p_sys->b_autocrop = config_GetInt( p_vout, "autocrop" );
160 /* Get geometry value from the user */
161 psz_var = config_GetPsz( p_vout, "crop-geometry" );
164 char *psz_parser, *psz_tmp;
166 psz_parser = psz_tmp = psz_var;
167 while( *psz_tmp && *psz_tmp != 'x' ) psz_tmp++;
172 p_vout->p_sys->i_width = atoi( psz_parser );
174 psz_parser = ++psz_tmp;
175 while( *psz_tmp && *psz_tmp != '+' ) psz_tmp++;
180 p_vout->p_sys->i_height = atoi( psz_parser );
182 psz_parser = ++psz_tmp;
183 while( *psz_tmp && *psz_tmp != '+' ) psz_tmp++;
188 p_vout->p_sys->i_x = atoi( psz_parser );
189 p_vout->p_sys->i_y = atoi( ++psz_tmp );
193 p_vout->p_sys->i_x = atoi( psz_parser );
195 ( p_vout->output.i_height - p_vout->p_sys->i_height ) / 2;
200 p_vout->p_sys->i_height = atoi( psz_parser );
202 ( p_vout->output.i_width - p_vout->p_sys->i_width ) / 2;
204 ( p_vout->output.i_height - p_vout->p_sys->i_height ) / 2;
209 p_vout->p_sys->i_width = atoi( psz_parser );
210 p_vout->p_sys->i_height = p_vout->output.i_height;
212 ( p_vout->output.i_width - p_vout->p_sys->i_width ) / 2;
214 ( p_vout->output.i_height - p_vout->p_sys->i_height ) / 2;
217 /* Check for validity */
218 if( p_vout->p_sys->i_x + p_vout->p_sys->i_width
219 > p_vout->output.i_width )
221 p_vout->p_sys->i_x = 0;
222 if( p_vout->p_sys->i_width > p_vout->output.i_width )
224 p_vout->p_sys->i_width = p_vout->output.i_width;
228 if( p_vout->p_sys->i_y + p_vout->p_sys->i_height
229 > p_vout->output.i_height )
231 p_vout->p_sys->i_y = 0;
232 if( p_vout->p_sys->i_height > p_vout->output.i_height )
234 p_vout->p_sys->i_height = p_vout->output.i_height;
242 p_vout->p_sys->i_width = p_vout->output.i_width;
243 p_vout->p_sys->i_height = p_vout->output.i_height;
244 p_vout->p_sys->i_x = p_vout->p_sys->i_y = 0;
247 /* Pheeew. Parsing done. */
248 msg_Dbg( p_vout, "cropping at %ix%i+%i+%i, %sautocropping",
249 p_vout->p_sys->i_width, p_vout->p_sys->i_height,
250 p_vout->p_sys->i_x, p_vout->p_sys->i_y,
251 p_vout->p_sys->b_autocrop ? "" : "not " );
253 /* Set current output image properties */
254 p_vout->p_sys->i_aspect = p_vout->output.i_aspect
255 * p_vout->output.i_height / p_vout->p_sys->i_height
256 * p_vout->p_sys->i_width / p_vout->output.i_width;
258 /* Try to open the real video output */
259 p_vout->p_sys->p_vout =
260 vout_CreateThread( p_vout,
261 p_vout->p_sys->i_width, p_vout->p_sys->i_height,
262 p_vout->render.i_chroma, p_vout->p_sys->i_aspect );
263 if( p_vout->p_sys->p_vout == NULL )
265 msg_Err( p_vout, "failed to create vout" );
269 ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
274 /*****************************************************************************
275 * vout_End: terminate Crop video thread output method
276 *****************************************************************************/
277 static void vout_End( vout_thread_t *p_vout )
281 /* Free the fake output buffers we allocated */
282 for( i_index = I_OUTPUTPICTURES ; i_index ; )
285 free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
289 /*****************************************************************************
290 * vout_Destroy: destroy Crop video thread output method
291 *****************************************************************************
292 * Terminate an output method created by CropCreateOutputMethod
293 *****************************************************************************/
294 static void vout_Destroy( vout_thread_t *p_vout )
296 vout_DestroyThread( p_vout->p_sys->p_vout );
297 free( p_vout->p_sys );
300 /*****************************************************************************
301 * vout_Manage: handle Crop events
302 *****************************************************************************
303 * This function should be called regularly by video output thread. It manages
304 * console events. It returns a non null value on error.
305 *****************************************************************************/
306 static int vout_Manage( vout_thread_t *p_vout )
308 if( !p_vout->p_sys->b_changed )
313 vout_DestroyThread( p_vout->p_sys->p_vout );
315 p_vout->p_sys->p_vout =
316 vout_CreateThread( p_vout,
317 p_vout->p_sys->i_width, p_vout->p_sys->i_height,
318 p_vout->render.i_chroma, p_vout->p_sys->i_aspect );
319 if( p_vout->p_sys->p_vout == NULL )
321 msg_Err( p_vout, "failed to create vout" );
325 p_vout->p_sys->b_changed = 0;
326 p_vout->p_sys->i_lastchange = 0;
331 /*****************************************************************************
332 * vout_Render: display previously rendered output
333 *****************************************************************************
334 * This function sends the currently rendered image to Crop image, waits
335 * until it is displayed and switches the two rendering buffers, preparing next
337 *****************************************************************************/
338 static void vout_Render( vout_thread_t *p_vout, picture_t *p_pic )
340 picture_t *p_outpic = NULL;
343 if( p_vout->p_sys->b_changed )
349 vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 )
352 if( p_vout->b_die || p_vout->b_error )
354 vout_DestroyPicture( p_vout->p_sys->p_vout, p_outpic );
358 msleep( VOUT_OUTMEM_SLEEP );
361 vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
362 vout_LinkPicture( p_vout->p_sys->p_vout, p_outpic );
364 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
366 u8 *p_in, *p_out, *p_out_end;
367 int i_in_pitch = p_pic->p[i_plane].i_pitch;
368 const int i_out_pitch = p_outpic->p[i_plane].i_pitch;
370 p_in = p_pic->p[i_plane].p_pixels
371 /* Skip the right amount of lines */
372 + i_in_pitch * ( p_pic->p[i_plane].i_lines * p_vout->p_sys->i_y
373 / p_vout->output.i_height )
374 /* Skip the right amount of columns */
375 + i_in_pitch * p_vout->p_sys->i_x / p_vout->output.i_width;
377 p_out = p_outpic->p[i_plane].p_pixels;
378 p_out_end = p_out + i_out_pitch * p_outpic->p[i_plane].i_lines;
380 while( p_out < p_out_end )
382 p_vout->p_vlc->pf_memcpy( p_out, p_in, i_out_pitch );
384 p_out += i_out_pitch;
388 vout_UnlinkPicture( p_vout->p_sys->p_vout, p_outpic );
389 vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
391 /* The source image may still be in the cache ... parse it! */
392 if( !p_vout->p_sys->b_autocrop )
397 UpdateStats( p_vout, p_pic );
400 /*****************************************************************************
401 * vout_Display: displays previously rendered output
402 *****************************************************************************
403 * This function send the currently rendered image to Invert image, waits
404 * until it is displayed and switch the two rendering buffers, preparing next
406 *****************************************************************************/
407 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
412 static void UpdateStats( vout_thread_t *p_vout, picture_t *p_pic )
414 u8 *p_in = p_pic->p[0].p_pixels;
415 int i_pitch = p_pic->p[0].i_pitch;
416 int i_lines = p_pic->p[0].i_lines;
417 int i_firstwhite = -1, i_lastwhite = -1, i;
419 /* Determine where black borders are */
420 switch( p_vout->output.i_chroma )
423 /* XXX: Do not laugh ! I know this is very naive. But it's just a
424 * proof of concept code snippet... */
425 for( i = i_lines ; i-- ; )
427 const int i_col = i * i_pitch / i_lines;
429 if( p_in[i_col/2] > 40
430 && p_in[i_pitch / 2] > 40
431 && p_in[i_pitch/2 + i_col/2] > 40 )
433 if( i_lastwhite == -1 )
447 /* Decide whether it's worth changing the size */
448 if( i_lastwhite == -1 )
450 p_vout->p_sys->i_lastchange = 0;
454 if( i_lastwhite - i_firstwhite < p_vout->p_sys->i_height / 2 )
456 p_vout->p_sys->i_lastchange = 0;
460 if( i_lastwhite - i_firstwhite < p_vout->p_sys->i_height + 16
461 && i_lastwhite - i_firstwhite + 16 > p_vout->p_sys->i_height )
463 p_vout->p_sys->i_lastchange = 0;
467 /* We need at least 25 images to make up our mind */
468 p_vout->p_sys->i_lastchange++;
469 if( p_vout->p_sys->i_lastchange < 25 )
474 /* Tune a few values */
475 if( i_firstwhite & 1 )
480 if( !(i_lastwhite & 1) )
486 p_vout->p_sys->i_y = i_firstwhite;
487 p_vout->p_sys->i_height = i_lastwhite - i_firstwhite + 1;
489 p_vout->p_sys->i_aspect = p_vout->output.i_aspect
490 * p_vout->output.i_height / p_vout->p_sys->i_height
491 * p_vout->p_sys->i_width / p_vout->output.i_width;
493 p_vout->p_sys->b_changed = 1;