stream_output.c 33.4 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
 *
 * 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
dionoea's avatar
dionoea committed
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24
25
26
27
28
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
29
30
31

#include <vlc/vlc.h>

32
33
34
35
36
#include <stdlib.h>                                                /* free() */
#include <stdio.h>                                              /* sprintf() */
#include <string.h>                                            /* strerror() */

#include <vlc/sout.h>
zorglub's avatar
zorglub committed
37
#include <vlc/input.h>
38
39
40

#include "vlc_meta.h"

41
#undef DEBUG_BUFFER
42
43
44
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
45
static void sout_CfgDestroy( sout_cfg_t * );
46

gbazin's avatar
gbazin committed
47
48
#define sout_stream_url_to_chain( p, s ) \
    _sout_stream_url_to_chain( VLC_OBJECT(p), s )
49
50
static char *_sout_stream_url_to_chain( vlc_object_t *, char * );

51
52
53
54
55
56
57
/*
 * Generic MRL parser
 *
 */

typedef struct
{
58
59
    char *psz_access;
    char *psz_way;
60
61
62
63
64
65
66
67
    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 );

68
69
70
/*****************************************************************************
 * sout_NewInstance: creates a new stream output instance
 *****************************************************************************/
71
sout_instance_t *__sout_NewInstance( vlc_object_t *p_parent, char * psz_dest )
72
{
73
    sout_instance_t *p_sout;
74
75
76
77
78
79
80
    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;
    }
81
    if( keep.b_bool )
82
    {
83
        if( ( p_sout = vlc_object_find( p_parent, VLC_OBJECT_SOUT,
84
                                        FIND_ANYWHERE ) ) != NULL )
85
86
87
        {
            if( !strcmp( p_sout->psz_sout, psz_dest ) )
            {
88
89
                msg_Dbg( p_parent, "sout keep: reusing sout" );
                msg_Dbg( p_parent, "sout keep: you probably want to use "
90
                          "gather stream_out" );
91
92
93
94
95
96
97
                vlc_object_detach( p_sout );
                vlc_object_attach( p_sout, p_parent );
                vlc_object_release( p_sout );
                return p_sout;
            }
            else
            {
98
                msg_Dbg( p_parent, "sout keep: destroying unusable sout" );
99
                vlc_object_release( p_sout );
100
101
102
103
104
105
                sout_DeleteInstance( p_sout );
            }
        }
    }
    else if( !keep.b_bool )
    {
106
        while( ( p_sout = vlc_object_find( p_parent, VLC_OBJECT_SOUT,
107
                                           FIND_PARENT ) ) != NULL )
108
        {
109
            msg_Dbg( p_parent, "sout keep: destroying old sout" );
110
            vlc_object_release( p_sout );
111
112
113
            sout_DeleteInstance( p_sout );
        }
    }
114

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

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

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

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

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

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

zorglub's avatar
zorglub committed
150
151
        FREENULL( p_sout->psz_sout );
        FREENULL( p_sout->psz_chain );
152

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

    return p_sout;
}
Jean-Paul Saman's avatar
Jean-Paul Saman committed
160

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

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

172
    /* *** free all string *** */
zorglub's avatar
zorglub committed
173
174
    FREENULL( p_sout->psz_sout );
    FREENULL( p_sout->psz_chain );
175

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

182
183
    vlc_mutex_destroy( &p_sout->lock );

184
    /* *** free structure *** */
185
186
187
    vlc_object_destroy( p_sout );
}

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

196
    msg_Dbg( p_sout, "adding a new input" );
197

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

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

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

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

220
    return( p_input );
221
}
222

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

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

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

239
    free( p_input );
240

241
242
    return( VLC_SUCCESS);
}
243

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

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

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

270
    return i_ret;
271
}
272

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

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

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

    p_access->i_writes = 0;
    p_access->i_sent_bytes = 0;

306
    vlc_object_attach( p_access, p_sout );
307

308
    p_access->p_module   =
gbazin's avatar
   
gbazin committed
309
        module_Need( p_access, "sout access", p_access->psz_access, VLC_TRUE );
310
311
312

    if( !p_access->p_module )
    {
313
314
        free( p_access->psz_access );
        free( p_access->psz_name );
315
        vlc_object_detach( p_access );
316
        vlc_object_destroy( p_access );
317
        return( NULL );
318
319
320
321
322
323
324
    }

    return p_access;
}
/*****************************************************************************
 * sout_AccessDelete: delete an access out
 *****************************************************************************/
325
void sout_AccessOutDelete( sout_access_out_t *p_access )
326
{
327
    vlc_object_detach( p_access );
328
329
330
331
332
    if( p_access->p_module )
    {
        module_Unneed( p_access, p_access->p_module );
    }
    free( p_access->psz_access );
333

334
    sout_CfgDestroy( p_access->p_cfg );
335

336
337
338
339
340
341
342
343
    free( p_access->psz_name );

    vlc_object_destroy( p_access );
}

/*****************************************************************************
 * sout_AccessSeek:
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
344
int sout_AccessOutSeek( sout_access_out_t *p_access, off_t i_pos )
345
{
gbazin's avatar
   
gbazin committed
346
    return p_access->pf_seek( p_access, i_pos );
347
348
349
}

/*****************************************************************************
gbazin's avatar
   
gbazin committed
350
 * sout_AccessRead:
351
 *****************************************************************************/
352
int sout_AccessOutRead( sout_access_out_t *p_access, block_t *p_buffer )
353
{
354
355
    return( p_access->pf_read ?
            p_access->pf_read( p_access, p_buffer ) : VLC_EGENERIC );
356
357
}

gbazin's avatar
   
gbazin committed
358
359
360
/*****************************************************************************
 * sout_AccessWrite:
 *****************************************************************************/
361
int sout_AccessOutWrite( sout_access_out_t *p_access, block_t *p_buffer )
gbazin's avatar
   
gbazin committed
362
{
363
    int i_total = 0;
364
365
    p_access->i_writes++;
    p_access->i_sent_bytes += p_buffer->i_buffer;
366
    if( p_access->p_libvlc->b_stats && p_access->i_writes % 30 == 0 )
zorglub's avatar
zorglub committed
367
    {
368
        /* Access_out -> sout_instance -> input_thread_t */
369
370
        input_thread_t *p_input =
            (input_thread_t *)vlc_object_find( p_access, VLC_OBJECT_INPUT,
371
372
373
                                               FIND_PARENT );
        if( p_input )
        {
374
375
376
            stats_UpdateInteger( p_input, p_input->counters.p_sout_sent_packets,
			         30, NULL );
            stats_UpdateInteger( p_input, p_input->counters.p_sout_sent_bytes,
zorglub's avatar
zorglub committed
377
                                 p_access->i_sent_bytes, &i_total );
378
379
            stats_UpdateFloat( p_input, p_input->counters.p_sout_send_bitrate,
			   	 (float)i_total, NULL );
380
            p_access->i_sent_bytes = 0;
381
382
            vlc_object_release( p_input );
        }
zorglub's avatar
zorglub committed
383
    }
gbazin's avatar
   
gbazin committed
384
385
    return p_access->pf_write( p_access, p_buffer );
}
386

387
/*****************************************************************************
388
 * sout_MuxNew: create a new mux
389
 *****************************************************************************/
390
391
sout_mux_t * sout_MuxNew( sout_instance_t *p_sout, char *psz_mux,
                          sout_access_out_t *p_access )
392
393
{
    sout_mux_t *p_mux;
394
    char       *psz_next;
395

gbazin's avatar
gbazin committed
396
    p_mux = vlc_object_create( p_sout, sizeof( sout_mux_t ) );
397
398
399
400
401
402
    if( p_mux == NULL )
    {
        msg_Err( p_sout, "out of memory" );
        return NULL;
    }

gbazin's avatar
gbazin committed
403
    p_mux->p_sout = p_sout;
404
    psz_next = sout_CfgCreate( &p_mux->psz_mux, &p_mux->p_cfg, psz_mux );
gbazin's avatar
gbazin committed
405
406
    if( psz_next ) free( psz_next );

407
    p_mux->p_access     = p_access;
408
    p_mux->pf_control   = NULL;
409
410
411
412
413
414
415
    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
416
417
418
419
420
    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;
421
422

    vlc_object_attach( p_mux, p_sout );
423

gbazin's avatar
gbazin committed
424
    p_mux->p_module =
gbazin's avatar
   
gbazin committed
425
        module_Need( p_mux, "sout mux", p_mux->psz_mux, VLC_TRUE );
426

427
428
    if( p_mux->p_module == NULL )
    {
zorglub's avatar
zorglub committed
429
        FREENULL( p_mux->psz_mux );
430

431
        vlc_object_detach( p_mux );
432
433
434
435
436
        vlc_object_destroy( p_mux );
        return NULL;
    }

    /* *** probe mux capacity *** */
437
    if( p_mux->pf_control )
438
    {
gbazin's avatar
gbazin committed
439
440
441
442
        int b_answer = VLC_FALSE;

        if( sout_MuxControl( p_mux, MUX_CAN_ADD_STREAM_WHILE_MUXING,
                             &b_answer ) )
443
444
445
        {
            b_answer = VLC_FALSE;
        }
gbazin's avatar
gbazin committed
446

447
448
449
450
451
        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
452

gbazin's avatar
gbazin committed
453
454
455
456
457
458
459
460
            /* 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
461
462
463
            {
                b_answer = VLC_FALSE;
            }
gbazin's avatar
gbazin committed
464

gbazin's avatar
   
gbazin committed
465
466
            if( b_answer )
            {
467
468
                msg_Dbg( p_sout, "muxer prefers to wait for all ES before "
                         "starting to mux" );
gbazin's avatar
   
gbazin committed
469
470
                p_mux->b_waiting_stream = VLC_TRUE;
            }
471
472
473
474
475
476
        }
    }

    return p_mux;
}

477
478
479
/*****************************************************************************
 * sout_MuxDelete:
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
480
void sout_MuxDelete( sout_mux_t *p_mux )
481
{
482
    vlc_object_detach( p_mux );
483
484
485
486
487
488
    if( p_mux->p_module )
    {
        module_Unneed( p_mux, p_mux->p_module );
    }
    free( p_mux->psz_mux );

489
    sout_CfgDestroy( p_mux->p_cfg );
490

491
492
493
    vlc_object_destroy( p_mux );
}

494
495
496
/*****************************************************************************
 * sout_MuxAddStream:
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
497
sout_input_t *sout_MuxAddStream( sout_mux_t *p_mux, es_format_t *p_fmt )
498
499
500
{
    sout_input_t *p_input;

gbazin's avatar
   
gbazin committed
501
    if( !p_mux->b_add_stream_any_time && !p_mux->b_waiting_stream )
502
    {
503
        msg_Err( p_mux, "cannot add a new stream (unsupported while muxing "
504
                        "to this format)" );
505
506
507
508
        return NULL;
    }

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

510
    /* create a new sout input */
511
512
513
    p_input = malloc( sizeof( sout_input_t ) );
    p_input->p_sout = p_mux->p_sout;
    p_input->p_fmt  = p_fmt;
514
    p_input->p_fifo = block_FifoNew( p_mux->p_sout );
515
    p_input->p_sys  = NULL;
516
517
518
519
520

    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
521
            TAB_REMOVE( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input );
522
            block_FifoRelease( p_input->p_fifo );
gbazin's avatar
   
gbazin committed
523
            free( p_input );
524
            return NULL;
525
526
    }

527
    return p_input;
528
529
}

530
531
532
533
/*****************************************************************************
 * sout_MuxDeleteStream:
 *****************************************************************************/
void sout_MuxDeleteStream( sout_mux_t *p_mux, sout_input_t *p_input )
534
535
536
{
    int i_index;

537
538
539
540
541
542
543
544
    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 );
    }

545
546
547
548
549
    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 )
        {
550
            msg_Err( p_mux, "cannot delete this stream from mux" );
551
552
553
554
555
556
557
        }

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

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

561
        block_FifoRelease( p_input->p_fifo );
562
        free( p_input );
563
564
565
    }
}

566
567
568
569
570
/*****************************************************************************
 * sout_MuxSendBuffer:
 *****************************************************************************/
void sout_MuxSendBuffer( sout_mux_t *p_mux, sout_input_t *p_input,
                         block_t *p_buffer )
571
{
572
    block_FifoPut( p_input->p_fifo, p_buffer );
573

574
575
576
577
578
579
580
581
    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 );
    }

582
583
    if( p_mux->b_waiting_stream )
    {
gbazin's avatar
gbazin committed
584
585
586
587
588
589
        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 &&
590
            p_mux->i_add_stream_start + I64C(1500000) < p_buffer->i_dts )
591
        {
gbazin's avatar
gbazin committed
592
593
            /* Wait until we have more than 1.5 seconds worth of data
             * before start muxing */
594
595
596
597
598
599
600
601
602
603
            p_mux->b_waiting_stream = VLC_FALSE;
        }
        else
        {
            return;
        }
    }
    p_mux->pf_mux( p_mux );
}

604
605
606
/*****************************************************************************
 *
 *****************************************************************************/
607
static int mrl_Parse( mrl_t *p_mrl, char *psz_mrl )
608
{
Laurent Aimar's avatar
Laurent Aimar committed
609
    char * psz_dup = strdup( psz_mrl );
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
    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 )
    {
637
638
        /* msg_Warn( p_sout, "drive letter %c: found in source string",
                          *psz_dup ) ; */
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
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
        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
718
    free( psz_dup );
719
720
721
722
723
724
725
    return( VLC_SUCCESS );
}


/* mrl_Clean: clean p_mrl  after a call to mrl_Parse */
static void mrl_Clean( mrl_t *p_mrl )
{
zorglub's avatar
zorglub committed
726
727
728
    FREENULL( p_mrl->psz_access );
    FREENULL( p_mrl->psz_way );
    FREENULL( p_mrl->psz_name );
729
730
731
}


732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
/****************************************************************************
 ****************************************************************************
 **
 **
 **
 ****************************************************************************
 ****************************************************************************/

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

754
755
756
/* go accross " " and { } */
static char *_get_chain_end( char *str )
{
757
    char c, *p = str;
758
759
760
761
762

    SKIPSPACE( p );

    for( ;; )
    {
763
        if( !*p || *p == ',' || *p == '}' ) return p;
764

765
766
        if( *p != '{' && *p != '"' && *p != '\'' )
        {
767
            p++;
768
769
            continue;
        }
770

771
772
773
        if( *p == '{' ) c = '}';
        else c = *p;
        p++;
774

775
        for( ;; )
776
        {
777
778
779
780
781
            if( !*p ) return p;

            if( *p == c ) return ++p;
            else if( *p == '{' && c == '}' ) p = _get_chain_end( p );
            else p++;
782
783
784
785
        }
    }
}

786
char *sout_CfgCreate( char **ppsz_name, sout_cfg_t **pp_cfg, char *psz_chain )
787
788
789
790
791
792
793
{
    sout_cfg_t *p_cfg = NULL;
    char       *p = psz_chain;

    *ppsz_name = NULL;
    *pp_cfg    = NULL;

794
    if( !p ) return NULL;
795

796
797
    SKIPSPACE( p );

798
    while( *p && *p != '{' && *p != ':' && *p != ' ' && *p != '\t' ) p++;
799

800
    if( p == psz_chain ) return NULL;
801

802
    *ppsz_name = strndup( psz_chain, p - psz_chain );
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819

    SKIPSPACE( p );

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

        p++;

        for( ;; )
        {
            sout_cfg_t cfg;

            SKIPSPACE( p );

            psz_name = p;

820
821
            while( *p && *p != '=' && *p != ',' && *p != '{' && *p != '}' &&
                   *p != ' ' && *p != '\t' ) p++;
822

823
            /* fprintf( stderr, "name=%s - rest=%s\n", psz_name, p ); */
824
825
826
827
828
829
            if( p == psz_name )
            {
                fprintf( stderr, "invalid options (empty)" );
                break;
            }

830
            cfg.psz_name = strndup( psz_name, p - psz_name );
831
832
833

            SKIPSPACE( p );

834
            if( *p == '=' || *p == '{' )
835
836
            {
                char *end;
837
                vlc_bool_t b_keep_brackets = (*p == '{');
838

839
                if( *p == '=' ) p++;
840
841
842
843
844
845
846
847

                end = _get_chain_end( p );
                if( end <= p )
                {
                    cfg.psz_value = NULL;
                }
                else
                {
848
849
850
851
852
853
854
855
856
857
858
859
860
                    /* 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 == '"' ||
861
                        ( !b_keep_brackets && *p == '{' ) )
862
863
                    {
                        p++;
864
865
866
867
868
869
870
871
872
873
874
875

                        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 );
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
                    }
                }

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

903
            if( *p == ',' ) p++;
904
905
906
907
908
909
910
911
912

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

913
    if( *p == ':' ) return( strdup( p + 1 ) );
914

915
    return NULL;
916
917
}

918
static void sout_CfgDestroy( sout_cfg_t *p_cfg )
919
920
921
922
{
    while( p_cfg != NULL )
    {
        sout_cfg_t *p_next;
923

924
        p_next = p_cfg->p_next;
925

zorglub's avatar
zorglub committed
926
927
        FREENULL( p_cfg->psz_name );
        FREENULL( p_cfg->psz_value );
928
929
930
931
932
        free( p_cfg );

        p_cfg = p_next;
    }
}
933

934
void __sout_CfgParse( vlc_object_t *p_this, char *psz_prefix,
935
                      const char **ppsz_options, sout_cfg_t *cfg )
936
937
938
939
940
941
942
943
{
    char *psz_name;
    int  i_type;
    int  i;

    /* First, var_Create all variables */
    for( i = 0; ppsz_options[i] != NULL; i++ )
    {
944
945
        asprintf( &psz_name, "%s%s", psz_prefix,
                  *ppsz_options[i] == '*' ? &ppsz_options[i][1] : ppsz_options[i] );
946
947
948
949
950
951
952
953
954
955
956
957
958

        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;
959
        vlc_bool_t b_yes = VLC_TRUE;
960
        vlc_bool_t b_once = VLC_FALSE;
dionoea's avatar
dionoea committed
961
        module_config_t *p_conf;
962
963
964
965
966
967
968
969
970
971
972
973

        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;
            }
974
975
976
977
            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 ) ) )
978
979
980
981
            {
                b_yes = VLC_FALSE;
                break;
            }
982
983
984
985
986
987
988
989

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

990
991
992
        }
        if( ppsz_options[i] == NULL )
        {
Laurent Aimar's avatar
Laurent Aimar committed
993
            msg_Warn( p_this, "option %s is unknown", cfg->psz_name );
994
995
996
997
998
            cfg = cfg->p_next;
            continue;
        }

        /* create name */
999
        asprintf( &psz_name, "%s%s", psz_prefix, b_once ? &ppsz_options[i][1] : ppsz_options[i] );
1000