es.c 12.6 KB
Newer Older
1
/*****************************************************************************
gbazin's avatar
   
gbazin committed
2
 * es.c: Elementary stream output module
3
 *****************************************************************************
gbazin's avatar
   
gbazin committed
4
 * Copyright (C) 2003-2004 VideoLAN
5
 * $Id$
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *
 * 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
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>
#include <string.h>

#include <vlc/vlc.h>
#include <vlc/input.h>
#include <vlc/sout.h>

/*****************************************************************************
35
 * Module descriptor
36
 *****************************************************************************/
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#define ACCESS_TEXT N_("Output access method")
#define ACCESS_LONGTEXT N_( \
    "Allows you to specify the output access method used for the streaming " \
    "output." )
#define ACCESSA_TEXT N_("Audio output access method")
#define ACCESSA_LONGTEXT N_( \
    "Allows you to specify the output access method used for the audio " \
    "streaming output." )
#define ACCESSV_TEXT N_("Video output access method")
#define ACCESSV_LONGTEXT N_( \
    "Allows you to specify the output access method used for the video " \
    "streaming output." )

#define MUX_TEXT N_("Output muxer")
#define MUX_LONGTEXT N_( \
    "Allows you to specify the muxer used for the streaming output." )
#define MUXA_TEXT N_("Audio output muxer")
#define MUXA_LONGTEXT N_( \
    "Allows you to specify the muxer used for the audio streaming output." )
#define MUXV_TEXT N_("Video output muxer")
#define MUXV_LONGTEXT N_( \
    "Allows you to specify the muxer used for the video streaming output." )

#define DEST_TEXT N_("Output URL")
#define DEST_LONGTEXT N_( \
    "Allows you to specify the output URL used for the streaming output." )
#define DESTA_TEXT N_("Audio output URL")
#define DESTA_LONGTEXT N_( \
    "Allows you to specify the output URL used for the audio streaming " \
    "output." )
#define DESTV_TEXT N_("Video output URL")
#define DESTV_LONGTEXT N_( \
    "Allows you to specify the output URL used for the video streaming " \
    "output." )

72
73
74
static int      Open    ( vlc_object_t * );
static void     Close   ( vlc_object_t * );

75
#define SOUT_CFG_PREFIX "sout-es-"
76
77

vlc_module_begin();
gbazin's avatar
   
gbazin committed
78
    set_description( _("Elementary stream output") );
79
80
    set_capability( "sout stream", 50 );
    add_shortcut( "es" );
81

82
83
84
85
86
87
    add_string( SOUT_CFG_PREFIX "access", "", NULL, ACCESS_TEXT,
                ACCESS_LONGTEXT, VLC_TRUE );
    add_string( SOUT_CFG_PREFIX "access-audio", "", NULL, ACCESSA_TEXT,
                ACCESSA_LONGTEXT, VLC_TRUE );
    add_string( SOUT_CFG_PREFIX "access-video", "", NULL, ACCESSV_TEXT,
                ACCESSV_LONGTEXT, VLC_TRUE );
88

89
90
91
92
93
94
    add_string( SOUT_CFG_PREFIX "mux", "", NULL, MUX_TEXT,
                MUX_LONGTEXT, VLC_TRUE );
    add_string( SOUT_CFG_PREFIX "mux-audio", "", NULL, MUXA_TEXT,
                MUXA_LONGTEXT, VLC_TRUE );
    add_string( SOUT_CFG_PREFIX "mux-video", "", NULL, MUXV_TEXT,
                MUXV_LONGTEXT, VLC_TRUE );
95

96
97
98
99
100
101
    add_string( SOUT_CFG_PREFIX "dst", "", NULL, DEST_TEXT,
                DEST_LONGTEXT, VLC_TRUE );
    add_string( SOUT_CFG_PREFIX "dst-audio", "", NULL, DESTA_TEXT,
                DESTA_LONGTEXT, VLC_TRUE );
    add_string( SOUT_CFG_PREFIX "dst-video", "", NULL, DESTV_TEXT,
                DESTV_LONGTEXT, VLC_TRUE );
102

103
104
105
    set_callbacks( Open, Close );
vlc_module_end();

106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121

#define FREE( p ) if( p ) { free( p ); (p) = NULL; }
/*****************************************************************************
 * Exported prototypes
 *****************************************************************************/
static const char *ppsz_sout_options[] = {
    "access", "access-audio", "access-video",
    "mux", "mux-audio", "mux-video",
    "dst", "dst-audio", "dst-video",
    NULL
};

static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
static int               Del ( sout_stream_t *, sout_stream_id_t * );
static int               Send( sout_stream_t *, sout_stream_id_t *, block_t* );

122
123
124
125
126
127
128
129
130
131
132
133
134
135
struct sout_stream_sys_t
{
    int  i_count_audio;
    int  i_count_video;
    int  i_count;

    char *psz_mux;
    char *psz_mux_audio;
    char *psz_mux_video;

    char *psz_access;
    char *psz_access_audio;
    char *psz_access_video;

136
137
138
    char *psz_dst;
    char *psz_dst_audio;
    char *psz_dst_video;
139
140
141
142
143
144
145
146
147
};

/*****************************************************************************
 * Open:
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    sout_stream_t       *p_stream = (sout_stream_t*)p_this;
    sout_stream_sys_t   *p_sys;
148
    vlc_value_t         val;
149

150
    sout_ParseCfg( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options, p_stream->p_cfg );
151
152
153
154
155
156
    p_sys                   = malloc( sizeof( sout_stream_sys_t ) );

    p_sys->i_count          = 0;
    p_sys->i_count_audio    = 0;
    p_sys->i_count_video    = 0;

157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
    var_Get( p_stream, SOUT_CFG_PREFIX "access", &val );
    p_sys->psz_access       = val.psz_string;
    var_Get( p_stream, SOUT_CFG_PREFIX "access-audio", &val );
    p_sys->psz_access_audio = val.psz_string;
    var_Get( p_stream, SOUT_CFG_PREFIX "access-video", &val );
    p_sys->psz_access_video = val.psz_string;

    var_Get( p_stream, SOUT_CFG_PREFIX "mux", &val );
    p_sys->psz_mux       = val.psz_string;
    var_Get( p_stream, SOUT_CFG_PREFIX "mux-audio", &val );
    p_sys->psz_mux_audio = val.psz_string;
    var_Get( p_stream, SOUT_CFG_PREFIX "mux-video", &val );
    p_sys->psz_mux_video = val.psz_string;

    var_Get( p_stream, SOUT_CFG_PREFIX "dst", &val );
    p_sys->psz_dst       = val.psz_string;
    var_Get( p_stream, SOUT_CFG_PREFIX "dst-audio", &val );
    p_sys->psz_dst_audio = val.psz_string;
    var_Get( p_stream, SOUT_CFG_PREFIX "dst-video", &val );
    p_sys->psz_dst_video = val.psz_string;
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

    p_stream->pf_add    = Add;
    p_stream->pf_del    = Del;
    p_stream->pf_send   = Send;

    p_stream->p_sys     = p_sys;

    return VLC_SUCCESS;
}

/*****************************************************************************
 * Close:
 *****************************************************************************/

static void Close( vlc_object_t * p_this )
{
    sout_stream_t     *p_stream = (sout_stream_t*)p_this;
    sout_stream_sys_t *p_sys = p_stream->p_sys;

196
197
198
199
200
201
202
203
204
205
206
207
    free( p_sys->psz_access );
    free( p_sys->psz_access_audio );
    free( p_sys->psz_access_video );

    free( p_sys->psz_mux );
    free( p_sys->psz_mux_audio );
    free( p_sys->psz_mux_video );

    free( p_sys->psz_dst );
    free( p_sys->psz_dst_audio );
    free( p_sys->psz_dst_video );

208
209
210
211
212
213
214
215
216
    free( p_sys );
}

struct sout_stream_id_t
{
    sout_input_t *p_input;
    sout_mux_t   *p_mux;
};

gbazin's avatar
   
gbazin committed
217
218
static char * es_print_url( char *psz_fmt, vlc_fourcc_t i_fourcc, int i_count,
                            char *psz_access, char *psz_mux )
219
{
220
    char *psz_dst, *p;
221
222
223
224
225
226

    if( psz_fmt == NULL || !*psz_fmt )
    {
        psz_fmt = "stream-%n-%c.%m";
    }

227
    p = psz_dst = malloc( 4096 );
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
    memset( p, 0, 4096 );
    for( ;; )
    {
        if( *psz_fmt == '\0' )
        {
            *p = '\0';
            break;
        }

        if( *psz_fmt != '%' )
        {
            *p++ = *psz_fmt++;
        }
        else
        {
            if( psz_fmt[1] == 'n' )
            {
                p += sprintf( p, "%d", i_count );
            }
            else if( psz_fmt[1] == 'c' )
            {
                p += sprintf( p, "%4.4s", (char*)&i_fourcc );
            }
            else if( psz_fmt[1] == 'm' )
            {
                p += sprintf( p, "%s", psz_mux );
            }
            else if( psz_fmt[1] == 'a' )
            {
                p += sprintf( p, "%s", psz_access );
            }
            else if( psz_fmt[1] != '\0' )
            {
                p += sprintf( p, "%c%c", psz_fmt[0], psz_fmt[1] );
            }
            else
            {
                p += sprintf( p, "%c", psz_fmt[0] );
                *p++ = '\0';
                break;
            }
            psz_fmt += 2;
        }
    }

273
    return( psz_dst );
274
275
}

gbazin's avatar
   
gbazin committed
276
static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
277
278
279
280
281
282
283
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    sout_instance_t   *p_sout = p_stream->p_sout;
    sout_stream_id_t  *id;

    char              *psz_access;
    char              *psz_mux;
284
    char              *psz_dst;
285
286
287
288
289

    sout_access_out_t *p_access;
    sout_mux_t        *p_mux;

    /* *** get access name *** */
290
    if( p_fmt->i_cat == AUDIO_ES && p_sys->psz_access_audio && *p_sys->psz_access_audio )
291
292
293
    {
        psz_access = p_sys->psz_access_audio;
    }
294
    else if( p_fmt->i_cat == VIDEO_ES && p_sys->psz_access_video && *p_sys->psz_access_video )
295
296
297
298
299
300
301
302
303
    {
        psz_access = p_sys->psz_access_video;
    }
    else
    {
        psz_access = p_sys->psz_access;
    }

    /* *** get mux name *** */
304
    if( p_fmt->i_cat == AUDIO_ES && p_sys->psz_mux_audio && *p_sys->psz_mux_audio )
305
306
307
    {
        psz_mux = p_sys->psz_mux_audio;
    }
308
    else if( p_fmt->i_cat == VIDEO_ES && p_sys->psz_mux_video && *p_sys->psz_mux_video )
309
310
311
312
313
314
315
316
    {
        psz_mux = p_sys->psz_mux_video;
    }
    else
    {
        psz_mux = p_sys->psz_mux;
    }

gbazin's avatar
   
gbazin committed
317
    /* Get url (%d expanded as a codec count, %c expanded as codec fcc ) */
318
    if( p_fmt->i_cat == AUDIO_ES && p_sys->psz_dst_audio && *p_sys->psz_dst_audio )
319
    {
320
        psz_dst = es_print_url( p_sys->psz_dst_audio, p_fmt->i_codec,
gbazin's avatar
   
gbazin committed
321
                                p_sys->i_count_audio, psz_access, psz_mux );
322
    }
323
    else if( p_fmt->i_cat == VIDEO_ES && p_sys->psz_dst_video && *p_sys->psz_dst_video )
324
    {
325
        psz_dst = es_print_url( p_sys->psz_dst_video, p_fmt->i_codec,
gbazin's avatar
   
gbazin committed
326
                                p_sys->i_count_video, psz_access, psz_mux );
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
    }
    else
    {
        int i_count;
        if( p_fmt->i_cat == VIDEO_ES )
        {
            i_count = p_sys->i_count_video;
        }
        else if( p_fmt->i_cat == AUDIO_ES )
        {
            i_count = p_sys->i_count_audio;
        }
        else
        {
            i_count = p_sys->i_count;
        }

344
        psz_dst = es_print_url( p_sys->psz_dst, p_fmt->i_codec,
gbazin's avatar
   
gbazin committed
345
                                i_count, psz_access, psz_mux );
346
347
348
349
350
351
352
353
354
355
356
357
    }

    p_sys->i_count++;
    if( p_fmt->i_cat == VIDEO_ES )
    {
        p_sys->i_count_video++;
    }
    else if( p_fmt->i_cat == AUDIO_ES )
    {
        p_sys->i_count_audio++;
    }
    msg_Dbg( p_stream, "creating `%s/%s://%s'",
358
             psz_access, psz_mux, psz_dst );
359
360

    /* *** find and open appropriate access module *** */
361
    p_access = sout_AccessOutNew( p_sout, psz_access, psz_dst );
362
363
364
    if( p_access == NULL )
    {
        msg_Err( p_stream, "no suitable sout access module for `%s/%s://%s'",
365
                 psz_access, psz_mux, psz_dst );
366
367
368
369
370
371
372
373
        return( NULL );
    }

    /* *** find and open appropriate mux module *** */
    p_mux = sout_MuxNew( p_sout, psz_mux, p_access );
    if( p_mux == NULL )
    {
        msg_Err( p_stream, "no suitable sout mux module for `%s/%s://%s'",
374
                 psz_access, psz_mux, psz_dst );
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
        sout_AccessOutDelete( p_access );
        return( NULL );
    }

    id = malloc( sizeof( sout_stream_id_t ) );
    id->p_mux = p_mux;
    id->p_input = sout_MuxAddStream( p_mux, p_fmt );

    if( id->p_input == NULL )
    {
        free( id );

        sout_MuxDelete( p_mux );
        sout_AccessOutDelete( p_access );
        free( id );
        return NULL;
    }

    return id;
}

gbazin's avatar
   
gbazin committed
396
static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
397
398
399
400
401
402
403
404
405
406
{
    sout_access_out_t *p_access = id->p_mux->p_access;

    sout_MuxDeleteStream( id->p_mux, id->p_input );
    sout_AccessOutDelete( p_access );

    free( id );
    return VLC_SUCCESS;
}

gbazin's avatar
   
gbazin committed
407
static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
408
                 block_t *p_buffer )
409
410
411
412
413
414
{
    sout_MuxSendBuffer( id->p_mux, id->p_input, p_buffer );

    return VLC_SUCCESS;
}