|
|
|
|
@ -99,6 +99,7 @@ CoreAV::CoreAV(std::unique_ptr<ToxAV, ToxAVDeleter> toxav_, CompatibleRecursiveM
@@ -99,6 +99,7 @@ CoreAV::CoreAV(std::unique_ptr<ToxAV, ToxAVDeleter> toxav_, CompatibleRecursiveM
|
|
|
|
|
connect(iterateTimer, &QTimer::timeout, this, &CoreAV::process); |
|
|
|
|
connect(coreavThread.get(), &QThread::finished, iterateTimer, &QTimer::stop); |
|
|
|
|
connect(coreavThread.get(), &QThread::started, this, &CoreAV::process); |
|
|
|
|
qRegisterMetaType<ToxFriendCallPtr>("ToxFriendCallPtr"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void CoreAV::connectCallbacks() |
|
|
|
|
@ -195,43 +196,6 @@ void CoreAV::process()
@@ -195,43 +196,6 @@ void CoreAV::process()
|
|
|
|
|
iterateTimer->start(toxav_iteration_interval(toxav.get())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Checks the call status for a Tox friend. |
|
|
|
|
* @param f the friend to check |
|
|
|
|
* @return true, if call is started for the friend, false otherwise |
|
|
|
|
*/ |
|
|
|
|
bool CoreAV::isCallStarted(const Friend* f) const |
|
|
|
|
{ |
|
|
|
|
QReadLocker locker{&callsLock}; |
|
|
|
|
return f && (calls.find(f->getId()) != calls.end()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Checks the call status for a Tox group. |
|
|
|
|
* @param g the group to check |
|
|
|
|
* @return true, if call is started for the group, false otherwise |
|
|
|
|
*/ |
|
|
|
|
bool CoreAV::isCallStarted(const Group* g) const |
|
|
|
|
{ |
|
|
|
|
QReadLocker locker{&callsLock}; |
|
|
|
|
return g && (groupCalls.find(g->getId()) != groupCalls.end()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Checks the call status for a Tox friend. |
|
|
|
|
* @param f the friend to check |
|
|
|
|
* @return true, if call is active for the friend, false otherwise |
|
|
|
|
*/ |
|
|
|
|
bool CoreAV::isCallActive(const Friend* f) const |
|
|
|
|
{ |
|
|
|
|
QReadLocker locker{&callsLock}; |
|
|
|
|
auto it = calls.find(f->getId()); |
|
|
|
|
if (it == calls.end()) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
return isCallStarted(f) && it->second->isActive(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Checks the call status for a Tox group. |
|
|
|
|
* @param g the group to check |
|
|
|
|
@ -244,14 +208,7 @@ bool CoreAV::isCallActive(const Group* g) const
@@ -244,14 +208,7 @@ bool CoreAV::isCallActive(const Group* g) const
|
|
|
|
|
if (it == groupCalls.end()) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
return isCallStarted(g) && it->second->isActive(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool CoreAV::isCallVideoEnabled(const Friend* f) const |
|
|
|
|
{ |
|
|
|
|
QReadLocker locker{&callsLock}; |
|
|
|
|
auto it = calls.find(f->getId()); |
|
|
|
|
return isCallStarted(f) && it->second->getVideoEnabled(); |
|
|
|
|
return it->second->isActive(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool CoreAV::answerCall(uint32_t friendNum, bool video) |
|
|
|
|
@ -279,7 +236,7 @@ bool CoreAV::answerCall(uint32_t friendNum, bool video)
@@ -279,7 +236,7 @@ bool CoreAV::answerCall(uint32_t friendNum, bool video)
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool CoreAV::startCall(uint32_t friendNum, bool video) |
|
|
|
|
CoreAV::ToxFriendCallPtr CoreAV::startCall(uint32_t friendNum, bool video) |
|
|
|
|
{ |
|
|
|
|
QWriteLocker locker{&callsLock}; |
|
|
|
|
QMutexLocker coreLocker{&coreLock}; |
|
|
|
|
@ -288,7 +245,7 @@ bool CoreAV::startCall(uint32_t friendNum, bool video)
@@ -288,7 +245,7 @@ bool CoreAV::startCall(uint32_t friendNum, bool video)
|
|
|
|
|
auto it = calls.find(friendNum); |
|
|
|
|
if (it != calls.end()) { |
|
|
|
|
qWarning() << QString("Can't start call with %1, we're already in this call!").arg(friendNum); |
|
|
|
|
return false; |
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
uint32_t videoBitrate = video ? VIDEO_DEFAULT_BITRATE : 0; |
|
|
|
|
@ -296,7 +253,7 @@ bool CoreAV::startCall(uint32_t friendNum, bool video)
@@ -296,7 +253,7 @@ bool CoreAV::startCall(uint32_t friendNum, bool video)
|
|
|
|
|
toxav_call(toxav.get(), friendNum, audioSettings.getAudioBitrate(), videoBitrate, |
|
|
|
|
&err); |
|
|
|
|
if (!PARSE_ERR(err)) { |
|
|
|
|
return false; |
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Audio backend must be set before making a call
|
|
|
|
|
@ -306,8 +263,8 @@ bool CoreAV::startCall(uint32_t friendNum, bool video)
@@ -306,8 +263,8 @@ bool CoreAV::startCall(uint32_t friendNum, bool video)
|
|
|
|
|
// Call object must be owned by this thread or there will be locking problems with Audio
|
|
|
|
|
call->moveToThread(thread()); |
|
|
|
|
assert(call != nullptr); |
|
|
|
|
calls.emplace(friendNum, std::move(call)); |
|
|
|
|
return true; |
|
|
|
|
calls.emplace(friendNum, call.get()); |
|
|
|
|
return call; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool CoreAV::cancelCall(uint32_t friendNum) |
|
|
|
|
@ -315,6 +272,11 @@ bool CoreAV::cancelCall(uint32_t friendNum)
@@ -315,6 +272,11 @@ bool CoreAV::cancelCall(uint32_t friendNum)
|
|
|
|
|
QWriteLocker locker{&callsLock}; |
|
|
|
|
QMutexLocker coreLocker{&coreLock}; |
|
|
|
|
|
|
|
|
|
auto it = calls.find(friendNum); |
|
|
|
|
if (it == calls.end()) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
qDebug() << QString("Cancelling call with %1").arg(friendNum); |
|
|
|
|
Toxav_Err_Call_Control err; |
|
|
|
|
toxav_call_control(toxav.get(), friendNum, TOXAV_CALL_CONTROL_CANCEL, &err); |
|
|
|
|
@ -329,17 +291,6 @@ bool CoreAV::cancelCall(uint32_t friendNum)
@@ -329,17 +291,6 @@ bool CoreAV::cancelCall(uint32_t friendNum)
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void CoreAV::timeoutCall(uint32_t friendNum) |
|
|
|
|
{ |
|
|
|
|
QWriteLocker locker{&callsLock}; |
|
|
|
|
|
|
|
|
|
if (!cancelCall(friendNum)) { |
|
|
|
|
qWarning() << QString("Failed to timeout call with %1").arg(friendNum); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
qDebug() << "Call with friend" << friendNum << "timed out"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Send audio frame to a friend |
|
|
|
|
* @param callId Id of friend in call list. |
|
|
|
|
@ -441,36 +392,6 @@ void CoreAV::sendCallVideo(uint32_t callId, std::shared_ptr<VideoFrame> vframe)
@@ -441,36 +392,6 @@ void CoreAV::sendCallVideo(uint32_t callId, std::shared_ptr<VideoFrame> vframe)
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Toggles the mute state of the call's input (microphone). |
|
|
|
|
* @param f The friend assigned to the call |
|
|
|
|
*/ |
|
|
|
|
void CoreAV::toggleMuteCallInput(const Friend* f) |
|
|
|
|
{ |
|
|
|
|
QWriteLocker locker{&callsLock}; |
|
|
|
|
|
|
|
|
|
auto it = calls.find(f->getId()); |
|
|
|
|
if (f && (it != calls.end())) { |
|
|
|
|
ToxCall& call = *it->second; |
|
|
|
|
call.setMuteMic(!call.getMuteMic()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Toggles the mute state of the call's output (speaker). |
|
|
|
|
* @param f The friend assigned to the call |
|
|
|
|
*/ |
|
|
|
|
void CoreAV::toggleMuteCallOutput(const Friend* f) |
|
|
|
|
{ |
|
|
|
|
QWriteLocker locker{&callsLock}; |
|
|
|
|
|
|
|
|
|
auto it = calls.find(f->getId()); |
|
|
|
|
if (f && (it != calls.end())) { |
|
|
|
|
ToxCall& call = *it->second; |
|
|
|
|
call.setMuteVol(!call.getMuteVol()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Called from Tox API when group call receives audio data. |
|
|
|
|
* |
|
|
|
|
@ -513,7 +434,7 @@ void CoreAV::groupCallCallback(void* tox, uint32_t group, uint32_t peer, const i
@@ -513,7 +434,7 @@ void CoreAV::groupCallCallback(void* tox, uint32_t group, uint32_t peer, const i
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ToxGroupCall& call = *it->second; |
|
|
|
|
auto& call = *it->second; |
|
|
|
|
|
|
|
|
|
if (call.getMuteVol() || !call.isActive()) { |
|
|
|
|
return; |
|
|
|
|
@ -522,46 +443,12 @@ void CoreAV::groupCallCallback(void* tox, uint32_t group, uint32_t peer, const i
@@ -522,46 +443,12 @@ void CoreAV::groupCallCallback(void* tox, uint32_t group, uint32_t peer, const i
|
|
|
|
|
call.playAudioBuffer(peerPk, data, samples, channels, sample_rate); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Called from core to make sure the source for that peer is invalidated when they leave. |
|
|
|
|
* @param group Group Index |
|
|
|
|
* @param peer Peer Index |
|
|
|
|
*/ |
|
|
|
|
void CoreAV::invalidateGroupCallPeerSource(const Group& group, ToxPk peerPk) |
|
|
|
|
{ |
|
|
|
|
QWriteLocker locker{&callsLock}; |
|
|
|
|
|
|
|
|
|
auto it = groupCalls.find(group.getId()); |
|
|
|
|
if (it == groupCalls.end()) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
it->second->removePeer(peerPk); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Get a call's video source. |
|
|
|
|
* @param friendNum Id of friend in call list. |
|
|
|
|
* @return Video surface to show |
|
|
|
|
*/ |
|
|
|
|
VideoSource* CoreAV::getVideoSourceFromCall(int friendNum) const |
|
|
|
|
{ |
|
|
|
|
QReadLocker locker{&callsLock}; |
|
|
|
|
|
|
|
|
|
auto it = calls.find(friendNum); |
|
|
|
|
if (it == calls.end()) { |
|
|
|
|
qWarning() << "CoreAV::getVideoSourceFromCall: No such call, possibly cancelled"; |
|
|
|
|
return nullptr; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return it->second->getVideoSource(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Starts a call in an existing AV groupchat. |
|
|
|
|
* @note Call from the GUI thread. |
|
|
|
|
* @param groupId Id of group to join |
|
|
|
|
*/ |
|
|
|
|
void CoreAV::joinGroupCall(const Group& group) |
|
|
|
|
CoreAV::ToxGroupCallPtr CoreAV::joinGroupCall(const Group& group) |
|
|
|
|
{ |
|
|
|
|
QWriteLocker locker{&callsLock}; |
|
|
|
|
|
|
|
|
|
@ -572,14 +459,15 @@ void CoreAV::joinGroupCall(const Group& group)
@@ -572,14 +459,15 @@ void CoreAV::joinGroupCall(const Group& group)
|
|
|
|
|
|
|
|
|
|
ToxGroupCallPtr groupcall = ToxGroupCallPtr(new ToxGroupCall{group, *this, *audio}); |
|
|
|
|
// Call Objects must be owned by CoreAV or there will be locking problems with Audio
|
|
|
|
|
groupcall->moveToThread(thread()); |
|
|
|
|
assert(groupcall != nullptr); |
|
|
|
|
auto ret = groupCalls.emplace(group.getId(), std::move(groupcall)); |
|
|
|
|
groupcall->moveToThread(this->thread()); |
|
|
|
|
auto ret = groupCalls.emplace(group.getId(), groupcall.get()); |
|
|
|
|
if (ret.second == false) { |
|
|
|
|
qWarning() << "This group call already exists, not joining!"; |
|
|
|
|
return; |
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
ret.first->second->setActive(true); |
|
|
|
|
groupcall->setActive(true); |
|
|
|
|
return groupcall; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -601,7 +489,7 @@ bool CoreAV::sendGroupCallAudio(int groupNum, const int16_t* pcm, size_t samples
@@ -601,7 +489,7 @@ bool CoreAV::sendGroupCallAudio(int groupNum, const int16_t* pcm, size_t samples
|
|
|
|
|
{ |
|
|
|
|
QReadLocker locker{&callsLock}; |
|
|
|
|
|
|
|
|
|
std::map<int, ToxGroupCallPtr>::const_iterator it = groupCalls.find(groupNum); |
|
|
|
|
auto it = groupCalls.find(groupNum); |
|
|
|
|
if (it == groupCalls.end()) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
@ -616,106 +504,6 @@ bool CoreAV::sendGroupCallAudio(int groupNum, const int16_t* pcm, size_t samples
@@ -616,106 +504,6 @@ bool CoreAV::sendGroupCallAudio(int groupNum, const int16_t* pcm, size_t samples
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Mutes or unmutes the group call's input (microphone). |
|
|
|
|
* @param g The group |
|
|
|
|
* @param mute True to mute, false to unmute |
|
|
|
|
*/ |
|
|
|
|
void CoreAV::muteCallInput(const Group* g, bool mute) |
|
|
|
|
{ |
|
|
|
|
QWriteLocker locker{&callsLock}; |
|
|
|
|
|
|
|
|
|
auto it = groupCalls.find(g->getId()); |
|
|
|
|
if (g && (it != groupCalls.end())) { |
|
|
|
|
it->second->setMuteMic(mute); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Mutes or unmutes the group call's output (speaker). |
|
|
|
|
* @param g The group |
|
|
|
|
* @param mute True to mute, false to unmute |
|
|
|
|
*/ |
|
|
|
|
void CoreAV::muteCallOutput(const Group* g, bool mute) |
|
|
|
|
{ |
|
|
|
|
QWriteLocker locker{&callsLock}; |
|
|
|
|
|
|
|
|
|
auto it = groupCalls.find(g->getId()); |
|
|
|
|
if (g && (it != groupCalls.end())) { |
|
|
|
|
it->second->setMuteVol(mute); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Returns the group calls input (microphone) state. |
|
|
|
|
* @param groupId The group id to check |
|
|
|
|
* @return true when muted, false otherwise |
|
|
|
|
*/ |
|
|
|
|
bool CoreAV::isGroupCallInputMuted(const Group* g) const |
|
|
|
|
{ |
|
|
|
|
QReadLocker locker{&callsLock}; |
|
|
|
|
|
|
|
|
|
if (!g) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const uint32_t groupId = g->getId(); |
|
|
|
|
auto it = groupCalls.find(groupId); |
|
|
|
|
return (it != groupCalls.end()) && it->second->getMuteMic(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Returns the group calls output (speaker) state. |
|
|
|
|
* @param groupId The group id to check |
|
|
|
|
* @return true when muted, false otherwise |
|
|
|
|
*/ |
|
|
|
|
bool CoreAV::isGroupCallOutputMuted(const Group* g) const |
|
|
|
|
{ |
|
|
|
|
QReadLocker locker{&callsLock}; |
|
|
|
|
|
|
|
|
|
if (!g) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const uint32_t groupId = g->getId(); |
|
|
|
|
auto it = groupCalls.find(groupId); |
|
|
|
|
return (it != groupCalls.end()) && it->second->getMuteVol(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Returns the calls input (microphone) mute state. |
|
|
|
|
* @param f The friend to check |
|
|
|
|
* @return true when muted, false otherwise |
|
|
|
|
*/ |
|
|
|
|
bool CoreAV::isCallInputMuted(const Friend* f) const |
|
|
|
|
{ |
|
|
|
|
QReadLocker locker{&callsLock}; |
|
|
|
|
|
|
|
|
|
if (!f) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
const uint32_t friendId = f->getId(); |
|
|
|
|
auto it = calls.find(friendId); |
|
|
|
|
return (it != calls.end()) && it->second->getMuteMic(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Returns the calls output (speaker) mute state. |
|
|
|
|
* @param friendId The friend to check |
|
|
|
|
* @return true when muted, false otherwise |
|
|
|
|
*/ |
|
|
|
|
bool CoreAV::isCallOutputMuted(const Friend* f) const |
|
|
|
|
{ |
|
|
|
|
QReadLocker locker{&callsLock}; |
|
|
|
|
|
|
|
|
|
if (!f) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
const uint32_t friendId = f->getId(); |
|
|
|
|
auto it = calls.find(friendId); |
|
|
|
|
return (it != calls.end()) && it->second->getMuteVol(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Signal to all peers that we're not sending video anymore. |
|
|
|
|
* @note The next frame sent cancels this. |
|
|
|
|
@ -751,7 +539,7 @@ void CoreAV::callCallback(ToxAV* toxav, uint32_t friendNum, bool audio, bool vid
@@ -751,7 +539,7 @@ void CoreAV::callCallback(ToxAV* toxav, uint32_t friendNum, bool audio, bool vid
|
|
|
|
|
call->moveToThread(self->thread()); |
|
|
|
|
assert(call != nullptr); |
|
|
|
|
|
|
|
|
|
auto it = self->calls.emplace(friendNum, std::move(call)); |
|
|
|
|
auto it = self->calls.emplace(friendNum, call.get()); |
|
|
|
|
if (it.second == false) { |
|
|
|
|
qWarning() << QString("Rejecting call invite from %1, we're already in that call!").arg(friendNum); |
|
|
|
|
Toxav_Err_Call_Control err; |
|
|
|
|
@ -772,7 +560,7 @@ void CoreAV::callCallback(ToxAV* toxav, uint32_t friendNum, bool audio, bool vid
@@ -772,7 +560,7 @@ void CoreAV::callCallback(ToxAV* toxav, uint32_t friendNum, bool audio, bool vid
|
|
|
|
|
// Must explicitely unlock, because a deadlock can happen via ChatForm/Audio
|
|
|
|
|
locker.unlock(); |
|
|
|
|
|
|
|
|
|
emit self->avInvite(friendNum, video); |
|
|
|
|
emit self->avInvite(friendNum, video, call); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void CoreAV::stateCallback(ToxAV* toxav, uint32_t friendNum, uint32_t state, void* vSelf) |
|
|
|
|
|