mirror of https://github.com/qTox/qTox.git
20 changed files with 929 additions and 194 deletions
@ -0,0 +1,205 @@
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox. |
||||
|
||||
This program is libre software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
||||
|
||||
See the COPYING file for more details. |
||||
*/ |
||||
|
||||
#include "cameraworker.h" |
||||
|
||||
#include <QTimer> |
||||
#include <QDebug> |
||||
|
||||
CameraWorker::CameraWorker(int index) |
||||
: clock(nullptr) |
||||
, camIndex(index) |
||||
, refCount(0) |
||||
{ |
||||
} |
||||
|
||||
void CameraWorker::onStart() |
||||
{ |
||||
clock = new QTimer(this); |
||||
clock->setSingleShot(false); |
||||
clock->setInterval(5); |
||||
|
||||
connect(clock, &QTimer::timeout, this, &CameraWorker::doWork); |
||||
|
||||
emit started(); |
||||
} |
||||
|
||||
void CameraWorker::_suspend() |
||||
{ |
||||
qDebug() << "Suspend"; |
||||
clock->stop(); |
||||
unsubscribe(); |
||||
} |
||||
|
||||
void CameraWorker::_resume() |
||||
{ |
||||
qDebug() << "Resume"; |
||||
subscribe(); |
||||
clock->start(); |
||||
} |
||||
|
||||
void CameraWorker::_setProp(int prop, double val) |
||||
{ |
||||
props[prop] = val; |
||||
|
||||
if (cam.isOpened()) |
||||
cam.set(prop, val); |
||||
} |
||||
|
||||
double CameraWorker::_getProp(int prop) |
||||
{ |
||||
if (!props.contains(prop)) |
||||
{ |
||||
subscribe(); |
||||
props[prop] = cam.get(prop); |
||||
unsubscribe(); |
||||
qDebug() << "ASKED " << prop << " VAL " << props[prop]; |
||||
} |
||||
|
||||
return props.value(prop); |
||||
} |
||||
|
||||
void CameraWorker::probeResolutions() |
||||
{ |
||||
if (resolutions.isEmpty()) |
||||
{ |
||||
subscribe(); |
||||
|
||||
// probe resolutions (TODO: add more)
|
||||
QList<QSize> propbeRes = { |
||||
QSize( 160, 120), // QQVGA
|
||||
QSize( 320, 240), // HVGA
|
||||
QSize(1024, 768), // XGA
|
||||
QSize( 432, 240), // WQVGA
|
||||
QSize( 640, 360), // nHD
|
||||
}; |
||||
|
||||
for (QSize res : propbeRes) |
||||
{ |
||||
cam.set(CV_CAP_PROP_FRAME_WIDTH, res.width()); |
||||
cam.set(CV_CAP_PROP_FRAME_HEIGHT, res.height()); |
||||
|
||||
double w = cam.get(CV_CAP_PROP_FRAME_WIDTH); |
||||
double h = cam.get(CV_CAP_PROP_FRAME_HEIGHT); |
||||
|
||||
//qDebug() << "PROBING:" << res << " got " << w << h;
|
||||
|
||||
if (!resolutions.contains(QSize(w,h))) |
||||
resolutions.append(QSize(w,h)); |
||||
} |
||||
|
||||
unsubscribe(); |
||||
} |
||||
|
||||
qDebug() << resolutions; |
||||
|
||||
emit resProbingFinished(resolutions); |
||||
} |
||||
|
||||
void CameraWorker::applyProps() |
||||
{ |
||||
if (!cam.isOpened()) |
||||
return; |
||||
|
||||
for(int prop : props.keys()) |
||||
cam.set(prop, props.value(prop)); |
||||
} |
||||
|
||||
void CameraWorker::subscribe() |
||||
{ |
||||
if (refCount == 0) |
||||
{ |
||||
if (!cam.isOpened()) |
||||
{ |
||||
cam.open(camIndex); |
||||
applyProps(); // restore props
|
||||
} |
||||
} |
||||
|
||||
refCount++; |
||||
} |
||||
|
||||
void CameraWorker::unsubscribe() |
||||
{ |
||||
refCount--; |
||||
|
||||
if(refCount <= 0) |
||||
{ |
||||
cam.release(); |
||||
} |
||||
} |
||||
|
||||
void CameraWorker::doWork() |
||||
{ |
||||
if (!cam.isOpened()) |
||||
return; |
||||
|
||||
if (queue.size() > 3) |
||||
{ |
||||
queue.dequeue(); |
||||
return; |
||||
} |
||||
|
||||
cam >> frame; |
||||
//qDebug() << "Decoding frame";
|
||||
mutex.lock(); |
||||
|
||||
queue.enqueue(frame); |
||||
mutex.unlock(); |
||||
|
||||
emit newFrameAvailable(); |
||||
} |
||||
|
||||
bool CameraWorker::hasFrame() |
||||
{ |
||||
mutex.lock(); |
||||
bool b = !queue.empty(); |
||||
mutex.unlock(); |
||||
|
||||
return b; |
||||
} |
||||
|
||||
cv::Mat3b CameraWorker::dequeueFrame() |
||||
{ |
||||
mutex.lock(); |
||||
cv::Mat3b f = queue.dequeue(); |
||||
mutex.unlock(); |
||||
|
||||
return f; |
||||
} |
||||
|
||||
void CameraWorker::suspend() |
||||
{ |
||||
QMetaObject::invokeMethod(this, "_suspend"); |
||||
} |
||||
|
||||
void CameraWorker::resume() |
||||
{ |
||||
QMetaObject::invokeMethod(this, "_resume"); |
||||
} |
||||
|
||||
void CameraWorker::setProp(int prop, double val) |
||||
{ |
||||
QMetaObject::invokeMethod(this, "_setProp", Q_ARG(int, prop), Q_ARG(double, val)); |
||||
} |
||||
|
||||
double CameraWorker::getProp(int prop) |
||||
{ |
||||
double ret = 0.0; |
||||
QMetaObject::invokeMethod(this, "_getProp", Qt::BlockingQueuedConnection, Q_RETURN_ARG(double, ret), Q_ARG(int, prop)); |
||||
|
||||
return ret; |
||||
} |
@ -0,0 +1,77 @@
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox. |
||||
|
||||
This program is libre software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
||||
|
||||
See the COPYING file for more details. |
||||
*/ |
||||
|
||||
#ifndef CAMERAWORKER_H |
||||
#define CAMERAWORKER_H |
||||
|
||||
#include <QObject> |
||||
#include <QList> |
||||
#include <QMap> |
||||
#include <QMutex> |
||||
#include <QQueue> |
||||
#include <QSize> |
||||
|
||||
#include "opencv2/opencv.hpp" |
||||
|
||||
class QTimer; |
||||
|
||||
class CameraWorker : public QObject |
||||
{ |
||||
Q_OBJECT |
||||
public: |
||||
CameraWorker(int index); |
||||
void doWork(); |
||||
bool hasFrame(); |
||||
cv::Mat3b dequeueFrame(); |
||||
|
||||
void suspend(); |
||||
void resume(); |
||||
void setProp(int prop, double val); |
||||
double getProp(int prop); // blocking call!
|
||||
void probeResolutions(); |
||||
|
||||
public slots: |
||||
void onStart(); |
||||
|
||||
signals: |
||||
void started(); |
||||
void newFrameAvailable(); |
||||
void resProbingFinished(QList<QSize> res); |
||||
|
||||
private slots: |
||||
void _suspend(); |
||||
void _resume(); |
||||
void _setProp(int prop, double val); |
||||
double _getProp(int prop); |
||||
|
||||
private: |
||||
void applyProps(); |
||||
void subscribe(); |
||||
void unsubscribe(); |
||||
|
||||
private: |
||||
QMutex mutex; |
||||
QQueue<cv::Mat3b> queue; |
||||
QTimer* clock; |
||||
cv::VideoCapture cam; |
||||
cv::Mat3b frame; |
||||
int camIndex; |
||||
QMap<int, double> props; |
||||
QList<QSize> resolutions; |
||||
int refCount; |
||||
}; |
||||
|
||||
#endif // CAMERAWORKER_H
|
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
#ifndef VIDEOSOURCE_H |
||||
#define VIDEOSOURCE_H |
||||
|
||||
#include <QObject> |
||||
#include <QSize> |
||||
|
||||
class VideoSource : public QObject |
||||
{ |
||||
Q_OBJECT |
||||
public: |
||||
virtual void* getData() = 0; // a pointer to a frame
|
||||
virtual int getDataSize() = 0; // size of a frame in bytes
|
||||
|
||||
virtual void lock() = 0; // locks a frame so that it can't change
|
||||
virtual void unlock() = 0; |
||||
|
||||
virtual QSize resolution() = 0; // resolution of a frame
|
||||
|
||||
virtual void subscribe() = 0; |
||||
virtual void unsubscribe() = 0; |
||||
|
||||
signals: |
||||
void frameAvailable(); |
||||
|
||||
}; |
||||
|
||||
#endif // VIDEOSOURCE_H
|
@ -1,69 +0,0 @@
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox. |
||||
|
||||
This program is libre software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
||||
|
||||
See the COPYING file for more details. |
||||
*/ |
||||
|
||||
#include "selfcamview.h" |
||||
#include "camera.h" |
||||
#include <QCloseEvent> |
||||
#include <QShowEvent> |
||||
#include <QTimer> |
||||
#include <QLabel> |
||||
#include <QHBoxLayout> |
||||
#include <opencv2/opencv.hpp> |
||||
|
||||
using namespace cv; |
||||
|
||||
SelfCamView::SelfCamView(Camera* Cam, QWidget* parent) |
||||
: QWidget(parent), displayLabel{new QLabel}, |
||||
mainLayout{new QHBoxLayout()}, cam(Cam), updateDisplayTimer{new QTimer} |
||||
{ |
||||
setLayout(mainLayout); |
||||
setWindowTitle(SelfCamView::tr("Tox video test","Title of the window to test the video/webcam")); |
||||
setMinimumSize(320,240); |
||||
|
||||
updateDisplayTimer->setInterval(5); |
||||
updateDisplayTimer->setSingleShot(false); |
||||
|
||||
displayLabel->setAlignment(Qt::AlignCenter); |
||||
|
||||
mainLayout->addWidget(displayLabel); |
||||
|
||||
connect(updateDisplayTimer, SIGNAL(timeout()), this, SLOT(updateDisplay())); |
||||
} |
||||
|
||||
void SelfCamView::closeEvent(QCloseEvent* event) |
||||
{ |
||||
cam->unsuscribe(); |
||||
updateDisplayTimer->stop(); |
||||
event->accept(); |
||||
} |
||||
|
||||
void SelfCamView::showEvent(QShowEvent* event) |
||||
{ |
||||
cam->suscribe(); |
||||
updateDisplayTimer->start(); |
||||
event->accept(); |
||||
} |
||||
|
||||
void SelfCamView::updateDisplay() |
||||
{ |
||||
displayLabel->setPixmap(QPixmap::fromImage(cam->getLastImage()).scaled(displayLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); |
||||
} |
||||
|
||||
void SelfCamView::resizeEvent(QResizeEvent *e) |
||||
{ |
||||
Q_UNUSED(e) |
||||
updateDisplay(); |
||||
} |
@ -0,0 +1,197 @@
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox. |
||||
|
||||
This program is libre software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
||||
|
||||
See the COPYING file for more details. |
||||
*/ |
||||
|
||||
#include "videosurface.h" |
||||
#include "camera.h" |
||||
#include <QTimer> |
||||
#include <opencv2/opencv.hpp> |
||||
#include <QOpenGLBuffer> |
||||
#include <QOpenGLShaderProgram> |
||||
#include <QDebug> |
||||
|
||||
VideoSurface::VideoSurface(VideoSource *Source, QWidget* parent) |
||||
: QGLWidget(QGLFormat(QGL::SampleBuffers), parent) |
||||
, source(Source) |
||||
, pbo(nullptr) |
||||
, program(nullptr) |
||||
, textureId(0) |
||||
, pboAllocSize(0) |
||||
, uploadFrame(false) |
||||
, hasSubscribed(false) |
||||
{ |
||||
setFixedSize(source->resolution()); |
||||
} |
||||
|
||||
VideoSurface::~VideoSurface() |
||||
{ |
||||
if (pbo) |
||||
delete pbo; |
||||
|
||||
if (textureId != 0) |
||||
glDeleteTextures(1, &textureId); |
||||
|
||||
source->unsubscribe(); |
||||
} |
||||
|
||||
void VideoSurface::hideEvent(QHideEvent *ev) |
||||
{ |
||||
if (hasSubscribed) |
||||
{ |
||||
source->unsubscribe(); |
||||
hasSubscribed = false; |
||||
disconnect(source, &VideoSource::frameAvailable, this, &VideoSurface::updateGL); |
||||
} |
||||
|
||||
QGLWidget::hideEvent(ev); |
||||
} |
||||
|
||||
void VideoSurface::showEvent(QShowEvent *ev) |
||||
{ |
||||
if (!hasSubscribed) |
||||
{ |
||||
source->subscribe(); |
||||
hasSubscribed = true; |
||||
connect(source, &VideoSource::frameAvailable, this, &VideoSurface::updateGL); |
||||
} |
||||
|
||||
QGLWidget::showEvent(ev); |
||||
} |
||||
|
||||
void VideoSurface::initializeGL() |
||||
{ |
||||
|
||||
} |
||||
|
||||
void VideoSurface::paintGL() |
||||
{ |
||||
if (!pbo) |
||||
{ |
||||
qDebug() << "Creating pbo, program"; |
||||
|
||||
// pbo
|
||||
pbo = new QOpenGLBuffer(QOpenGLBuffer::PixelUnpackBuffer); |
||||
pbo->setUsagePattern(QOpenGLBuffer::StreamDraw); |
||||
pbo->create(); |
||||
|
||||
// shaders
|
||||
program = new QOpenGLShaderProgram; |
||||
program->addShaderFromSourceCode(QOpenGLShader::Vertex, |
||||
"attribute vec4 vertices;" |
||||
"varying vec2 coords;" |
||||
"void main() {" |
||||
" gl_Position = vec4(vertices.xy,0.0,1.0);" |
||||
" coords = vertices.xy*vec2(0.5,0.5)+vec2(0.5,0.5);" |
||||
"}"); |
||||
program->addShaderFromSourceCode(QOpenGLShader::Fragment, |
||||
"uniform sampler2D texture0;" |
||||
"varying vec2 coords;" |
||||
"void main() {" |
||||
" vec4 color = texture2D(texture0,coords*vec2(1.0, -1.0));" |
||||
" gl_FragColor = vec4(color.b, color.g, color.r, 1);" |
||||
"}"); |
||||
|
||||
program->bindAttributeLocation("vertices", 0); |
||||
program->link(); |
||||
} |
||||
|
||||
if (res != source->resolution()) |
||||
{ |
||||
qDebug() << "Change resolution " << res << " to " << source->resolution(); |
||||
res = source->resolution(); |
||||
|
||||
// a texture used to render the pbo (has the match the pixelformat of the source)
|
||||
glGenTextures(1,&textureId); |
||||
glBindTexture(GL_TEXTURE_2D, textureId); |
||||
glTexImage2D(GL_TEXTURE_2D,0, GL_RGB, res.width(), res.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
||||
|
||||
setFixedSize(res); |
||||
} |
||||
|
||||
|
||||
if (uploadFrame) |
||||
{ |
||||
source->lock(); |
||||
void* frame = source->getData(); |
||||
int frameBytes = source->getDataSize(); |
||||
|
||||
if (pboAllocSize != frameBytes && frameBytes > 0) |
||||
{ |
||||
qDebug() << "Resize pbo " << frameBytes << "bytes (was" << pboAllocSize << ") res " << source->resolution(); |
||||
|
||||
pbo->bind(); |
||||
pbo->allocate(frameBytes); |
||||
pbo->release(); |
||||
|
||||
pboAllocSize = frameBytes; |
||||
} |
||||
|
||||
// transfer data
|
||||
pbo->bind(); |
||||
|
||||
void* ptr = pbo->map(QOpenGLBuffer::WriteOnly); |
||||
if (ptr && frame) |
||||
memcpy(ptr, frame, frameBytes); |
||||
pbo->unmap(); |
||||
|
||||
source->unlock(); |
||||
|
||||
//transfer pbo data to texture
|
||||
glBindTexture(GL_TEXTURE_2D, textureId); |
||||
glTexSubImage2D(GL_TEXTURE_2D,0,0,0, res.width(), res.height(), GL_RGB, GL_UNSIGNED_BYTE, 0); |
||||
glBindTexture(GL_TEXTURE_2D, 0); |
||||
|
||||
pbo->release(); |
||||
|
||||
uploadFrame = false; |
||||
} |
||||
|
||||
// render pbo
|
||||
float values[] = { |
||||
-1, -1, |
||||
1, -1, |
||||
-1, 1, |
||||
1, 1 |
||||
}; |
||||
|
||||
program->setAttributeArray(0, GL_FLOAT, values, 2); |
||||
|
||||
|
||||
glClearColor(0, 0, 0, 1); |
||||
glClear(GL_COLOR_BUFFER_BIT); |
||||
|
||||
glViewport(0, 0, width(), height()); |
||||
|
||||
program->bind(); |
||||
program->enableAttributeArray(0); |
||||
|
||||
glBindTexture(GL_TEXTURE_2D, textureId); |
||||
|
||||
//draw fullscreen quad
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
||||
glBindTexture(GL_TEXTURE_2D, 0); |
||||
|
||||
program->disableAttributeArray(0); |
||||
program->release(); |
||||
} |
||||
|
||||
void VideoSurface::updateGL() |
||||
{ |
||||
uploadFrame = true; |
||||
QGLWidget::updateGL(); |
||||
} |
||||
|
||||
|
Loading…
Reference in new issue