stream_output.c 31.1 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
548
549
550
551
552
553
    if( p_mux->p_sout->i_out_pace_nocontrol )
    {
        mtime_t current_date = mdate();
        if ( current_date > p_buffer->i_dts )
            msg_Warn( p_mux, "late buffer for mux input ("I64Fd")",
                      current_date - p_buffer->i_dts );
    }

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

576
577
578
/*****************************************************************************
 *
 *****************************************************************************/
579
static int mrl_Parse( mrl_t *p_mrl, char *psz_mrl )
580
{
Laurent Aimar's avatar
Laurent Aimar committed
581
    char * psz_dup = strdup( psz_mrl );
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
    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 )
    {
609
610
        /* msg_Warn( p_sout, "drive letter %c: found in source string",
                          *psz_dup ) ; */
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
682
683
684
685
686
687
688
689
        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
690
    free( psz_dup );
691
692
693
694
695
696
697
698
699
700
701
702
703
    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 );
}


704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
/****************************************************************************
 ****************************************************************************
 **
 **
 **
 ****************************************************************************
 ****************************************************************************/

/* 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++; }
723
724
725
#define SKIPTRAILINGSPACE( p, e ) \
    { while( e > p && ( *(e-1) == ' ' || *(e-1) == '\t' ) ) e--; }

726
727
728
/* go accross " " and { } */
static char *_get_chain_end( char *str )
{
729
    char c, *p = str;
730
731
732
733
734

    SKIPSPACE( p );

    for( ;; )
    {
735
        if( !*p || *p == ',' || *p == '}' ) return p;
736

737
738
        if( *p != '{' && *p != '"' && *p != '\'' )
        {
739
            p++;
740
741
            continue;
        }
742

743
744
745
        if( *p == '{' ) c = '}';
        else c = *p;
        p++;
746

747
        for( ;; )
748
        {
749
750
751
752
753
            if( !*p ) return p;

            if( *p == c ) return ++p;
            else if( *p == '{' && c == '}' ) p = _get_chain_end( p );
            else p++;
754
755
756
757
        }
    }
}

758
char *sout_CfgCreate( char **ppsz_name, sout_cfg_t **pp_cfg, char *psz_chain )
759
760
761
762
763
764
765
{
    sout_cfg_t *p_cfg = NULL;
    char       *p = psz_chain;

    *ppsz_name = NULL;
    *pp_cfg    = NULL;

766
    if( !p ) return NULL;
767

768
769
    SKIPSPACE( p );

770
    while( *p && *p != '{' && *p != ':' && *p != ' ' && *p != '\t' ) p++;
771

772
    if( p == psz_chain ) return NULL;
773

774
    *ppsz_name = strndup( psz_chain, p - psz_chain );
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791

    SKIPSPACE( p );

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

        p++;

        for( ;; )
        {
            sout_cfg_t cfg;

            SKIPSPACE( p );

            psz_name = p;

792
793
            while( *p && *p != '=' && *p != ',' && *p != '{' && *p != '}' &&
                   *p != ' ' && *p != '\t' ) p++;
794

795
            /* fprintf( stderr, "name=%s - rest=%s\n", psz_name, p ); */
796
797
798
799
800
801
            if( p == psz_name )
            {
                fprintf( stderr, "invalid options (empty)" );
                break;
            }

802
            cfg.psz_name = strndup( psz_name, p - psz_name );
803
804
805

            SKIPSPACE( p );

806
            if( *p == '=' || *p == '{' )
807
808
            {
                char *end;
809
                vlc_bool_t b_keep_brackets = (*p == '{');
810

811
                if( *p == '=' ) p++;
812
813
814
815
816
817
818
819

                end = _get_chain_end( p );
                if( end <= p )
                {
                    cfg.psz_value = NULL;
                }
                else
                {
820
821
822
823
824
825
826
827
828
829
830
831
832
                    /* 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 == '"' ||
833
                        ( !b_keep_brackets && *p == '{' ) )
834
835
                    {
                        p++;
836
837
838
839
840
841
842
843
844
845
846
847

                        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 );
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
                    }
                }

                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;
            }

875
            if( *p == ',' ) p++;
876
877
878
879
880
881
882
883
884

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

885
    if( *p == ':' ) return( strdup( p + 1 ) );
886

887
    return NULL;
888
889
}

890
static void sout_CfgDestroy( sout_cfg_t *p_cfg )
891
892
893
894
{
    while( p_cfg != NULL )
    {
        sout_cfg_t *p_next;
895

896
        p_next = p_cfg->p_next;
897

898
899
900
901
902
903
904
        FREE( p_cfg->psz_name );
        FREE( p_cfg->psz_value );
        free( p_cfg );

        p_cfg = p_next;
    }
}
905

906
void __sout_CfgParse( vlc_object_t *p_this, char *psz_prefix,
907
                      const char **ppsz_options, sout_cfg_t *cfg )
908
909
910
911
912
913
914
915
{
    char *psz_name;
    int  i_type;
    int  i;

    /* First, var_Create all variables */
    for( i = 0; ppsz_options[i] != NULL; i++ )
    {
916
917
        asprintf( &psz_name, "%s%s", psz_prefix,
                  *ppsz_options[i] == '*' ? &ppsz_options[i][1] : ppsz_options[i] );
918
919
920
921
922
923
924
925
926
927
928
929
930

        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;
931
        vlc_bool_t b_yes = VLC_TRUE;
932
        vlc_bool_t b_once = VLC_FALSE;
933
934
935
936
937
938
939
940
941
942
943
944

        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;
            }
945
946
947
948
            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 ) ) )
949
950
951
952
            {
                b_yes = VLC_FALSE;
                break;
            }
953
954
955
956
957
958
959
960

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

961
962
963
        }
        if( ppsz_options[i] == NULL )
        {
Laurent Aimar's avatar
Laurent Aimar committed
964
            msg_Warn( p_this, "option %s is unknown", cfg->psz_name );
965
966
967
968
969
            cfg = cfg->p_next;
            continue;
        }

        /* create name */
970
        asprintf( &psz_name, "%s%s", psz_prefix, b_once ? &ppsz_options[i][1] : ppsz_options[i] );
971
972
973
974
975

        /* get the type of the variable */
        i_type = config_GetType( p_this, psz_name );
        if( !i_type )
        {
976
977
            msg_Warn( p_this, "unknown option %s (value=%s)",
                      cfg->psz_name, cfg->psz_value );
978
979
980
981
982
983
984
            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;
        }
985
986
987
988
989
        if( i_type != VLC_VAR_STRING && b_once )
        {
            msg_Warn( p_this, "*option_name need to be a string option" );
            goto next;
        }
990
991
992

        switch( i_type )
        {
993
994
995
            case VLC_VAR_BOOL:
                val.b_bool = b_yes;
                break;
996
            case VLC_VAR_INTEGER:
997
998
                val.i_int = strtol( cfg->psz_value ? cfg->psz_value : "0",
                                    NULL, 0 );
999
1000
                break;
            case VLC_VAR_FLOAT:
For faster browsing, not all history is shown. View entire blame