1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00
qTox/src/core/coreav.cpp

409 lines
12 KiB
C++
Raw Normal View History

2014-09-02 02:45:48 +08:00
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
Copyright © 2014-2015 by The qTox Project
2014-09-02 02:45:48 +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.
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
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
2014-09-02 02:45:48 +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-06-06 07:44:47 +08:00
#include "src/audio/audio.h"
#include "src/persistence/settings.h"
2015-10-05 08:36:50 +08:00
#include "src/video/videoframe.h"
#include "src/video/corevideosource.h"
2015-06-06 00:53:27 +08:00
#include <assert.h>
2014-09-11 21:44:34 +08:00
#include <QDebug>
#ifdef QTOX_FILTER_AUDIO
2015-10-05 08:36:50 +08:00
#include "src/audio/audiofilterer.h"
2015-09-28 03:34:14 +08:00
#endif
2015-10-05 08:36:50 +08:00
IndexedList<ToxFriendCall> CoreAV::calls;
IndexedList<ToxGroupCall> CoreAV::groupCalls;
2014-08-28 21:46:11 +08:00
2015-10-05 08:36:50 +08:00
using namespace std;
2014-08-28 21:46:11 +08:00
CoreAV::CoreAV(Tox *tox)
2014-08-28 21:46:11 +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
}
CoreAV::~CoreAV()
{
2015-10-05 08:36:50 +08:00
for (const ToxFriendCall& call : calls)
cancelCall(call.callId);
toxav_kill(toxav);
}
const ToxAV *CoreAV::getToxAv() const
2014-08-28 21:46:11 +08:00
{
return toxav;
}
2015-06-26 19:00:16 +08:00
void CoreAV::process()
{
toxav_iterate(toxav);
2014-08-28 21:46:11 +08:00
}
bool CoreAV::anyActiveCalls()
2014-08-28 21:46:11 +08:00
{
return !calls.isEmpty();
2014-08-28 21:46:11 +08:00
}
void CoreAV::answerCall(uint32_t friendNum)
2014-08-28 20:30:38 +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-10-05 08:36:50 +08:00
calls[friendNum].inactive = false;
emit avStart(friendNum, calls[friendNum].videoEnabled);
}
else
{
qWarning() << "Failed to answer call with error"<<err;
calls.remove(friendNum);
emit avCallFailed(friendNum);
}
}
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
{
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});
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-10-05 08:36:50 +08:00
bool CoreAV::sendCallAudio(uint32_t callId)
2014-08-28 20:30:38 +08:00
{
if (!calls.contains(callId))
2015-10-05 08:36:50 +08:00
return false;
2014-08-28 23:13:26 +08:00
2015-10-05 08:36:50 +08:00
ToxFriendCall& call = calls[callId];
2015-09-28 05:16:40 +08:00
2015-10-05 08:36:50 +08:00
if (call.muteMic || call.inactive
2015-09-28 05:16:40 +08:00
|| !(call.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_A)
|| !Audio::getInstance().isInputReady())
2014-08-28 23:46:45 +08:00
{
2015-10-05 08:36:50 +08:00
return true;
2014-08-28 23:46:45 +08:00
}
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
{
#ifdef QTOX_FILTER_AUDIO
if (Settings::getInstance().getFilterAudio())
{
2015-09-28 05:16:40 +08:00
if (!call.filterer)
{
2015-09-28 05:16:40 +08:00
call.filterer = new AudioFilterer();
call.filterer->startFilter(AUDIO_SAMPLE_RATE);
}
// 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-09-28 05:16:40 +08:00
call.filterer->filterAudio(buf, AUDIO_FRAME_SAMPLE_COUNT);
}
2015-09-28 05:16:40 +08:00
else if (call.filterer)
{
2015-09-28 05:16:40 +08:00
delete call.filterer;
call.filterer = nullptr;
}
#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
2015-10-05 08:36:50 +08:00
return true;
2014-08-28 20:30:38 +08:00
}
2015-10-05 08:36:50 +08:00
void CoreAV::sendCallVideo(uint32_t callId, 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;
2015-10-05 08:36:50 +08:00
ToxFriendCall& call = calls[callId];
2015-09-28 07:04:39 +08:00
2015-10-05 08:36:50 +08:00
if (!call.videoEnabled || call.inactive
2015-09-28 07:04:39 +08:00
|| !(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
}
void CoreAV::micMuteToggle(uint32_t callId)
2014-08-28 20:30:38 +08:00
{
if (calls.contains(callId))
calls[callId].muteMic = !calls[callId].muteMic;
2014-08-28 20:30:38 +08:00
}
void CoreAV::volMuteToggle(uint32_t callId)
2014-10-28 20:50:30 +08:00
{
if (calls.contains(callId))
2014-10-28 20:50:30 +08:00
calls[callId].muteVol = !calls[callId].muteVol;
}
VideoSource *CoreAV::getVideoSourceFromCall(int friendNum)
2014-10-15 20:46:01 +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
{
qDebug() << QString("Joining group call %1").arg(groupId);
2015-10-05 08:36:50 +08:00
auto call = groupCalls.insert({groupId, *this});
call->inactive = false;
2014-11-13 19:18:04 +08:00
}
2015-06-26 19:00:16 +08:00
void CoreAV::leaveGroupCall(int groupId)
2014-11-13 19:18:04 +08:00
{
qDebug() << QString("Leaving group call %1").arg(groupId);
2015-10-05 08:36:50 +08:00
groupCalls.remove(groupId);
2014-11-13 19:18:04 +08:00
}
2015-10-05 08:36:50 +08:00
bool CoreAV::sendGroupCallAudio(int groupId)
2014-11-13 19:18:04 +08:00
{
2015-10-05 08:36:50 +08:00
if (!groupCalls.contains(groupId))
return false;
2014-11-13 19:18:04 +08:00
2015-10-05 08:36:50 +08:00
ToxGroupCall& call = groupCalls[groupId];
2014-11-13 19:18:04 +08:00
2015-10-05 08:36:50 +08:00
if (call.inactive || call.muteMic || !Audio::getInstance().isInputReady())
return true;
2014-11-13 19:18:04 +08:00
2015-10-05 08:36:50 +08:00
int16_t buf[AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS] = {0};
if (Audio::getInstance().tryCaptureSamples(buf, AUDIO_FRAME_SAMPLE_COUNT))
2014-11-13 19:18:04 +08:00
{
2015-10-05 08:36:50 +08:00
#ifdef QTOX_FILTER_AUDIO
if (Settings::getInstance().getFilterAudio())
2014-11-13 19:18:04 +08:00
{
2015-10-05 08:36:50 +08:00
if (!call.filterer)
{
call.filterer = new AudioFilterer();
call.filterer->startFilter(AUDIO_SAMPLE_RATE);
}
// is a null op #ifndef ALC_LOOPBACK_CAPTURE_SAMPLES
Audio::getEchoesToFilter(call.filterer, AUDIO_FRAME_SAMPLE_COUNT);
call.filterer->filterAudio(buf, AUDIO_FRAME_SAMPLE_COUNT);
}
else if (call.filterer)
{
delete call.filterer;
call.filterer = nullptr;
2014-11-13 19:18:04 +08:00
}
2015-06-26 19:00:16 +08:00
#endif
2015-10-05 08:36:50 +08:00
if (toxav_group_send_audio(toxav_get_tox(toxav), groupId, buf, AUDIO_FRAME_SAMPLE_COUNT,
AUDIO_CHANNELS, AUDIO_SAMPLE_RATE) != 0)
qDebug() << "toxav_group_send_audio error";
}
return true;
}
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;
}
bool CoreAV::isGroupCallMicEnabled(int groupId) const
{
return !groupCalls[groupId].muteMic;
}
bool CoreAV::isGroupCallVolEnabled(int groupId) const
{
return !groupCalls[groupId].muteVol;
}
2015-06-26 19:00:16 +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)
{
2015-10-05 08:36:50 +08:00
if (call.alSource)
{
Audio::deleteSource(&call.alSource);
Audio::createSource(&call.alSource);
}
2015-06-26 19:00:16 +08:00
}
2015-10-05 08:36:50 +08:00
for (ToxFriendCall& call : calls)
2015-06-26 19:00:16 +08:00
{
if (call.alSource)
2015-06-26 19:00:16 +08:00
{
2015-10-05 08:36:50 +08:00
Audio::deleteSource(&call.alSource);
Audio::createSource(&call.alSource);
2015-06-26 19:00:16 +08:00
}
}
}
2015-09-28 05:16:40 +08:00
void CoreAV::callCallback(ToxAV*, uint32_t friendNum, bool audio, bool video, void *_self)
{
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);
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)
{
CoreAV* self = static_cast<CoreAV*>(_self);
assert(self->calls.contains(friendNum));
2015-10-05 08:36:50 +08:00
ToxFriendCall& 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-10-05 08:36:50 +08:00
// If our state was null, we started the call and were still ringing
if (!call.state && state)
2015-09-28 05:16:40 +08:00
{
2015-10-05 08:36:50 +08:00
call.inactive = false;
emit self->avStart(friendNum, call.videoEnabled);
2015-09-28 05:16:40 +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-29 02:43:17 +08:00
qDebug() << "Audio bitrate with"<<friendNum<<" is now "<<rate<<", stability:"<<stable;
}
2015-09-29 02:43:17 +08:00
void CoreAV::videoBitrateCallback(ToxAV*, uint32_t friendNum, bool stable, uint32_t rate, void *)
{
2015-09-29 02:43:17 +08:00
qDebug() << "Video bitrate with"<<friendNum<<" is now "<<rate<<", stability:"<<stable;
}
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 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-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 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;
2015-10-05 08:36:50 +08:00
ToxFriendCall& call = calls[friendNum];
2015-09-28 07:04:39 +08:00
call.videoSource->pushFrame(&frame);
}