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;
|
||||
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
7
core.h
|
@ -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
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
|
||||
* ">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:
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ public slots:
|
|||
|
||||
private:
|
||||
QLabel *displayLabel;
|
||||
QImage lastFrame;
|
||||
QHBoxLayout* mainLayout;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
explicit Widget(QWidget *parent = 0);
|
||||
QString getUsername();
|
||||
Core* getCore();
|
||||
Camera* getCamera();
|
||||
static Widget* getInstance();
|
||||
void showTestCamview();
|
||||
~Widget();
|
||||
|
|
Loading…
Reference in New Issue
Block a user