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

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * filter_chain.c : Handle chains of filter_t objects.
  3.  *****************************************************************************
  4.  * Copyright (C) 2008 the VideoLAN team
  5.  * $Id: c2b14f9f3183f3873e7d6dccc762305401cf2875 $
  6.  *
  7.  * Author: 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. #ifdef HAVE_CONFIG_H
  24. # include "config.h"
  25. #endif
  26. #include <vlc_filter.h>
  27. #include <vlc_arrays.h>
  28. #include <vlc_osd.h>
  29. #include <libvlc.h>
  30. typedef struct
  31. {
  32.     int (*pf_init)( filter_t *, void *p_data ); /* Callback called once filter allocation has succeeded to initialize the filter's buffer allocation callbacks. This function is responsible for setting p_owner if needed. */
  33.     void (* pf_clean)( filter_t * ); /* Callback called on filter removal from chain to clean up buffer allocation callbacks data (ie p_owner) */
  34.     void *p_data; /* Data for pf_buffer_allocation_init */
  35. } filter_chain_allocator_t;
  36. static int  AllocatorInit( const filter_chain_allocator_t *, filter_t * );
  37. static void AllocatorClean( const filter_chain_allocator_t *, filter_t * );
  38. static bool IsInternalVideoAllocator( filter_t * );
  39. static int  InternalVideoInit( filter_t *, void * );
  40. static void InternalVideoClean( filter_t * );
  41. static const filter_chain_allocator_t internal_video_allocator = {
  42.     .pf_init = InternalVideoInit,
  43.     .pf_clean = InternalVideoClean,
  44.     .p_data = NULL,
  45. };
  46. /* */
  47. struct filter_chain_t
  48. {
  49.     /* Parent object */
  50.     vlc_object_t *p_this;
  51.     /* List of filters */
  52.     vlc_array_t filters;
  53.     /* Capability of all the filters in the chain */
  54.     char *psz_capability;
  55.     /* Input format (read only) */
  56.     es_format_t fmt_in;
  57.     /* allow changing fmt_out if true */
  58.     bool b_allow_fmt_out_change;
  59.     /* Output format (writable depending on ... */
  60.     es_format_t fmt_out;
  61.     /* User provided allocator */
  62.     filter_chain_allocator_t allocator;
  63. };
  64. /**
  65.  * Local prototypes
  66.  */
  67. static filter_t *filter_chain_AppendFilterInternal( filter_chain_t *,
  68.                                                     const char *, config_chain_t *,
  69.                                                     const es_format_t *, const es_format_t * );
  70. static int filter_chain_AppendFromStringInternal( filter_chain_t *, const char * );
  71. static int filter_chain_DeleteFilterInternal( filter_chain_t *, filter_t * );
  72. static int UpdateBufferFunctions( filter_chain_t * );
  73. /**
  74.  * Filter chain initialisation
  75.  */
  76. filter_chain_t *__filter_chain_New( vlc_object_t *p_this,
  77.                                     const char *psz_capability,
  78.                                     bool b_allow_fmt_out_change,
  79.                                     int  (*pf_buffer_allocation_init)( filter_t *, void * ),
  80.                                     void (*pf_buffer_allocation_clean)( filter_t * ),
  81.                                     void *p_buffer_allocation_data )
  82. {
  83.     filter_chain_t *p_chain = malloc( sizeof( *p_chain ) );
  84.     if( !p_chain )
  85.         return NULL;
  86.     p_chain->p_this = p_this;
  87.     vlc_array_init( &p_chain->filters );
  88.     p_chain->psz_capability = strdup( psz_capability );
  89.     if( !p_chain->psz_capability )
  90.     {
  91.         vlc_array_clear( &p_chain->filters );
  92.         free( p_chain );
  93.         return NULL;
  94.     }
  95.     es_format_Init( &p_chain->fmt_in, UNKNOWN_ES, 0 );
  96.     es_format_Init( &p_chain->fmt_out, UNKNOWN_ES, 0 );
  97.     p_chain->b_allow_fmt_out_change = b_allow_fmt_out_change;
  98.     p_chain->allocator.pf_init = pf_buffer_allocation_init;
  99.     p_chain->allocator.pf_clean = pf_buffer_allocation_clean;
  100.     p_chain->allocator.p_data = p_buffer_allocation_data;
  101.     return p_chain;
  102. }
  103. /**
  104.  * Filter chain destruction
  105.  */
  106. void filter_chain_Delete( filter_chain_t *p_chain )
  107. {
  108.     filter_chain_Reset( p_chain, NULL, NULL );
  109.     es_format_Clean( &p_chain->fmt_in );
  110.     es_format_Clean( &p_chain->fmt_out );
  111.     free( p_chain->psz_capability );
  112.     vlc_array_clear( &p_chain->filters );
  113.     free( p_chain );
  114. }
  115. /**
  116.  * Filter chain reinitialisation
  117.  */
  118. void filter_chain_Reset( filter_chain_t *p_chain, const es_format_t *p_fmt_in,
  119.                          const es_format_t *p_fmt_out )
  120. {
  121.     while( vlc_array_count( &p_chain->filters ) > 0 )
  122.     {
  123.         filter_t *p_filter = vlc_array_item_at_index( &p_chain->filters, 0 );
  124.         filter_chain_DeleteFilterInternal( p_chain, p_filter );
  125.     }
  126.     if( p_fmt_in )
  127.     {
  128.         es_format_Clean( &p_chain->fmt_in );
  129.         es_format_Copy( &p_chain->fmt_in, p_fmt_in );
  130.     }
  131.     if( p_fmt_out )
  132.     {
  133.         es_format_Clean( &p_chain->fmt_out );
  134.         es_format_Copy( &p_chain->fmt_out, p_fmt_out );
  135.     }
  136. }
  137. filter_t *filter_chain_AppendFilter( filter_chain_t *p_chain,
  138.                                      const char *psz_name,
  139.                                      config_chain_t *p_cfg,
  140.                                      const es_format_t *p_fmt_in,
  141.                                      const es_format_t *p_fmt_out )
  142. {
  143.     filter_t *p_filter = filter_chain_AppendFilterInternal( p_chain, psz_name,
  144.                                                             p_cfg, p_fmt_in,
  145.                                                             p_fmt_out );
  146.     if( UpdateBufferFunctions( p_chain ) < 0 )
  147.         msg_Err( p_filter, "Woah! This doesn't look good." );
  148.     return p_filter;
  149. }
  150. int filter_chain_AppendFromString( filter_chain_t *p_chain,
  151.                                    const char *psz_string )
  152. {
  153.     const int i_ret = filter_chain_AppendFromStringInternal( p_chain, psz_string );
  154.     if( i_ret < 0 )
  155.         return i_ret;
  156.     /* FIXME That one seems bad if a error is returned */
  157.     return UpdateBufferFunctions( p_chain );
  158. }
  159. int filter_chain_DeleteFilter( filter_chain_t *p_chain, filter_t *p_filter )
  160. {
  161.     const int i_ret = filter_chain_DeleteFilterInternal( p_chain, p_filter );
  162.     if( i_ret < 0 )
  163.         return i_ret;
  164.     /* FIXME That one seems bad if a error is returned */
  165.     return UpdateBufferFunctions( p_chain );
  166. }
  167. /**
  168.  * Reading from the filter chain
  169.  */
  170. filter_t *filter_chain_GetFilter( filter_chain_t *p_chain, int i_position,
  171.                                   const char *psz_name )
  172. {
  173.     if( psz_name )
  174.     {
  175.         if( i_position < 0 )
  176.             return NULL;
  177.         for( int i = 0; i < vlc_array_count( &p_chain->filters ); i++ )
  178.         {
  179.             filter_t *p_filter = vlc_array_item_at_index( &p_chain->filters, i );
  180.             if( !strcmp( p_filter->psz_object_name, psz_name ) )
  181.             {
  182.                 if( i_position <= 0 )
  183.                     return p_filter;
  184.                 i_position--;
  185.             }
  186.        }
  187.         return NULL;
  188.     }
  189.     else
  190.     {
  191.         if( i_position < 0 || i_position >= vlc_array_count( &p_chain->filters ) )
  192.             return NULL;
  193.         return vlc_array_item_at_index( &p_chain->filters, i_position );
  194.     }
  195. }
  196. int filter_chain_GetLength( filter_chain_t *p_chain )
  197. {
  198.     return vlc_array_count( &p_chain->filters );
  199. }
  200. const es_format_t *filter_chain_GetFmtOut( filter_chain_t *p_chain )
  201. {
  202.     if( p_chain->b_allow_fmt_out_change )
  203.         return &p_chain->fmt_out;
  204.     const int i_count = vlc_array_count( &p_chain->filters );
  205.     if( i_count > 0 )
  206.     {
  207.         filter_t *p_last = vlc_array_item_at_index( &p_chain->filters, i_count - 1 );
  208.         return &p_last->fmt_out;
  209.     }
  210.     /* Unless filter_chain_Reset has been called we are doomed */
  211.     return &p_chain->fmt_out;
  212. }
  213. picture_t *filter_chain_VideoFilter( filter_chain_t *p_chain, picture_t *p_pic )
  214. {
  215.     for( int i = 0; i < vlc_array_count( &p_chain->filters ); i++ )
  216.     {
  217.         filter_t *p_filter = vlc_array_item_at_index( &p_chain->filters, i );
  218.         p_pic = p_filter->pf_video_filter( p_filter, p_pic );
  219.         if( !p_pic )
  220.             break;
  221.     }
  222.     return p_pic;
  223. }
  224. block_t *filter_chain_AudioFilter( filter_chain_t *p_chain, block_t *p_block )
  225. {
  226.     for( int i = 0; i < vlc_array_count( &p_chain->filters ); i++ )
  227.     {
  228.         filter_t *p_filter = vlc_array_item_at_index( &p_chain->filters, i );
  229.         p_block = p_filter->pf_audio_filter( p_filter, p_block );
  230.         if( !p_block )
  231.             break;
  232.     }
  233.     return p_block;
  234. }
  235. void filter_chain_SubFilter( filter_chain_t *p_chain,
  236.                              mtime_t display_date )
  237. {
  238.     for( int i = 0; i < vlc_array_count( &p_chain->filters ); i++ )
  239.     {
  240.         filter_t *p_filter = vlc_array_item_at_index( &p_chain->filters, i );
  241.         subpicture_t *p_subpic = p_filter->pf_sub_filter( p_filter, display_date );
  242.         /* XXX I find that spu_t cast ugly */
  243.         if( p_subpic )
  244.             spu_DisplaySubpicture( (spu_t*)p_chain->p_this, p_subpic );
  245.     }
  246. }
  247. /* Helpers */
  248. static filter_t *filter_chain_AppendFilterInternal( filter_chain_t *p_chain,
  249.                                                     const char *psz_name,
  250.                                                     config_chain_t *p_cfg,
  251.                                                     const es_format_t *p_fmt_in,
  252.                                                     const es_format_t *p_fmt_out )
  253. {
  254.     filter_t *p_filter = vlc_custom_create( p_chain->p_this, sizeof(*p_filter),
  255.                                             VLC_OBJECT_GENERIC, "filter" );
  256.     if( !p_filter )
  257.         return NULL;
  258.     vlc_object_attach( p_filter, p_chain->p_this );
  259.     if( !p_fmt_in )
  260.     {
  261.         p_fmt_in = &p_chain->fmt_in;
  262.         const int i_count = vlc_array_count( &p_chain->filters );
  263.         if( i_count > 0 )
  264.         {
  265.             filter_t *p_last = vlc_array_item_at_index( &p_chain->filters, i_count - 1 );
  266.             p_fmt_in = &p_last->fmt_out;
  267.         }
  268.     }
  269.     if( !p_fmt_out )
  270.     {
  271.         p_fmt_out = &p_chain->fmt_out;
  272.     }
  273.     es_format_Copy( &p_filter->fmt_in, p_fmt_in );
  274.     es_format_Copy( &p_filter->fmt_out, p_fmt_out );
  275.     p_filter->p_cfg = p_cfg;
  276.     p_filter->b_allow_fmt_out_change = p_chain->b_allow_fmt_out_change;
  277.     p_filter->p_module = module_need( p_filter, p_chain->psz_capability,
  278.                                       psz_name, psz_name != NULL );
  279.     if( !p_filter->p_module )
  280.         goto error;
  281.     if( p_filter->b_allow_fmt_out_change )
  282.     {
  283.         es_format_Clean( &p_chain->fmt_out );
  284.         es_format_Copy( &p_chain->fmt_out, &p_filter->fmt_out );
  285.     }
  286.     if( AllocatorInit( &p_chain->allocator, p_filter ) )
  287.         goto error;
  288.     vlc_array_append( &p_chain->filters, p_filter );
  289.     msg_Dbg( p_chain->p_this, "Filter '%s' (%p) appended to chain",
  290.              psz_name ? psz_name : p_filter->psz_object_name, p_filter );
  291.     return p_filter;
  292. error:
  293.     if( psz_name )
  294.         msg_Err( p_chain->p_this, "Failed to create %s '%s'",
  295.                  p_chain->psz_capability, psz_name );
  296.     else
  297.         msg_Err( p_chain->p_this, "Failed to create %s",
  298.                  p_chain->psz_capability );
  299.     if( p_filter->p_module )
  300.         module_unneed( p_filter, p_filter->p_module );
  301.     es_format_Clean( &p_filter->fmt_in );
  302.     es_format_Clean( &p_filter->fmt_out );
  303.     vlc_object_detach( p_filter );
  304.     vlc_object_release( p_filter );
  305.     return NULL;
  306. }
  307. static int filter_chain_AppendFromStringInternal( filter_chain_t *p_chain,
  308.                                                   const char *psz_string )
  309. {
  310.     config_chain_t *p_cfg = NULL;
  311.     char *psz_name = NULL;
  312.     char* psz_new_string;
  313.     if( !psz_string || !*psz_string )
  314.         return 0;
  315.     psz_new_string = config_ChainCreate( &psz_name, &p_cfg, psz_string );
  316.     filter_t *p_filter = filter_chain_AppendFilterInternal( p_chain, psz_name,
  317.                                                             p_cfg, NULL, NULL );
  318.     if( !p_filter )
  319.     {
  320.         msg_Err( p_chain->p_this, "Failed while trying to append '%s' "
  321.                  "to filter chain", psz_name );
  322.         free( psz_name );
  323.         free( p_cfg );
  324.         free( psz_new_string );
  325.         return -1;
  326.     }
  327.     free( psz_name );
  328.     const int i_ret = filter_chain_AppendFromStringInternal( p_chain, psz_new_string );
  329.     free( psz_new_string );
  330.     if( i_ret < 0 )
  331.     {
  332.         filter_chain_DeleteFilterInternal( p_chain, p_filter );
  333.         return i_ret;
  334.     }
  335.     return 1 + i_ret;
  336. }
  337. static int filter_chain_DeleteFilterInternal( filter_chain_t *p_chain,
  338.                                               filter_t *p_filter )
  339. {
  340.     const int i_filter_idx = vlc_array_index_of_item( &p_chain->filters, p_filter );
  341.     if( i_filter_idx < 0 )
  342.     {
  343.         /* Oops, filter wasn't found
  344.          * FIXME shoulnd't it be an assert instead ? */
  345.         msg_Err( p_chain->p_this,
  346.                  "Couldn't find filter '%s' (%p) when trying to remove it from chain",
  347.                  p_filter->psz_object_name, p_filter );
  348.         return VLC_EGENERIC;
  349.     }
  350.     /* Remove it from the chain */
  351.     vlc_array_remove( &p_chain->filters, i_filter_idx );
  352.     msg_Dbg( p_chain->p_this, "Filter '%s' (%p) removed from chain",
  353.              p_filter->psz_object_name, p_filter );
  354.     /* Destroy the filter object */
  355.     if( IsInternalVideoAllocator( p_filter ) )
  356.         AllocatorClean( &internal_video_allocator, p_filter );
  357.     else
  358.         AllocatorClean( &p_chain->allocator, p_filter );
  359.     vlc_object_detach( p_filter );
  360.     if( p_filter->p_module )
  361.         module_unneed( p_filter, p_filter->p_module );
  362.     vlc_object_release( p_filter );
  363.     /* FIXME: check fmt_in/fmt_out consitency */
  364.     return VLC_SUCCESS;
  365. }
  366. /**
  367.  * Internal chain buffer handling
  368.  */
  369. static int UpdateVideoBufferFunctions( filter_chain_t *p_chain )
  370. {
  371.     /**
  372.      * Last filter uses the filter chain's parent buffer allocation
  373.      * functions. All the other filters use internal functions.
  374.      * This makes it possible to have format changes between each
  375.      * filter without having to worry about the parent's picture
  376.      * heap format.
  377.      */
  378.     const int i_count = vlc_array_count( &p_chain->filters );
  379.     for( int i = 0; i < i_count - 1; i++ )
  380.     {
  381.         filter_t *p_filter = vlc_array_item_at_index( &p_chain->filters, i );
  382.         if( !IsInternalVideoAllocator( p_filter ) )
  383.         {
  384.             AllocatorClean( &p_chain->allocator, p_filter );
  385.             AllocatorInit( &internal_video_allocator, p_filter );
  386.         }
  387.     }
  388.     if( i_count >= 1 )
  389.     {
  390.         filter_t * p_filter = vlc_array_item_at_index( &p_chain->filters, i_count - 1 );
  391.         if( IsInternalVideoAllocator( p_filter ) )
  392.         {
  393.             AllocatorClean( &internal_video_allocator, p_filter );
  394.             if( AllocatorInit( &p_chain->allocator, p_filter ) )
  395.                 return VLC_EGENERIC;
  396.         }
  397.     }
  398.     return VLC_SUCCESS;
  399. }
  400. /**
  401.  * This function should be called after every filter chain change
  402.  */
  403. static int UpdateBufferFunctions( filter_chain_t *p_chain )
  404. {
  405.     if( !strcmp( p_chain->psz_capability, "video filter2" ) )
  406.         return UpdateVideoBufferFunctions( p_chain );
  407.     return VLC_SUCCESS;
  408. }
  409. /* Internal video allocator functions */
  410. static picture_t *VideoBufferNew( filter_t *p_filter )
  411. {
  412.     const video_format_t *p_fmt = &p_filter->fmt_out.video;
  413.     picture_t *p_picture = picture_New( p_fmt->i_chroma,
  414.                                         p_fmt->i_width, p_fmt->i_height,
  415.                                         p_fmt->i_aspect );
  416.     if( !p_picture )
  417.         msg_Err( p_filter, "Failed to allocate picture" );
  418.     return p_picture;
  419. }
  420. static void VideoBufferDelete( filter_t *p_filter, picture_t *p_picture )
  421. {
  422.     VLC_UNUSED( p_filter );
  423.     picture_Release( p_picture );
  424. }
  425. static int InternalVideoInit( filter_t *p_filter, void *p_data )
  426. {
  427.     VLC_UNUSED(p_data);
  428.     p_filter->pf_vout_buffer_new = VideoBufferNew;
  429.     p_filter->pf_vout_buffer_del = VideoBufferDelete;
  430.     return VLC_SUCCESS;
  431. }
  432. static void InternalVideoClean( filter_t *p_filter )
  433. {
  434.     p_filter->pf_vout_buffer_new = NULL;
  435.     p_filter->pf_vout_buffer_del = NULL;
  436. }
  437. static bool IsInternalVideoAllocator( filter_t *p_filter )
  438. {
  439.     return p_filter->pf_vout_buffer_new == VideoBufferNew;
  440. }
  441. /* */
  442. static int AllocatorInit( const filter_chain_allocator_t *p_alloc, filter_t *p_filter )
  443. {
  444.     if( p_alloc->pf_init )
  445.         return p_alloc->pf_init( p_filter, p_alloc->p_data );
  446.     return VLC_SUCCESS;
  447. }
  448. static void AllocatorClean( const filter_chain_allocator_t *p_alloc, filter_t *p_filter )
  449. {
  450.     if( p_alloc->pf_clean )
  451.         p_alloc->pf_clean( p_filter );
  452. }