mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
commit
4e9d802d1f
205
cameraworker.cpp
Normal file
205
cameraworker.cpp
Normal file
@ -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;
|
||||||
|
}
|
77
cameraworker.h
Normal file
77
cameraworker.h
Normal file
@ -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
|
@ -57,7 +57,7 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled
|
|||||||
if (calls[callId].videoEnabled)
|
if (calls[callId].videoEnabled)
|
||||||
{
|
{
|
||||||
calls[callId].sendVideoTimer->start();
|
calls[callId].sendVideoTimer->start();
|
||||||
Camera::getInstance()->suscribe();
|
Camera::getInstance()->subscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,12 +73,12 @@ void Core::onAvMediaChange(void* toxav, int32_t callId, void* core)
|
|||||||
{
|
{
|
||||||
calls[callId].videoEnabled = false;
|
calls[callId].videoEnabled = false;
|
||||||
calls[callId].sendVideoTimer->stop();
|
calls[callId].sendVideoTimer->stop();
|
||||||
Camera::getInstance()->unsuscribe();
|
Camera::getInstance()->unsubscribe();
|
||||||
emit ((Core*)core)->avMediaChange(friendId, callId, false);
|
emit ((Core*)core)->avMediaChange(friendId, callId, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Camera::getInstance()->suscribe();
|
Camera::getInstance()->subscribe();
|
||||||
calls[callId].videoEnabled = true;
|
calls[callId].videoEnabled = true;
|
||||||
calls[callId].sendVideoTimer->start();
|
calls[callId].sendVideoTimer->start();
|
||||||
emit ((Core*)core)->avMediaChange(friendId, callId, true);
|
emit ((Core*)core)->avMediaChange(friendId, callId, true);
|
||||||
@ -161,7 +161,7 @@ void Core::cleanupCall(int callId)
|
|||||||
calls[callId].sendAudioTimer->stop();
|
calls[callId].sendAudioTimer->stop();
|
||||||
calls[callId].sendVideoTimer->stop();
|
calls[callId].sendVideoTimer->stop();
|
||||||
if (calls[callId].videoEnabled)
|
if (calls[callId].videoEnabled)
|
||||||
Camera::getInstance()->unsuscribe();
|
Camera::getInstance()->unsubscribe();
|
||||||
alcCaptureStop(alInDev);
|
alcCaptureStop(alInDev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
qtox.pro
15
qtox.pro
@ -20,7 +20,7 @@
|
|||||||
# See the COPYING file for more details.
|
# See the COPYING file for more details.
|
||||||
|
|
||||||
|
|
||||||
QT += core gui network xml
|
QT += core gui network xml opengl
|
||||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
TARGET = qtox
|
TARGET = qtox
|
||||||
@ -68,7 +68,7 @@ win32 {
|
|||||||
LIBS += -Wl,-Bdynamic -ltbb -lv4l1 -lv4l2 -lgnutls -lrtmp -lgnutls -lavformat -lavcodec -lavutil -lavfilter -lswscale -lusb-1.0
|
LIBS += -Wl,-Bdynamic -ltbb -lv4l1 -lv4l2 -lgnutls -lrtmp -lgnutls -lavformat -lavcodec -lavutil -lavfilter -lswscale -lusb-1.0
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -lvpx -lopenal -lopencv_core -lopencv_highgui
|
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -lvpx -lopenal -lopencv_core -lopencv_highgui -lopencv_imgproc
|
||||||
}
|
}
|
||||||
|
|
||||||
contains(JENKINS, YES) {
|
contains(JENKINS, YES) {
|
||||||
@ -111,7 +111,6 @@ HEADERS += widget/form/addfriendform.h \
|
|||||||
friendlist.h \
|
friendlist.h \
|
||||||
misc/cdata.h \
|
misc/cdata.h \
|
||||||
misc/cstring.h \
|
misc/cstring.h \
|
||||||
widget/selfcamview.h \
|
|
||||||
widget/camera.h \
|
widget/camera.h \
|
||||||
widget/netcamview.h \
|
widget/netcamview.h \
|
||||||
misc/smileypack.h \
|
misc/smileypack.h \
|
||||||
@ -132,7 +131,10 @@ HEADERS += widget/form/addfriendform.h \
|
|||||||
widget/tool/chatactions/filetransferaction.h \
|
widget/tool/chatactions/filetransferaction.h \
|
||||||
widget/tool/chatactions/systemmessageaction.h \
|
widget/tool/chatactions/systemmessageaction.h \
|
||||||
widget/tool/chatactions/actionaction.h \
|
widget/tool/chatactions/actionaction.h \
|
||||||
widget/maskablepixmapwidget.h
|
widget/maskablepixmapwidget.h \
|
||||||
|
videosource.h \
|
||||||
|
cameraworker.h \
|
||||||
|
widget/videosurface.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
widget/form/addfriendform.cpp \
|
widget/form/addfriendform.cpp \
|
||||||
@ -158,7 +160,6 @@ SOURCES += \
|
|||||||
misc/settings.cpp \
|
misc/settings.cpp \
|
||||||
misc/cdata.cpp \
|
misc/cdata.cpp \
|
||||||
misc/cstring.cpp \
|
misc/cstring.cpp \
|
||||||
widget/selfcamview.cpp \
|
|
||||||
widget/camera.cpp \
|
widget/camera.cpp \
|
||||||
widget/netcamview.cpp \
|
widget/netcamview.cpp \
|
||||||
misc/smileypack.cpp \
|
misc/smileypack.cpp \
|
||||||
@ -178,4 +179,6 @@ SOURCES += \
|
|||||||
widget/tool/chatactions/filetransferaction.cpp \
|
widget/tool/chatactions/filetransferaction.cpp \
|
||||||
widget/tool/chatactions/systemmessageaction.cpp \
|
widget/tool/chatactions/systemmessageaction.cpp \
|
||||||
widget/tool/chatactions/actionaction.cpp \
|
widget/tool/chatactions/actionaction.cpp \
|
||||||
widget/maskablepixmapwidget.cpp
|
widget/maskablepixmapwidget.cpp \
|
||||||
|
cameraworker.cpp \
|
||||||
|
widget/videosurface.cpp
|
||||||
|
27
videosource.h
Normal file
27
videosource.h
Normal file
@ -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
|
@ -16,68 +16,72 @@
|
|||||||
|
|
||||||
#include "camera.h"
|
#include "camera.h"
|
||||||
#include "widget.h"
|
#include "widget.h"
|
||||||
|
#include "cameraworker.h"
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
using namespace cv;
|
Camera* Camera::instance = nullptr;
|
||||||
|
|
||||||
Camera::Camera()
|
Camera::Camera()
|
||||||
: refcount{0}
|
: refcount(0)
|
||||||
|
, workerThread(nullptr)
|
||||||
|
, worker(nullptr)
|
||||||
{
|
{
|
||||||
|
worker = new CameraWorker(0);
|
||||||
|
workerThread = new QThread();
|
||||||
|
|
||||||
|
worker->moveToThread(workerThread);
|
||||||
|
|
||||||
|
connect(workerThread, &QThread::started, worker, &CameraWorker::onStart);
|
||||||
|
connect(workerThread, &QThread::finished, worker, &CameraWorker::deleteLater);
|
||||||
|
connect(workerThread, &QThread::deleteLater, worker, &CameraWorker::deleteLater);
|
||||||
|
connect(worker, &CameraWorker::started, this, &Camera::onWorkerStarted);
|
||||||
|
connect(worker, &CameraWorker::newFrameAvailable, this, &Camera::onNewFrameAvailable);
|
||||||
|
connect(worker, &CameraWorker::resProbingFinished, this, &Camera::onResProbingFinished);
|
||||||
|
workerThread->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::suscribe()
|
void Camera::onWorkerStarted()
|
||||||
|
{
|
||||||
|
worker->probeResolutions();
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera::~Camera()
|
||||||
|
{
|
||||||
|
workerThread->exit();
|
||||||
|
workerThread->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::subscribe()
|
||||||
{
|
{
|
||||||
if (refcount <= 0)
|
if (refcount <= 0)
|
||||||
{
|
worker->resume();
|
||||||
refcount = 1;
|
|
||||||
cam.open(0);
|
refcount++;
|
||||||
}
|
|
||||||
else
|
|
||||||
refcount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::unsuscribe()
|
void Camera::unsubscribe()
|
||||||
{
|
{
|
||||||
refcount--;
|
refcount--;
|
||||||
|
|
||||||
if (refcount <= 0)
|
if (refcount <= 0)
|
||||||
{
|
{
|
||||||
cam.release();
|
worker->suspend();
|
||||||
refcount = 0;
|
refcount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Mat Camera::getLastFrame()
|
|
||||||
{
|
|
||||||
Mat frame;
|
|
||||||
cam >> frame;
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
QImage Camera::getLastImage()
|
|
||||||
{
|
|
||||||
Mat3b src = getLastFrame();
|
|
||||||
QImage dest(src.cols, src.rows, QImage::Format_ARGB32);
|
|
||||||
for (int y = 0; y < src.rows; ++y)
|
|
||||||
{
|
|
||||||
const cv::Vec3b *srcrow = src[y];
|
|
||||||
QRgb *destrow = (QRgb*)dest.scanLine(y);
|
|
||||||
for (int x = 0; x < src.cols; ++x)
|
|
||||||
destrow[x] = qRgba(srcrow[x][2], srcrow[x][1], srcrow[x][0], 255);
|
|
||||||
}
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
vpx_image Camera::getLastVPXImage()
|
vpx_image Camera::getLastVPXImage()
|
||||||
{
|
{
|
||||||
Mat3b frame = getLastFrame();
|
lock();
|
||||||
vpx_image img;
|
vpx_image img;
|
||||||
int w = frame.size().width, h = frame.size().height;
|
int w = currFrame.size().width, h = currFrame.size().height;
|
||||||
vpx_img_alloc(&img, VPX_IMG_FMT_I420, w, h, 1); // I420 == YUV420P, same as YV12 with U and V switched
|
vpx_img_alloc(&img, VPX_IMG_FMT_I420, w, h, 1); // I420 == YUV420P, same as YV12 with U and V switched
|
||||||
|
|
||||||
size_t i=0, j=0;
|
size_t i=0, j=0;
|
||||||
for( int line = 0; line < h; ++line )
|
for( int line = 0; line < h; ++line )
|
||||||
{
|
{
|
||||||
const cv::Vec3b *srcrow = frame[line];
|
const cv::Vec3b *srcrow = currFrame[line];
|
||||||
if( !(line % 2) )
|
if( !(line % 2) )
|
||||||
{
|
{
|
||||||
for( int x = 0; x < w; x += 2 )
|
for( int x = 0; x < w; x += 2 )
|
||||||
@ -112,10 +116,123 @@ vpx_image Camera::getLastVPXImage()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
unlock();
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<QSize> Camera::getSupportedResolutions()
|
||||||
|
{
|
||||||
|
return resolutions;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize Camera::getBestVideoMode()
|
||||||
|
{
|
||||||
|
int bestScore = 0;
|
||||||
|
QSize bestRes;
|
||||||
|
|
||||||
|
for (QSize res : getSupportedResolutions())
|
||||||
|
{
|
||||||
|
int score = res.width() * res.height();
|
||||||
|
|
||||||
|
if (score > bestScore)
|
||||||
|
{
|
||||||
|
bestScore = score;
|
||||||
|
bestRes = res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::setResolution(QSize res)
|
||||||
|
{
|
||||||
|
worker->setProp(CV_CAP_PROP_FRAME_WIDTH, res.width());
|
||||||
|
worker->setProp(CV_CAP_PROP_FRAME_HEIGHT, res.height());
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize Camera::getResolution()
|
||||||
|
{
|
||||||
|
return QSize(worker->getProp(CV_CAP_PROP_FRAME_WIDTH), worker->getProp(CV_CAP_PROP_FRAME_HEIGHT));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::setProp(Camera::Prop prop, double val)
|
||||||
|
{
|
||||||
|
switch (prop)
|
||||||
|
{
|
||||||
|
case BRIGHTNESS:
|
||||||
|
worker->setProp(CV_CAP_PROP_BRIGHTNESS, val);
|
||||||
|
break;
|
||||||
|
case SATURATION:
|
||||||
|
worker->setProp(CV_CAP_PROP_SATURATION, val);
|
||||||
|
break;
|
||||||
|
case CONTRAST:
|
||||||
|
worker->setProp(CV_CAP_PROP_CONTRAST, val);
|
||||||
|
break;
|
||||||
|
case HUE:
|
||||||
|
worker->setProp(CV_CAP_PROP_HUE, val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double Camera::getProp(Camera::Prop prop)
|
||||||
|
{
|
||||||
|
switch (prop)
|
||||||
|
{
|
||||||
|
case BRIGHTNESS:
|
||||||
|
return worker->getProp(CV_CAP_PROP_BRIGHTNESS);
|
||||||
|
case SATURATION:
|
||||||
|
return worker->getProp(CV_CAP_PROP_SATURATION);
|
||||||
|
case CONTRAST:
|
||||||
|
return worker->getProp(CV_CAP_PROP_CONTRAST);
|
||||||
|
case HUE:
|
||||||
|
return worker->getProp(CV_CAP_PROP_HUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::onNewFrameAvailable()
|
||||||
|
{
|
||||||
|
emit frameAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::onResProbingFinished(QList<QSize> res)
|
||||||
|
{
|
||||||
|
resolutions = res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *Camera::getData()
|
||||||
|
{
|
||||||
|
return currFrame.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Camera::getDataSize()
|
||||||
|
{
|
||||||
|
return currFrame.total() * currFrame.channels();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::lock()
|
||||||
|
{
|
||||||
|
mutex.lock();
|
||||||
|
|
||||||
|
if (worker->hasFrame())
|
||||||
|
currFrame = worker->dequeueFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::unlock()
|
||||||
|
{
|
||||||
|
mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize Camera::resolution()
|
||||||
|
{
|
||||||
|
return QSize(currFrame.cols, currFrame.rows);
|
||||||
|
}
|
||||||
|
|
||||||
Camera* Camera::getInstance()
|
Camera* Camera::getInstance()
|
||||||
{
|
{
|
||||||
return Widget::getInstance()->getCamera();
|
if (!instance)
|
||||||
|
instance = new Camera();
|
||||||
|
|
||||||
|
return instance;
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,13 @@
|
|||||||
#define CAMERA_H
|
#define CAMERA_H
|
||||||
|
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
|
#include <QList>
|
||||||
|
#include <QMutex>
|
||||||
#include "vpx/vpx_image.h"
|
#include "vpx/vpx_image.h"
|
||||||
#include "opencv2/opencv.hpp"
|
#include "opencv2/opencv.hpp"
|
||||||
|
#include "videosource.h"
|
||||||
|
|
||||||
|
class CameraWorker;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is a wrapper to share a camera's captured video frames
|
* This class is a wrapper to share a camera's captured video frames
|
||||||
@ -27,20 +32,60 @@
|
|||||||
* the camera only when needed, and giving access to the last frames
|
* the camera only when needed, and giving access to the last frames
|
||||||
**/
|
**/
|
||||||
|
|
||||||
class Camera
|
class Camera : public VideoSource
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Camera();
|
enum Prop {
|
||||||
|
BRIGHTNESS,
|
||||||
|
SATURATION,
|
||||||
|
CONTRAST,
|
||||||
|
HUE,
|
||||||
|
};
|
||||||
|
|
||||||
|
~Camera();
|
||||||
|
|
||||||
static Camera* getInstance(); ///< Returns the global widget's Camera instance
|
static Camera* getInstance(); ///< Returns the global widget's Camera instance
|
||||||
void suscribe(); ///< Call this once before trying to get frames
|
|
||||||
void unsuscribe(); ///< Call this once when you don't need frames anymore
|
|
||||||
cv::Mat getLastFrame(); ///< Get the last captured frame
|
|
||||||
QImage getLastImage(); ///< Convert the last frame to a QImage (can be expensive !)
|
|
||||||
vpx_image getLastVPXImage(); ///< Convert the last frame to a vpx_image (can be expensive !)
|
vpx_image getLastVPXImage(); ///< Convert the last frame to a vpx_image (can be expensive !)
|
||||||
|
|
||||||
|
QList<QSize> getSupportedResolutions();
|
||||||
|
QSize getBestVideoMode();
|
||||||
|
|
||||||
|
void setResolution(QSize res);
|
||||||
|
QSize getResolution();
|
||||||
|
|
||||||
|
void setProp(Prop prop, double val);
|
||||||
|
double getProp(Prop prop);
|
||||||
|
|
||||||
|
// VideoSource interface
|
||||||
|
virtual void *getData();
|
||||||
|
virtual int getDataSize();
|
||||||
|
virtual void lock();
|
||||||
|
virtual void unlock();
|
||||||
|
virtual QSize resolution();
|
||||||
|
virtual void subscribe();
|
||||||
|
virtual void unsubscribe();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Camera();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int refcount; ///< Number of users suscribed to the camera
|
int refcount; ///< Number of users suscribed to the camera
|
||||||
cv::VideoCapture cam; ///< OpenCV camera capture opbject
|
cv::Mat3b currFrame;
|
||||||
|
QMutex mutex;
|
||||||
|
|
||||||
|
QThread* workerThread;
|
||||||
|
CameraWorker* worker;
|
||||||
|
|
||||||
|
QList<QSize> resolutions;
|
||||||
|
|
||||||
|
static Camera* instance;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onWorkerStarted();
|
||||||
|
void onNewFrameAvailable();
|
||||||
|
void onResProbingFinished(QList<QSize> res);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CAMERA_H
|
#endif // CAMERA_H
|
||||||
|
@ -18,17 +18,16 @@
|
|||||||
#include "widget/camera.h"
|
#include "widget/camera.h"
|
||||||
#include "ui_avsettings.h"
|
#include "ui_avsettings.h"
|
||||||
|
|
||||||
AVForm::AVForm(Camera* cam) :
|
AVForm::AVForm() :
|
||||||
GenericForm(tr("Audio/Video settings"), QPixmap(":/img/settings/av.png"))
|
GenericForm(tr("Audio/Video settings"), QPixmap(":/img/settings/av.png"))
|
||||||
{
|
{
|
||||||
bodyUI = new Ui::AVSettings;
|
bodyUI = new Ui::AVSettings;
|
||||||
bodyUI->setupUi(this);
|
bodyUI->setupUi(this);
|
||||||
|
|
||||||
camView = new SelfCamView(cam, this);
|
//cam->setVideoMode(cam->getBestVideoMode());
|
||||||
bodyUI->videoGroup->layout()->addWidget(camView);
|
camView = new VideoSurface(Camera::getInstance(), this);
|
||||||
camView->hide(); // hide by default
|
|
||||||
|
|
||||||
connect(bodyUI->testVideoBtn, &QPushButton::clicked, this, &AVForm::onTestVideoPressed);
|
bodyUI->CamViewLayout->addWidget(camView);
|
||||||
}
|
}
|
||||||
|
|
||||||
AVForm::~AVForm()
|
AVForm::~AVForm()
|
||||||
@ -36,22 +35,44 @@ AVForm::~AVForm()
|
|||||||
delete bodyUI;
|
delete bodyUI;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AVForm::showTestVideo()
|
void AVForm::present()
|
||||||
{
|
{
|
||||||
bodyUI->testVideoBtn->setText(tr("Hide video preview","On a button"));
|
bodyUI->videoModescomboBox->clear();
|
||||||
camView->show();
|
QList<QSize> res = Camera::getInstance()->getSupportedResolutions();
|
||||||
|
for (QSize r : res)
|
||||||
|
bodyUI->videoModescomboBox->addItem(QString("%1x%2").arg(QString::number(r.width()),QString::number(r.height())));
|
||||||
|
|
||||||
|
bodyUI->ContrastSlider->setValue(Camera::getInstance()->getProp(Camera::CONTRAST)*100);
|
||||||
|
bodyUI->BrightnessSlider->setValue(Camera::getInstance()->getProp(Camera::BRIGHTNESS)*100);
|
||||||
|
bodyUI->SaturationSlider->setValue(Camera::getInstance()->getProp(Camera::SATURATION)*100);
|
||||||
|
bodyUI->HueSlider->setValue(Camera::getInstance()->getProp(Camera::HUE)*100);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AVForm::closeTestVideo()
|
void AVForm::on_ContrastSlider_sliderMoved(int position)
|
||||||
{
|
{
|
||||||
bodyUI->testVideoBtn->setText(tr("Show video preview","On a button"));
|
Camera::getInstance()->setProp(Camera::CONTRAST, position / 100.0);
|
||||||
camView->close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AVForm::onTestVideoPressed()
|
void AVForm::on_SaturationSlider_sliderMoved(int position)
|
||||||
{
|
{
|
||||||
if (camView->isVisible())
|
Camera::getInstance()->setProp(Camera::SATURATION, position / 100.0);
|
||||||
closeTestVideo();
|
}
|
||||||
else
|
|
||||||
showTestVideo();
|
void AVForm::on_BrightnessSlider_sliderMoved(int position)
|
||||||
|
{
|
||||||
|
Camera::getInstance()->setProp(Camera::BRIGHTNESS, position / 100.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AVForm::on_HueSlider_sliderMoved(int position)
|
||||||
|
{
|
||||||
|
Camera::getInstance()->setProp(Camera::HUE, position / 100.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AVForm::on_videoModescomboBox_currentIndexChanged(const QString &arg1)
|
||||||
|
{
|
||||||
|
QStringList resStr = arg1.split("x");
|
||||||
|
int w = resStr[0].toInt();
|
||||||
|
int h = resStr[0].toInt();
|
||||||
|
|
||||||
|
Camera::getInstance()->setResolution(QSize(w,h));
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
#define AVFORM_H
|
#define AVFORM_H
|
||||||
|
|
||||||
#include "genericsettings.h"
|
#include "genericsettings.h"
|
||||||
#include "widget/selfcamview.h"
|
#include "widget/videosurface.h"
|
||||||
#include <QGroupBox>
|
#include <QGroupBox>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
@ -33,20 +33,21 @@ class AVForm : public GenericForm
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
AVForm(Camera* cam);
|
AVForm();
|
||||||
~AVForm();
|
~AVForm();
|
||||||
|
virtual void present();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onTestVideoPressed();
|
|
||||||
|
void on_ContrastSlider_sliderMoved(int position);
|
||||||
|
void on_SaturationSlider_sliderMoved(int position);
|
||||||
|
void on_BrightnessSlider_sliderMoved(int position);
|
||||||
|
void on_HueSlider_sliderMoved(int position);
|
||||||
|
void on_videoModescomboBox_currentIndexChanged(const QString &arg1);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::AVSettings *bodyUI;
|
Ui::AVSettings *bodyUI;
|
||||||
|
VideoSurface* camView;
|
||||||
SelfCamView* camView;
|
|
||||||
|
|
||||||
void showTestVideo();
|
|
||||||
void closeTestVideo();
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -6,27 +6,143 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>400</width>
|
<width>394</width>
|
||||||
<height>300</height>
|
<height>391</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Form</string>
|
<string>Form</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Volume Settings (Stubs)</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="formLayout_2">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>Playback</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QSlider" name="horizontalSlider">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<string>Microphone</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QSlider" name="horizontalSlider_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="videoGroup">
|
<widget class="QGroupBox" name="videoGroup">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Video settings</string>
|
<string>Video settings</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QFormLayout" name="formLayout">
|
||||||
<item>
|
<property name="fieldGrowthPolicy">
|
||||||
<widget class="QPushButton" name="testVideoBtn">
|
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Show video preview</string>
|
<string>Modes</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="videoModescomboBox">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Hue</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QSlider" name="HueSlider">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Brightness</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QSlider" name="BrightnessSlider">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Saturation</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QSlider" name="SaturationSlider">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Contrast</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QSlider" name="ContrastSlider">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>Preview</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<layout class="QVBoxLayout" name="CamViewLayout"/>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -38,7 +154,7 @@
|
|||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>20</width>
|
<width>20</width>
|
||||||
<height>40</height>
|
<height>75</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
|
@ -27,7 +27,7 @@ public:
|
|||||||
GenericForm(const QString &name, const QPixmap &icon) : formName(name), formIcon(icon) {;}
|
GenericForm(const QString &name, const QPixmap &icon) : formName(name), formIcon(icon) {;}
|
||||||
~GenericForm() {;}
|
~GenericForm() {;}
|
||||||
|
|
||||||
virtual void updateContent() {;}
|
virtual void present() {}
|
||||||
QString getFormName() {return formName;}
|
QString getFormName() {return formName;}
|
||||||
QPixmap getFormIcon() {return formIcon;}
|
QPixmap getFormIcon() {return formIcon;}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ void IdentityForm::onStatusMessageEdited()
|
|||||||
Core::getInstance()->setStatusMessage(bodyUI->statusMessage->text());
|
Core::getInstance()->setStatusMessage(bodyUI->statusMessage->text());
|
||||||
}
|
}
|
||||||
|
|
||||||
void IdentityForm::updateContent()
|
void IdentityForm::present()
|
||||||
{
|
{
|
||||||
toxId->setText(Core::getInstance()->getSelfId().toString());
|
toxId->setText(Core::getInstance()->getSelfId().toString());
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ public:
|
|||||||
void setUserName(const QString &name);
|
void setUserName(const QString &name);
|
||||||
void setStatusMessage(const QString &msg);
|
void setStatusMessage(const QString &msg);
|
||||||
|
|
||||||
virtual void updateContent();
|
virtual void present();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void userNameChanged(QString);
|
void userNameChanged(QString);
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
#include <QTabBar>
|
#include <QTabBar>
|
||||||
#include <QStackedWidget>
|
#include <QStackedWidget>
|
||||||
|
|
||||||
SettingsWidget::SettingsWidget(Camera* cam, QWidget* parent)
|
SettingsWidget::SettingsWidget(QWidget* parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
{
|
{
|
||||||
body = new QWidget(this);
|
body = new QWidget(this);
|
||||||
@ -55,7 +55,7 @@ SettingsWidget::SettingsWidget(Camera* cam, QWidget* parent)
|
|||||||
GeneralForm *gfrm = new GeneralForm;
|
GeneralForm *gfrm = new GeneralForm;
|
||||||
ifrm = new IdentityForm;
|
ifrm = new IdentityForm;
|
||||||
PrivacyForm *pfrm = new PrivacyForm;
|
PrivacyForm *pfrm = new PrivacyForm;
|
||||||
AVForm *avfrm = new AVForm(cam);
|
AVForm *avfrm = new AVForm;
|
||||||
|
|
||||||
GenericForm *cfgForms[] = {gfrm, ifrm, pfrm, avfrm};
|
GenericForm *cfgForms[] = {gfrm, ifrm, pfrm, avfrm};
|
||||||
for (auto cfgForm : cfgForms)
|
for (auto cfgForm : cfgForms)
|
||||||
@ -85,8 +85,8 @@ void SettingsWidget::show(Ui::MainWindow& ui)
|
|||||||
void SettingsWidget::onTabChanged(int index)
|
void SettingsWidget::onTabChanged(int index)
|
||||||
{
|
{
|
||||||
this->settingsWidgets->setCurrentIndex(index);
|
this->settingsWidgets->setCurrentIndex(index);
|
||||||
GenericForm *currentWidget = static_cast<GenericForm*>(this->settingsWidgets->widget(index));
|
GenericForm* currentWidget = static_cast<GenericForm*>(this->settingsWidgets->widget(index));
|
||||||
currentWidget->updateContent();
|
currentWidget->present();
|
||||||
nameLabel->setText(currentWidget->getFormName());
|
nameLabel->setText(currentWidget->getFormName());
|
||||||
imgLabel->setPixmap(currentWidget->getFormIcon().scaledToHeight(40, Qt::SmoothTransformation));
|
imgLabel->setPixmap(currentWidget->getFormIcon().scaledToHeight(40, Qt::SmoothTransformation));
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ class SettingsWidget : public QWidget
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
SettingsWidget(Camera* cam, QWidget* parent = nullptr);
|
SettingsWidget(QWidget* parent = nullptr);
|
||||||
~SettingsWidget();
|
~SettingsWidget();
|
||||||
|
|
||||||
void show(Ui::MainWindow &ui);
|
void show(Ui::MainWindow &ui);
|
||||||
|
@ -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();
|
|
||||||
}
|
|
197
widget/videosurface.cpp
Normal file
197
widget/videosurface.cpp
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -17,39 +17,42 @@
|
|||||||
#ifndef SELFCAMVIEW_H
|
#ifndef SELFCAMVIEW_H
|
||||||
#define SELFCAMVIEW_H
|
#define SELFCAMVIEW_H
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QGLWidget>
|
||||||
|
|
||||||
class QCloseEvent;
|
class QOpenGLBuffer;
|
||||||
class QShowEvent;
|
class QOpenGLShaderProgram;
|
||||||
class QPainter;
|
|
||||||
class Camera;
|
|
||||||
class QLabel;
|
|
||||||
class QHBoxLayout;
|
|
||||||
class QTimer;
|
class QTimer;
|
||||||
|
class VideoSource;
|
||||||
|
|
||||||
class SelfCamView : public QWidget
|
class VideoSurface : public QGLWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SelfCamView(Camera* Cam, QWidget *parent=0);
|
VideoSurface(VideoSource* source, QWidget* parent=0);
|
||||||
|
~VideoSurface();
|
||||||
|
|
||||||
private slots:
|
virtual void hideEvent(QHideEvent* ev);
|
||||||
void updateDisplay();
|
virtual void showEvent(QShowEvent* ev);
|
||||||
|
|
||||||
private:
|
|
||||||
void closeEvent(QCloseEvent*);
|
|
||||||
void showEvent(QShowEvent*);
|
|
||||||
void paint(QPainter *painter);
|
|
||||||
|
|
||||||
|
// QGLWidget interface
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent *e);
|
virtual void initializeGL();
|
||||||
|
virtual void paintGL();
|
||||||
|
virtual void updateGL();
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QLabel *displayLabel;
|
VideoSource* source;
|
||||||
QHBoxLayout* mainLayout;
|
QOpenGLBuffer* pbo;
|
||||||
Camera* cam;
|
QOpenGLShaderProgram* program;
|
||||||
QTimer* updateDisplayTimer;
|
GLuint textureId;
|
||||||
|
int pboAllocSize;
|
||||||
|
QSize res;
|
||||||
|
bool uploadFrame;
|
||||||
|
bool hasSubscribed;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SELFCAMVIEW_H
|
#endif // SELFCAMVIEW_H
|
@ -27,7 +27,6 @@
|
|||||||
#include "widget/groupwidget.h"
|
#include "widget/groupwidget.h"
|
||||||
#include "widget/form/groupchatform.h"
|
#include "widget/form/groupchatform.h"
|
||||||
#include "misc/style.h"
|
#include "misc/style.h"
|
||||||
#include "selfcamview.h"
|
|
||||||
#include "widget/friendlistwidget.h"
|
#include "widget/friendlistwidget.h"
|
||||||
#include "camera.h"
|
#include "camera.h"
|
||||||
#include "widget/form/chatform.h"
|
#include "widget/form/chatform.h"
|
||||||
@ -102,8 +101,7 @@ Widget::Widget(QWidget *parent)
|
|||||||
ui->statusButton->setProperty("status", "offline");
|
ui->statusButton->setProperty("status", "offline");
|
||||||
Style::repolish(ui->statusButton);
|
Style::repolish(ui->statusButton);
|
||||||
|
|
||||||
camera = new Camera;
|
settingsWidget = new SettingsWidget();
|
||||||
settingsWidget = new SettingsWidget(camera);
|
|
||||||
|
|
||||||
// Disable some widgets until we're connected to the DHT
|
// Disable some widgets until we're connected to the DHT
|
||||||
ui->statusButton->setEnabled(false);
|
ui->statusButton->setEnabled(false);
|
||||||
@ -119,7 +117,7 @@ Widget::Widget(QWidget *parent)
|
|||||||
qRegisterMetaType<ToxFile::FileDirection>("ToxFile::FileDirection");
|
qRegisterMetaType<ToxFile::FileDirection>("ToxFile::FileDirection");
|
||||||
|
|
||||||
coreThread = new QThread(this);
|
coreThread = new QThread(this);
|
||||||
core = new Core(camera, coreThread);
|
core = new Core(Camera::getInstance(), coreThread);
|
||||||
core->moveToThread(coreThread);
|
core->moveToThread(coreThread);
|
||||||
connect(coreThread, &QThread::started, core, &Core::start);
|
connect(coreThread, &QThread::started, core, &Core::start);
|
||||||
|
|
||||||
@ -218,11 +216,6 @@ QString Widget::getUsername()
|
|||||||
return core->getUsername();
|
return core->getUsername();
|
||||||
}
|
}
|
||||||
|
|
||||||
Camera* Widget::getCamera()
|
|
||||||
{
|
|
||||||
return camera;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Widget::onAvatarClicked()
|
void Widget::onAvatarClicked()
|
||||||
{
|
{
|
||||||
QString filename = QFileDialog::getOpenFileName(this, tr("Choose a profile picture"), QDir::homePath());
|
QString filename = QFileDialog::getOpenFileName(this, tr("Choose a profile picture"), QDir::homePath());
|
||||||
|
@ -34,7 +34,7 @@ class GenericChatroomWidget;
|
|||||||
class Group;
|
class Group;
|
||||||
struct Friend;
|
struct Friend;
|
||||||
class QSplitter;
|
class QSplitter;
|
||||||
class SelfCamView;
|
class VideoSurface;
|
||||||
class QMenu;
|
class QMenu;
|
||||||
class Core;
|
class Core;
|
||||||
class Camera;
|
class Camera;
|
||||||
@ -124,7 +124,6 @@ private:
|
|||||||
static Widget* instance;
|
static Widget* instance;
|
||||||
GenericChatroomWidget* activeChatroomWidget;
|
GenericChatroomWidget* activeChatroomWidget;
|
||||||
FriendListWidget* contactListWidget;
|
FriendListWidget* contactListWidget;
|
||||||
Camera* camera;
|
|
||||||
MaskablePixmapWidget* profilePicture;
|
MaskablePixmapWidget* profilePicture;
|
||||||
bool notify(QObject *receiver, QEvent *event);
|
bool notify(QObject *receiver, QEvent *event);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user