ogg.c 20 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.9 2003/09/25 23:09:41 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 43 44 45
 *
 * 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
 *****************************************************************************/
static int     Open   ( vlc_object_t * );
static void    Close  ( vlc_object_t * );

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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71

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

/*****************************************************************************
 *
 *****************************************************************************/
#define FREE( p ) if( p ) { free( p ); (p) = NULL; }

#define PACKET_TYPE_HEADER   0x01
#define PACKET_TYPE_COMMENT  0x03

#define PACKET_IS_SYNCPOINT      0x08

72 73 74 75
typedef struct
#ifdef HAVE_ATTRIBUTE_PACKED
    __attribute__((__packed__))
#endif
76 77 78 79 80
{
    int32_t i_width;
    int32_t i_height;
} ogg_stream_header_video_t;

81 82 83 84
typedef struct
#ifdef HAVE_ATTRIBUTE_PACKED
    __attribute__((__packed__))
#endif
85 86 87 88 89 90
{
    int16_t i_channels;
    int16_t i_block_align;
    int32_t i_avgbytespersec;
} ogg_stream_header_audio_t;

91 92 93 94
typedef struct
#ifdef HAVE_ATTRIBUTE_PACKED
    __attribute__((__packed__))
#endif
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
{
    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;


typedef struct
{
    int i_cat;
    int i_fourcc;

    ogg_stream_header_t header;

    int i_packet_no;

    mtime_t             i_dts;
    mtime_t             i_length;
    ogg_stream_state    os;

} ogg_stream_t;

134
struct sout_mux_sys_t
135
{
136 137
    int     b_write_header;
    int     i_streams;
138

139
    mtime_t i_start_dts;
140
};
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164

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

static void OggSetDate( sout_buffer_t *, mtime_t , mtime_t  );
Gildas Bazin's avatar
 
Gildas Bazin committed
165 166
static sout_buffer_t *OggStreamFlush( sout_mux_t *, ogg_stream_state *,
                                      mtime_t );
167 168 169 170 171 172

/*****************************************************************************
 * Open:
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
173 174
    sout_mux_t      *p_mux = (sout_mux_t*)p_this;
    sout_mux_sys_t  *p_sys;
175

176
    msg_Info( p_mux, "Open" );
177

178 179 180
    p_sys                 = malloc( sizeof( sout_mux_sys_t ) );
    p_sys->i_streams      = 0;
    p_sys->b_write_header = VLC_TRUE;
181

182 183 184 185 186 187
    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;
188 189 190 191 192 193 194 195 196 197

    return VLC_SUCCESS;
}

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

static void Close( vlc_object_t * p_this )
{
198 199
    sout_mux_t      *p_mux = (sout_mux_t*)p_this;
    sout_mux_sys_t  *p_sys = p_mux->p_sys;
200

201
    msg_Info( p_mux, "Close" );
202

203
    free( p_sys );
204 205
}

Gildas Bazin's avatar
 
Gildas Bazin committed
206 207
static int Capability( sout_mux_t *p_mux, int i_query, void *p_args,
                       void *p_answer )
208 209 210 211 212 213 214 215 216 217 218
{
   switch( i_query )
   {
        case SOUT_MUX_CAP_GET_ADD_STREAM_ANY_TIME:
            *(vlc_bool_t*)p_answer = VLC_FALSE;
            return( SOUT_MUX_CAP_ERR_OK );
        default:
            return( SOUT_MUX_CAP_ERR_UNIMPLEMENTED );
   }
}

219
static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
220
{
221
    sout_mux_sys_t  *p_sys = p_mux->p_sys;
222 223
    ogg_stream_t        *p_stream;

224 225
    msg_Dbg( p_mux, "adding input" );
    p_input->p_sys = (void*)p_stream = malloc( sizeof( ogg_stream_t ) );
226

227 228
    p_stream->i_cat       = p_input->p_fmt->i_cat;
    p_stream->i_fourcc    = p_input->p_fmt->i_fourcc;
229 230 231
    p_stream->i_packet_no = 0;

    p_stream->header.i_packet_type = PACKET_TYPE_HEADER;
232
    switch( p_input->p_fmt->i_cat )
233
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
234 235 236 237 238 239 240 241 242 243 244 245
    case VIDEO_ES:
        switch( p_input->p_fmt->i_fourcc )
        {
        case VLC_FOURCC( 'm', 'p','4', 'v' ):
        case VLC_FOURCC( 'D', 'I','V', '3' ):
            memcpy( p_stream->header.stream_type, "video    ", 8 );
            if( p_input->p_fmt->i_fourcc == VLC_FOURCC( 'm', 'p','4', 'v' ) )
            {
                memcpy( p_stream->header.sub_type, "XVID", 4 );
            }
            else if( p_input->p_fmt->i_fourcc ==
                     VLC_FOURCC( 'D', 'I','V', '3' ) )
246
            {
Gildas Bazin's avatar
 
Gildas Bazin committed
247
                memcpy( p_stream->header.sub_type, "DIV3", 4 );
248
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
249 250 251 252 253 254 255 256 257 258 259 260 261 262
            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 );
263
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
264

Gildas Bazin's avatar
 
Gildas Bazin committed
265 266 267 268
        case VLC_FOURCC( 't', 'h', 'e', 'o' ):
          msg_Dbg( p_mux, "theora stream" );
          break;

Gildas Bazin's avatar
 
Gildas Bazin committed
269 270 271 272 273
        default:
            FREE( p_input->p_sys );
            return( VLC_EGENERIC );
        }
        break;
Gildas Bazin's avatar
 
Gildas Bazin committed
274

Gildas Bazin's avatar
 
Gildas Bazin committed
275 276 277 278 279 280 281 282 283 284 285 286
    case AUDIO_ES:
        switch( p_input->p_fmt->i_fourcc )
        {
        case VLC_FOURCC( 'm', 'p','g', 'a' ):
        case VLC_FOURCC( 'a', '5','2', ' ' ):
            memcpy( p_stream->header.stream_type, "audio    ", 8 );
            if( p_input->p_fmt->i_fourcc == VLC_FOURCC( 'm', 'p','g', 'a' ) )
            {
                memcpy( p_stream->header.sub_type, "55  ", 4 );
            }
            else if( p_input->p_fmt->i_fourcc ==
                     VLC_FOURCC( 'a', '5','2', ' ' ) )
287
            {
Gildas Bazin's avatar
 
Gildas Bazin committed
288
                memcpy( p_stream->header.sub_type, "2000", 4 );
289
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
290 291 292 293 294 295 296 297 298 299 300 301 302
            SetDWLE( &p_stream->header.i_size,
                     sizeof( ogg_stream_header_t ) - 1);
            SetQWLE( &p_stream->header.i_time_unit, 1000000 );  /* is it used ? */
            SetDWLE( &p_stream->header.i_default_len, 0 );      /* ??? */
            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 );
303
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
304 305 306 307

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

309
        default:
310
            FREE( p_input->p_sys );
311
            return( VLC_EGENERIC );
Gildas Bazin's avatar
 
Gildas Bazin committed
312 313 314 315 316 317
        }
        break;

    default:
        FREE( p_input->p_sys );
        return( VLC_EGENERIC );
318 319
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
320
    ogg_stream_init( &p_stream->os, rand () );
321

322
    p_sys->i_streams++;
323 324 325
    return( VLC_SUCCESS );
}

326
static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
327
{
328
    ogg_stream_t        *p_stream = (ogg_stream_t*)p_input->p_sys;
329 330
    sout_buffer_t       *p_og;

331
    msg_Dbg( p_mux, "removing input" );
332 333 334

    /* flush all remaining data */

335
    if( p_input->p_sys )
336
    {
337 338 339 340
        p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
        if( p_og )
        {
            OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
341

342 343
            sout_AccessOutWrite( p_mux->p_access, p_og );
        }
344

345
        ogg_stream_clear( &p_stream->os );
346

347 348
        FREE( p_input->p_sys );
    }
349 350 351 352 353 354
    return( 0 );
}

/*
 * TODO  move this function to src/stream_output.c (used by nearly all muxers)
 */
Gildas Bazin's avatar
 
Gildas Bazin committed
355
static int MuxGetStream( sout_mux_t *p_mux, int *pi_stream, mtime_t *pi_dts )
356 357 358 359 360
{
    mtime_t i_dts;
    int     i_stream;
    int     i;

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

365
        p_fifo = p_mux->pp_inputs[i]->p_fifo;
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393

        if( p_fifo->i_depth > 1 )
        {
            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
        {
            return( -1 ); // wait that all fifo have at least 2 packets
        }
    }
    if( pi_stream )
    {
        *pi_stream = i_stream;
    }
    if( pi_dts )
    {
        *pi_dts = i_dts;
    }
    return( i_stream );
}

394
static sout_buffer_t *OggStreamFlush( sout_mux_t *p_mux,
Gildas Bazin's avatar
 
Gildas Bazin committed
395
                                      ogg_stream_state *p_os, mtime_t i_pts )
396
{
Gildas Bazin's avatar
 
Gildas Bazin committed
397 398
    sout_buffer_t *p_og, *p_og_first = NULL;
    ogg_page      og;
399 400 401 402 403 404 405 406 407 408 409

    for( ;; )
    {
        /* flush all data */
        int i_result;
        int i_size;
        if( ( i_result = ogg_stream_flush( p_os, &og ) ) == 0 )
        {
            break;
        }
        i_size = og.header_len + og.body_len;
410
        p_og = sout_BufferNew( p_mux->p_sout, i_size);
411

Gildas Bazin's avatar
 
Gildas Bazin committed
412 413
        memcpy( p_og->p_buffer, og.header, og.header_len );
        memcpy( p_og->p_buffer + og.header_len, og.body, og.body_len );
414 415 416 417 418 419 420 421 422 423 424 425
        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
426

427
static sout_buffer_t *OggStreamPageOut( sout_mux_t *p_mux,
Gildas Bazin's avatar
 
Gildas Bazin committed
428
                                        ogg_stream_state *p_os, mtime_t i_pts )
429
{
Gildas Bazin's avatar
 
Gildas Bazin committed
430 431
    sout_buffer_t *p_og, *p_og_first = NULL;
    ogg_page      og;
432 433 434 435 436 437 438 439 440 441 442

    for( ;; )
    {
        /* flush all data */
        int i_result;
        int i_size;
        if( ( i_result = ogg_stream_pageout( p_os, &og ) ) == 0 )
        {
            break;
        }
        i_size = og.header_len + og.body_len;
443
        p_og = sout_BufferNew( p_mux->p_sout, i_size);
444

Gildas Bazin's avatar
 
Gildas Bazin committed
445 446
        memcpy( p_og->p_buffer, og.header, og.header_len );
        memcpy( p_og->p_buffer + og.header_len, og.body, og.body_len );
447 448 449 450 451 452 453 454 455 456 457 458
        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
459

460
static sout_buffer_t *OggCreateHeader( sout_mux_t *p_mux, mtime_t i_dts )
461 462 463 464 465 466
{
    sout_buffer_t *p_hdr = NULL;
    sout_buffer_t *p_og;
    ogg_packet    op;
    int i;

Gildas Bazin's avatar
 
Gildas Bazin committed
467 468
    /* 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. */
469
    for( i = 0; i < p_mux->i_nb_inputs; i++ )
470 471 472
    {
        ogg_stream_t *p_stream;

473
        p_stream = (ogg_stream_t*)p_mux->pp_inputs[i]->p_sys;
474

Gildas Bazin's avatar
 
Gildas Bazin committed
475 476
        if( p_stream->i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) ||
            p_stream->i_fourcc == VLC_FOURCC( 't', 'h', 'e', 'o' ) )
477
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
478 479 480 481 482 483 484 485 486 487 488 489
            /* 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 );
490 491 492 493 494 495 496 497 498 499 500
        }
        else
        {
            /* ds header */
            op.packet = (uint8_t*)&p_stream->header;
            op.bytes  = sizeof( ogg_stream_t );
            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
501 502 503 504 505 506 507 508 509 510 511 512 513
        }

        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++ )
    {
        ogg_stream_t *p_stream;

        p_stream = (ogg_stream_t*)p_mux->pp_inputs[i]->p_sys;

Gildas Bazin's avatar
 
Gildas Bazin committed
514 515
        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
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
        {
            /* 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;

538 539 540 541 542 543 544 545 546 547 548 549
            /* comment */
            com[0] = PACKET_TYPE_COMMENT;
            i_com = snprintf( &com[1], 128, "VLC 0.5.x stream output" ) + 1;
            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 );
        }

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

554 555 556 557 558
    /* 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;
    }
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
    return( p_hdr );
}

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
583
static int Mux( sout_mux_t *p_mux )
584
{
585
    sout_mux_sys_t      *p_sys  = p_mux->p_sys;
586
    sout_buffer_t       *p_og = NULL;
Gildas Bazin's avatar
 
Gildas Bazin committed
587
    int                 i_stream;
588 589
    mtime_t             i_dts;

590
    if( p_sys->b_write_header )
591
    {
592
        if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 )
593
        {
594
            msg_Dbg( p_mux, "waiting data..." );
595 596
            return( VLC_SUCCESS );
        }
597 598 599
        p_sys->i_start_dts = i_dts;

        msg_Dbg( p_mux, "writing header" );
600 601
        sout_BufferChain( &p_og, OggCreateHeader( p_mux, i_dts ) );
        p_sys->b_write_header = VLC_FALSE;
602 603 604 605 606 607 608 609 610
    }

    for( ;; )
    {
        sout_input_t    *p_input;
        ogg_stream_t    *p_stream;
        sout_buffer_t   *p_data;
        ogg_packet          op;

611
        if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 )
612
        {
613
            //msg_Dbg( p_mux, "waiting data..." );
614 615
            return( VLC_SUCCESS );
        }
616
        //msg_Dbg( p_mux, "doing job" );
617

Gildas Bazin's avatar
 
Gildas Bazin committed
618 619
        if( p_sys->i_start_dts <= 0 ) p_sys->i_start_dts = i_dts;

620 621
        p_input  = p_mux->pp_inputs[i_stream];
        p_stream = (ogg_stream_t*)p_input->p_sys;
622 623 624

        p_data  = sout_FifoGet( p_input->p_fifo );

Gildas Bazin's avatar
 
Gildas Bazin committed
625 626 627 628 629 630 631 632 633 634 635
        if( p_stream->i_fourcc != VLC_FOURCC( 'v', 'o', 'r', 'b' ) )
        {
            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++;
636 637 638

        if( p_stream->i_cat == AUDIO_ES )
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
639 640 641 642 643 644 645 646 647 648 649 650 651
            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;
            }
652 653 654
        }
        else if( p_stream->i_cat == VIDEO_ES )
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
655 656 657 658 659 660
            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;
661
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
662

663 664
        ogg_stream_packetin( &p_stream->os, &op );

Gildas Bazin's avatar
 
Gildas Bazin committed
665 666
        sout_BufferChain( &p_og, OggStreamPageOut( p_mux, &p_stream->os,
                                                   p_data->i_dts ) );
667 668 669 670 671

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

674
            sout_AccessOutWrite( p_mux->p_access, p_og );
675 676 677 678 679 680 681

            p_og = NULL;
        }
        else
        {
            if( p_stream->i_dts < 0 )
            {
682
                p_stream->i_dts = p_data->i_dts;
683 684 685 686
            }
            p_stream->i_length += p_data->i_length;
        }

687
        sout_BufferDelete( p_mux->p_sout, p_data );
688 689 690 691
    }

    return( VLC_SUCCESS );
}