Browse Source

Webcam code update

Josh Yelon 18 years ago
parent
commit
b11496ea26

+ 2 - 1
panda/src/movies/Sources.pp

@@ -38,7 +38,8 @@
         ffmpegAudio.cxx \
         ffmpegAudioCursor.cxx \
         ffmpegVirtualFile.cxx \
-        webcamVideo.cxx webcamVideoDX.cxx webcamVideoV4L.cxx webcamVideoNull.cxx \
+	webcamVideo.cxx \
+	webcamVideoDS.cxx \
         config_movies.cxx
     
   #define INSTALL_HEADERS \

+ 0 - 1
panda/src/movies/config_movies.cxx

@@ -54,7 +54,6 @@ init_libmovies() {
   InkblotVideo::init_type();
   InkblotVideoCursor::init_type();
   WebcamVideo::init_type();
-  WebcamVideo::init_cursor_type();
 #ifdef HAVE_FFMPEG
   FfmpegVideo::init_type();
   FfmpegVideoCursor::init_type();

+ 33 - 0
panda/src/movies/movieVideoCursor.I

@@ -162,3 +162,36 @@ next_start() const {
   return _next_start;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::streaming
+//       Access: Published
+//  Description: Returns true if the video frames are being "pushed"
+//               at us by something that operates at its own speed - 
+//               for example, a webcam.  In this case, the frames come
+//               when they're ready to come.  Attempting to read too
+//               soon will produce nothing, reading too late will cause
+//               frames to be dropped.  In this case, the ready flag
+//               can be used to determine whether or not a frame is
+//               ready for reading.
+//
+//               When streaming, you should still pay attention to
+//               last_start, but the value of next_start is only a
+//               guess.
+////////////////////////////////////////////////////////////////////
+INLINE bool MovieVideoCursor::
+streaming() const {
+  return _streaming;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::ready
+//       Access: Published
+//  Description: Returns true if the cursor is a streaming source, and
+//               if a video frame is ready to be read.  For non-
+//               streaming sources, this is always false.
+////////////////////////////////////////////////////////////////////
+INLINE bool MovieVideoCursor::
+ready() const {
+  return _ready;
+}
+

+ 3 - 1
panda/src/movies/movieVideoCursor.cxx

@@ -43,7 +43,9 @@ MovieVideoCursor(MovieVideo *src) :
   _aborted(false),
   _last_start(-1.0),
   _next_start(0.0),
-  _conversion_buffer(0)
+  _conversion_buffer(0),
+  _streaming(false),
+  _ready(false)
 {
 }
 

+ 4 - 0
panda/src/movies/movieVideoCursor.h

@@ -53,6 +53,8 @@ class EXPCL_PANDA_MOVIES MovieVideoCursor : public TypedWritableReferenceCount {
   INLINE bool aborted() const;
   INLINE double last_start() const;
   INLINE double next_start() const;
+  INLINE bool ready() const;
+  INLINE bool streaming() const;
   void setup_texture(Texture *tex) const;
   virtual void fetch_into_bitbucket(double time);
   virtual void fetch_into_texture(double time, Texture *t, int page);
@@ -77,6 +79,8 @@ class EXPCL_PANDA_MOVIES MovieVideoCursor : public TypedWritableReferenceCount {
   bool _aborted;
   double _last_start;
   double _next_start;
+  bool _streaming;
+  bool _ready;
   
 public:
   static TypeHandle get_class_type() {

+ 1 - 0
panda/src/movies/movies_composite1.cxx

@@ -16,5 +16,6 @@
 #include "ffmpegVirtualFile.cxx"
 
 #include "webcamVideo.cxx"
+#include "webcamVideoDS.cxx"
 
 #include "config_movies.cxx"

+ 42 - 0
panda/src/movies/webcamVideo.I

@@ -16,3 +16,45 @@
 //
 ////////////////////////////////////////////////////////////////////
 
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideo::get_name
+//       Access: Published
+//  Description: Returns the camera's name / description.
+////////////////////////////////////////////////////////////////////
+INLINE const string &WebcamVideo::
+get_name() const {
+  return _name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideo::get_size_x
+//       Access: Published
+//  Description: Returns the camera's size_x.
+////////////////////////////////////////////////////////////////////
+INLINE int WebcamVideo::
+get_size_x() const {
+  return _size_x;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideo::get_size_y
+//       Access: Published
+//  Description: Returns the camera's size_y.
+////////////////////////////////////////////////////////////////////
+INLINE int WebcamVideo::
+get_size_y() const {
+  return _size_y;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideo::get_fps
+//       Access: Published
+//  Description: Returns the camera's framerate.  This
+//               is a maximum theoretical: the actual performance 
+//               will depend on the speed of the hardware.
+////////////////////////////////////////////////////////////////////
+INLINE int WebcamVideo::
+get_fps() const {
+  return _fps;
+}

+ 53 - 31
panda/src/movies/webcamVideo.cxx

@@ -20,28 +20,13 @@
 #include "pandabase.h"
 #include "movieVideoCursor.h"
 
+pvector<PT(WebcamVideo)> WebcamVideo::_all_webcams;
 TypeHandle WebcamVideo::_type_handle;
 
-////////////////////////////////////////////////////////////////////
-//     Function: WebcamVideo::Constructor
-//       Access: Public
-//  Description: The parameters x,y, and fps are suggestions.  The
-//               webcam will match these as closely as it can, but
-//               of course, there are no guarantees.
-////////////////////////////////////////////////////////////////////
-WebcamVideo::
-WebcamVideo(const string &dev, int x, int y, int fps) :
-  MovieVideo("webcam"),
-  _specified_device(dev),
-  _specified_x(x),
-  _specified_y(y),
-  _specified_fps(y)
-{
-}
 
 ////////////////////////////////////////////////////////////////////
 //     Function: WebcamVideo::Destructor
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 WebcamVideo::
@@ -49,22 +34,59 @@ WebcamVideo::
 }
 
 ////////////////////////////////////////////////////////////////////
-// The rest of this file is OS-dependent.
-// We include the appropriate version depending 
-// the user's compile-configuration.
+//     Function: WebcamVideo::find_all_webcams
+//       Access: Public
+//  Description: Scans the hardware for webcams, and pushes them
+//               onto the global list of all webcams.
+//
+//               There are several implementations of WebcamVideo,
+//               including one based on DirectShow, one based on
+//               Video4Linux, and so forth.  These implementations
+//               are contained in one C++ file each, and they export
+//               nothing at all except a single "find_all" function.
+//               Otherwise, they can only be accessed through the
+//               virtual methods of the WebcamVideo objects they
+//               create.
 ////////////////////////////////////////////////////////////////////
+void WebcamVideo::
+find_all_webcams() {
+  static bool initialized = false;
+  if (initialized) return;
+  initialized = true;
 
-#if defined(HAVE_DX9)
-
-#include "webcamVideoDX.cxx"
-
-#elif defined(HAVE_VIDEO4LINUX)
-
-#include "webcamVideoV4L.cxx"
-
-#else
-
-#include "webcamVideoNull.cxx"
+#ifdef HAVE_DIRECTCAM
+  extern void find_all_webcams_ds();
+  find_all_webcams_ds();
+#endif
 
+#ifdef HAVE_VIDEO4LINUX
+  extern void find_all_webcams_v4l();
+  find_all_webcams_v4l();
 #endif
+}
 
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideo::get_num_options
+//       Access: Public
+//  Description: Returns the number of webcam options.  An "option"
+//               consists of a device plus a set of configuration
+//               parameters.  For example, "Creative Webcam Live at
+//               640x480, 30 fps" is an option.  
+////////////////////////////////////////////////////////////////////
+int WebcamVideo::
+get_num_options() {
+  find_all_webcams();
+  return _all_webcams.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideo::get_option
+//       Access: Public
+//  Description: Returns the nth webcam option.
+////////////////////////////////////////////////////////////////////
+PT(WebcamVideo) WebcamVideo::
+get_option(int n) {
+  find_all_webcams();
+  nassertr((n >= 0) && (n < (int)_all_webcams.size()), NULL);
+  return _all_webcams[n];
+}

+ 20 - 14
panda/src/movies/webcamVideo.h

@@ -26,26 +26,32 @@
 // Description : Allows you to open a webcam or other video capture
 //               device as a video stream.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_MOVIES WebcamVideo : public MovieVideo {
+class EXPCL_PANDASKEL WebcamVideo : public MovieVideo {
 
- PUBLISHED:
-  WebcamVideo(const string &dev, int x=640, int y=480, int fps=24);
+PUBLISHED:
   virtual ~WebcamVideo();
 
-  static int    get_num_devices();
-  static string get_device_name(int n);
+  static int             get_num_options();
+  static PT(WebcamVideo) get_option(int n);
   
-  virtual PT(MovieVideoCursor) open();
-  
- private:
-  string _specified_device;
-  int    _specified_x;
-  int    _specified_y;
-  int    _specified_fps;
-  friend class WebcamVideoCursor;
+  INLINE const string &get_name() const;
+  INLINE int get_size_x() const;
+  INLINE int get_size_y() const;
+  INLINE int get_fps() const;
   
+  virtual PT(MovieVideoCursor) open() = 0;
+
 public:
-  static void init_cursor_type();
+  static void find_all_webcams();
+  
+
+protected:
+  string _name;
+  int _size_x;
+  int _size_y;
+  int _fps;
+
+  static pvector<PT(WebcamVideo)> _all_webcams;
 
 public:
   static TypeHandle get_class_type() {

+ 794 - 0
panda/src/movies/webcamVideoDS.cxx

@@ -0,0 +1,794 @@
+// Filename: webcamVideoDX.cxx
+// Created by: jyelon (01Nov2007)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2007, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+//
+// It goes against Panda3D coding style conventions to hide an
+// entire class in a C++ file and not expose it through header
+// files at all.  However, in this case, these classes are so full
+// of OS-specific junk that I feel it is better to hide them
+// entirely.  - Josh
+//
+////////////////////////////////////////////////////////////////////
+//
+// This code was created by studying and adapting the VDOGRAB
+// library by Shu-Kai Yang and the videoInput library by Theodore
+// Watson.  We owe both of them a great deal of thanks for
+// figuring all this out.  Both of their libraries have 
+// informal licenses (the "do whatever you want and don't blame
+// me" sort), so I think there's not a problem using their code.
+//
+////////////////////////////////////////////////////////////////////
+
+#ifdef HAVE_DIRECTCAM
+
+#define WIN32_LEAN_AND_MEAN 
+
+#undef Configure
+
+#pragma warning(disable:4100) // warning C4100: unreferenced formal parameter
+#pragma warning(disable:4201) // warning C4201: nonstandard extension used : nameless struct/union
+#pragma warning(disable:4511) // warning C4511: copy constructor could not be generated
+#pragma warning(disable:4512) // warning C4512: assignment operator could not be generated
+#pragma warning(disable:4514) // warning C4514: "unreferenced inline function has been removed"
+
+#include <windows.h>
+#include <windowsx.h>
+#include <olectl.h>
+#include <mmsystem.h>
+#include <strmif.h>     // Generated IDL header file for streams interfaces
+#include <amvideo.h>    // ActiveMovie video interfaces and definitions
+#include <amaudio.h>    // ActiveMovie audio interfaces and definitions
+#include <control.h>    // generated from control.odl
+#include <evcode.h>     // event code definitions
+#include <uuids.h>      // declaration of type GUIDs and well-known clsids
+#include <errors.h>     // HRESULT status and error definitions
+#include <edevdefs.h>   // External device control interface defines
+#include <audevcod.h>   // audio filter device error event codes
+#include <dvdevcod.h>   // DVD error event codes
+#include <comutil.h>
+
+#include <wchar.h>
+#include <string.h>
+#include <windows.h>
+#include <qedit.h>
+#include <atlbase.h>
+
+////////////////////////////////////////////////////////////////////
+//       Class : WebcamVideoDS
+// Description : The directshow implementation of webcams.
+////////////////////////////////////////////////////////////////////
+
+class WebcamVideoDS : public WebcamVideo
+{
+public:
+  static void find_all_webcams_ds();
+  friend void find_all_webcams_ds();
+
+private:
+  typedef pvector<PT(WebcamVideoDS)> WebcamVideoList;
+
+  static int  media_score(AM_MEDIA_TYPE *media);
+  static int  media_x(AM_MEDIA_TYPE *media);
+  static int  media_y(AM_MEDIA_TYPE *media);
+  static int  media_fps(AM_MEDIA_TYPE *media);
+  static void delete_media_type(AM_MEDIA_TYPE *media);
+  static string bstr_to_string(const BSTR &source);
+  static string get_moniker_name(IMoniker *pMoniker);
+  static void add_device(WebcamVideoList &list, IMoniker *pMoniker, AM_MEDIA_TYPE *media);
+
+  virtual PT(MovieVideoCursor) open();
+
+  IMoniker *_moniker;
+  AM_MEDIA_TYPE *_media;
+  
+  friend class WebcamVideoCursorDS;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    MovieVideo::init_type();
+    register_type(_type_handle, "WebcamVideoDS",
+                  WebcamVideo::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+TypeHandle WebcamVideoDS::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//       Class : WebcamVideoCursorDS
+// Description : The directshow implementation of webcams.
+////////////////////////////////////////////////////////////////////
+
+
+class WebcamVideoCursorDS : public MovieVideoCursor
+{
+public:
+  WebcamVideoCursorDS(WebcamVideoDS *src);
+  virtual ~WebcamVideoCursorDS();
+  virtual void fetch_into_buffer(double time, unsigned char *block, bool rgba);
+
+public:
+  void cleanup();
+
+  class CSampleGrabberCB : public ISampleGrabberCB 
+  { 
+  public:
+    WebcamVideoCursorDS *_host;
+    
+    ULONG __stdcall AddRef() { return 2; }
+    ULONG __stdcall Release() { return 1; }
+    
+    HRESULT __stdcall QueryInterface(REFIID riid, void ** ppv);
+    HRESULT __stdcall SampleCB(double SampleTime, IMediaSample *pSample);
+    HRESULT __stdcall BufferCB(double dblSampleTime, BYTE *pBuffer, long lBufferSize);
+  };
+
+  unsigned char *_buffer;
+  IMediaSample *_saved;
+
+  IGraphBuilder           *_pGraphBuilder;
+  ICaptureGraphBuilder2   *_pCaptureBuilder;
+  IBaseFilter             *_pSrcFilter;
+  IAMStreamConfig         *_pStreamConfig;
+  CComPtr<ISampleGrabber>  _pSampleGrabber; 
+  IBaseFilter             *_pStreamRenderer;
+  IMediaControl           *_pMediaCtrl;
+  //  IMemAllocator           *_pAllocator;
+  CSampleGrabberCB         _sample_cb;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    MovieVideoCursor::init_type();
+    register_type(_type_handle, "WebcamVideoCursorDS",
+                  MovieVideoCursor::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+TypeHandle WebcamVideoCursorDS::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideoDS::media_score
+//       Access: Public, Static
+//  Description: Evaluate an AM_MEDIA_TYPE to determine how
+//               desirable it is for our purposes.  Lower is better.
+////////////////////////////////////////////////////////////////////
+int WebcamVideoDS::
+media_score(AM_MEDIA_TYPE *media) {
+  const GUID &subtype = media->subtype;
+  if (subtype == MEDIASUBTYPE_RGB24)  return 1;
+  if (subtype == MEDIASUBTYPE_RGB32)  return 2;
+  if (subtype == MEDIASUBTYPE_RGB555) return 3;
+  if (subtype == MEDIASUBTYPE_RGB565) return 3;
+  return 4;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideoDS::media_x
+//       Access: Public, Static
+//  Description: Returns the x-resolution of the AM_MEDIA_TYPE
+////////////////////////////////////////////////////////////////////
+int WebcamVideoDS::
+media_x(AM_MEDIA_TYPE *media) {
+  VIDEOINFOHEADER *header = (VIDEOINFOHEADER*)(media->pbFormat);
+  return (header->bmiHeader.biWidth);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideoDS::media_y
+//       Access: Public, Static
+//  Description: Returns the y-resolution of the AM_MEDIA_TYPE
+////////////////////////////////////////////////////////////////////
+int WebcamVideoDS::
+media_y(AM_MEDIA_TYPE *media) {
+  VIDEOINFOHEADER *header = (VIDEOINFOHEADER*)(media->pbFormat);
+  return (header->bmiHeader.biHeight);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideoDS::media_fps
+//       Access: Public, Static
+//  Description: Returns the frame-rate of the AM_MEDIA_TYPE
+////////////////////////////////////////////////////////////////////
+int WebcamVideoDS::
+media_fps(AM_MEDIA_TYPE *media) {
+  VIDEOINFOHEADER *header = (VIDEOINFOHEADER*)(media->pbFormat);
+  return int(10000000.0 / (header->AvgTimePerFrame));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideoDS::delete_media_type
+//       Access: Public, Static
+//  Description: Free all memory of the AM_MEDIA_TYPE
+////////////////////////////////////////////////////////////////////
+void WebcamVideoDS::
+delete_media_type(AM_MEDIA_TYPE *pmt) {
+  if (pmt == NULL) {
+    return;
+  }
+  if (pmt->cbFormat != 0) {
+    CoTaskMemFree((PVOID)pmt->pbFormat);
+    pmt->cbFormat = 0;
+    pmt->pbFormat = NULL;
+  }
+  if (pmt->pUnk != NULL) {
+    // Unecessary because pUnk should not be used, but safest.
+    pmt->pUnk->Release();
+    pmt->pUnk = NULL;
+  }
+  CoTaskMemFree(pmt);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideoDS::bstr_to_string
+//       Access: Public, Static
+//  Description: Converts a visual basic BSTR to a C++ string.
+////////////////////////////////////////////////////////////////////
+string WebcamVideoDS::
+bstr_to_string(const BSTR &source) {
+  string res = "";
+  int count = 0;
+  while( source[count] != 0x00 ) {
+    res.push_back(source[count]);
+    count++;
+  }
+  return res;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideoDS::get_moniker_name
+//       Access: Public, Static
+//  Description: Obtains the text name associated with an IMoniker
+////////////////////////////////////////////////////////////////////
+string WebcamVideoDS::
+get_moniker_name(IMoniker *pMoniker) {
+  string res = "Unknown Device";
+  IPropertyBag *propBag=NULL;
+  VARIANT name; HRESULT hResult;
+  pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)&propBag);
+  VariantInit(&name);
+  hResult = propBag->Read(L"FriendlyName", &name, 0);
+  if (!hResult != S_OK) {
+    res = bstr_to_string(name.bstrVal);
+    goto done;
+  }
+  hResult = propBag->Read(L"Description", &name, 0);
+  if (!hResult != S_OK) {
+    res = bstr_to_string(name.bstrVal);
+    goto done;
+  }
+ done:
+  VariantClear(&name);
+  propBag->Release();
+  return res;
+}
+
+struct DeviceInfo {
+  string _name;
+  IMoniker *_moniker;
+  AM_MEDIA_TYPE *_media;
+};
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideoDS::add_device
+//       Access: Public, Static
+//  Description: Creates a new WebcamVideoDS and adds it to the list,
+//               unless there is already a very similar configuration
+//               in the list.  If there is already a very similar
+//               configuration, this routine will leave one or the
+//               other on the list based on a scoring system.
+////////////////////////////////////////////////////////////////////
+void WebcamVideoDS::
+add_device(WebcamVideoList &list, IMoniker *pMoniker, AM_MEDIA_TYPE *media) {
+  for (int i=0; i<(int)list.size(); i++) {
+    if ((list[i]->_moniker == pMoniker)&&
+	(media_x(list[i]->_media) == media_x(media))&&
+	(media_y(list[i]->_media) == media_y(media))) {
+      int oldscore = media_score(list[i]->_media);
+      if (media_score(media) < oldscore) {
+	delete_media_type(list[i]->_media);
+	list[i]->_media = media;
+      } else {
+	delete_media_type(media);
+      }
+      return;
+    }
+  }
+  PT(WebcamVideoDS) wc = new WebcamVideoDS;
+  ostringstream name;
+  name << "DirectShow: " << get_moniker_name(pMoniker) << " @ " << media_x(media) << " x " << media_y(media) << "  FPS:" << media_fps(media);
+  wc->_name = name.str();
+  wc->_size_x = media_x(media);
+  wc->_size_y = media_y(media);
+  wc->_fps = media_fps(media);
+  wc->_moniker = pMoniker;
+  wc->_media = media;
+  list.push_back(wc);
+  cerr << "Added device: " << wc->_name << "\n";
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideoDS::find_all_webcams_ds
+//       Access: Public, Static
+//  Description: Finds all DirectShow webcams and adds them to 
+//               the global list _all_webcams.
+////////////////////////////////////////////////////////////////////
+void WebcamVideoDS::
+find_all_webcams_ds() {
+
+  pvector <PT(WebcamVideoDS)> list;
+  
+  ICreateDevEnum *pCreateDevEnum=NULL;
+  IEnumMoniker *pEnumMoniker=NULL;
+  IGraphBuilder *pGraphBuilder=NULL;
+  ICaptureGraphBuilder2 *pCaptureGraphBuilder2=NULL;
+  IMoniker *pMoniker=NULL;
+  IBaseFilter *pBaseFilter=NULL;
+  IAMStreamConfig *pStreamConfig=NULL;
+  HRESULT hResult;
+  ULONG cFetched;
+  
+  hResult=CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
+			   IID_IGraphBuilder,(void**)&pGraphBuilder);
+  if (hResult != S_OK) goto cleanup;
+  hResult=CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC,
+			   IID_ICaptureGraphBuilder2, (void**)&pCaptureGraphBuilder2);
+  if (hResult != S_OK) goto cleanup;
+  hResult = pCaptureGraphBuilder2->SetFiltergraph(pGraphBuilder);
+  if (hResult != S_OK) goto cleanup;
+  hResult=CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
+                           IID_ICreateDevEnum, (void**)&pCreateDevEnum);
+  if (hResult != S_OK) goto cleanup;
+  hResult=pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
+						&pEnumMoniker, 0);
+  if(hResult != S_OK) goto cleanup;
+  
+  while(1) {
+    if (pMoniker)       { pMoniker->Release();  pMoniker=0; }
+    if (pBaseFilter)    { pBaseFilter->Release(); pBaseFilter=0; }
+    if (pStreamConfig)  { pStreamConfig->Release(); pStreamConfig=0; }
+
+    hResult = pEnumMoniker->Next(1, &pMoniker, &cFetched);
+    if (hResult != S_OK) break;
+
+    hResult = pMoniker->BindToObject(NULL,NULL,IID_IBaseFilter, (void**)&pBaseFilter);
+    if (hResult != S_OK) continue;
+    hResult = pCaptureGraphBuilder2->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pBaseFilter,
+						   IID_IAMStreamConfig, (void **)&pStreamConfig);
+    if (hResult != S_OK) continue;  
+    int iCount, iSize;
+    hResult = pStreamConfig->GetNumberOfCapabilities(&iCount, &iSize);
+    if (hResult != S_OK || (iSize != sizeof(VIDEO_STREAM_CONFIG_CAPS))) continue;
+    for (int iFormat=0; iFormat<iCount; iFormat++) {
+      AM_MEDIA_TYPE *mtype;
+      VIDEO_STREAM_CONFIG_CAPS caps;
+      hResult = pStreamConfig->GetStreamCaps(iFormat, &mtype, (BYTE*)&caps);
+      if (mtype->majortype == MEDIATYPE_Video) {
+	VIDEOINFOHEADER *header = (VIDEOINFOHEADER*)(mtype->pbFormat);
+	header->bmiHeader.biWidth  = caps.MaxOutputSize.cx;
+	header->bmiHeader.biHeight = caps.MaxOutputSize.cy;
+	add_device(list, pMoniker, mtype);
+      }
+    }
+    
+    pMoniker = 0;
+  }
+  
+ cleanup:
+  if (pCreateDevEnum) { pCreateDevEnum->Release(); pCreateDevEnum=0; }
+  if (pEnumMoniker)   { pEnumMoniker->Release();   pEnumMoniker=0; }
+  if (pGraphBuilder)  { pGraphBuilder->Release();  pGraphBuilder=0; }
+  if (pCaptureGraphBuilder2) { pCaptureGraphBuilder2->Release(); pCaptureGraphBuilder2=0; }
+  if (pMoniker)       { pMoniker->Release();  pMoniker=0; }
+  if (pBaseFilter)    { pBaseFilter->Release(); pBaseFilter=0; }
+  if (pStreamConfig)  { pStreamConfig->Release(); pStreamConfig=0; }
+  
+  for (int i=0; i<(int)list.size(); i++) {
+    WebcamVideoDS *obj = list[i];
+    _all_webcams.push_back(obj);
+  }
+}
+
+void find_all_webcams_ds() {
+  WebcamVideoDS::init_type();
+  WebcamVideoCursorDS::init_type();
+  WebcamVideoDS::find_all_webcams_ds();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideoDS::open
+//       Access: Published, Virtual
+//  Description: Open this video, returning a MovieVideoCursor.
+////////////////////////////////////////////////////////////////////
+PT(MovieVideoCursor) WebcamVideoDS::
+open() {
+  return new WebcamVideoCursorDS(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideoCursorDS::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+WebcamVideoCursorDS::
+WebcamVideoCursorDS(WebcamVideoDS *src) :
+  MovieVideoCursor(src),
+  _pGraphBuilder(NULL),
+  _pCaptureBuilder(NULL),
+  _pSrcFilter(NULL),
+  _pStreamConfig(NULL),
+  _pStreamRenderer(NULL),
+  _pMediaCtrl(NULL)
+{
+  AM_MEDIA_TYPE mediaType;
+  VIDEOINFOHEADER *pVideoInfo;
+  
+  HRESULT hResult;
+  
+  hResult=CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
+                           IID_IGraphBuilder,(void**)&_pGraphBuilder);
+  if(hResult != S_OK) { cleanup(); return; }
+  
+  hResult=CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC,
+                           IID_ICaptureGraphBuilder2, (void**)&_pCaptureBuilder);
+  if(hResult != S_OK) { cleanup(); return; }
+  
+  _pCaptureBuilder->SetFiltergraph(_pGraphBuilder);
+  cerr << "  IID_IGraphBuilder & IID_ICaptureGraphBuilder2 are established.\n";
+  
+  hResult=_pGraphBuilder->QueryInterface(IID_IMediaControl, (void **)&_pMediaCtrl);
+  if(hResult != S_OK)
+    {  cerr << "  Can not get the IID_IMediaControl interface!";
+    cleanup(); return;  }
+  cerr << "  IID_IMediaControl interface is acquired.\n";
+  
+  src->_moniker->BindToObject(NULL,NULL,IID_IBaseFilter, (void**)&_pSrcFilter);
+  if(_pSrcFilter == NULL)
+    {  cerr << "  Such capture device is not found.\n";
+    cleanup(); return;  }
+  cerr << "  The capture filter is acquired.\n";
+  
+  hResult=_pGraphBuilder->AddFilter(_pSrcFilter, L"Capture Filter");
+  if(hResult != DD_OK)
+    {  cerr << "  The capture filter can not be added to the graph.\n";
+    cleanup(); return;  }
+  cerr << "  The capture filter has been added to the graph.\n";
+  
+  
+  hResult = _pCaptureBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, _pSrcFilter,
+					    IID_IAMStreamConfig, (void **)&_pStreamConfig);
+  if (hResult != S_OK) {
+    cerr << "Could not get stream config interface.\n";
+    cleanup(); return;
+  }
+  hResult = _pStreamConfig->SetFormat(src->_media);
+  if (hResult != S_OK) {
+    cerr << "Could not select desired video resolution\n";
+    cleanup(); return;
+  }
+
+  _pSampleGrabber.CoCreateInstance(CLSID_SampleGrabber);
+  if(!_pSampleGrabber)
+    {  cerr << "  Can not create the sample grabber, maybe qedit.dll is not registered?";
+    cleanup(); return;  }
+  
+  
+  CComQIPtr< IBaseFilter, &IID_IBaseFilter > pGrabberFilter(_pSampleGrabber);
+  cerr << "  IID_IBaseFilter of CLSID_SampleGrabber is acquired.\n";
+  
+  ZeroMemory(&mediaType, sizeof(AM_MEDIA_TYPE));
+  mediaType.majortype=MEDIATYPE_Video;
+  mediaType.subtype=MEDIASUBTYPE_RGB24;
+  hResult=_pSampleGrabber->SetMediaType(&mediaType);
+  if(hResult != S_OK)
+    {  cerr << "  Fail to set the media type!";
+    cleanup(); return;  }
+  cerr << "  The media type of the sample grabber is set 24-bit RGB.\n";
+  
+  hResult=_pGraphBuilder->AddFilter(pGrabberFilter, L"Sample Grabber");
+  if(hResult != S_OK)
+    {  cerr << "  Fail to add the sample grabber to the graph.";
+    cleanup(); return;  }
+  cerr << "  The sample grabber has been added to the graph.\n";
+
+  //used to give the video stream somewhere to go to.  
+  hResult = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&_pStreamRenderer);
+  if(hResult != S_OK)
+    {  cerr << "  Can not create the null renderer.";
+    cleanup(); return;  }
+  cerr << "  IID_IBaseFilter of CLSID_NullRenderer is acquired.\n";
+
+  hResult=_pGraphBuilder->AddFilter(_pStreamRenderer, L"Stream Renderer");
+  if(hResult != S_OK)
+    {  cerr << "  Fail to add the Null Renderer to the graph.";
+    cleanup(); return;  }
+  cerr << "  The Null Renderer has been added to the graph.\n";
+
+  hResult=_pCaptureBuilder->RenderStream(&PIN_CATEGORY_CAPTURE,
+					 &MEDIATYPE_Video, _pSrcFilter, pGrabberFilter, _pStreamRenderer);
+  if(hResult != S_OK) {
+    cerr << "  ICaptureGraphBuilder2::RenderStream() can not connect the pins\n";
+    cleanup(); return;
+  }
+  
+  hResult=_pSampleGrabber->GetConnectedMediaType(&mediaType);
+  if(hResult != S_OK) {
+    cerr << "  Failed to read the connected media type.";
+    cleanup(); return; 
+  }
+  
+  //  IPin *iPin;
+  //  hResult = FindInputPin(pGrabberFilter, &iPin);
+  //  if ((iPin == 0)||(hResult != S_OK)) {
+  //    cerr << "Could not get sampler input pin.\n";
+  //    cleanup(); return;
+  //  }
+  //  CComQIPtr< IMemInputPin, &IID_IMemInputPin > pMemInputPin(iPin);
+  //  if (pMemInputPin == 0) {
+  //    cerr << "Could not get sampler meminput pin.\n";
+  //    cleanup(); return;
+  //  }
+  //  hResult = pMemInputPin->GetAllocator(&_pAllocator);
+  //  if (hResult != S_OK) {
+  //    cerr << "Could not get sample grabber allocator handle.\n";
+  //  }
+  //  ALLOCATOR_PROPERTIES props, aprops;
+  //  hResult = _pAllocator->GetProperties(&props);
+  //  if (hResult != S_OK) {
+  //    cerr << "Could not get allocator properties.\n";
+  //  }
+  //  cerr << "Allocator properties: cBuffers=" << props.cBuffers << "\n";
+  //  props.cBuffers += 10;
+  //  hResult = _pAllocator->SetProperties(&props, &aprops);
+  //  if (hResult != S_OK) {
+  //    cerr << "Could not set allocator properties.\n";
+  //  }
+  //  cerr << "Allocator properties (adjusted): cBuffers=" << aprops.cBuffers << "\n";
+  
+  pVideoInfo=(VIDEOINFOHEADER*)mediaType.pbFormat;
+  _size_x = pVideoInfo->bmiHeader.biWidth;
+  _size_y = pVideoInfo->bmiHeader.biHeight;
+  cerr << "Connected media type " << _size_x << " x " << _size_y << "\n";
+  
+  _sample_cb._host = this;
+  _num_components = 3;
+  _length = 1.0E10;
+  _can_seek = false;
+  _can_seek_fast = false;
+  _aborted = false;
+  _last_start = -1.0;
+  _next_start = 0.0;
+  _streaming = true;
+  _buffer = new unsigned char[_size_x * _size_y * 3];
+  _ready = false;
+
+  if(mediaType.cbFormat != 0) {
+    CoTaskMemFree((PVOID)mediaType.pbFormat);
+    mediaType.cbFormat=0;
+    mediaType.pbFormat=NULL;
+  }
+  
+  if(mediaType.pUnk != NULL) {
+    mediaType.pUnk->Release();
+    mediaType.pUnk=NULL;
+  }
+  
+  _pSampleGrabber->SetBufferSamples(FALSE);
+  _pSampleGrabber->SetOneShot(FALSE);
+  
+  hResult=_pSampleGrabber->SetCallback(&_sample_cb, 0);
+  if(hResult != S_OK) {
+    cerr << "  Can not set the callback interface!";
+    cleanup(); return;
+  }
+  
+  _pMediaCtrl->Run();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideoCursorDS::cleanup
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void WebcamVideoCursorDS::
+cleanup() {
+  if (_buffer) {
+    delete[] _buffer;
+    _buffer = 0;
+  }
+
+  if (_pMediaCtrl) {
+    _pMediaCtrl->Stop();
+  }
+  
+  if(_pMediaCtrl)       {  _pMediaCtrl->Release();  _pMediaCtrl=NULL;  }
+  if(_pCaptureBuilder)  {  _pCaptureBuilder->Release();  _pCaptureBuilder=NULL;  }
+  if(_pGraphBuilder)    {  _pGraphBuilder->Release();  _pGraphBuilder=NULL;  }
+  if(_pSampleGrabber.p) {  _pSampleGrabber.Release();  }
+  if(_pStreamRenderer)  {  _pStreamRenderer->Release();  _pStreamRenderer=NULL;  }
+  if(_pSrcFilter)       {  _pSrcFilter->Release();  _pSrcFilter=NULL;  }
+  if(_pStreamConfig)    {  _pStreamConfig->Release();  _pStreamConfig=NULL;  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideoCursorDS::Destructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+WebcamVideoCursorDS::
+~WebcamVideoCursorDS() {
+  cleanup();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideoCursorDS::fetch_into_buffer
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void WebcamVideoCursorDS::
+fetch_into_buffer(double time, unsigned char *block, bool bgra) {
+  
+  if (!_ready) {
+    return;
+  }
+
+#ifdef LOCKING_MODE  
+  unsigned char *ptr;
+  int pixels = _size_x * _size_y;
+  HRESULT res = _saved->GetPointer(&ptr);
+  if (res == S_OK) {
+    int size = _saved->GetActualDataLength();
+    if (size == pixels * 3) {
+      if (bgra) {
+	for (int i=0; i<pixels; i++) {
+	  block[i*4+0] = ptr[i*3+0];
+	  block[i*4+1] = ptr[i*3+1];
+	  block[i*4+2] = ptr[i*3+2];
+	  block[i*4+3] = 255;
+	}
+      } else {
+	memcpy(block, ptr, pixels * 3);
+      }
+    }
+  }
+  _saved->Release();
+#else
+  int pixels = _size_x * _size_y;
+  if (bgra) {
+    for (int i=0; i<pixels; i++) {
+      block[i*4+0] = _buffer[i*3+0];
+      block[i*4+1] = _buffer[i*3+1];
+      block[i*4+2] = _buffer[i*3+2];
+      block[i*4+3] = 255;
+    }
+  } else {
+    memcpy(block, _buffer, pixels * 3);
+  }
+#endif
+  
+  _ready = false;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideoCursorDS::CSampleGrabberCB::QueryInterface
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+HRESULT __stdcall WebcamVideoCursorDS::CSampleGrabberCB::QueryInterface(REFIID riid, void **ppv)
+{
+  if((riid == IID_ISampleGrabberCB) || (riid == IID_IUnknown)) {
+    *ppv=(void *)static_cast<ISampleGrabberCB *> (this);
+    return NOERROR;
+  } 
+  return E_NOINTERFACE;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideoCursorDS::CSampleGrabberCB::SampleCB
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+HRESULT __stdcall WebcamVideoCursorDS::CSampleGrabberCB::SampleCB(double SampleTime, IMediaSample *pSample)
+{
+  if (_host->_ready) {
+    return 0;
+  }
+
+#ifdef LOCKING_MODE  
+  pSample->AddRef();
+  _host->_saved = pSample;
+#else
+  unsigned char *ptr;
+  int pixels = _host->_size_x * _host->_size_y;
+  HRESULT res = pSample->GetPointer(&ptr);
+  if (res == S_OK) {
+    int size = pSample->GetActualDataLength();
+    if (size == pixels * 3) {
+      memcpy(_host->_buffer, ptr, size);
+    }
+  }
+#endif
+
+  _host->_ready = true;
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebcamVideoCursorDS::CSampleGrabberCB::BufferCB
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+HRESULT __stdcall WebcamVideoCursorDS::CSampleGrabberCB::BufferCB(double dblSampleTime, BYTE *pBuffer, long lBufferSize)
+{ 
+  // Not used.
+  return 0;
+}
+
+//HRESULT FindInputPin(IBaseFilter *pFilter, IPin **ppPin)
+//{
+//  if (!pFilter || ! ppPin)
+//    return E_POINTER;
+//  
+//  *ppPin = 0;
+//  HRESULT hr;
+//  //Find the output pin of the Source Filter
+//  IEnumPins *pPinEnum;
+//  hr = pFilter->EnumPins(&pPinEnum);
+//  if (FAILED(hr))
+//    return E_FAIL;
+//  
+//  IPin *pSearchPin;
+//  while (pPinEnum->Next(1, &pSearchPin, NULL) == S_OK)
+//    {
+//      PIN_DIRECTION pPinDir;
+//      hr = pSearchPin->QueryDirection(&pPinDir);
+//      if (FAILED(hr))
+//	return E_FAIL;
+//      if (pPinDir == PINDIR_INPUT)
+//	{
+//	  //Found out pin
+//	  *ppPin = pSearchPin;
+//	  break;
+//	}
+//    }
+//  pPinEnum->Release();
+//  return hr;
+//}
+
+
+#endif // HAVE_DIRECTSHOW