crop.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:32k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * crop.c : Crop video plugin for vlc
  3.  *****************************************************************************
  4.  * Copyright (C) 2002, 2003 the VideoLAN team
  5.  * $Id: d8a258a42a2bb0be765adb0b742acda7fa85b793 $
  6.  *
  7.  * Authors: Samuel Hocevar <sam@zoy.org>
  8.  *          mod by Cedric Cocquebert <Cedric.Cocquebert@supelec.fr>
  9.  *          based of DScaler idea (M. Samblanet)
  10.  *
  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.
  15.  *
  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.
  20.  *
  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.  *****************************************************************************/
  25. /*****************************************************************************
  26.  * Preamble
  27.  *****************************************************************************/
  28. #ifdef HAVE_CONFIG_H
  29. # include "config.h"
  30. #endif
  31. #include <vlc_common.h>
  32. #include <vlc_plugin.h>
  33. #include <vlc_vout.h>
  34. #include <vlc_dialog.h>
  35. #include "filter_common.h"
  36. #define BEST_AUTOCROP 1
  37. #ifdef BEST_AUTOCROP
  38.     #define RATIO_MAX 15000  // 10*4/3 for a 360
  39. #endif
  40. /*****************************************************************************
  41.  * Local prototypes
  42.  *****************************************************************************/
  43. static int  Create    ( vlc_object_t * );
  44. static void Destroy   ( vlc_object_t * );
  45. static int  Init      ( vout_thread_t * );
  46. static void End       ( vout_thread_t * );
  47. static int  Manage    ( vout_thread_t * );
  48. static void Render    ( vout_thread_t *, picture_t * );
  49. static void UpdateStats    ( vout_thread_t *, picture_t * );
  50. static int  MouseEvent( vlc_object_t *, char const *,
  51.                         vlc_value_t, vlc_value_t, void * );
  52. #ifdef BEST_AUTOCROP
  53. /*****************************************************************************
  54.  * Callback prototypes
  55.  *****************************************************************************/
  56. static int FilterCallback ( vlc_object_t *, char const *,
  57.                             vlc_value_t, vlc_value_t, void * );
  58. #endif
  59. /*****************************************************************************
  60.  * Module descriptor
  61.  *****************************************************************************/
  62. #define GEOMETRY_TEXT N_("Crop geometry (pixels)")
  63. #define GEOMETRY_LONGTEXT N_("Set the geometry of the zone to crop. This is set as <width> x <height> + <left offset> + <top offset>.")
  64. #define AUTOCROP_TEXT N_("Automatic cropping")
  65. #define AUTOCROP_LONGTEXT N_("Automatically detect black borders and crop them.")
  66. #ifdef BEST_AUTOCROP
  67. #define RATIOMAX_TEXT N_("Ratio max (x 1000)")
  68. #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.")
  69. #define RATIO_TEXT N_("Manual ratio")
  70. #define RATIO_LONGTEXT N_("Force a ratio (0 for automatic). Value is x1000: 1333 means 4/3.")
  71. #define TIME_TEXT N_("Number of images for change")
  72. #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.")
  73. #define DIFF_TEXT N_("Number of lines for change")
  74. #define DIFF_LONGTEXT N_("The minimum difference in the number of detected black lines to consider that ratio changed and trigger recrop.")
  75. #define NBP_TEXT N_("Number of non black pixels ")
  76. #define NBP_LONGTEXT N_("The maximum of non-black pixels in a line to consider"
  77.                         " that the line is black.")
  78. #define SKIP_TEXT N_("Skip percentage (%)")
  79. #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.")
  80. #define LUM_TEXT N_("Luminance threshold ")
  81. #define LUM_LONGTEXT N_("Maximum luminance to consider a pixel as black (0-255).")
  82. #endif
  83. vlc_module_begin ()
  84.     set_description( N_("Crop video filter") )
  85.     set_shortname( N_("Crop" ))
  86.     set_category( CAT_VIDEO )
  87.     set_subcategory( SUBCAT_VIDEO_VFILTER )
  88.     set_capability( "video filter", 0 )
  89.     add_string( "crop-geometry", NULL, NULL, GEOMETRY_TEXT,
  90.                                              GEOMETRY_LONGTEXT, false )
  91.     add_bool( "autocrop", 0, NULL, AUTOCROP_TEXT,
  92.                                    AUTOCROP_LONGTEXT, false )
  93. #ifdef BEST_AUTOCROP
  94.     add_integer_with_range( "autocrop-ratio-max", 2405, 0, RATIO_MAX, NULL,
  95.                             RATIOMAX_TEXT, RATIOMAX_LONGTEXT, true )
  96.     add_integer_with_range( "crop-ratio", 0, 0, RATIO_MAX, NULL, RATIO_TEXT,
  97.                             RATIO_LONGTEXT, false )
  98.     add_integer( "autocrop-time", 25, NULL, TIME_TEXT,
  99.                  TIME_LONGTEXT, true )
  100.     add_integer( "autocrop-diff", 16, NULL, DIFF_TEXT,
  101.                                             DIFF_LONGTEXT, true )
  102.     add_integer( "autocrop-non-black-pixels", 3, NULL,
  103.                  NBP_TEXT, NBP_LONGTEXT, true )
  104.     add_integer_with_range( "autocrop-skip-percent", 17, 0, 100, NULL,
  105.                             SKIP_TEXT, SKIP_LONGTEXT, true )
  106.     add_integer_with_range( "autocrop-luminance-threshold", 40, 0, 128, NULL,
  107.                             LUM_TEXT, LUM_LONGTEXT, true )
  108. #endif //BEST_AUTOCROP
  109.     add_shortcut( "crop" )
  110.     set_callbacks( Create, Destroy )
  111. vlc_module_end ()
  112. /*****************************************************************************
  113.  * vout_sys_t: Crop video output method descriptor
  114.  *****************************************************************************
  115.  * This structure is part of the video output thread descriptor.
  116.  * It describes the Crop specific properties of an output thread.
  117.  *****************************************************************************/
  118. struct vout_sys_t
  119. {
  120.     vlc_mutex_t lock;
  121.     vout_thread_t *p_vout;
  122.     unsigned int i_x, i_y;
  123.     unsigned int i_width, i_height, i_aspect;
  124.     bool b_autocrop;
  125.     /* Autocrop specific variables */
  126.     unsigned int i_lastchange;
  127.     bool   b_changed;
  128. #ifdef BEST_AUTOCROP
  129.     unsigned int i_ratio_max;
  130.     unsigned int i_threshold, i_skipPercent, i_nonBlackPixel, i_diff, i_time;
  131.     unsigned int i_ratio;
  132. #endif
  133. };
  134. /*****************************************************************************
  135.  * Control: control facility for the vout (forwards to child vout)
  136.  *****************************************************************************/
  137. static int Control( vout_thread_t *p_vout, int i_query, va_list args )
  138. {
  139.     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
  140. }
  141. /*****************************************************************************
  142.  * Create: allocates Crop video thread output method
  143.  *****************************************************************************
  144.  * This function allocates and initializes a Crop vout method.
  145.  *****************************************************************************/
  146. static int Create( vlc_object_t *p_this )
  147. {
  148.     vout_thread_t *p_vout = (vout_thread_t *)p_this;
  149.     /* Allocate structure */
  150.     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
  151.     if( p_vout->p_sys == NULL )
  152.         return VLC_ENOMEM;
  153.     p_vout->pf_init = Init;
  154.     p_vout->pf_end = End;
  155.     p_vout->pf_manage = Manage;
  156.     p_vout->pf_render = Render;
  157.     p_vout->pf_display = NULL;
  158.     p_vout->pf_control = Control;
  159.     return VLC_SUCCESS;
  160. }
  161. /*****************************************************************************
  162.  * Init: initialize Crop video thread output method
  163.  *****************************************************************************/
  164. static int Init( vout_thread_t *p_vout )
  165. {
  166.     char *psz_var;
  167.     video_format_t fmt;
  168.     I_OUTPUTPICTURES = 0;
  169.     memset( &fmt, 0, sizeof(video_format_t) );
  170.     p_vout->p_sys->i_lastchange = 0;
  171.     p_vout->p_sys->b_changed = false;
  172.     /* Initialize the output structure */
  173.     p_vout->output.i_chroma = p_vout->render.i_chroma;
  174.     p_vout->output.i_width  = p_vout->render.i_width;
  175.     p_vout->output.i_height = p_vout->render.i_height;
  176.     p_vout->output.i_aspect = p_vout->render.i_aspect;
  177.     p_vout->fmt_out = p_vout->fmt_in;
  178.     /* Shall we use autocrop ? */
  179.     p_vout->p_sys->b_autocrop = config_GetInt( p_vout, "autocrop" );
  180. #ifdef BEST_AUTOCROP
  181.     p_vout->p_sys->i_ratio_max = config_GetInt( p_vout, "autocrop-ratio-max" );
  182.     p_vout->p_sys->i_threshold =
  183.                     config_GetInt( p_vout, "autocrop-luminance-threshold" );
  184.     p_vout->p_sys->i_skipPercent =
  185.                     config_GetInt( p_vout, "autocrop-skip-percent" );
  186.     p_vout->p_sys->i_nonBlackPixel =
  187.                     config_GetInt( p_vout, "autocrop-non-black-pixels" );
  188.     p_vout->p_sys->i_diff = config_GetInt( p_vout, "autocrop-diff" );
  189.     p_vout->p_sys->i_time = config_GetInt( p_vout, "autocrop-time" );
  190.     var_SetString( p_vout, "ratio-crop", "0" );
  191.     if (p_vout->p_sys->b_autocrop)
  192.         p_vout->p_sys->i_ratio = 0;
  193.     else
  194.     {
  195.         p_vout->p_sys->i_ratio = config_GetInt( p_vout, "crop-ratio" );
  196.         // ratio < width / height => ratio = 0 (unchange ratio)
  197.         if (p_vout->p_sys->i_ratio < (p_vout->output.i_width * 1000) / p_vout->output.i_height)
  198.             p_vout->p_sys->i_ratio = 0;
  199.     }
  200. #endif
  201.     /* Get geometry value from the user */
  202.     psz_var = config_GetPsz( p_vout, "crop-geometry" );
  203.     if( psz_var )
  204.     {
  205.         char *psz_parser, *psz_tmp;
  206.         psz_parser = psz_tmp = psz_var;
  207.         while( *psz_tmp && *psz_tmp != 'x' ) psz_tmp++;
  208.         if( *psz_tmp )
  209.         {
  210.             psz_tmp[0] = '';
  211.             p_vout->p_sys->i_width = atoi( psz_parser );
  212.             psz_parser = ++psz_tmp;
  213.             while( *psz_tmp && *psz_tmp != '+' ) psz_tmp++;
  214.             if( *psz_tmp )
  215.             {
  216.                 psz_tmp[0] = '';
  217.                 p_vout->p_sys->i_height = atoi( psz_parser );
  218.                 psz_parser = ++psz_tmp;
  219.                 while( *psz_tmp && *psz_tmp != '+' ) psz_tmp++;
  220.                 if( *psz_tmp )
  221.                 {
  222.                     psz_tmp[0] = '';
  223.                     p_vout->p_sys->i_x = atoi( psz_parser );
  224.                     p_vout->p_sys->i_y = atoi( ++psz_tmp );
  225.                 }
  226.                 else
  227.                 {
  228.                     p_vout->p_sys->i_x = atoi( psz_parser );
  229.                     p_vout->p_sys->i_y =
  230.                      ( p_vout->output.i_height - p_vout->p_sys->i_height ) / 2;
  231.                 }
  232.             }
  233.             else
  234.             {
  235.                 p_vout->p_sys->i_height = atoi( psz_parser );
  236.                 p_vout->p_sys->i_x =
  237.                      ( p_vout->output.i_width - p_vout->p_sys->i_width ) / 2;
  238.                 p_vout->p_sys->i_y =
  239.                      ( p_vout->output.i_height - p_vout->p_sys->i_height ) / 2;
  240.             }
  241.         }
  242.         else
  243.         {
  244.             p_vout->p_sys->i_width = atoi( psz_parser );
  245.             p_vout->p_sys->i_height = p_vout->output.i_height;
  246.             p_vout->p_sys->i_x =
  247.                      ( p_vout->output.i_width - p_vout->p_sys->i_width ) / 2;
  248.             p_vout->p_sys->i_y =
  249.                      ( p_vout->output.i_height - p_vout->p_sys->i_height ) / 2;
  250.         }
  251.         /* Check for validity */
  252.         if( p_vout->p_sys->i_x + p_vout->p_sys->i_width
  253.                                                    > p_vout->output.i_width )
  254.         {
  255.             p_vout->p_sys->i_x = 0;
  256.             if( p_vout->p_sys->i_width > p_vout->output.i_width )
  257.             {
  258.                 p_vout->p_sys->i_width = p_vout->output.i_width;
  259.             }
  260.         }
  261.         if( p_vout->p_sys->i_y + p_vout->p_sys->i_height
  262.                                                    > p_vout->output.i_height )
  263.         {
  264.             p_vout->p_sys->i_y = 0;
  265.             if( p_vout->p_sys->i_height > p_vout->output.i_height )
  266.             {
  267.                 p_vout->p_sys->i_height = p_vout->output.i_height;
  268.             }
  269.         }
  270.         free( psz_var );
  271.     }
  272.     else
  273. #ifdef BEST_AUTOCROP
  274.     if (p_vout->p_sys->i_ratio)
  275.     {
  276.         p_vout->p_sys->i_aspect    =  p_vout->p_sys->i_ratio * 432;
  277.         p_vout->p_sys->i_width  = p_vout->fmt_out.i_visible_width;
  278.         p_vout->p_sys->i_height = p_vout->output.i_aspect
  279.                                 * p_vout->output.i_height / p_vout->p_sys->i_aspect
  280.                                 * p_vout->p_sys->i_width / p_vout->output.i_width;
  281.         p_vout->p_sys->i_height += p_vout->p_sys->i_height % 2;
  282.         p_vout->p_sys->i_x = p_vout->fmt_out.i_x_offset;
  283.         p_vout->p_sys->i_y = (p_vout->output.i_height - p_vout->p_sys->i_height) / 2;
  284.     }
  285.     else
  286. #endif
  287.     {
  288.         p_vout->p_sys->i_width  = p_vout->fmt_out.i_visible_width;
  289.         p_vout->p_sys->i_height = p_vout->fmt_out.i_visible_height;
  290.         p_vout->p_sys->i_x = p_vout->fmt_out.i_x_offset;
  291.         p_vout->p_sys->i_y = p_vout->fmt_out.i_y_offset;
  292.     }
  293.     /* Pheeew. Parsing done. */
  294.     msg_Dbg( p_vout, "cropping at %ix%i+%i+%i, %sautocropping",
  295.                      p_vout->p_sys->i_width, p_vout->p_sys->i_height,
  296.                      p_vout->p_sys->i_x, p_vout->p_sys->i_y,
  297.                      p_vout->p_sys->b_autocrop ? "" : "not " );
  298.     /* Set current output image properties */
  299.     p_vout->p_sys->i_aspect = p_vout->fmt_out.i_aspect
  300.            * p_vout->fmt_out.i_visible_height / p_vout->p_sys->i_height
  301.            * p_vout->p_sys->i_width / p_vout->fmt_out.i_visible_width;
  302. #ifdef BEST_AUTOCROP
  303.     msg_Info( p_vout, "ratio %d",  p_vout->p_sys->i_aspect / 432);
  304. #endif
  305.     fmt.i_width = fmt.i_visible_width = p_vout->p_sys->i_width;
  306.     fmt.i_height = fmt.i_visible_height = p_vout->p_sys->i_height;
  307.     fmt.i_x_offset = fmt.i_y_offset = 0;
  308.     fmt.i_chroma = p_vout->render.i_chroma;
  309.     fmt.i_aspect = p_vout->p_sys->i_aspect;
  310.     fmt.i_sar_num = p_vout->p_sys->i_aspect * fmt.i_height / fmt.i_width;
  311.     fmt.i_sar_den = VOUT_ASPECT_FACTOR;
  312.     /* Try to open the real video output */
  313.     p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
  314.     if( p_vout->p_sys->p_vout == NULL )
  315.     {
  316.         msg_Err( p_vout, "failed to create vout" );
  317.         dialog_Fatal( p_vout, _("Cropping failed"), "%s",
  318.                         _("VLC could not open the video output module.") );
  319.         return VLC_EGENERIC;
  320.     }
  321.     vlc_mutex_init( &p_vout->p_sys->lock );
  322. #ifdef BEST_AUTOCROP
  323.     var_AddCallback( p_vout, "ratio-crop", FilterCallback, NULL );
  324. #endif
  325.     vout_filter_AllocateDirectBuffers( p_vout, VOUT_MAX_PICTURES );
  326.     vout_filter_AddChild( p_vout, p_vout->p_sys->p_vout, MouseEvent );
  327.     return VLC_SUCCESS;
  328. }
  329. /*****************************************************************************
  330.  * End: terminate Crop video thread output method
  331.  *****************************************************************************/
  332. static void End( vout_thread_t *p_vout )
  333. {
  334.     vout_sys_t *p_sys = p_vout->p_sys;
  335.     if( p_sys->p_vout )
  336.     {
  337.         vout_filter_DelChild( p_vout, p_sys->p_vout, MouseEvent );
  338.         vout_CloseAndRelease( p_sys->p_vout );
  339.     }
  340.     vout_filter_ReleaseDirectBuffers( p_vout );
  341.     var_DelCallback( p_vout, "ratio-crop", FilterCallback, NULL );
  342.     vlc_mutex_destroy( &p_sys->lock );
  343. }
  344. /*****************************************************************************
  345.  * Destroy: destroy Crop video thread output method
  346.  *****************************************************************************
  347.  * Terminate an output method created by CropCreateOutputMethod
  348.  *****************************************************************************/
  349. static void Destroy( vlc_object_t *p_this )
  350. {
  351.     vout_thread_t *p_vout = (vout_thread_t *)p_this;
  352.     free( p_vout->p_sys );
  353. }
  354. /*****************************************************************************
  355.  * Manage: handle Crop events
  356.  *****************************************************************************
  357.  * This function should be called regularly by video output thread. It manages
  358.  * console events. It returns a non null value on error.
  359.  *****************************************************************************/
  360. static int Manage( vout_thread_t *p_vout )
  361. {
  362.     video_format_t fmt;
  363.     if( !p_vout->p_sys->b_changed )
  364.     {
  365.         return VLC_SUCCESS;
  366.     }
  367.     memset( &fmt, 0, sizeof(video_format_t) );
  368. #ifdef BEST_AUTOCROP
  369.     /* XXX: not thread-safe with FilterCallback */
  370.     msg_Dbg( p_vout, "cropping at %ix%i+%i+%i, %sautocropping",
  371.                      p_vout->p_sys->i_width, p_vout->p_sys->i_height,
  372.                      p_vout->p_sys->i_x, p_vout->p_sys->i_y,
  373.                      p_vout->p_sys->b_autocrop ? "" : "not " );
  374.     msg_Info( p_vout, "ratio %d",  p_vout->p_sys->i_aspect / 432);
  375. #endif
  376.     if( p_vout->p_sys->p_vout )
  377.     {
  378.         vout_filter_DelChild( p_vout, p_vout->p_sys->p_vout, MouseEvent );
  379.         vout_CloseAndRelease( p_vout->p_sys->p_vout );
  380.     }
  381.     fmt.i_width = fmt.i_visible_width = p_vout->p_sys->i_width;
  382.     fmt.i_height = fmt.i_visible_height = p_vout->p_sys->i_height;
  383.     fmt.i_x_offset = fmt.i_y_offset = 0;
  384.     fmt.i_chroma = p_vout->render.i_chroma;
  385.     fmt.i_aspect = p_vout->p_sys->i_aspect;
  386.     fmt.i_sar_num = p_vout->p_sys->i_aspect * fmt.i_height / fmt.i_width;
  387.     fmt.i_sar_den = VOUT_ASPECT_FACTOR;
  388.     p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
  389.     if( p_vout->p_sys->p_vout == NULL )
  390.     {
  391.         msg_Err( p_vout, "failed to create vout" );
  392.         dialog_Fatal( p_vout, _("Cropping failed"), "%s",
  393.                         _("VLC could not open the video output module.") );
  394.         return VLC_EGENERIC;
  395.     }
  396.     vout_filter_AddChild( p_vout, p_vout->p_sys->p_vout, MouseEvent );
  397.     p_vout->p_sys->b_changed = false;
  398.     vlc_mutex_lock( &p_vout->p_sys->lock );
  399.     p_vout->p_sys->i_lastchange = 0;
  400.     vlc_mutex_unlock( &p_vout->p_sys->lock );
  401.     return VLC_SUCCESS;
  402. }
  403. /*****************************************************************************
  404.  * Render: display previously rendered output
  405.  *****************************************************************************
  406.  * This function sends the currently rendered image to Crop image, waits
  407.  * until it is displayed and switches the two rendering buffers, preparing next
  408.  * frame.
  409.  *****************************************************************************/
  410. static void Render( vout_thread_t *p_vout, picture_t *p_pic )
  411. {
  412.     picture_t *p_outpic = NULL;
  413.     int i_plane;
  414.     if( p_vout->p_sys->b_changed )
  415.     {
  416.         return;
  417.     }
  418.     while( ( p_outpic =
  419.                  vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 )
  420.            ) == NULL )
  421.     {
  422.         if( !vlc_object_alive (p_vout) || p_vout->b_error )
  423.         {
  424.             vout_DestroyPicture( p_vout->p_sys->p_vout, p_outpic );
  425.             return;
  426.         }
  427.         msleep( VOUT_OUTMEM_SLEEP );
  428.     }
  429.     p_outpic->date = p_pic->date;
  430.     vout_LinkPicture( p_vout->p_sys->p_vout, p_outpic );
  431.     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
  432.     {
  433.         uint8_t *p_in, *p_out, *p_out_end;
  434.         int i_in_pitch = p_pic->p[i_plane].i_pitch;
  435.         const int i_out_pitch = p_outpic->p[i_plane].i_pitch;
  436.         const int i_copy_pitch = p_outpic->p[i_plane].i_visible_pitch;
  437.         p_in = p_pic->p[i_plane].p_pixels
  438.                 /* Skip the right amount of lines */
  439.                 + i_in_pitch * ( p_pic->p[i_plane].i_visible_lines *
  440.                                  p_vout->p_sys->i_y / p_vout->output.i_height )
  441.                 /* Skip the right amount of columns */
  442.                 + i_in_pitch * p_vout->p_sys->i_x / p_vout->output.i_width;
  443.         p_out = p_outpic->p[i_plane].p_pixels;
  444.         p_out_end = p_out + i_out_pitch * p_outpic->p[i_plane].i_visible_lines;
  445.         while( p_out < p_out_end )
  446.         {
  447.             vlc_memcpy( p_out, p_in, i_copy_pitch );
  448.             p_in += i_in_pitch;
  449.             p_out += i_out_pitch;
  450.         }
  451.     }
  452.     vout_UnlinkPicture( p_vout->p_sys->p_vout, p_outpic );
  453.     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
  454.     /* The source image may still be in the cache ... parse it! */
  455.     vlc_mutex_lock( &p_vout->p_sys->lock );
  456.     if( p_vout->p_sys->b_autocrop )
  457.         UpdateStats( p_vout, p_pic );
  458.     vlc_mutex_unlock( &p_vout->p_sys->lock );
  459. }
  460. #ifdef BEST_AUTOCROP
  461. static bool NonBlackLine(uint8_t *p_in, int i_line, int i_pitch,
  462.                                int i_visible_pitch, int i_lines,
  463.                                int i_lumThreshold, int i_skipCountPercent,
  464.                                int i_nonBlackPixel, int i_chroma)
  465. {
  466.     const int i_col = i_line * i_pitch / i_lines;
  467.     int i_index, i_count = 0;
  468.     int i_skipCount = 0;
  469.     switch(i_chroma)
  470.     {
  471.     // planar YUV
  472.         case VLC_FOURCC('I','4','4','4'):
  473.         case VLC_FOURCC('I','4','2','2'):
  474.         case VLC_FOURCC('I','4','2','0'):
  475.         case VLC_FOURCC('Y','V','1','2'):
  476.         case VLC_FOURCC('I','Y','U','V'):
  477.         case VLC_FOURCC('I','4','1','1'):
  478.         case VLC_FOURCC('I','4','1','0'):
  479.         case VLC_FOURCC('Y','V','U','9'):
  480.         case VLC_FOURCC('Y','U','V','A'):
  481.             i_skipCount = (i_pitch * i_skipCountPercent) / 100;
  482.             for (i_index = i_col/2 + i_skipCount/2;
  483.                  i_index <= i_visible_pitch/2 + i_col/2 - i_skipCount/2;
  484.                  i_index++)
  485.             {
  486.                  i_count += (p_in[i_index] > i_lumThreshold);
  487.             if (i_count > i_nonBlackPixel) break;
  488.             }
  489.             break;
  490.     // packed RGB
  491.         case VLC_FOURCC('R','G','B','2'):    // packed by 1
  492.             i_skipCount = (i_pitch * i_skipCountPercent) / 100;
  493.             for (i_index = i_col/2 + i_skipCount/2;
  494.                  i_index <= i_visible_pitch/2 + i_col/2 - i_skipCount/2;
  495.                  i_index++)
  496.             {
  497.                  i_count += (p_in[i_index] > i_lumThreshold);
  498.             if (i_count > i_nonBlackPixel) break;
  499.             }
  500.             break;
  501.         case VLC_FOURCC('R','V','1','5'):    // packed by 2
  502.         case VLC_FOURCC('R','V','1','6'):    // packed by 2
  503.             i_skipCount = (i_pitch * i_skipCountPercent) / 100;
  504.             for (i_index = i_col/2 + i_skipCount/2 -
  505.                                 (i_col/2 + i_skipCount/2) % 2;
  506.                  i_index <= i_visible_pitch/2 + i_col/2 - i_skipCount/2;
  507.                  i_index+=2)
  508.             {
  509.                  i_count += (p_in[i_index] > i_lumThreshold) &&
  510.                             (p_in[i_index + 1] > i_lumThreshold);
  511.             if (i_count > i_nonBlackPixel) break;
  512.             }
  513.             break;
  514.         case VLC_FOURCC('R','V','2','4'):    // packed by 3
  515.             i_skipCount = (i_pitch * i_skipCountPercent) / 100;
  516.             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)
  517.             {
  518.                  i_count += (p_in[i_index] > i_lumThreshold) &&
  519.                             (p_in[i_index + 1] > i_lumThreshold) &&
  520.                             (p_in[i_index + 2] > i_lumThreshold);
  521.             if (i_count > i_nonBlackPixel) break;
  522.             }
  523.             break;
  524.         case VLC_FOURCC('R','V','3','2'):    // packed by 4
  525.             i_skipCount = (i_pitch * i_skipCountPercent) / 100;
  526.             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)
  527.             {
  528.                  i_count += (uint32_t)(*(p_in + i_index)) > (uint32_t)i_lumThreshold;
  529.             if (i_count > i_nonBlackPixel) break;
  530.             }
  531.             break;
  532.     // packed YUV
  533.         case VLC_FOURCC('Y','U','Y','2'):    // packed by 2
  534.         case VLC_FOURCC('Y','U','N','V'):    // packed by 2
  535.         case VLC_FOURCC('U','Y','V','Y'):    // packed by 2
  536.         case VLC_FOURCC('U','Y','N','V'):    // packed by 2
  537.         case VLC_FOURCC('Y','4','2','2'):    // packed by 2
  538.             i_skipCount = (i_pitch * i_skipCountPercent) / 100;
  539.             for (i_index = (i_col/2 + i_skipCount/2) -
  540.                            (i_col/2 + i_skipCount/2) % 2;
  541.                  i_index <= i_visible_pitch/2 + i_col/2 - i_skipCount/2;
  542.                  i_index+=2)
  543.             {
  544.                  i_count += (p_in[i_index] > i_lumThreshold);
  545.             if (i_count > i_nonBlackPixel) break;
  546.             }
  547.             break;
  548.         default :
  549.             break;
  550.     }
  551.     return (i_count > i_nonBlackPixel);
  552. }
  553. #endif
  554. static void UpdateStats( vout_thread_t *p_vout, picture_t *p_pic )
  555. {
  556.    uint8_t *p_in = p_pic->p[0].p_pixels;
  557.     int i_pitch = p_pic->p[0].i_pitch;
  558.     int i_visible_pitch = p_pic->p[0].i_visible_pitch;
  559.     int i_lines = p_pic->p[0].i_visible_lines;
  560.     int i_firstwhite = -1, i_lastwhite = -1, i;
  561. #ifdef BEST_AUTOCROP
  562.     int i_time = p_vout->p_sys->i_time;
  563.     int i_diff = p_vout->p_sys->i_diff;
  564.     if (!p_vout->p_sys->i_ratio)
  565.     {
  566.         /* Determine where black borders are */
  567.         for( i = 0 ; i < i_lines ; i++)
  568.         {
  569.                    if (NonBlackLine(p_in, i, i_pitch, i_visible_pitch, i_lines,
  570.                             p_vout->p_sys->i_threshold,
  571.                             p_vout->p_sys->i_skipPercent,
  572.                             p_vout->p_sys->i_nonBlackPixel,
  573.                             p_vout->output.i_chroma))
  574.                 {
  575.                     i_firstwhite = i;
  576.                     i_lastwhite = i_lines - i;
  577.                     break;
  578.                 }
  579.                 p_in += i_pitch;
  580.         }
  581.         /* Decide whether it's worth changing the size */
  582.         if( i_lastwhite == -1 )
  583.         {
  584.             p_vout->p_sys->i_lastchange = 0;
  585.             return;
  586.         }
  587.         if( (i_lastwhite - i_firstwhite) < (int) (p_vout->p_sys->i_height / 2) )
  588.         {
  589.             p_vout->p_sys->i_lastchange = 0;
  590.             return;
  591.         }
  592.         if (p_vout->output.i_aspect
  593.                             * p_vout->output.i_height /
  594.                                 (i_lastwhite - i_firstwhite + 1)
  595.                             * p_vout->p_sys->i_width /
  596.                                p_vout->output.i_width >
  597.                                     p_vout->p_sys->i_ratio_max * 432)
  598.         {
  599.             int i_height = ((p_vout->output.i_aspect / 432) *
  600.                            p_vout->output.i_height * p_vout->p_sys->i_width) /
  601.                           (p_vout->output.i_width * p_vout->p_sys->i_ratio_max);
  602.             i_firstwhite = (p_vout->output.i_height - i_height) / 2;
  603.             i_lastwhite =  p_vout->output.i_height - i_firstwhite;
  604. /*
  605.             p_vout->p_sys->i_lastchange = 0;
  606.             return;
  607. */
  608.         }
  609.         if( (i_lastwhite - i_firstwhite) <
  610.                         (int) (p_vout->p_sys->i_height + i_diff)
  611.              && (i_lastwhite - i_firstwhite + i_diff) >
  612.                         (int) p_vout->p_sys->i_height )
  613.         {
  614.             p_vout->p_sys->i_lastchange = 0;
  615.             return;
  616.         }
  617.         /* We need at least 'i_time' images to make up our mind */
  618.         p_vout->p_sys->i_lastchange++;
  619.         if( p_vout->p_sys->i_lastchange < (unsigned int)i_time )
  620.         {
  621.             return;
  622.         }
  623.     }
  624.     else
  625.     {
  626.         if ( p_vout->p_sys->i_lastchange >= (unsigned int)i_time )
  627.         {
  628.             p_vout->p_sys->i_aspect    =  p_vout->p_sys->i_ratio * 432;
  629.             int i_height = p_vout->output.i_aspect
  630.                                     * p_vout->output.i_height /
  631.                                         p_vout->p_sys->i_aspect
  632.                                     * p_vout->p_sys->i_width /
  633.                                         p_vout->output.i_width;
  634.             i_firstwhite = (p_vout->output.i_height - i_height) / 2;
  635.             i_lastwhite =  p_vout->output.i_height - i_firstwhite;
  636.         }
  637.         else
  638.         {
  639.             return;
  640.         }
  641.     }
  642. #else
  643.     /* Determine where black borders are */
  644.     switch( p_vout->output.i_chroma )
  645.     {
  646.     case VLC_FOURCC('I','4','2','0'):
  647.         /* XXX: Do not laugh ! I know this is very naive. But it's just a
  648.          *      proof of concept code snippet... */
  649.         for( i = i_lines ; i-- ; )
  650.         {
  651.             const int i_col = i * i_pitch / i_lines;
  652.             if( p_in[i_col/2] > 40
  653.                  && p_in[i_visible_pitch/2] > 40
  654.                  && p_in[i_visible_pitch/2 + i_col/2] > 40 )
  655.             {
  656.                 if( i_lastwhite == -1 )
  657.                 {
  658.                     i_lastwhite = i;
  659.                 }
  660.                 i_firstwhite = i;
  661.             }
  662.             p_in += i_pitch;
  663.         }
  664.         break;
  665.     default:
  666.         break;
  667.     }
  668.     /* Decide whether it's worth changing the size */
  669.     if( i_lastwhite == -1 )
  670.     {
  671.         p_vout->p_sys->i_lastchange = 0;
  672.         return;
  673.     }
  674.     if( (unsigned int)(i_lastwhite - i_firstwhite)
  675.                                            < p_vout->p_sys->i_height / 2 )
  676.     {
  677.         p_vout->p_sys->i_lastchange = 0;
  678.         return;
  679.     }
  680.     if( (unsigned int)(i_lastwhite - i_firstwhite)
  681.                                           < p_vout->p_sys->i_height + 16
  682.          && (unsigned int)(i_lastwhite - i_firstwhite + 16)
  683.                                                 > p_vout->p_sys->i_height )
  684.     {
  685.         p_vout->p_sys->i_lastchange = 0;
  686.         return;
  687.     }
  688.     /* We need at least 25 images to make up our mind */
  689.     p_vout->p_sys->i_lastchange++;
  690.     if( p_vout->p_sys->i_lastchange < 25 )
  691.     {
  692.         return;
  693.     }
  694. #endif //BEST_AUTOCROP
  695.     /* Tune a few values */
  696.     if( i_firstwhite & 1 )
  697.     {
  698.         i_firstwhite--;
  699.     }
  700.     if( !(i_lastwhite & 1) )
  701.     {
  702.         i_lastwhite++;
  703.     }
  704.     /* Change size */
  705.     p_vout->p_sys->i_y = i_firstwhite;
  706.     p_vout->p_sys->i_height = i_lastwhite - i_firstwhite + 1;
  707. #ifdef BEST_AUTOCROP
  708.     // check p_vout->p_sys->i_height <= p_vout->output.i_height
  709.     if (p_vout->p_sys->i_height > p_vout->output.i_height)
  710.         p_vout->p_sys->i_height = p_vout->output.i_height;
  711. #endif
  712.     p_vout->p_sys->i_aspect = p_vout->output.i_aspect
  713.                             * p_vout->output.i_height / p_vout->p_sys->i_height
  714.                             * p_vout->p_sys->i_width / p_vout->output.i_width;
  715.     p_vout->p_sys->b_changed = true;
  716. }
  717. /**
  718.  * Forward mouse event with proper conversion.
  719.  */
  720. static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
  721.                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
  722. {
  723.     vout_thread_t *p_vout = p_data;
  724.     VLC_UNUSED(p_this); VLC_UNUSED(oldval);
  725.     /* Translate the mouse coordinates
  726.      * FIXME missing lock */
  727.     if( !strcmp( psz_var, "mouse-x" ) )
  728.         newval.i_int += p_vout->p_sys->i_x;
  729.     else if( !strcmp( psz_var, "mouse-y" ) )
  730.         newval.i_int += p_vout->p_sys->i_y;
  731.     return var_Set( p_vout, psz_var, newval );
  732. }
  733. #ifdef BEST_AUTOCROP
  734. /*****************************************************************************
  735.  * FilterCallback: called when changing the ratio on the fly.
  736.  *****************************************************************************/
  737. static int FilterCallback( vlc_object_t *p_this, char const *psz_var,
  738.                            vlc_value_t oldval, vlc_value_t newval,
  739.                            void *p_data )
  740. {
  741.     VLC_UNUSED(p_data); VLC_UNUSED(oldval);
  742.     vout_thread_t * p_vout = (vout_thread_t *)p_this;
  743.     if( !strcmp( psz_var, "ratio-crop" ) )
  744.     {
  745.         vlc_mutex_lock( &p_vout->p_sys->lock );
  746.         if ( !strcmp( newval.psz_string, "Auto" ) )
  747.             p_vout->p_sys->i_ratio = 0;
  748.         else
  749.         {
  750.             p_vout->p_sys->i_ratio = (unsigned int)atoi(newval.psz_string);
  751.             p_vout->p_sys->i_lastchange = p_vout->p_sys->i_time;
  752.             p_vout->p_sys->b_autocrop = true;
  753.         }
  754.         if (p_vout->p_sys->i_ratio)
  755.         {
  756.             if (p_vout->p_sys->i_ratio < (p_vout->output.i_width * 1000) /
  757.                                     p_vout->output.i_height)
  758.                 p_vout->p_sys->i_ratio = (p_vout->output.i_width * 1000) /
  759.                                     p_vout->output.i_height;
  760.             if (p_vout->p_sys->i_ratio < p_vout->output.i_aspect / 432)
  761.                 p_vout->p_sys->i_ratio = p_vout->output.i_aspect / 432;
  762.         }
  763.         vlc_mutex_unlock( &p_vout->p_sys->lock );
  764.      }
  765.     return VLC_SUCCESS;
  766. }
  767. #endif