//
// Copyright (c) 1998-2001 B2C2, Inc.  All Rights Reserved.
//
//
// THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF B2C2, INCORPORATED.
// The copyright notice above does not evidence any
// actual or intended publication of such source code.
//
// This file is proprietary source code of B2C2, Incorporated. and is released pursuant to and
// subject to the restrictions of the non-disclosure agreement and license contract entered
// into by the parties.
//
//
// File: TestAVFilter.cpp
//
//

#include <stdio.h>
#include <tchar.h>

#include <Dshow.h>

#include "B2C2_Defs.h"
#include "B2C2_Guids.h"
#include "b2c2mpeg2adapter.h"
#include "IB2C2MPEG2AVCtrl.h"
#include "IB2C2MPEG2TunerCtrl.h"

// Command line usage message

#define	USAGE_MSG "\
\n\
Usage: TestAVFilter Flags [Options]\n\
\n\
Note: Flags and Options must be separated from values with blanks;\n\
      e.g. TestDataFilter -i c\n\
\n\
Flags:\n\
-i    Tuner type; valid values are: c (cable), s (satellite)\n\
      t (Terrestrial DVB) and a (Terrestrial ATSC) \n\
-f    Frequency (Transponder) in MHz (c, s); e.g. 350\n\
-s    Symbol Rate in Ks/s (c, s); e.g. 6111\n\
-m    Modulation in QAM (c); valid values are:\n\
      4, 16, 32, 64, 128, 256\n\
-l    LNB Frequency in MHz (s); note: must be less than Transponder\n\
      Frequency specified with -f\n\
-e    FEC (s); valid values are:\n\
      1/2, 2/3, 3/4, 5/6, 7/8, auto\n\
-o    Polarity (s); valid values are: h (horizontal), v (vertical)\n\
-k    LNB Selection in KHz(s); valid values are: 0, 22, 33, 44\n\
-d    Diseqc (s); valid values are:\n\
      n (none), a, b, a/a, a/b, b/a, b/b\n\
-g    Guard Interval (t): valid values are:\n\
	  1/32, 1/16, 1/8, 1/4, Auto\n\
-b    Bandwidth (t): valid values are:\n\
	  6, 7, 8\n\
-pa   Audio PID as integer or hex. (c, s); e.g. 164, 0xa4 or 0XA4\n\
-pv   Video PID as integer or hex. (c, s); e.g. 164, 0xa4 or 0XA4\n\
\n\
Options:\n\
-h    Help\n"

// Macro for COM de-allocation

// Struct used for passing tuner parameters

typedef struct
{	
	long			lTunerType;
	bool			bTunerTypeSet;

	long			lFrequency;
	bool			bFrequencySet;
	long			lSymbolRate;
	bool			bSymbolRateSet;
	eModulation		eModulation;
	bool			bModulationSet;
	bool			bGuardIntervalSet;
	long			lLNB;
	bool			bLNBSet;
	eFEC			eFEC;
	bool			bFECSet;
	ePolarity		ePolarity;
	bool			bPolaritySet;
	eLNBSelection	eLNBSelection;
	bool			bLNBSelectionSet;
	eDiseqc			eDiseqc;
	bool			bDiseqcSet;
	eGuardInterval	eGuardInterval;
	eBandwidth		eBandwidth;
	bool			bBandwidthSet;

	long			lAudioPID;
	bool			bAudioPIDSet;
	long			lVideoPID;
	bool			bVideoPIDSet;
} SParams;

// Prototypes

int CheckCommandLineArgs (int argc, char * argv[], SParams * psParams);
UINT  __stdcall UpdateVideoAttributes (MPEG2_VIDEO_INFO * pInfo);


int main(int argc, char * argv[])
{
	SParams					sParams;
	IGraphBuilder *			pFilterGraph = NULL;
	IB2C2MPEG2AVCtrl2 *		pB2C2FilterAVCtrl = NULL;
	IB2C2MPEG2TunerCtrl3 *	pB2C2FilterTunerCtrl = NULL;
	IPin *					pPinOutAudio = NULL;
	IPin *					pPinOutVideo = NULL;
	IPin *					pPinOut = NULL;
	IMediaControl *			pMediaControl = NULL;
	IMediaEvent *			pMediaEvent = NULL;
	LONG					lEventCode;
	HRESULT					hr;

	// Initializations.

	sParams.bTunerTypeSet = FALSE;

	sParams.bFrequencySet = FALSE;
	sParams.bSymbolRateSet = FALSE;
	sParams.bLNBSet = FALSE;
	sParams.bFECSet = FALSE;
	sParams.bPolaritySet = FALSE;
	sParams.bLNBSelectionSet = FALSE;
	sParams.bDiseqcSet = FALSE;
	sParams.bModulationSet = FALSE;
	sParams.bGuardIntervalSet = FALSE;
	sParams.bBandwidthSet = FALSE;
	sParams.lAudioPID = 0;
	sParams.lVideoPID = 0;

	// Check command line arguments.

	if (CheckCommandLineArgs(argc, argv, &sParams) < 0)
	{
		return 1;	// *** APPLICATION EXIT POINT
	}

	if (sParams.bTunerTypeSet == FALSE)
	{
		fprintf (stderr, "\nError: Tuner type must be specified\n");
		fprintf (stderr, USAGE_MSG);

		return 1;	// *** APPLICATION EXIT POINT
	}

	// **********************************************************************
	// *** Initialize B2C2 Adapter
	// **********************************************************************

	B2C2MPEG2Adapter	b2c2Adapter("");	// No name requred on Windows

	hr = b2c2Adapter.Initialize();

	if (FAILED(hr)) 
	{
		fprintf(stderr, "%s failed (#%08X)\n", 
						b2c2Adapter.GetLastErrorText(), b2c2Adapter.GetLastError());

		return 1;	// *** APPLICATION EXIT POINT
	}

	// Note: Through the destructor COM will be correctly de-allocated.

	pB2C2FilterAVCtrl = b2c2Adapter.GetAvControl();
	pB2C2FilterTunerCtrl = b2c2Adapter.GetTunerControl();

	// Get Filter graph, to perform functions not implemented to B2C2 Adapter.

	// If Initialize succeded, there is a filter graph!

	pFilterGraph = b2c2Adapter.GetFilterGraph();

	// Get the audio and video PINs

	hr = b2c2Adapter.GetAudioVideoOutPins( &pPinOutAudio, &pPinOutVideo);

	if (FAILED(hr)) 
	{
		fprintf(stderr, "%s failed (#%08X)\n", 
						b2c2Adapter.GetLastErrorText(), b2c2Adapter.GetLastError());

		return 1;	// *** APPLICATION EXIT POINT
	}

	if (pPinOutAudio == NULL)
	{
		fprintf(stderr, "Cannot locate B2C2 Filter Audio pin\n");

		return 1;	// *** APPLICATION EXIT POINT
	}

	if (pPinOutVideo == NULL)
	{
		fprintf(stderr,"Cannot locate B2C2 Filter Video pin\n");

		return 1;	// *** APPLICATION EXIT POINT
	}

	// **********************************************************************
	// *** Set callback function for video mode
	// **********************************************************************

	hr = pB2C2FilterAVCtrl->SetCallbackForVideoMode(&UpdateVideoAttributes);

	if(FAILED(hr))
	{
		fprintf(stderr,"B2C2 MPEG2 Filter Audio/Video Ctrl. Interface 2 SetCallbackForVideoMode method failed, error: 0x%8.8x\n", hr);

		return 1;	// *** APPLICATION EXIT POINT
	}
	else
	{
		fprintf(stdout,"Successfully set callback function\n");
		
	}

	// Set tuner parameters depending on type of tuner specified.
	// Note: Some overlap exists between Satellite and Cable settings (specifically,
	// Frequency and Symbol Rate).

	switch (sParams.lTunerType)
	{

	// **********************************************************************
	// *** Terrestrial DVB
	// **********************************************************************

	case TUNER_TERRESTRIAL_DVB:
	{
		// If frequency and bandwidth are not set, do not attempt to tune.
		if (sParams.bFrequencySet == FALSE ||
			sParams.bBandwidthSet == FALSE )
		{
			fprintf (stderr, "\nError: One or more flags have not been specified for this tuner type\n");
			fprintf (stderr, USAGE_MSG);

			return 1;	// *** APPLICATION EXIT POINT
		}

		// Set the specified Frequency
		pB2C2FilterTunerCtrl->SetFrequencyKHz(sParams.lFrequency);
		
		if(FAILED(hr))
		{
			fprintf(stderr,"B2C2 MPEG2 Filter Tuner Ctrl. Interface SetFrequency method failed, error: 0x%8.8x\n", hr);
			
			return 1;	// *** APPLICATION EXIT POINT
		}

		// Check to set the specified Guard Interval
		if (sParams.bGuardIntervalSet == TRUE)
		{
			pB2C2FilterTunerCtrl->SetGuardInterval(sParams.eGuardInterval);
		
			if(FAILED(hr))
			{
				fprintf(stderr,"B2C2 MPEG2 Filter Tuner Ctrl. Interface SetGuardInterval method failed, error: 0x%8.8x\n", hr);
			
				return 1;	// *** APPLICATION EXIT POINT
			}
		}

		// Set the specified Bandwidth
		pB2C2FilterTunerCtrl->SetBandwidth(sParams.eBandwidth);
		
		if (FAILED (hr))
		{
			fprintf (stderr, "B2C2 MPEG2 Filter Tuner Ctrl. Interface SetGuardInterval method failed (#%08X)!\n", hr);

			return 1;	// *** APPLICATION EXIT POINT
		}

		break;
	}

	// **********************************************************************
	// *** ATSC
	// **********************************************************************
	
	case TUNER_TERRESTRIAL_ATSC:
	{
		// Make sure all flags have been set

		if (sParams.bFrequencySet == FALSE)
		{
			fprintf (stderr, "\nError: Frequency has not been specified\n");
			fprintf (stderr, USAGE_MSG);

			return 1;	// *** APPLICATION EXIT POINT
		}

		pB2C2FilterTunerCtrl->SetFrequencyKHz (sParams.lFrequency);
		
		if (FAILED (hr))
		{
			fprintf (stderr, "B2C2 MPEG2 Filter Tuner Ctrl. Interface SetFrequency method failed (#%08X)!\n", hr);

			return hr;	// *** APPLICATION EXIT POINT
		}

		break;
	}

	// **********************************************************************
	// *** Cable
	// **********************************************************************
	
	case TUNER_CABLE:

		// Make sure all flags have been set

		if (sParams.bFrequencySet == FALSE ||
			sParams.bSymbolRateSet == FALSE ||
			sParams.bModulationSet == FALSE)
		{
			fprintf (stderr, "\nError: One or more flags have not been specified for this tuner type\n");
			fprintf (stderr, USAGE_MSG);

			return 1;	// *** APPLICATION EXIT POINT
		}

		pB2C2FilterTunerCtrl->SetFrequencyKHz(sParams.lFrequency);
		
		if(FAILED(hr))
		{
			fprintf(stderr,"B2C2 MPEG2 Filter Tuner Ctrl. Interface SetFrequency method failed, error: 0x%8.8x\n", hr);
			
			return 1;	// *** APPLICATION EXIT POINT
		}

		pB2C2FilterTunerCtrl->SetSymbolRate(sParams.lSymbolRate);
		
		if(FAILED(hr))
		{
			fprintf(stderr,"B2C2 MPEG2 Filter Tuner Ctrl. Interface SetSymbolRate method failed, error: 0x%8.8x\n", hr);
			
			return 1;	// *** APPLICATION EXIT POINT
		}

		pB2C2FilterTunerCtrl->SetModulation(sParams.eModulation);

		if(FAILED(hr))
		{
			fprintf(stderr,"B2C2 MPEG2 Filter Tuner Ctrl. Interface SetModulation method failed, error: 0x%8.8x\n", hr);
			
			return 1;	// *** APPLICATION EXIT POINT
		}

		break;

	// **********************************************************************
	// *** Satellite
	// **********************************************************************
	
	case TUNER_SATELLITE:

		// Make sure all flags have been set

		if (sParams.bFrequencySet == FALSE ||
			sParams.bSymbolRateSet == FALSE ||
			sParams.bLNBSet == FALSE ||
			sParams.bFECSet == FALSE ||
			sParams.bPolaritySet == FALSE ||
			sParams.bLNBSelectionSet == FALSE ||
			sParams.bDiseqcSet == FALSE)
		{
			fprintf (stderr, "\nError: One or more flags have not been specified for this tuner type\n");
			fprintf (stderr, USAGE_MSG);

			return 1;	// *** APPLICATION EXIT POINT
		}

		pB2C2FilterTunerCtrl->SetFrequencyKHz(sParams.lFrequency);
		
		if(FAILED(hr))
		{
			fprintf(stderr,"B2C2 MPEG2 Filter Tuner Ctrl. Interface SetFrequency method failed, error: 0x%8.8x\n", hr);
			
			return 1;	// *** APPLICATION EXIT POINT
		}

		pB2C2FilterTunerCtrl->SetSymbolRate(sParams.lSymbolRate);
		
		if(FAILED(hr))
		{
			fprintf(stderr,"B2C2 MPEG2 Filter Tuner Ctrl. Interface SetSymbolRate method failed, error: 0x%8.8x\n", hr);
			
			return 1;	// *** APPLICATION EXIT POINT
		}

		pB2C2FilterTunerCtrl->SetLnbFrequency(sParams.lLNB);

		if(FAILED(hr))
		{
			fprintf(stderr,"B2C2 MPEG2 Filter Tuner Ctrl. Interface SetLnbFrequency method failed, error: 0x%8.8x\n", hr);
			
			return 1;	// *** APPLICATION EXIT POINT
		}

		pB2C2FilterTunerCtrl->SetFec(sParams.eFEC);

		if(FAILED(hr))
		{
			fprintf(stderr,"B2C2 MPEG2 Filter Tuner Ctrl. Interface SetFec method failed, error: 0x%8.8x\n", hr);
			
			return 1;	// *** APPLICATION EXIT POINT
		}

		pB2C2FilterTunerCtrl->SetPolarity(sParams.ePolarity);

		if(FAILED(hr))
		{
			fprintf(stderr,"B2C2 MPEG2 Filter Tuner Ctrl. Interface SetPolarity method failed, error: 0x%8.8x\n", hr);
			
			return 1;	// *** APPLICATION EXIT POINT
		}

		pB2C2FilterTunerCtrl->SetLnbKHz(sParams.eLNBSelection);

		if(FAILED(hr))
		{
			fprintf(stderr,"B2C2 MPEG2 Filter Tuner Ctrl. Interface SetLnbKHz method failed, error: 0x%8.8x\n", hr);
			
			return 1;	// *** APPLICATION EXIT POINT
		}

		pB2C2FilterTunerCtrl->SetDiseqc(sParams.eDiseqc);

		if(FAILED(hr))
		{
			fprintf(stderr,"B2C2 MPEG2 Filter Tuner Ctrl. Interface SetDiseqc method failed, error: 0x%8.8x\n", hr);

			return 1;	// *** APPLICATION EXIT POINT
		}

		break;

	default:
		// Following is an internal error (should not happen)

		fprintf (stderr, "\nError: Internal error; incorrect tuner type value detected\n");
		fprintf (stderr, USAGE_MSG);

		return 1;	// *** APPLICATION EXIT POINT
	}

	// Send settings to tuner.

	hr = pB2C2FilterTunerCtrl->SetTunerStatus();

	if(FAILED(hr))
	{
		fprintf(stderr, "B2C2 MPEG2 Filter Tuner Ctrl. Interface SetTunerStatus method failed, error: 0x%8.8x\n", hr);

		return 1;	// *** APPLICATION EXIT POINT
	}

	// Check for lock.

	hr = pB2C2FilterTunerCtrl->CheckLock();

	if(FAILED(hr))
	{
		fprintf(stderr, "B2C2 MPEG2 Filter Tuner Ctrl. Interface CheckLock method failed, error: 0x%8.8x\n", hr);

		return 1;	// *** APPLICATION EXIT POINT
	}

	// Set B2C2 MPEG2 Filter Audio/Video PIDs.

	// 2) Set Audio and Video PIDs.

	hr = pB2C2FilterAVCtrl->SetAudioVideoPIDs(sParams.lAudioPID, sParams.lVideoPID);

	if(FAILED(hr))
	{
		fprintf(stderr,"B2C2 MPEG2 Filter Audio/Video Ctrl. Interface SetAudioVideoPIDs method failed, error: 0x%8.8x\n", hr);
		
		return 1;	// *** APPLICATION EXIT POINT
	}

	// Build graph from Video output pin.
	
	if ( sParams.lVideoPID && (sParams.lVideoPID != 0x1FFF))
	{
		hr = pFilterGraph->Render(pPinOutVideo);

		if (FAILED(hr))
		{
			fprintf(stderr,"Filter Graph Video Render failed, 0x%8.8x\n", hr);
			
			return 1;	// *** APPLICATION EXIT POINT
		}
	}

	// Build graph from Audio output pin.
	if ( sParams.lAudioPID && (sParams.lAudioPID != 0x1FFF))
	{
		hr = pFilterGraph->Render(pPinOutAudio);

		if (FAILED(hr)) 
		{
			fprintf(stderr,"Filter Graph Audio Render failed, 0x%8.8x\n", hr);
			
			return 1;	// *** APPLICATION EXIT POINT
		}
	}

	// **********************************************************************
	// *** Run Filter Graph
	// **********************************************************************

	// Get Media Control interface for control of Filter Graph.

	hr = b2c2Adapter.GetMediaControl( &pMediaControl);

	if (FAILED(hr))
	{
		fprintf(stderr,"%s failed (#08X)", b2c2Adapter.GetLastErrorText(), b2c2Adapter.GetLastError());
		
		return 1;	// *** APPLICATION EXIT POINT
	}

	// Get Media Event interface.

	hr = b2c2Adapter.GetMediaEvent( &pMediaEvent);
	
	if (FAILED(hr)) 
	{
		fprintf(stderr,"%s failed (#08X)", b2c2Adapter.GetLastErrorText(), b2c2Adapter.GetLastError());

		return 1;	// *** APPLICATION EXIT POINT
	}

	// Start data flow thru Filter Graph.

	pMediaControl->Run();

	// Block for completion.

	pMediaEvent->WaitForCompletion(INFINITE, &lEventCode);

	// Stop the graph.
	
	pMediaControl->Stop();

	// b2c2Adapter's destructor will release all COM objects

	return 0;
}


// **********************************************************************
// *** Check command line arguments
// **********************************************************************

int CheckCommandLineArgs (int argc, char * argv[], SParams * psParams)
{
	// Parse command line args, if any. Eliminate program name itself which
	// counts as one arg.  All options are required unless specified otherwise
	// (see Usage message and comments).

	if (--argc > 0)
	{
		// Make sure there are even # of args (flag and value pairs -- e.g. "-f 350");
		// this will also catch both "-h" or "?".

		int temp = argc / 2;

		if ((temp * 2) != argc)
		{
			fprintf(stderr, USAGE_MSG);
			
			return -1;	// *** FUNCTION EXIT POINT
		}

		// Process and set values for specified flags.

		for (int ii = 1; ii < argc; ii += 2)
		{
			_strupr (argv[ii]);

			// Tuner Type: Must be first option specified; determines applicability of
			// most of remaining options.

			if (strcmp (argv[ii], "-I") == 0)
			{
				_strupr(argv[ii+1]);

				if (strncmp (argv[ii + 1], "C", 1) == 0)
				{
					psParams->lTunerType = TUNER_CABLE;
				}
				else if (strncmp (argv[ii + 1], "S", 1) == 0)
				{
					psParams->lTunerType = TUNER_SATELLITE;
				}
				else if (strncmp (argv[ii + 1], "A", 1) == 0)
				{
					psParams->lTunerType = TUNER_TERRESTRIAL_ATSC;
				}
				else if (strncmp (argv[ii + 1], "T", 1) == 0)
				{
					psParams->lTunerType = TUNER_TERRESTRIAL_DVB;
				}
				else
				{
					fprintf (stderr, "\nError: Incorrect tuner type value: %s\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}
				
				psParams->bTunerTypeSet = TRUE;
			}
			else if (strcmp (argv[ii], "-F") == 0)
			{
				// Frequency: Applicable to both Cable and Satellite

				psParams->lFrequency = (int) (atof (argv[ii + 1]) * 1000);

				if (psParams->lFrequency < 0)
				{
					fprintf (stderr, "\nError: Incorrect transponder frequency value: %s\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}
				else
					psParams->bFrequencySet = TRUE;
			}
			else if (strcmp (argv[ii], "-s") == 0 || strcmp (argv[ii], "-S") == 0)
			{
				// Symbol Rate: Applicable to both Cable and Satellite

				psParams->lSymbolRate = atoi (argv[ii + 1]);

				if (psParams->lSymbolRate < 0)
				{
					fprintf (stderr, "\nError: Incorrect symbol rate value: %s\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}
				else
					psParams->bSymbolRateSet = TRUE;
			}
			else if (strcmp (argv[ii], "-m") == 0 || strcmp (argv[ii], "-M") == 0)
			{
				// Modulation: Cable only

				long lModulationInput = atoi (argv[ii + 1]);

				switch (lModulationInput)
				{
				case 4:
					psParams->eModulation = QAM_4;
					break;

				case 16:
					psParams->eModulation = QAM_16;
					break;
				
				case 32:
					psParams->eModulation = QAM_32;
					break;
				
				case 64:
					psParams->eModulation = QAM_64;
					break;
				
				case 128:
					psParams->eModulation = QAM_128;
					break;
				
				case 256:
					psParams->eModulation = QAM_256;
					break;
				
				default:
					fprintf (stderr, "\nError: Incorrect modulation value: %s\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				psParams->bModulationSet = TRUE;
			}
			else if (strcmp (argv[ii], "-L") == 0)
			{
				// LNB: Satellite only

				psParams->lLNB = atoi (argv[ii + 1]);

				if (psParams->lLNB < 0)
				{
					fprintf (stderr, "\nError: Incorrect LNB value: %s\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}
				else
					psParams->bLNBSet = TRUE;
			}
			else if (strcmp (argv[ii], "-E") == 0)
			{
				_strupr (argv[ii+1]);

				// FEC: Satellite only

				if (strcmp (argv[ii + 1], "1/2") == 0)
				{
					psParams->eFEC = FEC_1_2;
				}
				else if (strcmp (argv[ii + 1], "2/3") == 0)
				{
					psParams->eFEC = FEC_2_3;
				}
				else if (strcmp (argv[ii + 1], "3/4") == 0)
				{
					psParams->eFEC = FEC_3_4;
				}
				else if (strcmp (argv[ii + 1], "5/6") == 0)
				{
					psParams->eFEC = FEC_5_6;
				}
				else if (strcmp (argv[ii + 1], "7/8") == 0)
				{
					psParams->eFEC = FEC_7_8;
				}
				else if (strncmp (argv[ii + 1], "A", 1) == 0)
				{
					psParams->eFEC = FEC_AUTO;
				}
				else
				{
					fprintf (stderr, "\nError: Incorrect value %s for option -e\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				psParams->bFECSet = TRUE;
			}
			else if (strcmp (argv[ii], "-O") == 0)
			{
				_strupr (argv[ii+1]);

				// Polarity: Satellite only

				if (strncmp (argv[ii + 1], "H", 1) == 0)
				{
					psParams->ePolarity = POLARITY_HORIZONTAL;
				}
				else if (strncmp (argv[ii + 1], "V", 1) == 0)
				{
					psParams->ePolarity = POLARITY_VERTICAL;
				}
				else
				{
					fprintf (stderr, "\nError: Incorrect value %s for option -o\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				psParams->bPolaritySet = TRUE;
			}
			else if (strcmp (argv[ii], "-K") == 0)
			{
				// LNB Selection: Satellite only

				long lLSInput = atoi (argv[ii + 1]);

				if (lLSInput == 0)
				{
					psParams->eLNBSelection = LNB_SELECTION_0;
				}
				else if (lLSInput == 22)
				{
					psParams->eLNBSelection = LNB_SELECTION_22;
				}
				else if (lLSInput == 33)
				{
					psParams->eLNBSelection = LNB_SELECTION_33;
				}
				else if (lLSInput == 44)
				{
					psParams->eLNBSelection = LNB_SELECTION_44;
				}
				else
				{
					fprintf (stderr, "\nError: Incorrect value %s for option -k\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				psParams->bLNBSelectionSet = TRUE;
			}
			else if (strcmp (argv[ii], "-D") == 0)
			{
				_strupr (argv[ii+1]);

				// Diseqc: Satellite only

				if (strncmp (argv[ii + 1], "N", 1) == 0)
				{
					psParams->eDiseqc = DISEQC_NONE;
				}
				else if (strcmp (argv[ii + 1], "A") == 0)
				{
					psParams->eDiseqc = DISEQC_SIMPLE_A;
				}
				else if (strcmp (argv[ii + 1], "B") == 0)
				{
					psParams->eDiseqc = DISEQC_SIMPLE_B;
				}
				else if (strcmp (argv[ii + 1], "A/A") == 0)
				{
					psParams->eDiseqc = DISEQC_LEVEL_1_A_A;
				}
				else if (strcmp (argv[ii + 1], "B/A") == 0)
				{
					psParams->eDiseqc = DISEQC_LEVEL_1_B_A;
				}
				else if (strcmp (argv[ii + 1], "A/B") == 0)
				{
					psParams->eDiseqc = DISEQC_LEVEL_1_A_B;
				}
				else if (strcmp (argv[ii + 1], "B/B") == 0)
				{
					psParams->eDiseqc = DISEQC_LEVEL_1_B_B;
				}
				else
				{
					fprintf (stderr, "\nError: Incorrect value %s for option -d\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				psParams->bDiseqcSet = TRUE;
			}
			else if (strcmp (argv[ii], "-G") == 0)
			{
				_strupr (argv[ii+1]);

				// Guard Interval: Terrestrial DVB Only

				if (strcmp (argv[ii + 1], "1/32") == 0)
				{
					psParams->eGuardInterval = GUARD_INTERVAL_1_32;
				}
				else if (strcmp (argv[ii + 1], "1/16") == 0)
				{
					psParams->eGuardInterval = GUARD_INTERVAL_1_16;
				}
				else if (strcmp (argv[ii + 1], "1/8") == 0)
				{
					psParams->eGuardInterval = GUARD_INTERVAL_1_8;
				}
				else if (strcmp (argv[ii + 1], "1/4") == 0)
				{
					psParams->eGuardInterval = GUARD_INTERVAL_1_4;
				}
				else if (strncmp (argv[ii + 1], "A", 1) == 0)
				{
					psParams->eGuardInterval = GUARD_INTERVAL_AUTO;
				}
				else
				{
					fprintf (stderr, "\nError: Incorrect value %s for option -e\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				psParams->bGuardIntervalSet = TRUE;
			}
			else if (strcmp (argv[ii], "-B") == 0)
			{
				_strupr (argv[ii+1]);

				// Bandwidth: Terrestrial DVB Only

				if (strcmp (argv[ii + 1], "6") == 0)
				{
					psParams->eBandwidth = BANDWIDTH_6_MHZ;
				}
				else if (strcmp (argv[ii + 1], "7") == 0)
				{
					psParams->eBandwidth = BANDWIDTH_7_MHZ;
				}
				else if (strcmp (argv[ii + 1], "8") == 0)
				{
					psParams->eBandwidth = BANDWIDTH_8_MHZ;
				}
				else
				{
					fprintf (stderr, "\nError: Incorrect value %s for option -e\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				psParams->bBandwidthSet = TRUE;

			}
			else if (strcmp (argv[ii], "-PA") == 0)
			{
				// Non-tuner specific option

				// Audio PID
				char *	pTerm;

				if (strlen (argv[ii + 1]) > 2 &&
					(strncmp (argv[ii + 1], "0x", 2) == 0 || strncmp (argv[ii + 1], "0X", 2) == 0))
				{
					// convert from hex

					psParams->lAudioPID = strtoul ((argv[ii + 1] + 2), &pTerm, 16);
				}
				else
				{
					// assume decimal

					psParams->lAudioPID = atoi (argv[ii + 1]);
				}

				psParams->bAudioPIDSet = TRUE;
			}
			else if (strcmp (argv[ii], "-PV") == 0)
			{
				// Non-tuner specific option

				// Video PID

				char *	pTerm;

				if (strlen (argv[ii + 1]) > 2 &&
					(strncmp (argv[ii + 1], "0x", 2) == 0 || strncmp (argv[ii + 1], "0X", 2) == 0))
				{
					// convert from hex

					psParams->lVideoPID = strtoul ((argv[ii + 1] + 2), &pTerm, 16);
				}
				else
				{
					// assume decimal

					psParams->lVideoPID = atoi (argv[ii + 1]);
				}

				psParams->bVideoPIDSet = TRUE;
			}
			else
			{
				// Catch-all error

				fprintf (stderr, "\nError: Unrecognized option flag %s\n", argv[ii]);
				fprintf (stderr, USAGE_MSG);

				return -1;	// *** FUNCTION EXIT POINT
			}
		}
	}

	return 0;
}

UINT __stdcall
UpdateVideoAttributes (MPEG2_VIDEO_INFO * pInfo)
{
	char	szBuf[1024];

	sprintf (szBuf, "\nTestAVFilter.exe: Horizontal size: %d\n", pInfo->wHSize);
	fprintf (stderr, szBuf);
	sprintf (szBuf,   "TestAVFilter.exe: Vertical size  : %d\n", pInfo->wVSize);
	fprintf (stderr, szBuf);
	sprintf (szBuf,   "TestAVFilter.exe: Aspect Ratio   : %d\n", pInfo->bAspectRatio);
	fprintf (stderr, szBuf);
	sprintf (szBuf,   "TestAVFilter.exe: Frame Rate     : %d\n", pInfo->bFrameRate);
	fprintf (stderr, szBuf);

	return (UINT) 1;
}