1 /*****************************************************************************
2 * duplicate.c: duplicate stream output module
3 *****************************************************************************
4 * Copyright (C) 2003-2004 the VideoLAN team
7 * Author: Laurent Aimar <fenrir@via.ecp.fr>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
35 #include <vlc_block.h>
37 /*****************************************************************************
39 *****************************************************************************/
40 static int Open ( vlc_object_t
* );
41 static void Close ( vlc_object_t
* );
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
)
53 /*****************************************************************************
55 *****************************************************************************/
56 static sout_stream_id_sys_t
*Add ( sout_stream_t
*, es_format_t
* );
57 static int Del ( sout_stream_t
*, sout_stream_id_sys_t
* );
58 static int Send( sout_stream_t
*, sout_stream_id_sys_t
*,
61 struct sout_stream_sys_t
64 sout_stream_t
**pp_streams
;
66 int i_nb_last_streams
;
67 sout_stream_t
**pp_last_streams
;
73 struct sout_stream_id_sys_t
79 static bool ESSelected( es_format_t
*fmt
, char *psz_select
);
81 /*****************************************************************************
83 *****************************************************************************/
84 static int Open( vlc_object_t
*p_this
)
86 sout_stream_t
*p_stream
= (sout_stream_t
*)p_this
;
87 sout_stream_sys_t
*p_sys
;
88 config_chain_t
*p_cfg
;
90 msg_Dbg( p_stream
, "creating 'duplicate'" );
92 p_sys
= malloc( sizeof( sout_stream_sys_t
) );
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
);
100 for( p_cfg
= p_stream
->p_cfg
; p_cfg
!= NULL
; p_cfg
= p_cfg
->p_next
)
102 if( !strncmp( p_cfg
->psz_name
, "dst", strlen( "dst" ) ) )
104 sout_stream_t
*s
, *p_last
;
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
);
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
,
115 TAB_APPEND( p_sys
->i_nb_select
, p_sys
->ppsz_select
, NULL
);
118 else if( !strncmp( p_cfg
->psz_name
, "select", strlen( "select" ) ) )
120 char *psz
= p_cfg
->psz_value
;
121 if( p_sys
->i_nb_select
> 0 && psz
&& *psz
)
123 char **ppsz_select
= &p_sys
->ppsz_select
[p_sys
->i_nb_select
- 1];
127 msg_Err( p_stream
, " * ignore selection `%s' (it already has `%s')",
132 msg_Dbg( p_stream
, " * apply selection `%s'", psz
);
133 *ppsz_select
= strdup( psz
);
139 msg_Err( p_stream
, " * ignore unknown option `%s'", p_cfg
->psz_name
);
143 if( p_sys
->i_nb_streams
== 0 )
145 msg_Err( p_stream
, "no destination given" );
151 p_stream
->pf_add
= Add
;
152 p_stream
->pf_del
= Del
;
153 p_stream
->pf_send
= Send
;
155 p_stream
->p_sys
= p_sys
;
160 /*****************************************************************************
162 *****************************************************************************/
163 static void Close( vlc_object_t
* p_this
)
165 sout_stream_t
*p_stream
= (sout_stream_t
*)p_this
;
166 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
170 msg_Dbg( p_stream
, "closing a duplication" );
171 for( i
= 0; i
< p_sys
->i_nb_streams
; i
++ )
173 sout_StreamChainDelete(p_sys
->pp_streams
[i
], p_sys
->pp_last_streams
[i
]);
174 free( p_sys
->ppsz_select
[i
] );
176 free( p_sys
->pp_streams
);
177 free( p_sys
->pp_last_streams
);
178 free( p_sys
->ppsz_select
);
183 /*****************************************************************************
185 *****************************************************************************/
186 static sout_stream_id_sys_t
* Add( sout_stream_t
*p_stream
, es_format_t
*p_fmt
)
188 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
189 sout_stream_id_sys_t
*id
;
190 int i_stream
, i_valid_streams
= 0;
192 id
= malloc( sizeof( sout_stream_id_sys_t
) );
196 TAB_INIT( id
->i_nb_ids
, id
->pp_ids
);
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
);
201 for( i_stream
= 0; i_stream
< p_sys
->i_nb_streams
; i_stream
++ )
205 if( ESSelected( p_fmt
, p_sys
->ppsz_select
[i_stream
] ) )
207 sout_stream_t
*out
= p_sys
->pp_streams
[i_stream
];
209 id_new
= (void*)sout_StreamIdAdd( out
, p_fmt
);
212 msg_Dbg( p_stream
, " - added for output %d", i_stream
);
217 msg_Dbg( p_stream
, " - failed for output %d", i_stream
);
222 msg_Dbg( p_stream
, " - ignored for output %d", i_stream
);
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
);
230 if( i_valid_streams
<= 0 )
239 /*****************************************************************************
241 *****************************************************************************/
242 static int Del( sout_stream_t
*p_stream
, sout_stream_id_sys_t
*id
)
244 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
247 for( i_stream
= 0; i_stream
< p_sys
->i_nb_streams
; i_stream
++ )
249 if( id
->pp_ids
[i_stream
] )
251 sout_stream_t
*out
= p_sys
->pp_streams
[i_stream
];
252 sout_StreamIdDel( out
, id
->pp_ids
[i_stream
] );
261 /*****************************************************************************
263 *****************************************************************************/
264 static int Send( sout_stream_t
*p_stream
, sout_stream_id_sys_t
*id
,
267 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
268 sout_stream_t
*p_dup_stream
;
271 /* Loop through the linked list of buffers */
274 block_t
*p_next
= p_buffer
->p_next
;
276 p_buffer
->p_next
= NULL
;
278 for( i_stream
= 0; i_stream
< p_sys
->i_nb_streams
- 1; i_stream
++ )
280 p_dup_stream
= p_sys
->pp_streams
[i_stream
];
282 if( id
->pp_ids
[i_stream
] )
284 block_t
*p_dup
= block_Duplicate( p_buffer
);
287 sout_StreamIdSend( p_dup_stream
, id
->pp_ids
[i_stream
], p_dup
);
291 if( i_stream
< p_sys
->i_nb_streams
&& id
->pp_ids
[i_stream
] )
293 p_dup_stream
= p_sys
->pp_streams
[i_stream
];
294 sout_StreamIdSend( p_dup_stream
, id
->pp_ids
[i_stream
], p_buffer
);
298 block_Release( p_buffer
);
306 /*****************************************************************************
308 *****************************************************************************/
309 static bool NumInRange( const char *psz_range
, int i_num
)
311 const char *psz
= strchr( psz_range
, '-' );
315 i_start
= strtol( psz_range
, &end
, 0 );
316 if( end
== psz_range
)
322 i_stop
= strtol( psz
, &end
, 0 );
329 return i_start
<= i_num
&& i_num
<= i_stop
;
332 static bool ESSelected( es_format_t
*fmt
, char *psz_select
)
337 /* We have tri-state variable : no tested (-1), failed(0), succeed(1) */
342 /* If empty all es are selected */
343 if( psz_select
== NULL
|| *psz_select
== '\0' )
347 psz_dup
= strdup( psz_select
);
352 /* If non empty, parse the selection:
353 * We have selection[,selection[,..]] where following selection are recognized:
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
366 while( *psz
== ' ' || *psz
== '\t' ) psz
++;
369 p
= strchr( psz
, ',' );
381 if( !strncmp( psz
, "no-audio", strlen( "no-audio" ) ) ||
382 !strncmp( psz
, "noaudio", strlen( "noaudio" ) ) )
386 i_cat
= fmt
->i_cat
!= AUDIO_ES
? 1 : 0;
389 else if( !strncmp( psz
, "no-video", strlen( "no-video" ) ) ||
390 !strncmp( psz
, "novideo", strlen( "novideo" ) ) )
394 i_cat
= fmt
->i_cat
!= VIDEO_ES
? 1 : 0;
397 else if( !strncmp( psz
, "no-spu", strlen( "no-spu" ) ) ||
398 !strncmp( psz
, "nospu", strlen( "nospu" ) ) )
402 i_cat
= fmt
->i_cat
!= SPU_ES
? 1 : 0;
405 else if( !strncmp( psz
, "audio", strlen( "audio" ) ) )
409 i_cat
= fmt
->i_cat
== AUDIO_ES
? 1 : 0;
412 else if( !strncmp( psz
, "video", strlen( "video" ) ) )
416 i_cat
= fmt
->i_cat
== VIDEO_ES
? 1 : 0;
419 else if( !strncmp( psz
, "spu", strlen( "spu" ) ) )
423 i_cat
= fmt
->i_cat
== SPU_ES
? 1 : 0;
426 else if( strchr( psz
, '=' ) != NULL
)
428 char *psz_arg
= strchr( psz
, '=' );
431 if( !strcmp( psz
, "no-es" ) || !strcmp( psz
, "noes" ) )
435 i_es
= NumInRange( psz_arg
, fmt
->i_id
) ? 0 : -1;
438 else if( !strcmp( psz
, "es" ) )
442 i_es
= NumInRange( psz_arg
, fmt
->i_id
) ? 1 : -1;
445 else if( !strcmp( psz
, "no-prgm" ) || !strcmp( psz
, "noprgm" ) ||
446 !strcmp( psz
, "no-program" ) || !strcmp( psz
, "noprogram" ) )
448 if( fmt
->i_group
>= 0 && i_prgm
== -1 )
450 i_prgm
= NumInRange( psz_arg
, fmt
->i_group
) ? 0 : -1;
453 else if( !strcmp( psz
, "prgm" ) || !strcmp( psz
, "program" ) )
455 if( fmt
->i_group
>= 0 && i_prgm
== -1 )
457 i_prgm
= NumInRange( psz_arg
, fmt
->i_group
) ? 1 : -1;
463 fprintf( stderr
, "unknown args (%s)\n", psz
);
471 if( i_cat
== 1 || i_es
== 1 || i_prgm
== 1 )