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