1
0
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:
Tux3 / Mlkj / !Lev.uXFMLA 2014-06-30 14:49:42 +02:00
parent 6e7887823d
commit b394372ad0
10 changed files with 107 additions and 37 deletions

View File

@ -34,8 +34,8 @@ QList<ToxFile> Core::fileSendQueue;
QList<ToxFile> 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<TOXAV_MAX_CALLS; i++)
{
if (calls[i].active && calls[i].videoEnabled)
{
sendVideoFrame(i, toxav, img);
}
}
}

7
core.h
View File

@ -46,6 +46,8 @@
#define TOXAV_VIDEO_WIDTH 640
#define TOXAV_VIDEO_HEIGHT 480
class Camera;
struct DhtServer
{
QString name;
@ -104,7 +106,7 @@ class Core : public QObject
{
Q_OBJECT
public:
explicit Core();
explicit Core(Camera* cam);
~Core();
int getGroupNumberPeers(int groupId) const;
@ -250,7 +252,7 @@ private:
static void playCallAudio(int callId, ToxAv* toxav);
static void sendCallAudio(int callId, ToxAv* toxav); // Blocking, start in a thread
void playCallVideo(int callId);
static void sendVideoFrame(int callId, ToxAv* toxav, vpx_image img);
void sendCallVideo(int callId);
void checkConnection();
void onBootstrapTimer();
@ -268,6 +270,7 @@ private:
Tox* tox;
ToxAv* toxav;
QTimer *toxTimer, *saveTimer, *fileTimer, *bootstrapTimer;
Camera* camera;
QList<DhtServer> dhtServerList;
int dhtServerId;
static QList<ToxFile> fileSendQueue, fileRecvQueue;

3
dialogs.ini Normal file
View File

@ -0,0 +1,3 @@
[General]
geometry=@Rect(147 345 604 375)
maximized=false

View File

@ -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:

View File

@ -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;
}

View File

@ -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));
}

View File

@ -23,6 +23,7 @@ public slots:
private:
QLabel *displayLabel;
QImage lastFrame;
QHBoxLayout* mainLayout;
};

View File

@ -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);

View File

@ -43,7 +43,7 @@ Widget::Widget(QWidget *parent) :
qRegisterMetaType<ToxFile>("ToxFile");
qRegisterMetaType<ToxFile::FileDirection>("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);

View File

@ -27,6 +27,7 @@ public:
explicit Widget(QWidget *parent = 0);
QString getUsername();
Core* getCore();
Camera* getCamera();
static Widget* getInstance();
void showTestCamview();
~Widget();