1
0
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:
tux3 2015-10-05 02:36:50 +02:00
parent 6cbd507ca8
commit f45256baf1
9 changed files with 365 additions and 252 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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