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 stream", 50 )
45 add_shortcut( "duplicate", "dup" )
46 set_category( CAT_SOUT
)
47 set_subcategory( SUBCAT_SOUT_STREAM
)
48 set_callbacks( Open
, Close
)
52 /*****************************************************************************
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
* );
62 sout_stream_t
**pp_streams
;
64 int i_nb_last_streams
;
65 sout_stream_t
**pp_last_streams
;
75 } sout_stream_id_sys_t
;
77 static bool ESSelected( const es_format_t
*fmt
, char *psz_select
);
79 /*****************************************************************************
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
;
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
++ )
96 sout_StreamControl( p_sys
->pp_streams
[i
], i_query
,
97 id
->pp_ids
[i
], spu_hl
);
106 /*****************************************************************************
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
) );
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
);
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
,
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];
152 msg_Err( p_stream
, " * ignore selection `%s' (it already has `%s')",
157 msg_Dbg( p_stream
, " * apply selection `%s'", psz
);
158 *ppsz_select
= strdup( psz
);
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" );
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
;
186 /*****************************************************************************
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
);
207 /*****************************************************************************
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
) );
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
++ )
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
);
236 msg_Dbg( p_stream
, " - added for output %d", i_stream
);
241 msg_Dbg( p_stream
, " - failed for output %d", i_stream
);
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 )
263 /*****************************************************************************
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
;
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
] );
285 /*****************************************************************************
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
;
295 /* Loop through the linked list of buffers */
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
);
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
);
322 block_Release( p_buffer
);
330 /*****************************************************************************
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
);
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
)
350 /* We have tri-state variable : no tested (-1), failed(0), succeed(1) */
355 /* If empty all es are selected */
356 if( psz_select
== NULL
|| *psz_select
== '\0' )
360 psz_dup
= strdup( psz_select
);
365 /* If non empty, parse the selection:
366 * We have selection[,selection[,..]] where following selection are recognized:
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
379 while( *psz
== ' ' || *psz
== '\t' ) psz
++;
382 p
= strchr( psz
, ',' );
394 if( !strncmp( psz
, "no-audio", strlen( "no-audio" ) ) ||
395 !strncmp( psz
, "noaudio", strlen( "noaudio" ) ) )
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" ) ) )
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" ) ) )
415 i_cat
= fmt
->i_cat
!= SPU_ES
? 1 : 0;
418 else if( !strncmp( psz
, "audio", strlen( "audio" ) ) )
422 i_cat
= fmt
->i_cat
== AUDIO_ES
? 1 : 0;
425 else if( !strncmp( psz
, "video", strlen( "video" ) ) )
429 i_cat
= fmt
->i_cat
== VIDEO_ES
? 1 : 0;
432 else if( !strncmp( psz
, "spu", strlen( "spu" ) ) )
436 i_cat
= fmt
->i_cat
== SPU_ES
? 1 : 0;
439 else if( strchr( psz
, '=' ) != NULL
)
441 char *psz_arg
= strchr( psz
, '=' );
444 if( !strcmp( psz
, "no-es" ) || !strcmp( psz
, "noes" ) )
448 i_es
= NumInRange( psz_arg
, fmt
->i_id
) ? 0 : -1;
451 else if( !strcmp( psz
, "es" ) )
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;
476 fprintf( stderr
, "unknown args (%s)\n", psz
);
484 if( i_cat
== 1 || i_es
== 1 || i_prgm
== 1 )