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