/*==============================================================================

FICHIER     : [dvdbackup.c]

DATE        : 2006/01/0005 20:56:20

CREATEUR    : [Linux!jef]

COMMENTAIRE :
 * encoding	UTF8 !-UTF8-!
		Released under GPL license, see gnu.org
================================================================================

==============================================================================*/
#define __USE_LARGEFILE64
#define _LARGEFILE64_SOURCE
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <alloca.h>
#include <math.h>

#include <gnome.h>

#ifdef __APPLE__
/* fopen is 64-bit aware on Mac OS X */
#define fopen64(A,B) fopen(A,B)
#endif

#define CTEST(x)		/***/

#include "support.h"

#include "const.h"
#include "dvdformat.h"
#include "dvdinfo.h"
#include "message.h"
#include "gconfig.h"
#include "interface.h"
#include "globals.h"
#include "systools.h"
#include "avancement.h"
#include "uitools.h"
#include "dvdtools.h"
#include "dvdcopy.h"
#include "ifo.h"
#include "bswap.h"
#include "vaporize.h"
#include "ac.h"
#include "dvdcell.h"
#include "../dvdauthor/libauthor.h"
#include "vapcontext.h"
#include "mpeg2dec.h"
#include "profile.h"
#include "asyncwriter.h"

static char TitreDvd[128];

static CellArray_t CellArray;
static TitleArray_t TitleArray;

static int CurVTS;
static FILE * OutFpVideo;	/* Video file pointer */
static long long OutSize;
static long long TotalSize;

static int CurVOB;

static unsigned char TempBuf[DVD_VIDEO_LB_LEN * 2];
static int TempSize = 0;

typedef struct {
	char *	vobFile;
	char *	aviFile;
	char *	logFile;
	int	bitrate;
	char 	cropParam[200];
	int	scaleX;
	int	scaleY;
	double	aspect;
	int	audioTrackId;
	char *	audioFormat; /* For specific lpcm audio */
	int	duree;
} EncodingInfos_t;


#define MEM_2GIG		( 2LL * 1024LL * 1024LL * 1024LL )
#define MEM_1GIG		( 1LL * 1024LL * 1024LL * 1024LL )

#define MIN_SECTORS4CROP	60000

/*@$#[dvd2avi.c] static proto. AutoProtoSigV1.1. date: 2010/03/15 23:03:38 */
#include "proto.h"
#ifdef __cplusplus
extern "C" {
#endif
static int Output(unsigned char *buffer, int len, char *targetDir);
static void MakeRoom(VapContext_t *c, int size);
static uint32_t _FindNextVobu(VapContext_t *c);
static int Readin(void *parm, unsigned char *buffer, int size);
static int OutputFlux(void *parm, unsigned char *buffer, int size);
static int CopyCell(Cell_t *c, char *targetDir);
static void FreeAll(void);
static int _readline(FILE *fp, char *line, int size);
static int ExecuteFfmpeg(char *sysCmd, int duree);
static int ExecuteMencoder(char *sysCmd, int duree);
static double GetAspectRatio(int noPisteVideo);
static void GetPictureSizes(int noPisteVideo, Profile_t *p, int cx, int cy, int *sx, int *sy, int bitrate);
static int CropDetection(char *iFile, char *cropParam, int duree, int startPos);
static char *ComputeCmdLine(Profile_t *profile, EncodingInfos_t *infos, int passNo);
static long long GetVobSize(int noPisteVideo, int breakFirstCellF);
static void InitGlobVars(void);
static int ComputeInfos(int noPisteVideo, AudioMap_t *audioMap, Profile_t *profile, EncodingInfos_t *infos);
static void VisuAvi(char *path);
static int OutputFluxFifo(void *parm, unsigned char *buffer, int size);
static int DvdToAviFifo(GtkWidget *forme, int noPisteVideo, AudioMap_t *audioMap, SubMap_t *subMap, int format);
#ifdef __cplusplus
}
#endif
/*@$% end of AutoProtoSigV1.1 (Dont remove this line) []*/

/*------------------------------------------------------------------------------
	OUTPUT-
Linux!jef 2006/01/03 21:05:50
------------------------------------------------------------------------------*/

static int Output( buffer, len, targetDir )
unsigned char * buffer;
int len;
char * targetDir;
{
	unsigned char * cBuff;

	if( !TempSize && len == DVD_VIDEO_LB_LEN ) {
		TempSize = len;
		cBuff = buffer;
	}
	else {
//!! fprintf(stderr,"Output: len %d TempSize: %d\n", len, TempSize );
		tc_memcpy( TempBuf + TempSize, buffer, len );
		TempSize += len;
		cBuff = TempBuf;
	}
	if( TempSize >= DVD_VIDEO_LB_LEN ) {

		if( !isNavPack( cBuff ) ) {
			Mpeg2Demux( cBuff, DVD_VIDEO_LB_LEN );
		}
		if( fwrite( cBuff, 1, DVD_VIDEO_LB_LEN, OutFpVideo ) != DVD_VIDEO_LB_LEN )	return( -1 );
		OutSize += DVD_VIDEO_LB_LEN;
		TotalSize += DVD_VIDEO_LB_LEN;
		SetTotalSize( TotalSize );
		TempSize -= DVD_VIDEO_LB_LEN;
		if( TempSize > 0 )	tc_memcpy( cBuff, cBuff + DVD_VIDEO_LB_LEN, TempSize );
	}
	return( 0 );
}
/*------------------------------------------------------------------------------
	MAKEROOM-
Linux!jef 2006/01/24 22:46:53
------------------------------------------------------------------------------*/

static void MakeRoom( VapContext_t * c, int size )
{
//	DBG('b',fprintf(stderr,"%s: size: %d\n", __FUNCTION__, size ););

	if( c->bufASize < (c->bufSize + size) ) {
		if( c->bufPtr != c->buf ) {
			memmove( c->buf, c->bufPtr, c->bufSize );
			c->bufPtr = c->buf;
		}
		c->bufASize = c->bufSize + size;
		c->buf = realloc( c->buf, c->bufASize );
		c->bufPtr = c->buf;
	}
	if( !c->bufSize ) {
		c->bufPtr = c->buf;
		return;
	}
}

/*------------------------------------------------------------------------------
	_FINDNEXTVOBU-
Linux!jef 2007/08/16 01:37:18
------------------------------------------------------------------------------*/

static uint32_t _FindNextVobu( c )
VapContext_t * c;
{
	vobu_admap_t * vobu_admap;
	uint32_t length, i;

	vobu_admap = c->cell->ifo->vts_vobu_admap;
	length = vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE;

	for( i = 0; i < length/sizeof(uint32_t); i++) {
// DBG('b',fprintf(stderr,"%s: vobu at sector: %d\n", __FUNCTION__, vobu_admap->vobu_start_sectors[i] ););
/* Vobu adress map seems to be ordered */
		if( vobu_admap->vobu_start_sectors[i] > c->sector) {
			DBG('b',fprintf(stderr,"%s: returning next vobu at sector: %d\n", __FUNCTION__, vobu_admap->vobu_start_sectors[i] ););
			return( vobu_admap->vobu_start_sectors[i] );
		}
//		if( vobu_admap->vobu_start_sectors[i] == c->sector) {
//			return( vobu_admap->vobu_start_sectors[i+1] );
//		}
	}
	DBG('b',fprintf(stderr,"%s: nextvobu notfound for sector: %d\n", __FUNCTION__, c->sector ););
	return( SRI_END_OF_CELL );
}

/*------------------------------------------------------------------------------
	READIN-
Linux!jef 2006/01/24 22:28:46
------------------------------------------------------------------------------*/

static int Readin( void * parm, unsigned char * buffer, int size )
{
	VapContext_t * c = (VapContext_t *) parm;
	int readed = 0;
	int len;
	dsi_t	dsi_pack;
	uint32_t nsectors;
	int badNavPack = 0;

//	DBG('b',fprintf(stderr,"%s: size: %d inBuf: %d\n", __FUNCTION__, size, c->bufSize ););

	if( c->bufSize ) {
		len = MyMin( size, c->bufSize );

		tc_memcpy( buffer, c->bufPtr, len );
		buffer += len;
		size -= len;
		c->bufPtr += len;
		c->bufSize -= len;
		readed += len;
	}
	if( !size ) {
//		DBG('b',fprintf(stderr,"%s: readed: %d\n", __FUNCTION__, readed ););
		return( readed );
	}

	if( c->dsi_next_vobu == SRI_END_OF_CELL ) {
		if( readed )	return( readed );

//		DBG('b',fprintf(stderr,"%s: EOF\n", __FUNCTION__ ););
		return( 0 ); /* EOF */
	}

/* read nav pack */
	MakeRoom( c, DVD_VIDEO_LB_LEN );
	len = MyDVDRead1Block( c->file_handle, c->sector, c->bufPtr );
	if( len == -1 )	return( -1 );
/* parse contained DSI pack */
	navRead_DSI( &dsi_pack, c->bufPtr + DSI_START_BYTE );
	if( len == 0 || dsi_pack.dsi_gi.nv_pck_lbn != c->sector ) {
		CreateDummyNavPack( c->bufPtr, c->sector );
		badNavPack = 1;
		c->dsi_next_vobu = _FindNextVobu( c );
	}

	len = DVD_VIDEO_LB_LEN;
	c->bufSize += len;

	Avancement( len );
	if( AvancementUi() < 0 )	return( -1 );
	if( badNavPack ) {
		MakeRoom( c, 1 * DVD_VIDEO_LB_LEN );
		CreateDummyPack( c->bufPtr + DVD_VIDEO_LB_LEN );
		nsectors = 1;
	}
	else {
		int i;
		unsigned char * cBuff;

		nsectors  = dsi_pack.dsi_gi.vobu_ea;
		if( !nsectors ) { /* Protected dvd ? */
			DBG('b',fprintf(stderr,"%s: no sectors in DSI !\n", __FUNCTION__););
			return( 0 ); /* EOF */
		}
		c->dsi_next_vobu = dsi_pack.vobu_sri.next_vobu;

		MakeRoom( c, nsectors*DVD_VIDEO_LB_LEN );
		cBuff = c->bufPtr + DVD_VIDEO_LB_LEN;
/* read VOBU */
		for( i = 0; i < nsectors; i++ ) {
			len = MyDVDRead1Block( c->file_handle, c->sector + 1 + i, cBuff );
// fprintf(stderr,"PACK: MyDVDRead1Block(%d)=%d\n", c->sector+1+i, len );
			if( len == -1 )	return( -1 );
			if( len == 0 ) {
				CreateDummyPack( cBuff );
				nsectors = 1;
				break;
			}
			cBuff += DVD_VIDEO_LB_LEN;
		}
	}
	len = nsectors * DVD_VIDEO_LB_LEN;
	c->bufSize += len;

	Avancement( len );
	if( AvancementUi() < 0 )	return( -1 );
	if( badNavPack ) {
		c->sector = c->dsi_next_vobu;
		c->dsi_next_vobu  = 0;
	}
	else
		c->sector += (c->dsi_next_vobu & 0x7fffffff);

	return( readed + Readin( parm, buffer, size ) );
}

/*------------------------------------------------------------------------------
	OUTPUTFLUX-
Linux!jef 2006/01/24 23:07:28
------------------------------------------------------------------------------*/

static int OutputFlux( void * parm, unsigned char * buffer, int size )
{
	VapContext_t * c = (VapContext_t *) parm;
	int writen = 0;

//	DBG('b',fprintf(stderr,"%s: size: %d\n", __FUNCTION__, size ););
	while( size > 0 ) {
		int len = MyMin( size, DVD_VIDEO_LB_LEN );

		if( Output( buffer, len, c->targetDir ) < 0 ) {
			return( -1 );
		}
		buffer += len;
		size -= len;
		writen += len;
	}
	return( writen );
}

/*------------------------------------------------------------------------------
	COPYCELL-
Linux!jef 2005/12/28 22:01:08
------------------------------------------------------------------------------*/

static int CopyCell( Cell_t * c, char * targetDir )
{
	uint32_t size;
	dvd_file_t * file_handle;
	VapContext_t vc;
	long long lSize;
	int ret;

	if( !c->selected )	return( 0 );

	DBG('b',fprintf(stderr,"%s: startSector: %u lastSector: %u vts: %d\n", __FUNCTION__, c->startSector, c->lastSector, c->vts ););

	size = c->lastSector - c->startSector;
	DisplayAvancement( _("Copie de la cellule %d (%d secteurs)\n"), c->chapter + 1, size);
	InitAvancement( size );
	file_handle = DVDOpenFile( Dvd, c->vts, DVD_READ_TITLE_VOBS);
	if( !file_handle ) {
		return( -1 );
	}
	memset( &vc, 0, sizeof(vc));
/* Initialize Vaporize context */
	vc.file_handle = file_handle;
	vc.sector = c->startSector;
	vc.cell = c;
	vc.targetDir = targetDir;
	lSize = (long long) size * (long long) DVD_VIDEO_LB_LEN;
	ret = Vaporize( lSize, &vc );

	if( vc.buf ) {
		free( vc.buf );
	}
	DVDCloseFile( file_handle );
	c->done = 1;
	return( ret );
}

/*------------------------------------------------------------------------------
	FREEALL-
Linux!jef 2006/01/16 22:10:25
------------------------------------------------------------------------------*/

static void FreeAll()
{
	int i;

	if( TitleArray.nbTitles ) {
		for( i = 0; i < TitleArray.nbTitles; i++ ) {
			FreeCellArray( &TitleArray.titles[i].ca );
		}
		free( TitleArray.titles );
		TitleArray.titles = NULL;
		TitleArray.nbTitles = 0;
	}
	FreeCellArray( &CellArray );
}
/*------------------------------------------------------------------------------
	_READLINE-
Linux!jef 2006/02/16 22:58:23
------------------------------------------------------------------------------*/

static int _readline( FILE * fp, char * line, int size )
{
	int readed = 0;

	size--;
	while( size ) {
		int c = fgetc( fp );

		if( c == EOF )	break;
		if( c == '\r' )	c = '\n';
		*line++ = (char)c;
		readed++;
		size--;
		if( c == '\n' )	break;
	}
	*line = 0;
	return( readed );
}
/*------------------------------------------------------------------------------
	EXECUTEFFMPEG-
Linux!jef 2005/12/20 22:17:17
------------------------------------------------------------------------------*/

static int ExecuteFfmpeg( char * sysCmd, int duree )
{
	FILE * fp;
	char line[1024];
	int res = 0;
	double lPct = -1;

	InitAvancement(0);
/* re-direct stderr */
	strcat( sysCmd, " 2>&1" );
	DBG('c',fprintf(stderr,"%s: executing (%s) duree: %d\n", __FUNCTION__, sysCmd, duree ););
	fp = my_popen( sysCmd, "r" );
	if( !fp )	 return( -1 );
	setbuf( fp, NULL );
	while( 1 )
	{
		char * p;

		if( !_readline( fp, line, sizeof(line)))	break;
		if( !*line )	continue;
// fprintf(stderr,"line(%s)\n", line );
		p = strstr( line, "time=" );
		if( p ) {
			double where;
			double pct;

			p += 5;
			where = atof( p );

			pct = ( where ) / (double) duree;;
// fprintf(stderr,"%f/%d = %f%%\n", where, duree, pct );
 			if( pct > lPct ) {
				if( AvancementUi2( pct ) < 0 ) { res = -1; break; }
				lPct = pct;
			}
			else
				GtkFlush();
		}
	}
	res = my_pclose( fp );
	DBG('c',fprintf(stderr,"%s: result= %d\n", __FUNCTION__, res ););
	if( res != 0 )	res = -1;
	return( res );
}
/*------------------------------------------------------------------------------
	EXECUTEMENCODER-
Linux!jef 2005/12/20 22:17:17
------------------------------------------------------------------------------*/

static int ExecuteMencoder( char * sysCmd, int duree )
{
	FILE * fp;
	char line[1024];
	int res = 0;
	double lPct = -1;

	InitAvancement(0);
/* re-direct stderr */
	strcat( sysCmd, " 2>&1" );
	DBG('c',fprintf(stderr,"%s: executing (%s) duree: %d\n", __FUNCTION__, sysCmd, duree ););
	fp = my_popen( sysCmd, "r" );
	if( !fp )	 return( -1 );
	setbuf( fp, NULL );
	while( 1 )
	{
		char * p;

		if( !_readline( fp, line, sizeof(line)))	break;
		if( !*line )	continue;
// fprintf(stderr,"line(%s)\n", line );
		p = strstr( line, "Pos:" );
		if( p ) {
			double where;
			double pct;

			p += 4;
			while( *p == ' ' )	p++;
			where = atof( p );

			pct = ( where ) / (double) duree;;
// fprintf(stderr,"%f/%d = %f%%\n", where, duree, pct );
 			if( pct > lPct ) {
				if( AvancementUi2( pct ) < 0 ) { res = -1; break; }
				lPct = pct;
			}
			else
				GtkFlush();
		}
	}
	res = my_pclose( fp );
	DBG('c',fprintf(stderr,"%s: result= %d\n", __FUNCTION__, res ););
	if( res != 0 )	res = -1;
	return( res );
}
/***************************************************************************//**
 *	GETASPECTRATIO-
 * @date	2009/01/19 22:11:49
 * @author	jef-Linux
 * @brief
 *	Pas d'explication
 * @details
 *	Pas de details
 * @param[in]	Pas de detail
 * @param[out]	Pas de detail
*******************************************************************************/

static double GetAspectRatio( int noPisteVideo )
{
	ifo_handle_t * ifo = Ifo[Ifo_zero->tt_srpt->title[noPisteVideo].title_set_nr];
	vtsi_mat_t *vtsi_mat = ifo->vtsi_mat;
	video_attr_t *video_attr;
	char *aspect_ratio[4] = {"4:3", "16:9", "4:3", "16:9"};

	video_attr = &vtsi_mat->vts_video_attr;

	if( *aspect_ratio[video_attr->display_aspect_ratio] == '4' )
		return( 4.0/3.0 );
	return( 16.0/9.0 );
}
/***************************************************************************//**
 *	GETPICTURESIZES-
 * @date	2009/01/20 20:49:21
 * @author	jef-Linux
 * @brief
 *	Pas d'explication
 * @details
 *	Pas de details
 * @param[in]	Pas de detail
 * @param[out]	Pas de detail
*******************************************************************************/

static void GetPictureSizes( int noPisteVideo, Profile_t * p, int cx, int cy, int * sx, int * sy, int bitrate )
{
	char *aspect_ratio[4] =  { "4:3",	"16:9",	"4:3",	"16:9"};
	double video_height[4] = { 480,		576,	576,	576 };
	double video_width[4]  = { 720,		720,	352,	352 };
	ifo_handle_t * ifo = Ifo[Ifo_zero->tt_srpt->title[noPisteVideo].title_set_nr];
	vtsi_mat_t *vtsi_mat = ifo->vtsi_mat;
	video_attr_t *video_attr;
	double ARa;
	double PRdvd;
	double ARc;
	double resX,resY;
	double CQ = p->cq; // 0.30;

	bitrate *= 1000;
	video_attr = &vtsi_mat->vts_video_attr;

	if( *aspect_ratio[video_attr->display_aspect_ratio] == '4' ) {
		ARa = 4.0/3.0;
		PRdvd = video_width[video_attr->picture_size] / video_height[video_attr->video_format];
	}
	else {
		ARa = 16.0/9.0;
		PRdvd = video_width[video_attr->picture_size] / video_height[video_attr->video_format];
	}
	ARc = ( (double)cx * ( ARa / PRdvd)) / (double)cy;
	resY = rint(sqrt( (double)bitrate / 25.0 / ARc / CQ ) / 16.0) * 16.0;
	resX = rint( resY * ARc / 16.0 ) * 16.0;
/*	Protect orginal sizes if bigger */
	if( resX >= cx || resY >= cy ) {
		resX = cx;
		resY = cy;
	}

	DBG( 'b',fprintf(stderr,"%s: CQ=%f\n", __FUNCTION__, CQ ););
	DBG( 'b',fprintf(stderr,"%s: ARa=%f\n", __FUNCTION__, ARa ););
	DBG( 'b',fprintf(stderr,"%s: PRdvd=%f\n", __FUNCTION__, PRdvd ););
	DBG( 'b',fprintf(stderr,"%s: ARc=%f\n", __FUNCTION__, ARc ););
	DBG( 'b',fprintf(stderr,"%s: ResX=%f\n", __FUNCTION__, resX ););
	DBG( 'b',fprintf(stderr,"%s: ResY=%f\n", __FUNCTION__, resY ););

	*sx = resX;
	*sy = resY;
}

/***************************************************************************//**
 *	CROPDETECTION-
 * @date	2009/01/19 22:50:34
 * @author	jef-Linux
 * @brief
 *	Pas d'explication
 * @details
 *	Pas de details
 * @param[in]	Pas de detail
 * @param[out]	Pas de detail
*******************************************************************************/

static int CropDetection( char * iFile, char * cropParam, int duree, int startPos )
{
	char sysCmd[1024];
	FILE * fp;
	char line[1024];
	int res = 0;
	double lPct = -1;
/*	int duree = 60;
	int startPos = 120;
*/
	int frames = duree * 25;

	*cropParam = 0;
	sprintf( sysCmd, "mplayer -vo null -ao null -ss %d -frames %d -nosound -speed 100 -vf cropdetect %s", startPos, frames, iFile );
	strcat( sysCmd, " 2>&1" );
	DBG('c',fprintf(stderr,"%s: executing (%s)\n", __FUNCTION__, sysCmd ););
	fp = my_popen( sysCmd, "r" );
	if( !fp )	 return( -1 );
	DisplayAvancement( _("Détection des bandes noires\n"));
	InitAvancement(0);
	setbuf( fp, NULL );
	while( 1 )
	{
		char * p;

		if( !_readline( fp, line, sizeof(line)))	break;
		if( !*line )	continue;
// fprintf(stderr,"line(%s)\n", line );
		p = strstr( line, "crop=" );
		if( p ) {
			char * pStart;

			p += 5;
			pStart = p;
			while( *p && *p != ')' )	p++;
			if( *p == ')' ) {
				*p = 0;
				strcpy( cropParam, pStart );
			}
		}
		p = strstr( line, "V:" );
		if( p ) {
			double where;
			double pct;

			p += 2;
			while( *p == ' ' )	p++;
			where = atof( p ) - startPos;

			pct = ( where ) / (double) duree;;
// fprintf(stderr,"(%s) %f/%d = %f%%\n", line,  where, duree, pct );
 			if( pct >= 0.0 && pct <= 1.0 && pct > lPct ) {
				if( AvancementUi2( pct ) < 0 ) { res = -1; break; }
				lPct = pct;
			}
		}
	}
	res = my_pclose( fp );
	DBG('c',fprintf(stderr,"%s: result= %d (%s)\n", __FUNCTION__, res, cropParam ););
	if( res != 0 )	res = -1;
	return( res );
}
/***************************************************************************//**
 *	COMPUTECMDLINE-
 * @date	2009/01/19 21:47:44
 * @author	jef-Linux
 * @brief
 *	Pas d'explication
 * @details
 *	Pas de details
 * @param[in]	Pas de detail
 * @param[out]	Pas de detail
*******************************************************************************/

static char * ComputeCmdLine( Profile_t * profile, EncodingInfos_t * infos, int passNo )
{
	char * oLine = malloc( 10240 ); /* KK */
	char * p = oLine;
	char * iLine;

	if( passNo == 1 )
		iLine = profile->pass1Opt;
	else
		iLine = profile->pass2Opt;

	strcpy( p, profile->encTool );
	strcat( p, " " );
	p += strlen( p );

	while( *iLine ) {
		if( *iLine == '%' ) {
			switch( iLine[1] ) {
				case 'b' :
					sprintf( p, "%d", infos->bitrate );
					p += strlen( p );
					break;
				case 'a' :
					sprintf( p, "%.5f", infos->aspect );
					p += strlen( p );
					break;
				case 'i' :
					strcpy( p, infos->vobFile );
					p += strlen( p );
					break;
				case 'o' :
					strcpy( p, infos->aviFile );
					p += strlen( p );
					break;
				case 'p' :
					strcpy( p, infos->logFile );
					p += strlen( p );
					break;
				case 'c' :
					strcpy( p, infos->cropParam );
					p += strlen( p );
					break;
				case 'u' :
					sprintf( p, "%d", GetNumCpu() );
					p += strlen( p );
					break;
				case 'x' :
					sprintf( p, "%d", infos->scaleX );
					p += strlen( p );
					break;
				case 'y' :
					sprintf( p, "%d", infos->scaleY );
					p += strlen( p );
					break;
				case 'A' :
					sprintf( p, "%d", infos->audioTrackId );
					p += strlen( p );
					break;
			}
			iLine++;
		}
		else
			*p++ = *iLine;
		iLine++;
	}
	*p = 0;
/* Handle special case: lpcm audio track doesn't support -aoc copy instead use -oac pcm */
	if( !strcmp( infos->audioFormat, "lpcm") ){
		char * p = strstr( oLine, "-oac copy" );

		if( p ) {
			memcpy( p, "-oac pcm ", 9);
		}
	}
	return( oLine );
}
/***************************************************************************//**
 *	GETVOBSIZE-
 * @date	2009/01/21 20:53:40
 * @author	jef-Linux
 * @brief
 *	Pas d'explication
 * @details
 *	Pas de details
 * @param[in]	Pas de detail
 * @param[out]	Pas de detail
*******************************************************************************/

static long long GetVobSize( int noPisteVideo, int breakFirstCellF )
{
	int totalSectors = 0;
	int i;

	for( i = 0; i < CellArray.nbCells; i++ ) {
		Cell_t * c = CellArray.cells + i;

		if( c->selected ) {
			totalSectors += SECTOR_SZ(c);
			if( breakFirstCellF )	break;
		}
	}
	return( (long long)totalSectors * (long long) DVD_BLOCK_LEN );
}

/***************************************************************************//**
 *	INITGLOBVARS-
 * @date	2010/02/02 20:46:35
 * @author	jef-Linux
 * @brief
 *	Pas d'explication
 * @details
 *	Pas de details
 * @param[in]	Pas de detail
 * @param[out]	Pas de detail
*******************************************************************************/
static void InitGlobVars()
{
	CurVTS = 0;
	CurVOB = 0;
	OutSize = 0;
	TotalSize = 0;
}
/***************************************************************************//**
 *	COMPUTEINFOS-
 * @date	2010/03/15 22:54:00
 * @author	jef-Linux
 * @brief
 *	Pas d'explication
 * @details
 *	Pas de details
 * @param[in]	Pas de detail
 * @param[out]	Pas de detail
*******************************************************************************/
static int ComputeInfos( int noPisteVideo, AudioMap_t * audioMap, Profile_t * profile, EncodingInfos_t  * infos )
{
	int i;
	long long dvdAudioSize =  0;
	double audioSize;
	double videoSize;
	double overHead;
	double bitrate;
	int cx,cy,dx,dy;

	TotalAudioSize( noPisteVideo, &infos->duree );
	for( i = 0; i < MAX_AUDIO_TRACK; i++ ) {
		if( audioMap->audioTracks[i] ) {
			dvdAudioSize = AudioSize( noPisteVideo, i );
		}
	}
	infos->aspect = GetAspectRatio( noPisteVideo );

	if( profile->audioBitrate == -1 ) {
		audioSize = dvdAudioSize;
	}
	else { /* Using less channel and so */
		audioSize = ( (double)profile->audioBitrate * (double)infos->duree ) / 8.0;
	}
	DBG( 'b',fprintf(stderr,"duree: %d audioSize: %f dvdAudioSize: %lld\n", infos->duree, audioSize, dvdAudioSize ););
	overHead = profile->fileSize * profile->overhead; /* Apply container overhead */
	videoSize = profile->fileSize - audioSize - overHead;
	bitrate = ( videoSize * 8.0 ) / (double)infos->duree;
	bitrate /= 1000;
	infos->bitrate = rint(bitrate);

	DBG( 'b',fprintf(stderr,"Bitrate: %dK\n", infos->bitrate ););
	*infos->cropParam = 0;
	CropDetection( infos->vobFile, infos->cropParam, 60, 0 );
/* Parse croping params */
	if( sscanf( infos->cropParam, "%d:%d:%d:%d", &cx,&cy,&dx,&dy ) != 4 ) {
		cx = 720;
		cy = 576;
	}
	GetPictureSizes( noPisteVideo, profile, cx, cy, &infos->scaleX, &infos->scaleY, infos->bitrate );

	DBG( 'b',fprintf(stderr,"ImageParameter: crop(%s) scale(%d,%d)\n", infos->cropParam, infos->scaleX, infos->scaleY ););
	return( 0 );
}
/***************************************************************************//**
 *	DVDTOAVI-
 * @date	2010/03/15 22:58:54
 * @author	jef-Linux
 * @brief
 *	Pas d'explication
 * @details
 *	Pas de details
 * @param[in]	Pas de detail
 * @param[out]	Pas de detail
*******************************************************************************/
int DvdToAvi( GtkWidget * forme, int noPisteVideo, AudioMap_t * audioMap, SubMap_t * subMap, int format )
{
	GtkWidget * item;
	char * tmpDir = ConfigGetString( KEY_TEMP, "/tmp" );
	char * isoDir = ConfigGetString( KEY_ISODIR, GetEnv( "HOME", "/tmp" ) );
	int lastCellF = ConfigGetInt( KEY_GENERIQUE, 0 );
	char targetDir[512];
	char vobFile[1024];
	char aviFile[1024];
	char logFile[1024];
	int i;
	char injectionFile[100];
	const gchar * text;
	int ret = 0;
	int res;
	int nbSteps;
	Profile_t * profile = GetProfile( format );
	int vobOk = 0;
	int aviDone = 0;
	long long fsize;
	int nbAudio = 0;
	Timer_t * timer = NULL;

	DBG('b',fprintf(stderr,"%s: format: %d\n", __FUNCTION__, format ););
	if( !profile )	return(-1);

	DBG('b',fprintf(stderr,"%s: useFifo: %d NumCpu(): %d KeepVob=%d\n", __FUNCTION__, profile->useFifo, GetNumCpu(), KeepVob ););
	if( profile->useFifo && GetNumCpu() >= 2 && !KeepVob) {
		return( DvdToAviFifo( forme, noPisteVideo, audioMap, subMap , format ) );
	}

/* Verify we have enought free space to extract */
	fsize = FsFree( tmpDir ) - GetVobSize( noPisteVideo, 0 );
	if( fsize < 0 ) {
		PlayError();
		MessageBoxError( _("Pas assez de place sur %s\nIl manque %s octets !"), tmpDir, LLSize2Giga( -fsize ) );
		return( -1 );
	}
/* Verify we have enought free space to encode */
	fsize = FsFree( isoDir ) - profile->fileSize;
	if( fsize < 0 ) {
		PlayError();
		MessageBoxError( _("Pas assez de place sur %s\nIl manque %s octets !"), isoDir, LLSize2Giga( -fsize ) );
		return( -1 );
	}
/* Verify that we can create a file on isodir */
	if( CanCreateFile( isoDir ) == 0 ) {
		PlayError();
		MessageBoxError( _("Pas le droit de créer de fichier dans %s !"), isoDir );
		return( -1 );
	}
/* Verify number of audio tracks selected */
	for( i = 0; i < MAX_AUDIO_TRACK; i++ ) {
		if( audioMap->audioTracks[i] ) {
			nbAudio++;
			DBG('b',fprintf(stderr,"%s: audioTrackId: %d=0x%x\n", __FUNCTION__, i, AudioStreamId( noPisteVideo, i ) ););
		}
	}
	if( nbAudio != 1 ) {
		PlayError();
		MessageBoxError( _("Sélectionnez qu'un seule piste audio !") );
		return( -1 );
	}

/* Reprise du titre du DVD */
	item = lookup_widget(GTK_WIDGET(forme), "titre" );
	text = gtk_entry_get_text( GTK_ENTRY(item) );
	strcpy( TitreDvd, text );

	FreeCellArray( &CellArray );
	BuildCells( noPisteVideo, &CellArray, lastCellF, 1 );


	InitGlobVars();
	CreateAvancement( forme );

	nbSteps = CountSelectedCells( &CellArray ) + profile->passNb;
	SetTotalSteps( nbSteps );

	DisplayAvancement( _("Copie de %d cellules.\n"), CountSelectedCells( &CellArray ) );
/*	Create empty filesystem tree */
	DisplayAvancement( _("Effacement des anciens fichiers.\n"));

	sprintf( injectionFile, "%s/vapinj.%d", tmpDir, getpid());
	sprintf( targetDir, "%s/%s", tmpDir, TitreDvd );
	sprintf( aviFile, "%s/%s.%s", isoDir, TitreDvd, profile->extension );
	sprintf( vobFile, "%s/%s.vob", tmpDir, TitreDvd );
	sprintf( logFile, "%s/%s.log", tmpDir, TitreDvd );

	if( FileExist( vobFile ) == 1 ) {
		res = MessageBoxYesNo( _("Le fichier %s existe. L'utiliser ?"), vobFile );
		if( res == 1 ) {
			vobOk = 1;
			goto doAvi;
		}
		unlink( vobFile );
	}
	OutFpVideo = fopen64( vobFile, "w" );
	if( !OutFpVideo ) {
		PlayError();
		MessageBoxError( _("Création de %s impossible !"), vobFile );
		DestroyAvancement();
		return( -1 );
	}
	timer = StartTimer();

/* Setup vaporizer */
	if( VaporizeInit( Readin, OutputFlux, 1 ) < 0 ) {
		ret = -1; goto out;
	}
/* Setup tracks & factor */
	for( i = 0; i < MAX_AUDIO_TRACK; i++ ) {
		if( audioMap->audioTracks[i] ) {
			int id = GetAudioId( noPisteVideo, i );

			DBG('b',fprintf(stderr,"%s: VaporizeAudio(%d)\n", __FUNCTION__, id ););
			VaporizeAudio( id );
		}
	}
	Mpeg2Init();
/* Must be 100% */
	VaporizeFactor( 1.0f, 0 );
	VaporizeInjection( injectionFile );

/* Copy cells */
	for( i = 0; i < CellArray.nbCells; i++ ) {
		Cell_t * c = CellArray.cells + i;

		if( CopyCell( c, targetDir ) < 0 ) {
			ret = -1;
			goto out;
		}
		if( c->selected ) NewStep();
	}

	{
		int vVideoSize;
		int vAudioSize;
		int vSubpSize;
		int vNavSize;

		VaporizeSizes( &vVideoSize, &vAudioSize, &vSubpSize, &vNavSize );

		DBG( 'b',fprintf(stderr,"VAPORIZE VIDEO SIZE: %lld\n", (long long)vVideoSize * DVD_BLOCK_LEN ););
		DBG( 'b',fprintf(stderr,"VAPORIZE AUDIO SIZE: %lld\n", (long long)vAudioSize * DVD_BLOCK_LEN ););
		DBG( 'b',fprintf(stderr,"VAPORIZE SUBP  SIZE: %lld\n", (long long)vSubpSize * DVD_BLOCK_LEN ););
		DBG( 'b',fprintf(stderr,"VAPORIZE NAV   SIZE: %lld\n", (long long)vNavSize * DVD_BLOCK_LEN ););
	}
	vobOk = 1;
out:;

	VaporizeTerm();

	if( OutFpVideo ) { fclose( OutFpVideo ); OutFpVideo = NULL; }

	Mpeg2End();

doAvi:;

/* Here calculate bitrate */
	if( !ret ) {
		char * cmdLine;
		EncodingInfos_t infos;

		HideMpegVisu();
		infos.vobFile = vobFile;
		infos.aviFile = aviFile;
		infos.logFile = logFile;
		ComputeInfos( noPisteVideo, audioMap, profile, &infos );

/* Establish Audio Track Id to support -aid mencoder option */
		for( i = 0; i < MAX_AUDIO_TRACK; i++ ) {
			if( audioMap->audioTracks[i] ) {
				infos.audioTrackId = AudioStreamId( noPisteVideo, i );
				infos.audioFormat = AudioFormat( noPisteVideo, i );
			}
		}

		ret = -1;

		if( !strcmp( profile->encTool, "ffmpeg" ) ) {
			cmdLine = ComputeCmdLine( profile, &infos, 1 );
			DBG( 'b',fprintf(stderr,"COMMANDLINE: %s\n", cmdLine ););
			DisplayAvancement( _("Encodage\n"));
			ret = ExecuteFfmpeg( cmdLine, infos.duree );
//			if( !ret )	FixAvi( aviFile );
			free( cmdLine );
		}
		if( !strcmp( profile->encTool, "mencoder" ) ) {
			int i;

			ret = 0;
			for( i = 0; i < profile->passNb; i++ ) {
				cmdLine = ComputeCmdLine( profile, &infos, i+1 );
				DBG( 'b',fprintf(stderr,"COMMANDLINE: %s\n", cmdLine ););
				DisplayAvancement( _("Encodage passe %d\n"), i+1);
				res = ExecuteMencoder( cmdLine, infos.duree );
				if( res < 0 ) {
					ret = -1;
					if( IsUserStop() && i == 0 && profile->passNb > 1 ) {
						res = MessageBoxYesNo( _("Continuer en passe 2 ?") );
						if( res == 1 ) {
							ResetUserStop();
						}
					}
				}
				free( cmdLine );
			}
		}
		aviDone = 1;
	}

	StopTimer( timer );
	DBG( 'b',fprintf(stderr,"Encoding last: %f\n", ElapsedTimer(timer) ););
	FreeTimer( timer );

	DestroyAvancement();
	FreeAll();
	if( !ret )
		PlaySuccess();
	else
		PlayError();
	if( vobOk ) {
		res = MessageBoxYesNo( _("Conserver le fichier vob pour un autre encodage ?") );
		if( res != 1 )	vobOk = 0;
	}
	if( !vobOk )	unlink( vobFile );
	if( ret == 0 && aviDone ) {
		res = MessageBoxYesNo( _("Visualiser le fichier %s ?"),  aviFile );
		if( res == 1 )	VisuAvi( aviFile );
	}
	if( ret == -1 && aviDone ) {
		res = MessageBoxYesNo( _("Conserver le fichier %s ?"),  aviFile );
		if( res != 1 )	unlink( aviFile );
	}
	unlink( logFile );
	return( ret );
}
/***************************************************************************//**
 *	VISUAVI-
 * @date	2010/01/27 21:55:54
 * @author	jef-Linux
 * @brief
 *	Pas d'explication
 * @details
 *	Pas de details
 * @param[in]	Pas de detail
 * @param[out]	Pas de detail
*******************************************************************************/

static void VisuAvi( path )
char * path;
{
	char * player = ConfigGetString( KEY_PLAYER, "xine" );
	char sysCmd[1024];
	int res;

	if( !strcmp( player, "xine" )) {
		sprintf( sysCmd, "xine \"file://%s\"", path );
		DBG('c',fprintf(stderr,"%s: executing (%s)\n", __FUNCTION__, sysCmd ););
		res = system( sysCmd );
		return;
	}
	if( !strcmp( player, "mplayer" )) {
		sprintf( sysCmd, "mplayer \"%s\"", path );
		DBG('c',fprintf(stderr,"%s: executing (%s)\n", __FUNCTION__, sysCmd ););
		res = system( sysCmd );
		return;
	}
	if( !strcmp( player, "vlc" )) {
		sprintf( sysCmd, "vlc \"%s\"", path );
		DBG('c',fprintf(stderr,"%s: executing (%s)\n", __FUNCTION__, sysCmd ););
		res = system( sysCmd );
		return;
	}
}

/***************************************************************************//**
 *	OUTPUTFLUXFIFO-
 * @date	2010/02/03 22:30:57
 * @author	jef-Linux
 * @brief
 *	Pas d'explication
 * @details
 *	Pas de details
 * @param[in]	Pas de detail
 * @param[out]	Pas de detail
*******************************************************************************/
static int OutputFluxFifo( void * parm, unsigned char * buffer, int size )
{
	if( size >= DVD_VIDEO_LB_LEN ) {
		if( !isNavPack( buffer ) ) {
			Mpeg2Demux( buffer, DVD_VIDEO_LB_LEN );
		}
	}
	if( FlusherWrite( buffer, size ) < 0 )	return( -1 );
	OutSize += size;
	TotalSize += size;
	SetTotalSize( TotalSize );
	return( size );
}

/***************************************************************************//**
 *	DVDTOAVIFIFO-
 * @date	2010/02/02 20:41:35
 * @author	jef-Linux
 * @brief
 *	Idem DvdToAvi but with system fifo
 * @details
 *	Pas de details
 * @param[in]	Pas de detail
 * @param[out]	Pas de detail
*******************************************************************************/

static int DvdToAviFifo( GtkWidget * forme, int noPisteVideo, AudioMap_t * audioMap, SubMap_t * subMap, int format )
{
	GtkWidget * item;
	char * tmpDir = ConfigGetString( KEY_TEMP, "/tmp" );
	char * isoDir = ConfigGetString( KEY_ISODIR, GetEnv( "HOME", "/tmp" ) );
	int lastCellF = ConfigGetInt( KEY_GENERIQUE, 0 );
	char targetDir[512];
	char vobFile[1024];
	char aviFile[1024];
	char logFile[1024];
	EncodingInfos_t infos;
	char * cmdLine;
	int i;
	char injectionFile[100];
	const gchar * text;
	int ret = 0;
	int res;
	int nbSteps;
	Profile_t * profile = GetProfile( format );
	long long fsize;
	int nbAudio = 0;
	Timer_t * timer = NULL;
	int bufferSize;
	long long memSize;
	int sectorsRead;

	DBG('b',fprintf(stderr,"%s: format: %d\n", __FUNCTION__, format ););
	if( !profile )	return(-1);
	if( profile->passNb != 1 )	return( -1 ); /* Multi-pass encoding not supported (profile.c error) */

/* Verify we have enought free space to extract thirst cell (crop detection)*/
	fsize = FsFree( tmpDir ) - GetVobSize( noPisteVideo, 1 );
	if( fsize < 0 ) {
		PlayError();
		MessageBoxError( _("Pas assez de place sur %s\nIl manque %s octets !"), tmpDir, LLSize2Giga( -fsize ) );
		return( -1 );
	}
/* Verify we have enought free space to encode */
	fsize = FsFree( isoDir ) - profile->fileSize;
	if( fsize < 0 ) {
		PlayError();
		MessageBoxError( _("Pas assez de place sur %s\nIl manque %s octets !"), isoDir, LLSize2Giga( -fsize ) );
		return( -1 );
	}
/* Verify that we can create a file on isodir */
	if( CanCreateFile( isoDir ) == 0 ) {
		PlayError();
		MessageBoxError( _("Pas le droit de créer de fichier dans %s !"), isoDir );
		return( -1 );
	}
/* Verify number of audio tracks selected */
	for( i = 0; i < MAX_AUDIO_TRACK; i++ ) {
		if( audioMap->audioTracks[i] ) {
			nbAudio++;
			DBG('b',fprintf(stderr,"%s: audioTrackId: %d=0x%x\n", __FUNCTION__, i, AudioStreamId( noPisteVideo, i ) ););
		}
	}
	if( nbAudio != 1 ) {
		PlayError();
		MessageBoxError( _("Sélectionnez qu'un seule piste audio !") );
		return( -1 );
	}

/* Reprise du titre du DVD */
	item = lookup_widget(GTK_WIDGET(forme), "titre" );
	text = gtk_entry_get_text( GTK_ENTRY(item) );
	strcpy( TitreDvd, text );

	FreeCellArray( &CellArray );
	BuildCells( noPisteVideo, &CellArray, lastCellF, 1 );

	InitGlobVars();

	CreateAvancement( forme );

	nbSteps = CountSelectedCells( &CellArray )  + 1 /* For crop detection */;
	SetTotalSteps( nbSteps );

	DisplayAvancement( _("Copie de %d cellules.\n"), CountSelectedCells( &CellArray ) );
/*	Create empty filesystem tree */
	DisplayAvancement( _("Effacement des anciens fichiers.\n"));

	sprintf( injectionFile, "%s/vapinj.%d", tmpDir, getpid());
	sprintf( targetDir, "%s/%s", tmpDir, TitreDvd );
	sprintf( aviFile, "%s/%s.%s", isoDir, TitreDvd, profile->extension );
	sprintf( vobFile, "%s/%s.vob", tmpDir, TitreDvd );
	sprintf( logFile, "%s/%s.log", tmpDir, TitreDvd );

	timer = StartTimer();

/* Rip first chapter in order to calculate crop parameters */
	unlink( vobFile ); /* In case of previous failure in encoding stage */
	OutFpVideo = fopen64( vobFile, "w" );
	if( !OutFpVideo ) {
		PlayError();
		MessageBoxError( _("Création de %s impossible !"), vobFile );
		DestroyAvancement();
		return( -1 );
	}

/* Setup vaporizer */
	if( VaporizeInit( Readin, OutputFlux, 1 ) < 0 ) {
		ret = -1; goto out;
	}
/* Setup tracks & factor */
	for( i = 0; i < MAX_AUDIO_TRACK; i++ ) {
		if( audioMap->audioTracks[i] ) {
			int id = GetAudioId( noPisteVideo, i );

			DBG('b',fprintf(stderr,"%s: VaporizeAudio(%d)\n", __FUNCTION__, id ););
			VaporizeAudio( id );
		}
	}
	Mpeg2Init();
/* Must be 100% */
	VaporizeFactor( 1.0f, 0 );
	VaporizeInjection( injectionFile );

/* Copy enought cells to do croping detection */
	sectorsRead = 0;
	for( i = 0; i < CellArray.nbCells; i++ ) {
		Cell_t * c = CellArray.cells + i;

		if( CopyCell( c, targetDir ) < 0 ) {
			ret = -1;
			goto out;
		}
		if( c->selected ) {
			c->done = 0;
			sectorsRead += SECTOR_SZ( c );
			if( sectorsRead >= MIN_SECTORS4CROP )
				break;
		}
	}
	NewStep();

	{
		int vVideoSize;
		int vAudioSize;
		int vSubpSize;
		int vNavSize;

		VaporizeSizes( &vVideoSize, &vAudioSize, &vSubpSize, &vNavSize );

		DBG( 'b',fprintf(stderr,"VAPORIZE VIDEO SIZE: %lld\n", (long long)vVideoSize * DVD_BLOCK_LEN ););
		DBG( 'b',fprintf(stderr,"VAPORIZE AUDIO SIZE: %lld\n", (long long)vAudioSize * DVD_BLOCK_LEN ););
		DBG( 'b',fprintf(stderr,"VAPORIZE SUBP  SIZE: %lld\n", (long long)vSubpSize * DVD_BLOCK_LEN ););
		DBG( 'b',fprintf(stderr,"VAPORIZE NAV   SIZE: %lld\n", (long long)vNavSize * DVD_BLOCK_LEN ););
	}
out:;

	VaporizeTerm();

	if( OutFpVideo ) { fclose( OutFpVideo ); OutFpVideo = NULL; }

	Mpeg2End();

/* Here calculate bitrate */
	if( !ret ) {

		infos.vobFile = vobFile;
		infos.aviFile = aviFile;
		infos.logFile = logFile;
		ComputeInfos( noPisteVideo, audioMap, profile, &infos );

/* Establish Audio Track Id to support -aid mencoder option */
		for( i = 0; i < MAX_AUDIO_TRACK; i++ ) {
			if( audioMap->audioTracks[i] ) {
				infos.audioTrackId = AudioStreamId( noPisteVideo, i );
				infos.audioFormat = AudioFormat( noPisteVideo, i );
			}
		}
	}
	unlink( vobFile );

	if( ret == -1 ) {
		DestroyAvancement();
		FreeAll();
		PlayError();
		return( ret );
	}

/* Ok then we can proceed with fifo method */
/* Create Fifo file */
	ret = mkfifo( vobFile, 0666 );
	if( ret == -1 ) {
		DBG( 'b',fprintf(stderr,"mkfifo(%s) failed errno: %d\n", vobFile, errno ););
		DestroyAvancement();
		FreeAll();
		PlayError();
		return( ret );
	}
/* Build Cmd line and launch encoder */
	cmdLine = ComputeCmdLine( profile, &infos, 1 );
	strcat( cmdLine, " > /dev/null &" );
	DBG( 'b',fprintf(stderr,"COMMAND LINE(%s)\n", cmdLine ););
	system( cmdLine );
/* Copy cells */
	OutFpVideo = fopen64( vobFile, "w" );
	if( !OutFpVideo ) {
		unlink( vobFile );
		PlayError();
		MessageBoxError( _("Création de %s impossible !"), vobFile );
		DestroyAvancement();
		return( -1 );
	}
	InitGlobVars();
/* Ok we need to mask DVD latency, so having to allocate big buffers, so just verify that whe have enought RAM to do this */

	memSize = CpuPhysMemSize();
	if( memSize >= MEM_2GIG )	bufferSize = 200; /* 200 mo buffering */
	else {
		if( memSize >= MEM_1GIG )	bufferSize = 100; /* 100 mo buffering */
		else				bufferSize = 50; /* 50 mo buffering */
	}
	DBG( 'b',fprintf(stderr,"FlusherStart: bufferSize=%d\n", bufferSize ););

	if( FlusherStart( OutFpVideo, bufferSize ) < 0 ) {
		ret = -1; goto out2;
	}
/* Setup vaporizer */
	if( VaporizeInit( Readin, OutputFluxFifo, 1 ) < 0 ) {
		ret = -1; goto out2;
	}
/* Setup tracks */
	for( i = 0; i < MAX_AUDIO_TRACK; i++ ) {
		if( audioMap->audioTracks[i] ) {
			int id = GetAudioId( noPisteVideo, i );

			DBG('b',fprintf(stderr,"%s: VaporizeAudio(%d)\n", __FUNCTION__, id ););
			VaporizeAudio( id );
		}
	}
	Mpeg2Init();
/* Must be 100% */
	VaporizeFactor( 1.0f, 0 );
	VaporizeInjection( injectionFile );
/* Copy each cell */
	for( i = 0; i < CellArray.nbCells; i++ ) {
		Cell_t * c = CellArray.cells + i;

		if( CopyCell( c, targetDir ) < 0 ) {
			ret = -1;
			goto out2;
		}
		if( c->selected ) NewStep();
	}

	{
		int vVideoSize;
		int vAudioSize;
		int vSubpSize;
		int vNavSize;

		VaporizeSizes( &vVideoSize, &vAudioSize, &vSubpSize, &vNavSize );

		DBG( 'b',fprintf(stderr,"VAPORIZE VIDEO SIZE: %lld\n", (long long)vVideoSize * DVD_BLOCK_LEN ););
		DBG( 'b',fprintf(stderr,"VAPORIZE AUDIO SIZE: %lld\n", (long long)vAudioSize * DVD_BLOCK_LEN ););
		DBG( 'b',fprintf(stderr,"VAPORIZE SUBP  SIZE: %lld\n", (long long)vSubpSize * DVD_BLOCK_LEN ););
		DBG( 'b',fprintf(stderr,"VAPORIZE NAV   SIZE: %lld\n", (long long)vNavSize * DVD_BLOCK_LEN ););
	}
out2:;

	VaporizeTerm();

	if( FlusherStop() < 0 )	ret = -1;

	if( OutFpVideo ) { fclose( OutFpVideo ); OutFpVideo = NULL; }

	Mpeg2End();
/* Finish */
	free( cmdLine );
	unlink( vobFile );
	DestroyAvancement();
	FreeAll();

	StopTimer( timer );
	DBG( 'b',fprintf(stderr,"Encoding last: %f\n", ElapsedTimer(timer) ););
	FreeTimer( timer );

	if( !ret )
		PlaySuccess();
	else
		PlayError();
	if( ret == 0 ) {
		res = MessageBoxYesNo( _("Visualiser le fichier %s ?"),  aviFile );
		if( res == 1 )	VisuAvi( aviFile );
	}
	unlink( logFile );
	return( ret );
}
