ref: afc79ed1e5e7cfa86a0dd4489a2fbc6ca6186bc5
dir: /wingui/EncoderJobProcessingManager.cpp/
// EncoderJobProcessingManager.cpp: implementation of the CEncoderJobProcessingManager class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "faac_wingui.h"
#include "EncoderJobProcessingManager.h"
#include "WindowUtil.h"
#include "RecursiveDirectoryTraverser.h"
#include <sndfile.h>
#include "faac.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//#define DUMMY_ENCODERJOB_PROCESSING
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CEncoderJobProcessingManager::CEncoderJobProcessingManager(
	CEncoderJob *poJobToProcess, CJobProcessingDynamicUserInputInfo &oUserInputInfo):
	m_poJobToProcess(poJobToProcess),
	m_poInfoTarget(0),
	m_eCurrentWorkingState(eInitial),
	m_oUserInputInfo(oUserInputInfo)
{
}
CEncoderJobProcessingManager::~CEncoderJobProcessingManager()
{
}
bool CEncoderJobProcessingManager::MayStartProcessingWithStatusDialog()
{
	long lStartTimeMillis=::GetTickCount();
	CEncoderJob *poJob=m_poJobToProcess;
	// make sure the target directory exists at all
	{
		CString oTargetFileDir(poJob->GetFiles().GetTargetFileDirectory());
		if (oTargetFileDir.GetLength()<1)
		{
			CString oError;
			oError.LoadString(IDS_InvalidTargetDirectory);
			poJob->SetProcessingOutcomeCurTime(CAbstractJob::eError, lStartTimeMillis, oError);
			return false;
		}
		
		while (oTargetFileDir.GetAt(oTargetFileDir.GetLength()-1)=='\\')
		{
			oTargetFileDir.Delete(oTargetFileDir.GetLength()-1);
		}
		if (CRecursiveDirectoryTraverser::CountMatchingFiles(oTargetFileDir)<1)
		{
			// the target directory doesn't exist;
			// there are two possibilities: create it
			// or
			// abort with an error
			CString oTargetDir(poJob->GetFiles().GetTargetFileDirectory());
			if (m_oUserInputInfo.GetAutoCreateDirectoryBool(oTargetDir))
			{
				if (!CRecursiveDirectoryTraverser::MakeSureDirectoryExists(oTargetDir))
				{
					// directory couldn't be created;
					// log the error
					CString oError;
					oError.Format(IDS_ErrorCreatingDirectory, poJob->GetFiles().GetTargetFileDirectory());
					poJob->SetProcessingOutcomeCurTime(CAbstractJob::eError, lStartTimeMillis, oError);
					return false;
				}
			}
			else
			{
				// the directory doesn't exist and the user refused to create it
				poJob->SetProcessingOutcomeCurTime(CAbstractJob::eError, lStartTimeMillis, IDS_UserRefusedToCreateTargetDirectory);
				return false;
			}
		}
		else
		{
			// the directory already exists so everything is ok up to here
		}
	}
	return true;
}
void CEncoderJobProcessingManager::Start(
	CProcessingStatusDialogInfoFeedbackCallbackInterface *poInfoTarget)
{
	if (poInfoTarget!=0)
	{
		m_poInfoTarget=poInfoTarget;
	}
	switch (m_eCurrentWorkingState)
	{
	case eInitial:
		{
			// initialize the status dialog
			{
				// define supported buttons
				m_poInfoTarget->SetAvailableActions(true, false);
				// set the dialog caption
				m_poInfoTarget->SetAdditionalCaptionInfo(m_poJobToProcess->GetJobProcessingAdditionalCaptionBarInformation());
			}
			// initialize ourselves and run the job
			m_eCurrentWorkingState=eRunning;
			m_poInfoTarget->ReturnToCaller(DoProcessing());
			break;
		}
	case ePaused:
		{
			m_eCurrentWorkingState=eRunning;
			break;
		}
	default:
		{
			// call to Start() is invalid except in the above two cases
			ASSERT(false);
		}
	}
}
void CEncoderJobProcessingManager::Stop()
{
	switch (m_eCurrentWorkingState)
	{
	case eRunning:
	case ePaused:
		{
			m_eCurrentWorkingState=eStopped;
			break;
		}
	case eCleanup:
		{
			// ignore
			break;
		}
	default:
		{
			// call to Stop() is invalid except in the above two cases
			ASSERT(false);
		}
	}
}
void CEncoderJobProcessingManager::Pause()
{
	switch (m_eCurrentWorkingState)
	{
	case eRunning:
		{
			m_eCurrentWorkingState=ePaused;
			break;
		}
	default:
		{
			// call to Pause() is invalid except in the above case
			ASSERT(false);
		}
	}
}
#ifdef DUMMY_ENCODERJOB_PROCESSING
bool CEncoderJobProcessingManager::DoProcessing()
{
	long lStartTimeMillis=::GetTickCount();
	const CEncoderJob *poJob=m_poJobToProcess;
	long lMaxCount=250*64000/(5+poJob->GetBitRate());
	for (long lPos=0; lPos<lMaxCount; lPos++)
	{
		long lMultiplicationDummy;
		for (long lPosInner=0; lPosInner<10000000; lPosInner++)
		{
			// just a pause to simulate work
			lMultiplicationDummy=234985;
			lMultiplicationDummy*=301872;
		}
		switch (m_eCurrentWorkingState)
		{
		case eRunning:
			{
				// just report our current state
				WriteProgress(lStartTimeMillis, lMaxCount, lPos);
				m_poInfoTarget->ProcessUserMessages();
				break;
			}
		case ePaused:
			{
				// must wait
				while (m_eCurrentWorkingState==ePaused)
				{
					// be idle
					m_poInfoTarget->ProcessUserMessages();
					Sleep(200);
				}
				break;
			}
		case eStopped:
			{
				// must interrupt
				poJob->SetProcessingOutcomeCurTime(CAbstractJob::eUserAbort, lStartTimeMillis, "");
				return false;
			}
		}
	}
	m_eCurrentWorkingState=eCleanup;
	poJob->SetProcessingOutcomeCurTime(CAbstractJob::eSuccessfullyProcessed, lStartTimeMillis, "");
	return true;
}
#else
bool CEncoderJobProcessingManager::DoProcessing()
{
	long lStartTimeMillis=::GetTickCount();
	CEncoderJob *poJob=m_poJobToProcess;
	bool bInterrupted=false;
#ifdef _DEBUG
	// make sure preprocessing has been done properly to warn the programmer if not
	if (!MayStartProcessingWithStatusDialog())
	{
		// you should call MayStartProcessingWithStatusDialog() before starting
		// a status dialog driven processing of the job
		ASSERT(false);
	}
#endif
	SNDFILE *phInFile;
	SF_INFO sctSfInfo;
	// open the input file
	if ((phInFile=sf_open_read(poJob->GetFiles().GetCompleteSourceFilePath(), &sctSfInfo)) != NULL)
	{
		// determine input file parameters
		long lSampleRate=sctSfInfo.samplerate;
		long lNumChannels=sctSfInfo.channels;
		// open and setup the encoder
		unsigned long ulInputSamplesPerLoopCycle;
		unsigned long ulMaxLoopCycleOutputSize;
		faacEncHandle hEncoder=faacEncOpen(lSampleRate, lNumChannels, &ulInputSamplesPerLoopCycle, &ulMaxLoopCycleOutputSize);
		if (hEncoder!=0)
		{
			// set encoder configuration
			faacEncConfigurationPtr pEncConfig=faacEncGetCurrentConfiguration(hEncoder);
			pEncConfig->allowMidside = poJob->GetAllowMidside() ? 1 : 0;
			pEncConfig->useTns = poJob->GetUseTns() ? 1 : 0;
			pEncConfig->useLtp = poJob->GetUseLtp() ? 1 : 0;
			pEncConfig->useLfe = poJob->GetUseLfe() ? 1 : 0;
			pEncConfig->bitRate = poJob->GetBitRate();
			pEncConfig->bandWidth = poJob->GetBandwidth();
			pEncConfig->aacProfile = GetAacProfileConstant(poJob->GetAacProfile());
			if (!faacEncSetConfiguration(hEncoder, pEncConfig))
			{
				faacEncClose(hEncoder);
				sf_close(phInFile);
				poJob->SetProcessingOutcomeCurTime(CAbstractJob::eError, lStartTimeMillis, IDS_FaacEncSetConfigurationFailed);
				return false;
			}
			// open the output file
			CFile *poFile;
			CArchive *poTargetFileOutputArchive;
			if (OpenOutputFileArchive(poJob->GetFiles().GetCompleteTargetFilePath(), poFile, poTargetFileOutputArchive))
			{
				long lStartTime=GetTickCount();
				long lLastUpdated=0;
				long lTotalBytesRead = 0;
				long lSamplesInput=0;
				long lBytesConsumed=0;
				short *parsPcmBuf;
				char *parcBitBuf;
				parsPcmBuf=new short[ulInputSamplesPerLoopCycle];
				parcBitBuf=new char[ulMaxLoopCycleOutputSize];
				while (true)
				{
					long lBytesWritten;
					lSamplesInput=sf_read_short(phInFile, parsPcmBuf, ulInputSamplesPerLoopCycle);
					
					lTotalBytesRead+=lSamplesInput*sizeof(short);
					// call the actual encoding routine
					lBytesWritten=faacEncEncode(
						hEncoder,
						parsPcmBuf,
						lSamplesInput,
						parcBitBuf,
						ulMaxLoopCycleOutputSize);
					switch (m_eCurrentWorkingState)
					{
					case eRunning:
						{
							// just report our current state and process waiting window messages
							WriteProgress(lStartTimeMillis, (sctSfInfo.samples*sizeof(short)*lNumChannels), lTotalBytesRead);
							m_poInfoTarget->ProcessUserMessages();
							break;
						}
					case ePaused:
						{
							// must wait
							while (m_eCurrentWorkingState==ePaused)
							{
								// be idle
								m_poInfoTarget->ProcessUserMessages();
								Sleep(200);
							}
							break;
						}
					case eStopped:
						{
							// must interrupt
							bInterrupted=true;
							break;
						}
					}
					
					if (bInterrupted) 
					{
						// Stop Pressed
						poJob->SetProcessingOutcomeCurTime(CAbstractJob::eUserAbort, lStartTimeMillis, "");
						break;
					}
					if (lSamplesInput==0 && lBytesWritten==0)
					{
						// all done, bail out
						poJob->SetProcessingOutcomeCurTime(CAbstractJob::eSuccessfullyProcessed, lStartTimeMillis, "");
						break;
					}
					if (lBytesWritten < 0)
					{
						poJob->SetProcessingOutcomeCurTime(CAbstractJob::eError, lStartTimeMillis, IDS_FaacEncEncodeFrameFailed);
						bInterrupted=true;
						break;
					}
					poTargetFileOutputArchive->Write(parcBitBuf, lBytesWritten);
				}
				// close the target file
				if (poTargetFileOutputArchive!=0) delete poTargetFileOutputArchive;
				if (poFile!=0) delete poFile;
				if (parsPcmBuf!=0) delete[] parsPcmBuf;
				if (parcBitBuf!=0) delete[] parcBitBuf;
			}
			faacEncClose(hEncoder);
		}
		sf_close(phInFile);
		//MessageBeep(1);		// no more done here
	}
	else
	{
		poJob->SetProcessingOutcomeCurTime(CAbstractJob::eError, lStartTimeMillis, IDS_CouldntOpenInputFile);
		bInterrupted=true;
	}
	return !bInterrupted;
}
#endif
void CEncoderJobProcessingManager::WriteProgress(long lOperationStartTickCount, long lMaxSteps, long lCurSteps)
{
	long lCurTime=::GetTickCount();
	double dProgress=100.*lCurSteps/lMaxSteps;
	if (dProgress>100) dProgress=100;		// just security
	if (dProgress<0) dProgress=0;			// just security
	CString oTopStatusText=m_poJobToProcess->GetDetailedDescriptionForStatusDialog();
	long lElapsedTime=lCurTime-lOperationStartTickCount;
	long lEstimateEntireTime=(long)((100.*lElapsedTime)/dProgress);
	long lETA=lEstimateEntireTime-lElapsedTime;
	CString oElapsedTime(CWindowUtil::GetTimeDescription(lElapsedTime));
	CString oEntireTime(CWindowUtil::GetTimeDescription(lEstimateEntireTime));
	CString oETA(CWindowUtil::GetTimeDescription(lETA));
	CString oBottomStatusText;
	oBottomStatusText.Format("%.1f %%\n\n%s / %s   -   %s", dProgress, oElapsedTime, oEntireTime, oETA);
	m_poInfoTarget->SetStatus(dProgress, oTopStatusText, oBottomStatusText);
}
int CEncoderJobProcessingManager::GetAacProfileConstant(CEncoderJob::EAacProfile eAacProfile)
{
	switch (eAacProfile)
	{
	case CEncoderJob::eAacProfileLc:
		{
			return LOW;
		}
	default:
		{
			ASSERT(false);
		}
	case CEncoderJob::eAacProfileMain:
		{
			return MAIN;
		}
	case CEncoderJob::eAacProfileSsr:
		{
			return SSR;
		}
	}
}
bool CEncoderJobProcessingManager::OpenOutputFileArchive(const CString &oFileName, CFile* &poFile, CArchive* &poArchive)
{
	try
	{
		poFile=0;
		poArchive=0;
		// open the file
		poFile=new CFile(oFileName, CFile::modeCreate | CFile::modeWrite | CFile::shareDenyWrite);
		poArchive=new CArchive(poFile, CArchive::store);
		return true;
	}
	catch (...)
	{
		// error opening the file for exclusive writing
		if (poArchive!=0)
		{
			delete poArchive;
		}
		if (poFile!=0)
		{
			delete poFile;
		}
		return false;
	}
}