stream_output.c 28.9 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
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

zorglub's avatar
zorglub committed
42
#include <vlc_sout.h>
43

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

#include <vlc_meta.h>
47
#include <vlc_block.h>
48
#include <vlc_codec.h>
zorglub's avatar
zorglub committed
49

50
#include "input/input_interface.h"
51

Laurent Aimar's avatar
Laurent Aimar committed
52
53
#define VLC_CODEC_NULL VLC_FOURCC( 'n', 'u', 'l', 'l' )

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

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

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

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

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

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

98
    /* *** Allocate descriptor *** */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
99
100
    p_sout = vlc_custom_create( p_parent, sizeof( *p_sout ),
                                VLC_OBJECT_GENERIC, typename );
101
102
103
    if( p_sout == NULL )
        return NULL;

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

106
107
    /* *** init descriptor *** */
    p_sout->psz_sout    = strdup( psz_dest );
108
    p_sout->p_meta      = NULL;
109
    p_sout->i_out_pace_nocontrol = 0;
110
    p_sout->p_sys       = NULL;
111

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

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

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

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

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

130
    FREENULL( p_sout->psz_sout );
131

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 *** */
zorglub's avatar
zorglub committed
145
    FREENULL( p_sout->psz_sout );
146

147
    /* delete meta */
148
149
150
151
152
    if( p_sout->p_meta )
    {
        vlc_meta_Delete( p_sout->p_meta );
    }

153
154
    vlc_mutex_destroy( &p_sout->lock );

155
    /* *** free structure *** */
156
    vlc_object_release( p_sout );
157
158
}

159
160
161
162
163
/*****************************************************************************
 * 
 *****************************************************************************/
void sout_UpdateStatistic( sout_instance_t *p_sout, sout_statistic_t i_type, int i_delta )
{
164
    if( !libvlc_stats( p_sout ) )
165
166
        return;

167
168
169
    /* */
    input_thread_t *p_input = vlc_object_find( p_sout, VLC_OBJECT_INPUT, FIND_PARENT );
    if( !p_input )
170
171
        return;

172
    int i_input_type;
173
174
175
    switch( i_type )
    {
    case SOUT_STATISTIC_DECODED_VIDEO:
176
        i_input_type = SOUT_STATISTIC_DECODED_VIDEO;
177
178
        break;
    case SOUT_STATISTIC_DECODED_AUDIO:
179
        i_input_type = SOUT_STATISTIC_DECODED_AUDIO;
180
181
        break;
    case SOUT_STATISTIC_DECODED_SUBTITLE:
182
        i_input_type = SOUT_STATISTIC_DECODED_SUBTITLE;
183
184
185
        break;

    case SOUT_STATISTIC_SENT_PACKET:
186
        i_input_type = SOUT_STATISTIC_SENT_PACKET;
187
        break;
188

189
    case SOUT_STATISTIC_SENT_BYTE:
190
        i_input_type = SOUT_STATISTIC_SENT_BYTE;
191
192
193
        break;

    default:
194
195
196
        msg_Err( p_sout, "Not yet supported statistic type %d", i_type );
        vlc_object_release( p_input );
        return;
197
    }
198
199
200

    input_UpdateStatistic( p_input, i_input_type, i_delta );

201
202
    vlc_object_release( p_input );
}
203
/*****************************************************************************
204
 * Packetizer/Input
205
 *****************************************************************************/
206
207
sout_packetizer_input_t *sout_InputNew( sout_instance_t *p_sout,
                                        es_format_t *p_fmt )
208
{
209
    sout_packetizer_input_t *p_input;
210

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

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

Laurent Aimar's avatar
Laurent Aimar committed
219
    if( p_fmt->i_codec == VLC_CODEC_NULL )
220
221
222
    {
        vlc_object_release( p_sout );
        return p_input;
223
224
    }

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

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

236
    return( p_input );
237
}
238

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

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

Laurent Aimar's avatar
Laurent Aimar committed
248
    if( p_input->p_fmt->i_codec != VLC_CODEC_NULL )
249
    {
250
251
252
        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 );
253
    }
254

255
    free( p_input );
256

257
258
    return( VLC_SUCCESS);
}
259

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

Laurent Aimar's avatar
Laurent Aimar committed
269
    if( p_input->p_fmt->i_codec == VLC_CODEC_NULL )
270
    {
271
        block_Release( p_buffer );
272
273
        return VLC_SUCCESS;
    }
274
275

    if( p_buffer->i_dts <= VLC_TS_INVALID )
gbazin's avatar
   
gbazin committed
276
277
    {
        msg_Warn( p_sout, "trying to send non-dated packet to stream output!");
278
        block_Release( p_buffer );
gbazin's avatar
   
gbazin committed
279
280
281
        return VLC_SUCCESS;
    }

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

287
    return i_ret;
288
}
289

290
#undef sout_AccessOutNew
291
292
293
/*****************************************************************************
 * sout_AccessOutNew: allocate a new access out
 *****************************************************************************/
294
sout_access_out_t *sout_AccessOutNew( vlc_object_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_sys      = NULL;
311
    p_access->pf_seek    = NULL;
gbazin's avatar
   
gbazin committed
312
    p_access->pf_read    = NULL;
313
    p_access->pf_write   = NULL;
314
    p_access->pf_control = NULL;
315
    p_access->p_module   = NULL;
316
317
318
319

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

320
    vlc_object_attach( p_access, p_sout );
321

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

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

    return p_access;
}
/*****************************************************************************
 * sout_AccessDelete: delete an access out
 *****************************************************************************/
338
void sout_AccessOutDelete( sout_access_out_t *p_access )
339
340
341
{
    if( p_access->p_module )
    {
342
        module_unneed( p_access, p_access->p_module );
343
344
    }
    free( p_access->psz_access );
345

346
    config_ChainDestroy( p_access->p_cfg );
347

348
    free( p_access->psz_path );
349

350
    vlc_object_release( p_access );
351
352
353
354
355
}

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

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

gbazin's avatar
   
gbazin committed
370
371
372
/*****************************************************************************
 * sout_AccessWrite:
 *****************************************************************************/
373
ssize_t sout_AccessOutWrite( sout_access_out_t *p_access, block_t *p_buffer )
gbazin's avatar
   
gbazin committed
374
{
375
#if 0
376
    const unsigned i_packets_gather = 30;
377
378
    p_access->i_writes++;
    p_access->i_sent_bytes += p_buffer->i_buffer;
379
    if( (p_access->i_writes % i_packets_gather) == 0 )
zorglub's avatar
zorglub committed
380
    {
381
382
383
        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
384
    }
385
#endif
gbazin's avatar
   
gbazin committed
386
387
    return p_access->pf_write( p_access, p_buffer );
}
388

389
390
391
/**
 * sout_AccessOutControl
 */
392
int sout_AccessOutControl (sout_access_out_t *access, int query, ...)
393
{
394
395
396
397
398
399
400
401
402
403
    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;
404
405
}

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

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

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

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

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

    vlc_object_attach( p_mux, p_sout );
441

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

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

449
        vlc_object_release( p_mux );
450
451
452
453
        return NULL;
    }

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

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

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

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

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

    return p_mux;
}

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

505
    config_ChainDestroy( p_mux->p_cfg );
506

507
    vlc_object_release( p_mux );
508
509
}

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

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

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

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

    TAB_APPEND( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input );
    if( p_mux->pf_addstream( p_mux, p_input ) < 0 )
    {
538
539
540
541
542
        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;
543
544
    }

545
    return p_input;
546
547
}

548
549
550
551
/*****************************************************************************
 * sout_MuxDeleteStream:
 *****************************************************************************/
void sout_MuxDeleteStream( sout_mux_t *p_mux, sout_input_t *p_input )
552
553
554
{
    int i_index;

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

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

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

        if( p_mux->i_nb_inputs == 0 )
        {
577
            msg_Warn( p_mux, "no more input streams for this mux" );
578
579
        }

580
        block_FifoRelease( p_input->p_fifo );
581
        free( p_input );
582
583
584
    }
}

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

593
594
595
596
    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
597
            msg_Warn( p_mux, "late buffer for mux input (%"PRId64")",
598
599
600
                      current_date - p_buffer->i_dts );
    }

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

gbazin's avatar
gbazin committed
605
606
607
        if( p_mux->i_add_stream_start < 0 )
            p_mux->i_add_stream_start = p_buffer->i_dts;

608
609
610
        /* 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 )
611
            return;
612
        p_mux->b_waiting_stream = false;
613
614
615
616
    }
    p_mux->pf_mux( p_mux );
}

617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654

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

    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;

        if( block_FifoCount( p_input->p_fifo ) < i_blocks )
        {
            if( p_input->p_fmt->i_cat != SPU_ES )
            {
                return -1;
            }
            /* FIXME: SPU muxing */
            continue;
        }

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


655
656
657
/*****************************************************************************
 *
 *****************************************************************************/
658
static int mrl_Parse( mrl_t *p_mrl, const char *psz_mrl )
659
{
Laurent Aimar's avatar
Laurent Aimar committed
660
    char * psz_dup = strdup( psz_mrl );
661
    char * psz_parser = psz_dup;
zorglub's avatar
zorglub committed
662
663
    const char * psz_access;
    const char * psz_way;
664
    char * psz_name;
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687

    /* *** 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 )
    {
688
689
        /* msg_Warn( p_sout, "drive letter %c: found in source string",
                          *psz_dup ) ; */
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
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
        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
769
    free( psz_dup );
770
771
772
773
774
775
776
    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
777
778
779
    FREENULL( p_mrl->psz_access );
    FREENULL( p_mrl->psz_way );
    FREENULL( p_mrl->psz_name );
780
781
782
}


783
784
785
786
787
788
789
790
/****************************************************************************
 ****************************************************************************
 **
 **
 **
 ****************************************************************************
 ****************************************************************************/

791
792
793
794
/* Destroy a "stream_out" module */
static void sout_StreamDelete( sout_stream_t *p_stream )
{
    msg_Dbg( p_stream, "destroying chain... (name=%s)", p_stream->psz_name );
795

796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
    if( p_stream->p_module ) module_unneed( p_stream, p_stream->p_module );

    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
812
 */
813
814
815
816
817
void sout_StreamChainDelete(sout_stream_t *p_first, sout_stream_t *p_last)
{
    if(!p_first)
        return;

818
    sout_stream_t *p_next = p_first->p_next;
819
820

    sout_StreamDelete(p_first);
821
822
    if(p_first != p_last)
        sout_StreamChainDelete(p_next, p_last);
823
}
824

825
/* Create a "stream_out" module, which may forward its ES to p_next module */
826
827
828
/*
 * XXX name and p_cfg are used (-> do NOT free them)
 */
829
830
static sout_stream_t *sout_StreamNew( sout_instance_t *p_sout, char *psz_name,
                               config_chain_t *p_cfg, sout_stream_t *p_next)
831
{
832
    static const char typename[] = "stream out";
833
834
    sout_stream_t *p_stream;

835
    assert(psz_name);
836

837
838
    p_stream = vlc_custom_create( p_sout, sizeof( *p_stream ),
                                  VLC_OBJECT_GENERIC, typename );
839
840
841
842
843
    if( !p_stream )
        return NULL;

    p_stream->p_sout   = p_sout;
    p_stream->p_sys    = NULL;
844
845
846
    p_stream->psz_name = psz_name;
    p_stream->p_cfg    = p_cfg;
    p_stream->p_next   = p_next;
gbazin's avatar
   
gbazin committed
847

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

850
851
    vlc_object_attach( p_stream, p_sout );

852
    p_stream->p_module =
853
        module_need( p_stream, "sout stream", p_stream->psz_name, true );
854
855
856

    if( !p_stream->p_module )
    {
857
858
859
860
        /* those must be freed by the caller if creation failed */
        p_stream->psz_name = NULL;
        p_stream->p_cfg = NULL;

861
        sout_StreamDelete( p_stream );
862
863
864
865
866
867
        return NULL;
    }

    return p_stream;
}

868
869
870
871
872
873
874
875
876
877
878
879
880
/* 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.
 */
sout_stream_t *sout_StreamChainNew(sout_instance_t *p_sout, char *psz_chain,
                                sout_stream_t *p_next, sout_stream_t **pp_last)
881
{
882
883
884
885
886
    if(!psz_chain || !*psz_chain)
    {
        if(pp_last) *pp_last = NULL;
        return p_next;
    }
887

888
889
890
    char *psz_parser = strdup(psz_chain);
    if(!psz_parser)
        return NULL;
891

892
893
894
    vlc_array_t cfg, name;
    vlc_array_init(&cfg);
    vlc_array_init(&name);
895

896
897
898
899
900
901
902
903
    /* parse chain */
    while(psz_parser)
    {
        config_chain_t *p_cfg;
        char *psz_name;
        psz_chain = config_ChainCreate( &psz_name, &p_cfg, psz_parser );
        free( psz_parser );
        psz_parser = psz_chain;
904

905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
        vlc_array_append(&cfg, p_cfg);
        vlc_array_append(&name, psz_name);
    }

    int i = vlc_array_count(&name);
    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 */

        vlc_array_append(&module, p_next);
    }

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

955
956
static char *sout_stream_url_to_chain( bool b_sout_display,
                                       const char *psz_url )
957
{
958
    mrl_t       mrl;
959
    char        *psz_chain;
960
961

    mrl_Parse( &mrl, psz_url );
962

963
964
965
966
967
968
    /* 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;

969
    if (strcmp (mrl.psz_access, "rtp") == 0)
970
    {
971
        char *port;
972
973
        /* For historical reasons, rtp:// means RTP over UDP */
        strcpy (mrl.psz_access, "udp");
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
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;
991
992
993
    }
    else
    {
994
995
996
997
998
        /* 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;
999
1000
1001
    }

    /* Duplicate and wrap if sout-display is on */
1002
    if (psz_chain && b_sout_display)
1003
1004
    {
        char *tmp;
ivoire's avatar
ivoire committed
1005
        if (asprintf (&tmp, "duplicate{dst=display,dst=%s}", psz_chain) == -1)
1006
1007
1008
            tmp = NULL;
        free (psz_chain);
        psz_chain = tmp;
1009
    }
1010

1011
    mrl_Clean( &mrl );
1012
    return psz_chain;
1013
}
1014
1015
1016
1017
1018
1019
1020
1021

#undef sout_EncoderCreate
encoder_t *sout_EncoderCreate( vlc_object_t *p_this )
{
    static const char type[] = "encoder";
    return vlc_custom_create( p_this, sizeof( encoder_t ), VLC_OBJECT_GENERIC,
                              type );
}