MSN Webcam patch

Jasper van Veghel vanveghel at home.nl
Mon Mar 14 21:21:38 CST 2005


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 at home.nl> -----

Date: Sat, 15 Jan 2005 14:18:47 +0100
From: Jasper van Veghel <vanveghel at home.nl>
To: xerox_xerox2000 at yahoo.co.uk, lgvlenders at 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 -----
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
Url : http://www.winehq.org/pipermail/wine-devel/attachments/20050315/93ca3330/attachment.pgp


More information about the wine-devel mailing list