stream_output.c 31.1 KB
Newer Older
1
2
3
/*****************************************************************************
 * stream_output.c : stream output module
 *****************************************************************************
4
 * Copyright (C) 2002-2004 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
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
    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;
    }
80
    if( keep.b_bool )
81
    {
82
        if( ( p_sout = vlc_object_find( p_parent, VLC_OBJECT_SOUT,
83
                                        FIND_ANYWHERE ) ) != NULL )
84
85
86
        {
            if( !strcmp( p_sout->psz_sout, psz_dest ) )
            {
87
88
                msg_Dbg( p_parent, "sout keep : reusing sout" );
                msg_Dbg( p_parent, "sout keep : you probably want to use "
89
                          "gather stream_out" );
90
91
92
93
94
95
96
                vlc_object_detach( p_sout );
                vlc_object_attach( p_sout, p_parent );
                vlc_object_release( p_sout );
                return p_sout;
            }
            else
            {
97
98
                msg_Dbg( p_parent, "sout keep : destroying unusable sout" );
                vlc_object_release( p_sout );
99
100
101
102
103
104
                sout_DeleteInstance( p_sout );
            }
        }
    }
    else if( !keep.b_bool )
    {
105
        while( ( p_sout = vlc_object_find( p_parent, VLC_OBJECT_SOUT,
106
                                           FIND_PARENT ) ) != NULL )
107
        {
108
109
            msg_Dbg( p_parent, "sout keep : destroying old sout" );
            vlc_object_release( p_sout );
110
111
112
            sout_DeleteInstance( p_sout );
        }
    }
113

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

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

    vlc_mutex_init( p_sout, &p_sout->lock );
129
    if( psz_dest && psz_dest[0] == '#' )
130
    {
131
        p_sout->psz_chain = strdup( &psz_dest[1] );
132
    }
133
    else
134
    {
135
136
        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 );
137
    }
138
139
140
141
    p_sout->p_stream = NULL;

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

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

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

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

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

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

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

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

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

180
181
    vlc_mutex_destroy( &p_sout->lock );

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

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

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

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

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

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

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

218
    return( p_input );
219
}
220

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

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

230
    if( p_input->p_fmt->i_codec != VLC_FOURCC( 'n', 'u', 'l', 'l' ) )
231
    {
232
233
234
        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 );
235
    }
236

237
    free( p_input );
238

239
240
    return( VLC_SUCCESS);
}
241

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

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

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

268
    return i_ret;
269
}
270

271
272
273
274
275
276
277
/*****************************************************************************
 * 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;
278
    char              *psz_next;
279
280
281
282
283
284
285

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

287
    psz_next = sout_CfgCreate( &p_access->psz_access, &p_access->p_cfg,
gbazin's avatar
   
gbazin committed
288
                                psz_access );
289
290
291
292
    if( psz_next )
    {
        free( psz_next );
    }
293
294
295
296
    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
297
    p_access->pf_read    = NULL;
298
    p_access->pf_write   = NULL;
299
300
    p_access->p_module   = NULL;
    vlc_object_attach( p_access, p_sout );
301

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

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

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

328
    sout_CfgDestroy( p_access->p_cfg );
329

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

    vlc_object_destroy( p_access );
}

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

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

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

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

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

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

380
    p_mux->p_access     = p_access;
381
    p_mux->pf_control   = NULL;
382
383
384
385
386
387
388
    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
389
390
391
392
393
    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;
394
395

    vlc_object_attach( p_mux, p_sout );
396

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

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

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

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

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

420
421
422
423
424
        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
425

gbazin's avatar
gbazin committed
426
427
428
429
430
431
432
433
            /* 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
434
435
436
            {
                b_answer = VLC_FALSE;
            }
gbazin's avatar
gbazin committed
437

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

    return p_mux;
}

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

462
    sout_CfgDestroy( p_mux->p_cfg );
463

464
465
466
    vlc_object_destroy( p_mux );
}

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

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

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

483
    /* create a new sout input */
484
485
486
    p_input = malloc( sizeof( sout_input_t ) );
    p_input->p_sout = p_mux->p_sout;
    p_input->p_fmt  = p_fmt;
487
    p_input->p_fifo = block_FifoNew( p_mux->p_sout );
488
    p_input->p_sys  = NULL;
489
490
491
492
493

    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
494
            TAB_REMOVE( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input );
495
            block_FifoRelease( p_input->p_fifo );
gbazin's avatar
   
gbazin committed
496
            free( p_input );
497
            return NULL;
498
499
    }

500
    return p_input;
501
502
}

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

510
511
512
513
514
515
516
517
    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 );
    }

518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
    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" );
        }

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

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

547
548
549
550
551
552
553
554
    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 );
    }

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

577
578
579
/*****************************************************************************
 *
 *****************************************************************************/
580
static int mrl_Parse( mrl_t *p_mrl, char *psz_mrl )
581
{
Laurent Aimar's avatar
Laurent Aimar committed
582
    char * psz_dup = strdup( psz_mrl );
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
609
    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 )
    {
610
611
        /* msg_Warn( p_sout, "drive letter %c: found in source string",
                          *psz_dup ) ; */
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
690
        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
691
    free( psz_dup );
692
693
694
695
696
697
698
699
700
701
702
703
704
    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 );
}


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

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

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

    SKIPSPACE( p );

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

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

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

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

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

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

    *ppsz_name = NULL;
    *pp_cfg    = NULL;

767
    if( !p ) return NULL;
768

769
770
    SKIPSPACE( p );

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

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

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

    SKIPSPACE( p );

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

        p++;

        for( ;; )
        {
            sout_cfg_t cfg;

            SKIPSPACE( p );

            psz_name = p;

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

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

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

            SKIPSPACE( p );

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

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

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

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

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

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

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

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

888
    return NULL;
889
890
}

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

897
        p_next = p_cfg->p_next;
898

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

        p_cfg = p_next;
    }
}
906

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

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

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

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

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

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

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

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

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