shithub: aacenc

ref: f451c32474504a0329b572f436f8eacd904fba9c
dir: /wingui/EncoderJobProcessingManager.cpp/

View raw version
// EncoderJobProcessingManager.cpp: implementation of the CEncoderJobProcessingManager class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "faac_wingui.h"
#include "EncoderJobProcessingManager.h"
#include "WindowUtil.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(const CEncoderJob *poJobToProcess):
	m_poJobToProcess(poJobToProcess),
	m_poInfoTarget(0),
	m_eCurrentWorkingState(eInitial)
{
}

CEncoderJobProcessingManager::~CEncoderJobProcessingManager()
{
}

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();

	long lMaxCount=250*64000/(5+m_poJobToProcess->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
				return false;
			}
		}
	}

	m_eCurrentWorkingState=eCleanup;

	return true;
}

#else

bool CEncoderJobProcessingManager::DoProcessing()
{
	long lStartTimeMillis=::GetTickCount();
	const CEncoderJob *poJob=m_poJobToProcess;
	bool bInterrupted=false;

	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);

				AfxMessageBox("faacEncSetConfiguration failed!", MB_OK | MB_ICONSTOP);

				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
						break;
					}

					if (lSamplesInput==0 && lBytesWritten==0)
					{
						// all done, bail out
						break;
					}

					if (lBytesWritten < 0)
					{
						AfxMessageBox("faacEncEncodeFrame failed!", MB_OK | MB_ICONSTOP);
						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
	{
		AfxMessageBox("Couldn't open input file!", MB_OK | MB_ICONSTOP);
		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)((double)(lElapsedTime)/(dProgress/100));
	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;
	}
}