Read the webcam frames in a separate thread, since the call blocks.

This commit is contained in:
Andrzej Kapolka 2013-06-17 18:07:53 -07:00
parent 3c58f0c0a7
commit a771f53df3
5 changed files with 104 additions and 50 deletions

View file

@ -68,7 +68,7 @@ include(${QT_USE_FILE})
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${QT_QTGUI_INCLUDE_DIR}")
# run qt moc on qt-enabled headers
qt4_wrap_cpp(INTERFACE_SRCS src/Application.h src/AvatarVoxelSystem.h)
qt4_wrap_cpp(INTERFACE_SRCS src/Application.h src/AvatarVoxelSystem.h src/Webcam.h)
# create the executable, make it a bundle on OS X
add_executable(${TARGET_NAME} MACOSX_BUNDLE ${INTERFACE_SRCS})

View file

@ -1569,9 +1569,6 @@ void Application::update(float deltaTime) {
// Update transmitter
// Get the webcam frame
_webcam.grabFrame();
// Sample hardware, update view frustum if needed, and send avatar data to mixer/agents
updateAvatar(deltaTime);
@ -2154,26 +2151,28 @@ void Application::displayOverlay() {
}
// render the webcam input frame
glBindTexture(GL_TEXTURE_2D, _webcam.getFrameTextureID());
glEnable(GL_TEXTURE_2D);
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
const int FRAME_PREVIEW_HEIGHT = 200;
int framePreviewWidth = _webcam.getFrameAspectRatio() * FRAME_PREVIEW_HEIGHT;
int bottom = _glWidget->height() - 400;
int left = _glWidget->width() - framePreviewWidth - 400;
glTexCoord2f(0, 0);
glVertex2f(left, bottom);
glTexCoord2f(1, 0);
glVertex2f(left + framePreviewWidth, bottom);
glTexCoord2f(1, 1);
glVertex2f(left + framePreviewWidth, bottom + FRAME_PREVIEW_HEIGHT);
glTexCoord2f(0, 1);
glVertex2f(left, bottom + FRAME_PREVIEW_HEIGHT);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
if (_webcam.getFrameTextureID() != 0) {
glBindTexture(GL_TEXTURE_2D, _webcam.getFrameTextureID());
glEnable(GL_TEXTURE_2D);
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
const int FRAME_PREVIEW_HEIGHT = 200;
int framePreviewWidth = _webcam.getFrameAspectRatio() * FRAME_PREVIEW_HEIGHT;
int bottom = _glWidget->height() - 400;
int left = _glWidget->width() - framePreviewWidth - 400;
glTexCoord2f(0, 0);
glVertex2f(left, bottom);
glTexCoord2f(1, 0);
glVertex2f(left + framePreviewWidth, bottom);
glTexCoord2f(1, 1);
glVertex2f(left + framePreviewWidth, bottom + FRAME_PREVIEW_HEIGHT);
glTexCoord2f(0, 1);
glVertex2f(left, bottom + FRAME_PREVIEW_HEIGHT);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}
glPopMatrix();
}

View file

@ -76,6 +76,7 @@ public:
VoxelSystem* getVoxels() { return &_voxels; }
QSettings* getSettings() { return _settings; }
Environment* getEnvironment() { return &_environment; }
Webcam* getWebcam() { return &_webcam; }
bool shouldEchoAudio() { return _echoAudioMode->isChecked(); }
QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; }

View file

@ -11,41 +11,83 @@
#include <Log.h>
#include "Application.h"
#include "Webcam.h"
Webcam::Webcam() {
if ((_capture = cvCaptureFromCAM(-1)) == 0) {
printLog("Failed to open webcam.\n");
return;
}
class FrameGrabber : public QObject {
public:
// get the dimensions, fps of the frames
_frameWidth = cvGetCaptureProperty(_capture, CV_CAP_PROP_FRAME_WIDTH);
_frameHeight = cvGetCaptureProperty(_capture, CV_CAP_PROP_FRAME_HEIGHT);
int fps = cvGetCaptureProperty(_capture, CV_CAP_PROP_FPS);
printLog("Opened camera [width=%d, height=%d, fps=%d].", _frameWidth, _frameHeight, fps);
}
FrameGrabber() : _capture(0) { }
virtual ~FrameGrabber();
void Webcam::init() {
// initialize the texture that will contain the grabbed frames
glGenTextures(1, &_frameTextureID);
glBindTexture(GL_TEXTURE_2D, _frameTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _frameWidth, _frameHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
}
protected:
virtual void timerEvent(QTimerEvent* event);
private:
CvCapture* _capture;
};
Webcam::~Webcam() {
FrameGrabber::~FrameGrabber() {
if (_capture != 0) {
cvReleaseCapture(&_capture);
}
}
void Webcam::grabFrame() {
void FrameGrabber::timerEvent(QTimerEvent* event) {
if (_capture == 0) {
if ((_capture = cvCaptureFromCAM(-1)) == 0) {
printLog("Failed to open webcam.\n");
return;
}
}
IplImage* image = cvQueryFrame(_capture);
if (image == 0) {
return;
}
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _frameWidth, _frameHeight, GL_RGB, GL_UNSIGNED_BYTE, image->imageData);
// make sure it's in the format we expect
if (image->nChannels != 3 || image->depth != IPL_DEPTH_8U || image->dataOrder != IPL_DATA_ORDER_PIXEL ||
image->origin != 0 || image->widthStep != image->width * 3) {
printLog("Invalid webcam image format.\n");
return;
}
QMetaObject::invokeMethod(Application::getInstance()->getWebcam(), "setFrame",
Q_ARG(QImage, QImage((uchar*)image->imageData, image->width, image->height, QImage::Format_RGB888)));
}
Webcam::Webcam() : _frameTextureID(0) {
// the grabber simply runs as fast as possible
_grabber = new FrameGrabber();
_grabber->startTimer(0);
_grabber->moveToThread(&_grabberThread);
}
void Webcam::init() {
// start the grabber thread
_grabberThread.start();
}
Webcam::~Webcam() {
// stop the grabber thread
_grabberThread.quit();
_grabberThread.wait();
delete _grabber;
}
void Webcam::setFrame(const QImage& image) {
if (_frameTextureID == 0) {
glGenTextures(1, &_frameTextureID);
glBindTexture(GL_TEXTURE_2D, _frameTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _frameWidth = image.width(), _frameHeight = image.height(), 0, GL_BGR,
GL_UNSIGNED_BYTE, image.constBits());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
} else {
glBindTexture(GL_TEXTURE_2D, _frameTextureID);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _frameWidth, _frameHeight, GL_BGR, GL_UNSIGNED_BYTE, image.constBits());
}
glBindTexture(GL_TEXTURE_2D, 0);
}

View file

@ -9,11 +9,20 @@
#ifndef __interface__Webcam__
#define __interface__Webcam__
#include <QObject>
#include <QThread>
#include "InterfaceConfig.h"
class QImage;
struct CvCapture;
class Webcam {
class FrameGrabber;
class Webcam : public QObject {
Q_OBJECT
public:
Webcam();
@ -26,11 +35,14 @@ public:
float getFrameAspectRatio() const { return _frameWidth / (float)_frameHeight; }
GLuint getFrameTextureID() const { return _frameTextureID; }
void grabFrame();
public slots:
void setFrame(const QImage& image);
private:
CvCapture* _capture;
QThread _grabberThread;
FrameGrabber* _grabber;
int _frameWidth;
int _frameHeight;