]> git.sesse.net Git - vlc/blob - modules/mux/avi.c
mux: avi: handle failed reallocs
[vlc] / modules / mux / avi.c
1 /*****************************************************************************
2  * avi.c
3  *****************************************************************************
4  * Copyright (C) 2001-2009 VLC authors and VideoLAN
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 it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * 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_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_sout.h>
37 #include <vlc_block.h>
38 #include <vlc_codecs.h>
39 #include <vlc_bits.h>
40
41 /*****************************************************************************
42  * Module descriptor
43  *****************************************************************************/
44 static int  Open   ( vlc_object_t * );
45 static void Close  ( vlc_object_t * );
46
47 #define SOUT_CFG_PREFIX "sout-avi-"
48
49 #define CFG_ARTIST_TEXT     N_("Artist")
50 #define CFG_DATE_TEXT       N_("Date")
51 #define CFG_GENRE_TEXT      N_("Genre")
52 #define CFG_COPYRIGHT_TEXT  N_("Copyright")
53 #define CFG_COMMENT_TEXT    N_("Comment")
54 #define CFG_NAME_TEXT       N_("Name")
55 #define CFG_SUBJECT_TEXT    N_("Subject")
56 #define CFG_ENCODER_TEXT    N_("Encoder")
57 #define CFG_KEYWORDS_TEXT   N_("Keywords")
58
59 vlc_module_begin ()
60     set_description( N_("AVI muxer") )
61     set_category( CAT_SOUT )
62     set_subcategory( SUBCAT_SOUT_MUX )
63     set_capability( "sout mux", 5 )
64     add_shortcut( "avi" )
65
66     add_string( SOUT_CFG_PREFIX "artist", NULL,    CFG_ARTIST_TEXT, NULL, true )
67     add_string( SOUT_CFG_PREFIX "date",   NULL,    CFG_DATE_TEXT, NULL, true )
68     add_string( SOUT_CFG_PREFIX "genre",  NULL,    CFG_GENRE_TEXT, NULL, true )
69     add_string( SOUT_CFG_PREFIX "copyright", NULL, CFG_COPYRIGHT_TEXT, NULL, true )
70     add_string( SOUT_CFG_PREFIX "comment", NULL,   CFG_COMMENT_TEXT, NULL, true )
71     add_string( SOUT_CFG_PREFIX "name", NULL,      CFG_NAME_TEXT, NULL, true )
72     add_string( SOUT_CFG_PREFIX "subject", NULL,   CFG_SUBJECT_TEXT, NULL, true )
73     add_string( SOUT_CFG_PREFIX "encoder",
74                 "VLC Media Player - " VERSION_MESSAGE,
75                                                    CFG_ENCODER_TEXT, NULL, true )
76     add_string( SOUT_CFG_PREFIX "keywords", NULL,  CFG_KEYWORDS_TEXT, NULL, true )
77
78     set_callbacks( Open, Close )
79 vlc_module_end ()
80
81
82 /*****************************************************************************
83  * Local prototypes
84  *****************************************************************************/
85 static int Control( sout_mux_t *, int, va_list );
86 static int AddStream( sout_mux_t *, sout_input_t * );
87 static void DelStream( sout_mux_t *, sout_input_t * );
88 static int Mux      ( sout_mux_t * );
89
90 typedef struct avi_stream_s
91 {
92     int i_cat;
93
94     char fcc[4];
95
96     mtime_t i_duration;       // in µs
97
98     int     i_frames;        // total frame count
99     int64_t i_totalsize;    // total stream size
100
101     float   f_fps;
102     int     i_bitrate;
103
104     VLC_BITMAPINFOHEADER    *p_bih;
105     WAVEFORMATEX            *p_wf;
106
107 } avi_stream_t;
108
109 typedef struct avi_idx1_entry_s
110 {
111     char     fcc[4];
112     uint32_t i_flags;
113     uint32_t i_pos;
114     uint32_t i_length;
115
116 } avi_idx1_entry_t;
117
118 typedef struct avi_idx1_s
119 {
120     unsigned int i_entry_count;
121     unsigned int i_entry_max;
122
123     avi_idx1_entry_t *entry;
124 } avi_idx1_t;
125
126 struct sout_mux_sys_t
127 {
128     bool b_write_header;
129
130     int i_streams;
131     int i_stream_video;
132
133     off_t i_movi_size;
134     avi_stream_t stream[100];
135
136     avi_idx1_t idx1;
137     off_t i_idx1_size;
138
139 };
140
141 #define HDR_BASE_SIZE 512 /* single video&audio ~ 400 bytes header */
142
143 /* Flags in avih */
144 #define AVIF_HASINDEX       0x00000010  // Index at end of file?
145 #define AVIF_ISINTERLEAVED  0x00000100
146 #define AVIF_TRUSTCKTYPE    0x00000800  // Use CKType to find key frames?
147
148 /* Flags for index */
149 #define AVIIF_KEYFRAME      0x00000010L /* this frame is a key frame.*/
150
151
152 static block_t *avi_HeaderCreateRIFF( sout_mux_t * );
153 static block_t *avi_HeaderCreateidx1( sout_mux_t * );
154
155 static void SetFCC( uint8_t *p, char *fcc )
156 {
157     memcpy( p, fcc, 4 );
158 }
159
160 /*****************************************************************************
161  * Open:
162  *****************************************************************************/
163 static int Open( vlc_object_t *p_this )
164 {
165     sout_mux_t      *p_mux = (sout_mux_t*)p_this;
166     sout_mux_sys_t  *p_sys;
167
168     msg_Dbg( p_mux, "AVI muxer opened" );
169
170     p_sys = malloc( sizeof( sout_mux_sys_t ) );
171     if( !p_sys )
172         return VLC_ENOMEM;
173     p_sys->i_streams = 0;
174     p_sys->i_stream_video = -1;
175     p_sys->i_movi_size = 0;
176
177     p_sys->idx1.i_entry_count = 0;
178     p_sys->idx1.i_entry_max = 10000;
179     p_sys->idx1.entry = calloc( p_sys->idx1.i_entry_max,
180                                 sizeof( avi_idx1_entry_t ) );
181     if( !p_sys->idx1.entry )
182     {
183         free( p_sys );
184         return VLC_ENOMEM;
185     }
186     p_sys->b_write_header = true;
187
188
189     p_mux->pf_control   = Control;
190     p_mux->pf_addstream = AddStream;
191     p_mux->pf_delstream = DelStream;
192     p_mux->pf_mux       = Mux;
193     p_mux->p_sys        = p_sys;
194
195     return VLC_SUCCESS;
196 }
197
198 /*****************************************************************************
199  * Close:
200  *****************************************************************************/
201 static void Close( vlc_object_t * p_this )
202 {
203     sout_mux_t      *p_mux = (sout_mux_t*)p_this;
204     sout_mux_sys_t  *p_sys = p_mux->p_sys;
205
206     block_t       *p_hdr, *p_idx1;
207     int                 i_stream;
208
209     msg_Dbg( p_mux, "AVI muxer closed" );
210
211     /* first create idx1 chunk (write at the end of the stream */
212     p_idx1 = avi_HeaderCreateidx1( p_mux );
213     if( p_idx1 )
214     {
215         p_sys->i_idx1_size = p_idx1->i_buffer;
216         sout_AccessOutWrite( p_mux->p_access, p_idx1 );
217     }
218     else p_sys->i_idx1_size = 0;
219
220     /* calculate some value for headers creations */
221     for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
222     {
223         avi_stream_t *p_stream;
224
225         p_stream = &p_sys->stream[i_stream];
226
227         p_stream->f_fps = 25;
228         if( p_stream->i_duration > 0 )
229         {
230             p_stream->f_fps = (float)p_stream->i_frames /
231                               ( (float)p_stream->i_duration /
232                                 (float)1000000 );
233         }
234         p_stream->i_bitrate = 128 * 1024;
235         if( p_stream->i_duration > 0 )
236         {
237             p_stream->i_bitrate =
238                 8 * (uint64_t)1000000 *
239                     (uint64_t)p_stream->i_totalsize /
240                     (uint64_t)p_stream->i_duration;
241         }
242         msg_Info( p_mux, "stream[%d] duration:%"PRId64" totalsize:%"PRId64
243                   " frames:%d fps:%f KiB/s:%d",
244                   i_stream,
245                   (int64_t)p_stream->i_duration / (int64_t)1000000,
246                   p_stream->i_totalsize,
247                   p_stream->i_frames,
248                   p_stream->f_fps, p_stream->i_bitrate/1024 );
249     }
250
251     p_hdr = avi_HeaderCreateRIFF( p_mux );
252     if ( p_hdr )
253     {
254         sout_AccessOutSeek( p_mux->p_access, 0 );
255         sout_AccessOutWrite( p_mux->p_access, p_hdr );
256     }
257
258     for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
259     {
260         avi_stream_t *p_stream;
261         p_stream = &p_sys->stream[i_stream];
262         free( p_stream->p_bih );
263         free( p_stream->p_wf );
264     }
265     free( p_sys->idx1.entry );
266     free( p_sys );
267 }
268
269 static int Control( sout_mux_t *p_mux, int i_query, va_list args )
270 {
271     VLC_UNUSED(p_mux);
272     bool *pb_bool;
273     char **ppsz;
274
275    switch( i_query )
276    {
277        case MUX_CAN_ADD_STREAM_WHILE_MUXING:
278            pb_bool = (bool*)va_arg( args, bool * );
279            *pb_bool = false;
280            return VLC_SUCCESS;
281
282        case MUX_GET_ADD_STREAM_WAIT:
283            pb_bool = (bool*)va_arg( args, bool * );
284            *pb_bool = true;
285            return VLC_SUCCESS;
286
287        case MUX_GET_MIME:
288            ppsz = (char**)va_arg( args, char ** );
289            *ppsz = strdup( "video/avi" );
290            return VLC_SUCCESS;
291
292         default:
293             return VLC_EGENERIC;
294    }
295 }
296
297 static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
298 {
299     sout_mux_sys_t  *p_sys = p_mux->p_sys;
300     avi_stream_t    *p_stream;
301
302     if( p_sys->i_streams >= 100 )
303     {
304         msg_Err( p_mux, "too many streams" );
305         return VLC_EGENERIC;
306     }
307
308     msg_Dbg( p_mux, "adding input" );
309     p_input->p_sys = malloc( sizeof( int ) );
310     if( !p_input->p_sys )
311         return VLC_ENOMEM;
312
313     *((int*)p_input->p_sys) = p_sys->i_streams;
314     p_stream = &p_sys->stream[p_sys->i_streams];
315
316     switch( p_input->p_fmt->i_cat )
317     {
318         case AUDIO_ES:
319             p_stream->i_cat = AUDIO_ES;
320             p_stream->fcc[0] = '0' + p_sys->i_streams / 10;
321             p_stream->fcc[1] = '0' + p_sys->i_streams % 10;
322             p_stream->fcc[2] = 'w';
323             p_stream->fcc[3] = 'b';
324
325             p_stream->p_bih = NULL;
326
327             WAVEFORMATEX *p_wf  = malloc( sizeof( WAVEFORMATEX ) +
328                                   p_input->p_fmt->i_extra );
329             if( !p_wf )
330             {
331                 free( p_input->p_sys );
332                 p_input->p_sys = NULL;
333                 return VLC_ENOMEM;
334             }
335
336             p_wf->cbSize = p_input->p_fmt->i_extra;
337             if( p_wf->cbSize > 0 )
338             {
339                 memcpy( &p_wf[1],
340                         p_input->p_fmt->p_extra,
341                         p_input->p_fmt->i_extra );
342             }
343             p_wf->nChannels      = p_input->p_fmt->audio.i_channels;
344             p_wf->nSamplesPerSec = p_input->p_fmt->audio.i_rate;
345             p_wf->nBlockAlign    = p_input->p_fmt->audio.i_blockalign;
346             p_wf->nAvgBytesPerSec= p_input->p_fmt->i_bitrate / 8;
347             p_wf->wBitsPerSample = 0;
348
349             switch( p_input->p_fmt->i_codec )
350             {
351                 case VLC_CODEC_A52:
352                     p_wf->wFormatTag = WAVE_FORMAT_A52;
353                     p_wf->nBlockAlign= 1;
354                     break;
355                 case VLC_CODEC_MP3:
356                     p_wf->wFormatTag = WAVE_FORMAT_MPEGLAYER3;
357                     p_wf->nBlockAlign= 1;
358                     break;
359                 case VLC_CODEC_WMA1:
360                     p_wf->wFormatTag = WAVE_FORMAT_WMA1;
361                     break;
362                 case VLC_CODEC_WMA2:
363                     p_wf->wFormatTag = WAVE_FORMAT_WMA2;
364                     break;
365                 case VLC_CODEC_WMAP:
366                     p_wf->wFormatTag = WAVE_FORMAT_WMAP;
367                     break;
368                 case VLC_CODEC_WMAL:
369                     p_wf->wFormatTag = WAVE_FORMAT_WMAL;
370                     break;
371                     /* raw codec */
372                 case VLC_CODEC_U8:
373                     p_wf->wFormatTag = WAVE_FORMAT_PCM;
374                     p_wf->nBlockAlign= p_wf->nChannels;
375                     p_wf->wBitsPerSample = 8;
376                     p_wf->nAvgBytesPerSec = (p_wf->wBitsPerSample/8) *
377                                       p_wf->nSamplesPerSec * p_wf->nChannels;
378                     break;
379                 case VLC_CODEC_S16L:
380                     p_wf->wFormatTag = WAVE_FORMAT_PCM;
381                     p_wf->nBlockAlign= 2 * p_wf->nChannels;
382                     p_wf->wBitsPerSample = 16;
383                     p_wf->nAvgBytesPerSec = (p_wf->wBitsPerSample/8) *
384                                       p_wf->nSamplesPerSec * p_wf->nChannels;
385                     break;
386                 case VLC_CODEC_S24L:
387                     p_wf->wFormatTag = WAVE_FORMAT_PCM;
388                     p_wf->nBlockAlign= 3 * p_wf->nChannels;
389                     p_wf->wBitsPerSample = 24;
390                     p_wf->nAvgBytesPerSec = (p_wf->wBitsPerSample/8) *
391                                       p_wf->nSamplesPerSec * p_wf->nChannels;
392                     break;
393                 case VLC_CODEC_S32L:
394                     p_wf->wFormatTag = WAVE_FORMAT_PCM;
395                     p_wf->nBlockAlign= 4 * p_wf->nChannels;
396                     p_wf->wBitsPerSample = 32;
397                     p_wf->nAvgBytesPerSec = (p_wf->wBitsPerSample/8) *
398                                       p_wf->nSamplesPerSec * p_wf->nChannels;
399                     break;
400                 default:
401                     free( p_wf );
402                     free( p_input->p_sys );
403                     p_input->p_sys = NULL;
404                     return VLC_EGENERIC;
405             }
406             p_stream->p_wf = p_wf;
407             break;
408         case VIDEO_ES:
409             p_stream->i_cat = VIDEO_ES;
410             p_stream->fcc[0] = '0' + p_sys->i_streams / 10;
411             p_stream->fcc[1] = '0' + p_sys->i_streams % 10;
412             p_stream->fcc[2] = 'd';
413             p_stream->fcc[3] = 'c';
414             if( p_sys->i_stream_video < 0 )
415             {
416                 p_sys->i_stream_video = p_sys->i_streams;
417             }
418             p_stream->p_wf  = NULL;
419             VLC_BITMAPINFOHEADER *p_bih = malloc( sizeof( VLC_BITMAPINFOHEADER ) +
420                                                  p_input->p_fmt->i_extra );
421             if( !p_bih )
422             {
423                 free( p_input->p_sys );
424                 p_input->p_sys = NULL;
425                 return VLC_ENOMEM;
426             }
427
428             p_bih->biSize  = sizeof( VLC_BITMAPINFOHEADER ) +
429                              p_input->p_fmt->i_extra;
430             if( p_input->p_fmt->i_extra > 0 )
431             {
432                 memcpy( &p_bih[1],
433                         p_input->p_fmt->p_extra,
434                         p_input->p_fmt->i_extra );
435             }
436             p_bih->biWidth = p_input->p_fmt->video.i_width;
437             p_bih->biHeight= p_input->p_fmt->video.i_height;
438             p_bih->biPlanes= 1;
439             p_bih->biBitCount       = 24;
440             p_bih->biSizeImage      = 0;
441             p_bih->biXPelsPerMeter  = 0;
442             p_bih->biYPelsPerMeter  = 0;
443             p_bih->biClrUsed        = 0;
444             p_bih->biClrImportant   = 0;
445             switch( p_input->p_fmt->i_codec )
446             {
447                 case VLC_CODEC_MP4V:
448                     p_bih->biCompression = VLC_FOURCC( 'X', 'V', 'I', 'D' );
449                     break;
450                 default:
451                     p_bih->biCompression = p_input->p_fmt->i_original_fourcc ?: p_input->p_fmt->i_codec;
452                     break;
453             }
454             p_stream->p_bih = p_bih;
455             break;
456         default:
457             free( p_input->p_sys );
458             p_input->p_sys = NULL;
459             return( VLC_EGENERIC );
460     }
461     p_stream->i_totalsize = 0;
462     p_stream->i_frames    = 0;
463     p_stream->i_duration  = 0;
464
465     /* fixed later */
466     p_stream->f_fps = 25;
467     p_stream->i_bitrate = 128 * 1024;
468
469     p_sys->i_streams++;
470     return( VLC_SUCCESS );
471 }
472
473 static void DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
474 {
475     msg_Dbg( p_mux, "removing input" );
476
477     free( p_input->p_sys );
478 }
479
480 static int Mux      ( sout_mux_t *p_mux )
481 {
482     sout_mux_sys_t  *p_sys = p_mux->p_sys;
483     avi_stream_t    *p_stream;
484     int i_stream, i;
485
486     if( p_sys->b_write_header )
487     {
488         msg_Dbg( p_mux, "writing header" );
489
490         block_t *p_hdr = avi_HeaderCreateRIFF( p_mux );
491         if ( !p_hdr )
492             return VLC_EGENERIC;
493         sout_AccessOutWrite( p_mux->p_access, p_hdr );
494
495         p_sys->b_write_header = false;
496     }
497
498     for( i = 0; i < p_mux->i_nb_inputs; i++ )
499     {
500         int i_count;
501         block_fifo_t *p_fifo;
502
503         if (!p_mux->pp_inputs[i]->p_sys)
504             continue;
505
506         i_stream = *((int*)p_mux->pp_inputs[i]->p_sys );
507         p_stream = &p_sys->stream[i_stream];
508
509         p_fifo = p_mux->pp_inputs[i]->p_fifo;
510         i_count = block_FifoCount(  p_fifo );
511         while( i_count > 1 )
512         {
513             avi_idx1_entry_t *p_idx;
514             block_t *p_data;
515
516             p_data = block_FifoGet( p_fifo );
517             if( block_FifoCount( p_fifo ) > 0 )
518             {
519                 block_t *p_next = block_FifoShow( p_fifo );
520                 p_data->i_length = p_next->i_dts - p_data->i_dts;
521             }
522
523
524             if( p_stream->i_frames == 0 &&p_stream->i_cat == VIDEO_ES )
525             {
526                /* Add header present at the end of BITMAP info header
527                   to first frame in case of XVID */
528                if( p_stream->p_bih->biCompression
529                                == VLC_FOURCC( 'X', 'V', 'I', 'D' ) )
530                {
531                    int i_header_length =
532                        p_stream->p_bih->biSize - sizeof(VLC_BITMAPINFOHEADER);
533                    p_data = block_Realloc( p_data,
534                                    i_header_length, p_data->i_buffer );
535                    if( !p_data)
536                        return VLC_ENOMEM;
537                    memcpy(p_data->p_buffer,&p_stream->p_bih[1], i_header_length);
538                }
539             }
540
541             p_stream->i_frames++;
542             if( p_data->i_length < 0 )
543             {
544                 msg_Warn( p_mux, "argg length < 0 l" );
545                 block_Release( p_data );
546                 i_count--;
547                 continue;
548             }
549             p_stream->i_duration  += p_data->i_length;
550             p_stream->i_totalsize += p_data->i_buffer;
551
552             /* add idx1 entry for this frame */
553             p_idx = &p_sys->idx1.entry[p_sys->idx1.i_entry_count];
554             memcpy( p_idx->fcc, p_stream->fcc, 4 );
555             p_idx->i_flags = 0;
556             if( ( p_data->i_flags & BLOCK_FLAG_TYPE_MASK ) == 0 || ( p_data->i_flags & BLOCK_FLAG_TYPE_I ) )
557                 p_idx->i_flags = AVIIF_KEYFRAME;
558             p_idx->i_pos   = p_sys->i_movi_size + 4;
559             p_idx->i_length= p_data->i_buffer;
560             p_sys->idx1.i_entry_count++;
561             if( p_sys->idx1.i_entry_count >= p_sys->idx1.i_entry_max )
562             {
563                 p_sys->idx1.i_entry_max += 10000;
564                 p_sys->idx1.entry = xrealloc( p_sys->idx1.entry,
565                        p_sys->idx1.i_entry_max * sizeof( avi_idx1_entry_t ) );
566             }
567
568             p_data = block_Realloc( p_data, 8, p_data->i_buffer );
569             if( p_data )
570             {
571                 SetFCC( p_data->p_buffer, p_stream->fcc );
572                 SetDWLE( p_data->p_buffer + 4, p_data->i_buffer - 8 );
573
574                 if( p_data->i_buffer & 0x01 )
575                 {
576                     p_data = block_Realloc( p_data, 0, p_data->i_buffer + 1 );
577                     if ( p_data )
578                         p_data->p_buffer[ p_data->i_buffer - 1 ] = '\0';
579                 }
580
581                 if ( p_data )
582                 {
583                     p_sys->i_movi_size += p_data->i_buffer;
584                     sout_AccessOutWrite( p_mux->p_access, p_data );
585                 }
586             }
587
588             i_count--;
589         }
590
591     }
592     return( 0 );
593 }
594
595 /****************************************************************************
596  ****************************************************************************
597  **
598  ** avi header generation
599  **
600  ****************************************************************************
601  ****************************************************************************/
602 #define AVI_BOX_ENTER( fcc ) \
603     int i_datasize_offset; \
604     bo_add_fourcc( p_bo, fcc ); \
605     i_datasize_offset = p_bo->b->i_buffer; \
606     bo_add_32le( p_bo, 0 )
607
608 #define AVI_BOX_ENTER_LIST( fcc ) \
609     AVI_BOX_ENTER( "LIST" ); \
610     bo_add_fourcc( p_bo, fcc )
611
612 #define AVI_BOX_EXIT( i_err ) \
613     if( p_bo->b->i_buffer&0x01 ) bo_add_8( p_bo, 0 ); \
614     bo_set_32le( p_bo, i_datasize_offset, p_bo->b->i_buffer - i_datasize_offset - 4 ); \
615     return( i_err );
616
617 static int avi_HeaderAdd_avih( sout_mux_t *p_mux,
618                                bo_t *p_bo )
619 {
620     sout_mux_sys_t  *p_sys = p_mux->p_sys;
621     avi_stream_t    *p_video = NULL;
622     int         i_stream;
623     uint32_t    i_microsecperframe;
624     int         i_maxbytespersec;
625     int         i_totalframes;
626     AVI_BOX_ENTER( "avih" );
627
628     if( p_sys->i_stream_video >= 0 )
629     {
630         p_video = &p_sys->stream[p_sys->i_stream_video];
631         if( p_video->i_frames <= 0 )
632         {
633         //    p_video = NULL;
634         }
635     }
636
637     if( p_video )
638     {
639         i_microsecperframe =
640             (uint32_t)( (float)1000000 /
641                         (float)p_sys->stream[p_sys->i_stream_video].f_fps );
642         i_totalframes = p_sys->stream[p_sys->i_stream_video].i_frames;
643     }
644     else
645     {
646         msg_Warn( p_mux, "avi file without video track isn't a good idea..." );
647         i_microsecperframe = 0;
648         i_totalframes = 0;
649     }
650
651     for( i_stream = 0,i_maxbytespersec = 0; i_stream < p_sys->i_streams; i_stream++ )
652     {
653         if( p_sys->stream[i_stream].i_duration > 0 )
654         {
655             i_maxbytespersec +=
656                 p_sys->stream[i_stream].i_totalsize /
657                 p_sys->stream[i_stream].i_duration;
658         }
659     }
660
661     bo_add_32le( p_bo, i_microsecperframe );
662     bo_add_32le( p_bo, i_maxbytespersec );
663     bo_add_32le( p_bo, 0 );                   /* padding */
664     bo_add_32le( p_bo, AVIF_TRUSTCKTYPE |
665                        AVIF_HASINDEX |
666                        AVIF_ISINTERLEAVED );  /* flags */
667     bo_add_32le( p_bo, i_totalframes );
668     bo_add_32le( p_bo, 0 );                   /* initial frame */
669     bo_add_32le( p_bo, p_sys->i_streams );    /* streams count */
670     bo_add_32le( p_bo, 1024 * 1024 );         /* suggested buffer size */
671     if( p_video )
672     {
673         bo_add_32le( p_bo, p_video->p_bih->biWidth );
674         bo_add_32le( p_bo, p_video->p_bih->biHeight );
675     }
676     else
677     {
678         bo_add_32le( p_bo, 0 );
679         bo_add_32le( p_bo, 0 );
680     }
681     bo_add_32le( p_bo, 0 );                   /* ???? */
682     bo_add_32le( p_bo, 0 );                   /* ???? */
683     bo_add_32le( p_bo, 0 );                   /* ???? */
684     bo_add_32le( p_bo, 0 );                   /* ???? */
685
686     AVI_BOX_EXIT( 0 );
687 }
688 static int avi_HeaderAdd_strh( bo_t *p_bo, avi_stream_t *p_stream )
689 {
690     AVI_BOX_ENTER( "strh" );
691
692     switch( p_stream->i_cat )
693     {
694         case VIDEO_ES:
695             {
696                 bo_add_fourcc( p_bo, "vids" );
697                 bo_add_32be( p_bo, p_stream->p_bih->biCompression );
698                 bo_add_32le( p_bo, 0 );   /* flags */
699                 bo_add_16le(  p_bo, 0 );   /* priority */
700                 bo_add_16le(  p_bo, 0 );   /* langage */
701                 bo_add_32le( p_bo, 0 );   /* initial frame */
702                 bo_add_32le( p_bo, 1000 );/* scale */
703                 bo_add_32le( p_bo, (uint32_t)( 1000 * p_stream->f_fps ));
704                 bo_add_32le( p_bo, 0 );   /* start */
705                 bo_add_32le( p_bo, p_stream->i_frames );
706                 bo_add_32le( p_bo, 1024 * 1024 );
707                 bo_add_32le( p_bo, -1 );  /* quality */
708                 bo_add_32le( p_bo, 0 );   /* samplesize */
709                 bo_add_16le(  p_bo, 0 );   /* ??? */
710                 bo_add_16le(  p_bo, 0 );   /* ??? */
711                 bo_add_16le(  p_bo, p_stream->p_bih->biWidth );
712                 bo_add_16le(  p_bo, p_stream->p_bih->biHeight );
713             }
714             break;
715         case AUDIO_ES:
716             {
717                 int i_rate, i_scale, i_samplesize;
718
719                 i_samplesize = p_stream->p_wf->nBlockAlign;
720                 if( i_samplesize > 1 )
721                 {
722                     i_scale = i_samplesize;
723                     i_rate = /*i_scale **/ p_stream->i_bitrate / 8;
724                 }
725                 else
726                 {
727                     i_samplesize = 1;
728                     i_scale = 1000;
729                     i_rate = 1000 * p_stream->i_bitrate / 8;
730                 }
731                 bo_add_fourcc( p_bo, "auds" );
732                 bo_add_32le( p_bo, 0 );   /* tag */
733                 bo_add_32le( p_bo, 0 );   /* flags */
734                 bo_add_16le(  p_bo, 0 );   /* priority */
735                 bo_add_16le(  p_bo, 0 );   /* langage */
736                 bo_add_32le( p_bo, 0 );   /* initial frame */
737                 bo_add_32le( p_bo, i_scale );/* scale */
738                 bo_add_32le( p_bo, i_rate );
739                 bo_add_32le( p_bo, 0 );   /* start */
740                 bo_add_32le( p_bo, p_stream->i_frames );
741                 bo_add_32le( p_bo, 10 * 1024 );
742                 bo_add_32le( p_bo, -1 );  /* quality */
743                 bo_add_32le( p_bo, i_samplesize );
744                 bo_add_16le(  p_bo, 0 );   /* ??? */
745                 bo_add_16le(  p_bo, 0 );   /* ??? */
746                 bo_add_16le(  p_bo, 0 );
747                 bo_add_16le(  p_bo, 0 );
748             }
749             break;
750     }
751
752     AVI_BOX_EXIT( 0 );
753 }
754
755 static int avi_HeaderAdd_strf( bo_t *p_bo, avi_stream_t *p_stream )
756 {
757     AVI_BOX_ENTER( "strf" );
758
759     switch( p_stream->i_cat )
760     {
761         case AUDIO_ES:
762             bo_add_16le( p_bo, p_stream->p_wf->wFormatTag );
763             bo_add_16le( p_bo, p_stream->p_wf->nChannels );
764             bo_add_32le( p_bo, p_stream->p_wf->nSamplesPerSec );
765             bo_add_32le( p_bo, p_stream->p_wf->nAvgBytesPerSec );
766             bo_add_16le( p_bo, p_stream->p_wf->nBlockAlign );
767             bo_add_16le( p_bo, p_stream->p_wf->wBitsPerSample );
768             bo_add_16le( p_bo, p_stream->p_wf->cbSize );
769             bo_add_mem( p_bo, p_stream->p_wf->cbSize, (uint8_t*)&p_stream->p_wf[1] );
770             break;
771         case VIDEO_ES:
772             bo_add_32le( p_bo, p_stream->p_bih->biSize );
773             bo_add_32le( p_bo, p_stream->p_bih->biWidth );
774             bo_add_32le( p_bo, p_stream->p_bih->biHeight );
775             bo_add_16le( p_bo, p_stream->p_bih->biPlanes );
776             bo_add_16le( p_bo, p_stream->p_bih->biBitCount );
777             if( VLC_FOURCC( 0, 0, 0, 1 ) == 0x00000001 )
778             {
779                 bo_add_32be( p_bo, p_stream->p_bih->biCompression );
780             }
781             else
782             {
783                 bo_add_32le( p_bo, p_stream->p_bih->biCompression );
784             }
785             bo_add_32le( p_bo, p_stream->p_bih->biSizeImage );
786             bo_add_32le( p_bo, p_stream->p_bih->biXPelsPerMeter );
787             bo_add_32le( p_bo, p_stream->p_bih->biYPelsPerMeter );
788             bo_add_32le( p_bo, p_stream->p_bih->biClrUsed );
789             bo_add_32le( p_bo, p_stream->p_bih->biClrImportant );
790             bo_add_mem( p_bo,
791                         p_stream->p_bih->biSize - sizeof( VLC_BITMAPINFOHEADER ),
792                         (uint8_t*)&p_stream->p_bih[1] );
793             break;
794     }
795
796     AVI_BOX_EXIT( 0 );
797 }
798
799 static int avi_HeaderAdd_strl( bo_t *p_bo, avi_stream_t *p_stream )
800 {
801     AVI_BOX_ENTER_LIST( "strl" );
802
803     avi_HeaderAdd_strh( p_bo, p_stream );
804     avi_HeaderAdd_strf( p_bo, p_stream );
805
806     AVI_BOX_EXIT( 0 );
807 }
808
809 static int avi_HeaderAdd_meta( bo_t *p_bo, const char psz_meta[4],
810                                const char *psz_data )
811 {
812     if ( psz_data == NULL ) return 1;
813     const char *psz = psz_data;
814     AVI_BOX_ENTER( psz_meta );
815     while (*psz) bo_add_8( p_bo, *psz++ );
816     bo_add_8( p_bo, 0 );
817     AVI_BOX_EXIT( 0 );
818 }
819
820 static int avi_HeaderAdd_INFO( sout_mux_t *p_mux, bo_t *p_bo )
821 {
822     char *psz;
823
824 #define APPLY_META(var, fourcc) \
825     psz = var_InheritString( p_mux, SOUT_CFG_PREFIX var );\
826     if ( psz )\
827     {\
828         avi_HeaderAdd_meta( p_bo, fourcc, psz );\
829         free( psz );\
830     }
831
832     AVI_BOX_ENTER_LIST( "INFO" );
833
834     APPLY_META( "artist",   "IART")
835     APPLY_META( "comment",  "ICMT")
836     APPLY_META( "copyright","ICOP")
837     APPLY_META( "date",     "ICRD")
838     APPLY_META( "genre",    "IGNR")
839     APPLY_META( "name",     "INAM")
840     APPLY_META( "keywords", "IKEY")
841     APPLY_META( "subject",  "ISBJ")
842     APPLY_META( "encoder",  "ISFT")
843     /* Some are missing, but are they really useful ?? */
844
845 #undef APPLY_META
846
847     AVI_BOX_EXIT( 0 );
848 }
849
850 static block_t *avi_HeaderCreateRIFF( sout_mux_t *p_mux )
851 {
852     sout_mux_sys_t      *p_sys = p_mux->p_sys;
853     int                 i_stream;
854     int                 i_junk;
855     bo_t                bo;
856
857     struct
858     {
859         int i_riffsize;
860         int i_hdrllistsize;
861         int i_hdrldatastart;
862     } offsets;
863
864     if (! bo_init( &bo, HDR_BASE_SIZE ) )
865         return NULL;
866
867     bo_add_fourcc( &bo, "RIFF" );
868     offsets.i_riffsize = bo.b->i_buffer;
869     bo_add_32le( &bo, 0xEFBEADDE );
870     bo_add_fourcc( &bo, "AVI " );
871
872     bo_add_fourcc( &bo, "LIST" );
873     /* HDRL List size should exclude following data in HDR buffer
874      *  -12 (RIFF, RIFF size, 'AVI ' tag),
875      *  - 8 (hdr1 LIST tag and its size)
876      *  - 12 (movi LIST tag, size, 'movi' listType )
877      */
878     offsets.i_hdrllistsize = bo.b->i_buffer;
879     bo_add_32le( &bo, 0xEFBEADDE );
880     bo_add_fourcc( &bo, "hdrl" );
881     offsets.i_hdrldatastart = bo.b->i_buffer;
882
883     avi_HeaderAdd_avih( p_mux, &bo );
884     for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
885     {
886         avi_HeaderAdd_strl( &bo, &p_sys->stream[i_stream] );
887     }
888
889     /* align on 16 bytes */
890     int i_align = ( ( bo.b->i_buffer + 12 + 0xE ) & ~ 0xF );
891     i_junk = i_align - bo.b->i_buffer;
892     bo_add_fourcc( &bo, "JUNK" );
893     bo_add_32le( &bo, i_junk );
894     for( int i=0; i< i_junk; i++ )
895     {
896         bo_add_8( &bo, 0 );
897     }
898
899     /* Now set hdrl size */
900     bo_set_32le( &bo, offsets.i_hdrllistsize,
901                  bo.b->i_buffer - offsets.i_hdrldatastart );
902
903     avi_HeaderAdd_INFO( p_mux, &bo );
904
905     bo_add_fourcc( &bo, "LIST" );
906     bo_add_32le( &bo, p_sys->i_movi_size + 4 );
907     bo_add_fourcc( &bo, "movi" );
908
909     /* Now set RIFF size */
910     bo_set_32le( &bo, offsets.i_riffsize, bo.b->i_buffer - 8
911                  + p_sys->i_movi_size + p_sys->i_idx1_size );
912
913     return( bo.b );
914 }
915
916 static block_t * avi_HeaderCreateidx1( sout_mux_t *p_mux )
917 {
918     sout_mux_sys_t      *p_sys = p_mux->p_sys;
919     uint32_t            i_idx1_size;
920     bo_t                bo;
921
922     i_idx1_size = 16 * p_sys->idx1.i_entry_count + 8;
923
924     if (!i_idx1_size || !bo_init( &bo, i_idx1_size ) )
925         return NULL;
926     memset( bo.b->p_buffer, 0, i_idx1_size);
927
928     bo_add_fourcc( &bo, "idx1" );
929     bo_add_32le( &bo, i_idx1_size - 8);
930
931     for( unsigned i = 0; i < p_sys->idx1.i_entry_count; i++ )
932     {
933         bo_add_fourcc( &bo, p_sys->idx1.entry[i].fcc );
934         bo_add_32le( &bo, p_sys->idx1.entry[i].i_flags );
935         bo_add_32le( &bo, p_sys->idx1.entry[i].i_pos );
936         bo_add_32le( &bo, p_sys->idx1.entry[i].i_length );
937     }
938
939     return( bo.b );
940 }