Add directshow strmbase library source code from Microsoft repo
authorLoic Le Page <llepage@fluendo.com>
Wed, 26 Jan 2022 19:43:09 +0000 (20:43 +0100)
committerLoïc Le Page <llepage@fluendo.com>
Fri, 18 Feb 2022 13:59:25 +0000 (14:59 +0100)
Original repo is here:
https://github.com/microsoft/Windows-classic-samples

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1577>

73 files changed:
subprojects/gst-plugins-bad/meson_options.txt
subprojects/gst-plugins-bad/sys/directshow/meson.build [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/LICENSE [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/amextra.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/amextra.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/amfilter.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/amfilter.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/amvideo.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/arithutil.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/cache.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/checkbmi.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/combase.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/combase.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/cprop.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/cprop.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/ctlutil.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/ctlutil.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/ddmm.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/ddmm.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/dllentry.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/dllsetup.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/dllsetup.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/dxmperf.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/fourcc.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/measure.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/msgthrd.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/mtype.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/mtype.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/outputq.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/outputq.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/perflog.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/perflog.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/perfstruct.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/pstream.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/pstream.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/pullpin.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/pullpin.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/refclock.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/refclock.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/reftime.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/renbase.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/renbase.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/schedule.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/schedule.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/seekpt.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/seekpt.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/source.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/source.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/streams.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/strmctl.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/strmctl.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/sysclock.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/sysclock.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/transfrm.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/transfrm.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/transip.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/transip.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/videoctl.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/videoctl.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/vtrans.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/vtrans.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/winctrl.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/winctrl.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/winutil.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/winutil.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/wxdebug.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/wxdebug.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/wxlist.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/wxlist.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/wxutil.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/wxutil.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/directshow/strmbase/meson.build [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/meson.build

index fc77967..c68eb75 100644 (file)
@@ -99,6 +99,7 @@ option('dc1394', type : 'feature', value : 'auto', description : 'libdc1394 IIDC
 option('decklink', type : 'feature', value : 'auto', description : 'DeckLink audio/video source/sink plugin')
 option('directfb', type : 'feature', value : 'auto', description : 'DirectFB video sink plugin')
 option('directsound', type : 'feature', value : 'auto', description : 'Directsound audio source plugin')
+option('directshow', type : 'feature', value : 'auto', description : 'Directshow audio/video plugins')
 option('dtls', type : 'feature', value : 'auto', description : 'DTLS encoder and decoder plugin')
 option('dts', type : 'feature', value : 'auto', description : 'DTS audio decoder plugin (GPL - only built if gpl option is also enabled!)')
 option('dvb', type : 'feature', value : 'auto', description : 'DVB video bin and source plugin')
diff --git a/subprojects/gst-plugins-bad/sys/directshow/meson.build b/subprojects/gst-plugins-bad/sys/directshow/meson.build
new file mode 100644 (file)
index 0000000..1af20f5
--- /dev/null
@@ -0,0 +1,26 @@
+if host_system == 'windows'
+  # Check whether we're building for UWP apps
+  code = '''
+  #include <windows.h>
+  #if !(WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP))
+  #error "Not building for UWP"
+  #endif'''
+  if cc.compiles(code, name : 'building for UWP')
+    if get_option('directshow').enabled()
+      error('directshow plugins cannot be built for UWP')
+    endif
+    subdir_done()
+  endif
+endif
+
+if cxx.get_id() != 'msvc' or get_option('directshow').disabled()
+  if get_option('directshow').enabled()
+    error('directshow plugins can only be built with MSVC')
+  endif
+  subdir_done()
+endif
+
+subdir('strmbase')
+#subdir('dshowdecwrapper')
+#subdir('dshowsrcwrapper')
+#subdir('dshowvideosink')
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/LICENSE b/subprojects/gst-plugins-bad/sys/directshow/strmbase/LICENSE
new file mode 100644 (file)
index 0000000..d10a44f
--- /dev/null
@@ -0,0 +1,24 @@
+The MIT License (MIT)\r
+\r
+Copyright (c) Microsoft Corporation\r
+\r
+Permission is hereby granted, free of charge, to any person obtaining a copy\r
+ of this software and associated documentation files (the "Software"), to deal\r
+ in the Software without restriction, including without limitation the rights\r
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
+ copies of the Software, and to permit persons to whom the Software is\r
+ furnished to do so, subject to the following conditions:\r
+\r
+The above copyright notice and this permission notice shall be included in\r
+ all copies or substantial portions of the Software.\r
+\r
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
+ THE SOFTWARE.\r
+\r
+Portions of this repo are provided under the SIL Open Font License.\r
+See the LICENSE file in individual samples for additional details.\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/amextra.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/amextra.cpp
new file mode 100644 (file)
index 0000000..af0de96
--- /dev/null
@@ -0,0 +1,111 @@
+//------------------------------------------------------------------------------\r
+// File: AMExtra.cpp\r
+//\r
+// Desc: DirectShow base classes - implements CRenderedInputPin class.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>        // DirectShow base class definitions\r
+#include <mmsystem.h>       // Needed for definition of timeGetTime\r
+#include <limits.h>         // Standard data type limit definitions\r
+#include <measure.h>        // Used for time critical log functions\r
+\r
+#include "amextra.h"\r
+\r
+#pragma warning(disable:4355)\r
+\r
+//  Implements CRenderedInputPin class\r
+\r
+CRenderedInputPin::CRenderedInputPin(__in_opt LPCTSTR pObjectName,\r
+                                     __in CBaseFilter *pFilter,\r
+                                     __in CCritSec *pLock,\r
+                                     __inout HRESULT *phr,\r
+                                     __in_opt LPCWSTR pName) :\r
+    CBaseInputPin(pObjectName, pFilter, pLock, phr, pName),\r
+    m_bAtEndOfStream(FALSE),\r
+    m_bCompleteNotified(FALSE)\r
+{\r
+}\r
+#ifdef UNICODE\r
+CRenderedInputPin::CRenderedInputPin(__in_opt LPCSTR pObjectName,\r
+                                     __in CBaseFilter *pFilter,\r
+                                     __in CCritSec *pLock,\r
+                                     __inout HRESULT *phr,\r
+                                     __in_opt LPCWSTR pName) :\r
+    CBaseInputPin(pObjectName, pFilter, pLock, phr, pName),\r
+    m_bAtEndOfStream(FALSE),\r
+    m_bCompleteNotified(FALSE)\r
+{\r
+}\r
+#endif\r
+\r
+// Flush end of stream condition - caller should do any\r
+// necessary stream level locking before calling this\r
+\r
+STDMETHODIMP CRenderedInputPin::EndOfStream()\r
+{\r
+    HRESULT hr = CheckStreaming();\r
+\r
+    //  Do EC_COMPLETE handling for rendered pins\r
+    if (S_OK == hr  && !m_bAtEndOfStream) {\r
+        m_bAtEndOfStream = TRUE;\r
+        FILTER_STATE fs;\r
+        EXECUTE_ASSERT(SUCCEEDED(m_pFilter->GetState(0, &fs)));\r
+        if (fs == State_Running) {\r
+            DoCompleteHandling();\r
+        }\r
+    }\r
+    return hr;\r
+}\r
+\r
+\r
+// Called to complete the flush\r
+\r
+STDMETHODIMP CRenderedInputPin::EndFlush()\r
+{\r
+    CAutoLock lck(m_pLock);\r
+\r
+    // Clean up renderer state\r
+    m_bAtEndOfStream = FALSE;\r
+    m_bCompleteNotified = FALSE;\r
+\r
+    return CBaseInputPin::EndFlush();\r
+}\r
+\r
+\r
+// Notify of Run() from filter\r
+\r
+HRESULT CRenderedInputPin::Run(REFERENCE_TIME tStart)\r
+{\r
+    UNREFERENCED_PARAMETER(tStart);\r
+    m_bCompleteNotified = FALSE;\r
+    if (m_bAtEndOfStream) {\r
+        DoCompleteHandling();\r
+    }\r
+    return S_OK;\r
+}\r
+\r
+\r
+//  Clear status on going into paused state\r
+\r
+HRESULT CRenderedInputPin::Active()\r
+{\r
+    m_bAtEndOfStream = FALSE;\r
+    m_bCompleteNotified = FALSE;\r
+    return CBaseInputPin::Active();\r
+}\r
+\r
+\r
+//  Do stuff to deliver end of stream\r
+\r
+void CRenderedInputPin::DoCompleteHandling()\r
+{\r
+    ASSERT(m_bAtEndOfStream);\r
+    if (!m_bCompleteNotified) {\r
+        m_bCompleteNotified = TRUE;\r
+        m_pFilter->NotifyEvent(EC_COMPLETE, S_OK, (LONG_PTR)(IBaseFilter *)m_pFilter);\r
+    }\r
+}\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/amextra.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/amextra.h
new file mode 100644 (file)
index 0000000..5a861bf
--- /dev/null
@@ -0,0 +1,56 @@
+//------------------------------------------------------------------------------\r
+// File: AMExtra.h\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef __AMEXTRA__\r
+#define __AMEXTRA__\r
+\r
+// Simple rendered input pin\r
+//\r
+// NOTE if your filter queues stuff before rendering then it may not be\r
+// appropriate to use this class\r
+//\r
+// In that case queue the end of stream condition until the last sample\r
+// is actually rendered and flush the condition appropriately\r
+\r
+class CRenderedInputPin : public CBaseInputPin\r
+{\r
+public:\r
+\r
+    CRenderedInputPin(__in_opt LPCTSTR pObjectName,\r
+                      __in CBaseFilter *pFilter,\r
+                      __in CCritSec *pLock,\r
+                      __inout HRESULT *phr,\r
+                      __in_opt LPCWSTR pName);\r
+#ifdef UNICODE\r
+    CRenderedInputPin(__in_opt LPCSTR pObjectName,\r
+                      __in CBaseFilter *pFilter,\r
+                      __in CCritSec *pLock,\r
+                      __inout HRESULT *phr,\r
+                      __in_opt LPCWSTR pName);\r
+#endif\r
+    \r
+    // Override methods to track end of stream state\r
+    STDMETHODIMP EndOfStream();\r
+    STDMETHODIMP EndFlush();\r
+\r
+    HRESULT Active();\r
+    HRESULT Run(REFERENCE_TIME tStart);\r
+\r
+protected:\r
+\r
+    // Member variables to track state\r
+    BOOL m_bAtEndOfStream;      // Set by EndOfStream\r
+    BOOL m_bCompleteNotified;   // Set when we notify for EC_COMPLETE\r
+\r
+private:\r
+    void DoCompleteHandling();\r
+};\r
+\r
+#endif // __AMEXTRA__\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/amfilter.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/amfilter.cpp
new file mode 100644 (file)
index 0000000..03c13d1
--- /dev/null
@@ -0,0 +1,5358 @@
+//------------------------------------------------------------------------------\r
+// File: AMFilter.cpp\r
+//\r
+// Desc: DirectShow base classes - implements class hierarchy for streams\r
+//       architecture.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// The following classes are declared in this header:\r
+//\r
+//\r
+// CBaseMediaFilter            Basic IMediaFilter support (abstract class)\r
+// CBaseFilter                 Support for IBaseFilter (incl. IMediaFilter)\r
+// CEnumPins                   Enumerate input and output pins\r
+// CEnumMediaTypes             Enumerate the preferred pin formats\r
+// CBasePin                    Abstract base class for IPin interface\r
+//    CBaseOutputPin           Adds data provider member functions\r
+//    CBaseInputPin            Implements IMemInputPin interface\r
+// CMediaSample                Basic transport unit for IMemInputPin\r
+// CBaseAllocator              General list guff for most allocators\r
+//    CMemAllocator            Implements memory buffer allocation\r
+//\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+#include <streams.h>\r
+#include <strsafe.h>\r
+\r
+#ifdef DXMPERF\r
+#include "dxmperf.h"\r
+#endif // DXMPERF\r
+\r
+\r
+//=====================================================================\r
+// Helpers\r
+//=====================================================================\r
+STDAPI CreateMemoryAllocator(__deref_out IMemAllocator **ppAllocator)\r
+{\r
+    return CoCreateInstance(CLSID_MemoryAllocator,\r
+                            0,\r
+                            CLSCTX_INPROC_SERVER,\r
+                            IID_IMemAllocator,\r
+                            (void **)ppAllocator);\r
+}\r
+\r
+//  Put this one here rather than in ctlutil.cpp to avoid linking\r
+//  anything brought in by ctlutil.cpp\r
+STDAPI CreatePosPassThru(\r
+    __in_opt LPUNKNOWN pAgg,\r
+    BOOL bRenderer,\r
+    IPin *pPin,\r
+    __deref_out IUnknown **ppPassThru\r
+)\r
+{\r
+    *ppPassThru = NULL;\r
+    IUnknown *pUnkSeek;\r
+    HRESULT hr = CoCreateInstance(CLSID_SeekingPassThru,\r
+                                  pAgg,\r
+                                  CLSCTX_INPROC_SERVER,\r
+                                  IID_IUnknown,\r
+                                  (void **)&pUnkSeek\r
+                                 );\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    ISeekingPassThru *pPassThru;\r
+    hr = pUnkSeek->QueryInterface(IID_ISeekingPassThru, (void**)&pPassThru);\r
+    if (FAILED(hr)) {\r
+        pUnkSeek->Release();\r
+        return hr;\r
+    }\r
+    hr = pPassThru->Init(bRenderer, pPin);\r
+    pPassThru->Release();\r
+    if (FAILED(hr)) {\r
+        pUnkSeek->Release();\r
+        return hr;\r
+    }\r
+    *ppPassThru = pUnkSeek;\r
+    return S_OK;\r
+}\r
+\r
+\r
+\r
+#define CONNECT_TRACE_LEVEL 3\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Implements CBaseMediaFilter\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+\r
+/* Constructor */\r
+\r
+CBaseMediaFilter::CBaseMediaFilter(__in_opt LPCTSTR pName,\r
+                   __inout_opt LPUNKNOWN    pUnk,\r
+                   __in CCritSec *pLock,\r
+                   REFCLSID clsid) :\r
+    CUnknown(pName, pUnk),\r
+    m_pLock(pLock),\r
+    m_clsid(clsid),\r
+    m_State(State_Stopped),\r
+    m_pClock(NULL)\r
+{\r
+}\r
+\r
+\r
+/* Destructor */\r
+\r
+CBaseMediaFilter::~CBaseMediaFilter()\r
+{\r
+    // must be stopped, but can't call Stop here since\r
+    // our critsec has been destroyed.\r
+\r
+    /* Release any clock we were using */\r
+\r
+    if (m_pClock) {\r
+        m_pClock->Release();\r
+        m_pClock = NULL;\r
+    }\r
+}\r
+\r
+\r
+/* Override this to say what interfaces we support and where */\r
+\r
+STDMETHODIMP\r
+CBaseMediaFilter::NonDelegatingQueryInterface(\r
+    REFIID riid,\r
+    __deref_out void ** ppv)\r
+{\r
+    if (riid == IID_IMediaFilter) {\r
+        return GetInterface((IMediaFilter *) this, ppv);\r
+    } else if (riid == IID_IPersist) {\r
+        return GetInterface((IPersist *) this, ppv);\r
+    } else {\r
+        return CUnknown::NonDelegatingQueryInterface(riid, ppv);\r
+    }\r
+}\r
+\r
+/* Return the filter's clsid */\r
+STDMETHODIMP\r
+CBaseMediaFilter::GetClassID(__out CLSID *pClsID)\r
+{\r
+    CheckPointer(pClsID,E_POINTER);\r
+    ValidateReadWritePtr(pClsID,sizeof(CLSID));\r
+    *pClsID = m_clsid;\r
+    return NOERROR;\r
+}\r
+\r
+/* Override this if your state changes are not done synchronously */\r
+\r
+STDMETHODIMP\r
+CBaseMediaFilter::GetState(DWORD dwMSecs, __out FILTER_STATE *State)\r
+{\r
+    UNREFERENCED_PARAMETER(dwMSecs);\r
+    CheckPointer(State,E_POINTER);\r
+    ValidateReadWritePtr(State,sizeof(FILTER_STATE));\r
+\r
+    *State = m_State;\r
+    return S_OK;\r
+}\r
+\r
+\r
+/* Set the clock we will use for synchronisation */\r
+\r
+STDMETHODIMP\r
+CBaseMediaFilter::SetSyncSource(__inout_opt IReferenceClock *pClock)\r
+{\r
+    CAutoLock cObjectLock(m_pLock);\r
+\r
+    // Ensure the new one does not go away - even if the same as the old\r
+    if (pClock) {\r
+        pClock->AddRef();\r
+    }\r
+\r
+    // if we have a clock, release it\r
+    if (m_pClock) {\r
+        m_pClock->Release();\r
+    }\r
+\r
+    // Set the new reference clock (might be NULL)\r
+    // Should we query it to ensure it is a clock?  Consider for a debug build.\r
+    m_pClock = pClock;\r
+\r
+    return NOERROR;\r
+}\r
+\r
+/* Return the clock we are using for synchronisation */\r
+STDMETHODIMP\r
+CBaseMediaFilter::GetSyncSource(__deref_out_opt IReferenceClock **pClock)\r
+{\r
+    CheckPointer(pClock,E_POINTER);\r
+    ValidateReadWritePtr(pClock,sizeof(IReferenceClock *));\r
+    CAutoLock cObjectLock(m_pLock);\r
+\r
+    if (m_pClock) {\r
+        // returning an interface... addref it...\r
+        m_pClock->AddRef();\r
+    }\r
+    *pClock = (IReferenceClock*)m_pClock;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+/* Put the filter into a stopped state */\r
+\r
+STDMETHODIMP\r
+CBaseMediaFilter::Stop()\r
+{\r
+    CAutoLock cObjectLock(m_pLock);\r
+\r
+    m_State = State_Stopped;\r
+    return S_OK;\r
+}\r
+\r
+\r
+/* Put the filter into a paused state */\r
+\r
+STDMETHODIMP\r
+CBaseMediaFilter::Pause()\r
+{\r
+    CAutoLock cObjectLock(m_pLock);\r
+\r
+    m_State = State_Paused;\r
+    return S_OK;\r
+}\r
+\r
+\r
+// Put the filter into a running state.\r
+\r
+// The time parameter is the offset to be added to the samples'\r
+// stream time to get the reference time at which they should be presented.\r
+//\r
+// you can either add these two and compare it against the reference clock,\r
+// or you can call CBaseMediaFilter::StreamTime and compare that against\r
+// the sample timestamp.\r
+\r
+STDMETHODIMP\r
+CBaseMediaFilter::Run(REFERENCE_TIME tStart)\r
+{\r
+    CAutoLock cObjectLock(m_pLock);\r
+\r
+    // remember the stream time offset\r
+    m_tStart = tStart;\r
+\r
+    if (m_State == State_Stopped){\r
+        HRESULT hr = Pause();\r
+\r
+        if (FAILED(hr)) {\r
+            return hr;\r
+        }\r
+    }\r
+    m_State = State_Running;\r
+    return S_OK;\r
+}\r
+\r
+\r
+//\r
+// return the current stream time - samples with start timestamps of this\r
+// time or before should be rendered by now\r
+HRESULT\r
+CBaseMediaFilter::StreamTime(CRefTime& rtStream)\r
+{\r
+    // Caller must lock for synchronization\r
+    // We can't grab the filter lock because we want to be able to call\r
+    // this from worker threads without deadlocking\r
+\r
+    if (m_pClock == NULL) {\r
+        return VFW_E_NO_CLOCK;\r
+    }\r
+\r
+    // get the current reference time\r
+    HRESULT hr = m_pClock->GetTime((REFERENCE_TIME*)&rtStream);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    // subtract the stream offset to get stream time\r
+    rtStream -= m_tStart;\r
+\r
+    return S_OK;\r
+}\r
+\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Implements CBaseFilter\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+\r
+/* Override this to say what interfaces we support and where */\r
+\r
+STDMETHODIMP CBaseFilter::NonDelegatingQueryInterface(REFIID riid,\r
+                                                      __deref_out void **ppv)\r
+{\r
+    /* Do we have this interface */\r
+\r
+    if (riid == IID_IBaseFilter) {\r
+        return GetInterface((IBaseFilter *) this, ppv);\r
+    } else if (riid == IID_IMediaFilter) {\r
+        return GetInterface((IMediaFilter *) this, ppv);\r
+    } else if (riid == IID_IPersist) {\r
+        return GetInterface((IPersist *) this, ppv);\r
+    } else if (riid == IID_IAMovieSetup) {\r
+        return GetInterface((IAMovieSetup *) this, ppv);\r
+    } else {\r
+        return CUnknown::NonDelegatingQueryInterface(riid, ppv);\r
+    }\r
+}\r
+\r
+#ifdef DEBUG\r
+STDMETHODIMP_(ULONG) CBaseFilter::NonDelegatingRelease()\r
+{\r
+    if (m_cRef == 1) {\r
+        KASSERT(m_pGraph == NULL);\r
+    }\r
+    return CUnknown::NonDelegatingRelease();\r
+}\r
+#endif\r
+\r
+\r
+/* Constructor */\r
+\r
+CBaseFilter::CBaseFilter(__in_opt LPCTSTR pName,\r
+             __inout_opt LPUNKNOWN  pUnk,\r
+             __in CCritSec   *pLock,\r
+             REFCLSID   clsid) :\r
+    CUnknown( pName, pUnk ),\r
+    m_pLock(pLock),\r
+    m_clsid(clsid),\r
+    m_State(State_Stopped),\r
+    m_pClock(NULL),\r
+    m_pGraph(NULL),\r
+    m_pSink(NULL),\r
+    m_pName(NULL),\r
+    m_PinVersion(1)\r
+{\r
+#ifdef DXMPERF\r
+    PERFLOG_CTOR( pName ? pName : L"CBaseFilter", (IBaseFilter *) this );\r
+#endif // DXMPERF\r
+\r
+    ASSERT(pLock != NULL);\r
+}\r
+\r
+/* Passes in a redundant HRESULT argument */\r
+\r
+CBaseFilter::CBaseFilter(__in_opt LPCTSTR pName,\r
+                         __in_opt LPUNKNOWN  pUnk,\r
+                         __in CCritSec  *pLock,\r
+                         REFCLSID   clsid,\r
+                         __inout HRESULT   *phr) :\r
+    CUnknown( pName, pUnk ),\r
+    m_pLock(pLock),\r
+    m_clsid(clsid),\r
+    m_State(State_Stopped),\r
+    m_pClock(NULL),\r
+    m_pGraph(NULL),\r
+    m_pSink(NULL),\r
+    m_pName(NULL),\r
+    m_PinVersion(1)\r
+{\r
+#ifdef DXMPERF\r
+    PERFLOG_CTOR( pName ? pName : L"CBaseFilter", (IBaseFilter *) this );\r
+#endif // DXMPERF\r
+\r
+    ASSERT(pLock != NULL);\r
+    UNREFERENCED_PARAMETER(phr);\r
+}\r
+\r
+#ifdef UNICODE\r
+CBaseFilter::CBaseFilter(__in_opt LPCSTR pName,\r
+             __in_opt LPUNKNOWN  pUnk,\r
+             __in CCritSec   *pLock,\r
+             REFCLSID   clsid) :\r
+    CUnknown( pName, pUnk ),\r
+    m_pLock(pLock),\r
+    m_clsid(clsid),\r
+    m_State(State_Stopped),\r
+    m_pClock(NULL),\r
+    m_pGraph(NULL),\r
+    m_pSink(NULL),\r
+    m_pName(NULL),\r
+    m_PinVersion(1)\r
+{\r
+#ifdef DXMPERF\r
+    PERFLOG_CTOR( L"CBaseFilter", (IBaseFilter *) this );\r
+#endif // DXMPERF\r
+\r
+    ASSERT(pLock != NULL);\r
+}\r
+CBaseFilter::CBaseFilter(__in_opt LPCSTR pName,\r
+                         __in_opt LPUNKNOWN  pUnk,\r
+                         __in CCritSec  *pLock,\r
+                         REFCLSID   clsid,\r
+                         __inout HRESULT   *phr) :\r
+    CUnknown( pName, pUnk ),\r
+    m_pLock(pLock),\r
+    m_clsid(clsid),\r
+    m_State(State_Stopped),\r
+    m_pClock(NULL),\r
+    m_pGraph(NULL),\r
+    m_pSink(NULL),\r
+    m_pName(NULL),\r
+    m_PinVersion(1)\r
+{\r
+#ifdef DXMPERF\r
+    PERFLOG_CTOR( L"CBaseFilter", (IBaseFilter *) this );\r
+#endif // DXMPERF\r
+\r
+    ASSERT(pLock != NULL);\r
+    UNREFERENCED_PARAMETER(phr);\r
+}\r
+#endif\r
+\r
+/* Destructor */\r
+\r
+CBaseFilter::~CBaseFilter()\r
+{\r
+#ifdef DXMPERF\r
+    PERFLOG_DTOR( L"CBaseFilter", (IBaseFilter *) this );\r
+#endif // DXMPERF\r
+\r
+    // NOTE we do NOT hold references on the filtergraph for m_pGraph or m_pSink\r
+    // When we did we had the circular reference problem.  Nothing would go away.\r
+\r
+    delete[] m_pName;\r
+\r
+    // must be stopped, but can't call Stop here since\r
+    // our critsec has been destroyed.\r
+\r
+    /* Release any clock we were using */\r
+    if (m_pClock) {\r
+        m_pClock->Release();\r
+        m_pClock = NULL;\r
+    }\r
+}\r
+\r
+/* Return the filter's clsid */\r
+STDMETHODIMP\r
+CBaseFilter::GetClassID(__out CLSID *pClsID)\r
+{\r
+    CheckPointer(pClsID,E_POINTER);\r
+    ValidateReadWritePtr(pClsID,sizeof(CLSID));\r
+    *pClsID = m_clsid;\r
+    return NOERROR;\r
+}\r
+\r
+/* Override this if your state changes are not done synchronously */\r
+STDMETHODIMP\r
+CBaseFilter::GetState(DWORD dwMSecs, __out FILTER_STATE *State)\r
+{\r
+    UNREFERENCED_PARAMETER(dwMSecs);\r
+    CheckPointer(State,E_POINTER);\r
+    ValidateReadWritePtr(State,sizeof(FILTER_STATE));\r
+\r
+    *State = m_State;\r
+    return S_OK;\r
+}\r
+\r
+\r
+/* Set the clock we will use for synchronisation */\r
+\r
+STDMETHODIMP\r
+CBaseFilter::SetSyncSource(__in_opt IReferenceClock *pClock)\r
+{\r
+    CAutoLock cObjectLock(m_pLock);\r
+\r
+    // Ensure the new one does not go away - even if the same as the old\r
+    if (pClock) {\r
+        pClock->AddRef();\r
+    }\r
+\r
+    // if we have a clock, release it\r
+    if (m_pClock) {\r
+        m_pClock->Release();\r
+    }\r
+\r
+    // Set the new reference clock (might be NULL)\r
+    // Should we query it to ensure it is a clock?  Consider for a debug build.\r
+    m_pClock = pClock;\r
+\r
+    return NOERROR;\r
+}\r
+\r
+/* Return the clock we are using for synchronisation */\r
+STDMETHODIMP\r
+CBaseFilter::GetSyncSource(__deref_out_opt IReferenceClock **pClock)\r
+{\r
+    CheckPointer(pClock,E_POINTER);\r
+    ValidateReadWritePtr(pClock,sizeof(IReferenceClock *));\r
+    CAutoLock cObjectLock(m_pLock);\r
+\r
+    if (m_pClock) {\r
+        // returning an interface... addref it...\r
+        m_pClock->AddRef();\r
+    }\r
+    *pClock = (IReferenceClock*)m_pClock;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+\r
+// override CBaseMediaFilter Stop method, to deactivate any pins this\r
+// filter has.\r
+STDMETHODIMP\r
+CBaseFilter::Stop()\r
+{\r
+    CAutoLock cObjectLock(m_pLock);\r
+    HRESULT hr = NOERROR;\r
+\r
+    // notify all pins of the state change\r
+    if (m_State != State_Stopped) {\r
+        int cPins = GetPinCount();\r
+        for (int c = 0; c < cPins; c++) {\r
+\r
+            CBasePin *pPin = GetPin(c);\r
+            if (NULL == pPin) {\r
+                break;\r
+            }\r
+\r
+            // Disconnected pins are not activated - this saves pins worrying\r
+            // about this state themselves. We ignore the return code to make\r
+            // sure everyone is inactivated regardless. The base input pin\r
+            // class can return an error if it has no allocator but Stop can\r
+            // be used to resync the graph state after something has gone bad\r
+\r
+            if (pPin->IsConnected()) {\r
+                HRESULT hrTmp = pPin->Inactive();\r
+                if (FAILED(hrTmp) && SUCCEEDED(hr)) {\r
+                    hr = hrTmp;\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+#ifdef DXMPERF\r
+    PERFLOG_STOP( m_pName ? m_pName : L"CBaseFilter", (IBaseFilter *) this, m_State );\r
+#endif // DXMPERF\r
+\r
+    m_State = State_Stopped;\r
+    return hr;\r
+}\r
+\r
+\r
+// override CBaseMediaFilter Pause method to activate any pins\r
+// this filter has (also called from Run)\r
+\r
+STDMETHODIMP\r
+CBaseFilter::Pause()\r
+{\r
+    CAutoLock cObjectLock(m_pLock);\r
+\r
+    // notify all pins of the change to active state\r
+    if (m_State == State_Stopped) {\r
+        int cPins = GetPinCount();\r
+        for (int c = 0; c < cPins; c++) {\r
+\r
+            CBasePin *pPin = GetPin(c);\r
+            if (NULL == pPin) {\r
+                break;\r
+            }\r
+\r
+            // Disconnected pins are not activated - this saves pins\r
+            // worrying about this state themselves\r
+\r
+            if (pPin->IsConnected()) {\r
+                HRESULT hr = pPin->Active();\r
+                if (FAILED(hr)) {\r
+                    return hr;\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+\r
+#ifdef DXMPERF\r
+    PERFLOG_PAUSE( m_pName ? m_pName : L"CBaseFilter", (IBaseFilter *) this, m_State );\r
+#endif // DXMPERF\r
+\r
+    m_State = State_Paused;\r
+    return S_OK;\r
+}\r
+\r
+// Put the filter into a running state.\r
+\r
+// The time parameter is the offset to be added to the samples'\r
+// stream time to get the reference time at which they should be presented.\r
+//\r
+// you can either add these two and compare it against the reference clock,\r
+// or you can call CBaseFilter::StreamTime and compare that against\r
+// the sample timestamp.\r
+\r
+STDMETHODIMP\r
+CBaseFilter::Run(REFERENCE_TIME tStart)\r
+{\r
+    CAutoLock cObjectLock(m_pLock);\r
+\r
+    // remember the stream time offset\r
+    m_tStart = tStart;\r
+\r
+    if (m_State == State_Stopped){\r
+    HRESULT hr = Pause();\r
+\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    }\r
+    // notify all pins of the change to active state\r
+    if (m_State != State_Running) {\r
+        int cPins = GetPinCount();\r
+        for (int c = 0; c < cPins; c++) {\r
+\r
+            CBasePin *pPin = GetPin(c);\r
+            if (NULL == pPin) {\r
+                break;\r
+            }\r
+\r
+            // Disconnected pins are not activated - this saves pins\r
+            // worrying about this state themselves\r
+\r
+            if (pPin->IsConnected()) {\r
+                HRESULT hr = pPin->Run(tStart);\r
+                if (FAILED(hr)) {\r
+                    return hr;\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+#ifdef DXMPERF\r
+    PERFLOG_RUN( m_pName ? m_pName : L"CBaseFilter", (IBaseFilter *) this, tStart, m_State );\r
+#endif // DXMPERF\r
+\r
+    m_State = State_Running;\r
+    return S_OK;\r
+}\r
+\r
+//\r
+// return the current stream time - samples with start timestamps of this\r
+// time or before should be rendered by now\r
+HRESULT\r
+CBaseFilter::StreamTime(CRefTime& rtStream)\r
+{\r
+    // Caller must lock for synchronization\r
+    // We can't grab the filter lock because we want to be able to call\r
+    // this from worker threads without deadlocking\r
+\r
+    if (m_pClock == NULL) {\r
+        return VFW_E_NO_CLOCK;\r
+    }\r
+\r
+    // get the current reference time\r
+    HRESULT hr = m_pClock->GetTime((REFERENCE_TIME*)&rtStream);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    // subtract the stream offset to get stream time\r
+    rtStream -= m_tStart;\r
+\r
+    return S_OK;\r
+}\r
+\r
+\r
+/* Create an enumerator for the pins attached to this filter */\r
+\r
+STDMETHODIMP\r
+CBaseFilter::EnumPins(__deref_out IEnumPins **ppEnum)\r
+{\r
+    CheckPointer(ppEnum,E_POINTER);\r
+    ValidateReadWritePtr(ppEnum,sizeof(IEnumPins *));\r
+\r
+    /* Create a new ref counted enumerator */\r
+\r
+    *ppEnum = new CEnumPins(this,\r
+                        NULL);\r
+\r
+    return *ppEnum == NULL ? E_OUTOFMEMORY : NOERROR;\r
+}\r
+\r
+\r
+// default behaviour of FindPin is to assume pins are named\r
+// by their pin names\r
+STDMETHODIMP\r
+CBaseFilter::FindPin(\r
+    LPCWSTR Id,\r
+    __deref_out IPin ** ppPin\r
+)\r
+{\r
+    CheckPointer(ppPin,E_POINTER);\r
+    ValidateReadWritePtr(ppPin,sizeof(IPin *));\r
+\r
+    //  We're going to search the pin list so maintain integrity\r
+    CAutoLock lck(m_pLock);\r
+    int iCount = GetPinCount();\r
+    for (int i = 0; i < iCount; i++) {\r
+        CBasePin *pPin = GetPin(i);\r
+        if (NULL == pPin) {\r
+            break;\r
+        }\r
+\r
+        if (0 == lstrcmpW(pPin->Name(), Id)) {\r
+            //  Found one that matches\r
+            //\r
+            //  AddRef() and return it\r
+            *ppPin = pPin;\r
+            pPin->AddRef();\r
+            return S_OK;\r
+        }\r
+    }\r
+    *ppPin = NULL;\r
+    return VFW_E_NOT_FOUND;\r
+}\r
+\r
+/* Return information about this filter */\r
+\r
+STDMETHODIMP\r
+CBaseFilter::QueryFilterInfo(__out FILTER_INFO * pInfo)\r
+{\r
+    CheckPointer(pInfo,E_POINTER);\r
+    ValidateReadWritePtr(pInfo,sizeof(FILTER_INFO));\r
+\r
+    if (m_pName) {\r
+        (void)StringCchCopyW(pInfo->achName, NUMELMS(pInfo->achName), m_pName);\r
+    } else {\r
+        pInfo->achName[0] = L'\0';\r
+    }\r
+    pInfo->pGraph = m_pGraph;\r
+    if (m_pGraph)\r
+        m_pGraph->AddRef();\r
+    return NOERROR;\r
+}\r
+\r
+\r
+/* Provide the filter with a filter graph */\r
+\r
+STDMETHODIMP\r
+CBaseFilter::JoinFilterGraph(\r
+    __inout_opt IFilterGraph * pGraph,\r
+    __in_opt LPCWSTR pName)\r
+{\r
+    CAutoLock cObjectLock(m_pLock);\r
+\r
+    // NOTE: we no longer hold references on the graph (m_pGraph, m_pSink)\r
+\r
+    m_pGraph = pGraph;\r
+    if (m_pGraph) {\r
+        HRESULT hr = m_pGraph->QueryInterface(IID_IMediaEventSink,\r
+                        (void**) &m_pSink);\r
+        if (FAILED(hr)) {\r
+            ASSERT(m_pSink == NULL);\r
+        }\r
+        else m_pSink->Release();        // we do NOT keep a reference on it.\r
+    } else {\r
+        // if graph pointer is null, then we should\r
+        // also release the IMediaEventSink on the same object - we don't\r
+        // refcount it, so just set it to null\r
+        m_pSink = NULL;\r
+    }\r
+\r
+\r
+    if (m_pName) {\r
+        delete[] m_pName;\r
+        m_pName = NULL;\r
+    }\r
+\r
+    if (pName) {\r
+        size_t namelen;\r
+        HRESULT hr = StringCchLengthW(pName, STRSAFE_MAX_CCH, &namelen);\r
+        if (FAILED(hr)) {\r
+            return hr;\r
+        }\r
+        m_pName = new WCHAR[namelen + 1];\r
+        if (m_pName) {\r
+            (void)StringCchCopyW(m_pName, namelen + 1, pName);\r
+        } else {\r
+            return E_OUTOFMEMORY;\r
+        }\r
+    }\r
+\r
+#ifdef DXMPERF\r
+    PERFLOG_JOINGRAPH( m_pName ? m_pName : L"CBaseFilter",(IBaseFilter *) this, pGraph );\r
+#endif // DXMPERF\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// return a Vendor information string. Optional - may return E_NOTIMPL.\r
+// memory returned should be freed using CoTaskMemFree\r
+// default implementation returns E_NOTIMPL\r
+STDMETHODIMP\r
+CBaseFilter::QueryVendorInfo(\r
+    __deref_out LPWSTR* pVendorInfo)\r
+{\r
+    UNREFERENCED_PARAMETER(pVendorInfo);\r
+    return E_NOTIMPL;\r
+}\r
+\r
+\r
+// send an event notification to the filter graph if we know about it.\r
+// returns S_OK if delivered, S_FALSE if the filter graph does not sink\r
+// events, or an error otherwise.\r
+HRESULT\r
+CBaseFilter::NotifyEvent(\r
+    long EventCode,\r
+    LONG_PTR EventParam1,\r
+    LONG_PTR EventParam2)\r
+{\r
+    // Snapshot so we don't have to lock up\r
+    IMediaEventSink *pSink = m_pSink;\r
+    if (pSink) {\r
+        if (EC_COMPLETE == EventCode) {\r
+            EventParam2 = (LONG_PTR)(IBaseFilter*)this;\r
+        }\r
+\r
+        return pSink->Notify(EventCode, EventParam1, EventParam2);\r
+    } else {\r
+        return E_NOTIMPL;\r
+    }\r
+}\r
+\r
+// Request reconnect\r
+// pPin is the pin to reconnect\r
+// pmt is the type to reconnect with - can be NULL\r
+// Calls ReconnectEx on the filter graph\r
+HRESULT\r
+CBaseFilter::ReconnectPin(\r
+    IPin *pPin,\r
+    __in_opt AM_MEDIA_TYPE const *pmt\r
+)\r
+{\r
+    IFilterGraph2 *pGraph2;\r
+    if (m_pGraph != NULL) {\r
+        HRESULT hr = m_pGraph->QueryInterface(IID_IFilterGraph2, (void **)&pGraph2);\r
+        if (SUCCEEDED(hr)) {\r
+            hr = pGraph2->ReconnectEx(pPin, pmt);\r
+            pGraph2->Release();\r
+            return hr;\r
+        } else {\r
+            return m_pGraph->Reconnect(pPin);\r
+        }\r
+    } else {\r
+        return E_NOINTERFACE;\r
+    }\r
+}\r
+\r
+\r
+\r
+/* This is the same idea as the media type version does for type enumeration\r
+   on pins but for the list of pins available. So if the list of pins you\r
+   provide changes dynamically then either override this virtual function\r
+   to provide the version number, or more simply call IncrementPinVersion */\r
+\r
+LONG CBaseFilter::GetPinVersion()\r
+{\r
+    return m_PinVersion;\r
+}\r
+\r
+\r
+/* Increment the current pin version cookie */\r
+\r
+void CBaseFilter::IncrementPinVersion()\r
+{\r
+    InterlockedIncrement(&m_PinVersion);\r
+}\r
+\r
+/* register filter */\r
+\r
+STDMETHODIMP CBaseFilter::Register()\r
+{\r
+    // get setup data, if it exists\r
+    //\r
+    LPAMOVIESETUP_FILTER psetupdata = GetSetupData();\r
+\r
+    // check we've got data\r
+    //\r
+    if( NULL == psetupdata ) return S_FALSE;\r
+\r
+    // init is ref counted so call just in case\r
+    // we're being called cold.\r
+    //\r
+    HRESULT hr = CoInitialize( (LPVOID)NULL );\r
+    ASSERT( SUCCEEDED(hr) );\r
+\r
+    // get hold of IFilterMapper\r
+    //\r
+    IFilterMapper *pIFM;\r
+    hr = CoCreateInstance( CLSID_FilterMapper\r
+                             , NULL\r
+                             , CLSCTX_INPROC_SERVER\r
+                             , IID_IFilterMapper\r
+                             , (void **)&pIFM       );\r
+    if( SUCCEEDED(hr) )\r
+    {\r
+        hr = AMovieSetupRegisterFilter( psetupdata, pIFM, TRUE );\r
+        pIFM->Release();\r
+    }\r
+\r
+    // and clear up\r
+    //\r
+    CoFreeUnusedLibraries();\r
+    CoUninitialize();\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+/* unregister filter */\r
+\r
+STDMETHODIMP CBaseFilter::Unregister()\r
+{\r
+    // get setup data, if it exists\r
+    //\r
+    LPAMOVIESETUP_FILTER psetupdata = GetSetupData();\r
+\r
+    // check we've got data\r
+    //\r
+    if( NULL == psetupdata ) return S_FALSE;\r
+\r
+    // OLE init is ref counted so call\r
+    // just in case we're being called cold.\r
+    //\r
+    HRESULT hr = CoInitialize( (LPVOID)NULL );\r
+    ASSERT( SUCCEEDED(hr) );\r
+\r
+    // get hold of IFilterMapper\r
+    //\r
+    IFilterMapper *pIFM;\r
+    hr = CoCreateInstance( CLSID_FilterMapper\r
+                             , NULL\r
+                             , CLSCTX_INPROC_SERVER\r
+                             , IID_IFilterMapper\r
+                             , (void **)&pIFM       );\r
+    if( SUCCEEDED(hr) )\r
+    {\r
+        hr = AMovieSetupRegisterFilter( psetupdata, pIFM, FALSE );\r
+\r
+        // release interface\r
+        //\r
+        pIFM->Release();\r
+    }\r
+\r
+    // clear up\r
+    //\r
+    CoFreeUnusedLibraries();\r
+    CoUninitialize();\r
+\r
+    // handle one acceptable "error" - that\r
+    // of filter not being registered!\r
+    // (couldn't find a suitable #define'd\r
+    // name for the error!)\r
+    //\r
+    if( 0x80070002 == hr)\r
+      return NOERROR;\r
+    else\r
+      return hr;\r
+}\r
+\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Implements CEnumPins\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+\r
+CEnumPins::CEnumPins(__in CBaseFilter *pFilter,\r
+                     __in_opt CEnumPins *pEnumPins) :\r
+    m_Position(0),\r
+    m_PinCount(0),\r
+    m_pFilter(pFilter),\r
+    m_cRef(1),               // Already ref counted\r
+    m_PinCache(NAME("Pin Cache"))\r
+{\r
+\r
+#ifdef DEBUG\r
+    m_dwCookie = DbgRegisterObjectCreation("CEnumPins", 0);\r
+#endif\r
+\r
+    /* We must be owned by a filter derived from CBaseFilter */\r
+\r
+    ASSERT(pFilter != NULL);\r
+\r
+    /* Hold a reference count on our filter */\r
+    m_pFilter->AddRef();\r
+\r
+    /* Are we creating a new enumerator */\r
+\r
+    if (pEnumPins == NULL) {\r
+        m_Version = m_pFilter->GetPinVersion();\r
+        m_PinCount = m_pFilter->GetPinCount();\r
+    } else {\r
+        ASSERT(m_Position <= m_PinCount);\r
+        m_Position = pEnumPins->m_Position;\r
+        m_PinCount = pEnumPins->m_PinCount;\r
+        m_Version = pEnumPins->m_Version;\r
+        m_PinCache.AddTail(&(pEnumPins->m_PinCache));\r
+    }\r
+}\r
+\r
+\r
+/* Destructor releases the reference count on our filter NOTE since we hold\r
+   a reference count on the filter who created us we know it is safe to\r
+   release it, no access can be made to it afterwards though as we have just\r
+   caused the last reference count to go and the object to be deleted */\r
+\r
+CEnumPins::~CEnumPins()\r
+{\r
+    m_pFilter->Release();\r
+\r
+#ifdef DEBUG\r
+    DbgRegisterObjectDestruction(m_dwCookie);\r
+#endif\r
+}\r
+\r
+\r
+/* Override this to say what interfaces we support where */\r
+\r
+STDMETHODIMP\r
+CEnumPins::QueryInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    CheckPointer(ppv, E_POINTER);\r
+\r
+    /* Do we have this interface */\r
+\r
+    if (riid == IID_IEnumPins || riid == IID_IUnknown) {\r
+        return GetInterface((IEnumPins *) this, ppv);\r
+    } else {\r
+        *ppv = NULL;\r
+        return E_NOINTERFACE;\r
+    }\r
+}\r
+\r
+STDMETHODIMP_(ULONG)\r
+CEnumPins::AddRef()\r
+{\r
+    return InterlockedIncrement(&m_cRef);\r
+}\r
+\r
+STDMETHODIMP_(ULONG)\r
+CEnumPins::Release()\r
+{\r
+    ULONG cRef = InterlockedDecrement(&m_cRef);\r
+    if (cRef == 0) {\r
+        delete this;\r
+    }\r
+    return cRef;\r
+}\r
+\r
+/* One of an enumerator's basic member functions allows us to create a cloned\r
+   interface that initially has the same state. Since we are taking a snapshot\r
+   of an object (current position and all) we must lock access at the start */\r
+\r
+STDMETHODIMP \r
+CEnumPins::Clone(__deref_out IEnumPins **ppEnum)\r
+{\r
+    CheckPointer(ppEnum,E_POINTER);\r
+    ValidateReadWritePtr(ppEnum,sizeof(IEnumPins *));\r
+    HRESULT hr = NOERROR;\r
+\r
+    /* Check we are still in sync with the filter */\r
+    if (AreWeOutOfSync() == TRUE) {\r
+        *ppEnum = NULL;\r
+        hr =  VFW_E_ENUM_OUT_OF_SYNC;\r
+    } else {\r
+        *ppEnum = new CEnumPins(m_pFilter, \r
+                                this);\r
+        if (*ppEnum == NULL) {\r
+            hr = E_OUTOFMEMORY;\r
+        }\r
+    }\r
+    return hr;\r
+}\r
+\r
+\r
+/* Return the next pin after the current position */\r
+\r
+STDMETHODIMP\r
+CEnumPins::Next(ULONG cPins,        // place this many pins...\r
+        __out_ecount(cPins) IPin **ppPins,      // ...in this array\r
+        __out_opt ULONG *pcFetched)   // actual count passed returned here\r
+{\r
+    CheckPointer(ppPins,E_POINTER);\r
+    ValidateReadWritePtr(ppPins,cPins * sizeof(IPin *));\r
+\r
+    ASSERT(ppPins);\r
+\r
+    if (pcFetched!=NULL) {\r
+        ValidateWritePtr(pcFetched, sizeof(ULONG));\r
+        *pcFetched = 0;           // default unless we succeed\r
+    }\r
+    // now check that the parameter is valid\r
+    else if (cPins>1) {   // pcFetched == NULL\r
+        return E_INVALIDARG;\r
+    }\r
+    ULONG cFetched = 0;           // increment as we get each one.\r
+\r
+    /* Check we are still in sync with the filter */\r
+    if (AreWeOutOfSync() == TRUE) {\r
+        // If we are out of sync, we should refresh the enumerator.\r
+        // This will reset the position and update the other members, but\r
+        // will not clear cache of pins we have already returned.\r
+        Refresh();\r
+    }\r
+\r
+    /* Return each pin interface NOTE GetPin returns CBasePin * not addrefed\r
+       so we must QI for the IPin (which increments its reference count)\r
+       If while we are retrieving a pin from the filter an error occurs we\r
+       assume that our internal state is stale with respect to the filter\r
+       (for example someone has deleted a pin) so we\r
+       return VFW_E_ENUM_OUT_OF_SYNC                            */\r
+\r
+    while (cFetched < cPins && m_PinCount > m_Position) {\r
+\r
+        /* Get the next pin object from the filter */\r
+\r
+        CBasePin *pPin = m_pFilter->GetPin(m_Position++);\r
+        if (pPin == NULL) {\r
+            // If this happend, and it's not the first time through, then we've got a problem,\r
+            // since we should really go back and release the iPins, which we have previously\r
+            // AddRef'ed.\r
+            ASSERT( cFetched==0 );\r
+            return VFW_E_ENUM_OUT_OF_SYNC;\r
+        }\r
+\r
+        /* We only want to return this pin, if it is not in our cache */\r
+        if (0 == m_PinCache.Find(pPin))\r
+        {\r
+            /* From the object get an IPin interface */\r
+\r
+            *ppPins = pPin;\r
+            pPin->AddRef();\r
+\r
+            cFetched++;\r
+            ppPins++;\r
+\r
+            m_PinCache.AddTail(pPin);\r
+        }\r
+    }\r
+\r
+    if (pcFetched!=NULL) {\r
+        *pcFetched = cFetched;\r
+    }\r
+\r
+    return (cPins==cFetched ? NOERROR : S_FALSE);\r
+}\r
+\r
+\r
+/* Skip over one or more entries in the enumerator */\r
+\r
+STDMETHODIMP\r
+CEnumPins::Skip(ULONG cPins)\r
+{\r
+    /* Check we are still in sync with the filter */\r
+    if (AreWeOutOfSync() == TRUE) {\r
+        return VFW_E_ENUM_OUT_OF_SYNC;\r
+    }\r
+\r
+    /* Work out how many pins are left to skip over */\r
+    /* We could position at the end if we are asked to skip too many... */\r
+    /* ..which would match the base implementation for CEnumMediaTypes::Skip */\r
+\r
+    ULONG PinsLeft = m_PinCount - m_Position;\r
+    if (cPins > PinsLeft) {\r
+        return S_FALSE;\r
+    }\r
+    m_Position += cPins;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+/* Set the current position back to the start */\r
+/* Reset has 4 simple steps:\r
+ *\r
+ * Set position to head of list\r
+ * Sync enumerator with object being enumerated\r
+ * Clear the cache of pins already returned\r
+ * return S_OK\r
+ */\r
+\r
+STDMETHODIMP\r
+CEnumPins::Reset()\r
+{\r
+    m_Version = m_pFilter->GetPinVersion();\r
+    m_PinCount = m_pFilter->GetPinCount();\r
+\r
+    m_Position = 0;\r
+\r
+    // Clear the cache\r
+    m_PinCache.RemoveAll();\r
+\r
+    return S_OK;\r
+}\r
+\r
+\r
+/* Set the current position back to the start */\r
+/* Refresh has 3 simple steps:\r
+ *\r
+ * Set position to head of list\r
+ * Sync enumerator with object being enumerated\r
+ * return S_OK\r
+ */\r
+\r
+STDMETHODIMP\r
+CEnumPins::Refresh()\r
+{\r
+    m_Version = m_pFilter->GetPinVersion();\r
+    m_PinCount = m_pFilter->GetPinCount();\r
+\r
+    m_Position = 0;\r
+    return S_OK;\r
+}\r
+\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Implements CEnumMediaTypes\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+\r
+CEnumMediaTypes::CEnumMediaTypes(__in CBasePin *pPin,\r
+                                 __in_opt CEnumMediaTypes *pEnumMediaTypes) :\r
+    m_Position(0),\r
+    m_pPin(pPin),\r
+    m_cRef(1)\r
+{\r
+\r
+#ifdef DEBUG\r
+    m_dwCookie = DbgRegisterObjectCreation("CEnumMediaTypes", 0);\r
+#endif\r
+\r
+    /* We must be owned by a pin derived from CBasePin */\r
+\r
+    ASSERT(pPin != NULL);\r
+\r
+    /* Hold a reference count on our pin */\r
+    m_pPin->AddRef();\r
+\r
+    /* Are we creating a new enumerator */\r
+\r
+    if (pEnumMediaTypes == NULL) {\r
+        m_Version = m_pPin->GetMediaTypeVersion();\r
+        return;\r
+    }\r
+\r
+    m_Position = pEnumMediaTypes->m_Position;\r
+    m_Version = pEnumMediaTypes->m_Version;\r
+}\r
+\r
+\r
+/* Destructor releases the reference count on our base pin. NOTE since we hold\r
+   a reference count on the pin who created us we know it is safe to release\r
+   it, no access can be made to it afterwards though as we might have just\r
+   caused the last reference count to go and the object to be deleted */\r
+\r
+CEnumMediaTypes::~CEnumMediaTypes()\r
+{\r
+#ifdef DEBUG\r
+    DbgRegisterObjectDestruction(m_dwCookie);\r
+#endif\r
+    m_pPin->Release();\r
+}\r
+\r
+\r
+/* Override this to say what interfaces we support where */\r
+\r
+STDMETHODIMP\r
+CEnumMediaTypes::QueryInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    CheckPointer(ppv, E_POINTER);\r
+\r
+    /* Do we have this interface */\r
+\r
+    if (riid == IID_IEnumMediaTypes || riid == IID_IUnknown) {\r
+        return GetInterface((IEnumMediaTypes *) this, ppv);\r
+    } else {\r
+        *ppv = NULL;\r
+        return E_NOINTERFACE;\r
+    }\r
+}\r
+\r
+STDMETHODIMP_(ULONG)\r
+CEnumMediaTypes::AddRef()\r
+{\r
+    return InterlockedIncrement(&m_cRef);\r
+}\r
+\r
+STDMETHODIMP_(ULONG)\r
+CEnumMediaTypes::Release()\r
+{\r
+    ULONG cRef = InterlockedDecrement(&m_cRef);\r
+    if (cRef == 0) {\r
+        delete this;\r
+    }\r
+    return cRef;\r
+}\r
+\r
+/* One of an enumerator's basic member functions allows us to create a cloned\r
+   interface that initially has the same state. Since we are taking a snapshot\r
+   of an object (current position and all) we must lock access at the start */\r
+\r
+STDMETHODIMP\r
+CEnumMediaTypes::Clone(__deref_out IEnumMediaTypes **ppEnum)\r
+{\r
+    CheckPointer(ppEnum,E_POINTER);\r
+    ValidateReadWritePtr(ppEnum,sizeof(IEnumMediaTypes *));\r
+    HRESULT hr = NOERROR;\r
+\r
+    /* Check we are still in sync with the pin */\r
+    if (AreWeOutOfSync() == TRUE) {\r
+        *ppEnum = NULL;\r
+        hr = VFW_E_ENUM_OUT_OF_SYNC;\r
+    } else {\r
+\r
+        *ppEnum = new CEnumMediaTypes(m_pPin,\r
+                                      this);\r
+\r
+        if (*ppEnum == NULL) {\r
+            hr =  E_OUTOFMEMORY;\r
+        }\r
+    }\r
+    return hr;\r
+}\r
+\r
+\r
+/* Enumerate the next pin(s) after the current position. The client using this\r
+   interface passes in a pointer to an array of pointers each of which will\r
+   be filled in with a pointer to a fully initialised media type format\r
+   Return NOERROR if it all works,\r
+          S_FALSE if fewer than cMediaTypes were enumerated.\r
+          VFW_E_ENUM_OUT_OF_SYNC if the enumerator has been broken by\r
+                                 state changes in the filter\r
+   The actual count always correctly reflects the number of types in the array.\r
+*/\r
+\r
+STDMETHODIMP\r
+CEnumMediaTypes::Next(ULONG cMediaTypes,          // place this many types...\r
+                      __out_ecount(cMediaTypes) AM_MEDIA_TYPE **ppMediaTypes,   // ...in this array\r
+                      __out ULONG *pcFetched)           // actual count passed\r
+{\r
+    CheckPointer(ppMediaTypes,E_POINTER);\r
+    ValidateReadWritePtr(ppMediaTypes,cMediaTypes * sizeof(AM_MEDIA_TYPE *));\r
+    /* Check we are still in sync with the pin */\r
+    if (AreWeOutOfSync() == TRUE) {\r
+        return VFW_E_ENUM_OUT_OF_SYNC;\r
+    }\r
+\r
+    if (pcFetched!=NULL) {\r
+        ValidateWritePtr(pcFetched, sizeof(ULONG));\r
+        *pcFetched = 0;           // default unless we succeed\r
+    }\r
+    // now check that the parameter is valid\r
+    else if (cMediaTypes>1) {     // pcFetched == NULL\r
+        return E_INVALIDARG;\r
+    }\r
+    ULONG cFetched = 0;           // increment as we get each one.\r
+\r
+    /* Return each media type by asking the filter for them in turn - If we\r
+       have an error code retured to us while we are retrieving a media type\r
+       we assume that our internal state is stale with respect to the filter\r
+       (for example the window size changing) so we return\r
+       VFW_E_ENUM_OUT_OF_SYNC */\r
+\r
+    while (cMediaTypes) {\r
+\r
+        CMediaType cmt;\r
+\r
+        HRESULT hr = m_pPin->GetMediaType(m_Position++, &cmt);\r
+        if (S_OK != hr) {\r
+            break;\r
+        }\r
+\r
+        /* We now have a CMediaType object that contains the next media type\r
+           but when we assign it to the array position we CANNOT just assign\r
+           the AM_MEDIA_TYPE structure because as soon as the object goes out of\r
+           scope it will delete the memory we have just copied. The function\r
+           we use is CreateMediaType which allocates a task memory block */\r
+\r
+        /*  Transfer across the format block manually to save an allocate\r
+            and free on the format block and generally go faster */\r
+\r
+        *ppMediaTypes = (AM_MEDIA_TYPE *)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));\r
+        if (*ppMediaTypes == NULL) {\r
+            break;\r
+        }\r
+\r
+        /*  Do a regular copy */\r
+        **ppMediaTypes = cmt;\r
+\r
+        /*  Make sure the destructor doesn't free these */\r
+        cmt.pbFormat = NULL;\r
+        cmt.cbFormat = NULL;\r
+        cmt.pUnk     = NULL;\r
+\r
+\r
+        ppMediaTypes++;\r
+        cFetched++;\r
+        cMediaTypes--;\r
+    }\r
+\r
+    if (pcFetched!=NULL) {\r
+        *pcFetched = cFetched;\r
+    }\r
+\r
+    return ( cMediaTypes==0 ? NOERROR : S_FALSE );\r
+}\r
+\r
+\r
+/* Skip over one or more entries in the enumerator */\r
+\r
+STDMETHODIMP\r
+CEnumMediaTypes::Skip(ULONG cMediaTypes)\r
+{\r
+    //  If we're skipping 0 elements we're guaranteed to skip the\r
+    //  correct number of elements\r
+    if (cMediaTypes == 0) {\r
+        return S_OK;\r
+    }\r
+\r
+    /* Check we are still in sync with the pin */\r
+    if (AreWeOutOfSync() == TRUE) {\r
+        return VFW_E_ENUM_OUT_OF_SYNC;\r
+    }\r
+\r
+    m_Position += cMediaTypes;\r
+\r
+    /*  See if we're over the end */\r
+    CMediaType cmt;\r
+    return S_OK == m_pPin->GetMediaType(m_Position - 1, &cmt) ? S_OK : S_FALSE;\r
+}\r
+\r
+\r
+/* Set the current position back to the start */\r
+/* Reset has 3 simple steps:\r
+ *\r
+ * set position to head of list\r
+ * sync enumerator with object being enumerated\r
+ * return S_OK\r
+ */\r
+\r
+STDMETHODIMP\r
+CEnumMediaTypes::Reset()\r
+\r
+{\r
+    m_Position = 0;\r
+\r
+    // Bring the enumerator back into step with the current state.  This\r
+    // may be a noop but ensures that the enumerator will be valid on the\r
+    // next call.\r
+    m_Version = m_pPin->GetMediaTypeVersion();\r
+    return NOERROR;\r
+}\r
+\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Implements CBasePin\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+\r
+/* NOTE The implementation of this class calls the CUnknown constructor with\r
+   a NULL outer unknown pointer. This has the effect of making us a self\r
+   contained class, ie any QueryInterface, AddRef or Release calls will be\r
+   routed to the class's NonDelegatingUnknown methods. You will typically\r
+   find that the classes that do this then override one or more of these\r
+   virtual functions to provide more specialised behaviour. A good example\r
+   of this is where a class wants to keep the QueryInterface internal but\r
+   still wants its lifetime controlled by the external object */\r
+\r
+/* Constructor */\r
+\r
+CBasePin::CBasePin(__in_opt LPCTSTR pObjectName,\r
+           __in CBaseFilter *pFilter,\r
+           __in CCritSec *pLock,\r
+           __inout HRESULT *phr,\r
+           __in_opt LPCWSTR pName,\r
+           PIN_DIRECTION dir) :\r
+    CUnknown( pObjectName, NULL ),\r
+    m_pFilter(pFilter),\r
+    m_pLock(pLock),\r
+    m_pName(NULL),\r
+    m_Connected(NULL),\r
+    m_dir(dir),\r
+    m_bRunTimeError(FALSE),\r
+    m_pQSink(NULL),\r
+    m_TypeVersion(1),\r
+    m_tStart(),\r
+    m_tStop(MAX_TIME),\r
+    m_bCanReconnectWhenActive(false),\r
+    m_bTryMyTypesFirst(false),\r
+    m_dRate(1.0)\r
+{\r
+    /*  WARNING - pFilter is often not a properly constituted object at\r
+        this state (in particular QueryInterface may not work) - this\r
+        is because its owner is often its containing object and we\r
+        have been called from the containing object's constructor so\r
+        the filter's owner has not yet had its CUnknown constructor\r
+        called\r
+    */\r
+#ifdef DXMPERF\r
+    PERFLOG_CTOR( pName ? pName : L"CBasePin", (IPin *) this );\r
+#endif // DXMPERF\r
+\r
+    ASSERT(pFilter != NULL);\r
+    ASSERT(pLock != NULL);\r
+\r
+    if (pName) {\r
+        size_t cchName;\r
+        HRESULT hr = StringCchLengthW(pName, STRSAFE_MAX_CCH, &cchName);\r
+        if (SUCCEEDED(hr)) {\r
+            m_pName = new WCHAR[cchName + 1];\r
+            if (m_pName) {\r
+                (void)StringCchCopyW(m_pName, cchName + 1, pName);\r
+            }\r
+        }\r
+    }\r
+\r
+#ifdef DEBUG\r
+    m_cRef = 0;\r
+#endif\r
+}\r
+\r
+#ifdef UNICODE\r
+CBasePin::CBasePin(__in_opt LPCSTR pObjectName,\r
+           __in CBaseFilter *pFilter,\r
+           __in CCritSec *pLock,\r
+           __inout HRESULT *phr,\r
+           __in_opt LPCWSTR pName,\r
+           PIN_DIRECTION dir) :\r
+    CUnknown( pObjectName, NULL ),\r
+    m_pFilter(pFilter),\r
+    m_pLock(pLock),\r
+    m_pName(NULL),\r
+    m_Connected(NULL),\r
+    m_dir(dir),\r
+    m_bRunTimeError(FALSE),\r
+    m_pQSink(NULL),\r
+    m_TypeVersion(1),\r
+    m_tStart(),\r
+    m_tStop(MAX_TIME),\r
+    m_bCanReconnectWhenActive(false),\r
+    m_bTryMyTypesFirst(false),\r
+    m_dRate(1.0)\r
+{\r
+    /*  WARNING - pFilter is often not a properly constituted object at\r
+        this state (in particular QueryInterface may not work) - this\r
+        is because its owner is often its containing object and we\r
+        have been called from the containing object's constructor so\r
+        the filter's owner has not yet had its CUnknown constructor\r
+        called\r
+    */\r
+#ifdef DXMPERF\r
+    PERFLOG_CTOR( pName ? pName : L"CBasePin", (IPin *) this );\r
+#endif // DXMPERF\r
+\r
+    ASSERT(pFilter != NULL);\r
+    ASSERT(pLock != NULL);\r
+\r
+    if (pName) {\r
+        size_t cchName;\r
+        HRESULT hr = StringCchLengthW(pName, STRSAFE_MAX_CCH, &cchName);\r
+        if (SUCCEEDED(hr)) {\r
+            m_pName = new WCHAR[cchName + 1];\r
+            if (m_pName) {\r
+                (void)StringCchCopyW(m_pName, cchName + 1, pName);\r
+            }\r
+        }\r
+    }\r
+\r
+\r
+#ifdef DEBUG\r
+    m_cRef = 0;\r
+#endif\r
+}\r
+#endif\r
+\r
+/* Destructor since a connected pin holds a reference count on us there is\r
+   no way that we can be deleted unless we are not currently connected */\r
+\r
+CBasePin::~CBasePin()\r
+{\r
+#ifdef DXMPERF\r
+    PERFLOG_DTOR( m_pName ? m_pName : L"CBasePin", (IPin *) this );\r
+#endif // DXMPERF\r
+\r
+    //  We don't call disconnect because if the filter is going away\r
+    //  all the pins must have a reference count of zero so they must\r
+    //  have been disconnected anyway - (but check the assumption)\r
+    ASSERT(m_Connected == FALSE);\r
+\r
+    delete[] m_pName;\r
+\r
+    // check the internal reference count is consistent\r
+    ASSERT(m_cRef == 0);\r
+}\r
+\r
+\r
+/* Override this to say what interfaces we support and where */\r
+\r
+STDMETHODIMP\r
+CBasePin::NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv)\r
+{\r
+    /* Do we have this interface */\r
+\r
+    if (riid == IID_IPin) {\r
+        return GetInterface((IPin *) this, ppv);\r
+    } else if (riid == IID_IQualityControl) {\r
+        return GetInterface((IQualityControl *) this, ppv);\r
+    } else {\r
+        return CUnknown::NonDelegatingQueryInterface(riid, ppv);\r
+    }\r
+}\r
+\r
+\r
+/* Override to increment the owning filter's reference count */\r
+\r
+STDMETHODIMP_(ULONG)\r
+CBasePin::NonDelegatingAddRef()\r
+{\r
+    ASSERT(InterlockedIncrement(&m_cRef) > 0);\r
+    return m_pFilter->AddRef();\r
+}\r
+\r
+\r
+/* Override to decrement the owning filter's reference count */\r
+\r
+STDMETHODIMP_(ULONG)\r
+CBasePin::NonDelegatingRelease()\r
+{\r
+    ASSERT(InterlockedDecrement(&m_cRef) >= 0);\r
+    return m_pFilter->Release();\r
+}\r
+\r
+\r
+/* Displays pin connection information */\r
+\r
+#ifdef DEBUG\r
+void\r
+CBasePin::DisplayPinInfo(IPin *pReceivePin)\r
+{\r
+\r
+    if (DbgCheckModuleLevel(LOG_TRACE, CONNECT_TRACE_LEVEL)) {\r
+        PIN_INFO ConnectPinInfo;\r
+        PIN_INFO ReceivePinInfo;\r
+\r
+        if (FAILED(QueryPinInfo(&ConnectPinInfo))) {\r
+            StringCchCopyW(ConnectPinInfo.achName, sizeof(ConnectPinInfo.achName)/sizeof(WCHAR), L"Bad Pin");\r
+        } else {\r
+            QueryPinInfoReleaseFilter(ConnectPinInfo);\r
+        }\r
+\r
+        if (FAILED(pReceivePin->QueryPinInfo(&ReceivePinInfo))) {\r
+            StringCchCopyW(ReceivePinInfo.achName, sizeof(ReceivePinInfo.achName)/sizeof(WCHAR), L"Bad Pin");\r
+        } else {\r
+            QueryPinInfoReleaseFilter(ReceivePinInfo);\r
+        }\r
+\r
+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Trying to connect Pins :")));\r
+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("    <%ls>"), ConnectPinInfo.achName));\r
+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("    <%ls>"), ReceivePinInfo.achName));\r
+    }\r
+}\r
+#endif\r
+\r
+\r
+/* Displays general information on the pin media type */\r
+\r
+#ifdef DEBUG\r
+void CBasePin::DisplayTypeInfo(IPin *pPin, const CMediaType *pmt)\r
+{\r
+    UNREFERENCED_PARAMETER(pPin);\r
+    if (DbgCheckModuleLevel(LOG_TRACE, CONNECT_TRACE_LEVEL)) {\r
+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Trying media type:")));\r
+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("    major type:  %hs"),\r
+               GuidNames[*pmt->Type()]));\r
+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("    sub type  :  %hs"),\r
+               GuidNames[*pmt->Subtype()]));\r
+    }\r
+}\r
+#endif\r
+\r
+/* Asked to connect to a pin. A pin is always attached to an owning filter\r
+   object so we always delegate our locking to that object. We first of all\r
+   retrieve a media type enumerator for the input pin and see if we accept\r
+   any of the formats that it would ideally like, failing that we retrieve\r
+   our enumerator and see if it will accept any of our preferred types */\r
+\r
+STDMETHODIMP\r
+CBasePin::Connect(\r
+    IPin * pReceivePin,\r
+    __in_opt const AM_MEDIA_TYPE *pmt   // optional media type\r
+)\r
+{\r
+    CheckPointer(pReceivePin,E_POINTER);\r
+    ValidateReadPtr(pReceivePin,sizeof(IPin));\r
+    CAutoLock cObjectLock(m_pLock);\r
+    DisplayPinInfo(pReceivePin);\r
+\r
+    /* See if we are already connected */\r
+\r
+    if (m_Connected) {\r
+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Already connected")));\r
+        return VFW_E_ALREADY_CONNECTED;\r
+    }\r
+\r
+    /* See if the filter is active */\r
+    if (!IsStopped() && !m_bCanReconnectWhenActive) {\r
+        return VFW_E_NOT_STOPPED;\r
+    }\r
+\r
+\r
+    // Find a mutually agreeable media type -\r
+    // Pass in the template media type. If this is partially specified,\r
+    // each of the enumerated media types will need to be checked against\r
+    // it. If it is non-null and fully specified, we will just try to connect\r
+    // with this.\r
+\r
+    const CMediaType * ptype = (CMediaType*)pmt;\r
+    HRESULT hr = AgreeMediaType(pReceivePin, ptype);\r
+    if (FAILED(hr)) {\r
+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Failed to agree type")));\r
+\r
+        // Since the procedure is already returning an error code, there\r
+        // is nothing else this function can do to report the error.\r
+        EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );\r
+\r
+#ifdef DXMPERF\r
+        PERFLOG_CONNECT( (IPin *) this, pReceivePin, hr, pmt );\r
+#endif // DXMPERF\r
+\r
+        return hr;\r
+    }\r
+\r
+    DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Connection succeeded")));\r
+\r
+#ifdef DXMPERF\r
+    PERFLOG_CONNECT( (IPin *) this, pReceivePin, NOERROR, pmt );\r
+#endif // DXMPERF\r
+\r
+    return NOERROR;\r
+}\r
+\r
+// given a specific media type, attempt a connection (includes\r
+// checking that the type is acceptable to this pin)\r
+HRESULT\r
+CBasePin::AttemptConnection(\r
+    IPin* pReceivePin,      // connect to this pin\r
+    const CMediaType* pmt   // using this type\r
+)\r
+{\r
+    // The caller should hold the filter lock becasue this function\r
+    // uses m_Connected.  The caller should also hold the filter lock\r
+    // because this function calls SetMediaType(), IsStopped() and\r
+    // CompleteConnect().\r
+    ASSERT(CritCheckIn(m_pLock));\r
+\r
+    // Check that the connection is valid  -- need to do this for every\r
+    // connect attempt since BreakConnect will undo it.\r
+    HRESULT hr = CheckConnect(pReceivePin);\r
+    if (FAILED(hr)) {\r
+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("CheckConnect failed")));\r
+\r
+        // Since the procedure is already returning an error code, there\r
+        // is nothing else this function can do to report the error.\r
+        EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );\r
+\r
+        return hr;\r
+    }\r
+\r
+    DisplayTypeInfo(pReceivePin, pmt);\r
+\r
+    /* Check we will accept this media type */\r
+\r
+    hr = CheckMediaType(pmt);\r
+    if (hr == NOERROR) {\r
+\r
+        /*  Make ourselves look connected otherwise ReceiveConnection\r
+            may not be able to complete the connection\r
+        */\r
+        m_Connected = pReceivePin;\r
+        m_Connected->AddRef();\r
+        hr = SetMediaType(pmt);\r
+        if (SUCCEEDED(hr)) {\r
+            /* See if the other pin will accept this type */\r
+\r
+            hr = pReceivePin->ReceiveConnection((IPin *)this, pmt);\r
+            if (SUCCEEDED(hr)) {\r
+                /* Complete the connection */\r
+\r
+                hr = CompleteConnect(pReceivePin);\r
+                if (SUCCEEDED(hr)) {\r
+                    return hr;\r
+                } else {\r
+                    DbgLog((LOG_TRACE,\r
+                            CONNECT_TRACE_LEVEL,\r
+                            TEXT("Failed to complete connection")));\r
+                    pReceivePin->Disconnect();\r
+                }\r
+            }\r
+        }\r
+    } else {\r
+        // we cannot use this media type\r
+\r
+        // return a specific media type error if there is one\r
+        // or map a general failure code to something more helpful\r
+        // (in particular S_FALSE gets changed to an error code)\r
+        if (SUCCEEDED(hr) ||\r
+            (hr == E_FAIL) ||\r
+            (hr == E_INVALIDARG)) {\r
+            hr = VFW_E_TYPE_NOT_ACCEPTED;\r
+        }\r
+    }\r
+\r
+    // BreakConnect and release any connection here in case CheckMediaType\r
+    // failed, or if we set anything up during a call back during\r
+    // ReceiveConnection.\r
+\r
+    // Since the procedure is already returning an error code, there\r
+    // is nothing else this function can do to report the error.\r
+    EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );\r
+\r
+    /*  If failed then undo our state */\r
+    if (m_Connected) {\r
+        m_Connected->Release();\r
+        m_Connected = NULL;\r
+    }\r
+\r
+    return hr;\r
+}\r
+\r
+/* Given an enumerator we cycle through all the media types it proposes and\r
+   firstly suggest them to our derived pin class and if that succeeds try\r
+   them with the pin in a ReceiveConnection call. This means that if our pin\r
+   proposes a media type we still check in here that we can support it. This\r
+   is deliberate so that in simple cases the enumerator can hold all of the\r
+   media types even if some of them are not really currently available */\r
+\r
+HRESULT CBasePin::TryMediaTypes(\r
+    IPin *pReceivePin,\r
+    __in_opt const CMediaType *pmt,\r
+    IEnumMediaTypes *pEnum)\r
+{\r
+    /* Reset the current enumerator position */\r
+\r
+    HRESULT hr = pEnum->Reset();\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    CMediaType *pMediaType = NULL;\r
+    ULONG ulMediaCount = 0;\r
+\r
+    // attempt to remember a specific error code if there is one\r
+    HRESULT hrFailure = S_OK;\r
+\r
+    for (;;) {\r
+\r
+        /* Retrieve the next media type NOTE each time round the loop the\r
+           enumerator interface will allocate another AM_MEDIA_TYPE structure\r
+           If we are successful then we copy it into our output object, if\r
+           not then we must delete the memory allocated before returning */\r
+\r
+        hr = pEnum->Next(1, (AM_MEDIA_TYPE**)&pMediaType,&ulMediaCount);\r
+        if (hr != S_OK) {\r
+            if (S_OK == hrFailure) {\r
+                hrFailure = VFW_E_NO_ACCEPTABLE_TYPES;\r
+            }\r
+            return hrFailure;\r
+        }\r
+\r
+\r
+        ASSERT(ulMediaCount == 1);\r
+        ASSERT(pMediaType);\r
+\r
+        // check that this matches the partial type (if any)\r
+\r
+        if (pMediaType &&\r
+            ((pmt == NULL) ||\r
+            pMediaType->MatchesPartial(pmt))) {\r
+\r
+            hr = AttemptConnection(pReceivePin, pMediaType);\r
+\r
+            // attempt to remember a specific error code\r
+            if (FAILED(hr) &&\r
+            SUCCEEDED(hrFailure) &&\r
+            (hr != E_FAIL) &&\r
+            (hr != E_INVALIDARG) &&\r
+            (hr != VFW_E_TYPE_NOT_ACCEPTED)) {\r
+                hrFailure = hr;\r
+            }\r
+        } else {\r
+            hr = VFW_E_NO_ACCEPTABLE_TYPES;\r
+        }\r
+\r
+        if(pMediaType) {\r
+            DeleteMediaType(pMediaType);\r
+            pMediaType = NULL;\r
+        }\r
+\r
+        if (S_OK == hr) {\r
+            return hr;\r
+        }\r
+    }\r
+}\r
+\r
+\r
+/* This is called to make the connection, including the taask of finding\r
+   a media type for the pin connection. pmt is the proposed media type\r
+   from the Connect call: if this is fully specified, we will try that.\r
+   Otherwise we enumerate and try all the input pin's types first and\r
+   if that fails we then enumerate and try all our preferred media types.\r
+   For each media type we check it against pmt (if non-null and partially\r
+   specified) as well as checking that both pins will accept it.\r
+ */\r
+\r
+HRESULT CBasePin::AgreeMediaType(\r
+    IPin *pReceivePin,\r
+    const CMediaType *pmt)\r
+{\r
+    ASSERT(pReceivePin);\r
+    IEnumMediaTypes *pEnumMediaTypes = NULL;\r
+\r
+    // if the media type is fully specified then use that\r
+    if ( (pmt != NULL) && (!pmt->IsPartiallySpecified())) {\r
+\r
+        // if this media type fails, then we must fail the connection\r
+        // since if pmt is nonnull we are only allowed to connect\r
+        // using a type that matches it.\r
+\r
+        return AttemptConnection(pReceivePin, pmt);\r
+    }\r
+\r
+\r
+    /* Try the other pin's enumerator */\r
+\r
+    HRESULT hrFailure = VFW_E_NO_ACCEPTABLE_TYPES;\r
+\r
+    for (int i = 0; i < 2; i++) {\r
+        HRESULT hr;\r
+        if (i == (int)m_bTryMyTypesFirst) {\r
+            hr = pReceivePin->EnumMediaTypes(&pEnumMediaTypes);\r
+        } else {\r
+            hr = EnumMediaTypes(&pEnumMediaTypes);\r
+        }\r
+        if (SUCCEEDED(hr)) {\r
+            ASSERT(pEnumMediaTypes);\r
+            hr = TryMediaTypes(pReceivePin,pmt,pEnumMediaTypes);\r
+            pEnumMediaTypes->Release();\r
+            if (SUCCEEDED(hr)) {\r
+                return NOERROR;\r
+            } else {\r
+                // try to remember specific error codes if there are any\r
+                if ((hr != E_FAIL) &&\r
+                    (hr != E_INVALIDARG) &&\r
+                    (hr != VFW_E_TYPE_NOT_ACCEPTED)) {\r
+                    hrFailure = hr;\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    return hrFailure;\r
+}\r
+\r
+\r
+/* Called when we want to complete a connection to another filter. Failing\r
+   this will also fail the connection and disconnect the other pin as well */\r
+\r
+HRESULT\r
+CBasePin::CompleteConnect(IPin *pReceivePin)\r
+{\r
+    UNREFERENCED_PARAMETER(pReceivePin);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+/* This is called to set the format for a pin connection - CheckMediaType\r
+   will have been called to check the connection format and if it didn't\r
+   return an error code then this (virtual) function will be invoked */\r
+\r
+HRESULT\r
+CBasePin::SetMediaType(const CMediaType *pmt)\r
+{\r
+    HRESULT hr = m_mt.Set(*pmt);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+/* This is called during Connect() to provide a virtual method that can do\r
+   any specific check needed for connection such as QueryInterface. This\r
+   base class method just checks that the pin directions don't match */\r
+\r
+HRESULT\r
+CBasePin::CheckConnect(IPin * pPin)\r
+{\r
+    /* Check that pin directions DONT match */\r
+\r
+    PIN_DIRECTION pd;\r
+    pPin->QueryDirection(&pd);\r
+\r
+    ASSERT((pd == PINDIR_OUTPUT) || (pd == PINDIR_INPUT));\r
+    ASSERT((m_dir == PINDIR_OUTPUT) || (m_dir == PINDIR_INPUT));\r
+\r
+    // we should allow for non-input and non-output connections?\r
+    if (pd == m_dir) {\r
+        return VFW_E_INVALID_DIRECTION;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+/* This is called when we realise we can't make a connection to the pin and\r
+   must undo anything we did in CheckConnect - override to release QIs done */\r
+\r
+HRESULT\r
+CBasePin::BreakConnect()\r
+{\r
+    return NOERROR;\r
+}\r
+\r
+\r
+/* Called normally by an output pin on an input pin to try and establish a\r
+   connection.\r
+*/\r
+\r
+STDMETHODIMP\r
+CBasePin::ReceiveConnection(\r
+    IPin * pConnector,   // this is the pin who we will connect to\r
+    const AM_MEDIA_TYPE *pmt  // this is the media type we will exchange\r
+)\r
+{\r
+    CheckPointer(pConnector,E_POINTER);\r
+    CheckPointer(pmt,E_POINTER);\r
+    ValidateReadPtr(pConnector,sizeof(IPin));\r
+    ValidateReadPtr(pmt,sizeof(AM_MEDIA_TYPE));\r
+    CAutoLock cObjectLock(m_pLock);\r
+\r
+    /* Are we already connected */\r
+    if (m_Connected) {\r
+        return VFW_E_ALREADY_CONNECTED;\r
+    }\r
+\r
+    /* See if the filter is active */\r
+    if (!IsStopped() && !m_bCanReconnectWhenActive) {\r
+        return VFW_E_NOT_STOPPED;\r
+    }\r
+\r
+    HRESULT hr = CheckConnect(pConnector);\r
+    if (FAILED(hr)) {\r
+        // Since the procedure is already returning an error code, there\r
+        // is nothing else this function can do to report the error.\r
+        EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );\r
+\r
+#ifdef DXMPERF\r
+        PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );\r
+#endif // DXMPERF\r
+\r
+        return hr;\r
+    }\r
+\r
+    /* Ask derived class if this media type is ok */\r
+\r
+    CMediaType * pcmt = (CMediaType*) pmt;\r
+    hr = CheckMediaType(pcmt);\r
+    if (hr != NOERROR) {\r
+        // no -we don't support this media type\r
+\r
+        // Since the procedure is already returning an error code, there\r
+        // is nothing else this function can do to report the error.\r
+        EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );\r
+\r
+        // return a specific media type error if there is one\r
+        // or map a general failure code to something more helpful\r
+        // (in particular S_FALSE gets changed to an error code)\r
+        if (SUCCEEDED(hr) ||\r
+            (hr == E_FAIL) ||\r
+            (hr == E_INVALIDARG)) {\r
+            hr = VFW_E_TYPE_NOT_ACCEPTED;\r
+        }\r
+\r
+#ifdef DXMPERF\r
+        PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );\r
+#endif // DXMPERF\r
+\r
+        return hr;\r
+    }\r
+\r
+    /* Complete the connection */\r
+\r
+    m_Connected = pConnector;\r
+    m_Connected->AddRef();\r
+    hr = SetMediaType(pcmt);\r
+    if (SUCCEEDED(hr)) {\r
+        hr = CompleteConnect(pConnector);\r
+        if (SUCCEEDED(hr)) {\r
+\r
+#ifdef DXMPERF\r
+            PERFLOG_RXCONNECT( pConnector, (IPin *) this, NOERROR, pmt );\r
+#endif // DXMPERF\r
+\r
+            return NOERROR;\r
+        }\r
+    }\r
+\r
+    DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Failed to set the media type or failed to complete the connection.")));\r
+    m_Connected->Release();\r
+    m_Connected = NULL;\r
+\r
+    // Since the procedure is already returning an error code, there\r
+    // is nothing else this function can do to report the error.\r
+    EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );\r
+\r
+#ifdef DXMPERF\r
+    PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );\r
+#endif // DXMPERF\r
+\r
+    return hr;\r
+}\r
+\r
+\r
+/* Called when we want to terminate a pin connection */\r
+\r
+STDMETHODIMP\r
+CBasePin::Disconnect()\r
+{\r
+    CAutoLock cObjectLock(m_pLock);\r
+\r
+    /* See if the filter is active */\r
+    if (!IsStopped()) {\r
+        return VFW_E_NOT_STOPPED;\r
+    }\r
+\r
+    return DisconnectInternal();\r
+}\r
+\r
+STDMETHODIMP\r
+CBasePin::DisconnectInternal()\r
+{\r
+    ASSERT(CritCheckIn(m_pLock));\r
+\r
+    if (m_Connected) {\r
+        HRESULT hr = BreakConnect();\r
+        if( FAILED( hr ) ) {\r
+\r
+#ifdef DXMPERF\r
+            PERFLOG_DISCONNECT( (IPin *) this, m_Connected, hr );\r
+#endif // DXMPERF\r
+\r
+            // There is usually a bug in the program if BreakConnect() fails.\r
+            DbgBreak( "WARNING: BreakConnect() failed in CBasePin::Disconnect()." );\r
+            return hr;\r
+        }\r
+\r
+        m_Connected->Release();\r
+        m_Connected = NULL;\r
+\r
+#ifdef DXMPERF\r
+        PERFLOG_DISCONNECT( (IPin *) this, m_Connected, S_OK );\r
+#endif // DXMPERF\r
+\r
+        return S_OK;\r
+    } else {\r
+        // no connection - not an error\r
+\r
+#ifdef DXMPERF\r
+        PERFLOG_DISCONNECT( (IPin *) this, m_Connected, S_FALSE );\r
+#endif // DXMPERF\r
+\r
+        return S_FALSE;\r
+    }\r
+}\r
+\r
+\r
+/* Return an AddRef()'d pointer to the connected pin if there is one */\r
+STDMETHODIMP\r
+CBasePin::ConnectedTo(\r
+    __deref_out IPin **ppPin\r
+)\r
+{\r
+    CheckPointer(ppPin,E_POINTER);\r
+    ValidateReadWritePtr(ppPin,sizeof(IPin *));\r
+    //\r
+    //  It's pointless to lock here.\r
+    //  The caller should ensure integrity.\r
+    //\r
+\r
+    IPin *pPin = m_Connected;\r
+    *ppPin = pPin;\r
+    if (pPin != NULL) {\r
+        pPin->AddRef();\r
+        return S_OK;\r
+    } else {\r
+        ASSERT(*ppPin == NULL);\r
+        return VFW_E_NOT_CONNECTED;\r
+    }\r
+}\r
+\r
+/* Return the media type of the connection */\r
+STDMETHODIMP\r
+CBasePin::ConnectionMediaType(\r
+    __out AM_MEDIA_TYPE *pmt\r
+)\r
+{\r
+    CheckPointer(pmt,E_POINTER);\r
+    ValidateReadWritePtr(pmt,sizeof(AM_MEDIA_TYPE));\r
+    CAutoLock cObjectLock(m_pLock);\r
+\r
+    /*  Copy constructor of m_mt allocates the memory */\r
+    if (IsConnected()) {\r
+        CopyMediaType( pmt, &m_mt );\r
+        return S_OK;\r
+    } else {\r
+        ((CMediaType *)pmt)->InitMediaType();\r
+        return VFW_E_NOT_CONNECTED;\r
+    }\r
+}\r
+\r
+/* Return information about the filter we are connect to */\r
+\r
+STDMETHODIMP\r
+CBasePin::QueryPinInfo(\r
+    __out PIN_INFO * pInfo\r
+)\r
+{\r
+    CheckPointer(pInfo,E_POINTER);\r
+    ValidateReadWritePtr(pInfo,sizeof(PIN_INFO));\r
+\r
+    pInfo->pFilter = m_pFilter;\r
+    if (m_pFilter) {\r
+        m_pFilter->AddRef();\r
+    }\r
+\r
+    if (m_pName) {\r
+        (void)StringCchCopyW(pInfo->achName, NUMELMS(pInfo->achName), m_pName);\r
+    } else {\r
+        pInfo->achName[0] = L'\0';\r
+    }\r
+\r
+    pInfo->dir = m_dir;\r
+\r
+    return NOERROR;\r
+}\r
+\r
+STDMETHODIMP\r
+CBasePin::QueryDirection(\r
+    __out PIN_DIRECTION * pPinDir\r
+)\r
+{\r
+    CheckPointer(pPinDir,E_POINTER);\r
+    ValidateReadWritePtr(pPinDir,sizeof(PIN_DIRECTION));\r
+\r
+    *pPinDir = m_dir;\r
+    return NOERROR;\r
+}\r
+\r
+// Default QueryId to return the pin's name\r
+STDMETHODIMP\r
+CBasePin::QueryId(\r
+    __deref_out LPWSTR * Id\r
+)\r
+{\r
+    //  We're not going away because someone's got a pointer to us\r
+    //  so there's no need to lock\r
+\r
+    return AMGetWideString(Name(), Id);\r
+}\r
+\r
+/* Does this pin support this media type WARNING this interface function does\r
+   not lock the main object as it is meant to be asynchronous by nature - if\r
+   the media types you support depend on some internal state that is updated\r
+   dynamically then you will need to implement locking in a derived class */\r
+\r
+STDMETHODIMP\r
+CBasePin::QueryAccept(\r
+    const AM_MEDIA_TYPE *pmt\r
+)\r
+{\r
+    CheckPointer(pmt,E_POINTER);\r
+    ValidateReadPtr(pmt,sizeof(AM_MEDIA_TYPE));\r
+\r
+    /* The CheckMediaType method is valid to return error codes if the media\r
+       type is horrible, an example might be E_INVALIDARG. What we do here\r
+       is map all the error codes into either S_OK or S_FALSE regardless */\r
+\r
+    HRESULT hr = CheckMediaType((CMediaType*)pmt);\r
+    if (FAILED(hr)) {\r
+        return S_FALSE;\r
+    }\r
+    // note that the only defined success codes should be S_OK and S_FALSE...\r
+    return hr;\r
+}\r
+\r
+\r
+/* This can be called to return an enumerator for the pin's list of preferred\r
+   media types. An input pin is not obliged to have any preferred formats\r
+   although it can do. For example, the window renderer has a preferred type\r
+   which describes a video image that matches the current window size. All\r
+   output pins should expose at least one preferred format otherwise it is\r
+   possible that neither pin has any types and so no connection is possible */\r
+\r
+STDMETHODIMP\r
+CBasePin::EnumMediaTypes(\r
+    __deref_out IEnumMediaTypes **ppEnum\r
+)\r
+{\r
+    CheckPointer(ppEnum,E_POINTER);\r
+    ValidateReadWritePtr(ppEnum,sizeof(IEnumMediaTypes *));\r
+\r
+    /* Create a new ref counted enumerator */\r
+\r
+    *ppEnum = new CEnumMediaTypes(this,\r
+                              NULL);\r
+\r
+    if (*ppEnum == NULL) {\r
+        return E_OUTOFMEMORY;\r
+    }\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+\r
+/* This is a virtual function that returns a media type corresponding with\r
+   place iPosition in the list. This base class simply returns an error as\r
+   we support no media types by default but derived classes should override */\r
+\r
+HRESULT CBasePin::GetMediaType(int iPosition, __inout CMediaType *pMediaType)\r
+{\r
+    UNREFERENCED_PARAMETER(iPosition);\r
+    UNREFERENCED_PARAMETER(pMediaType);\r
+    return E_UNEXPECTED;\r
+}\r
+\r
+\r
+/* This is a virtual function that returns the current media type version.\r
+   The base class initialises the media type enumerators with the value 1\r
+   By default we always returns that same value. A Derived class may change\r
+   the list of media types available and after doing so it should increment\r
+   the version either in a method derived from this, or more simply by just\r
+   incrementing the m_TypeVersion base pin variable. The type enumerators\r
+   call this when they want to see if their enumerations are out of date */\r
+\r
+LONG CBasePin::GetMediaTypeVersion()\r
+{\r
+    return m_TypeVersion;\r
+}\r
+\r
+\r
+/* Increment the cookie representing the current media type version */\r
+\r
+void CBasePin::IncrementTypeVersion()\r
+{\r
+    InterlockedIncrement(&m_TypeVersion);\r
+}\r
+\r
+\r
+/* Called by IMediaFilter implementation when the state changes from Stopped\r
+   to either paused or running and in derived classes could do things like\r
+   commit memory and grab hardware resource (the default is to do nothing) */\r
+\r
+HRESULT\r
+CBasePin::Active(void)\r
+{\r
+    return NOERROR;\r
+}\r
+\r
+/* Called by IMediaFilter implementation when the state changes from\r
+   to either paused to running and in derived classes could do things like\r
+   commit memory and grab hardware resource (the default is to do nothing) */\r
+\r
+HRESULT\r
+CBasePin::Run(REFERENCE_TIME tStart)\r
+{\r
+    UNREFERENCED_PARAMETER(tStart);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+/* Also called by the IMediaFilter implementation when the state changes to\r
+   Stopped at which point you should decommit allocators and free hardware\r
+   resources you grabbed in the Active call (default is also to do nothing) */\r
+\r
+HRESULT\r
+CBasePin::Inactive(void)\r
+{\r
+    m_bRunTimeError = FALSE;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Called when no more data will arrive\r
+STDMETHODIMP\r
+CBasePin::EndOfStream(void)\r
+{\r
+    return S_OK;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CBasePin::SetSink(IQualityControl * piqc)\r
+{\r
+    CAutoLock cObjectLock(m_pLock);\r
+    if (piqc) ValidateReadPtr(piqc,sizeof(IQualityControl));\r
+    m_pQSink = piqc;\r
+    return NOERROR;\r
+} // SetSink\r
+\r
+\r
+STDMETHODIMP\r
+CBasePin::Notify(IBaseFilter * pSender, Quality q)\r
+{\r
+    UNREFERENCED_PARAMETER(q);\r
+    UNREFERENCED_PARAMETER(pSender);\r
+    DbgBreak("IQualityControl::Notify not over-ridden from CBasePin.  (IGNORE is OK)");\r
+    return E_NOTIMPL;\r
+} //Notify\r
+\r
+\r
+// NewSegment notifies of the start/stop/rate applying to the data\r
+// about to be received. Default implementation records data and\r
+// returns S_OK.\r
+// Override this to pass downstream.\r
+STDMETHODIMP\r
+CBasePin::NewSegment(\r
+                REFERENCE_TIME tStart,\r
+                REFERENCE_TIME tStop,\r
+                double dRate)\r
+{\r
+    m_tStart = tStart;\r
+    m_tStop = tStop;\r
+    m_dRate = dRate;\r
+\r
+    return S_OK;\r
+}\r
+\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Implements CBaseOutputPin\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+\r
+CBaseOutputPin::CBaseOutputPin(__in_opt LPCTSTR pObjectName,\r
+                   __in CBaseFilter *pFilter,\r
+                   __in CCritSec *pLock,\r
+                   __inout HRESULT *phr,\r
+                   __in_opt LPCWSTR pName) :\r
+    CBasePin(pObjectName, pFilter, pLock, phr, pName, PINDIR_OUTPUT),\r
+    m_pAllocator(NULL),\r
+    m_pInputPin(NULL)\r
+{\r
+    ASSERT(pFilter);\r
+}\r
+\r
+#ifdef UNICODE\r
+CBaseOutputPin::CBaseOutputPin(__in_opt LPCSTR pObjectName,\r
+                   __in CBaseFilter *pFilter,\r
+                   __in CCritSec *pLock,\r
+                   __inout HRESULT *phr,\r
+                   __in_opt LPCWSTR pName) :\r
+    CBasePin(pObjectName, pFilter, pLock, phr, pName, PINDIR_OUTPUT),\r
+    m_pAllocator(NULL),\r
+    m_pInputPin(NULL)\r
+{\r
+    ASSERT(pFilter);\r
+}\r
+#endif\r
+\r
+/*   This is called after a media type has been proposed\r
+\r
+     Try to complete the connection by agreeing the allocator\r
+*/\r
+HRESULT\r
+CBaseOutputPin::CompleteConnect(IPin *pReceivePin)\r
+{\r
+    UNREFERENCED_PARAMETER(pReceivePin);\r
+    return DecideAllocator(m_pInputPin, &m_pAllocator);\r
+}\r
+\r
+\r
+/* This method is called when the output pin is about to try and connect to\r
+   an input pin. It is at this point that you should try and grab any extra\r
+   interfaces that you need, in this case IMemInputPin. Because this is\r
+   only called if we are not currently connected we do NOT need to call\r
+   BreakConnect. This also makes it easier to derive classes from us as\r
+   BreakConnect is only called when we actually have to break a connection\r
+   (or a partly made connection) and not when we are checking a connection */\r
+\r
+/* Overriden from CBasePin */\r
+\r
+HRESULT\r
+CBaseOutputPin::CheckConnect(IPin * pPin)\r
+{\r
+    HRESULT hr = CBasePin::CheckConnect(pPin);\r
+    if (FAILED(hr)) {\r
+    return hr;\r
+    }\r
+\r
+    // get an input pin and an allocator interface\r
+    hr = pPin->QueryInterface(IID_IMemInputPin, (void **) &m_pInputPin);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+/* Overriden from CBasePin */\r
+\r
+HRESULT\r
+CBaseOutputPin::BreakConnect()\r
+{\r
+    /* Release any allocator we hold */\r
+\r
+    if (m_pAllocator) {\r
+        // Always decommit the allocator because a downstream filter may or\r
+        // may not decommit the connection's allocator.  A memory leak could\r
+        // occur if the allocator is not decommited when a connection is broken.\r
+        HRESULT hr = m_pAllocator->Decommit();\r
+        if( FAILED( hr ) ) {\r
+            return hr;\r
+        }\r
+\r
+        m_pAllocator->Release();\r
+        m_pAllocator = NULL;\r
+    }\r
+\r
+    /* Release any input pin interface we hold */\r
+\r
+    if (m_pInputPin) {\r
+        m_pInputPin->Release();\r
+        m_pInputPin = NULL;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+/* This is called when the input pin didn't give us a valid allocator */\r
+\r
+HRESULT\r
+CBaseOutputPin::InitAllocator(__deref_out IMemAllocator **ppAlloc)\r
+{\r
+    return CreateMemoryAllocator(ppAlloc);\r
+}\r
+\r
+\r
+/* Decide on an allocator, override this if you want to use your own allocator\r
+   Override DecideBufferSize to call SetProperties. If the input pin fails\r
+   the GetAllocator call then this will construct a CMemAllocator and call\r
+   DecideBufferSize on that, and if that fails then we are completely hosed.\r
+   If the you succeed the DecideBufferSize call, we will notify the input\r
+   pin of the selected allocator. NOTE this is called during Connect() which\r
+   therefore looks after grabbing and locking the object's critical section */\r
+\r
+// We query the input pin for its requested properties and pass this to\r
+// DecideBufferSize to allow it to fulfill requests that it is happy\r
+// with (eg most people don't care about alignment and are thus happy to\r
+// use the downstream pin's alignment request).\r
+\r
+HRESULT\r
+CBaseOutputPin::DecideAllocator(IMemInputPin *pPin, __deref_out IMemAllocator **ppAlloc)\r
+{\r
+    HRESULT hr = NOERROR;\r
+    *ppAlloc = NULL;\r
+\r
+    // get downstream prop request\r
+    // the derived class may modify this in DecideBufferSize, but\r
+    // we assume that he will consistently modify it the same way,\r
+    // so we only get it once\r
+    ALLOCATOR_PROPERTIES prop;\r
+    ZeroMemory(&prop, sizeof(prop));\r
+\r
+    // whatever he returns, we assume prop is either all zeros\r
+    // or he has filled it out.\r
+    pPin->GetAllocatorRequirements(&prop);\r
+\r
+    // if he doesn't care about alignment, then set it to 1\r
+    if (prop.cbAlign == 0) {\r
+        prop.cbAlign = 1;\r
+    }\r
+\r
+    /* Try the allocator provided by the input pin */\r
+\r
+    hr = pPin->GetAllocator(ppAlloc);\r
+    if (SUCCEEDED(hr)) {\r
+\r
+        hr = DecideBufferSize(*ppAlloc, &prop);\r
+        if (SUCCEEDED(hr)) {\r
+            hr = pPin->NotifyAllocator(*ppAlloc, FALSE);\r
+            if (SUCCEEDED(hr)) {\r
+                return NOERROR;\r
+            }\r
+        }\r
+    }\r
+\r
+    /* If the GetAllocator failed we may not have an interface */\r
+\r
+    if (*ppAlloc) {\r
+        (*ppAlloc)->Release();\r
+        *ppAlloc = NULL;\r
+    }\r
+\r
+    /* Try the output pin's allocator by the same method */\r
+\r
+    hr = InitAllocator(ppAlloc);\r
+    if (SUCCEEDED(hr)) {\r
+\r
+        // note - the properties passed here are in the same\r
+        // structure as above and may have been modified by\r
+        // the previous call to DecideBufferSize\r
+        hr = DecideBufferSize(*ppAlloc, &prop);\r
+        if (SUCCEEDED(hr)) {\r
+            hr = pPin->NotifyAllocator(*ppAlloc, FALSE);\r
+            if (SUCCEEDED(hr)) {\r
+                return NOERROR;\r
+            }\r
+        }\r
+    }\r
+\r
+    /* Likewise we may not have an interface to release */\r
+\r
+    if (*ppAlloc) {\r
+        (*ppAlloc)->Release();\r
+        *ppAlloc = NULL;\r
+    }\r
+    return hr;\r
+}\r
+\r
+\r
+/* This returns an empty sample buffer from the allocator WARNING the same\r
+   dangers and restrictions apply here as described below for Deliver() */\r
+\r
+HRESULT\r
+CBaseOutputPin::GetDeliveryBuffer(__deref_out IMediaSample ** ppSample,\r
+                                  __in_opt REFERENCE_TIME * pStartTime,\r
+                                  __in_opt REFERENCE_TIME * pEndTime,\r
+                                  DWORD dwFlags)\r
+{\r
+    if (m_pAllocator != NULL) {\r
+        return m_pAllocator->GetBuffer(ppSample,pStartTime,pEndTime,dwFlags);\r
+    } else {\r
+        return E_NOINTERFACE;\r
+    }\r
+}\r
+\r
+\r
+/* Deliver a filled-in sample to the connected input pin. NOTE the object must\r
+   have locked itself before calling us otherwise we may get halfway through\r
+   executing this method only to find the filter graph has got in and\r
+   disconnected us from the input pin. If the filter has no worker threads\r
+   then the lock is best applied on Receive(), otherwise it should be done\r
+   when the worker thread is ready to deliver. There is a wee snag to worker\r
+   threads that this shows up. The worker thread must lock the object when\r
+   it is ready to deliver a sample, but it may have to wait until a state\r
+   change has completed, but that may never complete because the state change\r
+   is waiting for the worker thread to complete. The way to handle this is for\r
+   the state change code to grab the critical section, then set an abort event\r
+   for the worker thread, then release the critical section and wait for the\r
+   worker thread to see the event we set and then signal that it has finished\r
+   (with another event). At which point the state change code can complete */\r
+\r
+// note (if you've still got any breath left after reading that) that you\r
+// need to release the sample yourself after this call. if the connected\r
+// input pin needs to hold onto the sample beyond the call, it will addref\r
+// the sample itself.\r
+\r
+// of course you must release this one and call GetDeliveryBuffer for the\r
+// next. You cannot reuse it directly.\r
+\r
+HRESULT\r
+CBaseOutputPin::Deliver(IMediaSample * pSample)\r
+{\r
+    if (m_pInputPin == NULL) {\r
+        return VFW_E_NOT_CONNECTED;\r
+    }\r
+\r
+#ifdef DXMPERF\r
+    PERFLOG_DELIVER( m_pName ? m_pName : L"CBaseOutputPin", (IPin *) this, (IPin  *) m_pInputPin, pSample, &m_mt );\r
+#endif // DXMPERF\r
+\r
+    return m_pInputPin->Receive(pSample);\r
+}\r
+\r
+\r
+// called from elsewhere in our filter to pass EOS downstream to\r
+// our connected input pin\r
+HRESULT\r
+CBaseOutputPin::DeliverEndOfStream(void)\r
+{\r
+    // remember this is on IPin not IMemInputPin\r
+    if (m_Connected == NULL) {\r
+        return VFW_E_NOT_CONNECTED;\r
+    }\r
+    return m_Connected->EndOfStream();\r
+}\r
+\r
+\r
+/* Commit the allocator's memory, this is called through IMediaFilter\r
+   which is responsible for locking the object before calling us */\r
+\r
+HRESULT\r
+CBaseOutputPin::Active(void)\r
+{\r
+    if (m_pAllocator == NULL) {\r
+        return VFW_E_NO_ALLOCATOR;\r
+    }\r
+    return m_pAllocator->Commit();\r
+}\r
+\r
+\r
+/* Free up or unprepare allocator's memory, this is called through\r
+   IMediaFilter which is responsible for locking the object first */\r
+\r
+HRESULT\r
+CBaseOutputPin::Inactive(void)\r
+{\r
+    m_bRunTimeError = FALSE;\r
+    if (m_pAllocator == NULL) {\r
+        return VFW_E_NO_ALLOCATOR;\r
+    }\r
+    return m_pAllocator->Decommit();\r
+}\r
+\r
+// we have a default handling of EndOfStream which is to return\r
+// an error, since this should be called on input pins only\r
+STDMETHODIMP\r
+CBaseOutputPin::EndOfStream(void)\r
+{\r
+    return E_UNEXPECTED;\r
+}\r
+\r
+\r
+// BeginFlush should be called on input pins only\r
+STDMETHODIMP\r
+CBaseOutputPin::BeginFlush(void)\r
+{\r
+    return E_UNEXPECTED;\r
+}\r
+\r
+// EndFlush should be called on input pins only\r
+STDMETHODIMP\r
+CBaseOutputPin::EndFlush(void)\r
+{\r
+    return E_UNEXPECTED;\r
+}\r
+\r
+// call BeginFlush on the connected input pin\r
+HRESULT\r
+CBaseOutputPin::DeliverBeginFlush(void)\r
+{\r
+    // remember this is on IPin not IMemInputPin\r
+    if (m_Connected == NULL) {\r
+        return VFW_E_NOT_CONNECTED;\r
+    }\r
+    return m_Connected->BeginFlush();\r
+}\r
+\r
+// call EndFlush on the connected input pin\r
+HRESULT\r
+CBaseOutputPin::DeliverEndFlush(void)\r
+{\r
+    // remember this is on IPin not IMemInputPin\r
+    if (m_Connected == NULL) {\r
+        return VFW_E_NOT_CONNECTED;\r
+    }\r
+    return m_Connected->EndFlush();\r
+}\r
+// deliver NewSegment to connected pin\r
+HRESULT\r
+CBaseOutputPin::DeliverNewSegment(\r
+    REFERENCE_TIME tStart,\r
+    REFERENCE_TIME tStop,\r
+    double dRate)\r
+{\r
+    if (m_Connected == NULL) {\r
+        return VFW_E_NOT_CONNECTED;\r
+    }\r
+    return m_Connected->NewSegment(tStart, tStop, dRate);\r
+}\r
+\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Implements CBaseInputPin\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+\r
+/* Constructor creates a default allocator object */\r
+\r
+CBaseInputPin::CBaseInputPin(__in_opt LPCTSTR pObjectName,\r
+                 __in CBaseFilter *pFilter,\r
+                 __in CCritSec *pLock,\r
+                 __inout HRESULT *phr,\r
+                 __in_opt LPCWSTR pPinName) :\r
+    CBasePin(pObjectName, pFilter, pLock, phr, pPinName, PINDIR_INPUT),\r
+    m_pAllocator(NULL),\r
+    m_bReadOnly(FALSE),\r
+    m_bFlushing(FALSE)\r
+{\r
+    ZeroMemory(&m_SampleProps, sizeof(m_SampleProps));\r
+}\r
+\r
+#ifdef UNICODE\r
+CBaseInputPin::CBaseInputPin(__in LPCSTR pObjectName,\r
+                 __in CBaseFilter *pFilter,\r
+                 __in CCritSec *pLock,\r
+                 __inout HRESULT *phr,\r
+                 __in_opt LPCWSTR pPinName) :\r
+    CBasePin(pObjectName, pFilter, pLock, phr, pPinName, PINDIR_INPUT),\r
+    m_pAllocator(NULL),\r
+    m_bReadOnly(FALSE),\r
+    m_bFlushing(FALSE)\r
+{\r
+    ZeroMemory(&m_SampleProps, sizeof(m_SampleProps));\r
+}\r
+#endif\r
+\r
+/* Destructor releases it's reference count on the default allocator */\r
+\r
+CBaseInputPin::~CBaseInputPin()\r
+{\r
+    if (m_pAllocator != NULL) {\r
+    m_pAllocator->Release();\r
+    m_pAllocator = NULL;\r
+    }\r
+}\r
+\r
+\r
+// override this to publicise our interfaces\r
+STDMETHODIMP\r
+CBaseInputPin::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    /* Do we know about this interface */\r
+\r
+    if (riid == IID_IMemInputPin) {\r
+        return GetInterface((IMemInputPin *) this, ppv);\r
+    } else {\r
+        return CBasePin::NonDelegatingQueryInterface(riid, ppv);\r
+    }\r
+}\r
+\r
+\r
+/* Return the allocator interface that this input pin would like the output\r
+   pin to use. NOTE subsequent calls to GetAllocator should all return an\r
+   interface onto the SAME object so we create one object at the start\r
+\r
+   Note:\r
+       The allocator is Release()'d on disconnect and replaced on\r
+       NotifyAllocator().\r
+\r
+   Override this to provide your own allocator.\r
+*/\r
+\r
+STDMETHODIMP\r
+CBaseInputPin::GetAllocator(\r
+    __deref_out IMemAllocator **ppAllocator)\r
+{\r
+    CheckPointer(ppAllocator,E_POINTER);\r
+    ValidateReadWritePtr(ppAllocator,sizeof(IMemAllocator *));\r
+    CAutoLock cObjectLock(m_pLock);\r
+\r
+    if (m_pAllocator == NULL) {\r
+        HRESULT hr = CreateMemoryAllocator(&m_pAllocator);\r
+        if (FAILED(hr)) {\r
+            return hr;\r
+        }\r
+    }\r
+    ASSERT(m_pAllocator != NULL);\r
+    *ppAllocator = m_pAllocator;\r
+    m_pAllocator->AddRef();\r
+    return NOERROR;\r
+}\r
+\r
+\r
+/* Tell the input pin which allocator the output pin is actually going to use\r
+   Override this if you care - NOTE the locking we do both here and also in\r
+   GetAllocator is unnecessary but derived classes that do something useful\r
+   will undoubtedly have to lock the object so this might help remind people */\r
+\r
+STDMETHODIMP\r
+CBaseInputPin::NotifyAllocator(\r
+    IMemAllocator * pAllocator,\r
+    BOOL bReadOnly)\r
+{\r
+    CheckPointer(pAllocator,E_POINTER);\r
+    ValidateReadPtr(pAllocator,sizeof(IMemAllocator));\r
+    CAutoLock cObjectLock(m_pLock);\r
+\r
+    IMemAllocator *pOldAllocator = m_pAllocator;\r
+    pAllocator->AddRef();\r
+    m_pAllocator = pAllocator;\r
+\r
+    if (pOldAllocator != NULL) {\r
+        pOldAllocator->Release();\r
+    }\r
+\r
+    // the readonly flag indicates whether samples from this allocator should\r
+    // be regarded as readonly - if true, then inplace transforms will not be\r
+    // allowed.\r
+    m_bReadOnly = (BYTE)bReadOnly;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+HRESULT\r
+CBaseInputPin::BreakConnect()\r
+{\r
+    /* We don't need our allocator any more */\r
+    if (m_pAllocator) {\r
+        // Always decommit the allocator because a downstream filter may or\r
+        // may not decommit the connection's allocator.  A memory leak could\r
+        // occur if the allocator is not decommited when a pin is disconnected.\r
+        HRESULT hr = m_pAllocator->Decommit();\r
+        if( FAILED( hr ) ) {\r
+            return hr;\r
+        }\r
+\r
+        m_pAllocator->Release();\r
+        m_pAllocator = NULL;\r
+    }\r
+\r
+    return S_OK;\r
+}\r
+\r
+\r
+/* Do something with this media sample - this base class checks to see if the\r
+   format has changed with this media sample and if so checks that the filter\r
+   will accept it, generating a run time error if not. Once we have raised a\r
+   run time error we set a flag so that no more samples will be accepted\r
+\r
+   It is important that any filter should override this method and implement\r
+   synchronization so that samples are not processed when the pin is\r
+   disconnected etc\r
+*/\r
+\r
+STDMETHODIMP\r
+CBaseInputPin::Receive(IMediaSample *pSample)\r
+{\r
+    CheckPointer(pSample,E_POINTER);\r
+    ValidateReadPtr(pSample,sizeof(IMediaSample));\r
+    ASSERT(pSample);\r
+\r
+    HRESULT hr = CheckStreaming();\r
+    if (S_OK != hr) {\r
+        return hr;\r
+    }\r
+\r
+#ifdef DXMPERF\r
+    PERFLOG_RECEIVE( m_pName ? m_pName : L"CBaseInputPin", (IPin *) m_Connected, (IPin *) this, pSample, &m_mt );\r
+#endif // DXMPERF\r
+\r
+\r
+    /* Check for IMediaSample2 */\r
+    IMediaSample2 *pSample2;\r
+    if (SUCCEEDED(pSample->QueryInterface(IID_IMediaSample2, (void **)&pSample2))) {\r
+        hr = pSample2->GetProperties(sizeof(m_SampleProps), (PBYTE)&m_SampleProps);\r
+        pSample2->Release();\r
+        if (FAILED(hr)) {\r
+            return hr;\r
+        }\r
+    } else {\r
+        /*  Get the properties the hard way */\r
+        m_SampleProps.cbData = sizeof(m_SampleProps);\r
+        m_SampleProps.dwTypeSpecificFlags = 0;\r
+        m_SampleProps.dwStreamId = AM_STREAM_MEDIA;\r
+        m_SampleProps.dwSampleFlags = 0;\r
+        if (S_OK == pSample->IsDiscontinuity()) {\r
+            m_SampleProps.dwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY;\r
+        }\r
+        if (S_OK == pSample->IsPreroll()) {\r
+            m_SampleProps.dwSampleFlags |= AM_SAMPLE_PREROLL;\r
+        }\r
+        if (S_OK == pSample->IsSyncPoint()) {\r
+            m_SampleProps.dwSampleFlags |= AM_SAMPLE_SPLICEPOINT;\r
+        }\r
+        if (SUCCEEDED(pSample->GetTime(&m_SampleProps.tStart,\r
+                                       &m_SampleProps.tStop))) {\r
+            m_SampleProps.dwSampleFlags |= AM_SAMPLE_TIMEVALID |\r
+                                           AM_SAMPLE_STOPVALID;\r
+        }\r
+        if (S_OK == pSample->GetMediaType(&m_SampleProps.pMediaType)) {\r
+            m_SampleProps.dwSampleFlags |= AM_SAMPLE_TYPECHANGED;\r
+        }\r
+        pSample->GetPointer(&m_SampleProps.pbBuffer);\r
+        m_SampleProps.lActual = pSample->GetActualDataLength();\r
+        m_SampleProps.cbBuffer = pSample->GetSize();\r
+    }\r
+\r
+    /* Has the format changed in this sample */\r
+\r
+    if (!(m_SampleProps.dwSampleFlags & AM_SAMPLE_TYPECHANGED)) {\r
+        return NOERROR;\r
+    }\r
+\r
+    /* Check the derived class accepts this format */\r
+    /* This shouldn't fail as the source must call QueryAccept first */\r
+\r
+    hr = CheckMediaType((CMediaType *)m_SampleProps.pMediaType);\r
+\r
+    if (hr == NOERROR) {\r
+        return NOERROR;\r
+    }\r
+\r
+    /* Raise a runtime error if we fail the media type */\r
+\r
+    m_bRunTimeError = TRUE;\r
+    EndOfStream();\r
+    m_pFilter->NotifyEvent(EC_ERRORABORT,VFW_E_TYPE_NOT_ACCEPTED,0);\r
+    return VFW_E_INVALIDMEDIATYPE;\r
+}\r
+\r
+\r
+/*  Receive multiple samples */\r
+STDMETHODIMP\r
+CBaseInputPin::ReceiveMultiple (\r
+    __in_ecount(nSamples) IMediaSample **pSamples,\r
+    long nSamples,\r
+    __out long *nSamplesProcessed)\r
+{\r
+    CheckPointer(pSamples,E_POINTER);\r
+    ValidateReadPtr(pSamples,nSamples * sizeof(IMediaSample *));\r
+\r
+    HRESULT hr = S_OK;\r
+    *nSamplesProcessed = 0;\r
+    while (nSamples-- > 0) {\r
+         hr = Receive(pSamples[*nSamplesProcessed]);\r
+\r
+         /*  S_FALSE means don't send any more */\r
+         if (hr != S_OK) {\r
+             break;\r
+         }\r
+         (*nSamplesProcessed)++;\r
+    }\r
+    return hr;\r
+}\r
+\r
+/*  See if Receive() might block */\r
+STDMETHODIMP\r
+CBaseInputPin::ReceiveCanBlock()\r
+{\r
+    /*  Ask all the output pins if they block\r
+        If there are no output pin assume we do block\r
+    */\r
+    int cPins = m_pFilter->GetPinCount();\r
+    int cOutputPins = 0;\r
+    for (int c = 0; c < cPins; c++) {\r
+        CBasePin *pPin = m_pFilter->GetPin(c);\r
+        if (NULL == pPin) {\r
+            break;\r
+        }\r
+        PIN_DIRECTION pd;\r
+        HRESULT hr = pPin->QueryDirection(&pd);\r
+        if (FAILED(hr)) {\r
+            return hr;\r
+        }\r
+\r
+        if (pd == PINDIR_OUTPUT) {\r
+\r
+            IPin *pConnected;\r
+            hr = pPin->ConnectedTo(&pConnected);\r
+            if (SUCCEEDED(hr)) {\r
+                ASSERT(pConnected != NULL);\r
+                cOutputPins++;\r
+                IMemInputPin *pInputPin;\r
+                hr = pConnected->QueryInterface(\r
+                                              IID_IMemInputPin,\r
+                                              (void **)&pInputPin);\r
+                pConnected->Release();\r
+                if (SUCCEEDED(hr)) {\r
+                    hr = pInputPin->ReceiveCanBlock();\r
+                    pInputPin->Release();\r
+                    if (hr != S_FALSE) {\r
+                        return S_OK;\r
+                    }\r
+                } else {\r
+                    /*  There's a transport we don't understand here */\r
+                    return S_OK;\r
+                }\r
+            }\r
+        }\r
+    }\r
+    return cOutputPins == 0 ? S_OK : S_FALSE;\r
+}\r
+\r
+// Default handling for BeginFlush - call at the beginning\r
+// of your implementation (makes sure that all Receive calls\r
+// fail). After calling this, you need to free any queued data\r
+// and then call downstream.\r
+STDMETHODIMP\r
+CBaseInputPin::BeginFlush(void)\r
+{\r
+    //  BeginFlush is NOT synchronized with streaming but is part of\r
+    //  a control action - hence we synchronize with the filter\r
+    CAutoLock lck(m_pLock);\r
+\r
+    // if we are already in mid-flush, this is probably a mistake\r
+    // though not harmful - try to pick it up for now so I can think about it\r
+    ASSERT(!m_bFlushing);\r
+\r
+    // first thing to do is ensure that no further Receive calls succeed\r
+    m_bFlushing = TRUE;\r
+\r
+    // now discard any data and call downstream - must do that\r
+    // in derived classes\r
+    return S_OK;\r
+}\r
+\r
+// default handling for EndFlush - call at end of your implementation\r
+// - before calling this, ensure that there is no queued data and no thread\r
+// pushing any more without a further receive, then call downstream,\r
+// then call this method to clear the m_bFlushing flag and re-enable\r
+// receives\r
+STDMETHODIMP\r
+CBaseInputPin::EndFlush(void)\r
+{\r
+    //  Endlush is NOT synchronized with streaming but is part of\r
+    //  a control action - hence we synchronize with the filter\r
+    CAutoLock lck(m_pLock);\r
+\r
+    // almost certainly a mistake if we are not in mid-flush\r
+    ASSERT(m_bFlushing);\r
+\r
+    // before calling, sync with pushing thread and ensure\r
+    // no more data is going downstream, then call EndFlush on\r
+    // downstream pins.\r
+\r
+    // now re-enable Receives\r
+    m_bFlushing = FALSE;\r
+\r
+    // No more errors\r
+    m_bRunTimeError = FALSE;\r
+\r
+    return S_OK;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CBaseInputPin::Notify(IBaseFilter * pSender, Quality q)\r
+{\r
+    UNREFERENCED_PARAMETER(q);\r
+    CheckPointer(pSender,E_POINTER);\r
+    ValidateReadPtr(pSender,sizeof(IBaseFilter));\r
+    DbgBreak("IQuality::Notify called on an input pin");\r
+    return NOERROR;\r
+} // Notify\r
+\r
+/* Free up or unprepare allocator's memory, this is called through\r
+   IMediaFilter which is responsible for locking the object first */\r
+\r
+HRESULT\r
+CBaseInputPin::Inactive(void)\r
+{\r
+    m_bRunTimeError = FALSE;\r
+    if (m_pAllocator == NULL) {\r
+        return VFW_E_NO_ALLOCATOR;\r
+    }\r
+\r
+    m_bFlushing = FALSE;\r
+\r
+    return m_pAllocator->Decommit();\r
+}\r
+\r
+// what requirements do we have of the allocator - override if you want\r
+// to support other people's allocators but need a specific alignment\r
+// or prefix.\r
+STDMETHODIMP\r
+CBaseInputPin::GetAllocatorRequirements(__out ALLOCATOR_PROPERTIES*pProps)\r
+{\r
+    UNREFERENCED_PARAMETER(pProps);\r
+    return E_NOTIMPL;\r
+}\r
+\r
+//  Check if it's OK to process data\r
+//\r
+HRESULT\r
+CBaseInputPin::CheckStreaming()\r
+{\r
+    //  Shouldn't be able to get any data if we're not connected!\r
+    ASSERT(IsConnected());\r
+\r
+    //  Don't process stuff in Stopped state\r
+    if (IsStopped()) {\r
+        return VFW_E_WRONG_STATE;\r
+    }\r
+    if (m_bFlushing) {\r
+        return S_FALSE;\r
+    }\r
+    if (m_bRunTimeError) {\r
+        return VFW_E_RUNTIME_ERROR;\r
+    }\r
+    return S_OK;\r
+}\r
+\r
+// Pass on the Quality notification q to\r
+// a. Our QualityControl sink (if we have one) or else\r
+// b. to our upstream filter\r
+// and if that doesn't work, throw it away with a bad return code\r
+HRESULT\r
+CBaseInputPin::PassNotify(Quality& q)\r
+{\r
+    // We pass the message on, which means that we find the quality sink\r
+    // for our input pin and send it there\r
+\r
+    DbgLog((LOG_TRACE,3,TEXT("Passing Quality notification through transform")));\r
+    if (m_pQSink!=NULL) {\r
+        return m_pQSink->Notify(m_pFilter, q);\r
+    } else {\r
+        // no sink set, so pass it upstream\r
+        HRESULT hr;\r
+        IQualityControl * pIQC;\r
+\r
+        hr = VFW_E_NOT_FOUND;                   // default\r
+        if (m_Connected) {\r
+            m_Connected->QueryInterface(IID_IQualityControl, (void**)&pIQC);\r
+\r
+            if (pIQC!=NULL) {\r
+                hr = pIQC->Notify(m_pFilter, q);\r
+                pIQC->Release();\r
+            }\r
+        }\r
+        return hr;\r
+    }\r
+\r
+} // PassNotify\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Memory allocation class, implements CMediaSample\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+\r
+/* NOTE The implementation of this class calls the CUnknown constructor with\r
+   a NULL outer unknown pointer. This has the effect of making us a self\r
+   contained class, ie any QueryInterface, AddRef or Release calls will be\r
+   routed to the class's NonDelegatingUnknown methods. You will typically\r
+   find that the classes that do this then override one or more of these\r
+   virtual functions to provide more specialised behaviour. A good example\r
+   of this is where a class wants to keep the QueryInterface internal but\r
+   still wants it's lifetime controlled by the external object */\r
+\r
+/* The last two parameters have default values of NULL and zero */\r
+\r
+CMediaSample::CMediaSample(__in_opt LPCTSTR pName,\r
+               __in_opt CBaseAllocator *pAllocator,\r
+               __inout_opt HRESULT *phr,\r
+               __in_bcount_opt(length) LPBYTE pBuffer,\r
+               LONG length) :\r
+    m_pBuffer(pBuffer),             // Initialise the buffer\r
+    m_cbBuffer(length),             // And it's length\r
+    m_lActual(length),              // By default, actual = length\r
+    m_pMediaType(NULL),             // No media type change\r
+    m_dwFlags(0),                   // Nothing set\r
+    m_cRef(0),                      // 0 ref count\r
+    m_dwTypeSpecificFlags(0),       // Type specific flags\r
+    m_dwStreamId(AM_STREAM_MEDIA),  // Stream id\r
+    m_pAllocator(pAllocator)        // Allocator\r
+{\r
+#ifdef DXMPERF\r
+    PERFLOG_CTOR( pName ? pName : L"CMediaSample", (IMediaSample *) this );\r
+#endif // DXMPERF\r
+\r
+    /* We must have an owner and it must also be derived from class\r
+       CBaseAllocator BUT we do not hold a reference count on it */\r
+\r
+    ASSERT(pAllocator);\r
+\r
+    if (length < 0) {\r
+        *phr = VFW_E_BUFFER_OVERFLOW;\r
+        m_cbBuffer = 0;\r
+    }\r
+}\r
+\r
+#ifdef UNICODE\r
+CMediaSample::CMediaSample(__in_opt LPCSTR pName,\r
+               __in_opt CBaseAllocator *pAllocator,\r
+               __inout_opt HRESULT *phr,\r
+               __in_bcount_opt(length) LPBYTE pBuffer,\r
+               LONG length) :\r
+    m_pBuffer(pBuffer),             // Initialise the buffer\r
+    m_cbBuffer(length),             // And it's length\r
+    m_lActual(length),              // By default, actual = length\r
+    m_pMediaType(NULL),             // No media type change\r
+    m_dwFlags(0),                   // Nothing set\r
+    m_cRef(0),                      // 0 ref count\r
+    m_dwTypeSpecificFlags(0),       // Type specific flags\r
+    m_dwStreamId(AM_STREAM_MEDIA),  // Stream id\r
+    m_pAllocator(pAllocator)        // Allocator\r
+{\r
+#ifdef DXMPERF\r
+    PERFLOG_CTOR( L"CMediaSample", (IMediaSample *) this );\r
+#endif // DXMPERF\r
+\r
+    /* We must have an owner and it must also be derived from class\r
+       CBaseAllocator BUT we do not hold a reference count on it */\r
+\r
+    ASSERT(pAllocator);\r
+}\r
+#endif\r
+\r
+/* Destructor deletes the media type memory */\r
+\r
+CMediaSample::~CMediaSample()\r
+{\r
+#ifdef DXMPERF\r
+    PERFLOG_DTOR( L"CMediaSample", (IMediaSample *) this );\r
+#endif // DXMPERF\r
+\r
+    if (m_pMediaType) {\r
+    DeleteMediaType(m_pMediaType);\r
+    }\r
+}\r
+\r
+/* Override this to publicise our interfaces */\r
+\r
+STDMETHODIMP\r
+CMediaSample::QueryInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    if (riid == IID_IMediaSample ||\r
+        riid == IID_IMediaSample2 ||\r
+        riid == IID_IUnknown) {\r
+        return GetInterface((IMediaSample *) this, ppv);\r
+    } else {\r
+        *ppv = NULL;\r
+        return E_NOINTERFACE;\r
+    }\r
+}\r
+\r
+STDMETHODIMP_(ULONG)\r
+CMediaSample::AddRef()\r
+{\r
+    return InterlockedIncrement(&m_cRef);\r
+}\r
+\r
+\r
+// --  CMediaSample lifetimes --\r
+//\r
+// On final release of this sample buffer it is not deleted but\r
+// returned to the freelist of the owning memory allocator\r
+//\r
+// The allocator may be waiting for the last buffer to be placed on the free\r
+// list in order to decommit all the memory, so the ReleaseBuffer() call may\r
+// result in this sample being deleted. We also need to hold a refcount on\r
+// the allocator to stop that going away until we have finished with this.\r
+// However, we cannot release the allocator before the ReleaseBuffer, as the\r
+// release may cause us to be deleted. Similarly we can't do it afterwards.\r
+//\r
+// Thus we must leave it to the allocator to hold an addref on our behalf.\r
+// When he issues us in GetBuffer, he addref's himself. When ReleaseBuffer\r
+// is called, he releases himself, possibly causing us and him to be deleted.\r
+\r
+\r
+STDMETHODIMP_(ULONG)\r
+CMediaSample::Release()\r
+{\r
+    /* Decrement our own private reference count */\r
+    LONG lRef;\r
+    if (m_cRef == 1) {\r
+        lRef = 0;\r
+        m_cRef = 0;\r
+    } else {\r
+        lRef = InterlockedDecrement(&m_cRef);\r
+    }\r
+    ASSERT(lRef >= 0);\r
+\r
+    DbgLog((LOG_MEMORY,3,TEXT("    Unknown %X ref-- = %d"),\r
+        this, m_cRef));\r
+\r
+    /* Did we release our final reference count */\r
+    if (lRef == 0) {\r
+        /* Free all resources */\r
+        if (m_dwFlags & Sample_TypeChanged) {\r
+            SetMediaType(NULL);\r
+        }\r
+        ASSERT(m_pMediaType == NULL);\r
+        m_dwFlags = 0;\r
+        m_dwTypeSpecificFlags = 0;\r
+        m_dwStreamId = AM_STREAM_MEDIA;\r
+\r
+        /* This may cause us to be deleted */\r
+        // Our refcount is reliably 0 thus no-one will mess with us\r
+        m_pAllocator->ReleaseBuffer(this);\r
+    }\r
+    return (ULONG)lRef;\r
+}\r
+\r
+\r
+// set the buffer pointer and length. Used by allocators that\r
+// want variable sized pointers or pointers into already-read data.\r
+// This is only available through a CMediaSample* not an IMediaSample*\r
+// and so cannot be changed by clients.\r
+HRESULT\r
+CMediaSample::SetPointer(__in_bcount(cBytes) BYTE * ptr, LONG cBytes)\r
+{\r
+    if (cBytes < 0) {\r
+        return VFW_E_BUFFER_OVERFLOW;\r
+    }\r
+    m_pBuffer = ptr;            // new buffer area (could be null)\r
+    m_cbBuffer = cBytes;        // length of buffer\r
+    m_lActual = cBytes;         // length of data in buffer (assume full)\r
+\r
+    return S_OK;\r
+}\r
+\r
+\r
+// get me a read/write pointer to this buffer's memory. I will actually\r
+// want to use sizeUsed bytes.\r
+STDMETHODIMP\r
+CMediaSample::GetPointer(__deref_out BYTE ** ppBuffer)\r
+{\r
+    ValidateReadWritePtr(ppBuffer,sizeof(BYTE *));\r
+\r
+    // creator must have set pointer either during\r
+    // constructor or by SetPointer\r
+    ASSERT(m_pBuffer);\r
+\r
+    *ppBuffer = m_pBuffer;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// return the size in bytes of this buffer\r
+STDMETHODIMP_(LONG)\r
+CMediaSample::GetSize(void)\r
+{\r
+    return m_cbBuffer;\r
+}\r
+\r
+\r
+// get the stream time at which this sample should start and finish.\r
+STDMETHODIMP\r
+CMediaSample::GetTime(\r
+    __out REFERENCE_TIME * pTimeStart,     // put time here\r
+    __out REFERENCE_TIME * pTimeEnd\r
+)\r
+{\r
+    ValidateReadWritePtr(pTimeStart,sizeof(REFERENCE_TIME));\r
+    ValidateReadWritePtr(pTimeEnd,sizeof(REFERENCE_TIME));\r
+\r
+    if (!(m_dwFlags & Sample_StopValid)) {\r
+        if (!(m_dwFlags & Sample_TimeValid)) {\r
+            return VFW_E_SAMPLE_TIME_NOT_SET;\r
+        } else {\r
+            *pTimeStart = m_Start;\r
+\r
+            //  Make sure old stuff works\r
+            *pTimeEnd = m_Start + 1;\r
+            return VFW_S_NO_STOP_TIME;\r
+        }\r
+    }\r
+\r
+    *pTimeStart = m_Start;\r
+    *pTimeEnd = m_End;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Set the stream time at which this sample should start and finish.\r
+// NULL pointers means the time is reset\r
+STDMETHODIMP\r
+CMediaSample::SetTime(\r
+    __in_opt REFERENCE_TIME * pTimeStart,\r
+    __in_opt REFERENCE_TIME * pTimeEnd\r
+)\r
+{\r
+    if (pTimeStart == NULL) {\r
+        ASSERT(pTimeEnd == NULL);\r
+        m_dwFlags &= ~(Sample_TimeValid | Sample_StopValid);\r
+    } else {\r
+        if (pTimeEnd == NULL) {\r
+            m_Start = *pTimeStart;\r
+            m_dwFlags |= Sample_TimeValid;\r
+            m_dwFlags &= ~Sample_StopValid;\r
+        } else {\r
+            ValidateReadPtr(pTimeStart,sizeof(REFERENCE_TIME));\r
+            ValidateReadPtr(pTimeEnd,sizeof(REFERENCE_TIME));\r
+            ASSERT(*pTimeEnd >= *pTimeStart);\r
+\r
+            m_Start = *pTimeStart;\r
+            m_End = *pTimeEnd;\r
+            m_dwFlags |= Sample_TimeValid | Sample_StopValid;\r
+        }\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// get the media times (eg bytes) for this sample\r
+STDMETHODIMP\r
+CMediaSample::GetMediaTime(\r
+    __out LONGLONG * pTimeStart,\r
+    __out LONGLONG * pTimeEnd\r
+)\r
+{\r
+    ValidateReadWritePtr(pTimeStart,sizeof(LONGLONG));\r
+    ValidateReadWritePtr(pTimeEnd,sizeof(LONGLONG));\r
+\r
+    if (!(m_dwFlags & Sample_MediaTimeValid)) {\r
+        return VFW_E_MEDIA_TIME_NOT_SET;\r
+    }\r
+\r
+    *pTimeStart = m_MediaStart;\r
+    *pTimeEnd = (m_MediaStart + m_MediaEnd);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Set the media times for this sample\r
+STDMETHODIMP\r
+CMediaSample::SetMediaTime(\r
+    __in_opt LONGLONG * pTimeStart,\r
+    __in_opt LONGLONG * pTimeEnd\r
+)\r
+{\r
+    if (pTimeStart == NULL) {\r
+        ASSERT(pTimeEnd == NULL);\r
+        m_dwFlags &= ~Sample_MediaTimeValid;\r
+    } else {\r
+        if (NULL == pTimeEnd) {\r
+            return E_POINTER;\r
+        }\r
+        ValidateReadPtr(pTimeStart,sizeof(LONGLONG));\r
+        ValidateReadPtr(pTimeEnd,sizeof(LONGLONG));\r
+        ASSERT(*pTimeEnd >= *pTimeStart);\r
+\r
+        m_MediaStart = *pTimeStart;\r
+        m_MediaEnd = (LONG)(*pTimeEnd - *pTimeStart);\r
+        m_dwFlags |= Sample_MediaTimeValid;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CMediaSample::IsSyncPoint(void)\r
+{\r
+    if (m_dwFlags & Sample_SyncPoint) {\r
+        return S_OK;\r
+    } else {\r
+        return S_FALSE;\r
+    }\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CMediaSample::SetSyncPoint(BOOL bIsSyncPoint)\r
+{\r
+    if (bIsSyncPoint) {\r
+        m_dwFlags |= Sample_SyncPoint;\r
+    } else {\r
+        m_dwFlags &= ~Sample_SyncPoint;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+// returns S_OK if there is a discontinuity in the data (this same is\r
+// not a continuation of the previous stream of data\r
+// - there has been a seek).\r
+STDMETHODIMP\r
+CMediaSample::IsDiscontinuity(void)\r
+{\r
+    if (m_dwFlags & Sample_Discontinuity) {\r
+        return S_OK;\r
+    } else {\r
+        return S_FALSE;\r
+    }\r
+}\r
+\r
+// set the discontinuity property - TRUE if this sample is not a\r
+// continuation, but a new sample after a seek.\r
+STDMETHODIMP\r
+CMediaSample::SetDiscontinuity(BOOL bDiscont)\r
+{\r
+    // should be TRUE or FALSE\r
+    if (bDiscont) {\r
+        m_dwFlags |= Sample_Discontinuity;\r
+    } else {\r
+        m_dwFlags &= ~Sample_Discontinuity;\r
+    }\r
+    return S_OK;\r
+}\r
+\r
+STDMETHODIMP\r
+CMediaSample::IsPreroll(void)\r
+{\r
+    if (m_dwFlags & Sample_Preroll) {\r
+        return S_OK;\r
+    } else {\r
+        return S_FALSE;\r
+    }\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CMediaSample::SetPreroll(BOOL bIsPreroll)\r
+{\r
+    if (bIsPreroll) {\r
+        m_dwFlags |= Sample_Preroll;\r
+    } else {\r
+        m_dwFlags &= ~Sample_Preroll;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+STDMETHODIMP_(LONG)\r
+CMediaSample::GetActualDataLength(void)\r
+{\r
+    return m_lActual;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CMediaSample::SetActualDataLength(LONG lActual)\r
+{\r
+    if (lActual > m_cbBuffer || lActual < 0) {\r
+        ASSERT(lActual <= GetSize());\r
+        return VFW_E_BUFFER_OVERFLOW;\r
+    }\r
+    m_lActual = lActual;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+/* These allow for limited format changes in band */\r
+\r
+STDMETHODIMP\r
+CMediaSample::GetMediaType(__deref_out AM_MEDIA_TYPE **ppMediaType)\r
+{\r
+    ValidateReadWritePtr(ppMediaType,sizeof(AM_MEDIA_TYPE *));\r
+    ASSERT(ppMediaType);\r
+\r
+    /* Do we have a new media type for them */\r
+\r
+    if (!(m_dwFlags & Sample_TypeChanged)) {\r
+        ASSERT(m_pMediaType == NULL);\r
+        *ppMediaType = NULL;\r
+        return S_FALSE;\r
+    }\r
+\r
+    ASSERT(m_pMediaType);\r
+\r
+    /* Create a copy of our media type */\r
+\r
+    *ppMediaType = CreateMediaType(m_pMediaType);\r
+    if (*ppMediaType == NULL) {\r
+        return E_OUTOFMEMORY;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+/* Mark this sample as having a different format type */\r
+\r
+STDMETHODIMP\r
+CMediaSample::SetMediaType(__in_opt AM_MEDIA_TYPE *pMediaType)\r
+{\r
+    /* Delete the current media type */\r
+\r
+    if (m_pMediaType) {\r
+        DeleteMediaType(m_pMediaType);\r
+        m_pMediaType = NULL;\r
+    }\r
+\r
+    /* Mechanism for resetting the format type */\r
+\r
+    if (pMediaType == NULL) {\r
+        m_dwFlags &= ~Sample_TypeChanged;\r
+        return NOERROR;\r
+    }\r
+\r
+    ASSERT(pMediaType);\r
+    ValidateReadPtr(pMediaType,sizeof(AM_MEDIA_TYPE));\r
+\r
+    /* Take a copy of the media type */\r
+\r
+    m_pMediaType = CreateMediaType(pMediaType);\r
+    if (m_pMediaType == NULL) {\r
+        m_dwFlags &= ~Sample_TypeChanged;\r
+        return E_OUTOFMEMORY;\r
+    }\r
+\r
+    m_dwFlags |= Sample_TypeChanged;\r
+    return NOERROR;\r
+}\r
+\r
+// Set and get properties (IMediaSample2)\r
+STDMETHODIMP CMediaSample::GetProperties(\r
+    DWORD cbProperties,\r
+    __out_bcount(cbProperties) BYTE * pbProperties\r
+)\r
+{\r
+    if (0 != cbProperties) {\r
+        CheckPointer(pbProperties, E_POINTER);\r
+        //  Return generic stuff up to the length\r
+        AM_SAMPLE2_PROPERTIES Props;\r
+        Props.cbData     = min(cbProperties, sizeof(Props));\r
+        Props.dwSampleFlags = m_dwFlags & ~Sample_MediaTimeValid;\r
+        Props.dwTypeSpecificFlags = m_dwTypeSpecificFlags;\r
+        Props.pbBuffer   = m_pBuffer;\r
+        Props.cbBuffer   = m_cbBuffer;\r
+        Props.lActual    = m_lActual;\r
+        Props.tStart     = m_Start;\r
+        Props.tStop      = m_End;\r
+        Props.dwStreamId = m_dwStreamId;\r
+        if (m_dwFlags & AM_SAMPLE_TYPECHANGED) {\r
+            Props.pMediaType = m_pMediaType;\r
+        } else {\r
+            Props.pMediaType = NULL;\r
+        }\r
+        CopyMemory(pbProperties, &Props, Props.cbData);\r
+    }\r
+    return S_OK;\r
+}\r
+\r
+#define CONTAINS_FIELD(type, field, offset) \\r
+    ((FIELD_OFFSET(type, field) + sizeof(((type *)0)->field)) <= offset)\r
+\r
+HRESULT CMediaSample::SetProperties(\r
+    DWORD cbProperties,\r
+    __in_bcount(cbProperties) const BYTE * pbProperties\r
+)\r
+{\r
+\r
+    /*  Generic properties */\r
+    AM_MEDIA_TYPE *pMediaType = NULL;\r
+\r
+    if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbData, cbProperties)) {\r
+        CheckPointer(pbProperties, E_POINTER);\r
+        AM_SAMPLE2_PROPERTIES *pProps =\r
+            (AM_SAMPLE2_PROPERTIES *)pbProperties;\r
+\r
+        /*  Don't use more data than is actually there */\r
+        if (pProps->cbData < cbProperties) {\r
+            cbProperties = pProps->cbData;\r
+        }\r
+        /*  We only handle IMediaSample2 */\r
+        if (cbProperties > sizeof(*pProps) ||\r
+            pProps->cbData > sizeof(*pProps)) {\r
+            return E_INVALIDARG;\r
+        }\r
+        /*  Do checks first, the assignments (for backout) */\r
+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwSampleFlags, cbProperties)) {\r
+            /*  Check the flags */\r
+            if (pProps->dwSampleFlags &\r
+                    (~Sample_ValidFlags | Sample_MediaTimeValid)) {\r
+                return E_INVALIDARG;\r
+            }\r
+            /*  Check a flag isn't being set for a property\r
+                not being provided\r
+            */\r
+            if ((pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID) &&\r
+                 !(m_dwFlags & AM_SAMPLE_TIMEVALID) &&\r
+                 !CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStop, cbProperties)) {\r
+                 return E_INVALIDARG;\r
+            }\r
+        }\r
+        /*  NB - can't SET the pointer or size */\r
+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pbBuffer, cbProperties)) {\r
+\r
+            /*  Check pbBuffer */\r
+            if (pProps->pbBuffer != 0 && pProps->pbBuffer != m_pBuffer) {\r
+                return E_INVALIDARG;\r
+            }\r
+        }\r
+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbBuffer, cbProperties)) {\r
+\r
+            /*  Check cbBuffer */\r
+            if (pProps->cbBuffer != 0 && pProps->cbBuffer != m_cbBuffer) {\r
+                return E_INVALIDARG;\r
+            }\r
+        }\r
+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbBuffer, cbProperties) &&\r
+            CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, lActual, cbProperties)) {\r
+\r
+            /*  Check lActual */\r
+            if (pProps->cbBuffer < pProps->lActual) {\r
+                return E_INVALIDARG;\r
+            }\r
+        }\r
+\r
+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pMediaType, cbProperties)) {\r
+\r
+            /*  Check pMediaType */\r
+            if (pProps->dwSampleFlags & AM_SAMPLE_TYPECHANGED) {\r
+                CheckPointer(pProps->pMediaType, E_POINTER);\r
+                pMediaType = CreateMediaType(pProps->pMediaType);\r
+                if (pMediaType == NULL) {\r
+                    return E_OUTOFMEMORY;\r
+                }\r
+            }\r
+        }\r
+\r
+        /*  Now do the assignments */\r
+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwStreamId, cbProperties)) {\r
+            m_dwStreamId = pProps->dwStreamId;\r
+        }\r
+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwSampleFlags, cbProperties)) {\r
+            /*  Set the flags */\r
+            m_dwFlags = pProps->dwSampleFlags |\r
+                                (m_dwFlags & Sample_MediaTimeValid);\r
+            m_dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;\r
+        } else {\r
+            if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwTypeSpecificFlags, cbProperties)) {\r
+                m_dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;\r
+            }\r
+        }\r
+\r
+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, lActual, cbProperties)) {\r
+            /*  Set lActual */\r
+            m_lActual = pProps->lActual;\r
+        }\r
+\r
+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStop, cbProperties)) {\r
+\r
+            /*  Set the times */\r
+            m_End   = pProps->tStop;\r
+        }\r
+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStart, cbProperties)) {\r
+\r
+            /*  Set the times */\r
+            m_Start = pProps->tStart;\r
+        }\r
+\r
+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pMediaType, cbProperties)) {\r
+            /*  Set pMediaType */\r
+            if (pProps->dwSampleFlags & AM_SAMPLE_TYPECHANGED) {\r
+                if (m_pMediaType != NULL) {\r
+                    DeleteMediaType(m_pMediaType);\r
+                }\r
+                m_pMediaType = pMediaType;\r
+            }\r
+        }\r
+\r
+        /*  Fix up the type changed flag to correctly reflect the current state\r
+            If, for instance the input contained no type change but the\r
+            output does then if we don't do this we'd lose the\r
+            output media type.\r
+        */\r
+        if (m_pMediaType) {\r
+            m_dwFlags |= Sample_TypeChanged;\r
+        } else {\r
+            m_dwFlags &= ~Sample_TypeChanged;\r
+        }\r
+    }\r
+\r
+    return S_OK;\r
+}\r
+\r
+\r
+//\r
+// The streaming thread calls IPin::NewSegment(), IPin::EndOfStream(),\r
+// IMemInputPin::Receive() and IMemInputPin::ReceiveMultiple() on the\r
+// connected input pin.  The application thread calls Block().  The\r
+// following class members can only be called by the streaming thread.\r
+//\r
+//    Deliver()\r
+//    DeliverNewSegment()\r
+//    StartUsingOutputPin()\r
+//    StopUsingOutputPin()\r
+//    ChangeOutputFormat()\r
+//    ChangeMediaType()\r
+//    DynamicReconnect()\r
+//\r
+// The following class members can only be called by the application thread.\r
+//\r
+//    Block()\r
+//    SynchronousBlockOutputPin()\r
+//    AsynchronousBlockOutputPin()\r
+//\r
+\r
+CDynamicOutputPin::CDynamicOutputPin(\r
+    __in_opt LPCTSTR pObjectName,\r
+    __in CBaseFilter *pFilter,\r
+    __in CCritSec *pLock,\r
+    __inout HRESULT *phr,\r
+    __in_opt LPCWSTR pName) :\r
+        CBaseOutputPin(pObjectName, pFilter, pLock, phr, pName),\r
+        m_hStopEvent(NULL),\r
+        m_pGraphConfig(NULL),\r
+        m_bPinUsesReadOnlyAllocator(FALSE),\r
+        m_BlockState(NOT_BLOCKED),\r
+        m_hUnblockOutputPinEvent(NULL),\r
+        m_hNotifyCallerPinBlockedEvent(NULL),\r
+        m_dwBlockCallerThreadID(0),\r
+        m_dwNumOutstandingOutputPinUsers(0)\r
+{\r
+    HRESULT hr = Initialize();\r
+    if( FAILED( hr ) ) {\r
+        *phr = hr;\r
+        return;\r
+    }\r
+}\r
+\r
+#ifdef UNICODE\r
+CDynamicOutputPin::CDynamicOutputPin(\r
+    __in_opt LPCSTR pObjectName,\r
+    __in CBaseFilter *pFilter,\r
+    __in CCritSec *pLock,\r
+    __inout HRESULT *phr,\r
+    __in_opt LPCWSTR pName) :\r
+        CBaseOutputPin(pObjectName, pFilter, pLock, phr, pName),\r
+        m_hStopEvent(NULL),\r
+        m_pGraphConfig(NULL),\r
+        m_bPinUsesReadOnlyAllocator(FALSE),\r
+        m_BlockState(NOT_BLOCKED),\r
+        m_hUnblockOutputPinEvent(NULL),\r
+        m_hNotifyCallerPinBlockedEvent(NULL),\r
+        m_dwBlockCallerThreadID(0),\r
+        m_dwNumOutstandingOutputPinUsers(0)\r
+{\r
+    HRESULT hr = Initialize();\r
+    if( FAILED( hr ) ) {\r
+        *phr = hr;\r
+        return;\r
+    }\r
+}\r
+#endif\r
+\r
+CDynamicOutputPin::~CDynamicOutputPin()\r
+{\r
+    if(NULL != m_hUnblockOutputPinEvent) {\r
+        // This call should not fail because we have access to m_hUnblockOutputPinEvent\r
+        // and m_hUnblockOutputPinEvent is a valid event.\r
+        EXECUTE_ASSERT(::CloseHandle(m_hUnblockOutputPinEvent));\r
+    }\r
+\r
+    if(NULL != m_hNotifyCallerPinBlockedEvent) {\r
+        // This call should not fail because we have access to m_hNotifyCallerPinBlockedEvent\r
+        // and m_hNotifyCallerPinBlockedEvent is a valid event.\r
+        EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));\r
+    }\r
+}\r
+\r
+HRESULT CDynamicOutputPin::Initialize(void)\r
+{\r
+    m_hUnblockOutputPinEvent = ::CreateEvent( NULL,   // The event will have the default security descriptor.\r
+                                              TRUE,   // This is a manual reset event.\r
+                                              TRUE,   // The event is initially signaled.\r
+                                              NULL ); // The event is not named.\r
+\r
+    // CreateEvent() returns NULL if an error occurs.\r
+    if(NULL == m_hUnblockOutputPinEvent) {\r
+        return AmGetLastErrorToHResult();\r
+    }\r
+\r
+    //  Set flag to say we can reconnect while streaming.\r
+    SetReconnectWhenActive(true);\r
+\r
+    return S_OK;\r
+}\r
+\r
+STDMETHODIMP CDynamicOutputPin::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    if(riid == IID_IPinFlowControl) {\r
+        return GetInterface(static_cast<IPinFlowControl*>(this), ppv);\r
+    } else {\r
+        return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv);\r
+    }\r
+}\r
+\r
+STDMETHODIMP CDynamicOutputPin::Disconnect(void)\r
+{\r
+    CAutoLock cObjectLock(m_pLock);\r
+    return DisconnectInternal();\r
+}\r
+\r
+STDMETHODIMP CDynamicOutputPin::Block(DWORD dwBlockFlags, HANDLE hEvent)\r
+{\r
+    const DWORD VALID_FLAGS = AM_PIN_FLOW_CONTROL_BLOCK;\r
+\r
+    // Check for illegal flags.\r
+    if(dwBlockFlags & ~VALID_FLAGS) {\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+    // Make sure the event is unsignaled.\r
+    if((dwBlockFlags & AM_PIN_FLOW_CONTROL_BLOCK) && (NULL != hEvent)) {\r
+        if( !::ResetEvent( hEvent ) ) {\r
+            return AmGetLastErrorToHResult();\r
+        }\r
+    }\r
+\r
+    // No flags are set if we are unblocking the output pin.\r
+    if(0 == dwBlockFlags) {\r
+\r
+        // This parameter should be NULL because unblock operations are always synchronous.\r
+        // There is no need to notify the caller when the event is done.\r
+        if(NULL != hEvent) {\r
+            return E_INVALIDARG;\r
+        }\r
+    }\r
+\r
+    #ifdef DEBUG\r
+    AssertValid();\r
+    #endif // DEBUG\r
+\r
+    HRESULT hr;\r
+\r
+    if(dwBlockFlags & AM_PIN_FLOW_CONTROL_BLOCK) {\r
+        // IPinFlowControl::Block()'s hEvent parameter is NULL if the block is synchronous.\r
+        // If hEvent is not NULL, the block is asynchronous.\r
+        if(NULL == hEvent) {\r
+            hr = SynchronousBlockOutputPin();\r
+        } else {\r
+            hr = AsynchronousBlockOutputPin(hEvent);\r
+        }\r
+    } else {\r
+        hr = UnblockOutputPin();\r
+    }\r
+\r
+    #ifdef DEBUG\r
+    AssertValid();\r
+    #endif // DEBUG\r
+\r
+    if(FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    return S_OK;\r
+}\r
+\r
+HRESULT CDynamicOutputPin::SynchronousBlockOutputPin(void)\r
+{\r
+    HANDLE hNotifyCallerPinBlockedEvent = :: CreateEvent( NULL,   // The event will have the default security attributes.\r
+                                                          FALSE,  // This is an automatic reset event.\r
+                                                          FALSE,  // The event is initially unsignaled.\r
+                                                          NULL ); // The event is not named.\r
+\r
+    // CreateEvent() returns NULL if an error occurs.\r
+    if(NULL == hNotifyCallerPinBlockedEvent) {\r
+        return AmGetLastErrorToHResult();\r
+    }\r
+\r
+    HRESULT hr = AsynchronousBlockOutputPin(hNotifyCallerPinBlockedEvent);\r
+    if(FAILED(hr)) {\r
+        // This call should not fail because we have access to hNotifyCallerPinBlockedEvent\r
+        // and hNotifyCallerPinBlockedEvent is a valid event.\r
+        EXECUTE_ASSERT(::CloseHandle(hNotifyCallerPinBlockedEvent));\r
+\r
+        return hr;\r
+    }\r
+\r
+    hr = WaitEvent(hNotifyCallerPinBlockedEvent);\r
+\r
+    // This call should not fail because we have access to hNotifyCallerPinBlockedEvent\r
+    // and hNotifyCallerPinBlockedEvent is a valid event.\r
+    EXECUTE_ASSERT(::CloseHandle(hNotifyCallerPinBlockedEvent));\r
+\r
+    if(FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    return S_OK;\r
+}\r
+\r
+HRESULT CDynamicOutputPin::AsynchronousBlockOutputPin(HANDLE hNotifyCallerPinBlockedEvent)\r
+{\r
+    // This function holds the m_BlockStateLock because it uses\r
+    // m_dwBlockCallerThreadID, m_BlockState and\r
+    // m_hNotifyCallerPinBlockedEvent.\r
+    CAutoLock alBlockStateLock(&m_BlockStateLock);\r
+\r
+    if(NOT_BLOCKED != m_BlockState) {\r
+        if(m_dwBlockCallerThreadID == ::GetCurrentThreadId()) {\r
+            return VFW_E_PIN_ALREADY_BLOCKED_ON_THIS_THREAD;\r
+        } else {\r
+            return VFW_E_PIN_ALREADY_BLOCKED;\r
+        }\r
+    }\r
+\r
+    BOOL fSuccess = ::DuplicateHandle( ::GetCurrentProcess(),\r
+                                       hNotifyCallerPinBlockedEvent,\r
+                                       ::GetCurrentProcess(),\r
+                                       &m_hNotifyCallerPinBlockedEvent,\r
+                                       EVENT_MODIFY_STATE,\r
+                                       FALSE,\r
+                                       0 );\r
+    if( !fSuccess ) {\r
+        return AmGetLastErrorToHResult();\r
+    }\r
+\r
+    m_BlockState = PENDING;\r
+    m_dwBlockCallerThreadID = ::GetCurrentThreadId();\r
+\r
+    // The output pin cannot be blocked if the streaming thread is\r
+    // calling IPin::NewSegment(), IPin::EndOfStream(), IMemInputPin::Receive()\r
+    // or IMemInputPin::ReceiveMultiple() on the connected input pin.  Also, it\r
+    // cannot be blocked if the streaming thread is calling DynamicReconnect(),\r
+    // ChangeMediaType() or ChangeOutputFormat().\r
+    if(!StreamingThreadUsingOutputPin()) {\r
+\r
+        // The output pin can be immediately blocked.\r
+        BlockOutputPin();\r
+    }\r
+\r
+    return S_OK;\r
+}\r
+\r
+void CDynamicOutputPin::BlockOutputPin(void)\r
+{\r
+    // The caller should always hold the m_BlockStateLock because this function\r
+    // uses m_BlockState and m_hNotifyCallerPinBlockedEvent.\r
+    ASSERT(CritCheckIn(&m_BlockStateLock));\r
+\r
+    // This function should not be called if the streaming thread is modifying\r
+    // the connection state or it's passing data downstream.\r
+    ASSERT(!StreamingThreadUsingOutputPin());\r
+\r
+    // This should not fail because we successfully created the event\r
+    // and we have the security permissions to change it's state.\r
+    EXECUTE_ASSERT(::ResetEvent(m_hUnblockOutputPinEvent));\r
+\r
+    // This event should not fail because AsynchronousBlockOutputPin() successfully\r
+    // duplicated this handle and we have the appropriate security permissions.\r
+    EXECUTE_ASSERT(::SetEvent(m_hNotifyCallerPinBlockedEvent));\r
+    EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));\r
+\r
+    m_BlockState = BLOCKED;\r
+    m_hNotifyCallerPinBlockedEvent = NULL;\r
+}\r
+\r
+HRESULT CDynamicOutputPin::UnblockOutputPin(void)\r
+{\r
+    // UnblockOutputPin() holds the m_BlockStateLock because it\r
+    // uses m_BlockState, m_dwBlockCallerThreadID and\r
+    // m_hNotifyCallerPinBlockedEvent.\r
+    CAutoLock alBlockStateLock(&m_BlockStateLock);\r
+\r
+    if(NOT_BLOCKED == m_BlockState) {\r
+        return S_FALSE;\r
+    }\r
+\r
+    // This should not fail because we successfully created the event\r
+    // and we have the security permissions to change it's state.\r
+    EXECUTE_ASSERT(::SetEvent(m_hUnblockOutputPinEvent));\r
+\r
+    // Cancel the block operation if it's still pending.\r
+    if(NULL != m_hNotifyCallerPinBlockedEvent) {\r
+        // This event should not fail because AsynchronousBlockOutputPin() successfully\r
+        // duplicated this handle and we have the appropriate security permissions.\r
+        EXECUTE_ASSERT(::SetEvent(m_hNotifyCallerPinBlockedEvent));\r
+        EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));\r
+    }\r
+\r
+    m_BlockState = NOT_BLOCKED;\r
+    m_dwBlockCallerThreadID = 0;\r
+    m_hNotifyCallerPinBlockedEvent = NULL;\r
+\r
+    return S_OK;\r
+}\r
+\r
+HRESULT CDynamicOutputPin::StartUsingOutputPin(void)\r
+{\r
+    // The caller should not hold m_BlockStateLock.  If the caller does,\r
+    // a deadlock could occur.\r
+    ASSERT(CritCheckOut(&m_BlockStateLock));\r
+\r
+    CAutoLock alBlockStateLock(&m_BlockStateLock);\r
+\r
+    #ifdef DEBUG\r
+    AssertValid();\r
+    #endif // DEBUG\r
+\r
+    // Are we in the middle of a block operation?\r
+    while(BLOCKED == m_BlockState) {\r
+        m_BlockStateLock.Unlock();\r
+\r
+        // If this ASSERT fires, a deadlock could occur.  The caller should make sure\r
+        // that this thread never acquires the Block State lock more than once.\r
+        ASSERT(CritCheckOut( &m_BlockStateLock ));\r
+\r
+        // WaitForMultipleObjects() returns WAIT_OBJECT_0 if the unblock event\r
+        // is fired.  It returns WAIT_OBJECT_0 + 1 if the stop event if fired.\r
+        // See the Windows SDK documentation for more information on\r
+        // WaitForMultipleObjects().\r
+        const DWORD UNBLOCK = WAIT_OBJECT_0;\r
+        const DWORD STOP = WAIT_OBJECT_0 + 1;\r
+\r
+        HANDLE ahWaitEvents[] = { m_hUnblockOutputPinEvent, m_hStopEvent };\r
+        DWORD dwNumWaitEvents = sizeof(ahWaitEvents)/sizeof(HANDLE);\r
+\r
+        DWORD dwReturnValue = ::WaitForMultipleObjects( dwNumWaitEvents, ahWaitEvents, FALSE, INFINITE );\r
+\r
+        m_BlockStateLock.Lock();\r
+\r
+        #ifdef DEBUG\r
+        AssertValid();\r
+        #endif // DEBUG\r
+\r
+        switch( dwReturnValue ) {\r
+        case UNBLOCK:\r
+            break;\r
+\r
+        case STOP:\r
+            return VFW_E_STATE_CHANGED;\r
+\r
+        case WAIT_FAILED:\r
+            return AmGetLastErrorToHResult();\r
+\r
+        default:\r
+            DbgBreak( "An Unexpected case occured in CDynamicOutputPin::StartUsingOutputPin()." );\r
+            return E_UNEXPECTED;\r
+        }\r
+    }\r
+\r
+    m_dwNumOutstandingOutputPinUsers++;\r
+\r
+    #ifdef DEBUG\r
+    AssertValid();\r
+    #endif // DEBUG\r
+\r
+    return S_OK;\r
+}\r
+\r
+void CDynamicOutputPin::StopUsingOutputPin(void)\r
+{\r
+    CAutoLock alBlockStateLock(&m_BlockStateLock);\r
+\r
+    #ifdef DEBUG\r
+    AssertValid();\r
+    #endif // DEBUG\r
+\r
+    m_dwNumOutstandingOutputPinUsers--;\r
+\r
+    if((m_dwNumOutstandingOutputPinUsers == 0) && (NOT_BLOCKED != m_BlockState)) {\r
+        BlockOutputPin();\r
+    }\r
+\r
+    #ifdef DEBUG\r
+    AssertValid();\r
+    #endif // DEBUG\r
+}\r
+\r
+bool CDynamicOutputPin::StreamingThreadUsingOutputPin(void)\r
+{\r
+    CAutoLock alBlockStateLock(&m_BlockStateLock);\r
+\r
+    return (m_dwNumOutstandingOutputPinUsers > 0);\r
+}\r
+\r
+void CDynamicOutputPin::SetConfigInfo(IGraphConfig *pGraphConfig, HANDLE hStopEvent)\r
+{\r
+    // This pointer is not addrefed because filters are not allowed to\r
+    // hold references to the filter graph manager.  See the documentation for\r
+    // IBaseFilter::JoinFilterGraph() in the Direct Show SDK for more information.\r
+    m_pGraphConfig = pGraphConfig;\r
+\r
+    m_hStopEvent = hStopEvent;\r
+}\r
+\r
+HRESULT CDynamicOutputPin::Active(void)\r
+{\r
+    // Make sure the user initialized the object by calling SetConfigInfo().\r
+    if((NULL == m_hStopEvent) || (NULL == m_pGraphConfig)) {\r
+        DbgBreak( ERROR: CDynamicOutputPin::Active() failed because m_pGraphConfig and m_hStopEvent were not initialized.  Call SetConfigInfo() to initialize them. );\r
+        return E_FAIL;\r
+    }\r
+\r
+    // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().\r
+    // The ASSERT can also fire if the event if destroyed and then Active() is called.  An event\r
+    // handle is invalid if 1) the event does not exist or the user does not have the security\r
+    // permissions to use the event.\r
+    EXECUTE_ASSERT(ResetEvent(m_hStopEvent));\r
+\r
+    return CBaseOutputPin::Active();\r
+}\r
+\r
+HRESULT CDynamicOutputPin::Inactive(void)\r
+{\r
+    // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().\r
+    // The ASSERT can also fire if the event if destroyed and then Active() is called.  An event\r
+    // handle is invalid if 1) the event does not exist or the user does not have the security\r
+    // permissions to use the event.\r
+    EXECUTE_ASSERT(SetEvent(m_hStopEvent));\r
+\r
+    return CBaseOutputPin::Inactive();\r
+}\r
+\r
+HRESULT CDynamicOutputPin::DeliverBeginFlush(void)\r
+{\r
+    // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().\r
+    // The ASSERT can also fire if the event if destroyed and then DeliverBeginFlush() is called.\r
+    // An event handle is invalid if 1) the event does not exist or the user does not have the security\r
+    // permissions to use the event.\r
+    EXECUTE_ASSERT(SetEvent(m_hStopEvent));\r
+\r
+    return CBaseOutputPin::DeliverBeginFlush();\r
+}\r
+\r
+HRESULT CDynamicOutputPin::DeliverEndFlush(void)\r
+{\r
+    // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().\r
+    // The ASSERT can also fire if the event if destroyed and then DeliverBeginFlush() is called.\r
+    // An event handle is invalid if 1) the event does not exist or the user does not have the security\r
+    // permissions to use the event.\r
+    EXECUTE_ASSERT(ResetEvent(m_hStopEvent));\r
+\r
+    return CBaseOutputPin::DeliverEndFlush();\r
+}\r
+\r
+\r
+// ChangeOutputFormat() either dynamicly changes the connection's format type or it dynamicly\r
+// reconnects the output pin.\r
+HRESULT CDynamicOutputPin::ChangeOutputFormat\r
+    (\r
+    const AM_MEDIA_TYPE *pmt,\r
+    REFERENCE_TIME tSegmentStart,\r
+    REFERENCE_TIME tSegmentStop,\r
+    double dSegmentRate\r
+    )\r
+{\r
+    // The caller should call StartUsingOutputPin() before calling this\r
+    // method.\r
+    ASSERT(StreamingThreadUsingOutputPin());\r
+\r
+    // Callers should always pass a valid media type to ChangeOutputFormat() .\r
+    ASSERT(NULL != pmt);\r
+\r
+    CMediaType cmt(*pmt);\r
+    HRESULT hr = ChangeMediaType(&cmt);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    hr = DeliverNewSegment(tSegmentStart, tSegmentStop, dSegmentRate);\r
+    if( FAILED( hr ) ) {\r
+        return hr;\r
+    }\r
+\r
+    return S_OK;\r
+}\r
+\r
+HRESULT CDynamicOutputPin::ChangeMediaType(const CMediaType *pmt)\r
+{\r
+    // The caller should call StartUsingOutputPin() before calling this\r
+    // method.\r
+    ASSERT(StreamingThreadUsingOutputPin());\r
+\r
+    // This function assumes the filter graph is running.\r
+    ASSERT(!IsStopped());\r
+\r
+    if(!IsConnected()) {\r
+        return VFW_E_NOT_CONNECTED;\r
+    }\r
+\r
+    /*  First check if the downstream pin will accept a dynamic\r
+        format change\r
+    */\r
+    QzCComPtr<IPinConnection> pConnection;\r
+\r
+    m_Connected->QueryInterface(IID_IPinConnection, (void **)&pConnection);\r
+    if(pConnection != NULL) {\r
+\r
+        if(S_OK == pConnection->DynamicQueryAccept(pmt)) {\r
+\r
+            HRESULT hr = ChangeMediaTypeHelper(pmt);\r
+            if(FAILED(hr)) {\r
+                return hr;\r
+            }\r
+\r
+            return S_OK;\r
+        }\r
+    }\r
+\r
+    /*  Can't do the dynamic connection */\r
+    return DynamicReconnect(pmt);\r
+}\r
+\r
+HRESULT CDynamicOutputPin::ChangeMediaTypeHelper(const CMediaType *pmt)\r
+{\r
+    // The caller should call StartUsingOutputPin() before calling this\r
+    // method.\r
+    ASSERT(StreamingThreadUsingOutputPin());\r
+\r
+    HRESULT hr = m_Connected->ReceiveConnection(this, pmt);\r
+    if(FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    hr = SetMediaType(pmt);\r
+    if(FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    // Does this pin use the local memory transport?\r
+    if(NULL != m_pInputPin) {\r
+        // This function assumes that m_pInputPin and m_Connected are\r
+        // two different interfaces to the same object.\r
+        ASSERT(::IsEqualObject(m_Connected, m_pInputPin));\r
+\r
+        ALLOCATOR_PROPERTIES apInputPinRequirements;\r
+        apInputPinRequirements.cbAlign = 0;\r
+        apInputPinRequirements.cbBuffer = 0;\r
+        apInputPinRequirements.cbPrefix = 0;\r
+        apInputPinRequirements.cBuffers = 0;\r
+\r
+        m_pInputPin->GetAllocatorRequirements(&apInputPinRequirements);\r
+\r
+        // A zero allignment does not make any sense.\r
+        if(0 == apInputPinRequirements.cbAlign) {\r
+            apInputPinRequirements.cbAlign = 1;\r
+        }\r
+\r
+        hr = m_pAllocator->Decommit();\r
+        if(FAILED(hr)) {\r
+            return hr;\r
+        }\r
+\r
+        hr = DecideBufferSize(m_pAllocator,  &apInputPinRequirements);\r
+        if(FAILED(hr)) {\r
+            return hr;\r
+        }\r
+\r
+        hr = m_pAllocator->Commit();\r
+        if(FAILED(hr)) {\r
+            return hr;\r
+        }\r
+\r
+        hr = m_pInputPin->NotifyAllocator(m_pAllocator, m_bPinUsesReadOnlyAllocator);\r
+        if(FAILED(hr)) {\r
+            return hr;\r
+        }\r
+    }\r
+\r
+    return S_OK;\r
+}\r
+\r
+// this method has to be called from the thread that is pushing data,\r
+// and it's the caller's responsibility to make sure that the thread\r
+// has no outstand samples because they cannot be delivered after a\r
+// reconnect\r
+//\r
+HRESULT CDynamicOutputPin::DynamicReconnect( const CMediaType* pmt )\r
+{\r
+    // The caller should call StartUsingOutputPin() before calling this\r
+    // method.\r
+    ASSERT(StreamingThreadUsingOutputPin());\r
+\r
+    if((m_pGraphConfig == NULL) || (NULL == m_hStopEvent)) {\r
+        return E_FAIL;\r
+    }\r
+\r
+    HRESULT hr = m_pGraphConfig->Reconnect(\r
+        this,\r
+        NULL,\r
+        pmt,\r
+        NULL,\r
+        m_hStopEvent,\r
+        AM_GRAPH_CONFIG_RECONNECT_CACHE_REMOVED_FILTERS );\r
+\r
+    return hr;\r
+}\r
+\r
+HRESULT CDynamicOutputPin::CompleteConnect(IPin *pReceivePin)\r
+{\r
+    HRESULT hr = CBaseOutputPin::CompleteConnect(pReceivePin);\r
+    if(SUCCEEDED(hr)) {\r
+        if(!IsStopped() && m_pAllocator) {\r
+            hr = m_pAllocator->Commit();\r
+            ASSERT(hr != VFW_E_ALREADY_COMMITTED);\r
+        }\r
+    }\r
+\r
+    return hr;\r
+}\r
+\r
+#ifdef DEBUG\r
+void CDynamicOutputPin::AssertValid(void)\r
+{\r
+    // Make sure the object was correctly initialized.\r
+\r
+    // This ASSERT only fires if the object failed to initialize\r
+    // and the user ignored the constructor's return code (phr).\r
+    ASSERT(NULL != m_hUnblockOutputPinEvent);\r
+\r
+    // If either of these ASSERTs fire, the user did not correctly call\r
+    // SetConfigInfo().\r
+    ASSERT(NULL != m_hStopEvent);\r
+    ASSERT(NULL != m_pGraphConfig);\r
+\r
+    // Make sure the block state is consistent.\r
+\r
+    CAutoLock alBlockStateLock(&m_BlockStateLock);\r
+\r
+    // BLOCK_STATE variables only have three legal values: PENDING, BLOCKED and NOT_BLOCKED.\r
+    ASSERT((NOT_BLOCKED == m_BlockState) || (PENDING == m_BlockState) || (BLOCKED == m_BlockState));\r
+\r
+    // m_hNotifyCallerPinBlockedEvent is only needed when a block operation cannot complete\r
+    // immediately.\r
+    ASSERT(((NULL == m_hNotifyCallerPinBlockedEvent) && (PENDING != m_BlockState)) ||\r
+           ((NULL != m_hNotifyCallerPinBlockedEvent) && (PENDING == m_BlockState)) );\r
+\r
+    // m_dwBlockCallerThreadID should always be 0 if the pin is not blocked and\r
+    // the user is not trying to block the pin.\r
+    ASSERT((0 == m_dwBlockCallerThreadID) || (NOT_BLOCKED != m_BlockState));\r
+\r
+    // If this ASSERT fires, the streaming thread is using the output pin and the\r
+    // output pin is blocked.\r
+    ASSERT(((0 != m_dwNumOutstandingOutputPinUsers) && (BLOCKED != m_BlockState)) ||\r
+           ((0 == m_dwNumOutstandingOutputPinUsers) && (NOT_BLOCKED != m_BlockState)) ||\r
+           ((0 == m_dwNumOutstandingOutputPinUsers) && (NOT_BLOCKED == m_BlockState)) );\r
+}\r
+#endif // DEBUG\r
+\r
+HRESULT CDynamicOutputPin::WaitEvent(HANDLE hEvent)\r
+{\r
+    const DWORD EVENT_SIGNALED = WAIT_OBJECT_0;\r
+\r
+    DWORD dwReturnValue = ::WaitForSingleObject(hEvent, INFINITE);\r
+\r
+    switch( dwReturnValue ) {\r
+    case EVENT_SIGNALED:\r
+        return S_OK;\r
+\r
+    case WAIT_FAILED:\r
+        return AmGetLastErrorToHResult();\r
+\r
+    default:\r
+        DbgBreak( "An Unexpected case occured in CDynamicOutputPin::WaitEvent()." );\r
+        return E_UNEXPECTED;\r
+    }\r
+}\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Implements CBaseAllocator\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+\r
+/* Constructor overrides the default settings for the free list to request\r
+   that it be alertable (ie the list can be cast to a handle which can be\r
+   passed to WaitForSingleObject). Both of the allocator lists also ask for\r
+   object locking, the all list matches the object default settings but I\r
+   have included them here just so it is obvious what kind of list it is */\r
+\r
+CBaseAllocator::CBaseAllocator(__in_opt LPCTSTR pName,\r
+                               __inout_opt LPUNKNOWN pUnk,\r
+                               __inout HRESULT *phr,\r
+                               BOOL bEvent,\r
+                               BOOL fEnableReleaseCallback\r
+                               ) :\r
+    CUnknown(pName, pUnk),\r
+    m_lAllocated(0),\r
+    m_bChanged(FALSE),\r
+    m_bCommitted(FALSE),\r
+    m_bDecommitInProgress(FALSE),\r
+    m_lSize(0),\r
+    m_lCount(0),\r
+    m_lAlignment(0),\r
+    m_lPrefix(0),\r
+    m_hSem(NULL),\r
+    m_lWaiting(0),\r
+    m_fEnableReleaseCallback(fEnableReleaseCallback),\r
+    m_pNotify(NULL)\r
+{\r
+#ifdef DXMPERF\r
+    PERFLOG_CTOR( pName ? pName : L"CBaseAllocator", (IMemAllocator *) this );\r
+#endif // DXMPERF\r
+\r
+    if (bEvent) {\r
+        m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);\r
+        if (m_hSem == NULL) {\r
+            *phr = E_OUTOFMEMORY;\r
+            return;\r
+        }\r
+    }\r
+}\r
+\r
+#ifdef UNICODE\r
+CBaseAllocator::CBaseAllocator(__in_opt LPCSTR pName,\r
+                               __inout_opt LPUNKNOWN pUnk,\r
+                               __inout HRESULT *phr,\r
+                               BOOL bEvent,\r
+                               BOOL fEnableReleaseCallback) :\r
+    CUnknown(pName, pUnk),\r
+    m_lAllocated(0),\r
+    m_bChanged(FALSE),\r
+    m_bCommitted(FALSE),\r
+    m_bDecommitInProgress(FALSE),\r
+    m_lSize(0),\r
+    m_lCount(0),\r
+    m_lAlignment(0),\r
+    m_lPrefix(0),\r
+    m_hSem(NULL),\r
+    m_lWaiting(0),\r
+    m_fEnableReleaseCallback(fEnableReleaseCallback),\r
+    m_pNotify(NULL)\r
+{\r
+#ifdef DXMPERF\r
+    PERFLOG_CTOR( L"CBaseAllocator", (IMemAllocator *) this );\r
+#endif // DXMPERF\r
+\r
+    if (bEvent) {\r
+        m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);\r
+        if (m_hSem == NULL) {\r
+            *phr = E_OUTOFMEMORY;\r
+            return;\r
+        }\r
+    }\r
+}\r
+#endif\r
+\r
+/* Destructor */\r
+\r
+CBaseAllocator::~CBaseAllocator()\r
+{\r
+    // we can't call Decommit here since that would mean a call to a\r
+    // pure virtual in destructor.\r
+    // We must assume that the derived class has gone into decommit state in\r
+    // its destructor.\r
+#ifdef DXMPERF\r
+    PERFLOG_DTOR( L"CBaseAllocator", (IMemAllocator *) this );\r
+#endif // DXMPERF\r
+\r
+    ASSERT(!m_bCommitted);\r
+    if (m_hSem != NULL) {\r
+        EXECUTE_ASSERT(CloseHandle(m_hSem));\r
+    }\r
+    if (m_pNotify) {\r
+        m_pNotify->Release();\r
+    }\r
+}\r
+\r
+\r
+/* Override this to publicise our interfaces */\r
+\r
+STDMETHODIMP\r
+CBaseAllocator::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    /* Do we know about this interface */\r
+\r
+    if (riid == IID_IMemAllocator ||\r
+        riid == IID_IMemAllocatorCallbackTemp && m_fEnableReleaseCallback) {\r
+        return GetInterface((IMemAllocatorCallbackTemp *) this, ppv);\r
+    } else {\r
+        return CUnknown::NonDelegatingQueryInterface(riid, ppv);\r
+    }\r
+}\r
+\r
+\r
+/* This sets the size and count of the required samples. The memory isn't\r
+   actually allocated until Commit() is called, if memory has already been\r
+   allocated then assuming no samples are outstanding the user may call us\r
+   to change the buffering, the memory will be released in Commit() */\r
+\r
+STDMETHODIMP\r
+CBaseAllocator::SetProperties(\r
+                __in ALLOCATOR_PROPERTIES* pRequest,\r
+                __out ALLOCATOR_PROPERTIES* pActual)\r
+{\r
+    CheckPointer(pRequest, E_POINTER);\r
+    CheckPointer(pActual, E_POINTER);\r
+    ValidateReadWritePtr(pActual, sizeof(ALLOCATOR_PROPERTIES));\r
+    CAutoLock cObjectLock(this);\r
+\r
+    ZeroMemory(pActual, sizeof(ALLOCATOR_PROPERTIES));\r
+\r
+    ASSERT(pRequest->cbBuffer > 0);\r
+\r
+    /*  Check the alignment requested */\r
+    if (pRequest->cbAlign != 1) {\r
+        DbgLog((LOG_ERROR, 2, TEXT("Alignment requested was 0x%x, not 1"),\r
+               pRequest->cbAlign));\r
+        return VFW_E_BADALIGN;\r
+    }\r
+\r
+    /* Can't do this if already committed, there is an argument that says we\r
+       should not reject the SetProperties call if there are buffers still\r
+       active. However this is called by the source filter, which is the same\r
+       person who is holding the samples. Therefore it is not unreasonable\r
+       for them to free all their samples before changing the requirements */\r
+\r
+    if (m_bCommitted) {\r
+        return VFW_E_ALREADY_COMMITTED;\r
+    }\r
+\r
+    /* Must be no outstanding buffers */\r
+\r
+    if (m_lAllocated != m_lFree.GetCount()) {\r
+        return VFW_E_BUFFERS_OUTSTANDING;\r
+    }\r
+\r
+    /* There isn't any real need to check the parameters as they\r
+       will just be rejected when the user finally calls Commit */\r
+\r
+    pActual->cbBuffer = m_lSize = pRequest->cbBuffer;\r
+    pActual->cBuffers = m_lCount = pRequest->cBuffers;\r
+    pActual->cbAlign = m_lAlignment = pRequest->cbAlign;\r
+    pActual->cbPrefix = m_lPrefix = pRequest->cbPrefix;\r
+\r
+    m_bChanged = TRUE;\r
+    return NOERROR;\r
+}\r
+\r
+STDMETHODIMP\r
+CBaseAllocator::GetProperties(\r
+    __out ALLOCATOR_PROPERTIES * pActual)\r
+{\r
+    CheckPointer(pActual,E_POINTER);\r
+    ValidateReadWritePtr(pActual,sizeof(ALLOCATOR_PROPERTIES));\r
+\r
+    CAutoLock cObjectLock(this);\r
+    pActual->cbBuffer = m_lSize;\r
+    pActual->cBuffers = m_lCount;\r
+    pActual->cbAlign = m_lAlignment;\r
+    pActual->cbPrefix = m_lPrefix;\r
+    return NOERROR;\r
+}\r
+\r
+// get container for a sample. Blocking, synchronous call to get the\r
+// next free buffer (as represented by an IMediaSample interface).\r
+// on return, the time etc properties will be invalid, but the buffer\r
+// pointer and size will be correct.\r
+\r
+HRESULT CBaseAllocator::GetBuffer(__deref_out IMediaSample **ppBuffer,\r
+                                  __in_opt REFERENCE_TIME *pStartTime,\r
+                                  __in_opt REFERENCE_TIME *pEndTime,\r
+                                  DWORD dwFlags\r
+                                  )\r
+{\r
+    UNREFERENCED_PARAMETER(pStartTime);\r
+    UNREFERENCED_PARAMETER(pEndTime);\r
+    UNREFERENCED_PARAMETER(dwFlags);\r
+    CMediaSample *pSample;\r
+\r
+    *ppBuffer = NULL;\r
+    for (;;)\r
+    {\r
+        {  // scope for lock\r
+            CAutoLock cObjectLock(this);\r
+\r
+            /* Check we are committed */\r
+            if (!m_bCommitted) {\r
+                return VFW_E_NOT_COMMITTED;\r
+            }\r
+            pSample = (CMediaSample *) m_lFree.RemoveHead();\r
+            if (pSample == NULL) {\r
+                SetWaiting();\r
+            }\r
+        }\r
+\r
+        /* If we didn't get a sample then wait for the list to signal */\r
+\r
+        if (pSample) {\r
+            break;\r
+        }\r
+        if (dwFlags & AM_GBF_NOWAIT) {\r
+            return VFW_E_TIMEOUT;\r
+        }\r
+        ASSERT(m_hSem != NULL);\r
+        WaitForSingleObject(m_hSem, INFINITE);\r
+    }\r
+\r
+    /* Addref the buffer up to one. On release\r
+       back to zero instead of being deleted, it will requeue itself by\r
+       calling the ReleaseBuffer member function. NOTE the owner of a\r
+       media sample must always be derived from CBaseAllocator */\r
+\r
+\r
+    ASSERT(pSample->m_cRef == 0);\r
+    pSample->m_cRef = 1;\r
+    *ppBuffer = pSample;\r
+\r
+#ifdef DXMPERF\r
+    PERFLOG_GETBUFFER( (IMemAllocator *) this, pSample );\r
+#endif // DXMPERF\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+/* Final release of a CMediaSample will call this */\r
+\r
+STDMETHODIMP\r
+CBaseAllocator::ReleaseBuffer(IMediaSample * pSample)\r
+{\r
+    CheckPointer(pSample,E_POINTER);\r
+    ValidateReadPtr(pSample,sizeof(IMediaSample));\r
+\r
+#ifdef DXMPERF\r
+    PERFLOG_RELBUFFER( (IMemAllocator *) this, pSample );\r
+#endif // DXMPERF\r
+\r
+\r
+    BOOL bRelease = FALSE;\r
+    {\r
+        CAutoLock cal(this);\r
+\r
+        /* Put back on the free list */\r
+\r
+        m_lFree.Add((CMediaSample *)pSample);\r
+        if (m_lWaiting != 0) {\r
+            NotifySample();\r
+        }\r
+\r
+        // if there is a pending Decommit, then we need to complete it by\r
+        // calling Free() when the last buffer is placed on the free list\r
+\r
+        LONG l1 = m_lFree.GetCount();\r
+        if (m_bDecommitInProgress && (l1 == m_lAllocated)) {\r
+            Free();\r
+            m_bDecommitInProgress = FALSE;\r
+            bRelease = TRUE;\r
+        }\r
+    }\r
+\r
+    if (m_pNotify) {\r
+\r
+        ASSERT(m_fEnableReleaseCallback);\r
+\r
+        //\r
+        // Note that this is not synchronized with setting up a notification\r
+        // method.\r
+        //\r
+        m_pNotify->NotifyRelease();\r
+    }\r
+\r
+    /* For each buffer there is one AddRef, made in GetBuffer and released\r
+       here. This may cause the allocator and all samples to be deleted */\r
+\r
+    if (bRelease) {\r
+        Release();\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+STDMETHODIMP\r
+CBaseAllocator::SetNotify(\r
+    IMemAllocatorNotifyCallbackTemp* pNotify\r
+    )\r
+{\r
+    ASSERT(m_fEnableReleaseCallback);\r
+    CAutoLock lck(this);\r
+    if (pNotify) {\r
+        pNotify->AddRef();\r
+    }\r
+    if (m_pNotify) {\r
+        m_pNotify->Release();\r
+    }\r
+    m_pNotify = pNotify;\r
+    return S_OK;\r
+}\r
+\r
+STDMETHODIMP\r
+CBaseAllocator::GetFreeCount(\r
+    __out LONG* plBuffersFree\r
+    )\r
+{\r
+    ASSERT(m_fEnableReleaseCallback);\r
+    CAutoLock cObjectLock(this);\r
+    *plBuffersFree = m_lCount - m_lAllocated + m_lFree.GetCount();\r
+    return NOERROR;\r
+}\r
+\r
+void\r
+CBaseAllocator::NotifySample()\r
+{\r
+    if (m_lWaiting != 0) {\r
+        ASSERT(m_hSem != NULL);\r
+        ReleaseSemaphore(m_hSem, m_lWaiting, 0);\r
+        m_lWaiting = 0;\r
+    }\r
+}\r
+\r
+STDMETHODIMP\r
+CBaseAllocator::Commit()\r
+{\r
+    /* Check we are not decommitted */\r
+    CAutoLock cObjectLock(this);\r
+\r
+    // cannot need to alloc or re-alloc if we are committed\r
+    if (m_bCommitted) {\r
+        return NOERROR;\r
+    }\r
+\r
+    /* Allow GetBuffer calls */\r
+\r
+    m_bCommitted = TRUE;\r
+\r
+    // is there a pending decommit ? if so, just cancel it\r
+    if (m_bDecommitInProgress) {\r
+        m_bDecommitInProgress = FALSE;\r
+\r
+        // don't call Alloc at this point. He cannot allow SetProperties\r
+        // between Decommit and the last free, so the buffer size cannot have\r
+        // changed. And because some of the buffers are not free yet, he\r
+        // cannot re-alloc anyway.\r
+        return NOERROR;\r
+    }\r
+\r
+    DbgLog((LOG_MEMORY, 1, TEXT("Allocating: %ldx%ld"), m_lCount, m_lSize));\r
+\r
+    // actually need to allocate the samples\r
+    HRESULT hr = Alloc();\r
+    if (FAILED(hr)) {\r
+        m_bCommitted = FALSE;\r
+        return hr;\r
+    }\r
+    AddRef();\r
+    return NOERROR;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CBaseAllocator::Decommit()\r
+{\r
+    BOOL bRelease = FALSE;\r
+    {\r
+        /* Check we are not already decommitted */\r
+        CAutoLock cObjectLock(this);\r
+        if (m_bCommitted == FALSE) {\r
+            if (m_bDecommitInProgress == FALSE) {\r
+                return NOERROR;\r
+            }\r
+        }\r
+\r
+        /* No more GetBuffer calls will succeed */\r
+        m_bCommitted = FALSE;\r
+\r
+        // are any buffers outstanding?\r
+        if (m_lFree.GetCount() < m_lAllocated) {\r
+            // please complete the decommit when last buffer is freed\r
+            m_bDecommitInProgress = TRUE;\r
+        } else {\r
+            m_bDecommitInProgress = FALSE;\r
+\r
+            // need to complete the decommit here as there are no\r
+            // outstanding buffers\r
+\r
+            Free();\r
+            bRelease = TRUE;\r
+        }\r
+\r
+        // Tell anyone waiting that they can go now so we can\r
+        // reject their call\r
+#pragma warning(push)\r
+#ifndef _PREFAST_\r
+#pragma warning(disable:4068)\r
+#endif\r
+#pragma prefast(suppress:__WARNING_DEREF_NULL_PTR, "Suppress warning related to Free() invalidating 'this' which is no applicable to CBaseAllocator::Free()")\r
+        NotifySample();\r
+\r
+#pragma warning(pop)\r
+    }\r
+\r
+    if (bRelease) {\r
+        Release();\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+/* Base definition of allocation which checks we are ok to go ahead and do\r
+   the full allocation. We return S_FALSE if the requirements are the same */\r
+\r
+HRESULT\r
+CBaseAllocator::Alloc(void)\r
+{\r
+    /* Error if he hasn't set the size yet */\r
+    if (m_lCount <= 0 || m_lSize <= 0 || m_lAlignment <= 0) {\r
+        return VFW_E_SIZENOTSET;\r
+    }\r
+\r
+    /* should never get here while buffers outstanding */\r
+    ASSERT(m_lFree.GetCount() == m_lAllocated);\r
+\r
+    /* If the requirements haven't changed then don't reallocate */\r
+    if (m_bChanged == FALSE) {\r
+        return S_FALSE;\r
+    }\r
+\r
+    return NOERROR;\r
+}\r
+\r
+/*  Implement CBaseAllocator::CSampleList::Remove(pSample)\r
+    Removes pSample from the list\r
+*/\r
+void\r
+CBaseAllocator::CSampleList::Remove(__inout CMediaSample * pSample)\r
+{\r
+    CMediaSample **pSearch;\r
+    for (pSearch = &m_List;\r
+         *pSearch != NULL;\r
+         pSearch = &(CBaseAllocator::NextSample(*pSearch))) {\r
+       if (*pSearch == pSample) {\r
+           *pSearch = CBaseAllocator::NextSample(pSample);\r
+           CBaseAllocator::NextSample(pSample) = NULL;\r
+           m_nOnList--;\r
+           return;\r
+       }\r
+    }\r
+    DbgBreak("Couldn't find sample in list");\r
+}\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Implements CMemAllocator\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+\r
+/* This goes in the factory template table to create new instances */\r
+CUnknown *CMemAllocator::CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr)\r
+{\r
+    CUnknown *pUnkRet = new CMemAllocator(NAME("CMemAllocator"), pUnk, phr);\r
+    return pUnkRet;\r
+}\r
+\r
+CMemAllocator::CMemAllocator(\r
+    __in_opt LPCTSTR pName,\r
+    __inout_opt LPUNKNOWN pUnk,\r
+    __inout HRESULT *phr)\r
+    : CBaseAllocator(pName, pUnk, phr, TRUE, TRUE),\r
+    m_pBuffer(NULL)\r
+{\r
+}\r
+\r
+#ifdef UNICODE\r
+CMemAllocator::CMemAllocator(\r
+    __in_opt LPCSTR pName,\r
+    __inout_opt LPUNKNOWN pUnk,\r
+    __inout HRESULT *phr)\r
+    : CBaseAllocator(pName, pUnk, phr, TRUE, TRUE),\r
+    m_pBuffer(NULL)\r
+{\r
+}\r
+#endif\r
+\r
+/* This sets the size and count of the required samples. The memory isn't\r
+   actually allocated until Commit() is called, if memory has already been\r
+   allocated then assuming no samples are outstanding the user may call us\r
+   to change the buffering, the memory will be released in Commit() */\r
+STDMETHODIMP\r
+CMemAllocator::SetProperties(\r
+                __in ALLOCATOR_PROPERTIES* pRequest,\r
+                __out ALLOCATOR_PROPERTIES* pActual)\r
+{\r
+    CheckPointer(pActual,E_POINTER);\r
+    ValidateReadWritePtr(pActual,sizeof(ALLOCATOR_PROPERTIES));\r
+    CAutoLock cObjectLock(this);\r
+\r
+    ZeroMemory(pActual, sizeof(ALLOCATOR_PROPERTIES));\r
+\r
+    ASSERT(pRequest->cbBuffer > 0);\r
+\r
+    SYSTEM_INFO SysInfo;\r
+    GetSystemInfo(&SysInfo);\r
+\r
+    /*  Check the alignment request is a power of 2 */\r
+    if ((-pRequest->cbAlign & pRequest->cbAlign) != pRequest->cbAlign) {\r
+        DbgLog((LOG_ERROR, 1, TEXT("Alignment requested 0x%x not a power of 2!"),\r
+               pRequest->cbAlign));\r
+    }\r
+    /*  Check the alignment requested */\r
+    if (pRequest->cbAlign == 0 ||\r
+    (SysInfo.dwAllocationGranularity & (pRequest->cbAlign - 1)) != 0) {\r
+        DbgLog((LOG_ERROR, 1, TEXT("Invalid alignment 0x%x requested - granularity = 0x%x"),\r
+               pRequest->cbAlign, SysInfo.dwAllocationGranularity));\r
+        return VFW_E_BADALIGN;\r
+    }\r
+\r
+    /* Can't do this if already committed, there is an argument that says we\r
+       should not reject the SetProperties call if there are buffers still\r
+       active. However this is called by the source filter, which is the same\r
+       person who is holding the samples. Therefore it is not unreasonable\r
+       for them to free all their samples before changing the requirements */\r
+\r
+    if (m_bCommitted == TRUE) {\r
+        return VFW_E_ALREADY_COMMITTED;\r
+    }\r
+\r
+    /* Must be no outstanding buffers */\r
+\r
+    if (m_lFree.GetCount() < m_lAllocated) {\r
+        return VFW_E_BUFFERS_OUTSTANDING;\r
+    }\r
+\r
+    /* There isn't any real need to check the parameters as they\r
+       will just be rejected when the user finally calls Commit */\r
+\r
+    // round length up to alignment - remember that prefix is included in\r
+    // the alignment\r
+    LONG lSize = pRequest->cbBuffer + pRequest->cbPrefix;\r
+    LONG lRemainder = lSize % pRequest->cbAlign;\r
+    if (lRemainder != 0) {\r
+        lSize = lSize - lRemainder + pRequest->cbAlign;\r
+    }\r
+    pActual->cbBuffer = m_lSize = (lSize - pRequest->cbPrefix);\r
+\r
+    pActual->cBuffers = m_lCount = pRequest->cBuffers;\r
+    pActual->cbAlign = m_lAlignment = pRequest->cbAlign;\r
+    pActual->cbPrefix = m_lPrefix = pRequest->cbPrefix;\r
+\r
+    m_bChanged = TRUE;\r
+    return NOERROR;\r
+}\r
+\r
+// override this to allocate our resources when Commit is called.\r
+//\r
+// note that our resources may be already allocated when this is called,\r
+// since we don't free them on Decommit. We will only be called when in\r
+// decommit state with all buffers free.\r
+//\r
+// object locked by caller\r
+HRESULT\r
+CMemAllocator::Alloc(void)\r
+{\r
+    CAutoLock lck(this);\r
+\r
+    /* Check he has called SetProperties */\r
+    HRESULT hr = CBaseAllocator::Alloc();\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    /* If the requirements haven't changed then don't reallocate */\r
+    if (hr == S_FALSE) {\r
+        ASSERT(m_pBuffer);\r
+        return NOERROR;\r
+    }\r
+    ASSERT(hr == S_OK); // we use this fact in the loop below\r
+\r
+    /* Free the old resources */\r
+    if (m_pBuffer) {\r
+        ReallyFree();\r
+    }\r
+\r
+    /* Make sure we've got reasonable values */\r
+    if ( m_lSize < 0 || m_lPrefix < 0 || m_lCount < 0 ) {\r
+        return E_OUTOFMEMORY;\r
+    }\r
+\r
+    /* Compute the aligned size */\r
+    LONG lAlignedSize = m_lSize + m_lPrefix;\r
+\r
+    /*  Check overflow */\r
+    if (lAlignedSize < m_lSize) {\r
+        return E_OUTOFMEMORY;\r
+    }\r
+\r
+    if (m_lAlignment > 1) {\r
+        LONG lRemainder = lAlignedSize % m_lAlignment;\r
+        if (lRemainder != 0) {\r
+            LONG lNewSize = lAlignedSize + m_lAlignment - lRemainder;\r
+            if (lNewSize < lAlignedSize) {\r
+                return E_OUTOFMEMORY;\r
+            }\r
+            lAlignedSize = lNewSize;\r
+        }\r
+    }\r
+\r
+    /* Create the contiguous memory block for the samples\r
+       making sure it's properly aligned (64K should be enough!)\r
+    */\r
+    ASSERT(lAlignedSize % m_lAlignment == 0);\r
+\r
+    LONGLONG lToAllocate = m_lCount * (LONGLONG)lAlignedSize;\r
+\r
+    /*  Check overflow */\r
+    if (lToAllocate > MAXLONG) {\r
+        return E_OUTOFMEMORY;\r
+    }\r
+\r
+    m_pBuffer = (PBYTE)VirtualAlloc(NULL,\r
+                    (LONG)lToAllocate,\r
+                    MEM_COMMIT,\r
+                    PAGE_READWRITE);\r
+\r
+    if (m_pBuffer == NULL) {\r
+        return E_OUTOFMEMORY;\r
+    }\r
+\r
+    LPBYTE pNext = m_pBuffer;\r
+    CMediaSample *pSample;\r
+\r
+    ASSERT(m_lAllocated == 0);\r
+\r
+    // Create the new samples - we have allocated m_lSize bytes for each sample\r
+    // plus m_lPrefix bytes per sample as a prefix. We set the pointer to\r
+    // the memory after the prefix - so that GetPointer() will return a pointer\r
+    // to m_lSize bytes.\r
+    for (; m_lAllocated < m_lCount; m_lAllocated++, pNext += lAlignedSize) {\r
+\r
+\r
+        pSample = new CMediaSample(\r
+                            NAME("Default memory media sample"),\r
+                this,\r
+                            &hr,\r
+                            pNext + m_lPrefix,      // GetPointer() value\r
+                            m_lSize);               // not including prefix\r
+\r
+            ASSERT(SUCCEEDED(hr));\r
+        if (pSample == NULL) {\r
+            return E_OUTOFMEMORY;\r
+        }\r
+\r
+        // This CANNOT fail\r
+        m_lFree.Add(pSample);\r
+    }\r
+\r
+    m_bChanged = FALSE;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// override this to free up any resources we have allocated.\r
+// called from the base class on Decommit when all buffers have been\r
+// returned to the free list.\r
+//\r
+// caller has already locked the object.\r
+\r
+// in our case, we keep the memory until we are deleted, so\r
+// we do nothing here. The memory is deleted in the destructor by\r
+// calling ReallyFree()\r
+void\r
+CMemAllocator::Free(void)\r
+{\r
+    return;\r
+}\r
+\r
+\r
+// called from the destructor (and from Alloc if changing size/count) to\r
+// actually free up the memory\r
+void\r
+CMemAllocator::ReallyFree(void)\r
+{\r
+    /* Should never be deleting this unless all buffers are freed */\r
+\r
+    ASSERT(m_lAllocated == m_lFree.GetCount());\r
+\r
+    /* Free up all the CMediaSamples */\r
+\r
+    CMediaSample *pSample;\r
+    for (;;) {\r
+        pSample = m_lFree.RemoveHead();\r
+        if (pSample != NULL) {\r
+            delete pSample;\r
+        } else {\r
+            break;\r
+        }\r
+    }\r
+\r
+    m_lAllocated = 0;\r
+\r
+    // free the block of buffer memory\r
+    if (m_pBuffer) {\r
+        EXECUTE_ASSERT(VirtualFree(m_pBuffer, 0, MEM_RELEASE));\r
+        m_pBuffer = NULL;\r
+    }\r
+}\r
+\r
+\r
+/* Destructor frees our memory resources */\r
+\r
+CMemAllocator::~CMemAllocator()\r
+{\r
+    Decommit();\r
+    ReallyFree();\r
+}\r
+\r
+// ------------------------------------------------------------------------\r
+// filter registration through IFilterMapper. used if IFilterMapper is\r
+// not found (Quartz 1.0 install)\r
+\r
+STDAPI\r
+AMovieSetupRegisterFilter( const AMOVIESETUP_FILTER * const psetupdata\r
+                         , IFilterMapper *                  pIFM\r
+                         , BOOL                             bRegister  )\r
+{\r
+  DbgLog((LOG_TRACE, 3, TEXT("= AMovieSetupRegisterFilter")));\r
+\r
+  // check we've got data\r
+  //\r
+  if( NULL == psetupdata ) return S_FALSE;\r
+\r
+\r
+  // unregister filter\r
+  // (as pins are subkeys of filter's CLSID key\r
+  // they do not need to be removed separately).\r
+  //\r
+  DbgLog((LOG_TRACE, 3, TEXT("= = unregister filter")));\r
+  HRESULT hr = pIFM->UnregisterFilter( *(psetupdata->clsID) );\r
+\r
+\r
+  if( bRegister )\r
+  {\r
+    // register filter\r
+    //\r
+    DbgLog((LOG_TRACE, 3, TEXT("= = register filter")));\r
+    hr = pIFM->RegisterFilter( *(psetupdata->clsID)\r
+                             , psetupdata->strName\r
+                             , psetupdata->dwMerit    );\r
+    if( SUCCEEDED(hr) )\r
+    {\r
+      // all its pins\r
+      //\r
+      DbgLog((LOG_TRACE, 3, TEXT("= = register filter pins")));\r
+      for( UINT m1=0; m1 < psetupdata->nPins; m1++ )\r
+      {\r
+        hr = pIFM->RegisterPin( *(psetupdata->clsID)\r
+                              , psetupdata->lpPin[m1].strName\r
+                              , psetupdata->lpPin[m1].bRendered\r
+                              , psetupdata->lpPin[m1].bOutput\r
+                              , psetupdata->lpPin[m1].bZero\r
+                              , psetupdata->lpPin[m1].bMany\r
+                              , *(psetupdata->lpPin[m1].clsConnectsToFilter)\r
+                              , psetupdata->lpPin[m1].strConnectsToPin );\r
+\r
+        if( SUCCEEDED(hr) )\r
+        {\r
+          // and each pin's media types\r
+          //\r
+          DbgLog((LOG_TRACE, 3, TEXT("= = register filter pin types")));\r
+          for( UINT m2=0; m2 < psetupdata->lpPin[m1].nMediaTypes; m2++ )\r
+          {\r
+            hr = pIFM->RegisterPinType( *(psetupdata->clsID)\r
+                                      , psetupdata->lpPin[m1].strName\r
+                                      , *(psetupdata->lpPin[m1].lpMediaType[m2].clsMajorType)\r
+                                      , *(psetupdata->lpPin[m1].lpMediaType[m2].clsMinorType) );\r
+            if( FAILED(hr) ) break;\r
+          }\r
+          if( FAILED(hr) ) break;\r
+        }\r
+        if( FAILED(hr) ) break;\r
+      }\r
+    }\r
+  }\r
+\r
+  // handle one acceptable "error" - that\r
+  // of filter not being registered!\r
+  // (couldn't find a suitable #define'd\r
+  // name for the error!)\r
+  //\r
+  if( 0x80070002 == hr)\r
+    return NOERROR;\r
+  else\r
+    return hr;\r
+}\r
+\r
+//  Remove warnings about unreferenced inline functions\r
+#pragma warning(disable:4514)\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/amfilter.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/amfilter.h
new file mode 100644 (file)
index 0000000..14f17cd
--- /dev/null
@@ -0,0 +1,1587 @@
+//------------------------------------------------------------------------------\r
+// File: AMFilter.h\r
+//\r
+// Desc: DirectShow base classes - efines class hierarchy for streams\r
+//       architecture.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef __FILTER__\r
+#define __FILTER__\r
+\r
+/* The following classes are declared in this header: */\r
+\r
+class CBaseMediaFilter;     // IMediaFilter support\r
+class CBaseFilter;          // IBaseFilter,IMediaFilter support\r
+class CBasePin;             // Abstract base class for IPin interface\r
+class CEnumPins;            // Enumerate input and output pins\r
+class CEnumMediaTypes;      // Enumerate the pin's preferred formats\r
+class CBaseOutputPin;       // Adds data provider member functions\r
+class CBaseInputPin;        // Implements IMemInputPin interface\r
+class CMediaSample;         // Basic transport unit for IMemInputPin\r
+class CBaseAllocator;       // General list guff for most allocators\r
+class CMemAllocator;        // Implements memory buffer allocation\r
+\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+//\r
+// QueryFilterInfo and QueryPinInfo AddRef the interface pointers\r
+// they return.  You can use the macro below to release the interface.\r
+//\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+#define QueryFilterInfoReleaseGraph(fi) if ((fi).pGraph) (fi).pGraph->Release();\r
+\r
+#define QueryPinInfoReleaseFilter(pi) if ((pi).pFilter) (pi).pFilter->Release();\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Defines CBaseMediaFilter\r
+//\r
+// Abstract base class implementing IMediaFilter.\r
+//\r
+// Typically you will derive your filter from CBaseFilter rather than\r
+// this,  unless you are implementing an object such as a plug-in\r
+// distributor that needs to support IMediaFilter but not IBaseFilter.\r
+//\r
+// Note that IMediaFilter is derived from IPersist to allow query of\r
+// class id.\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+class AM_NOVTABLE CBaseMediaFilter : public CUnknown,\r
+                                     public IMediaFilter\r
+{\r
+\r
+protected:\r
+\r
+    FILTER_STATE    m_State;            // current state: running, paused\r
+    IReferenceClock *m_pClock;          // this filter's reference clock\r
+    // note: all filters in a filter graph use the same clock\r
+\r
+    // offset from stream time to reference time\r
+    CRefTime        m_tStart;\r
+\r
+    CLSID          m_clsid;            // This filters clsid\r
+                                        // used for serialization\r
+    CCritSec        *m_pLock;           // Object we use for locking\r
+\r
+public:\r
+\r
+    CBaseMediaFilter(\r
+        __in_opt LPCTSTR pName,\r
+        __inout_opt LPUNKNOWN pUnk,\r
+        __in CCritSec  *pLock,\r
+       REFCLSID   clsid);\r
+\r
+    virtual ~CBaseMediaFilter();\r
+\r
+    DECLARE_IUNKNOWN\r
+\r
+    // override this to say what interfaces we support where\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv);\r
+\r
+    //\r
+    // --- IPersist method ---\r
+    //\r
+\r
+    STDMETHODIMP GetClassID(__out CLSID *pClsID);\r
+\r
+    // --- IMediaFilter methods ---\r
+\r
+    STDMETHODIMP GetState(DWORD dwMSecs, __out FILTER_STATE *State);\r
+\r
+    STDMETHODIMP SetSyncSource(__inout_opt IReferenceClock *pClock);\r
+\r
+    STDMETHODIMP GetSyncSource(__deref_out_opt IReferenceClock **pClock);\r
+\r
+    // default implementation of Stop and Pause just record the\r
+    // state. Override to activate or de-activate your filter.\r
+    // Note that Run when called from Stopped state will call Pause\r
+    // to ensure activation, so if you are a source or transform\r
+    // you will probably not need to override Run.\r
+    STDMETHODIMP Stop();\r
+    STDMETHODIMP Pause();\r
+\r
+\r
+    // the start parameter is the difference to be added to the\r
+    // sample's stream time to get the reference time for\r
+    // its presentation\r
+    STDMETHODIMP Run(REFERENCE_TIME tStart);\r
+\r
+    // --- helper methods ---\r
+\r
+    // return the current stream time - ie find out what\r
+    // stream time should be appearing now\r
+    virtual HRESULT StreamTime(CRefTime& rtStream);\r
+\r
+    // Is the filter currently active? (running or paused)\r
+    BOOL IsActive() {\r
+        CAutoLock cObjectLock(m_pLock);\r
+        return ((m_State == State_Paused) || (m_State == State_Running));\r
+    };\r
+};\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Defines CBaseFilter\r
+//\r
+// An abstract class providing basic IBaseFilter support for pin\r
+// enumeration and filter information reading.\r
+//\r
+// We cannot derive from CBaseMediaFilter since methods in IMediaFilter\r
+// are also in IBaseFilter and would be ambiguous. Since much of the code\r
+// assumes that they derive from a class that has m_State and other state\r
+// directly available, we duplicate code from CBaseMediaFilter rather than\r
+// having a member variable.\r
+//\r
+// Derive your filter from this, or from a derived object such as\r
+// CTransformFilter.\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+\r
+class AM_NOVTABLE CBaseFilter : public CUnknown,        // Handles an IUnknown\r
+                    public IBaseFilter,     // The Filter Interface\r
+                    public IAMovieSetup     // For un/registration\r
+{\r
+\r
+friend class CBasePin;\r
+\r
+protected:\r
+    FILTER_STATE    m_State;            // current state: running, paused\r
+    IReferenceClock *m_pClock;          // this graph's ref clock\r
+    CRefTime        m_tStart;           // offset from stream time to reference time\r
+    CLSID          m_clsid;            // This filters clsid\r
+                                        // used for serialization\r
+    CCritSec        *m_pLock;           // Object we use for locking\r
+\r
+    WCHAR           *m_pName;           // Full filter name\r
+    IFilterGraph    *m_pGraph;          // Graph we belong to\r
+    IMediaEventSink *m_pSink;           // Called with notify events\r
+    LONG            m_PinVersion;       // Current pin version\r
+\r
+public:\r
+\r
+    CBaseFilter(\r
+        __in_opt LPCTSTR pName,   // Object description\r
+        __inout_opt LPUNKNOWN pUnk,  // IUnknown of delegating object\r
+        __in CCritSec  *pLock,    // Object who maintains lock\r
+       REFCLSID   clsid);        // The clsid to be used to serialize this filter\r
+\r
+    CBaseFilter(\r
+        __in_opt LPCTSTR pName,    // Object description\r
+        __in_opt LPUNKNOWN pUnk,  // IUnknown of delegating object\r
+        __in CCritSec  *pLock,    // Object who maintains lock\r
+       REFCLSID   clsid,         // The clsid to be used to serialize this filter\r
+        __inout HRESULT   *phr);  // General OLE return code\r
+#ifdef UNICODE\r
+    CBaseFilter(\r
+        __in_opt LPCSTR pName,    // Object description\r
+        __in_opt LPUNKNOWN pUnk,  // IUnknown of delegating object\r
+        __in CCritSec  *pLock,    // Object who maintains lock\r
+       REFCLSID   clsid);        // The clsid to be used to serialize this filter\r
+\r
+    CBaseFilter(\r
+        __in_opt LPCSTR pName,     // Object description\r
+        __in_opt LPUNKNOWN pUnk,  // IUnknown of delegating object\r
+        __in CCritSec  *pLock,    // Object who maintains lock\r
+       REFCLSID   clsid,         // The clsid to be used to serialize this filter\r
+        __inout HRESULT   *phr);  // General OLE return code\r
+#endif\r
+    ~CBaseFilter();\r
+\r
+    DECLARE_IUNKNOWN\r
+\r
+    // override this to say what interfaces we support where\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv);\r
+#ifdef DEBUG\r
+    STDMETHODIMP_(ULONG) NonDelegatingRelease();\r
+#endif\r
+\r
+    //\r
+    // --- IPersist method ---\r
+    //\r
+\r
+    STDMETHODIMP GetClassID(__out CLSID *pClsID);\r
+\r
+    // --- IMediaFilter methods ---\r
+\r
+    STDMETHODIMP GetState(DWORD dwMSecs, __out FILTER_STATE *State);\r
+\r
+    STDMETHODIMP SetSyncSource(__in_opt IReferenceClock *pClock);\r
+\r
+    STDMETHODIMP GetSyncSource(__deref_out_opt IReferenceClock **pClock);\r
+\r
+\r
+    // override Stop and Pause so we can activate the pins.\r
+    // Note that Run will call Pause first if activation needed.\r
+    // Override these if you want to activate your filter rather than\r
+    // your pins.\r
+    STDMETHODIMP Stop();\r
+    STDMETHODIMP Pause();\r
+\r
+    // the start parameter is the difference to be added to the\r
+    // sample's stream time to get the reference time for\r
+    // its presentation\r
+    STDMETHODIMP Run(REFERENCE_TIME tStart);\r
+\r
+    // --- helper methods ---\r
+\r
+    // return the current stream time - ie find out what\r
+    // stream time should be appearing now\r
+    virtual HRESULT StreamTime(CRefTime& rtStream);\r
+\r
+    // Is the filter currently active?\r
+    BOOL IsActive() {\r
+        CAutoLock cObjectLock(m_pLock);\r
+        return ((m_State == State_Paused) || (m_State == State_Running));\r
+    };\r
+\r
+    // Is this filter stopped (without locking)\r
+    BOOL IsStopped() {\r
+        return (m_State == State_Stopped);\r
+    };\r
+\r
+    //\r
+    // --- IBaseFilter methods ---\r
+    //\r
+\r
+    // pin enumerator\r
+    STDMETHODIMP EnumPins(\r
+                    __deref_out IEnumPins ** ppEnum);\r
+\r
+\r
+    // default behaviour of FindPin assumes pin ids are their names\r
+    STDMETHODIMP FindPin(\r
+        LPCWSTR Id,\r
+        __deref_out IPin ** ppPin\r
+    );\r
+\r
+    STDMETHODIMP QueryFilterInfo(\r
+                    __out FILTER_INFO * pInfo);\r
+\r
+    STDMETHODIMP JoinFilterGraph(\r
+                    __inout_opt IFilterGraph * pGraph,\r
+                    __in_opt LPCWSTR pName);\r
+\r
+    // return a Vendor information string. Optional - may return E_NOTIMPL.\r
+    // memory returned should be freed using CoTaskMemFree\r
+    // default implementation returns E_NOTIMPL\r
+    STDMETHODIMP QueryVendorInfo(\r
+                    __deref_out LPWSTR* pVendorInfo\r
+            );\r
+\r
+    // --- helper methods ---\r
+\r
+    // send an event notification to the filter graph if we know about it.\r
+    // returns S_OK if delivered, S_FALSE if the filter graph does not sink\r
+    // events, or an error otherwise.\r
+    HRESULT NotifyEvent(\r
+        long EventCode,\r
+        LONG_PTR EventParam1,\r
+        LONG_PTR EventParam2);\r
+\r
+    // return the filter graph we belong to\r
+    __out_opt IFilterGraph *GetFilterGraph() {\r
+        return m_pGraph;\r
+    }\r
+\r
+    // Request reconnect\r
+    // pPin is the pin to reconnect\r
+    // pmt is the type to reconnect with - can be NULL\r
+    // Calls ReconnectEx on the filter graph\r
+    HRESULT ReconnectPin(IPin *pPin, __in_opt AM_MEDIA_TYPE const *pmt);\r
+\r
+    // find out the current pin version (used by enumerators)\r
+    virtual LONG GetPinVersion();\r
+    void IncrementPinVersion();\r
+\r
+    // you need to supply these to access the pins from the enumerator\r
+    // and for default Stop and Pause/Run activation.\r
+    virtual int GetPinCount() PURE;\r
+    virtual CBasePin *GetPin(int n) PURE;\r
+\r
+    // --- IAMovieSetup methods ---\r
+\r
+    STDMETHODIMP Register();    // ask filter to register itself\r
+    STDMETHODIMP Unregister();  // and unregister itself\r
+\r
+    // --- setup helper methods ---\r
+    // (override to return filters setup data)\r
+\r
+    virtual __out_opt LPAMOVIESETUP_FILTER GetSetupData(){ return NULL; }\r
+\r
+};\r
+\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Defines CBasePin\r
+//\r
+// Abstract class that supports the basics of IPin\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+class  AM_NOVTABLE CBasePin : public CUnknown, public IPin, public IQualityControl\r
+{\r
+\r
+protected:\r
+\r
+    WCHAR *         m_pName;                   // This pin's name\r
+    IPin            *m_Connected;               // Pin we have connected to\r
+    PIN_DIRECTION   m_dir;                      // Direction of this pin\r
+    CCritSec        *m_pLock;                   // Object we use for locking\r
+    bool            m_bRunTimeError;            // Run time error generated\r
+    bool            m_bCanReconnectWhenActive;  // OK to reconnect when active\r
+    bool            m_bTryMyTypesFirst;         // When connecting enumerate\r
+                                                // this pin's types first\r
+    CBaseFilter    *m_pFilter;                  // Filter we were created by\r
+    IQualityControl *m_pQSink;                  // Target for Quality messages\r
+    LONG            m_TypeVersion;              // Holds current type version\r
+    CMediaType      m_mt;                       // Media type of connection\r
+\r
+    CRefTime        m_tStart;                   // time from NewSegment call\r
+    CRefTime        m_tStop;                    // time from NewSegment\r
+    double          m_dRate;                    // rate from NewSegment\r
+\r
+#ifdef DEBUG\r
+    LONG            m_cRef;                     // Ref count tracing\r
+#endif\r
+\r
+    // displays pin connection information\r
+\r
+#ifdef DEBUG\r
+    void DisplayPinInfo(IPin *pReceivePin);\r
+    void DisplayTypeInfo(IPin *pPin, const CMediaType *pmt);\r
+#else\r
+    void DisplayPinInfo(IPin *pReceivePin) {};\r
+    void DisplayTypeInfo(IPin *pPin, const CMediaType *pmt) {};\r
+#endif\r
+\r
+    // used to agree a media type for a pin connection\r
+\r
+    // given a specific media type, attempt a connection (includes\r
+    // checking that the type is acceptable to this pin)\r
+    HRESULT\r
+    AttemptConnection(\r
+        IPin* pReceivePin,      // connect to this pin\r
+        const CMediaType* pmt   // using this type\r
+    );\r
+\r
+    // try all the media types in this enumerator - for each that\r
+    // we accept, try to connect using ReceiveConnection.\r
+    HRESULT TryMediaTypes(\r
+                        IPin *pReceivePin,          // connect to this pin\r
+                        __in_opt const CMediaType *pmt,  // proposed type from Connect\r
+                        IEnumMediaTypes *pEnum);    // try this enumerator\r
+\r
+    // establish a connection with a suitable mediatype. Needs to\r
+    // propose a media type if the pmt pointer is null or partially\r
+    // specified - use TryMediaTypes on both our and then the other pin's\r
+    // enumerator until we find one that works.\r
+    HRESULT AgreeMediaType(\r
+                        IPin *pReceivePin,      // connect to this pin\r
+                        const CMediaType *pmt);      // proposed type from Connect\r
+\r
+public:\r
+\r
+    CBasePin(\r
+        __in_opt LPCTSTR pObjectName,         // Object description\r
+        __in CBaseFilter *pFilter,       // Owning filter who knows about pins\r
+        __in CCritSec *pLock,            // Object who implements the lock\r
+        __inout HRESULT *phr,               // General OLE return code\r
+        __in_opt LPCWSTR pName,              // Pin name for us\r
+        PIN_DIRECTION dir);         // Either PINDIR_INPUT or PINDIR_OUTPUT\r
+#ifdef UNICODE\r
+    CBasePin(\r
+        __in_opt LPCSTR pObjectName,         // Object description\r
+        __in CBaseFilter *pFilter,       // Owning filter who knows about pins\r
+        __in CCritSec *pLock,            // Object who implements the lock\r
+        __inout HRESULT *phr,               // General OLE return code\r
+        __in_opt LPCWSTR pName,              // Pin name for us\r
+        PIN_DIRECTION dir);         // Either PINDIR_INPUT or PINDIR_OUTPUT\r
+#endif\r
+    virtual ~CBasePin();\r
+\r
+    DECLARE_IUNKNOWN\r
+\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv);\r
+    STDMETHODIMP_(ULONG) NonDelegatingRelease();\r
+    STDMETHODIMP_(ULONG) NonDelegatingAddRef();\r
+\r
+    // --- IPin methods ---\r
+\r
+    // take lead role in establishing a connection. Media type pointer\r
+    // may be null, or may point to partially-specified mediatype\r
+    // (subtype or format type may be GUID_NULL).\r
+    STDMETHODIMP Connect(\r
+        IPin * pReceivePin,\r
+        __in_opt const AM_MEDIA_TYPE *pmt   // optional media type\r
+    );\r
+\r
+    // (passive) accept a connection from another pin\r
+    STDMETHODIMP ReceiveConnection(\r
+        IPin * pConnector,      // this is the initiating connecting pin\r
+        const AM_MEDIA_TYPE *pmt   // this is the media type we will exchange\r
+    );\r
+\r
+    STDMETHODIMP Disconnect();\r
+\r
+    STDMETHODIMP ConnectedTo(__deref_out IPin **pPin);\r
+\r
+    STDMETHODIMP ConnectionMediaType(__out AM_MEDIA_TYPE *pmt);\r
+\r
+    STDMETHODIMP QueryPinInfo(\r
+        __out PIN_INFO * pInfo\r
+    );\r
+\r
+    STDMETHODIMP QueryDirection(\r
+       __out PIN_DIRECTION * pPinDir\r
+    );\r
+\r
+    STDMETHODIMP QueryId(\r
+        __deref_out LPWSTR * Id\r
+    );\r
+\r
+    // does the pin support this media type\r
+    STDMETHODIMP QueryAccept(\r
+        const AM_MEDIA_TYPE *pmt\r
+    );\r
+\r
+    // return an enumerator for this pins preferred media types\r
+    STDMETHODIMP EnumMediaTypes(\r
+        __deref_out IEnumMediaTypes **ppEnum\r
+    );\r
+\r
+    // return an array of IPin* - the pins that this pin internally connects to\r
+    // All pins put in the array must be AddReffed (but no others)\r
+    // Errors: "Can't say" - FAIL, not enough slots - return S_FALSE\r
+    // Default: return E_NOTIMPL\r
+    // The filter graph will interpret NOT_IMPL as any input pin connects to\r
+    // all visible output pins and vice versa.\r
+    // apPin can be NULL if nPin==0 (not otherwise).\r
+    STDMETHODIMP QueryInternalConnections(\r
+        __out_ecount_part(*nPin,*nPin) IPin* *apPin,     // array of IPin*\r
+        __inout ULONG *nPin                  // on input, the number of slots\r
+                                             // on output  the number of pins\r
+    ) { return E_NOTIMPL; }\r
+\r
+    // Called when no more data will be sent\r
+    STDMETHODIMP EndOfStream(void);\r
+\r
+    // Begin/EndFlush still PURE\r
+\r
+    // NewSegment notifies of the start/stop/rate applying to the data\r
+    // about to be received. Default implementation records data and\r
+    // returns S_OK.\r
+    // Override this to pass downstream.\r
+    STDMETHODIMP NewSegment(\r
+                    REFERENCE_TIME tStart,\r
+                    REFERENCE_TIME tStop,\r
+                    double dRate);\r
+\r
+    //================================================================================\r
+    // IQualityControl methods\r
+    //================================================================================\r
+\r
+    STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);\r
+\r
+    STDMETHODIMP SetSink(IQualityControl * piqc);\r
+\r
+    // --- helper methods ---\r
+\r
+    // Returns true if the pin is connected. false otherwise.\r
+    BOOL IsConnected(void) {return (m_Connected != NULL); };\r
+    // Return the pin this is connected to (if any)\r
+    IPin * GetConnected() { return m_Connected; };\r
+\r
+    // Check if our filter is currently stopped\r
+    BOOL IsStopped() {\r
+        return (m_pFilter->m_State == State_Stopped);\r
+    };\r
+\r
+    // find out the current type version (used by enumerators)\r
+    virtual LONG GetMediaTypeVersion();\r
+    void IncrementTypeVersion();\r
+\r
+    // switch the pin to active (paused or running) mode\r
+    // not an error to call this if already active\r
+    virtual HRESULT Active(void);\r
+\r
+    // switch the pin to inactive state - may already be inactive\r
+    virtual HRESULT Inactive(void);\r
+\r
+    // Notify of Run() from filter\r
+    virtual HRESULT Run(REFERENCE_TIME tStart);\r
+\r
+    // check if the pin can support this specific proposed type and format\r
+    virtual HRESULT CheckMediaType(const CMediaType *) PURE;\r
+\r
+    // set the connection to use this format (previously agreed)\r
+    virtual HRESULT SetMediaType(const CMediaType *);\r
+\r
+    // check that the connection is ok before verifying it\r
+    // can be overridden eg to check what interfaces will be supported.\r
+    virtual HRESULT CheckConnect(IPin *);\r
+\r
+    // Set and release resources required for a connection\r
+    virtual HRESULT BreakConnect();\r
+    virtual HRESULT CompleteConnect(IPin *pReceivePin);\r
+\r
+    // returns the preferred formats for a pin\r
+    virtual HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType);\r
+\r
+    // access to NewSegment values\r
+    REFERENCE_TIME CurrentStopTime() {\r
+        return m_tStop;\r
+    }\r
+    REFERENCE_TIME CurrentStartTime() {\r
+        return m_tStart;\r
+    }\r
+    double CurrentRate() {\r
+        return m_dRate;\r
+    }\r
+\r
+    //  Access name\r
+    LPWSTR Name() { return m_pName; };\r
+\r
+    //  Can reconnectwhen active?\r
+    void SetReconnectWhenActive(bool bCanReconnect)\r
+    {\r
+        m_bCanReconnectWhenActive = bCanReconnect;\r
+    }\r
+\r
+    bool CanReconnectWhenActive()\r
+    {\r
+        return m_bCanReconnectWhenActive;\r
+    }\r
+\r
+protected:\r
+    STDMETHODIMP DisconnectInternal();\r
+};\r
+\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Defines CEnumPins\r
+//\r
+// Pin enumerator class that works by calling CBaseFilter. This interface\r
+// is provided by CBaseFilter::EnumPins and calls GetPinCount() and\r
+// GetPin() to enumerate existing pins. Needs to be a separate object so\r
+// that it can be cloned (creating an existing object at the same\r
+// position in the enumeration)\r
+//\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+class CEnumPins : public IEnumPins      // The interface we support\r
+{\r
+    int m_Position;                 // Current ordinal position\r
+    int m_PinCount;                 // Number of pins available\r
+    CBaseFilter *m_pFilter;         // The filter who owns us\r
+    LONG m_Version;                 // Pin version information\r
+    LONG m_cRef;\r
+\r
+    typedef CGenericList<CBasePin> CPinList;\r
+\r
+    CPinList m_PinCache;           // These pointers have not been AddRef'ed and\r
+                                   // so they should not be dereferenced.  They are\r
+                                   // merely kept to ID which pins have been enumerated.\r
+\r
+#ifdef DEBUG\r
+    DWORD m_dwCookie;\r
+#endif\r
+\r
+    /* If while we are retrieving a pin for example from the filter an error\r
+       occurs we assume that our internal state is stale with respect to the\r
+       filter (someone may have deleted all the pins). We can check before\r
+       starting whether or not the operation is likely to fail by asking the\r
+       filter what it's current version number is. If the filter has not\r
+       overriden the GetPinVersion method then this will always match */\r
+\r
+    BOOL AreWeOutOfSync() {\r
+        return (m_pFilter->GetPinVersion() == m_Version ? FALSE : TRUE);\r
+    };\r
+\r
+    /* This method performs the same operations as Reset, except is does not clear\r
+       the cache of pins already enumerated. */\r
+\r
+    STDMETHODIMP Refresh();\r
+\r
+public:\r
+\r
+    CEnumPins(\r
+        __in CBaseFilter *pFilter,\r
+        __in_opt CEnumPins *pEnumPins);\r
+\r
+    virtual ~CEnumPins();\r
+\r
+    // IUnknown\r
+    STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv);\r
+    STDMETHODIMP_(ULONG) AddRef();\r
+    STDMETHODIMP_(ULONG) Release();\r
+\r
+    // IEnumPins\r
+    STDMETHODIMP Next(\r
+        ULONG cPins,         // place this many pins...\r
+        __out_ecount(cPins) IPin ** ppPins,    // ...in this array of IPin*\r
+        __out_opt ULONG * pcFetched    // actual count passed returned here\r
+    );\r
+\r
+    STDMETHODIMP Skip(ULONG cPins);\r
+    STDMETHODIMP Reset();\r
+    STDMETHODIMP Clone(__deref_out IEnumPins **ppEnum);\r
+\r
+\r
+};\r
+\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Defines CEnumMediaTypes\r
+//\r
+// Enumerates the preferred formats for input and output pins\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+class CEnumMediaTypes : public IEnumMediaTypes    // The interface we support\r
+{\r
+    int m_Position;           // Current ordinal position\r
+    CBasePin *m_pPin;         // The pin who owns us\r
+    LONG m_Version;           // Media type version value\r
+    LONG m_cRef;\r
+#ifdef DEBUG\r
+    DWORD m_dwCookie;\r
+#endif\r
+\r
+    /* The media types a filter supports can be quite dynamic so we add to\r
+       the general IEnumXXXX interface the ability to be signaled when they\r
+       change via an event handle the connected filter supplies. Until the\r
+       Reset method is called after the state changes all further calls to\r
+       the enumerator (except Reset) will return E_UNEXPECTED error code */\r
+\r
+    BOOL AreWeOutOfSync() {\r
+        return (m_pPin->GetMediaTypeVersion() == m_Version ? FALSE : TRUE);\r
+    };\r
+\r
+public:\r
+\r
+    CEnumMediaTypes(\r
+        __in CBasePin *pPin,\r
+        __in_opt CEnumMediaTypes *pEnumMediaTypes);\r
+\r
+    virtual ~CEnumMediaTypes();\r
+\r
+    // IUnknown\r
+    STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv);\r
+    STDMETHODIMP_(ULONG) AddRef();\r
+    STDMETHODIMP_(ULONG) Release();\r
+\r
+    // IEnumMediaTypes\r
+    STDMETHODIMP Next(\r
+        ULONG cMediaTypes,          // place this many pins...\r
+        __out_ecount(cMediaTypes) AM_MEDIA_TYPE ** ppMediaTypes,  // ...in this array\r
+        __out_opt ULONG * pcFetched           // actual count passed\r
+    );\r
+\r
+    STDMETHODIMP Skip(ULONG cMediaTypes);\r
+    STDMETHODIMP Reset();\r
+    STDMETHODIMP Clone(__deref_out IEnumMediaTypes **ppEnum);\r
+};\r
+\r
+\r
+\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Defines CBaseOutputPin\r
+//\r
+// class derived from CBasePin that can pass buffers to a connected pin\r
+// that supports IMemInputPin. Supports IPin.\r
+//\r
+// Derive your output pin from this.\r
+//\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+class  AM_NOVTABLE CBaseOutputPin : public CBasePin\r
+{\r
+\r
+protected:\r
+\r
+    IMemAllocator *m_pAllocator;\r
+    IMemInputPin *m_pInputPin;        // interface on the downstreaminput pin\r
+                                      // set up in CheckConnect when we connect.\r
+\r
+public:\r
+\r
+    CBaseOutputPin(\r
+        __in_opt LPCTSTR pObjectName,\r
+        __in CBaseFilter *pFilter,\r
+        __in CCritSec *pLock,\r
+        __inout HRESULT *phr,\r
+        __in_opt LPCWSTR pName);\r
+#ifdef UNICODE\r
+    CBaseOutputPin(\r
+        __in_opt LPCSTR pObjectName,\r
+        __in CBaseFilter *pFilter,\r
+        __in CCritSec *pLock,\r
+        __inout HRESULT *phr,\r
+        __in_opt LPCWSTR pName);\r
+#endif\r
+    // override CompleteConnect() so we can negotiate an allocator\r
+    virtual HRESULT CompleteConnect(IPin *pReceivePin);\r
+\r
+    // negotiate the allocator and its buffer size/count and other properties\r
+    // Calls DecideBufferSize to set properties\r
+    virtual HRESULT DecideAllocator(IMemInputPin * pPin, __deref_out IMemAllocator ** pAlloc);\r
+\r
+    // override this to set the buffer size and count. Return an error\r
+    // if the size/count is not to your liking.\r
+    // The allocator properties passed in are those requested by the\r
+    // input pin - use eg the alignment and prefix members if you have\r
+    // no preference on these.\r
+    virtual HRESULT DecideBufferSize(\r
+        IMemAllocator * pAlloc,\r
+        __inout ALLOCATOR_PROPERTIES * ppropInputRequest\r
+    ) PURE;\r
+\r
+    // returns an empty sample buffer from the allocator\r
+    virtual HRESULT GetDeliveryBuffer(__deref_out IMediaSample ** ppSample,\r
+                                      __in_opt REFERENCE_TIME * pStartTime,\r
+                                      __in_opt REFERENCE_TIME * pEndTime,\r
+                                      DWORD dwFlags);\r
+\r
+    // deliver a filled-in sample to the connected input pin\r
+    // note - you need to release it after calling this. The receiving\r
+    // pin will addref the sample if it needs to hold it beyond the\r
+    // call.\r
+    virtual HRESULT Deliver(IMediaSample *);\r
+\r
+    // override this to control the connection\r
+    virtual HRESULT InitAllocator(__deref_out IMemAllocator **ppAlloc);\r
+    HRESULT CheckConnect(IPin *pPin);\r
+    HRESULT BreakConnect();\r
+\r
+    // override to call Commit and Decommit\r
+    HRESULT Active(void);\r
+    HRESULT Inactive(void);\r
+\r
+    // we have a default handling of EndOfStream which is to return\r
+    // an error, since this should be called on input pins only\r
+    STDMETHODIMP EndOfStream(void);\r
+\r
+    // called from elsewhere in our filter to pass EOS downstream to\r
+    // our connected input pin\r
+    virtual HRESULT DeliverEndOfStream(void);\r
+\r
+    // same for Begin/EndFlush - we handle Begin/EndFlush since it\r
+    // is an error on an output pin, and we have Deliver methods to\r
+    // call the methods on the connected pin\r
+    STDMETHODIMP BeginFlush(void);\r
+    STDMETHODIMP EndFlush(void);\r
+    virtual HRESULT DeliverBeginFlush(void);\r
+    virtual HRESULT DeliverEndFlush(void);\r
+\r
+    // deliver NewSegment to connected pin - you will need to\r
+    // override this if you queue any data in your output pin.\r
+    virtual HRESULT DeliverNewSegment(\r
+                        REFERENCE_TIME tStart,\r
+                        REFERENCE_TIME tStop,\r
+                        double dRate);\r
+\r
+    //================================================================================\r
+    // IQualityControl methods\r
+    //================================================================================\r
+\r
+    // All inherited from CBasePin and not overridden here.\r
+    // STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);\r
+    // STDMETHODIMP SetSink(IQualityControl * piqc);\r
+};\r
+\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Defines CBaseInputPin\r
+//\r
+// derive your standard input pin from this.\r
+// you need to supply GetMediaType and CheckConnect etc (see CBasePin),\r
+// and you need to supply Receive to do something more useful.\r
+//\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+class AM_NOVTABLE CBaseInputPin : public CBasePin,\r
+                                  public IMemInputPin\r
+{\r
+\r
+protected:\r
+\r
+    IMemAllocator *m_pAllocator;    // Default memory allocator\r
+\r
+    // allocator is read-only, so received samples\r
+    // cannot be modified (probably only relevant to in-place\r
+    // transforms\r
+    BYTE m_bReadOnly;\r
+\r
+    // in flushing state (between BeginFlush and EndFlush)\r
+    // if TRUE, all Receives are returned with S_FALSE\r
+    BYTE m_bFlushing;\r
+\r
+    // Sample properties - initalized in Receive\r
+    AM_SAMPLE2_PROPERTIES m_SampleProps;\r
+\r
+public:\r
+\r
+    CBaseInputPin(\r
+        __in_opt LPCTSTR pObjectName,\r
+        __in CBaseFilter *pFilter,\r
+        __in CCritSec *pLock,\r
+        __inout HRESULT *phr,\r
+        __in_opt LPCWSTR pName);\r
+#ifdef UNICODE\r
+    CBaseInputPin(\r
+        __in_opt LPCSTR pObjectName,\r
+        __in CBaseFilter *pFilter,\r
+        __in CCritSec *pLock,\r
+        __inout HRESULT *phr,\r
+        __in_opt LPCWSTR pName);\r
+#endif\r
+    virtual ~CBaseInputPin();\r
+\r
+    DECLARE_IUNKNOWN\r
+\r
+    // override this to publicise our interfaces\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);\r
+\r
+    // return the allocator interface that this input pin\r
+    // would like the output pin to use\r
+    STDMETHODIMP GetAllocator(__deref_out IMemAllocator ** ppAllocator);\r
+\r
+    // tell the input pin which allocator the output pin is actually\r
+    // going to use.\r
+    STDMETHODIMP NotifyAllocator(\r
+                    IMemAllocator * pAllocator,\r
+                    BOOL bReadOnly);\r
+\r
+    // do something with this media sample\r
+    STDMETHODIMP Receive(IMediaSample *pSample);\r
+\r
+    // do something with these media samples\r
+    STDMETHODIMP ReceiveMultiple (\r
+        __in_ecount(nSamples) IMediaSample **pSamples,\r
+        long nSamples,\r
+        __out long *nSamplesProcessed);\r
+\r
+    // See if Receive() blocks\r
+    STDMETHODIMP ReceiveCanBlock();\r
+\r
+    // Default handling for BeginFlush - call at the beginning\r
+    // of your implementation (makes sure that all Receive calls\r
+    // fail). After calling this, you need to free any queued data\r
+    // and then call downstream.\r
+    STDMETHODIMP BeginFlush(void);\r
+\r
+    // default handling for EndFlush - call at end of your implementation\r
+    // - before calling this, ensure that there is no queued data and no thread\r
+    // pushing any more without a further receive, then call downstream,\r
+    // then call this method to clear the m_bFlushing flag and re-enable\r
+    // receives\r
+    STDMETHODIMP EndFlush(void);\r
+\r
+    // this method is optional (can return E_NOTIMPL).\r
+    // default implementation returns E_NOTIMPL. Override if you have\r
+    // specific alignment or prefix needs, but could use an upstream\r
+    // allocator\r
+    STDMETHODIMP GetAllocatorRequirements(__out ALLOCATOR_PROPERTIES*pProps);\r
+\r
+    // Release the pin's allocator.\r
+    HRESULT BreakConnect();\r
+\r
+    // helper method to check the read-only flag\r
+    BOOL IsReadOnly() {\r
+        return m_bReadOnly;\r
+    };\r
+\r
+    // helper method to see if we are flushing\r
+    BOOL IsFlushing() {\r
+        return m_bFlushing;\r
+    };\r
+\r
+    //  Override this for checking whether it's OK to process samples\r
+    //  Also call this from EndOfStream.\r
+    virtual HRESULT CheckStreaming();\r
+\r
+    // Pass a Quality notification on to the appropriate sink\r
+    HRESULT PassNotify(Quality& q);\r
+\r
+\r
+    //================================================================================\r
+    // IQualityControl methods (from CBasePin)\r
+    //================================================================================\r
+\r
+    STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);\r
+\r
+    // no need to override:\r
+    // STDMETHODIMP SetSink(IQualityControl * piqc);\r
+\r
+\r
+    // switch the pin to inactive state - may already be inactive\r
+    virtual HRESULT Inactive(void);\r
+\r
+    // Return sample properties pointer\r
+    AM_SAMPLE2_PROPERTIES * SampleProps() {\r
+        ASSERT(m_SampleProps.cbData != 0);\r
+        return &m_SampleProps;\r
+    }\r
+\r
+};\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+// CDynamicOutputPin\r
+//\r
+\r
+class CDynamicOutputPin : public CBaseOutputPin,\r
+                          public IPinFlowControl\r
+{\r
+public:\r
+#ifdef UNICODE\r
+    CDynamicOutputPin(\r
+        __in_opt LPCSTR pObjectName,\r
+        __in CBaseFilter *pFilter,\r
+        __in CCritSec *pLock,\r
+        __inout HRESULT *phr,\r
+        __in_opt LPCWSTR pName);\r
+#endif\r
+\r
+    CDynamicOutputPin(\r
+        __in_opt LPCTSTR pObjectName,\r
+        __in CBaseFilter *pFilter,\r
+        __in CCritSec *pLock,\r
+        __inout HRESULT *phr,\r
+        __in_opt LPCWSTR pName);\r
+\r
+    ~CDynamicOutputPin();\r
+\r
+    // IUnknown Methods\r
+    DECLARE_IUNKNOWN\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);\r
+\r
+    // IPin Methods\r
+    STDMETHODIMP Disconnect(void);\r
+\r
+    // IPinFlowControl Methods\r
+    STDMETHODIMP Block(DWORD dwBlockFlags, HANDLE hEvent);\r
+\r
+    //  Set graph config info\r
+    void SetConfigInfo(IGraphConfig *pGraphConfig, HANDLE hStopEvent);\r
+\r
+    #ifdef DEBUG\r
+    virtual HRESULT Deliver(IMediaSample *pSample);\r
+    virtual HRESULT DeliverEndOfStream(void);\r
+    virtual HRESULT DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate);\r
+    #endif // DEBUG\r
+\r
+    HRESULT DeliverBeginFlush(void);\r
+    HRESULT DeliverEndFlush(void);\r
+\r
+    HRESULT Inactive(void);\r
+    HRESULT Active(void);\r
+    virtual HRESULT CompleteConnect(IPin *pReceivePin);\r
+\r
+    virtual HRESULT StartUsingOutputPin(void);\r
+    virtual void StopUsingOutputPin(void);\r
+    virtual bool StreamingThreadUsingOutputPin(void);\r
+\r
+    HRESULT ChangeOutputFormat\r
+        (\r
+        const AM_MEDIA_TYPE *pmt,\r
+        REFERENCE_TIME tSegmentStart,\r
+        REFERENCE_TIME tSegmentStop,\r
+        double dSegmentRate\r
+        );\r
+    HRESULT ChangeMediaType(const CMediaType *pmt);\r
+    HRESULT DynamicReconnect(const CMediaType *pmt);\r
+\r
+protected:\r
+    HRESULT SynchronousBlockOutputPin(void);\r
+    HRESULT AsynchronousBlockOutputPin(HANDLE hNotifyCallerPinBlockedEvent);\r
+    HRESULT UnblockOutputPin(void);\r
+\r
+    void BlockOutputPin(void);\r
+    void ResetBlockState(void);\r
+\r
+    static HRESULT WaitEvent(HANDLE hEvent);\r
+\r
+    enum BLOCK_STATE\r
+    {\r
+        NOT_BLOCKED,\r
+        PENDING,\r
+        BLOCKED\r
+    };\r
+\r
+    // This lock should be held when the following class members are\r
+    // being used: m_hNotifyCallerPinBlockedEvent, m_BlockState,\r
+    // m_dwBlockCallerThreadID and m_dwNumOutstandingOutputPinUsers.\r
+    CCritSec m_BlockStateLock;\r
+\r
+    // This event should be signaled when the output pin is\r
+    // not blocked.  This is a manual reset event.  For more\r
+    // information on events, see the documentation for\r
+    // CreateEvent() in the Windows SDK.\r
+    HANDLE m_hUnblockOutputPinEvent;\r
+\r
+    // This event will be signaled when block operation succeedes or\r
+    // when the user cancels the block operation.  The block operation\r
+    // can be canceled by calling IPinFlowControl2::Block( 0, NULL )\r
+    // while the block operation is pending.\r
+    HANDLE m_hNotifyCallerPinBlockedEvent;\r
+\r
+    // The state of the current block operation.\r
+    BLOCK_STATE m_BlockState;\r
+\r
+    // The ID of the thread which last called IPinFlowControl::Block().\r
+    // For more information on thread IDs, see the documentation for\r
+    // GetCurrentThreadID() in the Windows SDK.\r
+    DWORD m_dwBlockCallerThreadID;\r
+\r
+    // The number of times StartUsingOutputPin() has been sucessfully\r
+    // called and a corresponding call to StopUsingOutputPin() has not\r
+    // been made.  When this variable is greater than 0, the streaming\r
+    // thread is calling IPin::NewSegment(), IPin::EndOfStream(),\r
+    // IMemInputPin::Receive() or IMemInputPin::ReceiveMultiple().  The\r
+    // streaming thread could also be calling: DynamicReconnect(),\r
+    // ChangeMediaType() or ChangeOutputFormat().  The output pin cannot\r
+    // be blocked while the output pin is being used.\r
+    DWORD m_dwNumOutstandingOutputPinUsers;\r
+\r
+    // This event should be set when the IMediaFilter::Stop() is called.\r
+    // This is a manual reset event.  It is also set when the output pin\r
+    // delivers a flush to the connected input pin.\r
+    HANDLE m_hStopEvent;\r
+    IGraphConfig* m_pGraphConfig;\r
+\r
+    // TRUE if the output pin's allocator's samples are read only.\r
+    // Otherwise FALSE.  For more information, see the documentation\r
+    // for IMemInputPin::NotifyAllocator().\r
+    BOOL m_bPinUsesReadOnlyAllocator;\r
+\r
+private:\r
+    HRESULT Initialize(void);\r
+    HRESULT ChangeMediaTypeHelper(const CMediaType *pmt);\r
+\r
+    #ifdef DEBUG\r
+    void AssertValid(void);\r
+    #endif // DEBUG\r
+};\r
+\r
+class CAutoUsingOutputPin\r
+{\r
+public:\r
+    CAutoUsingOutputPin( __in CDynamicOutputPin* pOutputPin, __inout HRESULT* phr );\r
+    ~CAutoUsingOutputPin();\r
+\r
+private:\r
+    CDynamicOutputPin* m_pOutputPin;\r
+};\r
+\r
+inline CAutoUsingOutputPin::CAutoUsingOutputPin( __in CDynamicOutputPin* pOutputPin, __inout HRESULT* phr ) :\r
+    m_pOutputPin(NULL)\r
+{\r
+    // The caller should always pass in valid pointers.\r
+    ASSERT( NULL != pOutputPin );\r
+    ASSERT( NULL != phr );\r
+\r
+    // Make sure the user initialized phr.\r
+    ASSERT( S_OK == *phr );\r
+\r
+    HRESULT hr = pOutputPin->StartUsingOutputPin();\r
+    if( FAILED( hr ) )\r
+    {\r
+        *phr = hr;\r
+        return;\r
+    }\r
+\r
+    m_pOutputPin = pOutputPin;\r
+}\r
+\r
+inline CAutoUsingOutputPin::~CAutoUsingOutputPin()\r
+{\r
+    if( NULL != m_pOutputPin )\r
+    {\r
+        m_pOutputPin->StopUsingOutputPin();\r
+    }\r
+}\r
+\r
+#ifdef DEBUG\r
+\r
+inline HRESULT CDynamicOutputPin::Deliver(IMediaSample *pSample)\r
+{\r
+    // The caller should call StartUsingOutputPin() before calling this\r
+    // method.\r
+    ASSERT(StreamingThreadUsingOutputPin());\r
+\r
+    return CBaseOutputPin::Deliver(pSample);\r
+}\r
+\r
+inline HRESULT CDynamicOutputPin::DeliverEndOfStream(void)\r
+{\r
+    // The caller should call StartUsingOutputPin() before calling this\r
+    // method.\r
+    ASSERT( StreamingThreadUsingOutputPin() );\r
+\r
+    return CBaseOutputPin::DeliverEndOfStream();\r
+}\r
+\r
+inline HRESULT CDynamicOutputPin::DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)\r
+{\r
+    // The caller should call StartUsingOutputPin() before calling this\r
+    // method.\r
+    ASSERT(StreamingThreadUsingOutputPin());\r
+\r
+    return CBaseOutputPin::DeliverNewSegment(tStart, tStop, dRate);\r
+}\r
+\r
+#endif // DEBUG\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Memory allocators\r
+//\r
+// the shared memory transport between pins requires the input pin\r
+// to provide a memory allocator that can provide sample objects. A\r
+// sample object supports the IMediaSample interface.\r
+//\r
+// CBaseAllocator handles the management of free and busy samples. It\r
+// allocates CMediaSample objects. CBaseAllocator is an abstract class:\r
+// in particular it has no method of initializing the list of free\r
+// samples. CMemAllocator is derived from CBaseAllocator and initializes\r
+// the list of samples using memory from the standard IMalloc interface.\r
+//\r
+// If you want your buffers to live in some special area of memory,\r
+// derive your allocator object from CBaseAllocator. If you derive your\r
+// IMemInputPin interface object from CBaseMemInputPin, you will get\r
+// CMemAllocator-based allocation etc for free and will just need to\r
+// supply the Receive handling, and media type / format negotiation.\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Defines CMediaSample\r
+//\r
+// an object of this class supports IMediaSample and represents a buffer\r
+// for media data with some associated properties. Releasing it returns\r
+// it to a freelist managed by a CBaseAllocator derived object.\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+class CMediaSample : public IMediaSample2    // The interface we support\r
+{\r
+\r
+protected:\r
+\r
+    friend class CBaseAllocator;\r
+\r
+    /*  Values for dwFlags - these are used for backward compatiblity\r
+        only now - use AM_SAMPLE_xxx\r
+    */\r
+    enum { Sample_SyncPoint       = 0x01,   /* Is this a sync point */\r
+           Sample_Preroll         = 0x02,   /* Is this a preroll sample */\r
+           Sample_Discontinuity   = 0x04,   /* Set if start of new segment */\r
+           Sample_TypeChanged     = 0x08,   /* Has the type changed */\r
+           Sample_TimeValid       = 0x10,   /* Set if time is valid */\r
+           Sample_MediaTimeValid  = 0x20,   /* Is the media time valid */\r
+           Sample_TimeDiscontinuity = 0x40, /* Time discontinuity */\r
+           Sample_StopValid       = 0x100,  /* Stop time valid */\r
+           Sample_ValidFlags      = 0x1FF\r
+         };\r
+\r
+    /* Properties, the media sample class can be a container for a format\r
+       change in which case we take a copy of a type through the SetMediaType\r
+       interface function and then return it when GetMediaType is called. As\r
+       we do no internal processing on it we leave it as a pointer */\r
+\r
+    DWORD            m_dwFlags;         /* Flags for this sample */\r
+                                        /* Type specific flags are packed\r
+                                           into the top word\r
+                                        */\r
+    DWORD            m_dwTypeSpecificFlags; /* Media type specific flags */\r
+    __field_ecount_opt(m_cbBuffer) LPBYTE           m_pBuffer;         /* Pointer to the complete buffer */\r
+    LONG             m_lActual;         /* Length of data in this sample */\r
+    LONG             m_cbBuffer;        /* Size of the buffer */\r
+    CBaseAllocator  *m_pAllocator;      /* The allocator who owns us */\r
+    CMediaSample     *m_pNext;          /* Chaining in free list */\r
+    REFERENCE_TIME   m_Start;           /* Start sample time */\r
+    REFERENCE_TIME   m_End;             /* End sample time */\r
+    LONGLONG         m_MediaStart;      /* Real media start position */\r
+    LONG             m_MediaEnd;        /* A difference to get the end */\r
+    AM_MEDIA_TYPE    *m_pMediaType;     /* Media type change data */\r
+    DWORD            m_dwStreamId;      /* Stream id */\r
+public:\r
+    LONG             m_cRef;            /* Reference count */\r
+\r
+\r
+public:\r
+\r
+    CMediaSample(\r
+        __in_opt LPCTSTR pName,\r
+        __in_opt CBaseAllocator *pAllocator,\r
+        __inout_opt HRESULT *phr,\r
+        __in_bcount_opt(length) LPBYTE pBuffer = NULL,\r
+        LONG length = 0);\r
+#ifdef UNICODE\r
+    CMediaSample(\r
+        __in_opt LPCSTR pName,\r
+        __in_opt CBaseAllocator *pAllocator,\r
+        __inout_opt HRESULT *phr,\r
+        __in_bcount_opt(length) LPBYTE pBuffer = NULL,\r
+        LONG length = 0);\r
+#endif\r
+\r
+    virtual ~CMediaSample();\r
+\r
+    /* Note the media sample does not delegate to its owner */\r
+\r
+    STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv);\r
+    STDMETHODIMP_(ULONG) AddRef();\r
+    STDMETHODIMP_(ULONG) Release();\r
+\r
+    // set the buffer pointer and length. Used by allocators that\r
+    // want variable sized pointers or pointers into already-read data.\r
+    // This is only available through a CMediaSample* not an IMediaSample*\r
+    // and so cannot be changed by clients.\r
+    HRESULT SetPointer(__in_bcount(cBytes) BYTE * ptr, LONG cBytes);\r
+\r
+    // Get me a read/write pointer to this buffer's memory.\r
+    STDMETHODIMP GetPointer(__deref_out BYTE ** ppBuffer);\r
+\r
+    STDMETHODIMP_(LONG) GetSize(void);\r
+\r
+    // get the stream time at which this sample should start and finish.\r
+    STDMETHODIMP GetTime(\r
+        __out REFERENCE_TIME * pTimeStart,     // put time here\r
+        __out REFERENCE_TIME * pTimeEnd\r
+    );\r
+\r
+    // Set the stream time at which this sample should start and finish.\r
+    STDMETHODIMP SetTime(\r
+        __in_opt REFERENCE_TIME * pTimeStart,     // put time here\r
+        __in_opt REFERENCE_TIME * pTimeEnd\r
+    );\r
+    STDMETHODIMP IsSyncPoint(void);\r
+    STDMETHODIMP SetSyncPoint(BOOL bIsSyncPoint);\r
+    STDMETHODIMP IsPreroll(void);\r
+    STDMETHODIMP SetPreroll(BOOL bIsPreroll);\r
+\r
+    STDMETHODIMP_(LONG) GetActualDataLength(void);\r
+    STDMETHODIMP SetActualDataLength(LONG lActual);\r
+\r
+    // these allow for limited format changes in band\r
+\r
+    STDMETHODIMP GetMediaType(__deref_out AM_MEDIA_TYPE **ppMediaType);\r
+    STDMETHODIMP SetMediaType(__in_opt AM_MEDIA_TYPE *pMediaType);\r
+\r
+    // returns S_OK if there is a discontinuity in the data (this same is\r
+    // not a continuation of the previous stream of data\r
+    // - there has been a seek).\r
+    STDMETHODIMP IsDiscontinuity(void);\r
+    // set the discontinuity property - TRUE if this sample is not a\r
+    // continuation, but a new sample after a seek.\r
+    STDMETHODIMP SetDiscontinuity(BOOL bDiscontinuity);\r
+\r
+    // get the media times for this sample\r
+    STDMETHODIMP GetMediaTime(\r
+       __out LONGLONG * pTimeStart,\r
+           __out LONGLONG * pTimeEnd\r
+    );\r
+\r
+    // Set the media times for this sample\r
+    STDMETHODIMP SetMediaTime(\r
+       __in_opt LONGLONG * pTimeStart,\r
+           __in_opt LONGLONG * pTimeEnd\r
+    );\r
+\r
+    // Set and get properties (IMediaSample2)\r
+    STDMETHODIMP GetProperties(\r
+        DWORD cbProperties,\r
+        __out_bcount(cbProperties) BYTE * pbProperties\r
+    );\r
+\r
+    STDMETHODIMP SetProperties(\r
+        DWORD cbProperties,\r
+        __in_bcount(cbProperties) const BYTE * pbProperties\r
+    );\r
+};\r
+\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Defines CBaseAllocator\r
+//\r
+// Abstract base class that manages a list of media samples\r
+//\r
+// This class provides support for getting buffers from the free list,\r
+// including handling of commit and (asynchronous) decommit.\r
+//\r
+// Derive from this class and override the Alloc and Free functions to\r
+// allocate your CMediaSample (or derived) objects and add them to the\r
+// free list, preparing them as necessary.\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+class AM_NOVTABLE CBaseAllocator : public CUnknown,// A non delegating IUnknown\r
+                       public IMemAllocatorCallbackTemp, // The interface we support\r
+                       public CCritSec             // Provides object locking\r
+{\r
+    class CSampleList;\r
+    friend class CSampleList;\r
+\r
+    /*  Trick to get at protected member in CMediaSample */\r
+    static CMediaSample * &NextSample(__in CMediaSample *pSample)\r
+    {\r
+        return pSample->m_pNext;\r
+    };\r
+\r
+    /*  Mini list class for the free list */\r
+    class CSampleList\r
+    {\r
+    public:\r
+        CSampleList() : m_List(NULL), m_nOnList(0) {};\r
+#ifdef DEBUG\r
+        ~CSampleList()\r
+        {\r
+            ASSERT(m_nOnList == 0);\r
+        };\r
+#endif\r
+        CMediaSample *Head() const { return m_List; };\r
+        CMediaSample *Next(__in CMediaSample *pSample) const { return CBaseAllocator::NextSample(pSample); };\r
+        int GetCount() const { return m_nOnList; };\r
+        void Add(__inout CMediaSample *pSample)\r
+        {\r
+            ASSERT(pSample != NULL);\r
+            CBaseAllocator::NextSample(pSample) = m_List;\r
+            m_List = pSample;\r
+            m_nOnList++;\r
+        };\r
+        CMediaSample *RemoveHead()\r
+        {\r
+            CMediaSample *pSample = m_List;\r
+            if (pSample != NULL) {\r
+                m_List = CBaseAllocator::NextSample(m_List);\r
+                m_nOnList--;\r
+            }\r
+            return pSample;\r
+        };\r
+        void Remove(__inout CMediaSample *pSample);\r
+\r
+    public:\r
+        CMediaSample *m_List;\r
+        int           m_nOnList;\r
+    };\r
+protected:\r
+\r
+    CSampleList m_lFree;        // Free list\r
+\r
+    /*  Note to overriders of CBaseAllocator.\r
+\r
+        We use a lazy signalling mechanism for waiting for samples.\r
+        This means we don't call the OS if no waits occur.\r
+\r
+        In order to implement this:\r
+\r
+        1. When a new sample is added to m_lFree call NotifySample() which\r
+           calls ReleaseSemaphore on m_hSem with a count of m_lWaiting and\r
+           sets m_lWaiting to 0.\r
+           This must all be done holding the allocator's critical section.\r
+\r
+        2. When waiting for a sample call SetWaiting() which increments\r
+           m_lWaiting BEFORE leaving the allocator's critical section.\r
+\r
+        3. Actually wait by calling WaitForSingleObject(m_hSem, INFINITE)\r
+           having left the allocator's critical section.  The effect of\r
+           this is to remove 1 from the semaphore's count.  You MUST call\r
+           this once having incremented m_lWaiting.\r
+\r
+        The following are then true when the critical section is not held :\r
+            (let nWaiting = number about to wait or waiting)\r
+\r
+            (1) if (m_lFree.GetCount() != 0) then (m_lWaiting == 0)\r
+            (2) m_lWaiting + Semaphore count == nWaiting\r
+\r
+        We would deadlock if\r
+           nWaiting != 0 &&\r
+           m_lFree.GetCount() != 0 &&\r
+           Semaphore count == 0\r
+\r
+           But from (1) if m_lFree.GetCount() != 0 then m_lWaiting == 0 so\r
+           from (2) Semaphore count == nWaiting (which is non-0) so the\r
+           deadlock can't happen.\r
+    */\r
+\r
+    HANDLE m_hSem;              // For signalling\r
+    long m_lWaiting;            // Waiting for a free element\r
+    long m_lCount;              // how many buffers we have agreed to provide\r
+    long m_lAllocated;          // how many buffers are currently allocated\r
+    long m_lSize;               // agreed size of each buffer\r
+    long m_lAlignment;          // agreed alignment\r
+    long m_lPrefix;             // agreed prefix (preceeds GetPointer() value)\r
+    BOOL m_bChanged;            // Have the buffer requirements changed\r
+\r
+    // if true, we are decommitted and can't allocate memory\r
+    BOOL m_bCommitted;\r
+    // if true, the decommit has happened, but we haven't called Free yet\r
+    // as there are still outstanding buffers\r
+    BOOL m_bDecommitInProgress;\r
+\r
+    //  Notification interface\r
+    IMemAllocatorNotifyCallbackTemp *m_pNotify;\r
+\r
+    BOOL m_fEnableReleaseCallback;\r
+\r
+    // called to decommit the memory when the last buffer is freed\r
+    // pure virtual - need to override this\r
+    virtual void Free(void) PURE;\r
+\r
+    // override to allocate the memory when commit called\r
+    virtual HRESULT Alloc(void);\r
+\r
+public:\r
+\r
+    CBaseAllocator(\r
+        __in_opt LPCTSTR , __inout_opt LPUNKNOWN, __inout HRESULT *,\r
+        BOOL bEvent = TRUE, BOOL fEnableReleaseCallback = FALSE);\r
+#ifdef UNICODE\r
+    CBaseAllocator(\r
+        __in_opt LPCSTR , __inout_opt LPUNKNOWN, __inout HRESULT *,\r
+        BOOL bEvent = TRUE, BOOL fEnableReleaseCallback = FALSE);\r
+#endif\r
+    virtual ~CBaseAllocator();\r
+\r
+    DECLARE_IUNKNOWN\r
+\r
+    // override this to publicise our interfaces\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);\r
+\r
+    STDMETHODIMP SetProperties(\r
+                   __in ALLOCATOR_PROPERTIES* pRequest,\r
+                   __out ALLOCATOR_PROPERTIES* pActual);\r
+\r
+    // return the properties actually being used on this allocator\r
+    STDMETHODIMP GetProperties(\r
+                   __out ALLOCATOR_PROPERTIES* pProps);\r
+\r
+    // override Commit to allocate memory. We handle the GetBuffer\r
+    //state changes\r
+    STDMETHODIMP Commit();\r
+\r
+    // override this to handle the memory freeing. We handle any outstanding\r
+    // GetBuffer calls\r
+    STDMETHODIMP Decommit();\r
+\r
+    // get container for a sample. Blocking, synchronous call to get the\r
+    // next free buffer (as represented by an IMediaSample interface).\r
+    // on return, the time etc properties will be invalid, but the buffer\r
+    // pointer and size will be correct. The two time parameters are\r
+    // optional and either may be NULL, they may alternatively be set to\r
+    // the start and end times the sample will have attached to it\r
+    // bPrevFramesSkipped is not used (used only by the video renderer's\r
+    // allocator where it affects quality management in direct draw).\r
+\r
+    STDMETHODIMP GetBuffer(__deref_out IMediaSample **ppBuffer,\r
+                           __in_opt REFERENCE_TIME * pStartTime,\r
+                           __in_opt REFERENCE_TIME * pEndTime,\r
+                           DWORD dwFlags);\r
+\r
+    // final release of a CMediaSample will call this\r
+    STDMETHODIMP ReleaseBuffer(IMediaSample *pBuffer);\r
+    // obsolete:: virtual void PutOnFreeList(CMediaSample * pSample);\r
+\r
+    STDMETHODIMP SetNotify(IMemAllocatorNotifyCallbackTemp *pNotify);\r
+\r
+    STDMETHODIMP GetFreeCount(__out LONG *plBuffersFree);\r
+\r
+    // Notify that a sample is available\r
+    void NotifySample();\r
+\r
+    // Notify that we're waiting for a sample\r
+    void SetWaiting() { m_lWaiting++; };\r
+};\r
+\r
+\r
+//=====================================================================\r
+//=====================================================================\r
+// Defines CMemAllocator\r
+//\r
+// this is an allocator based on CBaseAllocator that allocates sample\r
+// buffers in main memory (from 'new'). You must call SetProperties\r
+// before calling Commit.\r
+//\r
+// we don't free the memory when going into Decommit state. The simplest\r
+// way to implement this without complicating CBaseAllocator is to\r
+// have a Free() function, called to go into decommit state, that does\r
+// nothing and a ReallyFree function called from our destructor that\r
+// actually frees the memory.\r
+//=====================================================================\r
+//=====================================================================\r
+\r
+//  Make me one from quartz.dll\r
+STDAPI CreateMemoryAllocator(__deref_out IMemAllocator **ppAllocator);\r
+\r
+class CMemAllocator : public CBaseAllocator\r
+{\r
+\r
+protected:\r
+\r
+    LPBYTE m_pBuffer;   // combined memory for all buffers\r
+\r
+    // override to free the memory when decommit completes\r
+    // - we actually do nothing, and save the memory until deletion.\r
+    void Free(void);\r
+\r
+    // called from the destructor (and from Alloc if changing size/count) to\r
+    // actually free up the memory\r
+    void ReallyFree(void);\r
+\r
+    // overriden to allocate the memory when commit called\r
+    HRESULT Alloc(void);\r
+\r
+public:\r
+    /* This goes in the factory template table to create new instances */\r
+    static CUnknown *CreateInstance(__inout_opt LPUNKNOWN, __inout HRESULT *);\r
+\r
+    STDMETHODIMP SetProperties(\r
+                   __in ALLOCATOR_PROPERTIES* pRequest,\r
+                   __out ALLOCATOR_PROPERTIES* pActual);\r
+\r
+    CMemAllocator(__in_opt LPCTSTR , __inout_opt LPUNKNOWN, __inout HRESULT *);\r
+#ifdef UNICODE\r
+    CMemAllocator(__in_opt LPCSTR , __inout_opt LPUNKNOWN, __inout HRESULT *);\r
+#endif\r
+    ~CMemAllocator();\r
+};\r
+\r
+// helper used by IAMovieSetup implementation\r
+STDAPI\r
+AMovieSetupRegisterFilter( const AMOVIESETUP_FILTER * const psetupdata\r
+                         , IFilterMapper *                  pIFM\r
+                         , BOOL                             bRegister  );\r
+\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+// ------------------------------------------------------------------------\r
+// ------------------------------------------------------------------------\r
+// ------------------------------------------------------------------------\r
+// ------------------------------------------------------------------------\r
+///////////////////////////////////////////////////////////////////////////\r
+\r
+#endif /* __FILTER__ */\r
+\r
+\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/amvideo.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/amvideo.cpp
new file mode 100644 (file)
index 0000000..42fe446
--- /dev/null
@@ -0,0 +1,275 @@
+//------------------------------------------------------------------------------\r
+// File: AMVideo.cpp\r
+//\r
+// Desc: DirectShow base classes - implements helper functions for\r
+//       bitmap formats.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+#include <limits.h>\r
+\r
+// These are bit field masks for true colour devices\r
+\r
+const DWORD bits555[] = {0x007C00,0x0003E0,0x00001F};\r
+const DWORD bits565[] = {0x00F800,0x0007E0,0x00001F};\r
+const DWORD bits888[] = {0xFF0000,0x00FF00,0x0000FF};\r
+\r
+// This maps bitmap subtypes into a bits per pixel value and also a\r
+// name. unicode and ansi versions are stored because we have to\r
+// return a pointer to a static string.\r
+const struct {\r
+    const GUID *pSubtype;\r
+    WORD BitCount;\r
+    CHAR *pName;\r
+    WCHAR *wszName;\r
+} BitCountMap[] =  { &MEDIASUBTYPE_RGB1,        1,   "RGB Monochrome",     L"RGB Monochrome",   \r
+                     &MEDIASUBTYPE_RGB4,        4,   "RGB VGA",            L"RGB VGA",          \r
+                     &MEDIASUBTYPE_RGB8,        8,   "RGB 8",              L"RGB 8",            \r
+                     &MEDIASUBTYPE_RGB565,      16,  "RGB 565 (16 bit)",   L"RGB 565 (16 bit)", \r
+                     &MEDIASUBTYPE_RGB555,      16,  "RGB 555 (16 bit)",   L"RGB 555 (16 bit)", \r
+                     &MEDIASUBTYPE_RGB24,       24,  "RGB 24",             L"RGB 24",           \r
+                     &MEDIASUBTYPE_RGB32,       32,  "RGB 32",             L"RGB 32",\r
+                     &MEDIASUBTYPE_ARGB32,    32,  "ARGB 32",             L"ARGB 32",\r
+                     &MEDIASUBTYPE_Overlay,     0,   "Overlay",            L"Overlay",          \r
+                     &GUID_NULL,                0,   "UNKNOWN",            L"UNKNOWN"           \r
+};\r
+\r
+// Return the size of the bitmap as defined by this header\r
+\r
+STDAPI_(DWORD) GetBitmapSize(const BITMAPINFOHEADER *pHeader)\r
+{\r
+    return DIBSIZE(*pHeader);\r
+}\r
+\r
+\r
+// This is called if the header has a 16 bit colour depth and needs to work\r
+// out the detailed type from the bit fields (either RGB 565 or RGB 555)\r
+\r
+STDAPI_(const GUID) GetTrueColorType(const BITMAPINFOHEADER *pbmiHeader)\r
+{\r
+    BITMAPINFO *pbmInfo = (BITMAPINFO *) pbmiHeader;\r
+    ASSERT(pbmiHeader->biBitCount == 16);\r
+\r
+    // If its BI_RGB then it's RGB 555 by default\r
+\r
+    if (pbmiHeader->biCompression == BI_RGB) {\r
+        return MEDIASUBTYPE_RGB555;\r
+    }\r
+\r
+    // Compare the bit fields with RGB 555\r
+\r
+    DWORD *pMask = (DWORD *) pbmInfo->bmiColors;\r
+    if (pMask[0] == bits555[0]) {\r
+        if (pMask[1] == bits555[1]) {\r
+            if (pMask[2] == bits555[2]) {\r
+                return MEDIASUBTYPE_RGB555;\r
+            }\r
+        }\r
+    }\r
+\r
+    // Compare the bit fields with RGB 565\r
+\r
+    pMask = (DWORD *) pbmInfo->bmiColors;\r
+    if (pMask[0] == bits565[0]) {\r
+        if (pMask[1] == bits565[1]) {\r
+            if (pMask[2] == bits565[2]) {\r
+                return MEDIASUBTYPE_RGB565;\r
+            }\r
+        }\r
+    }\r
+    return GUID_NULL;\r
+}\r
+\r
+\r
+// Given a BITMAPINFOHEADER structure this returns the GUID sub type that is\r
+// used to describe it in format negotiations. For example a video codec fills\r
+// in the format block with a VIDEOINFO structure, it also fills in the major\r
+// type with MEDIATYPE_VIDEO and the subtype with a GUID that matches the bit\r
+// count, for example if it is an eight bit image then MEDIASUBTYPE_RGB8\r
+\r
+STDAPI_(const GUID) GetBitmapSubtype(const BITMAPINFOHEADER *pbmiHeader)\r
+{\r
+    ASSERT(pbmiHeader);\r
+\r
+    // If it's not RGB then create a GUID from the compression type\r
+\r
+    if (pbmiHeader->biCompression != BI_RGB) {\r
+        if (pbmiHeader->biCompression != BI_BITFIELDS) {\r
+            FOURCCMap FourCCMap(pbmiHeader->biCompression);\r
+            return (const GUID) FourCCMap;\r
+        }\r
+    }\r
+\r
+    // Map the RGB DIB bit depth to a image GUID\r
+\r
+    switch(pbmiHeader->biBitCount) {\r
+        case 1    :   return MEDIASUBTYPE_RGB1;\r
+        case 4    :   return MEDIASUBTYPE_RGB4;\r
+        case 8    :   return MEDIASUBTYPE_RGB8;\r
+        case 16   :   return GetTrueColorType(pbmiHeader);\r
+        case 24   :   return MEDIASUBTYPE_RGB24;\r
+        case 32   :   return MEDIASUBTYPE_RGB32;\r
+    }\r
+    return GUID_NULL;\r
+}\r
+\r
+\r
+// Given a video bitmap subtype we return the number of bits per pixel it uses\r
+// We return a WORD bit count as thats what the BITMAPINFOHEADER uses. If the\r
+// GUID subtype is not found in the table we return an invalid USHRT_MAX\r
+\r
+STDAPI_(WORD) GetBitCount(const GUID *pSubtype)\r
+{\r
+    ASSERT(pSubtype);\r
+    const GUID *pMediaSubtype;\r
+    INT iPosition = 0;\r
+\r
+    // Scan the mapping list seeing if the source GUID matches any known\r
+    // bitmap subtypes, the list is terminated by a GUID_NULL entry\r
+\r
+    while (TRUE) {\r
+        pMediaSubtype = BitCountMap[iPosition].pSubtype;\r
+        if (IsEqualGUID(*pMediaSubtype,GUID_NULL)) {\r
+            return USHRT_MAX;\r
+        }\r
+        if (IsEqualGUID(*pMediaSubtype,*pSubtype)) {\r
+            return BitCountMap[iPosition].BitCount;\r
+        }\r
+        iPosition++;\r
+    }\r
+}\r
+\r
+\r
+// Given a bitmap subtype we return a description name that can be used for\r
+// debug purposes. In a retail build this function still returns the names\r
+// If the subtype isn't found in the lookup table we return string UNKNOWN\r
+\r
+int LocateSubtype(const GUID *pSubtype)\r
+{\r
+    ASSERT(pSubtype);\r
+    const GUID *pMediaSubtype;\r
+    INT iPosition = 0;\r
+\r
+    // Scan the mapping list seeing if the source GUID matches any known\r
+    // bitmap subtypes, the list is terminated by a GUID_NULL entry\r
+\r
+    while (TRUE) {\r
+        pMediaSubtype = BitCountMap[iPosition].pSubtype;\r
+        if (IsEqualGUID(*pMediaSubtype,*pSubtype) ||\r
+            IsEqualGUID(*pMediaSubtype,GUID_NULL)\r
+            )\r
+        {\r
+            break;\r
+        }\r
+        \r
+        iPosition++;\r
+    }\r
+\r
+    return iPosition;\r
+}\r
+\r
+\r
+\r
+STDAPI_(WCHAR *) GetSubtypeNameW(const GUID *pSubtype)\r
+{\r
+    return BitCountMap[LocateSubtype(pSubtype)].wszName;\r
+}\r
+\r
+STDAPI_(CHAR *) GetSubtypeNameA(const GUID *pSubtype)\r
+{\r
+    return BitCountMap[LocateSubtype(pSubtype)].pName;\r
+}\r
+\r
+#ifndef GetSubtypeName\r
+#error wxutil.h should have defined GetSubtypeName\r
+#endif\r
+#undef GetSubtypeName\r
+\r
+// this is here for people that linked to it directly; most people\r
+// would use the header file that picks the A or W version.\r
+STDAPI_(CHAR *) GetSubtypeName(const GUID *pSubtype)\r
+{\r
+    return GetSubtypeNameA(pSubtype);\r
+}\r
+\r
+\r
+// The mechanism for describing a bitmap format is with the BITMAPINFOHEADER\r
+// This is really messy to deal with because it invariably has fields that\r
+// follow it holding bit fields, palettes and the rest. This function gives\r
+// the number of bytes required to hold a VIDEOINFO that represents it. This\r
+// count includes the prefix information (like the rcSource rectangle) the\r
+// BITMAPINFOHEADER field, and any other colour information on the end.\r
+//\r
+// WARNING If you want to copy a BITMAPINFOHEADER into a VIDEOINFO always make\r
+// sure that you use the HEADER macro because the BITMAPINFOHEADER field isn't\r
+// right at the start of the VIDEOINFO (there are a number of other fields),\r
+//\r
+//     CopyMemory(HEADER(pVideoInfo),pbmi,sizeof(BITMAPINFOHEADER));\r
+//\r
+\r
+STDAPI_(LONG) GetBitmapFormatSize(const BITMAPINFOHEADER *pHeader)\r
+{\r
+    // Everyone has this to start with this  \r
+    LONG Size = SIZE_PREHEADER + pHeader->biSize;\r
+\r
+    ASSERT(pHeader->biSize >= sizeof(BITMAPINFOHEADER));\r
+    \r
+    // Does this format use a palette, if the number of colours actually used\r
+    // is zero then it is set to the maximum that are allowed for that colour\r
+    // depth (an example is 256 for eight bits). Truecolour formats may also\r
+    // pass a palette with them in which case the used count is non zero\r
+\r
+    // This would scare me.\r
+    ASSERT(pHeader->biBitCount <= iPALETTE || pHeader->biClrUsed == 0);\r
+\r
+    if (pHeader->biBitCount <= iPALETTE || pHeader->biClrUsed) {\r
+        LONG Entries = (DWORD) 1 << pHeader->biBitCount;\r
+        if (pHeader->biClrUsed) {\r
+            Entries = pHeader->biClrUsed;\r
+        }\r
+        Size += Entries * sizeof(RGBQUAD);\r
+    }\r
+\r
+    // Truecolour formats may have a BI_BITFIELDS specifier for compression\r
+    // type which means that room for three DWORDs should be allocated that\r
+    // specify where in each pixel the RGB colour components may be found\r
+\r
+    if (pHeader->biCompression == BI_BITFIELDS) {\r
+        Size += SIZE_MASKS;\r
+    }\r
+\r
+    // A BITMAPINFO for a palettised image may also contain a palette map that\r
+    // provides the information to map from a source palette to a destination\r
+    // palette during a BitBlt for example, because this information is only\r
+    // ever processed during drawing you don't normally store the palette map\r
+    // nor have any way of knowing if it is present in the data structure\r
+\r
+    return Size;\r
+}\r
+\r
+\r
+// Returns TRUE if the VIDEOINFO contains a palette\r
+\r
+STDAPI_(BOOL) ContainsPalette(const VIDEOINFOHEADER *pVideoInfo)\r
+{\r
+    if (PALETTISED(pVideoInfo) == FALSE) {\r
+        if (pVideoInfo->bmiHeader.biClrUsed == 0) {\r
+            return FALSE;\r
+        }\r
+    }\r
+    return TRUE;\r
+}\r
+\r
+\r
+// Return a pointer to the first entry in a palette\r
+\r
+STDAPI_(const RGBQUAD *) GetBitmapPalette(const VIDEOINFOHEADER *pVideoInfo)\r
+{\r
+    if (pVideoInfo->bmiHeader.biCompression == BI_BITFIELDS) {\r
+        return TRUECOLOR(pVideoInfo)->bmiColors;\r
+    }\r
+    return COLORS(pVideoInfo);\r
+}\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/arithutil.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/arithutil.cpp
new file mode 100644 (file)
index 0000000..cd0d127
--- /dev/null
@@ -0,0 +1,360 @@
+//------------------------------------------------------------------------------\r
+// File: ArithUtil.cpp\r
+//\r
+// Desc: DirectShow base classes - implements helper classes for building\r
+//       multimedia filters.\r
+//\r
+// Copyright (c) 1992-2004 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+#include <streams.h>\r
+\r
+//\r
+//  Declare function from largeint.h we need so that PPC can build\r
+//\r
+\r
+//\r
+// Enlarged integer divide - 64-bits / 32-bits > 32-bits\r
+//\r
+\r
+#ifndef _X86_\r
+\r
+#define LLtoU64(x) (*(unsigned __int64*)(void*)(&(x)))\r
+\r
+__inline\r
+ULONG\r
+WINAPI\r
+EnlargedUnsignedDivide (\r
+    IN ULARGE_INTEGER Dividend,\r
+    IN ULONG Divisor,\r
+    IN PULONG Remainder\r
+    )\r
+{\r
+        // return remainder if necessary\r
+        if (Remainder != NULL)\r
+                *Remainder = (ULONG)(LLtoU64(Dividend) % Divisor);\r
+        return (ULONG)(LLtoU64(Dividend) / Divisor);\r
+}\r
+\r
+#else\r
+__inline\r
+ULONG\r
+WINAPI\r
+EnlargedUnsignedDivide (\r
+    IN ULARGE_INTEGER Dividend,\r
+    IN ULONG Divisor,\r
+    IN PULONG Remainder\r
+    )\r
+{\r
+    ULONG ulResult;\r
+    _asm {\r
+        mov eax,Dividend.LowPart\r
+        mov edx,Dividend.HighPart\r
+        mov ecx,Remainder\r
+        div Divisor\r
+        or  ecx,ecx\r
+        jz  short label\r
+        mov [ecx],edx\r
+label:\r
+        mov ulResult,eax\r
+    }\r
+    return ulResult;\r
+}\r
+#endif\r
+\r
+\r
+/*  Arithmetic functions to help with time format conversions\r
+*/\r
+\r
+#ifdef _M_ALPHA\r
+// work around bug in version 12.00.8385 of the alpha compiler where\r
+// UInt32x32To64 sign-extends its arguments (?)\r
+#undef UInt32x32To64\r
+#define UInt32x32To64(a, b) (((ULONGLONG)((ULONG)(a)) & 0xffffffff) * ((ULONGLONG)((ULONG)(b)) & 0xffffffff))\r
+#endif\r
+\r
+/*   Compute (a * b + d) / c */\r
+LONGLONG WINAPI llMulDiv(LONGLONG a, LONGLONG b, LONGLONG c, LONGLONG d)\r
+{\r
+    /*  Compute the absolute values to avoid signed arithmetic problems */\r
+    ULARGE_INTEGER ua, ub;\r
+    DWORDLONG uc;\r
+\r
+    ua.QuadPart = (DWORDLONG)(a >= 0 ? a : -a);\r
+    ub.QuadPart = (DWORDLONG)(b >= 0 ? b : -b);\r
+    uc          = (DWORDLONG)(c >= 0 ? c : -c);\r
+    BOOL bSign = (a < 0) ^ (b < 0);\r
+\r
+    /*  Do long multiplication */\r
+    ULARGE_INTEGER p[2];\r
+    p[0].QuadPart  = UInt32x32To64(ua.LowPart, ub.LowPart);\r
+\r
+    /*  This next computation cannot overflow into p[1].HighPart because\r
+        the max number we can compute here is:\r
+\r
+                 (2 ** 32 - 1) * (2 ** 32 - 1) +  // ua.LowPart * ub.LowPart\r
+    (2 ** 32) *  (2 ** 31) * (2 ** 32 - 1) * 2    // x.LowPart * y.HighPart * 2\r
+\r
+    == 2 ** 96 - 2 ** 64 + (2 ** 64 - 2 ** 33 + 1)\r
+    == 2 ** 96 - 2 ** 33 + 1\r
+    < 2 ** 96\r
+    */\r
+\r
+    ULARGE_INTEGER x;\r
+    x.QuadPart     = UInt32x32To64(ua.LowPart, ub.HighPart) +\r
+                     UInt32x32To64(ua.HighPart, ub.LowPart) +\r
+                     p[0].HighPart;\r
+    p[0].HighPart  = x.LowPart;\r
+    p[1].QuadPart  = UInt32x32To64(ua.HighPart, ub.HighPart) + x.HighPart;\r
+\r
+    if (d != 0) {\r
+        ULARGE_INTEGER ud[2];\r
+        if (bSign) {\r
+            ud[0].QuadPart = (DWORDLONG)(-d);\r
+            if (d > 0) {\r
+                /*  -d < 0 */\r
+                ud[1].QuadPart = (DWORDLONG)(LONGLONG)-1;\r
+            } else {\r
+                ud[1].QuadPart = (DWORDLONG)0;\r
+            }\r
+        } else {\r
+            ud[0].QuadPart = (DWORDLONG)d;\r
+            if (d < 0) {\r
+                ud[1].QuadPart = (DWORDLONG)(LONGLONG)-1;\r
+            } else {\r
+                ud[1].QuadPart = (DWORDLONG)0;\r
+            }\r
+        }\r
+        /*  Now do extended addition */\r
+        ULARGE_INTEGER uliTotal;\r
+\r
+        /*  Add ls DWORDs */\r
+        uliTotal.QuadPart  = (DWORDLONG)ud[0].LowPart + p[0].LowPart;\r
+        p[0].LowPart       = uliTotal.LowPart;\r
+\r
+        /*  Propagate carry */\r
+        uliTotal.LowPart   = uliTotal.HighPart;\r
+        uliTotal.HighPart  = 0;\r
+\r
+        /*  Add 2nd most ls DWORDs */\r
+        uliTotal.QuadPart += (DWORDLONG)ud[0].HighPart + p[0].HighPart;\r
+        p[0].HighPart      = uliTotal.LowPart;\r
+\r
+        /*  Propagate carry */\r
+        uliTotal.LowPart   = uliTotal.HighPart;\r
+        uliTotal.HighPart  = 0;\r
+\r
+        /*  Add MS DWORDLONGs - no carry expected */\r
+        p[1].QuadPart     += ud[1].QuadPart + uliTotal.QuadPart;\r
+\r
+        /*  Now see if we got a sign change from the addition */\r
+        if ((LONG)p[1].HighPart < 0) {\r
+            bSign = !bSign;\r
+\r
+            /*  Negate the current value (ugh!) */\r
+            p[0].QuadPart  = ~p[0].QuadPart;\r
+            p[1].QuadPart  = ~p[1].QuadPart;\r
+            p[0].QuadPart += 1;\r
+            p[1].QuadPart += (p[0].QuadPart == 0);\r
+        }\r
+    }\r
+\r
+    /*  Now for the division */\r
+    if (c < 0) {\r
+        bSign = !bSign;\r
+    }\r
+\r
+\r
+    /*  This will catch c == 0 and overflow */\r
+    if (uc <= p[1].QuadPart) {\r
+        return bSign ? (LONGLONG)0x8000000000000000 :\r
+                       (LONGLONG)0x7FFFFFFFFFFFFFFF;\r
+    }\r
+\r
+    DWORDLONG ullResult;\r
+\r
+    /*  Do the division */\r
+    /*  If the dividend is a DWORD_LONG use the compiler */\r
+    if (p[1].QuadPart == 0) {\r
+        ullResult = p[0].QuadPart / uc;\r
+        return bSign ? -(LONGLONG)ullResult : (LONGLONG)ullResult;\r
+    }\r
+\r
+    /*  If the divisor is a DWORD then its simpler */\r
+    ULARGE_INTEGER ulic;\r
+    ulic.QuadPart = uc;\r
+    if (ulic.HighPart == 0) {\r
+        ULARGE_INTEGER uliDividend;\r
+        ULARGE_INTEGER uliResult;\r
+        DWORD dwDivisor = (DWORD)uc;\r
+        // ASSERT(p[1].HighPart == 0 && p[1].LowPart < dwDivisor);\r
+        uliDividend.HighPart = p[1].LowPart;\r
+        uliDividend.LowPart = p[0].HighPart;\r
+#ifndef USE_LARGEINT\r
+        uliResult.HighPart = (DWORD)(uliDividend.QuadPart / dwDivisor);\r
+        p[0].HighPart = (DWORD)(uliDividend.QuadPart % dwDivisor);\r
+        uliResult.LowPart = 0;\r
+        uliResult.QuadPart = p[0].QuadPart / dwDivisor + uliResult.QuadPart;\r
+#else\r
+        /*  NOTE - this routine will take exceptions if\r
+            the result does not fit in a DWORD\r
+        */\r
+        if (uliDividend.QuadPart >= (DWORDLONG)dwDivisor) {\r
+            uliResult.HighPart = EnlargedUnsignedDivide(\r
+                                     uliDividend,\r
+                                     dwDivisor,\r
+                                     &p[0].HighPart);\r
+        } else {\r
+            uliResult.HighPart = 0;\r
+        }\r
+        uliResult.LowPart = EnlargedUnsignedDivide(\r
+                                 p[0],\r
+                                 dwDivisor,\r
+                                 NULL);\r
+#endif\r
+        return bSign ? -(LONGLONG)uliResult.QuadPart :\r
+                        (LONGLONG)uliResult.QuadPart;\r
+    }\r
+\r
+\r
+    ullResult = 0;\r
+\r
+    /*  OK - do long division */\r
+    for (int i = 0; i < 64; i++) {\r
+        ullResult <<= 1;\r
+\r
+        /*  Shift 128 bit p left 1 */\r
+        p[1].QuadPart <<= 1;\r
+        if ((p[0].HighPart & 0x80000000) != 0) {\r
+            p[1].LowPart++;\r
+        }\r
+        p[0].QuadPart <<= 1;\r
+\r
+        /*  Compare */\r
+        if (uc <= p[1].QuadPart) {\r
+            p[1].QuadPart -= uc;\r
+            ullResult += 1;\r
+        }\r
+    }\r
+\r
+    return bSign ? - (LONGLONG)ullResult : (LONGLONG)ullResult;\r
+}\r
+\r
+LONGLONG WINAPI Int64x32Div32(LONGLONG a, LONG b, LONG c, LONG d)\r
+{\r
+    ULARGE_INTEGER ua;\r
+    DWORD ub;\r
+    DWORD uc;\r
+\r
+    /*  Compute the absolute values to avoid signed arithmetic problems */\r
+    ua.QuadPart = (DWORDLONG)(a >= 0 ? a : -a);\r
+    ub = (DWORD)(b >= 0 ? b : -b);\r
+    uc = (DWORD)(c >= 0 ? c : -c);\r
+    BOOL bSign = (a < 0) ^ (b < 0);\r
+\r
+    /*  Do long multiplication */\r
+    ULARGE_INTEGER p0;\r
+    DWORD p1;\r
+    p0.QuadPart  = UInt32x32To64(ua.LowPart, ub);\r
+\r
+    if (ua.HighPart != 0) {\r
+        ULARGE_INTEGER x;\r
+        x.QuadPart     = UInt32x32To64(ua.HighPart, ub) + p0.HighPart;\r
+        p0.HighPart  = x.LowPart;\r
+        p1   = x.HighPart;\r
+    } else {\r
+        p1 = 0;\r
+    }\r
+\r
+    if (d != 0) {\r
+        ULARGE_INTEGER ud0;\r
+        DWORD ud1;\r
+\r
+        if (bSign) {\r
+            //\r
+            //  Cast d to LONGLONG first otherwise -0x80000000 sign extends\r
+            //  incorrectly\r
+            //\r
+            ud0.QuadPart = (DWORDLONG)(-(LONGLONG)d);\r
+            if (d > 0) {\r
+                /*  -d < 0 */\r
+                ud1 = (DWORD)-1;\r
+            } else {\r
+                ud1 = (DWORD)0;\r
+            }\r
+        } else {\r
+            ud0.QuadPart = (DWORDLONG)d;\r
+            if (d < 0) {\r
+                ud1 = (DWORD)-1;\r
+            } else {\r
+                ud1 = (DWORD)0;\r
+            }\r
+        }\r
+        /*  Now do extended addition */\r
+        ULARGE_INTEGER uliTotal;\r
+\r
+        /*  Add ls DWORDs */\r
+        uliTotal.QuadPart  = (DWORDLONG)ud0.LowPart + p0.LowPart;\r
+        p0.LowPart       = uliTotal.LowPart;\r
+\r
+        /*  Propagate carry */\r
+        uliTotal.LowPart   = uliTotal.HighPart;\r
+        uliTotal.HighPart  = 0;\r
+\r
+        /*  Add 2nd most ls DWORDs */\r
+        uliTotal.QuadPart += (DWORDLONG)ud0.HighPart + p0.HighPart;\r
+        p0.HighPart      = uliTotal.LowPart;\r
+\r
+        /*  Add MS DWORDLONGs - no carry expected */\r
+        p1 += ud1 + uliTotal.HighPart;\r
+\r
+        /*  Now see if we got a sign change from the addition */\r
+        if ((LONG)p1 < 0) {\r
+            bSign = !bSign;\r
+\r
+            /*  Negate the current value (ugh!) */\r
+            p0.QuadPart  = ~p0.QuadPart;\r
+            p1 = ~p1;\r
+            p0.QuadPart += 1;\r
+            p1 += (p0.QuadPart == 0);\r
+        }\r
+    }\r
+\r
+    /*  Now for the division */\r
+    if (c < 0) {\r
+        bSign = !bSign;\r
+    }\r
+\r
+\r
+    /*  This will catch c == 0 and overflow */\r
+    if (uc <= p1) {\r
+        return bSign ? (LONGLONG)0x8000000000000000 :\r
+                       (LONGLONG)0x7FFFFFFFFFFFFFFF;\r
+    }\r
+\r
+    /*  Do the division */\r
+\r
+    /*  If the divisor is a DWORD then its simpler */\r
+    ULARGE_INTEGER uliDividend;\r
+    ULARGE_INTEGER uliResult;\r
+    DWORD dwDivisor = uc;\r
+    uliDividend.HighPart = p1;\r
+    uliDividend.LowPart = p0.HighPart;\r
+    /*  NOTE - this routine will take exceptions if\r
+        the result does not fit in a DWORD\r
+    */\r
+    if (uliDividend.QuadPart >= (DWORDLONG)dwDivisor) {\r
+        uliResult.HighPart = EnlargedUnsignedDivide(\r
+                                 uliDividend,\r
+                                 dwDivisor,\r
+                                 &p0.HighPart);\r
+    } else {\r
+        uliResult.HighPart = 0;\r
+    }\r
+    uliResult.LowPart = EnlargedUnsignedDivide(\r
+                             p0,\r
+                             dwDivisor,\r
+                             NULL);\r
+    return bSign ? -(LONGLONG)uliResult.QuadPart :\r
+                    (LONGLONG)uliResult.QuadPart;\r
+}\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/cache.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/cache.h
new file mode 100644 (file)
index 0000000..0a807c2
--- /dev/null
@@ -0,0 +1,74 @@
+//------------------------------------------------------------------------------\r
+// File: Cache.h\r
+//\r
+// Desc: DirectShow base classes - efines a non-MFC generic cache class.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+/* This class implements a simple cache. A cache object is instantiated\r
+   with the number of items it is to hold. An item is a pointer to an\r
+   object derived from CBaseObject (helps reduce memory leaks). The cache\r
+   can then have objects added to it and removed from it. The cache size\r
+   is fixed at construction time and may therefore run out or be flooded.\r
+   If it runs out it returns a NULL pointer, if it fills up it also returns\r
+   a NULL pointer instead of a pointer to the object just inserted */\r
+\r
+/* Making these classes inherit from CBaseObject does nothing for their\r
+   functionality but it allows us to check there are no memory leaks */\r
+\r
+/* WARNING Be very careful when using this class, what it lets you do is\r
+   store and retrieve objects so that you can minimise object creation\r
+   which in turns improves efficiency. However the object you store is\r
+   exactly the same as the object you get back which means that it short\r
+   circuits the constructor initialisation phase. This means any class\r
+   variables the object has (eg pointers) are highly likely to be invalid.\r
+   Therefore ensure you reinitialise the object before using it again */\r
+\r
+\r
+#ifndef __CACHE__\r
+#define __CACHE__\r
+\r
+\r
+class CCache : CBaseObject {\r
+\r
+    /* Make copy constructor and assignment operator inaccessible */\r
+\r
+    CCache(const CCache &refCache);\r
+    CCache &operator=(const CCache &refCache);\r
+\r
+private:\r
+\r
+    /* These are initialised in the constructor. The first variable points to\r
+       an array of pointers, each of which points to a CBaseObject derived\r
+       object. The m_iCacheSize is the static fixed size for the cache and the\r
+       m_iUsed defines the number of places filled with objects at any time.\r
+       We fill the array of pointers from the start (ie m_ppObjects[0] first)\r
+       and then only add and remove objects from the end position, so in this\r
+       respect the array of object pointers should be treated as a stack */\r
+\r
+    CBaseObject **m_ppObjects;\r
+    const INT m_iCacheSize;\r
+    INT m_iUsed;\r
+\r
+public:\r
+\r
+    CCache(__in_opt LPCTSTR pName,INT iItems);\r
+    virtual ~CCache();\r
+\r
+    /* Add an item to the cache */\r
+    CBaseObject *AddToCache(__in CBaseObject *pObject);\r
+\r
+    /* Remove an item from the cache */\r
+    CBaseObject *RemoveFromCache();\r
+\r
+    /* Delete all the objects held in the cache */\r
+    void RemoveAll(void);\r
+\r
+    /* Return the cache size which is set during construction */\r
+    INT GetCacheSize(void) const {return m_iCacheSize;};\r
+};\r
+\r
+#endif /* __CACHE__ */\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/checkbmi.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/checkbmi.h
new file mode 100644 (file)
index 0000000..7287967
--- /dev/null
@@ -0,0 +1,120 @@
+//  Copyright (c) 1992 - 1997  Microsoft Corporation.  All Rights Reserved.\r
+\r
+#ifndef _CHECKBMI_H_\r
+#define _CHECKBMI_H_\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+//  Helper\r
+__inline BOOL MultiplyCheckOverflow(DWORD a, DWORD b, __deref_out_range(==, a * b) DWORD *pab) {\r
+    *pab = a * b;\r
+    if ((a == 0) || (((*pab) / a) == b)) {\r
+        return TRUE;\r
+    }\r
+    return FALSE;\r
+}\r
+\r
+\r
+//  Checks if the fields in a BITMAPINFOHEADER won't generate\r
+//  overlows and buffer overruns\r
+//  This is not a complete check and does not guarantee code using this structure will be secure\r
+//  from attack\r
+//  Bugs this is guarding against:\r
+//        1.  Total structure size calculation overflowing\r
+//        2.  biClrUsed > 256 for 8-bit palettized content\r
+//        3.  Total bitmap size in bytes overflowing\r
+//        4.  biSize < size of the base structure leading to accessessing random memory\r
+//        5.  Total structure size exceeding know size of data\r
+//\r
+\r
+__success(return != 0) __inline BOOL ValidateBitmapInfoHeader(\r
+    const BITMAPINFOHEADER *pbmi,   // pointer to structure to check\r
+    __out_range(>=, sizeof(BITMAPINFOHEADER)) DWORD cbSize     // size of memory block containing structure\r
+)\r
+{\r
+    DWORD dwWidthInBytes;\r
+    DWORD dwBpp;\r
+    DWORD dwWidthInBits;\r
+    DWORD dwHeight;\r
+    DWORD dwSizeImage;\r
+    DWORD dwClrUsed;\r
+\r
+    // Reject bad parameters - do the size check first to avoid reading bad memory\r
+    if (cbSize < sizeof(BITMAPINFOHEADER) ||\r
+        pbmi->biSize < sizeof(BITMAPINFOHEADER) ||\r
+        pbmi->biSize > 4096) {\r
+        return FALSE;\r
+    }\r
+\r
+    //  Reject 0 size\r
+    if (pbmi->biWidth == 0 || pbmi->biHeight == 0) {\r
+        return FALSE;\r
+    }\r
+\r
+    // Use bpp of 200 for validating against further overflows if not set for compressed format\r
+    dwBpp = 200;\r
+\r
+    if (pbmi->biBitCount > dwBpp) {\r
+        return FALSE;\r
+    }\r
+\r
+    // Strictly speaking abs can overflow so cast explicitly to DWORD\r
+    dwHeight = (DWORD)abs(pbmi->biHeight);\r
+\r
+    if (!MultiplyCheckOverflow(dwBpp, (DWORD)pbmi->biWidth, &dwWidthInBits)) {\r
+        return FALSE;\r
+    }\r
+\r
+    //  Compute correct width in bytes - rounding up to 4 bytes\r
+    dwWidthInBytes = (dwWidthInBits / 8 + 3) & ~3;\r
+\r
+    if (!MultiplyCheckOverflow(dwWidthInBytes, dwHeight, &dwSizeImage)) {\r
+        return FALSE;\r
+    }\r
+\r
+    // Fail if total size is 0 - this catches indivual quantities being 0\r
+    // Also don't allow huge values > 1GB which might cause arithmetic\r
+    // errors for users\r
+    if (dwSizeImage > 0x40000000 ||\r
+        pbmi->biSizeImage > 0x40000000) {\r
+        return FALSE;\r
+    }\r
+\r
+    //  Fail if biClrUsed looks bad\r
+    if (pbmi->biClrUsed > 256) {\r
+        return FALSE;\r
+    }\r
+\r
+    if (pbmi->biClrUsed == 0 && pbmi->biBitCount <= 8 && pbmi->biBitCount > 0) {\r
+        dwClrUsed = (1 << pbmi->biBitCount);\r
+    } else {\r
+        dwClrUsed = pbmi->biClrUsed;\r
+    }\r
+\r
+    //  Check total size\r
+    if (cbSize < pbmi->biSize + dwClrUsed * sizeof(RGBQUAD) +\r
+                 (pbmi->biCompression == BI_BITFIELDS ? 3 * sizeof(DWORD) : 0)) {\r
+        return FALSE;\r
+    }\r
+\r
+    //  If it is RGB validate biSizeImage - lots of code assumes the size is correct\r
+    if (pbmi->biCompression == BI_RGB || pbmi->biCompression == BI_BITFIELDS) {\r
+        if (pbmi->biSizeImage != 0) {\r
+            DWORD dwBits = (DWORD)pbmi->biWidth * (DWORD)pbmi->biBitCount;\r
+            DWORD dwWidthInBytes = ((DWORD)((dwBits+31) & (~31)) / 8);\r
+            DWORD dwTotalSize = (DWORD)abs(pbmi->biHeight) * dwWidthInBytes;\r
+            if (dwTotalSize > pbmi->biSizeImage) {\r
+                return FALSE;\r
+            }\r
+        }\r
+    }\r
+    return TRUE;\r
+}\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+#endif // _CHECKBMI_H_\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/combase.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/combase.cpp
new file mode 100644 (file)
index 0000000..ec62a88
--- /dev/null
@@ -0,0 +1,265 @@
+//------------------------------------------------------------------------------\r
+// File: ComBase.cpp\r
+//\r
+// Desc: DirectShow base classes - implements class hierarchy for creating\r
+//       COM objects.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+#pragma warning( disable : 4514 )   // Disable warnings re unused inline functions\r
+\r
+\r
+/* Define the static member variable */\r
+\r
+LONG CBaseObject::m_cObjects = 0;\r
+\r
+\r
+/* Constructor */\r
+\r
+CBaseObject::CBaseObject(__in_opt LPCTSTR pName)\r
+{\r
+    /* Increment the number of active objects */\r
+    InterlockedIncrement(&m_cObjects);\r
+\r
+#ifdef DEBUG\r
+\r
+#ifdef UNICODE\r
+    m_dwCookie = DbgRegisterObjectCreation(0, pName);\r
+#else\r
+    m_dwCookie = DbgRegisterObjectCreation(pName, 0);\r
+#endif\r
+\r
+#endif\r
+}\r
+\r
+#ifdef UNICODE\r
+CBaseObject::CBaseObject(const char *pName)\r
+{\r
+    /* Increment the number of active objects */\r
+    InterlockedIncrement(&m_cObjects);\r
+\r
+#ifdef DEBUG\r
+    m_dwCookie = DbgRegisterObjectCreation(pName, 0);\r
+#endif\r
+}\r
+#endif\r
+\r
+HINSTANCE      hlibOLEAut32;\r
+\r
+/* Destructor */\r
+\r
+CBaseObject::~CBaseObject()\r
+{\r
+    /* Decrement the number of objects active */\r
+    if (InterlockedDecrement(&m_cObjects) == 0) {\r
+       if (hlibOLEAut32) {\r
+           FreeLibrary(hlibOLEAut32);\r
+\r
+           hlibOLEAut32 = 0;\r
+       }\r
+    };\r
+\r
+\r
+#ifdef DEBUG\r
+    DbgRegisterObjectDestruction(m_dwCookie);\r
+#endif\r
+}\r
+\r
+static const TCHAR szOle32Aut[]   = TEXT("OleAut32.dll");\r
+\r
+HINSTANCE LoadOLEAut32()\r
+{\r
+    if (hlibOLEAut32 == 0) {\r
+\r
+       hlibOLEAut32 = LoadLibrary(szOle32Aut);\r
+    }\r
+\r
+    return hlibOLEAut32;\r
+}\r
+\r
+\r
+/* Constructor */\r
+\r
+// We know we use "this" in the initialization list, we also know we don't modify *phr.\r
+#pragma warning( disable : 4355 4100 )\r
+CUnknown::CUnknown(__in_opt LPCTSTR pName, __in_opt LPUNKNOWN pUnk)\r
+: CBaseObject(pName)\r
+/* Start the object with a reference count of zero - when the      */\r
+/* object is queried for it's first interface this may be          */\r
+/* incremented depending on whether or not this object is          */\r
+/* currently being aggregated upon                                 */\r
+, m_cRef(0)\r
+/* Set our pointer to our IUnknown interface.                      */\r
+/* If we have an outer, use its, otherwise use ours.               */\r
+/* This pointer effectivly points to the owner of                  */\r
+/* this object and can be accessed by the GetOwner() method.       */\r
+, m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )\r
+ /* Why the double cast?  Well, the inner cast is a type-safe cast */\r
+ /* to pointer to a type from which we inherit.  The second is     */\r
+ /* type-unsafe but works because INonDelegatingUnknown "behaves   */\r
+ /* like" IUnknown. (Only the names on the methods change.)        */\r
+{\r
+    // Everything we need to do has been done in the initializer list\r
+}\r
+\r
+// This does the same as above except it has a useless HRESULT argument\r
+// use the previous constructor, this is just left for compatibility...\r
+CUnknown::CUnknown(__in_opt LPCTSTR pName, __in_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr) :\r
+    CBaseObject(pName),\r
+    m_cRef(0),\r
+    m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )\r
+{\r
+}\r
+\r
+#ifdef UNICODE\r
+CUnknown::CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk)\r
+: CBaseObject(pName), m_cRef(0),\r
+    m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )\r
+{ }\r
+\r
+CUnknown::CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr) :\r
+    CBaseObject(pName), m_cRef(0),\r
+    m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )\r
+{ }\r
+\r
+#endif\r
+\r
+#pragma warning( default : 4355 4100 )\r
+\r
+\r
+/* QueryInterface */\r
+\r
+STDMETHODIMP CUnknown::NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv)\r
+{\r
+    CheckPointer(ppv,E_POINTER);\r
+    ValidateReadWritePtr(ppv,sizeof(PVOID));\r
+\r
+    /* We know only about IUnknown */\r
+\r
+    if (riid == IID_IUnknown) {\r
+        GetInterface((LPUNKNOWN) (PNDUNKNOWN) this, ppv);\r
+        return NOERROR;\r
+    } else {\r
+        *ppv = NULL;\r
+        return E_NOINTERFACE;\r
+    }\r
+}\r
+\r
+/* We have to ensure that we DON'T use a max macro, since these will typically   */\r
+/* lead to one of the parameters being evaluated twice.  Since we are worried    */\r
+/* about concurrency, we can't afford to access the m_cRef twice since we can't  */\r
+/* afford to run the risk that its value having changed between accesses.        */\r
+\r
+template<class T> inline static T ourmax( const T & a, const T & b )\r
+{\r
+    return a > b ? a : b;\r
+}\r
+\r
+/* AddRef */\r
+\r
+STDMETHODIMP_(ULONG) CUnknown::NonDelegatingAddRef()\r
+{\r
+    LONG lRef = InterlockedIncrement( &m_cRef );\r
+    ASSERT(lRef > 0);\r
+    DbgLog((LOG_MEMORY,3,TEXT("    Obj %d ref++ = %d"),\r
+           m_dwCookie, m_cRef));\r
+    return ourmax(ULONG(m_cRef), 1ul);\r
+}\r
+\r
+\r
+/* Release */\r
+\r
+STDMETHODIMP_(ULONG) CUnknown::NonDelegatingRelease()\r
+{\r
+    /* If the reference count drops to zero delete ourselves */\r
+\r
+    LONG lRef = InterlockedDecrement( &m_cRef );\r
+    ASSERT(lRef >= 0);\r
+\r
+    DbgLog((LOG_MEMORY,3,TEXT("    Object %d ref-- = %d"),\r
+           m_dwCookie, m_cRef));\r
+    if (lRef == 0) {\r
+\r
+        // COM rules say we must protect against re-entrancy.\r
+        // If we are an aggregator and we hold our own interfaces\r
+        // on the aggregatee, the QI for these interfaces will\r
+        // addref ourselves. So after doing the QI we must release\r
+        // a ref count on ourselves. Then, before releasing the\r
+        // private interface, we must addref ourselves. When we do\r
+        // this from the destructor here it will result in the ref\r
+        // count going to 1 and then back to 0 causing us to\r
+        // re-enter the destructor. Hence we add an extra refcount here\r
+        // once we know we will delete the object.\r
+        // for an example aggregator see filgraph\distrib.cpp.\r
+\r
+        m_cRef++;\r
+\r
+        delete this;\r
+        return ULONG(0);\r
+    } else {\r
+        //  Don't touch m_cRef again even in this leg as the object\r
+        //  may have just been released on another thread too\r
+        return ourmax(ULONG(lRef), 1ul);\r
+    }\r
+}\r
+\r
+\r
+/* Return an interface pointer to a requesting client\r
+   performing a thread safe AddRef as necessary */\r
+\r
+STDAPI GetInterface(LPUNKNOWN pUnk, __out void **ppv)\r
+{\r
+    CheckPointer(ppv, E_POINTER);\r
+    *ppv = pUnk;\r
+    pUnk->AddRef();\r
+    return NOERROR;\r
+}\r
+\r
+\r
+/* Compares two interfaces and returns TRUE if they are on the same object */\r
+\r
+BOOL WINAPI IsEqualObject(IUnknown *pFirst, IUnknown *pSecond)\r
+{\r
+    /*  Different objects can't have the same interface pointer for\r
+        any interface\r
+    */\r
+    if (pFirst == pSecond) {\r
+        return TRUE;\r
+    }\r
+    /*  OK - do it the hard way - check if they have the same\r
+        IUnknown pointers - a single object can only have one of these\r
+    */\r
+    LPUNKNOWN pUnknown1;     // Retrieve the IUnknown interface\r
+    LPUNKNOWN pUnknown2;     // Retrieve the other IUnknown interface\r
+    HRESULT hr;              // General OLE return code\r
+\r
+    ASSERT(pFirst);\r
+    ASSERT(pSecond);\r
+\r
+    /* See if the IUnknown pointers match */\r
+\r
+    hr = pFirst->QueryInterface(IID_IUnknown,(void **) &pUnknown1);\r
+    if (FAILED(hr)) {\r
+        return FALSE;\r
+    }\r
+    ASSERT(pUnknown1);\r
+\r
+    /* Release the extra interface we hold */\r
+\r
+    pUnknown1->Release();\r
+\r
+    hr = pSecond->QueryInterface(IID_IUnknown,(void **) &pUnknown2);\r
+    if (FAILED(hr)) {\r
+        return FALSE;\r
+    }\r
+    ASSERT(pUnknown2);\r
+\r
+    /* Release the extra interface we hold */\r
+\r
+    pUnknown2->Release();\r
+    return (pUnknown1 == pUnknown2);\r
+}\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/combase.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/combase.h
new file mode 100644 (file)
index 0000000..f735ba9
--- /dev/null
@@ -0,0 +1,305 @@
+//------------------------------------------------------------------------------\r
+// File: ComBase.h\r
+//\r
+// Desc: DirectShow base classes - defines a class hierarchy for creating\r
+//       COM objects.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+/*\r
+\r
+a. Derive your COM object from CUnknown\r
+\r
+b. Make a static CreateInstance function that takes an LPUNKNOWN, an HRESULT *\r
+   and a TCHAR *. The LPUNKNOWN defines the object to delegate IUnknown calls\r
+   to. The HRESULT * allows error codes to be passed around constructors and\r
+   the TCHAR * is a descriptive name that can be printed on the debugger.\r
+\r
+   It is important that constructors only change the HRESULT * if they have\r
+   to set an ERROR code, if it was successful then leave it alone or you may\r
+   overwrite an error code from an object previously created.\r
+\r
+   When you call a constructor the descriptive name should be in static store\r
+   as we do not copy the string. To stop large amounts of memory being used\r
+   in retail builds by all these static strings use the NAME macro,\r
+\r
+   CMyFilter = new CImplFilter(NAME("My filter"),pUnknown,phr);\r
+   if (FAILED(hr)) {\r
+       return hr;\r
+   }\r
+\r
+   In retail builds NAME(_x_) compiles to NULL, the base CBaseObject class\r
+   knows not to do anything with objects that don't have a name.\r
+\r
+c. Have a constructor for your object that passes the LPUNKNOWN, HRESULT * and\r
+   TCHAR * to the CUnknown constructor. You can set the HRESULT if you have an\r
+   error, or just simply pass it through to the constructor.\r
+\r
+   The object creation will fail in the class factory if the HRESULT indicates\r
+   an error (ie FAILED(HRESULT) == TRUE)\r
+\r
+d. Create a FactoryTemplate with your object's class id and CreateInstance\r
+   function.\r
+\r
+Then (for each interface) either\r
+\r
+Multiple inheritance\r
+\r
+1. Also derive it from ISomeInterface\r
+2. Include DECLARE_IUNKNOWN in your class definition to declare\r
+   implementations of QueryInterface, AddRef and Release that\r
+   call the outer unknown\r
+3. Override NonDelegatingQueryInterface to expose ISomeInterface by\r
+   code something like\r
+\r
+     if (riid == IID_ISomeInterface) {\r
+         return GetInterface((ISomeInterface *) this, ppv);\r
+     } else {\r
+         return CUnknown::NonDelegatingQueryInterface(riid, ppv);\r
+     }\r
+\r
+4. Declare and implement the member functions of ISomeInterface.\r
+\r
+or: Nested interfaces\r
+\r
+1. Declare a class derived from CUnknown\r
+2. Include DECLARE_IUNKNOWN in your class definition\r
+3. Override NonDelegatingQueryInterface to expose ISomeInterface by\r
+   code something like\r
+\r
+     if (riid == IID_ISomeInterface) {\r
+         return GetInterface((ISomeInterface *) this, ppv);\r
+     } else {\r
+         return CUnknown::NonDelegatingQueryInterface(riid, ppv);\r
+     }\r
+\r
+4. Implement the member functions of ISomeInterface. Use GetOwner() to\r
+   access the COM object class.\r
+\r
+And in your COM object class:\r
+\r
+5. Make the nested class a friend of the COM object class, and declare\r
+   an instance of the nested class as a member of the COM object class.\r
+\r
+   NOTE that because you must always pass the outer unknown and an hResult\r
+   to the CUnknown constructor you cannot use a default constructor, in\r
+   other words you will have to make the member variable a pointer to the\r
+   class and make a NEW call in your constructor to actually create it.\r
+\r
+6. override the NonDelegatingQueryInterface with code like this:\r
+\r
+     if (riid == IID_ISomeInterface) {\r
+         return m_pImplFilter->\r
+            NonDelegatingQueryInterface(IID_ISomeInterface, ppv);\r
+     } else {\r
+         return CUnknown::NonDelegatingQueryInterface(riid, ppv);\r
+     }\r
+\r
+You can have mixed classes which support some interfaces via multiple\r
+inheritance and some via nested classes\r
+\r
+*/\r
+\r
+#ifndef __COMBASE__\r
+#define __COMBASE__\r
+\r
+// Filter Setup data structures no defined in axextend.idl\r
+\r
+typedef REGPINTYPES\r
+AMOVIESETUP_MEDIATYPE, * PAMOVIESETUP_MEDIATYPE, * FAR LPAMOVIESETUP_MEDIATYPE;\r
+\r
+typedef REGFILTERPINS\r
+AMOVIESETUP_PIN, * PAMOVIESETUP_PIN, * FAR LPAMOVIESETUP_PIN;\r
+\r
+typedef struct _AMOVIESETUP_FILTER\r
+{\r
+  const CLSID * clsID;\r
+  const WCHAR * strName;\r
+  DWORD      dwMerit;\r
+  UINT       nPins;\r
+  const AMOVIESETUP_PIN * lpPin;\r
+}\r
+AMOVIESETUP_FILTER, * PAMOVIESETUP_FILTER, * FAR LPAMOVIESETUP_FILTER;\r
+\r
+/* The DLLENTRY module initialises the module handle on loading */\r
+\r
+extern HINSTANCE g_hInst;\r
+\r
+/* On DLL load remember which platform we are running on */\r
+\r
+extern DWORD g_amPlatform;\r
+extern OSVERSIONINFO g_osInfo;     // Filled in by GetVersionEx\r
+\r
+/* Version of IUnknown that is renamed to allow a class to support both\r
+   non delegating and delegating IUnknowns in the same COM object */\r
+\r
+#ifndef INONDELEGATINGUNKNOWN_DEFINED\r
+DECLARE_INTERFACE(INonDelegatingUnknown)\r
+{\r
+    STDMETHOD(NonDelegatingQueryInterface) (THIS_ REFIID, LPVOID *) PURE;\r
+    STDMETHOD_(ULONG, NonDelegatingAddRef)(THIS) PURE;\r
+    STDMETHOD_(ULONG, NonDelegatingRelease)(THIS) PURE;\r
+};\r
+#define INONDELEGATINGUNKNOWN_DEFINED\r
+#endif\r
+\r
+typedef INonDelegatingUnknown *PNDUNKNOWN;\r
+\r
+\r
+/* This is the base object class that supports active object counting. As\r
+   part of the debug facilities we trace every time a C++ object is created\r
+   or destroyed. The name of the object has to be passed up through the class\r
+   derivation list during construction as you cannot call virtual functions\r
+   in the constructor. The downside of all this is that every single object\r
+   constructor has to take an object name parameter that describes it */\r
+\r
+class CBaseObject\r
+{\r
+\r
+private:\r
+\r
+    // Disable the copy constructor and assignment by default so you will get\r
+    //   compiler errors instead of unexpected behaviour if you pass objects\r
+    //   by value or assign objects.\r
+    CBaseObject(const CBaseObject& objectSrc);          // no implementation\r
+    void operator=(const CBaseObject& objectSrc);       // no implementation\r
+\r
+private:\r
+    static LONG m_cObjects;     /* Total number of objects active */\r
+\r
+protected:\r
+#ifdef DEBUG\r
+    DWORD m_dwCookie;           /* Cookie identifying this object */\r
+#endif\r
+\r
+\r
+public:\r
+\r
+    /* These increment and decrement the number of active objects */\r
+\r
+    CBaseObject(__in_opt LPCTSTR pName);\r
+#ifdef UNICODE\r
+    CBaseObject(__in_opt LPCSTR pName);\r
+#endif\r
+    ~CBaseObject();\r
+\r
+    /* Call this to find if there are any CUnknown derived objects active */\r
+\r
+    static LONG ObjectsActive() {\r
+        return m_cObjects;\r
+    };\r
+};\r
+\r
+\r
+/* An object that supports one or more COM interfaces will be based on\r
+   this class. It supports counting of total objects for DLLCanUnloadNow\r
+   support, and an implementation of the core non delegating IUnknown */\r
+\r
+class AM_NOVTABLE CUnknown : public INonDelegatingUnknown,\r
+                 public CBaseObject\r
+{\r
+private:\r
+    const LPUNKNOWN m_pUnknown; /* Owner of this object */\r
+\r
+protected:                      /* So we can override NonDelegatingRelease() */\r
+    volatile LONG m_cRef;       /* Number of reference counts */\r
+\r
+public:\r
+\r
+    CUnknown(__in_opt LPCTSTR pName, __in_opt LPUNKNOWN pUnk);\r
+    virtual ~CUnknown() {};\r
+\r
+    // This is redundant, just use the other constructor\r
+    //   as we never touch the HRESULT in this anyway\r
+    CUnknown(__in_opt LPCTSTR Name, __in_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr);\r
+#ifdef UNICODE\r
+    CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk);\r
+    CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk,__inout_opt HRESULT *phr);\r
+#endif\r
+\r
+    /* Return the owner of this object */\r
+\r
+    LPUNKNOWN GetOwner() const {\r
+        return m_pUnknown;\r
+    };\r
+\r
+    /* Called from the class factory to create a new instance, it is\r
+       pure virtual so it must be overriden in your derived class */\r
+\r
+    /* static CUnknown *CreateInstance(LPUNKNOWN, HRESULT *) */\r
+\r
+    /* Non delegating unknown implementation */\r
+\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID, __deref_out void **);\r
+    STDMETHODIMP_(ULONG) NonDelegatingAddRef();\r
+    STDMETHODIMP_(ULONG) NonDelegatingRelease();\r
+};\r
+\r
+/* Return an interface pointer to a requesting client\r
+   performing a thread safe AddRef as necessary */\r
+\r
+STDAPI GetInterface(LPUNKNOWN pUnk, __out void **ppv);\r
+\r
+/* A function that can create a new COM object */\r
+\r
+typedef CUnknown *(CALLBACK *LPFNNewCOMObject)(__in_opt LPUNKNOWN pUnkOuter, __inout_opt HRESULT *phr);\r
+\r
+/*  A function (can be NULL) which is called from the DLL entrypoint\r
+    routine for each factory template:\r
+\r
+    bLoading - TRUE on DLL load, FALSE on DLL unload\r
+    rclsid   - the m_ClsID of the entry\r
+*/\r
+typedef void (CALLBACK *LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid);\r
+\r
+/* Create one of these per object class in an array so that\r
+   the default class factory code can create new instances */\r
+\r
+class CFactoryTemplate {\r
+\r
+public:\r
+\r
+    const WCHAR *              m_Name;\r
+    const CLSID *              m_ClsID;\r
+    LPFNNewCOMObject           m_lpfnNew;\r
+    LPFNInitRoutine            m_lpfnInit;\r
+    const AMOVIESETUP_FILTER * m_pAMovieSetup_Filter;\r
+\r
+    BOOL IsClassID(REFCLSID rclsid) const {\r
+        return (IsEqualCLSID(*m_ClsID,rclsid));\r
+    };\r
+\r
+    CUnknown *CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr) const {\r
+        CheckPointer(phr,NULL);\r
+        return m_lpfnNew(pUnk, phr);\r
+    };\r
+};\r
+\r
+\r
+/* You must override the (pure virtual) NonDelegatingQueryInterface to return\r
+   interface pointers (using GetInterface) to the interfaces your derived\r
+   class supports (the default implementation only supports IUnknown) */\r
+\r
+#define DECLARE_IUNKNOWN                                        \\r
+    STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv) {      \\r
+        return GetOwner()->QueryInterface(riid,ppv);            \\r
+    };                                                          \\r
+    STDMETHODIMP_(ULONG) AddRef() {                             \\r
+        return GetOwner()->AddRef();                            \\r
+    };                                                          \\r
+    STDMETHODIMP_(ULONG) Release() {                            \\r
+        return GetOwner()->Release();                           \\r
+    };\r
+\r
+\r
+\r
+HINSTANCE      LoadOLEAut32();\r
+\r
+\r
+#endif /* __COMBASE__ */\r
+\r
+\r
+\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/cprop.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/cprop.cpp
new file mode 100644 (file)
index 0000000..7bd76b4
--- /dev/null
@@ -0,0 +1,383 @@
+//------------------------------------------------------------------------------\r
+// File: CProp.cpp\r
+//\r
+// Desc: DirectShow base classes - implements CBasePropertyPage class.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+\r
+// Constructor for the base property page class. As described in the header\r
+// file we must be initialised with dialog and title resource identifiers.\r
+// The class supports IPropertyPage and overrides AddRef and Release calls\r
+// to keep track of the reference counts. When the last count is released\r
+// we call SetPageSite(NULL) and SetObjects(0,NULL) to release interfaces\r
+// previously obtained by the property page when it had SetObjects called\r
+\r
+CBasePropertyPage::CBasePropertyPage(__in_opt LPCTSTR pName,   // Debug only name\r
+                                     __inout_opt LPUNKNOWN pUnk, // COM Delegator\r
+                                     int DialogId,      // Resource ID\r
+                                     int TitleId) :     // To get tital\r
+    CUnknown(pName,pUnk),\r
+    m_DialogId(DialogId),\r
+    m_TitleId(TitleId),\r
+    m_hwnd(NULL),\r
+    m_Dlg(NULL),\r
+    m_pPageSite(NULL),\r
+    m_bObjectSet(FALSE),\r
+    m_bDirty(FALSE)\r
+{\r
+}\r
+\r
+#ifdef UNICODE\r
+CBasePropertyPage::CBasePropertyPage(__in_opt LPCSTR pName,     // Debug only name\r
+                                     __inout_opt LPUNKNOWN pUnk,  // COM Delegator\r
+                                     int DialogId,      // Resource ID\r
+                                     int TitleId) :     // To get tital\r
+    CUnknown(pName,pUnk),\r
+    m_DialogId(DialogId),\r
+    m_TitleId(TitleId),\r
+    m_hwnd(NULL),\r
+    m_Dlg(NULL),\r
+    m_pPageSite(NULL),\r
+    m_bObjectSet(FALSE),\r
+    m_bDirty(FALSE)\r
+{\r
+}\r
+#endif\r
+\r
+// Increment our reference count\r
+\r
+STDMETHODIMP_(ULONG) CBasePropertyPage::NonDelegatingAddRef()\r
+{\r
+    LONG lRef = InterlockedIncrement(&m_cRef);\r
+    ASSERT(lRef > 0);\r
+    return max(ULONG(m_cRef),1ul);\r
+}\r
+\r
+\r
+// Release a reference count and protect against reentrancy\r
+\r
+STDMETHODIMP_(ULONG) CBasePropertyPage::NonDelegatingRelease()\r
+{\r
+    // If the reference count drops to zero delete ourselves\r
+\r
+    LONG lRef = InterlockedDecrement(&m_cRef);\r
+    if (lRef == 0) {\r
+        m_cRef++;\r
+        SetPageSite(NULL);\r
+        SetObjects(0,NULL);\r
+        delete this;\r
+        return ULONG(0);\r
+    } else {\r
+        //  Don't touch m_cRef again here!\r
+        return max(ULONG(lRef),1ul);\r
+    }\r
+}\r
+\r
+\r
+// Expose our IPropertyPage interface\r
+\r
+STDMETHODIMP\r
+CBasePropertyPage::NonDelegatingQueryInterface(REFIID riid,__deref_out void **ppv)\r
+{\r
+    if (riid == IID_IPropertyPage) {\r
+        return GetInterface((IPropertyPage *)this,ppv);\r
+    } else {\r
+        return CUnknown::NonDelegatingQueryInterface(riid,ppv);\r
+    }\r
+}\r
+\r
+\r
+// Get the page info so that the page site can size itself\r
+\r
+STDMETHODIMP CBasePropertyPage::GetPageInfo(__out LPPROPPAGEINFO pPageInfo)\r
+{\r
+    CheckPointer(pPageInfo,E_POINTER);\r
+    WCHAR wszTitle[STR_MAX_LENGTH];\r
+    WideStringFromResource(wszTitle,m_TitleId);\r
+\r
+    // Allocate dynamic memory for the property page title\r
+\r
+    LPOLESTR pszTitle;\r
+    HRESULT hr = AMGetWideString(wszTitle, &pszTitle);\r
+    if (FAILED(hr)) {\r
+        NOTE("No caption memory");\r
+        return hr;\r
+    }\r
+\r
+    pPageInfo->cb               = sizeof(PROPPAGEINFO);\r
+    pPageInfo->pszTitle         = pszTitle;\r
+    pPageInfo->pszDocString     = NULL;\r
+    pPageInfo->pszHelpFile      = NULL;\r
+    pPageInfo->dwHelpContext    = 0;\r
+\r
+    // Set defaults in case GetDialogSize fails\r
+    pPageInfo->size.cx          = 340;\r
+    pPageInfo->size.cy          = 150;\r
+\r
+    GetDialogSize(m_DialogId, DialogProc,0L,&pPageInfo->size);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Handles the messages for our property window\r
+\r
+INT_PTR CALLBACK CBasePropertyPage::DialogProc(HWND hwnd,\r
+                                            UINT uMsg,\r
+                                            WPARAM wParam,\r
+                                            LPARAM lParam)\r
+{\r
+    CBasePropertyPage *pPropertyPage;\r
+\r
+    switch (uMsg) {\r
+\r
+        case WM_INITDIALOG:\r
+\r
+            _SetWindowLongPtr(hwnd, DWLP_USER, lParam);\r
+\r
+            // This pointer may be NULL when calculating size\r
+\r
+            pPropertyPage = (CBasePropertyPage *) lParam;\r
+            if (pPropertyPage == NULL) {\r
+                return (LRESULT) 1;\r
+            }\r
+            pPropertyPage->m_Dlg = hwnd;\r
+    }\r
+\r
+    // This pointer may be NULL when calculating size\r
+\r
+    pPropertyPage = _GetWindowLongPtr<CBasePropertyPage*>(hwnd, DWLP_USER);\r
+    if (pPropertyPage == NULL) {\r
+        return (LRESULT) 1;\r
+    }\r
+    return pPropertyPage->OnReceiveMessage(hwnd,uMsg,wParam,lParam);\r
+}\r
+\r
+\r
+// Tells us the object that should be informed of the property changes\r
+\r
+STDMETHODIMP CBasePropertyPage::SetObjects(ULONG cObjects,__in_ecount_opt(cObjects) LPUNKNOWN *ppUnk)\r
+{\r
+    if (cObjects == 1) {\r
+\r
+        if ((ppUnk == NULL) || (*ppUnk == NULL)) {\r
+            return E_POINTER;\r
+        }\r
+\r
+        // Set a flag to say that we have set the Object\r
+        m_bObjectSet = TRUE ;\r
+        return OnConnect(*ppUnk);\r
+\r
+    } else if (cObjects == 0) {\r
+\r
+        // Set a flag to say that we have not set the Object for the page\r
+        m_bObjectSet = FALSE ;\r
+        return OnDisconnect();\r
+    }\r
+\r
+    DbgBreak("No support for more than one object");\r
+    return E_UNEXPECTED;\r
+}\r
+\r
+\r
+// Create the window we will use to edit properties\r
+\r
+STDMETHODIMP CBasePropertyPage::Activate(HWND hwndParent,\r
+                                         LPCRECT pRect,\r
+                                         BOOL fModal)\r
+{\r
+    CheckPointer(pRect,E_POINTER);\r
+\r
+    // Return failure if SetObject has not been called.\r
+    if (m_bObjectSet == FALSE) {\r
+        return E_UNEXPECTED;\r
+    }\r
+\r
+    if (m_hwnd) {\r
+        return E_UNEXPECTED;\r
+    }\r
+\r
+    m_hwnd = CreateDialogParam(g_hInst,\r
+                               MAKEINTRESOURCE(m_DialogId),\r
+                               hwndParent,\r
+                               DialogProc,\r
+                               (LPARAM) this);\r
+    if (m_hwnd == NULL) {\r
+        return E_OUTOFMEMORY;\r
+    }\r
+\r
+    OnActivate();\r
+    Move(pRect);\r
+    return Show(SW_SHOWNORMAL);\r
+}\r
+\r
+\r
+// Set the position of the property page\r
+\r
+STDMETHODIMP CBasePropertyPage::Move(LPCRECT pRect)\r
+{\r
+    CheckPointer(pRect,E_POINTER);\r
+\r
+    if (m_hwnd == NULL) {\r
+        return E_UNEXPECTED;\r
+    }\r
+\r
+    MoveWindow(m_hwnd,              // Property page handle\r
+               pRect->left,         // x coordinate\r
+               pRect->top,          // y coordinate\r
+               WIDTH(pRect),        // Overall window width\r
+               HEIGHT(pRect),       // And likewise height\r
+               TRUE);               // Should we repaint it\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Display the property dialog\r
+\r
+STDMETHODIMP CBasePropertyPage::Show(UINT nCmdShow)\r
+{\r
+   // Have we been activated yet\r
+\r
+    if (m_hwnd == NULL) {\r
+        return E_UNEXPECTED;\r
+    }\r
+\r
+    // Ignore wrong show flags\r
+\r
+    if ((nCmdShow != SW_SHOW) && (nCmdShow != SW_SHOWNORMAL) && (nCmdShow != SW_HIDE)) {\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+    ShowWindow(m_hwnd,nCmdShow);\r
+    InvalidateRect(m_hwnd,NULL,TRUE);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Destroy the property page dialog\r
+\r
+STDMETHODIMP CBasePropertyPage::Deactivate(void)\r
+{\r
+    if (m_hwnd == NULL) {\r
+        return E_UNEXPECTED;\r
+    }\r
+\r
+    // Remove WS_EX_CONTROLPARENT before DestroyWindow call\r
+\r
+    DWORD dwStyle = GetWindowLong(m_hwnd, GWL_EXSTYLE);\r
+    dwStyle = dwStyle & (~WS_EX_CONTROLPARENT);\r
+\r
+    //  Set m_hwnd to be NULL temporarily so the message handler\r
+    //  for WM_STYLECHANGING doesn't add the WS_EX_CONTROLPARENT\r
+    //  style back in\r
+    HWND hwnd = m_hwnd;\r
+    m_hwnd = NULL;\r
+    SetWindowLong(hwnd, GWL_EXSTYLE, dwStyle);\r
+    m_hwnd = hwnd;\r
+\r
+    OnDeactivate();\r
+\r
+    // Destroy the dialog window\r
+\r
+    DestroyWindow(m_hwnd);\r
+    m_hwnd = NULL;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Tells the application property page site\r
+\r
+STDMETHODIMP CBasePropertyPage::SetPageSite(__in_opt LPPROPERTYPAGESITE pPageSite)\r
+{\r
+    if (pPageSite) {\r
+\r
+        if (m_pPageSite) {\r
+            return E_UNEXPECTED;\r
+        }\r
+\r
+        m_pPageSite = pPageSite;\r
+        m_pPageSite->AddRef();\r
+\r
+    } else {\r
+\r
+        if (m_pPageSite == NULL) {\r
+            return E_UNEXPECTED;\r
+        }\r
+\r
+        m_pPageSite->Release();\r
+        m_pPageSite = NULL;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Apply any changes so far made\r
+\r
+STDMETHODIMP CBasePropertyPage::Apply()\r
+{\r
+    // In ActiveMovie 1.0 we used to check whether we had been activated or\r
+    // not. This is too constrictive. Apply should be allowed as long as\r
+    // SetObject was called to set an object. So we will no longer check to\r
+    // see if we have been activated (ie., m_hWnd != NULL), but instead\r
+    // make sure that m_bObjectSet is TRUE (ie., SetObject has been called).\r
+\r
+    if (m_bObjectSet == FALSE) {\r
+        return E_UNEXPECTED;\r
+    }\r
+\r
+    // Must have had a site set\r
+\r
+    if (m_pPageSite == NULL) {\r
+        return E_UNEXPECTED;\r
+    }\r
+\r
+    // Has anything changed\r
+\r
+    if (m_bDirty == FALSE) {\r
+        return NOERROR;\r
+    }\r
+\r
+    // Commit derived class changes\r
+\r
+    HRESULT hr = OnApplyChanges();\r
+    if (SUCCEEDED(hr)) {\r
+        m_bDirty = FALSE;\r
+    }\r
+    return hr;\r
+}\r
+\r
+\r
+// Base class definition for message handling\r
+\r
+INT_PTR CBasePropertyPage::OnReceiveMessage(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)\r
+{\r
+    // we would like the TAB key to move around the tab stops in our property\r
+    // page, but for some reason OleCreatePropertyFrame clears the CONTROLPARENT\r
+    // style behind our back, so we need to switch it back on now behind its\r
+    // back.  Otherwise the tab key will be useless in every page.\r
+    //\r
+\r
+    CBasePropertyPage *pPropertyPage;\r
+    {\r
+        pPropertyPage = _GetWindowLongPtr<CBasePropertyPage*>(hwnd, DWLP_USER);\r
+\r
+        if (pPropertyPage->m_hwnd == NULL) {\r
+            return 0;\r
+        }\r
+        switch (uMsg) {\r
+          case WM_STYLECHANGING:\r
+              if (wParam == GWL_EXSTYLE) {\r
+                  LPSTYLESTRUCT lpss = (LPSTYLESTRUCT)lParam;\r
+                  lpss->styleNew |= WS_EX_CONTROLPARENT;\r
+                  return 0;\r
+              }\r
+        }\r
+    }\r
+               \r
+    return DefWindowProc(hwnd,uMsg,wParam,lParam);\r
+}\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/cprop.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/cprop.h
new file mode 100644 (file)
index 0000000..db44940
--- /dev/null
@@ -0,0 +1,95 @@
+//------------------------------------------------------------------------------\r
+// File: CProp.h\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef __CPROP__\r
+#define __CPROP__\r
+\r
+// Base property page class. Filters typically expose custom properties by\r
+// implementing special control interfaces, examples are IDirectDrawVideo\r
+// and IQualProp on renderers. This allows property pages to be built that\r
+// use the given interface. Applications such as the ActiveMovie OCX query\r
+// filters for the property pages they support and expose them to the user\r
+//\r
+// This class provides all the framework for a property page. A property\r
+// page is a COM object that supports IPropertyPage. We should be created\r
+// with a resource ID for the dialog which we will load when required. We\r
+// should also be given in the constructor a resource ID for a title string\r
+// we will load from the DLLs STRINGTABLE. The property page titles must be\r
+// stored in resource files so that they can be easily internationalised\r
+//\r
+// We have a number of virtual methods (not PURE) that may be overriden in\r
+// derived classes to query for interfaces and so on. These functions have\r
+// simple implementations here that just return NOERROR. Derived classes\r
+// will almost definately have to override the message handler method called\r
+// OnReceiveMessage. We have a static dialog procedure that calls the method\r
+// so that derived classes don't have to fiddle around with the this pointer\r
+\r
+class AM_NOVTABLE CBasePropertyPage : public IPropertyPage, public CUnknown\r
+{\r
+protected:\r
+\r
+    LPPROPERTYPAGESITE m_pPageSite;       // Details for our property site\r
+    HWND m_hwnd;                          // Window handle for the page\r
+    HWND m_Dlg;                           // Actual dialog window handle\r
+    BOOL m_bDirty;                        // Has anything been changed\r
+    int m_TitleId;                        // Resource identifier for title\r
+    int m_DialogId;                       // Dialog resource identifier\r
+\r
+    static INT_PTR CALLBACK DialogProc(HWND hwnd,\r
+                                       UINT uMsg,\r
+                                       WPARAM wParam,\r
+                                       LPARAM lParam);\r
+\r
+private:\r
+    BOOL m_bObjectSet ;                  // SetObject has been called or not.\r
+public:\r
+\r
+    CBasePropertyPage(__in_opt LPCTSTR pName,      // Debug only name\r
+                      __inout_opt LPUNKNOWN pUnk, // COM Delegator\r
+                      int DialogId,               // Resource ID\r
+                      int TitleId);               // To get tital\r
+\r
+#ifdef UNICODE\r
+    CBasePropertyPage(__in_opt LPCSTR pName,\r
+                      __inout_opt LPUNKNOWN pUnk,\r
+                      int DialogId,  \r
+                      int TitleId);\r
+#endif\r
+    virtual ~CBasePropertyPage() { };\r
+    DECLARE_IUNKNOWN\r
+\r
+    // Override these virtual methods\r
+\r
+    virtual HRESULT OnConnect(IUnknown *pUnknown) { return NOERROR; };\r
+    virtual HRESULT OnDisconnect() { return NOERROR; };\r
+    virtual HRESULT OnActivate() { return NOERROR; };\r
+    virtual HRESULT OnDeactivate() { return NOERROR; };\r
+    virtual HRESULT OnApplyChanges() { return NOERROR; };\r
+    virtual INT_PTR OnReceiveMessage(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);\r
+\r
+    // These implement an IPropertyPage interface\r
+\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);\r
+    STDMETHODIMP_(ULONG) NonDelegatingRelease();\r
+    STDMETHODIMP_(ULONG) NonDelegatingAddRef();\r
+    STDMETHODIMP SetPageSite(__in_opt LPPROPERTYPAGESITE pPageSite);\r
+    STDMETHODIMP Activate(HWND hwndParent, LPCRECT prect,BOOL fModal);\r
+    STDMETHODIMP Deactivate(void);\r
+    STDMETHODIMP GetPageInfo(__out LPPROPPAGEINFO pPageInfo);\r
+    STDMETHODIMP SetObjects(ULONG cObjects, __in_ecount_opt(cObjects) LPUNKNOWN *ppUnk);\r
+    STDMETHODIMP Show(UINT nCmdShow);\r
+    STDMETHODIMP Move(LPCRECT prect);\r
+    STDMETHODIMP IsPageDirty(void) { return m_bDirty ? S_OK : S_FALSE; }\r
+    STDMETHODIMP Apply(void);\r
+    STDMETHODIMP Help(LPCWSTR lpszHelpDir) { return E_NOTIMPL; }\r
+    STDMETHODIMP TranslateAccelerator(__inout LPMSG lpMsg) { return E_NOTIMPL; }\r
+};\r
+\r
+#endif // __CPROP__\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/ctlutil.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/ctlutil.cpp
new file mode 100644 (file)
index 0000000..8ccb9dc
--- /dev/null
@@ -0,0 +1,2541 @@
+//------------------------------------------------------------------------------\r
+// File: CtlUtil.cpp\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+// Base classes implementing IDispatch parsing for the basic control dual\r
+// interfaces. Derive from these and implement just the custom method and\r
+// property methods. We also implement CPosPassThru that can be used by\r
+// renderers and transforms to pass by IMediaPosition and IMediaSeeking\r
+\r
+\r
+#include <streams.h>\r
+#include <limits.h>\r
+#include "seekpt.h"\r
+\r
+// 'bool' non standard reserved word\r
+#pragma warning(disable:4237)\r
+\r
+\r
+// --- CBaseDispatch implementation ----------\r
+CBaseDispatch::~CBaseDispatch()\r
+{\r
+    if (m_pti) {\r
+       m_pti->Release();\r
+    }\r
+}\r
+\r
+\r
+// return 1 if we support GetTypeInfo\r
+\r
+STDMETHODIMP\r
+CBaseDispatch::GetTypeInfoCount(__out UINT * pctinfo)\r
+{\r
+    CheckPointer(pctinfo,E_POINTER);\r
+    ValidateReadWritePtr(pctinfo,sizeof(UINT *));\r
+    *pctinfo = 1;\r
+    return S_OK;\r
+}\r
+\r
+\r
+typedef HRESULT (STDAPICALLTYPE *LPLOADTYPELIB)(\r
+                           const OLECHAR FAR *szFile,\r
+                           __deref_out ITypeLib FAR* FAR* pptlib);\r
+\r
+typedef HRESULT (STDAPICALLTYPE *LPLOADREGTYPELIB)(REFGUID rguid,\r
+                           WORD wVerMajor,\r
+                           WORD wVerMinor,\r
+                           LCID lcid,\r
+                           __deref_out ITypeLib FAR* FAR* pptlib);\r
+\r
+// attempt to find our type library\r
+\r
+STDMETHODIMP\r
+CBaseDispatch::GetTypeInfo(\r
+  REFIID riid,\r
+  UINT itinfo,\r
+  LCID lcid,\r
+  __deref_out ITypeInfo ** pptinfo)\r
+{\r
+    CheckPointer(pptinfo,E_POINTER);\r
+    ValidateReadWritePtr(pptinfo,sizeof(ITypeInfo *));\r
+    HRESULT hr;\r
+\r
+    *pptinfo = NULL;\r
+\r
+    // we only support one type element\r
+    if (0 != itinfo) {\r
+       return TYPE_E_ELEMENTNOTFOUND;\r
+    }\r
+\r
+    if (NULL == pptinfo) {\r
+       return E_POINTER;\r
+    }\r
+\r
+    // always look for neutral\r
+    if (NULL == m_pti) {\r
+\r
+       LPLOADTYPELIB       lpfnLoadTypeLib;\r
+       LPLOADREGTYPELIB    lpfnLoadRegTypeLib;\r
+       ITypeLib            *ptlib;\r
+       HINSTANCE           hInst;\r
+\r
+       static const char  szTypeLib[]    = "LoadTypeLib";\r
+       static const char  szRegTypeLib[] = "LoadRegTypeLib";\r
+       static const WCHAR szControl[]    = L"control.tlb";\r
+\r
+       //\r
+       // Try to get the Ole32Aut.dll module handle.\r
+       //\r
+\r
+       hInst = LoadOLEAut32();\r
+       if (hInst == NULL) {\r
+           DWORD dwError = GetLastError();\r
+           return AmHresultFromWin32(dwError);\r
+       }\r
+       lpfnLoadRegTypeLib = (LPLOADREGTYPELIB)GetProcAddress(hInst,\r
+                                                             szRegTypeLib);\r
+       if (lpfnLoadRegTypeLib == NULL) {\r
+           DWORD dwError = GetLastError();\r
+           return AmHresultFromWin32(dwError);\r
+       }\r
+\r
+       hr = (*lpfnLoadRegTypeLib)(LIBID_QuartzTypeLib, 1, 0, // version 1.0\r
+                                  lcid, &ptlib);\r
+\r
+       if (FAILED(hr)) {\r
+\r
+           // attempt to load directly - this will fill the\r
+           // registry in if it finds it\r
+\r
+           lpfnLoadTypeLib = (LPLOADTYPELIB)GetProcAddress(hInst, szTypeLib);\r
+           if (lpfnLoadTypeLib == NULL) {\r
+               DWORD dwError = GetLastError();\r
+               return AmHresultFromWin32(dwError);\r
+           }\r
+\r
+           hr = (*lpfnLoadTypeLib)(szControl, &ptlib);\r
+           if (FAILED(hr)) {\r
+               return hr;\r
+           }\r
+       }\r
+\r
+       hr = ptlib->GetTypeInfoOfGuid(\r
+                   riid,\r
+                   &m_pti);\r
+\r
+       ptlib->Release();\r
+\r
+       if (FAILED(hr)) {\r
+           return hr;\r
+       }\r
+    }\r
+\r
+    *pptinfo = m_pti;\r
+    m_pti->AddRef();\r
+    return S_OK;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CBaseDispatch::GetIDsOfNames(\r
+  REFIID riid,\r
+  __in_ecount(cNames) LPOLESTR * rgszNames,\r
+  UINT cNames,\r
+  LCID lcid,\r
+  __out_ecount(cNames) DISPID * rgdispid)\r
+{\r
+    // although the IDispatch riid is dead, we use this to pass from\r
+    // the interface implementation class to us the iid we are talking about.\r
+\r
+    ITypeInfo * pti;\r
+    HRESULT hr = GetTypeInfo(riid, 0, lcid, &pti);\r
+\r
+    if (SUCCEEDED(hr)) {\r
+       hr = pti->GetIDsOfNames(rgszNames, cNames, rgdispid);\r
+\r
+       pti->Release();\r
+    }\r
+    return hr;\r
+}\r
+\r
+\r
+// --- CMediaControl implementation ---------\r
+\r
+CMediaControl::CMediaControl(const TCHAR * name,LPUNKNOWN pUnk) :\r
+    CUnknown(name, pUnk)\r
+{\r
+}\r
+\r
+// expose our interfaces IMediaControl and IUnknown\r
+\r
+STDMETHODIMP\r
+CMediaControl::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    ValidateReadWritePtr(ppv,sizeof(PVOID));\r
+    if (riid == IID_IMediaControl) {\r
+       return GetInterface( (IMediaControl *) this, ppv);\r
+    } else {\r
+       return CUnknown::NonDelegatingQueryInterface(riid, ppv);\r
+    }\r
+}\r
+\r
+\r
+// return 1 if we support GetTypeInfo\r
+\r
+STDMETHODIMP\r
+CMediaControl::GetTypeInfoCount(__out UINT * pctinfo)\r
+{\r
+    return m_basedisp.GetTypeInfoCount(pctinfo);\r
+}\r
+\r
+\r
+// attempt to find our type library\r
+\r
+STDMETHODIMP\r
+CMediaControl::GetTypeInfo(\r
+  UINT itinfo,\r
+  LCID lcid,\r
+  __deref_out ITypeInfo ** pptinfo)\r
+{\r
+    return m_basedisp.GetTypeInfo(\r
+               IID_IMediaControl,\r
+               itinfo,\r
+               lcid,\r
+               pptinfo);\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CMediaControl::GetIDsOfNames(\r
+  REFIID riid,\r
+  __in_ecount(cNames) LPOLESTR * rgszNames,\r
+  UINT cNames,\r
+  LCID lcid,\r
+  __out_ecount(cNames) DISPID * rgdispid)\r
+{\r
+    return m_basedisp.GetIDsOfNames(\r
+                       IID_IMediaControl,\r
+                       rgszNames,\r
+                       cNames,\r
+                       lcid,\r
+                       rgdispid);\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CMediaControl::Invoke(\r
+  DISPID dispidMember,\r
+  REFIID riid,\r
+  LCID lcid,\r
+  WORD wFlags,\r
+  __in DISPPARAMS * pdispparams,\r
+  __out_opt VARIANT * pvarResult,\r
+  __out_opt EXCEPINFO * pexcepinfo,\r
+  __out_opt UINT * puArgErr)\r
+{\r
+    // this parameter is a dead leftover from an earlier interface\r
+    if (IID_NULL != riid) {\r
+       return DISP_E_UNKNOWNINTERFACE;\r
+    }\r
+\r
+    ITypeInfo * pti;\r
+    HRESULT hr = GetTypeInfo(0, lcid, &pti);\r
+\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    hr = pti->Invoke(\r
+           (IMediaControl *)this,\r
+           dispidMember,\r
+           wFlags,\r
+           pdispparams,\r
+           pvarResult,\r
+           pexcepinfo,\r
+           puArgErr);\r
+\r
+    pti->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+// --- CMediaEvent implementation ----------\r
+\r
+\r
+CMediaEvent::CMediaEvent(__in_opt LPCTSTR name,__in_opt LPUNKNOWN pUnk) :\r
+    CUnknown(name, pUnk)\r
+{\r
+}\r
+\r
+\r
+// expose our interfaces IMediaEvent and IUnknown\r
+\r
+STDMETHODIMP\r
+CMediaEvent::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    ValidateReadWritePtr(ppv,sizeof(PVOID));\r
+    if (riid == IID_IMediaEvent || riid == IID_IMediaEventEx) {\r
+       return GetInterface( (IMediaEventEx *) this, ppv);\r
+    } else {\r
+       return CUnknown::NonDelegatingQueryInterface(riid, ppv);\r
+    }\r
+}\r
+\r
+\r
+// return 1 if we support GetTypeInfo\r
+\r
+STDMETHODIMP\r
+CMediaEvent::GetTypeInfoCount(__out UINT * pctinfo)\r
+{\r
+    return m_basedisp.GetTypeInfoCount(pctinfo);\r
+}\r
+\r
+\r
+// attempt to find our type library\r
+\r
+STDMETHODIMP\r
+CMediaEvent::GetTypeInfo(\r
+  UINT itinfo,\r
+  LCID lcid,\r
+  __deref_out ITypeInfo ** pptinfo)\r
+{\r
+    return m_basedisp.GetTypeInfo(\r
+               IID_IMediaEvent,\r
+               itinfo,\r
+               lcid,\r
+               pptinfo);\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CMediaEvent::GetIDsOfNames(\r
+  REFIID riid,\r
+  __in_ecount(cNames) LPOLESTR * rgszNames,\r
+  UINT cNames,\r
+  LCID lcid,\r
+  __out_ecount(cNames) DISPID * rgdispid)\r
+{\r
+    return m_basedisp.GetIDsOfNames(\r
+                       IID_IMediaEvent,\r
+                       rgszNames,\r
+                       cNames,\r
+                       lcid,\r
+                       rgdispid);\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CMediaEvent::Invoke(\r
+  DISPID dispidMember,\r
+  REFIID riid,\r
+  LCID lcid,\r
+  WORD wFlags,\r
+  __in DISPPARAMS * pdispparams,\r
+  __out_opt VARIANT * pvarResult,\r
+  __out_opt EXCEPINFO * pexcepinfo,\r
+  __out_opt UINT * puArgErr)\r
+{\r
+    // this parameter is a dead leftover from an earlier interface\r
+    if (IID_NULL != riid) {\r
+       return DISP_E_UNKNOWNINTERFACE;\r
+    }\r
+\r
+    ITypeInfo * pti;\r
+    HRESULT hr = GetTypeInfo(0, lcid, &pti);\r
+\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    hr = pti->Invoke(\r
+           (IMediaEvent *)this,\r
+           dispidMember,\r
+           wFlags,\r
+           pdispparams,\r
+           pvarResult,\r
+           pexcepinfo,\r
+           puArgErr);\r
+\r
+    pti->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+// --- CMediaPosition implementation ----------\r
+\r
+\r
+CMediaPosition::CMediaPosition(__in_opt LPCTSTR name,__in_opt LPUNKNOWN pUnk) :\r
+    CUnknown(name, pUnk)\r
+{\r
+}\r
+\r
+CMediaPosition::CMediaPosition(__in_opt LPCTSTR name,\r
+                               __in_opt LPUNKNOWN pUnk,\r
+                               __inout HRESULT * phr) :\r
+    CUnknown(name, pUnk)\r
+{\r
+    UNREFERENCED_PARAMETER(phr);\r
+}\r
+\r
+\r
+// expose our interfaces IMediaPosition and IUnknown\r
+\r
+STDMETHODIMP\r
+CMediaPosition::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    ValidateReadWritePtr(ppv,sizeof(PVOID));\r
+    if (riid == IID_IMediaPosition) {\r
+       return GetInterface( (IMediaPosition *) this, ppv);\r
+    } else {\r
+       return CUnknown::NonDelegatingQueryInterface(riid, ppv);\r
+    }\r
+}\r
+\r
+\r
+// return 1 if we support GetTypeInfo\r
+\r
+STDMETHODIMP\r
+CMediaPosition::GetTypeInfoCount(__out UINT * pctinfo)\r
+{\r
+    return m_basedisp.GetTypeInfoCount(pctinfo);\r
+}\r
+\r
+\r
+// attempt to find our type library\r
+\r
+STDMETHODIMP\r
+CMediaPosition::GetTypeInfo(\r
+  UINT itinfo,\r
+  LCID lcid,\r
+  __deref_out ITypeInfo ** pptinfo)\r
+{\r
+    return m_basedisp.GetTypeInfo(\r
+               IID_IMediaPosition,\r
+               itinfo,\r
+               lcid,\r
+               pptinfo);\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CMediaPosition::GetIDsOfNames(\r
+  REFIID riid,\r
+  __in_ecount(cNames) LPOLESTR * rgszNames,\r
+  UINT cNames,\r
+  LCID lcid,\r
+  __out_ecount(cNames) DISPID * rgdispid)\r
+{\r
+    return m_basedisp.GetIDsOfNames(\r
+                       IID_IMediaPosition,\r
+                       rgszNames,\r
+                       cNames,\r
+                       lcid,\r
+                       rgdispid);\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CMediaPosition::Invoke(\r
+  DISPID dispidMember,\r
+  REFIID riid,\r
+  LCID lcid,\r
+  WORD wFlags,\r
+  __in DISPPARAMS * pdispparams,\r
+  __out_opt VARIANT * pvarResult,\r
+  __out_opt EXCEPINFO * pexcepinfo,\r
+  __out_opt UINT * puArgErr)\r
+{\r
+    // this parameter is a dead leftover from an earlier interface\r
+    if (IID_NULL != riid) {\r
+       return DISP_E_UNKNOWNINTERFACE;\r
+    }\r
+\r
+    ITypeInfo * pti;\r
+    HRESULT hr = GetTypeInfo(0, lcid, &pti);\r
+\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    hr = pti->Invoke(\r
+           (IMediaPosition *)this,\r
+           dispidMember,\r
+           wFlags,\r
+           pdispparams,\r
+           pvarResult,\r
+           pexcepinfo,\r
+           puArgErr);\r
+\r
+    pti->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+// --- IMediaPosition and IMediaSeeking pass through class ----------\r
+\r
+\r
+CPosPassThru::CPosPassThru(__in_opt LPCTSTR pName,\r
+                          __in_opt LPUNKNOWN pUnk,\r
+                          __inout HRESULT *phr,\r
+                          IPin *pPin) :\r
+    CMediaPosition(pName,pUnk),\r
+    m_pPin(pPin)\r
+{\r
+    if (pPin == NULL) {\r
+       *phr = E_POINTER;\r
+       return;\r
+    }\r
+}\r
+\r
+\r
+// Expose our IMediaSeeking and IMediaPosition interfaces\r
+\r
+STDMETHODIMP\r
+CPosPassThru::NonDelegatingQueryInterface(REFIID riid,__deref_out void **ppv)\r
+{\r
+    CheckPointer(ppv,E_POINTER);\r
+    *ppv = NULL;\r
+\r
+    if (riid == IID_IMediaSeeking) {\r
+       return GetInterface( static_cast<IMediaSeeking *>(this), ppv);\r
+    }\r
+    return CMediaPosition::NonDelegatingQueryInterface(riid,ppv);\r
+}\r
+\r
+\r
+// Return the IMediaPosition interface from our peer\r
+\r
+HRESULT\r
+CPosPassThru::GetPeer(IMediaPosition ** ppMP)\r
+{\r
+    *ppMP = NULL;\r
+\r
+    IPin *pConnected;\r
+    HRESULT hr = m_pPin->ConnectedTo(&pConnected);\r
+    if (FAILED(hr)) {\r
+       return E_NOTIMPL;\r
+    }\r
+    IMediaPosition * pMP;\r
+    hr = pConnected->QueryInterface(IID_IMediaPosition, (void **) &pMP);\r
+    pConnected->Release();\r
+    if (FAILED(hr)) {\r
+       return E_NOTIMPL;\r
+    }\r
+\r
+    *ppMP = pMP;\r
+    return S_OK;\r
+}\r
+\r
+\r
+// Return the IMediaSeeking interface from our peer\r
+\r
+HRESULT\r
+CPosPassThru::GetPeerSeeking(__deref_out IMediaSeeking ** ppMS)\r
+{\r
+    *ppMS = NULL;\r
+\r
+    IPin *pConnected;\r
+    HRESULT hr = m_pPin->ConnectedTo(&pConnected);\r
+    if (FAILED(hr)) {\r
+       return E_NOTIMPL;\r
+    }\r
+    IMediaSeeking * pMS;\r
+    hr = pConnected->QueryInterface(IID_IMediaSeeking, (void **) &pMS);\r
+    pConnected->Release();\r
+    if (FAILED(hr)) {\r
+       return E_NOTIMPL;\r
+    }\r
+\r
+    *ppMS = pMS;\r
+    return S_OK;\r
+}\r
+\r
+\r
+// --- IMediaSeeking methods ----------\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::GetCapabilities(__out DWORD * pCaps)\r
+{\r
+    IMediaSeeking* pMS;\r
+    HRESULT hr = GetPeerSeeking(&pMS);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    hr = pMS->GetCapabilities(pCaps);\r
+    pMS->Release();\r
+    return hr;\r
+}\r
+\r
+STDMETHODIMP\r
+CPosPassThru::CheckCapabilities(__inout DWORD * pCaps)\r
+{\r
+    IMediaSeeking* pMS;\r
+    HRESULT hr = GetPeerSeeking(&pMS);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    hr = pMS->CheckCapabilities(pCaps);\r
+    pMS->Release();\r
+    return hr;\r
+}\r
+\r
+STDMETHODIMP\r
+CPosPassThru::IsFormatSupported(const GUID * pFormat)\r
+{\r
+    IMediaSeeking* pMS;\r
+    HRESULT hr = GetPeerSeeking(&pMS);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    hr = pMS->IsFormatSupported(pFormat);\r
+    pMS->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::QueryPreferredFormat(__out GUID *pFormat)\r
+{\r
+    IMediaSeeking* pMS;\r
+    HRESULT hr = GetPeerSeeking(&pMS);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    hr = pMS->QueryPreferredFormat(pFormat);\r
+    pMS->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::SetTimeFormat(const GUID * pFormat)\r
+{\r
+    IMediaSeeking* pMS;\r
+    HRESULT hr = GetPeerSeeking(&pMS);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    hr = pMS->SetTimeFormat(pFormat);\r
+    pMS->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::GetTimeFormat(__out GUID *pFormat)\r
+{\r
+    IMediaSeeking* pMS;\r
+    HRESULT hr = GetPeerSeeking(&pMS);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    hr = pMS->GetTimeFormat(pFormat);\r
+    pMS->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::IsUsingTimeFormat(const GUID * pFormat)\r
+{\r
+    IMediaSeeking* pMS;\r
+    HRESULT hr = GetPeerSeeking(&pMS);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    hr = pMS->IsUsingTimeFormat(pFormat);\r
+    pMS->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::ConvertTimeFormat(__out LONGLONG * pTarget, \r
+                                __in_opt const GUID * pTargetFormat,\r
+                               LONGLONG Source, \r
+                                __in_opt const GUID * pSourceFormat )\r
+{\r
+    IMediaSeeking* pMS;\r
+    HRESULT hr = GetPeerSeeking(&pMS);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    hr = pMS->ConvertTimeFormat(pTarget, pTargetFormat, Source, pSourceFormat );\r
+    pMS->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::SetPositions( __inout_opt LONGLONG * pCurrent, \r
+                            DWORD CurrentFlags, \r
+                            __inout_opt LONGLONG * pStop, \r
+                            DWORD StopFlags )\r
+{\r
+    IMediaSeeking* pMS;\r
+    HRESULT hr = GetPeerSeeking(&pMS);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    hr = pMS->SetPositions(pCurrent, CurrentFlags, pStop, StopFlags );\r
+    pMS->Release();\r
+    return hr;\r
+}\r
+\r
+STDMETHODIMP\r
+CPosPassThru::GetPositions(__out_opt LONGLONG *pCurrent, __out_opt LONGLONG * pStop)\r
+{\r
+    IMediaSeeking* pMS;\r
+    HRESULT hr = GetPeerSeeking(&pMS);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    hr = pMS->GetPositions(pCurrent,pStop);\r
+    pMS->Release();\r
+    return hr;\r
+}\r
+\r
+HRESULT\r
+CPosPassThru::GetSeekingLongLong\r
+( HRESULT (__stdcall IMediaSeeking::*pMethod)( __out LONGLONG * )\r
+, LONGLONG * pll\r
+)\r
+{\r
+    IMediaSeeking* pMS;\r
+    HRESULT hr = GetPeerSeeking(&pMS);\r
+    if (SUCCEEDED(hr))\r
+    {\r
+       hr = (pMS->*pMethod)(pll);\r
+       pMS->Release();\r
+    }\r
+    return hr;\r
+}\r
+\r
+// If we don't have a current position then ask upstream\r
+\r
+STDMETHODIMP\r
+CPosPassThru::GetCurrentPosition(__out LONGLONG *pCurrent)\r
+{\r
+    // Can we report the current position\r
+    HRESULT hr = GetMediaTime(pCurrent,NULL);\r
+    if (SUCCEEDED(hr)) hr = NOERROR;\r
+    else hr = GetSeekingLongLong( &IMediaSeeking::GetCurrentPosition, pCurrent );\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::GetStopPosition(__out LONGLONG *pStop)\r
+{\r
+    return GetSeekingLongLong( &IMediaSeeking::GetStopPosition, pStop );;\r
+}\r
+\r
+STDMETHODIMP\r
+CPosPassThru::GetDuration(__out LONGLONG *pDuration)\r
+{\r
+    return GetSeekingLongLong( &IMediaSeeking::GetDuration, pDuration );;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::GetPreroll(__out LONGLONG *pllPreroll)\r
+{\r
+    return GetSeekingLongLong( &IMediaSeeking::GetPreroll, pllPreroll );;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::GetAvailable( __out_opt LONGLONG *pEarliest, __out_opt LONGLONG *pLatest )\r
+{\r
+    IMediaSeeking* pMS;\r
+    HRESULT hr = GetPeerSeeking(&pMS);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    hr = pMS->GetAvailable( pEarliest, pLatest );\r
+    pMS->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::GetRate(__out double * pdRate)\r
+{\r
+    IMediaSeeking* pMS;\r
+    HRESULT hr = GetPeerSeeking(&pMS);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+    hr = pMS->GetRate(pdRate);\r
+    pMS->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::SetRate(double dRate)\r
+{\r
+    if (0.0 == dRate) {\r
+               return E_INVALIDARG;\r
+    }\r
+\r
+    IMediaSeeking* pMS;\r
+    HRESULT hr = GetPeerSeeking(&pMS);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+    hr = pMS->SetRate(dRate);\r
+    pMS->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+\r
+\r
+// --- IMediaPosition methods ----------\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::get_Duration(__out REFTIME * plength)\r
+{\r
+    IMediaPosition* pMP;\r
+    HRESULT hr = GetPeer(&pMP);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    hr = pMP->get_Duration(plength);\r
+    pMP->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::get_CurrentPosition(__out REFTIME * pllTime)\r
+{\r
+    IMediaPosition* pMP;\r
+    HRESULT hr = GetPeer(&pMP);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+    hr = pMP->get_CurrentPosition(pllTime);\r
+    pMP->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::put_CurrentPosition(REFTIME llTime)\r
+{\r
+    IMediaPosition* pMP;\r
+    HRESULT hr = GetPeer(&pMP);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+    hr = pMP->put_CurrentPosition(llTime);\r
+    pMP->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::get_StopTime(__out REFTIME * pllTime)\r
+{\r
+    IMediaPosition* pMP;\r
+    HRESULT hr = GetPeer(&pMP);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+    hr = pMP->get_StopTime(pllTime);\r
+    pMP->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::put_StopTime(REFTIME llTime)\r
+{\r
+    IMediaPosition* pMP;\r
+    HRESULT hr = GetPeer(&pMP);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+    hr = pMP->put_StopTime(llTime);\r
+    pMP->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::get_PrerollTime(__out REFTIME * pllTime)\r
+{\r
+    IMediaPosition* pMP;\r
+    HRESULT hr = GetPeer(&pMP);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+    hr = pMP->get_PrerollTime(pllTime);\r
+    pMP->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::put_PrerollTime(REFTIME llTime)\r
+{\r
+    IMediaPosition* pMP;\r
+    HRESULT hr = GetPeer(&pMP);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+    hr = pMP->put_PrerollTime(llTime);\r
+    pMP->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::get_Rate(__out double * pdRate)\r
+{\r
+    IMediaPosition* pMP;\r
+    HRESULT hr = GetPeer(&pMP);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+    hr = pMP->get_Rate(pdRate);\r
+    pMP->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::put_Rate(double dRate)\r
+{\r
+    if (0.0 == dRate) {\r
+               return E_INVALIDARG;\r
+    }\r
+\r
+    IMediaPosition* pMP;\r
+    HRESULT hr = GetPeer(&pMP);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+    hr = pMP->put_Rate(dRate);\r
+    pMP->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::CanSeekForward(__out LONG *pCanSeekForward)\r
+{\r
+    IMediaPosition* pMP;\r
+    HRESULT hr = GetPeer(&pMP);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+    hr = pMP->CanSeekForward(pCanSeekForward);\r
+    pMP->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CPosPassThru::CanSeekBackward(__out LONG *pCanSeekBackward)\r
+{\r
+    IMediaPosition* pMP;\r
+    HRESULT hr = GetPeer(&pMP);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+    hr = pMP->CanSeekBackward(pCanSeekBackward);\r
+    pMP->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+// --- Implements the CRendererPosPassThru class ----------\r
+\r
+\r
+// Media times (eg current frame, field, sample etc) are passed through the\r
+// filtergraph in media samples. When a renderer gets a sample with media\r
+// times in it, it will call one of the RegisterMediaTime methods we expose\r
+// (one takes an IMediaSample, the other takes the media times direct). We\r
+// store the media times internally and return them in GetCurrentPosition.\r
+\r
+CRendererPosPassThru::CRendererPosPassThru(__in_opt LPCTSTR pName,\r
+                                          __in_opt LPUNKNOWN pUnk,\r
+                                          __inout HRESULT *phr,\r
+                                          IPin *pPin) :\r
+    CPosPassThru(pName,pUnk,phr,pPin),\r
+    m_StartMedia(0),\r
+    m_EndMedia(0),\r
+    m_bReset(TRUE)\r
+{\r
+}\r
+\r
+\r
+// Sets the media times the object should report\r
+\r
+HRESULT\r
+CRendererPosPassThru::RegisterMediaTime(IMediaSample *pMediaSample)\r
+{\r
+    ASSERT(pMediaSample);\r
+    LONGLONG StartMedia;\r
+    LONGLONG EndMedia;\r
+\r
+    CAutoLock cAutoLock(&m_PositionLock);\r
+\r
+    // Get the media times from the sample\r
+\r
+    HRESULT hr = pMediaSample->GetTime(&StartMedia,&EndMedia);\r
+    if (FAILED(hr))\r
+    {\r
+       ASSERT(hr == VFW_E_SAMPLE_TIME_NOT_SET);\r
+       return hr;\r
+    }\r
+\r
+    m_StartMedia = StartMedia;\r
+    m_EndMedia = EndMedia;\r
+    m_bReset = FALSE;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Sets the media times the object should report\r
+\r
+HRESULT\r
+CRendererPosPassThru::RegisterMediaTime(LONGLONG StartTime,LONGLONG EndTime)\r
+{\r
+    CAutoLock cAutoLock(&m_PositionLock);\r
+    m_StartMedia = StartTime;\r
+    m_EndMedia = EndTime;\r
+    m_bReset = FALSE;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Return the current media times registered in the object\r
+\r
+HRESULT\r
+CRendererPosPassThru::GetMediaTime(__out LONGLONG *pStartTime, __out_opt LONGLONG *pEndTime)\r
+{\r
+    ASSERT(pStartTime);\r
+\r
+    CAutoLock cAutoLock(&m_PositionLock);\r
+    if (m_bReset == TRUE) {\r
+       return E_FAIL;\r
+    }\r
+\r
+    // We don't have to return the end time\r
+\r
+    HRESULT hr = ConvertTimeFormat( pStartTime, 0, m_StartMedia, &TIME_FORMAT_MEDIA_TIME );\r
+    if (pEndTime && SUCCEEDED(hr)) {\r
+       hr = ConvertTimeFormat( pEndTime, 0, m_EndMedia, &TIME_FORMAT_MEDIA_TIME );\r
+    }\r
+    return hr;\r
+}\r
+\r
+\r
+// Resets the media times we hold\r
+\r
+HRESULT\r
+CRendererPosPassThru::ResetMediaTime()\r
+{\r
+    CAutoLock cAutoLock(&m_PositionLock);\r
+    m_StartMedia = 0;\r
+    m_EndMedia = 0;\r
+    m_bReset = TRUE;\r
+    return NOERROR;\r
+}\r
+\r
+// Intended to be called by the owing filter during EOS processing so\r
+// that the media times can be adjusted to the stop time.  This ensures\r
+// that the GetCurrentPosition will actully get to the stop position.\r
+HRESULT\r
+CRendererPosPassThru::EOS()\r
+{\r
+    HRESULT hr;\r
+\r
+    if ( m_bReset == TRUE ) hr = E_FAIL;\r
+    else\r
+    {\r
+       LONGLONG llStop;\r
+       if SUCCEEDED(hr=GetStopPosition(&llStop))\r
+       {\r
+           CAutoLock cAutoLock(&m_PositionLock);\r
+           m_StartMedia =\r
+           m_EndMedia   = llStop;\r
+       }\r
+    }\r
+    return hr;\r
+}\r
+\r
+// -- CSourceSeeking implementation ------------\r
+\r
+CSourceSeeking::CSourceSeeking(\r
+    __in_opt LPCTSTR pName,\r
+    __in_opt LPUNKNOWN pUnk,\r
+    __inout HRESULT* phr,\r
+    __in CCritSec * pLock) :\r
+        CUnknown(pName, pUnk),\r
+        m_pLock(pLock),\r
+        m_rtStart((long)0)\r
+{\r
+    m_rtStop = _I64_MAX / 2;\r
+    m_rtDuration = m_rtStop;\r
+    m_dRateSeeking = 1.0;\r
+\r
+    m_dwSeekingCaps = AM_SEEKING_CanSeekForwards\r
+        | AM_SEEKING_CanSeekBackwards\r
+        | AM_SEEKING_CanSeekAbsolute\r
+        | AM_SEEKING_CanGetStopPos\r
+        | AM_SEEKING_CanGetDuration;\r
+}\r
+\r
+HRESULT CSourceSeeking::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    if(riid == IID_IMediaSeeking) {\r
+        CheckPointer(ppv, E_POINTER);\r
+        return GetInterface(static_cast<IMediaSeeking *>(this), ppv);\r
+    }\r
+    else {\r
+        return CUnknown::NonDelegatingQueryInterface(riid, ppv);\r
+    }\r
+}\r
+\r
+\r
+HRESULT CSourceSeeking::IsFormatSupported(const GUID * pFormat)\r
+{\r
+    CheckPointer(pFormat, E_POINTER);\r
+    // only seeking in time (REFERENCE_TIME units) is supported\r
+    return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE;\r
+}\r
+\r
+HRESULT CSourceSeeking::QueryPreferredFormat(__out GUID *pFormat)\r
+{\r
+    CheckPointer(pFormat, E_POINTER);\r
+    *pFormat = TIME_FORMAT_MEDIA_TIME;\r
+    return S_OK;\r
+}\r
+\r
+HRESULT CSourceSeeking::SetTimeFormat(const GUID * pFormat)\r
+{\r
+    CheckPointer(pFormat, E_POINTER);\r
+\r
+    // nothing to set; just check that it's TIME_FORMAT_TIME\r
+    return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : E_INVALIDARG;\r
+}\r
+\r
+HRESULT CSourceSeeking::IsUsingTimeFormat(const GUID * pFormat)\r
+{\r
+    CheckPointer(pFormat, E_POINTER);\r
+    return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE;\r
+}\r
+\r
+HRESULT CSourceSeeking::GetTimeFormat(__out GUID *pFormat)\r
+{\r
+    CheckPointer(pFormat, E_POINTER);\r
+    *pFormat = TIME_FORMAT_MEDIA_TIME;\r
+    return S_OK;\r
+}\r
+\r
+HRESULT CSourceSeeking::GetDuration(__out LONGLONG *pDuration)\r
+{\r
+    CheckPointer(pDuration, E_POINTER);\r
+    CAutoLock lock(m_pLock);\r
+    *pDuration = m_rtDuration;\r
+    return S_OK;\r
+}\r
+\r
+HRESULT CSourceSeeking::GetStopPosition(__out LONGLONG *pStop)\r
+{\r
+    CheckPointer(pStop, E_POINTER);\r
+    CAutoLock lock(m_pLock);\r
+    *pStop = m_rtStop;\r
+    return S_OK;\r
+}\r
+\r
+HRESULT CSourceSeeking::GetCurrentPosition(__out LONGLONG *pCurrent)\r
+{\r
+    // GetCurrentPosition is typically supported only in renderers and\r
+    // not in source filters.\r
+    return E_NOTIMPL;\r
+}\r
+\r
+HRESULT CSourceSeeking::GetCapabilities( __out DWORD * pCapabilities )\r
+{\r
+    CheckPointer(pCapabilities, E_POINTER);\r
+    *pCapabilities = m_dwSeekingCaps;\r
+    return S_OK;\r
+}\r
+\r
+HRESULT CSourceSeeking::CheckCapabilities( __inout DWORD * pCapabilities )\r
+{\r
+    CheckPointer(pCapabilities, E_POINTER);\r
+\r
+    // make sure all requested capabilities are in our mask\r
+    return (~m_dwSeekingCaps & *pCapabilities) ? S_FALSE : S_OK;\r
+}\r
+\r
+HRESULT CSourceSeeking::ConvertTimeFormat( __out LONGLONG * pTarget, \r
+                                           __in_opt const GUID * pTargetFormat,\r
+                                           LONGLONG Source, \r
+                                           __in_opt const GUID * pSourceFormat )\r
+{\r
+    CheckPointer(pTarget, E_POINTER);\r
+    // format guids can be null to indicate current format\r
+\r
+    // since we only support TIME_FORMAT_MEDIA_TIME, we don't really\r
+    // offer any conversions.\r
+    if(pTargetFormat == 0 || *pTargetFormat == TIME_FORMAT_MEDIA_TIME)\r
+    {\r
+        if(pSourceFormat == 0 || *pSourceFormat == TIME_FORMAT_MEDIA_TIME)\r
+        {\r
+            *pTarget = Source;\r
+            return S_OK;\r
+        }\r
+    }\r
+\r
+    return E_INVALIDARG;\r
+}\r
+\r
+\r
+HRESULT CSourceSeeking::SetPositions( __inout_opt LONGLONG * pCurrent,  \r
+                                      DWORD CurrentFlags, \r
+                                      __inout_opt LONGLONG * pStop,  \r
+                                      DWORD StopFlags )\r
+{\r
+    DWORD StopPosBits = StopFlags & AM_SEEKING_PositioningBitsMask;\r
+    DWORD StartPosBits = CurrentFlags & AM_SEEKING_PositioningBitsMask;\r
+\r
+    if(StopFlags) {\r
+        CheckPointer(pStop, E_POINTER);\r
+\r
+        // accept only relative, incremental, or absolute positioning\r
+        if(StopPosBits != StopFlags) {\r
+            return E_INVALIDARG;\r
+        }\r
+    }\r
+\r
+    if(CurrentFlags) {\r
+        CheckPointer(pCurrent, E_POINTER);\r
+        if(StartPosBits != AM_SEEKING_AbsolutePositioning &&\r
+           StartPosBits != AM_SEEKING_RelativePositioning) {\r
+            return E_INVALIDARG;\r
+        }\r
+    }\r
+\r
+\r
+    // scope for autolock\r
+    {\r
+        CAutoLock lock(m_pLock);\r
+\r
+        // set start position\r
+        if(StartPosBits == AM_SEEKING_AbsolutePositioning)\r
+        {\r
+            m_rtStart = *pCurrent;\r
+        }\r
+        else if(StartPosBits == AM_SEEKING_RelativePositioning)\r
+        {\r
+            m_rtStart += *pCurrent;\r
+        }\r
+\r
+        // set stop position\r
+        if(StopPosBits == AM_SEEKING_AbsolutePositioning)\r
+        {\r
+            m_rtStop = *pStop;\r
+        }\r
+        else if(StopPosBits == AM_SEEKING_IncrementalPositioning)\r
+        {\r
+            m_rtStop = m_rtStart + *pStop;\r
+        }\r
+        else if(StopPosBits == AM_SEEKING_RelativePositioning)\r
+        {\r
+            m_rtStop = m_rtStop + *pStop;\r
+        }\r
+    }\r
+\r
+\r
+    HRESULT hr = S_OK;\r
+    if(SUCCEEDED(hr) && StopPosBits) {\r
+        hr = ChangeStop();\r
+    }\r
+    if(StartPosBits) {\r
+        hr = ChangeStart();\r
+    }\r
+\r
+    return hr;\r
+}\r
+\r
+\r
+HRESULT CSourceSeeking::GetPositions( __out_opt LONGLONG * pCurrent, __out_opt LONGLONG * pStop )\r
+{\r
+    if(pCurrent) {\r
+        *pCurrent = m_rtStart;\r
+    }\r
+    if(pStop) {\r
+        *pStop = m_rtStop;\r
+    }\r
+\r
+    return S_OK;;\r
+}\r
+\r
+\r
+HRESULT CSourceSeeking::GetAvailable( __out_opt LONGLONG * pEarliest, __out_opt LONGLONG * pLatest )\r
+{\r
+    if(pEarliest) {\r
+        *pEarliest = 0;\r
+    }\r
+    if(pLatest) {\r
+        CAutoLock lock(m_pLock);\r
+        *pLatest = m_rtDuration;\r
+    }\r
+    return S_OK;\r
+}\r
+\r
+HRESULT CSourceSeeking::SetRate( double dRate)\r
+{\r
+    {\r
+        CAutoLock lock(m_pLock);\r
+        m_dRateSeeking = dRate;\r
+    }\r
+    return ChangeRate();\r
+}\r
+\r
+HRESULT CSourceSeeking::GetRate( __out double * pdRate)\r
+{\r
+    CheckPointer(pdRate, E_POINTER);\r
+    CAutoLock lock(m_pLock);\r
+    *pdRate = m_dRateSeeking;\r
+    return S_OK;\r
+}\r
+\r
+HRESULT CSourceSeeking::GetPreroll(__out LONGLONG *pPreroll)\r
+{\r
+    CheckPointer(pPreroll, E_POINTER);\r
+    *pPreroll = 0;\r
+    return S_OK;\r
+}\r
+\r
+\r
+\r
+\r
+\r
+// --- CSourcePosition implementation ----------\r
+\r
+\r
+CSourcePosition::CSourcePosition(__in_opt LPCTSTR pName,\r
+                                __in_opt LPUNKNOWN pUnk,\r
+                                __inout HRESULT* phr,\r
+                                __in CCritSec * pLock) :\r
+    CMediaPosition(pName, pUnk),\r
+    m_pLock(pLock),\r
+    m_Start(CRefTime((LONGLONG)0))\r
+{\r
+    m_Stop = _I64_MAX;\r
+    m_Rate = 1.0;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CSourcePosition::get_Duration(__out REFTIME * plength)\r
+{\r
+    CheckPointer(plength,E_POINTER);\r
+    ValidateReadWritePtr(plength,sizeof(REFTIME));\r
+    CAutoLock lock(m_pLock);\r
+\r
+    *plength = m_Duration;\r
+    return S_OK;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CSourcePosition::put_CurrentPosition(REFTIME llTime)\r
+{\r
+    m_pLock->Lock();\r
+    m_Start = llTime;\r
+    m_pLock->Unlock();\r
+\r
+    return ChangeStart();\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CSourcePosition::get_StopTime(__out REFTIME * pllTime)\r
+{\r
+    CheckPointer(pllTime,E_POINTER);\r
+    ValidateReadWritePtr(pllTime,sizeof(REFTIME));\r
+    CAutoLock lock(m_pLock);\r
+\r
+    *pllTime = m_Stop;\r
+    return S_OK;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CSourcePosition::put_StopTime(REFTIME llTime)\r
+{\r
+    m_pLock->Lock();\r
+    m_Stop = llTime;\r
+    m_pLock->Unlock();\r
+\r
+    return ChangeStop();\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CSourcePosition::get_PrerollTime(__out REFTIME * pllTime)\r
+{\r
+    CheckPointer(pllTime,E_POINTER);\r
+    ValidateReadWritePtr(pllTime,sizeof(REFTIME));\r
+    return E_NOTIMPL;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CSourcePosition::put_PrerollTime(REFTIME llTime)\r
+{\r
+    return E_NOTIMPL;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CSourcePosition::get_Rate(__out double * pdRate)\r
+{\r
+    CheckPointer(pdRate,E_POINTER);\r
+    ValidateReadWritePtr(pdRate,sizeof(double));\r
+    CAutoLock lock(m_pLock);\r
+\r
+    *pdRate = m_Rate;\r
+    return S_OK;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CSourcePosition::put_Rate(double dRate)\r
+{\r
+    m_pLock->Lock();\r
+    m_Rate = dRate;\r
+    m_pLock->Unlock();\r
+\r
+    return ChangeRate();\r
+}\r
+\r
+\r
+// By default we can seek forwards\r
+\r
+STDMETHODIMP\r
+CSourcePosition::CanSeekForward(__out LONG *pCanSeekForward)\r
+{\r
+    CheckPointer(pCanSeekForward,E_POINTER);\r
+    *pCanSeekForward = OATRUE;\r
+    return S_OK;\r
+}\r
+\r
+\r
+// By default we can seek backwards\r
+\r
+STDMETHODIMP\r
+CSourcePosition::CanSeekBackward(__out LONG *pCanSeekBackward)\r
+{\r
+    CheckPointer(pCanSeekBackward,E_POINTER);\r
+    *pCanSeekBackward = OATRUE;\r
+    return S_OK;\r
+}\r
+\r
+\r
+// --- Implementation of CBasicAudio class ----------\r
+\r
+\r
+CBasicAudio::CBasicAudio(__in_opt LPCTSTR pName,__in_opt LPUNKNOWN punk) :\r
+    CUnknown(pName, punk)\r
+{\r
+}\r
+\r
+// overriden to publicise our interfaces\r
+\r
+STDMETHODIMP\r
+CBasicAudio::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    ValidateReadWritePtr(ppv,sizeof(PVOID));\r
+    if (riid == IID_IBasicAudio) {\r
+       return GetInterface( (IBasicAudio *) this, ppv);\r
+    } else {\r
+       return CUnknown::NonDelegatingQueryInterface(riid, ppv);\r
+    }\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CBasicAudio::GetTypeInfoCount(__out UINT * pctinfo)\r
+{\r
+    return m_basedisp.GetTypeInfoCount(pctinfo);\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CBasicAudio::GetTypeInfo(\r
+  UINT itinfo,\r
+  LCID lcid,\r
+  __deref_out ITypeInfo ** pptinfo)\r
+{\r
+    return m_basedisp.GetTypeInfo(\r
+               IID_IBasicAudio,\r
+               itinfo,\r
+               lcid,\r
+               pptinfo);\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CBasicAudio::GetIDsOfNames(\r
+  REFIID riid,\r
+  __in_ecount(cNames) LPOLESTR * rgszNames,\r
+  UINT cNames,\r
+  LCID lcid,\r
+  __out_ecount(cNames) DISPID * rgdispid)\r
+{\r
+    return m_basedisp.GetIDsOfNames(\r
+                       IID_IBasicAudio,\r
+                       rgszNames,\r
+                       cNames,\r
+                       lcid,\r
+                       rgdispid);\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CBasicAudio::Invoke(\r
+  DISPID dispidMember,\r
+  REFIID riid,\r
+  LCID lcid,\r
+  WORD wFlags,\r
+  __in DISPPARAMS * pdispparams,\r
+  __out_opt VARIANT * pvarResult,\r
+  __out_opt EXCEPINFO * pexcepinfo,\r
+  __out_opt UINT * puArgErr)\r
+{\r
+    // this parameter is a dead leftover from an earlier interface\r
+    if (IID_NULL != riid) {\r
+       return DISP_E_UNKNOWNINTERFACE;\r
+    }\r
+\r
+    ITypeInfo * pti;\r
+    HRESULT hr = GetTypeInfo(0, lcid, &pti);\r
+\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    hr = pti->Invoke(\r
+           (IBasicAudio *)this,\r
+           dispidMember,\r
+           wFlags,\r
+           pdispparams,\r
+           pvarResult,\r
+           pexcepinfo,\r
+           puArgErr);\r
+\r
+    pti->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+// --- IVideoWindow implementation ----------\r
+\r
+CBaseVideoWindow::CBaseVideoWindow(__in_opt LPCTSTR pName,__in_opt LPUNKNOWN punk) :\r
+    CUnknown(pName, punk)\r
+{\r
+}\r
+\r
+\r
+// overriden to publicise our interfaces\r
+\r
+STDMETHODIMP\r
+CBaseVideoWindow::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    ValidateReadWritePtr(ppv,sizeof(PVOID));\r
+    if (riid == IID_IVideoWindow) {\r
+       return GetInterface( (IVideoWindow *) this, ppv);\r
+    } else {\r
+       return CUnknown::NonDelegatingQueryInterface(riid, ppv);\r
+    }\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CBaseVideoWindow::GetTypeInfoCount(__out UINT * pctinfo)\r
+{\r
+    return m_basedisp.GetTypeInfoCount(pctinfo);\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CBaseVideoWindow::GetTypeInfo(\r
+  UINT itinfo,\r
+  LCID lcid,\r
+  __deref_out ITypeInfo ** pptinfo)\r
+{\r
+    return m_basedisp.GetTypeInfo(\r
+               IID_IVideoWindow,\r
+               itinfo,\r
+               lcid,\r
+               pptinfo);\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CBaseVideoWindow::GetIDsOfNames(\r
+  REFIID riid,\r
+  __in_ecount(cNames) LPOLESTR * rgszNames,\r
+  UINT cNames,\r
+  LCID lcid,\r
+  __out_ecount(cNames) DISPID * rgdispid)\r
+{\r
+    return m_basedisp.GetIDsOfNames(\r
+                       IID_IVideoWindow,\r
+                       rgszNames,\r
+                       cNames,\r
+                       lcid,\r
+                       rgdispid);\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CBaseVideoWindow::Invoke(\r
+  DISPID dispidMember,\r
+  REFIID riid,\r
+  LCID lcid,\r
+  WORD wFlags,\r
+  __in DISPPARAMS * pdispparams,\r
+  __out_opt VARIANT * pvarResult,\r
+  __out_opt EXCEPINFO * pexcepinfo,\r
+  __out_opt UINT * puArgErr)\r
+{\r
+    // this parameter is a dead leftover from an earlier interface\r
+    if (IID_NULL != riid) {\r
+       return DISP_E_UNKNOWNINTERFACE;\r
+    }\r
+\r
+    ITypeInfo * pti;\r
+    HRESULT hr = GetTypeInfo(0, lcid, &pti);\r
+\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    hr = pti->Invoke(\r
+           (IVideoWindow *)this,\r
+           dispidMember,\r
+           wFlags,\r
+           pdispparams,\r
+           pvarResult,\r
+           pexcepinfo,\r
+           puArgErr);\r
+\r
+    pti->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+// --- IBasicVideo implementation ----------\r
+\r
+\r
+CBaseBasicVideo::CBaseBasicVideo(__in_opt LPCTSTR pName,__in_opt LPUNKNOWN punk) :\r
+    CUnknown(pName, punk)\r
+{\r
+}\r
+\r
+\r
+// overriden to publicise our interfaces\r
+\r
+STDMETHODIMP\r
+CBaseBasicVideo::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    ValidateReadWritePtr(ppv,sizeof(PVOID));\r
+    if (riid == IID_IBasicVideo || riid == IID_IBasicVideo2) {\r
+       return GetInterface( static_cast<IBasicVideo2 *>(this), ppv);\r
+    } else {\r
+       return CUnknown::NonDelegatingQueryInterface(riid, ppv);\r
+    }\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CBaseBasicVideo::GetTypeInfoCount(__out UINT * pctinfo)\r
+{\r
+    return m_basedisp.GetTypeInfoCount(pctinfo);\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CBaseBasicVideo::GetTypeInfo(\r
+  UINT itinfo,\r
+  LCID lcid,\r
+  __deref_out ITypeInfo ** pptinfo)\r
+{\r
+    return m_basedisp.GetTypeInfo(\r
+               IID_IBasicVideo,\r
+               itinfo,\r
+               lcid,\r
+               pptinfo);\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CBaseBasicVideo::GetIDsOfNames(\r
+  REFIID riid,\r
+  __in_ecount(cNames) LPOLESTR * rgszNames,\r
+  UINT cNames,\r
+  LCID lcid,\r
+  __out_ecount(cNames) DISPID * rgdispid)\r
+{\r
+    return m_basedisp.GetIDsOfNames(\r
+                       IID_IBasicVideo,\r
+                       rgszNames,\r
+                       cNames,\r
+                       lcid,\r
+                       rgdispid);\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CBaseBasicVideo::Invoke(\r
+  DISPID dispidMember,\r
+  REFIID riid,\r
+  LCID lcid,\r
+  WORD wFlags,\r
+  __in DISPPARAMS * pdispparams,\r
+  __out_opt VARIANT * pvarResult,\r
+  __out_opt EXCEPINFO * pexcepinfo,\r
+  __out_opt UINT * puArgErr)\r
+{\r
+    // this parameter is a dead leftover from an earlier interface\r
+    if (IID_NULL != riid) {\r
+       return DISP_E_UNKNOWNINTERFACE;\r
+    }\r
+\r
+    ITypeInfo * pti;\r
+    HRESULT hr = GetTypeInfo(0, lcid, &pti);\r
+\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    hr = pti->Invoke(\r
+           (IBasicVideo *)this,\r
+           dispidMember,\r
+           wFlags,\r
+           pdispparams,\r
+           pvarResult,\r
+           pexcepinfo,\r
+           puArgErr);\r
+\r
+    pti->Release();\r
+    return hr;\r
+}\r
+\r
+\r
+// --- Implementation of Deferred Commands ----------\r
+\r
+\r
+CDispParams::CDispParams(UINT nArgs, __in_ecount(nArgs) VARIANT* pArgs, __inout_opt HRESULT *phr)\r
+{\r
+   cNamedArgs = 0;\r
+   rgdispidNamedArgs = NULL;\r
+   cArgs = nArgs;\r
+\r
+    if (cArgs) {\r
+       rgvarg = new VARIANT[cArgs];\r
+        if (NULL == rgvarg) {\r
+            cArgs = 0;\r
+            if (phr) {\r
+                *phr = E_OUTOFMEMORY;\r
+            }\r
+            return;\r
+        }\r
+\r
+       for (UINT i = 0; i < cArgs; i++) {\r
+\r
+            //  Why aren't we using VariantCopy?\r
+\r
+           VARIANT * pDest = &rgvarg[i];\r
+           VARIANT * pSrc = &pArgs[i];\r
+\r
+           pDest->vt = pSrc->vt;\r
+           switch(pDest->vt) {\r
+\r
+           case VT_I4:\r
+               pDest->lVal = pSrc->lVal;\r
+               break;\r
+\r
+           case VT_UI1:\r
+               pDest->bVal = pSrc->bVal;\r
+               break;\r
+\r
+           case VT_I2:\r
+               pDest->iVal = pSrc->iVal;\r
+               break;\r
+\r
+           case VT_R4:\r
+               pDest->fltVal = pSrc->fltVal;\r
+               break;\r
+\r
+           case VT_R8:\r
+               pDest->dblVal = pSrc->dblVal;\r
+               break;\r
+\r
+           case VT_BOOL:\r
+               pDest->boolVal = pSrc->boolVal;\r
+               break;\r
+\r
+           case VT_ERROR:\r
+               pDest->scode = pSrc->scode;\r
+               break;\r
+\r
+           case VT_CY:\r
+               pDest->cyVal = pSrc->cyVal;\r
+               break;\r
+\r
+           case VT_DATE:\r
+               pDest->date = pSrc->date;\r
+               break;\r
+\r
+           case VT_BSTR:\r
+               if ((PVOID)pSrc->bstrVal == NULL) {\r
+                   pDest->bstrVal = NULL;\r
+               } else {\r
+\r
+                   // a BSTR is a WORD followed by a UNICODE string.\r
+                   // the pointer points just after the WORD\r
+\r
+                   WORD len = * (WORD*) (pSrc->bstrVal - (sizeof(WORD) / sizeof(OLECHAR)));\r
+                   OLECHAR* pch = new OLECHAR[len + (sizeof(WORD)/sizeof(OLECHAR))];\r
+                    if (pch) {\r
+                       WORD *pui = (WORD*)pch;\r
+                       *pui = len;\r
+                       pDest->bstrVal = pch + (sizeof(WORD)/sizeof(OLECHAR));\r
+                       CopyMemory(pDest->bstrVal, pSrc->bstrVal, len*sizeof(OLECHAR));\r
+                    } else {\r
+                        cArgs = i;\r
+                        if (phr) {\r
+                            *phr = E_OUTOFMEMORY;\r
+                        }\r
+                    }\r
+               }\r
+               break;\r
+\r
+           case VT_UNKNOWN:\r
+               pDest->punkVal = pSrc->punkVal;\r
+               pDest->punkVal->AddRef();\r
+               break;\r
+\r
+           case VT_DISPATCH:\r
+               pDest->pdispVal = pSrc->pdispVal;\r
+               pDest->pdispVal->AddRef();\r
+               break;\r
+\r
+           default:\r
+               // a type we haven't got round to adding yet!\r
+               ASSERT(0);\r
+               break;\r
+           }\r
+       }\r
+\r
+    } else {\r
+       rgvarg = NULL;\r
+    }\r
+\r
+}\r
+\r
+\r
+CDispParams::~CDispParams()\r
+{\r
+    for (UINT i = 0; i < cArgs; i++) {\r
+       switch(rgvarg[i].vt) {\r
+        case VT_BSTR:\r
+            //  Explicitly cast BSTR to PVOID to tell code scanning tools we really mean to test the pointer\r
+           if ((PVOID)rgvarg[i].bstrVal != NULL) {\r
+               OLECHAR * pch = rgvarg[i].bstrVal - (sizeof(WORD)/sizeof(OLECHAR));\r
+               delete pch;\r
+           }\r
+           break;\r
+\r
+       case VT_UNKNOWN:\r
+           rgvarg[i].punkVal->Release();\r
+           break;\r
+\r
+       case VT_DISPATCH:\r
+           rgvarg[i].pdispVal->Release();\r
+           break;\r
+       }\r
+    }\r
+    delete[] rgvarg;\r
+}\r
+\r
+\r
+// lifetime is controlled by refcounts (see defer.h)\r
+\r
+CDeferredCommand::CDeferredCommand(\r
+    __inout CCmdQueue * pQ,\r
+    __in_opt LPUNKNOWN pUnk,\r
+    __inout HRESULT *  phr,\r
+    __in LPUNKNOWN     pUnkExecutor,\r
+    REFTIME    time,\r
+    __in GUID* iid,\r
+    long       dispidMethod,\r
+    short      wFlags,\r
+    long       nArgs,\r
+    __in_ecount(nArgs) VARIANT*        pDispParams,\r
+    __out VARIANT*     pvarResult,\r
+    __out short*       puArgErr,\r
+    BOOL       bStream\r
+    ) :\r
+       CUnknown(NAME("DeferredCommand"), pUnk),\r
+       m_pQueue(pQ),\r
+       m_pUnk(pUnkExecutor),\r
+       m_iid(iid),\r
+       m_dispidMethod(dispidMethod),\r
+       m_wFlags(wFlags),\r
+       m_DispParams(nArgs, pDispParams, phr),\r
+       m_pvarResult(pvarResult),\r
+       m_bStream(bStream),\r
+       m_hrResult(E_ABORT)\r
+\r
+{\r
+    // convert REFTIME to REFERENCE_TIME\r
+    COARefTime convertor(time);\r
+    m_time = convertor;\r
+\r
+    // no check of time validity - it's ok to queue a command that's\r
+    // already late\r
+\r
+    // check iid is supportable on pUnk by QueryInterface for it\r
+    IUnknown * pInterface;\r
+    HRESULT hr = m_pUnk->QueryInterface(GetIID(), (void**) &pInterface);\r
+    if (FAILED(hr)) {\r
+       *phr = hr;\r
+       return;\r
+    }\r
+    pInterface->Release();\r
+\r
+\r
+    // !!! check dispidMethod and param/return types using typelib\r
+    ITypeInfo *pti;\r
+    hr = m_Dispatch.GetTypeInfo(*iid, 0, 0, &pti);\r
+    if (FAILED(hr)) {\r
+       *phr = hr;\r
+       return;\r
+    }\r
+    // !!! some sort of ITypeInfo validity check here\r
+    pti->Release();\r
+\r
+\r
+    // Fix up the dispid for put and get\r
+    if (wFlags == DISPATCH_PROPERTYPUT) {\r
+        m_DispParams.cNamedArgs = 1;\r
+        m_DispId = DISPID_PROPERTYPUT;\r
+        m_DispParams.rgdispidNamedArgs = &m_DispId;\r
+    }\r
+\r
+    // all checks ok - add to queue\r
+    hr = pQ->Insert(this);\r
+    if (FAILED(hr)) {\r
+       *phr = hr;\r
+    }\r
+}\r
+\r
+\r
+// refcounts are held by caller of InvokeAt... and by list. So if\r
+// we get here, we can't be on the list\r
+\r
+#if 0\r
+CDeferredCommand::~CDeferredCommand()\r
+{\r
+    // this assert is invalid since if the queue is deleted while we are\r
+    // still on the queue, we will have been removed by the queue and this\r
+    // m_pQueue will not have been modified.\r
+    // ASSERT(m_pQueue == NULL);\r
+\r
+    // we don't hold a ref count on pUnk, which is the object that should\r
+    // execute the command.\r
+    // This is because there would otherwise be a circular refcount problem\r
+    // since pUnk probably owns the CmdQueue object that has a refcount\r
+    // on us.\r
+    // The lifetime of pUnk is guaranteed by it being part of, or lifetime\r
+    // controlled by, our parent object. As long as we are on the list, pUnk\r
+    // must be valid. Once we are off the list, we do not use pUnk.\r
+\r
+}\r
+#endif\r
+\r
+\r
+// overriden to publicise our interfaces\r
+\r
+STDMETHODIMP\r
+CDeferredCommand::NonDelegatingQueryInterface(REFIID riid, __out void **ppv)\r
+{\r
+    ValidateReadWritePtr(ppv,sizeof(PVOID));\r
+    if (riid == IID_IDeferredCommand) {\r
+       return GetInterface( (IDeferredCommand *) this, ppv);\r
+    } else {\r
+       return CUnknown::NonDelegatingQueryInterface(riid, ppv);\r
+    }\r
+}\r
+\r
+\r
+// remove from q. this will reduce the refcount by one (since the q\r
+// holds a count) but can't make us go away since he must have a\r
+// refcount in order to call this method.\r
+\r
+STDMETHODIMP\r
+CDeferredCommand::Cancel()\r
+{\r
+    if (m_pQueue == NULL) {\r
+       return VFW_E_ALREADY_CANCELLED;\r
+    }\r
+\r
+    HRESULT hr = m_pQueue->Remove(this);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    m_pQueue = NULL;\r
+    return S_OK;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CDeferredCommand::Confidence(__out LONG* pConfidence)\r
+{\r
+    return E_NOTIMPL;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CDeferredCommand::GetHResult(__out HRESULT * phrResult)\r
+{\r
+    CheckPointer(phrResult,E_POINTER);\r
+    ValidateReadWritePtr(phrResult,sizeof(HRESULT));\r
+\r
+    if (m_pQueue != NULL) {\r
+       return E_ABORT;\r
+    }\r
+    *phrResult = m_hrResult;\r
+    return S_OK;\r
+}\r
+\r
+\r
+// set the time to be a new time (checking that it is valid) and\r
+// then requeue\r
+\r
+STDMETHODIMP\r
+CDeferredCommand::Postpone(REFTIME newtime)\r
+{\r
+\r
+    // check that this time is not past\r
+    // convert REFTIME to REFERENCE_TIME\r
+    COARefTime convertor(newtime);\r
+\r
+    // check that the time has not passed\r
+    if (m_pQueue->CheckTime(convertor, IsStreamTime())) {\r
+       return VFW_E_TIME_ALREADY_PASSED;\r
+    }\r
+\r
+    // extract from list\r
+    HRESULT hr = m_pQueue->Remove(this);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    // change time\r
+    m_time = convertor;\r
+\r
+    // requeue\r
+    hr = m_pQueue->Insert(this);\r
+\r
+    return hr;\r
+}\r
+\r
+\r
+HRESULT\r
+CDeferredCommand::Invoke()\r
+{\r
+    // check that we are still outstanding\r
+    if (m_pQueue == NULL) {\r
+       return VFW_E_ALREADY_CANCELLED;\r
+    }\r
+\r
+    // get the type info\r
+    ITypeInfo* pti;\r
+    HRESULT hr = m_Dispatch.GetTypeInfo(GetIID(), 0, 0, &pti);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    // qi for the expected interface and then invoke it. Note that we have to\r
+    // treat the returned interface as IUnknown since we don't know its type.\r
+    IUnknown* pInterface;\r
+\r
+    hr = m_pUnk->QueryInterface(GetIID(), (void**) &pInterface);\r
+    if (FAILED(hr)) {\r
+       pti->Release();\r
+       return hr;\r
+    }\r
+\r
+    EXCEPINFO expinfo;\r
+    UINT uArgErr;\r
+    m_hrResult = pti->Invoke(\r
+       pInterface,\r
+       GetMethod(),\r
+       GetFlags(),\r
+       GetParams(),\r
+       GetResult(),\r
+       &expinfo,\r
+       &uArgErr);\r
+\r
+    // release the interface we QI'd for\r
+    pInterface->Release();\r
+    pti->Release();\r
+\r
+\r
+    // remove from list whether or not successful\r
+    // or we loop indefinitely\r
+    hr = m_pQueue->Remove(this);\r
+    m_pQueue = NULL;\r
+    return hr;\r
+}\r
+\r
+\r
+\r
+// --- CCmdQueue methods ----------\r
+\r
+\r
+CCmdQueue::CCmdQueue(__inout_opt HRESULT *phr) :\r
+    m_listPresentation(NAME("Presentation time command list")),\r
+    m_listStream(NAME("Stream time command list")),\r
+    m_evDue(TRUE, phr),    // manual reset\r
+    m_dwAdvise(0),\r
+    m_pClock(NULL),\r
+    m_bRunning(FALSE)\r
+{\r
+}\r
+\r
+\r
+CCmdQueue::~CCmdQueue()\r
+{\r
+    // empty all our lists\r
+\r
+    // we hold a refcount on each, so traverse and Release each\r
+    // entry then RemoveAll to empty the list\r
+    POSITION pos = m_listPresentation.GetHeadPosition();\r
+\r
+    while(pos) {\r
+       CDeferredCommand* pCmd = m_listPresentation.GetNext(pos);\r
+       pCmd->Release();\r
+    }\r
+    m_listPresentation.RemoveAll();\r
+\r
+    pos = m_listStream.GetHeadPosition();\r
+\r
+    while(pos) {\r
+       CDeferredCommand* pCmd = m_listStream.GetNext(pos);\r
+       pCmd->Release();\r
+    }\r
+    m_listStream.RemoveAll();\r
+\r
+    if (m_pClock) {\r
+       if (m_dwAdvise) {\r
+           m_pClock->Unadvise(m_dwAdvise);\r
+           m_dwAdvise = 0;\r
+       }\r
+       m_pClock->Release();\r
+    }\r
+}\r
+\r
+\r
+// returns a new CDeferredCommand object that will be initialised with\r
+// the parameters and will be added to the queue during construction.\r
+// returns S_OK if successfully created otherwise an error and\r
+// no object has been queued.\r
+\r
+HRESULT\r
+CCmdQueue::New(\r
+    __out CDeferredCommand **ppCmd,\r
+    __in     LPUNKNOWN pUnk,           // this object will execute command\r
+    REFTIME    time,\r
+    __in GUID* iid,\r
+    long       dispidMethod,\r
+    short      wFlags,\r
+    long       cArgs,\r
+    __in_ecount(cArgs) VARIANT*        pDispParams,\r
+    __out VARIANT*     pvarResult,\r
+    __out short*       puArgErr,\r
+    BOOL       bStream\r
+)\r
+{\r
+    CAutoLock lock(&m_Lock);\r
+\r
+    HRESULT hr = S_OK;\r
+    *ppCmd = NULL;\r
+\r
+    CDeferredCommand* pCmd;\r
+    pCmd = new CDeferredCommand(\r
+                   this,\r
+                   NULL,           // not aggregated\r
+                   &hr,\r
+                   pUnk,           // this guy will execute\r
+                   time,\r
+                   iid,\r
+                   dispidMethod,\r
+                   wFlags,\r
+                   cArgs,\r
+                   pDispParams,\r
+                   pvarResult,\r
+                   puArgErr,\r
+                   bStream);\r
+\r
+    if (pCmd == NULL) {\r
+       hr = E_OUTOFMEMORY;\r
+    } else {\r
+       *ppCmd = pCmd;\r
+    }\r
+    return hr;\r
+}\r
+\r
+\r
+HRESULT\r
+CCmdQueue::Insert(__in CDeferredCommand* pCmd)\r
+{\r
+    CAutoLock lock(&m_Lock);\r
+\r
+    // addref the item\r
+    pCmd->AddRef();\r
+\r
+    CGenericList<CDeferredCommand> * pList;\r
+    if (pCmd->IsStreamTime()) {\r
+       pList = &m_listStream;\r
+    } else {\r
+       pList = &m_listPresentation;\r
+    }\r
+    POSITION pos = pList->GetHeadPosition();\r
+\r
+    // seek past all items that are before us\r
+    while (pos &&\r
+       (pList->GetValid(pos)->GetTime() <= pCmd->GetTime())) {\r
+\r
+       pList->GetNext(pos);\r
+    }\r
+\r
+    // now at end of list or in front of items that come later\r
+    if (!pos) {\r
+       pList->AddTail(pCmd);\r
+    } else {\r
+       pList->AddBefore(pos, pCmd);\r
+    }\r
+\r
+    SetTimeAdvise();\r
+    return S_OK;\r
+}\r
+\r
+\r
+HRESULT\r
+CCmdQueue::Remove(__in CDeferredCommand* pCmd)\r
+{\r
+    CAutoLock lock(&m_Lock);\r
+    HRESULT hr = S_OK;\r
+\r
+    CGenericList<CDeferredCommand> * pList;\r
+    if (pCmd->IsStreamTime()) {\r
+       pList = &m_listStream;\r
+    } else {\r
+       pList = &m_listPresentation;\r
+    }\r
+    POSITION pos = pList->GetHeadPosition();\r
+\r
+    // traverse the list\r
+    while (pos && (pList->GetValid(pos) != pCmd)) {\r
+       pList->GetNext(pos);\r
+    }\r
+\r
+    // did we drop off the end?\r
+    if (!pos) {\r
+       hr = VFW_E_NOT_FOUND;\r
+    } else {\r
+\r
+       // found it - now take off list\r
+       pList->Remove(pos);\r
+\r
+       // Insert did an AddRef, so release it\r
+       pCmd->Release();\r
+\r
+       // check that timer request is still for earliest time\r
+       SetTimeAdvise();\r
+    }\r
+    return hr;\r
+}\r
+\r
+\r
+// set the clock used for timing\r
+\r
+HRESULT\r
+CCmdQueue::SetSyncSource(__in_opt IReferenceClock* pClock)\r
+{\r
+    CAutoLock lock(&m_Lock);\r
+\r
+    // addref the new clock first in case they are the same\r
+    if (pClock) {\r
+       pClock->AddRef();\r
+    }\r
+\r
+    // kill any advise on the old clock\r
+    if (m_pClock) {\r
+       if (m_dwAdvise) {\r
+           m_pClock->Unadvise(m_dwAdvise);\r
+           m_dwAdvise = 0;\r
+       }\r
+       m_pClock->Release();\r
+    }\r
+    m_pClock = pClock;\r
+\r
+    // set up a new advise\r
+    SetTimeAdvise();\r
+    return S_OK;\r
+}\r
+\r
+\r
+// set up a timer event with the reference clock\r
+\r
+void\r
+CCmdQueue::SetTimeAdvise(void)\r
+{\r
+    // make sure we have a clock to use\r
+    if (!m_pClock) {\r
+       return;\r
+    }\r
+\r
+    // reset the event whenever we are requesting a new signal\r
+    m_evDue.Reset();\r
+\r
+    // time 0 is earliest\r
+    CRefTime current;\r
+\r
+    // find the earliest presentation time\r
+    POSITION pos = m_listPresentation.GetHeadPosition();\r
+    if (pos != NULL) {\r
+       current = m_listPresentation.GetValid(pos)->GetTime();\r
+    }\r
+\r
+    // if we're running, check the stream times too\r
+    if (m_bRunning) {\r
+\r
+       CRefTime t;\r
+        pos = m_listStream.GetHeadPosition();\r
+       if (NULL != pos) {\r
+           t = m_listStream.GetValid(pos)->GetTime();\r
+\r
+           // add on stream time offset to get presentation time\r
+           t += m_StreamTimeOffset;\r
+\r
+           // is this earlier?\r
+           if ((current == TimeZero) || (t < current)) {\r
+               current = t;\r
+           }\r
+       }\r
+    }\r
+\r
+    // need to change?\r
+    if ((current > TimeZero) && (current != m_tCurrentAdvise)) {\r
+       if (m_dwAdvise) {\r
+           m_pClock->Unadvise(m_dwAdvise);\r
+           // reset the event whenever we are requesting a new signal\r
+           m_evDue.Reset();\r
+       }\r
+\r
+       // ask for time advice - the first two params are either\r
+       // stream time offset and stream time or\r
+       // presentation time and 0. we always use the latter\r
+       HRESULT hr = m_pClock->AdviseTime(\r
+                   (REFERENCE_TIME)current,\r
+                   TimeZero,\r
+                   (HEVENT) HANDLE(m_evDue),\r
+                   &m_dwAdvise);\r
+\r
+       ASSERT(SUCCEEDED(hr));\r
+       m_tCurrentAdvise = current;\r
+    }\r
+}\r
+\r
+\r
+// switch to run mode. Streamtime to Presentation time mapping known.\r
+\r
+HRESULT\r
+CCmdQueue::Run(REFERENCE_TIME tStreamTimeOffset)\r
+{\r
+    CAutoLock lock(&m_Lock);\r
+\r
+    m_StreamTimeOffset = tStreamTimeOffset;\r
+    m_bRunning = TRUE;\r
+\r
+    // ensure advise is accurate\r
+    SetTimeAdvise();\r
+    return S_OK;\r
+}\r
+\r
+\r
+// switch to Stopped or Paused mode. Time mapping not known.\r
+\r
+HRESULT\r
+CCmdQueue::EndRun()\r
+{\r
+    CAutoLock lock(&m_Lock);\r
+\r
+    m_bRunning = FALSE;\r
+\r
+    // check timer setting - stream times\r
+    SetTimeAdvise();\r
+    return S_OK;\r
+}\r
+\r
+\r
+// return a pointer to the next due command. Blocks for msTimeout\r
+// milliseconds until there is a due command.\r
+// Stream-time commands will only become due between Run and Endrun calls.\r
+// The command remains queued until invoked or cancelled.\r
+// Returns E_ABORT if timeout occurs, otherwise S_OK (or other error).\r
+//\r
+// returns an AddRef'd object\r
+\r
+HRESULT\r
+CCmdQueue::GetDueCommand(__out CDeferredCommand ** ppCmd, long msTimeout)\r
+{\r
+    // loop until we timeout or find a due command\r
+    for (;;) {\r
+\r
+       {\r
+           CAutoLock lock(&m_Lock);\r
+\r
+\r
+           // find the earliest command\r
+           CDeferredCommand * pCmd = NULL;\r
+\r
+           // check the presentation time and the\r
+           // stream time list to find the earliest\r
+\r
+            POSITION pos = m_listPresentation.GetHeadPosition();\r
+\r
+           if (NULL != pos) {\r
+               pCmd = m_listPresentation.GetValid(pos);\r
+           }\r
+\r
+           if (m_bRunning) {\r
+               pos = m_listStream.GetHeadPosition();\r
+                if (NULL != pos) {\r
+                    CDeferredCommand* pStrm = m_listStream.GetValid(pos);\r
+\r
+                    CRefTime t = pStrm->GetTime() + m_StreamTimeOffset;\r
+                    if (!pCmd || (t < pCmd->GetTime())) {\r
+                        pCmd = pStrm;\r
+                    }\r
+                }\r
+            }\r
+\r
+           //  if we have found one, is it due?\r
+           if (pCmd) {\r
+               if (CheckTime(pCmd->GetTime(), pCmd->IsStreamTime())) {\r
+\r
+                   // yes it's due - addref it\r
+                   pCmd->AddRef();\r
+                   *ppCmd = pCmd;\r
+                   return S_OK;\r
+               }\r
+           }\r
+       }\r
+\r
+       // block until the advise is signalled\r
+       if (WaitForSingleObject(m_evDue, msTimeout) != WAIT_OBJECT_0) {\r
+           return E_ABORT;\r
+       }\r
+    }\r
+}\r
+\r
+\r
+// return a pointer to a command that will be due for a given time.\r
+// Pass in a stream time here. The stream time offset will be passed\r
+// in via the Run method.\r
+// Commands remain queued until invoked or cancelled.\r
+// This method will not block. It will report E_ABORT if there are no\r
+// commands due yet.\r
+//\r
+// returns an AddRef'd object\r
+\r
+HRESULT\r
+CCmdQueue::GetCommandDueFor(REFERENCE_TIME rtStream, __out CDeferredCommand**ppCmd)\r
+{\r
+    CAutoLock lock(&m_Lock);\r
+\r
+    CRefTime tStream(rtStream);\r
+\r
+    // find the earliest stream and presentation time commands\r
+    CDeferredCommand* pStream = NULL;\r
+    POSITION pos = m_listStream.GetHeadPosition();\r
+    if (NULL != pos) {\r
+       pStream = m_listStream.GetValid(pos);\r
+    }\r
+    CDeferredCommand* pPresent = NULL;\r
+    pos = m_listPresentation.GetHeadPosition();\r
+    if (NULL != pos) {\r
+       pPresent = m_listPresentation.GetValid(pos);\r
+    }\r
+\r
+    // is there a presentation time that has passed already\r
+    if (pPresent && CheckTime(pPresent->GetTime(), FALSE)) {\r
+       pPresent->AddRef();\r
+       *ppCmd = pPresent;\r
+       return S_OK;\r
+    }\r
+\r
+    // is there a stream time command due before this stream time\r
+    if (pStream && (pStream->GetTime() <= tStream)) {\r
+       pStream->AddRef();\r
+       *ppCmd = pStream;\r
+       return S_OK;\r
+    }\r
+\r
+    // if we are running, we can map presentation times to\r
+    // stream time. In this case, is there a presentation time command\r
+    // that will be due before this stream time is presented?\r
+    if (m_bRunning && pPresent) {\r
+\r
+       // this stream time will appear at...\r
+       tStream += m_StreamTimeOffset;\r
+\r
+       // due before that?\r
+       if (pPresent->GetTime() <= tStream) {\r
+           *ppCmd = pPresent;\r
+           return S_OK;\r
+       }\r
+    }\r
+\r
+    // no commands due yet\r
+    return VFW_E_NOT_FOUND;\r
+}\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/ctlutil.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/ctlutil.h
new file mode 100644 (file)
index 0000000..7e4719c
--- /dev/null
@@ -0,0 +1,923 @@
+//------------------------------------------------------------------------------\r
+// File: CtlUtil.h\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+// Base classes implementing IDispatch parsing for the basic control dual\r
+// interfaces. Derive from these and implement just the custom method and\r
+// property methods. We also implement CPosPassThru that can be used by\r
+// renderers and transforms to pass by IMediaPosition and IMediaSeeking\r
+\r
+#ifndef __CTLUTIL__\r
+#define __CTLUTIL__\r
+\r
+// OLE Automation has different ideas of TRUE and FALSE\r
+\r
+#define OATRUE (-1)\r
+#define OAFALSE (0)\r
+\r
+\r
+// It's possible that we could replace this class with CreateStdDispatch\r
+\r
+class CBaseDispatch\r
+{\r
+    ITypeInfo * m_pti;\r
+\r
+public:\r
+\r
+    CBaseDispatch() : m_pti(NULL) {}\r
+    ~CBaseDispatch();\r
+\r
+    /* IDispatch methods */\r
+    STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);\r
+\r
+    STDMETHODIMP GetTypeInfo(\r
+      REFIID riid,\r
+      UINT itinfo,\r
+      LCID lcid,\r
+      __deref_out ITypeInfo ** pptinfo);\r
+\r
+    STDMETHODIMP GetIDsOfNames(\r
+      REFIID riid,\r
+      __in_ecount(cNames) LPOLESTR * rgszNames,\r
+      UINT cNames,\r
+      LCID lcid,\r
+      __out_ecount(cNames) DISPID * rgdispid);\r
+};\r
+\r
+\r
+class AM_NOVTABLE CMediaControl :\r
+    public IMediaControl,\r
+    public CUnknown\r
+{\r
+    CBaseDispatch m_basedisp;\r
+\r
+public:\r
+\r
+    CMediaControl(const TCHAR *, LPUNKNOWN);\r
+\r
+    DECLARE_IUNKNOWN\r
+\r
+    // override this to publicise our interfaces\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);\r
+\r
+    /* IDispatch methods */\r
+    STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);\r
+\r
+    STDMETHODIMP GetTypeInfo(\r
+      UINT itinfo,\r
+      LCID lcid,\r
+      __deref_out ITypeInfo ** pptinfo);\r
+\r
+    STDMETHODIMP GetIDsOfNames(\r
+      REFIID riid,\r
+      __in_ecount(cNames) LPOLESTR * rgszNames,\r
+      UINT cNames,\r
+      LCID lcid,\r
+      __out_ecount(cNames) DISPID * rgdispid);\r
+\r
+    STDMETHODIMP Invoke(\r
+      DISPID dispidMember,\r
+      REFIID riid,\r
+      LCID lcid,\r
+      WORD wFlags,\r
+      __in DISPPARAMS * pdispparams,\r
+      __out_opt VARIANT * pvarResult,\r
+      __out_opt EXCEPINFO * pexcepinfo,\r
+      __out_opt UINT * puArgErr);\r
+};\r
+\r
+\r
+class AM_NOVTABLE CMediaEvent :\r
+    public IMediaEventEx,\r
+    public CUnknown\r
+{\r
+    CBaseDispatch m_basedisp;\r
+\r
+public:\r
+\r
+    CMediaEvent(__in_opt LPCTSTR, __in_opt LPUNKNOWN);\r
+\r
+    DECLARE_IUNKNOWN\r
+\r
+    // override this to publicise our interfaces\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);\r
+\r
+    /* IDispatch methods */\r
+    STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);\r
+\r
+    STDMETHODIMP GetTypeInfo(\r
+      UINT itinfo,\r
+      LCID lcid,\r
+      __deref_out ITypeInfo ** pptinfo);\r
+\r
+    STDMETHODIMP GetIDsOfNames(\r
+      REFIID riid,\r
+      __in_ecount(cNames) LPOLESTR * rgszNames,\r
+      UINT cNames,\r
+      LCID lcid,\r
+      __out_ecount(cNames) DISPID * rgdispid);\r
+\r
+    STDMETHODIMP Invoke(\r
+      DISPID dispidMember,\r
+      REFIID riid,\r
+      LCID lcid,\r
+      WORD wFlags,\r
+      __in DISPPARAMS * pdispparams,\r
+      __out_opt VARIANT * pvarResult,\r
+      __out_opt EXCEPINFO * pexcepinfo,\r
+      __out_opt UINT * puArgErr);\r
+};\r
+\r
+\r
+class AM_NOVTABLE CMediaPosition :\r
+    public IMediaPosition,\r
+    public CUnknown\r
+{\r
+    CBaseDispatch m_basedisp;\r
+\r
+\r
+public:\r
+\r
+    CMediaPosition(__in_opt LPCTSTR, __in_opt LPUNKNOWN);\r
+    CMediaPosition(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT *phr);\r
+\r
+    DECLARE_IUNKNOWN\r
+\r
+    // override this to publicise our interfaces\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);\r
+\r
+    /* IDispatch methods */\r
+    STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);\r
+\r
+    STDMETHODIMP GetTypeInfo(\r
+      UINT itinfo,\r
+      LCID lcid,\r
+      __deref_out ITypeInfo ** pptinfo);\r
+\r
+    STDMETHODIMP GetIDsOfNames(\r
+      REFIID riid,\r
+      __in_ecount(cNames) LPOLESTR * rgszNames,\r
+      UINT cNames,\r
+      LCID lcid,\r
+      __out_ecount(cNames) DISPID * rgdispid);\r
+\r
+    STDMETHODIMP Invoke(\r
+      DISPID dispidMember,\r
+      REFIID riid,\r
+      LCID lcid,\r
+      WORD wFlags,\r
+      __in DISPPARAMS * pdispparams,\r
+      __out_opt VARIANT * pvarResult,\r
+      __out_opt EXCEPINFO * pexcepinfo,\r
+      __out_opt UINT * puArgErr);\r
+\r
+};\r
+\r
+\r
+// OA-compatibility means that we must use double as the RefTime value,\r
+// and REFERENCE_TIME (essentially a LONGLONG) within filters.\r
+// this class converts between the two\r
+\r
+class COARefTime : public CRefTime {\r
+public:\r
+\r
+    COARefTime() {\r
+    };\r
+\r
+    COARefTime(CRefTime t)\r
+        : CRefTime(t)\r
+    {\r
+    };\r
+\r
+    COARefTime(REFERENCE_TIME t)\r
+        : CRefTime(t)\r
+    {\r
+    };\r
+\r
+    COARefTime(double d) {\r
+        m_time = (LONGLONG) (d * 10000000);\r
+    };\r
+\r
+    operator double() {\r
+        return double(m_time) / 10000000;\r
+    };\r
+\r
+    operator REFERENCE_TIME() {\r
+        return m_time;\r
+    };\r
+\r
+    COARefTime& operator=(const double& rd)  {\r
+        m_time = (LONGLONG) (rd * 10000000);\r
+        return *this;\r
+    }\r
+\r
+    COARefTime& operator=(const REFERENCE_TIME& rt)  {\r
+        m_time = rt;\r
+        return *this;\r
+    }\r
+\r
+    inline BOOL operator==(const COARefTime& rt)\r
+    {\r
+        return m_time == rt.m_time;\r
+    };\r
+\r
+    inline BOOL operator!=(const COARefTime& rt)\r
+    {\r
+        return m_time != rt.m_time;\r
+    };\r
+\r
+    inline BOOL operator < (const COARefTime& rt)\r
+    {\r
+        return m_time < rt.m_time;\r
+    };\r
+\r
+    inline BOOL operator > (const COARefTime& rt)\r
+    {\r
+        return m_time > rt.m_time;\r
+    };\r
+\r
+    inline BOOL operator >= (const COARefTime& rt)\r
+    {\r
+        return m_time >= rt.m_time;\r
+    };\r
+\r
+    inline BOOL operator <= (const COARefTime& rt)\r
+    {\r
+        return m_time <= rt.m_time;\r
+    };\r
+\r
+    inline COARefTime operator+(const COARefTime& rt)\r
+    {\r
+        return COARefTime(m_time + rt.m_time);\r
+    };\r
+\r
+    inline COARefTime operator-(const COARefTime& rt)\r
+    {\r
+        return COARefTime(m_time - rt.m_time);\r
+    };\r
+\r
+    inline COARefTime operator*(LONG l)\r
+    {\r
+        return COARefTime(m_time * l);\r
+    };\r
+\r
+    inline COARefTime operator/(LONG l)\r
+    {\r
+        return COARefTime(m_time / l);\r
+    };\r
+\r
+private:\r
+    //  Prevent bugs from constructing from LONG (which gets\r
+    //  converted to double and then multiplied by 10000000\r
+    COARefTime(LONG);\r
+    LONG operator=(LONG);\r
+};\r
+\r
+\r
+// A utility class that handles IMediaPosition and IMediaSeeking on behalf\r
+// of single-input pin renderers, or transform filters.\r
+//\r
+// Renderers will expose this from the filter; transform filters will\r
+// expose it from the output pin and not the renderer.\r
+//\r
+// Create one of these, giving it your IPin* for your input pin, and delegate\r
+// all IMediaPosition methods to it. It will query the input pin for\r
+// IMediaPosition and respond appropriately.\r
+//\r
+// Call ForceRefresh if the pin connection changes.\r
+//\r
+// This class no longer caches the upstream IMediaPosition or IMediaSeeking\r
+// it acquires it on each method call. This means ForceRefresh is not needed.\r
+// The method is kept for source compatibility and to minimise the changes\r
+// if we need to put it back later for performance reasons.\r
+\r
+class CPosPassThru : public IMediaSeeking, public CMediaPosition\r
+{\r
+    IPin *m_pPin;\r
+\r
+    HRESULT GetPeer(__deref_out IMediaPosition **ppMP);\r
+    HRESULT GetPeerSeeking(__deref_out IMediaSeeking **ppMS);\r
+\r
+public:\r
+\r
+    CPosPassThru(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, IPin *);\r
+    DECLARE_IUNKNOWN\r
+\r
+    HRESULT ForceRefresh() {\r
+        return S_OK;\r
+    };\r
+\r
+    // override to return an accurate current position\r
+    virtual HRESULT GetMediaTime(__out LONGLONG *pStartTime, __out_opt LONGLONG *pEndTime) {\r
+        return E_FAIL;\r
+    }\r
+\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,__deref_out void **ppv);\r
+\r
+    // IMediaSeeking methods\r
+    STDMETHODIMP GetCapabilities( __out DWORD * pCapabilities );\r
+    STDMETHODIMP CheckCapabilities( __inout DWORD * pCapabilities );\r
+    STDMETHODIMP SetTimeFormat(const GUID * pFormat);\r
+    STDMETHODIMP GetTimeFormat(__out GUID *pFormat);\r
+    STDMETHODIMP IsUsingTimeFormat(const GUID * pFormat);\r
+    STDMETHODIMP IsFormatSupported( const GUID * pFormat);\r
+    STDMETHODIMP QueryPreferredFormat( __out GUID *pFormat);\r
+    STDMETHODIMP ConvertTimeFormat(__out LONGLONG * pTarget, \r
+                                   __in_opt const GUID * pTargetFormat,\r
+                                   LONGLONG Source, \r
+                                   __in_opt const GUID * pSourceFormat );\r
+    STDMETHODIMP SetPositions( __inout_opt LONGLONG * pCurrent, DWORD CurrentFlags\r
+                             , __inout_opt LONGLONG * pStop, DWORD StopFlags );\r
+\r
+    STDMETHODIMP GetPositions( __out_opt LONGLONG * pCurrent, __out_opt LONGLONG * pStop );\r
+    STDMETHODIMP GetCurrentPosition( __out LONGLONG * pCurrent );\r
+    STDMETHODIMP GetStopPosition( __out LONGLONG * pStop );\r
+    STDMETHODIMP SetRate( double dRate);\r
+    STDMETHODIMP GetRate( __out double * pdRate);\r
+    STDMETHODIMP GetDuration( __out LONGLONG *pDuration);\r
+    STDMETHODIMP GetAvailable( __out_opt LONGLONG *pEarliest, __out_opt LONGLONG *pLatest );\r
+    STDMETHODIMP GetPreroll( __out LONGLONG *pllPreroll );\r
+\r
+    // IMediaPosition properties\r
+    STDMETHODIMP get_Duration(__out REFTIME * plength);\r
+    STDMETHODIMP put_CurrentPosition(REFTIME llTime);\r
+    STDMETHODIMP get_StopTime(__out REFTIME * pllTime);\r
+    STDMETHODIMP put_StopTime(REFTIME llTime);\r
+    STDMETHODIMP get_PrerollTime(__out REFTIME * pllTime);\r
+    STDMETHODIMP put_PrerollTime(REFTIME llTime);\r
+    STDMETHODIMP get_Rate(__out double * pdRate);\r
+    STDMETHODIMP put_Rate(double dRate);\r
+    STDMETHODIMP get_CurrentPosition(__out REFTIME * pllTime);\r
+    STDMETHODIMP CanSeekForward(__out LONG *pCanSeekForward);\r
+    STDMETHODIMP CanSeekBackward(__out LONG *pCanSeekBackward);\r
+\r
+private:\r
+    HRESULT GetSeekingLongLong( HRESULT (__stdcall IMediaSeeking::*pMethod)( LONGLONG * ),\r
+                                __out LONGLONG * pll );\r
+};\r
+\r
+\r
+// Adds the ability to return a current position\r
+\r
+class CRendererPosPassThru : public CPosPassThru\r
+{\r
+    CCritSec m_PositionLock;    // Locks access to our position\r
+    LONGLONG m_StartMedia;      // Start media time last seen\r
+    LONGLONG m_EndMedia;        // And likewise the end media\r
+    BOOL m_bReset;              // Have media times been set\r
+\r
+public:\r
+\r
+    // Used to help with passing media times through graph\r
+\r
+    CRendererPosPassThru(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, IPin *);\r
+    HRESULT RegisterMediaTime(IMediaSample *pMediaSample);\r
+    HRESULT RegisterMediaTime(LONGLONG StartTime,LONGLONG EndTime);\r
+    HRESULT GetMediaTime(__out LONGLONG *pStartTime,__out_opt LONGLONG *pEndTime);\r
+    HRESULT ResetMediaTime();\r
+    HRESULT EOS();\r
+};\r
+\r
+STDAPI CreatePosPassThru(\r
+    __in_opt LPUNKNOWN pAgg,\r
+    BOOL bRenderer,\r
+    IPin *pPin,\r
+    __deref_out IUnknown **ppPassThru\r
+);\r
+\r
+// A class that handles the IDispatch part of IBasicAudio and leaves the\r
+// properties and methods themselves pure virtual.\r
+\r
+class AM_NOVTABLE CBasicAudio : public IBasicAudio, public CUnknown\r
+{\r
+    CBaseDispatch m_basedisp;\r
+\r
+public:\r
+\r
+    CBasicAudio(__in_opt LPCTSTR, __in_opt LPUNKNOWN);\r
+\r
+    DECLARE_IUNKNOWN\r
+\r
+    // override this to publicise our interfaces\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);\r
+\r
+    /* IDispatch methods */\r
+    STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);\r
+\r
+    STDMETHODIMP GetTypeInfo(\r
+      UINT itinfo,\r
+      LCID lcid,\r
+      __deref_out ITypeInfo ** pptinfo);\r
+\r
+    STDMETHODIMP GetIDsOfNames(\r
+      REFIID riid,\r
+      __in_ecount(cNames) LPOLESTR * rgszNames,\r
+      UINT cNames,\r
+      LCID lcid,\r
+      __out_ecount(cNames) DISPID * rgdispid);\r
+\r
+    STDMETHODIMP Invoke(\r
+      DISPID dispidMember,\r
+      REFIID riid,\r
+      LCID lcid,\r
+      WORD wFlags,\r
+      __in DISPPARAMS * pdispparams,\r
+      __out_opt VARIANT * pvarResult,\r
+      __out_opt EXCEPINFO * pexcepinfo,\r
+      __out_opt UINT * puArgErr);\r
+};\r
+\r
+\r
+// A class that handles the IDispatch part of IBasicVideo and leaves the\r
+// properties and methods themselves pure virtual.\r
+\r
+class AM_NOVTABLE CBaseBasicVideo : public IBasicVideo2, public CUnknown\r
+{\r
+    CBaseDispatch m_basedisp;\r
+\r
+public:\r
+\r
+    CBaseBasicVideo(__in_opt LPCTSTR, __in_opt LPUNKNOWN);\r
+\r
+    DECLARE_IUNKNOWN\r
+\r
+    // override this to publicise our interfaces\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);\r
+\r
+    /* IDispatch methods */\r
+    STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);\r
+\r
+    STDMETHODIMP GetTypeInfo(\r
+      UINT itinfo,\r
+      LCID lcid,\r
+      __deref_out ITypeInfo ** pptinfo);\r
+\r
+    STDMETHODIMP GetIDsOfNames(\r
+      REFIID riid,\r
+      __in_ecount(cNames) LPOLESTR * rgszNames,\r
+      UINT cNames,\r
+      LCID lcid,\r
+      __out_ecount(cNames) DISPID * rgdispid);\r
+\r
+    STDMETHODIMP Invoke(\r
+      DISPID dispidMember,\r
+      REFIID riid,\r
+      LCID lcid,\r
+      WORD wFlags,\r
+      __in DISPPARAMS * pdispparams,\r
+      __out_opt VARIANT * pvarResult,\r
+      __out_opt EXCEPINFO * pexcepinfo,\r
+      __out_opt UINT * puArgErr);\r
+\r
+    STDMETHODIMP GetPreferredAspectRatio(\r
+      __out long *plAspectX,\r
+      __out long *plAspectY)\r
+    {\r
+        return E_NOTIMPL;\r
+    }\r
+};\r
+\r
+\r
+// A class that handles the IDispatch part of IVideoWindow and leaves the\r
+// properties and methods themselves pure virtual.\r
+\r
+class AM_NOVTABLE CBaseVideoWindow : public IVideoWindow, public CUnknown\r
+{\r
+    CBaseDispatch m_basedisp;\r
+\r
+public:\r
+\r
+    CBaseVideoWindow(__in_opt LPCTSTR, __in_opt LPUNKNOWN);\r
+\r
+    DECLARE_IUNKNOWN\r
+\r
+    // override this to publicise our interfaces\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);\r
+\r
+    /* IDispatch methods */\r
+    STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);\r
+\r
+    STDMETHODIMP GetTypeInfo(\r
+      UINT itinfo,\r
+      LCID lcid,\r
+      __deref_out ITypeInfo ** pptinfo);\r
+\r
+    STDMETHODIMP GetIDsOfNames(\r
+      REFIID riid,\r
+      __in_ecount(cNames) LPOLESTR * rgszNames,\r
+      UINT cNames,\r
+      LCID lcid,\r
+      __out_ecount(cNames) DISPID * rgdispid);\r
+\r
+    STDMETHODIMP Invoke(\r
+      DISPID dispidMember,\r
+      REFIID riid,\r
+      LCID lcid,\r
+      WORD wFlags,\r
+      __in DISPPARAMS * pdispparams,\r
+      __out_opt VARIANT * pvarResult,\r
+      __out_opt EXCEPINFO * pexcepinfo,\r
+      __out_opt UINT * puArgErr);\r
+};\r
+\r
+\r
+// abstract class to help source filters with their implementation\r
+// of IMediaPosition. Derive from this and set the duration (and stop\r
+// position). Also override NotifyChange to do something when the properties\r
+// change.\r
+\r
+class AM_NOVTABLE CSourcePosition : public CMediaPosition\r
+{\r
+\r
+public:\r
+    CSourcePosition(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, __in CCritSec *);\r
+\r
+    // IMediaPosition methods\r
+    STDMETHODIMP get_Duration(__out REFTIME * plength);\r
+    STDMETHODIMP put_CurrentPosition(REFTIME llTime);\r
+    STDMETHODIMP get_StopTime(__out REFTIME * pllTime);\r
+    STDMETHODIMP put_StopTime(REFTIME llTime);\r
+    STDMETHODIMP get_PrerollTime(__out REFTIME * pllTime);\r
+    STDMETHODIMP put_PrerollTime(REFTIME llTime);\r
+    STDMETHODIMP get_Rate(__out double * pdRate);\r
+    STDMETHODIMP put_Rate(double dRate);\r
+    STDMETHODIMP CanSeekForward(__out LONG *pCanSeekForward);\r
+    STDMETHODIMP CanSeekBackward(__out LONG *pCanSeekBackward);\r
+\r
+    // override if you can return the data you are actually working on\r
+    STDMETHODIMP get_CurrentPosition(__out REFTIME * pllTime) {\r
+        return E_NOTIMPL;\r
+    };\r
+\r
+protected:\r
+\r
+    // we call this to notify changes. Override to handle them\r
+    virtual HRESULT ChangeStart() PURE;\r
+    virtual HRESULT ChangeStop() PURE;\r
+    virtual HRESULT ChangeRate() PURE;\r
+\r
+    COARefTime m_Duration;\r
+    COARefTime m_Start;\r
+    COARefTime m_Stop;\r
+    double m_Rate;\r
+\r
+    CCritSec * m_pLock;\r
+};\r
+\r
+class AM_NOVTABLE CSourceSeeking :\r
+    public IMediaSeeking,\r
+    public CUnknown\r
+{\r
+\r
+public:\r
+\r
+    DECLARE_IUNKNOWN;\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);\r
+\r
+    // IMediaSeeking methods\r
+\r
+    STDMETHODIMP IsFormatSupported(const GUID * pFormat);\r
+    STDMETHODIMP QueryPreferredFormat(__out GUID *pFormat);\r
+    STDMETHODIMP SetTimeFormat(const GUID * pFormat);\r
+    STDMETHODIMP IsUsingTimeFormat(const GUID * pFormat);\r
+    STDMETHODIMP GetTimeFormat(__out GUID *pFormat);\r
+    STDMETHODIMP GetDuration(__out LONGLONG *pDuration);\r
+    STDMETHODIMP GetStopPosition(__out LONGLONG *pStop);\r
+    STDMETHODIMP GetCurrentPosition(__out LONGLONG *pCurrent);\r
+    STDMETHODIMP GetCapabilities( __out DWORD * pCapabilities );\r
+    STDMETHODIMP CheckCapabilities( __inout DWORD * pCapabilities );\r
+    STDMETHODIMP ConvertTimeFormat( __out LONGLONG * pTarget, \r
+                                    __in_opt const GUID * pTargetFormat,\r
+                                    LONGLONG Source, \r
+                                    __in_opt const GUID * pSourceFormat );\r
+\r
+    STDMETHODIMP SetPositions( __inout_opt LONGLONG * pCurrent,  DWORD CurrentFlags\r
+                            , __inout_opt LONGLONG * pStop,  DWORD StopFlags );\r
+\r
+    STDMETHODIMP GetPositions( __out_opt LONGLONG * pCurrent, __out_opt LONGLONG * pStop );\r
+\r
+    STDMETHODIMP GetAvailable( __out_opt LONGLONG * pEarliest, __out_opt LONGLONG * pLatest );\r
+    STDMETHODIMP SetRate( double dRate);\r
+    STDMETHODIMP GetRate( __out double * pdRate);\r
+    STDMETHODIMP GetPreroll(__out LONGLONG *pPreroll);\r
+\r
+\r
+protected:\r
+\r
+    // ctor\r
+    CSourceSeeking(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, __in CCritSec *);\r
+\r
+    // we call this to notify changes. Override to handle them\r
+    virtual HRESULT ChangeStart() PURE;\r
+    virtual HRESULT ChangeStop() PURE;\r
+    virtual HRESULT ChangeRate() PURE;\r
+\r
+    CRefTime m_rtDuration;      // length of stream\r
+    CRefTime m_rtStart;         // source will start here\r
+    CRefTime m_rtStop;          // source will stop here\r
+    double m_dRateSeeking;\r
+\r
+    // seeking capabilities\r
+    DWORD m_dwSeekingCaps;\r
+\r
+    CCritSec * m_pLock;\r
+};\r
+\r
+\r
+// Base classes supporting Deferred commands.\r
+\r
+// Deferred commands are queued by calls to methods on the IQueueCommand\r
+// interface, exposed by the filtergraph and by some filters. A successful\r
+// call to one of these methods will return an IDeferredCommand interface\r
+// representing the queued command.\r
+//\r
+// A CDeferredCommand object represents a single deferred command, and exposes\r
+// the IDeferredCommand interface as well as other methods permitting time\r
+// checks and actual execution. It contains a reference to the CCommandQueue\r
+// object on which it is queued.\r
+//\r
+// CCommandQueue is a base class providing a queue of CDeferredCommand\r
+// objects, and methods to add, remove, check status and invoke the queued\r
+// commands. A CCommandQueue object would be part of an object that\r
+// implemented IQueueCommand.\r
+\r
+class CCmdQueue;\r
+\r
+// take a copy of the params and store them. Release any allocated\r
+// memory in destructor\r
+\r
+class CDispParams : public DISPPARAMS\r
+{\r
+public:\r
+    CDispParams(UINT nArgs, __in_ecount(nArgs) VARIANT* pArgs, __inout_opt HRESULT *phr = NULL);\r
+    ~CDispParams();\r
+};\r
+\r
+\r
+// CDeferredCommand lifetime is controlled by refcounts. Caller of\r
+// InvokeAt.. gets a refcounted interface pointer, and the CCmdQueue\r
+// object also holds a refcount on us. Calling Cancel or Invoke takes\r
+// us off the CCmdQueue and thus reduces the refcount by 1. Once taken\r
+// off the queue we cannot be put back on the queue.\r
+\r
+class CDeferredCommand\r
+    : public CUnknown,\r
+      public IDeferredCommand\r
+{\r
+public:\r
+\r
+    CDeferredCommand(\r
+        __inout CCmdQueue * pQ,\r
+        __in_opt LPUNKNOWN   pUnk,               // aggregation outer unk\r
+        __inout HRESULT *   phr,\r
+        __in LPUNKNOWN   pUnkExecutor,       // object that will execute this cmd\r
+        REFTIME     time,\r
+        __in GUID*       iid,\r
+        long        dispidMethod,\r
+        short       wFlags,\r
+        long        cArgs,\r
+        __in_ecount(cArgs) VARIANT*    pDispParams,\r
+        __out VARIANT*    pvarResult,\r
+        __out short*      puArgErr,\r
+        BOOL        bStream\r
+        );\r
+\r
+    DECLARE_IUNKNOWN\r
+\r
+    // override this to publicise our interfaces\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __out void **ppv);\r
+\r
+    // IDeferredCommand methods\r
+    STDMETHODIMP Cancel();\r
+    STDMETHODIMP Confidence(\r
+                    __out LONG* pConfidence);\r
+    STDMETHODIMP Postpone(\r
+                    REFTIME newtime);\r
+    STDMETHODIMP GetHResult(\r
+                    __out HRESULT* phrResult);\r
+\r
+    // other public methods\r
+\r
+    HRESULT Invoke();\r
+\r
+    // access methods\r
+\r
+    // returns TRUE if streamtime, FALSE if presentation time\r
+    BOOL IsStreamTime() {\r
+       return m_bStream;\r
+    };\r
+\r
+    CRefTime GetTime() {\r
+        return m_time;\r
+    };\r
+\r
+    REFIID GetIID() {\r
+        return *m_iid;\r
+    };\r
+\r
+    long GetMethod() {\r
+        return m_dispidMethod;\r
+    };\r
+\r
+    short GetFlags() {\r
+        return m_wFlags;\r
+    };\r
+\r
+    DISPPARAMS* GetParams() {\r
+        return &m_DispParams;\r
+    };\r
+\r
+    VARIANT* GetResult() {\r
+        return m_pvarResult;\r
+    };\r
+\r
+protected:\r
+\r
+    CCmdQueue* m_pQueue;\r
+\r
+    // pUnk for the interface that we will execute the command on\r
+    LPUNKNOWN   m_pUnk;\r
+\r
+    // stored command data\r
+    REFERENCE_TIME     m_time;\r
+    GUID*       m_iid;\r
+    long        m_dispidMethod;\r
+    short       m_wFlags;\r
+    VARIANT*    m_pvarResult;\r
+    BOOL        m_bStream;\r
+    CDispParams m_DispParams;\r
+    DISPID      m_DispId;         //  For get and put\r
+\r
+    // we use this for ITypeInfo access\r
+    CBaseDispatch   m_Dispatch;\r
+\r
+    // save retval here\r
+    HRESULT     m_hrResult;\r
+};\r
+\r
+\r
+// a list of CDeferredCommand objects. this is a base class providing\r
+// the basics of access to the list. If you want to use CDeferredCommand\r
+// objects then your queue needs to be derived from this class.\r
+\r
+class AM_NOVTABLE CCmdQueue\r
+{\r
+public:\r
+    CCmdQueue(__inout_opt HRESULT *phr = NULL);\r
+    virtual ~CCmdQueue();\r
+\r
+    // returns a new CDeferredCommand object that will be initialised with\r
+    // the parameters and will be added to the queue during construction.\r
+    // returns S_OK if successfully created otherwise an error and\r
+    // no object has been queued.\r
+    virtual HRESULT  New(\r
+        __out CDeferredCommand **ppCmd,\r
+        __in LPUNKNOWN   pUnk,\r
+        REFTIME     time,\r
+        __in GUID*       iid,\r
+        long        dispidMethod,\r
+        short       wFlags,\r
+        long        cArgs,\r
+        __in_ecount(cArgs) VARIANT*    pDispParams,\r
+        __out VARIANT*    pvarResult,\r
+        __out short*      puArgErr,\r
+        BOOL        bStream\r
+    );\r
+\r
+    // called by the CDeferredCommand object to add and remove itself\r
+    // from the queue\r
+    virtual HRESULT Insert(__in CDeferredCommand* pCmd);\r
+    virtual HRESULT Remove(__in CDeferredCommand* pCmd);\r
+\r
+    // Command-Due Checking\r
+    //\r
+    // There are two schemes of synchronisation: coarse and accurate. In\r
+    // coarse mode, you wait till the time arrives and then execute the cmd.\r
+    // In accurate mode, you wait until you are processing the sample that\r
+    // will appear at the time, and then execute the command. It's up to the\r
+    // filter which one it will implement. The filtergraph will always\r
+    // implement coarse mode for commands queued at the filtergraph.\r
+    //\r
+    // If you want coarse sync, you probably want to wait until there is a\r
+    // command due, and then execute it. You can do this by calling\r
+    // GetDueCommand. If you have several things to wait for, get the\r
+    // event handle from GetDueHandle() and when this is signalled then call\r
+    // GetDueCommand. Stream time will only advance between calls to Run and\r
+    // EndRun. Note that to avoid an extra thread there is no guarantee that\r
+    // if the handle is set there will be a command ready. Each time the\r
+    // event is signalled, call GetDueCommand (probably with a 0 timeout);\r
+    // This may return E_ABORT.\r
+    //\r
+    // If you want accurate sync, you must call GetCommandDueFor, passing\r
+    // as a parameter the stream time of the samples you are about to process.\r
+    // This will return:\r
+    //   -- a stream-time command due at or before that stream time\r
+    //   -- a presentation-time command due at or before the\r
+    //      time that stream time will be presented (only between Run\r
+    //      and EndRun calls, since outside of this, the mapping from\r
+    //      stream time to presentation time is not known.\r
+    //   -- any presentation-time command due now.\r
+    // This means that if you want accurate synchronisation on samples that\r
+    // might be processed during Paused mode, you need to use\r
+    // stream-time commands.\r
+    //\r
+    // In all cases, commands remain queued until Invoked or Cancelled. The\r
+    // setting and resetting of the event handle is managed entirely by this\r
+    // queue object.\r
+\r
+    // set the clock used for timing\r
+    virtual HRESULT SetSyncSource(__in_opt IReferenceClock*);\r
+\r
+    // switch to run mode. Streamtime to Presentation time mapping known.\r
+    virtual HRESULT Run(REFERENCE_TIME tStreamTimeOffset);\r
+\r
+    // switch to Stopped or Paused mode. Time mapping not known.\r
+    virtual HRESULT EndRun();\r
+\r
+    // return a pointer to the next due command. Blocks for msTimeout\r
+    // milliseconds until there is a due command.\r
+    // Stream-time commands will only become due between Run and Endrun calls.\r
+    // The command remains queued until invoked or cancelled.\r
+    // Returns E_ABORT if timeout occurs, otherwise S_OK (or other error).\r
+    // Returns an AddRef-ed object\r
+    virtual HRESULT GetDueCommand(__out CDeferredCommand ** ppCmd, long msTimeout);\r
+\r
+    // return the event handle that will be signalled whenever\r
+    // there are deferred commands due for execution (when GetDueCommand\r
+    // will not block).\r
+    HANDLE GetDueHandle() {\r
+        return HANDLE(m_evDue);\r
+    };\r
+\r
+    // return a pointer to a command that will be due for a given time.\r
+    // Pass in a stream time here. The stream time offset will be passed\r
+    // in via the Run method.\r
+    // Commands remain queued until invoked or cancelled.\r
+    // This method will not block. It will report VFW_E_NOT_FOUND if there\r
+    // are no commands due yet.\r
+    // Returns an AddRef-ed object\r
+    virtual HRESULT GetCommandDueFor(REFERENCE_TIME tStream, __out CDeferredCommand**ppCmd);\r
+\r
+    // check if a given time is due (TRUE if it is due yet)\r
+    BOOL CheckTime(CRefTime time, BOOL bStream) {\r
+\r
+        // if no clock, nothing is due!\r
+        if (!m_pClock) {\r
+            return FALSE;\r
+        }\r
+\r
+        // stream time\r
+        if (bStream) {\r
+\r
+            // not valid if not running\r
+            if (!m_bRunning) {\r
+                return FALSE;\r
+            }\r
+            // add on known stream time offset to get presentation time\r
+            time += m_StreamTimeOffset;\r
+        }\r
+\r
+        CRefTime Now;\r
+        m_pClock->GetTime((REFERENCE_TIME*)&Now);\r
+        return (time <= Now);\r
+    };\r
+\r
+protected:\r
+\r
+    // protect access to lists etc\r
+    CCritSec m_Lock;\r
+\r
+    // commands queued in presentation time are stored here\r
+    CGenericList<CDeferredCommand> m_listPresentation;\r
+\r
+    // commands queued in stream time are stored here\r
+    CGenericList<CDeferredCommand> m_listStream;\r
+\r
+    // set when any commands are due\r
+    CAMEvent m_evDue;\r
+\r
+    // creates an advise for the earliest time required, if any\r
+    void SetTimeAdvise(void);\r
+\r
+    // advise id from reference clock (0 if no outstanding advise)\r
+    DWORD_PTR m_dwAdvise;\r
+\r
+    // advise time is for this presentation time\r
+    CRefTime m_tCurrentAdvise;\r
+\r
+    // the reference clock we are using (addrefed)\r
+    IReferenceClock* m_pClock;\r
+\r
+    // true when running\r
+    BOOL m_bRunning;\r
+\r
+    // contains stream time offset when m_bRunning is true\r
+    CRefTime m_StreamTimeOffset;\r
+};\r
+\r
+#endif // __CTLUTIL__\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/ddmm.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/ddmm.cpp
new file mode 100644 (file)
index 0000000..bfa700c
--- /dev/null
@@ -0,0 +1,129 @@
+//------------------------------------------------------------------------------\r
+// File: DDMM.cpp\r
+//\r
+// Desc: DirectShow base classes - implements routines for using DirectDraw\r
+//       on a multimonitor system.\r
+//\r
+// Copyright (c) 1995-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+#include <ddraw.h>\r
+#include "ddmm.h"\r
+\r
+/*\r
+ * FindDeviceCallback\r
+ */\r
+typedef struct {\r
+       LPSTR   szDevice;\r
+       GUID*   lpGUID;\r
+       GUID    GUID;\r
+       BOOL    fFound;\r
+}   FindDeviceData;\r
+\r
+BOOL CALLBACK FindDeviceCallback(__in_opt GUID* lpGUID, __in LPSTR szName, __in LPSTR szDevice, __in LPVOID lParam)\r
+{\r
+       FindDeviceData *p = (FindDeviceData*)lParam;\r
+\r
+       if (lstrcmpiA(p->szDevice, szDevice) == 0) {\r
+           if (lpGUID) {\r
+               p->GUID = *lpGUID;\r
+               p->lpGUID = &p->GUID;\r
+           } else {\r
+               p->lpGUID = NULL;\r
+           }\r
+           p->fFound = TRUE;\r
+           return FALSE;\r
+       }\r
+       return TRUE;\r
+}\r
+\r
+\r
+BOOL CALLBACK FindDeviceCallbackEx(__in_opt GUID* lpGUID, __in LPSTR szName, __in LPSTR szDevice, __in LPVOID lParam, HMONITOR hMonitor)\r
+{\r
+       FindDeviceData *p = (FindDeviceData*)lParam;\r
+\r
+       if (lstrcmpiA(p->szDevice, szDevice) == 0) {\r
+           if (lpGUID) {\r
+               p->GUID = *lpGUID;\r
+               p->lpGUID = &p->GUID;\r
+           } else {\r
+               p->lpGUID = NULL;\r
+           }\r
+           p->fFound = TRUE;\r
+           return FALSE;\r
+       }\r
+       return TRUE;\r
+}\r
+\r
+\r
+/*\r
+ * DirectDrawCreateFromDevice\r
+ *\r
+ * create a DirectDraw object for a particular device\r
+ */\r
+IDirectDraw * DirectDrawCreateFromDevice(__in_opt LPSTR szDevice, PDRAWCREATE DirectDrawCreateP, PDRAWENUM DirectDrawEnumerateP)\r
+{\r
+       IDirectDraw*    pdd = NULL;\r
+       FindDeviceData  find;\r
+\r
+       if (szDevice == NULL) {\r
+               DirectDrawCreateP(NULL, &pdd, NULL);\r
+               return pdd;\r
+       }\r
+\r
+       find.szDevice = szDevice;\r
+       find.fFound   = FALSE;\r
+       DirectDrawEnumerateP(FindDeviceCallback, (LPVOID)&find);\r
+\r
+       if (find.fFound)\r
+       {\r
+               //\r
+               // In 4bpp mode the following DDraw call causes a message box to be popped\r
+               // up by DDraw (!?!).  It's DDraw's fault, but we don't like it.  So we\r
+               // make sure it doesn't happen.\r
+               //\r
+               UINT ErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);\r
+               DirectDrawCreateP(find.lpGUID, &pdd, NULL);\r
+               SetErrorMode(ErrorMode);\r
+       }\r
+\r
+       return pdd;\r
+}\r
+\r
+\r
+/*\r
+ * DirectDrawCreateFromDeviceEx\r
+ *\r
+ * create a DirectDraw object for a particular device\r
+ */\r
+IDirectDraw * DirectDrawCreateFromDeviceEx(__in_opt LPSTR szDevice, PDRAWCREATE DirectDrawCreateP, LPDIRECTDRAWENUMERATEEXA DirectDrawEnumerateExP)\r
+{\r
+       IDirectDraw*    pdd = NULL;\r
+       FindDeviceData  find;\r
+\r
+       if (szDevice == NULL) {\r
+               DirectDrawCreateP(NULL, &pdd, NULL);\r
+               return pdd;\r
+       }\r
+\r
+       find.szDevice = szDevice;\r
+       find.fFound   = FALSE;\r
+       DirectDrawEnumerateExP(FindDeviceCallbackEx, (LPVOID)&find,\r
+                                       DDENUM_ATTACHEDSECONDARYDEVICES);\r
+\r
+       if (find.fFound)\r
+       {\r
+               //\r
+               // In 4bpp mode the following DDraw call causes a message box to be popped\r
+               // up by DDraw (!?!).  It's DDraw's fault, but we don't like it.  So we\r
+               // make sure it doesn't happen.\r
+               //\r
+               UINT ErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);\r
+               DirectDrawCreateP(find.lpGUID, &pdd, NULL);\r
+               SetErrorMode(ErrorMode);\r
+       }\r
+\r
+       return pdd;\r
+}\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/ddmm.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/ddmm.h
new file mode 100644 (file)
index 0000000..7b311bc
--- /dev/null
@@ -0,0 +1,28 @@
+//------------------------------------------------------------------------------\r
+// File: DDMM.h\r
+//\r
+// Desc: DirectShow base classes - efines routines for using DirectDraw \r
+//       on a multimonitor system.\r
+//\r
+// Copyright (c) 1995-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifdef __cplusplus\r
+extern "C" {            /* Assume C declarations for C++ */\r
+#endif  /* __cplusplus */\r
+\r
+// DDRAW.H might not include these\r
+#ifndef DDENUM_ATTACHEDSECONDARYDEVICES\r
+#define DDENUM_ATTACHEDSECONDARYDEVICES     0x00000001L\r
+#endif\r
+\r
+typedef HRESULT (*PDRAWCREATE)(IID *,LPDIRECTDRAW *,LPUNKNOWN);\r
+typedef HRESULT (*PDRAWENUM)(LPDDENUMCALLBACKA, LPVOID);\r
+\r
+IDirectDraw * DirectDrawCreateFromDevice(__in_opt LPSTR, PDRAWCREATE, PDRAWENUM);\r
+IDirectDraw * DirectDrawCreateFromDeviceEx(__in_opt LPSTR, PDRAWCREATE, LPDIRECTDRAWENUMERATEEXA);\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif /* __cplusplus */\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/dllentry.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/dllentry.cpp
new file mode 100644 (file)
index 0000000..130aad6
--- /dev/null
@@ -0,0 +1,367 @@
+//------------------------------------------------------------------------------\r
+// File: DlleEntry.cpp\r
+//\r
+// Desc: DirectShow base classes - implements classes used to support dll\r
+//       entry points for COM objects.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+#include <initguid.h>\r
+\r
+#ifdef DEBUG\r
+#ifdef UNICODE\r
+#ifndef _UNICODE\r
+#define _UNICODE\r
+#endif // _UNICODE\r
+#endif // UNICODE\r
+\r
+#include <tchar.h>\r
+#endif // DEBUG\r
+#include <strsafe.h>\r
+\r
+extern CFactoryTemplate g_Templates[];\r
+extern int g_cTemplates;\r
+\r
+HINSTANCE g_hInst;\r
+DWORD    g_amPlatform;         // VER_PLATFORM_WIN32_WINDOWS etc... (from GetVersionEx)\r
+OSVERSIONINFO g_osInfo;\r
+\r
+//\r
+// an instance of this is created by the DLLGetClassObject entrypoint\r
+// it uses the CFactoryTemplate object it is given to support the\r
+// IClassFactory interface\r
+\r
+class CClassFactory : public IClassFactory, public CBaseObject\r
+{\r
+\r
+private:\r
+    const CFactoryTemplate *const m_pTemplate;\r
+\r
+    ULONG m_cRef;\r
+\r
+    static int m_cLocked;\r
+public:\r
+    CClassFactory(const CFactoryTemplate *);\r
+\r
+    // IUnknown\r
+    STDMETHODIMP QueryInterface(REFIID riid, __deref_out void ** ppv);\r
+    STDMETHODIMP_(ULONG)AddRef();\r
+    STDMETHODIMP_(ULONG)Release();\r
+\r
+    // IClassFactory\r
+    STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, __deref_out void **pv);\r
+    STDMETHODIMP LockServer(BOOL fLock);\r
+\r
+    // allow DLLGetClassObject to know about global server lock status\r
+    static BOOL IsLocked() {\r
+        return (m_cLocked > 0);\r
+    };\r
+};\r
+\r
+// process-wide dll locked state\r
+int CClassFactory::m_cLocked = 0;\r
+\r
+CClassFactory::CClassFactory(const CFactoryTemplate *pTemplate)\r
+: CBaseObject(NAME("Class Factory"))\r
+, m_cRef(0)\r
+, m_pTemplate(pTemplate)\r
+{\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CClassFactory::QueryInterface(REFIID riid,__deref_out void **ppv)\r
+{\r
+    CheckPointer(ppv,E_POINTER)\r
+    ValidateReadWritePtr(ppv,sizeof(PVOID));\r
+    *ppv = NULL;\r
+\r
+    // any interface on this object is the object pointer.\r
+    if ((riid == IID_IUnknown) || (riid == IID_IClassFactory)) {\r
+        *ppv = (LPVOID) this;\r
+       // AddRef returned interface pointer\r
+        ((LPUNKNOWN) *ppv)->AddRef();\r
+        return NOERROR;\r
+    }\r
+\r
+    return ResultFromScode(E_NOINTERFACE);\r
+}\r
+\r
+\r
+STDMETHODIMP_(ULONG)\r
+CClassFactory::AddRef()\r
+{\r
+    return ++m_cRef;\r
+}\r
+\r
+STDMETHODIMP_(ULONG)\r
+CClassFactory::Release()\r
+{\r
+    LONG lRef = InterlockedDecrement((volatile LONG *)&m_cRef);\r
+    if (lRef == 0) {\r
+        delete this;\r
+        return 0;\r
+    } else {\r
+        return lRef;\r
+    }\r
+}\r
+\r
+STDMETHODIMP\r
+CClassFactory::CreateInstance(\r
+    LPUNKNOWN pUnkOuter,\r
+    REFIID riid,\r
+    __deref_out void **pv)\r
+{\r
+    CheckPointer(pv,E_POINTER)\r
+    ValidateReadWritePtr(pv,sizeof(void *));\r
+    *pv = NULL;\r
+\r
+    /* Enforce the normal OLE rules regarding interfaces and delegation */\r
+\r
+    if (pUnkOuter != NULL) {\r
+        if (IsEqualIID(riid,IID_IUnknown) == FALSE) {\r
+            *pv = NULL;\r
+            return ResultFromScode(E_NOINTERFACE);\r
+        }\r
+    }\r
+\r
+    /* Create the new object through the derived class's create function */\r
+\r
+    HRESULT hr = NOERROR;\r
+    CUnknown *pObj = m_pTemplate->CreateInstance(pUnkOuter, &hr);\r
+\r
+    if (pObj == NULL) {\r
+        *pv = NULL;\r
+       if (SUCCEEDED(hr)) {\r
+           hr = E_OUTOFMEMORY;\r
+       }\r
+       return hr;\r
+    }\r
+\r
+    /* Delete the object if we got a construction error */\r
+\r
+    if (FAILED(hr)) {\r
+        delete pObj;\r
+        *pv = NULL;\r
+        return hr;\r
+    }\r
+\r
+    /* Get a reference counted interface on the object */\r
+\r
+    /* We wrap the non-delegating QI with NDAddRef & NDRelease. */\r
+    /* This protects any outer object from being prematurely    */\r
+    /* released by an inner object that may have to be created  */\r
+    /* in order to supply the requested interface.              */\r
+    pObj->NonDelegatingAddRef();\r
+    hr = pObj->NonDelegatingQueryInterface(riid, pv);\r
+    pObj->NonDelegatingRelease();\r
+    /* Note that if NonDelegatingQueryInterface fails, it will  */\r
+    /* not increment the ref count, so the NonDelegatingRelease */\r
+    /* will drop the ref back to zero and the object will "self-*/\r
+    /* destruct".  Hence we don't need additional tidy-up code  */\r
+    /* to cope with NonDelegatingQueryInterface failing.        */\r
+\r
+    if (SUCCEEDED(hr)) {\r
+        ASSERT(*pv);\r
+    }\r
+\r
+    return hr;\r
+}\r
+\r
+STDMETHODIMP\r
+CClassFactory::LockServer(BOOL fLock)\r
+{\r
+    if (fLock) {\r
+        m_cLocked++;\r
+    } else {\r
+        m_cLocked--;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// --- COM entrypoints -----------------------------------------\r
+\r
+//called by COM to get the class factory object for a given class\r
+__control_entrypoint(DllExport) STDAPI\r
+DllGetClassObject(\r
+    __in REFCLSID rClsID,\r
+    __in REFIID riid,\r
+    __deref_out void **pv)\r
+{\r
+    *pv = NULL;\r
+    if (!(riid == IID_IUnknown) && !(riid == IID_IClassFactory)) {\r
+            return E_NOINTERFACE;\r
+    }\r
+\r
+    // traverse the array of templates looking for one with this\r
+    // class id\r
+    for (int i = 0; i < g_cTemplates; i++) {\r
+        const CFactoryTemplate * pT = &g_Templates[i];\r
+        if (pT->IsClassID(rClsID)) {\r
+\r
+            // found a template - make a class factory based on this\r
+            // template\r
+\r
+            *pv = (LPVOID) (LPUNKNOWN) new CClassFactory(pT);\r
+            if (*pv == NULL) {\r
+                return E_OUTOFMEMORY;\r
+            }\r
+            ((LPUNKNOWN)*pv)->AddRef();\r
+            return NOERROR;\r
+        }\r
+    }\r
+    return CLASS_E_CLASSNOTAVAILABLE;\r
+}\r
+\r
+//\r
+//  Call any initialization routines\r
+//\r
+void\r
+DllInitClasses(BOOL bLoading)\r
+{\r
+    int i;\r
+\r
+    // traverse the array of templates calling the init routine\r
+    // if they have one\r
+    for (i = 0; i < g_cTemplates; i++) {\r
+        const CFactoryTemplate * pT = &g_Templates[i];\r
+        if (pT->m_lpfnInit != NULL) {\r
+            (*pT->m_lpfnInit)(bLoading, pT->m_ClsID);\r
+        }\r
+    }\r
+\r
+}\r
+\r
+// called by COM to determine if this dll can be unloaded\r
+// return ok unless there are outstanding objects or a lock requested\r
+// by IClassFactory::LockServer\r
+//\r
+// CClassFactory has a static function that can tell us about the locks,\r
+// and CCOMObject has a static function that can tell us about the active\r
+// object count\r
+STDAPI\r
+DllCanUnloadNow()\r
+{\r
+    DbgLog((LOG_MEMORY,2,TEXT("DLLCanUnloadNow called - IsLocked = %d, Active objects = %d"),\r
+        CClassFactory::IsLocked(),\r
+        CBaseObject::ObjectsActive()));\r
+\r
+    if (CClassFactory::IsLocked() || CBaseObject::ObjectsActive()) {\r
+       return S_FALSE;\r
+    } else {\r
+        return S_OK;\r
+    }\r
+}\r
+\r
+\r
+// --- standard WIN32 entrypoints --------------------------------------\r
+\r
+\r
+extern "C" void __cdecl __security_init_cookie(void);\r
+extern "C" BOOL WINAPI _DllEntryPoint(HINSTANCE, ULONG, __inout_opt LPVOID);\r
+#pragma comment(linker, "/merge:.CRT=.rdata")\r
+\r
+extern "C"\r
+DECLSPEC_NOINLINE\r
+BOOL \r
+WINAPI\r
+DllEntryPoint(\r
+    HINSTANCE hInstance, \r
+    ULONG ulReason, \r
+    __inout_opt LPVOID pv\r
+    )\r
+{\r
+    if ( ulReason == DLL_PROCESS_ATTACH ) {\r
+        // Must happen before any other code is executed.  Thankfully - it's re-entrant\r
+        __security_init_cookie();\r
+    }\r
+    return _DllEntryPoint(hInstance, ulReason, pv);\r
+}\r
+\r
+\r
+DECLSPEC_NOINLINE\r
+BOOL \r
+WINAPI\r
+_DllEntryPoint(\r
+    HINSTANCE hInstance, \r
+    ULONG ulReason, \r
+    __inout_opt LPVOID pv\r
+    )\r
+{\r
+#ifdef DEBUG\r
+    extern bool g_fDbgInDllEntryPoint;\r
+    g_fDbgInDllEntryPoint = true;\r
+#endif\r
+\r
+    switch (ulReason)\r
+    {\r
+\r
+    case DLL_PROCESS_ATTACH:\r
+        DisableThreadLibraryCalls(hInstance);\r
+        DbgInitialise(hInstance);\r
+\r
+       {\r
+           // The platform identifier is used to work out whether\r
+           // full unicode support is available or not.  Hence the\r
+           // default will be the lowest common denominator - i.e. N/A\r
+                g_amPlatform = VER_PLATFORM_WIN32_WINDOWS; // win95 assumed in case GetVersionEx fails\r
+    \r
+                g_osInfo.dwOSVersionInfoSize = sizeof(g_osInfo);\r
+                if (GetVersionEx(&g_osInfo)) {\r
+               g_amPlatform = g_osInfo.dwPlatformId;\r
+           } else {\r
+               DbgLog((LOG_ERROR, 1, TEXT("Failed to get the OS platform, assuming Win95")));\r
+           }\r
+       }\r
+\r
+        g_hInst = hInstance;\r
+        DllInitClasses(TRUE);\r
+        break;\r
+\r
+    case DLL_PROCESS_DETACH:\r
+        DllInitClasses(FALSE);\r
+\r
+#ifdef DEBUG\r
+        if (CBaseObject::ObjectsActive()) {\r
+            DbgSetModuleLevel(LOG_MEMORY, 2);\r
+            TCHAR szInfo[512];\r
+            extern TCHAR m_ModuleName[];     // Cut down module name\r
+\r
+            TCHAR FullName[_MAX_PATH];      // Load the full path and module name\r
+            TCHAR *pName;                   // Searches from the end for a backslash\r
+\r
+            GetModuleFileName(NULL,FullName,_MAX_PATH);\r
+            pName = _tcsrchr(FullName,'\\');\r
+            if (pName == NULL) {\r
+                pName = FullName;\r
+            } else {\r
+                pName++;\r
+            }\r
+\r
+            (void)StringCchPrintf(szInfo, NUMELMS(szInfo), TEXT("Executable: %s  Pid %x  Tid %x. "),\r
+                           pName, GetCurrentProcessId(), GetCurrentThreadId());\r
+\r
+            (void)StringCchPrintf(szInfo+lstrlen(szInfo), NUMELMS(szInfo) - lstrlen(szInfo), TEXT("Module %s, %d objects left active!"),\r
+                     m_ModuleName, CBaseObject::ObjectsActive());\r
+            DbgAssert(szInfo, TEXT(__FILE__),__LINE__);\r
+\r
+           // If running remotely wait for the Assert to be acknowledged\r
+           // before dumping out the object register\r
+            DbgDumpObjectRegister();\r
+        }\r
+        DbgTerminate();\r
+#endif\r
+        break;\r
+    }\r
+\r
+#ifdef DEBUG\r
+    g_fDbgInDllEntryPoint = false;\r
+#endif\r
+    return TRUE;\r
+}\r
+\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/dllsetup.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/dllsetup.cpp
new file mode 100644 (file)
index 0000000..ede9c3f
--- /dev/null
@@ -0,0 +1,693 @@
+//------------------------------------------------------------------------------\r
+// File: DllSetup.cpp\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+#include <strsafe.h>\r
+\r
+//---------------------------------------------------------------------------\r
+// defines\r
+\r
+#define MAX_KEY_LEN  260\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// externally defined functions/variable\r
+\r
+extern int g_cTemplates;\r
+extern CFactoryTemplate g_Templates[];\r
+\r
+//---------------------------------------------------------------------------\r
+//\r
+// EliminateSubKey\r
+//\r
+// Try to enumerate all keys under this one.\r
+// if we find anything, delete it completely.\r
+// Otherwise just delete it.\r
+//\r
+// note - this was pinched/duplicated from\r
+// Filgraph\Mapper.cpp - so should it be in\r
+// a lib somewhere?\r
+//\r
+//---------------------------------------------------------------------------\r
+\r
+STDAPI\r
+EliminateSubKey( HKEY hkey, LPCTSTR strSubKey )\r
+{\r
+  HKEY hk;\r
+  if (0 == lstrlen(strSubKey) ) {\r
+      // defensive approach\r
+      return E_FAIL;\r
+  }\r
+\r
+  LONG lreturn = RegOpenKeyEx( hkey\r
+                             , strSubKey\r
+                             , 0\r
+                             , MAXIMUM_ALLOWED\r
+                             , &hk );\r
+\r
+  ASSERT(    lreturn == ERROR_SUCCESS\r
+          || lreturn == ERROR_FILE_NOT_FOUND\r
+          || lreturn == ERROR_INVALID_HANDLE );\r
+\r
+  if( ERROR_SUCCESS == lreturn )\r
+  {\r
+    // Keep on enumerating the first (zero-th)\r
+    // key and deleting that\r
+\r
+    for( ; ; )\r
+    {\r
+      TCHAR Buffer[MAX_KEY_LEN];\r
+      DWORD dw = MAX_KEY_LEN;\r
+      FILETIME ft;\r
+\r
+      lreturn = RegEnumKeyEx( hk\r
+                            , 0\r
+                            , Buffer\r
+                            , &dw\r
+                            , NULL\r
+                            , NULL\r
+                            , NULL\r
+                            , &ft);\r
+\r
+      ASSERT(    lreturn == ERROR_SUCCESS\r
+              || lreturn == ERROR_NO_MORE_ITEMS );\r
+\r
+      if( ERROR_SUCCESS == lreturn )\r
+      {\r
+        EliminateSubKey(hk, Buffer);\r
+      }\r
+      else\r
+      {\r
+        break;\r
+      }\r
+    }\r
+\r
+    RegCloseKey(hk);\r
+    RegDeleteKey(hkey, strSubKey);\r
+  }\r
+\r
+  return NOERROR;\r
+}\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//\r
+// AMovieSetupRegisterServer()\r
+//\r
+// registers specfied file "szFileName" as server for\r
+// CLSID "clsServer".  A description is also required.\r
+// The ThreadingModel and ServerType are optional, as\r
+// they default to InprocServer32 (i.e. dll) and Both.\r
+//\r
+//---------------------------------------------------------------------------\r
+\r
+STDAPI\r
+AMovieSetupRegisterServer( CLSID   clsServer\r
+                         , LPCWSTR szDescription\r
+                         , LPCWSTR szFileName\r
+                         , LPCWSTR szThreadingModel = L"Both"\r
+                         , LPCWSTR szServerType     = L"InprocServer32" )\r
+{\r
+  // temp buffer\r
+  //\r
+  TCHAR achTemp[MAX_PATH];\r
+\r
+  // convert CLSID uuid to string and write\r
+  // out subkey as string - CLSID\{}\r
+  //\r
+  OLECHAR szCLSID[CHARS_IN_GUID];\r
+  HRESULT hr = StringFromGUID2( clsServer\r
+                              , szCLSID\r
+                              , CHARS_IN_GUID );\r
+  ASSERT( SUCCEEDED(hr) );\r
+\r
+  // create key\r
+  //\r
+  HKEY hkey;\r
+  (void)StringCchPrintf( achTemp, NUMELMS(achTemp), TEXT("CLSID\\%ls"), szCLSID );\r
+  LONG lreturn = RegCreateKey( HKEY_CLASSES_ROOT\r
+                             , (LPCTSTR)achTemp\r
+                             , &hkey              );\r
+  if( ERROR_SUCCESS != lreturn )\r
+  {\r
+    return AmHresultFromWin32(lreturn);\r
+  }\r
+\r
+  // set description string\r
+  //\r
+\r
+  (void)StringCchPrintf( achTemp, NUMELMS(achTemp), TEXT("%ls"), szDescription );\r
+  lreturn = RegSetValue( hkey\r
+                       , (LPCTSTR)NULL\r
+                       , REG_SZ\r
+                       , achTemp\r
+                       , sizeof(achTemp) );\r
+  if( ERROR_SUCCESS != lreturn )\r
+  {\r
+    RegCloseKey( hkey );\r
+    return AmHresultFromWin32(lreturn);\r
+  }\r
+\r
+  // create CLSID\\{"CLSID"}\\"ServerType" key,\r
+  // using key to CLSID\\{"CLSID"} passed back by\r
+  // last call to RegCreateKey().\r
+  //\r
+  HKEY hsubkey;\r
+\r
+  (void)StringCchPrintf( achTemp, NUMELMS(achTemp), TEXT("%ls"), szServerType );\r
+  lreturn = RegCreateKey( hkey\r
+                        , achTemp\r
+                        , &hsubkey     );\r
+  if( ERROR_SUCCESS != lreturn )\r
+  {\r
+    RegCloseKey( hkey );\r
+    return AmHresultFromWin32(lreturn);\r
+  }\r
+\r
+  // set Server string\r
+  //\r
+  (void)StringCchPrintf( achTemp, NUMELMS(achTemp), TEXT("%ls"), szFileName );\r
+  lreturn = RegSetValue( hsubkey\r
+                       , (LPCTSTR)NULL\r
+                       , REG_SZ\r
+                       , (LPCTSTR)achTemp\r
+                       , sizeof(TCHAR) * (lstrlen(achTemp)+1) );\r
+  if( ERROR_SUCCESS != lreturn )\r
+  {\r
+    RegCloseKey( hkey );\r
+    RegCloseKey( hsubkey );\r
+    return AmHresultFromWin32(lreturn);\r
+  }\r
+\r
+  (void)StringCchPrintf( achTemp, NUMELMS(achTemp), TEXT("%ls"), szThreadingModel );\r
+  lreturn = RegSetValueEx( hsubkey\r
+                         , TEXT("ThreadingModel")\r
+                         , 0L\r
+                         , REG_SZ\r
+                         , (CONST BYTE *)achTemp\r
+                         , sizeof(TCHAR) * (lstrlen(achTemp)+1) );\r
+\r
+  // close hkeys\r
+  //\r
+  RegCloseKey( hkey );\r
+  RegCloseKey( hsubkey );\r
+\r
+  // and return\r
+  //\r
+  return HRESULT_FROM_WIN32(lreturn);\r
+\r
+}\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//\r
+// AMovieSetupUnregisterServer()\r
+//\r
+// default ActiveMovie dll setup function\r
+// - to use must be called from an exported\r
+//   function named DllRegisterServer()\r
+//\r
+//---------------------------------------------------------------------------\r
+\r
+STDAPI\r
+AMovieSetupUnregisterServer( CLSID clsServer )\r
+{\r
+  // convert CLSID uuid to string and write\r
+  // out subkey CLSID\{}\r
+  //\r
+  OLECHAR szCLSID[CHARS_IN_GUID];\r
+  HRESULT hr = StringFromGUID2( clsServer\r
+                              , szCLSID\r
+                              , CHARS_IN_GUID );\r
+  ASSERT( SUCCEEDED(hr) );\r
+\r
+  TCHAR achBuffer[MAX_KEY_LEN];\r
+  (void)StringCchPrintf( achBuffer, NUMELMS(achBuffer), TEXT("CLSID\\%ls"), szCLSID );\r
+\r
+  // delete subkey\r
+  //\r
+\r
+  hr = EliminateSubKey( HKEY_CLASSES_ROOT, achBuffer );\r
+  ASSERT( SUCCEEDED(hr) );\r
+\r
+  // return\r
+  //\r
+  return NOERROR;\r
+}\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//\r
+// AMovieSetupRegisterFilter through IFilterMapper2\r
+//\r
+//---------------------------------------------------------------------------\r
+\r
+STDAPI\r
+AMovieSetupRegisterFilter2( const AMOVIESETUP_FILTER * const psetupdata\r
+                          , IFilterMapper2 *                 pIFM2\r
+                          , BOOL                             bRegister  )\r
+{\r
+  DbgLog((LOG_TRACE, 3, TEXT("= AMovieSetupRegisterFilter")));\r
+\r
+  // check we've got data\r
+  //\r
+  if( NULL == psetupdata ) return S_FALSE;\r
+\r
+\r
+  // unregister filter\r
+  // (as pins are subkeys of filter's CLSID key\r
+  // they do not need to be removed separately).\r
+  //\r
+  DbgLog((LOG_TRACE, 3, TEXT("= = unregister filter")));\r
+  HRESULT hr = pIFM2->UnregisterFilter(\r
+      0,                        // default category\r
+      0,                        // default instance name\r
+      *psetupdata->clsID );\r
+\r
+\r
+  if( bRegister )\r
+  {\r
+    REGFILTER2 rf2;\r
+    rf2.dwVersion = 1;\r
+    rf2.dwMerit = psetupdata->dwMerit;\r
+    rf2.cPins = psetupdata->nPins;\r
+    rf2.rgPins = psetupdata->lpPin;\r
+    \r
+    // register filter\r
+    //\r
+    DbgLog((LOG_TRACE, 3, TEXT("= = register filter")));\r
+    hr = pIFM2->RegisterFilter(*psetupdata->clsID\r
+                             , psetupdata->strName\r
+                             , 0 // moniker\r
+                             , 0 // category\r
+                             , NULL // instance\r
+                             , &rf2);\r
+  }\r
+\r
+  // handle one acceptable "error" - that\r
+  // of filter not being registered!\r
+  // (couldn't find a suitable #define'd\r
+  // name for the error!)\r
+  //\r
+  if( 0x80070002 == hr)\r
+    return NOERROR;\r
+  else\r
+    return hr;\r
+}\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//\r
+// RegisterAllServers()\r
+//\r
+//---------------------------------------------------------------------------\r
+\r
+STDAPI\r
+RegisterAllServers( LPCWSTR szFileName, BOOL bRegister )\r
+{\r
+  HRESULT hr = NOERROR;\r
+\r
+  for( int i = 0; i < g_cTemplates; i++ )\r
+  {\r
+    // get i'th template\r
+    //\r
+    const CFactoryTemplate *pT = &g_Templates[i];\r
+\r
+    DbgLog((LOG_TRACE, 2, TEXT("- - register %ls"),\r
+           (LPCWSTR)pT->m_Name ));\r
+\r
+    // register CLSID and InprocServer32\r
+    //\r
+    if( bRegister )\r
+    {\r
+      hr = AMovieSetupRegisterServer( *(pT->m_ClsID)\r
+                                    , (LPCWSTR)pT->m_Name\r
+                                    , szFileName );\r
+    }\r
+    else\r
+    {\r
+      hr = AMovieSetupUnregisterServer( *(pT->m_ClsID) );\r
+    }\r
+\r
+    // check final error for this pass\r
+    // and break loop if we failed\r
+    //\r
+    if( FAILED(hr) )\r
+      break;\r
+  }\r
+\r
+  return hr;\r
+}\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//\r
+// AMovieDllRegisterServer2()\r
+//\r
+// default ActiveMovie dll setup function\r
+// - to use must be called from an exported\r
+//   function named DllRegisterServer()\r
+//\r
+// this function is table driven using the\r
+// static members of the CFactoryTemplate\r
+// class defined in the dll.\r
+//\r
+// it registers the Dll as the InprocServer32\r
+// and then calls the IAMovieSetup.Register\r
+// method.\r
+//\r
+//---------------------------------------------------------------------------\r
+\r
+STDAPI\r
+AMovieDllRegisterServer2( BOOL bRegister )\r
+{\r
+  HRESULT hr = NOERROR;\r
+\r
+  DbgLog((LOG_TRACE, 2, TEXT("AMovieDllRegisterServer2()")));\r
+\r
+  // get file name (where g_hInst is the\r
+  // instance handle of the filter dll)\r
+  //\r
+  WCHAR achFileName[MAX_PATH];\r
+\r
+  // WIN95 doesn't support GetModuleFileNameW\r
+  //\r
+  {\r
+    char achTemp[MAX_PATH];\r
+\r
+    DbgLog((LOG_TRACE, 2, TEXT("- get module file name")));\r
+\r
+    // g_hInst handle is set in our dll entry point. Make sure\r
+    // DllEntryPoint in dllentry.cpp is called\r
+    ASSERT(g_hInst != 0);\r
+\r
+    if( 0 == GetModuleFileNameA( g_hInst\r
+                              , achTemp\r
+                              , sizeof(achTemp) ) )\r
+    {\r
+      // we've failed!\r
+      DWORD dwerr = GetLastError();\r
+      return AmHresultFromWin32(dwerr);\r
+    }\r
+\r
+    MultiByteToWideChar( CP_ACP\r
+                       , 0L\r
+                       , achTemp\r
+                       , lstrlenA(achTemp) + 1\r
+                       , achFileName\r
+                       , NUMELMS(achFileName) );\r
+  }\r
+\r
+  //\r
+  // first registering, register all OLE servers\r
+  //\r
+  if( bRegister )\r
+  {\r
+    DbgLog((LOG_TRACE, 2, TEXT("- register OLE Servers")));\r
+    hr = RegisterAllServers( achFileName, TRUE );\r
+  }\r
+\r
+  //\r
+  // next, register/unregister all filters\r
+  //\r
+\r
+  if( SUCCEEDED(hr) )\r
+  {\r
+    // init is ref counted so call just in case\r
+    // we're being called cold.\r
+    //\r
+    DbgLog((LOG_TRACE, 2, TEXT("- CoInitialize")));\r
+    hr = CoInitialize( (LPVOID)NULL );\r
+    ASSERT( SUCCEEDED(hr) );\r
+\r
+    // get hold of IFilterMapper2\r
+    //\r
+    DbgLog((LOG_TRACE, 2, TEXT("- obtain IFilterMapper2")));\r
+    IFilterMapper2 *pIFM2 = 0;\r
+    IFilterMapper *pIFM = 0;\r
+    hr = CoCreateInstance( CLSID_FilterMapper2\r
+                         , NULL\r
+                         , CLSCTX_INPROC_SERVER\r
+                         , IID_IFilterMapper2\r
+                         , (void **)&pIFM2       );\r
+    if(FAILED(hr))\r
+    {\r
+        DbgLog((LOG_TRACE, 2, TEXT("- trying IFilterMapper instead")));\r
+\r
+        hr = CoCreateInstance(\r
+            CLSID_FilterMapper,\r
+            NULL,\r
+            CLSCTX_INPROC_SERVER,\r
+            IID_IFilterMapper,\r
+            (void **)&pIFM);\r
+    }\r
+    if( SUCCEEDED(hr) )\r
+    {\r
+      // scan through array of CFactoryTemplates\r
+      // registering servers and filters.\r
+      //\r
+      DbgLog((LOG_TRACE, 2, TEXT("- register Filters")));\r
+      for( int i = 0; i < g_cTemplates; i++ )\r
+      {\r
+        // get i'th template\r
+        //\r
+        const CFactoryTemplate *pT = &g_Templates[i];\r
+\r
+        if( NULL != pT->m_pAMovieSetup_Filter )\r
+        {\r
+          DbgLog((LOG_TRACE, 2, TEXT("- - register %ls"), (LPCWSTR)pT->m_Name ));\r
+\r
+          if(pIFM2)\r
+          {\r
+              hr = AMovieSetupRegisterFilter2( pT->m_pAMovieSetup_Filter, pIFM2, bRegister );\r
+          }\r
+          else\r
+          {\r
+              hr = AMovieSetupRegisterFilter( pT->m_pAMovieSetup_Filter, pIFM, bRegister );\r
+          }\r
+        }\r
+\r
+        // check final error for this pass\r
+        // and break loop if we failed\r
+        //\r
+        if( FAILED(hr) )\r
+          break;\r
+      }\r
+\r
+      // release interface\r
+      //\r
+      if(pIFM2)\r
+          pIFM2->Release();\r
+      else\r
+          pIFM->Release();\r
+\r
+    }\r
+\r
+    // and clear up\r
+    //\r
+    CoFreeUnusedLibraries();\r
+    CoUninitialize();\r
+  }\r
+\r
+  //\r
+  // if unregistering, unregister all OLE servers\r
+  //\r
+  if( SUCCEEDED(hr) && !bRegister )\r
+  {\r
+    DbgLog((LOG_TRACE, 2, TEXT("- register OLE Servers")));\r
+    hr = RegisterAllServers( achFileName, FALSE );\r
+  }\r
+\r
+  DbgLog((LOG_TRACE, 2, TEXT("- return %0x"), hr));\r
+  return hr;\r
+}\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//\r
+// AMovieDllRegisterServer()\r
+//\r
+// default ActiveMovie dll setup function\r
+// - to use must be called from an exported\r
+//   function named DllRegisterServer()\r
+//\r
+// this function is table driven using the\r
+// static members of the CFactoryTemplate\r
+// class defined in the dll.\r
+//\r
+// it registers the Dll as the InprocServer32\r
+// and then calls the IAMovieSetup.Register\r
+// method.\r
+//\r
+//---------------------------------------------------------------------------\r
+\r
+\r
+STDAPI\r
+AMovieDllRegisterServer( void )\r
+{\r
+  HRESULT hr = NOERROR;\r
+\r
+  // get file name (where g_hInst is the\r
+  // instance handle of the filter dll)\r
+  //\r
+  WCHAR achFileName[MAX_PATH];\r
+\r
+  {\r
+    // WIN95 doesn't support GetModuleFileNameW\r
+    //\r
+    char achTemp[MAX_PATH];\r
+\r
+    if( 0 == GetModuleFileNameA( g_hInst\r
+                              , achTemp\r
+                              , sizeof(achTemp) ) )\r
+    {\r
+      // we've failed!\r
+      DWORD dwerr = GetLastError();\r
+      return AmHresultFromWin32(dwerr);\r
+    }\r
+\r
+    MultiByteToWideChar( CP_ACP\r
+                       , 0L\r
+                       , achTemp\r
+                       , lstrlenA(achTemp) + 1\r
+                       , achFileName\r
+                       , NUMELMS(achFileName) );\r
+  }\r
+\r
+  // scan through array of CFactoryTemplates\r
+  // registering servers and filters.\r
+  //\r
+  for( int i = 0; i < g_cTemplates; i++ )\r
+  {\r
+    // get i'th template\r
+    //\r
+    const CFactoryTemplate *pT = &g_Templates[i];\r
+\r
+    // register CLSID and InprocServer32\r
+    //\r
+    hr = AMovieSetupRegisterServer( *(pT->m_ClsID)\r
+                                  , (LPCWSTR)pT->m_Name\r
+                                  , achFileName );\r
+\r
+    // instantiate all servers and get hold of\r
+    // IAMovieSetup, if implemented, and call\r
+    // IAMovieSetup.Register() method\r
+    //\r
+    if( SUCCEEDED(hr) && (NULL != pT->m_lpfnNew) )\r
+    {\r
+      // instantiate object\r
+      //\r
+      PAMOVIESETUP psetup;\r
+      hr = CoCreateInstance( *(pT->m_ClsID)\r
+                           , 0\r
+                           , CLSCTX_INPROC_SERVER\r
+                           , IID_IAMovieSetup\r
+                           , reinterpret_cast<void**>(&psetup) );\r
+      if( SUCCEEDED(hr) )\r
+      {\r
+        hr = psetup->Unregister();\r
+        if( SUCCEEDED(hr) )\r
+          hr = psetup->Register();\r
+        psetup->Release();\r
+      }\r
+      else\r
+      {\r
+        if(    (E_NOINTERFACE      == hr )\r
+            || (VFW_E_NEED_OWNER == hr ) )\r
+          hr = NOERROR;\r
+      }\r
+    }\r
+\r
+    // check final error for this pass\r
+    // and break loop if we failed\r
+    //\r
+    if( FAILED(hr) )\r
+      break;\r
+\r
+  } // end-for\r
+\r
+  return hr;\r
+}\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//\r
+// AMovieDllUnregisterServer()\r
+//\r
+// default ActiveMovie dll uninstall function\r
+// - to use must be called from an exported\r
+//   function named DllRegisterServer()\r
+//\r
+// this function is table driven using the\r
+// static members of the CFactoryTemplate\r
+// class defined in the dll.\r
+//\r
+// it calls the IAMovieSetup.Unregister\r
+// method and then unregisters the Dll\r
+// as the InprocServer32\r
+//\r
+//---------------------------------------------------------------------------\r
+\r
+STDAPI\r
+AMovieDllUnregisterServer()\r
+{\r
+  // initialize return code\r
+  //\r
+  HRESULT hr = NOERROR;\r
+\r
+  // scan through CFactory template and unregister\r
+  // all OLE servers and filters.\r
+  //\r
+  for( int i = g_cTemplates; i--; )\r
+  {\r
+    // get i'th template\r
+    //\r
+    const CFactoryTemplate *pT = &g_Templates[i];\r
+\r
+    // check method exists\r
+    //\r
+    if( NULL != pT->m_lpfnNew )\r
+    {\r
+      // instantiate object\r
+      //\r
+      PAMOVIESETUP psetup;\r
+      hr = CoCreateInstance( *(pT->m_ClsID)\r
+                           , 0\r
+                           , CLSCTX_INPROC_SERVER\r
+                           , IID_IAMovieSetup\r
+                           , reinterpret_cast<void**>(&psetup) );\r
+      if( SUCCEEDED(hr) )\r
+      {\r
+        hr = psetup->Unregister();\r
+        psetup->Release();\r
+      }\r
+      else\r
+      {\r
+        if(    (E_NOINTERFACE      == hr )\r
+            || (VFW_E_NEED_OWNER == hr ) )\r
+           hr = NOERROR;\r
+      }\r
+    }\r
+\r
+    // unregister CLSID and InprocServer32\r
+    //\r
+    if( SUCCEEDED(hr) )\r
+    {\r
+      hr = AMovieSetupUnregisterServer( *(pT->m_ClsID) );\r
+    }\r
+\r
+    // check final error for this pass\r
+    // and break loop if we failed\r
+    //\r
+    if( FAILED(hr) )\r
+      break;\r
+  }\r
+\r
+  return hr;\r
+}\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/dllsetup.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/dllsetup.h
new file mode 100644 (file)
index 0000000..e363b8b
--- /dev/null
@@ -0,0 +1,46 @@
+//------------------------------------------------------------------------------\r
+// File: DllSetup.h\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+// To be self registering, OLE servers must\r
+// export functions named DllRegisterServer\r
+// and DllUnregisterServer.  To allow use of\r
+// custom and default implementations the\r
+// defaults are named AMovieDllRegisterServer\r
+// and AMovieDllUnregisterServer.\r
+//\r
+// To the use the default implementation you\r
+// must provide stub functions.\r
+//\r
+// i.e. STDAPI DllRegisterServer()\r
+//      {\r
+//        return AMovieDllRegisterServer();\r
+//      }\r
+//\r
+//      STDAPI DllUnregisterServer()\r
+//      {\r
+//        return AMovieDllUnregisterServer();\r
+//      }\r
+//\r
+//\r
+// AMovieDllRegisterServer   calls IAMovieSetup.Register(), and\r
+// AMovieDllUnregisterServer calls IAMovieSetup.Unregister().\r
+\r
+STDAPI AMovieDllRegisterServer2( BOOL );\r
+STDAPI AMovieDllRegisterServer();\r
+STDAPI AMovieDllUnregisterServer();\r
+\r
+// helper functions\r
+STDAPI EliminateSubKey( HKEY, LPCTSTR );\r
+\r
+\r
+STDAPI\r
+AMovieSetupRegisterFilter2( const AMOVIESETUP_FILTER * const psetupdata\r
+                          , IFilterMapper2 *         pIFM2\r
+                          , BOOL                             bRegister  );\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/dxmperf.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/dxmperf.h
new file mode 100644 (file)
index 0000000..54a2120
--- /dev/null
@@ -0,0 +1,250 @@
+//------------------------------------------------------------------------------\r
+// File: DXMPerf.h\r
+//\r
+// Desc: Macros for DirectShow performance logging.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef _DXMPERF_H_\r
+#define _DXMPERF_H_\r
+\r
+#include <perfstruct.h>\r
+#include "perflog.h"\r
+\r
+#ifdef _IA64_\r
+extern "C" unsigned __int64 __getReg( int whichReg );\r
+#pragma intrinsic(__getReg)\r
+#endif // _IA64_\r
+\r
+\r
+inline ULONGLONG _RDTSC( void ) {\r
+#ifdef _X86_\r
+    LARGE_INTEGER   li;\r
+    __asm {\r
+        _emit   0x0F\r
+        _emit   0x31\r
+        mov li.LowPart,eax\r
+        mov li.HighPart,edx\r
+    }\r
+    return li.QuadPart;\r
+\r
+#if 0 // This isn't tested yet\r
+\r
+#elif defined (_IA64_)\r
+\r
+#define INL_REGID_APITC 3116\r
+    return __getReg( INL_REGID_APITC );\r
+\r
+#endif // 0\r
+\r
+#else // unsupported platform\r
+    // not implemented on non x86/IA64 platforms\r
+    return 0;\r
+#endif // _X86_/_IA64_\r
+}\r
+\r
+#define DXMPERF_VIDEOREND   0x00000001\r
+#define DXMPERF_AUDIOGLITCH 0x00000002\r
+//#define GETTIME_BIT         0x00000001\r
+//#define AUDIOREND_BIT       0x00000004\r
+//#define FRAMEDROP_BIT       0x00000008\r
+#define AUDIOBREAK_BIT      0x00000010\r
+#define DXMPERF_AUDIORECV   0x00000020\r
+#define DXMPERF_AUDIOSLAVE  0x00000040\r
+#define DXMPERF_AUDIOBREAK  0x00000080\r
+\r
+#define PERFLOG_CTOR( name, iface )\r
+#define PERFLOG_DTOR( name, iface )\r
+#define PERFLOG_DELIVER( name, source, dest, sample, pmt )\r
+#define PERFLOG_RECEIVE( name, source, dest, sample, pmt )\r
+#define PERFLOG_RUN( name, iface, time, oldstate )\r
+#define PERFLOG_PAUSE( name, iface, oldstate )\r
+#define PERFLOG_STOP( name, iface, oldstate )\r
+#define PERFLOG_JOINGRAPH( name, iface, graph )\r
+#define PERFLOG_GETBUFFER( allocator, sample )\r
+#define PERFLOG_RELBUFFER( allocator, sample )\r
+#define PERFLOG_CONNECT( connector, connectee, status, pmt )\r
+#define PERFLOG_RXCONNECT( connector, connectee, status, pmt )\r
+#define PERFLOG_DISCONNECT( disconnector, disconnectee, status )\r
+\r
+#define PERFLOG_GETTIME( clock, time )    /*{ \\r
+    PERFINFO_WMI_GETTIME    perfData; \\r
+    if (NULL != g_pTraceEvent) { \\r
+        memset( &perfData, 0, sizeof( perfData ) ); \\r
+        perfData.header.Size  = sizeof( perfData ); \\r
+        perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \\r
+        perfData.header.Guid  = GUID_GETTIME; \\r
+        perfData.data.cycleCounter = _RDTSC(); \\r
+        perfData.data.dshowClock   = (ULONGLONG) (time); \\r
+        if (g_perfMasks[GETTIME_INDEX] & GETTIME_BIT) \\r
+            (*g_pTraceEvent)( g_traceHandle, (PEVENT_TRACE_HEADER) &perfData ); \\r
+        } \\r
+    }*/\r
+\r
+#define PERFLOG_AUDIOREND( clocktime, sampletime, psample, bytetime, cbytes ) /*{ \\r
+    PERFINFO_WMI_AVREND    perfData; \\r
+    if (NULL != g_pTraceEvent) { \\r
+        memset( &perfData, 0, sizeof( perfData ) ); \\r
+        perfData.header.Size  = sizeof( perfData ); \\r
+        perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \\r
+        perfData.header.Guid  = GUID_AUDIOREND; \\r
+        perfData.data.cycleCounter = _RDTSC(); \\r
+        perfData.data.dshowClock   = (clocktime); \\r
+        perfData.data.sampleTime   = (sampletime); \\r
+        if (g_perfMasks[AUDIOREND_INDEX] & AUDIOREND_BIT) \\r
+            (*g_pTraceEvent)( g_traceHandle, (PEVENT_TRACE_HEADER) &perfData ); \\r
+        } \\r
+    }*/\r
+\r
+#define PERFLOG_AUDIORECV(StreamTime,SampleStart,SampleStop,Discontinuity,Duration) \\r
+    if (PerflogEnableFlags & DXMPERF_AUDIORECV) {           \\r
+        PERFINFO_WMI_AUDIORECV  perfData;                   \\r
+        memset( &perfData, 0, sizeof( perfData ) );         \\r
+        perfData.header.Size = sizeof( perfData );          \\r
+        perfData.header.Flags = WNODE_FLAG_TRACED_GUID;     \\r
+        perfData.header.Guid        = GUID_AUDIORECV;       \\r
+        perfData.data.streamTime    = StreamTime;           \\r
+        perfData.data.sampleStart   = SampleStart;          \\r
+        perfData.data.sampleStop    = SampleStop;           \\r
+        perfData.data.discontinuity = Discontinuity;        \\r
+        perfData.data.hwduration    = Duration;             \\r
+        PerflogTraceEvent((PEVENT_TRACE_HEADER) &perfData); \\r
+    }\r
+\r
+#define PERFLOG_AUDIOSLAVE(MasterClock,SlaveClock,ErrorAccum,LastHighErrorSeen,LastLowErrorSeen) \\r
+    if (PerflogEnableFlags & DXMPERF_AUDIOSLAVE) {          \\r
+        PERFINFO_WMI_AUDIOSLAVE perfData;                   \\r
+        memset( &perfData, 0, sizeof( perfData ) );         \\r
+        perfData.header.Size = sizeof( perfData );          \\r
+        perfData.header.Flags = WNODE_FLAG_TRACED_GUID;     \\r
+        perfData.header.Guid            = GUID_AUDIOSLAVE;  \\r
+        perfData.data.masterClock       = MasterClock;      \\r
+        perfData.data.slaveClock        = SlaveClock;       \\r
+        perfData.data.errorAccum        = ErrorAccum;       \\r
+        perfData.data.lastHighErrorSeen = LastHighErrorSeen;\\r
+        perfData.data.lastLowErrorSeen  = LastLowErrorSeen; \\r
+        PerflogTraceEvent((PEVENT_TRACE_HEADER) &perfData); \\r
+    }\r
+\r
+#define PERFLOG_AUDIOADDBREAK(IterNextWrite,OffsetNextWrite,IterWrite,OffsetWrite) \\r
+    if (PerflogEnableFlags & DXMPERF_AUDIOBREAK) {              \\r
+        PERFINFO_WMI_AUDIOADDBREAK perfData;                    \\r
+        memset( &perfData, 0, sizeof( perfData ) );             \\r
+        perfData.header.Size = sizeof( perfData );              \\r
+        perfData.header.Flags = WNODE_FLAG_TRACED_GUID;         \\r
+        perfData.header.Guid            = GUID_AUDIOADDBREAK;   \\r
+        perfData.data.iterNextWrite     = IterNextWrite;        \\r
+        perfData.data.offsetNextWrite   = OffsetNextWrite;      \\r
+        perfData.data.iterWrite         = IterWrite;            \\r
+        perfData.data.offsetWrite       = OffsetWrite;          \\r
+        PerflogTraceEvent((PEVENT_TRACE_HEADER) &perfData);     \\r
+    }\r
+\r
+#define PERFLOG_VIDEOREND( sampletime, clocktime, psample ) \\r
+    if (PerflogEnableFlags & DXMPERF_VIDEOREND) { \\r
+        PERFINFO_WMI_AVREND perfData; \\r
+        memset( &perfData, 0, sizeof( perfData ) ); \\r
+        perfData.header.Size = sizeof( perfData ); \\r
+        perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \\r
+        perfData.header.Guid = GUID_VIDEOREND; \\r
+        perfData.data.cycleCounter = _RDTSC(); \\r
+        perfData.data.dshowClock = (clocktime); \\r
+        perfData.data.sampleTime = (sampletime); \\r
+        PerflogTraceEvent ((PEVENT_TRACE_HEADER) &perfData); \\r
+    }\r
+\r
+#define PERFLOG_AUDIOGLITCH( instance, glitchtype, currenttime, previoustime ) \\r
+    if (PerflogEnableFlags & DXMPERF_AUDIOGLITCH) { \\r
+        PERFINFO_WMI_AUDIOGLITCH perfData; \\r
+        memset( &perfData, 0, sizeof( perfData ) ); \\r
+        perfData.header.Size = sizeof( perfData ); \\r
+        perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \\r
+        perfData.header.Guid = GUID_DSOUNDGLITCH; \\r
+        perfData.data.cycleCounter = _RDTSC(); \\r
+        perfData.data.glitchType = (glitchtype); \\r
+        perfData.data.sampleTime = (currenttime); \\r
+        perfData.data.previousTime = (previoustime); \\r
+        perfData.data.instanceId = (instance); \\r
+        PerflogTraceEvent ((PEVENT_TRACE_HEADER) &perfData); \\r
+    }\r
+\r
+#define PERFLOG_FRAMEDROP( sampletime, clocktime, psample, renderer )    /*{ \\r
+    PERFINFO_WMI_FRAMEDROP    perfData; \\r
+    if (NULL != g_pTraceEvent) { \\r
+        memset( &perfData, 0, sizeof( perfData ) ); \\r
+        perfData.header.Size  = sizeof( perfData ); \\r
+        perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \\r
+        perfData.header.Guid  = GUID_FRAMEDROP; \\r
+        perfData.data.cycleCounter = _RDTSC(); \\r
+        perfData.data.dshowClock   = (clocktime); \\r
+        perfData.data.frameTime    = (sampletime); \\r
+        if (g_perfMasks[FRAMEDROP_INDEX] & FRAMEDROP_BIT) \\r
+            (*g_pTraceEvent)( g_traceHandle, (PEVENT_TRACE_HEADER) &perfData ); \\r
+        } \\r
+    }*/\r
+\r
+/*\r
+#define PERFLOG_AUDIOBREAK( nextwrite, writepos, msecs )    { \\r
+    PERFINFO_WMI_AUDIOBREAK    perfData; \\r
+    if (NULL != g_pTraceEvent) { \\r
+        memset( &perfData, 0, sizeof( perfData ) ); \\r
+        perfData.header.Size  = sizeof( perfData ); \\r
+        perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \\r
+        perfData.header.Guid  = GUID_AUDIOBREAK; \\r
+        perfData.data.cycleCounter   = _RDTSC(); \\r
+        perfData.data.dshowClock     = (writepos); \\r
+        perfData.data.sampleTime     = (nextwrite); \\r
+        perfData.data.sampleDuration = (msecs); \\r
+        if (g_perfMasks[AUDIOBREAK_INDEX] & AUDIOBREAK_BIT) \\r
+            (*g_pTraceEvent)( g_traceHandle, (PEVENT_TRACE_HEADER) &perfData ); \\r
+        } \\r
+    }\r
+*/\r
+\r
+#define PERFLOG_AUDIOBREAK( nextwrite, writepos, msecs )  \\r
+    if (PerflogEnableFlags & AUDIOBREAK_BIT) { \\r
+        PERFINFO_WMI_AUDIOBREAK    perfData; \\r
+        memset( &perfData, 0, sizeof( perfData ) ); \\r
+        perfData.header.Size  = sizeof( perfData ); \\r
+        perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \\r
+        perfData.header.Guid  = GUID_AUDIOBREAK; \\r
+        perfData.data.cycleCounter   = _RDTSC(); \\r
+        perfData.data.dshowClock     = (writepos); \\r
+        perfData.data.sampleTime     = (nextwrite); \\r
+        perfData.data.sampleDuration = (msecs); \\r
+        PerflogTraceEvent ((PEVENT_TRACE_HEADER) &perfData); \\r
+    } \\r
+\r
+\r
+inline\r
+VOID PERFLOG_STREAMTRACE(\r
+    ULONG Level,\r
+    ULONG Id,\r
+    ULONGLONG DShowClock,\r
+    ULONGLONG Data1,\r
+    ULONGLONG Data2,\r
+    ULONGLONG Data3,\r
+    ULONGLONG Data4\r
+    )\r
+{\r
+    if (Level <= PerflogModuleLevel)\r
+    {\r
+        PERFINFO_WMI_STREAMTRACE perfData;\r
+        memset( &perfData, 0, sizeof( perfData ) );\r
+        perfData.header.Size = sizeof( perfData );\r
+        perfData.header.Flags = WNODE_FLAG_TRACED_GUID;\r
+        perfData.header.Guid = GUID_STREAMTRACE;\r
+        perfData.data.dshowClock = DShowClock;\r
+        perfData.data.id = Id;\r
+        perfData.data.data[0] = Data1;\r
+        perfData.data.data[1] = Data2;\r
+        perfData.data.data[2] = Data3;\r
+        perfData.data.data[3] = Data4;\r
+        PerflogTraceEvent((PEVENT_TRACE_HEADER) &perfData);\r
+    }\r
+}\r
+\r
+\r
+#endif // _DXMPERF_H_\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/fourcc.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/fourcc.h
new file mode 100644 (file)
index 0000000..19c0fcd
--- /dev/null
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------\r
+// File: FourCC.h\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+// FOURCCMap\r
+//\r
+// provides a mapping between old-style multimedia format DWORDs\r
+// and new-style GUIDs.\r
+//\r
+// A range of 4 billion GUIDs has been allocated to ensure that this\r
+// mapping can be done straightforwardly one-to-one in both directions.\r
+//\r
+// January 95\r
+\r
+\r
+#ifndef __FOURCC__\r
+#define __FOURCC__\r
+\r
+\r
+// Multimedia format types are marked with DWORDs built from four 8-bit\r
+// chars and known as FOURCCs. New multimedia AM_MEDIA_TYPE definitions include\r
+// a subtype GUID. In order to simplify the mapping, GUIDs in the range:\r
+//    XXXXXXXX-0000-0010-8000-00AA00389B71\r
+// are reserved for FOURCCs.\r
+\r
+class FOURCCMap : public GUID\r
+{\r
+\r
+public:\r
+    FOURCCMap();\r
+    FOURCCMap(DWORD Fourcc);\r
+    FOURCCMap(const GUID *);\r
+\r
+\r
+    DWORD GetFOURCC(void);\r
+    void SetFOURCC(DWORD fourcc);\r
+    void SetFOURCC(const GUID *);\r
+\r
+private:\r
+    void InitGUID();\r
+};\r
+\r
+#define GUID_Data2      0\r
+#define GUID_Data3     0x10\r
+#define GUID_Data4_1   0xaa000080\r
+#define GUID_Data4_2   0x719b3800\r
+\r
+inline void\r
+FOURCCMap::InitGUID() {\r
+    Data2 = GUID_Data2;\r
+    Data3 = GUID_Data3;\r
+    ((DWORD *)Data4)[0] = GUID_Data4_1;\r
+    ((DWORD *)Data4)[1] = GUID_Data4_2;\r
+}\r
+\r
+inline\r
+FOURCCMap::FOURCCMap() {\r
+    InitGUID();\r
+    SetFOURCC( DWORD(0));\r
+}\r
+\r
+inline\r
+FOURCCMap::FOURCCMap(DWORD fourcc)\r
+{\r
+    InitGUID();\r
+    SetFOURCC(fourcc);\r
+}\r
+\r
+inline\r
+FOURCCMap::FOURCCMap(const GUID * pGuid)\r
+{\r
+    InitGUID();\r
+    SetFOURCC(pGuid);\r
+}\r
+\r
+inline void\r
+FOURCCMap::SetFOURCC(const GUID * pGuid)\r
+{\r
+    FOURCCMap * p = (FOURCCMap*) pGuid;\r
+    SetFOURCC(p->GetFOURCC());\r
+}\r
+\r
+inline void\r
+FOURCCMap::SetFOURCC(DWORD fourcc)\r
+{\r
+    Data1 = fourcc;\r
+}\r
+\r
+inline DWORD\r
+FOURCCMap::GetFOURCC(void)\r
+{\r
+    return Data1;\r
+}\r
+\r
+#endif /* __FOURCC__ */\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/measure.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/measure.h
new file mode 100644 (file)
index 0000000..a71a075
--- /dev/null
@@ -0,0 +1,222 @@
+//------------------------------------------------------------------------------\r
+// File: Measure.h\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+/*\r
+   The idea is to pepper the source code with interesting measurements and\r
+   have the last few thousand of these recorded in a circular buffer that\r
+   can be post-processed to give interesting numbers.\r
+\r
+   WHAT THE LOG LOOKS LIKE:\r
+\r
+  Time (sec)   Type        Delta  Incident_Name\r
+    0.055,41  NOTE      -.       Incident Nine  - Another note\r
+    0.055,42  NOTE      0.000,01 Incident Nine  - Another note\r
+    0.055,44  NOTE      0.000,02 Incident Nine  - Another note\r
+    0.055,45  STOP      -.       Incident Eight - Also random\r
+    0.055,47  START     -.       Incident Seven - Random\r
+    0.055,49  NOTE      0.000,05 Incident Nine  - Another note\r
+    ------- <etc.  there is a lot of this> ----------------\r
+    0.125,60  STOP      0.000,03 Msr_Stop\r
+    0.125,62  START     -.       Msr_Start\r
+    0.125,63  START     -.       Incident Two   - Start/Stop\r
+    0.125,65  STOP      0.000,03 Msr_Start\r
+    0.125,66  START     -.       Msr_Stop\r
+    0.125,68  STOP      0.000,05 Incident Two   - Start/Stop\r
+    0.125,70  STOP      0.000,04 Msr_Stop\r
+    0.125,72  START     -.       Msr_Start\r
+    0.125,73  START     -.       Incident Two   - Start/Stop\r
+    0.125,75  STOP      0.000,03 Msr_Start\r
+    0.125,77  START     -.       Msr_Stop\r
+    0.125,78  STOP      0.000,05 Incident Two   - Start/Stop\r
+    0.125,80  STOP      0.000,03 Msr_Stop\r
+    0.125,81  NOTE      -.       Incident Three - single Note\r
+    0.125,83  START     -.       Incident Four  - Start, no stop\r
+    0.125,85  START     -.       Incident Five  - Single Start/Stop\r
+    0.125,87  STOP      0.000,02 Incident Five  - Single Start/Stop\r
+\r
+Number      Average       StdDev     Smallest      Largest Incident_Name\r
+    10     0.000,58     0.000,10     0.000,55     0.000,85 Incident One   - Note\r
+    50     0.000,05     0.000,00     0.000,05     0.000,05 Incident Two   - Start/Stop\r
+     1     -.           -.           -.           -.       Incident Three - single Note\r
+     0     -.           -.           -.           -.       Incident Four  - Start, no stop\r
+     1     0.000,02     -.           0.000,02     0.000,02 Incident Five  - Single Start/Stop\r
+     0     -.           -.           -.           -.       Incident Six   - zero occurrences\r
+   100     0.000,25     0.000,12     0.000,02     0.000,62 Incident Seven - Random\r
+   100     0.000,79     0.000,48     0.000,02     0.001,92 Incident Eight - Also random\r
+  5895     0.000,01     0.000,01     0.000,01     0.000,56 Incident Nine  - Another note\r
+    10     0.000,03     0.000,00     0.000,03     0.000,04 Msr_Note\r
+    50     0.000,03     0.000,00     0.000,03     0.000,04 Msr_Start\r
+    50     0.000,04     0.000,03     0.000,03     0.000,31 Msr_Stop\r
+\r
+  WHAT IT MEANS:\r
+    The log shows what happened and when.  Each line shows the time at which\r
+    something happened (see WHAT YOU CODE below) what it was that happened\r
+    and (if approporate) the time since the corresponding previous event\r
+    (that's the delta column).\r
+\r
+    The statistics show how many times each event occurred, what the average\r
+    delta time was, also the standard deviation, largest and smalles delta.\r
+\r
+   WHAT YOU CODE:\r
+\r
+   Before anything else executes: - register your ids\r
+\r
+    int id1     = Msr_Register("Incident One   - Note");\r
+    int id2     = Msr_Register("Incident Two   - Start/Stop");\r
+    int id3     = Msr_Register("Incident Three - single Note");\r
+    etc.\r
+\r
+   At interesting moments:\r
+\r
+       // To measure a repetitive event - e.g. end of bitblt to screen\r
+       Msr_Note(Id9);             // e.g. "video frame hiting the screen NOW!"\r
+\r
+           or\r
+\r
+       // To measure an elapsed time e.g. time taken to decode an MPEG B-frame\r
+       Msr_Start(Id2);            // e.g. "Starting to decode MPEG B-frame"\r
+         . . .\r
+       MsrStop(Id2);              //      "Finished MPEG decode"\r
+\r
+   At the end:\r
+\r
+       HANDLE hFile;\r
+       hFile = CreateFile("Perf.log", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);\r
+       Msr_Dump(hFile);           // This writes the log out to the file\r
+       CloseHandle(hFile);\r
+\r
+           or\r
+\r
+       Msr_Dump(NULL);            // This writes it to DbgLog((LOG_TRACE,0, ... ));\r
+                                  // but if you are writing it out to the debugger\r
+                                  // then the times are probably all garbage because\r
+                                  // the debugger can make things run awfully slow.\r
+\r
+    A given id should be used either for start / stop or Note calls.  If Notes\r
+    are mixed in with Starts and Stops their statistics will be gibberish.\r
+\r
+    If you code the calls in upper case i.e. MSR_START(idMunge); then you get\r
+    macros which will turn into nothing unless PERF is defined.\r
+\r
+    You can reset the statistical counts for a given id by calling Reset(Id).\r
+    They are reset by default at the start.\r
+    It logs Reset as a special incident, so you can see it in the log.\r
+\r
+    The log is a circular buffer in storage (to try to minimise disk I/O).\r
+    It overwrites the oldest entries once full.  The statistics include ALL\r
+    incidents since the last Reset, whether still visible in the log or not.\r
+*/\r
+\r
+#ifndef __MEASURE__\r
+#define __MEASURE__\r
+\r
+#ifdef PERF\r
+#define MSR_INIT() Msr_Init()\r
+#define MSR_TERMINATE() Msr_Terminate()\r
+#define MSR_REGISTER(a) Msr_Register(a)\r
+#define MSR_RESET(a) Msr_Reset(a)\r
+#define MSR_CONTROL(a) Msr_Control(a)\r
+#define MSR_START(a) Msr_Start(a)\r
+#define MSR_STOP(a) Msr_Stop(a)\r
+#define MSR_NOTE(a) Msr_Note(a)\r
+#define MSR_INTEGER(a,b) Msr_Integer(a,b)\r
+#define MSR_DUMP(a) Msr_Dump(a)\r
+#define MSR_DUMPSTATS(a) Msr_DumpStats(a)\r
+#else\r
+#define MSR_INIT() ((void)0)\r
+#define MSR_TERMINATE() ((void)0)\r
+#define MSR_REGISTER(a) 0\r
+#define MSR_RESET(a) ((void)0)\r
+#define MSR_CONTROL(a) ((void)0)\r
+#define MSR_START(a) ((void)0)\r
+#define MSR_STOP(a) ((void)0)\r
+#define MSR_NOTE(a) ((void)0)\r
+#define MSR_INTEGER(a,b) ((void)0)\r
+#define MSR_DUMP(a) ((void)0)\r
+#define MSR_DUMPSTATS(a) ((void)0)\r
+#endif\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+// This must be called first - (called by the DllEntry)\r
+\r
+void WINAPI Msr_Init(void);\r
+\r
+\r
+// Call this last to clean up (or just let it fall off the end - who cares?)\r
+\r
+void WINAPI Msr_Terminate(void);\r
+\r
+\r
+// Call this to get an Id for an "incident" that you can pass to Start, Stop or Note\r
+// everything that's logged is called an "incident".\r
+\r
+int  WINAPI Msr_Register(__in LPTSTR Incident);\r
+\r
+\r
+// Reset the statistical counts for an incident\r
+\r
+void WINAPI Msr_Reset(int Id);\r
+\r
+\r
+// Reset all the counts for all incidents\r
+#define MSR_RESET_ALL 0\r
+#define MSR_PAUSE 1\r
+#define MSR_RUN 2\r
+\r
+void WINAPI Msr_Control(int iAction);\r
+\r
+\r
+// log the start of an operation\r
+\r
+void WINAPI Msr_Start(int Id);\r
+\r
+\r
+// log the end of an operation\r
+\r
+void WINAPI Msr_Stop(int Id);\r
+\r
+\r
+// log a one-off or repetitive operation\r
+\r
+void WINAPI Msr_Note(int Id);\r
+\r
+\r
+// log an integer (on which we can see statistics later)\r
+void WINAPI Msr_Integer(int Id, int n);\r
+\r
+\r
+// print out all the vaialable log (it may have wrapped) and then the statistics.\r
+// When the log wraps you lose log but the statistics are still complete.\r
+// hFIle==NULL => use DbgLog\r
+// otherwise hFile must have come from CreateFile or OpenFile.\r
+\r
+void WINAPI Msr_Dump(HANDLE hFile);\r
+\r
+\r
+// just dump the statistics - never mind the log\r
+\r
+void WINAPI Msr_DumpStats(HANDLE hFile);\r
+\r
+// Type definitions in case you want to declare a pointer to the dump functions\r
+// (makes it a trifle easier to do dynamic linking\r
+// i.e. LoadModule, GetProcAddress and call that)\r
+\r
+// Typedefs so can declare MSR_DUMPPROC *MsrDumpStats; or whatever\r
+typedef void WINAPI MSR_DUMPPROC(HANDLE hFile);\r
+typedef void WINAPI MSR_CONTROLPROC(int iAction);\r
+\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+#endif // __MEASURE__\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/msgthrd.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/msgthrd.h
new file mode 100644 (file)
index 0000000..45adc01
--- /dev/null
@@ -0,0 +1,120 @@
+//------------------------------------------------------------------------------\r
+// File: MsgThrd.h\r
+//\r
+// Desc: DirectShow base classes - provides support for a worker thread \r
+//       class to which one can asynchronously post messages.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+// Message class - really just a structure.\r
+//\r
+class CMsg {\r
+public:\r
+    UINT uMsg;\r
+    DWORD dwFlags;\r
+    LPVOID lpParam;\r
+    CAMEvent *pEvent;\r
+\r
+    CMsg(UINT u, DWORD dw, __inout_opt LPVOID lp, __in_opt CAMEvent *pEvnt)\r
+        : uMsg(u), dwFlags(dw), lpParam(lp), pEvent(pEvnt) {}\r
+\r
+    CMsg()\r
+        : uMsg(0), dwFlags(0L), lpParam(NULL), pEvent(NULL) {}\r
+};\r
+\r
+// This is the actual thread class.  It exports all the usual thread control\r
+// functions.  The created thread is different from a normal WIN32 thread in\r
+// that it is prompted to perform particaular tasks by responding to messages\r
+// posted to its message queue.\r
+//\r
+class AM_NOVTABLE CMsgThread {\r
+private:\r
+    static DWORD WINAPI DefaultThreadProc(__inout LPVOID lpParam);\r
+    DWORD               m_ThreadId;\r
+    HANDLE              m_hThread;\r
+\r
+protected:\r
+\r
+    // if you want to override GetThreadMsg to block on other things\r
+    // as well as this queue, you need access to this\r
+    CGenericList<CMsg>        m_ThreadQueue;\r
+    CCritSec                  m_Lock;\r
+    HANDLE                    m_hSem;\r
+    LONG                      m_lWaiting;\r
+\r
+public:\r
+    CMsgThread()\r
+        : m_ThreadId(0),\r
+        m_hThread(NULL),\r
+        m_lWaiting(0),\r
+        m_hSem(NULL),\r
+        // make a list with a cache of 5 items\r
+        m_ThreadQueue(NAME("MsgThread list"), 5)\r
+        {\r
+        }\r
+\r
+    ~CMsgThread();\r
+    // override this if you want to block on other things as well\r
+    // as the message loop\r
+    void virtual GetThreadMsg(__out CMsg *msg);\r
+\r
+    // override this if you want to do something on thread startup\r
+    virtual void OnThreadInit() {\r
+    };\r
+\r
+    BOOL CreateThread();\r
+\r
+    BOOL WaitForThreadExit(__out LPDWORD lpdwExitCode) {\r
+        if (m_hThread != NULL) {\r
+            WaitForSingleObject(m_hThread, INFINITE);\r
+            return GetExitCodeThread(m_hThread, lpdwExitCode);\r
+        }\r
+        return FALSE;\r
+    }\r
+\r
+    DWORD ResumeThread() {\r
+        return ::ResumeThread(m_hThread);\r
+    }\r
+\r
+    DWORD SuspendThread() {\r
+        return ::SuspendThread(m_hThread);\r
+    }\r
+\r
+    int GetThreadPriority() {\r
+        return ::GetThreadPriority(m_hThread);\r
+    }\r
+\r
+    BOOL SetThreadPriority(int nPriority) {\r
+        return ::SetThreadPriority(m_hThread, nPriority);\r
+    }\r
+\r
+    HANDLE GetThreadHandle() {\r
+        return m_hThread;\r
+    }\r
+\r
+    DWORD GetThreadId() {\r
+        return m_ThreadId;\r
+    }\r
+\r
+\r
+    void PutThreadMsg(UINT uMsg, DWORD dwMsgFlags,\r
+                      __in_opt LPVOID lpMsgParam, __in_opt CAMEvent *pEvent = NULL) {\r
+        CAutoLock lck(&m_Lock);\r
+        CMsg* pMsg = new CMsg(uMsg, dwMsgFlags, lpMsgParam, pEvent);\r
+        m_ThreadQueue.AddTail(pMsg);\r
+        if (m_lWaiting != 0) {\r
+            ReleaseSemaphore(m_hSem, m_lWaiting, 0);\r
+            m_lWaiting = 0;\r
+        }\r
+    }\r
+\r
+    // This is the function prototype of the function that the client\r
+    // supplies.  It is always called on the created thread, never on\r
+    // the creator thread.\r
+    //\r
+    virtual LRESULT ThreadMessageProc(\r
+        UINT uMsg, DWORD dwFlags, __inout_opt LPVOID lpParam, __in_opt CAMEvent *pEvent) = 0;\r
+};\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/mtype.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/mtype.cpp
new file mode 100644 (file)
index 0000000..fffbcf7
--- /dev/null
@@ -0,0 +1,478 @@
+//------------------------------------------------------------------------------\r
+// File: MType.cpp\r
+//\r
+// Desc: DirectShow base classes - implements a class that holds and \r
+//       manages media type information.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+// helper class that derived pin objects can use to compare media\r
+// types etc. Has same data members as the struct AM_MEDIA_TYPE defined\r
+// in the streams IDL file, but also has (non-virtual) functions\r
+\r
+#include <streams.h>\r
+#include <mmreg.h>\r
+\r
+CMediaType::~CMediaType(){\r
+    FreeMediaType(*this);\r
+}\r
+\r
+\r
+CMediaType::CMediaType()\r
+{\r
+    InitMediaType();\r
+}\r
+\r
+\r
+CMediaType::CMediaType(const GUID * type)\r
+{\r
+    InitMediaType();\r
+    majortype = *type;\r
+}\r
+\r
+\r
+// copy constructor does a deep copy of the format block\r
+\r
+CMediaType::CMediaType(const AM_MEDIA_TYPE& rt, __out_opt HRESULT* phr)\r
+{\r
+    HRESULT hr = CopyMediaType(this, &rt);\r
+    if (FAILED(hr) && (NULL != phr)) {\r
+        *phr = hr;\r
+    }\r
+}\r
+\r
+\r
+CMediaType::CMediaType(const CMediaType& rt, __out_opt HRESULT* phr)\r
+{\r
+    HRESULT hr = CopyMediaType(this, &rt);\r
+    if (FAILED(hr) && (NULL != phr)) {\r
+        *phr = hr;\r
+    }\r
+}\r
+\r
+\r
+// this class inherits publicly from AM_MEDIA_TYPE so the compiler could generate\r
+// the following assignment operator itself, however it could introduce some\r
+// memory conflicts and leaks in the process because the structure contains\r
+// a dynamically allocated block (pbFormat) which it will not copy correctly\r
+\r
+CMediaType&\r
+CMediaType::operator=(const AM_MEDIA_TYPE& rt)\r
+{\r
+    Set(rt);\r
+    return *this;\r
+}\r
+\r
+CMediaType&\r
+CMediaType::operator=(const CMediaType& rt)\r
+{\r
+    *this = (AM_MEDIA_TYPE &) rt;\r
+    return *this;\r
+}\r
+\r
+BOOL\r
+CMediaType::operator == (const CMediaType& rt) const\r
+{\r
+    // I don't believe we need to check sample size or\r
+    // temporal compression flags, since I think these must\r
+    // be represented in the type, subtype and format somehow. They\r
+    // are pulled out as separate flags so that people who don't understand\r
+    // the particular format representation can still see them, but\r
+    // they should duplicate information in the format block.\r
+\r
+    return ((IsEqualGUID(majortype,rt.majortype) == TRUE) &&\r
+        (IsEqualGUID(subtype,rt.subtype) == TRUE) &&\r
+        (IsEqualGUID(formattype,rt.formattype) == TRUE) &&\r
+        (cbFormat == rt.cbFormat) &&\r
+        ( (cbFormat == 0) ||\r
+          pbFormat != NULL && rt.pbFormat != NULL &&\r
+          (memcmp(pbFormat, rt.pbFormat, cbFormat) == 0)));\r
+}\r
+\r
+\r
+BOOL\r
+CMediaType::operator != (const CMediaType& rt) const\r
+{\r
+    /* Check to see if they are equal */\r
+\r
+    if (*this == rt) {\r
+        return FALSE;\r
+    }\r
+    return TRUE;\r
+}\r
+\r
+\r
+HRESULT\r
+CMediaType::Set(const CMediaType& rt)\r
+{\r
+    return Set((AM_MEDIA_TYPE &) rt);\r
+}\r
+\r
+\r
+HRESULT\r
+CMediaType::Set(const AM_MEDIA_TYPE& rt)\r
+{\r
+    if (&rt != this) {\r
+        FreeMediaType(*this);\r
+        HRESULT hr = CopyMediaType(this, &rt);\r
+        if (FAILED(hr)) {\r
+            return E_OUTOFMEMORY;\r
+        }\r
+    }\r
+\r
+    return S_OK;    \r
+}\r
+\r
+\r
+BOOL\r
+CMediaType::IsValid() const\r
+{\r
+    return (!IsEqualGUID(majortype,GUID_NULL));\r
+}\r
+\r
+\r
+void\r
+CMediaType::SetType(const GUID* ptype)\r
+{\r
+    majortype = *ptype;\r
+}\r
+\r
+\r
+void\r
+CMediaType::SetSubtype(const GUID* ptype)\r
+{\r
+    subtype = *ptype;\r
+}\r
+\r
+\r
+ULONG\r
+CMediaType::GetSampleSize() const {\r
+    if (IsFixedSize()) {\r
+        return lSampleSize;\r
+    } else {\r
+        return 0;\r
+    }\r
+}\r
+\r
+\r
+void\r
+CMediaType::SetSampleSize(ULONG sz) {\r
+    if (sz == 0) {\r
+        SetVariableSize();\r
+    } else {\r
+        bFixedSizeSamples = TRUE;\r
+        lSampleSize = sz;\r
+    }\r
+}\r
+\r
+\r
+void\r
+CMediaType::SetVariableSize() {\r
+    bFixedSizeSamples = FALSE;\r
+}\r
+\r
+\r
+void\r
+CMediaType::SetTemporalCompression(BOOL bCompressed) {\r
+    bTemporalCompression = bCompressed;\r
+}\r
+\r
+BOOL\r
+CMediaType::SetFormat(__in_bcount(cb) BYTE * pformat, ULONG cb)\r
+{\r
+    if (NULL == AllocFormatBuffer(cb))\r
+       return(FALSE);\r
+\r
+    ASSERT(pbFormat);\r
+    memcpy(pbFormat, pformat, cb);\r
+    return(TRUE);\r
+}\r
+\r
+\r
+// set the type of the media type format block, this type defines what you\r
+// will actually find in the format pointer. For example FORMAT_VideoInfo or\r
+// FORMAT_WaveFormatEx. In the future this may be an interface pointer to a\r
+// property set. Before sending out media types this should be filled in.\r
+\r
+void\r
+CMediaType::SetFormatType(const GUID *pformattype)\r
+{\r
+    formattype = *pformattype;\r
+}\r
+\r
+\r
+// reset the format buffer\r
+\r
+void CMediaType::ResetFormatBuffer()\r
+{\r
+    if (cbFormat) {\r
+        CoTaskMemFree((PVOID)pbFormat);\r
+    }\r
+    cbFormat = 0;\r
+    pbFormat = NULL;\r
+}\r
+\r
+\r
+// allocate length bytes for the format and return a read/write pointer\r
+// If we cannot allocate the new block of memory we return NULL leaving\r
+// the original block of memory untouched (as does ReallocFormatBuffer)\r
+\r
+BYTE*\r
+CMediaType::AllocFormatBuffer(ULONG length)\r
+{\r
+    ASSERT(length);\r
+\r
+    // do the types have the same buffer size\r
+\r
+    if (cbFormat == length) {\r
+        return pbFormat;\r
+    }\r
+\r
+    // allocate the new format buffer\r
+\r
+    BYTE *pNewFormat = (PBYTE)CoTaskMemAlloc(length);\r
+    if (pNewFormat == NULL) {\r
+        if (length <= cbFormat) return pbFormat; //reuse the old block anyway.\r
+        return NULL;\r
+    }\r
+\r
+    // delete the old format\r
+\r
+    if (cbFormat != 0) {\r
+        ASSERT(pbFormat);\r
+        CoTaskMemFree((PVOID)pbFormat);\r
+    }\r
+\r
+    cbFormat = length;\r
+    pbFormat = pNewFormat;\r
+    return pbFormat;\r
+}\r
+\r
+\r
+// reallocate length bytes for the format and return a read/write pointer\r
+// to it. We keep as much information as we can given the new buffer size\r
+// if this fails the original format buffer is left untouched. The caller\r
+// is responsible for ensuring the size of memory required is non zero\r
+\r
+BYTE*\r
+CMediaType::ReallocFormatBuffer(ULONG length)\r
+{\r
+    ASSERT(length);\r
+\r
+    // do the types have the same buffer size\r
+\r
+    if (cbFormat == length) {\r
+        return pbFormat;\r
+    }\r
+\r
+    // allocate the new format buffer\r
+\r
+    BYTE *pNewFormat = (PBYTE)CoTaskMemAlloc(length);\r
+    if (pNewFormat == NULL) {\r
+        if (length <= cbFormat) return pbFormat; //reuse the old block anyway.\r
+        return NULL;\r
+    }\r
+\r
+    // copy any previous format (or part of if new is smaller)\r
+    // delete the old format and replace with the new one\r
+\r
+    if (cbFormat != 0) {\r
+        ASSERT(pbFormat);\r
+        memcpy(pNewFormat,pbFormat,min(length,cbFormat));\r
+        CoTaskMemFree((PVOID)pbFormat);\r
+    }\r
+\r
+    cbFormat = length;\r
+    pbFormat = pNewFormat;\r
+    return pNewFormat;\r
+}\r
+\r
+// initialise a media type structure\r
+\r
+void CMediaType::InitMediaType()\r
+{\r
+    ZeroMemory((PVOID)this, sizeof(*this));\r
+    lSampleSize = 1;\r
+    bFixedSizeSamples = TRUE;\r
+}\r
+\r
+\r
+// a partially specified media type can be passed to IPin::Connect\r
+// as a constraint on the media type used in the connection.\r
+// the type, subtype or format type can be null.\r
+BOOL\r
+CMediaType::IsPartiallySpecified(void) const\r
+{\r
+    if ((majortype == GUID_NULL) ||\r
+        (formattype == GUID_NULL)) {\r
+            return TRUE;\r
+    } else {\r
+        return FALSE;\r
+    }\r
+}\r
+\r
+BOOL\r
+CMediaType::MatchesPartial(const CMediaType* ppartial) const\r
+{\r
+    if ((ppartial->majortype != GUID_NULL) &&\r
+        (majortype != ppartial->majortype)) {\r
+            return FALSE;\r
+    }\r
+    if ((ppartial->subtype != GUID_NULL) &&\r
+        (subtype != ppartial->subtype)) {\r
+            return FALSE;\r
+    }\r
+\r
+    if (ppartial->formattype != GUID_NULL) {\r
+        // if the format block is specified then it must match exactly\r
+        if (formattype != ppartial->formattype) {\r
+            return FALSE;\r
+        }\r
+        if (cbFormat != ppartial->cbFormat) {\r
+            return FALSE;\r
+        }\r
+        if ((cbFormat != 0) &&\r
+            (memcmp(pbFormat, ppartial->pbFormat, cbFormat) != 0)) {\r
+                return FALSE;\r
+        }\r
+    }\r
+\r
+    return TRUE;\r
+\r
+}\r
+\r
+\r
+\r
+// general purpose function to delete a heap allocated AM_MEDIA_TYPE structure\r
+// which is useful when calling IEnumMediaTypes::Next as the interface\r
+// implementation allocates the structures which you must later delete\r
+// the format block may also be a pointer to an interface to release\r
+\r
+void WINAPI DeleteMediaType(__inout_opt AM_MEDIA_TYPE *pmt)\r
+{\r
+    // allow NULL pointers for coding simplicity\r
+\r
+    if (pmt == NULL) {\r
+        return;\r
+    }\r
+\r
+    FreeMediaType(*pmt);\r
+    CoTaskMemFree((PVOID)pmt);\r
+}\r
+\r
+\r
+// this also comes in useful when using the IEnumMediaTypes interface so\r
+// that you can copy a media type, you can do nearly the same by creating\r
+// a CMediaType object but as soon as it goes out of scope the destructor\r
+// will delete the memory it allocated (this takes a copy of the memory)\r
+\r
+AM_MEDIA_TYPE * WINAPI CreateMediaType(AM_MEDIA_TYPE const *pSrc)\r
+{\r
+    ASSERT(pSrc);\r
+\r
+    // Allocate a block of memory for the media type\r
+\r
+    AM_MEDIA_TYPE *pMediaType =\r
+        (AM_MEDIA_TYPE *)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));\r
+\r
+    if (pMediaType == NULL) {\r
+        return NULL;\r
+    }\r
+    // Copy the variable length format block\r
+\r
+    HRESULT hr = CopyMediaType(pMediaType,pSrc);\r
+    if (FAILED(hr)) {\r
+        CoTaskMemFree((PVOID)pMediaType);\r
+        return NULL;\r
+    }\r
+\r
+    return pMediaType;\r
+}\r
+\r
+\r
+//  Copy 1 media type to another\r
+\r
+HRESULT WINAPI CopyMediaType(__out AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource)\r
+{\r
+    //  We'll leak if we copy onto one that already exists - there's one\r
+    //  case we can check like that - copying to itself.\r
+    ASSERT(pmtSource != pmtTarget);\r
+    *pmtTarget = *pmtSource;\r
+    if (pmtSource->cbFormat != 0) {\r
+        ASSERT(pmtSource->pbFormat != NULL);\r
+        pmtTarget->pbFormat = (PBYTE)CoTaskMemAlloc(pmtSource->cbFormat);\r
+        if (pmtTarget->pbFormat == NULL) {\r
+            pmtTarget->cbFormat = 0;\r
+            return E_OUTOFMEMORY;\r
+        } else {\r
+            CopyMemory((PVOID)pmtTarget->pbFormat, (PVOID)pmtSource->pbFormat,\r
+                       pmtTarget->cbFormat);\r
+        }\r
+    }\r
+    if (pmtTarget->pUnk != NULL) {\r
+        pmtTarget->pUnk->AddRef();\r
+    }\r
+\r
+    return S_OK;\r
+}\r
+\r
+//  Free an existing media type (ie free resources it holds)\r
+\r
+void WINAPI FreeMediaType(__inout AM_MEDIA_TYPE& mt)\r
+{\r
+    if (mt.cbFormat != 0) {\r
+        CoTaskMemFree((PVOID)mt.pbFormat);\r
+\r
+        // Strictly unnecessary but tidier\r
+        mt.cbFormat = 0;\r
+        mt.pbFormat = NULL;\r
+    }\r
+    if (mt.pUnk != NULL) {\r
+        mt.pUnk->Release();\r
+        mt.pUnk = NULL;\r
+    }\r
+}\r
+\r
+//  Initialize a media type from a WAVEFORMATEX\r
+\r
+STDAPI CreateAudioMediaType(\r
+    const WAVEFORMATEX *pwfx,\r
+    __out AM_MEDIA_TYPE *pmt,\r
+    BOOL bSetFormat\r
+)\r
+{\r
+    pmt->majortype            = MEDIATYPE_Audio;\r
+    if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {\r
+        pmt->subtype = ((PWAVEFORMATEXTENSIBLE)pwfx)->SubFormat;\r
+    } else {\r
+        pmt->subtype              = FOURCCMap(pwfx->wFormatTag);\r
+    }\r
+    pmt->formattype           = FORMAT_WaveFormatEx;\r
+    pmt->bFixedSizeSamples    = TRUE;\r
+    pmt->bTemporalCompression = FALSE;\r
+    pmt->lSampleSize          = pwfx->nBlockAlign;\r
+    pmt->pUnk                 = NULL;\r
+    if (bSetFormat) {\r
+        if (pwfx->wFormatTag == WAVE_FORMAT_PCM) {\r
+            pmt->cbFormat         = sizeof(WAVEFORMATEX);\r
+        } else {\r
+            pmt->cbFormat         = sizeof(WAVEFORMATEX) + pwfx->cbSize;\r
+        }\r
+        pmt->pbFormat             = (PBYTE)CoTaskMemAlloc(pmt->cbFormat);\r
+        if (pmt->pbFormat == NULL) {\r
+            return E_OUTOFMEMORY;\r
+        }\r
+        if (pwfx->wFormatTag == WAVE_FORMAT_PCM) {\r
+            CopyMemory(pmt->pbFormat, pwfx, sizeof(PCMWAVEFORMAT));\r
+            ((WAVEFORMATEX *)pmt->pbFormat)->cbSize = 0;\r
+        } else {\r
+            CopyMemory(pmt->pbFormat, pwfx, pmt->cbFormat);\r
+        }\r
+    }\r
+    return S_OK;\r
+}\r
+\r
+// eliminate very many spurious warnings from MS compiler\r
+#pragma warning(disable:4514)\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/mtype.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/mtype.h
new file mode 100644 (file)
index 0000000..fc2fe53
--- /dev/null
@@ -0,0 +1,89 @@
+//------------------------------------------------------------------------------\r
+// File: MtType.h\r
+//\r
+// Desc: DirectShow base classes - defines a class that holds and manages\r
+//       media type information.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef __MTYPE__\r
+#define __MTYPE__\r
+\r
+/* Helper class that derived pin objects can use to compare media\r
+   types etc. Has same data members as the struct AM_MEDIA_TYPE defined\r
+   in the streams IDL file, but also has (non-virtual) functions */\r
+\r
+class CMediaType : public _AMMediaType {\r
+\r
+public:\r
+\r
+    ~CMediaType();\r
+    CMediaType();\r
+    CMediaType(const GUID * majortype);\r
+    CMediaType(const AM_MEDIA_TYPE&, __out_opt HRESULT* phr = NULL);\r
+    CMediaType(const CMediaType&, __out_opt HRESULT* phr = NULL);\r
+\r
+    CMediaType& operator=(const CMediaType&);\r
+    CMediaType& operator=(const AM_MEDIA_TYPE&);\r
+\r
+    BOOL operator == (const CMediaType&) const;\r
+    BOOL operator != (const CMediaType&) const;\r
+\r
+    HRESULT Set(const CMediaType& rt);\r
+    HRESULT Set(const AM_MEDIA_TYPE& rt);\r
+\r
+    BOOL IsValid() const;\r
+\r
+    const GUID *Type() const { return &majortype;} ;\r
+    void SetType(const GUID *);\r
+    const GUID *Subtype() const { return &subtype;} ;\r
+    void SetSubtype(const GUID *);\r
+\r
+    BOOL IsFixedSize() const {return bFixedSizeSamples; };\r
+    BOOL IsTemporalCompressed() const {return bTemporalCompression; };\r
+    ULONG GetSampleSize() const;\r
+\r
+    void SetSampleSize(ULONG sz);\r
+    void SetVariableSize();\r
+    void SetTemporalCompression(BOOL bCompressed);\r
+\r
+    // read/write pointer to format - can't change length without\r
+    // calling SetFormat, AllocFormatBuffer or ReallocFormatBuffer\r
+\r
+    BYTE*   Format() const {return pbFormat; };\r
+    ULONG   FormatLength() const { return cbFormat; };\r
+\r
+    void SetFormatType(const GUID *);\r
+    const GUID *FormatType() const {return &formattype; };\r
+    BOOL SetFormat(__in_bcount(length) BYTE *pFormat, ULONG length);\r
+    void ResetFormatBuffer();\r
+    BYTE* AllocFormatBuffer(ULONG length);\r
+    BYTE* ReallocFormatBuffer(ULONG length);\r
+\r
+    void InitMediaType();\r
+\r
+    BOOL MatchesPartial(const CMediaType* ppartial) const;\r
+    BOOL IsPartiallySpecified(void) const;\r
+};\r
+\r
+\r
+/* General purpose functions to copy and delete a task allocated AM_MEDIA_TYPE\r
+   structure which is useful when using the IEnumMediaFormats interface as\r
+   the implementation allocates the structures which you must later delete */\r
+\r
+void WINAPI DeleteMediaType(__inout_opt AM_MEDIA_TYPE *pmt);\r
+AM_MEDIA_TYPE * WINAPI CreateMediaType(AM_MEDIA_TYPE const *pSrc);\r
+HRESULT WINAPI CopyMediaType(__out AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource);\r
+void WINAPI FreeMediaType(__inout AM_MEDIA_TYPE& mt);\r
+\r
+//  Initialize a media type from a WAVEFORMATEX\r
+\r
+STDAPI CreateAudioMediaType(\r
+    const WAVEFORMATEX *pwfx,\r
+    __out AM_MEDIA_TYPE *pmt,\r
+    BOOL bSetFormat);\r
+\r
+#endif /* __MTYPE__ */\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/outputq.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/outputq.cpp
new file mode 100644 (file)
index 0000000..d3ab617
--- /dev/null
@@ -0,0 +1,801 @@
+//------------------------------------------------------------------------------\r
+// File: OutputQ.cpp\r
+//\r
+// Desc: DirectShow base classes - implements COutputQueue class used by an\r
+//       output pin which may sometimes want to queue output samples on a\r
+//       separate thread and sometimes call Receive() directly on the input\r
+//       pin.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+\r
+\r
+//\r
+//  COutputQueue Constructor :\r
+//\r
+//  Determines if a thread is to be created and creates resources\r
+//\r
+//     pInputPin  - the downstream input pin we're queueing samples to\r
+//\r
+//     phr        - changed to a failure code if this function fails\r
+//                  (otherwise unchanges)\r
+//\r
+//     bAuto      - Ask pInputPin if it can block in Receive by calling\r
+//                  its ReceiveCanBlock method and create a thread if\r
+//                  it can block, otherwise not.\r
+//\r
+//     bQueue     - if bAuto == FALSE then we create a thread if and only\r
+//                  if bQueue == TRUE\r
+//\r
+//     lBatchSize - work in batches of lBatchSize\r
+//\r
+//     bBatchEact - Use exact batch sizes so don't send until the\r
+//                  batch is full or SendAnyway() is called\r
+//\r
+//     lListSize  - If we create a thread make the list of samples queued\r
+//                  to the thread have this size cache\r
+//\r
+//     dwPriority - If we create a thread set its priority to this\r
+//\r
+COutputQueue::COutputQueue(\r
+             IPin         *pInputPin,          //  Pin to send stuff to\r
+             __inout HRESULT      *phr,        //  'Return code'\r
+             BOOL          bAuto,              //  Ask pin if queue or not\r
+             BOOL          bQueue,             //  Send through queue\r
+             LONG          lBatchSize,         //  Batch\r
+             BOOL          bBatchExact,        //  Batch exactly to BatchSize\r
+             LONG          lListSize,\r
+             DWORD         dwPriority,\r
+             bool          bFlushingOpt        // flushing optimization\r
+            ) : m_lBatchSize(lBatchSize),\r
+                m_bBatchExact(bBatchExact && (lBatchSize > 1)),\r
+                m_hThread(NULL),\r
+                m_hSem(NULL),\r
+                m_List(NULL),\r
+                m_pPin(pInputPin),\r
+                m_ppSamples(NULL),\r
+                m_lWaiting(0),\r
+                m_evFlushComplete(FALSE, phr),\r
+                m_pInputPin(NULL),\r
+                m_bSendAnyway(FALSE),\r
+                m_nBatched(0),\r
+                m_bFlushing(FALSE),\r
+                m_bFlushed(TRUE),\r
+                m_bFlushingOpt(bFlushingOpt),\r
+                m_bTerminate(FALSE),\r
+                m_hEventPop(NULL),\r
+                m_hr(S_OK)\r
+{\r
+    ASSERT(m_lBatchSize > 0);\r
+\r
+\r
+    if (FAILED(*phr)) {\r
+        return;\r
+    }\r
+\r
+    //  Check the input pin is OK and cache its IMemInputPin interface\r
+\r
+    *phr = pInputPin->QueryInterface(IID_IMemInputPin, (void **)&m_pInputPin);\r
+    if (FAILED(*phr)) {\r
+        return;\r
+    }\r
+\r
+    // See if we should ask the downstream pin\r
+\r
+    if (bAuto) {\r
+        HRESULT hr = m_pInputPin->ReceiveCanBlock();\r
+        if (SUCCEEDED(hr)) {\r
+            bQueue = hr == S_OK;\r
+        }\r
+    }\r
+\r
+    //  Create our sample batch\r
+\r
+    m_ppSamples = new PMEDIASAMPLE[m_lBatchSize];\r
+    if (m_ppSamples == NULL) {\r
+        *phr = E_OUTOFMEMORY;\r
+        return;\r
+    }\r
+\r
+    //  If we're queueing allocate resources\r
+\r
+    if (bQueue) {\r
+        DbgLog((LOG_TRACE, 2, TEXT("Creating thread for output pin")));\r
+        m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);\r
+        if (m_hSem == NULL) {\r
+            DWORD dwError = GetLastError();\r
+            *phr = AmHresultFromWin32(dwError);\r
+            return;\r
+        }\r
+        m_List = new CSampleList(NAME("Sample Queue List"),\r
+                                 lListSize,\r
+                                 FALSE         // No lock\r
+                                );\r
+        if (m_List == NULL) {\r
+            *phr = E_OUTOFMEMORY;\r
+            return;\r
+        }\r
+\r
+\r
+        DWORD dwThreadId;\r
+        m_hThread = CreateThread(NULL,\r
+                                 0,\r
+                                 InitialThreadProc,\r
+                                 (LPVOID)this,\r
+                                 0,\r
+                                 &dwThreadId);\r
+        if (m_hThread == NULL) {\r
+            DWORD dwError = GetLastError();\r
+            *phr = AmHresultFromWin32(dwError);\r
+            return;\r
+        }\r
+        SetThreadPriority(m_hThread, dwPriority);\r
+    } else {\r
+        DbgLog((LOG_TRACE, 2, TEXT("Calling input pin directly - no thread")));\r
+    }\r
+}\r
+\r
+//\r
+//  COutputQueuee Destructor :\r
+//\r
+//  Free all resources -\r
+//\r
+//      Thread,\r
+//      Batched samples\r
+//\r
+COutputQueue::~COutputQueue()\r
+{\r
+    DbgLog((LOG_TRACE, 3, TEXT("COutputQueue::~COutputQueue")));\r
+    /*  Free our pointer */\r
+    if (m_pInputPin != NULL) {\r
+        m_pInputPin->Release();\r
+    }\r
+    if (m_hThread != NULL) {\r
+        {\r
+            CAutoLock lck(this);\r
+            m_bTerminate = TRUE;\r
+            m_hr = S_FALSE;\r
+            NotifyThread();\r
+        }\r
+        DbgWaitForSingleObject(m_hThread);\r
+        EXECUTE_ASSERT(CloseHandle(m_hThread));\r
+\r
+        //  The thread frees the samples when asked to terminate\r
+\r
+        ASSERT(m_List->GetCount() == 0);\r
+        delete m_List;\r
+    } else {\r
+        FreeSamples();\r
+    }\r
+    if (m_hSem != NULL) {\r
+        EXECUTE_ASSERT(CloseHandle(m_hSem));\r
+    }\r
+    delete [] m_ppSamples;\r
+}\r
+\r
+//\r
+//  Call the real thread proc as a member function\r
+//\r
+DWORD WINAPI COutputQueue::InitialThreadProc(__in LPVOID pv)\r
+{\r
+    HRESULT hrCoInit = CAMThread::CoInitializeHelper();\r
+    \r
+    COutputQueue *pSampleQueue = (COutputQueue *)pv;\r
+    DWORD dwReturn = pSampleQueue->ThreadProc();\r
+\r
+    if(hrCoInit == S_OK) {\r
+        CoUninitialize();\r
+    }\r
+    \r
+    return dwReturn;\r
+}\r
+\r
+//\r
+//  Thread sending the samples downstream :\r
+//\r
+//  When there is nothing to do the thread sets m_lWaiting (while\r
+//  holding the critical section) and then waits for m_hSem to be\r
+//  set (not holding the critical section)\r
+//\r
+DWORD COutputQueue::ThreadProc()\r
+{\r
+    while (TRUE) {\r
+        BOOL          bWait = FALSE;\r
+        IMediaSample *pSample;\r
+        LONG          lNumberToSend; // Local copy\r
+        NewSegmentPacket* ppacket;\r
+\r
+        //\r
+        //  Get a batch of samples and send it if possible\r
+        //  In any case exit the loop if there is a control action\r
+        //  requested\r
+        //\r
+        {\r
+            CAutoLock lck(this);\r
+            while (TRUE) {\r
+\r
+                if (m_bTerminate) {\r
+                    FreeSamples();\r
+                    return 0;\r
+                }\r
+                if (m_bFlushing) {\r
+                    FreeSamples();\r
+                    SetEvent(m_evFlushComplete);\r
+                }\r
+\r
+                //  Get a sample off the list\r
+\r
+                pSample = m_List->RemoveHead();\r
+               // inform derived class we took something off the queue\r
+               if (m_hEventPop) {\r
+                    //DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered  SET EVENT")));\r
+                   SetEvent(m_hEventPop);\r
+               }\r
+\r
+                if (pSample != NULL &&\r
+                    !IsSpecialSample(pSample)) {\r
+\r
+                    //  If its just a regular sample just add it to the batch\r
+                    //  and exit the loop if the batch is full\r
+\r
+                    m_ppSamples[m_nBatched++] = pSample;\r
+                    if (m_nBatched == m_lBatchSize) {\r
+                        break;\r
+                    }\r
+                } else {\r
+\r
+                    //  If there was nothing in the queue and there's nothing\r
+                    //  to send (either because there's nothing or the batch\r
+                    //  isn't full) then prepare to wait\r
+\r
+                    if (pSample == NULL &&\r
+                        (m_bBatchExact || m_nBatched == 0)) {\r
+\r
+                        //  Tell other thread to set the event when there's\r
+                        //  something do to\r
+\r
+                        ASSERT(m_lWaiting == 0);\r
+                        m_lWaiting++;\r
+                        bWait      = TRUE;\r
+                    } else {\r
+\r
+                        //  We break out of the loop on SEND_PACKET unless\r
+                        //  there's nothing to send\r
+\r
+                        if (pSample == SEND_PACKET && m_nBatched == 0) {\r
+                            continue;\r
+                        }\r
+\r
+                        if (pSample == NEW_SEGMENT) {\r
+                            // now we need the parameters - we are\r
+                            // guaranteed that the next packet contains them\r
+                            ppacket = (NewSegmentPacket *) m_List->RemoveHead();\r
+                           // we took something off the queue\r
+                           if (m_hEventPop) {\r
+                               //DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered  SET EVENT")));\r
+                               SetEvent(m_hEventPop);\r
+                           }\r
+\r
+                            ASSERT(ppacket);\r
+                        }\r
+                        //  EOS_PACKET falls through here and we exit the loop\r
+                        //  In this way it acts like SEND_PACKET\r
+                    }\r
+                    break;\r
+                }\r
+            }\r
+            if (!bWait) {\r
+                // We look at m_nBatched from the client side so keep\r
+                // it up to date inside the critical section\r
+                lNumberToSend = m_nBatched;  // Local copy\r
+                m_nBatched = 0;\r
+            }\r
+        }\r
+\r
+        //  Wait for some more data\r
+\r
+        if (bWait) {\r
+            DbgWaitForSingleObject(m_hSem);\r
+            continue;\r
+        }\r
+\r
+\r
+\r
+        //  OK - send it if there's anything to send\r
+        //  We DON'T check m_bBatchExact here because either we've got\r
+        //  a full batch or we dropped through because we got\r
+        //  SEND_PACKET or EOS_PACKET - both of which imply we should\r
+        //  flush our batch\r
+\r
+        if (lNumberToSend != 0) {\r
+            long nProcessed;\r
+            if (m_hr == S_OK) {\r
+                ASSERT(!m_bFlushed);\r
+                HRESULT hr = m_pInputPin->ReceiveMultiple(m_ppSamples,\r
+                                                          lNumberToSend,\r
+                                                          &nProcessed);\r
+                /*  Don't overwrite a flushing state HRESULT */\r
+                CAutoLock lck(this);\r
+                if (m_hr == S_OK) {\r
+                    m_hr = hr;\r
+                }\r
+                ASSERT(!m_bFlushed);\r
+            }\r
+            while (lNumberToSend != 0) {\r
+                m_ppSamples[--lNumberToSend]->Release();\r
+            }\r
+            if (m_hr != S_OK) {\r
+\r
+                //  In any case wait for more data - S_OK just\r
+                //  means there wasn't an error\r
+\r
+                DbgLog((LOG_ERROR, 2, TEXT("ReceiveMultiple returned %8.8X"),\r
+                       m_hr));\r
+            }\r
+        }\r
+\r
+        //  Check for end of stream\r
+\r
+        if (pSample == EOS_PACKET) {\r
+\r
+            //  We don't send even end of stream on if we've previously\r
+            //  returned something other than S_OK\r
+            //  This is because in that case the pin which returned\r
+            //  something other than S_OK should have either sent\r
+            //  EndOfStream() or notified the filter graph\r
+\r
+            if (m_hr == S_OK) {\r
+                DbgLog((LOG_TRACE, 2, TEXT("COutputQueue sending EndOfStream()")));\r
+                HRESULT hr = m_pPin->EndOfStream();\r
+                if (FAILED(hr)) {\r
+                    DbgLog((LOG_ERROR, 2, TEXT("COutputQueue got code 0x%8.8X from EndOfStream()")));\r
+                }\r
+            }\r
+        }\r
+\r
+        //  Data from a new source\r
+\r
+        if (pSample == RESET_PACKET) {\r
+            m_hr = S_OK;\r
+            SetEvent(m_evFlushComplete);\r
+        }\r
+\r
+        if (pSample == NEW_SEGMENT) {\r
+            m_pPin->NewSegment(ppacket->tStart, ppacket->tStop, ppacket->dRate);\r
+            delete ppacket;\r
+        }\r
+    }\r
+}\r
+\r
+//  Send batched stuff anyway\r
+void COutputQueue::SendAnyway()\r
+{\r
+    if (!IsQueued()) {\r
+\r
+        //  m_bSendAnyway is a private parameter checked in ReceiveMultiple\r
+\r
+        m_bSendAnyway = TRUE;\r
+        LONG nProcessed;\r
+        ReceiveMultiple(NULL, 0, &nProcessed);\r
+        m_bSendAnyway = FALSE;\r
+\r
+    } else {\r
+        CAutoLock lck(this);\r
+        QueueSample(SEND_PACKET);\r
+        NotifyThread();\r
+    }\r
+}\r
+\r
+void\r
+COutputQueue::NewSegment(\r
+    REFERENCE_TIME tStart,\r
+    REFERENCE_TIME tStop,\r
+    double dRate)\r
+{\r
+    if (!IsQueued()) {\r
+        if (S_OK == m_hr) {\r
+            if (m_bBatchExact) {\r
+                SendAnyway();\r
+            }\r
+            m_pPin->NewSegment(tStart, tStop, dRate);\r
+        }\r
+    } else {\r
+        if (m_hr == S_OK) {\r
+            //\r
+            // we need to queue the new segment to appear in order in the\r
+            // data, but we need to pass parameters to it. Rather than\r
+            // take the hit of wrapping every single sample so we can tell\r
+            // special ones apart, we queue special pointers to indicate\r
+            // special packets, and we guarantee (by holding the\r
+            // critical section) that the packet immediately following a\r
+            // NEW_SEGMENT value is a NewSegmentPacket containing the\r
+            // parameters.\r
+            NewSegmentPacket * ppack = new NewSegmentPacket;\r
+            if (ppack == NULL) {\r
+                return;\r
+            }\r
+            ppack->tStart = tStart;\r
+            ppack->tStop = tStop;\r
+            ppack->dRate = dRate;\r
+\r
+            CAutoLock lck(this);\r
+            QueueSample(NEW_SEGMENT);\r
+            QueueSample( (IMediaSample*) ppack);\r
+            NotifyThread();\r
+        }\r
+    }\r
+}\r
+\r
+\r
+//\r
+//  End of Stream is queued to output device\r
+//\r
+void COutputQueue::EOS()\r
+{\r
+    CAutoLock lck(this);\r
+    if (!IsQueued()) {\r
+        if (m_bBatchExact) {\r
+            SendAnyway();\r
+        }\r
+        if (m_hr == S_OK) {\r
+            DbgLog((LOG_TRACE, 2, TEXT("COutputQueue sending EndOfStream()")));\r
+            m_bFlushed = FALSE;\r
+            HRESULT hr = m_pPin->EndOfStream();\r
+            if (FAILED(hr)) {\r
+                DbgLog((LOG_ERROR, 2, TEXT("COutputQueue got code 0x%8.8X from EndOfStream()")));\r
+            }\r
+        }\r
+    } else {\r
+        if (m_hr == S_OK) {\r
+            m_bFlushed = FALSE;\r
+            QueueSample(EOS_PACKET);\r
+            NotifyThread();\r
+        }\r
+    }\r
+}\r
+\r
+//\r
+//  Flush all the samples in the queue\r
+//\r
+void COutputQueue::BeginFlush()\r
+{\r
+    if (IsQueued()) {\r
+        {\r
+            CAutoLock lck(this);\r
+\r
+            // block receives -- we assume this is done by the\r
+            // filter in which we are a component\r
+\r
+            // discard all queued data\r
+\r
+            m_bFlushing = TRUE;\r
+\r
+            //  Make sure we discard all samples from now on\r
+\r
+            if (m_hr == S_OK) {\r
+                m_hr = S_FALSE;\r
+            }\r
+\r
+            // Optimize so we don't keep calling downstream all the time\r
+\r
+            if (m_bFlushed && m_bFlushingOpt) {\r
+                return;\r
+            }\r
+\r
+            // Make sure we really wait for the flush to complete\r
+            m_evFlushComplete.Reset();\r
+\r
+            NotifyThread();\r
+        }\r
+\r
+        // pass this downstream\r
+\r
+        m_pPin->BeginFlush();\r
+    } else {\r
+        // pass downstream first to avoid deadlocks\r
+        m_pPin->BeginFlush();\r
+        CAutoLock lck(this);\r
+        // discard all queued data\r
+\r
+        m_bFlushing = TRUE;\r
+\r
+        //  Make sure we discard all samples from now on\r
+\r
+        if (m_hr == S_OK) {\r
+            m_hr = S_FALSE;\r
+        }\r
+    }\r
+\r
+}\r
+\r
+//\r
+// leave flush mode - pass this downstream\r
+void COutputQueue::EndFlush()\r
+{\r
+    {\r
+        CAutoLock lck(this);\r
+        ASSERT(m_bFlushing);\r
+        if (m_bFlushingOpt && m_bFlushed && IsQueued()) {\r
+            m_bFlushing = FALSE;\r
+            m_hr = S_OK;\r
+            return;\r
+        }\r
+    }\r
+\r
+    // sync with pushing thread -- done in BeginFlush\r
+    // ensure no more data to go downstream -- done in BeginFlush\r
+    //\r
+    // Because we are synching here there is no need to hold the critical\r
+    // section (in fact we'd deadlock if we did!)\r
+\r
+    if (IsQueued()) {\r
+        m_evFlushComplete.Wait();\r
+    } else {\r
+        FreeSamples();\r
+    }\r
+\r
+    //  Be daring - the caller has guaranteed no samples will arrive\r
+    //  before EndFlush() returns\r
+\r
+    m_bFlushing = FALSE;\r
+    m_bFlushed  = TRUE;\r
+\r
+    // call EndFlush on downstream pins\r
+\r
+    m_pPin->EndFlush();\r
+\r
+    m_hr = S_OK;\r
+}\r
+\r
+//  COutputQueue::QueueSample\r
+//\r
+//  private method to Send a sample to the output queue\r
+//  The critical section MUST be held when this is called\r
+\r
+void COutputQueue::QueueSample(IMediaSample *pSample)\r
+{\r
+    if (NULL == m_List->AddTail(pSample)) {\r
+        if (!IsSpecialSample(pSample)) {\r
+            pSample->Release();\r
+        }\r
+    }\r
+}\r
+\r
+//\r
+//  COutputQueue::Receive()\r
+//\r
+//  Send a single sample by the multiple sample route\r
+//  (NOTE - this could be optimized if necessary)\r
+//\r
+//  On return the sample will have been Release()'d\r
+//\r
+\r
+HRESULT COutputQueue::Receive(IMediaSample *pSample)\r
+{\r
+    LONG nProcessed;\r
+    return ReceiveMultiple(&pSample, 1, &nProcessed);\r
+}\r
+\r
+//\r
+//  COutputQueue::ReceiveMultiple()\r
+//\r
+//  Send a set of samples to the downstream pin\r
+//\r
+//      ppSamples           - array of samples\r
+//      nSamples            - how many\r
+//      nSamplesProcessed   - How many were processed\r
+//\r
+//  On return all samples will have been Release()'d\r
+//\r
+\r
+HRESULT COutputQueue::ReceiveMultiple (\r
+    __in_ecount(nSamples) IMediaSample **ppSamples,\r
+    long nSamples,\r
+    __out long *nSamplesProcessed)\r
+{\r
+    if (nSamples < 0) {\r
+        return E_INVALIDARG;\r
+    }\r
+    \r
+    CAutoLock lck(this);\r
+    //  Either call directly or queue up the samples\r
+\r
+    if (!IsQueued()) {\r
+\r
+        //  If we already had a bad return code then just return\r
+\r
+        if (S_OK != m_hr) {\r
+\r
+            //  If we've never received anything since the last Flush()\r
+            //  and the sticky return code is not S_OK we must be\r
+            //  flushing\r
+            //  ((!A || B) is equivalent to A implies B)\r
+            ASSERT(!m_bFlushed || m_bFlushing);\r
+\r
+            //  We're supposed to Release() them anyway!\r
+            *nSamplesProcessed = 0;\r
+            for (int i = 0; i < nSamples; i++) {\r
+                DbgLog((LOG_TRACE, 3, TEXT("COutputQueue (direct) : Discarding %d samples code 0x%8.8X"),\r
+                        nSamples, m_hr));\r
+                ppSamples[i]->Release();\r
+            }\r
+\r
+            return m_hr;\r
+        }\r
+        //\r
+        //  If we're flushing the sticky return code should be S_FALSE\r
+        //\r
+        ASSERT(!m_bFlushing);\r
+        m_bFlushed = FALSE;\r
+\r
+        ASSERT(m_nBatched < m_lBatchSize);\r
+        ASSERT(m_nBatched == 0 || m_bBatchExact);\r
+\r
+        //  Loop processing the samples in batches\r
+\r
+        LONG iLost = 0;\r
+        long iDone = 0;\r
+        for (iDone = 0;\r
+             iDone < nSamples || (m_nBatched != 0 && m_bSendAnyway);\r
+            ) {\r
+\r
+//pragma message (REMIND("Implement threshold scheme"))\r
+            ASSERT(m_nBatched < m_lBatchSize);\r
+            if (iDone < nSamples) {\r
+                m_ppSamples[m_nBatched++] = ppSamples[iDone++];\r
+            }\r
+            if (m_nBatched == m_lBatchSize ||\r
+                nSamples == 0 && (m_bSendAnyway || !m_bBatchExact)) {\r
+                LONG nDone;\r
+                DbgLog((LOG_TRACE, 4, TEXT("Batching %d samples"),\r
+                       m_nBatched));\r
+\r
+                if (m_hr == S_OK) {\r
+                    m_hr = m_pInputPin->ReceiveMultiple(m_ppSamples,\r
+                                                        m_nBatched,\r
+                                                        &nDone);\r
+                } else {\r
+                    nDone = 0;\r
+                }\r
+                iLost += m_nBatched - nDone;\r
+                for (LONG i = 0; i < m_nBatched; i++) {\r
+                    m_ppSamples[i]->Release();\r
+                }\r
+                m_nBatched = 0;\r
+            }\r
+        }\r
+        *nSamplesProcessed = iDone - iLost;\r
+        if (*nSamplesProcessed < 0) {\r
+            *nSamplesProcessed = 0;\r
+        }\r
+        return m_hr;\r
+    } else {\r
+        /*  We're sending to our thread */\r
+\r
+        if (m_hr != S_OK) {\r
+            *nSamplesProcessed = 0;\r
+            DbgLog((LOG_TRACE, 3, TEXT("COutputQueue (queued) : Discarding %d samples code 0x%8.8X"),\r
+                    nSamples, m_hr));\r
+            for (int i = 0; i < nSamples; i++) {\r
+                ppSamples[i]->Release();\r
+            }\r
+            return m_hr;\r
+        }\r
+        m_bFlushed = FALSE;\r
+        for (long i = 0; i < nSamples; i++) {\r
+            QueueSample(ppSamples[i]);\r
+        }\r
+        *nSamplesProcessed = nSamples;\r
+        if (!m_bBatchExact ||\r
+            m_nBatched + m_List->GetCount() >= m_lBatchSize) {\r
+            NotifyThread();\r
+        }\r
+        return S_OK;\r
+    }\r
+}\r
+\r
+//  Get ready for new data - cancels sticky m_hr\r
+void COutputQueue::Reset()\r
+{\r
+    if (!IsQueued()) {\r
+        m_hr = S_OK;\r
+    } else {\r
+        {\r
+            CAutoLock lck(this);\r
+            QueueSample(RESET_PACKET);\r
+            NotifyThread();\r
+        }\r
+        m_evFlushComplete.Wait();\r
+    }\r
+}\r
+\r
+//  Remove and Release() all queued and Batched samples\r
+void COutputQueue::FreeSamples()\r
+{\r
+    CAutoLock lck(this);\r
+    if (IsQueued()) {\r
+        while (TRUE) {\r
+            IMediaSample *pSample = m_List->RemoveHead();\r
+           // inform derived class we took something off the queue\r
+           if (m_hEventPop) {\r
+                //DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered  SET EVENT")));\r
+               SetEvent(m_hEventPop);\r
+           }\r
+\r
+            if (pSample == NULL) {\r
+                break;\r
+            }\r
+            if (!IsSpecialSample(pSample)) {\r
+                pSample->Release();\r
+            } else {\r
+                if (pSample == NEW_SEGMENT) {\r
+                    //  Free NEW_SEGMENT packet\r
+                    NewSegmentPacket *ppacket =\r
+                        (NewSegmentPacket *) m_List->RemoveHead();\r
+                   // inform derived class we took something off the queue\r
+                   if (m_hEventPop) {\r
+                        //DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered  SET EVENT")));\r
+                       SetEvent(m_hEventPop);\r
+                   }\r
+\r
+                    ASSERT(ppacket != NULL);\r
+                    delete ppacket;\r
+                }\r
+            }\r
+        }\r
+    }\r
+    for (int i = 0; i < m_nBatched; i++) {\r
+        m_ppSamples[i]->Release();\r
+    }\r
+    m_nBatched = 0;\r
+}\r
+\r
+//  Notify the thread if there is something to do\r
+//\r
+//  The critical section MUST be held when this is called\r
+void COutputQueue::NotifyThread()\r
+{\r
+    //  Optimize - no need to signal if it's not waiting\r
+    ASSERT(IsQueued());\r
+    if (m_lWaiting) {\r
+        ReleaseSemaphore(m_hSem, m_lWaiting, NULL);\r
+        m_lWaiting = 0;\r
+    }\r
+}\r
+\r
+//  See if there's any work to do\r
+//  Returns\r
+//      TRUE  if there is nothing on the queue and nothing in the batch\r
+//            and all data has been sent\r
+//      FALSE otherwise\r
+//\r
+BOOL COutputQueue::IsIdle()\r
+{\r
+    CAutoLock lck(this);\r
+\r
+    //  We're idle if\r
+    //      there is no thread (!IsQueued()) OR\r
+    //      the thread is waiting for more work  (m_lWaiting != 0)\r
+    //  AND\r
+    //      there's nothing in the current batch (m_nBatched == 0)\r
+\r
+    if (IsQueued() && m_lWaiting == 0 || m_nBatched != 0) {\r
+        return FALSE;\r
+    } else {\r
+\r
+        //  If we're idle it shouldn't be possible for there\r
+        //  to be anything on the work queue\r
+\r
+        ASSERT(!IsQueued() || m_List->GetCount() == 0);\r
+        return TRUE;\r
+    }\r
+}\r
+\r
+\r
+void COutputQueue::SetPopEvent(HANDLE hEvent)\r
+{\r
+    m_hEventPop = hEvent;\r
+}\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/outputq.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/outputq.h
new file mode 100644 (file)
index 0000000..db3d424
--- /dev/null
@@ -0,0 +1,137 @@
+//------------------------------------------------------------------------------\r
+// File: OutputQ.h\r
+//\r
+// Desc: DirectShow base classes -  defines the COutputQueue class, which\r
+//       makes a queue of samples and sends them to an output pin.  The \r
+//       class will optionally send the samples to the pin directly.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+typedef CGenericList<IMediaSample> CSampleList;\r
+\r
+class COutputQueue : public CCritSec\r
+{\r
+public:\r
+    //  Constructor\r
+    COutputQueue(IPin      *pInputPin,          //  Pin to send stuff to\r
+                 __inout HRESULT *phr,          //  'Return code'\r
+                 BOOL       bAuto = TRUE,       //  Ask pin if blocks\r
+                 BOOL       bQueue = TRUE,      //  Send through queue (ignored if\r
+                                                //  bAuto set)\r
+                 LONG       lBatchSize = 1,     //  Batch\r
+                 BOOL       bBatchExact = FALSE,//  Batch exactly to BatchSize\r
+                 LONG       lListSize =         //  Likely number in the list\r
+                                DEFAULTCACHE,\r
+                 DWORD      dwPriority =        //  Priority of thread to create\r
+                                THREAD_PRIORITY_NORMAL,\r
+                 bool       bFlushingOpt = false // flushing optimization\r
+                );\r
+    ~COutputQueue();\r
+\r
+    // enter flush state - discard all data\r
+    void BeginFlush();      // Begin flushing samples\r
+\r
+    // re-enable receives (pass this downstream)\r
+    void EndFlush();        // Complete flush of samples - downstream\r
+                            // pin guaranteed not to block at this stage\r
+\r
+    void EOS();             // Call this on End of stream\r
+\r
+    void SendAnyway();      // Send batched samples anyway (if bBatchExact set)\r
+\r
+    void NewSegment(\r
+            REFERENCE_TIME tStart,\r
+            REFERENCE_TIME tStop,\r
+            double dRate);\r
+\r
+    HRESULT Receive(IMediaSample *pSample);\r
+\r
+    // do something with these media samples\r
+    HRESULT ReceiveMultiple (\r
+        __in_ecount(nSamples) IMediaSample **pSamples,\r
+        long nSamples,\r
+        __out long *nSamplesProcessed);\r
+\r
+    void Reset();           // Reset m_hr ready for more data\r
+\r
+    //  See if its idle or not\r
+    BOOL IsIdle();\r
+\r
+    // give the class an event to fire after everything removed from the queue\r
+    void SetPopEvent(HANDLE hEvent);\r
+\r
+protected:\r
+    static DWORD WINAPI InitialThreadProc(__in LPVOID pv);\r
+    DWORD ThreadProc();\r
+    BOOL  IsQueued()\r
+    {\r
+        return m_List != NULL;\r
+    };\r
+\r
+    //  The critical section MUST be held when this is called\r
+    void QueueSample(IMediaSample *pSample);\r
+\r
+    BOOL IsSpecialSample(IMediaSample *pSample)\r
+    {\r
+        return (DWORD_PTR)pSample > (DWORD_PTR)(LONG_PTR)(-16);\r
+    };\r
+\r
+    //  Remove and Release() batched and queued samples\r
+    void FreeSamples();\r
+\r
+    //  Notify the thread there is something to do\r
+    void NotifyThread();\r
+\r
+\r
+protected:\r
+    //  Queue 'messages'\r
+    #define SEND_PACKET      ((IMediaSample *)(LONG_PTR)(-2))  // Send batch\r
+    #define EOS_PACKET       ((IMediaSample *)(LONG_PTR)(-3))  // End of stream\r
+    #define RESET_PACKET     ((IMediaSample *)(LONG_PTR)(-4))  // Reset m_hr\r
+    #define NEW_SEGMENT      ((IMediaSample *)(LONG_PTR)(-5))  // send NewSegment\r
+\r
+    // new segment packet is always followed by one of these\r
+    struct NewSegmentPacket {\r
+        REFERENCE_TIME tStart;\r
+        REFERENCE_TIME tStop;\r
+        double dRate;\r
+    };\r
+\r
+    // Remember input stuff\r
+    IPin          * const m_pPin;\r
+    IMemInputPin  *       m_pInputPin;\r
+    BOOL            const m_bBatchExact;\r
+    LONG            const m_lBatchSize;\r
+\r
+    CSampleList   *       m_List;\r
+    HANDLE                m_hSem;\r
+    CAMEvent                m_evFlushComplete;\r
+    HANDLE                m_hThread;\r
+    __field_ecount_opt(m_lBatchSize) IMediaSample  **      m_ppSamples;\r
+    __range(0, m_lBatchSize)         LONG                  m_nBatched;\r
+\r
+    //  Wait optimization\r
+    LONG                  m_lWaiting;\r
+    //  Flush synchronization\r
+    BOOL                  m_bFlushing;\r
+\r
+    // flushing optimization. some downstream filters have trouble\r
+    // with the queue's flushing optimization. other rely on it\r
+    BOOL                  m_bFlushed;\r
+    bool                  m_bFlushingOpt;\r
+\r
+    //  Terminate now\r
+    BOOL                  m_bTerminate;\r
+\r
+    //  Send anyway flag for batching\r
+    BOOL                  m_bSendAnyway;\r
+\r
+    //  Deferred 'return code'\r
+    HRESULT volatile         m_hr;\r
+\r
+    // an event that can be fired after every deliver\r
+    HANDLE m_hEventPop;\r
+};\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/perflog.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/perflog.cpp
new file mode 100644 (file)
index 0000000..e642538
--- /dev/null
@@ -0,0 +1,347 @@
+//------------------------------------------------------------------------------\r
+// File: perflog.cpp\r
+//\r
+// Desc: Macros for DirectShow performance logging.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+#pragma warning (disable:4201)\r
+\r
+#include <streams.h>\r
+#include <windows.h>\r
+#include <tchar.h>\r
+#include <winperf.h>\r
+#include <wmistr.h>\r
+#include <evntrace.h>\r
+#include <strsafe.h>\r
+#include "perflog.h"\r
+\r
+//\r
+// Local function prototypes.\r
+//\r
+\r
+ULONG\r
+WINAPI\r
+PerflogCallback (\r
+    WMIDPREQUESTCODE RequestCode,\r
+    __in PVOID Context,\r
+    __out ULONG* BufferSize,\r
+    __in PVOID Buffer\r
+    );\r
+\r
+//\r
+// Event tracing function pointers.\r
+// We have to do this to run on down-level platforms.\r
+//\r
+\r
+#ifdef UNICODE\r
+\r
+ULONG\r
+(__stdcall * _RegisterTraceGuids) (\r
+    __in IN WMIDPREQUEST RequestAddress,\r
+    __in IN PVOID RequestContext,\r
+    IN LPCGUID ControlGuid,\r
+    IN ULONG GuidCount,\r
+    __in IN PTRACE_GUID_REGISTRATION TraceGuidReg,\r
+    IN LPCWSTR MofImagePath,\r
+    IN LPCWSTR MofResourceName,\r
+    OUT PTRACEHANDLE RegistrationHandle\r
+    );\r
+\r
+#define REGISTERTRACEGUIDS_NAME "RegisterTraceGuidsW"\r
+\r
+#else\r
+\r
+ULONG\r
+(__stdcall * _RegisterTraceGuids) (\r
+    __in IN WMIDPREQUEST RequestAddress,\r
+    __in IN PVOID RequestContext,\r
+    IN LPCGUID ControlGuid,\r
+    IN ULONG GuidCount,\r
+    __in IN PTRACE_GUID_REGISTRATION TraceGuidReg,\r
+    IN LPCSTR MofImagePath,\r
+    IN LPCSTR MofResourceName,\r
+    __out OUT PTRACEHANDLE RegistrationHandle\r
+    );\r
+\r
+#define REGISTERTRACEGUIDS_NAME "RegisterTraceGuidsA"\r
+\r
+#endif\r
+\r
+ULONG\r
+(__stdcall * _UnregisterTraceGuids) (\r
+    TRACEHANDLE RegistrationHandle\r
+    );\r
+\r
+TRACEHANDLE\r
+(__stdcall * _GetTraceLoggerHandle) (\r
+    __in PVOID Buffer\r
+    );\r
+\r
+UCHAR\r
+(__stdcall * _GetTraceEnableLevel) (\r
+    TRACEHANDLE TraceHandle\r
+    );\r
+\r
+ULONG\r
+(__stdcall * _GetTraceEnableFlags) (\r
+    TRACEHANDLE TraceHandle\r
+    );\r
+\r
+ULONG\r
+(__stdcall * _TraceEvent) (\r
+    TRACEHANDLE TraceHandle,\r
+    __in PEVENT_TRACE_HEADER EventTrace\r
+    );\r
+\r
+HINSTANCE _Advapi32;\r
+\r
+//\r
+// Global variables.\r
+//\r
+\r
+BOOL EventTracingAvailable=FALSE;\r
+ULONG PerflogEnableFlags;\r
+UCHAR PerflogEnableLevel;\r
+ULONG PerflogModuleLevel = 0;\r
+void (*OnStateChanged)(void);\r
+TRACEHANDLE PerflogTraceHandle=NULL;\r
+TRACEHANDLE PerflogRegHandle;\r
+\r
+// The Win32 wsprintf() function writes a maximum of 1024 characters to it's output buffer.\r
+// See the documentation for wsprintf()'s lpOut parameter for more information.\r
+const INT iDEBUGINFO = 1024; // Used to format strings\r
+\r
+//\r
+// This routine initializes performance logging.\r
+// It should be called from DllMain().\r
+//\r
+\r
+\r
+VOID\r
+PerflogReadModuleLevel(\r
+    HINSTANCE hInstance\r
+    )\r
+{\r
+    LONG lReturn;                   // Create key return value\r
+    TCHAR szInfo[iDEBUGINFO];       // Constructs key names\r
+    TCHAR szFullName[iDEBUGINFO];   // Load the full path and module name\r
+    HKEY hModuleKey;                // Module key handle\r
+    LPTSTR pName;                   // Searches from the end for a backslash\r
+    DWORD dwKeySize, dwKeyType, dwKeyValue;\r
+\r
+    DWORD dwSize = GetModuleFileName(\r
+        (hInstance ? hInstance : GetModuleHandle( NULL )),\r
+        szFullName,\r
+        iDEBUGINFO );\r
+\r
+    if (0 == dwSize || iDEBUGINFO == dwSize) {\r
+        return;\r
+    }\r
+\r
+    pName = _tcsrchr(szFullName,'\\');\r
+    if (pName == NULL) {\r
+        pName = szFullName;\r
+    } else {\r
+        pName++;\r
+    }\r
+\r
+    /* Construct the base key name */\r
+    (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("SOFTWARE\\Debug\\%s"),pName);\r
+\r
+    /* Open the key for this module */\r
+    lReturn =\r
+        RegOpenKeyEx(\r
+            HKEY_LOCAL_MACHINE,   // Handle of an open key\r
+            szInfo,               // Address of subkey name\r
+            (DWORD) 0,            // Reserved value\r
+            KEY_QUERY_VALUE,      // Desired security access\r
+            &hModuleKey );        // Opened handle buffer\r
+\r
+    if (lReturn != ERROR_SUCCESS) {\r
+        return;\r
+    }\r
+\r
+    dwKeySize = sizeof(DWORD);\r
+    lReturn = RegQueryValueEx(\r
+        hModuleKey,                 // Handle to an open key\r
+        TEXT("PERFLOG"),\r
+        NULL,                       // Reserved field\r
+        &dwKeyType,                 // Returns the field type\r
+        (LPBYTE) &dwKeyValue,       // Returns the field's value\r
+        &dwKeySize );               // Number of bytes transferred\r
+\r
+    if ((lReturn == ERROR_SUCCESS) && (dwKeyType == REG_DWORD))\r
+    {\r
+        PerflogModuleLevel = dwKeyValue;\r
+    }\r
+\r
+    RegCloseKey(hModuleKey);\r
+}\r
+\r
+BOOL PerflogInitIfEnabled(\r
+    IN HINSTANCE hInstance,\r
+    __in IN PPERFLOG_LOGGING_PARAMS LogParams\r
+    )\r
+{\r
+    PerflogReadModuleLevel( hInstance );\r
+    if (PerflogModuleLevel)\r
+    {\r
+        return PerflogInitialize( LogParams );\r
+    }\r
+    else\r
+    {\r
+        return FALSE;\r
+    }\r
+}\r
+\r
+BOOL\r
+PerflogInitialize (\r
+    __in IN PPERFLOG_LOGGING_PARAMS LogParams\r
+    )\r
+{\r
+    ULONG status;\r
+\r
+    //\r
+    // If we're running on a recent-enough platform, this will get\r
+    // pointers to the event tracing routines.\r
+    //\r
+\r
+    _Advapi32 = GetModuleHandle (_T("ADVAPI32.DLL"));\r
+    if (_Advapi32 == NULL) {\r
+        return FALSE;\r
+    }\r
+\r
+    *((FARPROC*) &_RegisterTraceGuids) = GetProcAddress (_Advapi32, REGISTERTRACEGUIDS_NAME);\r
+    *((FARPROC*) &_UnregisterTraceGuids) = GetProcAddress (_Advapi32, "UnregisterTraceGuids");\r
+    *((FARPROC*) &_GetTraceLoggerHandle) = GetProcAddress (_Advapi32, "GetTraceLoggerHandle");\r
+    *((FARPROC*) &_GetTraceEnableLevel) = GetProcAddress (_Advapi32, "GetTraceEnableLevel");\r
+    *((FARPROC*) &_GetTraceEnableFlags) = GetProcAddress (_Advapi32, "GetTraceEnableFlags");\r
+    *((FARPROC*) &_TraceEvent) = GetProcAddress (_Advapi32, "TraceEvent");\r
+\r
+    if (_RegisterTraceGuids == NULL ||\r
+        _UnregisterTraceGuids == NULL ||\r
+        _GetTraceEnableLevel == NULL ||\r
+        _GetTraceEnableFlags == NULL ||\r
+        _TraceEvent == NULL) {\r
+\r
+        return FALSE;\r
+    }\r
+\r
+    EventTracingAvailable = TRUE;\r
+\r
+    OnStateChanged = LogParams->OnStateChanged;\r
+\r
+    //\r
+    // Register our GUIDs.\r
+    //\r
+\r
+    status = _RegisterTraceGuids (PerflogCallback,\r
+                                  LogParams,\r
+                                  &LogParams->ControlGuid,\r
+                                  LogParams->NumberOfTraceGuids,\r
+                                  LogParams->TraceGuids,\r
+                                  NULL,\r
+                                  NULL,\r
+                                  &PerflogRegHandle);\r
+\r
+    return (status == ERROR_SUCCESS);\r
+}\r
+\r
+//\r
+// This routine shuts down performance logging.\r
+//\r
+\r
+VOID\r
+PerflogShutdown (\r
+    VOID\r
+    )\r
+{\r
+    if (!EventTracingAvailable) {\r
+        return;\r
+    }\r
+\r
+    _UnregisterTraceGuids (PerflogRegHandle);\r
+    PerflogRegHandle = NULL;\r
+    PerflogTraceHandle = NULL;\r
+}\r
+\r
+//\r
+// Event tracing callback routine.\r
+// It's called when controllers call event tracing control functions.\r
+//\r
+\r
+ULONG\r
+WINAPI\r
+PerflogCallback (\r
+    WMIDPREQUESTCODE RequestCode,\r
+    __in PVOID Context,\r
+    __out ULONG* BufferSize,\r
+    __in PVOID Buffer\r
+    )\r
+{\r
+    ULONG status;\r
+\r
+    UNREFERENCED_PARAMETER (Context);\r
+\r
+    ASSERT (EventTracingAvailable);\r
+\r
+    status = ERROR_SUCCESS;\r
+\r
+    switch (RequestCode) {\r
+\r
+    case WMI_ENABLE_EVENTS:\r
+        PerflogTraceHandle = _GetTraceLoggerHandle (Buffer);\r
+        PerflogEnableFlags = _GetTraceEnableFlags (PerflogTraceHandle);\r
+        PerflogEnableLevel = _GetTraceEnableLevel (PerflogTraceHandle);\r
+        break;\r
+\r
+    case WMI_DISABLE_EVENTS:\r
+        PerflogTraceHandle = NULL;\r
+        PerflogEnableFlags = 0;\r
+        PerflogEnableLevel = 0;\r
+        break;\r
+\r
+    default:\r
+        status = ERROR_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (OnStateChanged != NULL) {\r
+        OnStateChanged();\r
+    }\r
+\r
+    *BufferSize = 0;\r
+    return status;\r
+}\r
+\r
+//\r
+// Logging routine.\r
+//\r
+\r
+VOID\r
+PerflogTraceEvent (\r
+    __in PEVENT_TRACE_HEADER Event\r
+    )\r
+{\r
+    if (!EventTracingAvailable) {\r
+        return;\r
+    }\r
+\r
+    _TraceEvent (PerflogTraceHandle, Event);\r
+}\r
+\r
+VOID\r
+PerflogTraceEventLevel(\r
+    ULONG Level,\r
+    __in PEVENT_TRACE_HEADER Event\r
+    )\r
+{\r
+    if ((!EventTracingAvailable) || (Level <= PerflogModuleLevel)) {\r
+        return;\r
+    }\r
+\r
+    _TraceEvent (PerflogTraceHandle, Event);\r
+}\r
+\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/perflog.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/perflog.h
new file mode 100644 (file)
index 0000000..503a130
--- /dev/null
@@ -0,0 +1,56 @@
+//------------------------------------------------------------------------------\r
+// File: perflog.h\r
+//\r
+// Desc: Performance logging framework.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+typedef struct _PERFLOG_LOGGING_PARAMS {\r
+    GUID ControlGuid;\r
+    void (*OnStateChanged)(void);\r
+    ULONG NumberOfTraceGuids;\r
+    TRACE_GUID_REGISTRATION TraceGuids[ANYSIZE_ARRAY];\r
+} PERFLOG_LOGGING_PARAMS, *PPERFLOG_LOGGING_PARAMS;\r
+\r
+BOOL\r
+PerflogInitIfEnabled(\r
+    IN HINSTANCE hInstance,\r
+    __in PPERFLOG_LOGGING_PARAMS LogParams\r
+    );\r
+\r
+BOOL\r
+PerflogInitialize (\r
+    __in PPERFLOG_LOGGING_PARAMS LogParams\r
+    );\r
+\r
+VOID\r
+PerflogShutdown (\r
+    VOID\r
+    );\r
+\r
+VOID\r
+PerflogTraceEvent (\r
+    __in PEVENT_TRACE_HEADER Event\r
+    );\r
+\r
+extern ULONG PerflogEnableFlags;\r
+extern UCHAR PerflogEnableLevel;\r
+extern ULONG PerflogModuleLevel;\r
+extern TRACEHANDLE PerflogTraceHandle;\r
+extern TRACEHANDLE PerflogRegHandle;\r
+\r
+#define PerflogTracingEnabled() (PerflogTraceHandle != 0)\r
+\r
+#define PerflogEvent( _x_ ) PerflogTraceEventLevel _x_\r
+\r
+VOID\r
+PerflogTraceEventLevel(\r
+    ULONG Level,\r
+    __in PEVENT_TRACE_HEADER Event\r
+    );\r
+\r
+VOID\r
+PerflogTraceEvent (\r
+    __in PEVENT_TRACE_HEADER Event\r
+    );\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/perfstruct.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/perfstruct.h
new file mode 100644 (file)
index 0000000..9c67b73
--- /dev/null
@@ -0,0 +1,194 @@
+//------------------------------------------------------------------------------\r
+// File: PerfStruct.h\r
+//\r
+// Desc: Structures for DirectShow performance logging.\r
+//\r
+// Copyright (c) 2000-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef _PERFSTRUCT_H_\r
+#define _PERFSTRUCT_H_\r
+\r
+#include <wmistr.h>\r
+#include <evntrace.h>\r
+\r
+// {28CF047A-2437-4b24-B653-B9446A419A69}\r
+DEFINE_GUID(GUID_DSHOW_CTL,\r
+0x28cf047a, 0x2437, 0x4b24, 0xb6, 0x53, 0xb9, 0x44, 0x6a, 0x41, 0x9a, 0x69);\r
+\r
+// {D0DA7AD6-AE80-4de5-AAFC-C126711E7593}\r
+DEFINE_GUID(GUID_VIDEOREND,\r
+0xd0da7ad6, 0xae80, 0x4de5, 0xaa, 0xfc, 0xc1, 0x26, 0x71, 0x1e, 0x75, 0x93);\r
+\r
+// {DC70AC3E-93E5-48db-88AB-E42064EC276A}\r
+DEFINE_GUID(GUID_DSOUNDGLITCH,\r
+0xdc70ac3e, 0x93e5, 0x48db, 0x88, 0xab, 0xe4, 0x20, 0x64, 0xec, 0x27, 0x6a);\r
+\r
+// {3d7e7d93-2fc8-4a07-a719-e0922ff2899}\r
+DEFINE_GUID(GUID_STREAMTRACE,\r
+0x3d7e7d93, 0x2fc8, 0x4a07, 0xa7, 0x19, 0xe0, 0x92, 0x2f, 0xf2, 0x89, 0x9e);\r
+\r
+// AZFIX: the following GUIDs aren't useful right now.\r
+\r
+// {3C33F7F5-EE54-493c-BA25-1656539C05AC}\r
+DEFINE_GUID(GUID_GETTIME,\r
+0x3c33f7f5, 0xee54, 0x493c, 0xba, 0x25, 0x16, 0x56, 0x53, 0x9c, 0x5, 0xac);\r
+\r
+// {CC44B44D-8169-4952-9E4A-A4E13295E492}\r
+DEFINE_GUID(GUID_AUDIOREND,\r
+0xcc44b44d, 0x8169, 0x4952, 0x9e, 0x4a, 0xa4, 0xe1, 0x32, 0x95, 0xe4, 0x92);\r
+\r
+// {775D19BF-4D8B-4de6-8DC9-66BAC7B310A2}\r
+DEFINE_GUID(GUID_FRAMEDROP,\r
+0x775d19bf, 0x4d8b, 0x4de6, 0x8d, 0xc9, 0x66, 0xba, 0xc7, 0xb3, 0x10, 0xa2);\r
+\r
+// {56D29065-EFBE-42dc-8C29-E325DC9C27D5}\r
+DEFINE_GUID(GUID_AUDIOBREAK,\r
+0x56d29065, 0xefbe, 0x42dc, 0x8c, 0x29, 0xe3, 0x25, 0xdc, 0x9c, 0x27, 0xd5);\r
+\r
+// {E1E6EA87-95A8-497e-BFBA-0295AEBCC707}\r
+DEFINE_GUID(GUID_AUDIORECV,\r
+0xe1e6ea87, 0x95a8, 0x497e, 0xbf, 0xba, 0x2, 0x95, 0xae, 0xbc, 0xc7, 0x7);\r
+\r
+// {10F7768A-B1E7-4242-AD90-A2D44683D9F0}\r
+DEFINE_GUID(GUID_AUDIOSLAVE,\r
+0x10f7768a, 0xb1e7, 0x4242, 0xad, 0x90, 0xa2, 0xd4, 0x46, 0x83, 0xd9, 0xf0);\r
+\r
+// {8983803D-691A-49bc-8FF6-962A39C0198F}\r
+DEFINE_GUID(GUID_AUDIOADDBREAK,\r
+0x8983803d, 0x691a, 0x49bc, 0x8f, 0xf6, 0x96, 0x2a, 0x39, 0xc0, 0x19, 0x8f);\r
+\r
+#define GLITCHTYPE_DSOUNDFIRSTGOOD 0\r
+#define GLITCHTYPE_DSOUNDFIRSTBAD  1\r
+\r
+typedef struct PERFINFO_DSHOW_AUDIOGLITCH {\r
+    ULONGLONG   cycleCounter;\r
+    DWORD       glitchType;\r
+    LONGLONG   sampleTime;\r
+    LONGLONG   previousTime;\r
+    ULONG_PTR       instanceId;\r
+} PERFINFO_DSHOW_AUDIOGLITCH, *PPERFINFO_DSHOW_AUDIOGLITCH;\r
+\r
+typedef struct PERFINFO_WMI_AUDIOGLITCH {\r
+    EVENT_TRACE_HEADER          header;\r
+    PERFINFO_DSHOW_AUDIOGLITCH  data;\r
+} PERFINFO_WMI_AUDIO_GLITCH, *PPERFINFO_WMI_AUDIOGLITCH;\r
+\r
+typedef struct PERFINFO_DSHOW_GETTIME {\r
+    ULONGLONG    cycleCounter;\r
+    ULONGLONG    dshowClock;\r
+} PERFINFO_DSHOW_GETTIME, *PPERFINFO_DSHOW_GETTIME;\r
+\r
+typedef struct PERFINFO_WMI_GETTIME {\r
+    EVENT_TRACE_HEADER        header;\r
+    PERFINFO_DSHOW_GETTIME    data;\r
+} PERFINFO_WMI_GETTIME, *PPERFINFO_WMI_GETTIME;\r
+\r
+typedef struct PERFINFO_DSHOW_AVREND {\r
+    ULONGLONG    cycleCounter;\r
+    ULONGLONG    dshowClock;\r
+    ULONGLONG    sampleTime;\r
+} PERFINFO_DSHOW_AVREND, *PPERFINFO_DSHOW_AVREND;\r
+\r
+typedef struct PERFINFO_WMI_AVREND {\r
+    EVENT_TRACE_HEADER      header;\r
+    PERFINFO_DSHOW_AVREND   data;\r
+} PERFINFO_WMI_AVREND, *PPERFINFO_WMI_AVREND;\r
+\r
+typedef struct PERFINFO_DSHOW_AUDIOBREAK {\r
+    ULONGLONG    cycleCounter;\r
+    ULONGLONG    dshowClock;\r
+    ULONGLONG    sampleTime;\r
+    ULONGLONG    sampleDuration;\r
+} PERFINFO_DSHOW_AUDIOBREAK, *PPERFINFO_DSHOW_AUDIOBREAK;\r
+\r
+typedef struct PERFINFO_WMI_AUDIOBREAK {\r
+    EVENT_TRACE_HEADER          header;\r
+    PERFINFO_DSHOW_AUDIOBREAK   data;\r
+} PERFINFO_WMI_AUDIOBREAK, *PPERFINFO_WMI_AUDIOBREAK;\r
+\r
+typedef struct PERFINFO_DSHOW_FRAMEDROP {\r
+    ULONGLONG    cycleCounter;\r
+    ULONGLONG    dshowClock;\r
+    ULONGLONG    frameTime;\r
+} PERFINFO_DSHOW_FRAMEDROP, *PPERFINFO_DSHOW_FRAMEDROP;\r
+\r
+typedef struct PERFINFO_WMI_FRAMEDROP {\r
+    EVENT_TRACE_HEADER          header;\r
+    PERFINFO_DSHOW_FRAMEDROP    data;\r
+} PERFINFO_WMI_FRAMEDROP, *PPERFINFO_WMI_FRAMEDROP;\r
+\r
+#define PERFINFO_STREAMTRACE_MPEG2DEMUX_PTS_TRANSLATION     1\r
+#define PERFINFO_STREAMTRACE_MPEG2DEMUX_SAMPLE_RECEIVED     2\r
+#define PERFINFO_STREAMTRACE_VMR_BEGIN_ADVISE               3\r
+#define PERFINFO_STREAMTRACE_VMR_END_ADVISE                 4\r
+#define PERFINFO_STREAMTRACE_VMR_RECEIVE                    5\r
+#define PERFINFO_STREAMTRACE_VMR_BEGIN_DEINTERLACE          6\r
+#define PERFINFO_STREAMTRACE_VMR_END_DEINTERLACE            7\r
+#define PERFINFO_STREAMTRACE_VMR_BEGIN_DECODE               8\r
+#define PERFINFO_STREAMTRACE_VMR_END_DECODE                 9\r
+#define PERFINFO_STREAMTRACE_VMR_DROPPED_FRAME              10\r
+#define PERFINFO_STREAMTRACE_ENCDEC_DTFILTERINPUT           11\r
+#define PERFINFO_STREAMTRACE_ENCDEC_DTFILTEROUTPUT          12\r
+#define PERFINFO_STREAMTRACE_ENCDEC_ETFILTERINPUT           13\r
+#define PERFINFO_STREAMTRACE_ENCDEC_ETFILTEROUTPUT          14\r
+#define PERFINFO_STREAMTRACE_ENCDEC_XDSCODECINPUT           15\r
+#define PERFINFO_STREAMTRACE_SBE_DVRANALYSISINPUT_RECEIVE   16\r
+#define PERFINFO_STREAMTRACE_SBE_DVRANALYSISINPUT_DELIVER   17\r
+#define PERFINFO_STREAMTRACE_SBE_DVRINPUTPIN_RECEIVE        18\r
+#define PERFINFO_STREAMTRACE_SBE_DVROUTPUTPIN_RECEIVE       19\r
+#define PERFINFO_STREAMTRACE_VMR_RENDER_TIME                20\r
+\r
+typedef struct _PERFINFO_DSHOW_STREAMTRACE {\r
+    ULONG        id;\r
+    ULONG        reserved;\r
+    ULONGLONG    dshowClock;\r
+    ULONGLONG    data[ 4 ];\r
+} PERFINFO_DSHOW_STREAMTRACE, *PPERFINFO_DSHOW_STREAMTRACE;\r
+\r
+typedef struct _PERFINFO_WMI_STREAMTRACE {\r
+    EVENT_TRACE_HEADER          header;\r
+    PERFINFO_DSHOW_STREAMTRACE  data;\r
+} PERFINFO_WMI_STREAMTRACE, *PPERFINFO_WMI_STREAMTRACE;\r
+\r
+\r
+typedef struct PERFINFO_DSHOW_AUDIORECV {\r
+    LONGLONG    streamTime ;\r
+    LONGLONG    sampleStart ;\r
+    LONGLONG    sampleStop ;\r
+    LONGLONG    hwduration ;\r
+    BOOL        discontinuity ;\r
+} PERFINFO_DSHOW_AUDIORECV, *PPERFINFO_DSHOW_AUDIORECV;\r
+\r
+typedef struct PERFINFO_WMI_AUDIORECV {\r
+    EVENT_TRACE_HEADER          header;\r
+    PERFINFO_DSHOW_AUDIORECV   data;\r
+} PERFINFO_WMI_AUDIORECV, *PPERFINFO_WMI_AUDIORECV;\r
+\r
+typedef struct PERFINFO_DSHOW_AUDIOSLAVE {\r
+    LONGLONG    masterClock ;\r
+    LONGLONG    slaveClock ;\r
+    LONGLONG    errorAccum ;\r
+    LONGLONG    lastHighErrorSeen ;\r
+    LONGLONG    lastLowErrorSeen ;\r
+} PERFINFO_DSHOW_AUDIOSLAVE, *PPERFINFO_DSHOW_AUDIOSLAVE;\r
+\r
+typedef struct PERFINFO_WMI_AUDIOSLAVE {\r
+    EVENT_TRACE_HEADER          header;\r
+    PERFINFO_DSHOW_AUDIOSLAVE   data;\r
+} PERFINFO_WMI_AUDIOSLAVE, *PPERFINFO_WMI_AUDIOSLAVE;\r
+\r
+typedef struct PERFINFO_DSHOW_AUDIOADDBREAK {\r
+    DWORD   iterNextWrite ;\r
+    DWORD   offsetNextWrite ;\r
+    DWORD   iterWrite ;\r
+    DWORD   offsetWrite ;\r
+} PERFINFO_DSHOW_AUDIOADDBREAK, *PPERFINFO_DSHOW_AUDIOADDBREAK;\r
+\r
+typedef struct PERFINFO_WMI_AUDIOADDBREAK {\r
+    EVENT_TRACE_HEADER              header;\r
+    PERFINFO_DSHOW_AUDIOADDBREAK    data;\r
+} PERFINFO_WMI_AUDIOADDBREAK, *PPERFINFO_WMI_AUDIOADDBREAK;\r
+\r
+#endif // _PREFSTRUCT_H_\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/pstream.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/pstream.cpp
new file mode 100644 (file)
index 0000000..d20171f
--- /dev/null
@@ -0,0 +1,197 @@
+//------------------------------------------------------------------------------\r
+// File: PStream.cpp\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+#include <strsafe.h>\r
+\r
+#ifdef PERF\r
+#include <measure.h>\r
+#endif\r
+// #include "pstream.h"  in streams.h\r
+\r
+//\r
+// Constructor\r
+//\r
+CPersistStream::CPersistStream(IUnknown *punk, __inout HRESULT *phr)\r
+    : mPS_fDirty(FALSE)\r
+{\r
+    mPS_dwFileVersion = GetSoftwareVersion();\r
+}\r
+\r
+\r
+//\r
+// Destructor\r
+//\r
+CPersistStream::~CPersistStream() {\r
+    // Nothing to do\r
+}\r
+\r
+#if 0\r
+SAMPLE CODE TO COPY - not active at the moment\r
+\r
+//\r
+// NonDelegatingQueryInterface\r
+//\r
+// This object supports IPersist & IPersistStream\r
+STDMETHODIMP CPersistStream::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    if (riid == IID_IPersist) {\r
+        return GetInterface((IPersist *) this, ppv);      // ???\r
+    }\r
+    else if (riid == IID_IPersistStream) {\r
+        return GetInterface((IPersistStream *) this, ppv);\r
+    }\r
+    else {\r
+        return CUnknown::NonDelegatingQueryInterface(riid, ppv);\r
+    }\r
+}\r
+#endif\r
+\r
+\r
+//\r
+// WriteToStream\r
+//\r
+// Writes to the stream (default action is to write nothing)\r
+HRESULT CPersistStream::WriteToStream(IStream *pStream)\r
+{\r
+    // You can override this to do things like\r
+    // hr = pStream->Write(MyStructure, sizeof(MyStructure), NULL);\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+\r
+HRESULT CPersistStream::ReadFromStream(IStream * pStream)\r
+{\r
+    // You can override this to do things like\r
+    // hr = pStream->Read(MyStructure, sizeof(MyStructure), NULL);\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+//\r
+// Load\r
+//\r
+// Load all the data from the given stream\r
+STDMETHODIMP CPersistStream::Load(LPSTREAM pStm)\r
+{\r
+    HRESULT hr;\r
+    // Load the version number then the data\r
+    mPS_dwFileVersion = ReadInt(pStm, hr);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    return ReadFromStream(pStm);\r
+}  // Load\r
+\r
+\r
+\r
+//\r
+// Save\r
+//\r
+// Save the contents of this Stream.\r
+STDMETHODIMP CPersistStream::Save(LPSTREAM pStm, BOOL fClearDirty)\r
+{\r
+\r
+    HRESULT hr = WriteInt(pStm, GetSoftwareVersion());\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    hr = WriteToStream(pStm);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    mPS_fDirty = !fClearDirty;\r
+\r
+    return hr;\r
+} // Save\r
+\r
+\r
+// WriteInt\r
+//\r
+// Writes an integer to an IStream as 11 UNICODE characters followed by one space.\r
+// You could use this for shorts or unsigneds or anything (up to 32 bits)\r
+// where the value isn't actually truncated by squeezing it into 32 bits.\r
+// Values such as (unsigned) 0x80000000 would come out as -2147483648\r
+// but would then load as 0x80000000 through ReadInt.  Cast as you please.\r
+\r
+STDAPI WriteInt(IStream *pIStream, int n)\r
+{\r
+    WCHAR Buff[13];  // Allows for trailing null that we don't write\r
+    (void)StringCchPrintfW(Buff, NUMELMS(Buff),L"%011d ",n);\r
+    return pIStream->Write(&(Buff[0]), 12*sizeof(WCHAR), NULL);\r
+} // WriteInt\r
+\r
+\r
+// ReadInt\r
+//\r
+// Reads an integer from an IStream.\r
+// Read as 4 bytes.  You could use this for shorts or unsigneds or anything\r
+// where the value isn't actually truncated by squeezing it into 32 bits\r
+// Striped down subset of what sscanf can do (without dragging in the C runtime)\r
+\r
+STDAPI_(int) ReadInt(IStream *pIStream, __out HRESULT &hr)\r
+{\r
+\r
+    int Sign = 1;\r
+    unsigned int n = 0;    // result wil be n*Sign\r
+    WCHAR wch;\r
+\r
+    hr = pIStream->Read( &wch, sizeof(wch), NULL);\r
+    if (FAILED(hr)) {\r
+        return 0;\r
+    }\r
+\r
+    if (wch==L'-'){\r
+        Sign = -1;\r
+        hr = pIStream->Read( &wch, sizeof(wch), NULL);\r
+        if (FAILED(hr)) {\r
+            return 0;\r
+        }\r
+    }\r
+\r
+    for( ; ; ) {\r
+        if (wch>=L'0' && wch<=L'9') {\r
+            n = 10*n+(int)(wch-L'0');\r
+        } else if (  wch == L' '\r
+                  || wch == L'\t'\r
+                  || wch == L'\r'\r
+                  || wch == L'\n'\r
+                  || wch == L'\0'\r
+                  ) {\r
+            break;\r
+        } else {\r
+            hr = VFW_E_INVALID_FILE_FORMAT;\r
+            return 0;\r
+        }\r
+\r
+        hr = pIStream->Read( &wch, sizeof(wch), NULL);\r
+        if (FAILED(hr)) {\r
+            return 0;\r
+        }\r
+    }\r
+\r
+    if (n==0x80000000 && Sign==-1) {\r
+        // This is the negative number that has no positive version!\r
+        return (int)n;\r
+    }\r
+    else return (int)n * Sign;\r
+} // ReadInt\r
+\r
+\r
+// The microsoft C/C++ compile generates level 4 warnings to the effect that\r
+// a particular inline function (from some base class) was not needed.\r
+// This line gets rid of hundreds of such unwanted messages and makes\r
+// -W4 compilation feasible:\r
+#pragma warning(disable: 4514)\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/pstream.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/pstream.h
new file mode 100644 (file)
index 0000000..04b6af6
--- /dev/null
@@ -0,0 +1,114 @@
+//------------------------------------------------------------------------------\r
+// File: PStream.h\r
+//\r
+// Desc: DirectShow base classes - defines a class for persistent properties\r
+//       of filters.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef __PSTREAM__\r
+#define __PSTREAM__\r
+\r
+// Base class for persistent properties of filters\r
+// (i.e. filter properties in saved graphs)\r
+\r
+// The simplest way to use this is:\r
+// 1. Arrange for your filter to inherit this class\r
+// 2. Implement in your class WriteToStream and ReadFromStream\r
+//    These will override the "do nothing" functions here.\r
+// 3. Change your NonDelegatingQueryInterface to handle IPersistStream\r
+// 4. Implement SizeMax to return the number of bytes of data you save.\r
+//    If you save UNICODE data, don't forget a char is 2 bytes.\r
+// 5. Whenever your data changes, call SetDirty()\r
+//\r
+// At some point you may decide to alter, or extend the format of your data.\r
+// At that point you will wish that you had a version number in all the old\r
+// saved graphs, so that you can tell, when you read them, whether they\r
+// represent the old or new form.  To assist you in this, this class\r
+// writes and reads a version number.\r
+// When it writes, it calls GetSoftwareVersion()  to enquire what version\r
+// of the software we have at the moment.  (In effect this is a version number\r
+// of the data layout in the file).  It writes this as the first thing in the data.\r
+// If you want to change the version, implement (override) GetSoftwareVersion().\r
+// It reads this from the file into mPS_dwFileVersion before calling ReadFromStream,\r
+// so in ReadFromStream you can check mPS_dwFileVersion to see if you are reading\r
+// an old version file.\r
+// Normally you should accept files whose version is no newer than the software\r
+// version that's reading them.\r
+\r
+\r
+// CPersistStream\r
+//\r
+// Implements IPersistStream.\r
+// See 'OLE Programmers Reference (Vol 1):Structured Storage Overview' for\r
+// more implementation information.\r
+class CPersistStream : public IPersistStream {\r
+    private:\r
+\r
+        // Internal state:\r
+\r
+    protected:\r
+        DWORD     mPS_dwFileVersion;         // version number of file (being read)\r
+        BOOL      mPS_fDirty;\r
+\r
+    public:\r
+\r
+        // IPersistStream methods\r
+\r
+        STDMETHODIMP IsDirty()\r
+            {return (mPS_fDirty ? S_OK : S_FALSE);}  // note FALSE means clean\r
+        STDMETHODIMP Load(LPSTREAM pStm);\r
+        STDMETHODIMP Save(LPSTREAM pStm, BOOL fClearDirty);\r
+        STDMETHODIMP GetSizeMax(__out ULARGE_INTEGER * pcbSize)\r
+                         // Allow 24 bytes for version.\r
+                         { pcbSize->QuadPart = 12*sizeof(WCHAR)+SizeMax(); return NOERROR; }\r
+\r
+        // implementation\r
+\r
+        CPersistStream(IUnknown *punk, __inout HRESULT *phr);\r
+        ~CPersistStream();\r
+\r
+        HRESULT SetDirty(BOOL fDirty)\r
+            { mPS_fDirty = fDirty; return NOERROR;}\r
+\r
+\r
+        // override to reveal IPersist & IPersistStream\r
+        // STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);\r
+\r
+        // --- IPersist ---\r
+\r
+        // You must override this to provide your own class id\r
+        STDMETHODIMP GetClassID(__out CLSID *pClsid) PURE;\r
+\r
+        // overrideable if you want\r
+        // file version number.  Override it if you ever change format\r
+        virtual DWORD GetSoftwareVersion(void) { return 0; }\r
+\r
+\r
+        //=========================================================================\r
+        // OVERRIDE THESE to read and write your data\r
+        // OVERRIDE THESE to read and write your data\r
+        // OVERRIDE THESE to read and write your data\r
+\r
+        virtual int SizeMax() {return 0;}\r
+        virtual HRESULT WriteToStream(IStream *pStream);\r
+        virtual HRESULT ReadFromStream(IStream *pStream);\r
+        //=========================================================================\r
+\r
+    private:\r
+\r
+};\r
+\r
+\r
+// --- Useful helpers ---\r
+\r
+\r
+// Writes an int to an IStream as UNICODE.\r
+STDAPI WriteInt(IStream *pIStream, int n);\r
+\r
+// inverse of WriteInt\r
+STDAPI_(int) ReadInt(IStream *pIStream, __out HRESULT &hr);\r
+\r
+#endif // __PSTREAM__\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/pullpin.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/pullpin.cpp
new file mode 100644 (file)
index 0000000..a197ba5
--- /dev/null
@@ -0,0 +1,588 @@
+//------------------------------------------------------------------------------\r
+// File: PullPin.cpp\r
+//\r
+// Desc: DirectShow base classes - implements CPullPin class that pulls data\r
+//       from IAsyncReader.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+#include "pullpin.h"\r
+\r
+#ifdef DXMPERF\r
+#include "dxmperf.h"\r
+#endif // DXMPERF\r
+\r
+\r
+CPullPin::CPullPin()\r
+  : m_pReader(NULL),\r
+    m_pAlloc(NULL),\r
+    m_State(TM_Exit)\r
+{\r
+#ifdef DXMPERF\r
+       PERFLOG_CTOR( L"CPullPin", this );\r
+#endif // DXMPERF\r
+\r
+}\r
+\r
+CPullPin::~CPullPin()\r
+{\r
+    Disconnect();\r
+\r
+#ifdef DXMPERF\r
+       PERFLOG_DTOR( L"CPullPin", this );\r
+#endif // DXMPERF\r
+\r
+}\r
+\r
+// returns S_OK if successfully connected to an IAsyncReader interface\r
+// from this object\r
+// Optional allocator should be proposed as a preferred allocator if\r
+// necessary\r
+HRESULT\r
+CPullPin::Connect(IUnknown* pUnk, IMemAllocator* pAlloc, BOOL bSync)\r
+{\r
+    CAutoLock lock(&m_AccessLock);\r
+\r
+    if (m_pReader) {\r
+       return VFW_E_ALREADY_CONNECTED;\r
+    }\r
+\r
+    HRESULT hr = pUnk->QueryInterface(IID_IAsyncReader, (void**)&m_pReader);\r
+    if (FAILED(hr)) {\r
+\r
+#ifdef DXMPERF\r
+               {\r
+               AM_MEDIA_TYPE * pmt = NULL;\r
+               PERFLOG_CONNECT( this, pUnk, hr, pmt );\r
+               }\r
+#endif // DXMPERF\r
+\r
+       return(hr);\r
+    }\r
+\r
+    hr = DecideAllocator(pAlloc, NULL);\r
+    if (FAILED(hr)) {\r
+       Disconnect();\r
+\r
+#ifdef DXMPERF\r
+               {\r
+               AM_MEDIA_TYPE * pmt = NULL;\r
+               PERFLOG_CONNECT( this, pUnk, hr, pmt );\r
+               }\r
+#endif // DXMPERF\r
+\r
+       return hr;\r
+    }\r
+\r
+    LONGLONG llTotal, llAvail;\r
+    hr = m_pReader->Length(&llTotal, &llAvail);\r
+    if (FAILED(hr)) {\r
+       Disconnect();\r
+\r
+#ifdef DXMPERF\r
+               {\r
+               AM_MEDIA_TYPE * pmt = NULL;\r
+               PERFLOG_CONNECT( this, pUnk, hr, pmt );\r
+               }\r
+#endif\r
+\r
+       return hr;\r
+    }\r
+\r
+    // convert from file position to reference time\r
+    m_tDuration = llTotal * UNITS;\r
+    m_tStop = m_tDuration;\r
+    m_tStart = 0;\r
+\r
+    m_bSync = bSync;\r
+\r
+#ifdef DXMPERF\r
+       {\r
+       AM_MEDIA_TYPE * pmt = NULL;\r
+       PERFLOG_CONNECT( this, pUnk, S_OK, pmt );\r
+       }\r
+#endif // DXMPERF\r
+\r
+\r
+    return S_OK;\r
+}\r
+\r
+// disconnect any connection made in Connect\r
+HRESULT\r
+CPullPin::Disconnect()\r
+{\r
+    CAutoLock lock(&m_AccessLock);\r
+\r
+    StopThread();\r
+\r
+\r
+#ifdef DXMPERF\r
+       PERFLOG_DISCONNECT( this, m_pReader, S_OK );\r
+#endif // DXMPERF\r
+\r
+\r
+    if (m_pReader) {\r
+       m_pReader->Release();\r
+       m_pReader = NULL;\r
+    }\r
+\r
+    if (m_pAlloc) {\r
+       m_pAlloc->Release();\r
+       m_pAlloc = NULL;\r
+    }\r
+\r
+    return S_OK;\r
+}\r
+\r
+// agree an allocator using RequestAllocator - optional\r
+// props param specifies your requirements (non-zero fields).\r
+// returns an error code if fail to match requirements.\r
+// optional IMemAllocator interface is offered as a preferred allocator\r
+// but no error occurs if it can't be met.\r
+HRESULT\r
+CPullPin::DecideAllocator(\r
+    IMemAllocator * pAlloc,\r
+    __inout_opt ALLOCATOR_PROPERTIES * pProps)\r
+{\r
+    ALLOCATOR_PROPERTIES *pRequest;\r
+    ALLOCATOR_PROPERTIES Request;\r
+    if (pProps == NULL) {\r
+       Request.cBuffers = 3;\r
+       Request.cbBuffer = 64*1024;\r
+       Request.cbAlign = 0;\r
+       Request.cbPrefix = 0;\r
+       pRequest = &Request;\r
+    } else {\r
+       pRequest = pProps;\r
+    }\r
+    HRESULT hr = m_pReader->RequestAllocator(\r
+                   pAlloc,\r
+                   pRequest,\r
+                   &m_pAlloc);\r
+    return hr;\r
+}\r
+\r
+// start pulling data\r
+HRESULT\r
+CPullPin::Active(void)\r
+{\r
+    ASSERT(!ThreadExists());\r
+    return StartThread();\r
+}\r
+\r
+// stop pulling data\r
+HRESULT\r
+CPullPin::Inactive(void)\r
+{\r
+    StopThread();\r
+\r
+    return S_OK;\r
+}\r
+\r
+HRESULT\r
+CPullPin::Seek(REFERENCE_TIME tStart, REFERENCE_TIME tStop)\r
+{\r
+    CAutoLock lock(&m_AccessLock);\r
+\r
+    ThreadMsg AtStart = m_State;\r
+\r
+    if (AtStart == TM_Start) {\r
+       BeginFlush();\r
+       PauseThread();\r
+       EndFlush();\r
+    }\r
+\r
+    m_tStart = tStart;\r
+    m_tStop = tStop;\r
+\r
+    HRESULT hr = S_OK;\r
+    if (AtStart == TM_Start) {\r
+       hr = StartThread();\r
+    }\r
+\r
+    return hr;\r
+}\r
+\r
+HRESULT\r
+CPullPin::Duration(__out REFERENCE_TIME* ptDuration)\r
+{\r
+    *ptDuration = m_tDuration;\r
+    return S_OK;\r
+}\r
+\r
+\r
+HRESULT\r
+CPullPin::StartThread()\r
+{\r
+    CAutoLock lock(&m_AccessLock);\r
+\r
+    if (!m_pAlloc || !m_pReader) {\r
+       return E_UNEXPECTED;\r
+    }\r
+\r
+    HRESULT hr;\r
+    if (!ThreadExists()) {\r
+\r
+       // commit allocator\r
+       hr = m_pAlloc->Commit();\r
+       if (FAILED(hr)) {\r
+           return hr;\r
+       }\r
+\r
+       // start thread\r
+       if (!Create()) {\r
+           return E_FAIL;\r
+       }\r
+    }\r
+\r
+    m_State = TM_Start;\r
+    hr = (HRESULT) CallWorker(m_State);\r
+    return hr;\r
+}\r
+\r
+HRESULT\r
+CPullPin::PauseThread()\r
+{\r
+    CAutoLock lock(&m_AccessLock);\r
+\r
+    if (!ThreadExists()) {\r
+       return E_UNEXPECTED;\r
+    }\r
+\r
+    // need to flush to ensure the thread is not blocked\r
+    // in WaitForNext\r
+    HRESULT hr = m_pReader->BeginFlush();\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    m_State = TM_Pause;\r
+    hr = CallWorker(TM_Pause);\r
+\r
+    m_pReader->EndFlush();\r
+    return hr;\r
+}\r
+\r
+HRESULT\r
+CPullPin::StopThread()\r
+{\r
+    CAutoLock lock(&m_AccessLock);\r
+\r
+    if (!ThreadExists()) {\r
+       return S_FALSE;\r
+    }\r
+\r
+    // need to flush to ensure the thread is not blocked\r
+    // in WaitForNext\r
+    HRESULT hr = m_pReader->BeginFlush();\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    m_State = TM_Exit;\r
+    hr = CallWorker(TM_Exit);\r
+\r
+    m_pReader->EndFlush();\r
+\r
+    // wait for thread to completely exit\r
+    Close();\r
+\r
+    // decommit allocator\r
+    if (m_pAlloc) {\r
+       m_pAlloc->Decommit();\r
+    }\r
+\r
+    return S_OK;\r
+}\r
+\r
+\r
+DWORD\r
+CPullPin::ThreadProc(void)\r
+{\r
+    while(1) {\r
+       DWORD cmd = GetRequest();\r
+       switch(cmd) {\r
+       case TM_Exit:\r
+           Reply(S_OK);\r
+           return 0;\r
+\r
+       case TM_Pause:\r
+           // we are paused already\r
+           Reply(S_OK);\r
+           break;\r
+\r
+       case TM_Start:\r
+           Reply(S_OK);\r
+           Process();\r
+           break;\r
+       }\r
+\r
+       // at this point, there should be no outstanding requests on the\r
+       // upstream filter.\r
+       // We should force begin/endflush to ensure that this is true.\r
+       // !!!Note that we may currently be inside a BeginFlush/EndFlush pair\r
+       // on another thread, but the premature EndFlush will do no harm now\r
+       // that we are idle.\r
+       m_pReader->BeginFlush();\r
+       CleanupCancelled();\r
+       m_pReader->EndFlush();\r
+    }\r
+}\r
+\r
+HRESULT\r
+CPullPin::QueueSample(\r
+    __inout REFERENCE_TIME& tCurrent,\r
+    REFERENCE_TIME tAlignStop,\r
+    BOOL bDiscontinuity\r
+    )\r
+{\r
+    IMediaSample* pSample;\r
+\r
+    HRESULT hr = m_pAlloc->GetBuffer(&pSample, NULL, NULL, 0);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    LONGLONG tStopThis = tCurrent + (pSample->GetSize() * UNITS);\r
+    if (tStopThis > tAlignStop) {\r
+       tStopThis = tAlignStop;\r
+    }\r
+    pSample->SetTime(&tCurrent, &tStopThis);\r
+    tCurrent = tStopThis;\r
+\r
+    pSample->SetDiscontinuity(bDiscontinuity);\r
+\r
+    hr = m_pReader->Request(\r
+                       pSample,\r
+                       0);\r
+    if (FAILED(hr)) {\r
+       pSample->Release();\r
+\r
+       CleanupCancelled();\r
+       OnError(hr);\r
+    }\r
+    return hr;\r
+}\r
+\r
+HRESULT\r
+CPullPin::CollectAndDeliver(\r
+    REFERENCE_TIME tStart,\r
+    REFERENCE_TIME tStop)\r
+{\r
+    IMediaSample* pSample = NULL;   // better be sure pSample is set\r
+    DWORD_PTR dwUnused;\r
+    HRESULT hr = m_pReader->WaitForNext(\r
+                       INFINITE,\r
+                       &pSample,\r
+                       &dwUnused);\r
+    if (FAILED(hr)) {\r
+       if (pSample) {\r
+           pSample->Release();\r
+       }\r
+    } else {\r
+       hr = DeliverSample(pSample, tStart, tStop);\r
+    }\r
+    if (FAILED(hr)) {\r
+       CleanupCancelled();\r
+       OnError(hr);\r
+    }\r
+    return hr;\r
+\r
+}\r
+\r
+HRESULT\r
+CPullPin::DeliverSample(\r
+    IMediaSample* pSample,\r
+    REFERENCE_TIME tStart,\r
+    REFERENCE_TIME tStop\r
+    )\r
+{\r
+    // fix up sample if past actual stop (for sector alignment)\r
+    REFERENCE_TIME t1, t2;\r
+    if (S_OK == pSample->GetTime(&t1, &t2)) {\r
+        if (t2 > tStop) {\r
+            t2 = tStop;\r
+        }\r
+\r
+        // adjust times to be relative to (aligned) start time\r
+        t1 -= tStart;\r
+        t2 -= tStart;\r
+        HRESULT hr = pSample->SetTime(&t1, &t2);\r
+        if (FAILED(hr)) {\r
+            return hr;\r
+        }\r
+    }\r
+\r
+#ifdef DXMPERF\r
+       {\r
+       AM_MEDIA_TYPE * pmt = NULL;\r
+       pSample->GetMediaType( &pmt );\r
+       PERFLOG_RECEIVE( L"CPullPin", m_pReader, this, pSample, pmt );\r
+       }\r
+#endif\r
+\r
+    HRESULT hr = Receive(pSample);\r
+    pSample->Release();\r
+    return hr;\r
+}\r
+\r
+void\r
+CPullPin::Process(void)\r
+{\r
+    // is there anything to do?\r
+    if (m_tStop <= m_tStart) {\r
+       EndOfStream();\r
+       return;\r
+    }\r
+\r
+    BOOL bDiscontinuity = TRUE;\r
+\r
+    // if there is more than one sample at the allocator,\r
+    // then try to queue 2 at once in order to overlap.\r
+    // -- get buffer count and required alignment\r
+    ALLOCATOR_PROPERTIES Actual;\r
+    HRESULT hr = m_pAlloc->GetProperties(&Actual);\r
+\r
+    // align the start position downwards\r
+    REFERENCE_TIME tStart = AlignDown(m_tStart / UNITS, Actual.cbAlign) * UNITS;\r
+    REFERENCE_TIME tCurrent = tStart;\r
+\r
+    REFERENCE_TIME tStop = m_tStop;\r
+    if (tStop > m_tDuration) {\r
+       tStop = m_tDuration;\r
+    }\r
+\r
+    // align the stop position - may be past stop, but that\r
+    // doesn't matter\r
+    REFERENCE_TIME tAlignStop = AlignUp(tStop / UNITS, Actual.cbAlign) * UNITS;\r
+\r
+\r
+    DWORD dwRequest;\r
+\r
+    if (!m_bSync) {\r
+\r
+       //  Break out of the loop either if we get to the end or we're asked\r
+       //  to do something else\r
+       while (tCurrent < tAlignStop) {\r
+\r
+           // Break out without calling EndOfStream if we're asked to\r
+           // do something different\r
+           if (CheckRequest(&dwRequest)) {\r
+               return;\r
+           }\r
+\r
+           // queue a first sample\r
+           if (Actual.cBuffers > 1) {\r
+\r
+               hr = QueueSample(tCurrent, tAlignStop, TRUE);\r
+               bDiscontinuity = FALSE;\r
+\r
+               if (FAILED(hr)) {\r
+                   return;\r
+               }\r
+           }\r
+\r
+\r
+\r
+           // loop queueing second and waiting for first..\r
+           while (tCurrent < tAlignStop) {\r
+\r
+               hr = QueueSample(tCurrent, tAlignStop, bDiscontinuity);\r
+               bDiscontinuity = FALSE;\r
+\r
+               if (FAILED(hr)) {\r
+                   return;\r
+               }\r
+\r
+               hr = CollectAndDeliver(tStart, tStop);\r
+               if (S_OK != hr) {\r
+\r
+                   // stop if error, or if downstream filter said\r
+                   // to stop.\r
+                   return;\r
+               }\r
+           }\r
+\r
+           if (Actual.cBuffers > 1) {\r
+               hr = CollectAndDeliver(tStart, tStop);\r
+               if (FAILED(hr)) {\r
+                   return;\r
+               }\r
+           }\r
+       }\r
+    } else {\r
+\r
+       // sync version of above loop\r
+       while (tCurrent < tAlignStop) {\r
+\r
+           // Break out without calling EndOfStream if we're asked to\r
+           // do something different\r
+           if (CheckRequest(&dwRequest)) {\r
+               return;\r
+           }\r
+\r
+           IMediaSample* pSample;\r
+\r
+           hr = m_pAlloc->GetBuffer(&pSample, NULL, NULL, 0);\r
+           if (FAILED(hr)) {\r
+               OnError(hr);\r
+               return;\r
+           }\r
+\r
+           LONGLONG tStopThis = tCurrent + (pSample->GetSize() * UNITS);\r
+           if (tStopThis > tAlignStop) {\r
+               tStopThis = tAlignStop;\r
+           }\r
+           pSample->SetTime(&tCurrent, &tStopThis);\r
+           tCurrent = tStopThis;\r
+\r
+           if (bDiscontinuity) {\r
+               pSample->SetDiscontinuity(TRUE);\r
+               bDiscontinuity = FALSE;\r
+           }\r
+\r
+           hr = m_pReader->SyncReadAligned(pSample);\r
+\r
+           if (FAILED(hr)) {\r
+               pSample->Release();\r
+               OnError(hr);\r
+               return;\r
+           }\r
+\r
+           hr = DeliverSample(pSample, tStart, tStop);\r
+           if (hr != S_OK) {\r
+               if (FAILED(hr)) {\r
+                   OnError(hr);\r
+               }\r
+               return;\r
+           }\r
+       }\r
+    }\r
+\r
+    EndOfStream();\r
+}\r
+\r
+// after a flush, cancelled i/o will be waiting for collection\r
+// and release\r
+void\r
+CPullPin::CleanupCancelled(void)\r
+{\r
+    while (1) {\r
+       IMediaSample * pSample;\r
+       DWORD_PTR dwUnused;\r
+\r
+       HRESULT hr = m_pReader->WaitForNext(\r
+                           0,          // no wait\r
+                           &pSample,\r
+                           &dwUnused);\r
+       if(pSample) {\r
+           pSample->Release();\r
+       } else {\r
+           // no more samples\r
+           return;\r
+       }\r
+    }\r
+}\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/pullpin.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/pullpin.h
new file mode 100644 (file)
index 0000000..03ad40e
--- /dev/null
@@ -0,0 +1,152 @@
+//------------------------------------------------------------------------------\r
+// File: PullPin.h\r
+//\r
+// Desc: DirectShow base classes - defines CPullPin class.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef __PULLPIN_H__\r
+#define __PULLPIN_H__\r
+\r
+//\r
+// CPullPin\r
+//\r
+// object supporting pulling data from an IAsyncReader interface.\r
+// Given a start/stop position, calls a pure Receive method with each\r
+// IMediaSample received.\r
+//\r
+// This is essentially for use in a MemInputPin when it finds itself\r
+// connected to an IAsyncReader pin instead of a pushing pin.\r
+//\r
+\r
+class CPullPin : public CAMThread\r
+{\r
+    IAsyncReader*       m_pReader;\r
+    REFERENCE_TIME      m_tStart;\r
+    REFERENCE_TIME      m_tStop;\r
+    REFERENCE_TIME      m_tDuration;\r
+    BOOL                m_bSync;\r
+\r
+    enum ThreadMsg {\r
+       TM_Pause,       // stop pulling and wait for next message\r
+       TM_Start,       // start pulling\r
+       TM_Exit,        // stop and exit\r
+    };\r
+\r
+    ThreadMsg m_State;\r
+\r
+    // override pure thread proc from CAMThread\r
+    DWORD ThreadProc(void);\r
+\r
+    // running pull method (check m_bSync)\r
+    void Process(void);\r
+\r
+    // clean up any cancelled i/o after a flush\r
+    void CleanupCancelled(void);\r
+\r
+    // suspend thread from pulling, eg during seek\r
+    HRESULT PauseThread();\r
+\r
+    // start thread pulling - create thread if necy\r
+    HRESULT StartThread();\r
+\r
+    // stop and close thread\r
+    HRESULT StopThread();\r
+\r
+    // called from ProcessAsync to queue and collect requests\r
+    HRESULT QueueSample(\r
+               __inout REFERENCE_TIME& tCurrent,\r
+               REFERENCE_TIME tAlignStop,\r
+               BOOL bDiscontinuity);\r
+\r
+    HRESULT CollectAndDeliver(\r
+               REFERENCE_TIME tStart,\r
+               REFERENCE_TIME tStop);\r
+\r
+    HRESULT DeliverSample(\r
+               IMediaSample* pSample,\r
+               REFERENCE_TIME tStart,\r
+               REFERENCE_TIME tStop);\r
+\r
+protected:\r
+    IMemAllocator *     m_pAlloc;\r
+\r
+public:\r
+    CPullPin();\r
+    virtual ~CPullPin();\r
+\r
+    // returns S_OK if successfully connected to an IAsyncReader interface\r
+    // from this object\r
+    // Optional allocator should be proposed as a preferred allocator if\r
+    // necessary\r
+    // bSync is TRUE if we are to use sync reads instead of the\r
+    // async methods.\r
+    HRESULT Connect(IUnknown* pUnk, IMemAllocator* pAlloc, BOOL bSync);\r
+\r
+    // disconnect any connection made in Connect\r
+    HRESULT Disconnect();\r
+\r
+    // agree an allocator using RequestAllocator - optional\r
+    // props param specifies your requirements (non-zero fields).\r
+    // returns an error code if fail to match requirements.\r
+    // optional IMemAllocator interface is offered as a preferred allocator\r
+    // but no error occurs if it can't be met.\r
+    virtual HRESULT DecideAllocator(\r
+               IMemAllocator* pAlloc,\r
+               __inout_opt ALLOCATOR_PROPERTIES * pProps);\r
+\r
+    // set start and stop position. if active, will start immediately at\r
+    // the new position. Default is 0 to duration\r
+    HRESULT Seek(REFERENCE_TIME tStart, REFERENCE_TIME tStop);\r
+\r
+    // return the total duration\r
+    HRESULT Duration(__out REFERENCE_TIME* ptDuration);\r
+\r
+    // start pulling data\r
+    HRESULT Active(void);\r
+\r
+    // stop pulling data\r
+    HRESULT Inactive(void);\r
+\r
+    // helper functions\r
+    LONGLONG AlignDown(LONGLONG ll, LONG lAlign) {\r
+       // aligning downwards is just truncation\r
+       return ll & ~(lAlign-1);\r
+    };\r
+\r
+    LONGLONG AlignUp(LONGLONG ll, LONG lAlign) {\r
+       // align up: round up to next boundary\r
+       return (ll + (lAlign -1)) & ~(lAlign -1);\r
+    };\r
+\r
+    // GetReader returns the (addrefed) IAsyncReader interface\r
+    // for SyncRead etc\r
+    IAsyncReader* GetReader() {\r
+       m_pReader->AddRef();\r
+       return m_pReader;\r
+    };\r
+\r
+    // -- pure --\r
+\r
+    // override this to handle data arrival\r
+    // return value other than S_OK will stop data\r
+    virtual HRESULT Receive(IMediaSample*) PURE;\r
+\r
+    // override this to handle end-of-stream\r
+    virtual HRESULT EndOfStream(void) PURE;\r
+\r
+    // called on runtime errors that will have caused pulling\r
+    // to stop\r
+    // these errors are all returned from the upstream filter, who\r
+    // will have already reported any errors to the filtergraph.\r
+    virtual void OnError(HRESULT hr) PURE;\r
+\r
+    // flush this pin and all downstream\r
+    virtual HRESULT BeginFlush() PURE;\r
+    virtual HRESULT EndFlush() PURE;\r
+\r
+};\r
+\r
+#endif //__PULLPIN_H__\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/refclock.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/refclock.cpp
new file mode 100644 (file)
index 0000000..8ae25f4
--- /dev/null
@@ -0,0 +1,402 @@
+//------------------------------------------------------------------------------\r
+// File: RefClock.cpp\r
+//\r
+// Desc: DirectShow base classes - implements the IReferenceClock interface.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+#include <limits.h>\r
+\r
+#ifdef DXMPERF\r
+#include "dxmperf.h"\r
+#endif // DXMPERF\r
+\r
+\r
+// 'this' used in constructor list\r
+#pragma warning(disable:4355)\r
+\r
+\r
+STDMETHODIMP CBaseReferenceClock::NonDelegatingQueryInterface(\r
+    REFIID riid,\r
+    __deref_out void ** ppv)\r
+{\r
+    HRESULT hr;\r
+\r
+    if (riid == IID_IReferenceClock)\r
+    {\r
+        hr = GetInterface((IReferenceClock *) this, ppv);\r
+    }\r
+    else if (riid == IID_IReferenceClockTimerControl)\r
+    {\r
+        hr = GetInterface((IReferenceClockTimerControl *) this, ppv);\r
+    }\r
+    else\r
+    {\r
+        hr = CUnknown::NonDelegatingQueryInterface(riid, ppv);\r
+    }\r
+    return hr;\r
+}\r
+\r
+CBaseReferenceClock::~CBaseReferenceClock()\r
+{\r
+#ifdef DXMPERF\r
+    PERFLOG_DTOR( L"CBaseReferenceClock", (IReferenceClock *) this );\r
+#endif // DXMPERF\r
+\r
+    if (m_TimerResolution) timeEndPeriod(m_TimerResolution);\r
+\r
+    if (m_pSchedule)\r
+    {\r
+        m_pSchedule->DumpLinkedList();\r
+    }\r
+\r
+    if (m_hThread)\r
+    {\r
+        m_bAbort = TRUE;\r
+        TriggerThread();\r
+        WaitForSingleObject( m_hThread, INFINITE );\r
+        EXECUTE_ASSERT( CloseHandle(m_hThread) );\r
+        m_hThread = 0;\r
+        EXECUTE_ASSERT( CloseHandle(m_pSchedule->GetEvent()) );\r
+       delete m_pSchedule;\r
+    }\r
+}\r
+\r
+// A derived class may supply a hThreadEvent if it has its own thread that will take care\r
+// of calling the schedulers Advise method.  (Refere to CBaseReferenceClock::AdviseThread()\r
+// to see what such a thread has to do.)\r
+CBaseReferenceClock::CBaseReferenceClock( __in_opt LPCTSTR pName, \r
+                                          __inout_opt LPUNKNOWN pUnk, \r
+                                          __inout HRESULT *phr, \r
+                                          __inout_opt CAMSchedule * pShed )\r
+: CUnknown( pName, pUnk )\r
+, m_rtLastGotTime(0)\r
+, m_TimerResolution(0)\r
+, m_bAbort( FALSE )\r
+, m_pSchedule( pShed ? pShed : new CAMSchedule(CreateEvent(NULL, FALSE, FALSE, NULL)) )\r
+, m_hThread(0)\r
+{\r
+\r
+#ifdef DXMPERF\r
+    PERFLOG_CTOR( pName ? pName : L"CBaseReferenceClock", (IReferenceClock *) this );\r
+#endif // DXMPERF\r
+\r
+    ASSERT(m_pSchedule);\r
+    if (!m_pSchedule)\r
+    {\r
+        *phr = E_OUTOFMEMORY;\r
+    }\r
+    else\r
+    {\r
+        // Set up the highest resolution timer we can manage\r
+        TIMECAPS tc;\r
+        m_TimerResolution = (TIMERR_NOERROR == timeGetDevCaps(&tc, sizeof(tc)))\r
+                            ? tc.wPeriodMin\r
+                            : 1;\r
+\r
+        timeBeginPeriod(m_TimerResolution);\r
+\r
+        /* Initialise our system times - the derived clock should set the right values */\r
+        m_dwPrevSystemTime = timeGetTime();\r
+        m_rtPrivateTime = (UNITS / MILLISECONDS) * m_dwPrevSystemTime;\r
+\r
+        #ifdef PERF\r
+            m_idGetSystemTime = MSR_REGISTER(TEXT("CBaseReferenceClock::GetTime"));\r
+        #endif\r
+\r
+        if ( !pShed )\r
+        {\r
+            DWORD ThreadID;\r
+            m_hThread = ::CreateThread(NULL,                  // Security attributes\r
+                                       (DWORD) 0,             // Initial stack size\r
+                                       AdviseThreadFunction,  // Thread start address\r
+                                       (LPVOID) this,         // Thread parameter\r
+                                       (DWORD) 0,             // Creation flags\r
+                                       &ThreadID);            // Thread identifier\r
+\r
+            if (m_hThread)\r
+            {\r
+                SetThreadPriority( m_hThread, THREAD_PRIORITY_TIME_CRITICAL );\r
+            }\r
+            else\r
+            {\r
+                *phr = E_FAIL;\r
+                EXECUTE_ASSERT( CloseHandle(m_pSchedule->GetEvent()) );\r
+                delete m_pSchedule;\r
+                m_pSchedule = NULL;\r
+            }\r
+        }\r
+    }\r
+}\r
+\r
+void CBaseReferenceClock::Restart (IN REFERENCE_TIME rtMinTime)\r
+{\r
+    Lock();\r
+    m_rtLastGotTime = rtMinTime ;\r
+    Unlock();\r
+}\r
+\r
+STDMETHODIMP CBaseReferenceClock::GetTime(__out REFERENCE_TIME *pTime)\r
+{\r
+    HRESULT hr;\r
+    if (pTime)\r
+    {\r
+        REFERENCE_TIME rtNow;\r
+        Lock();\r
+        rtNow = GetPrivateTime();\r
+        if (rtNow > m_rtLastGotTime)\r
+        {\r
+            m_rtLastGotTime = rtNow;\r
+            hr = S_OK;\r
+        }\r
+        else\r
+        {\r
+            hr = S_FALSE;\r
+        }\r
+        *pTime = m_rtLastGotTime;\r
+        Unlock();\r
+        MSR_INTEGER(m_idGetSystemTime, LONG((*pTime) / (UNITS/MILLISECONDS)) );\r
+\r
+#ifdef DXMPERF\r
+        PERFLOG_GETTIME( (IReferenceClock *) this, *pTime );\r
+#endif // DXMPERF\r
+\r
+    }\r
+    else hr = E_POINTER;\r
+\r
+    return hr;\r
+}\r
+\r
+/* Ask for an async notification that a time has elapsed */\r
+\r
+STDMETHODIMP CBaseReferenceClock::AdviseTime(\r
+    REFERENCE_TIME baseTime,         // base reference time\r
+    REFERENCE_TIME streamTime,       // stream offset time\r
+    HEVENT hEvent,                   // advise via this event\r
+    __out DWORD_PTR *pdwAdviseCookie)// where your cookie goes\r
+{\r
+    CheckPointer(pdwAdviseCookie, E_POINTER);\r
+    *pdwAdviseCookie = 0;\r
+\r
+    // Check that the event is not already set\r
+    ASSERT(WAIT_TIMEOUT == WaitForSingleObject(HANDLE(hEvent),0));\r
+\r
+    HRESULT hr;\r
+\r
+    const REFERENCE_TIME lRefTime = baseTime + streamTime;\r
+    if ( lRefTime <= 0 || lRefTime == MAX_TIME )\r
+    {\r
+        hr = E_INVALIDARG;\r
+    }\r
+    else\r
+    {\r
+        *pdwAdviseCookie = m_pSchedule->AddAdvisePacket( lRefTime, 0, HANDLE(hEvent), FALSE );\r
+        hr = *pdwAdviseCookie ? NOERROR : E_OUTOFMEMORY;\r
+    }\r
+    return hr;\r
+}\r
+\r
+\r
+/* Ask for an asynchronous periodic notification that a time has elapsed */\r
+\r
+STDMETHODIMP CBaseReferenceClock::AdvisePeriodic(\r
+    REFERENCE_TIME StartTime,         // starting at this time\r
+    REFERENCE_TIME PeriodTime,        // time between notifications\r
+    HSEMAPHORE hSemaphore,            // advise via a semaphore\r
+    __out DWORD_PTR *pdwAdviseCookie) // where your cookie goes\r
+{\r
+    CheckPointer(pdwAdviseCookie, E_POINTER);\r
+    *pdwAdviseCookie = 0;\r
+\r
+    HRESULT hr;\r
+    if (StartTime > 0 && PeriodTime > 0 && StartTime != MAX_TIME )\r
+    {\r
+        *pdwAdviseCookie = m_pSchedule->AddAdvisePacket( StartTime, PeriodTime, HANDLE(hSemaphore), TRUE );\r
+        hr = *pdwAdviseCookie ? NOERROR : E_OUTOFMEMORY;\r
+    }\r
+    else hr = E_INVALIDARG;\r
+\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP CBaseReferenceClock::Unadvise(DWORD_PTR dwAdviseCookie)\r
+{\r
+    return m_pSchedule->Unadvise(dwAdviseCookie);\r
+}\r
+\r
+\r
+REFERENCE_TIME CBaseReferenceClock::GetPrivateTime()\r
+{\r
+    CAutoLock cObjectLock(this);\r
+\r
+\r
+    /* If the clock has wrapped then the current time will be less than\r
+     * the last time we were notified so add on the extra milliseconds\r
+     *\r
+     * The time period is long enough so that the likelihood of\r
+     * successive calls spanning the clock cycle is not considered.\r
+     */\r
+\r
+    DWORD dwTime = timeGetTime();\r
+    {\r
+        m_rtPrivateTime += Int32x32To64(UNITS / MILLISECONDS, (DWORD)(dwTime - m_dwPrevSystemTime));\r
+        m_dwPrevSystemTime = dwTime;\r
+    }\r
+\r
+    return m_rtPrivateTime;\r
+}\r
+\r
+\r
+/* Adjust the current time by the input value.  This allows an\r
+   external time source to work out some of the latency of the clock\r
+   system and adjust the "current" time accordingly.  The intent is\r
+   that the time returned to the user is synchronised to a clock\r
+   source and allows drift to be catered for.\r
+\r
+   For example: if the clock source detects a drift it can pass a delta\r
+   to the current time rather than having to set an explicit time.\r
+*/\r
+\r
+STDMETHODIMP CBaseReferenceClock::SetTimeDelta(const REFERENCE_TIME & TimeDelta)\r
+{\r
+#ifdef DEBUG\r
+\r
+    // Just break if passed an improper time delta value\r
+    LONGLONG llDelta = TimeDelta > 0 ? TimeDelta : -TimeDelta;\r
+    if (llDelta > UNITS * 1000) {\r
+        DbgLog((LOG_TRACE, 0, TEXT("Bad Time Delta")));\r
+        //DebugBreak();\r
+    }\r
+\r
+    // We're going to calculate a "severity" for the time change. Max -1\r
+    // min 8.  We'll then use this as the debug logging level for a\r
+    // debug log message.\r
+    const LONG usDelta = LONG(TimeDelta/10);      // Delta in micro-secs\r
+\r
+    DWORD delta        = abs(usDelta);            // varying delta\r
+    // Severity == 8 - ceil(log<base 8>(abs( micro-secs delta)))\r
+    int   Severity     = 8;\r
+    while ( delta > 0 )\r
+    {\r
+        delta >>= 3;                              // div 8\r
+        Severity--;\r
+    }\r
+\r
+    // Sev == 0 => > 2 second delta!\r
+    DbgLog((LOG_TIMING, Severity < 0 ? 0 : Severity,\r
+        TEXT("Sev %2i: CSystemClock::SetTimeDelta(%8ld us) %lu -> %lu ms."),\r
+        Severity, usDelta, DWORD(ConvertToMilliseconds(m_rtPrivateTime)),\r
+        DWORD(ConvertToMilliseconds(TimeDelta+m_rtPrivateTime)) ));\r
+\r
+    // Don't want the DbgBreak to fire when running stress on debug-builds.\r
+    #ifdef BREAK_ON_SEVERE_TIME_DELTA\r
+        if (Severity < 0)\r
+            DbgBreakPoint(TEXT("SetTimeDelta > 16 seconds!"),\r
+                          TEXT(__FILE__),__LINE__);\r
+    #endif\r
+\r
+#endif\r
+\r
+    CAutoLock cObjectLock(this);\r
+    m_rtPrivateTime += TimeDelta;\r
+    // If time goes forwards, and we have advises, then we need to\r
+    // trigger the thread so that it can re-evaluate its wait time.\r
+    // Since we don't want the cost of the thread switches if the change\r
+    // is really small, only do it if clock goes forward by more than\r
+    // 0.5 millisecond.  If the time goes backwards, the thread will\r
+    // wake up "early" (relativly speaking) and will re-evaluate at\r
+    // that time.\r
+    if ( TimeDelta > 5000 && m_pSchedule->GetAdviseCount() > 0 ) TriggerThread();\r
+    return NOERROR;\r
+}\r
+\r
+// Thread stuff\r
+\r
+DWORD __stdcall CBaseReferenceClock::AdviseThreadFunction(__in LPVOID p)\r
+{\r
+    return DWORD(reinterpret_cast<CBaseReferenceClock*>(p)->AdviseThread());\r
+}\r
+\r
+HRESULT CBaseReferenceClock::AdviseThread()\r
+{\r
+    DWORD dwWait = INFINITE;\r
+\r
+    // The first thing we do is wait until something interesting happens\r
+    // (meaning a first advise or shutdown).  This prevents us calling\r
+    // GetPrivateTime immediately which is goodness as that is a virtual\r
+    // routine and the derived class may not yet be constructed.  (This\r
+    // thread is created in the base class constructor.)\r
+\r
+    while ( !m_bAbort )\r
+    {\r
+        // Wait for an interesting event to happen\r
+        DbgLog((LOG_TIMING, 3, TEXT("CBaseRefClock::AdviseThread() Delay: %lu ms"), dwWait ));\r
+        WaitForSingleObject(m_pSchedule->GetEvent(), dwWait);\r
+        if (m_bAbort) break;\r
+\r
+        // There are several reasons why we need to work from the internal\r
+        // time, mainly to do with what happens when time goes backwards.\r
+        // Mainly, it stop us looping madly if an event is just about to\r
+        // expire when the clock goes backward (i.e. GetTime stop for a\r
+        // while).\r
+        const REFERENCE_TIME  rtNow = GetPrivateTime();\r
+\r
+        DbgLog((LOG_TIMING, 3,\r
+              TEXT("CBaseRefClock::AdviseThread() Woke at = %lu ms"),\r
+              ConvertToMilliseconds(rtNow) ));\r
+\r
+        // We must add in a millisecond, since this is the resolution of our\r
+        // WaitForSingleObject timer.  Failure to do so will cause us to loop\r
+        // franticly for (approx) 1 a millisecond.\r
+        m_rtNextAdvise = m_pSchedule->Advise( 10000 + rtNow );\r
+        LONGLONG llWait = m_rtNextAdvise - rtNow;\r
+\r
+        ASSERT( llWait > 0 );\r
+\r
+        llWait = ConvertToMilliseconds(llWait);\r
+        // DON'T replace this with a max!! (The type's of these things is VERY important)\r
+        dwWait = (llWait > REFERENCE_TIME(UINT_MAX)) ? UINT_MAX : DWORD(llWait);\r
+    };\r
+    return NOERROR;\r
+}\r
+\r
+HRESULT CBaseReferenceClock::SetDefaultTimerResolution(\r
+        REFERENCE_TIME timerResolution // in 100ns\r
+    )\r
+{\r
+    CAutoLock cObjectLock(this);\r
+    if( 0 == timerResolution  ) {\r
+        if( m_TimerResolution ) {\r
+           timeEndPeriod( m_TimerResolution );\r
+           m_TimerResolution = 0;\r
+        }\r
+    } else {\r
+        TIMECAPS tc;\r
+        DWORD dwMinResolution = (TIMERR_NOERROR == timeGetDevCaps(&tc, sizeof(tc)))\r
+                            ? tc.wPeriodMin\r
+                            : 1;\r
+        DWORD dwResolution = max( dwMinResolution, DWORD(timerResolution / 10000) );\r
+        if( dwResolution != m_TimerResolution ) {\r
+            timeEndPeriod(m_TimerResolution);\r
+            m_TimerResolution = dwResolution;\r
+            timeBeginPeriod( m_TimerResolution );\r
+        }\r
+    }\r
+    return S_OK;\r
+}\r
+\r
+HRESULT CBaseReferenceClock::GetDefaultTimerResolution(\r
+        __out REFERENCE_TIME* pTimerResolution // in 100ns\r
+    )\r
+{\r
+    if( !pTimerResolution ) {\r
+        return E_POINTER;\r
+    }\r
+    CAutoLock cObjectLock(this);\r
+    *pTimerResolution = m_TimerResolution * 10000;\r
+    return S_OK;\r
+}\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/refclock.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/refclock.h
new file mode 100644 (file)
index 0000000..d2b0bb1
--- /dev/null
@@ -0,0 +1,184 @@
+//------------------------------------------------------------------------------\r
+// File: RefClock.h\r
+//\r
+// Desc: DirectShow base classes - defines the IReferenceClock interface.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef __BASEREFCLOCK__\r
+#define __BASEREFCLOCK__\r
+\r
+#include <Schedule.h>\r
+\r
+const UINT RESOLUTION = 1;                      /* High resolution timer */\r
+const INT ADVISE_CACHE = 4;                     /* Default cache size */\r
+const LONGLONG MAX_TIME = 0x7FFFFFFFFFFFFFFF;   /* Maximum LONGLONG value */\r
+\r
+inline LONGLONG WINAPI ConvertToMilliseconds(const REFERENCE_TIME& RT)\r
+{\r
+    /* This converts an arbitrary value representing a reference time\r
+       into a MILLISECONDS value for use in subsequent system calls */\r
+\r
+    return (RT / (UNITS / MILLISECONDS));\r
+}\r
+\r
+/* This class hierarchy will support an IReferenceClock interface so\r
+   that an audio card (or other externally driven clock) can update the\r
+   system wide clock that everyone uses.\r
+\r
+   The interface will be pretty thin with probably just one update method\r
+   This interface has not yet been defined.\r
+ */\r
+\r
+/* This abstract base class implements the IReferenceClock\r
+ * interface.  Classes that actually provide clock signals (from\r
+ * whatever source) have to be derived from this class.\r
+ *\r
+ * The abstract class provides implementations for:\r
+ *  CUnknown support\r
+ *      locking support (CCritSec)\r
+ *  client advise code (creates a thread)\r
+ *\r
+ * Question: what can we do about quality?  Change the timer\r
+ * resolution to lower the system load?  Up the priority of the\r
+ * timer thread to force more responsive signals?\r
+ *\r
+ * During class construction we create a worker thread that is destroyed during\r
+ * destuction.  This thread executes a series of WaitForSingleObject calls,\r
+ * waking up when a command is given to the thread or the next wake up point\r
+ * is reached.  The wakeup points are determined by clients making Advise\r
+ * calls.\r
+ *\r
+ * Each advise call defines a point in time when they wish to be notified.  A\r
+ * periodic advise is a series of these such events.  We maintain a list of\r
+ * advise links and calculate when the nearest event notification is due for.\r
+ * We then call WaitForSingleObject with a timeout equal to this time.  The\r
+ * handle we wait on is used by the class to signal that something has changed\r
+ * and that we must reschedule the next event.  This typically happens when\r
+ * someone comes in and asks for an advise link while we are waiting for an\r
+ * event to timeout.\r
+ *\r
+ * While we are modifying the list of advise requests we\r
+ * are protected from interference through a critical section.  Clients are NOT\r
+ * advised through callbacks.  One shot clients have an event set, while\r
+ * periodic clients have a semaphore released for each event notification.  A\r
+ * semaphore allows a client to be kept up to date with the number of events\r
+ * actually triggered and be assured that they can't miss multiple events being\r
+ * set.\r
+ *\r
+ * Keeping track of advises is taken care of by the CAMSchedule class.\r
+ */\r
+\r
+class CBaseReferenceClock\r
+: public CUnknown, public IReferenceClock, public CCritSec, public IReferenceClockTimerControl \r
+{\r
+protected:\r
+    virtual ~CBaseReferenceClock();     // Don't let me be created on the stack!\r
+public:\r
+    CBaseReferenceClock(__in_opt LPCTSTR pName, \r
+                        __inout_opt LPUNKNOWN pUnk, \r
+                        __inout HRESULT *phr, \r
+                        __inout_opt CAMSchedule * pSched = 0 );\r
+\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv);\r
+\r
+    DECLARE_IUNKNOWN\r
+\r
+    /* IReferenceClock methods */\r
+    // Derived classes must implement GetPrivateTime().  All our GetTime\r
+    // does is call GetPrivateTime and then check so that time does not\r
+    // go backwards.  A return code of S_FALSE implies that the internal\r
+    // clock has gone backwards and GetTime time has halted until internal\r
+    // time has caught up. (Don't know if this will be much use to folk,\r
+    // but it seems odd not to use the return code for something useful.)\r
+    STDMETHODIMP GetTime(__out REFERENCE_TIME *pTime);\r
+    // When this is called, it sets m_rtLastGotTime to the time it returns.\r
+\r
+    /* Provide standard mechanisms for scheduling events */\r
+\r
+    /* Ask for an async notification that a time has elapsed */\r
+    STDMETHODIMP AdviseTime(\r
+        REFERENCE_TIME baseTime,        // base reference time\r
+        REFERENCE_TIME streamTime,      // stream offset time\r
+        HEVENT hEvent,                  // advise via this event\r
+        __out DWORD_PTR *pdwAdviseCookie// where your cookie goes\r
+    );\r
+\r
+    /* Ask for an asynchronous periodic notification that a time has elapsed */\r
+    STDMETHODIMP AdvisePeriodic(\r
+        REFERENCE_TIME StartTime,       // starting at this time\r
+        REFERENCE_TIME PeriodTime,      // time between notifications\r
+        HSEMAPHORE hSemaphore,          // advise via a semaphore\r
+        __out DWORD_PTR *pdwAdviseCookie// where your cookie goes\r
+    );\r
+\r
+    /* Cancel a request for notification(s) - if the notification was\r
+     * a one shot timer then this function doesn't need to be called\r
+     * as the advise is automatically cancelled, however it does no\r
+     * harm to explicitly cancel a one-shot advise.  It is REQUIRED that\r
+     * clients call Unadvise to clear a Periodic advise setting.\r
+     */\r
+\r
+    STDMETHODIMP Unadvise(DWORD_PTR dwAdviseCookie);\r
+\r
+    /* Methods for the benefit of derived classes or outer objects */\r
+\r
+    // GetPrivateTime() is the REAL clock.  GetTime is just a cover for\r
+    // it.  Derived classes will probably override this method but not\r
+    // GetTime() itself.\r
+    // The important point about GetPrivateTime() is it's allowed to go\r
+    // backwards.  Our GetTime() will keep returning the LastGotTime\r
+    // until GetPrivateTime() catches up.\r
+    virtual REFERENCE_TIME GetPrivateTime();\r
+\r
+    /* Provide a method for correcting drift */\r
+    STDMETHODIMP SetTimeDelta( const REFERENCE_TIME& TimeDelta );\r
+\r
+    CAMSchedule * GetSchedule() const { return m_pSchedule; }\r
+\r
+    // IReferenceClockTimerControl methods\r
+    //\r
+    // Setting a default of 0 disables the default of 1ms\r
+    STDMETHODIMP SetDefaultTimerResolution(\r
+        REFERENCE_TIME timerResolution // in 100ns\r
+    );\r
+    STDMETHODIMP GetDefaultTimerResolution(\r
+        __out REFERENCE_TIME* pTimerResolution // in 100ns\r
+    );\r
+\r
+private:\r
+    REFERENCE_TIME m_rtPrivateTime;     // Current best estimate of time\r
+    DWORD          m_dwPrevSystemTime;  // Last vaule we got from timeGetTime\r
+    REFERENCE_TIME m_rtLastGotTime;     // Last time returned by GetTime\r
+    REFERENCE_TIME m_rtNextAdvise;      // Time of next advise\r
+    UINT           m_TimerResolution;\r
+\r
+#ifdef PERF\r
+    int m_idGetSystemTime;\r
+#endif\r
+\r
+// Thread stuff\r
+public:\r
+    void TriggerThread()    // Wakes thread up.  Need to do this if\r
+    {                       // time to next advise needs reevaluating.\r
+        EXECUTE_ASSERT(SetEvent(m_pSchedule->GetEvent()));\r
+    }\r
+\r
+\r
+private:\r
+    BOOL           m_bAbort;            // Flag used for thread shutdown\r
+    HANDLE         m_hThread;           // Thread handle\r
+\r
+    HRESULT AdviseThread();             // Method in which the advise thread runs\r
+    static DWORD __stdcall AdviseThreadFunction(__in LPVOID); // Function used to get there\r
+\r
+protected:\r
+    CAMSchedule * m_pSchedule;\r
+\r
+    void Restart (IN REFERENCE_TIME rtMinTime = 0I64) ;\r
+};\r
+\r
+#endif\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/reftime.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/reftime.h
new file mode 100644 (file)
index 0000000..5bc99a6
--- /dev/null
@@ -0,0 +1,116 @@
+//------------------------------------------------------------------------------\r
+// File: RefTime.h\r
+//\r
+// Desc: DirectShow base classes - defines CRefTime, a class that manages\r
+//       reference times.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+//\r
+// CRefTime\r
+//\r
+// Manage reference times.\r
+// Shares same data layout as REFERENCE_TIME, but adds some (nonvirtual)\r
+// functions providing simple comparison, conversion and arithmetic.\r
+//\r
+// A reference time (at the moment) is a unit of seconds represented in\r
+// 100ns units as is used in the Win32 FILETIME structure. BUT the time\r
+// a REFERENCE_TIME represents is NOT the time elapsed since 1/1/1601 it\r
+// will either be stream time or reference time depending upon context\r
+//\r
+// This class provides simple arithmetic operations on reference times\r
+//\r
+// keep non-virtual otherwise the data layout will not be the same as\r
+// REFERENCE_TIME\r
+\r
+\r
+// -----\r
+// note that you are safe to cast a CRefTime* to a REFERENCE_TIME*, but\r
+// you will need to do so explicitly\r
+// -----\r
+\r
+\r
+#ifndef __REFTIME__\r
+#define __REFTIME__\r
+\r
+\r
+const LONGLONG MILLISECONDS = (1000);            // 10 ^ 3\r
+const LONGLONG NANOSECONDS = (1000000000);       // 10 ^ 9\r
+const LONGLONG UNITS = (NANOSECONDS / 100);      // 10 ^ 7\r
+\r
+/*  Unfortunately an inline function here generates a call to __allmul\r
+    - even for constants!\r
+*/\r
+#define MILLISECONDS_TO_100NS_UNITS(lMs) \\r
+    Int32x32To64((lMs), (UNITS / MILLISECONDS))\r
+\r
+class CRefTime\r
+{\r
+public:\r
+\r
+    // *MUST* be the only data member so that this class is exactly\r
+    // equivalent to a REFERENCE_TIME.\r
+    // Also, must be *no virtual functions*\r
+\r
+    REFERENCE_TIME m_time;\r
+\r
+    inline CRefTime()\r
+    {\r
+        // default to 0 time\r
+        m_time = 0;\r
+    };\r
+\r
+    inline CRefTime(LONG msecs)\r
+    {\r
+        m_time = MILLISECONDS_TO_100NS_UNITS(msecs);\r
+    };\r
+\r
+    inline CRefTime(REFERENCE_TIME rt)\r
+    {\r
+        m_time = rt;\r
+    };\r
+\r
+    inline operator REFERENCE_TIME() const\r
+    {\r
+        return m_time;\r
+    };\r
+\r
+    inline CRefTime& operator=(const CRefTime& rt)\r
+    {\r
+        m_time = rt.m_time;\r
+        return *this;\r
+    };\r
+\r
+    inline CRefTime& operator=(const LONGLONG ll)\r
+    {\r
+        m_time = ll;\r
+        return *this;\r
+    };\r
+\r
+    inline CRefTime& operator+=(const CRefTime& rt)\r
+    {\r
+        return (*this = *this + rt);\r
+    };\r
+\r
+    inline CRefTime& operator-=(const CRefTime& rt)\r
+    {\r
+        return (*this = *this - rt);\r
+    };\r
+\r
+    inline LONG Millisecs(void)\r
+    {\r
+        return (LONG)(m_time / (UNITS / MILLISECONDS));\r
+    };\r
+\r
+    inline LONGLONG GetUnits(void)\r
+    {\r
+        return m_time;\r
+    };\r
+};\r
+\r
+const LONGLONG TimeZero = 0;\r
+\r
+#endif /* __REFTIME__ */\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/renbase.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/renbase.cpp
new file mode 100644 (file)
index 0000000..c6e1962
--- /dev/null
@@ -0,0 +1,2858 @@
+//------------------------------------------------------------------------------\r
+// File: RenBase.cpp\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>        // DirectShow base class definitions\r
+#include <mmsystem.h>       // Needed for definition of timeGetTime\r
+#include <limits.h>         // Standard data type limit definitions\r
+#include <measure.h>        // Used for time critical log functions\r
+\r
+#pragma warning(disable:4355)\r
+\r
+//  Helper function for clamping time differences\r
+int inline TimeDiff(REFERENCE_TIME rt)\r
+{\r
+    if (rt < - (50 * UNITS)) {\r
+        return -(50 * UNITS);\r
+    } else\r
+    if (rt > 50 * UNITS) {\r
+        return 50 * UNITS;\r
+    } else return (int)rt;\r
+}\r
+\r
+// Implements the CBaseRenderer class\r
+\r
+CBaseRenderer::CBaseRenderer(REFCLSID RenderClass, // CLSID for this renderer\r
+                             __in_opt LPCTSTR pName,         // Debug ONLY description\r
+                             __inout_opt LPUNKNOWN pUnk,       // Aggregated owner object\r
+                             __inout HRESULT *phr) :       // General OLE return code\r
+\r
+    CBaseFilter(pName,pUnk,&m_InterfaceLock,RenderClass),\r
+    m_evComplete(TRUE, phr),\r
+    m_RenderEvent(FALSE, phr),\r
+    m_bAbort(FALSE),\r
+    m_pPosition(NULL),\r
+    m_ThreadSignal(TRUE, phr),\r
+    m_bStreaming(FALSE),\r
+    m_bEOS(FALSE),\r
+    m_bEOSDelivered(FALSE),\r
+    m_pMediaSample(NULL),\r
+    m_dwAdvise(0),\r
+    m_pQSink(NULL),\r
+    m_pInputPin(NULL),\r
+    m_bRepaintStatus(TRUE),\r
+    m_SignalTime(0),\r
+    m_bInReceive(FALSE),\r
+    m_EndOfStreamTimer(0)\r
+{\r
+    if (SUCCEEDED(*phr)) {\r
+        Ready();\r
+#ifdef PERF\r
+        m_idBaseStamp = MSR_REGISTER(TEXT("BaseRenderer: sample time stamp"));\r
+        m_idBaseRenderTime = MSR_REGISTER(TEXT("BaseRenderer: draw time (msec)"));\r
+        m_idBaseAccuracy = MSR_REGISTER(TEXT("BaseRenderer: Accuracy (msec)"));\r
+#endif\r
+    }\r
+}\r
+\r
+\r
+// Delete the dynamically allocated IMediaPosition and IMediaSeeking helper\r
+// object. The object is created when somebody queries us. These are standard\r
+// control interfaces for seeking and setting start/stop positions and rates.\r
+// We will probably also have made an input pin based on CRendererInputPin\r
+// that has to be deleted, it's created when an enumerator calls our GetPin\r
+\r
+CBaseRenderer::~CBaseRenderer()\r
+{\r
+    ASSERT(m_bStreaming == FALSE);\r
+    ASSERT(m_EndOfStreamTimer == 0);\r
+    StopStreaming();\r
+    ClearPendingSample();\r
+\r
+    // Delete any IMediaPosition implementation\r
+\r
+    if (m_pPosition) {\r
+        delete m_pPosition;\r
+        m_pPosition = NULL;\r
+    }\r
+\r
+    // Delete any input pin created\r
+\r
+    if (m_pInputPin) {\r
+        delete m_pInputPin;\r
+        m_pInputPin = NULL;\r
+    }\r
+\r
+    // Release any Quality sink\r
+\r
+    ASSERT(m_pQSink == NULL);\r
+}\r
+\r
+\r
+// This returns the IMediaPosition and IMediaSeeking interfaces\r
+\r
+HRESULT CBaseRenderer::GetMediaPositionInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    CAutoLock cObjectCreationLock(&m_ObjectCreationLock);\r
+    if (m_pPosition) {\r
+        return m_pPosition->NonDelegatingQueryInterface(riid,ppv);\r
+    }\r
+\r
+    CBasePin *pPin = GetPin(0);\r
+    if (NULL == pPin) {\r
+        return E_OUTOFMEMORY;\r
+    }\r
+\r
+    HRESULT hr = NOERROR;\r
+\r
+    // Create implementation of this dynamically since sometimes we may\r
+    // never try and do a seek. The helper object implements a position\r
+    // control interface (IMediaPosition) which in fact simply takes the\r
+    // calls normally from the filter graph and passes them upstream\r
+\r
+    m_pPosition = new CRendererPosPassThru(NAME("Renderer CPosPassThru"),\r
+                                           CBaseFilter::GetOwner(),\r
+                                           (HRESULT *) &hr,\r
+                                           pPin);\r
+    if (m_pPosition == NULL) {\r
+        return E_OUTOFMEMORY;\r
+    }\r
+\r
+    if (FAILED(hr)) {\r
+        delete m_pPosition;\r
+        m_pPosition = NULL;\r
+        return E_NOINTERFACE;\r
+    }\r
+    return GetMediaPositionInterface(riid,ppv);\r
+}\r
+\r
+\r
+// Overriden to say what interfaces we support and where\r
+\r
+STDMETHODIMP CBaseRenderer::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    // Do we have this interface\r
+\r
+    if (riid == IID_IMediaPosition || riid == IID_IMediaSeeking) {\r
+        return GetMediaPositionInterface(riid,ppv);\r
+    } else {\r
+        return CBaseFilter::NonDelegatingQueryInterface(riid,ppv);\r
+    }\r
+}\r
+\r
+\r
+// This is called whenever we change states, we have a manual reset event that\r
+// is signalled whenever we don't won't the source filter thread to wait in us\r
+// (such as in a stopped state) and likewise is not signalled whenever it can\r
+// wait (during paused and running) this function sets or resets the thread\r
+// event. The event is used to stop source filter threads waiting in Receive\r
+\r
+HRESULT CBaseRenderer::SourceThreadCanWait(BOOL bCanWait)\r
+{\r
+    if (bCanWait == TRUE) {\r
+        m_ThreadSignal.Reset();\r
+    } else {\r
+        m_ThreadSignal.Set();\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+#ifdef DEBUG\r
+// Dump the current renderer state to the debug terminal. The hardest part of\r
+// the renderer is the window where we unlock everything to wait for a clock\r
+// to signal it is time to draw or for the application to cancel everything\r
+// by stopping the filter. If we get things wrong we can leave the thread in\r
+// WaitForRenderTime with no way for it to ever get out and we will deadlock\r
+\r
+void CBaseRenderer::DisplayRendererState()\r
+{\r
+    DbgLog((LOG_TIMING, 1, TEXT("\nTimed out in WaitForRenderTime")));\r
+\r
+    // No way should this be signalled at this point\r
+\r
+    BOOL bSignalled = m_ThreadSignal.Check();\r
+    DbgLog((LOG_TIMING, 1, TEXT("Signal sanity check %d"),bSignalled));\r
+\r
+    // Now output the current renderer state variables\r
+\r
+    DbgLog((LOG_TIMING, 1, TEXT("Filter state %d"),m_State));\r
+\r
+    DbgLog((LOG_TIMING, 1, TEXT("Abort flag %d"),m_bAbort));\r
+\r
+    DbgLog((LOG_TIMING, 1, TEXT("Streaming flag %d"),m_bStreaming));\r
+\r
+    DbgLog((LOG_TIMING, 1, TEXT("Clock advise link %d"),m_dwAdvise));\r
+\r
+    DbgLog((LOG_TIMING, 1, TEXT("Current media sample %x"),m_pMediaSample));\r
+\r
+    DbgLog((LOG_TIMING, 1, TEXT("EOS signalled %d"),m_bEOS));\r
+\r
+    DbgLog((LOG_TIMING, 1, TEXT("EOS delivered %d"),m_bEOSDelivered));\r
+\r
+    DbgLog((LOG_TIMING, 1, TEXT("Repaint status %d"),m_bRepaintStatus));\r
+\r
+\r
+    // Output the delayed end of stream timer information\r
+\r
+    DbgLog((LOG_TIMING, 1, TEXT("End of stream timer %x"),m_EndOfStreamTimer));\r
+\r
+    DbgLog((LOG_TIMING, 1, TEXT("Deliver time %s"),CDisp((LONGLONG)m_SignalTime)));\r
+\r
+\r
+    // Should never timeout during a flushing state\r
+\r
+    BOOL bFlushing = m_pInputPin->IsFlushing();\r
+    DbgLog((LOG_TIMING, 1, TEXT("Flushing sanity check %d"),bFlushing));\r
+\r
+    // Display the time we were told to start at\r
+    DbgLog((LOG_TIMING, 1, TEXT("Last run time %s"),CDisp((LONGLONG)m_tStart.m_time)));\r
+\r
+    // Have we got a reference clock\r
+    if (m_pClock == NULL) return;\r
+\r
+    // Get the current time from the wall clock\r
+\r
+    CRefTime CurrentTime,StartTime,EndTime;\r
+    m_pClock->GetTime((REFERENCE_TIME*) &CurrentTime);\r
+    CRefTime Offset = CurrentTime - m_tStart;\r
+\r
+    // Display the current time from the clock\r
+\r
+    DbgLog((LOG_TIMING, 1, TEXT("Clock time %s"),CDisp((LONGLONG)CurrentTime.m_time)));\r
+\r
+    DbgLog((LOG_TIMING, 1, TEXT("Time difference %dms"),Offset.Millisecs()));\r
+\r
+\r
+    // Do we have a sample ready to render\r
+    if (m_pMediaSample == NULL) return;\r
+\r
+    m_pMediaSample->GetTime((REFERENCE_TIME*)&StartTime, (REFERENCE_TIME*)&EndTime);\r
+    DbgLog((LOG_TIMING, 1, TEXT("Next sample stream times (Start %d End %d ms)"),\r
+           StartTime.Millisecs(),EndTime.Millisecs()));\r
+\r
+    // Calculate how long it is until it is due for rendering\r
+    CRefTime Wait = (m_tStart + StartTime) - CurrentTime;\r
+    DbgLog((LOG_TIMING, 1, TEXT("Wait required %d ms"),Wait.Millisecs()));\r
+}\r
+#endif\r
+\r
+\r
+// Wait until the clock sets the timer event or we're otherwise signalled. We\r
+// set an arbitrary timeout for this wait and if it fires then we display the\r
+// current renderer state on the debugger. It will often fire if the filter's\r
+// left paused in an application however it may also fire during stress tests\r
+// if the synchronisation with application seeks and state changes is faulty\r
+\r
+#define RENDER_TIMEOUT 10000\r
+\r
+HRESULT CBaseRenderer::WaitForRenderTime()\r
+{\r
+    HANDLE WaitObjects[] = { m_ThreadSignal, m_RenderEvent };\r
+    DWORD Result = WAIT_TIMEOUT;\r
+\r
+    // Wait for either the time to arrive or for us to be stopped\r
+\r
+    OnWaitStart();\r
+    while (Result == WAIT_TIMEOUT) {\r
+        Result = WaitForMultipleObjects(2,WaitObjects,FALSE,RENDER_TIMEOUT);\r
+\r
+#ifdef DEBUG\r
+        if (Result == WAIT_TIMEOUT) DisplayRendererState();\r
+#endif\r
+\r
+    }\r
+    OnWaitEnd();\r
+\r
+    // We may have been awoken without the timer firing\r
+\r
+    if (Result == WAIT_OBJECT_0) {\r
+        return VFW_E_STATE_CHANGED;\r
+    }\r
+\r
+    SignalTimerFired();\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Poll waiting for Receive to complete.  This really matters when\r
+// Receive may set the palette and cause window messages\r
+// The problem is that if we don't really wait for a renderer to\r
+// stop processing we can deadlock waiting for a transform which\r
+// is calling the renderer's Receive() method because the transform's\r
+// Stop method doesn't know to process window messages to unblock\r
+// the renderer's Receive processing\r
+void CBaseRenderer::WaitForReceiveToComplete()\r
+{\r
+    for (;;) {\r
+        if (!m_bInReceive) {\r
+            break;\r
+        }\r
+\r
+        MSG msg;\r
+        //  Receive all interthread snedmessages\r
+        PeekMessage(&msg, NULL, WM_NULL, WM_NULL, PM_NOREMOVE);\r
+\r
+        Sleep(1);\r
+    }\r
+\r
+    // If the wakebit for QS_POSTMESSAGE is set, the PeekMessage call\r
+    // above just cleared the changebit which will cause some messaging\r
+    // calls to block (waitMessage, MsgWaitFor...) now.\r
+    // Post a dummy message to set the QS_POSTMESSAGE bit again\r
+    if (HIWORD(GetQueueStatus(QS_POSTMESSAGE)) & QS_POSTMESSAGE) {\r
+        //  Send dummy message\r
+        PostThreadMessage(GetCurrentThreadId(), WM_NULL, 0, 0);\r
+    }\r
+}\r
+\r
+// A filter can have four discrete states, namely Stopped, Running, Paused,\r
+// Intermediate. We are in an intermediate state if we are currently trying\r
+// to pause but haven't yet got the first sample (or if we have been flushed\r
+// in paused state and therefore still have to wait for a sample to arrive)\r
+\r
+// This class contains an event called m_evComplete which is signalled when\r
+// the current state is completed and is not signalled when we are waiting to\r
+// complete the last state transition. As mentioned above the only time we\r
+// use this at the moment is when we wait for a media sample in paused state\r
+// If while we are waiting we receive an end of stream notification from the\r
+// source filter then we know no data is imminent so we can reset the event\r
+// This means that when we transition to paused the source filter must call\r
+// end of stream on us or send us an image otherwise we'll hang indefinately\r
+\r
+\r
+// Simple internal way of getting the real state\r
+\r
+FILTER_STATE CBaseRenderer::GetRealState() {\r
+    return m_State;\r
+}\r
+\r
+\r
+// The renderer doesn't complete the full transition to paused states until\r
+// it has got one media sample to render. If you ask it for its state while\r
+// it's waiting it will return the state along with VFW_S_STATE_INTERMEDIATE\r
+\r
+STDMETHODIMP CBaseRenderer::GetState(DWORD dwMSecs,FILTER_STATE *State)\r
+{\r
+    CheckPointer(State,E_POINTER);\r
+\r
+    if (WaitDispatchingMessages(m_evComplete, dwMSecs) == WAIT_TIMEOUT) {\r
+        *State = m_State;\r
+        return VFW_S_STATE_INTERMEDIATE;\r
+    }\r
+    *State = m_State;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// If we're pausing and we have no samples we don't complete the transition\r
+// to State_Paused and we return S_FALSE. However if the m_bAbort flag has\r
+// been set then all samples are rejected so there is no point waiting for\r
+// one. If we do have a sample then return NOERROR. We will only ever return\r
+// VFW_S_STATE_INTERMEDIATE from GetState after being paused with no sample\r
+// (calling GetState after either being stopped or Run will NOT return this)\r
+\r
+HRESULT CBaseRenderer::CompleteStateChange(FILTER_STATE OldState)\r
+{\r
+    // Allow us to be paused when disconnected\r
+\r
+    if (m_pInputPin->IsConnected() == FALSE) {\r
+        Ready();\r
+        return S_OK;\r
+    }\r
+\r
+    // Have we run off the end of stream\r
+\r
+    if (IsEndOfStream() == TRUE) {\r
+        Ready();\r
+        return S_OK;\r
+    }\r
+\r
+    // Make sure we get fresh data after being stopped\r
+\r
+    if (HaveCurrentSample() == TRUE) {\r
+        if (OldState != State_Stopped) {\r
+            Ready();\r
+            return S_OK;\r
+        }\r
+    }\r
+    NotReady();\r
+    return S_FALSE;\r
+}\r
+\r
+\r
+// When we stop the filter the things we do are:-\r
+\r
+//      Decommit the allocator being used in the connection\r
+//      Release the source filter if it's waiting in Receive\r
+//      Cancel any advise link we set up with the clock\r
+//      Any end of stream signalled is now obsolete so reset\r
+//      Allow us to be stopped when we are not connected\r
+\r
+STDMETHODIMP CBaseRenderer::Stop()\r
+{\r
+    CAutoLock cRendererLock(&m_InterfaceLock);\r
+\r
+    // Make sure there really is a state change\r
+\r
+    if (m_State == State_Stopped) {\r
+        return NOERROR;\r
+    }\r
+\r
+    // Is our input pin connected\r
+\r
+    if (m_pInputPin->IsConnected() == FALSE) {\r
+        NOTE("Input pin is not connected");\r
+        m_State = State_Stopped;\r
+        return NOERROR;\r
+    }\r
+\r
+    CBaseFilter::Stop();\r
+\r
+    // If we are going into a stopped state then we must decommit whatever\r
+    // allocator we are using it so that any source filter waiting in the\r
+    // GetBuffer can be released and unlock themselves for a state change\r
+\r
+    if (m_pInputPin->Allocator()) {\r
+        m_pInputPin->Allocator()->Decommit();\r
+    }\r
+\r
+    // Cancel any scheduled rendering\r
+\r
+    SetRepaintStatus(TRUE);\r
+    StopStreaming();\r
+    SourceThreadCanWait(FALSE);\r
+    ResetEndOfStream();\r
+    CancelNotification();\r
+\r
+    // There should be no outstanding clock advise\r
+    ASSERT(CancelNotification() == S_FALSE);\r
+    ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));\r
+    ASSERT(m_EndOfStreamTimer == 0);\r
+\r
+    Ready();\r
+    WaitForReceiveToComplete();\r
+    m_bAbort = FALSE;\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// When we pause the filter the things we do are:-\r
+\r
+//      Commit the allocator being used in the connection\r
+//      Allow a source filter thread to wait in Receive\r
+//      Cancel any clock advise link (we may be running)\r
+//      Possibly complete the state change if we have data\r
+//      Allow us to be paused when we are not connected\r
+\r
+STDMETHODIMP CBaseRenderer::Pause()\r
+{\r
+    CAutoLock cRendererLock(&m_InterfaceLock);\r
+    FILTER_STATE OldState = m_State;\r
+    ASSERT(m_pInputPin->IsFlushing() == FALSE);\r
+\r
+    // Make sure there really is a state change\r
+\r
+    if (m_State == State_Paused) {\r
+        return CompleteStateChange(State_Paused);\r
+    }\r
+\r
+    // Has our input pin been connected\r
+\r
+    if (m_pInputPin->IsConnected() == FALSE) {\r
+        NOTE("Input pin is not connected");\r
+        m_State = State_Paused;\r
+        return CompleteStateChange(State_Paused);\r
+    }\r
+\r
+    // Pause the base filter class\r
+\r
+    HRESULT hr = CBaseFilter::Pause();\r
+    if (FAILED(hr)) {\r
+        NOTE("Pause failed");\r
+        return hr;\r
+    }\r
+\r
+    // Enable EC_REPAINT events again\r
+\r
+    SetRepaintStatus(TRUE);\r
+    StopStreaming();\r
+    SourceThreadCanWait(TRUE);\r
+    CancelNotification();\r
+    ResetEndOfStreamTimer();\r
+\r
+    // If we are going into a paused state then we must commit whatever\r
+    // allocator we are using it so that any source filter can call the\r
+    // GetBuffer and expect to get a buffer without returning an error\r
+\r
+    if (m_pInputPin->Allocator()) {\r
+        m_pInputPin->Allocator()->Commit();\r
+    }\r
+\r
+    // There should be no outstanding advise\r
+    ASSERT(CancelNotification() == S_FALSE);\r
+    ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));\r
+    ASSERT(m_EndOfStreamTimer == 0);\r
+    ASSERT(m_pInputPin->IsFlushing() == FALSE);\r
+\r
+    // When we come out of a stopped state we must clear any image we were\r
+    // holding onto for frame refreshing. Since renderers see state changes\r
+    // first we can reset ourselves ready to accept the source thread data\r
+    // Paused or running after being stopped causes the current position to\r
+    // be reset so we're not interested in passing end of stream signals\r
+\r
+    if (OldState == State_Stopped) {\r
+        m_bAbort = FALSE;\r
+        ClearPendingSample();\r
+    }\r
+    return CompleteStateChange(OldState);\r
+}\r
+\r
+\r
+// When we run the filter the things we do are:-\r
+\r
+//      Commit the allocator being used in the connection\r
+//      Allow a source filter thread to wait in Receive\r
+//      Signal the render event just to get us going\r
+//      Start the base class by calling StartStreaming\r
+//      Allow us to be run when we are not connected\r
+//      Signal EC_COMPLETE if we are not connected\r
+\r
+STDMETHODIMP CBaseRenderer::Run(REFERENCE_TIME StartTime)\r
+{\r
+    CAutoLock cRendererLock(&m_InterfaceLock);\r
+    FILTER_STATE OldState = m_State;\r
+\r
+    // Make sure there really is a state change\r
+\r
+    if (m_State == State_Running) {\r
+        return NOERROR;\r
+    }\r
+\r
+    // Send EC_COMPLETE if we're not connected\r
+\r
+    if (m_pInputPin->IsConnected() == FALSE) {\r
+        NotifyEvent(EC_COMPLETE,S_OK,(LONG_PTR)(IBaseFilter *)this);\r
+        m_State = State_Running;\r
+        return NOERROR;\r
+    }\r
+\r
+    Ready();\r
+\r
+    // Pause the base filter class\r
+\r
+    HRESULT hr = CBaseFilter::Run(StartTime);\r
+    if (FAILED(hr)) {\r
+        NOTE("Run failed");\r
+        return hr;\r
+    }\r
+\r
+    // Allow the source thread to wait\r
+    ASSERT(m_pInputPin->IsFlushing() == FALSE);\r
+    SourceThreadCanWait(TRUE);\r
+    SetRepaintStatus(FALSE);\r
+\r
+    // There should be no outstanding advise\r
+    ASSERT(CancelNotification() == S_FALSE);\r
+    ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));\r
+    ASSERT(m_EndOfStreamTimer == 0);\r
+    ASSERT(m_pInputPin->IsFlushing() == FALSE);\r
+\r
+    // If we are going into a running state then we must commit whatever\r
+    // allocator we are using it so that any source filter can call the\r
+    // GetBuffer and expect to get a buffer without returning an error\r
+\r
+    if (m_pInputPin->Allocator()) {\r
+        m_pInputPin->Allocator()->Commit();\r
+    }\r
+\r
+    // When we come out of a stopped state we must clear any image we were\r
+    // holding onto for frame refreshing. Since renderers see state changes\r
+    // first we can reset ourselves ready to accept the source thread data\r
+    // Paused or running after being stopped causes the current position to\r
+    // be reset so we're not interested in passing end of stream signals\r
+\r
+    if (OldState == State_Stopped) {\r
+        m_bAbort = FALSE;\r
+        ClearPendingSample();\r
+    }\r
+    return StartStreaming();\r
+}\r
+\r
+\r
+// Return the number of input pins we support\r
+\r
+int CBaseRenderer::GetPinCount()\r
+{\r
+    if (m_pInputPin == NULL) {\r
+        //  Try to create it\r
+        (void)GetPin(0);\r
+    }\r
+    return m_pInputPin != NULL ? 1 : 0;\r
+}\r
+\r
+\r
+// We only support one input pin and it is numbered zero\r
+\r
+CBasePin *CBaseRenderer::GetPin(int n)\r
+{\r
+    CAutoLock cObjectCreationLock(&m_ObjectCreationLock);\r
+\r
+    // Should only ever be called with zero\r
+    ASSERT(n == 0);\r
+\r
+    if (n != 0) {\r
+        return NULL;\r
+    }\r
+\r
+    // Create the input pin if not already done so\r
+\r
+    if (m_pInputPin == NULL) {\r
+\r
+        // hr must be initialized to NOERROR because\r
+        // CRendererInputPin's constructor only changes\r
+        // hr's value if an error occurs.\r
+        HRESULT hr = NOERROR;\r
+\r
+        m_pInputPin = new CRendererInputPin(this,&hr,L"In");\r
+        if (NULL == m_pInputPin) {\r
+            return NULL;\r
+        }\r
+\r
+        if (FAILED(hr)) {\r
+            delete m_pInputPin;\r
+            m_pInputPin = NULL;\r
+            return NULL;\r
+        }\r
+    }\r
+    return m_pInputPin;\r
+}\r
+\r
+\r
+// If "In" then return the IPin for our input pin, otherwise NULL and error\r
+\r
+STDMETHODIMP CBaseRenderer::FindPin(LPCWSTR Id, __deref_out IPin **ppPin)\r
+{\r
+    CheckPointer(ppPin,E_POINTER);\r
+\r
+    if (0==lstrcmpW(Id,L"In")) {\r
+        *ppPin = GetPin(0);\r
+        if (*ppPin) {\r
+            (*ppPin)->AddRef();\r
+        } else {\r
+            return E_OUTOFMEMORY;\r
+        }\r
+    } else {\r
+        *ppPin = NULL;\r
+        return VFW_E_NOT_FOUND;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Called when the input pin receives an EndOfStream notification. If we have\r
+// not got a sample, then notify EC_COMPLETE now. If we have samples, then set\r
+// m_bEOS and check for this on completing samples. If we're waiting to pause\r
+// then complete the transition to paused state by setting the state event\r
+\r
+HRESULT CBaseRenderer::EndOfStream()\r
+{\r
+    // Ignore these calls if we are stopped\r
+\r
+    if (m_State == State_Stopped) {\r
+        return NOERROR;\r
+    }\r
+\r
+    // If we have a sample then wait for it to be rendered\r
+\r
+    m_bEOS = TRUE;\r
+    if (m_pMediaSample) {\r
+        return NOERROR;\r
+    }\r
+\r
+    // If we are waiting for pause then we are now ready since we cannot now\r
+    // carry on waiting for a sample to arrive since we are being told there\r
+    // won't be any. This sets an event that the GetState function picks up\r
+\r
+    Ready();\r
+\r
+    // Only signal completion now if we are running otherwise queue it until\r
+    // we do run in StartStreaming. This is used when we seek because a seek\r
+    // causes a pause where early notification of completion is misleading\r
+\r
+    if (m_bStreaming) {\r
+        SendEndOfStream();\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// When we are told to flush we should release the source thread\r
+\r
+HRESULT CBaseRenderer::BeginFlush()\r
+{\r
+    // If paused then report state intermediate until we get some data\r
+\r
+    if (m_State == State_Paused) {\r
+        NotReady();\r
+    }\r
+\r
+    SourceThreadCanWait(FALSE);\r
+    CancelNotification();\r
+    ClearPendingSample();\r
+    //  Wait for Receive to complete\r
+    WaitForReceiveToComplete();\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// After flushing the source thread can wait in Receive again\r
+\r
+HRESULT CBaseRenderer::EndFlush()\r
+{\r
+    // Reset the current sample media time\r
+    if (m_pPosition) m_pPosition->ResetMediaTime();\r
+\r
+    // There should be no outstanding advise\r
+\r
+    ASSERT(CancelNotification() == S_FALSE);\r
+    SourceThreadCanWait(TRUE);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// We can now send EC_REPAINTs if so required\r
+\r
+HRESULT CBaseRenderer::CompleteConnect(IPin *pReceivePin)\r
+{\r
+    // The caller should always hold the interface lock because\r
+    // the function uses CBaseFilter::m_State.\r
+    ASSERT(CritCheckIn(&m_InterfaceLock));\r
+\r
+    m_bAbort = FALSE;\r
+\r
+    if (State_Running == GetRealState()) {\r
+        HRESULT hr = StartStreaming();\r
+        if (FAILED(hr)) {\r
+            return hr;\r
+        }\r
+\r
+        SetRepaintStatus(FALSE);\r
+    } else {\r
+        SetRepaintStatus(TRUE);\r
+    }\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Called when we go paused or running\r
+\r
+HRESULT CBaseRenderer::Active()\r
+{\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Called when we go into a stopped state\r
+\r
+HRESULT CBaseRenderer::Inactive()\r
+{\r
+    if (m_pPosition) {\r
+        m_pPosition->ResetMediaTime();\r
+    }\r
+    //  People who derive from this may want to override this behaviour\r
+    //  to keep hold of the sample in some circumstances\r
+    ClearPendingSample();\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Tell derived classes about the media type agreed\r
+\r
+HRESULT CBaseRenderer::SetMediaType(const CMediaType *pmt)\r
+{\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// When we break the input pin connection we should reset the EOS flags. When\r
+// we are asked for either IMediaPosition or IMediaSeeking we will create a\r
+// CPosPassThru object to handles media time pass through. When we're handed\r
+// samples we store (by calling CPosPassThru::RegisterMediaTime) their media\r
+// times so we can then return a real current position of data being rendered\r
+\r
+HRESULT CBaseRenderer::BreakConnect()\r
+{\r
+    // Do we have a quality management sink\r
+\r
+    if (m_pQSink) {\r
+        m_pQSink->Release();\r
+        m_pQSink = NULL;\r
+    }\r
+\r
+    // Check we have a valid connection\r
+\r
+    if (m_pInputPin->IsConnected() == FALSE) {\r
+        return S_FALSE;\r
+    }\r
+\r
+    // Check we are stopped before disconnecting\r
+    if (m_State != State_Stopped && !m_pInputPin->CanReconnectWhenActive()) {\r
+        return VFW_E_NOT_STOPPED;\r
+    }\r
+\r
+    SetRepaintStatus(FALSE);\r
+    ResetEndOfStream();\r
+    ClearPendingSample();\r
+    m_bAbort = FALSE;\r
+\r
+    if (State_Running == m_State) {\r
+        StopStreaming();\r
+    }\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Retrieves the sample times for this samples (note the sample times are\r
+// passed in by reference not value). We return S_FALSE to say schedule this\r
+// sample according to the times on the sample. We also return S_OK in\r
+// which case the object should simply render the sample data immediately\r
+\r
+HRESULT CBaseRenderer::GetSampleTimes(IMediaSample *pMediaSample,\r
+                                      __out REFERENCE_TIME *pStartTime,\r
+                                      __out REFERENCE_TIME *pEndTime)\r
+{\r
+    ASSERT(m_dwAdvise == 0);\r
+    ASSERT(pMediaSample);\r
+\r
+    // If the stop time for this sample is before or the same as start time,\r
+    // then just ignore it (release it) and schedule the next one in line\r
+    // Source filters should always fill in the start and end times properly!\r
+\r
+    if (SUCCEEDED(pMediaSample->GetTime(pStartTime, pEndTime))) {\r
+        if (*pEndTime < *pStartTime) {\r
+            return VFW_E_START_TIME_AFTER_END;\r
+        }\r
+    } else {\r
+        // no time set in the sample... draw it now?\r
+        return S_OK;\r
+    }\r
+\r
+    // Can't synchronise without a clock so we return S_OK which tells the\r
+    // caller that the sample should be rendered immediately without going\r
+    // through the overhead of setting a timer advise link with the clock\r
+\r
+    if (m_pClock == NULL) {\r
+        return S_OK;\r
+    }\r
+    return ShouldDrawSampleNow(pMediaSample,pStartTime,pEndTime);\r
+}\r
+\r
+\r
+// By default all samples are drawn according to their time stamps so we\r
+// return S_FALSE. Returning S_OK means draw immediately, this is used\r
+// by the derived video renderer class in its quality management.\r
+\r
+HRESULT CBaseRenderer::ShouldDrawSampleNow(IMediaSample *pMediaSample,\r
+                                           __out REFERENCE_TIME *ptrStart,\r
+                                           __out REFERENCE_TIME *ptrEnd)\r
+{\r
+    return S_FALSE;\r
+}\r
+\r
+\r
+// We must always reset the current advise time to zero after a timer fires\r
+// because there are several possible ways which lead us not to do any more\r
+// scheduling such as the pending image being cleared after state changes\r
+\r
+void CBaseRenderer::SignalTimerFired()\r
+{\r
+    m_dwAdvise = 0;\r
+}\r
+\r
+\r
+// Cancel any notification currently scheduled. This is called by the owning\r
+// window object when it is told to stop streaming. If there is no timer link\r
+// outstanding then calling this is benign otherwise we go ahead and cancel\r
+// We must always reset the render event as the quality management code can\r
+// signal immediate rendering by setting the event without setting an advise\r
+// link. If we're subsequently stopped and run the first attempt to setup an\r
+// advise link with the reference clock will find the event still signalled\r
+\r
+HRESULT CBaseRenderer::CancelNotification()\r
+{\r
+    ASSERT(m_dwAdvise == 0 || m_pClock);\r
+    DWORD_PTR dwAdvise = m_dwAdvise;\r
+\r
+    // Have we a live advise link\r
+\r
+    if (m_dwAdvise) {\r
+        m_pClock->Unadvise(m_dwAdvise);\r
+        SignalTimerFired();\r
+        ASSERT(m_dwAdvise == 0);\r
+    }\r
+\r
+    // Clear the event and return our status\r
+\r
+    m_RenderEvent.Reset();\r
+    return (dwAdvise ? S_OK : S_FALSE);\r
+}\r
+\r
+\r
+// Responsible for setting up one shot advise links with the clock\r
+// Return FALSE if the sample is to be dropped (not drawn at all)\r
+// Return TRUE if the sample is to be drawn and in this case also\r
+// arrange for m_RenderEvent to be set at the appropriate time\r
+\r
+BOOL CBaseRenderer::ScheduleSample(IMediaSample *pMediaSample)\r
+{\r
+    REFERENCE_TIME StartSample, EndSample;\r
+\r
+    // Is someone pulling our leg\r
+\r
+    if (pMediaSample == NULL) {\r
+        return FALSE;\r
+    }\r
+\r
+    // Get the next sample due up for rendering.  If there aren't any ready\r
+    // then GetNextSampleTimes returns an error.  If there is one to be done\r
+    // then it succeeds and yields the sample times. If it is due now then\r
+    // it returns S_OK other if it's to be done when due it returns S_FALSE\r
+\r
+    HRESULT hr = GetSampleTimes(pMediaSample, &StartSample, &EndSample);\r
+    if (FAILED(hr)) {\r
+        return FALSE;\r
+    }\r
+\r
+    // If we don't have a reference clock then we cannot set up the advise\r
+    // time so we simply set the event indicating an image to render. This\r
+    // will cause us to run flat out without any timing or synchronisation\r
+\r
+    if (hr == S_OK) {\r
+        EXECUTE_ASSERT(SetEvent((HANDLE) m_RenderEvent));\r
+        return TRUE;\r
+    }\r
+\r
+    ASSERT(m_dwAdvise == 0);\r
+    ASSERT(m_pClock);\r
+    ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));\r
+\r
+    // We do have a valid reference clock interface so we can ask it to\r
+    // set an event when the image comes due for rendering. We pass in\r
+    // the reference time we were told to start at and also the current\r
+    // stream time which is the offset from the start reference time\r
+\r
+    hr = m_pClock->AdviseTime(\r
+            (REFERENCE_TIME) m_tStart,          // Start run time\r
+            StartSample,                        // Stream time\r
+            (HEVENT)(HANDLE) m_RenderEvent,     // Render notification\r
+            &m_dwAdvise);                       // Advise cookie\r
+\r
+    if (SUCCEEDED(hr)) {\r
+        return TRUE;\r
+    }\r
+\r
+    // We could not schedule the next sample for rendering despite the fact\r
+    // we have a valid sample here. This is a fair indication that either\r
+    // the system clock is wrong or the time stamp for the sample is duff\r
+\r
+    ASSERT(m_dwAdvise == 0);\r
+    return FALSE;\r
+}\r
+\r
+\r
+// This is called when a sample comes due for rendering. We pass the sample\r
+// on to the derived class. After rendering we will initialise the timer for\r
+// the next sample, NOTE signal that the last one fired first, if we don't\r
+// do this it thinks there is still one outstanding that hasn't completed\r
+\r
+HRESULT CBaseRenderer::Render(IMediaSample *pMediaSample)\r
+{\r
+    // If the media sample is NULL then we will have been notified by the\r
+    // clock that another sample is ready but in the mean time someone has\r
+    // stopped us streaming which causes the next sample to be released\r
+\r
+    if (pMediaSample == NULL) {\r
+        return S_FALSE;\r
+    }\r
+\r
+    // If we have stopped streaming then don't render any more samples, the\r
+    // thread that got in and locked us and then reset this flag does not\r
+    // clear the pending sample as we can use it to refresh any output device\r
+\r
+    if (m_bStreaming == FALSE) {\r
+        return S_FALSE;\r
+    }\r
+\r
+    // Time how long the rendering takes\r
+\r
+    OnRenderStart(pMediaSample);\r
+    DoRenderSample(pMediaSample);\r
+    OnRenderEnd(pMediaSample);\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Checks if there is a sample waiting at the renderer\r
+\r
+BOOL CBaseRenderer::HaveCurrentSample()\r
+{\r
+    CAutoLock cRendererLock(&m_RendererLock);\r
+    return (m_pMediaSample == NULL ? FALSE : TRUE);\r
+}\r
+\r
+\r
+// Returns the current sample waiting at the video renderer. We AddRef the\r
+// sample before returning so that should it come due for rendering the\r
+// person who called this method will hold the remaining reference count\r
+// that will stop the sample being added back onto the allocator free list\r
+\r
+IMediaSample *CBaseRenderer::GetCurrentSample()\r
+{\r
+    CAutoLock cRendererLock(&m_RendererLock);\r
+    if (m_pMediaSample) {\r
+        m_pMediaSample->AddRef();\r
+    }\r
+    return m_pMediaSample;\r
+}\r
+\r
+\r
+// Called when the source delivers us a sample. We go through a few checks to\r
+// make sure the sample can be rendered. If we are running (streaming) then we\r
+// have the sample scheduled with the reference clock, if we are not streaming\r
+// then we have received an sample in paused mode so we can complete any state\r
+// transition. On leaving this function everything will be unlocked so an app\r
+// thread may get in and change our state to stopped (for example) in which\r
+// case it will also signal the thread event so that our wait call is stopped\r
+\r
+HRESULT CBaseRenderer::PrepareReceive(IMediaSample *pMediaSample)\r
+{\r
+    CAutoLock cInterfaceLock(&m_InterfaceLock);\r
+    m_bInReceive = TRUE;\r
+\r
+    // Check our flushing and filter state\r
+\r
+    // This function must hold the interface lock because it calls \r
+    // CBaseInputPin::Receive() and CBaseInputPin::Receive() uses\r
+    // CBasePin::m_bRunTimeError.\r
+    HRESULT hr = m_pInputPin->CBaseInputPin::Receive(pMediaSample);\r
+\r
+    if (hr != NOERROR) {\r
+        m_bInReceive = FALSE;\r
+        return E_FAIL;\r
+    }\r
+\r
+    // Has the type changed on a media sample. We do all rendering\r
+    // synchronously on the source thread, which has a side effect\r
+    // that only one buffer is ever outstanding. Therefore when we\r
+    // have Receive called we can go ahead and change the format\r
+    // Since the format change can cause a SendMessage we just don't\r
+    // lock\r
+    if (m_pInputPin->SampleProps()->pMediaType) {\r
+        hr = m_pInputPin->SetMediaType(\r
+                (CMediaType *)m_pInputPin->SampleProps()->pMediaType);\r
+        if (FAILED(hr)) {\r
+            m_bInReceive = FALSE;\r
+            return hr;\r
+        }\r
+    }\r
+\r
+\r
+    CAutoLock cSampleLock(&m_RendererLock);\r
+\r
+    ASSERT(IsActive() == TRUE);\r
+    ASSERT(m_pInputPin->IsFlushing() == FALSE);\r
+    ASSERT(m_pInputPin->IsConnected() == TRUE);\r
+    ASSERT(m_pMediaSample == NULL);\r
+\r
+    // Return an error if we already have a sample waiting for rendering\r
+    // source pins must serialise the Receive calls - we also check that\r
+    // no data is being sent after the source signalled an end of stream\r
+\r
+    if (m_pMediaSample || m_bEOS || m_bAbort) {\r
+        Ready();\r
+        m_bInReceive = FALSE;\r
+        return E_UNEXPECTED;\r
+    }\r
+\r
+    // Store the media times from this sample\r
+    if (m_pPosition) m_pPosition->RegisterMediaTime(pMediaSample);\r
+\r
+    // Schedule the next sample if we are streaming\r
+\r
+    if ((m_bStreaming == TRUE) && (ScheduleSample(pMediaSample) == FALSE)) {\r
+        ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));\r
+        ASSERT(CancelNotification() == S_FALSE);\r
+        m_bInReceive = FALSE;\r
+        return VFW_E_SAMPLE_REJECTED;\r
+    }\r
+\r
+    // Store the sample end time for EC_COMPLETE handling\r
+    m_SignalTime = m_pInputPin->SampleProps()->tStop;\r
+\r
+    // BEWARE we sometimes keep the sample even after returning the thread to\r
+    // the source filter such as when we go into a stopped state (we keep it\r
+    // to refresh the device with) so we must AddRef it to keep it safely. If\r
+    // we start flushing the source thread is released and any sample waiting\r
+    // will be released otherwise GetBuffer may never return (see BeginFlush)\r
+\r
+    m_pMediaSample = pMediaSample;\r
+    m_pMediaSample->AddRef();\r
+\r
+    if (m_bStreaming == FALSE) {\r
+        SetRepaintStatus(TRUE);\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Called by the source filter when we have a sample to render. Under normal\r
+// circumstances we set an advise link with the clock, wait for the time to\r
+// arrive and then render the data using the PURE virtual DoRenderSample that\r
+// the derived class will have overriden. After rendering the sample we may\r
+// also signal EOS if it was the last one sent before EndOfStream was called\r
+\r
+HRESULT CBaseRenderer::Receive(IMediaSample *pSample)\r
+{\r
+    ASSERT(pSample);\r
+\r
+    // It may return VFW_E_SAMPLE_REJECTED code to say don't bother\r
+\r
+    HRESULT hr = PrepareReceive(pSample);\r
+    ASSERT(m_bInReceive == SUCCEEDED(hr));\r
+    if (FAILED(hr)) {\r
+        if (hr == VFW_E_SAMPLE_REJECTED) {\r
+            return NOERROR;\r
+        }\r
+        return hr;\r
+    }\r
+\r
+    // We realize the palette in "PrepareRender()" so we have to give away the\r
+    // filter lock here.\r
+    if (m_State == State_Paused) {\r
+        PrepareRender();\r
+        // no need to use InterlockedExchange\r
+        m_bInReceive = FALSE;\r
+        {\r
+            // We must hold both these locks\r
+            CAutoLock cRendererLock(&m_InterfaceLock);\r
+            if (m_State == State_Stopped)\r
+                return NOERROR;\r
+\r
+            m_bInReceive = TRUE;\r
+            CAutoLock cSampleLock(&m_RendererLock);\r
+            OnReceiveFirstSample(pSample);\r
+        }\r
+        Ready();\r
+    }\r
+    // Having set an advise link with the clock we sit and wait. We may be\r
+    // awoken by the clock firing or by a state change. The rendering call\r
+    // will lock the critical section and check we can still render the data\r
+\r
+    hr = WaitForRenderTime();\r
+    if (FAILED(hr)) {\r
+        m_bInReceive = FALSE;\r
+        return NOERROR;\r
+    }\r
+\r
+    PrepareRender();\r
+\r
+    //  Set this here and poll it until we work out the locking correctly\r
+    //  It can't be right that the streaming stuff grabs the interface\r
+    //  lock - after all we want to be able to wait for this stuff\r
+    //  to complete\r
+    m_bInReceive = FALSE;\r
+\r
+    // We must hold both these locks\r
+    CAutoLock cRendererLock(&m_InterfaceLock);\r
+\r
+    // since we gave away the filter wide lock, the sate of the filter could\r
+    // have chnaged to Stopped\r
+    if (m_State == State_Stopped)\r
+        return NOERROR;\r
+\r
+    CAutoLock cSampleLock(&m_RendererLock);\r
+\r
+    // Deal with this sample\r
+\r
+    Render(m_pMediaSample);\r
+    ClearPendingSample();\r
+    SendEndOfStream();\r
+    CancelNotification();\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This is called when we stop or are inactivated to clear the pending sample\r
+// We release the media sample interface so that they can be allocated to the\r
+// source filter again, unless of course we are changing state to inactive in\r
+// which case GetBuffer will return an error. We must also reset the current\r
+// media sample to NULL so that we know we do not currently have an image\r
+\r
+HRESULT CBaseRenderer::ClearPendingSample()\r
+{\r
+    CAutoLock cRendererLock(&m_RendererLock);\r
+    if (m_pMediaSample) {\r
+        m_pMediaSample->Release();\r
+        m_pMediaSample = NULL;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Used to signal end of stream according to the sample end time\r
+\r
+void CALLBACK EndOfStreamTimer(UINT uID,        // Timer identifier\r
+                               UINT uMsg,       // Not currently used\r
+                               DWORD_PTR dwUser,// User information\r
+                               DWORD_PTR dw1,   // Windows reserved\r
+                               DWORD_PTR dw2)   // is also reserved\r
+{\r
+    CBaseRenderer *pRenderer = (CBaseRenderer *) dwUser;\r
+    NOTE1("EndOfStreamTimer called (%d)",uID);\r
+    pRenderer->TimerCallback();\r
+}\r
+\r
+//  Do the timer callback work\r
+void CBaseRenderer::TimerCallback()\r
+{\r
+    //  Lock for synchronization (but don't hold this lock when calling\r
+    //  timeKillEvent)\r
+    CAutoLock cRendererLock(&m_RendererLock);\r
+\r
+    // See if we should signal end of stream now\r
+\r
+    if (m_EndOfStreamTimer) {\r
+        m_EndOfStreamTimer = 0;\r
+        SendEndOfStream();\r
+    }\r
+}\r
+\r
+\r
+// If we are at the end of the stream signal the filter graph but do not set\r
+// the state flag back to FALSE. Once we drop off the end of the stream we\r
+// leave the flag set (until a subsequent ResetEndOfStream). Each sample we\r
+// get delivered will update m_SignalTime to be the last sample's end time.\r
+// We must wait this long before signalling end of stream to the filtergraph\r
+\r
+#define TIMEOUT_DELIVERYWAIT 50\r
+#define TIMEOUT_RESOLUTION 10\r
+\r
+HRESULT CBaseRenderer::SendEndOfStream()\r
+{\r
+    ASSERT(CritCheckIn(&m_RendererLock));\r
+    if (m_bEOS == FALSE || m_bEOSDelivered || m_EndOfStreamTimer) {\r
+        return NOERROR;\r
+    }\r
+\r
+    // If there is no clock then signal immediately\r
+    if (m_pClock == NULL) {\r
+        return NotifyEndOfStream();\r
+    }\r
+\r
+    // How long into the future is the delivery time\r
+\r
+    REFERENCE_TIME Signal = m_tStart + m_SignalTime;\r
+    REFERENCE_TIME CurrentTime;\r
+    m_pClock->GetTime(&CurrentTime);\r
+    LONG Delay = LONG((Signal - CurrentTime) / 10000);\r
+\r
+    // Dump the timing information to the debugger\r
+\r
+    NOTE1("Delay until end of stream delivery %d",Delay);\r
+    NOTE1("Current %s",(LPCTSTR)CDisp((LONGLONG)CurrentTime));\r
+    NOTE1("Signal %s",(LPCTSTR)CDisp((LONGLONG)Signal));\r
+\r
+    // Wait for the delivery time to arrive\r
+\r
+    if (Delay < TIMEOUT_DELIVERYWAIT) {\r
+        return NotifyEndOfStream();\r
+    }\r
+\r
+    // Signal a timer callback on another worker thread\r
+\r
+    m_EndOfStreamTimer = CompatibleTimeSetEvent((UINT) Delay, // Period of timer\r
+                                      TIMEOUT_RESOLUTION,     // Timer resolution\r
+                                      EndOfStreamTimer,       // Callback function\r
+                                      DWORD_PTR(this),        // Used information\r
+                                      TIME_ONESHOT);          // Type of callback\r
+    if (m_EndOfStreamTimer == 0) {\r
+        return NotifyEndOfStream();\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Signals EC_COMPLETE to the filtergraph manager\r
+\r
+HRESULT CBaseRenderer::NotifyEndOfStream()\r
+{\r
+    CAutoLock cRendererLock(&m_RendererLock);\r
+    ASSERT(m_bEOSDelivered == FALSE);\r
+    ASSERT(m_EndOfStreamTimer == 0);\r
+\r
+    // Has the filter changed state\r
+\r
+    if (m_bStreaming == FALSE) {\r
+        ASSERT(m_EndOfStreamTimer == 0);\r
+        return NOERROR;\r
+    }\r
+\r
+    // Reset the end of stream timer\r
+    m_EndOfStreamTimer = 0;\r
+\r
+    // If we've been using the IMediaPosition interface, set it's start\r
+    // and end media "times" to the stop position by hand.  This ensures\r
+    // that we actually get to the end, even if the MPEG guestimate has\r
+    // been bad or if the quality management dropped the last few frames\r
+\r
+    if (m_pPosition) m_pPosition->EOS();\r
+    m_bEOSDelivered = TRUE;\r
+    NOTE("Sending EC_COMPLETE...");\r
+    return NotifyEvent(EC_COMPLETE,S_OK,(LONG_PTR)(IBaseFilter *)this);\r
+}\r
+\r
+\r
+// Reset the end of stream flag, this is typically called when we transfer to\r
+// stopped states since that resets the current position back to the start so\r
+// we will receive more samples or another EndOfStream if there aren't any. We\r
+// keep two separate flags one to say we have run off the end of the stream\r
+// (this is the m_bEOS flag) and another to say we have delivered EC_COMPLETE\r
+// to the filter graph. We need the latter otherwise we can end up sending an\r
+// EC_COMPLETE every time the source changes state and calls our EndOfStream\r
+\r
+HRESULT CBaseRenderer::ResetEndOfStream()\r
+{\r
+    ResetEndOfStreamTimer();\r
+    CAutoLock cRendererLock(&m_RendererLock);\r
+\r
+    m_bEOS = FALSE;\r
+    m_bEOSDelivered = FALSE;\r
+    m_SignalTime = 0;\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Kills any outstanding end of stream timer\r
+\r
+void CBaseRenderer::ResetEndOfStreamTimer()\r
+{\r
+    ASSERT(CritCheckOut(&m_RendererLock));\r
+    if (m_EndOfStreamTimer) {\r
+        timeKillEvent(m_EndOfStreamTimer);\r
+        m_EndOfStreamTimer = 0;\r
+    }\r
+}\r
+\r
+\r
+// This is called when we start running so that we can schedule any pending\r
+// image we have with the clock and display any timing information. If we\r
+// don't have any sample but we have queued an EOS flag then we send it. If\r
+// we do have a sample then we wait until that has been rendered before we\r
+// signal the filter graph otherwise we may change state before it's done\r
+\r
+HRESULT CBaseRenderer::StartStreaming()\r
+{\r
+    CAutoLock cRendererLock(&m_RendererLock);\r
+    if (m_bStreaming == TRUE) {\r
+        return NOERROR;\r
+    }\r
+\r
+    // Reset the streaming times ready for running\r
+\r
+    m_bStreaming = TRUE;\r
+\r
+    timeBeginPeriod(1);\r
+    OnStartStreaming();\r
+\r
+    // There should be no outstanding advise\r
+    ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));\r
+    ASSERT(CancelNotification() == S_FALSE);\r
+\r
+    // If we have an EOS and no data then deliver it now\r
+\r
+    if (m_pMediaSample == NULL) {\r
+        return SendEndOfStream();\r
+    }\r
+\r
+    // Have the data rendered\r
+\r
+    ASSERT(m_pMediaSample);\r
+    if (!ScheduleSample(m_pMediaSample))\r
+        m_RenderEvent.Set();\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This is called when we stop streaming so that we can set our internal flag\r
+// indicating we are not now to schedule any more samples arriving. The state\r
+// change methods in the filter implementation take care of cancelling any\r
+// clock advise link we have set up and clearing any pending sample we have\r
+\r
+HRESULT CBaseRenderer::StopStreaming()\r
+{\r
+    CAutoLock cRendererLock(&m_RendererLock);\r
+    m_bEOSDelivered = FALSE;\r
+\r
+    if (m_bStreaming == TRUE) {\r
+        m_bStreaming = FALSE;\r
+        OnStopStreaming();\r
+        timeEndPeriod(1);\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// We have a boolean flag that is reset when we have signalled EC_REPAINT to\r
+// the filter graph. We set this when we receive an image so that should any\r
+// conditions arise again we can send another one. By having a flag we ensure\r
+// we don't flood the filter graph with redundant calls. We do not set the\r
+// event when we receive an EndOfStream call since there is no point in us\r
+// sending further EC_REPAINTs. In particular the AutoShowWindow method and\r
+// the DirectDraw object use this method to control the window repainting\r
+\r
+void CBaseRenderer::SetRepaintStatus(BOOL bRepaint)\r
+{\r
+    CAutoLock cSampleLock(&m_RendererLock);\r
+    m_bRepaintStatus = bRepaint;\r
+}\r
+\r
+\r
+// Pass the window handle to the upstream filter\r
+\r
+void CBaseRenderer::SendNotifyWindow(IPin *pPin,HWND hwnd)\r
+{\r
+    IMediaEventSink *pSink;\r
+\r
+    // Does the pin support IMediaEventSink\r
+    HRESULT hr = pPin->QueryInterface(IID_IMediaEventSink,(void **)&pSink);\r
+    if (SUCCEEDED(hr)) {\r
+        pSink->Notify(EC_NOTIFY_WINDOW,LONG_PTR(hwnd),0);\r
+        pSink->Release();\r
+    }\r
+    NotifyEvent(EC_NOTIFY_WINDOW,LONG_PTR(hwnd),0);\r
+}\r
+\r
+\r
+// Signal an EC_REPAINT to the filter graph. This can be used to have data\r
+// sent to us. For example when a video window is first displayed it may\r
+// not have an image to display, at which point it signals EC_REPAINT. The\r
+// filtergraph will either pause the graph if stopped or if already paused\r
+// it will call put_CurrentPosition of the current position. Setting the\r
+// current position to itself has the stream flushed and the image resent\r
+\r
+#define RLOG(_x_) DbgLog((LOG_TRACE,1,TEXT(_x_)));\r
+\r
+void CBaseRenderer::SendRepaint()\r
+{\r
+    CAutoLock cSampleLock(&m_RendererLock);\r
+    ASSERT(m_pInputPin);\r
+\r
+    // We should not send repaint notifications when...\r
+    //    - An end of stream has been notified\r
+    //    - Our input pin is being flushed\r
+    //    - The input pin is not connected\r
+    //    - We have aborted a video playback\r
+    //    - There is a repaint already sent\r
+\r
+    if (m_bAbort == FALSE) {\r
+        if (m_pInputPin->IsConnected() == TRUE) {\r
+            if (m_pInputPin->IsFlushing() == FALSE) {\r
+                if (IsEndOfStream() == FALSE) {\r
+                    if (m_bRepaintStatus == TRUE) {\r
+                        IPin *pPin = (IPin *) m_pInputPin;\r
+                        NotifyEvent(EC_REPAINT,(LONG_PTR) pPin,0);\r
+                        SetRepaintStatus(FALSE);\r
+                        RLOG("Sending repaint");\r
+                    }\r
+                }\r
+            }\r
+        }\r
+    }\r
+}\r
+\r
+\r
+// When a video window detects a display change (WM_DISPLAYCHANGE message) it\r
+// can send an EC_DISPLAY_CHANGED event code along with the renderer pin. The\r
+// filtergraph will stop everyone and reconnect our input pin. As we're then\r
+// reconnected we can accept the media type that matches the new display mode\r
+// since we may no longer be able to draw the current image type efficiently\r
+\r
+BOOL CBaseRenderer::OnDisplayChange()\r
+{\r
+    // Ignore if we are not connected yet\r
+\r
+    CAutoLock cSampleLock(&m_RendererLock);\r
+    if (m_pInputPin->IsConnected() == FALSE) {\r
+        return FALSE;\r
+    }\r
+\r
+    RLOG("Notification of EC_DISPLAY_CHANGE");\r
+\r
+    // Pass our input pin as parameter on the event\r
+\r
+    IPin *pPin = (IPin *) m_pInputPin;\r
+    m_pInputPin->AddRef();\r
+    NotifyEvent(EC_DISPLAY_CHANGED,(LONG_PTR) pPin,0);\r
+    SetAbortSignal(TRUE);\r
+    ClearPendingSample();\r
+    m_pInputPin->Release();\r
+\r
+    return TRUE;\r
+}\r
+\r
+\r
+// Called just before we start drawing.\r
+// Store the current time in m_trRenderStart to allow the rendering time to be\r
+// logged.  Log the time stamp of the sample and how late it is (neg is early)\r
+\r
+void CBaseRenderer::OnRenderStart(IMediaSample *pMediaSample)\r
+{\r
+#ifdef PERF\r
+    REFERENCE_TIME trStart, trEnd;\r
+    pMediaSample->GetTime(&trStart, &trEnd);\r
+\r
+    MSR_INTEGER(m_idBaseStamp, (int)trStart);     // dump low order 32 bits\r
+\r
+    m_pClock->GetTime(&m_trRenderStart);\r
+    MSR_INTEGER(0, (int)m_trRenderStart);\r
+    REFERENCE_TIME trStream;\r
+    trStream = m_trRenderStart-m_tStart;     // convert reftime to stream time\r
+    MSR_INTEGER(0,(int)trStream);\r
+\r
+    const int trLate = (int)(trStream - trStart);\r
+    MSR_INTEGER(m_idBaseAccuracy, trLate/10000);  // dump in mSec\r
+#endif\r
+\r
+} // OnRenderStart\r
+\r
+\r
+// Called directly after drawing an image.\r
+// calculate the time spent drawing and log it.\r
+\r
+void CBaseRenderer::OnRenderEnd(IMediaSample *pMediaSample)\r
+{\r
+#ifdef PERF\r
+    REFERENCE_TIME trNow;\r
+    m_pClock->GetTime(&trNow);\r
+    MSR_INTEGER(0,(int)trNow);\r
+    int t = (int)((trNow - m_trRenderStart)/10000);   // convert UNITS->msec\r
+    MSR_INTEGER(m_idBaseRenderTime, t);\r
+#endif\r
+} // OnRenderEnd\r
+\r
+\r
+\r
+\r
+// Constructor must be passed the base renderer object\r
+\r
+CRendererInputPin::CRendererInputPin(__inout CBaseRenderer *pRenderer,\r
+                                     __inout HRESULT *phr,\r
+                                     __in_opt LPCWSTR pPinName) :\r
+    CBaseInputPin(NAME("Renderer pin"),\r
+                  pRenderer,\r
+                  &pRenderer->m_InterfaceLock,\r
+                  (HRESULT *) phr,\r
+                  pPinName)\r
+{\r
+    m_pRenderer = pRenderer;\r
+    ASSERT(m_pRenderer);\r
+}\r
+\r
+\r
+// Signals end of data stream on the input pin\r
+\r
+STDMETHODIMP CRendererInputPin::EndOfStream()\r
+{\r
+    CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);\r
+    CAutoLock cSampleLock(&m_pRenderer->m_RendererLock);\r
+\r
+    // Make sure we're streaming ok\r
+\r
+    HRESULT hr = CheckStreaming();\r
+    if (hr != NOERROR) {\r
+        return hr;\r
+    }\r
+\r
+    // Pass it onto the renderer\r
+\r
+    hr = m_pRenderer->EndOfStream();\r
+    if (SUCCEEDED(hr)) {\r
+        hr = CBaseInputPin::EndOfStream();\r
+    }\r
+    return hr;\r
+}\r
+\r
+\r
+// Signals start of flushing on the input pin - we do the final reset end of\r
+// stream with the renderer lock unlocked but with the interface lock locked\r
+// We must do this because we call timeKillEvent, our timer callback method\r
+// has to take the renderer lock to serialise our state. Therefore holding a\r
+// renderer lock when calling timeKillEvent could cause a deadlock condition\r
+\r
+STDMETHODIMP CRendererInputPin::BeginFlush()\r
+{\r
+    CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);\r
+    {\r
+        CAutoLock cSampleLock(&m_pRenderer->m_RendererLock);\r
+        CBaseInputPin::BeginFlush();\r
+        m_pRenderer->BeginFlush();\r
+    }\r
+    return m_pRenderer->ResetEndOfStream();\r
+}\r
+\r
+\r
+// Signals end of flushing on the input pin\r
+\r
+STDMETHODIMP CRendererInputPin::EndFlush()\r
+{\r
+    CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);\r
+    CAutoLock cSampleLock(&m_pRenderer->m_RendererLock);\r
+\r
+    HRESULT hr = m_pRenderer->EndFlush();\r
+    if (SUCCEEDED(hr)) {\r
+        hr = CBaseInputPin::EndFlush();\r
+    }\r
+    return hr;\r
+}\r
+\r
+\r
+// Pass the sample straight through to the renderer object\r
+\r
+STDMETHODIMP CRendererInputPin::Receive(IMediaSample *pSample)\r
+{\r
+    HRESULT hr = m_pRenderer->Receive(pSample);\r
+    if (FAILED(hr)) {\r
+\r
+        // A deadlock could occur if the caller holds the renderer lock and\r
+        // attempts to acquire the interface lock.\r
+        ASSERT(CritCheckOut(&m_pRenderer->m_RendererLock));\r
+\r
+        {\r
+            // The interface lock must be held when the filter is calling\r
+            // IsStopped() or IsFlushing().  The interface lock must also\r
+            // be held because the function uses m_bRunTimeError.\r
+            CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);\r
+\r
+            // We do not report errors which occur while the filter is stopping,\r
+            // flushing or if the m_bAbort flag is set .  Errors are expected to \r
+            // occur during these operations and the streaming thread correctly \r
+            // handles the errors.  \r
+            if (!IsStopped() && !IsFlushing() && !m_pRenderer->m_bAbort && !m_bRunTimeError) {\r
+\r
+                // EC_ERRORABORT's first parameter is the error which caused\r
+                // the event and its' last parameter is 0.  See the Direct\r
+                // Show SDK documentation for more information.\r
+                m_pRenderer->NotifyEvent(EC_ERRORABORT,hr,0);\r
+\r
+                {\r
+                    CAutoLock alRendererLock(&m_pRenderer->m_RendererLock);\r
+                    if (m_pRenderer->IsStreaming() && !m_pRenderer->IsEndOfStreamDelivered()) {\r
+                        m_pRenderer->NotifyEndOfStream();\r
+                    }\r
+                }\r
+    \r
+                m_bRunTimeError = TRUE;\r
+            }\r
+        }\r
+    }\r
+\r
+    return hr;\r
+}\r
+\r
+\r
+// Called when the input pin is disconnected\r
+\r
+HRESULT CRendererInputPin::BreakConnect()\r
+{\r
+    HRESULT hr = m_pRenderer->BreakConnect();\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return CBaseInputPin::BreakConnect();\r
+}\r
+\r
+\r
+// Called when the input pin is connected\r
+\r
+HRESULT CRendererInputPin::CompleteConnect(IPin *pReceivePin)\r
+{\r
+    HRESULT hr = m_pRenderer->CompleteConnect(pReceivePin);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return CBaseInputPin::CompleteConnect(pReceivePin);\r
+}\r
+\r
+\r
+// Give the pin id of our one and only pin\r
+\r
+STDMETHODIMP CRendererInputPin::QueryId(__deref_out LPWSTR *Id)\r
+{\r
+    CheckPointer(Id,E_POINTER);\r
+\r
+    const WCHAR szIn[] = L"In";\r
+\r
+    *Id = (LPWSTR)CoTaskMemAlloc(sizeof(szIn));\r
+    if (*Id == NULL) {\r
+        return E_OUTOFMEMORY;\r
+    }\r
+    CopyMemory(*Id, szIn, sizeof(szIn));\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Will the filter accept this media type\r
+\r
+HRESULT CRendererInputPin::CheckMediaType(const CMediaType *pmt)\r
+{\r
+    return m_pRenderer->CheckMediaType(pmt);\r
+}\r
+\r
+\r
+// Called when we go paused or running\r
+\r
+HRESULT CRendererInputPin::Active()\r
+{\r
+    return m_pRenderer->Active();\r
+}\r
+\r
+\r
+// Called when we go into a stopped state\r
+\r
+HRESULT CRendererInputPin::Inactive()\r
+{\r
+    // The caller must hold the interface lock because \r
+    // this function uses m_bRunTimeError.\r
+    ASSERT(CritCheckIn(&m_pRenderer->m_InterfaceLock));\r
+\r
+    m_bRunTimeError = FALSE;\r
+\r
+    return m_pRenderer->Inactive();\r
+}\r
+\r
+\r
+// Tell derived classes about the media type agreed\r
+\r
+HRESULT CRendererInputPin::SetMediaType(const CMediaType *pmt)\r
+{\r
+    HRESULT hr = CBaseInputPin::SetMediaType(pmt);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return m_pRenderer->SetMediaType(pmt);\r
+}\r
+\r
+\r
+// We do not keep an event object to use when setting up a timer link with\r
+// the clock but are given a pointer to one by the owning object through the\r
+// SetNotificationObject method - this must be initialised before starting\r
+// We can override the default quality management process to have it always\r
+// draw late frames, this is currently done by having the following registry\r
+// key (actually an INI key) called DrawLateFrames set to 1 (default is 0)\r
+\r
+const TCHAR AMQUALITY[] = TEXT("ActiveMovie");\r
+const TCHAR DRAWLATEFRAMES[] = TEXT("DrawLateFrames");\r
+\r
+CBaseVideoRenderer::CBaseVideoRenderer(\r
+      REFCLSID RenderClass, // CLSID for this renderer\r
+      __in_opt LPCTSTR pName,         // Debug ONLY description\r
+      __inout_opt LPUNKNOWN pUnk,       // Aggregated owner object\r
+      __inout HRESULT *phr) :       // General OLE return code\r
+\r
+    CBaseRenderer(RenderClass,pName,pUnk,phr),\r
+    m_cFramesDropped(0),\r
+    m_cFramesDrawn(0),\r
+    m_bSupplierHandlingQuality(FALSE)\r
+{\r
+    ResetStreamingTimes();\r
+\r
+#ifdef PERF\r
+    m_idTimeStamp       = MSR_REGISTER(TEXT("Frame time stamp"));\r
+    m_idEarliness       = MSR_REGISTER(TEXT("Earliness fudge"));\r
+    m_idTarget          = MSR_REGISTER(TEXT("Target (mSec)"));\r
+    m_idSchLateTime     = MSR_REGISTER(TEXT("mSec late when scheduled"));\r
+    m_idDecision        = MSR_REGISTER(TEXT("Scheduler decision code"));\r
+    m_idQualityRate     = MSR_REGISTER(TEXT("Quality rate sent"));\r
+    m_idQualityTime     = MSR_REGISTER(TEXT("Quality time sent"));\r
+    m_idWaitReal        = MSR_REGISTER(TEXT("Render wait"));\r
+    // m_idWait            = MSR_REGISTER(TEXT("wait time recorded (msec)"));\r
+    m_idFrameAccuracy   = MSR_REGISTER(TEXT("Frame accuracy (msecs)"));\r
+    m_bDrawLateFrames = GetProfileInt(AMQUALITY, DRAWLATEFRAMES, FALSE);\r
+    //m_idSendQuality      = MSR_REGISTER(TEXT("Processing Quality message"));\r
+\r
+    m_idRenderAvg       = MSR_REGISTER(TEXT("Render draw time Avg"));\r
+    m_idFrameAvg        = MSR_REGISTER(TEXT("FrameAvg"));\r
+    m_idWaitAvg         = MSR_REGISTER(TEXT("WaitAvg"));\r
+    m_idDuration        = MSR_REGISTER(TEXT("Duration"));\r
+    m_idThrottle        = MSR_REGISTER(TEXT("Audio-video throttle wait"));\r
+    // m_idDebug           = MSR_REGISTER(TEXT("Debug stuff"));\r
+#endif // PERF\r
+} // Constructor\r
+\r
+\r
+// Destructor is just a placeholder\r
+\r
+CBaseVideoRenderer::~CBaseVideoRenderer()\r
+{\r
+    ASSERT(m_dwAdvise == 0);\r
+}\r
+\r
+\r
+// The timing functions in this class are called by the window object and by\r
+// the renderer's allocator.\r
+// The windows object calls timing functions as it receives media sample\r
+// images for drawing using GDI.\r
+// The allocator calls timing functions when it starts passing DCI/DirectDraw\r
+// surfaces which are not rendered in the same way; The decompressor writes\r
+// directly to the surface with no separate rendering, so those code paths\r
+// call direct into us.  Since we only ever hand out DCI/DirectDraw surfaces\r
+// when we have allocated one and only one image we know there cannot be any\r
+// conflict between the two.\r
+//\r
+// We use timeGetTime to return the timing counts we use (since it's relative\r
+// performance we are interested in rather than absolute compared to a clock)\r
+// The window object sets the accuracy of the system clock (normally 1ms) by\r
+// calling timeBeginPeriod/timeEndPeriod when it changes streaming states\r
+\r
+\r
+// Reset all times controlling streaming.\r
+// Set them so that\r
+// 1. Frames will not initially be dropped\r
+// 2. The first frame will definitely be drawn (achieved by saying that there\r
+//    has not ben a frame drawn for a long time).\r
+\r
+HRESULT CBaseVideoRenderer::ResetStreamingTimes()\r
+{\r
+    m_trLastDraw = -1000;     // set up as first frame since ages (1 sec) ago\r
+    m_tStreamingStart = timeGetTime();\r
+    m_trRenderAvg = 0;\r
+    m_trFrameAvg = -1;        // -1000 fps == "unset"\r
+    m_trDuration = 0;         // 0 - strange value\r
+    m_trRenderLast = 0;\r
+    m_trWaitAvg = 0;\r
+    m_tRenderStart = 0;\r
+    m_cFramesDrawn = 0;\r
+    m_cFramesDropped = 0;\r
+    m_iTotAcc = 0;\r
+    m_iSumSqAcc = 0;\r
+    m_iSumSqFrameTime = 0;\r
+    m_trFrame = 0;          // hygeine - not really needed\r
+    m_trLate = 0;           // hygeine - not really needed\r
+    m_iSumFrameTime = 0;\r
+    m_nNormal = 0;\r
+    m_trEarliness = 0;\r
+    m_trTarget = -300000;  // 30mSec early\r
+    m_trThrottle = 0;\r
+    m_trRememberStampForPerf = 0;\r
+\r
+#ifdef PERF\r
+    m_trRememberFrameForPerf = 0;\r
+#endif\r
+\r
+    return NOERROR;\r
+} // ResetStreamingTimes\r
+\r
+\r
+// Reset all times controlling streaming. Note that we're now streaming. We\r
+// don't need to set the rendering event to have the source filter released\r
+// as it is done during the Run processing. When we are run we immediately\r
+// release the source filter thread and draw any image waiting (that image\r
+// may already have been drawn once as a poster frame while we were paused)\r
+\r
+HRESULT CBaseVideoRenderer::OnStartStreaming()\r
+{\r
+    ResetStreamingTimes();\r
+    return NOERROR;\r
+} // OnStartStreaming\r
+\r
+\r
+// Called at end of streaming.  Fixes times for property page report\r
+\r
+HRESULT CBaseVideoRenderer::OnStopStreaming()\r
+{\r
+    m_tStreamingStart = timeGetTime()-m_tStreamingStart;\r
+    return NOERROR;\r
+} // OnStopStreaming\r
+\r
+\r
+// Called when we start waiting for a rendering event.\r
+// Used to update times spent waiting and not waiting.\r
+\r
+void CBaseVideoRenderer::OnWaitStart()\r
+{\r
+    MSR_START(m_idWaitReal);\r
+} // OnWaitStart\r
+\r
+\r
+// Called when we are awoken from the wait in the window OR by our allocator\r
+// when it is hanging around until the next sample is due for rendering on a\r
+// DCI/DirectDraw surface. We add the wait time into our rolling average.\r
+// We grab the interface lock so that we're serialised with the application\r
+// thread going through the run code - which in due course ends up calling\r
+// ResetStreaming times - possibly as we run through this section of code\r
+\r
+void CBaseVideoRenderer::OnWaitEnd()\r
+{\r
+#ifdef PERF\r
+    MSR_STOP(m_idWaitReal);\r
+    // for a perf build we want to know just exactly how late we REALLY are.\r
+    // even if this means that we have to look at the clock again.\r
+\r
+    REFERENCE_TIME trRealStream;     // the real time now expressed as stream time.\r
+#if 0\r
+    m_pClock->GetTime(&trRealStream); // Calling clock here causes W95 deadlock!\r
+#else\r
+    // We will be discarding overflows like mad here!\r
+    // This is wrong really because timeGetTime() can wrap but it's\r
+    // only for PERF\r
+    REFERENCE_TIME tr = timeGetTime()*10000;\r
+    trRealStream = tr + m_llTimeOffset;\r
+#endif\r
+    trRealStream -= m_tStart;     // convert to stream time (this is a reftime)\r
+\r
+    if (m_trRememberStampForPerf==0) {\r
+        // This is probably the poster frame at the start, and it is not scheduled\r
+        // in the usual way at all.  Just count it.  The rememberstamp gets set\r
+        // in ShouldDrawSampleNow, so this does invalid frame recording until we\r
+        // actually start playing.\r
+        PreparePerformanceData(0, 0);\r
+    } else {\r
+        int trLate = (int)(trRealStream - m_trRememberStampForPerf);\r
+        int trFrame = (int)(tr - m_trRememberFrameForPerf);\r
+        PreparePerformanceData(trLate, trFrame);\r
+    }\r
+    m_trRememberFrameForPerf = tr;\r
+#endif //PERF\r
+} // OnWaitEnd\r
+\r
+\r
+// Put data on one side that describes the lateness of the current frame.\r
+// We don't yet know whether it will actually be drawn.  In direct draw mode,\r
+// this decision is up to the filter upstream, and it could change its mind.\r
+// The rules say that if it did draw it must call Receive().  One way or\r
+// another we eventually get into either OnRenderStart or OnDirectRender and\r
+// these both call RecordFrameLateness to update the statistics.\r
+\r
+void CBaseVideoRenderer::PreparePerformanceData(int trLate, int trFrame)\r
+{\r
+    m_trLate = trLate;\r
+    m_trFrame = trFrame;\r
+} // PreparePerformanceData\r
+\r
+\r
+// update the statistics:\r
+// m_iTotAcc, m_iSumSqAcc, m_iSumSqFrameTime, m_iSumFrameTime, m_cFramesDrawn\r
+// Note that because the properties page reports using these variables,\r
+// 1. We need to be inside a critical section\r
+// 2. They must all be updated together.  Updating the sums here and the count\r
+// elsewhere can result in imaginary jitter (i.e. attempts to find square roots\r
+// of negative numbers) in the property page code.\r
+\r
+void CBaseVideoRenderer::RecordFrameLateness(int trLate, int trFrame)\r
+{\r
+    // Record how timely we are.\r
+    int tLate = trLate/10000;\r
+\r
+    // Best estimate of moment of appearing on the screen is average of\r
+    // start and end draw times.  Here we have only the end time.  This may\r
+    // tend to show us as spuriously late by up to 1/2 frame rate achieved.\r
+    // Decoder probably monitors draw time.  We don't bother.\r
+    MSR_INTEGER( m_idFrameAccuracy, tLate );\r
+\r
+    // This is a kludge - we can get frames that are very late\r
+    // especially (at start-up) and they invalidate the statistics.\r
+    // So ignore things that are more than 1 sec off.\r
+    if (tLate>1000 || tLate<-1000) {\r
+        if (m_cFramesDrawn<=1) {\r
+            tLate = 0;\r
+        } else if (tLate>0) {\r
+            tLate = 1000;\r
+        } else {\r
+            tLate = -1000;\r
+        }\r
+    }\r
+    // The very first frame often has a invalid time, so don't\r
+    // count it into the statistics.   (???)\r
+    if (m_cFramesDrawn>1) {\r
+        m_iTotAcc += tLate;\r
+        m_iSumSqAcc += (tLate*tLate);\r
+    }\r
+\r
+    // calculate inter-frame time.  Doesn't make sense for first frame\r
+    // second frame suffers from invalid first frame stamp.\r
+    if (m_cFramesDrawn>2) {\r
+        int tFrame = trFrame/10000;    // convert to mSec else it overflows\r
+\r
+        // This is a kludge.  It can overflow anyway (a pause can cause\r
+        // a very long inter-frame time) and it overflows at 2**31/10**7\r
+        // or about 215 seconds i.e. 3min 35sec\r
+        if (tFrame>1000||tFrame<0) tFrame = 1000;\r
+        m_iSumSqFrameTime += tFrame*tFrame;\r
+        ASSERT(m_iSumSqFrameTime>=0);\r
+        m_iSumFrameTime += tFrame;\r
+    }\r
+    ++m_cFramesDrawn;\r
+\r
+} // RecordFrameLateness\r
+\r
+\r
+void CBaseVideoRenderer::ThrottleWait()\r
+{\r
+    if (m_trThrottle>0) {\r
+        int iThrottle = m_trThrottle/10000;    // convert to mSec\r
+        MSR_INTEGER( m_idThrottle, iThrottle);\r
+        DbgLog((LOG_TRACE, 0, TEXT("Throttle %d ms"), iThrottle));\r
+        Sleep(iThrottle);\r
+    } else {\r
+        Sleep(0);\r
+    }\r
+} // ThrottleWait\r
+\r
+\r
+// Whenever a frame is rendered it goes though either OnRenderStart\r
+// or OnDirectRender.  Data that are generated during ShouldDrawSample\r
+// are added to the statistics by calling RecordFrameLateness from both\r
+// these two places.\r
+\r
+// Called in place of OnRenderStart..OnRenderEnd\r
+// When a DirectDraw image is drawn\r
+void CBaseVideoRenderer::OnDirectRender(IMediaSample *pMediaSample)\r
+{\r
+    m_trRenderAvg = 0;\r
+    m_trRenderLast = 5000000;  // If we mode switch, we do NOT want this\r
+                               // to inhibit the new average getting going!\r
+                               // so we set it to half a second\r
+    // MSR_INTEGER(m_idRenderAvg, m_trRenderAvg/10000);\r
+    RecordFrameLateness(m_trLate, m_trFrame);\r
+    ThrottleWait();\r
+} // OnDirectRender\r
+\r
+\r
+// Called just before we start drawing.  All we do is to get the current clock\r
+// time (from the system) and return.  We have to store the start render time\r
+// in a member variable because it isn't used until we complete the drawing\r
+// The rest is just performance logging.\r
+\r
+void CBaseVideoRenderer::OnRenderStart(IMediaSample *pMediaSample)\r
+{\r
+    RecordFrameLateness(m_trLate, m_trFrame);\r
+    m_tRenderStart = timeGetTime();\r
+} // OnRenderStart\r
+\r
+\r
+// Called directly after drawing an image.  We calculate the time spent in the\r
+// drawing code and if this doesn't appear to have any odd looking spikes in\r
+// it then we add it to the current average draw time.  Measurement spikes may\r
+// occur if the drawing thread is interrupted and switched to somewhere else.\r
+\r
+void CBaseVideoRenderer::OnRenderEnd(IMediaSample *pMediaSample)\r
+{\r
+    // The renderer time can vary erratically if we are interrupted so we do\r
+    // some smoothing to help get more sensible figures out but even that is\r
+    // not enough as figures can go 9,10,9,9,83,9 and we must disregard 83\r
+\r
+    int tr = (timeGetTime() - m_tRenderStart)*10000;   // convert mSec->UNITS\r
+    if (tr < m_trRenderAvg*2 || tr < 2 * m_trRenderLast) {\r
+        // DO_MOVING_AVG(m_trRenderAvg, tr);\r
+        m_trRenderAvg = (tr + (AVGPERIOD-1)*m_trRenderAvg)/AVGPERIOD;\r
+    }\r
+    m_trRenderLast = tr;\r
+    ThrottleWait();\r
+} // OnRenderEnd\r
+\r
+\r
+STDMETHODIMP CBaseVideoRenderer::SetSink( IQualityControl * piqc)\r
+{\r
+\r
+    m_pQSink = piqc;\r
+\r
+    return NOERROR;\r
+} // SetSink\r
+\r
+\r
+STDMETHODIMP CBaseVideoRenderer::Notify( IBaseFilter * pSelf, Quality q)\r
+{\r
+    // NOTE:  We are NOT getting any locks here.  We could be called\r
+    // asynchronously and possibly even on a time critical thread of\r
+    // someone else's - so we do the minumum.  We only set one state\r
+    // variable (an integer) and if that happens to be in the middle\r
+    // of another thread reading it they will just get either the new\r
+    // or the old value.  Locking would achieve no more than this.\r
+\r
+    // It might be nice to check that we are being called from m_pGraph, but\r
+    // it turns out to be a millisecond or so per throw!\r
+\r
+    // This is heuristics, these numbers are aimed at being "what works"\r
+    // rather than anything based on some theory.\r
+    // We use a hyperbola because it's easy to calculate and it includes\r
+    // a panic button asymptote (which we push off just to the left)\r
+    // The throttling fits the following table (roughly)\r
+    // Proportion   Throttle (msec)\r
+    //     >=1000         0\r
+    //        900         3\r
+    //        800         7\r
+    //        700        11\r
+    //        600        17\r
+    //        500        25\r
+    //        400        35\r
+    //        300        50\r
+    //        200        72\r
+    //        125       100\r
+    //        100       112\r
+    //         50       146\r
+    //          0       200\r
+\r
+    // (some evidence that we could go for a sharper kink - e.g. no throttling\r
+    // until below the 750 mark - might give fractionally more frames on a\r
+    // P60-ish machine).  The easy way to get these coefficients is to use\r
+    // Renbase.xls follow the instructions therein using excel solver.\r
+\r
+    if (q.Proportion>=1000) { m_trThrottle = 0; }\r
+    else {\r
+        // The DWORD is to make quite sure I get unsigned arithmetic\r
+        // as the constant is between 2**31 and 2**32\r
+        m_trThrottle = -330000 + (388880000/(q.Proportion+167));\r
+    }\r
+    return NOERROR;\r
+} // Notify\r
+\r
+\r
+// Send a message to indicate what our supplier should do about quality.\r
+// Theory:\r
+// What a supplier wants to know is "is the frame I'm working on NOW\r
+// going to be late?".\r
+// F1 is the frame at the supplier (as above)\r
+// Tf1 is the due time for F1\r
+// T1 is the time at that point (NOW!)\r
+// Tr1 is the time that f1 WILL actually be rendered\r
+// L1 is the latency of the graph for frame F1 = Tr1-T1\r
+// D1 (for delay) is how late F1 will be beyond its due time i.e.\r
+// D1 = (Tr1-Tf1) which is what the supplier really wants to know.\r
+// Unfortunately Tr1 is in the future and is unknown, so is L1\r
+//\r
+// We could estimate L1 by its value for a previous frame,\r
+// L0 = Tr0-T0 and work off\r
+// D1' = ((T1+L0)-Tf1) = (T1 + (Tr0-T0) -Tf1)\r
+// Rearranging terms:\r
+// D1' = (T1-T0) + (Tr0-Tf1)\r
+//       adding (Tf0-Tf0) and rearranging again:\r
+//     = (T1-T0) + (Tr0-Tf0) + (Tf0-Tf1)\r
+//     = (T1-T0) - (Tf1-Tf0) + (Tr0-Tf0)\r
+// But (Tr0-Tf0) is just D0 - how late frame zero was, and this is the\r
+// Late field in the quality message that we send.\r
+// The other two terms just state what correction should be applied before\r
+// using the lateness of F0 to predict the lateness of F1.\r
+// (T1-T0) says how much time has actually passed (we have lost this much)\r
+// (Tf1-Tf0) says how much time should have passed if we were keeping pace\r
+// (we have gained this much).\r
+//\r
+// Suppliers should therefore work off:\r
+//    Quality.Late + (T1-T0)  - (Tf1-Tf0)\r
+// and see if this is "acceptably late" or even early (i.e. negative).\r
+// They get T1 and T0 by polling the clock, they get Tf1 and Tf0 from\r
+// the time stamps in the frames.  They get Quality.Late from us.\r
+//\r
+\r
+HRESULT CBaseVideoRenderer::SendQuality(REFERENCE_TIME trLate,\r
+                                        REFERENCE_TIME trRealStream)\r
+{\r
+    Quality q;\r
+    HRESULT hr;\r
+\r
+    // If we are the main user of time, then report this as Flood/Dry.\r
+    // If our suppliers are, then report it as Famine/Glut.\r
+    //\r
+    // We need to take action, but avoid hunting.  Hunting is caused by\r
+    // 1. Taking too much action too soon and overshooting\r
+    // 2. Taking too long to react (so averaging can CAUSE hunting).\r
+    //\r
+    // The reason why we use trLate as well as Wait is to reduce hunting;\r
+    // if the wait time is coming down and about to go into the red, we do\r
+    // NOT want to rely on some average which is only telling is that it used\r
+    // to be OK once.\r
+\r
+    q.TimeStamp = (REFERENCE_TIME)trRealStream;\r
+\r
+    if (m_trFrameAvg<0) {\r
+        q.Type = Famine;      // guess\r
+    }\r
+    // Is the greater part of the time taken bltting or something else\r
+    else if (m_trFrameAvg > 2*m_trRenderAvg) {\r
+        q.Type = Famine;                        // mainly other\r
+    } else {\r
+        q.Type = Flood;                         // mainly bltting\r
+    }\r
+\r
+    q.Proportion = 1000;               // default\r
+\r
+    if (m_trFrameAvg<0) {\r
+        // leave it alone - we don't know enough\r
+    }\r
+    else if ( trLate> 0 ) {\r
+        // try to catch up over the next second\r
+        // We could be Really, REALLY late, but rendering all the frames\r
+        // anyway, just because it's so cheap.\r
+\r
+        q.Proportion = 1000 - (int)((trLate)/(UNITS/1000));\r
+        if (q.Proportion<500) {\r
+           q.Proportion = 500;      // don't go daft. (could've been negative!)\r
+        } else {\r
+        }\r
+\r
+    } else if (  m_trWaitAvg>20000\r
+              && trLate<-20000\r
+              ){\r
+        // Go cautiously faster - aim at 2mSec wait.\r
+        if (m_trWaitAvg>=m_trFrameAvg) {\r
+            // This can happen because of some fudges.\r
+            // The waitAvg is how long we originally planned to wait\r
+            // The frameAvg is more honest.\r
+            // It means that we are spending a LOT of time waiting\r
+            q.Proportion = 2000;    // double.\r
+        } else {\r
+            if (m_trFrameAvg+20000 > m_trWaitAvg) {\r
+                q.Proportion\r
+                    = 1000 * (m_trFrameAvg / (m_trFrameAvg + 20000 - m_trWaitAvg));\r
+            } else {\r
+                // We're apparently spending more than the whole frame time waiting.\r
+                // Assume that the averages are slightly out of kilter, but that we\r
+                // are indeed doing a lot of waiting.  (This leg probably never\r
+                // happens, but the code avoids any potential divide by zero).\r
+                q.Proportion = 2000;\r
+            }\r
+        }\r
+\r
+        if (q.Proportion>2000) {\r
+            q.Proportion = 2000;    // don't go crazy.\r
+        }\r
+    }\r
+\r
+    // Tell the supplier how late frames are when they get rendered\r
+    // That's how late we are now.\r
+    // If we are in directdraw mode then the guy upstream can see the drawing\r
+    // times and we'll just report on the start time.  He can figure out any\r
+    // offset to apply.  If we are in DIB Section mode then we will apply an\r
+    // extra offset which is half of our drawing time.  This is usually small\r
+    // but can sometimes be the dominant effect.  For this we will use the\r
+    // average drawing time rather than the last frame.  If the last frame took\r
+    // a long time to draw and made us late, that's already in the lateness\r
+    // figure.  We should not add it in again unless we expect the next frame\r
+    // to be the same.  We don't, we expect the average to be a better shot.\r
+    // In direct draw mode the RenderAvg will be zero.\r
+\r
+    q.Late = trLate + m_trRenderAvg/2;\r
+\r
+    // log what we're doing\r
+    MSR_INTEGER(m_idQualityRate, q.Proportion);\r
+    MSR_INTEGER( m_idQualityTime, (int)q.Late / 10000 );\r
+\r
+    // A specific sink interface may be set through IPin\r
+\r
+    if (m_pQSink==NULL) {\r
+        // Get our input pin's peer.  We send quality management messages\r
+        // to any nominated receiver of these things (set in the IPin\r
+        // interface), or else to our source filter.\r
+\r
+        IQualityControl *pQC = NULL;\r
+        IPin *pOutputPin = m_pInputPin->GetConnected();\r
+        ASSERT(pOutputPin != NULL);\r
+\r
+        // And get an AddRef'd quality control interface\r
+\r
+        hr = pOutputPin->QueryInterface(IID_IQualityControl,(void**) &pQC);\r
+        if (SUCCEEDED(hr)) {\r
+            m_pQSink = pQC;\r
+        }\r
+    }\r
+    if (m_pQSink) {\r
+        return m_pQSink->Notify(this,q);\r
+    }\r
+\r
+    return S_FALSE;\r
+\r
+} // SendQuality\r
+\r
+\r
+// We are called with a valid IMediaSample image to decide whether this is to\r
+// be drawn or not.  There must be a reference clock in operation.\r
+// Return S_OK if it is to be drawn Now (as soon as possible)\r
+// Return S_FALSE if it is to be drawn when it's due\r
+// Return an error if we want to drop it\r
+// m_nNormal=-1 indicates that we dropped the previous frame and so this\r
+// one should be drawn early.  Respect it and update it.\r
+// Use current stream time plus a number of heuristics (detailed below)\r
+// to make the decision\r
+\r
+HRESULT CBaseVideoRenderer::ShouldDrawSampleNow(IMediaSample *pMediaSample,\r
+                                                __inout REFERENCE_TIME *ptrStart,\r
+                                                __inout REFERENCE_TIME *ptrEnd)\r
+{\r
+\r
+    // Don't call us unless there's a clock interface to synchronise with\r
+    ASSERT(m_pClock);\r
+\r
+    MSR_INTEGER(m_idTimeStamp, (int)((*ptrStart)>>32));   // high order 32 bits\r
+    MSR_INTEGER(m_idTimeStamp, (int)(*ptrStart));         // low order 32 bits\r
+\r
+    // We lose a bit of time depending on the monitor type waiting for the next\r
+    // screen refresh.  On average this might be about 8mSec - so it will be\r
+    // later than we think when the picture appears.  To compensate a bit\r
+    // we bias the media samples by -8mSec i.e. 80000 UNITs.\r
+    // We don't ever make a stream time negative (call it paranoia)\r
+    if (*ptrStart>=80000) {\r
+        *ptrStart -= 80000;\r
+        *ptrEnd -= 80000;       // bias stop to to retain valid frame duration\r
+    }\r
+\r
+    // Cache the time stamp now.  We will want to compare what we did with what\r
+    // we started with (after making the monitor allowance).\r
+    m_trRememberStampForPerf = *ptrStart;\r
+\r
+    // Get reference times (current and late)\r
+    REFERENCE_TIME trRealStream;     // the real time now expressed as stream time.\r
+    m_pClock->GetTime(&trRealStream);\r
+#ifdef PERF\r
+    // While the reference clock is expensive:\r
+    // Remember the offset from timeGetTime and use that.\r
+    // This overflows all over the place, but when we subtract to get\r
+    // differences the overflows all cancel out.\r
+    m_llTimeOffset = trRealStream-timeGetTime()*10000;\r
+#endif\r
+    trRealStream -= m_tStart;     // convert to stream time (this is a reftime)\r
+\r
+    // We have to wory about two versions of "lateness".  The truth, which we\r
+    // try to work out here and the one measured against m_trTarget which\r
+    // includes long term feedback.  We report statistics against the truth\r
+    // but for operational decisions we work to the target.\r
+    // We use TimeDiff to make sure we get an integer because we\r
+    // may actually be late (or more likely early if there is a big time\r
+    // gap) by a very long time.\r
+    const int trTrueLate = TimeDiff(trRealStream - *ptrStart);\r
+    const int trLate = trTrueLate;\r
+\r
+    MSR_INTEGER(m_idSchLateTime, trTrueLate/10000);\r
+\r
+    // Send quality control messages upstream, measured against target\r
+    HRESULT hr = SendQuality(trLate, trRealStream);\r
+    // Note: the filter upstream is allowed to this FAIL meaning "you do it".\r
+    m_bSupplierHandlingQuality = (hr==S_OK);\r
+\r
+    // Decision time!  Do we drop, draw when ready or draw immediately?\r
+\r
+    const int trDuration = (int)(*ptrEnd - *ptrStart);\r
+    {\r
+        // We need to see if the frame rate of the file has just changed.\r
+        // This would make comparing our previous frame rate with the current\r
+        // frame rate inefficent.  Hang on a moment though.  I've seen files\r
+        // where the frames vary between 33 and 34 mSec so as to average\r
+        // 30fps.  A minor variation like that won't hurt us.\r
+        int t = m_trDuration/32;\r
+        if (  trDuration > m_trDuration+t\r
+           || trDuration < m_trDuration-t\r
+           ) {\r
+            // There's a major variation.  Reset the average frame rate to\r
+            // exactly the current rate to disable decision 9002 for this frame,\r
+            // and remember the new rate.\r
+            m_trFrameAvg = trDuration;\r
+            m_trDuration = trDuration;\r
+        }\r
+    }\r
+\r
+    MSR_INTEGER(m_idEarliness, m_trEarliness/10000);\r
+    MSR_INTEGER(m_idRenderAvg, m_trRenderAvg/10000);\r
+    MSR_INTEGER(m_idFrameAvg, m_trFrameAvg/10000);\r
+    MSR_INTEGER(m_idWaitAvg, m_trWaitAvg/10000);\r
+    MSR_INTEGER(m_idDuration, trDuration/10000);\r
+\r
+#ifdef PERF\r
+    if (S_OK==pMediaSample->IsDiscontinuity()) {\r
+        MSR_INTEGER(m_idDecision, 9000);\r
+    }\r
+#endif\r
+\r
+    // Control the graceful slide back from slow to fast machine mode.\r
+    // After a frame drop accept an early frame and set the earliness to here\r
+    // If this frame is already later than the earliness then slide it to here\r
+    // otherwise do the standard slide (reduce by about 12% per frame).\r
+    // Note: earliness is normally NEGATIVE\r
+    BOOL bJustDroppedFrame\r
+        = (  m_bSupplierHandlingQuality\r
+          //  Can't use the pin sample properties because we might\r
+          //  not be in Receive when we call this\r
+          && (S_OK == pMediaSample->IsDiscontinuity())          // he just dropped one\r
+          )\r
+       || (m_nNormal==-1);                          // we just dropped one\r
+\r
+\r
+    // Set m_trEarliness (slide back from slow to fast machine mode)\r
+    if (trLate>0) {\r
+        m_trEarliness = 0;   // we are no longer in fast machine mode at all!\r
+    } else if (  (trLate>=m_trEarliness) || bJustDroppedFrame) {\r
+        m_trEarliness = trLate;  // Things have slipped of their own accord\r
+    } else {\r
+        m_trEarliness = m_trEarliness - m_trEarliness/8;  // graceful slide\r
+    }\r
+\r
+    // prepare the new wait average - but don't pollute the old one until\r
+    // we have finished with it.\r
+    int trWaitAvg;\r
+    {\r
+        // We never mix in a negative wait.  This causes us to believe in fast machines\r
+        // slightly more.\r
+        int trL = trLate<0 ? -trLate : 0;\r
+        trWaitAvg = (trL + m_trWaitAvg*(AVGPERIOD-1))/AVGPERIOD;\r
+    }\r
+\r
+\r
+    int trFrame;\r
+    {\r
+        REFERENCE_TIME tr = trRealStream - m_trLastDraw; // Cd be large - 4 min pause!\r
+        if (tr>10000000) {\r
+            tr = 10000000;   // 1 second - arbitrarily.\r
+        }\r
+        trFrame = int(tr);\r
+    }\r
+\r
+    // We will DRAW this frame IF...\r
+    if (\r
+          // ...the time we are spending drawing is a small fraction of the total\r
+          // observed inter-frame time so that dropping it won't help much.\r
+          (3*m_trRenderAvg <= m_trFrameAvg)\r
+\r
+         // ...or our supplier is NOT handling things and the next frame would\r
+         // be less timely than this one or our supplier CLAIMS to be handling\r
+         // things, and is now less than a full FOUR frames late.\r
+       || ( m_bSupplierHandlingQuality\r
+          ? (trLate <= trDuration*4)\r
+          : (trLate+trLate < trDuration)\r
+          )\r
+\r
+          // ...or we are on average waiting for over eight milliseconds then\r
+          // this may be just a glitch.  Draw it and we'll hope to catch up.\r
+       || (m_trWaitAvg > 80000)\r
+\r
+          // ...or we haven't drawn an image for over a second.  We will update\r
+          // the display, which stops the video looking hung.\r
+          // Do this regardless of how late this media sample is.\r
+       || ((trRealStream - m_trLastDraw) > UNITS)\r
+\r
+    ) {\r
+        HRESULT Result;\r
+\r
+        // We are going to play this frame.  We may want to play it early.\r
+        // We will play it early if we think we are in slow machine mode.\r
+        // If we think we are NOT in slow machine mode, we will still play\r
+        // it early by m_trEarliness as this controls the graceful slide back.\r
+        // and in addition we aim at being m_trTarget late rather than "on time".\r
+\r
+        BOOL bPlayASAP = FALSE;\r
+\r
+        // we will play it AT ONCE (slow machine mode) if...\r
+\r
+            // ...we are playing catch-up\r
+        if ( bJustDroppedFrame) {\r
+            bPlayASAP = TRUE;\r
+            MSR_INTEGER(m_idDecision, 9001);\r
+        }\r
+\r
+            // ...or if we are running below the true frame rate\r
+            // exact comparisons are glitchy, for these measurements,\r
+            // so add an extra 5% or so\r
+        else if (  (m_trFrameAvg > trDuration + trDuration/16)\r
+\r
+                   // It's possible to get into a state where we are losing ground, but\r
+                   // are a very long way ahead.  To avoid this or recover from it\r
+                   // we refuse to play early by more than 10 frames.\r
+                && (trLate > - trDuration*10)\r
+                ){\r
+            bPlayASAP = TRUE;\r
+            MSR_INTEGER(m_idDecision, 9002);\r
+        }\r
+#if 0\r
+            // ...or if we have been late and are less than one frame early\r
+        else if (  (trLate + trDuration > 0)\r
+                && (m_trWaitAvg<=20000)\r
+                ) {\r
+            bPlayASAP = TRUE;\r
+            MSR_INTEGER(m_idDecision, 9003);\r
+        }\r
+#endif\r
+        // We will NOT play it at once if we are grossly early.  On very slow frame\r
+        // rate movies - e.g. clock.avi - it is not a good idea to leap ahead just\r
+        // because we got starved (for instance by the net) and dropped one frame\r
+        // some time or other.  If we are more than 900mSec early, then wait.\r
+        if (trLate<-9000000) {\r
+            bPlayASAP = FALSE;\r
+        }\r
+\r
+        if (bPlayASAP) {\r
+\r
+            m_nNormal = 0;\r
+            MSR_INTEGER(m_idDecision, 0);\r
+            // When we are here, we are in slow-machine mode.  trLate may well\r
+            // oscillate between negative and positive when the supplier is\r
+            // dropping frames to keep sync.  We should not let that mislead\r
+            // us into thinking that we have as much as zero spare time!\r
+            // We just update with a zero wait.\r
+            m_trWaitAvg = (m_trWaitAvg*(AVGPERIOD-1))/AVGPERIOD;\r
+\r
+            // Assume that we draw it immediately.  Update inter-frame stats\r
+            m_trFrameAvg = (trFrame + m_trFrameAvg*(AVGPERIOD-1))/AVGPERIOD;\r
+#ifndef PERF\r
+            // If this is NOT a perf build, then report what we know so far\r
+            // without looking at the clock any more.  This assumes that we\r
+            // actually wait for exactly the time we hope to.  It also reports\r
+            // how close we get to the manipulated time stamps that we now have\r
+            // rather than the ones we originally started with.  It will\r
+            // therefore be a little optimistic.  However it's fast.\r
+            PreparePerformanceData(trTrueLate, trFrame);\r
+#endif\r
+            m_trLastDraw = trRealStream;\r
+            if (m_trEarliness > trLate) {\r
+                m_trEarliness = trLate;  // if we are actually early, this is neg\r
+            }\r
+            Result = S_OK;                   // Draw it now\r
+\r
+        } else {\r
+            ++m_nNormal;\r
+            // Set the average frame rate to EXACTLY the ideal rate.\r
+            // If we are exiting slow-machine mode then we will have caught up\r
+            // and be running ahead, so as we slide back to exact timing we will\r
+            // have a longer than usual gap at this point.  If we record this\r
+            // real gap then we'll think that we're running slow and go back\r
+            // into slow-machine mode and vever get it straight.\r
+            m_trFrameAvg = trDuration;\r
+            MSR_INTEGER(m_idDecision, 1);\r
+\r
+            // Play it early by m_trEarliness and by m_trTarget\r
+\r
+            {\r
+                int trE = m_trEarliness;\r
+                if (trE < -m_trFrameAvg) {\r
+                    trE = -m_trFrameAvg;\r
+                }\r
+                *ptrStart += trE;           // N.B. earliness is negative\r
+            }\r
+\r
+            int Delay = -trTrueLate;\r
+            Result = Delay<=0 ? S_OK : S_FALSE;     // OK = draw now, FALSE = wait\r
+\r
+            m_trWaitAvg = trWaitAvg;\r
+\r
+            // Predict when it will actually be drawn and update frame stats\r
+\r
+            if (Result==S_FALSE) {   // We are going to wait\r
+                trFrame = TimeDiff(*ptrStart-m_trLastDraw);\r
+                m_trLastDraw = *ptrStart;\r
+            } else {\r
+                // trFrame is already = trRealStream-m_trLastDraw;\r
+                m_trLastDraw = trRealStream;\r
+            }\r
+#ifndef PERF\r
+            int iAccuracy;\r
+            if (Delay>0) {\r
+                // Report lateness based on when we intend to play it\r
+                iAccuracy = TimeDiff(*ptrStart-m_trRememberStampForPerf);\r
+            } else {\r
+                // Report lateness based on playing it *now*.\r
+                iAccuracy = trTrueLate;     // trRealStream-RememberStampForPerf;\r
+            }\r
+            PreparePerformanceData(iAccuracy, trFrame);\r
+#endif\r
+        }\r
+        return Result;\r
+    }\r
+\r
+    // We are going to drop this frame!\r
+    // Of course in DirectDraw mode the guy upstream may draw it anyway.\r
+\r
+    // This will probably give a large negative wack to the wait avg.\r
+    m_trWaitAvg = trWaitAvg;\r
+\r
+#ifdef PERF\r
+    // Respect registry setting - debug only!\r
+    if (m_bDrawLateFrames) {\r
+       return S_OK;                        // draw it when it's ready\r
+    }                                      // even though it's late.\r
+#endif\r
+\r
+    // We are going to drop this frame so draw the next one early\r
+    // n.b. if the supplier is doing direct draw then he may draw it anyway\r
+    // but he's doing something funny to arrive here in that case.\r
+\r
+    MSR_INTEGER(m_idDecision, 2);\r
+    m_nNormal = -1;\r
+    return E_FAIL;                         // drop it\r
+\r
+} // ShouldDrawSampleNow\r
+\r
+\r
+// NOTE we're called by both the window thread and the source filter thread\r
+// so we have to be protected by a critical section (locked before called)\r
+// Also, when the window thread gets signalled to render an image, it always\r
+// does so regardless of how late it is. All the degradation is done when we\r
+// are scheduling the next sample to be drawn. Hence when we start an advise\r
+// link to draw a sample, that sample's time will always become the last one\r
+// drawn - unless of course we stop streaming in which case we cancel links\r
+\r
+BOOL CBaseVideoRenderer::ScheduleSample(IMediaSample *pMediaSample)\r
+{\r
+    // We override ShouldDrawSampleNow to add quality management\r
+\r
+    BOOL bDrawImage = CBaseRenderer::ScheduleSample(pMediaSample);\r
+    if (bDrawImage == FALSE) {\r
+       ++m_cFramesDropped;\r
+       return FALSE;\r
+    }\r
+\r
+    // m_cFramesDrawn must NOT be updated here.  It has to be updated\r
+    // in RecordFrameLateness at the same time as the other statistics.\r
+    return TRUE;\r
+}\r
+\r
+\r
+// Implementation of IQualProp interface needed to support the property page\r
+// This is how the property page gets the data out of the scheduler. We are\r
+// passed into the constructor the owning object in the COM sense, this will\r
+// either be the video renderer or an external IUnknown if we're aggregated.\r
+// We initialise our CUnknown base class with this interface pointer. Then\r
+// all we have to do is to override NonDelegatingQueryInterface to expose\r
+// our IQualProp interface. The AddRef and Release are handled automatically\r
+// by the base class and will be passed on to the appropriate outer object\r
+\r
+STDMETHODIMP CBaseVideoRenderer::get_FramesDroppedInRenderer(__out int *pcFramesDropped)\r
+{\r
+    CheckPointer(pcFramesDropped,E_POINTER);\r
+    CAutoLock cVideoLock(&m_InterfaceLock);\r
+    *pcFramesDropped = m_cFramesDropped;\r
+    return NOERROR;\r
+} // get_FramesDroppedInRenderer\r
+\r
+\r
+// Set *pcFramesDrawn to the number of frames drawn since\r
+// streaming started.\r
+\r
+STDMETHODIMP CBaseVideoRenderer::get_FramesDrawn( int *pcFramesDrawn)\r
+{\r
+    CheckPointer(pcFramesDrawn,E_POINTER);\r
+    CAutoLock cVideoLock(&m_InterfaceLock);\r
+    *pcFramesDrawn = m_cFramesDrawn;\r
+    return NOERROR;\r
+} // get_FramesDrawn\r
+\r
+\r
+// Set iAvgFrameRate to the frames per hundred secs since\r
+// streaming started.  0 otherwise.\r
+\r
+STDMETHODIMP CBaseVideoRenderer::get_AvgFrameRate( int *piAvgFrameRate)\r
+{\r
+    CheckPointer(piAvgFrameRate,E_POINTER);\r
+    CAutoLock cVideoLock(&m_InterfaceLock);\r
+\r
+    int t;\r
+    if (m_bStreaming) {\r
+        t = timeGetTime()-m_tStreamingStart;\r
+    } else {\r
+        t = m_tStreamingStart;\r
+    }\r
+\r
+    if (t<=0) {\r
+        *piAvgFrameRate = 0;\r
+        ASSERT(m_cFramesDrawn == 0);\r
+    } else {\r
+        // i is frames per hundred seconds\r
+        *piAvgFrameRate = MulDiv(100000, m_cFramesDrawn, t);\r
+    }\r
+    return NOERROR;\r
+} // get_AvgFrameRate\r
+\r
+\r
+// Set *piAvg to the average sync offset since streaming started\r
+// in mSec.  The sync offset is the time in mSec between when the frame\r
+// should have been drawn and when the frame was actually drawn.\r
+\r
+STDMETHODIMP CBaseVideoRenderer::get_AvgSyncOffset(__out int *piAvg)\r
+{\r
+    CheckPointer(piAvg,E_POINTER);\r
+    CAutoLock cVideoLock(&m_InterfaceLock);\r
+\r
+    if (NULL==m_pClock) {\r
+        *piAvg = 0;\r
+        return NOERROR;\r
+    }\r
+\r
+    // Note that we didn't gather the stats on the first frame\r
+    // so we use m_cFramesDrawn-1 here\r
+    if (m_cFramesDrawn<=1) {\r
+        *piAvg = 0;\r
+    } else {\r
+        *piAvg = (int)(m_iTotAcc / (m_cFramesDrawn-1));\r
+    }\r
+    return NOERROR;\r
+} // get_AvgSyncOffset\r
+\r
+\r
+// To avoid dragging in the maths library - a cheap\r
+// approximate integer square root.\r
+// We do this by getting a starting guess which is between 1\r
+// and 2 times too large, followed by THREE iterations of\r
+// Newton Raphson.  (That will give accuracy to the nearest mSec\r
+// for the range in question - roughly 0..1000)\r
+//\r
+// It would be faster to use a linear interpolation and ONE NR, but\r
+// who cares.  If anyone does - the best linear interpolation is\r
+// to approximates sqrt(x) by\r
+// y = x * (sqrt(2)-1) + 1 - 1/sqrt(2) + 1/(8*(sqrt(2)-1))\r
+// 0r y = x*0.41421 + 0.59467\r
+// This minimises the maximal error in the range in question.\r
+// (error is about +0.008883 and then one NR will give error .0000something\r
+// (Of course these are integers, so you can't just multiply by 0.41421\r
+// you'd have to do some sort of MulDiv).\r
+// Anyone wanna check my maths?  (This is only for a property display!)\r
+\r
+int isqrt(int x)\r
+{\r
+    int s = 1;\r
+    // Make s an initial guess for sqrt(x)\r
+    if (x > 0x40000000) {\r
+       s = 0x8000;     // prevent any conceivable closed loop\r
+    } else {\r
+        while (s*s<x) {    // loop cannot possible go more than 31 times\r
+            s = 2*s;       // normally it goes about 6 times\r
+        }\r
+        // Three NR iterations.\r
+        if (x==0) {\r
+           s= 0; // Wouldn't it be tragic to divide by zero whenever our\r
+                 // accuracy was perfect!\r
+        } else {\r
+            s = (s*s+x)/(2*s);\r
+            if (s>=0) s = (s*s+x)/(2*s);\r
+            if (s>=0) s = (s*s+x)/(2*s);\r
+        }\r
+    }\r
+    return s;\r
+}\r
+\r
+//\r
+//  Do estimates for standard deviations for per-frame\r
+//  statistics\r
+//\r
+HRESULT CBaseVideoRenderer::GetStdDev(\r
+    int nSamples,\r
+    __out int *piResult,\r
+    LONGLONG llSumSq,\r
+    LONGLONG iTot\r
+)\r
+{\r
+    CheckPointer(piResult,E_POINTER);\r
+    CAutoLock cVideoLock(&m_InterfaceLock);\r
+\r
+    if (NULL==m_pClock) {\r
+        *piResult = 0;\r
+        return NOERROR;\r
+    }\r
+\r
+    // If S is the Sum of the Squares of observations and\r
+    //    T the Total (i.e. sum) of the observations and there were\r
+    //    N observations, then an estimate of the standard deviation is\r
+    //      sqrt( (S - T**2/N) / (N-1) )\r
+\r
+    if (nSamples<=1) {\r
+        *piResult = 0;\r
+    } else {\r
+        LONGLONG x;\r
+        // First frames have invalid stamps, so we get no stats for them\r
+        // So we need 2 frames to get 1 datum, so N is cFramesDrawn-1\r
+\r
+        // so we use m_cFramesDrawn-1 here\r
+        x = llSumSq - llMulDiv(iTot, iTot, nSamples, 0);\r
+        x = x / (nSamples-1);\r
+        ASSERT(x>=0);\r
+        *piResult = isqrt((LONG)x);\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+// Set *piDev to the standard deviation in mSec of the sync offset\r
+// of each frame since streaming started.\r
+\r
+STDMETHODIMP CBaseVideoRenderer::get_DevSyncOffset(__out int *piDev)\r
+{\r
+    // First frames have invalid stamps, so we get no stats for them\r
+    // So we need 2 frames to get 1 datum, so N is cFramesDrawn-1\r
+    return GetStdDev(m_cFramesDrawn - 1,\r
+                     piDev,\r
+                     m_iSumSqAcc,\r
+                     m_iTotAcc);\r
+} // get_DevSyncOffset\r
+\r
+\r
+// Set *piJitter to the standard deviation in mSec of the inter-frame time\r
+// of frames since streaming started.\r
+\r
+STDMETHODIMP CBaseVideoRenderer::get_Jitter(__out int *piJitter)\r
+{\r
+    // First frames have invalid stamps, so we get no stats for them\r
+    // So second frame gives invalid inter-frame time\r
+    // So we need 3 frames to get 1 datum, so N is cFramesDrawn-2\r
+    return GetStdDev(m_cFramesDrawn - 2,\r
+                     piJitter,\r
+                     m_iSumSqFrameTime,\r
+                     m_iSumFrameTime);\r
+} // get_Jitter\r
+\r
+\r
+// Overidden to return our IQualProp interface\r
+\r
+STDMETHODIMP\r
+CBaseVideoRenderer::NonDelegatingQueryInterface(REFIID riid,__deref_out VOID **ppv)\r
+{\r
+    // We return IQualProp and delegate everything else\r
+\r
+    if (riid == IID_IQualProp) {\r
+        return GetInterface( (IQualProp *)this, ppv);\r
+    } else if (riid == IID_IQualityControl) {\r
+        return GetInterface( (IQualityControl *)this, ppv);\r
+    }\r
+    return CBaseRenderer::NonDelegatingQueryInterface(riid,ppv);\r
+}\r
+\r
+\r
+// Override JoinFilterGraph so that, just before leaving\r
+// the graph we can send an EC_WINDOW_DESTROYED event\r
+\r
+STDMETHODIMP\r
+CBaseVideoRenderer::JoinFilterGraph(__inout_opt IFilterGraph *pGraph, __in_opt LPCWSTR pName)\r
+{\r
+    // Since we send EC_ACTIVATE, we also need to ensure\r
+    // we send EC_WINDOW_DESTROYED or the resource manager may be\r
+    // holding us as a focus object\r
+    if (!pGraph && m_pGraph) {\r
+\r
+        // We were in a graph and now we're not\r
+        // Do this properly in case we are aggregated\r
+        IBaseFilter* pFilter = this;\r
+        NotifyEvent(EC_WINDOW_DESTROYED, (LPARAM) pFilter, 0);\r
+    }\r
+    return CBaseFilter::JoinFilterGraph(pGraph, pName);\r
+}\r
+\r
+\r
+// This removes a large number of level 4 warnings from the\r
+// Microsoft compiler which in this case are not very useful\r
+#pragma warning(disable: 4514)\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/renbase.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/renbase.h
new file mode 100644 (file)
index 0000000..c2685bb
--- /dev/null
@@ -0,0 +1,478 @@
+//------------------------------------------------------------------------------\r
+// File: RenBase.h\r
+//\r
+// Desc: DirectShow base classes - defines a generic ActiveX base renderer\r
+//       class.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef __RENBASE__\r
+#define __RENBASE__\r
+\r
+// Forward class declarations\r
+\r
+class CBaseRenderer;\r
+class CBaseVideoRenderer;\r
+class CRendererInputPin;\r
+\r
+// This is our input pin class that channels calls to the renderer\r
+\r
+class CRendererInputPin : public CBaseInputPin\r
+{\r
+protected:\r
+\r
+    CBaseRenderer *m_pRenderer;\r
+\r
+public:\r
+\r
+    CRendererInputPin(__inout CBaseRenderer *pRenderer,\r
+                      __inout HRESULT *phr,\r
+                      __in_opt LPCWSTR Name);\r
+\r
+    // Overriden from the base pin classes\r
+\r
+    HRESULT BreakConnect();\r
+    HRESULT CompleteConnect(IPin *pReceivePin);\r
+    HRESULT SetMediaType(const CMediaType *pmt);\r
+    HRESULT CheckMediaType(const CMediaType *pmt);\r
+    HRESULT Active();\r
+    HRESULT Inactive();\r
+\r
+    // Add rendering behaviour to interface functions\r
+\r
+    STDMETHODIMP QueryId(__deref_out LPWSTR *Id);\r
+    STDMETHODIMP EndOfStream();\r
+    STDMETHODIMP BeginFlush();\r
+    STDMETHODIMP EndFlush();\r
+    STDMETHODIMP Receive(IMediaSample *pMediaSample);\r
+\r
+    // Helper\r
+    IMemAllocator inline *Allocator() const\r
+    {\r
+        return m_pAllocator;\r
+    }\r
+};\r
+\r
+// Main renderer class that handles synchronisation and state changes\r
+\r
+class CBaseRenderer : public CBaseFilter\r
+{\r
+protected:\r
+\r
+    friend class CRendererInputPin;\r
+\r
+    friend void CALLBACK EndOfStreamTimer(UINT uID,      // Timer identifier\r
+                                          UINT uMsg,     // Not currently used\r
+                                          DWORD_PTR dwUser,  // User information\r
+                                          DWORD_PTR dw1,     // Windows reserved\r
+                                          DWORD_PTR dw2);    // Is also reserved\r
+\r
+    CRendererPosPassThru *m_pPosition;  // Media seeking pass by object\r
+    CAMEvent m_RenderEvent;             // Used to signal timer events\r
+    CAMEvent m_ThreadSignal;            // Signalled to release worker thread\r
+    CAMEvent m_evComplete;              // Signalled when state complete\r
+    BOOL m_bAbort;                      // Stop us from rendering more data\r
+    BOOL m_bStreaming;                  // Are we currently streaming\r
+    DWORD_PTR m_dwAdvise;                   // Timer advise cookie\r
+    IMediaSample *m_pMediaSample;       // Current image media sample\r
+    BOOL m_bEOS;                        // Any more samples in the stream\r
+    BOOL m_bEOSDelivered;               // Have we delivered an EC_COMPLETE\r
+    CRendererInputPin *m_pInputPin;     // Our renderer input pin object\r
+    CCritSec m_InterfaceLock;           // Critical section for interfaces\r
+    CCritSec m_RendererLock;            // Controls access to internals\r
+    IQualityControl * m_pQSink;         // QualityControl sink\r
+    BOOL m_bRepaintStatus;              // Can we signal an EC_REPAINT\r
+    //  Avoid some deadlocks by tracking filter during stop\r
+    volatile BOOL  m_bInReceive;        // Inside Receive between PrepareReceive\r
+                                        // And actually processing the sample\r
+    REFERENCE_TIME m_SignalTime;        // Time when we signal EC_COMPLETE\r
+    UINT m_EndOfStreamTimer;            // Used to signal end of stream\r
+    CCritSec m_ObjectCreationLock;      // This lock protects the creation and\r
+                                        // of m_pPosition and m_pInputPin.  It\r
+                                        // ensures that two threads cannot create\r
+                                        // either object simultaneously.\r
+\r
+public:\r
+\r
+    CBaseRenderer(REFCLSID RenderClass, // CLSID for this renderer\r
+                  __in_opt LPCTSTR pName,         // Debug ONLY description\r
+                  __inout_opt LPUNKNOWN pUnk,       // Aggregated owner object\r
+                  __inout HRESULT *phr);        // General OLE return code\r
+\r
+    ~CBaseRenderer();\r
+\r
+    // Overriden to say what interfaces we support and where\r
+\r
+    virtual HRESULT GetMediaPositionInterface(REFIID riid, __deref_out void **ppv);\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID, __deref_out void **);\r
+\r
+    virtual HRESULT SourceThreadCanWait(BOOL bCanWait);\r
+\r
+#ifdef DEBUG\r
+    // Debug only dump of the renderer state\r
+    void DisplayRendererState();\r
+#endif\r
+    virtual HRESULT WaitForRenderTime();\r
+    virtual HRESULT CompleteStateChange(FILTER_STATE OldState);\r
+\r
+    // Return internal information about this filter\r
+\r
+    BOOL IsEndOfStream() { return m_bEOS; };\r
+    BOOL IsEndOfStreamDelivered() { return m_bEOSDelivered; };\r
+    BOOL IsStreaming() { return m_bStreaming; };\r
+    void SetAbortSignal(BOOL bAbort) { m_bAbort = bAbort; };\r
+    virtual void OnReceiveFirstSample(IMediaSample *pMediaSample) { };\r
+    CAMEvent *GetRenderEvent() { return &m_RenderEvent; };\r
+\r
+    // Permit access to the transition state\r
+\r
+    void Ready() { m_evComplete.Set(); };\r
+    void NotReady() { m_evComplete.Reset(); };\r
+    BOOL CheckReady() { return m_evComplete.Check(); };\r
+\r
+    virtual int GetPinCount();\r
+    virtual CBasePin *GetPin(int n);\r
+    FILTER_STATE GetRealState();\r
+    void SendRepaint();\r
+    void SendNotifyWindow(IPin *pPin,HWND hwnd);\r
+    BOOL OnDisplayChange();\r
+    void SetRepaintStatus(BOOL bRepaint);\r
+\r
+    // Override the filter and pin interface functions\r
+\r
+    STDMETHODIMP Stop();\r
+    STDMETHODIMP Pause();\r
+    STDMETHODIMP Run(REFERENCE_TIME StartTime);\r
+    STDMETHODIMP GetState(DWORD dwMSecs, __out FILTER_STATE *State);\r
+    STDMETHODIMP FindPin(LPCWSTR Id, __deref_out IPin **ppPin);\r
+\r
+    // These are available for a quality management implementation\r
+\r
+    virtual void OnRenderStart(IMediaSample *pMediaSample);\r
+    virtual void OnRenderEnd(IMediaSample *pMediaSample);\r
+    virtual HRESULT OnStartStreaming() { return NOERROR; };\r
+    virtual HRESULT OnStopStreaming() { return NOERROR; };\r
+    virtual void OnWaitStart() { };\r
+    virtual void OnWaitEnd() { };\r
+    virtual void PrepareRender() { };\r
+\r
+#ifdef PERF\r
+    REFERENCE_TIME m_trRenderStart; // Just before we started drawing\r
+                                    // Set in OnRenderStart, Used in OnRenderEnd\r
+    int m_idBaseStamp;              // MSR_id for frame time stamp\r
+    int m_idBaseRenderTime;         // MSR_id for true wait time\r
+    int m_idBaseAccuracy;           // MSR_id for time frame is late (int)\r
+#endif\r
+\r
+    // Quality management implementation for scheduling rendering\r
+\r
+    virtual BOOL ScheduleSample(IMediaSample *pMediaSample);\r
+    virtual HRESULT GetSampleTimes(IMediaSample *pMediaSample,\r
+                                   __out REFERENCE_TIME *pStartTime,\r
+                                   __out REFERENCE_TIME *pEndTime);\r
+\r
+    virtual HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample,\r
+                                        __out REFERENCE_TIME *ptrStart,\r
+                                        __out REFERENCE_TIME *ptrEnd);\r
+\r
+    // Lots of end of stream complexities\r
+\r
+    void TimerCallback();\r
+    void ResetEndOfStreamTimer();\r
+    HRESULT NotifyEndOfStream();\r
+    virtual HRESULT SendEndOfStream();\r
+    virtual HRESULT ResetEndOfStream();\r
+    virtual HRESULT EndOfStream();\r
+\r
+    // Rendering is based around the clock\r
+\r
+    void SignalTimerFired();\r
+    virtual HRESULT CancelNotification();\r
+    virtual HRESULT ClearPendingSample();\r
+\r
+    // Called when the filter changes state\r
+\r
+    virtual HRESULT Active();\r
+    virtual HRESULT Inactive();\r
+    virtual HRESULT StartStreaming();\r
+    virtual HRESULT StopStreaming();\r
+    virtual HRESULT BeginFlush();\r
+    virtual HRESULT EndFlush();\r
+\r
+    // Deal with connections and type changes\r
+\r
+    virtual HRESULT BreakConnect();\r
+    virtual HRESULT SetMediaType(const CMediaType *pmt);\r
+    virtual HRESULT CompleteConnect(IPin *pReceivePin);\r
+\r
+    // These look after the handling of data samples\r
+\r
+    virtual HRESULT PrepareReceive(IMediaSample *pMediaSample);\r
+    virtual HRESULT Receive(IMediaSample *pMediaSample);\r
+    virtual BOOL HaveCurrentSample();\r
+    virtual IMediaSample *GetCurrentSample();\r
+    virtual HRESULT Render(IMediaSample *pMediaSample);\r
+\r
+    // Derived classes MUST override these\r
+    virtual HRESULT DoRenderSample(IMediaSample *pMediaSample) PURE;\r
+    virtual HRESULT CheckMediaType(const CMediaType *) PURE;\r
+\r
+    // Helper\r
+    void WaitForReceiveToComplete();\r
+};\r
+\r
+\r
+// CBaseVideoRenderer is a renderer class (see its ancestor class) and\r
+// it handles scheduling of media samples so that they are drawn at the\r
+// correct time by the reference clock.  It implements a degradation\r
+// strategy.  Possible degradation modes are:\r
+//    Drop frames here (only useful if the drawing takes significant time)\r
+//    Signal supplier (upstream) to drop some frame(s) - i.e. one-off skip.\r
+//    Signal supplier to change the frame rate - i.e. ongoing skipping.\r
+//    Or any combination of the above.\r
+// In order to determine what's useful to try we need to know what's going\r
+// on.  This is done by timing various operations (including the supplier).\r
+// This timing is done by using timeGetTime as it is accurate enough and\r
+// usually cheaper than calling the reference clock.  It also tells the\r
+// truth if there is an audio break and the reference clock stops.\r
+// We provide a number of public entry points (named OnXxxStart, OnXxxEnd)\r
+// which the rest of the renderer calls at significant moments.  These do\r
+// the timing.\r
+\r
+// the number of frames that the sliding averages are averaged over.\r
+// the rule is (1024*NewObservation + (AVGPERIOD-1) * PreviousAverage)/AVGPERIOD\r
+#define AVGPERIOD 4\r
+#define DO_MOVING_AVG(avg,obs) (avg = (1024*obs + (AVGPERIOD-1)*avg)/AVGPERIOD)\r
+// Spot the bug in this macro - I can't. but it doesn't work!\r
+\r
+class CBaseVideoRenderer : public CBaseRenderer,    // Base renderer class\r
+                           public IQualProp,        // Property page guff\r
+                           public IQualityControl   // Allow throttling\r
+{\r
+protected:\r
+\r
+    // Hungarian:\r
+    //     tFoo is the time Foo in mSec (beware m_tStart from filter.h)\r
+    //     trBar is the time Bar by the reference clock\r
+\r
+    //******************************************************************\r
+    // State variables to control synchronisation\r
+    //******************************************************************\r
+\r
+    // Control of sending Quality messages.  We need to know whether\r
+    // we are in trouble (e.g. frames being dropped) and where the time\r
+    // is being spent.\r
+\r
+    // When we drop a frame we play the next one early.\r
+    // The frame after that is likely to wait before drawing and counting this\r
+    // wait as spare time is unfair, so we count it as a zero wait.\r
+    // We therefore need to know whether we are playing frames early or not.\r
+\r
+    int m_nNormal;                  // The number of consecutive frames\r
+                                    // drawn at their normal time (not early)\r
+                                    // -1 means we just dropped a frame.\r
+\r
+#ifdef PERF\r
+    BOOL m_bDrawLateFrames;         // Don't drop any frames (debug and I'm\r
+                                    // not keen on people using it!)\r
+#endif\r
+\r
+    BOOL m_bSupplierHandlingQuality;// The response to Quality messages says\r
+                                    // our supplier is handling things.\r
+                                    // We will allow things to go extra late\r
+                                    // before dropping frames.  We will play\r
+                                    // very early after he has dropped one.\r
+\r
+    // Control of scheduling, frame dropping etc.\r
+    // We need to know where the time is being spent so as to tell whether\r
+    // we should be taking action here, signalling supplier or what.\r
+    // The variables are initialised to a mode of NOT dropping frames.\r
+    // They will tell the truth after a few frames.\r
+    // We typically record a start time for an event, later we get the time\r
+    // again and subtract to get the elapsed time, and we average this over\r
+    // a few frames.  The average is used to tell what mode we are in.\r
+\r
+    // Although these are reference times (64 bit) they are all DIFFERENCES\r
+    // between times which are small.  An int will go up to 214 secs before\r
+    // overflow.  Avoiding 64 bit multiplications and divisions seems\r
+    // worth while.\r
+\r
+\r
+\r
+    // Audio-video throttling.  If the user has turned up audio quality\r
+    // very high (in principle it could be any other stream, not just audio)\r
+    // then we can receive cries for help via the graph manager.  In this case\r
+    // we put in a wait for some time after rendering each frame.\r
+    int m_trThrottle;\r
+\r
+    // The time taken to render (i.e. BitBlt) frames controls which component\r
+    // needs to degrade.  If the blt is expensive, the renderer degrades.\r
+    // If the blt is cheap it's done anyway and the supplier degrades.\r
+    int m_trRenderAvg;              // Time frames are taking to blt\r
+    int m_trRenderLast;             // Time for last frame blt\r
+    int m_tRenderStart;             // Just before we started drawing (mSec)\r
+                                    // derived from timeGetTime.\r
+\r
+    // When frames are dropped we will play the next frame as early as we can.\r
+    // If it was a false alarm and the machine is fast we slide gently back to\r
+    // normal timing.  To do this, we record the offset showing just how early\r
+    // we really are.  This will normally be negative meaning early or zero.\r
+    int m_trEarliness;\r
+\r
+    // Target provides slow long-term feedback to try to reduce the\r
+    // average sync offset to zero.  Whenever a frame is actually rendered\r
+    // early we add a msec or two, whenever late we take off a few.\r
+    // We add or take off 1/32 of the error time.\r
+    // Eventually we should be hovering around zero.  For a really bad case\r
+    // where we were (say) 300mSec off, it might take 100 odd frames to\r
+    // settle down.  The rate of change of this is intended to be slower\r
+    // than any other mechanism in Quartz, thereby avoiding hunting.\r
+    int m_trTarget;\r
+\r
+    // The proportion of time spent waiting for the right moment to blt\r
+    // controls whether we bother to drop a frame or whether we reckon that\r
+    // we're doing well enough that we can stand a one-frame glitch.\r
+    int m_trWaitAvg;                // Average of last few wait times\r
+                                    // (actually we just average how early\r
+                                    // we were).  Negative here means LATE.\r
+\r
+    // The average inter-frame time.\r
+    // This is used to calculate the proportion of the time used by the\r
+    // three operations (supplying us, waiting, rendering)\r
+    int m_trFrameAvg;               // Average inter-frame time\r
+    int m_trDuration;               // duration of last frame.\r
+\r
+#ifdef PERF\r
+    // Performance logging identifiers\r
+    int m_idTimeStamp;              // MSR_id for frame time stamp\r
+    int m_idEarliness;              // MSR_id for earliness fudge\r
+    int m_idTarget;                 // MSR_id for Target fudge\r
+    int m_idWaitReal;               // MSR_id for true wait time\r
+    int m_idWait;                   // MSR_id for wait time recorded\r
+    int m_idFrameAccuracy;          // MSR_id for time frame is late (int)\r
+    int m_idRenderAvg;              // MSR_id for Render time recorded (int)\r
+    int m_idSchLateTime;            // MSR_id for lateness at scheduler\r
+    int m_idQualityRate;            // MSR_id for Quality rate requested\r
+    int m_idQualityTime;            // MSR_id for Quality time requested\r
+    int m_idDecision;               // MSR_id for decision code\r
+    int m_idDuration;               // MSR_id for duration of a frame\r
+    int m_idThrottle;               // MSR_id for audio-video throttling\r
+    //int m_idDebug;                  // MSR_id for trace style debugging\r
+    //int m_idSendQuality;          // MSR_id for timing the notifications per se\r
+#endif // PERF\r
+    REFERENCE_TIME m_trRememberStampForPerf;  // original time stamp of frame\r
+                                              // with no earliness fudges etc.\r
+#ifdef PERF\r
+    REFERENCE_TIME m_trRememberFrameForPerf;  // time when previous frame rendered\r
+\r
+    // debug...\r
+    int m_idFrameAvg;\r
+    int m_idWaitAvg;\r
+#endif\r
+\r
+    // PROPERTY PAGE\r
+    // This has edit fields that show the user what's happening\r
+    // These member variables hold these counts.\r
+\r
+    int m_cFramesDropped;           // cumulative frames dropped IN THE RENDERER\r
+    int m_cFramesDrawn;             // Frames since streaming started seen BY THE\r
+                                    // RENDERER (some may be dropped upstream)\r
+\r
+    // Next two support average sync offset and standard deviation of sync offset.\r
+    LONGLONG m_iTotAcc;                  // Sum of accuracies in mSec\r
+    LONGLONG m_iSumSqAcc;           // Sum of squares of (accuracies in mSec)\r
+\r
+    // Next two allow jitter calculation.  Jitter is std deviation of frame time.\r
+    REFERENCE_TIME m_trLastDraw;    // Time of prev frame (for inter-frame times)\r
+    LONGLONG m_iSumSqFrameTime;     // Sum of squares of (inter-frame time in mSec)\r
+    LONGLONG m_iSumFrameTime;            // Sum of inter-frame times in mSec\r
+\r
+    // To get performance statistics on frame rate, jitter etc, we need\r
+    // to record the lateness and inter-frame time.  What we actually need are the\r
+    // data above (sum, sum of squares and number of entries for each) but the data\r
+    // is generated just ahead of time and only later do we discover whether the\r
+    // frame was actually drawn or not.  So we have to hang on to the data\r
+    int m_trLate;                   // hold onto frame lateness\r
+    int m_trFrame;                  // hold onto inter-frame time\r
+\r
+    int m_tStreamingStart;          // if streaming then time streaming started\r
+                                    // else time of last streaming session\r
+                                    // used for property page statistics\r
+#ifdef PERF\r
+    LONGLONG m_llTimeOffset;        // timeGetTime()*10000+m_llTimeOffset==ref time\r
+#endif\r
+\r
+public:\r
+\r
+\r
+    CBaseVideoRenderer(REFCLSID RenderClass, // CLSID for this renderer\r
+                       __in_opt LPCTSTR pName,         // Debug ONLY description\r
+                       __inout_opt LPUNKNOWN pUnk,       // Aggregated owner object\r
+                       __inout HRESULT *phr);        // General OLE return code\r
+\r
+    ~CBaseVideoRenderer();\r
+\r
+    // IQualityControl methods - Notify allows audio-video throttling\r
+\r
+    STDMETHODIMP SetSink( IQualityControl * piqc);\r
+    STDMETHODIMP Notify( IBaseFilter * pSelf, Quality q);\r
+\r
+    // These provide a full video quality management implementation\r
+\r
+    void OnRenderStart(IMediaSample *pMediaSample);\r
+    void OnRenderEnd(IMediaSample *pMediaSample);\r
+    void OnWaitStart();\r
+    void OnWaitEnd();\r
+    HRESULT OnStartStreaming();\r
+    HRESULT OnStopStreaming();\r
+    void ThrottleWait();\r
+\r
+    // Handle the statistics gathering for our quality management\r
+\r
+    void PreparePerformanceData(int trLate, int trFrame);\r
+    virtual void RecordFrameLateness(int trLate, int trFrame);\r
+    virtual void OnDirectRender(IMediaSample *pMediaSample);\r
+    virtual HRESULT ResetStreamingTimes();\r
+    BOOL ScheduleSample(IMediaSample *pMediaSample);\r
+    HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample,\r
+                                __inout REFERENCE_TIME *ptrStart,\r
+                                __inout REFERENCE_TIME *ptrEnd);\r
+\r
+    virtual HRESULT SendQuality(REFERENCE_TIME trLate, REFERENCE_TIME trRealStream);\r
+    STDMETHODIMP JoinFilterGraph(__inout_opt IFilterGraph * pGraph, __in_opt LPCWSTR pName);\r
+\r
+    //\r
+    //  Do estimates for standard deviations for per-frame\r
+    //  statistics\r
+    //\r
+    //  *piResult = (llSumSq - iTot * iTot / m_cFramesDrawn - 1) /\r
+    //                            (m_cFramesDrawn - 2)\r
+    //  or 0 if m_cFramesDrawn <= 3\r
+    //\r
+    HRESULT GetStdDev(\r
+        int nSamples,\r
+        __out int *piResult,\r
+        LONGLONG llSumSq,\r
+        LONGLONG iTot\r
+    );\r
+public:\r
+\r
+    // IQualProp property page support\r
+\r
+    STDMETHODIMP get_FramesDroppedInRenderer(__out int *cFramesDropped);\r
+    STDMETHODIMP get_FramesDrawn(__out int *pcFramesDrawn);\r
+    STDMETHODIMP get_AvgFrameRate(__out int *piAvgFrameRate);\r
+    STDMETHODIMP get_Jitter(__out int *piJitter);\r
+    STDMETHODIMP get_AvgSyncOffset(__out int *piAvg);\r
+    STDMETHODIMP get_DevSyncOffset(__out int *piDev);\r
+\r
+    // Implement an IUnknown interface and expose IQualProp\r
+\r
+    DECLARE_IUNKNOWN\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,__deref_out VOID **ppv);\r
+};\r
+\r
+#endif // __RENBASE__\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/schedule.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/schedule.cpp
new file mode 100644 (file)
index 0000000..7d79830
--- /dev/null
@@ -0,0 +1,284 @@
+//------------------------------------------------------------------------------\r
+// File: Schedule.cpp\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1996-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+\r
+// DbgLog values (all on LOG_TIMING):\r
+//\r
+// 2 for schedulting, firing and shunting of events\r
+// 3 for wait delays and wake-up times of event thread\r
+// 4 for details of whats on the list when the thread awakes\r
+\r
+/* Construct & destructors */\r
+\r
+CAMSchedule::CAMSchedule( HANDLE ev )\r
+: CBaseObject(TEXT("CAMSchedule"))\r
+, head(&z, 0), z(0, MAX_TIME)\r
+, m_dwNextCookie(0), m_dwAdviseCount(0)\r
+, m_pAdviseCache(0), m_dwCacheCount(0)\r
+, m_ev( ev )\r
+{\r
+    head.m_dwAdviseCookie = z.m_dwAdviseCookie = 0;\r
+}\r
+\r
+CAMSchedule::~CAMSchedule()\r
+{\r
+    m_Serialize.Lock();\r
+\r
+    // Delete cache\r
+    CAdvisePacket * p = m_pAdviseCache;\r
+    while (p)\r
+    {\r
+        CAdvisePacket *const p_next = p->m_next;\r
+        delete p;\r
+        p = p_next;\r
+    }\r
+\r
+    ASSERT( m_dwAdviseCount == 0 );\r
+    // Better to be safe than sorry\r
+    if ( m_dwAdviseCount > 0 )\r
+    {\r
+        DumpLinkedList();\r
+        while ( !head.m_next->IsZ() )\r
+        {\r
+            head.DeleteNext();\r
+            --m_dwAdviseCount;\r
+        }\r
+    }\r
+\r
+    // If, in the debug version, we assert twice, it means, not only\r
+    // did we have left over advises, but we have also let m_dwAdviseCount\r
+    // get out of sync. with the number of advises actually on the list.\r
+    ASSERT( m_dwAdviseCount == 0 );\r
+\r
+    m_Serialize.Unlock();\r
+}\r
+\r
+/* Public methods */\r
+\r
+DWORD CAMSchedule::GetAdviseCount()\r
+{\r
+    // No need to lock, m_dwAdviseCount is 32bits & declared volatile\r
+    return m_dwAdviseCount;\r
+}\r
+\r
+REFERENCE_TIME CAMSchedule::GetNextAdviseTime()\r
+{\r
+    CAutoLock lck(&m_Serialize); // Need to stop the linked list from changing\r
+    return head.m_next->m_rtEventTime;\r
+}\r
+\r
+DWORD_PTR CAMSchedule::AddAdvisePacket\r
+( const REFERENCE_TIME & time1\r
+, const REFERENCE_TIME & time2\r
+, HANDLE h, BOOL periodic\r
+)\r
+{\r
+    // Since we use MAX_TIME as a sentry, we can't afford to\r
+    // schedule a notification at MAX_TIME\r
+    ASSERT( time1 < MAX_TIME );\r
+    DWORD_PTR Result;\r
+    CAdvisePacket * p;\r
+\r
+    m_Serialize.Lock();\r
+\r
+    if (m_pAdviseCache)\r
+    {\r
+        p = m_pAdviseCache;\r
+        m_pAdviseCache = p->m_next;\r
+        --m_dwCacheCount;\r
+    }\r
+    else\r
+    {\r
+        p = new CAdvisePacket();\r
+    }\r
+    if (p)\r
+    {\r
+        p->m_rtEventTime = time1; p->m_rtPeriod = time2;\r
+        p->m_hNotify = h; p->m_bPeriodic = periodic;\r
+        Result = AddAdvisePacket( p );\r
+    }\r
+    else Result = 0;\r
+\r
+    m_Serialize.Unlock();\r
+\r
+    return Result;\r
+}\r
+\r
+HRESULT CAMSchedule::Unadvise(DWORD_PTR dwAdviseCookie)\r
+{\r
+    HRESULT hr = S_FALSE;\r
+    CAdvisePacket * p_prev = &head;\r
+    CAdvisePacket * p_n;\r
+    m_Serialize.Lock();\r
+    while ( p_n = p_prev->Next() ) // The Next() method returns NULL when it hits z\r
+    {\r
+        if ( p_n->m_dwAdviseCookie == dwAdviseCookie )\r
+        {\r
+            Delete( p_prev->RemoveNext() );\r
+            --m_dwAdviseCount;\r
+            hr = S_OK;\r
+           // Having found one cookie that matches, there should be no more\r
+            #ifdef DEBUG\r
+              while (p_n = p_prev->Next())\r
+               {\r
+                   ASSERT(p_n->m_dwAdviseCookie != dwAdviseCookie);\r
+                   p_prev = p_n;\r
+               }\r
+            #endif\r
+            break;\r
+        }\r
+        p_prev = p_n;\r
+    };\r
+    m_Serialize.Unlock();\r
+    return hr;\r
+}\r
+\r
+REFERENCE_TIME CAMSchedule::Advise( const REFERENCE_TIME & rtTime )\r
+{\r
+    REFERENCE_TIME  rtNextTime;\r
+    CAdvisePacket * pAdvise;\r
+\r
+    DbgLog((LOG_TIMING, 2,\r
+        TEXT("CAMSchedule::Advise( %lu ms )"), ULONG(rtTime / (UNITS / MILLISECONDS))));\r
+\r
+    CAutoLock lck(&m_Serialize);\r
+\r
+    #ifdef DEBUG\r
+        if (DbgCheckModuleLevel(LOG_TIMING, 4)) DumpLinkedList();\r
+    #endif\r
+\r
+    //  Note - DON'T cache the difference, it might overflow \r
+    while ( rtTime >= (rtNextTime = (pAdvise=head.m_next)->m_rtEventTime) &&\r
+            !pAdvise->IsZ() )\r
+    {\r
+        ASSERT(pAdvise->m_dwAdviseCookie); // If this is zero, its the head or the tail!!\r
+\r
+        ASSERT(pAdvise->m_hNotify != INVALID_HANDLE_VALUE);\r
+\r
+        if (pAdvise->m_bPeriodic == TRUE)\r
+        {\r
+            ReleaseSemaphore(pAdvise->m_hNotify,1,NULL);\r
+            pAdvise->m_rtEventTime += pAdvise->m_rtPeriod;\r
+            ShuntHead();\r
+        }\r
+        else\r
+        {\r
+            ASSERT( pAdvise->m_bPeriodic == FALSE );\r
+            EXECUTE_ASSERT(SetEvent(pAdvise->m_hNotify));\r
+            --m_dwAdviseCount;\r
+            Delete( head.RemoveNext() );\r
+        }\r
+\r
+    }\r
+\r
+    DbgLog((LOG_TIMING, 3,\r
+            TEXT("CAMSchedule::Advise() Next time stamp: %lu ms, for advise %lu."),\r
+            DWORD(rtNextTime / (UNITS / MILLISECONDS)), pAdvise->m_dwAdviseCookie ));\r
+\r
+    return rtNextTime;\r
+}\r
+\r
+/* Private methods */\r
+\r
+DWORD_PTR CAMSchedule::AddAdvisePacket( __inout CAdvisePacket * pPacket )\r
+{\r
+    ASSERT(pPacket->m_rtEventTime >= 0 && pPacket->m_rtEventTime < MAX_TIME);\r
+    ASSERT(CritCheckIn(&m_Serialize));\r
+\r
+    CAdvisePacket * p_prev = &head;\r
+    CAdvisePacket * p_n;\r
+\r
+    const DWORD_PTR Result = pPacket->m_dwAdviseCookie = ++m_dwNextCookie;\r
+    // This relies on the fact that z is a sentry with a maximal m_rtEventTime\r
+    for(;;p_prev = p_n)\r
+    {\r
+        p_n = p_prev->m_next;\r
+        if ( p_n->m_rtEventTime >= pPacket->m_rtEventTime ) break;\r
+    }\r
+    p_prev->InsertAfter( pPacket );\r
+    ++m_dwAdviseCount;\r
+\r
+    DbgLog((LOG_TIMING, 2, TEXT("Added advise %lu, for thread 0x%02X, scheduled at %lu"),\r
+       pPacket->m_dwAdviseCookie, GetCurrentThreadId(), (pPacket->m_rtEventTime / (UNITS / MILLISECONDS)) ));\r
+\r
+    // If packet added at the head, then clock needs to re-evaluate wait time.\r
+    if ( p_prev == &head ) SetEvent( m_ev );\r
+\r
+    return Result;\r
+}\r
+\r
+void CAMSchedule::Delete( __inout CAdvisePacket * pPacket )\r
+{\r
+    if ( m_dwCacheCount >= dwCacheMax ) delete pPacket;\r
+    else\r
+    {\r
+        m_Serialize.Lock();\r
+        pPacket->m_next = m_pAdviseCache;\r
+        m_pAdviseCache = pPacket;\r
+        ++m_dwCacheCount;\r
+        m_Serialize.Unlock();\r
+    }\r
+}\r
+\r
+\r
+// Takes the head of the list & repositions it\r
+void CAMSchedule::ShuntHead()\r
+{\r
+    CAdvisePacket * p_prev = &head;\r
+    CAdvisePacket * p_n;\r
+\r
+    m_Serialize.Lock();\r
+    CAdvisePacket *const pPacket = head.m_next;\r
+\r
+    // This will catch both an empty list,\r
+    // and if somehow a MAX_TIME time gets into the list\r
+    // (which would also break this method).\r
+    ASSERT( pPacket->m_rtEventTime < MAX_TIME );\r
+\r
+    // This relies on the fact that z is a sentry with a maximal m_rtEventTime\r
+    for(;;p_prev = p_n)\r
+    {\r
+        p_n = p_prev->m_next;\r
+        if ( p_n->m_rtEventTime > pPacket->m_rtEventTime ) break;\r
+    }\r
+    // If p_prev == pPacket then we're already in the right place\r
+    if (p_prev != pPacket)\r
+    {\r
+        head.m_next = pPacket->m_next;\r
+        (p_prev->m_next = pPacket)->m_next = p_n;\r
+    }\r
+    #ifdef DEBUG\r
+        DbgLog((LOG_TIMING, 2, TEXT("Periodic advise %lu, shunted to %lu"),\r
+           pPacket->m_dwAdviseCookie, (pPacket->m_rtEventTime / (UNITS / MILLISECONDS)) ));\r
+    #endif\r
+    m_Serialize.Unlock();\r
+}\r
+\r
+\r
+#ifdef DEBUG\r
+void CAMSchedule::DumpLinkedList()\r
+{\r
+    m_Serialize.Lock();\r
+    int i=0;\r
+    DbgLog((LOG_TIMING, 1, TEXT("CAMSchedule::DumpLinkedList() this = 0x%p"), this));\r
+    for ( CAdvisePacket * p = &head\r
+        ; p\r
+        ; p = p->m_next         , i++\r
+        )      \r
+    {\r
+        DbgLog((LOG_TIMING, 1, TEXT("Advise List # %lu, Cookie %d,  RefTime %lu"),\r
+            i,\r
+           p->m_dwAdviseCookie,\r
+           p->m_rtEventTime / (UNITS / MILLISECONDS)\r
+            ));\r
+    }\r
+    m_Serialize.Unlock();\r
+}\r
+#endif\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/schedule.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/schedule.h
new file mode 100644 (file)
index 0000000..c16700a
--- /dev/null
@@ -0,0 +1,128 @@
+//------------------------------------------------------------------------------\r
+// File: Schedule.h\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1996-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef __CAMSchedule__\r
+#define __CAMSchedule__\r
+\r
+class CAMSchedule : private CBaseObject\r
+{\r
+public:\r
+    virtual ~CAMSchedule();\r
+    // ev is the event we should fire if the advise time needs re-evaluating\r
+    CAMSchedule( HANDLE ev );\r
+\r
+    DWORD GetAdviseCount();\r
+    REFERENCE_TIME GetNextAdviseTime();\r
+\r
+    // We need a method for derived classes to add advise packets, we return the cookie\r
+    DWORD_PTR AddAdvisePacket( const REFERENCE_TIME & time1, const REFERENCE_TIME & time2, HANDLE h, BOOL periodic );\r
+    // And a way to cancel\r
+    HRESULT Unadvise(DWORD_PTR dwAdviseCookie);\r
+\r
+    // Tell us the time please, and we'll dispatch the expired events.  We return the time of the next event.\r
+    // NB: The time returned will be "useless" if you start adding extra Advises.  But that's the problem of\r
+    // whoever is using this helper class (typically a clock).\r
+    REFERENCE_TIME Advise( const REFERENCE_TIME & rtTime );\r
+\r
+    // Get the event handle which will be set if advise time requires re-evaluation.\r
+    HANDLE GetEvent() const { return m_ev; }\r
+\r
+private:\r
+    // We define the nodes that will be used in our singly linked list\r
+    // of advise packets.  The list is ordered by time, with the\r
+    // elements that will expire first at the front.\r
+    class CAdvisePacket\r
+    {\r
+    public:\r
+        CAdvisePacket()\r
+        {}\r
+\r
+        CAdvisePacket * m_next;\r
+        DWORD_PTR       m_dwAdviseCookie;\r
+        REFERENCE_TIME  m_rtEventTime;      // Time at which event should be set\r
+        REFERENCE_TIME  m_rtPeriod;         // Periodic time\r
+        HANDLE          m_hNotify;          // Handle to event or semephore\r
+        BOOL            m_bPeriodic;        // TRUE => Periodic event\r
+\r
+        CAdvisePacket( __inout_opt CAdvisePacket * next, LONGLONG time ) : m_next(next), m_rtEventTime(time)\r
+        {}\r
+\r
+        void InsertAfter( __inout CAdvisePacket * p )\r
+        {\r
+            p->m_next = m_next;\r
+            m_next    = p;\r
+        }\r
+\r
+        int IsZ() const // That is, is it the node that represents the end of the list\r
+        { return m_next == 0; }\r
+\r
+        CAdvisePacket * RemoveNext()\r
+        {\r
+            CAdvisePacket *const next = m_next;\r
+            CAdvisePacket *const new_next = next->m_next;\r
+            m_next = new_next;\r
+            return next;\r
+        }\r
+\r
+        void DeleteNext()\r
+        {\r
+            delete RemoveNext();\r
+        }\r
+\r
+        CAdvisePacket * Next() const\r
+        {\r
+            CAdvisePacket * result = m_next;\r
+            if (result->IsZ()) result = 0;\r
+            return result;\r
+        }\r
+\r
+        DWORD_PTR Cookie() const\r
+        { return m_dwAdviseCookie; }\r
+    };\r
+\r
+    // Structure is:\r
+    // head -> elmt1 -> elmt2 -> z -> null\r
+    // So an empty list is:       head -> z -> null\r
+    // Having head & z as links makes insertaion,\r
+    // deletion and shunting much easier.\r
+    CAdvisePacket   head, z;            // z is both a tail and a sentry\r
+\r
+    volatile DWORD_PTR  m_dwNextCookie;     // Strictly increasing\r
+    volatile DWORD  m_dwAdviseCount;    // Number of elements on list\r
+\r
+    CCritSec        m_Serialize;\r
+\r
+    // AddAdvisePacket: adds the packet, returns the cookie (0 if failed)\r
+    DWORD_PTR AddAdvisePacket( __inout CAdvisePacket * pPacket );\r
+    // Event that we should set if the packed added above will be the next to fire.\r
+    const HANDLE m_ev;\r
+\r
+    // A Shunt is where we have changed the first element in the\r
+    // list and want it re-evaluating (i.e. repositioned) in\r
+    // the list.\r
+    void ShuntHead();\r
+\r
+    // Rather than delete advise packets, we cache them for future use\r
+    CAdvisePacket * m_pAdviseCache;\r
+    DWORD           m_dwCacheCount;\r
+    enum { dwCacheMax = 5 };             // Don't bother caching more than five\r
+\r
+    void Delete( __inout CAdvisePacket * pLink );// This "Delete" will cache the Link\r
+\r
+// Attributes and methods for debugging\r
+public:\r
+#ifdef DEBUG\r
+    void DumpLinkedList();\r
+#else\r
+    void DumpLinkedList() {}\r
+#endif\r
+\r
+};\r
+\r
+#endif // __CAMSchedule__\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/seekpt.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/seekpt.cpp
new file mode 100644 (file)
index 0000000..bb13d6f
--- /dev/null
@@ -0,0 +1,83 @@
+//------------------------------------------------------------------------------\r
+// File: SeekPT.cpp\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+#include "seekpt.h"\r
+\r
+//==================================================================\r
+// CreateInstance\r
+// This goes in the factory template table to create new instances\r
+// If there is already a mapper instance - return that, else make one\r
+// and save it in a static variable so that forever after we can return that.\r
+//==================================================================\r
+\r
+CUnknown * CSeekingPassThru::CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr)\r
+{\r
+    return new CSeekingPassThru(NAME("Seeking PassThru"),pUnk, phr);\r
+}\r
+\r
+\r
+STDMETHODIMP CSeekingPassThru::NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv)\r
+{\r
+    if (riid == IID_ISeekingPassThru) {\r
+        return GetInterface((ISeekingPassThru *) this, ppv);\r
+    } else {\r
+        if (m_pPosPassThru &&\r
+            (riid == IID_IMediaSeeking ||\r
+             riid == IID_IMediaPosition)) {\r
+            return m_pPosPassThru->NonDelegatingQueryInterface(riid,ppv);\r
+        } else {\r
+            return CUnknown::NonDelegatingQueryInterface(riid, ppv);\r
+        }\r
+    }\r
+}\r
+\r
+\r
+CSeekingPassThru::CSeekingPassThru( __in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr )\r
+                            : CUnknown(pName, pUnk, phr),\r
+                            m_pPosPassThru(NULL)\r
+{\r
+}\r
+\r
+\r
+CSeekingPassThru::~CSeekingPassThru()\r
+{\r
+    delete m_pPosPassThru;\r
+}\r
+\r
+STDMETHODIMP CSeekingPassThru::Init(BOOL bRendererSeeking, IPin *pPin)\r
+{\r
+    HRESULT hr = NOERROR;\r
+    if (m_pPosPassThru) {\r
+        hr = E_FAIL;\r
+    } else {\r
+        m_pPosPassThru =\r
+            bRendererSeeking ?\r
+                new CRendererPosPassThru(\r
+                    NAME("Render Seeking COM object"),\r
+                    (IUnknown *)this,\r
+                    &hr,\r
+                    pPin) :\r
+                new CPosPassThru(\r
+                    NAME("Render Seeking COM object"),\r
+                    (IUnknown *)this,\r
+                    &hr,\r
+                    pPin);\r
+        if (!m_pPosPassThru) {\r
+            hr = E_OUTOFMEMORY;\r
+        } else {\r
+            if (FAILED(hr)) {\r
+                delete m_pPosPassThru;\r
+                m_pPosPassThru = NULL;\r
+            }\r
+        }\r
+    }\r
+    return hr;\r
+}\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/seekpt.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/seekpt.h
new file mode 100644 (file)
index 0000000..208d418
--- /dev/null
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------\r
+// File: SeekPT.h\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef __seekpt_h__\r
+#define __seekpt_h__\r
+\r
+\r
+class CSeekingPassThru : public ISeekingPassThru, public CUnknown\r
+{\r
+public:\r
+    static CUnknown *CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr);\r
+    CSeekingPassThru(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr);\r
+    ~CSeekingPassThru();\r
+\r
+    DECLARE_IUNKNOWN;\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv);\r
+\r
+    STDMETHODIMP Init(BOOL bSupportRendering, IPin *pPin);\r
+\r
+private:\r
+    CPosPassThru              *m_pPosPassThru;\r
+};\r
+\r
+#endif\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/source.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/source.cpp
new file mode 100644 (file)
index 0000000..ef7795c
--- /dev/null
@@ -0,0 +1,522 @@
+//------------------------------------------------------------------------------\r
+// File: Source.cpp\r
+//\r
+// Desc: DirectShow  base classes - implements CSource, which is a Quartz\r
+//       source filter 'template.'\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+// Locking Strategy.\r
+//\r
+// Hold the filter critical section (m_pFilter->pStateLock()) to serialise\r
+// access to functions. Note that, in general, this lock may be held\r
+// by a function when the worker thread may want to hold it. Therefore\r
+// if you wish to access shared state from the worker thread you will\r
+// need to add another critical section object. The execption is during\r
+// the threads processing loop, when it is safe to get the filter critical\r
+// section from within FillBuffer().\r
+\r
+#include <streams.h>\r
+\r
+\r
+//\r
+// CSource::Constructor\r
+//\r
+// Initialise the pin count for the filter. The user will create the pins in\r
+// the derived class.\r
+CSource::CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid)\r
+    : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),\r
+      m_iPins(0),\r
+      m_paStreams(NULL)\r
+{\r
+}\r
+\r
+CSource::CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr)\r
+    : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),\r
+      m_iPins(0),\r
+      m_paStreams(NULL)\r
+{\r
+    UNREFERENCED_PARAMETER(phr);\r
+}\r
+\r
+#ifdef UNICODE\r
+CSource::CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid)\r
+    : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),\r
+      m_iPins(0),\r
+      m_paStreams(NULL)\r
+{\r
+}\r
+\r
+CSource::CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr)\r
+    : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),\r
+      m_iPins(0),\r
+      m_paStreams(NULL)\r
+{\r
+    UNREFERENCED_PARAMETER(phr);\r
+}\r
+#endif\r
+\r
+//\r
+// CSource::Destructor\r
+//\r
+CSource::~CSource()\r
+{\r
+    /*  Free our pins and pin array */\r
+    while (m_iPins != 0) {\r
+       // deleting the pins causes them to be removed from the array...\r
+       delete m_paStreams[m_iPins - 1];\r
+    }\r
+\r
+    ASSERT(m_paStreams == NULL);\r
+}\r
+\r
+\r
+//\r
+//  Add a new pin\r
+//\r
+HRESULT CSource::AddPin(__in CSourceStream *pStream)\r
+{\r
+    CAutoLock lock(&m_cStateLock);\r
+\r
+    /*  Allocate space for this pin and the old ones */\r
+    CSourceStream **paStreams = new CSourceStream *[m_iPins + 1];\r
+    if (paStreams == NULL) {\r
+        return E_OUTOFMEMORY;\r
+    }\r
+    if (m_paStreams != NULL) {\r
+        CopyMemory((PVOID)paStreams, (PVOID)m_paStreams,\r
+                   m_iPins * sizeof(m_paStreams[0]));\r
+        paStreams[m_iPins] = pStream;\r
+        delete [] m_paStreams;\r
+    }\r
+    m_paStreams = paStreams;\r
+    m_paStreams[m_iPins] = pStream;\r
+    m_iPins++;\r
+    return S_OK;\r
+}\r
+\r
+//\r
+//  Remove a pin - pStream is NOT deleted\r
+//\r
+HRESULT CSource::RemovePin(__in CSourceStream *pStream)\r
+{\r
+    int i;\r
+    for (i = 0; i < m_iPins; i++) {\r
+        if (m_paStreams[i] == pStream) {\r
+            if (m_iPins == 1) {\r
+                delete [] m_paStreams;\r
+                m_paStreams = NULL;\r
+            } else {\r
+                /*  no need to reallocate */\r
+               while (++i < m_iPins)\r
+                   m_paStreams[i - 1] = m_paStreams[i];\r
+            }\r
+            m_iPins--;\r
+            return S_OK;\r
+        }\r
+    }\r
+    return S_FALSE;\r
+}\r
+\r
+//\r
+// FindPin\r
+//\r
+// Set *ppPin to the IPin* that has the id Id.\r
+// or to NULL if the Id cannot be matched.\r
+STDMETHODIMP CSource::FindPin(LPCWSTR Id, __deref_out IPin **ppPin)\r
+{\r
+    CheckPointer(ppPin,E_POINTER);\r
+    ValidateReadWritePtr(ppPin,sizeof(IPin *));\r
+    // The -1 undoes the +1 in QueryId and ensures that totally invalid\r
+    // strings (for which WstrToInt delivers 0) give a deliver a NULL pin.\r
+    int i = WstrToInt(Id) -1;\r
+    *ppPin = GetPin(i);\r
+    if (*ppPin!=NULL){\r
+        (*ppPin)->AddRef();\r
+        return NOERROR;\r
+    } else {\r
+        return VFW_E_NOT_FOUND;\r
+    }\r
+}\r
+\r
+//\r
+// FindPinNumber\r
+//\r
+// return the number of the pin with this IPin* or -1 if none\r
+int CSource::FindPinNumber(__in IPin *iPin) {\r
+    int i;\r
+    for (i=0; i<m_iPins; ++i) {\r
+        if ((IPin *)(m_paStreams[i])==iPin) {\r
+            return i;\r
+        }\r
+    }\r
+    return -1;\r
+}\r
+\r
+//\r
+// GetPinCount\r
+//\r
+// Returns the number of pins this filter has\r
+int CSource::GetPinCount(void) {\r
+\r
+    CAutoLock lock(&m_cStateLock);\r
+    return m_iPins;\r
+}\r
+\r
+\r
+//\r
+// GetPin\r
+//\r
+// Return a non-addref'd pointer to pin n\r
+// needed by CBaseFilter\r
+CBasePin *CSource::GetPin(int n) {\r
+\r
+    CAutoLock lock(&m_cStateLock);\r
+\r
+    // n must be in the range 0..m_iPins-1\r
+    // if m_iPins>n  && n>=0 it follows that m_iPins>0\r
+    // which is what used to be checked (i.e. checking that we have a pin)\r
+    if ((n >= 0) && (n < m_iPins)) {\r
+\r
+        ASSERT(m_paStreams[n]);\r
+       return m_paStreams[n];\r
+    }\r
+    return NULL;\r
+}\r
+\r
+\r
+//\r
+\r
+\r
+// *\r
+// * --- CSourceStream ----\r
+// *\r
+\r
+//\r
+// Set Id to point to a CoTaskMemAlloc'd\r
+STDMETHODIMP CSourceStream::QueryId(__deref_out LPWSTR *Id) {\r
+    CheckPointer(Id,E_POINTER);\r
+    ValidateReadWritePtr(Id,sizeof(LPWSTR));\r
+\r
+    // We give the pins id's which are 1,2,...\r
+    // FindPinNumber returns -1 for an invalid pin\r
+    int i = 1+ m_pFilter->FindPinNumber(this);\r
+    if (i<1) return VFW_E_NOT_FOUND;\r
+    *Id = (LPWSTR)CoTaskMemAlloc(sizeof(WCHAR) * 12);\r
+    if (*Id==NULL) {\r
+       return E_OUTOFMEMORY;\r
+    }\r
+    IntToWstr(i, *Id);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+\r
+//\r
+// CSourceStream::Constructor\r
+//\r
+// increments the number of pins present on the filter\r
+CSourceStream::CSourceStream(\r
+    __in_opt LPCTSTR pObjectName,\r
+    __inout HRESULT *phr,\r
+    __inout CSource *ps,\r
+    __in_opt LPCWSTR pPinName)\r
+    : CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),\r
+      m_pFilter(ps) {\r
+\r
+     *phr = m_pFilter->AddPin(this);\r
+}\r
+\r
+#ifdef UNICODE\r
+CSourceStream::CSourceStream(\r
+    __in_opt LPCSTR pObjectName,\r
+    __inout HRESULT *phr,\r
+    __inout CSource *ps,\r
+    __in_opt LPCWSTR pPinName)\r
+    : CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),\r
+      m_pFilter(ps) {\r
+\r
+     *phr = m_pFilter->AddPin(this);\r
+}\r
+#endif\r
+//\r
+// CSourceStream::Destructor\r
+//\r
+// Decrements the number of pins on this filter\r
+CSourceStream::~CSourceStream(void) {\r
+\r
+     m_pFilter->RemovePin(this);\r
+}\r
+\r
+\r
+//\r
+// CheckMediaType\r
+//\r
+// Do we support this type? Provides the default support for 1 type.\r
+HRESULT CSourceStream::CheckMediaType(const CMediaType *pMediaType) {\r
+\r
+    CAutoLock lock(m_pFilter->pStateLock());\r
+\r
+    CMediaType mt;\r
+    GetMediaType(&mt);\r
+\r
+    if (mt == *pMediaType) {\r
+        return NOERROR;\r
+    }\r
+\r
+    return E_FAIL;\r
+}\r
+\r
+\r
+//\r
+// GetMediaType/3\r
+//\r
+// By default we support only one type\r
+// iPosition indexes are 0-n\r
+HRESULT CSourceStream::GetMediaType(int iPosition, __inout CMediaType *pMediaType) {\r
+\r
+    CAutoLock lock(m_pFilter->pStateLock());\r
+\r
+    if (iPosition<0) {\r
+        return E_INVALIDARG;\r
+    }\r
+    if (iPosition>0) {\r
+        return VFW_S_NO_MORE_ITEMS;\r
+    }\r
+    return GetMediaType(pMediaType);\r
+}\r
+\r
+\r
+//\r
+// Active\r
+//\r
+// The pin is active - start up the worker thread\r
+HRESULT CSourceStream::Active(void) {\r
+\r
+    CAutoLock lock(m_pFilter->pStateLock());\r
+\r
+    HRESULT hr;\r
+\r
+    if (m_pFilter->IsActive()) {\r
+       return S_FALSE; // succeeded, but did not allocate resources (they already exist...)\r
+    }\r
+\r
+    // do nothing if not connected - its ok not to connect to\r
+    // all pins of a source filter\r
+    if (!IsConnected()) {\r
+        return NOERROR;\r
+    }\r
+\r
+    hr = CBaseOutputPin::Active();\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    ASSERT(!ThreadExists());\r
+\r
+    // start the thread\r
+    if (!Create()) {\r
+        return E_FAIL;\r
+    }\r
+\r
+    // Tell thread to initialize. If OnThreadCreate Fails, so does this.\r
+    hr = Init();\r
+    if (FAILED(hr))\r
+       return hr;\r
+\r
+    return Pause();\r
+}\r
+\r
+\r
+//\r
+// Inactive\r
+//\r
+// Pin is inactive - shut down the worker thread\r
+// Waits for the worker to exit before returning.\r
+HRESULT CSourceStream::Inactive(void) {\r
+\r
+    CAutoLock lock(m_pFilter->pStateLock());\r
+\r
+    HRESULT hr;\r
+\r
+    // do nothing if not connected - its ok not to connect to\r
+    // all pins of a source filter\r
+    if (!IsConnected()) {\r
+        return NOERROR;\r
+    }\r
+\r
+    // !!! need to do this before trying to stop the thread, because\r
+    // we may be stuck waiting for our own allocator!!!\r
+\r
+    hr = CBaseOutputPin::Inactive();  // call this first to Decommit the allocator\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    if (ThreadExists()) {\r
+       hr = Stop();\r
+\r
+       if (FAILED(hr)) {\r
+           return hr;\r
+       }\r
+\r
+       hr = Exit();\r
+       if (FAILED(hr)) {\r
+           return hr;\r
+       }\r
+\r
+       Close();        // Wait for the thread to exit, then tidy up.\r
+    }\r
+\r
+    // hr = CBaseOutputPin::Inactive();  // call this first to Decommit the allocator\r
+    //if (FAILED(hr)) {\r
+    // return hr;\r
+    //}\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+//\r
+// ThreadProc\r
+//\r
+// When this returns the thread exits\r
+// Return codes > 0 indicate an error occured\r
+DWORD CSourceStream::ThreadProc(void) {\r
+\r
+    HRESULT hr;  // the return code from calls\r
+    Command com;\r
+\r
+    do {\r
+       com = GetRequest();\r
+       if (com != CMD_INIT) {\r
+           DbgLog((LOG_ERROR, 1, TEXT("Thread expected init command")));\r
+           Reply((DWORD) E_UNEXPECTED);\r
+       }\r
+    } while (com != CMD_INIT);\r
+\r
+    DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread initializing")));\r
+\r
+    hr = OnThreadCreate(); // perform set up tasks\r
+    if (FAILED(hr)) {\r
+        DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadCreate failed. Aborting thread.")));\r
+        OnThreadDestroy();\r
+       Reply(hr);      // send failed return code from OnThreadCreate\r
+        return 1;\r
+    }\r
+\r
+    // Initialisation suceeded\r
+    Reply(NOERROR);\r
+\r
+    Command cmd;\r
+    do {\r
+       cmd = GetRequest();\r
+\r
+       switch (cmd) {\r
+\r
+       case CMD_EXIT:\r
+           Reply(NOERROR);\r
+           break;\r
+\r
+       case CMD_RUN:\r
+           DbgLog((LOG_ERROR, 1, TEXT("CMD_RUN received before a CMD_PAUSE???")));\r
+           // !!! fall through???\r
+       \r
+       case CMD_PAUSE:\r
+           Reply(NOERROR);\r
+           DoBufferProcessingLoop();\r
+           break;\r
+\r
+       case CMD_STOP:\r
+           Reply(NOERROR);\r
+           break;\r
+\r
+       default:\r
+           DbgLog((LOG_ERROR, 1, TEXT("Unknown command %d received!"), cmd));\r
+           Reply((DWORD) E_NOTIMPL);\r
+           break;\r
+       }\r
+    } while (cmd != CMD_EXIT);\r
+\r
+    hr = OnThreadDestroy();    // tidy up.\r
+    if (FAILED(hr)) {\r
+        DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadDestroy failed. Exiting thread.")));\r
+        return 1;\r
+    }\r
+\r
+    DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread exiting")));\r
+    return 0;\r
+}\r
+\r
+\r
+//\r
+// DoBufferProcessingLoop\r
+//\r
+// Grabs a buffer and calls the users processing function.\r
+// Overridable, so that different delivery styles can be catered for.\r
+HRESULT CSourceStream::DoBufferProcessingLoop(void) {\r
+\r
+    Command com;\r
+\r
+    OnThreadStartPlay();\r
+\r
+    do {\r
+       while (!CheckRequest(&com)) {\r
+\r
+           IMediaSample *pSample;\r
+\r
+           HRESULT hr = GetDeliveryBuffer(&pSample,NULL,NULL,0);\r
+           if (FAILED(hr)) {\r
+                Sleep(1);\r
+               continue;       // go round again. Perhaps the error will go away\r
+                           // or the allocator is decommited & we will be asked to\r
+                           // exit soon.\r
+           }\r
+\r
+           // Virtual function user will override.\r
+           hr = FillBuffer(pSample);\r
+\r
+           if (hr == S_OK) {\r
+               hr = Deliver(pSample);\r
+                pSample->Release();\r
+\r
+                // downstream filter returns S_FALSE if it wants us to\r
+                // stop or an error if it's reporting an error.\r
+                if(hr != S_OK)\r
+                {\r
+                  DbgLog((LOG_TRACE, 2, TEXT("Deliver() returned %08x; stopping"), hr));\r
+                  return S_OK;\r
+                }\r
+\r
+           } else if (hr == S_FALSE) {\r
+                // derived class wants us to stop pushing data\r
+               pSample->Release();\r
+               DeliverEndOfStream();\r
+               return S_OK;\r
+           } else {\r
+                // derived class encountered an error\r
+                pSample->Release();\r
+               DbgLog((LOG_ERROR, 1, TEXT("Error %08lX from FillBuffer!!!"), hr));\r
+                DeliverEndOfStream();\r
+                m_pFilter->NotifyEvent(EC_ERRORABORT, hr, 0);\r
+                return hr;\r
+           }\r
+\r
+            // all paths release the sample\r
+       }\r
+\r
+        // For all commands sent to us there must be a Reply call!\r
+\r
+       if (com == CMD_RUN || com == CMD_PAUSE) {\r
+           Reply(NOERROR);\r
+       } else if (com != CMD_STOP) {\r
+           Reply((DWORD) E_UNEXPECTED);\r
+           DbgLog((LOG_ERROR, 1, TEXT("Unexpected command!!!")));\r
+       }\r
+    } while (com != CMD_STOP);\r
+\r
+    return S_FALSE;\r
+}\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/source.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/source.h
new file mode 100644 (file)
index 0000000..528d5bc
--- /dev/null
@@ -0,0 +1,172 @@
+//------------------------------------------------------------------------------\r
+// File: Source.h\r
+//\r
+// Desc: DirectShow base classes - defines classes to simplify creation of\r
+//       ActiveX source filters that support continuous generation of data.\r
+//       No support is provided for IMediaControl or IMediaPosition.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+//\r
+// Derive your source filter from CSource.\r
+// During construction either:\r
+//    Create some CSourceStream objects to manage your pins\r
+//    Provide the user with a means of doing so eg, an IPersistFile interface.\r
+//\r
+// CSource provides:\r
+//    IBaseFilter interface management\r
+//    IMediaFilter interface management, via CBaseFilter\r
+//    Pin counting for CBaseFilter\r
+//\r
+// Derive a class from CSourceStream to manage your output pin types\r
+//  Implement GetMediaType/1 to return the type you support. If you support multiple\r
+//   types then overide GetMediaType/3, CheckMediaType and GetMediaTypeCount.\r
+//  Implement Fillbuffer() to put data into one buffer.\r
+//\r
+// CSourceStream provides:\r
+//    IPin management via CBaseOutputPin\r
+//    Worker thread management\r
+\r
+#ifndef __CSOURCE__\r
+#define __CSOURCE__\r
+\r
+class CSourceStream;  // The class that will handle each pin\r
+\r
+\r
+//\r
+// CSource\r
+//\r
+// Override construction to provide a means of creating\r
+// CSourceStream derived objects - ie a way of creating pins.\r
+class CSource : public CBaseFilter {\r
+public:\r
+\r
+    CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr);\r
+    CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid);\r
+#ifdef UNICODE\r
+    CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr);\r
+    CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid);\r
+#endif\r
+    ~CSource();\r
+\r
+    int       GetPinCount(void);\r
+    CBasePin *GetPin(int n);\r
+\r
+    // -- Utilities --\r
+\r
+    CCritSec*  pStateLock(void) { return &m_cStateLock; }      // provide our critical section\r
+\r
+    HRESULT     AddPin(__in CSourceStream *);\r
+    HRESULT     RemovePin(__in CSourceStream *);\r
+\r
+    STDMETHODIMP FindPin(\r
+        LPCWSTR Id,\r
+        __deref_out IPin ** ppPin\r
+    );\r
+\r
+    int FindPinNumber(__in IPin *iPin);\r
+    \r
+protected:\r
+\r
+    int             m_iPins;       // The number of pins on this filter. Updated by CSourceStream\r
+                                  // constructors & destructors.\r
+    CSourceStream **m_paStreams;   // the pins on this filter.\r
+\r
+    CCritSec m_cStateLock;     // Lock this to serialize function accesses to the filter state\r
+\r
+};\r
+\r
+\r
+//\r
+// CSourceStream\r
+//\r
+// Use this class to manage a stream of data that comes from a\r
+// pin.\r
+// Uses a worker thread to put data on the pin.\r
+class CSourceStream : public CAMThread, public CBaseOutputPin {\r
+public:\r
+\r
+    CSourceStream(__in_opt LPCTSTR pObjectName,\r
+                  __inout HRESULT *phr,\r
+                  __inout CSource *pms,\r
+                  __in_opt LPCWSTR pName);\r
+#ifdef UNICODE\r
+    CSourceStream(__in_opt LPCSTR pObjectName,\r
+                  __inout HRESULT *phr,\r
+                  __inout CSource *pms,\r
+                  __in_opt LPCWSTR pName);\r
+#endif\r
+    virtual ~CSourceStream(void);  // virtual destructor ensures derived class destructors are called too.\r
+\r
+protected:\r
+\r
+    CSource *m_pFilter;        // The parent of this stream\r
+\r
+    // *\r
+    // * Data Source\r
+    // *\r
+    // * The following three functions: FillBuffer, OnThreadCreate/Destroy, are\r
+    // * called from within the ThreadProc. They are used in the creation of\r
+    // * the media samples this pin will provide\r
+    // *\r
+\r
+    // Override this to provide the worker thread a means\r
+    // of processing a buffer\r
+    virtual HRESULT FillBuffer(IMediaSample *pSamp) PURE;\r
+\r
+    // Called as the thread is created/destroyed - use to perform\r
+    // jobs such as start/stop streaming mode\r
+    // If OnThreadCreate returns an error the thread will exit.\r
+    virtual HRESULT OnThreadCreate(void) {return NOERROR;};\r
+    virtual HRESULT OnThreadDestroy(void) {return NOERROR;};\r
+    virtual HRESULT OnThreadStartPlay(void) {return NOERROR;};\r
+\r
+    // *\r
+    // * Worker Thread\r
+    // *\r
+\r
+    HRESULT Active(void);    // Starts up the worker thread\r
+    HRESULT Inactive(void);  // Exits the worker thread.\r
+\r
+public:\r
+    // thread commands\r
+    enum Command {CMD_INIT, CMD_PAUSE, CMD_RUN, CMD_STOP, CMD_EXIT};\r
+    HRESULT Init(void) { return CallWorker(CMD_INIT); }\r
+    HRESULT Exit(void) { return CallWorker(CMD_EXIT); }\r
+    HRESULT Run(void) { return CallWorker(CMD_RUN); }\r
+    HRESULT Pause(void) { return CallWorker(CMD_PAUSE); }\r
+    HRESULT Stop(void) { return CallWorker(CMD_STOP); }\r
+\r
+protected:\r
+    Command GetRequest(void) { return (Command) CAMThread::GetRequest(); }\r
+    BOOL    CheckRequest(Command *pCom) { return CAMThread::CheckRequest( (DWORD *) pCom); }\r
+\r
+    // override these if you want to add thread commands\r
+    virtual DWORD ThreadProc(void);            // the thread function\r
+\r
+    virtual HRESULT DoBufferProcessingLoop(void);    // the loop executed whilst running\r
+\r
+\r
+    // *\r
+    // * AM_MEDIA_TYPE support\r
+    // *\r
+\r
+    // If you support more than one media type then override these 2 functions\r
+    virtual HRESULT CheckMediaType(const CMediaType *pMediaType);\r
+    virtual HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType);  // List pos. 0-n\r
+\r
+    // If you support only one type then override this fn.\r
+    // This will only be called by the default implementations\r
+    // of CheckMediaType and GetMediaType(int, CMediaType*)\r
+    // You must override this fn. or the above 2!\r
+    virtual HRESULT GetMediaType(__inout CMediaType *pMediaType) {return E_UNEXPECTED;}\r
+\r
+    STDMETHODIMP QueryId(\r
+        __deref_out LPWSTR * Id\r
+    );\r
+};\r
+\r
+#endif // __CSOURCE__\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/streams.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/streams.h
new file mode 100644 (file)
index 0000000..1926321
--- /dev/null
@@ -0,0 +1,202 @@
+//------------------------------------------------------------------------------\r
+// File: Streams.h\r
+//\r
+// Desc: DirectShow base classes - defines overall streams architecture.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef __STREAMS__\r
+#define __STREAMS__\r
+\r
+#ifdef _MSC_VER\r
+// disable some level-4 warnings, use #pragma warning(enable:###) to re-enable\r
+#pragma warning(disable:4100) // warning C4100: unreferenced formal parameter\r
+#pragma warning(disable:4201) // warning C4201: nonstandard extension used : nameless struct/union\r
+#pragma warning(disable:4511) // warning C4511: copy constructor could not be generated\r
+#pragma warning(disable:4512) // warning C4512: assignment operator could not be generated\r
+#pragma warning(disable:4514) // warning C4514: "unreferenced inline function has been removed"\r
+\r
+#if _MSC_VER>=1100\r
+#define AM_NOVTABLE __declspec(novtable)\r
+#else\r
+#define AM_NOVTABLE\r
+#endif\r
+#endif // MSC_VER\r
+\r
+\r
+// Because of differences between Visual C++ and older Microsoft SDKs,\r
+// you may have defined _DEBUG without defining DEBUG.  This logic\r
+// ensures that both will be set if Visual C++ sets _DEBUG.\r
+#ifdef _DEBUG\r
+#ifndef DEBUG\r
+#define DEBUG\r
+#endif\r
+#endif\r
+\r
+\r
+#include <windows.h>\r
+#include <windowsx.h>\r
+#include <olectl.h>\r
+#include <ddraw.h>\r
+#include <mmsystem.h>\r
+\r
+\r
+#ifndef NUMELMS\r
+#if _WIN32_WINNT < 0x0600\r
+   #define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0]))\r
+#else\r
+   #define NUMELMS(aa) ARRAYSIZE(aa)\r
+#endif   \r
+#endif\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+// The following definitions come from the Platform SDK and are required if\r
+// the applicaiton is being compiled with the headers from Visual C++ 6.0.\r
+/////////////////////////////////////////////////// ////////////////////////\r
+#ifndef InterlockedExchangePointer\r
+       #define InterlockedExchangePointer(Target, Value) \\r
+   (PVOID)InterlockedExchange((PLONG)(Target), (LONG)(Value))\r
+#endif\r
+\r
+#ifndef _WAVEFORMATEXTENSIBLE_\r
+#define _WAVEFORMATEXTENSIBLE_\r
+typedef struct {\r
+    WAVEFORMATEX    Format;\r
+    union {\r
+        WORD wValidBitsPerSample;       /* bits of precision  */\r
+        WORD wSamplesPerBlock;          /* valid if wBitsPerSample==0 */\r
+        WORD wReserved;                 /* If neither applies, set to zero. */\r
+    } Samples;\r
+    DWORD           dwChannelMask;      /* which channels are */\r
+                                        /* present in stream  */\r
+    GUID            SubFormat;\r
+} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;\r
+#endif // !_WAVEFORMATEXTENSIBLE_\r
+\r
+#if !defined(WAVE_FORMAT_EXTENSIBLE)\r
+#define  WAVE_FORMAT_EXTENSIBLE                 0xFFFE\r
+#endif // !defined(WAVE_FORMAT_EXTENSIBLE)\r
+\r
+#ifndef GetWindowLongPtr\r
+  #define GetWindowLongPtrA   GetWindowLongA\r
+  #define GetWindowLongPtrW   GetWindowLongW\r
+  #ifdef UNICODE\r
+    #define GetWindowLongPtr  GetWindowLongPtrW\r
+  #else\r
+    #define GetWindowLongPtr  GetWindowLongPtrA\r
+  #endif // !UNICODE\r
+#endif // !GetWindowLongPtr\r
+\r
+#ifndef SetWindowLongPtr\r
+  #define SetWindowLongPtrA   SetWindowLongA\r
+  #define SetWindowLongPtrW   SetWindowLongW\r
+  #ifdef UNICODE\r
+    #define SetWindowLongPtr  SetWindowLongPtrW\r
+  #else\r
+    #define SetWindowLongPtr  SetWindowLongPtrA\r
+  #endif // !UNICODE\r
+#endif // !SetWindowLongPtr\r
+\r
+#ifndef GWLP_WNDPROC\r
+  #define GWLP_WNDPROC        (-4)\r
+#endif\r
+#ifndef GWLP_HINSTANCE\r
+  #define GWLP_HINSTANCE      (-6)\r
+#endif\r
+#ifndef GWLP_HWNDPARENT\r
+  #define GWLP_HWNDPARENT     (-8)\r
+#endif\r
+#ifndef GWLP_USERDATA\r
+  #define GWLP_USERDATA       (-21)\r
+#endif\r
+#ifndef GWLP_ID\r
+  #define GWLP_ID             (-12)\r
+#endif\r
+#ifndef DWLP_MSGRESULT\r
+  #define DWLP_MSGRESULT  0\r
+#endif\r
+#ifndef DWLP_DLGPROC \r
+  #define DWLP_DLGPROC    DWLP_MSGRESULT + sizeof(LRESULT)\r
+#endif\r
+#ifndef DWLP_USER\r
+  #define DWLP_USER       DWLP_DLGPROC + sizeof(DLGPROC)\r
+#endif\r
+\r
+\r
+#pragma warning(push)\r
+#pragma warning(disable: 4312 4244)\r
+// _GetWindowLongPtr\r
+// Templated version of GetWindowLongPtr, to suppress spurious compiler warning.\r
+template <class T>\r
+T _GetWindowLongPtr(HWND hwnd, int nIndex)\r
+{\r
+    return (T)GetWindowLongPtr(hwnd, nIndex);\r
+}\r
+\r
+// _SetWindowLongPtr\r
+// Templated version of SetWindowLongPtr, to suppress spurious compiler warning.\r
+template <class T>\r
+LONG_PTR _SetWindowLongPtr(HWND hwnd, int nIndex, T p)\r
+{\r
+    return SetWindowLongPtr(hwnd, nIndex, (LONG_PTR)p);\r
+}\r
+#pragma warning(pop)\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+// End Platform SDK definitions\r
+///////////////////////////////////////////////////////////////////////////\r
+\r
+\r
+#include <strmif.h>     // Generated IDL header file for streams interfaces\r
+#include <intsafe.h>    // required by amvideo.h\r
+\r
+#include <reftime.h>    // Helper class for REFERENCE_TIME management\r
+#include <wxdebug.h>    // Debug support for logging and ASSERTs\r
+#include <amvideo.h>    // ActiveMovie video interfaces and definitions\r
+//include amaudio.h explicitly if you need it.  it requires the DX SDK.\r
+//#include <amaudio.h>    // ActiveMovie audio interfaces and definitions\r
+#include <wxutil.h>     // General helper classes for threads etc\r
+#include <combase.h>    // Base COM classes to support IUnknown\r
+#include <dllsetup.h>   // Filter registration support functions\r
+#include <measure.h>    // Performance measurement\r
+#include <comlite.h>    // Light weight com function prototypes\r
+\r
+#include <cache.h>      // Simple cache container class\r
+#include <wxlist.h>     // Non MFC generic list class\r
+#include <msgthrd.h>   // CMsgThread\r
+#include <mtype.h>      // Helper class for managing media types\r
+#include <fourcc.h>     // conversions between FOURCCs and GUIDs\r
+#include <control.h>    // generated from control.odl\r
+#include <ctlutil.h>    // control interface utility classes\r
+#include <evcode.h>     // event code definitions\r
+#include <amfilter.h>   // Main streams architecture class hierachy\r
+#include <transfrm.h>   // Generic transform filter\r
+#include <transip.h>    // Generic transform-in-place filter\r
+#include <uuids.h>      // declaration of type GUIDs and well-known clsids\r
+#include <source.h>    // Generic source filter\r
+#include <outputq.h>    // Output pin queueing\r
+#include <errors.h>     // HRESULT status and error definitions\r
+#include <renbase.h>    // Base class for writing ActiveX renderers\r
+#include <winutil.h>    // Helps with filters that manage windows\r
+#include <winctrl.h>    // Implements the IVideoWindow interface\r
+#include <videoctl.h>   // Specifically video related classes\r
+#include <refclock.h>  // Base clock class\r
+#include <sysclock.h>  // System clock\r
+#include <pstream.h>    // IPersistStream helper class\r
+#include <vtrans.h>     // Video Transform Filter base class\r
+#include <amextra.h>\r
+#include <cprop.h>      // Base property page class\r
+#include <strmctl.h>    // IAMStreamControl support\r
+#include <edevdefs.h>   // External device control interface defines\r
+#include <audevcod.h>   // audio filter device error event codes\r
+\r
+\r
+\r
+#else\r
+    #ifdef DEBUG\r
+    #pragma message("STREAMS.H included TWICE")\r
+    #endif\r
+#endif // __STREAMS__\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/strmctl.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/strmctl.cpp
new file mode 100644 (file)
index 0000000..b7f5952
--- /dev/null
@@ -0,0 +1,402 @@
+//------------------------------------------------------------------------------\r
+// File: StrmCtl.cpp\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1996-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+#include <strmctl.h>\r
+\r
+CBaseStreamControl::CBaseStreamControl(__inout HRESULT *phr)\r
+: m_StreamState(STREAM_FLOWING)\r
+, m_StreamStateOnStop(STREAM_FLOWING) // means no pending stop\r
+, m_tStartTime(MAX_TIME)\r
+, m_tStopTime(MAX_TIME)\r
+, m_StreamEvent(FALSE, phr)\r
+, m_dwStartCookie(0)\r
+, m_dwStopCookie(0)\r
+, m_pRefClock(NULL)\r
+, m_FilterState(State_Stopped)\r
+, m_bIsFlushing(FALSE)\r
+, m_bStopSendExtra(FALSE)\r
+{}\r
+\r
+CBaseStreamControl::~CBaseStreamControl()\r
+{\r
+    // Make sure we release the clock.\r
+    SetSyncSource(NULL);\r
+    return;\r
+}\r
+\r
+\r
+STDMETHODIMP CBaseStreamControl::StopAt(const REFERENCE_TIME * ptStop, BOOL bSendExtra, DWORD dwCookie)\r
+{\r
+    CAutoLock lck(&m_CritSec);\r
+    m_bStopSendExtra = FALSE;  // reset\r
+    m_bStopExtraSent = FALSE;\r
+    if (ptStop)\r
+    {\r
+        if (*ptStop == MAX_TIME)\r
+        {\r
+            DbgLog((LOG_TRACE,2,TEXT("StopAt: Cancel stop")));\r
+            CancelStop();\r
+           // If there's now a command to start in the future, we assume\r
+           // they want to be stopped when the graph is first run\r
+           if (m_FilterState == State_Stopped && m_tStartTime < MAX_TIME) {\r
+               m_StreamState = STREAM_DISCARDING;\r
+                DbgLog((LOG_TRACE,2,TEXT("graph will begin by DISCARDING")));\r
+           }\r
+            return NOERROR;\r
+        }\r
+        DbgLog((LOG_TRACE,2,TEXT("StopAt: %dms extra=%d"),\r
+                               (int)(*ptStop/10000), bSendExtra));\r
+       // if the first command is to stop in the future, then we assume they\r
+        // want to be started when the graph is first run\r
+       if (m_FilterState == State_Stopped && m_tStartTime > *ptStop) {\r
+           m_StreamState = STREAM_FLOWING;\r
+            DbgLog((LOG_TRACE,2,TEXT("graph will begin by FLOWING")));\r
+       }\r
+        m_bStopSendExtra = bSendExtra;\r
+        m_tStopTime = *ptStop;\r
+        m_dwStopCookie = dwCookie;\r
+        m_StreamStateOnStop = STREAM_DISCARDING;\r
+    }\r
+    else\r
+    {\r
+        DbgLog((LOG_TRACE,2,TEXT("StopAt: now")));\r
+       // sending an extra frame when told to stop now would mess people up\r
+        m_bStopSendExtra = FALSE;\r
+        m_tStopTime = MAX_TIME;\r
+        m_dwStopCookie = 0;\r
+        m_StreamState = STREAM_DISCARDING;\r
+        m_StreamStateOnStop = STREAM_FLOWING;  // no pending stop\r
+    }\r
+    // we might change our mind what to do with a sample we're blocking\r
+    m_StreamEvent.Set();\r
+    return NOERROR;\r
+}\r
+\r
+STDMETHODIMP CBaseStreamControl::StartAt\r
+( const REFERENCE_TIME *ptStart, DWORD dwCookie )\r
+{\r
+    CAutoLock lck(&m_CritSec);\r
+    if (ptStart)\r
+    {\r
+        if (*ptStart == MAX_TIME)\r
+        {\r
+            DbgLog((LOG_TRACE,2,TEXT("StartAt: Cancel start")));\r
+            CancelStart();\r
+           // If there's now a command to stop in the future, we assume\r
+           // they want to be started when the graph is first run\r
+           if (m_FilterState == State_Stopped && m_tStopTime < MAX_TIME) {\r
+                DbgLog((LOG_TRACE,2,TEXT("graph will begin by FLOWING")));\r
+               m_StreamState = STREAM_FLOWING;\r
+           }\r
+            return NOERROR;\r
+        }\r
+        DbgLog((LOG_TRACE,2,TEXT("StartAt: %dms"), (int)(*ptStart/10000)));\r
+       // if the first command is to start in the future, then we assume they\r
+        // want to be stopped when the graph is first run\r
+       if (m_FilterState == State_Stopped && m_tStopTime >= *ptStart) {\r
+            DbgLog((LOG_TRACE,2,TEXT("graph will begin by DISCARDING")));\r
+           m_StreamState = STREAM_DISCARDING;\r
+       }\r
+        m_tStartTime = *ptStart;\r
+        m_dwStartCookie = dwCookie;\r
+        // if (m_tStopTime == m_tStartTime) CancelStop();\r
+    }\r
+    else\r
+    {\r
+        DbgLog((LOG_TRACE,2,TEXT("StartAt: now")));\r
+        m_tStartTime = MAX_TIME;\r
+        m_dwStartCookie = 0;\r
+        m_StreamState = STREAM_FLOWING;\r
+    }\r
+    // we might change our mind what to do with a sample we're blocking\r
+    m_StreamEvent.Set();\r
+    return NOERROR;\r
+}\r
+\r
+//  Retrieve information about current settings\r
+STDMETHODIMP CBaseStreamControl::GetInfo(__out AM_STREAM_INFO *pInfo)\r
+{\r
+    if (pInfo == NULL)\r
+       return E_POINTER;\r
+\r
+    pInfo->tStart = m_tStartTime;\r
+    pInfo->tStop  = m_tStopTime;\r
+    pInfo->dwStartCookie = m_dwStartCookie;\r
+    pInfo->dwStopCookie  = m_dwStopCookie;\r
+    pInfo->dwFlags = m_bStopSendExtra ? AM_STREAM_INFO_STOP_SEND_EXTRA : 0;\r
+    pInfo->dwFlags |= m_tStartTime == MAX_TIME ? 0 : AM_STREAM_INFO_START_DEFINED;\r
+    pInfo->dwFlags |= m_tStopTime == MAX_TIME ? 0 : AM_STREAM_INFO_STOP_DEFINED;\r
+    switch (m_StreamState) {\r
+    default:\r
+        DbgBreak("Invalid stream state");\r
+    case STREAM_FLOWING:\r
+        break;\r
+    case STREAM_DISCARDING:\r
+        pInfo->dwFlags |= AM_STREAM_INFO_DISCARDING;\r
+        break;\r
+    }\r
+    return S_OK;\r
+}\r
+\r
+\r
+void CBaseStreamControl::ExecuteStop()\r
+{\r
+    ASSERT(CritCheckIn(&m_CritSec));\r
+    m_StreamState = m_StreamStateOnStop;\r
+    if (m_dwStopCookie && m_pSink) {\r
+       DbgLog((LOG_TRACE,2,TEXT("*sending EC_STREAM_CONTROL_STOPPED (%d)"),\r
+                                                       m_dwStopCookie));\r
+        m_pSink->Notify(EC_STREAM_CONTROL_STOPPED, (LONG_PTR)this, m_dwStopCookie);\r
+    }\r
+    CancelStop(); // This will do the tidy up\r
+}\r
+\r
+void CBaseStreamControl::ExecuteStart()\r
+{\r
+    ASSERT(CritCheckIn(&m_CritSec));\r
+    m_StreamState = STREAM_FLOWING;\r
+    if (m_dwStartCookie) {\r
+       DbgLog((LOG_TRACE,2,TEXT("*sending EC_STREAM_CONTROL_STARTED (%d)"),\r
+                                                       m_dwStartCookie));\r
+        m_pSink->Notify(EC_STREAM_CONTROL_STARTED, (LONG_PTR)this, m_dwStartCookie);\r
+    }\r
+    CancelStart(); // This will do the tidy up\r
+}\r
+\r
+void CBaseStreamControl::CancelStop()\r
+{\r
+    ASSERT(CritCheckIn(&m_CritSec));\r
+    m_tStopTime = MAX_TIME;\r
+    m_dwStopCookie = 0;\r
+    m_StreamStateOnStop = STREAM_FLOWING;\r
+}\r
+\r
+void CBaseStreamControl::CancelStart()\r
+{\r
+    ASSERT(CritCheckIn(&m_CritSec));\r
+    m_tStartTime = MAX_TIME;\r
+    m_dwStartCookie = 0;\r
+}\r
+\r
+\r
+// This guy will return one of the three StreamControlState's.  Here's what the caller\r
+// should do for each one:\r
+//\r
+// STREAM_FLOWING:      Proceed as usual (render or pass the sample on)\r
+// STREAM_DISCARDING:   Calculate the time 'til *pSampleStart and wait that long\r
+//                      for the event handle (GetStreamEventHandle()).  If the\r
+//                      wait expires, throw the sample away.  If the event\r
+//                     fires, call me back, I've changed my mind.\r
+//                     I use pSampleStart (not Stop) so that live sources don't\r
+//                     block for the duration of their samples, since the clock\r
+//                     will always read approximately pSampleStart when called\r
+\r
+\r
+// All through this code, you'll notice the following rules:\r
+// - When start and stop time are the same, it's as if start was first\r
+// - An event is considered inside the sample when it's >= sample start time\r
+//   but < sample stop time\r
+// - if any part of the sample is supposed to be sent, we'll send the whole\r
+//   thing since we don't break it into smaller pieces\r
+// - If we skip over a start or stop without doing it, we still signal the event\r
+//   and reset ourselves in case somebody's waiting for the event, and to make\r
+//   sure we notice that the event is past and should be forgotten\r
+// Here are the 19 cases that have to be handled (x=start o=stop <-->=sample):\r
+//\r
+// 1.  xo<-->          start then stop\r
+// 2.  ox<-->          stop then start\r
+// 3.   x<o->          start\r
+// 4.   o<x->          stop then start\r
+// 5.   x<-->o         start\r
+// 6.   o<-->x         stop\r
+// 7.    <x->o         start\r
+// 8.    <o->x         no change\r
+// 9.    <xo>          start\r
+// 10.   <ox>          stop then start\r
+// 11.   <-->xo        no change\r
+// 12.   <-->ox        no change\r
+// 13.  x<-->          start\r
+// 14.    <x->         start\r
+// 15.    <-->x                no change\r
+// 16.   o<-->         stop\r
+// 17.   <o->          no change\r
+// 18.   <-->o         no change\r
+// 19.    <-->         no change\r
+\r
+\r
+enum CBaseStreamControl::StreamControlState CBaseStreamControl::CheckSampleTimes\r
+( __in const REFERENCE_TIME * pSampleStart, __in const REFERENCE_TIME * pSampleStop )\r
+{\r
+    CAutoLock lck(&m_CritSec);\r
+\r
+    ASSERT(!m_bIsFlushing);\r
+    ASSERT(pSampleStart && pSampleStop);\r
+\r
+    // Don't ask me how I came up with the code below to handle all 19 cases\r
+    // - DannyMi\r
+\r
+    if (m_tStopTime >= *pSampleStart)\r
+    {\r
+        if (m_tStartTime >= *pSampleStop)\r
+           return m_StreamState;               // cases  8 11 12 15 17 18 19\r
+       if (m_tStopTime < m_tStartTime)\r
+           ExecuteStop();                      // case 10\r
+       ExecuteStart();                         // cases 3 5 7 9 13 14\r
+       return m_StreamState;\r
+    }\r
+\r
+    if (m_tStartTime >= *pSampleStop)\r
+    {\r
+        ExecuteStop();                          // cases 6 16\r
+        return m_StreamState;\r
+    }\r
+\r
+    if (m_tStartTime <= m_tStopTime)\r
+    {\r
+       ExecuteStart();\r
+       ExecuteStop();\r
+        return m_StreamState;          // case 1\r
+    }\r
+    else\r
+    {\r
+       ExecuteStop();\r
+       ExecuteStart();\r
+        return m_StreamState;          // cases 2 4\r
+    }\r
+}\r
+\r
+\r
+enum CBaseStreamControl::StreamControlState CBaseStreamControl::CheckStreamState( IMediaSample * pSample )\r
+{\r
+\r
+    REFERENCE_TIME rtBufferStart, rtBufferStop;\r
+    const BOOL bNoBufferTimes =\r
+              pSample == NULL ||\r
+              FAILED(pSample->GetTime(&rtBufferStart, &rtBufferStop));\r
+\r
+    StreamControlState state;\r
+    LONG lWait;\r
+\r
+    do\r
+        {\r
+           // something has to break out of the blocking\r
+            if (m_bIsFlushing || m_FilterState == State_Stopped)\r
+               return STREAM_DISCARDING;\r
+\r
+            if (bNoBufferTimes) {\r
+                //  Can't do anything until we get a time stamp\r
+                state = m_StreamState;\r
+                break;\r
+            } else {\r
+                state = CheckSampleTimes( &rtBufferStart, &rtBufferStop );\r
+                if (state == STREAM_FLOWING)\r
+                   break;\r
+\r
+               // we aren't supposed to send this, but we've been\r
+               // told to send one more than we were supposed to\r
+               // (and the stop isn't still pending and we're streaming)\r
+               if (m_bStopSendExtra && !m_bStopExtraSent &&\r
+                                       m_tStopTime == MAX_TIME &&\r
+                                       m_FilterState != State_Stopped) {\r
+                   m_bStopExtraSent = TRUE;\r
+                   DbgLog((LOG_TRACE,2,TEXT("%d sending an EXTRA frame"),\r
+                                                           m_dwStopCookie));\r
+                   state = STREAM_FLOWING;\r
+                   break;\r
+               }\r
+            }\r
+\r
+            // We're in discarding mode\r
+\r
+            // If we've no clock, discard as fast as we can\r
+            if (!m_pRefClock) {\r
+               break;\r
+\r
+           // If we're paused, we can't discard in a timely manner because\r
+           // there's no such thing as stream times.  We must block until\r
+           // we run or stop, or we'll end up throwing the whole stream away\r
+           // as quickly as possible\r
+           } else if (m_FilterState == State_Paused) {\r
+               lWait = INFINITE;\r
+\r
+           } else {\r
+               // wait until it's time for the sample until we say "discard"\r
+               // ("discard in a timely fashion")\r
+               REFERENCE_TIME rtNow;\r
+                EXECUTE_ASSERT(SUCCEEDED(m_pRefClock->GetTime(&rtNow)));\r
+                rtNow -= m_tRunStart;   // Into relative ref-time\r
+                lWait = LONG((rtBufferStart - rtNow)/10000); // 100ns -> ms\r
+                if (lWait < 10) break; // Not worth waiting - discard early\r
+           }\r
+\r
+    } while(WaitForSingleObject(GetStreamEventHandle(), lWait) != WAIT_TIMEOUT);\r
+\r
+    return state;\r
+}\r
+\r
+\r
+void CBaseStreamControl::NotifyFilterState( FILTER_STATE new_state, REFERENCE_TIME tStart )\r
+{\r
+    CAutoLock lck(&m_CritSec);\r
+\r
+    // or we will get confused\r
+    if (m_FilterState == new_state)\r
+       return;\r
+\r
+    switch (new_state)\r
+    {\r
+        case State_Stopped:\r
+\r
+            DbgLog((LOG_TRACE,2,TEXT("Filter is STOPPED")));\r
+\r
+           // execute any pending starts and stops in the right order,\r
+           // to make sure all notifications get sent, and we end up\r
+           // in the right state to begin next time (??? why not?)\r
+\r
+           if (m_tStartTime != MAX_TIME && m_tStopTime == MAX_TIME) {\r
+               ExecuteStart();\r
+           } else if (m_tStopTime != MAX_TIME && m_tStartTime == MAX_TIME) {\r
+               ExecuteStop();\r
+           } else if (m_tStopTime != MAX_TIME && m_tStartTime != MAX_TIME) {\r
+               if (m_tStartTime <= m_tStopTime) {\r
+                   ExecuteStart();\r
+                   ExecuteStop();\r
+               } else {\r
+                   ExecuteStop();\r
+                   ExecuteStart();\r
+               }\r
+           }\r
+           // always start off flowing when the graph starts streaming\r
+           // unless told otherwise\r
+           m_StreamState = STREAM_FLOWING;\r
+            m_FilterState = new_state;\r
+            break;\r
+\r
+        case State_Running:\r
+\r
+            DbgLog((LOG_TRACE,2,TEXT("Filter is RUNNING")));\r
+\r
+            m_tRunStart = tStart;\r
+            // fall-through\r
+\r
+        default: // case State_Paused:\r
+            m_FilterState = new_state;\r
+    }\r
+    // unblock!\r
+    m_StreamEvent.Set();\r
+}\r
+\r
+\r
+void CBaseStreamControl::Flushing(BOOL bInProgress)\r
+{\r
+    CAutoLock lck(&m_CritSec);\r
+    m_bIsFlushing = bInProgress;\r
+    m_StreamEvent.Set();\r
+}\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/strmctl.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/strmctl.h
new file mode 100644 (file)
index 0000000..cb2adf3
--- /dev/null
@@ -0,0 +1,157 @@
+//------------------------------------------------------------------------------\r
+// File: StrmCtl.h\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1996-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef __strmctl_h__\r
+#define __strmctl_h__\r
+\r
+class CBaseStreamControl : public IAMStreamControl\r
+{\r
+public:\r
+    // Used by the implementation\r
+    enum StreamControlState\r
+    { STREAM_FLOWING = 0x1000,\r
+      STREAM_DISCARDING\r
+    };\r
+\r
+private:\r
+    enum StreamControlState    m_StreamState;          // Current stream state\r
+    enum StreamControlState    m_StreamStateOnStop;    // State after next stop\r
+                                               // (i.e.Blocking or Discarding)\r
+\r
+    REFERENCE_TIME     m_tStartTime;       // MAX_TIME implies none\r
+    REFERENCE_TIME     m_tStopTime;        // MAX_TIME implies none\r
+    DWORD              m_dwStartCookie;    // Cookie for notification to app\r
+    DWORD              m_dwStopCookie;     // Cookie for notification to app\r
+    volatile BOOL       m_bIsFlushing;        // No optimization pls!\r
+    volatile BOOL      m_bStopSendExtra;   // bSendExtra was set\r
+    volatile BOOL      m_bStopExtraSent;   // the extra one was sent\r
+\r
+    CCritSec           m_CritSec;          // CritSec to guard above attributes\r
+\r
+    // Event to fire when we can come\r
+    // out of blocking, or to come out of waiting\r
+    // to discard if we change our minds.\r
+    //\r
+    CAMEvent                   m_StreamEvent;\r
+\r
+    // All of these methods execute immediately.  Helpers for others.\r
+    //\r
+    void ExecuteStop();\r
+    void ExecuteStart();\r
+    void CancelStop();\r
+    void CancelStart();\r
+\r
+    // Some things we need to be told by our owning filter\r
+    // Your pin must also expose IAMStreamControl when QI'd for it!\r
+    //\r
+    IReferenceClock *  m_pRefClock;        // Need it to set advises\r
+                                           // Filter must tell us via\r
+                                           // SetSyncSource\r
+    IMediaEventSink *   m_pSink;            // Event sink\r
+                                           // Filter must tell us after it\r
+                                           // creates it in JoinFilterGraph()\r
+    FILTER_STATE       m_FilterState;      // Just need it!\r
+                                           // Filter must tell us via\r
+                                           // NotifyFilterState\r
+    REFERENCE_TIME     m_tRunStart;        // Per the Run call to the filter\r
+\r
+    // This guy will return one of the three StreamControlState's.  Here's what\r
+    // the caller should do for each one:\r
+    //\r
+    // STREAM_FLOWING:         Proceed as usual (render or pass the sample on)\r
+    // STREAM_DISCARDING:      Calculate the time 'til *pSampleStop and wait\r
+    //                         that long for the event handle\r
+    //                         (GetStreamEventHandle()).  If the wait\r
+    //                         expires, throw the sample away.  If the event\r
+    //                         fires, call me back - I've changed my mind.\r
+    //\r
+    enum StreamControlState CheckSampleTimes( __in const REFERENCE_TIME * pSampleStart,\r
+                                             __in const REFERENCE_TIME * pSampleStop );\r
+\r
+public:\r
+    // You don't have to tell us much when we're created, but there are other\r
+    // obligations that must be met.  See SetSyncSource & NotifyFilterState\r
+    // below.\r
+    //\r
+    CBaseStreamControl(__inout_opt HRESULT *phr = NULL);\r
+    ~CBaseStreamControl();\r
+\r
+    // If you want this class to work properly, there are thing you need to\r
+    // (keep) telling it.  Filters with pins that use this class\r
+    // should ensure that they pass through to this method any calls they\r
+    // receive on their SetSyncSource.\r
+\r
+    // We need a clock to see what time it is.  This is for the\r
+    // "discard in a timely fashion" logic.  If we discard everything as\r
+    // quick as possible, a whole 60 minute file could get discarded in the\r
+    // first 10 seconds, and if somebody wants to turn streaming on at 30 \r
+    // minutes into the file, and they make the call more than a few seconds\r
+    // after the graph is run, it may be too late!\r
+    // So we hold every sample until it's time has gone, then we discard it.\r
+    // The filter should call this when it gets a SetSyncSource\r
+    //\r
+    void SetSyncSource( IReferenceClock * pRefClock )\r
+    {\r
+       CAutoLock lck(&m_CritSec);\r
+       if (m_pRefClock) m_pRefClock->Release();\r
+       m_pRefClock = pRefClock;\r
+       if (m_pRefClock) m_pRefClock->AddRef();\r
+    }\r
+\r
+    // Set event sink for notifications\r
+    // The filter should call this in its JoinFilterGraph after it creates the\r
+    // IMediaEventSink\r
+    //\r
+    void SetFilterGraph( IMediaEventSink *pSink ) {\r
+        m_pSink = pSink;\r
+    }\r
+\r
+    // Since we schedule in stream time, we need the tStart and must track the\r
+    // state of our owning filter.\r
+    // The app should call this ever state change\r
+    //\r
+    void NotifyFilterState( FILTER_STATE new_state, REFERENCE_TIME tStart = 0 );\r
+\r
+    // Filter should call Flushing(TRUE) in BeginFlush,\r
+    // and Flushing(FALSE) in EndFlush.\r
+    //\r
+    void Flushing( BOOL bInProgress );\r
+\r
+\r
+    // The two main methods of IAMStreamControl\r
+\r
+    // Class adds default values suitable for immediate\r
+    // muting and unmuting of the stream.\r
+\r
+    STDMETHODIMP StopAt( const REFERENCE_TIME * ptStop = NULL,\r
+                        BOOL bSendExtra = FALSE,\r
+                        DWORD dwCookie = 0 );\r
+    STDMETHODIMP StartAt( const REFERENCE_TIME * ptStart = NULL,\r
+                         DWORD dwCookie = 0 );\r
+    STDMETHODIMP GetInfo( __out AM_STREAM_INFO *pInfo);\r
+\r
+    // Helper function for pin's receive method.  Call this with\r
+    // the sample and we'll tell you what to do with it.  We'll do a\r
+    // WaitForSingleObject within this call if one is required.  This is\r
+    // a "What should I do with this sample?" kind of call. We'll tell the\r
+    // caller to either flow it or discard it.\r
+    // If pSample is NULL we evaluate based on the current state\r
+    // settings\r
+    enum StreamControlState CheckStreamState( IMediaSample * pSample );\r
+\r
+private:\r
+    // These don't require locking, but we are relying on the fact that\r
+    // m_StreamState can be retrieved with integrity, and is a snap shot that\r
+    // may have just been, or may be just about to be, changed.\r
+    HANDLE GetStreamEventHandle() const { return m_StreamEvent; }\r
+    enum StreamControlState GetStreamState() const { return m_StreamState; }\r
+    BOOL IsStreaming() const { return m_StreamState == STREAM_FLOWING; }\r
+};\r
+\r
+#endif\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/sysclock.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/sysclock.cpp
new file mode 100644 (file)
index 0000000..0d58291
--- /dev/null
@@ -0,0 +1,74 @@
+//------------------------------------------------------------------------------\r
+// File: SysClock.cpp\r
+//\r
+// Desc: DirectShow base classes - implements a system clock based on \r
+//       IReferenceClock.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+#include <limits.h>\r
+\r
+\r
+#ifdef FILTER_DLL\r
+\r
+/* List of class IDs and creator functions for the class factory. This\r
+   provides the link between the OLE entry point in the DLL and an object\r
+   being created. The class factory will call the static CreateInstance\r
+   function when it is asked to create a CLSID_SystemClock object */\r
+\r
+CFactoryTemplate g_Templates[1] = {\r
+    {&CLSID_SystemClock, CSystemClock::CreateInstance}\r
+};\r
+\r
+int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);\r
+#endif\r
+\r
+/* This goes in the factory template table to create new instances */\r
+CUnknown * WINAPI CSystemClock::CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr)\r
+{\r
+    return new CSystemClock(NAME("System reference clock"),pUnk, phr);\r
+}\r
+\r
+\r
+CSystemClock::CSystemClock(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr) :\r
+    CBaseReferenceClock(pName, pUnk, phr)\r
+{\r
+}\r
+\r
+STDMETHODIMP CSystemClock::NonDelegatingQueryInterface(\r
+    REFIID riid,\r
+    __deref_out void ** ppv)\r
+{\r
+    if (riid == IID_IPersist)\r
+    {\r
+        return GetInterface(static_cast<IPersist *>(this), ppv);\r
+    }\r
+    else if (riid == IID_IAMClockAdjust)\r
+    {\r
+        return GetInterface(static_cast<IAMClockAdjust *>(this), ppv);\r
+    }\r
+    else\r
+    {\r
+        return CBaseReferenceClock::NonDelegatingQueryInterface(riid, ppv);\r
+    }\r
+}\r
+\r
+/* Return the clock's clsid */\r
+STDMETHODIMP\r
+CSystemClock::GetClassID(__out CLSID *pClsID)\r
+{\r
+    CheckPointer(pClsID,E_POINTER);\r
+    ValidateReadWritePtr(pClsID,sizeof(CLSID));\r
+    *pClsID = CLSID_SystemClock;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+STDMETHODIMP \r
+CSystemClock::SetClockDelta(REFERENCE_TIME rtDelta)\r
+{\r
+    return SetTimeDelta(rtDelta);\r
+}\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/sysclock.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/sysclock.h
new file mode 100644 (file)
index 0000000..3976d34
--- /dev/null
@@ -0,0 +1,39 @@
+//------------------------------------------------------------------------------\r
+// File: SysClock.h\r
+//\r
+// Desc: DirectShow base classes - defines a system clock implementation of\r
+//       IReferenceClock.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef __SYSTEMCLOCK__\r
+#define __SYSTEMCLOCK__\r
+\r
+//\r
+// Base clock.  Uses timeGetTime ONLY\r
+// Uses most of the code in the base reference clock.\r
+// Provides GetTime\r
+//\r
+\r
+class CSystemClock : public CBaseReferenceClock, public IAMClockAdjust, public IPersist\r
+{\r
+public:\r
+    // We must be able to create an instance of ourselves\r
+    static CUnknown * WINAPI CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr);\r
+    CSystemClock(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr);\r
+\r
+    DECLARE_IUNKNOWN\r
+\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv);\r
+\r
+    // Yield up our class id so that we can be persisted\r
+    // Implement required Ipersist method\r
+    STDMETHODIMP GetClassID(__out CLSID *pClsID);\r
+\r
+    //  IAMClockAdjust methods\r
+    STDMETHODIMP SetClockDelta(REFERENCE_TIME rtDelta);\r
+}; //CSystemClock\r
+\r
+#endif /* __SYSTEMCLOCK__ */\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/transfrm.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/transfrm.cpp
new file mode 100644 (file)
index 0000000..3d17077
--- /dev/null
@@ -0,0 +1,1016 @@
+//------------------------------------------------------------------------------\r
+// File: Transfrm.cpp\r
+//\r
+// Desc: DirectShow base classes - implements class for simple transform\r
+//       filters such as video decompressors.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+#include <measure.h>\r
+\r
+\r
+// =================================================================\r
+// Implements the CTransformFilter class\r
+// =================================================================\r
+\r
+CTransformFilter::CTransformFilter(__in_opt LPCTSTR pName,\r
+                                   __inout_opt LPUNKNOWN pUnk,\r
+                                   REFCLSID  clsid) :\r
+    CBaseFilter(pName,pUnk,&m_csFilter, clsid),\r
+    m_pInput(NULL),\r
+    m_pOutput(NULL),\r
+    m_bEOSDelivered(FALSE),\r
+    m_bQualityChanged(FALSE),\r
+    m_bSampleSkipped(FALSE)\r
+{\r
+#ifdef PERF\r
+    RegisterPerfId();\r
+#endif //  PERF\r
+}\r
+\r
+#ifdef UNICODE\r
+CTransformFilter::CTransformFilter(__in_opt LPCSTR pName,\r
+                                   __inout_opt LPUNKNOWN pUnk,\r
+                                   REFCLSID  clsid) :\r
+    CBaseFilter(pName,pUnk,&m_csFilter, clsid),\r
+    m_pInput(NULL),\r
+    m_pOutput(NULL),\r
+    m_bEOSDelivered(FALSE),\r
+    m_bQualityChanged(FALSE),\r
+    m_bSampleSkipped(FALSE)\r
+{\r
+#ifdef PERF\r
+    RegisterPerfId();\r
+#endif //  PERF\r
+}\r
+#endif\r
+\r
+// destructor\r
+\r
+CTransformFilter::~CTransformFilter()\r
+{\r
+    // Delete the pins\r
+\r
+    delete m_pInput;\r
+    delete m_pOutput;\r
+}\r
+\r
+\r
+// Transform place holder - should never be called\r
+HRESULT CTransformFilter::Transform(IMediaSample * pIn, IMediaSample *pOut)\r
+{\r
+    UNREFERENCED_PARAMETER(pIn);\r
+    UNREFERENCED_PARAMETER(pOut);\r
+    DbgBreak("CTransformFilter::Transform() should never be called");\r
+    return E_UNEXPECTED;\r
+}\r
+\r
+\r
+// return the number of pins we provide\r
+\r
+int CTransformFilter::GetPinCount()\r
+{\r
+    return 2;\r
+}\r
+\r
+\r
+// return a non-addrefed CBasePin * for the user to addref if he holds onto it\r
+// for longer than his pointer to us. We create the pins dynamically when they\r
+// are asked for rather than in the constructor. This is because we want to\r
+// give the derived class an oppportunity to return different pin objects\r
+\r
+// We return the objects as and when they are needed. If either of these fails\r
+// then we return NULL, the assumption being that the caller will realise the\r
+// whole deal is off and destroy us - which in turn will delete everything.\r
+\r
+CBasePin *\r
+CTransformFilter::GetPin(int n)\r
+{\r
+    HRESULT hr = S_OK;\r
+\r
+    // Create an input pin if necessary\r
+\r
+    if (m_pInput == NULL) {\r
+\r
+        m_pInput = new CTransformInputPin(NAME("Transform input pin"),\r
+                                          this,              // Owner filter\r
+                                          &hr,               // Result code\r
+                                          L"XForm In");      // Pin name\r
+\r
+\r
+        //  Can't fail\r
+        ASSERT(SUCCEEDED(hr));\r
+        if (m_pInput == NULL) {\r
+            return NULL;\r
+        }\r
+        m_pOutput = (CTransformOutputPin *)\r
+                  new CTransformOutputPin(NAME("Transform output pin"),\r
+                                            this,            // Owner filter\r
+                                            &hr,             // Result code\r
+                                            L"XForm Out");   // Pin name\r
+\r
+\r
+        // Can't fail\r
+        ASSERT(SUCCEEDED(hr));\r
+        if (m_pOutput == NULL) {\r
+            delete m_pInput;\r
+            m_pInput = NULL;\r
+        }\r
+    }\r
+\r
+    // Return the appropriate pin\r
+\r
+    if (n == 0) {\r
+        return m_pInput;\r
+    } else\r
+    if (n == 1) {\r
+        return m_pOutput;\r
+    } else {\r
+        return NULL;\r
+    }\r
+}\r
+\r
+\r
+//\r
+// FindPin\r
+//\r
+// If Id is In or Out then return the IPin* for that pin\r
+// creating the pin if need be.  Otherwise return NULL with an error.\r
+\r
+STDMETHODIMP CTransformFilter::FindPin(LPCWSTR Id, __deref_out IPin **ppPin)\r
+{\r
+    CheckPointer(ppPin,E_POINTER);\r
+    ValidateReadWritePtr(ppPin,sizeof(IPin *));\r
+\r
+    if (0==lstrcmpW(Id,L"In")) {\r
+        *ppPin = GetPin(0);\r
+    } else if (0==lstrcmpW(Id,L"Out")) {\r
+        *ppPin = GetPin(1);\r
+    } else {\r
+        *ppPin = NULL;\r
+        return VFW_E_NOT_FOUND;\r
+    }\r
+\r
+    HRESULT hr = NOERROR;\r
+    //  AddRef() returned pointer - but GetPin could fail if memory is low.\r
+    if (*ppPin) {\r
+        (*ppPin)->AddRef();\r
+    } else {\r
+        hr = E_OUTOFMEMORY;  // probably.  There's no pin anyway.\r
+    }\r
+    return hr;\r
+}\r
+\r
+\r
+// override these two functions if you want to inform something\r
+// about entry to or exit from streaming state.\r
+\r
+HRESULT\r
+CTransformFilter::StartStreaming()\r
+{\r
+    return NOERROR;\r
+}\r
+\r
+\r
+HRESULT\r
+CTransformFilter::StopStreaming()\r
+{\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// override this to grab extra interfaces on connection\r
+\r
+HRESULT\r
+CTransformFilter::CheckConnect(PIN_DIRECTION dir, IPin *pPin)\r
+{\r
+    UNREFERENCED_PARAMETER(dir);\r
+    UNREFERENCED_PARAMETER(pPin);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// place holder to allow derived classes to release any extra interfaces\r
+\r
+HRESULT\r
+CTransformFilter::BreakConnect(PIN_DIRECTION dir)\r
+{\r
+    UNREFERENCED_PARAMETER(dir);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Let derived classes know about connection completion\r
+\r
+HRESULT\r
+CTransformFilter::CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin)\r
+{\r
+    UNREFERENCED_PARAMETER(direction);\r
+    UNREFERENCED_PARAMETER(pReceivePin);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// override this to know when the media type is really set\r
+\r
+HRESULT\r
+CTransformFilter::SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt)\r
+{\r
+    UNREFERENCED_PARAMETER(direction);\r
+    UNREFERENCED_PARAMETER(pmt);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Set up our output sample\r
+HRESULT\r
+CTransformFilter::InitializeOutputSample(IMediaSample *pSample, __deref_out IMediaSample **ppOutSample)\r
+{\r
+    IMediaSample *pOutSample;\r
+\r
+    // default - times are the same\r
+\r
+    AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps();\r
+    DWORD dwFlags = m_bSampleSkipped ? AM_GBF_PREVFRAMESKIPPED : 0;\r
+\r
+    // This will prevent the image renderer from switching us to DirectDraw\r
+    // when we can't do it without skipping frames because we're not on a\r
+    // keyframe.  If it really has to switch us, it still will, but then we\r
+    // will have to wait for the next keyframe\r
+    if (!(pProps->dwSampleFlags & AM_SAMPLE_SPLICEPOINT)) {\r
+       dwFlags |= AM_GBF_NOTASYNCPOINT;\r
+    }\r
+\r
+    ASSERT(m_pOutput->m_pAllocator != NULL);\r
+    HRESULT hr = m_pOutput->m_pAllocator->GetBuffer(\r
+             &pOutSample\r
+             , pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID ?\r
+                   &pProps->tStart : NULL\r
+             , pProps->dwSampleFlags & AM_SAMPLE_STOPVALID ?\r
+                   &pProps->tStop : NULL\r
+             , dwFlags\r
+         );\r
+    *ppOutSample = pOutSample;\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    ASSERT(pOutSample);\r
+    IMediaSample2 *pOutSample2;\r
+    if (SUCCEEDED(pOutSample->QueryInterface(IID_IMediaSample2,\r
+                                             (void **)&pOutSample2))) {\r
+        /*  Modify it */\r
+        AM_SAMPLE2_PROPERTIES OutProps;\r
+        EXECUTE_ASSERT(SUCCEEDED(pOutSample2->GetProperties(\r
+            FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, tStart), (PBYTE)&OutProps)\r
+        ));\r
+        OutProps.dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;\r
+        OutProps.dwSampleFlags =\r
+            (OutProps.dwSampleFlags & AM_SAMPLE_TYPECHANGED) |\r
+            (pProps->dwSampleFlags & ~AM_SAMPLE_TYPECHANGED);\r
+        OutProps.tStart = pProps->tStart;\r
+        OutProps.tStop  = pProps->tStop;\r
+        OutProps.cbData = FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, dwStreamId);\r
+        hr = pOutSample2->SetProperties(\r
+            FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, dwStreamId),\r
+            (PBYTE)&OutProps\r
+        );\r
+        if (pProps->dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) {\r
+            m_bSampleSkipped = FALSE;\r
+        }\r
+        pOutSample2->Release();\r
+    } else {\r
+        if (pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID) {\r
+            pOutSample->SetTime(&pProps->tStart,\r
+                                &pProps->tStop);\r
+        }\r
+        if (pProps->dwSampleFlags & AM_SAMPLE_SPLICEPOINT) {\r
+            pOutSample->SetSyncPoint(TRUE);\r
+        }\r
+        if (pProps->dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) {\r
+            pOutSample->SetDiscontinuity(TRUE);\r
+            m_bSampleSkipped = FALSE;\r
+        }\r
+        // Copy the media times\r
+\r
+        LONGLONG MediaStart, MediaEnd;\r
+        if (pSample->GetMediaTime(&MediaStart,&MediaEnd) == NOERROR) {\r
+            pOutSample->SetMediaTime(&MediaStart,&MediaEnd);\r
+        }\r
+    }\r
+    return S_OK;\r
+}\r
+\r
+// override this to customize the transform process\r
+\r
+HRESULT\r
+CTransformFilter::Receive(IMediaSample *pSample)\r
+{\r
+    /*  Check for other streams and pass them on */\r
+    AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps();\r
+    if (pProps->dwStreamId != AM_STREAM_MEDIA) {\r
+        return m_pOutput->m_pInputPin->Receive(pSample);\r
+    }\r
+    HRESULT hr;\r
+    ASSERT(pSample);\r
+    IMediaSample * pOutSample;\r
+\r
+    // If no output to deliver to then no point sending us data\r
+\r
+    ASSERT (m_pOutput != NULL) ;\r
+\r
+    // Set up the output sample\r
+    hr = InitializeOutputSample(pSample, &pOutSample);\r
+\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    // Start timing the transform (if PERF is defined)\r
+    MSR_START(m_idTransform);\r
+\r
+    // have the derived class transform the data\r
+\r
+    hr = Transform(pSample, pOutSample);\r
+\r
+    // Stop the clock and log it (if PERF is defined)\r
+    MSR_STOP(m_idTransform);\r
+\r
+    if (FAILED(hr)) {\r
+       DbgLog((LOG_TRACE,1,TEXT("Error from transform")));\r
+    } else {\r
+        // the Transform() function can return S_FALSE to indicate that the\r
+        // sample should not be delivered; we only deliver the sample if it's\r
+        // really S_OK (same as NOERROR, of course.)\r
+        if (hr == NOERROR) {\r
+           hr = m_pOutput->m_pInputPin->Receive(pOutSample);\r
+            m_bSampleSkipped = FALSE;  // last thing no longer dropped\r
+        } else {\r
+            // S_FALSE returned from Transform is a PRIVATE agreement\r
+            // We should return NOERROR from Receive() in this cause because returning S_FALSE\r
+            // from Receive() means that this is the end of the stream and no more data should\r
+            // be sent.\r
+            if (S_FALSE == hr) {\r
+\r
+                //  Release the sample before calling notify to avoid\r
+                //  deadlocks if the sample holds a lock on the system\r
+                //  such as DirectDraw buffers do\r
+                pOutSample->Release();\r
+                m_bSampleSkipped = TRUE;\r
+                if (!m_bQualityChanged) {\r
+                    NotifyEvent(EC_QUALITY_CHANGE,0,0);\r
+                    m_bQualityChanged = TRUE;\r
+                }\r
+                return NOERROR;\r
+            }\r
+        }\r
+    }\r
+\r
+    // release the output buffer. If the connected pin still needs it,\r
+    // it will have addrefed it itself.\r
+    pOutSample->Release();\r
+\r
+    return hr;\r
+}\r
+\r
+\r
+// Return S_FALSE to mean "pass the note on upstream"\r
+// Return NOERROR (Same as S_OK)\r
+// to mean "I've done something about it, don't pass it on"\r
+HRESULT CTransformFilter::AlterQuality(Quality q)\r
+{\r
+    UNREFERENCED_PARAMETER(q);\r
+    return S_FALSE;\r
+}\r
+\r
+\r
+// EndOfStream received. Default behaviour is to deliver straight\r
+// downstream, since we have no queued data. If you overrode Receive\r
+// and have queue data, then you need to handle this and deliver EOS after\r
+// all queued data is sent\r
+HRESULT\r
+CTransformFilter::EndOfStream(void)\r
+{\r
+    HRESULT hr = NOERROR;\r
+    if (m_pOutput != NULL) {\r
+        hr = m_pOutput->DeliverEndOfStream();\r
+    }\r
+\r
+    return hr;\r
+}\r
+\r
+\r
+// enter flush state. Receives already blocked\r
+// must override this if you have queued data or a worker thread\r
+HRESULT\r
+CTransformFilter::BeginFlush(void)\r
+{\r
+    HRESULT hr = NOERROR;\r
+    if (m_pOutput != NULL) {\r
+       // block receives -- done by caller (CBaseInputPin::BeginFlush)\r
+\r
+       // discard queued data -- we have no queued data\r
+\r
+       // free anyone blocked on receive - not possible in this filter\r
+\r
+       // call downstream\r
+       hr = m_pOutput->DeliverBeginFlush();\r
+    }\r
+    return hr;\r
+}\r
+\r
+\r
+// leave flush state. must override this if you have queued data\r
+// or a worker thread\r
+HRESULT\r
+CTransformFilter::EndFlush(void)\r
+{\r
+    // sync with pushing thread -- we have no worker thread\r
+\r
+    // ensure no more data to go downstream -- we have no queued data\r
+\r
+    // call EndFlush on downstream pins\r
+    ASSERT (m_pOutput != NULL);\r
+    return m_pOutput->DeliverEndFlush();\r
+\r
+    // caller (the input pin's method) will unblock Receives\r
+}\r
+\r
+\r
+// override these so that the derived filter can catch them\r
+\r
+STDMETHODIMP\r
+CTransformFilter::Stop()\r
+{\r
+    CAutoLock lck1(&m_csFilter);\r
+    if (m_State == State_Stopped) {\r
+        return NOERROR;\r
+    }\r
+\r
+    // Succeed the Stop if we are not completely connected\r
+\r
+    ASSERT(m_pInput == NULL || m_pOutput != NULL);\r
+    if (m_pInput == NULL || m_pInput->IsConnected() == FALSE ||\r
+        m_pOutput->IsConnected() == FALSE) {\r
+                m_State = State_Stopped;\r
+                m_bEOSDelivered = FALSE;\r
+                return NOERROR;\r
+    }\r
+\r
+    ASSERT(m_pInput);\r
+    ASSERT(m_pOutput);\r
+\r
+    // decommit the input pin before locking or we can deadlock\r
+    m_pInput->Inactive();\r
+\r
+    // synchronize with Receive calls\r
+\r
+    CAutoLock lck2(&m_csReceive);\r
+    m_pOutput->Inactive();\r
+\r
+    // allow a class derived from CTransformFilter\r
+    // to know about starting and stopping streaming\r
+\r
+    HRESULT hr = StopStreaming();\r
+    if (SUCCEEDED(hr)) {\r
+       // complete the state transition\r
+       m_State = State_Stopped;\r
+       m_bEOSDelivered = FALSE;\r
+    }\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP\r
+CTransformFilter::Pause()\r
+{\r
+    CAutoLock lck(&m_csFilter);\r
+    HRESULT hr = NOERROR;\r
+\r
+    if (m_State == State_Paused) {\r
+        // (This space left deliberately blank)\r
+    }\r
+\r
+    // If we have no input pin or it isn't yet connected then when we are\r
+    // asked to pause we deliver an end of stream to the downstream filter.\r
+    // This makes sure that it doesn't sit there forever waiting for\r
+    // samples which we cannot ever deliver without an input connection.\r
+\r
+    else if (m_pInput == NULL || m_pInput->IsConnected() == FALSE) {\r
+        if (m_pOutput && m_bEOSDelivered == FALSE) {\r
+            m_pOutput->DeliverEndOfStream();\r
+            m_bEOSDelivered = TRUE;\r
+        }\r
+        m_State = State_Paused;\r
+    }\r
+\r
+    // We may have an input connection but no output connection\r
+    // However, if we have an input pin we do have an output pin\r
+\r
+    else if (m_pOutput->IsConnected() == FALSE) {\r
+        m_State = State_Paused;\r
+    }\r
+\r
+    else {\r
+       if (m_State == State_Stopped) {\r
+           // allow a class derived from CTransformFilter\r
+           // to know about starting and stopping streaming\r
+            CAutoLock lck2(&m_csReceive);\r
+           hr = StartStreaming();\r
+       }\r
+       if (SUCCEEDED(hr)) {\r
+           hr = CBaseFilter::Pause();\r
+       }\r
+    }\r
+\r
+    m_bSampleSkipped = FALSE;\r
+    m_bQualityChanged = FALSE;\r
+    return hr;\r
+}\r
+\r
+HRESULT\r
+CTransformFilter::NewSegment(\r
+    REFERENCE_TIME tStart,\r
+    REFERENCE_TIME tStop,\r
+    double dRate)\r
+{\r
+    if (m_pOutput != NULL) {\r
+        return m_pOutput->DeliverNewSegment(tStart, tStop, dRate);\r
+    }\r
+    return S_OK;\r
+}\r
+\r
+// Check streaming status\r
+HRESULT\r
+CTransformInputPin::CheckStreaming()\r
+{\r
+    ASSERT(m_pTransformFilter->m_pOutput != NULL);\r
+    if (!m_pTransformFilter->m_pOutput->IsConnected()) {\r
+        return VFW_E_NOT_CONNECTED;\r
+    } else {\r
+        //  Shouldn't be able to get any data if we're not connected!\r
+        ASSERT(IsConnected());\r
+\r
+        //  we're flushing\r
+        if (m_bFlushing) {\r
+            return S_FALSE;\r
+        }\r
+        //  Don't process stuff in Stopped state\r
+        if (IsStopped()) {\r
+            return VFW_E_WRONG_STATE;\r
+        }\r
+        if (m_bRunTimeError) {\r
+           return VFW_E_RUNTIME_ERROR;\r
+        }\r
+        return S_OK;\r
+    }\r
+}\r
+\r
+\r
+// =================================================================\r
+// Implements the CTransformInputPin class\r
+// =================================================================\r
+\r
+\r
+// constructor\r
+\r
+CTransformInputPin::CTransformInputPin(\r
+    __in_opt LPCTSTR pObjectName,\r
+    __inout CTransformFilter *pTransformFilter,\r
+    __inout HRESULT * phr,\r
+    __in_opt LPCWSTR pName)\r
+    : CBaseInputPin(pObjectName, pTransformFilter, &pTransformFilter->m_csFilter, phr, pName)\r
+{\r
+    DbgLog((LOG_TRACE,2,TEXT("CTransformInputPin::CTransformInputPin")));\r
+    m_pTransformFilter = pTransformFilter;\r
+}\r
+\r
+#ifdef UNICODE\r
+CTransformInputPin::CTransformInputPin(\r
+    __in_opt LPCSTR pObjectName,\r
+    __inout CTransformFilter *pTransformFilter,\r
+    __inout HRESULT * phr,\r
+    __in_opt LPCWSTR pName)\r
+    : CBaseInputPin(pObjectName, pTransformFilter, &pTransformFilter->m_csFilter, phr, pName)\r
+{\r
+    DbgLog((LOG_TRACE,2,TEXT("CTransformInputPin::CTransformInputPin")));\r
+    m_pTransformFilter = pTransformFilter;\r
+}\r
+#endif\r
+\r
+// provides derived filter a chance to grab extra interfaces\r
+\r
+HRESULT\r
+CTransformInputPin::CheckConnect(IPin *pPin)\r
+{\r
+    HRESULT hr = m_pTransformFilter->CheckConnect(PINDIR_INPUT,pPin);\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+    return CBaseInputPin::CheckConnect(pPin);\r
+}\r
+\r
+\r
+// provides derived filter a chance to release it's extra interfaces\r
+\r
+HRESULT\r
+CTransformInputPin::BreakConnect()\r
+{\r
+    //  Can't disconnect unless stopped\r
+    ASSERT(IsStopped());\r
+    m_pTransformFilter->BreakConnect(PINDIR_INPUT);\r
+    return CBaseInputPin::BreakConnect();\r
+}\r
+\r
+\r
+// Let derived class know when the input pin is connected\r
+\r
+HRESULT\r
+CTransformInputPin::CompleteConnect(IPin *pReceivePin)\r
+{\r
+    HRESULT hr = m_pTransformFilter->CompleteConnect(PINDIR_INPUT,pReceivePin);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return CBaseInputPin::CompleteConnect(pReceivePin);\r
+}\r
+\r
+\r
+// check that we can support a given media type\r
+\r
+HRESULT\r
+CTransformInputPin::CheckMediaType(const CMediaType* pmt)\r
+{\r
+    // Check the input type\r
+\r
+    HRESULT hr = m_pTransformFilter->CheckInputType(pmt);\r
+    if (S_OK != hr) {\r
+        return hr;\r
+    }\r
+\r
+    // if the output pin is still connected, then we have\r
+    // to check the transform not just the input format\r
+\r
+    if ((m_pTransformFilter->m_pOutput != NULL) &&\r
+        (m_pTransformFilter->m_pOutput->IsConnected())) {\r
+            return m_pTransformFilter->CheckTransform(\r
+                      pmt,\r
+                     &m_pTransformFilter->m_pOutput->CurrentMediaType());\r
+    } else {\r
+        return hr;\r
+    }\r
+}\r
+\r
+\r
+// set the media type for this connection\r
+\r
+HRESULT\r
+CTransformInputPin::SetMediaType(const CMediaType* mtIn)\r
+{\r
+    // Set the base class media type (should always succeed)\r
+    HRESULT hr = CBasePin::SetMediaType(mtIn);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    // check the transform can be done (should always succeed)\r
+    ASSERT(SUCCEEDED(m_pTransformFilter->CheckInputType(mtIn)));\r
+\r
+    return m_pTransformFilter->SetMediaType(PINDIR_INPUT,mtIn);\r
+}\r
+\r
+\r
+// =================================================================\r
+// Implements IMemInputPin interface\r
+// =================================================================\r
+\r
+\r
+// provide EndOfStream that passes straight downstream\r
+// (there is no queued data)\r
+STDMETHODIMP\r
+CTransformInputPin::EndOfStream(void)\r
+{\r
+    CAutoLock lck(&m_pTransformFilter->m_csReceive);\r
+    HRESULT hr = CheckStreaming();\r
+    if (S_OK == hr) {\r
+       hr = m_pTransformFilter->EndOfStream();\r
+    }\r
+    return hr;\r
+}\r
+\r
+\r
+// enter flushing state. Call default handler to block Receives, then\r
+// pass to overridable method in filter\r
+STDMETHODIMP\r
+CTransformInputPin::BeginFlush(void)\r
+{\r
+    CAutoLock lck(&m_pTransformFilter->m_csFilter);\r
+    //  Are we actually doing anything?\r
+    ASSERT(m_pTransformFilter->m_pOutput != NULL);\r
+    if (!IsConnected() ||\r
+        !m_pTransformFilter->m_pOutput->IsConnected()) {\r
+        return VFW_E_NOT_CONNECTED;\r
+    }\r
+    HRESULT hr = CBaseInputPin::BeginFlush();\r
+    if (FAILED(hr)) {\r
+       return hr;\r
+    }\r
+\r
+    return m_pTransformFilter->BeginFlush();\r
+}\r
+\r
+\r
+// leave flushing state.\r
+// Pass to overridable method in filter, then call base class\r
+// to unblock receives (finally)\r
+STDMETHODIMP\r
+CTransformInputPin::EndFlush(void)\r
+{\r
+    CAutoLock lck(&m_pTransformFilter->m_csFilter);\r
+    //  Are we actually doing anything?\r
+    ASSERT(m_pTransformFilter->m_pOutput != NULL);\r
+    if (!IsConnected() ||\r
+        !m_pTransformFilter->m_pOutput->IsConnected()) {\r
+        return VFW_E_NOT_CONNECTED;\r
+    }\r
+\r
+    HRESULT hr = m_pTransformFilter->EndFlush();\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    return CBaseInputPin::EndFlush();\r
+}\r
+\r
+\r
+// here's the next block of data from the stream.\r
+// AddRef it yourself if you need to hold it beyond the end\r
+// of this call.\r
+\r
+HRESULT\r
+CTransformInputPin::Receive(IMediaSample * pSample)\r
+{\r
+    HRESULT hr;\r
+    CAutoLock lck(&m_pTransformFilter->m_csReceive);\r
+    ASSERT(pSample);\r
+\r
+    // check all is well with the base class\r
+    hr = CBaseInputPin::Receive(pSample);\r
+    if (S_OK == hr) {\r
+        hr = m_pTransformFilter->Receive(pSample);\r
+    }\r
+    return hr;\r
+}\r
+\r
+\r
+\r
+\r
+// override to pass downstream\r
+STDMETHODIMP\r
+CTransformInputPin::NewSegment(\r
+    REFERENCE_TIME tStart,\r
+    REFERENCE_TIME tStop,\r
+    double dRate)\r
+{\r
+    //  Save the values in the pin\r
+    CBasePin::NewSegment(tStart, tStop, dRate);\r
+    return m_pTransformFilter->NewSegment(tStart, tStop, dRate);\r
+}\r
+\r
+\r
+\r
+\r
+// =================================================================\r
+// Implements the CTransformOutputPin class\r
+// =================================================================\r
+\r
+\r
+// constructor\r
+\r
+CTransformOutputPin::CTransformOutputPin(\r
+    __in_opt LPCTSTR pObjectName,\r
+    __inout CTransformFilter *pTransformFilter,\r
+    __inout HRESULT * phr,\r
+    __in_opt LPCWSTR pPinName)\r
+    : CBaseOutputPin(pObjectName, pTransformFilter, &pTransformFilter->m_csFilter, phr, pPinName),\r
+      m_pPosition(NULL)\r
+{\r
+    DbgLog((LOG_TRACE,2,TEXT("CTransformOutputPin::CTransformOutputPin")));\r
+    m_pTransformFilter = pTransformFilter;\r
+\r
+}\r
+\r
+#ifdef UNICODE\r
+CTransformOutputPin::CTransformOutputPin(\r
+    __in_opt LPCSTR pObjectName,\r
+    __inout CTransformFilter *pTransformFilter,\r
+    __inout HRESULT * phr,\r
+    __in_opt LPCWSTR pPinName)\r
+    : CBaseOutputPin(pObjectName, pTransformFilter, &pTransformFilter->m_csFilter, phr, pPinName),\r
+      m_pPosition(NULL)\r
+{\r
+    DbgLog((LOG_TRACE,2,TEXT("CTransformOutputPin::CTransformOutputPin")));\r
+    m_pTransformFilter = pTransformFilter;\r
+\r
+}\r
+#endif\r
+\r
+// destructor\r
+\r
+CTransformOutputPin::~CTransformOutputPin()\r
+{\r
+    DbgLog((LOG_TRACE,2,TEXT("CTransformOutputPin::~CTransformOutputPin")));\r
+\r
+    if (m_pPosition) m_pPosition->Release();\r
+}\r
+\r
+\r
+// overriden to expose IMediaPosition and IMediaSeeking control interfaces\r
+\r
+STDMETHODIMP\r
+CTransformOutputPin::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    CheckPointer(ppv,E_POINTER);\r
+    ValidateReadWritePtr(ppv,sizeof(PVOID));\r
+    *ppv = NULL;\r
+\r
+    if (riid == IID_IMediaPosition || riid == IID_IMediaSeeking) {\r
+\r
+        // we should have an input pin by now\r
+\r
+        ASSERT(m_pTransformFilter->m_pInput != NULL);\r
+\r
+        if (m_pPosition == NULL) {\r
+\r
+            HRESULT hr = CreatePosPassThru(\r
+                             GetOwner(),\r
+                             FALSE,\r
+                             (IPin *)m_pTransformFilter->m_pInput,\r
+                             &m_pPosition);\r
+            if (FAILED(hr)) {\r
+                return hr;\r
+            }\r
+        }\r
+        return m_pPosition->QueryInterface(riid, ppv);\r
+    } else {\r
+        return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv);\r
+    }\r
+}\r
+\r
+\r
+// provides derived filter a chance to grab extra interfaces\r
+\r
+HRESULT\r
+CTransformOutputPin::CheckConnect(IPin *pPin)\r
+{\r
+    // we should have an input connection first\r
+\r
+    ASSERT(m_pTransformFilter->m_pInput != NULL);\r
+    if ((m_pTransformFilter->m_pInput->IsConnected() == FALSE)) {\r
+           return E_UNEXPECTED;\r
+    }\r
+\r
+    HRESULT hr = m_pTransformFilter->CheckConnect(PINDIR_OUTPUT,pPin);\r
+    if (FAILED(hr)) {\r
+           return hr;\r
+    }\r
+    return CBaseOutputPin::CheckConnect(pPin);\r
+}\r
+\r
+\r
+// provides derived filter a chance to release it's extra interfaces\r
+\r
+HRESULT\r
+CTransformOutputPin::BreakConnect()\r
+{\r
+    //  Can't disconnect unless stopped\r
+    ASSERT(IsStopped());\r
+    m_pTransformFilter->BreakConnect(PINDIR_OUTPUT);\r
+    return CBaseOutputPin::BreakConnect();\r
+}\r
+\r
+\r
+// Let derived class know when the output pin is connected\r
+\r
+HRESULT\r
+CTransformOutputPin::CompleteConnect(IPin *pReceivePin)\r
+{\r
+    HRESULT hr = m_pTransformFilter->CompleteConnect(PINDIR_OUTPUT,pReceivePin);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return CBaseOutputPin::CompleteConnect(pReceivePin);\r
+}\r
+\r
+\r
+// check a given transform - must have selected input type first\r
+\r
+HRESULT\r
+CTransformOutputPin::CheckMediaType(const CMediaType* pmtOut)\r
+{\r
+    // must have selected input first\r
+    ASSERT(m_pTransformFilter->m_pInput != NULL);\r
+    if ((m_pTransformFilter->m_pInput->IsConnected() == FALSE)) {\r
+               return E_INVALIDARG;\r
+    }\r
+\r
+    return m_pTransformFilter->CheckTransform(\r
+                                   &m_pTransformFilter->m_pInput->CurrentMediaType(),\r
+                                   pmtOut);\r
+}\r
+\r
+\r
+// called after we have agreed a media type to actually set it in which case\r
+// we run the CheckTransform function to get the output format type again\r
+\r
+HRESULT\r
+CTransformOutputPin::SetMediaType(const CMediaType* pmtOut)\r
+{\r
+    HRESULT hr = NOERROR;\r
+    ASSERT(m_pTransformFilter->m_pInput != NULL);\r
+\r
+    ASSERT(m_pTransformFilter->m_pInput->CurrentMediaType().IsValid());\r
+\r
+    // Set the base class media type (should always succeed)\r
+    hr = CBasePin::SetMediaType(pmtOut);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+#ifdef DEBUG\r
+    if (FAILED(m_pTransformFilter->CheckTransform(&m_pTransformFilter->\r
+                                       m_pInput->CurrentMediaType(),pmtOut))) {\r
+       DbgLog((LOG_ERROR,0,TEXT("*** This filter is accepting an output media type")));\r
+       DbgLog((LOG_ERROR,0,TEXT("    that it can't currently transform to.  I hope")));\r
+       DbgLog((LOG_ERROR,0,TEXT("    it's smart enough to reconnect its input.")));\r
+    }\r
+#endif\r
+\r
+    return m_pTransformFilter->SetMediaType(PINDIR_OUTPUT,pmtOut);\r
+}\r
+\r
+\r
+// pass the buffer size decision through to the main transform class\r
+\r
+HRESULT\r
+CTransformOutputPin::DecideBufferSize(\r
+    IMemAllocator * pAllocator,\r
+    __inout ALLOCATOR_PROPERTIES* pProp)\r
+{\r
+    return m_pTransformFilter->DecideBufferSize(pAllocator, pProp);\r
+}\r
+\r
+\r
+\r
+// return a specific media type indexed by iPosition\r
+\r
+HRESULT\r
+CTransformOutputPin::GetMediaType(\r
+    int iPosition,\r
+    __inout CMediaType *pMediaType)\r
+{\r
+    ASSERT(m_pTransformFilter->m_pInput != NULL);\r
+\r
+    //  We don't have any media types if our input is not connected\r
+\r
+    if (m_pTransformFilter->m_pInput->IsConnected()) {\r
+        return m_pTransformFilter->GetMediaType(iPosition,pMediaType);\r
+    } else {\r
+        return VFW_S_NO_MORE_ITEMS;\r
+    }\r
+}\r
+\r
+\r
+// Override this if you can do something constructive to act on the\r
+// quality message.  Consider passing it upstream as well\r
+\r
+// Pass the quality mesage on upstream.\r
+\r
+STDMETHODIMP\r
+CTransformOutputPin::Notify(IBaseFilter * pSender, Quality q)\r
+{\r
+    UNREFERENCED_PARAMETER(pSender);\r
+    ValidateReadPtr(pSender,sizeof(IBaseFilter));\r
+\r
+    // First see if we want to handle this ourselves\r
+    HRESULT hr = m_pTransformFilter->AlterQuality(q);\r
+    if (hr!=S_FALSE) {\r
+        return hr;        // either S_OK or a failure\r
+    }\r
+\r
+    // S_FALSE means we pass the message on.\r
+    // Find the quality sink for our input pin and send it there\r
+\r
+    ASSERT(m_pTransformFilter->m_pInput != NULL);\r
+\r
+    return m_pTransformFilter->m_pInput->PassNotify(q);\r
+\r
+} // Notify\r
+\r
+\r
+// the following removes a very large number of level 4 warnings from the microsoft\r
+// compiler output, which are not useful at all in this case.\r
+#pragma warning(disable:4514)\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/transfrm.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/transfrm.h
new file mode 100644 (file)
index 0000000..9b27647
--- /dev/null
@@ -0,0 +1,304 @@
+//------------------------------------------------------------------------------\r
+// File: Transfrm.h\r
+//\r
+// Desc: DirectShow base classes - defines classes from which simple \r
+//       transform codecs may be derived.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+// It assumes the codec has one input and one output stream, and has no\r
+// interest in memory management, interface negotiation or anything else.\r
+//\r
+// derive your class from this, and supply Transform and the media type/format\r
+// negotiation functions. Implement that class, compile and link and\r
+// you're done.\r
+\r
+\r
+#ifndef __TRANSFRM__\r
+#define __TRANSFRM__\r
+\r
+// ======================================================================\r
+// This is the com object that represents a simple transform filter. It\r
+// supports IBaseFilter, IMediaFilter and two pins through nested interfaces\r
+// ======================================================================\r
+\r
+class CTransformFilter;\r
+\r
+// ==================================================\r
+// Implements the input pin\r
+// ==================================================\r
+\r
+class CTransformInputPin : public CBaseInputPin\r
+{\r
+    friend class CTransformFilter;\r
+\r
+protected:\r
+    CTransformFilter *m_pTransformFilter;\r
+\r
+\r
+public:\r
+\r
+    CTransformInputPin(\r
+        __in_opt LPCTSTR pObjectName,\r
+        __inout CTransformFilter *pTransformFilter,\r
+        __inout HRESULT * phr,\r
+        __in_opt LPCWSTR pName);\r
+#ifdef UNICODE\r
+    CTransformInputPin(\r
+        __in_opt LPCSTR pObjectName,\r
+        __inout CTransformFilter *pTransformFilter,\r
+        __inout HRESULT * phr,\r
+        __in_opt LPCWSTR pName);\r
+#endif\r
+\r
+    STDMETHODIMP QueryId(__deref_out LPWSTR * Id)\r
+    {\r
+        return AMGetWideString(L"In", Id);\r
+    }\r
+\r
+    // Grab and release extra interfaces if required\r
+\r
+    HRESULT CheckConnect(IPin *pPin);\r
+    HRESULT BreakConnect();\r
+    HRESULT CompleteConnect(IPin *pReceivePin);\r
+\r
+    // check that we can support this output type\r
+    HRESULT CheckMediaType(const CMediaType* mtIn);\r
+\r
+    // set the connection media type\r
+    HRESULT SetMediaType(const CMediaType* mt);\r
+\r
+    // --- IMemInputPin -----\r
+\r
+    // here's the next block of data from the stream.\r
+    // AddRef it yourself if you need to hold it beyond the end\r
+    // of this call.\r
+    STDMETHODIMP Receive(IMediaSample * pSample);\r
+\r
+    // provide EndOfStream that passes straight downstream\r
+    // (there is no queued data)\r
+    STDMETHODIMP EndOfStream(void);\r
+\r
+    // passes it to CTransformFilter::BeginFlush\r
+    STDMETHODIMP BeginFlush(void);\r
+\r
+    // passes it to CTransformFilter::EndFlush\r
+    STDMETHODIMP EndFlush(void);\r
+\r
+    STDMETHODIMP NewSegment(\r
+                        REFERENCE_TIME tStart,\r
+                        REFERENCE_TIME tStop,\r
+                        double dRate);\r
+\r
+    // Check if it's OK to process samples\r
+    virtual HRESULT CheckStreaming();\r
+\r
+    // Media type\r
+public:\r
+    CMediaType& CurrentMediaType() { return m_mt; };\r
+\r
+};\r
+\r
+// ==================================================\r
+// Implements the output pin\r
+// ==================================================\r
+\r
+class CTransformOutputPin : public CBaseOutputPin\r
+{\r
+    friend class CTransformFilter;\r
+\r
+protected:\r
+    CTransformFilter *m_pTransformFilter;\r
+\r
+public:\r
+\r
+    // implement IMediaPosition by passing upstream\r
+    IUnknown * m_pPosition;\r
+\r
+    CTransformOutputPin(\r
+        __in_opt LPCTSTR pObjectName,\r
+        __inout CTransformFilter *pTransformFilter,\r
+        __inout HRESULT * phr,\r
+        __in_opt LPCWSTR pName);\r
+#ifdef UNICODE\r
+    CTransformOutputPin(\r
+        __in_opt LPCSTR pObjectName,\r
+        __inout CTransformFilter *pTransformFilter,\r
+        __inout HRESULT * phr,\r
+        __in_opt LPCWSTR pName);\r
+#endif\r
+    ~CTransformOutputPin();\r
+\r
+    // override to expose IMediaPosition\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);\r
+\r
+    // --- CBaseOutputPin ------------\r
+\r
+    STDMETHODIMP QueryId(__deref_out LPWSTR * Id)\r
+    {\r
+        return AMGetWideString(L"Out", Id);\r
+    }\r
+\r
+    // Grab and release extra interfaces if required\r
+\r
+    HRESULT CheckConnect(IPin *pPin);\r
+    HRESULT BreakConnect();\r
+    HRESULT CompleteConnect(IPin *pReceivePin);\r
+\r
+    // check that we can support this output type\r
+    HRESULT CheckMediaType(const CMediaType* mtOut);\r
+\r
+    // set the connection media type\r
+    HRESULT SetMediaType(const CMediaType *pmt);\r
+\r
+    // called from CBaseOutputPin during connection to ask for\r
+    // the count and size of buffers we need.\r
+    HRESULT DecideBufferSize(\r
+                IMemAllocator * pAlloc,\r
+                __inout ALLOCATOR_PROPERTIES *pProp);\r
+\r
+    // returns the preferred formats for a pin\r
+    HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType);\r
+\r
+    // inherited from IQualityControl via CBasePin\r
+    STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);\r
+\r
+    // Media type\r
+public:\r
+    CMediaType& CurrentMediaType() { return m_mt; };\r
+};\r
+\r
+\r
+class AM_NOVTABLE CTransformFilter : public CBaseFilter\r
+{\r
+\r
+public:\r
+\r
+    // map getpin/getpincount for base enum of pins to owner\r
+    // override this to return more specialised pin objects\r
+\r
+    virtual int GetPinCount();\r
+    virtual CBasePin * GetPin(int n);\r
+    STDMETHODIMP FindPin(LPCWSTR Id, __deref_out IPin **ppPin);\r
+\r
+    // override state changes to allow derived transform filter\r
+    // to control streaming start/stop\r
+    STDMETHODIMP Stop();\r
+    STDMETHODIMP Pause();\r
+\r
+public:\r
+\r
+    CTransformFilter(__in_opt LPCTSTR , __inout_opt LPUNKNOWN, REFCLSID clsid);\r
+#ifdef UNICODE\r
+    CTransformFilter(__in_opt LPCSTR , __inout_opt LPUNKNOWN, REFCLSID clsid);\r
+#endif\r
+    ~CTransformFilter();\r
+\r
+    // =================================================================\r
+    // ----- override these bits ---------------------------------------\r
+    // =================================================================\r
+\r
+    // These must be supplied in a derived class\r
+\r
+    virtual HRESULT Transform(IMediaSample * pIn, IMediaSample *pOut);\r
+\r
+    // check if you can support mtIn\r
+    virtual HRESULT CheckInputType(const CMediaType* mtIn) PURE;\r
+\r
+    // check if you can support the transform from this input to this output\r
+    virtual HRESULT CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut) PURE;\r
+\r
+    // this goes in the factory template table to create new instances\r
+    // static CCOMObject * CreateInstance(__inout_opt LPUNKNOWN, HRESULT *);\r
+\r
+    // call the SetProperties function with appropriate arguments\r
+    virtual HRESULT DecideBufferSize(\r
+                        IMemAllocator * pAllocator,\r
+                        __inout ALLOCATOR_PROPERTIES *pprop) PURE;\r
+\r
+    // override to suggest OUTPUT pin media types\r
+    virtual HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType) PURE;\r
+\r
+\r
+\r
+    // =================================================================\r
+    // ----- Optional Override Methods           -----------------------\r
+    // =================================================================\r
+\r
+    // you can also override these if you want to know about streaming\r
+    virtual HRESULT StartStreaming();\r
+    virtual HRESULT StopStreaming();\r
+\r
+    // override if you can do anything constructive with quality notifications\r
+    virtual HRESULT AlterQuality(Quality q);\r
+\r
+    // override this to know when the media type is actually set\r
+    virtual HRESULT SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt);\r
+\r
+    // chance to grab extra interfaces on connection\r
+    virtual HRESULT CheckConnect(PIN_DIRECTION dir,IPin *pPin);\r
+    virtual HRESULT BreakConnect(PIN_DIRECTION dir);\r
+    virtual HRESULT CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin);\r
+\r
+    // chance to customize the transform process\r
+    virtual HRESULT Receive(IMediaSample *pSample);\r
+\r
+    // Standard setup for output sample\r
+    HRESULT InitializeOutputSample(IMediaSample *pSample, __deref_out IMediaSample **ppOutSample);\r
+\r
+    // if you override Receive, you may need to override these three too\r
+    virtual HRESULT EndOfStream(void);\r
+    virtual HRESULT BeginFlush(void);\r
+    virtual HRESULT EndFlush(void);\r
+    virtual HRESULT NewSegment(\r
+                        REFERENCE_TIME tStart,\r
+                        REFERENCE_TIME tStop,\r
+                        double dRate);\r
+\r
+#ifdef PERF\r
+    // Override to register performance measurement with a less generic string\r
+    // You should do this to avoid confusion with other filters\r
+    virtual void RegisterPerfId()\r
+         {m_idTransform = MSR_REGISTER(TEXT("Transform"));}\r
+#endif // PERF\r
+\r
+\r
+// implementation details\r
+\r
+protected:\r
+\r
+#ifdef PERF\r
+    int m_idTransform;                 // performance measuring id\r
+#endif\r
+    BOOL m_bEOSDelivered;              // have we sent EndOfStream\r
+    BOOL m_bSampleSkipped;             // Did we just skip a frame\r
+    BOOL m_bQualityChanged;            // Have we degraded?\r
+\r
+    // critical section protecting filter state.\r
+\r
+    CCritSec m_csFilter;\r
+\r
+    // critical section stopping state changes (ie Stop) while we're\r
+    // processing a sample.\r
+    //\r
+    // This critical section is held when processing\r
+    // events that occur on the receive thread - Receive() and EndOfStream().\r
+    //\r
+    // If you want to hold both m_csReceive and m_csFilter then grab\r
+    // m_csFilter FIRST - like CTransformFilter::Stop() does.\r
+\r
+    CCritSec m_csReceive;\r
+\r
+    // these hold our input and output pins\r
+\r
+    friend class CTransformInputPin;\r
+    friend class CTransformOutputPin;\r
+    CTransformInputPin *m_pInput;\r
+    CTransformOutputPin *m_pOutput;\r
+};\r
+\r
+#endif /* __TRANSFRM__ */\r
+\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/transip.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/transip.cpp
new file mode 100644 (file)
index 0000000..e8e12eb
--- /dev/null
@@ -0,0 +1,974 @@
+//------------------------------------------------------------------------------\r
+// File: TransIP.cpp\r
+//\r
+// Desc: DirectShow base classes - implements class for simple Transform-\r
+//       In-Place filters such as audio.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+// How allocators are decided.\r
+//\r
+// An in-place transform tries to do its work in someone else's buffers.\r
+// It tries to persuade the filters on either side to use the same allocator\r
+// (and for that matter the same media type).  In desperation, if the downstream\r
+// filter refuses to supply an allocator and the upstream filter offers only\r
+// a read-only one then it will provide an allocator.\r
+// if the upstream filter insists on a read-only allocator then the transform\r
+// filter will (reluctantly) copy the data before transforming it.\r
+//\r
+// In order to pass an allocator through it needs to remember the one it got\r
+// from the first connection to pass it on to the second one.\r
+//\r
+// It is good if we can avoid insisting on a particular order of connection\r
+// (There is a precedent for insisting on the input\r
+// being connected first.  Insisting on the output being connected first is\r
+// not allowed.  That would break RenderFile.)\r
+//\r
+// The base pin classes (CBaseOutputPin and CBaseInputPin) both have a\r
+// m_pAllocator member which is used in places like\r
+// CBaseOutputPin::GetDeliveryBuffer and CBaseInputPin::Inactive.\r
+// To avoid lots of extra overriding, we should keep these happy\r
+// by using these pointers.\r
+//\r
+// When each pin is connected, it will set the corresponding m_pAllocator\r
+// and will have a single ref-count on that allocator.\r
+//\r
+// Refcounts are acquired by GetAllocator calls which return AddReffed\r
+// allocators and are released in one of:\r
+//     CBaseInputPin::Disconnect\r
+//     CBaseOutputPin::BreakConect\r
+// In each case m_pAllocator is set to NULL after the release, so this\r
+// is the last chance to ever release it.  If there should ever be\r
+// multiple refcounts associated with the same pointer, this had better\r
+// be cleared up before that happens.  To avoid such problems, we'll\r
+// stick with one per pointer.\r
+\r
+\r
+\r
+// RECONNECTING and STATE CHANGES\r
+//\r
+// Each pin could be disconnected, connected with a read-only allocator,\r
+// connected with an upstream read/write allocator, connected with an\r
+// allocator from downstream or connected with its own allocator.\r
+// Five states for each pin gives a data space of 25 states.\r
+//\r
+// Notation:\r
+//\r
+// R/W == read/write\r
+// R-O == read-only\r
+//\r
+// <input pin state> <output pin state> <comments>\r
+//\r
+// 00 means an unconnected pin.\r
+// <- means using a R/W allocator from the upstream filter\r
+// <= means using a R-O allocator from an upstream filter\r
+// || means using our own (R/W) allocator.\r
+// -> means using a R/W allocator from a downstream filter\r
+//    (a R-O allocator from downstream is nonsense, it can't ever work).\r
+//\r
+//\r
+// That makes 25 possible states.  Some states are nonsense (two different\r
+// allocators from the same place).  These are just an artifact of the notation.\r
+//        <=  <-  Nonsense.\r
+//        <-  <=  Nonsense\r
+// Some states are illegal (the output pin never accepts a R-O allocator):\r
+//        00  <=  !! Error !!\r
+//        <=  <=  !! Error !!\r
+//        ||  <=  !! Error !!\r
+//        ->  <=  !! Error !!\r
+// Three states appears to be inaccessible:\r
+//        ->  ||  Inaccessible\r
+//        ||  ->  Inaccessible\r
+//        ||  <-  Inaccessible\r
+// Some states only ever occur as intermediates with a pending reconnect which\r
+// is guaranteed to finish in another state.\r
+//        ->  00  ?? unstable goes to || 00\r
+//        00  <-  ?? unstable goes to 00 ||\r
+//        ->  <-  ?? unstable goes to -> ->\r
+//        <-  ||  ?? unstable goes to <- <-\r
+//        <-  ->  ?? unstable goes to <- <-\r
+// And that leaves 11 possible resting states:\r
+// 1      00  00  Nothing connected.\r
+// 2      <-  00  Input pin connected.\r
+// 3      <=  00  Input pin connected using R-O allocator.\r
+// 4      ||  00  Needs several state changes to get here.\r
+// 5      00  ||  Output pin connected using our allocator\r
+// 6      00  ->  Downstream only connected\r
+// 7      ||  ||  Undesirable but can be forced upon us.\r
+// 8      <=  ||  Copy forced.  <=  -> is preferable\r
+// 9      <=  ->  OK - forced to copy.\r
+// 10     <-  <-  Transform in place (ideal)\r
+// 11     ->  ->  Transform in place (ideal)\r
+//\r
+// The object of the exercise is to ensure that we finish up in states\r
+// 10 or 11 whenever possible.  State 10 is only possible if the upstream\r
+// filter has a R/W allocator (the AVI splitter notoriously\r
+// doesn't) and state 11 is only possible if the downstream filter does\r
+// offer an allocator.\r
+//\r
+// The transition table (entries marked * go via a reconnect)\r
+//\r
+// There are 8 possible transitions:\r
+// A: Connect upstream to filter with R-O allocator that insists on using it.\r
+// B: Connect upstream to filter with R-O allocator but chooses not to use it.\r
+// C: Connect upstream to filter with R/W allocator and insists on using it.\r
+// D: Connect upstream to filter with R/W allocator but chooses not to use it.\r
+// E: Connect downstream to a filter that offers an allocator\r
+// F: Connect downstream to a filter that does not offer an allocator\r
+// G: disconnect upstream\r
+// H: Disconnect downstream\r
+//\r
+//            A      B      C      D      E      F      G      H\r
+//           ---------------------------------------------------------\r
+// 00  00 1 | 3      3      2      2      6      5      .      .      |1  00  00\r
+// <-  00 2 | .      .      .      .      *10/11 10     1      .      |2  <-  00\r
+// <=  00 3 | .      .      .      .      *9/11  *7/8   1      .      |3  <=  00\r
+// ||  00 4 | .      .      .      .      *8     *7     1      .      |4  ||  00\r
+// 00  || 5 | 8      7      *10    7      .      .      .      1      |5  00  ||\r
+// 00  -> 6 | 9      11     *10    11     .      .      .      1      |6  00  ->\r
+// ||  || 7 | .      .      .      .      .      .      5      4      |7  ||  ||\r
+// <=  || 8 | .      .      .      .      .      .      5      3      |8  <=  ||\r
+// <=  -> 9 | .      .      .      .      .      .      6      3      |9  <=  ->\r
+// <-  <- 10| .      .      .      .      .      .      *5/6   2      |10 <-  <-\r
+// ->  -> 11| .      .      .      .      .      .      6      *2/3   |11 ->  ->\r
+//           ---------------------------------------------------------\r
+//            A      B      C      D      E      F      G      H\r
+//\r
+// All these states are accessible without requiring any filter to\r
+// change its behaviour but not all transitions are accessible, for\r
+// instance a transition from state 4 to anywhere other than\r
+// state 8 requires that the upstream filter first offer a R-O allocator\r
+// and then changes its mind and offer R/W.  This is NOT allowable - it\r
+// leads to things like the output pin getting a R/W allocator from\r
+// upstream and then the input pin being told it can only have a R-O one.\r
+// Note that you CAN change (say) the upstream filter for a different one, but\r
+// only as a disconnect / connect, not as a Reconnect.  (Exercise for\r
+// the reader is to see how you get into state 4).\r
+//\r
+// The reconnection stuff goes as follows (some of the cases shown here as\r
+// "no reconnect" may get one to finalise media type - an old story).\r
+// If there is a reconnect where it says "no reconnect" here then the\r
+// reconnection must not change the allocator choice.\r
+//\r
+// state 2: <- 00 transition E <- <- case C <- <- (no change)\r
+//                                   case D -> <- and then to -> ->\r
+//\r
+// state 2: <- 00 transition F <- <- (no reconnect)\r
+//\r
+// state 3: <= 00 transition E <= -> case A <= -> (no change)\r
+//                                   case B -> ->\r
+//                transition F <= || case A <= || (no change)\r
+//                                   case B || ||\r
+//\r
+// state 4: || 00 transition E || || case B -> || and then all cases to -> ->\r
+//                           F || || case B || || (no change)\r
+//\r
+// state 5: 00 || transition A <= || (no reconnect)\r
+//                           B || || (no reconnect)\r
+//                           C <- || all cases     <- <-\r
+//                           D || || (unfortunate, but upstream's choice)\r
+//\r
+// state 6: 00 -> transition A <= -> (no reconnect)\r
+//                           B -> -> (no reconnect)\r
+//                           C <- -> all cases <- <-\r
+//                           D -> -> (no reconnect)\r
+//\r
+// state 10:<- <- transition G 00 <- case E 00 ->\r
+//                                   case F 00 ||\r
+//\r
+// state 11:-> -> transition H -> 00 case A <= 00 (schizo)\r
+//                                   case B <= 00\r
+//                                   case C <- 00 (schizo)\r
+//                                   case D <- 00\r
+//\r
+// The Rules:\r
+// To sort out media types:\r
+// The input is reconnected\r
+//    if the input pin is connected and the output pin connects\r
+// The output is reconnected\r
+//    If the output pin is connected\r
+//    and the input pin connects to a different media type\r
+//\r
+// To sort out allocators:\r
+// The input is reconnected\r
+//    if the output disconnects and the input was using a downstream allocator\r
+// The output pin calls SetAllocator to pass on a new allocator\r
+//    if the output is connected and\r
+//       if the input disconnects and the output was using an upstream allocator\r
+//       if the input acquires an allocator different from the output one\r
+//          and that new allocator is not R-O\r
+//\r
+// Data is copied (i.e. call getbuffer and copy the data before transforming it)\r
+//    if the two allocators are different.\r
+\r
+\r
+\r
+// CHAINS of filters:\r
+//\r
+// We sit between two filters (call them A and Z).  We should finish up\r
+// with the same allocator on both of our pins and that should be the\r
+// same one that A and Z would have agreed on if we hadn't been in the\r
+// way.  Furthermore, it should not matter how many in-place transforms\r
+// are in the way.  Let B, C, D... be in-place transforms ("us").\r
+// Here's how it goes:\r
+//\r
+// 1.\r
+// A connects to B.  They agree on A's allocator.\r
+//   A-a->B\r
+//\r
+// 2.\r
+// B connects to C.  Same story. There is no point in a reconnect, but\r
+// B will request an input reconnect anyway.\r
+//   A-a->B-a->C\r
+//\r
+// 3.\r
+// C connects to Z.\r
+// C insists on using A's allocator, but compromises by requesting a reconnect.\r
+// of C's input.\r
+//   A-a->B-?->C-a->Z\r
+//\r
+// We now have pending reconnects on both A--->B and B--->C\r
+//\r
+// 4.\r
+// The A--->B link is reconnected.\r
+// A asks B for an allocator.  B sees that it has a downstream connection so\r
+// asks its downstream input pin i.e. C's input pin for an allocator.  C sees\r
+// that it too has a downstream connection so asks Z for an allocator.\r
+//\r
+// Even though Z's input pin is connected, it is being asked for an allocator.\r
+// It could refuse, in which case the chain is done and will use A's allocator\r
+// Alternatively, Z may supply one.  A chooses either Z's or A's own one.\r
+// B's input pin gets NotifyAllocator called to tell it the decision and it\r
+// propagates this downstream by calling ReceiveAllocator on its output pin\r
+// which calls NotifyAllocator on the next input pin downstream etc.\r
+// If the choice is Z then it goes:\r
+//   A-z->B-a->C-a->Z\r
+//   A-z->B-z->C-a->Z\r
+//   A-z->B-z->C-z->Z\r
+//\r
+// And that's IT!!  Any further (essentially spurious) reconnects peter out\r
+// with no change in the chain.\r
+\r
+#include <streams.h>\r
+#include <measure.h>\r
+#include <transip.h>\r
+\r
+\r
+// =================================================================\r
+// Implements the CTransInPlaceFilter class\r
+// =================================================================\r
+\r
+CTransInPlaceFilter::CTransInPlaceFilter\r
+   ( __in_opt LPCTSTR    pName,\r
+     __inout_opt LPUNKNOWN  pUnk,\r
+     REFCLSID   clsid,\r
+     __inout HRESULT   *phr,\r
+     bool       bModifiesData\r
+   )\r
+   : CTransformFilter(pName, pUnk, clsid),\r
+     m_bModifiesData(bModifiesData)\r
+{\r
+#ifdef PERF\r
+    RegisterPerfId();\r
+#endif //  PERF\r
+\r
+} // constructor\r
+\r
+#ifdef UNICODE\r
+CTransInPlaceFilter::CTransInPlaceFilter\r
+   ( __in_opt LPCSTR  pName,\r
+     __inout_opt LPUNKNOWN  pUnk,\r
+     REFCLSID   clsid,\r
+     __inout HRESULT   *phr,\r
+     bool       bModifiesData\r
+   )\r
+   : CTransformFilter(pName, pUnk, clsid),\r
+     m_bModifiesData(bModifiesData)\r
+{\r
+#ifdef PERF\r
+    RegisterPerfId();\r
+#endif //  PERF\r
+\r
+} // constructor\r
+#endif\r
+\r
+// return a non-addrefed CBasePin * for the user to addref if he holds onto it\r
+// for longer than his pointer to us. We create the pins dynamically when they\r
+// are asked for rather than in the constructor. This is because we want to\r
+// give the derived class an oppportunity to return different pin objects\r
+\r
+// As soon as any pin is needed we create both (this is different from the\r
+// usual transform filter) because enumerators, allocators etc are passed\r
+// through from one pin to another and it becomes very painful if the other\r
+// pin isn't there.  If we fail to create either pin we ensure we fail both.\r
+\r
+CBasePin *\r
+CTransInPlaceFilter::GetPin(int n)\r
+{\r
+    HRESULT hr = S_OK;\r
+\r
+    // Create an input pin if not already done\r
+\r
+    if (m_pInput == NULL) {\r
+\r
+        m_pInput = new CTransInPlaceInputPin( NAME("TransInPlace input pin")\r
+                                            , this        // Owner filter\r
+                                            , &hr         // Result code\r
+                                            , L"Input"    // Pin name\r
+                                            );\r
+\r
+        // Constructor for CTransInPlaceInputPin can't fail\r
+        ASSERT(SUCCEEDED(hr));\r
+    }\r
+\r
+    // Create an output pin if not already done\r
+\r
+    if (m_pInput!=NULL && m_pOutput == NULL) {\r
+\r
+        m_pOutput = new CTransInPlaceOutputPin( NAME("TransInPlace output pin")\r
+                                              , this       // Owner filter\r
+                                              , &hr        // Result code\r
+                                              , L"Output"  // Pin name\r
+                                              );\r
+\r
+        // a failed return code should delete the object\r
+\r
+        ASSERT(SUCCEEDED(hr));\r
+        if (m_pOutput == NULL) {\r
+            delete m_pInput;\r
+            m_pInput = NULL;\r
+        }\r
+    }\r
+\r
+    // Return the appropriate pin\r
+\r
+    ASSERT (n>=0 && n<=1);\r
+    if (n == 0) {\r
+        return m_pInput;\r
+    } else if (n==1) {\r
+        return m_pOutput;\r
+    } else {\r
+        return NULL;\r
+    }\r
+\r
+} // GetPin\r
+\r
+\r
+\r
+// dir is the direction of our pin.\r
+// pReceivePin is the pin we are connecting to.\r
+HRESULT CTransInPlaceFilter::CompleteConnect(PIN_DIRECTION dir, IPin *pReceivePin)\r
+{\r
+    UNREFERENCED_PARAMETER(pReceivePin);\r
+    ASSERT(m_pInput);\r
+    ASSERT(m_pOutput);\r
+\r
+    // if we are not part of a graph, then don't indirect the pointer\r
+    // this probably prevents use of the filter without a filtergraph\r
+    if (!m_pGraph) {\r
+        return VFW_E_NOT_IN_GRAPH;\r
+    }\r
+\r
+    // Always reconnect the input to account for buffering changes\r
+    //\r
+    // Because we don't get to suggest a type on ReceiveConnection\r
+    // we need another way of making sure the right type gets used.\r
+    //\r
+    // One way would be to have our EnumMediaTypes return our output\r
+    // connection type first but more deterministic and simple is to\r
+    // call ReconnectEx passing the type we want to reconnect with\r
+    // via the base class ReconeectPin method.\r
+\r
+    if (dir == PINDIR_OUTPUT) {\r
+        if( m_pInput->IsConnected() ) {\r
+            return ReconnectPin( m_pInput, &m_pOutput->CurrentMediaType() );\r
+        }\r
+        return NOERROR;\r
+    }\r
+\r
+    ASSERT(dir == PINDIR_INPUT);\r
+\r
+    // Reconnect output if necessary\r
+\r
+    if( m_pOutput->IsConnected() ) {\r
+\r
+        if (  m_pInput->CurrentMediaType()\r
+           != m_pOutput->CurrentMediaType()\r
+           ) {\r
+            return ReconnectPin( m_pOutput, &m_pInput->CurrentMediaType() );\r
+        }\r
+    }\r
+    return NOERROR;\r
+\r
+} // ComnpleteConnect\r
+\r
+\r
+//\r
+// DecideBufferSize\r
+//\r
+// Tell the output pin's allocator what size buffers we require.\r
+// *pAlloc will be the allocator our output pin is using.\r
+//\r
+\r
+HRESULT CTransInPlaceFilter::DecideBufferSize\r
+            ( IMemAllocator *pAlloc\r
+            , __inout ALLOCATOR_PROPERTIES *pProperties\r
+            )\r
+{\r
+    ALLOCATOR_PROPERTIES Request, Actual;\r
+    HRESULT hr;\r
+\r
+    // If we are connected upstream, get his views\r
+    if (m_pInput->IsConnected()) {\r
+        // Get the input pin allocator, and get its size and count.\r
+        // we don't care about his alignment and prefix.\r
+\r
+        hr = InputPin()->PeekAllocator()->GetProperties(&Request);\r
+        if (FAILED(hr)) {\r
+            // Input connected but with a secretive allocator - enough!\r
+            return hr;\r
+        }\r
+    } else {\r
+        // Propose one byte\r
+        // If this isn't enough then when the other pin does get connected\r
+        // we can revise it.\r
+        ZeroMemory(&Request, sizeof(Request));\r
+        Request.cBuffers = 1;\r
+        Request.cbBuffer = 1;\r
+    }\r
+\r
+\r
+    DbgLog((LOG_MEMORY,1,TEXT("Setting Allocator Requirements")));\r
+    DbgLog((LOG_MEMORY,1,TEXT("Count %d, Size %d"),\r
+           Request.cBuffers, Request.cbBuffer));\r
+\r
+    // Pass the allocator requirements to our output side\r
+    // but do a little sanity checking first or we'll just hit\r
+    // asserts in the allocator.\r
+\r
+    pProperties->cBuffers = Request.cBuffers;\r
+    pProperties->cbBuffer = Request.cbBuffer;\r
+    pProperties->cbAlign = Request.cbAlign;\r
+    if (pProperties->cBuffers<=0) {pProperties->cBuffers = 1; }\r
+    if (pProperties->cbBuffer<=0) {pProperties->cbBuffer = 1; }\r
+    hr = pAlloc->SetProperties(pProperties, &Actual);\r
+\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    DbgLog((LOG_MEMORY,1,TEXT("Obtained Allocator Requirements")));\r
+    DbgLog((LOG_MEMORY,1,TEXT("Count %d, Size %d, Alignment %d"),\r
+           Actual.cBuffers, Actual.cbBuffer, Actual.cbAlign));\r
+\r
+    // Make sure we got the right alignment and at least the minimum required\r
+\r
+    if (  (Request.cBuffers > Actual.cBuffers)\r
+       || (Request.cbBuffer > Actual.cbBuffer)\r
+       || (Request.cbAlign  > Actual.cbAlign)\r
+       ) {\r
+        return E_FAIL;\r
+    }\r
+    return NOERROR;\r
+\r
+} // DecideBufferSize\r
+\r
+//\r
+// Copy\r
+//\r
+// return a pointer to an identical copy of pSample\r
+__out_opt IMediaSample * CTransInPlaceFilter::Copy(IMediaSample *pSource)\r
+{\r
+    IMediaSample * pDest;\r
+\r
+    HRESULT hr;\r
+    REFERENCE_TIME tStart, tStop;\r
+    const BOOL bTime = S_OK == pSource->GetTime( &tStart, &tStop);\r
+\r
+    // this may block for an indeterminate amount of time\r
+    hr = OutputPin()->PeekAllocator()->GetBuffer(\r
+              &pDest\r
+              , bTime ? &tStart : NULL\r
+              , bTime ? &tStop : NULL\r
+              , m_bSampleSkipped ? AM_GBF_PREVFRAMESKIPPED : 0\r
+              );\r
+\r
+    if (FAILED(hr)) {\r
+        return NULL;\r
+    }\r
+\r
+    ASSERT(pDest);\r
+    IMediaSample2 *pSample2;\r
+    if (SUCCEEDED(pDest->QueryInterface(IID_IMediaSample2, (void **)&pSample2))) {\r
+        HRESULT hrProps = pSample2->SetProperties(\r
+            FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, pbBuffer),\r
+            (PBYTE)m_pInput->SampleProps());\r
+        pSample2->Release();\r
+        if (FAILED(hrProps)) {\r
+            pDest->Release();\r
+            return NULL;\r
+        }\r
+    } else {\r
+        if (bTime) {\r
+            pDest->SetTime(&tStart, &tStop);\r
+        }\r
+\r
+        if (S_OK == pSource->IsSyncPoint()) {\r
+            pDest->SetSyncPoint(TRUE);\r
+        }\r
+        if (S_OK == pSource->IsDiscontinuity() || m_bSampleSkipped) {\r
+            pDest->SetDiscontinuity(TRUE);\r
+        }\r
+        if (S_OK == pSource->IsPreroll()) {\r
+            pDest->SetPreroll(TRUE);\r
+        }\r
+\r
+        // Copy the media type\r
+        AM_MEDIA_TYPE *pMediaType;\r
+        if (S_OK == pSource->GetMediaType(&pMediaType)) {\r
+            pDest->SetMediaType(pMediaType);\r
+            DeleteMediaType( pMediaType );\r
+        }\r
+\r
+    }\r
+\r
+    m_bSampleSkipped = FALSE;\r
+\r
+    // Copy the sample media times\r
+    REFERENCE_TIME TimeStart, TimeEnd;\r
+    if (pSource->GetMediaTime(&TimeStart,&TimeEnd) == NOERROR) {\r
+        pDest->SetMediaTime(&TimeStart,&TimeEnd);\r
+    }\r
+\r
+    // Copy the actual data length and the actual data.\r
+    {\r
+        const long lDataLength = pSource->GetActualDataLength();\r
+        if (FAILED(pDest->SetActualDataLength(lDataLength))) {\r
+            pDest->Release();\r
+            return NULL;\r
+        }\r
+\r
+        // Copy the sample data\r
+        {\r
+            BYTE *pSourceBuffer, *pDestBuffer;\r
+            long lSourceSize  = pSource->GetSize();\r
+            long lDestSize = pDest->GetSize();\r
+\r
+            ASSERT(lDestSize >= lSourceSize && lDestSize >= lDataLength);\r
+\r
+            if (FAILED(pSource->GetPointer(&pSourceBuffer)) ||\r
+                FAILED(pDest->GetPointer(&pDestBuffer)) ||\r
+                lDestSize < lDataLength ||\r
+                lDataLength < 0) {\r
+                pDest->Release();\r
+                return NULL;\r
+            }\r
+            ASSERT(lDestSize == 0 || pSourceBuffer != NULL && pDestBuffer != NULL);\r
+\r
+            CopyMemory( (PVOID) pDestBuffer, (PVOID) pSourceBuffer, lDataLength );\r
+        }\r
+    }\r
+\r
+    return pDest;\r
+\r
+} // Copy\r
+\r
+\r
+// override this to customize the transform process\r
+\r
+HRESULT\r
+CTransInPlaceFilter::Receive(IMediaSample *pSample)\r
+{\r
+    /*  Check for other streams and pass them on */\r
+    AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps();\r
+    if (pProps->dwStreamId != AM_STREAM_MEDIA) {\r
+        return m_pOutput->Deliver(pSample);\r
+    }\r
+    HRESULT hr;\r
+\r
+    // Start timing the TransInPlace (if PERF is defined)\r
+    MSR_START(m_idTransInPlace);\r
+\r
+    if (UsingDifferentAllocators()) {\r
+\r
+        // We have to copy the data.\r
+\r
+        pSample = Copy(pSample);\r
+\r
+        if (pSample==NULL) {\r
+            MSR_STOP(m_idTransInPlace);\r
+            return E_UNEXPECTED;\r
+        }\r
+    }\r
+\r
+    // have the derived class transform the data\r
+    hr = Transform(pSample);\r
+\r
+    // Stop the clock and log it (if PERF is defined)\r
+    MSR_STOP(m_idTransInPlace);\r
+\r
+    if (FAILED(hr)) {\r
+        DbgLog((LOG_TRACE, 1, TEXT("Error from TransInPlace")));\r
+        if (UsingDifferentAllocators()) {\r
+            pSample->Release();\r
+        }\r
+        return hr;\r
+    }\r
+\r
+    // the Transform() function can return S_FALSE to indicate that the\r
+    // sample should not be delivered; we only deliver the sample if it's\r
+    // really S_OK (same as NOERROR, of course.)\r
+    if (hr == NOERROR) {\r
+        hr = m_pOutput->Deliver(pSample);\r
+    } else {\r
+        //  But it would be an error to return this private workaround\r
+        //  to the caller ...\r
+        if (S_FALSE == hr) {\r
+            // S_FALSE returned from Transform is a PRIVATE agreement\r
+            // We should return NOERROR from Receive() in this cause because\r
+            // returning S_FALSE from Receive() means that this is the end\r
+            // of the stream and no more data should be sent.\r
+            m_bSampleSkipped = TRUE;\r
+            if (!m_bQualityChanged) {\r
+                NotifyEvent(EC_QUALITY_CHANGE,0,0);\r
+                m_bQualityChanged = TRUE;\r
+            }\r
+            hr = NOERROR;\r
+        }\r
+    }\r
+\r
+    // release the output buffer. If the connected pin still needs it,\r
+    // it will have addrefed it itself.\r
+    if (UsingDifferentAllocators()) {\r
+        pSample->Release();\r
+    }\r
+\r
+    return hr;\r
+\r
+} // Receive\r
+\r
+\r
+\r
+// =================================================================\r
+// Implements the CTransInPlaceInputPin class\r
+// =================================================================\r
+\r
+\r
+// constructor\r
+\r
+CTransInPlaceInputPin::CTransInPlaceInputPin\r
+    ( __in_opt LPCTSTR             pObjectName\r
+    , __inout CTransInPlaceFilter *pFilter\r
+    , __inout HRESULT             *phr\r
+    , __in_opt LPCWSTR             pName\r
+    )\r
+    : CTransformInputPin(pObjectName,\r
+                         pFilter,\r
+                         phr,\r
+                         pName)\r
+    , m_bReadOnly(FALSE)\r
+    , m_pTIPFilter(pFilter)\r
+{\r
+    DbgLog((LOG_TRACE, 2\r
+           , TEXT("CTransInPlaceInputPin::CTransInPlaceInputPin")));\r
+\r
+} // constructor\r
+\r
+\r
+// =================================================================\r
+// Implements IMemInputPin interface\r
+// =================================================================\r
+\r
+\r
+// If the downstream filter has one then offer that (even if our own output\r
+// pin is not using it yet.  If the upstream filter chooses it then we will\r
+// tell our output pin to ReceiveAllocator).\r
+// Else if our output pin is using an allocator then offer that.\r
+//     ( This could mean offering the upstream filter his own allocator,\r
+//       it could mean offerring our own\r
+//     ) or it could mean offering the one from downstream\r
+// Else fail to offer any allocator at all.\r
+\r
+STDMETHODIMP CTransInPlaceInputPin::GetAllocator(__deref_out IMemAllocator ** ppAllocator)\r
+{\r
+    CheckPointer(ppAllocator,E_POINTER);\r
+    ValidateReadWritePtr(ppAllocator,sizeof(IMemAllocator *));\r
+    CAutoLock cObjectLock(m_pLock);\r
+\r
+    HRESULT hr;\r
+\r
+    if ( m_pTIPFilter->m_pOutput->IsConnected() ) {\r
+        //  Store the allocator we got\r
+        hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()\r
+                                        ->GetAllocator( ppAllocator );\r
+        if (SUCCEEDED(hr)) {\r
+            m_pTIPFilter->OutputPin()->SetAllocator( *ppAllocator );\r
+        }\r
+    }\r
+    else {\r
+        //  Help upstream filter (eg TIP filter which is having to do a copy)\r
+        //  by providing a temp allocator here - we'll never use\r
+        //  this allocator because when our output is connected we'll\r
+        //  reconnect this pin\r
+        hr = CTransformInputPin::GetAllocator( ppAllocator );\r
+    }\r
+    return hr;\r
+\r
+} // GetAllocator\r
+\r
+\r
+\r
+/* Get told which allocator the upstream output pin is actually going to use */\r
+\r
+\r
+STDMETHODIMP\r
+CTransInPlaceInputPin::NotifyAllocator(\r
+    IMemAllocator * pAllocator,\r
+    BOOL bReadOnly)\r
+{\r
+    HRESULT hr = S_OK;\r
+    CheckPointer(pAllocator,E_POINTER);\r
+    ValidateReadPtr(pAllocator,sizeof(IMemAllocator));\r
+\r
+    CAutoLock cObjectLock(m_pLock);\r
+\r
+    m_bReadOnly = bReadOnly;\r
+    //  If we modify data then don't accept the allocator if it's\r
+    //  the same as the output pin's allocator\r
+\r
+    //  If our output is not connected just accept the allocator\r
+    //  We're never going to use this allocator because when our\r
+    //  output pin is connected we'll reconnect this pin\r
+    if (!m_pTIPFilter->OutputPin()->IsConnected()) {\r
+        return CTransformInputPin::NotifyAllocator(pAllocator, bReadOnly);\r
+    }\r
+\r
+    //  If the allocator is read-only and we're modifying data\r
+    //  and the allocator is the same as the output pin's\r
+    //  then reject\r
+    if (bReadOnly && m_pTIPFilter->m_bModifiesData) {\r
+        IMemAllocator *pOutputAllocator =\r
+            m_pTIPFilter->OutputPin()->PeekAllocator();\r
+\r
+        //  Make sure we have an output allocator\r
+        if (pOutputAllocator == NULL) {\r
+            hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()->\r
+                                      GetAllocator(&pOutputAllocator);\r
+            if(FAILED(hr)) {\r
+                hr = CreateMemoryAllocator(&pOutputAllocator);\r
+            }\r
+            if (SUCCEEDED(hr)) {\r
+                m_pTIPFilter->OutputPin()->SetAllocator(pOutputAllocator);\r
+                pOutputAllocator->Release();\r
+            }\r
+        }\r
+        if (pAllocator == pOutputAllocator) {\r
+            hr = E_FAIL;\r
+        } else if(SUCCEEDED(hr)) {\r
+            //  Must copy so set the allocator properties on the output\r
+            ALLOCATOR_PROPERTIES Props, Actual;\r
+            hr = pAllocator->GetProperties(&Props);\r
+            if (SUCCEEDED(hr)) {\r
+                hr = pOutputAllocator->SetProperties(&Props, &Actual);\r
+            }\r
+            if (SUCCEEDED(hr)) {\r
+                if (  (Props.cBuffers > Actual.cBuffers)\r
+                   || (Props.cbBuffer > Actual.cbBuffer)\r
+                   || (Props.cbAlign  > Actual.cbAlign)\r
+                   ) {\r
+                    hr =  E_FAIL;\r
+                }\r
+            }\r
+\r
+            //  Set the allocator on the output pin\r
+            if (SUCCEEDED(hr)) {\r
+                hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()\r
+                                       ->NotifyAllocator( pOutputAllocator, FALSE );\r
+            }\r
+        }\r
+    } else {\r
+        hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()\r
+                                   ->NotifyAllocator( pAllocator, bReadOnly );\r
+        if (SUCCEEDED(hr)) {\r
+            m_pTIPFilter->OutputPin()->SetAllocator( pAllocator );\r
+        }\r
+    }\r
+\r
+    if (SUCCEEDED(hr)) {\r
+\r
+        // It's possible that the old and the new are the same thing.\r
+        // AddRef before release ensures that we don't unload it.\r
+        pAllocator->AddRef();\r
+\r
+        if( m_pAllocator != NULL )\r
+            m_pAllocator->Release();\r
+\r
+        m_pAllocator = pAllocator;    // We have an allocator for the input pin\r
+    }\r
+\r
+    return hr;\r
+\r
+} // NotifyAllocator\r
+\r
+\r
+// EnumMediaTypes\r
+// - pass through to our downstream filter\r
+STDMETHODIMP CTransInPlaceInputPin::EnumMediaTypes( __deref_out IEnumMediaTypes **ppEnum )\r
+{\r
+    // Can only pass through if connected\r
+    if( !m_pTIPFilter->m_pOutput->IsConnected() )\r
+        return VFW_E_NOT_CONNECTED;\r
+\r
+    return m_pTIPFilter->m_pOutput->GetConnected()->EnumMediaTypes( ppEnum );\r
+\r
+} // EnumMediaTypes\r
+\r
+\r
+// CheckMediaType\r
+// - agree to anything if not connected,\r
+// otherwise pass through to the downstream filter.\r
+// This assumes that the filter does not change the media type.\r
+\r
+HRESULT CTransInPlaceInputPin::CheckMediaType(const CMediaType *pmt )\r
+{\r
+    HRESULT hr = m_pTIPFilter->CheckInputType(pmt);\r
+    if (hr!=S_OK) return hr;\r
+\r
+    if( m_pTIPFilter->m_pOutput->IsConnected() )\r
+        return m_pTIPFilter->m_pOutput->GetConnected()->QueryAccept( pmt );\r
+    else\r
+        return S_OK;\r
+\r
+} // CheckMediaType\r
+\r
+\r
+// If upstream asks us what our requirements are, we will try to ask downstream\r
+// if that doesn't work, we'll just take the defaults.\r
+STDMETHODIMP\r
+CTransInPlaceInputPin::GetAllocatorRequirements(__out ALLOCATOR_PROPERTIES *pProps)\r
+{\r
+\r
+    if( m_pTIPFilter->m_pOutput->IsConnected() )\r
+        return m_pTIPFilter->OutputPin()\r
+               ->ConnectedIMemInputPin()->GetAllocatorRequirements( pProps );\r
+    else\r
+        return E_NOTIMPL;\r
+\r
+} // GetAllocatorRequirements\r
+\r
+\r
+// CTransInPlaceInputPin::CompleteConnect() calls CBaseInputPin::CompleteConnect()\r
+// and then calls CTransInPlaceFilter::CompleteConnect().  It does this because \r
+// CTransInPlaceFilter::CompleteConnect() can reconnect a pin and we do not\r
+// want to reconnect a pin if CBaseInputPin::CompleteConnect() fails.\r
+HRESULT\r
+CTransInPlaceInputPin::CompleteConnect(IPin *pReceivePin)\r
+{\r
+    HRESULT hr = CBaseInputPin::CompleteConnect(pReceivePin);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    return m_pTransformFilter->CompleteConnect(PINDIR_INPUT,pReceivePin);\r
+} // CompleteConnect\r
+\r
+\r
+// =================================================================\r
+// Implements the CTransInPlaceOutputPin class\r
+// =================================================================\r
+\r
+\r
+// constructor\r
+\r
+CTransInPlaceOutputPin::CTransInPlaceOutputPin(\r
+    __in_opt LPCTSTR pObjectName,\r
+    __inout CTransInPlaceFilter *pFilter,\r
+    __inout HRESULT * phr,\r
+    __in_opt LPCWSTR pPinName)\r
+    : CTransformOutputPin( pObjectName\r
+                         , pFilter\r
+                         , phr\r
+                         , pPinName),\r
+      m_pTIPFilter(pFilter)\r
+{\r
+    DbgLog(( LOG_TRACE, 2\r
+           , TEXT("CTransInPlaceOutputPin::CTransInPlaceOutputPin")));\r
+\r
+} // constructor\r
+\r
+\r
+// EnumMediaTypes\r
+// - pass through to our upstream filter\r
+STDMETHODIMP CTransInPlaceOutputPin::EnumMediaTypes( __deref_out IEnumMediaTypes **ppEnum )\r
+{\r
+    // Can only pass through if connected.\r
+    if( ! m_pTIPFilter->m_pInput->IsConnected() )\r
+        return VFW_E_NOT_CONNECTED;\r
+\r
+    return m_pTIPFilter->m_pInput->GetConnected()->EnumMediaTypes( ppEnum );\r
+\r
+} // EnumMediaTypes\r
+\r
+\r
+\r
+// CheckMediaType\r
+// - agree to anything if not connected,\r
+// otherwise pass through to the upstream filter.\r
+\r
+HRESULT CTransInPlaceOutputPin::CheckMediaType(const CMediaType *pmt )\r
+{\r
+    // Don't accept any output pin type changes if we're copying\r
+    // between allocators - it's too late to change the input\r
+    // allocator size.\r
+    if (m_pTIPFilter->UsingDifferentAllocators() && !m_pFilter->IsStopped()) {\r
+        if (*pmt == m_mt) {\r
+            return S_OK;\r
+        } else {\r
+            return VFW_E_TYPE_NOT_ACCEPTED;\r
+        }\r
+    }\r
+\r
+    // Assumes the type does not change.  That's why we're calling\r
+    // CheckINPUTType here on the OUTPUT pin.\r
+    HRESULT hr = m_pTIPFilter->CheckInputType(pmt);\r
+    if (hr!=S_OK) return hr;\r
+\r
+    if( m_pTIPFilter->m_pInput->IsConnected() )\r
+        return m_pTIPFilter->m_pInput->GetConnected()->QueryAccept( pmt );\r
+    else\r
+        return S_OK;\r
+\r
+} // CheckMediaType\r
+\r
+\r
+/* Save the allocator pointer in the output pin\r
+*/\r
+void\r
+CTransInPlaceOutputPin::SetAllocator(IMemAllocator * pAllocator)\r
+{\r
+    pAllocator->AddRef();\r
+    if (m_pAllocator) {\r
+        m_pAllocator->Release();\r
+    }\r
+    m_pAllocator = pAllocator;\r
+} // SetAllocator\r
+\r
+\r
+// CTransInPlaceOutputPin::CompleteConnect() calls CBaseOutputPin::CompleteConnect()\r
+// and then calls CTransInPlaceFilter::CompleteConnect().  It does this because \r
+// CTransInPlaceFilter::CompleteConnect() can reconnect a pin and we do not want to \r
+// reconnect a pin if CBaseOutputPin::CompleteConnect() fails.  \r
+// CBaseOutputPin::CompleteConnect() often fails when our output pin is being connected \r
+// to the Video Mixing Renderer.\r
+HRESULT\r
+CTransInPlaceOutputPin::CompleteConnect(IPin *pReceivePin)\r
+{\r
+    HRESULT hr = CBaseOutputPin::CompleteConnect(pReceivePin);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    return m_pTransformFilter->CompleteConnect(PINDIR_OUTPUT,pReceivePin);\r
+} // CompleteConnect\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/transip.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/transip.h
new file mode 100644 (file)
index 0000000..3fc335e
--- /dev/null
@@ -0,0 +1,250 @@
+//------------------------------------------------------------------------------\r
+// File: TransIP.h\r
+//\r
+// Desc: DirectShow base classes - defines classes from which simple\r
+//       Transform-In-Place filters may be derived.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+//\r
+// The difference between this and Transfrm.h is that Transfrm copies the data.\r
+//\r
+// It assumes the filter has one input and one output stream, and has no\r
+// interest in memory management, interface negotiation or anything else.\r
+//\r
+// Derive your class from this, and supply Transform and the media type/format\r
+// negotiation functions. Implement that class, compile and link and\r
+// you're done.\r
+\r
+\r
+#ifndef __TRANSIP__\r
+#define __TRANSIP__\r
+\r
+// ======================================================================\r
+// This is the com object that represents a simple transform filter. It\r
+// supports IBaseFilter, IMediaFilter and two pins through nested interfaces\r
+// ======================================================================\r
+\r
+class CTransInPlaceFilter;\r
+\r
+// Several of the pin functions call filter functions to do the work,\r
+// so you can often use the pin classes unaltered, just overriding the\r
+// functions in CTransInPlaceFilter.  If that's not enough and you want\r
+// to derive your own pin class, override GetPin in the filter to supply\r
+// your own pin classes to the filter.\r
+\r
+// ==================================================\r
+// Implements the input pin\r
+// ==================================================\r
+\r
+class CTransInPlaceInputPin : public CTransformInputPin\r
+{\r
+\r
+protected:\r
+    CTransInPlaceFilter * const m_pTIPFilter;    // our filter\r
+    BOOL                 m_bReadOnly;    // incoming stream is read only\r
+\r
+public:\r
+\r
+    CTransInPlaceInputPin(\r
+        __in_opt LPCTSTR     pObjectName,\r
+        __inout CTransInPlaceFilter *pFilter,\r
+        __inout HRESULT             *phr,\r
+        __in_opt LPCWSTR              pName);\r
+\r
+    // --- IMemInputPin -----\r
+\r
+    // Provide an enumerator for media types by getting one from downstream\r
+    STDMETHODIMP EnumMediaTypes( __deref_out IEnumMediaTypes **ppEnum );\r
+\r
+    // Say whether media type is acceptable.\r
+    HRESULT CheckMediaType(const CMediaType* pmt);\r
+\r
+    // Return our upstream allocator\r
+    STDMETHODIMP GetAllocator(__deref_out IMemAllocator ** ppAllocator);\r
+\r
+    // get told which allocator the upstream output pin is actually\r
+    // going to use.\r
+    STDMETHODIMP NotifyAllocator(IMemAllocator * pAllocator,\r
+                                 BOOL bReadOnly);\r
+\r
+    // Allow the filter to see what allocator we have\r
+    // N.B. This does NOT AddRef\r
+    __out IMemAllocator * PeekAllocator() const\r
+        {  return m_pAllocator; }\r
+\r
+    // Pass this on downstream if it ever gets called.\r
+    STDMETHODIMP GetAllocatorRequirements(__out ALLOCATOR_PROPERTIES *pProps);\r
+\r
+    HRESULT CompleteConnect(IPin *pReceivePin);\r
+\r
+    inline const BOOL ReadOnly() { return m_bReadOnly ; }\r
+\r
+};  // CTransInPlaceInputPin\r
+\r
+// ==================================================\r
+// Implements the output pin\r
+// ==================================================\r
+\r
+class CTransInPlaceOutputPin : public CTransformOutputPin\r
+{\r
+\r
+protected:\r
+    // m_pFilter points to our CBaseFilter\r
+    CTransInPlaceFilter * const m_pTIPFilter;\r
+\r
+public:\r
+\r
+    CTransInPlaceOutputPin(\r
+        __in_opt LPCTSTR     pObjectName,\r
+        __inout CTransInPlaceFilter *pFilter,\r
+        __inout HRESULT             *phr,\r
+        __in_opt LPCWSTR              pName);\r
+\r
+\r
+    // --- CBaseOutputPin ------------\r
+\r
+    // negotiate the allocator and its buffer size/count\r
+    // Insists on using our own allocator.  (Actually the one upstream of us).\r
+    // We don't override this - instead we just agree the default\r
+    // then let the upstream filter decide for itself on reconnect\r
+    // virtual HRESULT DecideAllocator(IMemInputPin * pPin, IMemAllocator ** pAlloc);\r
+\r
+    // Provide a media type enumerator.  Get it from upstream.\r
+    STDMETHODIMP EnumMediaTypes( __deref_out IEnumMediaTypes **ppEnum );\r
+\r
+    // Say whether media type is acceptable.\r
+    HRESULT CheckMediaType(const CMediaType* pmt);\r
+\r
+    //  This just saves the allocator being used on the output pin\r
+    //  Also called by input pin's GetAllocator()\r
+    void SetAllocator(IMemAllocator * pAllocator);\r
+\r
+    __out_opt IMemInputPin * ConnectedIMemInputPin()\r
+        { return m_pInputPin; }\r
+\r
+    // Allow the filter to see what allocator we have\r
+    // N.B. This does NOT AddRef\r
+    __out IMemAllocator * PeekAllocator() const\r
+        {  return m_pAllocator; }\r
+\r
+    HRESULT CompleteConnect(IPin *pReceivePin);\r
+\r
+};  // CTransInPlaceOutputPin\r
+\r
+\r
+class AM_NOVTABLE CTransInPlaceFilter : public CTransformFilter\r
+{\r
+\r
+public:\r
+\r
+    // map getpin/getpincount for base enum of pins to owner\r
+    // override this to return more specialised pin objects\r
+\r
+    virtual CBasePin *GetPin(int n);\r
+\r
+public:\r
+\r
+    //  Set bModifiesData == false if your derived filter does\r
+    //  not modify the data samples (for instance it's just copying\r
+    //  them somewhere else or looking at the timestamps).\r
+\r
+    CTransInPlaceFilter(__in_opt LPCTSTR, __inout_opt LPUNKNOWN, REFCLSID clsid, __inout HRESULT *,\r
+                        bool bModifiesData = true);\r
+#ifdef UNICODE\r
+    CTransInPlaceFilter(__in_opt LPCSTR, __inout_opt LPUNKNOWN, REFCLSID clsid, __inout HRESULT *,\r
+                        bool bModifiesData = true);\r
+#endif\r
+    // The following are defined to avoid undefined pure virtuals.\r
+    // Even if they are never called, they will give linkage warnings/errors\r
+\r
+    // We override EnumMediaTypes to bypass the transform class enumerator\r
+    // which would otherwise call this.\r
+    HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType)\r
+        {   DbgBreak("CTransInPlaceFilter::GetMediaType should never be called");\r
+            return E_UNEXPECTED;\r
+        }\r
+\r
+    // This is called when we actually have to provide our own allocator.\r
+    HRESULT DecideBufferSize(IMemAllocator*, __inout ALLOCATOR_PROPERTIES *);\r
+\r
+    // The functions which call this in CTransform are overridden in this\r
+    // class to call CheckInputType with the assumption that the type\r
+    // does not change.  In Debug builds some calls will be made and\r
+    // we just ensure that they do not assert.\r
+    HRESULT CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut)\r
+    {\r
+        return S_OK;\r
+    };\r
+\r
+\r
+    // =================================================================\r
+    // ----- You may want to override this -----------------------------\r
+    // =================================================================\r
+\r
+    HRESULT CompleteConnect(PIN_DIRECTION dir,IPin *pReceivePin);\r
+\r
+    // chance to customize the transform process\r
+    virtual HRESULT Receive(IMediaSample *pSample);\r
+\r
+    // =================================================================\r
+    // ----- You MUST override these -----------------------------------\r
+    // =================================================================\r
+\r
+    virtual HRESULT Transform(IMediaSample *pSample) PURE;\r
+\r
+    // this goes in the factory template table to create new instances\r
+    // static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *);\r
+\r
+\r
+#ifdef PERF\r
+    // Override to register performance measurement with a less generic string\r
+    // You should do this to avoid confusion with other filters\r
+    virtual void RegisterPerfId()\r
+         {m_idTransInPlace = MSR_REGISTER(TEXT("TransInPlace"));}\r
+#endif // PERF\r
+\r
+\r
+// implementation details\r
+\r
+protected:\r
+\r
+    __out_opt IMediaSample * CTransInPlaceFilter::Copy(IMediaSample *pSource);\r
+\r
+#ifdef PERF\r
+    int m_idTransInPlace;                 // performance measuring id\r
+#endif // PERF\r
+    bool  m_bModifiesData;                // Does this filter change the data?\r
+\r
+    // these hold our input and output pins\r
+\r
+    friend class CTransInPlaceInputPin;\r
+    friend class CTransInPlaceOutputPin;\r
+\r
+    __out CTransInPlaceInputPin  *InputPin() const\r
+    {\r
+        return (CTransInPlaceInputPin *)m_pInput;\r
+    };\r
+    __out CTransInPlaceOutputPin *OutputPin() const\r
+    {\r
+        return (CTransInPlaceOutputPin *)m_pOutput;\r
+    };\r
+\r
+    //  Helper to see if the input and output types match\r
+    BOOL TypesMatch()\r
+    {\r
+        return InputPin()->CurrentMediaType() ==\r
+               OutputPin()->CurrentMediaType();\r
+    }\r
+\r
+    //  Are the input and output allocators different?\r
+    BOOL UsingDifferentAllocators() const\r
+    {\r
+        return InputPin()->PeekAllocator() != OutputPin()->PeekAllocator();\r
+    }\r
+}; // CTransInPlaceFilter\r
+\r
+#endif /* __TRANSIP__ */\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/videoctl.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/videoctl.cpp
new file mode 100644 (file)
index 0000000..b12ccbd
--- /dev/null
@@ -0,0 +1,746 @@
+//------------------------------------------------------------------------------\r
+// File: VideoCtl.cpp\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+#include "ddmm.h"\r
+\r
+// Load a string from the resource file string table. The buffer must be at\r
+// least STR_MAX_LENGTH bytes. The easiest way to use this is to declare a\r
+// buffer in the property page class and use it for all string loading. It\r
+// cannot be static as multiple property pages may be active simultaneously\r
+\r
+LPTSTR WINAPI StringFromResource(__out_ecount(STR_MAX_LENGTH) LPTSTR pBuffer, int iResourceID)\r
+{\r
+    if (LoadString(g_hInst,iResourceID,pBuffer,STR_MAX_LENGTH) == 0) {\r
+        return TEXT("");\r
+    }\r
+    return pBuffer;\r
+}\r
+\r
+#ifdef UNICODE\r
+LPSTR WINAPI StringFromResource(__out_ecount(STR_MAX_LENGTH) LPSTR pBuffer, int iResourceID)\r
+{\r
+    if (LoadStringA(g_hInst,iResourceID,pBuffer,STR_MAX_LENGTH) == 0) {\r
+        return "";\r
+    }\r
+    return pBuffer;\r
+}\r
+#endif\r
+\r
+\r
+\r
+// Property pages typically are called through their OLE interfaces. These\r
+// use UNICODE strings regardless of how the binary is built. So when we\r
+// load strings from the resource file we sometimes want to convert them\r
+// to UNICODE. This method is passed the target UNICODE buffer and does a\r
+// convert after loading the string (if built UNICODE this is not needed)\r
+// On WinNT we can explicitly call LoadStringW which saves two conversions\r
+\r
+#ifndef UNICODE\r
+\r
+LPWSTR WINAPI WideStringFromResource(__out_ecount(STR_MAX_LENGTH) LPWSTR pBuffer, int iResourceID)\r
+{\r
+    *pBuffer = 0;\r
+\r
+    if (g_amPlatform == VER_PLATFORM_WIN32_NT) {\r
+       LoadStringW(g_hInst,iResourceID,pBuffer,STR_MAX_LENGTH);\r
+    } else {\r
+\r
+       CHAR szBuffer[STR_MAX_LENGTH];\r
+       DWORD dwStringLength = LoadString(g_hInst,iResourceID,szBuffer,STR_MAX_LENGTH);\r
+       // if we loaded a string convert it to wide characters, ensuring\r
+       // that we also null terminate the result.\r
+       if (dwStringLength++) {\r
+           MultiByteToWideChar(CP_ACP,0,szBuffer,dwStringLength,pBuffer,STR_MAX_LENGTH);\r
+       }\r
+    }\r
+    return pBuffer;\r
+}\r
+\r
+#endif\r
+\r
+\r
+// Helper function to calculate the size of the dialog\r
+\r
+BOOL WINAPI GetDialogSize(int iResourceID,\r
+                          DLGPROC pDlgProc,\r
+                          LPARAM lParam,\r
+                          __out SIZE *pResult)\r
+{\r
+    RECT rc;\r
+    HWND hwnd;\r
+\r
+    // Create a temporary property page\r
+\r
+    hwnd = CreateDialogParam(g_hInst,\r
+                             MAKEINTRESOURCE(iResourceID),\r
+                             GetDesktopWindow(),\r
+                             pDlgProc,\r
+                             lParam);\r
+    if (hwnd == NULL) {\r
+        return FALSE;\r
+    }\r
+\r
+    GetWindowRect(hwnd, &rc);\r
+    pResult->cx = rc.right - rc.left;\r
+    pResult->cy = rc.bottom - rc.top;\r
+\r
+    DestroyWindow(hwnd);\r
+    return TRUE;\r
+}\r
+\r
+\r
+// Class that aggregates on the IDirectDraw interface. Although DirectDraw\r
+// has the ability in its interfaces to be aggregated they're not currently\r
+// implemented. This makes it difficult for various parts of Quartz that want\r
+// to aggregate these interfaces. In particular the video renderer passes out\r
+// media samples that expose IDirectDraw and IDirectDrawSurface. The filter\r
+// graph manager also exposes IDirectDraw as a plug in distributor. For these\r
+// objects we provide these aggregation classes that republish the interfaces\r
+\r
+STDMETHODIMP CAggDirectDraw::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+\r
+    // Do we have this interface\r
+\r
+    if (riid == IID_IDirectDraw) {\r
+        return GetInterface((IDirectDraw *)this,ppv);\r
+    } else {\r
+        return CUnknown::NonDelegatingQueryInterface(riid,ppv);\r
+    }\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::Compact()\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->Compact();\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::CreateClipper(DWORD dwFlags, __deref_out LPDIRECTDRAWCLIPPER *lplpDDClipper, __inout_opt IUnknown *pUnkOuter)\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->CreateClipper(dwFlags,lplpDDClipper,pUnkOuter);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::CreatePalette(DWORD dwFlags,\r
+                                           __in LPPALETTEENTRY lpColorTable,\r
+                                           __deref_out LPDIRECTDRAWPALETTE *lplpDDPalette,\r
+                                           __inout_opt IUnknown *pUnkOuter)\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->CreatePalette(dwFlags,lpColorTable,lplpDDPalette,pUnkOuter);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::CreateSurface(__in LPDDSURFACEDESC lpDDSurfaceDesc,\r
+                                           __deref_out LPDIRECTDRAWSURFACE *lplpDDSurface,\r
+                                           __inout_opt IUnknown *pUnkOuter)\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->CreateSurface(lpDDSurfaceDesc,lplpDDSurface,pUnkOuter);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::DuplicateSurface(__in LPDIRECTDRAWSURFACE lpDDSurface,\r
+                                              __deref_out LPDIRECTDRAWSURFACE *lplpDupDDSurface)\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->DuplicateSurface(lpDDSurface,lplpDupDDSurface);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::EnumDisplayModes(DWORD dwSurfaceDescCount,\r
+                                              __in LPDDSURFACEDESC lplpDDSurfaceDescList,\r
+                                              __in LPVOID lpContext,\r
+                                              __in LPDDENUMMODESCALLBACK lpEnumCallback)\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->EnumDisplayModes(dwSurfaceDescCount,lplpDDSurfaceDescList,lpContext,lpEnumCallback);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::EnumSurfaces(DWORD dwFlags,\r
+                                          __in LPDDSURFACEDESC lpDDSD,\r
+                                          __in LPVOID lpContext,\r
+                                          __in LPDDENUMSURFACESCALLBACK lpEnumCallback)\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->EnumSurfaces(dwFlags,lpDDSD,lpContext,lpEnumCallback);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::FlipToGDISurface()\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->FlipToGDISurface();\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::GetCaps(__out LPDDCAPS lpDDDriverCaps,__out LPDDCAPS lpDDHELCaps)\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->GetCaps(lpDDDriverCaps,lpDDHELCaps);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::GetDisplayMode(__out LPDDSURFACEDESC lpDDSurfaceDesc)\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->GetDisplayMode(lpDDSurfaceDesc);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::GetFourCCCodes(__inout LPDWORD lpNumCodes,__out_ecount(*lpNumCodes) LPDWORD lpCodes)\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->GetFourCCCodes(lpNumCodes,lpCodes);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::GetGDISurface(__deref_out LPDIRECTDRAWSURFACE *lplpGDIDDSurface)\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->GetGDISurface(lplpGDIDDSurface);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::GetMonitorFrequency(__out LPDWORD lpdwFrequency)\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->GetMonitorFrequency(lpdwFrequency);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::GetScanLine(__out LPDWORD lpdwScanLine)\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->GetScanLine(lpdwScanLine);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::GetVerticalBlankStatus(__out LPBOOL lpblsInVB)\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->GetVerticalBlankStatus(lpblsInVB);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::Initialize(__in GUID *lpGUID)\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->Initialize(lpGUID);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::RestoreDisplayMode()\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->RestoreDisplayMode();\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::SetCooperativeLevel(HWND hWnd,DWORD dwFlags)\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->SetCooperativeLevel(hWnd,dwFlags);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::SetDisplayMode(DWORD dwWidth,DWORD dwHeight,DWORD dwBpp)\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->SetDisplayMode(dwWidth,dwHeight,dwBpp);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDirectDraw::WaitForVerticalBlank(DWORD dwFlags,HANDLE hEvent)\r
+{\r
+    ASSERT(m_pDirectDraw);\r
+    return m_pDirectDraw->WaitForVerticalBlank(dwFlags,hEvent);\r
+}\r
+\r
+\r
+// Class that aggregates an IDirectDrawSurface interface. Although DirectDraw\r
+// has the ability in its interfaces to be aggregated they're not currently\r
+// implemented. This makes it difficult for various parts of Quartz that want\r
+// to aggregate these interfaces. In particular the video renderer passes out\r
+// media samples that expose IDirectDraw and IDirectDrawSurface. The filter\r
+// graph manager also exposes IDirectDraw as a plug in distributor. For these\r
+// objects we provide these aggregation classes that republish the interfaces\r
+\r
+STDMETHODIMP CAggDrawSurface::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+\r
+    // Do we have this interface\r
+\r
+    if (riid == IID_IDirectDrawSurface) {\r
+        return GetInterface((IDirectDrawSurface *)this,ppv);\r
+    } else {\r
+        return CUnknown::NonDelegatingQueryInterface(riid,ppv);\r
+    }\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::AddAttachedSurface(__in LPDIRECTDRAWSURFACE lpDDSAttachedSurface)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->AddAttachedSurface(lpDDSAttachedSurface);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::AddOverlayDirtyRect(__in LPRECT lpRect)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->AddOverlayDirtyRect(lpRect);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::Blt(__in LPRECT lpDestRect,\r
+                                  __in LPDIRECTDRAWSURFACE lpDDSrcSurface,\r
+                                  __in LPRECT lpSrcRect,\r
+                                  DWORD dwFlags,\r
+                                  __in LPDDBLTFX lpDDBltFx)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->Blt(lpDestRect,lpDDSrcSurface,lpSrcRect,dwFlags,lpDDBltFx);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::BltBatch(__in_ecount(dwCount) LPDDBLTBATCH lpDDBltBatch,DWORD dwCount,DWORD dwFlags)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->BltBatch(lpDDBltBatch,dwCount,dwFlags);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::BltFast(DWORD dwX,DWORD dwY,\r
+                                      __in LPDIRECTDRAWSURFACE lpDDSrcSurface,\r
+                                      __in LPRECT lpSrcRect,\r
+                                      DWORD dwTrans)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->BltFast(dwX,dwY,lpDDSrcSurface,lpSrcRect,dwTrans);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::DeleteAttachedSurface(DWORD dwFlags,\r
+                                                    __in LPDIRECTDRAWSURFACE lpDDSAttachedSurface)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->DeleteAttachedSurface(dwFlags,lpDDSAttachedSurface);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::EnumAttachedSurfaces(__in LPVOID lpContext,\r
+                                                   __in LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->EnumAttachedSurfaces(lpContext,lpEnumSurfacesCallback);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::EnumOverlayZOrders(DWORD dwFlags,\r
+                                                 __in LPVOID lpContext,\r
+                                                 __in LPDDENUMSURFACESCALLBACK lpfnCallback)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->EnumOverlayZOrders(dwFlags,lpContext,lpfnCallback);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::Flip(__in LPDIRECTDRAWSURFACE lpDDSurfaceTargetOverride,DWORD dwFlags)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->Flip(lpDDSurfaceTargetOverride,dwFlags);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::GetAttachedSurface(__in LPDDSCAPS lpDDSCaps,\r
+                                                 __deref_out LPDIRECTDRAWSURFACE *lplpDDAttachedSurface)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->GetAttachedSurface(lpDDSCaps,lplpDDAttachedSurface);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::GetBltStatus(DWORD dwFlags)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->GetBltStatus(dwFlags);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::GetCaps(__out LPDDSCAPS lpDDSCaps)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->GetCaps(lpDDSCaps);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::GetClipper(__deref_out LPDIRECTDRAWCLIPPER *lplpDDClipper)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->GetClipper(lplpDDClipper);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::GetColorKey(DWORD dwFlags,__out LPDDCOLORKEY lpDDColorKey)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->GetColorKey(dwFlags,lpDDColorKey);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::GetDC(__out HDC *lphDC)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->GetDC(lphDC);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::GetFlipStatus(DWORD dwFlags)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->GetFlipStatus(dwFlags);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::GetOverlayPosition(__out LPLONG lpdwX,__out LPLONG lpdwY)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->GetOverlayPosition(lpdwX,lpdwY);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::GetPalette(__deref_out LPDIRECTDRAWPALETTE *lplpDDPalette)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->GetPalette(lplpDDPalette);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::GetPixelFormat(__out LPDDPIXELFORMAT lpDDPixelFormat)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->GetPixelFormat(lpDDPixelFormat);\r
+}\r
+\r
+\r
+// A bit of a warning here: Our media samples in DirectShow aggregate on\r
+// IDirectDraw and IDirectDrawSurface (ie are available through IMediaSample\r
+// by QueryInterface). Unfortunately the underlying DirectDraw code cannot\r
+// be aggregated so we have to use these classes. The snag is that when we\r
+// call a different surface and pass in this interface as perhaps the source\r
+// surface the call will fail because DirectDraw dereferences the pointer to\r
+// get at its private data structures. Therefore we supply this workaround to give\r
+// access to the real IDirectDraw surface. A filter can call GetSurfaceDesc\r
+// and we will fill in the lpSurface pointer with the real underlying surface\r
+\r
+STDMETHODIMP CAggDrawSurface::GetSurfaceDesc(__out LPDDSURFACEDESC lpDDSurfaceDesc)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+\r
+    // First call down to the underlying DirectDraw\r
+\r
+    HRESULT hr = m_pDirectDrawSurface->GetSurfaceDesc(lpDDSurfaceDesc);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    // Store the real DirectDrawSurface interface\r
+    lpDDSurfaceDesc->lpSurface = m_pDirectDrawSurface;\r
+    return hr;\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::Initialize(__in LPDIRECTDRAW lpDD,__in LPDDSURFACEDESC lpDDSurfaceDesc)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->Initialize(lpDD,lpDDSurfaceDesc);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::IsLost()\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->IsLost();\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::Lock(__in LPRECT lpDestRect,\r
+                                   __inout LPDDSURFACEDESC lpDDSurfaceDesc,\r
+                                   DWORD dwFlags,\r
+                                   HANDLE hEvent)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->Lock(lpDestRect,lpDDSurfaceDesc,dwFlags,hEvent);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::ReleaseDC(HDC hDC)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->ReleaseDC(hDC);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::Restore()\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->Restore();\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::SetClipper(__in LPDIRECTDRAWCLIPPER lpDDClipper)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->SetClipper(lpDDClipper);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::SetColorKey(DWORD dwFlags,__in LPDDCOLORKEY lpDDColorKey)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->SetColorKey(dwFlags,lpDDColorKey);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::SetOverlayPosition(LONG dwX,LONG dwY)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->SetOverlayPosition(dwX,dwY);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::SetPalette(__in LPDIRECTDRAWPALETTE lpDDPalette)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->SetPalette(lpDDPalette);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::Unlock(__in LPVOID lpSurfaceData)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->Unlock(lpSurfaceData);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::UpdateOverlay(__in LPRECT lpSrcRect,\r
+                                            __in LPDIRECTDRAWSURFACE lpDDDestSurface,\r
+                                            __in LPRECT lpDestRect,\r
+                                            DWORD dwFlags,\r
+                                            __in LPDDOVERLAYFX lpDDOverlayFX)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->UpdateOverlay(lpSrcRect,lpDDDestSurface,lpDestRect,dwFlags,lpDDOverlayFX);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::UpdateOverlayDisplay(DWORD dwFlags)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->UpdateOverlayDisplay(dwFlags);\r
+}\r
+\r
+\r
+STDMETHODIMP CAggDrawSurface::UpdateOverlayZOrder(DWORD dwFlags,__in LPDIRECTDRAWSURFACE lpDDSReference)\r
+{\r
+    ASSERT(m_pDirectDrawSurface);\r
+    return m_pDirectDrawSurface->UpdateOverlayZOrder(dwFlags,lpDDSReference);\r
+}\r
+\r
+\r
+// DirectShow must work on multiple platforms.  In particular, it also runs on\r
+// Windows NT 3.51 which does not have DirectDraw capabilities. The filters\r
+// cannot therefore link statically to the DirectDraw library. To make their\r
+// lives that little bit easier we provide this class that manages loading\r
+// and unloading the library and creating the initial IDirectDraw interface\r
+\r
+CLoadDirectDraw::CLoadDirectDraw() :\r
+    m_pDirectDraw(NULL),\r
+    m_hDirectDraw(NULL)\r
+{\r
+}\r
+\r
+\r
+// Destructor forces unload\r
+\r
+CLoadDirectDraw::~CLoadDirectDraw()\r
+{\r
+    ReleaseDirectDraw();\r
+\r
+    if (m_hDirectDraw) {\r
+        NOTE("Unloading library");\r
+        FreeLibrary(m_hDirectDraw);\r
+    }\r
+}\r
+\r
+\r
+// We can't be sure that DirectDraw is always available so we can't statically\r
+// link to the library. Therefore we load the library, get the function entry\r
+// point addresses and call them to create the driver objects. We return S_OK\r
+// if we manage to load DirectDraw correctly otherwise we return E_NOINTERFACE\r
+// We initialise a DirectDraw instance by explicitely loading the library and\r
+// calling GetProcAddress on the DirectDrawCreate entry point that it exports\r
+\r
+// On a multi monitor system, we can get the DirectDraw object for any\r
+// monitor (device) with the optional szDevice parameter\r
+\r
+HRESULT CLoadDirectDraw::LoadDirectDraw(__in LPSTR szDevice)\r
+{\r
+    PDRAWCREATE pDrawCreate;\r
+    PDRAWENUM pDrawEnum;\r
+    LPDIRECTDRAWENUMERATEEXA pDrawEnumEx;\r
+    HRESULT hr = NOERROR;\r
+\r
+    NOTE("Entering DoLoadDirectDraw");\r
+\r
+    // Is DirectDraw already loaded\r
+\r
+    if (m_pDirectDraw) {\r
+        NOTE("Already loaded");\r
+        ASSERT(m_hDirectDraw);\r
+        return NOERROR;\r
+    }\r
+\r
+    // Make sure the library is available\r
+\r
+    if(!m_hDirectDraw)\r
+    {\r
+        UINT ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX);\r
+        m_hDirectDraw = LoadLibrary(TEXT("DDRAW.DLL"));\r
+        SetErrorMode(ErrorMode);\r
+\r
+        if (m_hDirectDraw == NULL) {\r
+            DbgLog((LOG_ERROR,1,TEXT("Can't load DDRAW.DLL")));\r
+            NOTE("No library");\r
+            return E_NOINTERFACE;\r
+        }\r
+    }\r
+\r
+    // Get the DLL address for the creator function\r
+\r
+    pDrawCreate = (PDRAWCREATE)GetProcAddress(m_hDirectDraw,"DirectDrawCreate");\r
+    // force ANSI, we assume it\r
+    pDrawEnum = (PDRAWENUM)GetProcAddress(m_hDirectDraw,"DirectDrawEnumerateA");\r
+    pDrawEnumEx = (LPDIRECTDRAWENUMERATEEXA)GetProcAddress(m_hDirectDraw,\r
+                                               "DirectDrawEnumerateExA");\r
+\r
+    // We don't NEED DirectDrawEnumerateEx, that's just for multimon stuff\r
+    if (pDrawCreate == NULL || pDrawEnum == NULL) {\r
+        DbgLog((LOG_ERROR,1,TEXT("Can't get functions: Create=%x Enum=%x"),\r
+                       pDrawCreate, pDrawEnum));\r
+        NOTE("No entry point");\r
+        ReleaseDirectDraw();\r
+        return E_NOINTERFACE;\r
+    }\r
+\r
+    DbgLog((LOG_TRACE,3,TEXT("Creating DDraw for device %s"),\r
+                                       szDevice ? szDevice : "<NULL>"));\r
+\r
+    // Create a DirectDraw display provider for this device, using the fancy\r
+    // multimon-aware version, if it exists\r
+    if (pDrawEnumEx)\r
+        m_pDirectDraw = DirectDrawCreateFromDeviceEx(szDevice, pDrawCreate,\r
+                                                               pDrawEnumEx);\r
+    else\r
+        m_pDirectDraw = DirectDrawCreateFromDevice(szDevice, pDrawCreate,\r
+                                                               pDrawEnum);\r
+\r
+    if (m_pDirectDraw == NULL) {\r
+            DbgLog((LOG_ERROR,1,TEXT("Can't create DDraw")));\r
+            NOTE("No instance");\r
+            ReleaseDirectDraw();\r
+            return E_NOINTERFACE;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Called to release any DirectDraw provider we previously loaded. We may be\r
+// called at any time especially when something goes horribly wrong and when\r
+// we need to clean up before returning so we can't guarantee that all state\r
+// variables are consistent so free only those really allocated allocated\r
+// This should only be called once all reference counts have been released\r
+\r
+void CLoadDirectDraw::ReleaseDirectDraw()\r
+{\r
+    NOTE("Releasing DirectDraw driver");\r
+\r
+    // Release any DirectDraw provider interface\r
+\r
+    if (m_pDirectDraw) {\r
+        NOTE("Releasing instance");\r
+        m_pDirectDraw->Release();\r
+        m_pDirectDraw = NULL;\r
+    }\r
+\r
+}\r
+\r
+\r
+// Return NOERROR (S_OK) if DirectDraw has been loaded by this object\r
+\r
+HRESULT CLoadDirectDraw::IsDirectDrawLoaded()\r
+{\r
+    NOTE("Entering IsDirectDrawLoaded");\r
+\r
+    if (m_pDirectDraw == NULL) {\r
+        NOTE("DirectDraw not loaded");\r
+        return S_FALSE;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Return the IDirectDraw interface we look after\r
+\r
+LPDIRECTDRAW CLoadDirectDraw::GetDirectDraw()\r
+{\r
+    NOTE("Entering GetDirectDraw");\r
+\r
+    if (m_pDirectDraw == NULL) {\r
+        NOTE("No DirectDraw");\r
+        return NULL;\r
+    }\r
+\r
+    NOTE("Returning DirectDraw");\r
+    m_pDirectDraw->AddRef();\r
+    return m_pDirectDraw;\r
+}\r
+\r
+\r
+// Are we running on Direct Draw version 1?  We need to find out as\r
+// we rely on specific bug fixes in DirectDraw 2 for fullscreen playback. To\r
+// find out, we simply see if it supports IDirectDraw2.  Only version 2 and\r
+// higher support this.\r
+\r
+BOOL CLoadDirectDraw::IsDirectDrawVersion1()\r
+{\r
+\r
+    if (m_pDirectDraw == NULL)\r
+       return FALSE;\r
+\r
+    IDirectDraw2 *p = NULL;\r
+    HRESULT hr = m_pDirectDraw->QueryInterface(IID_IDirectDraw2, (void **)&p);\r
+    if (p)\r
+       p->Release();\r
+    if (hr == NOERROR) {\r
+        DbgLog((LOG_TRACE,3,TEXT("Direct Draw Version 2 or greater")));\r
+       return FALSE;\r
+    } else {\r
+        DbgLog((LOG_TRACE,3,TEXT("Direct Draw Version 1")));\r
+       return TRUE;\r
+    }\r
+}\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/videoctl.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/videoctl.h
new file mode 100644 (file)
index 0000000..30c3778
--- /dev/null
@@ -0,0 +1,168 @@
+//------------------------------------------------------------------------------\r
+// File: VideoCtl.h\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef __VIDEOCTL__\r
+#define __VIDEOCTL__\r
+\r
+// These help with property page implementations. The first can be used to\r
+// load any string from a resource file. The buffer to load into is passed\r
+// as an input parameter. The same buffer is the return value if the string\r
+// was found otherwise it returns TEXT(""). The GetDialogSize is passed the\r
+// resource ID of a dialog box and returns the size of it in screen pixels\r
+\r
+#define STR_MAX_LENGTH 256\r
+LPTSTR WINAPI StringFromResource(__out_ecount(STR_MAX_LENGTH) LPTSTR pBuffer, int iResourceID);\r
+\r
+#ifdef UNICODE\r
+#define WideStringFromResource StringFromResource\r
+LPSTR WINAPI StringFromResource(__out_ecount(STR_MAX_LENGTH) LPSTR pBuffer, int iResourceID);\r
+#else\r
+LPWSTR WINAPI WideStringFromResource(__out_ecount(STR_MAX_LENGTH) LPWSTR pBuffer, int iResourceID);\r
+#endif\r
+\r
+\r
+BOOL WINAPI GetDialogSize(int iResourceID,     // Dialog box resource identifier\r
+                          DLGPROC pDlgProc,    // Pointer to dialog procedure\r
+                          LPARAM lParam,       // Any user data wanted in pDlgProc\r
+                          __out SIZE *pResult);// Returns the size of dialog box\r
+\r
+// Class that aggregates an IDirectDraw interface\r
+\r
+class CAggDirectDraw : public IDirectDraw, public CUnknown\r
+{\r
+protected:\r
+\r
+    LPDIRECTDRAW m_pDirectDraw;\r
+\r
+public:\r
+\r
+    DECLARE_IUNKNOWN\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,__deref_out void **ppv);\r
+\r
+    // Constructor and destructor\r
+\r
+    CAggDirectDraw(__in_opt LPCTSTR pName,__inout_opt LPUNKNOWN pUnk) :\r
+        CUnknown(pName,pUnk),\r
+        m_pDirectDraw(NULL) { };\r
+\r
+    virtual CAggDirectDraw::~CAggDirectDraw() { };\r
+\r
+    // Set the object we should be aggregating\r
+    void SetDirectDraw(__inout LPDIRECTDRAW pDirectDraw) {\r
+        m_pDirectDraw = pDirectDraw;\r
+    }\r
+\r
+    // IDirectDraw methods\r
+\r
+    STDMETHODIMP Compact();\r
+    STDMETHODIMP CreateClipper(DWORD dwFlags,__deref_out LPDIRECTDRAWCLIPPER *lplpDDClipper,__inout_opt IUnknown *pUnkOuter);\r
+    STDMETHODIMP CreatePalette(DWORD dwFlags,__in LPPALETTEENTRY lpColorTable,__deref_out LPDIRECTDRAWPALETTE *lplpDDPalette,__inout_opt IUnknown *pUnkOuter);\r
+    STDMETHODIMP CreateSurface(__in LPDDSURFACEDESC lpDDSurfaceDesc,__deref_out LPDIRECTDRAWSURFACE *lplpDDSurface,__inout_opt IUnknown *pUnkOuter);\r
+    STDMETHODIMP DuplicateSurface(__in LPDIRECTDRAWSURFACE lpDDSurface,__deref_out LPDIRECTDRAWSURFACE *lplpDupDDSurface);\r
+    STDMETHODIMP EnumDisplayModes(DWORD dwSurfaceDescCount,__in LPDDSURFACEDESC lplpDDSurfaceDescList,__in LPVOID lpContext,__in LPDDENUMMODESCALLBACK lpEnumCallback);\r
+    STDMETHODIMP EnumSurfaces(DWORD dwFlags,__in LPDDSURFACEDESC lpDDSD,__in LPVOID lpContext,__in LPDDENUMSURFACESCALLBACK lpEnumCallback);\r
+    STDMETHODIMP FlipToGDISurface();\r
+    STDMETHODIMP GetCaps(__out LPDDCAPS lpDDDriverCaps,__out LPDDCAPS lpDDHELCaps);\r
+    STDMETHODIMP GetDisplayMode(__out LPDDSURFACEDESC lpDDSurfaceDesc);\r
+    STDMETHODIMP GetFourCCCodes(__inout LPDWORD lpNumCodes,__out_ecount(*lpNumCodes) LPDWORD lpCodes);\r
+    STDMETHODIMP GetGDISurface(__deref_out LPDIRECTDRAWSURFACE *lplpGDIDDSurface);\r
+    STDMETHODIMP GetMonitorFrequency(__out LPDWORD lpdwFrequency);\r
+    STDMETHODIMP GetScanLine(__out LPDWORD lpdwScanLine);\r
+    STDMETHODIMP GetVerticalBlankStatus(__out LPBOOL lpblsInVB);\r
+    STDMETHODIMP Initialize(__in GUID *lpGUID);\r
+    STDMETHODIMP RestoreDisplayMode();\r
+    STDMETHODIMP SetCooperativeLevel(HWND hWnd,DWORD dwFlags);\r
+    STDMETHODIMP SetDisplayMode(DWORD dwWidth,DWORD dwHeight,DWORD dwBpp);\r
+    STDMETHODIMP WaitForVerticalBlank(DWORD dwFlags,HANDLE hEvent);\r
+};\r
+\r
+\r
+// Class that aggregates an IDirectDrawSurface interface\r
+\r
+class CAggDrawSurface : public IDirectDrawSurface, public CUnknown\r
+{\r
+protected:\r
+\r
+    LPDIRECTDRAWSURFACE m_pDirectDrawSurface;\r
+\r
+public:\r
+\r
+    DECLARE_IUNKNOWN\r
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,__deref_out void **ppv);\r
+\r
+    // Constructor and destructor\r
+\r
+    CAggDrawSurface(__in_opt LPCTSTR pName,__inout_opt LPUNKNOWN pUnk) :\r
+        CUnknown(pName,pUnk),\r
+        m_pDirectDrawSurface(NULL) { };\r
+\r
+    virtual ~CAggDrawSurface() { };\r
+\r
+    // Set the object we should be aggregating\r
+    void SetDirectDrawSurface(__inout LPDIRECTDRAWSURFACE pDirectDrawSurface) {\r
+        m_pDirectDrawSurface = pDirectDrawSurface;\r
+    }\r
+\r
+    // IDirectDrawSurface methods\r
+\r
+    STDMETHODIMP AddAttachedSurface(__in LPDIRECTDRAWSURFACE lpDDSAttachedSurface);\r
+    STDMETHODIMP AddOverlayDirtyRect(__in LPRECT lpRect);\r
+    STDMETHODIMP Blt(__in LPRECT lpDestRect,__in LPDIRECTDRAWSURFACE lpDDSrcSurface,__in LPRECT lpSrcRect,DWORD dwFlags,__in LPDDBLTFX lpDDBltFx);\r
+    STDMETHODIMP BltBatch(__in_ecount(dwCount) LPDDBLTBATCH lpDDBltBatch,DWORD dwCount,DWORD dwFlags);\r
+    STDMETHODIMP BltFast(DWORD dwX,DWORD dwY,__in LPDIRECTDRAWSURFACE lpDDSrcSurface,__in LPRECT lpSrcRect,DWORD dwTrans);\r
+    STDMETHODIMP DeleteAttachedSurface(DWORD dwFlags,__in LPDIRECTDRAWSURFACE lpDDSAttachedSurface);\r
+    STDMETHODIMP EnumAttachedSurfaces(__in LPVOID lpContext,__in LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback);\r
+    STDMETHODIMP EnumOverlayZOrders(DWORD dwFlags,__in LPVOID lpContext,__in LPDDENUMSURFACESCALLBACK lpfnCallback);\r
+    STDMETHODIMP Flip(__in LPDIRECTDRAWSURFACE lpDDSurfaceTargetOverride,DWORD dwFlags);\r
+    STDMETHODIMP GetAttachedSurface(__in LPDDSCAPS lpDDSCaps,__deref_out LPDIRECTDRAWSURFACE *lplpDDAttachedSurface);\r
+    STDMETHODIMP GetBltStatus(DWORD dwFlags);\r
+    STDMETHODIMP GetCaps(__out LPDDSCAPS lpDDSCaps);\r
+    STDMETHODIMP GetClipper(__deref_out LPDIRECTDRAWCLIPPER *lplpDDClipper);\r
+    STDMETHODIMP GetColorKey(DWORD dwFlags,__out LPDDCOLORKEY lpDDColorKey);\r
+    STDMETHODIMP GetDC(__out HDC *lphDC);\r
+    STDMETHODIMP GetFlipStatus(DWORD dwFlags);\r
+    STDMETHODIMP GetOverlayPosition(__out LPLONG lpdwX,__out LPLONG lpdwY);\r
+    STDMETHODIMP GetPalette(__deref_out LPDIRECTDRAWPALETTE *lplpDDPalette);\r
+    STDMETHODIMP GetPixelFormat(__out LPDDPIXELFORMAT lpDDPixelFormat);\r
+    STDMETHODIMP GetSurfaceDesc(__out LPDDSURFACEDESC lpDDSurfaceDesc);\r
+    STDMETHODIMP Initialize(__in LPDIRECTDRAW lpDD,__in LPDDSURFACEDESC lpDDSurfaceDesc);\r
+    STDMETHODIMP IsLost();\r
+    STDMETHODIMP Lock(__in LPRECT lpDestRect,__inout LPDDSURFACEDESC lpDDSurfaceDesc,DWORD dwFlags,HANDLE hEvent);\r
+    STDMETHODIMP ReleaseDC(HDC hDC);\r
+    STDMETHODIMP Restore();\r
+    STDMETHODIMP SetClipper(__in LPDIRECTDRAWCLIPPER lpDDClipper);\r
+    STDMETHODIMP SetColorKey(DWORD dwFlags,__in LPDDCOLORKEY lpDDColorKey);\r
+    STDMETHODIMP SetOverlayPosition(LONG dwX,LONG dwY);\r
+    STDMETHODIMP SetPalette(__in LPDIRECTDRAWPALETTE lpDDPalette);\r
+    STDMETHODIMP Unlock(__in LPVOID lpSurfaceData);\r
+    STDMETHODIMP UpdateOverlay(__in LPRECT lpSrcRect,__in LPDIRECTDRAWSURFACE lpDDDestSurface,__in LPRECT lpDestRect,DWORD dwFlags,__in LPDDOVERLAYFX lpDDOverlayFX);\r
+    STDMETHODIMP UpdateOverlayDisplay(DWORD dwFlags);\r
+    STDMETHODIMP UpdateOverlayZOrder(DWORD dwFlags,__in LPDIRECTDRAWSURFACE lpDDSReference);\r
+};\r
+\r
+\r
+class CLoadDirectDraw\r
+{\r
+    LPDIRECTDRAW m_pDirectDraw;     // The DirectDraw driver instance\r
+    HINSTANCE m_hDirectDraw;        // Handle to the loaded library\r
+\r
+public:\r
+\r
+    CLoadDirectDraw();\r
+    ~CLoadDirectDraw();\r
+\r
+    HRESULT LoadDirectDraw(__in LPSTR szDevice);\r
+    void ReleaseDirectDraw();\r
+    HRESULT IsDirectDrawLoaded();\r
+    LPDIRECTDRAW GetDirectDraw();\r
+    BOOL IsDirectDrawVersion1();\r
+};\r
+\r
+#endif // __VIDEOCTL__\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/vtrans.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/vtrans.cpp
new file mode 100644 (file)
index 0000000..cb4fa99
--- /dev/null
@@ -0,0 +1,468 @@
+//------------------------------------------------------------------------------\r
+// File: Vtrans.cpp\r
+//\r
+// Desc: DirectShow base classes.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+#include <measure.h>\r
+// #include <vtransfr.h>         // now in precomp file streams.h\r
+\r
+CVideoTransformFilter::CVideoTransformFilter\r
+    ( __in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, REFCLSID clsid)\r
+    : CTransformFilter(pName, pUnk, clsid)\r
+    , m_itrLate(0)\r
+    , m_nKeyFramePeriod(0)      // No QM until we see at least 2 key frames\r
+    , m_nFramesSinceKeyFrame(0)\r
+    , m_bSkipping(FALSE)\r
+    , m_tDecodeStart(0)\r
+    , m_itrAvgDecode(300000)    // 30mSec - probably allows skipping\r
+    , m_bQualityChanged(FALSE)\r
+{\r
+#ifdef PERF\r
+    RegisterPerfId();\r
+#endif //  PERF\r
+}\r
+\r
+\r
+CVideoTransformFilter::~CVideoTransformFilter()\r
+{\r
+  // nothing to do\r
+}\r
+\r
+\r
+// Reset our quality management state\r
+\r
+HRESULT CVideoTransformFilter::StartStreaming()\r
+{\r
+    m_itrLate = 0;\r
+    m_nKeyFramePeriod = 0;       // No QM until we see at least 2 key frames\r
+    m_nFramesSinceKeyFrame = 0;\r
+    m_bSkipping = FALSE;\r
+    m_tDecodeStart = 0;\r
+    m_itrAvgDecode = 300000;     // 30mSec - probably allows skipping\r
+    m_bQualityChanged = FALSE;\r
+    m_bSampleSkipped = FALSE;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Overriden to reset quality management information\r
+\r
+HRESULT CVideoTransformFilter::EndFlush()\r
+{\r
+    {\r
+        //  Synchronize\r
+        CAutoLock lck(&m_csReceive);\r
+\r
+        // Reset our stats\r
+        //\r
+        // Note - we don't want to call derived classes here,\r
+        // we only want to reset our internal variables and this\r
+        // is a convenient way to do it\r
+        CVideoTransformFilter::StartStreaming();\r
+    }\r
+    return CTransformFilter::EndFlush();\r
+}\r
+\r
+\r
+HRESULT CVideoTransformFilter::AbortPlayback(HRESULT hr)\r
+{\r
+    NotifyEvent(EC_ERRORABORT, hr, 0);\r
+    m_pOutput->DeliverEndOfStream();\r
+    return hr;\r
+}\r
+\r
+\r
+// Receive()\r
+//\r
+// Accept a sample from upstream, decide whether to process it\r
+// or drop it.  If we process it then get a buffer from the\r
+// allocator of the downstream connection, transform it into the\r
+// new buffer and deliver it to the downstream filter.\r
+// If we decide not to process it then we do not get a buffer.\r
+\r
+// Remember that although this code will notice format changes coming into\r
+// the input pin, it will NOT change its output format if that results\r
+// in the filter needing to make a corresponding output format change.  Your\r
+// derived filter will have to take care of that.  (eg. a palette change if\r
+// the input and output is an 8 bit format).  If the input sample is discarded\r
+// and nothing is sent out for this Receive, please remember to put the format\r
+// change on the first output sample that you actually do send.\r
+// If your filter will produce the same output type even when the input type\r
+// changes, then this base class code will do everything you need.\r
+\r
+HRESULT CVideoTransformFilter::Receive(IMediaSample *pSample)\r
+{\r
+    // If the next filter downstream is the video renderer, then it may\r
+    // be able to operate in DirectDraw mode which saves copying the data\r
+    // and gives higher performance.  In that case the buffer which we\r
+    // get from GetDeliveryBuffer will be a DirectDraw buffer, and\r
+    // drawing into this buffer draws directly onto the display surface.\r
+    // This means that any waiting for the correct time to draw occurs\r
+    // during GetDeliveryBuffer, and that once the buffer is given to us\r
+    // the video renderer will count it in its statistics as a frame drawn.\r
+    // This means that any decision to drop the frame must be taken before\r
+    // calling GetDeliveryBuffer.\r
+\r
+    ASSERT(CritCheckIn(&m_csReceive));\r
+    AM_MEDIA_TYPE *pmtOut, *pmt;\r
+#ifdef DEBUG\r
+    FOURCCMap fccOut;\r
+#endif\r
+    HRESULT hr;\r
+    ASSERT(pSample);\r
+    IMediaSample * pOutSample;\r
+\r
+    // If no output pin to deliver to then no point sending us data\r
+    ASSERT (m_pOutput != NULL) ;\r
+\r
+    // The source filter may dynamically ask us to start transforming from a\r
+    // different media type than the one we're using now.  If we don't, we'll\r
+    // draw garbage. (typically, this is a palette change in the movie,\r
+    // but could be something more sinister like the compression type changing,\r
+    // or even the video size changing)\r
+\r
+#define rcS1 ((VIDEOINFOHEADER *)(pmt->pbFormat))->rcSource\r
+#define rcT1 ((VIDEOINFOHEADER *)(pmt->pbFormat))->rcTarget\r
+\r
+    pSample->GetMediaType(&pmt);\r
+    if (pmt != NULL && pmt->pbFormat != NULL) {\r
+\r
+       // spew some debug output\r
+       ASSERT(!IsEqualGUID(pmt->majortype, GUID_NULL));\r
+#ifdef DEBUG\r
+        fccOut.SetFOURCC(&pmt->subtype);\r
+       LONG lCompression = HEADER(pmt->pbFormat)->biCompression;\r
+       LONG lBitCount = HEADER(pmt->pbFormat)->biBitCount;\r
+       LONG lStride = (HEADER(pmt->pbFormat)->biWidth * lBitCount + 7) / 8;\r
+       lStride = (lStride + 3) & ~3;\r
+        DbgLog((LOG_TRACE,3,TEXT("*Changing input type on the fly to")));\r
+        DbgLog((LOG_TRACE,3,TEXT("FourCC: %lx Compression: %lx BitCount: %ld"),\r
+               fccOut.GetFOURCC(), lCompression, lBitCount));\r
+        DbgLog((LOG_TRACE,3,TEXT("biHeight: %ld rcDst: (%ld, %ld, %ld, %ld)"),\r
+               HEADER(pmt->pbFormat)->biHeight,\r
+               rcT1.left, rcT1.top, rcT1.right, rcT1.bottom));\r
+        DbgLog((LOG_TRACE,3,TEXT("rcSrc: (%ld, %ld, %ld, %ld) Stride: %ld"),\r
+               rcS1.left, rcS1.top, rcS1.right, rcS1.bottom,\r
+               lStride));\r
+#endif\r
+\r
+       // now switch to using the new format.  I am assuming that the\r
+       // derived filter will do the right thing when its media type is\r
+       // switched and streaming is restarted.\r
+\r
+       StopStreaming();\r
+       m_pInput->CurrentMediaType() = *pmt;\r
+       DeleteMediaType(pmt);\r
+       // if this fails, playback will stop, so signal an error\r
+       hr = StartStreaming();\r
+       if (FAILED(hr)) {\r
+           return AbortPlayback(hr);\r
+       }\r
+    }\r
+\r
+    // Now that we have noticed any format changes on the input sample, it's\r
+    // OK to discard it.\r
+\r
+    if (ShouldSkipFrame(pSample)) {\r
+        MSR_NOTE(m_idSkip);\r
+        m_bSampleSkipped = TRUE;\r
+        return NOERROR;\r
+    }\r
+\r
+    // Set up the output sample\r
+    hr = InitializeOutputSample(pSample, &pOutSample);\r
+\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    m_bSampleSkipped = FALSE;\r
+\r
+    // The renderer may ask us to on-the-fly to start transforming to a\r
+    // different format.  If we don't obey it, we'll draw garbage\r
+\r
+#define rcS ((VIDEOINFOHEADER *)(pmtOut->pbFormat))->rcSource\r
+#define rcT ((VIDEOINFOHEADER *)(pmtOut->pbFormat))->rcTarget\r
+\r
+    pOutSample->GetMediaType(&pmtOut);\r
+    if (pmtOut != NULL && pmtOut->pbFormat != NULL) {\r
+\r
+       // spew some debug output\r
+       ASSERT(!IsEqualGUID(pmtOut->majortype, GUID_NULL));\r
+#ifdef DEBUG\r
+        fccOut.SetFOURCC(&pmtOut->subtype);\r
+       LONG lCompression = HEADER(pmtOut->pbFormat)->biCompression;\r
+       LONG lBitCount = HEADER(pmtOut->pbFormat)->biBitCount;\r
+       LONG lStride = (HEADER(pmtOut->pbFormat)->biWidth * lBitCount + 7) / 8;\r
+       lStride = (lStride + 3) & ~3;\r
+        DbgLog((LOG_TRACE,3,TEXT("*Changing output type on the fly to")));\r
+        DbgLog((LOG_TRACE,3,TEXT("FourCC: %lx Compression: %lx BitCount: %ld"),\r
+               fccOut.GetFOURCC(), lCompression, lBitCount));\r
+        DbgLog((LOG_TRACE,3,TEXT("biHeight: %ld rcDst: (%ld, %ld, %ld, %ld)"),\r
+               HEADER(pmtOut->pbFormat)->biHeight,\r
+               rcT.left, rcT.top, rcT.right, rcT.bottom));\r
+        DbgLog((LOG_TRACE,3,TEXT("rcSrc: (%ld, %ld, %ld, %ld) Stride: %ld"),\r
+               rcS.left, rcS.top, rcS.right, rcS.bottom,\r
+               lStride));\r
+#endif\r
+\r
+       // now switch to using the new format.  I am assuming that the\r
+       // derived filter will do the right thing when its media type is\r
+       // switched and streaming is restarted.\r
+\r
+       StopStreaming();\r
+       m_pOutput->CurrentMediaType() = *pmtOut;\r
+       DeleteMediaType(pmtOut);\r
+       hr = StartStreaming();\r
+\r
+       if (SUCCEEDED(hr)) {\r
+           // a new format, means a new empty buffer, so wait for a keyframe\r
+           // before passing anything on to the renderer.\r
+           // !!! a keyframe may never come, so give up after 30 frames\r
+            DbgLog((LOG_TRACE,3,TEXT("Output format change means we must wait for a keyframe")));\r
+           m_nWaitForKey = 30;\r
+\r
+       // if this fails, playback will stop, so signal an error\r
+       } else {\r
+\r
+            //  Must release the sample before calling AbortPlayback\r
+            //  because we might be holding the win16 lock or\r
+            //  ddraw lock\r
+            pOutSample->Release();\r
+           AbortPlayback(hr);\r
+            return hr;\r
+       }\r
+    }\r
+\r
+    // After a discontinuity, we need to wait for the next key frame\r
+    if (pSample->IsDiscontinuity() == S_OK) {\r
+        DbgLog((LOG_TRACE,3,TEXT("Non-key discontinuity - wait for keyframe")));\r
+       m_nWaitForKey = 30;\r
+    }\r
+\r
+    // Start timing the transform (and log it if PERF is defined)\r
+\r
+    if (SUCCEEDED(hr)) {\r
+        m_tDecodeStart = timeGetTime();\r
+        MSR_START(m_idTransform);\r
+\r
+        // have the derived class transform the data\r
+        hr = Transform(pSample, pOutSample);\r
+\r
+        // Stop the clock (and log it if PERF is defined)\r
+        MSR_STOP(m_idTransform);\r
+        m_tDecodeStart = timeGetTime()-m_tDecodeStart;\r
+        m_itrAvgDecode = m_tDecodeStart*(10000/16) + 15*(m_itrAvgDecode/16);\r
+\r
+        // Maybe we're waiting for a keyframe still?\r
+        if (m_nWaitForKey)\r
+            m_nWaitForKey--;\r
+        if (m_nWaitForKey && pSample->IsSyncPoint() == S_OK)\r
+           m_nWaitForKey = FALSE;\r
+\r
+        // if so, then we don't want to pass this on to the renderer\r
+        if (m_nWaitForKey && hr == NOERROR) {\r
+            DbgLog((LOG_TRACE,3,TEXT("still waiting for a keyframe")));\r
+           hr = S_FALSE;\r
+       }\r
+    }\r
+\r
+    if (FAILED(hr)) {\r
+        DbgLog((LOG_TRACE,1,TEXT("Error from video transform")));\r
+    } else {\r
+        // the Transform() function can return S_FALSE to indicate that the\r
+        // sample should not be delivered; we only deliver the sample if it's\r
+        // really S_OK (same as NOERROR, of course.)\r
+        // Try not to return S_FALSE to a direct draw buffer (it's wasteful)\r
+        // Try to take the decision earlier - before you get it.\r
+\r
+        if (hr == NOERROR) {\r
+           hr = m_pOutput->Deliver(pOutSample);\r
+        } else {\r
+            // S_FALSE returned from Transform is a PRIVATE agreement\r
+            // We should return NOERROR from Receive() in this case because returning S_FALSE\r
+            // from Receive() means that this is the end of the stream and no more data should\r
+            // be sent.\r
+            if (S_FALSE == hr) {\r
+\r
+                //  We must Release() the sample before doing anything\r
+                //  like calling the filter graph because having the\r
+                //  sample means we may have the DirectDraw lock\r
+                //  (== win16 lock on some versions)\r
+                pOutSample->Release();\r
+                m_bSampleSkipped = TRUE;\r
+                if (!m_bQualityChanged) {\r
+                    m_bQualityChanged = TRUE;\r
+                    NotifyEvent(EC_QUALITY_CHANGE,0,0);\r
+                }\r
+                return NOERROR;\r
+            }\r
+        }\r
+    }\r
+\r
+    // release the output buffer. If the connected pin still needs it,\r
+    // it will have addrefed it itself.\r
+    pOutSample->Release();\r
+    ASSERT(CritCheckIn(&m_csReceive));\r
+\r
+    return hr;\r
+}\r
+\r
+\r
+\r
+BOOL CVideoTransformFilter::ShouldSkipFrame( IMediaSample * pIn)\r
+{\r
+    REFERENCE_TIME trStart, trStopAt;\r
+    HRESULT hr = pIn->GetTime(&trStart, &trStopAt);\r
+\r
+    // Don't skip frames with no timestamps\r
+    if (hr != S_OK)\r
+       return FALSE;\r
+\r
+    int itrFrame = (int)(trStopAt - trStart);  // frame duration\r
+\r
+    if(S_OK==pIn->IsSyncPoint()) {\r
+        MSR_INTEGER(m_idFrameType, 1);\r
+        if ( m_nKeyFramePeriod < m_nFramesSinceKeyFrame ) {\r
+            // record the max\r
+            m_nKeyFramePeriod = m_nFramesSinceKeyFrame;\r
+        }\r
+        m_nFramesSinceKeyFrame = 0;\r
+        m_bSkipping = FALSE;\r
+    } else {\r
+        MSR_INTEGER(m_idFrameType, 2);\r
+        if (  m_nFramesSinceKeyFrame>m_nKeyFramePeriod\r
+           && m_nKeyFramePeriod>0\r
+           ) {\r
+            // We haven't seen the key frame yet, but we were clearly being\r
+            // overoptimistic about how frequent they are.\r
+            m_nKeyFramePeriod = m_nFramesSinceKeyFrame;\r
+        }\r
+    }\r
+\r
+\r
+    // Whatever we might otherwise decide,\r
+    // if we are taking only a small fraction of the required frame time to decode\r
+    // then any quality problems are actually coming from somewhere else.\r
+    // Could be a net problem at the source for instance.  In this case there's\r
+    // no point in us skipping frames here.\r
+    if (m_itrAvgDecode*4>itrFrame) {\r
+\r
+        // Don't skip unless we are at least a whole frame late.\r
+        // (We would skip B frames if more than 1/2 frame late, but they're safe).\r
+        if ( m_itrLate > itrFrame ) {\r
+\r
+            // Don't skip unless the anticipated key frame would be no more than\r
+            // 1 frame early.  If the renderer has not been waiting (we *guess*\r
+            // it hasn't because we're late) then it will allow frames to be\r
+            // played early by up to a frame.\r
+\r
+            // Let T = Stream time from now to anticipated next key frame\r
+            // = (frame duration) * (KeyFramePeriod - FramesSinceKeyFrame)\r
+            // So we skip if T - Late < one frame  i.e.\r
+            //   (duration) * (freq - FramesSince) - Late < duration\r
+            // or (duration) * (freq - FramesSince - 1) < Late\r
+\r
+            // We don't dare skip until we have seen some key frames and have\r
+            // some idea how often they occur and they are reasonably frequent.\r
+            if (m_nKeyFramePeriod>0) {\r
+                // It would be crazy - but we could have a stream with key frames\r
+                // a very long way apart - and if they are further than about\r
+                // 3.5 minutes apart then we could get arithmetic overflow in\r
+                // reference time units.  Therefore we switch to mSec at this point\r
+                int it = (itrFrame/10000)\r
+                         * (m_nKeyFramePeriod-m_nFramesSinceKeyFrame -  1);\r
+                MSR_INTEGER(m_idTimeTillKey, it);\r
+\r
+                // For debug - might want to see the details - dump them as scratch pad\r
+#ifdef VTRANSPERF\r
+                MSR_INTEGER(0, itrFrame);\r
+                MSR_INTEGER(0, m_nFramesSinceKeyFrame);\r
+                MSR_INTEGER(0, m_nKeyFramePeriod);\r
+#endif\r
+                if (m_itrLate/10000 > it) {\r
+                    m_bSkipping = TRUE;\r
+                    // Now we are committed.  Once we start skipping, we\r
+                    // cannot stop until we hit a key frame.\r
+                } else {\r
+#ifdef VTRANSPERF\r
+                    MSR_INTEGER(0, 777770);  // not near enough to next key\r
+#endif\r
+                }\r
+            } else {\r
+#ifdef VTRANSPERF\r
+                MSR_INTEGER(0, 777771);  // Next key not predictable\r
+#endif\r
+            }\r
+        } else {\r
+#ifdef VTRANSPERF\r
+            MSR_INTEGER(0, 777772);  // Less than one frame late\r
+            MSR_INTEGER(0, m_itrLate);\r
+            MSR_INTEGER(0, itrFrame);\r
+#endif\r
+        }\r
+    } else {\r
+#ifdef VTRANSPERF\r
+        MSR_INTEGER(0, 777773);  // Decode time short - not not worth skipping\r
+        MSR_INTEGER(0, m_itrAvgDecode);\r
+        MSR_INTEGER(0, itrFrame);\r
+#endif\r
+    }\r
+\r
+    ++m_nFramesSinceKeyFrame;\r
+\r
+    if (m_bSkipping) {\r
+        // We will count down the lateness as we skip each frame.\r
+        // We re-assess each frame.  The key frame might not arrive when expected.\r
+        // We reset m_itrLate if we get a new Quality message, but actually that's\r
+        // not likely because we're not sending frames on to the Renderer.  In\r
+        // fact if we DID get another one it would mean that there's a long\r
+        // pipe between us and the renderer and we might need an altogether\r
+        // better strategy to avoid hunting!\r
+        m_itrLate = m_itrLate - itrFrame;\r
+    }\r
+\r
+    MSR_INTEGER(m_idLate, (int)m_itrLate/10000 ); // Note how late we think we are\r
+    if (m_bSkipping) {\r
+        if (!m_bQualityChanged) {\r
+            m_bQualityChanged = TRUE;\r
+            NotifyEvent(EC_QUALITY_CHANGE,0,0);\r
+        }\r
+    }\r
+    return m_bSkipping;\r
+}\r
+\r
+\r
+HRESULT CVideoTransformFilter::AlterQuality(Quality q)\r
+{\r
+    // to reduce the amount of 64 bit arithmetic, m_itrLate is an int.\r
+    // +, -, >, == etc  are not too bad, but * and / are painful.\r
+    if (m_itrLate>300000000) {\r
+        // Avoid overflow and silliness - more than 30 secs late is already silly\r
+        m_itrLate = 300000000;\r
+    } else {\r
+        m_itrLate = (int)q.Late;\r
+    }\r
+    // We ignore the other fields\r
+\r
+    // We're actually not very good at handling this.  In non-direct draw mode\r
+    // most of the time can be spent in the renderer which can skip any frame.\r
+    // In that case we'd rather the renderer handled things.\r
+    // Nevertheless we will keep an eye on it and if we really start getting\r
+    // a very long way behind then we will actually skip - but we'll still tell\r
+    // the renderer (or whoever is downstream) that they should handle quality.\r
+\r
+    return E_FAIL;     // Tell the renderer to do his thing.\r
+\r
+}\r
+\r
+\r
+\r
+// This will avoid several hundred useless warnings if compiled -W4 by MS VC++ v4\r
+#pragma warning(disable:4514)\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/vtrans.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/vtrans.h
new file mode 100644 (file)
index 0000000..49b1509
--- /dev/null
@@ -0,0 +1,143 @@
+//------------------------------------------------------------------------------\r
+// File: VTrans.h\r
+//\r
+// Desc: DirectShow base classes - defines a video transform class.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+// This class is derived from CTransformFilter, but is specialised to handle\r
+// the requirements of video quality control by frame dropping.\r
+// This is a non-in-place transform, (i.e. it copies the data) such as a decoder.\r
+\r
+class CVideoTransformFilter : public CTransformFilter\r
+{\r
+  public:\r
+\r
+    CVideoTransformFilter(__in_opt LPCTSTR, __inout_opt LPUNKNOWN, REFCLSID clsid);\r
+    ~CVideoTransformFilter();\r
+    HRESULT EndFlush();\r
+\r
+    // =================================================================\r
+    // ----- override these bits ---------------------------------------\r
+    // =================================================================\r
+    // The following methods are in CTransformFilter which is inherited.\r
+    // They are mentioned here for completeness\r
+    //\r
+    // These MUST be supplied in a derived class\r
+    //\r
+    // NOTE:\r
+    // virtual HRESULT Transform(IMediaSample * pIn, IMediaSample *pOut);\r
+    // virtual HRESULT CheckInputType(const CMediaType* mtIn) PURE;\r
+    // virtual HRESULT CheckTransform\r
+    //     (const CMediaType* mtIn, const CMediaType* mtOut) PURE;\r
+    // static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *);\r
+    // virtual HRESULT DecideBufferSize\r
+    //     (IMemAllocator * pAllocator, ALLOCATOR_PROPERTIES *pprop) PURE;\r
+    // virtual HRESULT GetMediaType(int iPosition, CMediaType *pMediaType) PURE;\r
+    //\r
+    // These MAY also be overridden\r
+    //\r
+    // virtual HRESULT StopStreaming();\r
+    // virtual HRESULT SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt);\r
+    // virtual HRESULT CheckConnect(PIN_DIRECTION dir,IPin *pPin);\r
+    // virtual HRESULT BreakConnect(PIN_DIRECTION dir);\r
+    // virtual HRESULT CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin);\r
+    // virtual HRESULT EndOfStream(void);\r
+    // virtual HRESULT BeginFlush(void);\r
+    // virtual HRESULT EndFlush(void);\r
+    // virtual HRESULT NewSegment\r
+    //     (REFERENCE_TIME tStart,REFERENCE_TIME tStop,double dRate);\r
+#ifdef PERF\r
+\r
+    // If you override this - ensure that you register all these ids\r
+    // as well as any of your own,\r
+    virtual void RegisterPerfId() {\r
+        m_idSkip        = MSR_REGISTER(TEXT("Video Transform Skip frame"));\r
+        m_idFrameType   = MSR_REGISTER(TEXT("Video transform frame type"));\r
+        m_idLate        = MSR_REGISTER(TEXT("Video Transform Lateness"));\r
+        m_idTimeTillKey = MSR_REGISTER(TEXT("Video Transform Estd. time to next key"));\r
+        CTransformFilter::RegisterPerfId();\r
+    }\r
+#endif\r
+\r
+  protected:\r
+\r
+    // =========== QUALITY MANAGEMENT IMPLEMENTATION ========================\r
+    // Frames are assumed to come in three types:\r
+    // Type 1: an AVI key frame or an MPEG I frame.\r
+    //        This frame can be decoded with no history.\r
+    //        Dropping this frame means that no further frame can be decoded\r
+    //        until the next type 1 frame.\r
+    //        Type 1 frames are sync points.\r
+    // Type 2: an AVI non-key frame or an MPEG P frame.\r
+    //        This frame cannot be decoded unless the previous type 1 frame was\r
+    //        decoded and all type 2 frames since have been decoded.\r
+    //        Dropping this frame means that no further frame can be decoded\r
+    //        until the next type 1 frame.\r
+    // Type 3: An MPEG B frame.\r
+    //        This frame cannot be decoded unless the previous type 1 or 2 frame\r
+    //        has been decoded AND the subsequent type 1 or 2 frame has also\r
+    //        been decoded.  (This requires decoding the frames out of sequence).\r
+    //        Dropping this frame affects no other frames.  This implementation\r
+    //        does not allow for these.  All non-sync-point frames are treated\r
+    //        as being type 2.\r
+    //\r
+    // The spacing of frames of type 1 in a file is not guaranteed.  There MUST\r
+    // be a type 1 frame at (well, near) the start of the file in order to start\r
+    // decoding at all.  After that there could be one every half second or so,\r
+    // there could be one at the start of each scene (aka "cut", "shot") or\r
+    // there could be no more at all.\r
+    // If there is only a single type 1 frame then NO FRAMES CAN BE DROPPED\r
+    // without losing all the rest of the movie.  There is no way to tell whether\r
+    // this is the case, so we find that we are in the gambling business.\r
+    // To try to improve the odds, we record the greatest interval between type 1s\r
+    // that we have seen and we bet on things being no worse than this in the\r
+    // future.\r
+\r
+    // You can tell if it's a type 1 frame by calling IsSyncPoint().\r
+    // there is no architected way to test for a type 3, so you should override\r
+    // the quality management here if you have B-frames.\r
+\r
+    int m_nKeyFramePeriod; // the largest observed interval between type 1 frames\r
+                           // 1 means every frame is type 1, 2 means every other.\r
+\r
+    int m_nFramesSinceKeyFrame; // Used to count frames since the last type 1.\r
+                                // becomes the new m_nKeyFramePeriod if greater.\r
+\r
+    BOOL m_bSkipping;           // we are skipping to the next type 1 frame\r
+\r
+#ifdef PERF\r
+    int m_idFrameType;          // MSR id Frame type.  1=Key, 2="non-key"\r
+    int m_idSkip;               // MSR id skipping\r
+    int m_idLate;               // MSR id lateness\r
+    int m_idTimeTillKey;        // MSR id for guessed time till next key frame.\r
+#endif\r
+\r
+    virtual HRESULT StartStreaming();\r
+\r
+    HRESULT AbortPlayback(HRESULT hr); // if something bad happens\r
+\r
+    HRESULT Receive(IMediaSample *pSample);\r
+\r
+    HRESULT AlterQuality(Quality q);\r
+\r
+    BOOL ShouldSkipFrame(IMediaSample * pIn);\r
+\r
+    int m_itrLate;              // lateness from last Quality message\r
+                                // (this overflows at 214 secs late).\r
+    int m_tDecodeStart;         // timeGetTime when decode started.\r
+    int m_itrAvgDecode;         // Average decode time in reference units.\r
+\r
+    BOOL m_bNoSkip;             // debug - no skipping.\r
+\r
+    // We send an EC_QUALITY_CHANGE notification to the app if we have to degrade.\r
+    // We send one when we start degrading, not one for every frame, this means\r
+    // we track whether we've sent one yet.\r
+    BOOL m_bQualityChanged;\r
+\r
+    // When non-zero, don't pass anything to renderer until next keyframe\r
+    // If there are few keys, give up and eventually draw something\r
+    int m_nWaitForKey;\r
+};\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/winctrl.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/winctrl.cpp
new file mode 100644 (file)
index 0000000..4d1f52e
--- /dev/null
@@ -0,0 +1,2081 @@
+//------------------------------------------------------------------------------\r
+// File: WinCtrl.cpp\r
+//\r
+// Desc: DirectShow base classes - implements video control interface class.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+#include <intsafe.h>\r
+#include <checkbmi.h>\r
+\r
+// The control interface methods require us to be connected\r
+\r
+#define CheckConnected(pin,code)                    \\r
+{                                                   \\r
+    if (pin == NULL) {                              \\r
+        ASSERT(!TEXT("Pin not set"));               \\r
+    } else if (pin->IsConnected() == FALSE) {       \\r
+        return (code);                              \\r
+    }                                               \\r
+}\r
+\r
+// This checks to see whether the window has a drain. An application can in\r
+// most environments set the owner/parent of windows so that they appear in\r
+// a compound document context (for example). In this case, the application\r
+// would probably like to be told of any keyboard/mouse messages. Therefore\r
+// we pass these messages on untranslated, returning TRUE if we're successful\r
+\r
+BOOL WINAPI PossiblyEatMessage(HWND hwndDrain, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
+{\r
+    if (hwndDrain != NULL && !InSendMessage())\r
+    {\r
+        switch (uMsg)\r
+        {\r
+            case WM_CHAR:\r
+            case WM_DEADCHAR:\r
+            case WM_KEYDOWN:\r
+            case WM_KEYUP:\r
+            case WM_LBUTTONDBLCLK:\r
+            case WM_LBUTTONDOWN:\r
+            case WM_LBUTTONUP:\r
+            case WM_MBUTTONDBLCLK:\r
+            case WM_MBUTTONDOWN:\r
+            case WM_MBUTTONUP:\r
+            case WM_MOUSEACTIVATE:\r
+            case WM_MOUSEMOVE:\r
+            // If we pass this on we don't get any mouse clicks\r
+            //case WM_NCHITTEST:\r
+            case WM_NCLBUTTONDBLCLK:\r
+            case WM_NCLBUTTONDOWN:\r
+            case WM_NCLBUTTONUP:\r
+            case WM_NCMBUTTONDBLCLK:\r
+            case WM_NCMBUTTONDOWN:\r
+            case WM_NCMBUTTONUP:\r
+            case WM_NCMOUSEMOVE:\r
+            case WM_NCRBUTTONDBLCLK:\r
+            case WM_NCRBUTTONDOWN:\r
+            case WM_NCRBUTTONUP:\r
+            case WM_RBUTTONDBLCLK:\r
+            case WM_RBUTTONDOWN:\r
+            case WM_RBUTTONUP:\r
+            case WM_SYSCHAR:\r
+            case WM_SYSDEADCHAR:\r
+            case WM_SYSKEYDOWN:\r
+            case WM_SYSKEYUP:\r
+\r
+                DbgLog((LOG_TRACE, 2, TEXT("Forwarding %x to drain")));\r
+                PostMessage(hwndDrain, uMsg, wParam, lParam);\r
+\r
+                return TRUE;\r
+        }\r
+    }\r
+    return FALSE;\r
+}\r
+\r
+\r
+// This class implements the IVideoWindow control functions (dual interface)\r
+// we support a large number of properties and methods designed to allow the\r
+// client (whether it be an automation controller or a C/C++ application) to\r
+// set and get a number of window related properties such as it's position.\r
+// We also support some methods that duplicate the properties but provide a\r
+// more direct and efficient mechanism as many values may be changed in one\r
+\r
+CBaseControlWindow::CBaseControlWindow(\r
+                        __inout CBaseFilter *pFilter,     // Owning filter\r
+                        __in CCritSec *pInterfaceLock,    // Locking object\r
+                        __in_opt LPCTSTR pName,           // Object description\r
+                        __inout_opt LPUNKNOWN pUnk,       // Normal COM ownership\r
+                        __inout HRESULT *phr) :           // OLE return code\r
+\r
+    CBaseVideoWindow(pName,pUnk),\r
+    m_pInterfaceLock(pInterfaceLock),\r
+    m_hwndOwner(NULL),\r
+    m_hwndDrain(NULL),\r
+    m_bAutoShow(TRUE),\r
+    m_pFilter(pFilter),\r
+    m_bCursorHidden(FALSE),\r
+    m_pPin(NULL)\r
+{\r
+    ASSERT(m_pFilter);\r
+    ASSERT(m_pInterfaceLock);\r
+    ASSERT(phr);\r
+    m_BorderColour = VIDEO_COLOUR;\r
+}\r
+\r
+\r
+// Set the title caption on the base window, we don't do any field checking\r
+// as we really don't care what title they intend to have. We can always get\r
+// it back again later with GetWindowText. The only other complication is to\r
+// do the necessary string conversions between ANSI and OLE Unicode strings\r
+\r
+STDMETHODIMP CBaseControlWindow::put_Caption(__in BSTR strCaption)\r
+{\r
+    CheckPointer((PVOID)strCaption,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+#ifdef UNICODE\r
+    SetWindowText(m_hwnd, strCaption);\r
+#else\r
+    CHAR Caption[CAPTION];\r
+\r
+    WideCharToMultiByte(CP_ACP,0,strCaption,-1,Caption,CAPTION,NULL,NULL);\r
+    SetWindowText(m_hwnd, Caption);\r
+#endif\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Get the current base window title caption, once again we do no real field\r
+// checking. We allocate a string for the window title to be filled in with\r
+// which ensures the interface doesn't fiddle around with getting memory. A\r
+// BSTR is a normal C string with the length at position (-1), we use the\r
+// WriteBSTR helper function to create the caption to try and avoid OLE32\r
+\r
+STDMETHODIMP CBaseControlWindow::get_Caption(__out BSTR *pstrCaption)\r
+{\r
+    CheckPointer(pstrCaption,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    WCHAR WideCaption[CAPTION];\r
+\r
+#ifdef UNICODE\r
+    GetWindowText(m_hwnd,WideCaption,CAPTION);\r
+#else\r
+    // Convert the ASCII caption to a UNICODE string\r
+\r
+    TCHAR Caption[CAPTION];\r
+    GetWindowText(m_hwnd,Caption,CAPTION);\r
+    MultiByteToWideChar(CP_ACP,0,Caption,-1,WideCaption,CAPTION);\r
+#endif\r
+    return WriteBSTR(pstrCaption,WideCaption);\r
+}\r
+\r
+\r
+// Set the window style using GWL_EXSTYLE\r
+\r
+STDMETHODIMP CBaseControlWindow::put_WindowStyleEx(long WindowStyleEx)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+\r
+    // Should we be taking off WS_EX_TOPMOST\r
+\r
+    if (GetWindowLong(m_hwnd,GWL_EXSTYLE) & WS_EX_TOPMOST) {\r
+        if ((WindowStyleEx & WS_EX_TOPMOST) == 0) {\r
+            SendMessage(m_hwnd,m_ShowStageTop,(WPARAM) FALSE,(LPARAM) 0);\r
+        }\r
+    }\r
+\r
+    // Likewise should we be adding WS_EX_TOPMOST\r
+\r
+    if (WindowStyleEx & WS_EX_TOPMOST) {\r
+        SendMessage(m_hwnd,m_ShowStageTop,(WPARAM) TRUE,(LPARAM) 0);\r
+        WindowStyleEx &= (~WS_EX_TOPMOST);\r
+        if (WindowStyleEx == 0) return NOERROR;\r
+    }\r
+    return DoSetWindowStyle(WindowStyleEx,GWL_EXSTYLE);\r
+}\r
+\r
+\r
+// Gets the current GWL_EXSTYLE base window style\r
+\r
+STDMETHODIMP CBaseControlWindow::get_WindowStyleEx(__out long *pWindowStyleEx)\r
+{\r
+    CheckPointer(pWindowStyleEx,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    return DoGetWindowStyle(pWindowStyleEx,GWL_EXSTYLE);\r
+}\r
+\r
+\r
+// Set the window style using GWL_STYLE\r
+\r
+STDMETHODIMP CBaseControlWindow::put_WindowStyle(long WindowStyle)\r
+{\r
+    // These styles cannot be changed dynamically\r
+\r
+    if ((WindowStyle & WS_DISABLED) ||\r
+        (WindowStyle & WS_ICONIC) ||\r
+        (WindowStyle & WS_MAXIMIZE) ||\r
+        (WindowStyle & WS_MINIMIZE) ||\r
+        (WindowStyle & WS_HSCROLL) ||\r
+        (WindowStyle & WS_VSCROLL)) {\r
+\r
+            return E_INVALIDARG;\r
+    }\r
+\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    return DoSetWindowStyle(WindowStyle,GWL_STYLE);\r
+}\r
+\r
+\r
+// Get the current GWL_STYLE base window style\r
+\r
+STDMETHODIMP CBaseControlWindow::get_WindowStyle(__out long *pWindowStyle)\r
+{\r
+    CheckPointer(pWindowStyle,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    return DoGetWindowStyle(pWindowStyle,GWL_STYLE);\r
+}\r
+\r
+\r
+// Change the base window style or the extended styles depending on whether\r
+// WindowLong is GWL_STYLE or GWL_EXSTYLE. We must call SetWindowPos to have\r
+// the window displayed in it's new style after the change which is a little\r
+// tricky if the window is not currently visible as we realise it offscreen.\r
+// In most cases the client will call get_WindowStyle before they call this\r
+// and then AND and OR in extra bit settings according to the requirements\r
+\r
+HRESULT CBaseControlWindow::DoSetWindowStyle(long Style,long WindowLong)\r
+{\r
+    RECT WindowRect;\r
+\r
+    // Get the window's visibility before setting the style\r
+    BOOL bVisible = IsWindowVisible(m_hwnd);\r
+    EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect));\r
+\r
+    // Set the new style flags for the window\r
+    SetWindowLong(m_hwnd,WindowLong,Style);\r
+    UINT WindowFlags = SWP_SHOWWINDOW | SWP_FRAMECHANGED | SWP_NOACTIVATE;\r
+    WindowFlags |= SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE;\r
+\r
+    // Show the window again in the current position\r
+\r
+    if (bVisible == TRUE) {\r
+\r
+        SetWindowPos(m_hwnd,            // Base window handle\r
+                     HWND_TOP,          // Just a place holder\r
+                     0,0,0,0,           // Leave size and position\r
+                     WindowFlags);      // Just draw it again\r
+\r
+        return NOERROR;\r
+    }\r
+\r
+    // Move the window offscreen so the user doesn't see the changes\r
+\r
+    MoveWindow((HWND) m_hwnd,                     // Base window handle\r
+               GetSystemMetrics(SM_CXSCREEN),     // Current desktop width\r
+               GetSystemMetrics(SM_CYSCREEN),     // Likewise it's height\r
+               WIDTH(&WindowRect),                // Use the same width\r
+               HEIGHT(&WindowRect),               // Keep height same to\r
+               TRUE);                             // May as well repaint\r
+\r
+    // Now show the previously hidden window\r
+\r
+    SetWindowPos(m_hwnd,            // Base window handle\r
+                 HWND_TOP,          // Just a place holder\r
+                 0,0,0,0,           // Leave size and position\r
+                 WindowFlags);      // Just draw it again\r
+\r
+    ShowWindow(m_hwnd,SW_HIDE);\r
+\r
+    if (GetParent(m_hwnd)) {\r
+\r
+        MapWindowPoints(HWND_DESKTOP, GetParent(m_hwnd), (LPPOINT)&WindowRect, 2);\r
+    }\r
+\r
+    MoveWindow((HWND) m_hwnd,        // Base window handle\r
+               WindowRect.left,      // Existing x coordinate\r
+               WindowRect.top,       // Existing y coordinate\r
+               WIDTH(&WindowRect),   // Use the same width\r
+               HEIGHT(&WindowRect),  // Keep height same to\r
+               TRUE);                // May as well repaint\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Get the current base window style (either GWL_STYLE or GWL_EXSTYLE)\r
+\r
+HRESULT CBaseControlWindow::DoGetWindowStyle(__out long *pStyle,long WindowLong)\r
+{\r
+    *pStyle = GetWindowLong(m_hwnd,WindowLong);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Change the visibility of the base window, this takes the same parameters\r
+// as the ShowWindow Win32 API does, so the client can have the window hidden\r
+// or shown, minimised to an icon, or maximised to play in full screen mode\r
+// We pass the request on to the base window to actually make the change\r
+\r
+STDMETHODIMP CBaseControlWindow::put_WindowState(long WindowState)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    DoShowWindow(WindowState);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Get the current window state, this function returns a subset of the SW bit\r
+// settings available in ShowWindow, if the window is visible then SW_SHOW is\r
+// set, if it is hidden then the SW_HIDDEN is set, if it is either minimised\r
+// or maximised then the SW_MINIMIZE or SW_MAXIMIZE is set respectively. The\r
+// other SW bit settings are really set commands not readable output values\r
+\r
+STDMETHODIMP CBaseControlWindow::get_WindowState(__out long *pWindowState)\r
+{\r
+    CheckPointer(pWindowState,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    ASSERT(pWindowState);\r
+    *pWindowState = FALSE;\r
+\r
+    // Is the window visible, a window is termed visible if it is somewhere on\r
+    // the current desktop even if it is completely obscured by other windows\r
+    // so the flag is a style for each window set with the WS_VISIBLE bit\r
+\r
+    if (IsWindowVisible(m_hwnd) == TRUE) {\r
+\r
+        // Is the base window iconic\r
+        if (IsIconic(m_hwnd) == TRUE) {\r
+            *pWindowState |= SW_MINIMIZE;\r
+        }\r
+\r
+        // Has the window been maximised\r
+        else if (IsZoomed(m_hwnd) == TRUE) {\r
+            *pWindowState |= SW_MAXIMIZE;\r
+        }\r
+\r
+        // Window is normal\r
+        else {\r
+            *pWindowState |= SW_SHOW;\r
+        }\r
+\r
+    } else {\r
+        *pWindowState |= SW_HIDE;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This makes sure that any palette we realise in the base window (through a\r
+// media type or through the overlay interface) is done in the background and\r
+// is therefore mapped to existing device entries rather than taking it over\r
+// as it will do when we this window gets the keyboard focus. An application\r
+// uses this to make sure it doesn't have it's palette removed by the window\r
+\r
+STDMETHODIMP CBaseControlWindow::put_BackgroundPalette(long BackgroundPalette)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cWindowLock(&m_WindowLock);\r
+\r
+    // Check this is a valid automation boolean type\r
+\r
+    if (BackgroundPalette != OATRUE) {\r
+        if (BackgroundPalette != OAFALSE) {\r
+            return E_INVALIDARG;\r
+        }\r
+    }\r
+\r
+    // Make sure the window realises any palette it has again\r
+\r
+    m_bBackground = (BackgroundPalette == OATRUE ? TRUE : FALSE);\r
+    PostMessage(m_hwnd,m_RealizePalette,0,0);\r
+    PaintWindow(FALSE);\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This returns the current background realisation setting\r
+\r
+STDMETHODIMP\r
+CBaseControlWindow::get_BackgroundPalette(__out long *pBackgroundPalette)\r
+{\r
+    CheckPointer(pBackgroundPalette,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cWindowLock(&m_WindowLock);\r
+\r
+    // Get the current background palette setting\r
+\r
+    *pBackgroundPalette = (m_bBackground == TRUE ? OATRUE : OAFALSE);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Change the visibility of the base window\r
+\r
+STDMETHODIMP CBaseControlWindow::put_Visible(long Visible)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+\r
+    // Check this is a valid automation boolean type\r
+\r
+    if (Visible != OATRUE) {\r
+        if (Visible != OAFALSE) {\r
+            return E_INVALIDARG;\r
+        }\r
+    }\r
+\r
+    // Convert the boolean visibility into SW_SHOW and SW_HIDE\r
+\r
+    INT Mode = (Visible == OATRUE ? SW_SHOWNORMAL : SW_HIDE);\r
+    DoShowWindow(Mode);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Return OATRUE if the window is currently visible otherwise OAFALSE\r
+\r
+STDMETHODIMP CBaseControlWindow::get_Visible(__out long *pVisible)\r
+{\r
+    CheckPointer(pVisible,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+\r
+    // See if the base window has a WS_VISIBLE style - this will return TRUE\r
+    // even if the window is completely obscured by other desktop windows, we\r
+    // return FALSE if the window is not showing because of earlier calls\r
+\r
+    BOOL Mode = IsWindowVisible(m_hwnd);\r
+    *pVisible = (Mode == TRUE ? OATRUE : OAFALSE);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Change the left position of the base window. This keeps the window width\r
+// and height properties the same so it effectively shunts the window left or\r
+// right accordingly - there is the Width property to change that dimension\r
+\r
+STDMETHODIMP CBaseControlWindow::put_Left(long Left)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    BOOL bSuccess;\r
+    RECT WindowRect;\r
+\r
+    // Get the current window position in a RECT\r
+    EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect));\r
+\r
+    if (GetParent(m_hwnd)) {\r
+\r
+        MapWindowPoints(HWND_DESKTOP, GetParent(m_hwnd), (LPPOINT)&WindowRect, 2);\r
+    }\r
+\r
+    // Adjust the coordinates ready for SetWindowPos, the window rectangle we\r
+    // get back from GetWindowRect is in left,top,right and bottom while the\r
+    // coordinates SetWindowPos wants are left,top,width and height values\r
+\r
+    WindowRect.bottom = WindowRect.bottom - WindowRect.top;\r
+    WindowRect.right = WindowRect.right - WindowRect.left;\r
+    UINT WindowFlags = SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE;\r
+\r
+    bSuccess = SetWindowPos(m_hwnd,                // Window handle\r
+                            HWND_TOP,              // Put it at the top\r
+                            Left,                  // New left position\r
+                            WindowRect.top,        // Leave top alone\r
+                            WindowRect.right,      // The WIDTH (not right)\r
+                            WindowRect.bottom,     // The HEIGHT (not bottom)\r
+                            WindowFlags);          // Show window options\r
+\r
+    if (bSuccess == FALSE) {\r
+        return E_INVALIDARG;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Return the current base window left position\r
+\r
+STDMETHODIMP CBaseControlWindow::get_Left(__out long *pLeft)\r
+{\r
+    CheckPointer(pLeft,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    RECT WindowRect;\r
+\r
+    EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect));\r
+    *pLeft = WindowRect.left;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Change the current width of the base window. This property complements the\r
+// left position property so we must keep the left edge constant and expand or\r
+// contract to the right, the alternative would be to change the left edge so\r
+// keeping the right edge constant but this is maybe a little more intuitive\r
+\r
+STDMETHODIMP CBaseControlWindow::put_Width(long Width)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    BOOL bSuccess;\r
+    RECT WindowRect;\r
+\r
+    // Adjust the coordinates ready for SetWindowPos, the window rectangle we\r
+    // get back from GetWindowRect is in left,top,right and bottom while the\r
+    // coordinates SetWindowPos wants are left,top,width and height values\r
+\r
+    EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect));\r
+\r
+    if (GetParent(m_hwnd)) {\r
+\r
+        MapWindowPoints(HWND_DESKTOP, GetParent(m_hwnd), (LPPOINT)&WindowRect, 2);\r
+    }\r
+\r
+    WindowRect.bottom = WindowRect.bottom - WindowRect.top;\r
+    UINT WindowFlags = SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE;\r
+\r
+    // This seems to have a bug in that calling SetWindowPos on a window with\r
+    // just the width changing causes it to ignore the width that you pass in\r
+    // and sets it to a mimimum value of 110 pixels wide (Windows NT 3.51)\r
+\r
+    bSuccess = SetWindowPos(m_hwnd,                // Window handle\r
+                            HWND_TOP,              // Put it at the top\r
+                            WindowRect.left,       // Leave left alone\r
+                            WindowRect.top,        // Leave top alone\r
+                            Width,                 // New WIDTH dimension\r
+                            WindowRect.bottom,     // The HEIGHT (not bottom)\r
+                            WindowFlags);          // Show window options\r
+\r
+    if (bSuccess == FALSE) {\r
+        return E_INVALIDARG;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Return the current base window width\r
+\r
+STDMETHODIMP CBaseControlWindow::get_Width(__out long *pWidth)\r
+{\r
+    CheckPointer(pWidth,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    RECT WindowRect;\r
+\r
+    EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect));\r
+    *pWidth = WindowRect.right - WindowRect.left;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This allows the client program to change the top position for the window in\r
+// the same way that changing the left position does not affect the width of\r
+// the image so changing the top position does not affect the window height\r
+\r
+STDMETHODIMP CBaseControlWindow::put_Top(long Top)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    BOOL bSuccess;\r
+    RECT WindowRect;\r
+\r
+    // Get the current window position in a RECT\r
+    EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect));\r
+\r
+    if (GetParent(m_hwnd)) {\r
+\r
+        MapWindowPoints(HWND_DESKTOP, GetParent(m_hwnd), (LPPOINT)&WindowRect, 2);\r
+    }\r
+\r
+    // Adjust the coordinates ready for SetWindowPos, the window rectangle we\r
+    // get back from GetWindowRect is in left,top,right and bottom while the\r
+    // coordinates SetWindowPos wants are left,top,width and height values\r
+\r
+    WindowRect.bottom = WindowRect.bottom - WindowRect.top;\r
+    WindowRect.right = WindowRect.right - WindowRect.left;\r
+    UINT WindowFlags = SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE;\r
+\r
+    bSuccess = SetWindowPos(m_hwnd,                // Window handle\r
+                            HWND_TOP,              // Put it at the top\r
+                            WindowRect.left,       // Leave left alone\r
+                            Top,                   // New top position\r
+                            WindowRect.right,      // The WIDTH (not right)\r
+                            WindowRect.bottom,     // The HEIGHT (not bottom)\r
+                            WindowFlags);          // Show window flags\r
+\r
+    if (bSuccess == FALSE) {\r
+        return E_INVALIDARG;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Return the current base window top position\r
+\r
+STDMETHODIMP CBaseControlWindow::get_Top(long *pTop)\r
+{\r
+    CheckPointer(pTop,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    RECT WindowRect;\r
+\r
+    EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect));\r
+    *pTop = WindowRect.top;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Change the height of the window, this complements the top property so when\r
+// we change this we must keep the top position for the base window, as said\r
+// before we could keep the bottom and grow upwards although this is perhaps\r
+// a little more intuitive since we already have a top position property\r
+\r
+STDMETHODIMP CBaseControlWindow::put_Height(long Height)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    BOOL bSuccess;\r
+    RECT WindowRect;\r
+\r
+    // Adjust the coordinates ready for SetWindowPos, the window rectangle we\r
+    // get back from GetWindowRect is in left,top,right and bottom while the\r
+    // coordinates SetWindowPos wants are left,top,width and height values\r
+\r
+    EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect));\r
+\r
+    if (GetParent(m_hwnd)) {\r
+\r
+        MapWindowPoints(HWND_DESKTOP, GetParent(m_hwnd), (LPPOINT)&WindowRect, 2);\r
+    }\r
+\r
+    WindowRect.right = WindowRect.right - WindowRect.left;\r
+    UINT WindowFlags = SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE;\r
+\r
+    bSuccess = SetWindowPos(m_hwnd,                // Window handle\r
+                            HWND_TOP,              // Put it at the top\r
+                            WindowRect.left,       // Leave left alone\r
+                            WindowRect.top,        // Leave top alone\r
+                            WindowRect.right,      // The WIDTH (not right)\r
+                            Height,                // New height dimension\r
+                            WindowFlags);          // Show window flags\r
+\r
+    if (bSuccess == FALSE) {\r
+        return E_INVALIDARG;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Return the current base window height\r
+\r
+STDMETHODIMP CBaseControlWindow::get_Height(__out long *pHeight)\r
+{\r
+    CheckPointer(pHeight,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    RECT WindowRect;\r
+\r
+    EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect));\r
+    *pHeight = WindowRect.bottom - WindowRect.top;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This can be called to change the owning window. Setting the owner is done\r
+// through this function, however to make the window a true child window the\r
+// style must also be set to WS_CHILD. After resetting the owner to NULL an\r
+// application should also set the style to WS_OVERLAPPED | WS_CLIPCHILDREN.\r
+\r
+// We cannot lock the object here because the SetParent causes an interthread\r
+// SendMessage to the owner window. If they are in GetState we will sit here\r
+// incomplete with the critical section locked therefore blocking out source\r
+// filter threads from accessing us. Because the source thread can't enter us\r
+// it can't get buffers or call EndOfStream so the GetState will not complete\r
+\r
+STDMETHODIMP CBaseControlWindow::put_Owner(OAHWND Owner)\r
+{\r
+    // Check we are connected otherwise reject the call\r
+\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    m_hwndOwner = (HWND) Owner;\r
+    HWND hwndParent = m_hwndOwner;\r
+\r
+    // Add or remove WS_CHILD as appropriate\r
+\r
+    LONG Style = GetWindowLong(m_hwnd,GWL_STYLE);\r
+    if (Owner == NULL) {\r
+        Style &= (~WS_CHILD);\r
+    } else {\r
+        Style |= (WS_CHILD);\r
+    }\r
+    SetWindowLong(m_hwnd,GWL_STYLE,Style);\r
+\r
+    // Don't call this with the filter locked\r
+\r
+    SetParent(m_hwnd,hwndParent);\r
+\r
+    PaintWindow(TRUE);\r
+    NOTE1("Changed parent %lx",hwndParent);\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This complements the put_Owner to get the current owning window property\r
+// we always return NOERROR although the returned window handle may be NULL\r
+// to indicate no owning window (the desktop window doesn't qualify as one)\r
+// If an application sets the owner we call SetParent, however that returns\r
+// NULL until the WS_CHILD bit is set on, so we store the owner internally\r
+\r
+STDMETHODIMP CBaseControlWindow::get_Owner(__out OAHWND *Owner)\r
+{\r
+    CheckPointer(Owner,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    *Owner = (OAHWND) m_hwndOwner;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// And renderer supporting IVideoWindow may have an HWND set who will get any\r
+// keyboard and mouse messages we receive posted on to them. This is separate\r
+// from setting an owning window. By separating the two, applications may get\r
+// messages sent on even when they have set no owner (perhaps it's maximised)\r
+\r
+STDMETHODIMP CBaseControlWindow::put_MessageDrain(OAHWND Drain)\r
+{\r
+    // Check we are connected otherwise reject the call\r
+\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    m_hwndDrain = (HWND) Drain;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Return the current message drain\r
+\r
+STDMETHODIMP CBaseControlWindow::get_MessageDrain(__out OAHWND *Drain)\r
+{\r
+    CheckPointer(Drain,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    *Drain = (OAHWND) m_hwndDrain;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This is called by the filter graph to inform us of a message we should know\r
+// is being sent to our owning window. We have this because as a child window\r
+// we do not get certain messages that are only sent to top level windows. We\r
+// must see the palette changed/changing/query messages so that we know if we\r
+// have the foreground palette or not. We pass the message on to our window\r
+// using SendMessage - this will cause an interthread send message to occur\r
+\r
+STDMETHODIMP\r
+CBaseControlWindow::NotifyOwnerMessage(OAHWND hwnd,    // Window handle\r
+                                       long uMsg,    // Message ID\r
+                                       LONG_PTR wParam,  // Parameters\r
+                                       LONG_PTR lParam)  // for message\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+\r
+    // Only interested in these Windows messages\r
+\r
+    switch (uMsg) {\r
+\r
+        case WM_SYSCOLORCHANGE:\r
+        case WM_PALETTECHANGED:\r
+        case WM_PALETTEISCHANGING:\r
+        case WM_QUERYNEWPALETTE:\r
+        case WM_DEVMODECHANGE:\r
+        case WM_DISPLAYCHANGE:\r
+        case WM_ACTIVATEAPP:\r
+\r
+            // If we do not have an owner then ignore\r
+\r
+            if (m_hwndOwner == NULL) {\r
+                return NOERROR;\r
+            }\r
+            SendMessage(m_hwnd,uMsg,(WPARAM)wParam,(LPARAM)lParam);\r
+           break;\r
+\r
+       // do NOT fwd WM_MOVE. the parameters are the location of the parent\r
+       // window, NOT what the renderer should be looking at.  But we need\r
+       // to make sure the overlay is moved with the parent window, so we\r
+       // do this.\r
+       case WM_MOVE:\r
+           PostMessage(m_hwnd,WM_PAINT,0,0);\r
+           break;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Allow an application to have us set the base window in the foreground. We\r
+// have this because it is difficult for one thread to do do this to a window\r
+// owned by another thread. We ask the base window class to do the real work\r
+\r
+STDMETHODIMP CBaseControlWindow::SetWindowForeground(long Focus)\r
+{\r
+    // Check this is a valid automation boolean type\r
+\r
+    if (Focus != OATRUE) {\r
+        if (Focus != OAFALSE) {\r
+            return E_INVALIDARG;\r
+        }\r
+    }\r
+\r
+    // We shouldn't lock as this sends a message\r
+\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    BOOL bFocus = (Focus == OATRUE ? TRUE : FALSE);\r
+    DoSetWindowForeground(bFocus);\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This allows a client to set the complete window size and position in one\r
+// atomic operation. The same affect can be had by changing each dimension\r
+// in turn through their individual properties although some flashing will\r
+// occur as each of them gets updated (they are better set at design time)\r
+\r
+STDMETHODIMP\r
+CBaseControlWindow::SetWindowPosition(long Left,long Top,long Width,long Height)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    BOOL bSuccess;\r
+\r
+    // Set the new size and position\r
+    UINT WindowFlags = SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE;\r
+\r
+    ASSERT(IsWindow(m_hwnd));\r
+    bSuccess = SetWindowPos(m_hwnd,         // Window handle\r
+                            HWND_TOP,       // Put it at the top\r
+                            Left,           // Left position\r
+                            Top,            // Top position\r
+                            Width,          // Window width\r
+                            Height,         // Window height\r
+                            WindowFlags);   // Show window flags\r
+    ASSERT(bSuccess);\r
+#ifdef DEBUG\r
+    DbgLog((LOG_TRACE, 1, TEXT("SWP failed error %d"), GetLastError()));\r
+#endif\r
+    if (bSuccess == FALSE) {\r
+        return E_INVALIDARG;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This complements the SetWindowPosition to return the current window place\r
+// in device coordinates. As before the same information can be retrived by\r
+// calling the property get functions individually but this is atomic and is\r
+// therefore more suitable to a live environment rather than design time\r
+\r
+STDMETHODIMP\r
+CBaseControlWindow::GetWindowPosition(__out long *pLeft,__out long *pTop,__out long *pWidth,__out long *pHeight)\r
+{\r
+    // Should check the pointers are not NULL\r
+\r
+    CheckPointer(pLeft,E_POINTER);\r
+    CheckPointer(pTop,E_POINTER);\r
+    CheckPointer(pWidth,E_POINTER);\r
+    CheckPointer(pHeight,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    RECT WindowRect;\r
+\r
+    // Get the current window coordinates\r
+\r
+    EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect));\r
+\r
+    // Convert the RECT into left,top,width and height values\r
+\r
+    *pLeft = WindowRect.left;\r
+    *pTop = WindowRect.top;\r
+    *pWidth = WindowRect.right - WindowRect.left;\r
+    *pHeight = WindowRect.bottom - WindowRect.top;\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// When a window is maximised or iconic calling GetWindowPosition will return\r
+// the current window position (likewise for the properties). However if the\r
+// restored size (ie the size we'll return to when normally shown) is needed\r
+// then this should be used. When in a normal position (neither iconic nor\r
+// maximised) then this returns the same coordinates as GetWindowPosition\r
+\r
+STDMETHODIMP\r
+CBaseControlWindow::GetRestorePosition(__out long *pLeft,__out long *pTop,__out long *pWidth,__out long *pHeight)\r
+{\r
+    // Should check the pointers are not NULL\r
+\r
+    CheckPointer(pLeft,E_POINTER);\r
+    CheckPointer(pTop,E_POINTER);\r
+    CheckPointer(pWidth,E_POINTER);\r
+    CheckPointer(pHeight,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+\r
+    // Use GetWindowPlacement to find the restore position\r
+\r
+    WINDOWPLACEMENT Place;\r
+    Place.length = sizeof(WINDOWPLACEMENT);\r
+    EXECUTE_ASSERT(GetWindowPlacement(m_hwnd,&Place));\r
+\r
+    RECT WorkArea;\r
+\r
+    // We must take into account any task bar present\r
+\r
+    if (SystemParametersInfo(SPI_GETWORKAREA,0,&WorkArea,FALSE) == TRUE) {\r
+        if (GetParent(m_hwnd) == NULL) {\r
+            Place.rcNormalPosition.top += WorkArea.top;\r
+            Place.rcNormalPosition.bottom += WorkArea.top;\r
+            Place.rcNormalPosition.left += WorkArea.left;\r
+            Place.rcNormalPosition.right += WorkArea.left;\r
+        }\r
+    }\r
+\r
+    // Convert the RECT into left,top,width and height values\r
+\r
+    *pLeft = Place.rcNormalPosition.left;\r
+    *pTop = Place.rcNormalPosition.top;\r
+    *pWidth = Place.rcNormalPosition.right - Place.rcNormalPosition.left;\r
+    *pHeight = Place.rcNormalPosition.bottom - Place.rcNormalPosition.top;\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Return the current border colour, if we are playing something to a subset\r
+// of the base window display there is an outside area exposed. The default\r
+// action is to paint this colour in the Windows background colour (defined\r
+// as value COLOR_WINDOW) We reset to this default when we're disconnected\r
+\r
+STDMETHODIMP CBaseControlWindow::get_BorderColor(__out long *Color)\r
+{\r
+    CheckPointer(Color,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    *Color = (long) m_BorderColour;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This can be called to set the current border colour\r
+\r
+STDMETHODIMP CBaseControlWindow::put_BorderColor(long Color)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+\r
+    // Have the window repainted with the new border colour\r
+\r
+    m_BorderColour = (COLORREF) Color;\r
+    PaintWindow(TRUE);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Delegate fullscreen handling to plug in distributor\r
+\r
+STDMETHODIMP CBaseControlWindow::get_FullScreenMode(__out long *FullScreenMode)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CheckPointer(FullScreenMode,E_POINTER);\r
+    return E_NOTIMPL;\r
+}\r
+\r
+\r
+// Delegate fullscreen handling to plug in distributor\r
+\r
+STDMETHODIMP CBaseControlWindow::put_FullScreenMode(long FullScreenMode)\r
+{\r
+    return E_NOTIMPL;\r
+}\r
+\r
+\r
+// This sets the auto show property, this property causes the base window to\r
+// be displayed whenever we change state. This allows an application to have\r
+// to do nothing to have the window appear but still allow them to change the\r
+// default behaviour if for example they want to keep it hidden for longer\r
+\r
+STDMETHODIMP CBaseControlWindow::put_AutoShow(long AutoShow)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+\r
+    // Check this is a valid automation boolean type\r
+\r
+    if (AutoShow != OATRUE) {\r
+        if (AutoShow != OAFALSE) {\r
+            return E_INVALIDARG;\r
+        }\r
+    }\r
+\r
+    m_bAutoShow = (AutoShow == OATRUE ? TRUE : FALSE);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This can be called to get the current auto show flag. The flag is updated\r
+// when we connect and disconnect and through this interface all of which are\r
+// controlled and serialised by means of the main renderer critical section\r
+\r
+STDMETHODIMP CBaseControlWindow::get_AutoShow(__out long *AutoShow)\r
+{\r
+    CheckPointer(AutoShow,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    *AutoShow = (m_bAutoShow == TRUE ? OATRUE : OAFALSE);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Return the minimum ideal image size for the current video. This may differ\r
+// to the actual video dimensions because we may be using DirectDraw hardware\r
+// that has specific stretching requirements. For example the Cirrus Logic\r
+// cards have a minimum stretch factor depending on the overlay surface size\r
+\r
+STDMETHODIMP\r
+CBaseControlWindow::GetMinIdealImageSize(__out long *pWidth,__out long *pHeight)\r
+{\r
+    CheckPointer(pWidth,E_POINTER);\r
+    CheckPointer(pHeight,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    FILTER_STATE State;\r
+\r
+    // Must not be stopped for this to work correctly\r
+\r
+    m_pFilter->GetState(0,&State);\r
+    if (State == State_Stopped) {\r
+        return VFW_E_WRONG_STATE;\r
+    }\r
+\r
+    RECT DefaultRect = GetDefaultRect();\r
+    *pWidth = WIDTH(&DefaultRect);\r
+    *pHeight = HEIGHT(&DefaultRect);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Return the maximum ideal image size for the current video. This may differ\r
+// to the actual video dimensions because we may be using DirectDraw hardware\r
+// that has specific stretching requirements. For example the Cirrus Logic\r
+// cards have a maximum stretch factor depending on the overlay surface size\r
+\r
+STDMETHODIMP\r
+CBaseControlWindow::GetMaxIdealImageSize(__out long *pWidth,__out long *pHeight)\r
+{\r
+    CheckPointer(pWidth,E_POINTER);\r
+    CheckPointer(pHeight,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    FILTER_STATE State;\r
+\r
+    // Must not be stopped for this to work correctly\r
+\r
+    m_pFilter->GetState(0,&State);\r
+    if (State == State_Stopped) {\r
+        return VFW_E_WRONG_STATE;\r
+    }\r
+\r
+    RECT DefaultRect = GetDefaultRect();\r
+    *pWidth = WIDTH(&DefaultRect);\r
+    *pHeight = HEIGHT(&DefaultRect);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Allow an application to hide the cursor on our window\r
+\r
+STDMETHODIMP\r
+CBaseControlWindow::HideCursor(long HideCursor)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+\r
+    // Check this is a valid automation boolean type\r
+\r
+    if (HideCursor != OATRUE) {\r
+        if (HideCursor != OAFALSE) {\r
+            return E_INVALIDARG;\r
+        }\r
+    }\r
+\r
+    m_bCursorHidden = (HideCursor == OATRUE ? TRUE : FALSE);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Returns whether we have the cursor hidden or not\r
+\r
+STDMETHODIMP CBaseControlWindow::IsCursorHidden(__out long *CursorHidden)\r
+{\r
+    CheckPointer(CursorHidden,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    *CursorHidden = (m_bCursorHidden == TRUE ? OATRUE : OAFALSE);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This class implements the IBasicVideo control functions (dual interface)\r
+// we support a large number of properties and methods designed to allow the\r
+// client (whether it be an automation controller or a C/C++ application) to\r
+// set and get a number of video related properties such as the native video\r
+// size. We support some methods that duplicate the properties but provide a\r
+// more direct and efficient mechanism as many values may be changed in one\r
+\r
+CBaseControlVideo::CBaseControlVideo(\r
+                        __inout CBaseFilter *pFilter,     // Owning filter\r
+                        __in CCritSec *pInterfaceLock,    // Locking object\r
+                        __in_opt LPCTSTR pName,           // Object description\r
+                        __inout_opt LPUNKNOWN pUnk,       // Normal COM ownership\r
+                        __inout HRESULT *phr) :           // OLE return code\r
+\r
+    CBaseBasicVideo(pName,pUnk),\r
+    m_pFilter(pFilter),\r
+    m_pInterfaceLock(pInterfaceLock),\r
+    m_pPin(NULL)\r
+{\r
+    ASSERT(m_pFilter);\r
+    ASSERT(m_pInterfaceLock);\r
+    ASSERT(phr);\r
+}\r
+\r
+// Return an approximate average time per frame\r
+\r
+STDMETHODIMP CBaseControlVideo::get_AvgTimePerFrame(__out REFTIME *pAvgTimePerFrame)\r
+{\r
+    CheckPointer(pAvgTimePerFrame,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+\r
+    VIDEOINFOHEADER *pVideoInfo = GetVideoFormat();\r
+    if (pVideoInfo == NULL)\r
+    return E_OUTOFMEMORY;\r
+    COARefTime AvgTime(pVideoInfo->AvgTimePerFrame);\r
+    *pAvgTimePerFrame = (REFTIME) AvgTime;\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Return an approximate bit rate for the video\r
+\r
+STDMETHODIMP CBaseControlVideo::get_BitRate(__out long *pBitRate)\r
+{\r
+    CheckPointer(pBitRate,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+\r
+    VIDEOINFOHEADER *pVideoInfo = GetVideoFormat();\r
+    if (pVideoInfo == NULL)\r
+    return E_OUTOFMEMORY;\r
+    *pBitRate = pVideoInfo->dwBitRate;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Return an approximate bit error rate\r
+\r
+STDMETHODIMP CBaseControlVideo::get_BitErrorRate(__out long *pBitErrorRate)\r
+{\r
+    CheckPointer(pBitErrorRate,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+\r
+    VIDEOINFOHEADER *pVideoInfo = GetVideoFormat();\r
+    if (pVideoInfo == NULL)\r
+    return E_OUTOFMEMORY;\r
+    *pBitErrorRate = pVideoInfo->dwBitErrorRate;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This returns the current video width\r
+\r
+STDMETHODIMP CBaseControlVideo::get_VideoWidth(__out long *pVideoWidth)\r
+{\r
+    CheckPointer(pVideoWidth,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+\r
+    VIDEOINFOHEADER *pVideoInfo = GetVideoFormat();\r
+    if (pVideoInfo == NULL)\r
+    return E_OUTOFMEMORY;\r
+    *pVideoWidth = pVideoInfo->bmiHeader.biWidth;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This returns the current video height\r
+\r
+STDMETHODIMP CBaseControlVideo::get_VideoHeight(__out long *pVideoHeight)\r
+{\r
+    CheckPointer(pVideoHeight,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+\r
+    VIDEOINFOHEADER *pVideoInfo = GetVideoFormat();\r
+    if (pVideoInfo == NULL)\r
+    return E_OUTOFMEMORY;\r
+    *pVideoHeight = pVideoInfo->bmiHeader.biHeight;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This returns the current palette the video is using as an array allocated\r
+// by the user. To remain consistent we use PALETTEENTRY fields to return the\r
+// colours in rather than RGBQUADs that multimedia decided to use. The memory\r
+// is allocated by the user so we simple copy each in turn. We check that the\r
+// number of entries requested and the start position offset are both valid\r
+// If the number of entries evaluates to zero then we return an S_FALSE code\r
+\r
+STDMETHODIMP CBaseControlVideo::GetVideoPaletteEntries(long StartIndex,\r
+                                                       long Entries,\r
+                                                       __out long *pRetrieved,\r
+                                                       __out_ecount_part(Entries, *pRetrieved) long *pPalette)\r
+{\r
+    CheckPointer(pRetrieved,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    CMediaType MediaType;\r
+\r
+    // Get the video format from the derived class\r
+\r
+    VIDEOINFOHEADER *pVideoInfo = GetVideoFormat();\r
+    if (pVideoInfo == NULL)\r
+    return E_OUTOFMEMORY;\r
+    BITMAPINFOHEADER *pHeader = HEADER(pVideoInfo);\r
+\r
+    // Is the current format palettised\r
+\r
+    if (PALETTISED(pVideoInfo) == FALSE) {\r
+        *pRetrieved = 0;\r
+        return VFW_E_NO_PALETTE_AVAILABLE;\r
+    }\r
+\r
+    // Do they just want to know how many are available\r
+\r
+    if (pPalette == NULL) {\r
+        *pRetrieved = pHeader->biClrUsed;\r
+        return NOERROR;\r
+    }\r
+\r
+    // Make sure the start position is a valid offset\r
+\r
+    if (StartIndex >= (LONG) pHeader->biClrUsed || StartIndex < 0) {\r
+        *pRetrieved = 0;\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+    // Correct the number we can retrieve\r
+\r
+    LONG Available = (LONG) pHeader->biClrUsed - StartIndex;\r
+    *pRetrieved = max(0,min(Available,Entries));\r
+    if (*pRetrieved == 0) {\r
+        return S_FALSE;\r
+    }\r
+\r
+    // Copy the palette entries to the output buffer\r
+\r
+    PALETTEENTRY *pEntries = (PALETTEENTRY *) pPalette;\r
+    RGBQUAD *pColours = COLORS(pVideoInfo) + StartIndex;\r
+\r
+    for (LONG Count = 0;Count < *pRetrieved;Count++) {\r
+        pEntries[Count].peRed = pColours[Count].rgbRed;\r
+        pEntries[Count].peGreen = pColours[Count].rgbGreen;\r
+        pEntries[Count].peBlue = pColours[Count].rgbBlue;\r
+        pEntries[Count].peFlags = 0;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This returns the current video dimensions as a method rather than a number\r
+// of individual property get calls. For the same reasons as said before we\r
+// cannot access the renderer media type directly as the window object thread\r
+// may be updating it since dynamic format changes may change these values\r
+\r
+STDMETHODIMP CBaseControlVideo::GetVideoSize(__out long *pWidth,__out long *pHeight)\r
+{\r
+    CheckPointer(pWidth,E_POINTER);\r
+    CheckPointer(pHeight,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+\r
+    // Get the video format from the derived class\r
+    VIDEOINFOHEADER *pVideoInfo = GetVideoFormat();\r
+    if (pVideoInfo == NULL)\r
+    return E_OUTOFMEMORY;\r
+    *pWidth = pVideoInfo->bmiHeader.biWidth;\r
+    *pHeight = pVideoInfo->bmiHeader.biHeight;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Set the source video rectangle as left,top,right and bottom coordinates\r
+// rather than left,top,width and height as per OLE automation interfaces\r
+// Then pass the rectangle on to the window object to set the source\r
+\r
+STDMETHODIMP\r
+CBaseControlVideo::SetSourcePosition(long Left,long Top,long Width,long Height)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    RECT SourceRect;\r
+    SourceRect.left = Left;\r
+    SourceRect.top = Top;\r
+    SourceRect.right = Left + Width;\r
+    SourceRect.bottom = Top + Height;\r
+\r
+    // Check the source rectangle is valid\r
+\r
+    HRESULT hr = CheckSourceRect(&SourceRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    // Now set the source rectangle\r
+\r
+    hr = SetSourceRect(&SourceRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return OnUpdateRectangles();\r
+}\r
+\r
+\r
+// Return the source rectangle in left,top,width and height rather than the\r
+// left,top,right and bottom values that RECT uses (and which the window\r
+// object returns through GetSourceRect) which requires a little work\r
+\r
+STDMETHODIMP\r
+CBaseControlVideo::GetSourcePosition(__out long *pLeft,__out long *pTop,__out long *pWidth,__out long *pHeight)\r
+{\r
+    // Should check the pointers are non NULL\r
+\r
+    CheckPointer(pLeft,E_POINTER);\r
+    CheckPointer(pTop,E_POINTER);\r
+    CheckPointer(pWidth,E_POINTER);\r
+    CheckPointer(pHeight,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    RECT SourceRect;\r
+\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    GetSourceRect(&SourceRect);\r
+\r
+    *pLeft = SourceRect.left;\r
+    *pTop = SourceRect.top;\r
+    *pWidth = WIDTH(&SourceRect);\r
+    *pHeight = HEIGHT(&SourceRect);\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Set the video destination as left,top,right and bottom coordinates rather\r
+// than the left,top,width and height uses as per OLE automation interfaces\r
+// Then pass the rectangle on to the window object to set the destination\r
+\r
+STDMETHODIMP\r
+CBaseControlVideo::SetDestinationPosition(long Left,long Top,long Width,long Height)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    RECT DestinationRect;\r
+\r
+    DestinationRect.left = Left;\r
+    DestinationRect.top = Top;\r
+    DestinationRect.right = Left + Width;\r
+    DestinationRect.bottom = Top + Height;\r
+\r
+    // Check the target rectangle is valid\r
+\r
+    HRESULT hr = CheckTargetRect(&DestinationRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    // Now set the new target rectangle\r
+\r
+    hr = SetTargetRect(&DestinationRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return OnUpdateRectangles();\r
+}\r
+\r
+\r
+// Return the destination rectangle in left,top,width and height rather than\r
+// the left,top,right and bottom values that RECT uses (and which the window\r
+// object returns through GetDestinationRect) which requires a little work\r
+\r
+STDMETHODIMP\r
+CBaseControlVideo::GetDestinationPosition(__out long *pLeft,__out long *pTop,__out long *pWidth,__out long *pHeight)\r
+{\r
+    // Should check the pointers are not NULL\r
+\r
+    CheckPointer(pLeft,E_POINTER);\r
+    CheckPointer(pTop,E_POINTER);\r
+    CheckPointer(pWidth,E_POINTER);\r
+    CheckPointer(pHeight,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    RECT DestinationRect;\r
+\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    GetTargetRect(&DestinationRect);\r
+\r
+    *pLeft = DestinationRect.left;\r
+    *pTop = DestinationRect.top;\r
+    *pWidth = WIDTH(&DestinationRect);\r
+    *pHeight = HEIGHT(&DestinationRect);\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Set the source left position, the source rectangle we get back from the\r
+// window object is a true rectangle in left,top,right and bottom positions\r
+// so all we have to do is to update the left position and pass it back. We\r
+// must keep the current width constant when we're updating this property\r
+\r
+STDMETHODIMP CBaseControlVideo::put_SourceLeft(long SourceLeft)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    RECT SourceRect;\r
+    GetSourceRect(&SourceRect);\r
+    SourceRect.right = SourceLeft + WIDTH(&SourceRect);\r
+    SourceRect.left = SourceLeft;\r
+\r
+    // Check the source rectangle is valid\r
+\r
+    HRESULT hr = CheckSourceRect(&SourceRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    // Now set the source rectangle\r
+\r
+    hr = SetSourceRect(&SourceRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return OnUpdateRectangles();\r
+}\r
+\r
+\r
+// Return the current left source video position\r
+\r
+STDMETHODIMP CBaseControlVideo::get_SourceLeft(__out long *pSourceLeft)\r
+{\r
+    CheckPointer(pSourceLeft,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    RECT SourceRect;\r
+\r
+    GetSourceRect(&SourceRect);\r
+    *pSourceLeft = SourceRect.left;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Set the source width, we get the current source rectangle and then update\r
+// the right position to be the left position (thereby keeping it constant)\r
+// plus the new source width we are passed in (it expands to the right)\r
+\r
+STDMETHODIMP CBaseControlVideo::put_SourceWidth(long SourceWidth)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    RECT SourceRect;\r
+    GetSourceRect(&SourceRect);\r
+    SourceRect.right = SourceRect.left + SourceWidth;\r
+\r
+    // Check the source rectangle is valid\r
+\r
+    HRESULT hr = CheckSourceRect(&SourceRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    // Now set the source rectangle\r
+\r
+    hr = SetSourceRect(&SourceRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return OnUpdateRectangles();\r
+}\r
+\r
+\r
+// Return the current source width\r
+\r
+STDMETHODIMP CBaseControlVideo::get_SourceWidth(__out long *pSourceWidth)\r
+{\r
+    CheckPointer(pSourceWidth,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    RECT SourceRect;\r
+\r
+    GetSourceRect(&SourceRect);\r
+    *pSourceWidth = WIDTH(&SourceRect);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Set the source top position - changing this property does not affect the\r
+// current source height. So changing this shunts the source rectangle up and\r
+// down appropriately. Changing the height complements this functionality by\r
+// keeping the top position constant and simply changing the source height\r
+\r
+STDMETHODIMP CBaseControlVideo::put_SourceTop(long SourceTop)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    RECT SourceRect;\r
+    GetSourceRect(&SourceRect);\r
+    SourceRect.bottom = SourceTop + HEIGHT(&SourceRect);\r
+    SourceRect.top = SourceTop;\r
+\r
+    // Check the source rectangle is valid\r
+\r
+    HRESULT hr = CheckSourceRect(&SourceRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    // Now set the source rectangle\r
+\r
+    hr = SetSourceRect(&SourceRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return OnUpdateRectangles();\r
+}\r
+\r
+\r
+// Return the current top position\r
+\r
+STDMETHODIMP CBaseControlVideo::get_SourceTop(__out long *pSourceTop)\r
+{\r
+    CheckPointer(pSourceTop,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    RECT SourceRect;\r
+\r
+    GetSourceRect(&SourceRect);\r
+    *pSourceTop = SourceRect.top;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Set the source height\r
+\r
+STDMETHODIMP CBaseControlVideo::put_SourceHeight(long SourceHeight)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    RECT SourceRect;\r
+    GetSourceRect(&SourceRect);\r
+    SourceRect.bottom = SourceRect.top + SourceHeight;\r
+\r
+    // Check the source rectangle is valid\r
+\r
+    HRESULT hr = CheckSourceRect(&SourceRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    // Now set the source rectangle\r
+\r
+    hr = SetSourceRect(&SourceRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return OnUpdateRectangles();\r
+}\r
+\r
+\r
+// Return the current source height\r
+\r
+STDMETHODIMP CBaseControlVideo::get_SourceHeight(__out long *pSourceHeight)\r
+{\r
+    CheckPointer(pSourceHeight,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    RECT SourceRect;\r
+\r
+    GetSourceRect(&SourceRect);\r
+    *pSourceHeight = HEIGHT(&SourceRect);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Set the target left position, the target rectangle we get back from the\r
+// window object is a true rectangle in left,top,right and bottom positions\r
+// so all we have to do is to update the left position and pass it back. We\r
+// must keep the current width constant when we're updating this property\r
+\r
+STDMETHODIMP CBaseControlVideo::put_DestinationLeft(long DestinationLeft)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    RECT DestinationRect;\r
+    GetTargetRect(&DestinationRect);\r
+    DestinationRect.right = DestinationLeft + WIDTH(&DestinationRect);\r
+    DestinationRect.left = DestinationLeft;\r
+\r
+    // Check the target rectangle is valid\r
+\r
+    HRESULT hr = CheckTargetRect(&DestinationRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    // Now set the new target rectangle\r
+\r
+    hr = SetTargetRect(&DestinationRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return OnUpdateRectangles();\r
+}\r
+\r
+\r
+// Return the left position for the destination rectangle\r
+\r
+STDMETHODIMP CBaseControlVideo::get_DestinationLeft(__out long *pDestinationLeft)\r
+{\r
+    CheckPointer(pDestinationLeft,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    RECT DestinationRect;\r
+\r
+    GetTargetRect(&DestinationRect);\r
+    *pDestinationLeft = DestinationRect.left;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Set the destination width\r
+\r
+STDMETHODIMP CBaseControlVideo::put_DestinationWidth(long DestinationWidth)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    RECT DestinationRect;\r
+    GetTargetRect(&DestinationRect);\r
+    DestinationRect.right = DestinationRect.left + DestinationWidth;\r
+\r
+    // Check the target rectangle is valid\r
+\r
+    HRESULT hr = CheckTargetRect(&DestinationRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    // Now set the new target rectangle\r
+\r
+    hr = SetTargetRect(&DestinationRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return OnUpdateRectangles();\r
+}\r
+\r
+\r
+// Return the width for the destination rectangle\r
+\r
+STDMETHODIMP CBaseControlVideo::get_DestinationWidth(__out long *pDestinationWidth)\r
+{\r
+    CheckPointer(pDestinationWidth,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    RECT DestinationRect;\r
+\r
+    GetTargetRect(&DestinationRect);\r
+    *pDestinationWidth = WIDTH(&DestinationRect);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Set the target top position - changing this property does not affect the\r
+// current target height. So changing this shunts the target rectangle up and\r
+// down appropriately. Changing the height complements this functionality by\r
+// keeping the top position constant and simply changing the target height\r
+\r
+STDMETHODIMP CBaseControlVideo::put_DestinationTop(long DestinationTop)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    RECT DestinationRect;\r
+    GetTargetRect(&DestinationRect);\r
+    DestinationRect.bottom = DestinationTop + HEIGHT(&DestinationRect);\r
+    DestinationRect.top = DestinationTop;\r
+\r
+    // Check the target rectangle is valid\r
+\r
+    HRESULT hr = CheckTargetRect(&DestinationRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    // Now set the new target rectangle\r
+\r
+    hr = SetTargetRect(&DestinationRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return OnUpdateRectangles();\r
+}\r
+\r
+\r
+// Return the top position for the destination rectangle\r
+\r
+STDMETHODIMP CBaseControlVideo::get_DestinationTop(__out long *pDestinationTop)\r
+{\r
+    CheckPointer(pDestinationTop,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    RECT DestinationRect;\r
+\r
+    GetTargetRect(&DestinationRect);\r
+    *pDestinationTop = DestinationRect.top;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Set the destination height\r
+\r
+STDMETHODIMP CBaseControlVideo::put_DestinationHeight(long DestinationHeight)\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    RECT DestinationRect;\r
+    GetTargetRect(&DestinationRect);\r
+    DestinationRect.bottom = DestinationRect.top + DestinationHeight;\r
+\r
+    // Check the target rectangle is valid\r
+\r
+    HRESULT hr = CheckTargetRect(&DestinationRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    // Now set the new target rectangle\r
+\r
+    hr = SetTargetRect(&DestinationRect);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return OnUpdateRectangles();\r
+}\r
+\r
+\r
+// Return the height for the destination rectangle\r
+\r
+STDMETHODIMP CBaseControlVideo::get_DestinationHeight(__out long *pDestinationHeight)\r
+{\r
+    CheckPointer(pDestinationHeight,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    RECT DestinationRect;\r
+\r
+    GetTargetRect(&DestinationRect);\r
+    *pDestinationHeight = HEIGHT(&DestinationRect);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Reset the source rectangle to the full video dimensions\r
+\r
+STDMETHODIMP CBaseControlVideo::SetDefaultSourcePosition()\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    HRESULT hr = SetDefaultSourceRect();\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return OnUpdateRectangles();\r
+}\r
+\r
+\r
+// Return S_OK if we're using the default source otherwise S_FALSE\r
+\r
+STDMETHODIMP CBaseControlVideo::IsUsingDefaultSource()\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    return IsDefaultSourceRect();\r
+}\r
+\r
+\r
+// Reset the video renderer to use the entire playback area\r
+\r
+STDMETHODIMP CBaseControlVideo::SetDefaultDestinationPosition()\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    HRESULT hr = SetDefaultTargetRect();\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return OnUpdateRectangles();\r
+}\r
+\r
+\r
+// Return S_OK if we're using the default target otherwise S_FALSE\r
+\r
+STDMETHODIMP CBaseControlVideo::IsUsingDefaultDestination()\r
+{\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    return IsDefaultTargetRect();\r
+}\r
+\r
+\r
+// Return a copy of the current image in the video renderer\r
+\r
+STDMETHODIMP\r
+CBaseControlVideo::GetCurrentImage(__inout long *pBufferSize,__out_bcount_part(*pBufferSize, *pBufferSize) long *pVideoImage)\r
+{\r
+    CheckPointer(pBufferSize,E_POINTER);\r
+    CheckConnected(m_pPin,VFW_E_NOT_CONNECTED);\r
+    CAutoLock cInterfaceLock(m_pInterfaceLock);\r
+    FILTER_STATE State;\r
+\r
+    // Make sure we are in a paused state\r
+\r
+    if (pVideoImage != NULL) {\r
+        m_pFilter->GetState(0,&State);\r
+        if (State != State_Paused) {\r
+            return VFW_E_NOT_PAUSED;\r
+        }\r
+        return GetStaticImage(pBufferSize,pVideoImage);\r
+    }\r
+\r
+    // Just return the memory required\r
+\r
+    VIDEOINFOHEADER *pVideoInfo = GetVideoFormat();\r
+    if (pVideoInfo == NULL)\r
+    return E_OUTOFMEMORY;\r
+    RECT SourceRect;\r
+    GetSourceRect(&SourceRect);\r
+    return GetImageSize(pVideoInfo,pBufferSize,&SourceRect);\r
+}\r
+\r
+\r
+// An application has two ways of using GetCurrentImage, one is to pass a real\r
+// buffer which should be filled with the current image. The other is to pass\r
+// a NULL buffer pointer which is interpreted as asking us to return how much\r
+// memory is required for the image. The constraints for when the latter can\r
+// be called are much looser. To calculate the memory required we synthesize\r
+// a VIDEOINFO that takes into account the source rectangle that's being used\r
+\r
+HRESULT CBaseControlVideo::GetImageSize(__in VIDEOINFOHEADER *pVideoInfo,\r
+                                        __out long *pBufferSize,\r
+                                        __in RECT *pSourceRect)\r
+{\r
+    NOTE("Entering GetImageSize");\r
+    ASSERT(pSourceRect);\r
+\r
+    // Check we have the correct input parameters\r
+\r
+    if (pSourceRect == NULL ||\r
+            pVideoInfo == NULL ||\r
+            pBufferSize == NULL) {\r
+\r
+        return E_UNEXPECTED;\r
+    }\r
+\r
+    // Is the data format compatible\r
+\r
+    if (pVideoInfo->bmiHeader.biCompression != BI_RGB) {\r
+        if (pVideoInfo->bmiHeader.biCompression != BI_BITFIELDS) {\r
+            return E_INVALIDARG;\r
+        }\r
+    }\r
+\r
+    ASSERT(IsRectEmpty(pSourceRect) == FALSE);\r
+\r
+    BITMAPINFOHEADER bih;\r
+    bih.biWidth = WIDTH(pSourceRect);\r
+    bih.biHeight = HEIGHT(pSourceRect);\r
+    bih.biBitCount = pVideoInfo->bmiHeader.biBitCount;\r
+    LONG Size = DIBSIZE(bih);\r
+    Size += GetBitmapFormatSize(HEADER(pVideoInfo)) - SIZE_PREHEADER;\r
+    *pBufferSize = Size;\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Given an IMediaSample containing a linear buffer with an image and a type\r
+// describing the bitmap make a rendering of the image into the output buffer\r
+// This may be called by derived classes who render typical video images to\r
+// handle the IBasicVideo GetCurrentImage method. The pVideoImage pointer may\r
+// be NULL when passed to GetCurrentImage in which case GetImageSize will be\r
+// called instead, which will just do the calculation of the memory required\r
+\r
+HRESULT CBaseControlVideo::CopyImage(IMediaSample *pMediaSample,\r
+                                     __in VIDEOINFOHEADER *pVideoInfo,\r
+                                     __inout long *pBufferSize,\r
+                                     __out_bcount_part(*pBufferSize, *pBufferSize) BYTE *pVideoImage,\r
+                                     __in RECT *pSourceRect)\r
+{\r
+    NOTE("Entering CopyImage");\r
+    ASSERT(pSourceRect);\r
+    BYTE *pCurrentImage;\r
+\r
+    // Check we have an image to copy\r
+\r
+    if (pMediaSample == NULL || pSourceRect == NULL ||\r
+            pVideoInfo == NULL || pVideoImage == NULL ||\r
+            pBufferSize == NULL) {\r
+\r
+        return E_UNEXPECTED;\r
+    }\r
+\r
+    // Is the data format compatible\r
+\r
+    if (pVideoInfo->bmiHeader.biCompression != BI_RGB) {\r
+        if (pVideoInfo->bmiHeader.biCompression != BI_BITFIELDS) {\r
+            return E_INVALIDARG;\r
+        }\r
+    }\r
+\r
+    if (*pBufferSize < 0) {\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+    // Arbitrarily large size to prevent integer overflow problems\r
+    if (pVideoInfo->bmiHeader.biSize > 4096)\r
+    {\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+    ASSERT(IsRectEmpty(pSourceRect) == FALSE);\r
+\r
+    BITMAPINFOHEADER bih;\r
+    bih.biWidth = WIDTH(pSourceRect);\r
+    bih.biHeight = HEIGHT(pSourceRect);\r
+    bih.biBitCount = pVideoInfo->bmiHeader.biBitCount;\r
+    DWORD Size = GetBitmapFormatSize(HEADER(pVideoInfo)) - SIZE_PREHEADER;\r
+    DWORD Total;\r
+    DWORD dwDibSize;\r
+\r
+    if( !ValidateBitmapInfoHeader( HEADER(pVideoInfo), Size)) {\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+    //  ValidateBitmapInfoHeader checks this but for some reason code scanning\r
+    //  tools aren't picking up the annotation\r
+    __analysis_assume(Size >= sizeof(BITMAPINFOHEADER));\r
+\r
+    if (FAILED(SAFE_DIBSIZE(&bih, &dwDibSize))) {\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+    if (FAILED(DWordAdd(Size, dwDibSize, &Total))) {\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+    // Make sure we have a large enough buffer\r
+\r
+    if ((DWORD)*pBufferSize < Total) {\r
+        return E_OUTOFMEMORY;\r
+    }\r
+\r
+    // Copy the BITMAPINFO\r
+\r
+    CopyMemory((PVOID)pVideoImage, (PVOID)&pVideoInfo->bmiHeader, Size);\r
+    ((BITMAPINFOHEADER *)pVideoImage)->biWidth = WIDTH(pSourceRect);\r
+    ((BITMAPINFOHEADER *)pVideoImage)->biHeight = HEIGHT(pSourceRect);\r
+    ((BITMAPINFOHEADER *)pVideoImage)->biSizeImage = DIBSIZE(bih);\r
+    BYTE *pImageData = pVideoImage + Size;\r
+\r
+    // Get the pointer to it's image data\r
+\r
+    HRESULT hr = pMediaSample->GetPointer(&pCurrentImage);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    // Now we are ready to start copying the source scan lines\r
+\r
+    LONG ScanLine = (pVideoInfo->bmiHeader.biBitCount / 8) * WIDTH(pSourceRect);\r
+    LONG LinesToSkip = pVideoInfo->bmiHeader.biHeight;\r
+    LinesToSkip -= pSourceRect->top + HEIGHT(pSourceRect);\r
+    pCurrentImage += LinesToSkip * DIBWIDTHBYTES(pVideoInfo->bmiHeader);\r
+    pCurrentImage += pSourceRect->left * (pVideoInfo->bmiHeader.biBitCount / 8);\r
+\r
+    // Even money on this GP faulting sometime...\r
+\r
+    for (LONG Line = 0;Line < HEIGHT(pSourceRect);Line++) {\r
+        CopyMemory((PVOID)pImageData, (PVOID)pCurrentImage, ScanLine);\r
+        pImageData += DIBWIDTHBYTES(*(BITMAPINFOHEADER *)pVideoImage);\r
+        pCurrentImage += DIBWIDTHBYTES(pVideoInfo->bmiHeader);\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Called when we change media types either during connection or dynamically\r
+// We inform the filter graph and therefore the application that the video\r
+// size may have changed, we don't bother looking to see if it really has as\r
+// we leave that to the application - the dimensions are the event parameters\r
+\r
+HRESULT CBaseControlVideo::OnVideoSizeChange()\r
+{\r
+    // Get the video format from the derived class\r
+\r
+    VIDEOINFOHEADER *pVideoInfo = GetVideoFormat();\r
+    if (pVideoInfo == NULL)\r
+    return E_OUTOFMEMORY;\r
+    WORD Width = (WORD) pVideoInfo->bmiHeader.biWidth;\r
+    WORD Height = (WORD) pVideoInfo->bmiHeader.biHeight;\r
+\r
+    return m_pFilter->NotifyEvent(EC_VIDEO_SIZE_CHANGED,\r
+                                  MAKELPARAM(Width,Height),\r
+                                  MAKEWPARAM(0,0));\r
+}\r
+\r
+\r
+// Set the video source rectangle. We must check the source rectangle against\r
+// the actual video dimensions otherwise when we come to draw the pictures we\r
+// get access violations as GDI tries to touch data outside of the image data\r
+// Although we store the rectangle in left, top, right and bottom coordinates\r
+// instead of left, top, width and height as OLE uses we do take into account\r
+// that the rectangle is used up to, but not including, the right column and\r
+// bottom row of pixels, see the Win32 documentation on RECT for more details\r
+\r
+HRESULT CBaseControlVideo::CheckSourceRect(__in RECT *pSourceRect)\r
+{\r
+    CheckPointer(pSourceRect,E_POINTER);\r
+    LONG Width,Height;\r
+    GetVideoSize(&Width,&Height);\r
+\r
+    // Check the coordinates are greater than zero\r
+    // and that the rectangle is valid (left<right, top<bottom)\r
+\r
+    if ((pSourceRect->left >= pSourceRect->right) ||\r
+       (pSourceRect->left < 0) ||\r
+       (pSourceRect->top >= pSourceRect->bottom) ||\r
+       (pSourceRect->top < 0)) {\r
+\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+    // Check the coordinates are less than the extents\r
+\r
+    if ((pSourceRect->right > Width) ||\r
+        (pSourceRect->bottom > Height)) {\r
+\r
+        return E_INVALIDARG;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Check the target rectangle has some valid coordinates, which amounts to\r
+// little more than checking the destination rectangle isn't empty. Derived\r
+// classes may call this when they have their SetTargetRect method called to\r
+// check the rectangle validity, we do not update the rectangles passed in\r
+// Although we store the rectangle in left, top, right and bottom coordinates\r
+// instead of left, top, width and height as OLE uses we do take into account\r
+// that the rectangle is used up to, but not including, the right column and\r
+// bottom row of pixels, see the Win32 documentation on RECT for more details\r
+\r
+HRESULT CBaseControlVideo::CheckTargetRect(__in RECT *pTargetRect)\r
+{\r
+    // Check the pointer is valid\r
+\r
+    if (pTargetRect == NULL) {\r
+        return E_POINTER;\r
+    }\r
+\r
+    // These overflow the WIDTH and HEIGHT checks\r
+\r
+    if (pTargetRect->left > pTargetRect->right ||\r
+            pTargetRect->top > pTargetRect->bottom) {\r
+                return E_INVALIDARG;\r
+    }\r
+\r
+    // Check the rectangle has valid coordinates\r
+\r
+    if (WIDTH(pTargetRect) <= 0 || HEIGHT(pTargetRect) <= 0) {\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+    ASSERT(IsRectEmpty(pTargetRect) == FALSE);\r
+    return NOERROR;\r
+}\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/winctrl.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/winctrl.h
new file mode 100644 (file)
index 0000000..1080a47
--- /dev/null
@@ -0,0 +1,224 @@
+//------------------------------------------------------------------------------\r
+// File: WinCtrl.h\r
+//\r
+// Desc: DirectShow base classes - defines classes for video control \r
+//       interfaces.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef __WINCTRL__\r
+#define __WINCTRL__\r
+\r
+#define ABSOL(x) (x < 0 ? -x : x)\r
+#define NEGAT(x) (x > 0 ? -x : x)\r
+\r
+//  Helper\r
+BOOL WINAPI PossiblyEatMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);\r
+\r
+class CBaseControlWindow : public CBaseVideoWindow, public CBaseWindow\r
+{\r
+protected:\r
+\r
+    CBaseFilter *m_pFilter;            // Pointer to owning media filter\r
+    CBasePin *m_pPin;                  // Controls media types for connection\r
+    CCritSec *m_pInterfaceLock;        // Externally defined critical section\r
+    COLORREF m_BorderColour;           // Current window border colour\r
+    BOOL m_bAutoShow;                  // What happens when the state changes\r
+    HWND m_hwndOwner;                  // Owner window that we optionally have\r
+    HWND m_hwndDrain;                  // HWND to post any messages received\r
+    BOOL m_bCursorHidden;              // Should we hide the window cursor\r
+\r
+public:\r
+\r
+    // Internal methods for other objects to get information out\r
+\r
+    HRESULT DoSetWindowStyle(long Style,long WindowLong);\r
+    HRESULT DoGetWindowStyle(__out long *pStyle,long WindowLong);\r
+    BOOL IsAutoShowEnabled() { return m_bAutoShow; };\r
+    COLORREF GetBorderColour() { return m_BorderColour; };\r
+    HWND GetOwnerWindow() { return m_hwndOwner; };\r
+    BOOL IsCursorHidden() { return m_bCursorHidden; };\r
+\r
+    inline BOOL PossiblyEatMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)\r
+    {\r
+        return ::PossiblyEatMessage(m_hwndDrain, uMsg, wParam, lParam);\r
+    }\r
+\r
+    // Derived classes must call this to set the pin the filter is using\r
+    // We don't have the pin passed in to the constructor (as we do with\r
+    // the CBaseFilter object) because filters typically create the\r
+    // pins dynamically when requested in CBaseFilter::GetPin. This can\r
+    // not be called from our constructor because is is a virtual method\r
+\r
+    void SetControlWindowPin(CBasePin *pPin) {\r
+        m_pPin = pPin;\r
+    }\r
+\r
+public:\r
+\r
+    CBaseControlWindow(__inout CBaseFilter *pFilter,   // Owning media filter\r
+                       __in CCritSec *pInterfaceLock,  // Locking object\r
+                       __in_opt LPCTSTR pName,         // Object description\r
+                       __inout_opt LPUNKNOWN pUnk,     // Normal COM ownership\r
+                       __inout HRESULT *phr);          // OLE return code\r
+\r
+    // These are the properties we support\r
+\r
+    STDMETHODIMP put_Caption(__in BSTR strCaption);\r
+    STDMETHODIMP get_Caption(__out BSTR *pstrCaption);\r
+    STDMETHODIMP put_AutoShow(long AutoShow);\r
+    STDMETHODIMP get_AutoShow(__out long *AutoShow);\r
+    STDMETHODIMP put_WindowStyle(long WindowStyle);\r
+    STDMETHODIMP get_WindowStyle(__out long *pWindowStyle);\r
+    STDMETHODIMP put_WindowStyleEx(long WindowStyleEx);\r
+    STDMETHODIMP get_WindowStyleEx(__out long *pWindowStyleEx);\r
+    STDMETHODIMP put_WindowState(long WindowState);\r
+    STDMETHODIMP get_WindowState(__out long *pWindowState);\r
+    STDMETHODIMP put_BackgroundPalette(long BackgroundPalette);\r
+    STDMETHODIMP get_BackgroundPalette(__out long *pBackgroundPalette);\r
+    STDMETHODIMP put_Visible(long Visible);\r
+    STDMETHODIMP get_Visible(__out long *pVisible);\r
+    STDMETHODIMP put_Left(long Left);\r
+    STDMETHODIMP get_Left(__out long *pLeft);\r
+    STDMETHODIMP put_Width(long Width);\r
+    STDMETHODIMP get_Width(__out long *pWidth);\r
+    STDMETHODIMP put_Top(long Top);\r
+    STDMETHODIMP get_Top(__out long *pTop);\r
+    STDMETHODIMP put_Height(long Height);\r
+    STDMETHODIMP get_Height(__out long *pHeight);\r
+    STDMETHODIMP put_Owner(OAHWND Owner);\r
+    STDMETHODIMP get_Owner(__out OAHWND *Owner);\r
+    STDMETHODIMP put_MessageDrain(OAHWND Drain);\r
+    STDMETHODIMP get_MessageDrain(__out OAHWND *Drain);\r
+    STDMETHODIMP get_BorderColor(__out long *Color);\r
+    STDMETHODIMP put_BorderColor(long Color);\r
+    STDMETHODIMP get_FullScreenMode(__out long *FullScreenMode);\r
+    STDMETHODIMP put_FullScreenMode(long FullScreenMode);\r
+\r
+    // And these are the methods\r
+\r
+    STDMETHODIMP SetWindowForeground(long Focus);\r
+    STDMETHODIMP NotifyOwnerMessage(OAHWND hwnd,long uMsg,LONG_PTR wParam,LONG_PTR lParam);\r
+    STDMETHODIMP GetMinIdealImageSize(__out long *pWidth,__out long *pHeight);\r
+    STDMETHODIMP GetMaxIdealImageSize(__out long *pWidth,__out long *pHeight);\r
+    STDMETHODIMP SetWindowPosition(long Left,long Top,long Width,long Height);\r
+    STDMETHODIMP GetWindowPosition(__out long *pLeft,__out long *pTop,__out long *pWidth,__out long *pHeight);\r
+    STDMETHODIMP GetRestorePosition(__out long *pLeft,__out long *pTop,__out long *pWidth,__out long *pHeight);\r
+       STDMETHODIMP HideCursor(long HideCursor);\r
+    STDMETHODIMP IsCursorHidden(__out long *CursorHidden);\r
+};\r
+\r
+// This class implements the IBasicVideo interface\r
+\r
+class CBaseControlVideo : public CBaseBasicVideo\r
+{\r
+protected:\r
+\r
+    CBaseFilter *m_pFilter;   // Pointer to owning media filter\r
+    CBasePin *m_pPin;                   // Controls media types for connection\r
+    CCritSec *m_pInterfaceLock;         // Externally defined critical section\r
+\r
+public:\r
+\r
+    // Derived classes must provide these for the implementation\r
+\r
+    virtual HRESULT IsDefaultTargetRect() PURE;\r
+    virtual HRESULT SetDefaultTargetRect() PURE;\r
+    virtual HRESULT SetTargetRect(RECT *pTargetRect) PURE;\r
+    virtual HRESULT GetTargetRect(RECT *pTargetRect) PURE;\r
+    virtual HRESULT IsDefaultSourceRect() PURE;\r
+    virtual HRESULT SetDefaultSourceRect() PURE;\r
+    virtual HRESULT SetSourceRect(RECT *pSourceRect) PURE;\r
+    virtual HRESULT GetSourceRect(RECT *pSourceRect) PURE;\r
+    virtual HRESULT GetStaticImage(__inout long *pBufferSize,__out_bcount_part(*pBufferSize, *pBufferSize) long *pDIBImage) PURE;\r
+\r
+    // Derived classes must override this to return a VIDEOINFO representing\r
+    // the video format. We cannot call IPin ConnectionMediaType to get this\r
+    // format because various filters dynamically change the type when using\r
+    // DirectDraw such that the format shows the position of the logical\r
+    // bitmap in a frame buffer surface, so the size might be returned as\r
+    // 1024x768 pixels instead of 320x240 which is the real video dimensions\r
+\r
+    __out virtual VIDEOINFOHEADER *GetVideoFormat() PURE;\r
+\r
+    // Helper functions for creating memory renderings of a DIB image\r
+\r
+    HRESULT GetImageSize(__in VIDEOINFOHEADER *pVideoInfo,\r
+                         __out LONG *pBufferSize,\r
+                         __in RECT *pSourceRect);\r
+\r
+    HRESULT CopyImage(IMediaSample *pMediaSample,\r
+                      __in VIDEOINFOHEADER *pVideoInfo,\r
+                      __inout LONG *pBufferSize,\r
+                      __out_bcount_part(*pBufferSize, *pBufferSize) BYTE *pVideoImage,\r
+                      __in RECT *pSourceRect);\r
+\r
+    // Override this if you want notifying when the rectangles change\r
+    virtual HRESULT OnUpdateRectangles() { return NOERROR; };\r
+    virtual HRESULT OnVideoSizeChange();\r
+\r
+    // Derived classes must call this to set the pin the filter is using\r
+    // We don't have the pin passed in to the constructor (as we do with\r
+    // the CBaseFilter object) because filters typically create the\r
+    // pins dynamically when requested in CBaseFilter::GetPin. This can\r
+    // not be called from our constructor because is is a virtual method\r
+\r
+    void SetControlVideoPin(__inout CBasePin *pPin) {\r
+        m_pPin = pPin;\r
+    }\r
+\r
+    // Helper methods for checking rectangles\r
+    virtual HRESULT CheckSourceRect(__in RECT *pSourceRect);\r
+    virtual HRESULT CheckTargetRect(__in RECT *pTargetRect);\r
+\r
+public:\r
+\r
+    CBaseControlVideo(__inout CBaseFilter *pFilter,    // Owning media filter\r
+                      __in CCritSec *pInterfaceLock,   // Serialise interface\r
+                      __in_opt LPCTSTR pName,          // Object description\r
+                      __inout_opt LPUNKNOWN pUnk,      // Normal COM ownership\r
+                      __inout HRESULT *phr);           // OLE return code\r
+\r
+    // These are the properties we support\r
+\r
+    STDMETHODIMP get_AvgTimePerFrame(__out REFTIME *pAvgTimePerFrame);\r
+    STDMETHODIMP get_BitRate(__out long *pBitRate);\r
+    STDMETHODIMP get_BitErrorRate(__out long *pBitErrorRate);\r
+    STDMETHODIMP get_VideoWidth(__out long *pVideoWidth);\r
+    STDMETHODIMP get_VideoHeight(__out long *pVideoHeight);\r
+    STDMETHODIMP put_SourceLeft(long SourceLeft);\r
+    STDMETHODIMP get_SourceLeft(__out long *pSourceLeft);\r
+    STDMETHODIMP put_SourceWidth(long SourceWidth);\r
+    STDMETHODIMP get_SourceWidth(__out long *pSourceWidth);\r
+    STDMETHODIMP put_SourceTop(long SourceTop);\r
+    STDMETHODIMP get_SourceTop(__out long *pSourceTop);\r
+    STDMETHODIMP put_SourceHeight(long SourceHeight);\r
+    STDMETHODIMP get_SourceHeight(__out long *pSourceHeight);\r
+    STDMETHODIMP put_DestinationLeft(long DestinationLeft);\r
+    STDMETHODIMP get_DestinationLeft(__out long *pDestinationLeft);\r
+    STDMETHODIMP put_DestinationWidth(long DestinationWidth);\r
+    STDMETHODIMP get_DestinationWidth(__out long *pDestinationWidth);\r
+    STDMETHODIMP put_DestinationTop(long DestinationTop);\r
+    STDMETHODIMP get_DestinationTop(__out long *pDestinationTop);\r
+    STDMETHODIMP put_DestinationHeight(long DestinationHeight);\r
+    STDMETHODIMP get_DestinationHeight(__out long *pDestinationHeight);\r
+\r
+    // And these are the methods\r
+\r
+    STDMETHODIMP GetVideoSize(__out long *pWidth,__out long *pHeight);\r
+    STDMETHODIMP SetSourcePosition(long Left,long Top,long Width,long Height);\r
+    STDMETHODIMP GetSourcePosition(__out long *pLeft,__out long *pTop,__out long *pWidth,__out long *pHeight);\r
+    STDMETHODIMP GetVideoPaletteEntries(long StartIndex,long Entries,__out long *pRetrieved,__out_ecount_part(Entries, *pRetrieved) long *pPalette);\r
+    STDMETHODIMP SetDefaultSourcePosition();\r
+    STDMETHODIMP IsUsingDefaultSource();\r
+    STDMETHODIMP SetDestinationPosition(long Left,long Top,long Width,long Height);\r
+    STDMETHODIMP GetDestinationPosition(__out long *pLeft,__out long *pTop,__out long *pWidth,__out long *pHeight);\r
+    STDMETHODIMP SetDefaultDestinationPosition();\r
+    STDMETHODIMP IsUsingDefaultDestination();\r
+    STDMETHODIMP GetCurrentImage(__inout long *pBufferSize,__out_bcount_part(*pBufferSize, *pBufferSize) long *pVideoImage);\r
+};\r
+\r
+#endif // __WINCTRL__\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/winutil.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/winutil.cpp
new file mode 100644 (file)
index 0000000..6653f45
--- /dev/null
@@ -0,0 +1,2746 @@
+//------------------------------------------------------------------------------\r
+// File: WinUtil.cpp\r
+//\r
+// Desc: DirectShow base classes - implements generic window handler class.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+#include <limits.h>\r
+#include <dvdmedia.h>\r
+#include <strsafe.h>\r
+#include <checkbmi.h>\r
+\r
+static UINT MsgDestroy;\r
+\r
+// Constructor\r
+\r
+CBaseWindow::CBaseWindow(BOOL bDoGetDC, bool bDoPostToDestroy) :\r
+    m_hInstance(g_hInst),\r
+    m_hwnd(NULL),\r
+    m_hdc(NULL),\r
+    m_bActivated(FALSE),\r
+    m_pClassName(NULL),\r
+    m_ClassStyles(0),\r
+    m_WindowStyles(0),\r
+    m_WindowStylesEx(0),\r
+    m_ShowStageMessage(0),\r
+    m_ShowStageTop(0),\r
+    m_MemoryDC(NULL),\r
+    m_hPalette(NULL),\r
+    m_bBackground(FALSE),\r
+#ifdef DEBUG\r
+    m_bRealizing(FALSE),\r
+#endif\r
+    m_bNoRealize(FALSE),\r
+    m_bDoPostToDestroy(bDoPostToDestroy)\r
+{\r
+    m_bDoGetDC = bDoGetDC;\r
+}\r
+\r
+\r
+// Prepare a window by spinning off a worker thread to do the creation and\r
+// also poll the message input queue. We leave this to be called by derived\r
+// classes because they might want to override methods like MessageLoop and\r
+// InitialiseWindow, if we do this during construction they'll ALWAYS call\r
+// this base class methods. We make the worker thread create the window so\r
+// it owns it rather than the filter graph thread which is constructing us\r
+\r
+HRESULT CBaseWindow::PrepareWindow()\r
+{\r
+    if (m_hwnd) return NOERROR;\r
+    ASSERT(m_hwnd == NULL);\r
+    ASSERT(m_hdc == NULL);\r
+\r
+    // Get the derived object's window and class styles\r
+\r
+    m_pClassName = GetClassWindowStyles(&m_ClassStyles,\r
+                                        &m_WindowStyles,\r
+                                        &m_WindowStylesEx);\r
+    if (m_pClassName == NULL) {\r
+        return E_FAIL;\r
+    }\r
+\r
+    // Register our special private messages\r
+    m_ShowStageMessage = RegisterWindowMessage(SHOWSTAGE);\r
+\r
+    // RegisterWindowMessage() returns 0 if an error occurs.\r
+    if (0 == m_ShowStageMessage) {\r
+        return AmGetLastErrorToHResult();\r
+    }\r
+\r
+    m_ShowStageTop = RegisterWindowMessage(SHOWSTAGETOP);\r
+    if (0 == m_ShowStageTop) {\r
+        return AmGetLastErrorToHResult();\r
+    }\r
+\r
+    m_RealizePalette = RegisterWindowMessage(REALIZEPALETTE);\r
+    if (0 == m_RealizePalette) {\r
+        return AmGetLastErrorToHResult();\r
+    }\r
+\r
+    MsgDestroy = RegisterWindowMessage(TEXT("AM_DESTROY"));\r
+    if (0 == MsgDestroy) {\r
+        return AmGetLastErrorToHResult();\r
+    }\r
+\r
+    return DoCreateWindow();\r
+}\r
+\r
+\r
+// Destructor just a placeholder so that we know it becomes virtual\r
+// Derived classes MUST call DoneWithWindow in their destructors so\r
+// that no messages arrive after the derived class constructor ends\r
+\r
+#ifdef DEBUG\r
+CBaseWindow::~CBaseWindow()\r
+{\r
+    ASSERT(m_hwnd == NULL);\r
+    ASSERT(m_hdc == NULL);\r
+}\r
+#endif\r
+\r
+\r
+// We use the sync worker event to have the window destroyed. All we do is\r
+// signal the event and wait on the window thread handle. Trying to send it\r
+// messages causes too many problems, furthermore to be on the safe side we\r
+// just wait on the thread handle while it returns WAIT_TIMEOUT or there is\r
+// a sent message to process on this thread. If the constructor failed to\r
+// create the thread in the first place then the loop will get terminated\r
+\r
+HRESULT CBaseWindow::DoneWithWindow()\r
+{\r
+    if (!IsWindow(m_hwnd) || (GetWindowThreadProcessId(m_hwnd, NULL) != GetCurrentThreadId())) {\r
+\r
+        if (IsWindow(m_hwnd)) {\r
+\r
+            // This code should only be executed if the window exists and if the window's \r
+            // messages are processed on a different thread.\r
+            ASSERT(GetWindowThreadProcessId(m_hwnd, NULL) != GetCurrentThreadId());\r
+\r
+            if (m_bDoPostToDestroy) {\r
+\r
+                HRESULT hr = S_OK;\r
+                CAMEvent m_evDone(FALSE, &hr);\r
+                if (FAILED(hr)) {\r
+                    return hr;\r
+                }\r
+\r
+                //  We must post a message to destroy the window\r
+                //  That way we can't be in the middle of processing a\r
+                //  message posted to our window when we do go away\r
+                //  Sending a message gives less synchronization.\r
+                PostMessage(m_hwnd, MsgDestroy, (WPARAM)(HANDLE)m_evDone, 0);\r
+                WaitDispatchingMessages(m_evDone, INFINITE);\r
+            } else {\r
+                SendMessage(m_hwnd, MsgDestroy, 0, 0);\r
+            }\r
+        }\r
+\r
+        //\r
+        // This is not a leak, the window manager automatically free's\r
+        // hdc's that were got via GetDC, which is the case here.\r
+        // We set it to NULL so that we don't get any asserts later.\r
+        //\r
+        m_hdc = NULL;\r
+\r
+        //\r
+        // We need to free this DC though because USER32 does not know\r
+        // anything about it.\r
+        //\r
+        if (m_MemoryDC)\r
+        {\r
+            EXECUTE_ASSERT(DeleteDC(m_MemoryDC));\r
+            m_MemoryDC = NULL;\r
+        }\r
+\r
+        // Reset the window variables\r
+        m_hwnd = NULL;\r
+\r
+        return NOERROR;\r
+    }\r
+    const HWND hwnd = m_hwnd;\r
+    if (hwnd == NULL) {\r
+        return NOERROR;\r
+    }\r
+\r
+    InactivateWindow();\r
+    NOTE("Inactivated");\r
+\r
+    // Reset the window styles before destruction\r
+\r
+    SetWindowLong(hwnd,GWL_STYLE,m_WindowStyles);\r
+    ASSERT(GetParent(hwnd) == NULL);\r
+    NOTE1("Reset window styles %d",m_WindowStyles);\r
+\r
+    //  UnintialiseWindow sets m_hwnd to NULL so save a copy\r
+    UninitialiseWindow();\r
+    DbgLog((LOG_TRACE, 2, TEXT("Destroying 0x%8.8X"), hwnd));\r
+    if (!DestroyWindow(hwnd)) {\r
+        DbgLog((LOG_TRACE, 0, TEXT("DestroyWindow %8.8X failed code %d"),\r
+                hwnd, GetLastError()));\r
+        DbgBreak("");\r
+    }\r
+\r
+    // Reset our state so we can be prepared again\r
+\r
+    m_pClassName = NULL;\r
+    m_ClassStyles = 0;\r
+    m_WindowStyles = 0;\r
+    m_WindowStylesEx = 0;\r
+    m_ShowStageMessage = 0;\r
+    m_ShowStageTop = 0;\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Called at the end to put the window in an inactive state. The pending list\r
+// will always have been cleared by this time so event if the worker thread\r
+// gets has been signaled and gets in to render something it will find both\r
+// the state has been changed and that there are no available sample images\r
+// Since we wait on the window thread to complete we don't lock the object\r
+\r
+HRESULT CBaseWindow::InactivateWindow()\r
+{\r
+    // Has the window been activated\r
+    if (m_bActivated == FALSE) {\r
+        return S_FALSE;\r
+    }\r
+\r
+    m_bActivated = FALSE;\r
+    ShowWindow(m_hwnd,SW_HIDE);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+HRESULT CBaseWindow::CompleteConnect()\r
+{\r
+    m_bActivated = FALSE;\r
+    return NOERROR;\r
+}\r
+\r
+// This displays a normal window. We ask the base window class for default\r
+// sizes which unless overriden will return DEFWIDTH and DEFHEIGHT. We go\r
+// through a couple of extra hoops to get the client area the right size\r
+// as the object specifies which accounts for the AdjustWindowRectEx calls\r
+// We also DWORD align the left and top coordinates of the window here to\r
+// maximise the chance of being able to use DCI/DirectDraw primary surface\r
+\r
+HRESULT CBaseWindow::ActivateWindow()\r
+{\r
+    // Has the window been sized and positioned already\r
+\r
+    if (m_bActivated == TRUE || GetParent(m_hwnd) != NULL) {\r
+\r
+        SetWindowPos(m_hwnd,            // Our window handle\r
+                     HWND_TOP,          // Put it at the top\r
+                     0, 0, 0, 0,        // Leave in current position\r
+                     SWP_NOMOVE |       // Don't change it's place\r
+                     SWP_NOSIZE);       // Change Z-order only\r
+\r
+        m_bActivated = TRUE;\r
+        return S_FALSE;\r
+    }\r
+\r
+    // Calculate the desired client rectangle\r
+\r
+    RECT WindowRect, ClientRect = GetDefaultRect();\r
+    GetWindowRect(m_hwnd,&WindowRect);\r
+    AdjustWindowRectEx(&ClientRect,GetWindowLong(m_hwnd,GWL_STYLE),\r
+                       FALSE,GetWindowLong(m_hwnd,GWL_EXSTYLE));\r
+\r
+    // Align left and top edges on DWORD boundaries\r
+\r
+    UINT WindowFlags = (SWP_NOACTIVATE | SWP_FRAMECHANGED);\r
+    WindowRect.left -= (WindowRect.left & 3);\r
+    WindowRect.top -= (WindowRect.top & 3);\r
+\r
+    SetWindowPos(m_hwnd,                // Window handle\r
+                 HWND_TOP,              // Put it at the top\r
+                 WindowRect.left,       // Align left edge\r
+                 WindowRect.top,        // And also top place\r
+                 WIDTH(&ClientRect),    // Horizontal size\r
+                 HEIGHT(&ClientRect),   // Vertical size\r
+                 WindowFlags);          // Don't show window\r
+\r
+    m_bActivated = TRUE;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This can be used to DWORD align the window for maximum performance\r
+\r
+HRESULT CBaseWindow::PerformanceAlignWindow()\r
+{\r
+    RECT ClientRect,WindowRect;\r
+    GetWindowRect(m_hwnd,&WindowRect);\r
+    ASSERT(m_bActivated == TRUE);\r
+\r
+    // Don't do this if we're owned\r
+\r
+    if (GetParent(m_hwnd)) {\r
+        return NOERROR;\r
+    }\r
+\r
+    // Align left and top edges on DWORD boundaries\r
+\r
+    GetClientRect(m_hwnd, &ClientRect);\r
+    MapWindowPoints(m_hwnd, HWND_DESKTOP, (LPPOINT) &ClientRect, 2);\r
+    WindowRect.left -= (ClientRect.left & 3);\r
+    WindowRect.top  -= (ClientRect.top  & 3);\r
+    UINT WindowFlags = (SWP_NOACTIVATE | SWP_NOSIZE);\r
+\r
+    SetWindowPos(m_hwnd,                // Window handle\r
+                 HWND_TOP,              // Put it at the top\r
+                 WindowRect.left,       // Align left edge\r
+                 WindowRect.top,        // And also top place\r
+                 (int) 0,(int) 0,       // Ignore these sizes\r
+                 WindowFlags);          // Don't show window\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Install a palette into the base window - we may be called by a different\r
+// thread to the one that owns the window. We have to be careful how we do\r
+// the palette realisation as we could be a different thread to the window\r
+// which would cause an inter thread send message. Therefore we realise the\r
+// palette by sending it a special message but without the window locked\r
+\r
+HRESULT CBaseWindow::SetPalette(HPALETTE hPalette)\r
+{\r
+    // We must own the window lock during the change\r
+    {\r
+        CAutoLock cWindowLock(&m_WindowLock);\r
+        CAutoLock cPaletteLock(&m_PaletteLock);\r
+        ASSERT(hPalette);\r
+        m_hPalette = hPalette;\r
+    }\r
+    return SetPalette();\r
+}\r
+\r
+\r
+HRESULT CBaseWindow::SetPalette()\r
+{\r
+    if (!m_bNoRealize) {\r
+        SendMessage(m_hwnd, m_RealizePalette, 0, 0);\r
+        return S_OK;\r
+    } else {\r
+        // Just select the palette\r
+        ASSERT(m_hdc);\r
+        ASSERT(m_MemoryDC);\r
+\r
+        CAutoLock cPaletteLock(&m_PaletteLock);\r
+        SelectPalette(m_hdc,m_hPalette,m_bBackground);\r
+        SelectPalette(m_MemoryDC,m_hPalette,m_bBackground);\r
+\r
+        return S_OK;\r
+    }\r
+}\r
+\r
+\r
+void CBaseWindow::UnsetPalette()\r
+{\r
+    CAutoLock cWindowLock(&m_WindowLock);\r
+    CAutoLock cPaletteLock(&m_PaletteLock);\r
+\r
+    // Get a standard VGA colour palette\r
+\r
+    HPALETTE hPalette = (HPALETTE) GetStockObject(DEFAULT_PALETTE);\r
+    ASSERT(hPalette);\r
+\r
+    SelectPalette(GetWindowHDC(), hPalette, TRUE);\r
+    SelectPalette(GetMemoryHDC(), hPalette, TRUE);\r
+\r
+    m_hPalette = NULL;\r
+}\r
+\r
+\r
+void CBaseWindow::LockPaletteLock()\r
+{\r
+    m_PaletteLock.Lock();\r
+}\r
+\r
+\r
+void CBaseWindow::UnlockPaletteLock()\r
+{\r
+    m_PaletteLock.Unlock();\r
+}\r
+\r
+\r
+// Realise our palettes in the window and device contexts\r
+\r
+HRESULT CBaseWindow::DoRealisePalette(BOOL bForceBackground)\r
+{\r
+    {\r
+        CAutoLock cPaletteLock(&m_PaletteLock);\r
+\r
+        if (m_hPalette == NULL) {\r
+            return NOERROR;\r
+        }\r
+\r
+        // Realize the palette on the window thread\r
+        ASSERT(m_hdc);\r
+        ASSERT(m_MemoryDC);\r
+\r
+        SelectPalette(m_hdc,m_hPalette,m_bBackground || bForceBackground);\r
+        SelectPalette(m_MemoryDC,m_hPalette,m_bBackground);\r
+    }\r
+\r
+    //  If we grab a critical section here we can deadlock\r
+    //  with the window thread because one of the side effects\r
+    //  of RealizePalette is to send a WM_PALETTECHANGED message\r
+    //  to every window in the system.  In our handling\r
+    //  of WM_PALETTECHANGED we used to grab this CS too.\r
+    //  The really bad case is when our renderer calls DoRealisePalette()\r
+    //  while we're in the middle of processing a palette change\r
+    //  for another window.\r
+    //  So don't hold the critical section while actually realising\r
+    //  the palette.  In any case USER is meant to manage palette\r
+    //  handling - we shouldn't have to serialize everything as well\r
+    ASSERT(CritCheckOut(&m_WindowLock));\r
+    ASSERT(CritCheckOut(&m_PaletteLock));\r
+\r
+    EXECUTE_ASSERT(RealizePalette(m_hdc) != GDI_ERROR);\r
+    EXECUTE_ASSERT(RealizePalette(m_MemoryDC) != GDI_ERROR);\r
+\r
+    return (GdiFlush() == FALSE ? S_FALSE : S_OK);\r
+}\r
+\r
+\r
+// This is the global window procedure\r
+\r
+LRESULT CALLBACK WndProc(HWND hwnd,         // Window handle\r
+                         UINT uMsg,         // Message ID\r
+                         WPARAM wParam,     // First parameter\r
+                         LPARAM lParam)     // Other parameter\r
+{\r
+\r
+    // Get the window long that holds our window object pointer\r
+    // If it is NULL then we are initialising the window in which\r
+    // case the object pointer has been passed in the window creation\r
+    // structure.  IF we get any messages before WM_NCCREATE we will\r
+    // pass them to DefWindowProc.\r
+\r
+    CBaseWindow *pBaseWindow = _GetWindowLongPtr<CBaseWindow*>(hwnd,0);\r
+\r
+    if (pBaseWindow == NULL) {\r
+\r
+        // Get the structure pointer from the create struct.\r
+        // We can only do this for WM_NCCREATE which should be one of\r
+        // the first messages we receive.  Anything before this will\r
+        // have to be passed to DefWindowProc (i.e. WM_GETMINMAXINFO)\r
+\r
+        // If the message is WM_NCCREATE we set our pBaseWindow pointer\r
+        // and will then place it in the window structure\r
+\r
+        // turn off WS_EX_LAYOUTRTL style for quartz windows\r
+        if (uMsg == WM_NCCREATE) {\r
+            SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) & ~0x400000);\r
+        }\r
+\r
+        if ((uMsg != WM_NCCREATE)\r
+            || (NULL == (pBaseWindow = *(CBaseWindow**) ((LPCREATESTRUCT)lParam)->lpCreateParams)))\r
+        {\r
+            return(DefWindowProc(hwnd, uMsg, wParam, lParam));\r
+        }\r
+\r
+        // Set the window LONG to be the object who created us\r
+#ifdef DEBUG\r
+        SetLastError(0);  // because of the way SetWindowLong works\r
+#endif\r
+\r
+        LONG_PTR rc = _SetWindowLongPtr(hwnd, (DWORD) 0, pBaseWindow);\r
+\r
+\r
+#ifdef DEBUG\r
+        if (0 == rc) {\r
+            // SetWindowLong MIGHT have failed.  (Read the docs which admit\r
+            // that it is awkward to work out if you have had an error.)\r
+            LONG lasterror = GetLastError();\r
+            ASSERT(0 == lasterror);\r
+            // If this is not the case we have not set the pBaseWindow pointer\r
+            // into the window structure and we will blow up.\r
+        }\r
+#endif\r
+\r
+    }\r
+    // See if this is the packet of death\r
+    if (uMsg == MsgDestroy && uMsg != 0) {\r
+        pBaseWindow->DoneWithWindow();\r
+        if (pBaseWindow->m_bDoPostToDestroy) {\r
+            EXECUTE_ASSERT(SetEvent((HANDLE)wParam));\r
+        }\r
+        return 0;\r
+    }\r
+    return pBaseWindow->OnReceiveMessage(hwnd,uMsg,wParam,lParam);\r
+}\r
+\r
+\r
+// When the window size changes we adjust our member variables that\r
+// contain the dimensions of the client rectangle for our window so\r
+// that we come to render an image we will know whether to stretch\r
+\r
+BOOL CBaseWindow::OnSize(LONG Width, LONG Height)\r
+{\r
+    m_Width = Width;\r
+    m_Height = Height;\r
+    return TRUE;\r
+}\r
+\r
+\r
+// This function handles the WM_CLOSE message\r
+\r
+BOOL CBaseWindow::OnClose()\r
+{\r
+    ShowWindow(m_hwnd,SW_HIDE);\r
+    return TRUE;\r
+}\r
+\r
+\r
+// This is called by the worker window thread when it receives a terminate\r
+// message from the window object destructor to delete all the resources we\r
+// allocated during initialisation. By the time the worker thread exits all\r
+// processing will have been completed as the source filter disconnection\r
+// flushes the image pending sample, therefore the GdiFlush should succeed\r
+\r
+HRESULT CBaseWindow::UninitialiseWindow()\r
+{\r
+    // Have we already cleaned up\r
+\r
+    if (m_hwnd == NULL) {\r
+        ASSERT(m_hdc == NULL);\r
+        ASSERT(m_MemoryDC == NULL);\r
+        return NOERROR;\r
+    }\r
+\r
+    // Release the window resources\r
+\r
+    EXECUTE_ASSERT(GdiFlush());\r
+\r
+    if (m_hdc)\r
+    {\r
+        EXECUTE_ASSERT(ReleaseDC(m_hwnd,m_hdc));\r
+        m_hdc = NULL;\r
+    }\r
+\r
+    if (m_MemoryDC)\r
+    {\r
+        EXECUTE_ASSERT(DeleteDC(m_MemoryDC));\r
+        m_MemoryDC = NULL;\r
+    }\r
+\r
+    // Reset the window variables\r
+    m_hwnd = NULL;\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This is called by the worker window thread after it has created the main\r
+// window and it wants to initialise the rest of the owner objects window\r
+// variables such as the device contexts. We execute this function with the\r
+// critical section still locked. Nothing in this function must generate any\r
+// SendMessage calls to the window because this is executing on the window\r
+// thread so the message will never be processed and we will deadlock\r
+\r
+HRESULT CBaseWindow::InitialiseWindow(HWND hwnd)\r
+{\r
+    // Initialise the window variables\r
+\r
+    ASSERT(IsWindow(hwnd));\r
+    m_hwnd = hwnd;\r
+\r
+    if (m_bDoGetDC)\r
+    {\r
+        EXECUTE_ASSERT(m_hdc = GetDC(hwnd));\r
+        EXECUTE_ASSERT(m_MemoryDC = CreateCompatibleDC(m_hdc));\r
+\r
+        EXECUTE_ASSERT(SetStretchBltMode(m_hdc,COLORONCOLOR));\r
+        EXECUTE_ASSERT(SetStretchBltMode(m_MemoryDC,COLORONCOLOR));\r
+    }\r
+\r
+    return NOERROR;\r
+}\r
+\r
+HRESULT CBaseWindow::DoCreateWindow()\r
+{\r
+    WNDCLASS wndclass;                  // Used to register classes\r
+    BOOL bRegistered;                   // Is this class registered\r
+    HWND hwnd;                          // Handle to our window\r
+\r
+    bRegistered = GetClassInfo(m_hInstance,   // Module instance\r
+                               m_pClassName,  // Window class\r
+                               &wndclass);                 // Info structure\r
+\r
+    // if the window is to be used for drawing puposes and we are getting a DC\r
+    // for the entire lifetime of the window then changes the class style to do\r
+    // say so. If we don't set this flag then the DC comes from the cache and is\r
+    // really bad.\r
+    if (m_bDoGetDC)\r
+    {\r
+        m_ClassStyles |= CS_OWNDC;\r
+    }\r
+\r
+    if (bRegistered == FALSE) {\r
+\r
+        // Register the renderer window class\r
+\r
+        wndclass.lpszClassName = m_pClassName;\r
+        wndclass.style         = m_ClassStyles;\r
+        wndclass.lpfnWndProc   = WndProc;\r
+        wndclass.cbClsExtra    = 0;\r
+        wndclass.cbWndExtra    = sizeof(CBaseWindow *);\r
+        wndclass.hInstance     = m_hInstance;\r
+        wndclass.hIcon         = NULL;\r
+        wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);\r
+        wndclass.hbrBackground = (HBRUSH) NULL;\r
+        wndclass.lpszMenuName  = NULL;\r
+\r
+        RegisterClass(&wndclass);\r
+    }\r
+\r
+    // Create the frame window.  Pass the pBaseWindow information in the\r
+    // CreateStruct which allows our message handling loop to get hold of\r
+    // the pBaseWindow pointer.\r
+\r
+    CBaseWindow *pBaseWindow = this;                      // The owner window object\r
+    hwnd = CreateWindowEx(m_WindowStylesEx,               // Extended styles\r
+                          m_pClassName,                   // Registered name\r
+                          TEXT("ActiveMovie Window"),     // Window title\r
+                          m_WindowStyles,                 // Window styles\r
+                          CW_USEDEFAULT,                  // Start x position\r
+                          CW_USEDEFAULT,                  // Start y position\r
+                          DEFWIDTH,                       // Window width\r
+                          DEFHEIGHT,                      // Window height\r
+                          NULL,                           // Parent handle\r
+                          NULL,                           // Menu handle\r
+                          m_hInstance,                    // Instance handle\r
+                          &pBaseWindow);                  // Creation data\r
+\r
+    // If we failed signal an error to the object constructor (based on the\r
+    // last Win32 error on this thread) then signal the constructor thread\r
+    // to continue, release the mutex to let others have a go and exit\r
+\r
+    if (hwnd == NULL) {\r
+        DWORD Error = GetLastError();\r
+        return AmHresultFromWin32(Error);\r
+    }\r
+\r
+    // Check the window LONG is the object who created us\r
+    ASSERT(GetWindowLongPtr(hwnd, 0) == (LONG_PTR)this);\r
+\r
+    // Initialise the window and then signal the constructor so that it can\r
+    // continue and then finally unlock the object's critical section. The\r
+    // window class is left registered even after we terminate the thread\r
+    // as we don't know when the last window has been closed. So we allow\r
+    // the operating system to free the class resources as appropriate\r
+\r
+    InitialiseWindow(hwnd);\r
+\r
+    DbgLog((LOG_TRACE, 2, TEXT("Created window class (%s) HWND(%8.8X)"),\r
+            m_pClassName, hwnd));\r
+\r
+    return S_OK;\r
+}\r
+\r
+\r
+// The base class provides some default handling and calls DefWindowProc\r
+\r
+LRESULT CBaseWindow::OnReceiveMessage(HWND hwnd,         // Window handle\r
+                                      UINT uMsg,         // Message ID\r
+                                      WPARAM wParam,     // First parameter\r
+                                      LPARAM lParam)     // Other parameter\r
+{\r
+    ASSERT(IsWindow(hwnd));\r
+\r
+    if (PossiblyEatMessage(uMsg, wParam, lParam))\r
+        return 0;\r
+\r
+    // This is sent by the IVideoWindow SetWindowForeground method. If the\r
+    // window is invisible we will show it and make it topmost without the\r
+    // foreground focus. If the window is visible it will also be made the\r
+    // topmost window without the foreground focus. If wParam is TRUE then\r
+    // for both cases the window will be forced into the foreground focus\r
+\r
+    if (uMsg == m_ShowStageMessage) {\r
+\r
+        BOOL bVisible = IsWindowVisible(hwnd);\r
+        SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,\r
+                     SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW |\r
+                     (bVisible ? SWP_NOACTIVATE : 0));\r
+\r
+        // Should we bring the window to the foreground\r
+        if (wParam == TRUE) {\r
+            SetForegroundWindow(hwnd);\r
+        }\r
+        return (LRESULT) 1;\r
+    }\r
+\r
+    // When we go fullscreen we have to add the WS_EX_TOPMOST style to the\r
+    // video window so that it comes out above any task bar (this is more\r
+    // relevant to WindowsNT than Windows95). However the SetWindowPos call\r
+    // must be on the same thread as that which created the window. The\r
+    // wParam parameter can be TRUE or FALSE to set and reset the topmost\r
+\r
+    if (uMsg == m_ShowStageTop) {\r
+        HWND HwndTop = (wParam == TRUE ? HWND_TOPMOST : HWND_NOTOPMOST);\r
+        BOOL bVisible = IsWindowVisible(hwnd);\r
+        SetWindowPos(hwnd, HwndTop, 0, 0, 0, 0,\r
+                     SWP_NOMOVE | SWP_NOSIZE |\r
+                     (wParam == TRUE ? SWP_SHOWWINDOW : 0) |\r
+                     (bVisible ? SWP_NOACTIVATE : 0));\r
+        return (LRESULT) 1;\r
+    }\r
+\r
+    // New palette stuff\r
+    if (uMsg == m_RealizePalette) {\r
+        ASSERT(m_hwnd == hwnd);\r
+        return OnPaletteChange(m_hwnd,WM_QUERYNEWPALETTE);\r
+    }\r
+\r
+    switch (uMsg) {\r
+\r
+        // Repaint the window if the system colours change\r
+\r
+    case WM_SYSCOLORCHANGE:\r
+\r
+        InvalidateRect(hwnd,NULL,FALSE);\r
+        return (LRESULT) 1;\r
+\r
+    // Somebody has changed the palette\r
+    case WM_PALETTECHANGED:\r
+\r
+        OnPaletteChange((HWND)wParam,uMsg);\r
+        return (LRESULT) 0;\r
+\r
+        // We are about to receive the keyboard focus so we ask GDI to realise\r
+        // our logical palette again and hopefully it will be fully installed\r
+        // without any mapping having to be done during any picture rendering\r
+\r
+    case WM_QUERYNEWPALETTE:\r
+        ASSERT(m_hwnd == hwnd);\r
+        return OnPaletteChange(m_hwnd,uMsg);\r
+\r
+    // do NOT fwd WM_MOVE. the parameters are the location of the parent\r
+    // window, NOT what the renderer should be looking at.  But we need\r
+    // to make sure the overlay is moved with the parent window, so we\r
+    // do this.\r
+    case WM_MOVE:\r
+        if (IsWindowVisible(m_hwnd)) {\r
+            PostMessage(m_hwnd,WM_PAINT,0,0);\r
+        }\r
+        break;\r
+\r
+    // Store the width and height as useful base class members\r
+\r
+    case WM_SIZE:\r
+\r
+        OnSize(LOWORD(lParam), HIWORD(lParam));\r
+        return (LRESULT) 0;\r
+\r
+    // Intercept the WM_CLOSE messages to hide the window\r
+\r
+    case WM_CLOSE:\r
+\r
+        OnClose();\r
+        return (LRESULT) 0;\r
+    }\r
+    return DefWindowProc(hwnd,uMsg,wParam,lParam);\r
+}\r
+\r
+\r
+// This handles the Windows palette change messages - if we do realise our\r
+// palette then we return TRUE otherwise we return FALSE. If our window is\r
+// foreground application then we should get first choice of colours in the\r
+// system palette entries. We get best performance when our logical palette\r
+// includes the standard VGA colours (at the beginning and end) otherwise\r
+// GDI may have to map from our palette to the device palette while drawing\r
+\r
+LRESULT CBaseWindow::OnPaletteChange(HWND hwnd,UINT Message)\r
+{\r
+    // First check we are not changing the palette during closedown\r
+\r
+    if (m_hwnd == NULL || hwnd == NULL) {\r
+        return (LRESULT) 0;\r
+    }\r
+    ASSERT(!m_bRealizing);\r
+\r
+    // Should we realise our palette again\r
+\r
+    if ((Message == WM_QUERYNEWPALETTE || hwnd != m_hwnd)) {\r
+        //  It seems that even if we're invisible that we can get asked\r
+        //  to realize our palette and this can cause really ugly side-effects\r
+        //  Seems like there's another bug but this masks it a least for the\r
+        //  shutting down case.\r
+        if (!IsWindowVisible(m_hwnd)) {\r
+            DbgLog((LOG_TRACE, 1, TEXT("Realizing when invisible!")));\r
+            return (LRESULT) 0;\r
+        }\r
+\r
+        // Avoid recursion with multiple graphs in the same app\r
+#ifdef DEBUG\r
+        m_bRealizing = TRUE;\r
+#endif\r
+        DoRealisePalette(Message != WM_QUERYNEWPALETTE);\r
+#ifdef DEBUG\r
+        m_bRealizing = FALSE;\r
+#endif\r
+\r
+        // Should we redraw the window with the new palette\r
+        if (Message == WM_PALETTECHANGED) {\r
+            InvalidateRect(m_hwnd,NULL,FALSE);\r
+        }\r
+    }\r
+\r
+    return (LRESULT) 1;\r
+}\r
+\r
+\r
+// Determine if the window exists.\r
+\r
+bool CBaseWindow::WindowExists()\r
+{\r
+    return !!IsWindow(m_hwnd);\r
+}\r
+\r
+\r
+// Return the default window rectangle\r
+\r
+RECT CBaseWindow::GetDefaultRect()\r
+{\r
+    RECT DefaultRect = {0,0,DEFWIDTH,DEFHEIGHT};\r
+    ASSERT(m_hwnd);\r
+    // ASSERT(m_hdc);\r
+    return DefaultRect;\r
+}\r
+\r
+\r
+// Return the current window width\r
+\r
+LONG CBaseWindow::GetWindowWidth()\r
+{\r
+    ASSERT(m_hwnd);\r
+    // ASSERT(m_hdc);\r
+    return m_Width;\r
+}\r
+\r
+\r
+// Return the current window height\r
+\r
+LONG CBaseWindow::GetWindowHeight()\r
+{\r
+    ASSERT(m_hwnd);\r
+    // ASSERT(m_hdc);\r
+    return m_Height;\r
+}\r
+\r
+\r
+// Return the window handle\r
+\r
+HWND CBaseWindow::GetWindowHWND()\r
+{\r
+    ASSERT(m_hwnd);\r
+    // ASSERT(m_hdc);\r
+    return m_hwnd;\r
+}\r
+\r
+\r
+// Return the window drawing device context\r
+\r
+HDC CBaseWindow::GetWindowHDC()\r
+{\r
+    ASSERT(m_hwnd);\r
+    ASSERT(m_hdc);\r
+    return m_hdc;\r
+}\r
+\r
+\r
+// Return the offscreen window drawing device context\r
+\r
+HDC CBaseWindow::GetMemoryHDC()\r
+{\r
+    ASSERT(m_hwnd);\r
+    ASSERT(m_MemoryDC);\r
+    return m_MemoryDC;\r
+}\r
+\r
+\r
+#ifdef DEBUG\r
+HPALETTE CBaseWindow::GetPalette()\r
+{\r
+    // The palette lock should always be held when accessing\r
+    // m_hPalette.\r
+    ASSERT(CritCheckIn(&m_PaletteLock));\r
+    return m_hPalette;\r
+}\r
+#endif // DEBUG\r
+\r
+\r
+// This is available to clients who want to change the window visiblity. It's\r
+// little more than an indirection to the Win32 ShowWindow although these is\r
+// some benefit in going through here as this function may change sometime\r
+\r
+HRESULT CBaseWindow::DoShowWindow(LONG ShowCmd)\r
+{\r
+    ShowWindow(m_hwnd,ShowCmd);\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Generate a WM_PAINT message for the video window\r
+\r
+void CBaseWindow::PaintWindow(BOOL bErase)\r
+{\r
+    InvalidateRect(m_hwnd,NULL,bErase);\r
+}\r
+\r
+\r
+// Allow an application to have us set the video window in the foreground. We\r
+// have this because it is difficult for one thread to do do this to a window\r
+// owned by another thread. Rather than expose the message we use to execute\r
+// the inter thread send message we provide the interface function. All we do\r
+// is to SendMessage to the video window renderer thread with a WM_SHOWSTAGE\r
+\r
+void CBaseWindow::DoSetWindowForeground(BOOL bFocus)\r
+{\r
+    SendMessage(m_hwnd,m_ShowStageMessage,(WPARAM) bFocus,(LPARAM) 0);\r
+}\r
+\r
+\r
+// Constructor initialises the owning object pointer. Since we are a worker\r
+// class for the main window object we have relatively few state variables to\r
+// look after. We are given device context handles to use later on as well as\r
+// the source and destination rectangles (but reset them here just in case)\r
+\r
+CDrawImage::CDrawImage(__inout CBaseWindow *pBaseWindow) :\r
+    m_pBaseWindow(pBaseWindow),\r
+    m_hdc(NULL),\r
+    m_MemoryDC(NULL),\r
+    m_bStretch(FALSE),\r
+    m_pMediaType(NULL),\r
+    m_bUsingImageAllocator(FALSE)\r
+{\r
+    ASSERT(pBaseWindow);\r
+    ResetPaletteVersion();\r
+    SetRectEmpty(&m_TargetRect);\r
+    SetRectEmpty(&m_SourceRect);\r
+\r
+    m_perfidRenderTime = MSR_REGISTER(TEXT("Single Blt time"));\r
+}\r
+\r
+\r
+// Overlay the image time stamps on the picture. Access to this method is\r
+// serialised by the caller. We display the sample start and end times on\r
+// top of the video using TextOut on the device context we are handed. If\r
+// there isn't enough room in the window for the times we don't show them\r
+\r
+void CDrawImage::DisplaySampleTimes(IMediaSample *pSample)\r
+{\r
+#ifdef DEBUG\r
+    //\r
+    // Only allow the "annoying" time messages if the users has turned the\r
+    // logging "way up"\r
+    //\r
+    BOOL bAccept = DbgCheckModuleLevel(LOG_TRACE, 5);\r
+    if (bAccept == FALSE) {\r
+        return;\r
+    }\r
+#endif\r
+\r
+    TCHAR szTimes[TIMELENGTH];      // Time stamp strings\r
+    ASSERT(pSample);                // Quick sanity check\r
+    RECT ClientRect;                // Client window size\r
+    SIZE Size;                      // Size of text output\r
+\r
+    // Get the time stamps and window size\r
+\r
+    pSample->GetTime((REFERENCE_TIME*)&m_StartSample, (REFERENCE_TIME*)&m_EndSample);\r
+    HWND hwnd = m_pBaseWindow->GetWindowHWND();\r
+    EXECUTE_ASSERT(GetClientRect(hwnd,&ClientRect));\r
+\r
+    // Format the sample time stamps\r
+\r
+    (void)StringCchPrintf(szTimes,NUMELMS(szTimes),TEXT("%08d : %08d"),\r
+             m_StartSample.Millisecs(),\r
+             m_EndSample.Millisecs());\r
+\r
+    ASSERT(lstrlen(szTimes) < TIMELENGTH);\r
+\r
+    // Put the times in the middle at the bottom of the window\r
+\r
+    GetTextExtentPoint32(m_hdc,szTimes,lstrlen(szTimes),&Size);\r
+    INT XPos = ((ClientRect.right - ClientRect.left) - Size.cx) / 2;\r
+    INT YPos = ((ClientRect.bottom - ClientRect.top) - Size.cy) * 4 / 5;\r
+\r
+    // Check the window is big enough to have sample times displayed\r
+\r
+    if ((XPos > 0) && (YPos > 0)) {\r
+        TextOut(m_hdc,XPos,YPos,szTimes,lstrlen(szTimes));\r
+    }\r
+}\r
+\r
+\r
+// This is called when the drawing code sees that the image has a down level\r
+// palette cookie. We simply call the SetDIBColorTable Windows API with the\r
+// palette that is found after the BITMAPINFOHEADER - we return no errors\r
+\r
+void CDrawImage::UpdateColourTable(HDC hdc,__in BITMAPINFOHEADER *pbmi)\r
+{\r
+    ASSERT(pbmi->biClrUsed);\r
+    RGBQUAD *pColourTable = (RGBQUAD *)(pbmi+1);\r
+\r
+    // Set the new palette in the device context\r
+\r
+    UINT uiReturn = SetDIBColorTable(hdc,(UINT) 0,\r
+                                     pbmi->biClrUsed,\r
+                                     pColourTable);\r
+\r
+    // Should always succeed but check in debug builds\r
+    ASSERT(uiReturn == pbmi->biClrUsed);\r
+}\r
+\r
+\r
+// No source rectangle scaling is done by the base class\r
+\r
+RECT CDrawImage::ScaleSourceRect(const RECT *pSource)\r
+{\r
+    ASSERT(pSource);\r
+    return *pSource;\r
+}\r
+\r
+\r
+// This is called when the funky output pin uses our allocator. The samples we\r
+// allocate are special because the memory is shared between us and GDI thus\r
+// removing one copy when we ask for the image to be rendered. The source type\r
+// information is in the main renderer m_mtIn field which is initialised when\r
+// the media type is agreed in SetMediaType, the media type may be changed on\r
+// the fly if, for example, the source filter needs to change the palette\r
+\r
+void CDrawImage::FastRender(IMediaSample *pMediaSample)\r
+{\r
+    BITMAPINFOHEADER *pbmi;     // Image format data\r
+    DIBDATA *pDibData;          // Stores DIB information\r
+    BYTE *pImage;               // Pointer to image data\r
+    HBITMAP hOldBitmap;         // Store the old bitmap\r
+    CImageSample *pSample;      // Pointer to C++ object\r
+\r
+    ASSERT(m_pMediaType);\r
+\r
+    // From the untyped source format block get the VIDEOINFO and subsequently\r
+    // the BITMAPINFOHEADER structure. We can cast the IMediaSample interface\r
+    // to a CImageSample object so we can retrieve it's DIBSECTION details\r
+\r
+    pbmi = HEADER(m_pMediaType->Format());\r
+    pSample = (CImageSample *) pMediaSample;\r
+    pDibData = pSample->GetDIBData();\r
+    hOldBitmap = (HBITMAP) SelectObject(m_MemoryDC,pDibData->hBitmap);\r
+\r
+    // Get a pointer to the real image data\r
+\r
+    HRESULT hr = pMediaSample->GetPointer(&pImage);\r
+    if (FAILED(hr)) {\r
+        return;\r
+    }\r
+\r
+    // Do we need to update the colour table, we increment our palette cookie\r
+    // each time we get a dynamic format change. The sample palette cookie is\r
+    // stored in the DIBDATA structure so we try to keep the fields in sync\r
+    // By the time we get to draw the images the format change will be done\r
+    // so all we do is ask the renderer for what it's palette version is\r
+\r
+    if (pDibData->PaletteVersion < GetPaletteVersion()) {\r
+        ASSERT(pbmi->biBitCount <= iPALETTE);\r
+        UpdateColourTable(m_MemoryDC,pbmi);\r
+        pDibData->PaletteVersion = GetPaletteVersion();\r
+    }\r
+\r
+    // This allows derived classes to change the source rectangle that we do\r
+    // the drawing with. For example a renderer may ask a codec to stretch\r
+    // the video from 320x240 to 640x480, in which case the source we see in\r
+    // here will still be 320x240, although the source we want to draw with\r
+    // should be scaled up to 640x480. The base class implementation of this\r
+    // method does nothing but return the same rectangle as we are passed in\r
+\r
+    RECT SourceRect = ScaleSourceRect(&m_SourceRect);\r
+\r
+    // Is the window the same size as the video\r
+\r
+    if (m_bStretch == FALSE) {\r
+\r
+        // Put the image straight into the window\r
+\r
+        BitBlt(\r
+            (HDC) m_hdc,                            // Target device HDC\r
+            m_TargetRect.left,                      // X sink position\r
+            m_TargetRect.top,                       // Y sink position\r
+            m_TargetRect.right - m_TargetRect.left, // Destination width\r
+            m_TargetRect.bottom - m_TargetRect.top, // Destination height\r
+            m_MemoryDC,                             // Source device context\r
+            SourceRect.left,                        // X source position\r
+            SourceRect.top,                         // Y source position\r
+            SRCCOPY);                               // Simple copy\r
+\r
+    } else {\r
+\r
+        // Stretch the image when copying to the window\r
+\r
+        StretchBlt(\r
+            (HDC) m_hdc,                            // Target device HDC\r
+            m_TargetRect.left,                      // X sink position\r
+            m_TargetRect.top,                       // Y sink position\r
+            m_TargetRect.right - m_TargetRect.left, // Destination width\r
+            m_TargetRect.bottom - m_TargetRect.top, // Destination height\r
+            m_MemoryDC,                             // Source device HDC\r
+            SourceRect.left,                        // X source position\r
+            SourceRect.top,                         // Y source position\r
+            SourceRect.right - SourceRect.left,     // Source width\r
+            SourceRect.bottom - SourceRect.top,     // Source height\r
+            SRCCOPY);                               // Simple copy\r
+    }\r
+\r
+    // This displays the sample times over the top of the image. This used to\r
+    // draw the times into the offscreen device context however that actually\r
+    // writes the text into the image data buffer which may not be writable\r
+\r
+    #ifdef DEBUG\r
+    DisplaySampleTimes(pMediaSample);\r
+    #endif\r
+\r
+    // Put the old bitmap back into the device context so we don't leak\r
+    SelectObject(m_MemoryDC,hOldBitmap);\r
+}\r
+\r
+\r
+// This is called when there is a sample ready to be drawn, unfortunately the\r
+// output pin was being rotten and didn't choose our super excellent shared\r
+// memory DIB allocator so we have to do this slow render using boring old GDI\r
+// SetDIBitsToDevice and StretchDIBits. The down side of using these GDI\r
+// functions is that the image data has to be copied across from our address\r
+// space into theirs before going to the screen (although in reality the cost\r
+// is small because all they do is to map the buffer into their address space)\r
+\r
+void CDrawImage::SlowRender(IMediaSample *pMediaSample)\r
+{\r
+    // Get the BITMAPINFOHEADER for the connection\r
+\r
+    ASSERT(m_pMediaType);\r
+    BITMAPINFOHEADER *pbmi = HEADER(m_pMediaType->Format());\r
+    BYTE *pImage;\r
+\r
+    // Get the image data buffer\r
+\r
+    HRESULT hr = pMediaSample->GetPointer(&pImage);\r
+    if (FAILED(hr)) {\r
+        return;\r
+    }\r
+\r
+    // This allows derived classes to change the source rectangle that we do\r
+    // the drawing with. For example a renderer may ask a codec to stretch\r
+    // the video from 320x240 to 640x480, in which case the source we see in\r
+    // here will still be 320x240, although the source we want to draw with\r
+    // should be scaled up to 640x480. The base class implementation of this\r
+    // method does nothing but return the same rectangle as we are passed in\r
+\r
+    RECT SourceRect = ScaleSourceRect(&m_SourceRect);\r
+\r
+    LONG lAdjustedSourceTop = SourceRect.top;\r
+    // if the origin of bitmap is bottom-left, adjust soruce_rect_top\r
+    // to be the bottom-left corner instead of the top-left.\r
+    if (pbmi->biHeight > 0) {\r
+       lAdjustedSourceTop = pbmi->biHeight - SourceRect.bottom;\r
+    }\r
+    // Is the window the same size as the video\r
+\r
+    if (m_bStretch == FALSE) {\r
+\r
+        // Put the image straight into the window\r
+\r
+        SetDIBitsToDevice(\r
+            (HDC) m_hdc,                            // Target device HDC\r
+            m_TargetRect.left,                      // X sink position\r
+            m_TargetRect.top,                       // Y sink position\r
+            m_TargetRect.right - m_TargetRect.left, // Destination width\r
+            m_TargetRect.bottom - m_TargetRect.top, // Destination height\r
+            SourceRect.left,                        // X source position\r
+            lAdjustedSourceTop,                     // Adjusted Y source position\r
+            (UINT) 0,                               // Start scan line\r
+            pbmi->biHeight,                         // Scan lines present\r
+            pImage,                                 // Image data\r
+            (BITMAPINFO *) pbmi,                    // DIB header\r
+            DIB_RGB_COLORS);                        // Type of palette\r
+\r
+    } else {\r
+\r
+        // Stretch the image when copying to the window\r
+\r
+        StretchDIBits(\r
+            (HDC) m_hdc,                            // Target device HDC\r
+            m_TargetRect.left,                      // X sink position\r
+            m_TargetRect.top,                       // Y sink position\r
+            m_TargetRect.right - m_TargetRect.left, // Destination width\r
+            m_TargetRect.bottom - m_TargetRect.top, // Destination height\r
+            SourceRect.left,                        // X source position\r
+            lAdjustedSourceTop,                     // Adjusted Y source position\r
+            SourceRect.right - SourceRect.left,     // Source width\r
+            SourceRect.bottom - SourceRect.top,     // Source height\r
+            pImage,                                 // Image data\r
+            (BITMAPINFO *) pbmi,                    // DIB header\r
+            DIB_RGB_COLORS,                         // Type of palette\r
+            SRCCOPY);                               // Simple image copy\r
+    }\r
+\r
+    // This shows the sample reference times over the top of the image which\r
+    // looks a little flickery. I tried using GdiSetBatchLimit and GdiFlush to\r
+    // control the screen updates but it doesn't quite work as expected and\r
+    // only partially reduces the flicker. I also tried using a memory context\r
+    // and combining the two in that before doing a final BitBlt operation to\r
+    // the screen, unfortunately this has considerable performance penalties\r
+    // and also means that this code is not executed when compiled retail\r
+\r
+    #ifdef DEBUG\r
+    DisplaySampleTimes(pMediaSample);\r
+    #endif\r
+}\r
+\r
+\r
+// This is called with an IMediaSample interface on the image to be drawn. We\r
+// decide on the drawing mechanism based on who's allocator we are using. We\r
+// may be called when the window wants an image painted by WM_PAINT messages\r
+// We can't realise the palette here because we have the renderer lock, any\r
+// call to realise may cause an interthread send message to the window thread\r
+// which may in turn be waiting to get the renderer lock before servicing it\r
+\r
+BOOL CDrawImage::DrawImage(IMediaSample *pMediaSample)\r
+{\r
+    ASSERT(m_hdc);\r
+    ASSERT(m_MemoryDC);\r
+    NotifyStartDraw();\r
+\r
+    // If the output pin used our allocator then the samples passed are in\r
+    // fact CVideoSample objects that contain CreateDIBSection data that we\r
+    // use to do faster image rendering, they may optionally also contain a\r
+    // DirectDraw surface pointer in which case we do not do the drawing\r
+\r
+    if (m_bUsingImageAllocator == FALSE) {\r
+        SlowRender(pMediaSample);\r
+        EXECUTE_ASSERT(GdiFlush());\r
+        NotifyEndDraw();\r
+        return TRUE;\r
+    }\r
+\r
+    // This is a DIBSECTION buffer\r
+\r
+    FastRender(pMediaSample);\r
+    EXECUTE_ASSERT(GdiFlush());\r
+    NotifyEndDraw();\r
+    return TRUE;\r
+}\r
+\r
+\r
+BOOL CDrawImage::DrawVideoImageHere(\r
+    HDC hdc,\r
+    IMediaSample *pMediaSample,\r
+    __in LPRECT lprcSrc,\r
+    __in LPRECT lprcDst\r
+    )\r
+{\r
+    ASSERT(m_pMediaType);\r
+    BITMAPINFOHEADER *pbmi = HEADER(m_pMediaType->Format());\r
+    BYTE *pImage;\r
+\r
+    // Get the image data buffer\r
+\r
+    HRESULT hr = pMediaSample->GetPointer(&pImage);\r
+    if (FAILED(hr)) {\r
+        return FALSE;\r
+    }\r
+\r
+    RECT SourceRect;\r
+    RECT TargetRect;\r
+\r
+    if (lprcSrc) {\r
+        SourceRect = *lprcSrc;\r
+    }\r
+    else  SourceRect = ScaleSourceRect(&m_SourceRect);\r
+\r
+    if (lprcDst) {\r
+        TargetRect = *lprcDst;\r
+    }\r
+    else  TargetRect = m_TargetRect;\r
+\r
+    LONG lAdjustedSourceTop = SourceRect.top;\r
+    // if the origin of bitmap is bottom-left, adjust soruce_rect_top\r
+    // to be the bottom-left corner instead of the top-left.\r
+    if (pbmi->biHeight > 0) {\r
+       lAdjustedSourceTop = pbmi->biHeight - SourceRect.bottom;\r
+    }\r
+\r
+\r
+    // Stretch the image when copying to the DC\r
+\r
+    BOOL bRet = (0 != StretchDIBits(hdc,\r
+                                    TargetRect.left,\r
+                                    TargetRect.top,\r
+                                    TargetRect.right - TargetRect.left,\r
+                                    TargetRect.bottom - TargetRect.top,\r
+                                    SourceRect.left,\r
+                                    lAdjustedSourceTop,\r
+                                    SourceRect.right - SourceRect.left,\r
+                                    SourceRect.bottom - SourceRect.top,\r
+                                    pImage,\r
+                                    (BITMAPINFO *)pbmi,\r
+                                    DIB_RGB_COLORS,\r
+                                    SRCCOPY));\r
+    return bRet;\r
+}\r
+\r
+\r
+// This is called by the owning window object after it has created the window\r
+// and it's drawing contexts. We are constructed with the base window we'll\r
+// be drawing into so when given the notification we retrive the device HDCs\r
+// to draw with. We cannot call these in our constructor as they are virtual\r
+\r
+void CDrawImage::SetDrawContext()\r
+{\r
+    m_MemoryDC = m_pBaseWindow->GetMemoryHDC();\r
+    m_hdc = m_pBaseWindow->GetWindowHDC();\r
+}\r
+\r
+\r
+// This is called to set the target rectangle in the video window, it will be\r
+// called whenever a WM_SIZE message is retrieved from the message queue. We\r
+// simply store the rectangle and use it later when we do the drawing calls\r
+\r
+void CDrawImage::SetTargetRect(__in RECT *pTargetRect)\r
+{\r
+    ASSERT(pTargetRect);\r
+    m_TargetRect = *pTargetRect;\r
+    SetStretchMode();\r
+}\r
+\r
+\r
+// Return the current target rectangle\r
+\r
+void CDrawImage::GetTargetRect(__out RECT *pTargetRect)\r
+{\r
+    ASSERT(pTargetRect);\r
+    *pTargetRect = m_TargetRect;\r
+}\r
+\r
+\r
+// This is called when we want to change the section of the image to draw. We\r
+// use this information in the drawing operation calls later on. We must also\r
+// see if the source and destination rectangles have the same dimensions. If\r
+// not we must stretch during the drawing rather than a direct pixel copy\r
+\r
+void CDrawImage::SetSourceRect(__in RECT *pSourceRect)\r
+{\r
+    ASSERT(pSourceRect);\r
+    m_SourceRect = *pSourceRect;\r
+    SetStretchMode();\r
+}\r
+\r
+\r
+// Return the current source rectangle\r
+\r
+void CDrawImage::GetSourceRect(__out RECT *pSourceRect)\r
+{\r
+    ASSERT(pSourceRect);\r
+    *pSourceRect = m_SourceRect;\r
+}\r
+\r
+\r
+// This is called when either the source or destination rectanges change so we\r
+// can update the stretch flag. If the rectangles don't match we stretch the\r
+// video during the drawing otherwise we call the fast pixel copy functions\r
+// NOTE the source and/or the destination rectangle may be completely empty\r
+\r
+void CDrawImage::SetStretchMode()\r
+{\r
+    // Calculate the overall rectangle dimensions\r
+\r
+    LONG SourceWidth = m_SourceRect.right - m_SourceRect.left;\r
+    LONG SinkWidth = m_TargetRect.right - m_TargetRect.left;\r
+    LONG SourceHeight = m_SourceRect.bottom - m_SourceRect.top;\r
+    LONG SinkHeight = m_TargetRect.bottom - m_TargetRect.top;\r
+\r
+    m_bStretch = TRUE;\r
+    if (SourceWidth == SinkWidth) {\r
+        if (SourceHeight == SinkHeight) {\r
+            m_bStretch = FALSE;\r
+        }\r
+    }\r
+}\r
+\r
+\r
+// Tell us whose allocator we are using. This should be called with TRUE if\r
+// the filter agrees to use an allocator based around the CImageAllocator\r
+// SDK base class - whose image buffers are made through CreateDIBSection.\r
+// Otherwise this should be called with FALSE and we will draw the images\r
+// using SetDIBitsToDevice and StretchDIBitsToDevice. None of these calls\r
+// can handle buffers which have non zero strides (like DirectDraw uses)\r
+\r
+void CDrawImage::NotifyAllocator(BOOL bUsingImageAllocator)\r
+{\r
+    m_bUsingImageAllocator = bUsingImageAllocator;\r
+}\r
+\r
+\r
+// Are we using the image DIBSECTION allocator\r
+\r
+BOOL CDrawImage::UsingImageAllocator()\r
+{\r
+    return m_bUsingImageAllocator;\r
+}\r
+\r
+\r
+// We need the media type of the connection so that we can get the BITMAPINFO\r
+// from it. We use that in the calls to draw the image such as StretchDIBits\r
+// and also when updating the colour table held in shared memory DIBSECTIONs\r
+\r
+void CDrawImage::NotifyMediaType(__in CMediaType *pMediaType)\r
+{\r
+    m_pMediaType = pMediaType;\r
+}\r
+\r
+\r
+// We store in this object a cookie maintaining the current palette version.\r
+// Each time a palettised format is changed we increment this value so that\r
+// when we come to draw the images we look at the colour table value they\r
+// have and if less than the current we know to update it. This version is\r
+// only needed and indeed used when working with shared memory DIBSECTIONs\r
+\r
+LONG CDrawImage::GetPaletteVersion()\r
+{\r
+    return m_PaletteVersion;\r
+}\r
+\r
+\r
+// Resets the current palette version number\r
+\r
+void CDrawImage::ResetPaletteVersion()\r
+{\r
+    m_PaletteVersion = PALETTE_VERSION;\r
+}\r
+\r
+\r
+// Increment the current palette version\r
+\r
+void CDrawImage::IncrementPaletteVersion()\r
+{\r
+    m_PaletteVersion++;\r
+}\r
+\r
+\r
+// Constructor must initialise the base allocator. Each sample we create has a\r
+// palette version cookie on board. When the source filter changes the palette\r
+// during streaming the window object increments an internal cookie counter it\r
+// keeps as well. When it comes to render the samples it looks at the cookie\r
+// values and if they don't match then it knows to update the sample's colour\r
+// table. However we always create samples with a cookie of PALETTE_VERSION\r
+// If there have been multiple format changes and we disconnect and reconnect\r
+// thereby causing the samples to be reallocated we will create them with a\r
+// cookie much lower than the current version, this isn't a problem since it\r
+// will be seen by the window object and the versions will then be updated\r
+\r
+CImageAllocator::CImageAllocator(__inout CBaseFilter *pFilter,\r
+                                 __in_opt LPCTSTR pName,\r
+                                 __inout HRESULT *phr) :\r
+    CBaseAllocator(pName,NULL,phr,TRUE,TRUE),\r
+    m_pFilter(pFilter)\r
+{\r
+    ASSERT(phr);\r
+    ASSERT(pFilter);\r
+}\r
+\r
+\r
+// Check our DIB buffers have been released\r
+\r
+#ifdef DEBUG\r
+CImageAllocator::~CImageAllocator()\r
+{\r
+    ASSERT(m_bCommitted == FALSE);\r
+}\r
+#endif\r
+\r
+\r
+// Called from destructor and also from base class to free resources. We work\r
+// our way through the list of media samples deleting the DIBSECTION created\r
+// for each. All samples should be back in our list so there is no chance a\r
+// filter is still using one to write on the display or hold on a pending list\r
+\r
+void CImageAllocator::Free()\r
+{\r
+    ASSERT(m_lAllocated == m_lFree.GetCount());\r
+    EXECUTE_ASSERT(GdiFlush());\r
+    CImageSample *pSample;\r
+    DIBDATA *pDibData;\r
+\r
+    while (m_lFree.GetCount() != 0) {\r
+        pSample = (CImageSample *) m_lFree.RemoveHead();\r
+        pDibData = pSample->GetDIBData();\r
+        EXECUTE_ASSERT(DeleteObject(pDibData->hBitmap));\r
+        EXECUTE_ASSERT(CloseHandle(pDibData->hMapping));\r
+        delete pSample;\r
+    }\r
+\r
+    m_lAllocated = 0;\r
+}\r
+\r
+\r
+// Prepare the allocator by checking all the input parameters\r
+\r
+STDMETHODIMP CImageAllocator::CheckSizes(__in ALLOCATOR_PROPERTIES *pRequest)\r
+{\r
+    // Check we have a valid connection\r
+\r
+    if (m_pMediaType == NULL) {\r
+        return VFW_E_NOT_CONNECTED;\r
+    }\r
+\r
+    // NOTE We always create a DIB section with the source format type which\r
+    // may contain a source palette. When we do the BitBlt drawing operation\r
+    // the target display device may contain a different palette (we may not\r
+    // have the focus) in which case GDI will do after the palette mapping\r
+\r
+    VIDEOINFOHEADER *pVideoInfo = (VIDEOINFOHEADER *) m_pMediaType->Format();\r
+\r
+    // When we call CreateDIBSection it implicitly maps only enough memory\r
+    // for the image as defined by thee BITMAPINFOHEADER. If the user asks\r
+    // for an image smaller than this then we reject the call, if they ask\r
+    // for an image larger than this then we return what they can have\r
+\r
+    if ((DWORD) pRequest->cbBuffer < pVideoInfo->bmiHeader.biSizeImage) {\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+    // Reject buffer prefixes\r
+\r
+    if (pRequest->cbPrefix > 0) {\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+    pRequest->cbBuffer = pVideoInfo->bmiHeader.biSizeImage;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Agree the number of media sample buffers and their sizes. The base class\r
+// this allocator is derived from allows samples to be aligned only on byte\r
+// boundaries NOTE the buffers are not allocated until the Commit call\r
+\r
+STDMETHODIMP CImageAllocator::SetProperties(\r
+    __in ALLOCATOR_PROPERTIES * pRequest,\r
+    __out ALLOCATOR_PROPERTIES * pActual)\r
+{\r
+    ALLOCATOR_PROPERTIES Adjusted = *pRequest;\r
+\r
+    // Check the parameters fit with the current connection\r
+\r
+    HRESULT hr = CheckSizes(&Adjusted);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    return CBaseAllocator::SetProperties(&Adjusted, pActual);\r
+}\r
+\r
+\r
+// Commit the memory by allocating the agreed number of media samples. For\r
+// each sample we are committed to creating we have a CImageSample object\r
+// that we use to manage it's resources. This is initialised with a DIBDATA\r
+// structure that contains amongst other things the GDI DIBSECTION handle\r
+// We will access the renderer media type during this so we must have locked\r
+// (to prevent the format changing for example). The class overrides Commit\r
+// and Decommit to do this locking (base class Commit in turn calls Alloc)\r
+\r
+HRESULT CImageAllocator::Alloc(void)\r
+{\r
+    ASSERT(m_pMediaType);\r
+    CImageSample *pSample;\r
+    DIBDATA DibData;\r
+\r
+    // Check the base allocator says it's ok to continue\r
+\r
+    HRESULT hr = CBaseAllocator::Alloc();\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+\r
+    // We create a new memory mapped object although we don't map it into our\r
+    // address space because GDI does that in CreateDIBSection. It is possible\r
+    // that we run out of resources before creating all the samples in which\r
+    // case the available sample list is left with those already created\r
+\r
+    ASSERT(m_lAllocated == 0);\r
+    while (m_lAllocated < m_lCount) {\r
+\r
+        // Create and initialise a shared memory GDI buffer\r
+\r
+        hr = CreateDIB(m_lSize,DibData);\r
+        if (FAILED(hr)) {\r
+            return hr;\r
+        }\r
+\r
+        // Create the sample object and pass it the DIBDATA\r
+\r
+        pSample = CreateImageSample(DibData.pBase,m_lSize);\r
+        if (pSample == NULL) {\r
+            EXECUTE_ASSERT(DeleteObject(DibData.hBitmap));\r
+            EXECUTE_ASSERT(CloseHandle(DibData.hMapping));\r
+            return E_OUTOFMEMORY;\r
+        }\r
+\r
+        // Add the completed sample to the available list\r
+\r
+        pSample->SetDIBData(&DibData);\r
+        m_lFree.Add(pSample);\r
+        m_lAllocated++;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// We have a virtual method that allocates the samples so that a derived class\r
+// may override it and allocate more specialised sample objects. So long as it\r
+// derives its samples from CImageSample then all this code will still work ok\r
+\r
+CImageSample *CImageAllocator::CreateImageSample(__in_bcount(Length) LPBYTE pData,LONG Length)\r
+{\r
+    HRESULT hr = NOERROR;\r
+    CImageSample *pSample;\r
+\r
+    // Allocate the new sample and check the return codes\r
+\r
+    pSample = new CImageSample((CBaseAllocator *) this,   // Base class\r
+                               NAME("Video sample"),      // DEBUG name\r
+                               (HRESULT *) &hr,           // Return code\r
+                               (LPBYTE) pData,            // DIB address\r
+                               (LONG) Length);            // Size of DIB\r
+\r
+    if (pSample == NULL || FAILED(hr)) {\r
+        delete pSample;\r
+        return NULL;\r
+    }\r
+    return pSample;\r
+}\r
+\r
+\r
+// This function allocates a shared memory block for use by the source filter\r
+// generating DIBs for us to render. The memory block is created in shared\r
+// memory so that GDI doesn't have to copy the memory when we do a BitBlt\r
+\r
+HRESULT CImageAllocator::CreateDIB(LONG InSize,DIBDATA &DibData)\r
+{\r
+    BITMAPINFO *pbmi;       // Format information for pin\r
+    BYTE *pBase;            // Pointer to the actual image\r
+    HANDLE hMapping;        // Handle to mapped object\r
+    HBITMAP hBitmap;        // DIB section bitmap handle\r
+\r
+    // Create a file mapping object and map into our address space\r
+\r
+    hMapping = CreateFileMapping(hMEMORY,         // Use system page file\r
+                                 NULL,            // No security attributes\r
+                                 PAGE_READWRITE,  // Full access to memory\r
+                                 (DWORD) 0,       // Less than 4Gb in size\r
+                                 InSize,          // Size of buffer\r
+                                 NULL);           // No name to section\r
+    if (hMapping == NULL) {\r
+        DWORD Error = GetLastError();\r
+        return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, Error);\r
+    }\r
+\r
+    // NOTE We always create a DIB section with the source format type which\r
+    // may contain a source palette. When we do the BitBlt drawing operation\r
+    // the target display device may contain a different palette (we may not\r
+    // have the focus) in which case GDI will do after the palette mapping\r
+\r
+    pbmi = (BITMAPINFO *) HEADER(m_pMediaType->Format());\r
+    if (m_pMediaType == NULL) {\r
+        DbgBreak("Invalid media type");\r
+    }\r
+\r
+    hBitmap = CreateDIBSection((HDC) NULL,          // NO device context\r
+                               pbmi,                // Format information\r
+                               DIB_RGB_COLORS,      // Use the palette\r
+                               (VOID **) &pBase,    // Pointer to image data\r
+                               hMapping,            // Mapped memory handle\r
+                               (DWORD) 0);          // Offset into memory\r
+\r
+    if (hBitmap == NULL || pBase == NULL) {\r
+        EXECUTE_ASSERT(CloseHandle(hMapping));\r
+        DWORD Error = GetLastError();\r
+        return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, Error);\r
+    }\r
+\r
+    // Initialise the DIB information structure\r
+\r
+    DibData.hBitmap = hBitmap;\r
+    DibData.hMapping = hMapping;\r
+    DibData.pBase = pBase;\r
+    DibData.PaletteVersion = PALETTE_VERSION;\r
+    GetObject(hBitmap,sizeof(DIBSECTION),(VOID *)&DibData.DibSection);\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// We use the media type during the DIBSECTION creation\r
+\r
+void CImageAllocator::NotifyMediaType(__in CMediaType *pMediaType)\r
+{\r
+    m_pMediaType = pMediaType;\r
+}\r
+\r
+\r
+// Overriden to increment the owning object's reference count\r
+\r
+STDMETHODIMP_(ULONG) CImageAllocator::NonDelegatingAddRef()\r
+{\r
+    return m_pFilter->AddRef();\r
+}\r
+\r
+\r
+// Overriden to decrement the owning object's reference count\r
+\r
+STDMETHODIMP_(ULONG) CImageAllocator::NonDelegatingRelease()\r
+{\r
+    return m_pFilter->Release();\r
+}\r
+\r
+\r
+// If you derive a class from CMediaSample that has to transport specialised\r
+// member variables and entry points then there are three alternate solutions\r
+// The first is to create a memory buffer larger than actually required by the\r
+// sample and store your information either at the beginning of it or at the\r
+// end, the former being moderately safer allowing for misbehaving transform\r
+// filters. You then adjust the buffer address when you create the base media\r
+// sample. This has the disadvantage of breaking up the memory allocated to\r
+// the samples into separate blocks. The second solution is to implement a\r
+// class derived from CMediaSample and support additional interface(s) that\r
+// convey your private data. This means defining a custom interface. The final\r
+// alternative is to create a class that inherits from CMediaSample and adds\r
+// the private data structures, when you get an IMediaSample in your Receive()\r
+// call check to see if your allocator is being used, and if it is then cast\r
+// the IMediaSample into one of your objects. Additional checks can be made\r
+// to ensure the sample's this pointer is known to be one of your own objects\r
+\r
+CImageSample::CImageSample(__inout CBaseAllocator *pAllocator,\r
+                           __in_opt LPCTSTR pName,\r
+                           __inout HRESULT *phr,\r
+                           __in_bcount(length) LPBYTE pBuffer,\r
+                           LONG length) :\r
+    CMediaSample(pName,pAllocator,phr,pBuffer,length),\r
+    m_bInit(FALSE)\r
+{\r
+    ASSERT(pAllocator);\r
+    ASSERT(pBuffer);\r
+}\r
+\r
+\r
+// Set the shared memory DIB information\r
+\r
+void CImageSample::SetDIBData(__in DIBDATA *pDibData)\r
+{\r
+    ASSERT(pDibData);\r
+    m_DibData = *pDibData;\r
+    m_bInit = TRUE;\r
+}\r
+\r
+\r
+// Retrieve the shared memory DIB data\r
+\r
+__out DIBDATA *CImageSample::GetDIBData()\r
+{\r
+    ASSERT(m_bInit == TRUE);\r
+    return &m_DibData;\r
+}\r
+\r
+\r
+// This class handles the creation of a palette. It is fairly specialist and\r
+// is intended to simplify palette management for video renderer filters. It\r
+// is for this reason that the constructor requires three other objects with\r
+// which it interacts, namely a base media filter, a base window and a base\r
+// drawing object although the base window or the draw object may be NULL to\r
+// ignore that part of us. We try not to create and install palettes unless\r
+// absolutely necessary as they typically require WM_PALETTECHANGED messages\r
+// to be sent to every window thread in the system which is very expensive\r
+\r
+CImagePalette::CImagePalette(__inout CBaseFilter *pBaseFilter,\r
+                             __inout CBaseWindow *pBaseWindow,\r
+                             __inout CDrawImage *pDrawImage) :\r
+    m_pBaseWindow(pBaseWindow),\r
+    m_pFilter(pBaseFilter),\r
+    m_pDrawImage(pDrawImage),\r
+    m_hPalette(NULL)\r
+{\r
+    ASSERT(m_pFilter);\r
+}\r
+\r
+\r
+// Destructor\r
+\r
+#ifdef DEBUG\r
+CImagePalette::~CImagePalette()\r
+{\r
+    ASSERT(m_hPalette == NULL);\r
+}\r
+#endif\r
+\r
+\r
+// We allow dynamic format changes of the palette but rather than change the\r
+// palette every time we call this to work out whether an update is required.\r
+// If the original type didn't use a palette and the new one does (or vica\r
+// versa) then we return TRUE. If neither formats use a palette we'll return\r
+// FALSE. If both formats use a palette we compare their colours and return\r
+// FALSE if they match. This therefore short circuits palette creation unless\r
+// absolutely necessary since installing palettes is an expensive operation\r
+\r
+BOOL CImagePalette::ShouldUpdate(const VIDEOINFOHEADER *pNewInfo,\r
+                                 const VIDEOINFOHEADER *pOldInfo)\r
+{\r
+    // We may not have a current format yet\r
+\r
+    if (pOldInfo == NULL) {\r
+        return TRUE;\r
+    }\r
+\r
+    // Do both formats not require a palette\r
+\r
+    if (ContainsPalette(pNewInfo) == FALSE) {\r
+        if (ContainsPalette(pOldInfo) == FALSE) {\r
+            return FALSE;\r
+        }\r
+    }\r
+\r
+    // Compare the colours to see if they match\r
+\r
+    DWORD VideoEntries = pNewInfo->bmiHeader.biClrUsed;\r
+    if (ContainsPalette(pNewInfo) == TRUE)\r
+        if (ContainsPalette(pOldInfo) == TRUE)\r
+            if (pOldInfo->bmiHeader.biClrUsed == VideoEntries)\r
+                if (pOldInfo->bmiHeader.biClrUsed > 0)\r
+                    if (memcmp((PVOID) GetBitmapPalette(pNewInfo),\r
+                               (PVOID) GetBitmapPalette(pOldInfo),\r
+                               VideoEntries * sizeof(RGBQUAD)) == 0) {\r
+\r
+                        return FALSE;\r
+                    }\r
+    return TRUE;\r
+}\r
+\r
+\r
+// This is normally called when the input pin type is set to install a palette\r
+// We will typically be called from two different places. The first is when we\r
+// have negotiated a palettised media type after connection, the other is when\r
+// we receive a new type during processing with an updated palette in which\r
+// case we must remove and release the resources held by the current palette\r
+\r
+// We can be passed an optional device name if we wish to prepare a palette\r
+// for a specific monitor on a multi monitor system\r
+\r
+HRESULT CImagePalette::PreparePalette(const CMediaType *pmtNew,\r
+                                      const CMediaType *pmtOld,\r
+                                                     __in LPSTR szDevice)\r
+{\r
+    const VIDEOINFOHEADER *pNewInfo = (VIDEOINFOHEADER *) pmtNew->Format();\r
+    const VIDEOINFOHEADER *pOldInfo = (VIDEOINFOHEADER *) pmtOld->Format();\r
+    ASSERT(pNewInfo);\r
+\r
+    // This is an performance optimisation, when we get a media type we check\r
+    // to see if the format requires a palette change. If either we need one\r
+    // when previously we didn't or vica versa then this returns TRUE, if we\r
+    // previously needed a palette and we do now it compares their colours\r
+\r
+    if (ShouldUpdate(pNewInfo,pOldInfo) == FALSE) {\r
+        NOTE("No update needed");\r
+        return S_FALSE;\r
+    }\r
+\r
+    // We must notify the filter graph that the application may have changed\r
+    // the palette although in practice we don't bother checking to see if it\r
+    // is really different. If it tries to get the palette either the window\r
+    // or renderer lock will ensure it doesn't get in until we are finished\r
+\r
+    RemovePalette();\r
+    m_pFilter->NotifyEvent(EC_PALETTE_CHANGED,0,0);\r
+\r
+    // Do we need a palette for the new format\r
+\r
+    if (ContainsPalette(pNewInfo) == FALSE) {\r
+        NOTE("New has no palette");\r
+        return S_FALSE;\r
+    }\r
+\r
+    if (m_pBaseWindow) {\r
+        m_pBaseWindow->LockPaletteLock();\r
+    }\r
+\r
+    // If we're changing the palette on the fly then we increment our palette\r
+    // cookie which is compared against the cookie also stored in all of our\r
+    // DIBSECTION media samples. If they don't match when we come to draw it\r
+    // then we know the sample is out of date and we'll update it's palette\r
+\r
+    NOTE("Making new colour palette");\r
+    m_hPalette = MakePalette(pNewInfo, szDevice);\r
+    ASSERT(m_hPalette != NULL);\r
+\r
+    if (m_pBaseWindow) {\r
+        m_pBaseWindow->UnlockPaletteLock();\r
+    }\r
+\r
+    // The window in which the new palette is to be realised may be a NULL\r
+    // pointer to signal that no window is in use, if so we don't call it\r
+    // Some filters just want to use this object to create/manage palettes\r
+\r
+    if (m_pBaseWindow) m_pBaseWindow->SetPalette(m_hPalette);\r
+\r
+    // This is the only time where we need access to the draw object to say\r
+    // to it that a new palette will be arriving on a sample real soon. The\r
+    // constructor may take a NULL pointer in which case we don't call this\r
+\r
+    if (m_pDrawImage) m_pDrawImage->IncrementPaletteVersion();\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Helper function to copy a palette out of any kind of VIDEOINFO (ie it may\r
+// be YUV or true colour) into a palettised VIDEOINFO. We use this changing\r
+// palettes on DirectDraw samples as a source filter can attach a palette to\r
+// any buffer (eg YUV) and hand it back. We make a new palette out of that\r
+// format and then copy the palette colours into the current connection type\r
+\r
+HRESULT CImagePalette::CopyPalette(const CMediaType *pSrc,__out CMediaType *pDest)\r
+{\r
+    // Reset the destination palette before starting\r
+\r
+    VIDEOINFOHEADER *pDestInfo = (VIDEOINFOHEADER *) pDest->Format();\r
+    pDestInfo->bmiHeader.biClrUsed = 0;\r
+    pDestInfo->bmiHeader.biClrImportant = 0;\r
+\r
+    // Does the destination have a palette\r
+\r
+    if (PALETTISED(pDestInfo) == FALSE) {\r
+        NOTE("No destination palette");\r
+        return S_FALSE;\r
+    }\r
+\r
+    // Does the source contain a palette\r
+\r
+    const VIDEOINFOHEADER *pSrcInfo = (VIDEOINFOHEADER *) pSrc->Format();\r
+    if (ContainsPalette(pSrcInfo) == FALSE) {\r
+        NOTE("No source palette");\r
+        return S_FALSE;\r
+    }\r
+\r
+    // The number of colours may be zero filled\r
+\r
+    DWORD PaletteEntries = pSrcInfo->bmiHeader.biClrUsed;\r
+    if (PaletteEntries == 0) {\r
+        DWORD Maximum  = (1 << pSrcInfo->bmiHeader.biBitCount);\r
+        NOTE1("Setting maximum colours (%d)",Maximum);\r
+        PaletteEntries = Maximum;\r
+    }\r
+\r
+    // Make sure the destination has enough room for the palette\r
+\r
+    ASSERT(pSrcInfo->bmiHeader.biClrUsed <= iPALETTE_COLORS);\r
+    ASSERT(pSrcInfo->bmiHeader.biClrImportant <= PaletteEntries);\r
+    ASSERT(COLORS(pDestInfo) == GetBitmapPalette(pDestInfo));\r
+    pDestInfo->bmiHeader.biClrUsed = PaletteEntries;\r
+    pDestInfo->bmiHeader.biClrImportant = pSrcInfo->bmiHeader.biClrImportant;\r
+    ULONG BitmapSize = GetBitmapFormatSize(HEADER(pSrcInfo));\r
+\r
+    if (pDest->FormatLength() < BitmapSize) {\r
+        NOTE("Reallocating destination");\r
+        pDest->ReallocFormatBuffer(BitmapSize);\r
+    }\r
+\r
+    // Now copy the palette colours across\r
+\r
+    CopyMemory((PVOID) COLORS(pDestInfo),\r
+               (PVOID) GetBitmapPalette(pSrcInfo),\r
+               PaletteEntries * sizeof(RGBQUAD));\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// This is normally called when the palette is changed (typically during a\r
+// dynamic format change) to remove any palette we previously installed. We\r
+// replace it (if necessary) in the video window with a standard VGA palette\r
+// that should always be available even if this is a true colour display\r
+\r
+HRESULT CImagePalette::RemovePalette()\r
+{\r
+    if (m_pBaseWindow) {\r
+        m_pBaseWindow->LockPaletteLock();\r
+    }\r
+\r
+    // Do we have a palette to remove\r
+\r
+    if (m_hPalette != NULL) {\r
+\r
+        if (m_pBaseWindow) {\r
+            // Make sure that the window's palette handle matches\r
+            // our palette handle.\r
+            ASSERT(m_hPalette == m_pBaseWindow->GetPalette());\r
+\r
+            m_pBaseWindow->UnsetPalette();\r
+        }\r
+\r
+        EXECUTE_ASSERT(DeleteObject(m_hPalette));\r
+        m_hPalette = NULL;\r
+    }\r
+\r
+    if (m_pBaseWindow) {\r
+        m_pBaseWindow->UnlockPaletteLock();\r
+    }\r
+\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Called to create a palette for the object, the data structure used by GDI\r
+// to describe a palette is a LOGPALETTE, this includes a variable number of\r
+// PALETTEENTRY fields which are the colours, we have to convert the RGBQUAD\r
+// colour fields we are handed in a BITMAPINFO from the media type into these\r
+// This handles extraction of palettes from true colour and YUV media formats\r
+\r
+// We can be passed an optional device name if we wish to prepare a palette\r
+// for a specific monitor on a multi monitor system\r
+\r
+HPALETTE CImagePalette::MakePalette(const VIDEOINFOHEADER *pVideoInfo, __in LPSTR szDevice)\r
+{\r
+    ASSERT(ContainsPalette(pVideoInfo) == TRUE);\r
+    ASSERT(pVideoInfo->bmiHeader.biClrUsed <= iPALETTE_COLORS);\r
+    BITMAPINFOHEADER *pHeader = HEADER(pVideoInfo);\r
+\r
+    const RGBQUAD *pColours;            // Pointer to the palette\r
+    LOGPALETTE *lp;                     // Used to create a palette\r
+    HPALETTE hPalette;                  // Logical palette object\r
+\r
+    lp = (LOGPALETTE *) new BYTE[sizeof(LOGPALETTE) + SIZE_PALETTE];\r
+    if (lp == NULL) {\r
+        return NULL;\r
+    }\r
+\r
+    // Unfortunately for some hare brained reason a GDI palette entry (a\r
+    // PALETTEENTRY structure) is different to a palette entry from a DIB\r
+    // format (a RGBQUAD structure) so we have to do the field conversion\r
+    // The VIDEOINFO containing the palette may be a true colour type so\r
+    // we use GetBitmapPalette to skip over any bit fields if they exist\r
+\r
+    lp->palVersion = PALVERSION;\r
+    lp->palNumEntries = (USHORT) pHeader->biClrUsed;\r
+    if (lp->palNumEntries == 0) lp->palNumEntries = (1 << pHeader->biBitCount);\r
+    pColours = GetBitmapPalette(pVideoInfo);\r
+\r
+    for (DWORD dwCount = 0;dwCount < lp->palNumEntries;dwCount++) {\r
+        lp->palPalEntry[dwCount].peRed = pColours[dwCount].rgbRed;\r
+        lp->palPalEntry[dwCount].peGreen = pColours[dwCount].rgbGreen;\r
+        lp->palPalEntry[dwCount].peBlue = pColours[dwCount].rgbBlue;\r
+        lp->palPalEntry[dwCount].peFlags = 0;\r
+    }\r
+\r
+    MakeIdentityPalette(lp->palPalEntry, lp->palNumEntries, szDevice);\r
+\r
+    // Create a logical palette\r
+\r
+    hPalette = CreatePalette(lp);\r
+    ASSERT(hPalette != NULL);\r
+    delete[] lp;\r
+    return hPalette;\r
+}\r
+\r
+\r
+// GDI does a fair job of compressing the palette entries you give it, so for\r
+// example if you have five entries with an RGB colour (0,0,0) it will remove\r
+// all but one of them. When you subsequently draw an image it will map from\r
+// your logical palette to the compressed device palette. This function looks\r
+// to see if it is trying to be an identity palette and if so sets the flags\r
+// field in the PALETTEENTRYs so they remain expanded to boost performance\r
+\r
+// We can be passed an optional device name if we wish to prepare a palette\r
+// for a specific monitor on a multi monitor system\r
+\r
+HRESULT CImagePalette::MakeIdentityPalette(__inout_ecount_full(iColours) PALETTEENTRY *pEntry,INT iColours, __in LPSTR szDevice)\r
+{\r
+    PALETTEENTRY SystemEntries[10];         // System palette entries\r
+    BOOL bIdentityPalette = TRUE;           // Is an identity palette\r
+    ASSERT(iColours <= iPALETTE_COLORS);    // Should have a palette\r
+    const int PalLoCount = 10;              // First ten reserved colours\r
+    const int PalHiStart = 246;             // Last VGA palette entries\r
+\r
+    // Does this have the full colour range\r
+\r
+    if (iColours < 10) {\r
+        return S_FALSE;\r
+    }\r
+\r
+    // Apparently some displays have odd numbers of system colours\r
+\r
+    // Get a DC on the right monitor - it's ugly, but this is the way you have\r
+    // to do it\r
+    HDC hdc;\r
+    if (szDevice == NULL || lstrcmpiLocaleIndependentA(szDevice, "DISPLAY") == 0)\r
+        hdc = CreateDCA("DISPLAY", NULL, NULL, NULL);\r
+    else\r
+        hdc = CreateDCA(NULL, szDevice, NULL, NULL);\r
+    if (NULL == hdc) {\r
+        return E_OUTOFMEMORY;\r
+    }\r
+    INT Reserved = GetDeviceCaps(hdc,NUMRESERVED);\r
+    if (Reserved != 20) {\r
+        DeleteDC(hdc);\r
+        return S_FALSE;\r
+    }\r
+\r
+    // Compare our palette against the first ten system entries. The reason I\r
+    // don't do a memory compare between our two arrays of colours is because\r
+    // I am not sure what will be in the flags fields for the system entries\r
+\r
+    UINT Result = GetSystemPaletteEntries(hdc,0,PalLoCount,SystemEntries);\r
+    for (UINT Count = 0;Count < Result;Count++) {\r
+        if (SystemEntries[Count].peRed != pEntry[Count].peRed ||\r
+                SystemEntries[Count].peGreen != pEntry[Count].peGreen ||\r
+                    SystemEntries[Count].peBlue != pEntry[Count].peBlue) {\r
+                        bIdentityPalette = FALSE;\r
+        }\r
+    }\r
+\r
+    // And likewise compare against the last ten entries\r
+\r
+    Result = GetSystemPaletteEntries(hdc,PalHiStart,PalLoCount,SystemEntries);\r
+    for (UINT Count = 0;Count < Result;Count++) {\r
+        if (INT(Count) + PalHiStart < iColours) {\r
+            if (SystemEntries[Count].peRed != pEntry[PalHiStart + Count].peRed ||\r
+                    SystemEntries[Count].peGreen != pEntry[PalHiStart + Count].peGreen ||\r
+                        SystemEntries[Count].peBlue != pEntry[PalHiStart + Count].peBlue) {\r
+                            bIdentityPalette = FALSE;\r
+            }\r
+        }\r
+    }\r
+\r
+    // If not an identity palette then return S_FALSE\r
+\r
+    DeleteDC(hdc);\r
+    if (bIdentityPalette == FALSE) {\r
+        return S_FALSE;\r
+    }\r
+\r
+    // Set the non VGA entries so that GDI doesn't map them\r
+\r
+    for (UINT Count = PalLoCount;INT(Count) < min(PalHiStart,iColours);Count++) {\r
+        pEntry[Count].peFlags = PC_NOCOLLAPSE;\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Constructor initialises the VIDEOINFO we keep storing the current display\r
+// format. The format can be changed at any time, to reset the format held\r
+// by us call the RefreshDisplayType directly (it's a public method). Since\r
+// more than one thread will typically call us (ie window threads resetting\r
+// the type and source threads in the type checking methods) we have a lock\r
+\r
+CImageDisplay::CImageDisplay()\r
+{\r
+    RefreshDisplayType(NULL);\r
+}\r
+\r
+\r
+\r
+// This initialises the format we hold which contains the display device type\r
+// We do a conversion on the display device type in here so that when we start\r
+// type checking input formats we can assume that certain fields have been set\r
+// correctly, an example is when we make the 16 bit mask fields explicit. This\r
+// is normally called when we receive WM_DEVMODECHANGED device change messages\r
+\r
+// The optional szDeviceName parameter tells us which monitor we are interested\r
+// in for a multi monitor system\r
+\r
+HRESULT CImageDisplay::RefreshDisplayType(__in_opt LPSTR szDeviceName)\r
+{\r
+    CAutoLock cDisplayLock(this);\r
+\r
+    // Set the preferred format type\r
+\r
+    ZeroMemory((PVOID)&m_Display,sizeof(VIDEOINFOHEADER)+sizeof(TRUECOLORINFO));\r
+    m_Display.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);\r
+    m_Display.bmiHeader.biBitCount = FALSE;\r
+\r
+    // Get the bit depth of a device compatible bitmap\r
+\r
+    // get caps of whichever monitor they are interested in (multi monitor)\r
+    HDC hdcDisplay;\r
+    // it's ugly, but this is the way you have to do it\r
+    if (szDeviceName == NULL || lstrcmpiLocaleIndependentA(szDeviceName, "DISPLAY") == 0)\r
+        hdcDisplay = CreateDCA("DISPLAY", NULL, NULL, NULL);\r
+    else\r
+        hdcDisplay = CreateDCA(NULL, szDeviceName, NULL, NULL);\r
+    if (hdcDisplay == NULL) {\r
+    ASSERT(FALSE);\r
+    DbgLog((LOG_ERROR,1,TEXT("ACK! Can't get a DC for %hs"),\r
+                szDeviceName ? szDeviceName : "<NULL>"));\r
+    return E_FAIL;\r
+    } else {\r
+    DbgLog((LOG_TRACE,3,TEXT("Created a DC for %s"),\r
+                szDeviceName ? szDeviceName : "<NULL>"));\r
+    }\r
+    HBITMAP hbm = CreateCompatibleBitmap(hdcDisplay,1,1);\r
+    if ( hbm )\r
+    {\r
+        GetDIBits(hdcDisplay,hbm,0,1,NULL,(BITMAPINFO *)&m_Display.bmiHeader,DIB_RGB_COLORS);\r
+\r
+        // This call will get the colour table or the proper bitfields\r
+        GetDIBits(hdcDisplay,hbm,0,1,NULL,(BITMAPINFO *)&m_Display.bmiHeader,DIB_RGB_COLORS);\r
+        DeleteObject(hbm);\r
+    }\r
+    DeleteDC(hdcDisplay);\r
+\r
+    // Complete the display type initialisation\r
+\r
+    ASSERT(CheckHeaderValidity(&m_Display));\r
+    UpdateFormat(&m_Display);\r
+    DbgLog((LOG_TRACE,3,TEXT("New DISPLAY bit depth =%d"),\r
+                m_Display.bmiHeader.biBitCount));\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// We assume throughout this code that any bitfields masks are allowed no\r
+// more than eight bits to store a colour component. This checks that the\r
+// bit count assumption is enforced and also makes sure that all the bits\r
+// set are contiguous. We return a boolean TRUE if the field checks out ok\r
+\r
+BOOL CImageDisplay::CheckBitFields(const VIDEOINFO *pInput)\r
+{\r
+    DWORD *pBitFields = (DWORD *) BITMASKS(pInput);\r
+\r
+    for (INT iColour = iRED;iColour <= iBLUE;iColour++) {\r
+\r
+        // First of all work out how many bits are set\r
+\r
+        DWORD SetBits = CountSetBits(pBitFields[iColour]);\r
+        if (SetBits > iMAXBITS || SetBits == 0) {\r
+            NOTE1("Bit fields for component %d invalid",iColour);\r
+            return FALSE;\r
+        }\r
+\r
+        // Next work out the number of zero bits prefix\r
+        DWORD PrefixBits = CountPrefixBits(pBitFields[iColour]);\r
+\r
+        // This is going to see if all the bits set are contiguous (as they\r
+        // should be). We know how much to shift them right by from the\r
+        // count of prefix bits. The number of bits set defines a mask, we\r
+        // invert this (ones complement) and AND it with the shifted bit\r
+        // fields. If the result is NON zero then there are bit(s) sticking\r
+        // out the left hand end which means they are not contiguous\r
+\r
+        DWORD TestField = pBitFields[iColour] >> PrefixBits;\r
+        DWORD Mask = ULONG_MAX << SetBits;\r
+        if (TestField & Mask) {\r
+            NOTE1("Bit fields for component %d not contiguous",iColour);\r
+            return FALSE;\r
+        }\r
+    }\r
+    return TRUE;\r
+}\r
+\r
+\r
+// This counts the number of bits set in the input field\r
+\r
+DWORD CImageDisplay::CountSetBits(DWORD Field)\r
+{\r
+    // This is a relatively well known bit counting algorithm\r
+\r
+    DWORD Count = 0;\r
+    DWORD init = Field;\r
+\r
+    // Until the input is exhausted, count the number of bits\r
+\r
+    while (init) {\r
+        init = init & (init - 1);  // Turn off the bottommost bit\r
+        Count++;\r
+    }\r
+    return Count;\r
+}\r
+\r
+\r
+// This counts the number of zero bits upto the first one set NOTE the input\r
+// field should have been previously checked to ensure there is at least one\r
+// set although if we don't find one set we return the impossible value 32\r
+\r
+DWORD CImageDisplay::CountPrefixBits(DWORD Field)\r
+{\r
+    DWORD Mask = 1;\r
+    DWORD Count = 0;\r
+\r
+    while (TRUE) {\r
+        if (Field & Mask) {\r
+            return Count;\r
+        }\r
+        Count++;\r
+\r
+        ASSERT(Mask != 0x80000000);\r
+        if (Mask == 0x80000000) {\r
+            return Count;\r
+        }\r
+        Mask <<= 1;\r
+    }\r
+}\r
+\r
+\r
+// This is called to check the BITMAPINFOHEADER for the input type. There are\r
+// many implicit dependancies between the fields in a header structure which\r
+// if we validate now make for easier manipulation in subsequent handling. We\r
+// also check that the BITMAPINFOHEADER matches it's specification such that\r
+// fields likes the number of planes is one, that it's structure size is set\r
+// correctly and that the bitmap dimensions have not been set as negative\r
+\r
+BOOL CImageDisplay::CheckHeaderValidity(const VIDEOINFO *pInput)\r
+{\r
+    // Check the bitmap width and height are not negative.\r
+\r
+    if (pInput->bmiHeader.biWidth <= 0 ||\r
+    pInput->bmiHeader.biHeight <= 0) {\r
+        NOTE("Invalid bitmap dimensions");\r
+        return FALSE;\r
+    }\r
+\r
+    // Check the compression is either BI_RGB or BI_BITFIELDS\r
+\r
+    if (pInput->bmiHeader.biCompression != BI_RGB) {\r
+        if (pInput->bmiHeader.biCompression != BI_BITFIELDS) {\r
+            NOTE("Invalid compression format");\r
+            return FALSE;\r
+        }\r
+    }\r
+\r
+    // If BI_BITFIELDS compression format check the colour depth\r
+\r
+    if (pInput->bmiHeader.biCompression == BI_BITFIELDS) {\r
+        if (pInput->bmiHeader.biBitCount != 16) {\r
+            if (pInput->bmiHeader.biBitCount != 32) {\r
+                NOTE("BI_BITFIELDS not 16/32 bit depth");\r
+                return FALSE;\r
+            }\r
+        }\r
+    }\r
+\r
+    // Check the assumptions about the layout of the bit fields\r
+\r
+    if (pInput->bmiHeader.biCompression == BI_BITFIELDS) {\r
+        if (CheckBitFields(pInput) == FALSE) {\r
+            NOTE("Bit fields are not valid");\r
+            return FALSE;\r
+        }\r
+    }\r
+\r
+    // Are the number of planes equal to one\r
+\r
+    if (pInput->bmiHeader.biPlanes != 1) {\r
+        NOTE("Number of planes not one");\r
+        return FALSE;\r
+    }\r
+\r
+    // Check the image size is consistent (it can be zero)\r
+\r
+    if (pInput->bmiHeader.biSizeImage != GetBitmapSize(&pInput->bmiHeader)) {\r
+        if (pInput->bmiHeader.biSizeImage) {\r
+            NOTE("Image size incorrectly set");\r
+            return FALSE;\r
+        }\r
+    }\r
+\r
+    // Check the size of the structure\r
+\r
+    if (pInput->bmiHeader.biSize != sizeof(BITMAPINFOHEADER)) {\r
+        NOTE("Size of BITMAPINFOHEADER wrong");\r
+        return FALSE;\r
+    }\r
+    return CheckPaletteHeader(pInput);\r
+}\r
+\r
+\r
+// This runs a few simple tests against the palette fields in the input to\r
+// see if it looks vaguely correct. The tests look at the number of palette\r
+// colours present, the number considered important and the biCompression\r
+// field which should always be BI_RGB as no other formats are meaningful\r
+\r
+BOOL CImageDisplay::CheckPaletteHeader(const VIDEOINFO *pInput)\r
+{\r
+    // The checks here are for palettised videos only\r
+\r
+    if (PALETTISED(pInput) == FALSE) {\r
+        if (pInput->bmiHeader.biClrUsed) {\r
+            NOTE("Invalid palette entries");\r
+            return FALSE;\r
+        }\r
+        return TRUE;\r
+    }\r
+\r
+    // Compression type of BI_BITFIELDS is meaningless for palette video\r
+\r
+    if (pInput->bmiHeader.biCompression != BI_RGB) {\r
+        NOTE("Palettised video must be BI_RGB");\r
+        return FALSE;\r
+    }\r
+\r
+    // Check the number of palette colours is correct\r
+\r
+    if (pInput->bmiHeader.biClrUsed > PALETTE_ENTRIES(pInput)) {\r
+        NOTE("Too many colours in palette");\r
+        return FALSE;\r
+    }\r
+\r
+    // The number of important colours shouldn't exceed the number used\r
+\r
+    if (pInput->bmiHeader.biClrImportant > pInput->bmiHeader.biClrUsed) {\r
+        NOTE("Too many important colours");\r
+        return FALSE;\r
+    }\r
+    return TRUE;\r
+}\r
+\r
+\r
+// Return the format of the video display\r
+\r
+const VIDEOINFO *CImageDisplay::GetDisplayFormat()\r
+{\r
+    return &m_Display;\r
+}\r
+\r
+\r
+// Return TRUE if the display uses a palette\r
+\r
+BOOL CImageDisplay::IsPalettised()\r
+{\r
+    return PALETTISED(&m_Display);\r
+}\r
+\r
+\r
+// Return the bit depth of the current display setting\r
+\r
+WORD CImageDisplay::GetDisplayDepth()\r
+{\r
+    return m_Display.bmiHeader.biBitCount;\r
+}\r
+\r
+\r
+// Initialise the optional fields in a VIDEOINFO. These are mainly to do with\r
+// the source and destination rectangles and palette information such as the\r
+// number of colours present. It simplifies our code just a little if we don't\r
+// have to keep checking for all the different valid permutations in a header\r
+// every time we want to do anything with it (an example would be creating a\r
+// palette). We set the base class media type before calling this function so\r
+// that the media types between the pins match after a connection is made\r
+\r
+HRESULT CImageDisplay::UpdateFormat(__inout VIDEOINFO *pVideoInfo)\r
+{\r
+    ASSERT(pVideoInfo);\r
+\r
+    BITMAPINFOHEADER *pbmi = HEADER(pVideoInfo);\r
+    SetRectEmpty(&pVideoInfo->rcSource);\r
+    SetRectEmpty(&pVideoInfo->rcTarget);\r
+\r
+    // Set the number of colours explicitly\r
+\r
+    if (PALETTISED(pVideoInfo)) {\r
+        if (pVideoInfo->bmiHeader.biClrUsed == 0) {\r
+            pVideoInfo->bmiHeader.biClrUsed = PALETTE_ENTRIES(pVideoInfo);\r
+        }\r
+    }\r
+\r
+    // The number of important colours shouldn't exceed the number used, on\r
+    // some displays the number of important colours is not initialised when\r
+    // retrieving the display type so we set the colours used correctly\r
+\r
+    if (pVideoInfo->bmiHeader.biClrImportant > pVideoInfo->bmiHeader.biClrUsed) {\r
+        pVideoInfo->bmiHeader.biClrImportant = PALETTE_ENTRIES(pVideoInfo);\r
+    }\r
+\r
+    // Change the image size field to be explicit\r
+\r
+    if (pVideoInfo->bmiHeader.biSizeImage == 0) {\r
+        pVideoInfo->bmiHeader.biSizeImage = GetBitmapSize(&pVideoInfo->bmiHeader);\r
+    }\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Lots of video rendering filters want code to check proposed formats are ok\r
+// This checks the VIDEOINFO we are passed as a media type. If the media type\r
+// is a valid media type then we return NOERROR otherwise E_INVALIDARG. Note\r
+// however we only accept formats that can be easily displayed in the display\r
+// so if we are on a 16 bit device we will not accept 24 bit images. The one\r
+// complexity is that most displays draw 8 bit palettised images efficiently\r
+// Also if the input format is less colour bits per pixel then we also accept\r
+\r
+HRESULT CImageDisplay::CheckVideoType(const VIDEOINFO *pInput)\r
+{\r
+    // First of all check the VIDEOINFOHEADER looks correct\r
+\r
+    if (CheckHeaderValidity(pInput) == FALSE) {\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+    // Virtually all devices support palettised images efficiently\r
+\r
+    if (m_Display.bmiHeader.biBitCount == pInput->bmiHeader.biBitCount) {\r
+        if (PALETTISED(pInput) == TRUE) {\r
+            ASSERT(PALETTISED(&m_Display) == TRUE);\r
+            NOTE("(Video) Type connection ACCEPTED");\r
+            return NOERROR;\r
+        }\r
+    }\r
+\r
+\r
+    // Is the display depth greater than the input format\r
+\r
+    if (m_Display.bmiHeader.biBitCount > pInput->bmiHeader.biBitCount) {\r
+        NOTE("(Video) Mismatch agreed");\r
+        return NOERROR;\r
+    }\r
+\r
+    // Is the display depth less than the input format\r
+\r
+    if (m_Display.bmiHeader.biBitCount < pInput->bmiHeader.biBitCount) {\r
+        NOTE("(Video) Format mismatch");\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+\r
+    // Both input and display formats are either BI_RGB or BI_BITFIELDS\r
+\r
+    ASSERT(m_Display.bmiHeader.biBitCount == pInput->bmiHeader.biBitCount);\r
+    ASSERT(PALETTISED(pInput) == FALSE);\r
+    ASSERT(PALETTISED(&m_Display) == FALSE);\r
+\r
+    // BI_RGB 16 bit representation is implicitly RGB555, and likewise BI_RGB\r
+    // 24 bit representation is RGB888. So we initialise a pointer to the bit\r
+    // fields they really mean and check against the display device format\r
+    // This is only going to be called when both formats are equal bits pixel\r
+\r
+    const DWORD *pInputMask = GetBitMasks(pInput);\r
+    const DWORD *pDisplayMask = GetBitMasks((VIDEOINFO *)&m_Display);\r
+\r
+    if (pInputMask[iRED] != pDisplayMask[iRED] ||\r
+            pInputMask[iGREEN] != pDisplayMask[iGREEN] ||\r
+                pInputMask[iBLUE] != pDisplayMask[iBLUE]) {\r
+\r
+        NOTE("(Video) Bit field mismatch");\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+    NOTE("(Video) Type connection ACCEPTED");\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Return the bit masks for the true colour VIDEOINFO provided\r
+\r
+const DWORD *CImageDisplay::GetBitMasks(const VIDEOINFO *pVideoInfo)\r
+{\r
+    static const DWORD FailMasks[] = {0,0,0};\r
+\r
+    if (pVideoInfo->bmiHeader.biCompression == BI_BITFIELDS) {\r
+        return BITMASKS(pVideoInfo);\r
+    }\r
+\r
+    ASSERT(pVideoInfo->bmiHeader.biCompression == BI_RGB);\r
+\r
+    switch (pVideoInfo->bmiHeader.biBitCount) {\r
+        case 16: return bits555;\r
+        case 24: return bits888;\r
+        case 32: return bits888;\r
+        default: return FailMasks;\r
+    }\r
+}\r
+\r
+\r
+// Check to see if we can support media type pmtIn as proposed by the output\r
+// pin - We first check that the major media type is video and also identify\r
+// the media sub type. Then we thoroughly check the VIDEOINFO type provided\r
+// As well as the contained VIDEOINFO being correct the major type must be\r
+// video, the subtype a recognised video format and the type GUID correct\r
+\r
+HRESULT CImageDisplay::CheckMediaType(const CMediaType *pmtIn)\r
+{\r
+    // Does this have a VIDEOINFOHEADER format block\r
+\r
+    const GUID *pFormatType = pmtIn->FormatType();\r
+    if (*pFormatType != FORMAT_VideoInfo) {\r
+        NOTE("Format GUID not a VIDEOINFOHEADER");\r
+        return E_INVALIDARG;\r
+    }\r
+    ASSERT(pmtIn->Format());\r
+\r
+    // Check the format looks reasonably ok\r
+\r
+    ULONG Length = pmtIn->FormatLength();\r
+    if (Length < SIZE_VIDEOHEADER) {\r
+        NOTE("Format smaller than a VIDEOHEADER");\r
+        return E_FAIL;\r
+    }\r
+\r
+    VIDEOINFO *pInput = (VIDEOINFO *) pmtIn->Format();\r
+\r
+    // Check the major type is MEDIATYPE_Video\r
+\r
+    const GUID *pMajorType = pmtIn->Type();\r
+    if (*pMajorType != MEDIATYPE_Video) {\r
+        NOTE("Major type not MEDIATYPE_Video");\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+    // Check we can identify the media subtype\r
+\r
+    const GUID *pSubType = pmtIn->Subtype();\r
+    if (GetBitCount(pSubType) == USHRT_MAX) {\r
+        NOTE("Invalid video media subtype");\r
+        return E_INVALIDARG;\r
+    }\r
+    return CheckVideoType(pInput);\r
+}\r
+\r
+\r
+// Given a video format described by a VIDEOINFO structure we return the mask\r
+// that is used to obtain the range of acceptable colours for this type, for\r
+// example, the mask for a 24 bit true colour format is 0xFF in all cases. A\r
+// 16 bit 5:6:5 display format uses 0xF8, 0xFC and 0xF8, therefore given any\r
+// RGB triplets we can AND them with these fields to find one that is valid\r
+\r
+BOOL CImageDisplay::GetColourMask(__out DWORD *pMaskRed,\r
+                                  __out DWORD *pMaskGreen,\r
+                                  __out DWORD *pMaskBlue)\r
+{\r
+    CAutoLock cDisplayLock(this);\r
+    *pMaskRed = 0xFF;\r
+    *pMaskGreen = 0xFF;\r
+    *pMaskBlue = 0xFF;\r
+\r
+    // If this format is palettised then it doesn't have bit fields\r
+\r
+    if (m_Display.bmiHeader.biBitCount < 16) {\r
+        return FALSE;\r
+    }\r
+\r
+    // If this is a 24 bit true colour display then it can handle all the\r
+    // possible colour component ranges described by a byte. It is never\r
+    // allowed for a 24 bit colour depth image to have BI_BITFIELDS set\r
+\r
+    if (m_Display.bmiHeader.biBitCount == 24) {\r
+        ASSERT(m_Display.bmiHeader.biCompression == BI_RGB);\r
+        return TRUE;\r
+    }\r
+\r
+    // Calculate the mask based on the format's bit fields\r
+\r
+    const DWORD *pBitFields = (DWORD *) GetBitMasks((VIDEOINFO *)&m_Display);\r
+    DWORD *pOutputMask[] = { pMaskRed, pMaskGreen, pMaskBlue };\r
+\r
+    // We know from earlier testing that there are no more than iMAXBITS\r
+    // bits set in the mask and that they are all contiguous. All that\r
+    // therefore remains is to shift them into the correct position\r
+\r
+    for (INT iColour = iRED;iColour <= iBLUE;iColour++) {\r
+\r
+        // This works out how many bits there are and where they live\r
+\r
+        DWORD PrefixBits = CountPrefixBits(pBitFields[iColour]);\r
+        DWORD SetBits = CountSetBits(pBitFields[iColour]);\r
+\r
+        // The first shift moves the bit field so that it is right justified\r
+        // in the DWORD, after which we then shift it back left which then\r
+        // puts the leading bit in the bytes most significant bit position\r
+\r
+        *(pOutputMask[iColour]) = pBitFields[iColour] >> PrefixBits;\r
+        *(pOutputMask[iColour]) <<= (iMAXBITS - SetBits);\r
+    }\r
+    return TRUE;\r
+}\r
+\r
+\r
+/*  Helper to convert to VIDEOINFOHEADER2\r
+*/\r
+STDAPI ConvertVideoInfoToVideoInfo2(__inout AM_MEDIA_TYPE *pmt)\r
+{\r
+    if (pmt->formattype != FORMAT_VideoInfo) {\r
+        return E_INVALIDARG;\r
+    }\r
+    if (NULL == pmt->pbFormat || pmt->cbFormat < sizeof(VIDEOINFOHEADER)) {\r
+        return E_INVALIDARG;\r
+    }\r
+    VIDEOINFO *pVideoInfo = (VIDEOINFO *)pmt->pbFormat;\r
+    DWORD dwNewSize;\r
+    HRESULT hr = DWordAdd(pmt->cbFormat, sizeof(VIDEOINFOHEADER2) - sizeof(VIDEOINFOHEADER), &dwNewSize);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    PVOID pvNew = CoTaskMemAlloc(dwNewSize);\r
+    if (pvNew == NULL) {\r
+        return E_OUTOFMEMORY;\r
+    }\r
+    CopyMemory(pvNew, pmt->pbFormat, FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader));\r
+    ZeroMemory((PBYTE)pvNew + FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader),\r
+               sizeof(VIDEOINFOHEADER2) - sizeof(VIDEOINFOHEADER));\r
+    CopyMemory((PBYTE)pvNew + FIELD_OFFSET(VIDEOINFOHEADER2, bmiHeader),\r
+               pmt->pbFormat + FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader),\r
+               pmt->cbFormat - FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader));\r
+    VIDEOINFOHEADER2 *pVideoInfo2 = (VIDEOINFOHEADER2 *)pvNew;\r
+    pVideoInfo2->dwPictAspectRatioX = (DWORD)pVideoInfo2->bmiHeader.biWidth;\r
+    pVideoInfo2->dwPictAspectRatioY = (DWORD)abs(pVideoInfo2->bmiHeader.biHeight);\r
+    pmt->formattype = FORMAT_VideoInfo2;\r
+    CoTaskMemFree(pmt->pbFormat);\r
+    pmt->pbFormat = (PBYTE)pvNew;\r
+    pmt->cbFormat += sizeof(VIDEOINFOHEADER2) - sizeof(VIDEOINFOHEADER);\r
+    return S_OK;\r
+}\r
+\r
+\r
+//  Check a media type containing VIDEOINFOHEADER\r
+STDAPI CheckVideoInfoType(const AM_MEDIA_TYPE *pmt)\r
+{\r
+    if (NULL == pmt || NULL == pmt->pbFormat) {\r
+        return E_POINTER;\r
+    }\r
+    if (pmt->majortype != MEDIATYPE_Video || \r
+        pmt->formattype != FORMAT_VideoInfo ||\r
+        pmt->cbFormat < sizeof(VIDEOINFOHEADER)) {\r
+        return VFW_E_TYPE_NOT_ACCEPTED;\r
+    }\r
+    const VIDEOINFOHEADER *pHeader = (const VIDEOINFOHEADER *)pmt->pbFormat;\r
+    if (!ValidateBitmapInfoHeader(\r
+             &pHeader->bmiHeader, \r
+             pmt->cbFormat - FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader))) {\r
+        return VFW_E_TYPE_NOT_ACCEPTED;\r
+    }\r
+\r
+    return S_OK;\r
+}\r
+\r
+//  Check a media type containing VIDEOINFOHEADER2\r
+STDAPI CheckVideoInfo2Type(const AM_MEDIA_TYPE *pmt)\r
+{\r
+    if (NULL == pmt || NULL == pmt->pbFormat) {\r
+        return E_POINTER;\r
+    }    \r
+    if (pmt->majortype != MEDIATYPE_Video || \r
+        pmt->formattype != FORMAT_VideoInfo2 ||\r
+        pmt->cbFormat < sizeof(VIDEOINFOHEADER2)) {\r
+        return VFW_E_TYPE_NOT_ACCEPTED;\r
+    }\r
+    const VIDEOINFOHEADER2 *pHeader = (const VIDEOINFOHEADER2 *)pmt->pbFormat;\r
+    if (!ValidateBitmapInfoHeader(\r
+             &pHeader->bmiHeader, \r
+             pmt->cbFormat - FIELD_OFFSET(VIDEOINFOHEADER2, bmiHeader))) {\r
+        return VFW_E_TYPE_NOT_ACCEPTED;\r
+    }\r
+\r
+    return S_OK;\r
+}\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/winutil.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/winutil.h
new file mode 100644 (file)
index 0000000..6bd6235
--- /dev/null
@@ -0,0 +1,419 @@
+//------------------------------------------------------------------------------\r
+// File: WinUtil.h\r
+//\r
+// Desc: DirectShow base classes - defines generic handler classes.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+// Make sure that you call PrepareWindow to initialise the window after\r
+// the object has been constructed. It is a separate method so that\r
+// derived classes can override useful methods like MessageLoop. Also\r
+// any derived class must call DoneWithWindow in its destructor. If it\r
+// doesn't a message may be retrieved and call a derived class member\r
+// function while a thread is executing the base class destructor code\r
+\r
+#ifndef __WINUTIL__\r
+#define __WINUTIL__\r
+\r
+const int DEFWIDTH = 320;                    // Initial window width\r
+const int DEFHEIGHT = 240;                   // Initial window height\r
+const int CAPTION = 256;                     // Maximum length of caption\r
+const int TIMELENGTH = 50;                   // Maximum length of times\r
+const int PROFILESTR = 128;                  // Normal profile string\r
+const WORD PALVERSION = 0x300;               // GDI palette version\r
+const LONG PALETTE_VERSION = (LONG) 1;       // Initial palette version\r
+const COLORREF VIDEO_COLOUR = 0;             // Defaults to black background\r
+const HANDLE hMEMORY = (HANDLE) (-1);        // Says to open as memory file\r
+\r
+#define WIDTH(x) ((*(x)).right - (*(x)).left)\r
+#define HEIGHT(x) ((*(x)).bottom - (*(x)).top)\r
+#define SHOWSTAGE TEXT("WM_SHOWSTAGE")\r
+#define SHOWSTAGETOP TEXT("WM_SHOWSTAGETOP")\r
+#define REALIZEPALETTE TEXT("WM_REALIZEPALETTE")\r
+\r
+class AM_NOVTABLE CBaseWindow\r
+{\r
+protected:\r
+\r
+    HINSTANCE m_hInstance;          // Global module instance handle\r
+    HWND m_hwnd;                    // Handle for our window\r
+    HDC m_hdc;                      // Device context for the window\r
+    LONG m_Width;                   // Client window width\r
+    LONG m_Height;                  // Client window height\r
+    BOOL m_bActivated;              // Has the window been activated\r
+    LPTSTR m_pClassName;            // Static string holding class name\r
+    DWORD m_ClassStyles;            // Passed in to our constructor\r
+    DWORD m_WindowStyles;           // Likewise the initial window styles\r
+    DWORD m_WindowStylesEx;         // And the extended window styles\r
+    UINT m_ShowStageMessage;        // Have the window shown with focus\r
+    UINT m_ShowStageTop;            // Makes the window WS_EX_TOPMOST\r
+    UINT m_RealizePalette;          // Makes us realize our new palette\r
+    HDC m_MemoryDC;                 // Used for fast BitBlt operations\r
+    HPALETTE m_hPalette;            // Handle to any palette we may have\r
+    BYTE m_bNoRealize;              // Don't realize palette now\r
+    BYTE m_bBackground;             // Should we realise in background\r
+    BYTE m_bRealizing;              // already realizing the palette\r
+    CCritSec m_WindowLock;          // Serialise window object access\r
+    BOOL m_bDoGetDC;                // Should this window get a DC\r
+    bool m_bDoPostToDestroy;        // Use PostMessage to destroy\r
+    CCritSec m_PaletteLock;         // This lock protects m_hPalette.\r
+                                    // It should be held anytime the\r
+                                    // program use the value of m_hPalette.\r
+\r
+    // Maps windows message procedure into C++ methods\r
+    friend LRESULT CALLBACK WndProc(HWND hwnd,      // Window handle\r
+                                    UINT uMsg,      // Message ID\r
+                                    WPARAM wParam,  // First parameter\r
+                                    LPARAM lParam); // Other parameter\r
+\r
+    virtual LRESULT OnPaletteChange(HWND hwnd, UINT Message);\r
+\r
+public:\r
+\r
+    CBaseWindow(BOOL bDoGetDC = TRUE, bool bPostToDestroy = false);\r
+\r
+#ifdef DEBUG\r
+    virtual ~CBaseWindow();\r
+#endif\r
+\r
+    virtual HRESULT DoneWithWindow();\r
+    virtual HRESULT PrepareWindow();\r
+    virtual HRESULT InactivateWindow();\r
+    virtual HRESULT ActivateWindow();\r
+    virtual BOOL OnSize(LONG Width, LONG Height);\r
+    virtual BOOL OnClose();\r
+    virtual RECT GetDefaultRect();\r
+    virtual HRESULT UninitialiseWindow();\r
+    virtual HRESULT InitialiseWindow(HWND hwnd);\r
+\r
+    HRESULT CompleteConnect();\r
+    HRESULT DoCreateWindow();\r
+\r
+    HRESULT PerformanceAlignWindow();\r
+    HRESULT DoShowWindow(LONG ShowCmd);\r
+    void PaintWindow(BOOL bErase);\r
+    void DoSetWindowForeground(BOOL bFocus);\r
+    virtual HRESULT SetPalette(HPALETTE hPalette);\r
+    void SetRealize(BOOL bRealize)\r
+    {\r
+        m_bNoRealize = !bRealize;\r
+    }\r
+\r
+    //  Jump over to the window thread to set the current palette\r
+    HRESULT SetPalette();\r
+    void UnsetPalette(void);\r
+    virtual HRESULT DoRealisePalette(BOOL bForceBackground = FALSE);\r
+\r
+    void LockPaletteLock();\r
+    void UnlockPaletteLock();\r
+\r
+    virtual BOOL PossiblyEatMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)\r
+           { return FALSE; };\r
+\r
+    // Access our window information\r
+\r
+    bool WindowExists();\r
+    LONG GetWindowWidth();\r
+    LONG GetWindowHeight();\r
+    HWND GetWindowHWND();\r
+    HDC GetMemoryHDC();\r
+    HDC GetWindowHDC();\r
+\r
+    #ifdef DEBUG\r
+    HPALETTE GetPalette();\r
+    #endif // DEBUG\r
+\r
+    // This is the window procedure the derived object should override\r
+\r
+    virtual LRESULT OnReceiveMessage(HWND hwnd,          // Window handle\r
+                                     UINT uMsg,          // Message ID\r
+                                     WPARAM wParam,      // First parameter\r
+                                     LPARAM lParam);     // Other parameter\r
+\r
+    // Must be overriden to return class and window styles\r
+\r
+    virtual LPTSTR GetClassWindowStyles(\r
+                            __out DWORD *pClassStyles,          // Class styles\r
+                            __out DWORD *pWindowStyles,         // Window styles\r
+                            __out DWORD *pWindowStylesEx) PURE; // Extended styles\r
+};\r
+\r
+\r
+// This helper class is entirely subservient to the owning CBaseWindow object\r
+// All this object does is to split out the actual drawing operation from the\r
+// main object (because it was becoming too large). We have a number of entry\r
+// points to set things like the draw device contexts, to implement the actual\r
+// drawing and to set the destination rectangle in the client window. We have\r
+// no critical section locking in this class because we are used exclusively\r
+// by the owning window object which looks after serialising calls into us\r
+\r
+// If you want to use this class make sure you call NotifyAllocator once the\r
+// allocate has been agreed, also call NotifyMediaType with a pointer to a\r
+// NON stack based CMediaType once that has been set (we keep a pointer to\r
+// the original rather than taking a copy). When the palette changes call\r
+// IncrementPaletteVersion (easiest thing to do is to also call this method\r
+// in the SetMediaType method most filters implement). Finally before you\r
+// start rendering anything call SetDrawContext so that we can get the HDCs\r
+// for drawing from the CBaseWindow object we are given during construction\r
+\r
+class CDrawImage\r
+{\r
+protected:\r
+\r
+    CBaseWindow *m_pBaseWindow;     // Owning video window object\r
+    CRefTime m_StartSample;         // Start time for the current sample\r
+    CRefTime m_EndSample;           // And likewise it's end sample time\r
+    HDC m_hdc;                      // Main window device context\r
+    HDC m_MemoryDC;                 // Offscreen draw device context\r
+    RECT m_TargetRect;              // Target destination rectangle\r
+    RECT m_SourceRect;              // Source image rectangle\r
+    BOOL m_bStretch;                // Do we have to stretch the images\r
+    BOOL m_bUsingImageAllocator;    // Are the samples shared DIBSECTIONs\r
+    CMediaType *m_pMediaType;       // Pointer to the current format\r
+    int m_perfidRenderTime;         // Time taken to render an image\r
+    LONG m_PaletteVersion;          // Current palette version cookie\r
+\r
+    // Draw the video images in the window\r
+\r
+    void SlowRender(IMediaSample *pMediaSample);\r
+    void FastRender(IMediaSample *pMediaSample);\r
+    void DisplaySampleTimes(IMediaSample *pSample);\r
+    void UpdateColourTable(HDC hdc,__in BITMAPINFOHEADER *pbmi);\r
+    void SetStretchMode();\r
+\r
+public:\r
+\r
+    // Used to control the image drawing\r
+\r
+    CDrawImage(__inout CBaseWindow *pBaseWindow);\r
+    BOOL DrawImage(IMediaSample *pMediaSample);\r
+    BOOL DrawVideoImageHere(HDC hdc, IMediaSample *pMediaSample,\r
+                            __in LPRECT lprcSrc, __in LPRECT lprcDst);\r
+    void SetDrawContext();\r
+    void SetTargetRect(__in RECT *pTargetRect);\r
+    void SetSourceRect(__in RECT *pSourceRect);\r
+    void GetTargetRect(__out RECT *pTargetRect);\r
+    void GetSourceRect(__out RECT *pSourceRect);\r
+    virtual RECT ScaleSourceRect(const RECT *pSource);\r
+\r
+    // Handle updating palettes as they change\r
+\r
+    LONG GetPaletteVersion();\r
+    void ResetPaletteVersion();\r
+    void IncrementPaletteVersion();\r
+\r
+    // Tell us media types and allocator assignments\r
+\r
+    void NotifyAllocator(BOOL bUsingImageAllocator);\r
+    void NotifyMediaType(__in CMediaType *pMediaType);\r
+    BOOL UsingImageAllocator();\r
+\r
+    // Called when we are about to draw an image\r
+\r
+    void NotifyStartDraw() {\r
+        MSR_START(m_perfidRenderTime);\r
+    };\r
+\r
+    // Called when we complete an image rendering\r
+\r
+    void NotifyEndDraw() {\r
+        MSR_STOP(m_perfidRenderTime);\r
+    };\r
+};\r
+\r
+\r
+// This is the structure used to keep information about each GDI DIB. All the\r
+// samples we create from our allocator will have a DIBSECTION allocated to\r
+// them. When we receive the sample we know we can BitBlt straight to an HDC\r
+\r
+typedef struct tagDIBDATA {\r
+\r
+    LONG        PaletteVersion;     // Current palette version in use\r
+    DIBSECTION  DibSection;         // Details of DIB section allocated\r
+    HBITMAP     hBitmap;            // Handle to bitmap for drawing\r
+    HANDLE      hMapping;           // Handle to shared memory block\r
+    BYTE        *pBase;             // Pointer to base memory address\r
+\r
+} DIBDATA;\r
+\r
+\r
+// This class inherits from CMediaSample and uses all of it's methods but it\r
+// overrides the constructor to initialise itself with the DIBDATA structure\r
+// When we come to render an IMediaSample we will know if we are using our own\r
+// allocator, and if we are, we can cast the IMediaSample to a pointer to one\r
+// of these are retrieve the DIB section information and hence the HBITMAP\r
+\r
+class CImageSample : public CMediaSample\r
+{\r
+protected:\r
+\r
+    DIBDATA m_DibData;      // Information about the DIBSECTION\r
+    BOOL m_bInit;           // Is the DIB information setup\r
+\r
+public:\r
+\r
+    // Constructor\r
+\r
+    CImageSample(__inout CBaseAllocator *pAllocator,\r
+                 __in_opt LPCTSTR pName,\r
+                 __inout HRESULT *phr,\r
+                 __in_bcount(length) LPBYTE pBuffer,\r
+                 LONG length);\r
+\r
+    // Maintain the DIB/DirectDraw state\r
+\r
+    void SetDIBData(__in DIBDATA *pDibData);\r
+    __out DIBDATA *GetDIBData();\r
+};\r
+\r
+\r
+// This is an allocator based on the abstract CBaseAllocator base class that\r
+// allocates sample buffers in shared memory. The number and size of these\r
+// are determined when the output pin calls Prepare on us. The shared memory\r
+// blocks are used in subsequent calls to GDI CreateDIBSection, once that\r
+// has been done the output pin can fill the buffers with data which will\r
+// then be handed to GDI through BitBlt calls and thereby remove one copy\r
+\r
+class CImageAllocator : public CBaseAllocator\r
+{\r
+protected:\r
+\r
+    CBaseFilter *m_pFilter;   // Delegate reference counts to\r
+    CMediaType *m_pMediaType;           // Pointer to the current format\r
+\r
+    // Used to create and delete samples\r
+\r
+    HRESULT Alloc();\r
+    void Free();\r
+\r
+    // Manage the shared DIBSECTION and DCI/DirectDraw buffers\r
+\r
+    HRESULT CreateDIB(LONG InSize,DIBDATA &DibData);\r
+    STDMETHODIMP CheckSizes(__in ALLOCATOR_PROPERTIES *pRequest);\r
+    virtual CImageSample *CreateImageSample(__in_bcount(Length) LPBYTE pData,LONG Length);\r
+\r
+public:\r
+\r
+    // Constructor and destructor\r
+\r
+    CImageAllocator(__inout CBaseFilter *pFilter,__in_opt LPCTSTR pName,__inout HRESULT *phr);\r
+#ifdef DEBUG\r
+    ~CImageAllocator();\r
+#endif\r
+\r
+    STDMETHODIMP_(ULONG) NonDelegatingAddRef();\r
+    STDMETHODIMP_(ULONG) NonDelegatingRelease();\r
+    void NotifyMediaType(__in CMediaType *pMediaType);\r
+\r
+    // Agree the number of buffers to be used and their size\r
+\r
+    STDMETHODIMP SetProperties(\r
+        __in ALLOCATOR_PROPERTIES *pRequest,\r
+        __out ALLOCATOR_PROPERTIES *pActual);\r
+};\r
+\r
+\r
+// This class is a fairly specialised helper class for image renderers that\r
+// have to create and manage palettes. The CBaseWindow class looks after\r
+// realising palettes once they have been installed. This class can be used\r
+// to create the palette handles from a media format (which must contain a\r
+// VIDEOINFO structure in the format block). We try to make the palette an\r
+// identity palette to maximise performance and also only change palettes\r
+// if actually required to (we compare palette colours before updating).\r
+// All the methods are virtual so that they can be overriden if so required\r
+\r
+class CImagePalette\r
+{\r
+protected:\r
+\r
+    CBaseWindow *m_pBaseWindow;             // Window to realise palette in\r
+    CBaseFilter *m_pFilter;                 // Media filter to send events\r
+    CDrawImage *m_pDrawImage;               // Object who will be drawing\r
+    HPALETTE m_hPalette;                    // The palette handle we own\r
+\r
+public:\r
+\r
+    CImagePalette(__inout CBaseFilter *pBaseFilter,\r
+                  __inout CBaseWindow *pBaseWindow,\r
+                  __inout CDrawImage *pDrawImage);\r
+\r
+#ifdef DEBUG\r
+    virtual ~CImagePalette();\r
+#endif\r
+\r
+    static HPALETTE MakePalette(const VIDEOINFOHEADER *pVideoInfo, __in LPSTR szDevice);\r
+    HRESULT RemovePalette();\r
+    static HRESULT MakeIdentityPalette(__inout_ecount_full(iColours) PALETTEENTRY *pEntry,INT iColours, __in LPSTR szDevice);\r
+    HRESULT CopyPalette(const CMediaType *pSrc,__out CMediaType *pDest);\r
+    BOOL ShouldUpdate(const VIDEOINFOHEADER *pNewInfo,const VIDEOINFOHEADER *pOldInfo);\r
+    HRESULT PreparePalette(const CMediaType *pmtNew,const CMediaType *pmtOld,__in LPSTR szDevice);\r
+\r
+    BOOL DrawVideoImageHere(HDC hdc, IMediaSample *pMediaSample, __in LPRECT lprcSrc, __in LPRECT lprcDst)\r
+    {\r
+        return m_pDrawImage->DrawVideoImageHere(hdc, pMediaSample, lprcSrc,lprcDst);\r
+    }\r
+};\r
+\r
+\r
+// Another helper class really for video based renderers. Most such renderers\r
+// need to know what the display format is to some degree or another. This\r
+// class initialises itself with the display format. The format can be asked\r
+// for through GetDisplayFormat and various other accessor functions. If a\r
+// filter detects a display format change (perhaps it gets a WM_DEVMODECHANGE\r
+// message then it can call RefreshDisplayType to reset that format). Also\r
+// many video renderers will want to check formats as they are proposed by\r
+// source filters. This class provides methods to check formats and only\r
+// accept those video formats that can be efficiently drawn using GDI calls\r
+\r
+class CImageDisplay : public CCritSec\r
+{\r
+protected:\r
+\r
+    // This holds the display format; biSize should not be too big, so we can\r
+    // safely use the VIDEOINFO structure\r
+    VIDEOINFO m_Display;\r
+\r
+    static DWORD CountSetBits(const DWORD Field);\r
+    static DWORD CountPrefixBits(const DWORD Field);\r
+    static BOOL CheckBitFields(const VIDEOINFO *pInput);\r
+\r
+public:\r
+\r
+    // Constructor and destructor\r
+\r
+    CImageDisplay();\r
+\r
+    // Used to manage BITMAPINFOHEADERs and the display format\r
+\r
+    const VIDEOINFO *GetDisplayFormat();\r
+    HRESULT RefreshDisplayType(__in_opt LPSTR szDeviceName);\r
+    static BOOL CheckHeaderValidity(const VIDEOINFO *pInput);\r
+    static BOOL CheckPaletteHeader(const VIDEOINFO *pInput);\r
+    BOOL IsPalettised();\r
+    WORD GetDisplayDepth();\r
+\r
+    // Provide simple video format type checking\r
+\r
+    HRESULT CheckMediaType(const CMediaType *pmtIn);\r
+    HRESULT CheckVideoType(const VIDEOINFO *pInput);\r
+    HRESULT UpdateFormat(__inout VIDEOINFO *pVideoInfo);\r
+    const DWORD *GetBitMasks(const VIDEOINFO *pVideoInfo);\r
+\r
+    BOOL GetColourMask(__out DWORD *pMaskRed,\r
+                       __out DWORD *pMaskGreen,\r
+                       __out DWORD *pMaskBlue);\r
+};\r
+\r
+//  Convert a FORMAT_VideoInfo to FORMAT_VideoInfo2\r
+STDAPI ConvertVideoInfoToVideoInfo2(__inout AM_MEDIA_TYPE *pmt);\r
+\r
+//  Check a media type containing VIDEOINFOHEADER\r
+STDAPI CheckVideoInfoType(const AM_MEDIA_TYPE *pmt);\r
+\r
+//  Check a media type containing VIDEOINFOHEADER\r
+STDAPI CheckVideoInfo2Type(const AM_MEDIA_TYPE *pmt);\r
+\r
+#endif // __WINUTIL__\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/wxdebug.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/wxdebug.cpp
new file mode 100644 (file)
index 0000000..3c43303
--- /dev/null
@@ -0,0 +1,1474 @@
+//------------------------------------------------------------------------------\r
+// File: WXDebug.cpp\r
+//\r
+// Desc: DirectShow base classes - implements ActiveX system debugging\r
+//       facilities.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#define _WINDLL\r
+\r
+#include <streams.h>\r
+#include <stdarg.h>\r
+#include <stdio.h>\r
+#include <dvdmedia.h>\r
+\r
+#ifdef DEBUG\r
+#ifdef UNICODE\r
+#ifndef _UNICODE\r
+#define _UNICODE\r
+#endif // _UNICODE\r
+#endif // UNICODE\r
+#endif // DEBUG\r
+\r
+#include <tchar.h>\r
+#include <strsafe.h>\r
+\r
+#ifdef DEBUG\r
+static void DisplayBITMAPINFO(const BITMAPINFOHEADER* pbmi);\r
+static void DisplayRECT(LPCTSTR szLabel, const RECT& rc);\r
+\r
+// The Win32 wsprintf() function writes a maximum of 1024 characters to it's output buffer.\r
+// See the documentation for wsprintf()'s lpOut parameter for more information.\r
+const INT iDEBUGINFO = 1024;                 // Used to format strings\r
+\r
+/* For every module and executable we store a debugging level for each of\r
+   the five categories (eg LOG_ERROR and LOG_TIMING). This makes it easy\r
+   to isolate and debug individual modules without seeing everybody elses\r
+   spurious debug output. The keys are stored in the registry under the\r
+   HKEY_LOCAL_MACHINE\SOFTWARE\Debug\<Module Name>\<KeyName> key values\r
+   NOTE these must be in the same order as their enumeration definition */\r
+\r
+const LPCTSTR pKeyNames[] = {\r
+    TEXT("TIMING"),      // Timing and performance measurements\r
+    TEXT("TRACE"),       // General step point call tracing\r
+    TEXT("MEMORY"),      // Memory and object allocation/destruction\r
+    TEXT("LOCKING"),     // Locking/unlocking of critical sections\r
+    TEXT("ERROR"),       // Debug error notification\r
+    TEXT("CUSTOM1"),\r
+    TEXT("CUSTOM2"),\r
+    TEXT("CUSTOM3"),\r
+    TEXT("CUSTOM4"),\r
+    TEXT("CUSTOM5")\r
+    };\r
+\r
+const TCHAR CAutoTrace::_szEntering[] = TEXT("->: %s");\r
+const TCHAR CAutoTrace::_szLeaving[]  = TEXT("<-: %s");\r
+\r
+const INT iMAXLEVELS = NUMELMS(pKeyNames);  // Maximum debug categories\r
+\r
+HINSTANCE m_hInst;                          // Module instance handle\r
+TCHAR m_ModuleName[iDEBUGINFO];             // Cut down module name\r
+DWORD m_Levels[iMAXLEVELS];                 // Debug level per category\r
+CRITICAL_SECTION m_CSDebug;                 // Controls access to list\r
+DWORD m_dwNextCookie;                       // Next active object ID\r
+ObjectDesc *pListHead = NULL;               // First active object\r
+DWORD m_dwObjectCount;                      // Active object count\r
+BOOL m_bInit = FALSE;                       // Have we been initialised\r
+HANDLE m_hOutput = INVALID_HANDLE_VALUE;    // Optional output written here\r
+DWORD dwWaitTimeout = INFINITE;             // Default timeout value\r
+DWORD dwTimeOffset;                        // Time of first DbgLog call\r
+bool g_fUseKASSERT = false;                 // don't create messagebox\r
+bool g_fDbgInDllEntryPoint = false;\r
+bool g_fAutoRefreshLevels = false;\r
+\r
+LPCTSTR pBaseKey = TEXT("SOFTWARE\\Microsoft\\DirectShow\\Debug");\r
+LPCTSTR pGlobalKey = TEXT("GLOBAL");\r
+static CHAR *pUnknownName = "UNKNOWN";\r
+\r
+LPCTSTR TimeoutName = TEXT("TIMEOUT");\r
+\r
+/* This sets the instance handle that the debug library uses to find\r
+   the module's file name from the Win32 GetModuleFileName function */\r
+\r
+void WINAPI DbgInitialise(HINSTANCE hInst)\r
+{\r
+    InitializeCriticalSection(&m_CSDebug);\r
+    m_bInit = TRUE;\r
+\r
+    m_hInst = hInst;\r
+    DbgInitModuleName();\r
+    if (GetProfileInt(m_ModuleName, TEXT("BreakOnLoad"), 0))\r
+       DebugBreak();\r
+    DbgInitModuleSettings(false);\r
+    DbgInitGlobalSettings(true);\r
+    dwTimeOffset = timeGetTime();\r
+}\r
+\r
+\r
+/* This is called to clear up any resources the debug library uses - at the\r
+   moment we delete our critical section and the object list. The values we\r
+   retrieve from the registry are all done during initialisation but we don't\r
+   go looking for update notifications while we are running, if the values\r
+   are changed then the application has to be restarted to pick them up */\r
+\r
+void WINAPI DbgTerminate()\r
+{\r
+    if (m_hOutput != INVALID_HANDLE_VALUE) {\r
+       EXECUTE_ASSERT(CloseHandle(m_hOutput));\r
+       m_hOutput = INVALID_HANDLE_VALUE;\r
+    }\r
+    DeleteCriticalSection(&m_CSDebug);\r
+    m_bInit = FALSE;\r
+}\r
+\r
+\r
+/* This is called by DbgInitLogLevels to read the debug settings\r
+   for each logging category for this module from the registry */\r
+\r
+void WINAPI DbgInitKeyLevels(HKEY hKey, bool fTakeMax)\r
+{\r
+    LONG lReturn;               // Create key return value\r
+    LONG lKeyPos;               // Current key category\r
+    DWORD dwKeySize;            // Size of the key value\r
+    DWORD dwKeyType;            // Receives it's type\r
+    DWORD dwKeyValue;           // This fields value\r
+\r
+    /* Try and read a value for each key position in turn */\r
+    for (lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) {\r
+\r
+        dwKeySize = sizeof(DWORD);\r
+        lReturn = RegQueryValueEx(\r
+            hKey,                       // Handle to an open key\r
+            pKeyNames[lKeyPos],         // Subkey name derivation\r
+            NULL,                       // Reserved field\r
+            &dwKeyType,                 // Returns the field type\r
+            (LPBYTE) &dwKeyValue,       // Returns the field's value\r
+            &dwKeySize );               // Number of bytes transferred\r
+\r
+        /* If either the key was not available or it was not a DWORD value\r
+           then we ensure only the high priority debug logging is output\r
+           but we try and update the field to a zero filled DWORD value */\r
+\r
+        if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD)  {\r
+\r
+            dwKeyValue = 0;\r
+            lReturn = RegSetValueEx(\r
+                hKey,                   // Handle of an open key\r
+                pKeyNames[lKeyPos],     // Address of subkey name\r
+                (DWORD) 0,              // Reserved field\r
+                REG_DWORD,              // Type of the key field\r
+                (PBYTE) &dwKeyValue,    // Value for the field\r
+                sizeof(DWORD));         // Size of the field buffer\r
+\r
+            if (lReturn != ERROR_SUCCESS) {\r
+                DbgLog((LOG_ERROR,1,TEXT("Could not create subkey %s"),pKeyNames[lKeyPos]));\r
+                dwKeyValue = 0;\r
+            }\r
+        }\r
+        if(fTakeMax)\r
+        {\r
+            m_Levels[lKeyPos] = max(dwKeyValue,m_Levels[lKeyPos]);\r
+        }\r
+        else\r
+        {\r
+            if((m_Levels[lKeyPos] & LOG_FORCIBLY_SET) == 0) {\r
+                m_Levels[lKeyPos] = dwKeyValue;\r
+            }\r
+        }\r
+    }\r
+\r
+    /*  Read the timeout value for catching hangs */\r
+    dwKeySize = sizeof(DWORD);\r
+    lReturn = RegQueryValueEx(\r
+        hKey,                       // Handle to an open key\r
+        TimeoutName,                // Subkey name derivation\r
+        NULL,                       // Reserved field\r
+        &dwKeyType,                 // Returns the field type\r
+        (LPBYTE) &dwWaitTimeout,    // Returns the field's value\r
+        &dwKeySize );               // Number of bytes transferred\r
+\r
+    /* If either the key was not available or it was not a DWORD value\r
+       then we ensure only the high priority debug logging is output\r
+       but we try and update the field to a zero filled DWORD value */\r
+\r
+    if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD)  {\r
+\r
+        dwWaitTimeout = INFINITE;\r
+        lReturn = RegSetValueEx(\r
+            hKey,                   // Handle of an open key\r
+            TimeoutName,            // Address of subkey name\r
+            (DWORD) 0,              // Reserved field\r
+            REG_DWORD,              // Type of the key field\r
+            (PBYTE) &dwWaitTimeout, // Value for the field\r
+            sizeof(DWORD));         // Size of the field buffer\r
+\r
+        if (lReturn != ERROR_SUCCESS) {\r
+            DbgLog((LOG_ERROR,1,TEXT("Could not create subkey %s"),pKeyNames[lKeyPos]));\r
+            dwWaitTimeout = INFINITE;\r
+        }\r
+    }\r
+}\r
+\r
+void WINAPI DbgOutString(LPCTSTR psz)\r
+{\r
+    if (m_hOutput != INVALID_HANDLE_VALUE) {\r
+        UINT  cb = lstrlen(psz);\r
+        DWORD dw;\r
+#ifdef UNICODE\r
+        CHAR szDest[2048];\r
+        WideCharToMultiByte(CP_ACP, 0, psz, -1, szDest, NUMELMS(szDest), 0, 0);\r
+        WriteFile (m_hOutput, szDest, cb, &dw, NULL);\r
+#else\r
+        WriteFile (m_hOutput, psz, cb, &dw, NULL);\r
+#endif\r
+    } else {\r
+        OutputDebugString (psz);\r
+    }\r
+}\r
+\r
+\r
+\r
+\r
+HRESULT  DbgUniqueProcessName(LPCTSTR inName, LPTSTR outName)\r
+{\r
+    HRESULT hr = S_OK;\r
+    const TCHAR *pIn = inName;\r
+    int dotPos = -1;\r
+\r
+    //scan the input and record the last '.' position\r
+    while (*pIn && (pIn - inName) < MAX_PATH)\r
+    {\r
+        if ( TEXT('.') == *pIn )\r
+            dotPos = (int)(pIn-inName);\r
+        ++pIn;\r
+    }\r
+\r
+    if (*pIn) //input should be zero-terminated within MAX_PATH\r
+        return E_INVALIDARG;\r
+\r
+    DWORD dwProcessId = GetCurrentProcessId();\r
+\r
+    if (dotPos < 0) \r
+    {\r
+        //no extension in the input, appending process id to the input\r
+        hr = StringCchPrintf(outName, MAX_PATH, TEXT("%s_%d"), inName, dwProcessId);\r
+    }\r
+    else\r
+    {\r
+        TCHAR pathAndBasename[MAX_PATH] = {0};\r
+        \r
+        //there's an extension  - zero-terminate the path and basename first by copying\r
+        hr = StringCchCopyN(pathAndBasename, MAX_PATH, inName, (size_t)dotPos);\r
+\r
+        //re-combine path, basename and extension with processId appended to a basename\r
+        if (SUCCEEDED(hr))\r
+            hr = StringCchPrintf(outName, MAX_PATH, TEXT("%s_%d%s"), pathAndBasename, dwProcessId, inName + dotPos);\r
+    }\r
+\r
+    return hr;\r
+}\r
+\r
+\r
+/* Called by DbgInitGlobalSettings to setup alternate logging destinations\r
+ */\r
+\r
+void WINAPI DbgInitLogTo (\r
+    HKEY hKey)\r
+{\r
+    LONG  lReturn;\r
+    DWORD dwKeyType;\r
+    DWORD dwKeySize;\r
+    TCHAR szFile[MAX_PATH] = {0};\r
+    static const TCHAR cszKey[] = TEXT("LogToFile");\r
+\r
+    dwKeySize = MAX_PATH;\r
+    lReturn = RegQueryValueEx(\r
+        hKey,                       // Handle to an open key\r
+        cszKey,                     // Subkey name derivation\r
+        NULL,                       // Reserved field\r
+        &dwKeyType,                 // Returns the field type\r
+        (LPBYTE) szFile,            // Returns the field's value\r
+        &dwKeySize);                // Number of bytes transferred\r
+\r
+    // create an empty key if it does not already exist\r
+    //\r
+    if (lReturn != ERROR_SUCCESS || dwKeyType != REG_SZ)\r
+       {\r
+       dwKeySize = sizeof(TCHAR);\r
+       lReturn = RegSetValueEx(\r
+            hKey,                   // Handle of an open key\r
+            cszKey,                 // Address of subkey name\r
+            (DWORD) 0,              // Reserved field\r
+            REG_SZ,                 // Type of the key field\r
+            (PBYTE)szFile,          // Value for the field\r
+            dwKeySize);            // Size of the field buffer\r
+       }\r
+\r
+    // if an output-to was specified.  try to open it.\r
+    //\r
+    if (m_hOutput != INVALID_HANDLE_VALUE) {\r
+       EXECUTE_ASSERT(CloseHandle (m_hOutput));\r
+       m_hOutput = INVALID_HANDLE_VALUE;\r
+    }\r
+    if (szFile[0] != 0)\r
+       {\r
+       if (!lstrcmpi(szFile, TEXT("Console"))) {\r
+          m_hOutput = GetStdHandle (STD_OUTPUT_HANDLE);\r
+          if (m_hOutput == INVALID_HANDLE_VALUE) {\r
+             AllocConsole ();\r
+             m_hOutput = GetStdHandle (STD_OUTPUT_HANDLE);\r
+          }\r
+          SetConsoleTitle (TEXT("ActiveX Debug Output"));\r
+       } else if (szFile[0] &&\r
+                lstrcmpi(szFile, TEXT("Debug")) &&\r
+                lstrcmpi(szFile, TEXT("Debugger")) &&\r
+                lstrcmpi(szFile, TEXT("Deb")))\r
+          {\r
+            m_hOutput = CreateFile(szFile, GENERIC_WRITE,\r
+                                 FILE_SHARE_READ,\r
+                                 NULL, OPEN_ALWAYS,\r
+                                 FILE_ATTRIBUTE_NORMAL,\r
+                                 NULL);\r
+\r
+            if (INVALID_HANDLE_VALUE == m_hOutput &&\r
+                GetLastError() == ERROR_SHARING_VIOLATION)\r
+            {\r
+               TCHAR uniqueName[MAX_PATH] = {0};\r
+               if (SUCCEEDED(DbgUniqueProcessName(szFile, uniqueName)))\r
+               {\r
+                    m_hOutput = CreateFile(uniqueName, GENERIC_WRITE,\r
+                                         FILE_SHARE_READ,\r
+                                         NULL, OPEN_ALWAYS,\r
+                                         FILE_ATTRIBUTE_NORMAL,\r
+                                         NULL);\r
+               }\r
+            }\r
+               \r
+            if (INVALID_HANDLE_VALUE != m_hOutput)\r
+            {\r
+              static const TCHAR cszBar[] = TEXT("\r\n\r\n=====DbgInitialize()=====\r\n\r\n");\r
+              SetFilePointer (m_hOutput, 0, NULL, FILE_END);\r
+              DbgOutString (cszBar);\r
+            }\r
+          }\r
+       }\r
+}\r
+\r
+\r
+\r
+/* This is called by DbgInitLogLevels to read the global debug settings for\r
+   each logging category for this module from the registry. Normally each\r
+   module has it's own values set for it's different debug categories but\r
+   setting the global SOFTWARE\Debug\Global applies them to ALL modules */\r
+\r
+void WINAPI DbgInitGlobalSettings(bool fTakeMax)\r
+{\r
+    LONG lReturn;               // Create key return value\r
+    TCHAR szInfo[iDEBUGINFO];   // Constructs key names\r
+    HKEY hGlobalKey;            // Global override key\r
+\r
+    /* Construct the global base key name */\r
+    (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%s\\%s"),pBaseKey,pGlobalKey);\r
+\r
+    /* Create or open the key for this module */\r
+    lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE,   // Handle of an open key\r
+                             szInfo,               // Address of subkey name\r
+                             (DWORD) 0,            // Reserved value\r
+                             NULL,                 // Address of class name\r
+                             (DWORD) 0,            // Special options flags\r
+                             GENERIC_READ | GENERIC_WRITE,   // Desired security access\r
+                             NULL,                 // Key security descriptor\r
+                             &hGlobalKey,          // Opened handle buffer\r
+                             NULL);                // What really happened\r
+\r
+    if (lReturn != ERROR_SUCCESS) {\r
+        lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE,   // Handle of an open key\r
+                                 szInfo,               // Address of subkey name\r
+                                 (DWORD) 0,            // Reserved value\r
+                                 NULL,                 // Address of class name\r
+                                 (DWORD) 0,            // Special options flags\r
+                                 GENERIC_READ,         // Desired security access\r
+                                 NULL,                 // Key security descriptor\r
+                                 &hGlobalKey,          // Opened handle buffer\r
+                                 NULL);                // What really happened\r
+        if (lReturn != ERROR_SUCCESS) {\r
+            DbgLog((LOG_ERROR,1,TEXT("Could not access GLOBAL module key")));\r
+        }\r
+        return;\r
+    }\r
+\r
+    DbgInitKeyLevels(hGlobalKey, fTakeMax);\r
+    RegCloseKey(hGlobalKey);\r
+}\r
+\r
+\r
+/* This sets the debugging log levels for the different categories. We start\r
+   by opening (or creating if not already available) the SOFTWARE\Debug key\r
+   that all these settings live under. We then look at the global values\r
+   set under SOFTWARE\Debug\Global which apply on top of the individual\r
+   module settings. We then load the individual module registry settings */\r
+\r
+void WINAPI DbgInitModuleSettings(bool fTakeMax)\r
+{\r
+    LONG lReturn;               // Create key return value\r
+    TCHAR szInfo[iDEBUGINFO];   // Constructs key names\r
+    HKEY hModuleKey;            // Module key handle\r
+\r
+    /* Construct the base key name */\r
+    (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%s\\%s"),pBaseKey,m_ModuleName);\r
+\r
+    /* Create or open the key for this module */\r
+    lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE,   // Handle of an open key\r
+                             szInfo,               // Address of subkey name\r
+                             (DWORD) 0,            // Reserved value\r
+                             NULL,                 // Address of class name\r
+                             (DWORD) 0,            // Special options flags\r
+                             GENERIC_READ | GENERIC_WRITE, // Desired security access\r
+                             NULL,                 // Key security descriptor\r
+                             &hModuleKey,          // Opened handle buffer\r
+                             NULL);                // What really happened\r
+\r
+    if (lReturn != ERROR_SUCCESS) {\r
+        lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE,   // Handle of an open key\r
+                                 szInfo,               // Address of subkey name\r
+                                 (DWORD) 0,            // Reserved value\r
+                                 NULL,                 // Address of class name\r
+                                 (DWORD) 0,            // Special options flags\r
+                                 GENERIC_READ,         // Desired security access\r
+                                 NULL,                 // Key security descriptor\r
+                                 &hModuleKey,          // Opened handle buffer\r
+                                 NULL);                // What really happened\r
+        if (lReturn != ERROR_SUCCESS) {\r
+            DbgLog((LOG_ERROR,1,TEXT("Could not access module key")));\r
+        }\r
+        return;\r
+    }\r
+\r
+    DbgInitLogTo(hModuleKey);\r
+    DbgInitKeyLevels(hModuleKey, fTakeMax);\r
+    RegCloseKey(hModuleKey);\r
+}\r
+\r
+\r
+/* Initialise the module file name */\r
+\r
+void WINAPI DbgInitModuleName()\r
+{\r
+    TCHAR FullName[iDEBUGINFO];     // Load the full path and module name\r
+    LPTSTR pName;                   // Searches from the end for a backslash\r
+\r
+    GetModuleFileName(m_hInst,FullName,iDEBUGINFO);\r
+    pName = _tcsrchr(FullName,'\\');\r
+    if (pName == NULL) {\r
+        pName = FullName;\r
+    } else {\r
+        pName++;\r
+    }\r
+    (void)StringCchCopy(m_ModuleName,NUMELMS(m_ModuleName), pName);\r
+}\r
+\r
+struct MsgBoxMsg\r
+{\r
+    HWND hwnd;\r
+    LPCTSTR szTitle;\r
+    LPCTSTR szMessage;\r
+    DWORD dwFlags;\r
+    INT iResult;\r
+};\r
+\r
+//\r
+// create a thread to call MessageBox(). calling MessageBox() on\r
+// random threads at bad times can confuse the host (eg IE).\r
+//\r
+DWORD WINAPI MsgBoxThread(\r
+  __inout LPVOID lpParameter   // thread data\r
+  )\r
+{\r
+    MsgBoxMsg *pmsg = (MsgBoxMsg *)lpParameter;\r
+    pmsg->iResult = MessageBox(\r
+        pmsg->hwnd,\r
+        pmsg->szTitle,\r
+        pmsg->szMessage,\r
+        pmsg->dwFlags);\r
+\r
+    return 0;\r
+}\r
+\r
+INT MessageBoxOtherThread(\r
+    HWND hwnd,\r
+    LPCTSTR szTitle,\r
+    LPCTSTR szMessage,\r
+    DWORD dwFlags)\r
+{\r
+    if(g_fDbgInDllEntryPoint)\r
+    {\r
+        // can't wait on another thread because we have the loader\r
+        // lock held in the dll entry point.\r
+        // This can crash sometimes so just skip it\r
+        // return MessageBox(hwnd, szTitle, szMessage, dwFlags);\r
+        return IDCANCEL;\r
+    }\r
+    else\r
+    {\r
+        MsgBoxMsg msg = {hwnd, szTitle, szMessage, dwFlags, 0};\r
+        DWORD dwid;\r
+        HANDLE hThread = CreateThread(\r
+            0,                      // security\r
+            0,                      // stack size\r
+            MsgBoxThread,\r
+            (void *)&msg,           // arg\r
+            0,                      // flags\r
+            &dwid);\r
+        if(hThread)\r
+        {\r
+            WaitForSingleObject(hThread, INFINITE);\r
+            CloseHandle(hThread);\r
+            return msg.iResult;\r
+        }\r
+\r
+        // break into debugger on failure.\r
+        return IDCANCEL;\r
+    }\r
+}\r
+\r
+/* Displays a message box if the condition evaluated to FALSE */\r
+\r
+void WINAPI DbgAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine)\r
+{\r
+    if(g_fUseKASSERT)\r
+    {\r
+        DbgKernelAssert(pCondition, pFileName, iLine);\r
+    }\r
+    else\r
+    {\r
+\r
+        TCHAR szInfo[iDEBUGINFO];\r
+\r
+        (void)StringCchPrintf(szInfo, NUMELMS(szInfo),TEXT("%s \nAt line %d of %s\nContinue? (Cancel to debug)"),\r
+                 pCondition, iLine, pFileName);\r
+\r
+        INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("ASSERT Failed"),\r
+                                          MB_SYSTEMMODAL |\r
+                                          MB_ICONHAND |\r
+                                          MB_YESNOCANCEL |\r
+                                          MB_SETFOREGROUND);\r
+        switch (MsgId)\r
+        {\r
+          case IDNO:              /* Kill the application */\r
+\r
+              FatalAppExit(FALSE, TEXT("Application terminated"));\r
+              break;\r
+\r
+          case IDCANCEL:          /* Break into the debugger */\r
+\r
+              DebugBreak();\r
+              break;\r
+\r
+          case IDYES:             /* Ignore assertion continue execution */\r
+              break;\r
+        }\r
+    }\r
+}\r
+\r
+/* Displays a message box at a break point */\r
+\r
+void WINAPI DbgBreakPoint(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine)\r
+{\r
+    if(g_fUseKASSERT)\r
+    {\r
+        DbgKernelAssert(pCondition, pFileName, iLine);\r
+    }\r
+    else\r
+    {\r
+        TCHAR szInfo[iDEBUGINFO];\r
+\r
+        (void)StringCchPrintf(szInfo, NUMELMS(szInfo),TEXT("%s \nAt line %d of %s\nContinue? (Cancel to debug)"),\r
+                 pCondition, iLine, pFileName);\r
+\r
+        INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("Hard coded break point"),\r
+                                          MB_SYSTEMMODAL |\r
+                                          MB_ICONHAND |\r
+                                          MB_YESNOCANCEL |\r
+                                          MB_SETFOREGROUND);\r
+        switch (MsgId)\r
+        {\r
+          case IDNO:              /* Kill the application */\r
+\r
+              FatalAppExit(FALSE, TEXT("Application terminated"));\r
+              break;\r
+\r
+          case IDCANCEL:          /* Break into the debugger */\r
+\r
+              DebugBreak();\r
+              break;\r
+\r
+          case IDYES:             /* Ignore break point continue execution */\r
+              break;\r
+        }\r
+    }\r
+}\r
+\r
+void WINAPI DbgBreakPoint(LPCTSTR pFileName,INT iLine,__format_string LPCTSTR szFormatString,...)\r
+{\r
+    // A debug break point message can have at most 2000 characters if\r
+    // ANSI or UNICODE characters are being used.  A debug break point message\r
+    // can have between 1000 and 2000 double byte characters in it.  If a\r
+    // particular message needs more characters, then the value of this constant\r
+    // should be increased.\r
+    const DWORD MAX_BREAK_POINT_MESSAGE_SIZE = 2000;\r
+\r
+    TCHAR szBreakPointMessage[MAX_BREAK_POINT_MESSAGE_SIZE];\r
+\r
+    va_list va;\r
+    va_start( va, szFormatString );\r
+\r
+    HRESULT hr = StringCchVPrintf( szBreakPointMessage, NUMELMS(szBreakPointMessage), szFormatString, va );\r
+\r
+    va_end(va);\r
+\r
+    if( FAILED(hr) ) {\r
+        DbgBreak( "ERROR in DbgBreakPoint().  The variable length debug message could not be displayed because StringCchVPrintf() failed." );\r
+        return;\r
+    }\r
+\r
+    ::DbgBreakPoint( szBreakPointMessage, pFileName, iLine );\r
+}\r
+\r
+\r
+/* When we initialised the library we stored in the m_Levels array the current\r
+   debug output level for this module for each of the five categories. When\r
+   some debug logging is sent to us it can be sent with a combination of the\r
+   categories (if it is applicable to many for example) in which case we map\r
+   the type's categories into their current debug levels and see if any of\r
+   them can be accepted. The function looks at each bit position in turn from\r
+   the input type field and then compares it's debug level with the modules.\r
+\r
+   A level of 0 means that output is always sent to the debugger.  This is\r
+   due to producing output if the input level is <= m_Levels.\r
+*/\r
+\r
+\r
+BOOL WINAPI DbgCheckModuleLevel(DWORD Type,DWORD Level)\r
+{\r
+    if(g_fAutoRefreshLevels)\r
+    {\r
+        // re-read the registry every second. We cannot use RegNotify() to\r
+        // notice registry changes because it's not available on win9x.\r
+        static DWORD g_dwLastRefresh = 0;\r
+        DWORD dwTime = timeGetTime();\r
+        if(dwTime - g_dwLastRefresh > 1000) {\r
+            g_dwLastRefresh = dwTime;\r
+\r
+            // there's a race condition: multiple threads could update the\r
+            // values. plus read and write not synchronized. no harm\r
+            // though.\r
+            DbgInitModuleSettings(false);\r
+        }\r
+    }\r
+\r
+\r
+    DWORD Mask = 0x01;\r
+\r
+    // If no valid bits are set return FALSE\r
+    if ((Type & ((1<<iMAXLEVELS)-1))) {\r
+\r
+       // speed up unconditional output.\r
+       if (0==Level)\r
+           return(TRUE);\r
+       \r
+        for (LONG lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) {\r
+            if (Type & Mask) {\r
+                if (Level <= (m_Levels[lKeyPos] & ~LOG_FORCIBLY_SET)) {\r
+                    return TRUE;\r
+                }\r
+            }\r
+            Mask <<= 1;\r
+        }\r
+    }\r
+    return FALSE;\r
+}\r
+\r
+\r
+/* Set debug levels to a given value */\r
+\r
+void WINAPI DbgSetModuleLevel(DWORD Type, DWORD Level)\r
+{\r
+    DWORD Mask = 0x01;\r
+\r
+    for (LONG lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) {\r
+        if (Type & Mask) {\r
+            m_Levels[lKeyPos] = Level | LOG_FORCIBLY_SET;\r
+        }\r
+        Mask <<= 1;\r
+    }\r
+}\r
+\r
+/* whether to check registry values periodically. this isn't turned\r
+   automatically because of the potential performance hit. */\r
+void WINAPI DbgSetAutoRefreshLevels(bool fAuto)\r
+{\r
+    g_fAutoRefreshLevels = fAuto;\r
+}\r
+\r
+#ifdef UNICODE\r
+//\r
+// warning -- this function is implemented twice for ansi applications\r
+// linking to the unicode library\r
+//\r
+void WINAPI DbgLogInfo(DWORD Type,DWORD Level,__format_string LPCSTR pFormat,...)\r
+{\r
+    /* Check the current level for this type combination */\r
+\r
+    BOOL bAccept = DbgCheckModuleLevel(Type,Level);\r
+    if (bAccept == FALSE) {\r
+        return;\r
+    }\r
+\r
+    TCHAR szInfo[2000];\r
+\r
+    /* Format the variable length parameter list */\r
+\r
+    va_list va;\r
+    va_start(va, pFormat);\r
+\r
+    (void)StringCchPrintf(szInfo, NUMELMS(szInfo),\r
+             TEXT("%s(tid %x) %8d : "),\r
+             m_ModuleName,\r
+             GetCurrentThreadId(), timeGetTime() - dwTimeOffset);\r
+\r
+    CHAR szInfoA[2000];\r
+    WideCharToMultiByte(CP_ACP, 0, szInfo, -1, szInfoA, NUMELMS(szInfoA), 0, 0);\r
+\r
+    (void)StringCchVPrintfA(szInfoA + lstrlenA(szInfoA), NUMELMS(szInfoA) - lstrlenA(szInfoA), pFormat, va);\r
+    (void)StringCchCatA(szInfoA, NUMELMS(szInfoA), "\r\n");\r
+\r
+    WCHAR wszOutString[2000];\r
+    MultiByteToWideChar(CP_ACP, 0, szInfoA, -1, wszOutString, NUMELMS(wszOutString));\r
+    DbgOutString(wszOutString);\r
+\r
+    va_end(va);\r
+}\r
+\r
+void WINAPI DbgAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine)\r
+{\r
+    if(g_fUseKASSERT)\r
+    {\r
+        DbgKernelAssert(pCondition, pFileName, iLine);\r
+    }\r
+    else\r
+    {\r
+\r
+        TCHAR szInfo[iDEBUGINFO];\r
+\r
+        (void)StringCchPrintf(szInfo, NUMELMS(szInfo), TEXT("%hs \nAt line %d of %hs\nContinue? (Cancel to debug)"),\r
+                 pCondition, iLine, pFileName);\r
+\r
+        INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("ASSERT Failed"),\r
+                                          MB_SYSTEMMODAL |\r
+                                          MB_ICONHAND |\r
+                                          MB_YESNOCANCEL |\r
+                                          MB_SETFOREGROUND);\r
+        switch (MsgId)\r
+        {\r
+          case IDNO:              /* Kill the application */\r
+\r
+              FatalAppExit(FALSE, TEXT("Application terminated"));\r
+              break;\r
+\r
+          case IDCANCEL:          /* Break into the debugger */\r
+\r
+              DebugBreak();\r
+              break;\r
+\r
+          case IDYES:             /* Ignore assertion continue execution */\r
+              break;\r
+        }\r
+    }\r
+}\r
+\r
+/* Displays a message box at a break point */\r
+\r
+void WINAPI DbgBreakPoint(LPCSTR pCondition,LPCSTR pFileName,INT iLine)\r
+{\r
+    if(g_fUseKASSERT)\r
+    {\r
+        DbgKernelAssert(pCondition, pFileName, iLine);\r
+    }\r
+    else\r
+    {\r
+        TCHAR szInfo[iDEBUGINFO];\r
+\r
+        (void)StringCchPrintf(szInfo, NUMELMS(szInfo),TEXT("%hs \nAt line %d of %hs\nContinue? (Cancel to debug)"),\r
+                 pCondition, iLine, pFileName);\r
+\r
+        INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("Hard coded break point"),\r
+                                          MB_SYSTEMMODAL |\r
+                                          MB_ICONHAND |\r
+                                          MB_YESNOCANCEL |\r
+                                          MB_SETFOREGROUND);\r
+        switch (MsgId)\r
+        {\r
+          case IDNO:              /* Kill the application */\r
+\r
+              FatalAppExit(FALSE, TEXT("Application terminated"));\r
+              break;\r
+\r
+          case IDCANCEL:          /* Break into the debugger */\r
+\r
+              DebugBreak();\r
+              break;\r
+\r
+          case IDYES:             /* Ignore break point continue execution */\r
+              break;\r
+        }\r
+    }\r
+}\r
+\r
+void WINAPI DbgKernelAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine)\r
+{\r
+    DbgLog((LOG_ERROR,0,TEXT("Assertion FAILED (%hs) at line %d in file %hs"),\r
+           pCondition, iLine, pFileName));\r
+    DebugBreak();\r
+}\r
+\r
+#endif\r
+\r
+/* Print a formatted string to the debugger prefixed with this module's name\r
+   Because the COMBASE classes are linked statically every module loaded will\r
+   have their own copy of this code. It therefore helps if the module name is\r
+   included on the output so that the offending code can be easily found */\r
+\r
+//\r
+// warning -- this function is implemented twice for ansi applications\r
+// linking to the unicode library\r
+//\r
+void WINAPI DbgLogInfo(DWORD Type,DWORD Level,LPCTSTR pFormat,...)\r
+{\r
+\r
+    /* Check the current level for this type combination */\r
+\r
+    BOOL bAccept = DbgCheckModuleLevel(Type,Level);\r
+    if (bAccept == FALSE) {\r
+        return;\r
+    }\r
+\r
+    TCHAR szInfo[2000];\r
+\r
+    /* Format the variable length parameter list */\r
+\r
+    va_list va;\r
+    va_start(va, pFormat);\r
+\r
+    (void)StringCchPrintf(szInfo, NUMELMS(szInfo),\r
+             TEXT("%s(tid %x) %8d : "),\r
+             m_ModuleName,\r
+             GetCurrentThreadId(), timeGetTime() - dwTimeOffset);\r
+\r
+    (void)StringCchVPrintf(szInfo + lstrlen(szInfo), NUMELMS(szInfo) - lstrlen(szInfo), pFormat, va);\r
+    (void)StringCchCat(szInfo, NUMELMS(szInfo), TEXT("\r\n"));\r
+    DbgOutString(szInfo);\r
+\r
+    va_end(va);\r
+}\r
+\r
+\r
+/* If we are executing as a pure kernel filter we cannot display message\r
+   boxes to the user, this provides an alternative which puts the error\r
+   condition on the debugger output with a suitable eye catching message */\r
+\r
+void WINAPI DbgKernelAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine)\r
+{\r
+    DbgLog((LOG_ERROR,0,TEXT("Assertion FAILED (%s) at line %d in file %s"),\r
+           pCondition, iLine, pFileName));\r
+    DebugBreak();\r
+}\r
+\r
+\r
+\r
+/* Each time we create an object derived from CBaseObject the constructor will\r
+   call us to register the creation of the new object. We are passed a string\r
+   description which we store away. We return a cookie that the constructor\r
+   uses to identify the object when it is destroyed later on. We update the\r
+   total number of active objects in the DLL mainly for debugging purposes */\r
+\r
+DWORD WINAPI DbgRegisterObjectCreation(LPCSTR szObjectName,\r
+                                       LPCWSTR wszObjectName)\r
+{\r
+    /* If this fires you have a mixed DEBUG/RETAIL build */\r
+\r
+    ASSERT(!!szObjectName ^ !!wszObjectName);\r
+\r
+    /* Create a place holder for this object description */\r
+\r
+    ObjectDesc *pObject = new ObjectDesc;\r
+    ASSERT(pObject);\r
+\r
+    /* It is valid to pass a NULL object name */\r
+    if (pObject == NULL) {\r
+        return FALSE;\r
+    }\r
+\r
+    /* Check we have been initialised - we may not be initialised when we are\r
+       being pulled in from an executable which has globally defined objects\r
+       as they are created by the C++ run time before WinMain is called */\r
+\r
+    if (m_bInit == FALSE) {\r
+        DbgInitialise(GetModuleHandle(NULL));\r
+    }\r
+\r
+    /* Grab the list critical section */\r
+    EnterCriticalSection(&m_CSDebug);\r
+\r
+    /* If no name then default to UNKNOWN */\r
+    if (!szObjectName && !wszObjectName) {\r
+        szObjectName = pUnknownName;\r
+    }\r
+\r
+    /* Put the new description at the head of the list */\r
+\r
+    pObject->m_szName = szObjectName;\r
+    pObject->m_wszName = wszObjectName;\r
+    pObject->m_dwCookie = ++m_dwNextCookie;\r
+    pObject->m_pNext = pListHead;\r
+\r
+    pListHead = pObject;\r
+    m_dwObjectCount++;\r
+\r
+    DWORD ObjectCookie = pObject->m_dwCookie;\r
+    ASSERT(ObjectCookie);\r
+\r
+    if(wszObjectName) {\r
+        DbgLog((LOG_MEMORY,2,TEXT("Object created   %d (%ls) %d Active"),\r
+                pObject->m_dwCookie, wszObjectName, m_dwObjectCount));\r
+    } else {\r
+        DbgLog((LOG_MEMORY,2,TEXT("Object created   %d (%hs) %d Active"),\r
+                pObject->m_dwCookie, szObjectName, m_dwObjectCount));\r
+    }\r
+\r
+    LeaveCriticalSection(&m_CSDebug);\r
+    return ObjectCookie;\r
+}\r
+\r
+\r
+/* This is called by the CBaseObject destructor when an object is about to be\r
+   destroyed, we are passed the cookie we returned during construction that\r
+   identifies this object. We scan the object list for a matching cookie and\r
+   remove the object if successful. We also update the active object count */\r
+\r
+BOOL WINAPI DbgRegisterObjectDestruction(DWORD dwCookie)\r
+{\r
+    /* Grab the list critical section */\r
+    EnterCriticalSection(&m_CSDebug);\r
+\r
+    ObjectDesc *pObject = pListHead;\r
+    ObjectDesc *pPrevious = NULL;\r
+\r
+    /* Scan the object list looking for a cookie match */\r
+\r
+    while (pObject) {\r
+        if (pObject->m_dwCookie == dwCookie) {\r
+            break;\r
+        }\r
+        pPrevious = pObject;\r
+        pObject = pObject->m_pNext;\r
+    }\r
+\r
+    if (pObject == NULL) {\r
+        DbgBreak("Apparently destroying a bogus object");\r
+        LeaveCriticalSection(&m_CSDebug);\r
+        return FALSE;\r
+    }\r
+\r
+    /* Is the object at the head of the list */\r
+\r
+    if (pPrevious == NULL) {\r
+        pListHead = pObject->m_pNext;\r
+    } else {\r
+        pPrevious->m_pNext = pObject->m_pNext;\r
+    }\r
+\r
+    /* Delete the object and update the housekeeping information */\r
+\r
+    m_dwObjectCount--;\r
+\r
+    if(pObject->m_wszName) {\r
+        DbgLog((LOG_MEMORY,2,TEXT("Object destroyed %d (%ls) %d Active"),\r
+                pObject->m_dwCookie, pObject->m_wszName, m_dwObjectCount));\r
+    } else {\r
+        DbgLog((LOG_MEMORY,2,TEXT("Object destroyed %d (%hs) %d Active"),\r
+                pObject->m_dwCookie, pObject->m_szName, m_dwObjectCount));\r
+    }\r
+\r
+    delete pObject;\r
+    LeaveCriticalSection(&m_CSDebug);\r
+    return TRUE;\r
+}\r
+\r
+\r
+/* This runs through the active object list displaying their details */\r
+\r
+void WINAPI DbgDumpObjectRegister()\r
+{\r
+    TCHAR szInfo[iDEBUGINFO];\r
+\r
+    /* Grab the list critical section */\r
+\r
+    EnterCriticalSection(&m_CSDebug);\r
+    ObjectDesc *pObject = pListHead;\r
+\r
+    /* Scan the object list displaying the name and cookie */\r
+\r
+    DbgLog((LOG_MEMORY,2,TEXT("")));\r
+    DbgLog((LOG_MEMORY,2,TEXT("   ID             Object Description")));\r
+    DbgLog((LOG_MEMORY,2,TEXT("")));\r
+\r
+    while (pObject) {\r
+        if(pObject->m_wszName) {\r
+            (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%5d (%p) %30ls"),pObject->m_dwCookie, &pObject, pObject->m_wszName);\r
+        } else {\r
+            (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%5d (%p) %30hs"),pObject->m_dwCookie, &pObject, pObject->m_szName);\r
+        }\r
+        DbgLog((LOG_MEMORY,2,szInfo));\r
+        pObject = pObject->m_pNext;\r
+    }\r
+\r
+    (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("Total object count %5d"),m_dwObjectCount);\r
+    DbgLog((LOG_MEMORY,2,TEXT("")));\r
+    DbgLog((LOG_MEMORY,1,szInfo));\r
+    LeaveCriticalSection(&m_CSDebug);\r
+}\r
+\r
+/*  Debug infinite wait stuff */\r
+DWORD WINAPI DbgWaitForSingleObject(HANDLE h)\r
+{\r
+    DWORD dwWaitResult;\r
+    do {\r
+        dwWaitResult = WaitForSingleObject(h, dwWaitTimeout);\r
+        ASSERT(dwWaitResult == WAIT_OBJECT_0);\r
+    } while (dwWaitResult == WAIT_TIMEOUT);\r
+    return dwWaitResult;\r
+}\r
+DWORD WINAPI DbgWaitForMultipleObjects(DWORD nCount,\r
+                                __in_ecount(nCount) CONST HANDLE *lpHandles,\r
+                                BOOL bWaitAll)\r
+{\r
+    DWORD dwWaitResult;\r
+    do {\r
+        dwWaitResult = WaitForMultipleObjects(nCount,\r
+                                              lpHandles,\r
+                                              bWaitAll,\r
+                                              dwWaitTimeout);\r
+        ASSERT((DWORD)(dwWaitResult - WAIT_OBJECT_0) < MAXIMUM_WAIT_OBJECTS);\r
+    } while (dwWaitResult == WAIT_TIMEOUT);\r
+    return dwWaitResult;\r
+}\r
+\r
+void WINAPI DbgSetWaitTimeout(DWORD dwTimeout)\r
+{\r
+    dwWaitTimeout = dwTimeout;\r
+}\r
+\r
+#endif /* DEBUG */\r
+\r
+#ifdef _OBJBASE_H_\r
+\r
+    /*  Stuff for printing out our GUID names */\r
+\r
+    GUID_STRING_ENTRY g_GuidNames[] = {\r
+    #define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \\r
+    { #name, { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } } },\r
+        #include <uuids.h>\r
+    };\r
+\r
+    CGuidNameList GuidNames;\r
+    int g_cGuidNames = sizeof(g_GuidNames) / sizeof(g_GuidNames[0]);\r
+\r
+    char *CGuidNameList::operator [] (const GUID &guid)\r
+    {\r
+        for (int i = 0; i < g_cGuidNames; i++) {\r
+            if (g_GuidNames[i].guid == guid) {\r
+                return g_GuidNames[i].szName;\r
+            }\r
+        }\r
+        if (guid == GUID_NULL) {\r
+            return "GUID_NULL";\r
+        }\r
+\r
+       // !!! add something to print FOURCC guids?\r
+       \r
+       // shouldn't this print the hex CLSID?\r
+        return "Unknown GUID Name";\r
+    }\r
+\r
+#endif /* _OBJBASE_H_ */\r
+\r
+/*  CDisp class - display our data types */\r
+\r
+// clashes with REFERENCE_TIME\r
+CDisp::CDisp(LONGLONG ll, int Format)\r
+{\r
+    // note: this could be combined with CDisp(LONGLONG) by\r
+    // introducing a default format of CDISP_REFTIME\r
+    LARGE_INTEGER li;\r
+    li.QuadPart = ll;\r
+    switch (Format) {\r
+       case CDISP_DEC:\r
+       {\r
+           TCHAR  temp[20];\r
+           int pos=20;\r
+           temp[--pos] = 0;\r
+           int digit;\r
+           // always output at least one digit\r
+           do {\r
+               // Get the rightmost digit - we only need the low word\r
+               digit = li.LowPart % 10;\r
+               li.QuadPart /= 10;\r
+               temp[--pos] = (TCHAR) digit+L'0';\r
+           } while (li.QuadPart);\r
+           (void)StringCchCopy(m_String, NUMELMS(m_String), temp+pos);\r
+           break;\r
+       }\r
+       case CDISP_HEX:\r
+       default:\r
+           (void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("0x%X%8.8X"), li.HighPart, li.LowPart);\r
+    }\r
+};\r
+\r
+CDisp::CDisp(REFCLSID clsid)\r
+{\r
+#ifdef UNICODE \r
+    (void)StringFromGUID2(clsid, m_String, NUMELMS(m_String));\r
+#else\r
+    WCHAR wszTemp[50];\r
+    (void)StringFromGUID2(clsid, wszTemp, NUMELMS(wszTemp));\r
+    (void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("%S"), wszTemp);\r
+#endif\r
+};\r
+\r
+#ifdef __STREAMS__\r
+/*  Display stuff */\r
+CDisp::CDisp(CRefTime llTime)\r
+{\r
+    LONGLONG llDiv;\r
+    if (llTime < 0) {\r
+        llTime = -llTime;\r
+        (void)StringCchCopy(m_String, NUMELMS(m_String), TEXT("-"));\r
+    }\r
+    llDiv = (LONGLONG)24 * 3600 * 10000000;\r
+    if (llTime >= llDiv) {\r
+        (void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d days "), (LONG)(llTime / llDiv));\r
+        llTime = llTime % llDiv;\r
+    }\r
+    llDiv = (LONGLONG)3600 * 10000000;\r
+    if (llTime >= llDiv) {\r
+        (void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d hrs "), (LONG)(llTime / llDiv));\r
+        llTime = llTime % llDiv;\r
+    }\r
+    llDiv = (LONGLONG)60 * 10000000;\r
+    if (llTime >= llDiv) {\r
+        (void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d mins "), (LONG)(llTime / llDiv));\r
+        llTime = llTime % llDiv;\r
+    }\r
+    (void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d.%3.3d sec"),\r
+             (LONG)llTime / 10000000,\r
+             (LONG)((llTime % 10000000) / 10000));\r
+};\r
+\r
+#endif // __STREAMS__\r
+\r
+\r
+/*  Display pin */\r
+CDisp::CDisp(IPin *pPin)\r
+{\r
+    PIN_INFO pi;\r
+    TCHAR str[MAX_PIN_NAME];\r
+    CLSID clsid;\r
+\r
+    if (pPin) {\r
+       pPin->QueryPinInfo(&pi);\r
+       pi.pFilter->GetClassID(&clsid);\r
+       QueryPinInfoReleaseFilter(pi);\r
+      #ifndef UNICODE\r
+       WideCharToMultiByte(GetACP(), 0, pi.achName, lstrlenW(pi.achName) + 1,\r
+                           str, MAX_PIN_NAME, NULL, NULL);\r
+      #else\r
+       (void)StringCchCopy(str, NUMELMS(str), pi.achName);\r
+      #endif\r
+    } else {\r
+       (void)StringCchCopy(str, NUMELMS(str), TEXT("NULL IPin"));\r
+    }\r
+\r
+    m_pString = (PTCHAR) new TCHAR[lstrlen(str)+64];\r
+    if (!m_pString) {\r
+       return;\r
+    }\r
+\r
+    (void)StringCchPrintf(m_pString, lstrlen(str) + 64, TEXT("%hs(%s)"), GuidNames[clsid], str);\r
+}\r
+\r
+/*  Display filter or pin */\r
+CDisp::CDisp(IUnknown *pUnk)\r
+{\r
+    IBaseFilter *pf;\r
+    HRESULT hr = pUnk->QueryInterface(IID_IBaseFilter, (void **)&pf);\r
+    if(SUCCEEDED(hr))\r
+    {\r
+        FILTER_INFO fi;\r
+        hr = pf->QueryFilterInfo(&fi);\r
+        if(SUCCEEDED(hr))\r
+        {\r
+            QueryFilterInfoReleaseGraph(fi);\r
+\r
+            size_t len = lstrlenW(fi.achName)  + 1;\r
+\r
+            m_pString = new TCHAR[len];\r
+            if(m_pString)\r
+            {\r
+#ifdef UNICODE\r
+                (void)StringCchCopy(m_pString, len, fi.achName);\r
+#else\r
+                (void)StringCchPrintf(m_pString, len, "%S", fi.achName);\r
+#endif\r
+            }\r
+        }\r
+\r
+        pf->Release();\r
+\r
+        return;\r
+    }\r
+\r
+    IPin *pp;\r
+    hr = pUnk->QueryInterface(IID_IPin, (void **)&pp);\r
+    if(SUCCEEDED(hr))\r
+    {\r
+        CDisp::CDisp(pp);\r
+        pp->Release();\r
+        return;\r
+    }\r
+}\r
+\r
+\r
+CDisp::~CDisp()\r
+{\r
+}\r
+\r
+CDispBasic::~CDispBasic()\r
+{\r
+    if (m_pString != m_String) {\r
+       delete [] m_pString;\r
+    }\r
+}\r
+\r
+CDisp::CDisp(double d)\r
+{\r
+    (void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("%d.%03d"), (int) d, (int) ((d - (int) d) * 1000));\r
+}\r
+\r
+\r
+/* If built for debug this will display the media type details. We convert the\r
+   major and subtypes into strings and also ask the base classes for a string\r
+   description of the subtype, so MEDIASUBTYPE_RGB565 becomes RGB 565 16 bit\r
+   We also display the fields in the BITMAPINFOHEADER structure, this should\r
+   succeed as we do not accept input types unless the format is big enough */\r
+\r
+#ifdef DEBUG\r
+void WINAPI DisplayType(LPCTSTR label, const AM_MEDIA_TYPE *pmtIn)\r
+{\r
+\r
+    /* Dump the GUID types and a short description */\r
+\r
+    DbgLog((LOG_TRACE,5,TEXT("")));\r
+    DbgLog((LOG_TRACE,2,TEXT("%s  M type %hs  S type %hs"), label,\r
+           GuidNames[pmtIn->majortype],\r
+           GuidNames[pmtIn->subtype]));\r
+    DbgLog((LOG_TRACE,5,TEXT("Subtype description %s"),GetSubtypeName(&pmtIn->subtype)));\r
+\r
+    /* Dump the generic media types */\r
+\r
+    if (pmtIn->bTemporalCompression) {\r
+        DbgLog((LOG_TRACE,5,TEXT("Temporally compressed")));\r
+    } else {\r
+        DbgLog((LOG_TRACE,5,TEXT("Not temporally compressed")));\r
+    }\r
+\r
+    if (pmtIn->bFixedSizeSamples) {\r
+        DbgLog((LOG_TRACE,5,TEXT("Sample size %d"),pmtIn->lSampleSize));\r
+    } else {\r
+        DbgLog((LOG_TRACE,5,TEXT("Variable size samples")));\r
+    }\r
+\r
+    if (pmtIn->formattype == FORMAT_VideoInfo) {\r
+\r
+        VIDEOINFOHEADER *pVideoInfo = (VIDEOINFOHEADER *)pmtIn->pbFormat;\r
+\r
+        DisplayRECT(TEXT("Source rectangle"),pVideoInfo->rcSource);\r
+        DisplayRECT(TEXT("Target rectangle"),pVideoInfo->rcTarget);\r
+        DisplayBITMAPINFO(HEADER(pmtIn->pbFormat));\r
+\r
+    } if (pmtIn->formattype == FORMAT_VideoInfo2) {\r
+\r
+        VIDEOINFOHEADER2 *pVideoInfo2 = (VIDEOINFOHEADER2 *)pmtIn->pbFormat;\r
+\r
+        DisplayRECT(TEXT("Source rectangle"),pVideoInfo2->rcSource);\r
+        DisplayRECT(TEXT("Target rectangle"),pVideoInfo2->rcTarget);\r
+        DbgLog((LOG_TRACE, 5, TEXT("Aspect Ratio: %d:%d"),\r
+            pVideoInfo2->dwPictAspectRatioX,\r
+            pVideoInfo2->dwPictAspectRatioY));\r
+        DisplayBITMAPINFO(&pVideoInfo2->bmiHeader);\r
+\r
+    } else if (pmtIn->majortype == MEDIATYPE_Audio) {\r
+        DbgLog((LOG_TRACE,2,TEXT("     Format type %hs"),\r
+            GuidNames[pmtIn->formattype]));\r
+        DbgLog((LOG_TRACE,2,TEXT("     Subtype %hs"),\r
+            GuidNames[pmtIn->subtype]));\r
+\r
+        if ((pmtIn->subtype != MEDIASUBTYPE_MPEG1Packet)\r
+          && (pmtIn->cbFormat >= sizeof(PCMWAVEFORMAT)))\r
+        {\r
+            /* Dump the contents of the WAVEFORMATEX type-specific format structure */\r
+\r
+            WAVEFORMATEX *pwfx = (WAVEFORMATEX *) pmtIn->pbFormat;\r
+            DbgLog((LOG_TRACE,2,TEXT("wFormatTag %u"), pwfx->wFormatTag));\r
+            DbgLog((LOG_TRACE,2,TEXT("nChannels %u"), pwfx->nChannels));\r
+            DbgLog((LOG_TRACE,2,TEXT("nSamplesPerSec %lu"), pwfx->nSamplesPerSec));\r
+            DbgLog((LOG_TRACE,2,TEXT("nAvgBytesPerSec %lu"), pwfx->nAvgBytesPerSec));\r
+            DbgLog((LOG_TRACE,2,TEXT("nBlockAlign %u"), pwfx->nBlockAlign));\r
+            DbgLog((LOG_TRACE,2,TEXT("wBitsPerSample %u"), pwfx->wBitsPerSample));\r
+\r
+            /* PCM uses a WAVEFORMAT and does not have the extra size field */\r
+\r
+            if (pmtIn->cbFormat >= sizeof(WAVEFORMATEX)) {\r
+                DbgLog((LOG_TRACE,2,TEXT("cbSize %u"), pwfx->cbSize));\r
+            }\r
+        } else {\r
+        }\r
+\r
+    } else {\r
+        DbgLog((LOG_TRACE,2,TEXT("     Format type %hs"),\r
+            GuidNames[pmtIn->formattype]));\r
+    }\r
+}\r
+\r
+\r
+void DisplayBITMAPINFO(const BITMAPINFOHEADER* pbmi)\r
+{\r
+    DbgLog((LOG_TRACE,5,TEXT("Size of BITMAPINFO structure %d"),pbmi->biSize));\r
+    if (pbmi->biCompression < 256) {\r
+        DbgLog((LOG_TRACE,2,TEXT("%dx%dx%d bit  (%d)"),\r
+                pbmi->biWidth, pbmi->biHeight,\r
+                pbmi->biBitCount, pbmi->biCompression));\r
+    } else {\r
+        DbgLog((LOG_TRACE,2,TEXT("%dx%dx%d bit '%4.4hs'"),\r
+                pbmi->biWidth, pbmi->biHeight,\r
+                pbmi->biBitCount, &pbmi->biCompression));\r
+    }\r
+\r
+    DbgLog((LOG_TRACE,2,TEXT("Image size %d"),pbmi->biSizeImage));\r
+    DbgLog((LOG_TRACE,5,TEXT("Planes %d"),pbmi->biPlanes));\r
+    DbgLog((LOG_TRACE,5,TEXT("X Pels per metre %d"),pbmi->biXPelsPerMeter));\r
+    DbgLog((LOG_TRACE,5,TEXT("Y Pels per metre %d"),pbmi->biYPelsPerMeter));\r
+    DbgLog((LOG_TRACE,5,TEXT("Colours used %d"),pbmi->biClrUsed));\r
+}\r
+\r
+\r
+void DisplayRECT(LPCTSTR szLabel, const RECT& rc)\r
+{\r
+    DbgLog((LOG_TRACE,5,TEXT("%s (Left %d Top %d Right %d Bottom %d)"),\r
+            szLabel,\r
+            rc.left,\r
+            rc.top,\r
+            rc.right,\r
+            rc.bottom));\r
+}\r
+\r
+\r
+void WINAPI DumpGraph(IFilterGraph *pGraph, DWORD dwLevel)\r
+{\r
+    if( !pGraph )\r
+    {\r
+        return;\r
+    }\r
+\r
+    IEnumFilters *pFilters;\r
+\r
+    DbgLog((LOG_TRACE,dwLevel,TEXT("DumpGraph [%x]"), pGraph));\r
+\r
+    if (FAILED(pGraph->EnumFilters(&pFilters))) {\r
+       DbgLog((LOG_TRACE,dwLevel,TEXT("EnumFilters failed!")));\r
+    }\r
+\r
+    IBaseFilter *pFilter;\r
+    ULONG      n;\r
+    while (pFilters->Next(1, &pFilter, &n) == S_OK) {\r
+       FILTER_INFO     info;\r
+\r
+       if (FAILED(pFilter->QueryFilterInfo(&info))) {\r
+           DbgLog((LOG_TRACE,dwLevel,TEXT("    Filter [%p]  -- failed QueryFilterInfo"), pFilter));\r
+       } else {\r
+           QueryFilterInfoReleaseGraph(info);\r
+\r
+           // !!! should QueryVendorInfo here!\r
+       \r
+           DbgLog((LOG_TRACE,dwLevel,TEXT("    Filter [%p]  '%ls'"), pFilter, info.achName));\r
+\r
+           IEnumPins *pins;\r
+\r
+           if (FAILED(pFilter->EnumPins(&pins))) {\r
+               DbgLog((LOG_TRACE,dwLevel,TEXT("EnumPins failed!")));\r
+           } else {\r
+\r
+               IPin *pPin;\r
+               while (pins->Next(1, &pPin, &n) == S_OK) {\r
+                   PIN_INFO    pinInfo;\r
+\r
+                   if (FAILED(pPin->QueryPinInfo(&pinInfo))) {\r
+                       DbgLog((LOG_TRACE,dwLevel,TEXT("          Pin [%x]  -- failed QueryPinInfo"), pPin));\r
+                   } else {\r
+                       QueryPinInfoReleaseFilter(pinInfo);\r
+\r
+                       IPin *pPinConnected = NULL;\r
+\r
+                       HRESULT hr = pPin->ConnectedTo(&pPinConnected);\r
+\r
+                       if (pPinConnected) {\r
+                           DbgLog((LOG_TRACE,dwLevel,TEXT("          Pin [%p]  '%ls' [%sput]")\r
+                                                          TEXT("  Connected to pin [%p]"),\r
+                                   pPin, pinInfo.achName,\r
+                                   pinInfo.dir == PINDIR_INPUT ? TEXT("In") : TEXT("Out"),\r
+                                   pPinConnected));\r
+\r
+                           pPinConnected->Release();\r
+\r
+                           // perhaps we should really dump the type both ways as a sanity\r
+                           // check?\r
+                           if (pinInfo.dir == PINDIR_OUTPUT) {\r
+                               AM_MEDIA_TYPE mt;\r
+\r
+                               hr = pPin->ConnectionMediaType(&mt);\r
+\r
+                               if (SUCCEEDED(hr)) {\r
+                                   DisplayType(TEXT("Connection type"), &mt);\r
+\r
+                                   FreeMediaType(mt);\r
+                               }\r
+                           }\r
+                       } else {\r
+                           DbgLog((LOG_TRACE,dwLevel,\r
+                                   TEXT("          Pin [%x]  '%ls' [%sput]"),\r
+                                   pPin, pinInfo.achName,\r
+                                   pinInfo.dir == PINDIR_INPUT ? TEXT("In") : TEXT("Out")));\r
+\r
+                       }\r
+                   }\r
+\r
+                   pPin->Release();\r
+\r
+               }\r
+\r
+               pins->Release();\r
+           }\r
+\r
+       }\r
+       \r
+       pFilter->Release();\r
+    }\r
+\r
+    pFilters->Release();\r
+\r
+}\r
+\r
+#endif\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/wxdebug.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/wxdebug.h
new file mode 100644 (file)
index 0000000..d4c69db
--- /dev/null
@@ -0,0 +1,359 @@
+//------------------------------------------------------------------------------\r
+// File: WXDebug.h\r
+//\r
+// Desc: DirectShow base classes - provides debugging facilities.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef __WXDEBUG__\r
+#define __WXDEBUG__\r
+\r
+// This library provides fairly straight forward debugging functionality, this\r
+// is split into two main sections. The first is assertion handling, there are\r
+// three types of assertions provided here. The most commonly used one is the\r
+// ASSERT(condition) macro which will pop up a message box including the file\r
+// and line number if the condition evaluates to FALSE. Then there is the\r
+// EXECUTE_ASSERT macro which is the same as ASSERT except the condition will\r
+// still be executed in NON debug builds. The final type of assertion is the\r
+// KASSERT macro which is more suitable for pure (perhaps kernel) filters as\r
+// the condition is printed onto the debugger rather than in a message box.\r
+//\r
+// The other part of the debug module facilties is general purpose logging.\r
+// This is accessed by calling DbgLog(). The function takes a type and level\r
+// field which define the type of informational string you are presenting and\r
+// it's relative importance. The type field can be a combination (one or more)\r
+// of LOG_TIMING, LOG_TRACE, LOG_MEMORY, LOG_LOCKING and LOG_ERROR. The level\r
+// is a DWORD value where zero defines highest important. Use of zero as the\r
+// debug logging level is to be encouraged ONLY for major errors or events as\r
+// they will ALWAYS be displayed on the debugger. Other debug output has it's\r
+// level matched against the current debug output level stored in the registry\r
+// for this module and if less than the current setting it will be displayed.\r
+//\r
+// Each module or executable has it's own debug output level for each of the\r
+// five types. These are read in when the DbgInitialise function is called\r
+// for DLLs linking to STRMBASE.LIB this is done automatically when the DLL\r
+// is loaded, executables must call it explicitely with the module instance\r
+// handle given to them through the WINMAIN entry point. An executable must\r
+// also call DbgTerminate when they have finished to clean up the resources\r
+// the debug library uses, once again this is done automatically for DLLs\r
+\r
+// These are the five different categories of logging information\r
+\r
+enum {  LOG_TIMING = 0x01,    // Timing and performance measurements\r
+        LOG_TRACE = 0x02,     // General step point call tracing\r
+        LOG_MEMORY =  0x04,   // Memory and object allocation/destruction\r
+        LOG_LOCKING = 0x08,   // Locking/unlocking of critical sections\r
+        LOG_ERROR = 0x10,     // Debug error notification\r
+        LOG_CUSTOM1 = 0x20,\r
+        LOG_CUSTOM2 = 0x40,\r
+        LOG_CUSTOM3 = 0x80,\r
+        LOG_CUSTOM4 = 0x100,\r
+        LOG_CUSTOM5 = 0x200,\r
+};\r
+\r
+#define LOG_FORCIBLY_SET 0x80000000\r
+\r
+enum {  CDISP_HEX = 0x01,\r
+        CDISP_DEC = 0x02};\r
+\r
+// For each object created derived from CBaseObject (in debug builds) we\r
+// create a descriptor that holds it's name (statically allocated memory)\r
+// and a cookie we assign it. We keep a list of all the active objects\r
+// we have registered so that we can dump a list of remaining objects\r
+\r
+typedef struct tag_ObjectDesc {\r
+    LPCSTR m_szName;\r
+    LPCWSTR m_wszName;\r
+    DWORD m_dwCookie;\r
+    tag_ObjectDesc *m_pNext;\r
+} ObjectDesc;\r
+\r
+#define DLLIMPORT __declspec(dllimport)\r
+#define DLLEXPORT __declspec(dllexport)\r
+\r
+#ifdef DEBUG\r
+\r
+    #define NAME(x) TEXT(x)\r
+\r
+    // These are used internally by the debug library (PRIVATE)\r
+\r
+    void WINAPI DbgInitKeyLevels(HKEY hKey, bool fTakeMax);\r
+    void WINAPI DbgInitGlobalSettings(bool fTakeMax);\r
+    void WINAPI DbgInitModuleSettings(bool fTakeMax);\r
+    void WINAPI DbgInitModuleName();\r
+    DWORD WINAPI DbgRegisterObjectCreation(\r
+        LPCSTR szObjectName, LPCWSTR wszObjectName);\r
+\r
+    BOOL WINAPI DbgRegisterObjectDestruction(DWORD dwCookie);\r
+\r
+    // These are the PUBLIC entry points\r
+\r
+    BOOL WINAPI DbgCheckModuleLevel(DWORD Type,DWORD Level);\r
+    void WINAPI DbgSetModuleLevel(DWORD Type,DWORD Level);\r
+    void WINAPI DbgSetAutoRefreshLevels(bool fAuto);\r
+\r
+    // Initialise the library with the module handle\r
+\r
+    void WINAPI DbgInitialise(HINSTANCE hInst);\r
+    void WINAPI DbgTerminate();\r
+\r
+    void WINAPI DbgDumpObjectRegister();\r
+\r
+    // Display error and logging to the user\r
+\r
+    void WINAPI DbgAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine);\r
+    void WINAPI DbgBreakPoint(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine);\r
+    void WINAPI DbgBreakPoint(LPCTSTR pFileName,INT iLine,__format_string LPCTSTR  szFormatString,...);\r
+\r
+    void WINAPI DbgKernelAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine);\r
+    void WINAPI DbgLogInfo(DWORD Type,DWORD Level,__format_string LPCTSTR pFormat,...);\r
+#ifdef UNICODE\r
+    void WINAPI DbgLogInfo(DWORD Type,DWORD Level,__format_string LPCSTR pFormat,...);\r
+    void WINAPI DbgAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine);\r
+    void WINAPI DbgBreakPoint(LPCSTR pCondition,LPCSTR pFileName,INT iLine);\r
+    void WINAPI DbgKernelAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine);\r
+#endif\r
+    void WINAPI DbgOutString(LPCTSTR psz);\r
+\r
+    //  Debug infinite wait stuff\r
+    DWORD WINAPI DbgWaitForSingleObject(HANDLE h);\r
+    DWORD WINAPI DbgWaitForMultipleObjects(DWORD nCount,\r
+                                    __in_ecount(nCount) CONST HANDLE *lpHandles,\r
+                                    BOOL bWaitAll);\r
+    void WINAPI DbgSetWaitTimeout(DWORD dwTimeout);\r
+\r
+#ifdef __strmif_h__\r
+    // Display a media type: Terse at level 2, verbose at level 5\r
+    void WINAPI DisplayType(LPCTSTR label, const AM_MEDIA_TYPE *pmtIn);\r
+\r
+    // Dump lots of information about a filter graph\r
+    void WINAPI DumpGraph(IFilterGraph *pGraph, DWORD dwLevel);\r
+#endif\r
+\r
+    #define KASSERT(_x_) if (!(_x_))         \\r
+        DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)\r
+\r
+    //  Break on the debugger without putting up a message box\r
+    //  message goes to debugger instead\r
+\r
+    #define KDbgBreak(_x_)                   \\r
+        DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)\r
+\r
+    // We chose a common name for our ASSERT macro, MFC also uses this name\r
+    // So long as the implementation evaluates the condition and handles it\r
+    // then we will be ok. Rather than override the behaviour expected we\r
+    // will leave whatever first defines ASSERT as the handler (i.e. MFC)\r
+    #ifndef ASSERT\r
+        #define ASSERT(_x_) if (!(_x_))         \\r
+            DbgAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)\r
+    #endif\r
+\r
+    #define DbgAssertAligned( _ptr_, _alignment_ ) ASSERT( ((DWORD_PTR) (_ptr_)) % (_alignment_) == 0)\r
+\r
+    //  Put up a message box informing the user of a halt\r
+    //  condition in the program\r
+\r
+    #define DbgBreak(_x_)                   \\r
+        DbgBreakPoint(TEXT(#_x_),TEXT(__FILE__),__LINE__)\r
+\r
+    #define EXECUTE_ASSERT(_x_) ASSERT(_x_)\r
+    #define DbgLog(_x_) DbgLogInfo _x_\r
+    // MFC style trace macros\r
+\r
+    #define NOTE(_x_)             DbgLog((LOG_TRACE,5,TEXT(_x_)))\r
+    #define NOTE1(_x_,a)          DbgLog((LOG_TRACE,5,TEXT(_x_),a))\r
+    #define NOTE2(_x_,a,b)        DbgLog((LOG_TRACE,5,TEXT(_x_),a,b))\r
+    #define NOTE3(_x_,a,b,c)      DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c))\r
+    #define NOTE4(_x_,a,b,c,d)    DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d))\r
+    #define NOTE5(_x_,a,b,c,d,e)  DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d,e))\r
+\r
+#else\r
+\r
+    // Retail builds make public debug functions inert  - WARNING the source\r
+    // files do not define or build any of the entry points in debug builds\r
+    // (public entry points compile to nothing) so if you go trying to call\r
+    // any of the private entry points in your source they won't compile\r
+\r
+    #define NAME(_x_) ((LPTSTR) NULL)\r
+\r
+    #define DbgInitialise(hInst)\r
+    #define DbgTerminate()\r
+    #define DbgLog(_x_) 0\r
+    #define DbgOutString(psz)\r
+    #define DbgAssertAligned( _ptr_, _alignment_ ) 0\r
+\r
+    #define DbgRegisterObjectCreation(pObjectName)\r
+    #define DbgRegisterObjectDestruction(dwCookie)\r
+    #define DbgDumpObjectRegister()\r
+\r
+    #define DbgCheckModuleLevel(Type,Level)\r
+    #define DbgSetModuleLevel(Type,Level)\r
+    #define DbgSetAutoRefreshLevels(fAuto)\r
+\r
+    #define DbgWaitForSingleObject(h)  WaitForSingleObject(h, INFINITE)\r
+    #define DbgWaitForMultipleObjects(nCount, lpHandles, bWaitAll)     \\r
+               WaitForMultipleObjects(nCount, lpHandles, bWaitAll, INFINITE)\r
+    #define DbgSetWaitTimeout(dwTimeout)\r
+\r
+    #define KDbgBreak(_x_)\r
+    #define DbgBreak(_x_)\r
+\r
+    #define KASSERT(_x_) ((void)0)\r
+    #ifndef ASSERT\r
+       #define ASSERT(_x_) ((void)0)\r
+    #endif\r
+    #define EXECUTE_ASSERT(_x_) ((void)(_x_))\r
+\r
+    // MFC style trace macros\r
+\r
+    #define NOTE(_x_) ((void)0)\r
+    #define NOTE1(_x_,a) ((void)0)\r
+    #define NOTE2(_x_,a,b) ((void)0)\r
+    #define NOTE3(_x_,a,b,c) ((void)0)\r
+    #define NOTE4(_x_,a,b,c,d) ((void)0)\r
+    #define NOTE5(_x_,a,b,c,d,e) ((void)0)\r
+\r
+    #define DisplayType(label, pmtIn) ((void)0)\r
+    #define DumpGraph(pGraph, label) ((void)0)\r
+#endif\r
+\r
+\r
+// Checks a pointer which should be non NULL - can be used as follows.\r
+\r
+#define CheckPointer(p,ret) {if((p)==NULL) return (ret);}\r
+\r
+//   HRESULT Foo(VOID *pBar)\r
+//   {\r
+//       CheckPointer(pBar,E_INVALIDARG)\r
+//   }\r
+//\r
+//   Or if the function returns a boolean\r
+//\r
+//   BOOL Foo(VOID *pBar)\r
+//   {\r
+//       CheckPointer(pBar,FALSE)\r
+//   }\r
+\r
+#define ValidateReadPtr(p,cb) 0\r
+#define ValidateWritePtr(p,cb) 0\r
+#define ValidateReadWritePtr(p,cb) 0\r
+#define ValidateStringPtr(p) 0\r
+#define ValidateStringPtrA(p) 0\r
+#define ValidateStringPtrW(p) 0\r
+\r
+\r
+#ifdef _OBJBASE_H_\r
+\r
+    //  Outputting GUID names.  If you want to include the name\r
+    //  associated with a GUID (eg CLSID_...) then\r
+    //\r
+    //      GuidNames[yourGUID]\r
+    //\r
+    //  Returns the name defined in uuids.h as a string\r
+\r
+    typedef struct {\r
+        CHAR   *szName;\r
+        GUID    guid;\r
+    } GUID_STRING_ENTRY;\r
+\r
+    class CGuidNameList {\r
+    public:\r
+        CHAR *operator [] (const GUID& guid);\r
+    };\r
+\r
+    extern CGuidNameList GuidNames;\r
+\r
+#endif\r
+\r
+#ifndef REMIND\r
+    //  REMIND macro - generates warning as reminder to complete coding\r
+    //  (eg) usage:\r
+    //\r
+    //  #pragma message (REMIND("Add automation support"))\r
+\r
+\r
+    #define QUOTE(x) #x\r
+    #define QQUOTE(y) QUOTE(y)\r
+    #define REMIND(str) __FILE__ "(" QQUOTE(__LINE__) ") :  " str\r
+#endif\r
+\r
+//  Method to display objects in a useful format\r
+//\r
+//  eg If you want to display a LONGLONG ll in a debug string do (eg)\r
+//\r
+//  DbgLog((LOG_TRACE, n, TEXT("Value is %s"), (LPCTSTR)CDisp(ll, CDISP_HEX)));\r
+\r
+\r
+class CDispBasic\r
+{\r
+public:\r
+    CDispBasic() { m_pString = m_String; };\r
+    ~CDispBasic();\r
+protected:\r
+    PTCHAR m_pString;  // normally points to m_String... unless too much data\r
+    TCHAR m_String[50];\r
+};\r
+class CDisp : public CDispBasic\r
+{\r
+public:\r
+    CDisp(LONGLONG ll, int Format = CDISP_HEX); // Display a LONGLONG in CDISP_HEX or CDISP_DEC form\r
+    CDisp(REFCLSID clsid);      // Display a GUID\r
+    CDisp(double d);            // Display a floating point number\r
+#ifdef __strmif_h__\r
+#ifdef __STREAMS__\r
+    CDisp(CRefTime t);          // Display a Reference Time\r
+#endif\r
+    CDisp(IPin *pPin);          // Display a pin as {filter clsid}(pin name)\r
+    CDisp(IUnknown *pUnk);      // Display a filter or pin\r
+#endif // __strmif_h__\r
+    ~CDisp();\r
+\r
+    //  Implement cast to (LPCTSTR) as parameter to logger\r
+    operator LPCTSTR()\r
+    {\r
+        return (LPCTSTR)m_pString;\r
+    };\r
+};\r
+\r
+\r
+#if defined(DEBUG)\r
+class CAutoTrace\r
+{\r
+private:\r
+    LPCTSTR  _szBlkName;\r
+    const int _level;\r
+    static const TCHAR _szEntering[];\r
+    static const TCHAR _szLeaving[];\r
+public:\r
+    CAutoTrace(LPCTSTR szBlkName, const int level = 15)\r
+        : _szBlkName(szBlkName), _level(level)\r
+    {DbgLog((LOG_TRACE, _level, _szEntering, _szBlkName));}\r
+\r
+    ~CAutoTrace()\r
+    {DbgLog((LOG_TRACE, _level, _szLeaving, _szBlkName));}\r
+};\r
+\r
+#if defined (__FUNCTION__)\r
+\r
+#define AMTRACEFN()  CAutoTrace __trace(TEXT(__FUNCTION__))\r
+#define AMTRACE(_x_) CAutoTrace __trace(TEXT(__FUNCTION__))\r
+\r
+#else\r
+\r
+#define AMTRACE(_x_) CAutoTrace __trace _x_\r
+#define AMTRACEFN()\r
+\r
+#endif\r
+\r
+#else\r
+\r
+#define AMTRACE(_x_)\r
+#define AMTRACEFN()\r
+\r
+#endif\r
+\r
+#endif // __WXDEBUG__\r
+\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/wxlist.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/wxlist.cpp
new file mode 100644 (file)
index 0000000..2ec67a4
--- /dev/null
@@ -0,0 +1,891 @@
+//------------------------------------------------------------------------------\r
+// File: WXList.cpp\r
+//\r
+// Desc: DirectShow base classes - implements a non-MFC based generic list\r
+//       template class.\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+/* A generic list of pointers to objects.\r
+   Objectives: avoid using MFC libraries in ndm kernel mode and\r
+   provide a really useful list type.\r
+\r
+   The class is thread safe in that separate threads may add and\r
+   delete items in the list concurrently although the application\r
+   must ensure that constructor and destructor access is suitably\r
+   synchronised.\r
+\r
+   The list name must not conflict with MFC classes as an\r
+   application may use both\r
+\r
+   The nodes form a doubly linked, NULL terminated chain with an anchor\r
+   block (the list object per se) holding pointers to the first and last\r
+   nodes and a count of the nodes.\r
+   There is a node cache to reduce the allocation and freeing overhead.\r
+   It optionally (determined at construction time) has an Event which is\r
+   set whenever the list becomes non-empty and reset whenever it becomes\r
+   empty.\r
+   It optionally (determined at construction time) has a Critical Section\r
+   which is entered during the important part of each operation.  (About\r
+   all you can do outside it is some parameter checking).\r
+\r
+   The node cache is a repository of nodes that are NOT in the list to speed\r
+   up storage allocation.  Each list has its own cache to reduce locking and\r
+   serialising.  The list accesses are serialised anyway for a given list - a\r
+   common cache would mean that we would have to separately serialise access\r
+   of all lists within the cache.  Because the cache only stores nodes that are\r
+   not in the list, releasing the cache does not release any list nodes.  This\r
+   means that list nodes can be copied or rechained from one list to another\r
+   without danger of creating a dangling reference if the original cache goes\r
+   away.\r
+\r
+   Questionable design decisions:\r
+   1. Retaining the warts for compatibility\r
+   2. Keeping an element count -i.e. counting whenever we do anything\r
+      instead of only when we want the count.\r
+   3. Making the chain pointers NULL terminated.  If the list object\r
+      itself looks just like a node and the list is kept as a ring then\r
+      it reduces the number of special cases.  All inserts look the same.\r
+*/\r
+\r
+\r
+#include <streams.h>\r
+\r
+/* set cursor to the position of each element of list in turn  */\r
+#define INTERNALTRAVERSELIST(list, cursor)               \\r
+for ( cursor = (list).GetHeadPositionI()           \\r
+    ; cursor!=NULL                               \\r
+    ; cursor = (list).Next(cursor)                \\r
+    )\r
+\r
+\r
+/* set cursor to the position of each element of list in turn\r
+   in reverse order\r
+*/\r
+#define INTERNALREVERSETRAVERSELIST(list, cursor)        \\r
+for ( cursor = (list).GetTailPositionI()           \\r
+    ; cursor!=NULL                               \\r
+    ; cursor = (list).Prev(cursor)                \\r
+    )\r
+\r
+/* Constructor calls a separate initialisation function that\r
+   creates a node cache, optionally creates a lock object\r
+   and optionally creates a signaling object.\r
+\r
+   By default we create a locking object, a DEFAULTCACHE sized\r
+   cache but no event object so the list cannot be used in calls\r
+   to WaitForSingleObject\r
+*/\r
+CBaseList::CBaseList(__in_opt LPCTSTR pName,    // Descriptive list name\r
+                     INT iItems) :    // Node cache size\r
+#ifdef DEBUG\r
+    CBaseObject(pName),\r
+#endif\r
+    m_pFirst(NULL),\r
+    m_pLast(NULL),\r
+    m_Count(0),\r
+    m_Cache(iItems)\r
+{\r
+} // constructor\r
+\r
+CBaseList::CBaseList(__in_opt LPCTSTR pName) :  // Descriptive list name\r
+#ifdef DEBUG\r
+    CBaseObject(pName),\r
+#endif\r
+    m_pFirst(NULL),\r
+    m_pLast(NULL),\r
+    m_Count(0),\r
+    m_Cache(DEFAULTCACHE)\r
+{\r
+} // constructor\r
+\r
+#ifdef UNICODE\r
+CBaseList::CBaseList(__in_opt LPCSTR pName,    // Descriptive list name\r
+                     INT iItems) :    // Node cache size\r
+#ifdef DEBUG\r
+    CBaseObject(pName),\r
+#endif\r
+    m_pFirst(NULL),\r
+    m_pLast(NULL),\r
+    m_Count(0),\r
+    m_Cache(iItems)\r
+{\r
+} // constructor\r
+\r
+CBaseList::CBaseList(__in_opt LPCSTR pName) :  // Descriptive list name\r
+#ifdef DEBUG\r
+    CBaseObject(pName),\r
+#endif\r
+    m_pFirst(NULL),\r
+    m_pLast(NULL),\r
+    m_Count(0),\r
+    m_Cache(DEFAULTCACHE)\r
+{\r
+} // constructor\r
+\r
+#endif\r
+\r
+/* The destructor enumerates all the node objects in the list and\r
+   in the cache deleting each in turn. We do not do any processing\r
+   on the objects that the list holds (i.e. points to) so if they\r
+   represent interfaces for example the creator of the list should\r
+   ensure that each of them is released before deleting us\r
+*/\r
+CBaseList::~CBaseList()\r
+{\r
+    /* Delete all our list nodes */\r
+\r
+    RemoveAll();\r
+\r
+} // destructor\r
+\r
+/* Remove all the nodes from the list but don't do anything\r
+   with the objects that each node looks after (this is the\r
+   responsibility of the creator).\r
+   Aa a last act we reset the signalling event\r
+   (if available) to indicate to clients that the list\r
+   does not have any entries in it.\r
+*/\r
+void CBaseList::RemoveAll()\r
+{\r
+    /* Free up all the CNode objects NOTE we don't bother putting the\r
+       deleted nodes into the cache as this method is only really called\r
+       in serious times of change such as when we are being deleted at\r
+       which point the cache will be deleted anway */\r
+\r
+    CNode *pn = m_pFirst;\r
+    while (pn) {\r
+        CNode *op = pn;\r
+        pn = pn->Next();\r
+        delete op;\r
+    }\r
+\r
+    /* Reset the object count and the list pointers */\r
+\r
+    m_Count = 0;\r
+    m_pFirst = m_pLast = NULL;\r
+\r
+} // RemoveAll\r
+\r
+\r
+\r
+/* Return a position enumerator for the entire list.\r
+   A position enumerator is a pointer to a node object cast to a\r
+   transparent type so all we do is return the head/tail node\r
+   pointer in the list.\r
+   WARNING because the position is a pointer to a node there is\r
+   an implicit assumption for users a the list class that after\r
+   deleting an object from the list that any other position\r
+   enumerators that you have may be invalid (since the node\r
+   may be gone).\r
+*/\r
+__out_opt POSITION CBaseList::GetHeadPositionI() const\r
+{\r
+    return (POSITION) m_pFirst;\r
+} // GetHeadPosition\r
+\r
+\r
+\r
+__out_opt POSITION CBaseList::GetTailPositionI() const\r
+{\r
+    return (POSITION) m_pLast;\r
+} // GetTailPosition\r
+\r
+\r
+\r
+/* Get the number of objects in the list,\r
+   Get the lock before accessing the count.\r
+   Locking may not be entirely necessary but it has the side effect\r
+   of making sure that all operations are complete before we get it.\r
+   So for example if a list is being added to this list then that\r
+   will have completed in full before we continue rather than seeing\r
+   an intermediate albeit valid state\r
+*/\r
+int CBaseList::GetCountI() const\r
+{\r
+    return m_Count;\r
+} // GetCount\r
+\r
+\r
+\r
+/* Return the object at rp, update rp to the next object from\r
+   the list or NULL if you have moved over the last object.\r
+   You may still call this function once we return NULL but\r
+   we will continue to return a NULL position value\r
+*/\r
+__out void *CBaseList::GetNextI(__inout POSITION& rp) const\r
+{\r
+    /* have we reached the end of the list */\r
+\r
+    if (rp == NULL) {\r
+        return NULL;\r
+    }\r
+\r
+    /* Lock the object before continuing */\r
+\r
+    void *pObject;\r
+\r
+    /* Copy the original position then step on */\r
+\r
+    CNode *pn = (CNode *) rp;\r
+    ASSERT(pn != NULL);\r
+    rp = (POSITION) pn->Next();\r
+\r
+    /* Get the object at the original position from the list */\r
+\r
+    pObject = pn->GetData();\r
+    // ASSERT(pObject != NULL);    // NULL pointers in the list are allowed.\r
+    return pObject;\r
+} //GetNext\r
+\r
+\r
+\r
+/* Return the object at p.\r
+   Asking for the object at NULL ASSERTs then returns NULL\r
+   The object is NOT locked.  The list is not being changed\r
+   in any way.  If another thread is busy deleting the object\r
+   then locking would only result in a change from one bad\r
+   behaviour to another.\r
+*/\r
+__out_opt void *CBaseList::GetI(__in_opt POSITION p) const\r
+{\r
+    if (p == NULL) {\r
+        return NULL;\r
+    }\r
+\r
+    CNode * pn = (CNode *) p;\r
+    void *pObject = pn->GetData();\r
+    // ASSERT(pObject != NULL);    // NULL pointers in the list are allowed.\r
+    return pObject;\r
+} //Get\r
+\r
+__out void *CBaseList::GetValidI(__in POSITION p) const\r
+{\r
+    CNode * pn = (CNode *) p;\r
+    void *pObject = pn->GetData();\r
+    // ASSERT(pObject != NULL);    // NULL pointers in the list are allowed.\r
+    return pObject;\r
+} //Get\r
+\r
+\r
+/* Return the first position in the list which holds the given pointer.\r
+   Return NULL if it's not found.\r
+*/\r
+__out_opt POSITION CBaseList::FindI( __in void * pObj) const\r
+{\r
+    POSITION pn;\r
+    INTERNALTRAVERSELIST(*this, pn){\r
+        if (GetI(pn)==pObj) {\r
+            return pn;\r
+        }\r
+    }\r
+    return NULL;\r
+} // Find\r
+\r
+\r
+\r
+/* Remove the first node in the list (deletes the pointer to its object\r
+   from the list, does not free the object itself).\r
+   Return the pointer to its object or NULL if empty\r
+*/\r
+__out_opt void *CBaseList::RemoveHeadI()\r
+{\r
+    /* All we do is get the head position and ask for that to be deleted.\r
+       We could special case this since some of the code path checking\r
+       in Remove() is redundant as we know there is no previous\r
+       node for example but it seems to gain little over the\r
+       added complexity\r
+    */\r
+\r
+    return RemoveI((POSITION)m_pFirst);\r
+} // RemoveHead\r
+\r
+\r
+\r
+/* Remove the last node in the list (deletes the pointer to its object\r
+   from the list, does not free the object itself).\r
+   Return the pointer to its object or NULL if empty\r
+*/\r
+__out_opt void *CBaseList::RemoveTailI()\r
+{\r
+    /* All we do is get the tail position and ask for that to be deleted.\r
+       We could special case this since some of the code path checking\r
+       in Remove() is redundant as we know there is no previous\r
+       node for example but it seems to gain little over the\r
+       added complexity\r
+    */\r
+\r
+    return RemoveI((POSITION)m_pLast);\r
+} // RemoveTail\r
+\r
+\r
+\r
+/* Remove the pointer to the object in this position from the list.\r
+   Deal with all the chain pointers\r
+   Return a pointer to the object removed from the list.\r
+   The node object that is freed as a result\r
+   of this operation is added to the node cache where\r
+   it can be used again.\r
+   Remove(NULL) is a harmless no-op - but probably is a wart.\r
+*/\r
+__out_opt void *CBaseList::RemoveI(__in_opt POSITION pos)\r
+{\r
+    /* Lock the critical section before continuing */\r
+\r
+    // ASSERT (pos!=NULL);     // Removing NULL is to be harmless!\r
+    if (pos==NULL) return NULL;\r
+\r
+\r
+    CNode *pCurrent = (CNode *) pos;\r
+    ASSERT(pCurrent != NULL);\r
+\r
+    /* Update the previous node */\r
+\r
+    CNode *pNode = pCurrent->Prev();\r
+    if (pNode == NULL) {\r
+        m_pFirst = pCurrent->Next();\r
+    } else {\r
+        pNode->SetNext(pCurrent->Next());\r
+    }\r
+\r
+    /* Update the following node */\r
+\r
+    pNode = pCurrent->Next();\r
+    if (pNode == NULL) {\r
+        m_pLast = pCurrent->Prev();\r
+    } else {\r
+        pNode->SetPrev(pCurrent->Prev());\r
+    }\r
+\r
+    /* Get the object this node was looking after */\r
+\r
+    void *pObject = pCurrent->GetData();\r
+\r
+    // ASSERT(pObject != NULL);    // NULL pointers in the list are allowed.\r
+\r
+    /* Try and add the node object to the cache -\r
+       a NULL return code from the cache means we ran out of room.\r
+       The cache size is fixed by a constructor argument when the\r
+       list is created and defaults to DEFAULTCACHE.\r
+       This means that the cache will have room for this many\r
+       node objects. So if you have a list of media samples\r
+       and you know there will never be more than five active at\r
+       any given time of them for example then override the default\r
+       constructor\r
+    */\r
+\r
+    m_Cache.AddToCache(pCurrent);\r
+\r
+    /* If the list is empty then reset the list event */\r
+\r
+    --m_Count;\r
+    ASSERT(m_Count >= 0);\r
+    return pObject;\r
+} // Remove\r
+\r
+\r
+\r
+/* Add this object to the tail end of our list\r
+   Return the new tail position.\r
+*/\r
+\r
+__out_opt POSITION CBaseList::AddTailI(__in void *pObject)\r
+{\r
+    /* Lock the critical section before continuing */\r
+\r
+    CNode *pNode;\r
+    // ASSERT(pObject);   // NULL pointers in the list are allowed.\r
+\r
+    /* If there is a node objects in the cache then use\r
+       that otherwise we will have to create a new one */\r
+\r
+    pNode = (CNode *) m_Cache.RemoveFromCache();\r
+    if (pNode == NULL) {\r
+        pNode = new CNode;\r
+    }\r
+\r
+    /* Check we have a valid object */\r
+\r
+    if (pNode == NULL) {\r
+        return NULL;\r
+    }\r
+\r
+    /* Initialise all the CNode object\r
+       just in case it came from the cache\r
+    */\r
+\r
+    pNode->SetData(pObject);\r
+    pNode->SetNext(NULL);\r
+    pNode->SetPrev(m_pLast);\r
+\r
+    if (m_pLast == NULL) {\r
+        m_pFirst = pNode;\r
+    } else {\r
+        m_pLast->SetNext(pNode);\r
+    }\r
+\r
+    /* Set the new last node pointer and also increment the number\r
+       of list entries, the critical section is unlocked when we\r
+       exit the function\r
+    */\r
+\r
+    m_pLast = pNode;\r
+    ++m_Count;\r
+\r
+    return (POSITION) pNode;\r
+} // AddTail(object)\r
+\r
+\r
+\r
+/* Add this object to the head end of our list\r
+   Return the new head position.\r
+*/\r
+__out_opt POSITION CBaseList::AddHeadI(__in void *pObject)\r
+{\r
+    CNode *pNode;\r
+    // ASSERT(pObject);  // NULL pointers in the list are allowed.\r
+\r
+    /* If there is a node objects in the cache then use\r
+       that otherwise we will have to create a new one */\r
+\r
+    pNode = (CNode *) m_Cache.RemoveFromCache();\r
+    if (pNode == NULL) {\r
+        pNode = new CNode;\r
+    }\r
+\r
+    /* Check we have a valid object */\r
+\r
+    if (pNode == NULL) {\r
+        return NULL;\r
+    }\r
+\r
+    /* Initialise all the CNode object\r
+       just in case it came from the cache\r
+    */\r
+\r
+    pNode->SetData(pObject);\r
+\r
+    /* chain it in (set four pointers) */\r
+    pNode->SetPrev(NULL);\r
+    pNode->SetNext(m_pFirst);\r
+\r
+    if (m_pFirst == NULL) {\r
+        m_pLast = pNode;\r
+    } else {\r
+        m_pFirst->SetPrev(pNode);\r
+    }\r
+    m_pFirst = pNode;\r
+\r
+    ++m_Count;\r
+\r
+    return (POSITION) pNode;\r
+} // AddHead(object)\r
+\r
+\r
+\r
+/* Add all the elements in *pList to the tail of this list.\r
+   Return TRUE if it all worked, FALSE if it didn't.\r
+   If it fails some elements may have been added.\r
+*/\r
+BOOL CBaseList::AddTail(__in CBaseList *pList)\r
+{\r
+    /* lock the object before starting then enumerate\r
+       each entry in the source list and add them one by one to\r
+       our list (while still holding the object lock)\r
+       Lock the other list too.\r
+    */\r
+    POSITION pos = pList->GetHeadPositionI();\r
+\r
+    while (pos) {\r
+       if (NULL == AddTailI(pList->GetNextI(pos))) {\r
+           return FALSE;\r
+       }\r
+    }\r
+    return TRUE;\r
+} // AddTail(list)\r
+\r
+\r
+\r
+/* Add all the elements in *pList to the head of this list.\r
+   Return TRUE if it all worked, FALSE if it didn't.\r
+   If it fails some elements may have been added.\r
+*/\r
+BOOL CBaseList::AddHead(__in CBaseList *pList)\r
+{\r
+    /* lock the object before starting then enumerate\r
+       each entry in the source list and add them one by one to\r
+       our list (while still holding the object lock)\r
+       Lock the other list too.\r
+\r
+       To avoid reversing the list, traverse it backwards.\r
+    */\r
+\r
+    POSITION pos;\r
+\r
+    INTERNALREVERSETRAVERSELIST(*pList, pos) {\r
+        if (NULL== AddHeadI(pList->GetValidI(pos))){\r
+            return FALSE;\r
+        }\r
+    }\r
+    return TRUE;\r
+} // AddHead(list)\r
+\r
+\r
+\r
+/* Add the object after position p\r
+   p is still valid after the operation.\r
+   AddAfter(NULL,x) adds x to the start - same as AddHead\r
+   Return the position of the new object, NULL if it failed\r
+*/\r
+__out_opt POSITION  CBaseList::AddAfterI(__in_opt POSITION pos, __in void * pObj)\r
+{\r
+    if (pos==NULL)\r
+        return AddHeadI(pObj);\r
+\r
+    /* As someone else might be furkling with the list -\r
+       Lock the critical section before continuing\r
+    */\r
+    CNode *pAfter = (CNode *) pos;\r
+    ASSERT(pAfter != NULL);\r
+    if (pAfter==m_pLast)\r
+        return AddTailI(pObj);\r
+\r
+    /* set pnode to point to a new node, preferably from the cache */\r
+\r
+    CNode *pNode = (CNode *) m_Cache.RemoveFromCache();\r
+    if (pNode == NULL) {\r
+        pNode = new CNode;\r
+    }\r
+\r
+    /* Check we have a valid object */\r
+\r
+    if (pNode == NULL) {\r
+        return NULL;\r
+    }\r
+\r
+    /* Initialise all the CNode object\r
+       just in case it came from the cache\r
+    */\r
+\r
+    pNode->SetData(pObj);\r
+\r
+    /* It is to be added to the middle of the list - there is a before\r
+       and after node.  Chain it after pAfter, before pBefore.\r
+    */\r
+    CNode * pBefore = pAfter->Next();\r
+    ASSERT(pBefore != NULL);\r
+\r
+    /* chain it in (set four pointers) */\r
+    pNode->SetPrev(pAfter);\r
+    pNode->SetNext(pBefore);\r
+    pBefore->SetPrev(pNode);\r
+    pAfter->SetNext(pNode);\r
+\r
+    ++m_Count;\r
+\r
+    return (POSITION) pNode;\r
+\r
+} // AddAfter(object)\r
+\r
+\r
+\r
+BOOL CBaseList::AddAfter(__in_opt POSITION p, __in CBaseList *pList)\r
+{\r
+    POSITION pos;\r
+    INTERNALTRAVERSELIST(*pList, pos) {\r
+        /* p follows along the elements being added */\r
+        p = AddAfterI(p, pList->GetValidI(pos));\r
+        if (p==NULL) return FALSE;\r
+    }\r
+    return TRUE;\r
+} // AddAfter(list)\r
+\r
+\r
+\r
+/* Mirror images:\r
+   Add the element or list after position p.\r
+   p is still valid after the operation.\r
+   AddBefore(NULL,x) adds x to the end - same as AddTail\r
+*/\r
+__out_opt POSITION CBaseList::AddBeforeI(__in_opt POSITION pos, __in void * pObj)\r
+{\r
+    if (pos==NULL)\r
+        return AddTailI(pObj);\r
+\r
+    /* set pnode to point to a new node, preferably from the cache */\r
+\r
+    CNode *pBefore = (CNode *) pos;\r
+    ASSERT(pBefore != NULL);\r
+    if (pBefore==m_pFirst)\r
+        return AddHeadI(pObj);\r
+\r
+    CNode * pNode = (CNode *) m_Cache.RemoveFromCache();\r
+    if (pNode == NULL) {\r
+        pNode = new CNode;\r
+    }\r
+\r
+    /* Check we have a valid object */\r
+\r
+    if (pNode == NULL) {\r
+        return NULL;\r
+    }\r
+\r
+    /* Initialise all the CNode object\r
+       just in case it came from the cache\r
+    */\r
+\r
+    pNode->SetData(pObj);\r
+\r
+    /* It is to be added to the middle of the list - there is a before\r
+       and after node.  Chain it after pAfter, before pBefore.\r
+    */\r
+\r
+    CNode * pAfter = pBefore->Prev();\r
+    ASSERT(pAfter != NULL);\r
+\r
+    /* chain it in (set four pointers) */\r
+    pNode->SetPrev(pAfter);\r
+    pNode->SetNext(pBefore);\r
+    pBefore->SetPrev(pNode);\r
+    pAfter->SetNext(pNode);\r
+\r
+    ++m_Count;\r
+\r
+    return (POSITION) pNode;\r
+\r
+} // Addbefore(object)\r
+\r
+\r
+\r
+BOOL CBaseList::AddBefore(__in_opt POSITION p, __in CBaseList *pList)\r
+{\r
+    POSITION pos;\r
+    INTERNALREVERSETRAVERSELIST(*pList, pos) {\r
+        /* p follows along the elements being added */\r
+        p = AddBeforeI(p, pList->GetValidI(pos));\r
+        if (p==NULL) return FALSE;\r
+    }\r
+    return TRUE;\r
+} // AddBefore(list)\r
+\r
+\r
+\r
+/* Split *this after position p in *this\r
+   Retain as *this the tail portion of the original *this\r
+   Add the head portion to the tail end of *pList\r
+   Return TRUE if it all worked, FALSE if it didn't.\r
+\r
+   e.g.\r
+      foo->MoveToTail(foo->GetHeadPosition(), bar);\r
+          moves one element from the head of foo to the tail of bar\r
+      foo->MoveToTail(NULL, bar);\r
+          is a no-op\r
+      foo->MoveToTail(foo->GetTailPosition, bar);\r
+          concatenates foo onto the end of bar and empties foo.\r
+\r
+   A better, except excessively long name might be\r
+       MoveElementsFromHeadThroughPositionToOtherTail\r
+*/\r
+BOOL CBaseList::MoveToTail\r
+        (__in_opt POSITION pos, __in CBaseList *pList)\r
+{\r
+    /* Algorithm:\r
+       Note that the elements (including their order) in the concatenation\r
+       of *pList to the head of *this is invariant.\r
+       1. Count elements to be moved\r
+       2. Join *pList onto the head of this to make one long chain\r
+       3. Set first/Last pointers in *this and *pList\r
+       4. Break the chain at the new place\r
+       5. Adjust counts\r
+       6. Set/Reset any events\r
+    */\r
+\r
+    if (pos==NULL) return TRUE;  // no-op.  Eliminates special cases later.\r
+\r
+\r
+    /* Make cMove the number of nodes to move */\r
+    CNode * p = (CNode *)pos;\r
+    int cMove = 0;            // number of nodes to move\r
+    while(p!=NULL) {\r
+       p = p->Prev();\r
+       ++cMove;\r
+    }\r
+\r
+\r
+    /* Join the two chains together */\r
+    if (pList->m_pLast!=NULL)\r
+        pList->m_pLast->SetNext(m_pFirst);\r
+    if (m_pFirst!=NULL)\r
+        m_pFirst->SetPrev(pList->m_pLast);\r
+\r
+\r
+    /* set first and last pointers */\r
+    p = (CNode *)pos;\r
+\r
+    if (pList->m_pFirst==NULL)\r
+        pList->m_pFirst = m_pFirst;\r
+    m_pFirst = p->Next();\r
+    if (m_pFirst==NULL)\r
+        m_pLast = NULL;\r
+    pList->m_pLast = p;\r
+\r
+\r
+    /* Break the chain after p to create the new pieces */\r
+    if (m_pFirst!=NULL)\r
+        m_pFirst->SetPrev(NULL);\r
+    p->SetNext(NULL);\r
+\r
+\r
+    /* Adjust the counts */\r
+    m_Count -= cMove;\r
+    pList->m_Count += cMove;\r
+\r
+    return TRUE;\r
+\r
+} // MoveToTail\r
+\r
+\r
+\r
+/* Mirror image of MoveToTail:\r
+   Split *this before position p in *this.\r
+   Retain in *this the head portion of the original *this\r
+   Add the tail portion to the start (i.e. head) of *pList\r
+   Return TRUE if it all worked, FALSE if it didn't.\r
+\r
+   e.g.\r
+      foo->MoveToHead(foo->GetTailPosition(), bar);\r
+          moves one element from the tail of foo to the head of bar\r
+      foo->MoveToHead(NULL, bar);\r
+          is a no-op\r
+      foo->MoveToHead(foo->GetHeadPosition, bar);\r
+          concatenates foo onto the start of bar and empties foo.\r
+*/\r
+BOOL CBaseList::MoveToHead\r
+        (__in_opt POSITION pos, __in CBaseList *pList)\r
+{\r
+\r
+    /* See the comments on the algorithm in MoveToTail */\r
+\r
+    if (pos==NULL) return TRUE;  // no-op.  Eliminates special cases later.\r
+\r
+    /* Make cMove the number of nodes to move */\r
+    CNode * p = (CNode *)pos;\r
+    int cMove = 0;            // number of nodes to move\r
+    while(p!=NULL) {\r
+       p = p->Next();\r
+       ++cMove;\r
+    }\r
+\r
+\r
+    /* Join the two chains together */\r
+    if (pList->m_pFirst!=NULL)\r
+        pList->m_pFirst->SetPrev(m_pLast);\r
+    if (m_pLast!=NULL)\r
+        m_pLast->SetNext(pList->m_pFirst);\r
+\r
+\r
+    /* set first and last pointers */\r
+    p = (CNode *)pos;\r
+\r
+\r
+    if (pList->m_pLast==NULL)\r
+        pList->m_pLast = m_pLast;\r
+\r
+    m_pLast = p->Prev();\r
+    if (m_pLast==NULL)\r
+        m_pFirst = NULL;\r
+    pList->m_pFirst = p;\r
+\r
+\r
+    /* Break the chain after p to create the new pieces */\r
+    if (m_pLast!=NULL)\r
+        m_pLast->SetNext(NULL);\r
+    p->SetPrev(NULL);\r
+\r
+\r
+    /* Adjust the counts */\r
+    m_Count -= cMove;\r
+    pList->m_Count += cMove;\r
+\r
+    return TRUE;\r
+\r
+} // MoveToHead\r
+\r
+\r
+\r
+/* Reverse the order of the [pointers to] objects in *this\r
+*/\r
+void CBaseList::Reverse()\r
+{\r
+    /* algorithm:\r
+       The obvious booby trap is that you flip pointers around and lose\r
+       addressability to the node that you are going to process next.\r
+       The easy way to avoid this is do do one chain at a time.\r
+\r
+       Run along the forward chain,\r
+       For each node, set the reverse pointer to the one ahead of us.\r
+       The reverse chain is now a copy of the old forward chain, including\r
+       the NULL termination.\r
+\r
+       Run along the reverse chain (i.e. old forward chain again)\r
+       For each node set the forward pointer of the node ahead to point back\r
+       to the one we're standing on.\r
+       The first node needs special treatment,\r
+       it's new forward pointer is NULL.\r
+       Finally set the First/Last pointers\r
+\r
+    */\r
+    CNode * p;\r
+\r
+    // Yes we COULD use a traverse, but it would look funny!\r
+    p = m_pFirst;\r
+    while (p!=NULL) {\r
+        CNode * q;\r
+        q = p->Next();\r
+        p->SetNext(p->Prev());\r
+        p->SetPrev(q);\r
+        p = q;\r
+    }\r
+\r
+    p = m_pFirst;\r
+    m_pFirst = m_pLast;\r
+    m_pLast = p;\r
+\r
+\r
+#if 0     // old version\r
+\r
+    if (m_pFirst==NULL) return;          // empty list\r
+    if (m_pFirst->Next()==NULL) return;  // single node list\r
+\r
+\r
+    /* run along forward chain */\r
+    for ( p = m_pFirst\r
+        ; p!=NULL\r
+        ; p = p->Next()\r
+        ){\r
+        p->SetPrev(p->Next());\r
+    }\r
+\r
+\r
+    /* special case first element */\r
+    m_pFirst->SetNext(NULL);     // fix the old first element\r
+\r
+\r
+    /* run along new reverse chain i.e. old forward chain again */\r
+    for ( p = m_pFirst           // start at the old first element\r
+        ; p->Prev()!=NULL        // while there's a node still to be set\r
+        ; p = p->Prev()          // work in the same direction as before\r
+        ){\r
+        p->Prev()->SetNext(p);\r
+    }\r
+\r
+\r
+    /* fix forward and reverse pointers\r
+       - the triple XOR swap would work but all the casts look hideous */\r
+    p = m_pFirst;\r
+    m_pFirst = m_pLast;\r
+    m_pLast = p;\r
+#endif\r
+\r
+} // Reverse\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/wxlist.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/wxlist.h
new file mode 100644 (file)
index 0000000..47e7123
--- /dev/null
@@ -0,0 +1,553 @@
+//------------------------------------------------------------------------------\r
+// File: WXList.h\r
+//\r
+// Desc: DirectShow base classes - defines a non-MFC generic template list\r
+//       class.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+/* A generic list of pointers to objects.\r
+   No storage management or copying is done on the objects pointed to.\r
+   Objectives: avoid using MFC libraries in ndm kernel mode and\r
+   provide a really useful list type.\r
+\r
+   The class is thread safe in that separate threads may add and\r
+   delete items in the list concurrently although the application\r
+   must ensure that constructor and destructor access is suitably\r
+   synchronised. An application can cause deadlock with operations\r
+   which use two lists by simultaneously calling\r
+   list1->Operation(list2) and list2->Operation(list1).  So don't!\r
+\r
+   The names must not conflict with MFC classes as an application\r
+   may use both.\r
+   */\r
+\r
+#ifndef __WXLIST__\r
+#define __WXLIST__\r
+\r
+   /* A POSITION represents (in some fashion that's opaque) a cursor\r
+      on the list that can be set to identify any element.  NULL is\r
+      a valid value and several operations regard NULL as the position\r
+      "one step off the end of the list".  (In an n element list there\r
+      are n+1 places to insert and NULL is that "n+1-th" value).\r
+      The POSITION of an element in the list is only invalidated if\r
+      that element is deleted.  Move operations may mean that what\r
+      was a valid POSITION in one list is now a valid POSITION in\r
+      a different list.\r
+\r
+      Some operations which at first sight are illegal are allowed as\r
+      harmless no-ops.  For instance RemoveHead is legal on an empty\r
+      list and it returns NULL.  This allows an atomic way to test if\r
+      there is an element there, and if so, get it.  The two operations\r
+      AddTail and RemoveHead thus implement a MONITOR (See Hoare's paper).\r
+\r
+      Single element operations return POSITIONs, non-NULL means it worked.\r
+      whole list operations return a BOOL.  TRUE means it all worked.\r
+\r
+      This definition is the same as the POSITION type for MFCs, so we must\r
+      avoid defining it twice.\r
+   */\r
+#ifndef __AFX_H__\r
+struct __POSITION { int unused; };\r
+typedef __POSITION* POSITION;\r
+#endif\r
+\r
+const int DEFAULTCACHE = 10;    /* Default node object cache size */\r
+\r
+/* A class representing one node in a list.\r
+   Each node knows a pointer to it's adjacent nodes and also a pointer\r
+   to the object that it looks after.\r
+   All of these pointers can be retrieved or set through member functions.\r
+*/\r
+class CBaseList \r
+#ifdef DEBUG\r
+    : public CBaseObject\r
+#endif\r
+{\r
+    /* Making these classes inherit from CBaseObject does nothing\r
+       functionally but it allows us to check there are no memory\r
+       leaks in debug builds. \r
+    */\r
+\r
+public:\r
+\r
+#ifdef DEBUG\r
+    class CNode : public CBaseObject {\r
+#else\r
+    class CNode {\r
+#endif\r
+\r
+        CNode *m_pPrev;         /* Previous node in the list */\r
+        CNode *m_pNext;         /* Next node in the list */\r
+        void *m_pObject;      /* Pointer to the object */\r
+\r
+    public:\r
+\r
+        /* Constructor - initialise the object's pointers */\r
+        CNode()\r
+#ifdef DEBUG\r
+            : CBaseObject(NAME("List node"))\r
+#endif\r
+        {\r
+        };\r
+\r
+\r
+        /* Return the previous node before this one */\r
+        __out CNode *Prev() const { return m_pPrev; };\r
+\r
+\r
+        /* Return the next node after this one */\r
+        __out CNode *Next() const { return m_pNext; };\r
+\r
+\r
+        /* Set the previous node before this one */\r
+        void SetPrev(__in_opt CNode *p) { m_pPrev = p; };\r
+\r
+\r
+        /* Set the next node after this one */\r
+        void SetNext(__in_opt CNode *p) { m_pNext = p; };\r
+\r
+\r
+        /* Get the pointer to the object for this node */\r
+        __out void *GetData() const { return m_pObject; };\r
+\r
+\r
+        /* Set the pointer to the object for this node */\r
+        void SetData(__in void *p) { m_pObject = p; };\r
+    };\r
+\r
+    class CNodeCache\r
+    {\r
+    public:\r
+        CNodeCache(INT iCacheSize) : m_iCacheSize(iCacheSize),\r
+                                     m_pHead(NULL),\r
+                                     m_iUsed(0)\r
+                                     {};\r
+        ~CNodeCache() {\r
+            CNode *pNode = m_pHead;\r
+            while (pNode) {\r
+                CNode *pCurrent = pNode;\r
+                pNode = pNode->Next();\r
+                delete pCurrent;\r
+            }\r
+        };\r
+        void AddToCache(__inout CNode *pNode)\r
+        {\r
+            if (m_iUsed < m_iCacheSize) {\r
+                pNode->SetNext(m_pHead);\r
+                m_pHead = pNode;\r
+                m_iUsed++;\r
+            } else {\r
+                delete pNode;\r
+            }\r
+        };\r
+        CNode *RemoveFromCache()\r
+        {\r
+            CNode *pNode = m_pHead;\r
+            if (pNode != NULL) {\r
+                m_pHead = pNode->Next();\r
+                m_iUsed--;\r
+                ASSERT(m_iUsed >= 0);\r
+            } else {\r
+                ASSERT(m_iUsed == 0);\r
+            }\r
+            return pNode;\r
+        };\r
+    private:\r
+        INT m_iCacheSize;\r
+        INT m_iUsed;\r
+        CNode *m_pHead;\r
+    };\r
+\r
+protected:\r
+\r
+    CNode* m_pFirst;    /* Pointer to first node in the list */\r
+    CNode* m_pLast;     /* Pointer to the last node in the list */\r
+    LONG m_Count;       /* Number of nodes currently in the list */\r
+\r
+private:\r
+\r
+    CNodeCache m_Cache; /* Cache of unused node pointers */\r
+\r
+private:\r
+\r
+    /* These override the default copy constructor and assignment\r
+       operator for all list classes. They are in the private class\r
+       declaration section so that anybody trying to pass a list\r
+       object by value will generate a compile time error of\r
+       "cannot access the private member function". If these were\r
+       not here then the compiler will create default constructors\r
+       and assignment operators which when executed first take a\r
+       copy of all member variables and then during destruction\r
+       delete them all. This must not be done for any heap\r
+       allocated data.\r
+    */\r
+    CBaseList(const CBaseList &refList);\r
+    CBaseList &operator=(const CBaseList &refList);\r
+\r
+public:\r
+\r
+    CBaseList(__in_opt LPCTSTR pName,\r
+              INT iItems);\r
+\r
+    CBaseList(__in_opt LPCTSTR pName);\r
+#ifdef UNICODE\r
+    CBaseList(__in_opt LPCSTR pName,\r
+              INT iItems);\r
+\r
+    CBaseList(__in_opt LPCSTR pName);\r
+#endif\r
+    ~CBaseList();\r
+\r
+    /* Remove all the nodes from *this i.e. make the list empty */\r
+    void RemoveAll();\r
+\r
+\r
+    /* Return a cursor which identifies the first element of *this */\r
+    __out_opt POSITION GetHeadPositionI() const;\r
+\r
+\r
+    /* Return a cursor which identifies the last element of *this */\r
+    __out_opt POSITION GetTailPositionI() const;\r
+\r
+\r
+    /* Return the number of objects in *this */\r
+    int GetCountI() const;\r
+\r
+protected:\r
+    /* Return the pointer to the object at rp,\r
+       Update rp to the next node in *this\r
+       but make it NULL if it was at the end of *this.\r
+       This is a wart retained for backwards compatibility.\r
+       GetPrev is not implemented.\r
+       Use Next, Prev and Get separately.\r
+    */\r
+    __out void *GetNextI(__inout POSITION& rp) const;\r
+\r
+\r
+    /* Return a pointer to the object at p\r
+       Asking for the object at NULL will return NULL harmlessly.\r
+    */\r
+    __out_opt void *GetI(__in_opt POSITION p) const;\r
+    __out void *GetValidI(__in POSITION p) const;\r
+\r
+public:\r
+    /* return the next / prev position in *this\r
+       return NULL when going past the end/start.\r
+       Next(NULL) is same as GetHeadPosition()\r
+       Prev(NULL) is same as GetTailPosition()\r
+       An n element list therefore behaves like a n+1 element\r
+       cycle with NULL at the start/end.\r
+\r
+       !!WARNING!! - This handling of NULL is DIFFERENT from GetNext.\r
+\r
+       Some reasons are:\r
+       1. For a list of n items there are n+1 positions to insert\r
+          These are conveniently encoded as the n POSITIONs and NULL.\r
+       2. If you are keeping a list sorted (fairly common) and you\r
+          search forward for an element to insert before and don't\r
+          find it you finish up with NULL as the element before which\r
+          to insert.  You then want that NULL to be a valid POSITION\r
+          so that you can insert before it and you want that insertion\r
+          point to mean the (n+1)-th one that doesn't have a POSITION.\r
+          (symmetrically if you are working backwards through the list).\r
+       3. It simplifies the algebra which the methods generate.\r
+          e.g. AddBefore(p,x) is identical to AddAfter(Prev(p),x)\r
+          in ALL cases.  All the other arguments probably are reflections\r
+          of the algebraic point.\r
+    */\r
+    __out_opt POSITION Next(__in_opt POSITION pos) const\r
+    {\r
+        if (pos == NULL) {\r
+            return (POSITION) m_pFirst;\r
+        }\r
+        CNode *pn = (CNode *) pos;\r
+        return (POSITION) pn->Next();\r
+    } //Next\r
+\r
+    // See Next\r
+    __out_opt POSITION Prev(__in_opt POSITION pos) const\r
+    {\r
+        if (pos == NULL) {\r
+            return (POSITION) m_pLast;\r
+        }\r
+        CNode *pn = (CNode *) pos;\r
+        return (POSITION) pn->Prev();\r
+    } //Prev\r
+\r
+\r
+    /* Return the first position in *this which holds the given\r
+       pointer.  Return NULL if the pointer was not not found.\r
+    */\r
+protected:\r
+    __out_opt POSITION FindI( __in void * pObj) const;\r
+\r
+    // ??? Should there be (or even should there be only)\r
+    // ??? POSITION FindNextAfter(void * pObj, POSITION p)\r
+    // ??? And of course FindPrevBefore too.\r
+    // ??? List.Find(&Obj) then becomes List.FindNextAfter(&Obj, NULL)\r
+\r
+\r
+    /* Remove the first node in *this (deletes the pointer to its\r
+       object from the list, does not free the object itself).\r
+       Return the pointer to its object.\r
+       If *this was already empty it will harmlessly return NULL.\r
+    */\r
+    __out_opt void *RemoveHeadI();\r
+\r
+\r
+    /* Remove the last node in *this (deletes the pointer to its\r
+       object from the list, does not free the object itself).\r
+       Return the pointer to its object.\r
+       If *this was already empty it will harmlessly return NULL.\r
+    */\r
+    __out_opt void *RemoveTailI();\r
+\r
+\r
+    /* Remove the node identified by p from the list (deletes the pointer\r
+       to its object from the list, does not free the object itself).\r
+       Asking to Remove the object at NULL will harmlessly return NULL.\r
+       Return the pointer to the object removed.\r
+    */\r
+    __out_opt void *RemoveI(__in_opt POSITION p);\r
+\r
+    /* Add single object *pObj to become a new last element of the list.\r
+       Return the new tail position, NULL if it fails.\r
+       If you are adding a COM objects, you might want AddRef it first.\r
+       Other existing POSITIONs in *this are still valid\r
+    */\r
+    __out_opt POSITION AddTailI(__in void * pObj);\r
+public:\r
+\r
+\r
+    /* Add all the elements in *pList to the tail of *this.\r
+       This duplicates all the nodes in *pList (i.e. duplicates\r
+       all its pointers to objects).  It does not duplicate the objects.\r
+       If you are adding a list of pointers to a COM object into the list\r
+       it's a good idea to AddRef them all  it when you AddTail it.\r
+       Return TRUE if it all worked, FALSE if it didn't.\r
+       If it fails some elements may have been added.\r
+       Existing POSITIONs in *this are still valid\r
+\r
+       If you actually want to MOVE the elements, use MoveToTail instead.\r
+    */\r
+    BOOL AddTail(__in CBaseList *pList);\r
+\r
+\r
+    /* Mirror images of AddHead: */\r
+\r
+    /* Add single object to become a new first element of the list.\r
+       Return the new head position, NULL if it fails.\r
+       Existing POSITIONs in *this are still valid\r
+    */\r
+protected:\r
+    __out_opt POSITION AddHeadI(__in void * pObj);\r
+public:\r
+\r
+    /* Add all the elements in *pList to the head of *this.\r
+       Same warnings apply as for AddTail.\r
+       Return TRUE if it all worked, FALSE if it didn't.\r
+       If it fails some of the objects may have been added.\r
+\r
+       If you actually want to MOVE the elements, use MoveToHead instead.\r
+    */\r
+    BOOL AddHead(__in CBaseList *pList);\r
+\r
+\r
+    /* Add the object *pObj to *this after position p in *this.\r
+       AddAfter(NULL,x) adds x to the start - equivalent to AddHead\r
+       Return the position of the object added, NULL if it failed.\r
+       Existing POSITIONs in *this are undisturbed, including p.\r
+    */\r
+protected:\r
+    __out_opt POSITION AddAfterI(__in_opt POSITION p, __in void * pObj);\r
+public:\r
+\r
+    /* Add the list *pList to *this after position p in *this\r
+       AddAfter(NULL,x) adds x to the start - equivalent to AddHead\r
+       Return TRUE if it all worked, FALSE if it didn't.\r
+       If it fails, some of the objects may be added\r
+       Existing POSITIONs in *this are undisturbed, including p.\r
+    */\r
+    BOOL AddAfter(__in_opt POSITION p, __in CBaseList *pList);\r
+\r
+\r
+    /* Mirror images:\r
+       Add the object *pObj to this-List after position p in *this.\r
+       AddBefore(NULL,x) adds x to the end - equivalent to AddTail\r
+       Return the position of the new object, NULL if it fails\r
+       Existing POSITIONs in *this are undisturbed, including p.\r
+    */\r
+    protected:\r
+    __out_opt POSITION AddBeforeI(__in_opt POSITION p, __in void * pObj);\r
+    public:\r
+\r
+    /* Add the list *pList to *this before position p in *this\r
+       AddAfter(NULL,x) adds x to the start - equivalent to AddHead\r
+       Return TRUE if it all worked, FALSE if it didn't.\r
+       If it fails, some of the objects may be added\r
+       Existing POSITIONs in *this are undisturbed, including p.\r
+    */\r
+    BOOL AddBefore(__in_opt POSITION p, __in CBaseList *pList);\r
+\r
+\r
+    /* Note that AddAfter(p,x) is equivalent to AddBefore(Next(p),x)\r
+       even in cases where p is NULL or Next(p) is NULL.\r
+       Similarly for mirror images etc.\r
+       This may make it easier to argue about programs.\r
+    */\r
+\r
+\r
+\r
+    /* The following operations do not copy any elements.\r
+       They move existing blocks of elements around by switching pointers.\r
+       They are fairly efficient for long lists as for short lists.\r
+       (Alas, the Count slows things down).\r
+\r
+       They split the list into two parts.\r
+       One part remains as the original list, the other part\r
+       is appended to the second list.  There are eight possible\r
+       variations:\r
+       Split the list {after/before} a given element\r
+       keep the {head/tail} portion in the original list\r
+       append the rest to the {head/tail} of the new list.\r
+\r
+       Since After is strictly equivalent to Before Next\r
+       we are not in serious need of the Before/After variants.\r
+       That leaves only four.\r
+\r
+       If you are processing a list left to right and dumping\r
+       the bits that you have processed into another list as\r
+       you go, the Tail/Tail variant gives the most natural result.\r
+       If you are processing in reverse order, Head/Head is best.\r
+\r
+       By using NULL positions and empty lists judiciously either\r
+       of the other two can be built up in two operations.\r
+\r
+       The definition of NULL (see Next/Prev etc) means that\r
+       degenerate cases include\r
+          "move all elements to new list"\r
+          "Split a list into two lists"\r
+          "Concatenate two lists"\r
+          (and quite a few no-ops)\r
+\r
+       !!WARNING!! The type checking won't buy you much if you get list\r
+       positions muddled up - e.g. use a POSITION that's in a different\r
+       list and see what a mess you get!\r
+    */\r
+\r
+    /* Split *this after position p in *this\r
+       Retain as *this the tail portion of the original *this\r
+       Add the head portion to the tail end of *pList\r
+       Return TRUE if it all worked, FALSE if it didn't.\r
+\r
+       e.g.\r
+          foo->MoveToTail(foo->GetHeadPosition(), bar);\r
+              moves one element from the head of foo to the tail of bar\r
+          foo->MoveToTail(NULL, bar);\r
+              is a no-op, returns NULL\r
+          foo->MoveToTail(foo->GetTailPosition, bar);\r
+              concatenates foo onto the end of bar and empties foo.\r
+\r
+       A better, except excessively long name might be\r
+           MoveElementsFromHeadThroughPositionToOtherTail\r
+    */\r
+    BOOL MoveToTail(__in_opt POSITION pos, __in CBaseList *pList);\r
+\r
+\r
+    /* Mirror image:\r
+       Split *this before position p in *this.\r
+       Retain in *this the head portion of the original *this\r
+       Add the tail portion to the start (i.e. head) of *pList\r
+\r
+       e.g.\r
+          foo->MoveToHead(foo->GetTailPosition(), bar);\r
+              moves one element from the tail of foo to the head of bar\r
+          foo->MoveToHead(NULL, bar);\r
+              is a no-op, returns NULL\r
+          foo->MoveToHead(foo->GetHeadPosition, bar);\r
+              concatenates foo onto the start of bar and empties foo.\r
+    */\r
+    BOOL MoveToHead(__in_opt POSITION pos, __in CBaseList *pList);\r
+\r
+\r
+    /* Reverse the order of the [pointers to] objects in *this\r
+    */\r
+    void Reverse();\r
+\r
+\r
+    /* set cursor to the position of each element of list in turn  */\r
+    #define TRAVERSELIST(list, cursor)               \\r
+    for ( cursor = (list).GetHeadPosition()           \\r
+        ; cursor!=NULL                               \\r
+        ; cursor = (list).Next(cursor)                \\r
+        )\r
+\r
+\r
+    /* set cursor to the position of each element of list in turn\r
+       in reverse order\r
+    */\r
+    #define REVERSETRAVERSELIST(list, cursor)        \\r
+    for ( cursor = (list).GetTailPosition()           \\r
+        ; cursor!=NULL                               \\r
+        ; cursor = (list).Prev(cursor)                \\r
+        )\r
+\r
+}; // end of class declaration\r
+\r
+template<class OBJECT> class CGenericList : public CBaseList\r
+{\r
+public:\r
+    CGenericList(__in_opt LPCTSTR pName,\r
+                 INT iItems,\r
+                 BOOL bLock = TRUE,\r
+                 BOOL bAlert = FALSE) :\r
+                     CBaseList(pName, iItems) {\r
+        UNREFERENCED_PARAMETER(bAlert);\r
+        UNREFERENCED_PARAMETER(bLock);\r
+    };\r
+    CGenericList(__in_opt LPCTSTR pName) :\r
+                     CBaseList(pName) {\r
+    };\r
+\r
+    __out_opt POSITION GetHeadPosition() const { return (POSITION)m_pFirst; }\r
+    __out_opt POSITION GetTailPosition() const { return (POSITION)m_pLast; }\r
+    int GetCount() const { return m_Count; }\r
+\r
+    __out OBJECT *GetNext(__inout POSITION& rp) const { return (OBJECT *) GetNextI(rp); }\r
+\r
+    __out_opt OBJECT *Get(__in_opt POSITION p) const { return (OBJECT *) GetI(p); }\r
+    __out OBJECT *GetValid(__in POSITION p) const { return (OBJECT *) GetValidI(p); }\r
+    __out_opt OBJECT *GetHead() const  { return Get(GetHeadPosition()); }\r
+\r
+    __out_opt OBJECT *RemoveHead() { return (OBJECT *) RemoveHeadI(); }\r
+\r
+    __out_opt OBJECT *RemoveTail() { return (OBJECT *) RemoveTailI(); }\r
+\r
+    __out_opt OBJECT *Remove(__in_opt POSITION p) { return (OBJECT *) RemoveI(p); }\r
+    __out_opt POSITION AddBefore(__in_opt POSITION p, __in OBJECT * pObj) { return AddBeforeI(p, pObj); }\r
+    __out_opt POSITION AddAfter(__in_opt POSITION p, __in OBJECT * pObj)  { return AddAfterI(p, pObj); }\r
+    __out_opt POSITION AddHead(__in OBJECT * pObj) { return AddHeadI(pObj); }\r
+    __out_opt POSITION AddTail(__in OBJECT * pObj)  { return AddTailI(pObj); }\r
+    BOOL AddTail(__in CGenericList<OBJECT> *pList)\r
+            { return CBaseList::AddTail((CBaseList *) pList); }\r
+    BOOL AddHead(__in CGenericList<OBJECT> *pList)\r
+            { return CBaseList::AddHead((CBaseList *) pList); }\r
+    BOOL AddAfter(__in_opt POSITION p, __in CGenericList<OBJECT> *pList)\r
+            { return CBaseList::AddAfter(p, (CBaseList *) pList); };\r
+    BOOL AddBefore(__in_opt POSITION p, __in CGenericList<OBJECT> *pList)\r
+            { return CBaseList::AddBefore(p, (CBaseList *) pList); };\r
+    __out_opt POSITION Find( __in OBJECT * pObj) const { return FindI(pObj); }\r
+}; // end of class declaration\r
+\r
+\r
+\r
+/* These define the standard list types */\r
+\r
+typedef CGenericList<CBaseObject> CBaseObjectList;\r
+typedef CGenericList<IUnknown> CBaseInterfaceList;\r
+\r
+#endif /* __WXLIST__ */\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/wxutil.cpp b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/wxutil.cpp
new file mode 100644 (file)
index 0000000..5bc97a9
--- /dev/null
@@ -0,0 +1,769 @@
+//------------------------------------------------------------------------------\r
+// File: WXUtil.cpp\r
+//\r
+// Desc: DirectShow base classes - implements helper classes for building\r
+//       multimedia filters.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#include <streams.h>\r
+#define STRSAFE_NO_DEPRECATE\r
+#include <strsafe.h>\r
+\r
+\r
+// --- CAMEvent -----------------------\r
+CAMEvent::CAMEvent(BOOL fManualReset, __inout_opt HRESULT *phr)\r
+{\r
+    m_hEvent = CreateEvent(NULL, fManualReset, FALSE, NULL);\r
+    if (NULL == m_hEvent) {\r
+        if (NULL != phr && SUCCEEDED(*phr)) {\r
+            *phr = E_OUTOFMEMORY;\r
+        }\r
+    }\r
+}\r
+\r
+CAMEvent::CAMEvent(__inout_opt HRESULT *phr)\r
+{\r
+    m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);\r
+    if (NULL == m_hEvent) {\r
+        if (NULL != phr && SUCCEEDED(*phr)) {\r
+            *phr = E_OUTOFMEMORY;\r
+        }\r
+    }\r
+}\r
+\r
+CAMEvent::~CAMEvent()\r
+{\r
+    if (m_hEvent) {\r
+       EXECUTE_ASSERT(CloseHandle(m_hEvent));\r
+    }\r
+}\r
+\r
+\r
+// --- CAMMsgEvent -----------------------\r
+// One routine.  The rest is handled in CAMEvent\r
+\r
+CAMMsgEvent::CAMMsgEvent(__inout_opt HRESULT *phr) : CAMEvent(FALSE, phr)\r
+{\r
+}\r
+\r
+BOOL CAMMsgEvent::WaitMsg(DWORD dwTimeout)\r
+{\r
+    // wait for the event to be signalled, or for the\r
+    // timeout (in MS) to expire.  allow SENT messages\r
+    // to be processed while we wait\r
+    DWORD dwWait;\r
+    DWORD dwStartTime;\r
+\r
+    // set the waiting period.\r
+    DWORD dwWaitTime = dwTimeout;\r
+\r
+    // the timeout will eventually run down as we iterate\r
+    // processing messages.  grab the start time so that\r
+    // we can calculate elapsed times.\r
+    if (dwWaitTime != INFINITE) {\r
+        dwStartTime = timeGetTime();\r
+    }\r
+\r
+    do {\r
+        dwWait = MsgWaitForMultipleObjects(1,&m_hEvent,FALSE, dwWaitTime, QS_SENDMESSAGE);\r
+        if (dwWait == WAIT_OBJECT_0 + 1) {\r
+           MSG Message;\r
+            PeekMessage(&Message,NULL,0,0,PM_NOREMOVE);\r
+\r
+           // If we have an explicit length of time to wait calculate\r
+           // the next wake up point - which might be now.\r
+           // If dwTimeout is INFINITE, it stays INFINITE\r
+           if (dwWaitTime != INFINITE) {\r
+\r
+               DWORD dwElapsed = timeGetTime()-dwStartTime;\r
+\r
+               dwWaitTime =\r
+                   (dwElapsed >= dwTimeout)\r
+                       ? 0  // wake up with WAIT_TIMEOUT\r
+                       : dwTimeout-dwElapsed;\r
+           }\r
+        }\r
+    } while (dwWait == WAIT_OBJECT_0 + 1);\r
+\r
+    // return TRUE if we woke on the event handle,\r
+    //        FALSE if we timed out.\r
+    return (dwWait == WAIT_OBJECT_0);\r
+}\r
+\r
+// --- CAMThread ----------------------\r
+\r
+\r
+CAMThread::CAMThread(__inout_opt HRESULT *phr)\r
+    : m_EventSend(TRUE, phr),     // must be manual-reset for CheckRequest()\r
+      m_EventComplete(FALSE, phr)\r
+{\r
+    m_hThread = NULL;\r
+}\r
+\r
+CAMThread::~CAMThread() {\r
+    Close();\r
+}\r
+\r
+\r
+// when the thread starts, it calls this function. We unwrap the 'this'\r
+//pointer and call ThreadProc.\r
+DWORD WINAPI\r
+CAMThread::InitialThreadProc(__inout LPVOID pv)\r
+{\r
+    HRESULT hrCoInit = CAMThread::CoInitializeHelper();\r
+    if(FAILED(hrCoInit)) {\r
+        DbgLog((LOG_ERROR, 1, TEXT("CoInitializeEx failed.")));\r
+    }\r
+\r
+    CAMThread * pThread = (CAMThread *) pv;\r
+\r
+    HRESULT hr = pThread->ThreadProc();\r
+\r
+    if(SUCCEEDED(hrCoInit)) {\r
+        CoUninitialize();\r
+    }\r
+\r
+    return hr;\r
+}\r
+\r
+BOOL\r
+CAMThread::Create()\r
+{\r
+    DWORD threadid;\r
+\r
+    CAutoLock lock(&m_AccessLock);\r
+\r
+    if (ThreadExists()) {\r
+       return FALSE;\r
+    }\r
+\r
+    m_hThread = CreateThread(\r
+                   NULL,\r
+                   0,\r
+                   CAMThread::InitialThreadProc,\r
+                   this,\r
+                   0,\r
+                   &threadid);\r
+\r
+    if (!m_hThread) {\r
+       return FALSE;\r
+    }\r
+\r
+    return TRUE;\r
+}\r
+\r
+DWORD\r
+CAMThread::CallWorker(DWORD dwParam)\r
+{\r
+    // lock access to the worker thread for scope of this object\r
+    CAutoLock lock(&m_AccessLock);\r
+\r
+    if (!ThreadExists()) {\r
+       return (DWORD) E_FAIL;\r
+    }\r
+\r
+    // set the parameter\r
+    m_dwParam = dwParam;\r
+\r
+    // signal the worker thread\r
+    m_EventSend.Set();\r
+\r
+    // wait for the completion to be signalled\r
+    m_EventComplete.Wait();\r
+\r
+    // done - this is the thread's return value\r
+    return m_dwReturnVal;\r
+}\r
+\r
+// Wait for a request from the client\r
+DWORD\r
+CAMThread::GetRequest()\r
+{\r
+    m_EventSend.Wait();\r
+    return m_dwParam;\r
+}\r
+\r
+// is there a request?\r
+BOOL\r
+CAMThread::CheckRequest(__out_opt DWORD * pParam)\r
+{\r
+    if (!m_EventSend.Check()) {\r
+       return FALSE;\r
+    } else {\r
+       if (pParam) {\r
+           *pParam = m_dwParam;\r
+       }\r
+       return TRUE;\r
+    }\r
+}\r
+\r
+// reply to the request\r
+void\r
+CAMThread::Reply(DWORD dw)\r
+{\r
+    m_dwReturnVal = dw;\r
+\r
+    // The request is now complete so CheckRequest should fail from\r
+    // now on\r
+    //\r
+    // This event should be reset BEFORE we signal the client or\r
+    // the client may Set it before we reset it and we'll then\r
+    // reset it (!)\r
+\r
+    m_EventSend.Reset();\r
+\r
+    // Tell the client we're finished\r
+\r
+    m_EventComplete.Set();\r
+}\r
+\r
+HRESULT CAMThread::CoInitializeHelper()\r
+{\r
+    // call CoInitializeEx and tell OLE not to create a window (this\r
+    // thread probably won't dispatch messages and will hang on\r
+    // broadcast msgs o/w).\r
+    //\r
+    // If CoInitEx is not available, threads that don't call CoCreate\r
+    // aren't affected. Threads that do will have to handle the\r
+    // failure. Perhaps we should fall back to CoInitialize and risk\r
+    // hanging?\r
+    //\r
+\r
+    // older versions of ole32.dll don't have CoInitializeEx\r
+\r
+    HRESULT hr = E_FAIL;\r
+    HINSTANCE hOle = GetModuleHandle(TEXT("ole32.dll"));\r
+    if(hOle)\r
+    {\r
+        typedef HRESULT (STDAPICALLTYPE *PCoInitializeEx)(\r
+            LPVOID pvReserved, DWORD dwCoInit);\r
+        PCoInitializeEx pCoInitializeEx =\r
+            (PCoInitializeEx)(GetProcAddress(hOle, "CoInitializeEx"));\r
+        if(pCoInitializeEx)\r
+        {\r
+            hr = (*pCoInitializeEx)(0, COINIT_DISABLE_OLE1DDE );\r
+        }\r
+    }\r
+    else\r
+    {\r
+        // caller must load ole32.dll\r
+        DbgBreak("couldn't locate ole32.dll");\r
+    }\r
+\r
+    return hr;\r
+}\r
+\r
+\r
+// destructor for CMsgThread  - cleans up any messages left in the\r
+// queue when the thread exited\r
+CMsgThread::~CMsgThread()\r
+{\r
+    if (m_hThread != NULL) {\r
+        WaitForSingleObject(m_hThread, INFINITE);\r
+        EXECUTE_ASSERT(CloseHandle(m_hThread));\r
+    }\r
+\r
+    POSITION pos = m_ThreadQueue.GetHeadPosition();\r
+    while (pos) {\r
+        CMsg * pMsg = m_ThreadQueue.GetNext(pos);\r
+        delete pMsg;\r
+    }\r
+    m_ThreadQueue.RemoveAll();\r
+\r
+    if (m_hSem != NULL) {\r
+        EXECUTE_ASSERT(CloseHandle(m_hSem));\r
+    }\r
+}\r
+\r
+BOOL\r
+CMsgThread::CreateThread(\r
+    )\r
+{\r
+    m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);\r
+    if (m_hSem == NULL) {\r
+        return FALSE;\r
+    }\r
+\r
+    m_hThread = ::CreateThread(NULL, 0, DefaultThreadProc,\r
+                              (LPVOID)this, 0, &m_ThreadId);\r
+    return m_hThread != NULL;\r
+}\r
+\r
+\r
+// This is the threads message pump.  Here we get and dispatch messages to\r
+// clients thread proc until the client refuses to process a message.\r
+// The client returns a non-zero value to stop the message pump, this\r
+// value becomes the threads exit code.\r
+\r
+DWORD WINAPI\r
+CMsgThread::DefaultThreadProc(\r
+    __inout LPVOID lpParam\r
+    )\r
+{\r
+    CMsgThread *lpThis = (CMsgThread *)lpParam;\r
+    CMsg msg;\r
+    LRESULT lResult;\r
+\r
+    // !!!\r
+    CoInitialize(NULL);\r
+\r
+    // allow a derived class to handle thread startup\r
+    lpThis->OnThreadInit();\r
+\r
+    do {\r
+       lpThis->GetThreadMsg(&msg);\r
+       lResult = lpThis->ThreadMessageProc(msg.uMsg,msg.dwFlags,\r
+                                           msg.lpParam, msg.pEvent);\r
+    } while (lResult == 0L);\r
+\r
+    // !!!\r
+    CoUninitialize();\r
+\r
+    return (DWORD)lResult;\r
+}\r
+\r
+\r
+// Block until the next message is placed on the list m_ThreadQueue.\r
+// copies the message to the message pointed to by *pmsg\r
+void\r
+CMsgThread::GetThreadMsg(__out CMsg *msg)\r
+{\r
+    CMsg * pmsg = NULL;\r
+\r
+    // keep trying until a message appears\r
+    while (TRUE) {\r
+        {\r
+            CAutoLock lck(&m_Lock);\r
+            pmsg = m_ThreadQueue.RemoveHead();\r
+            if (pmsg == NULL) {\r
+                m_lWaiting++;\r
+            } else {\r
+                break;\r
+            }\r
+        }\r
+        // the semaphore will be signalled when it is non-empty\r
+        WaitForSingleObject(m_hSem, INFINITE);\r
+    }\r
+    // copy fields to caller's CMsg\r
+    *msg = *pmsg;\r
+\r
+    // this CMsg was allocated by the 'new' in PutThreadMsg\r
+    delete pmsg;\r
+\r
+}\r
+\r
+// Helper function - convert int to WSTR\r
+void WINAPI IntToWstr(int i, __out_ecount(12) LPWSTR wstr)\r
+{\r
+#ifdef UNICODE\r
+    if (FAILED(StringCchPrintf(wstr, 12, L"%d", i))) {\r
+        wstr[0] = 0;\r
+    }\r
+#else\r
+    TCHAR temp[12];\r
+    if (FAILED(StringCchPrintf(temp, NUMELMS(temp), "%d", i))) {\r
+        wstr[0] = 0;\r
+    } else {\r
+        MultiByteToWideChar(CP_ACP, 0, temp, -1, wstr, 12);\r
+    }\r
+#endif\r
+} // IntToWstr\r
+\r
+\r
+#define MEMORY_ALIGNMENT        4\r
+#define MEMORY_ALIGNMENT_LOG2   2\r
+#define MEMORY_ALIGNMENT_MASK   MEMORY_ALIGNMENT - 1\r
+\r
+void * __stdcall memmoveInternal(void * dst, const void * src, size_t count)\r
+{\r
+    void * ret = dst;\r
+\r
+#ifdef _X86_\r
+    if (dst <= src || (char *)dst >= ((char *)src + count)) {\r
+\r
+        /*\r
+         * Non-Overlapping Buffers\r
+         * copy from lower addresses to higher addresses\r
+         */\r
+        _asm {\r
+            mov     esi,src\r
+            mov     edi,dst\r
+            mov     ecx,count\r
+            cld\r
+            mov     edx,ecx\r
+            and     edx,MEMORY_ALIGNMENT_MASK\r
+            shr     ecx,MEMORY_ALIGNMENT_LOG2\r
+            rep     movsd\r
+            or      ecx,edx\r
+            jz      memmove_done\r
+            rep     movsb\r
+memmove_done:\r
+        }\r
+    }\r
+    else {\r
+\r
+        /*\r
+         * Overlapping Buffers\r
+         * copy from higher addresses to lower addresses\r
+         */\r
+        _asm {\r
+            mov     esi,src\r
+            mov     edi,dst\r
+            mov     ecx,count\r
+            std\r
+            add     esi,ecx\r
+            add     edi,ecx\r
+            dec     esi\r
+            dec     edi\r
+            rep     movsb\r
+            cld\r
+        }\r
+    }\r
+#else\r
+    MoveMemory(dst, src, count);\r
+#endif\r
+\r
+    return ret;\r
+}\r
+\r
+HRESULT AMSafeMemMoveOffset(\r
+    __in_bcount(dst_size) void * dst,\r
+    __in size_t dst_size,\r
+    __in DWORD cb_dst_offset,\r
+    __in_bcount(src_size) const void * src,\r
+    __in size_t src_size,\r
+    __in DWORD cb_src_offset,\r
+    __in size_t count)\r
+{\r
+    // prevent read overruns\r
+    if( count + cb_src_offset < count ||   // prevent integer overflow\r
+        count + cb_src_offset > src_size)  // prevent read overrun\r
+    {\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+    // prevent write overruns\r
+    if( count + cb_dst_offset < count ||   // prevent integer overflow\r
+        count + cb_dst_offset > dst_size)  // prevent write overrun\r
+    {\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+    memmoveInternal( (BYTE *)dst+cb_dst_offset, (BYTE *)src+cb_src_offset, count);\r
+    return S_OK;\r
+}\r
+\r
+\r
+#ifdef DEBUG\r
+/******************************Public*Routine******************************\\r
+* Debug CCritSec helpers\r
+*\r
+* We provide debug versions of the Constructor, destructor, Lock and Unlock\r
+* routines.  The debug code tracks who owns each critical section by\r
+* maintaining a depth count.\r
+*\r
+* History:\r
+*\r
+\**************************************************************************/\r
+\r
+CCritSec::CCritSec()\r
+{\r
+    InitializeCriticalSection(&m_CritSec);\r
+    m_currentOwner = m_lockCount = 0;\r
+    m_fTrace = FALSE;\r
+}\r
+\r
+CCritSec::~CCritSec()\r
+{\r
+    DeleteCriticalSection(&m_CritSec);\r
+}\r
+\r
+void CCritSec::Lock()\r
+{\r
+    UINT tracelevel=3;\r
+    DWORD us = GetCurrentThreadId();\r
+    DWORD currentOwner = m_currentOwner;\r
+    if (currentOwner && (currentOwner != us)) {\r
+        // already owned, but not by us\r
+        if (m_fTrace) {\r
+            DbgLog((LOG_LOCKING, 2, TEXT("Thread %d about to wait for lock %x owned by %d"),\r
+                GetCurrentThreadId(), &m_CritSec, currentOwner));\r
+            tracelevel=2;\r
+               // if we saw the message about waiting for the critical\r
+               // section we ensure we see the message when we get the\r
+               // critical section\r
+        }\r
+    }\r
+    EnterCriticalSection(&m_CritSec);\r
+    if (0 == m_lockCount++) {\r
+        // we now own it for the first time.  Set owner information\r
+        m_currentOwner = us;\r
+\r
+        if (m_fTrace) {\r
+            DbgLog((LOG_LOCKING, tracelevel, TEXT("Thread %d now owns lock %x"), m_currentOwner, &m_CritSec));\r
+        }\r
+    }\r
+}\r
+\r
+void CCritSec::Unlock() {\r
+    if (0 == --m_lockCount) {\r
+        // about to be unowned\r
+        if (m_fTrace) {\r
+            DbgLog((LOG_LOCKING, 3, TEXT("Thread %d releasing lock %x"), m_currentOwner, &m_CritSec));\r
+        }\r
+\r
+        m_currentOwner = 0;\r
+    }\r
+    LeaveCriticalSection(&m_CritSec);\r
+}\r
+\r
+void WINAPI DbgLockTrace(CCritSec * pcCrit, BOOL fTrace)\r
+{\r
+    pcCrit->m_fTrace = fTrace;\r
+}\r
+\r
+BOOL WINAPI CritCheckIn(CCritSec * pcCrit)\r
+{\r
+    return (GetCurrentThreadId() == pcCrit->m_currentOwner);\r
+}\r
+\r
+BOOL WINAPI CritCheckIn(const CCritSec * pcCrit)\r
+{\r
+    return (GetCurrentThreadId() == pcCrit->m_currentOwner);\r
+}\r
+\r
+BOOL WINAPI CritCheckOut(CCritSec * pcCrit)\r
+{\r
+    return (GetCurrentThreadId() != pcCrit->m_currentOwner);\r
+}\r
+\r
+BOOL WINAPI CritCheckOut(const CCritSec * pcCrit)\r
+{\r
+    return (GetCurrentThreadId() != pcCrit->m_currentOwner);\r
+}\r
+#endif\r
+\r
+\r
+STDAPI WriteBSTR(__deref_out BSTR *pstrDest, LPCWSTR szSrc)\r
+{\r
+    *pstrDest = SysAllocString( szSrc );\r
+    if( !(*pstrDest) ) return E_OUTOFMEMORY;\r
+    return NOERROR;\r
+}\r
+\r
+\r
+STDAPI FreeBSTR(__deref_in BSTR* pstr)\r
+{\r
+    if( (PVOID)*pstr == NULL ) return S_FALSE;\r
+    SysFreeString( *pstr );\r
+    return NOERROR;\r
+}\r
+\r
+\r
+// Return a wide string - allocating memory for it\r
+// Returns:\r
+//    S_OK          - no error\r
+//    E_POINTER     - ppszReturn == NULL\r
+//    E_OUTOFMEMORY - can't allocate memory for returned string\r
+STDAPI AMGetWideString(LPCWSTR psz, __deref_out LPWSTR *ppszReturn)\r
+{\r
+    CheckPointer(ppszReturn, E_POINTER);\r
+    ValidateReadWritePtr(ppszReturn, sizeof(LPWSTR));\r
+    *ppszReturn = NULL;\r
+    size_t nameLen;\r
+    HRESULT hr = StringCbLengthW(psz, 100000, &nameLen);\r
+    if (FAILED(hr)) {\r
+        return hr;\r
+    }\r
+    *ppszReturn = (LPWSTR)CoTaskMemAlloc(nameLen + sizeof(WCHAR));\r
+    if (*ppszReturn == NULL) {\r
+       return E_OUTOFMEMORY;\r
+    }\r
+    CopyMemory(*ppszReturn, psz, nameLen + sizeof(WCHAR));\r
+    return NOERROR;\r
+}\r
+\r
+// Waits for the HANDLE hObject.  While waiting messages sent\r
+// to windows on our thread by SendMessage will be processed.\r
+// Using this function to do waits and mutual exclusion\r
+// avoids some deadlocks in objects with windows.\r
+// Return codes are the same as for WaitForSingleObject\r
+DWORD WINAPI WaitDispatchingMessages(\r
+    HANDLE hObject,\r
+    DWORD dwWait,\r
+    HWND hwnd,\r
+    UINT uMsg,\r
+    HANDLE hEvent)\r
+{\r
+    BOOL bPeeked = FALSE;\r
+    DWORD dwResult;\r
+    DWORD dwStart;\r
+    DWORD dwThreadPriority;\r
+\r
+    static UINT uMsgId = 0;\r
+\r
+    HANDLE hObjects[2] = { hObject, hEvent };\r
+    if (dwWait != INFINITE && dwWait != 0) {\r
+        dwStart = GetTickCount();\r
+    }\r
+    for (; ; ) {\r
+        DWORD nCount = NULL != hEvent ? 2 : 1;\r
+\r
+        //  Minimize the chance of actually dispatching any messages\r
+        //  by seeing if we can lock immediately.\r
+        dwResult = WaitForMultipleObjects(nCount, hObjects, FALSE, 0);\r
+        if (dwResult < WAIT_OBJECT_0 + nCount) {\r
+            break;\r
+        }\r
+\r
+        DWORD dwTimeOut = dwWait;\r
+        if (dwTimeOut > 10) {\r
+            dwTimeOut = 10;\r
+        }\r
+        dwResult = MsgWaitForMultipleObjects(\r
+                             nCount,\r
+                             hObjects,\r
+                             FALSE,\r
+                             dwTimeOut,\r
+                             hwnd == NULL ? QS_SENDMESSAGE :\r
+                                            QS_SENDMESSAGE + QS_POSTMESSAGE);\r
+        if (dwResult == WAIT_OBJECT_0 + nCount ||\r
+            dwResult == WAIT_TIMEOUT && dwTimeOut != dwWait) {\r
+            MSG msg;\r
+            if (hwnd != NULL) {\r
+                while (PeekMessage(&msg, hwnd, uMsg, uMsg, PM_REMOVE)) {\r
+                    DispatchMessage(&msg);\r
+                }\r
+            }\r
+            // Do this anyway - the previous peek doesn't flush out the\r
+            // messages\r
+            PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);\r
+\r
+            if (dwWait != INFINITE && dwWait != 0) {\r
+                DWORD dwNow = GetTickCount();\r
+\r
+                // Working with differences handles wrap-around\r
+                DWORD dwDiff = dwNow - dwStart;\r
+                if (dwDiff > dwWait) {\r
+                    dwWait = 0;\r
+                } else {\r
+                    dwWait -= dwDiff;\r
+                }\r
+                dwStart = dwNow;\r
+            }\r
+            if (!bPeeked) {\r
+                //  Raise our priority to prevent our message queue\r
+                //  building up\r
+                dwThreadPriority = GetThreadPriority(GetCurrentThread());\r
+                if (dwThreadPriority < THREAD_PRIORITY_HIGHEST) {\r
+                    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);\r
+                }\r
+                bPeeked = TRUE;\r
+            }\r
+        } else {\r
+            break;\r
+        }\r
+    }\r
+    if (bPeeked) {\r
+        SetThreadPriority(GetCurrentThread(), dwThreadPriority);\r
+        if (HIWORD(GetQueueStatus(QS_POSTMESSAGE)) & QS_POSTMESSAGE) {\r
+            if (uMsgId == 0) {\r
+                uMsgId = RegisterWindowMessage(TEXT("AMUnblock"));\r
+            }\r
+            if (uMsgId != 0) {\r
+                MSG msg;\r
+                //  Remove old ones\r
+                while (PeekMessage(&msg, (HWND)-1, uMsgId, uMsgId, PM_REMOVE)) {\r
+                }\r
+            }\r
+            PostThreadMessage(GetCurrentThreadId(), uMsgId, 0, 0);\r
+        }\r
+    }\r
+    return dwResult;\r
+}\r
+\r
+HRESULT AmGetLastErrorToHResult()\r
+{\r
+    DWORD dwLastError = GetLastError();\r
+    if(dwLastError != 0)\r
+    {\r
+        return HRESULT_FROM_WIN32(dwLastError);\r
+    }\r
+    else\r
+    {\r
+        return E_FAIL;\r
+    }\r
+}\r
+\r
+IUnknown* QzAtlComPtrAssign(__deref_inout_opt IUnknown** pp, __in_opt IUnknown* lp)\r
+{\r
+    if (lp != NULL)\r
+        lp->AddRef();\r
+    if (*pp)\r
+        (*pp)->Release();\r
+    *pp = lp;\r
+    return lp;\r
+}\r
+\r
+/******************************************************************************\r
+\r
+CompatibleTimeSetEvent\r
+\r
+    CompatibleTimeSetEvent() sets the TIME_KILL_SYNCHRONOUS flag before calling\r
+timeSetEvent() if the current operating system supports it.  TIME_KILL_SYNCHRONOUS\r
+is supported on Windows XP and later operating systems.\r
+\r
+Parameters:\r
+- The same parameters as timeSetEvent().  See timeSetEvent()'s documentation in \r
+the Platform SDK for more information.\r
+\r
+Return Value:\r
+- The same return value as timeSetEvent().  See timeSetEvent()'s documentation in \r
+the Platform SDK for more information.\r
+\r
+******************************************************************************/\r
+MMRESULT CompatibleTimeSetEvent( UINT uDelay, UINT uResolution, __in LPTIMECALLBACK lpTimeProc, DWORD_PTR dwUser, UINT fuEvent )\r
+{\r
+    #if WINVER >= 0x0501\r
+    {\r
+        static bool fCheckedVersion = false;\r
+        static bool fTimeKillSynchronousFlagAvailable = false; \r
+\r
+        if( !fCheckedVersion ) {\r
+            fTimeKillSynchronousFlagAvailable = TimeKillSynchronousFlagAvailable();\r
+            fCheckedVersion = true;\r
+        }\r
+\r
+        if( fTimeKillSynchronousFlagAvailable ) {\r
+            fuEvent = fuEvent | TIME_KILL_SYNCHRONOUS;\r
+        }\r
+    }\r
+    #endif // WINVER >= 0x0501\r
+\r
+    return timeSetEvent( uDelay, uResolution, lpTimeProc, dwUser, fuEvent );\r
+}\r
+\r
+bool TimeKillSynchronousFlagAvailable( void )\r
+{\r
+    OSVERSIONINFO osverinfo;\r
+\r
+    osverinfo.dwOSVersionInfoSize = sizeof(osverinfo);\r
+\r
+    if( GetVersionEx( &osverinfo ) ) {\r
+        \r
+        // Windows XP's major version is 5 and its' minor version is 1.\r
+        // timeSetEvent() started supporting the TIME_KILL_SYNCHRONOUS flag\r
+        // in Windows XP.\r
+        if( (osverinfo.dwMajorVersion > 5) || \r
+            ( (osverinfo.dwMajorVersion == 5) && (osverinfo.dwMinorVersion >= 1) ) ) {\r
+            return true;\r
+        }\r
+    }\r
+\r
+    return false;\r
+}\r
+\r
+\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/wxutil.h b/subprojects/gst-plugins-bad/sys/directshow/strmbase/baseclasses/wxutil.h
new file mode 100644 (file)
index 0000000..305974a
--- /dev/null
@@ -0,0 +1,532 @@
+//------------------------------------------------------------------------------\r
+// File: WXUtil.h\r
+//\r
+// Desc: DirectShow base classes - defines helper classes and functions for\r
+//       building multimedia filters.\r
+//\r
+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.\r
+//------------------------------------------------------------------------------\r
+\r
+\r
+#ifndef __WXUTIL__\r
+#define __WXUTIL__\r
+\r
+// eliminate spurious "statement has no effect" warnings.\r
+#pragma warning(disable: 4705)\r
+\r
+// wrapper for whatever critical section we have\r
+class CCritSec {\r
+\r
+    // make copy constructor and assignment operator inaccessible\r
+\r
+    CCritSec(const CCritSec &refCritSec);\r
+    CCritSec &operator=(const CCritSec &refCritSec);\r
+\r
+    CRITICAL_SECTION m_CritSec;\r
+\r
+#ifdef DEBUG\r
+public:\r
+    DWORD   m_currentOwner;\r
+    DWORD   m_lockCount;\r
+    BOOL    m_fTrace;        // Trace this one\r
+public:\r
+    CCritSec();\r
+    ~CCritSec();\r
+    void Lock();\r
+    void Unlock();\r
+#else\r
+\r
+public:\r
+    CCritSec() {\r
+        InitializeCriticalSection(&m_CritSec);\r
+    };\r
+\r
+    ~CCritSec() {\r
+        DeleteCriticalSection(&m_CritSec);\r
+    };\r
+\r
+    void Lock() {\r
+        EnterCriticalSection(&m_CritSec);\r
+    };\r
+\r
+    void Unlock() {\r
+        LeaveCriticalSection(&m_CritSec);\r
+    };\r
+#endif\r
+};\r
+\r
+//\r
+// To make deadlocks easier to track it is useful to insert in the\r
+// code an assertion that says whether we own a critical section or\r
+// not.  We make the routines that do the checking globals to avoid\r
+// having different numbers of member functions in the debug and\r
+// retail class implementations of CCritSec.  In addition we provide\r
+// a routine that allows usage of specific critical sections to be\r
+// traced.  This is NOT on by default - there are far too many.\r
+//\r
+\r
+#ifdef DEBUG\r
+    BOOL WINAPI CritCheckIn(CCritSec * pcCrit);\r
+    BOOL WINAPI CritCheckIn(const CCritSec * pcCrit);\r
+    BOOL WINAPI CritCheckOut(CCritSec * pcCrit);\r
+    BOOL WINAPI CritCheckOut(const CCritSec * pcCrit);\r
+    void WINAPI DbgLockTrace(CCritSec * pcCrit, BOOL fTrace);\r
+#else\r
+    #define CritCheckIn(x) TRUE\r
+    #define CritCheckOut(x) TRUE\r
+    #define DbgLockTrace(pc, fT)\r
+#endif\r
+\r
+\r
+// locks a critical section, and unlocks it automatically\r
+// when the lock goes out of scope\r
+class CAutoLock {\r
+\r
+    // make copy constructor and assignment operator inaccessible\r
+\r
+    CAutoLock(const CAutoLock &refAutoLock);\r
+    CAutoLock &operator=(const CAutoLock &refAutoLock);\r
+\r
+protected:\r
+    CCritSec * m_pLock;\r
+\r
+public:\r
+    CAutoLock(CCritSec * plock)\r
+    {\r
+        m_pLock = plock;\r
+        m_pLock->Lock();\r
+    };\r
+\r
+    ~CAutoLock() {\r
+        m_pLock->Unlock();\r
+    };\r
+};\r
+\r
+\r
+\r
+// wrapper for event objects\r
+class CAMEvent\r
+{\r
+\r
+    // make copy constructor and assignment operator inaccessible\r
+\r
+    CAMEvent(const CAMEvent &refEvent);\r
+    CAMEvent &operator=(const CAMEvent &refEvent);\r
+\r
+protected:\r
+    HANDLE m_hEvent;\r
+public:\r
+    CAMEvent(BOOL fManualReset = FALSE, __inout_opt HRESULT *phr = NULL);\r
+    CAMEvent(__inout_opt HRESULT *phr);\r
+    ~CAMEvent();\r
+\r
+    // Cast to HANDLE - we don't support this as an lvalue\r
+    operator HANDLE () const { return m_hEvent; };\r
+\r
+    void Set() {EXECUTE_ASSERT(SetEvent(m_hEvent));};\r
+    BOOL Wait(DWORD dwTimeout = INFINITE) {\r
+       return (WaitForSingleObject(m_hEvent, dwTimeout) == WAIT_OBJECT_0);\r
+    };\r
+    void Reset() { ResetEvent(m_hEvent); };\r
+    BOOL Check() { return Wait(0); };\r
+};\r
+\r
+\r
+// wrapper for event objects that do message processing\r
+// This adds ONE method to the CAMEvent object to allow sent\r
+// messages to be processed while waiting\r
+\r
+class CAMMsgEvent : public CAMEvent\r
+{\r
+\r
+public:\r
+\r
+    CAMMsgEvent(__inout_opt HRESULT *phr = NULL);\r
+\r
+    // Allow SEND messages to be processed while waiting\r
+    BOOL WaitMsg(DWORD dwTimeout = INFINITE);\r
+};\r
+\r
+// old name supported for the time being\r
+#define CTimeoutEvent CAMEvent\r
+\r
+// support for a worker thread\r
+\r
+#ifdef AM_NOVTABLE\r
+// simple thread class supports creation of worker thread, synchronization\r
+// and communication. Can be derived to simplify parameter passing\r
+class AM_NOVTABLE CAMThread {\r
+\r
+    // make copy constructor and assignment operator inaccessible\r
+\r
+    CAMThread(const CAMThread &refThread);\r
+    CAMThread &operator=(const CAMThread &refThread);\r
+\r
+    CAMEvent m_EventSend;\r
+    CAMEvent m_EventComplete;\r
+\r
+    DWORD m_dwParam;\r
+    DWORD m_dwReturnVal;\r
+\r
+protected:\r
+    HANDLE m_hThread;\r
+\r
+    // thread will run this function on startup\r
+    // must be supplied by derived class\r
+    virtual DWORD ThreadProc() = 0;\r
+\r
+public:\r
+    CAMThread(__inout_opt HRESULT *phr = NULL);\r
+    virtual ~CAMThread();\r
+\r
+    CCritSec m_AccessLock;     // locks access by client threads\r
+    CCritSec m_WorkerLock;     // locks access to shared objects\r
+\r
+    // thread initially runs this. param is actually 'this'. function\r
+    // just gets this and calls ThreadProc\r
+    static DWORD WINAPI InitialThreadProc(__inout LPVOID pv);\r
+\r
+    // start thread running  - error if already running\r
+    BOOL Create();\r
+\r
+    // signal the thread, and block for a response\r
+    //\r
+    DWORD CallWorker(DWORD);\r
+\r
+    // accessor thread calls this when done with thread (having told thread\r
+    // to exit)\r
+    void Close() {\r
+\r
+        // Disable warning: Conversion from LONG to PVOID of greater size\r
+#pragma warning(push)\r
+#pragma warning(disable: 4312)\r
+        HANDLE hThread = (HANDLE)InterlockedExchangePointer(&m_hThread, 0);\r
+#pragma warning(pop)\r
+\r
+        if (hThread) {\r
+            WaitForSingleObject(hThread, INFINITE);\r
+            CloseHandle(hThread);\r
+        }\r
+    };\r
+\r
+    // ThreadExists\r
+    // Return TRUE if the thread exists. FALSE otherwise\r
+    BOOL ThreadExists(void) const\r
+    {\r
+        if (m_hThread == 0) {\r
+            return FALSE;\r
+        } else {\r
+            return TRUE;\r
+        }\r
+    }\r
+\r
+    // wait for the next request\r
+    DWORD GetRequest();\r
+\r
+    // is there a request?\r
+    BOOL CheckRequest(__out_opt DWORD * pParam);\r
+\r
+    // reply to the request\r
+    void Reply(DWORD);\r
+\r
+    // If you want to do WaitForMultipleObjects you'll need to include\r
+    // this handle in your wait list or you won't be responsive\r
+    HANDLE GetRequestHandle() const { return m_EventSend; };\r
+\r
+    // Find out what the request was\r
+    DWORD GetRequestParam() const { return m_dwParam; };\r
+\r
+    // call CoInitializeEx (COINIT_DISABLE_OLE1DDE) if\r
+    // available. S_FALSE means it's not available.\r
+    static HRESULT CoInitializeHelper();\r
+};\r
+#endif // AM_NOVTABLE\r
+\r
+\r
+// CQueue\r
+//\r
+// Implements a simple Queue ADT.  The queue contains a finite number of\r
+// objects, access to which is controlled by a semaphore.  The semaphore\r
+// is created with an initial count (N).  Each time an object is added\r
+// a call to WaitForSingleObject is made on the semaphore's handle.  When\r
+// this function returns a slot has been reserved in the queue for the new\r
+// object.  If no slots are available the function blocks until one becomes\r
+// available.  Each time an object is removed from the queue ReleaseSemaphore\r
+// is called on the semaphore's handle, thus freeing a slot in the queue.\r
+// If no objects are present in the queue the function blocks until an\r
+// object has been added.\r
+\r
+#define DEFAULT_QUEUESIZE   2\r
+\r
+template <class T> class CQueue {\r
+private:\r
+    HANDLE          hSemPut;        // Semaphore controlling queue "putting"\r
+    HANDLE          hSemGet;        // Semaphore controlling queue "getting"\r
+    CRITICAL_SECTION CritSect;      // Thread seriallization\r
+    int             nMax;           // Max objects allowed in queue\r
+    int             iNextPut;       // Array index of next "PutMsg"\r
+    int             iNextGet;       // Array index of next "GetMsg"\r
+    T              *QueueObjects;   // Array of objects (ptr's to void)\r
+\r
+    void Initialize(int n) {\r
+        iNextPut = iNextGet = 0;\r
+        nMax = n;\r
+        InitializeCriticalSection(&CritSect);\r
+        hSemPut = CreateSemaphore(NULL, n, n, NULL);\r
+        hSemGet = CreateSemaphore(NULL, 0, n, NULL);\r
+        QueueObjects = new T[n];\r
+    }\r
+\r
+\r
+public:\r
+    CQueue(int n) {\r
+        Initialize(n);\r
+    }\r
+\r
+    CQueue() {\r
+        Initialize(DEFAULT_QUEUESIZE);\r
+    }\r
+\r
+    ~CQueue() {\r
+        delete [] QueueObjects;\r
+        DeleteCriticalSection(&CritSect);\r
+        CloseHandle(hSemPut);\r
+        CloseHandle(hSemGet);\r
+    }\r
+\r
+    T GetQueueObject() {\r
+        int iSlot;\r
+        T Object;\r
+        LONG lPrevious;\r
+\r
+        // Wait for someone to put something on our queue, returns straight\r
+        // away is there is already an object on the queue.\r
+        //\r
+        WaitForSingleObject(hSemGet, INFINITE);\r
+\r
+        EnterCriticalSection(&CritSect);\r
+        iSlot = iNextGet++ % nMax;\r
+        Object = QueueObjects[iSlot];\r
+        LeaveCriticalSection(&CritSect);\r
+\r
+        // Release anyone waiting to put an object onto our queue as there\r
+        // is now space available in the queue.\r
+        //\r
+        ReleaseSemaphore(hSemPut, 1L, &lPrevious);\r
+        return Object;\r
+    }\r
+\r
+    void PutQueueObject(T Object) {\r
+        int iSlot;\r
+        LONG lPrevious;\r
+\r
+        // Wait for someone to get something from our queue, returns straight\r
+        // away is there is already an empty slot on the queue.\r
+        //\r
+        WaitForSingleObject(hSemPut, INFINITE);\r
+\r
+        EnterCriticalSection(&CritSect);\r
+        iSlot = iNextPut++ % nMax;\r
+        QueueObjects[iSlot] = Object;\r
+        LeaveCriticalSection(&CritSect);\r
+\r
+        // Release anyone waiting to remove an object from our queue as there\r
+        // is now an object available to be removed.\r
+        //\r
+        ReleaseSemaphore(hSemGet, 1L, &lPrevious);\r
+    }\r
+};\r
+\r
+// Ensures that memory is not read past the length source buffer\r
+// and that memory is not written past the length of the dst buffer\r
+//   dst - buffer to copy to\r
+//   dst_size - total size of destination buffer\r
+//   cb_dst_offset - offset, first byte copied to dst+cb_dst_offset\r
+//   src - buffer to copy from\r
+//   src_size - total size of source buffer\r
+//   cb_src_offset - offset, first byte copied from src+cb_src_offset\r
+//   count - number of bytes to copy\r
+//\r
+// Returns:\r
+//    S_OK          - no error\r
+//    E_INVALIDARG  - values passed would lead to overrun\r
+HRESULT AMSafeMemMoveOffset(\r
+    __in_bcount(dst_size) void * dst,\r
+    __in size_t dst_size,\r
+    __in DWORD cb_dst_offset,\r
+    __in_bcount(src_size) const void * src,\r
+    __in size_t src_size,\r
+    __in DWORD cb_src_offset,\r
+    __in size_t count);\r
+\r
+extern "C"\r
+void * __stdcall memmoveInternal(void *, const void *, size_t);\r
+\r
+inline void * __cdecl memchrInternal(const void *buf, int chr, size_t cnt)\r
+{\r
+#ifdef _X86_\r
+    void *pRet = NULL;\r
+\r
+    _asm {\r
+        cld                 // make sure we get the direction right\r
+        mov     ecx, cnt    // num of bytes to scan\r
+        mov     edi, buf    // pointer byte stream\r
+        mov     eax, chr    // byte to scan for\r
+        repne   scasb       // look for the byte in the byte stream\r
+        jnz     exit_memchr // Z flag set if byte found\r
+        dec     edi         // scasb always increments edi even when it\r
+                            // finds the required byte\r
+        mov     pRet, edi\r
+exit_memchr:\r
+    }\r
+    return pRet;\r
+\r
+#else\r
+    while ( cnt && (*(unsigned char *)buf != (unsigned char)chr) ) {\r
+        buf = (unsigned char *)buf + 1;\r
+        cnt--;\r
+    }\r
+\r
+    return(cnt ? (void *)buf : NULL);\r
+#endif\r
+}\r
+\r
+void WINAPI IntToWstr(int i, __out_ecount(12) LPWSTR wstr);\r
+\r
+#define WstrToInt(sz) _wtoi(sz)\r
+#define atoiW(sz) _wtoi(sz)\r
+#define atoiA(sz) atoi(sz)\r
+\r
+// These are available to help managing bitmap VIDEOINFOHEADER media structures\r
+\r
+extern const DWORD bits555[3];\r
+extern const DWORD bits565[3];\r
+extern const DWORD bits888[3];\r
+\r
+// These help convert between VIDEOINFOHEADER and BITMAPINFO structures\r
+\r
+STDAPI_(const GUID) GetTrueColorType(const BITMAPINFOHEADER *pbmiHeader);\r
+STDAPI_(const GUID) GetBitmapSubtype(const BITMAPINFOHEADER *pbmiHeader);\r
+STDAPI_(WORD) GetBitCount(const GUID *pSubtype);\r
+\r
+// strmbase.lib implements this for compatibility with people who\r
+// managed to link to this directly.  we don't want to advertise it.\r
+//\r
+// STDAPI_(/* T */ CHAR *) GetSubtypeName(const GUID *pSubtype);\r
+\r
+STDAPI_(CHAR *) GetSubtypeNameA(const GUID *pSubtype);\r
+STDAPI_(WCHAR *) GetSubtypeNameW(const GUID *pSubtype);\r
+\r
+#ifdef UNICODE\r
+#define GetSubtypeName GetSubtypeNameW\r
+#else\r
+#define GetSubtypeName GetSubtypeNameA\r
+#endif\r
+\r
+STDAPI_(LONG) GetBitmapFormatSize(const BITMAPINFOHEADER *pHeader);\r
+STDAPI_(DWORD) GetBitmapSize(const BITMAPINFOHEADER *pHeader);\r
+\r
+#ifdef __AMVIDEO__\r
+STDAPI_(BOOL) ContainsPalette(const VIDEOINFOHEADER *pVideoInfo);\r
+STDAPI_(const RGBQUAD *) GetBitmapPalette(const VIDEOINFOHEADER *pVideoInfo);\r
+#endif // __AMVIDEO__\r
+\r
+\r
+// Compares two interfaces and returns TRUE if they are on the same object\r
+BOOL WINAPI IsEqualObject(IUnknown *pFirst, IUnknown *pSecond);\r
+\r
+// This is for comparing pins\r
+#define EqualPins(pPin1, pPin2) IsEqualObject(pPin1, pPin2)\r
+\r
+\r
+// Arithmetic helper functions\r
+\r
+// Compute (a * b + rnd) / c\r
+LONGLONG WINAPI llMulDiv(LONGLONG a, LONGLONG b, LONGLONG c, LONGLONG rnd);\r
+LONGLONG WINAPI Int64x32Div32(LONGLONG a, LONG b, LONG c, LONG rnd);\r
+\r
+\r
+// Avoids us dyna-linking to SysAllocString to copy BSTR strings\r
+STDAPI WriteBSTR(__deref_out BSTR * pstrDest, LPCWSTR szSrc);\r
+STDAPI FreeBSTR(__deref_in BSTR* pstr);\r
+\r
+// Return a wide string - allocating memory for it\r
+// Returns:\r
+//    S_OK          - no error\r
+//    E_POINTER     - ppszReturn == NULL\r
+//    E_OUTOFMEMORY - can't allocate memory for returned string\r
+STDAPI AMGetWideString(LPCWSTR pszString, __deref_out LPWSTR *ppszReturn);\r
+\r
+// Special wait for objects owning windows\r
+DWORD WINAPI WaitDispatchingMessages(\r
+    HANDLE hObject,\r
+    DWORD dwWait,\r
+    HWND hwnd = NULL,\r
+    UINT uMsg = 0,\r
+    HANDLE hEvent = NULL);\r
+\r
+// HRESULT_FROM_WIN32 converts ERROR_SUCCESS to a success code, but in\r
+// our use of HRESULT_FROM_WIN32, it typically means a function failed\r
+// to call SetLastError(), and we still want a failure code.\r
+//\r
+#define AmHresultFromWin32(x) (MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, x))\r
+\r
+// call GetLastError and return an HRESULT value that will fail the\r
+// SUCCEEDED() macro.\r
+HRESULT AmGetLastErrorToHResult(void);\r
+\r
+// duplicate of ATL's CComPtr to avoid linker conflicts.\r
+\r
+IUnknown* QzAtlComPtrAssign(__deref_inout_opt IUnknown** pp, __in_opt IUnknown* lp);\r
+\r
+template <class T>\r
+class QzCComPtr\r
+{\r
+public:\r
+       typedef T _PtrClass;\r
+       QzCComPtr() {p=NULL;}\r
+       QzCComPtr(T* lp)\r
+       {\r
+               if ((p = lp) != NULL)\r
+                       p->AddRef();\r
+       }\r
+       QzCComPtr(const QzCComPtr<T>& lp)\r
+       {\r
+               if ((p = lp.p) != NULL)\r
+                       p->AddRef();\r
+       }\r
+       ~QzCComPtr() {if (p) p->Release();}\r
+       void Release() {if (p) p->Release(); p=NULL;}\r
+       operator T*() {return (T*)p;}\r
+       T& operator*() {ASSERT(p!=NULL); return *p; }\r
+       //The assert on operator& usually indicates a bug.  If this is really\r
+       //what is needed, however, take the address of the p member explicitly.\r
+       T** operator&() { ASSERT(p==NULL); return &p; }\r
+       T* operator->() { ASSERT(p!=NULL); return p; }\r
+       T* operator=(T* lp){return (T*)QzAtlComPtrAssign((IUnknown**)&p, lp);}\r
+       T* operator=(const QzCComPtr<T>& lp)\r
+       {\r
+               return (T*)QzAtlComPtrAssign((IUnknown**)&p, lp.p);\r
+       }\r
+#if _MSC_VER>1020\r
+       bool operator!(){return (p == NULL);}\r
+#else\r
+       BOOL operator!(){return (p == NULL) ? TRUE : FALSE;}\r
+#endif\r
+       T* p;\r
+};\r
+\r
+MMRESULT CompatibleTimeSetEvent( UINT uDelay, UINT uResolution, __in LPTIMECALLBACK lpTimeProc, DWORD_PTR dwUser, UINT fuEvent );\r
+bool TimeKillSynchronousFlagAvailable( void );\r
+\r
+//  Helper to replace lstrcpmi\r
+__inline int lstrcmpiLocaleIndependentW(LPCWSTR lpsz1, LPCWSTR lpsz2)\r
+{\r
+    return  CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, lpsz1, -1, lpsz2, -1) - CSTR_EQUAL;\r
+}\r
+__inline int lstrcmpiLocaleIndependentA(LPCSTR lpsz1, LPCSTR lpsz2)\r
+{\r
+    return  CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, lpsz1, -1, lpsz2, -1) - CSTR_EQUAL;\r
+}\r
+\r
+#endif /* __WXUTIL__ */\r
diff --git a/subprojects/gst-plugins-bad/sys/directshow/strmbase/meson.build b/subprojects/gst-plugins-bad/sys/directshow/strmbase/meson.build
new file mode 100644 (file)
index 0000000..e99d433
--- /dev/null
@@ -0,0 +1,72 @@
+strmbase_sources = [
+  'amextra.cpp',
+  'amfilter.cpp',
+  'amvideo.cpp',
+  'arithutil.cpp',
+  'combase.cpp',
+  'cprop.cpp',
+  'ctlutil.cpp',
+  'ddmm.cpp',
+  'dllentry.cpp',
+  'dllsetup.cpp',
+  'mtype.cpp',
+  'outputq.cpp',
+  'perflog.cpp',
+  'pstream.cpp',
+  'pullpin.cpp',
+  'refclock.cpp',
+  'renbase.cpp',
+  'schedule.cpp',
+  'seekpt.cpp',
+  'source.cpp',
+  'strmctl.cpp',
+  'sysclock.cpp',
+  'transfrm.cpp',
+  'transip.cpp',
+  'videoctl.cpp',
+  'vtrans.cpp',
+  'winctrl.cpp',
+  'winutil.cpp',
+  'wxdebug.cpp',
+  'wxlist.cpp',
+  'wxutil.cpp'
+]
+
+strmbase_cpp_args = ['-D_MBCS']
+strmbase_cpp_args += cxx.get_supported_arguments([
+  '/wd4189', # 'identifier' : local variable is initialized but not referenced
+  '/wd4456', # declaration of 'identifier' hides previous local declaration
+  '/wd4701', # potentially uninitialized local variable 'name' used
+  '/wd4703', # potentially uninitialized local pointer variable 'name' used
+  '/wd4706', # assignment within conditional expression
+  '/wd4996'  # code uses a function, class member, variable, or typedef that's marked deprecated
+])
+
+strmbase_subdir = 'baseclasses'
+strmbase_includes = include_directories(strmbase_subdir)
+
+strmiids_dep = cxx.find_library('strmiids', required: get_option('directshow'))
+if not strmiids_dep.found()
+  message('strmiids not found, not building directshow strmbase')
+  strmbase_dep = disabler()
+  subdir_done()
+endif
+
+strmbase_cpp_sources = []
+foreach file : strmbase_sources
+  strmbase_cpp_sources += strmbase_subdir + '/' + file
+endforeach
+
+strmbase_lib = static_library(
+  'strmbase',
+  strmbase_cpp_sources,
+  cpp_args: strmbase_cpp_args,
+  dependencies: strmiids_dep,
+  include_directories: strmbase_includes,
+  override_options: ['cpp_std=none'])
+
+strmbase_dep = declare_dependency(
+  link_with: strmbase_lib,
+  compile_args: strmbase_cpp_args,
+  dependencies: strmiids_dep,
+  include_directories: strmbase_includes)
index 2d12a6d..2b7d2f1 100644 (file)
@@ -6,9 +6,7 @@ subdir('d3d11')
 subdir('d3dvideosink')
 subdir('decklink')
 subdir('directsound')
-#subdir('dshowdecwrapper')
-#subdir('dshowsrcwrapper')
-#subdir('dshowvideosink')
+subdir('directshow')
 subdir('dvb')
 subdir('fbdev')
 subdir('ipcpipeline')