ogg.c 26 KB
Newer Older
1
/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
2
 * ogg.c: ogg muxer module for vlc
3 4
 *****************************************************************************
 * Copyright (C) 2001, 2002 VideoLAN
Gildas Bazin's avatar
 
Gildas Bazin committed
5
 * $Id: ogg.c,v 1.12 2003/09/28 21:54:21 gbazin Exp $
6 7
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
Gildas Bazin's avatar
 
Gildas Bazin committed
8
 *          Gildas Bazin <gbazin@netcourrier.com>
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 35 36 37 38 39 40 41 42
 *
 * 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 <sys/types.h>

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

#include "codecs.h"

#include <ogg/ogg.h>

/*****************************************************************************
 * Exported prototypes
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
43 44
static int  Open   ( vlc_object_t * );
static void Close  ( vlc_object_t * );
45

46 47 48 49
static int Capability(sout_mux_t *, int, void *, void * );
static int AddStream( sout_mux_t *, sout_input_t * );
static int DelStream( sout_mux_t *, sout_input_t * );
static int Mux      ( sout_mux_t * );
50

Gildas Bazin's avatar
 
Gildas Bazin committed
51 52 53
static sout_buffer_t *OggCreateHeader( sout_mux_t *, mtime_t );
static sout_buffer_t *OggCreateFooter( sout_mux_t *, mtime_t );

54 55 56 57 58 59 60 61 62 63 64 65
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
vlc_module_begin();
    set_description( _("Ogg/ogm muxer") );
    set_capability( "sout mux", 10 );
    add_shortcut( "ogg" );
    add_shortcut( "ogm" );
    set_callbacks( Open, Close );
vlc_module_end();

/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
66
 * Misc declarations
67 68 69
 *****************************************************************************/
#define FREE( p ) if( p ) { free( p ); (p) = NULL; }

Gildas Bazin's avatar
 
Gildas Bazin committed
70 71
/* Structures used for OggDS headers used in ogm files */

72 73
#define PACKET_TYPE_HEADER   0x01
#define PACKET_TYPE_COMMENT  0x03
Gildas Bazin's avatar
 
Gildas Bazin committed
74
#define PACKET_IS_SYNCPOINT  0x08
75

76 77 78 79
typedef struct
#ifdef HAVE_ATTRIBUTE_PACKED
    __attribute__((__packed__))
#endif
80 81 82 83 84
{
    int32_t i_width;
    int32_t i_height;
} ogg_stream_header_video_t;

85 86 87 88
typedef struct
#ifdef HAVE_ATTRIBUTE_PACKED
    __attribute__((__packed__))
#endif
89 90 91 92 93 94
{
    int16_t i_channels;
    int16_t i_block_align;
    int32_t i_avgbytespersec;
} ogg_stream_header_audio_t;

95 96 97 98
typedef struct
#ifdef HAVE_ATTRIBUTE_PACKED
    __attribute__((__packed__))
#endif
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
{
    uint8_t i_packet_type;

    char stream_type[8];
    char sub_type[4];

    int32_t i_size;

    int64_t i_time_unit;
    int64_t i_samples_per_unit;
    int32_t i_default_len;

    int32_t i_buffer_size;
    int16_t i_bits_per_sample;
    int16_t i_padding_0;            // hum hum
    union
    {
        ogg_stream_header_video_t video;
        ogg_stream_header_audio_t audio;
    } header;

} ogg_stream_header_t;

Gildas Bazin's avatar
 
Gildas Bazin committed
122
/* Helper writer functions */
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145

#define SetWLE( p, v ) _SetWLE( (uint8_t*)p, v)
static void _SetWLE( uint8_t *p, uint16_t i_dw )
{
    p[1] = ( i_dw >>  8 )&0xff;
    p[0] = ( i_dw       )&0xff;
}

#define SetDWLE( p, v ) _SetDWLE( (uint8_t*)p, v)
static void _SetDWLE( uint8_t *p, uint32_t i_dw )
{
    p[3] = ( i_dw >> 24 )&0xff;
    p[2] = ( i_dw >> 16 )&0xff;
    p[1] = ( i_dw >>  8 )&0xff;
    p[0] = ( i_dw       )&0xff;
}
#define SetQWLE( p, v ) _SetQWLE( (uint8_t*)p, v)
static void _SetQWLE( uint8_t *p, uint64_t i_qw )
{
    SetDWLE( p,   i_qw&0xffffffff );
    SetDWLE( p+4, ( i_qw >> 32)&0xffffffff );
}

Gildas Bazin's avatar
 
Gildas Bazin committed
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
/*
 * TODO  move this function to src/stream_output.c (used by nearly all muxers)
 */
static int MuxGetStream( sout_mux_t *p_mux, int *pi_stream, mtime_t *pi_dts )
{
    mtime_t i_dts;
    int     i_stream;
    int     i;

    for( i = 0, i_dts = 0, i_stream = -1; i < p_mux->i_nb_inputs; i++ )
    {
        sout_fifo_t  *p_fifo;

        p_fifo = p_mux->pp_inputs[i]->p_fifo;

        if( p_fifo->i_depth > 2 )
        {
            sout_buffer_t *p_buf;

            p_buf = sout_FifoShow( p_fifo );
            if( i_stream < 0 || p_buf->i_dts < i_dts )
            {
                i_dts = p_buf->i_dts;
                i_stream = i;
            }
        }
        else
        {
            // wait that all fifo have at least 3 packets (3 vorbis headers)
            return( -1 );
        }
    }
    if( pi_stream )
    {
        *pi_stream = i_stream;
    }
    if( pi_dts )
    {
        *pi_dts = i_dts;
    }
    return( i_stream );
}

/*****************************************************************************
 * Definitions of structures and functions used by this plugins 
 *****************************************************************************/
typedef struct
{
    int i_cat;
    int i_fourcc;

    int b_new;

    mtime_t i_dts;
    mtime_t i_length;
    int     i_packet_no;
    ogg_stream_state os;

    ogg_stream_header_t header;

} ogg_stream_t;

struct sout_mux_sys_t
{
    int     i_streams;

    mtime_t i_start_dts;

    /* number of logical streams pending to be added */
    int i_add_streams;

    /* logical streams pending to be deleted */
    int i_del_streams;
    ogg_stream_t **pp_del_streams;
};

222
static void OggSetDate( sout_buffer_t *, mtime_t , mtime_t  );
Gildas Bazin's avatar
 
Gildas Bazin committed
223 224
static sout_buffer_t *OggStreamFlush( sout_mux_t *, ogg_stream_state *,
                                      mtime_t );
225 226

/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
227
 * Open: Open muxer
228 229 230
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
231 232
    sout_mux_t      *p_mux = (sout_mux_t*)p_this;
    sout_mux_sys_t  *p_sys;
233

234
    msg_Info( p_mux, "Open" );
235

236 237
    p_sys                 = malloc( sizeof( sout_mux_sys_t ) );
    p_sys->i_streams      = 0;
Gildas Bazin's avatar
 
Gildas Bazin committed
238 239 240
    p_sys->i_add_streams  = 0;
    p_sys->i_del_streams  = 0;
    p_sys->pp_del_streams = 0;
241

242 243 244 245 246 247
    p_mux->p_sys        = p_sys;
    p_mux->pf_capacity  = Capability;
    p_mux->pf_addstream = AddStream;
    p_mux->pf_delstream = DelStream;
    p_mux->pf_mux       = Mux;
    p_mux->i_preheader  = 1;
248 249 250 251 252

    return VLC_SUCCESS;
}

/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
253
 * Close: Finalize ogg bitstream and close muxer
254 255 256
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
{
Gildas Bazin's avatar
 
Gildas Bazin committed
257 258
    sout_mux_t     *p_mux = (sout_mux_t*)p_this;
    sout_mux_sys_t *p_sys = p_mux->p_sys;
259

260
    msg_Info( p_mux, "Close" );
261

Gildas Bazin's avatar
 
Gildas Bazin committed
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
    if( p_sys->i_del_streams )
    {
        sout_buffer_t *p_og = NULL;
        mtime_t i_dts = -1;
        int i;

        /* Close the current ogg stream */
        msg_Dbg( p_mux, "writing footer" );
        sout_BufferChain( &p_og, OggCreateFooter( p_mux, 0 ) );

        /* Remove deleted logical streams */
        for( i = 0; i < p_sys->i_del_streams; i++ )
        {
            i_dts = p_sys->pp_del_streams[i]->i_dts;
            ogg_stream_clear( &p_sys->pp_del_streams[i]->os );
            FREE( p_sys->pp_del_streams[i] );
        }
        FREE( p_sys->pp_del_streams );
        p_sys->i_streams -= p_sys->i_del_streams;

        /* Write footer */
        OggSetDate( p_og, i_dts, 0 );
        sout_AccessOutWrite( p_mux->p_access, p_og );
    }

287
    free( p_sys );
288 289
}

Gildas Bazin's avatar
 
Gildas Bazin committed
290 291
static int Capability( sout_mux_t *p_mux, int i_query, void *p_args,
                       void *p_answer )
292 293 294 295
{
   switch( i_query )
   {
        case SOUT_MUX_CAP_GET_ADD_STREAM_ANY_TIME:
Gildas Bazin's avatar
 
Gildas Bazin committed
296
            *(vlc_bool_t*)p_answer = VLC_TRUE;
297 298 299 300 301 302
            return( SOUT_MUX_CAP_ERR_OK );
        default:
            return( SOUT_MUX_CAP_ERR_UNIMPLEMENTED );
   }
}

Gildas Bazin's avatar
 
Gildas Bazin committed
303 304 305
/*****************************************************************************
 * AddStream: Add an elementary stream to the muxed stream
 *****************************************************************************/
306
static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
307
{
308
    sout_mux_sys_t  *p_sys = p_mux->p_sys;
309 310
    ogg_stream_t        *p_stream;

311
    msg_Dbg( p_mux, "adding input" );
Gildas Bazin's avatar
 
Gildas Bazin committed
312 313

    p_input->p_sys = (void *)p_stream = malloc( sizeof( ogg_stream_t ) );
314

315 316
    p_stream->i_cat       = p_input->p_fmt->i_cat;
    p_stream->i_fourcc    = p_input->p_fmt->i_fourcc;
317 318 319
    p_stream->i_packet_no = 0;

    p_stream->header.i_packet_type = PACKET_TYPE_HEADER;
320
    switch( p_input->p_fmt->i_cat )
321
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
322
    case VIDEO_ES:
Gildas Bazin's avatar
 
Gildas Bazin committed
323
        switch( p_stream->i_fourcc )
Gildas Bazin's avatar
 
Gildas Bazin committed
324 325 326 327
        {
        case VLC_FOURCC( 'm', 'p','4', 'v' ):
        case VLC_FOURCC( 'D', 'I','V', '3' ):
            memcpy( p_stream->header.stream_type, "video    ", 8 );
Gildas Bazin's avatar
 
Gildas Bazin committed
328
            if( p_stream->i_fourcc == VLC_FOURCC( 'm', 'p','4', 'v' ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
329 330 331
            {
                memcpy( p_stream->header.sub_type, "XVID", 4 );
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
332
            else if( p_stream->i_fourcc == VLC_FOURCC( 'D', 'I','V', '3' ) )
333
            {
Gildas Bazin's avatar
 
Gildas Bazin committed
334
                memcpy( p_stream->header.sub_type, "DIV3", 4 );
335
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
336 337 338 339 340 341 342 343 344 345 346 347 348 349
            SetDWLE( &p_stream->header.i_size,
                     sizeof( ogg_stream_header_t ) - 1);
            /* XXX this won't make mplayer happy,
             * but vlc can read that without any problem so...*/
            SetQWLE( &p_stream->header.i_time_unit, 10*1000 );
            //(int64_t)10*1000*1000/(int64_t)25 );  // FIXME (25fps)
            SetQWLE( &p_stream->header.i_samples_per_unit, 1 );
            SetDWLE( &p_stream->header.i_default_len, 0 );      /* ??? */
            SetDWLE( &p_stream->header.i_buffer_size, 1024*1024 );
            SetWLE( &p_stream->header.i_bits_per_sample, 0 );
            SetDWLE( &p_stream->header.header.video.i_width,
                     p_input->p_fmt->i_width );
            SetDWLE( &p_stream->header.header.video.i_height,
                     p_input->p_fmt->i_height );
Gildas Bazin's avatar
 
Gildas Bazin committed
350
            msg_Dbg( p_mux, "mp4v/div3 stream" );
351
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
352

Gildas Bazin's avatar
 
Gildas Bazin committed
353
        case VLC_FOURCC( 't', 'h', 'e', 'o' ):
Gildas Bazin's avatar
 
Gildas Bazin committed
354 355
            msg_Dbg( p_mux, "theora stream" );
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
356

Gildas Bazin's avatar
 
Gildas Bazin committed
357 358 359 360 361
        default:
            FREE( p_input->p_sys );
            return( VLC_EGENERIC );
        }
        break;
Gildas Bazin's avatar
 
Gildas Bazin committed
362

Gildas Bazin's avatar
 
Gildas Bazin committed
363
    case AUDIO_ES:
Gildas Bazin's avatar
 
Gildas Bazin committed
364
        switch( p_stream->i_fourcc )
Gildas Bazin's avatar
 
Gildas Bazin committed
365 366 367 368
        {
        case VLC_FOURCC( 'm', 'p','g', 'a' ):
        case VLC_FOURCC( 'a', '5','2', ' ' ):
            memcpy( p_stream->header.stream_type, "audio    ", 8 );
Gildas Bazin's avatar
 
Gildas Bazin committed
369
            if( p_stream->i_fourcc == VLC_FOURCC( 'm', 'p','g', 'a' ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
370 371 372
            {
                memcpy( p_stream->header.sub_type, "55  ", 4 );
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
373
            else if( p_stream->i_fourcc == VLC_FOURCC( 'a', '5','2', ' ' ) )
374
            {
Gildas Bazin's avatar
 
Gildas Bazin committed
375
                memcpy( p_stream->header.sub_type, "2000", 4 );
376
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
377 378
            SetDWLE( &p_stream->header.i_size,
                     sizeof( ogg_stream_header_t ) - 1);
Gildas Bazin's avatar
 
Gildas Bazin committed
379 380
            SetQWLE( &p_stream->header.i_time_unit, 1000000 ); /*is it used ?*/
            SetDWLE( &p_stream->header.i_default_len, 0 );     /* ??? */
Gildas Bazin's avatar
 
Gildas Bazin committed
381 382 383 384 385 386 387 388 389
            SetDWLE( &p_stream->header.i_buffer_size, 30*1024 );
            SetQWLE( &p_stream->header.i_samples_per_unit,
                     p_input->p_fmt->i_sample_rate );
            SetWLE( &p_stream->header.i_bits_per_sample, 0 );
            SetDWLE( &p_stream->header.header.audio.i_channels,
                     p_input->p_fmt->i_channels );
            SetDWLE( &p_stream->header.header.audio.i_block_align,
                     p_input->p_fmt->i_block_align );
            SetDWLE( &p_stream->header.header.audio.i_avgbytespersec, 0 );
Gildas Bazin's avatar
 
Gildas Bazin committed
390
            msg_Dbg( p_mux, "mpga/a52 stream" );
391
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
392 393

        case VLC_FOURCC( 'v', 'o', 'r', 'b' ):
Gildas Bazin's avatar
 
Gildas Bazin committed
394 395
            msg_Dbg( p_mux, "vorbis stream" );
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
396

397
        default:
398
            FREE( p_input->p_sys );
399
            return( VLC_EGENERIC );
Gildas Bazin's avatar
 
Gildas Bazin committed
400 401 402
        }
        break;

Gildas Bazin's avatar
 
Gildas Bazin committed
403 404 405 406 407
    case SPU_ES:
        switch( p_stream->i_fourcc )
        {
        case VLC_FOURCC( 's', 'u','b', 't' ):
            memcpy( p_stream->header.stream_type, "text     ", 8 );
Gildas Bazin's avatar
 
Gildas Bazin committed
408
            msg_Dbg( p_mux, "subtitles stream" );
Gildas Bazin's avatar
 
Gildas Bazin committed
409 410 411 412 413 414 415
            break;

        default:
            FREE( p_input->p_sys );
            return( VLC_EGENERIC );
        }
        break;
Gildas Bazin's avatar
 
Gildas Bazin committed
416 417 418
    default:
        FREE( p_input->p_sys );
        return( VLC_EGENERIC );
419 420
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
421 422 423
    p_stream->b_new = VLC_TRUE;

    p_sys->i_add_streams++;
424 425 426 427

    return( VLC_SUCCESS );
}

Gildas Bazin's avatar
 
Gildas Bazin committed
428 429 430
/*****************************************************************************
 * DelStream: Delete an elementary stream from the muxed stream
 *****************************************************************************/
431
static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
432
{
Gildas Bazin's avatar
 
Gildas Bazin committed
433 434 435
    sout_mux_sys_t *p_sys  = p_mux->p_sys;
    ogg_stream_t   *p_stream = (ogg_stream_t*)p_input->p_sys;
    sout_buffer_t  *p_og;
436

437
    msg_Dbg( p_mux, "removing input" );
438 439

    /* flush all remaining data */
440
    if( p_input->p_sys )
441
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
442
        if( ( p_og = OggStreamFlush( p_mux, &p_stream->os, 0 ) ) )
443 444 445 446
        {
            OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
            sout_AccessOutWrite( p_mux->p_access, p_og );
        }
447

Gildas Bazin's avatar
 
Gildas Bazin committed
448 449
        /* move input in delete queue */
        if( !p_stream->b_new )
450
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
451 452 453 454
            p_sys->pp_del_streams = realloc( p_sys->pp_del_streams,
                                             (p_sys->i_del_streams + 1) *
                                             sizeof(ogg_stream_t *) );
            p_sys->pp_del_streams[p_sys->i_del_streams++] = p_stream;
455 456 457
        }
        else
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
458 459 460 461
            /* Wasn't already added so get rid of it */
            ogg_stream_clear( &p_stream->os );
            FREE( p_stream );
            p_sys->i_add_streams--;
462 463
        }
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
464 465 466 467

    p_input->p_sys = NULL;

    return( 0 );
468 469
}

Gildas Bazin's avatar
 
Gildas Bazin committed
470 471 472
/*****************************************************************************
 * Ogg bitstream manipulation routines
 *****************************************************************************/
473
static sout_buffer_t *OggStreamFlush( sout_mux_t *p_mux,
Gildas Bazin's avatar
 
Gildas Bazin committed
474
                                      ogg_stream_state *p_os, mtime_t i_pts )
475
{
Gildas Bazin's avatar
 
Gildas Bazin committed
476 477
    sout_buffer_t *p_og, *p_og_first = NULL;
    ogg_page      og;
478 479 480 481 482 483 484 485 486 487

    for( ;; )
    {
        /* flush all data */
        int i_result;
        int i_size;
        if( ( i_result = ogg_stream_flush( p_os, &og ) ) == 0 )
        {
            break;
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
488

489
        i_size = og.header_len + og.body_len;
490
        p_og = sout_BufferNew( p_mux->p_sout, i_size);
491

Gildas Bazin's avatar
 
Gildas Bazin committed
492 493
        memcpy( p_og->p_buffer, og.header, og.header_len );
        memcpy( p_og->p_buffer + og.header_len, og.body, og.body_len );
494 495 496 497 498 499 500 501 502 503 504 505
        p_og->i_size    = i_size;
        p_og->i_dts     = 0;
        p_og->i_pts     = i_pts;
        p_og->i_length  = 0;

        i_pts   = 0; // write it only once

        sout_BufferChain( &p_og_first, p_og );
    }

    return( p_og_first );
}
Gildas Bazin's avatar
 
Gildas Bazin committed
506

507
static sout_buffer_t *OggStreamPageOut( sout_mux_t *p_mux,
Gildas Bazin's avatar
 
Gildas Bazin committed
508
                                        ogg_stream_state *p_os, mtime_t i_pts )
509
{
Gildas Bazin's avatar
 
Gildas Bazin committed
510 511
    sout_buffer_t *p_og, *p_og_first = NULL;
    ogg_page      og;
512 513 514 515 516 517 518 519 520 521

    for( ;; )
    {
        /* flush all data */
        int i_result;
        int i_size;
        if( ( i_result = ogg_stream_pageout( p_os, &og ) ) == 0 )
        {
            break;
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
522

523
        i_size = og.header_len + og.body_len;
524
        p_og = sout_BufferNew( p_mux->p_sout, i_size);
525

Gildas Bazin's avatar
 
Gildas Bazin committed
526 527
        memcpy( p_og->p_buffer, og.header, og.header_len );
        memcpy( p_og->p_buffer + og.header_len, og.body, og.body_len );
528 529 530 531 532 533 534 535 536 537 538 539
        p_og->i_size    = i_size;
        p_og->i_dts     = 0;
        p_og->i_pts     = i_pts;
        p_og->i_length  = 0;

        i_pts   = 0; // write them only once

        sout_BufferChain( &p_og_first, p_og );
    }

    return( p_og_first );
}
Gildas Bazin's avatar
 
Gildas Bazin committed
540

541
static sout_buffer_t *OggCreateHeader( sout_mux_t *p_mux, mtime_t i_dts )
542 543 544 545 546 547
{
    sout_buffer_t *p_hdr = NULL;
    sout_buffer_t *p_og;
    ogg_packet    op;
    int i;

Gildas Bazin's avatar
 
Gildas Bazin committed
548 549
    /* Write header for each stream. All b_o_s (beginning of stream) packets
     * must appear first in the ogg stream so we take care of them first. */
550
    for( i = 0; i < p_mux->i_nb_inputs; i++ )
551
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
552 553
        ogg_stream_t *p_stream = (ogg_stream_t*)p_mux->pp_inputs[i]->p_sys;
        p_stream->b_new = VLC_FALSE;
554

Gildas Bazin's avatar
 
Gildas Bazin committed
555 556 557 558 559 560
        msg_Dbg( p_mux, "creating header for %4.4s",
                 (char *)&p_stream->i_fourcc );

        ogg_stream_init( &p_stream->os, rand() );
        p_stream->i_packet_no = 0;

Gildas Bazin's avatar
 
Gildas Bazin committed
561 562
        if( p_stream->i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) ||
            p_stream->i_fourcc == VLC_FOURCC( 't', 'h', 'e', 'o' ) )
563
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
564 565 566 567 568 569 570 571 572 573 574 575
            /* Special case, headers are already there in the
             * incoming stream */

            /* first packet in order: vorbis info */
            p_og = sout_FifoGet( p_mux->pp_inputs[i]->p_fifo );
            op.packet = p_og->p_buffer;
            op.bytes  = p_og->i_size;
            op.b_o_s  = 1;
            op.e_o_s  = 0;
            op.granulepos = 0;
            op.packetno = p_stream->i_packet_no++;
            ogg_stream_packetin( &p_stream->os, &op );
576 577 578 579 580
        }
        else
        {
            /* ds header */
            op.packet = (uint8_t*)&p_stream->header;
Gildas Bazin's avatar
 
Gildas Bazin committed
581
            op.bytes  = sizeof( ogg_stream_header_t );
582 583 584 585 586
            op.b_o_s  = 1;
            op.e_o_s  = 0;
            op.granulepos = 0;
            op.packetno = p_stream->i_packet_no++;
            ogg_stream_packetin( &p_stream->os, &op );
Gildas Bazin's avatar
 
Gildas Bazin committed
587 588 589 590 591 592 593 594 595
        }

        p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
        sout_BufferChain( &p_hdr, p_og );
    }

    /* Take care of the non b_o_s headers */
    for( i = 0; i < p_mux->i_nb_inputs; i++ )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
596
        ogg_stream_t *p_stream = (ogg_stream_t*)p_mux->pp_inputs[i]->p_sys;
Gildas Bazin's avatar
 
Gildas Bazin committed
597

Gildas Bazin's avatar
 
Gildas Bazin committed
598 599
        if( p_stream->i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) ||
            p_stream->i_fourcc == VLC_FOURCC( 't', 'h', 'e', 'o' ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
        {
            /* Special case, headers are already there in the incoming stream.
             * We need to gather them an mark them as headers. */
            int j;
            for( j = 0; j < 2; j++ )
            {
                /* next packets in order: comments and codebooks */
                p_og = sout_FifoGet( p_mux->pp_inputs[i]->p_fifo );
                op.packet = p_og->p_buffer;
                op.bytes  = p_og->i_size;
                op.b_o_s  = 0;
                op.e_o_s  = 0;
                op.granulepos = 0;
                op.packetno = p_stream->i_packet_no++;
                ogg_stream_packetin( &p_stream->os, &op );
            }
        }
        else
        {
            uint8_t com[128];
            int     i_com;

622 623
            /* comment */
            com[0] = PACKET_TYPE_COMMENT;
Gildas Bazin's avatar
 
Gildas Bazin committed
624
            i_com = snprintf( &com[1], 128, VERSION" stream output" ) + 1;
625 626 627 628 629 630 631 632 633
            op.packet = com;
            op.bytes  = i_com;
            op.b_o_s  = 0;
            op.e_o_s  = 0;
            op.granulepos = 0;
            op.packetno = p_stream->i_packet_no++;
            ogg_stream_packetin( &p_stream->os, &op );
        }

634
        p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
635 636 637
        sout_BufferChain( &p_hdr, p_og );
    }

638 639 640 641 642
    /* set HEADER flag */
    for( p_og = p_hdr; p_og != NULL; p_og = p_og->p_next )
    {
        p_og->i_flags |= SOUT_BUFFER_FLAGS_HEADER;
    }
643 644 645
    return( p_hdr );
}

Gildas Bazin's avatar
 
Gildas Bazin committed
646 647 648 649 650 651 652 653
static sout_buffer_t *OggCreateFooter( sout_mux_t *p_mux, mtime_t i_dts )
{
    sout_mux_sys_t *p_sys = p_mux->p_sys;
    sout_buffer_t *p_hdr = NULL;
    sout_buffer_t *p_og;
    ogg_packet    op;
    int i;

Gildas Bazin's avatar
 
Gildas Bazin committed
654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
    /* flush all remaining data */
    for( i = 0; i < p_mux->i_nb_inputs; i++ )
    {
        ogg_stream_t *p_stream = p_mux->pp_inputs[i]->p_sys;

        /* skip newly added streams */
        if( p_stream->b_new ) continue;

        if( ( p_og = OggStreamFlush( p_mux, &p_stream->os, 0 ) ) )
        {
            OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
            sout_AccessOutWrite( p_mux->p_access, p_og );
        }
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
    /* Write eos packets for each stream. */
    for( i = 0; i < p_mux->i_nb_inputs; i++ )
    {
        ogg_stream_t *p_stream = p_mux->pp_inputs[i]->p_sys;

        /* skip newly added streams */
        if( p_stream->b_new ) continue;

        op.packet = NULL;
        op.bytes  = 0;
        op.b_o_s  = 0;
        op.e_o_s  = 1;
        op.granulepos = -1;
        op.packetno = p_stream->i_packet_no++;
        ogg_stream_packetin( &p_stream->os, &op );

        p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
        sout_BufferChain( &p_hdr, p_og );
Gildas Bazin's avatar
 
Gildas Bazin committed
687
        ogg_stream_clear( &p_stream->os );
Gildas Bazin's avatar
 
Gildas Bazin committed
688 689 690 691 692 693 694 695 696 697 698 699 700 701
    }

    for( i = 0; i < p_sys->i_del_streams; i++ )
    {
        op.packet = NULL;
        op.bytes  = 0;
        op.b_o_s  = 0;
        op.e_o_s  = 1;
        op.granulepos = -1;
        op.packetno = p_sys->pp_del_streams[i]->i_packet_no++;
        ogg_stream_packetin( &p_sys->pp_del_streams[i]->os, &op );

        p_og = OggStreamFlush( p_mux, &p_sys->pp_del_streams[i]->os, 0 );
        sout_BufferChain( &p_hdr, p_og );
Gildas Bazin's avatar
 
Gildas Bazin committed
702
        ogg_stream_clear( &p_sys->pp_del_streams[i]->os );
Gildas Bazin's avatar
 
Gildas Bazin committed
703 704 705 706 707
    }

    return( p_hdr );
}

708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728
static void OggSetDate( sout_buffer_t *p_og, mtime_t i_dts, mtime_t i_length )
{
    int i_count;
    sout_buffer_t *p_tmp;
    mtime_t i_delta;

    for( p_tmp = p_og, i_count = 0; p_tmp != NULL; p_tmp = p_tmp->p_next )
    {
        i_count++;
    }
    i_delta = i_length / i_count;

    for( p_tmp = p_og; p_tmp != NULL; p_tmp = p_tmp->p_next )
    {
        p_tmp->i_dts    = i_dts;
        p_tmp->i_length = i_delta;

        i_dts += i_delta;
    }
}

Gildas Bazin's avatar
 
Gildas Bazin committed
729 730 731
/*****************************************************************************
 * Mux: multiplex available data in input fifos into the Ogg bitstream
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
732
static int Mux( sout_mux_t *p_mux )
733
{
Gildas Bazin's avatar
 
Gildas Bazin committed
734 735 736 737
    sout_mux_sys_t *p_sys = p_mux->p_sys;
    sout_buffer_t  *p_og = NULL;
    int            i_stream;
    mtime_t        i_dts;
738

Gildas Bazin's avatar
 
Gildas Bazin committed
739
    if( p_sys->i_add_streams || p_sys->i_del_streams )
740
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
741
        /* Open new ogg stream */
742
        if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 )
743
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
744
            msg_Dbg( p_mux, "waiting for data..." );
745 746
            return( VLC_SUCCESS );
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763

        if( p_sys->i_streams )
        {
            /* Close current ogg stream */
            int i;

            msg_Dbg( p_mux, "writing footer" );
            sout_BufferChain( &p_og, OggCreateFooter( p_mux, 0 ) );

            /* Remove deleted logical streams */
            for( i = 0; i < p_sys->i_del_streams; i++ )
            {
                FREE( p_sys->pp_del_streams[i] );
            }
            FREE( p_sys->pp_del_streams );
            p_sys->i_streams = 0;
        }
764 765

        msg_Dbg( p_mux, "writing header" );
Gildas Bazin's avatar
 
Gildas Bazin committed
766 767 768 769
        p_sys->i_start_dts = i_dts;
        p_sys->i_streams = p_mux->i_nb_inputs;
        p_sys->i_del_streams = 0;
        p_sys->i_add_streams = 0;
770
        sout_BufferChain( &p_og, OggCreateHeader( p_mux, i_dts ) );
Gildas Bazin's avatar
 
Gildas Bazin committed
771 772 773 774 775

        /* Write header and/or footer */
        OggSetDate( p_og, i_dts, 0 );
        sout_AccessOutWrite( p_mux->p_access, p_og );
        p_og = NULL;
776 777 778 779
    }

    for( ;; )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
780 781 782 783
        sout_input_t  *p_input;
        ogg_stream_t  *p_stream;
        sout_buffer_t *p_data;
        ogg_packet    op;
784

785
        if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 )
786 787 788
        {
            return( VLC_SUCCESS );
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
789

790 791
        p_input  = p_mux->pp_inputs[i_stream];
        p_stream = (ogg_stream_t*)p_input->p_sys;
Gildas Bazin's avatar
 
Gildas Bazin committed
792
        p_data   = sout_FifoGet( p_input->p_fifo );
793

Gildas Bazin's avatar
 
Gildas Bazin committed
794 795
        if( p_stream->i_fourcc != VLC_FOURCC( 'v', 'o', 'r', 'b' ) &&
            p_stream->i_fourcc != VLC_FOURCC( 't', 'h', 'e', 'o' ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
796 797 798 799 800 801 802 803 804 805
        {
            sout_BufferReallocFromPreHeader( p_mux->p_sout, p_data, 1 );
            p_data->p_buffer[0] = PACKET_IS_SYNCPOINT;      // FIXME
        }

        op.packet   = p_data->p_buffer;
        op.bytes    = p_data->i_size;
        op.b_o_s    = 0;
        op.e_o_s    = 0;
        op.packetno = p_stream->i_packet_no++;
806 807 808

        if( p_stream->i_cat == AUDIO_ES )
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
809 810 811 812 813 814 815 816 817 818 819 820 821
            if( p_stream->i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) )
            {
                /* number of sample from begining + current packet */
                op.granulepos =
                    ( i_dts + p_data->i_length - p_sys->i_start_dts ) *
                    p_input->p_fmt->i_sample_rate / (int64_t)1000000;
            }
            else
            {
                /* number of sample from begining */
                op.granulepos = ( i_dts - p_sys->i_start_dts ) *
                    p_stream->header.i_samples_per_unit / (int64_t)1000000;
            }
822 823 824
        }
        else if( p_stream->i_cat == VIDEO_ES )
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
825 826 827 828 829 830
            if( p_stream->i_fourcc == VLC_FOURCC( 't', 'h', 'e', 'o' ) )
            {
                op.granulepos = op.packetno; /* FIXME */
            }
            else
                op.granulepos = ( i_dts - p_sys->i_start_dts ) / 1000;
831
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
832 833 834 835 836
        else if( p_stream->i_cat == SPU_ES )
        {
            /* granulepos is in milisec */
            op.granulepos = ( i_dts - p_sys->i_start_dts ) / 1000;
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
837

838 839
        ogg_stream_packetin( &p_stream->os, &op );

Gildas Bazin's avatar
 
Gildas Bazin committed
840 841
        sout_BufferChain( &p_og, OggStreamPageOut( p_mux, &p_stream->os,
                                                   p_data->i_dts ) );
842 843 844 845 846

        if( p_og )
        {
            OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
            p_stream->i_dts = -1;
847 848
            p_stream->i_length = 0;

849
            sout_AccessOutWrite( p_mux->p_access, p_og );
850 851 852 853 854 855 856

            p_og = NULL;
        }
        else
        {
            if( p_stream->i_dts < 0 )
            {
857
                p_stream->i_dts = p_data->i_dts;
858 859 860 861
            }
            p_stream->i_length += p_data->i_length;
        }

862
        sout_BufferDelete( p_mux->p_sout, p_data );
863 864 865 866
    }

    return( VLC_SUCCESS );
}