source: trunk/DirectShowSpy/RunPropertyBag.h @ 588

Last change on this file since 588 was 588, checked in by roman, 6 years ago
File size: 15.4 KB
Line 
1////////////////////////////////////////////////////////////
2// Copyright (C) Roman Ryltsov, 2008-2015
3// Created by Roman Ryltsov roman@alax.info, http://alax.info
4//
5// This source code is published to complement DirectShowSpy developer powertoy
6// and demonstrate the internal use of APIs and tricks powering the tool. It is
7// allowed to freely re-use the portions of the code in other projects, commercial
8// or otherwise (provided that you don’t pretend that you wrote the original tool).
9//
10// Please keep in mind that DirectShowSpy is a developer tool, it is strongly recommended
11// that it is not shipped with release grade software. It is allowed to distribute
12// DirectShowSpy if only it is not registered with Windows by default and either
13// used privately, or registered on specific throubleshooting request. The advice applies
14// to hooking methods used by DirectShowSpy in general as well.
15
16#pragma once
17
18#include "Module_i.h"
19#include "Common.h"
20
21////////////////////////////////////////////////////////////
22// CRunPropertyBagHelper
23
24class CRunPropertyBagHelper
25{
26public:
27
28        ////////////////////////////////////////////////////////
29        // CSimpleSortTraitsT
30
31        class CNameSortTraits :
32                public CSimpleSortTraitsT<CString>
33        {
34        public:
35        // CNameSortTraits
36                static INT_PTR CompareElements(const CString& sElementA, const CString& sElementB, PARAMETERARGUMENT Parameter)
37                {
38                        return _tcsicmp(sElementA, sElementB);
39                }
40        };
41
42protected:
43        static CObjectPtr<CMemoryPropertyBag>& PropertyBagNeeded(CObjectPtr<CMemoryPropertyBag>& pPropertyBag)
44        {
45                if(!pPropertyBag)
46                        pPropertyBag.Construct();
47                return pPropertyBag;
48        }
49
50public:
51// CRunPropertyBagHelper
52        static CComVariantArray ReadRunPropertyBag(IUnknown* pBaseFilterUnknown, BOOL bAllowExtension = TRUE)
53        {
54                CComVariantArray vValue;
55                CRoArrayT<CComVariantArray> Array;
56                const CComQIPtr<DIRECTSHOWSPY_NAMESPACE_PREFIX IRunPropertyBagAware> pRunPropertyBagAware = pBaseFilterUnknown;
57                CComQIPtr<IPropertyBag2> pPropertyBag2;
58                #pragma region Extension
59                if(!pRunPropertyBagAware && bAllowExtension)
60                {
61                        CObjectPtr<CMemoryPropertyBag> pPropertyBag;
62                        #pragma region IQualProp
63                        const CComQIPtr<IQualProp, &IID_IQualProp> pQualProp = pBaseFilterUnknown;
64                        if(pQualProp)
65                                _ATLTRY
66                                {
67                                        PropertyBagNeeded(pPropertyBag);
68                                        // NOTE: IQualProp methods are not implemented on EVR even though the interface itself is exposed
69                                        INT nFramesDroppedInRenderer, nFramesDrawn, nAvgFrameRate, nJitter, nAvgSyncOffset, nDevSyncOffset;
70                                        if(SUCCEEDED(pQualProp->get_FramesDroppedInRenderer(&nFramesDroppedInRenderer)))
71                                                pPropertyBag->WriteValue(_T("FramesDroppedInRenderer"), CComVariant((LONG) nFramesDroppedInRenderer));
72                                        if(SUCCEEDED(pQualProp->get_FramesDrawn(&nFramesDrawn)))
73                                                pPropertyBag->WriteValue(_T("FramesDrawn"), CComVariant((LONG) nFramesDrawn));
74                                        if(SUCCEEDED(pQualProp->get_AvgFrameRate(&nAvgFrameRate)))
75                                                pPropertyBag->WriteValue(_T("AvgFrameRate"), CComVariant((DOUBLE) nAvgFrameRate / 100));
76                                        if(SUCCEEDED(pQualProp->get_Jitter(&nJitter)))
77                                                pPropertyBag->WriteValue(_T("Jitter"), CComVariant((LONG) nJitter));
78                                        if(SUCCEEDED(pQualProp->get_AvgSyncOffset(&nAvgSyncOffset)))
79                                                pPropertyBag->WriteValue(_T("AvgSyncOffset"), CComVariant((LONG) nAvgSyncOffset));
80                                        if(SUCCEEDED(pQualProp->get_DevSyncOffset(&nDevSyncOffset)))
81                                                pPropertyBag->WriteValue(_T("DevSyncOffset"), CComVariant((LONG) nDevSyncOffset));
82                                }
83                                _ATLCATCHALL()
84                                {
85                                        _Z_EXCEPTION();
86                                }
87                        #pragma endregion
88                        #pragma region IAMAudioRendererStats
89                        const CComQIPtr<IAMAudioRendererStats> pAmAudioRendererStats = pBaseFilterUnknown;
90                        if(pAmAudioRendererStats)
91                                _ATLTRY
92                                {
93                                        PropertyBagNeeded(pPropertyBag);
94                                        DWORD nDummy;
95                                        DWORD nBreakCount, nSlaveMode, nSilenceDuration, nLastBufferDuration, nDiscontinuityCount, nSlaveRate, nDropWriteDuration;
96                                        DWORD nHighestSlaveError, nLowestSlaveError, nLastHighSlaveError, nLastLowSlaveError;
97                                        DWORD nAccumulatedError, nBufferFullness;
98                                        DWORD nJitter;
99                                        if(SUCCEEDED(pAmAudioRendererStats->GetStatParam(AM_AUDREND_STAT_PARAM_BREAK_COUNT, &nBreakCount, &nDummy)))
100                                                pPropertyBag->WriteValue(_T("BreakCount"), CComVariant((LONG) nBreakCount));
101                                        if(SUCCEEDED(pAmAudioRendererStats->GetStatParam(AM_AUDREND_STAT_PARAM_SLAVE_MODE, &nSlaveMode, &nDummy)))
102                                        {
103                                                pPropertyBag->WriteValue(_T("SlaveMode"), CComVariant((LONG) nSlaveMode));
104                                                if(nSlaveMode)
105                                                {
106                                                        if(SUCCEEDED(pAmAudioRendererStats->GetStatParam(AM_AUDREND_STAT_PARAM_SLAVE_RATE, &nSlaveRate, &nDummy)))
107                                                                pPropertyBag->WriteValue(_T("SlaveRate"), CComVariant((LONG) nSlaveRate));
108                                                        if(SUCCEEDED(pAmAudioRendererStats->GetStatParam(AM_AUDREND_STAT_PARAM_SLAVE_DROPWRITE_DUR, &nDropWriteDuration, &nDummy)))
109                                                                pPropertyBag->WriteValue(_T("DropWriteDuration"), CComVariant((LONG) nDropWriteDuration));
110                                                        if(SUCCEEDED(pAmAudioRendererStats->GetStatParam(AM_AUDREND_STAT_PARAM_SLAVE_HIGHLOWERROR, &nHighestSlaveError, &nLowestSlaveError)))
111                                                        {
112                                                                pPropertyBag->WriteValue(_T("HighestSlaveError"), CComVariant((LONG) nHighestSlaveError));
113                                                                pPropertyBag->WriteValue(_T("LowestSlaveError"), CComVariant((LONG) nLowestSlaveError));
114                                                        }
115                                                        if(SUCCEEDED(pAmAudioRendererStats->GetStatParam(AM_AUDREND_STAT_PARAM_SLAVE_LASTHIGHLOWERROR, &nLastHighSlaveError, &nLastLowSlaveError)))
116                                                        {
117                                                                pPropertyBag->WriteValue(_T("LastHighSlaveError"), CComVariant((LONG) nLastHighSlaveError));
118                                                                pPropertyBag->WriteValue(_T("LastLowSlaveError"), CComVariant((LONG) nLastLowSlaveError));
119                                                        }
120                                                        if(SUCCEEDED(pAmAudioRendererStats->GetStatParam(AM_AUDREND_STAT_PARAM_SLAVE_ACCUMERROR, &nAccumulatedError, &nDummy)))
121                                                                pPropertyBag->WriteValue(_T("AccumulatedError"), CComVariant((LONG) nAccumulatedError));
122                                                }
123                                        }
124                                        if(SUCCEEDED(pAmAudioRendererStats->GetStatParam(AM_AUDREND_STAT_PARAM_SILENCE_DUR, &nSilenceDuration, &nDummy)))
125                                                pPropertyBag->WriteValue(_T("SilenceDuration"), CComVariant((LONG) nSilenceDuration));
126                                        if(SUCCEEDED(pAmAudioRendererStats->GetStatParam(AM_AUDREND_STAT_PARAM_LAST_BUFFER_DUR, &nLastBufferDuration, &nDummy)))
127                                                pPropertyBag->WriteValue(_T("LastBufferDuration"), CComVariant((LONG) nLastBufferDuration));
128                                        if(SUCCEEDED(pAmAudioRendererStats->GetStatParam(AM_AUDREND_STAT_PARAM_DISCONTINUITIES, &nDiscontinuityCount, &nDummy)))
129                                                pPropertyBag->WriteValue(_T("DiscontinuityCount"), CComVariant((LONG) nDiscontinuityCount));
130                                        if(SUCCEEDED(pAmAudioRendererStats->GetStatParam(AM_AUDREND_STAT_PARAM_BUFFERFULLNESS, &nBufferFullness, &nDummy)))
131                                                pPropertyBag->WriteValue(_T("BufferFullness"), CComVariant((LONG) nBufferFullness));
132                                        if(SUCCEEDED(pAmAudioRendererStats->GetStatParam(AM_AUDREND_STAT_PARAM_JITTER, &nJitter, &nDummy)))
133                                                pPropertyBag->WriteValue(_T("Jitter"), CComVariant((LONG) nJitter));
134                                }
135                                _ATLCATCHALL()
136                                {
137                                        _Z_EXCEPTION();
138                                }
139                        #pragma endregion
140                        if(pPropertyBag)
141                                pPropertyBag2 = pPropertyBag;
142                }
143                #pragma endregion
144                if(!pPropertyBag2 && pRunPropertyBagAware)
145                {
146                        CComPtr<IUnknown> pPropertyBagUnknown;
147                        __C(pRunPropertyBagAware->get_Value(&pPropertyBagUnknown));
148                        pPropertyBag2 = pPropertyBagUnknown;
149                }
150                if(pPropertyBag2)
151                {
152                        ULONG nPropertyCount = 0;
153                        __C(pPropertyBag2->CountProperties(&nPropertyCount));
154                        _Z4(atlTraceGeneral, 4, _T("nPropertyCount %d\n"), nPropertyCount);
155                        if(nPropertyCount)
156                        {
157                                CTempBufferT<PROPBAG2> pPropBags(nPropertyCount);
158                                ZeroMemory((PROPBAG2*) pPropBags, nPropertyCount * sizeof *pPropBags);
159                                ULONG nPropBagCount = 0;
160                                __C(pPropertyBag2->GetPropertyInfo(0, nPropertyCount, pPropBags, &nPropBagCount));
161                                _Z4(atlTraceGeneral, 4, _T("nPropBagCount %d\n"), nPropBagCount);
162                                CRoListT<CComHeapPtr<OLECHAR>> NameList;
163                                for(ULONG nIndex = 0; nIndex < nPropBagCount; nIndex++)
164                                        NameList.GetAt(NameList.AddTail()).Attach(pPropBags[nIndex].pstrName);
165                                CRoArrayT<CComVariantArray> ValueArray;
166                                __D(ValueArray.SetCount(nPropBagCount), E_OUTOFMEMORY);
167                                CTempBufferT<HRESULT> pnResults(nPropBagCount);
168                                __C(pPropertyBag2->Read(nPropBagCount, pPropBags, NULL, ValueArray.GetData(), pnResults));
169                                for(ULONG nIndex = 0; nIndex < nPropBagCount; nIndex++)
170                                        if(SUCCEEDED(pnResults[nIndex]))
171                                        {
172                                                CString sName(pPropBags[nIndex].pstrName);
173                                                _Z4(atlTraceGeneral, 4, _T("sName \"%s\"\n"), sName);
174                                                CComVariantArray vPropertyValue;
175                                                Array.Add(vPropertyValue.FromElements(2, CComVariant(sName), ValueArray[nIndex]));
176                                        }
177                                vValue.FromElementArray(Array);
178                        }
179                }
180                return vValue;
181        }
182        static CString GetPropertyBagText(CRoArrayT<CString>& NameArray, CRoMapT<CString, CComVariantArray>& Map)
183        {
184                CString sText;
185                _SortHelper::QuickSort<CNameSortTraits>(NameArray);
186                // SUGG: Nicer similar names, taking prefix at dot separator, also changing Foo-A-B into Foo (A; B)
187                for(SIZE_T nIndex = 0; nIndex < NameArray.GetCount(); nIndex++)
188                {
189                        const CString& sName = NameArray[nIndex];
190                        _Z4(atlTraceGeneral, 4, _T("sName \"%s\"\n"), sName);
191                        CString sValue;
192                        CComVariantArray vValue;
193                        if(!Map.Lookup(sName, vValue))
194                                continue;
195                        CRoArrayT<CString> CommentArray;
196                        #pragma region Friendly Comment
197                        switch(vValue.vt)
198                        {
199                        #pragma region VT_I4, VT_UI4
200                        case VT_I4:
201                        case VT_UI4:
202                                if(vValue.lVal < -999 || vValue.lVal > 999)
203                                        CommentArray.Add(_StringHelper::FormatNumber(vValue.lVal));
204                                CommentArray.Add(AtlFormatString(_T("0x%08X"), vValue.lVal));
205                                break;
206                        #pragma endregion
207                        #pragma region VT_I8, VT_UI8
208                        case VT_I8:
209                        case VT_UI8:
210                                {
211                                        CommentArray.Add(AtlFormatString(_T("0x%016X"), vValue.llVal));
212                                        if(vValue.ullVal >> 32)
213                                                CommentArray.Add(AtlFormatString(_T("%d, %d"), vValue.ullVal >> 32, (UINT32) vValue.ullVal));
214                                        CommentArray.Add(AtlFormatString(_T("0x%016X"), vValue.llVal));
215                                        if(vValue.lVal < -999 || vValue.lVal > 999)
216                                                CommentArray.Add(_StringHelper::FormatNumber(vValue.llVal));
217                                }
218                                break;
219                        #pragma endregion
220                        #pragma region VT_R8
221                        case VT_R8:
222                                if(vValue.dblVal > -0.001 || vValue.dblVal < 0.001)
223                                        CommentArray.Add(_StringHelper::FormatNumber(vValue.dblVal, 6));
224                                else 
225                                if(vValue.lVal < -999.0 || vValue.lVal > 999.0)
226                                        CommentArray.Add(_StringHelper::FormatNumber(vValue.dblVal, 1));
227                                break;
228                        #pragma endregion
229                        }
230                        #pragma endregion
231                        const HRESULT nChangeTypeResult = vValue.ChangeType(VT_BSTR);
232                        _Z45_HRESULT(nChangeTypeResult);
233                        if(FAILED(nChangeTypeResult))
234                                continue;
235                        sText.AppendFormat(_T(" * ") _T("%s: %s"), //_T("`%s`: `%s`"),
236                                sName, CString(vValue.bstrVal));
237                        if(!CommentArray.IsEmpty())
238                                sText.AppendFormat(_T(" // %s"), _StringHelper::Join(CommentArray, _T("; ")));
239                        sText.Append(_T("\r\n"));
240                }
241                return sText;
242        }
243        static ATL_DEPRECATED("Convert IPropertyBag2 into VARIANT using ISpy or CSpy instead") CString GetPropertyBagText(IPropertyBag2* pPropertyBag2)
244        {
245                _A(pPropertyBag2);
246                CString sText;
247                ULONG nPropertyCount = 0;
248                __C(pPropertyBag2->CountProperties(&nPropertyCount));
249                _Z4(atlTraceGeneral, 4, _T("nPropertyCount %d\n"), nPropertyCount);
250                if(!nPropertyCount)
251                        return sText;
252                #pragma region Read
253                CTempBufferT<PROPBAG2> pPropBags(nPropertyCount);
254                ZeroMemory((PROPBAG2*) pPropBags, nPropertyCount * sizeof *pPropBags);
255                ULONG nPropBagCount = 0;
256                __C(pPropertyBag2->GetPropertyInfo(0, nPropertyCount, pPropBags, &nPropBagCount));
257                _Z4(atlTraceGeneral, 4, _T("nPropBagCount %d\n"), nPropBagCount);
258                CRoListT<CComHeapPtr<OLECHAR>> NameList;
259                for(ULONG nIndex = 0; nIndex < nPropBagCount; nIndex++)
260                        NameList.GetAt(NameList.AddTail()).Attach(pPropBags[nIndex].pstrName);
261                CRoArrayT<CComVariantArray> ValueArray;
262                __D(ValueArray.SetCount(nPropBagCount), E_OUTOFMEMORY);
263                CTempBufferT<HRESULT> pnResults(nPropBagCount);
264                __C(pPropertyBag2->Read(nPropBagCount, pPropBags, NULL, ValueArray.GetData(), pnResults));
265                #pragma endregion
266                CRoMapT<CString, CComVariantArray> Map;
267                CRoArrayT<CString> NameArray;
268                for(ULONG nIndex = 0; nIndex < nPropBagCount; nIndex++)
269                        if(SUCCEEDED(pnResults[nIndex]))
270                        {
271                                CString sName(pPropBags[nIndex].pstrName);
272                                _Z4(atlTraceGeneral, 4, _T("sName \"%s\"\n"), sName);
273                                NameArray.Add(sName);
274                                _W(Map.SetAt(sName, ValueArray[nIndex]));
275                        }
276                return GetPropertyBagText(NameArray, Map);
277        }
278        static CString GetPropertyBagText(CComVariantArray& vValue)
279        {
280                if(vValue.vt <= VT_NULL)
281                        return _T("");
282                CRoArrayT<CComVariantArray> Array;
283                vValue.ToElementArray(Array);
284                CRoMapT<CString, CComVariantArray> Map;
285                CRoArrayT<CString> NameArray;
286                for(SIZE_T nIndex = 0; nIndex < Array.GetCount(); nIndex++)
287                {
288                        CComVariantArray vPropertyName, vPropertyValue;
289                        Array[nIndex].ToElements(2, &vPropertyName, &vPropertyValue);
290                        __C(vPropertyName.ChangeType(VT_BSTR));
291                        CString sName(vPropertyName.bstrVal);
292                        _Z4(atlTraceGeneral, 4, _T("sName \"%s\"\n"), sName);
293                        NameArray.Add(sName);
294                        _W(Map.SetAt(sName, vPropertyValue));
295                }
296                return GetPropertyBagText(NameArray, Map);
297        }
298        static CString GetPropertyBagText(VARIANT vValue)
299        {
300                return GetPropertyBagText(reinterpret_cast<CComVariantArray&>(vValue));
301        }
302        __if_exists(ISpy)
303        {
304                static CString GetPropertyBagText(IRunPropertyBagAware* pRunPropertyBagAware, ISpy* pSpy = NULL)
305                {
306                        if(!pRunPropertyBagAware)
307                                return _T("");
308                        return GetPropertyBagText(ReadRunPropertyBag(pRunPropertyBagAware));
309                }
310                static CString GetPropertyBagText(IUnknown* pBaseFilterUnknown, ISpy* pSpy = NULL)
311                {
312                        CComQIPtr<ISpy> pEffectiveSpy = pSpy;
313                        if(!pEffectiveSpy && pBaseFilterUnknown)
314                                pEffectiveSpy = _FilterGraphHelper::GetFilterGraph(CComQIPtr<IBaseFilter>(pBaseFilterUnknown));
315                        if(pEffectiveSpy && pBaseFilterUnknown)
316                        {
317                                CComVariantArray vValue;
318                                __C(pEffectiveSpy->ReadRunPropertyBag(pBaseFilterUnknown, ATL_VARIANT_TRUE, &vValue));
319                                return GetPropertyBagText(vValue);
320                        }
321                        const CComQIPtr<IRunPropertyBagAware> pRunPropertyBagAware = pBaseFilterUnknown;
322                        return GetPropertyBagText(pRunPropertyBagAware, pEffectiveSpy);
323                }
324                static CString GetPropertyBagText(IFilterGraph* pFilterGraph, ISpy* pSpy = NULL)
325                {
326                        const CComQIPtr<IServiceProvider> pServiceProvider = pFilterGraph;
327                        if(pServiceProvider)
328                        {
329                                CComPtr<IRunPropertyBagAware> pRunPropertyBagAware;
330                                pServiceProvider->QueryService<IRunPropertyBagAware>(__uuidof(IRunPropertyBagAware), &pRunPropertyBagAware);
331                                if(pRunPropertyBagAware)
332                                        return GetPropertyBagText(pRunPropertyBagAware, pSpy);
333                        }
334                        return _T("");
335                }
336        }
337};
338
339#if !defined(DIRECTSHOWSPY)
340
341////////////////////////////////////////////////////////////
342// CRunPropertyBagAwareT
343
344template <typename T>
345class ATL_NO_VTABLE CRunPropertyBagAwareT :
346        public IDispatchImpl<DIRECTSHOWSPY_NAMESPACE_PREFIX IRunPropertyBagAware, &__uuidof(DIRECTSHOWSPY_NAMESPACE_PREFIX IRunPropertyBagAware), &__uuidof(DIRECTSHOWSPY_NAMESPACE_PREFIX __AlaxInfoDirectShowSpy)>
347{
348public:
349// CRunPropertyBagAwareT
350
351// AlaxInfoDirectShowSpy::IRunPropertyBagAware
352        STDMETHOD(get_Value)(IUnknown** ppPropertyBagUnknown)
353        {
354                _Z5(atlTraceCOM, 5, _T("...\n"));
355                _ATLTRY
356                {
357                        __D(ppPropertyBagUnknown, E_POINTER);
358                        T* pT = static_cast<T*>(this);
359                        *ppPropertyBagUnknown = (IPropertyBag*) pT->CreatePerformancePropertyBag().Detach();
360                }
361                _ATLCATCH(Exception)
362                {
363                        _C(Exception);
364                }
365                return S_OK;
366        }
367};
368
369#endif // !defined(DIRECTSHOWSPY)
Note: See TracBrowser for help on using the repository browser.