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
Antoine Cellerier's avatar
Antoine Cellerier 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

Clément Stenac's avatar
Clément Stenac committed
40
#include <vlc_sout.h>
41

Clément Stenac's avatar
Clément Stenac 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
 *****************************************************************************/
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
{
78
    static const char typename[] = "stream output";
79
    sout_instance_t *p_sout;
80

81
    /* *** Allocate descriptor *** */
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

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 *** */
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
/*****************************************************************************
 *
 *****************************************************************************/
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 )
277 278
    {
        msg_Warn( p_sout, "trying to send non-dated packet to stream output!");
279
        block_Release( p_buffer );
280 281 282
        return VLC_SUCCESS;
    }

283
    vlc_mutex_lock( &p_sout->lock );
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;
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:
 *****************************************************************************/
359
int sout_AccessOutSeek( sout_access_out_t *p_access, off_t i_pos )
360
{
361
    return p_access->pf_seek( p_access, i_pos );
362 363 364
}

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

373 374 375
/*****************************************************************************
 * sout_AccessWrite:
 *****************************************************************************/
376
ssize_t sout_AccessOutWrite( sout_access_out_t *p_access, block_t *p_buffer )
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 )
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;
386
    }
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;

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 );
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;
435 436
    p_mux->p_module     = NULL;

437 438
    p_mux->b_add_stream_any_time = false;
    p_mux->b_waiting_stream = true;
439
    p_mux->i_add_stream_start = -1;
440 441

    vlc_object_attach( p_mux, p_sout );
442

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 )
    {
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;
459 460 461

        if( sout_MuxControl( p_mux, MUX_CAN_ADD_STREAM_WHILE_MUXING,
                             &b_answer ) )
462
        {
463
            b_answer = false;
464
        }
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;
471

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;
477 478 479
            }
            else if( sout_MuxControl( p_mux, MUX_GET_ADD_STREAM_WAIT,
                                      &b_answer ) )
480
            {
481
                b_answer = false;
482
            }
483

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;
489
            }
490 491 492 493 494 495
        }
    }

    return p_mux;
}

496 497 498
/*****************************************************************************
 * sout_MuxDelete:
 *****************************************************************************/
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:
 *****************************************************************************/
516
sout_input_t *sout_MuxAddStream( sout_mux_t *p_mux, es_format_t *p_fmt )
517 518 519
{
    sout_input_t *p_input;

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

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;
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 )
{
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;

789
    p_stream->psz_next =
790
        config_ChainCreate( &p_stream->psz_name, &p_stream->p_cfg, psz_chain);
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 );
813
    if( p_stream->p_module ) module_Unneed( p_stream, p_stream->p_module );
814

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
}