* mux/avi.c: added index creation.
[vlc.git] / modules / mux / avi.c
1 /*****************************************************************************
2  * avi.c
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: avi.c,v 1.3 2003/01/13 04:46:49 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 /* TODO: add OpenDML write support */
28
29 #include <stdlib.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <fcntl.h>
35
36 #include <vlc/vlc.h>
37 #include <vlc/input.h>
38 #include <vlc/sout.h>
39
40 #ifdef HAVE_UNISTD_H
41 #   include <unistd.h>
42 #elif defined( _MSC_VER ) && defined( _WIN32 ) && !defined( UNDER_CE )
43 #   include <io.h>
44 #endif
45
46 #include "codecs.h"
47
48
49 #define AVIF_HASINDEX       0x00000010  // Index at end of file?
50 #define AVIF_MUSTUSEINDEX   0x00000020
51 #define AVIF_ISINTERLEAVED  0x00000100
52 #define AVIF_TRUSTCKTYPE    0x00000800  // Use CKType to find key frames?
53 #define AVIF_WASCAPTUREFILE 0x00010000
54 #define AVIF_COPYRIGHTED    0x00020000
55
56 /* Flags for index */
57 #define AVIIF_LIST          0x00000001L /* chunk is a 'LIST' */
58 #define AVIIF_KEYFRAME      0x00000010L /* this frame is a key frame.*/
59 #define AVIIF_NOTIME        0x00000100L /* this frame doesn't take any time */
60 #define AVIIF_COMPUSE       0x0FFF0000L /* these bits are for compressor use */
61
62 /*****************************************************************************
63  * Exported prototypes
64  *****************************************************************************/
65 static int     Open   ( vlc_object_t * );
66 static void    Close  ( vlc_object_t * );
67
68 static int AddStream( sout_instance_t *, sout_input_t * );
69 static int DelStream( sout_instance_t *, sout_input_t * );
70 static int Mux      ( sout_instance_t * );
71
72 static sout_buffer_t *avi_HeaderCreateRIFF( sout_instance_t *p_sout );
73 static sout_buffer_t *avi_HeaderCreateidx1( sout_instance_t *p_sout );
74
75 static void SetFCC( uint8_t *p, char *fcc )
76 {
77     p[0] = fcc[0];
78     p[1] = fcc[1];
79     p[2] = fcc[2];
80     p[3] = fcc[3];
81 }
82
83 static void SetDWLE( uint8_t *p, uint32_t i_dw )
84 {
85     p[3] = ( i_dw >> 24 )&0xff;
86     p[2] = ( i_dw >> 16 )&0xff;
87     p[1] = ( i_dw >>  8 )&0xff;
88     p[0] = ( i_dw       )&0xff;
89 }
90
91 /*****************************************************************************
92  * Module descriptor
93  *****************************************************************************/
94 vlc_module_begin();
95     set_description( _("Avi muxer") );
96     set_capability( "sout mux", 5 );
97     add_shortcut( "avi" );
98     set_callbacks( Open, Close );
99 vlc_module_end();
100
101 // FIXME FIXME
102 #define HDR_SIZE 10240
103
104 typedef struct avi_stream_s
105 {
106     int i_cat;
107
108     char fcc[4];
109
110     mtime_t i_duration;       // in ┬Ás
111
112     int     i_frames;        // total frame count
113     int64_t i_totalsize;    // total stream size
114
115     float   f_fps;
116     int     i_bitrate;
117
118     BITMAPINFOHEADER    *p_bih;
119     WAVEFORMATEX        *p_wf;
120
121 } avi_stream_t;
122
123 typedef struct avi_idx1_entry_s
124 {
125     char     fcc[4];
126     uint32_t i_flags;
127     uint32_t i_pos;
128     uint32_t i_length;
129
130 } avi_idx1_entry_t;
131
132 typedef struct avi_idx1_s
133 {
134     unsigned int i_entry_count;
135     unsigned int i_entry_max;
136
137     avi_idx1_entry_t *entry;
138 } avi_idx1_t;
139
140 typedef struct sout_mux_s
141 {
142     int i_streams;
143     int i_stream_video;
144
145     off_t i_movi_size;
146     avi_stream_t stream[100];
147
148     avi_idx1_t idx1;
149     off_t i_idx1_size;
150
151 } sout_mux_t;
152
153 /*****************************************************************************
154  * Open:
155  *****************************************************************************/
156 static int Open( vlc_object_t *p_this )
157 {
158     sout_instance_t     *p_sout = (sout_instance_t*)p_this;
159     sout_mux_t          *p_mux;
160     sout_buffer_t       *p_hdr;
161
162     p_mux = malloc( sizeof( sout_mux_t ) );
163     p_mux->i_streams = 0;
164     p_mux->i_stream_video = -1;
165     p_mux->i_movi_size = 0;
166
167     p_mux->idx1.i_entry_count = 0;
168     p_mux->idx1.i_entry_max = 10000;
169     p_mux->idx1.entry = calloc( p_mux->idx1.i_entry_max, sizeof( avi_idx1_entry_t ) );
170
171     msg_Info( p_sout, "Open" );
172
173     p_sout->pf_mux_addstream = AddStream;
174     p_sout->pf_mux_delstream = DelStream;
175     p_sout->pf_mux           = Mux;
176     p_sout->p_mux_data = (void*)p_mux;
177     p_sout->i_mux_preheader  = 8; // (fourcc,length) header
178
179     /* room to add header at the end */
180     p_hdr = sout_BufferNew( p_sout, HDR_SIZE );
181     memset( p_hdr->p_buffer, 0, HDR_SIZE );
182     p_sout->pf_write( p_sout, p_hdr );
183
184     return VLC_SUCCESS;
185 }
186
187 /*****************************************************************************
188  * Close:
189  *****************************************************************************/
190
191 static void Close( vlc_object_t * p_this )
192 {
193     sout_instance_t     *p_sout = (sout_instance_t*)p_this;
194     sout_mux_t          *p_mux = (sout_mux_t*)p_sout->p_mux_data;
195     sout_buffer_t       *p_hdr, *p_idx1;
196     int                 i_stream;
197
198     msg_Info( p_sout, "Close" );
199
200     /* first create idx1 chunk (write at the end of the stream */
201     p_idx1 = avi_HeaderCreateidx1( p_sout );
202     p_mux->i_idx1_size = p_idx1->i_size;
203     p_sout->pf_write( p_sout, p_idx1 );
204
205     /* calculate some value for headers creations */
206     for( i_stream = 0; i_stream < p_mux->i_streams; i_stream++ )
207     {
208         avi_stream_t *p_stream;
209
210         p_stream = &p_mux->stream[i_stream];
211
212         p_stream->f_fps = 25;
213         if( p_stream->i_duration > 0 )
214         {
215             p_stream->f_fps = (float)p_stream->i_frames /
216                               ( (float)p_stream->i_duration /
217                                 (float)1000000 );
218         }
219         p_stream->i_bitrate = 128 * 1024;
220         if( p_stream->i_duration > 0 )
221         {
222             p_stream->i_bitrate =
223                 8 * (uint64_t)1000000 *
224                     (uint64_t)p_stream->i_totalsize /
225                     (uint64_t)p_stream->i_duration;
226         }
227         msg_Err( p_sout,"stream[%d] duration:%lld totalsize:%lld frames:%d fps:%f kb/s:%d",
228                 i_stream,
229                 p_stream->i_duration/1000000, p_stream->i_totalsize,
230                 p_stream->i_frames,
231                 p_stream->f_fps, p_stream->i_bitrate/1024 );
232     }
233
234     p_hdr = avi_HeaderCreateRIFF( p_sout );
235     p_sout->pf_seek( p_sout, 0 );
236     p_sout->pf_write( p_sout, p_hdr );
237 }
238
239
240 static int AddStream( sout_instance_t *p_sout, sout_input_t *p_input )
241 {
242     sout_mux_t          *p_mux = (sout_mux_t*)p_sout->p_mux_data;
243     avi_stream_t        *p_stream;
244
245     if( p_mux->i_streams >= 100 )
246     {
247         msg_Err( p_sout, "too many streams" );
248         return( -1 );
249     }
250     if( p_input->input_format.p_format == NULL )
251     {
252         msg_Err( p_sout, "stream descriptor missing" );
253         return( -1 );
254     }
255
256     msg_Dbg( p_sout, "adding input" );
257     p_input->p_mux_data = malloc( sizeof( int ) );
258
259     *((int*)p_input->p_mux_data) = p_mux->i_streams;
260     p_stream = &p_mux->stream[p_mux->i_streams];
261
262     switch( p_input->input_format.i_cat )
263     {
264         case AUDIO_ES:
265             {
266                 WAVEFORMATEX *p_wf =
267                     (WAVEFORMATEX*)p_input->input_format.p_format;
268
269                 p_stream->i_cat = AUDIO_ES;
270                 p_stream->fcc[0] = '0' + p_mux->i_streams / 10;
271                 p_stream->fcc[1] = '0' + p_mux->i_streams % 10;
272                 p_stream->fcc[2] = 'w';
273                 p_stream->fcc[3] = 'b';
274
275                 p_stream->p_bih = NULL;
276                 p_stream->p_wf  = malloc( sizeof( WAVEFORMATEX ) + p_wf->cbSize );
277                 memcpy( p_stream->p_wf,
278                         p_wf,
279                         sizeof( WAVEFORMATEX ) + p_wf->cbSize);
280             }
281             break;
282         case VIDEO_ES:
283             {
284                 BITMAPINFOHEADER *p_bih =
285                     (BITMAPINFOHEADER*)p_input->input_format.p_format;;
286
287                 p_stream->i_cat = VIDEO_ES;
288                 p_stream->fcc[0] = '0' + p_mux->i_streams / 10;
289                 p_stream->fcc[1] = '0' + p_mux->i_streams % 10;
290                 p_stream->fcc[2] = 'd';
291                 p_stream->fcc[3] = 'c';
292                 if( p_mux->i_stream_video < 0 )
293                 {
294                     p_mux->i_stream_video = p_mux->i_streams;
295                 }
296                 p_stream->p_wf  = NULL;
297                 p_stream->p_bih = malloc( p_bih->biSize );
298                 memcpy( p_stream->p_bih,
299                         p_bih,
300                         p_bih->biSize );
301             }
302             break;
303         default:
304             return( -1 );
305     }
306     p_stream->i_totalsize = 0;
307     p_stream->i_frames     = 0;
308     p_stream->i_duration  = 0;
309
310     p_mux->i_streams++;
311     return( 0 );
312 }
313
314 static int DelStream( sout_instance_t *p_sout, sout_input_t *p_input )
315 {
316 //    sout_mux_t          *p_mux = (sout_mux_t*)p_sout->p_mux_data;
317
318     msg_Dbg( p_sout, "removing input" );
319
320     free( p_input->p_mux_data ); p_input->p_mux_data = NULL;
321
322     return( 0 );
323 }
324
325 static int Mux      ( sout_instance_t *p_sout )
326 {
327     sout_mux_t          *p_mux = (sout_mux_t*)p_sout->p_mux_data;
328     avi_stream_t        *p_stream;
329     int i_stream;
330     int i;
331
332     for( i = 0; i < p_sout->i_nb_inputs; i++ )
333     {
334         int i_count;
335         sout_fifo_t *p_fifo;
336
337         i_stream = *((int*)p_sout->pp_inputs[i]->p_mux_data );
338         p_stream = &p_mux->stream[i_stream];
339
340         p_fifo = p_sout->pp_inputs[i]->p_fifo;
341         i_count = p_fifo->i_depth;
342         while( i_count > 0 )
343         {
344             avi_idx1_entry_t *p_idx;
345             sout_buffer_t *p_data;
346
347             p_data = sout_FifoGet( p_fifo );
348
349             p_stream->i_frames++;
350             p_stream->i_duration  += p_data->i_length;
351             p_stream->i_totalsize += p_data->i_size;
352
353             /* add idx1 entry for this frame */
354             p_idx = &p_mux->idx1.entry[p_mux->idx1.i_entry_count];
355             memcpy( p_idx->fcc, p_stream->fcc, 4 );
356             p_idx->i_flags = AVIIF_KEYFRAME;
357             p_idx->i_pos   = p_mux->i_movi_size + 4;
358             p_idx->i_length= p_data->i_size;
359             p_mux->idx1.i_entry_count++;
360             if( p_mux->idx1.i_entry_count >= p_mux->idx1.i_entry_max )
361             {
362                 p_mux->idx1.i_entry_max += 10000;
363                 p_mux->idx1.entry = realloc( p_mux->idx1.entry,
364                                              p_mux->idx1.i_entry_max * sizeof( avi_idx1_entry_t ) );
365             }
366
367
368             if( sout_BufferReallocFromPreHeader( p_sout, p_data, 8 ) )
369             {
370                 /* there isn't enough data in preheader */
371                 sout_buffer_t *p_hdr;
372
373                 p_hdr = sout_BufferNew( p_sout, 8 );
374                 SetFCC( p_hdr->p_buffer, p_stream->fcc );
375                 SetDWLE( p_hdr->p_buffer + 4, p_data->i_size );
376
377                 p_sout->pf_write( p_sout, p_hdr );
378                 p_mux->i_movi_size += p_hdr->i_size;
379
380             }
381             else
382             {
383                 SetFCC( p_data->p_buffer, p_stream->fcc );
384                 SetDWLE( p_data->p_buffer + 4, p_data->i_size - 8 );
385             }
386
387             if( p_data->i_size & 0x01 )
388             {
389                 sout_BufferRealloc( p_sout, p_data, p_data->i_size + 1 );
390                 p_data->i_size += 1;
391             }
392
393             p_sout->pf_write( p_sout, p_data );
394             p_mux->i_movi_size += p_data->i_size;
395
396             i_count--;
397         }
398
399     }
400     return( 0 );
401 }
402
403 /****************************************************************************/
404 /****************************************************************************/
405 /****************************************************************************/
406 /****************************************************************************/
407
408 typedef struct buffer_out_s
409 {
410     int      i_buffer_size;
411     int      i_buffer;
412     uint8_t  *p_buffer;
413
414 } buffer_out_t;
415
416 static void bo_Init( buffer_out_t *p_bo, int i_size, uint8_t *p_buffer )
417 {
418     p_bo->i_buffer_size = i_size;
419     p_bo->i_buffer = 0;
420     p_bo->p_buffer = p_buffer;
421 }
422 static void bo_AddByte( buffer_out_t *p_bo, uint8_t i )
423 {
424     if( p_bo->i_buffer < p_bo->i_buffer_size )
425     {
426         p_bo->p_buffer[p_bo->i_buffer] = i;
427     }
428     p_bo->i_buffer++;
429 }
430 static void bo_AddWordLE( buffer_out_t *p_bo, uint16_t i )
431 {
432     bo_AddByte( p_bo, i &0xff );
433     bo_AddByte( p_bo, ( ( i >> 8) &0xff ) );
434 }
435 static void bo_AddWordBE( buffer_out_t *p_bo, uint16_t i )
436 {
437     bo_AddByte( p_bo, ( ( i >> 8) &0xff ) );
438     bo_AddByte( p_bo, i &0xff );
439 }
440 static void bo_AddDWordLE( buffer_out_t *p_bo, uint32_t i )
441 {
442     bo_AddWordLE( p_bo, i &0xffff );
443     bo_AddWordLE( p_bo, ( ( i >> 16) &0xffff ) );
444 }
445 static void bo_AddDWordBE( buffer_out_t *p_bo, uint32_t i )
446 {
447     bo_AddWordBE( p_bo, ( ( i >> 16) &0xffff ) );
448     bo_AddWordBE( p_bo, i &0xffff );
449 }
450 #if 0
451 static void bo_AddLWordLE( buffer_out_t *p_bo, uint64_t i )
452 {
453     bo_AddDWordLE( p_bo, i &0xffffffff );
454     bo_AddDWordLE( p_bo, ( ( i >> 32) &0xffffffff ) );
455 }
456 static void bo_AddLWordBE( buffer_out_t *p_bo, uint64_t i )
457 {
458     bo_AddDWordBE( p_bo, ( ( i >> 32) &0xffffffff ) );
459     bo_AddDWordBE( p_bo, i &0xffffffff );
460 }
461 #endif
462
463 static void bo_AddFCC( buffer_out_t *p_bo, char *fcc )
464 {
465     bo_AddByte( p_bo, fcc[0] );
466     bo_AddByte( p_bo, fcc[1] );
467     bo_AddByte( p_bo, fcc[2] );
468     bo_AddByte( p_bo, fcc[3] );
469 }
470
471 static void bo_AddMem( buffer_out_t *p_bo, int i_size, uint8_t *p_mem )
472 {
473     int i;
474
475     for( i = 0; i < i_size; i++ )
476     {
477         bo_AddByte( p_bo, p_mem[i] );
478     }
479 }
480
481 /****************************************************************************
482  ****************************************************************************
483  **
484  ** avi header generation
485  **
486  ****************************************************************************
487  ****************************************************************************/
488 #define AVI_BOX_ENTER( fcc ) \
489     buffer_out_t _bo_sav_; \
490     bo_AddFCC( p_bo, fcc ); \
491     _bo_sav_ = *p_bo; \
492     bo_AddDWordLE( p_bo, 0 )
493
494 #define AVI_BOX_ENTER_LIST( fcc ) \
495     AVI_BOX_ENTER( "LIST" ); \
496     bo_AddFCC( p_bo, fcc )
497
498 #define AVI_BOX_EXIT( i_err ) \
499     if( p_bo->i_buffer&0x01 ) bo_AddByte( p_bo, 0 ); \
500     bo_AddDWordLE( &_bo_sav_, p_bo->i_buffer - _bo_sav_.i_buffer - 4 ); \
501     return( i_err );
502
503 static int avi_HeaderAdd_avih( sout_instance_t *p_sout,
504                                buffer_out_t *p_bo )
505 {
506     sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
507     avi_stream_t *p_video = NULL;
508     int         i_stream;
509     uint32_t    i_microsecperframe;
510     int         i_maxbytespersec;
511     int         i_totalframes;
512     AVI_BOX_ENTER( "avih" );
513
514     if( p_mux->i_stream_video >= 0 )
515     {
516         p_video = &p_mux->stream[p_mux->i_stream_video];
517         if( p_video->i_frames <= 0 )
518         {
519             p_video = NULL;
520         }
521     }
522
523     if( p_video )
524     {
525         i_microsecperframe =
526             (uint32_t)( (float)1000000 /
527                         (float)p_mux->stream[p_mux->i_stream_video].f_fps );
528         i_totalframes = p_mux->stream[p_mux->i_stream_video].i_frames;
529     }
530     else
531     {
532         msg_Warn( p_sout, "avi file without audio video track isn't a good idea..." );
533         i_microsecperframe = 0;
534         i_totalframes = 0;
535     }
536
537     for( i_stream = 0,i_maxbytespersec = 0; i_stream < p_mux->i_streams; i_stream++ )
538     {
539         if( p_mux->stream[p_mux->i_stream_video].i_duration > 0 )
540         {
541             i_maxbytespersec +=
542                 p_mux->stream[p_mux->i_stream_video].i_totalsize /
543                 p_mux->stream[p_mux->i_stream_video].i_duration;
544         }
545     }
546
547     bo_AddDWordLE( p_bo, i_microsecperframe );
548     bo_AddDWordLE( p_bo, i_maxbytespersec );
549     bo_AddDWordLE( p_bo, 0 );                   /* padding */
550     bo_AddDWordLE( p_bo, AVIF_TRUSTCKTYPE |
551                          AVIF_HASINDEX |
552                          AVIF_ISINTERLEAVED );  /* flags */
553     bo_AddDWordLE( p_bo, i_totalframes );
554     bo_AddDWordLE( p_bo, 0 );                   /* initial frame */
555     bo_AddDWordLE( p_bo, p_mux->i_streams );    /* streams count */
556     bo_AddDWordLE( p_bo, 1024 * 1024 );         /* suggested buffer size */
557     if( p_video )
558     {
559         bo_AddDWordLE( p_bo, p_video->p_bih->biWidth );
560         bo_AddDWordLE( p_bo, p_video->p_bih->biHeight );
561     }
562     else
563     {
564         bo_AddDWordLE( p_bo, 0 );
565         bo_AddDWordLE( p_bo, 0 );
566     }
567     bo_AddDWordLE( p_bo, 0 );                   /* ???? */
568     bo_AddDWordLE( p_bo, 0 );                   /* ???? */
569     bo_AddDWordLE( p_bo, 0 );                   /* ???? */
570     bo_AddDWordLE( p_bo, 0 );                   /* ???? */
571
572     AVI_BOX_EXIT( 0 );
573 }
574 static int avi_HeaderAdd_strh( sout_instance_t *p_sout,
575                                buffer_out_t *p_bo,
576                                avi_stream_t *p_stream )
577 {
578 //    sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
579     AVI_BOX_ENTER( "strh" );
580
581     switch( p_stream->i_cat )
582     {
583         case VIDEO_ES:
584             {
585                 bo_AddFCC( p_bo, "vids" );
586                 bo_AddDWordBE( p_bo, p_stream->p_bih->biCompression );
587                 bo_AddDWordLE( p_bo, 0 );   /* flags */
588                 bo_AddWordLE(  p_bo, 0 );   /* priority */
589                 bo_AddWordLE(  p_bo, 0 );   /* langage */
590                 bo_AddDWordLE( p_bo, 0 );   /* initial frame */
591                 bo_AddDWordLE( p_bo, 1000 );/* scale */
592                 bo_AddDWordLE( p_bo, (uint32_t)( 1000 * p_stream->f_fps ));
593                 bo_AddDWordLE( p_bo, 0 );   /* start */
594                 bo_AddDWordLE( p_bo, p_stream->i_frames );
595                 bo_AddDWordLE( p_bo, 1024 * 1024 );
596                 bo_AddDWordLE( p_bo, -1 );  /* quality */
597                 bo_AddDWordLE( p_bo, 0 );   /* samplesize */
598                 bo_AddWordLE(  p_bo, 0 );   /* ??? */
599                 bo_AddWordLE(  p_bo, 0 );   /* ??? */
600                 bo_AddWordLE(  p_bo, p_stream->p_bih->biWidth );
601                 bo_AddWordLE(  p_bo, p_stream->p_bih->biHeight );
602             }
603             break;
604         case AUDIO_ES:
605             {
606                 int i_rate, i_scale, i_samplesize;
607
608                 i_samplesize = p_stream->p_wf->nBlockAlign;
609                 if( i_samplesize > 1 )
610                 {
611                     i_scale = i_samplesize;
612                     i_rate = i_scale * p_stream->i_bitrate / 8;
613                 }
614                 else
615                 {
616                     i_samplesize = 1;
617                     i_scale = 1000;
618                     i_rate = 1000 * p_stream->i_bitrate / 8;
619                 }
620                 bo_AddFCC( p_bo, "auds" );
621                 bo_AddDWordLE( p_bo, 0 );   /* tag */
622                 bo_AddDWordLE( p_bo, 0 );   /* flags */
623                 bo_AddWordLE(  p_bo, 0 );   /* priority */
624                 bo_AddWordLE(  p_bo, 0 );   /* langage */
625                 bo_AddDWordLE( p_bo, 0 );   /* initial frame */
626                 bo_AddDWordLE( p_bo, i_scale );/* scale */
627                 bo_AddDWordLE( p_bo, i_rate );
628                 bo_AddDWordLE( p_bo, 0 );   /* start */
629                 bo_AddDWordLE( p_bo, p_stream->i_frames );
630                 bo_AddDWordLE( p_bo, 10 * 1024 );
631                 bo_AddDWordLE( p_bo, -1 );  /* quality */
632                 bo_AddDWordLE( p_bo, i_samplesize );
633                 bo_AddWordLE(  p_bo, 0 );   /* ??? */
634                 bo_AddWordLE(  p_bo, 0 );   /* ??? */
635                 bo_AddWordLE(  p_bo, 0 );
636                 bo_AddWordLE(  p_bo, 0 );
637             }
638             break;
639     }
640
641     AVI_BOX_EXIT( 0 );
642 }
643
644 static int avi_HeaderAdd_strf( sout_instance_t *p_sout,
645                                buffer_out_t *p_bo,
646                                avi_stream_t *p_stream )
647 {
648 //    sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
649     AVI_BOX_ENTER( "strf" );
650
651     switch( p_stream->i_cat )
652     {
653         case AUDIO_ES:
654             bo_AddWordLE( p_bo, p_stream->p_wf->wFormatTag );
655             bo_AddWordLE( p_bo, p_stream->p_wf->nChannels );
656             bo_AddDWordLE( p_bo, p_stream->p_wf->nSamplesPerSec );
657             bo_AddDWordLE( p_bo, p_stream->p_wf->nAvgBytesPerSec );
658             bo_AddWordLE( p_bo, p_stream->p_wf->nBlockAlign );
659             bo_AddWordLE( p_bo, p_stream->p_wf->wBitsPerSample );
660             bo_AddWordLE( p_bo, p_stream->p_wf->cbSize );
661             bo_AddMem( p_bo, p_stream->p_wf->cbSize, (uint8_t*)&p_stream->p_wf[1] );
662             break;
663         case VIDEO_ES:
664             bo_AddDWordLE( p_bo, p_stream->p_bih->biSize );
665             bo_AddDWordLE( p_bo, p_stream->p_bih->biWidth );
666             bo_AddDWordLE( p_bo, p_stream->p_bih->biHeight );
667             bo_AddWordLE( p_bo, p_stream->p_bih->biPlanes );
668             bo_AddWordLE( p_bo, p_stream->p_bih->biBitCount );
669             bo_AddDWordBE( p_bo, p_stream->p_bih->biCompression );
670             bo_AddDWordLE( p_bo, p_stream->p_bih->biSizeImage );
671             bo_AddDWordLE( p_bo, p_stream->p_bih->biXPelsPerMeter );
672             bo_AddDWordLE( p_bo, p_stream->p_bih->biYPelsPerMeter );
673             bo_AddDWordLE( p_bo, p_stream->p_bih->biClrUsed );
674             bo_AddDWordLE( p_bo, p_stream->p_bih->biClrImportant );
675             bo_AddMem( p_bo,
676                        p_stream->p_bih->biSize - sizeof( BITMAPINFOHEADER ),
677                        (uint8_t*)&p_stream->p_bih[1] );
678             break;
679     }
680
681     AVI_BOX_EXIT( 0 );
682 }
683
684 static int avi_HeaderAdd_strl( sout_instance_t *p_sout,
685                                buffer_out_t *p_bo,
686                                avi_stream_t *p_stream )
687 {
688 //    sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
689     AVI_BOX_ENTER_LIST( "strl" );
690
691     avi_HeaderAdd_strh( p_sout, p_bo, p_stream );
692     avi_HeaderAdd_strf( p_sout, p_bo, p_stream );
693
694     AVI_BOX_EXIT( 0 );
695 }
696
697 static sout_buffer_t *avi_HeaderCreateRIFF( sout_instance_t *p_sout )
698 {
699     sout_mux_t          *p_mux = (sout_mux_t*)p_sout->p_mux_data;
700     sout_buffer_t       *p_hdr;
701     int                 i_stream;
702     int                 i_maxbytespersec;
703     int                 i_junk;
704     buffer_out_t        bo;
705
706     p_hdr = sout_BufferNew( p_sout, HDR_SIZE );
707     memset( p_hdr->p_buffer, 0, HDR_SIZE );
708
709     bo_Init( &bo, HDR_SIZE, p_hdr->p_buffer );
710
711     bo_AddFCC( &bo, "RIFF" );
712     bo_AddDWordLE( &bo, p_mux->i_movi_size + HDR_SIZE - 8 + p_mux->i_idx1_size );
713     bo_AddFCC( &bo, "AVI " );
714
715     bo_AddFCC( &bo, "LIST" );
716     bo_AddDWordLE( &bo, HDR_SIZE - 8);
717     bo_AddFCC( &bo, "hdrl" );
718
719     avi_HeaderAdd_avih( p_sout, &bo );
720     for( i_stream = 0,i_maxbytespersec = 0; i_stream < p_mux->i_streams; i_stream++ )
721     {
722         avi_HeaderAdd_strl( p_sout, &bo, &p_mux->stream[i_stream] );
723     }
724
725     i_junk = HDR_SIZE - bo.i_buffer - 8 - 12;
726     bo_AddFCC( &bo, "JUNK" );
727     bo_AddDWordLE( &bo, i_junk );
728
729     bo.i_buffer += i_junk;
730     bo_AddFCC( &bo, "LIST" );
731     bo_AddDWordLE( &bo, p_mux->i_movi_size + 4 );
732     bo_AddFCC( &bo, "movi" );
733
734     return( p_hdr );
735 }
736
737 static sout_buffer_t * avi_HeaderCreateidx1( sout_instance_t *p_sout )
738 {
739     sout_mux_t          *p_mux = (sout_mux_t*)p_sout->p_mux_data;
740     sout_buffer_t       *p_idx1;
741     uint32_t            i_idx1_size;
742     unsigned int        i;
743     buffer_out_t        bo;
744
745     i_idx1_size = 16 * p_mux->idx1.i_entry_count;
746
747     p_idx1 = sout_BufferNew( p_sout, i_idx1_size + 8 );
748     memset( p_idx1->p_buffer, 0, i_idx1_size );
749
750     bo_Init( &bo, i_idx1_size, p_idx1->p_buffer );
751     bo_AddFCC( &bo, "idx1" );
752     bo_AddDWordLE( &bo, i_idx1_size );
753
754     for( i = 0; i < p_mux->idx1.i_entry_count; i++ )
755     {
756         bo_AddFCC( &bo, p_mux->idx1.entry[i].fcc );
757         bo_AddDWordLE( &bo, p_mux->idx1.entry[i].i_flags );
758         bo_AddDWordLE( &bo, p_mux->idx1.entry[i].i_pos );
759         bo_AddDWordLE( &bo, p_mux->idx1.entry[i].i_length );
760     }
761
762     return( p_idx1 );
763 }
764
765