]> git.sesse.net Git - vlc/blob - src/stream_output/stream_output.c
* stream_output: added a sout_ParseCfg to help sout modules, and attached
[vlc] / src / stream_output / stream_output.c
1 /*****************************************************************************
2  * stream_output.c : stream output module
3  *****************************************************************************
4  * Copyright (C) 2002-2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  *          Laurent Aimar <fenrir@via.ecp.fr>
9  *          Eric Petit <titer@videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>                                                /* free() */
30 #include <stdio.h>                                              /* sprintf() */
31 #include <string.h>                                            /* strerror() */
32
33 #include <vlc/vlc.h>
34 #include <vlc/sout.h>
35
36 #include "vlc_meta.h"
37
38 #undef DEBUG_BUFFER
39 /*****************************************************************************
40  * Local prototypes
41  *****************************************************************************/
42 static void sout_cfg_free( sout_cfg_t * );
43
44 #define sout_stream_url_to_chain( p, s ) _sout_stream_url_to_chain( VLC_OBJECT(p), s )
45 static char *_sout_stream_url_to_chain( vlc_object_t *, char * );
46
47 /*
48  * Generic MRL parser
49  *
50  */
51
52 typedef struct
53 {
54     char *psz_access;
55     char *psz_way;
56     char *psz_name;
57 } mrl_t;
58
59 /* mrl_Parse: parse psz_mrl and fill p_mrl */
60 static int  mrl_Parse( mrl_t *p_mrl, char *psz_mrl );
61 /* mrl_Clean: clean p_mrl  after a call to mrl_Parse */
62 static void mrl_Clean( mrl_t *p_mrl );
63
64 #define FREE( p ) if( p ) { free( p ); (p) = NULL; }
65
66 /*****************************************************************************
67  * sout_NewInstance: creates a new stream output instance
68  *****************************************************************************/
69 sout_instance_t *__sout_NewInstance( vlc_object_t *p_parent, char * psz_dest )
70 {
71     sout_instance_t *p_sout;
72     vlc_value_t keep;
73
74     if( var_Get( p_parent, "sout-keep", &keep ) < 0 )
75     {
76         msg_Warn( p_parent, "cannot get sout-keep value" );
77         keep.b_bool = VLC_FALSE;
78     }
79     else if( keep.b_bool )
80     {
81         msg_Warn( p_parent, "sout-keep true" );
82         if( ( p_sout = vlc_object_find( p_parent, VLC_OBJECT_SOUT, FIND_ANYWHERE ) ) )
83         {
84             if( !strcmp( p_sout->psz_sout, psz_dest ) )
85             {
86                 msg_Warn( p_parent, "sout keep : reusing sout" );
87                 msg_Warn( p_parent, "sout keep : you probably want to use gather stream_out" );
88                 vlc_object_detach( p_sout );
89                 vlc_object_attach( p_sout, p_parent );
90                 vlc_object_release( p_sout );
91                 return p_sout;
92             }
93             else
94             {
95                 msg_Warn( p_parent, "sout keep : destroying unusable sout" );
96                 sout_DeleteInstance( p_sout );
97             }
98         }
99     }
100     else if( !keep.b_bool )
101     {
102         while( ( p_sout = vlc_object_find( p_parent, VLC_OBJECT_SOUT, FIND_PARENT ) ) )
103         {
104             msg_Warn( p_parent, "sout keep : destroying old sout" );
105             sout_DeleteInstance( p_sout );
106         }
107     }
108
109     /* *** Allocate descriptor *** */
110     p_sout = vlc_object_create( p_parent, VLC_OBJECT_SOUT );
111     if( p_sout == NULL )
112     {
113         msg_Err( p_parent, "out of memory" );
114         return NULL;
115     }
116
117     /* *** init descriptor *** */
118     p_sout->psz_sout    = strdup( psz_dest );
119     p_sout->p_meta      = NULL;
120     p_sout->i_out_pace_nocontrol = 0;
121     p_sout->p_sys       = NULL;
122
123     vlc_mutex_init( p_sout, &p_sout->lock );
124     if( psz_dest && psz_dest[0] == '#' )
125     {
126         p_sout->psz_chain = strdup( &psz_dest[1] );
127     }
128     else
129     {
130         p_sout->psz_chain = sout_stream_url_to_chain( p_sout, psz_dest );
131         msg_Dbg( p_sout, "using sout chain=`%s'", p_sout->psz_chain );
132     }
133     p_sout->p_stream = NULL;
134
135     /* attach it for inherit */
136     vlc_object_attach( p_sout, p_parent );
137
138     p_sout->p_stream = sout_stream_new( p_sout, p_sout->psz_chain );
139
140     if( p_sout->p_stream == NULL )
141     {
142         msg_Err( p_sout, "stream chained failed for `%s'", p_sout->psz_chain );
143
144         FREE( p_sout->psz_sout );
145         FREE( p_sout->psz_chain );
146
147         vlc_object_destroy( p_sout );
148         return NULL;
149     }
150
151     return p_sout;
152 }
153 /*****************************************************************************
154  * sout_DeleteInstance: delete a previously allocated instance
155  *****************************************************************************/
156 void sout_DeleteInstance( sout_instance_t * p_sout )
157 {
158     /* Unlink object */
159     vlc_object_detach( p_sout );
160
161     /* remove the stream out chain */
162     sout_stream_delete( p_sout->p_stream );
163
164     /* *** free all string *** */
165     FREE( p_sout->psz_sout );
166     FREE( p_sout->psz_chain );
167
168     /* delete meta */
169     if( p_sout->p_meta )
170     {
171         vlc_meta_Delete( p_sout->p_meta );
172     }
173
174     vlc_mutex_destroy( &p_sout->lock );
175
176     /* *** free structure *** */
177     vlc_object_destroy( p_sout );
178 }
179
180 /*****************************************************************************
181  * Packetizer/Input
182  *****************************************************************************/
183 sout_packetizer_input_t *sout_InputNew( sout_instance_t *p_sout,
184                                         es_format_t *p_fmt )
185 {
186     sout_packetizer_input_t *p_input;
187
188     msg_Dbg( p_sout, "adding a new input" );
189
190     /* *** create a packetizer input *** */
191     p_input         = malloc( sizeof( sout_packetizer_input_t ) );
192     p_input->p_sout = p_sout;
193     p_input->p_fmt  = p_fmt;
194
195     if( p_fmt->i_codec == VLC_FOURCC( 'n', 'u', 'l', 'l' ) )
196     {
197         vlc_object_release( p_sout );
198         return p_input;
199     }
200
201     /* *** add it to the stream chain */
202     vlc_mutex_lock( &p_sout->lock );
203     p_input->id = p_sout->p_stream->pf_add( p_sout->p_stream, p_fmt );
204     vlc_mutex_unlock( &p_sout->lock );
205
206     if( p_input->id == NULL )
207     {
208         free( p_input );
209         return NULL;
210     }
211
212     return( p_input );
213 }
214
215 /*****************************************************************************
216  *
217  *****************************************************************************/
218 int sout_InputDelete( sout_packetizer_input_t *p_input )
219 {
220     sout_instance_t     *p_sout = p_input->p_sout;
221
222     msg_Dbg( p_sout, "removing an input" );
223
224     if( p_input->p_fmt->i_codec != VLC_FOURCC( 'n', 'u', 'l', 'l' ) )
225     {
226         vlc_mutex_lock( &p_sout->lock );
227         p_sout->p_stream->pf_del( p_sout->p_stream, p_input->id );
228         vlc_mutex_unlock( &p_sout->lock );
229     }
230
231     free( p_input );
232
233     return( VLC_SUCCESS);
234 }
235
236 /*****************************************************************************
237  *
238  *****************************************************************************/
239 int sout_InputSendBuffer( sout_packetizer_input_t *p_input,
240                           block_t *p_buffer )
241 {
242     sout_instance_t     *p_sout = p_input->p_sout;
243     int                 i_ret;
244
245     if( p_input->p_fmt->i_codec == VLC_FOURCC( 'n', 'u', 'l', 'l' ) )
246     {
247         block_Release( p_buffer );
248         return VLC_SUCCESS;
249     }
250     if( p_buffer->i_dts <= 0 )
251     {
252         msg_Warn( p_sout, "trying to send non-dated packet to stream output!");
253         block_Release( p_buffer );
254         return VLC_SUCCESS;
255     }
256
257     vlc_mutex_lock( &p_sout->lock );
258     i_ret = p_sout->p_stream->pf_send( p_sout->p_stream,
259                                        p_input->id, p_buffer );
260     vlc_mutex_unlock( &p_sout->lock );
261
262     return i_ret;
263 }
264
265 /*****************************************************************************
266  * sout_AccessOutNew: allocate a new access out
267  *****************************************************************************/
268 sout_access_out_t *sout_AccessOutNew( sout_instance_t *p_sout,
269                                       char *psz_access, char *psz_name )
270 {
271     sout_access_out_t *p_access;
272     char              *psz_next;
273
274     if( !( p_access = vlc_object_create( p_sout,
275                                          sizeof( sout_access_out_t ) ) ) )
276     {
277         msg_Err( p_sout, "out of memory" );
278         return NULL;
279     }
280
281     psz_next = sout_cfg_parser( &p_access->psz_access, &p_access->p_cfg,
282                                 psz_access );
283     if( psz_next )
284     {
285         free( psz_next );
286     }
287     p_access->psz_name   = strdup( psz_name ? psz_name : "" );
288     p_access->p_sout     = p_sout;
289     p_access->p_sys = NULL;
290     p_access->pf_seek    = NULL;
291     p_access->pf_read    = NULL;
292     p_access->pf_write   = NULL;
293     p_access->p_module   = NULL;
294     vlc_object_attach( p_access, p_sout );
295
296     p_access->p_module   =
297         module_Need( p_access, "sout access", p_access->psz_access, VLC_TRUE );
298
299     if( !p_access->p_module )
300     {
301         free( p_access->psz_access );
302         free( p_access->psz_name );
303         vlc_object_destroy( p_access );
304         return( NULL );
305     }
306
307     return p_access;
308 }
309 /*****************************************************************************
310  * sout_AccessDelete: delete an access out
311  *****************************************************************************/
312 void sout_AccessOutDelete( sout_access_out_t *p_access )
313 {
314     vlc_object_detach( p_access );
315     if( p_access->p_module )
316     {
317         module_Unneed( p_access, p_access->p_module );
318     }
319     free( p_access->psz_access );
320
321     sout_cfg_free( p_access->p_cfg );
322
323     free( p_access->psz_name );
324
325     vlc_object_destroy( p_access );
326 }
327
328 /*****************************************************************************
329  * sout_AccessSeek:
330  *****************************************************************************/
331 int sout_AccessOutSeek( sout_access_out_t *p_access, off_t i_pos )
332 {
333     return p_access->pf_seek( p_access, i_pos );
334 }
335
336 /*****************************************************************************
337  * sout_AccessRead:
338  *****************************************************************************/
339 int sout_AccessOutRead( sout_access_out_t *p_access, block_t *p_buffer )
340 {
341     return( p_access->pf_read ?
342             p_access->pf_read( p_access, p_buffer ) : VLC_EGENERIC );
343 }
344
345 /*****************************************************************************
346  * sout_AccessWrite:
347  *****************************************************************************/
348 int sout_AccessOutWrite( sout_access_out_t *p_access, block_t *p_buffer )
349 {
350     return p_access->pf_write( p_access, p_buffer );
351 }
352
353 /*****************************************************************************
354  * sout_MuxNew: create a new mux
355  *****************************************************************************/
356 sout_mux_t * sout_MuxNew         ( sout_instance_t *p_sout,
357                                    char *psz_mux,
358                                    sout_access_out_t *p_access )
359 {
360     sout_mux_t *p_mux;
361     char       *psz_next;
362
363     p_mux = vlc_object_create( p_sout,
364                                sizeof( sout_mux_t ) );
365     if( p_mux == NULL )
366     {
367         msg_Err( p_sout, "out of memory" );
368         return NULL;
369     }
370
371     p_mux->p_sout       = p_sout;
372     psz_next = sout_cfg_parser( &p_mux->psz_mux, &p_mux->p_cfg, psz_mux );
373     if( psz_next )
374     {
375         free( psz_next );
376     }
377     p_mux->p_access     = p_access;
378     p_mux->pf_capacity  = NULL;
379     p_mux->pf_addstream = NULL;
380     p_mux->pf_delstream = NULL;
381     p_mux->pf_mux       = NULL;
382     p_mux->i_nb_inputs  = 0;
383     p_mux->pp_inputs    = NULL;
384
385     p_mux->p_sys        = NULL;
386     p_mux->p_module = NULL;
387
388     vlc_object_attach( p_mux, p_sout );
389
390     p_mux->p_module     =
391         module_Need( p_mux, "sout mux", p_mux->psz_mux, VLC_TRUE );
392
393     if( p_mux->p_module == NULL )
394     {
395         FREE( p_mux->psz_mux );
396
397         vlc_object_destroy( p_mux );
398         return NULL;
399     }
400
401     /* *** probe mux capacity *** */
402     if( p_mux->pf_capacity )
403     {
404         int b_answer;
405         if( p_mux->pf_capacity( p_mux,
406                                 SOUT_MUX_CAP_GET_ADD_STREAM_ANY_TIME, NULL,
407                                 (void*)&b_answer ) != SOUT_MUX_CAP_ERR_OK )
408         {
409             b_answer = VLC_FALSE;
410         }
411         if( b_answer )
412         {
413             msg_Dbg( p_sout, "muxer support adding stream at any time" );
414             p_mux->b_add_stream_any_time = VLC_TRUE;
415             p_mux->b_waiting_stream = VLC_FALSE;
416
417             if( p_mux->pf_capacity( p_mux,
418                                     SOUT_MUX_CAP_GET_ADD_STREAM_WAIT, NULL,
419                                     (void*)&b_answer ) != SOUT_MUX_CAP_ERR_OK )
420             {
421                 b_answer = VLC_FALSE;
422             }
423             if( b_answer )
424             {
425                 msg_Dbg( p_sout, "muxer prefers waiting for all ES before "
426                          "starting muxing" );
427                 p_mux->b_waiting_stream = VLC_TRUE;
428             }
429         }
430         else
431         {
432             p_mux->b_add_stream_any_time = VLC_FALSE;
433             p_mux->b_waiting_stream = VLC_TRUE;
434         }
435     }
436     else
437     {
438         p_mux->b_add_stream_any_time = VLC_FALSE;
439         p_mux->b_waiting_stream = VLC_TRUE;
440     }
441     p_mux->i_add_stream_start = -1;
442
443     return p_mux;
444 }
445
446 /*****************************************************************************
447  * sout_MuxDelete:
448  *****************************************************************************/
449 void sout_MuxDelete( sout_mux_t *p_mux )
450 {
451     vlc_object_detach( p_mux );
452     if( p_mux->p_module )
453     {
454         module_Unneed( p_mux, p_mux->p_module );
455     }
456     free( p_mux->psz_mux );
457
458     sout_cfg_free( p_mux->p_cfg );
459
460     vlc_object_destroy( p_mux );
461 }
462
463 /*****************************************************************************
464  * sout_MuxAddStream:
465  *****************************************************************************/
466 sout_input_t *sout_MuxAddStream( sout_mux_t *p_mux, es_format_t *p_fmt )
467 {
468     sout_input_t *p_input;
469
470     if( !p_mux->b_add_stream_any_time && !p_mux->b_waiting_stream )
471     {
472         msg_Err( p_mux, "cannot add a new stream (unsuported while muxing "
473                         "for this format)" );
474         return NULL;
475     }
476     if( p_mux->i_add_stream_start < 0 )
477     {
478         /* we wait for one second */
479         p_mux->i_add_stream_start = mdate();
480     }
481
482     msg_Dbg( p_mux, "adding a new input" );
483
484     /* create a new sout input */
485     p_input = malloc( sizeof( sout_input_t ) );
486     p_input->p_sout = p_mux->p_sout;
487     p_input->p_fmt  = p_fmt;
488     p_input->p_fifo = block_FifoNew( p_mux->p_sout );
489     p_input->p_sys  = NULL;
490
491     TAB_APPEND( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input );
492     if( p_mux->pf_addstream( p_mux, p_input ) < 0 )
493     {
494             msg_Err( p_mux, "cannot add this stream" );
495             TAB_REMOVE( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input );
496             block_FifoRelease( p_input->p_fifo );
497             free( p_input );
498             return NULL;
499     }
500
501     return p_input;
502 }
503
504 /*****************************************************************************
505  * sout_MuxDeleteStream:
506  *****************************************************************************/
507 void sout_MuxDeleteStream( sout_mux_t *p_mux, sout_input_t *p_input )
508 {
509     int i_index;
510
511     if( p_mux->b_waiting_stream && p_input->p_fifo->i_depth > 0 )
512     {
513         /* We stop waiting, and call the muxer for taking care of the data
514          * before we remove this es */
515         p_mux->b_waiting_stream = VLC_FALSE;
516         p_mux->pf_mux( p_mux );
517     }
518
519     TAB_FIND( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input, i_index );
520     if( i_index >= 0 )
521     {
522         if( p_mux->pf_delstream( p_mux, p_input ) < 0 )
523         {
524             msg_Err( p_mux, "cannot del this stream from mux" );
525         }
526
527         /* remove the entry */
528         TAB_REMOVE( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input );
529
530         if( p_mux->i_nb_inputs == 0 )
531         {
532             msg_Warn( p_mux, "no more input stream for this mux" );
533         }
534
535         block_FifoRelease( p_input->p_fifo );
536         free( p_input );
537     }
538 }
539
540 /*****************************************************************************
541  * sout_MuxSendBuffer:
542  *****************************************************************************/
543 void sout_MuxSendBuffer( sout_mux_t *p_mux, sout_input_t *p_input,
544                          block_t *p_buffer )
545 {
546     block_FifoPut( p_input->p_fifo, p_buffer );
547
548     if( p_mux->b_waiting_stream )
549     {
550         if( p_mux->i_add_stream_start > 0 &&
551             p_mux->i_add_stream_start + (mtime_t)1500000 < mdate() )
552         {
553             /* more than 1.5 second, start muxing */
554             p_mux->b_waiting_stream = VLC_FALSE;
555         }
556         else
557         {
558             return;
559         }
560     }
561     p_mux->pf_mux( p_mux );
562 }
563
564 /*****************************************************************************
565  *
566  *****************************************************************************/
567 static int  mrl_Parse( mrl_t *p_mrl, char *psz_mrl )
568 {
569     char * psz_dup = strdup( psz_mrl );
570     char * psz_parser = psz_dup;
571     char * psz_access = "";
572     char * psz_way = "";
573     char * psz_name = "";
574
575     /* *** first parse psz_dest */
576     while( *psz_parser && *psz_parser != ':' )
577     {
578         if( *psz_parser == '{' )
579         {
580             while( *psz_parser && *psz_parser != '}' )
581             {
582                 psz_parser++;
583             }
584             if( *psz_parser )
585             {
586                 psz_parser++;
587             }
588         }
589         else
590         {
591             psz_parser++;
592         }
593     }
594 #if defined( WIN32 ) || defined( UNDER_CE )
595     if( psz_parser - psz_dup == 1 )
596     {
597         /* msg_Warn( p_sout, "drive letter %c: found in source string",
598                           *psz_dup ) ; */
599         psz_parser = "";
600     }
601 #endif
602
603     if( !*psz_parser )
604     {
605         psz_access = psz_way = "";
606         psz_name = psz_dup;
607     }
608     else
609     {
610         *psz_parser++ = '\0';
611
612         /* let's skip '//' */
613         if( psz_parser[0] == '/' && psz_parser[1] == '/' )
614         {
615             psz_parser += 2 ;
616         }
617
618         psz_name = psz_parser ;
619
620         /* Come back to parse the access and mux plug-ins */
621         psz_parser = psz_dup;
622
623         if( !*psz_parser )
624         {
625             /* No access */
626             psz_access = "";
627         }
628         else if( *psz_parser == '/' )
629         {
630             /* No access */
631             psz_access = "";
632             psz_parser++;
633         }
634         else
635         {
636             psz_access = psz_parser;
637
638             while( *psz_parser && *psz_parser != '/' )
639             {
640                 if( *psz_parser == '{' )
641                 {
642                     while( *psz_parser && *psz_parser != '}' )
643                     {
644                         psz_parser++;
645                     }
646                     if( *psz_parser )
647                     {
648                         psz_parser++;
649                     }
650                 }
651                 else
652                 {
653                     psz_parser++;
654                 }
655             }
656
657             if( *psz_parser == '/' )
658             {
659                 *psz_parser++ = '\0';
660             }
661         }
662
663         if( !*psz_parser )
664         {
665             /* No mux */
666             psz_way = "";
667         }
668         else
669         {
670             psz_way = psz_parser;
671         }
672     }
673
674     p_mrl->psz_access = strdup( psz_access );
675     p_mrl->psz_way    = strdup( psz_way );
676     p_mrl->psz_name   = strdup( psz_name );
677
678     free( psz_dup );
679     return( VLC_SUCCESS );
680 }
681
682
683 /* mrl_Clean: clean p_mrl  after a call to mrl_Parse */
684 static void mrl_Clean( mrl_t *p_mrl )
685 {
686     FREE( p_mrl->psz_access );
687     FREE( p_mrl->psz_way );
688     FREE( p_mrl->psz_name );
689 }
690
691
692 /****************************************************************************
693  ****************************************************************************
694  **
695  **
696  **
697  ****************************************************************************
698  ****************************************************************************/
699
700 /* create a complete chain */
701 /* chain format:
702     module{option=*:option=*}[:module{option=*:...}]
703  */
704
705 static char *_strndup( char *str, int i_len )
706 {
707     char *p;
708
709     p = malloc( i_len + 1 );
710     strncpy( p, str, i_len );
711     p[i_len] = '\0';
712
713     return( p );
714 }
715
716 /*
717  * parse module{options=str, option="str "}:
718  *  return a pointer on the rest
719  *  XXX: psz_chain is modified
720  */
721 #define SKIPSPACE( p ) { while( *p && ( *p == ' ' || *p == '\t' ) ) p++; }
722 /* go accross " " and { } */
723 static char *_get_chain_end( char *str )
724 {
725     char *p = str;
726
727     SKIPSPACE( p );
728
729     for( ;; )
730     {
731         if( *p == '{' || *p == '"' || *p == '\'')
732         {
733             char c;
734
735             if( *p == '{' )
736             {
737                 c = '}';
738             }
739             else
740             {
741                 c = *p;
742             }
743             p++;
744
745             for( ;; )
746             {
747                 if( *p == '\0' )
748                 {
749                     return p;
750                 }
751
752                 if( *p == c )
753                 {
754                     p++;
755                     return p;
756                 }
757                 else if( *p == '{' && c == '}' )
758                 {
759                     p = _get_chain_end( p );
760                 }
761                 else
762                 {
763                     p++;
764                 }
765             }
766         }
767         else if( *p == '\0' || *p == ',' || *p == '}' || *p == ' ' || *p == '\t' )
768         {
769             return p;
770         }
771         else
772         {
773             p++;
774         }
775     }
776 }
777
778 char * sout_cfg_parser( char **ppsz_name, sout_cfg_t **pp_cfg, char *psz_chain )
779 {
780     sout_cfg_t *p_cfg = NULL;
781     char       *p = psz_chain;
782
783     *ppsz_name = NULL;
784     *pp_cfg    = NULL;
785
786     if( p == NULL )
787     {
788         return NULL;
789     }
790
791     SKIPSPACE( p );
792
793     while( *p && *p != '{' && *p != ':' && *p != ' ' && *p != '\t' )
794     {
795         p++;
796     }
797
798     if( p == psz_chain )
799     {
800         return NULL;
801     }
802
803     *ppsz_name = _strndup( psz_chain, p - psz_chain );
804
805     SKIPSPACE( p );
806
807     if( *p == '{' )
808     {
809         char *psz_name;
810
811         p++;
812
813         for( ;; )
814         {
815             sout_cfg_t cfg;
816
817             SKIPSPACE( p );
818
819             psz_name = p;
820
821             while( *p && *p != '=' && *p != ',' && *p != '}' && *p != ' ' && *p != '\t' )
822             {
823                 p++;
824             }
825
826             /* fprintf( stderr, "name=%s - rest=%s\n", psz_name, p ); */
827             if( p == psz_name )
828             {
829                 fprintf( stderr, "invalid options (empty)" );
830                 break;
831             }
832
833             cfg.psz_name = _strndup( psz_name, p - psz_name );
834
835             SKIPSPACE( p );
836
837             if( *p == '=' )
838             {
839                 char *end;
840
841                 p++;
842
843                 end = _get_chain_end( p );
844                 if( end <= p )
845                 {
846                     cfg.psz_value = NULL;
847                 }
848                 else
849                 {
850                     if( *p == '\'' || *p =='"' || *p == '{' )
851                     {
852                         p++;
853                         end--;
854                     }
855                     if( end <= p )
856                     {
857                         cfg.psz_value = NULL;
858                     }
859                     else
860                     {
861                         cfg.psz_value = _strndup( p, end - p );
862                     }
863                 }
864
865                 p = end;
866                 SKIPSPACE( p );
867             }
868             else
869             {
870                 cfg.psz_value = NULL;
871             }
872
873             cfg.p_next = NULL;
874             if( p_cfg )
875             {
876                 p_cfg->p_next = malloc( sizeof( sout_cfg_t ) );
877                 memcpy( p_cfg->p_next, &cfg, sizeof( sout_cfg_t ) );
878
879                 p_cfg = p_cfg->p_next;
880             }
881             else
882             {
883                 p_cfg = malloc( sizeof( sout_cfg_t ) );
884                 memcpy( p_cfg, &cfg, sizeof( sout_cfg_t ) );
885
886                 *pp_cfg = p_cfg;
887             }
888
889             if( *p == ',' )
890             {
891                 p++;
892             }
893
894             if( *p == '}' )
895             {
896                 p++;
897
898                 break;
899             }
900         }
901     }
902
903     if( *p == ':' )
904     {
905         return( strdup( p + 1 ) );
906     }
907
908     return( NULL );
909 }
910
911 static void sout_cfg_free( sout_cfg_t *p_cfg )
912 {
913     while( p_cfg != NULL )
914     {
915         sout_cfg_t *p_next;
916
917         p_next = p_cfg->p_next;
918
919         FREE( p_cfg->psz_name );
920         FREE( p_cfg->psz_value );
921         free( p_cfg );
922
923         p_cfg = p_next;
924     }
925 }
926
927 void __sout_ParseCfg( vlc_object_t *p_this, char *psz_prefix, const char **ppsz_options, sout_cfg_t *cfg )
928 {
929     char *psz_name;
930     int  i_type;
931     int  i;
932
933     /* First, var_Create all variables */
934     for( i = 0; ppsz_options[i] != NULL; i++ )
935     {
936         asprintf( &psz_name, "%s%s", psz_prefix, ppsz_options[i] );
937
938         i_type = config_GetType( p_this, psz_name );
939
940         var_Create( p_this, psz_name, i_type | VLC_VAR_DOINHERIT );
941         free( psz_name );
942     }
943
944     /* Now parse options and set value */
945     if( psz_prefix == NULL ) psz_prefix = "";
946
947     while( cfg )
948     {
949         vlc_value_t val;
950
951         if( cfg->psz_name == NULL || *cfg->psz_name == '\0' )
952         {
953             cfg = cfg->p_next;
954             continue;
955         }
956         for( i = 0; ppsz_options[i] != NULL; i++ )
957         {
958             if( !strcmp( ppsz_options[i], cfg->psz_name ) )
959             {
960                 break;
961             }
962         }
963         if( ppsz_options[i] == NULL )
964         {
965             cfg = cfg->p_next;
966             continue;
967         }
968
969         /* create name */
970         asprintf( &psz_name, "%s%s", psz_prefix, cfg->psz_name );
971
972         /* get the type of the variable */
973         i_type = config_GetType( p_this, psz_name );
974         if( !i_type )
975         {
976             /* TODO check for no, no- */
977         }
978
979         if( !i_type )
980         {
981             msg_Warn( p_this, "unknown option %s (value=%s)", cfg->psz_name, cfg->psz_value );
982             goto next;
983         }
984         if( i_type != VLC_VAR_BOOL && cfg->psz_value == NULL )
985         {
986             msg_Warn( p_this, "missing value for option %s", cfg->psz_name );
987             goto next;
988         }
989
990         switch( i_type )
991         {
992             case VLC_VAR_INTEGER:
993                 val.i_int = atoi( cfg->psz_value ? cfg->psz_value : "0" );
994                 break;
995             case VLC_VAR_FLOAT:
996                 val.f_float = atof( cfg->psz_value ? cfg->psz_value : "0" );
997                 break;
998             case VLC_VAR_STRING:
999                 val.psz_string = cfg->psz_value;
1000                 break;
1001
1002             case VLC_VAR_BOOL:
1003             default:
1004                 msg_Warn( p_this, "unhandled config var type" );
1005                 memset( &val, 0, sizeof( vlc_value_t ) );
1006                 break;
1007         }
1008         var_Set( p_this, psz_name, val );
1009         msg_Dbg( p_this, "set sout option: %s to %s", psz_name, cfg->psz_value );
1010
1011     next:
1012         free( psz_name );
1013         cfg = cfg->p_next;
1014     }
1015 }
1016
1017
1018 /*
1019  * XXX name and p_cfg are used (-> do NOT free them)
1020  */
1021 sout_stream_t *sout_stream_new( sout_instance_t *p_sout,
1022                                 char *psz_chain )
1023 {
1024     sout_stream_t *p_stream;
1025
1026     if( !psz_chain )
1027     {
1028         msg_Err( p_sout, "invalid chain" );
1029         return NULL;
1030     }
1031
1032     p_stream = vlc_object_create( p_sout, sizeof( sout_stream_t ) );
1033
1034     if( !p_stream )
1035     {
1036         msg_Err( p_sout, "out of memory" );
1037         return NULL;
1038     }
1039
1040     p_stream->p_sout   = p_sout;
1041     p_stream->p_sys    = NULL;
1042
1043     p_stream->psz_next =
1044         sout_cfg_parser( &p_stream->psz_name, &p_stream->p_cfg, psz_chain);
1045
1046     msg_Dbg( p_sout, "stream=`%s'", p_stream->psz_name );
1047
1048     vlc_object_attach( p_stream, p_sout );
1049
1050     p_stream->p_module =
1051         module_Need( p_stream, "sout stream", p_stream->psz_name, VLC_TRUE );
1052
1053     if( !p_stream->p_module )
1054     {
1055         sout_stream_delete( p_stream );
1056         return NULL;
1057     }
1058
1059     return p_stream;
1060 }
1061
1062 void sout_stream_delete( sout_stream_t *p_stream )
1063 {
1064     msg_Dbg( p_stream, "destroying chain... (name=%s)", p_stream->psz_name );
1065
1066     vlc_object_detach( p_stream );
1067     if( p_stream->p_module ) module_Unneed( p_stream, p_stream->p_module );
1068
1069     FREE( p_stream->psz_name );
1070     FREE( p_stream->psz_next );
1071
1072     sout_cfg_free( p_stream->p_cfg );
1073
1074     msg_Dbg( p_stream, "destroying chain done" );
1075     vlc_object_destroy( p_stream );
1076 }
1077
1078 static char *_sout_stream_url_to_chain( vlc_object_t *p_this, char *psz_url )
1079 {
1080     mrl_t       mrl;
1081     char        *psz_chain, *p;
1082
1083     mrl_Parse( &mrl, psz_url );
1084     p = psz_chain = malloc( 500 + strlen( mrl.psz_way ) +
1085                                   strlen( mrl.psz_access ) +
1086                                   strlen( mrl.psz_name ) );
1087
1088
1089     if( config_GetInt( p_this, "sout-display" ) )
1090     {
1091         p += sprintf( p, "duplicate{dst=display,dst=std{mux=\"%s\",access=\"%s\",url=\"%s\"}}",
1092                       mrl.psz_way, mrl.psz_access, mrl.psz_name );
1093     }
1094     else
1095     {
1096         p += sprintf( p, "std{mux=\"%s\",access=\"%s\",url=\"%s\"}",
1097                       mrl.psz_way, mrl.psz_access, mrl.psz_name );
1098     }
1099
1100     mrl_Clean( &mrl );
1101     return( psz_chain );
1102 }