/*++ Copyright © 2000 Microsoft Corporation Module Name: dsperf.c Abstract: This module contains the implementation of the DShow performance counter provider. It acts as a WMI event tracing controller and consumer and exposes standard NT performance counters. Author: Arthur Zwiegincew (arthurz) 05-Oct-00 Revision History: 05-Oct-00 - Created --*/ #include #include #include #include #include #include #include #include "dscounters.h" #define WMIPERF #include typedef LONG LOGICAL; GUID KmixerGuid = { 0xe5a43a19, 0x6de0, 0x44f8, 0xb0, 0xd7, 0x77, 0x2d, 0xbd, 0xe4, 0x6c, 0xc0 }; GUID PortclsGuid = { 0x9d447297, 0xc576, 0x4015, 0x87, 0xb5, 0xa5, 0xa6, 0x98, 0xfd, 0x4d, 0xd1 }; GUID UsbaudioGuid = { 0xd6464a84, 0xa358, 0x4013, 0xa1, 0xe8, 0x6e, 0x2f, 0xb4, 0x8a, 0xab, 0x93 }; // // Video glitch threshold. When abs(PresentationTime - RenderTime) > this value, // we consider the event a glitch. // #define GLITCH_THRESHOLD 30 /* ms */ * 10000 /* 1 ms / 100 ns */ // // Performance data. // ULONG VideoGlitches; ULONG VideoGlitchesPerSec; ULONG VideoFrameRate; ULONG VideoJitter; ULONG DsoundGlitches; ULONG KmixerGlitches; ULONG PortclsGlitches; ULONG UsbaudioGlitches; ULONG VideoGlitchesSinceLastMeasurement; ULONG FramesRendered; ULONGLONG LastMeasurementTime; #define JITTER_HISTORY_BUFFER_SIZE 200 LONGLONG FrameJitterHistory[JITTER_HISTORY_BUFFER_SIZE]; ULONG NextJitterEntry; // // DSHOW WMI provider GUID. // GUID ControlGuid = GUID_DSHOW_CTL; // // Event tracing-related globals. // HANDLE MonitorThread; HANDLE ResetThread; TRACEHANDLE LoggerHandle; TRACEHANDLE ConsumerHandle; EVENT_TRACE_LOGFILE Logfile; ULONGLONG LastBufferFlushTime; // // Performance monitoring structures. // typedef struct _DSHOW_PERF_DATA_DEFINITION { PERF_OBJECT_TYPE ObjectType; PERF_COUNTER_DEFINITION VideoGlitches; PERF_COUNTER_DEFINITION VideoGlitchesPerSec; PERF_COUNTER_DEFINITION VideoFrameRate; PERF_COUNTER_DEFINITION VideoJitter; PERF_COUNTER_DEFINITION DsoundGlitches; PERF_COUNTER_DEFINITION KmixerGlitches; PERF_COUNTER_DEFINITION PortclsGlitches; PERF_COUNTER_DEFINITION UsbaudioGlitches; } DSHOW_PERF_DATA_DEFINITION, *PDSHOW_PERF_DATA_DEFINITION; typedef struct _DSHOW_PERF_COUNTERS { PERF_COUNTER_BLOCK CounterBlock; ULONG Pad; ULONG VideoGlitches; ULONG VideoGlitchesPerSec; ULONG VideoFrameRate; ULONG VideoJitter; ULONG DsoundGlitches; ULONG KmixerGlitches; ULONG PortclsGlitches; ULONG UsbaudioGlitches; //ULONG Pad2; //ULONG Pad3; } DSHOW_PERF_COUNTERS, *PDSHOW_PERF_COUNTERS; // // Perfmon-related globals. // DSHOW_PERF_DATA_DEFINITION DshowPerfDataDefinition; LONG OpenCount = 0; // Keeps track of how many threads have open ds counters. LOGICAL Initialized = 0; // Fixes an initialization race condition. // // Function prototypes. // PM_OPEN_PROC PerfOpen; PM_CLOSE_PROC PerfClose; PM_COLLECT_PROC PerfCollect; DWORD WINAPI MonitorThreadProc ( LPVOID Param ); DWORD WINAPI ResetThreadProc ( LPVOID Param ); VOID TerminateLogging ( VOID ); //////////////////////////////////////////////////////////////////////////// ULONG APIENTRY PerfOpen ( LPWSTR DevNames ) /*++ Routine Description: This routine initializes data collection. Arguments: DevNames - Supplies a REG_MULTISZ of object IDs of the devices to be opened. Return Value: ERROR_SUCCESS - OK. - initialization failed. Note: On remote connections, this function may be called more than once by multiple threads. --*/ { PDSHOW_PERF_DATA_DEFINITION Def = &DshowPerfDataDefinition; ULONG FirstCounter; ULONG FirstHelp; HKEY PerfKey; LOGICAL WasInitialized; ULONG Type; ULONG Size; LONG status; // // Initialize counters. // VideoGlitches = 0; VideoGlitchesSinceLastMeasurement = 0; VideoFrameRate = 0; DsoundGlitches = 0; KmixerGlitches = 0; PortclsGlitches = 0; UsbaudioGlitches = 0; FramesRendered = 0; NextJitterEntry = 0; GetSystemTimeAsFileTime ((FILETIME*)&LastMeasurementTime); // // Since WinLogon is multithreaded and will call this routine in order to // service remote performance queries, this library must keep track of how // many threads have accessed it). The registry routines will limit access // to the initialization routine to only one thread at a time. // WasInitialized = InterlockedCompareExchange (&Initialized, 0, 0); if (!WasInitialized) { // // Create the monitor thread. // MonitorThread = CreateThread (NULL, 0, MonitorThreadProc, 0, 0, NULL); if (MonitorThread == NULL) { return GetLastError(); } // // Create the reset thread. // ResetThread = CreateThread (NULL, 0, ResetThreadProc, 0, 0, NULL); if (ResetThread == NULL) { return GetLastError(); } // // Get counter and help index base values from the registry. // status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\DSPerf\\Performance", 0L, KEY_READ, &PerfKey); if (status != ERROR_SUCCESS) { return status; } Size = sizeof (ULONG); status = RegQueryValueEx (PerfKey, "First Counter", 0L, &Type, (LPBYTE) &FirstCounter, &Size); if (status != ERROR_SUCCESS) { RegCloseKey (PerfKey); return status; } Size = sizeof (ULONG); status = RegQueryValueEx (PerfKey, "First Help", 0L, &Type, (LPBYTE) &FirstHelp, &Size); if (status != ERROR_SUCCESS) { RegCloseKey (PerfKey); return status; } RegCloseKey (PerfKey); // // Fill the data definition structure. // Def->ObjectType.TotalByteLength = sizeof (DSHOW_PERF_DATA_DEFINITION) + sizeof (DSHOW_PERF_COUNTERS); Def->ObjectType.DefinitionLength = sizeof (DSHOW_PERF_DATA_DEFINITION); Def->ObjectType.HeaderLength = sizeof (PERF_OBJECT_TYPE); Def->ObjectType.ObjectNameTitleIndex = DSHOWPERF_OBJ + FirstCounter; Def->ObjectType.ObjectNameTitle = NULL; Def->ObjectType.ObjectHelpTitleIndex = DSHOWPERF_OBJ + FirstHelp; Def->ObjectType.ObjectHelpTitle = NULL; Def->ObjectType.DetailLevel = PERF_DETAIL_NOVICE; Def->ObjectType.NumCounters = (sizeof (DSHOW_PERF_DATA_DEFINITION) - sizeof (PERF_OBJECT_TYPE)) / sizeof (PERF_COUNTER_DEFINITION); Def->ObjectType.DefaultCounter = -1; Def->ObjectType.NumInstances = PERF_NO_INSTANCES; Def->ObjectType.CodePage = 0; Def->ObjectType.PerfFreq.QuadPart = 10000000; /// Def->VideoGlitches.ByteLength = sizeof (PERF_COUNTER_DEFINITION); Def->VideoGlitches.CounterNameTitleIndex = DSHOWPERF_VIDEO_GLITCHES + FirstCounter; Def->VideoGlitches.CounterNameTitle = NULL; Def->VideoGlitches.CounterHelpTitleIndex = DSHOWPERF_VIDEO_GLITCHES + FirstHelp; Def->VideoGlitches.CounterHelpTitle = NULL; Def->VideoGlitches.DefaultScale = -1; Def->VideoGlitches.DetailLevel = PERF_DETAIL_NOVICE; Def->VideoGlitches.CounterType = PERF_COUNTER_RAWCOUNT; Def->VideoGlitches.CounterSize = sizeof (ULONG); Def->VideoGlitches.CounterOffset = FIELD_OFFSET (DSHOW_PERF_COUNTERS, VideoGlitches); /// Def->VideoGlitchesPerSec.ByteLength = sizeof (PERF_COUNTER_DEFINITION); Def->VideoGlitchesPerSec.CounterNameTitleIndex = DSHOWPERF_VIDEO_GLITCHES_SEC + FirstCounter; Def->VideoGlitchesPerSec.CounterNameTitle = NULL; Def->VideoGlitchesPerSec.CounterHelpTitleIndex = DSHOWPERF_VIDEO_GLITCHES_SEC + FirstHelp; Def->VideoGlitchesPerSec.CounterHelpTitle = NULL; Def->VideoGlitchesPerSec.DefaultScale = 0; Def->VideoGlitchesPerSec.DetailLevel = PERF_DETAIL_NOVICE; Def->VideoGlitchesPerSec.CounterType = PERF_COUNTER_RAWCOUNT; Def->VideoGlitchesPerSec.CounterSize = sizeof (ULONG); Def->VideoGlitchesPerSec.CounterOffset = FIELD_OFFSET (DSHOW_PERF_COUNTERS, VideoGlitchesPerSec); /// Def->VideoFrameRate.ByteLength = sizeof (PERF_COUNTER_DEFINITION); Def->VideoFrameRate.CounterNameTitleIndex = DSHOWPERF_FRAME_RATE + FirstCounter; Def->VideoFrameRate.CounterNameTitle = NULL; Def->VideoFrameRate.CounterHelpTitleIndex = DSHOWPERF_FRAME_RATE + FirstHelp; Def->VideoFrameRate.CounterHelpTitle = NULL; Def->VideoFrameRate.DefaultScale = 0; Def->VideoFrameRate.DetailLevel = PERF_DETAIL_NOVICE; Def->VideoFrameRate.CounterType = PERF_COUNTER_RAWCOUNT; Def->VideoFrameRate.CounterSize = sizeof (ULONG); Def->VideoFrameRate.CounterOffset = FIELD_OFFSET (DSHOW_PERF_COUNTERS, VideoFrameRate); /// Def->VideoJitter.ByteLength = sizeof (PERF_COUNTER_DEFINITION); Def->VideoJitter.CounterNameTitleIndex = DSHOWPERF_JITTER + FirstCounter; Def->VideoJitter.CounterNameTitle = NULL; Def->VideoJitter.CounterHelpTitleIndex = DSHOWPERF_JITTER + FirstHelp; Def->VideoJitter.CounterHelpTitle = NULL; Def->VideoJitter.DefaultScale = 1; Def->VideoJitter.DetailLevel = PERF_DETAIL_NOVICE; Def->VideoJitter.CounterType = PERF_COUNTER_RAWCOUNT; Def->VideoJitter.CounterSize = sizeof (ULONG); Def->VideoJitter.CounterOffset = FIELD_OFFSET (DSHOW_PERF_COUNTERS, VideoJitter); /// Def->DsoundGlitches.ByteLength = sizeof (PERF_COUNTER_DEFINITION); Def->DsoundGlitches.CounterNameTitleIndex = DSHOWPERF_DSOUND_GLITCHES + FirstCounter; Def->DsoundGlitches.CounterNameTitle = NULL; Def->DsoundGlitches.CounterHelpTitleIndex = DSHOWPERF_DSOUND_GLITCHES + FirstHelp; Def->DsoundGlitches.CounterHelpTitle = NULL; Def->DsoundGlitches.DefaultScale = -1; Def->DsoundGlitches.DetailLevel = PERF_DETAIL_NOVICE; Def->DsoundGlitches.CounterType = PERF_COUNTER_RAWCOUNT; Def->DsoundGlitches.CounterSize = sizeof (ULONG); Def->DsoundGlitches.CounterOffset = FIELD_OFFSET (DSHOW_PERF_COUNTERS, DsoundGlitches); /// Def->KmixerGlitches.ByteLength = sizeof (PERF_COUNTER_DEFINITION); Def->KmixerGlitches.CounterNameTitleIndex = DSHOWPERF_KMIXER_GLITCHES + FirstCounter; Def->KmixerGlitches.CounterNameTitle = NULL; Def->KmixerGlitches.CounterHelpTitleIndex = DSHOWPERF_KMIXER_GLITCHES + FirstHelp; Def->KmixerGlitches.CounterHelpTitle = NULL; Def->KmixerGlitches.DefaultScale = -1; Def->KmixerGlitches.DetailLevel = PERF_DETAIL_NOVICE; Def->KmixerGlitches.CounterType = PERF_COUNTER_RAWCOUNT; Def->KmixerGlitches.CounterSize = sizeof (ULONG); Def->KmixerGlitches.CounterOffset = FIELD_OFFSET (DSHOW_PERF_COUNTERS, KmixerGlitches); /// Def->PortclsGlitches.ByteLength = sizeof (PERF_COUNTER_DEFINITION); Def->PortclsGlitches.CounterNameTitleIndex = DSHOWPERF_PORTCLS_GLITCHES + FirstCounter; Def->PortclsGlitches.CounterNameTitle = NULL; Def->PortclsGlitches.CounterHelpTitleIndex = DSHOWPERF_PORTCLS_GLITCHES + FirstHelp; Def->PortclsGlitches.CounterHelpTitle = NULL; Def->PortclsGlitches.DefaultScale = -1; Def->PortclsGlitches.DetailLevel = PERF_DETAIL_NOVICE; Def->PortclsGlitches.CounterType = PERF_COUNTER_RAWCOUNT; Def->PortclsGlitches.CounterSize = sizeof (ULONG); Def->PortclsGlitches.CounterOffset = FIELD_OFFSET (DSHOW_PERF_COUNTERS, PortclsGlitches); /// Def->UsbaudioGlitches.ByteLength = sizeof (PERF_COUNTER_DEFINITION); Def->UsbaudioGlitches.CounterNameTitleIndex = DSHOWPERF_USBAUDIO_GLITCHES + FirstCounter; Def->UsbaudioGlitches.CounterNameTitle = NULL; Def->UsbaudioGlitches.CounterHelpTitleIndex = DSHOWPERF_USBAUDIO_GLITCHES + FirstHelp; Def->UsbaudioGlitches.CounterHelpTitle = NULL; Def->UsbaudioGlitches.DefaultScale = -1; Def->UsbaudioGlitches.DetailLevel = PERF_DETAIL_NOVICE; Def->UsbaudioGlitches.CounterType = PERF_COUNTER_RAWCOUNT; Def->UsbaudioGlitches.CounterSize = sizeof (ULONG); Def->UsbaudioGlitches.CounterOffset = FIELD_OFFSET (DSHOW_PERF_COUNTERS, UsbaudioGlitches); } InterlockedExchange (&Initialized, 1); InterlockedIncrement (&OpenCount); return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////// ULONG APIENTRY PerfClose ( VOID ) /*++ Routine Description: This routine cleans up after data collection. Arguments: None. Return Value: ERROR_SUCCESS. Note: On remote connections, this function may be called more than once by multiple threads. --*/ { ULONG NewOpenCount; NewOpenCount = InterlockedDecrement (&OpenCount); if (NewOpenCount == 0) { // // Last thread has closed the counter. Perform cleanup here. // TerminateLogging(); } return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////// ULONG APIENTRY PerfCollect ( IN LPWSTR ValueName, IN OUT LPVOID* PData, IN OUT LPDWORD TotalBytes, OUT LPDWORD NumObjectTypes ) /*++ Routine Description: This routine provides perf mon data. Arguments: ValueName - Supplies a string specified by the performance monitor program in a call to the RegQueryValueEx function. PData - Supplies the address of the buffer to receive the completed PERF_DATA_BLOCK and subordinate structures. This routine will append its data to the buffer starting at the point referenced by *PData. Receives the pointer to the first byte after the data structure added by this routine. TotalBytes - Supplies the size in bytes of the buffer referenced by PData. Receives the number of bytes added by this routine. NumObjectTypes - Receives the number of objects added by this routine. Return Value: ERROR_MORE_DATA - if the buffer passed is too small to hold data. ERROR_SUCCESS - if success or any other error. Errors are also reported to the event log. AZFIX --*/ { PDSHOW_PERF_DATA_DEFINITION PerfDataDefinition; PDSHOW_PERF_COUNTERS PerfCounters; FILETIME FileTime; ULONG SpaceNeeded; SpaceNeeded = sizeof (DSHOW_PERF_DATA_DEFINITION) + sizeof (DSHOW_PERF_COUNTERS); if (!Initialized) { *TotalBytes = 0; *NumObjectTypes = 0; return ERROR_SUCCESS; // This is OK. } if (*TotalBytes < SpaceNeeded) { *TotalBytes = 0; *NumObjectTypes = 0; return ERROR_MORE_DATA; } // // Copy the object and counter definitions. // PerfDataDefinition = (PDSHOW_PERF_DATA_DEFINITION)(*PData); RtlMoveMemory (PerfDataDefinition, &DshowPerfDataDefinition, sizeof (DSHOW_PERF_DATA_DEFINITION)); GetSystemTimeAsFileTime (&FileTime); RtlCopyMemory (&PerfDataDefinition->ObjectType.PerfTime.QuadPart, &FileTime, sizeof (LONGLONG)); PerfDataDefinition->ObjectType.NumInstances = -1; PerfDataDefinition->ObjectType.TotalByteLength = SpaceNeeded; // // Fill in counter data. // PerfCounters = (PDSHOW_PERF_COUNTERS)(PerfDataDefinition + 1); PerfCounters->CounterBlock.ByteLength = sizeof (DSHOW_PERF_COUNTERS); PerfCounters->VideoGlitches = VideoGlitches; PerfCounters->VideoGlitchesPerSec = VideoGlitchesPerSec; PerfCounters->VideoFrameRate = VideoFrameRate; PerfCounters->VideoJitter = VideoJitter; PerfCounters->DsoundGlitches = DsoundGlitches; PerfCounters->KmixerGlitches = KmixerGlitches; PerfCounters->PortclsGlitches = PortclsGlitches; PerfCounters->UsbaudioGlitches = UsbaudioGlitches; // // Update out parameters. // *PData = (PVOID)(PerfCounters + 1); *TotalBytes = SpaceNeeded; *NumObjectTypes = 1; return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////// VOID TerminateLogging ( VOID ) { ULONG status; int i; struct EVENT_TRACE_INFO { EVENT_TRACE_PROPERTIES TraceProperties; char Logger[256]; char LogFileName[256]; } Info; ZeroMemory (&Info, sizeof (Info)); Info.TraceProperties.LoggerNameOffset = sizeof (EVENT_TRACE_PROPERTIES); Info.TraceProperties.LogFileNameOffset = sizeof (EVENT_TRACE_PROPERTIES) + 256; Info.TraceProperties.Wnode.BufferSize = sizeof (Info); ControlTrace (0, "DSPerf", &Info.TraceProperties, EVENT_TRACE_CONTROL_QUERY); ControlTraceA (LoggerHandle, NULL, &Info.TraceProperties, EVENT_TRACE_CONTROL_STOP); if (MonitorThread != NULL) { EnableTrace (FALSE, 0, 0, &ControlGuid, LoggerHandle); TerminateThread (MonitorThread, 0); } if (ResetThread != NULL) { TerminateThread (ResetThread, 0); } for (i = 0; i < 100; i += 1) { Sleep (50); status = CloseTrace (ConsumerHandle); if (status == ERROR_SUCCESS) { break; } } EnableTrace (FALSE, 0, 0, &ControlGuid, LoggerHandle); } //////////////////////////////////////////////////////////////////////////// VOID WINAPI EventCallback ( IN PEVENT_TRACE Event ) { PPERFINFO_DSHOW_AVREND PerfInfoAvRend; PPERFINFO_DSHOW_AUDIOGLITCH PerfInfoAudioGlitch; ULONG i; LONGLONG PresentationDelta; if (IsEqualGUID (Event->Header.Guid, GUID_VIDEOREND)) { FramesRendered += 1; PerfInfoAvRend = (PPERFINFO_DSHOW_AVREND)Event->MofData; PresentationDelta = PerfInfoAvRend->dshowClock - PerfInfoAvRend->sampleTime; FrameJitterHistory[NextJitterEntry] = PresentationDelta; if (NextJitterEntry < JITTER_HISTORY_BUFFER_SIZE - 1) { NextJitterEntry += 1; } if (labs ((int)PresentationDelta) > GLITCH_THRESHOLD) { // // Video glitch. // VideoGlitches += 1; VideoGlitchesSinceLastMeasurement += 1; } } else if (IsEqualGUID (Event->Header.Guid, GUID_DSOUNDGLITCH)) { PerfInfoAudioGlitch = (PPERFINFO_DSHOW_AUDIOGLITCH)Event->MofData; if (PerfInfoAudioGlitch->glitchType == 1) { DsoundGlitches += 1; } } else if (IsEqualGUID (Event->Header.Guid, KmixerGuid)) { PerfInfoAudioGlitch = (PPERFINFO_DSHOW_AUDIOGLITCH)Event->MofData; KmixerGlitches += 1; } else if (IsEqualGUID (Event->Header.Guid, PortclsGuid)) { PerfInfoAudioGlitch = (PPERFINFO_DSHOW_AUDIOGLITCH)Event->MofData; PortclsGlitches += 1; } else if (IsEqualGUID (Event->Header.Guid, UsbaudioGuid)) { PerfInfoAudioGlitch = (PPERFINFO_DSHOW_AUDIOGLITCH)Event->MofData; UsbaudioGlitches += 1; } } //////////////////////////////////////////////////////////////////////////// ULONG WINAPI BufferCallback ( PEVENT_TRACE_LOGFILE Log ) { ULONGLONG CurrentTime; double TimeDelta; double JitterMean; double Jitter; double x; ULONG i; // // Extend the reset timer (indirectly). // GetSystemTimeAsFileTime ((FILETIME*)&LastBufferFlushTime); // // Calculate rates. // GetSystemTimeAsFileTime ((FILETIME*)&CurrentTime); TimeDelta = (double)(CurrentTime - LastMeasurementTime) / 10000000.0L; if (CurrentTime > LastMeasurementTime) { VideoGlitchesPerSec = (ULONG)((double)VideoGlitchesSinceLastMeasurement / TimeDelta); VideoFrameRate = (ULONG)((double)FramesRendered / TimeDelta); } else { VideoGlitchesPerSec = 0; VideoFrameRate = 0; } // // Calculate jitter. // if (NextJitterEntry > 1) { JitterMean = 0.0L; for (i = 0; i < NextJitterEntry; i += 1) { JitterMean += (double)FrameJitterHistory[i]; } JitterMean /= (double)NextJitterEntry; Jitter = 0.0L; for (i = 0; i < NextJitterEntry; i += 1) { x = (double)FrameJitterHistory[i] - JitterMean; Jitter += (x * x); } Jitter /= (double)(NextJitterEntry - 1); Jitter = sqrt (Jitter) / 10000.0L; VideoJitter = (ULONG)Jitter; } else { VideoJitter = 0; } LastMeasurementTime = CurrentTime; VideoGlitchesSinceLastMeasurement = 0; FramesRendered = 0; NextJitterEntry = 0; return TRUE; } //////////////////////////////////////////////////////////////////////////// DWORD WINAPI MonitorThreadProc ( LPVOID Param ) { struct EVENT_TRACE_INFO { EVENT_TRACE_PROPERTIES TraceProperties; char Logger[256]; } Info; UNREFERENCED_PARAMETER (Param); ULONG status; // // Start the controller. // ZeroMemory (&Info, sizeof (Info)); Info.TraceProperties.Wnode.BufferSize = sizeof (Info); Info.TraceProperties.Wnode.Flags = WNODE_FLAG_TRACED_GUID; Info.TraceProperties.Wnode.Guid = ControlGuid; Info.TraceProperties.BufferSize = 4096; Info.TraceProperties.MinimumBuffers = 8; Info.TraceProperties.MaximumBuffers = 16; Info.TraceProperties.LogFileMode = EVENT_TRACE_REAL_TIME_MODE; Info.TraceProperties.FlushTimer = 1; Info.TraceProperties.EnableFlags = 1; Info.TraceProperties.AgeLimit = 1; Info.TraceProperties.LoggerNameOffset = sizeof (Info.TraceProperties); strcpy (Info.Logger, "DSPerf"); status = StartTrace (&LoggerHandle, "DSPerf", &Info.TraceProperties); if (status != ERROR_SUCCESS) { return 0; } status = EnableTrace (TRUE, 0x1f, 0, &ControlGuid, LoggerHandle); if (status != ERROR_SUCCESS) { return 0; } // // Start the consumer. // ZeroMemory (&Logfile, sizeof (Logfile)); Logfile.LoggerName = "DSPerf"; Logfile.LogFileMode = EVENT_TRACE_REAL_TIME_MODE; Logfile.BufferCallback = BufferCallback; Logfile.EventCallback = EventCallback; ConsumerHandle = OpenTrace (&Logfile); if (ConsumerHandle == (TRACEHANDLE)INVALID_HANDLE_VALUE) { return 0; } // // Consume. This call returns when logging is stopped. // status = ProcessTrace (&ConsumerHandle, 1, NULL, NULL); if (status != ERROR_SUCCESS) { return 1; } return 1; } //////////////////////////////////////////////////////////////////////////// DWORD WINAPI ResetThreadProc ( LPVOID Param ) { ULONGLONG Time; for (;;) { Sleep (500); GetSystemTimeAsFileTime ((FILETIME*)&Time); if (Time - LastBufferFlushTime > 15000000I64) { FramesRendered = 0; NextJitterEntry = 0; VideoGlitchesPerSec = 0; VideoFrameRate = 0; VideoJitter = 0; } } }