* stream_out: sout_buffer_t -> block_t.
[vlc.git] / 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_stream_new( 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_stream_delete( 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,
180              p_fmt->i_id,
181              p_fmt->i_group );
182     for( i_stream = 0; i_stream < p_sys->i_nb_streams; i_stream++ )
183     {
184         void *id_new = NULL;
185
186         if( ESSelected( p_fmt, p_sys->ppsz_select[i_stream] ) )
187         {
188             sout_stream_t *out = p_sys->pp_streams[i_stream];
189
190             id_new = (void*)out->pf_add( out, p_fmt );
191             if( id_new )
192             {
193                 msg_Dbg( p_stream, "    - added for output %d", i_stream );
194                 i_valid_streams++;
195             }
196             else
197             {
198                 msg_Dbg( p_stream, "    - failed for output %d", i_stream );
199             }
200         }
201         else
202         {
203             msg_Dbg( p_stream, "    - ignored for output %d", i_stream );
204         }
205
206         /* Append failed attempts as well to keep track of which pp_id
207          * belongs to which duplicated stream */
208         TAB_APPEND( id->i_nb_ids, id->pp_ids, id_new );
209     }
210
211     if( i_valid_streams <= 0 )
212     {
213         Del( p_stream, id );
214         return NULL;
215     }
216
217     return id;
218 }
219
220 /*****************************************************************************
221  * Del:
222  *****************************************************************************/
223 static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
224 {
225     sout_stream_sys_t *p_sys = p_stream->p_sys;
226     int               i_stream;
227
228     for( i_stream = 0; i_stream < p_sys->i_nb_streams; i_stream++ )
229     {
230         if( id->pp_ids[i_stream] )
231         {
232             sout_stream_t *out = p_sys->pp_streams[i_stream];
233             out->pf_del( out, id->pp_ids[i_stream] );
234         }
235     }
236
237     free( id->pp_ids );
238     free( id );
239     return VLC_SUCCESS;
240 }
241
242 /*****************************************************************************
243  * Send:
244  *****************************************************************************/
245 static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
246                  block_t *p_buffer )
247 {
248     sout_stream_sys_t *p_sys = p_stream->p_sys;
249     sout_stream_t     *p_dup_stream;
250     int               i_stream;
251
252     /* Loop through the linked list of buffers */
253     while( p_buffer )
254     {
255         block_t *p_next = p_buffer->p_next;
256
257         p_buffer->p_next = NULL;
258
259         for( i_stream = 0; i_stream < p_sys->i_nb_streams - 1; i_stream++ )
260         {
261             block_t *p_dup;
262             p_dup_stream = p_sys->pp_streams[i_stream];
263
264             if( id->pp_ids[i_stream] )
265             {
266                 p_dup = block_Duplicate( p_buffer );
267
268                 p_dup_stream->pf_send( p_dup_stream, id->pp_ids[i_stream],
269                                        p_dup );
270             }
271         }
272
273         if( i_stream < p_sys->i_nb_streams && id->pp_ids[i_stream] )
274         {
275             p_dup_stream = p_sys->pp_streams[i_stream];
276             p_dup_stream->pf_send( p_dup_stream, id->pp_ids[i_stream],
277                                    p_buffer );
278         }
279         else
280         {
281             block_Release( p_buffer );
282         }
283
284         p_buffer = p_next;
285     }
286     return VLC_SUCCESS;
287 }
288
289 /*****************************************************************************
290  * Divers
291  *****************************************************************************/
292 static vlc_bool_t NumInRange( char *psz_range, int i_num )
293 {
294     char *psz = strchr( psz_range, '-' );
295     char *end;
296     int  i_start, i_stop;
297
298     if( psz )
299     {
300         i_start = strtol( psz_range, &end, 0 );
301         if( end == psz_range ) i_start = i_num;
302
303         i_stop  = strtol( psz,       &end, 0 );
304         if( end == psz_range ) i_stop = i_num;
305     }
306     else
307     {
308         i_start = i_stop = strtol( psz_range, NULL, 0 );
309     }
310
311     return i_start <= i_num && i_num <= i_stop ? VLC_TRUE : VLC_FALSE;
312 }
313
314 static vlc_bool_t ESSelected( es_format_t *fmt, char *psz_select )
315 {
316     char  *psz_dup;
317     char  *psz;
318
319     /* We have tree state variable : no tested (-1), failed(0), succeed(1) */
320     int i_cat = -1;
321     int i_es  = -1;
322     int i_prgm= -1;
323
324     /* If empty all es are selected */
325     if( psz_select == NULL || *psz_select == '\0' )
326     {
327         return VLC_TRUE;
328     }
329     psz_dup = strdup( psz_select );
330     psz     = psz_dup;
331
332     /* If non empty, parse the selection:
333      * We have selection[,selection[,..]] where following selection are recognized:
334      *      (no(-))audio
335      *      (no(-))spu
336      *      (no(-))video
337      *      (no(-))es=[start]-[end] or es=num
338      *      (no(-))prgm=[start]-[end] or prgm=num (program works too)
339      *      if a negative test failed we exit directly
340      */
341     while( psz && *psz )
342     {
343         char *p;
344
345         /* Skip space */
346         while( *psz == ' ' || *psz == '\t' ) psz++;
347
348         /* Search end */
349         p = strchr( psz, ',' );
350         if( p == psz )
351         {
352             /* Empty */
353             psz = p + 1;
354             continue;
355         }
356         if( p )
357         {
358             *p++ = '\0';
359         }
360
361         if( !strncmp( psz, "no-audio", strlen( "no-audio" ) ) ||
362             !strncmp( psz, "noaudio", strlen( "noaudio" ) ) )
363         {
364             if( i_cat != 1 )
365             {
366                 i_cat = fmt->i_cat != AUDIO_ES ? 1 : 0;
367             }
368         }
369         else if( !strncmp( psz, "no-video", strlen( "no-video" ) ) ||
370                  !strncmp( psz, "novideo", strlen( "novideo" ) ) )
371         {
372             if( i_cat != 1 )
373             {
374                 i_cat = fmt->i_cat != VIDEO_ES ? 1 : 0;
375             }
376         }
377         else if( !strncmp( psz, "no-spu", strlen( "no-spu" ) ) ||
378                  !strncmp( psz, "nospu", strlen( "nospu" ) ) )
379         {
380             if( i_cat != 1 )
381             {
382                 i_cat = fmt->i_cat != SPU_ES ? 1 : 0;
383             }
384         }
385         else if( !strncmp( psz, "audio", strlen( "audio" ) ) )
386         {
387             if( i_cat != 1 )
388             {
389                 i_cat = fmt->i_cat == AUDIO_ES ? 1 : 0;
390             }
391         }
392         else if( !strncmp( psz, "video", strlen( "video" ) ) )
393         {
394             if( i_cat != 1 )
395             {
396                 i_cat = fmt->i_cat == VIDEO_ES ? 1 : 0;
397             }
398         }
399         else if( !strncmp( psz, "spu", strlen( "spu" ) ) )
400         {
401             if( i_cat != 1 )
402             {
403                 i_cat = fmt->i_cat == SPU_ES ? 1 : 0;
404             }
405         }
406         else if( strchr( psz, '=' ) != NULL )
407         {
408             char *psz_arg = strchr( psz, '=' );
409             *psz_arg++ = '\0';
410
411             if( !strcmp( psz, "no-es" ) || !strcmp( psz, "es" ) )
412             {
413                 if( i_es != 1 )
414                 {
415                     i_es = !NumInRange( psz_arg, fmt->i_id ) ? 1 : 0;
416                 }
417             }
418             else if( !strcmp( psz, "es" ) )
419             {
420                 if( i_es != 1 )
421                 {
422                     i_es = NumInRange( psz_arg, fmt->i_id) ? 1 : 0;
423                 }
424             }
425             else if( !strcmp( psz, "no-prgm" ) || !strcmp( psz, "noprgm" ) ||
426                       !strcmp( psz, "no-program" ) || !strcmp( psz, "noprogram" ) )
427             {
428                 if( fmt->i_group >= 0 && i_prgm != 1 )
429                 {
430                     i_prgm = !NumInRange( psz_arg, fmt->i_group ) ? 1 : 0;
431                 }
432             }
433             else if( !strcmp( psz, "prgm" ) || !strcmp( psz, "program" ) )
434             {
435                 if( fmt->i_group >= 0 && i_prgm != 1 )
436                 {
437                     i_prgm = NumInRange( psz_arg, fmt->i_group ) ? 1 : 0;
438                 }
439             }
440         }
441         else
442         {
443             fprintf( stderr, "unknown args (%s)\n", psz );
444         }
445         /* Next */
446         psz = p;
447     }
448
449     free( psz_dup );
450
451     if( i_cat == 0 || i_es == 0 || i_prgm == 0 )
452     {
453         /* One test failed */
454         return VLC_FALSE;
455     }
456     return VLC_TRUE;
457 }