ogg.c 28.9 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.17 2003/10/09 19:31:38 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
{
    int32_t i_width;
    int32_t i_height;
Gildas Bazin's avatar
 
Gildas Bazin committed
83
} oggds_header_video_t;
84

85 86 87 88
typedef struct
#ifdef HAVE_ATTRIBUTE_PACKED
    __attribute__((__packed__))
#endif
89 90 91 92
{
    int16_t i_channels;
    int16_t i_block_align;
    int32_t i_avgbytespersec;
Gildas Bazin's avatar
 
Gildas Bazin committed
93
} oggds_header_audio_t;
94

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
{
    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;
113 114 115

    int16_t i_padding_0; /* Because the original is using MSVC packing style */

116 117
    union
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
118 119
        oggds_header_video_t video;
        oggds_header_audio_t audio;
120 121
    } header;

122 123
    int32_t i_padding_1; /* Because the original is using MSVC packing style */

Gildas Bazin's avatar
 
Gildas Bazin committed
124
} oggds_header_t;
125

Gildas Bazin's avatar
 
Gildas Bazin committed
126
/* Helper writer functions */
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149

#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
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
/*
 * 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;

Gildas Bazin's avatar
 
Gildas Bazin committed
165 166 167 168 169 170 171 172
        /* We don't really need to have anything in the SPU fifo */
        if( p_mux->pp_inputs[i]->p_fmt->i_cat == SPU_ES &&
            p_fifo->i_depth == 0 ) continue;

        if( p_fifo->i_depth > 2 ||
            /* Special case for SPUs */
            ( p_mux->pp_inputs[i]->p_fmt->i_cat == SPU_ES &&
              p_fifo->i_depth > 0 ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
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
        {
            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;
Gildas Bazin's avatar
 
Gildas Bazin committed
213
    int     i_serial_no;
Gildas Bazin's avatar
 
Gildas Bazin committed
214
    int     i_keyframe_granule_shift; /* Theora only */
Gildas Bazin's avatar
 
Gildas Bazin committed
215 216
    ogg_stream_state os;

Gildas Bazin's avatar
 
Gildas Bazin committed
217 218 219 220
    oggds_header_t oggds_header;

    sout_buffer_t *pp_sout_headers[3];
    int           i_sout_headers;
Gildas Bazin's avatar
 
Gildas Bazin committed
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237

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

238
static void OggSetDate( sout_buffer_t *, mtime_t , mtime_t  );
Gildas Bazin's avatar
 
Gildas Bazin committed
239 240
static sout_buffer_t *OggStreamFlush( sout_mux_t *, ogg_stream_state *,
                                      mtime_t );
241 242

/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
243
 * Open: Open muxer
244 245 246
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
247 248
    sout_mux_t      *p_mux = (sout_mux_t*)p_this;
    sout_mux_sys_t  *p_sys;
249

250
    msg_Info( p_mux, "Open" );
251

252 253
    p_sys                 = malloc( sizeof( sout_mux_sys_t ) );
    p_sys->i_streams      = 0;
Gildas Bazin's avatar
 
Gildas Bazin committed
254 255 256
    p_sys->i_add_streams  = 0;
    p_sys->i_del_streams  = 0;
    p_sys->pp_del_streams = 0;
257

258 259 260 261 262 263
    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;
264 265 266 267 268

    return VLC_SUCCESS;
}

/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
269
 * Close: Finalize ogg bitstream and close muxer
270 271 272
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
{
Gildas Bazin's avatar
 
Gildas Bazin committed
273 274
    sout_mux_t     *p_mux = (sout_mux_t*)p_this;
    sout_mux_sys_t *p_sys = p_mux->p_sys;
275

276
    msg_Info( p_mux, "Close" );
277

Gildas Bazin's avatar
 
Gildas Bazin committed
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
    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 );
    }

303
    free( p_sys );
304 305
}

Gildas Bazin's avatar
 
Gildas Bazin committed
306 307
static int Capability( sout_mux_t *p_mux, int i_query, void *p_args,
                       void *p_answer )
308 309 310 311
{
   switch( i_query )
   {
        case SOUT_MUX_CAP_GET_ADD_STREAM_ANY_TIME:
Gildas Bazin's avatar
 
Gildas Bazin committed
312
            *(vlc_bool_t*)p_answer = VLC_TRUE;
313 314 315 316 317 318
            return( SOUT_MUX_CAP_ERR_OK );
        default:
            return( SOUT_MUX_CAP_ERR_UNIMPLEMENTED );
   }
}

Gildas Bazin's avatar
 
Gildas Bazin committed
319 320 321
/*****************************************************************************
 * AddStream: Add an elementary stream to the muxed stream
 *****************************************************************************/
322
static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
323
{
Gildas Bazin's avatar
 
Gildas Bazin committed
324 325
    sout_mux_sys_t *p_sys = p_mux->p_sys;
    ogg_stream_t   *p_stream;
326

327
    msg_Dbg( p_mux, "adding input" );
Gildas Bazin's avatar
 
Gildas Bazin committed
328 329

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

331 332
    p_stream->i_cat       = p_input->p_fmt->i_cat;
    p_stream->i_fourcc    = p_input->p_fmt->i_fourcc;
Gildas Bazin's avatar
 
Gildas Bazin committed
333
    p_stream->i_serial_no = rand();
334 335
    p_stream->i_packet_no = 0;

Gildas Bazin's avatar
 
Gildas Bazin committed
336 337
    p_stream->i_sout_headers = 0;

338
    memset( &p_stream->oggds_header, 0, sizeof(p_stream->oggds_header) );
Gildas Bazin's avatar
 
Gildas Bazin committed
339
    p_stream->oggds_header.i_packet_type = PACKET_TYPE_HEADER;
340
    switch( p_input->p_fmt->i_cat )
341
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
342
    case VIDEO_ES:
Gildas Bazin's avatar
 
Gildas Bazin committed
343
        switch( p_stream->i_fourcc )
Gildas Bazin's avatar
 
Gildas Bazin committed
344 345 346
        {
        case VLC_FOURCC( 'm', 'p','4', 'v' ):
        case VLC_FOURCC( 'D', 'I','V', '3' ):
347
            memcpy( p_stream->oggds_header.stream_type, "video", 5 );
Gildas Bazin's avatar
 
Gildas Bazin committed
348
            if( p_stream->i_fourcc == VLC_FOURCC( 'm', 'p','4', 'v' ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
349
            {
Gildas Bazin's avatar
 
Gildas Bazin committed
350
                memcpy( p_stream->oggds_header.sub_type, "XVID", 4 );
Gildas Bazin's avatar
 
Gildas Bazin committed
351
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
352
            else if( p_stream->i_fourcc == VLC_FOURCC( 'D', 'I','V', '3' ) )
353
            {
Gildas Bazin's avatar
 
Gildas Bazin committed
354
                memcpy( p_stream->oggds_header.sub_type, "DIV3", 4 );
355
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
356 357 358
            SetDWLE( &p_stream->oggds_header.i_size,
                     sizeof( oggds_header_t ) - 1);
            SetQWLE( &p_stream->oggds_header.i_time_unit,
Gildas Bazin's avatar
 
Gildas Bazin committed
359
                     I64C(10000000)/(int64_t)25 );  // FIXME (25fps)
Gildas Bazin's avatar
 
Gildas Bazin committed
360
            SetQWLE( &p_stream->oggds_header.i_samples_per_unit, 1 );
361
            SetDWLE( &p_stream->oggds_header.i_default_len, 1 ); /* ??? */
Gildas Bazin's avatar
 
Gildas Bazin committed
362 363 364
            SetDWLE( &p_stream->oggds_header.i_buffer_size, 1024*1024 );
            SetWLE( &p_stream->oggds_header.i_bits_per_sample, 0 );
            SetDWLE( &p_stream->oggds_header.header.video.i_width,
Gildas Bazin's avatar
 
Gildas Bazin committed
365
                     p_input->p_fmt->i_width );
Gildas Bazin's avatar
 
Gildas Bazin committed
366
            SetDWLE( &p_stream->oggds_header.header.video.i_height,
Gildas Bazin's avatar
 
Gildas Bazin committed
367
                     p_input->p_fmt->i_height );
Gildas Bazin's avatar
 
Gildas Bazin committed
368
            msg_Dbg( p_mux, "mp4v/div3 stream" );
369
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
370

Gildas Bazin's avatar
 
Gildas Bazin committed
371
        case VLC_FOURCC( 't', 'h', 'e', 'o' ):
Gildas Bazin's avatar
 
Gildas Bazin committed
372 373
            msg_Dbg( p_mux, "theora stream" );
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
374

Gildas Bazin's avatar
 
Gildas Bazin committed
375 376 377 378 379
        default:
            FREE( p_input->p_sys );
            return( VLC_EGENERIC );
        }
        break;
Gildas Bazin's avatar
 
Gildas Bazin committed
380

Gildas Bazin's avatar
 
Gildas Bazin committed
381
    case AUDIO_ES:
Gildas Bazin's avatar
 
Gildas Bazin committed
382
        switch( p_stream->i_fourcc )
Gildas Bazin's avatar
 
Gildas Bazin committed
383 384 385
        {
        case VLC_FOURCC( 'm', 'p','g', 'a' ):
        case VLC_FOURCC( 'a', '5','2', ' ' ):
386
            memcpy( p_stream->oggds_header.stream_type, "audio", 5 );
Gildas Bazin's avatar
 
Gildas Bazin committed
387
            if( p_stream->i_fourcc == VLC_FOURCC( 'm', 'p','g', 'a' ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
388
            {
Gildas Bazin's avatar
 
Gildas Bazin committed
389
                memcpy( p_stream->oggds_header.sub_type, "55  ", 4 );
Gildas Bazin's avatar
 
Gildas Bazin committed
390
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
391
            else if( p_stream->i_fourcc == VLC_FOURCC( 'a', '5','2', ' ' ) )
392
            {
Gildas Bazin's avatar
 
Gildas Bazin committed
393
                memcpy( p_stream->oggds_header.sub_type, "2000", 4 );
394
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
395 396 397
            SetDWLE( &p_stream->oggds_header.i_size,
                     sizeof( oggds_header_t ) - 1);
            SetQWLE( &p_stream->oggds_header.i_time_unit, 0 /* not used */ );
398
            SetDWLE( &p_stream->oggds_header.i_default_len, 1 );
Gildas Bazin's avatar
 
Gildas Bazin committed
399 400
            SetDWLE( &p_stream->oggds_header.i_buffer_size, 30*1024 );
            SetQWLE( &p_stream->oggds_header.i_samples_per_unit,
Gildas Bazin's avatar
 
Gildas Bazin committed
401
                     p_input->p_fmt->i_sample_rate );
Gildas Bazin's avatar
 
Gildas Bazin committed
402 403
            SetWLE( &p_stream->oggds_header.i_bits_per_sample, 0 );
            SetDWLE( &p_stream->oggds_header.header.audio.i_channels,
Gildas Bazin's avatar
 
Gildas Bazin committed
404
                     p_input->p_fmt->i_channels );
Gildas Bazin's avatar
 
Gildas Bazin committed
405
            SetDWLE( &p_stream->oggds_header.header.audio.i_block_align,
Gildas Bazin's avatar
 
Gildas Bazin committed
406
                     p_input->p_fmt->i_block_align );
Gildas Bazin's avatar
 
Gildas Bazin committed
407
            SetDWLE( &p_stream->oggds_header.header.audio.i_avgbytespersec, 0);
Gildas Bazin's avatar
 
Gildas Bazin committed
408
            msg_Dbg( p_mux, "mpga/a52 stream" );
409
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
410 411

        case VLC_FOURCC( 'v', 'o', 'r', 'b' ):
Gildas Bazin's avatar
 
Gildas Bazin committed
412 413
            msg_Dbg( p_mux, "vorbis stream" );
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
414

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

Gildas Bazin's avatar
 
Gildas Bazin committed
421 422 423 424
    case SPU_ES:
        switch( p_stream->i_fourcc )
        {
        case VLC_FOURCC( 's', 'u','b', 't' ):
425
            memcpy( p_stream->oggds_header.stream_type, "text", 4 );
Gildas Bazin's avatar
 
Gildas Bazin committed
426
            msg_Dbg( p_mux, "subtitles stream" );
Gildas Bazin's avatar
 
Gildas Bazin committed
427 428 429 430 431 432 433
            break;

        default:
            FREE( p_input->p_sys );
            return( VLC_EGENERIC );
        }
        break;
Gildas Bazin's avatar
 
Gildas Bazin committed
434 435 436
    default:
        FREE( p_input->p_sys );
        return( VLC_EGENERIC );
437 438
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
439 440 441
    p_stream->b_new = VLC_TRUE;

    p_sys->i_add_streams++;
442 443 444 445

    return( VLC_SUCCESS );
}

Gildas Bazin's avatar
 
Gildas Bazin committed
446 447 448
/*****************************************************************************
 * DelStream: Delete an elementary stream from the muxed stream
 *****************************************************************************/
449
static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
450
{
Gildas Bazin's avatar
 
Gildas Bazin committed
451 452 453
    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;
454

455
    msg_Dbg( p_mux, "removing input" );
456 457

    /* flush all remaining data */
458
    if( p_input->p_sys )
459
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
460 461
        int i;

Gildas Bazin's avatar
 
Gildas Bazin committed
462
        if( ( p_og = OggStreamFlush( p_mux, &p_stream->os, 0 ) ) )
463 464 465 466
        {
            OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
            sout_AccessOutWrite( p_mux->p_access, p_og );
        }
467

Gildas Bazin's avatar
 
Gildas Bazin committed
468 469 470 471 472 473
        for( i = 0; i < p_stream->i_sout_headers; i++ )
        {
            sout_BufferDelete( p_mux->p_sout, p_stream->pp_sout_headers[i] );
            p_stream->i_sout_headers = 0;
        }

Gildas Bazin's avatar
 
Gildas Bazin committed
474 475
        /* move input in delete queue */
        if( !p_stream->b_new )
476
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
477 478 479 480
            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;
481 482 483
        }
        else
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
484 485 486 487
            /* Wasn't already added so get rid of it */
            ogg_stream_clear( &p_stream->os );
            FREE( p_stream );
            p_sys->i_add_streams--;
488 489
        }
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
490 491 492 493

    p_input->p_sys = NULL;

    return( 0 );
494 495
}

Gildas Bazin's avatar
 
Gildas Bazin committed
496 497 498
/*****************************************************************************
 * Ogg bitstream manipulation routines
 *****************************************************************************/
499
static sout_buffer_t *OggStreamFlush( sout_mux_t *p_mux,
Gildas Bazin's avatar
 
Gildas Bazin committed
500
                                      ogg_stream_state *p_os, mtime_t i_pts )
501
{
Gildas Bazin's avatar
 
Gildas Bazin committed
502 503
    sout_buffer_t *p_og, *p_og_first = NULL;
    ogg_page      og;
504 505 506 507 508 509 510 511 512 513

    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
514

515
        i_size = og.header_len + og.body_len;
516
        p_og = sout_BufferNew( p_mux->p_sout, i_size);
517

Gildas Bazin's avatar
 
Gildas Bazin committed
518 519
        memcpy( p_og->p_buffer, og.header, og.header_len );
        memcpy( p_og->p_buffer + og.header_len, og.body, og.body_len );
520 521 522 523 524 525 526 527 528 529 530 531
        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
532

533
static sout_buffer_t *OggStreamPageOut( sout_mux_t *p_mux,
Gildas Bazin's avatar
 
Gildas Bazin committed
534
                                        ogg_stream_state *p_os, mtime_t i_pts )
535
{
Gildas Bazin's avatar
 
Gildas Bazin committed
536 537
    sout_buffer_t *p_og, *p_og_first = NULL;
    ogg_page      og;
538 539 540 541 542 543 544 545 546 547

    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
548

549
        i_size = og.header_len + og.body_len;
550
        p_og = sout_BufferNew( p_mux->p_sout, i_size);
551

Gildas Bazin's avatar
 
Gildas Bazin committed
552 553
        memcpy( p_og->p_buffer, og.header, og.header_len );
        memcpy( p_og->p_buffer + og.header_len, og.body, og.body_len );
554 555 556 557 558 559 560 561 562 563 564 565
        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
566

567
static sout_buffer_t *OggCreateHeader( sout_mux_t *p_mux, mtime_t i_dts )
568 569 570 571 572 573
{
    sout_buffer_t *p_hdr = NULL;
    sout_buffer_t *p_og;
    ogg_packet    op;
    int i;

Gildas Bazin's avatar
 
Gildas Bazin committed
574 575
    /* 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. */
576
    for( i = 0; i < p_mux->i_nb_inputs; i++ )
577
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
578 579
        ogg_stream_t *p_stream = (ogg_stream_t*)p_mux->pp_inputs[i]->p_sys;
        p_stream->b_new = VLC_FALSE;
580

Gildas Bazin's avatar
 
Gildas Bazin committed
581 582 583
        msg_Dbg( p_mux, "creating header for %4.4s",
                 (char *)&p_stream->i_fourcc );

Gildas Bazin's avatar
 
Gildas Bazin committed
584
        ogg_stream_init( &p_stream->os, p_stream->i_serial_no );
Gildas Bazin's avatar
 
Gildas Bazin committed
585 586
        p_stream->i_packet_no = 0;

Gildas Bazin's avatar
 
Gildas Bazin committed
587 588
        if( p_stream->i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) ||
            p_stream->i_fourcc == VLC_FOURCC( 't', 'h', 'e', 'o' ) )
589
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
590
            /* Special case, headers are already there in the
Gildas Bazin's avatar
 
Gildas Bazin committed
591
             * incoming stream or we backed them up earlier */
Gildas Bazin's avatar
 
Gildas Bazin committed
592

Gildas Bazin's avatar
 
Gildas Bazin committed
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
            /* first packet in order: vorbis/theora info */
            if( !p_stream->i_sout_headers )
            {
                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 );
                p_stream->pp_sout_headers[0] =
                    OggStreamFlush( p_mux, &p_stream->os, 0 );
                p_stream->i_sout_headers++;
            }
            p_og = sout_BufferDuplicate( p_mux->p_sout,
                                         p_stream->pp_sout_headers[0] );
Gildas Bazin's avatar
 
Gildas Bazin committed
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624

            /* Get keyframe_granule_shift for theora granulepos calculation */
            if( p_stream->i_fourcc == VLC_FOURCC( 't', 'h', 'e', 'o' ) )
            {
                int i_keyframe_frequency_force = 1 << (op.packet[36] >> 3);

                /* granule_shift = i_log( frequency_force -1 ) */
                p_stream->i_keyframe_granule_shift = 0;
                i_keyframe_frequency_force--;
                while( i_keyframe_frequency_force )
                {
                    p_stream->i_keyframe_granule_shift++;
                    i_keyframe_frequency_force >>= 1;
                }
            }
625 626 627 628
        }
        else
        {
            /* ds header */
Gildas Bazin's avatar
 
Gildas Bazin committed
629 630
            op.packet = (uint8_t*)&p_stream->oggds_header;
            op.bytes  = sizeof( oggds_header_t );
631 632 633 634 635
            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
636
            p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
Gildas Bazin's avatar
 
Gildas Bazin committed
637 638 639 640 641 642 643 644
        }

        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
645
        ogg_stream_t *p_stream = (ogg_stream_t*)p_mux->pp_inputs[i]->p_sys;
Gildas Bazin's avatar
 
Gildas Bazin committed
646

Gildas Bazin's avatar
 
Gildas Bazin committed
647 648
        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
649 650 651 652 653 654
        {
            /* 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++ )
            {
Gildas Bazin's avatar
 
Gildas Bazin committed
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
                if( p_stream->i_sout_headers < j + 2 )
                {
                    /* 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 );
                    p_stream->pp_sout_headers[j+1] =
                        OggStreamFlush( p_mux, &p_stream->os, 0 );
                    p_stream->i_sout_headers++;
                }

                p_og = sout_BufferDuplicate( p_mux->p_sout,
                                             p_stream->pp_sout_headers[j+1] );
                sout_BufferChain( &p_hdr, p_og );
Gildas Bazin's avatar
 
Gildas Bazin committed
674 675 676 677 678 679 680
            }
        }
        else
        {
            uint8_t com[128];
            int     i_com;

681 682
            /* comment */
            com[0] = PACKET_TYPE_COMMENT;
Gildas Bazin's avatar
 
Gildas Bazin committed
683
            i_com = snprintf( &com[1], 128, VERSION" stream output" ) + 1;
684 685 686 687 688 689 690
            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 );
Gildas Bazin's avatar
 
Gildas Bazin committed
691 692
            p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
            sout_BufferChain( &p_hdr, p_og );
693 694 695
        }
    }

696 697 698 699 700
    /* 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;
    }
701 702 703
    return( p_hdr );
}

Gildas Bazin's avatar
 
Gildas Bazin committed
704 705 706 707 708 709 710 711
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
712 713 714 715 716 717 718 719 720 721 722 723 724 725 726
    /* 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
727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744
    /* 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
745
        ogg_stream_clear( &p_stream->os );
Gildas Bazin's avatar
 
Gildas Bazin committed
746 747 748 749 750 751 752 753 754 755 756 757 758 759
    }

    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
760
        ogg_stream_clear( &p_sys->pp_del_streams[i]->os );
Gildas Bazin's avatar
 
Gildas Bazin committed
761 762 763 764 765
    }

    return( p_hdr );
}

766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
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
787 788 789
/*****************************************************************************
 * Mux: multiplex available data in input fifos into the Ogg bitstream
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
790
static int Mux( sout_mux_t *p_mux )
791
{
Gildas Bazin's avatar
 
Gildas Bazin committed
792 793 794 795
    sout_mux_sys_t *p_sys = p_mux->p_sys;
    sout_buffer_t  *p_og = NULL;
    int            i_stream;
    mtime_t        i_dts;
796

Gildas Bazin's avatar
 
Gildas Bazin committed
797
    if( p_sys->i_add_streams || p_sys->i_del_streams )
798
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
799
        /* Open new ogg stream */
800
        if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 )
801
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
802
            msg_Dbg( p_mux, "waiting for data..." );
803 804
            return( VLC_SUCCESS );
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821

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

        msg_Dbg( p_mux, "writing header" );
Gildas Bazin's avatar
 
Gildas Bazin committed
824 825 826 827
        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;
828
        sout_BufferChain( &p_og, OggCreateHeader( p_mux, i_dts ) );
Gildas Bazin's avatar
 
Gildas Bazin committed
829 830 831 832 833

        /* Write header and/or footer */
        OggSetDate( p_og, i_dts, 0 );
        sout_AccessOutWrite( p_mux->p_access, p_og );
        p_og = NULL;
834 835 836 837
    }

    for( ;; )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
838 839 840 841
        sout_input_t  *p_input;
        ogg_stream_t  *p_stream;
        sout_buffer_t *p_data;
        ogg_packet    op;
842

843
        if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 )
844 845 846
        {
            return( VLC_SUCCESS );
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
847

848 849
        p_input  = p_mux->pp_inputs[i_stream];
        p_stream = (ogg_stream_t*)p_input->p_sys;
Gildas Bazin's avatar
 
Gildas Bazin committed
850
        p_data   = sout_FifoGet( p_input->p_fifo );
851

Gildas Bazin's avatar
 
Gildas Bazin committed
852 853
        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
854 855 856 857 858 859 860 861 862 863
        {
            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++;
864 865 866

        if( p_stream->i_cat == AUDIO_ES )
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
867 868 869 870 871
            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 ) *
Gildas Bazin's avatar
 
Gildas Bazin committed
872
                    p_input->p_fmt->i_sample_rate / I64C(1000000);
Gildas Bazin's avatar
 
Gildas Bazin committed
873 874 875 876 877
            }
            else
            {
                /* number of sample from begining */
                op.granulepos = ( i_dts - p_sys->i_start_dts ) *
Gildas Bazin's avatar
 
Gildas Bazin committed
878
                    p_stream->oggds_header.i_samples_per_unit / I64C(1000000);
Gildas Bazin's avatar
 
Gildas Bazin committed
879
            }
880 881 882
        }
        else if( p_stream->i_cat == VIDEO_ES )
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
883 884
            if( p_stream->i_fourcc == VLC_FOURCC( 't', 'h', 'e', 'o' ) )
            {
Gildas Bazin's avatar
 
Gildas Bazin committed
885 886 887
                /* FIXME, we assume only keyframes and 25fps */
  	        op.granulepos = ( ( i_dts - p_sys->i_start_dts ) * I64C(25)
                    / I64C(1000000) ) << p_stream->i_keyframe_granule_shift;
Gildas Bazin's avatar
 
Gildas Bazin committed
888 889
            }
            else
Gildas Bazin's avatar
 
Gildas Bazin committed
890 891
                op.granulepos = ( i_dts - p_sys->i_start_dts ) * I64C(10) /
                    p_stream->oggds_header.i_time_unit;
892
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
893 894 895 896 897
        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
898

899 900
        ogg_stream_packetin( &p_stream->os, &op );

Gildas Bazin's avatar
 
Gildas Bazin committed
901 902 903 904 905 906 907 908 909 910 911
        if( p_stream->i_cat == SPU_ES )
        {
            /* Subtitles need to be flushed to be sent on time */
            sout_BufferChain( &p_og, OggStreamFlush( p_mux, &p_stream->os,
                                                     p_data->i_dts ) );
        }
        else
        {
            sout_BufferChain( &p_og, OggStreamPageOut( p_mux, &p_stream->os,
                                                       p_data->i_dts ) );
        }
912 913 914 915 916

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

919
            sout_AccessOutWrite( p_mux->p_access, p_og );
920 921 922 923 924 925 926

            p_og = NULL;
        }
        else
        {
            if( p_stream->i_dts < 0 )
            {
927
                p_stream->i_dts = p_data->i_dts;
928 929 930 931
            }
            p_stream->i_length += p_data->i_length;
        }

932
        sout_BufferDelete( p_mux->p_sout, p_data );
933 934 935 936
    }

    return( VLC_SUCCESS );
}