]> git.sesse.net Git - vlc/blob - modules/mux/ogg.c
* modules/stream_out/transcode.c: couple of fixes.
[vlc] / modules / mux / ogg.c
1 /*****************************************************************************
2  * ogg.c: ogg muxer module for vlc
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: ogg.c,v 1.16 2003/10/09 18:53:01 gbazin Exp $
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          Gildas Bazin <gbazin@netcourrier.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/types.h>
31
32 #include <vlc/vlc.h>
33 #include <vlc/input.h>
34 #include <vlc/sout.h>
35
36 #include "codecs.h"
37
38 #include <ogg/ogg.h>
39
40 /*****************************************************************************
41  * Exported prototypes
42  *****************************************************************************/
43 static int  Open   ( vlc_object_t * );
44 static void Close  ( vlc_object_t * );
45
46 static int Capability(sout_mux_t *, int, void *, void * );
47 static int AddStream( sout_mux_t *, sout_input_t * );
48 static int DelStream( sout_mux_t *, sout_input_t * );
49 static int Mux      ( sout_mux_t * );
50
51 static sout_buffer_t *OggCreateHeader( sout_mux_t *, mtime_t );
52 static sout_buffer_t *OggCreateFooter( sout_mux_t *, mtime_t );
53
54 /*****************************************************************************
55  * Module descriptor
56  *****************************************************************************/
57 vlc_module_begin();
58     set_description( _("Ogg/ogm muxer") );
59     set_capability( "sout mux", 10 );
60     add_shortcut( "ogg" );
61     add_shortcut( "ogm" );
62     set_callbacks( Open, Close );
63 vlc_module_end();
64
65 /*****************************************************************************
66  * Misc declarations
67  *****************************************************************************/
68 #define FREE( p ) if( p ) { free( p ); (p) = NULL; }
69
70 /* Structures used for OggDS headers used in ogm files */
71
72 #define PACKET_TYPE_HEADER   0x01
73 #define PACKET_TYPE_COMMENT  0x03
74 #define PACKET_IS_SYNCPOINT  0x08
75
76 typedef struct
77 #ifdef HAVE_ATTRIBUTE_PACKED
78     __attribute__((__packed__))
79 #endif
80 {
81     int32_t i_width;
82     int32_t i_height;
83 } oggds_header_video_t;
84
85 typedef struct
86 #ifdef HAVE_ATTRIBUTE_PACKED
87     __attribute__((__packed__))
88 #endif
89 {
90     int16_t i_channels;
91     int16_t i_block_align;
92     int32_t i_avgbytespersec;
93 } oggds_header_audio_t;
94
95 typedef struct
96 #ifdef HAVE_ATTRIBUTE_PACKED
97     __attribute__((__packed__))
98 #endif
99 {
100     uint8_t i_packet_type;
101
102     char stream_type[8];
103     char sub_type[4];
104
105     int32_t i_size;
106
107     int64_t i_time_unit;
108     int64_t i_samples_per_unit;
109     int32_t i_default_len;
110
111     int32_t i_buffer_size;
112     int16_t i_bits_per_sample;
113
114     int16_t i_padding_0; /* Because the original is using MSVC packing style */
115
116     union
117     {
118         oggds_header_video_t video;
119         oggds_header_audio_t audio;
120     } header;
121
122     int32_t i_padding_1; /* Because the original is using MSVC packing style */
123
124 } oggds_header_t;
125
126 /* Helper writer functions */
127
128 #define SetWLE( p, v ) _SetWLE( (uint8_t*)p, v)
129 static void _SetWLE( uint8_t *p, uint16_t i_dw )
130 {
131     p[1] = ( i_dw >>  8 )&0xff;
132     p[0] = ( i_dw       )&0xff;
133 }
134
135 #define SetDWLE( p, v ) _SetDWLE( (uint8_t*)p, v)
136 static void _SetDWLE( uint8_t *p, uint32_t i_dw )
137 {
138     p[3] = ( i_dw >> 24 )&0xff;
139     p[2] = ( i_dw >> 16 )&0xff;
140     p[1] = ( i_dw >>  8 )&0xff;
141     p[0] = ( i_dw       )&0xff;
142 }
143 #define SetQWLE( p, v ) _SetQWLE( (uint8_t*)p, v)
144 static void _SetQWLE( uint8_t *p, uint64_t i_qw )
145 {
146     SetDWLE( p,   i_qw&0xffffffff );
147     SetDWLE( p+4, ( i_qw >> 32)&0xffffffff );
148 }
149
150 /*
151  * TODO  move this function to src/stream_output.c (used by nearly all muxers)
152  */
153 static int MuxGetStream( sout_mux_t *p_mux, int *pi_stream, mtime_t *pi_dts )
154 {
155     mtime_t i_dts;
156     int     i_stream;
157     int     i;
158
159     for( i = 0, i_dts = 0, i_stream = -1; i < p_mux->i_nb_inputs; i++ )
160     {
161         sout_fifo_t  *p_fifo;
162
163         p_fifo = p_mux->pp_inputs[i]->p_fifo;
164
165         /* We don't really need to have anything in the SPU fifo */
166         if( p_mux->pp_inputs[i]->p_fmt->i_cat == SPU_ES &&
167             p_fifo->i_depth == 0 ) continue;
168
169         if( p_fifo->i_depth > 2 ||
170             /* Special case for SPUs */
171             ( p_mux->pp_inputs[i]->p_fmt->i_cat == SPU_ES &&
172               p_fifo->i_depth > 0 ) )
173         {
174             sout_buffer_t *p_buf;
175
176             p_buf = sout_FifoShow( p_fifo );
177             if( p_buf->i_dts ) // To ignore vorbis and theora header packets
178             if( i_stream < 0 || p_buf->i_dts < i_dts )
179             {
180                 i_dts = p_buf->i_dts;
181                 i_stream = i;
182             }
183         }
184         else
185         {
186             // wait that all fifo have at least 3 packets (3 vorbis headers)
187             return( -1 );
188         }
189     }
190     if( pi_stream )
191     {
192         *pi_stream = i_stream;
193     }
194     if( pi_dts )
195     {
196         *pi_dts = i_dts;
197     }
198     return( i_stream );
199 }
200
201 /*****************************************************************************
202  * Definitions of structures and functions used by this plugins 
203  *****************************************************************************/
204 typedef struct
205 {
206     int i_cat;
207     int i_fourcc;
208
209     int b_new;
210
211     mtime_t i_dts;
212     mtime_t i_length;
213     int     i_packet_no;
214     int     i_serial_no;
215     int     i_keyframe_granule_shift; /* Theora only */
216     ogg_stream_state os;
217
218     oggds_header_t oggds_header;
219
220     sout_buffer_t *pp_sout_headers[3];
221     int           i_sout_headers;
222
223 } ogg_stream_t;
224
225 struct sout_mux_sys_t
226 {
227     int     i_streams;
228
229     mtime_t i_start_dts;
230
231     /* number of logical streams pending to be added */
232     int i_add_streams;
233
234     /* logical streams pending to be deleted */
235     int i_del_streams;
236     ogg_stream_t **pp_del_streams;
237 };
238
239 static void OggSetDate( sout_buffer_t *, mtime_t , mtime_t  );
240 static sout_buffer_t *OggStreamFlush( sout_mux_t *, ogg_stream_state *,
241                                       mtime_t );
242
243 /*****************************************************************************
244  * Open: Open muxer
245  *****************************************************************************/
246 static int Open( vlc_object_t *p_this )
247 {
248     sout_mux_t      *p_mux = (sout_mux_t*)p_this;
249     sout_mux_sys_t  *p_sys;
250
251     msg_Info( p_mux, "Open" );
252
253     p_sys                 = malloc( sizeof( sout_mux_sys_t ) );
254     p_sys->i_streams      = 0;
255     p_sys->i_add_streams  = 0;
256     p_sys->i_del_streams  = 0;
257     p_sys->pp_del_streams = 0;
258
259     p_mux->p_sys        = p_sys;
260     p_mux->pf_capacity  = Capability;
261     p_mux->pf_addstream = AddStream;
262     p_mux->pf_delstream = DelStream;
263     p_mux->pf_mux       = Mux;
264     p_mux->i_preheader  = 1;
265
266     return VLC_SUCCESS;
267 }
268
269 /*****************************************************************************
270  * Close: Finalize ogg bitstream and close muxer
271  *****************************************************************************/
272 static void Close( vlc_object_t * p_this )
273 {
274     sout_mux_t     *p_mux = (sout_mux_t*)p_this;
275     sout_mux_sys_t *p_sys = p_mux->p_sys;
276
277     msg_Info( p_mux, "Close" );
278
279     if( p_sys->i_del_streams )
280     {
281         sout_buffer_t *p_og = NULL;
282         mtime_t i_dts = -1;
283         int i;
284
285         /* Close the current ogg stream */
286         msg_Dbg( p_mux, "writing footer" );
287         sout_BufferChain( &p_og, OggCreateFooter( p_mux, 0 ) );
288
289         /* Remove deleted logical streams */
290         for( i = 0; i < p_sys->i_del_streams; i++ )
291         {
292             i_dts = p_sys->pp_del_streams[i]->i_dts;
293             ogg_stream_clear( &p_sys->pp_del_streams[i]->os );
294             FREE( p_sys->pp_del_streams[i] );
295         }
296         FREE( p_sys->pp_del_streams );
297         p_sys->i_streams -= p_sys->i_del_streams;
298
299         /* Write footer */
300         OggSetDate( p_og, i_dts, 0 );
301         sout_AccessOutWrite( p_mux->p_access, p_og );
302     }
303
304     free( p_sys );
305 }
306
307 static int Capability( sout_mux_t *p_mux, int i_query, void *p_args,
308                        void *p_answer )
309 {
310    switch( i_query )
311    {
312         case SOUT_MUX_CAP_GET_ADD_STREAM_ANY_TIME:
313             *(vlc_bool_t*)p_answer = VLC_TRUE;
314             return( SOUT_MUX_CAP_ERR_OK );
315         default:
316             return( SOUT_MUX_CAP_ERR_UNIMPLEMENTED );
317    }
318 }
319
320 /*****************************************************************************
321  * AddStream: Add an elementary stream to the muxed stream
322  *****************************************************************************/
323 static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
324 {
325     sout_mux_sys_t *p_sys = p_mux->p_sys;
326     ogg_stream_t   *p_stream;
327
328     msg_Dbg( p_mux, "adding input" );
329
330     p_input->p_sys = (void *)p_stream = malloc( sizeof( ogg_stream_t ) );
331
332     p_stream->i_cat       = p_input->p_fmt->i_cat;
333     p_stream->i_fourcc    = p_input->p_fmt->i_fourcc;
334     p_stream->i_serial_no = rand();
335     p_stream->i_packet_no = 0;
336
337     p_stream->i_sout_headers = 0;
338
339     memset( &p_stream->oggds_header, 0, sizeof(p_stream->oggds_header) );
340     p_stream->oggds_header.i_packet_type = PACKET_TYPE_HEADER;
341     switch( p_input->p_fmt->i_cat )
342     {
343     case VIDEO_ES:
344         switch( p_stream->i_fourcc )
345         {
346         case VLC_FOURCC( 'm', 'p','4', 'v' ):
347         case VLC_FOURCC( 'D', 'I','V', '3' ):
348             memcpy( p_stream->oggds_header.stream_type, "video", 5 );
349             if( p_stream->i_fourcc == VLC_FOURCC( 'm', 'p','4', 'v' ) )
350             {
351                 memcpy( p_stream->oggds_header.sub_type, "XVID", 4 );
352             }
353             else if( p_stream->i_fourcc == VLC_FOURCC( 'D', 'I','V', '3' ) )
354             {
355                 memcpy( p_stream->oggds_header.sub_type, "DIV3", 4 );
356             }
357             SetDWLE( &p_stream->oggds_header.i_size,
358                      sizeof( oggds_header_t ) - 1);
359             SetQWLE( &p_stream->oggds_header.i_time_unit,
360                      I64C(10000000)/(int64_t)25 );  // FIXME (25fps)
361             SetQWLE( &p_stream->oggds_header.i_samples_per_unit, 1 );
362             SetDWLE( &p_stream->oggds_header.i_default_len, 1 ); /* ??? */
363             SetDWLE( &p_stream->oggds_header.i_buffer_size, 1024*1024 );
364             SetWLE( &p_stream->oggds_header.i_bits_per_sample, 0 );
365             SetDWLE( &p_stream->oggds_header.header.video.i_width,
366                      p_input->p_fmt->i_width );
367             SetDWLE( &p_stream->oggds_header.header.video.i_height,
368                      p_input->p_fmt->i_height );
369             msg_Dbg( p_mux, "mp4v/div3 stream" );
370             break;
371
372         case VLC_FOURCC( 't', 'h', 'e', 'o' ):
373             msg_Dbg( p_mux, "theora stream" );
374             break;
375
376         default:
377             FREE( p_input->p_sys );
378             return( VLC_EGENERIC );
379         }
380         break;
381
382     case AUDIO_ES:
383         switch( p_stream->i_fourcc )
384         {
385         case VLC_FOURCC( 'm', 'p','g', 'a' ):
386         case VLC_FOURCC( 'a', '5','2', ' ' ):
387             memcpy( p_stream->oggds_header.stream_type, "audio", 5 );
388             if( p_stream->i_fourcc == VLC_FOURCC( 'm', 'p','g', 'a' ) )
389             {
390                 memcpy( p_stream->oggds_header.sub_type, "55  ", 4 );
391             }
392             else if( p_stream->i_fourcc == VLC_FOURCC( 'a', '5','2', ' ' ) )
393             {
394                 memcpy( p_stream->oggds_header.sub_type, "2000", 4 );
395             }
396             SetDWLE( &p_stream->oggds_header.i_size,
397                      sizeof( oggds_header_t ) - 1);
398             SetQWLE( &p_stream->oggds_header.i_time_unit, 0 /* not used */ );
399             SetDWLE( &p_stream->oggds_header.i_default_len, 1 );
400             SetDWLE( &p_stream->oggds_header.i_buffer_size, 30*1024 );
401             SetQWLE( &p_stream->oggds_header.i_samples_per_unit,
402                      p_input->p_fmt->i_sample_rate );
403             SetWLE( &p_stream->oggds_header.i_bits_per_sample, 0 );
404             SetDWLE( &p_stream->oggds_header.header.audio.i_channels,
405                      p_input->p_fmt->i_channels );
406             SetDWLE( &p_stream->oggds_header.header.audio.i_block_align,
407                      p_input->p_fmt->i_block_align );
408             SetDWLE( &p_stream->oggds_header.header.audio.i_avgbytespersec, 0);
409             msg_Dbg( p_mux, "mpga/a52 stream" );
410             break;
411
412         case VLC_FOURCC( 'v', 'o', 'r', 'b' ):
413             msg_Dbg( p_mux, "vorbis stream" );
414             break;
415
416         default:
417             FREE( p_input->p_sys );
418             return( VLC_EGENERIC );
419         }
420         break;
421
422     case SPU_ES:
423         switch( p_stream->i_fourcc )
424         {
425         case VLC_FOURCC( 's', 'u','b', 't' ):
426             memcpy( p_stream->oggds_header.stream_type, "text", 4 );
427             msg_Dbg( p_mux, "subtitles stream" );
428             break;
429
430         default:
431             FREE( p_input->p_sys );
432             return( VLC_EGENERIC );
433         }
434         break;
435     default:
436         FREE( p_input->p_sys );
437         return( VLC_EGENERIC );
438     }
439
440     p_stream->b_new = VLC_TRUE;
441
442     p_sys->i_add_streams++;
443
444     return( VLC_SUCCESS );
445 }
446
447 /*****************************************************************************
448  * DelStream: Delete an elementary stream from the muxed stream
449  *****************************************************************************/
450 static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
451 {
452     sout_mux_sys_t *p_sys  = p_mux->p_sys;
453     ogg_stream_t   *p_stream = (ogg_stream_t*)p_input->p_sys;
454     sout_buffer_t  *p_og;
455
456     msg_Dbg( p_mux, "removing input" );
457
458     /* flush all remaining data */
459     if( p_input->p_sys )
460     {
461         int i;
462
463         if( ( p_og = OggStreamFlush( p_mux, &p_stream->os, 0 ) ) )
464         {
465             OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
466             sout_AccessOutWrite( p_mux->p_access, p_og );
467         }
468
469         for( i = 0; i < p_stream->i_sout_headers; i++ )
470         {
471             sout_BufferDelete( p_mux->p_sout, p_stream->pp_sout_headers[i] );
472             p_stream->i_sout_headers = 0;
473         }
474
475         /* move input in delete queue */
476         if( !p_stream->b_new )
477         {
478             p_sys->pp_del_streams = realloc( p_sys->pp_del_streams,
479                                              (p_sys->i_del_streams + 1) *
480                                              sizeof(ogg_stream_t *) );
481             p_sys->pp_del_streams[p_sys->i_del_streams++] = p_stream;
482         }
483         else
484         {
485             /* Wasn't already added so get rid of it */
486             ogg_stream_clear( &p_stream->os );
487             FREE( p_stream );
488             p_sys->i_add_streams--;
489         }
490     }
491
492     p_input->p_sys = NULL;
493
494     return( 0 );
495 }
496
497 /*****************************************************************************
498  * Ogg bitstream manipulation routines
499  *****************************************************************************/
500 static sout_buffer_t *OggStreamFlush( sout_mux_t *p_mux,
501                                       ogg_stream_state *p_os, mtime_t i_pts )
502 {
503     sout_buffer_t *p_og, *p_og_first = NULL;
504     ogg_page      og;
505
506     for( ;; )
507     {
508         /* flush all data */
509         int i_result;
510         int i_size;
511         if( ( i_result = ogg_stream_flush( p_os, &og ) ) == 0 )
512         {
513             break;
514         }
515
516         i_size = og.header_len + og.body_len;
517         p_og = sout_BufferNew( p_mux->p_sout, i_size);
518
519         memcpy( p_og->p_buffer, og.header, og.header_len );
520         memcpy( p_og->p_buffer + og.header_len, og.body, og.body_len );
521         p_og->i_size    = i_size;
522         p_og->i_dts     = 0;
523         p_og->i_pts     = i_pts;
524         p_og->i_length  = 0;
525
526         i_pts   = 0; // write it only once
527
528         sout_BufferChain( &p_og_first, p_og );
529     }
530
531     return( p_og_first );
532 }
533
534 static sout_buffer_t *OggStreamPageOut( sout_mux_t *p_mux,
535                                         ogg_stream_state *p_os, mtime_t i_pts )
536 {
537     sout_buffer_t *p_og, *p_og_first = NULL;
538     ogg_page      og;
539
540     for( ;; )
541     {
542         /* flush all data */
543         int i_result;
544         int i_size;
545         if( ( i_result = ogg_stream_pageout( p_os, &og ) ) == 0 )
546         {
547             break;
548         }
549
550         i_size = og.header_len + og.body_len;
551         p_og = sout_BufferNew( p_mux->p_sout, i_size);
552
553         memcpy( p_og->p_buffer, og.header, og.header_len );
554         memcpy( p_og->p_buffer + og.header_len, og.body, og.body_len );
555         p_og->i_size    = i_size;
556         p_og->i_dts     = 0;
557         p_og->i_pts     = i_pts;
558         p_og->i_length  = 0;
559
560         i_pts   = 0; // write them only once
561
562         sout_BufferChain( &p_og_first, p_og );
563     }
564
565     return( p_og_first );
566 }
567
568 static sout_buffer_t *OggCreateHeader( sout_mux_t *p_mux, mtime_t i_dts )
569 {
570     sout_buffer_t *p_hdr = NULL;
571     sout_buffer_t *p_og;
572     ogg_packet    op;
573     int i;
574
575     /* Write header for each stream. All b_o_s (beginning of stream) packets
576      * must appear first in the ogg stream so we take care of them first. */
577     for( i = 0; i < p_mux->i_nb_inputs; i++ )
578     {
579         ogg_stream_t *p_stream = (ogg_stream_t*)p_mux->pp_inputs[i]->p_sys;
580         p_stream->b_new = VLC_FALSE;
581
582         msg_Dbg( p_mux, "creating header for %4.4s",
583                  (char *)&p_stream->i_fourcc );
584
585         ogg_stream_init( &p_stream->os, p_stream->i_serial_no );
586         p_stream->i_packet_no = 0;
587
588         if( p_stream->i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) ||
589             p_stream->i_fourcc == VLC_FOURCC( 't', 'h', 'e', 'o' ) )
590         {
591             /* Special case, headers are already there in the
592              * incoming stream or we backed them up earlier */
593
594             /* first packet in order: vorbis/theora info */
595             if( !p_stream->i_sout_headers )
596             {
597                 p_og = sout_FifoGet( p_mux->pp_inputs[i]->p_fifo );
598                 op.packet = p_og->p_buffer;
599                 op.bytes  = p_og->i_size;
600                 op.b_o_s  = 1;
601                 op.e_o_s  = 0;
602                 op.granulepos = 0;
603                 op.packetno = p_stream->i_packet_no++;
604                 ogg_stream_packetin( &p_stream->os, &op );
605                 p_stream->pp_sout_headers[0] =
606                     OggStreamFlush( p_mux, &p_stream->os, 0 );
607                 p_stream->i_sout_headers++;
608             }
609             p_og = sout_BufferDuplicate( p_mux->p_sout,
610                                          p_stream->pp_sout_headers[0] );
611
612             /* Get keyframe_granule_shift for theora granulepos calculation */
613             if( p_stream->i_fourcc == VLC_FOURCC( 't', 'h', 'e', 'o' ) )
614             {
615                 int i_keyframe_frequency_force = 1 << (op.packet[36] >> 3);
616
617                 /* granule_shift = i_log( frequency_force -1 ) */
618                 p_stream->i_keyframe_granule_shift = 0;
619                 i_keyframe_frequency_force--;
620                 while( i_keyframe_frequency_force )
621                 {
622                     p_stream->i_keyframe_granule_shift++;
623                     i_keyframe_frequency_force >>= 1;
624                 }
625             }
626         }
627         else
628         {
629             /* ds header */
630             op.packet = (uint8_t*)&p_stream->oggds_header;
631             op.bytes  = sizeof( oggds_header_t );
632             op.b_o_s  = 1;
633             op.e_o_s  = 0;
634             op.granulepos = 0;
635             op.packetno = p_stream->i_packet_no++;
636             ogg_stream_packetin( &p_stream->os, &op );
637             p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
638         }
639
640         sout_BufferChain( &p_hdr, p_og );
641     }
642
643     /* Take care of the non b_o_s headers */
644     for( i = 0; i < p_mux->i_nb_inputs; i++ )
645     {
646         ogg_stream_t *p_stream = (ogg_stream_t*)p_mux->pp_inputs[i]->p_sys;
647
648         if( p_stream->i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) ||
649             p_stream->i_fourcc == VLC_FOURCC( 't', 'h', 'e', 'o' ) )
650         {
651             /* Special case, headers are already there in the incoming stream.
652              * We need to gather them an mark them as headers. */
653             int j;
654             for( j = 0; j < 2; j++ )
655             {
656                 if( p_stream->i_sout_headers < j + 2 )
657                 {
658                     /* next packets in order: comments and codebooks */
659                     p_og = sout_FifoGet( p_mux->pp_inputs[i]->p_fifo );
660                     op.packet = p_og->p_buffer;
661                     op.bytes  = p_og->i_size;
662                     op.b_o_s  = 0;
663                     op.e_o_s  = 0;
664                     op.granulepos = 0;
665                     op.packetno = p_stream->i_packet_no++;
666                     ogg_stream_packetin( &p_stream->os, &op );
667                     p_stream->pp_sout_headers[j+1] =
668                         OggStreamFlush( p_mux, &p_stream->os, 0 );
669                     p_stream->i_sout_headers++;
670                 }
671
672                 p_og = sout_BufferDuplicate( p_mux->p_sout,
673                                              p_stream->pp_sout_headers[j+1] );
674                 sout_BufferChain( &p_hdr, p_og );
675             }
676         }
677         else
678         {
679             uint8_t com[128];
680             int     i_com;
681
682             /* comment */
683             com[0] = PACKET_TYPE_COMMENT;
684             i_com = snprintf( &com[1], 128, VERSION" stream output" ) + 1;
685             op.packet = com;
686             op.bytes  = i_com;
687             op.b_o_s  = 0;
688             op.e_o_s  = 0;
689             op.granulepos = 0;
690             op.packetno = p_stream->i_packet_no++;
691             ogg_stream_packetin( &p_stream->os, &op );
692             p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
693             sout_BufferChain( &p_hdr, p_og );
694         }
695     }
696
697     /* set HEADER flag */
698     for( p_og = p_hdr; p_og != NULL; p_og = p_og->p_next )
699     {
700         p_og->i_flags |= SOUT_BUFFER_FLAGS_HEADER;
701     }
702     return( p_hdr );
703 }
704
705 static sout_buffer_t *OggCreateFooter( sout_mux_t *p_mux, mtime_t i_dts )
706 {
707     sout_mux_sys_t *p_sys = p_mux->p_sys;
708     sout_buffer_t *p_hdr = NULL;
709     sout_buffer_t *p_og;
710     ogg_packet    op;
711     int i;
712
713     /* flush all remaining data */
714     for( i = 0; i < p_mux->i_nb_inputs; i++ )
715     {
716         ogg_stream_t *p_stream = p_mux->pp_inputs[i]->p_sys;
717
718         /* skip newly added streams */
719         if( p_stream->b_new ) continue;
720
721         if( ( p_og = OggStreamFlush( p_mux, &p_stream->os, 0 ) ) )
722         {
723             OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
724             sout_AccessOutWrite( p_mux->p_access, p_og );
725         }
726     }
727
728     /* Write eos packets for each stream. */
729     for( i = 0; i < p_mux->i_nb_inputs; i++ )
730     {
731         ogg_stream_t *p_stream = p_mux->pp_inputs[i]->p_sys;
732
733         /* skip newly added streams */
734         if( p_stream->b_new ) continue;
735
736         op.packet = NULL;
737         op.bytes  = 0;
738         op.b_o_s  = 0;
739         op.e_o_s  = 1;
740         op.granulepos = -1;
741         op.packetno = p_stream->i_packet_no++;
742         ogg_stream_packetin( &p_stream->os, &op );
743
744         p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
745         sout_BufferChain( &p_hdr, p_og );
746         ogg_stream_clear( &p_stream->os );
747     }
748
749     for( i = 0; i < p_sys->i_del_streams; i++ )
750     {
751         op.packet = NULL;
752         op.bytes  = 0;
753         op.b_o_s  = 0;
754         op.e_o_s  = 1;
755         op.granulepos = -1;
756         op.packetno = p_sys->pp_del_streams[i]->i_packet_no++;
757         ogg_stream_packetin( &p_sys->pp_del_streams[i]->os, &op );
758
759         p_og = OggStreamFlush( p_mux, &p_sys->pp_del_streams[i]->os, 0 );
760         sout_BufferChain( &p_hdr, p_og );
761         ogg_stream_clear( &p_sys->pp_del_streams[i]->os );
762     }
763
764     return( p_hdr );
765 }
766
767 static void OggSetDate( sout_buffer_t *p_og, mtime_t i_dts, mtime_t i_length )
768 {
769     int i_count;
770     sout_buffer_t *p_tmp;
771     mtime_t i_delta;
772
773     for( p_tmp = p_og, i_count = 0; p_tmp != NULL; p_tmp = p_tmp->p_next )
774     {
775         i_count++;
776     }
777     i_delta = i_length / i_count;
778
779     for( p_tmp = p_og; p_tmp != NULL; p_tmp = p_tmp->p_next )
780     {
781         p_tmp->i_dts    = i_dts;
782         p_tmp->i_length = i_delta;
783
784         i_dts += i_delta;
785     }
786 }
787
788 /*****************************************************************************
789  * Mux: multiplex available data in input fifos into the Ogg bitstream
790  *****************************************************************************/
791 static int Mux( sout_mux_t *p_mux )
792 {
793     sout_mux_sys_t *p_sys = p_mux->p_sys;
794     sout_buffer_t  *p_og = NULL;
795     int            i_stream;
796     mtime_t        i_dts;
797
798     if( p_sys->i_add_streams || p_sys->i_del_streams )
799     {
800         /* Open new ogg stream */
801         if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 )
802         {
803             msg_Dbg( p_mux, "waiting for data..." );
804             return( VLC_SUCCESS );
805         }
806
807         if( p_sys->i_streams )
808         {
809             /* Close current ogg stream */
810             int i;
811
812             msg_Dbg( p_mux, "writing footer" );
813             sout_BufferChain( &p_og, OggCreateFooter( p_mux, 0 ) );
814
815             /* Remove deleted logical streams */
816             for( i = 0; i < p_sys->i_del_streams; i++ )
817             {
818                 FREE( p_sys->pp_del_streams[i] );
819             }
820             FREE( p_sys->pp_del_streams );
821             p_sys->i_streams = 0;
822         }
823
824         msg_Dbg( p_mux, "writing header" );
825         p_sys->i_start_dts = i_dts;
826         p_sys->i_streams = p_mux->i_nb_inputs;
827         p_sys->i_del_streams = 0;
828         p_sys->i_add_streams = 0;
829         sout_BufferChain( &p_og, OggCreateHeader( p_mux, i_dts ) );
830
831         /* Write header and/or footer */
832         OggSetDate( p_og, i_dts, 0 );
833         sout_AccessOutWrite( p_mux->p_access, p_og );
834         p_og = NULL;
835     }
836
837     for( ;; )
838     {
839         sout_input_t  *p_input;
840         ogg_stream_t  *p_stream;
841         sout_buffer_t *p_data;
842         ogg_packet    op;
843
844         if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 )
845         {
846             return( VLC_SUCCESS );
847         }
848
849         p_input  = p_mux->pp_inputs[i_stream];
850         p_stream = (ogg_stream_t*)p_input->p_sys;
851         p_data   = sout_FifoGet( p_input->p_fifo );
852
853         if( p_stream->i_fourcc != VLC_FOURCC( 'v', 'o', 'r', 'b' ) &&
854             p_stream->i_fourcc != VLC_FOURCC( 't', 'h', 'e', 'o' ) )
855         {
856             sout_BufferReallocFromPreHeader( p_mux->p_sout, p_data, 1 );
857             p_data->p_buffer[0] = PACKET_IS_SYNCPOINT;      // FIXME
858         }
859
860         op.packet   = p_data->p_buffer;
861         op.bytes    = p_data->i_size;
862         op.b_o_s    = 0;
863         op.e_o_s    = 0;
864         op.packetno = p_stream->i_packet_no++;
865
866         if( p_stream->i_cat == AUDIO_ES )
867         {
868             if( p_stream->i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) )
869             {
870                 /* number of sample from begining + current packet */
871                 op.granulepos =
872                     ( i_dts + p_data->i_length - p_sys->i_start_dts ) *
873                     p_input->p_fmt->i_sample_rate / I64C(1000000);
874             }
875             else
876             {
877                 /* number of sample from begining */
878                 op.granulepos = ( i_dts - p_sys->i_start_dts ) *
879                     p_stream->oggds_header.i_samples_per_unit / I64C(1000000);
880             }
881         }
882         else if( p_stream->i_cat == VIDEO_ES )
883         {
884             if( p_stream->i_fourcc == VLC_FOURCC( 't', 'h', 'e', 'o' ) )
885             {
886                 /* FIXME, we assume only keyframes and 25fps */
887                 op.granulepos = ( ( i_dts - p_sys->i_start_dts ) * I64C(25)
888                     / I64C(1000000) ) << p_stream->i_keyframe_granule_shift;
889             }
890             else
891                 op.granulepos = ( i_dts - p_sys->i_start_dts ) * I64C(10) /
892                     p_stream->oggds_header.i_time_unit;
893         }
894         else if( p_stream->i_cat == SPU_ES )
895         {
896             /* granulepos is in milisec */
897             op.granulepos = ( i_dts - p_sys->i_start_dts ) / 1000;
898         }
899
900         ogg_stream_packetin( &p_stream->os, &op );
901
902         if( p_stream->i_cat == SPU_ES )
903         {
904             /* Subtitles need to be flushed to be sent on time */
905             sout_BufferChain( &p_og, OggStreamFlush( p_mux, &p_stream->os,
906                                                      p_data->i_dts ) );
907         }
908         else
909         {
910             sout_BufferChain( &p_og, OggStreamPageOut( p_mux, &p_stream->os,
911                                                        p_data->i_dts ) );
912         }
913
914         if( p_og )
915         {
916             OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
917             p_stream->i_dts = -1;
918             p_stream->i_length = 0;
919
920             sout_AccessOutWrite( p_mux->p_access, p_og );
921
922             p_og = NULL;
923         }
924         else
925         {
926             if( p_stream->i_dts < 0 )
927             {
928                 p_stream->i_dts = p_data->i_dts;
929             }
930             p_stream->i_length += p_data->i_length;
931         }
932
933         sout_BufferDelete( p_mux->p_sout, p_data );
934     }
935
936     return( VLC_SUCCESS );
937 }