]> git.sesse.net Git - vlc/blob - src/stream_output/stream_output.c
dc586418eabd50b02bcce1b795d3a6ee47c67811
[vlc] / src / stream_output / stream_output.c
1 /*****************************************************************************
2  * stream_output.c : stream output module
3  *****************************************************************************
4  * Copyright (C) 2002-2007 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_playlist.h>
38
39 #include "stream_output.h"
40
41 #include <vlc_meta.h>
42
43 #include "input/input_internal.h"
44
45 #undef DEBUG_BUFFER
46 /*****************************************************************************
47  * Local prototypes
48  *****************************************************************************/
49 #define sout_stream_url_to_chain( p, s ) \
50     _sout_stream_url_to_chain( VLC_OBJECT(p), s )
51 static char *_sout_stream_url_to_chain( vlc_object_t *, char * );
52
53 /*
54  * Generic MRL parser
55  *
56  */
57
58 typedef struct
59 {
60     char *psz_access;
61     char *psz_way;
62     char *psz_name;
63 } mrl_t;
64
65 /* mrl_Parse: parse psz_mrl and fill p_mrl */
66 static int  mrl_Parse( mrl_t *p_mrl, const char *psz_mrl );
67 /* mrl_Clean: clean p_mrl  after a call to mrl_Parse */
68 static void mrl_Clean( mrl_t *p_mrl );
69
70 /*****************************************************************************
71  * sout_NewInstance: creates a new stream output instance
72  *****************************************************************************/
73 sout_instance_t *__sout_NewInstance( vlc_object_t *p_parent, char * psz_dest )
74 {
75     sout_instance_t *p_sout;
76     vlc_value_t keep;
77
78     if( var_Get( p_parent, "sout-keep", &keep ) >= 0 && keep.b_bool )
79     {
80         /* Remove the sout from the playlist garbage collector */
81         playlist_t *p_playlist = pl_Yield( p_parent );
82
83         vlc_mutex_lock( &p_playlist->gc_lock );
84         p_sout = vlc_object_find( p_playlist, VLC_OBJECT_SOUT, FIND_CHILD );
85         if( p_sout && p_sout->p_parent != (vlc_object_t *)p_playlist )
86         {
87             vlc_object_release( p_sout );
88             p_sout = NULL;
89         }
90         if( p_sout )
91             vlc_object_detach( p_sout );    /* Remove it from the GC */
92         vlc_mutex_unlock( &p_playlist->gc_lock );
93
94         pl_Release( p_parent );
95
96         /* */
97
98         if( p_sout )
99         {
100             if( !strcmp( p_sout->psz_sout, psz_dest ) )
101             {
102                 msg_Dbg( p_parent, "sout keep: reusing sout" );
103                 msg_Dbg( p_parent, "sout keep: you probably want to use "
104                           "gather stream_out" );
105                 vlc_object_attach( p_sout, p_parent );
106                 vlc_object_release( p_sout );
107                 return p_sout;
108             }
109
110             msg_Dbg( p_parent, "sout keep: destroying unusable sout" );
111             vlc_object_release( p_sout );
112             sout_DeleteInstance( p_sout );
113         }
114     }
115
116     /* *** Allocate descriptor *** */
117     p_sout = vlc_object_create( p_parent, VLC_OBJECT_SOUT );
118     if( p_sout == NULL )
119     {
120         msg_Err( p_parent, "out of memory" );
121         return NULL;
122     }
123
124     /* *** init descriptor *** */
125     p_sout->psz_sout    = strdup( psz_dest );
126     p_sout->p_meta      = NULL;
127     p_sout->i_out_pace_nocontrol = 0;
128     p_sout->p_sys       = NULL;
129
130     vlc_mutex_init( p_sout, &p_sout->lock );
131     if( psz_dest && psz_dest[0] == '#' )
132     {
133         p_sout->psz_chain = strdup( &psz_dest[1] );
134     }
135     else
136     {
137         p_sout->psz_chain = sout_stream_url_to_chain( p_sout, psz_dest );
138         msg_Dbg( p_sout, "using sout chain=`%s'", p_sout->psz_chain );
139     }
140     p_sout->p_stream = NULL;
141
142     /* attach it for inherit */
143     vlc_object_attach( p_sout, p_parent );
144
145     p_sout->p_stream = sout_StreamNew( p_sout, p_sout->psz_chain );
146
147     if( p_sout->p_stream == NULL )
148     {
149         msg_Err( p_sout, "stream chain failed for `%s'", p_sout->psz_chain );
150
151         FREENULL( p_sout->psz_sout );
152         FREENULL( p_sout->psz_chain );
153
154         vlc_object_detach( p_sout );
155         vlc_object_destroy( p_sout );
156         return NULL;
157     }
158
159     return p_sout;
160 }
161
162 /*****************************************************************************
163  * sout_DeleteInstance: delete a previously allocated instance
164  *****************************************************************************/
165 void sout_DeleteInstance( sout_instance_t * p_sout )
166 {
167     /* Unlink object */
168     vlc_object_detach( p_sout );
169
170     /* remove the stream out chain */
171     sout_StreamDelete( p_sout->p_stream );
172
173     /* *** free all string *** */
174     FREENULL( p_sout->psz_sout );
175     FREENULL( p_sout->psz_chain );
176
177     /* delete meta */
178     if( p_sout->p_meta )
179     {
180         vlc_meta_Delete( p_sout->p_meta );
181     }
182
183     vlc_mutex_destroy( &p_sout->lock );
184
185     /* *** free structure *** */
186     vlc_object_destroy( p_sout );
187 }
188
189 /*****************************************************************************
190  * Packetizer/Input
191  *****************************************************************************/
192 sout_packetizer_input_t *sout_InputNew( sout_instance_t *p_sout,
193                                         es_format_t *p_fmt )
194 {
195     sout_packetizer_input_t *p_input;
196
197     msg_Dbg( p_sout, "adding a new input" );
198
199     /* *** create a packetizer input *** */
200     p_input         = malloc( sizeof( sout_packetizer_input_t ) );
201     p_input->p_sout = p_sout;
202     p_input->p_fmt  = p_fmt;
203
204     if( p_fmt->i_codec == VLC_FOURCC( 'n', 'u', 'l', 'l' ) )
205     {
206         vlc_object_release( p_sout );
207         return p_input;
208     }
209
210     /* *** add it to the stream chain */
211     vlc_mutex_lock( &p_sout->lock );
212     p_input->id = p_sout->p_stream->pf_add( p_sout->p_stream, p_fmt );
213     vlc_mutex_unlock( &p_sout->lock );
214
215     if( p_input->id == NULL )
216     {
217         free( p_input );
218         return NULL;
219     }
220
221     return( p_input );
222 }
223
224 /*****************************************************************************
225  *
226  *****************************************************************************/
227 int sout_InputDelete( sout_packetizer_input_t *p_input )
228 {
229     sout_instance_t     *p_sout = p_input->p_sout;
230
231     msg_Dbg( p_sout, "removing an input" );
232
233     if( p_input->p_fmt->i_codec != VLC_FOURCC( 'n', 'u', 'l', 'l' ) )
234     {
235         vlc_mutex_lock( &p_sout->lock );
236         p_sout->p_stream->pf_del( p_sout->p_stream, p_input->id );
237         vlc_mutex_unlock( &p_sout->lock );
238     }
239
240     free( p_input );
241
242     return( VLC_SUCCESS);
243 }
244
245 /*****************************************************************************
246  *
247  *****************************************************************************/
248 int sout_InputSendBuffer( sout_packetizer_input_t *p_input,
249                           block_t *p_buffer )
250 {
251     sout_instance_t     *p_sout = p_input->p_sout;
252     int                 i_ret;
253
254     if( p_input->p_fmt->i_codec == VLC_FOURCC( 'n', 'u', 'l', 'l' ) )
255     {
256         block_Release( p_buffer );
257         return VLC_SUCCESS;
258     }
259     if( p_buffer->i_dts <= 0 )
260     {
261         msg_Warn( p_sout, "trying to send non-dated packet to stream output!");
262         block_Release( p_buffer );
263         return VLC_SUCCESS;
264     }
265
266     vlc_mutex_lock( &p_sout->lock );
267     i_ret = p_sout->p_stream->pf_send( p_sout->p_stream,
268                                        p_input->id, p_buffer );
269     vlc_mutex_unlock( &p_sout->lock );
270
271     return i_ret;
272 }
273
274 /*****************************************************************************
275  * sout_AccessOutNew: allocate a new access out
276  *****************************************************************************/
277 sout_access_out_t *sout_AccessOutNew( sout_instance_t *p_sout,
278                                       const char *psz_access, const char *psz_name )
279 {
280     sout_access_out_t *p_access;
281     char              *psz_next;
282
283     if( !( p_access = vlc_object_create( p_sout,
284                                          sizeof( sout_access_out_t ) ) ) )
285     {
286         msg_Err( p_sout, "out of memory" );
287         return NULL;
288     }
289
290     psz_next = config_ChainCreate( &p_access->psz_access, &p_access->p_cfg,
291                                    psz_access );
292     if( psz_next )
293     {
294         free( psz_next );
295     }
296     p_access->psz_path   = strdup( psz_name ? psz_name : "" );
297     p_access->p_sout     = p_sout;
298     p_access->p_sys = NULL;
299     p_access->pf_seek    = NULL;
300     p_access->pf_read    = NULL;
301     p_access->pf_write   = NULL;
302     p_access->pf_control = NULL;
303     p_access->p_module   = NULL;
304
305     p_access->i_writes = 0;
306     p_access->i_sent_bytes = 0;
307
308     vlc_object_attach( p_access, p_sout );
309
310     p_access->p_module   =
311         module_Need( p_access, "sout access", p_access->psz_access, VLC_TRUE );
312
313     if( !p_access->p_module )
314     {
315         free( p_access->psz_access );
316         free( p_access->psz_path );
317         vlc_object_detach( p_access );
318         vlc_object_destroy( p_access );
319         return( NULL );
320     }
321
322     return p_access;
323 }
324 /*****************************************************************************
325  * sout_AccessDelete: delete an access out
326  *****************************************************************************/
327 void sout_AccessOutDelete( sout_access_out_t *p_access )
328 {
329     vlc_object_detach( p_access );
330     if( p_access->p_module )
331     {
332         module_Unneed( p_access, p_access->p_module );
333     }
334     free( p_access->psz_access );
335
336     config_ChainDestroy( p_access->p_cfg );
337
338     free( p_access->psz_path );
339
340     vlc_object_destroy( p_access );
341 }
342
343 /*****************************************************************************
344  * sout_AccessSeek:
345  *****************************************************************************/
346 int sout_AccessOutSeek( sout_access_out_t *p_access, off_t i_pos )
347 {
348     return p_access->pf_seek( p_access, i_pos );
349 }
350
351 /*****************************************************************************
352  * sout_AccessRead:
353  *****************************************************************************/
354 int sout_AccessOutRead( sout_access_out_t *p_access, block_t *p_buffer )
355 {
356     return( p_access->pf_read ?
357             p_access->pf_read( p_access, p_buffer ) : VLC_EGENERIC );
358 }
359
360 /*****************************************************************************
361  * sout_AccessWrite:
362  *****************************************************************************/
363 int sout_AccessOutWrite( sout_access_out_t *p_access, block_t *p_buffer )
364 {
365     int i_total = 0;
366     p_access->i_writes++;
367     p_access->i_sent_bytes += p_buffer->i_buffer;
368     if( p_access->p_libvlc->b_stats && p_access->i_writes % 30 == 0 )
369     {
370         /* Access_out -> sout_instance -> input_thread_t */
371         input_thread_t *p_input =
372             (input_thread_t *)vlc_object_find( p_access, VLC_OBJECT_INPUT,
373                                                FIND_PARENT );
374         if( p_input )
375         {
376             stats_UpdateInteger( p_input, p_input->p->counters.p_sout_sent_packets,
377                                  30, NULL );
378             stats_UpdateInteger( p_input, p_input->p->counters.p_sout_sent_bytes,
379                                  p_access->i_sent_bytes, &i_total );
380             stats_UpdateFloat( p_input, p_input->p->counters.p_sout_send_bitrate,
381                                  (float)i_total, NULL );
382             p_access->i_sent_bytes = 0;
383             vlc_object_release( p_input );
384         }
385     }
386     return p_access->pf_write( p_access, p_buffer );
387 }
388
389 /**
390  * sout_AccessOutControl
391  */
392 int sout_AccessOutControl (sout_access_out_t *access, int query, va_list args)
393 {
394     return (access->pf_control) ? access->pf_control (access, query, args)
395                                 : VLC_EGENERIC;
396 }
397
398 /*****************************************************************************
399  * sout_MuxNew: create a new mux
400  *****************************************************************************/
401 sout_mux_t * sout_MuxNew( sout_instance_t *p_sout, char *psz_mux,
402                           sout_access_out_t *p_access )
403 {
404     sout_mux_t *p_mux;
405     char       *psz_next;
406
407     p_mux = vlc_object_create( p_sout, sizeof( sout_mux_t ) );
408     if( p_mux == NULL )
409     {
410         msg_Err( p_sout, "out of memory" );
411         return NULL;
412     }
413
414     p_mux->p_sout = p_sout;
415     psz_next = config_ChainCreate( &p_mux->psz_mux, &p_mux->p_cfg, psz_mux );
416     if( psz_next ) free( psz_next );
417
418     p_mux->p_access     = p_access;
419     p_mux->pf_control   = NULL;
420     p_mux->pf_addstream = NULL;
421     p_mux->pf_delstream = NULL;
422     p_mux->pf_mux       = NULL;
423     p_mux->i_nb_inputs  = 0;
424     p_mux->pp_inputs    = NULL;
425
426     p_mux->p_sys        = NULL;
427     p_mux->p_module     = NULL;
428
429     p_mux->b_add_stream_any_time = VLC_FALSE;
430     p_mux->b_waiting_stream = VLC_TRUE;
431     p_mux->i_add_stream_start = -1;
432
433     vlc_object_attach( p_mux, p_sout );
434
435     p_mux->p_module =
436         module_Need( p_mux, "sout mux", p_mux->psz_mux, VLC_TRUE );
437
438     if( p_mux->p_module == NULL )
439     {
440         FREENULL( p_mux->psz_mux );
441
442         vlc_object_detach( p_mux );
443         vlc_object_destroy( p_mux );
444         return NULL;
445     }
446
447     /* *** probe mux capacity *** */
448     if( p_mux->pf_control )
449     {
450         int b_answer = VLC_FALSE;
451
452         if( sout_MuxControl( p_mux, MUX_CAN_ADD_STREAM_WHILE_MUXING,
453                              &b_answer ) )
454         {
455             b_answer = VLC_FALSE;
456         }
457
458         if( b_answer )
459         {
460             msg_Dbg( p_sout, "muxer support adding stream at any time" );
461             p_mux->b_add_stream_any_time = VLC_TRUE;
462             p_mux->b_waiting_stream = VLC_FALSE;
463
464             /* If we control the output pace then it's better to wait before
465              * starting muxing (generates better streams/files). */
466             if( !p_sout->i_out_pace_nocontrol )
467             {
468                 b_answer = VLC_TRUE;
469             }
470             else if( sout_MuxControl( p_mux, MUX_GET_ADD_STREAM_WAIT,
471                                       &b_answer ) )
472             {
473                 b_answer = VLC_FALSE;
474             }
475
476             if( b_answer )
477             {
478                 msg_Dbg( p_sout, "muxer prefers to wait for all ES before "
479                          "starting to mux" );
480                 p_mux->b_waiting_stream = VLC_TRUE;
481             }
482         }
483     }
484
485     return p_mux;
486 }
487
488 /*****************************************************************************
489  * sout_MuxDelete:
490  *****************************************************************************/
491 void sout_MuxDelete( sout_mux_t *p_mux )
492 {
493     vlc_object_detach( p_mux );
494     if( p_mux->p_module )
495     {
496         module_Unneed( p_mux, p_mux->p_module );
497     }
498     free( p_mux->psz_mux );
499
500     config_ChainDestroy( p_mux->p_cfg );
501
502     vlc_object_destroy( p_mux );
503 }
504
505 /*****************************************************************************
506  * sout_MuxAddStream:
507  *****************************************************************************/
508 sout_input_t *sout_MuxAddStream( sout_mux_t *p_mux, es_format_t *p_fmt )
509 {
510     sout_input_t *p_input;
511
512     if( !p_mux->b_add_stream_any_time && !p_mux->b_waiting_stream )
513     {
514         msg_Err( p_mux, "cannot add a new stream (unsupported while muxing "
515                         "to this format)" );
516         return NULL;
517     }
518
519     msg_Dbg( p_mux, "adding a new input" );
520
521     /* create a new sout input */
522     p_input = malloc( sizeof( sout_input_t ) );
523     p_input->p_sout = p_mux->p_sout;
524     p_input->p_fmt  = p_fmt;
525     p_input->p_fifo = block_FifoNew( p_mux->p_sout );
526     p_input->p_sys  = NULL;
527
528     TAB_APPEND( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input );
529     if( p_mux->pf_addstream( p_mux, p_input ) < 0 )
530     {
531             msg_Err( p_mux, "cannot add this stream" );
532             TAB_REMOVE( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input );
533             block_FifoRelease( p_input->p_fifo );
534             free( p_input );
535             return NULL;
536     }
537
538     return p_input;
539 }
540
541 /*****************************************************************************
542  * sout_MuxDeleteStream:
543  *****************************************************************************/
544 void sout_MuxDeleteStream( sout_mux_t *p_mux, sout_input_t *p_input )
545 {
546     int i_index;
547
548     if( p_mux->b_waiting_stream && p_input->p_fifo->i_depth > 0 )
549     {
550         /* We stop waiting, and call the muxer for taking care of the data
551          * before we remove this es */
552         p_mux->b_waiting_stream = VLC_FALSE;
553         p_mux->pf_mux( p_mux );
554     }
555
556     TAB_FIND( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input, i_index );
557     if( i_index >= 0 )
558     {
559         if( p_mux->pf_delstream( p_mux, p_input ) < 0 )
560         {
561             msg_Err( p_mux, "cannot delete this stream from mux" );
562         }
563
564         /* remove the entry */
565         TAB_REMOVE( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input );
566
567         if( p_mux->i_nb_inputs == 0 )
568         {
569             msg_Warn( p_mux, "no more input streams for this mux" );
570         }
571
572         block_FifoRelease( p_input->p_fifo );
573         free( p_input );
574     }
575 }
576
577 /*****************************************************************************
578  * sout_MuxSendBuffer:
579  *****************************************************************************/
580 void sout_MuxSendBuffer( sout_mux_t *p_mux, sout_input_t *p_input,
581                          block_t *p_buffer )
582 {
583     block_FifoPut( p_input->p_fifo, p_buffer );
584
585     if( p_mux->p_sout->i_out_pace_nocontrol )
586     {
587         mtime_t current_date = mdate();
588         if ( current_date > p_buffer->i_dts )
589             msg_Warn( p_mux, "late buffer for mux input ("I64Fd")",
590                       current_date - p_buffer->i_dts );
591     }
592
593     if( p_mux->b_waiting_stream )
594     {
595         if( p_mux->i_add_stream_start < 0 )
596         {
597             p_mux->i_add_stream_start = p_buffer->i_dts;
598         }
599
600         if( p_mux->i_add_stream_start >= 0 &&
601             p_mux->i_add_stream_start + I64C(1500000) < p_buffer->i_dts )
602         {
603             /* Wait until we have more than 1.5 seconds worth of data
604              * before start muxing */
605             p_mux->b_waiting_stream = VLC_FALSE;
606         }
607         else
608         {
609             return;
610         }
611     }
612     p_mux->pf_mux( p_mux );
613 }
614
615 /*****************************************************************************
616  *
617  *****************************************************************************/
618 static int mrl_Parse( mrl_t *p_mrl, const char *psz_mrl )
619 {
620     char * psz_dup = strdup( psz_mrl );
621     char * psz_parser = psz_dup;
622     const char * psz_access;
623     const char * psz_way;
624     char * psz_name;
625
626     /* *** first parse psz_dest */
627     while( *psz_parser && *psz_parser != ':' )
628     {
629         if( *psz_parser == '{' )
630         {
631             while( *psz_parser && *psz_parser != '}' )
632             {
633                 psz_parser++;
634             }
635             if( *psz_parser )
636             {
637                 psz_parser++;
638             }
639         }
640         else
641         {
642             psz_parser++;
643         }
644     }
645 #if defined( WIN32 ) || defined( UNDER_CE )
646     if( psz_parser - psz_dup == 1 )
647     {
648         /* msg_Warn( p_sout, "drive letter %c: found in source string",
649                           *psz_dup ) ; */
650         psz_parser = "";
651     }
652 #endif
653
654     if( !*psz_parser )
655     {
656         psz_access = psz_way = "";
657         psz_name = psz_dup;
658     }
659     else
660     {
661         *psz_parser++ = '\0';
662
663         /* let's skip '//' */
664         if( psz_parser[0] == '/' && psz_parser[1] == '/' )
665         {
666             psz_parser += 2 ;
667         }
668
669         psz_name = psz_parser ;
670
671         /* Come back to parse the access and mux plug-ins */
672         psz_parser = psz_dup;
673
674         if( !*psz_parser )
675         {
676             /* No access */
677             psz_access = "";
678         }
679         else if( *psz_parser == '/' )
680         {
681             /* No access */
682             psz_access = "";
683             psz_parser++;
684         }
685         else
686         {
687             psz_access = psz_parser;
688
689             while( *psz_parser && *psz_parser != '/' )
690             {
691                 if( *psz_parser == '{' )
692                 {
693                     while( *psz_parser && *psz_parser != '}' )
694                     {
695                         psz_parser++;
696                     }
697                     if( *psz_parser )
698                     {
699                         psz_parser++;
700                     }
701                 }
702                 else
703                 {
704                     psz_parser++;
705                 }
706             }
707
708             if( *psz_parser == '/' )
709             {
710                 *psz_parser++ = '\0';
711             }
712         }
713
714         if( !*psz_parser )
715         {
716             /* No mux */
717             psz_way = "";
718         }
719         else
720         {
721             psz_way = psz_parser;
722         }
723     }
724
725     p_mrl->psz_access = strdup( psz_access );
726     p_mrl->psz_way    = strdup( psz_way );
727     p_mrl->psz_name   = strdup( psz_name );
728
729     free( psz_dup );
730     return( VLC_SUCCESS );
731 }
732
733
734 /* mrl_Clean: clean p_mrl  after a call to mrl_Parse */
735 static void mrl_Clean( mrl_t *p_mrl )
736 {
737     FREENULL( p_mrl->psz_access );
738     FREENULL( p_mrl->psz_way );
739     FREENULL( p_mrl->psz_name );
740 }
741
742
743 /****************************************************************************
744  ****************************************************************************
745  **
746  **
747  **
748  ****************************************************************************
749  ****************************************************************************/
750
751 /* create a complete chain */
752 /* chain format:
753     module{option=*:option=*}[:module{option=*:...}]
754  */
755
756 /*
757  * parse module{options=str, option="str "}:
758  *  return a pointer on the rest
759  *  XXX: psz_chain is modified
760  */
761 #define SKIPSPACE( p ) { while( *p && ( *p == ' ' || *p == '\t' ) ) p++; }
762 #define SKIPTRAILINGSPACE( p, e ) \
763     { while( e > p && ( *(e-1) == ' ' || *(e-1) == '\t' ) ) e--; }
764
765 /* go accross " " and { } */
766 static char *_get_chain_end( char *str )
767 {
768     char c, *p = str;
769
770     SKIPSPACE( p );
771
772     for( ;; )
773     {
774         if( !*p || *p == ',' || *p == '}' ) return p;
775
776         if( *p != '{' && *p != '"' && *p != '\'' )
777         {
778             p++;
779             continue;
780         }
781
782         if( *p == '{' ) c = '}';
783         else c = *p;
784         p++;
785
786         for( ;; )
787         {
788             if( !*p ) return p;
789
790             if( *p == c ) return ++p;
791             else if( *p == '{' && c == '}' ) p = _get_chain_end( p );
792             else p++;
793         }
794     }
795 }
796
797 /*
798  * XXX name and p_cfg are used (-> do NOT free them)
799  */
800 sout_stream_t *sout_StreamNew( sout_instance_t *p_sout, char *psz_chain )
801 {
802     sout_stream_t *p_stream;
803
804     if( !psz_chain )
805     {
806         msg_Err( p_sout, "invalid chain" );
807         return NULL;
808     }
809
810     p_stream = vlc_object_create( p_sout, sizeof( sout_stream_t ) );
811
812     if( !p_stream )
813     {
814         msg_Err( p_sout, "out of memory" );
815         return NULL;
816     }
817
818     p_stream->p_sout   = p_sout;
819     p_stream->p_sys    = NULL;
820
821     p_stream->psz_next =
822         config_ChainCreate( &p_stream->psz_name, &p_stream->p_cfg, psz_chain);
823
824     msg_Dbg( p_sout, "stream=`%s'", p_stream->psz_name );
825
826     vlc_object_attach( p_stream, p_sout );
827
828     p_stream->p_module =
829         module_Need( p_stream, "sout stream", p_stream->psz_name, VLC_TRUE );
830
831     if( !p_stream->p_module )
832     {
833         sout_StreamDelete( p_stream );
834         return NULL;
835     }
836
837     return p_stream;
838 }
839
840 void sout_StreamDelete( sout_stream_t *p_stream )
841 {
842     msg_Dbg( p_stream, "destroying chain... (name=%s)", p_stream->psz_name );
843
844     vlc_object_detach( p_stream );
845     if( p_stream->p_module ) module_Unneed( p_stream, p_stream->p_module );
846
847     FREENULL( p_stream->psz_name );
848     FREENULL( p_stream->psz_next );
849
850     config_ChainDestroy( p_stream->p_cfg );
851
852     msg_Dbg( p_stream, "destroying chain done" );
853     vlc_object_destroy( p_stream );
854 }
855
856 static char *_sout_stream_url_to_chain( vlc_object_t *p_this, char *psz_url )
857 {
858     mrl_t       mrl;
859     char        *psz_chain, *p;
860
861     mrl_Parse( &mrl, psz_url );
862     p = psz_chain = malloc( 500 + strlen( mrl.psz_way ) +
863                                   strlen( mrl.psz_access ) +
864                                   strlen( mrl.psz_name ) );
865
866
867     if( config_GetInt( p_this, "sout-display" ) )
868     {
869         p += sprintf( p, "duplicate{dst=display,dst=std{mux=\"%s\","
870                       "access=\"%s\",dst=\"%s\"}}",
871                       mrl.psz_way, mrl.psz_access, mrl.psz_name );
872     }
873     else
874     {
875         p += sprintf( p, "std{mux=\"%s\",access=\"%s\",dst=\"%s\"}",
876                       mrl.psz_way, mrl.psz_access, mrl.psz_name );
877     }
878
879     mrl_Clean( &mrl );
880     return( psz_chain );
881 }