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