From b394372ad005d32ae9a74ca3941c308a29276a53 Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Mon, 30 Jun 2014 14:49:42 +0200 Subject: [PATCH] Add video reception --- core.cpp | 78 ++++++++++++++++++++++++++---------------- core.h | 7 ++-- dialogs.ini | 3 ++ main.cpp | 3 +- widget/camera.cpp | 37 ++++++++++++++++++++ widget/netcamview.cpp | 5 +-- widget/netcamview.h | 1 + widget/selfcamview.cpp | 2 +- widget/widget.cpp | 7 +++- widget/widget.h | 1 + 10 files changed, 107 insertions(+), 37 deletions(-) create mode 100644 dialogs.ini diff --git a/core.cpp b/core.cpp index 0db29b8e9..b32162f28 100644 --- a/core.cpp +++ b/core.cpp @@ -34,8 +34,8 @@ QList Core::fileSendQueue; QList Core::fileRecvQueue; ToxCall Core::calls[TOXAV_MAX_CALLS]; -Core::Core() : - tox(nullptr) +Core::Core(Camera* cam) : + tox(nullptr), camera(cam) { toxTimer = new QTimer(this); toxTimer->setSingleShot(true); @@ -1142,6 +1142,8 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled } // Go + calls[callId].active = true; + if (calls[callId].audioOutput != nullptr) { 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].playVideoTimer.setInterval(50); - calls[callId].playVideoTimer.setSingleShot(true); - connect(&calls[callId].playVideoTimer, &QTimer::timeout, [=](){ - Widget::getInstance()->getCore()->playCallVideo(callId);}); - calls[callId].playVideoTimer.start(); + if (calls[callId].videoEnabled) + { + calls[callId].playVideoTimer.setInterval(50); + calls[callId].playVideoTimer.setSingleShot(true); + 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) @@ -1183,6 +1194,8 @@ void Core::cleanupCall(int callId) calls[callId].audioInput = nullptr; 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].sendVideoTimer,0,0,0); calls[callId].audioBuffer.clear(); @@ -1268,7 +1281,13 @@ void Core::playCallVideo(int callId) if(toxav_recv_video(toxav, callId, &image) == 0) { if (image) + { emit videoFrameReceived(*image); + } + else + { + //qDebug() << "Core: Received null video frame\n"; + } } else qDebug() << "Core: Error receiving video frame\n"; @@ -1276,19 +1295,31 @@ void Core::playCallVideo(int callId) calls[callId].playVideoTimer.start(); } - -void Core::sendVideoFrame(int callId, ToxAv* toxav, vpx_image img) +void Core::sendCallVideo(int callId) { - uint8_t videobuf[TOXAV_VIDEO_WIDTH * TOXAV_VIDEO_HEIGHT * 4]; - 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); + if (!calls[callId].active || !calls[callId].videoEnabled) return; - } - if((result = toxav_send_video(toxav, callId, videobuf, result)) < 0) - qDebug() << QString("Core: Error sending video frame: %1").arg(result); + uint8_t videobuf[TOXAV_VIDEO_WIDTH * TOXAV_VIDEO_HEIGHT * 4]; + 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) @@ -1300,14 +1331,3 @@ void Core::createGroup() { emit emptyGroupCreated(tox_add_groupchat(tox)); } - -void Core::dispatchVideoFrame(vpx_image img) const -{ - for (int i=0; i dhtServerList; int dhtServerId; static QList fileSendQueue, fileRecvQueue; diff --git a/dialogs.ini b/dialogs.ini new file mode 100644 index 000000000..dcd367e79 --- /dev/null +++ b/dialogs.ini @@ -0,0 +1,3 @@ +[General] +geometry=@Rect(147 345 604 375) +maximized=false diff --git a/main.cpp b/main.cpp index a0bc52ad0..1e7133dc3 100644 --- a/main.cpp +++ b/main.cpp @@ -24,9 +24,9 @@ int main(int argc, char *argv[]) /** TODO * ">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?) * => 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 * Don't do anything if a friend is disconnected, don't print to the chat * 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 * 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 - * 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 (?) * * In the file transfer widget: diff --git a/widget/camera.cpp b/widget/camera.cpp index 9aee81151..b5167906e 100644 --- a/widget/camera.cpp +++ b/widget/camera.cpp @@ -164,3 +164,40 @@ QImage Camera::getLastImage() lastFrame.unmap(); 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; + +} diff --git a/widget/netcamview.cpp b/widget/netcamview.cpp index fe14351d2..fb0537bf1 100644 --- a/widget/netcamview.cpp +++ b/widget/netcamview.cpp @@ -15,7 +15,7 @@ NetCamView::NetCamView(QWidget* parent) 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]; QImage img(w, h, QImage::Format_RGB32); @@ -25,7 +25,7 @@ void NetCamView::updateDisplay(vpx_image frame) for (int i = 0; i< h; 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 U = uData[i*cxbpl/2 + j/2]; @@ -39,5 +39,6 @@ void NetCamView::updateDisplay(vpx_image frame) } } + lastFrame = img; displayLabel->setPixmap(QPixmap::fromImage(img)); } diff --git a/widget/netcamview.h b/widget/netcamview.h index 30e72d893..5adfaeeba 100644 --- a/widget/netcamview.h +++ b/widget/netcamview.h @@ -23,6 +23,7 @@ public slots: private: QLabel *displayLabel; + QImage lastFrame; QHBoxLayout* mainLayout; }; diff --git a/widget/selfcamview.cpp b/widget/selfcamview.cpp index 3ddaf8ff0..52b4766c1 100644 --- a/widget/selfcamview.cpp +++ b/widget/selfcamview.cpp @@ -16,7 +16,7 @@ SelfCamView::SelfCamView(Camera* Cam, QWidget* parent) setWindowTitle("Tox video test"); setMinimumSize(320,240); - updateDisplayTimer.setInterval(75); + updateDisplayTimer.setInterval(5); updateDisplayTimer.setSingleShot(false); displayLabel->setScaledContents(true); diff --git a/widget/widget.cpp b/widget/widget.cpp index 014514f1a..c947ef11a 100644 --- a/widget/widget.cpp +++ b/widget/widget.cpp @@ -43,7 +43,7 @@ Widget::Widget(QWidget *parent) : qRegisterMetaType("ToxFile"); qRegisterMetaType("ToxFile::FileDirection"); - core = new Core(); + core = new Core(camera); coreThread = new QThread(this); core->moveToThread(coreThread); connect(coreThread, &QThread::started, core, &Core::start); @@ -123,6 +123,11 @@ QString Widget::getUsername() return ui->nameLabel->text(); } +Camera* Widget::getCamera() +{ + return camera; +} + void Widget::onConnected() { emit statusSet(Status::Online); diff --git a/widget/widget.h b/widget/widget.h index c28be5559..0f37e5289 100644 --- a/widget/widget.h +++ b/widget/widget.h @@ -27,6 +27,7 @@ public: explicit Widget(QWidget *parent = 0); QString getUsername(); Core* getCore(); + Camera* getCamera(); static Widget* getInstance(); void showTestCamview(); ~Widget();