22 #include "audio/iaudiosettings.h"
29 #include "util/compatiblerecursivemutex.h"
30 #include <QCoreApplication>
34 #include <QtConcurrent/QtConcurrentRun>
73 CoreAV::CoreAV(std::unique_ptr<ToxAV, ToxAVDeleter> toxav, CompatibleRecursiveMutex& toxCoreLock,
76 , toxav{std::move(toxav)}
77 , coreavThread{
new QThread{
this}}
78 , iterateTimer{
new QTimer{
this}}
79 , coreLock{toxCoreLock}
80 , audioSettings{_audioSettings}
81 , groupSettings{_groupSettings}
86 coreavThread->setObjectName(
"qTox CoreAV");
87 moveToThread(coreavThread.get());
89 connectCallbacks(*this->toxav);
91 iterateTimer->setSingleShot(
true);
94 connect(coreavThread.get(), &QThread::finished, iterateTimer, &QTimer::stop);
117 std::unique_ptr<ToxAV, ToxAVDeleter>
toxav{toxav_new(core, &err)};
119 case TOXAV_ERR_NEW_OK:
121 case TOXAV_ERR_NEW_MALLOC:
122 qCritical() <<
"Failed to allocate resources for ToxAV";
124 case TOXAV_ERR_NEW_MULTIPLE:
125 qCritical() <<
"Attempted to create multiple ToxAV instances";
127 case TOXAV_ERR_NEW_NULL:
128 qCritical() <<
"Unexpected NULL parameter";
132 assert(
toxav !=
nullptr);
144 audio.exchange(&newAudio);
161 for (
const auto& call :
calls) {
168 assert(
calls.empty());
186 toxav_iterate(
toxav.get());
221 if (it ==
calls.end()) {
254 qDebug() << QString(
"Answering call %1").arg(friendNum);
255 auto it =
calls.find(friendNum);
256 assert(it !=
calls.end());
257 Toxav_Err_Answer err;
261 videoBitrate, &err)) {
262 it->second->setActive(
true);
265 qWarning() <<
"Failed to answer call with error" << err;
266 toxav_call_control(
toxav.get(), friendNum, TOXAV_CALL_CONTROL_CANCEL,
nullptr);
277 qDebug() << QString(
"Starting call with %1").arg(friendNum);
278 auto it =
calls.find(friendNum);
279 if (it !=
calls.end()) {
280 qWarning() << QString(
"Can't start call with %1, we're already in this call!").arg(friendNum);
285 if (!toxav_call(
toxav.get(), friendNum,
audioSettings.getAudioBitrate(), videoBitrate,
290 assert(
audio !=
nullptr);
293 call->moveToThread(this->thread());
294 assert(call !=
nullptr);
295 calls.emplace(friendNum, std::move(call));
304 qDebug() << QString(
"Cancelling call with %1").arg(friendNum);
305 if (!toxav_call_control(
toxav.get(), friendNum, TOXAV_CALL_CONTROL_CANCEL,
nullptr)) {
306 qWarning() << QString(
"Failed to cancel call with %1").arg(friendNum);
310 calls.erase(friendNum);
313 emit
avEnd(friendNum);
322 qWarning() << QString(
"Failed to timeout call with %1").arg(friendNum);
325 qDebug() <<
"Call with friend" << friendNum <<
"timed out";
342 auto it =
calls.find(callId);
343 if (it ==
calls.end()) {
350 || !(call.
getState() & TOXAV_FRIEND_CALL_STATE_ACCEPTING_A)) {
355 Toxav_Err_Send_Frame err;
358 if (!toxav_audio_send_frame(
toxav.get(), callId, pcm, samples, chans, rate, &err)) {
359 if (err == TOXAV_ERR_SEND_FRAME_SYNC) {
361 QThread::usleep(500);
363 qDebug() <<
"toxav_audio_send_frame error: " << err;
366 }
while (err == TOXAV_ERR_SEND_FRAME_SYNC && retries < 5);
367 if (err == TOXAV_ERR_SEND_FRAME_SYNC) {
368 qDebug() <<
"toxav_audio_send_frame error: Lock busy, dropping frame";
380 auto it =
calls.find(callId);
381 if (it ==
calls.end()) {
388 || !(call.
getState() & TOXAV_FRIEND_CALL_STATE_ACCEPTING_V)) {
393 qDebug() <<
"Restarting video stream to friend" << callId;
407 Toxav_Err_Send_Frame err;
410 if (!toxav_video_send_frame(
toxav.get(), callId, frame.
width, frame.
height, frame.
y,
411 frame.
u, frame.
v, &err)) {
412 if (err == TOXAV_ERR_SEND_FRAME_SYNC) {
414 QThread::usleep(500);
416 qDebug() <<
"toxav_video_send_frame error: " << err;
419 }
while (err == TOXAV_ERR_SEND_FRAME_SYNC && retries < 5);
420 if (err == TOXAV_ERR_SEND_FRAME_SYNC) {
421 qDebug() <<
"toxav_video_send_frame error: Lock busy, dropping frame";
434 if (f && (it !=
calls.end())) {
449 if (f && (it !=
calls.end())) {
468 unsigned samples, uint8_t channels, uint32_t sample_rate,
void* core)
519 it->second->removePeer(peerPk);
531 auto it =
calls.find(friendNum);
532 if (it ==
calls.end()) {
533 qWarning() <<
"CoreAV::getVideoSourceFromCall: No such call, possibly cancelled";
537 return it->second->getVideoSource();
549 qDebug() << QString(
"Joining group call %1").arg(group.
getId());
552 assert(
audio !=
nullptr);
556 groupcall->moveToThread(this->thread());
557 assert(groupcall !=
nullptr);
559 if (ret.second ==
false) {
560 qWarning() <<
"This group call already exists, not joining!";
563 ret.first->second->setActive(
true);
575 qDebug() << QString(
"Leaving group call %1").arg(groupId);
585 std::map<int, ToxGroupCallPtr>::const_iterator it =
groupCalls.find(groupId);
590 if (!it->second->isActive() || it->second->getMuteMic()) {
594 if (toxav_group_send_audio(toxav_get_tox(
toxav.get()), groupId, pcm, samples, chans, rate) != 0)
595 qDebug() <<
"toxav_group_send_audio error";
611 it->second->setMuteMic(mute);
626 it->second->setMuteVol(mute);
643 const uint32_t groupId = g->
getId();
645 return (it !=
groupCalls.end()) && it->second->getMuteMic();
661 const uint32_t groupId = g->
getId();
663 return (it !=
groupCalls.end()) && it->second->getMuteVol();
678 const uint32_t friendId = f->
getId();
679 auto it =
calls.find(friendId);
680 return (it !=
calls.end()) && it->second->getMuteMic();
695 const uint32_t friendId = f->
getId();
696 auto it =
calls.find(friendId);
697 return (it !=
calls.end()) && it->second->getMuteVol();
709 qDebug() <<
"CoreAV: Signaling end of video sending";
710 for (
auto& kv :
calls) {
712 toxav_video_set_bit_rate(
toxav.get(), kv.first, 0,
nullptr);
724 assert(self->audio !=
nullptr);
727 call->moveToThread(self->thread());
728 assert(call !=
nullptr);
730 auto it =
self->calls.emplace(friendNum, std::move(call));
731 if (it.second ==
false) {
732 qWarning() << QString(
"Rejecting call invite from %1, we're already in that call!").arg(friendNum);
733 toxav_call_control(
toxav, friendNum, TOXAV_CALL_CONTROL_CANCEL,
nullptr);
736 qDebug() << QString(
"Received call invite from %1").arg(friendNum);
741 state |= TOXAV_FRIEND_CALL_STATE_SENDING_A | TOXAV_FRIEND_CALL_STATE_ACCEPTING_A;
743 state |= TOXAV_FRIEND_CALL_STATE_SENDING_V | TOXAV_FRIEND_CALL_STATE_ACCEPTING_V;
744 it.first->second->setState(
static_cast<TOXAV_FRIEND_CALL_STATE
>(state));
749 emit
self->avInvite(friendNum, video);
760 auto it =
self->calls.find(friendNum);
761 if (it == self->calls.end()) {
762 qWarning() << QString(
"stateCallback called, but call %1 is already dead").arg(friendNum);
768 if (state & TOXAV_FRIEND_CALL_STATE_ERROR) {
769 qWarning() <<
"Call with friend" << friendNum <<
"died of unnatural causes!";
770 self->calls.erase(friendNum);
772 emit
self->avEnd(friendNum,
true);
773 }
else if (state & TOXAV_FRIEND_CALL_STATE_FINISHED) {
774 qDebug() <<
"Call with friend" << friendNum <<
"finished quietly";
775 self->calls.erase(friendNum);
777 emit
self->avEnd(friendNum);
783 call.
setState(
static_cast<TOXAV_FRIEND_CALL_STATE
>(state));
785 emit
self->avStart(friendNum, videoEnabled);
786 }
else if ((call.
getState() & TOXAV_FRIEND_CALL_STATE_SENDING_V)
787 && !(state & TOXAV_FRIEND_CALL_STATE_SENDING_V)) {
788 qDebug() <<
"Friend" << friendNum <<
"stopped sending video";
793 call.
setState(
static_cast<TOXAV_FRIEND_CALL_STATE
>(state));
794 }
else if (!(call.
getState() & TOXAV_FRIEND_CALL_STATE_SENDING_V)
795 && (state & TOXAV_FRIEND_CALL_STATE_SENDING_V)) {
805 call.
setState(
static_cast<TOXAV_FRIEND_CALL_STATE
>(state));
818 qDebug() <<
"Recommended bitrate with" << friendNum <<
" is now " << arate <<
"/" << vrate
829 qDebug() <<
"Recommended audio bitrate with" << friendNum <<
" is now " << rate <<
", ignoring it";
839 qDebug() <<
"Recommended video bitrate with" << friendNum <<
" is now " << rate <<
", ignoring it";
843 uint8_t channels, uint32_t samplingRate,
void* vSelf)
847 assert(QThread::currentThread() == self->coreavThread.get());
848 QReadLocker locker{&
self->callsLock};
850 auto it =
self->calls.find(friendNum);
851 if (it == self->calls.end()) {
865 const uint8_t* y,
const uint8_t* u,
const uint8_t* v,
866 int32_t ystride, int32_t ustride, int32_t vstride,
void* vSelf)
868 auto self =
static_cast<CoreAV*
>(vSelf);
870 assert(QThread::currentThread() == self->coreavThread.get());
871 QReadLocker locker{&
self->callsLock};
873 auto it =
self->calls.find(friendNum);
874 if (it == self->calls.end()) {
886 frame.planes[0] =
const_cast<uint8_t*
>(y);
887 frame.planes[1] =
const_cast<uint8_t*
>(u);
888 frame.planes[2] =
const_cast<uint8_t*
>(v);
889 frame.stride[0] = ystride;
890 frame.stride[1] = ustride;
891 frame.stride[2] = vstride;