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