stream_output.c 25.4 KB
Newer Older
1
2
3
/*****************************************************************************
 * stream_output.c : stream output module
 *****************************************************************************
4
 * Copyright (C) 2002-2007 the VideoLAN team
5
 * $Id$
6
7
 *
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8
 *          Laurent Aimar <fenrir@via.ecp.fr>
9
 *          Eric Petit <titer@videolan.org>
10
11
12
13
14
15
16
17
18
19
20
21
22
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24
25
26
27
28
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
29

30
31
32
33
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

34
#include <vlc_common.h>
35

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

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

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

#include <vlc_meta.h>
45
#include <vlc_block.h>
46
#include <vlc_codec.h>
zorglub's avatar
zorglub committed
47

48
#include "input/input_interface.h"
49

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

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

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

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

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

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

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

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

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

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

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

zorglub's avatar
zorglub committed
119
120
        FREENULL( p_sout->psz_sout );
        FREENULL( p_sout->psz_chain );
121

122
        vlc_object_detach( p_sout );
123
        vlc_object_release( p_sout );
124
        return NULL;
125
126
127
128
    }

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

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

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

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

148
149
    vlc_mutex_destroy( &p_sout->lock );

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

154
155
156
157
158
/*****************************************************************************
 * 
 *****************************************************************************/
void sout_UpdateStatistic( sout_instance_t *p_sout, sout_statistic_t i_type, int i_delta )
{
159
    if( !libvlc_stats( p_sout ) )
160
161
        return;

162
163
164
    /* */
    input_thread_t *p_input = vlc_object_find( p_sout, VLC_OBJECT_INPUT, FIND_PARENT );
    if( !p_input )
165
166
        return;

167
    int i_input_type;
168
169
170
    switch( i_type )
    {
    case SOUT_STATISTIC_DECODED_VIDEO:
171
        i_input_type = SOUT_STATISTIC_DECODED_VIDEO;
172
173
        break;
    case SOUT_STATISTIC_DECODED_AUDIO:
174
        i_input_type = SOUT_STATISTIC_DECODED_AUDIO;
175
176
        break;
    case SOUT_STATISTIC_DECODED_SUBTITLE:
177
        i_input_type = SOUT_STATISTIC_DECODED_SUBTITLE;
178
179
180
        break;

    case SOUT_STATISTIC_SENT_PACKET:
181
        i_input_type = SOUT_STATISTIC_SENT_PACKET;
182
        break;
183

184
    case SOUT_STATISTIC_SENT_BYTE:
185
        i_input_type = SOUT_STATISTIC_SENT_BYTE;
186
187
188
        break;

    default:
189
190
191
        msg_Err( p_sout, "Not yet supported statistic type %d", i_type );
        vlc_object_release( p_input );
        return;
192
    }
193
194
195

    input_UpdateStatistic( p_input, i_input_type, i_delta );

196
197
    vlc_object_release( p_input );
}
198
/*****************************************************************************
199
 * Packetizer/Input
200
 *****************************************************************************/
201
202
sout_packetizer_input_t *sout_InputNew( sout_instance_t *p_sout,
                                        es_format_t *p_fmt )
203
{
204
    sout_packetizer_input_t *p_input;
205

206
207
    /* *** create a packetizer input *** */
    p_input         = malloc( sizeof( sout_packetizer_input_t ) );
Rafaël Carré's avatar
Rafaël Carré committed
208
    if( !p_input )  return NULL;
209
210
    p_input->p_sout = p_sout;
    p_input->p_fmt  = p_fmt;
211

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

214
    if( p_fmt->i_codec == VLC_FOURCC( 'n', 'u', 'l', 'l' ) )
215
216
217
    {
        vlc_object_release( p_sout );
        return p_input;
218
219
    }

220
    /* *** add it to the stream chain */
221
    vlc_mutex_lock( &p_sout->lock );
222
    p_input->id = p_sout->p_stream->pf_add( p_sout->p_stream, p_fmt );
223
    vlc_mutex_unlock( &p_sout->lock );
224

225
    if( p_input->id == NULL )
226
    {
227
        free( p_input );
228
        return NULL;
229
    }
230

231
    return( p_input );
232
}
233

234
235
236
/*****************************************************************************
 *
 *****************************************************************************/
237
int sout_InputDelete( sout_packetizer_input_t *p_input )
238
{
239
240
    sout_instance_t     *p_sout = p_input->p_sout;

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

243
    if( p_input->p_fmt->i_codec != VLC_FOURCC( 'n', 'u', 'l', 'l' ) )
244
    {
245
246
247
        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 );
248
    }
249

250
    free( p_input );
251

252
253
    return( VLC_SUCCESS);
}
254

255
256
257
/*****************************************************************************
 *
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
258
int sout_InputSendBuffer( sout_packetizer_input_t *p_input,
259
                          block_t *p_buffer )
260
261
{
    sout_instance_t     *p_sout = p_input->p_sout;
262
    int                 i_ret;
263

264
    if( p_input->p_fmt->i_codec == VLC_FOURCC( 'n', 'u', 'l', 'l' ) )
265
    {
266
        block_Release( p_buffer );
267
268
        return VLC_SUCCESS;
    }
269
    if( p_buffer->i_dts <= 0 )
gbazin's avatar
   
gbazin committed
270
271
    {
        msg_Warn( p_sout, "trying to send non-dated packet to stream output!");
272
        block_Release( p_buffer );
gbazin's avatar
   
gbazin committed
273
274
275
        return VLC_SUCCESS;
    }

276
    vlc_mutex_lock( &p_sout->lock );
gbazin's avatar
   
gbazin committed
277
278
    i_ret = p_sout->p_stream->pf_send( p_sout->p_stream,
                                       p_input->id, p_buffer );
279
    vlc_mutex_unlock( &p_sout->lock );
280

281
    return i_ret;
282
}
283

284
#undef sout_AccessOutNew
285
286
287
/*****************************************************************************
 * sout_AccessOutNew: allocate a new access out
 *****************************************************************************/
288
sout_access_out_t *sout_AccessOutNew( vlc_object_t *p_sout,
289
                                      const char *psz_access, const char *psz_name )
290
{
291
    static const char typename[] = "access out";
292
    sout_access_out_t *p_access;
293
    char              *psz_next;
294

295
296
297
    p_access = vlc_custom_create( p_sout, sizeof( *p_access ),
                                  VLC_OBJECT_GENERIC, typename );
    if( !p_access )
298
        return NULL;
299

300
301
    psz_next = config_ChainCreate( &p_access->psz_access, &p_access->p_cfg,
                                   psz_access );
302
    free( psz_next );
303
    p_access->psz_path   = strdup( psz_name ? psz_name : "" );
304
    p_access->p_sys      = NULL;
305
    p_access->pf_seek    = NULL;
gbazin's avatar
   
gbazin committed
306
    p_access->pf_read    = NULL;
307
    p_access->pf_write   = NULL;
308
    p_access->pf_control = NULL;
309
    p_access->p_module   = NULL;
310
311
312
313

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

314
    vlc_object_attach( p_access, p_sout );
315

316
    p_access->p_module   =
317
        module_need( p_access, "sout access", p_access->psz_access, true );
318
319
320

    if( !p_access->p_module )
    {
321
        free( p_access->psz_access );
322
        free( p_access->psz_path );
323
        vlc_object_detach( p_access );
324
        vlc_object_release( p_access );
325
        return( NULL );
326
327
328
329
330
331
332
    }

    return p_access;
}
/*****************************************************************************
 * sout_AccessDelete: delete an access out
 *****************************************************************************/
333
void sout_AccessOutDelete( sout_access_out_t *p_access )
334
{
335
    vlc_object_detach( p_access );
336
337
    if( p_access->p_module )
    {
338
        module_unneed( p_access, p_access->p_module );
339
340
    }
    free( p_access->psz_access );
341

342
    config_ChainDestroy( p_access->p_cfg );
343

344
    free( p_access->psz_path );
345

346
    vlc_object_release( p_access );
347
348
349
350
351
}

/*****************************************************************************
 * sout_AccessSeek:
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
352
int sout_AccessOutSeek( sout_access_out_t *p_access, off_t i_pos )
353
{
gbazin's avatar
   
gbazin committed
354
    return p_access->pf_seek( p_access, i_pos );
355
356
357
}

/*****************************************************************************
gbazin's avatar
   
gbazin committed
358
 * sout_AccessRead:
359
 *****************************************************************************/
360
ssize_t sout_AccessOutRead( sout_access_out_t *p_access, block_t *p_buffer )
361
{
362
363
    return( p_access->pf_read ?
            p_access->pf_read( p_access, p_buffer ) : VLC_EGENERIC );
364
365
}

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

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

402
/*****************************************************************************
403
 * sout_MuxNew: create a new mux
404
 *****************************************************************************/
405
406
sout_mux_t * sout_MuxNew( sout_instance_t *p_sout, char *psz_mux,
                          sout_access_out_t *p_access )
407
{
408
    static const char typename[] = "mux";
409
    sout_mux_t *p_mux;
410
    char       *psz_next;
411

412
413
    p_mux = vlc_custom_create( p_sout, sizeof( *p_mux ), VLC_OBJECT_GENERIC,
                               typename);
414
415
416
    if( p_mux == NULL )
        return NULL;

gbazin's avatar
gbazin committed
417
    p_mux->p_sout = p_sout;
418
    psz_next = config_ChainCreate( &p_mux->psz_mux, &p_mux->p_cfg, psz_mux );
419
    free( psz_next );
gbazin's avatar
gbazin committed
420

421
    p_mux->p_access     = p_access;
422
    p_mux->pf_control   = NULL;
423
424
425
426
427
428
429
    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
430
431
    p_mux->p_module     = NULL;

432
433
    p_mux->b_add_stream_any_time = false;
    p_mux->b_waiting_stream = true;
gbazin's avatar
gbazin committed
434
    p_mux->i_add_stream_start = -1;
435
436

    vlc_object_attach( p_mux, p_sout );
437

gbazin's avatar
gbazin committed
438
    p_mux->p_module =
439
        module_need( p_mux, "sout mux", p_mux->psz_mux, true );
440

441
442
    if( p_mux->p_module == NULL )
    {
zorglub's avatar
zorglub committed
443
        FREENULL( p_mux->psz_mux );
444

445
        vlc_object_detach( p_mux );
446
        vlc_object_release( p_mux );
447
448
449
450
        return NULL;
    }

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

        if( sout_MuxControl( p_mux, MUX_CAN_ADD_STREAM_WHILE_MUXING,
                             &b_answer ) )
457
        {
458
            b_answer = false;
459
        }
gbazin's avatar
gbazin committed
460

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

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

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

    return p_mux;
}

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

503
    config_ChainDestroy( p_mux->p_cfg );
504

505
    vlc_object_release( p_mux );
506
507
}

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

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

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

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

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

543
    return p_input;
544
545
}

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

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

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

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

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

578
        block_FifoRelease( p_input->p_fifo );
579
        free( p_input );
580
581
582
    }
}

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

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

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

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

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

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

    /* *** 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 )
    {
648
649
        /* msg_Warn( p_sout, "drive letter %c: found in source string",
                          *psz_dup ) ; */
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
        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
729
    free( psz_dup );
730
731
732
733
734
735
736
    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
737
738
739
    FREENULL( p_mrl->psz_access );
    FREENULL( p_mrl->psz_way );
    FREENULL( p_mrl->psz_name );
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
/****************************************************************************
 ****************************************************************************
 **
 **
 **
 ****************************************************************************
 ****************************************************************************/

/* 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)
 */
765
sout_stream_t *sout_StreamNew( sout_instance_t *p_sout, char *psz_chain )
766
{
767
    static const char typename[] = "stream out";
768
769
    sout_stream_t *p_stream;

770
771
772
773
774
775
    if( !psz_chain )
    {
        msg_Err( p_sout, "invalid chain" );
        return NULL;
    }

776
777
    p_stream = vlc_custom_create( p_sout, sizeof( *p_stream ),
                                  VLC_OBJECT_GENERIC, typename );
778
779
780
781
782
783
    if( !p_stream )
        return NULL;

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

gbazin's avatar
   
gbazin committed
784
    p_stream->psz_next =
785
        config_ChainCreate( &p_stream->psz_name, &p_stream->p_cfg, psz_chain);
gbazin's avatar
   
gbazin committed
786

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

789
790
    vlc_object_attach( p_stream, p_sout );

791
    p_stream->p_module =
792
        module_need( p_stream, "sout stream", p_stream->psz_name, true );
793
794
795

    if( !p_stream->p_module )
    {
796
        sout_StreamDelete( p_stream );
797
798
799
800
801
802
        return NULL;
    }

    return p_stream;
}

803
void sout_StreamDelete( sout_stream_t *p_stream )
804
805
{
    msg_Dbg( p_stream, "destroying chain... (name=%s)", p_stream->psz_name );
806
807

    vlc_object_detach( p_stream );
808
    if( p_stream->p_module ) module_unneed( p_stream, p_stream->p_module );
809

zorglub's avatar
zorglub committed
810
811
    FREENULL( p_stream->psz_name );
    FREENULL( p_stream->psz_next );
812

813
    config_ChainDestroy( p_stream->p_cfg );
814
815

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

819
820
static char *_sout_stream_url_to_chain( vlc_object_t *p_this,
                                        const char *psz_url )
821
{
822
    mrl_t       mrl;
823
    char        *psz_chain;
824
825

    mrl_Parse( &mrl, psz_url );
826

827
828
829
830
831
832
    /* 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;

833
    if (strcmp (mrl.psz_access, "rtp") == 0)
834
    {
835
        char *port;
836
837
        /* For historical reasons, rtp:// means RTP over UDP */
        strcpy (mrl.psz_access, "udp");
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
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;
855
856
857
    }
    else
    {
858
859
860
861
862
        /* 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;
863
864
865
866
867
868
869
870
871
872
    }

    /* 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;
873
    }
874

875
    mrl_Clean( &mrl );
876
    return psz_chain;
877
}
878
879
880
881
882
883
884
885

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