Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

crop.c

00001 /*****************************************************************************
00002  * crop.c : Crop video plugin for vlc
00003  *****************************************************************************
00004  * Copyright (C) 2002, 2003 the VideoLAN team
00005  * $Id: crop.c 12982 2005-10-27 09:29:36Z md $
00006  *
00007  * Authors: Samuel Hocevar <[email protected]>
00008  *
00009  * This program is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00022  *****************************************************************************/
00023 
00024 /*****************************************************************************
00025  * Preamble
00026  *****************************************************************************/
00027 #include <stdlib.h>                                      /* malloc(), free() */
00028 #include <string.h>
00029 
00030 #include <vlc/vlc.h>
00031 #include <vlc/vout.h>
00032 
00033 #include "filter_common.h"
00034 
00035 /*****************************************************************************
00036  * Local prototypes
00037  *****************************************************************************/
00038 static int  Create    ( vlc_object_t * );
00039 static void Destroy   ( vlc_object_t * );
00040 
00041 static int  Init      ( vout_thread_t * );
00042 static void End       ( vout_thread_t * );
00043 static int  Manage    ( vout_thread_t * );
00044 static void Render    ( vout_thread_t *, picture_t * );
00045 
00046 static void UpdateStats    ( vout_thread_t *, picture_t * );
00047 
00048 static int  SendEvents( vlc_object_t *, char const *,
00049                         vlc_value_t, vlc_value_t, void * );
00050 
00051 /*****************************************************************************
00052  * Module descriptor
00053  *****************************************************************************/
00054 #define GEOMETRY_TEXT N_("Crop geometry (pixels)")
00055 #define GEOMETRY_LONGTEXT N_("Set the geometry of the zone to crop. This is set as <width> x <height> + <left offset> + <top offset>.")
00056 
00057 #define AUTOCROP_TEXT N_("Automatic cropping")
00058 #define AUTOCROP_LONGTEXT N_("Activate automatic black border cropping.")
00059 
00060 vlc_module_begin();
00061     set_description( _("Crop video filter") );
00062     set_shortname( N_("Crop" ));
00063     set_category( CAT_VIDEO );
00064     set_subcategory( SUBCAT_VIDEO_VFILTER );
00065     set_capability( "video filter", 0 );
00066 
00067     add_string( "crop-geometry", NULL, NULL, GEOMETRY_TEXT, GEOMETRY_LONGTEXT, VLC_FALSE );
00068     add_bool( "autocrop", 0, NULL, AUTOCROP_TEXT, AUTOCROP_LONGTEXT, VLC_FALSE );
00069 
00070     add_shortcut( "crop" );
00071     set_callbacks( Create, Destroy );
00072 vlc_module_end();
00073 
00074 /*****************************************************************************
00075  * vout_sys_t: Crop video output method descriptor
00076  *****************************************************************************
00077  * This structure is part of the video output thread descriptor.
00078  * It describes the Crop specific properties of an output thread.
00079  *****************************************************************************/
00080 struct vout_sys_t
00081 {
00082     vout_thread_t *p_vout;
00083 
00084     unsigned int i_x, i_y;
00085     unsigned int i_width, i_height, i_aspect;
00086 
00087     vlc_bool_t b_autocrop;
00088 
00089     /* Autocrop specific variables */
00090     unsigned int i_lastchange;
00091     vlc_bool_t   b_changed;
00092 };
00093 
00094 /*****************************************************************************
00095  * Control: control facility for the vout (forwards to child vout)
00096  *****************************************************************************/
00097 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
00098 {
00099     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
00100 }
00101 
00102 /*****************************************************************************
00103  * Create: allocates Crop video thread output method
00104  *****************************************************************************
00105  * This function allocates and initializes a Crop vout method.
00106  *****************************************************************************/
00107 static int Create( vlc_object_t *p_this )
00108 {
00109     vout_thread_t *p_vout = (vout_thread_t *)p_this;
00110 
00111     /* Allocate structure */
00112     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
00113     if( p_vout->p_sys == NULL )
00114     {
00115         msg_Err( p_vout, "out of memory" );
00116         return VLC_ENOMEM;
00117     }
00118 
00119     p_vout->pf_init = Init;
00120     p_vout->pf_end = End;
00121     p_vout->pf_manage = Manage;
00122     p_vout->pf_render = Render;
00123     p_vout->pf_display = NULL;
00124     p_vout->pf_control = Control;
00125 
00126     return VLC_SUCCESS;
00127 }
00128 
00129 /*****************************************************************************
00130  * Init: initialize Crop video thread output method
00131  *****************************************************************************/
00132 static int Init( vout_thread_t *p_vout )
00133 {
00134     int   i_index;
00135     char *psz_var;
00136     picture_t *p_pic;
00137     video_format_t fmt = {0};
00138 
00139     I_OUTPUTPICTURES = 0;
00140 
00141     p_vout->p_sys->i_lastchange = 0;
00142     p_vout->p_sys->b_changed = VLC_FALSE;
00143 
00144     /* Initialize the output structure */
00145     p_vout->output.i_chroma = p_vout->render.i_chroma;
00146     p_vout->output.i_width  = p_vout->render.i_width;
00147     p_vout->output.i_height = p_vout->render.i_height;
00148     p_vout->output.i_aspect = p_vout->render.i_aspect;
00149     p_vout->fmt_out = p_vout->fmt_in;
00150 
00151     /* Shall we use autocrop ? */
00152     p_vout->p_sys->b_autocrop = config_GetInt( p_vout, "autocrop" );
00153 
00154     /* Get geometry value from the user */
00155     psz_var = config_GetPsz( p_vout, "crop-geometry" );
00156     if( psz_var )
00157     {
00158         char *psz_parser, *psz_tmp;
00159 
00160         psz_parser = psz_tmp = psz_var;
00161         while( *psz_tmp && *psz_tmp != 'x' ) psz_tmp++;
00162 
00163         if( *psz_tmp )
00164         {
00165             psz_tmp[0] = '\0';
00166             p_vout->p_sys->i_width = atoi( psz_parser );
00167 
00168             psz_parser = ++psz_tmp;
00169             while( *psz_tmp && *psz_tmp != '+' ) psz_tmp++;
00170 
00171             if( *psz_tmp )
00172             {
00173                 psz_tmp[0] = '\0';
00174                 p_vout->p_sys->i_height = atoi( psz_parser );
00175 
00176                 psz_parser = ++psz_tmp;
00177                 while( *psz_tmp && *psz_tmp != '+' ) psz_tmp++;
00178 
00179                 if( *psz_tmp )
00180                 {
00181                     psz_tmp[0] = '\0';
00182                     p_vout->p_sys->i_x = atoi( psz_parser );
00183                     p_vout->p_sys->i_y = atoi( ++psz_tmp );
00184                 }
00185                 else
00186                 {
00187                     p_vout->p_sys->i_x = atoi( psz_parser );
00188                     p_vout->p_sys->i_y =
00189                      ( p_vout->output.i_height - p_vout->p_sys->i_height ) / 2;
00190                 }
00191             }
00192             else
00193             {
00194                 p_vout->p_sys->i_height = atoi( psz_parser );
00195                 p_vout->p_sys->i_x =
00196                      ( p_vout->output.i_width - p_vout->p_sys->i_width ) / 2;
00197                 p_vout->p_sys->i_y =
00198                      ( p_vout->output.i_height - p_vout->p_sys->i_height ) / 2;
00199             }
00200         }
00201         else
00202         {
00203             p_vout->p_sys->i_width = atoi( psz_parser );
00204             p_vout->p_sys->i_height = p_vout->output.i_height;
00205             p_vout->p_sys->i_x =
00206                      ( p_vout->output.i_width - p_vout->p_sys->i_width ) / 2;
00207             p_vout->p_sys->i_y =
00208                      ( p_vout->output.i_height - p_vout->p_sys->i_height ) / 2;
00209         }
00210 
00211         /* Check for validity */
00212         if( p_vout->p_sys->i_x + p_vout->p_sys->i_width
00213                                                    > p_vout->output.i_width )
00214         {
00215             p_vout->p_sys->i_x = 0;
00216             if( p_vout->p_sys->i_width > p_vout->output.i_width )
00217             {
00218                 p_vout->p_sys->i_width = p_vout->output.i_width;
00219             }
00220         }
00221 
00222         if( p_vout->p_sys->i_y + p_vout->p_sys->i_height
00223                                                    > p_vout->output.i_height )
00224         {
00225             p_vout->p_sys->i_y = 0;
00226             if( p_vout->p_sys->i_height > p_vout->output.i_height )
00227             {
00228                 p_vout->p_sys->i_height = p_vout->output.i_height;
00229             }
00230         }
00231 
00232         free( psz_var );
00233     }
00234     else
00235     {
00236         p_vout->p_sys->i_width  = p_vout->fmt_out.i_visible_width;
00237         p_vout->p_sys->i_height = p_vout->fmt_out.i_visible_height;
00238         p_vout->p_sys->i_x = p_vout->fmt_out.i_x_offset;
00239         p_vout->p_sys->i_y = p_vout->fmt_out.i_y_offset;
00240     }
00241 
00242     /* Pheeew. Parsing done. */
00243     msg_Dbg( p_vout, "cropping at %ix%i+%i+%i, %sautocropping",
00244                      p_vout->p_sys->i_width, p_vout->p_sys->i_height,
00245                      p_vout->p_sys->i_x, p_vout->p_sys->i_y,
00246                      p_vout->p_sys->b_autocrop ? "" : "not " );
00247 
00248     /* Set current output image properties */
00249     p_vout->p_sys->i_aspect = p_vout->fmt_out.i_aspect
00250            * p_vout->fmt_out.i_visible_height / p_vout->p_sys->i_height
00251            * p_vout->p_sys->i_width / p_vout->fmt_out.i_visible_width;
00252 
00253     fmt.i_width = fmt.i_visible_width = p_vout->p_sys->i_width;
00254     fmt.i_height = fmt.i_visible_height = p_vout->p_sys->i_height;
00255     fmt.i_x_offset = fmt.i_y_offset = 0;
00256     fmt.i_chroma = p_vout->render.i_chroma;
00257     fmt.i_aspect = p_vout->p_sys->i_aspect;
00258     fmt.i_sar_num = p_vout->p_sys->i_aspect * fmt.i_height / fmt.i_width;
00259     fmt.i_sar_den = VOUT_ASPECT_FACTOR;
00260 
00261     /* Try to open the real video output */
00262     p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
00263     if( p_vout->p_sys->p_vout == NULL )
00264     {
00265         msg_Err( p_vout, "failed to create vout" );
00266         return VLC_EGENERIC;
00267     }
00268 
00269     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
00270 
00271     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
00272 
00273     ADD_PARENT_CALLBACKS( SendEventsToChild );
00274 
00275     return VLC_SUCCESS;
00276 }
00277 
00278 /*****************************************************************************
00279  * End: terminate Crop video thread output method
00280  *****************************************************************************/
00281 static void End( vout_thread_t *p_vout )
00282 {
00283     int i_index;
00284 
00285     /* Free the fake output buffers we allocated */
00286     for( i_index = I_OUTPUTPICTURES ; i_index ; )
00287     {
00288         i_index--;
00289         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
00290     }
00291 }
00292 
00293 /*****************************************************************************
00294  * Destroy: destroy Crop video thread output method
00295  *****************************************************************************
00296  * Terminate an output method created by CropCreateOutputMethod
00297  *****************************************************************************/
00298 static void Destroy( vlc_object_t *p_this )
00299 {
00300     vout_thread_t *p_vout = (vout_thread_t *)p_this;
00301 
00302     if( p_vout->p_sys->p_vout )
00303     {
00304         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
00305         vlc_object_detach( p_vout->p_sys->p_vout );
00306         vout_Destroy( p_vout->p_sys->p_vout );
00307     }
00308 
00309     DEL_PARENT_CALLBACKS( SendEventsToChild );
00310 
00311     free( p_vout->p_sys );
00312 }
00313 
00314 /*****************************************************************************
00315  * Manage: handle Crop events
00316  *****************************************************************************
00317  * This function should be called regularly by video output thread. It manages
00318  * console events. It returns a non null value on error.
00319  *****************************************************************************/
00320 static int Manage( vout_thread_t *p_vout )
00321 {
00322     video_format_t fmt = {0};
00323 
00324     if( !p_vout->p_sys->b_changed )
00325     {
00326         return VLC_SUCCESS;
00327     }
00328 
00329     vout_Destroy( p_vout->p_sys->p_vout );
00330 
00331     fmt.i_width = fmt.i_visible_width = p_vout->p_sys->i_width;
00332     fmt.i_height = fmt.i_visible_height = p_vout->p_sys->i_height;
00333     fmt.i_x_offset = fmt.i_y_offset = 0;
00334     fmt.i_chroma = p_vout->render.i_chroma;
00335     fmt.i_aspect = p_vout->p_sys->i_aspect;
00336     fmt.i_sar_num = p_vout->p_sys->i_aspect * fmt.i_height / fmt.i_width;
00337     fmt.i_sar_den = VOUT_ASPECT_FACTOR;
00338 
00339     p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
00340     if( p_vout->p_sys->p_vout == NULL )
00341     {
00342         msg_Err( p_vout, "failed to create vout" );
00343         return VLC_EGENERIC;
00344     }
00345 
00346     p_vout->p_sys->b_changed = VLC_FALSE;
00347     p_vout->p_sys->i_lastchange = 0;
00348 
00349     return VLC_SUCCESS;
00350 }
00351 
00352 /*****************************************************************************
00353  * Render: display previously rendered output
00354  *****************************************************************************
00355  * This function sends the currently rendered image to Crop image, waits
00356  * until it is displayed and switches the two rendering buffers, preparing next
00357  * frame.
00358  *****************************************************************************/
00359 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
00360 {
00361     picture_t *p_outpic = NULL;
00362     int i_plane;
00363 
00364     if( p_vout->p_sys->b_changed )
00365     {
00366         return;
00367     }
00368 
00369     while( ( p_outpic =
00370                  vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 )
00371            ) == NULL )
00372     {
00373         if( p_vout->b_die || p_vout->b_error )
00374         {
00375             vout_DestroyPicture( p_vout->p_sys->p_vout, p_outpic );
00376             return;
00377         }
00378 
00379         msleep( VOUT_OUTMEM_SLEEP );
00380     }
00381 
00382     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
00383     vout_LinkPicture( p_vout->p_sys->p_vout, p_outpic );
00384 
00385     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
00386     {
00387         uint8_t *p_in, *p_out, *p_out_end;
00388         int i_in_pitch = p_pic->p[i_plane].i_pitch;
00389         const int i_out_pitch = p_outpic->p[i_plane].i_pitch;
00390         const int i_copy_pitch = p_outpic->p[i_plane].i_visible_pitch;
00391 
00392         p_in = p_pic->p[i_plane].p_pixels
00393                 /* Skip the right amount of lines */
00394                 + i_in_pitch * ( p_pic->p[i_plane].i_visible_lines *
00395                                  p_vout->p_sys->i_y / p_vout->output.i_height )
00396                 /* Skip the right amount of columns */
00397                 + i_in_pitch * p_vout->p_sys->i_x / p_vout->output.i_width;
00398 
00399         p_out = p_outpic->p[i_plane].p_pixels;
00400         p_out_end = p_out + i_out_pitch * p_outpic->p[i_plane].i_visible_lines;
00401 
00402         while( p_out < p_out_end )
00403         {
00404             p_vout->p_vlc->pf_memcpy( p_out, p_in, i_copy_pitch );
00405             p_in += i_in_pitch;
00406             p_out += i_out_pitch;
00407         }
00408     }
00409 
00410     vout_UnlinkPicture( p_vout->p_sys->p_vout, p_outpic );
00411     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
00412 
00413     /* The source image may still be in the cache ... parse it! */
00414     if( p_vout->p_sys->b_autocrop )
00415     {
00416         UpdateStats( p_vout, p_pic );
00417     }
00418 }
00419 
00420 static void UpdateStats( vout_thread_t *p_vout, picture_t *p_pic )
00421 {
00422     uint8_t *p_in = p_pic->p[0].p_pixels;
00423     int i_pitch = p_pic->p[0].i_pitch;
00424     int i_visible_pitch = p_pic->p[0].i_visible_pitch;
00425     int i_lines = p_pic->p[0].i_visible_lines;
00426     int i_firstwhite = -1, i_lastwhite = -1, i;
00427 
00428     /* Determine where black borders are */
00429     switch( p_vout->output.i_chroma )
00430     {
00431     case VLC_FOURCC('I','4','2','0'):
00432         /* XXX: Do not laugh ! I know this is very naive. But it's just a
00433          *      proof of concept code snippet... */
00434         for( i = i_lines ; i-- ; )
00435         {
00436             const int i_col = i * i_pitch / i_lines;
00437 
00438             if( p_in[i_col/2] > 40
00439                  && p_in[i_visible_pitch/2] > 40
00440                  && p_in[i_visible_pitch/2 + i_col/2] > 40 )
00441             {
00442                 if( i_lastwhite == -1 )
00443                 {
00444                     i_lastwhite = i;
00445                 }
00446                 i_firstwhite = i;
00447             }
00448             p_in += i_pitch;
00449         }
00450         break;
00451 
00452     default:
00453         break;
00454     }
00455 
00456     /* Decide whether it's worth changing the size */
00457     if( i_lastwhite == -1 )
00458     {
00459         p_vout->p_sys->i_lastchange = 0;
00460         return;
00461     }
00462 
00463     if( (unsigned int)(i_lastwhite - i_firstwhite)
00464                                            < p_vout->p_sys->i_height / 2 )
00465     {
00466         p_vout->p_sys->i_lastchange = 0;
00467         return;
00468     }
00469 
00470     if( (unsigned int)(i_lastwhite - i_firstwhite)
00471                                           < p_vout->p_sys->i_height + 16
00472          && (unsigned int)(i_lastwhite - i_firstwhite + 16)
00473                                                 > p_vout->p_sys->i_height )
00474     {
00475         p_vout->p_sys->i_lastchange = 0;
00476         return;
00477     }
00478 
00479     /* We need at least 25 images to make up our mind */
00480     p_vout->p_sys->i_lastchange++;
00481     if( p_vout->p_sys->i_lastchange < 25 )
00482     {
00483         return;
00484     }
00485 
00486     /* Tune a few values */
00487     if( i_firstwhite & 1 )
00488     {
00489         i_firstwhite--;
00490     }
00491 
00492     if( !(i_lastwhite & 1) )
00493     {
00494         i_lastwhite++;
00495     }
00496 
00497     /* Change size */
00498     p_vout->p_sys->i_y = i_firstwhite;
00499     p_vout->p_sys->i_height = i_lastwhite - i_firstwhite + 1;
00500 
00501     p_vout->p_sys->i_aspect = p_vout->output.i_aspect
00502                             * p_vout->output.i_height / p_vout->p_sys->i_height
00503                             * p_vout->p_sys->i_width / p_vout->output.i_width;
00504 
00505     p_vout->p_sys->b_changed = VLC_TRUE;
00506 }
00507 
00508 /*****************************************************************************
00509  * SendEvents: forward mouse and keyboard events to the parent p_vout
00510  *****************************************************************************/
00511 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
00512                        vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
00513 {
00514     vout_thread_t *p_vout = (vout_thread_t *)_p_vout;
00515     vlc_value_t sentval = newval;
00516 
00517     /* Translate the mouse coordinates */
00518     if( !strcmp( psz_var, "mouse-x" ) )
00519     {
00520         sentval.i_int += p_vout->p_sys->i_x;
00521     }
00522     else if( !strcmp( psz_var, "mouse-y" ) )
00523     {
00524         sentval.i_int += p_vout->p_sys->i_y;
00525     }
00526 
00527     var_Set( p_vout, psz_var, sentval );
00528 
00529     return VLC_SUCCESS;
00530 }
00531 
00532 /*****************************************************************************
00533  * SendEventsToChild: forward events to the child/children vout
00534  *****************************************************************************/
00535 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
00536                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
00537 {
00538     vout_thread_t *p_vout = (vout_thread_t *)p_this;
00539     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
00540     return VLC_SUCCESS;
00541 }

Generated on Tue Dec 20 10:14:51 2005 for vlc-0.8.4a by  doxygen 1.4.2