mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Add video reception
This commit is contained in:
parent
6e7887823d
commit
b394372ad0
78
core.cpp
78
core.cpp
|
@ -34,8 +34,8 @@ QList<ToxFile> Core::fileSendQueue;
|
||||||
QList<ToxFile> Core::fileRecvQueue;
|
QList<ToxFile> Core::fileRecvQueue;
|
||||||
ToxCall Core::calls[TOXAV_MAX_CALLS];
|
ToxCall Core::calls[TOXAV_MAX_CALLS];
|
||||||
|
|
||||||
Core::Core() :
|
Core::Core(Camera* cam) :
|
||||||
tox(nullptr)
|
tox(nullptr), camera(cam)
|
||||||
{
|
{
|
||||||
toxTimer = new QTimer(this);
|
toxTimer = new QTimer(this);
|
||||||
toxTimer->setSingleShot(true);
|
toxTimer->setSingleShot(true);
|
||||||
|
@ -1142,6 +1142,8 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go
|
// Go
|
||||||
|
calls[callId].active = true;
|
||||||
|
|
||||||
if (calls[callId].audioOutput != nullptr)
|
if (calls[callId].audioOutput != nullptr)
|
||||||
{
|
{
|
||||||
calls[callId].playAudioTimer.setInterval(5);
|
calls[callId].playAudioTimer.setInterval(5);
|
||||||
|
@ -1158,13 +1160,22 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled
|
||||||
calls[callId].sendAudioTimer.start();
|
calls[callId].sendAudioTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
calls[callId].playVideoTimer.setInterval(50);
|
if (calls[callId].videoEnabled)
|
||||||
calls[callId].playVideoTimer.setSingleShot(true);
|
{
|
||||||
connect(&calls[callId].playVideoTimer, &QTimer::timeout, [=](){
|
calls[callId].playVideoTimer.setInterval(50);
|
||||||
Widget::getInstance()->getCore()->playCallVideo(callId);});
|
calls[callId].playVideoTimer.setSingleShot(true);
|
||||||
calls[callId].playVideoTimer.start();
|
connect(&calls[callId].playVideoTimer, &QTimer::timeout, [=](){
|
||||||
|
Widget::getInstance()->getCore()->playCallVideo(callId);});
|
||||||
|
calls[callId].playVideoTimer.start();
|
||||||
|
|
||||||
calls[callId].active = true;
|
calls[callId].sendVideoTimer.setInterval(50);
|
||||||
|
calls[callId].sendVideoTimer.setSingleShot(true);
|
||||||
|
connect(&calls[callId].sendVideoTimer, &QTimer::timeout, [=](){
|
||||||
|
Widget::getInstance()->getCore()->sendCallVideo(callId);});
|
||||||
|
//calls[callId].sendVideoTimer.start();
|
||||||
|
|
||||||
|
Widget::getInstance()->getCamera()->suscribe();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::cleanupCall(int callId)
|
void Core::cleanupCall(int callId)
|
||||||
|
@ -1183,6 +1194,8 @@ void Core::cleanupCall(int callId)
|
||||||
calls[callId].audioInput = nullptr;
|
calls[callId].audioInput = nullptr;
|
||||||
disconnect(&calls[callId].sendAudioTimer,0,0,0);
|
disconnect(&calls[callId].sendAudioTimer,0,0,0);
|
||||||
}
|
}
|
||||||
|
if (calls[callId].videoEnabled)
|
||||||
|
Widget::getInstance()->getCamera()->unsuscribe();
|
||||||
disconnect(&calls[callId].playVideoTimer,0,0,0);
|
disconnect(&calls[callId].playVideoTimer,0,0,0);
|
||||||
disconnect(&calls[callId].sendVideoTimer,0,0,0);
|
disconnect(&calls[callId].sendVideoTimer,0,0,0);
|
||||||
calls[callId].audioBuffer.clear();
|
calls[callId].audioBuffer.clear();
|
||||||
|
@ -1268,7 +1281,13 @@ void Core::playCallVideo(int callId)
|
||||||
if(toxav_recv_video(toxav, callId, &image) == 0)
|
if(toxav_recv_video(toxav, callId, &image) == 0)
|
||||||
{
|
{
|
||||||
if (image)
|
if (image)
|
||||||
|
{
|
||||||
emit videoFrameReceived(*image);
|
emit videoFrameReceived(*image);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//qDebug() << "Core: Received null video frame\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
qDebug() << "Core: Error receiving video frame\n";
|
qDebug() << "Core: Error receiving video frame\n";
|
||||||
|
@ -1276,19 +1295,31 @@ void Core::playCallVideo(int callId)
|
||||||
calls[callId].playVideoTimer.start();
|
calls[callId].playVideoTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::sendCallVideo(int callId)
|
||||||
void Core::sendVideoFrame(int callId, ToxAv* toxav, vpx_image img)
|
|
||||||
{
|
{
|
||||||
uint8_t videobuf[TOXAV_VIDEO_WIDTH * TOXAV_VIDEO_HEIGHT * 4];
|
if (!calls[callId].active || !calls[callId].videoEnabled)
|
||||||
int result;
|
|
||||||
if((result = toxav_prepare_video_frame(toxav, callId, videobuf, sizeof(videobuf), &img)) < 0)
|
|
||||||
{
|
|
||||||
qDebug() << QString("Core: Error preparing video frame: %1").arg(result);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if((result = toxav_send_video(toxav, callId, videobuf, result)) < 0)
|
uint8_t videobuf[TOXAV_VIDEO_WIDTH * TOXAV_VIDEO_HEIGHT * 4];
|
||||||
qDebug() << QString("Core: Error sending video frame: %1").arg(result);
|
vpx_image frame = camera->getLastVPXImage();
|
||||||
|
if (frame.w && frame.h)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
if((result = toxav_prepare_video_frame(toxav, callId, videobuf, sizeof(videobuf), &frame)) < 0)
|
||||||
|
{
|
||||||
|
qDebug() << "Core: toxav_prepare_video_frame error\n";
|
||||||
|
calls[callId].sendVideoTimer.start();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((result = toxav_send_video(toxav, callId, (uint8_t*)videobuf, result)) < 0)
|
||||||
|
qDebug() << QString("Core: toxav_send_video error: %1").arg(result);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
qDebug("Core::sendCallVideo: Invalid frame (bad camera ?)");
|
||||||
|
|
||||||
|
calls[callId].sendVideoTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::groupInviteFriend(int friendId, int groupId)
|
void Core::groupInviteFriend(int friendId, int groupId)
|
||||||
|
@ -1300,14 +1331,3 @@ void Core::createGroup()
|
||||||
{
|
{
|
||||||
emit emptyGroupCreated(tox_add_groupchat(tox));
|
emit emptyGroupCreated(tox_add_groupchat(tox));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::dispatchVideoFrame(vpx_image img) const
|
|
||||||
{
|
|
||||||
for (int i=0; i<TOXAV_MAX_CALLS; i++)
|
|
||||||
{
|
|
||||||
if (calls[i].active && calls[i].videoEnabled)
|
|
||||||
{
|
|
||||||
sendVideoFrame(i, toxav, img);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
7
core.h
7
core.h
|
@ -46,6 +46,8 @@
|
||||||
#define TOXAV_VIDEO_WIDTH 640
|
#define TOXAV_VIDEO_WIDTH 640
|
||||||
#define TOXAV_VIDEO_HEIGHT 480
|
#define TOXAV_VIDEO_HEIGHT 480
|
||||||
|
|
||||||
|
class Camera;
|
||||||
|
|
||||||
struct DhtServer
|
struct DhtServer
|
||||||
{
|
{
|
||||||
QString name;
|
QString name;
|
||||||
|
@ -104,7 +106,7 @@ class Core : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit Core();
|
explicit Core(Camera* cam);
|
||||||
~Core();
|
~Core();
|
||||||
|
|
||||||
int getGroupNumberPeers(int groupId) const;
|
int getGroupNumberPeers(int groupId) const;
|
||||||
|
@ -250,7 +252,7 @@ private:
|
||||||
static void playCallAudio(int callId, ToxAv* toxav);
|
static void playCallAudio(int callId, ToxAv* toxav);
|
||||||
static void sendCallAudio(int callId, ToxAv* toxav); // Blocking, start in a thread
|
static void sendCallAudio(int callId, ToxAv* toxav); // Blocking, start in a thread
|
||||||
void playCallVideo(int callId);
|
void playCallVideo(int callId);
|
||||||
static void sendVideoFrame(int callId, ToxAv* toxav, vpx_image img);
|
void sendCallVideo(int callId);
|
||||||
|
|
||||||
void checkConnection();
|
void checkConnection();
|
||||||
void onBootstrapTimer();
|
void onBootstrapTimer();
|
||||||
|
@ -268,6 +270,7 @@ private:
|
||||||
Tox* tox;
|
Tox* tox;
|
||||||
ToxAv* toxav;
|
ToxAv* toxav;
|
||||||
QTimer *toxTimer, *saveTimer, *fileTimer, *bootstrapTimer;
|
QTimer *toxTimer, *saveTimer, *fileTimer, *bootstrapTimer;
|
||||||
|
Camera* camera;
|
||||||
QList<DhtServer> dhtServerList;
|
QList<DhtServer> dhtServerList;
|
||||||
int dhtServerId;
|
int dhtServerId;
|
||||||
static QList<ToxFile> fileSendQueue, fileRecvQueue;
|
static QList<ToxFile> fileSendQueue, fileRecvQueue;
|
||||||
|
|
3
dialogs.ini
Normal file
3
dialogs.ini
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[General]
|
||||||
|
geometry=@Rect(147 345 604 375)
|
||||||
|
maximized=false
|
3
main.cpp
3
main.cpp
|
@ -24,9 +24,9 @@ int main(int argc, char *argv[])
|
||||||
/** TODO
|
/** TODO
|
||||||
* ">using a dedicated tool to maintain a TODO list" edition
|
* ">using a dedicated tool to maintain a TODO list" edition
|
||||||
*
|
*
|
||||||
|
* Groupchat users count not updated when people leave
|
||||||
* Sending large files (~380MB) "restarts" after ~10MB. Goes back to 0%, consumes twice as much ram (reloads the file?)
|
* Sending large files (~380MB) "restarts" after ~10MB. Goes back to 0%, consumes twice as much ram (reloads the file?)
|
||||||
* => Don't load the whole file at once, load small chunks (25MB?) when needed, then free them and load the next
|
* => Don't load the whole file at once, load small chunks (25MB?) when needed, then free them and load the next
|
||||||
* Notifications/ringing when a call is received
|
|
||||||
* Sort the friend list by status, online first then busy then offline
|
* Sort the friend list by status, online first then busy then offline
|
||||||
* Don't do anything if a friend is disconnected, don't print to the chat
|
* Don't do anything if a friend is disconnected, don't print to the chat
|
||||||
* Changing online/away/busy/offline by clicking the bubble
|
* Changing online/away/busy/offline by clicking the bubble
|
||||||
|
@ -36,7 +36,6 @@ int main(int argc, char *argv[])
|
||||||
* Show the picture's size between name and size after transfer completion if it's a pic
|
* Show the picture's size between name and size after transfer completion if it's a pic
|
||||||
* Adjust all status icons to match the mockup, including scooting the friendslist ones to the left and making the user one the same size
|
* Adjust all status icons to match the mockup, including scooting the friendslist ones to the left and making the user one the same size
|
||||||
* Sidepanel (friendlist) should be resizeable
|
* Sidepanel (friendlist) should be resizeable
|
||||||
* The online/offline/away status at the top (our) is way too big i think (follow the mockup/uTox)
|
|
||||||
* An extra side panel for groupchats, like Venom does (?)
|
* An extra side panel for groupchats, like Venom does (?)
|
||||||
*
|
*
|
||||||
* In the file transfer widget:
|
* In the file transfer widget:
|
||||||
|
|
|
@ -164,3 +164,40 @@ QImage Camera::getLastImage()
|
||||||
lastFrame.unmap();
|
lastFrame.unmap();
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vpx_image Camera::getLastVPXImage()
|
||||||
|
{
|
||||||
|
lastFrame.map(QAbstractVideoBuffer::ReadOnly);
|
||||||
|
int w = lastFrame.width(), h = lastFrame.height();
|
||||||
|
int bpl = lastFrame.bytesPerLine(), cxbpl = bpl/2;
|
||||||
|
vpx_image img;
|
||||||
|
vpx_img_alloc(&img, VPX_IMG_FMT_I420, w, h, 1); // I420 == YUV420P, same as YV12 with U and V switched
|
||||||
|
|
||||||
|
if (frameFormat == QVideoFrame::Format_YUV420P)
|
||||||
|
{
|
||||||
|
uint8_t* yData = lastFrame.bits();
|
||||||
|
uint8_t* uData = yData + (bpl * h);
|
||||||
|
uint8_t* vData = uData + (bpl * h / 4);
|
||||||
|
img.planes[VPX_PLANE_Y] = yData;
|
||||||
|
img.planes[VPX_PLANE_U] = uData;
|
||||||
|
img.planes[VPX_PLANE_V] = vData;
|
||||||
|
}
|
||||||
|
else if (frameFormat == QVideoFrame::Format_YV12)
|
||||||
|
{
|
||||||
|
uint8_t* yData = lastFrame.bits();
|
||||||
|
uint8_t* uData = yData + (bpl * h);
|
||||||
|
uint8_t* vData = uData + (bpl * h / 4);
|
||||||
|
img.planes[VPX_PLANE_Y] = yData;
|
||||||
|
img.planes[VPX_PLANE_U] = vData;
|
||||||
|
img.planes[VPX_PLANE_V] = uData;
|
||||||
|
}
|
||||||
|
else if (frameFormat == QVideoFrame::Format_RGB32)
|
||||||
|
{
|
||||||
|
img.w = img.h = 0; // Invalid frame. TODO: Implement conversion
|
||||||
|
qWarning() << "Camera: Can't convert from RGB32! Go complain at github.com/tux3/toxgui";
|
||||||
|
}
|
||||||
|
|
||||||
|
lastFrame.unmap();
|
||||||
|
return img;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ NetCamView::NetCamView(QWidget* parent)
|
||||||
|
|
||||||
void NetCamView::updateDisplay(vpx_image frame)
|
void NetCamView::updateDisplay(vpx_image frame)
|
||||||
{
|
{
|
||||||
int w = frame.w, h = frame.h;
|
int w = frame.d_w, h = frame.d_h;
|
||||||
int bpl = frame.stride[VPX_PLANE_Y], cxbpl = frame.stride[VPX_PLANE_V];
|
int bpl = frame.stride[VPX_PLANE_Y], cxbpl = frame.stride[VPX_PLANE_V];
|
||||||
QImage img(w, h, QImage::Format_RGB32);
|
QImage img(w, h, QImage::Format_RGB32);
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ void NetCamView::updateDisplay(vpx_image frame)
|
||||||
for (int i = 0; i< h; i++)
|
for (int i = 0; i< h; i++)
|
||||||
{
|
{
|
||||||
uint32_t* scanline = (uint32_t*)img.scanLine(i);
|
uint32_t* scanline = (uint32_t*)img.scanLine(i);
|
||||||
for (int j=0; j < bpl; j++)
|
for (int j=0; j < w; j++)
|
||||||
{
|
{
|
||||||
float Y = yData[i*bpl + j];
|
float Y = yData[i*bpl + j];
|
||||||
float U = uData[i*cxbpl/2 + j/2];
|
float U = uData[i*cxbpl/2 + j/2];
|
||||||
|
@ -39,5 +39,6 @@ void NetCamView::updateDisplay(vpx_image frame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastFrame = img;
|
||||||
displayLabel->setPixmap(QPixmap::fromImage(img));
|
displayLabel->setPixmap(QPixmap::fromImage(img));
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ public slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QLabel *displayLabel;
|
QLabel *displayLabel;
|
||||||
|
QImage lastFrame;
|
||||||
QHBoxLayout* mainLayout;
|
QHBoxLayout* mainLayout;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ SelfCamView::SelfCamView(Camera* Cam, QWidget* parent)
|
||||||
setWindowTitle("Tox video test");
|
setWindowTitle("Tox video test");
|
||||||
setMinimumSize(320,240);
|
setMinimumSize(320,240);
|
||||||
|
|
||||||
updateDisplayTimer.setInterval(75);
|
updateDisplayTimer.setInterval(5);
|
||||||
updateDisplayTimer.setSingleShot(false);
|
updateDisplayTimer.setSingleShot(false);
|
||||||
|
|
||||||
displayLabel->setScaledContents(true);
|
displayLabel->setScaledContents(true);
|
||||||
|
|
|
@ -43,7 +43,7 @@ Widget::Widget(QWidget *parent) :
|
||||||
qRegisterMetaType<ToxFile>("ToxFile");
|
qRegisterMetaType<ToxFile>("ToxFile");
|
||||||
qRegisterMetaType<ToxFile::FileDirection>("ToxFile::FileDirection");
|
qRegisterMetaType<ToxFile::FileDirection>("ToxFile::FileDirection");
|
||||||
|
|
||||||
core = new Core();
|
core = new Core(camera);
|
||||||
coreThread = new QThread(this);
|
coreThread = new QThread(this);
|
||||||
core->moveToThread(coreThread);
|
core->moveToThread(coreThread);
|
||||||
connect(coreThread, &QThread::started, core, &Core::start);
|
connect(coreThread, &QThread::started, core, &Core::start);
|
||||||
|
@ -123,6 +123,11 @@ QString Widget::getUsername()
|
||||||
return ui->nameLabel->text();
|
return ui->nameLabel->text();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Camera* Widget::getCamera()
|
||||||
|
{
|
||||||
|
return camera;
|
||||||
|
}
|
||||||
|
|
||||||
void Widget::onConnected()
|
void Widget::onConnected()
|
||||||
{
|
{
|
||||||
emit statusSet(Status::Online);
|
emit statusSet(Status::Online);
|
||||||
|
|
|
@ -27,6 +27,7 @@ public:
|
||||||
explicit Widget(QWidget *parent = 0);
|
explicit Widget(QWidget *parent = 0);
|
||||||
QString getUsername();
|
QString getUsername();
|
||||||
Core* getCore();
|
Core* getCore();
|
||||||
|
Camera* getCamera();
|
||||||
static Widget* getInstance();
|
static Widget* getInstance();
|
||||||
void showTestCamview();
|
void showTestCamview();
|
||||||
~Widget();
|
~Widget();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user