]> git.sesse.net Git - vlc/blob - modules/stream_out/bridge.c
* modules/stream_out/bridge.c: New bridge-in/bridge-out modules to bridge
[vlc] / modules / stream_out / bridge.c
1 /*****************************************************************************
2  * bridge.c: bridge stream output module
3  *****************************************************************************
4  * Copyright (C) 2005 VideoLAN
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include <vlc/vlc.h>
31 #include <vlc/sout.h>
32
33 /*****************************************************************************
34  * Module descriptor
35  *****************************************************************************/
36 #define ID_TEXT N_("ID")
37 #define ID_LONGTEXT N_( \
38     "Specify an identifier integer for this elementary stream" )
39
40 #define DELAY_TEXT N_("Delay")
41 #define DELAY_LONGTEXT N_("Pictures coming from the picture video outputs " \
42         "will be delayed accordingly (in milliseconds, >= 100 ms). For high " \
43         "values you will need to raise file-caching and others.")
44
45 #define ID_OFFSET_TEXT N_("ID Offset")
46 #define ID_OFFSET_LONGTEXT N_("Offset to add to the stream IDs specified in " \
47         "bridge_out to obtain the stream IDs bridge_in will register.")
48
49 static int  OpenOut ( vlc_object_t * );
50 static void CloseOut( vlc_object_t * );
51 static int  OpenIn  ( vlc_object_t * );
52 static void CloseIn ( vlc_object_t * );
53
54 #define SOUT_CFG_PREFIX_OUT "sout-bridge-out-"
55 #define SOUT_CFG_PREFIX_IN "sout-bridge-in-"
56
57 vlc_module_begin();
58     add_submodule();
59     set_shortname( _("Bridge out"));
60     set_description( _("Bridge out stream output") );
61     set_capability( "sout stream", 50 );
62     add_shortcut( "bridge-out" );
63     set_category( CAT_SOUT );
64     set_subcategory( SUBCAT_SOUT_STREAM );
65     add_integer( SOUT_CFG_PREFIX_OUT "id", 0, NULL, ID_TEXT, ID_LONGTEXT,
66                  VLC_FALSE );
67     set_callbacks( OpenOut, CloseOut );
68
69     add_submodule();
70     set_shortname( _("Bridge in"));
71     set_description( _("Bridge in stream output") );
72     set_capability( "sout stream", 50 );
73     add_shortcut( "bridge-in" );
74     set_category( CAT_SOUT );
75     set_subcategory( SUBCAT_SOUT_STREAM );
76     add_integer( SOUT_CFG_PREFIX_IN "delay", 100, NULL, DELAY_TEXT,
77                  DELAY_LONGTEXT, VLC_FALSE );
78     add_integer( SOUT_CFG_PREFIX_IN "id-offset", 8192, NULL, ID_OFFSET_TEXT,
79                  ID_OFFSET_LONGTEXT, VLC_FALSE );
80     set_callbacks( OpenIn, CloseIn );
81
82     var_Create( p_module->p_libvlc, "bridge-lock", VLC_VAR_MUTEX );
83 vlc_module_end();
84
85
86 /*****************************************************************************
87  * Local prototypes
88  *****************************************************************************/
89 static const char *ppsz_sout_options_out[] = {
90     "id", NULL
91 };
92
93 static const char *ppsz_sout_options_in[] = {
94     "delay", "id-offset", NULL
95 };
96
97 static sout_stream_id_t *AddOut ( sout_stream_t *, es_format_t * );
98 static int               DelOut ( sout_stream_t *, sout_stream_id_t * );
99 static int               SendOut( sout_stream_t *, sout_stream_id_t *, block_t * );
100
101 static sout_stream_id_t *AddIn ( sout_stream_t *, es_format_t * );
102 static int               DelIn ( sout_stream_t *, sout_stream_id_t * );
103 static int               SendIn( sout_stream_t *, sout_stream_id_t *, block_t * );
104
105 typedef struct bridged_es_t
106 {
107     es_format_t fmt;
108     block_t *p_block;
109     block_t **pp_last;
110     vlc_bool_t b_empty;
111     int i_id;
112
113     /* bridge in part */
114     sout_stream_id_t *id;
115     vlc_bool_t b_changed;
116 } bridged_es_t;
117
118 typedef struct bridge_t
119 {
120     bridged_es_t *p_es;
121     int i_es_num;
122 } bridge_t;
123
124 #define GetBridge(a) __GetBridge( VLC_OBJECT(a) )
125 static bridge_t *__GetBridge( vlc_object_t *p_object )
126 {
127     libvlc_t *p_libvlc = p_object->p_libvlc;
128     bridge_t *p_bridge;
129     vlc_value_t val;
130
131     if( var_Get( p_libvlc, "bridge-struct", &val ) != VLC_SUCCESS )
132     {
133         p_bridge = NULL;
134     }
135     else
136     {
137         p_bridge = val.p_address;
138     }    
139
140     return p_bridge;
141 }
142
143
144 /*
145  * Bridge out
146  */
147
148 typedef struct out_sout_stream_sys_t
149 {
150     vlc_mutex_t *p_lock;
151     int i_id;
152     vlc_bool_t b_inited;
153     int i_position;
154 } out_sout_stream_sys_t;
155
156 /*****************************************************************************
157  * OpenOut:
158  *****************************************************************************/
159 static int OpenOut( vlc_object_t *p_this )
160 {
161     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
162     out_sout_stream_sys_t *p_sys;
163     vlc_value_t val;
164
165     sout_CfgParse( p_stream, SOUT_CFG_PREFIX_OUT, ppsz_sout_options_out,
166                    p_stream->p_cfg );
167
168     p_sys          = malloc( sizeof( out_sout_stream_sys_t ) );
169     p_sys->b_inited = VLC_FALSE;
170
171     var_Get( p_this->p_libvlc, "bridge-lock", &val );
172     p_sys->p_lock = val.p_address;
173
174     var_Get( p_stream, SOUT_CFG_PREFIX_OUT "id", &val );
175     p_sys->i_id = val.i_int;
176
177     p_stream->pf_add    = AddOut;
178     p_stream->pf_del    = DelOut;
179     p_stream->pf_send   = SendOut;
180
181     p_stream->p_sys     = (sout_stream_sys_t *)p_sys;
182
183     /* update p_sout->i_out_pace_nocontrol */
184     p_stream->p_sout->i_out_pace_nocontrol++;
185
186     return VLC_SUCCESS;
187 }
188
189 /*****************************************************************************
190  * CloseOut:
191  *****************************************************************************/
192 static void CloseOut( vlc_object_t * p_this )
193 {
194     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
195     out_sout_stream_sys_t *p_sys = (out_sout_stream_sys_t *)p_stream->p_sys;
196
197     /* update p_sout->i_out_pace_nocontrol */
198     p_stream->p_sout->i_out_pace_nocontrol--;
199
200     free( p_sys );
201 }
202
203 static sout_stream_id_t * AddOut( sout_stream_t *p_stream, es_format_t *p_fmt )
204 {
205     out_sout_stream_sys_t *p_sys = (out_sout_stream_sys_t *)p_stream->p_sys;
206     bridge_t *p_bridge;
207     bridged_es_t *p_es;
208     int i;
209
210     if ( p_sys->b_inited )
211     {
212         return NULL;
213     }
214     p_sys->b_inited = VLC_TRUE;
215
216     vlc_mutex_lock( p_sys->p_lock );
217
218     p_bridge = GetBridge( p_stream );
219     if ( p_bridge == NULL )
220     {
221         libvlc_t *p_libvlc = p_stream->p_libvlc;
222         vlc_value_t val;
223
224         p_bridge = malloc( sizeof( bridge_t ) );
225
226         var_Create( p_libvlc, "bridge-struct", VLC_VAR_ADDRESS );
227         val.p_address = p_bridge;
228         var_Set( p_libvlc, "bridge-struct", val );
229
230         p_bridge->i_es_num = 0;
231         p_bridge->p_es = NULL;
232     }
233
234     for ( i = 0; i < p_bridge->i_es_num; i++ )
235     {
236         if ( p_bridge->p_es[i].b_empty && !p_bridge->p_es[i].b_changed )
237             break;
238     }
239
240     if ( i == p_bridge->i_es_num )
241     {
242         p_bridge->p_es = realloc( p_bridge->p_es,
243                                   (p_bridge->i_es_num + 1)
244                                     * sizeof(bridged_es_t) );
245         p_sys->i_position = p_bridge->i_es_num;
246         p_bridge->i_es_num++;
247     }
248
249     p_es = &p_bridge->p_es[ p_sys->i_position ];
250     p_es->fmt = *p_fmt;
251     p_es->fmt.i_id = p_sys->i_id;
252     p_es->p_block = NULL;
253     p_es->pp_last = &p_es->p_block;
254     p_es->b_empty = VLC_FALSE;
255
256     p_es->id = NULL;
257     p_es->b_changed = VLC_TRUE;
258
259     vlc_mutex_unlock( p_sys->p_lock );
260
261     return (sout_stream_id_t *)p_sys;
262 }
263
264 static int DelOut( sout_stream_t *p_stream, sout_stream_id_t *id )
265 {
266     out_sout_stream_sys_t *p_sys = (out_sout_stream_sys_t *)p_stream->p_sys;
267     bridge_t *p_bridge;
268     bridged_es_t *p_es;
269
270     if ( !p_sys->b_inited )
271     {
272         return VLC_SUCCESS;
273     }
274
275     vlc_mutex_lock( p_sys->p_lock );
276
277     p_bridge = GetBridge( p_stream );
278     p_es = &p_bridge->p_es[ p_sys->i_position ];
279
280     p_es->b_empty = VLC_TRUE;
281     block_ChainRelease( p_es->p_block );
282
283     p_es->b_changed = VLC_TRUE;
284     vlc_mutex_unlock( p_sys->p_lock );
285
286     return VLC_SUCCESS;
287 }
288
289 static int SendOut( sout_stream_t *p_stream, sout_stream_id_t *id,
290                     block_t *p_buffer )
291 {
292     out_sout_stream_sys_t *p_sys = (out_sout_stream_sys_t *)p_stream->p_sys;
293     bridge_t *p_bridge;
294     bridged_es_t *p_es;
295
296     if ( (out_sout_stream_sys_t *)id != p_sys )
297     {
298         block_ChainRelease( p_buffer );
299         return VLC_SUCCESS;
300     }
301
302     vlc_mutex_lock( p_sys->p_lock );
303
304     p_bridge = GetBridge( p_stream );
305     p_es = &p_bridge->p_es[ p_sys->i_position ];
306     *p_es->pp_last = p_buffer;
307     while ( p_buffer != NULL )
308     {
309         p_es->pp_last = &p_buffer->p_next;
310         p_buffer = p_buffer->p_next;
311     }
312
313     vlc_mutex_unlock( p_sys->p_lock );
314
315     return VLC_SUCCESS;
316 }
317
318
319 /*
320  * Bridge in
321  */
322
323 typedef struct in_sout_stream_sys_t
324 {
325     sout_stream_t *p_out;
326     vlc_mutex_t *p_lock;
327     int i_id_offset;
328     mtime_t i_delay;
329 } in_sout_stream_sys_t;
330
331 /*****************************************************************************
332  * OpenIn:
333  *****************************************************************************/
334 static int OpenIn( vlc_object_t *p_this )
335 {
336     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
337     in_sout_stream_sys_t *p_sys;
338     vlc_value_t val;
339
340     p_sys          = malloc( sizeof( in_sout_stream_sys_t ) );
341
342     p_sys->p_out = sout_StreamNew( p_stream->p_sout, p_stream->psz_next );
343     if( !p_sys->p_out )
344     {
345         msg_Err( p_stream, "cannot create chain" );
346         free( p_sys );
347         return VLC_EGENERIC;
348     }
349
350     sout_CfgParse( p_stream, SOUT_CFG_PREFIX_IN, ppsz_sout_options_in,
351                    p_stream->p_cfg );
352
353     var_Get( p_this->p_libvlc, "bridge-lock", &val );
354     p_sys->p_lock = val.p_address;
355
356     var_Get( p_stream, SOUT_CFG_PREFIX_IN "id-offset", &val );
357     p_sys->i_id_offset = val.i_int;
358
359     var_Get( p_stream, SOUT_CFG_PREFIX_IN "delay", &val );
360     p_sys->i_delay = (mtime_t)val.i_int * 1000;
361
362     p_stream->pf_add    = AddIn;
363     p_stream->pf_del    = DelIn;
364     p_stream->pf_send   = SendIn;
365
366     p_stream->p_sys     = (sout_stream_sys_t *)p_sys;
367
368     /* update p_sout->i_out_pace_nocontrol */
369     p_stream->p_sout->i_out_pace_nocontrol++;
370
371     return VLC_SUCCESS;
372 }
373
374 /*****************************************************************************
375  * CloseIn:
376  *****************************************************************************/
377 static void CloseIn( vlc_object_t * p_this )
378 {
379     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
380     in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
381
382     sout_StreamDelete( p_sys->p_out );
383
384     /* update p_sout->i_out_pace_nocontrol */
385     p_stream->p_sout->i_out_pace_nocontrol--;
386
387
388     free( p_sys );
389 }
390
391 static sout_stream_id_t * AddIn( sout_stream_t *p_stream, es_format_t *p_fmt )
392 {
393     in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
394
395     return p_sys->p_out->pf_add( p_sys->p_out, p_fmt );
396 }
397
398 static int DelIn( sout_stream_t *p_stream, sout_stream_id_t *id )
399 {
400     in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
401
402     return p_sys->p_out->pf_del( p_sys->p_out, id );
403 }
404
405 static int SendIn( sout_stream_t *p_stream, sout_stream_id_t *id,
406                    block_t *p_buffer )
407 {
408     in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
409     bridge_t *p_bridge;
410     vlc_bool_t b_no_es = VLC_TRUE;
411     int i;
412
413     /* First forward the packet for our own ES */
414     p_sys->p_out->pf_send( p_sys->p_out, id, p_buffer );
415
416     /* Then check all bridged streams */
417     vlc_mutex_lock( p_sys->p_lock );
418
419     p_bridge = GetBridge( p_stream );
420     if ( p_bridge == NULL )
421     {
422         vlc_mutex_unlock( p_sys->p_lock );
423         return VLC_SUCCESS;
424     }
425
426     for ( i = 0; i < p_bridge->i_es_num; i++ )
427     {
428         if ( p_bridge->p_es[i].b_changed )
429         {
430             if ( p_bridge->p_es[i].b_empty )
431             {
432                 p_sys->p_out->pf_del( p_sys->p_out, p_bridge->p_es[i].id );
433             }
434             else
435             {
436                 p_bridge->p_es[i].fmt.i_id += p_sys->i_id_offset;
437                 p_bridge->p_es[i].id = p_sys->p_out->pf_add(
438                             p_sys->p_out, &p_bridge->p_es[i].fmt );
439                 if ( p_bridge->p_es[i].id == NULL )
440                 {
441                     msg_Warn( p_stream, "couldn't create chain for id %d",
442                               p_bridge->p_es[i].fmt.i_id );
443                 }
444             }
445         }
446         p_bridge->p_es[i].b_changed = VLC_FALSE;
447
448         if ( p_bridge->p_es[i].b_empty )
449             continue;
450
451         b_no_es = VLC_FALSE;
452
453         if ( p_bridge->p_es[i].p_block == NULL )
454             continue;
455
456         if ( p_bridge->p_es[i].id != NULL )
457         {
458             block_t *p_block = p_bridge->p_es[i].p_block;
459             while ( p_block != NULL )
460             {
461                 p_block->i_pts += p_sys->i_delay;
462                 p_block->i_dts += p_sys->i_delay;
463                 p_block = p_block->p_next;
464             }
465             p_sys->p_out->pf_send( p_sys->p_out, p_bridge->p_es[i].id,
466                                    p_bridge->p_es[i].p_block );
467         }
468         else
469             block_ChainRelease( p_bridge->p_es[i].p_block );
470
471         p_bridge->p_es[i].p_block = NULL;
472         p_bridge->p_es[i].pp_last = &p_bridge->p_es[i].p_block;
473     }
474
475     if( b_no_es )
476     {
477         libvlc_t *p_libvlc = p_stream->p_libvlc;
478         free( p_bridge->p_es );
479         free( p_bridge );
480         var_Destroy( p_libvlc, "bridge-struct" );
481     }
482
483     vlc_mutex_unlock( p_sys->p_lock );
484
485     return VLC_SUCCESS;
486 }