stream_output.c 27.4 KB
Newer Older
1 2 3
/*****************************************************************************
 * stream_output.c : stream output module
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2002-2007 VLC authors and VideoLAN
5
 * $Id$
6 7
 *
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8
 *          Laurent Aimar <fenrir@via.ecp.fr>
9
 *          Eric Petit <titer@videolan.org>
10
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
11 12 13
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
14 15 16 17
 * (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
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
18 19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
20
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
21 22 23
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software 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 35
#include <assert.h>

36
#include <vlc_common.h>
37

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

Clément Stenac's avatar
Clément Stenac committed
42
#include <vlc_sout.h>
43

Clément Stenac's avatar
Clément Stenac committed
44 45 46
#include "stream_output.h"

#include <vlc_meta.h>
47
#include <vlc_block.h>
48
#include <vlc_codec.h>
49
#include <vlc_modules.h>
Clément Stenac's avatar
Clément Stenac committed
50

51
#include "input/input_interface.h"
52

53
#undef DEBUG_BUFFER
54 55 56
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
57
static char *sout_stream_url_to_chain( bool, const char * );
58

59 60 61 62 63 64 65
/*
 * Generic MRL parser
 *
 */

typedef struct
{
66 67
    char *psz_access;
    char *psz_way;
68 69 70 71
    char *psz_name;
} mrl_t;

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

76 77
#undef sout_NewInstance

78 79 80
/*****************************************************************************
 * sout_NewInstance: creates a new stream output instance
 *****************************************************************************/
81
sout_instance_t *sout_NewInstance( vlc_object_t *p_parent, const char *psz_dest )
82
{
83
    sout_instance_t *p_sout;
84
    char *psz_chain;
85 86 87 88

    assert( psz_dest != NULL );

    if( psz_dest[0] == '#' )
89 90 91 92 93 94
    {
        psz_chain = strdup( &psz_dest[1] );
    }
    else
    {
        psz_chain = sout_stream_url_to_chain(
95
            var_InheritBool(p_parent, "sout-display"), psz_dest );
96 97 98 99
    }
    if(!psz_chain)
        return NULL;

100
    /* *** Allocate descriptor *** */
101
    p_sout = vlc_custom_create( p_parent, sizeof( *p_sout ), "stream output" );
102
    if( p_sout == NULL )
Rémi Duraffort's avatar
Rémi Duraffort committed
103 104
    {
        free( psz_chain );
105
        return NULL;
Rémi Duraffort's avatar
Rémi Duraffort committed
106
    }
107

108 109
    msg_Dbg( p_sout, "using sout chain=`%s'", psz_chain );

110 111
    /* *** init descriptor *** */
    p_sout->psz_sout    = strdup( psz_dest );
112
    p_sout->i_out_pace_nocontrol = 0;
113

114
    vlc_mutex_init( &p_sout->lock );
115 116
    p_sout->p_stream = NULL;

117
    var_Create( p_sout, "sout-mux-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
118

119 120
    p_sout->p_stream = sout_StreamChainNew( p_sout, psz_chain, NULL, NULL );
    if( p_sout->p_stream )
121
    {
122 123 124
        free( psz_chain );
        return p_sout;
    }
125

126
    msg_Err( p_sout, "stream chain failed for `%s'", psz_chain );
127
    free( psz_chain );
128

129
    FREENULL( p_sout->psz_sout );
130

131
    vlc_mutex_destroy( &p_sout->lock );
132 133
    vlc_object_release( p_sout );
    return NULL;
134
}
Jean-Paul Saman's avatar
Jean-Paul Saman committed
135

136 137 138 139 140
/*****************************************************************************
 * sout_DeleteInstance: delete a previously allocated instance
 *****************************************************************************/
void sout_DeleteInstance( sout_instance_t * p_sout )
{
141
    /* remove the stream out chain */
142
    sout_StreamChainDelete( p_sout->p_stream, NULL );
143

144
    /* *** free all string *** */
145
    FREENULL( p_sout->psz_sout );
146 147 148

    vlc_mutex_destroy( &p_sout->lock );

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

153
/*****************************************************************************
154
 * Packetizer/Input
155
 *****************************************************************************/
156
sout_packetizer_input_t *sout_InputNew( sout_instance_t *p_sout,
157
                                        const es_format_t *p_fmt )
158
{
159
    sout_packetizer_input_t *p_input;
160

161
    /* *** create a packetizer input *** */
162 163 164
    if( !p_fmt->i_codec || !(p_input = malloc(sizeof(sout_packetizer_input_t))) )
        return NULL;

165
    p_input->p_sout = p_sout;
166

167 168
    msg_Dbg( p_sout, "adding a new sout input for `%4.4s` (sout_input: %p)",
             (char*) &p_fmt->i_codec, (void *)p_input );
169

170
    /* *** add it to the stream chain */
171
    vlc_mutex_lock( &p_sout->lock );
172
    p_input->id = p_sout->p_stream->pf_add( p_sout->p_stream, p_fmt );
173
    vlc_mutex_unlock( &p_sout->lock );
174

175
    if( p_input->id == NULL )
176
    {
177 178
        msg_Warn( p_sout, "new sout input failed (sout_input: %p)",
                 (void *)p_input );
179
        free( p_input );
180
        p_input = NULL;
181
    }
182

183
    return( p_input );
184
}
185

186 187 188
/*****************************************************************************
 *
 *****************************************************************************/
189
int sout_InputDelete( sout_packetizer_input_t *p_input )
190
{
191 192
    sout_instance_t     *p_sout = p_input->p_sout;

193 194
    msg_Dbg( p_sout, "removing a sout input (sout_input: %p)",
             (void *)p_input );
195

196 197 198
    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 );
199

200
    free( p_input );
201

202 203
    return( VLC_SUCCESS);
}
204

205 206 207 208 209 210 211 212 213 214 215 216
bool sout_InputIsEmpty( sout_packetizer_input_t *p_input )
{
    sout_instance_t *p_sout = p_input->p_sout;
    bool b;

    vlc_mutex_lock( &p_sout->lock );
    if( sout_StreamControl( p_sout->p_stream, SOUT_STREAM_EMPTY, &b ) != VLC_SUCCESS )
        b = true;
    vlc_mutex_unlock( &p_sout->lock );
    return b;
}

217 218 219 220 221 222 223 224 225
void sout_InputFlush( sout_packetizer_input_t *p_input )
{
    sout_instance_t     *p_sout = p_input->p_sout;

    vlc_mutex_lock( &p_sout->lock );
    sout_StreamFlush( p_sout->p_stream, p_input->id );
    vlc_mutex_unlock( &p_sout->lock );
}

226 227 228
/*****************************************************************************
 *
 *****************************************************************************/
229
int sout_InputSendBuffer( sout_packetizer_input_t *p_input,
230
                          block_t *p_buffer )
231 232
{
    sout_instance_t     *p_sout = p_input->p_sout;
233
    int                 i_ret;
234

235
    vlc_mutex_lock( &p_sout->lock );
236 237
    i_ret = p_sout->p_stream->pf_send( p_sout->p_stream,
                                       p_input->id, p_buffer );
238
    vlc_mutex_unlock( &p_sout->lock );
239

240
    return i_ret;
241
}
242

243
#undef sout_AccessOutNew
244 245 246
/*****************************************************************************
 * sout_AccessOutNew: allocate a new access out
 *****************************************************************************/
247
sout_access_out_t *sout_AccessOutNew( vlc_object_t *p_sout,
248
                                      const char *psz_access, const char *psz_name )
249 250
{
    sout_access_out_t *p_access;
251
    char              *psz_next;
252

253
    p_access = vlc_custom_create( p_sout, sizeof( *p_access ), "access out" );
254
    if( !p_access )
255
        return NULL;
256

257 258
    psz_next = config_ChainCreate( &p_access->psz_access, &p_access->p_cfg,
                                   psz_access );
259
    free( psz_next );
260
    p_access->psz_path   = strdup( psz_name ? psz_name : "" );
261
    p_access->p_sys      = NULL;
262
    p_access->pf_seek    = NULL;
263
    p_access->pf_read    = NULL;
264
    p_access->pf_write   = NULL;
265
    p_access->pf_control = NULL;
266
    p_access->p_module   = NULL;
267

268
    p_access->p_module   =
269
        module_need( p_access, "sout access", p_access->psz_access, true );
270 271 272

    if( !p_access->p_module )
    {
273
        free( p_access->psz_access );
274
        free( p_access->psz_path );
275
        vlc_object_release( p_access );
276
        return( NULL );
277 278 279 280 281 282 283
    }

    return p_access;
}
/*****************************************************************************
 * sout_AccessDelete: delete an access out
 *****************************************************************************/
284
void sout_AccessOutDelete( sout_access_out_t *p_access )
285 286 287
{
    if( p_access->p_module )
    {
288
        module_unneed( p_access, p_access->p_module );
289 290
    }
    free( p_access->psz_access );
291

292
    config_ChainDestroy( p_access->p_cfg );
293

294
    free( p_access->psz_path );
295

296
    vlc_object_release( p_access );
297 298 299 300 301
}

/*****************************************************************************
 * sout_AccessSeek:
 *****************************************************************************/
302
int sout_AccessOutSeek( sout_access_out_t *p_access, off_t i_pos )
303
{
304 305
    if (p_access->pf_seek == NULL)
        return VLC_EGENERIC;
306
    return p_access->pf_seek( p_access, i_pos );
307 308 309
}

/*****************************************************************************
310
 * sout_AccessRead:
311
 *****************************************************************************/
312
ssize_t sout_AccessOutRead( sout_access_out_t *p_access, block_t *p_buffer )
313
{
314 315
    return( p_access->pf_read ?
            p_access->pf_read( p_access, p_buffer ) : VLC_EGENERIC );
316 317
}

318 319 320
/*****************************************************************************
 * sout_AccessWrite:
 *****************************************************************************/
321
ssize_t sout_AccessOutWrite( sout_access_out_t *p_access, block_t *p_buffer )
322 323 324
{
    return p_access->pf_write( p_access, p_buffer );
}
325

326 327 328
/**
 * sout_AccessOutControl
 */
329
int sout_AccessOutControl (sout_access_out_t *access, int query, ...)
330
{
331 332 333 334 335 336 337 338 339 340
    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;
341 342
}

343
/*****************************************************************************
344
 * sout_MuxNew: create a new mux
345
 *****************************************************************************/
Rémi Duraffort's avatar
Rémi Duraffort committed
346
sout_mux_t * sout_MuxNew( sout_instance_t *p_sout, const char *psz_mux,
347
                          sout_access_out_t *p_access )
348 349
{
    sout_mux_t *p_mux;
350
    char       *psz_next;
351

352
    p_mux = vlc_custom_create( p_sout, sizeof( *p_mux ), "mux" );
353 354 355
    if( p_mux == NULL )
        return NULL;

356
    p_mux->p_sout = p_sout;
357
    psz_next = config_ChainCreate( &p_mux->psz_mux, &p_mux->p_cfg, psz_mux );
358
    free( psz_next );
359

360
    p_mux->p_access     = p_access;
361
    p_mux->pf_control   = NULL;
362 363 364 365 366 367 368
    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;
369 370
    p_mux->p_module     = NULL;

371 372
    p_mux->b_add_stream_any_time = false;
    p_mux->b_waiting_stream = true;
373
    p_mux->i_add_stream_start = -1;
374

375
    p_mux->p_module =
376
        module_need( p_mux, "sout mux", p_mux->psz_mux, true );
377

378 379
    if( p_mux->p_module == NULL )
    {
380
        FREENULL( p_mux->psz_mux );
381

382
        vlc_object_release( p_mux );
383 384 385 386
        return NULL;
    }

    /* *** probe mux capacity *** */
387
    if( p_mux->pf_control )
388
    {
389
        int b_answer = false;
390 391 392

        if( sout_MuxControl( p_mux, MUX_CAN_ADD_STREAM_WHILE_MUXING,
                             &b_answer ) )
393
        {
394
            b_answer = false;
395
        }
396

397 398 399
        if( b_answer )
        {
            msg_Dbg( p_sout, "muxer support adding stream at any time" );
400 401
            p_mux->b_add_stream_any_time = true;
            p_mux->b_waiting_stream = false;
402

403 404 405 406
            /* 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 )
            {
407
                b_answer = true;
408 409 410
            }
            else if( sout_MuxControl( p_mux, MUX_GET_ADD_STREAM_WAIT,
                                      &b_answer ) )
411
            {
412
                b_answer = false;
413
            }
414

415 416
            if( b_answer )
            {
417 418
                msg_Dbg( p_sout, "muxer prefers to wait for all ES before "
                         "starting to mux" );
419
                p_mux->b_waiting_stream = true;
420
            }
421 422 423 424 425 426
        }
    }

    return p_mux;
}

427 428 429
/*****************************************************************************
 * sout_MuxDelete:
 *****************************************************************************/
430
void sout_MuxDelete( sout_mux_t *p_mux )
431 432 433
{
    if( p_mux->p_module )
    {
434
        module_unneed( p_mux, p_mux->p_module );
435 436 437
    }
    free( p_mux->psz_mux );

438
    config_ChainDestroy( p_mux->p_cfg );
439

440
    vlc_object_release( p_mux );
441 442
}

443 444 445
/*****************************************************************************
 * sout_MuxAddStream:
 *****************************************************************************/
446
sout_input_t *sout_MuxAddStream( sout_mux_t *p_mux, const es_format_t *p_fmt )
447 448 449
{
    sout_input_t *p_input;

450
    if( !p_mux->b_add_stream_any_time && !p_mux->b_waiting_stream )
451
    {
452
        msg_Err( p_mux, "cannot add a new stream (unsupported while muxing "
453
                        "to this format). You can try increasing sout-mux-caching value" );
454 455 456 457
        return NULL;
    }

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

459
    /* create a new sout input */
460
    p_input = malloc( sizeof( sout_input_t ) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
461 462
    if( !p_input )
        return NULL;
463 464 465 466 467

    // FIXME: remove either fmt or p_fmt...
    es_format_Copy( &p_input->fmt, p_fmt );
    p_input->p_fmt = &p_input->fmt;

468
    p_input->p_fifo = block_FifoNew();
469
    p_input->p_sys  = NULL;
470 471 472 473

    TAB_APPEND( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input );
    if( p_mux->pf_addstream( p_mux, p_input ) < 0 )
    {
474 475 476
        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 );
477
        es_format_Clean( &p_input->fmt );
478 479
        free( p_input );
        return NULL;
480 481
    }

482
    return p_input;
483 484
}

485 486 487 488
/*****************************************************************************
 * sout_MuxDeleteStream:
 *****************************************************************************/
void sout_MuxDeleteStream( sout_mux_t *p_mux, sout_input_t *p_input )
489 490 491
{
    int i_index;

492 493
    if( p_mux->b_waiting_stream
     && block_FifoCount( p_input->p_fifo ) > 0 )
494 495 496
    {
        /* We stop waiting, and call the muxer for taking care of the data
         * before we remove this es */
497
        p_mux->b_waiting_stream = false;
498 499 500
        p_mux->pf_mux( p_mux );
    }

501 502 503
    TAB_FIND( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input, i_index );
    if( i_index >= 0 )
    {
504
        p_mux->pf_delstream( p_mux, p_input );
505 506 507 508 509 510

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

        if( p_mux->i_nb_inputs == 0 )
        {
511
            msg_Warn( p_mux, "no more input streams for this mux" );
512 513
        }

514
        block_FifoRelease( p_input->p_fifo );
515
        es_format_Clean( &p_input->fmt );
516
        free( p_input );
517 518 519
    }
}

520 521 522
/*****************************************************************************
 * sout_MuxSendBuffer:
 *****************************************************************************/
523
int sout_MuxSendBuffer( sout_mux_t *p_mux, sout_input_t *p_input,
524
                         block_t *p_buffer )
525
{
526
    mtime_t i_dts = p_buffer->i_dts;
527
    block_FifoPut( p_input->p_fifo, p_buffer );
528

529 530 531
    if( p_mux->p_sout->i_out_pace_nocontrol )
    {
        mtime_t current_date = mdate();
532
        if ( current_date > i_dts )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
533
            msg_Warn( p_mux, "late buffer for mux input (%"PRId64")",
534
                      current_date - i_dts );
535 536
    }

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

541
        if( p_mux->i_add_stream_start < 0 )
542
            p_mux->i_add_stream_start = i_dts;
543

Steve Lhomme's avatar
Steve Lhomme committed
544
        /* Wait until we have enough data before muxing */
545
        if( p_mux->i_add_stream_start < 0 ||
546
            i_dts < p_mux->i_add_stream_start + i_caching )
547
            return VLC_SUCCESS;
548
        p_mux->b_waiting_stream = false;
549
    }
550
    return p_mux->pf_mux( p_mux );
551 552
}

553 554 555 556 557
void sout_MuxFlush( sout_mux_t *p_mux, sout_input_t *p_input )
{
    VLC_UNUSED(p_mux);
    block_FifoEmpty( p_input->p_fifo );
}
558 559 560 561

/*****************************************************************************
 * sout_MuxGetStream: find stream to be muxed
 *****************************************************************************/
562
int sout_MuxGetStream( sout_mux_t *p_mux, unsigned i_blocks, mtime_t *pi_dts )
563 564 565 566
{
    mtime_t i_dts = 0;
    int     i_stream = -1;

567 568
    assert( i_blocks > 0 );

569 570 571 572 573
    for( int i = 0; i < p_mux->i_nb_inputs; i++ )
    {
        sout_input_t *p_input = p_mux->pp_inputs[i];
        block_t *p_data;

574
        if( block_FifoCount( p_input->p_fifo ) < i_blocks )
575
        {
576 577
            if( (!p_mux->b_add_stream_any_time) &&
                (p_input->p_fmt->i_cat != SPU_ES ) )
578 579 580 581
            {
                return -1;
            }
            /* FIXME: SPU muxing */
582
            continue;
583
        }
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598

        p_data = block_FifoShow( p_input->p_fifo );
        if( i_stream < 0 || p_data->i_dts < i_dts )
        {
            i_stream = i;
            i_dts    = p_data->i_dts;
        }
    }

    if( pi_dts ) *pi_dts = i_dts;

    return i_stream;
}


599 600 601
/*****************************************************************************
 *
 *****************************************************************************/
602
static int mrl_Parse( mrl_t *p_mrl, const char *psz_mrl )
603
{
Laurent Aimar's avatar
Laurent Aimar committed
604
    char * psz_dup = strdup( psz_mrl );
605
    char * psz_parser = psz_dup;
606 607
    const char * psz_access;
    const char * psz_way;
608
    char * psz_name;
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628

    /* *** 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++;
        }
    }
629
#if defined( _WIN32 ) || defined( __OS2__ )
630 631
    if( psz_parser - psz_dup == 1 )
    {
632 633
        /* msg_Warn( p_sout, "drive letter %c: found in source string",
                          *psz_dup ) ; */
634
        *psz_parser = '\0';
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
    }
#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
713
    free( psz_dup );
714 715 716 717 718 719 720
    return( VLC_SUCCESS );
}


/* mrl_Clean: clean p_mrl  after a call to mrl_Parse */
static void mrl_Clean( mrl_t *p_mrl )
{
721 722 723
    FREENULL( p_mrl->psz_access );
    FREENULL( p_mrl->psz_way );
    FREENULL( p_mrl->psz_name );
724 725 726
}


727 728 729 730 731 732 733 734
/****************************************************************************
 ****************************************************************************
 **
 **
 **
 ****************************************************************************
 ****************************************************************************/

735 736 737
/* Destroy a "stream_out" module */
static void sout_StreamDelete( sout_stream_t *p_stream )
{
738
    sout_instance_t *p_sout = (sout_instance_t *)(p_stream->obj.parent);
739

740
    msg_Dbg( p_stream, "destroying chain... (name=%s)", p_stream->psz_name );
741

742 743 744 745
    p_sout->i_out_pace_nocontrol -= p_stream->pace_nocontrol;

    if( p_stream->p_module != NULL )
        module_unneed( p_stream, p_stream->p_module );
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760

    FREENULL( p_stream->psz_name );

    config_ChainDestroy( p_stream->p_cfg );

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

/* Destroy a "stream_out" modules chain
 *
 * p_first is the first module to be destroyed in the chain
 * p_last is the last module to be destroyed
 *  if NULL, all modules are destroyed
 *  if not NULL, modules following it must be destroyed separately
761
 */
762 763
void sout_StreamChainDelete(sout_stream_t *p_first, sout_stream_t *p_last)
{
764 765 766
    while(p_first != NULL)
    {
        sout_stream_t *p_next = p_first->p_next;
767

768 769 770 771 772
        sout_StreamDelete(p_first);
        if(p_first == p_last)
           break;
        p_first = p_next;
    }
773
}
774

775
/* Create a "stream_out" module, which may forward its ES to p_next module */
776 777 778
/*
 * XXX name and p_cfg are used (-> do NOT free them)
 */
779 780
static sout_stream_t *sout_StreamNew( sout_instance_t *p_sout, char *psz_name,
                               config_chain_t *p_cfg, sout_stream_t *p_next)
781 782 783
{
    sout_stream_t *p_stream;

784
    assert(psz_name);
785

786
    p_stream = vlc_custom_create( p_sout, sizeof( *p_stream ), "stream out" );
787 788 789 790
    if( !p_stream )
        return NULL;

    p_stream->p_sout   = p_sout;
791 792 793
    p_stream->psz_name = psz_name;
    p_stream->p_cfg    = p_cfg;
    p_stream->p_next   = p_next;
794
    p_stream->pf_flush = NULL;
795
    p_stream->pf_control = NULL;
796 797
    p_stream->pace_nocontrol = false;
    p_stream->p_sys = NULL;
798

799 800 801
    msg_Dbg( p_sout, "stream=`%s'", p_stream->psz_name );

    p_stream->p_module =
802
        module_need( p_stream, "sout stream", p_stream->psz_name, true );
803 804 805

    if( !p_stream->p_module )
    {
806 807 808 809
        /* those must be freed by the caller if creation failed */
        p_stream->psz_name = NULL;
        p_stream->p_cfg = NULL;

810
        sout_StreamDelete( p_stream );
811 812 813
        return NULL;
    }

814
    p_sout->i_out_pace_nocontrol += p_stream->pace_nocontrol;
815 816 817
    return p_stream;
}

818 819 820 821 822 823 824 825 826 827 828
/* Creates a complete "stream_out" modules chain
 *
 *  chain format: module1{option=*:option=*}[:module2{option=*:...}]
 *
 *  The modules are created starting from the last one and linked together
 *  A pointer to the last module created is stored if pp_last isn't NULL, to
 *  make sure sout_StreamChainDelete doesn't delete modules created in another
 *  place.
 *
 *  Returns a pointer to the first module.
 */
829
sout_stream_t *sout_StreamChainNew(sout_instance_t *p_sout, const char *psz_chain,
830
                                sout_stream_t *p_next, sout_stream_t **pp_last)
831
{
832 833 834 835 836
    if(!psz_chain || !*psz_chain)
    {
        if(pp_last) *pp_last = NULL;
        return p_next;
    }
837

838 839 840
    char *psz_parser = strdup(psz_chain);
    if(!psz_parser)
        return NULL;
841

842 843 844
    vlc_array_t cfg, name;
    vlc_array_init(&cfg);
    vlc_array_init(&name);
845

846 847 848 849 850
    /* parse chain */
    while(psz_parser)
    {
        config_chain_t *p_cfg;
        char *psz_name;
851
        char *psz_rest_chain = config_ChainCreate( &psz_name, &p_cfg, psz_parser );
852
        free( psz_parser );
853
        psz_parser = psz_rest_chain;
854

855 856
        vlc_array_append_or_abort(&cfg, p_cfg);
        vlc_array_append_or_abort(&name, psz_name);
857 858
    }

859
    size_t i = vlc_array_count(&name);
860 861 862 863 864 865 866 867 868 869 870 871 872
    vlc_array_t module;
    vlc_array_init(&module);
    while(i--)
    {
        p_next = sout_StreamNew( p_sout, vlc_array_item_at_index(&name, i),
            vlc_array_item_at_index(&cfg, i), p_next);

        if(!p_next)
            goto error;

        if(i == vlc_array_count(&name) - 1 && pp_last)
            *pp_last = p_next;   /* last module created in the chain */

873
        vlc_array_append_or_abort(&module, p_next);
874 875 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
    }

    vlc_array_clear(&name);
    vlc_array_clear(&cfg);
    vlc_array_clear(&module);

    return p_next;

error:

    i++;    /* last module couldn't be created */

    /* destroy all modules created, starting with the last one */
    int modules = vlc_array_count(&module);
    while(modules--)
        sout_StreamDelete(vlc_array_item_at_index(&module, modules));
    vlc_array_clear(&module);

    /* then destroy all names and config which weren't destroyed by
     * sout_StreamDelete */
    while(i--)
    {
        free(vlc_array_item_at_index(&name, i));
        config_ChainDestroy(vlc_array_item_at_index(&cfg, i));
    }
    vlc_array_clear(&name);
    vlc_array_clear(&cfg);

    return NULL;
903 904
}

905 906
static char *sout_stream_url_to_chain( bool b_sout_display,
                                       const char *psz_url )
907
{
908
    mrl_t       mrl;
909
    char        *psz_chain;
910 911

    mrl_Parse( &mrl, psz_url );
912

913 914 915 916 917 918
    /* 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;

919
    if (strcmp (mrl.psz_access, "rtp") == 0)
920
    {
921
        char *port;
922 923
        /* For historical reasons, rtp:// means RTP over UDP */
        strcpy (mrl.psz_access, "udp");
924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
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;
941 942 943
    }
    else
    {
944 945 946 947 948
        /* 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;
949 950 951
    }

    /* Duplicate and wrap if sout-display is on */
952
    if (psz_chain && b_sout_display)
953 954
    {
        char *tmp;
Rémi Duraffort's avatar
Rémi Duraffort committed
955
        if (asprintf (&tmp, "duplicate{dst=display,dst=%s}", psz_chain) == -1)
956 957 958
            tmp = NULL;
        free (psz_chain);
        psz_chain = tmp;
959
    }
960

961
    mrl_Clean( &mrl );
962
    return psz_chain;
963
}
964 965 966 967

#undef sout_EncoderCreate
encoder_t *sout_EncoderCreate( vlc_object_t *p_this )
{
968
    return vlc_custom_create( p_this, sizeof( encoder_t ), "encoder" );
969
}