]> git.sesse.net Git - vlc/blob - modules/stream_out/bridge.c
Merge branch 'master' of git@git.videolan.org:vlc
[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/vlc.h>
33 #include <vlc_sout.h>
34 #include <vlc_block.h>
35
36 /*****************************************************************************
37  * Module descriptor
38  *****************************************************************************/
39 #define ID_TEXT N_("ID")
40 #define ID_LONGTEXT N_( \
41     "Integer identifier for this elementary stream. This will be used to " \
42     "\"find\" this stream later." )
43
44 #define DELAY_TEXT N_("Delay")
45 #define DELAY_LONGTEXT N_("Pictures coming from the picture video outputs " \
46         "will be delayed according to this value (in milliseconds, should be "\
47         ">= 100 ms). For high values, you will need to raise caching values." )
48
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
53 static int  OpenOut ( vlc_object_t * );
54 static void CloseOut( vlc_object_t * );
55 static int  OpenIn  ( vlc_object_t * );
56 static void CloseIn ( vlc_object_t * );
57
58 #define SOUT_CFG_PREFIX_OUT "sout-bridge-out-"
59 #define SOUT_CFG_PREFIX_IN "sout-bridge-in-"
60
61 vlc_module_begin();
62     set_shortname( _("Bridge"));
63     set_description( _("Bridge stream output"));
64     add_submodule();
65     set_section( N_("Bridge out"), NULL );
66     set_capability( "sout stream", 50 );
67     add_shortcut( "bridge-out" );
68     /* Only usable with VLM. No category so not in gui preferences
69     set_category( CAT_SOUT );
70     set_subcategory( SUBCAT_SOUT_STREAM );*/
71     add_integer( SOUT_CFG_PREFIX_OUT "id", 0, NULL, ID_TEXT, ID_LONGTEXT,
72                  false );
73     set_callbacks( OpenOut, CloseOut );
74
75     add_submodule();
76     set_section( N_("Bridge in"), NULL );
77     set_capability( "sout stream", 50 );
78     add_shortcut( "bridge-in" );
79     /*set_category( CAT_SOUT );
80     set_subcategory( SUBCAT_SOUT_STREAM );*/
81     add_integer( SOUT_CFG_PREFIX_IN "delay", 0, NULL, DELAY_TEXT,
82                  DELAY_LONGTEXT, false );
83     add_integer( SOUT_CFG_PREFIX_IN "id-offset", 8192, NULL, ID_OFFSET_TEXT,
84                  ID_OFFSET_LONGTEXT, false );
85     set_callbacks( OpenIn, CloseIn );
86
87 vlc_module_end();
88
89
90 /*****************************************************************************
91  * Local prototypes
92  *****************************************************************************/
93 static const char *ppsz_sout_options_out[] = {
94     "id", NULL
95 };
96
97 static const char *ppsz_sout_options_in[] = {
98     "delay", "id-offset", NULL
99 };
100
101 static sout_stream_id_t *AddOut ( sout_stream_t *, es_format_t * );
102 static int               DelOut ( sout_stream_t *, sout_stream_id_t * );
103 static int               SendOut( sout_stream_t *, sout_stream_id_t *, block_t * );
104
105 static sout_stream_id_t *AddIn ( sout_stream_t *, es_format_t * );
106 static int               DelIn ( sout_stream_t *, sout_stream_id_t * );
107 static int               SendIn( sout_stream_t *, sout_stream_id_t *, block_t * );
108
109 typedef struct bridged_es_t
110 {
111     es_format_t fmt;
112     block_t *p_block;
113     block_t **pp_last;
114     bool b_empty;
115
116     /* bridge in part */
117     sout_stream_id_t *id;
118     mtime_t i_last;
119     bool b_changed;
120 } bridged_es_t;
121
122 typedef struct bridge_t
123 {
124     bridged_es_t **pp_es;
125     int i_es_num;
126 } bridge_t;
127
128 #define GetBridge(a) __GetBridge( VLC_OBJECT(a) )
129 static bridge_t *__GetBridge( vlc_object_t *p_object )
130 {
131     bridge_t *p_bridge;
132     vlc_value_t val;
133
134     if( var_Get( p_object->p_libvlc, "bridge-struct", &val ) )
135     {
136         p_bridge = NULL;
137     }
138     else
139     {
140         p_bridge = val.p_address;
141     }
142
143     return p_bridge;
144 }
145
146
147 /*
148  * Bridge out
149  */
150
151 typedef struct out_sout_stream_sys_t
152 {
153     vlc_mutex_t *p_lock;
154     bridged_es_t *p_es;
155     int i_id;
156     bool b_inited;
157 } out_sout_stream_sys_t;
158
159 /*****************************************************************************
160  * OpenOut:
161  *****************************************************************************/
162 static int OpenOut( vlc_object_t *p_this )
163 {
164     sout_stream_t     *p_stream = (sout_stream_t *)p_this;
165     out_sout_stream_sys_t *p_sys;
166     vlc_value_t val;
167
168     config_ChainParse( p_stream, SOUT_CFG_PREFIX_OUT, ppsz_sout_options_out,
169                    p_stream->p_cfg );
170
171     p_sys          = malloc( sizeof( out_sout_stream_sys_t ) );
172     p_sys->b_inited = false;
173
174     var_Create( p_this->p_libvlc, "bridge-lock", VLC_VAR_MUTEX );
175     var_Get( p_this->p_libvlc, "bridge-lock", &val );
176     p_sys->p_lock = val.p_address;
177
178     var_Get( p_stream, SOUT_CFG_PREFIX_OUT "id", &val );
179     p_sys->i_id = val.i_int;
180
181     p_stream->pf_add    = AddOut;
182     p_stream->pf_del    = DelOut;
183     p_stream->pf_send   = SendOut;
184
185     p_stream->p_sys     = (sout_stream_sys_t *)p_sys;
186
187     p_stream->p_sout->i_out_pace_nocontrol++;
188
189     return VLC_SUCCESS;
190 }
191
192 /*****************************************************************************
193  * CloseOut:
194  *****************************************************************************/
195 static void CloseOut( vlc_object_t * p_this )
196 {
197     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
198     out_sout_stream_sys_t *p_sys = (out_sout_stream_sys_t *)p_stream->p_sys;
199
200     p_stream->p_sout->i_out_pace_nocontrol--;
201
202     free( p_sys );
203 }
204
205 static sout_stream_id_t * AddOut( sout_stream_t *p_stream, es_format_t *p_fmt )
206 {
207     out_sout_stream_sys_t *p_sys = (out_sout_stream_sys_t *)p_stream->p_sys;
208     bridge_t *p_bridge;
209     bridged_es_t *p_es;
210     int i;
211
212     if ( p_sys->b_inited )
213     {
214         return NULL;
215     }
216     p_sys->b_inited = true;
217
218     vlc_mutex_lock( p_sys->p_lock );
219
220     p_bridge = GetBridge( p_stream );
221     if ( p_bridge == NULL )
222     {
223         vlc_object_t *p_libvlc = VLC_OBJECT( p_stream->p_libvlc );
224         vlc_value_t val;
225
226         p_bridge = malloc( sizeof( bridge_t ) );
227
228         var_Create( p_libvlc, "bridge-struct", VLC_VAR_ADDRESS );
229         val.p_address = p_bridge;
230         var_Set( p_libvlc, "bridge-struct", val );
231
232         p_bridge->i_es_num = 0;
233         p_bridge->pp_es = NULL;
234     }
235
236     for ( i = 0; i < p_bridge->i_es_num; i++ )
237     {
238         if ( p_bridge->pp_es[i]->b_empty && !p_bridge->pp_es[i]->b_changed )
239             break;
240     }
241
242     if ( i == p_bridge->i_es_num )
243     {
244         p_bridge->pp_es = realloc( p_bridge->pp_es,
245                                    (p_bridge->i_es_num + 1)
246                                      * sizeof(bridged_es_t *) );
247         p_bridge->i_es_num++;
248         p_bridge->pp_es[i] = malloc( sizeof(bridged_es_t) );
249     }
250
251     p_sys->p_es = p_es = p_bridge->pp_es[i];
252
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
259     p_es->id = NULL;
260     p_es->i_last = 0;
261     p_es->b_changed = true;
262
263     msg_Dbg( p_stream, "bridging out input codec=%4.4s id=%d pos=%d",
264              (char*)&p_es->fmt.i_codec, p_es->fmt.i_id, i );
265
266     vlc_mutex_unlock( p_sys->p_lock );
267
268     return (sout_stream_id_t *)p_sys;
269 }
270
271 static int DelOut( sout_stream_t *p_stream, sout_stream_id_t *id )
272 {
273     VLC_UNUSED(id);
274     out_sout_stream_sys_t *p_sys = (out_sout_stream_sys_t *)p_stream->p_sys;
275     bridged_es_t *p_es;
276
277     if ( !p_sys->b_inited )
278     {
279         return VLC_SUCCESS;
280     }
281
282     vlc_mutex_lock( p_sys->p_lock );
283
284     p_es = p_sys->p_es;
285
286     p_es->b_empty = true;
287     block_ChainRelease( p_es->p_block );
288     p_es->p_block = false;
289
290     p_es->b_changed = true;
291     vlc_mutex_unlock( p_sys->p_lock );
292
293     p_sys->b_inited = false;
294
295     return VLC_SUCCESS;
296 }
297
298 static int SendOut( sout_stream_t *p_stream, sout_stream_id_t *id,
299                     block_t *p_buffer )
300 {
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 ( (out_sout_stream_sys_t *)id != p_sys )
305     {
306         block_ChainRelease( p_buffer );
307         return VLC_SUCCESS;
308     }
309
310     vlc_mutex_lock( p_sys->p_lock );
311
312     p_es = p_sys->p_es;
313     *p_es->pp_last = p_buffer;
314     while ( p_buffer != NULL )
315     {
316         p_es->pp_last = &p_buffer->p_next;
317         p_buffer = p_buffer->p_next;
318     }
319
320     vlc_mutex_unlock( p_sys->p_lock );
321
322     return VLC_SUCCESS;
323 }
324
325
326 /*
327  * Bridge in
328  */
329
330 typedef struct in_sout_stream_sys_t
331 {
332     sout_stream_t *p_out;
333     vlc_mutex_t *p_lock;
334     int i_id_offset;
335     mtime_t i_delay;
336 } in_sout_stream_sys_t;
337
338 /*****************************************************************************
339  * OpenIn:
340  *****************************************************************************/
341 static int OpenIn( vlc_object_t *p_this )
342 {
343     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
344     in_sout_stream_sys_t *p_sys;
345     vlc_value_t val;
346
347     p_sys          = malloc( sizeof( in_sout_stream_sys_t ) );
348
349     p_sys->p_out = sout_StreamNew( p_stream->p_sout, p_stream->psz_next );
350     if( !p_sys->p_out )
351     {
352         msg_Err( p_stream, "cannot create chain" );
353         free( p_sys );
354         return VLC_EGENERIC;
355     }
356
357     config_ChainParse( p_stream, SOUT_CFG_PREFIX_IN, ppsz_sout_options_in,
358                    p_stream->p_cfg );
359
360     var_Create( p_this->p_libvlc, "bridge-lock", VLC_VAR_MUTEX );
361     var_Get( p_this->p_libvlc, "bridge-lock", &val );
362     p_sys->p_lock = val.p_address;
363
364     var_Get( p_stream, SOUT_CFG_PREFIX_IN "id-offset", &val );
365     p_sys->i_id_offset = val.i_int;
366
367     var_Get( p_stream, SOUT_CFG_PREFIX_IN "delay", &val );
368     p_sys->i_delay = (mtime_t)val.i_int * 1000;
369
370     p_stream->pf_add    = AddIn;
371     p_stream->pf_del    = DelIn;
372     p_stream->pf_send   = SendIn;
373
374     p_stream->p_sys     = (sout_stream_sys_t *)p_sys;
375
376     /* update p_sout->i_out_pace_nocontrol */
377     p_stream->p_sout->i_out_pace_nocontrol++;
378
379     return VLC_SUCCESS;
380 }
381
382 /*****************************************************************************
383  * CloseIn:
384  *****************************************************************************/
385 static void CloseIn( vlc_object_t * p_this )
386 {
387     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
388     in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
389
390     sout_StreamDelete( p_sys->p_out );
391     p_stream->p_sout->i_out_pace_nocontrol--;
392
393     free( p_sys );
394 }
395
396 static sout_stream_id_t * AddIn( sout_stream_t *p_stream, es_format_t *p_fmt )
397 {
398     in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
399
400     return p_sys->p_out->pf_add( p_sys->p_out, p_fmt );
401 }
402
403 static int DelIn( sout_stream_t *p_stream, sout_stream_id_t *id )
404 {
405     in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
406
407     return p_sys->p_out->pf_del( p_sys->p_out, id );
408 }
409
410 static int SendIn( sout_stream_t *p_stream, sout_stream_id_t *id,
411                    block_t *p_buffer )
412 {
413     in_sout_stream_sys_t *p_sys = (in_sout_stream_sys_t *)p_stream->p_sys;
414     bridge_t *p_bridge;
415     bool b_no_es = true;
416     int i;
417
418     /* First forward the packet for our own ES */
419     p_sys->p_out->pf_send( p_sys->p_out, id, p_buffer );
420
421     /* Then check all bridged streams */
422     vlc_mutex_lock( p_sys->p_lock );
423
424     p_bridge = GetBridge( p_stream );
425     if ( p_bridge == NULL )
426     {
427         vlc_mutex_unlock( p_sys->p_lock );
428         return VLC_SUCCESS;
429     }
430
431     for ( i = 0; i < p_bridge->i_es_num; i++ )
432     {
433         if ( !p_bridge->pp_es[i]->b_empty )
434             b_no_es = false;
435
436         while ( p_bridge->pp_es[i]->p_block != NULL
437                  && (p_bridge->pp_es[i]->p_block->i_dts + p_sys->i_delay
438                        < mdate()
439                       || p_bridge->pp_es[i]->p_block->i_dts + p_sys->i_delay
440                           < p_bridge->pp_es[i]->i_last) )
441         {
442             block_t *p_block = p_bridge->pp_es[i]->p_block;
443             msg_Dbg( p_stream, "dropping a packet (" I64Fd ")",
444                      mdate() - p_block->i_dts - p_sys->i_delay );
445             p_bridge->pp_es[i]->p_block
446                 = p_bridge->pp_es[i]->p_block->p_next;
447             block_Release( p_block );
448         }
449
450         if ( p_bridge->pp_es[i]->p_block == NULL )
451         {
452             p_bridge->pp_es[i]->pp_last = &p_bridge->pp_es[i]->p_block;
453         }
454
455         if ( p_bridge->pp_es[i]->b_changed )
456         {
457             if ( p_bridge->pp_es[i]->b_empty && p_bridge->pp_es[i]->id != NULL )
458             {
459                 p_sys->p_out->pf_del( p_sys->p_out, p_bridge->pp_es[i]->id );
460             }
461             else
462             {
463                 /* We need at least two packets to enter the mux. */
464                 if ( p_bridge->pp_es[i]->p_block == NULL
465                       || p_bridge->pp_es[i]->p_block->p_next == NULL )
466                 {
467                     continue;
468                 }
469
470                 p_bridge->pp_es[i]->fmt.i_id += p_sys->i_id_offset;
471                 p_bridge->pp_es[i]->id = p_sys->p_out->pf_add(
472                             p_sys->p_out, &p_bridge->pp_es[i]->fmt );
473                 if ( p_bridge->pp_es[i]->id == NULL )
474                 {
475                     msg_Warn( p_stream, "couldn't create chain for id %d",
476                               p_bridge->pp_es[i]->fmt.i_id );
477                 }
478                 msg_Dbg( p_stream, "bridging in input codec=%4.4s id=%d pos=%d",
479                          (char*)&p_bridge->pp_es[i]->fmt.i_codec,
480                          p_bridge->pp_es[i]->fmt.i_id, i );
481             }
482         }
483         p_bridge->pp_es[i]->b_changed = false;
484
485         if ( p_bridge->pp_es[i]->b_empty )
486             continue;
487
488         if ( p_bridge->pp_es[i]->p_block == NULL )
489         {
490             if ( p_bridge->pp_es[i]->id != NULL
491                   && p_bridge->pp_es[i]->i_last < mdate() )
492             {
493                 p_sys->p_out->pf_del( p_sys->p_out, p_bridge->pp_es[i]->id );
494                 p_bridge->pp_es[i]->fmt.i_id -= p_sys->i_id_offset;
495                 p_bridge->pp_es[i]->b_changed = true;
496                 p_bridge->pp_es[i]->id = NULL;
497             }
498             continue;
499         }
500
501         if ( p_bridge->pp_es[i]->id != NULL )
502         {
503             block_t *p_block = p_bridge->pp_es[i]->p_block;
504             while ( p_block != NULL )
505             {
506                 p_bridge->pp_es[i]->i_last = p_block->i_dts;
507                 p_block->i_pts += p_sys->i_delay;
508                 p_block->i_dts += p_sys->i_delay;
509                 p_block = p_block->p_next;
510             }
511             p_sys->p_out->pf_send( p_sys->p_out, p_bridge->pp_es[i]->id,
512                                    p_bridge->pp_es[i]->p_block );
513         }
514         else
515         {
516             block_ChainRelease( p_bridge->pp_es[i]->p_block );
517         }
518
519         p_bridge->pp_es[i]->p_block = NULL;
520         p_bridge->pp_es[i]->pp_last = &p_bridge->pp_es[i]->p_block;
521     }
522
523     if( b_no_es )
524     {
525         vlc_object_t *p_libvlc = VLC_OBJECT( p_stream->p_libvlc );
526         for ( i = 0; i < p_bridge->i_es_num; i++ )
527             free( p_bridge->pp_es[i] );
528         free( p_bridge->pp_es );
529         free( p_bridge );
530         var_Destroy( p_libvlc, "bridge-struct" );
531     }
532
533     vlc_mutex_unlock( p_sys->p_lock );
534
535     return VLC_SUCCESS;
536 }