Webcam code update

This commit is contained in:
Josh Yelon 2007-11-04 07:35:54 +00:00
parent 63aa6ff557
commit b11496ea26
10 changed files with 953 additions and 49 deletions

View File

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

View File

@ -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();

View File

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

View File

@ -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)
{
}

View File

@ -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() {

View File

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

View File

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

View File

@ -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];
}

View File

@ -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() {

View File

@ -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
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
//
// 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