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

Strip out old AV code

This commit is contained in:
tux3 2015-06-26 13:00:16 +02:00
parent bcca4537d5
commit dbbc702c60
16 changed files with 220 additions and 510 deletions

View File

@ -29,10 +29,12 @@
#include "audio.h"
#include "src/core/core.h"
#include "src/persistence/settings.h"
#include "src/core/coreav.h"
#include <QDebug>
#include <QTimer>
#include <QThread>
#include <QMutexLocker>
#include <cassert>
@ -110,7 +112,7 @@ void Audio::setOutputVolume(qreal volume)
outputVolume = volume;
alSourcef(alMainSource, AL_GAIN, outputVolume);
for (const ToxGroupCall& call : Core::groupCalls)
for (const ToxGroupCall& call : CoreAV::groupCalls)
{
if (!call.active)
continue;
@ -118,7 +120,7 @@ void Audio::setOutputVolume(qreal volume)
alSourcef(source, AL_GAIN, outputVolume);
}
for (const ToxCall& call : Core::calls)
for (const ToxCall& call : CoreAV::calls)
{
if (!call.active)
continue;
@ -193,10 +195,11 @@ void Audio::openInput(const QString& inDevDescr)
}
alInDev = nullptr;
int stereoFlag = av_DefaultSettings.audio_channels==1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
const uint32_t sampleRate = av_DefaultSettings.audio_sample_rate;
const uint16_t frameDuration = av_DefaultSettings.audio_frame_duration;
const uint32_t chnls = av_DefaultSettings.audio_channels;
/// TODO: Try to actually detect if our audio source is stereo
int stereoFlag = DefaultSettings::audioChannels ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
const uint32_t sampleRate = DefaultSettings::sampleRate;
const uint16_t frameDuration = DefaultSettings::frameDuration;
const uint32_t chnls = DefaultSettings::audioChannels;
const ALCsizei bufSize = (frameDuration * sampleRate * 4) / 1000 * chnls;
if (inDevDescr.isEmpty())
alInDev = alcCaptureOpenDevice(nullptr, sampleRate, stereoFlag, bufSize);
@ -211,7 +214,7 @@ void Audio::openInput(const QString& inDevDescr)
Core* core = Core::getInstance();
if (core)
core->resetCallSources(); // Force to regen each group call's sources
CoreAV::resetCallSources(); // Force to regen each group call's sources
// Restart the capture if necessary
if (alInDev)
@ -269,7 +272,7 @@ bool Audio::openOutput(const QString &outDevDescr)
Core* core = Core::getInstance();
if (core)
core->resetCallSources(); // Force to regen each group call's sources
CoreAV::resetCallSources(); // Force to regen each group call's sources
return true;
}
@ -371,7 +374,7 @@ void Audio::playMono16Sound(const QByteArray& data)
The first and last argument are ignored, but allow direct compatibility with toxcore.
*/
void Audio::playGroupAudioQueued(Tox*,int group, int peer, const int16_t* data,
void Audio::playGroupAudioQueued(void*,int group, int peer, const int16_t* data,
unsigned samples, uint8_t channels, unsigned sample_rate, void* core)
{
QMetaObject::invokeMethod(instance, "playGroupAudio", Qt::BlockingQueuedConnection,
@ -391,7 +394,7 @@ void Audio::playGroupAudio(int group, int peer, const int16_t* data,
assert(QThread::currentThread() == audioThread);
QMutexLocker lock(&audioOutLock);
ToxGroupCall& call = Core::groupCalls[group];
ToxGroupCall& call = CoreAV::groupCalls[group];
if (!call.active || call.muteVol)
return;
@ -482,7 +485,7 @@ bool Audio::tryCaptureSamples(uint8_t* buf, int framesize)
if (samples < framesize)
return false;
memset(buf, 0, framesize * 2 * av_DefaultSettings.audio_channels); // Avoid uninitialized values (Valgrind)
memset(buf, 0, framesize * 2 * DefaultSettings::audioChannels); // Avoid uninitialized values (Valgrind)
alcCaptureSamples(Audio::alInDev, buf, framesize);
if (inputVolume != 1)

View File

@ -22,6 +22,7 @@
#define AUDIO_H
#include <QObject>
#include <QHash>
#include <QMutexLocker>
#include <atomic>
#include <cmath>
@ -34,7 +35,11 @@
#include <AL/alc.h>
#endif
class QString;
class QByteArray;
class QTimer;
class QThread;
class QMutex;
struct Tox;
class AudioFilterer;
@ -64,7 +69,7 @@ public:
void playMono16Sound(const QByteArray& data);
bool tryCaptureSamples(uint8_t* buf, int framesize);
static void playGroupAudioQueued(Tox*, int group, int peer, const int16_t* data,
static void playGroupAudioQueued(void *, int group, int peer, const int16_t* data,
unsigned samples, uint8_t channels, unsigned sample_rate, void*);
#ifdef QTOX_FILTER_AUDIO
@ -102,6 +107,12 @@ private:
ALuint alMainSource;
ALCcontext* alContext;
QTimer* timer;
struct DefaultSettings {
static constexpr int sampleRate = 48000;
static constexpr int frameDuration = 20;
static constexpr int audioChannels = 1;
};
};
#endif // AUDIO_H

View File

@ -22,6 +22,7 @@
#include "src/nexus.h"
#include "src/core/cdata.h"
#include "src/core/cstring.h"
#include "src/core/coreav.h"
#include "src/persistence/settings.h"
#include "src/widget/gui.h"
#include "src/persistence/historykeeper.h"
@ -33,6 +34,7 @@
#include "src/video/camerasource.h"
#include <tox/tox.h>
#include <tox/toxav.h>
#include <ctime>
#include <cassert>
@ -54,32 +56,23 @@
const QString Core::CONFIG_FILE_NAME = "data";
const QString Core::TOX_EXT = ".tox";
QHash<int, ToxGroupCall> Core::groupCalls;
QThread* Core::coreThread{nullptr};
#define MAX_GROUP_MESSAGE_LEN 1024
Core::Core(QThread *CoreThread, Profile& profile) :
tox(nullptr), toxav(nullptr), profile(profile), ready{false}
tox(nullptr), toxav(nullptr), av(new CoreAV), profile(profile), ready{false}
{
coreThread = CoreThread;
Audio::getInstance();
videobuf = nullptr;
toxTimer = new QTimer(this);
toxTimer->setSingleShot(true);
connect(toxTimer, &QTimer::timeout, this, &Core::process);
connect(&Settings::getInstance(), &Settings::dhtServerListChanged, this, &Core::process);
for (int i=0; i<TOXAV_MAX_CALLS;i++)
{
calls[i].active = false;
calls[i].alSource = 0;
calls[i].sendAudioTimer = new QTimer();
calls[i].sendAudioTimer->moveToThread(coreThread);
}
}
void Core::deadifyTox()
@ -113,18 +106,10 @@ Core::~Core()
coreThread->wait(500);
}
for (ToxCall call : calls)
{
if (!call.active)
continue;
hangupCall(call.callId);
}
delete av;
deadifyTox();
delete[] videobuf;
videobuf=nullptr;
Audio& audio = Audio::getInstance();
audio.closeInput();
audio.closeOutput();
@ -135,6 +120,11 @@ Core* Core::getInstance()
return Nexus::getCore();
}
const CoreAV *Core::getAv() const
{
return av;
}
void Core::makeTox(QByteArray savedata)
{
// IPv6 needed for LAN discovery, but can crash some weird routers. On by default, can be disabled in options.
@ -233,7 +223,7 @@ void Core::makeTox(QByteArray savedata)
return;
}
toxav = toxav_new(tox, TOXAV_MAX_CALLS);
toxav = toxav_new(tox, nullptr);
if (toxav == nullptr)
{
qCritical() << "Toxav core failed to start";
@ -310,20 +300,6 @@ void Core::start()
tox_callback_file_recv_chunk(tox, CoreFile::onFileRecvChunkCallback, this);
tox_callback_file_recv_control(tox, CoreFile::onFileControlCallback, this);
toxav_register_callstate_callback(toxav, onAvInvite, av_OnInvite, this);
toxav_register_callstate_callback(toxav, onAvStart, av_OnStart, this);
toxav_register_callstate_callback(toxav, onAvCancel, av_OnCancel, this);
toxav_register_callstate_callback(toxav, onAvReject, av_OnReject, this);
toxav_register_callstate_callback(toxav, onAvEnd, av_OnEnd, this);
toxav_register_callstate_callback(toxav, onAvRinging, av_OnRinging, this);
toxav_register_callstate_callback(toxav, onAvMediaChange, av_OnPeerCSChange, this);
toxav_register_callstate_callback(toxav, onAvMediaChange, av_OnSelfCSChange, this);
toxav_register_callstate_callback(toxav, onAvRequestTimeout, av_OnRequestTimeout, this);
toxav_register_callstate_callback(toxav, onAvPeerTimeout, av_OnPeerTimeout, this);
toxav_register_audio_callback(toxav, playCallAudio, this);
toxav_register_video_callback(toxav, playCallVideo, this);
HistoryKeeper::getInstance()->importAvatarToDatabase(getSelfId().toString().left(64));
QPixmap pic = Settings::getInstance().getSavedAvatar(getSelfId().toString());
if (!pic.isNull() && !pic.size().isEmpty())
@ -374,7 +350,7 @@ void Core::process()
static int tolerance = CORE_DISCONNECT_TOLERANCE;
tox_iterate(tox);
toxav_do(toxav);
toxav_iterate(toxav); ///< TODO: This is best called in a separate thread, as per the doc.
#ifdef DEBUG
//we want to see the debug messages immediately
@ -391,7 +367,7 @@ void Core::process()
tolerance = 3*CORE_DISCONNECT_TOLERANCE;
}
unsigned sleeptime = qMin(tox_iteration_interval(tox), toxav_do_interval(toxav));
unsigned sleeptime = qMin(tox_iteration_interval(tox), toxav_iteration_interval(toxav));
sleeptime = qMin(sleeptime, CoreFile::corefileIterationInterval());
toxTimer->start(sleeptime);
}
@ -754,8 +730,8 @@ void Core::removeGroup(int groupId, bool fake)
tox_del_groupchat(tox, groupId);
if (groupCalls[groupId].active)
leaveGroupCall(groupId);
if (av->groupCalls[groupId].active)
av->leaveGroupCall(groupId);
}
QString Core::getUsername() const
@ -1078,11 +1054,6 @@ void Core::createGroup(uint8_t type)
}
}
bool Core::isGroupAvEnabled(int groupId)
{
return tox_group_get_type(tox, groupId) == TOX_GROUPCHAT_TYPE_AV;
}
bool Core::isFriendOnline(uint32_t friendId) const
{
return tox_friend_get_connection_status(tox, friendId, nullptr) != TOX_CONNECTION_NONE;
@ -1235,28 +1206,6 @@ void Core::setNospam(uint32_t nospam)
tox_self_set_nospam(tox, nospam);
}
void Core::resetCallSources()
{
for (ToxGroupCall& call : groupCalls)
{
for (ALuint source : call.alSources)
alDeleteSources(1, &source);
call.alSources.clear();
}
for (ToxCall& call : calls)
{
if (call.active && call.alSource)
{
ALuint tmp = call.alSource;
call.alSource = 0;
alDeleteSources(1, &tmp);
alGenSources(1, &call.alSource);
}
}
}
void Core::killTimers(bool onlyStop)
{
assert(QThread::currentThread() == coreThread);

View File

@ -29,7 +29,6 @@
#include <tox/toxencryptsave.h>
#include "corestructs.h"
#include "coreav.h"
#include "coredefines.h"
#include "toxid.h"
@ -38,11 +37,9 @@ template <typename T> class QList;
class QTimer;
class QString;
class CString;
class VideoSource;
class VideoFrame;
#ifdef QTOX_FILTER_AUDIO
class AudioFilterer;
#endif
struct ToxAV;
class CoreAV;
struct vpx_image;
class Core : public QObject
{
@ -50,6 +47,7 @@ class Core : public QObject
public:
explicit Core(QThread* coreThread, Profile& profile);
static Core* getInstance(); ///< Returns the global widget's Core instance
const CoreAV* getAv() const;
~Core();
static const QString TOX_EXT;
@ -88,13 +86,8 @@ public:
static QByteArray decryptData(const QByteArray& data, const TOX_PASS_KEY &encryptionKey);
static QByteArray decryptData(const QByteArray& data); ///< Uses the default profile's key
VideoSource* getVideoSourceFromCall(int callNumber); ///< Get a call's video source
static bool anyActiveCalls(); ///< true is any calls are currently active (note: a call about to start is not yet active)
bool isReady(); ///< Most of the API shouldn't be used until Core is ready, call start() first
void resetCallSources(); ///< Forces to regenerate each call's audio sources
public slots:
void start(); ///< Initializes the core, must be called before anything else
void reset(); ///< Reinitialized the core. Must be called from the Core thread, with the GUI thread ready to process events.
@ -132,27 +125,8 @@ public slots:
void pauseResumeFileSend(uint32_t friendId, uint32_t fileNum);
void pauseResumeFileRecv(uint32_t friendId, uint32_t fileNum);
void answerCall(int callId);
void rejectCall(int callId);
void hangupCall(int callId);
void startCall(uint32_t friendId, bool video=false);
void cancelCall(int callId, uint32_t friendId);
void micMuteToggle(int callId);
void volMuteToggle(int callId);
void setNospam(uint32_t nospam);
bool isGroupAvEnabled(int groupId); ///< True for AV groups, false for text-only groups
static void joinGroupCall(int groupId); ///< Starts a call in an existing AV groupchat. Call from the GUI thread.
static void leaveGroupCall(int groupId); ///< Will not leave the group, just stop the call. Call from the GUI thread.
static void disableGroupCallMic(int groupId);
static void disableGroupCallVol(int groupId);
static void enableGroupCallMic(int groupId);
static void enableGroupCallVol(int groupId);
static bool isGroupCallMicEnabled(int groupId);
static bool isGroupCallVolEnabled(int groupId);
signals:
void connected();
@ -256,28 +230,6 @@ private:
const uint8_t* title, uint8_t len, void* _core);
static void onReadReceiptCallback(Tox *tox, uint32_t friendId, uint32_t receipt, void *core);
static void onAvInvite(void* toxav, int32_t call_index, void* core);
static void onAvStart(void* toxav, int32_t call_index, void* core);
static void onAvCancel(void* toxav, int32_t call_index, void* core);
static void onAvReject(void* toxav, int32_t call_index, void* core);
static void onAvEnd(void* toxav, int32_t call_index, void* core);
static void onAvRinging(void* toxav, int32_t call_index, void* core);
static void onAvRequestTimeout(void* toxav, int32_t call_index, void* core);
static void onAvPeerTimeout(void* toxav, int32_t call_index, void* core);
static void onAvMediaChange(void *toxav, int32_t call_index, void* core);
static void sendGroupCallAudio(int groupId, ToxAv* toxav);
static void prepareCall(uint32_t friendId, int callId, ToxAv *toxav, bool videoEnabled);
static void cleanupCall(int callId);
static void playCallAudio(void *toxav, int32_t callId, const int16_t *data,
uint16_t samples, void *user_data); // Callback
static void sendCallAudio(int callId, ToxAv* toxav);
static void playAudioBuffer(ALuint alSource, const int16_t *data, int samples,
unsigned channels, int sampleRate);
static void playCallVideo(void *toxav, int32_t callId, const vpx_image_t* img, void *user_data);
static void sendCallVideo(int callId, ToxAv* toxav, std::shared_ptr<VideoFrame> frame);
bool checkConnection();
void checkEncryptedHistory();
@ -293,24 +245,18 @@ private slots:
private:
Tox* tox;
ToxAv* toxav;
ToxAV* toxav;
CoreAV* av;
QTimer *toxTimer;
Profile& profile;
static ToxCall calls[TOXAV_MAX_CALLS];
#ifdef QTOX_FILTER_AUDIO
static AudioFilterer * filterer[TOXAV_MAX_CALLS];
#endif
static QHash<int, ToxGroupCall> groupCalls; // Maps group IDs to ToxGroupCalls
QMutex messageSendMutex;
bool ready;
static const int videobufsize;
static uint8_t* videobuf;
static QThread *coreThread;
friend class Audio; ///< Audio can access our calls directly to reduce latency
friend class CoreFile; ///< CoreFile can access tox* and emit our signals
friend class CoreAV; ///< CoreAV accesses our toxav* for now
};
#endif // CORE_HPP

View File

@ -19,6 +19,7 @@
*/
#include "core.h"
#include "coreav.h"
#include "src/video/camerasource.h"
#include "src/video/corevideosource.h"
#include "src/video/videoframe.h"
@ -31,14 +32,23 @@
#include <QDebug>
#include <QTimer>
ToxCall Core::calls[TOXAV_MAX_CALLS];
QVector<ToxCall> CoreAV::calls;
QHash<int, ToxGroupCall> CoreAV::groupCalls;
#ifdef QTOX_FILTER_AUDIO
AudioFilterer * Core::filterer[TOXAV_MAX_CALLS] {nullptr};
QVector<AudioFilterer*> CoreAV::filterer;
#endif
const int Core::videobufsize{TOXAV_MAX_VIDEO_WIDTH * TOXAV_MAX_VIDEO_HEIGHT * 4};
uint8_t* Core::videobuf;
bool Core::anyActiveCalls()
CoreAV::~CoreAV()
{
for (ToxCall call : calls)
{
if (!call.active)
continue;
hangupCall(call.callId);
}
}
bool CoreAV::anyActiveCalls()
{
for (auto& call : calls)
{
@ -48,26 +58,17 @@ bool Core::anyActiveCalls()
return false;
}
void Core::prepareCall(uint32_t friendId, int32_t callId, ToxAv* toxav, bool videoEnabled)
void CoreAV::prepareCall(uint32_t friendId, int32_t callId, ToxAV* toxav, bool videoEnabled)
{
qDebug() << QString("preparing call %1").arg(callId);
if (!videobuf)
videobuf = new uint8_t[videobufsize];
calls[callId].callId = callId;
calls[callId].friendId = friendId;
calls[callId].muteMic = false;
calls[callId].muteVol = false;
// the following three lines are also now redundant from startCall, but are
// necessary there for outbound and here for inbound
calls[callId].codecSettings = av_DefaultSettings;
calls[callId].codecSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH;
calls[callId].codecSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT;
calls[callId].videoEnabled = videoEnabled;
int r = toxav_prepare_transmission(toxav, callId, videoEnabled);
if (r < 0)
qWarning() << QString("Error starting call %1: toxav_prepare_transmission failed with %2").arg(callId).arg(r);
// Audio
Audio::getInstance().subscribeInput();
@ -102,135 +103,35 @@ void Core::prepareCall(uint32_t friendId, int32_t callId, ToxAv* toxav, bool vid
#endif
}
void Core::onAvMediaChange(void* toxav, int32_t callId, void* core)
void CoreAV::answerCall(int32_t callId)
{
int friendId;
int cap = toxav_capability_supported((ToxAv*)toxav, callId,
(ToxAvCapabilities)(av_VideoEncoding|av_VideoDecoding));
if (!cap)
goto fail;
friendId = toxav_get_peer_id((ToxAv*)toxav, callId, 0);
if (friendId < 0)
goto fail;
qDebug() << "Received media change from friend "<<friendId;
if (cap == (av_VideoEncoding|av_VideoDecoding)) // Video call
{
emit static_cast<Core*>(core)->avMediaChange(friendId, callId, true);
calls[callId].videoSource = new CoreVideoSource;
CameraSource& source = CameraSource::getInstance();
source.subscribe();
calls[callId].videoEnabled = true;
}
else // Audio call
{
emit static_cast<Core*>(core)->avMediaChange(friendId, callId, false);
calls[callId].videoEnabled = false;
CameraSource::getInstance().unsubscribe();
calls[callId].videoSource->setDeleteOnClose(true);
calls[callId].videoSource = nullptr;
}
return;
fail: // Centralized error handling
qWarning() << "Toxcore error while receiving media change on call "<<callId;
return;
}
void Core::answerCall(int32_t callId)
{
int friendId = toxav_get_peer_id(toxav, callId, 0);
if (friendId < 0)
{
qWarning() << "Received invalid AV answer peer ID";
return;
}
ToxAvCSettings* transSettings = new ToxAvCSettings;
int err = toxav_get_peer_csettings(toxav, callId, 0, transSettings);
if (err != av_ErrorNone)
{
qWarning() << "answerCall: error getting call settings";
delete transSettings;
return;
}
if (transSettings->call_type == av_TypeVideo)
{
qDebug() << QString("answering call %1 with video").arg(callId);
toxav_answer(toxav, callId, transSettings);
}
else
{
qDebug() << QString("answering call %1 without video").arg(callId);
toxav_answer(toxav, callId, transSettings);
}
delete transSettings;
}
void Core::hangupCall(int32_t callId)
void CoreAV::hangupCall(int32_t callId)
{
qDebug() << QString("hanging up call %1").arg(callId);
toxav_hangup(toxav, callId);
calls[callId].active = false;
}
void Core::rejectCall(int32_t callId)
void CoreAV::rejectCall(int32_t callId)
{
qDebug() << QString("rejecting call %1").arg(callId);
calls[callId].active = false;
toxav_reject(toxav, callId, nullptr);
}
void Core::startCall(uint32_t friendId, bool video)
void CoreAV::startCall(uint32_t friendId, bool video)
{
int32_t callId;
ToxAvCSettings cSettings = av_DefaultSettings;
cSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH;
cSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT;
if (video)
{
qDebug() << QString("Starting new call with %1 with video").arg(friendId);
cSettings.call_type = av_TypeVideo;
if (toxav_call(toxav, &callId, friendId, &cSettings, TOXAV_RINGING_TIME) == 0)
{
calls[callId].videoEnabled=true;
}
else
{
qWarning() << QString("Failed to start new video call with %1").arg(friendId);
emit avCallFailed(friendId);
return;
}
}
else
{
qDebug() << QString("Starting new call with %1 without video").arg(friendId);
cSettings.call_type = av_TypeAudio;
if (toxav_call(toxav, &callId, friendId, &cSettings, TOXAV_RINGING_TIME) == 0)
{
calls[callId].videoEnabled=false;
}
else
{
qWarning() << QString("Failed to start new audio call with %1").arg(friendId);
emit avCallFailed(friendId);
return;
}
}
}
void Core::cancelCall(int32_t callId, uint32_t friendId)
void CoreAV::cancelCall(int32_t callId, uint32_t friendId)
{
qDebug() << QString("Cancelling call with %1").arg(friendId);
calls[callId].active = false;
toxav_cancel(toxav, callId, friendId, nullptr);
}
void Core::cleanupCall(int32_t callId)
void CoreAV::cleanupCall(int32_t callId)
{
assert(calls[callId].active);
qDebug() << QString("cleaning up call %1").arg(callId);
@ -249,16 +150,10 @@ void Core::cleanupCall(int32_t callId)
}
Audio::getInstance().unsubscribeInput();
toxav_kill_transmission(Core::getInstance()->toxav, callId);
if (!anyActiveCalls())
{
delete[] videobuf;
videobuf = nullptr;
}
//toxav_kill_transmission(Core::getInstance()->toxav, callId);
}
void Core::playCallAudio(void* toxav, int32_t callId, const int16_t *data, uint16_t samples, void *user_data)
void CoreAV::playCallAudio(void* toxav, int32_t callId, const int16_t *data, uint16_t samples, void *user_data)
{
Q_UNUSED(user_data);
@ -268,12 +163,12 @@ void Core::playCallAudio(void* toxav, int32_t callId, const int16_t *data, uint1
if (!calls[callId].alSource)
alGenSources(1, &calls[callId].alSource);
ToxAvCSettings dest;
if (toxav_get_peer_csettings((ToxAv*)toxav, callId, 0, &dest) == 0)
playAudioBuffer(calls[callId].alSource, data, samples, dest.audio_channels, dest.audio_sample_rate);
//ToxAvCSettings dest;
//if (toxav_get_peer_csettings((ToxAV*)toxav, callId, 0, &dest) == 0)
// playAudioBuffer(calls[callId].alSource, data, samples, dest.audio_channels, dest.audio_sample_rate);
}
void Core::sendCallAudio(int32_t callId, ToxAv* toxav)
void CoreAV::sendCallAudio(int32_t callId, ToxAV* toxav)
{
if (!calls[callId].active)
return;
@ -284,6 +179,7 @@ void Core::sendCallAudio(int32_t callId, ToxAv* toxav)
return;
}
#if 0
const int framesize = (calls[callId].codecSettings.audio_frame_duration * calls[callId].codecSettings.audio_sample_rate) / 1000 * av_DefaultSettings.audio_channels;
const int bufsize = framesize * 2 * av_DefaultSettings.audio_channels;
uint8_t buf[bufsize];
@ -322,10 +218,11 @@ void Core::sendCallAudio(int32_t callId, ToxAv* toxav)
if ((r = toxav_send_audio(toxav, callId, dest, r)) < 0)
qDebug() << "toxav_send_audio error";
}
#endif
calls[callId].sendAudioTimer->start();
}
void Core::playCallVideo(void*, int32_t callId, const vpx_image_t* img, void *user_data)
void CoreAV::playCallVideo(void*, int32_t callId, const vpx_image *img, void *user_data)
{
Q_UNUSED(user_data);
@ -335,7 +232,7 @@ void Core::playCallVideo(void*, int32_t callId, const vpx_image_t* img, void *us
calls[callId].videoSource->pushFrame(img);
}
void Core::sendCallVideo(int32_t callId, ToxAv* toxav, std::shared_ptr<VideoFrame> vframe)
void CoreAV::sendCallVideo(int32_t callId, ToxAV* toxav, std::shared_ptr<VideoFrame> vframe)
{
if (!calls[callId].active || !calls[callId].videoEnabled)
return;
@ -349,6 +246,7 @@ void Core::sendCallVideo(int32_t callId, ToxAv* toxav, std::shared_ptr<VideoFram
return;
}
#if 0
int result;
if ((result = toxav_prepare_video_frame(toxav, callId, videobuf, videobufsize, frame)) < 0)
{
@ -359,213 +257,25 @@ void Core::sendCallVideo(int32_t callId, ToxAv* toxav, std::shared_ptr<VideoFram
if ((result = toxav_send_video(toxav, callId, (uint8_t*)videobuf, result)) < 0)
qDebug() << QString("toxav_send_video error: %1").arg(result);
#endif
delete frame;
}
void Core::micMuteToggle(int32_t callId)
void CoreAV::micMuteToggle(int32_t callId)
{
if (calls[callId].active)
calls[callId].muteMic = !calls[callId].muteMic;
}
void Core::volMuteToggle(int32_t callId)
void CoreAV::volMuteToggle(int32_t callId)
{
if (calls[callId].active)
calls[callId].muteVol = !calls[callId].muteVol;
}
void Core::onAvCancel(void* _toxav, int32_t callId, void* core)
{
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
int friendId = toxav_get_peer_id(toxav, callId, 0);
if (friendId < 0)
{
qWarning() << "Received invalid AV cancel";
return;
}
qDebug() << QString("AV cancel from %1").arg(friendId);
calls[callId].active = false;
#ifdef QTOX_FILTER_AUDIO
if (filterer[callId])
{
filterer[callId]->closeFilter();
delete filterer[callId];
filterer[callId] = nullptr;
}
#endif
emit static_cast<Core*>(core)->avCancel(friendId, callId);
}
void Core::onAvReject(void* _toxav, int32_t callId, void* core)
{
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
int friendId = toxav_get_peer_id(toxav, callId, 0);
if (friendId < 0)
{
qWarning() << "Received invalid AV reject";
return;
}
qDebug() << QString("AV reject from %1").arg(friendId);
emit static_cast<Core*>(core)->avRejected(friendId, callId);
}
void Core::onAvEnd(void* _toxav, int32_t call_index, void* core)
{
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
int friendId = toxav_get_peer_id(toxav, call_index, 0);
if (friendId < 0)
{
qWarning() << "Received invalid AV end";
return;
}
qDebug() << QString("AV end from %1").arg(friendId);
emit static_cast<Core*>(core)->avEnd(friendId, call_index);
if (calls[call_index].active)
cleanupCall(call_index);
}
void Core::onAvRinging(void* _toxav, int32_t call_index, void* core)
{
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
int friendId = toxav_get_peer_id(toxav, call_index, 0);
if (friendId < 0)
{
qWarning() << "Received invalid AV ringing";
return;
}
if (calls[call_index].videoEnabled)
{
qDebug() << QString("AV ringing with %1 with video").arg(friendId);
emit static_cast<Core*>(core)->avRinging(friendId, call_index, true);
}
else
{
qDebug() << QString("AV ringing with %1 without video").arg(friendId);
emit static_cast<Core*>(core)->avRinging(friendId, call_index, false);
}
}
void Core::onAvRequestTimeout(void* _toxav, int32_t call_index, void* core)
{
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
int friendId = toxav_get_peer_id(toxav, call_index, 0);
if (friendId < 0)
{
qWarning() << "Received invalid AV request timeout";
return;
}
qDebug() << QString("AV request timeout with %1").arg(friendId);
emit static_cast<Core*>(core)->avRequestTimeout(friendId, call_index);
if (calls[call_index].active)
cleanupCall(call_index);
}
void Core::onAvPeerTimeout(void* _toxav, int32_t call_index, void* core)
{
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
int friendId = toxav_get_peer_id(toxav, call_index, 0);
if (friendId < 0)
{
qWarning() << "Received invalid AV peer timeout";
return;
}
qDebug() << QString("AV peer timeout with %1").arg(friendId);
emit static_cast<Core*>(core)->avPeerTimeout(friendId, call_index);
if (calls[call_index].active)
cleanupCall(call_index);
}
void Core::onAvInvite(void* _toxav, int32_t call_index, void* core)
{
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
int friendId = toxav_get_peer_id(toxav, call_index, 0);
if (friendId < 0)
{
qWarning() << "Received invalid AV invite";
return;
}
ToxAvCSettings* transSettings = new ToxAvCSettings;
int err = toxav_get_peer_csettings(toxav, call_index, 0, transSettings);
if (err != av_ErrorNone)
{
qWarning() << "onAvInvite: error getting call type";
delete transSettings;
return;
}
if (transSettings->call_type == av_TypeVideo)
{
qDebug() << QString("AV invite from %1 with video").arg(friendId);
emit static_cast<Core*>(core)->avInvite(friendId, call_index, true);
}
else
{
qDebug() << QString("AV invite from %1 without video").arg(friendId);
emit static_cast<Core*>(core)->avInvite(friendId, call_index, false);
}
delete transSettings;
}
void Core::onAvStart(void* _toxav, int32_t call_index, void* core)
{
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
int friendId = toxav_get_peer_id(toxav, call_index, 0);
if (friendId < 0)
{
qWarning() << "Received invalid AV start";
return;
}
ToxAvCSettings* transSettings = new ToxAvCSettings;
int err = toxav_get_peer_csettings(toxav, call_index, 0, transSettings);
if (err != av_ErrorNone)
{
qWarning() << "onAvStart: error getting call type";
delete transSettings;
return;
}
if (transSettings->call_type == av_TypeVideo)
{
qDebug() << QString("AV start from %1 with video").arg(friendId);
prepareCall(friendId, call_index, toxav, true);
emit static_cast<Core*>(core)->avStart(friendId, call_index, true);
}
else
{
qDebug() << QString("AV start from %1 without video").arg(friendId);
prepareCall(friendId, call_index, toxav, false);
emit static_cast<Core*>(core)->avStart(friendId, call_index, false);
}
delete transSettings;
}
// This function's logic was shamelessly stolen from uTox
void Core::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate)
void CoreAV::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate)
{
if (!channels || channels > 2)
{
@ -610,12 +320,12 @@ void Core::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, un
}
}
VideoSource *Core::getVideoSourceFromCall(int callNumber)
VideoSource *CoreAV::getVideoSourceFromCall(int callNumber)
{
return calls[callNumber].videoSource;
}
void Core::joinGroupCall(int groupId)
void CoreAV::joinGroupCall(int groupId)
{
qDebug() << QString("Joining group call %1").arg(groupId);
groupCalls[groupId].groupId = groupId;
@ -623,16 +333,13 @@ void Core::joinGroupCall(int groupId)
groupCalls[groupId].muteVol = false;
// the following three lines are also now redundant from startCall, but are
// necessary there for outbound and here for inbound
groupCalls[groupId].codecSettings = av_DefaultSettings;
groupCalls[groupId].codecSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH;
groupCalls[groupId].codecSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT;
// Audio
Audio::getInstance().subscribeInput();
// Go
Core* core = Core::getInstance();
ToxAv* toxav = core->toxav;
ToxAV* toxav = core->toxav;
groupCalls[groupId].sendAudioTimer = new QTimer();
groupCalls[groupId].active = true;
@ -642,7 +349,7 @@ void Core::joinGroupCall(int groupId)
groupCalls[groupId].sendAudioTimer->start();
}
void Core::leaveGroupCall(int groupId)
void CoreAV::leaveGroupCall(int groupId)
{
qDebug() << QString("Leaving group call %1").arg(groupId);
groupCalls[groupId].active = false;
@ -655,7 +362,7 @@ void Core::leaveGroupCall(int groupId)
delete groupCalls[groupId].sendAudioTimer;
}
void Core::sendGroupCallAudio(int groupId, ToxAv* toxav)
void CoreAV::sendGroupCallAudio(int groupId, ToxAV *toxav)
{
if (!groupCalls[groupId].active)
return;
@ -666,6 +373,7 @@ void Core::sendGroupCallAudio(int groupId, ToxAv* toxav)
return;
}
#if 0
const int framesize = (groupCalls[groupId].codecSettings.audio_frame_duration * groupCalls[groupId].codecSettings.audio_sample_rate) / 1000 * av_DefaultSettings.audio_channels;
const int bufsize = framesize * 2 * av_DefaultSettings.audio_channels;
uint8_t buf[bufsize];
@ -680,35 +388,63 @@ void Core::sendGroupCallAudio(int groupId, ToxAv* toxav)
return;
}
}
#endif
groupCalls[groupId].sendAudioTimer->start();
}
void Core::disableGroupCallMic(int groupId)
void CoreAV::disableGroupCallMic(int groupId)
{
groupCalls[groupId].muteMic = true;
}
void Core::disableGroupCallVol(int groupId)
void CoreAV::disableGroupCallVol(int groupId)
{
groupCalls[groupId].muteVol = true;
}
void Core::enableGroupCallMic(int groupId)
void CoreAV::enableGroupCallMic(int groupId)
{
groupCalls[groupId].muteMic = false;
}
void Core::enableGroupCallVol(int groupId)
void CoreAV::enableGroupCallVol(int groupId)
{
groupCalls[groupId].muteVol = false;
}
bool Core::isGroupCallMicEnabled(int groupId)
bool CoreAV::isGroupCallMicEnabled(int groupId)
{
return !groupCalls[groupId].muteMic;
}
bool Core::isGroupCallVolEnabled(int groupId)
bool CoreAV::isGroupCallVolEnabled(int groupId)
{
return !groupCalls[groupId].muteVol;
}
bool CoreAV::isGroupAvEnabled(int groupId)
{
return tox_group_get_type(Core::getInstance()->tox, groupId) == TOX_GROUPCHAT_TYPE_AV;
}
void CoreAV::resetCallSources()
{
for (ToxGroupCall& call : groupCalls)
{
for (ALuint source : call.alSources)
alDeleteSources(1, &source);
call.alSources.clear();
}
for (ToxCall& call : calls)
{
if (call.active && call.alSource)
{
ALuint tmp = call.alSource;
call.alSource = 0;
alDeleteSources(1, &tmp);
alGenSources(1, &call.alSource);
}
}
}

View File

@ -22,6 +22,8 @@
#define COREAV_H
#include <QHash>
#include <QThread>
#include <memory>
#include <tox/toxav.h>
#if defined(__APPLE__) && defined(__MACH__)
@ -32,13 +34,19 @@
#include <AL/alc.h>
#endif
#ifdef QTOX_FILTER_AUDIO
class AudioFilterer;
#endif
class QTimer;
class CoreVideoSource;
class CameraSource;
class VideoSource;
class VideoFrame;
struct vpx_image;
struct ToxCall
{
ToxAvCSettings codecSettings;
QTimer *sendAudioTimer;
int32_t callId;
uint32_t friendId;
@ -52,7 +60,6 @@ struct ToxCall
struct ToxGroupCall
{
ToxAvCSettings codecSettings;
QTimer *sendAudioTimer;
int groupId;
bool active = false;
@ -61,4 +68,58 @@ struct ToxGroupCall
QHash<int, ALuint> alSources;
};
class CoreAV : public QThread
{
Q_OBJECT
public:
CoreAV() = default;
~CoreAV();
static bool anyActiveCalls(); ///< true is any calls are currently active (note: a call about to start is not yet active)
static void prepareCall(uint32_t friendId, int callId, ToxAV *toxav, bool videoEnabled);
static void cleanupCall(int callId);
static void playCallAudio(void *toxav, int32_t callId, const int16_t *data,
uint16_t samples, void *user_data); // Callback
static void sendCallAudio(int callId, ToxAV* toxav);
static void playAudioBuffer(ALuint alSource, const int16_t *data, int samples,
unsigned channels, int sampleRate);
static void playCallVideo(void *toxav, int32_t callId, const vpx_image* img, void *user_data);
static void sendCallVideo(int callId, ToxAV* toxav, std::shared_ptr<VideoFrame> frame);
static void sendGroupCallAudio(int groupId, ToxAV* toxav);
static VideoSource* getVideoSourceFromCall(int callNumber); ///< Get a call's video source
static void resetCallSources(); ///< Forces to regenerate each call's audio sources
static void joinGroupCall(int groupId); ///< Starts a call in an existing AV groupchat. Call from the GUI thread.
static void leaveGroupCall(int groupId); ///< Will not leave the group, just stop the call. Call from the GUI thread.
static void disableGroupCallMic(int groupId);
static void disableGroupCallVol(int groupId);
static void enableGroupCallMic(int groupId);
static void enableGroupCallVol(int groupId);
static bool isGroupCallMicEnabled(int groupId);
static bool isGroupCallVolEnabled(int groupId);
static bool isGroupAvEnabled(int groupId); ///< True for AV groups, false for text-only groups
public slots:
static void answerCall(int callId);
static void rejectCall(int callId);
static void hangupCall(int callId);
static void startCall(uint32_t friendId, bool video=false);
static void cancelCall(int callId, uint32_t friendId);
static void micMuteToggle(int callId);
static void volMuteToggle(int callId);
private:
static QVector<ToxCall> calls;
#ifdef QTOX_FILTER_AUDIO
static QVector<AudioFilterer*> filterer;
#endif
static QHash<int, ToxGroupCall> groupCalls; // Maps group IDs to ToxGroupCalls
friend class Audio;
friend class Core;
};
#endif // COREAV_H

View File

@ -21,7 +21,6 @@
#ifndef COREDEFINES_H
#define COREDEFINES_H
#define TOXAV_MAX_CALLS 16
#define TOXAV_RINGING_TIME 45
// TODO: Put that in the settings

View File

@ -32,6 +32,7 @@
#include <QFile>
#include <QApplication>
#include <cassert>
#include <vpx/vpx_image.h>
#ifdef Q_OS_ANDROID
#include <src/widget/androidgui.h>

View File

@ -35,8 +35,8 @@ public:
virtual void unsubscribe() override;
private:
// Only Core should create a CoreVideoSource since
// only Core can push images to it
// Only CoreAV should create a CoreVideoSource since
// only CoreAV can push images to it
CoreVideoSource();
/// Makes a copy of the vpx_image_t and emits it as a new VideoFrame
@ -49,7 +49,7 @@ private:
std::atomic_bool deleteOnClose; ///< If true, self-delete after the last suscriber is gone
std::atomic_bool biglock; ///< Fast lock
friend class Core;
friend class CoreAV;
};
#endif // COREVIDEOSOURCE_H

View File

@ -33,6 +33,7 @@ class VideoSource : public QObject
Q_OBJECT
public:
virtual ~VideoSource() = default;
/// If subscribe sucessfully opens the source, it will start emitting frameAvailable signals
virtual bool subscribe() = 0;
/// Stop emitting frameAvailable signals, and free associated resources if necessary

View File

@ -35,6 +35,7 @@
#include <cassert>
#include "chatform.h"
#include "src/core/core.h"
#include "src/core/coreav.h"
#include "src/friend.h"
#include "src/persistence/historykeeper.h"
#include "src/widget/style.h"
@ -756,7 +757,7 @@ GenericNetCamView *ChatForm::createNetcam()
{
qDebug() << "creating netcam";
NetCamView* view = new NetCamView(f->getFriendID(), this);
view->show(Core::getInstance()->getVideoSourceFromCall(callId), f->getDisplayedName());
view->show(CoreAV::getVideoSourceFromCall(callId), f->getDisplayedName());
return view;
}

View File

@ -25,6 +25,7 @@
#include "src/widget/tool/croppinglabel.h"
#include "src/widget/maskablepixmapwidget.h"
#include "src/core/core.h"
#include "src/core/coreav.h"
#include "src/widget/style.h"
#include "src/persistence/historykeeper.h"
#include "src/widget/flowlayout.h"
@ -282,13 +283,13 @@ void GroupChatForm::onMicMuteToggle()
{
if (micButton->objectName() == "red")
{
Core::getInstance()->enableGroupCallMic(group->getGroupId());
CoreAV::enableGroupCallMic(group->getGroupId());
micButton->setObjectName("green");
micButton->setToolTip(tr("Mute microphone"));
}
else
{
Core::getInstance()->disableGroupCallMic(group->getGroupId());
CoreAV::disableGroupCallMic(group->getGroupId());
micButton->setObjectName("red");
micButton->setToolTip(tr("Unmute microphone"));
}
@ -303,13 +304,13 @@ void GroupChatForm::onVolMuteToggle()
{
if (volButton->objectName() == "red")
{
Core::getInstance()->enableGroupCallVol(group->getGroupId());
CoreAV::enableGroupCallVol(group->getGroupId());
volButton->setObjectName("green");
volButton->setToolTip(tr("Mute call"));
}
else
{
Core::getInstance()->disableGroupCallVol(group->getGroupId());
CoreAV::disableGroupCallVol(group->getGroupId());
volButton->setObjectName("red");
volButton->setToolTip(tr("Unmute call"));
}
@ -322,7 +323,7 @@ void GroupChatForm::onCallClicked()
{
if (!inCall)
{
Core::getInstance()->joinGroupCall(group->getGroupId());
CoreAV::joinGroupCall(group->getGroupId());
audioInputFlag = true;
audioOutputFlag = true;
callButton->setObjectName("red");
@ -339,7 +340,7 @@ void GroupChatForm::onCallClicked()
}
else
{
Core::getInstance()->leaveGroupCall(group->getGroupId());
CoreAV::leaveGroupCall(group->getGroupId());
audioInputFlag = false;
audioOutputFlag = false;
callButton->setObjectName("green");
@ -375,10 +376,9 @@ void GroupChatForm::keyPressEvent(QKeyEvent* ev)
// Push to talk (CTRL+P)
if (ev->key() == Qt::Key_P && (ev->modifiers() & Qt::ControlModifier) && inCall)
{
Core* core = Core::getInstance();
if (!core->isGroupCallMicEnabled(group->getGroupId()))
if (!CoreAV::isGroupCallMicEnabled(group->getGroupId()))
{
core->enableGroupCallMic(group->getGroupId());
CoreAV::enableGroupCallMic(group->getGroupId());
micButton->setObjectName("green");
micButton->style()->polish(micButton);
Style::repolish(micButton);
@ -394,10 +394,9 @@ void GroupChatForm::keyReleaseEvent(QKeyEvent* ev)
// Push to talk (CTRL+P)
if (ev->key() == Qt::Key_P && (ev->modifiers() & Qt::ControlModifier) && inCall)
{
Core* core = Core::getInstance();
if (core->isGroupCallMicEnabled(group->getGroupId()))
if (CoreAV::isGroupCallMicEnabled(group->getGroupId()))
{
core->disableGroupCallMic(group->getGroupId());
CoreAV::disableGroupCallMic(group->getGroupId());
micButton->setObjectName("red");
micButton->style()->polish(micButton);
Style::repolish(micButton);

View File

@ -24,6 +24,7 @@
#include "src/persistence/settings.h"
#include "src/persistence/smileypack.h"
#include "src/core/core.h"
#include "src/core/coreav.h"
#include "src/widget/style.h"
#include "src/nexus.h"
#include "src/persistence/profile.h"
@ -358,7 +359,7 @@ void GeneralForm::onUseProxyUpdated()
void GeneralForm::onReconnectClicked()
{
if (Core::getInstance()->anyActiveCalls())
if (CoreAV::anyActiveCalls())
{
QMessageBox::warning(this, tr("Call active", "popup title"),
tr("You can't disconnect while a call is active!", "popup text"));

View File

@ -25,7 +25,7 @@ class GenericForm : public QWidget
Q_OBJECT
public:
GenericForm(const QPixmap &icon) : formIcon(icon) {;}
~GenericForm() {}
virtual ~GenericForm() {}
virtual QString getFormName() = 0;
QPixmap getFormIcon() {return formIcon;}

View File

@ -27,6 +27,7 @@ class AdjustingScrollArea : public QScrollArea
Q_OBJECT
public:
explicit AdjustingScrollArea(QWidget *parent = 0);
virtual ~AdjustingScrollArea() = default;
protected:
virtual void resizeEvent(QResizeEvent *ev) override;

View File

@ -21,6 +21,7 @@
#include "contentlayout.h"
#include "ui_mainwindow.h"
#include "src/core/core.h"
#include "src/core/coreav.h"
#include "src/persistence/settings.h"
#include "contentdialog.h"
#include "src/friend.h"
@ -890,13 +891,13 @@ void Widget::addFriend(int friendId, const QString &userId)
connect(newfriend->getChatForm(), &GenericChatForm::sendMessage, core, &Core::sendMessage);
connect(newfriend->getChatForm(), &GenericChatForm::sendAction, core, &Core::sendAction);
connect(newfriend->getChatForm(), &ChatForm::sendFile, core, &Core::sendFile);
connect(newfriend->getChatForm(), &ChatForm::answerCall, core, &Core::answerCall);
connect(newfriend->getChatForm(), &ChatForm::hangupCall, core, &Core::hangupCall);
connect(newfriend->getChatForm(), &ChatForm::rejectCall, core, &Core::rejectCall);
connect(newfriend->getChatForm(), &ChatForm::startCall, core, &Core::startCall);
connect(newfriend->getChatForm(), &ChatForm::cancelCall, core, &Core::cancelCall);
connect(newfriend->getChatForm(), &ChatForm::micMuteToggle, core, &Core::micMuteToggle);
connect(newfriend->getChatForm(), &ChatForm::volMuteToggle, core, &Core::volMuteToggle);
connect(newfriend->getChatForm(), &ChatForm::answerCall, core->getAv(), &CoreAV::answerCall);
connect(newfriend->getChatForm(), &ChatForm::hangupCall, core->getAv(), &CoreAV::hangupCall);
connect(newfriend->getChatForm(), &ChatForm::rejectCall, core->getAv(), &CoreAV::rejectCall);
connect(newfriend->getChatForm(), &ChatForm::startCall, core->getAv(), &CoreAV::startCall);
connect(newfriend->getChatForm(), &ChatForm::cancelCall, core->getAv(), &CoreAV::cancelCall);
connect(newfriend->getChatForm(), &ChatForm::micMuteToggle, core->getAv(), &CoreAV::micMuteToggle);
connect(newfriend->getChatForm(), &ChatForm::volMuteToggle, core->getAv(), &CoreAV::volMuteToggle);
connect(newfriend->getChatForm(), &ChatForm::aliasChanged, newfriend->getFriendWidget(), &FriendWidget::setAlias);
connect(core, &Core::fileReceiveRequested, newfriend->getChatForm(), &ChatForm::onFileRecvRequest);
connect(core, &Core::avInvite, newfriend->getChatForm(), &ChatForm::onAvInvite);
@ -1588,7 +1589,7 @@ Group *Widget::createGroup(int groupId)
Core* core = Nexus::getCore();
QString groupName = QString("Groupchat #%1").arg(groupId);
Group* newgroup = GroupList::addGroup(groupId, groupName, core->isGroupAvEnabled(groupId));
Group* newgroup = GroupList::addGroup(groupId, groupName, CoreAV::isGroupAvEnabled(groupId));
contactListWidget->addGroupWidget(newgroup->getGroupWidget());
newgroup->getGroupWidget()->updateStatusLight();