]> git.sesse.net Git - vlc/blob - modules/mux/ogg.c
08607be629fc38ef0bea8609e412fb2091ef8724
[vlc] / modules / mux / ogg.c
1 /*****************************************************************************
2  * ogg.c
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: ogg.c,v 1.4 2003/03/31 03:46:11 fenrir Exp $
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30
31 #include <vlc/vlc.h>
32 #include <vlc/input.h>
33 #include <vlc/sout.h>
34
35 #include "codecs.h"
36
37 #include <ogg/ogg.h>
38
39 /*****************************************************************************
40  * Exported prototypes
41  *****************************************************************************/
42 static int     Open   ( vlc_object_t * );
43 static void    Close  ( vlc_object_t * );
44
45 static int Capability(sout_mux_t *, int, void *, void * );
46 static int AddStream( sout_mux_t *, sout_input_t * );
47 static int DelStream( sout_mux_t *, sout_input_t * );
48 static int Mux      ( sout_mux_t * );
49
50 /*****************************************************************************
51  * Module descriptor
52  *****************************************************************************/
53 vlc_module_begin();
54     set_description( _("Ogg/ogm muxer") );
55     set_capability( "sout mux", 10 );
56     add_shortcut( "ogg" );
57     add_shortcut( "ogm" );
58     set_callbacks( Open, Close );
59 vlc_module_end();
60
61 /*****************************************************************************
62  *
63  *****************************************************************************/
64 #define FREE( p ) if( p ) { free( p ); (p) = NULL; }
65
66 #define PACKET_TYPE_HEADER   0x01
67 #define PACKET_TYPE_COMMENT  0x03
68
69 #define PACKET_IS_SYNCPOINT      0x08
70
71 typedef struct __attribute__((__packed__))
72 {
73     int32_t i_width;
74     int32_t i_height;
75 } ogg_stream_header_video_t;
76
77 typedef struct __attribute__((__packed__))
78 {
79     int16_t i_channels;
80     int16_t i_block_align;
81     int32_t i_avgbytespersec;
82 } ogg_stream_header_audio_t;
83
84 typedef struct __attribute__((__packed__))
85 {
86     uint8_t i_packet_type;
87
88     char stream_type[8];
89     char sub_type[4];
90
91     int32_t i_size;
92
93     int64_t i_time_unit;
94     int64_t i_samples_per_unit;
95     int32_t i_default_len;
96
97     int32_t i_buffer_size;
98     int16_t i_bits_per_sample;
99     int16_t i_padding_0;            // hum hum
100     union
101     {
102         ogg_stream_header_video_t video;
103         ogg_stream_header_audio_t audio;
104     } header;
105
106 } ogg_stream_header_t;
107
108
109 typedef struct
110 {
111     int i_cat;
112     int i_fourcc;
113
114     ogg_stream_header_t header;
115
116     int i_packet_no;
117
118     mtime_t             i_dts;
119     mtime_t             i_length;
120     ogg_stream_state    os;
121
122 } ogg_stream_t;
123
124 struct sout_mux_sys_t
125 {
126     int     b_write_header;
127     int     i_streams;
128
129     mtime_t i_start_dts;
130 };
131
132 #define SetWLE( p, v ) _SetWLE( (uint8_t*)p, v)
133 static void _SetWLE( uint8_t *p, uint16_t i_dw )
134 {
135     p[1] = ( i_dw >>  8 )&0xff;
136     p[0] = ( i_dw       )&0xff;
137 }
138
139 #define SetDWLE( p, v ) _SetDWLE( (uint8_t*)p, v)
140 static void _SetDWLE( uint8_t *p, uint32_t i_dw )
141 {
142     p[3] = ( i_dw >> 24 )&0xff;
143     p[2] = ( i_dw >> 16 )&0xff;
144     p[1] = ( i_dw >>  8 )&0xff;
145     p[0] = ( i_dw       )&0xff;
146 }
147 #define SetQWLE( p, v ) _SetQWLE( (uint8_t*)p, v)
148 static void _SetQWLE( uint8_t *p, uint64_t i_qw )
149 {
150     SetDWLE( p,   i_qw&0xffffffff );
151     SetDWLE( p+4, ( i_qw >> 32)&0xffffffff );
152 }
153
154 static void OggSetDate( sout_buffer_t *, mtime_t , mtime_t  );
155 static sout_buffer_t *OggStreamFlush( sout_mux_t *, ogg_stream_state *, mtime_t );
156
157 /*****************************************************************************
158  * Open:
159  *****************************************************************************/
160 static int Open( vlc_object_t *p_this )
161 {
162     sout_mux_t      *p_mux = (sout_mux_t*)p_this;
163     sout_mux_sys_t  *p_sys;
164
165     msg_Info( p_mux, "Open" );
166
167     p_sys                 = malloc( sizeof( sout_mux_sys_t ) );
168     p_sys->i_streams      = 0;
169     p_sys->b_write_header = VLC_TRUE;
170
171     p_mux->p_sys        = p_sys;
172     p_mux->pf_capacity  = Capability;
173     p_mux->pf_addstream = AddStream;
174     p_mux->pf_delstream = DelStream;
175     p_mux->pf_mux       = Mux;
176     p_mux->i_preheader  = 1;
177
178     return VLC_SUCCESS;
179 }
180
181 /*****************************************************************************
182  * Close:
183  *****************************************************************************/
184
185 static void Close( vlc_object_t * p_this )
186 {
187     sout_mux_t      *p_mux = (sout_mux_t*)p_this;
188     sout_mux_sys_t  *p_sys = p_mux->p_sys;
189
190     msg_Info( p_mux, "Close" );
191
192     free( p_sys );
193 }
194
195 static int Capability( sout_mux_t *p_mux, int i_query, void *p_args, void *p_answer )
196 {
197    switch( i_query )
198    {
199         case SOUT_MUX_CAP_GET_ADD_STREAM_ANY_TIME:
200             *(vlc_bool_t*)p_answer = VLC_FALSE;
201             return( SOUT_MUX_CAP_ERR_OK );
202         default:
203             return( SOUT_MUX_CAP_ERR_UNIMPLEMENTED );
204    }
205 }
206
207 static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
208 {
209     sout_mux_sys_t  *p_sys = p_mux->p_sys;
210     ogg_stream_t        *p_stream;
211
212     BITMAPINFOHEADER    *p_bih;
213     WAVEFORMATEX        *p_wf;
214
215     msg_Dbg( p_mux, "adding input" );
216     p_input->p_sys = (void*)p_stream = malloc( sizeof( ogg_stream_t ) );
217
218     p_stream->i_cat       = p_input->input_format.i_cat;
219     p_stream->i_fourcc    = p_input->input_format.i_fourcc;
220     p_stream->i_packet_no = 0;
221
222     p_stream->header.i_packet_type = PACKET_TYPE_HEADER;
223     switch( p_input->input_format.i_cat )
224     {
225         case VIDEO_ES:
226             p_bih = (BITMAPINFOHEADER*)p_input->input_format.p_format;
227             switch( p_input->input_format.i_fourcc )
228             {
229                 case VLC_FOURCC( 'm', 'p','4', 'v' ):
230                 case VLC_FOURCC( 'D', 'I','V', '3' ):
231                     memcpy( p_stream->header.stream_type,
232                             "video    ",
233                             8 );
234                     if( p_input->input_format.i_fourcc == VLC_FOURCC( 'm', 'p','4', 'v' ) )
235                     {
236                         memcpy( p_stream->header.sub_type, "XVID", 4 );
237                     }
238                     else if( p_input->input_format.i_fourcc == VLC_FOURCC( 'D', 'I','V', '3' ) )
239                     {
240                         memcpy( p_stream->header.sub_type, "DIV3", 4 );
241                     }
242                     SetDWLE( &p_stream->header.i_size, sizeof( ogg_stream_header_t ) - 1);
243                     /* XXX this won't make mplayer happy, but vlc can read that without any problem so...*/
244                     SetQWLE( &p_stream->header.i_time_unit, 10*1000 );//(int64_t)10*1000*1000/(int64_t)25 );  // FIXME (25fps)
245                     SetQWLE( &p_stream->header.i_samples_per_unit, 1 );
246                     SetDWLE( &p_stream->header.i_default_len, 0 );      /* ??? */
247                     SetDWLE( &p_stream->header.i_buffer_size, 1024*1024 );
248                     SetWLE( &p_stream->header.i_bits_per_sample, 0 );
249                     if( p_bih )
250                     {
251                         SetDWLE( &p_stream->header.header.video.i_width, p_bih->biWidth );
252                         SetDWLE( &p_stream->header.header.video.i_height, p_bih->biHeight );
253                     }
254                     else
255                     {
256                         SetDWLE( &p_stream->header.header.video.i_width,  0 );
257                         SetDWLE( &p_stream->header.header.video.i_height, 0 );
258                     }
259                     break;
260                 default:
261                     FREE( p_input->p_sys );
262                     return( VLC_EGENERIC );
263             }
264             break;
265         case AUDIO_ES:
266             p_wf = (WAVEFORMATEX*)p_input->input_format.p_format;
267             switch( p_input->input_format.i_fourcc )
268             {
269                 case VLC_FOURCC( 'm', 'p','g', 'a' ):
270                 case VLC_FOURCC( 'a', '5','2', ' ' ):
271                     memcpy( p_stream->header.stream_type,
272                             "audio    ",
273                             8 );
274                     if( p_input->input_format.i_fourcc == VLC_FOURCC( 'm', 'p','g', 'a' ) )
275                     {
276                         memcpy( p_stream->header.sub_type, "55  ", 4 );
277                     }
278                     else if( p_input->input_format.i_fourcc == VLC_FOURCC( 'a', '5','2', ' ' ) )
279                     {
280                         memcpy( p_stream->header.sub_type, "2000", 4 );
281                     }
282                     SetDWLE( &p_stream->header.i_size, sizeof( ogg_stream_header_t ) - 1);
283                     SetQWLE( &p_stream->header.i_time_unit, 1000000 );  /* is it used ? */
284                     SetDWLE( &p_stream->header.i_default_len, 0 );      /* ??? */
285                     SetDWLE( &p_stream->header.i_buffer_size, 30*1024 );
286                     if( p_wf )
287                     {
288                         SetQWLE( &p_stream->header.i_samples_per_unit, p_wf->nSamplesPerSec );
289                         SetWLE( &p_stream->header.i_bits_per_sample, p_wf->wBitsPerSample );
290                         SetDWLE( &p_stream->header.header.audio.i_channels, p_wf->nChannels );
291                         SetDWLE( &p_stream->header.header.audio.i_block_align, p_wf->nBlockAlign );
292                         SetDWLE( &p_stream->header.header.audio.i_avgbytespersec, p_wf->nAvgBytesPerSec );
293                     }
294                     else
295                     {
296                         /* perhaps it's better to fail */
297                         SetQWLE( &p_stream->header.i_samples_per_unit, 44100 );
298                         SetWLE( &p_stream->header.i_bits_per_sample, 0 );
299                         SetDWLE( &p_stream->header.header.audio.i_channels, 2 );
300                         SetDWLE( &p_stream->header.header.audio.i_block_align, 0 );
301                         SetDWLE( &p_stream->header.header.audio.i_avgbytespersec, 0 );
302                     }
303                     break;
304                 case VLC_FOURCC( 'v', 'o', 'r', 'b' ):
305                 default:
306                     FREE( p_input->p_sys );
307                     return( VLC_EGENERIC );
308             }
309             break;
310         default:
311             FREE( p_input->p_sys );
312             return( VLC_EGENERIC );
313     }
314
315     ogg_stream_init (&p_stream->os, rand ());
316
317     p_sys->i_streams++;
318     return( VLC_SUCCESS );
319 }
320
321 static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
322 {
323     ogg_stream_t        *p_stream = (ogg_stream_t*)p_input->p_sys;
324     sout_buffer_t       *p_og;
325
326     msg_Dbg( p_mux, "removing input" );
327
328     /* flush all remaining data */
329
330     if( p_input->p_sys )
331     {
332         p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
333         if( p_og )
334         {
335             OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
336
337             sout_AccessOutWrite( p_mux->p_access, p_og );
338         }
339
340         ogg_stream_clear( &p_stream->os );
341
342         FREE( p_input->p_sys );
343     }
344     return( 0 );
345 }
346
347 /*
348  * TODO  move this function to src/stream_output.c (used by nearly all muxers)
349  */
350 static int MuxGetStream( sout_mux_t *p_mux,
351                          int        *pi_stream,
352                          mtime_t    *pi_dts )
353 {
354     mtime_t i_dts;
355     int     i_stream;
356     int     i;
357
358     for( i = 0, i_dts = 0, i_stream = -1; i < p_mux->i_nb_inputs; i++ )
359     {
360         sout_fifo_t  *p_fifo;
361
362         p_fifo = p_mux->pp_inputs[i]->p_fifo;
363
364         if( p_fifo->i_depth > 1 )
365         {
366             sout_buffer_t *p_buf;
367
368             p_buf = sout_FifoShow( p_fifo );
369             if( i_stream < 0 || p_buf->i_dts < i_dts )
370             {
371                 i_dts = p_buf->i_dts;
372                 i_stream = i;
373             }
374         }
375         else
376         {
377             return( -1 ); // wait that all fifo have at least 2 packets
378         }
379     }
380     if( pi_stream )
381     {
382         *pi_stream = i_stream;
383     }
384     if( pi_dts )
385     {
386         *pi_dts = i_dts;
387     }
388     return( i_stream );
389 }
390
391
392 static sout_buffer_t *OggStreamFlush( sout_mux_t *p_mux,
393                                       ogg_stream_state *p_os,
394                                       mtime_t i_pts )
395 {
396     sout_buffer_t       *p_og, *p_og_first = NULL;
397     ogg_page            og;
398
399     for( ;; )
400     {
401         /* flush all data */
402         int i_result;
403         int i_size;
404         if( ( i_result = ogg_stream_flush( p_os, &og ) ) == 0 )
405         {
406             break;
407         }
408         i_size = og.header_len + og.body_len;
409         p_og = sout_BufferNew( p_mux->p_sout, i_size);
410
411         memcpy( p_og->p_buffer,
412                 og.header,
413                 og.header_len );
414         memcpy( p_og->p_buffer + og.header_len,
415                 og.body,
416                 og.body_len );
417         p_og->i_size    = i_size;
418         p_og->i_dts     = 0;
419         p_og->i_pts     = i_pts;
420         p_og->i_length  = 0;
421
422         i_pts   = 0; // write it only once
423
424         sout_BufferChain( &p_og_first, p_og );
425     }
426
427     return( p_og_first );
428 }
429 static sout_buffer_t *OggStreamPageOut( sout_mux_t *p_mux,
430                                         ogg_stream_state *p_os,
431                                         mtime_t i_pts )
432 {
433     sout_buffer_t       *p_og, *p_og_first = NULL;
434     ogg_page            og;
435
436     for( ;; )
437     {
438         /* flush all data */
439         int i_result;
440         int i_size;
441         if( ( i_result = ogg_stream_pageout( p_os, &og ) ) == 0 )
442         {
443             break;
444         }
445         i_size = og.header_len + og.body_len;
446         p_og = sout_BufferNew( p_mux->p_sout, i_size);
447
448         memcpy( p_og->p_buffer,
449                 og.header,
450                 og.header_len );
451         memcpy( p_og->p_buffer + og.header_len,
452                 og.body,
453                 og.body_len );
454         p_og->i_size    = i_size;
455         p_og->i_dts     = 0;
456         p_og->i_pts     = i_pts;
457         p_og->i_length  = 0;
458
459         i_pts   = 0; // write them only once
460
461         sout_BufferChain( &p_og_first, p_og );
462     }
463
464     return( p_og_first );
465 }
466 static sout_buffer_t *OggCreateHeader( sout_mux_t *p_mux, mtime_t i_dts )
467 {
468     sout_buffer_t *p_hdr = NULL;
469     sout_buffer_t *p_og;
470     ogg_packet    op;
471     int i;
472
473     /* write header for each stream */
474     for( i = 0; i < p_mux->i_nb_inputs; i++ )
475     {
476         ogg_stream_t *p_stream;
477
478         p_stream = (ogg_stream_t*)p_mux->pp_inputs[i]->p_sys;
479
480         if( p_stream->i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) )
481         {
482             /* special case */
483         }
484         else
485         {
486             /* ds header */
487 #if 0
488             uint8_t com[128];
489             int     i_com;
490 #endif
491
492             /* header */
493             op.packet = (uint8_t*)&p_stream->header;
494             op.bytes  = sizeof( ogg_stream_t );
495             op.b_o_s  = 1;
496             op.e_o_s  = 0;
497             op.granulepos = 0;
498             op.packetno = p_stream->i_packet_no++;
499             ogg_stream_packetin( &p_stream->os, &op );
500 #if 0
501             /* I don't know why but this produce broken file */
502             /* comment */
503             com[0] = PACKET_TYPE_COMMENT;
504             i_com = snprintf( &com[1], 128, "VLC 0.5.x stream output" ) + 1;
505             op.packet = com;
506             op.bytes  = i_com;
507             op.b_o_s  = 0;
508             op.e_o_s  = 0;
509             op.granulepos = 0;
510             op.packetno = p_stream->i_packet_no++;
511             ogg_stream_packetin( &p_stream->os, &op );
512 #endif
513         }
514
515         p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
516         sout_BufferChain( &p_hdr, p_og );
517     }
518
519     /* set HEADER flag */
520     for( p_og = p_hdr; p_og != NULL; p_og = p_og->p_next )
521     {
522         p_og->i_flags |= SOUT_BUFFER_FLAGS_HEADER;
523     }
524     return( p_hdr );
525 }
526
527
528 static void OggSetDate( sout_buffer_t *p_og, mtime_t i_dts, mtime_t i_length )
529 {
530     int i_count;
531     sout_buffer_t *p_tmp;
532     mtime_t i_delta;
533
534     for( p_tmp = p_og, i_count = 0; p_tmp != NULL; p_tmp = p_tmp->p_next )
535     {
536         i_count++;
537     }
538     i_delta = i_length / i_count;
539
540     for( p_tmp = p_og; p_tmp != NULL; p_tmp = p_tmp->p_next )
541     {
542         p_tmp->i_dts    = i_dts;
543         p_tmp->i_length = i_delta;
544
545         i_dts += i_delta;
546     }
547 }
548
549 static int Mux      ( sout_mux_t *p_mux )
550 {
551     sout_mux_sys_t      *p_sys  = p_mux->p_sys;
552     sout_buffer_t       *p_og = NULL;
553     int i_stream;
554     mtime_t             i_dts;
555
556     if( p_sys->b_write_header )
557     {
558         if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 )
559         {
560             msg_Dbg( p_mux, "waiting data..." );
561             return( VLC_SUCCESS );
562         }
563         p_sys->i_start_dts = i_dts;
564
565         msg_Dbg( p_mux, "writing header" );
566         sout_BufferChain( &p_og, OggCreateHeader( p_mux, i_dts ) );
567         p_sys->b_write_header = VLC_FALSE;
568     }
569
570     for( ;; )
571     {
572         sout_input_t    *p_input;
573         ogg_stream_t    *p_stream;
574         sout_buffer_t   *p_data;
575         ogg_packet          op;
576
577         if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 )
578         {
579             //msg_Dbg( p_mux, "waiting data..." );
580             return( VLC_SUCCESS );
581         }
582         //msg_Dbg( p_mux, "doing job" );
583
584         p_input  = p_mux->pp_inputs[i_stream];
585         p_stream = (ogg_stream_t*)p_input->p_sys;
586
587         p_data  = sout_FifoGet( p_input->p_fifo );
588
589         sout_BufferReallocFromPreHeader( p_mux->p_sout, p_data, 1 );
590         p_data->p_buffer[0] = PACKET_IS_SYNCPOINT;      // FIXME
591
592         op.packet = p_data->p_buffer;
593         op.bytes  = p_data->i_size;
594         op.b_o_s  = 0;
595         op.e_o_s  = 0;
596         if( p_stream->i_cat == AUDIO_ES )
597         {
598             /* number of sample from begining */
599             op.granulepos = ( i_dts - p_sys->i_start_dts ) *
600                                 p_stream->header.i_samples_per_unit / (int64_t)1000000;
601         }
602         else if( p_stream->i_cat == VIDEO_ES )
603         {
604             op.granulepos = ( i_dts - p_sys->i_start_dts ) / 1000;//p_stream->i_packet_no;
605         }
606         op.packetno = p_stream->i_packet_no++;
607         ogg_stream_packetin( &p_stream->os, &op );
608
609         sout_BufferChain( &p_og,
610                           OggStreamPageOut( p_mux,
611                                             &p_stream->os,
612                                             p_data->i_dts ) );
613
614         if( p_og )
615         {
616             OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
617             p_stream->i_dts = -1;
618             p_stream->i_length = 0;
619
620             msg_Dbg( p_mux, "writing data..." );
621             sout_AccessOutWrite( p_mux->p_access, p_og );
622
623             p_og = NULL;
624         }
625         else
626         {
627             if( p_stream->i_dts < 0 )
628             {
629                 p_stream->i_dts = p_data->i_dts;
630             }
631             p_stream->i_length += p_data->i_length;
632         }
633
634         sout_BufferDelete( p_mux->p_sout, p_data );
635     }
636
637     return( VLC_SUCCESS );
638 }
639