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