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/micfeedbackwidget.cpp \
|
||||
src/widget/tool/removefrienddialog.cpp \
|
||||
src/video/groupnetcamview.cpp
|
||||
src/video/groupnetcamview.cpp \
|
||||
src/core/toxcall.cpp
|
||||
|
||||
HEADERS += \
|
||||
src/audio/audio.h \
|
||||
|
@ -553,4 +554,5 @@ HEADERS += \
|
|||
src/widget/tool/movablewidget.h \
|
||||
src/video/genericnetcamview.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)
|
||||
{
|
||||
if (!call.active)
|
||||
continue;
|
||||
for (ALuint source : call.alSources)
|
||||
alSourcef(source, AL_GAIN, outputVolume);
|
||||
alSourcef(call.alSource, AL_GAIN, outputVolume);
|
||||
}
|
||||
|
||||
for (const ToxCall& call : CoreAV::calls)
|
||||
for (const ToxFriendCall& call : CoreAV::calls)
|
||||
{
|
||||
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);
|
||||
QMutexLocker lock(&audioOutLock);
|
||||
|
||||
ToxGroupCall& call = CoreAV::groupCalls[group];
|
||||
|
||||
if (!call.active || call.muteVol)
|
||||
if (!CoreAV::groupCalls.contains(group))
|
||||
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]);
|
||||
alSourcef(call.alSources[peer], AL_GAIN, outputVolume);
|
||||
alGenSources(1, &call.alSource);
|
||||
alSourcef(call.alSource, AL_GAIN, outputVolume);
|
||||
}
|
||||
|
||||
qreal volume = 0.;
|
||||
|
@ -410,7 +410,7 @@ void Audio::playGroupAudio(int group, int peer, const int16_t* data,
|
|||
|
||||
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)
|
||||
|
@ -471,6 +471,20 @@ bool Audio::isOutputClosed()
|
|||
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
|
||||
*/
|
||||
|
|
|
@ -72,6 +72,9 @@ public:
|
|||
bool isInputReady();
|
||||
bool isOutputClosed();
|
||||
|
||||
static void createSource(ALuint* source);
|
||||
static void deleteSource(ALuint* source);
|
||||
|
||||
void playMono16Sound(const QByteArray& data);
|
||||
bool tryCaptureSamples(int16_t *buf, int samples);
|
||||
|
||||
|
|
|
@ -728,7 +728,7 @@ void Core::removeGroup(int groupId, bool fake)
|
|||
|
||||
tox_del_groupchat(tox, groupId);
|
||||
|
||||
if (av->groupCalls[groupId].active)
|
||||
if (!av->groupCalls[groupId].inactive)
|
||||
av->leaveGroupCall(groupId);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,121 +20,21 @@
|
|||
|
||||
#include "core.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/persistence/settings.h"
|
||||
#include "src/video/videoframe.h"
|
||||
#include "src/video/corevideosource.h"
|
||||
#include <assert.h>
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
#include "src/audio/audiofilterer.h"
|
||||
#endif
|
||||
#include "src/persistence/settings.h"
|
||||
#include <assert.h>
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
|
||||
IndexedList<ToxCall> CoreAV::calls;
|
||||
QHash<int, ToxGroupCall> CoreAV::groupCalls;
|
||||
IndexedList<ToxFriendCall> CoreAV::calls;
|
||||
IndexedList<ToxGroupCall> CoreAV::groupCalls;
|
||||
|
||||
ToxCall::ToxCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av)
|
||||
: 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;
|
||||
}
|
||||
using namespace std;
|
||||
|
||||
CoreAV::CoreAV(Tox *tox)
|
||||
{
|
||||
|
@ -150,8 +50,8 @@ CoreAV::CoreAV(Tox *tox)
|
|||
|
||||
CoreAV::~CoreAV()
|
||||
{
|
||||
for (const ToxCall& call : calls)
|
||||
cancelCall(call.friendNum);
|
||||
for (const ToxFriendCall& call : calls)
|
||||
cancelCall(call.callId);
|
||||
toxav_kill(toxav);
|
||||
}
|
||||
|
||||
|
@ -177,7 +77,7 @@ void CoreAV::answerCall(uint32_t friendNum)
|
|||
TOXAV_ERR_ANSWER 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);
|
||||
}
|
||||
else
|
||||
|
@ -209,19 +109,18 @@ void CoreAV::cancelCall(uint32_t friendId)
|
|||
calls.remove(friendId);
|
||||
}
|
||||
|
||||
void CoreAV::sendCallAudio(uint32_t callId)
|
||||
bool CoreAV::sendCallAudio(uint32_t 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)
|
||||
|| !Audio::getInstance().isInputReady())
|
||||
{
|
||||
call.sendAudioTimer->start();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
call.sendAudioTimer->start();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CoreAV::playCallVideo(void*, int32_t callId, const vpx_image *img, void *user_data)
|
||||
{
|
||||
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)
|
||||
void CoreAV::sendCallVideo(uint32_t callId, shared_ptr<VideoFrame> vframe)
|
||||
{
|
||||
if (!calls.contains(callId))
|
||||
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))
|
||||
return;
|
||||
|
||||
|
@ -313,65 +202,57 @@ VideoSource *CoreAV::getVideoSourceFromCall(int friendNum)
|
|||
void CoreAV::joinGroupCall(int 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
|
||||
Audio::getInstance().subscribeInput();
|
||||
|
||||
// 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();
|
||||
auto call = groupCalls.insert({groupId, *this});
|
||||
call->inactive = false;
|
||||
}
|
||||
|
||||
void CoreAV::leaveGroupCall(int groupId)
|
||||
{
|
||||
qDebug() << QString("Leaving group call %1").arg(groupId);
|
||||
groupCalls[groupId].active = false;
|
||||
disconnect(groupCalls[groupId].sendAudioTimer,0,0,0);
|
||||
groupCalls[groupId].sendAudioTimer->stop();
|
||||
for (ALuint source : groupCalls[groupId].alSources)
|
||||
alDeleteSources(1, &source);
|
||||
groupCalls[groupId].alSources.clear();
|
||||
Audio::getInstance().unsubscribeInput();
|
||||
delete groupCalls[groupId].sendAudioTimer;
|
||||
|
||||
groupCalls.remove(groupId);
|
||||
}
|
||||
|
||||
void CoreAV::sendGroupCallAudio(int groupId, ToxAV *toxav)
|
||||
bool CoreAV::sendGroupCallAudio(int groupId)
|
||||
{
|
||||
if (!groupCalls[groupId].active)
|
||||
return;
|
||||
if (!groupCalls.contains(groupId))
|
||||
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();
|
||||
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];
|
||||
|
||||
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)
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
if (Settings::getInstance().getFilterAudio())
|
||||
{
|
||||
qDebug() << "toxav_group_send_audio error";
|
||||
groupCalls[groupId].sendAudioTimer->start();
|
||||
return;
|
||||
if (!call.filterer)
|
||||
{
|
||||
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
|
||||
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)
|
||||
|
@ -413,20 +294,19 @@ void CoreAV::resetCallSources()
|
|||
{
|
||||
for (ToxGroupCall& call : groupCalls)
|
||||
{
|
||||
for (ALuint source : call.alSources)
|
||||
alDeleteSources(1, &source);
|
||||
call.alSources.clear();
|
||||
if (call.alSource)
|
||||
{
|
||||
Audio::deleteSource(&call.alSource);
|
||||
Audio::createSource(&call.alSource);
|
||||
}
|
||||
}
|
||||
|
||||
for (ToxCall& call : calls)
|
||||
for (ToxFriendCall& call : calls)
|
||||
{
|
||||
if (call.alSource)
|
||||
{
|
||||
ALuint tmp = call.alSource;
|
||||
call.alSource = 0;
|
||||
alDeleteSources(1, &tmp);
|
||||
|
||||
alGenSources(1, &call.alSource);
|
||||
Audio::deleteSource(&call.alSource);
|
||||
Audio::createSource(&call.alSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -452,7 +332,7 @@ void CoreAV::stateCallback(ToxAV *, uint32_t friendNum, uint32_t state, void *_s
|
|||
CoreAV* self = static_cast<CoreAV*>(_self);
|
||||
|
||||
assert(self->calls.contains(friendNum));
|
||||
ToxCall& call = self->calls[friendNum];
|
||||
ToxFriendCall& call = self->calls[friendNum];
|
||||
|
||||
if (state & TOXAV_FRIEND_CALL_STATE_ERROR)
|
||||
{
|
||||
|
@ -467,10 +347,10 @@ void CoreAV::stateCallback(ToxAV *, uint32_t friendNum, uint32_t state, void *_s
|
|||
}
|
||||
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)
|
||||
{
|
||||
call.ringing = false;
|
||||
call.inactive = false;
|
||||
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[2] = vstride;
|
||||
|
||||
ToxCall& call = calls[friendNum];
|
||||
ToxFriendCall& call = calls[friendNum];
|
||||
call.videoSource->pushFrame(&frame);
|
||||
}
|
||||
|
|
|
@ -21,10 +21,8 @@
|
|||
#ifndef COREAV_H
|
||||
#define COREAV_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QThread>
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <tox/toxav.h>
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
|
@ -35,7 +33,7 @@
|
|||
#include <AL/alc.h>
|
||||
#endif
|
||||
|
||||
#include "src/core/indexedlist.h"
|
||||
#include "src/core/toxcall.h"
|
||||
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
class AudioFilterer;
|
||||
|
@ -49,43 +47,7 @@ class VideoFrame;
|
|||
class CoreAV;
|
||||
struct vpx_image;
|
||||
|
||||
struct ToxCall
|
||||
{
|
||||
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
|
||||
class CoreAV : public QObject
|
||||
{
|
||||
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)
|
||||
void prepareCall(uint32_t friendId, ToxAV *toxav, bool videoEnabled);
|
||||
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,
|
||||
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 sendGroupCallAudio(int groupId, ToxAV* toxav);
|
||||
bool sendGroupCallAudio(int groupId);
|
||||
|
||||
VideoSource* getVideoSourceFromCall(int callNumber); ///< Get a call's video source
|
||||
void resetCallSources(); ///< Forces to regenerate each call's audio sources
|
||||
|
@ -160,8 +121,8 @@ private:
|
|||
|
||||
private:
|
||||
ToxAV* toxav;
|
||||
static IndexedList<ToxCall> calls;
|
||||
static QHash<int, ToxGroupCall> groupCalls; // Maps group IDs to ToxGroupCalls
|
||||
static IndexedList<ToxFriendCall> calls;
|
||||
static IndexedList<ToxGroupCall> groupCalls; // Maps group IDs to ToxGroupCalls
|
||||
|
||||
friend class Audio;
|
||||
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
|
||||
|
||||
friend class CoreAV;
|
||||
friend struct ToxCall;
|
||||
friend struct ToxFriendCall;
|
||||
};
|
||||
|
||||
#endif // COREVIDEOSOURCE_H
|
||||
|
|
Loading…
Reference in New Issue
Block a user