stream_output.c 30.8 KB
Newer Older
1
2
3
/*****************************************************************************
 * stream_output.c : stream output module
 *****************************************************************************
zorglub's avatar
zorglub committed
4
 * Copyright (C) 2002-2004 VideoLAN
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
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                                                /* free() */
#include <stdio.h>                                              /* sprintf() */
#include <string.h>                                            /* strerror() */

#include <vlc/vlc.h>
#include <vlc/sout.h>
35
36
37

#include "vlc_meta.h"

38
#undef DEBUG_BUFFER
39
40
41
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
42
static void sout_CfgDestroy( sout_cfg_t * );
43

gbazin's avatar
gbazin committed
44
45
#define sout_stream_url_to_chain( p, s ) \
    _sout_stream_url_to_chain( VLC_OBJECT(p), s )
46
47
static char *_sout_stream_url_to_chain( vlc_object_t *, char * );

48
49
50
51
52
53
54
/*
 * Generic MRL parser
 *
 */

typedef struct
{
55
56
    char *psz_access;
    char *psz_way;
57
58
59
60
61
62
63
64
65
    char *psz_name;
} mrl_t;

/* mrl_Parse: parse psz_mrl and fill p_mrl */
static int  mrl_Parse( mrl_t *p_mrl, char *psz_mrl );
/* mrl_Clean: clean p_mrl  after a call to mrl_Parse */
static void mrl_Clean( mrl_t *p_mrl );

#define FREE( p ) if( p ) { free( p ); (p) = NULL; }
66

67
68
69
/*****************************************************************************
 * sout_NewInstance: creates a new stream output instance
 *****************************************************************************/
70
sout_instance_t *__sout_NewInstance( vlc_object_t *p_parent, char * psz_dest )
71
{
72
    sout_instance_t *p_sout;
73
74
75
76
77
78
79
80
81
82
    vlc_value_t keep;

    if( var_Get( p_parent, "sout-keep", &keep ) < 0 )
    {
        msg_Warn( p_parent, "cannot get sout-keep value" );
        keep.b_bool = VLC_FALSE;
    }
    else if( keep.b_bool )
    {
        msg_Warn( p_parent, "sout-keep true" );
83
84
        if( ( p_sout = vlc_object_find( p_parent, VLC_OBJECT_SOUT,
                                        FIND_ANYWHERE ) ) )
85
86
87
88
        {
            if( !strcmp( p_sout->psz_sout, psz_dest ) )
            {
                msg_Warn( p_parent, "sout keep : reusing sout" );
89
90
                msg_Warn( p_parent, "sout keep : you probably want to use "
                          "gather stream_out" );
91
92
93
94
95
96
97
98
99
100
101
102
103
104
                vlc_object_detach( p_sout );
                vlc_object_attach( p_sout, p_parent );
                vlc_object_release( p_sout );
                return p_sout;
            }
            else
            {
                msg_Warn( p_parent, "sout keep : destroying unusable sout" );
                sout_DeleteInstance( p_sout );
            }
        }
    }
    else if( !keep.b_bool )
    {
105
106
        while( ( p_sout = vlc_object_find( p_parent, VLC_OBJECT_SOUT,
                                           FIND_PARENT ) ) )
107
108
109
110
111
        {
            msg_Warn( p_parent, "sout keep : destroying old sout" );
            sout_DeleteInstance( p_sout );
        }
    }
112

113
    /* *** Allocate descriptor *** */
114
115
116
117
118
119
120
    p_sout = vlc_object_create( p_parent, VLC_OBJECT_SOUT );
    if( p_sout == NULL )
    {
        msg_Err( p_parent, "out of memory" );
        return NULL;
    }

121
122
    /* *** init descriptor *** */
    p_sout->psz_sout    = strdup( psz_dest );
123
    p_sout->p_meta      = NULL;
124
    p_sout->i_out_pace_nocontrol = 0;
125
    p_sout->p_sys       = NULL;
126
127

    vlc_mutex_init( p_sout, &p_sout->lock );
128
    if( psz_dest && psz_dest[0] == '#' )
129
    {
130
        p_sout->psz_chain = strdup( &psz_dest[1] );
131
    }
132
    else
133
    {
134
135
        p_sout->psz_chain = sout_stream_url_to_chain( p_sout, psz_dest );
        msg_Dbg( p_sout, "using sout chain=`%s'", p_sout->psz_chain );
136
    }
137
138
139
140
    p_sout->p_stream = NULL;

    /* attach it for inherit */
    vlc_object_attach( p_sout, p_parent );
141

142
    p_sout->p_stream = sout_StreamNew( p_sout, p_sout->psz_chain );
143

144
    if( p_sout->p_stream == NULL )
145
    {
146
147
148
149
150
        msg_Err( p_sout, "stream chained failed for `%s'", p_sout->psz_chain );

        FREE( p_sout->psz_sout );
        FREE( p_sout->psz_chain );

151
        vlc_object_detach( p_sout );
152
        vlc_object_destroy( p_sout );
153
        return NULL;
154
155
156
157
    }

    return p_sout;
}
158
159
160
161
162
163
164
165
/*****************************************************************************
 * sout_DeleteInstance: delete a previously allocated instance
 *****************************************************************************/
void sout_DeleteInstance( sout_instance_t * p_sout )
{
    /* Unlink object */
    vlc_object_detach( p_sout );

166
    /* remove the stream out chain */
167
    sout_StreamDelete( p_sout->p_stream );
168

169
170
    /* *** free all string *** */
    FREE( p_sout->psz_sout );
171
    FREE( p_sout->psz_chain );
172

173
    /* delete meta */
174
175
176
177
178
    if( p_sout->p_meta )
    {
        vlc_meta_Delete( p_sout->p_meta );
    }

179
180
    vlc_mutex_destroy( &p_sout->lock );

181
    /* *** free structure *** */
182
183
184
    vlc_object_destroy( p_sout );
}

185
/*****************************************************************************
186
 * Packetizer/Input
187
 *****************************************************************************/
188
189
sout_packetizer_input_t *sout_InputNew( sout_instance_t *p_sout,
                                        es_format_t *p_fmt )
190
{
191
    sout_packetizer_input_t *p_input;
192

193
    msg_Dbg( p_sout, "adding a new input" );
194

195
196
197
198
    /* *** create a packetizer input *** */
    p_input         = malloc( sizeof( sout_packetizer_input_t ) );
    p_input->p_sout = p_sout;
    p_input->p_fmt  = p_fmt;
199

200
    if( p_fmt->i_codec == VLC_FOURCC( 'n', 'u', 'l', 'l' ) )
201
202
203
    {
        vlc_object_release( p_sout );
        return p_input;
204
205
    }

206
    /* *** add it to the stream chain */
207
    vlc_mutex_lock( &p_sout->lock );
208
    p_input->id = p_sout->p_stream->pf_add( p_sout->p_stream, p_fmt );
209
    vlc_mutex_unlock( &p_sout->lock );
210

211
    if( p_input->id == NULL )
212
    {
213
        free( p_input );
214
        return NULL;
215
    }
216

217
    return( p_input );
218
}
219

220
221
222
/*****************************************************************************
 *
 *****************************************************************************/
223
int sout_InputDelete( sout_packetizer_input_t *p_input )
224
{
225
226
227
    sout_instance_t     *p_sout = p_input->p_sout;

    msg_Dbg( p_sout, "removing an input" );
228

229
    if( p_input->p_fmt->i_codec != VLC_FOURCC( 'n', 'u', 'l', 'l' ) )
230
    {
231
232
233
        vlc_mutex_lock( &p_sout->lock );
        p_sout->p_stream->pf_del( p_sout->p_stream, p_input->id );
        vlc_mutex_unlock( &p_sout->lock );
234
    }
235

236
    free( p_input );
237

238
239
    return( VLC_SUCCESS);
}
240

241
242
243
/*****************************************************************************
 *
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
244
int sout_InputSendBuffer( sout_packetizer_input_t *p_input,
245
                          block_t *p_buffer )
246
247
{
    sout_instance_t     *p_sout = p_input->p_sout;
248
    int                 i_ret;
249

250
    if( p_input->p_fmt->i_codec == VLC_FOURCC( 'n', 'u', 'l', 'l' ) )
251
    {
252
        block_Release( p_buffer );
253
254
        return VLC_SUCCESS;
    }
255
    if( p_buffer->i_dts <= 0 )
gbazin's avatar
   
gbazin committed
256
257
    {
        msg_Warn( p_sout, "trying to send non-dated packet to stream output!");
258
        block_Release( p_buffer );
gbazin's avatar
   
gbazin committed
259
260
261
        return VLC_SUCCESS;
    }

262
    vlc_mutex_lock( &p_sout->lock );
gbazin's avatar
   
gbazin committed
263
264
    i_ret = p_sout->p_stream->pf_send( p_sout->p_stream,
                                       p_input->id, p_buffer );
265
    vlc_mutex_unlock( &p_sout->lock );
266

267
    return i_ret;
268
}
269

270
271
272
273
274
275
276
/*****************************************************************************
 * sout_AccessOutNew: allocate a new access out
 *****************************************************************************/
sout_access_out_t *sout_AccessOutNew( sout_instance_t *p_sout,
                                      char *psz_access, char *psz_name )
{
    sout_access_out_t *p_access;
277
    char              *psz_next;
278
279
280
281
282
283
284

    if( !( p_access = vlc_object_create( p_sout,
                                         sizeof( sout_access_out_t ) ) ) )
    {
        msg_Err( p_sout, "out of memory" );
        return NULL;
    }
285

286
    psz_next = sout_CfgCreate( &p_access->psz_access, &p_access->p_cfg,
gbazin's avatar
   
gbazin committed
287
                                psz_access );
288
289
290
291
    if( psz_next )
    {
        free( psz_next );
    }
292
293
294
295
    p_access->psz_name   = strdup( psz_name ? psz_name : "" );
    p_access->p_sout     = p_sout;
    p_access->p_sys = NULL;
    p_access->pf_seek    = NULL;
gbazin's avatar
   
gbazin committed
296
    p_access->pf_read    = NULL;
297
    p_access->pf_write   = NULL;
298
299
    p_access->p_module   = NULL;
    vlc_object_attach( p_access, p_sout );
300

301
    p_access->p_module   =
gbazin's avatar
   
gbazin committed
302
        module_Need( p_access, "sout access", p_access->psz_access, VLC_TRUE );
303
304
305

    if( !p_access->p_module )
    {
306
307
        free( p_access->psz_access );
        free( p_access->psz_name );
308
        vlc_object_detach( p_access );
309
        vlc_object_destroy( p_access );
310
        return( NULL );
311
312
313
314
315
316
317
    }

    return p_access;
}
/*****************************************************************************
 * sout_AccessDelete: delete an access out
 *****************************************************************************/
318
void sout_AccessOutDelete( sout_access_out_t *p_access )
319
{
320
    vlc_object_detach( p_access );
321
322
323
324
325
    if( p_access->p_module )
    {
        module_Unneed( p_access, p_access->p_module );
    }
    free( p_access->psz_access );
326

327
    sout_CfgDestroy( p_access->p_cfg );
328

329
330
331
332
333
334
335
336
    free( p_access->psz_name );

    vlc_object_destroy( p_access );
}

/*****************************************************************************
 * sout_AccessSeek:
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
337
int sout_AccessOutSeek( sout_access_out_t *p_access, off_t i_pos )
338
{
gbazin's avatar
   
gbazin committed
339
    return p_access->pf_seek( p_access, i_pos );
340
341
342
}

/*****************************************************************************
gbazin's avatar
   
gbazin committed
343
 * sout_AccessRead:
344
 *****************************************************************************/
345
int sout_AccessOutRead( sout_access_out_t *p_access, block_t *p_buffer )
346
{
347
348
    return( p_access->pf_read ?
            p_access->pf_read( p_access, p_buffer ) : VLC_EGENERIC );
349
350
}

gbazin's avatar
   
gbazin committed
351
352
353
/*****************************************************************************
 * sout_AccessWrite:
 *****************************************************************************/
354
int sout_AccessOutWrite( sout_access_out_t *p_access, block_t *p_buffer )
gbazin's avatar
   
gbazin committed
355
356
357
{
    return p_access->pf_write( p_access, p_buffer );
}
358

359
/*****************************************************************************
360
 * sout_MuxNew: create a new mux
361
 *****************************************************************************/
362
363
sout_mux_t * sout_MuxNew( sout_instance_t *p_sout, char *psz_mux,
                          sout_access_out_t *p_access )
364
365
{
    sout_mux_t *p_mux;
366
    char       *psz_next;
367

gbazin's avatar
gbazin committed
368
    p_mux = vlc_object_create( p_sout, sizeof( sout_mux_t ) );
369
370
371
372
373
374
    if( p_mux == NULL )
    {
        msg_Err( p_sout, "out of memory" );
        return NULL;
    }

gbazin's avatar
gbazin committed
375
    p_mux->p_sout = p_sout;
376
    psz_next = sout_CfgCreate( &p_mux->psz_mux, &p_mux->p_cfg, psz_mux );
gbazin's avatar
gbazin committed
377
378
    if( psz_next ) free( psz_next );

379
    p_mux->p_access     = p_access;
380
    p_mux->pf_control   = NULL;
381
382
383
384
385
386
387
    p_mux->pf_addstream = NULL;
    p_mux->pf_delstream = NULL;
    p_mux->pf_mux       = NULL;
    p_mux->i_nb_inputs  = 0;
    p_mux->pp_inputs    = NULL;

    p_mux->p_sys        = NULL;
gbazin's avatar
gbazin committed
388
389
390
391
392
    p_mux->p_module     = NULL;

    p_mux->b_add_stream_any_time = VLC_FALSE;
    p_mux->b_waiting_stream = VLC_TRUE;
    p_mux->i_add_stream_start = -1;
393
394

    vlc_object_attach( p_mux, p_sout );
395

gbazin's avatar
gbazin committed
396
    p_mux->p_module =
gbazin's avatar
   
gbazin committed
397
        module_Need( p_mux, "sout mux", p_mux->psz_mux, VLC_TRUE );
398

399
400
401
402
    if( p_mux->p_module == NULL )
    {
        FREE( p_mux->psz_mux );

403
        vlc_object_detach( p_mux );
404
405
406
407
408
        vlc_object_destroy( p_mux );
        return NULL;
    }

    /* *** probe mux capacity *** */
409
    if( p_mux->pf_control )
410
    {
gbazin's avatar
gbazin committed
411
412
413
414
        int b_answer = VLC_FALSE;

        if( sout_MuxControl( p_mux, MUX_CAN_ADD_STREAM_WHILE_MUXING,
                             &b_answer ) )
415
416
417
        {
            b_answer = VLC_FALSE;
        }
gbazin's avatar
gbazin committed
418

419
420
421
422
423
        if( b_answer )
        {
            msg_Dbg( p_sout, "muxer support adding stream at any time" );
            p_mux->b_add_stream_any_time = VLC_TRUE;
            p_mux->b_waiting_stream = VLC_FALSE;
gbazin's avatar
   
gbazin committed
424

gbazin's avatar
gbazin committed
425
426
427
428
429
430
431
432
            /* If we control the output pace then it's better to wait before
             * starting muxing (generates better streams/files). */
            if( !p_sout->i_out_pace_nocontrol )
            {
                b_answer = VLC_TRUE;
            }
            else if( sout_MuxControl( p_mux, MUX_GET_ADD_STREAM_WAIT,
                                      &b_answer ) )
gbazin's avatar
   
gbazin committed
433
434
435
            {
                b_answer = VLC_FALSE;
            }
gbazin's avatar
gbazin committed
436

gbazin's avatar
   
gbazin committed
437
438
439
440
441
442
            if( b_answer )
            {
                msg_Dbg( p_sout, "muxer prefers waiting for all ES before "
                         "starting muxing" );
                p_mux->b_waiting_stream = VLC_TRUE;
            }
443
444
445
446
447
448
        }
    }

    return p_mux;
}

449
450
451
/*****************************************************************************
 * sout_MuxDelete:
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
452
void sout_MuxDelete( sout_mux_t *p_mux )
453
{
454
    vlc_object_detach( p_mux );
455
456
457
458
459
460
    if( p_mux->p_module )
    {
        module_Unneed( p_mux, p_mux->p_module );
    }
    free( p_mux->psz_mux );

461
    sout_CfgDestroy( p_mux->p_cfg );
462

463
464
465
    vlc_object_destroy( p_mux );
}

466
467
468
/*****************************************************************************
 * sout_MuxAddStream:
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
469
sout_input_t *sout_MuxAddStream( sout_mux_t *p_mux, es_format_t *p_fmt )
470
471
472
{
    sout_input_t *p_input;

gbazin's avatar
   
gbazin committed
473
    if( !p_mux->b_add_stream_any_time && !p_mux->b_waiting_stream )
474
    {
gbazin's avatar
   
gbazin committed
475
476
        msg_Err( p_mux, "cannot add a new stream (unsuported while muxing "
                        "for this format)" );
477
478
479
480
        return NULL;
    }

    msg_Dbg( p_mux, "adding a new input" );
481

482
    /* create a new sout input */
483
484
485
    p_input = malloc( sizeof( sout_input_t ) );
    p_input->p_sout = p_mux->p_sout;
    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
490
491
492

    TAB_APPEND( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input );
    if( p_mux->pf_addstream( p_mux, p_input ) < 0 )
    {
            msg_Err( p_mux, "cannot add this stream" );
gbazin's avatar
   
gbazin committed
493
            TAB_REMOVE( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input );
494
            block_FifoRelease( p_input->p_fifo );
gbazin's avatar
   
gbazin committed
495
            free( p_input );
496
            return NULL;
497
498
    }

499
    return p_input;
500
501
}

502
503
504
505
/*****************************************************************************
 * sout_MuxDeleteStream:
 *****************************************************************************/
void sout_MuxDeleteStream( sout_mux_t *p_mux, sout_input_t *p_input )
506
507
508
{
    int i_index;

509
510
511
512
513
514
515
516
    if( p_mux->b_waiting_stream && p_input->p_fifo->i_depth > 0 )
    {
        /* We stop waiting, and call the muxer for taking care of the data
         * before we remove this es */
        p_mux->b_waiting_stream = VLC_FALSE;
        p_mux->pf_mux( p_mux );
    }

517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
    TAB_FIND( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input, i_index );
    if( i_index >= 0 )
    {
        if( p_mux->pf_delstream( p_mux, p_input ) < 0 )
        {
            msg_Err( p_mux, "cannot del this stream from mux" );
        }

        /* remove the entry */
        TAB_REMOVE( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input );

        if( p_mux->i_nb_inputs == 0 )
        {
            msg_Warn( p_mux, "no more input stream for this mux" );
        }

533
        block_FifoRelease( p_input->p_fifo );
534
        free( p_input );
535
536
537
    }
}

538
539
540
541
542
/*****************************************************************************
 * sout_MuxSendBuffer:
 *****************************************************************************/
void sout_MuxSendBuffer( sout_mux_t *p_mux, sout_input_t *p_input,
                         block_t *p_buffer )
543
{
544
    block_FifoPut( p_input->p_fifo, p_buffer );
545
546
547

    if( p_mux->b_waiting_stream )
    {
gbazin's avatar
gbazin committed
548
549
550
551
552
553
        if( p_mux->i_add_stream_start < 0 )
        {
            p_mux->i_add_stream_start = p_buffer->i_dts;
        }

        if( p_mux->i_add_stream_start >= 0 &&
554
            p_mux->i_add_stream_start + I64C(1500000) < p_buffer->i_dts )
555
        {
gbazin's avatar
gbazin committed
556
557
            /* Wait until we have more than 1.5 seconds worth of data
             * before start muxing */
558
559
560
561
562
563
564
565
566
567
            p_mux->b_waiting_stream = VLC_FALSE;
        }
        else
        {
            return;
        }
    }
    p_mux->pf_mux( p_mux );
}

568
569
570
/*****************************************************************************
 *
 *****************************************************************************/
571
static int mrl_Parse( mrl_t *p_mrl, char *psz_mrl )
572
{
Laurent Aimar's avatar
Laurent Aimar committed
573
    char * psz_dup = strdup( psz_mrl );
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
    char * psz_parser = psz_dup;
    char * psz_access = "";
    char * psz_way = "";
    char * psz_name = "";

    /* *** first parse psz_dest */
    while( *psz_parser && *psz_parser != ':' )
    {
        if( *psz_parser == '{' )
        {
            while( *psz_parser && *psz_parser != '}' )
            {
                psz_parser++;
            }
            if( *psz_parser )
            {
                psz_parser++;
            }
        }
        else
        {
            psz_parser++;
        }
    }
#if defined( WIN32 ) || defined( UNDER_CE )
    if( psz_parser - psz_dup == 1 )
    {
601
602
        /* msg_Warn( p_sout, "drive letter %c: found in source string",
                          *psz_dup ) ; */
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
        psz_parser = "";
    }
#endif

    if( !*psz_parser )
    {
        psz_access = psz_way = "";
        psz_name = psz_dup;
    }
    else
    {
        *psz_parser++ = '\0';

        /* let's skip '//' */
        if( psz_parser[0] == '/' && psz_parser[1] == '/' )
        {
            psz_parser += 2 ;
        }

        psz_name = psz_parser ;

        /* Come back to parse the access and mux plug-ins */
        psz_parser = psz_dup;

        if( !*psz_parser )
        {
            /* No access */
            psz_access = "";
        }
        else if( *psz_parser == '/' )
        {
            /* No access */
            psz_access = "";
            psz_parser++;
        }
        else
        {
            psz_access = psz_parser;

            while( *psz_parser && *psz_parser != '/' )
            {
                if( *psz_parser == '{' )
                {
                    while( *psz_parser && *psz_parser != '}' )
                    {
                        psz_parser++;
                    }
                    if( *psz_parser )
                    {
                        psz_parser++;
                    }
                }
                else
                {
                    psz_parser++;
                }
            }

            if( *psz_parser == '/' )
            {
                *psz_parser++ = '\0';
            }
        }

        if( !*psz_parser )
        {
            /* No mux */
            psz_way = "";
        }
        else
        {
            psz_way = psz_parser;
        }
    }

    p_mrl->psz_access = strdup( psz_access );
    p_mrl->psz_way    = strdup( psz_way );
    p_mrl->psz_name   = strdup( psz_name );

Laurent Aimar's avatar
Laurent Aimar committed
682
    free( psz_dup );
683
684
685
686
687
688
689
690
691
692
693
694
695
    return( VLC_SUCCESS );
}


/* mrl_Clean: clean p_mrl  after a call to mrl_Parse */
static void mrl_Clean( mrl_t *p_mrl )
{
    FREE( p_mrl->psz_access );
    FREE( p_mrl->psz_way );
    FREE( p_mrl->psz_name );
}


696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
/****************************************************************************
 ****************************************************************************
 **
 **
 **
 ****************************************************************************
 ****************************************************************************/

/* create a complete chain */
/* chain format:
    module{option=*:option=*}[:module{option=*:...}]
 */

/*
 * parse module{options=str, option="str "}:
 *  return a pointer on the rest
 *  XXX: psz_chain is modified
 */
#define SKIPSPACE( p ) { while( *p && ( *p == ' ' || *p == '\t' ) ) p++; }
715
716
717
#define SKIPTRAILINGSPACE( p, e ) \
    { while( e > p && ( *(e-1) == ' ' || *(e-1) == '\t' ) ) e--; }

718
719
720
/* go accross " " and { } */
static char *_get_chain_end( char *str )
{
721
    char c, *p = str;
722
723
724
725
726

    SKIPSPACE( p );

    for( ;; )
    {
727
        if( !*p || *p == ',' || *p == '}' ) return p;
728

729
730
        if( *p != '{' && *p != '"' && *p != '\'' )
        {
731
            p++;
732
733
            continue;
        }
734

735
736
737
        if( *p == '{' ) c = '}';
        else c = *p;
        p++;
738

739
        for( ;; )
740
        {
741
742
743
744
745
            if( !*p ) return p;

            if( *p == c ) return ++p;
            else if( *p == '{' && c == '}' ) p = _get_chain_end( p );
            else p++;
746
747
748
749
        }
    }
}

750
char *sout_CfgCreate( char **ppsz_name, sout_cfg_t **pp_cfg, char *psz_chain )
751
752
753
754
755
756
757
{
    sout_cfg_t *p_cfg = NULL;
    char       *p = psz_chain;

    *ppsz_name = NULL;
    *pp_cfg    = NULL;

758
    if( !p ) return NULL;
759

760
761
    SKIPSPACE( p );

762
    while( *p && *p != '{' && *p != ':' && *p != ' ' && *p != '\t' ) p++;
763

764
    if( p == psz_chain ) return NULL;
765

766
    *ppsz_name = strndup( psz_chain, p - psz_chain );
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783

    SKIPSPACE( p );

    if( *p == '{' )
    {
        char *psz_name;

        p++;

        for( ;; )
        {
            sout_cfg_t cfg;

            SKIPSPACE( p );

            psz_name = p;

784
785
            while( *p && *p != '=' && *p != ',' && *p != '{' && *p != '}' &&
                   *p != ' ' && *p != '\t' ) p++;
786

787
            /* fprintf( stderr, "name=%s - rest=%s\n", psz_name, p ); */
788
789
790
791
792
793
            if( p == psz_name )
            {
                fprintf( stderr, "invalid options (empty)" );
                break;
            }

794
            cfg.psz_name = strndup( psz_name, p - psz_name );
795
796
797

            SKIPSPACE( p );

798
            if( *p == '=' || *p == '{' )
799
800
            {
                char *end;
801
                vlc_bool_t b_keep_brackets = (*p == '{');
802

803
                if( *p == '=' ) p++;
804
805
806
807
808
809
810
811

                end = _get_chain_end( p );
                if( end <= p )
                {
                    cfg.psz_value = NULL;
                }
                else
                {
812
813
814
815
816
817
818
819
820
821
822
823
824
                    /* Skip heading and trailing spaces.
                     * This ain't necessary but will avoid simple
                     * user mistakes. */
                    SKIPSPACE( p );
                }

                if( end <= p )
                {
                    cfg.psz_value = NULL;
                }
                else
                {
                    if( *p == '\'' || *p == '"' ||
825
                        ( !b_keep_brackets && *p == '{' ) )
826
827
                    {
                        p++;
828
829
830
831
832
833
834
835
836
837
838
839

                        if( *(end-1) != '\'' && *(end-1) == '"' )
                            SKIPTRAILINGSPACE( p, end );

                        if( end - 1 <= p ) cfg.psz_value = NULL;
                        else cfg.psz_value = strndup( p, end -1 - p );
                    }
                    else
                    {
                        SKIPTRAILINGSPACE( p, end );
                        if( end <= p ) cfg.psz_value = NULL;
                        else cfg.psz_value = strndup( p, end - p );
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
                    }
                }

                p = end;
                SKIPSPACE( p );
            }
            else
            {
                cfg.psz_value = NULL;
            }

            cfg.p_next = NULL;
            if( p_cfg )
            {
                p_cfg->p_next = malloc( sizeof( sout_cfg_t ) );
                memcpy( p_cfg->p_next, &cfg, sizeof( sout_cfg_t ) );

                p_cfg = p_cfg->p_next;
            }
            else
            {
                p_cfg = malloc( sizeof( sout_cfg_t ) );
                memcpy( p_cfg, &cfg, sizeof( sout_cfg_t ) );

                *pp_cfg = p_cfg;
            }

867
            if( *p == ',' ) p++;
868
869
870
871
872
873
874
875
876

            if( *p == '}' )
            {
                p++;
                break;
            }
        }
    }

877
    if( *p == ':' ) return( strdup( p + 1 ) );
878

879
    return NULL;
880
881
}

882
static void sout_CfgDestroy( sout_cfg_t *p_cfg )
883
884
885
886
{
    while( p_cfg != NULL )
    {
        sout_cfg_t *p_next;
887

888
        p_next = p_cfg->p_next;
889

890
891
892
893
894
895
896
        FREE( p_cfg->psz_name );
        FREE( p_cfg->psz_value );
        free( p_cfg );

        p_cfg = p_next;
    }
}
897

898
void __sout_CfgParse( vlc_object_t *p_this, char *psz_prefix,
899
                      const char **ppsz_options, sout_cfg_t *cfg )
900
901
902
903
904
905
906
907
{
    char *psz_name;
    int  i_type;
    int  i;

    /* First, var_Create all variables */
    for( i = 0; ppsz_options[i] != NULL; i++ )
    {
908
909
        asprintf( &psz_name, "%s%s", psz_prefix,
                  *ppsz_options[i] == '*' ? &ppsz_options[i][1] : ppsz_options[i] );
910
911
912
913
914
915
916
917
918
919
920
921
922

        i_type = config_GetType( p_this, psz_name );

        var_Create( p_this, psz_name, i_type | VLC_VAR_DOINHERIT );
        free( psz_name );
    }

    /* Now parse options and set value */
    if( psz_prefix == NULL ) psz_prefix = "";

    while( cfg )
    {
        vlc_value_t val;
923
        vlc_bool_t b_yes = VLC_TRUE;
924
        vlc_bool_t b_once = VLC_FALSE;
925
926
927
928
929
930
931
932
933
934
935
936

        if( cfg->psz_name == NULL || *cfg->psz_name == '\0' )
        {
            cfg = cfg->p_next;
            continue;
        }
        for( i = 0; ppsz_options[i] != NULL; i++ )
        {
            if( !strcmp( ppsz_options[i], cfg->psz_name ) )
            {
                break;
            }
937
938
939
940
            if( ( !strncmp( cfg->psz_name, "no-", 3 ) &&
                  !strcmp( ppsz_options[i], cfg->psz_name + 3 ) ) ||
                ( !strncmp( cfg->psz_name, "no", 2 ) &&
                  !strcmp( ppsz_options[i], cfg->psz_name + 2 ) ) )
941
942
943
944
            {
                b_yes = VLC_FALSE;
                break;
            }
945
946
947
948
949
950
951
952

            if( *ppsz_options[i] == '*' &&
                !strcmp( &ppsz_options[i][1], cfg->psz_name ) )
            {
                b_once = VLC_TRUE;
                break;
            }

953
954
955
        }
        if( ppsz_options[i] == NULL )
        {
Laurent Aimar's avatar
Laurent Aimar committed
956
            msg_Warn( p_this, "option %s is unknown", cfg->psz_name );
957
958
959
960
961
            cfg = cfg->p_next;
            continue;
        }

        /* create name */
962
        asprintf( &psz_name, "%s%s", psz_prefix, b_once ? &ppsz_options[i][1] : ppsz_options[i] );
963
964
965
966
967

        /* get the type of the variable */
        i_type = config_GetType( p_this, psz_name );
        if( !i_type )
        {
968
969
            msg_Warn( p_this, "unknown option %s (value=%s)",
                      cfg->psz_name, cfg->psz_value );
970
971
972
973
974
975
976
            goto next;
        }
        if( i_type != VLC_VAR_BOOL && cfg->psz_value == NULL )
        {
            msg_Warn( p_this, "missing value for option %s", cfg->psz_name );
            goto next;
        }
977
978
979
980
981
        if( i_type != VLC_VAR_STRING && b_once )
        {
            msg_Warn( p_this, "*option_name need to be a string option" );
            goto next;
        }
982
983
984

        switch( i_type )
        {
985
986
987
            case VLC_VAR_BOOL:
                val.b_bool = b_yes;
                break;
988
            case VLC_VAR_INTEGER:
989
990
                val.i_int = strtol( cfg->psz_value ? cfg->psz_value : "0",
                                    NULL, 0 );
991
992
993
994
995
996
997
998
999
1000
                break;
            case VLC_VAR_FLOAT:
                val.f_float = atof( cfg->psz_value ? cfg->psz_value : "0" );
                break;
            case VLC_VAR_STRING:
                val.psz_string = cfg->psz_value;
                break;
            default:
                msg_Warn( p_this, "unhandled config var type" );
                memset( &val, 0, sizeof( vlc_value_t ) );
For faster browsing, not all history is shown. View entire blame