2014-09-02 02:45:48 +08:00
|
|
|
/*
|
|
|
|
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
|
2015-06-06 09:40:08 +08:00
|
|
|
Copyright © 2014-2015 by The qTox Project
|
2014-09-02 02:45:48 +08:00
|
|
|
|
2015-06-06 09:40:08 +08:00
|
|
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
2014-09-02 02:45:48 +08:00
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
2015-06-06 09:40:08 +08:00
|
|
|
|
|
|
|
qTox is distributed in the hope that it will be useful,
|
2014-09-02 02:45:48 +08:00
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2015-06-06 09:40:08 +08:00
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
2014-09-02 02:45:48 +08:00
|
|
|
|
2015-06-06 09:40:08 +08:00
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
2014-09-02 02:45:48 +08:00
|
|
|
*/
|
|
|
|
|
2014-08-28 20:30:38 +08:00
|
|
|
#include "core.h"
|
2015-06-26 19:00:16 +08:00
|
|
|
#include "coreav.h"
|
2015-05-14 10:46:28 +08:00
|
|
|
#include "src/video/camerasource.h"
|
|
|
|
#include "src/video/corevideosource.h"
|
|
|
|
#include "src/video/videoframe.h"
|
2015-06-06 07:44:47 +08:00
|
|
|
#include "src/audio/audio.h"
|
2014-12-14 16:50:18 +08:00
|
|
|
#ifdef QTOX_FILTER_AUDIO
|
2015-06-06 07:44:47 +08:00
|
|
|
#include "src/audio/audiofilterer.h"
|
2014-12-14 16:50:18 +08:00
|
|
|
#endif
|
2015-06-06 07:44:47 +08:00
|
|
|
#include "src/persistence/settings.h"
|
2015-06-06 00:53:27 +08:00
|
|
|
#include <assert.h>
|
2014-09-11 21:44:34 +08:00
|
|
|
#include <QDebug>
|
|
|
|
#include <QTimer>
|
2014-08-28 20:30:38 +08:00
|
|
|
|
2015-09-28 03:34:14 +08:00
|
|
|
IndexedList<ToxCall> CoreAV::calls;
|
2015-06-26 19:00:16 +08:00
|
|
|
QHash<int, ToxGroupCall> CoreAV::groupCalls;
|
2014-08-28 20:30:38 +08:00
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
ToxCall::ToxCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av)
|
|
|
|
: sendAudioTimer{new QTimer}, friendNum{FriendNum},
|
2015-09-28 05:16:40 +08:00
|
|
|
ringing{true}, muteMic{false}, muteVol{false},
|
2015-09-28 01:59:26 +08:00
|
|
|
videoEnabled{VideoEnabled},
|
|
|
|
alSource{0}, videoSource{nullptr},
|
|
|
|
state{static_cast<TOXAV_FRIEND_CALL_STATE>(0)}
|
2015-06-26 19:00:16 +08:00
|
|
|
{
|
2015-10-20 09:46:44 +08:00
|
|
|
Audio::getInstance().subscribeInput();
|
2015-09-28 01:59:26 +08:00
|
|
|
sendAudioTimer->setInterval(5);
|
|
|
|
sendAudioTimer->setSingleShot(true);
|
2015-09-28 05:16:40 +08:00
|
|
|
QObject::connect(sendAudioTimer, &QTimer::timeout, [FriendNum,&av](){av.sendCallAudio(FriendNum);});
|
2015-09-28 01:59:26 +08:00
|
|
|
sendAudioTimer->start();
|
2014-08-28 23:13:26 +08:00
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
if (videoEnabled)
|
2014-08-28 20:30:38 +08:00
|
|
|
{
|
2015-09-28 01:59:26 +08:00
|
|
|
videoSource = new CoreVideoSource;
|
2015-06-26 23:38:53 +08:00
|
|
|
CameraSource& source = CameraSource::getInstance();
|
2015-09-28 07:04:39 +08:00
|
|
|
if (!source.isOpen())
|
|
|
|
source.open();
|
2015-06-26 23:38:53 +08:00
|
|
|
source.subscribe();
|
2015-09-28 01:59:26 +08:00
|
|
|
QObject::connect(&source, &VideoSource::frameAvailable,
|
2015-09-28 07:04:39 +08:00
|
|
|
[FriendNum,&av](std::shared_ptr<VideoFrame> frame){av.sendCallVideo(FriendNum,frame);});
|
2014-08-28 20:30:38 +08:00
|
|
|
}
|
2014-12-14 16:50:18 +08:00
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
|
2014-12-14 16:50:18 +08:00
|
|
|
#ifdef QTOX_FILTER_AUDIO
|
|
|
|
if (Settings::getInstance().getFilterAudio())
|
|
|
|
{
|
2015-09-28 01:59:26 +08:00
|
|
|
filterer = new AudioFilterer();
|
2015-09-28 07:04:39 +08:00
|
|
|
filterer->startFilter(AUDIO_SAMPLE_RATE);
|
2014-12-14 16:50:18 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-09-28 01:59:26 +08:00
|
|
|
filterer = nullptr;
|
2014-12-14 16:50:18 +08:00
|
|
|
}
|
|
|
|
#endif
|
2014-08-28 20:30:38 +08:00
|
|
|
}
|
|
|
|
|
2015-09-28 03:34:14 +08:00
|
|
|
ToxCall::ToxCall(ToxCall&& other)
|
|
|
|
: sendAudioTimer{other.sendAudioTimer}, friendNum{other.friendNum},
|
2015-09-28 05:16:40 +08:00
|
|
|
ringing{other.ringing}, muteMic{other.muteMic}, muteVol{other.muteVol},
|
2015-09-28 03:34:14 +08:00
|
|
|
videoEnabled{other.videoEnabled},
|
|
|
|
alSource{other.alSource}, videoSource{other.videoSource},
|
|
|
|
state{other.state}
|
|
|
|
{
|
|
|
|
other.sendAudioTimer = nullptr;
|
|
|
|
other.friendNum = std::numeric_limits<decltype(friendNum)>::max();
|
2015-09-28 07:04:39 +08:00
|
|
|
other.videoEnabled = false;
|
2015-09-28 03:34:14 +08:00
|
|
|
other.alSource = 0;
|
|
|
|
other.videoSource = nullptr;
|
|
|
|
|
|
|
|
#ifdef QTOX_FILTER_AUDIO
|
|
|
|
filterer = other.filterer;
|
|
|
|
other.filterer = nullptr;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
ToxCall::~ToxCall()
|
2014-08-28 21:46:11 +08:00
|
|
|
{
|
2015-09-28 03:34:14 +08:00
|
|
|
if (sendAudioTimer)
|
|
|
|
{
|
|
|
|
QObject::disconnect(sendAudioTimer, nullptr, nullptr, nullptr);
|
|
|
|
sendAudioTimer->stop();
|
|
|
|
Audio::getInstance().unsubscribeInput();
|
|
|
|
}
|
2015-09-28 01:59:26 +08:00
|
|
|
if (videoEnabled)
|
|
|
|
{
|
|
|
|
CameraSource::getInstance().unsubscribe();
|
|
|
|
if (videoSource)
|
|
|
|
{
|
|
|
|
videoSource->setDeleteOnClose(true);
|
|
|
|
videoSource = nullptr;
|
|
|
|
}
|
|
|
|
}
|
2015-09-28 03:34:14 +08:00
|
|
|
}
|
2014-08-28 21:46:11 +08:00
|
|
|
|
2015-09-28 03:34:14 +08:00
|
|
|
const ToxCall& ToxCall::operator=(ToxCall&& other)
|
|
|
|
{
|
|
|
|
sendAudioTimer = other.sendAudioTimer;
|
|
|
|
other.sendAudioTimer = nullptr;
|
|
|
|
friendNum = other.friendNum;
|
|
|
|
other.friendNum = std::numeric_limits<decltype(friendNum)>::max();
|
2015-09-28 05:16:40 +08:00
|
|
|
ringing = other.ringing;
|
2015-09-28 03:34:14 +08:00
|
|
|
muteMic = other.muteMic;
|
|
|
|
muteVol = other.muteVol;
|
|
|
|
videoEnabled = other.videoEnabled;
|
2015-09-28 07:04:39 +08:00
|
|
|
other.videoEnabled = false;
|
2015-09-28 03:34:14 +08:00
|
|
|
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;
|
2014-08-28 21:46:11 +08:00
|
|
|
}
|
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
CoreAV::CoreAV(Tox *tox)
|
2014-08-28 21:46:11 +08:00
|
|
|
{
|
2015-09-28 01:59:26 +08:00
|
|
|
toxav = toxav_new(tox, nullptr);
|
|
|
|
|
|
|
|
toxav_callback_call(toxav, CoreAV::callCallback, this);
|
|
|
|
toxav_callback_call_state(toxav, CoreAV::stateCallback, this);
|
|
|
|
toxav_callback_audio_bit_rate_status(toxav, CoreAV::audioBitrateCallback, this);
|
|
|
|
toxav_callback_video_bit_rate_status(toxav, CoreAV::videoBitrateCallback, this);
|
|
|
|
toxav_callback_audio_receive_frame(toxav, CoreAV::audioFrameCallback, this);
|
|
|
|
toxav_callback_video_receive_frame(toxav, CoreAV::videoFrameCallback, this);
|
2014-08-28 21:46:11 +08:00
|
|
|
}
|
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
CoreAV::~CoreAV()
|
2015-01-25 11:57:20 +08:00
|
|
|
{
|
2015-09-28 03:34:14 +08:00
|
|
|
for (const ToxCall& call : calls)
|
2015-09-28 01:59:26 +08:00
|
|
|
cancelCall(call.friendNum);
|
|
|
|
toxav_kill(toxav);
|
2015-01-25 11:57:20 +08:00
|
|
|
}
|
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
const ToxAV *CoreAV::getToxAv() const
|
2014-08-28 21:46:11 +08:00
|
|
|
{
|
2015-09-28 01:59:26 +08:00
|
|
|
return toxav;
|
|
|
|
}
|
2015-06-26 19:00:16 +08:00
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
void CoreAV::process()
|
|
|
|
{
|
|
|
|
toxav_iterate(toxav);
|
2014-08-28 21:46:11 +08:00
|
|
|
}
|
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
bool CoreAV::anyActiveCalls()
|
2014-08-28 21:46:11 +08:00
|
|
|
{
|
2015-09-28 01:59:26 +08:00
|
|
|
return !calls.isEmpty();
|
2014-08-28 21:46:11 +08:00
|
|
|
}
|
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
void CoreAV::answerCall(uint32_t friendNum)
|
2014-08-28 20:30:38 +08:00
|
|
|
{
|
2015-09-28 01:59:26 +08:00
|
|
|
qDebug() << QString("answering call %1").arg(friendNum);
|
|
|
|
assert(calls.contains(friendNum));
|
|
|
|
TOXAV_ERR_ANSWER err;
|
|
|
|
if (toxav_answer(toxav, friendNum, AUDIO_DEFAULT_BITRATE, VIDEO_DEFAULT_BITRATE, &err))
|
|
|
|
{
|
2015-09-28 05:16:40 +08:00
|
|
|
calls[friendNum].ringing = false;
|
2015-09-28 01:59:26 +08:00
|
|
|
emit avStart(friendNum, calls[friendNum].videoEnabled);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
qWarning() << "Failed to answer call with error"<<err;
|
|
|
|
calls.remove(friendNum);
|
|
|
|
emit avCallFailed(friendNum);
|
|
|
|
}
|
|
|
|
}
|
2015-10-11 14:38:44 +08:00
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
void CoreAV::startCall(uint32_t friendId, bool video)
|
|
|
|
{
|
|
|
|
assert(!calls.contains(friendId));
|
|
|
|
uint32_t videoBitrate = video ? VIDEO_DEFAULT_BITRATE : 0;
|
|
|
|
if (!toxav_call(toxav, friendId, AUDIO_DEFAULT_BITRATE, videoBitrate, nullptr))
|
2015-05-14 10:46:28 +08:00
|
|
|
{
|
2015-09-28 01:59:26 +08:00
|
|
|
emit avCallFailed(friendId);
|
|
|
|
return;
|
2015-05-14 10:46:28 +08:00
|
|
|
}
|
2015-04-24 19:01:50 +08:00
|
|
|
|
2015-09-28 03:34:14 +08:00
|
|
|
calls.insert({friendId, video, *this});
|
2015-09-28 01:59:26 +08:00
|
|
|
emit avRinging(friendId, video);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CoreAV::cancelCall(uint32_t friendId)
|
|
|
|
{
|
|
|
|
qDebug() << QString("Cancelling call with %1").arg(friendId);
|
|
|
|
toxav_call_control(toxav, friendId, TOXAV_CALL_CONTROL_CANCEL, nullptr);
|
|
|
|
calls.remove(friendId);
|
2014-08-28 20:30:38 +08:00
|
|
|
}
|
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
void CoreAV::sendCallAudio(uint32_t callId)
|
2014-08-28 20:30:38 +08:00
|
|
|
{
|
2015-09-28 01:59:26 +08:00
|
|
|
if (!calls.contains(callId))
|
2014-08-28 20:30:38 +08:00
|
|
|
return;
|
2014-08-28 23:13:26 +08:00
|
|
|
|
2015-09-28 05:16:40 +08:00
|
|
|
ToxCall& call = calls[callId];
|
|
|
|
|
|
|
|
if (call.muteMic || call.ringing
|
|
|
|
|| !(call.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_A)
|
|
|
|
|| !Audio::getInstance().isInputReady())
|
2014-08-28 23:46:45 +08:00
|
|
|
{
|
2015-09-28 05:16:40 +08:00
|
|
|
call.sendAudioTimer->start();
|
2014-08-28 23:46:45 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-09-28 05:16:40 +08:00
|
|
|
int16_t buf[AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS] = {0};
|
|
|
|
if (Audio::getInstance().tryCaptureSamples(buf, AUDIO_FRAME_SAMPLE_COUNT))
|
2014-08-28 23:13:26 +08:00
|
|
|
{
|
2015-02-18 13:20:31 +08:00
|
|
|
#ifdef QTOX_FILTER_AUDIO
|
2015-03-12 10:13:18 +08:00
|
|
|
if (Settings::getInstance().getFilterAudio())
|
2015-02-18 14:25:25 +08:00
|
|
|
{
|
2015-09-28 05:16:40 +08:00
|
|
|
if (!call.filterer)
|
2015-03-12 10:13:18 +08:00
|
|
|
{
|
2015-09-28 05:16:40 +08:00
|
|
|
call.filterer = new AudioFilterer();
|
|
|
|
call.filterer->startFilter(AUDIO_SAMPLE_RATE);
|
2015-03-12 10:13:18 +08:00
|
|
|
}
|
2015-02-18 14:25:25 +08:00
|
|
|
// is a null op #ifndef ALC_LOOPBACK_CAPTURE_SAMPLES
|
2015-09-28 05:16:40 +08:00
|
|
|
Audio::getEchoesToFilter(call.filterer, AUDIO_FRAME_SAMPLE_COUNT);
|
2015-02-18 14:25:25 +08:00
|
|
|
|
2015-09-28 05:16:40 +08:00
|
|
|
call.filterer->filterAudio(buf, AUDIO_FRAME_SAMPLE_COUNT);
|
2015-02-18 14:25:25 +08:00
|
|
|
}
|
2015-09-28 05:16:40 +08:00
|
|
|
else if (call.filterer)
|
2015-03-12 10:13:18 +08:00
|
|
|
{
|
2015-09-28 05:16:40 +08:00
|
|
|
delete call.filterer;
|
|
|
|
call.filterer = nullptr;
|
2015-03-12 10:13:18 +08:00
|
|
|
}
|
2015-02-18 13:20:31 +08:00
|
|
|
#endif
|
|
|
|
|
2015-09-28 05:16:40 +08:00
|
|
|
if (!toxav_audio_send_frame(toxav, callId, buf, AUDIO_FRAME_SAMPLE_COUNT,
|
|
|
|
AUDIO_CHANNELS, AUDIO_SAMPLE_RATE, nullptr))
|
|
|
|
qDebug() << "toxav_audio_send_frame error";
|
2014-08-28 23:13:26 +08:00
|
|
|
}
|
2015-09-28 05:16:40 +08:00
|
|
|
|
|
|
|
call.sendAudioTimer->start();
|
2014-08-28 20:30:38 +08:00
|
|
|
}
|
|
|
|
|
2015-06-26 19:00:16 +08:00
|
|
|
void CoreAV::playCallVideo(void*, int32_t callId, const vpx_image *img, void *user_data)
|
2014-08-28 20:30:38 +08:00
|
|
|
{
|
|
|
|
Q_UNUSED(user_data);
|
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
if (!calls.contains(callId) || !calls[callId].videoEnabled)
|
2014-08-28 20:30:38 +08:00
|
|
|
return;
|
|
|
|
|
2015-05-14 10:46:28 +08:00
|
|
|
calls[callId].videoSource->pushFrame(img);
|
2014-08-28 20:30:38 +08:00
|
|
|
}
|
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
void CoreAV::sendCallVideo(uint32_t callId, std::shared_ptr<VideoFrame> vframe)
|
2014-08-28 20:30:38 +08:00
|
|
|
{
|
2015-09-28 07:04:39 +08:00
|
|
|
if (!calls.contains(callId))
|
|
|
|
return;
|
|
|
|
|
|
|
|
ToxCall& call = calls[callId];
|
|
|
|
|
|
|
|
if (!call.videoEnabled || call.ringing
|
|
|
|
|| !(call.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_V))
|
2014-08-28 20:30:38 +08:00
|
|
|
return;
|
|
|
|
|
2015-05-14 10:46:28 +08:00
|
|
|
// This frame shares vframe's buffers, we don't call vpx_img_free but just delete it
|
|
|
|
vpx_image* frame = vframe->toVpxImage();
|
|
|
|
if (frame->fmt == VPX_IMG_FMT_NONE)
|
2014-08-28 20:30:38 +08:00
|
|
|
{
|
2015-05-14 10:46:28 +08:00
|
|
|
qWarning() << "Invalid frame";
|
|
|
|
delete frame;
|
|
|
|
return;
|
2014-08-28 20:30:38 +08:00
|
|
|
}
|
2015-05-14 10:46:28 +08:00
|
|
|
|
2015-09-28 07:04:39 +08:00
|
|
|
if (!toxav_video_send_frame(toxav, callId, frame->d_w, frame->d_h,
|
|
|
|
frame->planes[0], frame->planes[1], frame->planes[2], nullptr))
|
|
|
|
qDebug() << "toxav_video_send_frame error";
|
2015-05-14 10:46:28 +08:00
|
|
|
|
|
|
|
delete frame;
|
2014-08-28 20:30:38 +08:00
|
|
|
}
|
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
void CoreAV::micMuteToggle(uint32_t callId)
|
2014-08-28 20:30:38 +08:00
|
|
|
{
|
2015-09-28 01:59:26 +08:00
|
|
|
if (calls.contains(callId))
|
2014-09-22 04:33:07 +08:00
|
|
|
calls[callId].muteMic = !calls[callId].muteMic;
|
2014-08-28 20:30:38 +08:00
|
|
|
}
|
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
void CoreAV::volMuteToggle(uint32_t callId)
|
2014-10-28 20:50:30 +08:00
|
|
|
{
|
2015-09-28 01:59:26 +08:00
|
|
|
if (calls.contains(callId))
|
2014-10-28 20:50:30 +08:00
|
|
|
calls[callId].muteVol = !calls[callId].muteVol;
|
|
|
|
}
|
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
VideoSource *CoreAV::getVideoSourceFromCall(int friendNum)
|
2014-10-15 20:46:01 +08:00
|
|
|
{
|
2015-09-28 01:59:26 +08:00
|
|
|
assert(calls.contains(friendNum));
|
|
|
|
return calls[friendNum].videoSource;
|
2014-10-15 20:46:01 +08:00
|
|
|
}
|
|
|
|
|
2015-06-26 19:00:16 +08:00
|
|
|
void CoreAV::joinGroupCall(int groupId)
|
2014-11-13 19:18:04 +08:00
|
|
|
{
|
2015-05-11 20:54:03 +08:00
|
|
|
qDebug() << QString("Joining group call %1").arg(groupId);
|
2014-11-13 19:18:04 +08:00
|
|
|
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
|
2015-10-20 09:46:44 +08:00
|
|
|
Audio::getInstance().subscribeInput();
|
2014-11-13 19:18:04 +08:00
|
|
|
|
|
|
|
// Go
|
2014-11-13 20:11:23 +08:00
|
|
|
groupCalls[groupId].sendAudioTimer = new QTimer();
|
2014-11-13 19:18:04 +08:00
|
|
|
groupCalls[groupId].active = true;
|
|
|
|
groupCalls[groupId].sendAudioTimer->setInterval(5);
|
|
|
|
groupCalls[groupId].sendAudioTimer->setSingleShot(true);
|
2014-11-15 04:16:28 +08:00
|
|
|
connect(groupCalls[groupId].sendAudioTimer, &QTimer::timeout, [=](){sendGroupCallAudio(groupId,toxav);});
|
2014-11-13 19:18:04 +08:00
|
|
|
groupCalls[groupId].sendAudioTimer->start();
|
|
|
|
}
|
|
|
|
|
2015-06-26 19:00:16 +08:00
|
|
|
void CoreAV::leaveGroupCall(int groupId)
|
2014-11-13 19:18:04 +08:00
|
|
|
{
|
2015-05-11 20:54:03 +08:00
|
|
|
qDebug() << QString("Leaving group call %1").arg(groupId);
|
2014-11-13 19:18:04 +08:00
|
|
|
groupCalls[groupId].active = false;
|
|
|
|
disconnect(groupCalls[groupId].sendAudioTimer,0,0,0);
|
|
|
|
groupCalls[groupId].sendAudioTimer->stop();
|
2015-05-26 00:38:52 +08:00
|
|
|
for (ALuint source : groupCalls[groupId].alSources)
|
|
|
|
alDeleteSources(1, &source);
|
2014-11-16 23:41:30 +08:00
|
|
|
groupCalls[groupId].alSources.clear();
|
2015-10-20 09:46:44 +08:00
|
|
|
Audio::getInstance().unsubscribeInput();
|
2014-11-18 08:25:15 +08:00
|
|
|
delete groupCalls[groupId].sendAudioTimer;
|
2014-11-13 19:18:04 +08:00
|
|
|
}
|
|
|
|
|
2015-06-26 19:00:16 +08:00
|
|
|
void CoreAV::sendGroupCallAudio(int groupId, ToxAV *toxav)
|
2014-11-13 19:18:04 +08:00
|
|
|
{
|
|
|
|
if (!groupCalls[groupId].active)
|
|
|
|
return;
|
|
|
|
|
2015-10-20 09:46:44 +08:00
|
|
|
if (groupCalls[groupId].muteMic || !Audio::getInstance().isInputReady())
|
2014-11-13 19:18:04 +08:00
|
|
|
{
|
|
|
|
groupCalls[groupId].sendAudioTimer->start();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-26 19:00:16 +08:00
|
|
|
#if 0
|
2014-11-16 00:04:09 +08:00
|
|
|
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];
|
2014-11-13 19:18:04 +08:00
|
|
|
|
2015-10-20 09:46:44 +08:00
|
|
|
if (Audio::getInstance().tryCaptureSamples(buf, framesize))
|
2014-11-13 19:18:04 +08:00
|
|
|
{
|
2015-01-30 04:23:33 +08:00
|
|
|
if (toxav_group_send_audio(toxav_get_tox(toxav), groupId, (int16_t*)buf,
|
|
|
|
framesize, av_DefaultSettings.audio_channels, av_DefaultSettings.audio_sample_rate) < 0)
|
2014-11-13 19:18:04 +08:00
|
|
|
{
|
2015-05-11 20:54:03 +08:00
|
|
|
qDebug() << "toxav_group_send_audio error";
|
2014-11-13 19:18:04 +08:00
|
|
|
groupCalls[groupId].sendAudioTimer->start();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2015-06-26 19:00:16 +08:00
|
|
|
#endif
|
2014-11-13 19:18:04 +08:00
|
|
|
groupCalls[groupId].sendAudioTimer->start();
|
2014-11-11 23:05:01 +08:00
|
|
|
}
|
2014-11-13 20:11:23 +08:00
|
|
|
|
2015-06-26 19:00:16 +08:00
|
|
|
void CoreAV::disableGroupCallMic(int groupId)
|
2014-11-13 20:11:23 +08:00
|
|
|
{
|
|
|
|
groupCalls[groupId].muteMic = true;
|
|
|
|
}
|
|
|
|
|
2015-06-26 19:00:16 +08:00
|
|
|
void CoreAV::disableGroupCallVol(int groupId)
|
2014-11-13 20:11:23 +08:00
|
|
|
{
|
|
|
|
groupCalls[groupId].muteVol = true;
|
|
|
|
}
|
|
|
|
|
2015-06-26 19:00:16 +08:00
|
|
|
void CoreAV::enableGroupCallMic(int groupId)
|
2014-11-13 20:11:23 +08:00
|
|
|
{
|
|
|
|
groupCalls[groupId].muteMic = false;
|
|
|
|
}
|
|
|
|
|
2015-06-26 19:00:16 +08:00
|
|
|
void CoreAV::enableGroupCallVol(int groupId)
|
2014-11-13 20:11:23 +08:00
|
|
|
{
|
|
|
|
groupCalls[groupId].muteVol = false;
|
|
|
|
}
|
2014-11-14 02:20:06 +08:00
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
bool CoreAV::isGroupCallMicEnabled(int groupId) const
|
2014-11-14 02:20:06 +08:00
|
|
|
{
|
|
|
|
return !groupCalls[groupId].muteMic;
|
|
|
|
}
|
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
bool CoreAV::isGroupCallVolEnabled(int groupId) const
|
2014-11-14 02:20:06 +08:00
|
|
|
{
|
|
|
|
return !groupCalls[groupId].muteVol;
|
|
|
|
}
|
2015-06-26 19:00:16 +08:00
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
bool CoreAV::isGroupAvEnabled(int groupId) const
|
2015-06-26 19:00:16 +08:00
|
|
|
{
|
|
|
|
return tox_group_get_type(Core::getInstance()->tox, groupId) == TOX_GROUPCHAT_TYPE_AV;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CoreAV::resetCallSources()
|
|
|
|
{
|
|
|
|
for (ToxGroupCall& call : groupCalls)
|
|
|
|
{
|
|
|
|
for (ALuint source : call.alSources)
|
|
|
|
alDeleteSources(1, &source);
|
|
|
|
call.alSources.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ToxCall& call : calls)
|
|
|
|
{
|
2015-09-28 01:59:26 +08:00
|
|
|
if (call.alSource)
|
2015-06-26 19:00:16 +08:00
|
|
|
{
|
|
|
|
ALuint tmp = call.alSource;
|
|
|
|
call.alSource = 0;
|
|
|
|
alDeleteSources(1, &tmp);
|
|
|
|
|
|
|
|
alGenSources(1, &call.alSource);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-09-28 01:59:26 +08:00
|
|
|
|
2015-09-28 05:16:40 +08:00
|
|
|
void CoreAV::callCallback(ToxAV*, uint32_t friendNum, bool audio, bool video, void *_self)
|
2015-09-28 01:59:26 +08:00
|
|
|
{
|
|
|
|
CoreAV* self = static_cast<CoreAV*>(_self);
|
2015-09-28 05:16:40 +08:00
|
|
|
const auto& callIt = calls.insert({friendNum, video, *self});
|
|
|
|
|
|
|
|
// We don't get a state callback when answering, so fill the state ourselves in advance
|
|
|
|
int state = 0;
|
|
|
|
if (audio)
|
|
|
|
state |= TOXAV_FRIEND_CALL_STATE_SENDING_A | TOXAV_FRIEND_CALL_STATE_ACCEPTING_A;
|
|
|
|
if (video)
|
|
|
|
state |= TOXAV_FRIEND_CALL_STATE_SENDING_V | TOXAV_FRIEND_CALL_STATE_ACCEPTING_V;
|
|
|
|
callIt->state = static_cast<TOXAV_FRIEND_CALL_STATE>(state);
|
|
|
|
|
2015-09-28 01:59:26 +08:00
|
|
|
emit reinterpret_cast<CoreAV*>(self)->avInvite(friendNum, video);
|
|
|
|
}
|
|
|
|
|
2015-09-29 02:43:17 +08:00
|
|
|
void CoreAV::stateCallback(ToxAV *, uint32_t friendNum, uint32_t state, void *_self)
|
2015-09-28 01:59:26 +08:00
|
|
|
{
|
|
|
|
CoreAV* self = static_cast<CoreAV*>(_self);
|
|
|
|
|
|
|
|
assert(self->calls.contains(friendNum));
|
|
|
|
ToxCall& call = self->calls[friendNum];
|
|
|
|
|
|
|
|
if (state & TOXAV_FRIEND_CALL_STATE_ERROR)
|
|
|
|
{
|
|
|
|
qWarning() << "Call with friend"<<friendNum<<"died of unnatural causes";
|
|
|
|
calls.remove(friendNum);
|
|
|
|
emit self->avCallFailed(friendNum);
|
|
|
|
}
|
|
|
|
else if (state & TOXAV_FRIEND_CALL_STATE_FINISHED)
|
|
|
|
{
|
|
|
|
calls.remove(friendNum);
|
|
|
|
emit self->avEnd(friendNum);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-09-28 05:16:40 +08:00
|
|
|
// If our state was null, we started the call and still ringing
|
2015-09-28 01:59:26 +08:00
|
|
|
if (!call.state && state)
|
2015-09-28 05:16:40 +08:00
|
|
|
{
|
|
|
|
call.ringing = false;
|
2015-09-28 01:59:26 +08:00
|
|
|
emit self->avStart(friendNum, call.videoEnabled);
|
2015-09-28 05:16:40 +08:00
|
|
|
}
|
2015-09-28 01:59:26 +08:00
|
|
|
|
|
|
|
call.state = static_cast<TOXAV_FRIEND_CALL_STATE>(state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-29 02:43:17 +08:00
|
|
|
void CoreAV::audioBitrateCallback(ToxAV*, uint32_t friendNum, bool stable, uint32_t rate, void *)
|
2015-09-28 01:59:26 +08:00
|
|
|
{
|
2015-09-29 02:43:17 +08:00
|
|
|
qDebug() << "Audio bitrate with"<<friendNum<<" is now "<<rate<<", stability:"<<stable;
|
2015-09-28 01:59:26 +08:00
|
|
|
}
|
|
|
|
|
2015-09-29 02:43:17 +08:00
|
|
|
void CoreAV::videoBitrateCallback(ToxAV*, uint32_t friendNum, bool stable, uint32_t rate, void *)
|
2015-09-28 01:59:26 +08:00
|
|
|
{
|
2015-09-29 02:43:17 +08:00
|
|
|
qDebug() << "Video bitrate with"<<friendNum<<" is now "<<rate<<", stability:"<<stable;
|
2015-09-28 01:59:26 +08:00
|
|
|
}
|
|
|
|
|
2015-09-29 02:43:17 +08:00
|
|
|
void CoreAV::audioFrameCallback(ToxAV *, uint32_t friendNum, const int16_t *pcm,
|
2015-09-28 05:16:40 +08:00
|
|
|
size_t sampleCount, uint8_t channels, uint32_t samplingRate, void *_self)
|
2015-09-28 01:59:26 +08:00
|
|
|
{
|
2015-09-28 05:16:40 +08:00
|
|
|
CoreAV* self = static_cast<CoreAV*>(_self);
|
|
|
|
if (!self->calls.contains(friendNum))
|
|
|
|
return;
|
|
|
|
|
|
|
|
ToxCall& call = self->calls[friendNum];
|
|
|
|
|
|
|
|
if (call.muteVol)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!call.alSource)
|
|
|
|
alGenSources(1, &call.alSource);
|
|
|
|
|
|
|
|
Audio::playAudioBuffer(call.alSource, pcm, sampleCount, channels, samplingRate);
|
2015-09-28 01:59:26 +08:00
|
|
|
}
|
|
|
|
|
2015-09-29 02:43:17 +08:00
|
|
|
void CoreAV::videoFrameCallback(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 *)
|
2015-09-28 01:59:26 +08:00
|
|
|
{
|
2015-09-28 07:04:39 +08:00
|
|
|
if (!calls.contains(friendNum))
|
|
|
|
return;
|
|
|
|
|
|
|
|
vpx_image frame;
|
|
|
|
frame.d_h = h;
|
|
|
|
frame.d_w = w;
|
|
|
|
frame.planes[0] = const_cast<uint8_t*>(y);
|
|
|
|
frame.planes[1] = const_cast<uint8_t*>(u);
|
|
|
|
frame.planes[2] = const_cast<uint8_t*>(v);
|
|
|
|
frame.stride[0] = ystride;
|
|
|
|
frame.stride[1] = ustride;
|
|
|
|
frame.stride[2] = vstride;
|
|
|
|
|
|
|
|
ToxCall& call = calls[friendNum];
|
|
|
|
call.videoSource->pushFrame(&frame);
|
2015-09-28 01:59:26 +08:00
|
|
|
}
|