Video4Linux2 support for WebcamVideo and a few minor improvements to the webcam code.

This commit is contained in:
rdb 2010-06-23 08:51:53 +00:00
parent b709455d4e
commit 00ee003d1e
11 changed files with 780 additions and 76 deletions

View File

@ -9,26 +9,30 @@
#define TARGET p3vision
#define LOCAL_LIBS \
display text pgraph gobj linmath putil audio movies
#define COMBINED_SOURCES vision_composite1.cxx
#define SOURCES \
arToolKit.I arToolKit.h \
config_vision.h \
openCVTexture.I openCVTexture.h \
webcamVideo.h webcamVideo.I
webcamVideo.h webcamVideo.I \
webcamVideoCursorV4L.h webcamVideoV4L.h
#define INCLUDED_SOURCES \
arToolKit.cxx \
config_vision.cxx \
openCVTexture.cxx \
webcamVideo.cxx \
webcamVideoDS.cxx
webcamVideoDS.cxx \
webcamVideoCursorV4L.cxx \
webcamVideoV4L.cxx
#define INSTALL_HEADERS \
arToolKit.I arToolKit.h \
openCVTexture.I openCVTexture.h \
webcamVideo.h webcamVideo.I
webcamVideo.h webcamVideo.I \
webcamVideoCursorV4L.h webcamVideoV4L.h
#define IGATESCAN all

View File

@ -15,6 +15,8 @@
#include "config_vision.h"
#include "openCVTexture.h"
#include "webcamVideo.h"
#include "webcamVideoCursorV4L.h"
#include "webcamVideoV4L.h"
#include "pandaSystem.h"
#include "texturePool.h"
@ -44,6 +46,8 @@ init_libvision() {
initialized = true;
WebcamVideo::init_type();
WebcamVideoCursorV4L::init_type();
WebcamVideoV4L::init_type();
#ifdef HAVE_OPENCV
OpenCVTexture::init_type();

View File

@ -2,5 +2,7 @@
#include "config_vision.cxx"
#include "openCVTexture.cxx"
#include "webcamVideo.cxx"
#include "webcamVideoCursorV4L.cxx"
#include "webcamVideoDS.cxx"
#include "webcamVideoV4L.cxx"

View File

@ -37,10 +37,27 @@ get_size_y() const {
// Function: WebcamVideo::get_fps
// Access: Published
// Description: Returns the camera's framerate. This
// is a maximum theoretical: the actual performance
// is a maximum theoretical: the actual performance
// will depend on the speed of the hardware.
////////////////////////////////////////////////////////////////////
INLINE int WebcamVideo::
get_fps() const {
return _fps;
}
////////////////////////////////////////////////////////////////////
// Function: WebcamVideo::output
// Access: Public
// Description: Outputs the WebcamVideo. This function simply
// writes the name, size and FPS to the output stream.
////////////////////////////////////////////////////////////////////
INLINE void WebcamVideo::
output(ostream &out) const {
out << get_name() << ": " << get_size_x() << "x" << get_size_y() << " @ " << get_fps() << "Hz";
}
INLINE ostream &operator << (ostream &out, const WebcamVideo &n) {
n.output(out);
return out;
}

View File

@ -23,7 +23,7 @@ TypeHandle WebcamVideo::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: WebcamVideo::Destructor
// Access: Published, Virtual
// Description:
// Description:
////////////////////////////////////////////////////////////////////
WebcamVideo::
~WebcamVideo() {
@ -55,7 +55,7 @@ find_all_webcams() {
find_all_webcams_ds();
#endif
#ifdef HAVE_VIDEO4LINUX
#ifdef IS_LINUX
extern void find_all_webcams_v4l();
find_all_webcams_v4l();
#endif
@ -63,11 +63,11 @@ find_all_webcams() {
////////////////////////////////////////////////////////////////////
// Function: WebcamVideo::get_num_options
// Access: Public
// Access: Public, Static
// 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.
// 640x480, 30 fps" is an option.
////////////////////////////////////////////////////////////////////
int WebcamVideo::
get_num_options() {
@ -77,7 +77,7 @@ get_num_options() {
////////////////////////////////////////////////////////////////////
// Function: WebcamVideo::get_option
// Access: Public
// Access: Public, Static
// Description: Returns the nth webcam option.
////////////////////////////////////////////////////////////////////
PT(WebcamVideo) WebcamVideo::

View File

@ -30,13 +30,15 @@ PUBLISHED:
static int get_num_options();
static PT(WebcamVideo) get_option(int n);
MAKE_SEQ(get_options, get_num_options, get_option);
INLINE int get_size_x() const;
INLINE int get_size_y() const;
INLINE int get_fps() const;
virtual PT(MovieVideoCursor) open() = 0;
INLINE void output(ostream &out) const;
public:
static void find_all_webcams();
@ -65,6 +67,8 @@ private:
static TypeHandle _type_handle;
};
INLINE ostream &operator << (ostream &out, const WebcamVideo &n);
#include "webcamVideo.I"
#endif

View File

@ -0,0 +1,413 @@
// Filename: webcamVideoCursorV4L.cxx
// Created by: rdb (11Jun2010)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifdef IS_LINUX
#include "webcamVideoV4L.h"
#include <sys/mman.h>
#include <linux/videodev.h>
#include <linux/videodev2.h>
#ifdef HAVE_JPEG
extern "C" {
#include <jpeglib.h>
#include <jpegint.h>
#include <jerror.h>
}
#include <setjmp.h>
#endif
TypeHandle WebcamVideoCursorV4L::_type_handle;
#define clamp(x) min(max(x, 0.0), 255.0)
INLINE static void yuv_to_rgb(unsigned char *dest, const unsigned char *src) {
double y1 = (255 / 219.0) * (src[0] - 16);
double pb = (255 / 224.0) * (src[1] - 128);
double pr = (255 / 224.0) * (src[2] - 128);
dest[2] = clamp(1.0 * y1 + 0 * pb + 1.402 * pr);
dest[1] = clamp(1.0 * y1 - 0.344 * pb - 0.714 * pr);
dest[0] = clamp(1.0 * y1 + 1.772 * pb + 0 * pr);
}
INLINE static void yuyv_to_rgbrgb(unsigned char *dest, const unsigned char *src) {
unsigned char yuv[] = {src[0], src[1], src[3]};
yuv_to_rgb(dest, yuv);
yuv[0] = src[2];
yuv_to_rgb(dest + 3, yuv);
}
INLINE static void yuyv_to_rgbargba(unsigned char *dest, const unsigned char *src) {
unsigned char yuv[] = {src[0], src[1], src[3]};
yuv_to_rgb(dest, yuv);
yuv[0] = src[2];
yuv_to_rgb(dest + 4, yuv);
dest[3] = (unsigned char) -1;
dest[7] = (unsigned char) -1;
}
#if defined(HAVE_JPEG) && !defined(CPPPARSER)
struct my_error_mgr {
struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer;
};
typedef struct my_error_mgr *my_error_ptr;
static void my_error_exit (j_common_ptr cinfo) {
// Output the error message
char buffer[JMSG_LENGTH_MAX];
(*cinfo->err->format_message) (cinfo, buffer);
vision_cat.error() << buffer << "\n";
// cinfo->err really points to a my_error_mgr struct, so coerce pointer
my_error_ptr myerr = (my_error_ptr) cinfo->err;
// Return control to the setjmp point
longjmp(myerr->setjmp_buffer, 1);
}
static void my_output_message (j_common_ptr cinfo){
char buffer[JMSG_LENGTH_MAX];
(*cinfo->err->format_message) (cinfo, buffer);
vision_cat.warning() << buffer << "\n";
}
static void my_init_source(j_decompress_ptr cinfo) {
}
static boolean my_fill_input_buffer(j_decompress_ptr cinfo) {
struct jpeg_source_mgr *src = cinfo->src;
static JOCTET FakeEOI[] = {0xFF, JPEG_EOI};
WARNMS(cinfo, JWRN_JPEG_EOF);
src->next_input_byte = FakeEOI;
src->bytes_in_buffer = 2;
return TRUE;
}
static void my_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
struct jpeg_source_mgr *src = cinfo->src;
if (num_bytes >= (long) src->bytes_in_buffer) {
my_fill_input_buffer(cinfo);
return;
}
src->bytes_in_buffer -= num_bytes;
src->next_input_byte += num_bytes;
}
static void my_term_source(j_decompress_ptr cinfo) {
}
#endif
////////////////////////////////////////////////////////////////////
// Function: WebcamVideoCursorV4L::Constructor
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
WebcamVideoCursorV4L::
WebcamVideoCursorV4L(WebcamVideoV4L *src) : MovieVideoCursor(src) {
_size_x = src->_size_x;
_size_y = src->_size_y;
_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;
_ready = false;
_format = (struct v4l2_format *) malloc(sizeof(struct v4l2_format));
memset(_format, 0, sizeof(struct v4l2_format));
_cinfo = NULL;
_buffers = NULL;
_buflens = NULL;
_fd = open(src->_device.c_str(), O_RDWR);
if (-1 == _fd) {
vision_cat.error() << "Failed to open " << src->_device.c_str() << "\n";
return;
}
_format->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
pvector<uint32_t>::iterator it;
for (it = src->_pformats.begin(); it != src->_pformats.end(); ++it) {
#ifdef HAVE_JPEG
if (*it == V4L2_PIX_FMT_MJPEG) {
_format->fmt.pix.pixelformat = *it;
break;
} else
#endif
if (*it == V4L2_PIX_FMT_YUYV) {
_format->fmt.pix.pixelformat = *it;
break;
}
}
if (it == src->_pformats.end()) {
vision_cat.error() << "Failed to find a suitable pixel format!\n";
_ready = false;
close(_fd);
_fd = -1;
return;
}
// No interlacing
_format->fmt.pix.field = V4L2_FIELD_NONE;
// This really should be there, but for
// some reason it causes weird memory corruption.
_format->fmt.pix.width = _size_x;
_format->fmt.pix.height = _size_y;
// Now politely ask the driver to switch to this format
if (-1 == ioctl(_fd, VIDIOC_S_FMT, _format)) {
vision_cat.error() << "Driver rejected format!\n";
_ready = false;
close(_fd);
_fd = -1;
return;
}
_size_x = _format->fmt.pix.width;
_size_y = _format->fmt.pix.height;
struct v4l2_requestbuffers req;
memset(&req, 0, sizeof req);
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (-1 == ioctl (_fd, VIDIOC_REQBUFS, &req)) {
vision_cat.error() << "Failed to request buffers from webcam!\n";
}
if (req.count < 2) {
vision_cat.error() << "Insufficient buffer memory!\n";
}
_bufcount = req.count;
_buffers = (void* *) calloc (req.count, sizeof (void*));
_buflens = (size_t*) calloc (req.count, sizeof (size_t));
if (!_buffers || !_buflens) {
vision_cat.error() << "Not enough memory!\n";
}
struct v4l2_buffer buf;
for (int i = 0; i < _bufcount; ++i) {
memset(&buf, 0, sizeof buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (-1 == ioctl(_fd, VIDIOC_QUERYBUF, &buf)) {
vision_cat.error() << "Failed to query buffer!\n";
}
_buflens[i] = buf.length;
_buffers[i] = mmap (NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, buf.m.offset);
if (_buffers[i] == MAP_FAILED) {
vision_cat.error() << "Failed to map buffer!\n";
}
if (-1 == ioctl(_fd, VIDIOC_QBUF, &buf)) {
vision_cat.error() << "Failed to exchange buffer with driver!\n";
}
}
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == ioctl(_fd, VIDIOC_STREAMON, &type)) {
vision_cat.error() << "Failed to stream from buffer!\n";
}
#ifdef HAVE_JPEG
// Initialize the JPEG library, if necessary
if (_format->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) {
_cinfo = (struct jpeg_decompress_struct *) malloc(sizeof(struct jpeg_decompress_struct));
jpeg_create_decompress(_cinfo);
_cinfo->src = (struct jpeg_source_mgr *)
(*_cinfo->mem->alloc_small) ((j_common_ptr) _cinfo, JPOOL_PERMANENT,
sizeof(struct jpeg_source_mgr));
// Set up function pointers
_cinfo->src->init_source = my_init_source;
_cinfo->src->fill_input_buffer = my_fill_input_buffer;
_cinfo->src->skip_input_data = my_skip_input_data;
_cinfo->src->resync_to_restart = jpeg_resync_to_restart;
_cinfo->src->term_source = my_term_source;
}
#endif
_ready = true;
}
////////////////////////////////////////////////////////////////////
// Function: WebcamVideoCursorV4L::Destructor
// Access: Published, Virtual
// Description:
////////////////////////////////////////////////////////////////////
WebcamVideoCursorV4L::
~WebcamVideoCursorV4L() {
#ifdef HAVE_JPEG
if (_cinfo != NULL) {
jpeg_destroy_decompress(_cinfo);
free(_cinfo);
}
#endif
if (_format != NULL) {
free(_format);
}
if (-1 != _fd) {
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(_fd, VIDIOC_STREAMOFF, &type);
close(_fd);
}
if (_buffers) {
for (int i = 0; i < _bufcount; ++i) {
munmap(_buffers[i], _buflens[i]);
}
free(_buffers);
}
if (_buflens) {
free(_buflens);
}
}
////////////////////////////////////////////////////////////////////
// Function: WebcamVideoCursorV4L::fetch_into_buffer
// Access: Published, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void WebcamVideoCursorV4L::
fetch_into_buffer(double time, unsigned char *block, bool bgra) {
if (!_ready) {
return;
}
struct v4l2_buffer vbuf;
memset(&vbuf, 0, sizeof vbuf);
vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vbuf.memory = V4L2_MEMORY_MMAP;
if (-1 == ioctl(_fd, VIDIOC_DQBUF, &vbuf) && errno != EIO) {
vision_cat.error() << "Failed to dequeue buffer!\n";
return;
}
nassertv(vbuf.index < _bufcount);
size_t bufsize = _buflens[vbuf.index];
size_t old_bpl = _format->fmt.pix.bytesperline;
size_t new_bpl = bgra ? _size_x * 4 : _size_x * 3;
unsigned char *buf = (unsigned char *) _buffers[vbuf.index];
if (_format->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) {
#ifdef HAVE_JPEG
nassertv(!bgra);
struct my_error_mgr jerr;
_cinfo->err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
jerr.pub.output_message = my_output_message;
unsigned char *newbuf = (unsigned char*) malloc(new_bpl * _size_y);
// Establish the setjmp return context for my_error_exit to use
if (setjmp(jerr.setjmp_buffer)) {
if (_cinfo->global_state > DSTATE_READY) {
jpeg_abort_decompress(_cinfo);
}
} else {
// Set up data pointer
_cinfo->src->bytes_in_buffer = bufsize;
_cinfo->src->next_input_byte = buf;
if (jpeg_read_header(_cinfo, TRUE) == JPEG_HEADER_OK) {
_cinfo->scale_num = 1;
_cinfo->scale_denom = 1;
_cinfo->out_color_space = JCS_RGB;
if (jpeg_start_decompress(_cinfo) && _cinfo->output_components == 3
&& _size_x == _cinfo->output_width && _size_y == _cinfo->output_height) {
JSAMPLE *buffer_end = newbuf + new_bpl * _cinfo->output_height;
JSAMPLE *rowptr = newbuf;
while (_cinfo->output_scanline < _cinfo->output_height) {
nassertd(rowptr + new_bpl <= buffer_end) break;
jpeg_read_scanlines(_cinfo, &rowptr, 1);
rowptr += new_bpl;
}
if (_cinfo->output_scanline < _cinfo->output_height) {
jpeg_abort_decompress(_cinfo);
} else {
jpeg_finish_decompress(_cinfo);
}
}
}
}
// Flip the image vertically
for (size_t row = 0; row < _size_y; ++row) {
memcpy(block + (_size_y - row - 1) * new_bpl, newbuf + row * new_bpl, new_bpl);
}
free(newbuf);
// Swap red / blue
unsigned char ex;
if (bgra) {
for (size_t i = 0; i < new_bpl * _size_y; i += 4) {
ex = block[i];
block[i] = block[i + 2];
block[i + 2] = ex;
}
} else {
for (size_t i = 0; i < new_bpl * _size_y; i += 3) {
ex = block[i];
block[i] = block[i + 2];
block[i + 2] = ex;
}
}
#else
nassertv(false); // Not compiled with JPEG support
#endif
} else {
if (bgra) {
for (size_t row = 0; row < _size_y; ++row) {
size_t c = 0;
for (size_t i = 0; i < old_bpl; i += 4) {
yuyv_to_rgbargba(block + (_size_y - row - 1) * new_bpl + c, buf + row * old_bpl + i);
c += 8;
}
}
} else {
for (size_t row = 0; row < _size_y; ++row) {
size_t c = 0;
for (size_t i = 0; i < old_bpl; i += 4) {
yuyv_to_rgbrgb(block + (_size_y - row - 1) * new_bpl + c, buf + row * old_bpl + i);
c += 6;
}
}
}
}
if (-1 == ioctl(_fd, VIDIOC_QBUF, &vbuf)) {
vision_cat.error() << "Failed to exchange buffer with driver!\n";
}
}
#endif

View File

@ -0,0 +1,69 @@
// Filename: webcamVideoCursorV4L.h
// Created by: rdb (11Jun2010)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef WEBCAMVIDEOCURSORV4L_H
#define WEBCAMVIDEOCURSORV4L_H
#ifdef IS_LINUX
#include "webcamVideo.h"
struct v4l2_format;
#ifdef HAVE_JPEG
struct jpeg_decompress_struct;
#endif
class WebcamVideoV4L;
////////////////////////////////////////////////////////////////////
// Class : WebcamVideoCursorV4L
// Description : The Video4Linux implementation of webcams.
////////////////////////////////////////////////////////////////////
class WebcamVideoCursorV4L : public MovieVideoCursor {
public:
WebcamVideoCursorV4L(WebcamVideoV4L *src);
virtual ~WebcamVideoCursorV4L();
virtual void fetch_into_buffer(double time, unsigned char *block, bool rgba);
private:
int _fd;
void **_buffers;
size_t *_buflens;
size_t _bufcount;
struct v4l2_format *_format;
#ifdef HAVE_JPEG
struct jpeg_decompress_struct *_cinfo;
#endif
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
MovieVideoCursor::init_type();
register_type(_type_handle, "WebcamVideoCursorV4L",
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;
};
#endif // IS_LINUX
#endif

View File

@ -23,7 +23,7 @@
// 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
// 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.
//
@ -31,7 +31,7 @@
#ifdef HAVE_DIRECTCAM
#define WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#undef Configure
@ -102,7 +102,7 @@ private:
IMoniker *_moniker;
AM_MEDIA_TYPE *_media;
friend class WebcamVideoCursorDS;
public:
@ -141,14 +141,14 @@ public:
public:
void cleanup();
class CSampleGrabberCB : public ISampleGrabberCB
{
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);
@ -161,12 +161,12 @@ public:
ICaptureGraphBuilder2 *_pCaptureBuilder;
IBaseFilter *_pSrcFilter;
IAMStreamConfig *_pStreamConfig;
CComPtr<ISampleGrabber> _pSampleGrabber;
CComPtr<ISampleGrabber> _pSampleGrabber;
IBaseFilter *_pStreamRenderer;
IMediaControl *_pMediaCtrl;
// IMemAllocator *_pAllocator;
CSampleGrabberCB _sample_cb;
public:
static TypeHandle get_class_type() {
return _type_handle;
@ -329,30 +329,28 @@ add_device(WebcamVideoList &list, IMoniker *pMoniker, AM_MEDIA_TYPE *media) {
}
}
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->set_name(name.str());
wc->set_name(get_moniker_name(pMoniker));
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->get_name() << "\n";
cerr << "Added device: DirectShow: " << wc << "\n";
}
////////////////////////////////////////////////////////////////////
// Function: WebcamVideoDS::find_all_webcams_ds
// Access: Public, Static
// Description: Finds all DirectShow webcams and adds them to
// 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;
@ -362,7 +360,7 @@ find_all_webcams_ds() {
IAMStreamConfig *pStreamConfig=NULL;
HRESULT hResult;
ULONG cFetched;
hResult=CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
IID_IGraphBuilder,(void**)&pGraphBuilder);
if (hResult != S_OK) goto cleanup;
@ -377,7 +375,7 @@ find_all_webcams_ds() {
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; }
@ -390,7 +388,7 @@ find_all_webcams_ds() {
if (hResult != S_OK) continue;
hResult = pCaptureGraphBuilder2->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pBaseFilter,
IID_IAMStreamConfig, (void **)&pStreamConfig);
if (hResult != S_OK) continue;
if (hResult != S_OK) continue;
int iCount, iSize;
hResult = pStreamConfig->GetNumberOfCapabilities(&iCount, &iSize);
if (hResult != S_OK || (iSize != sizeof(VIDEO_STREAM_CONFIG_CAPS))) continue;
@ -405,10 +403,10 @@ find_all_webcams_ds() {
add_device(list, pMoniker, mtype);
}
}
pMoniker = 0;
}
cleanup:
if (pCreateDevEnum) { pCreateDevEnum->Release(); pCreateDevEnum=0; }
if (pEnumMoniker) { pEnumMoniker->Release(); pEnumMoniker=0; }
@ -417,7 +415,7 @@ find_all_webcams_ds() {
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);
@ -443,7 +441,7 @@ open() {
////////////////////////////////////////////////////////////////////
// Function: WebcamVideoCursorDS::Constructor
// Access: Published
// Description:
// Description:
////////////////////////////////////////////////////////////////////
WebcamVideoCursorDS::
WebcamVideoCursorDS(WebcamVideoDS *src) :
@ -457,39 +455,39 @@ WebcamVideoCursorDS(WebcamVideoDS *src) :
{
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) {
@ -501,16 +499,16 @@ WebcamVideoCursorDS(WebcamVideoDS *src) :
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;
@ -519,14 +517,14 @@ WebcamVideoCursorDS(WebcamVideoDS *src) :
{ 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.
//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.";
@ -538,20 +536,20 @@ WebcamVideoCursorDS(WebcamVideoDS *src) :
{ 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;
cleanup(); return;
}
// IPin *iPin;
// hResult = FindInputPin(pGrabberFilter, &iPin);
// if ((iPin == 0)||(hResult != S_OK)) {
@ -579,12 +577,12 @@ WebcamVideoCursorDS(WebcamVideoDS *src) :
// 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;
@ -602,28 +600,28 @@ WebcamVideoCursorDS(WebcamVideoDS *src) :
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:
// Description:
////////////////////////////////////////////////////////////////////
void WebcamVideoCursorDS::
cleanup() {
@ -635,7 +633,7 @@ cleanup() {
if (_pMediaCtrl) {
_pMediaCtrl->Stop();
}
if(_pMediaCtrl) { _pMediaCtrl->Release(); _pMediaCtrl=NULL; }
if(_pCaptureBuilder) { _pCaptureBuilder->Release(); _pCaptureBuilder=NULL; }
if(_pGraphBuilder) { _pGraphBuilder->Release(); _pGraphBuilder=NULL; }
@ -648,7 +646,7 @@ cleanup() {
////////////////////////////////////////////////////////////////////
// Function: WebcamVideoCursorDS::Destructor
// Access: Published
// Description:
// Description:
////////////////////////////////////////////////////////////////////
WebcamVideoCursorDS::
~WebcamVideoCursorDS() {
@ -658,16 +656,16 @@ WebcamVideoCursorDS::
////////////////////////////////////////////////////////////////////
// Function: WebcamVideoCursorDS::fetch_into_buffer
// Access: Published
// Description:
// Description:
////////////////////////////////////////////////////////////////////
void WebcamVideoCursorDS::
fetch_into_buffer(double time, unsigned char *block, bool bgra) {
if (!_ready) {
return;
}
#ifdef LOCKING_MODE
#ifdef LOCKING_MODE
unsigned char *ptr;
int pixels = _size_x * _size_y;
HRESULT res = _saved->GetPointer(&ptr);
@ -700,7 +698,7 @@ fetch_into_buffer(double time, unsigned char *block, bool bgra) {
memcpy(block, _buffer, pixels * 3);
}
#endif
_ready = false;
}
@ -708,14 +706,14 @@ fetch_into_buffer(double time, unsigned char *block, bool bgra) {
////////////////////////////////////////////////////////////////////
// Function: WebcamVideoCursorDS::CSampleGrabberCB::QueryInterface
// Access: Private
// Description:
// 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;
}
@ -723,7 +721,7 @@ HRESULT __stdcall WebcamVideoCursorDS::CSampleGrabberCB::QueryInterface(REFIID r
////////////////////////////////////////////////////////////////////
// Function: WebcamVideoCursorDS::CSampleGrabberCB::SampleCB
// Access: Private
// Description:
// Description:
////////////////////////////////////////////////////////////////////
HRESULT __stdcall WebcamVideoCursorDS::CSampleGrabberCB::SampleCB(double SampleTime, IMediaSample *pSample)
{
@ -731,7 +729,7 @@ HRESULT __stdcall WebcamVideoCursorDS::CSampleGrabberCB::SampleCB(double SampleT
return 0;
}
#ifdef LOCKING_MODE
#ifdef LOCKING_MODE
pSample->AddRef();
_host->_saved = pSample;
#else
@ -753,10 +751,10 @@ HRESULT __stdcall WebcamVideoCursorDS::CSampleGrabberCB::SampleCB(double SampleT
////////////////////////////////////////////////////////////////////
// Function: WebcamVideoCursorDS::CSampleGrabberCB::BufferCB
// Access: Private
// Description:
// Description:
////////////////////////////////////////////////////////////////////
HRESULT __stdcall WebcamVideoCursorDS::CSampleGrabberCB::BufferCB(double dblSampleTime, BYTE *pBuffer, long lBufferSize)
{
{
// Not used.
return 0;
}
@ -765,7 +763,7 @@ HRESULT __stdcall WebcamVideoCursorDS::CSampleGrabberCB::BufferCB(double dblSamp
//{
// if (!pFilter || ! ppPin)
// return E_POINTER;
//
//
// *ppPin = 0;
// HRESULT hr;
// //Find the output pin of the Source Filter
@ -773,7 +771,7 @@ HRESULT __stdcall WebcamVideoCursorDS::CSampleGrabberCB::BufferCB(double dblSamp
// hr = pFilter->EnumPins(&pPinEnum);
// if (FAILED(hr))
// return E_FAIL;
//
//
// IPin *pSearchPin;
// while (pPinEnum->Next(1, &pSearchPin, NULL) == S_OK)
// {

View File

@ -0,0 +1,135 @@
// Filename: webcamVideoV4L.cxx
// Created by: rdb (11Jun2010)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifdef IS_LINUX
#include "webcamVideoV4L.h"
#include "webcamVideoCursorV4L.h"
#include <linux/videodev.h>
#include <linux/videodev2.h>
TypeHandle WebcamVideoV4L::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: find_all_webcams_v4l
// Access: Public, Static
// Description: Finds all Video4Linux webcams and adds them to
// the global list _all_webcams.
////////////////////////////////////////////////////////////////////
void find_all_webcams_v4l() {
struct video_capability cap;
struct v4l2_capability cap2;
vector_string devs;
GlobPattern pattern ("/dev/video*");
pattern.match_files(devs);
for (vector_string::iterator it = devs.begin(); it != devs.end(); ++it) {
int fd = open(it->c_str(), O_RDWR);
if (fd != -1) {
// Check for Video4Linux2 capabilities
if (ioctl(fd, VIDIOC_QUERYCAP, &cap2) != -1) {
if ((cap2.capabilities & V4L2_CAP_VIDEO_CAPTURE) &&
(cap2.capabilities & V4L2_CAP_STREAMING)) {
struct v4l2_fmtdesc fmt;
for (int i = 0;; i++) {
memset(&fmt, 0, sizeof fmt);
fmt.index = i;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) == -1) {
break;
}
struct v4l2_frmsizeenum frmsizeenum;
for (int j = 0;; j++) {
memset(&frmsizeenum, 0, sizeof frmsizeenum);
frmsizeenum.index = j;
frmsizeenum.pixel_format = fmt.pixelformat;
if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) == -1) {
break;
}
if (frmsizeenum.type != V4L2_FRMSIZE_TYPE_DISCRETE) {
continue;
}
struct v4l2_frmivalenum frmivalenum;
for (int k = 0;; k++) {
memset(&frmivalenum, 0, sizeof frmivalenum);
frmivalenum.index = k;
frmivalenum.pixel_format = fmt.pixelformat;
frmivalenum.width = frmsizeenum.discrete.width;
frmivalenum.height = frmsizeenum.discrete.height;
if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmivalenum) == -1) {
break;
}
if (frmivalenum.type != V4L2_FRMIVAL_TYPE_DISCRETE) {
continue;
}
// Create a new webcam video object
PT(WebcamVideoV4L) wc = new WebcamVideoV4L;
wc->set_name((const char*) cap2.card);
wc->_device = *it;
wc->_size_x = frmsizeenum.discrete.width;
wc->_size_y = frmsizeenum.discrete.height;
wc->_fps = ((double) frmivalenum.discrete.denominator) / ((double) frmivalenum.discrete.numerator);
wc->_pformats.push_back(fmt.pixelformat);
// Iterate through the webcams to make sure we don't put any duplicates in there
pvector<PT(WebcamVideo)>::iterator wvi;
for (wvi = WebcamVideoV4L::_all_webcams.begin(); wvi != WebcamVideoV4L::_all_webcams.end(); ++wvi) {
if ((*wvi)->is_of_type(WebcamVideoV4L::get_class_type())) {
PT(WebcamVideoV4L) wv_v4l = DCAST(WebcamVideoV4L, *wvi);
if (wv_v4l->_device == wc->_device &&
wv_v4l->_size_x == wc->_size_x &&
wv_v4l->_size_y == wc->_size_y &&
wv_v4l->_fps == wc->_fps) {
wv_v4l->_pformats.push_back(fmt.pixelformat);
break;
}
}
}
// Did the loop finish, meaning that a webcam of these
// properties does not exist? Add it.
if (wvi == WebcamVideoV4L::_all_webcams.end()) {
WebcamVideoV4L::_all_webcams.push_back(DCAST(WebcamVideo, wc));
}
}
}
}
continue;
}
}
// Check for Video4Linux capabilities
if (ioctl(fd, VIDIOCGCAP, &cap) != -1) {
if (cap.type & VID_TYPE_CAPTURE) {
//TODO: Video4Linux support
continue;
}
}
}
close(fd);
}
}
////////////////////////////////////////////////////////////////////
// Function: WebcamVideoV4L::open
// Access: Published, Virtual
// Description: Open this video, returning a MovieVideoCursor.
////////////////////////////////////////////////////////////////////
PT(MovieVideoCursor) WebcamVideoV4L::
open() {
return new WebcamVideoCursorV4L(this);
}
#endif

View File

@ -0,0 +1,58 @@
// Filename: webcamVideoV4L.h
// Created by: rdb (11Jun2010)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef WEBCAMVIDEOV4L_H
#define WEBCAMVIDEOV4L_H
#ifdef IS_LINUX
#include "webcamVideo.h"
class WebcamVideoCursorV4L;
////////////////////////////////////////////////////////////////////
// Class : WebcamVideoV4L
// Description : The Video4Linux implementation of webcams.
////////////////////////////////////////////////////////////////////
class WebcamVideoV4L : public WebcamVideo {
private:
virtual PT(MovieVideoCursor) open();
friend class WebcamVideoCursorV4L;
friend void find_all_webcams_v4l();
string _device;
pvector<uint32_t> _pformats;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
WebcamVideo::init_type();
register_type(_type_handle, "WebcamVideoV4L",
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;
};
#endif // IS_LINUX
#endif