/* ***** BEGIN LICENSE BLOCK ***** 
 * Version: RCSL 1.0/RPSL 1.0 
 *  
 * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. 
 *      
 * The contents of this file, and the files included with this file, are 
 * subject to the current version of the RealNetworks Public Source License 
 * Version 1.0 (the "RPSL") available at 
 * http://www.helixcommunity.org/content/rpsl unless you have licensed 
 * the file under the RealNetworks Community Source License Version 1.0 
 * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, 
 * in which case the RCSL will apply. You may also obtain the license terms 
 * directly from RealNetworks.  You may not use this file except in 
 * compliance with the RPSL or, if you have a valid RCSL with RealNetworks 
 * applicable to this file, the RCSL.  Please see the applicable RPSL or 
 * RCSL for the rights, obligations and limitations governing use of the 
 * contents of the file.  
 *  
 * This file is part of the Helix DNA Technology. RealNetworks is the 
 * developer of the Original Code and owns the copyrights in the portions 
 * it created. 
 *  
 * This file, and the files included with this file, is distributed and made 
 * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
 * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS 
 * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
 * 
 * Technology Compatibility Kit Test Suite(s) Location: 
 *    http://www.helixcommunity.org/content/tck 
 * 
 * Contributor(s): 
 *  
 * ***** END LICENSE BLOCK ***** */

#include "ihxtprofile.h"
#ifdef _MAC_UNIX
#include <Carbon/Carbon.h>
#endif
#include "hxcom.h"
#include "encoder.h"

#include "logobserver.h"

// standard includes
#include <iostream>
#include <assert.h>
#include <string.h>
using namespace std;

#ifdef _WINDOWS
#include <direct.h>
#include "winpreview.h"
#elif defined _LINUX
#define _snprintf snprintf
#include <unistd.h>
#include <dlfcn.h>
#include <signal.h>
#elif defined _MAC_UNIX
#define _snprintf snprintf
#endif
//#define _DO_EVENTS

const UINT32 MAX_BUFFER_SIZE = 255;

BOOL GetChosenStream( IHXTEncodingJob * pIJob, IHXTAudience *pIRTAAudience, const char* pszStreamType,IHXTStreamConfig ** ppIRTAStreamConfig);

// Sample Preview sing
class CSamplePreviewSink :
public IHXTPreviewSink
{
public:
    CSamplePreviewSink(
	UINT32 nPreviewPosition, 
	UINT nPreviewIndex,
	const char* szMediaFormat, 
	UINT32 nColorFormat = HXT_VIDEO_FORMAT_BGR24_INVERTED,
	UINT32 nVideoWidth = 160,
	UINT32 nVideoHeight = 120,
	INT32 nOutputProfile = -1,
	INT32 nAudience = -1)
	: m_ulRefCount(0)
	, m_nPreviewPosition(nPreviewPosition)
	, m_nPreviewIndex(nPreviewIndex)
	, m_pszMediaFormat(NULL)
	, m_nColorFormat(nColorFormat)
#ifdef _WIN32
	, m_pWinPreview(NULL)
#endif
	, m_nOutputProfile(nOutputProfile)
	, m_nAudience(nAudience)
    {
	UINT32 nSize = strlen(szMediaFormat) + 1;
	m_pszMediaFormat = new char[nSize];
	strncpy(m_pszMediaFormat, szMediaFormat, nSize);
	
	if(strcmp(szMediaFormat, kValueMediaFormatUncompVideo) == 0)
	{
	    if(m_nColorFormat == HXT_VIDEO_FORMAT_BGR24_INVERTED || 
		m_nColorFormat == HXT_VIDEO_FORMAT_BGR24_NONINVERTED)
	    {
#ifdef _WIN32
		m_pWinPreview = new CWinPreview();
		if(m_pWinPreview)
		{
		    m_pWinPreview->CreatePreviewWindow(nPreviewIndex, nVideoWidth, nVideoHeight);
		}
#endif
	    }
	    else
	    {
		fprintf(stderr, "INFORMATIONAL:  Unable to create the video preview window. The format must be RGB24 or RGB24 Inverted to display in the video window.\n");
	    }
	}
    }
    
    ~CSamplePreviewSink()
    {
	HX_VECTOR_DELETE(m_pszMediaFormat);
	
#ifdef _WIN32
	HX_DELETE(m_pWinPreview);
#endif
    }
    
    UINT32 GetPreviewSinkPosition() { return m_nPreviewPosition; };
    INT32 GetOutputProfile() { return m_nOutputProfile; };
    INT32 GetAudience() { return m_nAudience; };
    
    //IUnknown methods
    STDMETHOD_( ULONG32, AddRef )()
    {
	return InterlockedIncrement(&m_ulRefCount);
    }
    STDMETHOD_( ULONG32, Release )()
    {
	if (InterlockedDecrement(&m_ulRefCount) > 0)
	{
	    return m_ulRefCount;
	}
	
	delete this;
	return 0;
    }
    STDMETHOD( QueryInterface )( REFIID riid, void** ppvObj )
    {
	if(!ppvObj)
	{
	    return HXR_POINTER;				
	}
	
	if(IsEqualIID(IID_IHXTPreviewSink, riid))			
	{							
	    AddRef();					
	    *ppvObj = (IHXTPreviewSink*)this;
	    return HXR_OK;					
	}
	
	return HXR_FAIL;
    }
    
    //	IHXTPreviewSink methods
    STDMETHOD(OnFormatChanged)(THIS_ IHXTPropertyBag* pProps)
    {
	HX_RESULT res = HXR_OK;
	
	if(strcmp(m_pszMediaFormat, kValueMediaFormatUncompAudio) == 0)
	{
	    UINT32 nSampleRate = 0;
	    UINT32 nSampleFormat = 0;
	    UINT32 nChannelFormat = 0;
	    
	    pProps->GetUint(kPropAudioSampleRate, &nSampleRate);
	    pProps->GetUint(kPropAudioSampleFormat, &nSampleFormat);
	    pProps->GetUint(kPropAudioChannelFormat, &nChannelFormat);
	    
	    fprintf(stdout, "Preview %s:%d:  OnFormatChanged(samplerate=%d, sampleformat=%d, channelformat=%d)\n", 
		m_pszMediaFormat, m_nPreviewIndex,
		nSampleRate, nSampleFormat, nChannelFormat);
	}
	else if(strcmp(m_pszMediaFormat, kValueMediaFormatUncompVideo) == 0)
	{
	    UINT32 nColorFormat = 0;
	    UINT32 nVideoWidth = 0;
	    UINT32 nVideoHeight = 0;
	    
	    pProps->GetUint(kPropVideoColorFormat, &nColorFormat);
	    pProps->GetUint(kPropVideoFrameWidth, &nVideoWidth);
	    pProps->GetUint(kPropVideoFrameHeight, &nVideoHeight);
	    
	    fprintf(stdout, "Preview %s:%d:  OnFormatChanged(format=%d, videowidth=%d, videoheight=%d)\n", 
		m_pszMediaFormat, m_nPreviewIndex,
		nColorFormat, nVideoWidth, nVideoHeight);
	    
#ifdef _WIN32
	    m_pWinPreview->FreeWindow();
	    if(m_nColorFormat == HXT_VIDEO_FORMAT_BGR24_INVERTED || 
		m_nColorFormat == HXT_VIDEO_FORMAT_BGR24_NONINVERTED)
	    {
#ifdef _WIN32
		m_pWinPreview->CreatePreviewWindow(m_nPreviewIndex, nVideoWidth, nVideoHeight); 
#endif
	    }
	    else
	    {
		fprintf(stderr, "INFORMATIONAL:  Unable to create the video preview window. The format must be RGB24 or RGB24 Inverted to display in the video window.\n");
	    }
#endif
	}
	
	return res;
    }
    
    STDMETHOD(OnSample)(THIS_ IHXTMediaSample* pSample)
    {
	HX_RESULT res = HXR_FAIL;
	
	if(pSample)
	{
	    pSample->AddRef();
	    
	    HXT_TIME tmStart;
	    HXT_TIME tmEnd;
	    pSample->GetTime(&tmStart, &tmEnd);
	    
	    UINT32 ultstart = tmStart;
	    UINT32 ultend = tmEnd;
	    //	    printf("Preview %s:%d:  OnSample start %lu, end %lu\n", 
	    //		m_pszMediaFormat, m_nPreviewIndex, ultstart, ultend);
	    
#ifdef _WIN32
	    if(m_pWinPreview)
	    {
		if(pSample->GetDataSize())
		{
		    m_pWinPreview->DrawVideo(pSample->GetDataStartForReading(), pSample->GetDataSize());
		}
	    }
#endif
	    
	    pSample->Release();	    
	    res = HXR_OK;
	}
	
	return res;
    }
    
private:
    UINT32 m_ulRefCount;
    UINT32 m_nPreviewPosition;
    UINT32 m_nPreviewIndex;
    char* m_pszMediaFormat;
    UINT32 m_nColorFormat;
    
    INT32 m_nOutputProfile;
    INT32 m_nAudience;
#ifdef _WIN32
    CWinPreview* m_pWinPreview;
#endif
};


// Sample event sink helper class -- prints out encoding status
// Registered for events in CEncoderApp::CreateJob()
class CSampleEventSink :
public IHXTEventSink
{
public:
    CSampleEventSink() {m_ulRefCount = 0;m_pencoder = NULL;}
    
    //IUnknown methods
    STDMETHOD_( ULONG32, AddRef )()
    {
	return InterlockedIncrement(&m_ulRefCount);
    }
    STDMETHOD_( ULONG32, Release )()
    {
	if (InterlockedDecrement(&m_ulRefCount) > 0)
	{
	    return m_ulRefCount;
	}
	
	delete this;
	return 0;
    }
    STDMETHOD( QueryInterface )( REFIID riid, void** ppvObj )
    {
	if(!ppvObj)
	{
	    return HXR_POINTER;				
	}
	
	if(IsEqualIID(IID_IHXTEventSink, riid))			
	{							
	    AddRef();					
	    *ppvObj = (IHXTEventSink*)this;
	    return HXR_OK;					
	}
	
	return HXR_FAIL;
    }
    
    // IHXTEventSink methods
    STDMETHOD( HandleEvent )( EHXTEvent eEvent, UINT32 *puValue, const char *cszValue, IUnknown *pUnknown )
    {
	switch(eEvent)
	{
	case (eEventEncodingStarted):
	    {
		printf("\nEncoding started..\n");
		break;
	    }
	    
	case(eEventEncodeProgress):
	    {
		if (puValue)
		{
		    printf("Encoding %ld percent complete..\n", *puValue);
		    if(m_pencoder)
		    {
			m_pencoder->UpdateStats();
			if(*puValue % 10 == 0)
			{
			    m_pencoder->TogglePreviews();
#ifdef _DO_EVENTS
			    m_pencoder->SendEvent();
			    if(*puValue >= 80)
			    {
				m_pencoder->SendEvent(TRUE);
			    }
#endif	//  _DO_EVENTS
			}
		    }
		}
		break;
	    }
	    
	case(eEventAnalysisProgress):
	    {
		if (puValue)
		    printf("Analysis %ld percent complete..\n", *puValue);
		break;
	    }
	    
	case(eEventEncodingFinished):
	    {
		printf("\nEncoding finished..\n");
		break;
	    }			

	case eEventPerformanceLoad:
	    {
		/*
		 * eEventPerformanceLoad
		 *
		 * This event is sent by the load observer to signal that the load
		 * management system has updated the target load level.  It is meant to be
		 * informational only and should not be used by load managed plugins.
		 * Plugins should use the dedicated IHXTLoadAdjustment and observers
		 * should use the IHXTLoadObserver interface.
		 */

		static bool bFirstTime = true;
		if (bFirstTime)
		{
		    printf("Received eEventPerformanceLoadUpdate event...\n");
		    printf("The system has updated the target load level.");
		    bFirstTime = false;
		}

		break;
	    }			

	case eEventPerformanceOverload:
	    {
		/*
		 * eEventPerformanceOverload
		 *
		 * This event is sent by load observer to signal that the system has been
		 * overloaded for a significant period of time.  The encoding job handles 
		 * this event by aborting the encoding process.
		 */

		static bool bFirstTime = true;
		if (bFirstTime)
		{
		    printf("Received eEventPerformanceOverload event...\n");
		    printf("The system has been overloaded for a significant period of time."
			   "It would be appropriate to abort at this point.");
		    bFirstTime = false;
		}
		break;
	    }			
	}
	
	return HXR_OK;
    }
    
    void Init(CEncoderApp *pencoder )
    {
	m_pencoder = pencoder;
    }
private:
    UINT32 m_ulRefCount;
    UINT32 m_uLastLoadLevel;
    CEncoderApp *m_pencoder;
};

CEncoderApp::CEncoderApp(istream& cInput) :
m_pFactory(NULL)
    ,m_pJob(NULL)
    ,m_pSampleLogObserver(NULL)
    ,m_pFileLogObserver(NULL)
    ,m_pEventSink(NULL)
    ,m_pLogSystem(NULL)
    ,m_RmsessionDLL(0)
    ,m_ulNumberofVideoStats(0)
    ,m_ulNumberofAudioStats(0)
    ,m_ppAudioStreamStats(NULL)
    ,m_ppVideoStreamStats(NULL)
    ,m_pCurentAudioStatsBag(NULL)
    ,m_pLifeTimeAudioStatsBag(NULL)
    ,m_pCurentVideoStatsBag(NULL)
    ,m_pLifeTimeVideoStatsBag(NULL)
    ,m_bEnableStats(FALSE)
    ,m_nPreviewSinkCount(0)
    ,m_nCurrentOutputProfile(-1)
    ,m_nCurrentAudience(-1)
    ,m_bOpenInputPreviews(FALSE)
    ,m_bOpenPrefilterPreviews(FALSE)
    ,m_bSinkEnabled(TRUE)
    ,m_bTogglePreviews(FALSE)
    ,m_pEventPin(NULL)
    ,m_bDoEvents(FALSE)
    ,m_cInput(cInput)
{
    for (UINT32 i = 0; i < MAX_PREVIEW_SINK; i++)
    {
	m_ppPreviewSink[i] = NULL;
    }
}


CEncoderApp::~CEncoderApp()
{
    
}

HX_RESULT CEncoderApp::SetupDLLPaths()
{
    HX_RESULT res = HXR_OK;
    
    cout << "Step 1: Setting up DLL Paths" << endl;
    
    // This sample app must be run from the %sdk_install_dir%\bin directory.  Applications using the
    // SDK can be run from anywhere as long as SetDLLAccessPaths is called with the location of
    // the SDK binaries.
    char szCurrentDir[MAX_BUFFER_SIZE] = "";
#ifdef _WINDOWS	
    _getcwd(szCurrentDir, MAX_BUFFER_SIZE);
#elif defined _LINUX
    getcwd(szCurrentDir, MAX_BUFFER_SIZE);
#elif defined _MAC_UNIX    
    getcwd(szCurrentDir, MAX_BUFFER_SIZE);    
#endif
    
    if (strstr(szCurrentDir, "bin") == NULL)
    {
	cout << "********************************************************************" << endl;
	cout << "*** Sample app must be run from %sdk_install_dir%\\bin directory ***" << endl;
	cout << "********************************************************************" << endl << endl;
    }
    
    // Note that if you do not call SetDLLAccessPaths, the SDK will assume the current working
    // directory matches the structure of %sdk_install_dir%\bin.  Namely, that the current working directory
    // has plugins, codecs, tools, and common subdirectories with the appropriate DLLs.  As long as this application is run
    // from the %sdk_install_dir%\bin directory, you can skip this step (just comment out the call to SetDLLAccessPath
    // to try it)
    
    // Create a null delimited, double-null terminated string containing DLL category
    // name/path pairs.  The default structure of the SDK has discrete subdirectories for each DLL category.  If
    // you wish, all DLLs can be placed within a single directory.  The path passed to SetDLLAccessPath simply
    // needs to reflect this.
    
    memset(m_szDllPath, 0, 2048);
    UINT32 ulNumChars = 0;
    
    // Assume the current working directory is %sdk_install_dir%\bin -- this would normally be set to an absolute path
    // Note that this is the one and only place in the Producer SDK that requires an HFS path (all other pathnames
    // on the Mac should be POSIX-style).
#ifdef _WINDOWS	
    const char* pCurrentDir = ".\\";
#elif defined _LINUX
    const char* pCurrentDir = "./";
#elif defined _MAC_UNIX
    const char* pCurrentDir = "./";
#endif
    
    ulNumChars += sprintf(m_szDllPath+ulNumChars, "%s=%s%s", "DT_Plugins", pCurrentDir, "plugins") + 1;
    ulNumChars += sprintf(m_szDllPath+ulNumChars, "%s=%s%s", "DT_Codecs", pCurrentDir, "codecs") + 1;
    ulNumChars += sprintf(m_szDllPath+ulNumChars, "%s=%s%s", "DT_EncSDK", pCurrentDir, "tools") + 1;
    ulNumChars += sprintf(m_szDllPath+ulNumChars, "%s=%s%s", "DT_Common", pCurrentDir, "common") + 1;
    
    // Load the rmsession binary (DLL/so) to get the SetDLLAccessPath entry point
    if (SUCCEEDED(res))
    {		
#ifdef _WINDOWS
	m_RmsessionDLL = ::LoadLibrary(".\\tools\\encsession.dll");
#elif defined _LINUX
	m_RmsessionDLL = dlopen("./tools/encsession.so", RTLD_LAZY);
#elif defined _MAC_UNIX
        CFURLRef bundleURL = NULL;
        bundleURL = CFURLCreateWithFileSystemPath ( kCFAllocatorDefault, CFSTR("./tools/encsession.bundle"), kCFURLPOSIXPathStyle, true );
       
        if (bundleURL)
        {
            m_RmsessionDLL = CFBundleCreate ( kCFAllocatorDefault, bundleURL );
            CFRelease(bundleURL);
        }       
#endif
	if (m_RmsessionDLL)
	{
	    // Get the SetDLLAccessPath entry point
	    FPRMBUILDSETDLLACCESSPATH fpSetDllAccessPath = NULL;
#ifdef _WINDOWS
	    fpSetDllAccessPath = (FPRMBUILDSETDLLACCESSPATH)(::GetProcAddress(m_RmsessionDLL, "SetDLLAccessPath"));		
#elif defined _LINUX
	    fpSetDllAccessPath = (FPRMBUILDSETDLLACCESSPATH)(dlsym(m_RmsessionDLL, "SetDLLAccessPath"));
#elif defined _MAC_UNIX
	    fpSetDllAccessPath = (FPRMBUILDSETDLLACCESSPATH) CFBundleGetFunctionPointerForName(m_RmsessionDLL, CFSTR("SetDLLAccessPath"));
#endif
	    // Set the DLL access paths
	    if (fpSetDllAccessPath)
	    {
		res = (*fpSetDllAccessPath)(m_szDllPath);
	    }
	    else
	    {
		res = HXR_FAIL;
	    }
	}
	else
	{
	    res = HXR_FAIL;
	}
    }
    
    // Get the HXTCreateJobFactory entry point from the encsession binary
    if (SUCCEEDED(res))
    {	
	FPCREATEJOBFACTORY fpCreateClassFactory = NULL;	
#ifdef _WINDOWS
	fpCreateClassFactory = (FPCREATEJOBFACTORY)(::GetProcAddress(m_RmsessionDLL, "HXTCreateJobFactory"));
#elif defined _LINUX
	fpCreateClassFactory = (FPCREATEJOBFACTORY)(dlsym(m_RmsessionDLL, "HXTCreateJobFactory"));
#elif defined _MAC_UNIX
	fpCreateClassFactory = (FPCREATEJOBFACTORY) CFBundleGetFunctionPointerForName(m_RmsessionDLL, CFSTR("HXTCreateJobFactory"));
#endif
	// Create the job class factory
	if (fpCreateClassFactory)
	{
	    res = (*fpCreateClassFactory)(&m_pFactory);
	}
	else
	{
	    res = HXR_FAIL;
	}
    }
    
    return res;
}

HX_RESULT CEncoderApp::InitializeLogSystem()
{
    HX_RESULT res = HXR_OK;
    
    cout << "Step 2: Set up log system" << endl;
    
    // Check if logging should be enabled
    char szQuestion1[MAX_BUFFER_SIZE] = "";
    char szFile1[MAX_BUFFER_SIZE] = "";
    char szQuestion2[MAX_BUFFER_SIZE] = "";
    char szFile2[MAX_BUFFER_SIZE] = "";
    cout << "  Log errors and warnings to file (using the sample observer) (y/n)?: ";		
    m_cInput >> szQuestion1;
    
    if (szQuestion1[0] == 'y' || szQuestion1[0] == 'Y' )
    {
	cout << "  Enter log filename: ";		
	m_cInput >> szFile1;
    }
    
    cout << "  Log all messages to a different file (using the RN file observer) (y/n)?: ";		
    m_cInput >> szQuestion2;
    
    if (szQuestion2[0] == 'y' || szQuestion2[0] == 'Y' )
    {
	cout << "  Enter log filename: ";		
	m_cInput >> szFile2;
    }
    
    // Set up logging observer
    if (szQuestion1[0] == 'y' || szQuestion1[0] == 'Y' ||
	szQuestion2[0] == 'y' || szQuestion2[0] == 'Y')
    {	
	
	// Create/init log system -- note that it is necessary to do this prior to instantiating the log observer
	if (SUCCEEDED(res))
	{
	    res = m_pFactory->CreateInstance(IID_IHXTLogSystem, (IUnknown**)&m_pLogSystem);
	    
	    if (SUCCEEDED(res))
	    {
#ifdef _WINDOWS
		res = m_pLogSystem->SetTranslationFileDirectory(".\\tools");
#elif defined _LINUX
		res = m_pLogSystem->SetTranslationFileDirectory("./tools");
#elif defined _MAC_UNIX
		res = m_pLogSystem->SetTranslationFileDirectory("./tools");
#endif
	    }
	}
	
	if (SUCCEEDED(res) && szQuestion1[0] == 'y' || szQuestion1[0] == 'Y')
	{
	    m_pSampleLogObserver = new CSampleLogObserver;
	    m_pSampleLogObserver->AddRef();
	    
	    // Note that unlike events, logging is a system-wide subsystem (events are per-job)
	    res = m_pSampleLogObserver->Initialize(szFile1, m_pLogSystem);
	    
	    if (SUCCEEDED(res))
	    {
		res = m_pSampleLogObserver->Subscribe();
	    }
	    
	    //Now set the sample observer to only receive Error and Warning level log messages
	    if (SUCCEEDED(res))
	    {
		res = m_pSampleLogObserver->SetCategoryFilter(ERROR_MESSAGE_MASK|WARN_MESSAGE_MASK);
	    }
	}		
	
	if (SUCCEEDED(res) && szQuestion2[0] == 'y' || szQuestion2[0] == 'Y')
	{
	    // Create/init file observer
	    res = m_pFactory->CreateInstance(IID_IHXTFileObserver, (IUnknown**)&m_pFileLogObserver);
	    
	    if( SUCCEEDED(res) )
	    {
		m_pFileLogObserver->SetFormat(Detailed);
		m_pFileLogObserver->Init(szFile2);
	    }
	}
    }
    
    return res;
}

HX_RESULT CEncoderApp::CreateJob()
{
    HX_RESULT res = HXR_OK;
    
    cout << "Step 3: Creating encoding job" << endl;
    
    // Use the class factory to create the encoding job object
    if (SUCCEEDED(res))
	res = m_pFactory->CreateInstance(IID_IHXTEncodingJob, (IUnknown**)&m_pJob);
    
    // Register event sink
    if (SUCCEEDED(res))
    {
	// Create event sink helper class
	m_pEventSink = new CSampleEventSink;
	m_pEventSink->Init(this);
	m_pEventSink->AddRef();
	
	// Get the event manager
	IHXTEventManager* pEventMgr = NULL;
	res = m_pJob->GetEventManager(&pEventMgr);
	
	// Subscribe for events
	if (SUCCEEDED(res))
	    res = pEventMgr->Subscribe(m_pEventSink);
	
	HX_RELEASE(pEventMgr);
    }
    
    return res;
}


HX_RESULT CEncoderApp::SetupInput()
{
    HX_RESULT res = HXR_OK;
    
    cout << "Step 4: Setting up input" << endl;
    
    char szNumInputsChoice[MAX_BUFFER_SIZE] = "";
    cout << "  Use multiple parallel inputs(y/n)? ";
    m_cInput >> szNumInputsChoice;
    if(szNumInputsChoice[0] == 'y' || szNumInputsChoice[0] == 'Y')
    {
	res = SetupMultipleInputs();
    }
    else
    {
	// Pick file or capture input
	char szInputChoice[MAX_BUFFER_SIZE] = "";
	cout << "  Use file or capture input (f/c)? ";
	m_cInput >> szInputChoice;
	
	IHXTPropertyBag* pInitParams = NULL;
	
	// Set up init params differently for file and capture inputs
	if (szInputChoice[0] == 'c' || szInputChoice[0] == 'C')
	{
	    res = SetupCaptureInputParams(&pInitParams);
	    if(SUCCEEDED(res))
	    {
		res = BuildAndAddInput(pInitParams);
		HX_RELEASE(pInitParams);
	    }
#ifdef _WINDOWS
	    IHXTInput* pInput = NULL;
	    // Get the input
	    if (SUCCEEDED(res))
	    {
		res = m_pJob->GetInput(&pInput);
	    }
	    
        if (SUCCEEDED(res))
        {
	    const char* pkszDeviceID = NULL;
	    if ( SUCCEEDED( pInput->GetString(kPropVideoDeviceID, &pkszDeviceID) ) )
	    {
		LaunchCaptureDialogs( pInput, CAPTURE_DIALOG_VIDEO );
	    }
	    
	    if ( SUCCEEDED( pInput->GetString(kPropAudioDeviceID, &pkszDeviceID) ) )
	    {
		LaunchCaptureDialogs( pInput, CAPTURE_DIALOG_AUDIO );
	    }
        }
	    HX_RELEASE(pInput);
#endif
	    
	}
	else
	{
	    res = SetupFileInputParams(&pInitParams);
	    if(SUCCEEDED(res))
	    {
		res = BuildAndAddInput(pInitParams);
		HX_RELEASE(pInitParams);
	    }
	}
    }
    
    return res;
}

HX_RESULT
CEncoderApp::SetupMultipleInputs()
{
    HX_RESULT res = HXR_OK;
    
    IHXTInput2* pInputGroup = NULL;
    
    IHXTPropertyBag* pInitParams = NULL;
    res = SetupInputParGroupParams(&pInitParams);
    if(SUCCEEDED(res))
    {
	IHXTInput* pInput = NULL;
	res = BuildAndAddInput(pInitParams, &pInput);
	if(SUCCEEDED(res))
	{
	    res = pInput->QueryInterface(IID_IHXTInput2, (void**)&pInputGroup);
	    HX_RELEASE(pInput);
	}
	HX_RELEASE(pInitParams);
    }
    
    if(SUCCEEDED(res))
    {
	char szInputChoice[MAX_BUFFER_SIZE] = "";
	
	do
	{
	    BOOL bFileInput = FALSE;
	    // Pick file or capture input
	    cout << "  Use file or capture input (f/c)? ";
	    char szInputTypeChoice[MAX_BUFFER_SIZE] = "";
	    m_cInput >> szInputTypeChoice;
	    // Set up init params differently for file and capture inputs
	    if (szInputTypeChoice[0] == 'c' || szInputTypeChoice[0] == 'C')
	    {
		res = SetupCaptureInputParams(&pInitParams);
	    }
	    else
	    {
		bFileInput = TRUE;
		res = SetupFileInputParams(&pInitParams);
	    }
	    
	    if(SUCCEEDED(res))
	    {
		IHXTInput* pInput = NULL;
		res = BuildAndAddMultipleInput(pInputGroup, pInitParams, &pInput);
		if(SUCCEEDED(res))
		{
		    if(bFileInput)
		    {
			char szDisableChoice[MAX_BUFFER_SIZE] = "";
			
			BOOL bHasAudio = FALSE;
			res = pInput->GetBool(kPropHasAudio, &bHasAudio);
			if(SUCCEEDED(res) && bHasAudio)
			{
			    cout << "  Disable audio(y/n)? ";
			    m_cInput >> szDisableChoice;
			    if(szDisableChoice[0] == 'Y' || szDisableChoice[0] =='y')
			    {
				pInput->SetBool(kPropDisableAudio, TRUE);
			    }
			}
			
			BOOL bHasVideo = FALSE;
			res = pInput->GetBool(kPropHasVideo, &bHasVideo);
			if(SUCCEEDED(res) && bHasVideo)
			{
			    cout << "  Disable video(y/n)? ";
			    m_cInput >> szDisableChoice;
			    if(szDisableChoice[0] == 'Y' || szDisableChoice[0] =='y')
			    {
				pInput->SetBool(kPropDisableVideo, TRUE);
			    }
			}
			
			BOOL bHasEvents = FALSE;
			res = pInput->GetBool(kPropHasEvents, &bHasEvents);
			if(SUCCEEDED(res) && bHasEvents)
			{
			    cout << "  Disable events(y/n)? ";
			    m_cInput >> szDisableChoice;
			    if(szDisableChoice[0] == 'Y' || szDisableChoice[0] =='y')
			    {
				pInput->SetBool(kPropDisableEvents, TRUE);
			    }
			    else
			    {
				m_bDoEvents = TRUE;
			    }
			}
			
			BOOL bHasImageMaps = FALSE;
			res = pInput->GetBool(kPropHasImageMaps, &bHasImageMaps);
			if(SUCCEEDED(res) && bHasImageMaps)
			{
			    cout << "  Disable imagemaps(y/n)? ";
			    m_cInput >> szDisableChoice;
			    if(szDisableChoice[0] == 'Y' || szDisableChoice[0] =='y')
			    {
				pInput->SetBool(kPropDisableImageMaps, TRUE);
			    }
			}
		    }
		}
		
		HX_RELEASE(pInitParams);
	    }
	    
	    cout << "  Select another input(y/n)? ";
	    m_cInput >> szInputChoice;
	} while(szInputChoice[0] == 'Y' || szInputChoice[0] == 'y');
    }
    
#ifdef _DO_EVENTS
    if (SUCCEEDED(res) && !m_bDoEvents)
    {
	res = SetupEvents(pInputGroup);
    }
#endif	//  _DO_EVENTS
    
    HX_RELEASE(pInputGroup);
    
    return res;
}

#ifdef _WINDOWS
//Capture dialog currently are supported only on widows.
HX_RESULT CEncoderApp::LaunchCaptureDialogs( IHXTInput *pInput, UINT32 ulCaptureDialogType )
{
    HX_RESULT res = HXR_OK;
    char szInputDisplayDialogs[MAX_BUFFER_SIZE] = "";
    
    if ( ulCaptureDialogType == CAPTURE_DIALOG_VIDEO )
    {
	cout << "Would you like to display video capture dialogs? (y/n)";
    }
    else if ( ulCaptureDialogType == CAPTURE_DIALOG_AUDIO )
    {
	cout << "Would you like to display audio capture dialogs? (y/n)";
    }
    
    m_cInput >> szInputDisplayDialogs;
    
    IHXTPropertyBag *pDialogBag = NULL;
    while ( szInputDisplayDialogs[0] == 'y' || szInputDisplayDialogs[0] == 'Y' )
    {
	//Output Device Dialog names
	HX_RELEASE( pDialogBag );
	if ( ulCaptureDialogType == CAPTURE_DIALOG_VIDEO )
	{
	    res = pInput->GetPropertyBag( kPropVideoCaptureDialogs, &pDialogBag );
	}
	else if ( ulCaptureDialogType == CAPTURE_DIALOG_AUDIO )
	{
	    res = pInput->GetPropertyBag( kPropAudioCaptureDialogs, &pDialogBag );
	}
	
	IHXTPropertyBag* pDialogNamesBag = NULL;
	if (SUCCEEDED(res))
	{
	    //kPropCaptureDialogNames property bag contains
	    // properties of type int32 with name corresponding to CaptureDialogs
	    // and value consisting of id's used to identify the corresponding dialog.
	    res = pDialogBag->GetPropertyBag(kPropCaptureDialogNames, &pDialogNamesBag );
	}
	
	IHXTPropertyEnumerator* pEnum =	NULL;
	if (SUCCEEDED(res))
	{
	    res = pDialogNamesBag->GetPropertyEnumerator( &pEnum );
	}
	
	IHXTProperty* pProp = NULL;
	HX_RESULT resEnum = HXR_OK;
	if ( SUCCEEDED(res) )
	{
	    resEnum = pEnum->First( &pProp );
	}
	
	INT32 iDialogID = -1;
	while (SUCCEEDED(resEnum) && resEnum != HXR_ELEMENT_NOT_FOUND && SUCCEEDED(res) )
	{
	    res = pProp->GetInt(&iDialogID);
	    if ( SUCCEEDED(res) )
	    {
		cout << iDialogID << pProp->GetKey() <<endl;
	    }
	    
	    //Get Next property
	    if ( SUCCEEDED(res) )
	    {
		resEnum = pEnum->Next( &pProp );
	    }
	}
	
	HX_RELEASE( pProp );
	HX_RELEASE( pEnum );
	HX_RELEASE( pDialogNamesBag );
	
	UINT32 ulDisplayDialogId = 0;
	cout << "Enter the display dialog id to display.";
	m_cInput  >> ulDisplayDialogId;
	
	IUnknown *pUnkDialog = 0;
	if (SUCCEEDED(res))
	{
	    res = pDialogBag->GetUnknown(kPropCaptureDialogControl, &pUnkDialog);
	}
	
	IHXTCaptureDialogControl* pDialogCtrl;
	if (SUCCEEDED(res))
	{
	    res = pUnkDialog->QueryInterface(IID_IHXTCaptureDialogControl, (void**)&pDialogCtrl );	
	}
	
	HX_RELEASE(pUnkDialog);
	if (SUCCEEDED(res))
	{
	    //LaunchDialog takes the handle of parent widow as second parent.
	    //Not sure if GetForegroundWindow() is the correct choice.
	    pDialogCtrl->LaunchDialog( ulDisplayDialogId, GetForegroundWindow() );
	}
	
	HX_RELEASE(pDialogCtrl);
	
	if ( ulCaptureDialogType == CAPTURE_DIALOG_VIDEO )
	{
	    cout << "Would you like to display video capture dialogs? (y/n)";
	}
	else if ( ulCaptureDialogType == CAPTURE_DIALOG_AUDIO )
	{
	    cout << "Would you like to display audio capture dialogs? (y/n)";
	}
	
	m_cInput >> szInputDisplayDialogs;
    }
    
    HX_RELEASE( pDialogBag );
    
    return res;
}
#endif

HX_RESULT
CEncoderApp::SetupEvents(IHXTInput2* pInputGroup)
{
    HX_RESULT res = HXR_OK;
    
    // Pick file or capture input
    cout << "  Use input events(y/n)? ";
    char szInputChoice[MAX_BUFFER_SIZE] = "";
    m_cInput >> szInputChoice;
    if(szInputChoice[0] == 'Y' || szInputChoice[0] == 'y')
    {
	m_bDoEvents = TRUE;
	
	IHXTPropertyBag* pInitParams = NULL;
	res = SetupEventInputParams(&pInitParams);
	if(SUCCEEDED(res))
	{
	    // Create the input
	    IHXTInput* pInput = NULL;
	    res = BuildAndAddMultipleInput(pInputGroup, pInitParams, &pInput);
	    if(SUCCEEDED(res))
	    {
		IUnknown* pUnk = NULL;
		res = pInput->GetUnknown(kPropEventInputPin, &pUnk);
		
		if (SUCCEEDED(res))
		    res = pUnk->QueryInterface(IID_IHXTMediaInputPin, (void**)&m_pEventPin);
		
		// Enable pin
		if (SUCCEEDED(res))
		    res = m_pEventPin->SetPinEnabled(TRUE);
		
		HX_RELEASE(pUnk);
		
		HX_RELEASE(pInput);
	    }
	    
	    HX_RELEASE(pInitParams);
	}
    }
    
    return res;
}

HX_RESULT
CEncoderApp::BuildAndAddInput(IHXTPropertyBag* pInitParams, IHXTInput** ppInput)
{
    HX_RESULT res = HXR_OK;
    
    IHXTInput* pInput = NULL;
    res = m_pFactory->BuildInstance(IID_IHXTInput, pInitParams, (IUnknown**)&pInput);
    // Set the input
    if (SUCCEEDED(res))
    {
	res = m_pJob->SetInput(pInput); 
	if(ppInput)
	{
	    *ppInput = pInput;
	    (*ppInput)->AddRef();
	}
	
	HX_RELEASE(pInput);
    }
    
    return res;
}

HX_RESULT
CEncoderApp::BuildAndAddMultipleInput(IHXTInput2* pInputGroup, IHXTPropertyBag* pInitParams, IHXTInput** ppInput)
{
    HX_RESULT res = HXR_OK;
    
    IHXTInput* pInput = NULL;
    
    // Create the input
    res = m_pFactory->BuildInstance(IID_IHXTInput, pInitParams, (IUnknown**)&pInput);
    
    // Set the input on the encoding job
    if (SUCCEEDED(res))
    {
	res = pInputGroup->AddInput(pInput);
    }
    
    // Print name of input plugin
    if (SUCCEEDED(res))
    {
	const char* szPluginName = "";
	res = pInput->GetString(kPropPluginName, &szPluginName);
	
	if (SUCCEEDED(res))
	    cout << "  Using input plugin: " << szPluginName << endl;
    }
    
    if(SUCCEEDED(res) && ppInput)
    {
	*ppInput = pInput;
	(*ppInput)->AddRef();
    }
    
    HX_RELEASE(pInput);
    
    return res;
}

HX_RESULT 
CEncoderApp::SetupFileInputParams(IHXTPropertyBag** ppInitParams)
{
    HX_RESULT res = HXR_OK;
    
    res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)ppInitParams);
    if(SUCCEEDED(res))
    {
	// Set the plugin type
	// Note that kPropPluginName is not set -- this allows the Producer SDK to pick the optimal
	// input reader plugin to use from the set of all input reader plugins
	if (SUCCEEDED(res))
	    res = (*ppInitParams)->SetString(kPropPluginType, kValuePluginTypeInputAVFile);
	
	// Get input pathname
	cout << "  Enter input pathname: ";
	char szInputPathname[MAX_BUFFER_SIZE] = "";
	m_cInput >> szInputPathname;
	
	// Set the pathname
	if (SUCCEEDED(res))
	    res = (*ppInitParams)->SetString(kPropInputPathname, szInputPathname);
    }
    
    return res;
}

HX_RESULT 
CEncoderApp::SetupCaptureInputParams(IHXTPropertyBag** ppInitParams)
{
    HX_RESULT res = HXR_OK;
    
    res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)ppInitParams);
    if(SUCCEEDED(res))
    {
	// Set plugin type/name
	if (SUCCEEDED(res))
	    res = (*ppInitParams)->SetString(kPropPluginType, kValuePluginTypeInputCapture);
	
	if (SUCCEEDED(res))
	    res = (*ppInitParams)->SetString(kPropPluginName, kValuePluginNameCaptureAV);
	
	// Set capture duration to 30 seconds
	if (SUCCEEDED(res))
	{
	    // Create time object
	    IUnknown* pUnk = NULL;
	    res = m_pFactory->CreateInstance(IID_IHXTTime, &pUnk);
	    
	    IHXTTime* pTime = NULL;
	    if (SUCCEEDED(res))
		res = pUnk->QueryInterface(IID_IHXTTime, (void**)&pTime);
	    
	    if (SUCCEEDED(res))
		res = pTime->SetTime(30000);
	    
	    // Add duration to the init bag
	    if (SUCCEEDED(res))
		res = (*ppInitParams)->SetUnknown(kPropDuration, pUnk);
	    
	    HX_RELEASE(pTime);
	    HX_RELEASE(pUnk);
	}
	
	// Display all video capture devices
	if (SUCCEEDED(res))
	    res = DisplayCaptureDevices(kValueCaptureMediaTypeVideoCapture, kPropVideoDeviceID);
	
	// Display all audio capture devices
	if (SUCCEEDED(res))
	    res = DisplayCaptureDevices(kValueCaptureMediaTypeAudioCapture, kPropAudioDeviceID);
	
	// Check if the user wants to capture video
	if (SUCCEEDED(res))
	{		
	    cout << "  Enter video capture device number (-1 to skip): ";
	    char szVideoChoice[MAX_BUFFER_SIZE] = "";
	    m_cInput >> szVideoChoice;
	    
	    // Add the video device ID to the init bag
	    if (szVideoChoice[0] != '-')
		res = (*ppInitParams)->SetString(kPropVideoDeviceID, szVideoChoice);
	}
	
	// Check if the user wants to capture audio
	if (SUCCEEDED(res))
	{		
	    cout << "  Enter audio capture device number (-1 to skip): ";
	    char szAudioChoice[MAX_BUFFER_SIZE] = "";
	    m_cInput >> szAudioChoice;
	    
	    // Add the video device ID to the init bag
	    if (szAudioChoice[0] != '-')
		res = (*ppInitParams)->SetString(kPropAudioDeviceID, szAudioChoice);
	}
    }
    
    return res;
}

HX_RESULT
CEncoderApp::SetupEventInputParams(IHXTPropertyBag** ppInitParams)
{
    HX_RESULT res = HXR_OK;
    
    res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)ppInitParams);
    if(SUCCEEDED(res))
    {
	// Set the plugin type
	// Note that kPropPluginName is not set -- this allows the Producer SDK to pick the optimal
	// input reader plugin to use from the set of all input reader plugins
	res = (*ppInitParams)->SetString(kPropPluginType, kValuePluginTypeInputMediaSink);
	
	// Media samples generated in StartEncoding are not strictly real-time
	if (SUCCEEDED(res))
	    res = (*ppInitParams)->SetBool(kPropIsRealTime, FALSE);
    }
    
    return res;
}

HX_RESULT
CEncoderApp::SetupInputParGroupParams(IHXTPropertyBag** ppInitParams)
{
    HX_RESULT res = HXR_OK;
    
    res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)ppInitParams);
    if(SUCCEEDED(res))
    {
	res = (*ppInitParams)->SetString(kPropPluginType, kValuePluginTypeInputParGroup);
    }
    
    return res;
}

HX_RESULT CEncoderApp::DisplayCaptureDevices(const char* szCaptureType, const char* szDeviceIDType)
{
    HX_RESULT res = HXR_OK;
    
    // Get access to the capture device enumerator
    IHXTPluginInfoManager* pCaptureDeviceManager = NULL;
    res = m_pFactory->CreateInstance(CLSID_IHXTCaptureDeviceInfoManager, (IUnknown**)&pCaptureDeviceManager);
    
    // Create property bag -- used to specify enumeration category
    IHXTPropertyBag* pQueryPropertyBag = NULL;
    if (SUCCEEDED(res))
	res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)&pQueryPropertyBag);
    
    // Search for all capture devices of type szCaptureType
    if (SUCCEEDED(res))
	res = pQueryPropertyBag->SetString(kPropCaptureMediaType, szCaptureType);
    
    IHXTPluginInfoEnum* pPluginInfoEnum = NULL;
    if (SUCCEEDED(res))
	res = pCaptureDeviceManager->GetPluginInfoEnum(pQueryPropertyBag, &pPluginInfoEnum);
    
    // Display all capture devices that were found
    if (SUCCEEDED(res))
    {	
	cout << "  " << szCaptureType << " devices" << endl;
	for (int i = 0; i<pPluginInfoEnum->GetCount(); i++)
	{
	    // Get info about the current device
	    IHXTPropertyBag* pCurrentPluginInfo = NULL;						
	    HX_RESULT resEnum = pPluginInfoEnum->GetPluginInfoAt(i, &pCurrentPluginInfo);
	    
	    // Get the device name
	    const char* szDeviceID = NULL;
	    if (SUCCEEDED(resEnum))
		resEnum = pCurrentPluginInfo->GetString(szDeviceIDType, &szDeviceID);
	    
	    if (SUCCEEDED(resEnum))
		cout << "    Device " << i << ": "<< szDeviceID << endl;
	    
	    HX_RELEASE(pCurrentPluginInfo);
	}                       
	
	if (pPluginInfoEnum->GetCount() == 0)
	{
	    cout << "    -- No devices detected" << endl;
	}
    }
    
    HX_RELEASE(pCaptureDeviceManager);
    HX_RELEASE(pQueryPropertyBag);
    HX_RELEASE(pPluginInfoEnum);

    return res;
}

HX_RESULT 
CEncoderApp::CreatePreviewSinkInputAudio(
					 UINT32 nSampleRate,
					 UINT32 nSampleSize,
					 UINT32 nNumChannels)
{
    HX_RESULT res = HXR_OK;
    
    IHXTInput* pInput = NULL;
    res = m_pJob->GetInput(&pInput);
    
    BOOL bInputHasAudio = FALSE;
    if(SUCCEEDED(res))
	res = pInput->GetBool(kPropHasAudio, &bInputHasAudio);
    
    if(!bInputHasAudio)
    {
	res = HXR_UNEXPECTED;
    }
    
    //	Handle the input and prefiltered previews
    if(SUCCEEDED(res))
    {
	IHXTPreviewSinkControl* pIHXTPreviewSinkControl = NULL;
	res = pInput->QueryInterface(IID_IHXTPreviewSinkControl, (void**)&pIHXTPreviewSinkControl);
	if(SUCCEEDED(res))
	{
	    //	Create the preview sink
	    res = CreateAudioSink(pIHXTPreviewSinkControl, kValueBeforeAllPrefilters, nSampleRate, nSampleSize, nNumChannels);
	    
	    HX_RELEASE(pIHXTPreviewSinkControl);
	}
    }
    
    HX_RELEASE(pInput);
    
    return res;
}



HX_RESULT 
CEncoderApp::CreatePreviewSinkInputVideo(
					 UINT32 nColorFormat,
					 UINT32 nVideoWidth,
					 UINT32 nVideoHeight)
{
    HX_RESULT res = HXR_OK;
    
    IHXTInput* pInput = NULL;
    res = m_pJob->GetInput(&pInput);
    
    // Check if the input source has a video track
    BOOL bInputHasVideo = FALSE;
    if (SUCCEEDED(res))
	res = pInput->GetBool(kPropHasVideo, &bInputHasVideo);
    
    if(!bInputHasVideo)
    {
	res = HXR_UNEXPECTED;
    }
    
    //	Handle the input and prefiltered previews
    if(SUCCEEDED(res))
    {
	IHXTPreviewSinkControl* pIHXTPreviewSinkControl = NULL;
	res = pInput->QueryInterface(IID_IHXTPreviewSinkControl, (void**)&pIHXTPreviewSinkControl);
	if(SUCCEEDED(res) && bInputHasVideo)
	{
	    //	Create the preview sink
	    res = CreateVideoSink(pIHXTPreviewSinkControl, kValueBeforeAllPrefilters, nColorFormat, nVideoWidth, nVideoHeight);	   	    
	}

	HX_RELEASE(pIHXTPreviewSinkControl);
    }
    
    HX_RELEASE(pInput);
    
    return res;
}

HX_RESULT 
CEncoderApp::CreatePreviewSinkPrefilteredAudio(
					       UINT32 nSampleRate,
					       UINT32 nSampleSize,
					       UINT32 nNumChannels)
{
    HX_RESULT res = HXR_OK;
    
    IHXTInput* pInput = NULL;
    res = m_pJob->GetInput(&pInput);
    
    BOOL bInputHasAudio = FALSE;
    if(SUCCEEDED(res))
	res = pInput->GetBool(kPropHasAudio, &bInputHasAudio);
    
    if(!bInputHasAudio)
    {
	res = HXR_UNEXPECTED;
    }
    
    //	Handle the input and prefiltered previews
    if(SUCCEEDED(res))
    {
	IHXTPreviewSinkControl* pIHXTPreviewSinkControl = NULL;
	res = pInput->QueryInterface(IID_IHXTPreviewSinkControl, (void**)&pIHXTPreviewSinkControl);
	if(SUCCEEDED(res))
	{
	    //	Create the preview sink
	    res = CreateAudioSink(pIHXTPreviewSinkControl, kValueAfterAllPrefilters, nSampleRate, nSampleSize, nNumChannels);
	    
	    HX_RELEASE(pIHXTPreviewSinkControl);
	}
    }
    
    HX_RELEASE(pInput);
    
    return res;
}

HX_RESULT 
CEncoderApp::CreatePreviewSinkPrefilteredVideo(
					       UINT32 nColorFormat,
					       UINT32 nVideoWidth,
					       UINT32 nVideoHeight)
{
    HX_RESULT res = HXR_OK;
    
    IHXTInput* pInput = NULL;
    res = m_pJob->GetInput(&pInput);
    
    // Check if the input source has a video track
    BOOL bInputHasVideo = FALSE;
    if (SUCCEEDED(res))
	res = pInput->GetBool(kPropHasVideo, &bInputHasVideo);
    
    if(!bInputHasVideo)
    {
	res = HXR_UNEXPECTED;
    }
    
    //	Handle the input and prefiltered previews
    if(SUCCEEDED(res))
    {
	IHXTPreviewSinkControl* pIHXTPreviewSinkControl = NULL;
	res = pInput->QueryInterface(IID_IHXTPreviewSinkControl, (void**)&pIHXTPreviewSinkControl);
	if(SUCCEEDED(res))
	{
	    //	Create the preview sink
	    res = CreateVideoSink(pIHXTPreviewSinkControl, kValueAfterAllPrefilters, nColorFormat, nVideoWidth, nVideoHeight);
	    
	    HX_RELEASE(pIHXTPreviewSinkControl);
	}
    }
    
    HX_RELEASE(pInput);
    
    return res;
}

HX_RESULT
CEncoderApp::CreatePreviewSinkEncodedVideo(
					   UINT32 nColorFormat,
					   UINT32 nVideoWidth,
					   UINT32 nVideoHeight)
{
    HX_RESULT res = HXR_OK;
    
    IHXTInput* pInput = NULL;
    res = m_pJob->GetInput(&pInput);
    
    // Check if the input source has a video track
    BOOL bInputHasVideo = FALSE;
    if (SUCCEEDED(res))
	res = pInput->GetBool(kPropHasVideo, &bInputHasVideo);
    
    if(!bInputHasVideo)
    {
	res = HXR_UNEXPECTED;
    }
    
    if(SUCCEEDED(res))
    {
	// Handle the encoded preview	
	for(UINT32 uiProfile = 0; uiProfile < m_pJob->GetOutputProfileCount(); uiProfile++)
	{
	    m_nCurrentOutputProfile = uiProfile;
	    
	    IHXTOutputProfile* pOutputProfile = NULL;
	    res = m_pJob->GetOutputProfile(uiProfile, &pOutputProfile);
	    if(SUCCEEDED(res))
	    {
		IHXTMediaProfile* pMediaProfile = NULL;
		res = pOutputProfile->GetMediaProfile(&pMediaProfile);
		if(SUCCEEDED(res))
		{		    
		    for(UINT32 uiAudience = 0; uiAudience < pMediaProfile->GetAudienceCount(); uiAudience++)
		    {
			m_nCurrentAudience = uiAudience;

			IHXTAudience* pAudience = NULL;
			res = pMediaProfile->GetAudience(uiAudience, &pAudience);
			if(SUCCEEDED(res))
			{
			    IHXTPreviewSinkControl* pIHXTPreviewSinkControl = NULL;
			    res = pAudience->QueryInterface(IID_IHXTPreviewSinkControl, (void**)&pIHXTPreviewSinkControl);

			    if(SUCCEEDED(res))
			    {
				res = CreateVideoSink(pIHXTPreviewSinkControl, kValueAfterCodec, nColorFormat, nVideoWidth, nVideoHeight);
			    }
			    HX_RELEASE(pIHXTPreviewSinkControl);
			}
			HX_RELEASE(pAudience);
		    }
		}
		HX_RELEASE(pMediaProfile);
	    }
	    HX_RELEASE(pOutputProfile);
	}
    }
    
    HX_RELEASE(pInput);
    
    return res;
}

HX_RESULT
CEncoderApp::CreateSink(
			UINT32 nPreviewPosition, 
			const char* szMediaFormat, 
			UINT32 nColorFormat,
			UINT32 nVideoWidth, 
			UINT32 nVideoHeight)
{
    HX_RESULT res = HXR_OK;
    
    if(!(m_nPreviewSinkCount + 1 < MAX_PREVIEW_SINK))
    {
	res = HXR_FAIL;
	return res;
    }
    
    CSamplePreviewSink* pPreviewSink = new CSamplePreviewSink(
	nPreviewPosition, 
	m_nPreviewSinkCount,
	szMediaFormat,
	nColorFormat,
	nVideoWidth,
	nVideoHeight,
	m_nCurrentOutputProfile,
	m_nCurrentAudience);
    if(pPreviewSink)
    {
	m_ppPreviewSink[m_nPreviewSinkCount] = pPreviewSink;
	pPreviewSink->AddRef();
    }
    else
    {
	res = HXR_OUTOFMEMORY;
    }
    
    return res;
}

HX_RESULT 
CEncoderApp::CreateAudioSink(
			     IHXTPreviewSinkControl* pIHXTPreviewSinkControl,
			     UINT32 nPreviewPosition,
			     UINT32 nSampleRate,
			     UINT32 nSampleSize,
			     UINT32 nNumChannels)
{
    HX_RESULT res = HXR_OK;
    
    IHXTPropertyBag* pIPreviewProps = NULL;
    res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)&pIPreviewProps);
    if(SUCCEEDED(res))
    {
	if(SUCCEEDED(res))
	    res = pIPreviewProps->SetUint(kPropPreviewSinkPosition, nPreviewPosition);
	
	if(SUCCEEDED(res))
	    res = pIPreviewProps->SetString(kPropMediaFormat, kValueMediaFormatUncompAudio);
	
	if(SUCCEEDED(res))
	    res = pIHXTPreviewSinkControl->GetOptimalSinkProperties(&pIPreviewProps);
	
	if(SUCCEEDED(res))
	    res = pIPreviewProps->SetInt(kPropSinkUpdateInterval, kValueAllSamples);
	
	if(SUCCEEDED(res) && nSampleRate)
	{
	    res = pIPreviewProps->SetUint(kPropAudioSampleRate, nSampleRate);
	}
	
	if(SUCCEEDED(res) && nSampleSize)
	{
	    res = pIPreviewProps->SetUint(kPropAudioSampleFormat, nSampleSize);
	}
	
	if(SUCCEEDED(res) && nNumChannels)
	{
	    res = pIPreviewProps->SetUint(kPropAudioChannelFormat, nNumChannels);
	}
    }
    
    if(SUCCEEDED(res))
    {
	res = CreateSink(nPreviewPosition, kValueMediaFormatUncompAudio);
	if(SUCCEEDED(res))
	{
	    res = pIHXTPreviewSinkControl->AddSink(m_ppPreviewSink[m_nPreviewSinkCount], pIPreviewProps);
	}
    }
    
    if(SUCCEEDED(res))
    {
	m_nPreviewSinkCount++;
    }
    
    HX_RELEASE(pIPreviewProps);

    return res;
}

HX_RESULT 
CEncoderApp::CreateVideoSink(
			     IHXTPreviewSinkControl* pIHXTPreviewSinkControl,
			     UINT32 nPreviewPosition,
			     UINT32 nColorFormat,
			     UINT32 nVideoWidth,
			     UINT32 nVideoHeight)
{
    HX_RESULT res = HXR_OK;
    
    UINT32 nAdjColorFormat = 0;
    
    IHXTPropertyBag* pIPreviewProps = NULL;
    res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)&pIPreviewProps);
    if(SUCCEEDED(res))
    {
	if(SUCCEEDED(res))
	    res = pIPreviewProps->SetUint(kPropPreviewSinkPosition, nPreviewPosition);
	
	if(SUCCEEDED(res))
	    res = pIPreviewProps->SetString(kPropMediaFormat, kValueMediaFormatUncompVideo);
	
	if(SUCCEEDED(res))
	{
	    if(nPreviewPosition == kValueBeforeAllPrefilters ||
		nPreviewPosition == kValueAfterAllPrefilters)
	    {
		res = pIHXTPreviewSinkControl->GetOptimalSinkProperties(&pIPreviewProps);
	    }
	    else if(nPreviewPosition == kValueAfterCodec)
	    {
		IHXTInput* pInput = NULL;
		res = m_pJob->GetInput(&pInput);
		if(SUCCEEDED(res))
		{
		    IHXTPreviewSinkControl* pIHXTPreviewSinkControl2 = NULL;
		    res = pInput->QueryInterface(IID_IHXTPreviewSinkControl, (void**)&pIHXTPreviewSinkControl2);
		    if(SUCCEEDED(res))
		    {
			res = pIHXTPreviewSinkControl2->GetOptimalSinkProperties(&pIPreviewProps);
		    }

		    HX_RELEASE(pIHXTPreviewSinkControl2);
		}

		HX_RELEASE(pInput);
	    }
	}
	
	if(SUCCEEDED(res))
	    res = pIPreviewProps->SetInt(kPropSinkUpdateInterval, kValueAllSamples);
	
	if(SUCCEEDED(res))
	{
	    if(nColorFormat)
	    {
		//	Convert color format to zero based enum
		res = pIPreviewProps->SetUint(kPropVideoColorFormat, nColorFormat - 1);
		nAdjColorFormat = nColorFormat - 1;
	    }
	    else
	    {
		res = pIPreviewProps->GetUint(kPropVideoColorFormat, &nAdjColorFormat);
	    }
	}
	
	if(SUCCEEDED(res))
	{
	    if(nVideoWidth)
	    {
		res = pIPreviewProps->SetUint(kPropVideoFrameWidth, nVideoWidth);
	    }
	    else
	    {
		res = pIPreviewProps->GetUint(kPropVideoFrameWidth, &nVideoWidth);
	    }
	}
	
	if(SUCCEEDED(res))
	{
	    if(nVideoHeight)
	    {
		res = pIPreviewProps->SetUint(kPropVideoFrameHeight, nVideoHeight);
	    }
	    else
	    {
		res = pIPreviewProps->GetUint(kPropVideoFrameHeight, &nVideoHeight);
	    }
	}
    }
    
    if(SUCCEEDED(res))
    {
	res = CreateSink(nPreviewPosition, kValueMediaFormatUncompVideo, nAdjColorFormat, nVideoWidth, nVideoHeight);
	if(SUCCEEDED(res))
	{
	    res = pIHXTPreviewSinkControl->AddSink(m_ppPreviewSink[m_nPreviewSinkCount], pIPreviewProps);
	}
    }
    
    if(SUCCEEDED(res))
    {
	m_nPreviewSinkCount++;
    }
    
    HX_RELEASE(pIPreviewProps);

    return res;
}
			     
HX_RESULT CEncoderApp::DestroyPreviewSinks()
{
    HX_RESULT res = HXR_OK;
    
    IHXTPreviewSinkControl* pIHXTInputPreviewSinkControl = NULL;
    
    IHXTInput* pInput = NULL;
    res = m_pJob->GetInput(&pInput);
    if(SUCCEEDED(res))
    {
	res = pInput->QueryInterface(IID_IHXTPreviewSinkControl, (void**)&pIHXTInputPreviewSinkControl);
    }
    
    if(SUCCEEDED(res))
    {
	for(UINT32 i = 0; i < MAX_PREVIEW_SINK; i++)
	{
	    if(m_ppPreviewSink[i]) 
	    {
		if(m_ppPreviewSink[i]->GetPreviewSinkPosition() == kValueBeforeAllPrefilters ||
		    m_ppPreviewSink[i]->GetPreviewSinkPosition() == kValueAfterAllPrefilters)
		{
		    HX_RESULT res2 = pIHXTInputPreviewSinkControl->RemoveSink(m_ppPreviewSink[i]);
		}
		else if(m_ppPreviewSink[i]->GetPreviewSinkPosition() == kValueAfterCodec)
		{
		    IHXTPreviewSinkControl* pIHXTOutputPreviewSinkControl = NULL;
		    if(SUCCEEDED(res))
		    {
			IHXTOutputProfile* pOutputProfile = NULL;
			for(UINT32 uiProfile = 0; uiProfile < m_pJob->GetOutputProfileCount(); uiProfile++)
			{
			    if(m_ppPreviewSink[i]->GetOutputProfile() == uiProfile)
			    {
				res = m_pJob->GetOutputProfile(uiProfile, &pOutputProfile);
				if(SUCCEEDED(res))
				{
				    IHXTMediaProfile* pMediaProfile = NULL;
				    res = pOutputProfile->GetMediaProfile(&pMediaProfile);
				    if(SUCCEEDED(res))
				    {
					IHXTAudience* pAudience = NULL;
					for(UINT32 uiAudience = 0; uiAudience < pMediaProfile->GetAudienceCount(); uiAudience++)
					{
					    if(m_ppPreviewSink[i]->GetAudience() == uiAudience)
					    {
						res = pMediaProfile->GetAudience(uiAudience, &pAudience);
						if(SUCCEEDED(res))
						{
						    res = pAudience->QueryInterface(IID_IHXTPreviewSinkControl, (void**)&pIHXTOutputPreviewSinkControl);
						    if(SUCCEEDED(res))
						    {
							HX_RESULT res2 = pIHXTOutputPreviewSinkControl->RemoveSink(m_ppPreviewSink[i]);
							
							HX_RELEASE(pIHXTOutputPreviewSinkControl);
						    }
						}
					    }
					    HX_RELEASE(pAudience);
					}
					HX_RELEASE(pMediaProfile);
				    }
				}
				HX_RELEASE(pOutputProfile);
			    }
			}
		    }
		}
		
		HX_RELEASE(m_ppPreviewSink[i]);
	    }
	}
    }
    
    HX_RELEASE(pIHXTInputPreviewSinkControl);
    HX_RELEASE(pInput);
    
    return res;
}

HX_RESULT 
CEncoderApp::SetupPreviews()
{
    HX_RESULT res = HXR_OK;
    
    cout << "Step 13: Setting up preview" << endl;
    
    cout << "   Would you like to enable/disable toggle the previews during the encode (y/n)? ";
    char szInputChoice[MAX_BUFFER_SIZE] = "";
    m_cInput >> szInputChoice;
    
    if (szInputChoice[0] == 'y' || szInputChoice[0] == 'Y')
    {
	m_bTogglePreviews = TRUE;
    }
    else
    {
	m_bTogglePreviews = FALSE;
    }
    
    return res;
}


HX_RESULT
CEncoderApp::SetupInputPreviews()
{
    HX_RESULT res = HXR_OK;
    
    cout << "Step 4a: Setting up input preview" << endl;
    
    res = SetupPreview(INPUT_PREVIEW);
    
    return res;
}

HX_RESULT
CEncoderApp::SetupOpenInputPreviews()
{
    HX_RESULT res = HXR_OK;
    
    if(m_nPreviewSinkCount)
    {
	cout << "Step 4b: Start previewing inputs now (y/n)?  ";
	res = SetupOpenPreviews(INPUT_PREVIEW);
    }
    
    return res;
}

HX_RESULT
CEncoderApp::SetupOpenPrefilterPreviews()
{
    HX_RESULT res = HXR_OK;
    
    if(m_nPreviewSinkCount)
    {
	cout << "Step 5b: Start previewing inputs and prefilters now (y/n)?  ";
	res = SetupOpenPreviews(PREFILTER_PREVIEW);
    }
    
    return res;
}

HX_RESULT
CEncoderApp::SetupOpenPreviews(ePreviewType nPreviewType)
{
    HX_RESULT res = HXR_OK;
    
    if(m_nPreviewSinkCount)
    {
	char szInputChoice[MAX_BUFFER_SIZE] = "";
	m_cInput >> szInputChoice;		    
	if (szInputChoice[0] == 'y' || szInputChoice[0] == 'Y')
	{
	    IHXTInput* pInput = NULL;
	    res = m_pJob->GetInput(&pInput);
	    
	    //	Handle the input and prefiltered previews
	    if(SUCCEEDED(res))
	    {
		IHXTInputPreviewControl* pIHXTInputPreviewControl = NULL;
		res = pInput->QueryInterface(IID_IHXTInputPreviewControl, (void**)&pIHXTInputPreviewControl);
		if(SUCCEEDED(res))
		{
		    res = pIHXTInputPreviewControl->Open();
		    if(SUCCEEDED(res))
		    {
			if(nPreviewType == INPUT_PREVIEW)
			{
			    m_bOpenInputPreviews = TRUE;
			}
			else if(nPreviewType == PREFILTER_PREVIEW)
			{
			    m_bOpenPrefilterPreviews = TRUE;
			}
		    }
		    
		    HX_RELEASE(pIHXTInputPreviewControl);
		}
	    }
	    HX_RELEASE(pInput);
	}
    }
    
    return res;
}

HX_RESULT
CEncoderApp::SetupClosePreviews()
{
    HX_RESULT res = HXR_OK;
    
    if(m_nPreviewSinkCount && (m_bOpenInputPreviews || m_bOpenPrefilterPreviews))
    {
	IHXTInput* pInput = NULL;
	res = m_pJob->GetInput(&pInput);
	
	if(SUCCEEDED(res))
	{
	    IHXTInputPreviewControl* pIHXTInputPreviewControl = NULL;
	    res = pInput->QueryInterface(IID_IHXTInputPreviewControl, (void**)&pIHXTInputPreviewControl);
	    if(SUCCEEDED(res))
	    {
		pIHXTInputPreviewControl->Close();
		
		HX_RELEASE(pIHXTInputPreviewControl);
	    }
	}
	HX_RELEASE(pInput);
    }
    
    return res;
}

HX_RESULT
CEncoderApp::SetupPreview(ePreviewType nPreviewType)
{
    HX_RESULT res = HXR_OK;
    
    
    INT32 nPreview = 0;	//  0 none, 1 audio, 2 video
    
    do
    {
	char szMessage[MAX_BUFFER_SIZE];
	
	if(nPreviewType == INPUT_PREVIEW)
	{
	    _snprintf(szMessage, MAX_BUFFER_SIZE, "  Would you like to preview %s data? ","input");
	}
	else if(nPreviewType == PREFILTER_PREVIEW)
	{
	    _snprintf(szMessage, MAX_BUFFER_SIZE, "  Would you like to preview %s data? ","prefiltered");
	}
	else if(nPreviewType == CODEC_PREVIEW)
	{
	    _snprintf(szMessage, MAX_BUFFER_SIZE, "  Would you like to preview %s data?   You will receive samples for each audience selected.","encoded");
	}
	
	IHXTInput* pInput = NULL;
	res = m_pJob->GetInput(&pInput);
	
	BOOL bInputHasAudio = FALSE;
	if (SUCCEEDED(res))
	    res = pInput->GetBool(kPropHasAudio, &bInputHasAudio);
	
	// Check if the input source has a video track
	BOOL bInputHasVideo = FALSE;
	if (SUCCEEDED(res))
	    res = pInput->GetBool(kPropHasVideo, &bInputHasVideo);
	
	cout << szMessage << endl;
	cout << "      0) No Preview" << endl;
	if(bInputHasAudio)
	{
	    cout << "      1) Audio Preview" << endl;
	}
	
	if(bInputHasVideo)
	{
	    cout << "      2) Video Preview" << endl;
	}
	cout << "   ?:  ";
	char szInputChoice[MAX_BUFFER_SIZE] = "";
	m_cInput >> szInputChoice;
	
	nPreview = atoi(szInputChoice);
	
	if((nPreview == 1 && bInputHasAudio) || 
	    (nPreview == 2 && bInputHasVideo))
	{
	    cout << "   Would you like to change the format of the previewed data (y/n)? ";
	    char szInputChoice2[MAX_BUFFER_SIZE] = "";
	    m_cInput >> szInputChoice2;
	    
	    UINT32 nSampleRate = 0;
	    UINT32 nSampleSize = 0;
	    UINT32 nNumChannels = 0;
	    
	    UINT32 nColorFormat = 0;
	    UINT32 nVideoWidth = 0;
	    UINT32 nVideoHeight = 0;
	    
	    if (szInputChoice2[0] == 'y' || szInputChoice2[0] == 'Y')
	    {
		if(nPreview == 1)
		{
		    res = SetupAudioPreviewFormat(&nSampleRate, &nSampleSize, &nNumChannels);
		}
		else if(nPreview == 2)
		{
		    res = SetupVideoPreviewFormat(&nColorFormat, &nVideoWidth, &nVideoHeight);
		}
	    }
	    
	    if(SUCCEEDED(res) && nPreview == 1)
	    {
		if(nPreviewType == INPUT_PREVIEW)
		{
		    res = CreatePreviewSinkInputAudio(nSampleRate, nSampleSize, nNumChannels);
		}
		else if(nPreviewType == PREFILTER_PREVIEW)
		{
		    res = CreatePreviewSinkPrefilteredAudio(nSampleRate, nSampleSize, nNumChannels);
		}
	    }
	    
	    if(SUCCEEDED(res) && nPreview == 2)
	    {
		if(nPreviewType == INPUT_PREVIEW)
		{
		    res = CreatePreviewSinkInputVideo(nColorFormat, nVideoWidth, nVideoHeight);
		}
		else if(nPreviewType == PREFILTER_PREVIEW)
		{
		    res = CreatePreviewSinkPrefilteredVideo(nColorFormat, nVideoWidth, nVideoHeight);
		}
		else if(nPreviewType == CODEC_PREVIEW)
		{
		    res = CreatePreviewSinkEncodedVideo(nColorFormat, nVideoWidth, nVideoHeight);
		}
	    }
	}
	else if(nPreview != 0)
	{
	    cout << endl;
	    cout << "   Invalid preview selection " << endl;
	}
	
	HX_RELEASE(pInput);
	
    } while(nPreview && SUCCEEDED(res));
    
    return res;
}

HX_RESULT
CEncoderApp::SetupAudioPreviewFormat(
				     UINT32* pnSampleRate, 
				     UINT32* pnSampleSize, 
				     UINT32* pnNumChannels)
{
    HX_RESULT res = HXR_OK;
    
    INT32 nSampleRate = 0;
    do
    {
	cout << "   Select the sample rate" << endl;
	cout << "      0) Use Default" << endl;
	cout << "      1) 22050 kHz" << endl;
	cout << "      2) 44100 kHz" << endl;
	cout << "   ?:  ";
	char szSampleRate[MAX_BUFFER_SIZE] = "";
	m_cInput >> szSampleRate;
	nSampleRate = atoi(szSampleRate);
	if(nSampleRate < 0 || 2 < nSampleRate)
	{
	    cout << endl;
	    cout << "   Invalid Sample Rate selected" << endl;
	}
    } while(nSampleRate < 0 || 2 < nSampleRate);
    
    switch(nSampleRate)
    {
    case 1: *pnSampleRate = 22050; break;
    case 2: *pnSampleRate = 44100; break;
    default: *pnSampleRate = nSampleRate; break;
    };
    
    INT32 nSampleSize = 0;
    do
    {
	cout << "   Select the sample size" << endl;
	cout << "      0) Use Default" << endl;
	cout << "      1) 8 Bit" << endl;
	cout << "      2) 16 Bit" << endl;
	cout << "   ?:  ";
	char szSampleSize[MAX_BUFFER_SIZE] = "";
	m_cInput >> szSampleSize;
	nSampleSize = atoi(szSampleSize);
	if(nSampleSize < 0 || 2 < nSampleSize)
	{
	    cout << endl;
	    cout << "   Invalid Sample Size selected" << endl;
	}
    } while(nSampleSize < 0 || 2 < nSampleSize);
    
    switch(nSampleSize)
    {
    case 1: *pnSampleSize = 8; break;
    case 2: *pnSampleSize = 16; break;
    default: *pnSampleSize = nSampleSize; break;
    };
    
    INT32 nNumChannels = 0;
    do
    {
	cout << "   Select the number of channels" << endl;
	cout << "      0) Use Default" << endl;
	cout << "      1) Mono" << endl;
	cout << "      2) Stereo" << endl;
	cout << "   ?:  ";
	char szNumChannels[MAX_BUFFER_SIZE] = "";
	m_cInput >> szNumChannels;
	nNumChannels = atoi(szNumChannels);
	if(nNumChannels < 0 || 2 < nNumChannels)
	{
	    cout << endl;
	    cout << "   Invalid Number of Channels selected" << endl;
	}
    } while(nNumChannels < 0 || 2 < nNumChannels);
    
    *pnNumChannels = nNumChannels;
    
    return res;
}

HX_RESULT
CEncoderApp::SetupVideoPreviewFormat(
				     UINT32* pnColorFormat, 
				     UINT32* pnVideoWidth, 
				     UINT32* pnVideoHeight)
{
    HX_RESULT res = HXR_OK;
    
    INT32 nColorFormat = 0;
    do
    {
	cout << "   Select a color format from the list below" << endl;
	cout << "      0) Use Default" << endl;
	cout << "      1) RGB 24 Inverted" << endl;
	cout << "      2) RGB 24 Non-Inverted" << endl;
	cout << "      ?:  ";
	char szColorFormat[MAX_BUFFER_SIZE] = "";
	m_cInput >> szColorFormat;
	nColorFormat = atoi(szColorFormat);
	if(nColorFormat < 0 || 2 < nColorFormat)
	{
	    cout << endl;
	    cout << "   Invalid Color Format selected" << endl;
	}
    } while(nColorFormat < 0 || 2 < nColorFormat);
    
    //	will change later to zero based enum for now is enum value + 1
    *pnColorFormat = nColorFormat;
    
    INT32 nVideoWidth = 0;
    do
    {
	cout << "   Enter video width (in range 32 to 2048 increments of 4) or 0 to use default: ";
	char szVideoWidth[MAX_BUFFER_SIZE] = "";
	m_cInput >> szVideoWidth;
	nVideoWidth = atoi(szVideoWidth);
	if((nVideoWidth != 0 && nVideoWidth < 32) || 
	    2048 < nVideoWidth ||
	    nVideoWidth % 4 != 0)
	{
	    cout << endl;
	    cout << "   Invalid video width selected" << endl;
	}
    } while((nVideoWidth != 0 && nVideoWidth < 32) || 
	2048 < nVideoWidth ||
	nVideoWidth % 4 != 0);
    
    *pnVideoWidth = nVideoWidth;
    
    INT32 nVideoHeight = 0;
    do
    {
	cout << "   Enter video height (in range 32 to 2048 increments of 4) or 0 to use default: ";
	char szVideoHeight[MAX_BUFFER_SIZE] = "";
	m_cInput >> szVideoHeight;
	nVideoHeight = atoi(szVideoHeight);
	if((nVideoHeight != 0 && nVideoHeight < 32) || 
	    2048 < nVideoHeight ||
	    nVideoHeight % 4 != 0)
	{
	    cout << endl;
	    cout << "   Invalid video height selected" << endl;
	}    
    } while((nVideoHeight != 0 && nVideoHeight < 32) || 
	2048 < nVideoHeight ||
	nVideoHeight % 4 != 0);
    
    *pnVideoHeight = nVideoHeight;
    
    return res;
}

HX_RESULT CEncoderApp::SetupPrefilters()
{
    HX_RESULT res = HXR_OK;
    
    cout << "Step 5: Setting up prefilters" << endl;
    
    IHXTInput* pInput = NULL;
    res = m_pJob->GetInput(&pInput);
    
    // Check if the input source has a video track
    BOOL bInputHasVideo = FALSE;
    if (SUCCEEDED(res))
	res = pInput->GetBool(kPropHasVideo, &bInputHasVideo);
    
    // Only add video prefilters if the input source has video
    if (SUCCEEDED(res) && bInputHasVideo)
    {
	char szInput[MAX_BUFFER_SIZE] = "";
	
	// Check if the black-level filter should added
	cout << "  Would you like to use the black-level prefilter (y/n)?: ";		
	m_cInput >> szInput;
	
	// Add the black-level prefilter
	if (szInput[0] == 'y' || szInput[0] == 'Y')
	{
	    // Create the property bag used to initialize the prefilter
	    IHXTPropertyBag* pInitParams = NULL;
	    if (SUCCEEDED(res))
		res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)&pInitParams);
	    
	    // Set plugin type
	    if (SUCCEEDED(res))
		res = pInitParams->SetString(kPropPluginType, kValuePluginTypePrefilterBlackLevel);
	    
	    // Set plugin name.  Note that plugin name is typically specified when creating prefilters -- this
	    // is different than the code to create inputs (plugin name wasn't specified then).  This is because
	    // there were several input readers that the Producer SDK selected from, while there is only a single
	    // prefilter that matches the black level prefilter plugin type.
	    if (SUCCEEDED(res))
		res = pInitParams->SetString(kPropPluginName, kValuePluginNamePrefilterBlackLevel);
	    
	    // Create the prefilter
	    IHXTPrefilter* pPrefilter = NULL;
	    if (SUCCEEDED(res))
		res = m_pFactory->BuildInstance(IID_IHXTPrefilter, pInitParams, (IUnknown**)&pPrefilter);
	    
	    // Add the prefilter to the input
	    if (SUCCEEDED(res))
		res = pInput->AddPrefilter(pPrefilter);
	    
	    HX_RELEASE(pInitParams);
	    HX_RELEASE(pPrefilter);
	}
    }
    
    // Check if the input source has an audio track
    BOOL bInputHasAudio = FALSE;
    if (SUCCEEDED(res))
	res = pInput->GetBool(kPropHasAudio, &bInputHasAudio);
    
    // Only add audio prefilters if the input source has audio
    if (SUCCEEDED(res) && bInputHasAudio)
    {
	char szInput[MAX_BUFFER_SIZE] = "";
	
	// Check if the audio limiter filter should added
	cout << "  Would you like to use the audio limiter prefilter (y/n)?: ";		
	m_cInput >> szInput;
	
	// Add the audio limiter
	if (szInput[0] == 'y' || szInput[0] == 'Y')
	{
	    // Create the property bag used to initialize the prefilter
	    IHXTPropertyBag* pInitParams = NULL;
	    if (SUCCEEDED(res))
		res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)&pInitParams);
	    
	    // Set plugin type
	    if (SUCCEEDED(res))
		res = pInitParams->SetString(kPropPluginType, kValuePluginTypePrefilterAudioGain);
	    
	    // Set plugin name.  Note that plugin name is typically specified when creating prefilters -- this
	    // is different than the code to create inputs (plugin name wasn't specified then).  This is because
	    // there were several input readers that the Producer SDK selected from, while there is only a single
	    // prefilter that matches the black level prefilter plugin type.
	    if (SUCCEEDED(res))
		res = pInitParams->SetString(kPropPluginName, kValuePluginNamePrefilterAudioGain);
	    
	    // Create the prefilter
	    IHXTPrefilter* pPrefilter = NULL;
	    if (SUCCEEDED(res))
		res = m_pFactory->BuildInstance(IID_IHXTPrefilter, pInitParams, (IUnknown**)&pPrefilter);
	    
	    // Set the gain level.  Note that this property could have also been set as a part of
	    // the pInitParams property bag.
	    if (SUCCEEDED(res))
		res = pPrefilter->SetDouble(kPropAudioLimiterGain, 6.0);
	    
	    // Add the prefilter to the input
	    if (SUCCEEDED(res))
		res = pInput->AddPrefilter(pPrefilter);
	    
	    HX_RELEASE(pInitParams);
	    HX_RELEASE(pPrefilter);
	}
    }
    
    HX_RELEASE(pInput);
    
    return res;
}

HX_RESULT 
CEncoderApp::SetupPrefilterPreviews()
{
    HX_RESULT res = HXR_OK;
    
    cout << "Step 5a: Setting up prefiltered preview" << endl;
    
    res = SetupPreview(PREFILTER_PREVIEW);
    
    return res;
}

HX_RESULT 
CEncoderApp::SetupOutput(INT32* pnOutputIndex)
{
    HX_RESULT res = HXR_OK;
    
    cout << "Step 6: Setting up outputs" << endl;
    
    BOOL bAddOutput = FALSE;
    
    if(*pnOutputIndex > 0)
    {
	char szInput[MAX_BUFFER_SIZE] = "";
	cout << "  Would you like to add another output (y/n)?: ";		
	m_cInput >> szInput;
	
	// Setup G2 push broadcast
	if (szInput[0] == 'y' || szInput[0] == 'Y')
	{
	    bAddOutput = TRUE;
	}
    }
    else
    {
	bAddOutput = TRUE;
    }
    
    if(bAddOutput)
    {
	// Create the output profile
	IHXTOutputProfile* pOutputProfile = NULL;
	if (SUCCEEDED(res))
	    res = m_pFactory->BuildInstance(IID_IHXTOutputProfile, NULL, (IUnknown**)&pOutputProfile);
	
	if (SUCCEEDED(res))
	    res = m_pJob->AddOutputProfile(pOutputProfile);
	
	if(SUCCEEDED(res))
	    *pnOutputIndex = m_pJob->GetOutputProfileCount() - 1;
	
	HX_RELEASE(pOutputProfile);
    }
    else
    {
	*pnOutputIndex = -1;
    }
    
    return res;
}

HX_RESULT CEncoderApp::SetupDestination(INT32 nOutputIndex)
{
    HX_RESULT res = HXR_OK;
    
    cout << "Step 6a: Setting up destinations" << endl;
    
    // Get the output profile
    IHXTOutputProfile* pOutputProfile = NULL;
    res = m_pJob->GetOutputProfile(nOutputIndex, &pOutputProfile);
    
    char szInput[MAX_BUFFER_SIZE] = "";
    
    // Check if output should be written to .rm file
    cout << "  Would you like to create an .rm file (y/n)?: ";		
    m_cInput >> szInput;
    
    // Create .rm file
    if (szInput[0] == 'y' || szInput[0] == 'Y')
    {		
	// Create the property bag used to initialize the destination
	IHXTPropertyBag* pInitParams = NULL;
	if (SUCCEEDED(res))
	    res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)&pInitParams);
	
	if (SUCCEEDED(res))
	    res = SetupFileWriterParams(pInitParams);
	
	// Create the destination
	IHXTDestination* pDest = NULL;
	if (SUCCEEDED(res))
	    res = m_pFactory->BuildInstance(IID_IHXTDestination, pInitParams, (IUnknown**)&pDest);
	
	// Set the destination on the output profile
	if (SUCCEEDED(res))
	    res = pOutputProfile->AddDestination(pDest);
	
	HX_RELEASE(pDest);
	HX_RELEASE(pInitParams);
    }
   
#if defined(_WIN32) || defined(_LINUX)
    // Check if output should be sent to G2 broadcast
    cout << "  Would you like to broadcast the encode using G2 push broadcast (y/n)?: ";		
    m_cInput >> szInput;
    
    // Setup G2 push broadcast
    if (szInput[0] == 'y' || szInput[0] == 'Y')
    {		
	// Create the property bag used to initialize the destination
	IHXTPropertyBag* pInitParams = NULL;
	if (SUCCEEDED(res))
	    res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)&pInitParams);
	
	if (SUCCEEDED(res))
	    res = SetupG2PushBroadcastParams(pInitParams);
	
	// Create the destination
	IHXTDestination* pDest = NULL;
	if (SUCCEEDED(res))
	    res = m_pFactory->BuildInstance(IID_IHXTDestination, pInitParams, (IUnknown**)&pDest);
	
	// Set the destination on the output profile
	if (SUCCEEDED(res))
	    res = pOutputProfile->AddDestination(pDest);
	
	HX_RELEASE(pDest);
	HX_RELEASE(pInitParams);
    }
    
    
    // Check if output should be sent to 9.0 push broadcast
    cout << "  Would you like to broadcast the encode using 9.0 push broadcast (y/n)?: ";		
    m_cInput >> szInput;
    
    // Setup 9.0 push broadcast
    if (szInput[0] == 'y' || szInput[0] == 'Y')
    {		
	// Create the property bag used to initialize the destination
	IHXTPropertyBag* pInitParams = NULL;
	if (SUCCEEDED(res))
	    res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)&pInitParams);
	
	if (SUCCEEDED(res))
	    res = Setup90PushBroadcastParams(pInitParams);
	
	// Create the destination
	IHXTDestination* pDest = NULL;
	if (SUCCEEDED(res))
	    res = m_pFactory->BuildInstance(IID_IHXTDestination, pInitParams, (IUnknown**)&pDest);
	
	// Set the destination on the output profile
	if (SUCCEEDED(res))
	    res = pOutputProfile->AddDestination(pDest);
	
	HX_RELEASE(pDest);
	HX_RELEASE(pInitParams);
    }
#endif    
    HX_RELEASE(pOutputProfile);
    
    return res;
}


HX_RESULT CEncoderApp::SetupFileWriterParams(IHXTPropertyBag* pInitParams)
{
    HX_RESULT res = HXR_OK;
    
    // Set the plugin type
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropPluginType, kValuePluginTypeDestinationFile);
    
    // Set the plugin name
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropPluginName, kValuePluginNameFileDestRealMedia);
    
    // Get the destination pathname
    cout << "  Enter output file pathname: ";
    char szOutputPathname[MAX_BUFFER_SIZE] = "";
    m_cInput >> szOutputPathname;
    
    // Set the pathname
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropOutputPathname, szOutputPathname);
    
    return res;
}


HX_RESULT CEncoderApp::SetupG2PushBroadcastParams(IHXTPropertyBag* pInitParams)
{
    HX_RESULT res = HXR_OK;
    
    // Set the plugin type
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropPluginType, kValuePluginTypeDestinationG2PushServer);
    
    // Set the plugin name
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropPluginName, kValuePluginNameServerG2Push);
    
    // Get the broadcast address
    cout << "  Enter broadcast address (server name or IP address): ";
    char szInput[MAX_BUFFER_SIZE] = "";
    m_cInput >> szInput;
    
    // Set the broadcast address
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropBroadcastAddress, szInput);
    
    // Get the stream name
    cout << "  Enter stream name (e.g., mybroadcast.rm): ";
    m_cInput >> szInput;
    
    // Set the stream name
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropBroadcastStreamname, szInput);
    
    // Get the broadcast port
    UINT32 ulPort = 0;
    cout << "  Enter port number (e.g., 4040): ";
    m_cInput >> ulPort;
    
    // Set the broadcast port
    if (SUCCEEDED(res))
	res = pInitParams->SetUint(kPropBroadcastPort, ulPort);
    
    // Get the username
    cout << "  Enter username:";
    m_cInput >> szInput;
    
    // Set the username
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropBroadcastUsername, szInput);
    
    // Get the password
    cout << "  Enter password:";
    m_cInput >> szInput;
    
    // Set the password
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropBroadcastPassword, szInput);
    
#ifdef _UNIX
    //Ignore sigpipe signal.
    //When broadcasting to server with tcp, SIGPIPE is signalled when
    //server closes it's tcp socket,but encoder writes to it's connected socket.
    signal(SIGPIPE,SIG_IGN);
#endif	
    return res;
}


HX_RESULT CEncoderApp::Setup90PushBroadcastParams(IHXTPropertyBag* pInitParams)
{
    HX_RESULT res = HXR_OK;
    
    // Set the plugin type
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropPluginType, kValuePluginTypeDestinationPushServer);
    
    // Set the plugin name
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropPluginName, kValuePluginNameServerRBS);
    
    // Set the object name
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropObjectName, "mybroadcast");
    
    // Get the broadcast address
    cout << "  Enter broadcast address (server name or IP address): ";
    char szInput[MAX_BUFFER_SIZE] = "";
    m_cInput >> szInput;
    
    // Set the broadcast address
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropBroadcastAddress, szInput);
    
    // Get the stream name
    cout << "  Enter stream name (e.g., mybroadcast.rm): ";
    m_cInput >> szInput;
    
    // Set the stream name
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropBroadcastStreamname, szInput);
    
    // Get the broadcast port
    UINT32 ulPort = 0;
    cout << "  Enter port number (e.g., 80): ";
    m_cInput >> ulPort;
    
    // Set the broadcast port
    if (SUCCEEDED(res))
	res = pInitParams->SetUint(kPropBroadcastPort, ulPort);
    
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropBroadcastAuthType, kValueAccountBased);
    
    // Get the username
    cout << "  Enter username:";
    m_cInput >> szInput;
    
    // Set the username
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropBroadcastUsername, szInput);
    
    // Get the password
    cout << "  Enter password:";
    m_cInput >> szInput;
    
    // Set the password
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropBroadcastPassword, szInput);
    
#ifdef _UNIX
    //Ignore sigpipe signal.
    //When broadcasting to server with tcp, SIGPIPE is signalled when
    //server closes it's tcp socket,but encoder writes to it's connected socket.
    signal(SIGPIPE,SIG_IGN);
#endif	
    return res;
}

HX_RESULT CEncoderApp::SetupMediaProfile(INT32 nOutputIndex)
{
    HX_RESULT res = HXR_OK;
    
    cout << "Step 7: Setting up media profile" << endl;
    
    // Get the output profile
    IHXTOutputProfile* pOutputProfile = NULL;
    res = m_pJob->GetOutputProfile(nOutputIndex, &pOutputProfile);
    
    // Create the media profile
    IHXTMediaProfile* pMediaProfile = NULL;
    if (SUCCEEDED(res))
        res = m_pFactory->BuildInstance(IID_IHXTMediaProfile, NULL, (IUnknown**)&pMediaProfile);
    
    // Set the media profile
    if (SUCCEEDED(res))
	res = pOutputProfile->SetMediaProfile(pMediaProfile);
    
    // Get the current video mode
    if (SUCCEEDED(res))
    {
	const char* szVideoMode = "";
	res = pMediaProfile->GetString(kPropVideoMode, &szVideoMode);
	
	if (SUCCEEDED(res))
	    cout << "  Current video mode: " << szVideoMode << endl;
    }
    
    // Set the video encoding mode
    UINT32 ulVideo = 0;
    cout << "  Please enter the video mode you would like for encoding.\n    " \
	"0) Normal\n    1) Smoothest Motion\n    2) Sharpest Image\n    3) Slideshow\n? ";
    m_cInput >> ulVideo;
    
    if (SUCCEEDED(res))
    {
	if (ulVideo == 0)
	    res = pMediaProfile->SetString(kPropVideoMode, kValueVideoModeNormal);
	else if (ulVideo == 1)
	    res = pMediaProfile->SetString(kPropVideoMode, kValueVideoModeSmooth);
	else if (ulVideo == 2)
	    res = pMediaProfile->SetString(kPropVideoMode, kValueVideoModeSharp);
	else if (ulVideo == 3)
	    res = pMediaProfile->SetString(kPropVideoMode, kValueVideoModeSharp);
	else
	    res = HXR_FAIL;
    }
    
    // Set output video size
    if ( SUCCEEDED(res) )
    {
	char szInput[MAX_BUFFER_SIZE] = "";

	// Check if the video should be resized
	cout << "  Resize the encoded video width/height (y/n)?: ";		
	m_cInput >> szInput;

	if (szInput[0] == 'y' || szInput[0] == 'Y')
	{	    
	    UINT32 ulOutputWidth = 0;
	    cout << "  Enter output video width (0 to leave alone): ";		
	    m_cInput >> ulOutputWidth;
	    
	    res = pMediaProfile->SetUint( kPropOutputWidth, ulOutputWidth);
	    	    
	    UINT32 ulOutputHeight = 0;
	    cout << "  Enter output video height (0 to leave alone): ";		
	    m_cInput >> ulOutputHeight;

	    if (SUCCEEDED(res))
		res = pMediaProfile->SetUint( kPropOutputHeight, ulOutputHeight);	    
	}	
    }
    
    // Get the current audio mode
    if (SUCCEEDED(res))
    {
	const char* szAudioMode = "";
	res = pMediaProfile->GetString(kPropAudioMode, &szAudioMode);
	
	if (SUCCEEDED(res))
	    cout << "  Current audio mode: " << szAudioMode << endl;
    }
    
    // Set the audio encoding mode
    UINT32 ulAudio = 0;
    cout << "  Please enter the audio mode you would like for encoding.\n" \
	"    0) Music\n    1) Voice\n? ";
    m_cInput >> ulAudio;
    
    if (SUCCEEDED(res))
    {
	if (ulAudio == 0)
	    res = pMediaProfile->SetString(kPropAudioMode, kValueAudioModeMusic);
	else if (ulAudio == 1)
	    res = pMediaProfile->SetString(kPropAudioMode, kValueAudioModeVoice);
	else
	    res = HXR_FAIL;
    }
    
    HX_RELEASE(pOutputProfile);
    HX_RELEASE(pMediaProfile);
    
    return res;
}



HX_RESULT CEncoderApp::SetupAudiences(INT32 nOutputIndex)
{
    HX_RESULT res = HXR_OK;
    
    cout << "Step 8: Setting up audiences" << endl;
    
    // Get the output profile
    IHXTOutputProfile* pOutputProfile = NULL;
    res = m_pJob->GetOutputProfile(nOutputIndex, &pOutputProfile);
    
    // Get the media profile
    IHXTMediaProfile* pMediaProfile = NULL;
    if (SUCCEEDED(res))
	res = pOutputProfile->GetMediaProfile(&pMediaProfile);
    
    // Create the audience enumerator
    IHXTAudienceEnumerator* pAudienceEnum = NULL;
    if (SUCCEEDED(res))
	res = m_pFactory->CreateInstance(IID_IHXTAudienceEnumerator, (IUnknown**)&pAudienceEnum);
    
    if (SUCCEEDED(res))
    {
	res = pAudienceEnum->SetProfileDirectory("audiences");
    }
    
    // Get the number of audiences in the profile directory
    UINT32 ulNumAudiences = 0;
    if (SUCCEEDED(res))
	ulNumAudiences = pAudienceEnum->GetAudienceCount();
    
    // Print all of the available audiences
    cout << "  Available audiences:" << endl;
    for (UINT32 i=0; i<ulNumAudiences && SUCCEEDED(res); i++)
    {
	IHXTAudience* pAudience = NULL;
	res = pAudienceEnum->GetAudience(i, &pAudience, NULL);
	
	if (SUCCEEDED(res))
	{
	    const char* szName = "";
	    res = pAudience->GetString(kPropObjectName, &szName);
	    
	    cout << "    " << i << ") " << szName << endl;
	}
	
	HX_RELEASE(pAudience);
    }
    
    // Select audiences to add to the media profile
    if (SUCCEEDED(res))
    {
	cout << "  Select an audience to add to your encode, -1 to stop adding audiences." << endl;
	cout << "  Select multiple audiences to create a SureStream encode." << endl;
	
	INT32 nAudience = -1;
	do
	{			
	    m_cInput >> nAudience;
	    if (nAudience != -1)
	    {
		// Get the audience from the enumerator
		IHXTAudience* pAudience = NULL;
		res = pAudienceEnum->GetAudience((UINT32)nAudience, &pAudience, NULL);
		
		//  Add Events to the audience if the user wants events
		if(SUCCEEDED(res) && m_bDoEvents)
		{
		    res = AddEventsToAudience(pAudience);
		}
		
		// Add the audience to the media profile
		if (SUCCEEDED(res))
		{
		    res = pMediaProfile->AddAudience(pAudience);
		}
		
		HX_RELEASE(pAudience);
	    }
	    
	} while (nAudience != -1 && SUCCEEDED(res));
    }	
    
    // Set some a/v codec properties
    if (SUCCEEDED(res))
    {		
	// Get max startup latency
	UINT32 ulMaxStartupLatency = 0;
	cout << "  Enter max video startup latency in seconds (typical value is 4): ";
	m_cInput >> ulMaxStartupLatency;
	
	// Get max time between keyframes
	UINT32 ulMaxTimeBetweenKeyframes = 0;
	cout << "  Enter max time between video keyframes in seconds (typical value is 10): ";
	m_cInput >> ulMaxTimeBetweenKeyframes;
	
	// Get encoding complexity
	UINT32 ulEncodeComplexity = 0;
	cout << "  Please enter the encoding complexity (speed vs. quality tradeoff):\n    " \
	    "0) Low\n    1) Medium\n    2) High\n? ";
	m_cInput >> ulEncodeComplexity;
	
	// Set the values on the appropriate streams within each audience
	UINT32 ulNumAudiences = pMediaProfile->GetAudienceCount();
	for (UINT32 i=0; i<ulNumAudiences && SUCCEEDED(res); i++)
	{
	    // Get the audience
	    IHXTAudience* pAudience = 0;
	    res = pMediaProfile->GetAudience(i, &pAudience);
	    
	    if (SUCCEEDED(res))
	    {
		// Iterate through all streams within the audience
		UINT32 ulNumStreams = pAudience->GetStreamConfigCount();
		for (UINT32 j=0; j<ulNumStreams && SUCCEEDED(res); j++)
		{
		    // Get the stream
		    IHXTStreamConfig* pStream = NULL;
		    res = pAudience->GetStreamConfig(j, &pStream);
		    
		    // Check if the current stream is a video stream
		    if (SUCCEEDED(res))
		    {
			const char* szStreamType = "";
			res = pStream->GetString(kPropPluginType, &szStreamType);
			
			// Set max startup latency and max time between keyframes on all video streams
			if (SUCCEEDED(res) && strcmp(szStreamType, kValuePluginTypeVideoStream) == 0)
			{
			    res = pStream->SetDouble(kPropMaxStartupLatency, ulMaxStartupLatency);
			    
			    if (SUCCEEDED(res))
				res = pStream->SetDouble(kPropMaxTimeBetweenKeyFrames, ulMaxTimeBetweenKeyframes);
			}
		    }
		    
		    // Set encoding complexity on audio/video streams
		    if (SUCCEEDED(res))
		    {
			switch (ulEncodeComplexity)
			{
			case 0:
			    res = pStream->SetString(kPropEncodingComplexity, kValueEncodingComplexityLow);
			    break;
			case 1:
			    res = pStream->SetString(kPropEncodingComplexity, kValueEncodingComplexityMedium);
			    break;
			default:
			case 2:
			    res = pStream->SetString(kPropEncodingComplexity, kValueEncodingComplexityHigh);
			    break;
			}
		    }
		    
		    HX_RELEASE(pStream);
		}
	    }
	    
	    HX_RELEASE(pAudience);
	}
    }
    
    HX_RELEASE(pOutputProfile);
    HX_RELEASE(pMediaProfile);
    HX_RELEASE(pAudienceEnum);
    
    return res;
}

HX_RESULT
CEncoderApp::AddEventsToAudience(IHXTAudience* pAudience)
{
    HX_RESULT res = HXR_OK;
    
    // Create the property bag used to initialize the stream
    IHXTPropertyBag* pInitParams = NULL;
    if (SUCCEEDED(res))
	res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)&pInitParams);
    
    // Set the plugin type
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropPluginType, kValuePluginTypeEventStream);
    
    // Set the plugin name
    if (SUCCEEDED(res))
	res = pInitParams->SetString(kPropPluginName, kValuePluginNameTransformRealEvents);
    
    // Create the stream
    IHXTStreamConfig* pStream = NULL;
    if (SUCCEEDED(res))
	res = m_pFactory->BuildInstance(IID_IHXTStreamConfig, pInitParams, (IUnknown**)&pStream);
    
    // Add the stream to the current audience (assumes the audience doesn't 
    // already have an event stream
    if (SUCCEEDED(res))
	res = pAudience->AddStreamConfig(pStream);
    
    HX_RELEASE(pStream);
    HX_RELEASE(pInitParams);
    
    return res;
}

HX_RESULT 
CEncoderApp::SetupCodecPreviews()
{
    HX_RESULT res = HXR_OK;
    
    cout << "Step 8a: Setting up encoded preview" << endl;
    
    res = SetupPreview(CODEC_PREVIEW);
    
    return res;
}

HX_RESULT CEncoderApp::SetupJob()
{
    HX_RESULT res = HXR_OK;
    
    cout << "Step 9: Setting up encoding job" << endl;
    
    char szInput[MAX_BUFFER_SIZE] = "";
    
    // Check if the encode should be two-pass
    cout << "  Perform two-pass encode (y/n)?: ";		
    m_cInput >> szInput;
    
    // Set two-pass encode state
    if (szInput[0] == 'y' || szInput[0] == 'Y')
    {
	res = m_pJob->SetBool(kPropEnableTwoPass, TRUE);
    }
    
    // Get metadata property bag
    IHXTPropertyBag* pMetadata = NULL;
    if (SUCCEEDED(res))
	res = m_pJob->GetMetadata(&pMetadata);
    
    // Get the title
    if (SUCCEEDED(res))
    {
	cout << "  Enter clip title: ";
	m_cInput >> szInput;
    }
    
    // Set the title	
    if (SUCCEEDED(res))
	res = pMetadata->SetString(kPropTitle, szInput);
    
    // Get the author
    if (SUCCEEDED(res))
    {
	cout << "  Enter clip author: ";
	m_cInput >> szInput;
    }
    
    // Set the author	
    if (SUCCEEDED(res))
	res = pMetadata->SetString(kPropAuthor, szInput);
    
    
    HX_RELEASE(pMetadata);
    
    return res;
}

HX_RESULT CEncoderApp::SerializeJob()
{
    HX_RESULT res = HXR_OK;
    
    cout << "Step 11: Serialize job -- optional step" << endl;
    
    char szInput[MAX_BUFFER_SIZE] = "";
    
    // Check if the job should be saved
    cout << "  Save job to file (y/n)?: ";		
    m_cInput >> szInput;
    
    // Serialize the job
    if (szInput[0] == 'y' || szInput[0] == 'Y')
    {
	cout << "  Enter job filename: ";		
	m_cInput >> szInput;
	
	// Get the serialization interface
	IHXTUserConfigFile* pSerializer = NULL;
	res = m_pJob->QueryInterface(IID_IHXTUserConfigFile, (void**)&pSerializer);
	
	// Save the job to a file
	if (SUCCEEDED(res))
	    res = pSerializer->WriteToFile(szInput);
	
	HX_RELEASE(pSerializer);
    }
    
    return res;
}


HX_RESULT CEncoderApp::StartEncoding()
{
    HX_RESULT res = HXR_OK;
    
    cout << "Step 14: Starting encode" << endl;
    
    // Start the encode -- this call will block until encoding has completed
    if (SUCCEEDED(res))
	res = m_pJob->StartEncoding();
    
    // Unsubscribe the event sink -- not needed anymore.  Preserve the StartEncoding() return code.
    if (m_pEventSink)
    {
	// Get the event manager
	IHXTEventManager* pEventMgr = NULL;
	HX_RESULT resEvent = m_pJob->GetEventManager(&pEventMgr);
	
	// Unsubscribe event sink
	if (SUCCEEDED(resEvent))
	    resEvent = pEventMgr->Unsubscribe(m_pEventSink);
	
	HX_RELEASE(pEventMgr);
	HX_RELEASE(m_pEventSink);
    }
         
    return res;
}

HX_RESULT CEncoderApp::Shutdown()
{
    cout << "Step 15: Shutting down sdk" << endl;
    
    //	Destroy the preview sink
    DestroyPreviewSinks();

    if (m_pFileLogObserver)
    {
	m_pFileLogObserver->Shutdown();
	HX_RELEASE(m_pFileLogObserver);
    }

    if (m_pSampleLogObserver)
    {
	m_pSampleLogObserver->Unsubscribe();
        HX_RELEASE(m_pSampleLogObserver);
    }
    
    HX_RELEASE(m_pEventPin);
    HX_RELEASE(m_pFileLogObserver);
    HX_RELEASE(m_pEventSink);
    HX_RELEASE(m_pJob);
        
    if (m_ppAudioStreamStats)
    {
	for (UINT32 i=0; i<m_ulNumberofAudioStats ; ++i)
	{
	    HX_RELEASE(m_ppAudioStreamStats[i]);
	}
	delete [] m_ppAudioStreamStats;
    }
    
    if (m_ppVideoStreamStats)
    {
	for (UINT32 i=0; i<m_ulNumberofVideoStats ; ++i)
	{
	    HX_RELEASE(m_ppVideoStreamStats[i]);
	}
	delete [] m_ppVideoStreamStats;
    }
    
    HX_RELEASE(m_pCurentAudioStatsBag);
    HX_RELEASE(m_pLifeTimeAudioStatsBag);
    HX_RELEASE(m_pCurentVideoStatsBag);
    HX_RELEASE(m_pLifeTimeVideoStatsBag);
    
    m_StatsWriter.Close();

    if (m_pLogSystem)
    {
	m_pLogSystem->Shutdown();
	HX_RELEASE(m_pLogSystem);
    }

    // Note that the factory should be the last object released as it may hold other DLLs 
    // in memory for other objects (such as the log system)
    HX_RELEASE(m_pFactory);
    
    if (m_RmsessionDLL)
    {
#ifdef _WINDOWS
	::FreeLibrary(m_RmsessionDLL);
#elif defined _LINUX
	dlclose(m_RmsessionDLL);
#elif defined _MAC_UNIX
	CFBundleUnloadExecutable(m_RmsessionDLL);
	CFRelease(m_RmsessionDLL);
#endif
    }
    
    return HXR_OK;
}


HX_RESULT CEncoderApp::SetupStatsInterfaces()
{
    HX_RESULT res = HXR_OK;
    
    cout << "Step 10: Save Stats -- optional step" << endl;
    
    char szInput[MAX_BUFFER_SIZE] = "";
    
    // Check if the stats should be saved
    cout << "  Save stats to file (y/n)?: ";		
    m_cInput >> szInput;
    
    // Serialize the job
    if (szInput[0] == 'y' || szInput[0] == 'Y')
    {
	cout << "  Enter stats filename: ";		
	m_cInput >> szInput;
	res = m_StatsWriter.Init(szInput);
    }
    else
    {
	return res;
    }
    
    
    if (SUCCEEDED(res) && m_pJob->GetOutputProfileCount() == 0 )
    {
	res = HXR_FAIL;
    }
    
    
    
    IHXTOutputProfile *pIOutputProfile = NULL;
    if (SUCCEEDED(res))
    {
	res = m_pJob->GetOutputProfile(0, &pIOutputProfile);
    }
    
    IHXTMediaProfile* pIRTAMediaProfile = NULL;
    if (SUCCEEDED(res))
    {
	res = pIOutputProfile->GetMediaProfile(&pIRTAMediaProfile);
    }
    
    
    UINT32 ulNumAudiences = 0;
    IHXTAudience *pIRTAAudience = NULL;
    if (SUCCEEDED(res))
    {
	ulNumAudiences = pIRTAMediaProfile->GetAudienceCount();
	m_ppAudioStreamStats = new IHXTStatistics* [ulNumAudiences];
	m_ppVideoStreamStats = new IHXTStatistics* [ulNumAudiences];
	
	if ( (m_ppAudioStreamStats == NULL) || (m_ppVideoStreamStats == NULL) )
	{
	    res = HXR_FAIL;
	}
	
	for (UINT32 i=0; SUCCEEDED(res) && i<ulNumAudiences ;i++ )
	{
	    BOOL bDisableAudio = FALSE;
	    BOOL bDisableVideo = FALSE;
	    BOOL bFound = FALSE;
	    res = pIRTAMediaProfile->GetAudience(i, &pIRTAAudience);
	    if (SUCCEEDED(res))
	    {
		IHXTStreamConfig *pIStreamConfig = NULL;
		
		// Get stats interface of selceted audio stream
		bFound = GetChosenStream(m_pJob,pIRTAAudience,kValuePluginTypeAudioStream,&pIStreamConfig);
		
		if (bFound)
		{
		    IHXTStatistics *pIStats;
		    res = pIStreamConfig->QueryInterface( IID_IHXTStatistics, (void**)&pIStats);
		    if (SUCCEEDED(res))
		    {
			m_ppAudioStreamStats[m_ulNumberofAudioStats] = pIStats;
			( m_ppAudioStreamStats[m_ulNumberofAudioStats])->AddRef();
			m_ulNumberofAudioStats++;
		    }
		    HX_RELEASE(pIStats);
		}
		HX_RELEASE(pIStreamConfig);
		
		// Get stats interface of selceted video stream
		bFound = GetChosenStream(m_pJob,pIRTAAudience,kValuePluginTypeVideoStream,&pIStreamConfig);
		
		if (bFound)
		{
		    IHXTStatistics *pIStats;
		    res = pIStreamConfig->QueryInterface( IID_IHXTStatistics, (void**)&pIStats);
		    if (SUCCEEDED(res))
		    {
			m_ppVideoStreamStats[m_ulNumberofVideoStats] = pIStats;
			(m_ppVideoStreamStats[m_ulNumberofVideoStats])->AddRef();
			m_ulNumberofVideoStats++;
		    }
		    HX_RELEASE(pIStats);
		}
		HX_RELEASE(pIStreamConfig);
		
	    }
	    
	    HX_RELEASE(pIRTAAudience);
	}
    }
    
    //Same stats bag should not be passed to audio and video stream.
    //As audio & video streams might have 2 properties with same name but different types.
    if (SUCCEEDED(res))
    {
	res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)&m_pCurentAudioStatsBag);
    }
    
    if (SUCCEEDED(res))
    {
	res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)&m_pLifeTimeAudioStatsBag);
    }
    
    if (SUCCEEDED(res))
    {
	res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)&m_pCurentVideoStatsBag);
    }
    
    if (SUCCEEDED(res))
    {
	res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)&m_pLifeTimeVideoStatsBag);
    }
    
    HX_RELEASE(pIOutputProfile);
    HX_RELEASE(pIRTAMediaProfile);
    
    if (SUCCEEDED(res))
    {
	m_bEnableStats = TRUE;
    }
    
    return res;
}

HX_RESULT CEncoderApp::UpdateStats()
{
    HX_RESULT res = HXR_OK;
    
    if (!m_bEnableStats)
	return res;
    
    //Curent statistics are received only after short interval after start of encoding
    //Stats can be got only in 2nd pass of 2 pass encode.
    //so it is ok to get a failure code.
    
    //Handle Audio Stream stats
    UINT32 i=0;
    for (i=0; i<m_ulNumberofAudioStats ; ++i)
    {
	res = m_ppAudioStreamStats[i]->GetCurrentStatistics( m_pCurentAudioStatsBag );
	if(SUCCEEDED(res))
	{
	    m_StatsWriter.WriteBag("CurrentAudioStats",m_pCurentAudioStatsBag);
	}
	
	res = m_ppAudioStreamStats[i]->GetLifeTimeStatistics( m_pLifeTimeAudioStatsBag );
	if(SUCCEEDED(res))
	{
	    m_StatsWriter.WriteBag("LifeTimeAudioStats",m_pLifeTimeAudioStatsBag);
	}
	
    }
    
    //Handle Video Stream Stats
    for (i=0; i<m_ulNumberofVideoStats ; ++i)
    {
	res = m_ppVideoStreamStats[i]->GetCurrentStatistics( m_pCurentVideoStatsBag );
	if(SUCCEEDED(res))
	{
	    m_StatsWriter.WriteBag("CurrentVideoStats",m_pCurentVideoStatsBag);
	}
	
	res = m_ppVideoStreamStats[i]->GetLifeTimeStatistics( m_pLifeTimeVideoStatsBag );
	if(SUCCEEDED(res))
	{
	    m_StatsWriter.WriteBag("LifeTimeVideoStats",m_pLifeTimeVideoStatsBag);
	}
	
    }
    
    
    return res;
}

HX_RESULT 
CEncoderApp::TogglePreviews()
{
    HX_RESULT res = HXR_OK;
    
    if(m_bTogglePreviews)
    {
	if(m_bSinkEnabled)
	{
	    cout << "Disabling the previews..." << endl;
	}
	else
	{
	    cout << "Enabling the previews..." << endl;
	}
	
	IHXTPreviewSinkControl3* pIHXTInputPreviewSinkControl = NULL;
	
	IHXTInput* pInput = NULL;
	res = m_pJob->GetInput(&pInput);
	if(SUCCEEDED(res))
	{
	    res = pInput->QueryInterface(IID_IHXTPreviewSinkControl3, (void**)&pIHXTInputPreviewSinkControl);
	}
	
	HX_RELEASE(pInput);
	
	if(SUCCEEDED(res))
	{
	    for(UINT32 i = 0; i < MAX_PREVIEW_SINK; i++)
	    {
		if(m_ppPreviewSink[i]) 
		{
		    if(m_ppPreviewSink[i]->GetPreviewSinkPosition() == kValueBeforeAllPrefilters ||
			m_ppPreviewSink[i]->GetPreviewSinkPosition() == kValueAfterAllPrefilters)
		    {
			if(m_bSinkEnabled)
			{
			    HX_RESULT res2 = pIHXTInputPreviewSinkControl->DisableSink(m_ppPreviewSink[i]);
			}
			else
			{
			    HX_RESULT res2 = pIHXTInputPreviewSinkControl->EnableSink(m_ppPreviewSink[i]);
			}
		    }
		    else if(m_ppPreviewSink[i]->GetPreviewSinkPosition() == kValueAfterCodec)
		    {
			IHXTPreviewSinkControl3* pIHXTOutputPreviewSinkControl = NULL;
			if(SUCCEEDED(res))
			{
			    IHXTOutputProfile* pOutputProfile = NULL;
			    for(UINT32 uiProfile = 0; uiProfile < m_pJob->GetOutputProfileCount(); uiProfile++)
			    {
				if(m_ppPreviewSink[i]->GetOutputProfile() == uiProfile)
				{
				    res = m_pJob->GetOutputProfile(uiProfile, &pOutputProfile);
				    if(SUCCEEDED(res))
				    {
					IHXTMediaProfile* pMediaProfile = NULL;
					res = pOutputProfile->GetMediaProfile(&pMediaProfile);
					if(SUCCEEDED(res))
					{
					    IHXTAudience* pAudience = NULL;
					    for(UINT32 uiAudience = 0; uiAudience < pMediaProfile->GetAudienceCount(); uiAudience++)
					    {
						if(m_ppPreviewSink[i]->GetAudience() == uiAudience)
						{
						    res = pMediaProfile->GetAudience(uiAudience, &pAudience);
						    if(SUCCEEDED(res))
						    {
							res = pAudience->QueryInterface(IID_IHXTPreviewSinkControl3, (void**)&pIHXTOutputPreviewSinkControl);
							if(SUCCEEDED(res))
							{
							    if(m_bSinkEnabled)
							    {
								HX_RESULT res2 = pIHXTOutputPreviewSinkControl->DisableSink(m_ppPreviewSink[i]);
							    }
							    else
							    {
								HX_RESULT res2 = pIHXTOutputPreviewSinkControl->EnableSink(m_ppPreviewSink[i]);
							    }
							    HX_RELEASE(pIHXTOutputPreviewSinkControl);
							}
						    }
						    
						    HX_RELEASE(pAudience);
						}
					    }
					}
					HX_RELEASE(pMediaProfile);
				    }
				}
				HX_RELEASE(pOutputProfile);
			    }
			}
		    }
		}
	    }
	}
	
	HX_RELEASE(pIHXTInputPreviewSinkControl);
    }
    
    m_bSinkEnabled = !m_bSinkEnabled;
    
    return res;
}

HX_RESULT 
CEncoderApp::SendEvent(BOOL bLast)
{
    HX_RESULT res = HXR_OK;
    
    if(m_pEventPin)
    {
	// Create event sample -- note that event sample comes from class factory
	IHXTEventSample* pEventSample = NULL;
	res = m_pFactory->CreateInstance(IID_IHXTEventSample, (IUnknown**)&pEventSample);
	
	// Set target URL
	if (SUCCEEDED(res))
	    res = pEventSample->SetAction(HXEventMediaSample_URL, "http://www.real.com", NULL);
	
	// Set start/finish time
	if (SUCCEEDED(res))
	    res = pEventSample->SetTime(0, 6000);
	
	if(SUCCEEDED(res) && bLast)
	    res = pEventSample->SetSampleFlags(HXT_SAMPLE_ENDOFSTREAM);
	
	// Encode sample
	if (SUCCEEDED(res))
	    res = m_pEventPin->EncodeSample( pEventSample );
	
	HX_RELEASE(pEventSample);
	printf( "Sent URL Event\n" );
    }
    
    return res;
}

HX_RESULT 
CEncoderApp::GetInput(IHXTInput** ppInput)
{
    HX_RESULT res = HXR_FAIL;
    
    if(!m_pJob)
    {
	res = HXR_UNEXPECTED;
	return res;
    }
    
    IHXTInput* pInput = NULL;
    res = m_pJob->GetInput(&pInput);
    if(SUCCEEDED(res))
    {
	IHXTInput2* pInput2 = NULL;
	res = pInput->QueryInterface(IID_IHXTInput2, (void**)&pInput2);
	if(SUCCEEDED(res))
	{
	    for(UINT32 nInput = 0; nInput < pInput2->GetInputCount() && SUCCEEDED(res); nInput++)
	    {
		IHXTInput* pInputContained = NULL;
		res = pInput2->GetInput(nInput, &pInputContained);
		if(SUCCEEDED(res))
		{
		    BOOL bAudioPresent = FALSE;
		    BOOL bVideoPresent = FALSE;
		    
		    pInputContained->GetBool(kPropHasAudio,&bAudioPresent);
		    pInputContained->GetBool(kPropHasVideo,&bVideoPresent);
		    if(bAudioPresent || bVideoPresent)
		    {
			*ppInput = pInputContained;
			(*ppInput)->AddRef();
			res = HXR_OK;
			HX_RELEASE(pInputContained);
			break;
		    }
		    
		    HX_RELEASE(pInputContained);
		}
	    }
	    
	    HX_RELEASE(pInput2);
	}
	else
	{
	    *ppInput = pInput;
	    (*ppInput)->AddRef();
	    res = HXR_OK;
	}
	
	HX_RELEASE(pInput);
    }
    
    return res;
}


BOOL GetChosenStream( IHXTEncodingJob * pIJob, IHXTAudience *pIRTAAudience, const char* pszStreamType,IHXTStreamConfig ** ppIRTAStreamConfig)
{
    
    HX_RESULT res = HXR_OK;
    if ( (pIJob == NULL) || (pIRTAAudience == NULL) || ( pszStreamType == NULL ) || ( ppIRTAStreamConfig == NULL ) )
    {
	return HXR_FAIL;
    }
    
    //Check if input has audio and video.
    BOOL bAudioPresent = FALSE;
    BOOL bVideoPresent = FALSE;
    BOOL bEncodeAudio  = TRUE;
    BOOL bEncodeVideo  = TRUE;
    
    IHXTInput* pInput = NULL;
    res = pIJob->GetInput(&pInput);
    if (SUCCEEDED(res))
    {
	pInput->GetBool(kPropHasAudio,&bAudioPresent);
	pInput->GetBool(kPropHasVideo,&bVideoPresent);
    }
    
    if (pIJob->GetOutputProfileCount() == 0 )
    {
	res = HXR_FAIL;
    }
    
    
    
    IHXTOutputProfile *pIOutputProfile = NULL;
    if (SUCCEEDED(res))
    {
	res = pIJob->GetOutputProfile(0, &pIOutputProfile);
    }
    
    IHXTMediaProfile* pIRTAMediaProfile = NULL;
    if (SUCCEEDED(res))
    {
	res = pIOutputProfile->GetMediaProfile(&pIRTAMediaProfile);
    }
    
    const char *pszAudioMode = NULL;
    if (SUCCEEDED(res))
    {
	BOOL bDisableAudio = FALSE;
	BOOL bDisableVideo = FALSE;
	
	pIOutputProfile->GetBool(kPropDisableAudio,&bDisableAudio);
	pIOutputProfile->GetBool(kPropDisableVideo,&bDisableVideo);
	
	bEncodeAudio = (bAudioPresent && !bDisableAudio);
	bEncodeVideo = (bVideoPresent && !bDisableVideo);
	
	if (bEncodeAudio)
	    pIRTAMediaProfile->GetString(kPropAudioMode, &pszAudioMode);
    }
    
    UINT32 ulNumStreamConfigs = 0;
    if (SUCCEEDED(res))
	ulNumStreamConfigs = pIRTAAudience->GetStreamConfigCount();
    
    IHXTStreamConfig *pIRTAStreamConfig = NULL;
    const CHAR *szPluginType = NULL;
    BOOL bStreamFound = FALSE;
    for(UINT32 ulstream=0; SUCCEEDED(res) && ulstream<ulNumStreamConfigs ; ++ulstream)
    {
	res = pIRTAAudience->GetStreamConfig( ulstream, &pIRTAStreamConfig);
	
	if (SUCCEEDED(res))
	{
	    res = pIRTAStreamConfig->GetString(kPropPluginType ,&szPluginType);
	}
	
	
	if ( SUCCEEDED(res) && bEncodeAudio && (strcmp(pszStreamType, kValuePluginTypeAudioStream) == 0)
	    && (strcmp(szPluginType, kValuePluginTypeAudioStream) == 0) )
	{
	    //We are encoding audio and current stream is audio stream and requested stream is audio.
	    
	    //Check if stream Context matches.
	    IHXTPropertyBag *pIStreamContextBag = NULL;
	    pIRTAStreamConfig->GetPropertyBag(kPropStreamContext, &pIStreamContextBag);
	    if (pIStreamContextBag)
	    {
		//Check if audio mode matches.
		const char* pszStreamAudioMode = NULL;
		res = pIStreamContextBag->GetString(kPropAudioMode, &pszStreamAudioMode);
		if (SUCCEEDED(res))
		{
		    if ( strcmp( pszAudioMode, pszStreamAudioMode)==0 )
		    {
			//Check if presentation type matches.
			const char* pszPresensentationType = NULL;
			res = pIStreamContextBag->GetString(kPropPresentationType, &pszPresensentationType);
			if (SUCCEEDED(res))
			{
			    if (bEncodeAudio && bEncodeVideo)
			    {
				if (strcmp(pszPresensentationType,kValuePresentationAudioVideo) == 0)
				{
				    bStreamFound = TRUE;
				}
			    }
			    else if (bEncodeAudio && !bEncodeVideo)
			    {
				if (strcmp(pszPresensentationType,kValuePresentationAudioOnly) == 0)
				{
				    bStreamFound = TRUE;
				}
				
			    }
			}
		    }
		}
	    }
            HX_RELEASE(pIStreamContextBag);
        }
        
	
	
	if ( SUCCEEDED(res) && bEncodeVideo && (strcmp(pszStreamType, kValuePluginTypeVideoStream) == 0) && 
	    (strcmp(szPluginType, kValuePluginTypeVideoStream) == 0) )
	{
	    //We are encoding video and looking for video stream and this video stream
	    bStreamFound = TRUE;
	}
	
	if ( bStreamFound == TRUE )
	{
	    *ppIRTAStreamConfig = pIRTAStreamConfig;
	    break;
	}
	HX_RELEASE(pIRTAStreamConfig);
    }
    
    HX_RELEASE(pInput);
    HX_RELEASE(pIOutputProfile);
    HX_RELEASE(pIRTAMediaProfile);
    
    if ( bStreamFound == TRUE )
    {
	return TRUE;
    }
    else
    {
	return FALSE;
    }
}

HX_RESULT CEncoderApp::DumpCodecsInfo()
{
    HX_RESULT res = HXR_OK;
    char szInput[MAX_BUFFER_SIZE] = "";
    
    // Check if the codec info should be saved
    cout << "Step 12: Dumping Codec Info -- optional step" << endl;
    
    cout << "  Dump Codec Info (y/n)?: ";		
    m_cInput >> szInput;
    
    // Serialize the job
    if (szInput[0] == 'y' || szInput[0] == 'Y')
    {
	DumpAllCodecInfo();
	DumpAllAudioCodecInfo();
	DumpRealNetworksVideoCodecInfo();
    }
    
    return res;
}

HX_RESULT CEncoderApp::DumpAllCodecInfo()
{
    HX_RESULT res = HXR_OK;
    IUnknown* pICodecInfoUnknown = NULL;
    IHXTPluginInfoManager* pICodecInfoManger = NULL;
    res = m_pFactory->CreateInstance(CLSID_IHXTCodecInfoManager, (IUnknown**)&pICodecInfoUnknown);
    if (SUCCEEDED(res))
    {
	res = pICodecInfoUnknown->QueryInterface(IID_IHXTPluginInfoManager,(void **)&pICodecInfoManger);
    }
    
    IHXTPluginInfoEnum *pIPluginInfoEnum = NULL;
    if (SUCCEEDED(res))
    {
	res = pICodecInfoManger->GetPluginInfoEnum(NULL, &pIPluginInfoEnum);
    }
    
    cout << "Dumping All Codecs Info to codecinfo.txt" << endl;
    
    CFileWriter CodecFileWriter;
    res = CodecFileWriter.Init( "codecinfo.txt");
    
    IHXTPropertyBag *pCodecInfoBag = NULL;
    for (UINT32 i=0; SUCCEEDED(res) && i<pIPluginInfoEnum->GetCount(); i++)
    {
	res = pIPluginInfoEnum->GetPluginInfoAt(i,&pCodecInfoBag);
	if (SUCCEEDED(res))
	    CodecFileWriter.WriteBag("CodecInfo",pCodecInfoBag);
	
	HX_RELEASE(pCodecInfoBag);
    }
    
    CodecFileWriter.Close();
    HX_RELEASE(pIPluginInfoEnum);
    HX_RELEASE(pICodecInfoManger);
    HX_RELEASE(pICodecInfoUnknown);
    return res;
}


HX_RESULT CEncoderApp::DumpAllAudioCodecInfo()
{
    HX_RESULT res = HXR_OK;
    IUnknown* pICodecInfoUnknown = NULL;
    IHXTPluginInfoManager* pICodecInfoManger = NULL;
    res = m_pFactory->CreateInstance(CLSID_IHXTCodecInfoManager, (IUnknown**)&pICodecInfoUnknown);
    if (SUCCEEDED(res))
    {
	res = pICodecInfoUnknown->QueryInterface(IID_IHXTPluginInfoManager,(void **)&pICodecInfoManger);
    }
    
    IHXTPropertyBag *pIQueryBag= NULL;
    if (SUCCEEDED(res))
    {
	res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)&pIQueryBag);
    }
    
    if (SUCCEEDED(res))
    {
	res = pIQueryBag->SetString( kPropPluginType, kValuePluginTypeAudioStream );
    }
    
    IHXTPluginInfoEnum *pIPluginInfoEnum = NULL;
    if (SUCCEEDED(res))
    {
	res = pICodecInfoManger->GetPluginInfoEnum(pIQueryBag, &pIPluginInfoEnum);
    }
    
    cout << "Dumping All Audio Codecs Info to audiocodecinfo.txt" << endl;
    
    CFileWriter CodecFileWriter;
    res = CodecFileWriter.Init( "audiocodecinfo.txt");
    
    IHXTPropertyBag *pCodecInfoBag = NULL;
    for (UINT32 i=0; SUCCEEDED(res) && i<pIPluginInfoEnum->GetCount(); i++)
    {
	res = pIPluginInfoEnum->GetPluginInfoAt(i,&pCodecInfoBag);
	if (SUCCEEDED(res))
	    CodecFileWriter.WriteBag("CodecInfo",pCodecInfoBag);
	
	HX_RELEASE(pCodecInfoBag);
    }
    
    CodecFileWriter.Close();
    HX_RELEASE(pIQueryBag);
    HX_RELEASE(pIPluginInfoEnum);
    HX_RELEASE(pICodecInfoManger);
    HX_RELEASE(pICodecInfoUnknown);
    return res;
}

HX_RESULT CEncoderApp::DumpRealNetworksVideoCodecInfo()
{
    HX_RESULT res = HXR_OK;
    IUnknown* pICodecInfoUnknown = NULL;
    IHXTPluginInfoManager* pICodecInfoManger = NULL;
    res = m_pFactory->CreateInstance(CLSID_IHXTCodecInfoManager, (IUnknown**)&pICodecInfoUnknown);
    if (SUCCEEDED(res))
    {
	res = pICodecInfoUnknown->QueryInterface(IID_IHXTPluginInfoManager,(void **)&pICodecInfoManger);
    }
    
    IHXTPropertyBag *pIQueryBag= NULL;
    if (SUCCEEDED(res))
    {
	res = m_pFactory->CreateInstance(IID_IHXTPropertyBag, (IUnknown**)&pIQueryBag);
    }
    
    if (SUCCEEDED(res))
    {
	res = pIQueryBag->SetString( kPropPluginType, kValuePluginTypeVideoStream );
    }
    
    if (SUCCEEDED(res))
    {
	res = pIQueryBag->SetString( kPropPluginName, kValuePluginNameCodecRealVideo );
    }
    
    IHXTPluginInfoEnum *pIPluginInfoEnum = NULL;
    if (SUCCEEDED(res))
    {
	res = pICodecInfoManger->GetPluginInfoEnum(pIQueryBag, &pIPluginInfoEnum);
    }
    
    cout << "Dumping All RealNetworks Video Codecs Info to rnvidecodecinfo.txt" << endl;
    CFileWriter CodecFileWriter;
    res = CodecFileWriter.Init( "rnvidecodecinfo.txt");
    
    IHXTPropertyBag *pCodecInfoBag = NULL;
    for (UINT32 i=0; SUCCEEDED(res) && i<pIPluginInfoEnum->GetCount(); i++)
    {
	res = pIPluginInfoEnum->GetPluginInfoAt(i,&pCodecInfoBag);
	if (SUCCEEDED(res))
	    CodecFileWriter.WriteBag("CodecInfo",pCodecInfoBag);
	
	HX_RELEASE(pCodecInfoBag);
    }
    
    CodecFileWriter.Close();
    HX_RELEASE(pIQueryBag);
    HX_RELEASE(pIPluginInfoEnum);
    HX_RELEASE(pICodecInfoManger);
    HX_RELEASE(pICodecInfoUnknown);
    return res;
}
