]> git.sesse.net Git - vlc/blob - modules/stream_out/duplicate.c
Use var_InheritString for --decklink-video-connection.
[vlc] / modules / stream_out / duplicate.c
1 /*****************************************************************************
2  * duplicate.c: duplicate stream output module
3  *****************************************************************************
4  * Copyright (C) 2003-2004 the VideoLAN team
5  * $Id$
6  *
7  * Author: Laurent Aimar <fenrir@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 static int      Open    ( vlc_object_t * );
41 static void     Close   ( vlc_object_t * );
42
43 vlc_module_begin ()
44     set_description( N_("Duplicate stream output") )
45     set_capability( "sout stream", 50 )
46     add_shortcut( "duplicate", "dup" )
47     set_category( CAT_SOUT )
48     set_subcategory( SUBCAT_SOUT_STREAM )
49     set_callbacks( Open, Close )
50 vlc_module_end ()
51
52
53 /*****************************************************************************
54  * Exported prototypes
55  *****************************************************************************/
56 static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
57 static int               Del ( sout_stream_t *, sout_stream_id_t * );
58 static int               Send( sout_stream_t *, sout_stream_id_t *,
59                                block_t* );
60
61 struct sout_stream_sys_t
62 {
63     int             i_nb_streams;
64     sout_stream_t   **pp_streams;
65
66     int             i_nb_last_streams;
67     sout_stream_t   **pp_last_streams;
68
69     int             i_nb_select;
70     char            **ppsz_select;
71 };
72
73 struct sout_stream_id_t
74 {
75     int                 i_nb_ids;
76     void                **pp_ids;
77 };
78
79 static bool ESSelected( es_format_t *fmt, char *psz_select );
80
81 /*****************************************************************************
82  * Open:
83  *****************************************************************************/
84 static int Open( vlc_object_t *p_this )
85 {
86     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
87     sout_stream_sys_t *p_sys;
88     config_chain_t        *p_cfg;
89
90     msg_Dbg( p_stream, "creating 'duplicate'" );
91
92     p_sys = malloc( sizeof( sout_stream_sys_t ) );
93     if( !p_sys )
94         return VLC_ENOMEM;
95
96     TAB_INIT( p_sys->i_nb_streams, p_sys->pp_streams );
97     TAB_INIT( p_sys->i_nb_last_streams, p_sys->pp_last_streams );
98     TAB_INIT( p_sys->i_nb_select, p_sys->ppsz_select );
99
100     for( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )
101     {
102         if( !strncmp( p_cfg->psz_name, "dst", strlen( "dst" ) ) )
103         {
104             sout_stream_t *s, *p_last;
105
106             msg_Dbg( p_stream, " * adding `%s'", p_cfg->psz_value );
107             s = sout_StreamChainNew( p_stream->p_sout, p_cfg->psz_value,
108                 p_stream->p_next, &p_last );
109
110             if( s )
111             {
112                 TAB_APPEND( p_sys->i_nb_streams, p_sys->pp_streams, s );
113                 TAB_APPEND( p_sys->i_nb_last_streams, p_sys->pp_last_streams,
114                     p_last );
115                 TAB_APPEND( p_sys->i_nb_select,  p_sys->ppsz_select, NULL );
116             }
117         }
118         else if( !strncmp( p_cfg->psz_name, "select", strlen( "select" ) ) )
119         {
120             char *psz = p_cfg->psz_value;
121             if( p_sys->i_nb_select > 0 && psz && *psz )
122             {
123                 char **ppsz_select = &p_sys->ppsz_select[p_sys->i_nb_select - 1];
124
125                 if( *ppsz_select )
126                 {
127                     msg_Err( p_stream, " * ignore selection `%s' (it already has `%s')",
128                              psz, *ppsz_select );
129                 }
130                 else
131                 {
132                     msg_Dbg( p_stream, " * apply selection `%s'", psz );
133                     *ppsz_select = strdup( psz );
134                 }
135             }
136         }
137         else
138         {
139             msg_Err( p_stream, " * ignore unknown option `%s'", p_cfg->psz_name );
140         }
141     }
142
143     if( p_sys->i_nb_streams == 0 )
144     {
145         msg_Err( p_stream, "no destination given" );
146         free( p_sys );
147
148         return VLC_EGENERIC;
149     }
150
151     p_stream->pf_add    = Add;
152     p_stream->pf_del    = Del;
153     p_stream->pf_send   = Send;
154
155     p_stream->p_sys     = p_sys;
156
157     return VLC_SUCCESS;
158 }
159
160 /*****************************************************************************
161  * Close:
162  *****************************************************************************/
163 static void Close( vlc_object_t * p_this )
164 {
165     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
166     sout_stream_sys_t *p_sys = p_stream->p_sys;
167
168     int i;
169
170     msg_Dbg( p_stream, "closing a duplication" );
171     for( i = 0; i < p_sys->i_nb_streams; i++ )
172     {
173         sout_StreamChainDelete(p_sys->pp_streams[i], p_sys->pp_last_streams[i]);
174         free( p_sys->ppsz_select[i] );
175     }
176     free( p_sys->pp_streams );
177     free( p_sys->pp_last_streams );
178     free( p_sys->ppsz_select );
179
180     free( p_sys );
181 }
182
183 /*****************************************************************************
184  * Add:
185  *****************************************************************************/
186 static sout_stream_id_t * Add( sout_stream_t *p_stream, es_format_t *p_fmt )
187 {
188     sout_stream_sys_t *p_sys = p_stream->p_sys;
189     sout_stream_id_t  *id;
190     int i_stream, i_valid_streams = 0;
191
192     id = malloc( sizeof( sout_stream_id_t ) );
193     if( !id )
194         return NULL;
195
196     TAB_INIT( id->i_nb_ids, id->pp_ids );
197
198     msg_Dbg( p_stream, "duplicated a new stream codec=%4.4s (es=%d group=%d)",
199              (char*)&p_fmt->i_codec, p_fmt->i_id, p_fmt->i_group );
200
201     for( i_stream = 0; i_stream < p_sys->i_nb_streams; i_stream++ )
202     {
203         void *id_new = NULL;
204
205         if( ESSelected( p_fmt, p_sys->ppsz_select[i_stream] ) )
206         {
207             sout_stream_t *out = p_sys->pp_streams[i_stream];
208
209             id_new = (void*)sout_StreamIdAdd( out, p_fmt );
210             if( id_new )
211             {
212                 msg_Dbg( p_stream, "    - added for output %d", i_stream );
213                 i_valid_streams++;
214             }
215             else
216             {
217                 msg_Dbg( p_stream, "    - failed for output %d", i_stream );
218             }
219         }
220         else
221         {
222             msg_Dbg( p_stream, "    - ignored for output %d", i_stream );
223         }
224
225         /* Append failed attempts as well to keep track of which pp_id
226          * belongs to which duplicated stream */
227         TAB_APPEND( id->i_nb_ids, id->pp_ids, id_new );
228     }
229
230     if( i_valid_streams <= 0 )
231     {
232         Del( p_stream, id );
233         return NULL;
234     }
235
236     return id;
237 }
238
239 /*****************************************************************************
240  * Del:
241  *****************************************************************************/
242 static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
243 {
244     sout_stream_sys_t *p_sys = p_stream->p_sys;
245     int               i_stream;
246
247     for( i_stream = 0; i_stream < p_sys->i_nb_streams; i_stream++ )
248     {
249         if( id->pp_ids[i_stream] )
250         {
251             sout_stream_t *out = p_sys->pp_streams[i_stream];
252             sout_StreamIdDel( out, id->pp_ids[i_stream] );
253         }
254     }
255
256     free( id->pp_ids );
257     free( id );
258     return VLC_SUCCESS;
259 }
260
261 /*****************************************************************************
262  * Send:
263  *****************************************************************************/
264 static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
265                  block_t *p_buffer )
266 {
267     sout_stream_sys_t *p_sys = p_stream->p_sys;
268     sout_stream_t     *p_dup_stream;
269     int               i_stream;
270
271     /* Loop through the linked list of buffers */
272     while( p_buffer )
273     {
274         block_t *p_next = p_buffer->p_next;
275
276         p_buffer->p_next = NULL;
277
278         for( i_stream = 0; i_stream < p_sys->i_nb_streams - 1; i_stream++ )
279         {
280             p_dup_stream = p_sys->pp_streams[i_stream];
281
282             if( id->pp_ids[i_stream] )
283             {
284                 block_t *p_dup = block_Duplicate( p_buffer );
285
286                 if( p_dup )
287                     sout_StreamIdSend( p_dup_stream, id->pp_ids[i_stream], p_dup );
288             }
289         }
290
291         if( i_stream < p_sys->i_nb_streams && id->pp_ids[i_stream] )
292         {
293             p_dup_stream = p_sys->pp_streams[i_stream];
294             sout_StreamIdSend( p_dup_stream, id->pp_ids[i_stream], p_buffer );
295         }
296         else
297         {
298             block_Release( p_buffer );
299         }
300
301         p_buffer = p_next;
302     }
303     return VLC_SUCCESS;
304 }
305
306 /*****************************************************************************
307  * Divers
308  *****************************************************************************/
309 static bool NumInRange( const char *psz_range, int i_num )
310 {
311     const char *psz = strchr( psz_range, '-' );
312     char *end;
313     int  i_start, i_stop;
314
315     i_start = strtol( psz_range, &end, 0 );
316     if( end == psz_range )
317         i_start = i_num;
318
319     if( psz )
320     {
321         psz++;
322         i_stop = strtol( psz, &end, 0 );
323         if( end == psz )
324             i_stop = i_num;
325     }
326     else
327         i_stop = i_start;
328
329     return i_start <= i_num && i_num <= i_stop;
330 }
331
332 static bool ESSelected( es_format_t *fmt, char *psz_select )
333 {
334     char  *psz_dup;
335     char  *psz;
336
337     /* We have tri-state variable : no tested (-1), failed(0), succeed(1) */
338     int i_cat = -1;
339     int i_es  = -1;
340     int i_prgm= -1;
341
342     /* If empty all es are selected */
343     if( psz_select == NULL || *psz_select == '\0' )
344     {
345         return true;
346     }
347     psz_dup = strdup( psz_select );
348     if( !psz_dup )
349         return false;
350     psz = psz_dup;
351
352     /* If non empty, parse the selection:
353      * We have selection[,selection[,..]] where following selection are recognized:
354      *      (no(-))audio
355      *      (no(-))spu
356      *      (no(-))video
357      *      (no(-))es=[start]-[end] or es=num
358      *      (no(-))prgm=[start]-[end] or prgm=num (program works too)
359      *      if a negative test failed we exit directly
360      */
361     while( psz && *psz )
362     {
363         char *p;
364
365         /* Skip space */
366         while( *psz == ' ' || *psz == '\t' ) psz++;
367
368         /* Search end */
369         p = strchr( psz, ',' );
370         if( p == psz )
371         {
372             /* Empty */
373             psz = p + 1;
374             continue;
375         }
376         if( p )
377         {
378             *p++ = '\0';
379         }
380
381         if( !strncmp( psz, "no-audio", strlen( "no-audio" ) ) ||
382             !strncmp( psz, "noaudio", strlen( "noaudio" ) ) )
383         {
384             if( i_cat == -1 )
385             {
386                 i_cat = fmt->i_cat != AUDIO_ES ? 1 : 0;
387             }
388         }
389         else if( !strncmp( psz, "no-video", strlen( "no-video" ) ) ||
390                  !strncmp( psz, "novideo", strlen( "novideo" ) ) )
391         {
392             if( i_cat == -1 )
393             {
394                 i_cat = fmt->i_cat != VIDEO_ES ? 1 : 0;
395             }
396         }
397         else if( !strncmp( psz, "no-spu", strlen( "no-spu" ) ) ||
398                  !strncmp( psz, "nospu", strlen( "nospu" ) ) )
399         {
400             if( i_cat == -1 )
401             {
402                 i_cat = fmt->i_cat != SPU_ES ? 1 : 0;
403             }
404         }
405         else if( !strncmp( psz, "audio", strlen( "audio" ) ) )
406         {
407             if( i_cat == -1 )
408             {
409                 i_cat = fmt->i_cat == AUDIO_ES ? 1 : 0;
410             }
411         }
412         else if( !strncmp( psz, "video", strlen( "video" ) ) )
413         {
414             if( i_cat == -1 )
415             {
416                 i_cat = fmt->i_cat == VIDEO_ES ? 1 : 0;
417             }
418         }
419         else if( !strncmp( psz, "spu", strlen( "spu" ) ) )
420         {
421             if( i_cat == -1 )
422             {
423                 i_cat = fmt->i_cat == SPU_ES ? 1 : 0;
424             }
425         }
426         else if( strchr( psz, '=' ) != NULL )
427         {
428             char *psz_arg = strchr( psz, '=' );
429             *psz_arg++ = '\0';
430
431             if( !strcmp( psz, "no-es" ) || !strcmp( psz, "noes" ) )
432             {
433                 if( i_es == -1 )
434                 {
435                     i_es = NumInRange( psz_arg, fmt->i_id ) ? 0 : -1;
436                 }
437             }
438             else if( !strcmp( psz, "es" ) )
439             {
440                 if( i_es == -1 )
441                 {
442                     i_es = NumInRange( psz_arg, fmt->i_id) ? 1 : -1;
443                 }
444             }
445             else if( !strcmp( psz, "no-prgm" ) || !strcmp( psz, "noprgm" ) ||
446                       !strcmp( psz, "no-program" ) || !strcmp( psz, "noprogram" ) )
447             {
448                 if( fmt->i_group >= 0 && i_prgm == -1 )
449                 {
450                     i_prgm = NumInRange( psz_arg, fmt->i_group ) ? 0 : -1;
451                 }
452             }
453             else if( !strcmp( psz, "prgm" ) || !strcmp( psz, "program" ) )
454             {
455                 if( fmt->i_group >= 0 && i_prgm == -1 )
456                 {
457                     i_prgm = NumInRange( psz_arg, fmt->i_group ) ? 1 : -1;
458                 }
459             }
460         }
461         else
462         {
463             fprintf( stderr, "unknown args (%s)\n", psz );
464         }
465         /* Next */
466         psz = p;
467     }
468
469     free( psz_dup );
470
471     if( i_cat == 1 || i_es == 1 || i_prgm == 1 )
472     {
473         return true;
474     }
475     return false;
476 }