|
|
@@ -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
|