1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00
qTox/core.cpp

1447 lines
48 KiB
C++
Raw Normal View History

2014-06-25 04:11:11 +08:00
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
This file is part of Tox Qt GUI.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the COPYING file for more details.
*/
#include "core.h"
#include "cdata.h"
#include "cstring.h"
#include "settings.h"
#include "widget/widget.h"
#include "audioinputproxy.h"
#include "audiooutputproxy.h"
2014-06-25 04:11:11 +08:00
#include <functional>
2014-06-25 04:11:11 +08:00
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QSaveFile>
#include <QStandardPaths>
#include <QtEndian>
#include <QThread>
#include <QtConcurrent/QtConcurrent>
2014-06-25 04:11:11 +08:00
2014-06-29 01:09:16 +08:00
const QString Core::CONFIG_FILE_NAME = "data";
2014-06-26 04:43:28 +08:00
QList<ToxFile> Core::fileSendQueue;
QList<ToxFile> Core::fileRecvQueue;
ToxCall Core::calls[TOXAV_MAX_CALLS];
2014-07-12 18:50:40 +08:00
const int Core::videobufsize{TOXAV_MAX_VIDEO_WIDTH * TOXAV_MAX_VIDEO_HEIGHT * 4};
uint8_t* Core::videobuf;
int Core::videoBusyness;
2014-06-25 04:11:11 +08:00
Core::Core(Camera* cam, QThread *coreThread) :
2014-06-30 20:49:42 +08:00
tox(nullptr), camera(cam)
2014-06-25 04:11:11 +08:00
{
2014-07-12 18:50:40 +08:00
videobuf = new uint8_t[videobufsize];
videoBusyness=0;
2014-07-12 18:50:40 +08:00
2014-06-25 04:11:11 +08:00
toxTimer = new QTimer(this);
toxTimer->setSingleShot(true);
2014-07-04 01:38:30 +08:00
//saveTimer = new QTimer(this);
//saveTimer->start(TOX_SAVE_INTERVAL);
//fileTimer = new QTimer(this);
//fileTimer->start(TOX_FILE_INTERVAL);
bootstrapTimer = new QTimer(this);
bootstrapTimer->start(TOX_BOOTSTRAP_INTERVAL);
2014-06-25 04:11:11 +08:00
connect(toxTimer, &QTimer::timeout, this, &Core::process);
2014-07-04 01:38:30 +08:00
//connect(saveTimer, &QTimer::timeout, this, &Core::saveConfiguration); //Disable save timer in favor of saving on events
//connect(fileTimer, &QTimer::timeout, this, &Core::fileHeartbeat);
connect(bootstrapTimer, &QTimer::timeout, this, &Core::onBootstrapTimer);
2014-06-25 04:11:11 +08:00
connect(&Settings::getInstance(), &Settings::dhtServerListChanged, this, &Core::bootstrapDht);
connect(this, SIGNAL(fileTransferFinished(ToxFile)), this, SLOT(onFileTransferFinished(ToxFile)));
for (int i=0; i<TOXAV_MAX_CALLS; i++)
{
calls[i].audioInputProxy = nullptr; // TODO: struct -> class; constructor + destructor
calls[i].audioOutputProxy = nullptr; // and here too
calls[i].framesize = 0;
calls[i].audio_packet_samples = nullptr;
calls[i].audio_packet_data = nullptr;
calls[i].sendVideoTimer = new QTimer(this); // TODO: "this" will not work until our parent not defined
calls[i].sendVideoTimer->moveToThread(coreThread); // TODO: QObject inheritance allows to do it automatically
connect(calls[i].sendVideoTimer, &QTimer::timeout, [this,i](){sendCallVideo(i);});
}
2014-06-25 04:11:11 +08:00
}
Core::~Core()
{
if (tox) {
saveConfiguration();
toxav_kill(toxav);
2014-06-25 04:11:11 +08:00
tox_kill(tox);
}
2014-07-12 18:50:40 +08:00
if (videobuf)
{
delete[] videobuf;
videobuf=nullptr;
}
2014-06-25 04:11:11 +08:00
}
void Core::start()
{
2014-07-02 06:47:06 +08:00
// IPv6 needed for LAN discovery, but can crash some weird routers. On by default, can be disabled in options.
bool enableIPv6 = Settings::getInstance().getEnableIPv6();
if (enableIPv6)
qDebug() << "Core starting with IPv6 enabled";
else
qWarning() << "Core starting with IPv6 disabled. LAN discovery may not work properly.";
tox = tox_new(enableIPv6);
if (tox == nullptr)
{
qCritical() << "Tox core failed to start";
emit failedToStart();
return;
}
toxav = toxav_new(tox, TOXAV_MAX_CALLS);
if (toxav == nullptr)
{
qCritical() << "Toxav core failed to start";
emit failedToStart();
return;
}
loadConfiguration();
tox_callback_friend_request(tox, onFriendRequest, this);
tox_callback_friend_message(tox, onFriendMessage, this);
tox_callback_friend_action(tox, onAction, this);
tox_callback_name_change(tox, onFriendNameChange, this);
tox_callback_typing_change(tox, onFriendTypingChange, this);
tox_callback_status_message(tox, onStatusMessageChanged, this);
tox_callback_user_status(tox, onUserStatusChanged, this);
tox_callback_connection_status(tox, onConnectionStatusChanged, this);
tox_callback_group_invite(tox, onGroupInvite, this);
tox_callback_group_message(tox, onGroupMessage, this);
tox_callback_group_namelist_change(tox, onGroupNamelistChange, this);
tox_callback_file_send_request(tox, onFileSendRequestCallback, this);
tox_callback_file_control(tox, onFileControlCallback, this);
tox_callback_file_data(tox, onFileDataCallback, this);
2014-06-27 07:17:10 +08:00
toxav_register_callstate_callback(onAvInvite, av_OnInvite, this);
toxav_register_callstate_callback(onAvStart, av_OnStart, this);
toxav_register_callstate_callback(onAvCancel, av_OnCancel, this);
toxav_register_callstate_callback(onAvReject, av_OnReject, this);
toxav_register_callstate_callback(onAvEnd, av_OnEnd, this);
toxav_register_callstate_callback(onAvRinging, av_OnRinging, this);
toxav_register_callstate_callback(onAvStarting, av_OnStarting, this);
toxav_register_callstate_callback(onAvEnding, av_OnEnding, this);
toxav_register_callstate_callback(onAvError, av_OnError, this);
toxav_register_callstate_callback(onAvRequestTimeout, av_OnRequestTimeout, this);
toxav_register_callstate_callback(onAvPeerTimeout, av_OnPeerTimeout, this);
2014-07-09 15:55:25 +08:00
toxav_register_audio_recv_callback(toxav, playCallAudio);
toxav_register_video_recv_callback(toxav, playCallVideo);
uint8_t friendAddress[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(tox, friendAddress);
emit friendAddressGenerated(CFriendAddress::toString(friendAddress));
bootstrapDht();
toxTimer->start(tox_do_interval(tox));
}
void Core::onBootstrapTimer()
{
if (!tox)
return;
if(!tox_isconnected(tox))
bootstrapDht();
}
2014-06-25 04:11:11 +08:00
void Core::onFriendRequest(Tox*/* tox*/, const uint8_t* cUserId, const uint8_t* cMessage, uint16_t cMessageSize, void* core)
{
emit static_cast<Core*>(core)->friendRequestReceived(CUserId::toString(cUserId), CString::toString(cMessage, cMessageSize));
}
2014-07-03 15:22:12 +08:00
void Core::onFriendMessage(Tox*/* tox*/, int friendId, const uint8_t* cMessage, uint16_t cMessageSize, void* core)
2014-06-25 04:11:11 +08:00
{
emit static_cast<Core*>(core)->friendMessageReceived(friendId, CString::toString(cMessage, cMessageSize));
}
2014-07-03 15:22:12 +08:00
void Core::onFriendNameChange(Tox*/* tox*/, int friendId, const uint8_t* cName, uint16_t cNameSize, void* core)
2014-06-25 04:11:11 +08:00
{
emit static_cast<Core*>(core)->friendUsernameChanged(friendId, CString::toString(cName, cNameSize));
}
void Core::onFriendTypingChange(Tox*/* tox*/, int friendId, uint8_t isTyping, void *core)
{
emit static_cast<Core*>(core)->friendTypingChanged(friendId, isTyping ? true : false);
}
2014-07-03 15:22:12 +08:00
void Core::onStatusMessageChanged(Tox*/* tox*/, int friendId, const uint8_t* cMessage, uint16_t cMessageSize, void* core)
2014-06-25 04:11:11 +08:00
{
emit static_cast<Core*>(core)->friendStatusMessageChanged(friendId, CString::toString(cMessage, cMessageSize));
}
void Core::onUserStatusChanged(Tox*/* tox*/, int friendId, uint8_t userstatus, void* core)
{
Status status;
switch (userstatus) {
case TOX_USERSTATUS_NONE:
status = Status::Online;
break;
case TOX_USERSTATUS_AWAY:
status = Status::Away;
break;
case TOX_USERSTATUS_BUSY:
status = Status::Busy;
break;
default:
status = Status::Online;
break;
}
emit static_cast<Core*>(core)->friendStatusChanged(friendId, status);
}
void Core::onConnectionStatusChanged(Tox*/* tox*/, int friendId, uint8_t status, void* core)
{
Status friendStatus = status ? Status::Online : Status::Offline;
emit static_cast<Core*>(core)->friendStatusChanged(friendId, friendStatus);
if (friendStatus == Status::Offline) {
static_cast<Core*>(core)->checkLastOnline(friendId);
}
}
2014-07-03 15:22:12 +08:00
void Core::onAction(Tox*/* tox*/, int friendId, const uint8_t *cMessage, uint16_t cMessageSize, void *core)
2014-06-25 04:11:11 +08:00
{
emit static_cast<Core*>(core)->actionReceived(friendId, CString::toString(cMessage, cMessageSize));
}
2014-07-03 15:22:12 +08:00
void Core::onGroupInvite(Tox*, int friendnumber, const uint8_t *group_public_key, void *core)
2014-06-25 04:11:11 +08:00
{
qDebug() << QString("Core: Group invite by %1").arg(friendnumber);
emit static_cast<Core*>(core)->groupInviteReceived(friendnumber, group_public_key);
}
2014-07-03 15:22:12 +08:00
void Core::onGroupMessage(Tox*, int groupnumber, int friendgroupnumber, const uint8_t * message, uint16_t length, void *core)
2014-06-25 04:11:11 +08:00
{
emit static_cast<Core*>(core)->groupMessageReceived(groupnumber, friendgroupnumber, CString::toString(message, length));
}
void Core::onGroupNamelistChange(Tox*, int groupnumber, int peernumber, uint8_t change, void *core)
{
qDebug() << QString("Core: Group namelist change %1:%2 %3").arg(groupnumber).arg(peernumber).arg(change);
emit static_cast<Core*>(core)->groupNamelistChanged(groupnumber, peernumber, change);
}
void Core::onFileSendRequestCallback(Tox*, int32_t friendnumber, uint8_t filenumber, uint64_t filesize,
2014-07-03 15:22:12 +08:00
const uint8_t *filename, uint16_t filename_length, void *core)
2014-06-26 04:43:28 +08:00
{
qDebug() << QString("Core: Received file request %1 with friend %2").arg(filenumber).arg(friendnumber);
2014-07-01 19:33:59 +08:00
ToxFile file{filenumber, friendnumber,
CString::toString(filename,filename_length).toUtf8(), "", ToxFile::RECEIVING};
file.filesize = filesize;
fileRecvQueue.append(file);
emit static_cast<Core*>(core)->fileReceiveRequested(fileRecvQueue.last());
2014-06-26 04:43:28 +08:00
}
void Core::onFileControlCallback(Tox*, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber,
2014-07-03 15:22:12 +08:00
uint8_t control_type, const uint8_t*, uint16_t, void *core)
2014-06-26 04:43:28 +08:00
{
2014-06-26 06:30:24 +08:00
ToxFile* file{nullptr};
if (receive_send == 1)
2014-06-26 06:30:24 +08:00
{
for (ToxFile& f : fileSendQueue)
2014-06-26 06:30:24 +08:00
{
if (f.fileNum == filenumber && f.friendId == friendnumber)
{
file = &f;
break;
}
}
}
else
{
for (ToxFile& f : fileRecvQueue)
{
if (f.fileNum == filenumber && f.friendId == friendnumber)
{
file = &f;
break;
}
2014-06-26 06:30:24 +08:00
}
}
if (!file)
{
qWarning("Core::onFileControlCallback: No such file in queue");
return;
}
2014-06-26 04:43:28 +08:00
if (control_type == TOX_FILECONTROL_ACCEPT && receive_send == 1)
{
file->status = ToxFile::TRANSMITTING;
emit static_cast<Core*>(core)->fileTransferAccepted(*file);
2014-06-26 04:43:28 +08:00
qDebug() << "Core: File control callback, file accepted";
file->sendFuture = QtConcurrent::run(sendAllFileData, static_cast<Core*>(core), file);
2014-06-26 04:43:28 +08:00
}
2014-06-26 06:30:24 +08:00
else if (receive_send == 1 && control_type == TOX_FILECONTROL_KILL)
{
qDebug() << QString("Core::onFileControlCallback: Transfer of file %1 cancelled by friend %2")
.arg(file->fileNum).arg(file->friendId);
file->status = ToxFile::STOPPED;
emit static_cast<Core*>(core)->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
file->sendFuture.waitForFinished(); // Wait for sendAllFileData to return before deleting the ToxFile
removeFileFromQueue(true, file->friendId, file->fileNum);
}
else if (receive_send == 0 && control_type == TOX_FILECONTROL_KILL)
{
qDebug() << QString("Core::onFileControlCallback: Transfer of file %1 cancelled by friend %2")
.arg(file->fileNum).arg(file->friendId);
2014-06-26 06:30:24 +08:00
file->status = ToxFile::STOPPED;
emit static_cast<Core*>(core)->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::RECEIVING);
removeFileFromQueue(false, file->friendId, file->fileNum);
}
else if (receive_send == 0 && control_type == TOX_FILECONTROL_FINISHED)
{
qDebug() << QString("Core::onFileControlCallback: Reception of file %1 from %2 finished")
.arg(file->fileNum).arg(file->friendId);
file->status = ToxFile::STOPPED;
emit static_cast<Core*>(core)->fileTransferFinished(*file);
removeFileFromQueue(false, file->friendId, file->fileNum);
2014-06-26 06:30:24 +08:00
}
2014-06-26 04:43:28 +08:00
else
{
qDebug() << QString("Core: File control callback, receive_send=%1, control_type=%2")
.arg(receive_send).arg(control_type);
}
}
2014-07-03 15:22:12 +08:00
void Core::onFileDataCallback(Tox*, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *core)
2014-06-26 04:43:28 +08:00
{
ToxFile* file{nullptr};
for (ToxFile& f : fileRecvQueue)
{
if (f.fileNum == filenumber && f.friendId == friendnumber)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::onFileDataCallback: No such file in queue");
return;
}
2014-07-01 19:33:59 +08:00
file->file->write((char*)data,length);
file->bytesSent += length;
2014-07-13 00:40:01 +08:00
//qDebug() << QString("Core::onFileDataCallback: received %1/%2 bytes").arg(file->bytesSent).arg(file->filesize);
emit static_cast<Core*>(core)->fileTransferInfo(file->friendId, file->fileNum,
file->filesize, file->bytesSent, ToxFile::RECEIVING);
2014-06-26 04:43:28 +08:00
}
2014-06-25 04:11:11 +08:00
void Core::acceptFriendRequest(const QString& userId)
{
int friendId = tox_add_friend_norequest(tox, CUserId(userId).data());
if (friendId == -1) {
emit failedToAddFriend(userId);
} else {
2014-07-04 01:38:30 +08:00
saveConfiguration();
2014-06-25 04:11:11 +08:00
emit friendAdded(friendId, userId);
}
}
void Core::requestFriendship(const QString& friendAddress, const QString& message)
{
qDebug() << "Core: requesting friendship of "+friendAddress;
2014-06-25 04:11:11 +08:00
CString cMessage(message);
int friendId = tox_add_friend(tox, CFriendAddress(friendAddress).data(), cMessage.data(), cMessage.size());
const QString userId = friendAddress.mid(0, TOX_CLIENT_ID_SIZE * 2);
if (friendId < 0) {
emit failedToAddFriend(userId);
} else {
emit friendAdded(friendId, userId);
}
saveConfiguration();
2014-06-25 04:11:11 +08:00
}
void Core::sendMessage(int friendId, const QString& message)
{
CString cMessage(message);
int messageId = tox_send_message(tox, friendId, cMessage.data(), cMessage.size());
emit messageSentResult(friendId, message, messageId);
}
void Core::sendAction(int friendId, const QString &action)
{
CString cMessage(action);
int ret = tox_send_action(tox, friendId, cMessage.data(), cMessage.size());
emit actionSentResult(friendId, action, ret);
}
void Core::sendTyping(int friendId, bool typing)
{
int ret = tox_set_user_is_typing(tox, friendId, typing);
if (ret == -1)
emit failedToSetTyping(typing);
}
void Core::sendGroupMessage(int groupId, const QString& message)
{
CString cMessage(message);
tox_group_message_send(tox, groupId, cMessage.data(), cMessage.size());
}
2014-07-01 19:33:59 +08:00
void Core::sendFile(int32_t friendId, QString Filename, QString FilePath, long long filesize)
2014-06-26 04:43:28 +08:00
{
QByteArray fileName = Filename.toUtf8();
2014-07-01 19:33:59 +08:00
int fileNum = tox_new_file_sender(tox, friendId, filesize, (uint8_t*)fileName.data(), fileName.size());
2014-06-26 04:43:28 +08:00
if (fileNum == -1)
{
qWarning() << "Core::sendFile: Can't create the Tox file sender";
return;
}
qDebug() << QString("Core::sendFile: Created file sender %1 with friend %2").arg(fileNum).arg(friendId);
2014-07-01 19:33:59 +08:00
ToxFile file{fileNum, friendId, fileName, FilePath, ToxFile::SENDING};
file.filesize = filesize;
if (!file.open(false))
{
qWarning() << QString("Core::sendFile: Can't open file, error: %1").arg(file.file->errorString());
}
2014-07-01 19:33:59 +08:00
fileSendQueue.append(file);
emit fileSendStarted(fileSendQueue.last());
}
2014-06-26 04:43:28 +08:00
void Core::pauseResumeFileSend(int friendId, int fileNum)
{
ToxFile* file{nullptr};
for (ToxFile& f : fileSendQueue)
{
if (f.fileNum == fileNum && f.friendId == friendId)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::cancelFileSend: No such file in queue");
return;
}
if (file->status == ToxFile::TRANSMITTING)
{
file->status = ToxFile::PAUSED;
emit fileTransferPaused(file->friendId, file->fileNum, ToxFile::SENDING);
tox_file_send_control(tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_PAUSE, nullptr, 0);
}
else if (file->status == ToxFile::PAUSED)
{
file->status = ToxFile::TRANSMITTING;
emit fileTransferAccepted(*file);
tox_file_send_control(tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_ACCEPT, nullptr, 0);
}
else
qWarning() << "Core::pauseResumeFileSend: File is stopped";
}
2014-06-26 06:30:24 +08:00
void Core::pauseResumeFileRecv(int friendId, int fileNum)
{
ToxFile* file{nullptr};
for (ToxFile& f : fileRecvQueue)
{
if (f.fileNum == fileNum && f.friendId == friendId)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::cancelFileRecv: No such file in queue");
return;
}
if (file->status == ToxFile::TRANSMITTING)
{
file->status = ToxFile::PAUSED;
emit fileTransferPaused(file->friendId, file->fileNum, ToxFile::RECEIVING);
tox_file_send_control(tox, file->friendId, 1, file->fileNum, TOX_FILECONTROL_PAUSE, nullptr, 0);
}
else if (file->status == ToxFile::PAUSED)
{
file->status = ToxFile::TRANSMITTING;
emit fileTransferAccepted(*file);
tox_file_send_control(tox, file->friendId, 1, file->fileNum, TOX_FILECONTROL_ACCEPT, nullptr, 0);
}
else
qWarning() << "Core::pauseResumeFileRecv: File is stopped";
2014-06-26 04:43:28 +08:00
}
void Core::cancelFileSend(int friendId, int fileNum)
2014-06-26 07:48:20 +08:00
{
ToxFile* file{nullptr};
for (ToxFile& f : fileSendQueue)
{
if (f.fileNum == fileNum && f.friendId == friendId)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::cancelFileSend: No such file in queue");
return;
}
2014-06-26 07:48:20 +08:00
file->status = ToxFile::STOPPED;
emit fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
2014-06-26 07:48:20 +08:00
tox_file_send_control(tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
file->sendFuture.waitForFinished(); // Wait until sendAllFileData returns before deleting
removeFileFromQueue(true, friendId, fileNum);
}
void Core::cancelFileRecv(int friendId, int fileNum)
{
ToxFile* file{nullptr};
for (ToxFile& f : fileRecvQueue)
{
if (f.fileNum == fileNum && f.friendId == friendId)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::cancelFileRecv: No such file in queue");
return;
}
file->status = ToxFile::STOPPED;
emit fileTransferCancelled(file->friendId, file->fileNum, ToxFile::RECEIVING);
tox_file_send_control(tox, file->friendId, 1, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
removeFileFromQueue(true, friendId, fileNum);
}
void Core::rejectFileRecvRequest(int friendId, int fileNum)
{
ToxFile* file{nullptr};
for (ToxFile& f : fileRecvQueue)
{
if (f.fileNum == fileNum && f.friendId == friendId)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::rejectFileRecvRequest: No such file in queue");
return;
}
file->status = ToxFile::STOPPED;
emit fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
tox_file_send_control(tox, file->friendId, 1, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
removeFileFromQueue(false, friendId, fileNum);
}
void Core::acceptFileRecvRequest(int friendId, int fileNum, QString path)
{
ToxFile* file{nullptr};
for (ToxFile& f : fileRecvQueue)
{
if (f.fileNum == fileNum && f.friendId == friendId)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::acceptFileRecvRequest: No such file in queue");
return;
}
file->setFilePath(path);
if (!file->open(true))
{
qWarning() << "Core::acceptFileRecvRequest: Unable to open file";
return;
}
file->status = ToxFile::TRANSMITTING;
emit fileTransferAccepted(*file);
tox_file_send_control(tox, file->friendId, 1, file->fileNum, TOX_FILECONTROL_ACCEPT, nullptr, 0);
2014-06-26 07:48:20 +08:00
}
2014-06-25 04:11:11 +08:00
void Core::removeFriend(int friendId)
{
if (tox_del_friend(tox, friendId) == -1) {
emit failedToRemoveFriend(friendId);
} else {
2014-07-04 01:38:30 +08:00
saveConfiguration();
2014-06-25 04:11:11 +08:00
emit friendRemoved(friendId);
}
}
void Core::removeGroup(int groupId)
{
tox_del_groupchat(tox, groupId);
}
QString Core::getUsername()
{
int size = tox_get_self_name_size(tox);
uint8_t* name = new uint8_t[size];
if (tox_get_self_name(tox, name) == size)
return QString(CString::toString(name, size));
else
return QString();
delete[] name;
}
2014-06-25 04:11:11 +08:00
void Core::setUsername(const QString& username)
{
CString cUsername(username);
if (tox_set_name(tox, cUsername.data(), cUsername.size()) == -1) {
emit failedToSetUsername(username);
} else {
2014-07-04 01:38:30 +08:00
saveConfiguration();
2014-06-25 04:11:11 +08:00
emit usernameSet(username);
}
}
QString Core::getStatusMessage()
{
int size = tox_get_self_status_message_size(tox);
uint8_t* name = new uint8_t[size];
if (tox_get_self_status_message(tox, name, size) == size)
return QString(CString::toString(name, size));
else
return QString();
delete[] name;
}
2014-06-25 04:11:11 +08:00
void Core::setStatusMessage(const QString& message)
{
CString cMessage(message);
if (tox_set_status_message(tox, cMessage.data(), cMessage.size()) == -1) {
emit failedToSetStatusMessage(message);
} else {
2014-07-04 01:38:30 +08:00
saveConfiguration();
2014-06-25 04:11:11 +08:00
emit statusMessageSet(message);
}
}
void Core::setStatus(Status status)
{
TOX_USERSTATUS userstatus;
switch (status) {
case Status::Online:
userstatus = TOX_USERSTATUS_NONE;
break;
case Status::Away:
userstatus = TOX_USERSTATUS_AWAY;
break;
case Status::Busy:
userstatus = TOX_USERSTATUS_BUSY;
break;
default:
userstatus = TOX_USERSTATUS_INVALID;
break;
}
if (tox_set_user_status(tox, userstatus) == 0) {
2014-07-04 01:38:30 +08:00
saveConfiguration();
2014-06-25 04:11:11 +08:00
emit statusSet(status);
} else {
emit failedToSetStatus(status);
}
}
void Core::onFileTransferFinished(ToxFile file)
{
if (file.direction == file.SENDING)
emit fileUploadFinished(QString(file.fileName));
else
emit fileDownloadFinished(QString(file.fileName));
}
2014-06-25 04:11:11 +08:00
void Core::bootstrapDht()
{
qDebug() << "Core: Bootstraping DHT";
2014-06-25 04:11:11 +08:00
const Settings& s = Settings::getInstance();
QList<Settings::DhtServer> dhtServerList = s.getDhtServerList();
static int j = 0;
int i=0;
int listSize = dhtServerList.size();
while (i<5)
{
const Settings::DhtServer& dhtServer = dhtServerList[j % listSize];
if (tox_bootstrap_from_address(tox, dhtServer.address.toLatin1().data(),
0, qToBigEndian(dhtServer.port), CUserId(dhtServer.userId).data()) == 1)
qDebug() << QString("Core: Bootstraping from ")+dhtServer.name+QString(", addr ")+dhtServer.address.toLatin1().data()
2014-07-01 16:37:39 +08:00
+QString(", port ")+QString().setNum(dhtServer.port);
else
qDebug() << "Core: Error bootstraping from "+dhtServer.name;
j++;
i++;
2014-06-25 04:11:11 +08:00
}
}
void Core::process()
{
tox_do(tox);
#ifdef DEBUG
//we want to see the debug messages immediately
fflush(stdout);
#endif
checkConnection();
2014-06-28 03:59:25 +08:00
//int toxInterval = tox_do_interval(tox);
//qDebug() << QString("Tox interval %1").arg(toxInterval);
toxTimer->start(50);
2014-06-25 04:11:11 +08:00
}
void Core::checkConnection()
{
static bool isConnected = false;
if (tox_isconnected(tox) && !isConnected) {
qDebug() << "Core: Connected to DHT";
2014-06-25 04:11:11 +08:00
emit connected();
isConnected = true;
} else if (!tox_isconnected(tox) && isConnected) {
qDebug() << "Core: Disconnected to DHT";
2014-06-25 04:11:11 +08:00
emit disconnected();
isConnected = false;
}
}
void Core::loadConfiguration()
{
QString path = Settings::getSettingsDirPath() + '/' + CONFIG_FILE_NAME;
QFile configurationFile(path);
if (!configurationFile.exists()) {
qWarning() << "The Tox configuration file was not found";
return;
}
if (!configurationFile.open(QIODevice::ReadOnly)) {
qCritical() << "File " << path << " cannot be opened";
return;
}
qint64 fileSize = configurationFile.size();
if (fileSize > 0) {
QByteArray data = configurationFile.readAll();
tox_load(tox, reinterpret_cast<uint8_t *>(data.data()), data.size());
}
configurationFile.close();
// set GUI with user and statusmsg
QString name = getUsername();
if (name != "")
emit usernameSet(name);
QString msg = getStatusMessage();
if (msg != "")
emit statusMessageSet(msg);
2014-06-25 04:11:11 +08:00
loadFriends();
}
void Core::saveConfiguration()
{
2014-07-01 03:26:37 +08:00
if (!tox)
{
qWarning() << "Core::saveConfiguration: Tox not started, aborting!";
return;
}
2014-06-25 04:11:11 +08:00
QString path = Settings::getSettingsDirPath();
QDir directory(path);
if (!directory.exists() && !directory.mkpath(directory.absolutePath())) {
qCritical() << "Error while creating directory " << path;
return;
}
path += '/' + CONFIG_FILE_NAME;
QSaveFile configurationFile(path);
if (!configurationFile.open(QIODevice::WriteOnly)) {
qCritical() << "File " << path << " cannot be opened";
return;
}
qDebug() << "Core: writing tox_save";
uint32_t fileSize = tox_size(tox);
if (fileSize > 0 && fileSize <= INT32_MAX) {
uint8_t *data = new uint8_t[fileSize];
tox_save(tox, data);
configurationFile.write(reinterpret_cast<char *>(data), fileSize);
configurationFile.commit();
delete[] data;
}
}
void Core::loadFriends()
{
const uint32_t friendCount = tox_count_friendlist(tox);
if (friendCount > 0) {
// assuming there are not that many friends to fill up the whole stack
int32_t *ids = new int32_t[friendCount];
tox_get_friendlist(tox, ids, friendCount);
uint8_t clientId[TOX_CLIENT_ID_SIZE];
for (int32_t i = 0; i < static_cast<int32_t>(friendCount); ++i) {
if (tox_get_client_id(tox, ids[i], clientId) == 0) {
emit friendAdded(ids[i], CUserId::toString(clientId));
const int nameSize = tox_get_name_size(tox, ids[i]);
if (nameSize > 0) {
uint8_t *name = new uint8_t[nameSize];
if (tox_get_name(tox, ids[i], name) == nameSize) {
emit friendUsernameLoaded(ids[i], CString::toString(name, nameSize));
}
delete[] name;
}
const int statusMessageSize = tox_get_status_message_size(tox, ids[i]);
if (statusMessageSize > 0) {
uint8_t *statusMessage = new uint8_t[statusMessageSize];
if (tox_get_status_message(tox, ids[i], statusMessage, statusMessageSize) == statusMessageSize) {
emit friendStatusMessageLoaded(ids[i], CString::toString(statusMessage, statusMessageSize));
}
delete[] statusMessage;
}
checkLastOnline(ids[i]);
}
}
delete[] ids;
}
}
void Core::checkLastOnline(int friendId) {
const uint64_t lastOnline = tox_get_last_online(tox, friendId);
if (lastOnline > 0) {
emit friendLastSeenChanged(friendId, QDateTime::fromTime_t(lastOnline));
}
}
int Core::getGroupNumberPeers(int groupId) const
{
return tox_group_number_peers(tox, groupId);
}
QString Core::getGroupPeerName(int groupId, int peerId) const
{
QString name;
uint8_t nameArray[TOX_MAX_NAME_LENGTH];
int length = tox_group_peername(tox, groupId, peerId, nameArray);
if (length == -1)
{
qWarning() << "Core::getGroupPeerName: Unknown error";
return name;
}
name = CString::toString(nameArray, length);
return name;
}
QList<QString> Core::getGroupPeerNames(int groupId) const
{
QList<QString> names;
int nPeers = getGroupNumberPeers(groupId);
if (nPeers == -1)
{
qWarning() << "Core::getGroupPeerNames: Unable to get number of peers";
return names;
}
uint8_t namesArray[nPeers][TOX_MAX_NAME_LENGTH];
uint16_t* lengths = new uint16_t[nPeers];
int result = tox_group_get_names(tox, groupId, namesArray, lengths, nPeers);
if (result != nPeers)
{
qWarning() << "Core::getGroupPeerNames: Unexpected result";
return names;
}
for (int i=0; i<nPeers; i++)
names.push_back(CString::toString(namesArray[i], lengths[i]));
return names;
}
2014-07-03 15:22:12 +08:00
int Core::joinGroupchat(int32_t friendnumber, const uint8_t* friend_group_public_key) const
2014-06-25 04:11:11 +08:00
{
qDebug() << QString("Trying to join groupchat invite by friend %1").arg(friendnumber);
2014-06-25 04:11:11 +08:00
return tox_join_groupchat(tox, friendnumber, friend_group_public_key);
}
void Core::quitGroupChat(int groupId) const
{
tox_del_groupchat(tox, groupId);
}
void Core::removeFileFromQueue(bool sendQueue, int friendId, int fileId)
{
bool found = false;
if (sendQueue)
{
for (int i=0; i<fileSendQueue.size();)
{
if (fileSendQueue[i].friendId == friendId && fileSendQueue[i].fileNum == fileId)
{
found = true;
fileSendQueue[i].file->close();
delete fileSendQueue[i].file;
fileSendQueue.removeAt(i);
continue;
}
i++;
}
}
else
{
for (int i=0; i<fileRecvQueue.size();)
{
if (fileRecvQueue[i].friendId == friendId && fileRecvQueue[i].fileNum == fileId)
{
found = true;
fileRecvQueue[i].file->close();
delete fileRecvQueue[i].file;
fileRecvQueue.removeAt(i);
continue;
}
i++;
}
}
if (!found)
qWarning() << "Core::removeFileFromQueue: No such file in queue";
}
void Core::sendAllFileData(Core *core, ToxFile* file)
{
2014-07-01 19:33:59 +08:00
while (file->bytesSent < file->filesize)
{
if (file->status == ToxFile::PAUSED)
{
QThread::sleep(5);
continue;
}
else if (file->status == ToxFile::STOPPED)
{
qWarning("Core::sendAllFileData: File is stopped");
return;
}
emit core->fileTransferInfo(file->friendId, file->fileNum, file->filesize, file->bytesSent, ToxFile::SENDING);
qApp->processEvents();
2014-07-01 19:33:59 +08:00
long long chunkSize = tox_file_data_size(core->tox, file->friendId);
if (chunkSize == -1)
{
qWarning("Core::fileHeartbeat: Error getting preffered chunk size, aborting file send");
file->status = ToxFile::STOPPED;
emit core->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
removeFileFromQueue(true, file->friendId, file->fileNum);
return;
}
2014-07-01 19:33:59 +08:00
chunkSize = std::min(chunkSize, file->filesize);
uint8_t* data = new uint8_t[chunkSize];
file->file->seek(file->bytesSent);
int readSize = file->file->read((char*)data, chunkSize);
if (readSize == -1)
{
qWarning() << QString("Core::sendAllFileData: Error reading from file: %1").arg(file->file->errorString());
delete[] data;
QThread::msleep(5);
continue;
}
else if (readSize == 0)
{
qWarning() << QString("Core::sendAllFileData: Nothing to read from file: %1").arg(file->file->errorString());
delete[] data;
QThread::msleep(5);
continue;
}
if (tox_file_send_data(core->tox, file->friendId, file->fileNum, data, readSize) == -1)
{
2014-06-27 02:13:37 +08:00
//qWarning("Core::fileHeartbeat: Error sending data chunk");
core->process();
delete[] data;
QThread::msleep(5);
continue;
}
delete[] data;
file->bytesSent += readSize;
//qDebug() << QString("Core::fileHeartbeat: sent %1/%2 bytes").arg(file->bytesSent).arg(file->fileData.size());
}
qDebug("Core::fileHeartbeat: Transfer finished");
tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_FINISHED, nullptr, 0);
file->status = ToxFile::STOPPED;
emit core->fileTransferFinished(*file);
removeFileFromQueue(true, file->friendId, file->fileNum);
}
2014-06-27 07:17:10 +08:00
void Core::onAvInvite(int32_t call_index, void* core)
2014-06-27 07:17:10 +08:00
{
int friendId = toxav_get_peer_id(static_cast<Core*>(core)->toxav, call_index, 0);
if (friendId < 0)
{
qWarning() << "Core: Received invalid AV invite";
return;
}
int transType = toxav_get_peer_transmission_type(static_cast<Core*>(core)->toxav, call_index, 0);
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);
}
2014-06-27 07:17:10 +08:00
}
void Core::onAvStart(int32_t call_index, void* core)
2014-06-27 07:17:10 +08:00
{
int friendId = toxav_get_peer_id(static_cast<Core*>(core)->toxav, call_index, 0);
if (friendId < 0)
{
qWarning() << "Core: Received invalid AV start";
return;
}
int transType = toxav_get_peer_transmission_type(static_cast<Core*>(core)->toxav, call_index, 0);
2014-06-30 05:38:48 +08:00
if (transType == TypeVideo)
{
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);
2014-06-30 05:38:48 +08:00
}
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);
2014-06-30 05:38:48 +08:00
}
2014-06-27 07:17:10 +08:00
}
void Core::onAvCancel(int32_t call_index, void* core)
2014-06-27 07:17:10 +08:00
{
int friendId = toxav_get_peer_id(static_cast<Core*>(core)->toxav, call_index, 0);
if (friendId < 0)
{
qWarning() << "Core: Received invalid AV cancel";
return;
}
qDebug() << QString("Core: AV cancel from %1").arg(friendId);
emit static_cast<Core*>(core)->avCancel(friendId, call_index);
2014-06-27 07:17:10 +08:00
}
2014-07-01 04:52:03 +08:00
void Core::onAvReject(int32_t, void*)
2014-06-27 07:17:10 +08:00
{
qDebug() << "Core: AV reject";
}
void Core::onAvEnd(int32_t call_index, void* core)
2014-06-27 07:17:10 +08:00
{
int friendId = toxav_get_peer_id(static_cast<Core*>(core)->toxav, call_index, 0);
if (friendId < 0)
{
qWarning() << "Core: Received invalid AV end";
return;
}
qDebug() << QString("Core: AV end from %1").arg(friendId);
cleanupCall(call_index);
emit static_cast<Core*>(core)->avEnd(friendId, call_index);
2014-06-27 07:17:10 +08:00
}
void Core::onAvRinging(int32_t call_index, void* core)
2014-06-27 07:17:10 +08:00
{
2014-06-27 09:06:56 +08:00
int friendId = toxav_get_peer_id(static_cast<Core*>(core)->toxav, call_index, 0);
if (friendId < 0)
{
qWarning() << "Core: Received invalid AV ringing";
return;
}
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);
}
2014-06-27 07:17:10 +08:00
}
void Core::onAvStarting(int32_t call_index, void* core)
2014-06-27 07:17:10 +08:00
{
2014-06-27 09:06:56 +08:00
int friendId = toxav_get_peer_id(static_cast<Core*>(core)->toxav, call_index, 0);
if (friendId < 0)
{
qWarning() << "Core: Received invalid AV starting";
return;
}
int transType = toxav_get_peer_transmission_type(static_cast<Core*>(core)->toxav, call_index, 0);
2014-06-30 05:38:48 +08:00
if (transType == TypeVideo)
{
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);
2014-06-30 05:38:48 +08:00
}
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);
2014-06-30 05:38:48 +08:00
}
2014-06-27 07:17:10 +08:00
}
void Core::onAvEnding(int32_t call_index, void* core)
2014-06-27 07:17:10 +08:00
{
int friendId = toxav_get_peer_id(static_cast<Core*>(core)->toxav, call_index, 0);
if (friendId < 0)
{
qWarning() << "Core: Received invalid AV ending";
return;
}
qDebug() << QString("Core: AV ending from %1").arg(friendId);
cleanupCall(call_index);
emit static_cast<Core*>(core)->avEnding(friendId, call_index);
2014-06-27 07:17:10 +08:00
}
2014-07-01 04:52:03 +08:00
void Core::onAvError(int32_t, void*)
2014-06-27 07:17:10 +08:00
{
qDebug() << "Core: AV error";
}
void Core::onAvRequestTimeout(int32_t call_index, void* core)
2014-06-27 07:17:10 +08:00
{
2014-06-27 09:06:56 +08:00
int friendId = toxav_get_peer_id(static_cast<Core*>(core)->toxav, call_index, 0);
if (friendId < 0)
{
qWarning() << "Core: Received invalid AV request timeout";
return;
}
qDebug() << QString("Core: AV request timeout with %1").arg(friendId);
2014-07-02 03:16:17 +08:00
cleanupCall(call_index);
2014-06-27 09:06:56 +08:00
emit static_cast<Core*>(core)->avRequestTimeout(friendId, call_index);
2014-06-27 07:17:10 +08:00
}
void Core::onAvPeerTimeout(int32_t call_index, void* core)
2014-06-27 07:17:10 +08:00
{
2014-06-28 03:59:25 +08:00
int friendId = toxav_get_peer_id(static_cast<Core*>(core)->toxav, call_index, 0);
if (friendId < 0)
{
qWarning() << "Core: Received invalid AV peer timeout";
return;
}
2014-06-28 03:59:25 +08:00
qDebug() << QString("Core: AV peer timeout with %1").arg(friendId);
2014-07-02 03:16:17 +08:00
cleanupCall(call_index);
2014-06-28 03:59:25 +08:00
emit static_cast<Core*>(core)->avPeerTimeout(friendId, call_index);
2014-06-27 07:17:10 +08:00
}
void Core::answerCall(int callId)
2014-06-30 05:38:48 +08:00
{
int friendId = toxav_get_peer_id(toxav, callId, 0);
if (friendId < 0)
{
qWarning() << "Core: Received invalid AV answer peer ID";
return;
}
int transType = toxav_get_peer_transmission_type(toxav, callId, 0);
2014-06-30 05:38:48 +08:00
if (transType == TypeVideo)
{
qDebug() << QString("Core: answering call %1 with video").arg(callId);
toxav_answer(toxav, callId, TypeVideo);
}
else
{
qDebug() << QString("Core: answering call %1 without video").arg(callId);
toxav_answer(toxav, callId, TypeAudio);
}
}
void Core::hangupCall(int callId)
{
qDebug() << QString("Core: hanging up call %1").arg(callId);
2014-06-28 03:59:25 +08:00
calls[callId].active = false;
toxav_hangup(toxav, callId);
}
2014-06-27 09:06:56 +08:00
2014-06-30 05:38:48 +08:00
void Core::startCall(int friendId, bool video)
2014-06-27 09:06:56 +08:00
{
int callId;
2014-06-30 05:38:48 +08:00
if (video)
{
qDebug() << QString("Core: Starting new call with %1 with video").arg(friendId);
2014-06-30 05:38:48 +08:00
toxav_call(toxav, &callId, friendId, TypeVideo, TOXAV_RINGING_TIME);
calls[callId].videoEnabled=true;
}
2014-06-30 05:38:48 +08:00
else
{
qDebug() << QString("Core: Starting new call with %1 without video").arg(friendId);
2014-06-30 05:38:48 +08:00
toxav_call(toxav, &callId, friendId, TypeAudio, TOXAV_RINGING_TIME);
calls[callId].videoEnabled=false;
}
2014-06-27 09:06:56 +08:00
}
void Core::cancelCall(int callId, int friendId)
{
qDebug() << QString("Core: Cancelling call with %1").arg(friendId);
2014-06-28 03:59:25 +08:00
calls[callId].active = false;
2014-06-27 09:06:56 +08:00
toxav_cancel(toxav, callId, friendId, 0);
}
2014-06-30 05:38:48 +08:00
void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled)
{
qDebug() << QString("Core: preparing call %1").arg(callId);
calls[callId].callId = callId;
calls[callId].friendId = friendId;
calls[callId].codecSettings = av_DefaultSettings;
2014-07-12 18:50:40 +08:00
calls[callId].codecSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH;
calls[callId].codecSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT;
2014-06-30 05:38:48 +08:00
calls[callId].videoEnabled = videoEnabled;
calls[callId].framesize = (calls[callId].codecSettings.audio_frame_duration * calls[callId].codecSettings.audio_sample_rate) / 1000;;
calls[callId].audio_packet_samples = new int16_t[calls[callId].framesize];
calls[callId].audio_packet_data = new char[calls[callId].framesize * 2];
2014-06-30 05:38:48 +08:00
toxav_prepare_transmission(toxav, callId, &calls[callId].codecSettings, videoEnabled);
2014-06-28 03:59:25 +08:00
// Prepare output
QAudioFormat format;
format.setSampleRate(calls[callId].codecSettings.audio_sample_rate);
format.setChannelCount(calls[callId].codecSettings.audio_channels);
format.setSampleSize(16);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
2014-07-14 21:16:44 +08:00
if (QAudioDeviceInfo::defaultOutputDevice().isFormatSupported(format))
2014-06-28 03:59:25 +08:00
{
2014-07-14 21:16:44 +08:00
qDebug() << "Core: opening output device";
2014-06-28 03:59:25 +08:00
calls[callId].audioOutput = new QAudioOutput(format);
calls[callId].audioOutputProxy = new AudioOutputProxy(); // TODO: init with proper parent
calls[callId].audioOutput->start(calls[callId].audioOutputProxy);
2014-06-29 03:00:11 +08:00
int error = calls[callId].audioOutput->error();
if (error != QAudio::NoError)
{
qWarning() << QString("Core: Error %1 when starting audio output").arg(error);
}
2014-07-14 21:16:44 +08:00
} else {
qFatal("Core: Raw audio format not supported by output backend, cannot play audio.");
}
2014-06-28 03:59:25 +08:00
// Start input
2014-07-14 21:16:44 +08:00
if (QAudioDeviceInfo::defaultInputDevice().isFormatSupported(format))
2014-06-28 03:59:25 +08:00
{
2014-07-14 21:16:44 +08:00
qDebug() << "Core: opening input device";
2014-06-28 03:59:25 +08:00
calls[callId].audioInput = new QAudioInput(format);
calls[callId].audioInputProxy = new AudioInputProxy(); // TODO: init with proper parent
calls[callId].audioInputProxy->callback = [=]() { Core::sendCallAudio(callId, toxav); };
calls[callId].audioInput->start(calls[callId].audioInputProxy);
int error = calls[callId].audioInput->error();
if (error != QAudio::NoError)
{
qWarning() << QString("Core: Error %1 when starting audio input").arg(error);
}
2014-07-14 21:16:44 +08:00
} else {
qFatal("Default input format not supported, cannot record audio");
2014-06-28 03:59:25 +08:00
}
// Go
2014-06-30 20:49:42 +08:00
calls[callId].active = true;
if (calls[callId].videoEnabled)
{
calls[callId].sendVideoTimer->setInterval(50);
calls[callId].sendVideoTimer->setSingleShot(true);
calls[callId].sendVideoTimer->start();
2014-06-30 20:49:42 +08:00
Widget::getInstance()->getCamera()->suscribe();
}
}
void Core::cleanupCall(int callId)
{
qDebug() << QString("Core: cleaning up call %1").arg(callId);
calls[callId].active = false;
calls[callId].sendVideoTimer->stop();
if (calls[callId].audioOutput != nullptr)
{
2014-07-09 16:37:46 +08:00
calls[callId].audioOutput->stop();
2014-07-14 21:16:44 +08:00
delete calls[callId].audioOutput;
calls[callId].audioOutput = nullptr;
}
if (calls[callId].audioInput != nullptr)
{
2014-07-09 16:37:46 +08:00
calls[callId].audioInput->stop();
2014-07-14 21:16:44 +08:00
delete calls[callId].audioInput;
calls[callId].audioInput = nullptr;
}
2014-06-30 20:49:42 +08:00
if (calls[callId].videoEnabled)
Widget::getInstance()->getCamera()->unsuscribe();
2014-07-14 21:16:44 +08:00
calls[callId].audioInputProxy->deleteLater();
calls[callId].audioInputProxy = nullptr; // TODO: test that harakiri here goes well
2014-07-14 21:16:44 +08:00
calls[callId].audioOutputProxy->deleteLater();
calls[callId].audioOutputProxy = nullptr; // and here too
calls[callId].framesize = 0;
delete [] calls[callId].audio_packet_samples; calls[callId].audio_packet_samples = nullptr;
delete [] calls[callId].audio_packet_data; calls[callId].audio_packet_data = nullptr;
}
void Core::playCallAudio(ToxAv*, int32_t callId, int16_t *data, int length)
{
2014-07-09 16:37:46 +08:00
if (!calls[callId].active)
return;
calls[callId].audioOutputProxy->push(data, length);
2014-07-14 21:16:44 +08:00
// TODO: we can subscribes
int state = calls[callId].audioOutput->state();
2014-07-14 21:16:44 +08:00
if (state == QAudio::ActiveState) {
// everything is fine
} else if (state == QAudio::SuspendedState) {
calls[callId].audioOutput->resume();
qWarning()<< "Core::playCallAudio() audioOutput state is suspended";
} else if (state == QAudio::IdleState) {
// qWarning() << "Core::playCallAudio() audioOutput state is idle";
} else if (state == QAudio::StoppedState) {
qWarning() << "Core::playCallAudio() audioOutput state is stopped";
} else {
2014-07-14 21:16:44 +08:00
qWarning() << "Core::playCallAudio() audioOutput state is unknown: " << state;
}
int error = calls[callId].audioOutput->error(); // TODO: we can subscribe
if (error == QAudio::NoError) {
// (^__^)
} else if (error == QAudio::OpenError) {
qWarning() << "Core::playCallAudio() audioOutput OpenError";
} else if (error == QAudio::IOError) {
qWarning() << "Core::playCallAudio() audioOutput IOError";
} else if (error == QAudio::UnderrunError) {
2014-07-14 21:16:44 +08:00
// qWarning() << "Core::playCallAudio() audioOutput UnderrunError";
} else if (error == QAudio::FatalError) {
qWarning() << "Core::playCallAudio() audioOutput FatalError";
} else {
qWarning() << "Core::playCallAudio() unknown error: " << error;
}
}
void Core::sendCallAudio(int callId, ToxAv* toxav)
{
if (!calls[callId].active)
return;
while (true) {
auto samplesReady = calls[callId].audioInputProxy->readSpace();
if (samplesReady < calls[callId].framesize) {
qDebug() << "not enough samples ready" << samplesReady << "of" << calls[callId].framesize;
return;
}
calls[callId].audioInputProxy->pull(calls[callId].audio_packet_samples, calls[callId].framesize);
2014-07-14 21:16:44 +08:00
int result = toxav_prepare_audio_frame(toxav, callId,
(uint8_t*)calls[callId].audio_packet_data,
calls[callId].framesize*2,
calls[callId].audio_packet_samples,
calls[callId].framesize);
if (result < 0) {
qWarning() << QString("Core: Unable to prepare audio frame, error %1").arg(result);
return;
}
result = toxav_send_audio(toxav, callId, (uint8_t*)calls[callId].audio_packet_data, result);
if (result < 0) {
qWarning() << QString("Core: Unable to send audio frame, error %1").arg(result);
}
}
}
2014-07-09 15:55:25 +08:00
void Core::playCallVideo(ToxAv*, int32_t callId, vpx_image_t* img)
{
if (!calls[callId].active || !calls[callId].videoEnabled)
return;
2014-07-12 20:52:01 +08:00
if (videoBusyness >= 1)
qWarning() << "Core: playCallVideo: Busy, dropping current frame";
else
emit Widget::getInstance()->getCore()->videoFrameReceived(img);
vpx_img_free(img);
}
2014-06-30 20:49:42 +08:00
void Core::sendCallVideo(int callId)
2014-06-30 05:38:48 +08:00
{
2014-06-30 20:49:42 +08:00
if (!calls[callId].active || !calls[callId].videoEnabled)
return;
vpx_image frame = camera->getLastVPXImage();
if (frame.w && frame.h)
2014-06-30 05:38:48 +08:00
{
2014-06-30 20:49:42 +08:00
int result;
2014-07-12 18:50:40 +08:00
if((result = toxav_prepare_video_frame(toxav, callId, videobuf, videobufsize, &frame)) < 0)
2014-06-30 20:49:42 +08:00
{
qDebug() << QString("Core: toxav_prepare_video_frame: error %1").arg(result);
2014-07-12 18:50:40 +08:00
vpx_img_free(&frame);
calls[callId].sendVideoTimer->start();
2014-06-30 20:49:42 +08:00
return;
}
if((result = toxav_send_video(toxav, callId, (uint8_t*)videobuf, result)) < 0)
qDebug() << QString("Core: toxav_send_video error: %1").arg(result);
2014-07-12 18:50:40 +08:00
vpx_img_free(&frame);
2014-06-30 05:38:48 +08:00
}
2014-06-30 20:49:42 +08:00
else
qDebug("Core::sendCallVideo: Invalid frame (bad camera ?)");
2014-06-30 05:38:48 +08:00
calls[callId].sendVideoTimer->start();
2014-06-30 05:38:48 +08:00
}
void Core::groupInviteFriend(int friendId, int groupId)
{
tox_invite_friend(tox, friendId, groupId);
}
2014-06-30 05:38:48 +08:00
void Core::createGroup()
{
emit emptyGroupCreated(tox_add_groupchat(tox));
}
void Core::increaseVideoBusyness()
{
videoBusyness++;
}
void Core::decreaseVideoBusyness()
{
videoBusyness--;
}