1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2001, 2002 VideoLAN
5 * $Id: avi.c,v 1.2 2003/01/13 02:33:13 fenrir Exp $
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
28 #include <sys/types.h>
35 #include <vlc/input.h>
40 #elif defined( _MSC_VER ) && defined( _WIN32 ) && !defined( UNDER_CE )
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
54 /*****************************************************************************
56 *****************************************************************************/
57 static int Open ( vlc_object_t * );
58 static void Close ( vlc_object_t * );
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 * );
64 static sout_buffer_t *avi_HeaderCreateRIFF( sout_instance_t *p_sout );
66 static void SetFCC( uint8_t *p, char *fcc )
74 static void SetDWLE( uint8_t *p, uint32_t i_dw )
76 p[3] = ( i_dw >> 24 )&0xff;
77 p[2] = ( i_dw >> 16 )&0xff;
78 p[1] = ( i_dw >> 8 )&0xff;
82 /*****************************************************************************
84 *****************************************************************************/
86 set_description( _("Avi muxer") );
87 set_capability( "sout mux", 5 );
88 add_shortcut( "avi" );
89 set_callbacks( Open, Close );
93 #define HDR_SIZE 10240
95 typedef struct avi_stream_s
101 mtime_t i_duration; // in µs
103 int i_frames; // total frame count
104 int64_t i_totalsize; // total stream size
109 BITMAPINFOHEADER *p_bih;
114 typedef struct sout_mux_s
120 avi_stream_t stream[100];
124 /*****************************************************************************
126 *****************************************************************************/
127 static int Open( vlc_object_t *p_this )
129 sout_instance_t *p_sout = (sout_instance_t*)p_this;
131 sout_buffer_t *p_hdr;
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;
138 msg_Info( p_sout, "Open" );
140 p_sout->pf_mux_addstream = AddStream;
141 p_sout->pf_mux_delstream = DelStream;
142 p_sout->pf_mux = Mux;
143 p_sout->p_mux_data = (void*)p_mux;
144 p_sout->i_mux_preheader = 8; // (fourcc,length) header
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 );
154 /*****************************************************************************
156 *****************************************************************************/
158 static void Close( vlc_object_t * p_this )
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;
165 msg_Info( p_sout, "Close" );
167 for( i_stream = 0; i_stream < p_mux->i_streams; i_stream++ )
169 avi_stream_t *p_stream;
171 p_stream = &p_mux->stream[i_stream];
173 p_stream->f_fps = 25;
174 if( p_stream->i_duration > 0 )
176 p_stream->f_fps = (float)p_stream->i_frames /
177 ( (float)p_stream->i_duration /
180 p_stream->i_bitrate = 128 * 1024;
181 if( p_stream->i_duration > 0 )
183 p_stream->i_bitrate =
184 8 * (uint64_t)1000000 *
185 (uint64_t)p_stream->i_totalsize /
186 (uint64_t)p_stream->i_duration;
188 msg_Err( p_sout,"stream[%d] duration:%lld totalsize:%lld frames:%d fps:%f kb/s:%d",
190 p_stream->i_duration/1000000, p_stream->i_totalsize,
192 p_stream->f_fps, p_stream->i_bitrate/1024 );
195 p_hdr = avi_HeaderCreateRIFF( p_sout );
196 p_sout->pf_seek( p_sout, 0 );
197 p_sout->pf_write( p_sout, p_hdr );
201 static int AddStream( sout_instance_t *p_sout, sout_input_t *p_input )
203 sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
204 avi_stream_t *p_stream;
206 if( p_mux->i_streams >= 100 )
208 msg_Err( p_sout, "too many streams" );
211 if( p_input->input_format.p_format == NULL )
213 msg_Err( p_sout, "stream descriptor missing" );
217 msg_Dbg( p_sout, "adding input" );
218 p_input->p_mux_data = malloc( sizeof( int ) );
220 *((int*)p_input->p_mux_data) = p_mux->i_streams;
221 p_stream = &p_mux->stream[p_mux->i_streams];
223 switch( p_input->input_format.i_cat )
228 (WAVEFORMATEX*)p_input->input_format.p_format;
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';
236 p_stream->p_bih = NULL;
237 p_stream->p_wf = malloc( sizeof( WAVEFORMATEX ) + p_wf->cbSize );
238 memcpy( p_stream->p_wf,
240 sizeof( WAVEFORMATEX ) + p_wf->cbSize);
245 BITMAPINFOHEADER *p_bih =
246 (BITMAPINFOHEADER*)p_input->input_format.p_format;;
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 )
255 p_mux->i_stream_video = p_mux->i_streams;
257 p_stream->p_wf = NULL;
258 p_stream->p_bih = malloc( p_bih->biSize );
259 memcpy( p_stream->p_bih,
267 p_stream->i_totalsize = 0;
268 p_stream->i_frames = 0;
269 p_stream->i_duration = 0;
275 static int DelStream( sout_instance_t *p_sout, sout_input_t *p_input )
277 // sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
279 msg_Dbg( p_sout, "removing input" );
281 free( p_input->p_mux_data ); p_input->p_mux_data = NULL;
286 static int Mux ( sout_instance_t *p_sout )
288 sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
289 avi_stream_t *p_stream;
293 for( i = 0; i < p_sout->i_nb_inputs; i++ )
298 i_stream = *((int*)p_sout->pp_inputs[i]->p_mux_data );
299 p_stream = &p_mux->stream[i_stream];
301 p_fifo = p_sout->pp_inputs[i]->p_fifo;
302 i_count = p_fifo->i_depth;
305 sout_buffer_t *p_data;
307 p_data = sout_FifoGet( p_fifo );
309 p_stream->i_frames++;
310 p_stream->i_duration += p_data->i_length;
311 p_stream->i_totalsize += p_data->i_size;
313 if( sout_BufferReallocFromPreHeader( p_sout, p_data, 8 ) )
315 /* there isn't enough data in preheader */
316 sout_buffer_t *p_hdr;
318 p_hdr = sout_BufferNew( p_sout, 8 );
319 SetFCC( p_hdr->p_buffer, p_stream->fcc );
320 SetDWLE( p_hdr->p_buffer + 4, p_data->i_size );
322 p_sout->pf_write( p_sout, p_hdr );
323 p_mux->i_movi_size += p_hdr->i_size;
328 SetFCC( p_data->p_buffer, p_stream->fcc );
329 SetDWLE( p_data->p_buffer + 4, p_data->i_size - 8 );
332 if( p_data->i_size & 0x01 )
334 sout_BufferRealloc( p_sout, p_data, p_data->i_size + 1 );
338 p_sout->pf_write( p_sout, p_data );
339 p_mux->i_movi_size += p_data->i_size;
348 /****************************************************************************/
349 /****************************************************************************/
350 /****************************************************************************/
351 /****************************************************************************/
353 typedef struct buffer_out_s
361 static void bo_Init( buffer_out_t *p_bo, int i_size, uint8_t *p_buffer )
363 p_bo->i_buffer_size = i_size;
365 p_bo->p_buffer = p_buffer;
367 static void bo_AddByte( buffer_out_t *p_bo, uint8_t i )
369 if( p_bo->i_buffer < p_bo->i_buffer_size )
371 p_bo->p_buffer[p_bo->i_buffer] = i;
375 static void bo_AddWordLE( buffer_out_t *p_bo, uint16_t i )
377 bo_AddByte( p_bo, i &0xff );
378 bo_AddByte( p_bo, ( ( i >> 8) &0xff ) );
380 static void bo_AddWordBE( buffer_out_t *p_bo, uint16_t i )
382 bo_AddByte( p_bo, ( ( i >> 8) &0xff ) );
383 bo_AddByte( p_bo, i &0xff );
385 static void bo_AddDWordLE( buffer_out_t *p_bo, uint32_t i )
387 bo_AddWordLE( p_bo, i &0xffff );
388 bo_AddWordLE( p_bo, ( ( i >> 16) &0xffff ) );
390 static void bo_AddDWordBE( buffer_out_t *p_bo, uint32_t i )
392 bo_AddWordLE( p_bo, ( ( i >> 16) &0xffff ) );
393 bo_AddWordLE( p_bo, i &0xffff );
396 static void bo_AddLWordLE( buffer_out_t *p_bo, uint64_t i )
398 bo_AddDWordLE( p_bo, i &0xffffffff );
399 bo_AddDWordLE( p_bo, ( ( i >> 32) &0xffffffff ) );
401 static void bo_AddLWordBE( buffer_out_t *p_bo, uint64_t i )
403 bo_AddDWordBE( p_bo, ( ( i >> 32) &0xffffffff ) );
404 bo_AddDWordBE( p_bo, i &0xffffffff );
408 static void bo_AddFCC( buffer_out_t *p_bo, char *fcc )
410 bo_AddByte( p_bo, fcc[0] );
411 bo_AddByte( p_bo, fcc[1] );
412 bo_AddByte( p_bo, fcc[2] );
413 bo_AddByte( p_bo, fcc[3] );
416 static void bo_AddMem( buffer_out_t *p_bo, int i_size, uint8_t *p_mem )
420 for( i = 0; i < i_size; i++ )
422 bo_AddByte( p_bo, p_mem[i] );
426 /****************************************************************************
427 ****************************************************************************
429 ** avi header generation
431 ****************************************************************************
432 ****************************************************************************/
433 #define AVI_BOX_ENTER( fcc ) \
434 buffer_out_t _bo_sav_; \
435 bo_AddFCC( p_bo, fcc ); \
437 bo_AddDWordLE( p_bo, 0 )
439 #define AVI_BOX_ENTER_LIST( fcc ) \
440 AVI_BOX_ENTER( "LIST" ); \
441 bo_AddFCC( p_bo, fcc )
443 #define AVI_BOX_EXIT( i_err ) \
444 if( p_bo->i_buffer&0x01 ) bo_AddByte( p_bo, 0 ); \
445 bo_AddDWordLE( &_bo_sav_, p_bo->i_buffer - _bo_sav_.i_buffer - 4 ); \
448 static int avi_HeaderAdd_avih( sout_instance_t *p_sout,
451 sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
452 avi_stream_t *p_video = NULL;
454 uint32_t i_microsecperframe;
455 int i_maxbytespersec;
457 AVI_BOX_ENTER( "avih" );
459 if( p_mux->i_stream_video >= 0 )
461 p_video = &p_mux->stream[p_mux->i_stream_video];
462 if( p_video->i_frames <= 0 )
471 (uint32_t)( (float)1000000 /
472 (float)p_mux->stream[p_mux->i_stream_video].f_fps );
473 i_totalframes = p_mux->stream[p_mux->i_stream_video].i_frames;
477 msg_Warn( p_sout, "avi file without audio video track isn't a good idea..." );
478 i_microsecperframe = 0;
482 for( i_stream = 0,i_maxbytespersec = 0; i_stream < p_mux->i_streams; i_stream++ )
484 if( p_mux->stream[p_mux->i_stream_video].i_duration > 0 )
487 p_mux->stream[p_mux->i_stream_video].i_totalsize /
488 p_mux->stream[p_mux->i_stream_video].i_duration;
492 bo_AddDWordLE( p_bo, i_microsecperframe );
493 bo_AddDWordLE( p_bo, i_maxbytespersec );
494 bo_AddDWordLE( p_bo, 0 ); /* padding */
495 bo_AddDWordLE( p_bo, AVIF_TRUSTCKTYPE |
497 AVIF_ISINTERLEAVED ); /* flags */
498 bo_AddDWordLE( p_bo, i_totalframes );
499 bo_AddDWordLE( p_bo, 0 ); /* initial frame */
500 bo_AddDWordLE( p_bo, p_mux->i_streams ); /* streams count */
501 bo_AddDWordLE( p_bo, 1024 * 1024 ); /* suggested buffer size */
504 bo_AddDWordLE( p_bo, p_video->p_bih->biWidth );
505 bo_AddDWordLE( p_bo, p_video->p_bih->biHeight );
509 bo_AddDWordLE( p_bo, 0 );
510 bo_AddDWordLE( p_bo, 0 );
512 bo_AddDWordLE( p_bo, 0 ); /* ???? */
513 bo_AddDWordLE( p_bo, 0 ); /* ???? */
514 bo_AddDWordLE( p_bo, 0 ); /* ???? */
515 bo_AddDWordLE( p_bo, 0 ); /* ???? */
519 static int avi_HeaderAdd_strh( sout_instance_t *p_sout,
521 avi_stream_t *p_stream )
523 // sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
524 AVI_BOX_ENTER( "strh" );
526 switch( p_stream->i_cat )
530 bo_AddFCC( p_bo, "vids" );
531 bo_AddDWordBE( p_bo, p_stream->p_bih->biCompression );
532 bo_AddDWordLE( p_bo, 0 ); /* flags */
533 bo_AddWordLE( p_bo, 0 ); /* priority */
534 bo_AddWordLE( p_bo, 0 ); /* langage */
535 bo_AddDWordLE( p_bo, 0 ); /* initial frame */
536 bo_AddDWordLE( p_bo, 1000 );/* scale */
537 bo_AddDWordLE( p_bo, (uint32_t)( 1000 * p_stream->f_fps ));
538 bo_AddDWordLE( p_bo, 0 ); /* start */
539 bo_AddDWordLE( p_bo, p_stream->i_frames );
540 bo_AddDWordLE( p_bo, 1024 * 1024 );
541 bo_AddDWordLE( p_bo, -1 ); /* quality */
542 bo_AddDWordLE( p_bo, 0 ); /* samplesize */
543 bo_AddWordLE( p_bo, 0 ); /* ??? */
544 bo_AddWordLE( p_bo, 0 ); /* ??? */
545 bo_AddWordLE( p_bo, p_stream->p_bih->biWidth );
546 bo_AddWordLE( p_bo, p_stream->p_bih->biHeight );
551 int i_rate, i_scale, i_samplesize;
553 i_samplesize = p_stream->p_wf->nBlockAlign;
554 if( i_samplesize > 1 )
556 i_scale = i_samplesize;
557 i_rate = i_scale * p_stream->i_bitrate / 8;
563 i_rate = 1000 * p_stream->i_bitrate / 8;
565 bo_AddFCC( p_bo, "auds" );
566 bo_AddDWordLE( p_bo, 1 ); /* tag */
567 bo_AddDWordLE( p_bo, 0 ); /* flags */
568 bo_AddWordLE( p_bo, 0 ); /* priority */
569 bo_AddWordLE( p_bo, 0 ); /* langage */
570 bo_AddDWordLE( p_bo, 0 ); /* initial frame */
571 bo_AddDWordLE( p_bo, i_scale );/* scale */
572 bo_AddDWordLE( p_bo, i_rate );
573 bo_AddDWordLE( p_bo, 0 ); /* start */
574 bo_AddDWordLE( p_bo, p_stream->i_frames );
575 bo_AddDWordLE( p_bo, 10 * 1024 );
576 bo_AddDWordLE( p_bo, -1 ); /* quality */
577 bo_AddDWordLE( p_bo, i_samplesize );
578 bo_AddWordLE( p_bo, 0 ); /* ??? */
579 bo_AddWordLE( p_bo, 0 ); /* ??? */
580 bo_AddWordLE( p_bo, 0 );
581 bo_AddWordLE( p_bo, 0 );
589 static int avi_HeaderAdd_strf( sout_instance_t *p_sout,
591 avi_stream_t *p_stream )
593 // sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
594 AVI_BOX_ENTER( "strf" );
596 switch( p_stream->i_cat )
599 bo_AddWordLE( p_bo, p_stream->p_wf->wFormatTag );
600 bo_AddWordLE( p_bo, p_stream->p_wf->nChannels );
601 bo_AddDWordLE( p_bo, p_stream->p_wf->nSamplesPerSec );
602 bo_AddDWordLE( p_bo, p_stream->p_wf->nAvgBytesPerSec );
603 bo_AddWordLE( p_bo, p_stream->p_wf->nBlockAlign );
604 bo_AddWordLE( p_bo, p_stream->p_wf->wBitsPerSample );
605 bo_AddWordLE( p_bo, p_stream->p_wf->cbSize );
606 bo_AddMem( p_bo, p_stream->p_wf->cbSize, (uint8_t*)&p_stream->p_wf[1] );
609 bo_AddDWordLE( p_bo, p_stream->p_bih->biSize );
610 bo_AddDWordLE( p_bo, p_stream->p_bih->biWidth );
611 bo_AddDWordLE( p_bo, p_stream->p_bih->biHeight );
612 bo_AddWordLE( p_bo, p_stream->p_bih->biPlanes );
613 bo_AddWordLE( p_bo, p_stream->p_bih->biBitCount );
614 bo_AddDWordLE( p_bo, p_stream->p_bih->biCompression );
615 bo_AddDWordLE( p_bo, p_stream->p_bih->biSizeImage );
616 bo_AddDWordLE( p_bo, p_stream->p_bih->biXPelsPerMeter );
617 bo_AddDWordLE( p_bo, p_stream->p_bih->biYPelsPerMeter );
618 bo_AddDWordLE( p_bo, p_stream->p_bih->biClrUsed );
619 bo_AddDWordLE( p_bo, p_stream->p_bih->biClrImportant );
621 p_stream->p_bih->biSize - sizeof( BITMAPINFOHEADER ),
622 (uint8_t*)&p_stream->p_bih[1] );
629 static int avi_HeaderAdd_strl( sout_instance_t *p_sout,
631 avi_stream_t *p_stream )
633 // sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
634 AVI_BOX_ENTER_LIST( "strl" );
636 avi_HeaderAdd_strh( p_sout, p_bo, p_stream );
637 avi_HeaderAdd_strf( p_sout, p_bo, p_stream );
642 static sout_buffer_t *avi_HeaderCreateRIFF( sout_instance_t *p_sout )
644 sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
645 sout_buffer_t *p_hdr;
647 int i_maxbytespersec;
651 p_hdr = sout_BufferNew( p_sout, HDR_SIZE );
652 memset( p_hdr->p_buffer, 0, HDR_SIZE );
654 bo_Init( &bo, HDR_SIZE, p_hdr->p_buffer );
656 bo_AddFCC( &bo, "RIFF" );
657 bo_AddDWordLE( &bo, p_mux->i_movi_size + HDR_SIZE - 8 );
658 bo_AddFCC( &bo, "AVI " );
660 bo_AddFCC( &bo, "LIST" );
661 bo_AddDWordLE( &bo, HDR_SIZE - 8);
662 bo_AddFCC( &bo, "hdrl" );
664 avi_HeaderAdd_avih( p_sout, &bo );
665 for( i_stream = 0,i_maxbytespersec = 0; i_stream < p_mux->i_streams; i_stream++ )
667 avi_HeaderAdd_strl( p_sout, &bo, &p_mux->stream[i_stream] );
670 i_junk = HDR_SIZE - bo.i_buffer - 8 - 12;
671 bo_AddFCC( &bo, "JUNK" );
672 bo_AddDWordLE( &bo, i_junk );
674 bo.i_buffer += i_junk;
675 bo_AddFCC( &bo, "LIST" );
676 bo_AddDWordLE( &bo, p_mux->i_movi_size);
677 bo_AddFCC( &bo, "movi" );