mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
ce2f8fd1d5
We now subscribe to an event and wait for frames when capturing audio input, the big avdantage is that we only have to fetch the frames from the hardware once, and we don't need to cache anything. The frames are simply dispatched to the client's callbacks immediately. Also removes some outdated ifdefs that did not apply anymore.
187 lines
5.5 KiB
C++
187 lines
5.5 KiB
C++
#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>
|
|
#include <QtConcurrent/QtConcurrent>
|
|
|
|
#ifdef QTOX_FILTER_AUDIO
|
|
#include "src/audio/audiofilterer.h"
|
|
#endif
|
|
|
|
using namespace std;
|
|
|
|
ToxCall::ToxCall(uint32_t CallId)
|
|
: callId{CallId}, alSource{0},
|
|
inactive{true}, muteMic{false}, muteVol{false}
|
|
{
|
|
Audio& audio = Audio::getInstance();
|
|
audio.subscribeInput();
|
|
audio.subscribeOutput(alSource);
|
|
}
|
|
|
|
ToxCall::ToxCall(ToxCall&& other) noexcept
|
|
: audioInConn{other.audioInConn}, callId{other.callId}, alSource{other.alSource},
|
|
inactive{other.inactive}, muteMic{other.muteMic}, muteVol{other.muteVol}
|
|
{
|
|
other.audioInConn = QMetaObject::Connection();
|
|
other.callId = numeric_limits<decltype(callId)>::max();
|
|
other.alSource = 0;
|
|
|
|
// required -> ownership of audio input is moved to new instance
|
|
Audio& audio = Audio::getInstance();
|
|
audio.subscribeInput();
|
|
}
|
|
|
|
ToxCall::~ToxCall()
|
|
{
|
|
Audio& audio = Audio::getInstance();
|
|
|
|
QObject::disconnect(audioInConn);
|
|
audio.unsubscribeInput();
|
|
audio.unsubscribeOutput(alSource);
|
|
}
|
|
|
|
const ToxCall& ToxCall::operator=(ToxCall&& other) noexcept
|
|
{
|
|
audioInConn = other.audioInConn;
|
|
other.audioInConn = QMetaObject::Connection();
|
|
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;
|
|
|
|
// required -> ownership of audio input is moved to new instance
|
|
Audio& audio = Audio::getInstance();
|
|
audio.subscribeInput();
|
|
|
|
return *this;
|
|
}
|
|
|
|
void ToxFriendCall::startTimeout()
|
|
{
|
|
if (!timeoutTimer)
|
|
{
|
|
timeoutTimer = new QTimer();
|
|
// We might move, so we need copies of members. CoreAV won't move while we're alive
|
|
CoreAV* avCopy = av;
|
|
auto callIdCopy = callId;
|
|
QObject::connect(timeoutTimer, &QTimer::timeout, [avCopy, callIdCopy](){
|
|
avCopy->timeoutCall(callIdCopy);
|
|
});
|
|
}
|
|
|
|
if (!timeoutTimer->isActive())
|
|
timeoutTimer->start(CALL_TIMEOUT);
|
|
}
|
|
|
|
void ToxFriendCall::stopTimeout()
|
|
{
|
|
if (!timeoutTimer)
|
|
return;
|
|
|
|
timeoutTimer->stop();
|
|
delete timeoutTimer;
|
|
timeoutTimer = nullptr;
|
|
}
|
|
|
|
ToxFriendCall::ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av)
|
|
: ToxCall(FriendNum),
|
|
videoEnabled{VideoEnabled}, nullVideoBitrate{false}, videoSource{nullptr},
|
|
state{static_cast<TOXAV_FRIEND_CALL_STATE>(0)},
|
|
av{&av}, timeoutTimer{nullptr}
|
|
{
|
|
audioInConn = QObject::connect(&Audio::getInstance(), &Audio::frameAvailable,
|
|
[&av,FriendNum](const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate)
|
|
{
|
|
av.sendCallAudio(FriendNum, pcm, samples, chans, rate);
|
|
});
|
|
|
|
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) noexcept
|
|
: ToxCall(move(other)),
|
|
videoEnabled{other.videoEnabled}, nullVideoBitrate{other.nullVideoBitrate},
|
|
videoSource{other.videoSource}, state{other.state},
|
|
av{other.av}, timeoutTimer{other.timeoutTimer}
|
|
{
|
|
other.videoEnabled = false;
|
|
other.videoSource = nullptr;
|
|
other.timeoutTimer = nullptr;
|
|
}
|
|
|
|
ToxFriendCall::~ToxFriendCall()
|
|
{
|
|
if (timeoutTimer)
|
|
delete timeoutTimer;
|
|
|
|
if (videoEnabled)
|
|
{
|
|
// This destructor could be running in a toxav callback while holding toxav locks.
|
|
// If the CameraSource thread calls toxav *_send_frame, we might deadlock the toxav and CameraSource locks,
|
|
// so we unsuscribe asynchronously, it's fine if the webcam takes a couple milliseconds more to poweroff.
|
|
QtConcurrent::run([](){CameraSource::getInstance().unsubscribe();});
|
|
if (videoSource)
|
|
{
|
|
videoSource->setDeleteOnClose(true);
|
|
videoSource = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
const ToxFriendCall& ToxFriendCall::operator=(ToxFriendCall&& other) noexcept
|
|
{
|
|
ToxCall::operator =(move(other));
|
|
videoEnabled = other.videoEnabled;
|
|
other.videoEnabled = false;
|
|
videoSource = other.videoSource;
|
|
other.videoSource = nullptr;
|
|
state = other.state;
|
|
timeoutTimer = other.timeoutTimer;
|
|
other.timeoutTimer = nullptr;
|
|
av = other.av;
|
|
nullVideoBitrate = other.nullVideoBitrate;
|
|
|
|
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");
|
|
|
|
audioInConn = QObject::connect(&Audio::getInstance(), &Audio::frameAvailable,
|
|
[&av,GroupNum](const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate)
|
|
{
|
|
av.sendGroupCallAudio(GroupNum, pcm, samples, chans, rate);
|
|
});
|
|
}
|
|
|
|
ToxGroupCall::ToxGroupCall(ToxGroupCall&& other) noexcept
|
|
: ToxCall(move(other))
|
|
{
|
|
}
|
|
|
|
const ToxGroupCall &ToxGroupCall::operator=(ToxGroupCall &&other) noexcept
|
|
{
|
|
ToxCall::operator =(move(other));
|
|
|
|
return *this;
|
|
}
|