mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
EXPERIMENTAL video call reception
Can not send video in calls, just receive and play it Known bug : The video isn't converted perfectly to RGB, there are some visible errors
This commit is contained in:
parent
2efe9ac269
commit
3fde891b91
151
core.cpp
151
core.cpp
|
@ -18,6 +18,7 @@
|
|||
#include "cdata.h"
|
||||
#include "cstring.h"
|
||||
#include "settings.h"
|
||||
#include "widget/widget.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
|
@ -880,9 +881,18 @@ void Core::onAvInvite(int32_t call_index, void* core)
|
|||
qWarning() << "Core: Received invalid AV invite";
|
||||
return;
|
||||
}
|
||||
qDebug() << QString("Core: AV invite from %1").arg(friendId);
|
||||
|
||||
emit static_cast<Core*>(core)->avInvite(friendId, call_index);
|
||||
int transType = toxav_get_peer_transmission_type(static_cast<Core*>(core)->toxav, call_index, friendId);
|
||||
if (transType == TypeVideo)
|
||||
{
|
||||
qDebug() << QString("Core: AV invite from %1 with video").arg(friendId);
|
||||
emit static_cast<Core*>(core)->avInvite(friendId, call_index, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: AV invite from %1 without video").arg(friendId);
|
||||
emit static_cast<Core*>(core)->avInvite(friendId, call_index, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Core::onAvStart(int32_t call_index, void* core)
|
||||
|
@ -899,14 +909,14 @@ void Core::onAvStart(int32_t call_index, void* core)
|
|||
{
|
||||
qDebug() << QString("Core: AV start from %1 with video").arg(friendId);
|
||||
prepareCall(friendId, call_index, static_cast<Core*>(core)->toxav, true);
|
||||
emit static_cast<Core*>(core)->avStart(friendId, call_index, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: AV start from %1 without video").arg(friendId);
|
||||
prepareCall(friendId, call_index, static_cast<Core*>(core)->toxav, false);
|
||||
emit static_cast<Core*>(core)->avStart(friendId, call_index, false);
|
||||
}
|
||||
|
||||
emit static_cast<Core*>(core)->avStart(friendId, call_index);
|
||||
}
|
||||
|
||||
void Core::onAvCancel(int32_t call_index, void* core)
|
||||
|
@ -950,9 +960,17 @@ void Core::onAvRinging(int32_t call_index, void* core)
|
|||
qWarning() << "Core: Received invalid AV ringing";
|
||||
return;
|
||||
}
|
||||
qDebug() << QString("Core: AV ringing with %1").arg(friendId);
|
||||
|
||||
emit static_cast<Core*>(core)->avRinging(friendId, call_index);
|
||||
if (calls[call_index].videoEnabled)
|
||||
{
|
||||
qDebug() << QString("Core: AV ringing with %1 with video").arg(friendId);
|
||||
emit static_cast<Core*>(core)->avRinging(friendId, call_index, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: AV ringing with %1 without video").arg(friendId);
|
||||
emit static_cast<Core*>(core)->avRinging(friendId, call_index, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Core::onAvStarting(int32_t call_index, void* core)
|
||||
|
@ -968,14 +986,14 @@ void Core::onAvStarting(int32_t call_index, void* core)
|
|||
{
|
||||
qDebug() << QString("Core: AV starting from %1 with video").arg(friendId);
|
||||
prepareCall(friendId, call_index, static_cast<Core*>(core)->toxav, true);
|
||||
emit static_cast<Core*>(core)->avStarting(friendId, call_index, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: AV starting from %1 without video").arg(friendId);
|
||||
prepareCall(friendId, call_index, static_cast<Core*>(core)->toxav, false);
|
||||
emit static_cast<Core*>(core)->avStarting(friendId, call_index, false);
|
||||
}
|
||||
|
||||
emit static_cast<Core*>(core)->avStarting(friendId, call_index);
|
||||
}
|
||||
|
||||
void Core::onAvEnding(int32_t call_index, void* core)
|
||||
|
@ -1054,12 +1072,19 @@ void Core::hangupCall(int callId)
|
|||
|
||||
void Core::startCall(int friendId, bool video)
|
||||
{
|
||||
qDebug() << QString("Core: Starting call with %1").arg(friendId);
|
||||
int callId;
|
||||
if (video)
|
||||
{
|
||||
qDebug() << QString("Core: Starting new call with %1 with video").arg(friendId);
|
||||
toxav_call(toxav, &callId, friendId, TypeVideo, TOXAV_RINGING_TIME);
|
||||
calls[callId].videoEnabled=true;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: Starting new call with %1 without video").arg(friendId);
|
||||
toxav_call(toxav, &callId, friendId, TypeAudio, TOXAV_RINGING_TIME);
|
||||
calls[callId].videoEnabled=false;
|
||||
}
|
||||
}
|
||||
|
||||
void Core::cancelCall(int callId, int friendId)
|
||||
|
@ -1075,6 +1100,8 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled
|
|||
calls[callId].callId = callId;
|
||||
calls[callId].friendId = friendId;
|
||||
calls[callId].codecSettings = av_DefaultSettings;
|
||||
calls[callId].codecSettings.video_width = TOXAV_VIDEO_WIDTH;
|
||||
calls[callId].codecSettings.video_height = TOXAV_VIDEO_HEIGHT;
|
||||
calls[callId].videoEnabled = videoEnabled;
|
||||
toxav_prepare_transmission(toxav, callId, &calls[callId].codecSettings, videoEnabled);
|
||||
|
||||
|
@ -1115,16 +1142,28 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled
|
|||
}
|
||||
|
||||
// Go
|
||||
/// BUG: TODO: Memory corryption and crashes atexit when playCallAudio is threaded
|
||||
//if (calls[callId].audioOutput != nullptr)
|
||||
// calls[callId].playFuture = QtConcurrent::run(playCallAudio, callId, toxav);
|
||||
if (calls[callId].audioOutput != nullptr)
|
||||
{
|
||||
calls[callId].playAudioTimer.setInterval(5);
|
||||
calls[callId].playAudioTimer.setSingleShot(true);
|
||||
connect(&calls[callId].playAudioTimer, &QTimer::timeout, [=](){playCallAudio(callId,toxav);});
|
||||
calls[callId].playAudioTimer.start();
|
||||
}
|
||||
|
||||
calls[callId].playAudioTimer.setInterval(5);
|
||||
calls[callId].playAudioTimer.setSingleShot(true);
|
||||
connect(&calls[callId].playAudioTimer, &QTimer::timeout, [=](){playCallAudio(callId,toxav);});
|
||||
calls[callId].playAudioTimer.start();
|
||||
if (calls[callId].audioInput != nullptr)
|
||||
calls[callId].recordFuture = QtConcurrent::run(sendCallAudio, callId, toxav);
|
||||
{
|
||||
calls[callId].sendAudioTimer.setInterval(5);
|
||||
calls[callId].sendAudioTimer.setSingleShot(true);
|
||||
connect(&calls[callId].sendAudioTimer, &QTimer::timeout, [=](){sendCallAudio(callId,toxav);});
|
||||
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();
|
||||
|
||||
calls[callId].active = true;
|
||||
}
|
||||
|
||||
|
@ -1132,12 +1171,20 @@ void Core::cleanupCall(int callId)
|
|||
{
|
||||
qDebug() << QString("Core: cleaning up call %1").arg(callId);
|
||||
calls[callId].active = false;
|
||||
calls[callId].playFuture.waitForFinished();
|
||||
calls[callId].recordFuture.waitForFinished();
|
||||
if (calls[callId].audioOutput != nullptr)
|
||||
{
|
||||
delete calls[callId].audioOutput;
|
||||
calls[callId].audioOutput = nullptr;
|
||||
disconnect(&calls[callId].playAudioTimer,0,0,0);
|
||||
}
|
||||
if (calls[callId].audioInput != nullptr)
|
||||
{
|
||||
delete calls[callId].audioInput;
|
||||
calls[callId].audioInput = nullptr;
|
||||
disconnect(&calls[callId].sendAudioTimer,0,0,0);
|
||||
}
|
||||
disconnect(&calls[callId].playVideoTimer,0,0,0);
|
||||
disconnect(&calls[callId].sendVideoTimer,0,0,0);
|
||||
calls[callId].audioBuffer.clear();
|
||||
}
|
||||
|
||||
|
@ -1161,7 +1208,8 @@ void Core::playCallAudio(int callId, ToxAv* toxav)
|
|||
}
|
||||
if (len == 0)
|
||||
{
|
||||
calls[callId].playAudioTimer.start();
|
||||
if (calls[callId].audioBuffer.bufferSize() >= framesize*2)
|
||||
calls[callId].playAudioTimer.start();
|
||||
return;
|
||||
}
|
||||
//qDebug() << QString("Core: Received %1 bytes, %2 audio bytes free, %3 core buffer size")
|
||||
|
@ -1171,7 +1219,7 @@ void Core::playCallAudio(int callId, ToxAv* toxav)
|
|||
if (state != QAudio::ActiveState)
|
||||
{
|
||||
qDebug() << QString("Core: Audio state is %1").arg(state);
|
||||
if (state == 3 && calls[callId].audioBuffer.bytesAvailable() >= framesize*2)
|
||||
if (state == 3 && calls[callId].audioBuffer.bufferSize() >= framesize*2)
|
||||
calls[callId].audioOutput->start(&calls[callId].audioBuffer);
|
||||
}
|
||||
int error = calls[callId].audioOutput->error();
|
||||
|
@ -1184,34 +1232,51 @@ void Core::playCallAudio(int callId, ToxAv* toxav)
|
|||
|
||||
void Core::sendCallAudio(int callId, ToxAv* toxav)
|
||||
{
|
||||
while (calls[callId].active)
|
||||
if (!calls[callId].active)
|
||||
return;
|
||||
int framesize = (calls[callId].codecSettings.audio_frame_duration * calls[callId].codecSettings.audio_sample_rate) / 1000;
|
||||
uint8_t buf[framesize*2], dest[framesize*2];
|
||||
int bytesReady = calls[callId].audioInput->bytesReady();
|
||||
if (bytesReady >= framesize*2)
|
||||
{
|
||||
int framesize = (calls[callId].codecSettings.audio_frame_duration * calls[callId].codecSettings.audio_sample_rate) / 1000;
|
||||
uint8_t buf[framesize*2], dest[framesize*2];
|
||||
int bytesReady = calls[callId].audioInput->bytesReady();
|
||||
if (bytesReady >= framesize*2)
|
||||
calls[callId].audioInputDevice->read((char*)buf, framesize*2);
|
||||
int result = toxav_prepare_audio_frame(toxav, callId, dest, framesize*2, (int16_t*)buf, framesize);
|
||||
if (result < 0)
|
||||
{
|
||||
calls[callId].audioInputDevice->read((char*)buf, framesize*2);
|
||||
int result = toxav_prepare_audio_frame(toxav, callId, dest, framesize*2, (int16_t*)buf, framesize);
|
||||
if (result < 0)
|
||||
{
|
||||
qWarning() << QString("Core: Unable to prepare audio frame, error %1").arg(result);
|
||||
QThread::msleep(5);
|
||||
continue;
|
||||
}
|
||||
result = toxav_send_audio(toxav, callId, dest, result);
|
||||
if (result < 0)
|
||||
{
|
||||
qWarning() << QString("Core: Unable to send audio frame, error %1").arg(result);
|
||||
QThread::msleep(5);
|
||||
continue;
|
||||
}
|
||||
qWarning() << QString("Core: Unable to prepare audio frame, error %1").arg(result);
|
||||
calls[callId].sendAudioTimer.start();
|
||||
return;
|
||||
}
|
||||
else
|
||||
QThread::msleep(5);
|
||||
result = toxav_send_audio(toxav, callId, dest, result);
|
||||
if (result < 0)
|
||||
{
|
||||
qWarning() << QString("Core: Unable to send audio frame, error %1").arg(result);
|
||||
calls[callId].sendAudioTimer.start();
|
||||
return;
|
||||
}
|
||||
calls[callId].sendAudioTimer.start(0);
|
||||
}
|
||||
else
|
||||
calls[callId].sendAudioTimer.start();
|
||||
}
|
||||
|
||||
void Core::playCallVideo(int callId)
|
||||
{
|
||||
if (!calls[callId].active || !calls[callId].videoEnabled)
|
||||
return;
|
||||
vpx_image_t *image;
|
||||
if(toxav_recv_video(toxav, callId, &image) == 0)
|
||||
{
|
||||
if (image)
|
||||
emit videoFrameReceived(*image);
|
||||
}
|
||||
else
|
||||
qDebug() << "Core: Error receiving video frame\n";
|
||||
|
||||
calls[callId].playVideoTimer.start();
|
||||
}
|
||||
|
||||
|
||||
void Core::sendVideoFrame(int callId, ToxAv* toxav, vpx_image img)
|
||||
{
|
||||
uint8_t videobuf[TOXAV_VIDEO_WIDTH * TOXAV_VIDEO_HEIGHT * 4];
|
||||
|
|
17
core.h
17
core.h
|
@ -93,12 +93,10 @@ public:
|
|||
QAudioInput* audioInput;
|
||||
QIODevice* audioInputDevice;
|
||||
ToxAvCodecSettings codecSettings;
|
||||
QTimer playAudioTimer, sendAudioTimer;
|
||||
QTimer playAudioTimer, sendAudioTimer, playVideoTimer, sendVideoTimer;
|
||||
int callId;
|
||||
int friendId;
|
||||
bool videoEnabled;
|
||||
QFuture<void> playFuture;
|
||||
QFuture<void> recordFuture;
|
||||
bool active;
|
||||
};
|
||||
|
||||
|
@ -205,16 +203,18 @@ signals:
|
|||
void fileTransferPaused(int FriendId, int FileNum, ToxFile::FileDirection direction);
|
||||
void fileTransferInfo(int FriendId, int FileNum, int Filesize, int BytesSent, ToxFile::FileDirection direction);
|
||||
|
||||
void avInvite(int friendId, int callIndex);
|
||||
void avStart(int friendId, int callIndex);
|
||||
void avInvite(int friendId, int callIndex, bool video);
|
||||
void avStart(int friendId, int callIndex, bool video);
|
||||
void avCancel(int friendId, int callIndex);
|
||||
void avEnd(int friendId, int callIndex);
|
||||
void avRinging(int friendId, int callIndex);
|
||||
void avStarting(int friendId, int callIndex);
|
||||
void avRinging(int friendId, int callIndex, bool video);
|
||||
void avStarting(int friendId, int callIndex, bool video);
|
||||
void avEnding(int friendId, int callIndex);
|
||||
void avRequestTimeout(int friendId, int callIndex);
|
||||
void avPeerTimeout(int friendId, int callIndex);
|
||||
|
||||
void videoFrameReceived(vpx_image frame);
|
||||
|
||||
private:
|
||||
static void onFriendRequest(Tox* tox, const uint8_t* cUserId, const uint8_t* cMessage, uint16_t cMessageSize, void* core);
|
||||
static void onFriendMessage(Tox* tox, int friendId, uint8_t* cMessage, uint16_t cMessageSize, void* core);
|
||||
|
@ -247,8 +247,9 @@ private:
|
|||
|
||||
static void prepareCall(int friendId, int callId, ToxAv *toxav, bool videoEnabled);
|
||||
static void cleanupCall(int callId);
|
||||
static void playCallAudio(int callId, ToxAv* toxav); // Blocking, start in a thread
|
||||
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 checkConnection();
|
||||
|
|
|
@ -37,7 +37,8 @@ HEADERS += widget/form/addfriendform.h \
|
|||
audiobuffer.h \
|
||||
widget/selfcamview.h \
|
||||
widget/videosurface.h \
|
||||
widget/camera.h
|
||||
widget/camera.h \
|
||||
widget/netcamview.h
|
||||
|
||||
FORMS += widget.ui
|
||||
|
||||
|
@ -76,7 +77,8 @@ SOURCES += \
|
|||
audiobuffer.cpp \
|
||||
widget/selfcamview.cpp \
|
||||
widget/videosurface.cpp \
|
||||
widget/camera.cpp
|
||||
widget/camera.cpp \
|
||||
widget/netcamview.cpp
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ ChatForm::ChatForm(Friend* chatFriend)
|
|||
msgEdit = new ChatTextEdit();
|
||||
sendButton = new QPushButton(), fileButton = new QPushButton(), emoteButton = new QPushButton(), callButton = new QPushButton(), videoButton = new QPushButton();
|
||||
chatArea = new QScrollArea();
|
||||
netcam = new NetCamView();
|
||||
|
||||
QFont bold;
|
||||
bold.setBold(true);
|
||||
|
@ -152,9 +153,11 @@ ChatForm::ChatForm(Friend* chatFriend)
|
|||
chatArea->setWidget(chatAreaWidget);
|
||||
|
||||
connect(Widget::getInstance()->getCore(), &Core::fileSendStarted, this, &ChatForm::startFileSend);
|
||||
connect(Widget::getInstance()->getCore(), &Core::videoFrameReceived, netcam, &NetCamView::updateDisplay);
|
||||
connect(sendButton, SIGNAL(clicked()), this, SLOT(onSendTriggered()));
|
||||
connect(fileButton, SIGNAL(clicked()), this, SLOT(onAttachClicked()));
|
||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
|
||||
connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered()));
|
||||
connect(msgEdit, SIGNAL(enterPressed()), this, SLOT(onSendTriggered()));
|
||||
connect(chatArea->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(onSliderRangeChanged()));
|
||||
connect(chatArea, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
||||
|
@ -164,6 +167,7 @@ ChatForm::~ChatForm()
|
|||
{
|
||||
delete main;
|
||||
delete head;
|
||||
delete netcam;
|
||||
}
|
||||
|
||||
void ChatForm::show(Ui::Widget &ui)
|
||||
|
@ -344,97 +348,186 @@ void ChatForm::onFileRecvRequest(ToxFile file)
|
|||
connect(Widget::getInstance()->getCore(), &Core::fileTransferFinished, fileTrans, &FileTransfertWidget::onFileTransferFinished);
|
||||
}
|
||||
|
||||
void ChatForm::onAvInvite(int FriendId, int CallId)
|
||||
void ChatForm::onAvInvite(int FriendId, int CallId, bool video)
|
||||
{
|
||||
if (FriendId != f->friendId)
|
||||
return;
|
||||
callId = CallId;
|
||||
callButton->setObjectName("yellow");
|
||||
callButton->style()->polish(callButton);
|
||||
callButton->disconnect();
|
||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onAnswerCallTriggered()));
|
||||
videoButton->disconnect();
|
||||
if (video)
|
||||
{
|
||||
callButton->setObjectName("grey");
|
||||
callButton->style()->polish(callButton);
|
||||
videoButton->setObjectName("yellow");
|
||||
videoButton->style()->polish(videoButton);
|
||||
connect(videoButton, SIGNAL(clicked()), this, SLOT(onAnswerCallTriggered()));
|
||||
}
|
||||
else
|
||||
{
|
||||
callButton->setObjectName("yellow");
|
||||
callButton->style()->polish(callButton);
|
||||
videoButton->setObjectName("grey");
|
||||
videoButton->style()->polish(videoButton);
|
||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onAnswerCallTriggered()));
|
||||
}
|
||||
}
|
||||
|
||||
void ChatForm::onAvStart(int FriendId, int CallId)
|
||||
void ChatForm::onAvStart(int FriendId, int CallId, bool video)
|
||||
{
|
||||
if (FriendId != f->friendId)
|
||||
return;
|
||||
callId = CallId;
|
||||
callButton->setObjectName("red");
|
||||
callButton->style()->polish(callButton);
|
||||
callButton->disconnect();
|
||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onHangupCallTriggered()));
|
||||
videoButton->disconnect();
|
||||
if (video)
|
||||
{
|
||||
callButton->setObjectName("grey");
|
||||
callButton->style()->polish(callButton);
|
||||
videoButton->setObjectName("red");
|
||||
videoButton->style()->polish(videoButton);
|
||||
connect(videoButton, SIGNAL(clicked()), this, SLOT(onHangupCallTriggered()));
|
||||
netcam->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
callButton->setObjectName("red");
|
||||
callButton->style()->polish(callButton);
|
||||
videoButton->setObjectName("grey");
|
||||
videoButton->style()->polish(videoButton);
|
||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onHangupCallTriggered()));
|
||||
}
|
||||
}
|
||||
|
||||
void ChatForm::onAvCancel(int FriendId, int)
|
||||
{
|
||||
if (FriendId != f->friendId)
|
||||
return;
|
||||
callButton->setObjectName("red");
|
||||
callButton->style()->polish(callButton);
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
callButton->setObjectName("green");
|
||||
callButton->style()->polish(callButton);
|
||||
videoButton->setObjectName("green");
|
||||
videoButton->style()->polish(videoButton);
|
||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
|
||||
connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered()));
|
||||
netcam->hide();
|
||||
}
|
||||
|
||||
void ChatForm::onAvEnd(int FriendId, int)
|
||||
{
|
||||
if (FriendId != f->friendId)
|
||||
return;
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
callButton->setObjectName("green");
|
||||
callButton->style()->polish(callButton);
|
||||
callButton->disconnect();
|
||||
videoButton->setObjectName("green");
|
||||
videoButton->style()->polish(videoButton);
|
||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
|
||||
connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered()));
|
||||
netcam->hide();
|
||||
}
|
||||
|
||||
void ChatForm::onAvRinging(int FriendId, int CallId)
|
||||
void ChatForm::onAvRinging(int FriendId, int CallId, bool video)
|
||||
{
|
||||
if (FriendId != f->friendId)
|
||||
return;
|
||||
callId = CallId;
|
||||
callButton->setObjectName("grey");
|
||||
callButton->style()->polish(callButton);
|
||||
callButton->disconnect();
|
||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onCancelCallTriggered()));
|
||||
videoButton->disconnect();
|
||||
if (video)
|
||||
{
|
||||
callButton->setObjectName("grey");
|
||||
callButton->style()->polish(callButton);
|
||||
videoButton->setObjectName("yellow");
|
||||
videoButton->style()->polish(videoButton);
|
||||
connect(videoButton, SIGNAL(clicked()), this, SLOT(onCancelCallTriggered()));
|
||||
}
|
||||
else
|
||||
{
|
||||
callButton->setObjectName("yellow");
|
||||
callButton->style()->polish(callButton);
|
||||
videoButton->setObjectName("grey");
|
||||
videoButton->style()->polish(videoButton);
|
||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onCancelCallTriggered()));
|
||||
}
|
||||
}
|
||||
|
||||
void ChatForm::onAvStarting(int FriendId, int)
|
||||
void ChatForm::onAvStarting(int FriendId, int, bool video)
|
||||
{
|
||||
if (FriendId != f->friendId)
|
||||
return;
|
||||
callButton->setObjectName("red");
|
||||
callButton->style()->polish(callButton);
|
||||
callButton->disconnect();
|
||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onHangupCallTriggered()));
|
||||
videoButton->disconnect();
|
||||
if (video)
|
||||
{
|
||||
callButton->setObjectName("grey");
|
||||
callButton->style()->polish(callButton);
|
||||
videoButton->setObjectName("red");
|
||||
videoButton->style()->polish(videoButton);
|
||||
connect(videoButton, SIGNAL(clicked()), this, SLOT(onHangupCallTriggered()));
|
||||
netcam->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
callButton->setObjectName("red");
|
||||
callButton->style()->polish(callButton);
|
||||
videoButton->setObjectName("grey");
|
||||
videoButton->style()->polish(videoButton);
|
||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onHangupCallTriggered()));
|
||||
}
|
||||
}
|
||||
|
||||
void ChatForm::onAvEnding(int FriendId, int)
|
||||
{
|
||||
if (FriendId != f->friendId)
|
||||
return;
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
callButton->setObjectName("green");
|
||||
callButton->style()->polish(callButton);
|
||||
callButton->disconnect();
|
||||
videoButton->setObjectName("green");
|
||||
videoButton->style()->polish(videoButton);
|
||||
videoButton->disconnect();
|
||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
|
||||
connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered()));
|
||||
netcam->hide();
|
||||
}
|
||||
|
||||
void ChatForm::onAvRequestTimeout(int FriendId, int)
|
||||
{
|
||||
if (FriendId != f->friendId)
|
||||
return;
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
callButton->setObjectName("green");
|
||||
callButton->style()->polish(callButton);
|
||||
callButton->disconnect();
|
||||
videoButton->setObjectName("green");
|
||||
videoButton->style()->polish(videoButton);
|
||||
videoButton->disconnect();
|
||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
|
||||
connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered()));
|
||||
netcam->hide();
|
||||
}
|
||||
|
||||
void ChatForm::onAvPeerTimeout(int FriendId, int)
|
||||
{
|
||||
if (FriendId != f->friendId)
|
||||
return;
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
callButton->setObjectName("green");
|
||||
callButton->style()->polish(callButton);
|
||||
callButton->disconnect();
|
||||
videoButton->setObjectName("green");
|
||||
videoButton->style()->polish(videoButton);
|
||||
videoButton->disconnect();
|
||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
|
||||
connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered()));
|
||||
netcam->hide();
|
||||
}
|
||||
|
||||
void ChatForm::onAnswerCallTriggered()
|
||||
|
@ -450,15 +543,30 @@ void ChatForm::onHangupCallTriggered()
|
|||
void ChatForm::onCallTriggered()
|
||||
{
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
emit startCall(f->friendId);
|
||||
}
|
||||
|
||||
void ChatForm::onVideoCallTriggered()
|
||||
{
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
emit startVideoCall(f->friendId, true);
|
||||
}
|
||||
|
||||
void ChatForm::onCancelCallTriggered()
|
||||
{
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
callButton->setObjectName("green");
|
||||
callButton->style()->polish(callButton);
|
||||
callButton->disconnect();
|
||||
videoButton->setObjectName("green");
|
||||
videoButton->style()->polish(videoButton);
|
||||
videoButton->disconnect();
|
||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
|
||||
connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered()));
|
||||
netcam->hide();
|
||||
emit cancelCall(callId, f->friendId);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "widget/tool/chattextedit.h"
|
||||
#include "ui_widget.h"
|
||||
#include "core.h"
|
||||
#include "widget/netcamview.h"
|
||||
|
||||
// Spacing in px inserted when the author of the last message changes
|
||||
#define AUTHOR_CHANGE_SPACING 5
|
||||
|
@ -37,6 +38,7 @@ signals:
|
|||
void sendMessage(int, QString);
|
||||
void sendFile(int32_t friendId, QString, QByteArray);
|
||||
void startCall(int friendId);
|
||||
void startVideoCall(int friendId, bool video);
|
||||
void answerCall(int callId);
|
||||
void hangupCall(int callId);
|
||||
void cancelCall(int callId, int friendId);
|
||||
|
@ -44,12 +46,12 @@ signals:
|
|||
public slots:
|
||||
void startFileSend(ToxFile file);
|
||||
void onFileRecvRequest(ToxFile file);
|
||||
void onAvInvite(int FriendId, int CallId);
|
||||
void onAvStart(int FriendId, int CallId);
|
||||
void onAvInvite(int FriendId, int CallId, bool video);
|
||||
void onAvStart(int FriendId, int CallId, bool video);
|
||||
void onAvCancel(int FriendId, int CallId);
|
||||
void onAvEnd(int FriendId, int CallId);
|
||||
void onAvRinging(int FriendId, int CallId);
|
||||
void onAvStarting(int FriendId, int CallId);
|
||||
void onAvRinging(int FriendId, int CallId, bool video);
|
||||
void onAvStarting(int FriendId, int CallId, bool video);
|
||||
void onAvEnding(int FriendId, int CallId);
|
||||
void onAvRequestTimeout(int FriendId, int CallId);
|
||||
void onAvPeerTimeout(int FriendId, int CallId);
|
||||
|
@ -59,6 +61,7 @@ private slots:
|
|||
void onAttachClicked();
|
||||
void onSliderRangeChanged();
|
||||
void onCallTriggered();
|
||||
void onVideoCallTriggered();
|
||||
void onAnswerCallTriggered();
|
||||
void onHangupCallTriggered();
|
||||
void onCancelCallTriggered();
|
||||
|
@ -76,6 +79,7 @@ private:
|
|||
QScrollArea *chatArea;
|
||||
QWidget *main, *head, *chatAreaWidget;
|
||||
QString previousName;
|
||||
NetCamView* netcam;
|
||||
int curRow;
|
||||
bool lockSliderToBottom;
|
||||
int callId;
|
||||
|
|
43
widget/netcamview.cpp
Normal file
43
widget/netcamview.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include "netcamview.h"
|
||||
|
||||
NetCamView::NetCamView(QWidget* parent)
|
||||
: QWidget(parent), displayLabel{new QLabel},
|
||||
mainLayout{new QHBoxLayout()}
|
||||
{
|
||||
setLayout(mainLayout);
|
||||
setWindowTitle("Tox video");
|
||||
setMinimumSize(320,240);
|
||||
|
||||
displayLabel->setScaledContents(true);
|
||||
|
||||
mainLayout->addWidget(displayLabel);
|
||||
}
|
||||
|
||||
void NetCamView::updateDisplay(vpx_image frame)
|
||||
{
|
||||
int w = frame.w, h = frame.h;
|
||||
int bpl = frame.stride[VPX_PLANE_Y], cxbpl = frame.stride[VPX_PLANE_V];
|
||||
QImage img(w, h, QImage::Format_RGB32);
|
||||
|
||||
uint8_t* yData = frame.planes[VPX_PLANE_Y];
|
||||
uint8_t* uData = frame.planes[VPX_PLANE_V];
|
||||
uint8_t* vData = frame.planes[VPX_PLANE_U];
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
displayLabel->setPixmap(QPixmap::fromImage(img));
|
||||
}
|
29
widget/netcamview.h
Normal file
29
widget/netcamview.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef NETCAMVIEW_H
|
||||
#define NETCAMVIEW_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QHBoxLayout>
|
||||
#include <QTimer>
|
||||
#include <QLabel>
|
||||
#include <vpx/vpx_image.h>
|
||||
|
||||
class QCloseEvent;
|
||||
class QShowEvent;
|
||||
class QPainter;
|
||||
|
||||
class NetCamView : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
NetCamView(QWidget *parent=0);
|
||||
|
||||
public slots:
|
||||
void updateDisplay(vpx_image frame);
|
||||
|
||||
private:
|
||||
QLabel *displayLabel;
|
||||
QHBoxLayout* mainLayout;
|
||||
};
|
||||
|
||||
#endif // NETCAMVIEW_H
|
|
@ -254,6 +254,7 @@ void Widget::addFriend(int friendId, const QString &userId)
|
|||
connect(newfriend->chatForm, SIGNAL(answerCall(int)), core, SLOT(answerCall(int)));
|
||||
connect(newfriend->chatForm, SIGNAL(hangupCall(int)), core, SLOT(hangupCall(int)));
|
||||
connect(newfriend->chatForm, SIGNAL(startCall(int)), core, SLOT(startCall(int)));
|
||||
connect(newfriend->chatForm, SIGNAL(startVideoCall(int,bool)), core, SLOT(startCall(int,bool)));
|
||||
connect(newfriend->chatForm, SIGNAL(cancelCall(int,int)), core, SLOT(cancelCall(int,int)));
|
||||
connect(core, &Core::fileReceiveRequested, newfriend->chatForm, &ChatForm::onFileRecvRequest);
|
||||
connect(core, &Core::avInvite, newfriend->chatForm, &ChatForm::onAvInvite);
|
||||
|
|
Loading…
Reference in New Issue
Block a user