demux: ogg: rename field
[vlc.git] / modules / stream_out / duplicate.c
blob916b264b9218f0e6c094ecdc89d6190a36e4708d
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 stream", 50 )
45 add_shortcut( "duplicate", "dup" )
46 set_category( CAT_SOUT )
47 set_subcategory( SUBCAT_SOUT_STREAM )
48 set_callbacks( Open, Close )
49 vlc_module_end ()
52 /*****************************************************************************
53 * Exported prototypes
54 *****************************************************************************/
55 static void *Add( sout_stream_t *, const es_format_t * );
56 static void Del( sout_stream_t *, void * );
57 static int Send( sout_stream_t *, void *, block_t * );
59 typedef struct
61 int i_nb_streams;
62 sout_stream_t **pp_streams;
64 int i_nb_last_streams;
65 sout_stream_t **pp_last_streams;
67 int i_nb_select;
68 char **ppsz_select;
69 } sout_stream_sys_t;
71 typedef struct
73 int i_nb_ids;
74 void **pp_ids;
75 } sout_stream_id_sys_t;
77 static bool ESSelected( const es_format_t *fmt, char *psz_select );
79 /*****************************************************************************
80 * Control
81 *****************************************************************************/
82 static int Control( sout_stream_t *p_stream, int i_query, va_list args )
84 sout_stream_sys_t *p_sys = p_stream->p_sys;
86 /* Fanout controls */
87 switch( i_query )
89 case SOUT_STREAM_ID_SPU_HIGHLIGHT:
91 sout_stream_id_sys_t *id = va_arg(args, void *);
92 void *spu_hl = va_arg(args, void *);
93 for( int i = 0; i < id->i_nb_ids; i++ )
95 if( id->pp_ids[i] )
96 sout_StreamControl( p_sys->pp_streams[i], i_query,
97 id->pp_ids[i], spu_hl );
99 return VLC_SUCCESS;
103 return VLC_EGENERIC;
106 /*****************************************************************************
107 * Open:
108 *****************************************************************************/
109 static int Open( vlc_object_t *p_this )
111 sout_stream_t *p_stream = (sout_stream_t*)p_this;
112 sout_stream_sys_t *p_sys;
113 config_chain_t *p_cfg;
115 msg_Dbg( p_stream, "creating 'duplicate'" );
117 p_sys = malloc( sizeof( sout_stream_sys_t ) );
118 if( !p_sys )
119 return VLC_ENOMEM;
121 TAB_INIT( p_sys->i_nb_streams, p_sys->pp_streams );
122 TAB_INIT( p_sys->i_nb_last_streams, p_sys->pp_last_streams );
123 TAB_INIT( p_sys->i_nb_select, p_sys->ppsz_select );
125 for( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )
127 if( !strncmp( p_cfg->psz_name, "dst", strlen( "dst" ) ) )
129 sout_stream_t *s, *p_last;
131 msg_Dbg( p_stream, " * adding `%s'", p_cfg->psz_value );
132 s = sout_StreamChainNew( p_stream->p_sout, p_cfg->psz_value,
133 p_stream->p_next, &p_last );
135 if( s )
137 TAB_APPEND( p_sys->i_nb_streams, p_sys->pp_streams, s );
138 TAB_APPEND( p_sys->i_nb_last_streams, p_sys->pp_last_streams,
139 p_last );
140 TAB_APPEND( p_sys->i_nb_select, p_sys->ppsz_select, NULL );
143 else if( !strncmp( p_cfg->psz_name, "select", strlen( "select" ) ) )
145 char *psz = p_cfg->psz_value;
146 if( p_sys->i_nb_select > 0 && psz && *psz )
148 char **ppsz_select = &p_sys->ppsz_select[p_sys->i_nb_select - 1];
150 if( *ppsz_select )
152 msg_Err( p_stream, " * ignore selection `%s' (it already has `%s')",
153 psz, *ppsz_select );
155 else
157 msg_Dbg( p_stream, " * apply selection `%s'", psz );
158 *ppsz_select = strdup( psz );
162 else
164 msg_Err( p_stream, " * ignore unknown option `%s'", p_cfg->psz_name );
168 if( p_sys->i_nb_streams == 0 )
170 msg_Err( p_stream, "no destination given" );
171 free( p_sys );
173 return VLC_EGENERIC;
176 p_stream->pf_add = Add;
177 p_stream->pf_del = Del;
178 p_stream->pf_send = Send;
179 p_stream->pf_control = Control;
181 p_stream->p_sys = p_sys;
183 return VLC_SUCCESS;
186 /*****************************************************************************
187 * Close:
188 *****************************************************************************/
189 static void Close( vlc_object_t * p_this )
191 sout_stream_t *p_stream = (sout_stream_t*)p_this;
192 sout_stream_sys_t *p_sys = p_stream->p_sys;
194 msg_Dbg( p_stream, "closing a duplication" );
195 for( int i = 0; i < p_sys->i_nb_streams; i++ )
197 sout_StreamChainDelete(p_sys->pp_streams[i], p_sys->pp_last_streams[i]);
198 free( p_sys->ppsz_select[i] );
200 free( p_sys->pp_streams );
201 free( p_sys->pp_last_streams );
202 free( p_sys->ppsz_select );
204 free( p_sys );
207 /*****************************************************************************
208 * Add:
209 *****************************************************************************/
210 static void *Add( sout_stream_t *p_stream, const es_format_t *p_fmt )
212 sout_stream_sys_t *p_sys = p_stream->p_sys;
213 sout_stream_id_sys_t *id;
214 int i_stream, i_valid_streams = 0;
216 id = malloc( sizeof( sout_stream_id_sys_t ) );
217 if( !id )
218 return NULL;
220 TAB_INIT( id->i_nb_ids, id->pp_ids );
222 msg_Dbg( p_stream, "duplicated a new stream codec=%4.4s (es=%d group=%d)",
223 (char*)&p_fmt->i_codec, p_fmt->i_id, p_fmt->i_group );
225 for( i_stream = 0; i_stream < p_sys->i_nb_streams; i_stream++ )
227 void *id_new = NULL;
229 if( ESSelected( p_fmt, p_sys->ppsz_select[i_stream] ) )
231 sout_stream_t *out = p_sys->pp_streams[i_stream];
233 id_new = (void*)sout_StreamIdAdd( out, p_fmt );
234 if( id_new )
236 msg_Dbg( p_stream, " - added for output %d", i_stream );
237 i_valid_streams++;
239 else
241 msg_Dbg( p_stream, " - failed for output %d", i_stream );
244 else
246 msg_Dbg( p_stream, " - ignored for output %d", i_stream );
249 /* Append failed attempts as well to keep track of which pp_id
250 * belongs to which duplicated stream */
251 TAB_APPEND( id->i_nb_ids, id->pp_ids, id_new );
254 if( i_valid_streams <= 0 )
256 Del( p_stream, id );
257 return NULL;
260 return id;
263 /*****************************************************************************
264 * Del:
265 *****************************************************************************/
266 static void Del( sout_stream_t *p_stream, void *_id )
268 sout_stream_sys_t *p_sys = p_stream->p_sys;
269 sout_stream_id_sys_t *id = (sout_stream_id_sys_t *)_id;
270 int i_stream;
272 for( i_stream = 0; i_stream < p_sys->i_nb_streams; i_stream++ )
274 if( id->pp_ids[i_stream] )
276 sout_stream_t *out = p_sys->pp_streams[i_stream];
277 sout_StreamIdDel( out, id->pp_ids[i_stream] );
281 free( id->pp_ids );
282 free( id );
285 /*****************************************************************************
286 * Send:
287 *****************************************************************************/
288 static int Send( sout_stream_t *p_stream, void *_id, block_t *p_buffer )
290 sout_stream_sys_t *p_sys = p_stream->p_sys;
291 sout_stream_id_sys_t *id = (sout_stream_id_sys_t *)_id;
292 sout_stream_t *p_dup_stream;
293 int i_stream;
295 /* Loop through the linked list of buffers */
296 while( p_buffer )
298 block_t *p_next = p_buffer->p_next;
300 p_buffer->p_next = NULL;
302 for( i_stream = 0; i_stream < p_sys->i_nb_streams - 1; i_stream++ )
304 p_dup_stream = p_sys->pp_streams[i_stream];
306 if( id->pp_ids[i_stream] )
308 block_t *p_dup = block_Duplicate( p_buffer );
310 if( p_dup )
311 sout_StreamIdSend( p_dup_stream, id->pp_ids[i_stream], p_dup );
315 if( i_stream < p_sys->i_nb_streams && id->pp_ids[i_stream] )
317 p_dup_stream = p_sys->pp_streams[i_stream];
318 sout_StreamIdSend( p_dup_stream, id->pp_ids[i_stream], p_buffer );
320 else
322 block_Release( p_buffer );
325 p_buffer = p_next;
327 return VLC_SUCCESS;
330 /*****************************************************************************
331 * Divers
332 *****************************************************************************/
333 static bool NumInRange( const char *psz_range, int i_num )
335 int beginRange, endRange;
336 int res = sscanf(psz_range, "%d-%d", &beginRange, &endRange);
337 if (res == 0)
338 return false;
339 else if (res == 1)
340 return beginRange == i_num;
341 return (i_num >= beginRange && i_num <= endRange)
342 || (beginRange > endRange && (i_num <= beginRange && i_num >= endRange));
345 static bool ESSelected( const es_format_t *fmt, char *psz_select )
347 char *psz_dup;
348 char *psz;
350 /* We have tri-state variable : no tested (-1), failed(0), succeed(1) */
351 int i_cat = -1;
352 int i_es = -1;
353 int i_prgm= -1;
355 /* If empty all es are selected */
356 if( psz_select == NULL || *psz_select == '\0' )
358 return true;
360 psz_dup = strdup( psz_select );
361 if( !psz_dup )
362 return false;
363 psz = psz_dup;
365 /* If non empty, parse the selection:
366 * We have selection[,selection[,..]] where following selection are recognized:
367 * (no(-))audio
368 * (no(-))spu
369 * (no(-))video
370 * (no(-))es=[start]-[end] or es=num
371 * (no(-))prgm=[start]-[end] or prgm=num (program works too)
372 * if a negative test failed we exit directly
374 while( psz && *psz )
376 char *p;
378 /* Skip space */
379 while( *psz == ' ' || *psz == '\t' ) psz++;
381 /* Search end */
382 p = strchr( psz, ',' );
383 if( p == psz )
385 /* Empty */
386 psz = p + 1;
387 continue;
389 if( p )
391 *p++ = '\0';
394 if( !strncmp( psz, "no-audio", strlen( "no-audio" ) ) ||
395 !strncmp( psz, "noaudio", strlen( "noaudio" ) ) )
397 if( i_cat == -1 )
399 i_cat = fmt->i_cat != AUDIO_ES ? 1 : 0;
402 else if( !strncmp( psz, "no-video", strlen( "no-video" ) ) ||
403 !strncmp( psz, "novideo", strlen( "novideo" ) ) )
405 if( i_cat == -1 )
407 i_cat = fmt->i_cat != VIDEO_ES ? 1 : 0;
410 else if( !strncmp( psz, "no-spu", strlen( "no-spu" ) ) ||
411 !strncmp( psz, "nospu", strlen( "nospu" ) ) )
413 if( i_cat == -1 )
415 i_cat = fmt->i_cat != SPU_ES ? 1 : 0;
418 else if( !strncmp( psz, "audio", strlen( "audio" ) ) )
420 if( i_cat == -1 )
422 i_cat = fmt->i_cat == AUDIO_ES ? 1 : 0;
425 else if( !strncmp( psz, "video", strlen( "video" ) ) )
427 if( i_cat == -1 )
429 i_cat = fmt->i_cat == VIDEO_ES ? 1 : 0;
432 else if( !strncmp( psz, "spu", strlen( "spu" ) ) )
434 if( i_cat == -1 )
436 i_cat = fmt->i_cat == SPU_ES ? 1 : 0;
439 else if( strchr( psz, '=' ) != NULL )
441 char *psz_arg = strchr( psz, '=' );
442 *psz_arg++ = '\0';
444 if( !strcmp( psz, "no-es" ) || !strcmp( psz, "noes" ) )
446 if( i_es == -1 )
448 i_es = NumInRange( psz_arg, fmt->i_id ) ? 0 : -1;
451 else if( !strcmp( psz, "es" ) )
453 if( i_es == -1 )
455 i_es = NumInRange( psz_arg, fmt->i_id) ? 1 : -1;
458 else if( !strcmp( psz, "no-prgm" ) || !strcmp( psz, "noprgm" ) ||
459 !strcmp( psz, "no-program" ) || !strcmp( psz, "noprogram" ) )
461 if( fmt->i_group >= 0 && i_prgm == -1 )
463 i_prgm = NumInRange( psz_arg, fmt->i_group ) ? 0 : -1;
466 else if( !strcmp( psz, "prgm" ) || !strcmp( psz, "program" ) )
468 if( fmt->i_group >= 0 && i_prgm == -1 )
470 i_prgm = NumInRange( psz_arg, fmt->i_group ) ? 1 : -1;
474 else
476 fprintf( stderr, "unknown args (%s)\n", psz );
478 /* Next */
479 psz = p;
482 free( psz_dup );
484 if( i_cat == 1 || i_es == 1 || i_prgm == 1 )
486 return true;
488 return false;