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

/*****************************************************************************
 * Preamble
 *****************************************************************************/

28 29 30 31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

32
#include <vlc_common.h>
33
#include <vlc_plugin.h>
Clément Stenac's avatar
Clément Stenac committed
34 35
#include <vlc_sout.h>
#include <vlc_block.h>
36 37

/*****************************************************************************
38
 * Module descriptor
39 40 41 42
 *****************************************************************************/
static int      Open    ( vlc_object_t * );
static void     Close   ( vlc_object_t * );

43 44 45
vlc_module_begin ()
    set_description( N_("Duplicate stream output") )
    set_capability( "sout stream", 50 )
46
    add_shortcut( "duplicate", "dup" )
47 48 49 50
    set_category( CAT_SOUT )
    set_subcategory( SUBCAT_SOUT_STREAM )
    set_callbacks( Open, Close )
vlc_module_end ()
51

52 53 54 55 56 57 58 59 60

/*****************************************************************************
 * Exported prototypes
 *****************************************************************************/
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* );

61 62 63 64
struct sout_stream_sys_t
{
    int             i_nb_streams;
    sout_stream_t   **pp_streams;
65

66 67 68
    int             i_nb_last_streams;
    sout_stream_t   **pp_last_streams;

69 70
    int             i_nb_select;
    char            **ppsz_select;
71 72 73 74 75 76 77 78
};

struct sout_stream_id_t
{
    int                 i_nb_ids;
    void                **pp_ids;
};

79
static bool ESSelected( es_format_t *fmt, char *psz_select );
80

81 82 83 84 85 86 87
/*****************************************************************************
 * 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;
88
    config_chain_t        *p_cfg;
89

90
    msg_Dbg( p_stream, "creating 'duplicate'" );
91 92

    p_sys = malloc( sizeof( sout_stream_sys_t ) );
93 94
    if( !p_sys )
        return VLC_ENOMEM;
95

96
    TAB_INIT( p_sys->i_nb_streams, p_sys->pp_streams );
97
    TAB_INIT( p_sys->i_nb_last_streams, p_sys->pp_last_streams );
98
    TAB_INIT( p_sys->i_nb_select, p_sys->ppsz_select );
99 100 101 102 103

    for( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )
    {
        if( !strncmp( p_cfg->psz_name, "dst", strlen( "dst" ) ) )
        {
104
            sout_stream_t *s, *p_last;
105 106

            msg_Dbg( p_stream, " * adding `%s'", p_cfg->psz_value );
107 108
            s = sout_StreamChainNew( p_stream->p_sout, p_cfg->psz_value,
                p_stream->p_next, &p_last );
109

Gildas Bazin's avatar
 
Gildas Bazin committed
110 111 112
            if( s )
            {
                TAB_APPEND( p_sys->i_nb_streams, p_sys->pp_streams, s );
113 114
                TAB_APPEND( p_sys->i_nb_last_streams, p_sys->pp_last_streams,
                    p_last );
115 116 117 118 119 120 121 122
                TAB_APPEND( p_sys->i_nb_select,  p_sys->ppsz_select, NULL );
            }
        }
        else if( !strncmp( p_cfg->psz_name, "select", strlen( "select" ) ) )
        {
            char *psz = p_cfg->psz_value;
            if( p_sys->i_nb_select > 0 && psz && *psz )
            {
123 124 125 126 127 128 129 130 131 132 133 134
                char **ppsz_select = &p_sys->ppsz_select[p_sys->i_nb_select - 1];

                if( *ppsz_select )
                {
                    msg_Err( p_stream, " * ignore selection `%s' (it already has `%s')",
                             psz, *ppsz_select );
                }
                else
                {
                    msg_Dbg( p_stream, " * apply selection `%s'", psz );
                    *ppsz_select = strdup( psz );
                }
Gildas Bazin's avatar
 
Gildas Bazin committed
135
            }
136
        }
137 138 139 140
        else
        {
            msg_Err( p_stream, " * ignore unknown option `%s'", p_cfg->psz_name );
        }
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
    }

    if( p_sys->i_nb_streams == 0 )
    {
        msg_Err( p_stream, "no destination given" );
        free( p_sys );

        return VLC_EGENERIC;
    }

    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;

    int i;

170
    msg_Dbg( p_stream, "closing a duplication" );
171 172
    for( i = 0; i < p_sys->i_nb_streams; i++ )
    {
173
        sout_StreamChainDelete(p_sys->pp_streams[i], p_sys->pp_last_streams[i]);
174
        free( p_sys->ppsz_select[i] );
175
    }
176
    free( p_sys->pp_streams );
177
    free( p_sys->pp_last_streams );
178
    free( p_sys->ppsz_select );
179 180 181 182

    free( p_sys );
}

183 184 185
/*****************************************************************************
 * Add:
 *****************************************************************************/
186
static sout_stream_id_t * Add( sout_stream_t *p_stream, es_format_t *p_fmt )
187 188 189
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    sout_stream_id_t  *id;
190
    int i_stream, i_valid_streams = 0;
191 192

    id = malloc( sizeof( sout_stream_id_t ) );
193 194 195
    if( !id )
        return NULL;

196
    TAB_INIT( id->i_nb_ids, id->pp_ids );
197

198
    msg_Dbg( p_stream, "duplicated a new stream codec=%4.4s (es=%d group=%d)",
199 200
             (char*)&p_fmt->i_codec, p_fmt->i_id, p_fmt->i_group );

201 202
    for( i_stream = 0; i_stream < p_sys->i_nb_streams; i_stream++ )
    {
203
        void *id_new = NULL;
204

Laurent Aimar's avatar
Laurent Aimar committed
205
        if( ESSelected( p_fmt, p_sys->ppsz_select[i_stream] ) )
206 207 208
        {
            sout_stream_t *out = p_sys->pp_streams[i_stream];

209
            id_new = (void*)sout_StreamIdAdd( out, p_fmt );
210 211 212 213 214 215 216 217 218 219 220 221 222 223
            if( id_new )
            {
                msg_Dbg( p_stream, "    - added for output %d", i_stream );
                i_valid_streams++;
            }
            else
            {
                msg_Dbg( p_stream, "    - failed for output %d", i_stream );
            }
        }
        else
        {
            msg_Dbg( p_stream, "    - ignored for output %d", i_stream );
        }
224 225

        /* Append failed attempts as well to keep track of which pp_id
226
         * belongs to which duplicated stream */
227
        TAB_APPEND( id->i_nb_ids, id->pp_ids, id_new );
228 229
    }

230
    if( i_valid_streams <= 0 )
231
    {
232
        Del( p_stream, id );
233 234 235 236 237 238
        return NULL;
    }

    return id;
}

239 240 241
/*****************************************************************************
 * Del:
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
242
static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
243 244 245 246 247 248
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    int               i_stream;

    for( i_stream = 0; i_stream < p_sys->i_nb_streams; i_stream++ )
    {
249
        if( id->pp_ids[i_stream] )
250
        {
251
            sout_stream_t *out = p_sys->pp_streams[i_stream];
252
            sout_StreamIdDel( out, id->pp_ids[i_stream] );
253 254 255 256 257 258 259 260
        }
    }

    free( id->pp_ids );
    free( id );
    return VLC_SUCCESS;
}

261 262 263
/*****************************************************************************
 * Send:
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
264
static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
265
                 block_t *p_buffer )
266 267
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;
268
    sout_stream_t     *p_dup_stream;
269 270
    int               i_stream;

271 272
    /* Loop through the linked list of buffers */
    while( p_buffer )
273
    {
274
        block_t *p_next = p_buffer->p_next;
275

276 277 278
        p_buffer->p_next = NULL;

        for( i_stream = 0; i_stream < p_sys->i_nb_streams - 1; i_stream++ )
279
        {
280 281 282 283
            p_dup_stream = p_sys->pp_streams[i_stream];

            if( id->pp_ids[i_stream] )
            {
284
                block_t *p_dup = block_Duplicate( p_buffer );
285

286
                if( p_dup )
287
                    sout_StreamIdSend( p_dup_stream, id->pp_ids[i_stream], p_dup );
288
            }
289 290
        }

291 292 293
        if( i_stream < p_sys->i_nb_streams && id->pp_ids[i_stream] )
        {
            p_dup_stream = p_sys->pp_streams[i_stream];
294
            sout_StreamIdSend( p_dup_stream, id->pp_ids[i_stream], p_buffer );
295 296 297
        }
        else
        {
298
            block_Release( p_buffer );
299
        }
300

301 302
        p_buffer = p_next;
    }
303 304
    return VLC_SUCCESS;
}
305 306 307 308

/*****************************************************************************
 * Divers
 *****************************************************************************/
309
static bool NumInRange( const char *psz_range, int i_num )
310
{
311
    const char *psz = strchr( psz_range, '-' );
312 313 314
    char *end;
    int  i_start, i_stop;

315 316 317 318
    i_start = strtol( psz_range, &end, 0 );
    if( end == psz_range )
        i_start = i_num;

319 320
    if( psz )
    {
321 322 323
        psz++;
        i_stop = strtol( psz, &end, 0 );
        if( end == psz )
Rémi Denis-Courmont's avatar
Typo  
Rémi Denis-Courmont committed
324
            i_stop = i_num;
325 326
    }
    else
327
        i_stop = i_start;
328

329
    return i_start <= i_num && i_num <= i_stop;
330 331
}

332
static bool ESSelected( es_format_t *fmt, char *psz_select )
333 334 335 336
{
    char  *psz_dup;
    char  *psz;

Antoine Cellerier's avatar
Antoine Cellerier committed
337
    /* We have tri-state variable : no tested (-1), failed(0), succeed(1) */
338 339 340 341 342 343 344
    int i_cat = -1;
    int i_es  = -1;
    int i_prgm= -1;

    /* If empty all es are selected */
    if( psz_select == NULL || *psz_select == '\0' )
    {
345
        return true;
346 347
    }
    psz_dup = strdup( psz_select );
348 349 350
    if( !psz_dup )
        return false;
    psz = psz_dup;
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383

    /* If non empty, parse the selection:
     * We have selection[,selection[,..]] where following selection are recognized:
     *      (no(-))audio
     *      (no(-))spu
     *      (no(-))video
     *      (no(-))es=[start]-[end] or es=num
     *      (no(-))prgm=[start]-[end] or prgm=num (program works too)
     *      if a negative test failed we exit directly
     */
    while( psz && *psz )
    {
        char *p;

        /* Skip space */
        while( *psz == ' ' || *psz == '\t' ) psz++;

        /* Search end */
        p = strchr( psz, ',' );
        if( p == psz )
        {
            /* Empty */
            psz = p + 1;
            continue;
        }
        if( p )
        {
            *p++ = '\0';
        }

        if( !strncmp( psz, "no-audio", strlen( "no-audio" ) ) ||
            !strncmp( psz, "noaudio", strlen( "noaudio" ) ) )
        {
384
            if( i_cat == -1 )
385
            {
386
                i_cat = fmt->i_cat != AUDIO_ES ? 1 : 0;
387 388 389 390 391
            }
        }
        else if( !strncmp( psz, "no-video", strlen( "no-video" ) ) ||
                 !strncmp( psz, "novideo", strlen( "novideo" ) ) )
        {
392
            if( i_cat == -1 )
393
            {
394
                i_cat = fmt->i_cat != VIDEO_ES ? 1 : 0;
395 396 397 398 399
            }
        }
        else if( !strncmp( psz, "no-spu", strlen( "no-spu" ) ) ||
                 !strncmp( psz, "nospu", strlen( "nospu" ) ) )
        {
400
            if( i_cat == -1 )
401
            {
402
                i_cat = fmt->i_cat != SPU_ES ? 1 : 0;
403 404 405 406
            }
        }
        else if( !strncmp( psz, "audio", strlen( "audio" ) ) )
        {
407
            if( i_cat == -1 )
408 409 410 411 412 413
            {
                i_cat = fmt->i_cat == AUDIO_ES ? 1 : 0;
            }
        }
        else if( !strncmp( psz, "video", strlen( "video" ) ) )
        {
414
            if( i_cat == -1 )
415 416 417 418 419 420
            {
                i_cat = fmt->i_cat == VIDEO_ES ? 1 : 0;
            }
        }
        else if( !strncmp( psz, "spu", strlen( "spu" ) ) )
        {
421
            if( i_cat == -1 )
422 423 424 425 426 427 428 429 430
            {
                i_cat = fmt->i_cat == SPU_ES ? 1 : 0;
            }
        }
        else if( strchr( psz, '=' ) != NULL )
        {
            char *psz_arg = strchr( psz, '=' );
            *psz_arg++ = '\0';

431
            if( !strcmp( psz, "no-es" ) || !strcmp( psz, "noes" ) )
432
            {
433
                if( i_es == -1 )
434
                {
435
                    i_es = NumInRange( psz_arg, fmt->i_id ) ? 0 : -1;
436 437 438 439
                }
            }
            else if( !strcmp( psz, "es" ) )
            {
440
                if( i_es == -1 )
441
                {
442
                    i_es = NumInRange( psz_arg, fmt->i_id) ? 1 : -1;
443 444 445 446 447
                }
            }
            else if( !strcmp( psz, "no-prgm" ) || !strcmp( psz, "noprgm" ) ||
                      !strcmp( psz, "no-program" ) || !strcmp( psz, "noprogram" ) )
            {
448
                if( fmt->i_group >= 0 && i_prgm == -1 )
449
                {
450
                    i_prgm = NumInRange( psz_arg, fmt->i_group ) ? 0 : -1;
451 452
                }
            }
453
            else if( !strcmp( psz, "prgm" ) || !strcmp( psz, "program" ) )
454
            {
455
                if( fmt->i_group >= 0 && i_prgm == -1 )
456
                {
457
                    i_prgm = NumInRange( psz_arg, fmt->i_group ) ? 1 : -1;
458 459 460 461 462 463 464 465 466 467 468 469 470
                }
            }
        }
        else
        {
            fprintf( stderr, "unknown args (%s)\n", psz );
        }
        /* Next */
        psz = p;
    }

    free( psz_dup );

471
    if( i_cat == 1 || i_es == 1 || i_prgm == 1 )
472
    {
473
        return true;
474
    }
475
    return false;
476
}