duplicate: simplify select= handling
[vlc.git] / modules / stream_out / duplicate.c
blob63e186ee35af07c17df0e096dff6e9677fce80e1
1 /*****************************************************************************
2 * duplicate.c: duplicate stream output module
3 *****************************************************************************
4 * Copyright (C) 2003-2004 the VideoLAN team
6 * Author: Laurent Aimar <fenrir@via.ecp.fr>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 /*****************************************************************************
24 * Preamble
25 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_sout.h>
34 #include <vlc_block.h>
36 /*****************************************************************************
37 * Module descriptor
38 *****************************************************************************/
39 static int Open ( vlc_object_t * );
40 static void Close ( vlc_object_t * );
42 vlc_module_begin ()
43 set_description( N_("Duplicate stream output") )
44 set_capability( "sout output", 50 )
45 add_shortcut( "duplicate", "dup" )
46 set_category( CAT_SOUT )
47 set_subcategory( SUBCAT_SOUT_STREAM )
48 set_callbacks( Open, Close )
49 add_submodule()
50 set_capability("sout filter", 0)
51 add_shortcut("duplicate", "dup")
52 set_callbacks(Open, Close)
53 vlc_module_end ()
56 /*****************************************************************************
57 * Exported prototypes
58 *****************************************************************************/
59 static void *Add( sout_stream_t *, const es_format_t * );
60 static void Del( sout_stream_t *, void * );
61 static int Send( sout_stream_t *, void *, block_t * );
63 typedef struct
65 int i_nb_streams;
66 sout_stream_t **pp_streams;
68 int i_nb_last_streams;
69 sout_stream_t **pp_last_streams;
71 int i_nb_select;
72 char **ppsz_select;
73 } sout_stream_sys_t;
75 typedef struct
77 int i_nb_ids;
78 void **pp_ids;
79 } sout_stream_id_sys_t;
81 static bool ESSelected( struct vlc_logger *, const es_format_t *fmt,
82 char *psz_select );
84 /*****************************************************************************
85 * Control
86 *****************************************************************************/
87 static int Control( sout_stream_t *p_stream, int i_query, va_list args )
89 sout_stream_sys_t *p_sys = p_stream->p_sys;
91 /* Fanout controls */
92 switch( i_query )
94 case SOUT_STREAM_ID_SPU_HIGHLIGHT:
96 sout_stream_id_sys_t *id = va_arg(args, void *);
97 void *spu_hl = va_arg(args, void *);
98 for( int i = 0; i < id->i_nb_ids; i++ )
100 if( id->pp_ids[i] )
101 sout_StreamControl( p_sys->pp_streams[i], i_query,
102 id->pp_ids[i], spu_hl );
104 return VLC_SUCCESS;
108 return VLC_EGENERIC;
111 static const struct sout_stream_operations ops = {
112 Add, Del, Send, Control, NULL,
115 /*****************************************************************************
116 * Open:
117 *****************************************************************************/
118 static int Open( vlc_object_t *p_this )
120 sout_stream_t *p_stream = (sout_stream_t*)p_this;
121 sout_stream_sys_t *p_sys;
122 config_chain_t *p_cfg;
124 msg_Dbg( p_stream, "creating 'duplicate'" );
126 p_sys = malloc( sizeof( sout_stream_sys_t ) );
127 if( !p_sys )
128 return VLC_ENOMEM;
130 TAB_INIT( p_sys->i_nb_streams, p_sys->pp_streams );
131 TAB_INIT( p_sys->i_nb_last_streams, p_sys->pp_last_streams );
132 TAB_INIT( p_sys->i_nb_select, p_sys->ppsz_select );
134 char **ppsz_select = NULL;
136 for( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )
138 if( !strncmp( p_cfg->psz_name, "dst", strlen( "dst" ) ) )
140 sout_stream_t *s, *p_last;
142 msg_Dbg( p_stream, " * adding `%s'", p_cfg->psz_value );
143 s = sout_StreamChainNew( VLC_OBJECT(p_stream), p_cfg->psz_value,
144 p_stream->p_next, &p_last );
146 if( s )
148 TAB_APPEND( p_sys->i_nb_streams, p_sys->pp_streams, s );
149 TAB_APPEND( p_sys->i_nb_last_streams, p_sys->pp_last_streams,
150 p_last );
151 TAB_APPEND( p_sys->i_nb_select, p_sys->ppsz_select, NULL );
152 ppsz_select = &p_sys->ppsz_select[p_sys->i_nb_select - 1];
155 else if( !strncmp( p_cfg->psz_name, "select", strlen( "select" ) ) )
157 char *psz = p_cfg->psz_value;
159 if( psz && *psz )
161 if( ppsz_select == NULL )
163 msg_Err( p_stream, " * ignore selection `%s'", psz );
165 else
167 msg_Dbg( p_stream, " * apply selection `%s'", psz );
168 *ppsz_select = strdup( psz );
169 ppsz_select = NULL;
173 else
175 msg_Err( p_stream, " * ignore unknown option `%s'", p_cfg->psz_name );
179 if( p_sys->i_nb_streams == 0 )
181 msg_Err( p_stream, "no destination given" );
182 free( p_sys );
184 return VLC_EGENERIC;
187 p_stream->p_sys = p_sys;
188 p_stream->ops = &ops;
189 return VLC_SUCCESS;
192 /*****************************************************************************
193 * Close:
194 *****************************************************************************/
195 static void Close( vlc_object_t * p_this )
197 sout_stream_t *p_stream = (sout_stream_t*)p_this;
198 sout_stream_sys_t *p_sys = p_stream->p_sys;
200 msg_Dbg( p_stream, "closing a duplication" );
201 for( int i = 0; i < p_sys->i_nb_streams; i++ )
203 sout_StreamChainDelete(p_sys->pp_streams[i], p_sys->pp_last_streams[i]);
204 free( p_sys->ppsz_select[i] );
206 free( p_sys->pp_streams );
207 free( p_sys->pp_last_streams );
208 free( p_sys->ppsz_select );
210 free( p_sys );
213 /*****************************************************************************
214 * Add:
215 *****************************************************************************/
216 static void *Add( sout_stream_t *p_stream, const es_format_t *p_fmt )
218 sout_stream_sys_t *p_sys = p_stream->p_sys;
219 sout_stream_id_sys_t *id;
220 int i_stream, i_valid_streams = 0;
222 id = malloc( sizeof( sout_stream_id_sys_t ) );
223 if( !id )
224 return NULL;
226 TAB_INIT( id->i_nb_ids, id->pp_ids );
228 msg_Dbg( p_stream, "duplicated a new stream codec=%4.4s (es=%d group=%d)",
229 (char*)&p_fmt->i_codec, p_fmt->i_id, p_fmt->i_group );
231 for( i_stream = 0; i_stream < p_sys->i_nb_streams; i_stream++ )
233 void *id_new = NULL;
235 if( ESSelected( p_stream->obj.logger, p_fmt,
236 p_sys->ppsz_select[i_stream] ) )
238 sout_stream_t *out = p_sys->pp_streams[i_stream];
240 id_new = (void*)sout_StreamIdAdd( out, p_fmt );
241 if( id_new )
243 msg_Dbg( p_stream, " - added for output %d", i_stream );
244 i_valid_streams++;
246 else
248 msg_Dbg( p_stream, " - failed for output %d", i_stream );
251 else
253 msg_Dbg( p_stream, " - ignored for output %d", i_stream );
256 /* Append failed attempts as well to keep track of which pp_id
257 * belongs to which duplicated stream */
258 TAB_APPEND( id->i_nb_ids, id->pp_ids, id_new );
261 if( i_valid_streams <= 0 )
263 Del( p_stream, id );
264 return NULL;
267 return id;
270 /*****************************************************************************
271 * Del:
272 *****************************************************************************/
273 static void Del( sout_stream_t *p_stream, void *_id )
275 sout_stream_sys_t *p_sys = p_stream->p_sys;
276 sout_stream_id_sys_t *id = (sout_stream_id_sys_t *)_id;
277 int i_stream;
279 for( i_stream = 0; i_stream < p_sys->i_nb_streams; i_stream++ )
281 if( id->pp_ids[i_stream] )
283 sout_stream_t *out = p_sys->pp_streams[i_stream];
284 sout_StreamIdDel( out, id->pp_ids[i_stream] );
288 free( id->pp_ids );
289 free( id );
292 /*****************************************************************************
293 * Send:
294 *****************************************************************************/
295 static int Send( sout_stream_t *p_stream, void *_id, block_t *p_buffer )
297 sout_stream_sys_t *p_sys = p_stream->p_sys;
298 sout_stream_id_sys_t *id = (sout_stream_id_sys_t *)_id;
299 sout_stream_t *p_dup_stream;
300 int i_stream;
302 /* Loop through the linked list of buffers */
303 while( p_buffer )
305 block_t *p_next = p_buffer->p_next;
307 p_buffer->p_next = NULL;
309 for( i_stream = 0; i_stream < p_sys->i_nb_streams - 1; i_stream++ )
311 p_dup_stream = p_sys->pp_streams[i_stream];
313 if( id->pp_ids[i_stream] )
315 block_t *p_dup = block_Duplicate( p_buffer );
317 if( p_dup )
318 sout_StreamIdSend( p_dup_stream, id->pp_ids[i_stream], p_dup );
322 if( i_stream < p_sys->i_nb_streams && id->pp_ids[i_stream] )
324 p_dup_stream = p_sys->pp_streams[i_stream];
325 sout_StreamIdSend( p_dup_stream, id->pp_ids[i_stream], p_buffer );
327 else
329 block_Release( p_buffer );
332 p_buffer = p_next;
334 return VLC_SUCCESS;
337 /*****************************************************************************
338 * Divers
339 *****************************************************************************/
340 static bool NumInRange( const char *psz_range, int i_num )
342 int beginRange, endRange;
343 int res = sscanf(psz_range, "%d-%d", &beginRange, &endRange);
344 if (res == 0)
345 return false;
346 else if (res == 1)
347 return beginRange == i_num;
348 return (i_num >= beginRange && i_num <= endRange)
349 || (beginRange > endRange && (i_num <= beginRange && i_num >= endRange));
352 static bool ESSelected( struct vlc_logger *logger, const es_format_t *fmt,
353 char *psz_select )
355 char *psz_dup;
356 char *psz;
358 /* We have tri-state variable : no tested (-1), failed(0), succeed(1) */
359 int i_cat = -1;
360 int i_es = -1;
361 int i_prgm= -1;
363 /* If empty all es are selected */
364 if( psz_select == NULL || *psz_select == '\0' )
366 return true;
368 psz_dup = strdup( psz_select );
369 if( !psz_dup )
370 return false;
371 psz = psz_dup;
373 /* If non empty, parse the selection:
374 * We have selection[,selection[,..]] where following selection are recognized:
375 * (no(-))audio
376 * (no(-))spu
377 * (no(-))video
378 * (no(-))es=[start]-[end] or es=num
379 * (no(-))prgm=[start]-[end] or prgm=num (program works too)
380 * if a negative test failed we exit directly
382 while( psz && *psz )
384 char *p;
386 /* Skip space */
387 while( *psz == ' ' || *psz == '\t' ) psz++;
389 /* Search end */
390 p = strchr( psz, ',' );
391 if( p == psz )
393 /* Empty */
394 psz = p + 1;
395 continue;
397 if( p )
399 *p++ = '\0';
402 if( !strncmp( psz, "no-audio", strlen( "no-audio" ) ) ||
403 !strncmp( psz, "noaudio", strlen( "noaudio" ) ) )
405 if( i_cat == -1 )
407 i_cat = fmt->i_cat != AUDIO_ES ? 1 : 0;
410 else if( !strncmp( psz, "no-video", strlen( "no-video" ) ) ||
411 !strncmp( psz, "novideo", strlen( "novideo" ) ) )
413 if( i_cat == -1 )
415 i_cat = fmt->i_cat != VIDEO_ES ? 1 : 0;
418 else if( !strncmp( psz, "no-spu", strlen( "no-spu" ) ) ||
419 !strncmp( psz, "nospu", strlen( "nospu" ) ) )
421 if( i_cat == -1 )
423 i_cat = fmt->i_cat != SPU_ES ? 1 : 0;
426 else if( !strncmp( psz, "audio", strlen( "audio" ) ) )
428 if( i_cat == -1 )
430 i_cat = fmt->i_cat == AUDIO_ES ? 1 : 0;
433 else if( !strncmp( psz, "video", strlen( "video" ) ) )
435 if( i_cat == -1 )
437 i_cat = fmt->i_cat == VIDEO_ES ? 1 : 0;
440 else if( !strncmp( psz, "spu", strlen( "spu" ) ) )
442 if( i_cat == -1 )
444 i_cat = fmt->i_cat == SPU_ES ? 1 : 0;
447 else if( strchr( psz, '=' ) != NULL )
449 char *psz_arg = strchr( psz, '=' );
450 *psz_arg++ = '\0';
452 if( !strcmp( psz, "no-es" ) || !strcmp( psz, "noes" ) )
454 if( i_es == -1 )
456 i_es = NumInRange( psz_arg, fmt->i_id ) ? 0 : -1;
459 else if( !strcmp( psz, "es" ) )
461 if( i_es == -1 )
463 i_es = NumInRange( psz_arg, fmt->i_id) ? 1 : -1;
466 else if( !strcmp( psz, "no-prgm" ) || !strcmp( psz, "noprgm" ) ||
467 !strcmp( psz, "no-program" ) || !strcmp( psz, "noprogram" ) )
469 if( fmt->i_group >= 0 && i_prgm == -1 )
471 i_prgm = NumInRange( psz_arg, fmt->i_group ) ? 0 : -1;
474 else if( !strcmp( psz, "prgm" ) || !strcmp( psz, "program" ) )
476 if( fmt->i_group >= 0 && i_prgm == -1 )
478 i_prgm = NumInRange( psz_arg, fmt->i_group ) ? 1 : -1;
482 else
484 vlc_error( logger, "unknown args (%s)", psz );
486 /* Next */
487 psz = p;
490 free( psz_dup );
492 if( i_cat == 1 || i_es == 1 || i_prgm == 1 )
494 return true;
496 return false;