1
0
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:
Tux3 / Mlkj / !Lev.uXFMLA 2014-06-30 04:39:43 +02:00
parent 2efe9ac269
commit 3fde891b91
8 changed files with 329 additions and 76 deletions

151
core.cpp
View File

@ -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
View File

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

View File

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

View File

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

View File

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

View File

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