]> git.sesse.net Git - vlc/blob - modules/stream_out/duplicate.c
aout: add wait parameter to aout_DecFlush()
[vlc] / modules / stream_out / duplicate.c
1 /*****************************************************************************
2  * duplicate.c: duplicate stream output module
3  *****************************************************************************
4  * Copyright (C) 2003-2004 the VideoLAN team
5  * $Id$
6  *
7  * Author: Laurent Aimar <fenrir@via.ecp.fr>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_sout.h>
35 #include <vlc_block.h>
36
37 /*****************************************************************************
38  * Module descriptor
39  *****************************************************************************/
40 static int      Open    ( vlc_object_t * );
41 static void     Close   ( vlc_object_t * );
42
43 vlc_module_begin ()
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 )
50 vlc_module_end ()
51
52
53 /*****************************************************************************
54  * Exported prototypes
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 *,
59                                block_t* );
60
61 struct sout_stream_sys_t
62 {
63     int             i_nb_streams;
64     sout_stream_t   **pp_streams;
65
66     int             i_nb_last_streams;
67     sout_stream_t   **pp_last_streams;
68
69     int             i_nb_select;
70     char            **ppsz_select;
71 };
72
73 struct sout_stream_id_sys_t
74 {
75     int                 i_nb_ids;
76     void                **pp_ids;
77 };
78
79 static bool ESSelected( const es_format_t *fmt, char *psz_select );
80
81 /*****************************************************************************
82  * Open:
83  *****************************************************************************/
84 static int Open( vlc_object_t *p_this )
85 {
86     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
87     sout_stream_sys_t *p_sys;
88     config_chain_t        *p_cfg;
89
90     msg_Dbg( p_stream, "creating 'duplicate'" );
91
92     p_sys = malloc( sizeof( sout_stream_sys_t ) );
93     if( !p_sys )
94         return VLC_ENOMEM;
95
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 );
99
100     for( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )
101     {
102         if( !strncmp( p_cfg->psz_name, "dst", strlen( "dst" ) ) )
103         {
104             sout_stream_t *s, *p_last;
105
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 );
109
110             if( s )
111             {
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,
114                     p_last );
115                 TAB_APPEND( p_sys->i_nb_select,  p_sys->ppsz_select, NULL );
116             }
117         }
118         else if( !strncmp( p_cfg->psz_name, "select", strlen( "select" ) ) )
119         {
120             char *psz = p_cfg->psz_value;
121             if( p_sys->i_nb_select > 0 && psz && *psz )
122             {
123                 char **ppsz_select = &p_sys->ppsz_select[p_sys->i_nb_select - 1];
124
125                 if( *ppsz_select )
126                 {
127                     msg_Err( p_stream, " * ignore selection `%s' (it already has `%s')",
128                              psz, *ppsz_select );
129                 }
130                 else
131                 {
132                     msg_Dbg( p_stream, " * apply selection `%s'", psz );
133                     *ppsz_select = strdup( psz );
134                 }
135             }
136         }
137         else
138         {
139             msg_Err( p_stream, " * ignore unknown option `%s'", p_cfg->psz_name );
140         }
141     }
142
143     if( p_sys->i_nb_streams == 0 )
144     {
145         msg_Err( p_stream, "no destination given" );
146         free( p_sys );
147
148         return VLC_EGENERIC;
149     }
150
151     p_stream->pf_add    = Add;
152     p_stream->pf_del    = Del;
153     p_stream->pf_send   = Send;
154
155     p_stream->p_sys     = p_sys;
156
157     return VLC_SUCCESS;
158 }
159
160 /*****************************************************************************
161  * Close:
162  *****************************************************************************/
163 static void Close( vlc_object_t * p_this )
164 {
165     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
166     sout_stream_sys_t *p_sys = p_stream->p_sys;
167
168     int i;
169
170     msg_Dbg( p_stream, "closing a duplication" );
171     for( i = 0; i < p_sys->i_nb_streams; i++ )
172     {
173         sout_StreamChainDelete(p_sys->pp_streams[i], p_sys->pp_last_streams[i]);
174         free( p_sys->ppsz_select[i] );
175     }
176     free( p_sys->pp_streams );
177     free( p_sys->pp_last_streams );
178     free( p_sys->ppsz_select );
179
180     free( p_sys );
181 }
182
183 /*****************************************************************************
184  * Add:
185  *****************************************************************************/
186 static sout_stream_id_sys_t * Add( sout_stream_t *p_stream, const es_format_t *p_fmt )
187 {
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;
191
192     id = malloc( sizeof( sout_stream_id_sys_t ) );
193     if( !id )
194         return NULL;
195
196     TAB_INIT( id->i_nb_ids, id->pp_ids );
197
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 );
200
201     for( i_stream = 0; i_stream < p_sys->i_nb_streams; i_stream++ )
202     {
203         void *id_new = NULL;
204
205         if( ESSelected( p_fmt, p_sys->ppsz_select[i_stream] ) )
206         {
207             sout_stream_t *out = p_sys->pp_streams[i_stream];
208
209             id_new = (void*)sout_StreamIdAdd( out, p_fmt );
210             if( id_new )
211             {
212                 msg_Dbg( p_stream, "    - added for output %d", i_stream );
213                 i_valid_streams++;
214             }
215             else
216             {
217                 msg_Dbg( p_stream, "    - failed for output %d", i_stream );
218             }
219         }
220         else
221         {
222             msg_Dbg( p_stream, "    - ignored for output %d", i_stream );
223         }
224
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 );
228     }
229
230     if( i_valid_streams <= 0 )
231     {
232         Del( p_stream, id );
233         return NULL;
234     }
235
236     return id;
237 }
238
239 /*****************************************************************************
240  * Del:
241  *****************************************************************************/
242 static void Del( sout_stream_t *p_stream, sout_stream_id_sys_t *id )
243 {
244     sout_stream_sys_t *p_sys = p_stream->p_sys;
245     int               i_stream;
246
247     for( i_stream = 0; i_stream < p_sys->i_nb_streams; i_stream++ )
248     {
249         if( id->pp_ids[i_stream] )
250         {
251             sout_stream_t *out = p_sys->pp_streams[i_stream];
252             sout_StreamIdDel( out, id->pp_ids[i_stream] );
253         }
254     }
255
256     free( id->pp_ids );
257     free( id );
258 }
259
260 /*****************************************************************************
261  * Send:
262  *****************************************************************************/
263 static int Send( sout_stream_t *p_stream, sout_stream_id_sys_t *id,
264                  block_t *p_buffer )
265 {
266     sout_stream_sys_t *p_sys = p_stream->p_sys;
267     sout_stream_t     *p_dup_stream;
268     int               i_stream;
269
270     /* Loop through the linked list of buffers */
271     while( p_buffer )
272     {
273         block_t *p_next = p_buffer->p_next;
274
275         p_buffer->p_next = NULL;
276
277         for( i_stream = 0; i_stream < p_sys->i_nb_streams - 1; i_stream++ )
278         {
279             p_dup_stream = p_sys->pp_streams[i_stream];
280
281             if( id->pp_ids[i_stream] )
282             {
283                 block_t *p_dup = block_Duplicate( p_buffer );
284
285                 if( p_dup )
286                     sout_StreamIdSend( p_dup_stream, id->pp_ids[i_stream], p_dup );
287             }
288         }
289
290         if( i_stream < p_sys->i_nb_streams && id->pp_ids[i_stream] )
291         {
292             p_dup_stream = p_sys->pp_streams[i_stream];
293             sout_StreamIdSend( p_dup_stream, id->pp_ids[i_stream], p_buffer );
294         }
295         else
296         {
297             block_Release( p_buffer );
298         }
299
300         p_buffer = p_next;
301     }
302     return VLC_SUCCESS;
303 }
304
305 /*****************************************************************************
306  * Divers
307  *****************************************************************************/
308 static bool NumInRange( const char *psz_range, int i_num )
309 {
310     int beginRange, endRange;
311     int res = sscanf(psz_range, "%d-%d", &beginRange, &endRange);
312     if (res == 0)
313         return false;
314     else if (res == 1)
315         return beginRange == i_num;
316     return (i_num >= beginRange && i_num <= endRange)
317         || (beginRange > endRange && (i_num <= beginRange && i_num >= endRange));
318 }
319
320 static bool ESSelected( const es_format_t *fmt, char *psz_select )
321 {
322     char  *psz_dup;
323     char  *psz;
324
325     /* We have tri-state variable : no tested (-1), failed(0), succeed(1) */
326     int i_cat = -1;
327     int i_es  = -1;
328     int i_prgm= -1;
329
330     /* If empty all es are selected */
331     if( psz_select == NULL || *psz_select == '\0' )
332     {
333         return true;
334     }
335     psz_dup = strdup( psz_select );
336     if( !psz_dup )
337         return false;
338     psz = psz_dup;
339
340     /* If non empty, parse the selection:
341      * We have selection[,selection[,..]] where following selection are recognized:
342      *      (no(-))audio
343      *      (no(-))spu
344      *      (no(-))video
345      *      (no(-))es=[start]-[end] or es=num
346      *      (no(-))prgm=[start]-[end] or prgm=num (program works too)
347      *      if a negative test failed we exit directly
348      */
349     while( psz && *psz )
350     {
351         char *p;
352
353         /* Skip space */
354         while( *psz == ' ' || *psz == '\t' ) psz++;
355
356         /* Search end */
357         p = strchr( psz, ',' );
358         if( p == psz )
359         {
360             /* Empty */
361             psz = p + 1;
362             continue;
363         }
364         if( p )
365         {
366             *p++ = '\0';
367         }
368
369         if( !strncmp( psz, "no-audio", strlen( "no-audio" ) ) ||
370             !strncmp( psz, "noaudio", strlen( "noaudio" ) ) )
371         {
372             if( i_cat == -1 )
373             {
374                 i_cat = fmt->i_cat != AUDIO_ES ? 1 : 0;
375             }
376         }
377         else if( !strncmp( psz, "no-video", strlen( "no-video" ) ) ||
378                  !strncmp( psz, "novideo", strlen( "novideo" ) ) )
379         {
380             if( i_cat == -1 )
381             {
382                 i_cat = fmt->i_cat != VIDEO_ES ? 1 : 0;
383             }
384         }
385         else if( !strncmp( psz, "no-spu", strlen( "no-spu" ) ) ||
386                  !strncmp( psz, "nospu", strlen( "nospu" ) ) )
387         {
388             if( i_cat == -1 )
389             {
390                 i_cat = fmt->i_cat != SPU_ES ? 1 : 0;
391             }
392         }
393         else if( !strncmp( psz, "audio", strlen( "audio" ) ) )
394         {
395             if( i_cat == -1 )
396             {
397                 i_cat = fmt->i_cat == AUDIO_ES ? 1 : 0;
398             }
399         }
400         else if( !strncmp( psz, "video", strlen( "video" ) ) )
401         {
402             if( i_cat == -1 )
403             {
404                 i_cat = fmt->i_cat == VIDEO_ES ? 1 : 0;
405             }
406         }
407         else if( !strncmp( psz, "spu", strlen( "spu" ) ) )
408         {
409             if( i_cat == -1 )
410             {
411                 i_cat = fmt->i_cat == SPU_ES ? 1 : 0;
412             }
413         }
414         else if( strchr( psz, '=' ) != NULL )
415         {
416             char *psz_arg = strchr( psz, '=' );
417             *psz_arg++ = '\0';
418
419             if( !strcmp( psz, "no-es" ) || !strcmp( psz, "noes" ) )
420             {
421                 if( i_es == -1 )
422                 {
423                     i_es = NumInRange( psz_arg, fmt->i_id ) ? 0 : -1;
424                 }
425             }
426             else if( !strcmp( psz, "es" ) )
427             {
428                 if( i_es == -1 )
429                 {
430                     i_es = NumInRange( psz_arg, fmt->i_id) ? 1 : -1;
431                 }
432             }
433             else if( !strcmp( psz, "no-prgm" ) || !strcmp( psz, "noprgm" ) ||
434                       !strcmp( psz, "no-program" ) || !strcmp( psz, "noprogram" ) )
435             {
436                 if( fmt->i_group >= 0 && i_prgm == -1 )
437                 {
438                     i_prgm = NumInRange( psz_arg, fmt->i_group ) ? 0 : -1;
439                 }
440             }
441             else if( !strcmp( psz, "prgm" ) || !strcmp( psz, "program" ) )
442             {
443                 if( fmt->i_group >= 0 && i_prgm == -1 )
444                 {
445                     i_prgm = NumInRange( psz_arg, fmt->i_group ) ? 1 : -1;
446                 }
447             }
448         }
449         else
450         {
451             fprintf( stderr, "unknown args (%s)\n", psz );
452         }
453         /* Next */
454         psz = p;
455     }
456
457     free( psz_dup );
458
459     if( i_cat == 1 || i_es == 1 || i_prgm == 1 )
460     {
461         return true;
462     }
463     return false;
464 }