mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Audio calls, both ways
This commit is contained in:
parent
45eb104801
commit
6b744ca15e
84
core.cpp
84
core.cpp
@ -622,7 +622,7 @@ void Core::process()
|
|||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
#endif
|
#endif
|
||||||
checkConnection();
|
checkConnection();
|
||||||
int toxInterval = tox_do_interval(tox);
|
//int toxInterval = tox_do_interval(tox);
|
||||||
//qDebug() << QString("Tox interval %1").arg(toxInterval);
|
//qDebug() << QString("Tox interval %1").arg(toxInterval);
|
||||||
toxTimer->start(50);
|
toxTimer->start(50);
|
||||||
}
|
}
|
||||||
@ -995,7 +995,15 @@ void Core::onAvRequestTimeout(int32_t call_index, void* core)
|
|||||||
|
|
||||||
void Core::onAvPeerTimeout(int32_t call_index, void* core)
|
void Core::onAvPeerTimeout(int32_t call_index, void* core)
|
||||||
{
|
{
|
||||||
qDebug() << "Core: AV peer timeout";
|
int friendId = toxav_get_peer_id(static_cast<Core*>(core)->toxav, call_index, 0);
|
||||||
|
if (friendId < 0)
|
||||||
|
{
|
||||||
|
qWarning() << "Core: Received invalid AV peer timeout";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qDebug() << QString("Core: AV peer timeout with %1").arg(friendId);
|
||||||
|
|
||||||
|
emit static_cast<Core*>(core)->avPeerTimeout(friendId, call_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::answerCall(int callId)
|
void Core::answerCall(int callId)
|
||||||
@ -1007,6 +1015,7 @@ void Core::answerCall(int callId)
|
|||||||
void Core::hangupCall(int callId)
|
void Core::hangupCall(int callId)
|
||||||
{
|
{
|
||||||
qDebug() << QString("Core: hanging up call %1").arg(callId);
|
qDebug() << QString("Core: hanging up call %1").arg(callId);
|
||||||
|
calls[callId].active = false;
|
||||||
toxav_hangup(toxav, callId);
|
toxav_hangup(toxav, callId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1020,6 +1029,7 @@ void Core::startCall(int friendId)
|
|||||||
void Core::cancelCall(int callId, int friendId)
|
void Core::cancelCall(int callId, int friendId)
|
||||||
{
|
{
|
||||||
qDebug() << QString("Core: Cancelling call with %1").arg(friendId);
|
qDebug() << QString("Core: Cancelling call with %1").arg(friendId);
|
||||||
|
calls[callId].active = false;
|
||||||
toxav_cancel(toxav, callId, friendId, 0);
|
toxav_cancel(toxav, callId, friendId, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1030,6 +1040,8 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav)
|
|||||||
calls[callId].friendId = friendId;
|
calls[callId].friendId = friendId;
|
||||||
calls[callId].codecSettings = av_DefaultSettings;
|
calls[callId].codecSettings = av_DefaultSettings;
|
||||||
toxav_prepare_transmission(toxav, callId, &calls[callId].codecSettings, false);
|
toxav_prepare_transmission(toxav, callId, &calls[callId].codecSettings, false);
|
||||||
|
|
||||||
|
// Prepare output
|
||||||
QAudioFormat format;
|
QAudioFormat format;
|
||||||
format.setSampleRate(calls[callId].codecSettings.audio_sample_rate);
|
format.setSampleRate(calls[callId].codecSettings.audio_sample_rate);
|
||||||
format.setChannelCount(calls[callId].codecSettings.audio_channels);
|
format.setChannelCount(calls[callId].codecSettings.audio_channels);
|
||||||
@ -1037,26 +1049,35 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav)
|
|||||||
format.setCodec("audio/pcm");
|
format.setCodec("audio/pcm");
|
||||||
format.setByteOrder(QAudioFormat::LittleEndian);
|
format.setByteOrder(QAudioFormat::LittleEndian);
|
||||||
format.setSampleType(QAudioFormat::SignedInt);
|
format.setSampleType(QAudioFormat::SignedInt);
|
||||||
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
|
if (!QAudioDeviceInfo::defaultOutputDevice().isFormatSupported(format))
|
||||||
if (!info.isFormatSupported(format)) {
|
|
||||||
calls[callId].audioOutput = nullptr;
|
|
||||||
qWarning() << "Core: Raw audio format not supported by backend, cannot play audio.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
calls[callId].audioOutput = new QAudioOutput(format);
|
|
||||||
calls[callId].active = true;
|
|
||||||
|
|
||||||
calls[callId].audioOutput->setBufferSize(24000);
|
|
||||||
calls[callId].audioOutput->start(&calls[callId].audioBuffer);
|
|
||||||
if (calls[callId].audioOutput->state() == QAudio::StoppedState
|
|
||||||
&& calls[callId].audioOutput->error() == QAudio::OpenError)
|
|
||||||
{
|
{
|
||||||
qWarning() << "Core: Unable to start audio";
|
calls[callId].audioOutput = nullptr;
|
||||||
|
qWarning() << "Core: Raw audio format not supported by output backend, cannot play audio.";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
qDebug() << QString("Core: Audio started, buffer size %1").arg(calls[callId].audioOutput->bufferSize());
|
{
|
||||||
calls[callId].playFuture = QtConcurrent::run(playCallAudio, callId, toxav);
|
calls[callId].audioOutput = new QAudioOutput(format);
|
||||||
|
calls[callId].audioOutput->start(&calls[callId].audioBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start input
|
||||||
|
if (!QAudioDeviceInfo::defaultInputDevice().isFormatSupported(format))
|
||||||
|
{
|
||||||
|
calls[callId].audioInput = nullptr;
|
||||||
|
qWarning() << "Default input format not supported, cannot record audio";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
calls[callId].audioInput = new QAudioInput(format);
|
||||||
|
calls[callId].audioInputDevice = calls[callId].audioInput->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go
|
||||||
|
if (calls[callId].audioOutput != nullptr)
|
||||||
|
calls[callId].playFuture = QtConcurrent::run(playCallAudio, callId, toxav);
|
||||||
|
if (calls[callId].audioInput != nullptr)
|
||||||
|
calls[callId].recordFuture = QtConcurrent::run(sendCallAudio, callId, toxav);
|
||||||
|
calls[callId].active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::cleanupCall(int callId)
|
void Core::cleanupCall(int callId)
|
||||||
@ -1064,6 +1085,7 @@ void Core::cleanupCall(int callId)
|
|||||||
qDebug() << QString("Core: cleaning up call %1").arg(callId);
|
qDebug() << QString("Core: cleaning up call %1").arg(callId);
|
||||||
calls[callId].active = false;
|
calls[callId].active = false;
|
||||||
calls[callId].playFuture.waitForFinished();
|
calls[callId].playFuture.waitForFinished();
|
||||||
|
calls[callId].recordFuture.waitForFinished();
|
||||||
if (calls[callId].audioOutput != nullptr)
|
if (calls[callId].audioOutput != nullptr)
|
||||||
{
|
{
|
||||||
delete calls[callId].audioOutput;
|
delete calls[callId].audioOutput;
|
||||||
@ -1115,6 +1137,28 @@ void Core::sendCallAudio(int callId, ToxAv* toxav)
|
|||||||
{
|
{
|
||||||
while (calls[callId].active)
|
while (calls[callId].active)
|
||||||
{
|
{
|
||||||
QThread::msleep(calls[callId].codecSettings.audio_frame_duration / 2);
|
int framesize = (calls[callId].codecSettings.audio_frame_duration * calls[callId].codecSettings.audio_sample_rate) / 1000;
|
||||||
|
uint8_t buf[framesize*2], dest[framesize*2];
|
||||||
|
int bytesReady = calls[callId].audioInput->bytesReady();
|
||||||
|
if (bytesReady >= framesize*2)
|
||||||
|
{
|
||||||
|
calls[callId].audioInputDevice->read((char*)buf, framesize*2);
|
||||||
|
int result = toxav_prepare_audio_frame(toxav, callId, dest, framesize*2, (int16_t*)buf, framesize);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
qWarning() << QString("Core: Unable to prepare audio frame, error %1").arg(result);
|
||||||
|
QThread::msleep(5);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result = toxav_send_audio(toxav, callId, dest, result);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
qWarning() << QString("Core: Unable to send audio frame, error %1").arg(result);
|
||||||
|
QThread::msleep(5);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
QThread::msleep(5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
5
core.h
5
core.h
@ -33,6 +33,7 @@
|
|||||||
#include <QFuture>
|
#include <QFuture>
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
#include <QAudioOutput>
|
#include <QAudioOutput>
|
||||||
|
#include <QAudioInput>
|
||||||
|
|
||||||
#define TOXAV_MAX_CALLS 16
|
#define TOXAV_MAX_CALLS 16
|
||||||
#define GROUPCHAT_MAX_SIZE 32
|
#define GROUPCHAT_MAX_SIZE 32
|
||||||
@ -85,11 +86,14 @@ struct ToxCall
|
|||||||
public:
|
public:
|
||||||
AudioBuffer audioBuffer;
|
AudioBuffer audioBuffer;
|
||||||
QAudioOutput* audioOutput;
|
QAudioOutput* audioOutput;
|
||||||
|
QAudioInput* audioInput;
|
||||||
|
QIODevice* audioInputDevice;
|
||||||
ToxAvCodecSettings codecSettings;
|
ToxAvCodecSettings codecSettings;
|
||||||
int callId;
|
int callId;
|
||||||
int friendId;
|
int friendId;
|
||||||
bool active;
|
bool active;
|
||||||
QFuture<void> playFuture;
|
QFuture<void> playFuture;
|
||||||
|
QFuture<void> recordFuture;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Core : public QObject
|
class Core : public QObject
|
||||||
@ -199,6 +203,7 @@ signals:
|
|||||||
void avStarting(int friendId, int callIndex);
|
void avStarting(int friendId, int callIndex);
|
||||||
void avEnding(int friendId, int callIndex);
|
void avEnding(int friendId, int callIndex);
|
||||||
void avRequestTimeout(int friendId, int callIndex);
|
void avRequestTimeout(int friendId, int callIndex);
|
||||||
|
void avPeerTimeout(int friendId, int callIndex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void onFriendRequest(Tox* tox, const uint8_t* cUserId, const uint8_t* cMessage, uint16_t cMessageSize, void* core);
|
static void onFriendRequest(Tox* tox, const uint8_t* cUserId, const uint8_t* cMessage, uint16_t cMessageSize, void* core);
|
||||||
|
@ -361,6 +361,17 @@ void ChatForm::onAvRequestTimeout(int FriendId, int)
|
|||||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
|
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatForm::onAvPeerTimeout(int FriendId, int)
|
||||||
|
{
|
||||||
|
if (FriendId != f->friendId)
|
||||||
|
return;
|
||||||
|
QPalette toxgreen;
|
||||||
|
toxgreen.setColor(QPalette::Button, QColor(107,194,96)); // Tox Green
|
||||||
|
callButton->setPalette(toxgreen);
|
||||||
|
callButton->disconnect();
|
||||||
|
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
|
||||||
|
}
|
||||||
|
|
||||||
void ChatForm::onAnswerCallTriggered()
|
void ChatForm::onAnswerCallTriggered()
|
||||||
{
|
{
|
||||||
emit answerCall(callId);
|
emit answerCall(callId);
|
||||||
|
@ -51,6 +51,7 @@ public slots:
|
|||||||
void onAvStarting(int FriendId, int CallId);
|
void onAvStarting(int FriendId, int CallId);
|
||||||
void onAvEnding(int FriendId, int CallId);
|
void onAvEnding(int FriendId, int CallId);
|
||||||
void onAvRequestTimeout(int FriendId, int CallId);
|
void onAvRequestTimeout(int FriendId, int CallId);
|
||||||
|
void onAvPeerTimeout(int FriendId, int CallId);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onSendTriggered();
|
void onSendTriggered();
|
||||||
|
@ -258,6 +258,7 @@ void Widget::addFriend(int friendId, const QString &userId)
|
|||||||
connect(core, &Core::avStarting, newfriend->chatForm, &ChatForm::onAvStarting);
|
connect(core, &Core::avStarting, newfriend->chatForm, &ChatForm::onAvStarting);
|
||||||
connect(core, &Core::avEnding, newfriend->chatForm, &ChatForm::onAvEnding);
|
connect(core, &Core::avEnding, newfriend->chatForm, &ChatForm::onAvEnding);
|
||||||
connect(core, &Core::avRequestTimeout, newfriend->chatForm, &ChatForm::onAvRequestTimeout);
|
connect(core, &Core::avRequestTimeout, newfriend->chatForm, &ChatForm::onAvRequestTimeout);
|
||||||
|
connect(core, &Core::avPeerTimeout, newfriend->chatForm, &ChatForm::onAvPeerTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::addFriendFailed(const QString&)
|
void Widget::addFriendFailed(const QString&)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user