diff --git a/cameraworker.cpp b/cameraworker.cpp new file mode 100644 index 000000000..b0c964bc5 --- /dev/null +++ b/cameraworker.cpp @@ -0,0 +1,205 @@ +/* + Copyright (C) 2014 by Project Tox + + 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 +#include + +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 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; +} diff --git a/cameraworker.h b/cameraworker.h new file mode 100644 index 000000000..47fb10f87 --- /dev/null +++ b/cameraworker.h @@ -0,0 +1,77 @@ +/* + Copyright (C) 2014 by Project Tox + + 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 +#include +#include +#include +#include +#include + +#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 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 queue; + QTimer* clock; + cv::VideoCapture cam; + cv::Mat3b frame; + int camIndex; + QMap props; + QList resolutions; + int refCount; +}; + +#endif // CAMERAWORKER_H diff --git a/coreav.cpp b/coreav.cpp index fa4ce5cd8..b7aae0103 100644 --- a/coreav.cpp +++ b/coreav.cpp @@ -57,7 +57,7 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled if (calls[callId].videoEnabled) { 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].sendVideoTimer->stop(); - Camera::getInstance()->unsuscribe(); + Camera::getInstance()->unsubscribe(); emit ((Core*)core)->avMediaChange(friendId, callId, false); } else { - Camera::getInstance()->suscribe(); + Camera::getInstance()->subscribe(); calls[callId].videoEnabled = true; calls[callId].sendVideoTimer->start(); emit ((Core*)core)->avMediaChange(friendId, callId, true); @@ -161,7 +161,7 @@ void Core::cleanupCall(int callId) calls[callId].sendAudioTimer->stop(); calls[callId].sendVideoTimer->stop(); if (calls[callId].videoEnabled) - Camera::getInstance()->unsuscribe(); + Camera::getInstance()->unsubscribe(); alcCaptureStop(alInDev); } diff --git a/main.cpp b/main.cpp index e21c1c647..1d447dbf9 100644 --- a/main.cpp +++ b/main.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include int main(int argc, char *argv[]) @@ -47,7 +48,24 @@ int main(int argc, char *argv[]) QFontDatabase::addApplicationFont("://DejaVuSans.ttf"); Widget* w = Widget::getInstance(); - w->show(); + if (QSystemTrayIcon::isSystemTrayAvailable() == false) + { + qWarning() << "No system tray detected!"; + w->show(); + } + else + { + QSystemTrayIcon *icon = new QSystemTrayIcon(w); + QObject::connect(icon, + SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + w, + SLOT(onIconClick())); + icon->setIcon(w->windowIcon()); + icon->show(); + if(Settings::getInstance().getAutostartInTray() == false) + w->show(); + } + int errorcode = a.exec(); diff --git a/misc/settings.cpp b/misc/settings.cpp index aa8aa4047..e06c0d997 100644 --- a/misc/settings.cpp +++ b/misc/settings.cpp @@ -1,8 +1,8 @@ /* Copyright (C) 2013 by Maxim Biro - + This file is part of Tox Qt GUI. - + This program is free 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 @@ -10,7 +10,7 @@ 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. */ @@ -110,6 +110,7 @@ void Settings::load() enableIPv6 = s.value("enableIPv6", true).toBool(); useTranslations = s.value("useTranslations", true).toBool(); makeToxPortable = s.value("makeToxPortable", false).toBool(); + autostartInTray = s.value("autostartInTray", false).toBool(); forceTCP = s.value("forceTCP", false).toBool(); useProxy = s.value("useProxy", false).toBool(); proxyAddr = s.value("proxyAddr", "").toString(); @@ -214,6 +215,7 @@ void Settings::save(QString path) s.setValue("enableIPv6", enableIPv6); s.setValue("useTranslations",useTranslations); s.setValue("makeToxPortable",makeToxPortable); + s.setValue("autostartInTray",autostartInTray); s.setValue("useProxy", useProxy); s.setValue("forceTCP", forceTCP); s.setValue("proxyAddr", proxyAddr); @@ -351,14 +353,14 @@ void Settings::setMakeToxPortable(bool newValue) save(); } -QString Settings::getCurrentProfile() const +bool Settings::getAutostartInTray() const { - return currentProfile; + return autostartInTray; } -void Settings::setCurrentProfile(QString profile) +void Settings::setAutostartInTray(bool newValue) { - currentProfile = profile; + autostartInTray = newValue; } bool Settings::getUseTranslations() const @@ -410,6 +412,16 @@ void Settings::setProxyPort(int newValue) proxyPort = newValue; } +QString Settings::getCurrentProfile() const +{ + return currentProfile; +} + +void Settings::setCurrentProfile(QString profile) +{ + currentProfile = profile; +} + bool Settings::getEnableLogging() const { return enableLogging; diff --git a/misc/settings.h b/misc/settings.h index a7eeb9d8b..cf88bde31 100644 --- a/misc/settings.h +++ b/misc/settings.h @@ -1,8 +1,8 @@ /* Copyright (C) 2013 by Maxim Biro - + This file is part of Tox Qt GUI. - + This program is free 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 @@ -10,7 +10,7 @@ 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. */ @@ -49,6 +49,9 @@ public: bool getMakeToxPortable() const; void setMakeToxPortable(bool newValue); + bool getAutostartInTray() const; + void setAutostartInTray(bool newValue); + QString getCurrentProfile() const; void setCurrentProfile(QString profile); @@ -57,7 +60,7 @@ public: bool getForceTCP() const; void setForceTCP(bool newValue); - + QString getProxyAddr() const; void setProxyAddr(const QString& newValue); @@ -168,7 +171,8 @@ private: bool enableIPv6; bool useTranslations; static bool makeToxPortable; - + bool autostartInTray; + bool forceTCP; bool useProxy; diff --git a/qtox.pro b/qtox.pro index 14e233ad1..d6f0e1969 100644 --- a/qtox.pro +++ b/qtox.pro @@ -20,7 +20,7 @@ # 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 TARGET = qtox @@ -68,7 +68,7 @@ win32 { LIBS += -Wl,-Bdynamic -ltbb -lv4l1 -lv4l2 -lgnutls -lrtmp -lgnutls -lavformat -lavcodec -lavutil -lavfilter -lswscale -lusb-1.0 } 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) { @@ -111,7 +111,6 @@ HEADERS += widget/form/addfriendform.h \ friendlist.h \ misc/cdata.h \ misc/cstring.h \ - widget/selfcamview.h \ widget/camera.h \ widget/netcamview.h \ misc/smileypack.h \ @@ -132,7 +131,10 @@ HEADERS += widget/form/addfriendform.h \ widget/tool/chatactions/filetransferaction.h \ widget/tool/chatactions/systemmessageaction.h \ widget/tool/chatactions/actionaction.h \ - widget/maskablepixmapwidget.h + widget/maskablepixmapwidget.h \ + videosource.h \ + cameraworker.h \ + widget/videosurface.h SOURCES += \ widget/form/addfriendform.cpp \ @@ -158,7 +160,6 @@ SOURCES += \ misc/settings.cpp \ misc/cdata.cpp \ misc/cstring.cpp \ - widget/selfcamview.cpp \ widget/camera.cpp \ widget/netcamview.cpp \ misc/smileypack.cpp \ @@ -178,4 +179,6 @@ SOURCES += \ widget/tool/chatactions/filetransferaction.cpp \ widget/tool/chatactions/systemmessageaction.cpp \ widget/tool/chatactions/actionaction.cpp \ - widget/maskablepixmapwidget.cpp + widget/maskablepixmapwidget.cpp \ + cameraworker.cpp \ + widget/videosurface.cpp diff --git a/translations/uk.qm b/translations/uk.qm index 53c004e9b..80b128b79 100644 Binary files a/translations/uk.qm and b/translations/uk.qm differ diff --git a/translations/uk.ts b/translations/uk.ts index 70d143cdc..79d617223 100644 --- a/translations/uk.ts +++ b/translations/uk.ts @@ -1,26 +1,60 @@ - + + + AVForm + + + Audio/Video settings + Параметри аудіо/відео + + + + Hide video preview + On a button + Приховати вікно перегляду + + + + Show video preview + On a button + Показати вікно перегляду + + AVPage - Video Settings + Параметри відео + + + Show video preview + On a button + Показати вікно попереднього перегляду + + + Hide video preview + On a button + Приховати вікно попереднього перегляду + + + + AVSettings + + + Form + Form + + + + Video settings Параметри відео - - + Show video preview - On a button Показати вікно попереднього перегляду - - - Hide video preview - On a button - Приховати вікно попереднього перегляду - AddFriendForm @@ -105,27 +139,42 @@ ChatForm - + Send a file Надіслати файл + + Core + + + Encrypted profile + Зашифрований профіль + + + + Your tox profile seems to be encrypted, qTox can't open it +Do you want to erase this profile ? + Схоже, що ваш tox-профіль зашифровано, qTox не може його відкрити. +Бажаєте стерти цей профіль? + + FileTransferInstance - + Save a file Title of the file saving dialog Зберегти файл - + Location not writable Title of permissions popup Немає прав на запис - + You do not have permission to write that location. Choose another, or cancel the save dialog. text of permissions popup Ви не маєте прав на запис за цим розташуванням. Оберіть інше місце призначення, або скасуйте передачу. @@ -189,71 +238,153 @@ FriendWidget - + Copy friend ID Menu to copy the Tox ID of that friend Копіювати дружній ID - + Invite in group Menu to invite a friend in a groupchat Запросити до групи - + Remove friend Menu to remove the friend from our friendlist Вилучити з друзів + + GeneralForm + + + General Settings + Основні параметри + + GeneralPage - + General Settings + Основні параметри + + + Enable IPv6 (recommended) + Text on a checkbox to enable IPv6 + Дозволити IPv6 (рекомендовано) + + + Use translations + Text on a checkbox to enable translations + Використовувати мову системи + + + Make Tox portable + Text on a checkbox to make qTox a portable application + Портативний запуск + + + Save settings to the working directory instead of the usual conf dir + describes makeToxPortable checkbox + Зберігати налаштування в робочий теці + + + Theme + Графічна тема + + + Smiley Pack + Графічний пакунок емоційних картинок + + + + GeneralSettings + + + Form + Form + + + General Settings Основні параметри - - Enable IPv6 (recommended) - Text on a checkbox to enable IPv6 - Дозволити IPv6 (рекомендовано) - - - + Use translations - Text on a checkbox to enable translations + Text on a checkbox to enable translations Використовувати мову системи - - Make Tox portable - Text on a checkbox to make qTox a portable application - Портативний запуск - - - + Save settings to the working directory instead of the usual conf dir - describes makeToxPortable checkbox + describes makeToxPortable checkbox Зберігати налаштування в робочий теці - + + Make Tox portable + Портативний запуск + + + Theme Графічна тема - + Smiley Pack + Text on smiley pack label Графічний пакунок емоційних картинок + + + Connection Settings + Параметри підключення + + + + Enable IPv6 (recommended) + Text on a checkbox to enable IPv6 + Дозволити IPv6 (рекомендовано) + + + + This allows, e.g., toxing over Tor. It adds load to the Tox network however, so use only when necessary. + force tcp checkbox tooltip + Це дозволяє використовувати tox, наприклад, поверх протоколу Tor. Але це збульшує навантаження на мережу, тому використовуйте лише в разі необхідності. + + + + Disable UDP (not recommended) + Text on checkbox to disable UDP + Вимкнути IPv6 (не рекомендовано) + + + + Use proxy (SOCKS5) + Використовувати проксі (SOCKS5) + + + + Address + Text on proxy addr label + Адреса + + + + Port + Text on proxy port label + Порт + GenericChatForm - - + + Save chat log Зберегти чат @@ -261,18 +392,17 @@ GroupChatForm - + %1 users in chat Number of users in chat Користувачів у чаті: %1 - <Unknown> - <Невідомо> + <Невідомо> - + %1 users in chat Користувачів у чаті: %1 @@ -280,52 +410,88 @@ GroupWidget - - + + %1 users in chat Користувачів у чаті: %1 - - + + 0 users in chat Немає користувачів - + Quit group Menu to quit a groupchat Вийти з групи + + IdentityForm + + + Your identity + Ваш ідентифікатор + + IdentityPage - + Public Information + Публічна інформація + + + Name + Username/nick + Ім'я + + + Status + Status message + Статус + + + Tox ID + Tox ID + + + Your Tox ID + Ваш Tox ID + + + + IdentitySettings + + + Form + Form + + + Public Information Публічна інформація - + Name - Username/nick Ім'я - + Status - Status message Статус - + Tox ID Tox ID - - Your Tox ID - Ваш Tox ID + + Your Tox ID (click to copy) + Ваш Tox ID (клацніть аби скопіювати) @@ -336,46 +502,54 @@ qTox - + Your name Ваше ім'я - + Your status Ваш статус - + Add friends Додати друзів - + Create a group chat Створити груповий чат - + View completed file transfers Переглянути завершені передачі файлів - + Change your settings Змінити параметри - + Close Закрити - + Ctrl+Q Ctrl+Q + + PrivacyForm + + + Privacy settings + Параметри приватності + + SelfCamView @@ -388,95 +562,98 @@ SettingsDialog - qTox – Settings - qTox - Параметри + qTox - Параметри - General - Основні + Основні - Identity - Ідентифікація + Ідентифікація - Privacy - Приватність + Приватність - Audio/Video - Аудіо/Відео + Аудіо/Відео - Ok - Гаразд + Гаразд - Cancel - Скасувати + Скасувати - Apply - Застосувати + Застосувати Widget - + Online Button to set your status to 'Online' В мережі - + Away Button to set your status to 'Away' Відійшов - + Busy Button to set your status to 'Busy' Зайнятий - + Choose a profile picture Оберіть зображення для профілю - - - + + + Error Помилка - + Unable to open this file Неможливо відкрити цей файл - + Unable to read this image Неможливо прочитати це зображення - + This image is too big Зображення завелике - + + Toxcore failed to start, the application will terminate after you close this message. + Помилка запуску ядра tox, програма буде завершена після закриття цього повідомлення. + + + + toxcore failed to start with your proxy settings. qTox cannot run; please modify your settings and restart. + popup text + Помилка запуску ядра tox із поточними параметрами проксі. qTox не працює; змініть параметри і перезапустіть. + + + <Unknown> Placeholder when we don't know someone's name in a group chat <Невідомо> diff --git a/videosource.h b/videosource.h new file mode 100644 index 000000000..f6c11592c --- /dev/null +++ b/videosource.h @@ -0,0 +1,27 @@ +#ifndef VIDEOSOURCE_H +#define VIDEOSOURCE_H + +#include +#include + +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 diff --git a/widget/camera.cpp b/widget/camera.cpp index dab371005..a92e3d256 100644 --- a/widget/camera.cpp +++ b/widget/camera.cpp @@ -16,68 +16,72 @@ #include "camera.h" #include "widget.h" +#include "cameraworker.h" +#include +#include -using namespace cv; +Camera* Camera::instance = nullptr; 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) - { - refcount = 1; - cam.open(0); - } - else - refcount++; + worker->resume(); + + refcount++; } -void Camera::unsuscribe() +void Camera::unsubscribe() { refcount--; if (refcount <= 0) { - cam.release(); + worker->suspend(); 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() { - Mat3b frame = getLastFrame(); + lock(); 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 size_t i=0, j=0; for( int line = 0; line < h; ++line ) { - const cv::Vec3b *srcrow = frame[line]; + const cv::Vec3b *srcrow = currFrame[line]; if( !(line % 2) ) { for( int x = 0; x < w; x += 2 ) @@ -112,10 +116,123 @@ vpx_image Camera::getLastVPXImage() } } } + unlock(); return img; } +QList 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 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() { - return Widget::getInstance()->getCamera(); + if (!instance) + instance = new Camera(); + + return instance; } diff --git a/widget/camera.h b/widget/camera.h index e48307d41..5503f696c 100644 --- a/widget/camera.h +++ b/widget/camera.h @@ -18,8 +18,13 @@ #define CAMERA_H #include +#include +#include #include "vpx/vpx_image.h" #include "opencv2/opencv.hpp" +#include "videosource.h" + +class CameraWorker; /** * 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 **/ -class Camera +class Camera : public VideoSource { + Q_OBJECT public: - Camera(); + enum Prop { + BRIGHTNESS, + SATURATION, + CONTRAST, + HUE, + }; + + ~Camera(); + 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 !) + QList 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: 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 resolutions; + + static Camera* instance; + +private slots: + void onWorkerStarted(); + void onNewFrameAvailable(); + void onResProbingFinished(QList res); + }; #endif // CAMERA_H diff --git a/widget/form/settings/avform.cpp b/widget/form/settings/avform.cpp index 4b7afef25..4745bac2c 100644 --- a/widget/form/settings/avform.cpp +++ b/widget/form/settings/avform.cpp @@ -18,17 +18,16 @@ #include "widget/camera.h" #include "ui_avsettings.h" -AVForm::AVForm(Camera* cam) : +AVForm::AVForm() : GenericForm(tr("Audio/Video settings"), QPixmap(":/img/settings/av.png")) { bodyUI = new Ui::AVSettings; bodyUI->setupUi(this); - camView = new SelfCamView(cam, this); - bodyUI->videoGroup->layout()->addWidget(camView); - camView->hide(); // hide by default + //cam->setVideoMode(cam->getBestVideoMode()); + camView = new VideoSurface(Camera::getInstance(), this); - connect(bodyUI->testVideoBtn, &QPushButton::clicked, this, &AVForm::onTestVideoPressed); + bodyUI->CamViewLayout->addWidget(camView); } AVForm::~AVForm() @@ -36,22 +35,44 @@ AVForm::~AVForm() delete bodyUI; } -void AVForm::showTestVideo() +void AVForm::present() { - bodyUI->testVideoBtn->setText(tr("Hide video preview","On a button")); - camView->show(); + bodyUI->videoModescomboBox->clear(); + QList 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")); - camView->close(); + Camera::getInstance()->setProp(Camera::CONTRAST, position / 100.0); } -void AVForm::onTestVideoPressed() +void AVForm::on_SaturationSlider_sliderMoved(int position) { - if (camView->isVisible()) - closeTestVideo(); - else - showTestVideo(); + Camera::getInstance()->setProp(Camera::SATURATION, position / 100.0); +} + +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)); } diff --git a/widget/form/settings/avform.h b/widget/form/settings/avform.h index e75f742da..978506418 100644 --- a/widget/form/settings/avform.h +++ b/widget/form/settings/avform.h @@ -18,7 +18,7 @@ #define AVFORM_H #include "genericsettings.h" -#include "widget/selfcamview.h" +#include "widget/videosurface.h" #include #include #include @@ -33,20 +33,21 @@ class AVForm : public GenericForm { Q_OBJECT public: - AVForm(Camera* cam); + AVForm(); ~AVForm(); + virtual void present(); 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: Ui::AVSettings *bodyUI; - - SelfCamView* camView; - - void showTestVideo(); - void closeTestVideo(); - + VideoSurface* camView; }; #endif diff --git a/widget/form/settings/avsettings.ui b/widget/form/settings/avsettings.ui index 77fbb18b4..20ebbf43e 100644 --- a/widget/form/settings/avsettings.ui +++ b/widget/form/settings/avsettings.ui @@ -6,27 +6,143 @@ 0 0 - 400 - 300 + 394 + 391 Form + + + + Volume Settings (Stubs) + + + + + + Playback + + + + + + + Qt::Horizontal + + + + + + + Microphone + + + + + + + Qt::Horizontal + + + + + + Video settings - - - + + + QFormLayout::ExpandingFieldsGrow + + + - Show video preview + Modes + + + + + 0 + 0 + + + + + + + + Hue + + + + + + + Qt::Horizontal + + + + + + + Brightness + + + + + + + Qt::Horizontal + + + + + + + Saturation + + + + + + + Qt::Horizontal + + + + + + + Contrast + + + + + + + Qt::Horizontal + + + + + + + Preview + + + + + + @@ -38,7 +154,7 @@ 20 - 40 + 75 diff --git a/widget/form/settings/generalform.cpp b/widget/form/settings/generalform.cpp index b8138874e..0df370048 100644 --- a/widget/form/settings/generalform.cpp +++ b/widget/form/settings/generalform.cpp @@ -31,13 +31,14 @@ GeneralForm::GeneralForm() : bodyUI->cbEnableIPv6->setChecked(Settings::getInstance().getEnableIPv6()); bodyUI->cbUseTranslations->setChecked(Settings::getInstance().getUseTranslations()); bodyUI->cbMakeToxPortable->setChecked(Settings::getInstance().getMakeToxPortable()); + bodyUI->startInTray->setChecked(Settings::getInstance().getAutostartInTray()); for (auto entry : SmileyPack::listSmileyPacks()) { bodyUI->smileyPackBrowser->addItem(entry.first, entry.second); } bodyUI->smileyPackBrowser->setCurrentIndex(bodyUI->smileyPackBrowser->findData(Settings::getInstance().getSmileyPack())); - + bodyUI->cbUDPDisabled->setChecked(Settings::getInstance().getForceTCP()); bodyUI->proxyAddr->setText(Settings::getInstance().getProxyAddr()); int port = Settings::getInstance().getProxyPort(); @@ -46,10 +47,11 @@ GeneralForm::GeneralForm() : bodyUI->cbUseProxy->setChecked(Settings::getInstance().getUseProxy()); onUseProxyUpdated(); - + connect(bodyUI->cbEnableIPv6, &QCheckBox::stateChanged, this, &GeneralForm::onEnableIPv6Updated); connect(bodyUI->cbUseTranslations, &QCheckBox::stateChanged, this, &GeneralForm::onUseTranslationUpdated); connect(bodyUI->cbMakeToxPortable, &QCheckBox::stateChanged, this, &GeneralForm::onMakeToxPortableUpdated); + connect(bodyUI->startInTray, &QCheckBox::stateChanged, this, &GeneralForm::onSetAutostartInTray); connect(bodyUI->smileyPackBrowser, SIGNAL(currentIndexChanged(int)), this, SLOT(onSmileyBrowserIndexChanged(int))); // new syntax can't handle overloaded signals... (at least not in a pretty way) connect(bodyUI->cbUDPDisabled, &QCheckBox::stateChanged, this, &GeneralForm::onUDPUpdated); @@ -78,6 +80,11 @@ void GeneralForm::onMakeToxPortableUpdated() Settings::getInstance().setMakeToxPortable(bodyUI->cbMakeToxPortable->isChecked()); } +void GeneralForm::onSetAutostartInTray() +{ + Settings::getInstance().setAutostartInTray(bodyUI->startInTray->isChecked()); +} + void GeneralForm::onSmileyBrowserIndexChanged(int index) { QString filename = bodyUI->smileyPackBrowser->itemData(index).toString(); diff --git a/widget/form/settings/generalform.h b/widget/form/settings/generalform.h index ddfde9522..224c6ee26 100644 --- a/widget/form/settings/generalform.h +++ b/widget/form/settings/generalform.h @@ -36,6 +36,7 @@ private slots: void onEnableIPv6Updated(); void onUseTranslationUpdated(); void onMakeToxPortableUpdated(); + void onSetAutostartInTray(); void onSmileyBrowserIndexChanged(int index); void onUDPUpdated(); void onProxyAddrEdited(); diff --git a/widget/form/settings/generalsettings.ui b/widget/form/settings/generalsettings.ui index 653f15204..e0ffb3ab4 100644 --- a/widget/form/settings/generalsettings.ui +++ b/widget/form/settings/generalsettings.ui @@ -7,7 +7,7 @@ 0 0 527 - 367 + 369 @@ -46,6 +46,13 @@ + + + + Start in tray + + + diff --git a/widget/form/settings/genericsettings.h b/widget/form/settings/genericsettings.h index e79ec7b06..ba3791ada 100644 --- a/widget/form/settings/genericsettings.h +++ b/widget/form/settings/genericsettings.h @@ -27,7 +27,7 @@ public: GenericForm(const QString &name, const QPixmap &icon) : formName(name), formIcon(icon) {;} ~GenericForm() {;} - virtual void updateContent() {;} + virtual void present() {} QString getFormName() {return formName;} QPixmap getFormIcon() {return formIcon;} diff --git a/widget/form/settings/identityform.cpp b/widget/form/settings/identityform.cpp index cea181318..7581e8e98 100644 --- a/widget/form/settings/identityform.cpp +++ b/widget/form/settings/identityform.cpp @@ -85,7 +85,7 @@ void IdentityForm::onStatusMessageEdited() Core::getInstance()->setStatusMessage(bodyUI->statusMessage->text()); } -void IdentityForm::updateContent() +void IdentityForm::present() { toxId->setText(Core::getInstance()->getSelfId().toString()); toxId->setCursorPosition(0); diff --git a/widget/form/settings/identityform.h b/widget/form/settings/identityform.h index a313aa135..fa3759903 100644 --- a/widget/form/settings/identityform.h +++ b/widget/form/settings/identityform.h @@ -50,7 +50,7 @@ public: void setUserName(const QString &name); void setStatusMessage(const QString &msg); - virtual void updateContent(); + virtual void present(); signals: void userNameChanged(QString); diff --git a/widget/form/settingswidget.cpp b/widget/form/settingswidget.cpp index b7539a4c5..6ef030f53 100644 --- a/widget/form/settingswidget.cpp +++ b/widget/form/settingswidget.cpp @@ -25,7 +25,7 @@ #include #include -SettingsWidget::SettingsWidget(Camera* cam, QWidget* parent) +SettingsWidget::SettingsWidget(QWidget* parent) : QWidget(parent) { body = new QWidget(this); @@ -55,7 +55,7 @@ SettingsWidget::SettingsWidget(Camera* cam, QWidget* parent) GeneralForm *gfrm = new GeneralForm; ifrm = new IdentityForm; PrivacyForm *pfrm = new PrivacyForm; - AVForm *avfrm = new AVForm(cam); + AVForm *avfrm = new AVForm; GenericForm *cfgForms[] = {gfrm, ifrm, pfrm, avfrm}; for (auto cfgForm : cfgForms) @@ -85,8 +85,8 @@ void SettingsWidget::show(Ui::MainWindow& ui) void SettingsWidget::onTabChanged(int index) { this->settingsWidgets->setCurrentIndex(index); - GenericForm *currentWidget = static_cast(this->settingsWidgets->widget(index)); - currentWidget->updateContent(); + GenericForm* currentWidget = static_cast(this->settingsWidgets->widget(index)); + currentWidget->present(); nameLabel->setText(currentWidget->getFormName()); imgLabel->setPixmap(currentWidget->getFormIcon().scaledToHeight(40, Qt::SmoothTransformation)); } diff --git a/widget/form/settingswidget.h b/widget/form/settingswidget.h index 431e52c1c..6dca68b82 100644 --- a/widget/form/settingswidget.h +++ b/widget/form/settingswidget.h @@ -35,7 +35,7 @@ class SettingsWidget : public QWidget { Q_OBJECT public: - SettingsWidget(Camera* cam, QWidget* parent = nullptr); + SettingsWidget(QWidget* parent = nullptr); ~SettingsWidget(); void show(Ui::MainWindow &ui); diff --git a/widget/selfcamview.cpp b/widget/selfcamview.cpp deleted file mode 100644 index 7e9c3d252..000000000 --- a/widget/selfcamview.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - Copyright (C) 2014 by Project Tox - - 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 -#include -#include -#include -#include -#include - -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(); -} diff --git a/widget/videosurface.cpp b/widget/videosurface.cpp new file mode 100644 index 000000000..bd49b49c0 --- /dev/null +++ b/widget/videosurface.cpp @@ -0,0 +1,197 @@ +/* + Copyright (C) 2014 by Project Tox + + 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 +#include +#include +#include +#include + +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(); +} + + diff --git a/widget/selfcamview.h b/widget/videosurface.h similarity index 53% rename from widget/selfcamview.h rename to widget/videosurface.h index 26a8b315c..7de304682 100644 --- a/widget/selfcamview.h +++ b/widget/videosurface.h @@ -17,39 +17,42 @@ #ifndef SELFCAMVIEW_H #define SELFCAMVIEW_H -#include +#include -class QCloseEvent; -class QShowEvent; -class QPainter; -class Camera; -class QLabel; -class QHBoxLayout; +class QOpenGLBuffer; +class QOpenGLShaderProgram; class QTimer; +class VideoSource; -class SelfCamView : public QWidget +class VideoSurface : public QGLWidget { Q_OBJECT public: - SelfCamView(Camera* Cam, QWidget *parent=0); + VideoSurface(VideoSource* source, QWidget* parent=0); + ~VideoSurface(); -private slots: - void updateDisplay(); - -private: - void closeEvent(QCloseEvent*); - void showEvent(QShowEvent*); - void paint(QPainter *painter); + virtual void hideEvent(QHideEvent* ev); + virtual void showEvent(QShowEvent* ev); + // QGLWidget interface protected: - void resizeEvent(QResizeEvent *e); + virtual void initializeGL(); + virtual void paintGL(); + virtual void updateGL(); + + void update(); private: - QLabel *displayLabel; - QHBoxLayout* mainLayout; - Camera* cam; - QTimer* updateDisplayTimer; + VideoSource* source; + QOpenGLBuffer* pbo; + QOpenGLShaderProgram* program; + GLuint textureId; + int pboAllocSize; + QSize res; + bool uploadFrame; + bool hasSubscribed; + }; #endif // SELFCAMVIEW_H diff --git a/widget/widget.cpp b/widget/widget.cpp index 85ce6ae3c..ff51f96b2 100644 --- a/widget/widget.cpp +++ b/widget/widget.cpp @@ -27,7 +27,6 @@ #include "widget/groupwidget.h" #include "widget/form/groupchatform.h" #include "misc/style.h" -#include "selfcamview.h" #include "widget/friendlistwidget.h" #include "camera.h" #include "widget/form/chatform.h" @@ -103,8 +102,7 @@ Widget::Widget(QWidget *parent) ui->statusButton->setProperty("status", "offline"); Style::repolish(ui->statusButton); - camera = new Camera; - settingsWidget = new SettingsWidget(camera); + settingsWidget = new SettingsWidget(); // Disable some widgets until we're connected to the DHT ui->statusButton->setEnabled(false); @@ -121,7 +119,7 @@ Widget::Widget(QWidget *parent) QString profilePath = detectProfile(); coreThread = new QThread(this); - core = new Core(camera, coreThread, profilePath); + core = new Core(Camera::getInstance(), coreThread, profilePath); core->moveToThread(coreThread); connect(coreThread, &QThread::started, core, &Core::start); @@ -283,11 +281,6 @@ QString Widget::getUsername() return core->getUsername(); } -Camera* Widget::getCamera() -{ - return camera; -} - void Widget::onAvatarClicked() { QString filename = QFileDialog::getOpenFileName(this, tr("Choose a profile picture"), QDir::homePath()); @@ -409,6 +402,14 @@ void Widget::onTransferClicked() activeChatroomWidget = nullptr; } +void Widget::onIconClick() +{ + if(this->isHidden() == true) + this->show(); + else + this->hide(); +} + void Widget::onSettingsClicked() { hideMainForms(); diff --git a/widget/widget.h b/widget/widget.h index a7f210d98..d93ced7c4 100644 --- a/widget/widget.h +++ b/widget/widget.h @@ -34,7 +34,7 @@ class GenericChatroomWidget; class Group; struct Friend; class QSplitter; -class SelfCamView; +class VideoSurface; class QMenu; class Core; class Camera; @@ -107,6 +107,7 @@ private slots: void onMessageSendResult(int friendId, const QString& message, int messageId); void onGroupSendResult(int groupId, const QString& message, int result); void playRingtone(); + void onIconClick(); private: void hideMainForms(); @@ -128,7 +129,6 @@ private: static Widget* instance; GenericChatroomWidget* activeChatroomWidget; FriendListWidget* contactListWidget; - Camera* camera; MaskablePixmapWidget* profilePicture; bool notify(QObject *receiver, QEvent *event); };