1 /*****************************************************************************
2 * bridge.c: bridge stream output module
3 *****************************************************************************
4 * Copyright (C) 2005 the VideoLAN team
7 * Authors: Christophe Massiot <massiot@via.ecp.fr>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
35 #include <vlc_block.h>
37 /*****************************************************************************
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." )
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." )
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." )
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.")
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." )
64 static int OpenOut ( vlc_object_t * );
65 static void CloseOut( vlc_object_t * );
66 static int OpenIn ( vlc_object_t * );
67 static void CloseIn ( vlc_object_t * );
69 #define SOUT_CFG_PREFIX_OUT "sout-bridge-out-"
70 #define SOUT_CFG_PREFIX_IN "sout-bridge-in-"
73 set_shortname( N_("Bridge"));
74 set_description( N_("Bridge stream output"));
76 set_section( N_("Bridge out"), NULL );
77 set_capability( "sout stream", 50 );
78 add_shortcut( "bridge-out" );
79 /* Only usable with VLM. No category so not in gui preferences
80 set_category( CAT_SOUT );
81 set_subcategory( SUBCAT_SOUT_STREAM );*/
82 add_integer( SOUT_CFG_PREFIX_OUT "id", 0, NULL, ID_TEXT, ID_LONGTEXT,
84 add_string( SOUT_CFG_PREFIX_OUT "in-name", "default", NULL,
85 DEST_TEXT, DEST_LONGTEXT, false );
86 set_callbacks( OpenOut, CloseOut );
89 set_section( N_("Bridge in"), NULL );
90 set_capability( "sout stream", 50 );
91 add_shortcut( "bridge-in" );
92 /*set_category( CAT_SOUT );
93 set_subcategory( SUBCAT_SOUT_STREAM );*/
94 add_integer( SOUT_CFG_PREFIX_IN "delay", 0, NULL, DELAY_TEXT,
95 DELAY_LONGTEXT, false );
96 add_integer( SOUT_CFG_PREFIX_IN "id-offset", 8192, NULL, ID_OFFSET_TEXT,
97 ID_OFFSET_LONGTEXT, false );
98 add_string( SOUT_CFG_PREFIX_IN "name", "default", NULL,
99 NAME_TEXT, NAME_LONGTEXT, false );
100 set_callbacks( OpenIn, CloseIn );
105 /*****************************************************************************
107 *****************************************************************************/
108 static const char *const ppsz_sout_options_out[] = {
109 "id", "in-name", NULL
112 static const char *const ppsz_sout_options_in[] = {
113 "delay", "id-offset", "name", NULL
116 static sout_stream_id_t *AddOut ( sout_stream_t *, es_format_t * );
117 static int DelOut ( sout_stream_t *, sout_stream_id_t * );
118 static int SendOut( sout_stream_t *, sout_stream_id_t *, block_t * );
120 static sout_stream_id_t *AddIn ( sout_stream_t *, es_format_t * );
121 static int DelIn ( sout_stream_t *, sout_stream_id_t * );
122 static int SendIn( sout_stream_t *, sout_stream_id_t *, block_t * );
124 typedef struct bridged_es_t
132 sout_stream_id_t *id;
137 typedef struct bridge_t
139 bridged_es_t **pp_es;
143 #define GetBridge(a,b) __GetBridge( VLC_OBJECT(a), b )
144 static bridge_t *__GetBridge( vlc_object_t *p_object, const char *psz_name )
149 if( var_Get( p_object->p_libvlc, psz_name, &val ) )
155 p_bridge = val.p_address;
166 typedef struct out_sout_stream_sys_t
174 } out_sout_stream_sys_t;
176 /*****************************************************************************
178 *****************************************************************************/
179 static int OpenOut( vlc_object_t *p_this )
181 sout_stream_t *p_stream = (sout_stream_t *)p_this;
182 out_sout_stream_sys_t *p_sys;
185 config_ChainParse( p_stream, SOUT_CFG_PREFIX_OUT, ppsz_sout_options_out,
188 p_sys = malloc( sizeof( out_sout_stream_sys_t ) );
189 p_sys->b_inited = false;
191 var_Create( p_this->p_libvlc, "bridge-lock", VLC_VAR_MUTEX );
192 var_Get( p_this->p_libvlc, "bridge-lock", &val );
193 p_sys->p_lock = val.p_address;
195 var_Get( p_stream, SOUT_CFG_PREFIX_OUT "id", &val );
196 p_sys->i_id = val.i_int;
198 var_Get( p_stream, SOUT_CFG_PREFIX_OUT "in-name", &val );
199 if( asprintf( &p_sys->psz_name, "bridge-struct-%s", val.psz_string )<0 )
201 free( val.psz_string );
205 free( val.psz_string );
207 p_stream->pf_add = AddOut;
208 p_stream->pf_del = DelOut;
209 p_stream->pf_send = SendOut;
211 p_stream->p_sys = (sout_stream_sys_t *)p_sys;
213 p_stream->p_sout->i_out_pace_nocontrol++;
218 /*****************************************************************************
220 *****************************************************************************/
221 static void CloseOut( vlc_object_t * p_this )
223 sout_stream_t *p_stream = (sout_stream_t*)p_this;
224 out_sout_stream_sys_t *p_sys = (out_sout_stream_sys_t *)p_stream->p_sys;
226 p_stream->p_sout->i_out_pace_nocontrol--;
228 free( p_sys->psz_name );
232 static sout_stream_id_t * AddOut( sout_stream_t *p_stream, es_format_t *p_fmt )
234 out_sout_stream_sys_t *p_sys = (out_sout_stream_sys_t *)p_stream->p_sys;
239 if ( p_sys->b_inited )
243 p_sys->b_inited = true;
245 vlc_mutex_lock( p_sys->p_lock );
247 p_bridge = GetBridge( p_stream, p_sys->psz_name );
248 if ( p_bridge == NULL )
250 vlc_object_t *p_libvlc = VLC_OBJECT( p_stream->p_libvlc );
253 p_bridge = malloc( sizeof( bridge_t ) );
255 var_Create( p_libvlc, p_sys->psz_name, VLC_VAR_ADDRESS );
256 val.p_address = p_bridge;
257 var_Set( p_libvlc, p_sys->psz_name, val );
259 p_bridge->i_es_num = 0;
260 p_bridge->pp_es = NULL;
263 for ( i = 0; i < p_bridge->i_es_num; i++ )
265 if ( p_bridge->pp_es[i]->b_empty && !p_bridge->pp_es[i]->b_changed )
269 if ( i == p_bridge->i_es_num )
271 p_bridge->pp_es = realloc( p_bridge->pp_es,
272 (p_bridge->i_es_num + 1)
273 * sizeof(bridged_es_t *) );
274 p_bridge->i_es_num++;
275 p_bridge->pp_es[i] = malloc( sizeof(bridged_es_t) );
278 p_sys->p_es = p_es = p_bridge->pp_es[i];
281 p_es->fmt.i_id = p_sys->i_id;
282 p_es->p_block = NULL;
283 p_es->pp_last = &p_es->p_block;
284 p_es->b_empty = false;
288 p_es->b_changed = true;
290 msg_Dbg( p_stream, "bridging out input codec=%4.4s id=%d pos=%d",
291 (char*)&p_es->fmt.i_codec, p_es->fmt.i_id, i );
293 vlc_mutex_unlock( p_sys->p_lock );
295 return (sout_stream_id_t *)p_sys;
298 static int DelOut( sout_stream_t *p_stream, sout_stream_id_t *id )
301 out_sout_stream_sys_t *p_sys = (out_sout_stream_sys_t *)p_stream->p_sys;
304 if ( !p_sys->b_inited )
309 vlc_mutex_lock( p_sys->p_lock );
313 p_es->b_empty = true;
314 block_ChainRelease( p_es->p_block );
315 p_es->p_block = false;
317 p_es->b_changed = true;
318 vlc_mutex_unlock( p_sys->p_lock );
320 p_sys->b_inited = false;
325 static int SendOut( sout_stream_t *p_stream, sout_stream_id_t *id,
328 out_sout_stream_sys_t *p_sys = (out_sout_stream_sys_t *)p_stream->p_sys;
331 if ( (out_sout_stream_sys_t *)id != p_sys )
333 block_ChainRelease( p_buffer );
337 vlc_mutex_lock( p_sys->p_lock );
340 *p_es->pp_last = p_buffer;
341 while ( p_buffer != NULL )
343 p_es->pp_last = &p_buffer->p_next;
344 p_buffer = p_buffer->p_next;
347 vlc_mutex_unlock( p_sys->p_lock );
357 typedef struct in_sout_stream_sys_t
359 sout_stream_t *p_out;
365 } in_sout_stream_sys_t;
367 /*****************************************************************************
369 *****************************************************************************/
370 static int OpenIn( vlc_object_t *p_this )
372 sout_stream_t *p_stream = (sout_stream_t*)p_this;
373 in_sout_stream_sys_t *p_sys;
376 p_sys = malloc( sizeof( in_sout_stream_sys_t ) );
378 p_sys->p_out = sout_StreamNew( p_stream->p_sout, p_stream->psz_next );
381 msg_Err( p_stream, "cannot create chain" );
386 config_ChainParse( p_stream, SOUT_CFG_PREFIX_IN, ppsz_sout_options_in,
389 var_Create( p_this->p_libvlc, "bridge-lock", VLC_VAR_MUTEX );
390 var_Get( p_this->p_libvlc, "bridge-lock", &val );
391 p_sys->p_lock = val.p_address;
393 var_Get( p_stream, SOUT_CFG_PREFIX_IN "id-offset", &val );
394 p_sys->i_id_offset = val.i_int;
396 var_Get( p_stream, SOUT_CFG_PREFIX_IN "delay", &val );
397 p_sys->i_delay = (mtime_t)val.i_int * 1000;
399 var_Get( p_stream, SOUT_CFG_PREFIX_IN "name", &val );
400 if( asprintf( &p_sys->psz_name, "bridge-struct-%s", val.psz_string )<0 )
402 free( val.psz_string );
406 free( val.psz_string );
408 p_stream->pf_add = AddIn;
409 p_stream->pf_del = DelIn;
410 p_stream->pf_send = SendIn;
412 p_stream->p_sys = (sout_stream_sys_t *)p_sys;
414 /* update p_sout->i_out_pace_nocontrol */
415 p_stream->p_sout->i_out_pace_nocontrol++;
420 /*****************************************************************************
422 *****************************************************************************/
423 static void CloseIn( vlc_object_t * p_this )
425 sout_stream_t *p_stream = (sout_stream_t*)p_this;
426 in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
428 sout_StreamDelete( p_sys->p_out );
429 p_stream->p_sout->i_out_pace_nocontrol--;
431 free( p_sys->psz_name );
435 static sout_stream_id_t * AddIn( sout_stream_t *p_stream, es_format_t *p_fmt )
437 in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
439 return p_sys->p_out->pf_add( p_sys->p_out, p_fmt );
442 static int DelIn( sout_stream_t *p_stream, sout_stream_id_t *id )
444 in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
446 return p_sys->p_out->pf_del( p_sys->p_out, id );
449 static int SendIn( sout_stream_t *p_stream, sout_stream_id_t *id,
452 in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
457 /* First forward the packet for our own ES */
458 p_sys->p_out->pf_send( p_sys->p_out, id, p_buffer );
460 /* Then check all bridged streams */
461 vlc_mutex_lock( p_sys->p_lock );
463 p_bridge = GetBridge( p_stream, p_sys->psz_name );
464 if ( p_bridge == NULL )
466 vlc_mutex_unlock( p_sys->p_lock );
470 for ( i = 0; i < p_bridge->i_es_num; i++ )
472 if ( !p_bridge->pp_es[i]->b_empty )
475 while ( p_bridge->pp_es[i]->p_block != NULL
476 && (p_bridge->pp_es[i]->p_block->i_dts + p_sys->i_delay
478 || p_bridge->pp_es[i]->p_block->i_dts + p_sys->i_delay
479 < p_bridge->pp_es[i]->i_last) )
481 block_t *p_block = p_bridge->pp_es[i]->p_block;
482 msg_Dbg( p_stream, "dropping a packet (%"PRId64 ")",
483 mdate() - p_block->i_dts - p_sys->i_delay );
484 p_bridge->pp_es[i]->p_block
485 = p_bridge->pp_es[i]->p_block->p_next;
486 block_Release( p_block );
489 if ( p_bridge->pp_es[i]->p_block == NULL )
491 p_bridge->pp_es[i]->pp_last = &p_bridge->pp_es[i]->p_block;
494 if ( p_bridge->pp_es[i]->b_changed )
496 if ( p_bridge->pp_es[i]->b_empty && p_bridge->pp_es[i]->id != NULL )
498 p_sys->p_out->pf_del( p_sys->p_out, p_bridge->pp_es[i]->id );
502 /* We need at least two packets to enter the mux. */
503 if ( p_bridge->pp_es[i]->p_block == NULL
504 || p_bridge->pp_es[i]->p_block->p_next == NULL )
509 p_bridge->pp_es[i]->fmt.i_id += p_sys->i_id_offset;
510 p_bridge->pp_es[i]->id = p_sys->p_out->pf_add(
511 p_sys->p_out, &p_bridge->pp_es[i]->fmt );
512 if ( p_bridge->pp_es[i]->id == NULL )
514 msg_Warn( p_stream, "couldn't create chain for id %d",
515 p_bridge->pp_es[i]->fmt.i_id );
517 msg_Dbg( p_stream, "bridging in input codec=%4.4s id=%d pos=%d",
518 (char*)&p_bridge->pp_es[i]->fmt.i_codec,
519 p_bridge->pp_es[i]->fmt.i_id, i );
522 p_bridge->pp_es[i]->b_changed = false;
524 if ( p_bridge->pp_es[i]->b_empty )
527 if ( p_bridge->pp_es[i]->p_block == NULL )
529 if ( p_bridge->pp_es[i]->id != NULL
530 && p_bridge->pp_es[i]->i_last < mdate() )
532 p_sys->p_out->pf_del( p_sys->p_out, p_bridge->pp_es[i]->id );
533 p_bridge->pp_es[i]->fmt.i_id -= p_sys->i_id_offset;
534 p_bridge->pp_es[i]->b_changed = true;
535 p_bridge->pp_es[i]->id = NULL;
540 if ( p_bridge->pp_es[i]->id != NULL )
542 block_t *p_block = p_bridge->pp_es[i]->p_block;
543 while ( p_block != NULL )
545 p_bridge->pp_es[i]->i_last = p_block->i_dts;
546 p_block->i_pts += p_sys->i_delay;
547 p_block->i_dts += p_sys->i_delay;
548 p_block = p_block->p_next;
550 p_sys->p_out->pf_send( p_sys->p_out, p_bridge->pp_es[i]->id,
551 p_bridge->pp_es[i]->p_block );
555 block_ChainRelease( p_bridge->pp_es[i]->p_block );
558 p_bridge->pp_es[i]->p_block = NULL;
559 p_bridge->pp_es[i]->pp_last = &p_bridge->pp_es[i]->p_block;
564 vlc_object_t *p_libvlc = VLC_OBJECT( p_stream->p_libvlc );
565 for ( i = 0; i < p_bridge->i_es_num; i++ )
566 free( p_bridge->pp_es[i] );
567 free( p_bridge->pp_es );
569 var_Destroy( p_libvlc, p_sys->psz_name );
572 vlc_mutex_unlock( p_sys->p_lock );