]> git.sesse.net Git - vlc/blob - modules/stream_out/bridge.c
Make it possible to have more than one bridge in a VLC instance
[vlc] / modules / stream_out / bridge.c
1 /*****************************************************************************
2  * bridge.c: bridge stream output module
3  *****************************************************************************
4  * Copyright (C) 2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
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 /*****************************************************************************
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 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 * );
68
69 #define SOUT_CFG_PREFIX_OUT "sout-bridge-out-"
70 #define SOUT_CFG_PREFIX_IN "sout-bridge-in-"
71
72 vlc_module_begin();
73     set_shortname( N_("Bridge"));
74     set_description( N_("Bridge stream output"));
75     add_submodule();
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,
83                  false );
84     add_string( SOUT_CFG_PREFIX_OUT "in-name", "default", NULL,
85                 DEST_TEXT, DEST_LONGTEXT, false );
86     set_callbacks( OpenOut, CloseOut );
87
88     add_submodule();
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 );
101
102 vlc_module_end();
103
104
105 /*****************************************************************************
106  * Local prototypes
107  *****************************************************************************/
108 static const char *const ppsz_sout_options_out[] = {
109     "id", "in-name", NULL
110 };
111
112 static const char *const ppsz_sout_options_in[] = {
113     "delay", "id-offset", "name", NULL
114 };
115
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 * );
119
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 * );
123
124 typedef struct bridged_es_t
125 {
126     es_format_t fmt;
127     block_t *p_block;
128     block_t **pp_last;
129     bool b_empty;
130
131     /* bridge in part */
132     sout_stream_id_t *id;
133     mtime_t i_last;
134     bool b_changed;
135 } bridged_es_t;
136
137 typedef struct bridge_t
138 {
139     bridged_es_t **pp_es;
140     int i_es_num;
141 } bridge_t;
142
143 #define GetBridge(a,b) __GetBridge( VLC_OBJECT(a), b )
144 static bridge_t *__GetBridge( vlc_object_t *p_object, const char *psz_name )
145 {
146     bridge_t *p_bridge;
147     vlc_value_t val;
148
149     if( var_Get( p_object->p_libvlc, psz_name, &val ) )
150     {
151         p_bridge = NULL;
152     }
153     else
154     {
155         p_bridge = val.p_address;
156     }
157
158     return p_bridge;
159 }
160
161
162 /*
163  * Bridge out
164  */
165
166 typedef struct out_sout_stream_sys_t
167 {
168     vlc_mutex_t *p_lock;
169     bridged_es_t *p_es;
170     int i_id;
171     bool b_inited;
172
173     char *psz_name;
174 } out_sout_stream_sys_t;
175
176 /*****************************************************************************
177  * OpenOut:
178  *****************************************************************************/
179 static int OpenOut( vlc_object_t *p_this )
180 {
181     sout_stream_t     *p_stream = (sout_stream_t *)p_this;
182     out_sout_stream_sys_t *p_sys;
183     vlc_value_t val;
184
185     config_ChainParse( p_stream, SOUT_CFG_PREFIX_OUT, ppsz_sout_options_out,
186                    p_stream->p_cfg );
187
188     p_sys          = malloc( sizeof( out_sout_stream_sys_t ) );
189     p_sys->b_inited = false;
190
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;
194
195     var_Get( p_stream, SOUT_CFG_PREFIX_OUT "id", &val );
196     p_sys->i_id = val.i_int;
197
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 )
200     {
201         free( val.psz_string );
202         free( p_sys );
203         return VLC_ENOMEM;
204     }
205     free( val.psz_string );
206
207     p_stream->pf_add    = AddOut;
208     p_stream->pf_del    = DelOut;
209     p_stream->pf_send   = SendOut;
210
211     p_stream->p_sys     = (sout_stream_sys_t *)p_sys;
212
213     p_stream->p_sout->i_out_pace_nocontrol++;
214
215     return VLC_SUCCESS;
216 }
217
218 /*****************************************************************************
219  * CloseOut:
220  *****************************************************************************/
221 static void CloseOut( vlc_object_t * p_this )
222 {
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;
225
226     p_stream->p_sout->i_out_pace_nocontrol--;
227
228     free( p_sys->psz_name );
229     free( p_sys );
230 }
231
232 static sout_stream_id_t * AddOut( sout_stream_t *p_stream, es_format_t *p_fmt )
233 {
234     out_sout_stream_sys_t *p_sys = (out_sout_stream_sys_t *)p_stream->p_sys;
235     bridge_t *p_bridge;
236     bridged_es_t *p_es;
237     int i;
238
239     if ( p_sys->b_inited )
240     {
241         return NULL;
242     }
243     p_sys->b_inited = true;
244
245     vlc_mutex_lock( p_sys->p_lock );
246
247     p_bridge = GetBridge( p_stream, p_sys->psz_name );
248     if ( p_bridge == NULL )
249     {
250         vlc_object_t *p_libvlc = VLC_OBJECT( p_stream->p_libvlc );
251         vlc_value_t val;
252
253         p_bridge = malloc( sizeof( bridge_t ) );
254
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 );
258
259         p_bridge->i_es_num = 0;
260         p_bridge->pp_es = NULL;
261     }
262
263     for ( i = 0; i < p_bridge->i_es_num; i++ )
264     {
265         if ( p_bridge->pp_es[i]->b_empty && !p_bridge->pp_es[i]->b_changed )
266             break;
267     }
268
269     if ( i == p_bridge->i_es_num )
270     {
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) );
276     }
277
278     p_sys->p_es = p_es = p_bridge->pp_es[i];
279
280     p_es->fmt = *p_fmt;
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;
285
286     p_es->id = NULL;
287     p_es->i_last = 0;
288     p_es->b_changed = true;
289
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 );
292
293     vlc_mutex_unlock( p_sys->p_lock );
294
295     return (sout_stream_id_t *)p_sys;
296 }
297
298 static int DelOut( sout_stream_t *p_stream, sout_stream_id_t *id )
299 {
300     VLC_UNUSED(id);
301     out_sout_stream_sys_t *p_sys = (out_sout_stream_sys_t *)p_stream->p_sys;
302     bridged_es_t *p_es;
303
304     if ( !p_sys->b_inited )
305     {
306         return VLC_SUCCESS;
307     }
308
309     vlc_mutex_lock( p_sys->p_lock );
310
311     p_es = p_sys->p_es;
312
313     p_es->b_empty = true;
314     block_ChainRelease( p_es->p_block );
315     p_es->p_block = false;
316
317     p_es->b_changed = true;
318     vlc_mutex_unlock( p_sys->p_lock );
319
320     p_sys->b_inited = false;
321
322     return VLC_SUCCESS;
323 }
324
325 static int SendOut( sout_stream_t *p_stream, sout_stream_id_t *id,
326                     block_t *p_buffer )
327 {
328     out_sout_stream_sys_t *p_sys = (out_sout_stream_sys_t *)p_stream->p_sys;
329     bridged_es_t *p_es;
330
331     if ( (out_sout_stream_sys_t *)id != p_sys )
332     {
333         block_ChainRelease( p_buffer );
334         return VLC_SUCCESS;
335     }
336
337     vlc_mutex_lock( p_sys->p_lock );
338
339     p_es = p_sys->p_es;
340     *p_es->pp_last = p_buffer;
341     while ( p_buffer != NULL )
342     {
343         p_es->pp_last = &p_buffer->p_next;
344         p_buffer = p_buffer->p_next;
345     }
346
347     vlc_mutex_unlock( p_sys->p_lock );
348
349     return VLC_SUCCESS;
350 }
351
352
353 /*
354  * Bridge in
355  */
356
357 typedef struct in_sout_stream_sys_t
358 {
359     sout_stream_t *p_out;
360     vlc_mutex_t *p_lock;
361     int i_id_offset;
362     mtime_t i_delay;
363
364     char *psz_name;
365 } in_sout_stream_sys_t;
366
367 /*****************************************************************************
368  * OpenIn:
369  *****************************************************************************/
370 static int OpenIn( vlc_object_t *p_this )
371 {
372     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
373     in_sout_stream_sys_t *p_sys;
374     vlc_value_t val;
375
376     p_sys          = malloc( sizeof( in_sout_stream_sys_t ) );
377
378     p_sys->p_out = sout_StreamNew( p_stream->p_sout, p_stream->psz_next );
379     if( !p_sys->p_out )
380     {
381         msg_Err( p_stream, "cannot create chain" );
382         free( p_sys );
383         return VLC_EGENERIC;
384     }
385
386     config_ChainParse( p_stream, SOUT_CFG_PREFIX_IN, ppsz_sout_options_in,
387                    p_stream->p_cfg );
388
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;
392
393     var_Get( p_stream, SOUT_CFG_PREFIX_IN "id-offset", &val );
394     p_sys->i_id_offset = val.i_int;
395
396     var_Get( p_stream, SOUT_CFG_PREFIX_IN "delay", &val );
397     p_sys->i_delay = (mtime_t)val.i_int * 1000;
398
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 )
401     {
402         free( val.psz_string );
403         free( p_sys );
404         return VLC_ENOMEM;
405     }
406     free( val.psz_string );
407
408     p_stream->pf_add    = AddIn;
409     p_stream->pf_del    = DelIn;
410     p_stream->pf_send   = SendIn;
411
412     p_stream->p_sys     = (sout_stream_sys_t *)p_sys;
413
414     /* update p_sout->i_out_pace_nocontrol */
415     p_stream->p_sout->i_out_pace_nocontrol++;
416
417     return VLC_SUCCESS;
418 }
419
420 /*****************************************************************************
421  * CloseIn:
422  *****************************************************************************/
423 static void CloseIn( vlc_object_t * p_this )
424 {
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;
427
428     sout_StreamDelete( p_sys->p_out );
429     p_stream->p_sout->i_out_pace_nocontrol--;
430
431     free( p_sys->psz_name );
432     free( p_sys );
433 }
434
435 static sout_stream_id_t * AddIn( sout_stream_t *p_stream, es_format_t *p_fmt )
436 {
437     in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
438
439     return p_sys->p_out->pf_add( p_sys->p_out, p_fmt );
440 }
441
442 static int DelIn( sout_stream_t *p_stream, sout_stream_id_t *id )
443 {
444     in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
445
446     return p_sys->p_out->pf_del( p_sys->p_out, id );
447 }
448
449 static int SendIn( sout_stream_t *p_stream, sout_stream_id_t *id,
450                    block_t *p_buffer )
451 {
452     in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
453     bridge_t *p_bridge;
454     bool b_no_es = true;
455     int i;
456
457     /* First forward the packet for our own ES */
458     p_sys->p_out->pf_send( p_sys->p_out, id, p_buffer );
459
460     /* Then check all bridged streams */
461     vlc_mutex_lock( p_sys->p_lock );
462
463     p_bridge = GetBridge( p_stream, p_sys->psz_name );
464     if ( p_bridge == NULL )
465     {
466         vlc_mutex_unlock( p_sys->p_lock );
467         return VLC_SUCCESS;
468     }
469
470     for ( i = 0; i < p_bridge->i_es_num; i++ )
471     {
472         if ( !p_bridge->pp_es[i]->b_empty )
473             b_no_es = false;
474
475         while ( p_bridge->pp_es[i]->p_block != NULL
476                  && (p_bridge->pp_es[i]->p_block->i_dts + p_sys->i_delay
477                        < mdate()
478                       || p_bridge->pp_es[i]->p_block->i_dts + p_sys->i_delay
479                           < p_bridge->pp_es[i]->i_last) )
480         {
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 );
487         }
488
489         if ( p_bridge->pp_es[i]->p_block == NULL )
490         {
491             p_bridge->pp_es[i]->pp_last = &p_bridge->pp_es[i]->p_block;
492         }
493
494         if ( p_bridge->pp_es[i]->b_changed )
495         {
496             if ( p_bridge->pp_es[i]->b_empty && p_bridge->pp_es[i]->id != NULL )
497             {
498                 p_sys->p_out->pf_del( p_sys->p_out, p_bridge->pp_es[i]->id );
499             }
500             else
501             {
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 )
505                 {
506                     continue;
507                 }
508
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 )
513                 {
514                     msg_Warn( p_stream, "couldn't create chain for id %d",
515                               p_bridge->pp_es[i]->fmt.i_id );
516                 }
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 );
520             }
521         }
522         p_bridge->pp_es[i]->b_changed = false;
523
524         if ( p_bridge->pp_es[i]->b_empty )
525             continue;
526
527         if ( p_bridge->pp_es[i]->p_block == NULL )
528         {
529             if ( p_bridge->pp_es[i]->id != NULL
530                   && p_bridge->pp_es[i]->i_last < mdate() )
531             {
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;
536             }
537             continue;
538         }
539
540         if ( p_bridge->pp_es[i]->id != NULL )
541         {
542             block_t *p_block = p_bridge->pp_es[i]->p_block;
543             while ( p_block != NULL )
544             {
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;
549             }
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 );
552         }
553         else
554         {
555             block_ChainRelease( p_bridge->pp_es[i]->p_block );
556         }
557
558         p_bridge->pp_es[i]->p_block = NULL;
559         p_bridge->pp_es[i]->pp_last = &p_bridge->pp_es[i]->p_block;
560     }
561
562     if( b_no_es )
563     {
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 );
568         free( p_bridge );
569         var_Destroy( p_libvlc, p_sys->psz_name );
570     }
571
572     vlc_mutex_unlock( p_sys->p_lock );
573
574     return VLC_SUCCESS;
575 }