[MPlayer-dev-eng] [RFC] TV support under Win32 using DirectShow
Vladimir Voroshilov
voroshil at gmail.com
Tue Jan 16 21:24:28 CET 2007
2007/1/17, Reimar Döffinger <Reimar.Doeffinger at stud.uni-karlsruhe.de>:
> Hello,
> On Tue, Jan 16, 2007 at 02:16:41PM +0600, Vladimir Voroshilov wrote:
> > Driver consist of two parts:
> > 1. DirectShow filter with one video and one audio input pin to grab
> > media samples from live source (almost finished)
>
> I strongly prefer solutions that do not require installation if that was
> possible.
It does need any installation.I meant that one part of my code acts as
DirectShow filter, by i did not say that it is full-featured
DirectShow filter's DLL. My driver is part of MPlayer. MPlayer will
build filter (as DirectShow object) at runtime while opening tv://
under Win32.
> > * Pausing or moving video windows causes buffer overflow (no pause
> > callback exists for tv subsystem in MPlayer and i'm afraid the same
> > issue can be applyed to the linux TV code).
>
> Will have to see about that in detail, but in principle the stream layer
> can just drop frames, assuming it knows where frames start and end...
> I'm fairly certain that should be fixable (though even nicer would be if
> moving the window wouldn't stop everything - interestingly the same
> issue exists under linux, but only with some window managers...)
I'll continue to investigate it.
> > * Sets channels only by it's number, not by freq (probably device
> > driver's limitation) that requires some not fine workaround.
>
> "not fine" meaning? A table for translating frequencies into channels
> seems like a perfectly fine way to handle this.
> Anyway, without seeing the real code it all is hard to say...
But used IAMTVTuner Directshow interface doesn't allow to set
frequency, just channel number. Thus i have to read DirectShow's
channel table first for proper country (not implemented yet), and
every time translate given by tv.c frequency into channel number. This
is what i meant under "not fine workaround". I am planning to do
following:
1. try to set frequency using IKsPropertySet (direct call to tuner, i
think, but not sure).
2. if first call fails driver will set up some flag and will use 3
instead this method until MPlayer finished.
3 driver will use workaround described above.
> > If anybody will be interesting in testing it i can upload prebuilded
> > binaries+source anywhere in Web to make testing more easy.
>
> This is the developers list, I'm sure some here would prefer not to test even
> their own patches. If you want testing, MPlayer-cygwin or so seems the
> better place to suggest it...
:) ok.
Well, i have attached current development version.
First i want hear something about driver's architecture.
Probably i am running in wrong way (i hope, I'm not).
I'm afraid that (due to output.c changes) it can also broke any
functionality in Win32 loader (it shouldn't but who knows...). I have
tested (under Linux) all media files from my HDD, but non of them used
dshow loader (just dmo). Thus my test was incomplete.
I also does not decide where put tvi_dshow.h contents to.
It contains some declaration, that already defined in either recent
wine versions or used mingw32. Unfortunately it is not possible to mix
it together.
--
Regards,
Vladimir Voroshilov mailto:voroshil at gmail.com
JID: voroshil at jabber.ru
ICQ: 95587719
-------------- next part --------------
Index: configure
===================================================================
--- configure (revision 21934)
+++ configure (working copy)
@@ -1721,6 +1721,7 @@
_tv_v4l1=auto
_tv_v4l2=auto
_tv_bsdbt848=auto
+_tv_dshow=auto
_pvr=auto
_network=yes
_winsock2=auto
@@ -1971,6 +1972,8 @@
--disable-tv-v4l1) _tv_v4l1=no ;;
--enable-tv-v4l2) _tv_v4l2=yes ;;
--disable-tv-v4l2) _tv_v4l2=no ;;
+ --enable-tv-dshow) _tv_dshow=yes ;;
+ --disable-tv-dshow) _tv_dshow=no ;;
--enable-radio) _radio=yes ;;
--enable-radio-capture) _radio_capture=yes ;;
--disable-radio-capture) _radio_capture=no ;;
@@ -6692,6 +6695,33 @@
fi #if bsd
+echocheck "DirectShow TV interface"
+if test "$_tv_dshow" = auto ; then
+ _tv_dshow=no
+ if test "$_tv" = yes && mingw32 ; then
+ cat > $TMPC <<EOF
+#include <basetyps.h>
+int main(void) {
+ void* p;
+ CoCreateInstance((GUID*)&GUID_NULL, NULL, CLSCTX_INPROC_SERVER, &GUID_NULL, (void**)&p);
+ return 0;
+}
+EOF
+ cc_check && _tv_dshow=yes
+ fi
+fi
+if test "$_tv_dshow" = yes ; then
+ _tv_v4l=yes
+ _def_tv_v4l='#define HAVE_TV_DSHOW 1'
+ _def_tv_v4l1='#define HAVE_TV_DSHOW 1'
+ _inputmodules="tv-dshow $_inputmodules"
+else
+ _noinputmodules="tv-dshow $_noinputmodules"
+ _def_tv_v4l='#undef HAVE_TV_DSHOW'
+fi
+echores "$_tv_dshow"
+
+
echocheck "Video 4 Linux TV interface"
if test "$_tv_v4l1" = auto ; then
_tv_v4l1=no
@@ -7526,6 +7556,7 @@
TV_V4L = $_tv_v4l
TV_V4L1 = $_tv_v4l1
TV_V4L2 = $_tv_v4l2
+TV_DSHOW = $_tv_dshow
TV_BSDBT848 = $_tv_bsdbt848
PVR = $_pvr
VCD = $_vcd
@@ -8040,6 +8071,9 @@
/* Enable Video 4 Linux 2 TV interface support */
$_def_tv_v4l2
+/* Enable DirectShow TV interface support */
+$_def_tv_dshow
+
/* Enable *BSD BrookTree TV interface support */
$_def_tv_bsdbt848
Index: loader/com.h
===================================================================
--- loader/com.h (revision 21934)
+++ loader/com.h (working copy)
@@ -25,8 +25,6 @@
extern "C" {
#endif
-void* CoTaskMemAlloc(unsigned long cb);
-void CoTaskMemFree(void* cb);
#ifndef GUID_TYPE
#define GUID_TYPE
@@ -77,8 +75,18 @@
struct IClassFactory_vt* vt;
};
+#if !defined(__MINGW32__)
+//need proper ifdef to check Co* functions availability
long CoCreateInstance(GUID* rclsid, struct IUnknown* pUnkOuter,
long dwClsContext, const GUID* riid, void** ppv);
+void* CoTaskMemAlloc(unsigned long cb);
+void CoTaskMemFree(void* cb);
+#else
+long STDCALL CoCreateInstance(GUID* rclsid, struct IUnknown* pUnkOuter,
+ long dwClsContext, const GUID* riid, void** ppv);
+void* STDCALL CoTaskMemAlloc(unsigned long);
+void STDCALL CoTaskMemFree(void*);
+#endif
#ifdef __cplusplus
};
Index: loader/dshow/DS_Filter.c
===================================================================
--- loader/dshow/DS_Filter.c (revision 21934)
+++ loader/dshow/DS_Filter.c (working copy)
@@ -249,7 +249,7 @@
break;
}
- This->m_pOurOutput = COutputPinCreate(This->m_pDestType);
+ This->m_pOurOutput = COutputPinCreate(This->m_pDestType,(IBaseFilter*)This);
result = This->m_pOutputPin->vt->ReceiveConnection(This->m_pOutputPin,
(IPin*) This->m_pOurOutput,
Index: loader/dshow/DS_MPGrabber.c
===================================================================
--- loader/dshow/DS_MPGrabber.c (revision 0)
+++ loader/dshow/DS_MPGrabber.c (revision 0)
@@ -0,0 +1,730 @@
+#include "DS_MPGrabber.h"
+#include "wine/winerror.h"
+#include "outputpin.h"
+#include "inputpin.h"
+#include "interfaces.h"
+
+#include <stdlib.h>
+#include "mp_msg.h"
+
+
+#ifndef NOAVIFILE_HEADERS
+#include "audiodecoder.h"
+#include "except.h"
+#define VFW_E_NOT_RUNNING 0x80040226
+#define VFW_E_NO_ALLOCATOR 0x8004020A
+//#include "fourcc.h"
+//#include "except.h"
+#else
+#include "libwin32.h"
+#endif
+
+#define Debug if(0)
+
+const GUID IID_IAMFilterMiscFlags={0x2dd74950, 0xa890, 0x11d1,
+ { 0xab, 0xe8, 0x00, 0xa0, 0xc9, 0x05, 0xf3, 0x75}};
+
+const GUID IID_IPersist={0x0000010c,0x0000,0x0000,
+ { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
+
+static inline int unimplemented(const char* s, void* p)
+{
+ Debug mp_msg(MSGT_TV,MSGL_DBG3,"%s(%p) called (UNIMPLEMENTED)\n", s, p);
+ return E_NOTIMPL;
+}
+
+//FIXME: need a real GUID
+const CLSID CLSID_MPGrabber={0x12345678,0x4643,0x0654,
+ { 0x05, 0xc3, 0x02, 0x50, 0x05, 0x20, 0x30, 0x46}};
+
+/**
+ Implementation of IAMFilterMiscFlags interface
+
+ Used to point filter graph that our filter is Renderer
+ TODO: check if this really need
+
+*/
+typedef struct CAMFilterMiscFlags{
+ IAMFilterMiscFlags_vt* vt;
+ DECLARE_IUNKNOWN();
+ GUID interfaces[2];
+
+ ULONG flags;
+} CAMFilterMiscFlags;
+
+
+/**
+ * \brief IAMFilterMiscFlags::GetMiscFlags
+ *
+ * \param[in] this IAMFilterMiscFlags interface
+ *
+ * \return flags
+ */
+static ULONG STDCALL CAMFilterMiscFlags_GetMiscFlags(IAMFilterMiscFlags* This)
+{
+ return ((CAMFilterMiscFlags*)This)->flags;
+}
+
+/**
+ * \brief CAMFilterMiscFlags destructor
+ *
+ * \param[in] this IAMFilterMiscFlags interface
+ *
+ */
+void CAMFilterMiscFlags_Destroy(CAMFilterMiscFlags* This){
+ if (This->vt)
+ free(This->vt);
+ free(This);
+}
+
+IMPLEMENT_IUNKNOWN(CAMFilterMiscFlags)
+
+
+/**
+ * \brief CAMFilterMiscFlags constructor
+ *
+ * \param[in] this IAMFilterMiscFlags interface
+ * \param[in] flags flags to return with IAMFilterMiscFlags::GetMiscFlags
+ *
+ * \return CAMFilterMiscFlags object pointer
+ * \return NULL if error occured
+ *
+ */
+CAMFilterMiscFlags* CAMFilterMiscFlagsCreate(ULONG flags){
+ CAMFilterMiscFlags* This = (CAMFilterMiscFlags*) malloc(sizeof(CAMFilterMiscFlags));
+ if (!This)
+ return NULL;
+ This->vt = (IBaseFilter_vt*) malloc(sizeof(IBaseFilter_vt));
+
+ if (!This->vt)
+ {
+ CAMFilterMiscFlags_Destroy(This);
+ return NULL;
+ }
+
+ This->refcount = 1;
+ This->flags=flags;
+ This->vt->QueryInterface = CAMFilterMiscFlags_QueryInterface;
+ This->vt->AddRef = CAMFilterMiscFlags_AddRef;
+ This->vt->Release = CAMFilterMiscFlags_Release;
+
+ This->interfaces[0] = IID_IUnknown;
+ This->interfaces[1] = IID_IAMFilterMiscFlags;
+
+ This->vt->GetMiscFlags = CAMFilterMiscFlags_GetMiscFlags;
+ return This;
+}
+
+/*******************************************
+ * Implementation of DS_MPGrabber methods
+ ********************************************/
+
+/**
+ * \brief fills given buffer with data of next media sample
+ *
+ * \param[in] This pointer to DS_MPGrabber object
+ * \param[out] buf pointer to buffer
+ * \param[in] len buffer length
+ * \param[in] bVideo 1-video sample,0-audio sample
+ * \param[in] pPts address of variable receives timestamp
+ *
+ * \return number of data written to buffer
+ *
+ */
+static int STDCALL DS_Grabber_FillBuffer(DS_MPGrabber* This,char* buf,int len,int bVideo,long long * pPts){
+ IMediaSample* sample;
+ IMemAllocator* pAll=NULL;
+ HRESULT hr;
+ int i,bytes=0;
+ BYTE* pBuffer=NULL;
+ COutputPin* pin=NULL;
+
+ Debug mp_msg(MSGT_TV,MSGL_DBG3,"DS_MPGrabber_FillBuffer (%p,%d)\n", This,bVideo);
+
+
+ if (bVideo)
+ pin=This->pinVideo;
+ else
+ pin=This->pinAudio;
+
+ if(!pin) return 0; //No required byffer
+ for(i=0;i<100 && !bytes;i++)
+ {
+ bytes=pin->FillBuffer(pin,buf,len,pPts,NULL);
+ if(bytes) break;
+ sleep(1);
+ }
+ return bytes;
+}
+
+/**
+ * \brief IPersist::GetClassID (returns filter's CLSID)
+ *
+ * \param[in] this pointer to IPersis interface
+ * \param[out] pClassID address of CLSID variables that receives Filter's class id
+ *
+ * \return E_POINTER Null pointer
+ * \return S_OK success
+ * \return E_FAIL error occured
+ *
+ * stub
+ *
+ */
+static long STDCALL DS_MPGrabber_GetClassID(IBaseFilter * This, CLSID *pClassID)
+{
+ Debug mp_msg(MSGT_TV,MSGL_DBG3,"DS_MPGrabber_GetClassID(%p)\n", This);
+ if (!pClassID) return E_POINTER;
+
+ memcpy(pClassID,&CLSID_MPGrabber,16); //FIXME: stub
+ return E_FAIL;
+}
+
+/**
+ * \brief IMediaFilter::Stop (stops filter)
+ *
+ * \param[in] This pointer to IBaseFilter interface
+ *
+ * \return S_OK success
+ * \return S_FALSE transition is not complete
+ *
+ * FIXME: fix return codes
+ */
+static long STDCALL DS_MPGrabber_Stop(IBaseFilter* This)
+{
+ HRESULT hr;
+ IMemAllocator* pAll;
+ DS_MPGrabber* this=(DS_MPGrabber*)This;
+ Debug mp_msg(MSGT_TV,MSGL_DBG3,"DS_MPGrabber_Stop (%p)\n", This);
+
+ if (this->state==State_Stopped)
+ return S_OK;
+
+ if(this->state==State_Running){
+ if(this->mempinVideo){
+ hr=this->mempinVideo->vt->GetAllocator(this->mempinVideo,&pAll);
+ if(FAILED(hr) || !pAll){
+ Debug mp_msg(MSGT_TV,MSGL_WARN,"DS_MPGrabber_Stop (%p): video->GetAllocator failure!\n", This);
+ return E_FAIL;
+ }
+ hr=pAll->vt->Decommit(pAll);
+ if(FAILED(hr) || !pAll){
+ Debug mp_msg(MSGT_TV,MSGL_WARN,"DS_MPGrabber_Stop (%p): video->Decommit failure!\n", This);
+ pAll->vt->Release((IUnknown*)pAll);
+ return E_FAIL;
+ }
+ pAll->vt->Release((IUnknown*)pAll);
+ }
+ if(this->mempinAudio){
+ hr=this->mempinAudio->vt->GetAllocator(this->mempinAudio,&pAll);
+ if(FAILED(hr) || !pAll){
+ Debug mp_msg(MSGT_TV,MSGL_WARN,"DS_MPGrabber_Stop (%p): audio->GetAllocator failure!\n", This);
+ return E_FAIL;
+ }
+ hr=pAll->vt->Decommit(pAll);
+ if(FAILED(hr) || !pAll){
+ Debug mp_msg(MSGT_TV,MSGL_WARN,"DS_MPGrabber_Stop (%p): audio->Decommit failure!\n", This);
+ pAll->vt->Release((IUnknown*)pAll);
+ return E_FAIL;
+ }
+ pAll->vt->Release((IUnknown*)pAll);
+ }
+ }
+
+ this->state=State_Stopped;
+ return S_OK;
+}
+
+/**
+ * \brief IMediaFilter::Pause (pauses filter)
+ *
+ * \param[in] This pointer to IBaseFilter interface
+ *
+ * \return S_OK success
+ * \return S_FALSE transition is not complete
+ *
+ * \note
+ * When going into running state DirectShow first moves all filters into
+ * paused state. Filter should prepare itself to start if it was in stopped
+ * state.
+ *
+ */
+static long STDCALL DS_MPGrabber_Pause(IBaseFilter* This)
+{
+ HRESULT hr;
+ IMemAllocator* pAll;
+ DS_MPGrabber* this=(DS_MPGrabber*)This;
+ Debug mp_msg(MSGT_TV,MSGL_DBG3,"DS_MPGrabber_Pause(%p)\n", This);
+
+ if (this->state==State_Paused)
+ return S_OK;
+
+ if(this->state==State_Stopped){
+ if(this->mempinVideo){
+ hr=this->mempinVideo->vt->GetAllocator(this->mempinVideo,&pAll);
+ if(FAILED(hr) || !pAll){
+ Debug mp_msg(MSGT_TV,MSGL_WARN,"DS_MPGrabber_Pause (%p): video->GetAllocator failure!\n", This);
+ return E_FAIL;
+ }
+ hr=pAll->vt->Commit(pAll);
+ if(FAILED(hr) || !pAll){
+ Debug mp_msg(MSGT_TV,MSGL_WARN,"DS_MPGrabber_Pause (%p): video->Commit failure!\n", This);
+ pAll->vt->Release((IUnknown*)pAll);
+ return E_FAIL;
+ }
+ pAll->vt->Release((IUnknown*)pAll);
+ }
+ if(this->mempinAudio){
+ hr=this->mempinAudio->vt->GetAllocator(this->mempinAudio,&pAll);
+ if(FAILED(hr) || !pAll){
+ Debug mp_msg(MSGT_TV,MSGL_WARN,"DS_MPGrabber_Pause (%p): audio->GetAllocator failure!\n", This);
+ return E_FAIL;
+ }
+ hr=pAll->vt->Commit(pAll);
+ if(FAILED(hr) || !pAll){
+ Debug mp_msg(MSGT_TV,MSGL_WARN,"DS_MPGrabber_Pause (%p): audio->Commit failure!\n", This);
+ pAll->vt->Release((IUnknown*)pAll);
+ return E_FAIL;
+ }
+ pAll->vt->Release((IUnknown*)pAll);
+ }
+ }
+
+ this->state=State_Paused;
+ return S_OK;
+}
+
+/**
+ * \brief IMediaFilter::Run (starts filter)
+ *
+ * \param[in] This pointer to IBaseFilter interface
+ * \param[in] tStart start time in 100 nanoseconds units
+ *
+ * \return S_OK success
+ * \return S_FALSE transition is not complete
+ *
+ */
+static long STDCALL DS_MPGrabber_Run(IBaseFilter* This, REFERENCE_TIME tStart)
+{
+ HRESULT hr;
+ DS_MPGrabber* this=(DS_MPGrabber*)This;
+ Debug mp_msg(MSGT_TV,MSGL_DBG3,"DS_MPGrabber_Run(%p)\n", This);
+ if (this->state==State_Stopped){
+ hr=This->vt->Pause(This);
+ if(FAILED(hr)) return hr;
+ }
+ this->m_tStart=tStart;
+ this->state=State_Running;
+ return S_OK;
+}
+
+/**
+ * \brief IMediaFilter::GetState (gets filter's current state)
+ *
+ * \param[in] This pointer to IBaseFilter interface
+ * \param[in] dwMilliSecsTimeout -??
+ * \param[out] State address of variable that receives current state
+ *
+ * \return S_OK success
+ * \return E_POINTER Null pointer
+ * \return VFW_E_STATE_INTERMEDIATE Immediate state
+ * \return VFW_E_CANT_CUE Filter is active, but cannot deliver data
+ *
+ */
+static long STDCALL DS_MPGrabber_GetState(IBaseFilter* This,
+ /* [in] */ unsigned long dwMilliSecsTimeout,
+ /* [out] */ FILTER_STATE *State)
+{
+ DS_MPGrabber* this=(DS_MPGrabber*)This;
+ Debug mp_msg(MSGT_TV,MSGL_DBG3,"DS_MPGrabber_GetState(%p)\n", This);
+
+ if (!State) return E_POINTER;
+ *State=this->state;
+ return S_OK;
+}
+
+/**
+ * \brief IMediaFilter::SetSyncSource (notifies filter for used sync source in graph)
+ *
+ * \param[in] This pointer to IBaseFilter interface
+ * \param[in] pClock pointer to IReferenceClock of used sync source
+ *
+ * \return S_OK success
+ * \return Apropriate return code otherwise
+ *
+ */
+static long STDCALL DS_MPGrabber_SetSyncSource(IBaseFilter* This,
+ /* [in] */ IReferenceClock *pClock)
+{
+ DS_MPGrabber* this=(DS_MPGrabber*)This;
+ Debug mp_msg(MSGT_TV,MSGL_DBG3,"DS_MPGrabber_SetSyncSource(%p,%p)\n", This,pClock);
+ if(!pClock && !this->pClock) return S_OK;
+
+ if(this->pClock)
+ this->pClock->vt->Release((IUnknown*)this->pClock);
+
+ this->pClock=pClock;
+
+ if(this->pClock)
+ this->pClock->vt->AddRef((IUnknown*)this->pClock);
+ return S_OK;
+}
+
+/**
+ * \brief IMediaFilter::GetSyncSource (gets filter's sync source)
+ *
+ * \param[in] This pointer to IBaseFilter interface
+ * \param[out] pClock address of variable that receives IReferenceClock of used sync source
+ *
+ * \return S_OK success
+ * \return E_POINTER Null pointer
+ *
+ */
+static long STDCALL DS_MPGrabber_GetSyncSource(IBaseFilter* This,
+ /* [out] */ IReferenceClock **pClock)
+{
+ DS_MPGrabber* this=(DS_MPGrabber*)This;
+ Debug mp_msg(MSGT_TV,MSGL_DBG3,"DS_MPGrabber_GetSyncSource(%p)\n", This);
+
+ if(!pClock) return E_POINTER;
+ *pClock=this->pClock;
+ if(this->pClock)
+ this->pClock->vt->AddRef((IUnknown*)this->pClock);
+ return S_OK;
+}
+
+/**
+ * \brief IBaseFilter::EnumPins (returns pins enumerator)
+ *
+ * \param[in] This pointer to IBaseFilter interface
+ * \param[out] ppEnum address of variable that receives IEnumPins interface
+ *
+ * \return S_OK success
+ * \return E_POINTER Null pointer
+ * \return E_OUTOFMEMORY Insufficient memory
+ *
+ */
+static long STDCALL DS_MPGrabber_EnumPins(IBaseFilter* This,
+ /* [out] */ IEnumPins **ppEnum)
+{
+ DS_MPGrabber* this=(DS_MPGrabber*)This;
+ Debug mp_msg(MSGT_TV,MSGL_DBG4,"DS_MPGrabber_EnumPins(%p) called\n", This);
+ if(!ppEnum) return E_POINTER;
+
+ if(this->pinVideo && this->pinAudio)
+ *ppEnum = (IEnumPins*) CEnumPinsCreate(this->pinVideo, this->pinAudio);
+ else if(this->pinVideo)
+ *ppEnum = (IEnumPins*) CEnumPinsCreate(this->pinVideo, NULL);
+ else if(this->pinAudio)
+ *ppEnum = (IEnumPins*) CEnumPinsCreate(this->pinAudio, NULL);
+ else
+ return E_POINTER;
+
+ return S_OK;
+}
+
+/**
+ * \brief IBaseFilter::FindPin (returns specified pin)
+ *
+ * \param[in] This pointer to IBaseFilter interface
+ * \param[in] id id of pin to return
+ * \param[out] ppPin address of variable that receives IPin interface
+ *
+ * \return S_OK success
+ * \return E_POINTER Null pointer
+ * \return VFW_E_NOT_FOUND pin not found
+ *
+ */
+static long STDCALL DS_MPGrabber_FindPin(IBaseFilter* This,
+ /* [string][in] */ const unsigned short* Id,
+ /* [out] */ IPin **ppPin)
+{
+ Debug unimplemented("DS_MPGrabber_FindPin\n", This);
+ return E_NOTIMPL;
+}
+
+/**
+ * \brief IBaseFilter::QueryFilterInfo (returns filter info)
+ *
+ * \param[in] This pointer to IBaseFilter interface
+ * \param[out] pInfo pointer to FILTER_INFO that receives filter info
+ *
+ * \return S_OK success
+ * \return E_POINTER Null pointer
+ *
+ */
+static long STDCALL DS_MPGrabber_QueryFilterInfo(IBaseFilter* This,
+ /* [out] */ FILTER_INFO *pInfo)
+{
+ DS_MPGrabber* this=(DS_MPGrabber*)This;
+ Debug mp_msg(MSGT_TV,MSGL_DBG3,"DS_MPGrabber_QueryFilterInfo(%p) Returning:Graph=%p\n", This,((DS_MPGrabber*)This)->m_pGraph);
+
+ if (!pInfo) return E_POINTER;
+ memcpy(pInfo->achName,this->achName,128);
+ pInfo->pGraph=this->m_pGraph;
+ if(pInfo->pGraph)
+ pInfo->pGraph->vt->AddRef((IUnknown*)pInfo->pGraph);
+ return S_OK;
+}
+
+/**
+ * \brief IBaseFilter::JoinFilterGraph (notifies filter that it was added to filter graph)
+ *
+ * \param[in] This pointer to IBaseFilter interface
+ * \param[in] pGraph pointer to IFilterGraph interface of parent graph
+ * \param[in] pName suggested name for the filter
+ *
+ * \return S_OK success
+ * \return E_POINTER Null pointer
+ *
+ * \note
+ * pGraph==NULL means removing from graph
+ *
+ */
+static long STDCALL DS_MPGrabber_JoinFilterGraph(IBaseFilter* This,
+ /* [in] */ IFilterGraph* pGraph,
+ /* [string][in] */ WCHAR* pName)
+{
+ DS_MPGrabber* this=(DS_MPGrabber*)This;
+ Debug mp_msg(MSGT_TV,MSGL_DBG3,"DS_MPGrabber_JoinFilterGraph(%p,%p,%s)\n", This,pGraph,(char*)pName);
+
+ this->m_pGraph=pGraph;
+ if(pGraph && pName) wcscpy(this->achName,pName);
+ return S_OK;
+}
+
+/**
+ * \brief IBaseFilter::JoinFilterGraph (notifies filter that it was added to filter graph)
+ *
+ * \param[in] This pointer to IBaseFilter interface
+ * \param[out] address variable that receives pointer to string, containing vendor info
+ *
+ * \return S_OK success
+ * \return E_POINTER Null pointer
+ * \return E_NOTIMPL Not implemented
+ *
+ */
+static long STDCALL DS_MPGrabber_QueryVendorInfo(IBaseFilter* This,
+ /* [string][out] */ unsigned short** pVendorInfo)
+{
+ Debug unimplemented("DS_MPGrabber_QueryVendorInfo", This);
+ return E_NOTIMPL;
+}
+
+/**
+ * \brief DS_MPGrabber destructor
+ *
+ * \param[in] This pointer to IBaseFilter interface
+ *
+ */
+void DS_MPGrabber_Destroy(DS_MPGrabber* This){
+ Debug mp_msg(MSGT_TV,MSGL_DBG4,"%s(%p) called\n","DS_MPGrabber_Destroy" , This);
+ if (This->mempinVideo)
+ This->mempinVideo->vt->Release((IUnknown*)This->mempinVideo);
+
+ if (This->pinVideo)
+ This->pinVideo->vt->Release((IUnknown*)This->pinVideo);
+
+ if (This->mempinAudio)
+ This->mempinAudio->vt->Release((IUnknown*)This->mempinAudio);
+
+ if (This->pinAudio)
+ This->pinAudio->vt->Release((IUnknown*)This->pinAudio);
+
+ if (This->m_pGraph)
+ This->m_pGraph->vt->Release((IUnknown*)This->m_pGraph);
+
+ if (This->pClock)
+ This->pClock->vt->Release((IUnknown*)This->pClock);
+
+ if (This->m_pFlags)
+ This->m_pFlags->vt->Release((IUnknown*)This->m_pFlags);
+
+ if (This->vt)
+ free(This->vt);
+ free(This);
+}
+
+
+/**
+ * \brief IUnknown::QueryInterface (queryes specified interface from filter)
+ *
+ * \param[in] This pointer to IUnknown interface
+ * \param[in] riid pointer to IID of interface to return
+ * \param[in] ppvObject address of variable that receives pointer to object
+ *
+ * \return S_OK success
+ * \return E_NOINTERFACE no specified interface
+ * \return E_POINTER Null pointer
+ *
+ */
+static long STDCALL DS_MPGrabber_QueryInterface(IUnknown * This,
+ const GUID* riid, void **ppvObject)
+{
+ DS_MPGrabber* me = (DS_MPGrabber*)This;
+ GUID* r; unsigned int i = 0;
+ Debug mp_msg(MSGT_TV,MSGL_DBG4,"DS_MPGrabber_QueryInterface(%p) called\n", This);
+ if (!ppvObject) return E_POINTER;
+ for(r=me->interfaces; i<sizeof(me->interfaces)/sizeof(me->interfaces[0]); r++, i++)
+ if(!memcmp(r, riid, sizeof(*r)))
+ {
+ me->vt->AddRef((IUnknown*)This);
+ *ppvObject=This;
+ return 0;
+ }
+ if(!memcmp(&IID_IAMFilterMiscFlags, riid, sizeof(IID_IAMFilterMiscFlags)))
+ {
+ me->m_pFlags->vt->AddRef((IUnknown*)me->m_pFlags);
+ *ppvObject=me->m_pFlags;
+ return 0;
+ }
+ Debug mp_msg(MSGT_TV,MSGL_DBG3,"DS_MPGrabber_QueryInterface failed! (GUID: %08x-%04x-%04x-%02x%02x-"
+ "%02x%02x%02x%02x%02x%02x\n",
+ riid->f1, riid->f2, riid->f3,
+ (unsigned char)riid->f4[0], (unsigned char)riid->f4[1],
+ (unsigned char)riid->f4[2], (unsigned char)riid->f4[3],
+ (unsigned char)riid->f4[4], (unsigned char)riid->f4[5],
+ (unsigned char)riid->f4[6], (unsigned char)riid->f4[7]);
+
+ return E_NOINTERFACE;
+}
+
+/**
+ * \brief IUnknown::AddRef (increases reference counter for interface)
+ *
+ * \param[in] This pointer to IUnknown class
+ *
+ * \return new value of reference counter
+ *
+ */
+static long STDCALL DS_MPGrabber_AddRef(IUnknown * This)
+{
+ DS_MPGrabber* me=( DS_MPGrabber*)This;
+ Debug mp_msg(MSGT_TV,MSGL_DBG4,"DS_MPGrabber_AddRef(%p) called (ref:%d)\n", This, me->refcount);
+ return ++(me->refcount);
+}
+
+/**
+ * \brief IUnknown::Release (desreases reference counter for interface)
+ *
+ * \param[in] This pointer to IUnknown class
+ *
+ * \return S_OK - success
+ *
+ * \remarks
+ * When reference counter reaches zero calls destructor
+ *
+ */
+static long STDCALL DS_MPGrabber_Release(IUnknown * This)
+{
+ DS_MPGrabber* me=( DS_MPGrabber*)This;
+ Debug mp_msg(MSGT_TV,MSGL_DBG4,"%s_Release(%p) called (new ref:%d)\n", "DS_MPGrabber",This, me->refcount - 1);
+
+ if(--(me->refcount) == 0)
+ DS_MPGrabber_Destroy(me);
+ return S_OK;
+}
+
+/**
+ * \breaf DS_MPGrabber constructor
+ *
+ * \param video pointer to AM_MEDIA_TYPE for video pin
+ * \param audio pointer to AM_MEDIA_TYPE for audio pin
+ *
+ * \return pointer to DS_MPGrabber object if success
+ * \return NULL othewise
+ *
+ * \note
+ * if audio or video parameter is null corresponding pin will not be created
+ */
+DS_MPGrabber* DS_MPGrabberCreate(const AM_MEDIA_TYPE* video,const AM_MEDIA_TYPE* audio){
+ const char* em = NULL;
+ int init=0;
+ HRESULT result;
+ DS_MPGrabber* This;
+
+ mp_msg(MSGT_TV,MSGL_DBG3,"DS_MPGrabberCreate\n");
+
+ if(!audio && !video)
+ return NULL;
+ This = (DS_MPGrabber*) malloc(sizeof(DS_MPGrabber));
+ if (!This)
+ return NULL;
+
+ This->vt = (IBaseFilter_vt*) malloc(sizeof(IBaseFilter_vt));
+ if (!This->vt){
+ free(This);
+ return NULL;
+ }
+
+ This->pinVideo=NULL;
+ This->mempinVideo=NULL;
+
+ This->pinAudio=NULL;
+ This->mempinAudio=NULL;
+
+ This->m_pFlags=CAMFilterMiscFlagsCreate(1); //AM_FILTER_MISC_FLAGS_IS_RENDERER
+ if(!This->m_pFlags){
+ DS_MPGrabber_Destroy(This);
+ return NULL;
+ }
+ //video pin
+ if (video){
+ This->pinVideo = (IPin*) COutputPinCreate(video,This);
+ if (!This->pinVideo){
+ DS_MPGrabber_Destroy(This);
+ return NULL;
+ }
+
+ result=This->pinVideo->vt->QueryInterface((IUnknown*)This->pinVideo,&IID_IMemInputPin,(void**)&(This->mempinVideo));
+ if(FAILED(result) || !This->mempinVideo){
+ DS_MPGrabber_Destroy(This);
+ return NULL;
+ }
+ }
+
+ //audio pin
+ if(audio){
+ This->pinAudio = (IPin*) COutputPinCreate(audio,This);
+ if(!This->pinAudio){
+ DS_MPGrabber_Destroy(This);
+ return NULL;
+ }
+
+ result=This->pinAudio->vt->QueryInterface((IUnknown*)This->pinAudio,&IID_IMemInputPin,(void**)&(This->mempinAudio));
+ if(FAILED(result) || !This->mempinAudio){
+ DS_MPGrabber_Destroy(This);
+ return NULL;
+ }
+ }
+
+ This->refcount = 1;
+ This->m_pGraph=NULL;
+ This->pClock=NULL;
+ This->m_tStart=0;
+ This->state=State_Stopped;
+ memset(This->achName,0,128);
+
+ This->vt->QueryInterface = DS_MPGrabber_QueryInterface;
+ This->vt->AddRef = DS_MPGrabber_AddRef;
+ This->vt->Release = DS_MPGrabber_Release;
+ This->vt->GetClassID = DS_MPGrabber_GetClassID;
+ This->vt->Stop = DS_MPGrabber_Stop;
+ This->vt->Pause = DS_MPGrabber_Pause;
+ This->vt->Run = DS_MPGrabber_Run;
+ This->vt->GetState = DS_MPGrabber_GetState;
+ This->vt->SetSyncSource = DS_MPGrabber_SetSyncSource;
+ This->vt->GetSyncSource = DS_MPGrabber_GetSyncSource;
+ This->vt->EnumPins = DS_MPGrabber_EnumPins;
+ This->vt->FindPin = DS_MPGrabber_FindPin;
+ This->vt->QueryFilterInfo = DS_MPGrabber_QueryFilterInfo;
+ This->vt->JoinFilterGraph = DS_MPGrabber_JoinFilterGraph;
+ This->vt->QueryVendorInfo = DS_MPGrabber_QueryVendorInfo;
+
+ This->interfaces[0] = IID_IUnknown;
+ This->interfaces[1] = IID_IBaseFilter;
+ This->interfaces[2] = IID_IPersist;
+
+ This->FillBuffer=DS_Grabber_FillBuffer;
+ return This;
+
+}
Index: loader/dshow/DS_MPGrabber.h
===================================================================
--- loader/dshow/DS_MPGrabber.h (revision 0)
+++ loader/dshow/DS_MPGrabber.h (revision 0)
@@ -0,0 +1,50 @@
+#ifndef _DS_MPGRABBER_H_
+#define _DS_MPGRABBER_H_
+
+#include "outputpin.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct _IAMFilterMiscFlags IAMFilterMiscFlags;
+typedef struct IAMFilterMiscFlags_vt {
+ INHERIT_IUNKNOWN();
+ HRESULT (*GetMiscFlags)(IAMFilterMiscFlags* This);
+} IAMFilterMiscFlags_vt;
+struct _IAMFilterMiscFlags {struct IAMFilterMiscFlags_vt* vt;};
+
+typedef struct _DS_MPGrabber DS_MPGrabber;
+struct _DS_MPGrabber
+{
+ IBaseFilter_vt *vt;
+ DECLARE_IUNKNOWN();
+
+ COutputPin* pinVideo;
+ IMemInputPin* mempinVideo;
+
+ COutputPin* pinAudio;
+ IMemInputPin* mempinAudio;
+
+ IFilterGraph* m_pGraph;
+ IReferenceClock* pClock;
+ IAMFilterMiscFlags* m_pFlags;
+ GUID interfaces[3];
+
+ FILTER_STATE state;
+ REFERENCE_TIME m_tStart;
+ WCHAR achName[128];
+ int STDCALL ( *FillBuffer)(DS_MPGrabber* This,char* buf,int len,int bVideo,long long* pPts);
+};
+
+
+DS_MPGrabber* DS_MPGrabberCreate(const AM_MEDIA_TYPE* video,const AM_MEDIA_TYPE* audio);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif //_DS_MPGRABBER_H_
+
+
Index: loader/dshow/guids.c
===================================================================
--- loader/dshow/guids.c (revision 21934)
+++ loader/dshow/guids.c (working copy)
@@ -1,5 +1,7 @@
#include "guids.h"
+
+
const GUID CLSID_DivxDecompressorCF={0x82CCd3E0, 0xF71A, 0x11D0,
{ 0x9f, 0xe5, 0x00, 0x60, 0x97, 0x78, 0xaa, 0xaa}};
const GUID IID_IDivxFilterInterface={0xd132ee97, 0x3e38, 0x4030,
@@ -9,6 +11,8 @@
{0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
const GUID IID_IBaseFilter={0x56a86895, 0x0ad4, 0x11ce,
{0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}};
+const GUID IID_IPin = {0x56a86891, 0x0ad4, 0x11ce,
+ {0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}};
const GUID IID_IEnumPins={0x56a86892, 0x0ad4, 0x11ce,
{0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}};
const GUID IID_IEnumMediaTypes={0x89c31040, 0x846b, 0x11ce,
Index: loader/dshow/guids.h
===================================================================
--- loader/dshow/guids.h (revision 21934)
+++ loader/dshow/guids.h (working copy)
@@ -44,6 +44,7 @@
typedef GUID IID;
extern const GUID IID_IBaseFilter;
+extern const GUID IID_IPin;
extern const GUID IID_IEnumPins;
extern const GUID IID_IEnumMediaTypes;
extern const GUID IID_IMemInputPin;
@@ -82,4 +83,5 @@
extern const GUID MEDIATYPE_Audio;
extern const GUID MEDIASUBTYPE_PCM;
+
#endif /* DS_GUIDS_H */
Index: loader/dshow/inputpin.c
===================================================================
--- loader/dshow/inputpin.c (revision 21934)
+++ loader/dshow/inputpin.c (working copy)
@@ -20,16 +20,6 @@
* EnumPins
***********/
-typedef struct
-{
- IEnumPins_vt* vt;
- DECLARE_IUNKNOWN();
- IPin* pin1;
- IPin* pin2;
- int counter;
- GUID interfaces[2];
-} CEnumPins;
-
static long STDCALL CEnumPins_Next(IEnumPins* This,
/* [in] */ unsigned long cMediaTypes,
/* [size_is][out] */ IPin** ppMediaTypes,
@@ -97,13 +87,17 @@
static void CEnumPins_Destroy(CEnumPins* This)
{
+ if(This->pin1)
+ This->pin1->vt->Release((IUnknown*)This->pin1);
+ if(This->pin2)
+ This->pin2->vt->Release((IUnknown*)This->pin2);
free(This->vt);
free(This);
}
IMPLEMENT_IUNKNOWN(CEnumPins)
-static CEnumPins* CEnumPinsCreate(IPin* p, IPin* pp)
+CEnumPins* CEnumPinsCreate(IPin* p, IPin* pp)
{
CEnumPins* This = (CEnumPins*) malloc(sizeof(CEnumPins));
@@ -113,6 +107,10 @@
This->refcount = 1;
This->pin1 = p;
This->pin2 = pp;
+ if(p)
+ p->vt->AddRef((IUnknown*)p);
+ if(pp)
+ pp->vt->AddRef((IUnknown*)pp);
This->counter = 0;
This->vt = (IEnumPins_vt*) malloc(sizeof(IEnumPins_vt));
@@ -346,8 +344,7 @@
static long STDCALL CBaseFilter_GetState(IBaseFilter* This,
/* [in] */ unsigned long dwMilliSecsTimeout,
- // /* [out] */ FILTER_STATE *State)
- void* State)
+ /* [out] */ FILTER_STATE *State)
{
Debug unimplemented("CBaseFilter_GetState", This);
return E_NOTIMPL;
@@ -385,8 +382,7 @@
}
static long STDCALL CBaseFilter_QueryFilterInfo(IBaseFilter* This,
- // /* [out] */ FILTER_INFO *pInfo)
- void* pInfo)
+ /* [out] */ FILTER_INFO *pInfo)
{
Debug unimplemented("CBaseFilter_QueryFilterInfo", This);
return E_NOTIMPL;
@@ -507,8 +503,7 @@
static long STDCALL CBaseFilter2_GetState(IBaseFilter* This,
/* [in] */ unsigned long dwMilliSecsTimeout,
- // /* [out] */ FILTER_STATE *State)
- void* State)
+ /* [out] */ FILTER_STATE *State)
{
Debug unimplemented("CBaseFilter2_GetState", This);
return E_NOTIMPL;
@@ -545,8 +540,7 @@
}
static long STDCALL CBaseFilter2_QueryFilterInfo(IBaseFilter* This,
- // /* [out] */ FILTER_INFO *pInfo)
- void* pInfo)
+ /* [out] */ FILTER_INFO *pInfo)
{
Debug unimplemented("CBaseFilter2_QueryFilterInfo", This);
return E_NOTIMPL;
Index: loader/dshow/inputpin.h
===================================================================
--- loader/dshow/inputpin.h (revision 21934)
+++ loader/dshow/inputpin.h (working copy)
@@ -67,4 +67,16 @@
CRemotePin2* CRemotePin2Create(CBaseFilter2* parent);
+typedef struct
+{
+ IEnumPins_vt* vt;
+ DECLARE_IUNKNOWN();
+ IPin* pin1;
+ IPin* pin2;
+ int counter;
+ GUID interfaces[2];
+} CEnumPins;
+
+CEnumPins* CEnumPinsCreate(IPin* p, IPin* pp);
+
#endif /* DS_INPUTPIN_H */
Index: loader/dshow/interfaces.h
===================================================================
--- loader/dshow/interfaces.h (revision 21934)
+++ loader/dshow/interfaces.h (working copy)
@@ -16,6 +16,7 @@
typedef struct _IFilterGraph IFilterGraph;
typedef struct _IBaseFilter IBaseFilter;
+typedef struct _IEnumFilters IEnumFilters;
typedef enum
{
@@ -38,6 +39,17 @@
long cbPrefix;
} ALLOCATOR_PROPERTIES;
+typedef enum _FilterState {
+ State_Stopped = 0,
+ State_Paused = 1,
+ State_Running = 2
+} FILTER_STATE;
+
+typedef struct _FilterInfo {
+ WCHAR achName[128];
+ IFilterGraph *pGraph;
+} FILTER_INFO;
+
typedef struct _IEnumMediaTypes IEnumMediaTypes;
typedef struct IEnumMediaTypes_vt
{
@@ -191,8 +203,7 @@
REFERENCE_TIME tStart);
HRESULT STDCALL ( *GetState )(IBaseFilter * This,
/* [in] */ unsigned long dwMilliSecsTimeout,
- ///* [out] */ FILTER_STATE *State);
- void* State);
+ /* [out] */ FILTER_STATE *State);
HRESULT STDCALL ( *SetSyncSource )(IBaseFilter* This,
/* [in] */ IReferenceClock *pClock);
HRESULT STDCALL ( *GetSyncSource )(IBaseFilter* This,
@@ -203,8 +214,7 @@
/* [string][in] */ const unsigned short* Id,
/* [out] */ IPin** ppPin);
HRESULT STDCALL ( *QueryFilterInfo )(IBaseFilter* This,
- // /* [out] */ FILTER_INFO *pInfo);
- void* pInfo);
+ /* [out] */ FILTER_INFO *pInfo);
HRESULT STDCALL ( *JoinFilterGraph )(IBaseFilter* This,
/* [in] */ IFilterGraph* pGraph,
/* [string][in] */ const unsigned short* pName);
@@ -329,4 +339,34 @@
HRESULT STDCALL ( *get_AspectRatio )(IDivxFilterInterface* This, int* x, IDivxFilterInterface* Thisit, int* y);
};
+typedef struct IFilterGraph_vt
+{
+ INHERIT_IUNKNOWN();
+
+ HRESULT STDCALL (*AddFilter)(IFilterGraph* This,IBaseFilter* pFilter,LPCWSTR pName);
+ HRESULT STDCALL (*RemoveFilter)(IFilterGraph* This,IBaseFilter* pFilter);
+ HRESULT STDCALL (*EnumFilters)(IFilterGraph* This,IEnumFilters** ppEnum);
+ HRESULT STDCALL (*FindFilterByName)(IFilterGraph* This,LPCWSTR pName,IBaseFilter** ppFilter);
+ HRESULT STDCALL (*ConnectDirect)(IFilterGraph* This,IPin* ppinOut,IPin* ppinIn,const AM_MEDIA_TYPE* pmt);
+ HRESULT STDCALL (*Reconnect)(IFilterGraph* This,IPin* ppin);
+ HRESULT STDCALL (*Disconnect)(IFilterGraph* This,IPin* ppin);
+ HRESULT STDCALL (*SetDefaultSyncSource)(IFilterGraph* This);
+} IFilterGraph_vt;
+struct _IFilterGraph {struct IFilterGraph_vt* vt;};
+
+typedef DWORD_PTR HSEMAPHORE;
+typedef DWORD_PTR HEVENT;
+
+typedef struct IReferenceClock_vt {
+ INHERIT_IUNKNOWN();
+
+ HRESULT STDCALL ( *GetTime)(IReferenceClock* This,REFERENCE_TIME* pTime);
+ HRESULT STDCALL ( *AdviseTime)(IReferenceClock* This,REFERENCE_TIME baseTime,REFERENCE_TIME streamTime,HEVENT hEvent,DWORD_PTR* pdwAdviseCookie);
+ HRESULT STDCALL ( *AdvisePeriodic)(IReferenceClock* This,REFERENCE_TIME startTime,REFERENCE_TIME periodTime,HSEMAPHORE hSemaphore,DWORD_PTR* pdwAdviseCookie);
+ HRESULT STDCALL ( *Unadvise)(IReferenceClock* This,DWORD_PTR dwAdviseCookie);
+} IReferenceClock_vt;
+struct _IReferenceClock {struct IReferenceClock_vt* vt;};
+
+
+
#endif /* DS_INTERFACES_H */
Index: loader/dshow/outputpin.c
===================================================================
--- loader/dshow/outputpin.c (revision 21934)
+++ loader/dshow/outputpin.c (working copy)
@@ -11,41 +11,326 @@
#include <string.h>
#include <stdlib.h>
-/*
- An object beyond interface IEnumMediaTypes.
- Returned by COutputPin through call IPin::EnumMediaTypes().
-*/
+
+#ifndef NOAVIFILE_HEADERS
+#include "audiodecoder.h"
+#include "except.h"
+#else
+#include "libwin32.h"
+#endif
+
+#define VFW_E_NOT_CONNECTED ((HRESULT)0x80040209L)
+#define VFW_E_TYPE_NOT_ACCEPTED ((HRESULT)0x8004022AL)
+
+
+#include "DS_Filter.h"
+
+#define Debug if(0)
+
+// FIXME: I don't sure that it is proper place for AM_MEDIA_TYPE related routines
+
+/**
+ * \brief print info from AM_MEDIA_TYPE structure
+ * \param[in] pmt pointer to AM_MEDIA_TYPE
+ *
+ * routine used for debug purposes
+ *
+ */
+void print_pmt_info(const AM_MEDIA_TYPE* pmt){
+ WAVEFORMATEX* pWF;
+ VIDEOINFOHEADER* Vhdr;
+ int i;
+ GUID* iid;
+
+
+ printf("=======================\n");
+ printf("AM_MEDIA_TYPE:\n");
+ printf("-(Ptr:%p)--------\n",pmt);
+ for(i=0;i<sizeof(AM_MEDIA_TYPE);i++){
+ printf("%02x ",(BYTE)((BYTE*)pmt)[i]);
+ if((i+1)%8==0) printf("\n");
+ }
+ if((i)%8!=0) printf("\n");
+ printf("-(Ptr:%p)--(%02d)--\n",pmt->pbFormat,pmt->cbFormat);
+ for(i=0;i<pmt->cbFormat;i++){
+ printf("%02x ",(BYTE)pmt->pbFormat[i]);
+ if((i+1)%8==0) printf("\n");
+ }
+ if((i)%8!=0) printf("\n");
+ printf("-----------------------\n");
+ iid=&(pmt->subtype);
+ printf("Subtype: %08x-%04x-%04x-%02x%02x-"
+ "%02x%02x%02x%02x%02x%02x\n",
+ iid->f1, iid->f2, iid->f3,
+ (unsigned char)iid->f4[1], (unsigned char)iid->f4[0],
+ (unsigned char)iid->f4[2], (unsigned char)iid->f4[3],
+ (unsigned char)iid->f4[4], (unsigned char)iid->f4[5],
+ (unsigned char)iid->f4[6], (unsigned char)iid->f4[7]);
+
+ iid=&(pmt->formattype);
+ printf("Format type: %08x-%04x-%04x-%02x%02x-"
+ "%02x%02x%02x%02x%02x%02x\n",
+ iid->f1, iid->f2, iid->f3,
+ (unsigned char)iid->f4[1], (unsigned char)iid->f4[0],
+ (unsigned char)iid->f4[2], (unsigned char)iid->f4[3],
+ (unsigned char)iid->f4[4], (unsigned char)iid->f4[5],
+ (unsigned char)iid->f4[6], (unsigned char)iid->f4[7]);
+ if(pmt && memcmp(&pmt->formattype,&FORMAT_WaveFormatEx,16)==0 && pmt->pbFormat){
+ pWF=(WAVEFORMATEX*)pmt->pbFormat;
+ printf("PMT: nChannels %d\n",pWF->nChannels);
+ printf("PMT: nSamplesPerSec %d\n",pWF->nSamplesPerSec);
+ printf("PMT: wBitsPerSample %d\n",pWF->wBitsPerSample);
+ printf("PMT: nBlockAlign %d\n",pWF->nBlockAlign);
+ printf("PMT: nAvgBytesPerSec %d\n",pWF->nAvgBytesPerSec);
+ printf("PMT: SampleSize %ld\n",pmt->lSampleSize);
+ }
+ if(pmt && memcmp(&pmt->formattype,&FORMAT_VideoInfo,16)==0 && pmt->pbFormat){
+ Vhdr=(VIDEOINFOHEADER*)pmt->pbFormat;
+ printf("Vhdr: dwBitRate %ld\n",Vhdr->dwBitRate);
+ printf("Vhdr: biWidth %ld\n",Vhdr->bmiHeader.biWidth);
+ printf("Vhdr: biHeight %d\n",Vhdr->bmiHeader.biHeight);
+ printf("Vhdr: biBitCount %d\n",Vhdr->bmiHeader.biBitCount);
+ if(Vhdr->bmiHeader.biCompression){
+ printf("Vhdr: biComression 0x%08x (%s)\n",Vhdr->bmiHeader.biCompression,vo_format_name(Vhdr->bmiHeader.biCompression));
+ }else
+ printf("Vhdr: biComression 0x00000000\n");
+
+ }
+ printf("=======================\n");
+}
+
+/**
+ * \brief frees memory, pointed by pbFormat and pUnk members of AM_MEDIA_TYPE structure
+ *
+ * \param[in] pmt pointer to structure
+ *
+ * \note
+ * routine does not frees memory allocated for AM_MEDIA_TYPE, so given pointer will be
+ * valid after this routine call.
+ *
+ */
+void free_media_type(AM_MEDIA_TYPE* pmt){
+ if(!pmt) return;
+ if (pmt->cbFormat){
+ CoTaskMemFree(pmt->pbFormat);
+ pmt->pbFormat=NULL;
+ pmt->cbFormat=0;
+ }
+ if (pmt->pUnk){
+ pmt->pUnk->vt->Release(pmt->pUnk);
+ pmt->pUnk=NULL;
+ }
+}
+
+/**
+ * \brief frees memory allocated for AM_MEDIA_TYPE structure, including pbFormat and pUnk
+ * members
+ *
+ * \param[in] pmt pointer to structure
+ *
+ * \note
+ * after call to this routine, pointer to AM_MEDIA_TYPE will not be valid anymore
+ *
+ */
+void delete_media_type(AM_MEDIA_TYPE* pmt){
+ if(!pmt) return;
+ free_media_type(pmt);
+ CoTaskMemFree(pmt);
+}
+
+/**
+ * \brief copyies info from source to destination AM_MEDIA_TYPE structures
+ *
+ * \param[in] pSrc pointer to AM_MEDIA_TYPE structure to copy data from
+ * \param[out] pDst pointer to AM_MEDIA_TYPE structure to copy data to
+ *
+ * \return S_OK - success
+ * \return E_POINTER - pSrc or pDst is NULL or (pSrc->cbFormat && !pSrc->pbFormat)
+ * \return E_INVALIDARG - (pSrc == pDst)
+ * \return E_OUTOFMEMORY - Insufficient memory
+ *
+ * \note
+ * - pDst must point to existing AM_MEDIA_TYPE structure (all data will be overwritten)
+ * - if pDst->pbFormat!=NULL this will cause memory leak (as described in Directshow SDK)!
+ *
+ */
+HRESULT copy_media_type(AM_MEDIA_TYPE* pDst,const AM_MEDIA_TYPE* pSrc){
+ Debug printf("%s(%p) called\n", "copy_media_type",pSrc);
+
+ if(!pSrc || !pDst) return E_POINTER;
+
+ if(pSrc == pDst) return E_INVALIDARG;
+
+ if(!pSrc->pbFormat && pSrc->cbFormat) return E_POINTER;
+
+ *pDst=*pSrc;
+
+ if(pDst->cbFormat>0){
+ pDst->pbFormat=CoTaskMemAlloc(pDst->cbFormat);
+ if(!pDst->pbFormat){
+ pDst->pbFormat=NULL;
+ pDst->cbFormat=0;
+ return E_OUTOFMEMORY;
+ }else
+ memcpy(pDst->pbFormat,pSrc->pbFormat,pDst->cbFormat);
+ }
+#if 0
+ //for debug purpose
+ printf ("================CopyMEdiaTyoe: Source===========\n");
+ print_pmt_info(pSrc);
+ printf ("================CopyMEdiaTyoe: Dest===========\n");
+ print_pmt_info(pDst);
+#endif
+ return S_OK;
+}
+
+/**
+ * \brief allocates new AM_MEDIA_TYPE structure and fills it with info from given one
+ *
+ * \param[in] pSrc pointer to AM_MEDIA_TYPE structure to copy data from
+ *
+ * \return result code, returned from copy_media_type
+ *
+ */
+AM_MEDIA_TYPE* create_media_type(const AM_MEDIA_TYPE* pSrc){
+ HRESULT hr;
+ AM_MEDIA_TYPE* pDst=CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
+ if (!pDst) return NULL;
+ hr=copy_media_type(pDst,pSrc);
+ if(!FAILED(hr)) return pDst;
+ //Copy failed
+ CoTaskMemFree(pDst);
+ return NULL;
+}
+
static inline int output_unimplemented(const char* s, void* p)
{
Debug printf("%s(%p) called (UNIMPLEMENTED)", s, p);
return E_NOTIMPL;
}
+/**
+ An object beyond interface IEnumMediaTypes.
+ Returned by COutputPin through call IPin::EnumMediaTypes().
+*/
typedef struct CEnumMediaTypes
{
IEnumMediaTypes_vt* vt;
DECLARE_IUNKNOWN();
AM_MEDIA_TYPE type;
GUID interfaces[2];
+ int counter;
} CEnumMediaTypes;
+/**
+ IMemOutput interface implementation
+*/
struct _COutputMemPin
{
IMemInputPin_vt* vt;
DECLARE_IUNKNOWN();
char** frame_pointer;
long* frame_size_pointer;
- MemAllocator* pAllocator;
+ /*
+ FIXME:
+ Previous version uses MemAllocator pointer. For calling from DS_Filter or DS_*Decoder
+ this does not matter, because pAllocator can contain only MemAllocator object.
+ But when using inside DirectShow graph this pointer would contain pointer to
+ IMemAllocator interface, provided by DirectShow graph.
+ */
+ IMemAllocator* pAllocator;
COutputPin* parent;
+
+#if defined(HAVE_TV_DSHOW)
+ CRITICAL_SECTION crit;
+ char** ringbuffer;
+ long long* pts;
+ long long* sampleno;
+
+ int buffersize;
+ int blocksize;
+ int head;
+ int tail;
+ int count;
+#endif
};
+#if defined(HAVE_TV_DSHOW)
+/**
+ * \brief free COutputMemPin's internal ringbuffer
+ *
+ * \param[in] this pointer to COutputPin object
+ *
+ */
+void free_ringbuffer(COutputMemPin* this){
+ int i;
+ Debug printf("free_ringbuffer(%p) called\n", this);
+ if(!this->ringbuffer) return;
+
+ for(i=0;i<this->buffersize;i++) free(this->ringbuffer[i]);
+ free(this->ringbuffer);
+ if (this->pts) free(this->pts);
+ if (this->sampleno) free(this->sampleno);
+
+ this->ringbuffer=NULL;
+ this->sampleno=NULL;
+ this->pts=NULL;
+
+ this->head=0;
+ this->tail=0;
+ this->count=0;
+ DeleteCriticalSection(&(this->crit));
+}
+
+/**
+ * \brief initialize COutputMemPin's internal buffer
+ *
+ * \param[in] this COutputMemPin object
+ * \param[in] ringbuffer size in blocks
+ * \param[in] blocksize in bytes
+ *
+ * \return currently only 1
+ *
+ */
+int init_ringbuffer(COutputMemPin*this,int buffersize,int blocksize){
+ int i;
+ Debug printf("init_ringbuffer(%p) called\n", this);
+ if(this->ringbuffer) free_ringbuffer(this);
+
+ this->buffersize=buffersize<50?50:buffersize;
+ this->blocksize=blocksize;
+
+ this->ringbuffer=(char**)malloc(this->buffersize*sizeof(char*));
+ for (i=0;i<this->buffersize;i++) this->ringbuffer[i]=(char*)malloc(this->blocksize*sizeof(char));
+ this->pts=(long long*)malloc(this->buffersize*sizeof(long long));
+ this->sampleno=(long long*)malloc(this->buffersize*sizeof(long long));
+ this->head=0;
+ this->tail=0;
+ this->count=0;
+ InitializeCriticalSection(&(this->crit));
+ return 1;
+}
+#endif
+
+/**
+ * \brief IEnumMediaTypes:Next (skips over a specified number of media types)
+ *
+ * \param[in] This pointer to CEnumMEdiaTypes object
+ * \param[in] cMediaTypes number of media types to skip
+ *
+ * \return S_OK - success
+ * \return S_FALSE - success
+ * \return VFW_E_ENUM_OUT_OF_SYNC - pin's state has changed and is now inconsistent with enumerator
+ *
+ */
static HRESULT STDCALL CEnumMediaTypes_Next(IEnumMediaTypes * This,
/* [in] */ ULONG cMediaTypes,
/* [size_is][out] */ AM_MEDIA_TYPE **ppMediaTypes,
/* [out] */ ULONG *pcFetched)
{
- AM_MEDIA_TYPE* type = &((CEnumMediaTypes*)This)->type;
+ CEnumMediaTypes* this=(CEnumMediaTypes*)This;
+
Debug printf("CEnumMediaTypes::Next(%p) called\n", This);
if (!ppMediaTypes)
return E_INVALIDARG;
@@ -54,34 +339,70 @@
if (cMediaTypes <= 0)
return 0;
+ if(this->counter==1){
+ if (pcFetched)
+ *pcFetched=0;
+ return S_FALSE;
+ }
if (pcFetched)
*pcFetched=1;
- ppMediaTypes[0] = malloc(sizeof(AM_MEDIA_TYPE));
- // copy structures - C can handle this...
- **ppMediaTypes = *type;
- if (ppMediaTypes[0]->pbFormat)
- {
- ppMediaTypes[0]->pbFormat=malloc(ppMediaTypes[0]->cbFormat);
- memcpy(ppMediaTypes[0]->pbFormat, type->pbFormat, ppMediaTypes[0]->cbFormat);
- }
+ ppMediaTypes[0] = create_media_type(&(this->type));
+ if(!ppMediaTypes[0]) return E_OUTOFMEMORY;
+
+ this->counter++;
+
if (cMediaTypes == 1)
return 0;
return 1;
}
/* I expect that these methods are unused. */
+
+/**
+ * \brief IEnumMediaTypes::Reset (skips over a specified number of media types)
+ *
+ * \param[in] This pointer to CEnumMEdiaTypes object
+ * \param[in] cMediaTypes number of media types to skip
+ *
+ * \return S_OK - success
+ * \return S_FALSE - success
+ * \return VFW_E_ENUM_OUT_OF_SYNC - pin's state has changed and is now inconsistent with enumerator
+ *
+ */
static HRESULT STDCALL CEnumMediaTypes_Skip(IEnumMediaTypes * This,
/* [in] */ ULONG cMediaTypes)
{
return output_unimplemented("CEnumMediaTypes::Skip", This);
}
+/**
+ * \brief IEnumMediaTypes::Reset (resets enumeration sequence to beginning)
+ *
+ * \param[in] This pointer to CEnumMEdiaTypes object
+ *
+ * \return S_OK - success
+ *
+ */
static HRESULT STDCALL CEnumMediaTypes_Reset(IEnumMediaTypes * This)
{
Debug printf("CEnumMediaTypes::Reset(%p) called\n", This);
+ ((CEnumMediaTypes*)This)->counter = 0;
return 0;
}
+/**
+ * \brief IEnumMediaTypes::Clone (makes a copy of enumerator, returned object
+ * starts at the same position as original)
+ *
+ * \param[in] This pointer to CEnumMEdiaTypes object
+ * \param[out] ppEnum address of variable that receives pointer to IEnumMediaTypes interface
+ *
+ * \return S_OK - success
+ * \return E_OUTOFMEMRY - Insufficient memory
+ * \return E_POINTER - Null pointer
+ * \return VFW_E_ENUM_OUT_OF_SYNC - pin's state has changed and is now inconsistent with enumerator
+ *
+ */
static HRESULT STDCALL CEnumMediaTypes_Clone(IEnumMediaTypes * This,
/* [out] */ IEnumMediaTypes **ppEnum)
{
@@ -89,13 +410,20 @@
return E_NOTIMPL;
}
+/**
+ * \brief CEnumMediaTypes destructor
+ *
+ * \param[in] This pointer to CEnumMediaTypes object
+ *
+ */
static void CEnumMediaTypes_Destroy(CEnumMediaTypes* This)
{
+ free_media_type(&(This->type));
free(This->vt);
free(This);
}
-// IPin->IUnknown methods
+// IEnumMediaTypes->IUnknown methods
IMPLEMENT_IUNKNOWN(CEnumMediaTypes)
static CEnumMediaTypes* CEnumMediaTypesCreate(const AM_MEDIA_TYPE* amt)
@@ -113,7 +441,7 @@
}
This->refcount = 1;
- This->type = *amt;
+ copy_media_type(&(This->type),amt);
This->vt->QueryInterface = CEnumMediaTypes_QueryInterface;
This->vt->AddRef = CEnumMediaTypes_AddRef;
@@ -132,9 +460,27 @@
/*************
* COutputPin
+ *
+ * \warning
+ * This is implementation of INPUT pin in DirectShow's terms
+ *
*************/
+/**
+ *
+ * \brief IUnknown::QueryInterface (query object for interface)
+ * \param[in] This pointer to IUnknown interface
+ * \param[in] iid GUID of requested interface
+ * \param[out] ppv receives pointer to interface
+ *
+ * \return S_OK - success (and *ppv contains valid pointer)
+ * \return E_NOINTERFACE - interface not found (and *ppv was set NULL)
+ *
+ * \note
+ * Make sure to call Release on received interface when you are done
+ *
+ */
static HRESULT STDCALL COutputPin_QueryInterface(IUnknown* This, const GUID* iid, void** ppv)
{
COutputPin* p = (COutputPin*) This;
@@ -149,6 +495,12 @@
p->vt->AddRef(This);
return 0;
}
+ if (memcmp(iid, &IID_IPin, 16) == 0)
+ {
+ *ppv = p;
+ p->vt->AddRef(This);
+ return 0;
+ }
if (memcmp(iid, &IID_IMemInputPin, 16) == 0)
{
*ppv = p->mempin;
@@ -167,11 +519,26 @@
}
// IPin methods
+
+/**
+ * \brief IPin::Connect (connects pin to another pin)
+ *
+ * \param[in] This pointer to IPin interface
+ * \param[in] pReceivePin pointer to IPin interface of remote pin
+ * \param[in] pmt suggested media type for link. Can be NULL (any media type)
+ *
+ * \return S_OK - success.
+ * \return VFW_E_ALREADY_CONNECTED - pin already connected
+ * \return VFW_E_NOT_STOPPED - filter is active
+ * \return VFW_E_TYPE_NOT_ACCEPT - type is not acceptable
+ * \return Apropriate error code otherwise.
+ *
+ */
static HRESULT STDCALL COutputPin_Connect(IPin * This,
/* [in] */ IPin *pReceivePin,
/* [in] */ /* const */ AM_MEDIA_TYPE *pmt)
{
- Debug printf("COutputPin_Connect() called\n");
+ Debug printf("COutputPin_Connect(%p) called\n",This);
/*
*pmt=((COutputPin*)This)->type;
if(pmt->cbFormat>0)
@@ -185,21 +552,81 @@
// if I put return 0; here, it crashes
}
+/**
+ * \brief IPin::ReceiveConnection (accepts a connection from another pin)
+ *
+ * \param[in] This pointer to IPin interface
+ * \param[in] pConnector connecting pin's IPin interface
+ * \param[in] pmt suggested media type for connection
+ *
+ * \return S_OK - success
+ * \return E_POINTER - Null pointer
+ * \return VFW_E_ALREADY_CONNECTED - pin already connected
+ * \return VFW_E_NOT_STOPPED - filter is active
+ * \return VFW_E_TYPE_NOT_ACCEPT - type is not acceptable
+ *
+ * \note
+ * When returning S_OK method should also do the following:
+ * - store media type and return the same type in IPin::ConnectionMediaType
+ * - store pConnector and return it in IPin::ConnectedTo
+ *
+ */
static HRESULT STDCALL COutputPin_ReceiveConnection(IPin * This,
/* [in] */ IPin *pConnector,
/* [in] */ const AM_MEDIA_TYPE *pmt)
{
+ COutputPin* this=(COutputPin*)This;
Debug printf("COutputPin_ReceiveConnection(%p) called\n", This);
- ((COutputPin*)This)->remote = pConnector;
+ Debug printf("Suggested format:\n");
+ Debug print_pmt_info(pmt);
+ if(This->vt->QueryAccept(This,pmt)!=S_OK){
+ Debug printf("ReceiveConnection: Rejecting format.\n");
+ return VFW_E_TYPE_NOT_ACCEPTED;
+ }else
+ Debug printf("ReceiveConnection: Accepting format.\n");
+ this->remote = pConnector;
+ pConnector->vt->AddRef((IUnknown*)pConnector);
+ free_media_type(&(this->type));
+ copy_media_type(&(this->type),pmt);
return 0;
}
+/**
+ * \brief IPin::Disconnect (accepts a connection from another pin)
+ *
+ * \param[in] This pointer to IPin interface
+ *
+ * \return S_OK - success
+ * \return S_FALSE - pin was not connected
+ * \return VFW_E_NOT_STOPPED - filter is active
+ *
+ * \note
+ * To break connection you have to also call Disconnect on other pin
+ */
static HRESULT STDCALL COutputPin_Disconnect(IPin * This)
{
+ COutputPin* this=(COutputPin*)This;
Debug printf("COutputPin_Disconnect(%p) called\n", This);
- return 1;
+ if(this->remote){
+ this->remote->vt->Release((IUnknown*)this->remote);
+ this->remote=NULL;
+ }
+ return S_OK;
}
+/**
+ * \brief IPin::ConnectedTo (retrieves pointer to the connected pin, if such exist)
+ *
+ * \param[in] This pointer to IPin interface
+ * \param[out] pPin pointer to remote pin's IPin interface
+ *
+ * \return S_OK - success
+ * \return E_POINTER - Null pointer
+ * \return VFW_E_NOT_CONNECTED - pin is not connected
+ *
+ * \note
+ * Caller must call Release on received IPin, when done
+ */
static HRESULT STDCALL COutputPin_ConnectedTo(IPin * This,
/* [out] */ IPin **pPin)
{
@@ -207,30 +634,69 @@
if (!pPin)
return E_INVALIDARG;
*pPin = ((COutputPin*)This)->remote;
+ if (!*pPin)
+ return VFW_E_NOT_CONNECTED;
+ (*pPin)->vt->AddRef((IUnknown*)*pPin);
+
return 0;
}
+/**
+ * \brief IPin::ConnectionMediaType (retrieves media type for connection, if such exist)
+ *
+ * \param[in] This pointer to IPin interface
+ * \param[out] pmt pointer to AM_MEDIA_TYPE, that receives connection media type
+ *
+ * \return S_OK - success
+ * \return E_POINTER - Null pointer
+ * \return VFW_E_NOT_CONNECTED - pin is not connected
+ *
+ */
static HRESULT STDCALL COutputPin_ConnectionMediaType(IPin * This,
/* [out] */ AM_MEDIA_TYPE *pmt)
{
- Debug printf("CInputPin::ConnectionMediaType() called\n");
+ Debug printf("COutputPin_ConnectionMediaType(%p) called\n",This);
if (!pmt)
return E_INVALIDARG;
- *pmt = ((COutputPin*)This)->type;
- if (pmt->cbFormat>0)
- {
- pmt->pbFormat=malloc(pmt->cbFormat);
- memcpy(pmt->pbFormat, ((COutputPin*)This)->type.pbFormat, pmt->cbFormat);
- }
- return 0;
+ return copy_media_type(pmt,(&((COutputPin*)This)->type));
}
+/**
+ * \brief IPin::QueryPinInfo (retrieves information about the pin)
+ *
+ * \param[in] This pointer to IPin interface
+ * \param[out] pInfo pointer to PIN_INFO structure, that receives pin info
+ *
+ * \return S_OK - success
+ * \return E_POINTER - Null pointer
+ *
+ * \note
+ * If pInfo->pFilter is not NULL, then caller must call Release on pInfo->pFilter when done
+ *
+ */
static HRESULT STDCALL COutputPin_QueryPinInfo(IPin * This,
/* [out] */ PIN_INFO *pInfo)
{
- return output_unimplemented("COutputPin_QueryPinInfo", This);
+ Debug printf("COutputPin_QueryPinInfo(%p) called\n", This);
+ if(!pInfo)
+ return E_INVALIDARG;
+ pInfo->pFilter=((COutputPin*)This)->pFilter;
+ pInfo->pFilter->vt->AddRef((IUnknown*)pInfo->pFilter);
+ pInfo->dir=PINDIR_INPUT;
+ memset(pInfo->achName,0,128); //FIXME need some name
+ return 0;
}
+/**
+ * \brief IPin::QueryDirection (retrieves pin direction)
+ *
+ * \param[in] This pointer to IPin interface
+ * \param[out] pPinDir pointer to variable, that receives pin direction (PINDIR_INPUT,PINDIR_OUTPUT)
+ *
+ * \return S_OK - success
+ * \return E_POINTER - Null pointer
+ *
+ */
static HRESULT STDCALL COutputPin_QueryDirection(IPin * This,
/* [out] */ PIN_DIRECTION *pPinDir)
{
@@ -241,28 +707,104 @@
return 0;
}
+/**
+ * \brief IPin::QueryId (retrieves pin identificator)
+ *
+ * \param[in] This pointer to IPin interface
+ * \param[out] Id adress of variable, that receives string with pin's Id.
+ *
+ * \return S_OK - success
+ * \return E_OUTOFMEMORY - Insufficient memory
+ * \return E_POINTER - Null pointer
+ *
+ * \note
+ * Pin's Id is not the same as pin's name
+ *
+ */
static HRESULT STDCALL COutputPin_QueryId(IPin * This,
/* [out] */ LPWSTR *Id)
{
return output_unimplemented("COutputPin_QueryId", This);
}
+/**
+ * \brief IPin::QueryAccept (determines can media type be accepted or not)
+ *
+ * \param[in] This pointer to IPin interface
+ * \param[in] pmt Media type to check
+ *
+ * \return S_OK - success
+ * \return S_FALSE - pin rejects media type
+ *
+ */
static HRESULT STDCALL COutputPin_QueryAccept(IPin * This,
/* [in] */ const AM_MEDIA_TYPE *pmt)
{
- return output_unimplemented("COutputPin_QueryAccept", This);
+ COutputPin* this=(COutputPin*)This;
+ Debug printf("COutputPin_QueryAccept(%p)\n", This);
+ if(memcmp(&(this->type.majortype),&(pmt->majortype),16)!=0) {
+ Debug printf("COutputPin_QueryAccept majortype differs. Rejecting(%p)\n", This);
+ return S_FALSE;
+ }
+ if(memcmp(&(this->type.subtype),&(pmt->subtype),16)!=0) {
+ Debug printf("COutputPin_QueryAccept subtype differs. Rejecting(%p)\n", This);
+ return S_FALSE;
+ }
+ if(memcmp(&pmt->formattype,&FORMAT_WaveFormatEx,16)==0 && pmt->pbFormat){
+ WAVEFORMATEX* pWF=(WAVEFORMATEX*)pmt->pbFormat;
+ if(pWF->nChannels==0) {
+ Debug printf("COutputPin_QueryAccept nChannels=0. Rejecting(%p)\n", This);
+ return S_FALSE;
+ }
+ }
+ if(memcmp(&pmt->formattype,&FORMAT_VideoInfo,16)==0 && pmt->pbFormat){
+ VIDEOINFOHEADER* Vhdr=(VIDEOINFOHEADER*)pmt->pbFormat;
+ if(Vhdr->bmiHeader.biWidth==0 && Vhdr->bmiHeader.biHeight==0) {
+ Debug printf("COutputPin_QueryAccept picture 0x0. Rejecting(%p)\n", This);
+ return S_FALSE;
+ }
+ }
+ Debug printf("COutputPin_QueryAccept(%p) Accepting following format\n", This);
+ Debug print_pmt_info(pmt);
+ return S_OK;
}
+/**
+ * \brief IPin::EnumMediaTypes (enumerates the pin's preferred media types)
+ *
+ * \param[in] This pointer to IPin interface
+ * \param[out] ppEnum adress of variable that receives pointer to IEnumMEdiaTypes interface
+ *
+ * \return S_OK - success
+ * \return E_OUTOFMEMORY - Insufficient memory
+ * \return E_POINTER - Null pointer
+ *
+ * \note
+ * Caller must call Release on received interface when done
+ *
+ */
static HRESULT STDCALL COutputPin_EnumMediaTypes(IPin * This,
/* [out] */ IEnumMediaTypes **ppEnum)
{
- Debug printf("COutputPin_EnumMediaTypes() called\n");
+ Debug printf("COutputPin_EnumMediaTypes(%p) called\n",This);
if (!ppEnum)
return E_INVALIDARG;
*ppEnum = (IEnumMediaTypes*) CEnumMediaTypesCreate(&((COutputPin*)This)->type);
return 0;
}
+/**
+ * \brief IPin::QueryInternalConnections (retries pin's internal connections)
+ *
+ * \param[in] This pointer to IPin interface
+ * \param[out] apPin Array that receives pins, internally connected to this
+ * \param[in,out] nPint Size of an array
+ *
+ * \return S_OK - success
+ * \return S_FALSE - pin rejects media type
+ * \return E_NOTIMPL - not implemented
+ *
+ */
static HRESULT STDCALL COutputPin_QueryInternalConnections(IPin * This,
/* [out] */ IPin **apPin,
/* [out][in] */ ULONG *nPin)
@@ -270,21 +812,68 @@
return output_unimplemented("COutputPin_QueryInternalConnections", This);
}
+/**
+ * \brief IPin::EndOfStream (notifies pin, that no data is expected, until new run command)
+ *
+ * \param[in] This pointer to IPin interface
+ *
+ * \return S_OK - success
+ * \return E_UNEXPECTED - The pin is output pin
+ *
+ * \note
+ * IMemoryInputPin::Receive,IMemoryInputPin::ReceiveMultiple, IMemoryInputPin::EndOfStream,
+ * IMemAllocator::GetBuffer runs in different (streaming) thread then other
+ * methods (application thread).
+ * IMemoryInputPin::NewSegment runs either in streaming or application thread.
+ * Developer must use critical sections for thread-safing work.
+ *
+ */
static HRESULT STDCALL COutputPin_EndOfStream(IPin * This)
{
return output_unimplemented("COutputPin_EndOfStream", This);
}
+/**
+ * \brief IPin::BeginFlush (begins a flush operation)
+ *
+ * \param[in] This pointer to IPin interface
+ *
+ * \return S_OK - success
+ * \return E_UNEXPECTED - The pin is output pin
+ *
+ */
static HRESULT STDCALL COutputPin_BeginFlush(IPin * This)
{
return output_unimplemented("COutputPin_BeginFlush", This);
}
+/**
+ * \brief IPin::EndFlush (ends a flush operation)
+ *
+ * \param[in] This pointer to IPin interface
+ *
+ * \return S_OK - success
+ * \return E_UNEXPECTED - The pin is output pin
+ *
+ */
static HRESULT STDCALL COutputPin_EndFlush(IPin * This)
{
return output_unimplemented("COutputPin_EndFlush", This);
}
+/**
+ * \brief IPin::NewSegment (media sample received after this call grouped as segment with common
+ * start,stop time and rate)
+ *
+ * \param[in] This pointer to IPin interface
+ * \param[in] tStart start time of new segment
+ * \param[in] tStop end time of new segment
+ * \param[in] dRate rate at wich segment should be processed
+ *
+ * \return S_OK - success
+ * \return E_UNEXPECTED - The pin is output pin
+ *
+ */
static HRESULT STDCALL COutputPin_NewSegment(IPin * This,
/* [in] */ REFERENCE_TIME tStart,
/* [in] */ REFERENCE_TIME tStop,
@@ -301,8 +890,14 @@
static HRESULT STDCALL COutputPin_M_QueryInterface(IUnknown* This, const GUID* iid, void** ppv)
{
- COutputPin* p = (COutputPin*)This;
+ COutputMemPin* p = (COutputMemPin*)This;
+ /* FIXME WARN:
+ original version does wrong type cast IMHO.
+ This is method of MemInputPin class, not COutputPin.
+ So in previous version returning pointer was containing garbage, but
+ i'm not sure. Need additional comparation.
+ */
Debug printf("COutputPin_M_QueryInterface(%p) called\n", This);
if (!ppv)
return E_INVALIDARG;
@@ -313,17 +908,16 @@
p->vt->AddRef(This);
return 0;
}
- /*if(!memcmp(iid, &IID_IPin, 16))
+ if(!memcmp(iid, &IID_IPin, 16))
{
- COutputPin* ptr=(COutputPin*)(This-1);
- *ppv=(void*)ptr;
- AddRef((IUnknown*)ptr);
+ *ppv = p->parent; //COutputPin
+ p->parent->vt->AddRef((IUnknown*)p->parent);
return 0;
- }*/
+ }
if(!memcmp(iid, &IID_IMemInputPin, 16))
{
- *ppv = p->mempin;
- p->mempin->vt->AddRef(This);
+ *ppv = This;
+ This->vt->AddRef(This);
return 0;
}
Debug printf("Unknown interface : %08x-%04x-%04x-%02x%02x-" \
@@ -338,37 +932,189 @@
// IMemInputPin methods
-static HRESULT STDCALL COutputPin_GetAllocator(IMemInputPin* This,
+
+/**
+ * \brief IMemInputPin::GetAllocator (retrives memory allocator, proposed by pin)
+ *
+ * \param[in] This pointer to IMemInputPin interface
+ * \param[out] ppAllocator address of variable that receives allocator's IMemAllocator interface
+ *
+ * \return S_OK - success
+ * \return VFW_E_NO_ALLOCATOR - No allocator
+ *
+ * \note
+ * Make sure to call Release on received interface when you are done
+ *
+ */
+static HRESULT STDCALL COutputMemPin_GetAllocator(IMemInputPin* This,
/* [out] */ IMemAllocator** ppAllocator)
{
- Debug printf("COutputPin_GetAllocator(%p, %p) called\n", This->vt, ppAllocator);
- *ppAllocator = (IMemAllocator*) MemAllocatorCreate();
+ COutputMemPin* this=(COutputMemPin*)This;
+ Debug printf("COutputMemPin_GetAllocator(%p, %p) called\n", this, this->pAllocator);
+ // DirectShow provide us it's own allocator later, if necessery
+ if(!this->pAllocator) this->pAllocator=(IMemAllocator*)MemAllocatorCreate();
+ this->pAllocator->vt->AddRef((IUnknown*)this->pAllocator);
+ *ppAllocator = this->pAllocator;
return 0;
}
-static HRESULT STDCALL COutputPin_NotifyAllocator(IMemInputPin* This,
+/**
+ *
+ * \brief IMemInputPin::NotifyAllocator (specifies an allocator for the connection)
+ *
+ * \param[in] This pointer to IMemInputPin interface
+ * \param[in] pAllocator allocator's IMemAllocator interface
+ * \param[in] bReadOnly specifies whether samples from allocator are readonly
+ *
+ * \return S_OK - success
+ * \return Apropriate error code otherwise
+ *
+ */
+static HRESULT STDCALL COutputMemPin_NotifyAllocator(IMemInputPin* This,
/* [in] */ IMemAllocator* pAllocator,
/* [in] */ int bReadOnly)
{
- Debug printf("COutputPin_NotifyAllocator(%p, %p) called\n", This, pAllocator);
- ((COutputMemPin*)This)->pAllocator = (MemAllocator*) pAllocator;
+ COutputMemPin* this=(COutputMemPin*)This;
+ ALLOCATOR_PROPERTIES props;
+ Debug printf("COutputMemPin_NotifyAllocator(%p, %p => %p) called\n", This, this->pAllocator,pAllocator);
+ if (this->pAllocator && this->pAllocator != pAllocator){
+ //Remote pin suggest another allocator; => removing old one
+ this->pAllocator->vt->Release((IUnknown*)this->pAllocator);
+ }
+ this->pAllocator = pAllocator;
+ this->pAllocator->vt->AddRef((IUnknown*)this->pAllocator); //-- seem like we not need this
+
+ this->pAllocator->vt->GetProperties(this->pAllocator,&props);
+#if defined(HAVE_TV_DSHOW)
+ init_ringbuffer(this,props.cBuffers,props.cbBuffer);
+#endif
return 0;
}
-static HRESULT STDCALL COutputPin_GetAllocatorRequirements(IMemInputPin* This,
+/**
+ * \brief IMemInputPin::GetAllocatorRequirements (retrieves allocator properties requested by
+ * input pin)
+ *
+ * \param[in] This pointer to IMemInputPin interface
+ * \param[out] pProps pointer to a structure that receives allocator properties
+ *
+ * \return S_OK - success
+ * \return E_NOTIMPL - Not implemented
+ * \return E_POINTER - Null pointer
+ *
+ */
+static HRESULT STDCALL COutputMemPin_GetAllocatorRequirements(IMemInputPin* This,
/* [out] */ ALLOCATOR_PROPERTIES* pProps)
{
- return output_unimplemented("COutputPin_GetAllocatorRequirements", This);
+ return output_unimplemented("COutputMemPin_GetAllocatorRequirements", This);
}
-static HRESULT STDCALL COutputPin_Receive(IMemInputPin* This,
+
+#if defined(HAVE_TV_DSHOW)
+static int COutputPin_FillBuffer(COutputPin* This,char* buffer,int len,long long* ppts,long long*psampleno){
+ int bytes;
+ COutputMemPin* mp=This->mempin;
+ Debug printf("COutputPin_FillBuffer(%p) (%d/%d) called \n", This,mp->count,mp->buffersize);
+ if(!mp->ringbuffer || !mp->count) return 0; //underrrun
+
+ if (len<mp->blocksize)
+ bytes=len;
+ else
+ bytes=mp->blocksize;
+ memcpy(buffer,mp->ringbuffer[mp->head],bytes);
+ if (ppts) *ppts=mp->pts[mp->head];
+ if (psampleno) *psampleno=mp->sampleno[mp->head];
+ EnterCriticalSection(&(mp->crit));
+ mp->head=(mp->head+1)%mp->buffersize;
+ mp->count--;
+ LeaveCriticalSection(&(mp->crit));
+ return bytes;
+}
+/**
+ * \brief copies media data, sample number and timestamp from given media sample to
+ * internal ringbuffer
+ *
+ * \param[in] this pointer to COutputMemPin object
+ * \param[out] pSample media sample to copy data from
+ *
+ * TOTO: add proper error/warning messages
+ *
+ */
+static void CopySample(COutputMemPin* this,IMediaSample* pSample){
+ int bytes;
+ unsigned char* pointer;
+ long long start,end;
+ if(!this->ringbuffer) return ;
+ if(this->count>=this->buffersize) {
+ printf ("CopySample(%p) Buffer full! Loosing frame (block=%d ring=%d)!\n",this,this->blocksize,this->buffersize);
+ return ;
+ }
+
+ //data
+ bytes=pSample->vt->GetActualDataLength(pSample);
+ if(bytes>this->blocksize) bytes=this->blocksize; //FIXME: overflow!!
+ pSample->vt->GetPointer(pSample,&pointer);
+ memcpy(this->ringbuffer[this->tail],pointer,bytes);
+
+ //timestamp
+ start=end=0;
+ pSample->vt->GetTime(pSample,&start,&end);
+ Debug printf("CopySample(%p) called time=(%lld,%lld) \n", this,start,end);
+ this->pts[this->tail]=end; //probably need "end"
+ //sample number
+ start=end=0;
+ pSample->vt->GetMediaTime(pSample,&start,&end);
+ Debug printf("CopySample(%p) called mtime=(%lld,%lld) \n", this,start,end);
+ this->sampleno[this->tail]=start;
+
+ EnterCriticalSection(&(this->crit));
+ this->tail=(this->tail+1)%this->buffersize;
+ this->count++;
+ LeaveCriticalSection(&(this->crit));
+}
+#endif
+/**
+ * \brief IMemInputPin::Receive (receives the next media sample int thre stream)
+ *
+ * \param[in] This pointer to IMemInputPin interface
+ * \param[in] pSample pointer to sample's IMediaSample interface
+ *
+ * \return S_OK - success
+ * \return S_FALSE - The sample was rejected
+ * \return E_POINTER - Null pointer
+ * \return VFW_E_INVALIDMEDIATYPE - invalid media type
+ * \return VFW_E_RUNTIME_ERROR - run-time error occured
+ * \return VFW_E_WRONG_STATE - pin is stopped
+ *
+ * \remarks
+ * Method san do on of the following:
+ * - reject sample
+ * - accept sample and process it in another thread
+ * - accept sample and process it before returning
+ *
+ * In second case method should increase reference count for sample (through AddRef)
+ * In the last case method might block indefinitely. If this might
+ * happen IMemInpuPin::ReceiveCAnBlock returns S_OK
+ *
+ * \note
+ * IMemoryInputPin::Receive,IMemoryInputPin::ReceiveMultiple, IMemoryInputPin::EndOfStream,
+ * IMemAllocator::GetBuffer runs in different (streaming) thread then other
+ * methods (application thread).
+ * IMemoryInputPin::NewSegment runs either in streaming or application thread.
+ * Developer must use critical sections for thread-safing work.
+ *
+ */
+static HRESULT STDCALL COutputMemPin_Receive(IMemInputPin* This,
/* [in] */ IMediaSample* pSample)
{
COutputMemPin* mp = (COutputMemPin*)This;
+ IReferenceClock* pClock=NULL;
+ REFERENCE_TIME pts;
+ HRESULT hr;
char* pointer;
int len;
- Debug printf("COutputPin_Receive(%p) called\n", This);
+ Debug printf("COutputMemPin_Receive(%p) called\n", This);
if (!pSample)
return E_INVALIDARG;
if (pSample->vt->GetPointer(pSample, (BYTE**) &pointer))
@@ -382,6 +1128,10 @@
*(mp->frame_pointer) = pointer;
if (mp->frame_size_pointer)
*(mp->frame_size_pointer) = len;
+
+#if defined(__MINGW32__)
+ CopySample(mp,pSample);
+#endif
/*
FILE* file=fopen("./uncompr.bmp", "wb");
char head[14]={0x42, 0x4D, 0x36, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00};
@@ -396,43 +1146,134 @@
return 0;
}
-static HRESULT STDCALL COutputPin_ReceiveMultiple(IMemInputPin * This,
+/**
+ * \brief IMemInputPin::ReceiveMultiple (receives multiple samples in the stream)
+ *
+ * \param[in] This pointer to IMemInputPin interface
+ * \param[in] pSamples pointer to array with samples
+ * \param[in] nSamples number of samples in array
+ * \param[out] nSamplesProcessed number of processed samples
+ *
+ * \return S_OK - success
+ * \return S_FALSE - The sample was rejected
+ * \return E_POINTER - Null pointer
+ * \return VFW_E_INVALIDMEDIATYPE - invalid media type
+ * \return VFW_E_RUNTIME_ERROR - run-time error occured
+ * \return VFW_E_WRONG_STATE - pin is stopped
+ *
+ * \remarks
+ * This method behaves like IMemInputPin::Receive but for array of samples
+ *
+ * \note
+ * IMemoryInputPin::Receive,IMemoryInputPin::ReceiveMultiple, IMemoryInputPin::EndOfStream,
+ * IMemAllocator::GetBuffer runs in different (streaming) thread then other
+ * methods (application thread).
+ * IMemoryInputPin::NewSegment runs either in streaming or application thread.
+ * Developer must use critical sections for thread-safing work.
+ *
+ */
+static HRESULT STDCALL COutputMemPin_ReceiveMultiple(IMemInputPin * This,
/* [size_is][in] */ IMediaSample **pSamples,
/* [in] */ long nSamples,
/* [out] */ long *nSamplesProcessed)
{
- return output_unimplemented("COutputPin_ReceiveMultiple", This);
+ HRESULT hr;
+ Debug printf ("COutputMemPin_ReceiveMultiple(%p) %d\n", This,nSamples);
+ *nSamplesProcessed = 0;
+ while (nSamples-- > 0) {
+ hr = This->vt->Receive(This,pSamples[*nSamplesProcessed]);
+ if (hr != S_OK) break;
+ (*nSamplesProcessed)++;
+ }
+ return hr;
}
-static HRESULT STDCALL COutputPin_ReceiveCanBlock(IMemInputPin * This)
+/**
+ * \brief IMemInputPin::ReceiveCanBlock (determines whether IMemInputPin:::Receive might block)
+ *
+ * \param[in] This pointer to IMemInputPin interface
+ *
+ * \return S_OK - the pin might block
+ * \return S_FALSE - the pin will not block
+ *
+ */
+static HRESULT STDCALL COutputMemPin_ReceiveCanBlock(IMemInputPin * This)
{
- return output_unimplemented("COutputPin_ReceiveCanBlock", This);
+ return output_unimplemented("COutputMemPin_ReceiveCanBlock", This);
}
+/**
+ * \brief COutputPin::SetFramePointer (sets internal frame pointer to an external buffer)
+ *
+ * \param[in] This pointer to COutputPin class
+ * \param[in] z new pointer
+ *
+ */
static void COutputPin_SetFramePointer(COutputPin* This, char** z)
{
This->mempin->frame_pointer = z;
}
+/**
+ * \brief COutputPin::SetFramePointer2 (sets allocator's pointer to an external buffer)
+ *
+ * \param[in] This pointer to COutputPin class
+ * \param[in] z new pointer
+ *
+ */
static void COutputPin_SetPointer2(COutputPin* This, char* p)
{
if (This->mempin->pAllocator)
- // fixme
- This->mempin->pAllocator->SetPointer(This->mempin->pAllocator, p);
+ ((MemAllocator*)This->mempin->pAllocator)->SetPointer((MemAllocator*)This->mempin->pAllocator, p);
}
+/**
+ * \brief COutputPin::SetFrameSizePointer (sets pointer to variable that receives frame size)
+ *
+ * \param[in] This pointer to COutputPin class
+ * \param[in] z new pointer
+ *
+ */
static void COutputPin_SetFrameSizePointer(COutputPin* This, long* z)
{
This->mempin->frame_size_pointer = z;
}
+
+/**
+ * \brief COutputPin::SetNewFormat(sets new media format for the pin)
+ *
+ * \param[in] This pointer to COutputPin class
+ * \param[in] amt new media format
+ *
+ */
static void COutputPin_SetNewFormat(COutputPin* This, const AM_MEDIA_TYPE* amt)
{
- This->type = *amt;
+ free_media_type(&(This->type)); //Free pbFormat and pUnk
+ /* FIXME: this may fail and also we must Check and Reconnect pins with new format */
+ copy_media_type(&(This->type),amt);
}
+/**
+ * \brief COutputPin destructor
+ *
+ * \param[in] This pointer to COutputPin class
+ *
+ */
static void COutputPin_Destroy(COutputPin* This)
{
+ Debug printf("COutputPin_Destroy(%p) called\n",This);
+
+#if defined(__MINGW32__)
+ free_ringbuffer(This->mempin);
+#endif
+
+ if(This->remote){
+ This->remote->vt->Release((IUnknown*)This->remote);
+ This->remote=NULL;
+ }
+
+ free_media_type(&(This->type));
if (This->mempin->vt)
free(This->mempin->vt);
if (This->mempin)
@@ -442,6 +1283,14 @@
free(This);
}
+/**
+ * \brief IUnknown::AddRef (increases reference counter for interface)
+ *
+ * \param[in] This pointer to IUnknown class
+ *
+ * \return S_OK success
+ *
+ */
static HRESULT STDCALL COutputPin_AddRef(IUnknown* This)
{
Debug printf("COutputPin_AddRef(%p) called (%d)\n", This, ((COutputPin*)This)->refcount);
@@ -449,6 +1298,17 @@
return 0;
}
+/**
+ * \brief IUnknown::Release (desreases reference counter for interface)
+ *
+ * \param[in] This pointer to IUnknown class
+ *
+ * \return S_OK - success
+ *
+ * \remarks
+ * When reference counter reaches zero calls destructor
+ *
+ */
static HRESULT STDCALL COutputPin_Release(IUnknown* This)
{
Debug printf("COutputPin_Release(%p) called (%d)\n", This, ((COutputPin*)This)->refcount);
@@ -458,28 +1318,59 @@
return 0;
}
-static HRESULT STDCALL COutputPin_M_AddRef(IUnknown* This)
+/**
+ * \brief IUnknown::AddRef (increases reference counter for interface)
+ *
+ * \param[in] This pointer to IUnknown class
+ *
+ * \return S_OK - success
+ *
+ */
+static HRESULT STDCALL COutputMemPin_AddRef(IUnknown* This)
{
COutputMemPin* p = (COutputMemPin*) This;
- Debug printf("COutputPin_MAddRef(%p) called (%p, %d)\n", p, p->parent, p->parent->refcount);
+ Debug printf("COutputMemPin_AddRef(%p) called (%p, %d)\n", p, p->parent, p->parent->refcount);
p->parent->refcount++;
return 0;
}
-static HRESULT STDCALL COutputPin_M_Release(IUnknown* This)
+/**
+ * \brief IUnknown::Release (desreases reference counter for interface)
+ *
+ * \param[in] This pointer to IUnknown class
+ *
+ * \return S_OK - success
+ *
+ * \remarks
+ * When reference counter reaches zero calls destructor
+ *
+ */
+static HRESULT STDCALL COutputMemPin_Release(IUnknown* This)
{
COutputMemPin* p = (COutputMemPin*) This;
- Debug printf("COutputPin_MRelease(%p) called (%p, %d)\n",
+ Debug printf("COutputMemPin_Release(%p) called (%p, %d)\n",
p, p->parent, p->parent->refcount);
if (--p->parent->refcount <= 0)
COutputPin_Destroy(p->parent);
return 0;
}
-COutputPin* COutputPinCreate(const AM_MEDIA_TYPE* amt)
+/**
+ * \brief COutputPin constructor
+ *
+ * \param[in] amt media type for pin
+ *
+ * \return pointer to COutputPin if success
+ * \return NULL if error occured
+ *
+ */
+COutputPin* COutputPinCreate(const AM_MEDIA_TYPE* amt,IBaseFilter* parent)
{
+ IMemInputPin_vt* ivt;
COutputPin* This = (COutputPin*) malloc(sizeof(COutputPin));
- IMemInputPin_vt* ivt;
+ if (!parent)
+ return NULL;
+ printf("%s called\n", "COutputPinCreate");
if (!This)
return NULL;
@@ -494,11 +1385,12 @@
return NULL;
}
+ This->pFilter=parent;
This->mempin->vt = ivt;
This->refcount = 1;
This->remote = 0;
- This->type = *amt;
+ copy_media_type(&(This->type),amt);
This->vt->QueryInterface = COutputPin_QueryInterface;
This->vt->AddRef = COutputPin_AddRef;
@@ -520,25 +1412,34 @@
This->vt->NewSegment = COutputPin_NewSegment;
This->mempin->vt->QueryInterface = COutputPin_M_QueryInterface;
- This->mempin->vt->AddRef = COutputPin_M_AddRef;
- This->mempin->vt->Release = COutputPin_M_Release;
- This->mempin->vt->GetAllocator = COutputPin_GetAllocator;
- This->mempin->vt->NotifyAllocator = COutputPin_NotifyAllocator;
- This->mempin->vt->GetAllocatorRequirements = COutputPin_GetAllocatorRequirements;
- This->mempin->vt->Receive = COutputPin_Receive;
- This->mempin->vt->ReceiveMultiple = COutputPin_ReceiveMultiple;
- This->mempin->vt->ReceiveCanBlock = COutputPin_ReceiveCanBlock;
+ This->mempin->vt->AddRef = COutputMemPin_AddRef;
+ This->mempin->vt->Release = COutputMemPin_Release;
+ This->mempin->vt->GetAllocator = COutputMemPin_GetAllocator;
+ This->mempin->vt->NotifyAllocator = COutputMemPin_NotifyAllocator;
+ This->mempin->vt->GetAllocatorRequirements = COutputMemPin_GetAllocatorRequirements;
+ This->mempin->vt->Receive = COutputMemPin_Receive;
+ This->mempin->vt->ReceiveMultiple = COutputMemPin_ReceiveMultiple;
+ This->mempin->vt->ReceiveCanBlock = COutputMemPin_ReceiveCanBlock;
This->mempin->frame_size_pointer = 0;
This->mempin->frame_pointer = 0;
This->mempin->pAllocator = 0;
This->mempin->refcount = 1;
This->mempin->parent = This;
-
+#if defined(HAVE_TV_DSHOW)
+ This->mempin->blocksize=0;
+ This->mempin->buffersize=0;
+ This->mempin->ringbuffer=NULL;
+ This->mempin->sampleno=NULL;
+ This->mempin->pts=NULL;
+#endif
This->SetPointer2 = COutputPin_SetPointer2;
This->SetFramePointer = COutputPin_SetFramePointer;
This->SetFrameSizePointer = COutputPin_SetFrameSizePointer;
This->SetNewFormat = COutputPin_SetNewFormat;
+#if defined(HAVE_TV_DSHOW)
+ This->FillBuffer=COutputPin_FillBuffer;
+#endif
return This;
}
Index: loader/dshow/outputpin.h
===================================================================
--- loader/dshow/outputpin.h (revision 21934)
+++ loader/dshow/outputpin.h (working copy)
@@ -15,12 +15,22 @@
COutputMemPin* mempin;
AM_MEDIA_TYPE type;
IPin* remote;
+ IBaseFilter* pFilter;
void ( *SetFramePointer )(COutputPin*, char** z);
void ( *SetPointer2 )(COutputPin*, char* p);
void ( *SetFrameSizePointer )(COutputPin*, long* z);
void ( *SetNewFormat )(COutputPin*, const AM_MEDIA_TYPE* a);
+#if defined(HAVE_TV_DSHOW)
+ int ( *FillBuffer)(COutputPin*,char* ,int ,long long* ,long long*);
+#endif
};
-COutputPin* COutputPinCreate(const AM_MEDIA_TYPE* vhdr);
+COutputPin* COutputPinCreate(const AM_MEDIA_TYPE* vhdr,IBaseFilter* parent);
+void print_pmt_info(const AM_MEDIA_TYPE* pmt);
+void free_media_type(AM_MEDIA_TYPE* pmt);
+void delete_media_type(AM_MEDIA_TYPE* pmt);
+HRESULT copy_media_type(AM_MEDIA_TYPE* pDst,const AM_MEDIA_TYPE* pSrc);
+AM_MEDIA_TYPE* create_media_type(const AM_MEDIA_TYPE* pSrc);
+
#endif /* DS_OUTPUTPIN_H */
Index: loader/Makefile
===================================================================
--- loader/Makefile (revision 21934)
+++ loader/Makefile (working copy)
@@ -29,6 +29,10 @@
dmo/dmo.c \
dmo/dmo_guids.c \
+ifeq ($(TV_DSHOW),yes)
+SRCS+= dshow/DS_MPGrabber.c
+endif
+
include ../mpcommon.mak
CFLAGS+=-fno-omit-frame-pointer -mno-omit-leaf-frame-pointer
Index: stream/Makefile
===================================================================
--- stream/Makefile (revision 21934)
+++ stream/Makefile (working copy)
@@ -52,6 +52,7 @@
SRCS-$(TV_BSDBT848) += tvi_bsdbt848.c
SRCS-$(TV_V4L1) += tvi_v4l.c audio_in.c
SRCS-$(TV_V4L2) += tvi_v4l2.c audio_in.c
+SRCS-$(TV_DSHOW) += tvi_dshow.c
SRCS-$(VCD) += stream_vcd.c
SRCS-$(VSTREAM) += stream_vstream.c
Index: stream/tv.c
===================================================================
--- stream/tv.c (revision 21934)
+++ stream/tv.c (working copy)
@@ -636,11 +636,16 @@
tvi_handle_t *tvi_init_v4l(char *device, char *adevice);
tvi_handle_t *tvi_init_v4l2(char *device, char *adevice);
tvi_handle_t *tvi_init_bsdbt848(char *device);
+tvi_handle_t *tvi_init_dshow(char *device);
tvi_handle_t *tv_begin(void)
{
if (!strcmp(tv_param_driver, "dummy"))
return tvi_init_dummy(tv_param_device);
+#ifdef HAVE_TV_DSHOW
+ if (!strcmp(tv_param_driver, "dshow"))
+ return tvi_init_dshow(tv_param_device);
+#endif
#ifdef HAVE_TV_V4L1
if (!strcmp(tv_param_driver, "v4l"))
return tvi_init_v4l(tv_param_device, tv_param_adevice);
@@ -653,6 +658,10 @@
if (!strcmp(tv_param_driver, "bsdbt848"))
return tvi_init_bsdbt848(tv_param_device);
#endif
+#ifdef HAVE_TV_DSHOW
+ if (!strcmp(tv_param_driver, "dshow"))
+ return tvi_init_dshow(tv_param_device);
+#endif
mp_msg(MSGT_TV, MSGL_ERR, "No such driver: %s\n", tv_param_driver);
return(NULL);
Index: stream/tvi_dshow.c
===================================================================
--- stream/tvi_dshow.c (revision 0)
+++ stream/tvi_dshow.c (revision 0)
@@ -0,0 +1,1299 @@
+/*
+ * TV support under Win32
+ *
+ * Initially wrote by Vladimir Voroshilov <voroshil at gmail.com>.
+ * Based on tvi_dummy.c with help of tv.c, tvi_v4l2.c and /loader/dshow/ code .
+ *
+ * -------------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *
+ * -------------------------------------------------------------------------------
+ *
+ *
+ * WARNING: This is alpha code!
+ *
+ * Abilities:
+ * * Watching TV under Win32 using WDM Capture driver and DirectShow
+ * * Grabbing synchronized audio/video with mencoder (synchronization is beeing done by DirectShow)
+ *
+ * Disadvantages:
+ * * Audio only capture does not supported.
+ * * Pausing or moving video windows causes buffer overflow (no pause callback).
+ * * Supports fine only IMGFMT_YUY2 (BGR formats cause garbage input, trouble somewhere in code)
+ * * Sets channels only by it's number, not by freq, so durty hack is used
+ * * Uses frequency table hack to access,.
+ * * Tested only with LifeView FlyTV Prime 34FM (SAA7134 based) with driver from Ivan Uskov
+ * * Code potentually can be used for radio support, but due to bug in the driver can not be tested
+ *
+ * TODO:
+ * * rewrite startup parameters negotiation
+ * * Set default format from a one of available, then get parameters through ioctl,attepmt to set resulted format
+ * and exit if failure.
+ * * Frequency table selecting (and remove 'tvh' hack )
+ * * Flip image upside-down for RGB formats.
+ * * check using header files and keep only needed
+ * * Move some code from init() to start() (because there some parameters (formats,...) already negotiated
+ * * Add some notes to methods' parameters
+ * * replace parameter from V4L style frequency to Hz in get_frequency and set_frequency
+ * * make crossbar configurable
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include "libmpcodecs/img_format.h"
+#include "libaf/af_format.h"
+
+#include "loader/com.h"
+#include "loader/wine/mmreg.h"
+#include "loader/dshow/DS_MPGrabber.h"
+#include "loader/dshow/guids.h"
+
+#ifndef NOAVIFILE_HEADERS
+#define VFW_E_NOT_RUNNING 0x80040226
+#define VFW_E_NO_ALLOCATOR 0x8004020A
+#else
+#include "libwin32.h"
+#endif
+
+#include "tv.h"
+#include "mp_msg.h"
+#include "frequencies.h"
+
+
+#include "tvi_dshow.h"
+
+
+#define MY_PIN_CATEGORY PIN_CATEGORY_CAPTURE
+
+const GUID IID_IKsPropertySet={0x31efac30, 0x515c, 0x11d0,
+ {0xa9,0xaa, 0x00,0xaa,0x00,0x61,0xbe,0x93}};
+
+
+/*====== REVIEWED CODE BEGIN ============================*/
+/* information about this file */
+static tvi_info_t info = {
+ "DirectShow TV",
+ "dshow",
+ "voroshil",
+ "Very experimental!! Use with caution"
+};
+
+typedef struct {
+ int dev_index; ///< capture device index in device list (defaul: 0, dirst available device)
+ int dev_pin; ///< currently using for debug pruposes, can be used for selecting audio capture device
+ int state; ///< state: 1-filter graph running, 0-filter graph stopped
+
+ int fcc; ///< used video format code (FourCC)
+ int width; ///< picture width (default: 320)
+ int height; ///< picture height (default: 240)
+ int samplerate; ///< audio samplerate (default: 44100)
+ //would be fixed/removed in future
+ tvi_handle_t* tvh; ///< FIXME: hack to access frequency table
+
+ // DirectShow related stuff
+ AM_MEDIA_TYPE* pmtVideo; ///< DirectShow video stream properties
+ AM_MEDIA_TYPE* pmtAudio; ///< DirectShow audio stream properties. NULL mean audio disabled.
+ IAMTVTuner * pTVTuner; ///< interface for tuner device
+ IGraphBuilder * pGraph; ///< filter graph
+ ICaptureGraphBuilder2 * pBuilder; ///< graph builder
+ IBaseFilter * pBaseFilter; ///< interface for capture device
+ DS_MPGrabber * pGrabber; ///< MPlayer's grabber filter
+ IMediaControl * pMediaControl; ///< interface for controlling graph (start, stop,...)
+} priv_t;
+
+#include "tvi_def.h"
+/*====== REVIEWED CODE END ============================*/
+
+const GUID CLSID_CaptureGraphBuilder={0xBF87B6E0, 0x8C27, 0x11d0,
+ { 0xB3, 0xF0, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5}};
+const GUID CLSID_CaptureGraphBuilder2={0xBF87B6E1, 0x8C27, 0x11d0,
+ { 0xB3, 0xF0, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5}};
+const GUID CLSID_FilterGraph={0xe436ebb3, 0x524f, 0x11ce,
+ { 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}};
+const GUID CLSID_SystemDeviceEnum={0x62BE5D10, 0x60EB, 0x11d0,
+ { 0xBD, 0x3B, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86}};
+const GUID CLSID_VideoInputDeviceCategory={0x860BB310, 0x5D01, 0x11d0,
+ { 0xBD, 0x3B, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86}};
+const GUID CLSID_AudioInputDeviceCategory={0x33d9a762, 0x90c8, 0x11d0,
+ { 0xbd, 0x43, 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86}};
+
+const GUID IID_IAMTVTuner={0x211A8766, 0x03AC, 0x11d1,
+ { 0x8D, 0x13, 0x00, 0xAA, 0x00, 0xBD, 0x83, 0x39}};
+const GUID IID_IAMCrossbar={0xc6e13380, 0x30ac, 0x11d0,
+ { 0xa1, 0x8c, 0x00, 0xa0, 0xc9, 0x11, 0x89, 0x56}};
+const GUID IID_ICaptureGraphBuilder={0xbf87b6e0, 0x8c27, 0x11d0,
+ { 0xb3, 0xf0, 0x00, 0xaa, 0x00, 0x37, 0x61, 0xc5}};
+const GUID IID_ICaptureGraphBuilder2={0x93e5a4e0, 0x2d50, 0x11d2,
+ { 0xab, 0xfa, 0x00, 0xa0, 0xc9, 0xc6, 0xe3, 0x8d}};
+const GUID IID_IFilterGraph={0x56a8689f, 0x0ad4, 0x11ce,
+ { 0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}};
+const GUID IID_ICreateDevEnum={0x29840822, 0x5b84, 0x11d0,
+ { 0xbd, 0x3b, 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86}};
+const GUID IID_IPropertyBag={0x55272a00, 0x42cb, 0x11ce,
+ { 0x81, 0x35, 0x00, 0xaa, 0x00, 0x4b, 0xb8, 0x51}};
+const GUID IID_IMediaControl={0x56a868b1, 0x0ad4, 0x11ce,
+ { 0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}};
+const GUID IID_IGraphBuilder={0x56a868a9, 0x0ad4, 0x11ce,
+ { 0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}};
+
+const GUID PIN_CATEGORY_CAPTURE={0xfb6c4281, 0x0353, 0x11d1,
+ {0x90, 0x5f, 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba}};
+const GUID PIN_CATEGORY_PREVIEW={0xfb6c4282, 0x0353, 0x11d1,
+ {0x90, 0x5f, 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba}};
+
+
+struct tvi_indexed_string {
+ long index;
+ char* name;
+};
+
+typedef struct {
+ uint32_t fmt;
+ const GUID* subtype;
+ int nBits;
+ int nCompression;
+ int tail;
+} img_fmt ;
+
+const img_fmt img_fmt_list[]={
+{IMGFMT_YUY2,&MEDIASUBTYPE_YUY2, 0,IMGFMT_YUY2,0},
+{IMGFMT_YV12,&MEDIASUBTYPE_YV12, 0,IMGFMT_YV12,0},
+{IMGFMT_IYUV,&MEDIASUBTYPE_IYUV, 0,IMGFMT_IYUV,0},
+{IMGFMT_I420,&MEDIASUBTYPE_I420, 0,IMGFMT_I420,0},
+{IMGFMT_UYVY,&MEDIASUBTYPE_UYVY, 0,IMGFMT_UYVY,0},
+{IMGFMT_YVYU,&MEDIASUBTYPE_YVYU, 0,IMGFMT_YVYU,0},
+{IMGFMT_YVU9,&MEDIASUBTYPE_YVU9, 0,IMGFMT_YVU9,0},
+{IMGFMT_BGR32,&MEDIASUBTYPE_RGB32, 32, 0,0},
+{IMGFMT_BGR24,&MEDIASUBTYPE_RGB24, 24, 0,0},
+{IMGFMT_BGR16,&MEDIASUBTYPE_RGB565,16, 3,12},
+{IMGFMT_BGR15,&MEDIASUBTYPE_RGB555,16, 3,12},
+{0,&GUID_NULL,0,0,0}
+};
+
+AM_MEDIA_TYPE* create_video_type(int fmt){
+ AM_MEDIA_TYPE* pmt;
+ VIDEOINFOHEADER* Vhdr;
+ int index;
+
+ pmt=(AM_MEDIA_TYPE*)malloc(sizeof(AM_MEDIA_TYPE));
+ if(!pmt) return NULL;
+
+ memset(pmt,0,sizeof(AM_MEDIA_TYPE));
+
+ pmt->majortype=MEDIATYPE_Video;
+ pmt->bFixedSizeSamples=1;
+
+ for(index=0;img_fmt_list[index].fmt;index++)
+ if(img_fmt_list[index].fmt==fmt) break;
+
+ pmt->subtype=*img_fmt_list[index].subtype;
+
+ return pmt;
+}
+int subtype2imgfmt(const GUID* subtype){
+ int i;
+ for(i=0;img_fmt_list[i].fmt;i++){
+ if (memcmp(subtype,img_fmt_list[i].subtype,16)) return img_fmt_list[i].fmt;
+ }
+ return 0;
+}
+
+#if 0
+AM_MEDIA_TYPE* GetMediaType(int width,int height,int fmt){
+ return create_video_type(fmt);
+}
+#endif
+#define TV_NORMS_COUNT 19
+const struct tvi_indexed_string tv_norms[TV_NORMS_COUNT]={
+ {AnalogVideo_NTSC_M,"ntsc-m"},
+ {AnalogVideo_NTSC_M_J,"ntsc-mj"},
+ {AnalogVideo_NTSC_433,"ntsc-433"},
+ {AnalogVideo_PAL_B,"pal-b"},
+ {AnalogVideo_PAL_D,"pal-d"},
+ {AnalogVideo_PAL_G,"pal-g"},
+ {AnalogVideo_PAL_H,"pal-h"},
+ {AnalogVideo_PAL_I,"pal-i"},
+ {AnalogVideo_PAL_M,"pal-m"},
+ {AnalogVideo_PAL_N,"pal-n"},
+ {AnalogVideo_PAL_60,"pal-60"},
+ {AnalogVideo_SECAM_B,"secam-b"},
+ {AnalogVideo_SECAM_D,"secam-d"},
+ {AnalogVideo_SECAM_G,"secam-g"},
+ {AnalogVideo_SECAM_H,"secam-h"},
+ {AnalogVideo_SECAM_K,"secam-k"},
+ {AnalogVideo_SECAM_K1,"secam-k1"},
+ {AnalogVideo_SECAM_L,"secam-l"},
+ {AnalogVideo_SECAM_L1,"secam-l1"}
+};
+long tv_available_norms[TV_NORMS_COUNT];
+int tv_available_norms_count=0;
+
+ typedef struct
+ {
+ WORD CountryCode;
+ WORD CableFreqTable;
+ WORD BroadcastFreqTable;
+ DWORD VideoStandard;
+ } TRCCountryList;
+
+
+/* stub */
+#define TV_INPUTS_COUNT 1
+const struct tvi_indexed_string tv_inputs[TV_INPUTS_COUNT]={
+ {1,"Television"}
+};
+long tv_available_inputs[TV_NORMS_COUNT];
+int tv_available_inputs_count=0;
+
+
+/* Forward declaration of private methods */
+int setFreq(priv_t* priv,long lFreq);
+AM_MEDIA_TYPE* GetMediaType(int width,int height,int fmt);
+HRESULT SetVCrossbar(ICaptureGraphBuilder2* pBuilder,IBaseFilter* pBaseFilter);
+AM_MEDIA_TYPE* create_audio_media_type(int samplerate,int bits,int channels);
+
+HRESULT display_device_media_types(priv_t* priv,GUID* pmediatype){
+ IEnumMediaTypes* pEnum;
+ IPin* pPin;
+ int i;
+ ULONG cFetched;
+ AM_MEDIA_TYPE* pmt;
+ HRESULT hr;
+
+ hr=priv->pBuilder->vt->FindPin(priv->pBuilder,priv->pBaseFilter,PINDIR_OUTPUT,NULL,pmediatype,FALSE,0,&pPin);
+ if(FAILED(hr)) return hr;
+
+ hr=pPin->vt->EnumMediaTypes(pPin,&pEnum);
+ pPin->vt->Release((IUnknown*)pPin);
+ if(FAILED(hr)) return hr;
+
+ for (i=0;pEnum->vt->Next(pEnum,1, &pmt,&cFetched)==S_OK;i++) {
+ if(!pmt) break;
+
+ print_pmt_info(pmt);
+ delete_media_type(pmt);
+ }
+ pEnum->vt->Release((IUnknown*)pEnum);
+ return S_OK;
+}
+
+/**
+ TODO: recheck code
+*/
+HRESULT set_media_format(priv_t* priv, AM_MEDIA_TYPE* pmt){
+ IPin* pRemotePin;
+ COutputPin* pPin;
+ HRESULT hr;
+ int result=S_OK;
+ int isVideo=0;
+ AM_MEDIA_TYPE* pmt2;
+
+ if(!pmt) return E_POINTER;
+
+ if(memcmp(&(pmt->majortype),&MEDIATYPE_Video,16)==0)
+ isVideo=1;
+
+ mp_msg(MSGT_TV,MSGL_V,"Requesting following %s format:\n",isVideo?"video":"audio");
+ print_pmt_info(pmt);
+
+ hr=priv->pBuilder->vt->FindPin(priv->pBuilder,priv->pGrabber,PINDIR_INPUT,NULL,isVideo?&MEDIATYPE_Video:&MEDIATYPE_Audio,FALSE,0,&pPin);
+ if(FAILED(hr)) return hr;
+
+ pPin->vt->ConnectedTo(pPin,&pRemotePin);
+ if(hr==S_OK && pRemotePin) //S_OK is mandatory here
+ {
+ hr=priv->pGraph->vt->Disconnect(priv->pGraph,pPin);
+ if(FAILED(hr))
+ {
+ pPin->vt->Release((IUnknown*)pPin);
+ pRemotePin->vt->Release((IUnknown*)pRemotePin);
+ return hr;
+ }
+ hr=priv->pGraph->vt->Disconnect(priv->pGraph,pRemotePin);
+ if(FAILED(hr))
+ {
+ pPin->vt->Release((IUnknown*)pPin);
+ pRemotePin->vt->Release((IUnknown*)pRemotePin);
+ return hr;
+ }
+ hr=pRemotePin->vt->QueryAccept(pRemotePin,pmt);
+ if(hr!=S_OK){
+ mp_msg(MSGT_TV,MSGL_WARN,"Format %s not accepted by remote pin\n",isVideo?"video":"audio");
+ result=hr; //Remote pin accepts this type
+ }
+ pRemotePin->vt->Release((IUnknown*)pRemotePin);
+ }
+ if(result==S_OK)
+ pPin->SetNewFormat(pPin,pmt);
+
+ hr=priv->pBuilder->vt->RenderStream(priv->pBuilder,&MY_PIN_CATEGORY, isVideo?&MEDIATYPE_Video:&MEDIATYPE_Audio,(IUnknown*)priv->pBaseFilter, NULL, (IBaseFilter*)priv->pGrabber);
+ if(FAILED(hr)) {
+ mp_msg(MSGT_TV,MSGL_ERR,"Rebuilding %s chain in graph failure. Error=0x%08x",isVideo?"video":"audio",hr);
+ result=hr;
+ }
+ if(isVideo){
+ if(!priv->pmtVideo){
+ priv->pmtVideo=(AM_MEDIA_TYPE*)malloc(sizeof(AM_MEDIA_TYPE));
+ memset(priv->pmtVideo,0,sizeof(AM_MEDIA_TYPE));
+ }
+ }else{
+ if(!priv->pmtAudio){
+ priv->pmtAudio=(AM_MEDIA_TYPE*)malloc(sizeof(AM_MEDIA_TYPE));
+ memset(priv->pmtVideo,0,sizeof(AM_MEDIA_TYPE));
+ }
+ }
+ pmt2=isVideo?priv->pmtVideo:priv->pmtAudio;
+
+ if(result==S_OK){
+ mp_msg(MSGT_TV,MSGL_V,"Format %s accepted. Copying my ourselves\n",isVideo?"video":"audio");
+ free_media_type(pmt2);
+ pPin->vt->ConnectionMediaType(pPin,pmt2);
+ pPin->vt->Release((IUnknown*)pPin);
+
+ mp_msg(MSGT_TV,MSGL_V,"Negotiated following %s format:\n",isVideo?"video":"audio");
+ print_pmt_info(pmt2);
+ }
+ return result;
+}
+
+
+/*
+
+misc debug utilities
+
+*/
+char* err2str(HRESULT err){
+ switch(err){
+ case 0x8004025f: return "VFW_E_NOT_IN_GRAPH";
+ case E_POINTER: return "E_POINTER";
+ case E_FAIL: return "E_FAIL";
+ case E_OUTOFMEMORY: return "E_OUTOFMEMRY";
+ case E_NOTIMPL: return "E_NOTIMPL";
+ case E_INVALIDARG: return "E_INVALIDARG";
+ case S_OK: return "S_OK (no error)";
+ }
+ return "Unknown";
+}
+
+
+
+void showError(char* msg,char*step,HRESULT hr){
+ char buf[200];
+ snprintf(buf,200,"%s (step %s) Error: (0x%x) %s",msg,step,(unsigned int)hr,err2str(hr));
+ mp_msg(MSGT_TV,MSGL_ERR,"%s\n",buf);
+}
+void showSuccess(char* msg,char*step){
+ char buf[200];
+ snprintf(buf,200,"%s (step %s) Result success",msg,step);
+ mp_msg(MSGT_TV,MSGL_INFO,"%s\n",buf);
+}
+
+
+#define EXIT_IF_CONDITION(condition,msg,step,err) if(condition) {showError(msg,step,err); goto exit;}
+#define EXIT_IF_FAILURE_HR(hr,msg,step) EXIT_IF_CONDITION(FAILED(hr),msg,step,hr)
+
+// stub
+int chanlist2country(char* chanlist){
+ if (strcmp(chanlist,"us-bcast")==0) return 1;
+ if (strcmp(chanlist,"russia")==0) return 7;
+ if (strcmp(chanlist,"argentina")==0) return 54;
+ if (strcmp(chanlist,"japan-bcast")==0) return 81;
+ if (strcmp(chanlist,"china-bcast")==0) return 86;
+ if (strcmp(chanlist,"southafrica")==0) return 27;
+ if (strcmp(chanlist,"australia")==0) return 61;
+ if (strcmp(chanlist,"ireland")==0) return 353;
+ if (strcmp(chanlist,"france")==0) return 33;
+ if (strcmp(chanlist,"italy")==0) return 39;
+ if (strcmp(chanlist,"newzealand")==0) return 64;
+ if (strcmp(chanlist,"europe-east")==0) return 7; //directshow table uses eastern europe freq table for russia
+ if (strcmp(chanlist,"europe-west")==0) return 49; //directshow table uses western europe freq table for germany
+ return 1; //USA
+/*
+ { "us-cable", ntsc_cable, CHAN_COUNT(ntsc_cable) },
+ { "us-cable-hrc", ntsc_hrc, CHAN_COUNT(ntsc_hrc) },
+ { "japan-cable", ntsc_cable_jp, CHAN_COUNT(ntsc_cable_jp) },
+*/
+}
+
+
+#if 1
+AM_MEDIA_TYPE* GetMediaType(int width,int height,int fmt){
+ AM_MEDIA_TYPE* pmt;
+ VIDEOINFOHEADER* Vhdr;
+ int index;
+ printf("%d %d\n",sizeof(VIDEOINFOHEADER),sizeof(BITMAPINFOHEADER));
+
+ pmt=(AM_MEDIA_TYPE*)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
+ if(!pmt) return NULL;
+ memset(pmt, 0, sizeof(AM_MEDIA_TYPE));
+
+ for(index=0;img_fmt_list[index].fmt;index++)
+ if(img_fmt_list[index].fmt==fmt) break;
+
+ Vhdr = (VIDEOINFOHEADER*)CoTaskMemAlloc(sizeof(VIDEOINFOHEADER)+img_fmt_list[index].tail);
+
+ if(!Vhdr) {
+ free(pmt);
+ return NULL;
+ }
+ memset(Vhdr,0,sizeof(VIDEOINFOHEADER)+img_fmt_list[index].tail);
+
+ pmt->majortype=MEDIATYPE_Video;
+ pmt->subtype=*img_fmt_list[index].subtype;
+
+ if(img_fmt_list[index].nCompression && img_fmt_list[index].nCompression!=3)
+ pmt->bFixedSizeSamples = 0;
+ else
+ pmt->bFixedSizeSamples = 1;
+
+ Vhdr->bmiHeader.biBitCount=img_fmt_list[index].nBits;
+ Vhdr->bmiHeader.biCompression=img_fmt_list[index].nCompression;
+ Vhdr->bmiHeader.biPlanes=1;
+ Vhdr->bmiHeader.biWidth=width;
+ Vhdr->bmiHeader.biHeight=height;
+
+ if(!Vhdr->bmiHeader.biCompression==3)
+ Vhdr->bmiHeader.biSizeImage=abs((int)(2*Vhdr->bmiHeader.biWidth*Vhdr->bmiHeader.biHeight));
+ else if (!Vhdr->bmiHeader.biCompression)
+ Vhdr->bmiHeader.biSizeImage = labs(Vhdr->bmiHeader.biWidth * Vhdr->bmiHeader.biHeight) * ((Vhdr->bmiHeader.biBitCount + 7) / 8);
+ else
+ Vhdr->bmiHeader.biSizeImage=labs(Vhdr->bmiHeader.biBitCount* Vhdr->bmiHeader.biWidth*Vhdr->bmiHeader.biHeight)>>3;
+
+ if (Vhdr->bmiHeader.biCompression!= 0 && Vhdr->bmiHeader.biCompression!= 3 && Vhdr->bmiHeader.biHeight > 0)
+ Vhdr->bmiHeader.biHeight *= -1; // YUV formats uses should have height < 0
+
+ Vhdr->bmiHeader.biSize=sizeof(BITMAPINFOHEADER)+img_fmt_list[index].tail;
+
+ pmt->formattype = FORMAT_VideoInfo;
+ pmt->cbFormat = Vhdr->bmiHeader.biSize;
+// pmt->cbFormat = sizeof(VIDEOINFOHEADER)+img_fmt_list[index].tail;
+// pmt->pbFormat = (char*)Vhdr;
+ pmt->cbFormat = 0;
+ pmt->pbFormat = NULL;
+ pmt->lSampleSize = Vhdr->bmiHeader.biSizeImage;
+
+ return pmt;
+}
+
+#endif
+HRESULT SetVCrossbar(ICaptureGraphBuilder2* pBuilder,IBaseFilter* pBaseFilter)
+{
+ HRESULT hr;
+ long lOutPinCount,lInPinCount;
+ long lOutVideoPinIndex=-1, lInVideoPinIndex;
+ long lPhisicalPinInfo,lReleatedPin;
+ IAMCrossbar *pCrossbar=NULL;
+
+ do {
+
+ hr = pBuilder->vt->FindInterface(pBuilder,&MY_PIN_CATEGORY,&MEDIATYPE_Video,pBaseFilter, &IID_IAMCrossbar, (void **)&pCrossbar);
+
+ if (FAILED(hr)) break;
+
+ //???????? ????????? ???? ???????????
+ pCrossbar->vt->get_PinCounts(pCrossbar,&lOutPinCount,&lInPinCount);
+
+ for (lOutVideoPinIndex=0; lOutVideoPinIndex<lOutPinCount; lOutVideoPinIndex++) {
+ pCrossbar->vt->get_CrossbarPinInfo(pCrossbar,0,lOutVideoPinIndex,&lReleatedPin,&lPhisicalPinInfo);
+ if (lPhisicalPinInfo==PhysConn_Video_VideoDecoder) {
+ //????? ????? ????? ???????????
+ for(lInVideoPinIndex=0; lInVideoPinIndex<lInPinCount; lInVideoPinIndex++) {
+ pCrossbar->vt->get_CrossbarPinInfo(pCrossbar,-1,lInVideoPinIndex,&lReleatedPin,&lPhisicalPinInfo);
+ if (lPhisicalPinInfo==PhysConn_Video_Tuner) {
+ //????? ????????? ???? ????? ???????
+ hr=pCrossbar->vt->Route(pCrossbar,lOutVideoPinIndex,lInVideoPinIndex);//???????????...
+ break;
+ }
+ }
+ break;
+ }
+ }
+ } while (0);
+ if(pCrossbar) pCrossbar->vt->Release((IUnknown*)pCrossbar);
+ return hr;
+}
+
+#if 1
+int setFreq(priv_t* priv,long nFreq){
+ HRESULT hr;
+ int i;
+
+ if(!priv->pTVTuner) return TVI_CONTROL_FALSE;
+
+ nFreq=1000*nFreq/16;
+ for(i=0;i<chanlists[priv->tvh->chanlist].count;i++){
+ if (nFreq==chanlists[priv->tvh->chanlist].list[i].freq)
+ break;
+ }
+ if (i==chanlists[priv->tvh->chanlist].count){
+ showError("Channel not found","setFreq",E_FAIL);
+ return TVI_CONTROL_FALSE;
+ }
+ mp_msg(MSGT_TV,MSGL_ERR,"Setting channel: %d\n",i+1);
+ hr=priv->pTVTuner->vt->put_Channel(priv->pTVTuner,i+1,AMTUNER_SUBCHAN_DEFAULT,AMTUNER_SUBCHAN_DEFAULT);
+ if(FAILED(hr)){
+ showError("Cannot change freq","setFreq",hr);
+ return TVI_CONTROL_FALSE;
+ }
+ return TVI_CONTROL_TRUE;
+}
+#else
+#define INSTANCEDATA_OF_PROPERTY_PTR(x) (((KSPROPERTY*)(x)) + 1)
+#define INSTANCEDATA_OF_PROPERTY_SIZE(x) (sizeof((x)) - sizeof(KSPROPERTY))
+
+HRESULT SetFrequency(IAMTVTuner* m_pTVTuner,long Freq)
+{
+ HRESULT hr;
+ DWORD dwSupported=0;
+ KSPROPERTY_TUNER_MODE_CAPS_S ModeCaps;
+ KSPROPERTY_TUNER_FREQUENCY_S Frequency;
+ IKsPropertySet* m_pKSProp;
+
+ // Query the IKsPropertySet on your Device TV Tuner Filter.
+ // m_pTvtuner is IBaseFilter Pointer of your TV Tuner Filter.
+
+ hr = m_pTVTuner->vt->QueryInterface((IUnknown*)m_pTVTuner,&IID_IKsPropertySet, (void**)&m_pKSProp);
+ if (FAILED(hr)){
+ mp_msg(MSGT_TV,MSGL_ERR,"Set freq QueryInterface failure\n");
+ return E_FAIL;
+}
+ memset(&ModeCaps,0,sizeof(KSPROPERTY_TUNER_MODE_CAPS_S));
+ memset(&Frequency,0,sizeof(KSPROPERTY_TUNER_FREQUENCY_S));
+ ModeCaps.Mode = AMTUNER_MODE_TV;
+
+ // Check either the Property is supported or not by the Tuner drivers
+
+ hr = m_pKSProp->vt->QuerySupported(m_pKSProp,&PROPSETID_TUNER, KSPROPERTY_TUNER_MODE_CAPS,&dwSupported);
+ if(SUCCEEDED(hr) && dwSupported&KSPROPERTY_SUPPORT_GET)
+ {
+ DWORD cbBytes=0;
+ hr = m_pKSProp->vt->Get(m_pKSProp,&PROPSETID_TUNER,KSPROPERTY_TUNER_MODE_CAPS,
+ INSTANCEDATA_OF_PROPERTY_PTR(&ModeCaps),
+ INSTANCEDATA_OF_PROPERTY_SIZE(ModeCaps),
+ &ModeCaps,
+ sizeof(ModeCaps),
+ &cbBytes);
+ }
+ else{
+ mp_msg(MSGT_TV,MSGL_ERR,"Set freq QuerySupported failure\n");
+ return E_FAIL;
+}
+ Frequency.Frequency=Freq;
+ if(ModeCaps.Strategy==KS_TUNER_STRATEGY_DRIVER_TUNES)
+ Frequency.TuningFlags=KS_TUNER_TUNING_FINE;
+ else
+ Frequency.TuningFlags=KS_TUNER_TUNING_EXACT;
+
+ // Here the real magic starts
+
+ if(Freq>=ModeCaps.MinFrequency && Freq<=ModeCaps.MaxFrequency)
+ {
+ hr = m_pKSProp->vt->Set(m_pKSProp,&PROPSETID_TUNER,
+ KSPROPERTY_TUNER_FREQUENCY,
+ INSTANCEDATA_OF_PROPERTY_PTR(&Frequency),
+ INSTANCEDATA_OF_PROPERTY_SIZE(Frequency),
+ &Frequency,
+ sizeof(Frequency));
+ if(FAILED(hr)){
+ mp_msg(MSGT_TV,MSGL_ERR,"Set freq Set failure: %s (0x%x) %d\n\n",err2str(hr),hr,INSTANCEDATA_OF_PROPERTY_SIZE(Frequency) );
+ return E_FAIL; }
+ }
+ else{
+ mp_msg(MSGT_TV,MSGL_ERR,"Set freq MinMax failure %ld (%ld,%ld)\n",Freq,ModeCaps.MinFrequency ,ModeCaps.MaxFrequency);
+ return E_FAIL;
+}
+ return S_OK;
+}
+
+int setFreq(priv_t* priv,long nFreq){
+ HRESULT hr;
+ IUnknown* pUnk;
+
+
+ hr=SetFrequency(priv->pTVTuner,1000000*nFreq/16);
+ if(FAILED(hr)){
+ mp_msg(MSGT_TV,MSGL_ERR,"Set freq failure\n");
+ return TVI_CONTROL_FALSE;
+ }
+ return TVI_CONTROL_TRUE;
+}
+
+#endif
+
+/*====== REVIEWED CODE BEGIN ============================*/
+
+/*
+*---------------------------------------------------------------------------------------
+*
+* Grabbers
+*
+*---------------------------------------------------------------------------------------
+*/
+/*
+*---------------------------------------------------------------------------------------
+*
+* Methods, called only from this file
+*
+*---------------------------------------------------------------------------------------
+*/
+AM_MEDIA_TYPE* create_audio_media_type(int samplerate,int bits,int channels){
+ WAVEFORMATEX* pWF;
+ AM_MEDIA_TYPE* pmt;
+
+ pmt=(AM_MEDIA_TYPE*)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
+ if(!pmt){
+ CoTaskMemFree(pWF);
+ return NULL;
+ }
+
+ memset(pmt, 0, sizeof(AM_MEDIA_TYPE));
+
+ pmt->majortype=MEDIATYPE_Audio;
+ pmt->subtype=MEDIASUBTYPE_PCM;
+ pmt->formattype=FORMAT_WaveFormatEx;
+ pmt->bFixedSizeSamples=1;
+ pmt->bTemporalCompression=0;
+ pmt->pUnk = NULL;
+ pmt->lSampleSize=channels * bits /8;
+ pmt->cbFormat=0;
+ pmt->pbFormat=NULL;
+ return pmt;
+}
+/*****************************************************************
+ * \brief prints filter name and it pins
+ * \parameter pFilter - IBaseFilter to get data from
+ * \return S_OK if success, error code otherwise
+ *
+ */
+HRESULT show_filter_info(IBaseFilter *pFilter){
+ char tmp[200];
+ FILTER_INFO fi;
+ IEnumPins *pEnum = 0;
+ IPin *pPin = 0;
+ PIN_DIRECTION ThisPinDir;
+ PIN_INFO pi;
+ HRESULT hr;
+
+ memset(&fi,0,sizeof(fi));
+ memset(tmp,0,200);
+
+ pFilter->vt->QueryFilterInfo(pFilter,&fi);
+ if(fi.pGraph) fi.pGraph->vt->Release((IUnknown*)fi.pGraph);
+ wtoa(fi.achName,tmp,200);
+ mp_msg(MSGT_TV,MSGL_V,"tvi_dshow: BaseFilter (%p): Name=%s, Graph=%p\n",pFilter,tmp,fi.pGraph);
+ mp_msg(MSGT_TV,MSGL_V,"tvi_dshow: BaseFilter (%p) pins:",pFilter);
+ hr = pFilter->vt->EnumPins(pFilter,&pEnum);
+ if (FAILED(hr)) return hr;
+ while (pEnum->vt->Next(pEnum,1, &pPin, NULL) == S_OK)
+ {
+ memset(&pi,0,sizeof(pi));
+ memset(tmp,0,200);
+ pPin->vt->QueryDirection((IUnknown*)pPin,&ThisPinDir);
+ pPin->vt->QueryPinInfo(pPin,&pi);
+ wtoa(pi.achName,tmp,200);
+ pi.pFilter->vt->Release((IUnknown*)pi.pFilter);
+ mp_msg(MSGT_TV,MSGL_V,"%p (%s,%s);",pPin,ThisPinDir==PINDIR_INPUT?"in":"out",tmp);
+ pPin->vt->Release((IUnknown*)pPin);
+ }
+ mp_msg(MSGT_TV,MSGL_V,"\n");
+ pEnum->vt->Release((IUnknown*)pEnum);
+ return S_OK;
+}
+
+/*****************************************************************
+ * \brief gets device's frendly in ANSI encoding
+ * \parameter priv driver's private data structure
+ * \parameter lFreq - pointer to long int to store frequency to
+ * \return TVI_CONTROL_TRUE if success, TVI_CONTROL_FALSE otherwise
+ *
+ * TODO: replace parameter from V$L style frequency to Hz
+ */
+int get_frequency(priv_t* priv,long* lFreq){
+ long nChannel;
+ if(!priv->pTVTuner) return TVI_CONTROL_FALSE;
+ if(FAILED(priv->pTVTuner->vt->get_Channel(priv->pTVTuner,&nChannel,NULL,NULL)))
+ return TVI_CONTROL_FALSE;
+ if (nChannel>chanlists[priv->tvh->chanlist].count || nChannel<0)
+ return TVI_CONTROL_FALSE;
+
+ *lFreq=chanlists[priv->tvh->chanlist].list[nChannel].freq*16/1000;
+
+ return TVI_CONTROL_TRUE;
+}
+
+/*****************************************************************
+ * \brief gets device's frendly in ANSI encoding
+ * \parameter pM IMoniker interface, got in enumeration process
+ * \parameter category device category
+ * \return IBaseFilter interface for capture device with given index
+ *
+ * TODO: replace Moniker with some device interface
+ */
+void get_device_name(IMoniker* pM,char* pBuf,int nLen){
+ HRESULT hr;
+ VARIANT var;
+ IPropertyBag* pPropBag;
+ hr = pM->vt->BindToStorage(pM,0, 0, &IID_IPropertyBag, (void**)(&pPropBag));
+ if (FAILED(hr)) {
+ mp_msg(MSGT_TV,MSGL_ERR,"Call to BindToStorage failed\n");
+ return ;
+ }
+ var.vt=VT_BSTR;
+ hr = pPropBag->vt->Read(pPropBag,L"Description", (LPVARIANT)&var, NULL);
+ if (FAILED(hr))
+ {
+ hr = pPropBag->vt->Read(pPropBag,L"FriendlyName", (LPVARIANT)&var, NULL);
+ }
+ pPropBag->vt->Release((IUnknown*)pPropBag);
+ if (SUCCEEDED(hr))
+ {
+ wtoa(var.bstrVal,pBuf,nLen);
+ }
+}
+
+/*****************************************************************
+ * \brief find capture device at given index
+ * \parameter index device index to search for
+ * \parameter category device category
+ * \return IBaseFilter interface for capture device with given index
+ *
+ * Sample values for category:
+ * CLSID_VideoInputDeviceCategory - Video Capture Sources
+ * CLSID_AudioInputDeviceCategory - Audio Capture Sources
+ * See DirectShow SDK documentation for other possible values
+ */
+#define MSGSIZE_MAX 3072
+IBaseFilter* find_capture_device(int index,REFCLSID category){
+ IBaseFilter* pBaseFilter=NULL;
+ ICreateDevEnum *pDevEnum=NULL;
+ IEnumMoniker *pClassEnum=NULL;
+ IMoniker *pM;
+ HRESULT hr;
+ ULONG cFetched;
+ int i;
+ char tmp[MSGSIZE_MAX];
+ hr = CoCreateInstance((GUID*)&CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum, (void**)&pDevEnum);
+ if (FAILED(hr)){
+ mp_msg(MSGT_TV,MSGL_ERR,"Unable to create device enumerator\n");
+ return NULL;
+ }
+
+ hr = pDevEnum->vt->CreateClassEnumerator(pDevEnum,category, &pClassEnum,0);
+ pDevEnum->vt->Release((IUnknown*)pDevEnum);
+ if (FAILED(hr)){
+ mp_msg(MSGT_TV,MSGL_ERR,"Unable to create class enumerator\n");
+ return NULL;
+ }
+
+ pClassEnum->vt->Reset(pClassEnum);
+ for(i=0; pClassEnum->vt->Next(pClassEnum,1, &pM, &cFetched)==S_OK;i++){
+ get_device_name(pM,tmp,MSGSIZE_MAX-1);
+ mp_msg(MSGT_TV,MSGL_V,"tvi_dshow: Device #%d: %s\n",i,tmp);
+ if(i==index){
+ mp_msg(MSGT_TV,MSGL_INFO,"tvi_dshow: Using device #%d: %s\n",index,tmp);
+ hr = pM->vt->BindToObject(pM,0, 0, &IID_IBaseFilter, (void**)&pBaseFilter);
+ if (FAILED(hr))
+ pBaseFilter=NULL;
+ }
+ pM->vt->Release((IUnknown*)pM);
+ }
+ if(!pBaseFilter){
+ mp_msg(MSGT_TV,MSGL_ERR,"tvi_dshow: Device #%d not found\n",index);
+ }
+ pClassEnum->vt->Release((IUnknown*)pClassEnum);
+
+ return pBaseFilter;
+
+}
+
+/*****************************************************************
+ * \brief print tuner capabilities
+ * \parameter pTuner IAMTVtuner interface for capture device
+ *
+ * NOTE: currently only prints TV norms, supported by device
+ *
+ */
+void print_capabilities(IAMTVTuner* pTuner){
+ long lAvailableFormats;
+ HRESULT hr;
+ int i;
+
+ if(!pTuner) return ;
+
+ mp_msg(MSGT_TV, MSGL_INFO, " supported norms:");
+ hr= pTuner->vt->get_AvailableTVFormats(pTuner,&lAvailableFormats);
+ if (FAILED(hr)) { tv_available_norms_count=0; return ;}
+
+ for (i = 0;i<TV_NORMS_COUNT; i++) {
+ if (lAvailableFormats & tv_norms[i].index){
+ tv_available_norms[tv_available_norms_count]=i;
+ mp_msg(MSGT_TV, MSGL_INFO, " %d = %s;", tv_available_norms_count, tv_norms[i].name);
+ tv_available_norms_count++;
+ }
+ }
+ /* stub */
+ mp_msg(MSGT_TV, MSGL_INFO, "\n inputs:");
+ for(i=0;i<TV_INPUTS_COUNT;i++){
+ tv_available_inputs[tv_available_inputs_count]=i;
+ mp_msg(MSGT_TV, MSGL_INFO, " %d = %s;", tv_available_inputs_count, tv_inputs[i].name);
+ tv_available_inputs_count++;
+ }
+ mp_msg(MSGT_TV, MSGL_INFO, "\n");
+
+}
+
+/*
+*---------------------------------------------------------------------------------------
+*
+* Public methods
+*
+*---------------------------------------------------------------------------------------
+*/
+/*****************************************************************
+ * \brief fills given buffer with audio data (usually one block)
+ * \parameter priv driver's private data structure
+ * \parameter buffer buffer to store data to
+ * \parameter len buffer's size in bytes (usually one block size)
+ * \return audio pts if audio present, 1 - otherwise
+ *
+ */
+static double grab_audio_frame(priv_t *priv, char *buffer, int len)
+{
+ int bytes=0;
+ int i;
+ long long pts;
+ int total_bytes=0;
+ do{
+ bytes=priv->pGrabber->FillBuffer(priv->pGrabber,buffer+total_bytes,len-total_bytes,0,&pts);
+ mp_msg(MSGT_TV,MSGL_DBG3,"FillBuffer (audio) received %d bytes pts=%lld \n",bytes,pts);
+ total_bytes+=bytes;
+ } while (total_bytes<len);
+ return (double)1e-7*pts; // need real timer
+}
+
+/*****************************************************************
+ * \brief returns audio frame size
+ * \parameter priv driver's private data structure
+ * \return audio block size if audio enabled and 1 - otherwise
+ *
+ */
+static int get_audio_framesize(priv_t *priv)
+{
+ if(!priv->pmtAudio || !priv->pmtAudio->pbFormat) return 1;
+
+ return ((WAVEFORMATEX*)(priv->pmtAudio->pbFormat))->nAvgBytesPerSec/100;
+}
+
+/*****************************************************************
+ * \brief fills given buffer with video data (usually one frame)
+ * \parameter priv driver's private data structure
+ * \parameter buffer buffer to store data to
+ * \parameter len buffer's size in bytes (usually one frame size)
+ * \return frame size if video present, 0 - otherwise
+ *
+ */
+static double grab_video_frame(priv_t *priv, char *buffer, int len)
+{
+ int bytes=0;
+ int i;
+ long long pts;
+ if(!priv->pmtVideo){
+ memset(buffer,0,len);
+ return 1;
+ }
+ bytes=priv->pGrabber->FillBuffer(priv->pGrabber,buffer,len,1,&pts);
+ mp_msg(MSGT_TV,MSGL_DBG3,"FillBuffer (video) received %d pts=%lld bytes\n",bytes,pts);
+ return (double)1e-7*pts; // need real timer
+}
+
+/*****************************************************************
+ * \brief returns frame size
+ * \parameter priv driver's private data structure
+ * \return frame size if video present, 0 - otherwise
+ *
+ */
+static int get_video_framesize(priv_t *priv)
+{
+ if(!priv->pmtVideo) return 1; //no video
+ return(priv->pmtVideo->lSampleSize);
+}
+
+/*****************************************************************
+ * \brief playback/capture real start
+ * \parameter priv driver's private data structure
+ * \return 1 if success, 0 - otherwise
+ *
+ * TODO: move some code from init() here
+ */
+static int start(priv_t *priv)
+{
+ HRESULT hr;
+
+
+ if(set_media_format(priv,priv->pmtAudio)!=S_OK) return (0);
+ if(set_media_format(priv,priv->pmtVideo)!=S_OK) return (0);
+ /*
+ Graph ready to capture. Starting graph.
+ */
+ hr = priv->pMediaControl->vt->Run(priv->pMediaControl);
+ if (FAILED(hr))
+ {
+ mp_msg(MSGT_TV,MSGL_ERR,"Unable to start graph (error: 0x%08x)!\n",hr);
+ priv->pMediaControl->vt->Stop(priv->pMediaControl);
+ return 0;
+ }
+
+ priv->state=1;
+
+ return(1);
+exit:
+ return(0);
+}
+
+/*****************************************************************
+ * \brief driver initialization
+ * \parameter priv driver's private data structure
+ * \return 1 if success, 0 - otherwise
+ *
+ */
+static int init(priv_t *priv)
+{
+ HRESULT hr;
+ int result=0;
+
+ CoInitialize(NULL);
+
+ hr=CoCreateInstance ((GUID*)&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, (void **) &priv->pGraph);
+ EXIT_IF_FAILURE_HR(hr,"Directshow graph initialization failure","CoCreateInstance(CLSID_FilterGraph,IID_IGraphbuilder)");
+
+ hr=CoCreateInstance ((GUID*)&CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER, &IID_ICaptureGraphBuilder2, (void **) &priv->pBuilder);
+ EXIT_IF_FAILURE_HR(hr,"Directshow graph initialization failure","CaptureGraphBuilder");
+
+
+ hr=priv->pBuilder->vt->SetFiltergraph(priv->pBuilder,priv->pGraph);
+ EXIT_IF_FAILURE_HR(hr,"Directshow graph initialization failure","SetFiltergraph");
+
+ priv->pBaseFilter=find_capture_device(priv->dev_index,&CLSID_VideoInputDeviceCategory);
+ EXIT_IF_CONDITION(!priv->pBaseFilter,"Unable to find capture device","find_capture_device",E_POINTER);
+
+ mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: BaseFilter found (%p)\n",priv->pBaseFilter);
+
+ hr=priv->pGraph->vt->AddFilter(priv->pGraph,priv->pBaseFilter, NULL);
+ EXIT_IF_FAILURE_HR(hr,"Unable to add capture device to Directshow graph","AddFilter(pBaseFilter");
+
+ show_filter_info(priv->pBaseFilter);
+
+#if 0
+ hr=SetVCrossbar(priv->pBuilder,priv->pBaseFilter);
+ EXIT_IF_FAILURE_HR(hr,"Unable to set crossbar","");
+#endif
+#if 0
+ mp_msg(MSGT_TV,MSGL_V,"==== Video types ====\n");
+ display_device_media_types(priv,&MEDIATYPE_Video);
+ mp_msg(MSGT_TV,MSGL_V,"==== Audio types ====\n");
+ display_device_media_types(priv,&MEDIATYPE_Audio);
+#endif
+
+ hr = priv->pGraph->vt->QueryInterface((IUnknown*)priv->pGraph,&IID_IMediaControl, (void **)&(priv->pMediaControl));
+ EXIT_IF_FAILURE_HR(hr,"Unable create instance of IMediaControl","IGraphBuilder_QueryInterface(IID_IMediaCOntrol");
+
+ hr=priv->pBuilder->vt->FindInterface(priv->pBuilder,&MY_PIN_CATEGORY,NULL,priv->pBaseFilter,&IID_IAMTVTuner, (void **)&(priv->pTVTuner));
+ EXIT_IF_FAILURE_HR(hr,"Unable to access tuner!failed","FindInterface(IID_IAMTVTuner)");
+
+ mp_msg(MSGT_TV,MSGL_DBG3,"Got pTVTuner (%p)\n",priv->pTVTuner);
+ // shows Tuner capabilities
+ print_capabilities(priv->pTVTuner);
+
+ priv->pTVTuner->vt->put_CountryCode(priv->pTVTuner,chanlist2country(tv_param_chanlist));
+ priv->pTVTuner->vt->put_InputType(priv->pTVTuner,0,TunerInputAntenna);
+ hr=priv->pTVTuner->vt->put_Mode(priv->pTVTuner,AMTUNER_MODE_TV);
+
+ priv->pmtVideo=GetMediaType(priv->width,priv->height,priv->fcc);
+ priv->pmtAudio=create_audio_media_type(priv->samplerate,16,2);
+
+ priv->pGrabber=DS_MPGrabberCreate(priv->pmtVideo,priv->pmtAudio);
+ EXIT_IF_CONDITION(!priv->pGrabber,"Grabber create error","DS_MP_Grabber(create(pmt)",E_POINTER);
+
+ mp_msg(MSGT_TV,MSGL_DBG3,"DS_MPGrabber created (%p)\n",priv->pGrabber);
+
+ hr=priv->pGraph->vt->AddFilter(priv->pGraph,(IBaseFilter*)priv->pGrabber, NULL);
+ EXIT_IF_FAILURE_HR(hr,"Unable to add grabber to Directshow graph","AddFilter(gr)");
+
+ show_filter_info((IBaseFilter*)priv->pGrabber);
+
+ result=1;
+exit:
+ CoUninitialize();
+
+ if(!result) uninit(priv);
+
+ return result;
+}
+
+/*****************************************************************
+ * \brief driver uninitialization
+ * \parameter priv driver's private data structure
+ * \return always 1
+ *
+ */
+static int uninit(priv_t *priv)
+{
+
+ if (!priv) return (1);
+
+ //stop audio grabber thread
+
+ if(priv->pMediaControl){
+ priv->pMediaControl->vt->Stop(priv->pMediaControl);
+ priv->pMediaControl->vt->Release((IUnknown*)priv->pMediaControl);
+ priv->pMediaControl=NULL;
+ }
+
+ priv->state=0;
+
+ if(priv->pGrabber){
+ priv->pGrabber->vt->Release((IUnknown*)priv->pGrabber);
+ priv->pGrabber=NULL;
+ }
+ if(priv->pBaseFilter) {
+ priv->pBaseFilter->vt->Release((IUnknown*)priv->pBaseFilter);
+ priv->pBaseFilter=NULL;
+ }
+ if(priv->pGraph) {
+//This is failed. I dont know why.
+// IGraphBuilder_Release((IUnknown*)priv->pGraph);
+ priv->pGraph=NULL;
+ }
+
+ if(priv->pBuilder){
+// this is also failed!
+// priv->pBuilder->vt->Release((IUnknown*)priv->pBuilder);
+ priv->pBuilder=NULL;
+ }
+ if (priv->pmtVideo)
+ delete_media_type(priv->pmtVideo);
+ if (priv->pmtAudio)
+ delete_media_type(priv->pmtAudio);
+
+ return(1);
+}
+
+/*****************************************************************
+ * \brief driver pre-initialization
+ * \parameter device string, containing device name in form "x[.y]", where x is video capture device
+ * (default: 0, first available); y (if given) sets audio capture device
+ * \return 1 if success,0 - otherwise
+ *
+ */
+tvi_handle_t *tvi_init_dshow(char *device)
+{
+ tvi_handle_t *h;
+ priv_t *priv;
+ int a,b;
+
+ h = new_handle();
+ if (!h)
+ return(NULL);
+
+ priv = h->priv;
+ priv->tvh=h;
+
+ priv->pGraph=NULL;
+ priv->pBuilder=NULL;
+ priv->pTVTuner=NULL;
+ priv->pBaseFilter=NULL;
+ priv->pGrabber=NULL;
+ priv->pmtVideo=NULL;
+ priv->pmtAudio=NULL;
+ priv->pmtAudio=NULL;
+ priv->pmtVideo=NULL;
+ priv->dev_index=0;
+ priv->dev_pin=0;
+ priv->state=0;
+
+ priv->width=320;
+ priv->height=240;
+ priv->fcc=IMGFMT_YUY2;
+ priv->samplerate=44100;
+
+ if (tv_param_device){
+ if (sscanf(tv_param_device, "%d.%d", &a,&b)==2){
+ priv->dev_index=a;
+ priv->dev_pin=b;
+ }else if (sscanf(tv_param_device, "%d", &a)==1){
+ priv->dev_index=a;
+ }else{
+ mp_msg(MSGT_TV,MSGL_ERR,"Wrong device parameter: %s\n",tv_param_device);
+ free_handle(h);
+ return NULL;
+ }
+ if (priv->dev_index<0){
+ mp_msg(MSGT_TV,MSGL_ERR,"Wrong device index: %d\n",a);
+ free_handle(h);
+ return NULL;
+ }
+ }
+ return h;
+}
+
+/*****************************************************************
+ * \brief driver's ioctl handler
+ * \parameter priv driver's private data structure
+ * \parameter cmd ioctl command
+ * \parameter arg ioct command's parameter
+ * \return TVI_CONTROL_TRUE if success, TVI_CONTROL_FALSE if failure, TVI_CONTROL_UNKNOWN ifwhen unknowm cmd given
+ *
+ */
+static int control(priv_t *priv, int cmd, void *arg)
+{
+ switch(cmd)
+ {
+/* need rewrite */
+ case TVI_CONTROL_VID_SET_FORMAT:
+ {
+ if (priv->state) return TVI_CONTROL_FALSE;
+ priv->fcc = *(int *)arg;
+ return TVI_CONTROL_TRUE;
+ }
+ case TVI_CONTROL_VID_GET_FORMAT:
+ {
+ if(priv->fcc){
+ *(int *)arg = priv->fcc;
+ return(TVI_CONTROL_TRUE);
+ }else
+ return(TVI_CONTROL_FALSE);
+ }
+ case TVI_CONTROL_VID_SET_WIDTH:
+ {
+ if (priv->state) return TVI_CONTROL_FALSE;
+ priv->width = *(int *)arg;
+ return(TVI_CONTROL_FALSE);
+ }
+ case TVI_CONTROL_VID_GET_WIDTH:
+ {
+ if(priv->width){
+ *(int *)arg = priv->width;
+ return(TVI_CONTROL_TRUE);
+ }else
+ return TVI_CONTROL_FALSE;
+ }
+ case TVI_CONTROL_VID_SET_HEIGHT:
+ if (priv->state) return TVI_CONTROL_FALSE;
+ priv->height = *(int *)arg;
+ return(TVI_CONTROL_TRUE);
+ case TVI_CONTROL_VID_GET_HEIGHT:
+ {
+ if(priv->height){
+ *(int *)arg = priv->height;
+ return(TVI_CONTROL_TRUE);
+ }else
+ return TVI_CONTROL_FALSE;
+ }
+ case TVI_CONTROL_VID_CHK_WIDTH:
+ case TVI_CONTROL_VID_CHK_HEIGHT:
+ return(TVI_CONTROL_TRUE);
+ case TVI_CONTROL_IS_AUDIO:
+ return TVI_CONTROL_TRUE;
+ case TVI_CONTROL_IS_VIDEO:
+ return TVI_CONTROL_TRUE;
+ case TVI_CONTROL_AUD_GET_FORMAT:
+ {
+ *(int *)arg = AF_FORMAT_S16_LE;
+ return TVI_CONTROL_TRUE;
+ }
+ case TVI_CONTROL_AUD_GET_CHANNELS:
+ {
+ *(int *)arg = 2;
+ return TVI_CONTROL_TRUE;
+ }
+/* partually ready */
+ //currently i didn't find norm changing method
+ case TVI_CONTROL_TUN_SET_NORM:
+ return(TVI_CONTROL_TRUE);
+/* ready */
+ case TVI_CONTROL_AUD_SET_SAMPLERATE:
+ {
+ int samplerate=*(int*)arg;
+ AM_MEDIA_TYPE* pmt;
+ HRESULT hr;
+
+ pmt=create_audio_media_type(samplerate,16,2);
+ hr=set_media_format(priv,pmt);
+ if(FAILED(hr)) return TVI_CONTROL_FALSE;
+ priv->samplerate=samplerate;
+ return TVI_CONTROL_TRUE;
+ }
+ case TVI_CONTROL_AUD_GET_SAMPLERATE:
+ {
+ if(!priv->samplerate) return TVI_CONTROL_FALSE;
+ *(int *)arg = priv->samplerate;
+ return TVI_CONTROL_TRUE;
+ }
+ case TVI_CONTROL_AUD_GET_SAMPLESIZE:
+ {
+ WAVEFORMATEX* pWF;
+ if(!priv->pmtAudio) return TVI_CONTROL_FALSE;
+ if(!priv->pmtAudio->pbFormat) return TVI_CONTROL_FALSE;
+ pWF=(WAVEFORMATEX*)priv->pmtAudio->pbFormat;
+ *(int *)arg = pWF->wBitsPerSample / 8;
+ return TVI_CONTROL_TRUE;
+ }
+ case TVI_CONTROL_IS_TUNER:
+ {
+ if (!priv->pTVTuner)
+ return TVI_CONTROL_FALSE;
+
+ return (TVI_CONTROL_TRUE);
+ }
+ case TVI_CONTROL_TUN_GET_NORM:
+ {
+ long lAnalogFormat;
+ int i;
+ if(!priv->pTVTuner) return TVI_CONTROL_FALSE;
+ if (FAILED(priv->pTVTuner->vt->get_TVFormat(priv->pTVTuner,&lAnalogFormat)))
+ return TVI_CONTROL_FALSE;
+ for(i=0;i<tv_available_norms_count;i++){
+ if (tv_norms[tv_available_norms[i]].index == lAnalogFormat)
+ *(int *)arg = i;
+ return TVI_CONTROL_TRUE;
+ }
+ //currently i didn't find norm changing method
+ return(TVI_CONTROL_FALSE);
+ }
+ case TVI_CONTROL_SPC_GET_NORMID:
+ {
+ int i;
+ for(i=0;i<tv_available_norms_count;i++){
+ if (!strcasecmp(tv_norms[tv_available_norms[i]].name,(char *)arg))
+ *(int *)arg = i;
+ return TVI_CONTROL_TRUE;
+ }
+ return TVI_CONTROL_FALSE;
+ }
+ case TVI_CONTROL_TUN_GET_FREQ:
+ return get_frequency(priv,(unsigned long*)arg);
+ case TVI_CONTROL_TUN_SET_FREQ:
+ {
+ int nFreq=*(unsigned long *)arg;
+ return setFreq(priv,nFreq);
+ }
+ }
+ return(TVI_CONTROL_UNKNOWN);
+}
+
+/*====== REVIEWED CODE END ============================*/
Index: stream/tvi_dshow.h
===================================================================
--- stream/tvi_dshow.h (revision 0)
+++ stream/tvi_dshow.h (revision 0)
@@ -0,0 +1,547 @@
+#ifndef _H_TVI_DSHOW_H_
+#define _H_TVI_DSHOW_H_
+
+#include "loader/win32.h"
+#include "loader/dshow/guids.h"
+#include "loader/dshow/interfaces.h"
+
+#define GUID_DEFINED
+
+#include <basetyps.h>
+#include <winerror.h>
+
+STDAPI CoInitialize(PVOID);
+STDAPI_(void) CoUninitialize(void);
+
+extern const GUID CLSID_CaptureGraphBuilder;
+extern const GUID CLSID_CaptureGraphBuilder2;
+extern const GUID CLSID_FilterGraph;
+extern const GUID CLSID_SystemDeviceEnum;
+extern const GUID CLSID_VideoInputDeviceCategory;
+extern const GUID CLSID_AudioInputDeviceCategory;
+
+extern const GUID IID_IAMTVTuner;
+extern const GUID IID_IAMCrossbar;
+extern const GUID IID_ICaptureGraphBuilder;
+extern const GUID IID_ICaptureGraphBuilder2;
+extern const GUID IID_IFilterGraph;
+extern const GUID IID_ICreateDevEnum;
+extern const GUID IID_IPropertyBag;
+extern const GUID IID_IMediaControl;
+extern const GUID IID_IGraphBuilder;
+
+extern const GUID PIN_CATEGORY_CAPTURE;
+extern const GUID PIN_CATEGORY_PREVIEW;
+
+
+#ifndef BOOL
+#define BOOL unsigned char
+#define LPBOOL unsigned char*
+#endif
+
+
+#ifndef interface
+#define interface struct
+#endif
+
+#ifndef UINT
+#define UINT unsigned int
+#endif
+
+
+#ifndef ULONG
+#define ULONG unsigned long
+#endif
+
+#ifndef LONG
+#define LONG long
+#endif
+
+
+typedef LPCWSTR LPCOLESTR;
+typedef LPWSTR LPOLESTR;
+typedef WCHAR OLECHAR;
+typedef OLECHAR *BSTR;
+typedef LONG DISPID;
+typedef struct IUnknown* LPUNKNOWN;
+typedef struct IRecordInfo* LPRECORDINFO;
+typedef struct _IFileSinkFilter IFileSinkFilter;
+typedef struct _IAMCopyCaptureFileProgress IAMCopyCaptureFileProgress;
+
+#define wtoa(strW,strA,lenA) WideCharToMultiByte(0,0,strW,-1,strA,lenA,NULL,NULL)
+#define atow(strA,strW,lenW) MultiByteToWideChar(0,0,strA,strlen(strA),strW,lenW)
+typedef
+enum tagTunerInputType
+ { TunerInputCable = 0,
+ TunerInputAntenna = TunerInputCable + 1
+ } TunerInputType;
+typedef enum tagAMTunerModeType {
+ AMTUNER_MODE_DEFAULT = 0x0000,
+ AMTUNER_MODE_TV = 0x0001,
+ AMTUNER_MODE_FM_RADIO = 0x0002,
+ AMTUNER_MODE_AM_RADIO = 0x0004,
+ AMTUNER_MODE_DSS = 0x0008
+} AMTunerModeType;
+typedef
+enum tagAnalogVideoStandard
+ { AnalogVideo_None = 0,
+ AnalogVideo_NTSC_M = 0x1,
+ AnalogVideo_NTSC_M_J = 0x2,
+ AnalogVideo_NTSC_433 = 0x4,
+ AnalogVideo_PAL_B = 0x10,
+ AnalogVideo_PAL_D = 0x20,
+ AnalogVideo_PAL_G = 0x40,
+ AnalogVideo_PAL_H = 0x80,
+ AnalogVideo_PAL_I = 0x100,
+ AnalogVideo_PAL_M = 0x200,
+ AnalogVideo_PAL_N = 0x400,
+ AnalogVideo_PAL_60 = 0x800,
+ AnalogVideo_SECAM_B = 0x1000,
+ AnalogVideo_SECAM_D = 0x2000,
+ AnalogVideo_SECAM_G = 0x4000,
+ AnalogVideo_SECAM_H = 0x8000,
+ AnalogVideo_SECAM_K = 0x10000,
+ AnalogVideo_SECAM_K1 = 0x20000,
+ AnalogVideo_SECAM_L = 0x40000,
+ AnalogVideo_SECAM_L1 = 0x80000
+ } AnalogVideoStandard;
+
+typedef struct IDispatch* LPDISPATCH;
+typedef unsigned short VARTYPE;
+typedef short VARIANT_BOOL;
+typedef union tagCY {
+ struct {
+#ifdef WORDS_BIGENDIAN
+ LONG Hi;
+ ULONG Lo;
+#else
+ ULONG Lo;
+ LONG Hi;
+#endif
+ } DUMMYSTRUCTNAME;
+ LONGLONG int64;
+} CY;
+typedef struct tagDEC {
+ USHORT wReserved;
+ union {
+ struct {
+ BYTE scale;
+ BYTE sign;
+ } DUMMYSTRUCTNAME;
+ USHORT signscale;
+ } DUMMYUNIONNAME;
+ ULONG Hi32;
+ union {
+ struct {
+#ifdef WORDS_BIGENDIAN
+ ULONG Mid32;
+ ULONG Lo32;
+#else
+ ULONG Lo32;
+ ULONG Mid32;
+#endif
+ } DUMMYSTRUCTNAME1;
+ ULONGLONG Lo64;
+ } DUMMYUNIONNAME1;
+} DECIMAL;
+
+enum VARENUM {
+ VT_EMPTY,VT_NULL,VT_I2,VT_I4,VT_R4,VT_R8,VT_CY,VT_DATE,VT_BSTR,VT_DISPATCH,
+ VT_ERROR,VT_BOOL,VT_VARIANT,VT_UNKNOWN,VT_DECIMAL,VT_I1=16,VT_UI1,VT_UI2,VT_UI4,VT_I8,
+ VT_UI8,VT_INT,VT_UINT,VT_VOID,VT_HRESULT,VT_PTR,VT_SAFEARRAY,VT_CARRAY,VT_USERDEFINED,
+ VT_LPSTR,VT_LPWSTR,VT_RECORD=36,VT_FILETIME=64,VT_BLOB,VT_STREAM,VT_STORAGE,VT_STREAMED_OBJECT,
+ VT_STORED_OBJECT,VT_BLOB_OBJECT,VT_CF,VT_CLSID,VT_BSTR_BLOB=0xfff,VT_VECTOR=0x1000,
+ VT_ARRAY=0x2000,VT_BYREF=0x4000,VT_RESERVED=0x8000,VT_ILLEGAL= 0xffff,VT_ILLEGALMASKED=0xfff,
+ VT_TYPEMASK=0xfff
+};
+typedef struct tagSAFEARRAYBOUND {
+ ULONG cElements;
+ LONG lLbound;
+} SAFEARRAYBOUND, *LPSAFEARRAYBOUND;
+typedef struct tagSAFEARRAY {
+ USHORT cDims;
+ USHORT fFeatures;
+ ULONG cbElements;
+ ULONG cLocks;
+ PVOID pvData;
+ SAFEARRAYBOUND rgsabound[1];
+} SAFEARRAY;
+
+typedef SAFEARRAY *LPSAFEARRAY;
+
+
+typedef struct tagVARIANT VARIANT;
+typedef struct VARIANT* LPVARIANT;
+
+struct tagVARIANT {
+ union {
+ struct {
+ VARTYPE vt;
+ WORD wReserved1;
+ WORD wReserved2;
+ WORD wReserved3;
+ union {
+ signed char cVal;
+ USHORT uiVal;
+ ULONG ulVal;
+ INT intVal;
+ UINT uintVal;
+ BYTE bVal;
+ SHORT iVal;
+ LONG lVal;
+ FLOAT fltVal;
+ DOUBLE dblVal;
+ VARIANT_BOOL boolVal;
+ SCODE scode;
+ DATE date;
+ BSTR bstrVal;
+ CY cyVal;
+ LPUNKNOWN punkVal;
+ LPDISPATCH *pdispVal;
+ SAFEARRAY *parray;
+ LONGLONG llVal;
+ ULONGLONG ullVal;
+ signed char *pcVal;
+ USHORT *puiVal;
+ ULONG *pulVal;
+ INT *pintVal;
+ UINT *puintVal;
+ BYTE *pbVal;
+ SHORT *piVal;
+ LONG *plVal;
+ FLOAT *pfltVal;
+ DOUBLE *pdblVal;
+ VARIANT_BOOL *pboolVal;
+ SCODE *pscode;
+ DATE *pdate;
+ BSTR *pbstrVal;
+ VARIANT *pvarVal;
+ PVOID byref;
+ CY *pcyVal;
+ DECIMAL *pdecVal;
+ LPUNKNOWN *ppunkVal;
+ LPDISPATCH *ppdispVal;
+ SAFEARRAY **pparray;
+ LONGLONG *pllVal;
+ ULONGLONG *pullVal;
+ struct __tagBRECORD {
+ PVOID pvRecord;
+ LPRECORDINFO pRecInfo;
+ } ;
+ } ;
+ } ;
+ DECIMAL decVal;
+ } ;
+};
+
+typedef struct tagEXCEPINFO {
+ WORD wCode;
+ WORD wReserved;
+ BSTR bstrSource;
+ BSTR bstrDescription;
+ BSTR bstrHelpFile;
+ DWORD dwHelpContext;
+ PVOID pvReserved;
+ HRESULT(__stdcall * pfnDeferredFillIn)(struct tagEXCEPINFO*);
+ SCODE scode;
+} EXCEPINFO,*LPEXCEPINFO;
+
+typedef enum tagCLSCTX {
+ CLSCTX_INPROC_SERVER=1,CLSCTX_INPROC_HANDLER=2,CLSCTX_LOCAL_SERVER=4,
+ CLSCTX_INPROC_SERVER16=8,CLSCTX_REMOTE_SERVER=16
+} CLSCTX;
+typedef LONGLONG DWORDLONG;
+
+typedef long OAFilterState;
+
+typedef
+enum tagAMTunerSubChannel
+ { AMTUNER_SUBCHAN_NO_TUNE = -2,
+ AMTUNER_SUBCHAN_DEFAULT = -1
+ } AMTunerSubChannel;
+
+
+typedef
+enum tagPhysicalConnectorType
+ { PhysConn_Video_Tuner = 1,
+ PhysConn_Video_Composite = PhysConn_Video_Tuner + 1,
+ PhysConn_Video_SVideo = PhysConn_Video_Composite + 1,
+ PhysConn_Video_RGB = PhysConn_Video_SVideo + 1,
+ PhysConn_Video_YRYBY = PhysConn_Video_RGB + 1,
+ PhysConn_Video_SerialDigital = PhysConn_Video_YRYBY + 1,
+ PhysConn_Video_ParallelDigital = PhysConn_Video_SerialDigital + 1,
+ PhysConn_Video_SCSI = PhysConn_Video_ParallelDigital + 1,
+ PhysConn_Video_AUX = PhysConn_Video_SCSI + 1,
+ PhysConn_Video_1394 = PhysConn_Video_AUX + 1,
+ PhysConn_Video_USB = PhysConn_Video_1394 + 1,
+ PhysConn_Video_VideoDecoder = PhysConn_Video_USB + 1,
+ PhysConn_Video_VideoEncoder = PhysConn_Video_VideoDecoder + 1,
+ PhysConn_Video_SCART = PhysConn_Video_VideoEncoder + 1,
+ PhysConn_Video_Black = PhysConn_Video_SCART + 1,
+ PhysConn_Audio_Tuner = 0x1000,
+ PhysConn_Audio_Line = PhysConn_Audio_Tuner + 1,
+ PhysConn_Audio_Mic = PhysConn_Audio_Line + 1,
+ PhysConn_Audio_AESDigital = PhysConn_Audio_Mic + 1,
+ PhysConn_Audio_SPDIFDigital = PhysConn_Audio_AESDigital + 1,
+ PhysConn_Audio_SCSI = PhysConn_Audio_SPDIFDigital + 1,
+ PhysConn_Audio_AUX = PhysConn_Audio_SCSI + 1,
+ PhysConn_Audio_1394 = PhysConn_Audio_AUX + 1,
+ PhysConn_Audio_USB = PhysConn_Audio_1394 + 1,
+ PhysConn_Audio_AudioDecoder = PhysConn_Audio_USB + 1
+ } PhysicalConnectorType;
+
+typedef struct AMTunerNotification* LPAMTUNERNOTIFICATION;
+typedef struct IEnumFilters* LPENUMFILTERS;
+typedef struct IFileSinkFilter* LPFILESINKFILTER;
+typedef struct IStream* LPSTREAM;
+typedef struct IBindCtx* LPBINDCTX;
+typedef struct IErrorLog* LPERRORLOG;
+typedef struct ITypeInfo* LPTYPEINFO;
+typedef struct IAMCopyCaptureFileProgress* LPAMCOPYCAPTUREFILEPROGRESS;
+typedef struct DISPPARAMS* LPDISPPARAMS;
+
+typedef struct _IAMTVTuner IAMTVTuner;
+typedef struct IAMTVTuner_vt
+{
+ INHERIT_IUNKNOWN();
+ HRESULT STDCALL ( *put_Channel) (IAMTVTuner *, long,long,long) ;
+ HRESULT STDCALL ( *get_Channel) (IAMTVTuner *, long*,long*,long*) ;
+ HRESULT STDCALL ( *ChannelMinMax) (IAMTVTuner *, long*,long*) ;
+ HRESULT STDCALL ( *put_CountryCode) (IAMTVTuner *, long) ;
+ HRESULT STDCALL ( *get_CountryCode) (IAMTVTuner *, long*) ;
+ HRESULT STDCALL ( *put_TuningSpace) (IAMTVTuner *, long) ;
+ HRESULT STDCALL ( *get_TuningSpace) (IAMTVTuner *, long*) ;
+ HRESULT STDCALL ( *Logon) (IAMTVTuner *, HANDLE) ;
+ HRESULT STDCALL ( *Logout) (IAMTVTuner *) ;
+ HRESULT STDCALL ( *SignalPresen) (IAMTVTuner *, long*) ;
+ HRESULT STDCALL ( *put_Mode) (IAMTVTuner *, AMTunerModeType) ;
+ HRESULT STDCALL ( *get_Mode) (IAMTVTuner *, AMTunerModeType*) ;
+ HRESULT STDCALL ( *GetAvailableModes) (IAMTVTuner *, long*) ;
+ HRESULT STDCALL ( *RegisterNotificationCallBack)(IAMTVTuner *, LPAMTUNERNOTIFICATION,long) ;
+ HRESULT STDCALL ( *UnRegisterNotificationCallBack)(IAMTVTuner *, LPAMTUNERNOTIFICATION) ;
+ HRESULT STDCALL ( *get_AvailableTVFormats)(IAMTVTuner *, long*) ;
+ HRESULT STDCALL ( *get_TVFormat) (IAMTVTuner *, long*) ;
+ HRESULT STDCALL ( *AutoTune) (IAMTVTuner *, long,long*) ;
+ HRESULT STDCALL ( *StoreAutoTune) (IAMTVTuner *) ;
+ HRESULT STDCALL ( *get_NumInputConnections)(IAMTVTuner *, long*) ;
+ HRESULT STDCALL ( *put_InputType) (IAMTVTuner *, long,TunerInputType) ;
+ HRESULT STDCALL ( *get_InputType) (IAMTVTuner *, long,TunerInputType*) ;
+ HRESULT STDCALL ( *put_ConnectInput) (IAMTVTuner *, long) ;
+ HRESULT STDCALL ( *get_ConnectInput) (IAMTVTuner *, long*) ;
+ HRESULT STDCALL ( *get_VideoFrequency) (IAMTVTuner *, long*) ;
+ HRESULT STDCALL ( *get_AudioFrequency) (IAMTVTuner *, long*) ;
+} IAMTVTuner_vt;
+struct _IAMTVTuner { struct IAMTVTuner_vt* vt; };
+typedef struct _IMediaControl IMediaControl;
+typedef struct IMediaControl_vt
+{
+ INHERIT_IUNKNOWN();
+ HRESULT STDCALL ( *GetTypeInfoCount )(IMediaControl *, UINT *);
+ HRESULT STDCALL ( *GetTypeInfo )(IMediaControl *, UINT,LCID,LPTYPEINFO*);
+ HRESULT STDCALL ( *GetIDsOfNames )(IMediaControl *, REFIID,LPOLESTR *,UINT,LCID,DISPID *);
+ HRESULT STDCALL ( *Invoke )(IMediaControl *, DISPID ,REFIID ,LCID ,WORD ,LPDISPPARAMS ,VARIANT *,EXCEPINFO *,UINT *);
+ HRESULT STDCALL ( *Run )(IMediaControl *) ;
+ HRESULT STDCALL ( *Pause )(IMediaControl *) ;
+ HRESULT STDCALL ( *Stop )(IMediaControl *) ;
+ HRESULT STDCALL ( *GetState )(IMediaControl *, LONG,OAFilterState *);
+ HRESULT STDCALL ( *RenderFile )(IMediaControl *, BSTR);
+ HRESULT STDCALL ( *AddSourceFilter )(IMediaControl *, BSTR,LPDISPATCH *);
+ HRESULT STDCALL ( *get_FilterCollection )(IMediaControl *, LPDISPATCH*);
+ HRESULT STDCALL ( *get_RegFilterCollection )(IMediaControl *, LPDISPATCH*);
+ HRESULT STDCALL ( *StopWhenReady )(IMediaControl *) ;
+} IMediaControl_vt;
+struct _IMediaControl {struct IMediaControl_vt* vt;};
+
+typedef struct _IGraphBuilder IGraphBuilder;
+typedef struct IGraphBuilder_vt
+{
+ INHERIT_IUNKNOWN();
+ HRESULT STDCALL ( *AddFilter)(IGraphBuilder*, IBaseFilter*,LPCWSTR) ;
+ HRESULT STDCALL ( *RemoveFilter)(IGraphBuilder*, IBaseFilter*) ;
+ HRESULT STDCALL ( *EnumFilters)(IGraphBuilder*, LPENUMFILTERS*) ;
+ HRESULT STDCALL ( *FindFilterByName)(IGraphBuilder*, LPCWSTR,IBaseFilter**) ;
+ HRESULT STDCALL ( *ConnectDirect)(IGraphBuilder*, IPin*,IPin*,const AM_MEDIA_TYPE*) ;
+ HRESULT STDCALL ( *Reconnect)(IGraphBuilder*, IPin*) ;
+ HRESULT STDCALL ( *Disconnect)(IGraphBuilder*, IPin*) ;
+ HRESULT STDCALL ( *SetDefaultSyncSource)(IGraphBuilder*) ;
+ HRESULT STDCALL ( *Connect)(IGraphBuilder*, IPin* ,IPin*) ;
+ HRESULT STDCALL ( *Render)(IGraphBuilder*, IPin*) ;
+ HRESULT STDCALL ( *RenderFile)(IGraphBuilder*, LPCWSTR,LPCWSTR) ;
+ HRESULT STDCALL ( *AddSourceFilter)(IGraphBuilder*, LPCWSTR,LPCWSTR,IBaseFilter**) ;
+ HRESULT STDCALL ( *SetLogFile)(IGraphBuilder*, DWORD_PTR) ;
+ HRESULT STDCALL ( *Abort)(IGraphBuilder*) ;
+ HRESULT STDCALL ( *ShouldOperationContinue)(IGraphBuilder*) ;
+} IGraphBuilder_vt;
+struct _IGraphBuilder { struct IGraphBuilder_vt* vt; };
+
+typedef struct _ICaptureGraphBuilder ICaptureGraphBuilder;
+typedef struct ICaptureGraphBuilder_vt
+{
+ INHERIT_IUNKNOWN();
+ HRESULT STDCALL ( *SetFiltergraph)(ICaptureGraphBuilder*, IGraphBuilder*) ;
+ HRESULT STDCALL ( *GetFiltergraph)(ICaptureGraphBuilder*, IGraphBuilder**) ;
+ HRESULT STDCALL ( *SetOutputFileName)(ICaptureGraphBuilder*, const GUID*,LPCOLESTR,IBaseFilter**,LPFILESINKFILTER*) ;
+ HRESULT STDCALL ( *FindInterface)(ICaptureGraphBuilder*, const GUID*,IBaseFilter*,REFIID,void** ) ;
+ HRESULT STDCALL ( *RenderStream)(ICaptureGraphBuilder*, const GUID*,LPUNKNOWN,IBaseFilter*,IBaseFilter*) ;
+ HRESULT STDCALL ( *ControlStream)(ICaptureGraphBuilder*, const GUID*,IBaseFilter*,REFERENCE_TIME*,REFERENCE_TIME*,WORD,WORD) ;
+ HRESULT STDCALL ( *AllocCapFile)(ICaptureGraphBuilder*, LPCOLESTR,DWORDLONG) ;
+ HRESULT STDCALL ( *CopyCaptureFile)(ICaptureGraphBuilder*, LPOLESTR,LPOLESTR,int,LPAMCOPYCAPTUREFILEPROGRESS) ;
+} ICaptureGraphBuilder_vt;
+struct _ICaptureGraphBuilder {struct ICaptureGraphBuilder_vt* vt;};
+
+typedef struct _ICaptureGraphBuilder2 ICaptureGraphBuilder2;
+typedef struct ICaptureGraphBuilder2_vt
+{
+ INHERIT_IUNKNOWN();
+ HRESULT STDCALL ( *SetFiltergraph)(ICaptureGraphBuilder2* This,IGraphBuilder* pfg);
+ HRESULT STDCALL ( *GetFiltergraph)(ICaptureGraphBuilder2* This,IGraphBuilder** ppfg);
+ HRESULT STDCALL ( *SetOutputFileName)(ICaptureGraphBuilder2* This,const GUID* pType,LPCOLESTR lpstrFile,IBaseFilter** ppf,IFileSinkFilter** ppSink);
+ HRESULT STDCALL ( *FindInterface)(ICaptureGraphBuilder2* This,const GUID* pCategory,const GUID* pType,IBaseFilter* pf,REFIID riid,void** ppint);
+ HRESULT STDCALL ( *RenderStream)(ICaptureGraphBuilder2* This,const GUID* pCategory,const GUID* pType,IUnknown* pSource,IBaseFilter* pfCompressor,IBaseFilter* pfRenderer);
+ HRESULT STDCALL ( *ControlStream)(ICaptureGraphBuilder2* This,const GUID* pCategory,const GUID* pType,IBaseFilter* pFilter,REFERENCE_TIME* pstart,REFERENCE_TIME* pstop,WORD wStartCookie,WORD wStopCookie);
+ HRESULT STDCALL ( *AllocCapFile)(ICaptureGraphBuilder2* This,LPCOLESTR lpstr,DWORDLONG dwlSize);
+ HRESULT STDCALL ( *CopyCaptureFile)(ICaptureGraphBuilder2* This,LPOLESTR lpwstrOld,LPOLESTR lpwstrNew,int fAllowEscAbort,IAMCopyCaptureFileProgress* pCallback);
+ HRESULT STDCALL ( *FindPin)(ICaptureGraphBuilder2* This,IUnknown* pSource,PIN_DIRECTION pindir,const GUID* pCategory,const GUID* pType,BOOL fUnconnected,int num,IPin** ppPin);
+} ICaptureGraphBuilder2_vt;
+struct _ICaptureGraphBuilder2 {struct ICaptureGraphBuilder2_vt* vt;};
+
+typedef struct _IMoniker IMoniker;
+typedef struct _IEnumMoniker IEnumMoniker;
+
+typedef struct IMoniker_vt
+{
+ INHERIT_IUNKNOWN();
+ HRESULT STDCALL ( *GetClassID)(IMoniker*, LPCLSID) ;
+ HRESULT STDCALL ( *IsDirty)(IMoniker*) ;
+ HRESULT STDCALL ( *Load)(IMoniker*, LPSTREAM) ;
+ HRESULT STDCALL ( *Save)(IMoniker*, LPSTREAM,BOOL) ;
+ HRESULT STDCALL ( *GetSizeMax)(IMoniker*, PULARGE_INTEGER) ;
+ HRESULT STDCALL ( *BindToObject)(IMoniker*, LPBINDCTX,IMoniker*,REFIID,PVOID*) ;
+ HRESULT STDCALL ( *BindToStorage)(IMoniker*, LPBINDCTX,IMoniker*,REFIID,PVOID*) ;
+ HRESULT STDCALL ( *Reduce)(IMoniker*, LPBINDCTX,DWORD,IMoniker**,IMoniker**) ;
+ HRESULT STDCALL ( *ComposeWith)(IMoniker*, IMoniker*,BOOL,IMoniker**) ;
+ HRESULT STDCALL ( *Enum)(IMoniker*, BOOL,IEnumMoniker**) ;
+ HRESULT STDCALL ( *IsEqual)(IMoniker*, IMoniker*) ;
+ HRESULT STDCALL ( *Hash)(IMoniker*, PDWORD) ;
+ HRESULT STDCALL ( *IsRunning)(IMoniker*, LPBINDCTX,IMoniker*,IMoniker*) ;
+ HRESULT STDCALL ( *GetTimeOfLastChange)(IMoniker*, LPBINDCTX,IMoniker*,LPFILETIME) ;
+ HRESULT STDCALL ( *Inverse)(IMoniker*, IMoniker**) ;
+ HRESULT STDCALL ( *CommonPrefixWith)(IMoniker*, IMoniker*,IMoniker**) ;
+ HRESULT STDCALL ( *RelativePathTo)(IMoniker*, IMoniker*,IMoniker**) ;
+ HRESULT STDCALL ( *GetDisplayName)(IMoniker*, LPBINDCTX,IMoniker*,LPOLESTR*) ;
+ HRESULT STDCALL ( *ParseDisplayName)(IMoniker*, LPBINDCTX,IMoniker*,LPOLESTR,ULONG*,IMoniker**) ;
+ HRESULT STDCALL ( *IsSystemMoniker)(IMoniker*, PDWORD) ;
+} IMoniker_vt;
+struct _IMoniker {struct IMoniker_vt* vt;};
+
+typedef struct IEnumMoniker_vt
+{
+ INHERIT_IUNKNOWN();
+ HRESULT STDCALL ( *Next)(IEnumMoniker*,ULONG,IMoniker**,ULONG*) ;
+ HRESULT STDCALL ( *Skip)(IEnumMoniker*,ULONG) ;
+ HRESULT STDCALL ( *Reset)(IEnumMoniker*) ;
+ HRESULT STDCALL ( *Clone)(IEnumMoniker*,IEnumMoniker**) ;
+} IEnumMoniker_vt;
+struct _IEnumMoniker {struct IEnumMoniker_vt* vt;};
+
+typedef struct _ICreateDevEnum ICreateDevEnum;
+typedef struct ICreateDevEnum_vt
+{
+ INHERIT_IUNKNOWN();
+ HRESULT STDCALL ( *CreateClassEnumerator)(ICreateDevEnum*,REFCLSID,IEnumMoniker**,DWORD) ;
+} ICreateDevEnum_vt;
+struct _ICreateDevEnum {struct ICreateDevEnum_vt *vt;};
+
+typedef struct _IAMCrossbar IAMCrossbar;
+typedef struct IAMCrossbar_vt
+{
+ INHERIT_IUNKNOWN();
+ HRESULT STDCALL ( *get_PinCounts )( IAMCrossbar * This,long *OutputPinCount,long *InputPinCount);
+ HRESULT STDCALL ( *CanRoute )( IAMCrossbar * This,long OutputPinIndex,long InputPinIndex);
+ HRESULT STDCALL ( *Route )( IAMCrossbar * This,long OutputPinIndex,long InputPinIndex);
+ HRESULT STDCALL ( *get_IsRoutedTo )( IAMCrossbar * This,long OutputPinIndex,long *InputPinIndex);
+ HRESULT STDCALL ( *get_CrossbarPinInfo )( IAMCrossbar * This,BOOL IsInputPin,long PinIndex,long *PinIndexRelated,long *PhysicalType);
+} IAMCrossbar_vt;
+struct _IAMCrossbar {struct IAMCrossbar_vt* vt;};
+
+typedef struct _IPropertyBag IPropertyBag;
+typedef struct IPropertyBag_vt
+{
+ INHERIT_IUNKNOWN();
+ HRESULT STDCALL ( *Read)(IPropertyBag*, LPCOLESTR,LPVARIANT,LPERRORLOG) ;
+ HRESULT STDCALL ( *Write)(IPropertyBag*, LPCOLESTR,LPVARIANT) ;
+} IPropertyBag_vt;
+struct _IPropertyBag {struct IPropertyBag_vt* vt;};
+
+/*************************************************************************************/
+#define KSPROPERTY_SUPPORT_GET 1
+#define KSPROPERTY_SUPPORT_SET 2
+typedef struct {
+ GUID Set;
+ ULONG Id;
+ ULONG Flags;
+} KSIDENTIFIER;
+DEFINE_GUID(PROPSETID_TUNER, 0x6a2e0605, 0x28e4, 0x11d0, 0xa1, 0x8c, 0x00, 0xa0, 0xc9, 0x11, 0x89, 0x56);
+
+typedef KSIDENTIFIER KSPROPERTY;
+
+typedef struct {
+ KSPROPERTY Property;
+ ULONG Mode; // IN: KSPROPERTY_TUNER_MODE
+ ULONG StandardsSupported; // KS_AnalogVideo_* (if TV or DSS)
+ ULONG MinFrequency; // Hz
+ ULONG MaxFrequency; // Hz
+ ULONG TuningGranularity; // Hz
+ ULONG NumberOfInputs; // count of inputs
+ ULONG SettlingTime; // milliSeconds
+ ULONG Strategy; // KS_TUNER_STRATEGY
+} KSPROPERTY_TUNER_MODE_CAPS_S, *PKSPROPERTY_TUNER_MODE_CAPS_S;
+
+typedef struct {
+ KSPROPERTY Property;
+ ULONG Mode; // IN: KSPROPERTY_TUNER_MODE
+} KSPROPERTY_TUNER_MODE_S, *PKSPROPERTY_TUNER_MODE_S;
+
+typedef struct {
+ KSPROPERTY Property;
+ ULONG Frequency; // Hz
+ ULONG LastFrequency; // Hz (last known good)
+ ULONG TuningFlags; // KS_TUNER_TUNING_FLAGS
+ ULONG VideoSubChannel; // DSS
+ ULONG AudioSubChannel; // DSS
+ ULONG Channel; // VBI decoders
+ ULONG Country; // VBI decoders
+} KSPROPERTY_TUNER_FREQUENCY_S, *PKSPROPERTY_TUNER_FREQUENCY_S;
+typedef enum {
+ KS_TUNER_TUNING_EXACT = 1, // No fine tuning
+ KS_TUNER_TUNING_FINE, // Fine grained search
+ KS_TUNER_TUNING_COARSE, // Coarse search
+}KS_TUNER_TUNING_FLAGS;
+
+typedef enum {
+ KSPROPERTY_TUNER_CAPS, // R -overall device capabilities
+ KSPROPERTY_TUNER_MODE_CAPS, // R -capabilities in this mode
+ KSPROPERTY_TUNER_MODE, // RW -set a mode (TV, FM, AM, DSS)
+ KSPROPERTY_TUNER_STANDARD, // R -get TV standard (only if TV mode)
+ KSPROPERTY_TUNER_FREQUENCY, // RW -set/get frequency
+ KSPROPERTY_TUNER_INPUT, // RW -select an input
+ KSPROPERTY_TUNER_STATUS, // R -tuning status
+ KSPROPERTY_TUNER_IF_MEDIUM // R O-Medium for IF or Transport Pin
+} KSPROPERTY_TUNER;
+typedef enum {
+ KS_TUNER_STRATEGY_PLL = 0X01, // Tune by PLL offset
+ KS_TUNER_STRATEGY_SIGNAL_STRENGTH = 0X02, // Tune by signal strength
+ KS_TUNER_STRATEGY_DRIVER_TUNES = 0X04, // Driver does fine tuning
+}KS_TUNER_STRATEGY;
+
+typedef struct _IKsPropertySet IKsPropertySet;
+typedef struct IKsPropertySet_vt {
+ INHERIT_IUNKNOWN();
+ HRESULT (STDMETHODCALLTYPE *Set)(IKsPropertySet* This,REFGUID guidPropSet,DWORD dwPropID,LPVOID pInstanceData,DWORD cbInstanceData,LPVOID pPropData,DWORD cbPropData);
+ HRESULT (STDMETHODCALLTYPE *Get)(IKsPropertySet* This,REFGUID guidPropSet,DWORD dwPropID,LPVOID pInstanceData,DWORD cbInstanceData,LPVOID pPropData,DWORD cbPropData,DWORD* pcbReturned);
+ HRESULT (STDMETHODCALLTYPE *QuerySupported)(IKsPropertySet* This,REFGUID guidPropSet,DWORD dwPropID,DWORD* pTypeSupport);
+} IKsPropertySet_vt;
+struct _IKsPropertySet {struct IKsPropertySet_vt* vt;};
+
+
+#endif //_H_TVI_DSHOW_H_
More information about the MPlayer-dev-eng
mailing list