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