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