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

Merge pull request #1 from tux3/master

Merged back into main repo
This commit is contained in:
F1ynn 2014-06-27 11:37:56 -07:00
commit ba7f1d337a
9 changed files with 234 additions and 13 deletions

View File

@ -10,6 +10,7 @@ However, it is not a fork.
- Friends chat
- Group chats (experimental, can only accept invitations)
- File transfers, with previewing of images
- Audio calls (hearing only, not talking at the moment)
<h2>Requirements</h2>
@ -19,5 +20,6 @@ Linux and Mac users will have compile the source code themselves.
<a href="https://jenkins.libtoxcore.so/job/tux3-toxgui-win32/lastSuccessfulBuild/artifact/toxgui-win32.zip">Windows download</a>
<h3>Screenshots</h3>
<h5>Note: The screenshots may not always be up to date, but they should give a good idea of the general look and features</h5>
<img src="http://i.imgur.com/eMxaxib.png"/>
<img src="http://i.imgur.com/66ARBGC.png"/>

41
audiobuffer.cpp Normal file
View File

@ -0,0 +1,41 @@
#include "audiobuffer.h"
AudioBuffer::AudioBuffer() :
QIODevice(0)
{
open(QIODevice::ReadOnly);
}
AudioBuffer::~AudioBuffer()
{
close();
}
qint64 AudioBuffer::readData(char *data, qint64 len)
{
const qint64 total = qMin((qint64)buffer.size(), len);
memcpy(data, buffer.constData(), total);
buffer = buffer.mid(total);
return total;
}
qint64 AudioBuffer::writeData(const char* data, qint64 len)
{
buffer.append(data, len);
return 0;
}
qint64 AudioBuffer::bytesAvailable() const
{
return buffer.size() + QIODevice::bytesAvailable();
}
qint64 AudioBuffer::bufferSize() const
{
return buffer.size();
}
void AudioBuffer::clear()
{
buffer.clear();
}

24
audiobuffer.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef AUDIOBUFFER_H
#define AUDIOBUFFER_H
#include <QIODevice>
#include <QByteArray>
class AudioBuffer : public QIODevice
{
Q_OBJECT
public:
explicit AudioBuffer();
~AudioBuffer();
qint64 readData(char *data, qint64 maxlen);
qint64 writeData(const char *data, qint64 len);
qint64 bytesAvailable() const;
qint64 bufferSize() const;
void clear();
private:
QByteArray buffer;
};
#endif // AUDIOBUFFER_H

119
core.cpp
View File

@ -17,6 +17,7 @@
#include "core.h"
#include "cdata.h"
#include "cstring.h"
#include "settings.h"
#include <QDebug>
#include <QDir>
@ -27,18 +28,10 @@
#include <QThread>
#include <QtConcurrent/QtConcurrent>
#include "settings.h"
#define GROUPCHAT_MAX_SIZE 32
#define TOX_SAVE_INTERVAL 30*1000
#define TOX_FILE_INTERVAL 20
#define TOX_BOOTSTRAP_INTERVAL 10*1000
#define TOXAV_MAX_CALLS 32
#define TOXAV_RINGING_TIME 15
const QString Core::CONFIG_FILE_NAME = "tox_save";
QList<ToxFile> Core::fileSendQueue;
QList<ToxFile> Core::fileRecvQueue;
ToxCall Core::calls[TOXAV_MAX_CALLS];
Core::Core() :
tox(nullptr)
@ -629,7 +622,9 @@ void Core::process()
fflush(stdout);
#endif
checkConnection();
toxTimer->start(tox_do_interval(tox));
int toxInterval = tox_do_interval(tox);
//qDebug() << QString("Tox interval %1").arg(toxInterval);
toxTimer->start(50);
}
void Core::checkConnection()
@ -899,6 +894,8 @@ void Core::onAvStart(int32_t call_index, void* core)
}
qDebug() << QString("Core: AV start from %1").arg(friendId);
prepareCall(friendId, call_index, static_cast<Core*>(core)->toxav);
emit static_cast<Core*>(core)->avStart(friendId, call_index);
}
@ -930,6 +927,8 @@ void Core::onAvEnd(int32_t call_index, void* core)
}
qDebug() << QString("Core: AV end from %1").arg(friendId);
cleanupCall(call_index);
emit static_cast<Core*>(core)->avEnd(friendId, call_index);
}
@ -956,6 +955,8 @@ void Core::onAvStarting(int32_t call_index, void* core)
}
qDebug() << QString("Core: AV starting %1").arg(friendId);
prepareCall(friendId, call_index, static_cast<Core*>(core)->toxav);
emit static_cast<Core*>(core)->avStarting(friendId, call_index);
}
@ -969,6 +970,8 @@ void Core::onAvEnding(int32_t call_index, void* core)
}
qDebug() << QString("Core: AV ending from %1").arg(friendId);
cleanupCall(call_index);
emit static_cast<Core*>(core)->avEnding(friendId, call_index);
}
@ -1019,3 +1022,99 @@ void Core::cancelCall(int callId, int friendId)
qDebug() << QString("Core: Cancelling call with %1").arg(friendId);
toxav_cancel(toxav, callId, friendId, 0);
}
void Core::prepareCall(int friendId, int callId, ToxAv* toxav)
{
qDebug() << QString("Core: preparing call %1").arg(callId);
calls[callId].callId = callId;
calls[callId].friendId = friendId;
calls[callId].codecSettings = av_DefaultSettings;
toxav_prepare_transmission(toxav, callId, &calls[callId].codecSettings, false);
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);
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
if (!info.isFormatSupported(format)) {
calls[callId].audioOutput = nullptr;
qWarning() << "Core: Raw audio format not supported by backend, cannot play audio.";
return;
}
calls[callId].audioOutput = new QAudioOutput(format);
calls[callId].active = true;
calls[callId].audioOutput->setBufferSize(24000);
calls[callId].audioOutput->start(&calls[callId].audioBuffer);
if (calls[callId].audioOutput->state() == QAudio::StoppedState
&& calls[callId].audioOutput->error() == QAudio::OpenError)
{
qWarning() << "Core: Unable to start audio";
}
else
qDebug() << QString("Core: Audio started, buffer size %1").arg(calls[callId].audioOutput->bufferSize());
calls[callId].playFuture = QtConcurrent::run(playCallAudio, callId, toxav);
}
void Core::cleanupCall(int callId)
{
qDebug() << QString("Core: cleaning up call %1").arg(callId);
calls[callId].active = false;
calls[callId].playFuture.waitForFinished();
if (calls[callId].audioOutput != nullptr)
{
delete calls[callId].audioOutput;
}
calls[callId].audioBuffer.clear();
}
void Core::playCallAudio(int callId, ToxAv* toxav)
{
while (calls[callId].active)
{
int framesize = (calls[callId].codecSettings.audio_frame_duration * calls[callId].codecSettings.audio_sample_rate) / 1000;
uint8_t buf[framesize*2];
int len = toxav_recv_audio(toxav, callId, framesize, (int16_t*)buf);
if (len < 0)
{
if (len == -3) // Not in call !
{
qWarning("Core: Trying to play audio in an inactive call!");
return;
}
qDebug() << QString("Core::playCallAudio: Error receiving audio: %1").arg(len);
QThread::msleep(5);
continue;
}
if (len == 0)
{
qApp->processEvents();
QThread::msleep(5);
continue;
}
//qDebug() << QString("Core: Received %1 bytes, %2 audio bytes free, %3 core buffer size")
//.arg(len*2).arg(calls[callId].audioOutput->bytesFree()).arg(calls[callId].audioBuffer.bufferSize());
calls[callId].audioBuffer.writeData((char*)buf, len*2);
int state = calls[callId].audioOutput->state();
if (state != QAudio::ActiveState)
{
qDebug() << QString("Core: Audio state is %1").arg(state);
}
int error = calls[callId].audioOutput->error();
if (error != QAudio::NoError)
qWarning() << QString("Core::playCallAudio: Error: %1").arg(error);
QThread::msleep(5);
}
}
void Core::sendCallAudio(int callId, ToxAv* toxav)
{
while (calls[callId].active)
{
QThread::msleep(calls[callId].codecSettings.audio_frame_duration / 2);
}
}

28
core.h
View File

@ -18,6 +18,7 @@
#define CORE_HPP
#include "status.h"
#include "audiobuffer.h"
#include <tox/tox.h>
#include <tox/toxav.h>
@ -30,6 +31,15 @@
#include <QList>
#include <QByteArray>
#include <QFuture>
#include <QBuffer>
#include <QAudioOutput>
#define TOXAV_MAX_CALLS 16
#define GROUPCHAT_MAX_SIZE 32
#define TOX_SAVE_INTERVAL 30*1000
#define TOX_FILE_INTERVAL 20
#define TOX_BOOTSTRAP_INTERVAL 10*1000
#define TOXAV_RINGING_TIME 15
struct DhtServer
{
@ -70,6 +80,18 @@ struct ToxFile
QFuture<void> sendFuture;
};
struct ToxCall
{
public:
AudioBuffer audioBuffer;
QAudioOutput* audioOutput;
ToxAvCodecSettings codecSettings;
int callId;
int friendId;
bool active;
QFuture<void> playFuture;
};
class Core : public QObject
{
Q_OBJECT
@ -208,6 +230,11 @@ private:
static void onAvRequestTimeout(int32_t call_index, void* toxav);
static void onAvPeerTimeout(int32_t call_index, void* toxav);
static void prepareCall(int friendId, int callId, ToxAv *toxav);
static void cleanupCall(int callId);
static void playCallAudio(int callId, ToxAv* toxav); // Blocking, start in a thread
static void sendCallAudio(int callId, ToxAv* toxav); // Blocking, start in a thread
void checkConnection();
void onBootstrapTimer();
@ -227,6 +254,7 @@ private:
QList<DhtServer> dhtServerList;
int dhtServerId;
static QList<ToxFile> fileSendQueue, fileRecvQueue;
static ToxCall calls[TOXAV_MAX_CALLS];
static const QString CONFIG_FILE_NAME;
};

View File

@ -6,6 +6,9 @@ QList<Friend*> FriendList::friendList;
Friend* FriendList::addFriend(int friendId, QString userId)
{
for (Friend* f : friendList)
if (f->friendId == friendId)
qWarning() << "FriendList::addFriend: friendId already taken";
Friend* newfriend = new Friend(friendId, userId);
friendList.append(newfriend);
return newfriend;

View File

@ -33,3 +33,21 @@ int main(int argc, char *argv[])
* An extra side panel for groupchats, like Venom does (?)
*
*/
/** NAMES :
Botox
Ricin
Anthrax
Sarin
Cyanide
Polonium
Mercury
Arsenic
qTox
plague
Britney
Nightshade
Belladonna
toxer
GoyIM
*/

View File

@ -4,7 +4,7 @@
#
#-------------------------------------------------
QT += core gui
QT += core gui multimedia
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
@ -33,7 +33,8 @@ HEADERS += widget/form/addfriendform.h \
core.h \
friendlist.h \
cdata.h \
cstring.h
cstring.h \
audiobuffer.h
FORMS += widget.ui
@ -68,4 +69,5 @@ SOURCES += \
settings.cpp \
status.cpp \
cdata.cpp \
cstring.cpp
cstring.cpp \
audiobuffer.cpp

View File

@ -383,6 +383,8 @@ void Widget::onFriendRequestReceived(const QString& userId, const QString& messa
void Widget::removeFriend(int friendId)
{
Friend* f = FriendList::findFriend(friendId);
if (f->widget == activeFriendWidget)
activeFriendWidget = nullptr;
FriendList::removeFriend(friendId);
core->removeFriend(friendId);
delete f;
@ -449,6 +451,8 @@ void Widget::onGroupWidgetClicked(GroupWidget* widget)
void Widget::removeGroup(int groupId)
{
Group* g = GroupList::findGroup(groupId);
if (g->widget == activeGroupWidget)
activeGroupWidget == nullptr;
GroupList::removeGroup(groupId);
core->removeGroup(groupId);
delete g;