mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Implement group calls
This commit is contained in:
parent
6cbd507ca8
commit
f45256baf1
6
qtox.pro
6
qtox.pro
|
@ -501,7 +501,8 @@ SOURCES += \
|
||||||
src/widget/tool/movablewidget.cpp \
|
src/widget/tool/movablewidget.cpp \
|
||||||
src/widget/tool/micfeedbackwidget.cpp \
|
src/widget/tool/micfeedbackwidget.cpp \
|
||||||
src/widget/tool/removefrienddialog.cpp \
|
src/widget/tool/removefrienddialog.cpp \
|
||||||
src/video/groupnetcamview.cpp
|
src/video/groupnetcamview.cpp \
|
||||||
|
src/core/toxcall.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
src/audio/audio.h \
|
src/audio/audio.h \
|
||||||
|
@ -553,4 +554,5 @@ HEADERS += \
|
||||||
src/widget/tool/movablewidget.h \
|
src/widget/tool/movablewidget.h \
|
||||||
src/video/genericnetcamview.h \
|
src/video/genericnetcamview.h \
|
||||||
src/video/groupnetcamview.h \
|
src/video/groupnetcamview.h \
|
||||||
src/core/indexedlist.h
|
src/core/indexedlist.h \
|
||||||
|
src/core/toxcall.h
|
||||||
|
|
|
@ -114,13 +114,10 @@ void Audio::setOutputVolume(qreal volume)
|
||||||
|
|
||||||
for (const ToxGroupCall& call : CoreAV::groupCalls)
|
for (const ToxGroupCall& call : CoreAV::groupCalls)
|
||||||
{
|
{
|
||||||
if (!call.active)
|
alSourcef(call.alSource, AL_GAIN, outputVolume);
|
||||||
continue;
|
|
||||||
for (ALuint source : call.alSources)
|
|
||||||
alSourcef(source, AL_GAIN, outputVolume);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const ToxCall& call : CoreAV::calls)
|
for (const ToxFriendCall& call : CoreAV::calls)
|
||||||
{
|
{
|
||||||
alSourcef(call.alSource, AL_GAIN, outputVolume);
|
alSourcef(call.alSource, AL_GAIN, outputVolume);
|
||||||
}
|
}
|
||||||
|
@ -392,15 +389,18 @@ void Audio::playGroupAudio(int group, int peer, const int16_t* data,
|
||||||
assert(QThread::currentThread() == audioThread);
|
assert(QThread::currentThread() == audioThread);
|
||||||
QMutexLocker lock(&audioOutLock);
|
QMutexLocker lock(&audioOutLock);
|
||||||
|
|
||||||
ToxGroupCall& call = CoreAV::groupCalls[group];
|
if (!CoreAV::groupCalls.contains(group))
|
||||||
|
|
||||||
if (!call.active || call.muteVol)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!call.alSources.contains(peer))
|
ToxGroupCall& call = CoreAV::groupCalls[group];
|
||||||
|
|
||||||
|
if (call.inactive || call.muteVol)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!call.alSource)
|
||||||
{
|
{
|
||||||
alGenSources(1, &call.alSources[peer]);
|
alGenSources(1, &call.alSource);
|
||||||
alSourcef(call.alSources[peer], AL_GAIN, outputVolume);
|
alSourcef(call.alSource, AL_GAIN, outputVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal volume = 0.;
|
qreal volume = 0.;
|
||||||
|
@ -410,7 +410,7 @@ void Audio::playGroupAudio(int group, int peer, const int16_t* data,
|
||||||
|
|
||||||
emit groupAudioPlayed(group, peer, volume / bufsize);
|
emit groupAudioPlayed(group, peer, volume / bufsize);
|
||||||
|
|
||||||
playAudioBuffer(call.alSources[peer], data, samples, channels, sample_rate);
|
playAudioBuffer(call.alSource, data, samples, channels, sample_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate)
|
void Audio::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate)
|
||||||
|
@ -471,6 +471,20 @@ bool Audio::isOutputClosed()
|
||||||
return alOutDev;
|
return alOutDev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Audio::createSource(ALuint* source)
|
||||||
|
{
|
||||||
|
alGenSources(1, source);
|
||||||
|
alSourcef(*source, AL_GAIN, getInstance().outputVolume);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Audio::deleteSource(ALuint* source)
|
||||||
|
{
|
||||||
|
if (alIsSource(*source))
|
||||||
|
alDeleteSources(1, source);
|
||||||
|
else
|
||||||
|
qWarning() << "Trying to delete invalid audio source"<<*source;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Does nothing and return false on failure
|
Does nothing and return false on failure
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -72,6 +72,9 @@ public:
|
||||||
bool isInputReady();
|
bool isInputReady();
|
||||||
bool isOutputClosed();
|
bool isOutputClosed();
|
||||||
|
|
||||||
|
static void createSource(ALuint* source);
|
||||||
|
static void deleteSource(ALuint* source);
|
||||||
|
|
||||||
void playMono16Sound(const QByteArray& data);
|
void playMono16Sound(const QByteArray& data);
|
||||||
bool tryCaptureSamples(int16_t *buf, int samples);
|
bool tryCaptureSamples(int16_t *buf, int samples);
|
||||||
|
|
||||||
|
|
|
@ -728,7 +728,7 @@ void Core::removeGroup(int groupId, bool fake)
|
||||||
|
|
||||||
tox_del_groupchat(tox, groupId);
|
tox_del_groupchat(tox, groupId);
|
||||||
|
|
||||||
if (av->groupCalls[groupId].active)
|
if (!av->groupCalls[groupId].inactive)
|
||||||
av->leaveGroupCall(groupId);
|
av->leaveGroupCall(groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,121 +20,21 @@
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "coreav.h"
|
#include "coreav.h"
|
||||||
#include "src/video/camerasource.h"
|
|
||||||
#include "src/video/corevideosource.h"
|
|
||||||
#include "src/video/videoframe.h"
|
|
||||||
#include "src/audio/audio.h"
|
#include "src/audio/audio.h"
|
||||||
|
#include "src/persistence/settings.h"
|
||||||
|
#include "src/video/videoframe.h"
|
||||||
|
#include "src/video/corevideosource.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
#ifdef QTOX_FILTER_AUDIO
|
#ifdef QTOX_FILTER_AUDIO
|
||||||
#include "src/audio/audiofilterer.h"
|
#include "src/audio/audiofilterer.h"
|
||||||
#endif
|
#endif
|
||||||
#include "src/persistence/settings.h"
|
|
||||||
#include <assert.h>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
IndexedList<ToxCall> CoreAV::calls;
|
IndexedList<ToxFriendCall> CoreAV::calls;
|
||||||
QHash<int, ToxGroupCall> CoreAV::groupCalls;
|
IndexedList<ToxGroupCall> CoreAV::groupCalls;
|
||||||
|
|
||||||
ToxCall::ToxCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av)
|
using namespace std;
|
||||||
: sendAudioTimer{new QTimer}, friendNum{FriendNum},
|
|
||||||
ringing{true}, muteMic{false}, muteVol{false},
|
|
||||||
videoEnabled{VideoEnabled},
|
|
||||||
alSource{0}, videoSource{nullptr},
|
|
||||||
state{static_cast<TOXAV_FRIEND_CALL_STATE>(0)}
|
|
||||||
{
|
|
||||||
Audio::getInstance().subscribeInput();
|
|
||||||
sendAudioTimer->setInterval(5);
|
|
||||||
sendAudioTimer->setSingleShot(true);
|
|
||||||
QObject::connect(sendAudioTimer, &QTimer::timeout, [FriendNum,&av](){av.sendCallAudio(FriendNum);});
|
|
||||||
sendAudioTimer->start();
|
|
||||||
|
|
||||||
if (videoEnabled)
|
|
||||||
{
|
|
||||||
videoSource = new CoreVideoSource;
|
|
||||||
CameraSource& source = CameraSource::getInstance();
|
|
||||||
if (!source.isOpen())
|
|
||||||
source.open();
|
|
||||||
source.subscribe();
|
|
||||||
QObject::connect(&source, &VideoSource::frameAvailable,
|
|
||||||
[FriendNum,&av](std::shared_ptr<VideoFrame> frame){av.sendCallVideo(FriendNum,frame);});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef QTOX_FILTER_AUDIO
|
|
||||||
if (Settings::getInstance().getFilterAudio())
|
|
||||||
{
|
|
||||||
filterer = new AudioFilterer();
|
|
||||||
filterer->startFilter(AUDIO_SAMPLE_RATE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
filterer = nullptr;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
ToxCall::ToxCall(ToxCall&& other)
|
|
||||||
: sendAudioTimer{other.sendAudioTimer}, friendNum{other.friendNum},
|
|
||||||
ringing{other.ringing}, muteMic{other.muteMic}, muteVol{other.muteVol},
|
|
||||||
videoEnabled{other.videoEnabled},
|
|
||||||
alSource{other.alSource}, videoSource{other.videoSource},
|
|
||||||
state{other.state}
|
|
||||||
{
|
|
||||||
other.sendAudioTimer = nullptr;
|
|
||||||
other.friendNum = std::numeric_limits<decltype(friendNum)>::max();
|
|
||||||
other.videoEnabled = false;
|
|
||||||
other.alSource = 0;
|
|
||||||
other.videoSource = nullptr;
|
|
||||||
|
|
||||||
#ifdef QTOX_FILTER_AUDIO
|
|
||||||
filterer = other.filterer;
|
|
||||||
other.filterer = nullptr;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
ToxCall::~ToxCall()
|
|
||||||
{
|
|
||||||
if (sendAudioTimer)
|
|
||||||
{
|
|
||||||
QObject::disconnect(sendAudioTimer, nullptr, nullptr, nullptr);
|
|
||||||
sendAudioTimer->stop();
|
|
||||||
Audio::getInstance().unsubscribeInput();
|
|
||||||
}
|
|
||||||
if (videoEnabled)
|
|
||||||
{
|
|
||||||
CameraSource::getInstance().unsubscribe();
|
|
||||||
if (videoSource)
|
|
||||||
{
|
|
||||||
videoSource->setDeleteOnClose(true);
|
|
||||||
videoSource = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const ToxCall& ToxCall::operator=(ToxCall&& other)
|
|
||||||
{
|
|
||||||
sendAudioTimer = other.sendAudioTimer;
|
|
||||||
other.sendAudioTimer = nullptr;
|
|
||||||
friendNum = other.friendNum;
|
|
||||||
other.friendNum = std::numeric_limits<decltype(friendNum)>::max();
|
|
||||||
ringing = other.ringing;
|
|
||||||
muteMic = other.muteMic;
|
|
||||||
muteVol = other.muteVol;
|
|
||||||
videoEnabled = other.videoEnabled;
|
|
||||||
other.videoEnabled = false;
|
|
||||||
alSource = other.alSource;
|
|
||||||
other.alSource = 0;
|
|
||||||
videoSource = other.videoSource;
|
|
||||||
other.videoSource = nullptr;
|
|
||||||
state = other.state;
|
|
||||||
|
|
||||||
#ifdef QTOX_FILTER_AUDIO
|
|
||||||
filterer = other.filterer;
|
|
||||||
other.filterer = nullptr;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreAV::CoreAV(Tox *tox)
|
CoreAV::CoreAV(Tox *tox)
|
||||||
{
|
{
|
||||||
|
@ -150,8 +50,8 @@ CoreAV::CoreAV(Tox *tox)
|
||||||
|
|
||||||
CoreAV::~CoreAV()
|
CoreAV::~CoreAV()
|
||||||
{
|
{
|
||||||
for (const ToxCall& call : calls)
|
for (const ToxFriendCall& call : calls)
|
||||||
cancelCall(call.friendNum);
|
cancelCall(call.callId);
|
||||||
toxav_kill(toxav);
|
toxav_kill(toxav);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +77,7 @@ void CoreAV::answerCall(uint32_t friendNum)
|
||||||
TOXAV_ERR_ANSWER err;
|
TOXAV_ERR_ANSWER err;
|
||||||
if (toxav_answer(toxav, friendNum, AUDIO_DEFAULT_BITRATE, VIDEO_DEFAULT_BITRATE, &err))
|
if (toxav_answer(toxav, friendNum, AUDIO_DEFAULT_BITRATE, VIDEO_DEFAULT_BITRATE, &err))
|
||||||
{
|
{
|
||||||
calls[friendNum].ringing = false;
|
calls[friendNum].inactive = false;
|
||||||
emit avStart(friendNum, calls[friendNum].videoEnabled);
|
emit avStart(friendNum, calls[friendNum].videoEnabled);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -209,19 +109,18 @@ void CoreAV::cancelCall(uint32_t friendId)
|
||||||
calls.remove(friendId);
|
calls.remove(friendId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreAV::sendCallAudio(uint32_t callId)
|
bool CoreAV::sendCallAudio(uint32_t callId)
|
||||||
{
|
{
|
||||||
if (!calls.contains(callId))
|
if (!calls.contains(callId))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
ToxCall& call = calls[callId];
|
ToxFriendCall& call = calls[callId];
|
||||||
|
|
||||||
if (call.muteMic || call.ringing
|
if (call.muteMic || call.inactive
|
||||||
|| !(call.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_A)
|
|| !(call.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_A)
|
||||||
|| !Audio::getInstance().isInputReady())
|
|| !Audio::getInstance().isInputReady())
|
||||||
{
|
{
|
||||||
call.sendAudioTimer->start();
|
return true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t buf[AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS] = {0};
|
int16_t buf[AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS] = {0};
|
||||||
|
@ -252,27 +151,17 @@ void CoreAV::sendCallAudio(uint32_t callId)
|
||||||
qDebug() << "toxav_audio_send_frame error";
|
qDebug() << "toxav_audio_send_frame error";
|
||||||
}
|
}
|
||||||
|
|
||||||
call.sendAudioTimer->start();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreAV::playCallVideo(void*, int32_t callId, const vpx_image *img, void *user_data)
|
void CoreAV::sendCallVideo(uint32_t callId, shared_ptr<VideoFrame> vframe)
|
||||||
{
|
|
||||||
Q_UNUSED(user_data);
|
|
||||||
|
|
||||||
if (!calls.contains(callId) || !calls[callId].videoEnabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
calls[callId].videoSource->pushFrame(img);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreAV::sendCallVideo(uint32_t callId, std::shared_ptr<VideoFrame> vframe)
|
|
||||||
{
|
{
|
||||||
if (!calls.contains(callId))
|
if (!calls.contains(callId))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ToxCall& call = calls[callId];
|
ToxFriendCall& call = calls[callId];
|
||||||
|
|
||||||
if (!call.videoEnabled || call.ringing
|
if (!call.videoEnabled || call.inactive
|
||||||
|| !(call.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_V))
|
|| !(call.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_V))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -313,65 +202,57 @@ VideoSource *CoreAV::getVideoSourceFromCall(int friendNum)
|
||||||
void CoreAV::joinGroupCall(int groupId)
|
void CoreAV::joinGroupCall(int groupId)
|
||||||
{
|
{
|
||||||
qDebug() << QString("Joining group call %1").arg(groupId);
|
qDebug() << QString("Joining group call %1").arg(groupId);
|
||||||
groupCalls[groupId].groupId = groupId;
|
|
||||||
groupCalls[groupId].muteMic = false;
|
|
||||||
groupCalls[groupId].muteVol = false;
|
|
||||||
// the following three lines are also now redundant from startCall, but are
|
|
||||||
// necessary there for outbound and here for inbound
|
|
||||||
|
|
||||||
// Audio
|
auto call = groupCalls.insert({groupId, *this});
|
||||||
Audio::getInstance().subscribeInput();
|
call->inactive = false;
|
||||||
|
|
||||||
// Go
|
|
||||||
groupCalls[groupId].sendAudioTimer = new QTimer();
|
|
||||||
groupCalls[groupId].active = true;
|
|
||||||
groupCalls[groupId].sendAudioTimer->setInterval(5);
|
|
||||||
groupCalls[groupId].sendAudioTimer->setSingleShot(true);
|
|
||||||
connect(groupCalls[groupId].sendAudioTimer, &QTimer::timeout, [=](){sendGroupCallAudio(groupId,toxav);});
|
|
||||||
groupCalls[groupId].sendAudioTimer->start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreAV::leaveGroupCall(int groupId)
|
void CoreAV::leaveGroupCall(int groupId)
|
||||||
{
|
{
|
||||||
qDebug() << QString("Leaving group call %1").arg(groupId);
|
qDebug() << QString("Leaving group call %1").arg(groupId);
|
||||||
groupCalls[groupId].active = false;
|
|
||||||
disconnect(groupCalls[groupId].sendAudioTimer,0,0,0);
|
groupCalls.remove(groupId);
|
||||||
groupCalls[groupId].sendAudioTimer->stop();
|
|
||||||
for (ALuint source : groupCalls[groupId].alSources)
|
|
||||||
alDeleteSources(1, &source);
|
|
||||||
groupCalls[groupId].alSources.clear();
|
|
||||||
Audio::getInstance().unsubscribeInput();
|
|
||||||
delete groupCalls[groupId].sendAudioTimer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreAV::sendGroupCallAudio(int groupId, ToxAV *toxav)
|
bool CoreAV::sendGroupCallAudio(int groupId)
|
||||||
{
|
{
|
||||||
if (!groupCalls[groupId].active)
|
if (!groupCalls.contains(groupId))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
if (groupCalls[groupId].muteMic || !Audio::getInstance().isInputReady())
|
ToxGroupCall& call = groupCalls[groupId];
|
||||||
|
|
||||||
|
if (call.inactive || call.muteMic || !Audio::getInstance().isInputReady())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
int16_t buf[AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS] = {0};
|
||||||
|
if (Audio::getInstance().tryCaptureSamples(buf, AUDIO_FRAME_SAMPLE_COUNT))
|
||||||
{
|
{
|
||||||
groupCalls[groupId].sendAudioTimer->start();
|
#ifdef QTOX_FILTER_AUDIO
|
||||||
return;
|
if (Settings::getInstance().getFilterAudio())
|
||||||
}
|
|
||||||
|
|
||||||
#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];
|
|
||||||
|
|
||||||
if (Audio::getInstance().tryCaptureSamples(buf, framesize))
|
|
||||||
{
|
|
||||||
if (toxav_group_send_audio(toxav_get_tox(toxav), groupId, (int16_t*)buf,
|
|
||||||
framesize, av_DefaultSettings.audio_channels, av_DefaultSettings.audio_sample_rate) < 0)
|
|
||||||
{
|
{
|
||||||
qDebug() << "toxav_group_send_audio error";
|
if (!call.filterer)
|
||||||
groupCalls[groupId].sendAudioTimer->start();
|
{
|
||||||
return;
|
call.filterer = new AudioFilterer();
|
||||||
|
call.filterer->startFilter(AUDIO_SAMPLE_RATE);
|
||||||
|
}
|
||||||
|
// is a null op #ifndef ALC_LOOPBACK_CAPTURE_SAMPLES
|
||||||
|
Audio::getEchoesToFilter(call.filterer, AUDIO_FRAME_SAMPLE_COUNT);
|
||||||
|
|
||||||
|
call.filterer->filterAudio(buf, AUDIO_FRAME_SAMPLE_COUNT);
|
||||||
|
}
|
||||||
|
else if (call.filterer)
|
||||||
|
{
|
||||||
|
delete call.filterer;
|
||||||
|
call.filterer = nullptr;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
groupCalls[groupId].sendAudioTimer->start();
|
|
||||||
|
if (toxav_group_send_audio(toxav_get_tox(toxav), groupId, buf, AUDIO_FRAME_SAMPLE_COUNT,
|
||||||
|
AUDIO_CHANNELS, AUDIO_SAMPLE_RATE) != 0)
|
||||||
|
qDebug() << "toxav_group_send_audio error";
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreAV::disableGroupCallMic(int groupId)
|
void CoreAV::disableGroupCallMic(int groupId)
|
||||||
|
@ -413,20 +294,19 @@ void CoreAV::resetCallSources()
|
||||||
{
|
{
|
||||||
for (ToxGroupCall& call : groupCalls)
|
for (ToxGroupCall& call : groupCalls)
|
||||||
{
|
{
|
||||||
for (ALuint source : call.alSources)
|
if (call.alSource)
|
||||||
alDeleteSources(1, &source);
|
{
|
||||||
call.alSources.clear();
|
Audio::deleteSource(&call.alSource);
|
||||||
|
Audio::createSource(&call.alSource);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ToxCall& call : calls)
|
for (ToxFriendCall& call : calls)
|
||||||
{
|
{
|
||||||
if (call.alSource)
|
if (call.alSource)
|
||||||
{
|
{
|
||||||
ALuint tmp = call.alSource;
|
Audio::deleteSource(&call.alSource);
|
||||||
call.alSource = 0;
|
Audio::createSource(&call.alSource);
|
||||||
alDeleteSources(1, &tmp);
|
|
||||||
|
|
||||||
alGenSources(1, &call.alSource);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -452,7 +332,7 @@ void CoreAV::stateCallback(ToxAV *, uint32_t friendNum, uint32_t state, void *_s
|
||||||
CoreAV* self = static_cast<CoreAV*>(_self);
|
CoreAV* self = static_cast<CoreAV*>(_self);
|
||||||
|
|
||||||
assert(self->calls.contains(friendNum));
|
assert(self->calls.contains(friendNum));
|
||||||
ToxCall& call = self->calls[friendNum];
|
ToxFriendCall& call = self->calls[friendNum];
|
||||||
|
|
||||||
if (state & TOXAV_FRIEND_CALL_STATE_ERROR)
|
if (state & TOXAV_FRIEND_CALL_STATE_ERROR)
|
||||||
{
|
{
|
||||||
|
@ -467,10 +347,10 @@ void CoreAV::stateCallback(ToxAV *, uint32_t friendNum, uint32_t state, void *_s
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If our state was null, we started the call and still ringing
|
// If our state was null, we started the call and were still ringing
|
||||||
if (!call.state && state)
|
if (!call.state && state)
|
||||||
{
|
{
|
||||||
call.ringing = false;
|
call.inactive = false;
|
||||||
emit self->avStart(friendNum, call.videoEnabled);
|
emit self->avStart(friendNum, call.videoEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -523,6 +403,6 @@ void CoreAV::videoFrameCallback(ToxAV *, uint32_t friendNum, uint16_t w, uint16_
|
||||||
frame.stride[1] = ustride;
|
frame.stride[1] = ustride;
|
||||||
frame.stride[2] = vstride;
|
frame.stride[2] = vstride;
|
||||||
|
|
||||||
ToxCall& call = calls[friendNum];
|
ToxFriendCall& call = calls[friendNum];
|
||||||
call.videoSource->pushFrame(&frame);
|
call.videoSource->pushFrame(&frame);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,8 @@
|
||||||
#ifndef COREAV_H
|
#ifndef COREAV_H
|
||||||
#define COREAV_H
|
#define COREAV_H
|
||||||
|
|
||||||
#include <QHash>
|
#include <QObject>
|
||||||
#include <QThread>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <map>
|
|
||||||
#include <tox/toxav.h>
|
#include <tox/toxav.h>
|
||||||
|
|
||||||
#if defined(__APPLE__) && defined(__MACH__)
|
#if defined(__APPLE__) && defined(__MACH__)
|
||||||
|
@ -35,7 +33,7 @@
|
||||||
#include <AL/alc.h>
|
#include <AL/alc.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "src/core/indexedlist.h"
|
#include "src/core/toxcall.h"
|
||||||
|
|
||||||
#ifdef QTOX_FILTER_AUDIO
|
#ifdef QTOX_FILTER_AUDIO
|
||||||
class AudioFilterer;
|
class AudioFilterer;
|
||||||
|
@ -49,43 +47,7 @@ class VideoFrame;
|
||||||
class CoreAV;
|
class CoreAV;
|
||||||
struct vpx_image;
|
struct vpx_image;
|
||||||
|
|
||||||
struct ToxCall
|
class CoreAV : public QObject
|
||||||
{
|
|
||||||
ToxCall() = default;
|
|
||||||
ToxCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av);
|
|
||||||
ToxCall(const ToxCall& other) = delete;
|
|
||||||
ToxCall(ToxCall&& other) noexcept;
|
|
||||||
~ToxCall();
|
|
||||||
|
|
||||||
inline operator int() {return friendNum;}
|
|
||||||
const ToxCall& operator=(const ToxCall& other) = delete;
|
|
||||||
const ToxCall& operator=(ToxCall&& other) noexcept;
|
|
||||||
|
|
||||||
QTimer* sendAudioTimer;
|
|
||||||
uint32_t friendNum;
|
|
||||||
bool ringing; ///< True while we're ringing and the call hasn't started yet
|
|
||||||
bool muteMic;
|
|
||||||
bool muteVol;
|
|
||||||
bool videoEnabled; ///< True if our user asked for a video call, sending and recving
|
|
||||||
ALuint alSource;
|
|
||||||
CoreVideoSource* videoSource;
|
|
||||||
TOXAV_FRIEND_CALL_STATE state; ///< State of the peer (not ours!)
|
|
||||||
#ifdef QTOX_FILTER_AUDIO
|
|
||||||
AudioFilterer* filterer;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ToxGroupCall
|
|
||||||
{
|
|
||||||
QTimer *sendAudioTimer;
|
|
||||||
int groupId;
|
|
||||||
bool active = false;
|
|
||||||
bool muteMic;
|
|
||||||
bool muteVol;
|
|
||||||
QHash<int, ALuint> alSources;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CoreAV : public QThread
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -100,12 +62,11 @@ public:
|
||||||
bool anyActiveCalls(); ///< true is any calls are currently active (note: a call about to start is not yet active)
|
bool anyActiveCalls(); ///< true is any calls are currently active (note: a call about to start is not yet active)
|
||||||
void prepareCall(uint32_t friendId, ToxAV *toxav, bool videoEnabled);
|
void prepareCall(uint32_t friendId, ToxAV *toxav, bool videoEnabled);
|
||||||
void cleanupCall(uint32_t friendId);
|
void cleanupCall(uint32_t friendId);
|
||||||
void sendCallAudio(uint32_t friendId);
|
bool sendCallAudio(uint32_t friendId); ///< Returns false only on error, but not if there's nothing to send
|
||||||
void playAudioBuffer(ALuint alSource, const int16_t *data, int samples,
|
void playAudioBuffer(ALuint alSource, const int16_t *data, int samples,
|
||||||
unsigned channels, int sampleRate);
|
unsigned channels, int sampleRate);
|
||||||
void playCallVideo(void *toxav, int32_t callId, const vpx_image* img, void *user_data);
|
|
||||||
void sendCallVideo(uint32_t friendId, std::shared_ptr<VideoFrame> frame);
|
void sendCallVideo(uint32_t friendId, std::shared_ptr<VideoFrame> frame);
|
||||||
void sendGroupCallAudio(int groupId, ToxAV* toxav);
|
bool sendGroupCallAudio(int groupId);
|
||||||
|
|
||||||
VideoSource* getVideoSourceFromCall(int callNumber); ///< Get a call's video source
|
VideoSource* getVideoSourceFromCall(int callNumber); ///< Get a call's video source
|
||||||
void resetCallSources(); ///< Forces to regenerate each call's audio sources
|
void resetCallSources(); ///< Forces to regenerate each call's audio sources
|
||||||
|
@ -160,8 +121,8 @@ private:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ToxAV* toxav;
|
ToxAV* toxav;
|
||||||
static IndexedList<ToxCall> calls;
|
static IndexedList<ToxFriendCall> calls;
|
||||||
static QHash<int, ToxGroupCall> groupCalls; // Maps group IDs to ToxGroupCalls
|
static IndexedList<ToxGroupCall> groupCalls; // Maps group IDs to ToxGroupCalls
|
||||||
|
|
||||||
friend class Audio;
|
friend class Audio;
|
||||||
friend class Core;
|
friend class Core;
|
||||||
|
|
176
src/core/toxcall.cpp
Normal file
176
src/core/toxcall.cpp
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
#include "src/audio/audio.h"
|
||||||
|
#include "src/core/toxcall.h"
|
||||||
|
#include "src/core/coreav.h"
|
||||||
|
#include "src/persistence/settings.h"
|
||||||
|
#include "src/video/camerasource.h"
|
||||||
|
#include "src/video/corevideosource.h"
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#ifdef QTOX_FILTER_AUDIO
|
||||||
|
#include "src/audio/audiofilterer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
ToxCall::ToxCall(uint32_t CallId)
|
||||||
|
: sendAudioTimer{new QTimer}, callId{CallId},
|
||||||
|
inactive{true}, muteMic{false}, muteVol{false}, alSource{0}
|
||||||
|
{
|
||||||
|
sendAudioTimer->setInterval(5);
|
||||||
|
sendAudioTimer->setSingleShot(true);
|
||||||
|
|
||||||
|
Audio::getInstance().subscribeInput();
|
||||||
|
|
||||||
|
#ifdef QTOX_FILTER_AUDIO
|
||||||
|
if (Settings::getInstance().getFilterAudio())
|
||||||
|
{
|
||||||
|
filterer = new AudioFilterer();
|
||||||
|
filterer->startFilter(AUDIO_SAMPLE_RATE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filterer = nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxCall::ToxCall(ToxCall&& other)
|
||||||
|
: sendAudioTimer{other.sendAudioTimer}, callId{other.callId},
|
||||||
|
inactive{other.inactive}, muteMic{other.muteMic}, muteVol{other.muteVol},
|
||||||
|
alSource{other.alSource}
|
||||||
|
{
|
||||||
|
other.sendAudioTimer = nullptr;
|
||||||
|
other.callId = numeric_limits<decltype(callId)>::max();
|
||||||
|
other.alSource = 0;
|
||||||
|
|
||||||
|
#ifdef QTOX_FILTER_AUDIO
|
||||||
|
filterer = other.filterer;
|
||||||
|
other.filterer = nullptr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxCall::~ToxCall()
|
||||||
|
{
|
||||||
|
if (sendAudioTimer)
|
||||||
|
{
|
||||||
|
QObject::disconnect(sendAudioTimer, nullptr, nullptr, nullptr);
|
||||||
|
sendAudioTimer->stop();
|
||||||
|
Audio::getInstance().unsubscribeInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alSource)
|
||||||
|
Audio::deleteSource(&alSource);
|
||||||
|
|
||||||
|
#ifdef QTOX_FILTER_AUDIO
|
||||||
|
if (filterer)
|
||||||
|
delete filterer;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
const ToxCall& ToxCall::operator=(ToxCall&& other)
|
||||||
|
{
|
||||||
|
sendAudioTimer = other.sendAudioTimer;
|
||||||
|
other.sendAudioTimer = nullptr;
|
||||||
|
callId = other.callId;
|
||||||
|
other.callId = numeric_limits<decltype(callId)>::max();
|
||||||
|
inactive = other.inactive;
|
||||||
|
muteMic = other.muteMic;
|
||||||
|
muteVol = other.muteVol;
|
||||||
|
alSource = other.alSource;
|
||||||
|
other.alSource = 0;
|
||||||
|
|
||||||
|
#ifdef QTOX_FILTER_AUDIO
|
||||||
|
filterer = other.filterer;
|
||||||
|
other.filterer = nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxFriendCall::ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av)
|
||||||
|
: ToxCall(FriendNum),
|
||||||
|
videoEnabled{VideoEnabled}, videoSource{nullptr},
|
||||||
|
state{static_cast<TOXAV_FRIEND_CALL_STATE>(0)}
|
||||||
|
{
|
||||||
|
auto audioTimerCopy = sendAudioTimer; // this might change after a move, but not sendAudioTimer
|
||||||
|
QObject::connect(sendAudioTimer, &QTimer::timeout, [FriendNum,&av,audioTimerCopy]()
|
||||||
|
{
|
||||||
|
// If sendCallAudio returns false, there was a serious error and we might as well stop the timer
|
||||||
|
if (av.sendCallAudio(FriendNum))
|
||||||
|
audioTimerCopy->start();
|
||||||
|
});
|
||||||
|
sendAudioTimer->start();
|
||||||
|
|
||||||
|
if (videoEnabled)
|
||||||
|
{
|
||||||
|
videoSource = new CoreVideoSource;
|
||||||
|
CameraSource& source = CameraSource::getInstance();
|
||||||
|
if (!source.isOpen())
|
||||||
|
source.open();
|
||||||
|
source.subscribe();
|
||||||
|
QObject::connect(&source, &VideoSource::frameAvailable,
|
||||||
|
[FriendNum,&av](shared_ptr<VideoFrame> frame){av.sendCallVideo(FriendNum,frame);});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxFriendCall::ToxFriendCall(ToxFriendCall&& other)
|
||||||
|
: ToxCall(move(other)),
|
||||||
|
videoEnabled{other.videoEnabled}, videoSource{other.videoSource},
|
||||||
|
state{other.state}
|
||||||
|
{
|
||||||
|
other.videoEnabled = false;
|
||||||
|
other.videoSource = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxFriendCall::~ToxFriendCall()
|
||||||
|
{
|
||||||
|
if (videoEnabled)
|
||||||
|
{
|
||||||
|
CameraSource::getInstance().unsubscribe();
|
||||||
|
if (videoSource)
|
||||||
|
{
|
||||||
|
videoSource->setDeleteOnClose(true);
|
||||||
|
videoSource = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ToxFriendCall& ToxFriendCall::operator=(ToxFriendCall&& other)
|
||||||
|
{
|
||||||
|
ToxCall::operator =(move(other));
|
||||||
|
videoEnabled = other.videoEnabled;
|
||||||
|
other.videoEnabled = false;
|
||||||
|
videoSource = other.videoSource;
|
||||||
|
other.videoSource = nullptr;
|
||||||
|
state = other.state;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxGroupCall::ToxGroupCall(int GroupNum, CoreAV &av)
|
||||||
|
: ToxCall(static_cast<decltype(callId)>(GroupNum))
|
||||||
|
{
|
||||||
|
static_assert(numeric_limits<decltype(callId)>::max() >= numeric_limits<decltype(GroupNum)>::max(),
|
||||||
|
"The callId must be able to represent any group number, change its type if needed");
|
||||||
|
|
||||||
|
auto audioTimerCopy = sendAudioTimer; // this might change after a move, but not sendAudioTimer
|
||||||
|
QObject::connect(sendAudioTimer, &QTimer::timeout, [GroupNum,&av,audioTimerCopy]()
|
||||||
|
{
|
||||||
|
// If sendGroupCallAudio returns false, there was a serious error and we might as well stop the timer
|
||||||
|
if (av.sendGroupCallAudio(GroupNum))
|
||||||
|
audioTimerCopy->start();
|
||||||
|
});
|
||||||
|
sendAudioTimer->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxGroupCall::ToxGroupCall(ToxGroupCall&& other)
|
||||||
|
: ToxCall(move(other))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const ToxGroupCall &ToxGroupCall::operator=(ToxGroupCall &&other)
|
||||||
|
{
|
||||||
|
ToxCall::operator =(move(other));
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
77
src/core/toxcall.h
Normal file
77
src/core/toxcall.h
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
#ifndef TOXCALL_H
|
||||||
|
#define TOXCALL_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include "src/core/indexedlist.h"
|
||||||
|
|
||||||
|
#if defined(__APPLE__) && defined(__MACH__)
|
||||||
|
#include <OpenAL/al.h>
|
||||||
|
#include <OpenAL/alc.h>
|
||||||
|
#else
|
||||||
|
#include <AL/al.h>
|
||||||
|
#include <AL/alc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <tox/toxav.h>
|
||||||
|
|
||||||
|
class QTimer;
|
||||||
|
class AudioFilterer;
|
||||||
|
class CoreVideoSource;
|
||||||
|
class CoreAV;
|
||||||
|
|
||||||
|
struct ToxCall
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
ToxCall() = default;
|
||||||
|
ToxCall(uint32_t CallId);
|
||||||
|
~ToxCall();
|
||||||
|
public:
|
||||||
|
ToxCall(const ToxCall& other) = delete;
|
||||||
|
ToxCall(ToxCall&& other) noexcept;
|
||||||
|
|
||||||
|
inline operator int() {return callId;}
|
||||||
|
const ToxCall& operator=(const ToxCall& other) = delete;
|
||||||
|
const ToxCall& operator=(ToxCall&& other) noexcept;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QTimer* sendAudioTimer;
|
||||||
|
|
||||||
|
public:
|
||||||
|
uint32_t callId; ///< Could be a friendNum or groupNum, must uniquely identify the call
|
||||||
|
bool inactive; ///< True while we're not participating. (stopped group call, ringing but hasn't started yet, ...)
|
||||||
|
bool muteMic;
|
||||||
|
bool muteVol;
|
||||||
|
ALuint alSource;
|
||||||
|
|
||||||
|
#ifdef QTOX_FILTER_AUDIO
|
||||||
|
AudioFilterer* filterer;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ToxFriendCall : public ToxCall
|
||||||
|
{
|
||||||
|
ToxFriendCall() = default;
|
||||||
|
ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av);
|
||||||
|
ToxFriendCall(ToxFriendCall&& other) noexcept;
|
||||||
|
~ToxFriendCall();
|
||||||
|
|
||||||
|
const ToxFriendCall& operator=(ToxFriendCall&& other) noexcept;
|
||||||
|
|
||||||
|
bool videoEnabled; ///< True if our user asked for a video call, sending and recving
|
||||||
|
CoreVideoSource* videoSource;
|
||||||
|
TOXAV_FRIEND_CALL_STATE state; ///< State of the peer (not ours!)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ToxGroupCall : public ToxCall
|
||||||
|
{
|
||||||
|
ToxGroupCall() = default;
|
||||||
|
ToxGroupCall(int GroupNum, CoreAV& av);
|
||||||
|
ToxGroupCall(ToxGroupCall&& other) noexcept;
|
||||||
|
|
||||||
|
const ToxGroupCall& operator=(ToxGroupCall&& other) noexcept;
|
||||||
|
|
||||||
|
// If you add something here, don't forget to override the ctors and move operators!
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TOXCALL_H
|
||||||
|
|
|
@ -50,7 +50,7 @@ private:
|
||||||
std::atomic_bool biglock; ///< Fast lock
|
std::atomic_bool biglock; ///< Fast lock
|
||||||
|
|
||||||
friend class CoreAV;
|
friend class CoreAV;
|
||||||
friend struct ToxCall;
|
friend struct ToxFriendCall;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // COREVIDEOSOURCE_H
|
#endif // COREVIDEOSOURCE_H
|
||||||
|
|
Loading…
Reference in New Issue
Block a user