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