mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Merge branch 'pr525'
This commit is contained in:
commit
65331117d8
18
qtox.pro
18
qtox.pro
|
@ -120,7 +120,7 @@ HEADERS += src/widget/form/addfriendform.h \
|
|||
src/friendlist.h \
|
||||
src/misc/cdata.h \
|
||||
src/misc/cstring.h \
|
||||
src/camera.h \
|
||||
src/video/camera.h \
|
||||
src/widget/netcamview.h \
|
||||
src/misc/smileypack.h \
|
||||
src/widget/emoticonswidget.h \
|
||||
|
@ -142,8 +142,8 @@ HEADERS += src/widget/form/addfriendform.h \
|
|||
src/widget/tool/chatactions/actionaction.h \
|
||||
src/widget/tool/chatactions/alertaction.h \
|
||||
src/widget/maskablepixmapwidget.h \
|
||||
src/videosource.h \
|
||||
src/cameraworker.h \
|
||||
src/video/videosource.h \
|
||||
src/video/cameraworker.h \
|
||||
src/widget/videosurface.h \
|
||||
src/widget/form/loadhistorydialog.h \
|
||||
src/historykeeper.h \
|
||||
|
@ -152,7 +152,8 @@ HEADERS += src/widget/form/addfriendform.h \
|
|||
src/misc/db/encrypteddb.h \
|
||||
src/widget/form/inputpassworddialog.h \
|
||||
src/widget/form/setpassworddialog.h \
|
||||
src/widget/form/tabcompleter.h
|
||||
src/widget/form/tabcompleter.h \
|
||||
src/video/videoframe.h
|
||||
|
||||
SOURCES += \
|
||||
src/widget/form/addfriendform.cpp \
|
||||
|
@ -178,7 +179,7 @@ SOURCES += \
|
|||
src/misc/settings.cpp \
|
||||
src/misc/cdata.cpp \
|
||||
src/misc/cstring.cpp \
|
||||
src/camera.cpp \
|
||||
src/video/camera.cpp \
|
||||
src/widget/netcamview.cpp \
|
||||
src/misc/smileypack.cpp \
|
||||
src/widget/emoticonswidget.cpp \
|
||||
|
@ -199,7 +200,7 @@ SOURCES += \
|
|||
src/widget/tool/chatactions/actionaction.cpp \
|
||||
src/widget/tool/chatactions/alertaction.cpp \
|
||||
src/widget/maskablepixmapwidget.cpp \
|
||||
src/cameraworker.cpp \
|
||||
src/video/cameraworker.cpp \
|
||||
src/widget/videosurface.cpp \
|
||||
src/widget/form/loadhistorydialog.cpp \
|
||||
src/historykeeper.cpp \
|
||||
|
@ -208,5 +209,6 @@ SOURCES += \
|
|||
src/misc/db/encrypteddb.cpp \
|
||||
src/widget/form/inputpassworddialog.cpp \
|
||||
src/widget/form/setpassworddialog.cpp \
|
||||
src/netvideosource.cpp \
|
||||
src/widget/form/tabcompleter.cpp
|
||||
src/video/netvideosource.cpp \
|
||||
src/widget/form/tabcompleter.cpp \
|
||||
src/video/videoframe.cpp
|
||||
|
|
209
src/camera.cpp
209
src/camera.cpp
|
@ -1,209 +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 "camera.h"
|
||||
#include "src/cameraworker.h"
|
||||
#include <QDebug>
|
||||
#include <QThread>
|
||||
#include <QMutexLocker>
|
||||
|
||||
Camera* Camera::instance = nullptr;
|
||||
|
||||
Camera::Camera()
|
||||
: refcount(0)
|
||||
, workerThread(nullptr)
|
||||
, worker(nullptr)
|
||||
, needsInit(true)
|
||||
{
|
||||
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(worker, &CameraWorker::newFrameAvailable, this, &Camera::onNewFrameAvailable);
|
||||
connect(worker, &CameraWorker::resProbingFinished, this, &Camera::onResProbingFinished);
|
||||
workerThread->start();
|
||||
}
|
||||
|
||||
Camera::~Camera()
|
||||
{
|
||||
workerThread->exit();
|
||||
workerThread->deleteLater();
|
||||
}
|
||||
|
||||
void Camera::subscribe()
|
||||
{
|
||||
if (needsInit)
|
||||
{
|
||||
worker->probeResolutions();
|
||||
needsInit = false;
|
||||
}
|
||||
|
||||
if (refcount <= 0)
|
||||
worker->resume();
|
||||
|
||||
refcount++;
|
||||
}
|
||||
|
||||
void Camera::unsubscribe()
|
||||
{
|
||||
refcount--;
|
||||
|
||||
if (refcount <= 0)
|
||||
{
|
||||
worker->suspend();
|
||||
refcount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
vpx_image Camera::getLastVPXImage()
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
|
||||
vpx_image img;
|
||||
|
||||
if (currFrame.isNull())
|
||||
{
|
||||
img.w = 0;
|
||||
img.h = 0;
|
||||
return img;
|
||||
}
|
||||
|
||||
const int w = currFrame.resolution.width();
|
||||
const int h = currFrame.resolution.height();
|
||||
|
||||
// I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes."
|
||||
// http://fourcc.org/yuv.php#IYUV
|
||||
vpx_img_alloc(&img, VPX_IMG_FMT_VPXI420, w, h, 1);
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
uint8_t b = currFrame.frameData.data()[(x + y * w) * 3 + 0];
|
||||
uint8_t g = currFrame.frameData.data()[(x + y * w) * 3 + 1];
|
||||
uint8_t r = currFrame.frameData.data()[(x + y * w) * 3 + 2];
|
||||
|
||||
img.planes[VPX_PLANE_Y][x + y * img.stride[VPX_PLANE_Y]] = ((66 * r + 129 * g + 25 * b) >> 8) + 16;
|
||||
|
||||
if (!(x % 2) && !(y % 2))
|
||||
{
|
||||
const int i = x / 2;
|
||||
const int j = y / 2;
|
||||
|
||||
img.planes[VPX_PLANE_U][i + j * img.stride[VPX_PLANE_U]] = ((112 * r + -94 * g + -18 * b) >> 8) + 128;
|
||||
img.planes[VPX_PLANE_V][i + j * img.stride[VPX_PLANE_V]] = ((-38 * r + -74 * g + 112 * b) >> 8) + 128;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(const VideoFrame frame)
|
||||
{
|
||||
emit frameAvailable(frame);
|
||||
|
||||
mutex.lock();
|
||||
currFrame = frame;
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void Camera::onResProbingFinished(QList<QSize> res)
|
||||
{
|
||||
resolutions = res;
|
||||
}
|
||||
|
||||
Camera* Camera::getInstance()
|
||||
{
|
||||
if (!instance)
|
||||
instance = new Camera();
|
||||
|
||||
return instance;
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
|
||||
#include "core.h"
|
||||
#include "camera.h"
|
||||
#include "video/camera.h"
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
|
||||
|
@ -241,7 +241,7 @@ void Core::sendCallVideo(int callId)
|
|||
if (!calls[callId].active || !calls[callId].videoEnabled)
|
||||
return;
|
||||
|
||||
vpx_image frame = camera->getLastVPXImage();
|
||||
vpx_image frame = camera->getLastFrame().createVpxImage();
|
||||
if (frame.w && frame.h)
|
||||
{
|
||||
int result;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#define COREAV_H
|
||||
|
||||
#include <tox/toxav.h>
|
||||
#include "netvideosource.h"
|
||||
#include "video/netvideosource.h"
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#include <OpenAL/al.h>
|
||||
|
|
115
src/video/camera.cpp
Normal file
115
src/video/camera.cpp
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
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 "camera.h"
|
||||
#include "src/video/cameraworker.h"
|
||||
#include <QDebug>
|
||||
#include <QThread>
|
||||
#include <QMutexLocker>
|
||||
|
||||
Camera* Camera::getInstance()
|
||||
{
|
||||
static Camera instance;
|
||||
|
||||
return &instance;
|
||||
}
|
||||
|
||||
Camera::Camera()
|
||||
: 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(worker, &CameraWorker::newFrameAvailable, this, &Camera::onNewFrameAvailable);
|
||||
|
||||
connect(worker, &CameraWorker::resProbingFinished, this, &Camera::resolutionProbingFinished);
|
||||
connect(worker, &CameraWorker::propProbingFinished, this, [=](int prop, double val) { emit propProbingFinished(Prop(prop), val); } );
|
||||
|
||||
workerThread->start();
|
||||
}
|
||||
|
||||
Camera::~Camera()
|
||||
{
|
||||
workerThread->exit();
|
||||
workerThread->deleteLater();
|
||||
}
|
||||
|
||||
void Camera::subscribe()
|
||||
{
|
||||
if (refcount++ <= 0)
|
||||
worker->resume();
|
||||
}
|
||||
|
||||
void Camera::unsubscribe()
|
||||
{
|
||||
if (--refcount <= 0)
|
||||
{
|
||||
worker->suspend();
|
||||
refcount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::probeProp(Camera::Prop prop)
|
||||
{
|
||||
worker->probeProp(int(prop));
|
||||
}
|
||||
|
||||
void Camera::probeResolutions()
|
||||
{
|
||||
worker->probeResolutions();
|
||||
}
|
||||
|
||||
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::getCurrentResolution()
|
||||
{
|
||||
return QSize(worker->getProp(CV_CAP_PROP_FRAME_WIDTH), worker->getProp(CV_CAP_PROP_FRAME_HEIGHT));
|
||||
}
|
||||
|
||||
void Camera::setProp(Camera::Prop prop, double val)
|
||||
{
|
||||
worker->setProp(int(prop), val);
|
||||
}
|
||||
|
||||
double Camera::getProp(Camera::Prop prop)
|
||||
{
|
||||
return worker->getProp(int(prop));
|
||||
}
|
||||
|
||||
void Camera::onNewFrameAvailable(const VideoFrame frame)
|
||||
{
|
||||
emit frameAvailable(frame);
|
||||
|
||||
mutex.lock();
|
||||
currFrame = frame;
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
VideoFrame Camera::getLastFrame()
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
return currFrame;
|
||||
}
|
|
@ -22,7 +22,7 @@
|
|||
#include <QMutex>
|
||||
#include "vpx/vpx_image.h"
|
||||
#include "opencv2/opencv.hpp"
|
||||
#include "src/videosource.h"
|
||||
#include "src/video/videosource.h"
|
||||
|
||||
class CameraWorker;
|
||||
|
||||
|
@ -36,31 +36,37 @@ class Camera : public VideoSource
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Prop {
|
||||
BRIGHTNESS,
|
||||
SATURATION,
|
||||
CONTRAST,
|
||||
HUE,
|
||||
enum Prop : int {
|
||||
BRIGHTNESS = CV_CAP_PROP_BRIGHTNESS,
|
||||
SATURATION = CV_CAP_PROP_SATURATION,
|
||||
CONTRAST = CV_CAP_PROP_CONTRAST,
|
||||
HUE = CV_CAP_PROP_HUE,
|
||||
WIDTH = CV_CAP_PROP_FRAME_WIDTH,
|
||||
HEIGHT = CV_CAP_PROP_FRAME_HEIGHT,
|
||||
};
|
||||
|
||||
~Camera();
|
||||
|
||||
static Camera* getInstance(); ///< Returns the global widget's Camera instance
|
||||
vpx_image getLastVPXImage(); ///< Convert the last frame to a vpx_image (can be expensive !)
|
||||
|
||||
QList<QSize> getSupportedResolutions();
|
||||
QSize getBestVideoMode();
|
||||
VideoFrame getLastFrame();
|
||||
|
||||
void setResolution(QSize res);
|
||||
QSize getResolution();
|
||||
QSize getCurrentResolution();
|
||||
|
||||
void setProp(Prop prop, double val);
|
||||
double getProp(Prop prop);
|
||||
|
||||
void probeProp(Prop prop);
|
||||
void probeResolutions();
|
||||
|
||||
// VideoSource interface
|
||||
virtual void subscribe();
|
||||
virtual void unsubscribe();
|
||||
|
||||
signals:
|
||||
void resolutionProbingFinished(QList<QSize> res);
|
||||
void propProbingFinished(Prop prop, double val);
|
||||
|
||||
protected:
|
||||
Camera();
|
||||
|
||||
|
@ -72,14 +78,8 @@ private:
|
|||
QThread* workerThread;
|
||||
CameraWorker* worker;
|
||||
|
||||
QList<QSize> resolutions;
|
||||
|
||||
static Camera* instance;
|
||||
bool needsInit;
|
||||
|
||||
private slots:
|
||||
void onNewFrameAvailable(const VideoFrame frame);
|
||||
void onResProbingFinished(QList<QSize> res);
|
||||
|
||||
};
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
#include <QCoreApplication>
|
||||
#include <QThread>
|
||||
|
||||
CameraWorker::CameraWorker(int index)
|
||||
: clock(nullptr)
|
||||
|
@ -26,6 +26,7 @@ CameraWorker::CameraWorker(int index)
|
|||
, refCount(0)
|
||||
{
|
||||
qRegisterMetaType<VideoFrame>();
|
||||
qRegisterMetaType<QList<QSize>>();
|
||||
}
|
||||
|
||||
void CameraWorker::onStart()
|
||||
|
@ -67,13 +68,14 @@ double CameraWorker::_getProp(int prop)
|
|||
{
|
||||
subscribe();
|
||||
props[prop] = cam.get(prop);
|
||||
emit propProbingFinished(prop, props[prop]);
|
||||
unsubscribe();
|
||||
}
|
||||
|
||||
return props.value(prop);
|
||||
}
|
||||
|
||||
void CameraWorker::probeResolutions()
|
||||
void CameraWorker::_probeResolutions()
|
||||
{
|
||||
if (resolutions.isEmpty())
|
||||
{
|
||||
|
@ -115,9 +117,9 @@ void CameraWorker::probeResolutions()
|
|||
}
|
||||
|
||||
unsubscribe();
|
||||
}
|
||||
|
||||
qDebug() << "Camera resolutions:" << resolutions;
|
||||
qDebug() << "CameraWorker: Resolutions" <<resolutions;
|
||||
}
|
||||
|
||||
emit resProbingFinished(resolutions);
|
||||
}
|
||||
|
@ -133,7 +135,7 @@ void CameraWorker::applyProps()
|
|||
|
||||
void CameraWorker::subscribe()
|
||||
{
|
||||
if (refCount == 0)
|
||||
if (refCount++ == 0)
|
||||
{
|
||||
if (!cam.isOpened())
|
||||
{
|
||||
|
@ -142,15 +144,11 @@ void CameraWorker::subscribe()
|
|||
applyProps(); // restore props
|
||||
}
|
||||
}
|
||||
|
||||
refCount++;
|
||||
}
|
||||
|
||||
void CameraWorker::unsubscribe()
|
||||
{
|
||||
refCount--;
|
||||
|
||||
if(refCount <= 0)
|
||||
if(--refCount <= 0)
|
||||
{
|
||||
cam.release();
|
||||
refCount = 0;
|
||||
|
@ -188,10 +186,19 @@ void CameraWorker::setProp(int prop, double val)
|
|||
QMetaObject::invokeMethod(this, "_setProp", Q_ARG(int, prop), Q_ARG(double, val));
|
||||
}
|
||||
|
||||
void CameraWorker::probeProp(int prop)
|
||||
{
|
||||
QMetaObject::invokeMethod(this, "_getProp", Q_ARG(int, prop));
|
||||
}
|
||||
|
||||
void CameraWorker::probeResolutions()
|
||||
{
|
||||
QMetaObject::invokeMethod(this, "_probeResolutions");
|
||||
}
|
||||
|
||||
double CameraWorker::getProp(int prop)
|
||||
{
|
||||
double ret = 0.0;
|
||||
qApp->processEvents();
|
||||
QMetaObject::invokeMethod(this, "_getProp", Qt::BlockingQueuedConnection, Q_RETURN_ARG(double, ret), Q_ARG(int, prop));
|
||||
|
||||
return ret;
|
|
@ -40,21 +40,24 @@ public:
|
|||
void resume();
|
||||
void setProp(int prop, double val);
|
||||
double getProp(int prop); // blocking call!
|
||||
void probeResolutions();
|
||||
|
||||
public slots:
|
||||
void onStart();
|
||||
void probeProp(int prop);
|
||||
void probeResolutions();
|
||||
|
||||
signals:
|
||||
void started();
|
||||
void newFrameAvailable(const VideoFrame frame);
|
||||
void resProbingFinished(QList<QSize> res);
|
||||
void propProbingFinished(int prop, double val);
|
||||
|
||||
private slots:
|
||||
void _suspend();
|
||||
void _resume();
|
||||
void _setProp(int prop, double val);
|
||||
double _getProp(int prop);
|
||||
void _probeResolutions();
|
||||
|
||||
private:
|
||||
void applyProps();
|
|
@ -51,8 +51,8 @@ void NetVideoSource::pushVPXFrame(vpx_image *image)
|
|||
for (int x = 0; x < dw; ++x)
|
||||
{
|
||||
uint8_t Y = yData[x + y * bpl];
|
||||
uint8_t U = uData[x/2 + y/2*cxbpl];
|
||||
uint8_t V = vData[x/2 + y/2*cxbpl];
|
||||
uint8_t U = uData[x/(1 << image->x_chroma_shift) + y/(1 << image->y_chroma_shift)*cxbpl];
|
||||
uint8_t V = vData[x/(1 << image->x_chroma_shift) + y/(1 << image->y_chroma_shift)*cxbpl];
|
||||
|
||||
frame.frameData.data()[dw * 3 * y + x * 3 + 0] = Y;
|
||||
frame.frameData.data()[dw * 3 * y + x * 3 + 1] = U;
|
57
src/video/videoframe.cpp
Normal file
57
src/video/videoframe.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
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 "videoframe.h"
|
||||
|
||||
vpx_image_t VideoFrame::createVpxImage() const
|
||||
{
|
||||
vpx_image img;
|
||||
img.w = 0;
|
||||
img.h = 0;
|
||||
|
||||
if (!isValid())
|
||||
return img;
|
||||
|
||||
const int w = resolution.width();
|
||||
const int h = resolution.height();
|
||||
|
||||
// I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes."
|
||||
// http://fourcc.org/yuv.php#IYUV
|
||||
vpx_img_alloc(&img, VPX_IMG_FMT_VPXI420, w, h, 1);
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
uint8_t b = frameData.data()[(x + y * w) * 3 + 0];
|
||||
uint8_t g = frameData.data()[(x + y * w) * 3 + 1];
|
||||
uint8_t r = frameData.data()[(x + y * w) * 3 + 2];
|
||||
|
||||
img.planes[VPX_PLANE_Y][x + y * img.stride[VPX_PLANE_Y]] = ((66 * r + 129 * g + 25 * b) >> 8) + 16;
|
||||
|
||||
if (!(x % (1 << img.x_chroma_shift)) && !(y % (1 << img.y_chroma_shift)))
|
||||
{
|
||||
const int i = x / (1 << img.x_chroma_shift);
|
||||
const int j = y / (1 << img.y_chroma_shift);
|
||||
|
||||
img.planes[VPX_PLANE_U][i + j * img.stride[VPX_PLANE_U]] = ((112 * r + -94 * g + -18 * b) >> 8) + 128;
|
||||
img.planes[VPX_PLANE_V][i + j * img.stride[VPX_PLANE_V]] = ((-38 * r + -74 * g + 112 * b) >> 8) + 128;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
58
src/video/videoframe.h
Normal file
58
src/video/videoframe.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
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 VIDEOFRAME_H
|
||||
#define VIDEOFRAME_H
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QByteArray>
|
||||
#include <QSize>
|
||||
|
||||
#include "vpx/vpx_image.h"
|
||||
|
||||
struct VideoFrame
|
||||
{
|
||||
enum ColorFormat
|
||||
{
|
||||
NONE,
|
||||
BGR,
|
||||
YUV,
|
||||
};
|
||||
|
||||
QByteArray frameData;
|
||||
QSize resolution;
|
||||
ColorFormat format;
|
||||
|
||||
VideoFrame() : format(NONE) {}
|
||||
VideoFrame(QByteArray d, QSize r, ColorFormat f) : frameData(d), resolution(r), format(f) {}
|
||||
|
||||
void invalidate()
|
||||
{
|
||||
frameData = QByteArray();
|
||||
resolution = QSize(-1,-1);
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return !frameData.isEmpty() && resolution.isValid() && format != NONE;
|
||||
}
|
||||
|
||||
vpx_image_t createVpxImage() const;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(VideoFrame)
|
||||
|
||||
#endif // VIDEOFRAME_H
|
39
src/video/videosource.h
Normal file
39
src/video/videosource.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
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 VIDEOSOURCE_H
|
||||
#define VIDEOSOURCE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSize>
|
||||
#include <QRgb>
|
||||
|
||||
#include "videoframe.h"
|
||||
|
||||
class VideoSource : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
virtual void subscribe() = 0;
|
||||
virtual void unsubscribe() = 0;
|
||||
|
||||
signals:
|
||||
void frameAvailable(const VideoFrame frame);
|
||||
|
||||
};
|
||||
|
||||
#endif // VIDEOSOURCE_H
|
|
@ -1,60 +0,0 @@
|
|||
#ifndef VIDEOSOURCE_H
|
||||
#define VIDEOSOURCE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSize>
|
||||
#include <QRgb>
|
||||
|
||||
struct VideoFrame
|
||||
{
|
||||
enum ColorFormat
|
||||
{
|
||||
NONE,
|
||||
BGR,
|
||||
YUV,
|
||||
};
|
||||
|
||||
QByteArray frameData;
|
||||
QSize resolution;
|
||||
ColorFormat format;
|
||||
|
||||
VideoFrame() : format(NONE) {}
|
||||
VideoFrame(QByteArray d, QSize r, ColorFormat f) : frameData(d), resolution(r), format(f) {}
|
||||
|
||||
void setNull()
|
||||
{
|
||||
frameData = QByteArray();
|
||||
}
|
||||
|
||||
bool isNull()
|
||||
{
|
||||
return frameData.isEmpty();
|
||||
}
|
||||
|
||||
// assumes format is BGR
|
||||
QRgb getPixel(int x, int y)
|
||||
{
|
||||
char b = frameData.data()[resolution.width() * 3 * y + x * 3 + 0];
|
||||
char g = frameData.data()[resolution.width() * 3 * y + x * 3 + 1];
|
||||
char r = frameData.data()[resolution.width() * 3 * y + x * 3 + 2];
|
||||
|
||||
return qRgb(r, g, b);
|
||||
}
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(VideoFrame)
|
||||
|
||||
class VideoSource : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
virtual void subscribe() = 0;
|
||||
virtual void unsubscribe() = 0;
|
||||
|
||||
signals:
|
||||
void frameAvailable(const VideoFrame frame);
|
||||
|
||||
};
|
||||
|
||||
#endif // VIDEOSOURCE_H
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
|
||||
#include "avform.h"
|
||||
#include "src/camera.h"
|
||||
#include "ui_avsettings.h"
|
||||
|
||||
AVForm::AVForm() :
|
||||
|
@ -23,6 +22,9 @@ AVForm::AVForm() :
|
|||
{
|
||||
bodyUI = new Ui::AVSettings;
|
||||
bodyUI->setupUi(this);
|
||||
|
||||
connect(Camera::getInstance(), &Camera::propProbingFinished, this, &AVForm::onPropProbingFinished);
|
||||
connect(Camera::getInstance(), &Camera::resolutionProbingFinished, this, &AVForm::onResProbingFinished);
|
||||
}
|
||||
|
||||
AVForm::~AVForm()
|
||||
|
@ -34,15 +36,12 @@ void AVForm::present()
|
|||
{
|
||||
bodyUI->CamVideoSurface->setSource(Camera::getInstance());
|
||||
|
||||
bodyUI->videoModescomboBox->clear();
|
||||
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())));
|
||||
Camera::getInstance()->probeProp(Camera::SATURATION);
|
||||
Camera::getInstance()->probeProp(Camera::CONTRAST);
|
||||
Camera::getInstance()->probeProp(Camera::BRIGHTNESS);
|
||||
Camera::getInstance()->probeProp(Camera::HUE);
|
||||
|
||||
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);
|
||||
Camera::getInstance()->probeResolutions();
|
||||
}
|
||||
|
||||
void AVForm::on_ContrastSlider_sliderMoved(int position)
|
||||
|
@ -65,13 +64,39 @@ void AVForm::on_HueSlider_sliderMoved(int position)
|
|||
Camera::getInstance()->setProp(Camera::HUE, position / 100.0);
|
||||
}
|
||||
|
||||
void AVForm::on_videoModescomboBox_currentIndexChanged(const QString &arg1)
|
||||
void AVForm::on_videoModescomboBox_activated(int index)
|
||||
{
|
||||
QStringList resStr = arg1.split("x");
|
||||
int w = resStr[0].toInt();
|
||||
int h = resStr[0].toInt();
|
||||
Camera::getInstance()->setResolution(bodyUI->videoModescomboBox->itemData(index).toSize());
|
||||
}
|
||||
|
||||
Camera::getInstance()->setResolution(QSize(w,h));
|
||||
void AVForm::onPropProbingFinished(Camera::Prop prop, double val)
|
||||
{
|
||||
switch (prop)
|
||||
{
|
||||
case Camera::BRIGHTNESS:
|
||||
bodyUI->BrightnessSlider->setValue(val*100);
|
||||
break;
|
||||
case Camera::CONTRAST:
|
||||
bodyUI->ContrastSlider->setValue(val*100);
|
||||
break;
|
||||
case Camera::SATURATION:
|
||||
bodyUI->SaturationSlider->setValue(val*100);
|
||||
break;
|
||||
case Camera::HUE:
|
||||
bodyUI->HueSlider->setValue(val*100);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AVForm::onResProbingFinished(QList<QSize> res)
|
||||
{
|
||||
bodyUI->videoModescomboBox->clear();
|
||||
for (QSize r : res)
|
||||
bodyUI->videoModescomboBox->addItem(QString("%1x%2").arg(QString::number(r.width()),QString::number(r.height())), r);
|
||||
|
||||
bodyUI->videoModescomboBox->setCurrentIndex(bodyUI->videoModescomboBox->count()-1);
|
||||
}
|
||||
|
||||
void AVForm::hideEvent(QHideEvent *)
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "genericsettings.h"
|
||||
#include "src/widget/videosurface.h"
|
||||
#include "src/video/camera.h"
|
||||
#include <QGroupBox>
|
||||
#include <QVBoxLayout>
|
||||
#include <QPushButton>
|
||||
|
@ -43,7 +44,11 @@ private slots:
|
|||
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);
|
||||
void on_videoModescomboBox_activated(int index);
|
||||
|
||||
// camera
|
||||
void onPropProbingFinished(Camera::Prop prop, double val);
|
||||
void onResProbingFinished(QList<QSize> res);
|
||||
|
||||
virtual void hideEvent(QHideEvent*);
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include "settingswidget.h"
|
||||
#include "src/widget/widget.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include "src/camera.h"
|
||||
#include "src/video/camera.h"
|
||||
#include "src/widget/form/settings/generalform.h"
|
||||
#include "src/widget/form/settings/identityform.h"
|
||||
#include "src/widget/form/settings/privacyform.h"
|
||||
|
|
|
@ -15,13 +15,11 @@
|
|||
*/
|
||||
|
||||
#include "videosurface.h"
|
||||
#include "src/camera.h"
|
||||
#include "src/video/camera.h"
|
||||
#include <QTimer>
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QDebug>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
VideoSurface::VideoSurface(QWidget* parent)
|
||||
: QGLWidget(QGLFormat(QGL::SampleBuffers | QGL::SingleBuffer), parent)
|
||||
|
@ -128,12 +126,17 @@ void VideoSurface::paintGL()
|
|||
{
|
||||
mutex.lock();
|
||||
VideoFrame currFrame = frame;
|
||||
frame.invalidate();
|
||||
mutex.unlock();
|
||||
|
||||
if (res != currFrame.resolution)
|
||||
if (currFrame.isValid() && res != currFrame.resolution)
|
||||
{
|
||||
res = currFrame.resolution;
|
||||
|
||||
// delete old texture
|
||||
if (textureId != 0)
|
||||
glDeleteTextures(1, &textureId);
|
||||
|
||||
// a texture used to render the pbo (has the match the pixelformat of the source)
|
||||
glGenTextures(1,&textureId);
|
||||
glBindTexture(GL_TEXTURE_2D, textureId);
|
||||
|
@ -141,14 +144,14 @@ void VideoSurface::paintGL()
|
|||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
}
|
||||
|
||||
if (!currFrame.isNull())
|
||||
if (currFrame.isValid())
|
||||
{
|
||||
pboIndex = (pboIndex + 1) % 2;
|
||||
int nextPboIndex = (pboIndex + 1) % 2;
|
||||
|
||||
if (pboAllocSize != currFrame.frameData.size())
|
||||
{
|
||||
qDebug() << "VideoSurface: Resize pbo " << currFrame.frameData.size() << "bytes (before" << pboAllocSize << ")";
|
||||
qDebug() << "VideoSurface: Resize pbo " << currFrame.frameData.size() << "(" << currFrame.resolution << ")" << "bytes (before" << pboAllocSize << ")";
|
||||
|
||||
pbo[0]->bind();
|
||||
pbo[0]->allocate(currFrame.frameData.size());
|
||||
|
@ -175,10 +178,6 @@ void VideoSurface::paintGL()
|
|||
memcpy(ptr, currFrame.frameData.data(), currFrame.frameData.size());
|
||||
pbo[nextPboIndex]->unmap();
|
||||
pbo[nextPboIndex]->release();
|
||||
|
||||
mutex.lock();
|
||||
frame.setNull();
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
// render pbo
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include <QGLWidget>
|
||||
#include <QMutex>
|
||||
#include "src/videosource.h"
|
||||
#include "src/video/videosource.h"
|
||||
|
||||
class QOpenGLBuffer;
|
||||
class QOpenGLShaderProgram;
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include "form/groupchatform.h"
|
||||
#include "src/misc/style.h"
|
||||
#include "friendlistwidget.h"
|
||||
#include "src/camera.h"
|
||||
#include "src/video/camera.h"
|
||||
#include "form/chatform.h"
|
||||
#include "maskablepixmapwidget.h"
|
||||
#include <QMessageBox>
|
||||
|
|
Loading…
Reference in New Issue
Block a user