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

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * canvas.c : automatically resize and padd a video to fit in canvas
  3.  *****************************************************************************
  4.  * Copyright (C) 2008 the VideoLAN team
  5.  * $Id: f2074e5605bd5d72850c4e29cbdc91577ed8781d $
  6.  *
  7.  * Authors: Antoine Cellerier <dionoea at videolan dot 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  22.  *****************************************************************************/
  23. /*****************************************************************************
  24.  * Preamble
  25.  *****************************************************************************/
  26. #ifdef HAVE_CONFIG_H
  27. # include "config.h"
  28. #endif
  29. #include <limits.h>
  30. #include <vlc_common.h>
  31. #include <vlc_plugin.h>
  32. #include <vlc_filter.h>
  33. #include <vlc_vout.h>
  34. /*****************************************************************************
  35.  * Local and extern prototypes.
  36.  *****************************************************************************/
  37. static int  Activate( vlc_object_t * );
  38. static void Destroy( vlc_object_t * );
  39. static picture_t *Filter( filter_t *, picture_t * );
  40. static int alloc_init( filter_t *, void * );
  41. /* This module effectively implements a form of picture-in-picture.
  42.  *  - The outer picture is called the canvas.
  43.  *  - The innter picture is callsed the subpicture.
  44.  *
  45.  * NB, all of the following operatons take into account aspect ratio
  46.  *
  47.  * A canvas is of canvas_{width,height}.
  48.  * In Pad mode:
  49.  *  - The subpicture is upconverted with a inverse scalefactor of:
  50.  *     (The size of subpicture's largest dimension)
  51.  *     --------------------------------------------
  52.  *     (The size of canvas's equivalent dimension)
  53.  *
  54.  *   Ie, The subpicture's largest dimension is made equal to the
  55.  *   equivalent canvas dimension.
  56.  *
  57.  *  - The upconverted subpicture's smallest dimension is then padded
  58.  *    to make the upconverted subpicture have the same dimensions of
  59.  *    the canvas.
  60.  *
  61.  * In Crop mode:
  62.  *  - The subpicture is upconverted with an inverse scalefactor of:
  63.  *     (The size of subpicture's smallest dimension)
  64.  *     --------------------------------------------
  65.  *     (The size of canvas's equivalent dimension)
  66.  *
  67.  *   Ie, The subpicture's smallest dimension is made equal to the
  68.  *   equivalent canvas dimension. (The subpicture will then be
  69.  *   larger than the canvas)
  70.  *
  71.  *  - The upconverted subpicture's largest dimension is then cropped
  72.  *    to make the upconverted subpicture have the same dimensions of
  73.  *    the canvas.
  74.  */
  75. /* NB, use of `padd' in this module is a 16-17th Century spelling of `pad' */
  76. #define WIDTH_TEXT N_( "Output width" )
  77. #define WIDTH_LONGTEXT N_( 
  78.     "Output (canvas) image width" )
  79. #define HEIGHT_TEXT N_( "Output height" )
  80. #define HEIGHT_LONGTEXT N_( 
  81.     "Output (canvas) image height" )
  82. #define ASPECT_TEXT N_( "Output picture aspect ratio" )
  83. #define ASPECT_LONGTEXT N_( 
  84.     "Set the canvas' picture aspect ratio. " 
  85.     "If omitted, the canvas is assumed to have the same SAR as the input." )
  86. #define PADD_TEXT N_( "Pad video" )
  87. #define PADD_LONGTEXT N_( 
  88.     "If enabled, video will be padded to fit in canvas after scaling. " 
  89.     "Otherwise, video will be cropped to fix in canvas after scaling." )
  90. #define CFG_PREFIX "canvas-"
  91. /*****************************************************************************
  92.  * Module descriptor
  93.  *****************************************************************************/
  94. vlc_module_begin ()
  95.     set_shortname( N_("Canvas") )
  96.     set_description( N_("Automatically resize and pad a video") )
  97.     set_capability( "video filter2", 0 )
  98.     set_callbacks( Activate, Destroy )
  99.     set_category( CAT_VIDEO )
  100.     set_subcategory( SUBCAT_VIDEO_VFILTER2 )
  101.     add_integer_with_range( CFG_PREFIX "width", 0, 0, INT_MAX, NULL,
  102.                             WIDTH_TEXT, WIDTH_LONGTEXT, false )
  103.     add_integer_with_range( CFG_PREFIX "height", 0, 0, INT_MAX, NULL,
  104.                             HEIGHT_TEXT, HEIGHT_LONGTEXT, false )
  105.     add_string( CFG_PREFIX "aspect", NULL, NULL,
  106.                 ASPECT_TEXT, ASPECT_LONGTEXT, false )
  107.     add_bool( CFG_PREFIX "padd", true, NULL,
  108.               PADD_TEXT, PADD_LONGTEXT, false )
  109. vlc_module_end ()
  110. static const char *const ppsz_filter_options[] = {
  111.     "width", "height", "aspect", "padd", NULL
  112. };
  113. struct filter_sys_t
  114. {
  115.     filter_chain_t *p_chain;
  116. };
  117. /*****************************************************************************
  118.  *
  119.  *****************************************************************************/
  120. static int Activate( vlc_object_t *p_this )
  121. {
  122.     filter_t *p_filter = (filter_t *)p_this;
  123.     unsigned i_canvas_width; /* width of output canvas */
  124.     unsigned i_canvas_height; /* height of output canvas */
  125.     unsigned i_canvas_aspect; /* canvas PictureAspectRatio */
  126.     es_format_t fmt; /* target format after up/down conversion */
  127.     char psz_croppadd[100];
  128.     int i_padd,i_offset;
  129.     char *psz_aspect, *psz_parser;
  130.     bool b_padd;
  131.     if( !p_filter->b_allow_fmt_out_change )
  132.     {
  133.         msg_Err( p_filter, "Picture format change isn't allowed" );
  134.         return VLC_EGENERIC;
  135.     }
  136.     if( p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma )
  137.     {
  138.         msg_Err( p_filter, "Input and output chromas don't match" );
  139.         return VLC_EGENERIC;
  140.     }
  141.     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
  142.                        p_filter->p_cfg );
  143.     i_canvas_width = var_CreateGetInteger( p_filter, CFG_PREFIX "width" );
  144.     i_canvas_height = var_CreateGetInteger( p_filter, CFG_PREFIX "height" );
  145.     if( i_canvas_width == 0 || i_canvas_height == 0 )
  146.     {
  147.         msg_Err( p_filter, "Width and height options must be set" );
  148.         return VLC_EGENERIC;
  149.     }
  150.     if( i_canvas_width & 1 || i_canvas_height & 1 )
  151.     {
  152.         /* If this restriction were ever relaxed, it is very important to
  153.          * get the field polatiry correct */
  154.         msg_Err( p_filter, "Width and height options must be even integers" );
  155.         return VLC_EGENERIC;
  156.     }
  157.     psz_aspect = var_CreateGetNonEmptyString( p_filter, CFG_PREFIX "aspect" );
  158.     if( psz_aspect )
  159.     {
  160.         psz_parser = strchr( psz_aspect, ':' );
  161.         int numerator = atoi( psz_aspect );
  162.         int denominator = psz_parser ? atoi( psz_parser+1 ) : 0;
  163.         denominator = denominator == 0 ? 1 : denominator;
  164.         i_canvas_aspect = numerator * VOUT_ASPECT_FACTOR / denominator;
  165.         free( psz_aspect );
  166.         if( numerator <= 0 || denominator < 0 )
  167.         {
  168.             msg_Err( p_filter, "Aspect ratio must be strictly positive" );
  169.             return VLC_EGENERIC;
  170.         }
  171.     }
  172.     else
  173.     {
  174.         /* if there is no user supplied aspect ratio, assume the canvas
  175.          * has the same sample aspect ratio as the subpicture */
  176.         /* aspect = subpic_sar * canvas_width / canvas_height
  177.          *  where subpic_sar = subpic_ph * subpic_par / subpic_pw */
  178.         i_canvas_aspect = (uint64_t) p_filter->fmt_in.video.i_height
  179.                         * p_filter->fmt_in.video.i_aspect
  180.                         * i_canvas_width
  181.                         / (i_canvas_height * p_filter->fmt_in.video.i_width);
  182.     }
  183.     b_padd = var_CreateGetBool( p_filter, CFG_PREFIX "padd" );
  184.     filter_sys_t *p_sys = (filter_sys_t *)malloc( sizeof( filter_sys_t ) );
  185.     if( !p_sys )
  186.         return VLC_ENOMEM;
  187.     p_filter->p_sys = p_sys;
  188.     p_sys->p_chain = filter_chain_New( p_filter, "video filter2", true,
  189.                                        alloc_init, NULL, p_filter );
  190.     if( !p_sys->p_chain )
  191.     {
  192.         msg_Err( p_filter, "Could not allocate filter chain" );
  193.         free( p_sys );
  194.         return VLC_EGENERIC;
  195.     }
  196.     es_format_Copy( &fmt, &p_filter->fmt_in );
  197.     /* one dimension will end up with one of the following: */
  198.     fmt.video.i_width = i_canvas_width;
  199.     fmt.video.i_height = i_canvas_height;
  200.     if( b_padd )
  201.     {
  202.         /* Padd */
  203.         if( i_canvas_aspect > p_filter->fmt_in.video.i_aspect )
  204.         {
  205.             /* The canvas has a wider aspect than the subpicture:
  206.              *  ie, pillarbox the [scaled] subpicture */
  207.             /* The following is derived form:
  208.              * width = upconverted_subpic_height * subpic_par / canvas_sar
  209.              *  where canvas_sar = canvas_width / (canvas_height * canvas_par)
  210.              * then simplify */
  211.             fmt.video.i_width = i_canvas_width
  212.                               * p_filter->fmt_in.video.i_aspect
  213.                               / i_canvas_aspect;
  214.             if( fmt.video.i_width & 1 ) fmt.video.i_width -= 1;
  215.             i_padd = (i_canvas_width - fmt.video.i_width) / 2;
  216.             i_offset = (i_padd & 1);
  217.             snprintf( psz_croppadd, 100, "croppadd{paddleft=%d,paddright=%d}",
  218.                       i_padd - i_offset, i_padd + i_offset );
  219.         }
  220.         else
  221.         {
  222.             /* The canvas has a taller aspect than the subpicture:
  223.              *  ie, letterbox the [scaled] subpicture */
  224.             fmt.video.i_height = i_canvas_height
  225.                                * i_canvas_aspect
  226.                                / p_filter->fmt_in.video.i_aspect;
  227.             if( fmt.video.i_height & 1 ) fmt.video.i_height -= 1;
  228.             i_padd = (i_canvas_height - fmt.video.i_height ) / 2;
  229.             i_offset = (i_padd & 1);
  230.             snprintf( psz_croppadd, 100, "croppadd{paddtop=%d,paddbottom=%d}",
  231.                       i_padd - i_offset, i_padd + i_offset );
  232.         }
  233.     }
  234.     else
  235.     {
  236.         /* Crop */
  237.         if( i_canvas_aspect < p_filter->fmt_in.video.i_aspect )
  238.         {
  239.             /* The canvas has a narrower aspect than the subpicture:
  240.              *  ie, crop the [scaled] subpicture horizontally */
  241.             fmt.video.i_width = i_canvas_width
  242.                               * p_filter->fmt_in.video.i_aspect
  243.                               / i_canvas_aspect;
  244.             if( fmt.video.i_width & 1 ) fmt.video.i_width -= 1;
  245.             i_padd = (fmt.video.i_width - i_canvas_width) / 2;
  246.             i_offset = (i_padd & 1);
  247.             snprintf( psz_croppadd, 100, "croppadd{cropleft=%d,cropright=%d}",
  248.                       i_padd - i_offset, i_padd + i_offset );
  249.         }
  250.         else
  251.         {
  252.             /* The canvas has a shorter aspect than the subpicture:
  253.              *  ie, crop the [scaled] subpicture vertically */
  254.             fmt.video.i_height = i_canvas_height
  255.                                * i_canvas_aspect
  256.                                / p_filter->fmt_in.video.i_aspect;
  257.             if( fmt.video.i_height & 1 ) fmt.video.i_height -= 1;
  258.             i_padd = (fmt.video.i_height - i_canvas_height) / 2;
  259.             i_offset = (i_padd & 1);
  260.             snprintf( psz_croppadd, 100, "croppadd{croptop=%d,cropbottom=%d}",
  261.                       i_padd - i_offset, i_padd + i_offset );
  262.         }
  263.     }
  264.     /* xxx, should the clean area include the letter-boxing?
  265.      *  probably not, as some codecs can make use of that information
  266.      *  and it should be a scaled version of the input clean area
  267.      *   -- davidf */
  268.     fmt.video.i_visible_width = fmt.video.i_width;
  269.     fmt.video.i_visible_height = fmt.video.i_height;
  270.     filter_chain_Reset( p_sys->p_chain, &p_filter->fmt_in, &fmt );
  271.     /* Append scaling module */
  272.     filter_chain_AppendFilter( p_sys->p_chain, NULL, NULL, NULL, NULL );
  273.     /* Append padding module */
  274.     filter_chain_AppendFromString( p_sys->p_chain, psz_croppadd );
  275.     fmt = *filter_chain_GetFmtOut( p_sys->p_chain );
  276.     es_format_Copy( &p_filter->fmt_out, &fmt );
  277.     p_filter->fmt_out.video.i_aspect = i_canvas_aspect;
  278.     if( p_filter->fmt_out.video.i_width != i_canvas_width
  279.      || p_filter->fmt_out.video.i_height != i_canvas_height )
  280.     {
  281.         msg_Warn( p_filter, "Looks like something went wrong. "
  282.                   "Output size is %dx%d while we asked for %dx%d",
  283.                   p_filter->fmt_out.video.i_width,
  284.                   p_filter->fmt_out.video.i_height,
  285.                   i_canvas_width, i_canvas_height );
  286.     }
  287.     p_filter->pf_video_filter = Filter;
  288.     return VLC_SUCCESS;
  289. }
  290. /*****************************************************************************
  291.  *
  292.  *****************************************************************************/
  293. static void Destroy( vlc_object_t *p_this )
  294. {
  295.     filter_t *p_filter = (filter_t *)p_this;
  296.     filter_chain_Delete( p_filter->p_sys->p_chain );
  297.     free( p_filter->p_sys );
  298. }
  299. /*****************************************************************************
  300.  *
  301.  *****************************************************************************/
  302. static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
  303. {
  304.     return filter_chain_VideoFilter( p_filter->p_sys->p_chain, p_pic );
  305. }
  306. /*****************************************************************************
  307.  *
  308.  *****************************************************************************/
  309. static picture_t *video_new( filter_t *p_filter )
  310. {
  311.     return ((filter_t*)p_filter->p_owner)->pf_vout_buffer_new( (filter_t*)p_filter->p_owner );
  312. }
  313. static void video_del( filter_t *p_filter, picture_t *p_pic )
  314. {
  315.     if( ((filter_t*)p_filter->p_owner)->pf_vout_buffer_del )
  316.         ((filter_t*)p_filter->p_owner)->pf_vout_buffer_del( (filter_t*)p_filter->p_owner, p_pic );
  317. }
  318. static int alloc_init( filter_t *p_filter, void *p_data )
  319. {
  320.     p_filter->p_owner = p_data;
  321.     p_filter->pf_vout_buffer_new = video_new;
  322.     p_filter->pf_vout_buffer_del = video_del;
  323.     return VLC_SUCCESS;
  324. }