mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
New camera system, preparation for video
This commit is contained in:
parent
a2566d4183
commit
6ff776f345
246
toxgui.pro
246
toxgui.pro
@ -1,121 +1,125 @@
|
|||||||
#-------------------------------------------------
|
#-------------------------------------------------
|
||||||
#
|
#
|
||||||
# Project created by QtCreator 2014-06-22T14:07:35
|
# Project created by QtCreator 2014-06-22T14:07:35
|
||||||
#
|
#
|
||||||
#-------------------------------------------------
|
#-------------------------------------------------
|
||||||
|
|
||||||
QT += core gui multimedia multimediawidgets
|
QT += core gui multimedia multimediawidgets
|
||||||
|
|
||||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
TARGET = toxgui
|
TARGET = toxgui
|
||||||
TEMPLATE = app
|
TEMPLATE = app
|
||||||
|
|
||||||
HEADERS += widget/form/addfriendform.h \
|
HEADERS += widget/form/addfriendform.h \
|
||||||
widget/form/chatform.h \
|
widget/form/chatform.h \
|
||||||
widget/form/groupchatform.h \
|
widget/form/groupchatform.h \
|
||||||
widget/form/settingsform.h \
|
widget/form/settingsform.h \
|
||||||
widget/tool/chattextedit.h \
|
widget/tool/chattextedit.h \
|
||||||
widget/tool/copyableelidelabel.h \
|
widget/tool/copyableelidelabel.h \
|
||||||
widget/tool/editablelabelwidget.h \
|
widget/tool/editablelabelwidget.h \
|
||||||
widget/tool/elidelabel.h \
|
widget/tool/elidelabel.h \
|
||||||
widget/tool/esclineedit.h \
|
widget/tool/esclineedit.h \
|
||||||
widget/tool/friendrequestdialog.h \
|
widget/tool/friendrequestdialog.h \
|
||||||
widget/filetransfertwidget.h \
|
widget/filetransfertwidget.h \
|
||||||
widget/friendwidget.h \
|
widget/friendwidget.h \
|
||||||
widget/groupwidget.h \
|
widget/groupwidget.h \
|
||||||
widget/widget.h \
|
widget/widget.h \
|
||||||
friend.h \
|
friend.h \
|
||||||
group.h \
|
group.h \
|
||||||
grouplist.h \
|
grouplist.h \
|
||||||
settings.h \
|
settings.h \
|
||||||
status.h \
|
status.h \
|
||||||
core.h \
|
core.h \
|
||||||
friendlist.h \
|
friendlist.h \
|
||||||
cdata.h \
|
cdata.h \
|
||||||
cstring.h \
|
cstring.h \
|
||||||
audiobuffer.h \
|
audiobuffer.h \
|
||||||
widget/selfcamview.h
|
widget/selfcamview.h \
|
||||||
|
widget/videosurface.h \
|
||||||
FORMS += widget.ui
|
widget/camera.h
|
||||||
|
|
||||||
CONFIG += c++11
|
FORMS += widget.ui
|
||||||
|
|
||||||
RESOURCES += \
|
CONFIG += c++11
|
||||||
res.qrc
|
|
||||||
|
RESOURCES += \
|
||||||
LIBS += -ltoxcore -ltoxav -lsodium
|
res.qrc
|
||||||
|
|
||||||
SOURCES += \
|
LIBS += -ltoxcore -ltoxav -lsodium -lvpx
|
||||||
widget/form/addfriendform.cpp \
|
|
||||||
widget/form/chatform.cpp \
|
SOURCES += \
|
||||||
widget/form/groupchatform.cpp \
|
widget/form/addfriendform.cpp \
|
||||||
widget/form/settingsform.cpp \
|
widget/form/chatform.cpp \
|
||||||
widget/tool/chattextedit.cpp \
|
widget/form/groupchatform.cpp \
|
||||||
widget/tool/copyableelidelabel.cpp \
|
widget/form/settingsform.cpp \
|
||||||
widget/tool/editablelabelwidget.cpp \
|
widget/tool/chattextedit.cpp \
|
||||||
widget/tool/elidelabel.cpp \
|
widget/tool/copyableelidelabel.cpp \
|
||||||
widget/tool/esclineedit.cpp \
|
widget/tool/editablelabelwidget.cpp \
|
||||||
widget/tool/friendrequestdialog.cpp \
|
widget/tool/elidelabel.cpp \
|
||||||
widget/filetransfertwidget.cpp \
|
widget/tool/esclineedit.cpp \
|
||||||
widget/friendwidget.cpp \
|
widget/tool/friendrequestdialog.cpp \
|
||||||
widget/groupwidget.cpp \
|
widget/filetransfertwidget.cpp \
|
||||||
widget/widget.cpp \
|
widget/friendwidget.cpp \
|
||||||
core.cpp \
|
widget/groupwidget.cpp \
|
||||||
friend.cpp \
|
widget/widget.cpp \
|
||||||
friendlist.cpp \
|
core.cpp \
|
||||||
group.cpp \
|
friend.cpp \
|
||||||
grouplist.cpp \
|
friendlist.cpp \
|
||||||
main.cpp \
|
group.cpp \
|
||||||
settings.cpp \
|
grouplist.cpp \
|
||||||
status.cpp \
|
main.cpp \
|
||||||
cdata.cpp \
|
settings.cpp \
|
||||||
cstring.cpp \
|
status.cpp \
|
||||||
audiobuffer.cpp \
|
cdata.cpp \
|
||||||
widget/selfcamview.cpp
|
cstring.cpp \
|
||||||
|
audiobuffer.cpp \
|
||||||
|
widget/selfcamview.cpp \
|
||||||
|
widget/videosurface.cpp \
|
||||||
### EXAMPLE BUILD SETTINGS FOR WINDOWS
|
widget/camera.cpp
|
||||||
#win32: LIBS += -L$$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/ -ltoxcore
|
|
||||||
|
|
||||||
#INCLUDEPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
|
||||||
#DEPENDPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
### EXAMPLE BUILD SETTINGS FOR WINDOWS
|
||||||
|
win32: LIBS += -L$$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/ -ltoxcore
|
||||||
#win32:!win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/toxcore.lib
|
|
||||||
#else:win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/libtoxcore.a
|
INCLUDEPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
||||||
|
DEPENDPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
||||||
#win32: LIBS += -L$$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/ -ltoxav
|
|
||||||
|
win32:!win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/toxcore.lib
|
||||||
#INCLUDEPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
else:win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/libtoxcore.a
|
||||||
#DEPENDPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
|
||||||
|
win32: LIBS += -L$$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/ -ltoxav
|
||||||
#win32:!win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/toxav.lib
|
|
||||||
#else:win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/libtoxav.a
|
INCLUDEPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
||||||
|
DEPENDPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
||||||
#win32: LIBS += -L$$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/ -lvpx
|
|
||||||
|
win32:!win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/toxav.lib
|
||||||
#INCLUDEPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
else:win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/libtoxav.a
|
||||||
#DEPENDPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
|
||||||
|
win32: LIBS += -L$$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/ -lvpx
|
||||||
#win32:!win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/vpx.lib
|
|
||||||
#else:win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/libvpx.a
|
INCLUDEPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
||||||
|
DEPENDPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
||||||
|
|
||||||
#win32: LIBS += -L$$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/ -lopus
|
win32:!win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/vpx.lib
|
||||||
|
else:win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/libvpx.a
|
||||||
#INCLUDEPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
|
||||||
#DEPENDPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
|
||||||
|
win32: LIBS += -L$$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/ -lopus
|
||||||
#win32:!win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/opus.lib
|
|
||||||
#else:win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/libopus.a
|
INCLUDEPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
||||||
|
DEPENDPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
||||||
#win32: LIBS += -lws2_32
|
|
||||||
|
win32:!win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/opus.lib
|
||||||
#win32: LIBS += -L$$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/ -lsodium
|
else:win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/libopus.a
|
||||||
|
|
||||||
#INCLUDEPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
win32: LIBS += -lws2_32
|
||||||
#DEPENDPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
|
||||||
|
win32: LIBS += -L$$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/ -lsodium
|
||||||
#win32:!win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/sodium.lib
|
|
||||||
#else:win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/libsodium.a
|
INCLUDEPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
||||||
|
DEPENDPATH += $$PWD/../../../../Downloads/libtoxcore-win32-i686/include
|
||||||
|
|
||||||
|
win32:!win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/sodium.lib
|
||||||
|
else:win32-g++: PRE_TARGETDEPS += $$PWD/../../../../Downloads/libtoxcore-win32-i686/lib/libsodium.a
|
||||||
|
166
widget/camera.cpp
Normal file
166
widget/camera.cpp
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
#include "camera.h"
|
||||||
|
#include <QVideoSurfaceFormat>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
Camera::Camera()
|
||||||
|
: refcount{0}, camera{new QCamera}
|
||||||
|
{
|
||||||
|
camera->setCaptureMode(QCamera::CaptureVideo);
|
||||||
|
camera->setViewfinder(this);
|
||||||
|
|
||||||
|
connect(camera, SIGNAL(error(QCamera::Error)), this, SLOT(onCameraError(QCamera::Error)));
|
||||||
|
|
||||||
|
supportedFormats << QVideoFrame::Format_YUV420P << QVideoFrame::Format_YV12 << QVideoFrame::Format_RGB32;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::suscribe()
|
||||||
|
{
|
||||||
|
if (refcount <= 0)
|
||||||
|
{
|
||||||
|
refcount = 1;
|
||||||
|
camera->start();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
refcount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::unsuscribe()
|
||||||
|
{
|
||||||
|
refcount--;
|
||||||
|
|
||||||
|
if (refcount <= 0)
|
||||||
|
{
|
||||||
|
camera->stop();
|
||||||
|
refcount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVideoFrame Camera::getLastFrame()
|
||||||
|
{
|
||||||
|
return lastFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Camera::start(const QVideoSurfaceFormat &format)
|
||||||
|
{
|
||||||
|
if(supportedFormats.contains(format.pixelFormat()))
|
||||||
|
{
|
||||||
|
frameFormat = format.pixelFormat();
|
||||||
|
QAbstractVideoSurface::start(format);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QMessageBox::warning(0, "Camera error", "The camera only supports rare video formats, can't use it");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Camera::present(const QVideoFrame &frame)
|
||||||
|
{
|
||||||
|
QVideoFrame frameMap(frame); // Basically a const_cast because shallow copies
|
||||||
|
frameMap.map(QAbstractVideoBuffer::ReadOnly);
|
||||||
|
int w = frameMap.width(), h = frameMap.height();
|
||||||
|
int bpl = frameMap.bytesPerLine(), size = frameMap.mappedBytes();
|
||||||
|
QVideoFrame frameCopy(size, QSize(w, h), bpl, frameMap.pixelFormat());
|
||||||
|
frameCopy.map(QAbstractVideoBuffer::WriteOnly);
|
||||||
|
memcpy(frameCopy.bits(), frameMap.bits(), size);
|
||||||
|
frameCopy.unmap();
|
||||||
|
lastFrame = frameCopy;
|
||||||
|
frameMap.unmap();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QVideoFrame::PixelFormat> Camera::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
|
||||||
|
{
|
||||||
|
if (handleType == QAbstractVideoBuffer::NoHandle)
|
||||||
|
return supportedFormats;
|
||||||
|
else
|
||||||
|
return QList<QVideoFrame::PixelFormat>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::onCameraError(QCamera::Error value)
|
||||||
|
{
|
||||||
|
QMessageBox::warning(0,"Camera error",QString("Error %1 : %2")
|
||||||
|
.arg(value).arg(camera->errorString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Camera::isFormatSupported(const QVideoSurfaceFormat& format) const
|
||||||
|
{
|
||||||
|
if (format.pixelFormat() == 0)
|
||||||
|
{
|
||||||
|
//QMessageBox::warning(0, "Camera eror","The camera's video format is not supported !");
|
||||||
|
return QAbstractVideoSurface::isFormatSupported(format);
|
||||||
|
}
|
||||||
|
else if(supportedFormats.contains(format.pixelFormat()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QMessageBox::warning(0, "Camera eror",
|
||||||
|
QString("Camera format %1 not supported, can't use the camera")
|
||||||
|
.arg(format.pixelFormat()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QImage Camera::getLastImage()
|
||||||
|
{
|
||||||
|
lastFrame.map(QAbstractVideoBuffer::ReadOnly);
|
||||||
|
int w = lastFrame.width(), h = lastFrame.height();
|
||||||
|
int bpl = lastFrame.bytesPerLine(), cxbpl = bpl/2;
|
||||||
|
QImage img(w, h, QImage::Format_RGB32);
|
||||||
|
|
||||||
|
if (frameFormat == QVideoFrame::Format_YUV420P)
|
||||||
|
{
|
||||||
|
uint8_t* yData = lastFrame.bits();
|
||||||
|
uint8_t* uData = yData + (bpl * h);
|
||||||
|
uint8_t* vData = uData + (bpl * h / 4);
|
||||||
|
for (int i = 0; i< h; i++)
|
||||||
|
{
|
||||||
|
uint32_t* scanline = (uint32_t*)img.scanLine(i);
|
||||||
|
for (int j=0; j < bpl; j++)
|
||||||
|
{
|
||||||
|
float Y = yData[i*bpl + j];
|
||||||
|
float U = uData[i*cxbpl/2 + j/2];
|
||||||
|
float V = vData[i*cxbpl/2 + j/2];
|
||||||
|
|
||||||
|
uint8_t R = qMax(qMin((int)(Y + 1.402 * (V - 128)),255),0);
|
||||||
|
uint8_t G = qMax(qMin((int)(Y - 0.344 * (U - 128) - 0.714 * (V - 128)),255),0);
|
||||||
|
uint8_t B = qMax(qMin((int)(Y + 1.772 * (U - 128)),255),0);
|
||||||
|
|
||||||
|
scanline[j] = (0xFF<<24) + (R<<16) + (G<<8) + B;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (frameFormat == QVideoFrame::Format_YV12)
|
||||||
|
{
|
||||||
|
uint8_t* yData = lastFrame.bits();
|
||||||
|
uint8_t* vData = yData + (bpl * h);
|
||||||
|
uint8_t* uData = vData + (bpl * h / 4);
|
||||||
|
for (int i = 0; i< h; i++)
|
||||||
|
{
|
||||||
|
uint32_t* scanline = (uint32_t*)img.scanLine(i);
|
||||||
|
for (int j=0; j < bpl; j++)
|
||||||
|
{
|
||||||
|
float Y = yData[i*bpl + j];
|
||||||
|
float U = uData[i*cxbpl/2 + j/2];
|
||||||
|
float V = vData[i*cxbpl/2 + j/2];
|
||||||
|
|
||||||
|
uint8_t R = qMax(qMin((int)(Y + 1.402 * (V - 128)),255),0);
|
||||||
|
uint8_t G = qMax(qMin((int)(Y - 0.344 * (U - 128) - 0.714 * (V - 128)),255),0);
|
||||||
|
uint8_t B = qMax(qMin((int)(Y + 1.772 * (U - 128)),255),0);
|
||||||
|
|
||||||
|
scanline[j] = (0xFF<<24) + (R<<16) + (G<<8) + B;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (frameFormat == QVideoFrame::Format_RGB32)
|
||||||
|
{
|
||||||
|
memcpy(img.bits(), lastFrame.bits(), bpl*h);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastFrame.unmap();
|
||||||
|
return img;
|
||||||
|
}
|
47
widget/camera.h
Normal file
47
widget/camera.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#ifndef CAMERA_H
|
||||||
|
#define CAMERA_H
|
||||||
|
|
||||||
|
#include <QCamera>
|
||||||
|
#include <QVideoFrame>
|
||||||
|
#include <QAbstractVideoSurface>
|
||||||
|
#include "vpx/vpx_image.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is a wrapper to share a camera's captured video frames
|
||||||
|
* In Qt cameras normally only send their frames to a single output at a time
|
||||||
|
* So you can't, for example, send the frames over the network
|
||||||
|
* and output them to a widget on the screen at the same time
|
||||||
|
*
|
||||||
|
* Instead this class allows objects to surscribe and unsuscribe, starting
|
||||||
|
* the camera only when needed, and giving access to the last frame
|
||||||
|
**/
|
||||||
|
|
||||||
|
class Camera : private QAbstractVideoSurface
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Camera();
|
||||||
|
void suscribe(); ///< Call this once before trying to get frames
|
||||||
|
void unsuscribe(); ///< Call this once when you don't need frames anymore
|
||||||
|
QVideoFrame 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 !)
|
||||||
|
bool isFormatSupported(const QVideoSurfaceFormat & format) const;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onCameraError(QCamera::Error value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool start(const QVideoSurfaceFormat &format);
|
||||||
|
bool present(const QVideoFrame &frame);
|
||||||
|
QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int refcount; ///< Number of users suscribed to the camera
|
||||||
|
QCamera *camera;
|
||||||
|
QVideoFrame lastFrame;
|
||||||
|
int frameFormat;
|
||||||
|
QList<QVideoFrame::PixelFormat> supportedFormats;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CAMERA_H
|
@ -1,4 +1,5 @@
|
|||||||
#include "settingsform.h"
|
#include "settingsform.h"
|
||||||
|
#include "widget/widget.h"
|
||||||
#include <QFont>
|
#include <QFont>
|
||||||
|
|
||||||
SettingsForm::SettingsForm()
|
SettingsForm::SettingsForm()
|
||||||
@ -17,7 +18,6 @@ SettingsForm::SettingsForm()
|
|||||||
id.setFont(small);
|
id.setFont(small);
|
||||||
id.setTextInteractionFlags(Qt::TextSelectableByMouse);
|
id.setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||||
|
|
||||||
camview = new SelfCamView();
|
|
||||||
videoTest.setText("Test video");
|
videoTest.setText("Test video");
|
||||||
|
|
||||||
main->setLayout(&layout);
|
main->setLayout(&layout);
|
||||||
@ -38,7 +38,6 @@ SettingsForm::SettingsForm()
|
|||||||
|
|
||||||
SettingsForm::~SettingsForm()
|
SettingsForm::~SettingsForm()
|
||||||
{
|
{
|
||||||
delete camview;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsForm::setFriendAddress(const QString& friendAddress)
|
void SettingsForm::setFriendAddress(const QString& friendAddress)
|
||||||
@ -58,5 +57,5 @@ void SettingsForm::show(Ui::Widget &ui)
|
|||||||
|
|
||||||
void SettingsForm::onTestVideoClicked()
|
void SettingsForm::onTestVideoClicked()
|
||||||
{
|
{
|
||||||
camview->show();
|
Widget::getInstance()->showTestCamview();
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ private:
|
|||||||
QPushButton videoTest;
|
QPushButton videoTest;
|
||||||
QVBoxLayout layout, headLayout;
|
QVBoxLayout layout, headLayout;
|
||||||
QWidget *main, *head;
|
QWidget *main, *head;
|
||||||
SelfCamView* camview;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QLineEdit name, statusText;
|
QLineEdit name, statusText;
|
||||||
|
@ -3,78 +3,49 @@
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QCloseEvent>
|
#include <QCloseEvent>
|
||||||
#include <QShowEvent>
|
#include <QShowEvent>
|
||||||
|
#include <QVideoFrame>
|
||||||
|
|
||||||
SelfCamView::SelfCamView(QWidget* parent)
|
#include "videosurface.h"
|
||||||
: QWidget(parent), camera(nullptr), mainLayout{new QHBoxLayout()}
|
#include "widget.h"
|
||||||
|
|
||||||
|
SelfCamView::SelfCamView(Camera* Cam, QWidget* parent)
|
||||||
|
: QWidget(parent), displayLabel{new QLabel},
|
||||||
|
mainLayout{new QHBoxLayout()}, cam(Cam)
|
||||||
{
|
{
|
||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
setWindowTitle("Tox video test");
|
setWindowTitle("Tox video test");
|
||||||
setMinimumSize(320,240);
|
setMinimumSize(320,240);
|
||||||
|
|
||||||
QByteArray cameraDevice;
|
updateDisplayTimer.setInterval(75);
|
||||||
|
updateDisplayTimer.setSingleShot(false);
|
||||||
|
|
||||||
/*
|
displayLabel->setScaledContents(true);
|
||||||
QActionGroup *videoDevicesGroup = new QActionGroup(this);
|
|
||||||
videoDevicesGroup->setExclusive(true);
|
|
||||||
foreach(const QByteArray &deviceName, QCamera::availableDevices()) {
|
|
||||||
QString description = camera->deviceDescription(deviceName);
|
|
||||||
QAction *videoDeviceAction = new QAction(description, videoDevicesGroup);
|
|
||||||
videoDeviceAction->setCheckable(true);
|
|
||||||
videoDeviceAction->setData(QVariant(deviceName));
|
|
||||||
if (cameraDevice.isEmpty()) {
|
|
||||||
cameraDevice = deviceName;
|
|
||||||
videoDeviceAction->setChecked(true);
|
|
||||||
}
|
|
||||||
ui->menuDevices->addAction(videoDeviceAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(videoDevicesGroup, SIGNAL(triggered(QAction*)), SLOT(updateCameraDevice(QAction*)));
|
mainLayout->addWidget(displayLabel);
|
||||||
*/
|
|
||||||
|
|
||||||
viewfinder = new QCameraViewfinder(this);
|
connect(&updateDisplayTimer, SIGNAL(timeout()), this, SLOT(updateDisplay()));
|
||||||
mainLayout->addWidget(viewfinder);
|
|
||||||
viewfinder->show();
|
|
||||||
|
|
||||||
setCamera(cameraDevice);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SelfCamView::~SelfCamView()
|
SelfCamView::~SelfCamView()
|
||||||
{
|
{
|
||||||
delete camera;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SelfCamView::setCamera(const QByteArray &cameraDevice)
|
|
||||||
{
|
|
||||||
delete camera;
|
|
||||||
|
|
||||||
if (cameraDevice.isEmpty())
|
|
||||||
camera = new QCamera;
|
|
||||||
else
|
|
||||||
camera = new QCamera(cameraDevice);
|
|
||||||
|
|
||||||
//connect(camera, SIGNAL(stateChanged(QCamera::State)), this, SLOT(updateCameraState(QCamera::State)));
|
|
||||||
connect(camera, SIGNAL(error(QCamera::Error)), this, SLOT(displayCameraError()));
|
|
||||||
|
|
||||||
camera->setViewfinder(viewfinder);
|
|
||||||
|
|
||||||
//updateCameraState(camera->state());
|
|
||||||
|
|
||||||
camera->setCaptureMode(QCamera::CaptureVideo);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SelfCamView::displayCameraError()
|
|
||||||
{
|
|
||||||
QMessageBox::warning(this, tr("Camera error"), camera->errorString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SelfCamView::closeEvent(QCloseEvent* event)
|
void SelfCamView::closeEvent(QCloseEvent* event)
|
||||||
{
|
{
|
||||||
camera->stop();
|
cam->unsuscribe();
|
||||||
|
updateDisplayTimer.stop();
|
||||||
event->accept();
|
event->accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SelfCamView::showEvent(QShowEvent* event)
|
void SelfCamView::showEvent(QShowEvent* event)
|
||||||
{
|
{
|
||||||
camera->start();
|
cam->suscribe();
|
||||||
|
updateDisplayTimer.start();
|
||||||
event->accept();
|
event->accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SelfCamView::updateDisplay()
|
||||||
|
{
|
||||||
|
displayLabel->setPixmap(QPixmap::fromImage(cam->getLastImage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,37 +1,37 @@
|
|||||||
#ifndef SELFCAMVIEW_H
|
#ifndef SELFCAMVIEW_H
|
||||||
#define SELFCAMVIEW_H
|
#define SELFCAMVIEW_H
|
||||||
|
|
||||||
#include <QCamera>
|
|
||||||
#include <QCameraImageCapture>
|
|
||||||
#include <QMediaRecorder>
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QVideoWidget>
|
|
||||||
#include <QCameraViewfinder>
|
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QLabel>
|
||||||
|
#include "camera.h"
|
||||||
|
|
||||||
class QCloseEvent;
|
class QCloseEvent;
|
||||||
class QShowEvent;
|
class QShowEvent;
|
||||||
|
class QPainter;
|
||||||
|
|
||||||
class SelfCamView : public QWidget
|
class SelfCamView : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SelfCamView(QWidget *parent=0);
|
SelfCamView(Camera* Cam, QWidget *parent=0);
|
||||||
~SelfCamView();
|
~SelfCamView();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void updateDisplay();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void closeEvent(QCloseEvent*);
|
void closeEvent(QCloseEvent*);
|
||||||
void showEvent(QShowEvent*);
|
void showEvent(QShowEvent*);
|
||||||
|
void paint(QPainter *painter);
|
||||||
private slots:
|
|
||||||
void setCamera(const QByteArray &cameraDevice);
|
|
||||||
void displayCameraError();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QCamera *camera;
|
QLabel *displayLabel;
|
||||||
QCameraViewfinder* viewfinder;
|
|
||||||
QHBoxLayout* mainLayout;
|
QHBoxLayout* mainLayout;
|
||||||
|
Camera* cam;
|
||||||
|
QTimer updateDisplayTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SELFCAMVIEW_H
|
#endif // SELFCAMVIEW_H
|
||||||
|
87
widget/videosurface.cpp
Normal file
87
widget/videosurface.cpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#include "videosurface.h"
|
||||||
|
#include "core.h"
|
||||||
|
#include <QVideoFrame>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
VideoSurface::VideoSurface()
|
||||||
|
: QAbstractVideoSurface()
|
||||||
|
{
|
||||||
|
vpx_img_alloc(&input, VPX_IMG_FMT_YV12, TOXAV_VIDEO_WIDTH, TOXAV_VIDEO_HEIGHT, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VideoSurface::start(const QVideoSurfaceFormat &format)
|
||||||
|
{
|
||||||
|
mVideoFormat = format;
|
||||||
|
//start only if format is UYVY, dont handle other format now
|
||||||
|
if( format.pixelFormat() == QVideoFrame::Format_YV12 ){
|
||||||
|
QAbstractVideoSurface::start(format);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VideoSurface::present(const QVideoFrame &frame)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
mFrame = frame;
|
||||||
|
|
||||||
|
qDebug() << QString("Video: Frame format is %1").arg(mFrame.pixelFormat());
|
||||||
|
|
||||||
|
stop();
|
||||||
|
|
||||||
|
//this is necessary to get valid data from frame
|
||||||
|
mFrame.map(QAbstractVideoBuffer::ReadOnly);
|
||||||
|
|
||||||
|
uchar* data = new uchar[frame.mappedBytes()];
|
||||||
|
memcpy(data, frame.bits(), frame.mappedBytes());
|
||||||
|
|
||||||
|
input.planes[VPX_PLANE_Y] = data;
|
||||||
|
input.planes[VPX_PLANE_U] = data + (frame.bytesPerLine() * frame.height());
|
||||||
|
input.planes[VPX_PLANE_V] = input.planes[VPX_PLANE_U] + (frame.bytesPerLine()/2 * frame.height()/2);
|
||||||
|
input.planes[VPX_PLANE_ALPHA] = nullptr;
|
||||||
|
|
||||||
|
//qDebug() << QString("Got %1 bytes, first plane is %2 bytes long")
|
||||||
|
// .arg(frame.mappedBytes()).arg(frame.bytesPerLine() * frame.height());
|
||||||
|
|
||||||
|
// Slots MUST be called with a direct or blocking connection, or input may die before they return !
|
||||||
|
emit videoFrameReady(input);
|
||||||
|
|
||||||
|
|
||||||
|
QImage lastImage( mFrame.size(), QImage::Format_RGB16);
|
||||||
|
const uchar *src = mFrame.bits();
|
||||||
|
uchar *dst = lastImage.bits();
|
||||||
|
const int srcLineStep = mFrame.bytesPerLine();
|
||||||
|
const int dstLineStep = lastImage.bytesPerLine();
|
||||||
|
const int h = mFrame.height();
|
||||||
|
const int w = mFrame.width();
|
||||||
|
|
||||||
|
for (int y=0; y < h; y++) {
|
||||||
|
//this function you can find in qgraphicsvideoitem_maemo5.cpp,
|
||||||
|
//link is mentioned above
|
||||||
|
uyvy422_to_rgb16_line_neon(dst, src, w);
|
||||||
|
src += srcLineStep;
|
||||||
|
dst += dstLineStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
mLastFrame = QPixmap::fromImage(lastImage);
|
||||||
|
//emit signal, other can handle it and do necessary processing
|
||||||
|
emit frameUpdated(mLastFrame);
|
||||||
|
|
||||||
|
delete[] data;
|
||||||
|
mFrame.unmap();
|
||||||
|
|
||||||
|
*/
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QVideoFrame::PixelFormat> VideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
|
||||||
|
{
|
||||||
|
if (handleType == QAbstractVideoBuffer::NoHandle) {
|
||||||
|
qDebug() << "Video: No handle";
|
||||||
|
return QList<QVideoFrame::PixelFormat>() << QVideoFrame::Format_YV12;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Video: Handle type is not NoHandle";
|
||||||
|
return QList<QVideoFrame::PixelFormat>();
|
||||||
|
}
|
||||||
|
}
|
26
widget/videosurface.h
Normal file
26
widget/videosurface.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef VIDEOSURFACE_H
|
||||||
|
#define VIDEOSURFACE_H
|
||||||
|
|
||||||
|
#include <QAbstractVideoSurface>
|
||||||
|
#include <QVideoSurfaceFormat>
|
||||||
|
#include "vpx/vpx_image.h"
|
||||||
|
|
||||||
|
class VideoSurface : public QAbstractVideoSurface
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
VideoSurface();
|
||||||
|
bool start(const QVideoSurfaceFormat &format);
|
||||||
|
bool present(const QVideoFrame &frame);
|
||||||
|
QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// Slots MUST be called with a direct or blocking connection, or img may die before they return !
|
||||||
|
void videoFrameReady(vpx_image img);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVideoSurfaceFormat mVideoFormat;
|
||||||
|
vpx_image_t input;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VIDEOSURFACE_H
|
Loading…
x
Reference in New Issue
Block a user