1 /** *************************************************************************
2         \file op_aviwrite.cpp
3         \brief low level avi muxer
4 
5 		etc...
6 
7 LAll
8   LMain
9    LHeader
10    ( in indexer : LMovie)
11 
12 
13 
14     copyright            : (C) 2002 by mean
15                            (C) Feb 2005 by GMV: ODML write support
16     GPL V2.0
17  ***************************************************************************/
18 
19 /***************************************************************************
20  *                                                                         *
21  *   This program is free software; you can redistribute it and/or modify  *
22  *   it under the terms of the GNU General Public License as published by  *
23  *   the Free Software Foundation; either version 2 of the License, or     *
24  *   (at your option) any later version.                                   *
25  *                                                                         *
26  ***************************************************************************/
27 
28 #include "ADM_cpp.h"
29 #include "ADM_default.h"
30 #include <math.h>
31 #include "ADM_muxer.h"
32 
33 #include "fourcc.h"
34 
35 #include "avilist_avi.h"
36 #include "op_aviwrite.hxx"
37 
38 #include "ADM_quota.h"
39 #include "ADM_fileio.h"
40 
41 #include "muxerAvi.h"
42 
43 #if 0
44     #define aprintf printf
45 #else
46     #define aprintf(...) {}
47 #endif
48 
49 #include "aviIndex.h"
50 #include "aviIndexAvi.h"
51 #include "aviIndexOdml.h"
52 #include "ADM_memio.h"
53 /**
54     \class VBRext
55 */
56 class VBRext
57 {
58 public:
59   uint16_t   	     cbsize ;
60   uint16_t          wId ;
61   uint32_t          fdwflags ;
62   uint16_t          nblocksize ;
63   uint16_t          nframesperblock  ;
64   uint16_t          ncodecdelay ;
serialize(uint8_t * to)65   void               serialize(uint8_t *to)
66                      {
67                         ADMMemio io(14);
68                         io.write16(cbsize);
69                         io.write16(wId);
70                         io.write32(fdwflags);
71                         io.write16(nblocksize);
72                         io.write16(nframesperblock);
73                         io.write16(ncodecdelay);
74                         memcpy(to,io.getBuffer(),14);
75                      }
VBRext()76                     VBRext()
77                     {
78                           cbsize = 12;
79                           wId =1;
80                           fdwflags = 2;
81                           nframesperblock = 1;
82                           nblocksize=384;
83                           ncodecdelay = 0;
84                     }
85 };
86 
87 
88 //________________________________________________
89 //   Beginning of the write process
90 //   We fill-in the headers
91 //	3- Write audio headers
92 //   That one can be used several times so we pass stuff
93 //   as parameter
94 //_______________________________________________
95 static 	uint32_t aacBitrate[16]=
96 {
97 	96000, 88200, 64000, 48000,
98 	44100, 32000, 24000, 22050,
99 	16000, 12000, 11025,  8000,
100 	0,     0,     0,     0
101 };
102 //
103 
104 /**
105     \fn aviWrite
106 */
aviWrite(void)107 aviWrite::aviWrite( void )
108 {
109     _out=NULL;
110     _file=NULL;
111     indexMaker=NULL;
112     memset(&(audioTracks),0,sizeof(audioTracks));
113     memset(openDmlHeaderPosition,0,sizeof(openDmlHeaderPosition));
114 
115 }
116 /**
117     \fn ~ aviWrite
118 */
119 
~aviWrite()120 aviWrite::~aviWrite()
121 {
122 
123     if(indexMaker)
124         delete indexMaker;
125 
126     indexMaker=NULL;
127 }
128 
129 /**
130     \fn updateHeader
131     \brief when writing is done, we need to update the headers with what we actually wrote
132 */
updateHeader(MainAVIHeader * mainheader,AVIStreamHeader * videostream)133 uint8_t aviWrite::updateHeader (MainAVIHeader * mainheader,
134 			AVIStreamHeader  *videostream)
135 {
136 
137         ADM_assert(_file);
138         ADM_info("[Avi] Updating headers...\n");
139         _file->seek(32);
140         AviListAvi tmpList("dummy",_file);
141         _mainheader.dwTotalFrames=indexMaker->getNbVideoFrameForHeaders();
142         ADM_info("=>Main header nb frame = %d\n",(int) _mainheader.dwTotalFrames);
143         tmpList.writeMainHeaderStruct(_mainheader);
144         _file->seek(0x6c);
145         _videostream.dwLength=vframe;
146         ADM_info("=>Video stream nb frames = %d\n",(int) vframe);
147         tmpList.writeStreamHeaderStruct(_videostream);
148         for(int i=0;i<nb_audio;i++)
149         {
150             // update header
151             uint32_t size=indexMaker->getSizeInBytesForAudioTrack(i);
152             ADM_info("=>Audio stream %d size %d\n",i,(int)size);
153             _file->seek(audioStreamHeaderPosition[i]);
154             WAVHeader wav;
155             AVIStreamHeader header;
156             int extraLen;
157             uint8_t extra[32];
158             createAudioHeader(&wav,_audioStreams[i],&header,size,i,extra,&extraLen);
159             tmpList.writeStrh(header);
160         }
161         return 1;
162 }
163 
164 /**
165     \fn writeVideoHeader5
166 //   Beginning of the write process
167 //   We fill-in the headers
168 //	2- Write video headers
169 */
writeVideoHeader(uint8_t * extra,uint32_t extraLen)170 uint8_t aviWrite::writeVideoHeader( uint8_t *extra, uint32_t extraLen )
171 {
172 
173       ADM_assert (_file);
174       _videostream.fccType = fourCC::get ((uint8_t *) "vids");
175       _bih.biSize=sizeof(_bih)+extraLen;
176       setVideoStreamInfo(_file,_videostream,_bih,
177                     extra,extraLen, 0x1000);
178 	return 1;
179 }
180 /**
181     \fn createAudioHeader
182 */
createAudioHeader(WAVHeader * wav,ADM_audioStream * stream,AVIStreamHeader * header,uint32_t sizeInBytes,int trackNumber,uint8_t * extra,int * extraLen)183 bool aviWrite::createAudioHeader(WAVHeader *wav,ADM_audioStream *stream, AVIStreamHeader *header,
184                 uint32_t sizeInBytes,int	trackNumber, uint8_t *extra, int *extraLen)
185 {
186 uint8_t wmaheader[16];
187 VBRext   mp3vbr;
188 
189 	if(!stream) return true;
190 
191     memcpy(wav,stream->getInfo(),sizeof(*wav));
192 
193 
194       memset (header, 0, sizeof (AVIStreamHeader));
195       header->fccType = fourCC::get ((uint8_t *) "auds");
196       header->dwInitialFrames = 0;
197       header->dwStart = 0;
198       header->dwRate = wav->byterate;
199       header->dwSampleSize = 1;
200       header->dwQuality = 0xffffffff;
201       header->dwSuggestedBufferSize = 8000;
202       header->dwLength=sizeInBytes;
203 
204       printf("[ODML/Audio] Encoding 0x%x\n",wav->encoding);
205 	switch(wav->encoding)
206 	{
207         case WAV_IMAADPCM:
208                 wav->blockalign=1024;
209                 header->dwScale         = wav->blockalign;
210                 header->dwSampleSize    = 1;
211                 header->dwInitialFrames =1;
212                 header->dwSuggestedBufferSize=2048;
213                 break;
214         case WAV_AAC:
215           {
216             // nb sample in stream
217             // since it is vbr, assume N packet of 1024 samples
218             uint32_t frameLength=stream->getSamplesPerPacket();
219             header->dwFlags=1;
220             header->dwInitialFrames=0;
221             header->dwRate=wav->frequency;
222             header->dwScale=frameLength;
223             header->dwSampleSize = 0;
224             header->dwSuggestedBufferSize=8192;
225             header->dwInitialFrames = 0;
226 
227             wav->blockalign=frameLength;
228             wav->bitspersample = 0;
229 
230             uint32_t aLen;
231             uint8_t  *aData;
232             stream->getExtraData(&aLen,&aData);
233 
234             extra[0]=0x2;
235             extra[1]=0x0;
236             if(2==aLen)
237             {
238                 extra[2]=aData[0];
239                 extra[3]=aData[1];
240             }else
241             {
242                 int SRI=4;	// Default 44.1 khz
243                 if(frameLength==(AAC_DEFAULT_FRAME_LENGTH<<1)) // SBR
244                     wav->frequency>>=1; // make it implicit SBR, using the real extradata doesn't work well
245                 for(int i=0;i<16;i++) if(wav->frequency==aacBitrate[i]) SRI=i;
246                 extra[2]=(2<<3)+(SRI>>1); // Profile LOW
247                 extra[3]=((SRI&1)<<7)+((wav->channels)<<3);
248             }
249             *extraLen=4;
250           }
251           break;
252 
253         case WAV_DTS:
254         case WAV_AC3: // Vista compatibility
255                       extra[0]=0;
256                       extra[1]=0;
257                       *extraLen=2;
258                       header->dwScale = 1;
259                       wav->blockalign=1;
260                 break;
261         case WAV_MP3:
262            {
263           int samplePerFrame=1152; // see http://msdn.microsoft.com/en-us/library/ms787304(VS.85).aspx
264 		  // then update VBR fields
265 
266 		  wav->bitspersample = 0;
267 		  mp3vbr.nblocksize=samplePerFrame; //384; // ??
268 
269 		  header->dwScale = 1;
270 	  	  header->dwInitialFrames = 1;
271 
272 
273           if (!stream->isCBR()) // FIXME stream->isVBR()) //wav->blockalign ==1152)	// VBR audio
274 		  {			// We do like nandub do
275                 ADM_info("[avi] : VBR mp3\n");
276                 //ADM_assert (audiostream->asTimeTrack ());
277                 if(wav->frequency>=32000)  // mpeg1
278                 {
279                     samplePerFrame=1152;
280                 }else                       // Mpeg2 , we assume layer3
281                 {
282                     samplePerFrame=576;
283                 }
284                 wav->blockalign = samplePerFrame;	// just a try
285                 wav->bitspersample = 16;
286                 header->dwRate 	= wav->frequency;	//wav->byterate;
287                 header->dwScale = wav->blockalign;
288                 header->dwLength= _videostream.dwLength;
289                 header->dwSampleSize = 0;
290                 mp3vbr.nblocksize=samplePerFrame;
291 
292 		   }
293 		   else
294            {
295                 ADM_info("[avi] : CBR mp3\n");
296                 wav->blockalign=1;
297            }
298             mp3vbr.serialize(extra);
299             *extraLen=14;
300           }
301 		  break;
302 
303 
304 	case WAV_WMA:
305           {
306             memset(extra,0,12);
307 #if 0
308             extra[16-16]=0x0a;
309             extra[19-16]=0x08;
310             extra[22-16]=0x01;
311             extra[24-16]=0x74;
312             extra[25-16]=01;
313 #endif
314 			header->dwScale 	    = wav->blockalign;
315 			header->dwSampleSize 	= wav->blockalign;
316 			header->dwInitialFrames =1;
317 			header->dwSuggestedBufferSize=10*wav->blockalign;
318 			*extraLen=12;
319             uint32_t aLen;
320             uint8_t *aData;
321             stream->getExtraData(&aLen,&aData);
322             extra[0]=0x0a;
323             memcpy(extra+2,aData,(aLen<10)? aLen : 10);
324           }
325 		  break;
326     case WAV_PCM:
327     case WAV_LPCM:
328             header->dwScale=header->dwSampleSize=wav->blockalign=2*wav->channels; // Realign
329             header->dwLength/=header->dwScale;
330             break;
331     case WAV_8BITS_UNSIGNED:
332             wav->encoding=WAV_PCM;
333 			header->dwScale=header->dwSampleSize=wav->blockalign=wav->channels;
334 			header->dwLength/=header->dwScale;
335             wav->bitspersample=8;
336             break;
337 
338 
339 	default:
340 			header->dwScale = 1;
341 			wav->blockalign=1;
342 			break;
343     }
344     return true;
345 }
346 
347 /**
348         \fn WriteAudioHeader
349         \brief update *header with info taken from stream then write it to file
350 */
writeAudioHeader(ADM_audioStream * stream,AVIStreamHeader * header,uint32_t sizeInBytes,int trackNumber)351 uint8_t aviWrite::writeAudioHeader(ADM_audioStream *stream, AVIStreamHeader *header,
352             uint32_t sizeInBytes,int	trackNumber)
353 {
354 
355 WAVHeader wav;
356 // pre compute some headers with extra data in...
357 
358 uint8_t extra[16];
359 int extraLen=0;
360 
361     if(!createAudioHeader(&wav,stream,header,sizeInBytes,trackNumber,extra,&extraLen))
362     {
363         return false;
364     }
365 
366 	setAudioStreamInfo (_file,
367 			*header,
368 	 		wav,
369 			trackNumber,
370 			extra,extraLen, 0x1000);
371 
372   return 1;
373 }
374 
375 //_______________________________________________________
376 //
377 //   Begin to save, built header and prepare structure
378 //   The nb frames is indicative but the real value
379 //   must be smaller than this parameter
380 //
381 //_______________________________________________________
saveBegin(const char * name,ADM_videoStream * video,uint32_t nbAudioStreams,ADM_audioStream * audiostream[])382 uint8_t aviWrite::saveBegin (
383              const char         *name,
384              ADM_videoStream    *video,
385              uint32_t           nbAudioStreams,
386              ADM_audioStream 	*audiostream[])
387 {
388 
389 
390         _audioStreams=audiostream;
391 //  Sanity Check
392         ADM_assert (_out == NULL);
393         if (!(_out = qfopen (name, "wb")))
394         {
395                 printf("Problem writing : %s\n",name);
396                 return 0;
397         }
398         _file=new ADMFile();
399         if(!_file->open(_out))
400         {
401                 printf("Cannot create ADMfileio\n");
402                 delete _file;
403                 _file=NULL;
404                 return 0;
405         }
406         vframe = 0;
407         nb_audio=0;
408     // Build avi like structure from the one from vstream
409     //----------------------------------------------------
410         memset (&_mainheader, 0, sizeof (MainAVIHeader));
411         mx_mainHeaderFromVideoStream(&_mainheader,video);
412         _mainheader.dwStreams = 1+nbAudioStreams;
413         nb_audio=nbAudioStreams;
414         _mainheader.dwTotalFrames = 0;
415 
416         memset (&_videostream, 0, sizeof (AVIStreamHeader));
417         mx_streamHeaderFromVideo(&_videostream,video);
418         _videostream.dwLength = 0;
419 
420 
421         mx_bihFromVideo(&_bih,video);
422 
423   //___________________
424   // Prepare header
425   //___________________
426     uint32_t videoextraLen;
427     uint8_t  *videoextra;
428     video->getExtraData(&videoextraLen, &videoextra);
429 
430 
431       _file->seek(0);
432       AviListAvi *LAll = new AviListAvi ("RIFF", _file);
433       LAll->Begin();
434       LAll->Write32("AVI ");
435 
436 
437       // Header chunk
438       AviListAvi *LMain = new AviListAvi ("LIST", _file);
439       LMain->Begin();
440       LMain->Write32("hdrl");
441       LMain->Write32 ("avih");
442       LMain->Write32 (sizeof (MainAVIHeader));
443       LMain->writeMainHeaderStruct(_mainheader);
444       writeVideoHeader(videoextra,videoextraLen );
445       for(int i=0;i<nb_audio;i++)
446         {
447             writeAudioHeader(audiostream[i],&(audioTracks[i].header),0,i);
448         }
449     // Put a place holder for the futur odml
450     // size = 248+12
451         uint64_t odmlChunkPosition;
452         LMain->writeDummyChunk(248+12,&odmlChunkPosition);
453 	LMain->End();
454 	delete LMain;
455 	LMain=NULL;
456     for(int i=0;i<3;i++)
457         ADM_info("SuperIndex position so far %d : %" PRId64"\n",i,openDmlHeaderPosition[i]);
458   //___________________________________
459   // Write the beginning of the movie part
460   //___________________________________
461     switch(muxerConfig.odmlType)
462     {
463         case AVI_MUXER_TYPE1:
464         case AVI_MUXER_AUTO:
465                 indexMaker=new aviIndexAvi(this,LAll,odmlChunkPosition);
466                 break;
467         case AVI_MUXER_TYPE2:
468                 indexMaker=new aviIndexOdml(this,LAll,odmlChunkPosition);
469                 break;
470         default:
471                 ADM_assert(0);
472                 break;
473     }
474   LAll=NULL; // it now belongs to index
475 
476   vframe = 0;
477   return 1;
478 }
479 
480 /**
481     \fn    saveVideoFrame
482     \brief Write video frames and update index accordingly
483 */
saveVideoFrame(uint32_t len,uint32_t flags,uint8_t * data)484 uint8_t aviWrite::saveVideoFrame (uint32_t len, uint32_t flags, uint8_t * data)
485 {
486     if(indexMaker->switchToType2Needed(len))
487     {
488             ADM_info("Switching to type2 index\n");
489             aviIndexAvi *oldIndex=(aviIndexAvi *)indexMaker;
490             aviIndexOdml *nwIndex=new aviIndexOdml( this,oldIndex);
491             oldIndex->handOver();
492             delete oldIndex;
493             indexMaker=nwIndex;
494     }
495     vframe++;
496     return indexMaker->addVideoFrame(len, flags,data);
497 }
498 /**
499        \fn saveAudioFrame
500 */
saveAudioFrame(uint32_t index,uint32_t len,uint8_t * data)501 uint8_t aviWrite::saveAudioFrame (uint32_t index,uint32_t len, uint8_t * data)
502 {
503     // update our stats
504     aviAudioTrack *trk=&(audioTracks[index]);
505     trk->sizeInBytes+=len;
506     trk->nbBlocks++;
507     return indexMaker->addAudioFrame(index,len,AVI_KEY_FRAME,data);
508 }
509 /**
510     \fn setEnd
511     \brief end movie writing, update headers & close file
512 */
setEnd(void)513 uint8_t aviWrite::setEnd (void)
514 {
515 
516   // First close the movie
517   indexMaker->writeIndex();
518 
519   _mainheader.dwTotalFrames = vframe;
520   _videostream.dwLength = vframe;
521 
522 // Update Header
523 // AUDIO SIZE ->TODO
524   updateHeader (&_mainheader, &_videostream);
525   printf("\n End of movie, \n video frames : %u\n",vframe);
526     for(int i=0;i<nb_audio;i++)
527     {
528         printf("Track %d Size :%" PRIu32" bytes, %" PRIu32" blocks\n",i,audioTracks[i].sizeInBytes,audioTracks[i].nbBlocks);
529     }
530 
531   // cleanup
532   delete _file;
533   _file=NULL;
534 
535   qfclose (_out);
536   _out = NULL;
537   return 1;
538 
539 }
540 /**
541         \fn setVideoStreamInfo
542 
543 */
setVideoStreamInfo(ADMFile * fo,const AVIStreamHeader & stream,const ADM_BITMAPINFOHEADER & bih,uint8_t * extra,uint32_t extraLen,uint32_t maxxed)544 bool aviWrite::setVideoStreamInfo (ADMFile * fo,
545 			 const AVIStreamHeader      &stream,
546 			 const ADM_BITMAPINFOHEADER &bih,
547 			 uint8_t * extra, uint32_t extraLen,
548 			 uint32_t maxxed)
549 {
550   AviListAvi * alist;
551 
552   alist = new AviListAvi ("LIST", fo);
553   // 12 LIST
554   // 8 strf subchunk
555   // 8 strl subchunk
556   // 8 defaultoffset
557   alist->Begin();
558   alist->Write32("strl");
559   // sub chunk 1
560   alist->writeStrh(stream);
561   alist->writeStrfBih(bih,extraLen,extra);
562 
563   // Place holder for odml super index
564   uint64_t pos;
565   alist->writeDummyChunk(AVI_SUPER_INDEX_CHUNK_SIZE,&pos);
566   printf("[ODML] videoTrack : using ODML placeholder of size %u bytes at pos 0x%" PRIx64"\n",AVI_SUPER_INDEX_CHUNK_SIZE,pos);
567   openDmlHeaderPosition[0]=pos;
568   alist->End ();
569   delete alist;
570   return 1;
571 }
572 /**
573         \fn setVideoStreamInfo
574 
575 */
setAudioStreamInfo(ADMFile * fo,const AVIStreamHeader & stream,const WAVHeader & wav,int audioTrackNumber,uint8_t * extra,uint32_t extraLen,uint32_t maxxed)576 bool aviWrite::setAudioStreamInfo (ADMFile * fo,
577 			 const AVIStreamHeader      &stream,
578 			 const WAVHeader &wav,
579 			 int audioTrackNumber,
580 			 uint8_t * extra, uint32_t extraLen,
581 			 uint32_t maxxed)
582 {
583   AviListAvi * alist;
584 
585   alist = new AviListAvi ("LIST", fo);
586   // 12 LIST
587   // 8 strf subchunk
588   // 8 strl subchunk
589   // 8 defaultoffset
590   alist->Begin();
591   alist->Write32("strl");
592   // sub chunk 1
593   audioStreamHeaderPosition[audioTrackNumber]=alist->Tell();
594   alist->writeStrh(stream);
595   alist->writeStrfWav(wav,extraLen,extra);
596 
597 
598   uint64_t pos;
599   alist->writeDummyChunk(AVI_SUPER_INDEX_CHUNK_SIZE,&pos);
600   ADM_info("[ODML] Audio track %d, using ODML placeholder of size %u bytes, odmltrack=%d, pos=0x%" PRIx64"\n",
601                             audioTrackNumber,AVI_SUPER_INDEX_CHUNK_SIZE,1+audioTrackNumber,pos);
602   openDmlHeaderPosition[1+audioTrackNumber]=pos;
603   alist->End ();
604   delete alist;
605   return 1;
606 }
607 
608 
609 // EOF
610