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 it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * 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
*, const es_format_t
* );
57 static void 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( const 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
;
168 msg_Dbg( p_stream
, "closing a duplication" );
169 for( int i
= 0; i
< p_sys
->i_nb_streams
; i
++ )
171 sout_StreamChainDelete(p_sys
->pp_streams
[i
], p_sys
->pp_last_streams
[i
]);
172 free( p_sys
->ppsz_select
[i
] );
174 free( p_sys
->pp_streams
);
175 free( p_sys
->pp_last_streams
);
176 free( p_sys
->ppsz_select
);
181 /*****************************************************************************
183 *****************************************************************************/
184 static sout_stream_id_sys_t
* Add( sout_stream_t
*p_stream
, const es_format_t
*p_fmt
)
186 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
187 sout_stream_id_sys_t
*id
;
188 int i_stream
, i_valid_streams
= 0;
190 id
= malloc( sizeof( sout_stream_id_sys_t
) );
194 TAB_INIT( id
->i_nb_ids
, id
->pp_ids
);
196 msg_Dbg( p_stream
, "duplicated a new stream codec=%4.4s (es=%d group=%d)",
197 (char*)&p_fmt
->i_codec
, p_fmt
->i_id
, p_fmt
->i_group
);
199 for( i_stream
= 0; i_stream
< p_sys
->i_nb_streams
; i_stream
++ )
203 if( ESSelected( p_fmt
, p_sys
->ppsz_select
[i_stream
] ) )
205 sout_stream_t
*out
= p_sys
->pp_streams
[i_stream
];
207 id_new
= (void*)sout_StreamIdAdd( out
, p_fmt
);
210 msg_Dbg( p_stream
, " - added for output %d", i_stream
);
215 msg_Dbg( p_stream
, " - failed for output %d", i_stream
);
220 msg_Dbg( p_stream
, " - ignored for output %d", i_stream
);
223 /* Append failed attempts as well to keep track of which pp_id
224 * belongs to which duplicated stream */
225 TAB_APPEND( id
->i_nb_ids
, id
->pp_ids
, id_new
);
228 if( i_valid_streams
<= 0 )
237 /*****************************************************************************
239 *****************************************************************************/
240 static void Del( sout_stream_t
*p_stream
, sout_stream_id_sys_t
*id
)
242 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
245 for( i_stream
= 0; i_stream
< p_sys
->i_nb_streams
; i_stream
++ )
247 if( id
->pp_ids
[i_stream
] )
249 sout_stream_t
*out
= p_sys
->pp_streams
[i_stream
];
250 sout_StreamIdDel( out
, id
->pp_ids
[i_stream
] );
258 /*****************************************************************************
260 *****************************************************************************/
261 static int Send( sout_stream_t
*p_stream
, sout_stream_id_sys_t
*id
,
264 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
265 sout_stream_t
*p_dup_stream
;
268 /* Loop through the linked list of buffers */
271 block_t
*p_next
= p_buffer
->p_next
;
273 p_buffer
->p_next
= NULL
;
275 for( i_stream
= 0; i_stream
< p_sys
->i_nb_streams
- 1; i_stream
++ )
277 p_dup_stream
= p_sys
->pp_streams
[i_stream
];
279 if( id
->pp_ids
[i_stream
] )
281 block_t
*p_dup
= block_Duplicate( p_buffer
);
284 sout_StreamIdSend( p_dup_stream
, id
->pp_ids
[i_stream
], p_dup
);
288 if( i_stream
< p_sys
->i_nb_streams
&& id
->pp_ids
[i_stream
] )
290 p_dup_stream
= p_sys
->pp_streams
[i_stream
];
291 sout_StreamIdSend( p_dup_stream
, id
->pp_ids
[i_stream
], p_buffer
);
295 block_Release( p_buffer
);
303 /*****************************************************************************
305 *****************************************************************************/
306 static bool NumInRange( const char *psz_range
, int i_num
)
308 int beginRange
, endRange
;
309 int res
= sscanf(psz_range
, "%d-%d", &beginRange
, &endRange
);
313 return beginRange
== i_num
;
314 return (i_num
>= beginRange
&& i_num
<= endRange
)
315 || (beginRange
> endRange
&& (i_num
<= beginRange
&& i_num
>= endRange
));
318 static bool ESSelected( const es_format_t
*fmt
, char *psz_select
)
323 /* We have tri-state variable : no tested (-1), failed(0), succeed(1) */
328 /* If empty all es are selected */
329 if( psz_select
== NULL
|| *psz_select
== '\0' )
333 psz_dup
= strdup( psz_select
);
338 /* If non empty, parse the selection:
339 * We have selection[,selection[,..]] where following selection are recognized:
343 * (no(-))es=[start]-[end] or es=num
344 * (no(-))prgm=[start]-[end] or prgm=num (program works too)
345 * if a negative test failed we exit directly
352 while( *psz
== ' ' || *psz
== '\t' ) psz
++;
355 p
= strchr( psz
, ',' );
367 if( !strncmp( psz
, "no-audio", strlen( "no-audio" ) ) ||
368 !strncmp( psz
, "noaudio", strlen( "noaudio" ) ) )
372 i_cat
= fmt
->i_cat
!= AUDIO_ES
? 1 : 0;
375 else if( !strncmp( psz
, "no-video", strlen( "no-video" ) ) ||
376 !strncmp( psz
, "novideo", strlen( "novideo" ) ) )
380 i_cat
= fmt
->i_cat
!= VIDEO_ES
? 1 : 0;
383 else if( !strncmp( psz
, "no-spu", strlen( "no-spu" ) ) ||
384 !strncmp( psz
, "nospu", strlen( "nospu" ) ) )
388 i_cat
= fmt
->i_cat
!= SPU_ES
? 1 : 0;
391 else if( !strncmp( psz
, "audio", strlen( "audio" ) ) )
395 i_cat
= fmt
->i_cat
== AUDIO_ES
? 1 : 0;
398 else if( !strncmp( psz
, "video", strlen( "video" ) ) )
402 i_cat
= fmt
->i_cat
== VIDEO_ES
? 1 : 0;
405 else if( !strncmp( psz
, "spu", strlen( "spu" ) ) )
409 i_cat
= fmt
->i_cat
== SPU_ES
? 1 : 0;
412 else if( strchr( psz
, '=' ) != NULL
)
414 char *psz_arg
= strchr( psz
, '=' );
417 if( !strcmp( psz
, "no-es" ) || !strcmp( psz
, "noes" ) )
421 i_es
= NumInRange( psz_arg
, fmt
->i_id
) ? 0 : -1;
424 else if( !strcmp( psz
, "es" ) )
428 i_es
= NumInRange( psz_arg
, fmt
->i_id
) ? 1 : -1;
431 else if( !strcmp( psz
, "no-prgm" ) || !strcmp( psz
, "noprgm" ) ||
432 !strcmp( psz
, "no-program" ) || !strcmp( psz
, "noprogram" ) )
434 if( fmt
->i_group
>= 0 && i_prgm
== -1 )
436 i_prgm
= NumInRange( psz_arg
, fmt
->i_group
) ? 0 : -1;
439 else if( !strcmp( psz
, "prgm" ) || !strcmp( psz
, "program" ) )
441 if( fmt
->i_group
>= 0 && i_prgm
== -1 )
443 i_prgm
= NumInRange( psz_arg
, fmt
->i_group
) ? 1 : -1;
449 fprintf( stderr
, "unknown args (%s)\n", psz
);
457 if( i_cat
== 1 || i_es
== 1 || i_prgm
== 1 )