VfwCapture question (and attached a v4l2 patch for msn 6.2 webcam - Attempt 1)

Maarten Lankhorst m.b.lankhorst at gmail.com
Tue Apr 19 14:57:55 CDT 2005


Now that I'm pretty much done with cleaning up the stuff, I'm wondering 
on how to seperate /dev/video0 from /dev/video1, in the current code I 
don't have the seperation, and I'm unsure on how to add it...

Anyway, this is my first attempt to get v4l2 to work with msn 6.2, it is 
very unstable but it somehow.. works..
to transmit images over the web though,
here's what the patch does:
- add a lot more instability (sad but true)
- adds some routines to avicap32, so capGetDriverDescription works 
(partially.. currently only v4l1)
- makes devenum create videoinputdevices based on the information from 
capGetDriverDescription, native devenum does the same
- add a stub capturegraphbuilder (minimum required for msn - 
capturegraphbuilder.c)
- add a semi-stubbed vfwcapture interface (v4wsource.c and capture.c)

Currently it requires a video4linux2 device for the vfwcapture 
interface, but since the bttv driver supports v4l1 too, I'll work on it.
The code will crash when transmission is halted (because of the testbed 
thread not being stopped), but I thought I would add this so you would 
know I'm still working on it :).
to compile the code, make sure you have 
/usr/include/linux/videodev{,2}.h and then compile with make CC="gcc 
-DENABLE_V4L"
Don't expect to much from it.. oh and you need to use the native 
msvfw32.dll to native to transmit images.. you can probably steal it 
from a windows installation or download it from dll-files...
I made a screenshot of the preview screen at 
http://vladserver.ath.cx/MSN.jpg - II actully transmitted too, frame 
counter was at 7846 until it crashed (Destroying VfwCapture, TestBed 
thread crashed)

But if you really want to help, can someone _*PLEASE*_ figure out why it 
crashes if it's started with WINEDEBUG=+quartz and when you go to webcam 
settings screen? you can save me a lot of frustration.. thanks!!!
-------------- next part --------------
diff -Nru wine-old/dlls/avicap32/avicap32_main.c wine-new/dlls/avicap32/avicap32_main.c
--- wine-old/dlls/avicap32/avicap32_main.c	2003-09-09 21:39:32.000000000 +0200
+++ wine-new/dlls/avicap32/avicap32_main.c	2005-04-19 12:55:40.000000000 +0200
@@ -28,6 +28,18 @@
 #include "winternl.h"
 #include "wine/debug.h"
 
+#ifdef ENABLE_V4L
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <asm/types.h>
+#include <linux/videodev.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "winnls.h"
+#endif
+
 WINE_DEFAULT_DEBUG_CHANNEL(avicap);
 
 
@@ -63,15 +75,82 @@
     return retW;
 }
 
+#ifdef ENABLE_V4L
+static int xioctl(int fd, int request, void * arg)
+{
+   int r;
+
+   do r = ioctl (fd, request, arg);
+   while (-1 == r && EINTR == errno);
+
+   return r;
+}
+
+static int queryvideodevice(int devnum, struct video_capability* capa)
+{
+   int fd = -1;
+   char device[128];
+   struct stat st;
+   sprintf(device, "/dev/video%i", devnum);
+
+   if (stat (device, &st) == -1) {
+      ERR("%s: %s\n", device, strerror(errno));
+      return -1;
+   }
+
+   if (!S_ISCHR (st.st_mode)) {
+      ERR("%s: Not a device\n", device);
+      return -1;
+   }
+
+   fd = open(device, O_RDWR | O_NONBLOCK);
+   if (fd == -1) {
+      ERR("%s: Failed to open: %s\n", device, strerror(errno));
+      return -1;
+   }
+
+   memset(capa, 0, sizeof(capa));
+   if (xioctl(fd, VIDIOCGCAP, capa) == -1) {
+/* errno 515 is used by some webcam drivers for unknown IOICTL command */
+      if (errno != EINVAL && errno != 515) ERR("%s: Querying failed: %s\n", device, strerror(errno));
+      else ERR("%s: Querying failed: Not a V4L compatible device", device);
+      close(fd);
+      return -1;
+   }
+
+   if (!(capa->type & VID_TYPE_CAPTURE)) {
+      ERR("%s: This is not a video capture device\n", device);
+      close(fd);
+      return -1;
+   }
+   TRACE("Name: %s\n", capa->name);
+   return fd;
+}
+#endif
+
 /***********************************************************************
  *             capGetDriverDescriptionA   (AVICAP32.@)
  */
 BOOL VFWAPI capGetDriverDescriptionA(WORD wDriverIndex, LPSTR lpszName,
 				     INT cbName, LPSTR lpszVer, INT cbVer)
 {
+#ifndef ENABLE_V4L
     FIXME("(%d, %p, %d, %p, %d) stub!\n", wDriverIndex, lpszName, cbName,
-	  lpszVer, cbVer);
+          lpszVer, cbVer);
     return FALSE;
+#else
+   struct video_capability capa;
+   int fd;
+
+   fd = queryvideodevice(wDriverIndex, &capa);
+   if (fd == -1) return FALSE;
+   close(fd);
+
+   strncpy (lpszName, capa.name, cbName);
+   snprintf(lpszVer, cbVer, "Video For Wine v1.0.%i", wDriverIndex);
+   TRACE("Version: %s - Name: %s\n", lpszVer, lpszName);
+   return TRUE;
+#endif
 }
 
 /***********************************************************************
@@ -80,7 +159,24 @@
 BOOL VFWAPI capGetDriverDescriptionW(WORD wDriverIndex, LPWSTR lpszName,
 				     INT cbName, LPWSTR lpszVer, INT cbVer)
 {
+#ifndef ENABLE_V4L
     FIXME("(%d, %p, %d, %p, %d) stub!\n", wDriverIndex, lpszName, cbName,
 	  lpszVer, cbVer);
     return FALSE;
+#else
+   struct video_capability capa;
+   int fd;
+   char original[32];
+
+   fd = queryvideodevice(wDriverIndex, &capa);
+   if (fd == -1) return FALSE;
+   close(fd);
+
+   MultiByteToWideChar(CP_UTF8, 0, capa.name, 31, lpszName, cbName);
+   snprintf(original, cbVer, "Video For Wine v%i.0", wDriverIndex);
+   MultiByteToWideChar(CP_UTF8, 0, original, sizeof(original), lpszVer, cbVer);
+   TRACE("Version: %s - Name: %s\n", debugstr_w(lpszVer), debugstr_w(lpszName));
+   return TRUE;
+#endif
 }
+
diff -Nru wine-old/dlls/avicap32/Makefile.in wine-new/dlls/avicap32/Makefile.in
--- wine-old/dlls/avicap32/Makefile.in	2003-10-11 03:09:21.000000000 +0200
+++ wine-new/dlls/avicap32/Makefile.in	2005-04-19 12:55:40.000000000 +0200
@@ -3,7 +3,7 @@
 SRCDIR    = @srcdir@
 VPATH     = @srcdir@
 MODULE    = avicap32.dll
-IMPORTS   = ntdll
+IMPORTS   = ntdll unicows kernel32
 
 C_SRCS = avicap32_main.c
 
diff -Nru wine-old/dlls/devenum/createdevenum.c wine-new/dlls/devenum/createdevenum.c
--- wine-old/dlls/devenum/createdevenum.c	2005-01-25 11:56:39.000000000 +0100
+++ wine-new/dlls/devenum/createdevenum.c	2005-04-19 13:27:49.000000000 +0200
@@ -117,6 +117,7 @@
 
     if (IsEqualGUID(clsidDeviceClass, &CLSID_AudioRendererCategory) ||
         IsEqualGUID(clsidDeviceClass, &CLSID_AudioInputDeviceCategory) ||
+        IsEqualGUID(clsidDeviceClass, &CLSID_VideoInputDeviceCategory) ||
         IsEqualGUID(clsidDeviceClass, &CLSID_MidiRendererCategory))
     {
         hbasekey = HKEY_CURRENT_USER;
@@ -142,6 +143,7 @@
     {
         if (IsEqualGUID(clsidDeviceClass, &CLSID_AudioRendererCategory) ||
             IsEqualGUID(clsidDeviceClass, &CLSID_AudioInputDeviceCategory) ||
+            IsEqualGUID(clsidDeviceClass, &CLSID_VideoInputDeviceCategory) ||
             IsEqualGUID(clsidDeviceClass, &CLSID_MidiRendererCategory))
         {
              HRESULT hr = DEVENUM_CreateSpecialCategories();
@@ -413,6 +415,44 @@
                 CoTaskMemFree(pTypes);
 	    }
 	}
+        res = DEVENUM_CreateAMCategoryKey(&CLSID_VideoInputDeviceCategory);
+        if (SUCCEEDED(res))
+           for (i = 0; i < 10; i++) {
+              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;
+                 }
+
+                 pTypes[0].clsMajorType = &MEDIATYPE_Video;
+                 pTypes[0].clsMinorType = &MEDIASUBTYPE_RGB24;
+
+                 rfp2.lpMediaType = pTypes;
+
+                 res = IFilterMapper2_RegisterFilter(pMapper,
+                                      &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)
diff -Nru wine-old/dlls/devenum/devenum_main.c wine-new/dlls/devenum/devenum_main.c
--- wine-old/dlls/devenum/devenum_main.c	2004-12-07 15:37:11.000000000 +0100
+++ wine-new/dlls/devenum/devenum_main.c	2005-04-19 12:10:12.000000000 +0200
@@ -122,7 +122,7 @@
 	{&CLSID_AudioCompressorCategory, acmcat, TRUE},
 	{&CLSID_VideoCompressorCategory, vidcat, TRUE},
 	{&CLSID_LegacyAmFilterCategory, filtcat, TRUE},
-	{&CLSID_VideoInputDeviceCategory, vfwcat, FALSE},
+	{&CLSID_VideoInputDeviceCategory, vfwcat, TRUE},
 	{&CLSID_AudioInputDeviceCategory, wavein, FALSE},
 	{&CLSID_AudioRendererCategory, waveout, FALSE},
 	{&CLSID_MidiRendererCategory, midiout, FALSE},
@@ -156,7 +156,7 @@
 
         pMapper = (IFilterMapper2*)mapvptr;
 
-        IFilterMapper2_CreateCategory(pMapper, &CLSID_VideoInputDeviceCategory, MERIT_DO_NOT_USE, friendlyvidcap);
+        IFilterMapper2_CreateCategory(pMapper, &CLSID_VideoInputDeviceCategory, MERIT_NORMAL, friendlyvidcap);
         IFilterMapper2_CreateCategory(pMapper, &CLSID_LegacyAmFilterCategory, MERIT_NORMAL, friendlydshow);
         IFilterMapper2_CreateCategory(pMapper, &CLSID_VideoCompressorCategory, MERIT_DO_NOT_USE, friendlyvidcomp);
         IFilterMapper2_CreateCategory(pMapper, &CLSID_AudioInputDeviceCategory, MERIT_DO_NOT_USE, friendlyaudcap);
diff -Nru wine-old/dlls/devenum/devenum_private.h wine-new/dlls/devenum/devenum_private.h
--- wine-old/dlls/devenum/devenum_private.h	2004-12-13 22:19:02.000000000 +0100
+++ wine-new/dlls/devenum/devenum_private.h	2005-04-19 12:55:40.000000000 +0200
@@ -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
diff -Nru wine-old/dlls/devenum/Makefile.in wine-new/dlls/devenum/Makefile.in
--- wine-old/dlls/devenum/Makefile.in	2004-08-19 21:31:20.000000000 +0200
+++ wine-new/dlls/devenum/Makefile.in	2005-04-19 12:55:40.000000000 +0200
@@ -3,7 +3,7 @@
 SRCDIR    = @srcdir@
 VPATH     = @srcdir@
 MODULE    = devenum.dll
-IMPORTS   = ole32 oleaut32 winmm user32 advapi32 kernel32
+IMPORTS   = ole32 oleaut32 winmm user32 advapi32 kernel32 avicap32
 EXTRALIBS = -lstrmiids -luuid
 
 C_SRCS = \
diff -Nru wine-old/dlls/msvideo/msvideo_main.c wine-new/dlls/msvideo/msvideo_main.c
--- wine-old/dlls/msvideo/msvideo_main.c	2005-02-17 12:51:01.000000000 +0100
+++ wine-new/dlls/msvideo/msvideo_main.c	2005-04-19 12:10:12.000000000 +0200
@@ -1132,7 +1132,7 @@
 }
 
 /***********************************************************************
- *      ICSeqCompressFrameEnd   [MSVFW32.@]
+ *      ICSeqCompressFrameStart [MSVFW32.@]
  */
 BOOL VFWAPI ICSeqCompressFrameStart(PCOMPVARS pc, LPBITMAPINFO lpbiIn)
 {
diff -Nru wine-old/dlls/quartz/capture.c wine-new/dlls/quartz/capture.c
--- wine-old/dlls/quartz/capture.c	1970-01-01 01:00:00.000000000 +0100
+++ wine-new/dlls/quartz/capture.c	2005-04-19 17:22:37.000000000 +0200
@@ -0,0 +1,246 @@
+/* DirectShow capture services (QUARTZ.DLL)
+ *
+ * Copyright 2005 Maarten Lankhorst
+ *
+ * This file contains the (internal) driver registration functions,
+ * driver enumeration APIs and DirectDraw creation functions.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define NONAMELESSSTRUCT
+#define NONAMELESSUNION
+#include "quartz_private.h"
+#include "control_private.h"
+#include "pin.h"
+#include "capture.h"
+
+#include "uuids.h"
+#include "mmreg.h"
+#include "vfwmsgs.h"
+#include "amvideo.h"
+#include "fourcc.h"
+#include "windef.h"
+#include "winbase.h"
+#include "dshow.h"
+#include "strmif.h"
+#include "ddraw.h"
+
+#include "wine/unicode.h"
+#include "wine/debug.h"
+
+#ifdef ENABLE_V4L
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <asm/types.h>
+#include <linux/videodev.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "winnls.h"
+#endif
+   
+WINE_DEFAULT_DEBUG_CHANNEL(quartz);
+
+#ifdef ENABLE_V4L
+static int xioctl(int fd, int request, void * arg)
+{  
+   int r;
+
+   do r = ioctl (fd, request, arg);
+   while (-1 == r && EINTR == errno);
+
+   return r;
+}
+#endif
+
+HRESULT Capture_Initialise(CaptureBox * capBox, IPin * pOut, USHORT card)
+{
+#ifdef ENABLE_V4L
+   char device[128];
+   struct stat st;
+   struct v4l2_capability caps;
+
+   sprintf(device, "/dev/video%i", 0);
+
+   FIXME("(): Allow use of more then 1 video adapter, like I did in avicap\n");
+   FIXME("(): Add support for Video4Linux, currently only allowing v4l2\n");
+
+   if (stat (device, &st) == -1) {
+      ERR("%s: %s\n", device, strerror(errno));
+      return E_FAIL;
+   }
+
+   if (!S_ISCHR (st.st_mode)) {
+      ERR("%s: Not a device\n", device);
+      return E_FAIL;
+   }
+
+   capBox->fd = open(device, O_RDWR | O_NONBLOCK);
+   if (capBox->fd == -1) {
+      ERR("%s: Failed to open: %s\n", device, strerror(errno));
+      return E_FAIL;
+   }
+#define QUERY(io, sendwhat) if (xioctl(capBox->fd, io, sendwhat) == -1) {       ERR("%s: Can not perform ioctl: %s\n", device, strerror(errno)); close(capBox->fd); return E_FAIL; }
+#define FQUERY(io, sendwhat) if (xioctl(capBox->fd, io, sendwhat) == -1) { TRACE("%s: ioctl failed: %s - continuing anyway\n", device, strerror(errno)); }
+   QUERY(VIDIOC_QUERYCAP, &caps);
+   FIXME("Queried...\n");
+
+   struct v4l2_input in;
+   int inputs = 0;
+
+   in.index = 0;
+
+   while( ioctl( capBox->fd, VIDIOC_ENUMINPUT, &in ) >= 0 ) {
+      in.index = ++inputs;
+   }
+
+   if( !inputs ) {
+      ERR("No inputs available on device\n");
+      close(capBox->fd);
+      return E_FAIL;
+   }
+   TRACE("Amount of inputs: %d\n", inputs);
+   struct v4l2_format imgformat;
+
+   imgformat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+   memset( &(imgformat.fmt.pix), 0, sizeof( struct v4l2_pix_format ) );
+   imgformat.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32;
+   imgformat.fmt.pix.field = V4L2_FIELD_INTERLACED;
+   imgformat.fmt.pix.width = 320;
+   imgformat.fmt.pix.height = 240;
+
+   QUERY(VIDIOC_S_FMT, &imgformat);
+
+   capBox->height = imgformat.fmt.pix.height;
+   capBox->width = imgformat.fmt.pix.width;
+   capBox->bitDepth = 24;
+   capBox->pOut = pOut;
+   capBox->fps = 2;
+   FIXME("Using format: 24 bits - %d x %d\n", imgformat.fmt.pix.width, imgformat.fmt.pix.height);
+   return S_OK;
+#else
+   ERR("Video 4 Linux 2 support not enabled, and currently NOT supporting V4L1\n\tReturning E_NOTIMPL");
+   return  E_NOTIMPL;
+#endif
+}
+
+HRESULT Capture_Destroy(CaptureBox * capBox)
+{
+/* Destroy file handlers etc? This function can't fail.. */
+   close(capBox->fd);
+   return S_OK;
+}
+
+HRESULT Capture_SetMediaType(CaptureBox * capBox, AM_MEDIA_TYPE * mT)
+{
+/* Only time I can imagine this doesn't return is with incompatible type */
+/* We should check rcTarget rectangle? */
+   FIXME("%p -> (%p)\n", capBox, mT);
+   capBox->width = ((BITMAPINFOHEADER *)mT->pbFormat)->biWidth;
+   capBox->height = ((BITMAPINFOHEADER *)mT->pbFormat)->biHeight;
+   capBox->bitDepth = ((BITMAPINFOHEADER *)mT->pbFormat)->biBitCount;
+   return S_OK;
+}
+
+HRESULT Capture_GetMediaType(CaptureBox * capBox, AM_MEDIA_TYPE ** mT)
+{
+   FIXME("%p\n", capBox);
+   VIDEOINFOHEADER *vi;
+   mT[0] = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
+   if (!mT[0]) return E_OUTOFMEMORY;
+   vi = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
+   mT[0]->cbFormat = sizeof(AM_MEDIA_TYPE);
+   if (!vi)
+   {
+      CoTaskMemFree(mT[0]);
+      return E_OUTOFMEMORY;
+   }
+   memcpy(&mT[0]->majortype, &MEDIATYPE_Video, sizeof(GUID));
+   memcpy(&mT[0]->subtype, &MEDIASUBTYPE_RGB24, sizeof(GUID));
+   memcpy(&mT[0]->formattype, &FORMAT_VideoInfo, sizeof(GUID));
+   mT[0]->bFixedSizeSamples = 1;
+   mT[0]->bTemporalCompression = 0;
+   ZeroMemory(&vi->rcSource, sizeof(RECT));
+   ZeroMemory(&vi->rcTarget, sizeof(RECT));
+   vi->dwBitRate = capBox->fps * capBox->width * capBox->height * capBox->bitDepth / 8;
+   vi->dwBitErrorRate = 0;
+   vi->AvgTimePerFrame = 10000000 / capBox->fps;
+   vi->bmiHeader.biSize = capBox->width * capBox->height * capBox->bitDepth / 8;
+   vi->bmiHeader.biWidth = capBox->width;
+   vi->bmiHeader.biHeight = capBox->height;
+   vi->bmiHeader.biPlanes = 1;
+   vi->bmiHeader.biBitCount = 24;
+   vi->bmiHeader.biCompression = vi->bmiHeader.biSizeImage = 0;
+   vi->bmiHeader.biClrUsed = vi->bmiHeader.biClrImportant = 0;
+   mT[0]->pbFormat = (void *)vi;
+   return S_OK;
+}
+
+DWORD WINAPI TestBed(LPVOID lParam) {
+   CaptureBox * capBox = lParam;
+   HRESULT hr;
+   IMediaSample *pSample = NULL;
+   unsigned long framecount = 1;
+
+   hr = OutputPin_CommitAllocator((OutputPin *)capBox->pOut);
+   TRACE("Meat 0: %lx\n", hr);
+   if (SUCCEEDED(hr)) while (1) {
+      unsigned char readbuffer[320*240*4];
+      int heightidx = 0, width;
+      BYTE * myData;
+      OutputPin_GetDeliveryBuffer((OutputPin *)capBox->pOut, &pSample, NULL, NULL, 0);
+      IMediaSample_SetActualDataLength(pSample, 230400);
+      IMediaSample_GetPointer(pSample, &myData);
+      read(capBox->fd, readbuffer, sizeof(readbuffer));
+      while (heightidx < 240) {
+         for (width = 0; width < 320; width++) {
+            int p0ink = (239-heightidx)*320*3+width*3;
+            int p0ink2 = width*4+heightidx*320*4;
+            myData[p0ink + 2] = readbuffer[p0ink2 + 1];
+            myData[p0ink + 1] = readbuffer[p0ink2 + 2];
+            myData[p0ink + 0] = readbuffer[p0ink2 + 3];
+         }
+         heightidx++;
+      }
+      hr = OutputPin_SendSample((OutputPin *)capBox->pOut, pSample);
+      FIXME("%p -> Frame %lu: %lx\n", capBox, framecount++, hr);
+      usleep(250*1000);
+      IMediaSample_Release(pSample);
+   }
+   return 0x0;
+}
+
+HRESULT Capture_Run(CaptureBox * capBox, FILTER_STATE *state)
+{
+   DWORD dwThreadId = 0;
+   *state = State_Running;
+   FIXME("%p -> (%p) stub\n", capBox, state);
+   return CreateThread(NULL, 0, TestBed, capBox, 0, &dwThreadId) ? S_OK : E_FAIL;
+}
+
+HRESULT Capture_Pause(CaptureBox *capBox, FILTER_STATE *state)
+{
+   FIXME("%p -> (%p) stub\n", capBox, state);
+   return S_OK;
+}
+
+HRESULT Capture_Stop(CaptureBox *capBox, FILTER_STATE *state)
+{
+   FIXME("%p -> (%p) stub\n", capBox, state);
+   return S_OK;
+}
+
diff -Nru wine-old/dlls/quartz/capturegraph.c wine-new/dlls/quartz/capturegraph.c
--- wine-old/dlls/quartz/capturegraph.c	1970-01-01 01:00:00.000000000 +0100
+++ wine-new/dlls/quartz/capturegraph.c	2005-04-19 12:10:12.000000000 +0200
@@ -0,0 +1,209 @@
+/* Capture Graph Builder, Minimal edition..
+ * Copyright 2005 Maarten Lankhorst
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * TODO: Implement CaptureGraphBuilder2, and make all calls from
+ * CaptureGraphBuilder forward to it..
+ */
+
+#include "config.h"
+
+#define NONAMELESSSTRUCT
+#define NONAMELESSUNION
+#include "quartz_private.h"
+
+#include "uuids.h"
+#include "mmreg.h"
+#include "vfwmsgs.h"
+#include "amvideo.h"
+#include "windef.h"
+#include "winbase.h"
+#include "dshow.h"
+#include "evcode.h"
+#include "strmif.h"
+#include "ddraw.h"
+
+#include "wine/unicode.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(quartz);
+
+static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
+
+static const ICaptureGraphBuilderVtbl Builder_Vtbl;
+
+typedef struct CaptureGraphImpl
+{
+   const ICaptureGraphBuilderVtbl * lpVtbl;
+   IGraphBuilder *mygraph;
+
+   ULONG refCount;
+   CRITICAL_SECTION csFilter;
+} CaptureGraphImpl;
+
+HRESULT CaptureGraphBuilder_create(IUnknown * pUnkOuter, LPVOID * ppv)
+{
+   CaptureGraphImpl * pCapture;
+   TRACE("(%p, %p)\n", pUnkOuter, ppv);
+   *ppv = NULL;
+   if (pUnkOuter) return CLASS_E_NOAGGREGATION;
+   pCapture = CoTaskMemAlloc(sizeof(CaptureGraphImpl));
+   pCapture->lpVtbl = &Builder_Vtbl;
+   pCapture->refCount = 1;
+   pCapture->mygraph = NULL;
+   InitializeCriticalSection(&pCapture->csFilter);
+   *ppv = (LPVOID)pCapture;
+   return S_OK;
+}
+
+static HRESULT WINAPI CaptureGraphBuilder_QueryInterface(ICaptureGraphBuilder * iface, REFIID riid, LPVOID * ppv)
+{
+   struct CaptureGraphImpl *This = (CaptureGraphImpl *)iface;
+   TRACE("(%p/%p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv);
+   *ppv = NULL;
+   if (IsEqualIID(riid, &IID_IUnknown))
+      *ppv = (LPVOID)This;
+   else if (IsEqualIID(riid, &IID_ICaptureGraphBuilder))
+   {
+      *ppv = (LPVOID)This;
+      TRACE("Asking for myself?\n");
+   }
+
+   if (*ppv)
+   {
+       IUnknown_AddRef((IUnknown *)(*ppv));
+       return S_OK;
+   }
+
+   FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
+   return E_NOINTERFACE;
+}
+
+static ULONG WINAPI CaptureGraphBuilder_AddRef(ICaptureGraphBuilder * iface)
+{
+   CaptureGraphImpl *This = (CaptureGraphImpl *)iface;
+   ULONG refCount = InterlockedIncrement(&This->refCount);
+
+   TRACE("(%p/%p)->() AddRef from %ld\n", This, iface, refCount - 1);
+   return refCount;
+}
+
+static ULONG WINAPI CaptureGraphBuilder_Release(ICaptureGraphBuilder * iface)
+{
+   CaptureGraphImpl *This = (CaptureGraphImpl *)iface;
+   ULONG refCount = InterlockedDecrement(&This->refCount);
+
+   TRACE("(%p/%p)->() Release from %ld\n", This, iface, refCount + 1);
+
+   if (!refCount) {
+      FIXME("Release IGraphFilter or w/e\n");
+      DeleteCriticalSection(&This->csFilter);
+      This->lpVtbl = NULL;
+      if (This->mygraph != NULL)
+        IGraphBuilder_Release((IGraphBuilder *)This->mygraph);
+      CoTaskMemFree(This);
+      return 0;
+   }
+   else return refCount;
+}
+
+static HRESULT WINAPI CaptureGraphBuilder_SetFilterGraph(ICaptureGraphBuilder * iface, IGraphBuilder *pfg)
+{
+/* The graph builder will automatically create a filter graph if you don't call this method. If you call this method after the graph builder has created its own filter graph, the call will fail. */
+   struct CaptureGraphImpl *This = (CaptureGraphImpl *)iface;
+   if (This->mygraph != NULL) return E_NOTIMPL;
+   This->mygraph = pfg;
+   IGraphBuilder_AddRef((IGraphBuilder *)This->mygraph);
+   TRACE("%p: %p\n", iface, pfg);
+   return S_OK;
+}
+
+static HRESULT WINAPI CaptureGraphBuilder_GetFilterGraph(ICaptureGraphBuilder * iface, IGraphBuilder **pfg)
+{
+   struct CaptureGraphImpl *This = (CaptureGraphImpl *)iface;
+   FIXME("%p: Make our own filtergraph if we haven't got one already\n", iface);
+   if (This->mygraph == NULL) return E_NOTIMPL;
+
+   *pfg = This->mygraph;
+   IGraphBuilder_AddRef((IGraphBuilder *)This->mygraph);
+   
+   TRACE("%p: %p\n", iface, *pfg);
+   return S_OK;
+}
+
+static HRESULT WINAPI CaptureGraphBuilder_SetOutputFileName(ICaptureGraphBuilder * iface, const GUID *pType, LPCOLESTR lpstrFile,IBaseFilter **ppf, IFileSinkFilter **ppSink)
+{
+   FIXME("%p: %p, %p, %p, %p - stub\n", iface, pType, lpstrFile, *ppf, *ppSink);
+   return E_NOTIMPL;
+}
+
+static HRESULT WINAPI CaptureGraphBuilder_FindInterface(ICaptureGraphBuilder * iface, const GUID *pCategory, IBaseFilter *pf, REFIID riid, void **ppint)
+{
+   struct CaptureGraphImpl *This = (CaptureGraphImpl *)iface;
+   TRACE("%p: %s .. %p .. %s .. %p - unwanted stub workaround!\n", iface, qzdebugstr_guid(pCategory), pf, qzdebugstr_guid(riid), *ppint);
+   return IBaseFilter_QueryInterface(pf, riid, ppint);
+   /* Looks for the specified interface on the filter, upstream and
+    * downstream from the filter, and, optionally, only on the output
+    * pin of the given category.
+    */
+}
+
+/* [call_as(FindInterface)] HRESULT RemoteFindInterface(
+        [in, unique] const GUID *pCategory,
+        [in] IBaseFilter *pf,
+        [in] REFIID riid,
+        [out] IUnknown **ppint);
+   ^- What is this? */
+
+static HRESULT WINAPI CaptureGraphBuilder_RenderStream(ICaptureGraphBuilder * iface, const GUID *pCategory, IUnknown *pSource, IBaseFilter *pfCompressor, IBaseFilter *pfRenderer)
+{
+   FIXME("%p: .. Stub? !!! -- SHOULD NORMALLY WORK!!..\n", iface);
+   return E_NOTIMPL;
+}
+
+static HRESULT WINAPI CaptureGraphBuilder_ControlStream(ICaptureGraphBuilder * iface, const GUID *pCategory, IBaseFilter *pFilter, REFERENCE_TIME *pstart, REFERENCE_TIME *pstop, WORD wStartCookie, WORD wStopCookie)
+{
+   FIXME("%p: .. Stub? !!! ..\n", iface);
+   return E_NOTIMPL;
+}
+
+static HRESULT WINAPI CaptureGraphBuilder_AllocCapFile(ICaptureGraphBuilder * iface, LPCOLESTR lpstr, DWORDLONG dwlSize)
+{
+   FIXME("%p: .. Stub? !!! ..\n", iface);
+   return E_NOTIMPL;
+}
+
+static HRESULT WINAPI CaptureGraphBuilder_CopyCaptureFile(ICaptureGraphBuilder * iface, LPOLESTR lpwstrOld, LPOLESTR lpwstrNew, int fAllowEscAbort, IAMCopyCaptureFileProgress *pCallback)
+{
+   FIXME("%p: .. Stub? !!! ..\n", iface);
+   return E_NOTIMPL;
+}
+
+static const ICaptureGraphBuilderVtbl Builder_Vtbl =
+{   
+   CaptureGraphBuilder_QueryInterface,
+   CaptureGraphBuilder_AddRef,
+   CaptureGraphBuilder_Release,
+   CaptureGraphBuilder_SetFilterGraph,
+   CaptureGraphBuilder_GetFilterGraph,
+   CaptureGraphBuilder_SetOutputFileName,
+   CaptureGraphBuilder_FindInterface,
+   CaptureGraphBuilder_RenderStream,
+   CaptureGraphBuilder_ControlStream,
+   CaptureGraphBuilder_AllocCapFile,
+   CaptureGraphBuilder_CopyCaptureFile
+};
+
diff -Nru wine-old/dlls/quartz/capture.h wine-new/dlls/quartz/capture.h
--- wine-old/dlls/quartz/capture.h	1970-01-01 01:00:00.000000000 +0100
+++ wine-new/dlls/quartz/capture.h	2005-04-19 13:29:26.000000000 +0200
@@ -0,0 +1,38 @@
+/* DirectShow private capture header (QUARTZ.DLL)
+ *
+ * Copyright 2005 Maarten Lankhorst
+ *
+ * This file contains the (internal) driver registration functions,
+ * driver enumeration APIs and DirectDraw creation functions.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+typedef struct CaptureBox {
+/* Dunno what to put in here? */
+  UINT width, height, bitDepth, fps;
+  IPin *pOut;
+  int fd;
+} CaptureBox;
+
+HRESULT Capture_Query(USHORT card);
+HRESULT Capture_Initialise(CaptureBox * capBox, IPin *pOut, USHORT card);
+HRESULT Capture_GetMediaType(CaptureBox * capBox, AM_MEDIA_TYPE ** mT);
+HRESULT Capture_SetMediaType(CaptureBox * capBox, AM_MEDIA_TYPE * mT);
+HRESULT Capture_Run(CaptureBox * capBox, FILTER_STATE *state);
+HRESULT Capture_Stop(CaptureBox * capBox, FILTER_STATE *state);
+HRESULT Capture_Pause(CaptureBox * capBox, FILTER_STATE *state);
+HRESULT Capture_Destroy(CaptureBox * capBox);
+
diff -Nru wine-old/dlls/quartz/enummedia.c wine-new/dlls/quartz/enummedia.c
--- wine-old/dlls/quartz/enummedia.c	2005-01-06 20:36:47.000000000 +0100
+++ wine-new/dlls/quartz/enummedia.c	2005-04-19 12:10:12.000000000 +0200
@@ -20,6 +20,17 @@
 
 #include "quartz_private.h"
 
+#include "uuids.h"
+#include "mmreg.h"
+#include "vfwmsgs.h"
+#include "amvideo.h"
+#include "fourcc.h"
+#include "windef.h"
+#include "winbase.h"
+#include "dshow.h"
+#include "strmif.h"
+#include "ddraw.h"
+
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
@@ -61,7 +72,37 @@
 {
     if (!pmt)
         return;
-    TRACE("\t%s\n\t%s\n\t...\n\t%s\n", qzdebugstr_guid(&pmt->majortype), qzdebugstr_guid(&pmt->subtype), qzdebugstr_guid(&pmt->formattype));
+   TRACE("(): Printing out full format\n"
+         "\tMajor.Minor: %s.%s\n"
+         "\tFixedSize [%d] - Temporal [%d]\n"
+         "\tSample size [%lu] - Format type: %s\n"
+         "\tcb Format: %lu - Pointer: %p\n",
+         qzdebugstr_guid(&pmt->majortype), qzdebugstr_guid(&pmt->subtype),
+         pmt->bFixedSizeSamples, pmt->bTemporalCompression,
+         pmt->lSampleSize, qzdebugstr_guid(&pmt->formattype),
+         pmt->cbFormat, pmt->pbFormat);
+   if (!strcmp(qzdebugstr_guid(&pmt->formattype), qzdebugstr_guid(&FORMAT_VideoInfo))) {
+   BITMAPINFOHEADER *bmi;
+   VIDEOINFOHEADER *pvi;
+   pvi = (void *)pmt->pbFormat;
+
+   TRACE("(): Printing out whole video info header\n"
+         "\tSource rectangle: (%lu,%lu,%lu,%lu)\n"
+         "\tTarget rectangle: (%lu,%lu,%lu,%lu)\n"
+         "\tBitrate: %lu, Error rate: %lu\n"
+         "\tTime per frame: %lu\n",
+         pvi->rcSource.left, pvi->rcSource.right, pvi->rcSource.top, pvi->rcSource.bottom,
+         pvi->rcTarget.left, pvi->rcTarget.right, pvi->rcTarget.top, pvi->rcTarget.bottom,
+         pvi->dwBitRate, pvi->dwBitErrorRate,
+         (ulong) pvi->AvgTimePerFrame);
+   bmi = &pvi->bmiHeader;
+   TRACE("(): Printing out bmi header\n"
+         "\tSize, in pixels: %lu\n"
+         "\tFormat: %ldx%ld\n"
+         "\tBit count: %d\n"
+         "\tSize image: %lu\n",
+         bmi->biSize, bmi->biWidth, bmi->biHeight, bmi->biBitCount, bmi->biSizeImage);
+   }
 }
 
 typedef struct IEnumMediaTypesImpl
diff -Nru wine-old/dlls/quartz/main.c wine-new/dlls/quartz/main.c
--- wine-old/dlls/quartz/main.c	2005-03-02 13:23:22.000000000 +0100
+++ wine-new/dlls/quartz/main.c	2005-04-19 12:10:12.000000000 +0200
@@ -71,7 +71,9 @@
     { &CLSID_AVIDec, AVIDec_create },
     { &CLSID_SystemClock, &QUARTZ_CreateSystemClock },
     { &CLSID_ACMWrapper, &ACMWrapper_create },
-    { &CLSID_WAVEParser, &WAVEParser_create }
+    { &CLSID_VfwCapture, &VfwCapture_create },
+    { &CLSID_WAVEParser, &WAVEParser_create },
+    { &CLSID_CaptureGraphBuilder, &CaptureGraphBuilder_create }
 };
 
 static HRESULT WINAPI
diff -Nru wine-old/dlls/quartz/Makefile.in wine-new/dlls/quartz/Makefile.in
--- wine-old/dlls/quartz/Makefile.in	2005-02-10 18:13:18.000000000 +0100
+++ wine-new/dlls/quartz/Makefile.in	2005-04-19 12:10:12.000000000 +0200
@@ -10,6 +10,8 @@
 	acmwrapper.c \
 	avidec.c \
 	avisplit.c \
+	capture.c \
+	capturegraph.c \
 	control.c \
 	dsoundrender.c \
 	enumfilters.c \
@@ -27,6 +29,7 @@
 	regsvr.c \
 	systemclock.c \
 	transform.c \
+	v4wsource.c \
 	videorenderer.c \
 	waveparser.c
 
diff -Nru wine-old/dlls/quartz/pin.c wine-new/dlls/quartz/pin.c
--- wine-old/dlls/quartz/pin.c	2005-03-02 11:12:12.000000000 +0100
+++ wine-new/dlls/quartz/pin.c	2005-04-19 12:10:12.000000000 +0200
@@ -66,12 +66,17 @@
     dump_AM_MEDIA_TYPE(pmt);
 
     /* FIXME: call queryacceptproc */
+    wine_dbg_add_option("quartz", 15, 0);
 
     This->pin.pConnectedTo = pReceivePin;
     IPin_AddRef(pReceivePin);
     CopyMediaType(&This->pin.mtCurrent, pmt);
+    wine_dbg_add_option("quartz", 15, 0);
 
     hr = IPin_ReceiveConnection(pReceivePin, iface, pmt);
+    FIXME("(): MSN Crashes if quartz debug channel (TRACE & WARN somewhere before here\n"
+          "\t\tManually enabling full debug on quartz now -- %lx\n", hr);
+    wine_dbg_add_option("quartz", 15, 0);
 
     /* get the IMemInputPin interface we will use to deliver samples to the
      * connected pin */
diff -Nru wine-old/dlls/quartz/quartz_private.h wine-new/dlls/quartz/quartz_private.h
--- wine-old/dlls/quartz/quartz_private.h	2005-02-10 18:13:18.000000000 +0100
+++ wine-new/dlls/quartz/quartz_private.h	2005-04-19 12:10:12.000000000 +0200
@@ -52,6 +52,8 @@
 HRESULT QUARTZ_CreateSystemClock(IUnknown * pUnkOuter, LPVOID * ppv);
 HRESULT ACMWrapper_create(IUnknown * pUnkOuter, LPVOID * ppv);
 HRESULT WAVEParser_create(IUnknown * pUnkOuter, LPVOID * ppv);
+HRESULT VfwCapture_create(IUnknown * pUnkOuter, LPVOID * ppv);
+HRESULT CaptureGraphBuilder_create(IUnknown * pUnkOuter, LPVOID * ppv);
 
 HRESULT EnumMonikerImpl_Create(IMoniker ** ppMoniker, ULONG nMonikerCount, IEnumMoniker ** ppEnum);
 
diff -Nru wine-old/dlls/quartz/regsvr.c wine-new/dlls/quartz/regsvr.c
--- wine-old/dlls/quartz/regsvr.c	2005-02-10 18:13:18.000000000 +0100
+++ wine-new/dlls/quartz/regsvr.c	2005-04-19 12:10:12.000000000 +0200
@@ -922,6 +922,18 @@
 	"quartz.dll",
 	"Both"
     },
+    {   &CLSID_VfwCapture,
+        "Video for wine capture interface",
+        NULL,
+        "quartz.dll",
+        "Both"
+    },
+    {   &CLSID_CaptureGraphBuilder,
+        "Capture Graph Builder",
+        NULL,
+        "quartz.dll",
+        "Both"
+    },
     { NULL }			/* list terminator */
 };
 
@@ -1112,6 +1124,18 @@
 	    { 0xFFFFFFFF },
 	}
     },
+    {   &CLSID_VfwCapture,
+        &CLSID_VideoInputDeviceCategory,
+        {'V','i','d','e','o',' ','F','o','r',' ','W','i','n','e',' ','(','V','4','W',')', 0},
+        0x800000,
+        {   {   REG_PINFLAG_B_OUTPUT,
+                {   { &MEDIATYPE_Stream, &GUID_NULL },
+                    { NULL }
+                },
+            },
+            { 0xFFFFFFFF },
+        }
+    }, /* This creates a fake device for us with an ugly name */
     { NULL }		/* list terminator */
 };
 
diff -Nru wine-old/dlls/quartz/v4wsource.c wine-new/dlls/quartz/v4wsource.c
--- wine-old/dlls/quartz/v4wsource.c	1970-01-01 01:00:00.000000000 +0100
+++ wine-new/dlls/quartz/v4wsource.c	2005-04-19 13:28:31.000000000 +0200
@@ -0,0 +1,591 @@
+/* Video For Windows Steering structure
+ *
+ * Copyright 2005 Maarten Lankhorst
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * LETS SING IT ALL TOGETHER NOW! ON MY MARK!
+ * COM SUCKS ASS!
+ */
+
+#define NONAMELESSSTRUCT
+#define NONAMELESSUNION
+#include "quartz_private.h"
+#include "control_private.h"
+#include "pin.h"
+#include "capture.h"
+
+#include "uuids.h"
+#include "mmreg.h"
+#include "vfwmsgs.h"
+#include "amvideo.h"
+#include "fourcc.h"
+#include "windef.h"
+#include "winbase.h"
+#include "dshow.h"
+#include "strmif.h"
+#include "ddraw.h"
+
+#include "wine/unicode.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(quartz);
+
+static const IBaseFilterVtbl VfwCapture_Vtbl;
+static const IAMStreamConfigVtbl IAMStreamConfig_VTable;
+static const IPinVtbl VfwPin_Vtbl;
+
+static HRESULT VfwPin_Construct(IBaseFilter * pBaseFilter, LPCRITICAL_SECTION pCritSec, IPin ** ppPin);
+static const WCHAR wszOutputPinName[] = { 'O','u','t','p','u','t',0 };
+
+typedef struct VfwCapture
+{
+   const struct IBaseFilterVtbl * lpVtbl;
+   const struct IAMStreamConfigVtbl * IAMStreamConfig_vtbl;
+
+   CaptureBox myCap; /* Keep this as first, for FakeAMConfigImpl */
+   ULONG refCount;
+   FILTER_INFO filterInfo;
+   FILTER_STATE state;
+   CRITICAL_SECTION csFilter;
+
+   IPin * pOutputPin;
+} VfwCapture;
+
+typedef struct FakeAMConfigImpl {
+   const struct IAMStreamConfigVtbl * IAMStreamConfig_vtbl;
+   CaptureBox myCap;
+} FakeAMConfigImpl;
+
+/* VfwPin implementation */
+typedef struct VfwPinImpl
+{
+  OutputPin pin;
+
+  CaptureBox *myCap;
+  IKsPropertySetVtbl * KSP_VT;
+  HANDLE hEvent;
+  BOOL bFlushing;
+} VfwPinImpl;
+
+HRESULT VfwCapture_create(IUnknown * pUnkOuter, LPVOID * ppv)
+{
+   VfwCapture *pVfwCapture;
+   HRESULT hr;
+
+   if (pUnkOuter)
+      return CLASS_E_NOAGGREGATION;
+
+   pVfwCapture = CoTaskMemAlloc(sizeof(VfwCapture));
+
+   if (!pVfwCapture)
+      return E_OUTOFMEMORY;
+
+   pVfwCapture->lpVtbl = &VfwCapture_Vtbl;
+   pVfwCapture->IAMStreamConfig_vtbl = &IAMStreamConfig_VTable;
+   pVfwCapture->refCount = 1;
+   pVfwCapture->filterInfo.achName[0] = '\0';
+   pVfwCapture->filterInfo.pGraph = NULL;
+   InitializeCriticalSection(&pVfwCapture->csFilter);
+   hr = VfwPin_Construct((IBaseFilter *)&pVfwCapture->lpVtbl, &pVfwCapture->csFilter, &pVfwCapture->pOutputPin);
+   if (!SUCCEEDED(hr))
+   {
+      CoTaskMemFree(pVfwCapture);
+      return E_OUTOFMEMORY;
+   } else {
+      ((VfwPinImpl *)pVfwCapture->pOutputPin)->myCap = &pVfwCapture->myCap;
+      hr = Capture_Initialise(&pVfwCapture->myCap, pVfwCapture->pOutputPin, 0);
+      if (FAILED(hr)) {
+         IPin_Release((IPin *)pVfwCapture->pOutputPin);
+         CoTaskMemFree(pVfwCapture);
+         return hr;
+      }
+   }
+   *ppv = (LPVOID)pVfwCapture;
+   TRACE("-- created at %p\n", pVfwCapture);
+
+   return S_OK;
+}
+
+static HRESULT WINAPI VfwCapture_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
+{
+   VfwCapture *This = (VfwCapture *)iface;
+   TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
+   *ppv = NULL;
+
+   if (IsEqualIID(riid, &IID_IUnknown))
+      *ppv = (LPVOID)This;
+   else if (IsEqualIID(riid, &IID_IPersist))
+      *ppv = (LPVOID)This;
+   else if (IsEqualIID(riid, &IID_IMediaFilter))
+      *ppv = (LPVOID)This;
+   else if (IsEqualIID(riid, &IID_IBaseFilter))
+      *ppv = (LPVOID)This;
+   else if (IsEqualIID(riid, &IID_IAMStreamConfig))
+      *ppv = (LPVOID)&(This->IAMStreamConfig_vtbl);
+
+   if (*ppv)
+   {
+      TRACE("Returning %s interface\n", qzdebugstr_guid(riid));
+      IUnknown_AddRef((IUnknown *)(*ppv));
+      return S_OK;
+   }
+
+   FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
+   return E_NOINTERFACE;
+}
+
+static ULONG WINAPI VfwCapture_AddRef(IBaseFilter * iface)
+{
+   VfwCapture *This = (VfwCapture *)iface;
+   ULONG refCount = InterlockedIncrement(&This->refCount);
+
+   TRACE("(%p/%p)->() AddRef from %ld\n", This, iface, refCount - 1);
+
+   return refCount;
+}
+
+static ULONG WINAPI VfwCapture_Release(IBaseFilter * iface)
+{
+   VfwCapture *This = (VfwCapture *)iface;
+   ULONG refCount = InterlockedDecrement(&This->refCount);
+   TRACE("(%p/%p)->() Release from %ld\n", This, iface, refCount + 1);
+
+   if (!refCount)
+   {
+      TRACE("(): Destroying everything!\n");
+      if (This->state != State_Stopped)
+         Capture_Stop(&This->myCap, &This->state);
+      Capture_Destroy(&This->myCap);
+      IPin_Release(This->pOutputPin);
+      DeleteCriticalSection(&This->csFilter);
+      This->lpVtbl = NULL;
+      CoTaskMemFree(This);
+   }
+   return refCount;
+}
+
+/** IPersist methods **/
+
+static HRESULT WINAPI VfwCapture_GetClassID(IBaseFilter * iface, CLSID * pClsid)
+{
+   TRACE("(%p)\n", pClsid);
+   *pClsid = CLSID_VfwCapture;
+   return S_OK;
+}
+
+/** IMediaFilter methods **/
+
+static HRESULT WINAPI VfwCapture_Stop(IBaseFilter * iface)
+{
+   VfwCapture *This = (VfwCapture *)iface;
+   TRACE("()\n");
+   return Capture_Stop(&This->myCap, &This->state);
+}
+
+static HRESULT WINAPI VfwCapture_Pause(IBaseFilter * iface)
+{
+   VfwCapture *This = (VfwCapture *)iface;
+   TRACE("()\n");
+   return Capture_Pause(&This->myCap, &This->state);
+}
+
+static HRESULT WINAPI VfwCapture_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
+{
+   VfwCapture *This = (VfwCapture *)iface;
+   TRACE("(%lx%08lx)\n", (ULONG)(tStart >> 32), (ULONG)tStart);
+   return Capture_Run(&This->myCap, &This->state);
+}
+
+static HRESULT WINAPI VfwCapture_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
+{
+   VfwCapture *This = (VfwCapture *)iface;
+   TRACE("(%lu, %p)\n", dwMilliSecsTimeout, pState);
+   *pState = This->state;
+   return S_OK;
+}
+
+static HRESULT WINAPI VfwCapture_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
+{
+   TRACE("(%p)\n", pClock);
+   return S_OK;
+}
+
+static HRESULT WINAPI VfwCapture_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
+{
+   TRACE("(%p)\n", ppClock);
+   return S_OK;
+}
+
+/** IBaseFilter methods **/
+
+static HRESULT WINAPI VfwCapture_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
+{
+   ENUMPINDETAILS epd;
+   VfwCapture *This = (VfwCapture *)iface;
+
+   TRACE("(%p)\n", ppEnum);
+
+   epd.cPins = 1;
+   epd.ppPins = &This->pOutputPin;
+   return IEnumPinsImpl_Construct(&epd, ppEnum);
+}
+
+static HRESULT WINAPI VfwCapture_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
+{
+   FIXME("(%s, %p) - stub\n", debugstr_w(Id), ppPin);
+   return E_NOTIMPL;
+}
+
+static HRESULT WINAPI VfwCapture_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
+{
+   VfwCapture *This = (VfwCapture *)iface;
+
+   TRACE("(%p)\n", pInfo);
+
+   strcpyW(pInfo->achName, This->filterInfo.achName);
+   pInfo->pGraph = This->filterInfo.pGraph;
+
+   if (pInfo->pGraph)
+        IFilterGraph_AddRef(pInfo->pGraph);
+   return S_OK;
+}
+
+static HRESULT WINAPI VfwCapture_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
+{
+   VfwCapture *This = (VfwCapture *)iface;
+
+   TRACE("(%p, %s)\n", pGraph, debugstr_w(pName));
+
+   if (pName)
+       strcpyW(This->filterInfo.achName, pName);
+   else
+       *This->filterInfo.achName = 0;
+   This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
+
+   return S_OK;
+}
+
+static HRESULT WINAPI VfwCapture_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
+{
+   FIXME("(%p)\n", pVendorInfo);
+   return E_NOTIMPL;
+}
+
+static const IBaseFilterVtbl VfwCapture_Vtbl =
+{   
+   VfwCapture_QueryInterface,
+   VfwCapture_AddRef,
+   VfwCapture_Release,
+   VfwCapture_GetClassID,
+   VfwCapture_Stop,
+   VfwCapture_Pause,
+   VfwCapture_Run,
+   VfwCapture_GetState,
+   VfwCapture_SetSyncSource,
+   VfwCapture_GetSyncSource,
+   VfwCapture_EnumPins,
+   VfwCapture_FindPin,
+   VfwCapture_QueryFilterInfo,
+   VfwCapture_JoinFilterGraph,
+   VfwCapture_QueryVendorInfo
+};
+
+/* AMStreamConfig interface, we only need to implement {G,S}etFormat */
+static HRESULT WINAPI AMStreamConfig_QueryInterface(IAMStreamConfig * iface, REFIID riid, LPVOID * ppv)
+{
+   FIXME("%p: stub, not worth the effort\n", iface);
+   return S_OK;
+}
+
+static ULONG WINAPI AMStreamConfig_AddRef(IAMStreamConfig * iface)
+{
+   FIXME("%p: stub, not worth the effort\n", iface);
+   return 1;
+}
+
+static ULONG WINAPI AMStreamConfig_Release(IAMStreamConfig * iface)
+{
+   FIXME("%p: stub, not worth the effort\n", iface);
+   return 1;
+}
+
+static HRESULT WINAPI AMStreamConfig_SetFormat(IAMStreamConfig *iface,
+                                              AM_MEDIA_TYPE *pmt) {
+   FakeAMConfigImpl *This = (FakeAMConfigImpl *)iface;
+   BITMAPINFOHEADER *bmi;
+   VIDEOINFOHEADER *pvi;
+   TRACE("(): Printing out full format\n"
+         "\tMajor.Minor: %s.%s\n"
+         "\tFixedSize [%d] - Temporal [%d]\n"
+         "\tSample size [%lu] - Format type: %s\n"
+         "\tcb Format: %lu - Pointer: %p\n",
+         qzdebugstr_guid(&pmt->majortype), qzdebugstr_guid(&pmt->subtype),
+         pmt->bFixedSizeSamples, pmt->bTemporalCompression,
+         pmt->lSampleSize, qzdebugstr_guid(&pmt->formattype),
+         pmt->cbFormat, pmt->pbFormat);
+   // Presume pbFormat is a video
+   pvi = (void *)pmt->pbFormat;
+   TRACE("(): Printing out whole video info header\n"
+         "\tSource rectangle: (%lu,%lu,%lu,%lu)\n"
+         "\tTarget rectangle: (%lu,%lu,%lu,%lu)\n"
+         "\tBitrate: %lu, Error rate: %lu\n"
+         "\tTime per frame: %lu\n",
+         pvi->rcSource.left, pvi->rcSource.right, pvi->rcSource.top, pvi->rcSource.bottom,
+         pvi->rcTarget.left, pvi->rcTarget.right, pvi->rcTarget.top, pvi->rcTarget.bottom,
+         pvi->dwBitRate, pvi->dwBitErrorRate,
+         (ulong) pvi->AvgTimePerFrame);
+   bmi = &pvi->bmiHeader;
+   TRACE("(): Printing out bmi header\n"
+         "\tSize, in pixels: %lu\n"
+         "\tFormat: %ldx%ld\n"
+         "\tBit count: %d\n"
+         "\tSize image: %lu\n",
+         bmi->biSize, bmi->biWidth, bmi->biHeight, bmi->biBitCount, bmi->biSizeImage);
+   return Capture_SetMediaType(&This->myCap, pmt);
+}
+
+static HRESULT WINAPI AMStreamConfig_GetFormat(IAMStreamConfig *iface,
+                                              AM_MEDIA_TYPE **pmt) {
+   FakeAMConfigImpl *This = (FakeAMConfigImpl *)iface;
+   TRACE("%p -> (%p)\n", iface, pmt);
+   return Capture_GetMediaType(&This->myCap, pmt);
+}
+
+static HRESULT WINAPI AMStreamConfig_GetNumberOfCapabilities(IAMStreamConfig *iface, int *piCount, int *piSize)
+{
+   TRACE("%p: %p %p\n", iface, piCount, piSize);
+   return E_NOTIMPL; /* Not implemented for this interface */
+}
+
+static HRESULT WINAPI AMStreamConfig_GetStreamCaps(IAMStreamConfig *iface,
+int iIndex, AM_MEDIA_TYPE **pmt, BYTE *pSCC)
+{
+   TRACE("%p: %d %p %p\n", iface, iIndex, pmt, pSCC);
+   return E_NOTIMPL; /* Not implemented for this interface */
+}
+
+static const IAMStreamConfigVtbl IAMStreamConfig_VTable =
+{
+   AMStreamConfig_QueryInterface,
+   AMStreamConfig_AddRef,
+   AMStreamConfig_Release,
+   AMStreamConfig_SetFormat,
+   AMStreamConfig_GetFormat,
+   AMStreamConfig_GetNumberOfCapabilities,
+   AMStreamConfig_GetStreamCaps
+};
+
+/* IKsPropertySet interface */
+static HRESULT WINAPI KSP_QueryInterface(IKsPropertySet * iface, REFIID riid, LPVOID * ppv)
+{
+   TRACE("%p: stub, not worth the effort\n", iface);
+   return S_OK;
+}
+
+static ULONG WINAPI KSP_AddRef(IKsPropertySet * iface)
+{
+   TRACE("%p: stub, not worth the effort\n", iface);
+   return 1;
+}
+
+static ULONG WINAPI KSP_Release(IKsPropertySet * iface)
+{
+   TRACE("%p: stub, not worth the effort\n", iface);
+   return 1;
+}
+
+static HRESULT WINAPI KSP_Set(IKsPropertySet * iface, REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData)
+{
+   return E_NOTIMPL;
+}
+
+static HRESULT WINAPI KSP_Get(IKsPropertySet * iface, REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData, DWORD *pcbReturned)
+{
+   TRACE("()\n");
+   if (!IsEqualIID(guidPropSet, &AMPROPSETID_Pin))
+      return E_PROP_SET_UNSUPPORTED;
+   if (pPropData == NULL && pcbReturned == NULL)
+      return E_POINTER;
+   if (pcbReturned)
+      *pcbReturned = sizeof(GUID);
+   if (pPropData == NULL)
+      return S_OK;
+   if (cbPropData < sizeof(GUID))
+      return E_UNEXPECTED;
+   *(GUID *)pPropData = PIN_CATEGORY_PREVIEW;
+   TRACE("() Returning CaP\n");
+   return S_OK;
+}
+
+static HRESULT WINAPI KSP_QuerySupported(IKsPropertySet * iface, REFGUID guidPropSet, DWORD dwPropID, DWORD *pTypeSupport)
+{
+   TRACE("%p: stub\n", iface);
+   return E_NOTIMPL;
+}
+
+static IKsPropertySetVtbl KSP_VTable =
+{
+   KSP_QueryInterface,
+   KSP_AddRef,
+   KSP_Release,
+   KSP_Set,
+   KSP_Get,
+   KSP_QuerySupported
+};
+
+static HRESULT AcceptProcAFR(LPVOID iface, const AM_MEDIA_TYPE *pmt)
+{
+   VfwPinImpl *This = (VfwPinImpl *)iface;
+   FIXME("%p: %p\n", This, pmt);
+   return S_FALSE;
+}
+
+static HRESULT VfwPin_Construct(IBaseFilter * pBaseFilter, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
+{
+   VfwPinImpl * pPinImpl;
+   PIN_INFO piOutput;
+   ALLOCATOR_PROPERTIES ap;
+
+   pPinImpl = CoTaskMemAlloc(sizeof(*pPinImpl));
+   if (!pPinImpl)
+      return E_OUTOFMEMORY;
+
+   piOutput.dir = PINDIR_OUTPUT;
+   piOutput.pFilter = pBaseFilter;
+   strcpyW(piOutput.achName, wszOutputPinName);
+   ap.cBuffers = 8;
+   ap.cbBuffer = 256*1024;
+   ap.cbAlign = 0;
+   ap.cbPrefix = 0;
+
+   if (SUCCEEDED(OutputPin_Init(&piOutput, &ap, pBaseFilter, AcceptProcAFR, pCritSec, &pPinImpl->pin)))
+   {
+      pPinImpl->KSP_VT = &KSP_VTable;
+      pPinImpl->pin.pin.lpVtbl = &VfwPin_Vtbl;
+      pPinImpl->hEvent = CreateEventW(NULL, 0, 0, NULL);
+      pPinImpl->bFlushing = FALSE;
+      *ppPin = (IPin *)(&pPinImpl->pin.pin.lpVtbl);
+      return S_OK;
+   }
+   return E_FAIL;
+}
+
+static HRESULT WINAPI VfwPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
+{
+   VfwPinImpl *This = (VfwPinImpl *)iface;
+   TRACE("%s %p\n", qzdebugstr_guid(riid), ppv);
+   *ppv = NULL;
+   if (IsEqualIID(riid, &IID_IUnknown))
+      *ppv = (LPVOID)This;
+   else if (IsEqualIID(riid, &IID_IPin))
+      *ppv = (LPVOID)This;
+   else if (IsEqualIID(riid, &IID_IKsPropertySet))
+      *ppv = (LPVOID)&(This->KSP_VT);
+
+   if (*ppv)
+   {
+      IUnknown_AddRef((IUnknown *)(*ppv));
+      return S_OK;
+   }
+
+   FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
+   return E_NOINTERFACE;
+}
+
+static ULONG WINAPI VfwPin_Release(IPin * iface)
+{
+   VfwPinImpl *This = (VfwPinImpl *)iface;
+   ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
+   TRACE("()\n");
+
+   if (!refCount)
+   {
+      CloseHandle(This->hEvent);
+      CoTaskMemFree(This);
+      return 0;
+   }
+   return refCount;
+}
+
+static HRESULT WINAPI VfwPin_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
+{
+   ENUMMEDIADETAILS emd;
+   AM_MEDIA_TYPE q[1];
+   HRESULT hr;
+   int x = 0;
+
+   VfwPinImpl *This = (VfwPinImpl *)iface;
+   FIXME("(%p) - stub Why does this work and other things don't?!\n", ppEnum);
+   emd.cMediaTypes = 1;
+   for (; x < 1; x++) {
+      VIDEOINFOHEADER *pvi;
+      CopyMemory(&q[x].majortype, &MEDIATYPE_Video, sizeof (GUID));
+      CopyMemory(&q[x].subtype, &MEDIASUBTYPE_RGB24, sizeof (GUID));
+      q[x].bFixedSizeSamples = TRUE;
+      q[x].bTemporalCompression = FALSE;
+      q[x].lSampleSize = 230400;
+      CopyMemory(&q[x].formattype, &FORMAT_VideoInfo, sizeof (GUID));
+      q[x].cbFormat = sizeof(VIDEOINFOHEADER);
+      q[x].pbFormat = CoTaskMemAlloc(q[x].cbFormat);
+      pvi = (VIDEOINFOHEADER *)q[x].pbFormat;
+      pvi->rcSource.left = 0; pvi->rcSource.top = 0;
+      pvi->rcTarget.left = 0; pvi->rcTarget.top = 0;
+      pvi->rcSource.right = 320; pvi->rcSource.bottom = 240;
+      pvi->rcTarget.right = 320; pvi->rcTarget.bottom = 240;
+      pvi->dwBitRate = 10;
+      pvi->dwBitErrorRate = 0;
+      pvi->AvgTimePerFrame = (LONGLONG)(10000000.0 / 25);
+      pvi->bmiHeader.biSize = 40;
+      pvi->bmiHeader.biWidth = 320;
+      pvi->bmiHeader.biHeight = 240;
+      pvi->bmiHeader.biBitCount = 24;
+      pvi->bmiHeader.biCompression = BI_RGB;
+      pvi->bmiHeader.biSizeImage = 320*240*3;
+      pvi->bmiHeader.biXPelsPerMeter = 320;
+      pvi->bmiHeader.biYPelsPerMeter = 240;
+      pvi->bmiHeader.biClrUsed = 0;
+      pvi->bmiHeader.biPlanes = 1;
+      pvi->bmiHeader.biClrImportant = 0;
+   }
+   emd.pMediaTypes = q;
+   hr = IEnumMediaTypesImpl_Construct(&emd, ppEnum);
+   TRACE("%p -- %lx\n", This, hr);
+   /* CoTaskMemFree(q[0].pbFormat); */
+   return hr;
+}
+
+static const IPinVtbl VfwPin_Vtbl =
+{
+   VfwPin_QueryInterface,
+   IPinImpl_AddRef,
+   VfwPin_Release,
+   OutputPin_Connect,
+   OutputPin_ReceiveConnection,
+   IPinImpl_Disconnect,
+   IPinImpl_ConnectedTo,
+   IPinImpl_ConnectionMediaType,
+   IPinImpl_QueryPinInfo,
+   IPinImpl_QueryDirection,
+   IPinImpl_QueryId,
+   IPinImpl_QueryAccept,
+   VfwPin_EnumMediaTypes,
+   IPinImpl_QueryInternalConnections,
+   OutputPin_EndOfStream, 
+   OutputPin_BeginFlush,
+   OutputPin_EndFlush,
+   OutputPin_NewSegment
+};  
+
diff -Nru wine-old/include/axextend.idl wine-new/include/axextend.idl
--- wine-old/include/axextend.idl	2003-07-01 06:33:35.000000000 +0200
+++ wine-new/include/axextend.idl	2005-04-19 12:10:12.000000000 +0200
@@ -69,6 +69,23 @@
 interface IQualityControl;
 interface ISeekingPassThru;
 
+/*****************************************************************************
+ * 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);
+}
+
 typedef struct
 {
     CLSID Clsid;


More information about the wine-devel mailing list