1 /*****************************************************************************
2 * crop.c : Crop video plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2002, 2003 the VideoLAN team
7 * Authors: Samuel Hocevar <sam@zoy.org>
8 * mod by Cedric Cocquebert <Cedric.Cocquebert@supelec.fr>
9 * based of DScaler idea (M. Samblanet)
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
37 #include <vlc_dialog.h>
39 #include "filter_common.h"
41 #define BEST_AUTOCROP 1
43 #define RATIO_MAX 15000 // 10*4/3 for a 360
46 /*****************************************************************************
48 *****************************************************************************/
49 static int Create ( vlc_object_t
* );
50 static void Destroy ( vlc_object_t
* );
52 static int Init ( vout_thread_t
* );
53 static void End ( vout_thread_t
* );
54 static int Manage ( vout_thread_t
* );
55 static void Render ( vout_thread_t
*, picture_t
* );
57 static void UpdateStats ( vout_thread_t
*, picture_t
* );
59 static int MouseEvent( vlc_object_t
*, char const *,
60 vlc_value_t
, vlc_value_t
, void * );
63 /*****************************************************************************
65 *****************************************************************************/
66 static int FilterCallback ( vlc_object_t
*, char const *,
67 vlc_value_t
, vlc_value_t
, void * );
70 /*****************************************************************************
72 *****************************************************************************/
73 #define GEOMETRY_TEXT N_("Crop geometry (pixels)")
74 #define GEOMETRY_LONGTEXT N_("Set the geometry of the zone to crop. This is set as <width> x <height> + <left offset> + <top offset>.")
76 #define AUTOCROP_TEXT N_("Automatic cropping")
77 #define AUTOCROP_LONGTEXT N_("Automatically detect black borders and crop them.")
79 #define CROP_HELP N_("Remove borders of the video and replace them by black borders")
82 #define RATIOMAX_TEXT N_("Ratio max (x 1000)")
83 #define RATIOMAX_LONGTEXT N_("Maximum image ratio. The crop plugin will never automatically crop to a higher ratio (ie, to a more \"flat\" image). The value is x1000: 1333 means 4/3.")
85 #define RATIO_TEXT N_("Manual ratio")
86 #define RATIO_LONGTEXT N_("Force a ratio (0 for automatic). Value is x1000: 1333 means 4/3.")
88 #define TIME_TEXT N_("Number of images for change")
89 #define TIME_LONGTEXT N_("The number of consecutive images with the same detected ratio (different from the previously detected ratio) to consider that ratio chnged and trigger recrop.")
91 #define DIFF_TEXT N_("Number of lines for change")
92 #define DIFF_LONGTEXT N_("The minimum difference in the number of detected black lines to consider that ratio changed and trigger recrop.")
94 #define NBP_TEXT N_("Number of non black pixels ")
95 #define NBP_LONGTEXT N_("The maximum of non-black pixels in a line to consider"\
96 " that the line is black.")
98 #define SKIP_TEXT N_("Skip percentage (%)")
99 #define SKIP_LONGTEXT N_("Percentage of the line to consider while checking for black lines. This allows to skip logos in black borders and crop them anyway.")
101 #define LUM_TEXT N_("Luminance threshold ")
102 #define LUM_LONGTEXT N_("Maximum luminance to consider a pixel as black (0-255).")
106 set_description( N_("Crop video filter") )
107 set_shortname( N_("Crop" ))
109 set_category( CAT_VIDEO
)
110 set_subcategory( SUBCAT_VIDEO_VFILTER
)
111 set_capability( "video filter", 0 )
113 add_string( "crop-geometry", NULL
, NULL
, GEOMETRY_TEXT
,
114 GEOMETRY_LONGTEXT
, false )
115 add_bool( "autocrop", false, NULL
, AUTOCROP_TEXT
,
116 AUTOCROP_LONGTEXT
, false )
119 add_integer_with_range( "autocrop-ratio-max", 2405, 0, RATIO_MAX
, NULL
,
120 RATIOMAX_TEXT
, RATIOMAX_LONGTEXT
, true )
122 add_integer_with_range( "crop-ratio", 0, 0, RATIO_MAX
, NULL
, RATIO_TEXT
,
123 RATIO_LONGTEXT
, false )
124 add_integer( "autocrop-time", 25, NULL
, TIME_TEXT
,
125 TIME_LONGTEXT
, true )
126 add_integer( "autocrop-diff", 16, NULL
, DIFF_TEXT
,
127 DIFF_LONGTEXT
, true )
129 add_integer( "autocrop-non-black-pixels", 3, NULL
,
130 NBP_TEXT
, NBP_LONGTEXT
, true )
132 add_integer_with_range( "autocrop-skip-percent", 17, 0, 100, NULL
,
133 SKIP_TEXT
, SKIP_LONGTEXT
, true )
135 add_integer_with_range( "autocrop-luminance-threshold", 40, 0, 128, NULL
,
136 LUM_TEXT
, LUM_LONGTEXT
, true )
137 #endif //BEST_AUTOCROP
139 add_shortcut( "crop" )
140 set_callbacks( Create
, Destroy
)
143 /*****************************************************************************
144 * vout_sys_t: Crop video output method descriptor
145 *****************************************************************************
146 * This structure is part of the video output thread descriptor.
147 * It describes the Crop specific properties of an output thread.
148 *****************************************************************************/
152 vout_thread_t
*p_vout
;
154 unsigned int i_x
, i_y
;
155 unsigned int i_width
, i_height
, i_aspect
;
159 /* Autocrop specific variables */
160 unsigned int i_lastchange
;
163 unsigned int i_ratio_max
;
164 unsigned int i_threshold
, i_skipPercent
, i_nonBlackPixel
, i_diff
, i_time
;
165 unsigned int i_ratio
;
170 /*****************************************************************************
171 * Control: control facility for the vout (forwards to child vout)
172 *****************************************************************************/
173 static int Control( vout_thread_t
*p_vout
, int i_query
, va_list args
)
175 return vout_vaControl( p_vout
->p_sys
->p_vout
, i_query
, args
);
178 /*****************************************************************************
179 * Create: allocates Crop video thread output method
180 *****************************************************************************
181 * This function allocates and initializes a Crop vout method.
182 *****************************************************************************/
183 static int Create( vlc_object_t
*p_this
)
185 vout_thread_t
*p_vout
= (vout_thread_t
*)p_this
;
187 /* Allocate structure */
188 p_vout
->p_sys
= malloc( sizeof( vout_sys_t
) );
189 if( p_vout
->p_sys
== NULL
)
192 p_vout
->pf_init
= Init
;
193 p_vout
->pf_end
= End
;
194 p_vout
->pf_manage
= Manage
;
195 p_vout
->pf_render
= Render
;
196 p_vout
->pf_display
= NULL
;
197 p_vout
->pf_control
= Control
;
202 /*****************************************************************************
203 * Init: initialize Crop video thread output method
204 *****************************************************************************/
205 static int Init( vout_thread_t
*p_vout
)
210 I_OUTPUTPICTURES
= 0;
211 memset( &fmt
, 0, sizeof(video_format_t
) );
213 p_vout
->p_sys
->i_lastchange
= 0;
214 p_vout
->p_sys
->b_changed
= false;
216 /* Initialize the output structure */
217 p_vout
->output
.i_chroma
= p_vout
->render
.i_chroma
;
218 p_vout
->output
.i_width
= p_vout
->render
.i_width
;
219 p_vout
->output
.i_height
= p_vout
->render
.i_height
;
220 p_vout
->output
.i_aspect
= p_vout
->render
.i_aspect
;
221 p_vout
->fmt_out
= p_vout
->fmt_in
;
223 /* Shall we use autocrop ? */
224 p_vout
->p_sys
->b_autocrop
= var_InheritBool( p_vout
, "autocrop" );
226 p_vout
->p_sys
->i_ratio_max
=
227 var_InheritInteger( p_vout
, "autocrop-ratio-max" );
228 p_vout
->p_sys
->i_threshold
=
229 var_InheritInteger( p_vout
, "autocrop-luminance-threshold" );
230 p_vout
->p_sys
->i_skipPercent
=
231 var_InheritInteger( p_vout
, "autocrop-skip-percent" );
232 p_vout
->p_sys
->i_nonBlackPixel
=
233 var_InheritInteger( p_vout
, "autocrop-non-black-pixels" );
234 p_vout
->p_sys
->i_diff
=
235 var_InheritInteger( p_vout
, "autocrop-diff" );
236 p_vout
->p_sys
->i_time
=
237 var_InheritInteger( p_vout
, "autocrop-time" );
238 var_SetString( p_vout
, "ratio-crop", "0" );
240 if (p_vout
->p_sys
->b_autocrop
)
241 p_vout
->p_sys
->i_ratio
= 0;
244 p_vout
->p_sys
->i_ratio
= var_InheritInteger( p_vout
, "crop-ratio" );
245 // ratio < width / height => ratio = 0 (unchange ratio)
246 if (p_vout
->p_sys
->i_ratio
< (p_vout
->output
.i_width
* 1000) / p_vout
->output
.i_height
)
247 p_vout
->p_sys
->i_ratio
= 0;
252 /* Get geometry value from the user */
253 psz_var
= var_InheritString( p_vout
, "crop-geometry" );
256 char *psz_parser
, *psz_tmp
;
258 psz_parser
= psz_tmp
= psz_var
;
259 while( *psz_tmp
&& *psz_tmp
!= 'x' ) psz_tmp
++;
264 p_vout
->p_sys
->i_width
= atoi( psz_parser
);
266 psz_parser
= ++psz_tmp
;
267 while( *psz_tmp
&& *psz_tmp
!= '+' ) psz_tmp
++;
272 p_vout
->p_sys
->i_height
= atoi( psz_parser
);
274 psz_parser
= ++psz_tmp
;
275 while( *psz_tmp
&& *psz_tmp
!= '+' ) psz_tmp
++;
280 p_vout
->p_sys
->i_x
= atoi( psz_parser
);
281 p_vout
->p_sys
->i_y
= atoi( ++psz_tmp
);
285 p_vout
->p_sys
->i_x
= atoi( psz_parser
);
287 ( p_vout
->output
.i_height
- p_vout
->p_sys
->i_height
) / 2;
292 p_vout
->p_sys
->i_height
= atoi( psz_parser
);
294 ( p_vout
->output
.i_width
- p_vout
->p_sys
->i_width
) / 2;
296 ( p_vout
->output
.i_height
- p_vout
->p_sys
->i_height
) / 2;
301 p_vout
->p_sys
->i_width
= atoi( psz_parser
);
302 p_vout
->p_sys
->i_height
= p_vout
->output
.i_height
;
304 ( p_vout
->output
.i_width
- p_vout
->p_sys
->i_width
) / 2;
306 ( p_vout
->output
.i_height
- p_vout
->p_sys
->i_height
) / 2;
309 /* Check for validity */
310 if( p_vout
->p_sys
->i_x
+ p_vout
->p_sys
->i_width
311 > p_vout
->output
.i_width
)
313 p_vout
->p_sys
->i_x
= 0;
314 if( p_vout
->p_sys
->i_width
> p_vout
->output
.i_width
)
316 p_vout
->p_sys
->i_width
= p_vout
->output
.i_width
;
320 if( p_vout
->p_sys
->i_y
+ p_vout
->p_sys
->i_height
321 > p_vout
->output
.i_height
)
323 p_vout
->p_sys
->i_y
= 0;
324 if( p_vout
->p_sys
->i_height
> p_vout
->output
.i_height
)
326 p_vout
->p_sys
->i_height
= p_vout
->output
.i_height
;
334 if (p_vout
->p_sys
->i_ratio
)
336 p_vout
->p_sys
->i_aspect
= p_vout
->p_sys
->i_ratio
* 432;
337 p_vout
->p_sys
->i_width
= p_vout
->fmt_out
.i_visible_width
;
338 p_vout
->p_sys
->i_height
= p_vout
->output
.i_aspect
339 * p_vout
->output
.i_height
/ p_vout
->p_sys
->i_aspect
340 * p_vout
->p_sys
->i_width
/ p_vout
->output
.i_width
;
341 p_vout
->p_sys
->i_height
+= p_vout
->p_sys
->i_height
% 2;
342 p_vout
->p_sys
->i_x
= p_vout
->fmt_out
.i_x_offset
;
343 p_vout
->p_sys
->i_y
= (p_vout
->output
.i_height
- p_vout
->p_sys
->i_height
) / 2;
348 p_vout
->p_sys
->i_width
= p_vout
->fmt_out
.i_visible_width
;
349 p_vout
->p_sys
->i_height
= p_vout
->fmt_out
.i_visible_height
;
350 p_vout
->p_sys
->i_x
= p_vout
->fmt_out
.i_x_offset
;
351 p_vout
->p_sys
->i_y
= p_vout
->fmt_out
.i_y_offset
;
354 /* Pheeew. Parsing done. */
355 msg_Dbg( p_vout
, "cropping at %ix%i+%i+%i, %sautocropping",
356 p_vout
->p_sys
->i_width
, p_vout
->p_sys
->i_height
,
357 p_vout
->p_sys
->i_x
, p_vout
->p_sys
->i_y
,
358 p_vout
->p_sys
->b_autocrop
? "" : "not " );
359 /* Set current output image properties */
360 p_vout
->p_sys
->i_aspect
= (int64_t)VOUT_ASPECT_FACTOR
*
361 p_vout
->fmt_out
.i_sar_num
* p_vout
->p_sys
->i_width
/
362 (p_vout
->fmt_out
.i_sar_den
* p_vout
->p_sys
->i_height
);
365 msg_Info( p_vout
, "ratio %d", p_vout
->p_sys
->i_aspect
/ 432);
367 fmt
.i_width
= fmt
.i_visible_width
= p_vout
->p_sys
->i_width
;
368 fmt
.i_height
= fmt
.i_visible_height
= p_vout
->p_sys
->i_height
;
369 fmt
.i_x_offset
= fmt
.i_y_offset
= 0;
370 fmt
.i_chroma
= p_vout
->render
.i_chroma
;
371 fmt
.i_sar_num
= p_vout
->p_sys
->i_aspect
* fmt
.i_height
;
372 fmt
.i_sar_den
= VOUT_ASPECT_FACTOR
* fmt
.i_width
;
374 /* Try to open the real video output */
375 p_vout
->p_sys
->p_vout
= vout_Create( p_vout
, &fmt
);
376 if( p_vout
->p_sys
->p_vout
== NULL
)
378 msg_Err( p_vout
, "failed to create vout" );
379 dialog_Fatal( p_vout
, _("Cropping failed"), "%s",
380 _("VLC could not open the video output module.") );
384 vlc_mutex_init( &p_vout
->p_sys
->lock
);
386 var_AddCallback( p_vout
, "ratio-crop", FilterCallback
, NULL
);
389 vout_filter_AllocateDirectBuffers( p_vout
, VOUT_MAX_PICTURES
);
391 vout_filter_AddChild( p_vout
, p_vout
->p_sys
->p_vout
, MouseEvent
);
396 /*****************************************************************************
397 * End: terminate Crop video thread output method
398 *****************************************************************************/
399 static void End( vout_thread_t
*p_vout
)
401 vout_sys_t
*p_sys
= p_vout
->p_sys
;
405 vout_filter_DelChild( p_vout
, p_sys
->p_vout
, MouseEvent
);
406 vout_CloseAndRelease( p_sys
->p_vout
);
409 vout_filter_ReleaseDirectBuffers( p_vout
);
410 var_DelCallback( p_vout
, "ratio-crop", FilterCallback
, NULL
);
411 vlc_mutex_destroy( &p_sys
->lock
);
414 /*****************************************************************************
415 * Destroy: destroy Crop video thread output method
416 *****************************************************************************
417 * Terminate an output method created by CropCreateOutputMethod
418 *****************************************************************************/
419 static void Destroy( vlc_object_t
*p_this
)
421 vout_thread_t
*p_vout
= (vout_thread_t
*)p_this
;
423 free( p_vout
->p_sys
);
426 /*****************************************************************************
427 * Manage: handle Crop events
428 *****************************************************************************
429 * This function should be called regularly by video output thread. It manages
430 * console events. It returns a non null value on error.
431 *****************************************************************************/
432 static int Manage( vout_thread_t
*p_vout
)
436 if( !p_vout
->p_sys
->b_changed
)
441 memset( &fmt
, 0, sizeof(video_format_t
) );
444 /* XXX: not thread-safe with FilterCallback */
445 msg_Dbg( p_vout
, "cropping at %ix%i+%i+%i, %sautocropping",
446 p_vout
->p_sys
->i_width
, p_vout
->p_sys
->i_height
,
447 p_vout
->p_sys
->i_x
, p_vout
->p_sys
->i_y
,
448 p_vout
->p_sys
->b_autocrop
? "" : "not " );
450 msg_Info( p_vout
, "ratio %d", p_vout
->p_sys
->i_aspect
/ 432);
453 if( p_vout
->p_sys
->p_vout
)
455 vout_filter_DelChild( p_vout
, p_vout
->p_sys
->p_vout
, MouseEvent
);
456 vout_CloseAndRelease( p_vout
->p_sys
->p_vout
);
459 fmt
.i_width
= fmt
.i_visible_width
= p_vout
->p_sys
->i_width
;
460 fmt
.i_height
= fmt
.i_visible_height
= p_vout
->p_sys
->i_height
;
461 fmt
.i_x_offset
= fmt
.i_y_offset
= 0;
462 fmt
.i_chroma
= p_vout
->render
.i_chroma
;
463 fmt
.i_sar_num
= p_vout
->p_sys
->i_aspect
* fmt
.i_height
/ fmt
.i_width
;
464 fmt
.i_sar_den
= VOUT_ASPECT_FACTOR
;
466 p_vout
->p_sys
->p_vout
= vout_Create( p_vout
, &fmt
);
467 if( p_vout
->p_sys
->p_vout
== NULL
)
469 msg_Err( p_vout
, "failed to create vout" );
470 dialog_Fatal( p_vout
, _("Cropping failed"), "%s",
471 _("VLC could not open the video output module.") );
474 vout_filter_AddChild( p_vout
, p_vout
->p_sys
->p_vout
, MouseEvent
);
476 p_vout
->p_sys
->b_changed
= false;
477 vlc_mutex_lock( &p_vout
->p_sys
->lock
);
478 p_vout
->p_sys
->i_lastchange
= 0;
479 vlc_mutex_unlock( &p_vout
->p_sys
->lock
);
484 /*****************************************************************************
485 * Render: display previously rendered output
486 *****************************************************************************
487 * This function sends the currently rendered image to Crop image, waits
488 * until it is displayed and switches the two rendering buffers, preparing next
490 *****************************************************************************/
491 static void Render( vout_thread_t
*p_vout
, picture_t
*p_pic
)
493 picture_t
*p_outpic
= NULL
;
496 if( p_vout
->p_sys
->b_changed
)
502 vout_CreatePicture( p_vout
->p_sys
->p_vout
, 0, 0, 0 )
505 if( !vlc_object_alive (p_vout
) || p_vout
->b_error
)
507 vout_DestroyPicture( p_vout
->p_sys
->p_vout
, p_outpic
);
511 msleep( VOUT_OUTMEM_SLEEP
);
514 p_outpic
->date
= p_pic
->date
;
515 vout_LinkPicture( p_vout
->p_sys
->p_vout
, p_outpic
);
517 for( i_plane
= 0 ; i_plane
< p_pic
->i_planes
; i_plane
++ )
519 uint8_t *p_in
, *p_out
, *p_out_end
;
520 int i_in_pitch
= p_pic
->p
[i_plane
].i_pitch
;
521 const int i_out_pitch
= p_outpic
->p
[i_plane
].i_pitch
;
522 const int i_copy_pitch
= p_outpic
->p
[i_plane
].i_visible_pitch
;
524 p_in
= p_pic
->p
[i_plane
].p_pixels
525 /* Skip the right amount of lines */
526 + i_in_pitch
* ( p_pic
->p
[i_plane
].i_visible_lines
*
527 p_vout
->p_sys
->i_y
/ p_vout
->output
.i_height
)
528 /* Skip the right amount of columns */
529 + i_in_pitch
* p_vout
->p_sys
->i_x
/ p_vout
->output
.i_width
;
531 p_out
= p_outpic
->p
[i_plane
].p_pixels
;
532 p_out_end
= p_out
+ i_out_pitch
* p_outpic
->p
[i_plane
].i_visible_lines
;
534 while( p_out
< p_out_end
)
536 vlc_memcpy( p_out
, p_in
, i_copy_pitch
);
538 p_out
+= i_out_pitch
;
542 vout_UnlinkPicture( p_vout
->p_sys
->p_vout
, p_outpic
);
543 vout_DisplayPicture( p_vout
->p_sys
->p_vout
, p_outpic
);
545 /* The source image may still be in the cache ... parse it! */
546 vlc_mutex_lock( &p_vout
->p_sys
->lock
);
547 if( p_vout
->p_sys
->b_autocrop
)
548 UpdateStats( p_vout
, p_pic
);
549 vlc_mutex_unlock( &p_vout
->p_sys
->lock
);
553 static bool NonBlackLine(uint8_t *p_in
, int i_line
, int i_pitch
,
554 int i_visible_pitch
, int i_lines
,
555 int i_lumThreshold
, int i_skipCountPercent
,
556 int i_nonBlackPixel
, int i_chroma
)
558 const int i_col
= i_line
* i_pitch
/ i_lines
;
559 int i_index
, i_count
= 0;
572 i_skipCount
= (i_pitch
* i_skipCountPercent
) / 100;
573 for (i_index
= i_col
/2 + i_skipCount
/2;
574 i_index
<= i_visible_pitch
/2 + i_col
/2 - i_skipCount
/2;
577 i_count
+= (p_in
[i_index
] > i_lumThreshold
);
578 if (i_count
> i_nonBlackPixel
) break;
582 case VLC_CODEC_RGB8
: // packed by 1
583 i_skipCount
= (i_pitch
* i_skipCountPercent
) / 100;
584 for (i_index
= i_col
/2 + i_skipCount
/2;
585 i_index
<= i_visible_pitch
/2 + i_col
/2 - i_skipCount
/2;
588 i_count
+= (p_in
[i_index
] > i_lumThreshold
);
589 if (i_count
> i_nonBlackPixel
) break;
592 case VLC_CODEC_RGB15
: // packed by 2
593 case VLC_CODEC_RGB16
: // packed by 2
594 i_skipCount
= (i_pitch
* i_skipCountPercent
) / 100;
595 for (i_index
= i_col
/2 + i_skipCount
/2 -
596 (i_col
/2 + i_skipCount
/2) % 2;
597 i_index
<= i_visible_pitch
/2 + i_col
/2 - i_skipCount
/2;
600 i_count
+= (p_in
[i_index
] > i_lumThreshold
) &&
601 (p_in
[i_index
+ 1] > i_lumThreshold
);
602 if (i_count
> i_nonBlackPixel
) break;
605 case VLC_CODEC_RGB24
: // packed by 3
606 i_skipCount
= (i_pitch
* i_skipCountPercent
) / 100;
607 for (i_index
= i_col
/2 + i_skipCount
/2 - (i_col
/2 + i_skipCount
/2) % 3; i_index
<= i_visible_pitch
/2 + i_col
/2 - i_skipCount
/2; i_index
+=3)
609 i_count
+= (p_in
[i_index
] > i_lumThreshold
) &&
610 (p_in
[i_index
+ 1] > i_lumThreshold
) &&
611 (p_in
[i_index
+ 2] > i_lumThreshold
);
612 if (i_count
> i_nonBlackPixel
) break;
615 case VLC_CODEC_RGB32
: // packed by 4
616 i_skipCount
= (i_pitch
* i_skipCountPercent
) / 100;
617 for (i_index
= i_col
/2 + i_skipCount
/2 - (i_col
/2 + i_skipCount
/2) % 4; i_index
<= i_visible_pitch
/2 + i_col
/2 - i_skipCount
/2; i_index
+=4)
619 i_count
+= (uint32_t)(*(p_in
+ i_index
)) > (uint32_t)i_lumThreshold
;
620 if (i_count
> i_nonBlackPixel
) break;
624 case VLC_CODEC_YUYV
: // packed by 2
625 case VLC_CODEC_UYVY
: // packed by 2
626 i_skipCount
= (i_pitch
* i_skipCountPercent
) / 100;
627 for (i_index
= (i_col
/2 + i_skipCount
/2) -
628 (i_col
/2 + i_skipCount
/2) % 2;
629 i_index
<= i_visible_pitch
/2 + i_col
/2 - i_skipCount
/2;
632 i_count
+= (p_in
[i_index
] > i_lumThreshold
);
633 if (i_count
> i_nonBlackPixel
) break;
639 return (i_count
> i_nonBlackPixel
);
643 static void UpdateStats( vout_thread_t
*p_vout
, picture_t
*p_pic
)
645 uint8_t *p_in
= p_pic
->p
[0].p_pixels
;
646 int i_pitch
= p_pic
->p
[0].i_pitch
;
647 int i_visible_pitch
= p_pic
->p
[0].i_visible_pitch
;
648 int i_lines
= p_pic
->p
[0].i_visible_lines
;
649 int i_firstwhite
= -1, i_lastwhite
= -1, i
;
651 int i_time
= p_vout
->p_sys
->i_time
;
652 int i_diff
= p_vout
->p_sys
->i_diff
;
654 if (!p_vout
->p_sys
->i_ratio
)
656 /* Determine where black borders are */
657 for( i
= 0 ; i
< i_lines
; i
++)
659 if (NonBlackLine(p_in
, i
, i_pitch
, i_visible_pitch
, i_lines
,
660 p_vout
->p_sys
->i_threshold
,
661 p_vout
->p_sys
->i_skipPercent
,
662 p_vout
->p_sys
->i_nonBlackPixel
,
663 p_vout
->output
.i_chroma
))
666 i_lastwhite
= i_lines
- i
;
672 /* Decide whether it's worth changing the size */
673 if( i_lastwhite
== -1 )
675 p_vout
->p_sys
->i_lastchange
= 0;
679 if( (i_lastwhite
- i_firstwhite
) < (int) (p_vout
->p_sys
->i_height
/ 2) )
681 p_vout
->p_sys
->i_lastchange
= 0;
685 if (p_vout
->output
.i_aspect
686 * p_vout
->output
.i_height
/
687 (i_lastwhite
- i_firstwhite
+ 1)
688 * p_vout
->p_sys
->i_width
/
689 p_vout
->output
.i_width
>
690 p_vout
->p_sys
->i_ratio_max
* 432)
692 int i_height
= ((p_vout
->output
.i_aspect
/ 432) *
693 p_vout
->output
.i_height
* p_vout
->p_sys
->i_width
) /
694 (p_vout
->output
.i_width
* p_vout
->p_sys
->i_ratio_max
);
695 i_firstwhite
= (p_vout
->output
.i_height
- i_height
) / 2;
696 i_lastwhite
= p_vout
->output
.i_height
- i_firstwhite
;
698 p_vout->p_sys->i_lastchange = 0;
703 if( (i_lastwhite
- i_firstwhite
) <
704 (int) (p_vout
->p_sys
->i_height
+ i_diff
)
705 && (i_lastwhite
- i_firstwhite
+ i_diff
) >
706 (int) p_vout
->p_sys
->i_height
)
708 p_vout
->p_sys
->i_lastchange
= 0;
712 /* We need at least 'i_time' images to make up our mind */
713 p_vout
->p_sys
->i_lastchange
++;
714 if( p_vout
->p_sys
->i_lastchange
< (unsigned int)i_time
)
721 if ( p_vout
->p_sys
->i_lastchange
>= (unsigned int)i_time
)
723 p_vout
->p_sys
->i_aspect
= p_vout
->p_sys
->i_ratio
* 432;
724 int i_height
= p_vout
->output
.i_aspect
725 * p_vout
->output
.i_height
/
726 p_vout
->p_sys
->i_aspect
727 * p_vout
->p_sys
->i_width
/
728 p_vout
->output
.i_width
;
729 i_firstwhite
= (p_vout
->output
.i_height
- i_height
) / 2;
730 i_lastwhite
= p_vout
->output
.i_height
- i_firstwhite
;
739 /* Determine where black borders are */
740 switch( p_vout
->output
.i_chroma
)
743 /* XXX: Do not laugh ! I know this is very naive. But it's just a
744 * proof of concept code snippet... */
745 for( i
= i_lines
; i
-- ; )
747 const int i_col
= i
* i_pitch
/ i_lines
;
749 if( p_in
[i_col
/2] > 40
750 && p_in
[i_visible_pitch
/2] > 40
751 && p_in
[i_visible_pitch
/2 + i_col
/2] > 40 )
753 if( i_lastwhite
== -1 )
767 /* Decide whether it's worth changing the size */
768 if( i_lastwhite
== -1 )
770 p_vout
->p_sys
->i_lastchange
= 0;
774 if( (unsigned int)(i_lastwhite
- i_firstwhite
)
775 < p_vout
->p_sys
->i_height
/ 2 )
777 p_vout
->p_sys
->i_lastchange
= 0;
781 if( (unsigned int)(i_lastwhite
- i_firstwhite
)
782 < p_vout
->p_sys
->i_height
+ 16
783 && (unsigned int)(i_lastwhite
- i_firstwhite
+ 16)
784 > p_vout
->p_sys
->i_height
)
786 p_vout
->p_sys
->i_lastchange
= 0;
790 /* We need at least 25 images to make up our mind */
791 p_vout
->p_sys
->i_lastchange
++;
792 if( p_vout
->p_sys
->i_lastchange
< 25 )
796 #endif //BEST_AUTOCROP
798 /* Tune a few values */
799 if( i_firstwhite
& 1 )
804 if( !(i_lastwhite
& 1) )
810 p_vout
->p_sys
->i_y
= i_firstwhite
;
811 p_vout
->p_sys
->i_height
= i_lastwhite
- i_firstwhite
+ 1;
813 // check p_vout->p_sys->i_height <= p_vout->output.i_height
814 if (p_vout
->p_sys
->i_height
> p_vout
->output
.i_height
)
815 p_vout
->p_sys
->i_height
= p_vout
->output
.i_height
;
818 p_vout
->p_sys
->i_aspect
= p_vout
->output
.i_aspect
819 * p_vout
->output
.i_height
/ p_vout
->p_sys
->i_height
820 * p_vout
->p_sys
->i_width
/ p_vout
->output
.i_width
;
822 p_vout
->p_sys
->b_changed
= true;
826 * Forward mouse event with proper conversion.
828 static int MouseEvent( vlc_object_t
*p_this
, char const *psz_var
,
829 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
831 vout_thread_t
*p_vout
= p_data
;
832 VLC_UNUSED(p_this
); VLC_UNUSED(oldval
);
834 /* Translate the mouse coordinates
835 * FIXME missing lock */
836 if( !strcmp( psz_var
, "mouse-x" ) )
837 newval
.i_int
+= p_vout
->p_sys
->i_x
;
838 else if( !strcmp( psz_var
, "mouse-y" ) )
839 newval
.i_int
+= p_vout
->p_sys
->i_y
;
841 return var_Set( p_vout
, psz_var
, newval
);
845 /*****************************************************************************
846 * FilterCallback: called when changing the ratio on the fly.
847 *****************************************************************************/
848 static int FilterCallback( vlc_object_t
*p_this
, char const *psz_var
,
849 vlc_value_t oldval
, vlc_value_t newval
,
852 VLC_UNUSED(p_data
); VLC_UNUSED(oldval
);
853 vout_thread_t
* p_vout
= (vout_thread_t
*)p_this
;
855 if( !strcmp( psz_var
, "ratio-crop" ) )
857 vlc_mutex_lock( &p_vout
->p_sys
->lock
);
858 if ( !strcmp( newval
.psz_string
, "Auto" ) )
859 p_vout
->p_sys
->i_ratio
= 0;
862 p_vout
->p_sys
->i_ratio
= (unsigned int)atoi(newval
.psz_string
);
863 p_vout
->p_sys
->i_lastchange
= p_vout
->p_sys
->i_time
;
864 p_vout
->p_sys
->b_autocrop
= true;
866 if (p_vout
->p_sys
->i_ratio
)
868 if (p_vout
->p_sys
->i_ratio
< (p_vout
->output
.i_width
* 1000) /
869 p_vout
->output
.i_height
)
870 p_vout
->p_sys
->i_ratio
= (p_vout
->output
.i_width
* 1000) /
871 p_vout
->output
.i_height
;
872 if (p_vout
->p_sys
->i_ratio
< p_vout
->output
.i_aspect
/ 432)
873 p_vout
->p_sys
->i_ratio
= p_vout
->output
.i_aspect
/ 432;
875 vlc_mutex_unlock( &p_vout
->p_sys
->lock
);