]> git.sesse.net Git - vlc/blob - modules/mux/ogg.c
* all: new sout scheme. Now a chain of module are created that can
[vlc] / modules / mux / ogg.c
1 /*****************************************************************************
2  * ogg.c
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: ogg.c,v 1.5 2003/04/13 20:00:21 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     msg_Dbg( p_mux, "adding input" );
213     p_input->p_sys = (void*)p_stream = malloc( sizeof( ogg_stream_t ) );
214
215     p_stream->i_cat       = p_input->p_fmt->i_cat;
216     p_stream->i_fourcc    = p_input->p_fmt->i_fourcc;
217     p_stream->i_packet_no = 0;
218
219     p_stream->header.i_packet_type = PACKET_TYPE_HEADER;
220     switch( p_input->p_fmt->i_cat )
221     {
222         case VIDEO_ES:
223             switch( p_input->p_fmt->i_fourcc )
224             {
225                 case VLC_FOURCC( 'm', 'p','4', 'v' ):
226                 case VLC_FOURCC( 'D', 'I','V', '3' ):
227                     memcpy( p_stream->header.stream_type,
228                             "video    ",
229                             8 );
230                     if( p_input->p_fmt->i_fourcc == VLC_FOURCC( 'm', 'p','4', 'v' ) )
231                     {
232                         memcpy( p_stream->header.sub_type, "XVID", 4 );
233                     }
234                     else if( p_input->p_fmt->i_fourcc == VLC_FOURCC( 'D', 'I','V', '3' ) )
235                     {
236                         memcpy( p_stream->header.sub_type, "DIV3", 4 );
237                     }
238                     SetDWLE( &p_stream->header.i_size, sizeof( ogg_stream_header_t ) - 1);
239                     /* XXX this won't make mplayer happy, but vlc can read that without any problem so...*/
240                     SetQWLE( &p_stream->header.i_time_unit, 10*1000 );//(int64_t)10*1000*1000/(int64_t)25 );  // FIXME (25fps)
241                     SetQWLE( &p_stream->header.i_samples_per_unit, 1 );
242                     SetDWLE( &p_stream->header.i_default_len, 0 );      /* ??? */
243                     SetDWLE( &p_stream->header.i_buffer_size, 1024*1024 );
244                     SetWLE( &p_stream->header.i_bits_per_sample, 0 );
245                     SetDWLE( &p_stream->header.header.video.i_width,  p_input->p_fmt->i_width );
246                     SetDWLE( &p_stream->header.header.video.i_height, p_input->p_fmt->i_height );
247                     break;
248                 default:
249                     FREE( p_input->p_sys );
250                     return( VLC_EGENERIC );
251             }
252             break;
253         case AUDIO_ES:
254             switch( p_input->p_fmt->i_fourcc )
255             {
256                 case VLC_FOURCC( 'm', 'p','g', 'a' ):
257                 case VLC_FOURCC( 'a', '5','2', ' ' ):
258                     memcpy( p_stream->header.stream_type,
259                             "audio    ",
260                             8 );
261                     if( p_input->p_fmt->i_fourcc == VLC_FOURCC( 'm', 'p','g', 'a' ) )
262                     {
263                         memcpy( p_stream->header.sub_type, "55  ", 4 );
264                     }
265                     else if( p_input->p_fmt->i_fourcc == VLC_FOURCC( 'a', '5','2', ' ' ) )
266                     {
267                         memcpy( p_stream->header.sub_type, "2000", 4 );
268                     }
269                     SetDWLE( &p_stream->header.i_size, sizeof( ogg_stream_header_t ) - 1);
270                     SetQWLE( &p_stream->header.i_time_unit, 1000000 );  /* is it used ? */
271                     SetDWLE( &p_stream->header.i_default_len, 0 );      /* ??? */
272                     SetDWLE( &p_stream->header.i_buffer_size, 30*1024 );
273                     SetQWLE( &p_stream->header.i_samples_per_unit, p_input->p_fmt->i_sample_rate );
274                     SetWLE( &p_stream->header.i_bits_per_sample, 0 );
275                     SetDWLE( &p_stream->header.header.audio.i_channels, p_input->p_fmt->i_channels );
276                     SetDWLE( &p_stream->header.header.audio.i_block_align, p_input->p_fmt->i_block_align );
277                     SetDWLE( &p_stream->header.header.audio.i_avgbytespersec, 0 );
278                     break;
279                 case VLC_FOURCC( 'v', 'o', 'r', 'b' ):
280                 default:
281                     FREE( p_input->p_sys );
282                     return( VLC_EGENERIC );
283             }
284             break;
285         default:
286             FREE( p_input->p_sys );
287             return( VLC_EGENERIC );
288     }
289
290     ogg_stream_init (&p_stream->os, rand ());
291
292     p_sys->i_streams++;
293     return( VLC_SUCCESS );
294 }
295
296 static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
297 {
298     ogg_stream_t        *p_stream = (ogg_stream_t*)p_input->p_sys;
299     sout_buffer_t       *p_og;
300
301     msg_Dbg( p_mux, "removing input" );
302
303     /* flush all remaining data */
304
305     if( p_input->p_sys )
306     {
307         p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
308         if( p_og )
309         {
310             OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
311
312             sout_AccessOutWrite( p_mux->p_access, p_og );
313         }
314
315         ogg_stream_clear( &p_stream->os );
316
317         FREE( p_input->p_sys );
318     }
319     return( 0 );
320 }
321
322 /*
323  * TODO  move this function to src/stream_output.c (used by nearly all muxers)
324  */
325 static int MuxGetStream( sout_mux_t *p_mux,
326                          int        *pi_stream,
327                          mtime_t    *pi_dts )
328 {
329     mtime_t i_dts;
330     int     i_stream;
331     int     i;
332
333     for( i = 0, i_dts = 0, i_stream = -1; i < p_mux->i_nb_inputs; i++ )
334     {
335         sout_fifo_t  *p_fifo;
336
337         p_fifo = p_mux->pp_inputs[i]->p_fifo;
338
339         if( p_fifo->i_depth > 1 )
340         {
341             sout_buffer_t *p_buf;
342
343             p_buf = sout_FifoShow( p_fifo );
344             if( i_stream < 0 || p_buf->i_dts < i_dts )
345             {
346                 i_dts = p_buf->i_dts;
347                 i_stream = i;
348             }
349         }
350         else
351         {
352             return( -1 ); // wait that all fifo have at least 2 packets
353         }
354     }
355     if( pi_stream )
356     {
357         *pi_stream = i_stream;
358     }
359     if( pi_dts )
360     {
361         *pi_dts = i_dts;
362     }
363     return( i_stream );
364 }
365
366
367 static sout_buffer_t *OggStreamFlush( sout_mux_t *p_mux,
368                                       ogg_stream_state *p_os,
369                                       mtime_t i_pts )
370 {
371     sout_buffer_t       *p_og, *p_og_first = NULL;
372     ogg_page            og;
373
374     for( ;; )
375     {
376         /* flush all data */
377         int i_result;
378         int i_size;
379         if( ( i_result = ogg_stream_flush( p_os, &og ) ) == 0 )
380         {
381             break;
382         }
383         i_size = og.header_len + og.body_len;
384         p_og = sout_BufferNew( p_mux->p_sout, i_size);
385
386         memcpy( p_og->p_buffer,
387                 og.header,
388                 og.header_len );
389         memcpy( p_og->p_buffer + og.header_len,
390                 og.body,
391                 og.body_len );
392         p_og->i_size    = i_size;
393         p_og->i_dts     = 0;
394         p_og->i_pts     = i_pts;
395         p_og->i_length  = 0;
396
397         i_pts   = 0; // write it only once
398
399         sout_BufferChain( &p_og_first, p_og );
400     }
401
402     return( p_og_first );
403 }
404 static sout_buffer_t *OggStreamPageOut( sout_mux_t *p_mux,
405                                         ogg_stream_state *p_os,
406                                         mtime_t i_pts )
407 {
408     sout_buffer_t       *p_og, *p_og_first = NULL;
409     ogg_page            og;
410
411     for( ;; )
412     {
413         /* flush all data */
414         int i_result;
415         int i_size;
416         if( ( i_result = ogg_stream_pageout( p_os, &og ) ) == 0 )
417         {
418             break;
419         }
420         i_size = og.header_len + og.body_len;
421         p_og = sout_BufferNew( p_mux->p_sout, i_size);
422
423         memcpy( p_og->p_buffer,
424                 og.header,
425                 og.header_len );
426         memcpy( p_og->p_buffer + og.header_len,
427                 og.body,
428                 og.body_len );
429         p_og->i_size    = i_size;
430         p_og->i_dts     = 0;
431         p_og->i_pts     = i_pts;
432         p_og->i_length  = 0;
433
434         i_pts   = 0; // write them only once
435
436         sout_BufferChain( &p_og_first, p_og );
437     }
438
439     return( p_og_first );
440 }
441 static sout_buffer_t *OggCreateHeader( sout_mux_t *p_mux, mtime_t i_dts )
442 {
443     sout_buffer_t *p_hdr = NULL;
444     sout_buffer_t *p_og;
445     ogg_packet    op;
446     int i;
447
448     /* write header for each stream */
449     for( i = 0; i < p_mux->i_nb_inputs; i++ )
450     {
451         ogg_stream_t *p_stream;
452
453         p_stream = (ogg_stream_t*)p_mux->pp_inputs[i]->p_sys;
454
455         if( p_stream->i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) )
456         {
457             /* special case */
458         }
459         else
460         {
461             /* ds header */
462 #if 0
463             uint8_t com[128];
464             int     i_com;
465 #endif
466
467             /* header */
468             op.packet = (uint8_t*)&p_stream->header;
469             op.bytes  = sizeof( ogg_stream_t );
470             op.b_o_s  = 1;
471             op.e_o_s  = 0;
472             op.granulepos = 0;
473             op.packetno = p_stream->i_packet_no++;
474             ogg_stream_packetin( &p_stream->os, &op );
475 #if 0
476             /* I don't know why but this produce broken file */
477             /* comment */
478             com[0] = PACKET_TYPE_COMMENT;
479             i_com = snprintf( &com[1], 128, "VLC 0.5.x stream output" ) + 1;
480             op.packet = com;
481             op.bytes  = i_com;
482             op.b_o_s  = 0;
483             op.e_o_s  = 0;
484             op.granulepos = 0;
485             op.packetno = p_stream->i_packet_no++;
486             ogg_stream_packetin( &p_stream->os, &op );
487 #endif
488         }
489
490         p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
491         sout_BufferChain( &p_hdr, p_og );
492     }
493
494     /* set HEADER flag */
495     for( p_og = p_hdr; p_og != NULL; p_og = p_og->p_next )
496     {
497         p_og->i_flags |= SOUT_BUFFER_FLAGS_HEADER;
498     }
499     return( p_hdr );
500 }
501
502
503 static void OggSetDate( sout_buffer_t *p_og, mtime_t i_dts, mtime_t i_length )
504 {
505     int i_count;
506     sout_buffer_t *p_tmp;
507     mtime_t i_delta;
508
509     for( p_tmp = p_og, i_count = 0; p_tmp != NULL; p_tmp = p_tmp->p_next )
510     {
511         i_count++;
512     }
513     i_delta = i_length / i_count;
514
515     for( p_tmp = p_og; p_tmp != NULL; p_tmp = p_tmp->p_next )
516     {
517         p_tmp->i_dts    = i_dts;
518         p_tmp->i_length = i_delta;
519
520         i_dts += i_delta;
521     }
522 }
523
524 static int Mux      ( sout_mux_t *p_mux )
525 {
526     sout_mux_sys_t      *p_sys  = p_mux->p_sys;
527     sout_buffer_t       *p_og = NULL;
528     int i_stream;
529     mtime_t             i_dts;
530
531     if( p_sys->b_write_header )
532     {
533         if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 )
534         {
535             msg_Dbg( p_mux, "waiting data..." );
536             return( VLC_SUCCESS );
537         }
538         p_sys->i_start_dts = i_dts;
539
540         msg_Dbg( p_mux, "writing header" );
541         sout_BufferChain( &p_og, OggCreateHeader( p_mux, i_dts ) );
542         p_sys->b_write_header = VLC_FALSE;
543     }
544
545     for( ;; )
546     {
547         sout_input_t    *p_input;
548         ogg_stream_t    *p_stream;
549         sout_buffer_t   *p_data;
550         ogg_packet          op;
551
552         if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 )
553         {
554             //msg_Dbg( p_mux, "waiting data..." );
555             return( VLC_SUCCESS );
556         }
557         //msg_Dbg( p_mux, "doing job" );
558
559         p_input  = p_mux->pp_inputs[i_stream];
560         p_stream = (ogg_stream_t*)p_input->p_sys;
561
562         p_data  = sout_FifoGet( p_input->p_fifo );
563
564         sout_BufferReallocFromPreHeader( p_mux->p_sout, p_data, 1 );
565         p_data->p_buffer[0] = PACKET_IS_SYNCPOINT;      // FIXME
566
567         op.packet = p_data->p_buffer;
568         op.bytes  = p_data->i_size;
569         op.b_o_s  = 0;
570         op.e_o_s  = 0;
571         if( p_stream->i_cat == AUDIO_ES )
572         {
573             /* number of sample from begining */
574             op.granulepos = ( i_dts - p_sys->i_start_dts ) *
575                                 p_stream->header.i_samples_per_unit / (int64_t)1000000;
576         }
577         else if( p_stream->i_cat == VIDEO_ES )
578         {
579             op.granulepos = ( i_dts - p_sys->i_start_dts ) / 1000;//p_stream->i_packet_no;
580         }
581         op.packetno = p_stream->i_packet_no++;
582         ogg_stream_packetin( &p_stream->os, &op );
583
584         sout_BufferChain( &p_og,
585                           OggStreamPageOut( p_mux,
586                                             &p_stream->os,
587                                             p_data->i_dts ) );
588
589         if( p_og )
590         {
591             OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
592             p_stream->i_dts = -1;
593             p_stream->i_length = 0;
594
595             msg_Dbg( p_mux, "writing data..." );
596             sout_AccessOutWrite( p_mux->p_access, p_og );
597
598             p_og = NULL;
599         }
600         else
601         {
602             if( p_stream->i_dts < 0 )
603             {
604                 p_stream->i_dts = p_data->i_dts;
605             }
606             p_stream->i_length += p_data->i_length;
607         }
608
609         sout_BufferDelete( p_mux->p_sout, p_data );
610     }
611
612     return( VLC_SUCCESS );
613 }
614