www.pudn.com > VirtualVCR-src-v2.6.9.zip > FlowMeter.cpp


/* 
	Virtual VCR 
    Copyright (C) 2002  Shaun Faulds 
 
    This program is free software; you can redistribute it and/or modify 
    it under the terms of the GNU General Public License as published by 
    the Free Software Foundation; either version 2 of the License, or 
    (at your option) any later version. 
 
    This program is distributed in the hope that it will be useful, 
    but WITHOUT ANY WARRANTY; without even the implied warranty of 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    GNU General Public License for more details. 
 
    You should have received a copy of the GNU General Public License 
    along with this program; if not, write to the Free Software 
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 
	Acknowledgments: 
	This application and associated filters are based on the examples 
	from the Microsoft DirectX DirectShow SDK. 
*/ 
 
#include           // quartz, includes windows 
 
#pragma warning(disable: 4511 4512) 
 
#include           // performance measurement (MSR_) 
 
#include  
#if (1100 > _MSC_VER) 
#include  
#else 
#include  
#endif 
 
#include "FlowMeterUIDs.h"         // our own public guids 
 
#include "iFlowMeter.h"            // interface between filter and property sheet 
#include "FlowMeterProp.h"         // property sheet implementatino class 
#include "FlowMeter.h" 
 
#include "FlowMeter.version" 
 
//#include "InputPin.h" 
//#include "OutputPin.h" 
 
// 
// setup data 
// 
 
const AMOVIESETUP_MEDIATYPE 
sudPinTypes =   { &MEDIATYPE_NULL                // clsMajorType 
                , &MEDIASUBTYPE_NULL }  ;       // clsMinorType 
 
const AMOVIESETUP_PIN 
psudPins[] = { { L"Input"            // strName 
               , FALSE               // bRendered 
               , FALSE               // bOutput 
               , FALSE               // bZero 
               , FALSE               // bMany 
               , &CLSID_NULL         // clsConnectsToFilter 
               , L"Output"           // strConnectsToPin 
               , 1                   // nTypes 
               , &sudPinTypes }      // lpTypes 
             , { L"Output"           // strName 
               , FALSE               // bRendered 
               , TRUE                // bOutput 
               , FALSE               // bZero 
               , FALSE               // bMany 
               , &CLSID_NULL         // clsConnectsToFilter 
               , L"Input"            // strConnectsToPin 
               , 1                   // nTypes 
               , &sudPinTypes } };   // lpTypes 
 
 
const AMOVIESETUP_FILTER 
sudFlowMeter = { &CLSID_FlowMeter                 // clsID 
            , L"Flow Meter"                // strName 
            , MERIT_DO_NOT_USE                // dwMerit 
            , 2                               // nPins 
            , psudPins };                     // lpPin 
 
// 
// Needed for the CreateInstance mechanism 
// 
CFactoryTemplate g_Templates[]= 
    {   {L"Flow Meter" 
        , &CLSID_FlowMeter 
        , CFlowMeter::CreateInstance 
        , NULL 
        , &sudFlowMeter } 
    , 
        { L"Flow Meter Prop Page" 
        , &CLSID_FlowMeterPropertyPage 
        , FlowMeterProperties::CreateInstance } 
 
    }; 
int g_cTemplates = sizeof(g_Templates)/sizeof(g_Templates[0]); 
 
 
// Filter Transform Method 
HRESULT CFlowMeter::Transform(IMediaSample *pSample) 
{ 
	// 
	// Increment the counters 
	// 
	long sampleSize = pSample->GetActualDataLength(); 
	DataCount += sampleSize; 
	Transforms++; 
 
	// 
	// Calculate the dropped frames 
	// 
	REFERENCE_TIME start = 0; 
	REFERENCE_TIME end = 0; 
	pSample->GetTime(&start, &end); 
	sampleTiming = start - lastSampleTime; 
	lastSampleTime = start; 
 
	if(sampleSpacing == 0 && (end - start) != 0) 
		sampleSpacing = end - start; 
 
	// If the sample Timing (time detween start of last sample and 
	// the start of this sample) is greater then 1.5 times the sample 
	// length (start to end time of the samples) then this sample will 
	// be dropped by the AVImux filter. 
	if((sampleTiming > (sampleSpacing * 1.5)) && (Transforms > 1)) 
	{ 
		double sampleOffest =  
			(double)(sampleTiming-sampleSpacing) / (double)sampleSpacing; 
 
		LONGLONG droppedNow = (LONGLONG)(1.0 + (sampleOffest - 0.5)); 
 
		//Add Dropped Frames to List Array 
		for(int y = 0; (y < droppedNow) && droppedFrameCount < 5000; y++) 
		{ 
			droppedFrameList[droppedFrameCount++] =  
				droppedFrames + // Taking into account the previous dropped frames  
				Transforms +   // all the transactions so far 
				y + // if more than one dropped frame this time 
				1; // this dropped frame 
 
			DbgLog((LOG_TRACE, 0, TEXT("Dropped Frame Detected: droppedFrames=%d droppedNow=%d Transforms=%d y=%d sampleOffest=%f"), (__int32)droppedFrames, (__int32)droppedNow, (__int32)Transforms, (__int32)y, sampleOffest)); 
		} 
 
		droppedFrames += droppedNow; 
	} 
	 
 
	// 
	// Work out the samples per second etc 
	// Default is performance counters, 
	// other option are media smaple times, GetTickCount 
	// 
 
	//DbgLog((LOG_TRACE, 0,TEXT("FlowMeter: using QueryPerformanceCounter for rate calc"))); 
	 
	// original using performace counters 
	// Calculate the data rate 
	LARGE_INTEGER now; 
	timerAvailable = QueryPerformanceCounter(&now); 
	DWORD nowTicks = 0; 
	nowTicks = GetTickCount(); 
	 
	__int64 totalSamples = 0; 
	LONG ignoreFor = 0; 
	if(m_BytesPerSample != 0) 
	{ 
		totalSamples = (DataCount - dataHOLD) / (m_BytesPerSample * m_Channels); 
		ignoreFor = 4; 
	} 
	else 
	{ 
		//Include the dropped frames in the calc 
		totalSamples = (Transforms + droppedFrames) - tranHOLD; 
		ignoreFor = 10; 
	} 
	 
	samplesPerSecond = (double)totalSamples /  
		(((double)(now.QuadPart - timerHold.QuadPart) / (double)frequency.QuadPart)); 
	 
	if(Transforms < 10) 
	{ 
		dataHOLD = DataCount; 
		tranHOLD = Transforms; 
		timerHold.QuadPart = now.QuadPart; 
	} 
 
	// 
	// Watch for Performance Counter jumps, this is because 
	// of a bug in some chip sets causing the performance counter to 
	// jump forward by several seconds. see the following URL for 
	// more info 
	// http://support.microsoft.com/default.aspx?scid=kb;EN-US;q274323 
	// 
 
	DWORD dwPerfElapsed = (DWORD) (((now.QuadPart - prevPC.QuadPart)  
            * 1000) / frequency.QuadPart); 
	DWORD dwTicksElapsed = nowTicks - prevTicks; 
 
	__int32 timeDiff = abs(dwPerfElapsed - dwTicksElapsed); 
 
	// if the performace counter differs more than 0.2 Sec from the  
	// getTicks Counter then warn the user. 
    if ((timeDiff > 200) && Transforms > 1) 
	{ 
		// only Show the error once 
		if(errorShown == FALSE) 
		{ 
			errorShown = TRUE; 
			MsgBOX(TEXT("FlowMeter: Timer Problem Detected Audio Resample may be broken: %d"), timeDiff); 
		} 
		DbgLog((LOG_TRACE, 0, TEXT("FlowMeter: Timer Problem Detected %d"), timeDiff)); 
	} 
 
	prevTicks = nowTicks; 
	prevPC = now; 
 
	 
/* 
	if(timerType == 2) 
	{ 
		//DbgLog((LOG_TRACE,0,TEXT("FlowMeter: using Sample Times for rate calc"))); 
 
		// Use the sample times to calc the flow rate 
		__int64 totalSamples = 0; 
		LONG ignoreFor = 0; 
		if(m_BytesPerSample != 0) 
		{ 
			totalSamples = (DataCount - dataHOLD) / (m_BytesPerSample * m_Channels); 
			ignoreFor = 4; 
		} 
		else 
		{ 
			totalSamples = (Transforms + droppedFrames) - tranHOLD; 
			ignoreFor = 30; 
		} 
 
		samplesPerSecond = (double)totalSamples /  
			((double)(start-timerHold.QuadPart) / 10000000.0); 
 
		if(Transforms < ignoreFor) 
		{ 
			dataHOLD = DataCount; 
			tranHOLD = Transforms; 
			timerHold.QuadPart = start; 
		} 
	} 
 
	*/ 
 
	return NOERROR; 
} 
 
////////////////////////////////////////////////////// 
// External Meothods 
////////////////////////////////////////////////////// 
// get Byte Data Method 
STDMETHODIMP CFlowMeter::get_FlowData(LONGLONG *FlowData) 
{ 
    *FlowData = DataCount; 
    return NOERROR; 
} 
// get Transform Count Data Method 
STDMETHODIMP CFlowMeter::get_TransformCount(LONGLONG *TransCount) 
{ 
    *TransCount = Transforms; 
    return NOERROR; 
} 
// get Byte Data Method 
STDMETHODIMP CFlowMeter::resetALL() 
{ 
	DataCount = 0; 
	Transforms = 0; 
	droppedFrames = 0; 
 
	sampleTiming = 0; 
	sampleSpacing = 0; 
 
	//Zero the dropped Frame list 
	memset(droppedFrameList , 0, sizeof(droppedFrameList[0])*5000); 
	droppedFrameCount = 0; 
 
    return NOERROR; 
} 
STDMETHODIMP CFlowMeter::get_SamplesPerSecond(double *data) 
{ 
	*data = samplesPerSecond; 
    return NOERROR; 
} 
STDMETHODIMP CFlowMeter::get_DroppedFrames(LONGLONG *data) 
{ 
	*data = droppedFrames; 
	return NOERROR; 
} 
 
STDMETHODIMP CFlowMeter::get_DroppedFramesList(__int64 *&data) 
{ 
	for(int x = 0; x < 5000; x++) 
	{ 
		if(droppedFrameList[x] == 0) break; 
		data[x] = droppedFrameList[x]; 
	} 
 
	return NOERROR; 
} 
 
//This is for the common interface IFilterProperties 
// 
// Gets the data properties 
// 
STDMETHODIMP CFlowMeter::get_Prop(char*& data) 
{ 
	return NOERROR; 
} 
 
STDMETHODIMP CFlowMeter::set_Prop(char* data) 
{ 
	return NOERROR; 
} 
 
STDMETHODIMP CFlowMeter::get_Version(char *data) 
{ 
	wsprintf(data, "%s", MAIN_VERSION); 
 
	return NOERROR; 
} 
 
////////////////////////////////////////////////////// 
 
// 
// CFlowMeter::Constructor 
// 
CFlowMeter::CFlowMeter(TCHAR *tszName, LPUNKNOWN punk, HRESULT *phr) 
    : CTransInPlaceFilter (tszName, punk, CLSID_FlowMeter, phr) 
{ 
	DataCount = 0; 
	Transforms = 0; 
 
	timerAvailable = QueryPerformanceFrequency(&frequency); 
	timerAvailable = QueryPerformanceCounter(&timerHold); 
	samplesPerSecond = 0; 
	droppedFrames = 0; 
	sampleTiming = 0; 
	sampleSpacing = 0; 
 
	//Zero the dropped Frame list 
	memset(droppedFrameList , 0, sizeof(droppedFrameList[0])*5000); 
	droppedFrameCount = 0; 
	errorShown = 0; 
 
} // (CFlowMeter constructor) 
 
 
// 
// NonDelegatingQueryInterface 
// 
// Override CUnknown method. 
// Part of the basic COM (Compound Object Model) mechanism. 
// This is how we expose our interfaces. 
// 
STDMETHODIMP CFlowMeter::NonDelegatingQueryInterface(REFIID riid, void **ppv) 
{ 
    CheckPointer(ppv,E_POINTER); 
 
    if (riid == IID_IFlowMeter) 
	{ 
        return GetInterface((IFlowMeter *) this, ppv); 
    } 
	else if (riid == IID_IFilterProperties) 
	{ 
		return GetInterface((IFilterProperties *) this, ppv); 
	} 
    else if (riid == IID_ISpecifyPropertyPages) 
	{ 
        return GetInterface((ISpecifyPropertyPages *) this, ppv); 
    } 
    else 
	{ 
        return CTransformFilter::NonDelegatingQueryInterface(riid, ppv); 
    } 
 
} // NonDelegatingQueryInterface 
 
// 
// CreateInstance 
// 
// Override CClassFactory method. 
// Provide the way for COM to create a CFlowMeter object 
// 
CUnknown * WINAPI CFlowMeter::CreateInstance(LPUNKNOWN punk, HRESULT *phr) 
{ 
 
    CFlowMeter *pNewObject = new CFlowMeter(NAME("Flow Meter"), punk, phr ); 
    if (pNewObject == NULL) 
	{ 
        *phr = E_OUTOFMEMORY; 
    } 
 
    return pNewObject; 
 
} // CreateInstance 
 
 
//----------------------------------------------------------------------------- 
//                  ISpecifyPropertyPages implementation 
//----------------------------------------------------------------------------- 
 
 
// 
// GetPages 
// 
// Returns the clsid's of the property pages we support 
// 
STDMETHODIMP CFlowMeter::GetPages(CAUUID *pPages) 
{ 
    pPages->cElems = 1; 
    pPages->pElems = (GUID *) CoTaskMemAlloc(sizeof(GUID)); 
    if (pPages->pElems == NULL) 
	{ 
        return E_OUTOFMEMORY; 
    } 
    *(pPages->pElems) = CLSID_FlowMeterPropertyPage; 
 
    return NOERROR; 
 
} // GetPages 
 
 
 
/*****************Public*Routine****************\ 
* exported entry points for registration and 
* unregistration. 
\***********************************************/ 
STDAPI 
DllRegisterServer() 
{ 
  return AMovieDllRegisterServer2( TRUE ); 
} 
 
STDAPI 
DllUnregisterServer() 
{ 
  return AMovieDllRegisterServer2( FALSE ); 
} 
 
/* 
// 
// gets the input and output pins 
// 
CBasePin *CFlowMeter::GetPin(int n) 
{ 
    // Create the single input pin and the single output pin 
    // If anything fails, fail the whole lot and clean up. 
	 
    if (m_pInput == NULL || m_pOutput == NULL) 
	{ 
		 
        HRESULT hr = S_OK; 
		 
        m_pInput = new CInputPin(NAME("Flow Input Pin"), 
			this,              // Owner filter 
			&hr,               // Result code 
			L"Input");         // Pin name 
		 
        // a failed return code should delete the object 
		 
        if (FAILED(hr) || m_pInput == NULL) 
		{ 
            delete m_pInput; 
            m_pInput = NULL; 
            return NULL; 
        } 
		 
        m_pOutput = new COutputPin(NAME("Flow Output pin"), 
			this,            // Owner filter 
			&hr,             // Result code 
			L"Output");      // Pin name 
		 
        // failed return codes cause both objects to be deleted 
		 
        if (FAILED(hr) || m_pOutput == NULL) 
		{ 
            delete m_pInput; 
            m_pInput = NULL; 
            delete m_pOutput; 
            m_pOutput = NULL; 
            return NULL; 
        } 
    } 
	 
 
    switch(n) 
	{ 
	case 0: 
		return m_pInput; 
	case 1: 
		return m_pOutput; 
    } 
    return NULL; 
	 
} // GetPin 
*/ 
 
/* 
// 
// the quality messages are sent to this 
// method so quality adjustments can be made 
// 
HRESULT CFlowMeter::AlterQuality(Quality q) 
{ 
	MessageBox(NULL, "Quality Message", "Message Box", MB_OK|MB_ICONINFORMATION|MB_TASKMODAL); 
 
//	if(ProportionAmount < 1000) 
//		MessageBox(NULL, "Quality Message", "Message Box", MB_OK|MB_ICONINFORMATION|MB_TASKMODAL); 
 
	//S_FALSE Did not handle the quality message. The message should be passed upstream.  
	//S_OK Handled the quality message. No further action is necessary.  
	return S_FALSE; 
} 
*/ 
 
////////////////////////////////////////////////////////////// 
// 
// Sets the input media type, this is called when SetMediaType 
// is called with a pin direction of INPUT 
// 
HRESULT CFlowMeter::SetInputMediaType(const CMediaType* pmt) 
{ 
    // Record what we need for doing the actual transform 
 
    WAVEFORMATEX *pwfx = (WAVEFORMATEX *) pmt->Format(); 
 
    m_Channels =  pwfx->nChannels; 
    m_SamplesPerSec = pwfx->nSamplesPerSec; 
    m_BytesPerSample = pwfx->wBitsPerSample/8; 
 
 
    //static TCHAR tach[2000]; 
	//wsprintf(tach, TEXT("%d %d %d"), m_Channels, m_SamplesPerSec, m_BytesPerSample); 
    //MessageBox(HWND_DESKTOP, tach, "Filter Message Box", MB_OK|MB_ICONEXCLAMATION|MB_TASKMODAL); 
 
	return S_OK; 
} 
 
////////////////////////////////////////////////////////////// 
// 
// Informs when the media type is established for the connection.  
// This then calls the SetInputMediaType and SetOutputMediaType 
// for each pin direction. 
// 
HRESULT CFlowMeter::SetMediaType(PIN_DIRECTION direction, const CMediaType* pmt) 
{ 
	// assertion 
	CheckPointer (pmt, E_POINTER); 
 
	// Call super method, pass it what we got here. 
	HRESULT result = this->CTransInPlaceFilter::SetMediaType (direction, pmt); 
	// Return if not OK 
	if(result != S_OK) 
		return result; 
 
	// set media type for input pin? 
	if (direction == PINDIR_INPUT) 
		this->SetInputMediaType(pmt); 
 
	return S_OK; 
} 
 
////////////////////////////////////////////////////////////// 
// 
// Method to display a message box to show data 
// 
int CFlowMeter::MsgBOX(LPTSTR sz,...)  
{ 
    static TCHAR tach[2000]; 
    va_list va; 
 
    va_start(va, sz); 
    wvsprintf(tach, sz, va); 
    va_end(va); 
    MessageBox(HWND_DESKTOP, tach, "Filter Message Box", MB_OK|MB_ICONEXCLAMATION|MB_TASKMODAL); 
    return FALSE; 
} 
 
// Microsoft C Compiler will give hundreds of warnings about 
// unused inline functions in header files.  Try to disable them. 
#pragma warning( disable:4514)