1 /*****************************************************************************
2 * crop.c : Crop video plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2002 VideoLAN
5 * $Id: crop.c,v 1.6 2002/07/31 20:56:51 sam 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 /*****************************************************************************
38 *****************************************************************************/
39 static int Create ( vlc_object_t * );
40 static void Destroy ( vlc_object_t * );
42 static int Init ( vout_thread_t * );
43 static void End ( vout_thread_t * );
44 static int Manage ( vout_thread_t * );
45 static void Render ( vout_thread_t *, picture_t * );
47 static void UpdateStats ( vout_thread_t *, picture_t * );
49 /*****************************************************************************
51 *****************************************************************************/
52 #define GEOMETRY_TEXT N_("Crop geometry")
53 #define GEOMETRY_LONGTEXT N_("Set the geometry of the zone to crop")
55 #define AUTOCROP_TEXT N_("Automatic cropping")
56 #define AUTOCROP_LONGTEXT N_("Activate automatic black border cropping")
59 add_category_hint( N_("Miscellaneous"), NULL );
60 add_string( "crop-geometry", NULL, NULL, GEOMETRY_TEXT, GEOMETRY_LONGTEXT );
61 add_bool( "autocrop", 0, NULL, AUTOCROP_TEXT, AUTOCROP_LONGTEXT );
62 set_description( _("image crop video module") );
63 set_capability( "video filter", 0 );
64 add_shortcut( "crop" );
65 set_callbacks( Create, Destroy );
68 /*****************************************************************************
69 * vout_sys_t: Crop video output method descriptor
70 *****************************************************************************
71 * This structure is part of the video output thread descriptor.
72 * It describes the Crop specific properties of an output thread.
73 *****************************************************************************/
76 vout_thread_t *p_vout;
78 unsigned int i_x, i_y;
79 unsigned int i_width, i_height, i_aspect;
81 vlc_bool_t b_autocrop;
83 /* Autocrop specific variables */
84 unsigned int i_lastchange;
88 /*****************************************************************************
89 * Create: allocates Crop video thread output method
90 *****************************************************************************
91 * This function allocates and initializes a Crop vout method.
92 *****************************************************************************/
93 static int Create( vlc_object_t *p_this )
95 vout_thread_t *p_vout = (vout_thread_t *)p_this;
97 /* Allocate structure */
98 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
99 if( p_vout->p_sys == NULL )
101 msg_Err( p_vout, "out of memory" );
105 p_vout->pf_init = Init;
106 p_vout->pf_end = End;
107 p_vout->pf_manage = Manage;
108 p_vout->pf_render = Render;
109 p_vout->pf_display = NULL;
114 /*****************************************************************************
115 * Init: initialize Crop video thread output method
116 *****************************************************************************/
117 static int Init( vout_thread_t *p_vout )
123 I_OUTPUTPICTURES = 0;
125 p_vout->p_sys->i_lastchange = 0;
126 p_vout->p_sys->b_changed = 0;
128 /* Initialize the output structure */
129 p_vout->output.i_chroma = p_vout->render.i_chroma;
130 p_vout->output.i_width = p_vout->render.i_width;
131 p_vout->output.i_height = p_vout->render.i_height;
132 p_vout->output.i_aspect = p_vout->render.i_aspect;
134 /* Shall we use autocrop ? */
135 p_vout->p_sys->b_autocrop = config_GetInt( p_vout, "autocrop" );
137 /* Get geometry value from the user */
138 psz_var = config_GetPsz( p_vout, "crop-geometry" );
141 char *psz_parser, *psz_tmp;
143 psz_parser = psz_tmp = psz_var;
144 while( *psz_tmp && *psz_tmp != 'x' ) psz_tmp++;
149 p_vout->p_sys->i_width = atoi( psz_parser );
151 psz_parser = ++psz_tmp;
152 while( *psz_tmp && *psz_tmp != '+' ) psz_tmp++;
157 p_vout->p_sys->i_height = atoi( psz_parser );
159 psz_parser = ++psz_tmp;
160 while( *psz_tmp && *psz_tmp != '+' ) psz_tmp++;
165 p_vout->p_sys->i_x = atoi( psz_parser );
166 p_vout->p_sys->i_y = atoi( ++psz_tmp );
170 p_vout->p_sys->i_x = atoi( psz_parser );
172 ( p_vout->output.i_height - p_vout->p_sys->i_height ) / 2;
177 p_vout->p_sys->i_height = atoi( psz_parser );
179 ( p_vout->output.i_width - p_vout->p_sys->i_width ) / 2;
181 ( p_vout->output.i_height - p_vout->p_sys->i_height ) / 2;
186 p_vout->p_sys->i_width = atoi( psz_parser );
187 p_vout->p_sys->i_height = p_vout->output.i_height;
189 ( p_vout->output.i_width - p_vout->p_sys->i_width ) / 2;
191 ( p_vout->output.i_height - p_vout->p_sys->i_height ) / 2;
194 /* Check for validity */
195 if( p_vout->p_sys->i_x + p_vout->p_sys->i_width
196 > p_vout->output.i_width )
198 p_vout->p_sys->i_x = 0;
199 if( p_vout->p_sys->i_width > p_vout->output.i_width )
201 p_vout->p_sys->i_width = p_vout->output.i_width;
205 if( p_vout->p_sys->i_y + p_vout->p_sys->i_height
206 > p_vout->output.i_height )
208 p_vout->p_sys->i_y = 0;
209 if( p_vout->p_sys->i_height > p_vout->output.i_height )
211 p_vout->p_sys->i_height = p_vout->output.i_height;
219 p_vout->p_sys->i_width = p_vout->output.i_width;
220 p_vout->p_sys->i_height = p_vout->output.i_height;
221 p_vout->p_sys->i_x = p_vout->p_sys->i_y = 0;
224 /* Pheeew. Parsing done. */
225 msg_Dbg( p_vout, "cropping at %ix%i+%i+%i, %sautocropping",
226 p_vout->p_sys->i_width, p_vout->p_sys->i_height,
227 p_vout->p_sys->i_x, p_vout->p_sys->i_y,
228 p_vout->p_sys->b_autocrop ? "" : "not " );
230 /* Set current output image properties */
231 p_vout->p_sys->i_aspect = p_vout->output.i_aspect
232 * p_vout->output.i_height / p_vout->p_sys->i_height
233 * p_vout->p_sys->i_width / p_vout->output.i_width;
235 /* Try to open the real video output */
236 p_vout->p_sys->p_vout =
237 vout_CreateThread( p_vout,
238 p_vout->p_sys->i_width, p_vout->p_sys->i_height,
239 p_vout->render.i_chroma, p_vout->p_sys->i_aspect );
240 if( p_vout->p_sys->p_vout == NULL )
242 msg_Err( p_vout, "failed to create vout" );
246 ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
251 /*****************************************************************************
252 * End: terminate Crop video thread output method
253 *****************************************************************************/
254 static void End( vout_thread_t *p_vout )
258 /* Free the fake output buffers we allocated */
259 for( i_index = I_OUTPUTPICTURES ; i_index ; )
262 free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
266 /*****************************************************************************
267 * Destroy: destroy Crop video thread output method
268 *****************************************************************************
269 * Terminate an output method created by CropCreateOutputMethod
270 *****************************************************************************/
271 static void Destroy( vlc_object_t *p_this )
273 vout_thread_t *p_vout = (vout_thread_t *)p_this;
275 vout_DestroyThread( p_vout->p_sys->p_vout );
276 free( p_vout->p_sys );
279 /*****************************************************************************
280 * Manage: handle Crop events
281 *****************************************************************************
282 * This function should be called regularly by video output thread. It manages
283 * console events. It returns a non null value on error.
284 *****************************************************************************/
285 static int Manage( vout_thread_t *p_vout )
287 if( !p_vout->p_sys->b_changed )
292 vout_DestroyThread( p_vout->p_sys->p_vout );
294 p_vout->p_sys->p_vout =
295 vout_CreateThread( p_vout,
296 p_vout->p_sys->i_width, p_vout->p_sys->i_height,
297 p_vout->render.i_chroma, p_vout->p_sys->i_aspect );
298 if( p_vout->p_sys->p_vout == NULL )
300 msg_Err( p_vout, "failed to create vout" );
304 p_vout->p_sys->b_changed = 0;
305 p_vout->p_sys->i_lastchange = 0;
310 /*****************************************************************************
311 * Render: display previously rendered output
312 *****************************************************************************
313 * This function sends the currently rendered image to Crop image, waits
314 * until it is displayed and switches the two rendering buffers, preparing next
316 *****************************************************************************/
317 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
319 picture_t *p_outpic = NULL;
322 if( p_vout->p_sys->b_changed )
328 vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 )
331 if( p_vout->b_die || p_vout->b_error )
333 vout_DestroyPicture( p_vout->p_sys->p_vout, p_outpic );
337 msleep( VOUT_OUTMEM_SLEEP );
340 vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
341 vout_LinkPicture( p_vout->p_sys->p_vout, p_outpic );
343 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
345 u8 *p_in, *p_out, *p_out_end;
346 int i_in_pitch = p_pic->p[i_plane].i_pitch;
347 const int i_out_pitch = p_outpic->p[i_plane].i_pitch;
349 p_in = p_pic->p[i_plane].p_pixels
350 /* Skip the right amount of lines */
351 + i_in_pitch * ( p_pic->p[i_plane].i_lines * p_vout->p_sys->i_y
352 / p_vout->output.i_height )
353 /* Skip the right amount of columns */
354 + i_in_pitch * p_vout->p_sys->i_x / p_vout->output.i_width;
356 p_out = p_outpic->p[i_plane].p_pixels;
357 p_out_end = p_out + i_out_pitch * p_outpic->p[i_plane].i_lines;
359 while( p_out < p_out_end )
361 p_vout->p_vlc->pf_memcpy( p_out, p_in, i_out_pitch );
363 p_out += i_out_pitch;
367 vout_UnlinkPicture( p_vout->p_sys->p_vout, p_outpic );
368 vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
370 /* The source image may still be in the cache ... parse it! */
371 if( !p_vout->p_sys->b_autocrop )
376 UpdateStats( p_vout, p_pic );
379 static void UpdateStats( vout_thread_t *p_vout, picture_t *p_pic )
381 u8 *p_in = p_pic->p[0].p_pixels;
382 int i_pitch = p_pic->p[0].i_pitch;
383 int i_lines = p_pic->p[0].i_lines;
384 int i_firstwhite = -1, i_lastwhite = -1, i;
386 /* Determine where black borders are */
387 switch( p_vout->output.i_chroma )
389 case VLC_FOURCC('I','4','2','0'):
390 /* XXX: Do not laugh ! I know this is very naive. But it's just a
391 * proof of concept code snippet... */
392 for( i = i_lines ; i-- ; )
394 const int i_col = i * i_pitch / i_lines;
396 if( p_in[i_col/2] > 40
397 && p_in[i_pitch / 2] > 40
398 && p_in[i_pitch/2 + i_col/2] > 40 )
400 if( i_lastwhite == -1 )
414 /* Decide whether it's worth changing the size */
415 if( i_lastwhite == -1 )
417 p_vout->p_sys->i_lastchange = 0;
421 if( i_lastwhite - i_firstwhite < p_vout->p_sys->i_height / 2 )
423 p_vout->p_sys->i_lastchange = 0;
427 if( i_lastwhite - i_firstwhite < p_vout->p_sys->i_height + 16
428 && i_lastwhite - i_firstwhite + 16 > p_vout->p_sys->i_height )
430 p_vout->p_sys->i_lastchange = 0;
434 /* We need at least 25 images to make up our mind */
435 p_vout->p_sys->i_lastchange++;
436 if( p_vout->p_sys->i_lastchange < 25 )
441 /* Tune a few values */
442 if( i_firstwhite & 1 )
447 if( !(i_lastwhite & 1) )
453 p_vout->p_sys->i_y = i_firstwhite;
454 p_vout->p_sys->i_height = i_lastwhite - i_firstwhite + 1;
456 p_vout->p_sys->i_aspect = p_vout->output.i_aspect
457 * p_vout->output.i_height / p_vout->p_sys->i_height
458 * p_vout->p_sys->i_width / p_vout->output.i_width;
460 p_vout->p_sys->b_changed = 1;