]> git.sesse.net Git - vlc/blob - modules/stream_out/duplicate.c
b694c5f06ae805d4b7159b2189a4dfe53607848e
[vlc] / modules / stream_out / duplicate.c
1 /*****************************************************************************
2  * duplicate.c: duplicate stream output module
3  *****************************************************************************
4  * Copyright (C) 2003-2004 VideoLAN
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
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  * Module descriptor
35  *****************************************************************************/
36 static int      Open    ( vlc_object_t * );
37 static void     Close   ( vlc_object_t * );
38
39 vlc_module_begin();
40     set_description( _("Duplicate stream output") );
41     set_capability( "sout stream", 50 );
42     add_shortcut( "duplicate" );
43     add_shortcut( "dup" );
44     set_callbacks( Open, Close );
45 vlc_module_end();
46
47
48 /*****************************************************************************
49  * Exported prototypes
50  *****************************************************************************/
51 static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
52 static int               Del ( sout_stream_t *, sout_stream_id_t * );
53 static int               Send( sout_stream_t *, sout_stream_id_t *,
54                                block_t* );
55
56 struct sout_stream_sys_t
57 {
58     int             i_nb_streams;
59     sout_stream_t   **pp_streams;
60
61     int             i_nb_select;
62     char            **ppsz_select;
63 };
64
65 struct sout_stream_id_t
66 {
67     int                 i_nb_ids;
68     void                **pp_ids;
69 };
70
71 static vlc_bool_t ESSelected( es_format_t *fmt, char *psz_select );
72
73 /*****************************************************************************
74  * Open:
75  *****************************************************************************/
76 static int Open( vlc_object_t *p_this )
77 {
78     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
79     sout_stream_sys_t *p_sys;
80     sout_cfg_t        *p_cfg;
81
82     msg_Dbg( p_stream, "creating 'duplicate'" );
83
84     p_sys = malloc( sizeof( sout_stream_sys_t ) );
85
86     p_sys->i_nb_streams = 0;
87     p_sys->pp_streams   = NULL;
88     p_sys->i_nb_select  = 0;
89     p_sys->ppsz_select  = NULL;
90
91     for( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )
92     {
93         if( !strncmp( p_cfg->psz_name, "dst", strlen( "dst" ) ) )
94         {
95             sout_stream_t *s;
96
97             msg_Dbg( p_stream, " * adding `%s'", p_cfg->psz_value );
98             s = sout_StreamNew( p_stream->p_sout, p_cfg->psz_value );
99
100             if( s )
101             {
102                 TAB_APPEND( p_sys->i_nb_streams, p_sys->pp_streams, s );
103                 TAB_APPEND( p_sys->i_nb_select,  p_sys->ppsz_select, NULL );
104             }
105         }
106         else if( !strncmp( p_cfg->psz_name, "select", strlen( "select" ) ) )
107         {
108             char *psz = p_cfg->psz_value;
109             if( p_sys->i_nb_select > 0 && psz && *psz )
110             {
111                 msg_Dbg( p_stream, " * apply selection %s", psz );
112                 p_sys->ppsz_select[p_sys->i_nb_select - 1] = strdup( psz );
113             }
114         }
115     }
116
117     if( p_sys->i_nb_streams == 0 )
118     {
119         msg_Err( p_stream, "no destination given" );
120         free( p_sys );
121
122         return VLC_EGENERIC;
123     }
124
125     p_stream->pf_add    = Add;
126     p_stream->pf_del    = Del;
127     p_stream->pf_send   = Send;
128
129     p_stream->p_sys     = p_sys;
130
131     return VLC_SUCCESS;
132 }
133
134 /*****************************************************************************
135  * Close:
136  *****************************************************************************/
137 static void Close( vlc_object_t * p_this )
138 {
139     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
140     sout_stream_sys_t *p_sys = p_stream->p_sys;
141
142     int i;
143
144     msg_Dbg( p_stream, "closing a duplication" );
145     for( i = 0; i < p_sys->i_nb_streams; i++ )
146     {
147         sout_StreamDelete( p_sys->pp_streams[i] );
148         if( p_sys->ppsz_select[i] )
149         {
150             free( p_sys->ppsz_select[i] );
151         }
152     }
153     if( p_sys->pp_streams )
154     {
155         free( p_sys->pp_streams );
156     }
157     if( p_sys->ppsz_select )
158     {
159         free( p_sys->ppsz_select );
160     }
161
162     free( p_sys );
163 }
164
165 /*****************************************************************************
166  * Add:
167  *****************************************************************************/
168 static sout_stream_id_t * Add( sout_stream_t *p_stream, es_format_t *p_fmt )
169 {
170     sout_stream_sys_t *p_sys = p_stream->p_sys;
171     sout_stream_id_t  *id;
172     int i_stream, i_valid_streams = 0;
173
174     id = malloc( sizeof( sout_stream_id_t ) );
175     id->i_nb_ids = 0;
176     id->pp_ids   = NULL;
177
178     msg_Dbg( p_stream, "duplicated a new stream codec=%4.4s (es=%d group=%d)",
179              (char*)&p_fmt->i_codec, p_fmt->i_id, p_fmt->i_group );
180
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                  block_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         block_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             block_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 = block_Duplicate( 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             block_Release( 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, "noes" ) )
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 }