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