Add support for ICE/STUN/TURN in res_rtp_asterisk and chan_sip.
[asterisk/asterisk.git] / res / pjproject / third_party / BaseClasses / wxdebug.cpp
1 //------------------------------------------------------------------------------
2 // File: WXDebug.cpp
3 //
4 // Desc: DirectShow base classes - implements ActiveX system debugging
5 //       facilities.
6 //
7 // Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.
8 //------------------------------------------------------------------------------
9
10 #include <pjmedia-videodev/config.h>
11
12 #if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0
13
14 #define _WINDLL
15
16 #include <streams.h>
17 #include <stdarg.h>
18 #include <stdio.h>
19 #include <dvdmedia.h>
20
21 #ifdef DEBUG
22 #ifdef UNICODE
23 #ifndef _UNICODE
24 #define _UNICODE
25 #endif // _UNICODE
26 #endif // UNICODE
27 #endif // DEBUG
28
29 #include <tchar.h>
30 #include <strsafe.h>
31
32 #ifdef DEBUG
33 static void DisplayBITMAPINFO(const BITMAPINFOHEADER* pbmi);
34 static void DisplayRECT(LPCTSTR szLabel, const RECT& rc);
35
36 // The Win32 wsprintf() function writes a maximum of 1024 characters to it's output buffer.
37 // See the documentation for wsprintf()'s lpOut parameter for more information.
38 const INT iDEBUGINFO = 1024;                 // Used to format strings
39
40 /* For every module and executable we store a debugging level for each of
41    the five categories (eg LOG_ERROR and LOG_TIMING). This makes it easy
42    to isolate and debug individual modules without seeing everybody elses
43    spurious debug output. The keys are stored in the registry under the
44    HKEY_LOCAL_MACHINE\SOFTWARE\Debug\<Module Name>\<KeyName> key values
45    NOTE these must be in the same order as their enumeration definition */
46
47 const LPCTSTR pKeyNames[] = {
48     TEXT("TIMING"),      // Timing and performance measurements
49     TEXT("TRACE"),       // General step point call tracing
50     TEXT("MEMORY"),      // Memory and object allocation/destruction
51     TEXT("LOCKING"),     // Locking/unlocking of critical sections
52     TEXT("ERROR"),       // Debug error notification
53     TEXT("CUSTOM1"),
54     TEXT("CUSTOM2"),
55     TEXT("CUSTOM3"),
56     TEXT("CUSTOM4"),
57     TEXT("CUSTOM5")
58     };
59
60 const TCHAR CAutoTrace::_szEntering[] = TEXT("->: %s");
61 const TCHAR CAutoTrace::_szLeaving[]  = TEXT("<-: %s");
62
63 const INT iMAXLEVELS = NUMELMS(pKeyNames);  // Maximum debug categories
64
65 HINSTANCE m_hInst;                          // Module instance handle
66 TCHAR m_ModuleName[iDEBUGINFO];             // Cut down module name
67 DWORD m_Levels[iMAXLEVELS];                 // Debug level per category
68 CRITICAL_SECTION m_CSDebug;                 // Controls access to list
69 DWORD m_dwNextCookie;                       // Next active object ID
70 ObjectDesc *pListHead = NULL;               // First active object
71 DWORD m_dwObjectCount;                      // Active object count
72 BOOL m_bInit = FALSE;                       // Have we been initialised
73 HANDLE m_hOutput = INVALID_HANDLE_VALUE;    // Optional output written here
74 DWORD dwWaitTimeout = INFINITE;             // Default timeout value
75 DWORD dwTimeOffset;                         // Time of first DbgLog call
76 bool g_fUseKASSERT = false;                 // don't create messagebox
77 bool g_fDbgInDllEntryPoint = false;
78 bool g_fAutoRefreshLevels = false;
79
80 LPCTSTR pBaseKey = TEXT("SOFTWARE\\Microsoft\\DirectShow\\Debug");
81 LPCTSTR pGlobalKey = TEXT("GLOBAL");
82 static CHAR *pUnknownName = "UNKNOWN";
83
84 LPCTSTR TimeoutName = TEXT("TIMEOUT");
85
86 /* This sets the instance handle that the debug library uses to find
87    the module's file name from the Win32 GetModuleFileName function */
88
89 void WINAPI DbgInitialise(HINSTANCE hInst)
90 {
91     InitializeCriticalSection(&m_CSDebug);
92     m_bInit = TRUE;
93
94     m_hInst = hInst;
95     DbgInitModuleName();
96     if (GetProfileInt(m_ModuleName, TEXT("BreakOnLoad"), 0))
97        DebugBreak();
98     DbgInitModuleSettings(false);
99     DbgInitGlobalSettings(true);
100     dwTimeOffset = timeGetTime();
101 }
102
103
104 /* This is called to clear up any resources the debug library uses - at the
105    moment we delete our critical section and the object list. The values we
106    retrieve from the registry are all done during initialisation but we don't
107    go looking for update notifications while we are running, if the values
108    are changed then the application has to be restarted to pick them up */
109
110 void WINAPI DbgTerminate()
111 {
112     if (m_hOutput != INVALID_HANDLE_VALUE) {
113        EXECUTE_ASSERT(CloseHandle(m_hOutput));
114        m_hOutput = INVALID_HANDLE_VALUE;
115     }
116     DeleteCriticalSection(&m_CSDebug);
117     m_bInit = FALSE;
118 }
119
120
121 /* This is called by DbgInitLogLevels to read the debug settings
122    for each logging category for this module from the registry */
123
124 void WINAPI DbgInitKeyLevels(HKEY hKey, bool fTakeMax)
125 {
126     LONG lReturn;               // Create key return value
127     LONG lKeyPos;               // Current key category
128     DWORD dwKeySize;            // Size of the key value
129     DWORD dwKeyType;            // Receives it's type
130     DWORD dwKeyValue;           // This fields value
131
132     /* Try and read a value for each key position in turn */
133     for (lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) {
134
135         dwKeySize = sizeof(DWORD);
136         lReturn = RegQueryValueEx(
137             hKey,                       // Handle to an open key
138             pKeyNames[lKeyPos],         // Subkey name derivation
139             NULL,                       // Reserved field
140             &dwKeyType,                 // Returns the field type
141             (LPBYTE) &dwKeyValue,       // Returns the field's value
142             &dwKeySize );               // Number of bytes transferred
143
144         /* If either the key was not available or it was not a DWORD value
145            then we ensure only the high priority debug logging is output
146            but we try and update the field to a zero filled DWORD value */
147
148         if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD)  {
149
150             dwKeyValue = 0;
151             lReturn = RegSetValueEx(
152                 hKey,                   // Handle of an open key
153                 pKeyNames[lKeyPos],     // Address of subkey name
154                 (DWORD) 0,              // Reserved field
155                 REG_DWORD,              // Type of the key field
156                 (PBYTE) &dwKeyValue,    // Value for the field
157                 sizeof(DWORD));         // Size of the field buffer
158
159             if (lReturn != ERROR_SUCCESS) {
160                 DbgLog((LOG_ERROR,1,TEXT("Could not create subkey %s"),pKeyNames[lKeyPos]));
161                 dwKeyValue = 0;
162             }
163         }
164         if(fTakeMax)
165         {
166             m_Levels[lKeyPos] = max(dwKeyValue,m_Levels[lKeyPos]);
167         }
168         else
169         {
170             if((m_Levels[lKeyPos] & LOG_FORCIBLY_SET) == 0) {
171                 m_Levels[lKeyPos] = dwKeyValue;
172             }
173         }
174     }
175
176     /*  Read the timeout value for catching hangs */
177     dwKeySize = sizeof(DWORD);
178     lReturn = RegQueryValueEx(
179         hKey,                       // Handle to an open key
180         TimeoutName,                // Subkey name derivation
181         NULL,                       // Reserved field
182         &dwKeyType,                 // Returns the field type
183         (LPBYTE) &dwWaitTimeout,    // Returns the field's value
184         &dwKeySize );               // Number of bytes transferred
185
186     /* If either the key was not available or it was not a DWORD value
187        then we ensure only the high priority debug logging is output
188        but we try and update the field to a zero filled DWORD value */
189
190     if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD)  {
191
192         dwWaitTimeout = INFINITE;
193         lReturn = RegSetValueEx(
194             hKey,                   // Handle of an open key
195             TimeoutName,            // Address of subkey name
196             (DWORD) 0,              // Reserved field
197             REG_DWORD,              // Type of the key field
198             (PBYTE) &dwWaitTimeout, // Value for the field
199             sizeof(DWORD));         // Size of the field buffer
200
201         if (lReturn != ERROR_SUCCESS) {
202             DbgLog((LOG_ERROR,1,TEXT("Could not create subkey %s"),pKeyNames[lKeyPos]));
203             dwWaitTimeout = INFINITE;
204         }
205     }
206 }
207
208 void WINAPI DbgOutString(LPCTSTR psz)
209 {
210     if (m_hOutput != INVALID_HANDLE_VALUE) {
211         UINT  cb = lstrlen(psz);
212         DWORD dw;
213 #ifdef UNICODE
214         CHAR szDest[2048];
215         WideCharToMultiByte(CP_ACP, 0, psz, -1, szDest, NUMELMS(szDest), 0, 0);
216         WriteFile (m_hOutput, szDest, cb, &dw, NULL);
217 #else
218         WriteFile (m_hOutput, psz, cb, &dw, NULL);
219 #endif
220     } else {
221         OutputDebugString (psz);
222     }
223 }
224
225
226
227
228 HRESULT  DbgUniqueProcessName(LPCTSTR inName, LPTSTR outName)
229 {
230     HRESULT hr = S_OK;
231     const TCHAR *pIn = inName;
232     int dotPos = -1;
233
234     //scan the input and record the last '.' position
235     while (*pIn && (pIn - inName) < MAX_PATH)
236     {
237         if ( TEXT('.') == *pIn )
238             dotPos = (int)(pIn-inName);
239         ++pIn;
240     }
241
242     if (*pIn) //input should be zero-terminated within MAX_PATH
243         return E_INVALIDARG;
244
245     DWORD dwProcessId = GetCurrentProcessId();
246
247     if (dotPos < 0) 
248     {
249         //no extension in the input, appending process id to the input
250         hr = StringCchPrintf(outName, MAX_PATH, TEXT("%s_%d"), inName, dwProcessId);
251     }
252     else
253     {
254         TCHAR pathAndBasename[MAX_PATH] = {0};
255         
256         //there's an extension  - zero-terminate the path and basename first by copying
257         hr = StringCchCopyN(pathAndBasename, MAX_PATH, inName, (size_t)dotPos);
258
259         //re-combine path, basename and extension with processId appended to a basename
260         if (SUCCEEDED(hr))
261             hr = StringCchPrintf(outName, MAX_PATH, TEXT("%s_%d%s"), pathAndBasename, dwProcessId, inName + dotPos);
262     }
263
264     return hr;
265 }
266
267
268 /* Called by DbgInitGlobalSettings to setup alternate logging destinations
269  */
270
271 void WINAPI DbgInitLogTo (
272     HKEY hKey)
273 {
274     LONG  lReturn;
275     DWORD dwKeyType;
276     DWORD dwKeySize;
277     TCHAR szFile[MAX_PATH] = {0};
278     static const TCHAR cszKey[] = TEXT("LogToFile");
279
280     dwKeySize = MAX_PATH;
281     lReturn = RegQueryValueEx(
282         hKey,                       // Handle to an open key
283         cszKey,                     // Subkey name derivation
284         NULL,                       // Reserved field
285         &dwKeyType,                 // Returns the field type
286         (LPBYTE) szFile,            // Returns the field's value
287         &dwKeySize);                // Number of bytes transferred
288
289     // create an empty key if it does not already exist
290     //
291     if (lReturn != ERROR_SUCCESS || dwKeyType != REG_SZ)
292        {
293        dwKeySize = sizeof(TCHAR);
294        lReturn = RegSetValueEx(
295             hKey,                   // Handle of an open key
296             cszKey,                 // Address of subkey name
297             (DWORD) 0,              // Reserved field
298             REG_SZ,                 // Type of the key field
299             (PBYTE)szFile,          // Value for the field
300             dwKeySize);            // Size of the field buffer
301        }
302
303     // if an output-to was specified.  try to open it.
304     //
305     if (m_hOutput != INVALID_HANDLE_VALUE) {
306        EXECUTE_ASSERT(CloseHandle (m_hOutput));
307        m_hOutput = INVALID_HANDLE_VALUE;
308     }
309     if (szFile[0] != 0)
310        {
311        if (!lstrcmpi(szFile, TEXT("Console"))) {
312           m_hOutput = GetStdHandle (STD_OUTPUT_HANDLE);
313           if (m_hOutput == INVALID_HANDLE_VALUE) {
314              AllocConsole ();
315              m_hOutput = GetStdHandle (STD_OUTPUT_HANDLE);
316           }
317           SetConsoleTitle (TEXT("ActiveX Debug Output"));
318        } else if (szFile[0] &&
319                 lstrcmpi(szFile, TEXT("Debug")) &&
320                 lstrcmpi(szFile, TEXT("Debugger")) &&
321                 lstrcmpi(szFile, TEXT("Deb")))
322           {
323             m_hOutput = CreateFile(szFile, GENERIC_WRITE,
324                                  FILE_SHARE_READ,
325                                  NULL, OPEN_ALWAYS,
326                                  FILE_ATTRIBUTE_NORMAL,
327                                  NULL);
328
329             if (INVALID_HANDLE_VALUE == m_hOutput &&
330                 GetLastError() == ERROR_SHARING_VIOLATION)
331             {
332                TCHAR uniqueName[MAX_PATH] = {0};
333                if (SUCCEEDED(DbgUniqueProcessName(szFile, uniqueName)))
334                {
335                     m_hOutput = CreateFile(uniqueName, GENERIC_WRITE,
336                                          FILE_SHARE_READ,
337                                          NULL, OPEN_ALWAYS,
338                                          FILE_ATTRIBUTE_NORMAL,
339                                          NULL);
340                }
341             }
342                
343             if (INVALID_HANDLE_VALUE != m_hOutput)
344             {
345               static const TCHAR cszBar[] = TEXT("\r\n\r\n=====DbgInitialize()=====\r\n\r\n");
346               SetFilePointer (m_hOutput, 0, NULL, FILE_END);
347               DbgOutString (cszBar);
348             }
349           }
350        }
351 }
352
353
354
355 /* This is called by DbgInitLogLevels to read the global debug settings for
356    each logging category for this module from the registry. Normally each
357    module has it's own values set for it's different debug categories but
358    setting the global SOFTWARE\Debug\Global applies them to ALL modules */
359
360 void WINAPI DbgInitGlobalSettings(bool fTakeMax)
361 {
362     LONG lReturn;               // Create key return value
363     TCHAR szInfo[iDEBUGINFO];   // Constructs key names
364     HKEY hGlobalKey;            // Global override key
365
366     /* Construct the global base key name */
367     (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%s\\%s"),pBaseKey,pGlobalKey);
368
369     /* Create or open the key for this module */
370     lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE,   // Handle of an open key
371                              szInfo,               // Address of subkey name
372                              (DWORD) 0,            // Reserved value
373                              NULL,                 // Address of class name
374                              (DWORD) 0,            // Special options flags
375                              GENERIC_READ | GENERIC_WRITE,   // Desired security access
376                              NULL,                 // Key security descriptor
377                              &hGlobalKey,          // Opened handle buffer
378                              NULL);                // What really happened
379
380     if (lReturn != ERROR_SUCCESS) {
381         lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE,   // Handle of an open key
382                                  szInfo,               // Address of subkey name
383                                  (DWORD) 0,            // Reserved value
384                                  NULL,                 // Address of class name
385                                  (DWORD) 0,            // Special options flags
386                                  GENERIC_READ,         // Desired security access
387                                  NULL,                 // Key security descriptor
388                                  &hGlobalKey,          // Opened handle buffer
389                                  NULL);                // What really happened
390         if (lReturn != ERROR_SUCCESS) {
391             DbgLog((LOG_ERROR,1,TEXT("Could not access GLOBAL module key")));
392         }
393         return;
394     }
395
396     DbgInitKeyLevels(hGlobalKey, fTakeMax);
397     RegCloseKey(hGlobalKey);
398 }
399
400
401 /* This sets the debugging log levels for the different categories. We start
402    by opening (or creating if not already available) the SOFTWARE\Debug key
403    that all these settings live under. We then look at the global values
404    set under SOFTWARE\Debug\Global which apply on top of the individual
405    module settings. We then load the individual module registry settings */
406
407 void WINAPI DbgInitModuleSettings(bool fTakeMax)
408 {
409     LONG lReturn;               // Create key return value
410     TCHAR szInfo[iDEBUGINFO];   // Constructs key names
411     HKEY hModuleKey;            // Module key handle
412
413     /* Construct the base key name */
414     (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%s\\%s"),pBaseKey,m_ModuleName);
415
416     /* Create or open the key for this module */
417     lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE,   // Handle of an open key
418                              szInfo,               // Address of subkey name
419                              (DWORD) 0,            // Reserved value
420                              NULL,                 // Address of class name
421                              (DWORD) 0,            // Special options flags
422                              GENERIC_READ | GENERIC_WRITE, // Desired security access
423                              NULL,                 // Key security descriptor
424                              &hModuleKey,          // Opened handle buffer
425                              NULL);                // What really happened
426
427     if (lReturn != ERROR_SUCCESS) {
428         lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE,   // Handle of an open key
429                                  szInfo,               // Address of subkey name
430                                  (DWORD) 0,            // Reserved value
431                                  NULL,                 // Address of class name
432                                  (DWORD) 0,            // Special options flags
433                                  GENERIC_READ,         // Desired security access
434                                  NULL,                 // Key security descriptor
435                                  &hModuleKey,          // Opened handle buffer
436                                  NULL);                // What really happened
437         if (lReturn != ERROR_SUCCESS) {
438             DbgLog((LOG_ERROR,1,TEXT("Could not access module key")));
439         }
440         return;
441     }
442
443     DbgInitLogTo(hModuleKey);
444     DbgInitKeyLevels(hModuleKey, fTakeMax);
445     RegCloseKey(hModuleKey);
446 }
447
448
449 /* Initialise the module file name */
450
451 void WINAPI DbgInitModuleName()
452 {
453     TCHAR FullName[iDEBUGINFO];     // Load the full path and module name
454     LPTSTR pName;                   // Searches from the end for a backslash
455
456     GetModuleFileName(m_hInst,FullName,iDEBUGINFO);
457     pName = _tcsrchr(FullName,'\\');
458     if (pName == NULL) {
459         pName = FullName;
460     } else {
461         pName++;
462     }
463     (void)StringCchCopy(m_ModuleName,NUMELMS(m_ModuleName), pName);
464 }
465
466 struct MsgBoxMsg
467 {
468     HWND hwnd;
469     LPCTSTR szTitle;
470     LPCTSTR szMessage;
471     DWORD dwFlags;
472     INT iResult;
473 };
474
475 //
476 // create a thread to call MessageBox(). calling MessageBox() on
477 // random threads at bad times can confuse the host (eg IE).
478 //
479 DWORD WINAPI MsgBoxThread(
480   __inout LPVOID lpParameter   // thread data
481   )
482 {
483     MsgBoxMsg *pmsg = (MsgBoxMsg *)lpParameter;
484     pmsg->iResult = MessageBox(
485         pmsg->hwnd,
486         pmsg->szTitle,
487         pmsg->szMessage,
488         pmsg->dwFlags);
489
490     return 0;
491 }
492
493 INT MessageBoxOtherThread(
494     HWND hwnd,
495     LPCTSTR szTitle,
496     LPCTSTR szMessage,
497     DWORD dwFlags)
498 {
499     if(g_fDbgInDllEntryPoint)
500     {
501         // can't wait on another thread because we have the loader
502         // lock held in the dll entry point.
503         // This can crash sometimes so just skip it
504         // return MessageBox(hwnd, szTitle, szMessage, dwFlags);
505         return IDCANCEL;
506     }
507     else
508     {
509         MsgBoxMsg msg = {hwnd, szTitle, szMessage, dwFlags, 0};
510         DWORD dwid;
511         HANDLE hThread = CreateThread(
512             0,                      // security
513             0,                      // stack size
514             MsgBoxThread,
515             (void *)&msg,           // arg
516             0,                      // flags
517             &dwid);
518         if(hThread)
519         {
520             WaitForSingleObject(hThread, INFINITE);
521             CloseHandle(hThread);
522             return msg.iResult;
523         }
524
525         // break into debugger on failure.
526         return IDCANCEL;
527     }
528 }
529
530 /* Displays a message box if the condition evaluated to FALSE */
531
532 void WINAPI DbgAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine)
533 {
534     if(g_fUseKASSERT)
535     {
536         DbgKernelAssert(pCondition, pFileName, iLine);
537     }
538     else
539     {
540
541         TCHAR szInfo[iDEBUGINFO];
542
543         (void)StringCchPrintf(szInfo, NUMELMS(szInfo),TEXT("%s \nAt line %d of %s\nContinue? (Cancel to debug)"),
544                  pCondition, iLine, pFileName);
545
546         INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("ASSERT Failed"),
547                                           MB_SYSTEMMODAL |
548                                           MB_ICONHAND |
549                                           MB_YESNOCANCEL |
550                                           MB_SETFOREGROUND);
551         switch (MsgId)
552         {
553           case IDNO:              /* Kill the application */
554
555               FatalAppExit(FALSE, TEXT("Application terminated"));
556               break;
557
558           case IDCANCEL:          /* Break into the debugger */
559
560               DebugBreak();
561               break;
562
563           case IDYES:             /* Ignore assertion continue execution */
564               break;
565         }
566     }
567 }
568
569 /* Displays a message box at a break point */
570
571 void WINAPI DbgBreakPoint(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine)
572 {
573     if(g_fUseKASSERT)
574     {
575         DbgKernelAssert(pCondition, pFileName, iLine);
576     }
577     else
578     {
579         TCHAR szInfo[iDEBUGINFO];
580
581         (void)StringCchPrintf(szInfo, NUMELMS(szInfo),TEXT("%s \nAt line %d of %s\nContinue? (Cancel to debug)"),
582                  pCondition, iLine, pFileName);
583
584         INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("Hard coded break point"),
585                                           MB_SYSTEMMODAL |
586                                           MB_ICONHAND |
587                                           MB_YESNOCANCEL |
588                                           MB_SETFOREGROUND);
589         switch (MsgId)
590         {
591           case IDNO:              /* Kill the application */
592
593               FatalAppExit(FALSE, TEXT("Application terminated"));
594               break;
595
596           case IDCANCEL:          /* Break into the debugger */
597
598               DebugBreak();
599               break;
600
601           case IDYES:             /* Ignore break point continue execution */
602               break;
603         }
604     }
605 }
606
607 void WINAPI DbgBreakPoint(LPCTSTR pFileName,INT iLine,__format_string LPCTSTR szFormatString,...)
608 {
609     // A debug break point message can have at most 2000 characters if
610     // ANSI or UNICODE characters are being used.  A debug break point message
611     // can have between 1000 and 2000 double byte characters in it.  If a
612     // particular message needs more characters, then the value of this constant
613     // should be increased.
614     const DWORD MAX_BREAK_POINT_MESSAGE_SIZE = 2000;
615
616     TCHAR szBreakPointMessage[MAX_BREAK_POINT_MESSAGE_SIZE];
617
618     va_list va;
619     va_start( va, szFormatString );
620
621     HRESULT hr = StringCchVPrintf( szBreakPointMessage, NUMELMS(szBreakPointMessage), szFormatString, va );
622
623     va_end(va);
624
625     if( FAILED(hr) ) {
626         DbgBreak( "ERROR in DbgBreakPoint().  The variable length debug message could not be displayed because StringCchVPrintf() failed." );
627         return;
628     }
629
630     ::DbgBreakPoint( szBreakPointMessage, pFileName, iLine );
631 }
632
633
634 /* When we initialised the library we stored in the m_Levels array the current
635    debug output level for this module for each of the five categories. When
636    some debug logging is sent to us it can be sent with a combination of the
637    categories (if it is applicable to many for example) in which case we map
638    the type's categories into their current debug levels and see if any of
639    them can be accepted. The function looks at each bit position in turn from
640    the input type field and then compares it's debug level with the modules.
641
642    A level of 0 means that output is always sent to the debugger.  This is
643    due to producing output if the input level is <= m_Levels.
644 */
645
646
647 BOOL WINAPI DbgCheckModuleLevel(DWORD Type,DWORD Level)
648 {
649     if(g_fAutoRefreshLevels)
650     {
651         // re-read the registry every second. We cannot use RegNotify() to
652         // notice registry changes because it's not available on win9x.
653         static DWORD g_dwLastRefresh = 0;
654         DWORD dwTime = timeGetTime();
655         if(dwTime - g_dwLastRefresh > 1000) {
656             g_dwLastRefresh = dwTime;
657
658             // there's a race condition: multiple threads could update the
659             // values. plus read and write not synchronized. no harm
660             // though.
661             DbgInitModuleSettings(false);
662         }
663     }
664
665
666     DWORD Mask = 0x01;
667
668     // If no valid bits are set return FALSE
669     if ((Type & ((1<<iMAXLEVELS)-1))) {
670
671         // speed up unconditional output.
672         if (0==Level)
673             return(TRUE);
674         
675         for (LONG lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) {
676             if (Type & Mask) {
677                 if (Level <= (m_Levels[lKeyPos] & ~LOG_FORCIBLY_SET)) {
678                     return TRUE;
679                 }
680             }
681             Mask <<= 1;
682         }
683     }
684     return FALSE;
685 }
686
687
688 /* Set debug levels to a given value */
689
690 void WINAPI DbgSetModuleLevel(DWORD Type, DWORD Level)
691 {
692     DWORD Mask = 0x01;
693
694     for (LONG lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) {
695         if (Type & Mask) {
696             m_Levels[lKeyPos] = Level | LOG_FORCIBLY_SET;
697         }
698         Mask <<= 1;
699     }
700 }
701
702 /* whether to check registry values periodically. this isn't turned
703    automatically because of the potential performance hit. */
704 void WINAPI DbgSetAutoRefreshLevels(bool fAuto)
705 {
706     g_fAutoRefreshLevels = fAuto;
707 }
708
709 #ifdef UNICODE
710 //
711 // warning -- this function is implemented twice for ansi applications
712 // linking to the unicode library
713 //
714 void WINAPI DbgLogInfo(DWORD Type,DWORD Level,__format_string LPCSTR pFormat,...)
715 {
716     /* Check the current level for this type combination */
717
718     BOOL bAccept = DbgCheckModuleLevel(Type,Level);
719     if (bAccept == FALSE) {
720         return;
721     }
722
723     TCHAR szInfo[2000];
724
725     /* Format the variable length parameter list */
726
727     va_list va;
728     va_start(va, pFormat);
729
730     (void)StringCchPrintf(szInfo, NUMELMS(szInfo),
731              TEXT("%s(tid %x) %8d : "),
732              m_ModuleName,
733              GetCurrentThreadId(), timeGetTime() - dwTimeOffset);
734
735     CHAR szInfoA[2000];
736     WideCharToMultiByte(CP_ACP, 0, szInfo, -1, szInfoA, NUMELMS(szInfoA), 0, 0);
737
738     (void)StringCchVPrintfA(szInfoA + lstrlenA(szInfoA), NUMELMS(szInfoA) - lstrlenA(szInfoA), pFormat, va);
739     (void)StringCchCatA(szInfoA, NUMELMS(szInfoA), "\r\n");
740
741     WCHAR wszOutString[2000];
742     MultiByteToWideChar(CP_ACP, 0, szInfoA, -1, wszOutString, NUMELMS(wszOutString));
743     DbgOutString(wszOutString);
744
745     va_end(va);
746 }
747
748 void WINAPI DbgAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine)
749 {
750     if(g_fUseKASSERT)
751     {
752         DbgKernelAssert(pCondition, pFileName, iLine);
753     }
754     else
755     {
756
757         TCHAR szInfo[iDEBUGINFO];
758
759         (void)StringCchPrintf(szInfo, NUMELMS(szInfo), TEXT("%hs \nAt line %d of %hs\nContinue? (Cancel to debug)"),
760                  pCondition, iLine, pFileName);
761
762         INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("ASSERT Failed"),
763                                           MB_SYSTEMMODAL |
764                                           MB_ICONHAND |
765                                           MB_YESNOCANCEL |
766                                           MB_SETFOREGROUND);
767         switch (MsgId)
768         {
769           case IDNO:              /* Kill the application */
770
771               FatalAppExit(FALSE, TEXT("Application terminated"));
772               break;
773
774           case IDCANCEL:          /* Break into the debugger */
775
776               DebugBreak();
777               break;
778
779           case IDYES:             /* Ignore assertion continue execution */
780               break;
781         }
782     }
783 }
784
785 /* Displays a message box at a break point */
786
787 void WINAPI DbgBreakPoint(LPCSTR pCondition,LPCSTR pFileName,INT iLine)
788 {
789     if(g_fUseKASSERT)
790     {
791         DbgKernelAssert(pCondition, pFileName, iLine);
792     }
793     else
794     {
795         TCHAR szInfo[iDEBUGINFO];
796
797         (void)StringCchPrintf(szInfo, NUMELMS(szInfo),TEXT("%hs \nAt line %d of %hs\nContinue? (Cancel to debug)"),
798                  pCondition, iLine, pFileName);
799
800         INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("Hard coded break point"),
801                                           MB_SYSTEMMODAL |
802                                           MB_ICONHAND |
803                                           MB_YESNOCANCEL |
804                                           MB_SETFOREGROUND);
805         switch (MsgId)
806         {
807           case IDNO:              /* Kill the application */
808
809               FatalAppExit(FALSE, TEXT("Application terminated"));
810               break;
811
812           case IDCANCEL:          /* Break into the debugger */
813
814               DebugBreak();
815               break;
816
817           case IDYES:             /* Ignore break point continue execution */
818               break;
819         }
820     }
821 }
822
823 void WINAPI DbgKernelAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine)
824 {
825     DbgLog((LOG_ERROR,0,TEXT("Assertion FAILED (%hs) at line %d in file %hs"),
826            pCondition, iLine, pFileName));
827     DebugBreak();
828 }
829
830 #endif
831
832 /* Print a formatted string to the debugger prefixed with this module's name
833    Because the COMBASE classes are linked statically every module loaded will
834    have their own copy of this code. It therefore helps if the module name is
835    included on the output so that the offending code can be easily found */
836
837 //
838 // warning -- this function is implemented twice for ansi applications
839 // linking to the unicode library
840 //
841 void WINAPI DbgLogInfo(DWORD Type,DWORD Level,LPCTSTR pFormat,...)
842 {
843
844     /* Check the current level for this type combination */
845
846     BOOL bAccept = DbgCheckModuleLevel(Type,Level);
847     if (bAccept == FALSE) {
848         return;
849     }
850
851     TCHAR szInfo[2000];
852
853     /* Format the variable length parameter list */
854
855     va_list va;
856     va_start(va, pFormat);
857
858     (void)StringCchPrintf(szInfo, NUMELMS(szInfo),
859              TEXT("%s(tid %x) %8d : "),
860              m_ModuleName,
861              GetCurrentThreadId(), timeGetTime() - dwTimeOffset);
862
863     (void)StringCchVPrintf(szInfo + lstrlen(szInfo), NUMELMS(szInfo) - lstrlen(szInfo), pFormat, va);
864     (void)StringCchCat(szInfo, NUMELMS(szInfo), TEXT("\r\n"));
865     DbgOutString(szInfo);
866
867     va_end(va);
868 }
869
870
871 /* If we are executing as a pure kernel filter we cannot display message
872    boxes to the user, this provides an alternative which puts the error
873    condition on the debugger output with a suitable eye catching message */
874
875 void WINAPI DbgKernelAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine)
876 {
877     DbgLog((LOG_ERROR,0,TEXT("Assertion FAILED (%s) at line %d in file %s"),
878            pCondition, iLine, pFileName));
879     DebugBreak();
880 }
881
882
883
884 /* Each time we create an object derived from CBaseObject the constructor will
885    call us to register the creation of the new object. We are passed a string
886    description which we store away. We return a cookie that the constructor
887    uses to identify the object when it is destroyed later on. We update the
888    total number of active objects in the DLL mainly for debugging purposes */
889
890 DWORD WINAPI DbgRegisterObjectCreation(LPCSTR szObjectName,
891                                        LPCWSTR wszObjectName)
892 {
893     /* If this fires you have a mixed DEBUG/RETAIL build */
894
895     ASSERT(!!szObjectName ^ !!wszObjectName);
896
897     /* Create a place holder for this object description */
898
899     ObjectDesc *pObject = new ObjectDesc;
900     ASSERT(pObject);
901
902     /* It is valid to pass a NULL object name */
903     if (pObject == NULL) {
904         return FALSE;
905     }
906
907     /* Check we have been initialised - we may not be initialised when we are
908        being pulled in from an executable which has globally defined objects
909        as they are created by the C++ run time before WinMain is called */
910
911     if (m_bInit == FALSE) {
912         DbgInitialise(GetModuleHandle(NULL));
913     }
914
915     /* Grab the list critical section */
916     EnterCriticalSection(&m_CSDebug);
917
918     /* If no name then default to UNKNOWN */
919     if (!szObjectName && !wszObjectName) {
920         szObjectName = pUnknownName;
921     }
922
923     /* Put the new description at the head of the list */
924
925     pObject->m_szName = szObjectName;
926     pObject->m_wszName = wszObjectName;
927     pObject->m_dwCookie = ++m_dwNextCookie;
928     pObject->m_pNext = pListHead;
929
930     pListHead = pObject;
931     m_dwObjectCount++;
932
933     DWORD ObjectCookie = pObject->m_dwCookie;
934     ASSERT(ObjectCookie);
935
936     if(wszObjectName) {
937         DbgLog((LOG_MEMORY,2,TEXT("Object created   %d (%ls) %d Active"),
938                 pObject->m_dwCookie, wszObjectName, m_dwObjectCount));
939     } else {
940         DbgLog((LOG_MEMORY,2,TEXT("Object created   %d (%hs) %d Active"),
941                 pObject->m_dwCookie, szObjectName, m_dwObjectCount));
942     }
943
944     LeaveCriticalSection(&m_CSDebug);
945     return ObjectCookie;
946 }
947
948
949 /* This is called by the CBaseObject destructor when an object is about to be
950    destroyed, we are passed the cookie we returned during construction that
951    identifies this object. We scan the object list for a matching cookie and
952    remove the object if successful. We also update the active object count */
953
954 BOOL WINAPI DbgRegisterObjectDestruction(DWORD dwCookie)
955 {
956     /* Grab the list critical section */
957     EnterCriticalSection(&m_CSDebug);
958
959     ObjectDesc *pObject = pListHead;
960     ObjectDesc *pPrevious = NULL;
961
962     /* Scan the object list looking for a cookie match */
963
964     while (pObject) {
965         if (pObject->m_dwCookie == dwCookie) {
966             break;
967         }
968         pPrevious = pObject;
969         pObject = pObject->m_pNext;
970     }
971
972     if (pObject == NULL) {
973         DbgBreak("Apparently destroying a bogus object");
974         LeaveCriticalSection(&m_CSDebug);
975         return FALSE;
976     }
977
978     /* Is the object at the head of the list */
979
980     if (pPrevious == NULL) {
981         pListHead = pObject->m_pNext;
982     } else {
983         pPrevious->m_pNext = pObject->m_pNext;
984     }
985
986     /* Delete the object and update the housekeeping information */
987
988     m_dwObjectCount--;
989
990     if(pObject->m_wszName) {
991         DbgLog((LOG_MEMORY,2,TEXT("Object destroyed %d (%ls) %d Active"),
992                 pObject->m_dwCookie, pObject->m_wszName, m_dwObjectCount));
993     } else {
994         DbgLog((LOG_MEMORY,2,TEXT("Object destroyed %d (%hs) %d Active"),
995                 pObject->m_dwCookie, pObject->m_szName, m_dwObjectCount));
996     }
997
998     delete pObject;
999     LeaveCriticalSection(&m_CSDebug);
1000     return TRUE;
1001 }
1002
1003
1004 /* This runs through the active object list displaying their details */
1005
1006 void WINAPI DbgDumpObjectRegister()
1007 {
1008     TCHAR szInfo[iDEBUGINFO];
1009
1010     /* Grab the list critical section */
1011
1012     EnterCriticalSection(&m_CSDebug);
1013     ObjectDesc *pObject = pListHead;
1014
1015     /* Scan the object list displaying the name and cookie */
1016
1017     DbgLog((LOG_MEMORY,2,TEXT("")));
1018     DbgLog((LOG_MEMORY,2,TEXT("   ID             Object Description")));
1019     DbgLog((LOG_MEMORY,2,TEXT("")));
1020
1021     while (pObject) {
1022         if(pObject->m_wszName) {
1023             (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%5d (%p) %30ls"),pObject->m_dwCookie, &pObject, pObject->m_wszName);
1024         } else {
1025             (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%5d (%p) %30hs"),pObject->m_dwCookie, &pObject, pObject->m_szName);
1026         }
1027         DbgLog((LOG_MEMORY,2,szInfo));
1028         pObject = pObject->m_pNext;
1029     }
1030
1031     (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("Total object count %5d"),m_dwObjectCount);
1032     DbgLog((LOG_MEMORY,2,TEXT("")));
1033     DbgLog((LOG_MEMORY,1,szInfo));
1034     LeaveCriticalSection(&m_CSDebug);
1035 }
1036
1037 /*  Debug infinite wait stuff */
1038 DWORD WINAPI DbgWaitForSingleObject(HANDLE h)
1039 {
1040     DWORD dwWaitResult;
1041     do {
1042         dwWaitResult = WaitForSingleObject(h, dwWaitTimeout);
1043         ASSERT(dwWaitResult == WAIT_OBJECT_0);
1044     } while (dwWaitResult == WAIT_TIMEOUT);
1045     return dwWaitResult;
1046 }
1047 DWORD WINAPI DbgWaitForMultipleObjects(DWORD nCount,
1048                                 __in_ecount(nCount) CONST HANDLE *lpHandles,
1049                                 BOOL bWaitAll)
1050 {
1051     DWORD dwWaitResult;
1052     do {
1053         dwWaitResult = WaitForMultipleObjects(nCount,
1054                                               lpHandles,
1055                                               bWaitAll,
1056                                               dwWaitTimeout);
1057         ASSERT((DWORD)(dwWaitResult - WAIT_OBJECT_0) < MAXIMUM_WAIT_OBJECTS);
1058     } while (dwWaitResult == WAIT_TIMEOUT);
1059     return dwWaitResult;
1060 }
1061
1062 void WINAPI DbgSetWaitTimeout(DWORD dwTimeout)
1063 {
1064     dwWaitTimeout = dwTimeout;
1065 }
1066
1067 #endif /* DEBUG */
1068
1069 #ifdef _OBJBASE_H_
1070
1071     /*  Stuff for printing out our GUID names */
1072
1073     GUID_STRING_ENTRY g_GuidNames[] = {
1074     #define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
1075     { #name, { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } } },
1076         #include <uuids.h>
1077     };
1078
1079     CGuidNameList GuidNames;
1080     int g_cGuidNames = sizeof(g_GuidNames) / sizeof(g_GuidNames[0]);
1081
1082     char *CGuidNameList::operator [] (const GUID &guid)
1083     {
1084         for (int i = 0; i < g_cGuidNames; i++) {
1085             if (g_GuidNames[i].guid == guid) {
1086                 return g_GuidNames[i].szName;
1087             }
1088         }
1089         if (guid == GUID_NULL) {
1090             return "GUID_NULL";
1091         }
1092
1093         // !!! add something to print FOURCC guids?
1094         
1095         // shouldn't this print the hex CLSID?
1096         return "Unknown GUID Name";
1097     }
1098
1099 #endif /* _OBJBASE_H_ */
1100
1101 /*  CDisp class - display our data types */
1102
1103 // clashes with REFERENCE_TIME
1104 CDisp::CDisp(LONGLONG ll, int Format)
1105 {
1106     // note: this could be combined with CDisp(LONGLONG) by
1107     // introducing a default format of CDISP_REFTIME
1108     LARGE_INTEGER li;
1109     li.QuadPart = ll;
1110     switch (Format) {
1111         case CDISP_DEC:
1112         {
1113             TCHAR  temp[20];
1114             int pos=20;
1115             temp[--pos] = 0;
1116             int digit;
1117             // always output at least one digit
1118             do {
1119                 // Get the rightmost digit - we only need the low word
1120                 digit = li.LowPart % 10;
1121                 li.QuadPart /= 10;
1122                 temp[--pos] = (TCHAR) digit+L'0';
1123             } while (li.QuadPart);
1124             (void)StringCchCopy(m_String, NUMELMS(m_String), temp+pos);
1125             break;
1126         }
1127         case CDISP_HEX:
1128         default:
1129             (void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("0x%X%8.8X"), li.HighPart, li.LowPart);
1130     }
1131 };
1132
1133 CDisp::CDisp(REFCLSID clsid)
1134 {
1135 #ifdef UNICODE 
1136     (void)StringFromGUID2(clsid, m_String, NUMELMS(m_String));
1137 #else
1138     WCHAR wszTemp[50];
1139     (void)StringFromGUID2(clsid, wszTemp, NUMELMS(wszTemp));
1140     (void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("%S"), wszTemp);
1141 #endif
1142 };
1143
1144 #ifdef __STREAMS__
1145 /*  Display stuff */
1146 CDisp::CDisp(CRefTime llTime)
1147 {
1148     LONGLONG llDiv;
1149     if (llTime < 0) {
1150         llTime = -llTime;
1151         (void)StringCchCopy(m_String, NUMELMS(m_String), TEXT("-"));
1152     }
1153     llDiv = (LONGLONG)24 * 3600 * 10000000;
1154     if (llTime >= llDiv) {
1155         (void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d days "), (LONG)(llTime / llDiv));
1156         llTime = llTime % llDiv;
1157     }
1158     llDiv = (LONGLONG)3600 * 10000000;
1159     if (llTime >= llDiv) {
1160         (void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d hrs "), (LONG)(llTime / llDiv));
1161         llTime = llTime % llDiv;
1162     }
1163     llDiv = (LONGLONG)60 * 10000000;
1164     if (llTime >= llDiv) {
1165         (void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d mins "), (LONG)(llTime / llDiv));
1166         llTime = llTime % llDiv;
1167     }
1168     (void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d.%3.3d sec"),
1169              (LONG)llTime / 10000000,
1170              (LONG)((llTime % 10000000) / 10000));
1171 };
1172
1173 #endif // __STREAMS__
1174
1175
1176 /*  Display pin */
1177 CDisp::CDisp(IPin *pPin)
1178 {
1179     PIN_INFO pi;
1180     TCHAR str[MAX_PIN_NAME];
1181     CLSID clsid;
1182
1183     if (pPin) {
1184        pPin->QueryPinInfo(&pi);
1185        pi.pFilter->GetClassID(&clsid);
1186        QueryPinInfoReleaseFilter(pi);
1187       #ifndef UNICODE
1188        WideCharToMultiByte(GetACP(), 0, pi.achName, lstrlenW(pi.achName) + 1,
1189                            str, MAX_PIN_NAME, NULL, NULL);
1190       #else
1191        (void)StringCchCopy(str, NUMELMS(str), pi.achName);
1192       #endif
1193     } else {
1194        (void)StringCchCopy(str, NUMELMS(str), TEXT("NULL IPin"));
1195     }
1196
1197     m_pString = (PTCHAR) new TCHAR[lstrlen(str)+64];
1198     if (!m_pString) {
1199         return;
1200     }
1201
1202     (void)StringCchPrintf(m_pString, lstrlen(str) + 64, TEXT("%hs(%s)"), GuidNames[clsid], str);
1203 }
1204
1205 /*  Display filter or pin */
1206 CDisp::CDisp(IUnknown *pUnk)
1207 {
1208     IBaseFilter *pf;
1209     HRESULT hr = pUnk->QueryInterface(IID_IBaseFilter, (void **)&pf);
1210     if(SUCCEEDED(hr))
1211     {
1212         FILTER_INFO fi;
1213         hr = pf->QueryFilterInfo(&fi);
1214         if(SUCCEEDED(hr))
1215         {
1216             QueryFilterInfoReleaseGraph(fi);
1217
1218             size_t len = lstrlenW(fi.achName)  + 1;
1219
1220             m_pString = new TCHAR[len];
1221             if(m_pString)
1222             {
1223 #ifdef UNICODE
1224                 (void)StringCchCopy(m_pString, len, fi.achName);
1225 #else
1226                 (void)StringCchPrintf(m_pString, len, "%S", fi.achName);
1227 #endif
1228             }
1229         }
1230
1231         pf->Release();
1232
1233         return;
1234     }
1235
1236     IPin *pp;
1237     hr = pUnk->QueryInterface(IID_IPin, (void **)&pp);
1238     if(SUCCEEDED(hr))
1239     {
1240         CDisp::CDisp(pp);
1241         pp->Release();
1242         return;
1243     }
1244 }
1245
1246
1247 CDisp::~CDisp()
1248 {
1249 }
1250
1251 CDispBasic::~CDispBasic()
1252 {
1253     if (m_pString != m_String) {
1254         delete [] m_pString;
1255     }
1256 }
1257
1258 CDisp::CDisp(double d)
1259 {
1260     (void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("%d.%03d"), (int) d, (int) ((d - (int) d) * 1000));
1261 }
1262
1263
1264 /* If built for debug this will display the media type details. We convert the
1265    major and subtypes into strings and also ask the base classes for a string
1266    description of the subtype, so MEDIASUBTYPE_RGB565 becomes RGB 565 16 bit
1267    We also display the fields in the BITMAPINFOHEADER structure, this should
1268    succeed as we do not accept input types unless the format is big enough */
1269
1270 #ifdef DEBUG
1271 void WINAPI DisplayType(LPCTSTR label, const AM_MEDIA_TYPE *pmtIn)
1272 {
1273
1274     /* Dump the GUID types and a short description */
1275
1276     DbgLog((LOG_TRACE,5,TEXT("")));
1277     DbgLog((LOG_TRACE,2,TEXT("%s  M type %hs  S type %hs"), label,
1278             GuidNames[pmtIn->majortype],
1279             GuidNames[pmtIn->subtype]));
1280     DbgLog((LOG_TRACE,5,TEXT("Subtype description %s"),GetSubtypeName(&pmtIn->subtype)));
1281
1282     /* Dump the generic media types */
1283
1284     if (pmtIn->bTemporalCompression) {
1285         DbgLog((LOG_TRACE,5,TEXT("Temporally compressed")));
1286     } else {
1287         DbgLog((LOG_TRACE,5,TEXT("Not temporally compressed")));
1288     }
1289
1290     if (pmtIn->bFixedSizeSamples) {
1291         DbgLog((LOG_TRACE,5,TEXT("Sample size %d"),pmtIn->lSampleSize));
1292     } else {
1293         DbgLog((LOG_TRACE,5,TEXT("Variable size samples")));
1294     }
1295
1296     if (pmtIn->formattype == FORMAT_VideoInfo) {
1297
1298         VIDEOINFOHEADER *pVideoInfo = (VIDEOINFOHEADER *)pmtIn->pbFormat;
1299
1300         DisplayRECT(TEXT("Source rectangle"),pVideoInfo->rcSource);
1301         DisplayRECT(TEXT("Target rectangle"),pVideoInfo->rcTarget);
1302         DisplayBITMAPINFO(HEADER(pmtIn->pbFormat));
1303
1304     } if (pmtIn->formattype == FORMAT_VideoInfo2) {
1305
1306         VIDEOINFOHEADER2 *pVideoInfo2 = (VIDEOINFOHEADER2 *)pmtIn->pbFormat;
1307
1308         DisplayRECT(TEXT("Source rectangle"),pVideoInfo2->rcSource);
1309         DisplayRECT(TEXT("Target rectangle"),pVideoInfo2->rcTarget);
1310         DbgLog((LOG_TRACE, 5, TEXT("Aspect Ratio: %d:%d"),
1311             pVideoInfo2->dwPictAspectRatioX,
1312             pVideoInfo2->dwPictAspectRatioY));
1313         DisplayBITMAPINFO(&pVideoInfo2->bmiHeader);
1314
1315     } else if (pmtIn->majortype == MEDIATYPE_Audio) {
1316         DbgLog((LOG_TRACE,2,TEXT("     Format type %hs"),
1317             GuidNames[pmtIn->formattype]));
1318         DbgLog((LOG_TRACE,2,TEXT("     Subtype %hs"),
1319             GuidNames[pmtIn->subtype]));
1320
1321         if ((pmtIn->subtype != MEDIASUBTYPE_MPEG1Packet)
1322           && (pmtIn->cbFormat >= sizeof(PCMWAVEFORMAT)))
1323         {
1324             /* Dump the contents of the WAVEFORMATEX type-specific format structure */
1325
1326             WAVEFORMATEX *pwfx = (WAVEFORMATEX *) pmtIn->pbFormat;
1327             DbgLog((LOG_TRACE,2,TEXT("wFormatTag %u"), pwfx->wFormatTag));
1328             DbgLog((LOG_TRACE,2,TEXT("nChannels %u"), pwfx->nChannels));
1329             DbgLog((LOG_TRACE,2,TEXT("nSamplesPerSec %lu"), pwfx->nSamplesPerSec));
1330             DbgLog((LOG_TRACE,2,TEXT("nAvgBytesPerSec %lu"), pwfx->nAvgBytesPerSec));
1331             DbgLog((LOG_TRACE,2,TEXT("nBlockAlign %u"), pwfx->nBlockAlign));
1332             DbgLog((LOG_TRACE,2,TEXT("wBitsPerSample %u"), pwfx->wBitsPerSample));
1333
1334             /* PCM uses a WAVEFORMAT and does not have the extra size field */
1335
1336             if (pmtIn->cbFormat >= sizeof(WAVEFORMATEX)) {
1337                 DbgLog((LOG_TRACE,2,TEXT("cbSize %u"), pwfx->cbSize));
1338             }
1339         } else {
1340         }
1341
1342     } else {
1343         DbgLog((LOG_TRACE,2,TEXT("     Format type %hs"),
1344             GuidNames[pmtIn->formattype]));
1345     }
1346 }
1347
1348
1349 void DisplayBITMAPINFO(const BITMAPINFOHEADER* pbmi)
1350 {
1351     DbgLog((LOG_TRACE,5,TEXT("Size of BITMAPINFO structure %d"),pbmi->biSize));
1352     if (pbmi->biCompression < 256) {
1353         DbgLog((LOG_TRACE,2,TEXT("%dx%dx%d bit  (%d)"),
1354                 pbmi->biWidth, pbmi->biHeight,
1355                 pbmi->biBitCount, pbmi->biCompression));
1356     } else {
1357         DbgLog((LOG_TRACE,2,TEXT("%dx%dx%d bit '%4.4hs'"),
1358                 pbmi->biWidth, pbmi->biHeight,
1359                 pbmi->biBitCount, &pbmi->biCompression));
1360     }
1361
1362     DbgLog((LOG_TRACE,2,TEXT("Image size %d"),pbmi->biSizeImage));
1363     DbgLog((LOG_TRACE,5,TEXT("Planes %d"),pbmi->biPlanes));
1364     DbgLog((LOG_TRACE,5,TEXT("X Pels per metre %d"),pbmi->biXPelsPerMeter));
1365     DbgLog((LOG_TRACE,5,TEXT("Y Pels per metre %d"),pbmi->biYPelsPerMeter));
1366     DbgLog((LOG_TRACE,5,TEXT("Colours used %d"),pbmi->biClrUsed));
1367 }
1368
1369
1370 void DisplayRECT(LPCTSTR szLabel, const RECT& rc)
1371 {
1372     DbgLog((LOG_TRACE,5,TEXT("%s (Left %d Top %d Right %d Bottom %d)"),
1373             szLabel,
1374             rc.left,
1375             rc.top,
1376             rc.right,
1377             rc.bottom));
1378 }
1379
1380
1381 void WINAPI DumpGraph(IFilterGraph *pGraph, DWORD dwLevel)
1382 {
1383     if( !pGraph )
1384     {
1385         return;
1386     }
1387
1388     IEnumFilters *pFilters;
1389
1390     DbgLog((LOG_TRACE,dwLevel,TEXT("DumpGraph [%x]"), pGraph));
1391
1392     if (FAILED(pGraph->EnumFilters(&pFilters))) {
1393         DbgLog((LOG_TRACE,dwLevel,TEXT("EnumFilters failed!")));
1394     }
1395
1396     IBaseFilter *pFilter;
1397     ULONG       n;
1398     while (pFilters->Next(1, &pFilter, &n) == S_OK) {
1399         FILTER_INFO     info;
1400
1401         if (FAILED(pFilter->QueryFilterInfo(&info))) {
1402             DbgLog((LOG_TRACE,dwLevel,TEXT("    Filter [%p]  -- failed QueryFilterInfo"), pFilter));
1403         } else {
1404             QueryFilterInfoReleaseGraph(info);
1405
1406             // !!! should QueryVendorInfo here!
1407         
1408             DbgLog((LOG_TRACE,dwLevel,TEXT("    Filter [%p]  '%ls'"), pFilter, info.achName));
1409
1410             IEnumPins *pins;
1411
1412             if (FAILED(pFilter->EnumPins(&pins))) {
1413                 DbgLog((LOG_TRACE,dwLevel,TEXT("EnumPins failed!")));
1414             } else {
1415
1416                 IPin *pPin;
1417                 while (pins->Next(1, &pPin, &n) == S_OK) {
1418                     PIN_INFO    pinInfo;
1419
1420                     if (FAILED(pPin->QueryPinInfo(&pinInfo))) {
1421                         DbgLog((LOG_TRACE,dwLevel,TEXT("          Pin [%x]  -- failed QueryPinInfo"), pPin));
1422                     } else {
1423                         QueryPinInfoReleaseFilter(pinInfo);
1424
1425                         IPin *pPinConnected = NULL;
1426
1427                         HRESULT hr = pPin->ConnectedTo(&pPinConnected);
1428
1429                         if (pPinConnected) {
1430                             DbgLog((LOG_TRACE,dwLevel,TEXT("          Pin [%p]  '%ls' [%sput]")
1431                                                            TEXT("  Connected to pin [%p]"),
1432                                     pPin, pinInfo.achName,
1433                                     pinInfo.dir == PINDIR_INPUT ? TEXT("In") : TEXT("Out"),
1434                                     pPinConnected));
1435
1436                             pPinConnected->Release();
1437
1438                             // perhaps we should really dump the type both ways as a sanity
1439                             // check?
1440                             if (pinInfo.dir == PINDIR_OUTPUT) {
1441                                 AM_MEDIA_TYPE mt;
1442
1443                                 hr = pPin->ConnectionMediaType(&mt);
1444
1445                                 if (SUCCEEDED(hr)) {
1446                                     DisplayType(TEXT("Connection type"), &mt);
1447
1448                                     FreeMediaType(mt);
1449                                 }
1450                             }
1451                         } else {
1452                             DbgLog((LOG_TRACE,dwLevel,
1453                                     TEXT("          Pin [%x]  '%ls' [%sput]"),
1454                                     pPin, pinInfo.achName,
1455                                     pinInfo.dir == PINDIR_INPUT ? TEXT("In") : TEXT("Out")));
1456
1457                         }
1458                     }
1459
1460                     pPin->Release();
1461
1462                 }
1463
1464                 pins->Release();
1465             }
1466
1467         }
1468         
1469         pFilter->Release();
1470     }
1471
1472     pFilters->Release();
1473
1474 }
1475
1476 #endif
1477
1478 #endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */