ogg.c 30.3 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
5
 * $Id: ogg.c,v 1.20 2003/11/21 13:01:05 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
 *
 * 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>
Gildas Bazin's avatar
 
Gildas Bazin committed
30 31 32 33

#ifdef HAVE_TIME_H
#   include <time.h>
#endif
34 35 36 37 38 39 40 41 42 43 44 45

#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
46 47
static int  Open   ( vlc_object_t * );
static void Close  ( vlc_object_t * );
48

49 50 51 52
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 * );
53

Gildas Bazin's avatar
 
Gildas Bazin committed
54 55 56
static sout_buffer_t *OggCreateHeader( sout_mux_t *, mtime_t );
static sout_buffer_t *OggCreateFooter( sout_mux_t *, mtime_t );

57 58 59 60 61 62 63 64 65 66 67 68
/*****************************************************************************
 * 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
69
 * Misc declarations
70 71 72
 *****************************************************************************/
#define FREE( p ) if( p ) { free( p ); (p) = NULL; }

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

75 76
#define PACKET_TYPE_HEADER   0x01
#define PACKET_TYPE_COMMENT  0x03
Gildas Bazin's avatar
 
Gildas Bazin committed
77
#define PACKET_IS_SYNCPOINT  0x08
78

79 80 81 82
typedef struct
#ifdef HAVE_ATTRIBUTE_PACKED
    __attribute__((__packed__))
#endif
83 84 85
{
    int32_t i_width;
    int32_t i_height;
Gildas Bazin's avatar
 
Gildas Bazin committed
86
} oggds_header_video_t;
87

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

98 99 100 101
typedef struct
#ifdef HAVE_ATTRIBUTE_PACKED
    __attribute__((__packed__))
#endif
102 103 104 105 106 107 108 109 110 111 112 113 114 115
{
    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;
116 117 118

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

119 120
    union
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
121 122
        oggds_header_video_t video;
        oggds_header_audio_t audio;
123 124
    } header;

125 126
    int32_t i_padding_1; /* Because the original is using MSVC packing style */

Gildas Bazin's avatar
 
Gildas Bazin committed
127
} oggds_header_t;
128

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

#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
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
/*
 * 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
168 169 170 171 172 173 174 175
        /* 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
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
        {
            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
216
    int     i_serial_no;
Gildas Bazin's avatar
 
Gildas Bazin committed
217
    int     i_keyframe_granule_shift; /* Theora only */
Gildas Bazin's avatar
 
Gildas Bazin committed
218 219
    ogg_stream_state os;

Gildas Bazin's avatar
 
Gildas Bazin committed
220 221 222 223
    oggds_header_t oggds_header;

    sout_buffer_t *pp_sout_headers[3];
    int           i_sout_headers;
Gildas Bazin's avatar
 
Gildas Bazin committed
224 225 226 227 228 229 230 231

} ogg_stream_t;

struct sout_mux_sys_t
{
    int     i_streams;

    mtime_t i_start_dts;
Gildas Bazin's avatar
 
Gildas Bazin committed
232
    int     i_next_serial_no;
Gildas Bazin's avatar
 
Gildas Bazin committed
233 234 235 236 237 238 239 240 241

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

242
static void OggSetDate( sout_buffer_t *, mtime_t , mtime_t  );
Gildas Bazin's avatar
 
Gildas Bazin committed
243 244
static sout_buffer_t *OggStreamFlush( sout_mux_t *, ogg_stream_state *,
                                      mtime_t );
245 246

/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
247
 * Open: Open muxer
248 249 250
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
251 252
    sout_mux_t      *p_mux = (sout_mux_t*)p_this;
    sout_mux_sys_t  *p_sys;
253

254
    msg_Info( p_mux, "Open" );
255

256 257
    p_sys                 = malloc( sizeof( sout_mux_sys_t ) );
    p_sys->i_streams      = 0;
Gildas Bazin's avatar
 
Gildas Bazin committed
258 259 260
    p_sys->i_add_streams  = 0;
    p_sys->i_del_streams  = 0;
    p_sys->pp_del_streams = 0;
261

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

Gildas Bazin's avatar
 
Gildas Bazin committed
269 270 271 272 273 274
    /* First serial number is random.
     * (Done like this because on win32 you need to seed the random number
     *  generator once per thread). */
    srand( (unsigned int)time( NULL ) );
    p_sys->i_next_serial_no = rand();

275 276 277 278
    return VLC_SUCCESS;
}

/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
279
 * Close: Finalize ogg bitstream and close muxer
280 281 282
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
{
Gildas Bazin's avatar
 
Gildas Bazin committed
283 284
    sout_mux_t     *p_mux = (sout_mux_t*)p_this;
    sout_mux_sys_t *p_sys = p_mux->p_sys;
285

286
    msg_Info( p_mux, "Close" );
287

Gildas Bazin's avatar
 
Gildas Bazin committed
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
    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 );
    }

313
    free( p_sys );
314 315
}

Gildas Bazin's avatar
 
Gildas Bazin committed
316 317
static int Capability( sout_mux_t *p_mux, int i_query, void *p_args,
                       void *p_answer )
318 319 320 321
{
   switch( i_query )
   {
        case SOUT_MUX_CAP_GET_ADD_STREAM_ANY_TIME:
Gildas Bazin's avatar
 
Gildas Bazin committed
322
            *(vlc_bool_t*)p_answer = VLC_TRUE;
323 324 325 326 327 328
            return( SOUT_MUX_CAP_ERR_OK );
        default:
            return( SOUT_MUX_CAP_ERR_UNIMPLEMENTED );
   }
}

Gildas Bazin's avatar
 
Gildas Bazin committed
329 330 331
/*****************************************************************************
 * AddStream: Add an elementary stream to the muxed stream
 *****************************************************************************/
332
static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
333
{
Gildas Bazin's avatar
 
Gildas Bazin committed
334 335
    sout_mux_sys_t *p_sys = p_mux->p_sys;
    ogg_stream_t   *p_stream;
336

337
    msg_Dbg( p_mux, "adding input" );
Gildas Bazin's avatar
 
Gildas Bazin committed
338 339

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

341 342
    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
343
    p_stream->i_serial_no = p_sys->i_next_serial_no++;
344 345
    p_stream->i_packet_no = 0;

Gildas Bazin's avatar
 
Gildas Bazin committed
346 347
    p_stream->i_sout_headers = 0;

348
    memset( &p_stream->oggds_header, 0, sizeof(p_stream->oggds_header) );
Gildas Bazin's avatar
 
Gildas Bazin committed
349
    p_stream->oggds_header.i_packet_type = PACKET_TYPE_HEADER;
350
    switch( p_input->p_fmt->i_cat )
351
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
352
    case VIDEO_ES:
Gildas Bazin's avatar
 
Gildas Bazin committed
353
        switch( p_stream->i_fourcc )
Gildas Bazin's avatar
 
Gildas Bazin committed
354 355 356
        {
        case VLC_FOURCC( 'm', 'p','4', 'v' ):
        case VLC_FOURCC( 'D', 'I','V', '3' ):
357
            memcpy( p_stream->oggds_header.stream_type, "video", 5 );
Gildas Bazin's avatar
 
Gildas Bazin committed
358
            if( p_stream->i_fourcc == VLC_FOURCC( 'm', 'p','4', 'v' ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
359
            {
Gildas Bazin's avatar
 
Gildas Bazin committed
360
                memcpy( p_stream->oggds_header.sub_type, "XVID", 4 );
Gildas Bazin's avatar
 
Gildas Bazin committed
361
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
362
            else if( p_stream->i_fourcc == VLC_FOURCC( 'D', 'I','V', '3' ) )
363
            {
Gildas Bazin's avatar
 
Gildas Bazin committed
364
                memcpy( p_stream->oggds_header.sub_type, "DIV3", 4 );
365
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
366 367 368
            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
369
                     I64C(10000000)/(int64_t)25 );  // FIXME (25fps)
Gildas Bazin's avatar
 
Gildas Bazin committed
370
            SetQWLE( &p_stream->oggds_header.i_samples_per_unit, 1 );
371
            SetDWLE( &p_stream->oggds_header.i_default_len, 1 ); /* ??? */
Gildas Bazin's avatar
 
Gildas Bazin committed
372 373 374
            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
375
                     p_input->p_fmt->i_width );
Gildas Bazin's avatar
 
Gildas Bazin committed
376
            SetDWLE( &p_stream->oggds_header.header.video.i_height,
Gildas Bazin's avatar
 
Gildas Bazin committed
377
                     p_input->p_fmt->i_height );
Gildas Bazin's avatar
 
Gildas Bazin committed
378
            msg_Dbg( p_mux, "mp4v/div3 stream" );
379
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
380

Gildas Bazin's avatar
 
Gildas Bazin committed
381
        case VLC_FOURCC( 't', 'h', 'e', 'o' ):
Gildas Bazin's avatar
 
Gildas Bazin committed
382 383
            msg_Dbg( p_mux, "theora stream" );
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
384

Gildas Bazin's avatar
 
Gildas Bazin committed
385 386 387 388 389
        default:
            FREE( p_input->p_sys );
            return( VLC_EGENERIC );
        }
        break;
Gildas Bazin's avatar
 
Gildas Bazin committed
390

Gildas Bazin's avatar
 
Gildas Bazin committed
391
    case AUDIO_ES:
Gildas Bazin's avatar
 
Gildas Bazin committed
392
        switch( p_stream->i_fourcc )
Gildas Bazin's avatar
 
Gildas Bazin committed
393 394 395
        {
        case VLC_FOURCC( 'm', 'p','g', 'a' ):
        case VLC_FOURCC( 'a', '5','2', ' ' ):
396
            memcpy( p_stream->oggds_header.stream_type, "audio", 5 );
Gildas Bazin's avatar
 
Gildas Bazin committed
397
            if( p_stream->i_fourcc == VLC_FOURCC( 'm', 'p','g', 'a' ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
398
            {
Gildas Bazin's avatar
 
Gildas Bazin committed
399
                memcpy( p_stream->oggds_header.sub_type, "55  ", 4 );
Gildas Bazin's avatar
 
Gildas Bazin committed
400
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
401
            else if( p_stream->i_fourcc == VLC_FOURCC( 'a', '5','2', ' ' ) )
402
            {
Gildas Bazin's avatar
 
Gildas Bazin committed
403
                memcpy( p_stream->oggds_header.sub_type, "2000", 4 );
404
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
405 406 407
            SetDWLE( &p_stream->oggds_header.i_size,
                     sizeof( oggds_header_t ) - 1);
            SetQWLE( &p_stream->oggds_header.i_time_unit, 0 /* not used */ );
408
            SetDWLE( &p_stream->oggds_header.i_default_len, 1 );
Gildas Bazin's avatar
 
Gildas Bazin committed
409 410
            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
411
                     p_input->p_fmt->i_sample_rate );
Gildas Bazin's avatar
 
Gildas Bazin committed
412 413
            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
414
                     p_input->p_fmt->i_channels );
Gildas Bazin's avatar
 
Gildas Bazin committed
415
            SetDWLE( &p_stream->oggds_header.header.audio.i_block_align,
Gildas Bazin's avatar
 
Gildas Bazin committed
416
                     p_input->p_fmt->i_block_align );
Gildas Bazin's avatar
 
Gildas Bazin committed
417
            SetDWLE( &p_stream->oggds_header.header.audio.i_avgbytespersec, 0);
Gildas Bazin's avatar
 
Gildas Bazin committed
418
            msg_Dbg( p_mux, "mpga/a52 stream" );
419
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
420 421

        case VLC_FOURCC( 'v', 'o', 'r', 'b' ):
Gildas Bazin's avatar
 
Gildas Bazin committed
422 423
            msg_Dbg( p_mux, "vorbis stream" );
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
424

Gildas Bazin's avatar
 
Gildas Bazin committed
425 426 427 428
        case VLC_FOURCC( 's', 'p', 'x', ' ' ):
            msg_Dbg( p_mux, "speex stream" );
            break;

429
        default:
430
            FREE( p_input->p_sys );
431
            return( VLC_EGENERIC );
Gildas Bazin's avatar
 
Gildas Bazin committed
432 433 434
        }
        break;

Gildas Bazin's avatar
 
Gildas Bazin committed
435 436 437 438
    case SPU_ES:
        switch( p_stream->i_fourcc )
        {
        case VLC_FOURCC( 's', 'u','b', 't' ):
439
            memcpy( p_stream->oggds_header.stream_type, "text", 4 );
Gildas Bazin's avatar
 
Gildas Bazin committed
440
            msg_Dbg( p_mux, "subtitles stream" );
Gildas Bazin's avatar
 
Gildas Bazin committed
441 442 443 444 445 446 447
            break;

        default:
            FREE( p_input->p_sys );
            return( VLC_EGENERIC );
        }
        break;
Gildas Bazin's avatar
 
Gildas Bazin committed
448 449 450
    default:
        FREE( p_input->p_sys );
        return( VLC_EGENERIC );
451 452
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
453 454 455
    p_stream->b_new = VLC_TRUE;

    p_sys->i_add_streams++;
456 457 458 459

    return( VLC_SUCCESS );
}

Gildas Bazin's avatar
 
Gildas Bazin committed
460 461 462
/*****************************************************************************
 * DelStream: Delete an elementary stream from the muxed stream
 *****************************************************************************/
463
static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
464
{
Gildas Bazin's avatar
 
Gildas Bazin committed
465 466 467
    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;
468

469
    msg_Dbg( p_mux, "removing input" );
470 471

    /* flush all remaining data */
472
    if( p_input->p_sys )
473
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
474 475
        int i;

Gildas Bazin's avatar
 
Gildas Bazin committed
476
        if( ( p_og = OggStreamFlush( p_mux, &p_stream->os, 0 ) ) )
477 478 479 480
        {
            OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
            sout_AccessOutWrite( p_mux->p_access, p_og );
        }
481

Gildas Bazin's avatar
 
Gildas Bazin committed
482 483 484 485 486 487
        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
488 489
        /* move input in delete queue */
        if( !p_stream->b_new )
490
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
491 492 493 494
            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;
495 496 497
        }
        else
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
498 499 500 501
            /* Wasn't already added so get rid of it */
            ogg_stream_clear( &p_stream->os );
            FREE( p_stream );
            p_sys->i_add_streams--;
502 503
        }
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
504 505 506 507

    p_input->p_sys = NULL;

    return( 0 );
508 509
}

Gildas Bazin's avatar
 
Gildas Bazin committed
510 511 512
/*****************************************************************************
 * Ogg bitstream manipulation routines
 *****************************************************************************/
513
static sout_buffer_t *OggStreamFlush( sout_mux_t *p_mux,
Gildas Bazin's avatar
 
Gildas Bazin committed
514
                                      ogg_stream_state *p_os, mtime_t i_pts )
515
{
Gildas Bazin's avatar
 
Gildas Bazin committed
516 517
    sout_buffer_t *p_og, *p_og_first = NULL;
    ogg_page      og;
518 519 520 521 522 523 524 525 526 527

    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
528

529
        i_size = og.header_len + og.body_len;
530
        p_og = sout_BufferNew( p_mux->p_sout, i_size);
531

Gildas Bazin's avatar
 
Gildas Bazin committed
532 533
        memcpy( p_og->p_buffer, og.header, og.header_len );
        memcpy( p_og->p_buffer + og.header_len, og.body, og.body_len );
534 535 536 537 538 539 540 541 542 543 544 545
        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
546

547
static sout_buffer_t *OggStreamPageOut( sout_mux_t *p_mux,
Gildas Bazin's avatar
 
Gildas Bazin committed
548
                                        ogg_stream_state *p_os, mtime_t i_pts )
549
{
Gildas Bazin's avatar
 
Gildas Bazin committed
550 551
    sout_buffer_t *p_og, *p_og_first = NULL;
    ogg_page      og;
552 553 554 555 556 557 558 559 560 561

    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
562

563
        i_size = og.header_len + og.body_len;
564
        p_og = sout_BufferNew( p_mux->p_sout, i_size);
565

Gildas Bazin's avatar
 
Gildas Bazin committed
566 567
        memcpy( p_og->p_buffer, og.header, og.header_len );
        memcpy( p_og->p_buffer + og.header_len, og.body, og.body_len );
568 569 570 571 572 573 574 575 576 577 578 579
        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
580

581
static sout_buffer_t *OggCreateHeader( sout_mux_t *p_mux, mtime_t i_dts )
582 583 584 585 586 587
{
    sout_buffer_t *p_hdr = NULL;
    sout_buffer_t *p_og;
    ogg_packet    op;
    int i;

Gildas Bazin's avatar
 
Gildas Bazin committed
588 589
    /* 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. */
590
    for( i = 0; i < p_mux->i_nb_inputs; i++ )
591
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
592 593
        ogg_stream_t *p_stream = (ogg_stream_t*)p_mux->pp_inputs[i]->p_sys;
        p_stream->b_new = VLC_FALSE;
594

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

Gildas Bazin's avatar
 
Gildas Bazin committed
598
        ogg_stream_init( &p_stream->os, p_stream->i_serial_no );
Gildas Bazin's avatar
 
Gildas Bazin committed
599 600
        p_stream->i_packet_no = 0;

Gildas Bazin's avatar
 
Gildas Bazin committed
601
        if( p_stream->i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) ||
Gildas Bazin's avatar
 
Gildas Bazin committed
602
            p_stream->i_fourcc == VLC_FOURCC( 's', 'p', 'x', ' ' ) ||
Gildas Bazin's avatar
 
Gildas Bazin committed
603
            p_stream->i_fourcc == VLC_FOURCC( 't', 'h', 'e', 'o' ) )
604
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
605
            /* Special case, headers are already there in the
Gildas Bazin's avatar
 
Gildas Bazin committed
606
             * incoming stream or we backed them up earlier */
Gildas Bazin's avatar
 
Gildas Bazin committed
607

Gildas Bazin's avatar
 
Gildas Bazin committed
608
            /* first packet in order: vorbis/speex/theora info */
Gildas Bazin's avatar
 
Gildas Bazin committed
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
            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
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639

            /* 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;
                }
            }
640 641 642 643
        }
        else
        {
            /* ds header */
Gildas Bazin's avatar
 
Gildas Bazin committed
644 645
            op.packet = (uint8_t*)&p_stream->oggds_header;
            op.bytes  = sizeof( oggds_header_t );
646 647 648 649 650
            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
651
            p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
Gildas Bazin's avatar
 
Gildas Bazin committed
652 653 654 655 656 657 658 659
        }

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

Gildas Bazin's avatar
 
Gildas Bazin committed
662
        if( p_stream->i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) ||
Gildas Bazin's avatar
 
Gildas Bazin committed
663
            p_stream->i_fourcc == VLC_FOURCC( 's', 'p', 'x', ' ' ) ||
Gildas Bazin's avatar
 
Gildas Bazin committed
664
            p_stream->i_fourcc == VLC_FOURCC( 't', 'h', 'e', 'o' ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
665 666 667 668 669 670
        {
            /* 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
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
                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
690 691 692 693 694 695 696
            }
        }
        else
        {
            uint8_t com[128];
            int     i_com;

697 698
            /* comment */
            com[0] = PACKET_TYPE_COMMENT;
Gildas Bazin's avatar
 
Gildas Bazin committed
699
            i_com = snprintf( &com[1], 128, VERSION" stream output" ) + 1;
700 701 702 703 704 705 706
            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
707 708
            p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
            sout_BufferChain( &p_hdr, p_og );
709
        }
710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725

        /* Special case for mp4v */
        if( p_stream->i_fourcc == VLC_FOURCC( 'm', 'p', '4', 'v' ) &&
            p_mux->pp_inputs[i]->p_fmt->i_extra_data )
        {
            /* Send a packet with the VOL data */
            op.bytes  = p_mux->pp_inputs[i]->p_fmt->i_extra_data;
            op.packet = p_mux->pp_inputs[i]->p_fmt->p_extra_data;
            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_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
            sout_BufferChain( &p_hdr, p_og );
        }
726 727
    }

728 729 730 731 732
    /* 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;
    }
733 734 735
    return( p_hdr );
}

Gildas Bazin's avatar
 
Gildas Bazin committed
736 737 738 739 740 741 742 743
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
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
    /* 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
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
    /* 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
777
        ogg_stream_clear( &p_stream->os );
Gildas Bazin's avatar
 
Gildas Bazin committed
778 779 780 781 782 783 784 785 786 787 788 789 790 791
    }

    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
792
        ogg_stream_clear( &p_sys->pp_del_streams[i]->os );
Gildas Bazin's avatar
 
Gildas Bazin committed
793 794 795 796 797
    }

    return( p_hdr );
}

798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
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
819 820 821
/*****************************************************************************
 * Mux: multiplex available data in input fifos into the Ogg bitstream
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
822
static int Mux( sout_mux_t *p_mux )
823
{
Gildas Bazin's avatar
 
Gildas Bazin committed
824 825 826 827
    sout_mux_sys_t *p_sys = p_mux->p_sys;
    sout_buffer_t  *p_og = NULL;
    int            i_stream;
    mtime_t        i_dts;
828

Gildas Bazin's avatar
 
Gildas Bazin committed
829
    if( p_sys->i_add_streams || p_sys->i_del_streams )
830
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
831
        /* Open new ogg stream */
832
        if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 )
833
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
834
            msg_Dbg( p_mux, "waiting for data..." );
835 836
            return( VLC_SUCCESS );
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853

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

        msg_Dbg( p_mux, "writing header" );
Gildas Bazin's avatar
 
Gildas Bazin committed
856 857 858 859
        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;
860
        sout_BufferChain( &p_og, OggCreateHeader( p_mux, i_dts ) );
Gildas Bazin's avatar
 
Gildas Bazin committed
861 862 863 864 865

        /* Write header and/or footer */
        OggSetDate( p_og, i_dts, 0 );
        sout_AccessOutWrite( p_mux->p_access, p_og );
        p_og = NULL;
866 867 868 869
    }

    for( ;; )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
870 871 872 873
        sout_input_t  *p_input;
        ogg_stream_t  *p_stream;
        sout_buffer_t *p_data;
        ogg_packet    op;
874

875
        if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 )
876 877 878
        {
            return( VLC_SUCCESS );
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
879

880 881
        p_input  = p_mux->pp_inputs[i_stream];
        p_stream = (ogg_stream_t*)p_input->p_sys;
Gildas Bazin's avatar
 
Gildas Bazin committed
882
        p_data   = sout_FifoGet( p_input->p_fifo );
883

Gildas Bazin's avatar
 
Gildas Bazin committed
884
        if( p_stream->i_fourcc != VLC_FOURCC( 'v', 'o', 'r', 'b' ) &&
Gildas Bazin's avatar
 
Gildas Bazin committed
885
            p_stream->i_fourcc != VLC_FOURCC( 's', 'p', 'x', ' ' ) &&
Gildas Bazin's avatar
 
Gildas Bazin committed
886
            p_stream->i_fourcc != VLC_FOURCC( 't', 'h', 'e', 'o' ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
887 888 889 890 891 892 893 894 895 896
        {
            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++;
897 898 899

        if( p_stream->i_cat == AUDIO_ES )
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
900 901
            if( p_stream->i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) ||
                p_stream->i_fourcc == VLC_FOURCC( 's', 'p', 'x', ' ' ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
902 903 904 905
            {
                /* 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
906
                    p_input->p_fmt->i_sample_rate / I64C(1000000);
Gildas Bazin's avatar
 
Gildas Bazin committed
907 908 909 910 911
            }
            else
            {
                /* number of sample from begining */
                op.granulepos = ( i_dts - p_sys->i_start_dts ) *
Gildas Bazin's avatar
 
Gildas Bazin committed
912
                    p_stream->oggds_header.i_samples_per_unit / I64C(1000000);
Gildas Bazin's avatar
 
Gildas Bazin committed
913
            }
914 915 916
        }
        else if( p_stream->i_cat == VIDEO_ES )
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
917 918
            if( p_stream->i_fourcc == VLC_FOURCC( 't', 'h', 'e', 'o' ) )
            {
Gildas Bazin's avatar
 
Gildas Bazin committed
919
                /* FIXME, we assume only keyframes and 25fps */
Gildas Bazin's avatar
 
Gildas Bazin committed
920
                op.granulepos = ( ( i_dts - p_sys->i_start_dts ) * I64C(25)
Gildas Bazin's avatar
 
Gildas Bazin committed
921
                    / I64C(1000000) ) << p_stream->i_keyframe_granule_shift;
Gildas Bazin's avatar
 
Gildas Bazin committed
922 923
            }
            else
Gildas Bazin's avatar
 
Gildas Bazin committed
924 925
                op.granulepos = ( i_dts - p_sys->i_start_dts ) * I64C(10) /
                    p_stream->oggds_header.i_time_unit;
926
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
927 928 929 930 931
        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
932

933 934
        ogg_stream_packetin( &p_stream->os, &op );

Gildas Bazin's avatar
 
Gildas Bazin committed
935 936
        if( p_stream->i_cat == SPU_ES ||
            p_stream->i_fourcc == VLC_FOURCC( 's', 'p', 'x', ' ' ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
937
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
938 939
            /* Subtitles or Speex packets are quite small so they 
             * need to be flushed to be sent on time */
Gildas Bazin's avatar
 
Gildas Bazin committed
940 941 942 943 944 945 946 947
            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 ) );
        }
948 949 950 951 952

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

955
            sout_AccessOutWrite( p_mux->p_access, p_og );
956 957 958 959 960 961 962

            p_og = NULL;
        }
        else
        {
            if( p_stream->i_dts < 0 )
            {
963
                p_stream->i_dts = p_data->i_dts;
964 965 966 967
            }
            p_stream->i_length += p_data->i_length;
        }

968
        sout_BufferDelete( p_mux->p_sout, p_data );
969 970 971 972
    }

    return( VLC_SUCCESS );
}