/***************************************************************************
                          oplug_avi.cpp  -  description
                             -------------------

		This set of function is here to provide a simple api to the gui
		It will facilitate the use of other function such as audio processing
		etc...

    begin                : Mon Feb 11 2002
    copyright            : (C) 2002 by mean
    email                : fixounet@free.fr
 ***************************************************************************/

 /*
 * MODIFIED Feb 2005 by GMV: ODML write support
 */
 
/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <stdio.h>
#include <stdlib.h>

#include <string.h>
#include <strings.h>
#include <ADM_assert.h>
#include <math.h>

#include "config.h"
#include "avifmt.h"
#include "avifmt2.h"
#include "ADM_audio/aviaudio.hxx"
#include "fourcc.h"
#include "riffparser.h"
#include "subchunk.h"
#include "avilist.h"
#include "op_aviwrite.hxx"
#include "ADM_toolkit/toolkit.hxx"
#include "ADM_library/ADM_fileio.h"

// MOD Feb 2005 by GMV
#include "ADM_toolkit/ADM_debugID.h"
#define MODULE_NAME MODULE_SAVE_AVI
#include "ADM_toolkit/ADM_debug.h"
#include "prefs.h"
// END MOD Feb 2005 by GMV

//------------
typedef struct
{
  uint32_t fcc, flags, offset, len;
}
IdxEntry;

//
// We put them

IdxEntry *myindex = NULL;


aviWrite::aviWrite( void )
{
	_out=NULL;
	LAll=NULL;
	LMovie=NULL;
	LMain=NULL;
        _file=NULL;
		
	// MOD Feb 2005 by GMV: ODML support
	odml_indexes=NULL;
	// END MOD Feb 2005 by GMV
}

// MOD Feb 2005 by GMV: remove ODML index
aviWrite::~aviWrite(){
	odml_destroy_index();
}
// END MOD Feb 2005 by GMV

uint8_t aviWrite::sync( void )
{
	ADM_assert(_file);
	_file->flush();
	return 1;

}
//
// Overwrite some headers with their final value
//
//
uint8_t aviWrite::updateHeader (MainAVIHeader * mainheader,
			AVIStreamHeader * videostream,
			AVIStreamHeader * astream)
{
  UNUSED_ARG(astream);

        ADM_assert(_file);
        
        _file->seek(32);
// Update main header
#ifdef ADM_BIG_ENDIAN
	MainAVIHeader ma;
	memcpy(&ma,mainheader,sizeof(MainAVIHeader));
	Endian_AviMainHeader(&ma);		
  	_file->write ((uint8_t *)&ma, sizeof (ma));
#else
  	_file->write ((uint8_t *)mainheader, sizeof (MainAVIHeader));
#endif
// now update video stream header
        _file->seek(0x6c);
#ifdef ADM_BIG_ENDIAN

	AVIStreamHeader as;
	memcpy(&as,videostream,sizeof(as));
	Endian_AviStreamHeader(&as);		
  	_file->write ((uint8_t *)&as, sizeof (as));
#else
        _file->write ((uint8_t *)videostream, sizeof (AVIStreamHeader));
#endif
  // should do audio too, but i's relatively harmless...
  // Yes, indeed it helps for VBR audio :)

  return 1;
}

//________________________________________________
//   Beginning of the write process
//   We fill-in the headers
//	1- Create list and write main header
//_______________________________________________
uint8_t aviWrite::writeMainHeader( void )
{

  ADM_assert (_file);
  ADM_assert (LAll == NULL);
  _file->seek(0);
  

  LAll = new AviList ("RIFF", _file);
  LAll->Begin ("AVI ");
  // Header chunk
  LMain = new AviList ("LIST", _file);
  LMain->Begin ("hdrl");
  LMain->Write32 ("avih");
  LMain->Write32 (sizeof (MainAVIHeader));
#ifdef ADM_BIG_ENDIAN
	MainAVIHeader ma;
	memcpy(&ma,&_mainheader,sizeof(ma));
	Endian_AviMainHeader(&ma);		
	LMain->Write((uint8_t *)&ma,sizeof(ma));	
#else
  	LMain->Write ((uint8_t *) &_mainheader, sizeof (MainAVIHeader));
#endif
	return 1;
}
//________________________________________________
//   Beginning of the write process
//   We fill-in the headers
//	2- Write video headers
//_______________________________________________
uint8_t aviWrite::writeVideoHeader( uint8_t *extra, uint32_t extraLen )
{

  ADM_assert (_file);

      _videostream.fccType = fourCC::get ((uint8_t *) "vids");
      _bih.biSize=sizeof(_bih)+extraLen;
	// MOD Feb 2005 by GMV: video super index length
	uint32_t odml_super_idx_size=24+odml_nbrof_index*16;
	// END MOD Feb 2005 by GMV
#ifdef ADM_BIG_ENDIAN
	// in case of Little endian, do the usual swap crap
	
	AVIStreamHeader as;
	BITMAPINFOHEADER b;
	memcpy(&as,&_videostream,sizeof(as));
	Endian_AviStreamHeader(&as);		
	memcpy(&b,&_bih,sizeof(_bih));
	Endian_BitMapInfo( &b );
  	setStreamInfo (_file, (uint8_t *) &as,
		  (uint8_t *)&b,sizeof(BITMAPINFOHEADER),
		// MOD Feb 2005 by GMV: ODML support
		odml_super_idx_size,0,
		// END MOD Feb 2005 by GMV
		  extra,extraLen, 	 
		 0x1000);
#else
  	setStreamInfo (_file, (uint8_t *) &_videostream,
		  (uint8_t *)&_bih,sizeof(BITMAPINFOHEADER),
		// MOD Feb 2005 by GMV: ODML support
		odml_super_idx_size,0,
		// END MOD Feb 2005 by GMV
		  extra,extraLen, 	 
		 0x1000);

#endif
	return 1;
}
typedef struct VBRext
    {
  uint16_t   	    cbsize ;
  uint16_t          wId ;
  uint32_t          fdwflags ;
  uint16_t          nblocksize ;
  uint16_t          nframesperblock  ;
  uint16_t          ncodecdelay ;
} VBRext;


//________________________________________________
//   Beginning of the write process
//   We fill-in the headers
//	3- Write audio headers 
//   That one can be used several times so we pass stuff
//   as parameter
//_______________________________________________
static 	uint32_t aacBitrate[16]=
{
	96000, 88200, 64000, 48000,
	44100, 32000, 24000, 22050,
	16000, 12000, 11025,  8000,
	0,     0,     0,     0 
};

uint8_t aviWrite::writeAudioHeader (	AVDMGenericAudioStream * stream, AVIStreamHeader *header
// MOD Feb 2005 by GMV: ODML support
,uint8_t	odml_stream_nbr
// END MOD Feb 2005 by GMV
)
{
// MOD Feb 2005 by GMV: audio super index length
uint32_t odml_super_idx_size=24+odml_nbrof_index*16;
// END MOD Feb 2005 by GMV
WAVHeader *wav=NULL;
// pre compute some headers with extra data in...
uint8_t wmaheader[12];
VBRext  mp3vbr;
uint8_t aacHeader[12];
uint8_t *extra=NULL;
uint32_t extraLen=0;
     
	if(!stream) return 1;

	memset(wmaheader,0,12);
	memset(&mp3vbr,0,sizeof(mp3vbr));

	wmaheader[16-16]=0x0a;
	wmaheader[19-16]=0x08;
	wmaheader[22-16]=0x01;
	wmaheader[24-16]=0x74;
	wmaheader[25-16]=01;

        wav = stream->getInfo ();
      ADM_assert (wav);

      memset (header, 0, sizeof (AVIStreamHeader));
      header->fccType = fourCC::get ((uint8_t *) "auds");
      header->dwInitialFrames = 0;
      header->dwStart = 0;
      header->dwRate = wav->byterate;
      header->dwSampleSize = 1;
      header->dwQuality = 0xffffffff;
      header->dwSuggestedBufferSize = 8000;
      header->dwLength = stream->getLength ();
      
	switch(wav->encoding)
	{
		case WAV_AAC:
		{
		// nb sample in stream  
		
			double len;
			len=_videostream.dwLength;
#if 1			
			len/=_videostream.dwRate;
			len*=_videostream.dwScale;			
			len*=wav->frequency;
			len/=1024;
#else		
			header->dwLength= floor(len);//_videostream.dwLength; 
#endif			
		 // AAC is mostly VBR
		 header->dwFlags=1;
		 header->dwInitialFrames=0;
		 header->dwRate=wav->frequency;
		 
		 	
		 
		 header->dwScale=1024; //sample/packet 1024 seems good for aac
		 header->dwSampleSize = 0;
		 header->dwSuggestedBufferSize=8192;
		 header->dwInitialFrames = 0;	 
		
		// header->dwLength= _videostream.dwLength; 
		 wav->blockalign=1024;	  
		 wav->bitspersample = 0; 
		 
		//*b++ = (BYTE)((profile +1) << 3 | (SRI >> 1));
		//*b++ = (BYTE)(((SRI & 0x1) << 7) | (aacsource->GetChannelCount() << 3));
		
		int SRI=4;	// Default 44.1 khz
		for(int i=0;i<16;i++) if(wav->frequency==aacBitrate[i]) SRI=i;
		aacHeader[0]=0x2;
		aacHeader[1]=0x0;
		aacHeader[2]=(2<<3)+(SRI>>1); // Profile LOW
		aacHeader[3]=((SRI&1)<<7)+((wav->channels)<<3);
		

		extra=&(aacHeader[0]);
		extraLen=4;
		}
		break;
	case WAV_MP3:							
		  // then update VBR fields
		  mp3vbr.cbsize = R16(12);
		  mp3vbr.wId = R16(1);
		  mp3vbr.fdwflags = R32(2);				  
	    	  mp3vbr.nframesperblock = R16(1);
		  mp3vbr.ncodecdelay = 0;
		  
		  wav->bitspersample = 0;
		  mp3vbr.nblocksize=R16(0x180); //383; // ??
    
		  header->dwScale = 1;
	  	  header->dwInitialFrames = 1;
    
		  if (stream->isVBR()) //wav->blockalign ==1152)	// VBR audio
			{			// We do like nandub do
		  	//ADM_assert (audiostream->asTimeTrack ());
		  	wav->blockalign = 1152;	// just a try
		     	wav->bitspersample = 16;
		  
		    	header->dwRate 	= wav->frequency;	//wav->byterate;
			header->dwScale = wav->blockalign;
			header->dwLength= _videostream.dwLength;
		   
  			header->dwSampleSize = 0;
		  	printf ("\n VBR audio detected\n");
		  	//
		  	// use extended headers
		  	//		
		  	//
			mp3vbr.nblocksize=1152;	
			extra=(uint8_t *)&mp3vbr;
			extraLen=sizeof(mp3vbr);
		   }	
		   else wav->blockalign=1;

			

			  break;
	
					 
	case WAV_WMA:
			header->dwScale 	= wav->blockalign;
			header->dwSampleSize 	= wav->blockalign;
			header->dwInitialFrames =1;				
			header->dwSuggestedBufferSize=10*wav->blockalign;				
			
			extra=(uint8_t *)&wmaheader;
			extraLen=12;
			break;
	default:
			header->dwScale = 1;  
			wav->blockalign=1;	
			break;
    }
#ifdef ADM_BIG_ENDIAN
	// in case of Little endian, do the usual swap crap
	
	AVIStreamHeader as;
	WAVHeader w;
	memcpy(&as,header,sizeof(as));
	Endian_AviStreamHeader(&as);		
	memcpy(&w,wav,sizeof(w));
	Endian_WavHeader( &w );
  	setStreamInfo (_file, 
		(uint8_t *) &as,
		  (uint8_t *)&w,sizeof(WAVHeader),
		// MOD Feb 2005 by GMV: ODML support
		odml_super_idx_size,odml_stream_nbr,
		// END MOD Feb 2005 by GMV
		  extra,extraLen, 	 
		 0x1000);
#else
	setStreamInfo (_file,
			(uint8_t *) header,
	 		(uint8_t *) wav, sizeof (WAVHeader),
			// MOD Feb 2005 by GMV: ODML support
			odml_super_idx_size,odml_stream_nbr,
			// END MOD Feb 2005 by GMV
			extra,extraLen, 0x1000);
#endif

  return 1;
}

//_______________________________________________________
//
//   Begin to save, built header and prepare structure
//   The nb frames is indicative but the real value
//   must be smaller than this parameter
//
//_______________________________________________________
uint8_t aviWrite::saveBegin (char 	*name,
		     MainAVIHeader 	*inmainheader, 
		     uint32_t 		nb_frame,
		     AVIStreamHeader * invideostream,
		     BITMAPINFOHEADER	*bih,
		     uint8_t 		*videoextra,
		     uint32_t  		videoextraLen,
		     AVDMGenericAudioStream * inaudiostream,
		     AVDMGenericAudioStream * inaudiostream2)
{

	asize=asize2=0;

//  Sanity Check
        ADM_assert (_out == NULL);
        if (!(_out = fopen (name, "wb")))
        {
                printf("Problem writing : %s\n",name);
                return 0;
        }
        _file=new ADMFile();
        if(!_file->open(_out))
        {
                printf("Cannot create ADMfileio\n");
                delete _file;
                _file=NULL;
                return 0;
        }
        curindex = 0;
        vframe = asize = 0;
        nb_audio=0;
        
// update avi header according to the information WE want
//
        memcpy (&_mainheader, inmainheader, sizeof (MainAVIHeader));
        _mainheader.dwFlags = AVIF_HASINDEX + AVIF_ISINTERLEAVED;
        if(!_mainheader.dwMicroSecPerFrame)
        {
                _mainheader.dwMicroSecPerFrame=40000;
        }
// update main header codec with video codev
        if (inaudiostream)
        {
                _mainheader.dwStreams = 2;
                nb_audio=1;
        }
        else
                _mainheader.dwStreams = 1;

	if(inaudiostream2)
	{
	 	printf("\n +++Dual audio stream...\n");
     		_mainheader.dwStreams ++;
		nb_audio++;
	}

  	_mainheader.dwTotalFrames = nb_frame;
//  Idem for video stream
//
  	memcpy (&_videostream, invideostream, sizeof (AVIStreamHeader));
  	_videostream.dwLength = nb_frame;
	_videostream.fccType=fourCC::get((uint8_t *)"vids");
	memcpy(&_bih,bih,sizeof(_bih));
	
	// MOD Feb 2005 by GMV: initialize ODML data
	// test for free data structures
	if(odml_indexes!=NULL){
		aprintf("\n ODML writer error: data structures not empty for init!");
		return 0;
	}
	// set generation mode
        uint32_t pref_odml=0;
        
        if(!prefs->get(FEATURE_USE_ODML, &pref_odml))
        {
          pref_odml=0;                
        }            
        
	doODML=NO;	// only option for users without largefile support
	#if defined _FILE_OFFSET_BITS && _FILE_OFFSET_BITS == 64
        if(pref_odml)        
	       doODML=HIDDEN;	// default; TODO: user should be able to choose NO for plain avi
	#endif
	if(doODML!=NO){
		// get number of streams
		odml_nbrof_streams=_mainheader.dwStreams;
		aprintf("\nnumber of streams: %lu\n",odml_nbrof_streams);
		// get number of frames per index
		odml_index_size=(long)ceil(1000000.0/(double)_mainheader.dwMicroSecPerFrame*600.0);	// one index per 10 Minutes; decrease if 4GB are not enough for this amount of time 
		aprintf("\nnumber of frames per index: %lu\n",odml_index_size);
		// get number or indexes per stream
		odml_nbrof_index=(long)ceil((double)nb_frame/(double)odml_index_size);
		aprintf("\nnumber of indexes per stream: %lu\n",odml_nbrof_index);
		// init some other values
		odml_header_fpos=0;
		odml_riff_fpos[0]=0;odml_riff_fpos[1]=0;odml_riff_fpos[2]=0;odml_riff_fpos[3]=0;
		odml_riff_count=0;
		odml_frames_inAVI=0;
		// create odml index data structure
		odml_indexes=(odml_super_index_t*) ADM_alloc (sizeof(odml_super_index_t) * odml_nbrof_streams); // super index list
		memset(odml_indexes,0,sizeof(odml_super_index_t) * odml_nbrof_streams);
		for(int a=0;a<odml_nbrof_streams;++a){	// for each stream -> one super index
			odml_indexes[a].odml_index= (odml_index_t*) ADM_alloc (sizeof(odml_index_t) * odml_nbrof_index); // index list
			memset(odml_indexes[a].odml_index,0,sizeof(odml_index_t) * odml_nbrof_index);
			for(int b=0;b<odml_nbrof_index;++b){	// for each index
				odml_indexes[a].odml_index[b].index=(odml_index_data_t*) ADM_alloc (sizeof(odml_index_data_t) * odml_index_size);	// index data
				memset(odml_indexes[a].odml_index[b].index,0,sizeof(odml_index_data_t) * odml_index_size);
				odml_indexes[a].odml_index[b].nEntriesInUse=0;	// (redundant)
			}
			// init data
			odml_indexes[a].index_count=0;
		}
	}
	// END MOD Feb 2005 by GMV
	
  //___________________
  // Prepare header
  //___________________

	writeMainHeader( );

	writeVideoHeader(videoextra,videoextraLen );

	// MOD Feb 2005 by GMV: ODML support
	/*writeAudioHeader (	inaudiostream , &_audio1 );
	writeAudioHeader (	inaudiostream2, &_audio2);*/ 
	writeAudioHeader (	inaudiostream , &_audio1,1);
	writeAudioHeader (	inaudiostream2, &_audio2,2); 
	// odml header placeholder
	odml_write_dummy_chunk(LMain, &odml_header_fpos, 16);
	// END MOD Feb 2005 by GMV

	LMain->End();
	delete LMain;
	LMain=NULL;
  //

  ADM_assert (!LMovie);
  
  LMovie = new AviList ("LIST", _file);
  LMovie->Begin ("movi");
  curindex = 0;
  // the *2 is for audio and video
  // the *3 if for security sake
  myindex = (IdxEntry *) ADM_alloc (sizeof (IdxEntry) * (nb_frame * 4));
  asize = 0;
  vframe = 0;
  return 1;
}

//_______________________________________________________
// Write video frames and update index accordingly
//_______________________________________________________
uint8_t aviWrite::saveVideoFrame (uint32_t len, uint32_t flags, uint8_t * data)
{
  vframe++;
  // MOD Feb 2005 by GMV:  interleave ODML index dummy and index frame 
	// write initial index chunks
	if(vframe==2 && doODML!=NO){	// apparently some players require a video frame at first in the movi list, so we put the initial index dummys behind it (bye bye index before data)
		odml_write_dummy_chunk(LMovie, &(odml_indexes[0].odml_index[0].fpos), 24+8*odml_index_size);
		if(odml_nbrof_streams>1)
			odml_write_dummy_chunk(LMovie, &(odml_indexes[1].odml_index[0].fpos), 24+8*odml_index_size);
		if(odml_nbrof_streams>2)
			odml_write_dummy_chunk(LMovie, &(odml_indexes[2].odml_index[0].fpos), 24+8*odml_index_size);
	}
	// test for new riff
	odml_riff_break(len+8); // data size + fcc + size info (padding is handled in odml_riff_break)
	// index frame
	if(!odml_index_frame(0, len,flags&AVI_KEY_FRAME)){
		aprintf("\ncan not index video frame %lu\n",vframe);
	}
// END MOD Feb 2005 by GMV
  return saveFrame (len, flags, data, (uint8_t *) "00dc");

}

uint8_t aviWrite::saveAudioFrame (uint32_t len, uint8_t * data)
{
  asize += len;
// MOD Feb 2005 by GMV: index frame and interleave ODML index dummy
	odml_riff_break(len+8); // data size + fcc + size info (padding is handled in odml_riff_break)
	if(!odml_index_frame(1, len,false)){
		aprintf("\ncan not index audio frame %lu\n",asize);
	}
// END MOD Feb 2005 by GMV
  return saveFrame (len, (uint32_t) 0, data, (uint8_t *) "01wb");
}
uint8_t aviWrite::saveAudioFrameDual (uint32_t len, uint8_t * data)
{
  asize2 += len;
// MOD Feb 2005 by GMV: index frame and interleave ODML index dummy
	odml_riff_break(len+8); // data size + fcc + size info (padding is handled in odml_riff_break)
	if(!odml_index_frame(2, len,false)){
		aprintf("\ncan not index audio (dual) frame %lu\n",asize);
	}
// END MOD Feb 2005 by GMV
  return saveFrame (len, (uint32_t) 0, data, (uint8_t *) "02wb");
}



uint8_t aviWrite::saveFrame (uint32_t len, uint32_t flags,
		     uint8_t * data, uint8_t * fcc)
{
  uint32_t offset;
  // offset of this chunk compared to the beginning
// MOD Feb 2005 by GMV: do not write idx1 in case of ODML
  //offset = LMovie->Tell () - 8 - LMovie->TellBegin ();
if(doODML!=NORMAL){
  offset = LMovie->Tell () - 8 - LMovie->TellBegin ();
}
// END MOD Feb 2005 by GMV
  LMovie->WriteChunk (fcc, len, data);
  // Now store the index part

// MOD Feb 2005 by GMV: do not write idx1 in case of ODML
if(doODML!=NORMAL){
// END MOD Feb 2005 by GMV
  myindex[curindex].fcc = fourCC::get (fcc);
  myindex[curindex].len = len;
  myindex[curindex].flags = flags;
  myindex[curindex].offset = offset;
  curindex++;
// MOD Feb 2005 by GMV: do not write idx1 in case of ODML
}
// END MOD Feb 2005 by GMV
  return 1;
}

//_______________________________________________________
// End movie
//_______________________________________________________
uint8_t aviWrite::setEnd (void)
{

  // First close the movie
  LMovie->End ();
  delete LMovie;
  LMovie = NULL;


// MOD Feb 2005 by GMV: do not write idx1 in case of ODML
if(doODML!=NORMAL){
// END MOD Feb 2005 by GMV
  printf ("\n writing %lu index parts", curindex);
  printf ("\n received %lu video parts", vframe);

// Updating compared to what has been really written
//

  // Write index  
  LAll->Write32 ("idx1");
  LAll->Write32 (curindex * 16);

  for (uint32_t i = 0; i < curindex; i++)
    {
      LAll->Write32 (myindex[i].fcc);
      LAll->Write32 (myindex[i].flags);
      LAll->Write32 (myindex[i].offset);	// abs position
      LAll->Write32 (myindex[i].len);
    }
// MOD Feb 2005 by GMV: do not write idx1 in case of ODML
}
// END MOD Feb 2005 by GMV
  // Close movie


  LAll->End ();
  delete
    LAll;
  LAll = NULL;
 printf ("\n Updating headers...\n");

// MOD Feb 2005 by GMV: ODML header and index
	if(doODML==NORMAL){
		odml_write_sindex(0, "00dc");	// video super index
		if(odml_nbrof_streams>1)odml_write_sindex(1,"01wb");	// audio super index
		if(odml_nbrof_streams>2)odml_write_sindex(2,"02wb");	// audio super index (dual)
		// odml header
		_file->seek(odml_header_fpos);
		AviList* LHeader =  new AviList("LIST", _file);
		LHeader->Begin("odml");
		LHeader->Write32("dmlh");
		LHeader->Write32((uint32_t)4);	// chunk size
		LHeader->Write32(vframe);	// total number of frames
		LHeader->End();
		delete LHeader;
		// indexes
		if(!odml_write_index(0, "00dc", "ix00")){	// video indexes
			aprintf("error writing video indexes");
		}
		if(odml_nbrof_streams>1)
			if(!odml_write_index(1, "01wb", "ix01")){	// audio indexes
				aprintf("error writing audio indexes");
			}
		if(odml_nbrof_streams>2)
			if(!odml_write_index(2, "02wb", "ix02")){	// audio indexes (dual)
				printf("error writing audio (dual) indexes");
			}
	}
	odml_destroy_index();		
// END MOD Feb 2005 by GMV


// MOD Feb 2005 by GMV: set number or frames in first riff
  //_mainheader.dwTotalFrames = vframe;
	if(doODML==NORMAL)
		_mainheader.dwTotalFrames=odml_frames_inAVI;
	else
  _mainheader.dwTotalFrames = vframe;
// END MOD Feb 2005 by GMV
	
  _videostream.dwLength = vframe;
  //astream.dwLength = asize;

// Update Header
  updateHeader (&_mainheader, &_videostream, NULL);


	printf("\n End of movie, \n video frames : %lu\n audio frames : %lu",vframe,asize);
  // need to update headers now
  // AUDIO SIZE ->TODO
  delete _file;
  _file=NULL;
  
  fclose (_out);
  _out = NULL;
  return 1;

}

//
//
//
uint8_t aviWrite::setStreamInfo (ADMFile * fo,
			 uint8_t * stream,
			 uint8_t * info, uint32_t infolen, 
			// MOD Feb 2005 by GMV: ODML support
			 uint32_t odml_headerlen,
			 uint8_t odml_stream_nbr,
			// END MOD Feb 2005 by GMV
			 uint8_t * extra, uint32_t extraLen,
			 uint32_t maxxed)
{


  AviList * alist;
  uint8_t * junk;
  uint32_t junklen;

  alist = new AviList ("LIST", fo);


  // 12 LIST
  // 8 strf subchunk
  // 8 strl subchunk
  // 8 defaultoffset
  alist->Begin ("strl");

  // sub chunk 1
  alist->WriteChunk ((uint8_t *) "strh", sizeof (AVIStreamHeader),
		     (uint8_t *) stream);

  uint8_t *buf=new uint8_t[infolen+extraLen];

	memcpy(buf,info,infolen);
	if(extraLen)
		memcpy(infolen+buf,extra,extraLen);

  alist->WriteChunk ((uint8_t *) "strf", infolen+extraLen, buf);

  junklen = maxxed - sizeof (AVIStreamHeader) - infolen-extraLen;
  junk = (uint8_t *) ADM_alloc (junklen);
  ADM_assert (junk);
  memset (junk,0, junklen);
  //
  // Fill junk with out info string  
  uint32_t len=strlen("Avidemux");
  
  if(junklen>len)
  	memcpy(junk,"Avidemux",len);	
  
  alist->WriteChunk ((uint8_t *) "JUNK", junklen, junk);
  ADM_dealloc (junk);

  // MOD Feb 2005 by GMV: ODML header
  odml_write_dummy_chunk(alist, &odml_indexes[odml_stream_nbr].fpos, odml_headerlen);
  // END MOD Feb 2005 by GMV

  alist->End ();
  delete alist;
  delete buf;
  return 1;


}
// return how much has been written
uint32_t	aviWrite::getPos( void )
{
uint32_t pos;
	 // we take size of file + index
	 // with 32 bytes per index entry
	 // 
	 ADM_assert(_file);
	 pos=_file->tell();
	 return pos+curindex*4*4;	 	
}

// MOD Feb 2005 by GMV:  ODML functions
void aviWrite::odml_destroy_index(void){
	// destroy odml index data structure
	if(doODML!=NO){
		if(odml_indexes){
			for(int a=0;a<odml_nbrof_streams;++a){
				if(odml_indexes[a].odml_index){
					for(int b=0;b<odml_nbrof_index;++b){
						if(odml_indexes[a].odml_index[b].index)
							ADM_dealloc (odml_indexes[a].odml_index[b].index);
					}
					ADM_dealloc (odml_indexes[a].odml_index);
				}
			}
			ADM_dealloc (odml_indexes);
		}
		odml_indexes=NULL;
	}
}
void aviWrite::odml_write_dummy_chunk(AviList* alist, uint64_t* fpos, uint32_t size){
	if(doODML!=NO){
		// save file position
		*fpos=alist->Tell();
		aprintf("\nwrite dummy chunk at file position %Lu with data size %lu\n",*fpos, size);
		// generate dummy data
		uint8_t* dummy=(uint8_t*)ADM_alloc (size);
		memset(dummy,0,size);
		// write dummy chunk
		alist->WriteChunk ((uint8_t *) "JUNK", size, dummy);	
		// clean up
		ADM_dealloc (dummy);
	}
}
bool aviWrite::odml_index_frame(int stream_nbr, uint32_t data_size, bool keyFrame){
	if(doODML!=NO){
		ADM_assert(!stream_nbr<odml_nbrof_streams);
		odml_super_index_t* sidx=odml_indexes+stream_nbr;	// access to super index
		if(sidx->odml_index[sidx->index_count].nEntriesInUse==odml_index_size){	// new index needed?
			if(sidx->index_count<odml_nbrof_index-1)	// can index counter be increased?
				++(sidx->index_count);	// increment index counter
			else{
				aprintf("\nindexes full!!\n");
				return false;
			}
			// handle possible riff break
			odml_riff_break(data_size+8); // data size + fcc + size info (padding is handled in odml_riff_break)
			// write placeholder
			odml_write_dummy_chunk(LMovie, &(sidx->odml_index[sidx->index_count].fpos), 24+8*odml_index_size);
			sidx->odml_index[sidx->index_count].nEntriesInUse=0;
		}
		odml_index_t* idx=sidx->odml_index+(sidx->index_count);		// access to index
		odml_index_data_t* idxd=idx->index+(idx->nEntriesInUse);	// access to unused index data
		
		uint64_t pos=LMovie->Tell()+8;	// preview position of data
		idxd->fpos=pos;	// store file position of data
		
		if(keyFrame)
			idxd->size=data_size; //store data size
		else	// if no key frame
			idxd->size=data_size|0x80000000; //store data size with bit 31 set
		
		++(idx->nEntriesInUse);	// advance to next free index data entry
	}
	return true;
}
void aviWrite::odml_write_sindex(int stream_nbr, char* stream_fcc){
	// Warning: This changes the file position
	if(doODML==NORMAL){
		_file->seek(odml_indexes[stream_nbr].fpos);
		aprintf("\nwriting super index at file pos %Lu\n",odml_indexes[stream_nbr].fpos);
		AviList* LIndex =  new AviList("JUNK", _file);	// abused writing aid (don't call Begin or End; the fcc is unused until 'Begin')
		LIndex->Write32("indx");			// 4cc
		LIndex->Write32(24+odml_nbrof_index*16);	// size
		LIndex->Write16(4);				// wLongsPerEntry
		LIndex->Write8(0);				// bIndexSubType
		LIndex->Write8(0);				// bIndexType (AVI_INDEX_OF_INDEXES)
		LIndex->Write32(odml_indexes[stream_nbr].index_count+1);// nEntriesInUse
		LIndex->Write32(stream_fcc);			// dwChunkId;
		LIndex->Write32((uint32_t)0);LIndex->Write32((uint32_t)0);LIndex->Write32((uint32_t)0);// reserved
		for(uint32_t a=0;a<=odml_indexes[stream_nbr].index_count;++a){	// for each chunk index
			LIndex->Write64(odml_indexes[stream_nbr].odml_index[a].fpos);	//absolute file position
			LIndex->Write32(32 + 8 * odml_index_size);	// complete index chunk size
			LIndex->Write32(odml_indexes[stream_nbr].odml_index[a].nEntriesInUse);	// duration
			aprintf("\nstream %lu, index %lu EntriesInUse:%lu\n",stream_nbr, a ,odml_indexes[stream_nbr].odml_index[a].nEntriesInUse);
		}
		delete LIndex;
	}	
}
bool aviWrite::odml_write_index(int stream_nbr, char* stream_fcc, char* index_fcc){	// write index
	// Warning: This changes the file position
	if(doODML==NORMAL){
		aprintf ("\n writing %lu interleaved ODML indexes for %lu frames in stream %s", odml_indexes[stream_nbr].index_count+1, vframe, stream_fcc);
		AviList* LIndex =  new AviList("JUNK", _file);	// abused writing aid (don't call Begin or End; the fcc is unused until 'Begin')
		for(int a=0;a<=odml_indexes[stream_nbr].index_count;++a){	// for each index
			odml_index_t* idx=odml_indexes[stream_nbr].odml_index+a;		// access to index
			_file->seek(idx->fpos);					// shift file pointer
			LIndex->Write32(index_fcc);			// 4cc
			LIndex->Write32(24+odml_index_size*8);		// data size
			LIndex->Write16(2);				// wLongsPerEntry
			LIndex->Write8(0);				// bIndexSubType
			LIndex->Write8(1);				// bIndexType (AVI_INDEX_OF_CHUNKS)
			LIndex->Write32(idx->nEntriesInUse);		// nEntriesInUse
			LIndex->Write32(stream_fcc);			// dwChunkId;
			uint64_t base_off=idx->index[0].fpos-8;		// lets take the position of the first frame in the index as base
			uint64_t rel_pos;
			LIndex->Write64(base_off);			// qwBaseOffset
			LIndex->Write32((uint32_t)0);			// reserved
			for(int b=0;b<idx->nEntriesInUse;++b){		// for each frame in the current index
				odml_index_data_t* idxd=idx->index+b;	// access to index data
				rel_pos=idxd->fpos-base_off;	// get relative file position
				if(rel_pos>(uint64_t)4*1024*1024*1024){	// index chunks have a maximum offset of 4GB
					printf("\nData rate too high for index size. Decrease index duration.\n"); // decrease the multiplicator in saveBegin that calculates odml_index_size
					printf("base:%Lu abs:%Lu rel:%Lu stream:%lu index:%lu entry:%lu",base_off,idxd->fpos,rel_pos,stream_nbr,a,b);
					delete LIndex;
					return false;
				}
				LIndex->Write32(rel_pos);	// relative file position
				LIndex->Write32(idxd->size);		// data size
			}
		}
		delete LIndex;
	}
	return true;
}
void aviWrite::odml_riff_break(uint32_t len){	// advance to the next riff if required
	if(doODML!=NO){
		// get padded size
		uint64_t len2=len;
		if(len & 1)++len2;
		// preview file position
		len2+=LMovie->Tell();
		// will we get over the next GB border?
		if( len2>((uint64_t)1024*1024*1024*(odml_riff_count+1)) ){
			if(doODML==HIDDEN){
				aprintf("\nstarting new (hidden) RIFF at %Lu\n",LMovie->Tell());
				if(odml_riff_count<4)	// we have only 4 buffers but this has to be enough
					odml_write_dummy_chunk(LMovie, odml_riff_fpos+odml_riff_count, 16);	// write dummy
				if(odml_riff_count==0) odml_frames_inAVI=vframe-1;	// rescue number of frames in first AVI (-1 since there may be no audio for the last video frame)
			}else{	// restart riff and movie
				aprintf("\nstarting new RIFF at %Lu\n",LMovie->Tell());
				// restart lists
				LMovie->End();
				LAll->End();
				LAll->Begin ("AVIX");
				LMovie->Begin ("movi");
			}
			++odml_riff_count;
		}
		// ODML required for movie?
		if(doODML==HIDDEN){
			if( ((uint64_t)getPos()+len+17) >= ((uint64_t)4*1024*1024*1024) ){	//if (written data + new chunk + index (old type) for new chunk + possible padding) does not fit into 4GB
				printf("\nswitching to ODML mode at %lu\n",LMovie->Tell());
				uint64_t last_pos=LMovie->Tell();	// rescue current file position
				// close First RIFF
				for(int a=0;a<4;++a){	// for each hidden riff
					if(odml_riff_fpos[a]!=0){
						_file->seek(odml_riff_fpos[a]);	// set file pointer to start of next riff
						LMovie->End();
						LAll->End();
						LAll->Begin("AVIX");
						LMovie->Begin("movi");
					}
				}
				// goto end of file
				_file->seek(last_pos);
				// following riffs can start directly
				doODML=NORMAL;	// write RIFF breaks directly
			}
		}
	}
}
// END MOD Feb 2005 by GMV

// EOF
