I have to be off somewhere in a few minutes (hence me being up this early), so I'll make this quick ;-)
I wrote a couple of patches for webcam support too a while back, and gave these to some people who were asking for them, so I thought I'd share them here now, to also provide some input on it - mind you that I haven't worked on this for a while now, so it's not really as fresh in my memory anymore, but with this rece- nt interest in it I guess I'll start looking into it again too.
Of course MSN still crashes after it's signed in .. but that's beside the point here, really now ;-) Would be nice to get the webcam preview stuff working tho, and make some headway with V4L and Wine along the way.
Some of the things that you've changed I've changed, but aren't in the patches below - I really should document what I change as I go along ;-) Most of the major changes are there though. I also got stuck at qcap, but ended up simply using a native DLL for that. Again, I was really just tinkering along, seeing how far I could go without having to implement some major interface, using both native and builtin DLLs.
Anyway, I'll see if I can find some time tomorrow to look back into it; I did have a lot of fun hacking on Wine, and indespite of my inexperience regarding DirectX, I'll try to contribute back some more useful patches, hopefully ulti- mately getting that webcam support working.
Kind regards,
Jasper
----- Forwarded message from Jasper van Veghel vanveghel@home.nl -----
Date: Sat, 15 Jan 2005 14:18:47 +0100 From: Jasper van Veghel vanveghel@home.nl To: xerox_xerox2000@yahoo.co.uk, lgvlenders@yahoo.co.uk Subject: Re: video4linux - wine
Hey Luis, Robbert,
It's been a few weeks ago since I've worked on this stuff; I wrote some note- worthy patches and some hacks to get MSN working; I'll let you decide what's what ;-)
In dlls/wininet/ I made the following two changes; The first is to get it to connect to MSN (not camera-related), as it tries to send text over SSL for some reason.. not sure if this is necessary (anymore) though, with rsabase and all:
--- netconnection.c.old 2005-01-15 13:46:46.000000000 +0100 +++ netconnection.c 2004-12-14 22:38:19.000000000 +0100 @@ -104,7 +104,7 @@
void NETCON_init(WININET_NETCONNECTION *connection, BOOL useSSL) { - connection->useSSL = useSSL; + connection->useSSL = /*useSSL*/0; connection->socketFD = -1; if (connection->useSSL) {
Note the date on those files by the way; they were taken from CVS around 12/14/04, so it's not a complete diff to the recent CVS.
This one takes care of an unknown / undocumented optionId in InternetSetOptionAW that MSN uses in dlls/wininet/:
--- internet.c.old 2005-01-15 13:55:56.000000000 +0100 +++ internet.c 2004-12-14 17:29:42.000000000 +0100 @@ -1962,6 +1962,9 @@ case INTERNET_OPTION_CONNECTED_STATE: FIXME("Option INTERNET_OPTION_CONNECTED_STATE: STUB\n"); break; + case 87: + FIXME("Undocumented option 87: STUB\n"); + break; default: FIXME("Option %ld STUB\n",dwOption); INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
Of more interest might be this change to dlls/devenum/createdevenum.c:
--- createdevenum.c.old 2005-01-15 14:02:24.000000000 +0100 +++ createdevenum.c 2004-12-27 22:07:27.000000000 +0100 @@ -117,7 +117,8 @@
if (IsEqualGUID(clsidDeviceClass, &CLSID_AudioRendererCategory) || IsEqualGUID(clsidDeviceClass, &CLSID_AudioInputDeviceCategory) || - IsEqualGUID(clsidDeviceClass, &CLSID_MidiRendererCategory)) + IsEqualGUID(clsidDeviceClass, &CLSID_MidiRendererCategory) || + IsEqualGUID(clsidDeviceClass, &CLSID_VideoInputDeviceCategory)) { hbasekey = HKEY_CURRENT_USER; strcpyW(wszRegKey, wszActiveMovieKey); @@ -142,7 +143,8 @@ { if (IsEqualGUID(clsidDeviceClass, &CLSID_AudioRendererCategory) || IsEqualGUID(clsidDeviceClass, &CLSID_AudioInputDeviceCategory) || - IsEqualGUID(clsidDeviceClass, &CLSID_MidiRendererCategory)) + IsEqualGUID(clsidDeviceClass, &CLSID_MidiRendererCategory) || + IsEqualGUID(clsidDeviceClass, &CLSID_VideoInputDeviceCategory)) { HRESULT hr = DEVENUM_CreateSpecialCategories(); if (FAILED(hr)) @@ -411,6 +413,55 @@ CoTaskMemFree(pTypes); } } + + res = DEVENUM_CreateAMCategoryKey(&CLSID_VideoInputDeviceCategory); + if (SUCCEEDED(res)) { /* can register device(s) in this category */ + for (i = 0; i < 10; i++) { // the index can range from 0 thru 9 + WCHAR szDeviceName[80], szDeviceVersion[80]; + + if (capGetDriverDescriptionW ((WORD) i, + szDeviceName, sizeof (szDeviceName), + szDeviceVersion, sizeof (szDeviceVersion))) { + IMoniker * pMoniker = NULL; + + TRACE("\nSuccess !\n"); + + rfp2.nMediaTypes = 1; + pTypes = CoTaskMemAlloc(rfp2.nMediaTypes * + sizeof(REGPINTYPES)); + if (!pTypes) { + IFilterMapper2_Release(pMapper); + + return E_OUTOFMEMORY; + } + + /* FIXME: Not sure if these are correct */ + pTypes[0].clsMajorType = &MEDIATYPE_Video; + pTypes[0].clsMinorType = &MEDIASUBTYPE_RGB24; + + rfp2.lpMediaType = pTypes; + + res = IFilterMapper2_RegisterFilter(pMapper, + // &CLSID_VideoRenderer, + &CLSID_VfwCapture, + szDeviceName, + &pMoniker, + &CLSID_VideoInputDeviceCategory, + szDeviceName, + &rf2); + + /* FIXME: do additional stuff with IMoniker here, depending on what RegisterFilter does */ + + if (pMoniker) + IMoniker_Release(pMoniker); + + if (i == iDefaultDevice) + FIXME("Default device\n"); + + CoTaskMemFree(pTypes); + } + } + } }
if (pMapper)
And a small addition to devenum_private.h:
--- devenum_private.h.old 2005-01-15 14:06:39.000000000 +0100 +++ devenum_private.h 2004-12-26 22:01:19.000000000 +0100 @@ -26,6 +26,7 @@
#include "windef.h" #include "winbase.h" +#include "wingdi.h" #include "winuser.h" #include "winreg.h" #include "winerror.h" @@ -38,6 +39,7 @@ #include "olectl.h" #include "wine/unicode.h" #include "uuids.h" +#include "vfw.h"
/********************************************************************** * Dll lifetime tracking declaration for devenum.dll
And the Makefile (dlls/devenum/Makefile.in):
--- Makefile.in.old 2005-01-15 14:11:31.000000000 +0100 +++ Makefile.in 2005-01-15 14:13:36.000000000 +0100 @@ -3,7 +3,7 @@ SRCDIR = @srcdir@ VPATH = @srcdir@ MODULE = devenum.dll -IMPORTS = ole32 oleaut32 winmm user32 advapi32 kernel32 +IMPORTS = ole32 oleaut32 winmm avicap32 user32 advapi32 kernel32 EXTRALIBS = -lstrmiids -luuid
C_SRCS = \
And so we add some to capGetDriverDescrptionAW aswell (dlls/avicap32/); I just added some static labels, not really getting these anywhere yet:
--- avicap32_main.c.old 2005-01-15 14:15:43.000000000 +0100 +++ avicap32_main.c 2004-12-27 22:21:22.000000000 +0100 @@ -69,11 +69,22 @@ BOOL VFWAPI capGetDriverDescriptionA(WORD wDriverIndex, LPSTR lpszName, INT cbName, LPSTR lpszVer, INT cbVer) { - FIXME("(%d, %p, %d, %p, %d) stub!\n", wDriverIndex, lpszName, cbName, + FIXME("stub: (%d, %p, %d, %p, %d) stub!\n", wDriverIndex, lpszName, cbName, lpszVer, cbVer); - return FALSE; + + // FIXME: Make use of Video4Linux here + + strncpy (lpszName, "Logitech QuickCam Pro 4000", cbName); + strncpy (lpszVer, "Wine Video4Linux Driver", cbVer); + + return TRUE; }
+WCHAR x1[] = {'L','o','g','i','t','e','c','h',' ','Q','u','i','c','k','c','a', + 'm',' ','P','r','o',' ','4','0','0','0',0}, + x2[] = {'W','i','n','e',' ','V','i','d','e','o','4','L','i','n','u','x', + ' ','d','r','i','v','e','r',0}; + /*********************************************************************** * capGetDriverDescriptionW (AVICAP32.@) */ @@ -82,5 +93,11 @@ { FIXME("(%d, %p, %d, %p, %d) stub!\n", wDriverIndex, lpszName, cbName, lpszVer, cbVer); - return FALSE; + + // FIXME: Make use of Video4Linux here + + lstrcpynW (lpszName, x1, cbName); + lstrcpynW (lpszVer, x2, cbVer); + + return TRUE; }
And the dlls/avicap32/Makefile.in changes for the strcpy stuff:
--- Makefile.in.old 2005-01-15 14:15:34.000000000 +0100 +++ Makefile.in 2004-12-26 22:55:13.000000000 +0100 @@ -3,7 +3,7 @@ SRCDIR = @srcdir@ VPATH = @srcdir@ MODULE = avicap32.dll -IMPORTS = ntdll +IMPORTS = unicows ntdll
C_SRCS = avicap32_main.c
Next up is dlls/quartz, which seemed to handle the DirectShow filter thingies; first off there's include/config.idl:
--- control.idl.old 2005-01-15 15:07:23.000000000 +0100 +++ control.idl 2004-12-27 04:25:25.000000000 +0100 @@ -26,6 +26,7 @@ interface IBasicAudio; interface IBasicVideo; interface IVideoWindow; +interface IAMStreamConfig; interface IMediaEvent; interface IMediaEventEx;
@@ -123,6 +124,22 @@ HRESULT IsCursorHidden( [out] long *CursorHidden ); }
+/***************************************************************************** + * IAMStreamConfig interface + */ +[ + object, + uuid(c6e13340-30ac-11d0-a18c-00a0c9118956), + pointer_default(unique) +] +interface IAMStreamConfig : IUnknown +{ + HRESULT SetFormat( [in] AM_MEDIA_TYPE *pmt); + HRESULT GetFormat( [in] AM_MEDIA_TYPE **pmt); + HRESULT GetNumberOfCapabilities( [out] int *piCount, [out] int *piSize); + HRESULT GetStreamCaps( [in] int iIndex, [out] AM_MEDIA_TYPE **pmt, + [out] BYTE *pSCC); +}
/***************************************************************************** * IBasicVideo interface
And of course the generated interface from this, added to videorenderer.c, plus some partial implementation stuff (if someone could finish this, plus add an implementation for IKsPin, they'd probably be well on their way (provided that is that it's actually coupled to a capturing device, rather then have bogus data in it as I've done here))
--- videorenderer.c.old 2005-01-15 14:39:24.000000000 +0100 +++ videorenderer.c 2004-12-27 21:15:01.000000000 +0100 @@ -48,12 +48,14 @@ static IBasicVideoVtbl IBasicVideo_VTable; static IVideoWindowVtbl IVideoWindow_VTable; static const IPinVtbl VideoRenderer_InputPin_Vtbl; +static IAMStreamConfigVtbl IAMStreamConfig_VTable;
typedef struct VideoRendererImpl { const IBaseFilterVtbl * lpVtbl; IBasicVideoVtbl * IBasicVideo_vtbl; IVideoWindowVtbl * IVideoWindow_vtbl; + IAMStreamConfigVtbl * IAMStreamConfig_vtbl;
ULONG refCount; CRITICAL_SECTION csFilter; @@ -327,6 +329,7 @@ pVideoRenderer->lpVtbl = &VideoRenderer_Vtbl; pVideoRenderer->IBasicVideo_vtbl = &IBasicVideo_VTable; pVideoRenderer->IVideoWindow_vtbl = &IVideoWindow_VTable; + pVideoRenderer->IAMStreamConfig_vtbl = &IAMStreamConfig_VTable;
pVideoRenderer->refCount = 1; InitializeCriticalSection(&pVideoRenderer->csFilter); @@ -378,6 +381,8 @@ *ppv = (LPVOID)&(This->IBasicVideo_vtbl); else if (IsEqualIID(riid, &IID_IVideoWindow)) *ppv = (LPVOID)&(This->IVideoWindow_vtbl); + else if (IsEqualIID(riid, &IID_IAMStreamConfig)) + *ppv = (LPVOID)&(This->IAMStreamConfig_vtbl);
if (*ppv) { @@ -1556,3 +1561,172 @@ Videowindow_HideCursor, Videowindow_IsCursorHidden }; + + +/*** IAMStreamConfig methods ***/ +static HRESULT WINAPI AMStreamConfig_QueryInterface(IAMStreamConfig *iface, + REFIID riid, + LPVOID*ppvObj) { + ICOM_THIS_MULTI(VideoRendererImpl, IAMStreamConfig_vtbl, iface); + + TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj); + + // return AMStreamConfig_QueryInterface((IUnknown*)This, riid, ppvObj); + return S_OK; +} + +static ULONG WINAPI AMStreamConfig_AddRef(IAMStreamConfig *iface) { + ICOM_THIS_MULTI(VideoRendererImpl, IAMStreamConfig_vtbl, iface); + + TRACE("(%p/%p)->()\n", This, iface); + + // return AMStreamConfig_AddRef((IUnknown*)This); + return S_OK; +} + +static ULONG WINAPI AMStreamConfig_Release(IAMStreamConfig *iface) { + ICOM_THIS_MULTI(VideoRendererImpl, IAMStreamConfig_vtbl, iface); + + TRACE("(%p/%p)->()\n", This, iface); + + // return AMStreamConfig_Release((IUnknown*)This); + return S_OK; +} + +static HRESULT WINAPI AMStreamConfig_SetFormat(IAMStreamConfig *iface, + AM_MEDIA_TYPE *pmt) { + ICOM_THIS_MULTI(VideoRendererImpl, IAMStreamConfig_vtbl, iface); + + TRACE("(%p/%p): stub !!\n", This, iface); + + return S_OK; +} + +static HRESULT WINAPI AMStreamConfig_GetFormat(IAMStreamConfig *iface, + AM_MEDIA_TYPE **pmt) { + ICOM_THIS_MULTI(VideoRendererImpl, IAMStreamConfig_vtbl, iface); + + TRACE("(%p/%p): stub !!\n", This, iface); + + return S_OK; +} + +typedef struct _VIDEO_STREAM_CONFIG_CAPS { + GUID guid; + ULONG VideoStandard; + SIZE InputSize; + SIZE MinCroppingSize; + SIZE MaxCroppingSize; + int CropGranularityX; + int CropGranularityY; + int CropAlignX; + int CropAlignY; + SIZE MinOutputSize; + SIZE MaxOutputSize; + int OutputGranularityX; + int OutputGranularityY; + int StretchTapsX; + int StretchTapsY; + int ShrinkTapsX; + int ShrinkTapsY; + LONGLONG MinFrameInterval; + LONGLONG MaxFrameInterval; + LONG MinBitsPerSecond; + LONG MaxBitsPerSecond; +} VIDEO_STREAM_CONFIG_CAPS; + +static HRESULT WINAPI AMStreamConfig_GetNumberOfCapabilities(IAMStreamConfig *iface, + int *piCount, + int *piSize) { + ICOM_THIS_MULTI(VideoRendererImpl, IAMStreamConfig_vtbl, iface); + + TRACE("((%p/%p): %p %p): semi-stub !!\n", This, iface, piCount, piSize); + + *piCount = 1; + *piSize = sizeof(VIDEO_STREAM_CONFIG_CAPS); + + return S_OK; +} + +static HRESULT WINAPI AMStreamConfig_GetStreamCaps(IAMStreamConfig *iface, + int iIndex, + AM_MEDIA_TYPE **pmt, + BYTE *pSCC) { + ICOM_THIS_MULTI(VideoRendererImpl, IAMStreamConfig_vtbl, iface); + VIDEOINFOHEADER *pvi; + VIDEO_STREAM_CONFIG_CAPS *vsc; + + TRACE("((%p/%p) %d %p %p): semi-stub !!\n", This, iface, iIndex, pmt, pSCC); + + // FIXME: Get actual stream information in here (IEnumMediaTypes ?) + + pmt[0] = CoTaskMemAlloc(sizeof (AM_MEDIA_TYPE)); + memcpy (&pmt[0]->formattype, &FORMAT_VideoInfo, sizeof (GUID)); + memcpy (&pmt[0]->majortype, &MEDIATYPE_Video, sizeof (GUID)); + memcpy (&pmt[0]->subtype, &MEDIASUBTYPE_RGB24, sizeof (GUID)); + pmt[0]->bTemporalCompression = 0; + pmt[0]->bFixedSizeSamples = 1; + pmt[0]->lSampleSize = 1; + + // FIXME: The stuff below is just there so it contains something + + pmt[0]->cbFormat = sizeof(VIDEOINFOHEADER); + pmt[0]->pbFormat = CoTaskMemAlloc(pmt[0]->cbFormat); + ZeroMemory(pmt[0]->pbFormat, pmt[0]->cbFormat); + pvi = (VIDEOINFOHEADER *)pmt[0]->pbFormat; + pvi->rcSource.right = 640; pvi->rcSource.bottom = 480; + pvi->rcTarget.right = 640; pvi->rcTarget.bottom = 480; + pvi->dwBitRate = 10; + pvi->dwBitErrorRate = 0; + pvi->AvgTimePerFrame = (LONGLONG)(10000000.0 / 25); // 25 is hardcoded + + // FIXME: The stuff below is just there so it contains something + + pvi->bmiHeader.biWidth = 640; + pvi->bmiHeader.biHeight = 480; + pvi->bmiHeader.biBitCount = 24; + pvi->bmiHeader.biCompression = BI_RGB; + pvi->bmiHeader.biSizeImage = 0; + pvi->bmiHeader.biXPelsPerMeter = 640; + pvi->bmiHeader.biYPelsPerMeter = 480; + pvi->bmiHeader.biClrUsed = 0; + pvi->bmiHeader.biClrImportant = 0; + + // FIXME: Blahblahblah + + vsc = (VIDEO_STREAM_CONFIG_CAPS *)pSCC; + memcpy (&vsc->guid, &FORMAT_VideoInfo, sizeof (GUID)); + vsc->VideoStandard = 0; + vsc->InputSize.cx = 640; vsc->InputSize.cy = 480; + vsc->MinCroppingSize.cx = 320; vsc->MinCroppingSize.cy = 240; + vsc->MaxCroppingSize.cx = 640; vsc->MaxCroppingSize.cy = 480; + vsc->CropGranularityX = 10; + vsc->CropGranularityY = 10; + vsc->CropAlignX = 0; + vsc->CropAlignY = 0; + vsc->MinOutputSize.cx = 320; vsc->MinOutputSize.cy = 240; + vsc->MaxOutputSize.cx = 640; vsc->MaxOutputSize.cy = 480; + vsc->OutputGranularityX = 10; + vsc->OutputGranularityY = 10; + vsc->StretchTapsX = 1; + vsc->StretchTapsY = 1; + vsc->ShrinkTapsX = 1; + vsc->ShrinkTapsY = 1; + vsc->MinFrameInterval = (LONGLONG)(10000000.0 / 25); + vsc->MaxFrameInterval = (LONGLONG)(10000000.0 / 25); + vsc->MinBitsPerSecond = 24; + vsc->MaxBitsPerSecond = 24; + + return S_OK; +} + +static IAMStreamConfigVtbl IAMStreamConfig_VTable = +{ + AMStreamConfig_QueryInterface, + AMStreamConfig_AddRef, + AMStreamConfig_Release, + AMStreamConfig_SetFormat, + AMStreamConfig_GetFormat, + AMStreamConfig_GetNumberOfCapabilities, + AMStreamConfig_GetStreamCaps +};
As for Quartz; there's been a lot of development over the last couple of weeks on this, especially on the FilterMapper, regsvr, AVI stuff, videorenderer, etc., so that aswell as future development might prove to be quite useful in getting this to work.
Anyway, that's pretty much all there is; I wrote some other hacks to various DLLs, but eventually ended up using native DLLs instead as the hacking proved too extensive (sorry to say, my goal at that point was getting my webcam to work under MSN with Wine, not to add anything useful to Wine itself (though it would have been a nice side-effect)) Hopefully the patches were of some use to you, and perhaps someone can get V4L/VFW working under WINE. I, for one, would cer- tainly appreciate it :-)
Best Regards,
Jasper
Robbert Xerox wrote:
Hi, i was wondering if you could post your hacks/patches you talk about in the message (involving this webcam/MSN messenger etc. stuff) either to the wine developement mailinglist or to me. I would be very gratefull as i really would like to have a look at it, to see if i could get any further. Thanks in advance, Robbert
___________________________________________________________ ALL-NEW Yahoo! Messenger - all new features - even more fun! http://uk.messenger.yahoo.com
----- End forwarded message -----