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