]> git.sesse.net Git - vlc/blob - plugins/filter/crop.c
* ALL: decoders now use a fourcc as a probe value.
[vlc] / plugins / filter / crop.c
1 /*****************************************************************************
2  * crop.c : Crop video plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2002 VideoLAN
5  * $Id: crop.c,v 1.5 2002/07/23 00:39:17 sam Exp $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *
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.
13  * 
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.
18  *
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  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <errno.h>
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <string.h>
30
31 #include <vlc/vlc.h>
32 #include <vlc/vout.h>
33
34 #include "filter_common.h"
35
36 /*****************************************************************************
37  * Capabilities defined in the other files.
38  *****************************************************************************/
39 static void vout_getfunctions( function_list_t * p_function_list );
40
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")
46
47 #define AUTOCROP_TEXT N_("Automatic cropping")
48 #define AUTOCROP_LONGTEXT N_("Activate automatic black border cropping")
49
50 MODULE_CONFIG_START
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 )
54 MODULE_CONFIG_STOP
55
56 MODULE_INIT_START
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" )
62 MODULE_INIT_STOP
63
64 MODULE_ACTIVATE_START
65     vout_getfunctions( &p_module->p_functions->vout );
66 MODULE_ACTIVATE_STOP
67
68 MODULE_DEACTIVATE_START
69 MODULE_DEACTIVATE_STOP
70
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  *****************************************************************************/
77 struct vout_sys_t
78 {
79     vout_thread_t *p_vout;
80
81     unsigned int i_x, i_y;
82     unsigned int i_width, i_height, i_aspect;
83
84     vlc_bool_t b_autocrop;
85
86     /* Autocrop specific variables */
87     unsigned int i_lastchange;
88     vlc_bool_t   b_changed;
89 };
90
91 /*****************************************************************************
92  * Local prototypes
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 * );
101
102 static void UpdateStats    ( vout_thread_t *, picture_t * );
103
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 )
109 {
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;
117 }
118
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 )
125 {
126     /* Allocate structure */
127     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
128     if( p_vout->p_sys == NULL )
129     {
130         msg_Err( p_vout, "out of memory" );
131         return 1;
132     }
133
134     return 0;
135 }
136
137 /*****************************************************************************
138  * vout_Init: initialize Crop video thread output method
139  *****************************************************************************/
140 static int vout_Init( vout_thread_t *p_vout )
141 {
142     int   i_index;
143     char *psz_var;
144     picture_t *p_pic;
145     
146     I_OUTPUTPICTURES = 0;
147
148     p_vout->p_sys->i_lastchange = 0;
149     p_vout->p_sys->b_changed = 0;
150
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;
156
157     /* Shall we use autocrop ? */
158     p_vout->p_sys->b_autocrop = config_GetInt( p_vout, "autocrop" );
159
160     /* Get geometry value from the user */
161     psz_var = config_GetPsz( p_vout, "crop-geometry" );
162     if( psz_var )
163     {
164         char *psz_parser, *psz_tmp;
165
166         psz_parser = psz_tmp = psz_var;
167         while( *psz_tmp && *psz_tmp != 'x' ) psz_tmp++;
168
169         if( *psz_tmp )
170         {
171             psz_tmp[0] = '\0';
172             p_vout->p_sys->i_width = atoi( psz_parser );
173
174             psz_parser = ++psz_tmp;
175             while( *psz_tmp && *psz_tmp != '+' ) psz_tmp++;
176
177             if( *psz_tmp )
178             {
179                 psz_tmp[0] = '\0';
180                 p_vout->p_sys->i_height = atoi( psz_parser );
181
182                 psz_parser = ++psz_tmp;
183                 while( *psz_tmp && *psz_tmp != '+' ) psz_tmp++;
184
185                 if( *psz_tmp )
186                 {
187                     psz_tmp[0] = '\0';
188                     p_vout->p_sys->i_x = atoi( psz_parser );
189                     p_vout->p_sys->i_y = atoi( ++psz_tmp );
190                 }
191                 else
192                 {
193                     p_vout->p_sys->i_x = atoi( psz_parser );
194                     p_vout->p_sys->i_y =
195                      ( p_vout->output.i_height - p_vout->p_sys->i_height ) / 2;
196                 }
197             }
198             else
199             {
200                 p_vout->p_sys->i_height = atoi( psz_parser );
201                 p_vout->p_sys->i_x =
202                      ( p_vout->output.i_width - p_vout->p_sys->i_width ) / 2;
203                 p_vout->p_sys->i_y =
204                      ( p_vout->output.i_height - p_vout->p_sys->i_height ) / 2;
205             }
206         }
207         else
208         {
209             p_vout->p_sys->i_width = atoi( psz_parser );
210             p_vout->p_sys->i_height = p_vout->output.i_height;
211             p_vout->p_sys->i_x =
212                      ( p_vout->output.i_width - p_vout->p_sys->i_width ) / 2;
213             p_vout->p_sys->i_y =
214                      ( p_vout->output.i_height - p_vout->p_sys->i_height ) / 2;
215         }
216
217         /* Check for validity */
218         if( p_vout->p_sys->i_x + p_vout->p_sys->i_width
219                                                    > p_vout->output.i_width )
220         {
221             p_vout->p_sys->i_x = 0;
222             if( p_vout->p_sys->i_width > p_vout->output.i_width )
223             {
224                 p_vout->p_sys->i_width = p_vout->output.i_width;
225             }
226         }
227
228         if( p_vout->p_sys->i_y + p_vout->p_sys->i_height
229                                                    > p_vout->output.i_height )
230         {
231             p_vout->p_sys->i_y = 0;
232             if( p_vout->p_sys->i_height > p_vout->output.i_height )
233             {
234                 p_vout->p_sys->i_height = p_vout->output.i_height;
235             }
236         }
237
238         free( psz_var );
239     }
240     else
241     {
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;
245     }
246
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 " );
252
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;
257
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 )
264     {
265         msg_Err( p_vout, "failed to create vout" );
266         return 0;
267     }
268
269     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
270
271     return 0;
272 }
273
274 /*****************************************************************************
275  * vout_End: terminate Crop video thread output method
276  *****************************************************************************/
277 static void vout_End( vout_thread_t *p_vout )
278 {
279     int i_index;
280
281     /* Free the fake output buffers we allocated */
282     for( i_index = I_OUTPUTPICTURES ; i_index ; )
283     {
284         i_index--;
285         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
286     }
287 }
288
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 )
295 {
296     vout_DestroyThread( p_vout->p_sys->p_vout );
297     free( p_vout->p_sys );
298 }
299
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 )
307 {
308     if( !p_vout->p_sys->b_changed )
309     {
310         return 0;
311     }
312
313     vout_DestroyThread( p_vout->p_sys->p_vout );
314
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 )
320     {
321         msg_Err( p_vout, "failed to create vout" );
322         return 1;
323     }
324
325     p_vout->p_sys->b_changed = 0;
326     p_vout->p_sys->i_lastchange = 0;
327
328     return 0;
329 }
330
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
336  * frame.
337  *****************************************************************************/
338 static void vout_Render( vout_thread_t *p_vout, picture_t *p_pic )
339 {
340     picture_t *p_outpic = NULL;
341     int i_plane;
342
343     if( p_vout->p_sys->b_changed )
344     {
345         return;
346     }
347
348     while( ( p_outpic =
349                  vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 )
350            ) == NULL )
351     {
352         if( p_vout->b_die || p_vout->b_error )
353         {
354             vout_DestroyPicture( p_vout->p_sys->p_vout, p_outpic );
355             return;
356         }
357
358         msleep( VOUT_OUTMEM_SLEEP );
359     }
360
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 );
363
364     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
365     {
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;
369
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;
376
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;
379
380         while( p_out < p_out_end )
381         {
382             p_vout->p_vlc->pf_memcpy( p_out, p_in, i_out_pitch );
383             p_in += i_in_pitch;
384             p_out += i_out_pitch;
385         }
386     }
387
388     vout_UnlinkPicture( p_vout->p_sys->p_vout, p_outpic );
389     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
390
391     /* The source image may still be in the cache ... parse it! */
392     if( !p_vout->p_sys->b_autocrop )
393     {
394         return;
395     }
396
397     UpdateStats( p_vout, p_pic );
398 }
399
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
405  * frame.
406  *****************************************************************************/
407 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
408 {
409     ;
410 }
411
412 static void UpdateStats( vout_thread_t *p_vout, picture_t *p_pic )
413 {
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;
418
419     /* Determine where black borders are */
420     switch( p_vout->output.i_chroma )
421     {
422     case VLC_FOURCC('I','4','2','0'):
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-- ; )
426         {
427             const int i_col = i * i_pitch / i_lines;
428
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 )
432             {
433                 if( i_lastwhite == -1 )
434                 {
435                     i_lastwhite = i;
436                 }
437                 i_firstwhite = i;
438             }
439             p_in += i_pitch;
440         }
441         break;
442
443     default:
444         break;
445     }
446
447     /* Decide whether it's worth changing the size */
448     if( i_lastwhite == -1 )
449     {
450         p_vout->p_sys->i_lastchange = 0;
451         return;
452     }
453
454     if( i_lastwhite - i_firstwhite < p_vout->p_sys->i_height / 2 )
455     {
456         p_vout->p_sys->i_lastchange = 0;
457         return;
458     }
459
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 )
462     {
463         p_vout->p_sys->i_lastchange = 0;
464         return;
465     }
466
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 )
470     {
471         return;
472     }
473
474     /* Tune a few values */
475     if( i_firstwhite & 1 )
476     {
477         i_firstwhite--;
478     }
479
480     if( !(i_lastwhite & 1) )
481     {
482         i_lastwhite++;
483     }
484
485     /* Change size */
486     p_vout->p_sys->i_y = i_firstwhite;
487     p_vout->p_sys->i_height = i_lastwhite - i_firstwhite + 1;
488
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;
492
493     p_vout->p_sys->b_changed = 1;
494 }