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 /*****************************************************************************
25 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
34 #include <vlc_block.h>
36 /*****************************************************************************
38 *****************************************************************************/
39 static int Open ( vlc_object_t
* );
40 static void Close ( vlc_object_t
* );
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
)
50 set_capability("sout filter", 0)
51 add_shortcut("duplicate", "dup")
52 set_callbacks(Open
, Close
)
56 /*****************************************************************************
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
* );
66 sout_stream_t
**pp_streams
;
68 int i_nb_last_streams
;
69 sout_stream_t
**pp_last_streams
;
79 } sout_stream_id_sys_t
;
81 static bool ESSelected( struct vlc_logger
*, const es_format_t
*fmt
,
84 /*****************************************************************************
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
;
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
++ )
101 sout_StreamControl( p_sys
->pp_streams
[i
], i_query
,
102 id
->pp_ids
[i
], spu_hl
);
111 static const struct sout_stream_operations ops
= {
112 Add
, Del
, Send
, Control
, NULL
,
115 /*****************************************************************************
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
) );
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
);
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
,
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
;
161 if( ppsz_select
== NULL
)
163 msg_Err( p_stream
, " * ignore selection `%s'", psz
);
167 msg_Dbg( p_stream
, " * apply selection `%s'", psz
);
168 *ppsz_select
= strdup( psz
);
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" );
187 p_stream
->p_sys
= p_sys
;
188 p_stream
->ops
= &ops
;
192 /*****************************************************************************
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
);
213 /*****************************************************************************
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
) );
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
++ )
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
);
243 msg_Dbg( p_stream
, " - added for output %d", i_stream
);
248 msg_Dbg( p_stream
, " - failed for output %d", i_stream
);
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 )
270 /*****************************************************************************
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
;
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
] );
292 /*****************************************************************************
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
;
302 /* Loop through the linked list of buffers */
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
);
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
);
329 block_Release( p_buffer
);
337 /*****************************************************************************
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
);
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
,
358 /* We have tri-state variable : no tested (-1), failed(0), succeed(1) */
363 /* If empty all es are selected */
364 if( psz_select
== NULL
|| *psz_select
== '\0' )
368 psz_dup
= strdup( psz_select
);
373 /* If non empty, parse the selection:
374 * We have selection[,selection[,..]] where following selection are recognized:
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
387 while( *psz
== ' ' || *psz
== '\t' ) psz
++;
390 p
= strchr( psz
, ',' );
402 if( !strncmp( psz
, "no-audio", strlen( "no-audio" ) ) ||
403 !strncmp( psz
, "noaudio", strlen( "noaudio" ) ) )
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" ) ) )
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" ) ) )
423 i_cat
= fmt
->i_cat
!= SPU_ES
? 1 : 0;
426 else if( !strncmp( psz
, "audio", strlen( "audio" ) ) )
430 i_cat
= fmt
->i_cat
== AUDIO_ES
? 1 : 0;
433 else if( !strncmp( psz
, "video", strlen( "video" ) ) )
437 i_cat
= fmt
->i_cat
== VIDEO_ES
? 1 : 0;
440 else if( !strncmp( psz
, "spu", strlen( "spu" ) ) )
444 i_cat
= fmt
->i_cat
== SPU_ES
? 1 : 0;
447 else if( strchr( psz
, '=' ) != NULL
)
449 char *psz_arg
= strchr( psz
, '=' );
452 if( !strcmp( psz
, "no-es" ) || !strcmp( psz
, "noes" ) )
456 i_es
= NumInRange( psz_arg
, fmt
->i_id
) ? 0 : -1;
459 else if( !strcmp( psz
, "es" ) )
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;
484 vlc_error( logger
, "unknown args (%s)", psz
);
492 if( i_cat
== 1 || i_es
== 1 || i_prgm
== 1 )