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