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)