qTox  Version: nightly | Commit: bc751c8e1cac455f9690654fcfe0f560d2d7dfdd
coreav.cpp
Go to the documentation of this file.
1 /*
2  Copyright © 2013 by Maxim Biro <nurupo.contributions@gmail.com>
3  Copyright © 2014-2019 by The qTox Project Contributors
4 
5  This file is part of qTox, a Qt-based graphical interface for Tox.
6 
7  qTox is libre software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  qTox is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with qTox. If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include "coreav.h"
22 #include "audio/iaudiosettings.h"
23 #include "core.h"
24 #include "src/model/friend.h"
25 #include "src/model/group.h"
28 #include "src/video/videoframe.h"
29 #include "util/compatiblerecursivemutex.h"
30 #include <QCoreApplication>
31 #include <QDebug>
32 #include <QThread>
33 #include <QTimer>
34 #include <QtConcurrent/QtConcurrentRun>
35 #include <cassert>
36 
73 CoreAV::CoreAV(std::unique_ptr<ToxAV, ToxAVDeleter> toxav, CompatibleRecursiveMutex& toxCoreLock,
74  IAudioSettings& _audioSettings, IGroupSettings& _groupSettings)
75  : audio{nullptr}
76  , toxav{std::move(toxav)}
77  , coreavThread{new QThread{this}}
78  , iterateTimer{new QTimer{this}}
79  , coreLock{toxCoreLock}
80  , audioSettings{_audioSettings}
81  , groupSettings{_groupSettings}
82 {
83  assert(coreavThread);
84  assert(iterateTimer);
85 
86  coreavThread->setObjectName("qTox CoreAV");
87  moveToThread(coreavThread.get());
88 
89  connectCallbacks(*this->toxav);
90 
91  iterateTimer->setSingleShot(true);
92 
93  connect(iterateTimer, &QTimer::timeout, this, &CoreAV::process);
94  connect(coreavThread.get(), &QThread::finished, iterateTimer, &QTimer::stop);
95  connect(coreavThread.get(), &QThread::started, this, &CoreAV::process);
96 }
97 
98 void CoreAV::connectCallbacks(ToxAV& toxav)
99 {
100  toxav_callback_call(&toxav, CoreAV::callCallback, this);
101  toxav_callback_call_state(&toxav, CoreAV::stateCallback, this);
102  toxav_callback_audio_bit_rate(&toxav, CoreAV::audioBitrateCallback, this);
103  toxav_callback_video_bit_rate(&toxav, CoreAV::videoBitrateCallback, this);
104  toxav_callback_audio_receive_frame(&toxav, CoreAV::audioFrameCallback, this);
105  toxav_callback_video_receive_frame(&toxav, CoreAV::videoFrameCallback, this);
106 }
107 
113 CoreAV::CoreAVPtr CoreAV::makeCoreAV(Tox* core, CompatibleRecursiveMutex& toxCoreLock,
114  IAudioSettings& audioSettings, IGroupSettings& groupSettings)
115 {
116  Toxav_Err_New err;
117  std::unique_ptr<ToxAV, ToxAVDeleter> toxav{toxav_new(core, &err)};
118  switch (err) {
119  case TOXAV_ERR_NEW_OK:
120  break;
121  case TOXAV_ERR_NEW_MALLOC:
122  qCritical() << "Failed to allocate resources for ToxAV";
123  return {};
124  case TOXAV_ERR_NEW_MULTIPLE:
125  qCritical() << "Attempted to create multiple ToxAV instances";
126  return {};
127  case TOXAV_ERR_NEW_NULL:
128  qCritical() << "Unexpected NULL parameter";
129  return {};
130  }
131 
132  assert(toxav != nullptr);
133 
134  return CoreAVPtr{new CoreAV{std::move(toxav), toxCoreLock, audioSettings, groupSettings}};
135 }
136 
142 void CoreAV::setAudio(IAudioControl& newAudio)
143 {
144  audio.exchange(&newAudio);
145 }
146 
153 IAudioControl* CoreAV::getAudio()
154 {
155  return audio;
156 }
157 
159 {
160  /* Gracefully leave calls and group calls to avoid deadlocks in destructor */
161  for (const auto& call : calls) {
162  cancelCall(call.first);
163  }
164  for (const auto& call : groupCalls) {
165  leaveGroupCall(call.first);
166  }
167 
168  assert(calls.empty());
169  assert(groupCalls.empty());
170 
171  coreavThread->exit(0);
172  coreavThread->wait();
173 }
174 
179 {
180  coreavThread->start();
181 }
182 
184 {
185  assert(QThread::currentThread() == coreavThread.get());
186  toxav_iterate(toxav.get());
187  iterateTimer->start(toxav_iteration_interval(toxav.get()));
188 }
189 
195 bool CoreAV::isCallStarted(const Friend* f) const
196 {
197  QReadLocker locker{&callsLock};
198  return f && (calls.find(f->getId()) != calls.end());
199 }
200 
206 bool CoreAV::isCallStarted(const Group* g) const
207 {
208  QReadLocker locker{&callsLock};
209  return g && (groupCalls.find(g->getId()) != groupCalls.end());
210 }
211 
217 bool CoreAV::isCallActive(const Friend* f) const
218 {
219  QReadLocker locker{&callsLock};
220  auto it = calls.find(f->getId());
221  if (it == calls.end()) {
222  return false;
223  }
224  return isCallStarted(f) && it->second->isActive();
225 }
226 
232 bool CoreAV::isCallActive(const Group* g) const
233 {
234  QReadLocker locker{&callsLock};
235  auto it = groupCalls.find(g->getId());
236  if (it == groupCalls.end()) {
237  return false;
238  }
239  return isCallStarted(g) && it->second->isActive();
240 }
241 
242 bool CoreAV::isCallVideoEnabled(const Friend* f) const
243 {
244  QReadLocker locker{&callsLock};
245  auto it = calls.find(f->getId());
246  return isCallStarted(f) && it->second->getVideoEnabled();
247 }
248 
249 bool CoreAV::answerCall(uint32_t friendNum, bool video)
250 {
251  QWriteLocker locker{&callsLock};
252  QMutexLocker coreLocker{&coreLock};
253 
254  qDebug() << QString("Answering call %1").arg(friendNum);
255  auto it = calls.find(friendNum);
256  assert(it != calls.end());
257  Toxav_Err_Answer err;
258 
259  const uint32_t videoBitrate = video ? VIDEO_DEFAULT_BITRATE : 0;
260  if (toxav_answer(toxav.get(), friendNum, audioSettings.getAudioBitrate(),
261  videoBitrate, &err)) {
262  it->second->setActive(true);
263  return true;
264  } else {
265  qWarning() << "Failed to answer call with error" << err;
266  toxav_call_control(toxav.get(), friendNum, TOXAV_CALL_CONTROL_CANCEL, nullptr);
267  calls.erase(it);
268  return false;
269  }
270 }
271 
272 bool CoreAV::startCall(uint32_t friendNum, bool video)
273 {
274  QWriteLocker locker{&callsLock};
275  QMutexLocker coreLocker{&coreLock};
276 
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);
281  return false;
282  }
283 
284  uint32_t videoBitrate = video ? VIDEO_DEFAULT_BITRATE : 0;
285  if (!toxav_call(toxav.get(), friendNum, audioSettings.getAudioBitrate(), videoBitrate,
286  nullptr))
287  return false;
288 
289  // Audio backend must be set before making a call
290  assert(audio != nullptr);
291  ToxFriendCallPtr call = ToxFriendCallPtr(new ToxFriendCall(friendNum, video, *this, *audio));
292  // Call object must be owned by this thread or there will be locking problems with Audio
293  call->moveToThread(this->thread());
294  assert(call != nullptr);
295  calls.emplace(friendNum, std::move(call));
296  return true;
297 }
298 
299 bool CoreAV::cancelCall(uint32_t friendNum)
300 {
301  QWriteLocker locker{&callsLock};
302  QMutexLocker coreLocker{&coreLock};
303 
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);
307  return false;
308  }
309 
310  calls.erase(friendNum);
311  locker.unlock();
312 
313  emit avEnd(friendNum);
314  return true;
315 }
316 
317 void CoreAV::timeoutCall(uint32_t friendNum)
318 {
319  QWriteLocker locker{&callsLock};
320 
321  if (!cancelCall(friendNum)) {
322  qWarning() << QString("Failed to timeout call with %1").arg(friendNum);
323  return;
324  }
325  qDebug() << "Call with friend" << friendNum << "timed out";
326 }
327 
337 bool CoreAV::sendCallAudio(uint32_t callId, const int16_t* pcm, size_t samples, uint8_t chans,
338  uint32_t rate) const
339 {
340  QReadLocker locker{&callsLock};
341 
342  auto it = calls.find(callId);
343  if (it == calls.end()) {
344  return false;
345  }
346 
347  ToxFriendCall const& call = *it->second;
348 
349  if (call.getMuteMic() || !call.isActive()
350  || !(call.getState() & TOXAV_FRIEND_CALL_STATE_ACCEPTING_A)) {
351  return true;
352  }
353 
354  // TOXAV_ERR_SEND_FRAME_SYNC means toxav failed to lock, retry 5 times in this case
355  Toxav_Err_Send_Frame err;
356  int retries = 0;
357  do {
358  if (!toxav_audio_send_frame(toxav.get(), callId, pcm, samples, chans, rate, &err)) {
359  if (err == TOXAV_ERR_SEND_FRAME_SYNC) {
360  ++retries;
361  QThread::usleep(500);
362  } else {
363  qDebug() << "toxav_audio_send_frame error: " << err;
364  }
365  }
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";
369  }
370 
371  return true;
372 }
373 
374 void CoreAV::sendCallVideo(uint32_t callId, std::shared_ptr<VideoFrame> vframe)
375 {
376  QWriteLocker locker{&callsLock};
377 
378  // We might be running in the FFmpeg thread and holding the CameraSource lock
379  // So be careful not to deadlock with anything while toxav locks in toxav_video_send_frame
380  auto it = calls.find(callId);
381  if (it == calls.end()) {
382  return;
383  }
384 
385  ToxFriendCall& call = *it->second;
386 
387  if (!call.getVideoEnabled() || !call.isActive()
388  || !(call.getState() & TOXAV_FRIEND_CALL_STATE_ACCEPTING_V)) {
389  return;
390  }
391 
392  if (call.getNullVideoBitrate()) {
393  qDebug() << "Restarting video stream to friend" << callId;
394  QMutexLocker coreLocker{&coreLock};
395  toxav_video_set_bit_rate(toxav.get(), callId, VIDEO_DEFAULT_BITRATE, nullptr);
396  call.setNullVideoBitrate(false);
397  }
398 
399  ToxYUVFrame frame = vframe->toToxYUVFrame();
400 
401  if (!frame) {
402  return;
403  }
404 
405  // TOXAV_ERR_SEND_FRAME_SYNC means toxav failed to lock, retry 5 times in this case
406  // We don't want to be dropping iframes because of some lock held by toxav_iterate
407  Toxav_Err_Send_Frame err;
408  int retries = 0;
409  do {
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) {
413  ++retries;
414  QThread::usleep(500);
415  } else {
416  qDebug() << "toxav_video_send_frame error: " << err;
417  }
418  }
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";
422  }
423 }
424 
430 {
431  QWriteLocker locker{&callsLock};
432 
433  auto it = calls.find(f->getId());
434  if (f && (it != calls.end())) {
435  ToxCall& call = *it->second;
436  call.setMuteMic(!call.getMuteMic());
437  }
438 }
439 
445 {
446  QWriteLocker locker{&callsLock};
447 
448  auto it = calls.find(f->getId());
449  if (f && (it != calls.end())) {
450  ToxCall& call = *it->second;
451  call.setMuteVol(!call.getMuteVol());
452  }
453 }
454 
467 void CoreAV::groupCallCallback(void* tox, uint32_t group, uint32_t peer, const int16_t* data,
468  unsigned samples, uint8_t channels, uint32_t sample_rate, void* core)
469 {
470  /*
471  * Currently group call audio decoding is handled in the Tox thread by c-toxcore,
472  * so we can be sure that this function is always called from the Core thread.
473  * To change this, an API change in c-toxcore is needed and this function probably must be
474  * changed.
475  * See https://github.com/TokTok/c-toxcore/issues/1364 for details.
476  */
477 
478  Q_UNUSED(tox)
479  Core* c = static_cast<Core*>(core);
480  CoreAV* cav = c->getAv();
481 
482  QReadLocker locker{&cav->callsLock};
483 
484  const ToxPk peerPk = c->getGroupPeerPk(group, peer);
485  // don't play the audio if it comes from a muted peer
486  if (cav->groupSettings.getBlackList().contains(peerPk.toString())) {
487  return;
488  }
489 
490  emit c->groupPeerAudioPlaying(group, peerPk);
491 
492  auto it = cav->groupCalls.find(group);
493  if (it == cav->groupCalls.end()) {
494  return;
495  }
496 
497  ToxGroupCall& call = *it->second;
498 
499  if (call.getMuteVol() || !call.isActive()) {
500  return;
501  }
502 
503  call.playAudioBuffer(peerPk, data, samples, channels, sample_rate);
504 }
505 
512 {
513  QWriteLocker locker{&callsLock};
514 
515  auto it = groupCalls.find(group.getId());
516  if (it == groupCalls.end()) {
517  return;
518  }
519  it->second->removePeer(peerPk);
520 }
521 
528 {
529  QReadLocker locker{&callsLock};
530 
531  auto it = calls.find(friendNum);
532  if (it == calls.end()) {
533  qWarning() << "CoreAV::getVideoSourceFromCall: No such call, possibly cancelled";
534  return nullptr;
535  }
536 
537  return it->second->getVideoSource();
538 }
539 
545 void CoreAV::joinGroupCall(const Group& group)
546 {
547  QWriteLocker locker{&callsLock};
548 
549  qDebug() << QString("Joining group call %1").arg(group.getId());
550 
551  // Audio backend must be set before starting a call
552  assert(audio != nullptr);
553 
554  ToxGroupCallPtr groupcall = ToxGroupCallPtr(new ToxGroupCall{group, *this, *audio});
555  // Call Objects must be owned by CoreAV or there will be locking problems with Audio
556  groupcall->moveToThread(this->thread());
557  assert(groupcall != nullptr);
558  auto ret = groupCalls.emplace(group.getId(), std::move(groupcall));
559  if (ret.second == false) {
560  qWarning() << "This group call already exists, not joining!";
561  return;
562  }
563  ret.first->second->setActive(true);
564 }
565 
571 void CoreAV::leaveGroupCall(int groupId)
572 {
573  QWriteLocker locker{&callsLock};
574 
575  qDebug() << QString("Leaving group call %1").arg(groupId);
576 
577  groupCalls.erase(groupId);
578 }
579 
580 bool CoreAV::sendGroupCallAudio(int groupId, const int16_t* pcm, size_t samples, uint8_t chans,
581  uint32_t rate) const
582 {
583  QReadLocker locker{&callsLock};
584 
585  std::map<int, ToxGroupCallPtr>::const_iterator it = groupCalls.find(groupId);
586  if (it == groupCalls.end()) {
587  return false;
588  }
589 
590  if (!it->second->isActive() || it->second->getMuteMic()) {
591  return true;
592  }
593 
594  if (toxav_group_send_audio(toxav_get_tox(toxav.get()), groupId, pcm, samples, chans, rate) != 0)
595  qDebug() << "toxav_group_send_audio error";
596 
597  return true;
598 }
599 
605 void CoreAV::muteCallInput(const Group* g, bool mute)
606 {
607  QWriteLocker locker{&callsLock};
608 
609  auto it = groupCalls.find(g->getId());
610  if (g && (it != groupCalls.end())) {
611  it->second->setMuteMic(mute);
612  }
613 }
614 
620 void CoreAV::muteCallOutput(const Group* g, bool mute)
621 {
622  QWriteLocker locker{&callsLock};
623 
624  auto it = groupCalls.find(g->getId());
625  if (g && (it != groupCalls.end())) {
626  it->second->setMuteVol(mute);
627  }
628 }
629 
636 {
637  QReadLocker locker{&callsLock};
638 
639  if (!g) {
640  return false;
641  }
642 
643  const uint32_t groupId = g->getId();
644  auto it = groupCalls.find(groupId);
645  return (it != groupCalls.end()) && it->second->getMuteMic();
646 }
647 
654 {
655  QReadLocker locker{&callsLock};
656 
657  if (!g) {
658  return false;
659  }
660 
661  const uint32_t groupId = g->getId();
662  auto it = groupCalls.find(groupId);
663  return (it != groupCalls.end()) && it->second->getMuteVol();
664 }
665 
671 bool CoreAV::isCallInputMuted(const Friend* f) const
672 {
673  QReadLocker locker{&callsLock};
674 
675  if (!f) {
676  return false;
677  }
678  const uint32_t friendId = f->getId();
679  auto it = calls.find(friendId);
680  return (it != calls.end()) && it->second->getMuteMic();
681 }
682 
688 bool CoreAV::isCallOutputMuted(const Friend* f) const
689 {
690  QReadLocker locker{&callsLock};
691 
692  if (!f) {
693  return false;
694  }
695  const uint32_t friendId = f->getId();
696  auto it = calls.find(friendId);
697  return (it != calls.end()) && it->second->getMuteVol();
698 }
699 
705 {
706  QWriteLocker locker{&callsLock};
707 
708  // We don't change the audio bitrate, but we signal that we're not sending video anymore
709  qDebug() << "CoreAV: Signaling end of video sending";
710  for (auto& kv : calls) {
711  ToxFriendCall& call = *kv.second;
712  toxav_video_set_bit_rate(toxav.get(), kv.first, 0, nullptr);
713  call.setNullVideoBitrate(true);
714  }
715 }
716 
717 void CoreAV::callCallback(ToxAV* toxav, uint32_t friendNum, bool audio, bool video, void* vSelf)
718 {
719  CoreAV* self = static_cast<CoreAV*>(vSelf);
720 
721  QWriteLocker locker{&self->callsLock};
722 
723  // Audio backend must be set before receiving a call
724  assert(self->audio != nullptr);
725  ToxFriendCallPtr call = ToxFriendCallPtr(new ToxFriendCall{friendNum, video, *self, *self->audio});
726  // Call object must be owned by CoreAV thread or there will be locking problems with Audio
727  call->moveToThread(self->thread());
728  assert(call != nullptr);
729 
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);
734  return;
735  }
736  qDebug() << QString("Received call invite from %1").arg(friendNum);
737 
738  // We don't get a state callback when answering, so fill the state ourselves in advance
739  int state = 0;
740  if (audio)
741  state |= TOXAV_FRIEND_CALL_STATE_SENDING_A | TOXAV_FRIEND_CALL_STATE_ACCEPTING_A;
742  if (video)
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));
745 
746  // Must explicitely unlock, because a deadlock can happen via ChatForm/Audio
747  locker.unlock();
748 
749  emit self->avInvite(friendNum, video);
750 }
751 
752 void CoreAV::stateCallback(ToxAV* toxav, uint32_t friendNum, uint32_t state, void* vSelf)
753 {
754  Q_UNUSED(toxav)
755  CoreAV* self = static_cast<CoreAV*>(vSelf);
756 
757  // we must unlock this lock before emitting any signals
758  QWriteLocker locker{&self->callsLock};
759 
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);
763  return;
764  }
765 
766  ToxFriendCall& call = *it->second;
767 
768  if (state & TOXAV_FRIEND_CALL_STATE_ERROR) {
769  qWarning() << "Call with friend" << friendNum << "died of unnatural causes!";
770  self->calls.erase(friendNum);
771  locker.unlock();
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);
776  locker.unlock();
777  emit self->avEnd(friendNum);
778  } else {
779  // If our state was null, we started the call and were still ringing
780  if (!call.getState() && state) {
781  call.setActive(true);
782  bool videoEnabled = call.getVideoEnabled();
783  call.setState(static_cast<TOXAV_FRIEND_CALL_STATE>(state));
784  locker.unlock();
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";
789  if (call.getVideoSource()) {
790  call.getVideoSource()->stopSource();
791  }
792 
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)) {
796  // Workaround toxav sometimes firing callbacks for "send last frame" -> "stop sending
797  // video"
798  // out of orders (even though they were sent in order by the other end).
799  // We simply stop the videoSource from emitting anything while the other end says it's
800  // not sending
801  if (call.getVideoSource()) {
802  call.getVideoSource()->restartSource();
803  }
804 
805  call.setState(static_cast<TOXAV_FRIEND_CALL_STATE>(state));
806  }
807  }
808 }
809 
810 // This is only a dummy implementation for now
811 void CoreAV::bitrateCallback(ToxAV* toxav, uint32_t friendNum, uint32_t arate, uint32_t vrate,
812  void* vSelf)
813 {
814  CoreAV* self = static_cast<CoreAV*>(vSelf);
815  Q_UNUSED(self)
816  Q_UNUSED(toxav)
817 
818  qDebug() << "Recommended bitrate with" << friendNum << " is now " << arate << "/" << vrate
819  << ", ignoring it";
820 }
821 
822 // This is only a dummy implementation for now
823 void CoreAV::audioBitrateCallback(ToxAV* toxav, uint32_t friendNum, uint32_t rate, void* vSelf)
824 {
825  CoreAV* self = static_cast<CoreAV*>(vSelf);
826  Q_UNUSED(self)
827  Q_UNUSED(toxav)
828 
829  qDebug() << "Recommended audio bitrate with" << friendNum << " is now " << rate << ", ignoring it";
830 }
831 
832 // This is only a dummy implementation for now
833 void CoreAV::videoBitrateCallback(ToxAV* toxav, uint32_t friendNum, uint32_t rate, void* vSelf)
834 {
835  CoreAV* self = static_cast<CoreAV*>(vSelf);
836  Q_UNUSED(self)
837  Q_UNUSED(toxav)
838 
839  qDebug() << "Recommended video bitrate with" << friendNum << " is now " << rate << ", ignoring it";
840 }
841 
842 void CoreAV::audioFrameCallback(ToxAV*, uint32_t friendNum, const int16_t* pcm, size_t sampleCount,
843  uint8_t channels, uint32_t samplingRate, void* vSelf)
844 {
845  CoreAV* self = static_cast<CoreAV*>(vSelf);
846  // This callback should come from the CoreAV thread
847  assert(QThread::currentThread() == self->coreavThread.get());
848  QReadLocker locker{&self->callsLock};
849 
850  auto it = self->calls.find(friendNum);
851  if (it == self->calls.end()) {
852  return;
853  }
854 
855  ToxFriendCall& call = *it->second;
856 
857  if (call.getMuteVol()) {
858  return;
859  }
860 
861  call.playAudioBuffer(pcm, sampleCount, channels, samplingRate);
862 }
863 
864 void CoreAV::videoFrameCallback(ToxAV*, uint32_t friendNum, uint16_t w, uint16_t h,
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)
867 {
868  auto self = static_cast<CoreAV*>(vSelf);
869  // This callback should come from the CoreAV thread
870  assert(QThread::currentThread() == self->coreavThread.get());
871  QReadLocker locker{&self->callsLock};
872 
873  auto it = self->calls.find(friendNum);
874  if (it == self->calls.end()) {
875  return;
876  }
877 
878  CoreVideoSource* videoSource = it->second->getVideoSource();
879  if (!videoSource) {
880  return;
881  }
882 
883  vpx_image frame;
884  frame.d_h = h;
885  frame.d_w = w;
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;
892 
893  videoSource->pushFrame(&frame);
894 }
ToxGroupCall
Definition: toxcall.h:115
CoreAV::~CoreAV
~CoreAV()
Definition: coreav.cpp:158
CoreAV::groupCallCallback
static void groupCallCallback(void *tox, uint32_t group, uint32_t peer, const int16_t *data, unsigned samples, uint8_t channels, uint32_t sample_rate, void *core)
Called from Tox API when group call receives audio data.
Definition: coreav.cpp:467
CoreAV::audio
std::atomic< IAudioControl * > audio
Definition: coreav.h:137
CoreAV::callCallback
static void callCallback(ToxAV *toxAV, uint32_t friendNum, bool audio, bool video, void *self)
Definition: coreav.cpp:717
CoreAV::iterateTimer
QTimer * iterateTimer
Definition: coreav.h:140
friend.h
ToxGroupCall::playAudioBuffer
void playAudioBuffer(const ToxPk &peer, const int16_t *data, int samples, unsigned channels, int sampleRate)
Definition: toxcall.cpp:292
VideoSource
An abstract source of video frames.
Definition: videosource.h:29
CoreAV::ToxGroupCallPtr
std::unique_ptr< ToxGroupCall > ToxGroupCallPtr
Definition: coreav.h:149
ToxFriendCall::getState
TOXAV_FRIEND_CALL_STATE getState() const
Definition: toxcall.cpp:184
CoreAV::isCallVideoEnabled
bool isCallVideoEnabled(const Friend *f) const
Definition: coreav.cpp:242
CoreAV::CoreAV
CoreAV(std::unique_ptr< ToxAV, ToxAVDeleter > tox, CompatibleRecursiveMutex &toxCoreLock, IAudioSettings &_audioSettings, IGroupSettings &_groupSettings)
Definition: coreav.cpp:73
CoreAV::calls
std::map< uint32_t, ToxFriendCallPtr > calls
Maps friend IDs to ToxFriendCall.
Definition: coreav.h:146
IGroupSettings
Definition: igroupsettings.h:26
group.h
CoreAV::sendCallAudio
bool sendCallAudio(uint32_t friendNum, const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate) const
Send audio frame to a friend.
Definition: coreav.cpp:337
CoreAV::sendNoVideo
void sendNoVideo()
Signal to all peers that we're not sending video anymore.
Definition: coreav.cpp:704
ToxCall::getVideoEnabled
bool getVideoEnabled() const
Definition: toxcall.cpp:96
CoreAV::start
void start()
Starts the CoreAV main loop that calls toxav's main loop.
Definition: coreav.cpp:178
CoreAV::isCallOutputMuted
bool isCallOutputMuted(const Friend *f) const
Returns the calls output (speaker) mute state.
Definition: coreav.cpp:688
CoreAV::bitrateCallback
static void bitrateCallback(ToxAV *toxAV, uint32_t friendNum, uint32_t arate, uint32_t vrate, void *self)
Definition: coreav.cpp:811
CoreAV::isCallActive
bool isCallActive(const Friend *f) const
Checks the call status for a Tox friend.
Definition: coreav.cpp:217
CoreAV::answerCall
bool answerCall(uint32_t friendNum, bool video)
Definition: coreav.cpp:249
videoframe.h
ToxYUVFrame::u
const uint8_t * u
Definition: videoframe.h:51
CoreAV::connectCallbacks
void connectCallbacks(ToxAV &toxav)
Definition: coreav.cpp:98
CoreAV::ToxFriendCallPtr
std::unique_ptr< ToxFriendCall > ToxFriendCallPtr
Definition: coreav.h:141
CoreAV::coreLock
CompatibleRecursiveMutex & coreLock
needed to synchronize with the Core thread, some toxav_* functions must not execute at the same time ...
Definition: coreav.h:164
ToxCall::setMuteVol
void setMuteVol(bool value)
Definition: toxcall.cpp:81
ToxFriendCall::playAudioBuffer
void playAudioBuffer(const int16_t *data, int samples, unsigned channels, int sampleRate) const
Definition: toxcall.cpp:194
CoreAV::cancelCall
bool cancelCall(uint32_t friendNum)
Definition: coreav.cpp:299
ToxCall
Definition: toxcall.h:41
Core::groupPeerAudioPlaying
void groupPeerAudioPlaying(int groupnumber, ToxPk peerPk)
Group::getId
uint32_t getId() const override
Definition: group.cpp:140
CoreAV::getVideoSourceFromCall
VideoSource * getVideoSourceFromCall(int callNumber) const
Get a call's video source.
Definition: coreav.cpp:527
Core::getAv
const CoreAV * getAv() const
Definition: core.cpp:703
CoreAV::process
void process()
Definition: coreav.cpp:183
ToxCall::getMuteMic
bool getMuteMic() const
Definition: toxcall.cpp:86
CoreAV::groupSettings
IGroupSettings & groupSettings
Definition: coreav.h:167
coreav.h
CoreAV::startCall
bool startCall(uint32_t friendNum, bool video)
Definition: coreav.cpp:272
IGroupSettings::getBlackList
virtual QStringList getBlackList() const =0
ToxCall::setActive
void setActive(bool value)
Definition: toxcall.cpp:71
CoreAV::invalidateGroupCallPeerSource
void invalidateGroupCallPeerSource(const Group &group, ToxPk peerPk)
Called from core to make sure the source for that peer is invalidated when they leave.
Definition: coreav.cpp:511
CoreVideoSource::pushFrame
void pushFrame(const vpx_image_t *frame)
Makes a copy of the vpx_image_t and emits it as a new VideoFrame.
Definition: corevideosource.cpp:60
ToxYUVFrame::height
const std::uint16_t height
Definition: videoframe.h:48
Core::getGroupPeerPk
ToxPk getGroupPeerPk(int groupId, int peerId) const override
Get the public key of a peer of a group.
Definition: core.cpp:1540
ToxFriendCall
Definition: toxcall.h:89
ToxYUVFrame::v
const uint8_t * v
Definition: videoframe.h:52
ToxYUVFrame::width
const std::uint16_t width
Definition: videoframe.h:47
ToxPk
This class represents a Tox Public Key, which is a part of Tox ID.
Definition: toxpk.h:26
Friend
Definition: friend.h:31
CoreAV::toggleMuteCallInput
void toggleMuteCallInput(const Friend *f)
Toggles the mute state of the call's input (microphone).
Definition: coreav.cpp:429
CoreAV::audioFrameCallback
static void audioFrameCallback(ToxAV *toxAV, uint32_t friendNum, const int16_t *pcm, size_t sampleCount, uint8_t channels, uint32_t samplingRate, void *self)
Definition: coreav.cpp:842
ToxFriendCall::setState
void setState(const TOXAV_FRIEND_CALL_STATE &value)
Definition: toxcall.cpp:189
CoreAV::muteCallOutput
void muteCallOutput(const Group *g, bool mute)
Mutes or unmutes the group call's output (speaker).
Definition: coreav.cpp:620
CoreAV::CoreAVPtr
std::unique_ptr< CoreAV > CoreAVPtr
Definition: coreav.h:52
CoreAV::VIDEO_DEFAULT_BITRATE
static constexpr uint32_t VIDEO_DEFAULT_BITRATE
Picked at random by fair dice roll.
Definition: coreav.h:133
CoreAV::setAudio
void setAudio(IAudioControl &newAudio)
Set the audio backend.
Definition: coreav.cpp:142
CoreAV::sendCallVideo
void sendCallVideo(uint32_t friendNum, std::shared_ptr< VideoFrame > frame)
Definition: coreav.cpp:374
CoreAV::coreavThread
std::unique_ptr< QThread > coreavThread
Definition: coreav.h:139
ToxCall::setNullVideoBitrate
void setNullVideoBitrate(bool value)
Definition: toxcall.cpp:111
CoreAV::leaveGroupCall
void leaveGroupCall(int groupNum)
Will not leave the group, just stop the call.
Definition: coreav.cpp:571
CoreAV::isGroupCallInputMuted
bool isGroupCallInputMuted(const Group *g) const
Returns the group calls input (microphone) state.
Definition: coreav.cpp:635
ToxCall::setMuteMic
void setMuteMic(bool value)
Definition: toxcall.cpp:91
CoreAV::isCallStarted
bool isCallStarted(const Friend *f) const
Checks the call status for a Tox friend.
Definition: coreav.cpp:195
CoreAV::isCallInputMuted
bool isCallInputMuted(const Friend *f) const
Returns the calls input (microphone) mute state.
Definition: coreav.cpp:671
Group
Definition: group.h:34
CoreAV::makeCoreAV
static CoreAVPtr makeCoreAV(Tox *core, CompatibleRecursiveMutex &toxCoreLock, IAudioSettings &audioSettings, IGroupSettings &groupSettings)
Factory method for CoreAV.
Definition: coreav.cpp:113
CoreAV::toxav
std::unique_ptr< ToxAV, ToxAVDeleter > toxav
Definition: coreav.h:138
CoreAV::muteCallInput
void muteCallInput(const Group *g, bool mute)
Mutes or unmutes the group call's input (microphone).
Definition: coreav.cpp:605
CoreAV::isGroupCallOutputMuted
bool isGroupCallOutputMuted(const Group *g) const
Returns the group calls output (speaker) state.
Definition: coreav.cpp:653
ToxYUVFrame::y
const uint8_t * y
Definition: videoframe.h:50
CoreVideoSource::restartSource
void restartSource()
Definition: corevideosource.cpp:151
CoreAV::videoBitrateCallback
static void videoBitrateCallback(ToxAV *toxAV, uint32_t friendNum, uint32_t rate, void *self)
Definition: coreav.cpp:833
CoreAV::joinGroupCall
void joinGroupCall(const Group &group)
Starts a call in an existing AV groupchat.
Definition: coreav.cpp:545
CoreAV::audioSettings
IAudioSettings & audioSettings
Definition: coreav.h:166
corevideosource.h
core.h
CoreAV::stateCallback
static void stateCallback(ToxAV *, uint32_t friendNum, uint32_t state, void *self)
Definition: coreav.cpp:752
CoreVideoSource
A VideoSource that emits frames received by Core.
Definition: corevideosource.h:28
CoreAV::sendGroupCallAudio
bool sendGroupCallAudio(int groupNum, const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate) const
Definition: coreav.cpp:580
ContactId::toString
QString toString() const
Converts the ContactId to a uppercase hex string.
Definition: contactid.cpp:78
ToxYUVFrame
A simple structure to represent a ToxYUV video frame (corresponds to a frame encoded under format: AV...
Definition: videoframe.h:41
CoreAV::toggleMuteCallOutput
void toggleMuteCallOutput(const Friend *f)
Toggles the mute state of the call's output (speaker).
Definition: coreav.cpp:444
CoreVideoSource::stopSource
void stopSource()
Stopping the source.
Definition: corevideosource.cpp:144
ToxCall::getMuteVol
bool getMuteVol() const
Definition: toxcall.cpp:76
CoreAV::groupCalls
std::map< int, ToxGroupCallPtr > groupCalls
Maps group IDs to ToxGroupCalls.
Definition: coreav.h:154
CoreAV::audioBitrateCallback
static void audioBitrateCallback(ToxAV *toxAV, uint32_t friendNum, uint32_t rate, void *self)
Definition: coreav.cpp:823
Friend::getId
uint32_t getId() const override
Definition: friend.cpp:136
ToxCall::audio
IAudioControl & audio
Definition: toxcall.h:78
CoreAV::callsLock
QReadWriteLock callsLock
Definition: coreav.h:157
ToxCall::getNullVideoBitrate
bool getNullVideoBitrate() const
Definition: toxcall.cpp:106
CoreAV::timeoutCall
void timeoutCall(uint32_t friendNum)
Definition: coreav.cpp:317
CoreAV::videoFrameCallback
static void videoFrameCallback(ToxAV *toxAV, uint32_t friendNum, uint16_t w, uint16_t h, const uint8_t *y, const uint8_t *u, const uint8_t *v, int32_t ystride, int32_t ustride, int32_t vstride, void *self)
Definition: coreav.cpp:864
CoreAV
Definition: coreav.h:47
ToxCall::isActive
bool isActive() const
Definition: toxcall.cpp:66
CoreAV::getAudio
IAudioControl * getAudio()
Get the audio backend used.
Definition: coreav.cpp:153
CoreAV::avEnd
void avEnd(uint32_t friendId, bool error=false)
Sent when a call was ended by the peer.
Core
Definition: core.h:59
igroupsettings.h
ToxCall::getVideoSource
CoreVideoSource * getVideoSource() const
Definition: toxcall.cpp:116