stream_output.c 25.6 KB
Newer Older
1
2
3
/*****************************************************************************
 * stream_output.c : stream output module
 *****************************************************************************
4
 * Copyright (C) 2002-2007 the VideoLAN team
5
 * $Id$
6
7
 *
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8
 *          Laurent Aimar <fenrir@via.ecp.fr>
9
 *          Eric Petit <titer@videolan.org>
10
11
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
32
33
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

34
#include <vlc_common.h>
35

36
37
#include <stdlib.h>                                                /* free() */
#include <stdio.h>                                              /* sprintf() */
38
#include <string.h>
39

zorglub's avatar
zorglub committed
40
#include <vlc_sout.h>
41

zorglub's avatar
zorglub committed
42
43
44
45
46
#include "stream_output.h"

#include <vlc_meta.h>

#include "input/input_internal.h"
47

48
#undef DEBUG_BUFFER
49
50
51
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
gbazin's avatar
gbazin committed
52
53
#define sout_stream_url_to_chain( p, s ) \
    _sout_stream_url_to_chain( VLC_OBJECT(p), s )
54
static char *_sout_stream_url_to_chain( vlc_object_t *, const char * );
55

56
57
58
59
60
61
62
/*
 * Generic MRL parser
 *
 */

typedef struct
{
63
64
    char *psz_access;
    char *psz_way;
65
66
67
68
    char *psz_name;
} mrl_t;

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

73
74
75
/*****************************************************************************
 * sout_NewInstance: creates a new stream output instance
 *****************************************************************************/
76
sout_instance_t *__sout_NewInstance( vlc_object_t *p_parent, const char *psz_dest )
77
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
78
    static const char typename[] = "stream output";
79
    sout_instance_t *p_sout;
80

81
    /* *** Allocate descriptor *** */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
82
83
    p_sout = vlc_custom_create( p_parent, sizeof( *p_sout ),
                                VLC_OBJECT_GENERIC, typename );
84
85
86
    if( p_sout == NULL )
        return NULL;

87
88
    /* *** init descriptor *** */
    p_sout->psz_sout    = strdup( psz_dest );
89
    p_sout->p_meta      = NULL;
90
    p_sout->i_out_pace_nocontrol = 0;
91
    p_sout->p_sys       = NULL;
92

93
    vlc_mutex_init( &p_sout->lock );
94
    if( psz_dest && psz_dest[0] == '#' )
95
    {
96
        p_sout->psz_chain = strdup( &psz_dest[1] );
97
    }
98
    else
99
    {
100
101
        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 );
102
    }
103
104
105
106
    p_sout->p_stream = NULL;

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

108
109
    /* */
    var_Create( p_sout, "sout-mux-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
110

111
112
    /* */
    p_sout->p_stream = sout_StreamNew( p_sout, p_sout->psz_chain );
113
    if( p_sout->p_stream == NULL )
114
    {
115
        msg_Err( p_sout, "stream chain failed for `%s'", p_sout->psz_chain );
116

zorglub's avatar
zorglub committed
117
118
        FREENULL( p_sout->psz_sout );
        FREENULL( p_sout->psz_chain );
119

120
        vlc_object_detach( p_sout );
121
        vlc_object_release( p_sout );
122
        return NULL;
123
124
125
126
    }

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

128
129
130
131
132
/*****************************************************************************
 * sout_DeleteInstance: delete a previously allocated instance
 *****************************************************************************/
void sout_DeleteInstance( sout_instance_t * p_sout )
{
133
    /* remove the stream out chain */
134
    sout_StreamDelete( p_sout->p_stream );
135

136
    /* *** free all string *** */
zorglub's avatar
zorglub committed
137
138
    FREENULL( p_sout->psz_sout );
    FREENULL( p_sout->psz_chain );
139

140
    /* delete meta */
141
142
143
144
145
    if( p_sout->p_meta )
    {
        vlc_meta_Delete( p_sout->p_meta );
    }

146
147
    vlc_mutex_destroy( &p_sout->lock );

148
    /* *** free structure *** */
149
    vlc_object_release( p_sout );
150
151
}

152
153
154
155
156
157
158
159
160
/*****************************************************************************
 * 
 *****************************************************************************/
void sout_UpdateStatistic( sout_instance_t *p_sout, sout_statistic_t i_type, int i_delta )
{
    input_thread_t *p_input;
    int i_bytes; /* That's pretty stupid to define it as an integer, it will overflow
                    really fast ... */

161
    if( !libvlc_stats (p_sout) )
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
        return;

    /* FIXME that's ugly
     * TODO add a private (ie not VLC_EXPORTed) input_UpdateStatistic for that */
    p_input = vlc_object_find( p_sout, VLC_OBJECT_INPUT, FIND_PARENT );
    if( !p_input || p_input->i_state == INIT_S || p_input->i_state == ERROR_S )
        return;

    switch( i_type )
    {
#define I(c) stats_UpdateInteger( p_input, p_input->p->counters.c, i_delta, NULL )
    case SOUT_STATISTIC_DECODED_VIDEO:
        I(p_decoded_video);
        break;
    case SOUT_STATISTIC_DECODED_AUDIO:
        I(p_decoded_audio);
        break;
    case SOUT_STATISTIC_DECODED_SUBTITLE:
        I(p_decoded_sub);
        break;
#if 0
    case SOUT_STATISTIC_ENCODED_VIDEO:
    case SOUT_STATISTIC_ENCODED_AUDIO:
    case SOUT_STATISTIC_ENCODED_SUBTITLE:
        msg_Warn( p_sout, "Not yet supported statistic type %d", i_type );
        break;
#endif

    case SOUT_STATISTIC_SENT_PACKET:
        I(p_sout_sent_packets);
        break;
#undef I
    case SOUT_STATISTIC_SENT_BYTE:
        if( !stats_UpdateInteger( p_input, p_input->p->counters.p_sout_sent_bytes, i_delta, &i_bytes ) )
            stats_UpdateFloat( p_input, p_input->p->counters.p_sout_send_bitrate, i_bytes, NULL );
        break;

    default:
        msg_Err( p_sout, "Invalid statistic type %d (internal error)", i_type );
        break;
    }
    vlc_object_release( p_input );
}
205
/*****************************************************************************
206
 * Packetizer/Input
207
 *****************************************************************************/
208
209
sout_packetizer_input_t *sout_InputNew( sout_instance_t *p_sout,
                                        es_format_t *p_fmt )
210
{
211
    sout_packetizer_input_t *p_input;
212

213
214
    /* *** create a packetizer input *** */
    p_input         = malloc( sizeof( sout_packetizer_input_t ) );
Rafaël Carré's avatar
Rafaël Carré committed
215
    if( !p_input )  return NULL;
216
217
    p_input->p_sout = p_sout;
    p_input->p_fmt  = p_fmt;
218

219
220
    msg_Dbg( p_sout, "adding a new sout input (sout_input:%p)", p_input );

221
    if( p_fmt->i_codec == VLC_FOURCC( 'n', 'u', 'l', 'l' ) )
222
223
224
    {
        vlc_object_release( p_sout );
        return p_input;
225
226
    }

227
    /* *** add it to the stream chain */
228
    vlc_mutex_lock( &p_sout->lock );
229
    p_input->id = p_sout->p_stream->pf_add( p_sout->p_stream, p_fmt );
230
    vlc_mutex_unlock( &p_sout->lock );
231

232
    if( p_input->id == NULL )
233
    {
234
        free( p_input );
235
        return NULL;
236
    }
237

238
    return( p_input );
239
}
240

241
242
243
/*****************************************************************************
 *
 *****************************************************************************/
244
int sout_InputDelete( sout_packetizer_input_t *p_input )
245
{
246
247
    sout_instance_t     *p_sout = p_input->p_sout;

248
    msg_Dbg( p_sout, "removing a sout input (sout_input:%p)", p_input );
249

250
    if( p_input->p_fmt->i_codec != VLC_FOURCC( 'n', 'u', 'l', 'l' ) )
251
    {
252
253
254
        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 );
255
    }
256

257
    free( p_input );
258

259
260
    return( VLC_SUCCESS);
}
261

262
263
264
/*****************************************************************************
 *
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
265
int sout_InputSendBuffer( sout_packetizer_input_t *p_input,
266
                          block_t *p_buffer )
267
268
{
    sout_instance_t     *p_sout = p_input->p_sout;
269
    int                 i_ret;
270

271
    if( p_input->p_fmt->i_codec == VLC_FOURCC( 'n', 'u', 'l', 'l' ) )
272
    {
273
        block_Release( p_buffer );
274
275
        return VLC_SUCCESS;
    }
276
    if( p_buffer->i_dts <= 0 )
gbazin's avatar
   
gbazin committed
277
278
    {
        msg_Warn( p_sout, "trying to send non-dated packet to stream output!");
279
        block_Release( p_buffer );
gbazin's avatar
   
gbazin committed
280
281
282
        return VLC_SUCCESS;
    }

283
    vlc_mutex_lock( &p_sout->lock );
gbazin's avatar
   
gbazin committed
284
285
    i_ret = p_sout->p_stream->pf_send( p_sout->p_stream,
                                       p_input->id, p_buffer );
286
    vlc_mutex_unlock( &p_sout->lock );
287

288
    return i_ret;
289
}
290

291
292
293
294
/*****************************************************************************
 * sout_AccessOutNew: allocate a new access out
 *****************************************************************************/
sout_access_out_t *sout_AccessOutNew( sout_instance_t *p_sout,
295
                                      const char *psz_access, const char *psz_name )
296
{
297
    static const char typename[] = "access out";
298
    sout_access_out_t *p_access;
299
    char              *psz_next;
300

301
302
303
    p_access = vlc_custom_create( p_sout, sizeof( *p_access ),
                                  VLC_OBJECT_GENERIC, typename );
    if( !p_access )
304
        return NULL;
305

306
307
    psz_next = config_ChainCreate( &p_access->psz_access, &p_access->p_cfg,
                                   psz_access );
308
    free( psz_next );
309
    p_access->psz_path   = strdup( psz_name ? psz_name : "" );
310
    p_access->p_sout     = p_sout;
311
    p_access->p_sys      = NULL;
312
    p_access->pf_seek    = NULL;
gbazin's avatar
   
gbazin committed
313
    p_access->pf_read    = NULL;
314
    p_access->pf_write   = NULL;
315
    p_access->pf_control = NULL;
316
    p_access->p_module   = NULL;
317
318
319
320

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

321
    vlc_object_attach( p_access, p_sout );
322

323
    p_access->p_module   =
324
        module_Need( p_access, "sout access", p_access->psz_access, true );
325
326
327

    if( !p_access->p_module )
    {
328
        free( p_access->psz_access );
329
        free( p_access->psz_path );
330
        vlc_object_detach( p_access );
331
        vlc_object_release( p_access );
332
        return( NULL );
333
334
335
336
337
338
339
    }

    return p_access;
}
/*****************************************************************************
 * sout_AccessDelete: delete an access out
 *****************************************************************************/
340
void sout_AccessOutDelete( sout_access_out_t *p_access )
341
{
342
    vlc_object_detach( p_access );
343
344
345
346
347
    if( p_access->p_module )
    {
        module_Unneed( p_access, p_access->p_module );
    }
    free( p_access->psz_access );
348

349
    config_ChainDestroy( p_access->p_cfg );
350

351
    free( p_access->psz_path );
352

353
    vlc_object_release( p_access );
354
355
356
357
358
}

/*****************************************************************************
 * sout_AccessSeek:
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
359
int sout_AccessOutSeek( sout_access_out_t *p_access, off_t i_pos )
360
{
gbazin's avatar
   
gbazin committed
361
    return p_access->pf_seek( p_access, i_pos );
362
363
364
}

/*****************************************************************************
gbazin's avatar
   
gbazin committed
365
 * sout_AccessRead:
366
 *****************************************************************************/
367
ssize_t sout_AccessOutRead( sout_access_out_t *p_access, block_t *p_buffer )
368
{
369
370
    return( p_access->pf_read ?
            p_access->pf_read( p_access, p_buffer ) : VLC_EGENERIC );
371
372
}

gbazin's avatar
   
gbazin committed
373
374
375
/*****************************************************************************
 * sout_AccessWrite:
 *****************************************************************************/
376
ssize_t sout_AccessOutWrite( sout_access_out_t *p_access, block_t *p_buffer )
gbazin's avatar
   
gbazin committed
377
{
378
    const unsigned i_packets_gather = 30;
379
380
    p_access->i_writes++;
    p_access->i_sent_bytes += p_buffer->i_buffer;
381
    if( (p_access->i_writes % i_packets_gather) == 0 )
zorglub's avatar
zorglub committed
382
    {
383
384
385
        sout_UpdateStatistic( p_access->p_sout, SOUT_STATISTIC_SENT_PACKET, i_packets_gather );
        sout_UpdateStatistic( p_access->p_sout, SOUT_STATISTIC_SENT_BYTE, p_access->i_sent_bytes );
        p_access->i_sent_bytes = 0;
zorglub's avatar
zorglub committed
386
    }
gbazin's avatar
   
gbazin committed
387
388
    return p_access->pf_write( p_access, p_buffer );
}
389

390
391
392
/**
 * sout_AccessOutControl
 */
393
int sout_AccessOutControl (sout_access_out_t *access, int query, ...)
394
{
395
396
397
398
399
400
401
402
403
404
    va_list ap;
    int ret;

    va_start (ap, query);
    if (access->pf_control)
        ret = access->pf_control (access, query, ap);
    else
        ret = VLC_EGENERIC;
    va_end (ap);
    return ret;
405
406
}

407
/*****************************************************************************
408
 * sout_MuxNew: create a new mux
409
 *****************************************************************************/
410
411
sout_mux_t * sout_MuxNew( sout_instance_t *p_sout, char *psz_mux,
                          sout_access_out_t *p_access )
412
{
413
    static const char typename[] = "mux";
414
    sout_mux_t *p_mux;
415
    char       *psz_next;
416

417
418
    p_mux = vlc_custom_create( p_sout, sizeof( *p_mux ), VLC_OBJECT_GENERIC,
                               typename);
419
420
421
    if( p_mux == NULL )
        return NULL;

gbazin's avatar
gbazin committed
422
    p_mux->p_sout = p_sout;
423
    psz_next = config_ChainCreate( &p_mux->psz_mux, &p_mux->p_cfg, psz_mux );
424
    free( psz_next );
gbazin's avatar
gbazin committed
425

426
    p_mux->p_access     = p_access;
427
    p_mux->pf_control   = NULL;
428
429
430
431
432
433
434
    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
435
436
    p_mux->p_module     = NULL;

437
438
    p_mux->b_add_stream_any_time = false;
    p_mux->b_waiting_stream = true;
gbazin's avatar
gbazin committed
439
    p_mux->i_add_stream_start = -1;
440
441

    vlc_object_attach( p_mux, p_sout );
442

gbazin's avatar
gbazin committed
443
    p_mux->p_module =
444
        module_Need( p_mux, "sout mux", p_mux->psz_mux, true );
445

446
447
    if( p_mux->p_module == NULL )
    {
zorglub's avatar
zorglub committed
448
        FREENULL( p_mux->psz_mux );
449

450
        vlc_object_detach( p_mux );
451
        vlc_object_release( p_mux );
452
453
454
455
        return NULL;
    }

    /* *** probe mux capacity *** */
456
    if( p_mux->pf_control )
457
    {
458
        int b_answer = false;
gbazin's avatar
gbazin committed
459
460
461

        if( sout_MuxControl( p_mux, MUX_CAN_ADD_STREAM_WHILE_MUXING,
                             &b_answer ) )
462
        {
463
            b_answer = false;
464
        }
gbazin's avatar
gbazin committed
465

466
467
468
        if( b_answer )
        {
            msg_Dbg( p_sout, "muxer support adding stream at any time" );
469
470
            p_mux->b_add_stream_any_time = true;
            p_mux->b_waiting_stream = false;
gbazin's avatar
   
gbazin committed
471

gbazin's avatar
gbazin committed
472
473
474
475
            /* 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 )
            {
476
                b_answer = true;
gbazin's avatar
gbazin committed
477
478
479
            }
            else if( sout_MuxControl( p_mux, MUX_GET_ADD_STREAM_WAIT,
                                      &b_answer ) )
gbazin's avatar
   
gbazin committed
480
            {
481
                b_answer = false;
gbazin's avatar
   
gbazin committed
482
            }
gbazin's avatar
gbazin committed
483

gbazin's avatar
   
gbazin committed
484
485
            if( b_answer )
            {
486
487
                msg_Dbg( p_sout, "muxer prefers to wait for all ES before "
                         "starting to mux" );
488
                p_mux->b_waiting_stream = true;
gbazin's avatar
   
gbazin committed
489
            }
490
491
492
493
494
495
        }
    }

    return p_mux;
}

496
497
498
/*****************************************************************************
 * sout_MuxDelete:
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
499
void sout_MuxDelete( sout_mux_t *p_mux )
500
{
501
    vlc_object_detach( p_mux );
502
503
504
505
506
507
    if( p_mux->p_module )
    {
        module_Unneed( p_mux, p_mux->p_module );
    }
    free( p_mux->psz_mux );

508
    config_ChainDestroy( p_mux->p_cfg );
509

510
    vlc_object_release( p_mux );
511
512
}

513
514
515
/*****************************************************************************
 * sout_MuxAddStream:
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
516
sout_input_t *sout_MuxAddStream( sout_mux_t *p_mux, es_format_t *p_fmt )
517
518
519
{
    sout_input_t *p_input;

gbazin's avatar
   
gbazin committed
520
    if( !p_mux->b_add_stream_any_time && !p_mux->b_waiting_stream )
521
    {
522
        msg_Err( p_mux, "cannot add a new stream (unsupported while muxing "
523
                        "to this format). You can try increasing sout-mux-caching value" );
524
525
526
527
        return NULL;
    }

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

529
    /* create a new sout input */
530
    p_input = malloc( sizeof( sout_input_t ) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
531
532
    if( !p_input )
        return NULL;
533
534
    p_input->p_sout = p_mux->p_sout;
    p_input->p_fmt  = p_fmt;
535
    p_input->p_fifo = block_FifoNew();
536
    p_input->p_sys  = NULL;
537
538
539
540

    TAB_APPEND( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input );
    if( p_mux->pf_addstream( p_mux, p_input ) < 0 )
    {
541
542
543
544
545
        msg_Err( p_mux, "cannot add this stream" );
        TAB_REMOVE( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input );
        block_FifoRelease( p_input->p_fifo );
        free( p_input );
        return NULL;
546
547
    }

548
    return p_input;
549
550
}

551
552
553
554
/*****************************************************************************
 * sout_MuxDeleteStream:
 *****************************************************************************/
void sout_MuxDeleteStream( sout_mux_t *p_mux, sout_input_t *p_input )
555
556
557
{
    int i_index;

558
559
    if( p_mux->b_waiting_stream
     && block_FifoCount( p_input->p_fifo ) > 0 )
560
561
562
    {
        /* We stop waiting, and call the muxer for taking care of the data
         * before we remove this es */
563
        p_mux->b_waiting_stream = false;
564
565
566
        p_mux->pf_mux( p_mux );
    }

567
568
569
570
571
    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 )
        {
572
            msg_Err( p_mux, "cannot delete this stream from mux" );
573
574
575
576
577
578
579
        }

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

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

583
        block_FifoRelease( p_input->p_fifo );
584
        free( p_input );
585
586
587
    }
}

588
589
590
591
592
/*****************************************************************************
 * sout_MuxSendBuffer:
 *****************************************************************************/
void sout_MuxSendBuffer( sout_mux_t *p_mux, sout_input_t *p_input,
                         block_t *p_buffer )
593
{
594
    block_FifoPut( p_input->p_fifo, p_buffer );
595

596
597
598
599
    if( p_mux->p_sout->i_out_pace_nocontrol )
    {
        mtime_t current_date = mdate();
        if ( current_date > p_buffer->i_dts )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
600
            msg_Warn( p_mux, "late buffer for mux input (%"PRId64")",
601
602
603
                      current_date - p_buffer->i_dts );
    }

604
605
    if( p_mux->b_waiting_stream )
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
606
        const int64_t i_caching = var_GetInteger( p_mux->p_sout, "sout-mux-caching" ) * INT64_C(1000);
607

gbazin's avatar
gbazin committed
608
609
610
        if( p_mux->i_add_stream_start < 0 )
            p_mux->i_add_stream_start = p_buffer->i_dts;

611
612
613
        /* Wait until we have enought data before muxing */
        if( p_mux->i_add_stream_start < 0 ||
            p_buffer->i_dts < p_mux->i_add_stream_start + i_caching )
614
            return;
615
        p_mux->b_waiting_stream = false;
616
617
618
619
    }
    p_mux->pf_mux( p_mux );
}

620
621
622
/*****************************************************************************
 *
 *****************************************************************************/
623
static int mrl_Parse( mrl_t *p_mrl, const char *psz_mrl )
624
{
Laurent Aimar's avatar
Laurent Aimar committed
625
    char * psz_dup = strdup( psz_mrl );
626
    char * psz_parser = psz_dup;
zorglub's avatar
zorglub committed
627
628
    const char * psz_access;
    const char * psz_way;
629
    char * psz_name;
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652

    /* *** 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 )
    {
653
654
        /* msg_Warn( p_sout, "drive letter %c: found in source string",
                          *psz_dup ) ; */
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
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
        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
734
    free( psz_dup );
735
736
737
738
739
740
741
    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
742
743
744
    FREENULL( p_mrl->psz_access );
    FREENULL( p_mrl->psz_way );
    FREENULL( p_mrl->psz_name );
745
746
747
}


748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
/****************************************************************************
 ****************************************************************************
 **
 **
 **
 ****************************************************************************
 ****************************************************************************/

/* 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
 */

/*
 * XXX name and p_cfg are used (-> do NOT free them)
 */
770
sout_stream_t *sout_StreamNew( sout_instance_t *p_sout, char *psz_chain )
771
{
772
    static const char typename[] = "stream out";
773
774
    sout_stream_t *p_stream;

775
776
777
778
779
780
    if( !psz_chain )
    {
        msg_Err( p_sout, "invalid chain" );
        return NULL;
    }

781
782
    p_stream = vlc_custom_create( p_sout, sizeof( *p_stream ),
                                  VLC_OBJECT_GENERIC, typename );
783
784
785
786
787
788
    if( !p_stream )
        return NULL;

    p_stream->p_sout   = p_sout;
    p_stream->p_sys    = NULL;

gbazin's avatar
   
gbazin committed
789
    p_stream->psz_next =
790
        config_ChainCreate( &p_stream->psz_name, &p_stream->p_cfg, psz_chain);
gbazin's avatar
   
gbazin committed
791

792
793
    msg_Dbg( p_sout, "stream=`%s'", p_stream->psz_name );

794
795
    vlc_object_attach( p_stream, p_sout );

796
    p_stream->p_module =
797
        module_Need( p_stream, "sout stream", p_stream->psz_name, true );
798
799
800

    if( !p_stream->p_module )
    {
801
        sout_StreamDelete( p_stream );
802
803
804
805
806
807
        return NULL;
    }

    return p_stream;
}

808
void sout_StreamDelete( sout_stream_t *p_stream )
809
810
{
    msg_Dbg( p_stream, "destroying chain... (name=%s)", p_stream->psz_name );
811
812

    vlc_object_detach( p_stream );
gbazin's avatar
   
gbazin committed
813
    if( p_stream->p_module ) module_Unneed( p_stream, p_stream->p_module );
814

zorglub's avatar
zorglub committed
815
816
    FREENULL( p_stream->psz_name );
    FREENULL( p_stream->psz_next );
817

818
    config_ChainDestroy( p_stream->p_cfg );
819
820

    msg_Dbg( p_stream, "destroying chain done" );
821
    vlc_object_release( p_stream );
822
823
}

824
825
static char *_sout_stream_url_to_chain( vlc_object_t *p_this,
                                        const char *psz_url )
826
{
827
    mrl_t       mrl;
828
    char        *psz_chain;
829
830

    mrl_Parse( &mrl, psz_url );
831

832
833
834
835
836
837
    /* Check if the URLs goes to #rtp - otherwise we'll use #standard */
    static const char rtplist[] = "dccp\0sctp\0tcp\0udplite\0";
    for (const char *a = rtplist; *a; a += strlen (a) + 1)
        if (strcmp (a, mrl.psz_access) == 0)
            goto rtp;

838
    if (strcmp (mrl.psz_access, "rtp") == 0)
839
    {
840
        char *port;
841
842
        /* For historical reasons, rtp:// means RTP over UDP */
        strcpy (mrl.psz_access, "udp");
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
rtp:
        if (mrl.psz_name[0] == '[')
        {
            port = strstr (mrl.psz_name, "]:");
            if (port != NULL)
                port++;
        }
        else
            port = strchr (mrl.psz_name, ':');
        if (port != NULL)
            *port++ = '\0'; /* erase ':' */

        if (asprintf (&psz_chain,
                      "rtp{mux=\"%s\",proto=\"%s\",dst=\"%s%s%s\"}",
                      mrl.psz_way, mrl.psz_access, mrl.psz_name,
                      port ? "\",port=\"" : "", port ? port : "") == -1)
            psz_chain = NULL;
860
861
862
    }
    else
    {
863
864
865
866
867
        /* Convert the URL to a basic standard sout chain */
        if (asprintf (&psz_chain,
                      "standard{mux=\"%s\",access=\"%s\",dst=\"%s\"}",
                      mrl.psz_way, mrl.psz_access, mrl.psz_name) == -1)
            psz_chain = NULL;
868
869
870
871
872
873
874
875
876
877
    }

    /* Duplicate and wrap if sout-display is on */
    if (psz_chain && (config_GetInt( p_this, "sout-display" ) > 0))
    {
        char *tmp;
        if (asprintf (&tmp, "duplicate{dst=display,dst=%s}", tmp) == -1)
            tmp = NULL;
        free (psz_chain);
        psz_chain = tmp;
878
    }
879

880
    mrl_Clean( &mrl );
881
    return psz_chain;
882
}