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

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * bridge.c: bridge stream output module
  3.  *****************************************************************************
  4.  * Copyright (C) 2005-2008 the VideoLAN team
  5.  * $Id: 16d6b4927af5072bbd2d3e7b12028330ef20b6e1 $
  6.  *
  7.  * Authors: Christophe Massiot <massiot@via.ecp.fr>
  8.  *          Antoine Cellerier <dionoea at videolan dot org>
  9.  *
  10.  * This program is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; either version 2 of the License, or
  13.  * (at your option) any later version.
  14.  *
  15.  * This program is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program; if not, write to the Free Software
  22.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  23.  *****************************************************************************/
  24. /*****************************************************************************
  25.  * Preamble
  26.  *****************************************************************************/
  27. #ifdef HAVE_CONFIG_H
  28. # include "config.h"
  29. #endif
  30. #include <vlc_common.h>
  31. #include <vlc_plugin.h>
  32. #include <vlc_sout.h>
  33. #include <vlc_block.h>
  34. /*****************************************************************************
  35.  * Module descriptor
  36.  *****************************************************************************/
  37. #define ID_TEXT N_("ID")
  38. #define ID_LONGTEXT N_( 
  39.     "Integer identifier for this elementary stream. This will be used to " 
  40.     ""find" this stream later." )
  41. #define DEST_TEXT N_( "Destination bridge-in name" )
  42. #define DEST_LONGTEXT N_( 
  43.     "Name of the destination bridge-in. If you do not need more " 
  44.     "than one bridge-in at a time, you can discard this option." )
  45. #define DELAY_TEXT N_("Delay")
  46. #define DELAY_LONGTEXT N_("Pictures coming from the picture video outputs " 
  47.         "will be delayed according to this value (in milliseconds, should be "
  48.         ">= 100 ms). For high values, you will need to raise caching values." )
  49. #define ID_OFFSET_TEXT N_("ID Offset")
  50. #define ID_OFFSET_LONGTEXT N_("Offset to add to the stream IDs specified in " 
  51.         "bridge_out to obtain the stream IDs bridge_in will register.")
  52. #define NAME_TEXT N_( "Name of current instance" )
  53. #define NAME_LONGTEXT N_( 
  54.     "Name of this bridge-in instance. If you do not need more " 
  55.     "than one bridge-in at a time, you can discard this option." )
  56. #define PLACEHOLDER_TEXT N_( "Fallback to placeholder stream when out of data" )
  57. #define PLACEHOLDER_LONGTEXT N_( 
  58.     "If set to true, the bridge will discard all input elementary streams " 
  59.     "except if it doesn't receive data from another bridge-in. This can " 
  60.     "be used to configure a place holder stream when the real source " 
  61.     "breaks. Source and placeholder streams should have the same format. " )
  62. #define PLACEHOLDER_DELAY_TEXT N_( "Placeholder delay" )
  63. #define PLACEHOLDER_DELAY_LONGTEXT N_( 
  64.     "Delay (in ms) before the placeholder kicks in." )
  65. #define PLACEHOLDER_IFRAME_TEXT N_( "Wait for I frame before toggling placholder" )
  66. #define PLACEHOLDER_IFRAME_LONGTEXT N_( 
  67.     "If enabled, switching between the placeholder and the normal stream " 
  68.     "will only occur on I frames. This will remove artifacts on stream " 
  69.     "switching at the expense of a slightly longer delay, depending on " 
  70.     "the frequence of I frames in the streams." )
  71. static int  OpenOut ( vlc_object_t * );
  72. static void CloseOut( vlc_object_t * );
  73. static int  OpenIn  ( vlc_object_t * );
  74. static void CloseIn ( vlc_object_t * );
  75. #define SOUT_CFG_PREFIX_OUT "sout-bridge-out-"
  76. #define SOUT_CFG_PREFIX_IN "sout-bridge-in-"
  77. vlc_module_begin ()
  78.     set_shortname( N_("Bridge"))
  79.     set_description( N_("Bridge stream output"))
  80.     add_submodule ()
  81.     set_section( N_("Bridge out"), NULL )
  82.     set_capability( "sout stream", 50 )
  83.     add_shortcut( "bridge-out" )
  84.     /* Only usable with VLM. No category so not in gui preferences
  85.     set_category( CAT_SOUT )
  86.     set_subcategory( SUBCAT_SOUT_STREAM )*/
  87.     add_integer( SOUT_CFG_PREFIX_OUT "id", 0, NULL, ID_TEXT, ID_LONGTEXT,
  88.                  false )
  89.     add_string( SOUT_CFG_PREFIX_OUT "in-name", "default", NULL,
  90.                 DEST_TEXT, DEST_LONGTEXT, false )
  91.     set_callbacks( OpenOut, CloseOut )
  92.     add_submodule ()
  93.     set_section( N_("Bridge in"), NULL )
  94.     set_capability( "sout stream", 50 )
  95.     add_shortcut( "bridge-in" )
  96.     /*set_category( CAT_SOUT )
  97.     set_subcategory( SUBCAT_SOUT_STREAM )*/
  98.     add_integer( SOUT_CFG_PREFIX_IN "delay", 0, NULL, DELAY_TEXT,
  99.                  DELAY_LONGTEXT, false )
  100.     add_integer( SOUT_CFG_PREFIX_IN "id-offset", 8192, NULL, ID_OFFSET_TEXT,
  101.                  ID_OFFSET_LONGTEXT, false )
  102.     add_string( SOUT_CFG_PREFIX_IN "name", "default", NULL,
  103.                 NAME_TEXT, NAME_LONGTEXT, false )
  104.     add_bool( SOUT_CFG_PREFIX_IN "placeholder", false, NULL,
  105.               PLACEHOLDER_TEXT, PLACEHOLDER_LONGTEXT, false )
  106.     add_integer( SOUT_CFG_PREFIX_IN "placeholder-delay", 200, NULL,
  107.                  PLACEHOLDER_DELAY_TEXT, PLACEHOLDER_DELAY_LONGTEXT, false )
  108.     add_bool( SOUT_CFG_PREFIX_IN "placeholder-switch-on-iframe", true, NULL,
  109.               PLACEHOLDER_IFRAME_TEXT, PLACEHOLDER_IFRAME_LONGTEXT, false )
  110.     set_callbacks( OpenIn, CloseIn )
  111. vlc_module_end ()
  112. /*****************************************************************************
  113.  * Local prototypes
  114.  *****************************************************************************/
  115. static const char *const ppsz_sout_options_out[] = {
  116.     "id", "in-name", NULL
  117. };
  118. static const char *const ppsz_sout_options_in[] = {
  119.     "delay", "id-offset", "name",
  120.     "placeholder", "placeholder-delay", "placeholder-switch-on-iframe",
  121.     NULL
  122. };
  123. static sout_stream_id_t *AddOut ( sout_stream_t *, es_format_t * );
  124. static int               DelOut ( sout_stream_t *, sout_stream_id_t * );
  125. static int               SendOut( sout_stream_t *, sout_stream_id_t *, block_t * );
  126. static sout_stream_id_t *AddIn ( sout_stream_t *, es_format_t * );
  127. static int               DelIn ( sout_stream_t *, sout_stream_id_t * );
  128. static int               SendIn( sout_stream_t *, sout_stream_id_t *, block_t * );
  129. typedef struct bridged_es_t
  130. {
  131.     es_format_t fmt;
  132.     block_t *p_block;
  133.     block_t **pp_last;
  134.     bool b_empty;
  135.     /* bridge in part */
  136.     sout_stream_id_t *id;
  137.     mtime_t i_last;
  138.     bool b_changed;
  139. } bridged_es_t;
  140. typedef struct bridge_t
  141. {
  142.     bridged_es_t **pp_es;
  143.     int i_es_num;
  144. } bridge_t;
  145. #define GetBridge(a,b) __GetBridge( VLC_OBJECT(a), b )
  146. static bridge_t *__GetBridge( vlc_object_t *p_object, const char *psz_name )
  147. {
  148.     bridge_t *p_bridge;
  149.     vlc_value_t val;
  150.     if( var_Get( p_object->p_libvlc, psz_name, &val ) )
  151.     {
  152.         p_bridge = NULL;
  153.     }
  154.     else
  155.     {
  156.         p_bridge = val.p_address;
  157.     }
  158.     return p_bridge;
  159. }
  160. /*
  161.  * Bridge out
  162.  */
  163. typedef struct out_sout_stream_sys_t
  164. {
  165.     vlc_mutex_t *p_lock;
  166.     bridged_es_t *p_es;
  167.     int i_id;
  168.     bool b_inited;
  169.     char *psz_name;
  170. } out_sout_stream_sys_t;
  171. /*****************************************************************************
  172.  * OpenOut:
  173.  *****************************************************************************/
  174. static int OpenOut( vlc_object_t *p_this )
  175. {
  176.     sout_stream_t     *p_stream = (sout_stream_t *)p_this;
  177.     out_sout_stream_sys_t *p_sys;
  178.     vlc_value_t val;
  179.     config_ChainParse( p_stream, SOUT_CFG_PREFIX_OUT, ppsz_sout_options_out,
  180.                    p_stream->p_cfg );
  181.     p_sys          = malloc( sizeof( out_sout_stream_sys_t ) );
  182.     p_sys->b_inited = false;
  183.     var_Create( p_this->p_libvlc, "bridge-lock", VLC_VAR_MUTEX );
  184.     var_Get( p_this->p_libvlc, "bridge-lock", &val );
  185.     p_sys->p_lock = val.p_address;
  186.     var_Get( p_stream, SOUT_CFG_PREFIX_OUT "id", &val );
  187.     p_sys->i_id = val.i_int;
  188.     var_Get( p_stream, SOUT_CFG_PREFIX_OUT "in-name", &val );
  189.     if( asprintf( &p_sys->psz_name, "bridge-struct-%s", val.psz_string )<0 )
  190.     {
  191.         free( val.psz_string );
  192.         free( p_sys );
  193.         return VLC_ENOMEM;
  194.     }
  195.     free( val.psz_string );
  196.     p_stream->pf_add    = AddOut;
  197.     p_stream->pf_del    = DelOut;
  198.     p_stream->pf_send   = SendOut;
  199.     p_stream->p_sys     = (sout_stream_sys_t *)p_sys;
  200.     p_stream->p_sout->i_out_pace_nocontrol++;
  201.     return VLC_SUCCESS;
  202. }
  203. /*****************************************************************************
  204.  * CloseOut:
  205.  *****************************************************************************/
  206. static void CloseOut( vlc_object_t * p_this )
  207. {
  208.     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
  209.     out_sout_stream_sys_t *p_sys = (out_sout_stream_sys_t *)p_stream->p_sys;
  210.     p_stream->p_sout->i_out_pace_nocontrol--;
  211.     free( p_sys->psz_name );
  212.     free( p_sys );
  213. }
  214. static sout_stream_id_t * AddOut( sout_stream_t *p_stream, es_format_t *p_fmt )
  215. {
  216.     out_sout_stream_sys_t *p_sys = (out_sout_stream_sys_t *)p_stream->p_sys;
  217.     bridge_t *p_bridge;
  218.     bridged_es_t *p_es;
  219.     int i;
  220.     if ( p_sys->b_inited )
  221.     {
  222.         msg_Err( p_stream, "bridge-out can only handle 1 es at a time." );
  223.         return NULL;
  224.     }
  225.     p_sys->b_inited = true;
  226.     vlc_mutex_lock( p_sys->p_lock );
  227.     p_bridge = GetBridge( p_stream, p_sys->psz_name );
  228.     if ( p_bridge == NULL )
  229.     {
  230.         vlc_object_t *p_libvlc = VLC_OBJECT( p_stream->p_libvlc );
  231.         vlc_value_t val;
  232.         p_bridge = malloc( sizeof( bridge_t ) );
  233.         var_Create( p_libvlc, p_sys->psz_name, VLC_VAR_ADDRESS );
  234.         val.p_address = p_bridge;
  235.         var_Set( p_libvlc, p_sys->psz_name, val );
  236.         p_bridge->i_es_num = 0;
  237.         p_bridge->pp_es = NULL;
  238.     }
  239.     for ( i = 0; i < p_bridge->i_es_num; i++ )
  240.     {
  241.         if ( p_bridge->pp_es[i]->b_empty && !p_bridge->pp_es[i]->b_changed )
  242.             break;
  243.     }
  244.     if ( i == p_bridge->i_es_num )
  245.     {
  246.         p_bridge->pp_es = realloc( p_bridge->pp_es,
  247.                                    (p_bridge->i_es_num + 1)
  248.                                      * sizeof(bridged_es_t *) );
  249.         p_bridge->i_es_num++;
  250.         p_bridge->pp_es[i] = malloc( sizeof(bridged_es_t) );
  251.     }
  252.     p_sys->p_es = p_es = p_bridge->pp_es[i];
  253.     p_es->fmt = *p_fmt;
  254.     p_es->fmt.i_id = p_sys->i_id;
  255.     p_es->p_block = NULL;
  256.     p_es->pp_last = &p_es->p_block;
  257.     p_es->b_empty = false;
  258.     p_es->id = NULL;
  259.     p_es->i_last = 0;
  260.     p_es->b_changed = true;
  261.     msg_Dbg( p_stream, "bridging out input codec=%4.4s id=%d pos=%d",
  262.              (char*)&p_es->fmt.i_codec, p_es->fmt.i_id, i );
  263.     vlc_mutex_unlock( p_sys->p_lock );
  264.     return (sout_stream_id_t *)p_sys;
  265. }
  266. static int DelOut( sout_stream_t *p_stream, sout_stream_id_t *id )
  267. {
  268.     VLC_UNUSED(id);
  269.     out_sout_stream_sys_t *p_sys = (out_sout_stream_sys_t *)p_stream->p_sys;
  270.     bridged_es_t *p_es;
  271.     if ( !p_sys->b_inited )
  272.     {
  273.         return VLC_SUCCESS;
  274.     }
  275.     vlc_mutex_lock( p_sys->p_lock );
  276.     p_es = p_sys->p_es;
  277.     p_es->b_empty = true;
  278.     block_ChainRelease( p_es->p_block );
  279.     p_es->p_block = false;
  280.     p_es->b_changed = true;
  281.     vlc_mutex_unlock( p_sys->p_lock );
  282.     p_sys->b_inited = false;
  283.     return VLC_SUCCESS;
  284. }
  285. static int SendOut( sout_stream_t *p_stream, sout_stream_id_t *id,
  286.                     block_t *p_buffer )
  287. {
  288.     out_sout_stream_sys_t *p_sys = (out_sout_stream_sys_t *)p_stream->p_sys;
  289.     bridged_es_t *p_es;
  290.     if ( (out_sout_stream_sys_t *)id != p_sys )
  291.     {
  292.         block_ChainRelease( p_buffer );
  293.         return VLC_SUCCESS;
  294.     }
  295.     vlc_mutex_lock( p_sys->p_lock );
  296.     p_es = p_sys->p_es;
  297.     *p_es->pp_last = p_buffer;
  298.     while ( p_buffer != NULL )
  299.     {
  300.         p_es->pp_last = &p_buffer->p_next;
  301.         p_buffer = p_buffer->p_next;
  302.     }
  303.     vlc_mutex_unlock( p_sys->p_lock );
  304.     return VLC_SUCCESS;
  305. }
  306. /*
  307.  * Bridge in
  308.  */
  309. typedef struct in_sout_stream_sys_t
  310. {
  311.     sout_stream_t *p_out;
  312.     vlc_mutex_t *p_lock;
  313.     int i_id_offset;
  314.     mtime_t i_delay;
  315.     char *psz_name;
  316.     bool b_placeholder;
  317.     bool b_switch_on_iframe;
  318.     int i_state;
  319.     mtime_t i_placeholder_delay;
  320.     sout_stream_id_t *id_video;
  321.     mtime_t i_last_video;
  322.     sout_stream_id_t *id_audio;
  323.     mtime_t i_last_audio;
  324. } in_sout_stream_sys_t;
  325. enum { placeholder_on, placeholder_off };
  326. /*****************************************************************************
  327.  * OpenIn:
  328.  *****************************************************************************/
  329. static int OpenIn( vlc_object_t *p_this )
  330. {
  331.     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
  332.     in_sout_stream_sys_t *p_sys;
  333.     vlc_value_t val;
  334.     p_sys          = malloc( sizeof( in_sout_stream_sys_t ) );
  335.     p_sys->p_out = sout_StreamNew( p_stream->p_sout, p_stream->psz_next );
  336.     if( !p_sys->p_out )
  337.     {
  338.         msg_Err( p_stream, "cannot create chain" );
  339.         free( p_sys );
  340.         return VLC_EGENERIC;
  341.     }
  342.     config_ChainParse( p_stream, SOUT_CFG_PREFIX_IN, ppsz_sout_options_in,
  343.                    p_stream->p_cfg );
  344.     var_Create( p_this->p_libvlc, "bridge-lock", VLC_VAR_MUTEX );
  345.     var_Get( p_this->p_libvlc, "bridge-lock", &val );
  346.     p_sys->p_lock = val.p_address;
  347.     var_Get( p_stream, SOUT_CFG_PREFIX_IN "id-offset", &val );
  348.     p_sys->i_id_offset = val.i_int;
  349.     var_Get( p_stream, SOUT_CFG_PREFIX_IN "delay", &val );
  350.     p_sys->i_delay = (mtime_t)val.i_int * 1000;
  351.     var_Get( p_stream, SOUT_CFG_PREFIX_IN "name", &val );
  352.     if( asprintf( &p_sys->psz_name, "bridge-struct-%s", val.psz_string )<0 )
  353.     {
  354.         free( val.psz_string );
  355.         free( p_sys );
  356.         return VLC_ENOMEM;
  357.     }
  358.     free( val.psz_string );
  359.     var_Get( p_stream, SOUT_CFG_PREFIX_IN "placeholder", &val );
  360.     p_sys->b_placeholder = val.b_bool;
  361.     var_Get( p_stream, SOUT_CFG_PREFIX_IN "placeholder-switch-on-iframe", &val);
  362.     p_sys->b_switch_on_iframe = val.b_bool;
  363.     p_sys->i_state = placeholder_on;
  364.     var_Get( p_stream, SOUT_CFG_PREFIX_IN "placeholder-delay", &val );
  365.     p_sys->i_placeholder_delay = (mtime_t)val.i_int * 1000;
  366.     p_sys->i_last_video = 0;
  367.     p_sys->i_last_audio = 0;
  368.     p_sys->id_video = NULL;
  369.     p_sys->id_audio = NULL;
  370.     p_stream->pf_add    = AddIn;
  371.     p_stream->pf_del    = DelIn;
  372.     p_stream->pf_send   = SendIn;
  373.     p_stream->p_sys     = (sout_stream_sys_t *)p_sys;
  374.     /* update p_sout->i_out_pace_nocontrol */
  375.     p_stream->p_sout->i_out_pace_nocontrol++;
  376.     return VLC_SUCCESS;
  377. }
  378. /*****************************************************************************
  379.  * CloseIn:
  380.  *****************************************************************************/
  381. static void CloseIn( vlc_object_t * p_this )
  382. {
  383.     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
  384.     in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
  385.     sout_StreamDelete( p_sys->p_out );
  386.     p_stream->p_sout->i_out_pace_nocontrol--;
  387.     free( p_sys->psz_name );
  388.     free( p_sys );
  389. }
  390. struct sout_stream_id_t
  391. {
  392.     sout_stream_id_t *id;
  393.     int i_cat; /* es category. Used for placeholder option */
  394. };
  395. static sout_stream_id_t * AddIn( sout_stream_t *p_stream, es_format_t *p_fmt )
  396. {
  397.     in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
  398.     sout_stream_id_t *id = malloc( sizeof( sout_stream_id_t ) );
  399.     if( !id ) return NULL;
  400.     id->id = p_sys->p_out->pf_add( p_sys->p_out, p_fmt );
  401.     if( !id->id )
  402.     {
  403.         free( id );
  404.         return NULL;
  405.     }
  406.     if( p_sys->b_placeholder )
  407.     {
  408.         id->i_cat = p_fmt->i_cat;
  409.         switch( p_fmt->i_cat )
  410.         {
  411.             case VIDEO_ES:
  412.                 if( p_sys->id_video != NULL )
  413.                     msg_Err( p_stream, "We already had a video es!" );
  414.                 p_sys->id_video = id->id;
  415.                 break;
  416.             case AUDIO_ES:
  417.                 if( p_sys->id_audio != NULL )
  418.                     msg_Err( p_stream, "We already had an audio es!" );
  419.                 p_sys->id_audio = id->id;
  420.                 break;
  421.         }
  422.     }
  423.     return id;
  424. }
  425. static int DelIn( sout_stream_t *p_stream, sout_stream_id_t *id )
  426. {
  427.     in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
  428.     if( id == p_sys->id_video ) p_sys->id_video = NULL;
  429.     if( id == p_sys->id_audio ) p_sys->id_audio = NULL;
  430.     int ret = p_sys->p_out->pf_del( p_sys->p_out, id->id );
  431.     free( id );
  432.     return ret;
  433. }
  434. static int SendIn( sout_stream_t *p_stream, sout_stream_id_t *id,
  435.                    block_t *p_buffer )
  436. {
  437.     in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
  438.     bridge_t *p_bridge;
  439.     bool b_no_es = true;
  440.     int i;
  441.     int i_date = mdate();
  442.     /* First forward the packet for our own ES */
  443.     if( !p_sys->b_placeholder )
  444.         p_sys->p_out->pf_send( p_sys->p_out, id->id, p_buffer );
  445.     /* Then check all bridged streams */
  446.     vlc_mutex_lock( p_sys->p_lock );
  447.     p_bridge = GetBridge( p_stream, p_sys->psz_name );
  448.     if( p_bridge )
  449.     {
  450.     for ( i = 0; i < p_bridge->i_es_num; i++ )
  451.     {
  452.         if ( !p_bridge->pp_es[i]->b_empty )
  453.             b_no_es = false;
  454.         while ( p_bridge->pp_es[i]->p_block != NULL
  455.                  && (p_bridge->pp_es[i]->p_block->i_dts + p_sys->i_delay
  456.                        < i_date
  457.                       || p_bridge->pp_es[i]->p_block->i_dts + p_sys->i_delay
  458.                           < p_bridge->pp_es[i]->i_last) )
  459.         {
  460.             block_t *p_block = p_bridge->pp_es[i]->p_block;
  461.             msg_Dbg( p_stream, "dropping a packet (%"PRId64 ")",
  462.                      i_date - p_block->i_dts - p_sys->i_delay );
  463.             p_bridge->pp_es[i]->p_block
  464.                 = p_bridge->pp_es[i]->p_block->p_next;
  465.             block_Release( p_block );
  466.         }
  467.         if ( p_bridge->pp_es[i]->p_block == NULL )
  468.         {
  469.             p_bridge->pp_es[i]->pp_last = &p_bridge->pp_es[i]->p_block;
  470.         }
  471.         if ( p_bridge->pp_es[i]->b_changed )
  472.         {
  473.             if ( p_bridge->pp_es[i]->b_empty && p_bridge->pp_es[i]->id != NULL )
  474.             {
  475.                 p_sys->p_out->pf_del( p_sys->p_out, p_bridge->pp_es[i]->id );
  476.             }
  477.             else
  478.             {
  479.                 /* We need at least two packets to enter the mux. */
  480.                 if ( p_bridge->pp_es[i]->p_block == NULL
  481.                       || p_bridge->pp_es[i]->p_block->p_next == NULL )
  482.                 {
  483.                     continue;
  484.                 }
  485.                 p_bridge->pp_es[i]->fmt.i_id += p_sys->i_id_offset;
  486.                 if( !p_sys->b_placeholder )
  487.                 {
  488.                     p_bridge->pp_es[i]->id = p_sys->p_out->pf_add(
  489.                                 p_sys->p_out, &p_bridge->pp_es[i]->fmt );
  490.                     if ( p_bridge->pp_es[i]->id == NULL )
  491.                     {
  492.                         msg_Warn( p_stream, "couldn't create chain for id %d",
  493.                                   p_bridge->pp_es[i]->fmt.i_id );
  494.                     }
  495.                 }
  496.                 msg_Dbg( p_stream, "bridging in input codec=%4.4s id=%d pos=%d",
  497.                          (char*)&p_bridge->pp_es[i]->fmt.i_codec,
  498.                          p_bridge->pp_es[i]->fmt.i_id, i );
  499.             }
  500.         }
  501.         p_bridge->pp_es[i]->b_changed = false;
  502.         if ( p_bridge->pp_es[i]->b_empty )
  503.             continue;
  504.         if ( p_bridge->pp_es[i]->p_block == NULL )
  505.         {
  506.             if ( p_bridge->pp_es[i]->id != NULL
  507.                   && p_bridge->pp_es[i]->i_last < i_date )
  508.             {
  509.                 if( !p_sys->b_placeholder )
  510.                     p_sys->p_out->pf_del( p_sys->p_out,
  511.                                           p_bridge->pp_es[i]->id );
  512.                 p_bridge->pp_es[i]->fmt.i_id -= p_sys->i_id_offset;
  513.                 p_bridge->pp_es[i]->b_changed = true;
  514.                 p_bridge->pp_es[i]->id = NULL;
  515.             }
  516.             continue;
  517.         }
  518.         if ( p_bridge->pp_es[i]->id != NULL || p_sys->b_placeholder)
  519.         {
  520.             block_t *p_block = p_bridge->pp_es[i]->p_block;
  521.             while ( p_block != NULL )
  522.             {
  523.                 p_bridge->pp_es[i]->i_last = p_block->i_dts;
  524.                 p_block->i_pts += p_sys->i_delay;
  525.                 p_block->i_dts += p_sys->i_delay;
  526.                 p_block = p_block->p_next;
  527.             }
  528.             sout_stream_id_t *newid = NULL;
  529.             if( p_sys->b_placeholder )
  530.             {
  531.                 switch( p_bridge->pp_es[i]->fmt.i_cat )
  532.                 {
  533.                     case VIDEO_ES:
  534.                         p_sys->i_last_video = i_date;
  535.                         newid = p_sys->id_video;
  536.                         if( !newid )
  537.                             break;
  538.                         if( !p_sys->b_switch_on_iframe ||
  539.                             p_sys->i_state == placeholder_off ||
  540.                             ( p_bridge->pp_es[i]->fmt.i_cat == VIDEO_ES &&
  541.                               p_bridge->pp_es[i]->p_block->i_flags & BLOCK_FLAG_TYPE_I ) )
  542.                         {
  543.                             p_sys->p_out->pf_send( p_sys->p_out,
  544.                                        newid,
  545.                                        p_bridge->pp_es[i]->p_block );
  546.                             p_sys->i_state = placeholder_off;
  547.                         }
  548.                         break;
  549.                     case AUDIO_ES:
  550.                         newid = p_sys->id_audio;
  551.                         if( !newid )
  552.                             break;
  553.                         p_sys->i_last_audio = i_date;
  554.                     default:
  555.                         p_sys->p_out->pf_send( p_sys->p_out,
  556.                                    newid?newid:p_bridge->pp_es[i]->id,
  557.                                    p_bridge->pp_es[i]->p_block );
  558.                         break;
  559.                 }
  560.             }
  561.             else /* !b_placeholder */
  562.                 p_sys->p_out->pf_send( p_sys->p_out,
  563.                                        p_bridge->pp_es[i]->id,
  564.                                        p_bridge->pp_es[i]->p_block );
  565.         }
  566.         else
  567.         {
  568.             block_ChainRelease( p_bridge->pp_es[i]->p_block );
  569.         }
  570.         p_bridge->pp_es[i]->p_block = NULL;
  571.         p_bridge->pp_es[i]->pp_last = &p_bridge->pp_es[i]->p_block;
  572.     }
  573.     if( b_no_es )
  574.     {
  575.         vlc_object_t *p_libvlc = VLC_OBJECT( p_stream->p_libvlc );
  576.         for ( i = 0; i < p_bridge->i_es_num; i++ )
  577.             free( p_bridge->pp_es[i] );
  578.         free( p_bridge->pp_es );
  579.         free( p_bridge );
  580.         var_Destroy( p_libvlc, p_sys->psz_name );
  581.     }
  582.     }
  583.     if( p_sys->b_placeholder )
  584.     {
  585.         switch( id->i_cat )
  586.         {
  587.             case VIDEO_ES:
  588.                 if( ( p_sys->i_last_video + p_sys->i_placeholder_delay < i_date
  589.                     && (  !p_sys->b_switch_on_iframe
  590.                        || p_buffer->i_flags & BLOCK_FLAG_TYPE_I ) )
  591.                   || p_sys->i_state == placeholder_on )
  592.                 {
  593.                     p_sys->p_out->pf_send( p_sys->p_out, id->id, p_buffer );
  594.                     p_sys->i_state = placeholder_on;
  595.                 }
  596.                 else
  597.                     block_Release( p_buffer );
  598.                 break;
  599.             case AUDIO_ES:
  600.                 if( p_sys->i_last_audio + p_sys->i_placeholder_delay < i_date )
  601.                     p_sys->p_out->pf_send( p_sys->p_out, id->id, p_buffer );
  602.                 else
  603.                     block_Release( p_buffer );
  604.                 break;
  605.             default:
  606.                 block_Release( p_buffer ); /* FIXME: placeholder subs anyone? */
  607.                 break;
  608.         }
  609.     }
  610.     vlc_mutex_unlock( p_sys->p_lock );
  611.     return VLC_SUCCESS;
  612. }