]> git.sesse.net Git - vlc/blob - modules/mux/avi.c
update module LIST file.
[vlc] / modules / mux / avi.c
1 /*****************************************************************************
2  * avi.c
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 the VideoLAN team
5  * $Id$
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 /* TODO: add OpenDML write support */
28
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <vlc/vlc.h>
35 #include <vlc_sout.h>
36 #include <vlc_block.h>
37 #include <vlc_codecs.h>
38
39 /*****************************************************************************
40  * Module descriptor
41  *****************************************************************************/
42 static int  Open   ( vlc_object_t * );
43 static void Close  ( vlc_object_t * );
44
45 vlc_module_begin();
46     set_description( _("AVI muxer") );
47     set_category( CAT_SOUT );
48     set_subcategory( SUBCAT_SOUT_MUX );
49     set_capability( "sout mux", 5 );
50     add_shortcut( "avi" );
51     set_callbacks( Open, Close );
52 vlc_module_end();
53
54
55 /*****************************************************************************
56  * Local prototypes
57  *****************************************************************************/
58 static int Control( sout_mux_t *, int, va_list );
59 static int AddStream( sout_mux_t *, sout_input_t * );
60 static int DelStream( sout_mux_t *, sout_input_t * );
61 static int Mux      ( sout_mux_t * );
62
63 typedef struct avi_stream_s
64 {
65     int i_cat;
66
67     char fcc[4];
68
69     mtime_t i_duration;       // in µs
70
71     int     i_frames;        // total frame count
72     int64_t i_totalsize;    // total stream size
73
74     float   f_fps;
75     int     i_bitrate;
76
77     BITMAPINFOHEADER    *p_bih;
78     WAVEFORMATEX        *p_wf;
79
80 } avi_stream_t;
81
82 typedef struct avi_idx1_entry_s
83 {
84     char     fcc[4];
85     uint32_t i_flags;
86     uint32_t i_pos;
87     uint32_t i_length;
88
89 } avi_idx1_entry_t;
90
91 typedef struct avi_idx1_s
92 {
93     unsigned int i_entry_count;
94     unsigned int i_entry_max;
95
96     avi_idx1_entry_t *entry;
97 } avi_idx1_t;
98
99 struct sout_mux_sys_t
100 {
101     vlc_bool_t b_write_header;
102
103     int i_streams;
104     int i_stream_video;
105
106     off_t i_movi_size;
107     avi_stream_t stream[100];
108
109     avi_idx1_t idx1;
110     off_t i_idx1_size;
111
112 };
113
114 // FIXME FIXME
115 #define HDR_SIZE 10240
116
117 /* Flags in avih */
118 #define AVIF_HASINDEX       0x00000010  // Index at end of file?
119 #define AVIF_ISINTERLEAVED  0x00000100
120 #define AVIF_TRUSTCKTYPE    0x00000800  // Use CKType to find key frames?
121
122 /* Flags for index */
123 #define AVIIF_KEYFRAME      0x00000010L /* this frame is a key frame.*/
124
125
126 static block_t *avi_HeaderCreateRIFF( sout_mux_t * );
127 static block_t *avi_HeaderCreateidx1( sout_mux_t * );
128
129 static void SetFCC( uint8_t *p, char *fcc )
130 {
131     memcpy( p, fcc, 4 );
132 }
133
134 /*****************************************************************************
135  * Open:
136  *****************************************************************************/
137 static int Open( vlc_object_t *p_this )
138 {
139     sout_mux_t      *p_mux = (sout_mux_t*)p_this;
140     sout_mux_sys_t  *p_sys = p_mux->p_sys;
141
142     msg_Dbg( p_mux, "AVI muxer opened" );
143
144     p_sys = malloc( sizeof( sout_mux_sys_t ) );
145     p_sys->i_streams = 0;
146     p_sys->i_stream_video = -1;
147     p_sys->i_movi_size = 0;
148
149     p_sys->idx1.i_entry_count = 0;
150     p_sys->idx1.i_entry_max = 10000;
151     p_sys->idx1.entry = calloc( p_sys->idx1.i_entry_max,
152                                 sizeof( avi_idx1_entry_t ) );
153     p_sys->b_write_header = VLC_TRUE;
154
155
156     p_mux->pf_control   = Control;
157     p_mux->pf_addstream = AddStream;
158     p_mux->pf_delstream = DelStream;
159     p_mux->pf_mux       = Mux;
160     p_mux->p_sys        = p_sys;
161
162     return VLC_SUCCESS;
163 }
164
165 /*****************************************************************************
166  * Close:
167  *****************************************************************************/
168 static void Close( vlc_object_t * p_this )
169 {
170     sout_mux_t      *p_mux = (sout_mux_t*)p_this;
171     sout_mux_sys_t  *p_sys = p_mux->p_sys;
172
173     block_t       *p_hdr, *p_idx1;
174     int                 i_stream;
175
176     msg_Dbg( p_mux, "AVI muxer closed" );
177
178     /* first create idx1 chunk (write at the end of the stream */
179     p_idx1 = avi_HeaderCreateidx1( p_mux );
180     p_sys->i_idx1_size = p_idx1->i_buffer;
181     sout_AccessOutWrite( p_mux->p_access, p_idx1 );
182
183     /* calculate some value for headers creations */
184     for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
185     {
186         avi_stream_t *p_stream;
187
188         p_stream = &p_sys->stream[i_stream];
189
190         p_stream->f_fps = 25;
191         if( p_stream->i_duration > 0 )
192         {
193             p_stream->f_fps = (float)p_stream->i_frames /
194                               ( (float)p_stream->i_duration /
195                                 (float)1000000 );
196         }
197         p_stream->i_bitrate = 128 * 1024;
198         if( p_stream->i_duration > 0 )
199         {
200             p_stream->i_bitrate =
201                 8 * (uint64_t)1000000 *
202                     (uint64_t)p_stream->i_totalsize /
203                     (uint64_t)p_stream->i_duration;
204         }
205         msg_Info( p_mux, "stream[%d] duration:"I64Fd" totalsize:"I64Fd
206                   " frames:%d fps:%f kb/s:%d",
207                   i_stream,
208                   (int64_t)p_stream->i_duration / (int64_t)1000000,
209                   p_stream->i_totalsize,
210                   p_stream->i_frames,
211                   p_stream->f_fps, p_stream->i_bitrate/1024 );
212     }
213
214     p_hdr = avi_HeaderCreateRIFF( p_mux );
215     sout_AccessOutSeek( p_mux->p_access, 0 );
216     sout_AccessOutWrite( p_mux->p_access, p_hdr );
217 }
218
219 static int Control( sout_mux_t *p_mux, int i_query, va_list args )
220 {
221     VLC_UNUSED(p_mux);
222     vlc_bool_t *pb_bool;
223     char **ppsz;
224
225    switch( i_query )
226    {
227        case MUX_CAN_ADD_STREAM_WHILE_MUXING:
228            pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
229            *pb_bool = VLC_FALSE;
230            return VLC_SUCCESS;
231
232        case MUX_GET_ADD_STREAM_WAIT:
233            pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
234            *pb_bool = VLC_TRUE;
235            return VLC_SUCCESS;
236
237        case MUX_GET_MIME:
238            ppsz = (char**)va_arg( args, char ** );
239            *ppsz = strdup( "video/avi" );
240            return VLC_SUCCESS;
241
242         default:
243             return VLC_EGENERIC;
244    }
245 }
246
247 static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
248 {
249     sout_mux_sys_t  *p_sys = p_mux->p_sys;
250     avi_stream_t    *p_stream;
251
252     if( p_sys->i_streams >= 100 )
253     {
254         msg_Err( p_mux, "too many streams" );
255         return( -1 );
256     }
257
258     msg_Dbg( p_mux, "adding input" );
259     p_input->p_sys = malloc( sizeof( int ) );
260
261     *((int*)p_input->p_sys) = p_sys->i_streams;
262     p_stream = &p_sys->stream[p_sys->i_streams];
263
264     switch( p_input->p_fmt->i_cat )
265     {
266         case AUDIO_ES:
267             p_stream->i_cat = AUDIO_ES;
268             p_stream->fcc[0] = '0' + p_sys->i_streams / 10;
269             p_stream->fcc[1] = '0' + p_sys->i_streams % 10;
270             p_stream->fcc[2] = 'w';
271             p_stream->fcc[3] = 'b';
272
273             p_stream->p_bih = NULL;
274
275             p_stream->p_wf  = malloc( sizeof( WAVEFORMATEX ) +
276                                       p_input->p_fmt->i_extra );
277 #define p_wf p_stream->p_wf
278             p_wf->cbSize = p_input->p_fmt->i_extra;
279             if( p_wf->cbSize > 0 )
280             {
281                 memcpy( &p_wf[1],
282                         p_input->p_fmt->p_extra,
283                         p_input->p_fmt->i_extra );
284             }
285             p_wf->nChannels      = p_input->p_fmt->audio.i_channels;
286             p_wf->nSamplesPerSec = p_input->p_fmt->audio.i_rate;
287             p_wf->nBlockAlign    = p_input->p_fmt->audio.i_blockalign;
288             p_wf->nAvgBytesPerSec= p_input->p_fmt->i_bitrate / 8;
289             p_wf->wBitsPerSample = 0;
290
291             switch( p_input->p_fmt->i_codec )
292             {
293                 case VLC_FOURCC( 'a', '5', '2', ' ' ):
294                     p_wf->wFormatTag = WAVE_FORMAT_A52;
295                     break;
296                 case VLC_FOURCC( 'm', 'p', 'g', 'a' ):
297                     p_wf->wFormatTag = WAVE_FORMAT_MPEGLAYER3;
298                     break;
299                 case VLC_FOURCC( 'w', 'm', 'a', '1' ):
300                     p_wf->wFormatTag = WAVE_FORMAT_WMA1;
301                     break;
302                 case VLC_FOURCC( 'w', 'm', 'a', ' ' ):
303                 case VLC_FOURCC( 'w', 'm', 'a', '2' ):
304                     p_wf->wFormatTag = WAVE_FORMAT_WMA2;
305                     break;
306                 case VLC_FOURCC( 'w', 'm', 'a', 'p' ):
307                     p_wf->wFormatTag = WAVE_FORMAT_WMAP;
308                     break;
309                 case VLC_FOURCC( 'w', 'm', 'a', 'l' ):
310                     p_wf->wFormatTag = WAVE_FORMAT_WMAL;
311                     break;
312                     /* raw codec */
313                 case VLC_FOURCC( 'u', '8', ' ', ' ' ):
314                     p_wf->wFormatTag = WAVE_FORMAT_PCM;
315                     p_wf->nBlockAlign= p_wf->nChannels;
316                     p_wf->wBitsPerSample = 8;
317                     break;
318                 case VLC_FOURCC( 's', '1', '6', 'l' ):
319                     p_wf->wFormatTag = WAVE_FORMAT_PCM;
320                     p_wf->nBlockAlign= 2 * p_wf->nChannels;
321                     p_wf->wBitsPerSample = 16;
322                     break;
323                 case VLC_FOURCC( 's', '2', '4', 'l' ):
324                     p_wf->wFormatTag = WAVE_FORMAT_PCM;
325                     p_wf->nBlockAlign= 3 * p_wf->nChannels;
326                     p_wf->wBitsPerSample = 24;
327                     break;
328                 case VLC_FOURCC( 's', '3', '2', 'l' ):
329                     p_wf->wFormatTag = WAVE_FORMAT_PCM;
330                     p_wf->nBlockAlign= 4 * p_wf->nChannels;
331                     p_wf->wBitsPerSample = 32;
332                     break;
333                 default:
334                     return VLC_EGENERIC;
335             }
336 #undef p_wf
337             break;
338         case VIDEO_ES:
339             p_stream->i_cat = VIDEO_ES;
340             p_stream->fcc[0] = '0' + p_sys->i_streams / 10;
341             p_stream->fcc[1] = '0' + p_sys->i_streams % 10;
342             p_stream->fcc[2] = 'd';
343             p_stream->fcc[3] = 'c';
344             if( p_sys->i_stream_video < 0 )
345             {
346                 p_sys->i_stream_video = p_sys->i_streams;
347             }
348             p_stream->p_wf  = NULL;
349             p_stream->p_bih = malloc( sizeof( BITMAPINFOHEADER ) +
350                                       p_input->p_fmt->i_extra );
351 #define p_bih p_stream->p_bih
352             p_bih->biSize  = sizeof( BITMAPINFOHEADER ) +
353                              p_input->p_fmt->i_extra;
354             if( p_input->p_fmt->i_extra > 0 )
355             {
356                 memcpy( &p_bih[1],
357                         p_input->p_fmt->p_extra,
358                         p_input->p_fmt->i_extra );
359             }
360             p_bih->biWidth = p_input->p_fmt->video.i_width;
361             p_bih->biHeight= p_input->p_fmt->video.i_height;
362             p_bih->biPlanes= 1;
363             p_bih->biBitCount       = 24;
364             p_bih->biSizeImage      = 0;
365             p_bih->biXPelsPerMeter  = 0;
366             p_bih->biYPelsPerMeter  = 0;
367             p_bih->biClrUsed        = 0;
368             p_bih->biClrImportant   = 0;
369             switch( p_input->p_fmt->i_codec )
370             {
371                 case VLC_FOURCC( 'm', 'p', '4', 'v' ):
372                     p_bih->biCompression = VLC_FOURCC( 'X', 'V', 'I', 'D' );
373                     break;
374                 default:
375                     p_bih->biCompression = p_input->p_fmt->i_codec;
376                     break;
377             }
378 #undef p_bih
379             break;
380         default:
381             return( VLC_EGENERIC );
382     }
383     p_stream->i_totalsize = 0;
384     p_stream->i_frames    = 0;
385     p_stream->i_duration  = 0;
386
387     /* fixed later */
388     p_stream->f_fps = 25;
389     p_stream->i_bitrate = 128 * 1024;
390
391     p_sys->i_streams++;
392     return( VLC_SUCCESS );
393 }
394
395 static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
396 {
397
398     msg_Dbg( p_mux, "removing input" );
399
400     free( p_input->p_sys );
401     return( 0 );
402 }
403
404 static int Mux      ( sout_mux_t *p_mux )
405 {
406     sout_mux_sys_t  *p_sys = p_mux->p_sys;
407     avi_stream_t    *p_stream;
408     int i_stream, i;
409
410     if( p_sys->b_write_header )
411     {
412         block_t *p_hdr;
413
414         msg_Dbg( p_mux, "writing header" );
415
416         p_hdr = avi_HeaderCreateRIFF( p_mux );
417         sout_AccessOutWrite( p_mux->p_access, p_hdr );
418
419         p_sys->b_write_header = VLC_FALSE;
420     }
421
422     for( i = 0; i < p_mux->i_nb_inputs; i++ )
423     {
424         int i_count;
425         block_fifo_t *p_fifo;
426
427         i_stream = *((int*)p_mux->pp_inputs[i]->p_sys );
428         p_stream = &p_sys->stream[i_stream];
429
430         p_fifo = p_mux->pp_inputs[i]->p_fifo;
431         i_count = block_FifoCount(  p_fifo );
432         while( i_count > 1 )
433         {
434             avi_idx1_entry_t *p_idx;
435             block_t *p_data;
436
437             p_data = block_FifoGet( p_fifo );
438             if( block_FifoCount( p_fifo ) > 0 )
439             {
440                 block_t *p_next = block_FifoShow( p_fifo );
441                 p_data->i_length = p_next->i_dts - p_data->i_dts;
442             }
443
444             p_stream->i_frames++;
445             if( p_data->i_length < 0 )
446             {
447                 msg_Warn( p_mux, "argg length < 0 l" );
448                 block_Release( p_data );
449                 i_count--;
450                 continue;
451             }
452             p_stream->i_duration  += p_data->i_length;
453             p_stream->i_totalsize += p_data->i_buffer;
454
455             /* add idx1 entry for this frame */
456             p_idx = &p_sys->idx1.entry[p_sys->idx1.i_entry_count];
457             memcpy( p_idx->fcc, p_stream->fcc, 4 );
458             p_idx->i_flags = AVIIF_KEYFRAME;
459             p_idx->i_pos   = p_sys->i_movi_size + 4;
460             p_idx->i_length= p_data->i_buffer;
461             p_sys->idx1.i_entry_count++;
462             if( p_sys->idx1.i_entry_count >= p_sys->idx1.i_entry_max )
463             {
464                 p_sys->idx1.i_entry_max += 10000;
465                 p_sys->idx1.entry = realloc( p_sys->idx1.entry,
466                                              p_sys->idx1.i_entry_max * sizeof( avi_idx1_entry_t ) );
467             }
468
469             p_data = block_Realloc( p_data, 8, p_data->i_buffer );
470             if( p_data )
471             {
472                 SetFCC( p_data->p_buffer, p_stream->fcc );
473                 SetDWLE( p_data->p_buffer + 4, p_data->i_buffer - 8 );
474
475                 if( p_data->i_buffer & 0x01 )
476                 {
477                     p_data = block_Realloc( p_data, 0, p_data->i_buffer + 1 );
478                     p_data->p_buffer[ p_data->i_buffer - 1 ] = '\0';
479                 }
480
481                 p_sys->i_movi_size += p_data->i_buffer;
482                 sout_AccessOutWrite( p_mux->p_access, p_data );
483             }
484
485             i_count--;
486         }
487
488     }
489     return( 0 );
490 }
491
492 /****************************************************************************/
493 /****************************************************************************/
494 /****************************************************************************/
495 /****************************************************************************/
496
497 typedef struct buffer_out_s
498 {
499     int      i_buffer_size;
500     int      i_buffer;
501     uint8_t  *p_buffer;
502
503 } buffer_out_t;
504
505 static void bo_Init( buffer_out_t *p_bo, int i_size, uint8_t *p_buffer )
506 {
507     p_bo->i_buffer_size = i_size;
508     p_bo->i_buffer = 0;
509     p_bo->p_buffer = p_buffer;
510 }
511 static void bo_AddByte( buffer_out_t *p_bo, uint8_t i )
512 {
513     if( p_bo->i_buffer < p_bo->i_buffer_size )
514     {
515         p_bo->p_buffer[p_bo->i_buffer] = i;
516     }
517     p_bo->i_buffer++;
518 }
519 static void bo_AddWordLE( buffer_out_t *p_bo, uint16_t i )
520 {
521     bo_AddByte( p_bo, i &0xff );
522     bo_AddByte( p_bo, ( ( i >> 8) &0xff ) );
523 }
524 static void bo_AddWordBE( buffer_out_t *p_bo, uint16_t i )
525 {
526     bo_AddByte( p_bo, ( ( i >> 8) &0xff ) );
527     bo_AddByte( p_bo, i &0xff );
528 }
529 static void bo_AddDWordLE( buffer_out_t *p_bo, uint32_t i )
530 {
531     bo_AddWordLE( p_bo, i &0xffff );
532     bo_AddWordLE( p_bo, ( ( i >> 16) &0xffff ) );
533 }
534 static void bo_AddDWordBE( buffer_out_t *p_bo, uint32_t i )
535 {
536     bo_AddWordBE( p_bo, ( ( i >> 16) &0xffff ) );
537     bo_AddWordBE( p_bo, i &0xffff );
538 }
539 #if 0
540 static void bo_AddLWordLE( buffer_out_t *p_bo, uint64_t i )
541 {
542     bo_AddDWordLE( p_bo, i &0xffffffff );
543     bo_AddDWordLE( p_bo, ( ( i >> 32) &0xffffffff ) );
544 }
545 static void bo_AddLWordBE( buffer_out_t *p_bo, uint64_t i )
546 {
547     bo_AddDWordBE( p_bo, ( ( i >> 32) &0xffffffff ) );
548     bo_AddDWordBE( p_bo, i &0xffffffff );
549 }
550 #endif
551
552 static void bo_AddFCC( buffer_out_t *p_bo, const char *fcc )
553 {
554     bo_AddByte( p_bo, fcc[0] );
555     bo_AddByte( p_bo, fcc[1] );
556     bo_AddByte( p_bo, fcc[2] );
557     bo_AddByte( p_bo, fcc[3] );
558 }
559
560 static void bo_AddMem( buffer_out_t *p_bo, int i_size, uint8_t *p_mem )
561 {
562     int i;
563
564     for( i = 0; i < i_size; i++ )
565     {
566         bo_AddByte( p_bo, p_mem[i] );
567     }
568 }
569
570 /****************************************************************************
571  ****************************************************************************
572  **
573  ** avi header generation
574  **
575  ****************************************************************************
576  ****************************************************************************/
577 #define AVI_BOX_ENTER( fcc ) \
578     buffer_out_t _bo_sav_; \
579     bo_AddFCC( p_bo, fcc ); \
580     _bo_sav_ = *p_bo; \
581     bo_AddDWordLE( p_bo, 0 )
582
583 #define AVI_BOX_ENTER_LIST( fcc ) \
584     AVI_BOX_ENTER( "LIST" ); \
585     bo_AddFCC( p_bo, fcc )
586
587 #define AVI_BOX_EXIT( i_err ) \
588     if( p_bo->i_buffer&0x01 ) bo_AddByte( p_bo, 0 ); \
589     bo_AddDWordLE( &_bo_sav_, p_bo->i_buffer - _bo_sav_.i_buffer - 4 ); \
590     return( i_err );
591
592 static int avi_HeaderAdd_avih( sout_mux_t *p_mux,
593                                buffer_out_t *p_bo )
594 {
595     sout_mux_sys_t  *p_sys = p_mux->p_sys;
596     avi_stream_t    *p_video = NULL;
597     int         i_stream;
598     uint32_t    i_microsecperframe;
599     int         i_maxbytespersec;
600     int         i_totalframes;
601     AVI_BOX_ENTER( "avih" );
602
603     if( p_sys->i_stream_video >= 0 )
604     {
605         p_video = &p_sys->stream[p_sys->i_stream_video];
606         if( p_video->i_frames <= 0 )
607         {
608         //    p_video = NULL;
609         }
610     }
611
612     if( p_video )
613     {
614         i_microsecperframe =
615             (uint32_t)( (float)1000000 /
616                         (float)p_sys->stream[p_sys->i_stream_video].f_fps );
617         i_totalframes = p_sys->stream[p_sys->i_stream_video].i_frames;
618     }
619     else
620     {
621         msg_Warn( p_mux, "avi file without video track isn't a good idea..." );
622         i_microsecperframe = 0;
623         i_totalframes = 0;
624     }
625
626     for( i_stream = 0,i_maxbytespersec = 0; i_stream < p_sys->i_streams; i_stream++ )
627     {
628         if( p_sys->stream[i_stream].i_duration > 0 )
629         {
630             i_maxbytespersec +=
631                 p_sys->stream[i_stream].i_totalsize /
632                 p_sys->stream[i_stream].i_duration;
633         }
634     }
635
636     bo_AddDWordLE( p_bo, i_microsecperframe );
637     bo_AddDWordLE( p_bo, i_maxbytespersec );
638     bo_AddDWordLE( p_bo, 0 );                   /* padding */
639     bo_AddDWordLE( p_bo, AVIF_TRUSTCKTYPE |
640                          AVIF_HASINDEX |
641                          AVIF_ISINTERLEAVED );  /* flags */
642     bo_AddDWordLE( p_bo, i_totalframes );
643     bo_AddDWordLE( p_bo, 0 );                   /* initial frame */
644     bo_AddDWordLE( p_bo, p_sys->i_streams );    /* streams count */
645     bo_AddDWordLE( p_bo, 1024 * 1024 );         /* suggested buffer size */
646     if( p_video )
647     {
648         bo_AddDWordLE( p_bo, p_video->p_bih->biWidth );
649         bo_AddDWordLE( p_bo, p_video->p_bih->biHeight );
650     }
651     else
652     {
653         bo_AddDWordLE( p_bo, 0 );
654         bo_AddDWordLE( p_bo, 0 );
655     }
656     bo_AddDWordLE( p_bo, 0 );                   /* ???? */
657     bo_AddDWordLE( p_bo, 0 );                   /* ???? */
658     bo_AddDWordLE( p_bo, 0 );                   /* ???? */
659     bo_AddDWordLE( p_bo, 0 );                   /* ???? */
660
661     AVI_BOX_EXIT( 0 );
662 }
663 static int avi_HeaderAdd_strh( buffer_out_t *p_bo, avi_stream_t *p_stream )
664 {
665     AVI_BOX_ENTER( "strh" );
666
667     switch( p_stream->i_cat )
668     {
669         case VIDEO_ES:
670             {
671                 bo_AddFCC( p_bo, "vids" );
672                 bo_AddDWordBE( p_bo, p_stream->p_bih->biCompression );
673                 bo_AddDWordLE( p_bo, 0 );   /* flags */
674                 bo_AddWordLE(  p_bo, 0 );   /* priority */
675                 bo_AddWordLE(  p_bo, 0 );   /* langage */
676                 bo_AddDWordLE( p_bo, 0 );   /* initial frame */
677                 bo_AddDWordLE( p_bo, 1000 );/* scale */
678                 bo_AddDWordLE( p_bo, (uint32_t)( 1000 * p_stream->f_fps ));
679                 bo_AddDWordLE( p_bo, 0 );   /* start */
680                 bo_AddDWordLE( p_bo, p_stream->i_frames );
681                 bo_AddDWordLE( p_bo, 1024 * 1024 );
682                 bo_AddDWordLE( p_bo, -1 );  /* quality */
683                 bo_AddDWordLE( p_bo, 0 );   /* samplesize */
684                 bo_AddWordLE(  p_bo, 0 );   /* ??? */
685                 bo_AddWordLE(  p_bo, 0 );   /* ??? */
686                 bo_AddWordLE(  p_bo, p_stream->p_bih->biWidth );
687                 bo_AddWordLE(  p_bo, p_stream->p_bih->biHeight );
688             }
689             break;
690         case AUDIO_ES:
691             {
692                 int i_rate, i_scale, i_samplesize;
693
694                 i_samplesize = p_stream->p_wf->nBlockAlign;
695                 if( i_samplesize > 1 )
696                 {
697                     i_scale = i_samplesize;
698                     i_rate = /*i_scale **/ p_stream->i_bitrate / 8;
699                 }
700                 else
701                 {
702                     i_samplesize = 1;
703                     i_scale = 1000;
704                     i_rate = 1000 * p_stream->i_bitrate / 8;
705                 }
706                 bo_AddFCC( p_bo, "auds" );
707                 bo_AddDWordLE( p_bo, 0 );   /* tag */
708                 bo_AddDWordLE( p_bo, 0 );   /* flags */
709                 bo_AddWordLE(  p_bo, 0 );   /* priority */
710                 bo_AddWordLE(  p_bo, 0 );   /* langage */
711                 bo_AddDWordLE( p_bo, 0 );   /* initial frame */
712                 bo_AddDWordLE( p_bo, i_scale );/* scale */
713                 bo_AddDWordLE( p_bo, i_rate );
714                 bo_AddDWordLE( p_bo, 0 );   /* start */
715                 bo_AddDWordLE( p_bo, p_stream->i_frames );
716                 bo_AddDWordLE( p_bo, 10 * 1024 );
717                 bo_AddDWordLE( p_bo, -1 );  /* quality */
718                 bo_AddDWordLE( p_bo, i_samplesize );
719                 bo_AddWordLE(  p_bo, 0 );   /* ??? */
720                 bo_AddWordLE(  p_bo, 0 );   /* ??? */
721                 bo_AddWordLE(  p_bo, 0 );
722                 bo_AddWordLE(  p_bo, 0 );
723             }
724             break;
725     }
726
727     AVI_BOX_EXIT( 0 );
728 }
729
730 static int avi_HeaderAdd_strf( buffer_out_t *p_bo, avi_stream_t *p_stream )
731 {
732     AVI_BOX_ENTER( "strf" );
733
734     switch( p_stream->i_cat )
735     {
736         case AUDIO_ES:
737             bo_AddWordLE( p_bo, p_stream->p_wf->wFormatTag );
738             bo_AddWordLE( p_bo, p_stream->p_wf->nChannels );
739             bo_AddDWordLE( p_bo, p_stream->p_wf->nSamplesPerSec );
740             bo_AddDWordLE( p_bo, p_stream->p_wf->nAvgBytesPerSec );
741             bo_AddWordLE( p_bo, p_stream->p_wf->nBlockAlign );
742             bo_AddWordLE( p_bo, p_stream->p_wf->wBitsPerSample );
743             bo_AddWordLE( p_bo, p_stream->p_wf->cbSize );
744             bo_AddMem( p_bo, p_stream->p_wf->cbSize, (uint8_t*)&p_stream->p_wf[1] );
745             break;
746         case VIDEO_ES:
747             bo_AddDWordLE( p_bo, p_stream->p_bih->biSize );
748             bo_AddDWordLE( p_bo, p_stream->p_bih->biWidth );
749             bo_AddDWordLE( p_bo, p_stream->p_bih->biHeight );
750             bo_AddWordLE( p_bo, p_stream->p_bih->biPlanes );
751             bo_AddWordLE( p_bo, p_stream->p_bih->biBitCount );
752             if( VLC_FOURCC( 0, 0, 0, 1 ) == 0x00000001 )
753             {
754                 bo_AddDWordBE( p_bo, p_stream->p_bih->biCompression );
755             }
756             else
757             {
758                 bo_AddDWordLE( p_bo, p_stream->p_bih->biCompression );
759             }
760             bo_AddDWordLE( p_bo, p_stream->p_bih->biSizeImage );
761             bo_AddDWordLE( p_bo, p_stream->p_bih->biXPelsPerMeter );
762             bo_AddDWordLE( p_bo, p_stream->p_bih->biYPelsPerMeter );
763             bo_AddDWordLE( p_bo, p_stream->p_bih->biClrUsed );
764             bo_AddDWordLE( p_bo, p_stream->p_bih->biClrImportant );
765             bo_AddMem( p_bo,
766                        p_stream->p_bih->biSize - sizeof( BITMAPINFOHEADER ),
767                        (uint8_t*)&p_stream->p_bih[1] );
768             break;
769     }
770
771     AVI_BOX_EXIT( 0 );
772 }
773
774 static int avi_HeaderAdd_strl( buffer_out_t *p_bo, avi_stream_t *p_stream )
775 {
776     AVI_BOX_ENTER_LIST( "strl" );
777
778     avi_HeaderAdd_strh( p_bo, p_stream );
779     avi_HeaderAdd_strf( p_bo, p_stream );
780
781     AVI_BOX_EXIT( 0 );
782 }
783
784 static block_t *avi_HeaderCreateRIFF( sout_mux_t *p_mux )
785 {
786     sout_mux_sys_t      *p_sys = p_mux->p_sys;
787     block_t       *p_hdr;
788     int                 i_stream;
789     int                 i_maxbytespersec;
790     int                 i_junk;
791     buffer_out_t        bo;
792
793     p_hdr = block_New( p_mux, HDR_SIZE );
794     memset( p_hdr->p_buffer, 0, HDR_SIZE );
795
796     bo_Init( &bo, HDR_SIZE, p_hdr->p_buffer );
797
798     bo_AddFCC( &bo, "RIFF" );
799     bo_AddDWordLE( &bo, p_sys->i_movi_size + HDR_SIZE - 8 + p_sys->i_idx1_size );
800     bo_AddFCC( &bo, "AVI " );
801
802     bo_AddFCC( &bo, "LIST" );
803     bo_AddDWordLE( &bo, HDR_SIZE - 8);
804     bo_AddFCC( &bo, "hdrl" );
805
806     avi_HeaderAdd_avih( p_mux, &bo );
807     for( i_stream = 0,i_maxbytespersec = 0; i_stream < p_sys->i_streams; i_stream++ )
808     {
809         avi_HeaderAdd_strl( &bo, &p_sys->stream[i_stream] );
810     }
811
812     i_junk = HDR_SIZE - bo.i_buffer - 8 - 12;
813     bo_AddFCC( &bo, "JUNK" );
814     bo_AddDWordLE( &bo, i_junk );
815
816     bo.i_buffer += i_junk;
817     bo_AddFCC( &bo, "LIST" );
818     bo_AddDWordLE( &bo, p_sys->i_movi_size + 4 );
819     bo_AddFCC( &bo, "movi" );
820
821     return( p_hdr );
822 }
823
824 static block_t * avi_HeaderCreateidx1( sout_mux_t *p_mux )
825 {
826     sout_mux_sys_t      *p_sys = p_mux->p_sys;
827     block_t       *p_idx1;
828     uint32_t            i_idx1_size;
829     unsigned int        i;
830     buffer_out_t        bo;
831
832     i_idx1_size = 16 * p_sys->idx1.i_entry_count;
833
834     p_idx1 = block_New( p_mux, i_idx1_size + 8 );
835     memset( p_idx1->p_buffer, 0, i_idx1_size );
836
837     bo_Init( &bo, i_idx1_size, p_idx1->p_buffer );
838     bo_AddFCC( &bo, "idx1" );
839     bo_AddDWordLE( &bo, i_idx1_size );
840
841     for( i = 0; i < p_sys->idx1.i_entry_count; i++ )
842     {
843         bo_AddFCC( &bo, p_sys->idx1.entry[i].fcc );
844         bo_AddDWordLE( &bo, p_sys->idx1.entry[i].i_flags );
845         bo_AddDWordLE( &bo, p_sys->idx1.entry[i].i_pos );
846         bo_AddDWordLE( &bo, p_sys->idx1.entry[i].i_length );
847     }
848
849     return( p_idx1 );
850 }
851