From 444972f214f50a9c3f2fbbb72f131cb59c6622ec Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 28 Aug 2014 12:12:56 +0200 Subject: [PATCH 01/18] Fix fileTransferFinsished not emitted on sender --- core.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core.cpp b/core.cpp index a9475071b..1b7f9f088 100644 --- a/core.cpp +++ b/core.cpp @@ -970,7 +970,7 @@ void Core::sendAllFileData(Core *core, ToxFile* file) { if (file->status == ToxFile::PAUSED) { - QThread::sleep(5); + QThread::sleep(1); continue; } else if (file->status == ToxFile::STOPPED) @@ -990,7 +990,7 @@ void Core::sendAllFileData(Core *core, ToxFile* file) removeFileFromQueue(true, file->friendId, file->fileNum); return; } - qDebug() << "chunkSize: " << chunkSize; + //qDebug() << "chunkSize: " << chunkSize; chunkSize = std::min(chunkSize, file->filesize); uint8_t* data = new uint8_t[chunkSize]; file->file->seek(file->bytesSent); @@ -1023,6 +1023,7 @@ void Core::sendAllFileData(Core *core, ToxFile* file) } qDebug("Core::fileHeartbeat: Transfer finished"); tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_FINISHED, nullptr, 0); + emit core->fileTransferFinished(*file); } void Core::onAvInvite(void* _toxav, int32_t call_index, void* core) From c657296535573df4e797da18f7cea308397d7815 Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 28 Aug 2014 12:52:04 +0200 Subject: [PATCH 02/18] Fix #152 and fix #131 We weren't reopening the audio input on subsequent calls --- core.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core.cpp b/core.cpp index 1b7f9f088..6e60004c9 100644 --- a/core.cpp +++ b/core.cpp @@ -1359,13 +1359,18 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled if (!QAudioDeviceInfo::defaultInputDevice().isFormatSupported(format)) { calls[callId].audioInput = nullptr; - qWarning() << "Default input format not supported, cannot record audio"; + qWarning() << "Core: Default input format not supported, cannot record audio"; } else if (calls[callId].audioInput==nullptr) { + qDebug() << "Core: Starting new audio input"; calls[callId].audioInput = new QAudioInput(format); calls[callId].audioInputDevice = calls[callId].audioInput->start(); } + else if (calls[callId].audioInput->state() == QAudio::StoppedState) + { + calls[callId].audioInputDevice = calls[callId].audioInput->start(); + } // Go calls[callId].active = true; From 8602327d16ff818882d11db55cdd37114ac6aeb1 Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 28 Aug 2014 13:09:26 +0200 Subject: [PATCH 03/18] Term deadlocked threads on exit To prevent deadlocking the whole thing when we really just want to exit Qt w/ pulseaudio can deadlock internally on call start if it fails to connect to the PA context. Awesome. --- widget/widget.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/widget/widget.cpp b/widget/widget.cpp index 2feb16d99..9f56f21f7 100644 --- a/widget/widget.cpp +++ b/widget/widget.cpp @@ -219,7 +219,9 @@ Widget::~Widget() core->saveConfiguration(); instance = nullptr; coreThread->exit(); - coreThread->wait(); + coreThread->wait(500); // In case of deadlock (can happen with QtAudio/PA bugs) + if (!coreThread->isFinished()) + coreThread->terminate(); delete core; delete camview; From 287d50eea47af8d3cd4e6971bbd44d1ee99b0869 Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 28 Aug 2014 14:30:38 +0200 Subject: [PATCH 04/18] Split core into core and coreav --- core.cpp | 504 ---------------------------------------------------- coreav.cpp | 512 +++++++++++++++++++++++++++++++++++++++++++++++++++++ qtox.pro | 3 +- 3 files changed, 514 insertions(+), 505 deletions(-) create mode 100644 coreav.cpp diff --git a/core.cpp b/core.cpp index 6e60004c9..7ad6d0a3c 100644 --- a/core.cpp +++ b/core.cpp @@ -34,10 +34,6 @@ const QString Core::CONFIG_FILE_NAME = "data"; QList Core::fileSendQueue; QList Core::fileRecvQueue; -ToxCall Core::calls[TOXAV_MAX_CALLS]; -const int Core::videobufsize{TOXAV_MAX_VIDEO_WIDTH * TOXAV_MAX_VIDEO_HEIGHT * 4}; -uint8_t* Core::videobuf; -int Core::videoBusyness; Core::Core(Camera* cam, QThread *coreThread) : tox(nullptr), camera(cam) @@ -1026,488 +1022,6 @@ void Core::sendAllFileData(Core *core, ToxFile* file) emit core->fileTransferFinished(*file); } -void Core::onAvInvite(void* _toxav, int32_t call_index, void* core) -{ - ToxAv* toxav = static_cast(_toxav); - - int friendId = toxav_get_peer_id(toxav, call_index, 0); - if (friendId < 0) - { - qWarning() << "Core: Received invalid AV invite"; - return; - } - - ToxAvCSettings* transSettings = new ToxAvCSettings; - int err = toxav_get_peer_csettings(toxav, call_index, 0, transSettings); - if (err != ErrorNone) - { - qWarning() << "Core::onAvInvite: error getting call type"; - delete transSettings; - return; - } - - if (transSettings->call_type == TypeVideo) - { - qDebug() << QString("Core: AV invite from %1 with video").arg(friendId); - emit static_cast(core)->avInvite(friendId, call_index, true); - } - else - { - qDebug() << QString("Core: AV invite from %1 without video").arg(friendId); - emit static_cast(core)->avInvite(friendId, call_index, false); - } - - delete transSettings; -} - -void Core::onAvStart(void* _toxav, int32_t call_index, void* core) -{ - ToxAv* toxav = static_cast(_toxav); - - int friendId = toxav_get_peer_id(toxav, call_index, 0); - if (friendId < 0) - { - qWarning() << "Core: Received invalid AV start"; - return; - } - - ToxAvCSettings* transSettings = new ToxAvCSettings; - int err = toxav_get_peer_csettings(toxav, call_index, 0, transSettings); - if (err != ErrorNone) - { - qWarning() << "Core::onAvStart: error getting call type"; - delete transSettings; - return; - } - - if (transSettings->call_type == TypeVideo) - { - qDebug() << QString("Core: AV start from %1 with video").arg(friendId); - prepareCall(friendId, call_index, toxav, true); - emit static_cast(core)->avStart(friendId, call_index, true); - } - else - { - qDebug() << QString("Core: AV start from %1 without video").arg(friendId); - prepareCall(friendId, call_index, toxav, false); - emit static_cast(core)->avStart(friendId, call_index, false); - } - - delete transSettings; -} - -void Core::onAvCancel(void* _toxav, int32_t call_index, void* core) -{ - ToxAv* toxav = static_cast(_toxav); - - int friendId = toxav_get_peer_id(toxav, call_index, 0); - if (friendId < 0) - { - qWarning() << "Core: Received invalid AV cancel"; - return; - } - qDebug() << QString("Core: AV cancel from %1").arg(friendId); - - emit static_cast(core)->avCancel(friendId, call_index); -} - -void Core::onAvReject(void*, int32_t, void*) -{ - qDebug() << "Core: AV reject"; -} - -void Core::onAvEnd(void* _toxav, int32_t call_index, void* core) -{ - ToxAv* toxav = static_cast(_toxav); - - int friendId = toxav_get_peer_id(toxav, call_index, 0); - if (friendId < 0) - { - qWarning() << "Core: Received invalid AV end"; - return; - } - qDebug() << QString("Core: AV end from %1").arg(friendId); - - cleanupCall(call_index); - - emit static_cast(core)->avEnd(friendId, call_index); -} - -void Core::onAvRinging(void* _toxav, int32_t call_index, void* core) -{ - ToxAv* toxav = static_cast(_toxav); - - int friendId = toxav_get_peer_id(toxav, call_index, 0); - if (friendId < 0) - { - qWarning() << "Core: Received invalid AV ringing"; - return; - } - - if (calls[call_index].videoEnabled) - { - qDebug() << QString("Core: AV ringing with %1 with video").arg(friendId); - emit static_cast(core)->avRinging(friendId, call_index, true); - } - else - { - qDebug() << QString("Core: AV ringing with %1 without video").arg(friendId); - emit static_cast(core)->avRinging(friendId, call_index, false); - } -} - -void Core::onAvStarting(void* _toxav, int32_t call_index, void* core) -{ - ToxAv* toxav = static_cast(_toxav); - - int friendId = toxav_get_peer_id(toxav, call_index, 0); - if (friendId < 0) - { - qWarning() << "Core: Received invalid AV starting"; - return; - } - - ToxAvCSettings* transSettings = new ToxAvCSettings; - int err = toxav_get_peer_csettings(toxav, call_index, 0, transSettings); - if (err != ErrorNone) - { - qWarning() << "Core::onAvStarting: error getting call type"; - delete transSettings; - return; - } - - if (transSettings->call_type == TypeVideo) - { - qDebug() << QString("Core: AV starting from %1 with video").arg(friendId); - prepareCall(friendId, call_index, toxav, true); - emit static_cast(core)->avStarting(friendId, call_index, true); - } - else - { - qDebug() << QString("Core: AV starting from %1 without video").arg(friendId); - prepareCall(friendId, call_index, toxav, false); - emit static_cast(core)->avStarting(friendId, call_index, false); - } - - delete transSettings; -} - -void Core::onAvEnding(void* _toxav, int32_t call_index, void* core) -{ - ToxAv* toxav = static_cast(_toxav); - - int friendId = toxav_get_peer_id(toxav, call_index, 0); - if (friendId < 0) - { - qWarning() << "Core: Received invalid AV ending"; - return; - } - qDebug() << QString("Core: AV ending from %1").arg(friendId); - - cleanupCall(call_index); - - emit static_cast(core)->avEnding(friendId, call_index); -} - -void Core::onAvRequestTimeout(void* _toxav, int32_t call_index, void* core) -{ - ToxAv* toxav = static_cast(_toxav); - - int friendId = toxav_get_peer_id(toxav, call_index, 0); - if (friendId < 0) - { - qWarning() << "Core: Received invalid AV request timeout"; - return; - } - qDebug() << QString("Core: AV request timeout with %1").arg(friendId); - - cleanupCall(call_index); - - emit static_cast(core)->avRequestTimeout(friendId, call_index); -} - -void Core::onAvPeerTimeout(void* _toxav, int32_t call_index, void* core) -{ - ToxAv* toxav = static_cast(_toxav); - - int friendId = toxav_get_peer_id(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); - - cleanupCall(call_index); - - emit static_cast(core)->avPeerTimeout(friendId, call_index); -} - -void Core::onAvMediaChange(void*, int32_t, void*) -{ - // HALP, PLS COMPLETE MEH - qWarning() << "If you see this, please complain on GitHub about seeing me! (Don't forget to say what caused me!)"; -} - -void Core::answerCall(int callId) -{ - int friendId = toxav_get_peer_id(toxav, callId, 0); - if (friendId < 0) - { - qWarning() << "Core: Received invalid AV answer peer ID"; - return; - } - - ToxAvCSettings* transSettings = new ToxAvCSettings; - int err = toxav_get_peer_csettings(toxav, callId, 0, transSettings); - if (err != ErrorNone) - { - qWarning() << "Core::answerCall: error getting call settings"; - delete transSettings; - return; - } - - if (transSettings->call_type == TypeVideo) - { - qDebug() << QString("Core: answering call %1 with video").arg(callId); - toxav_answer(toxav, callId, transSettings); - } - else - { - qDebug() << QString("Core: answering call %1 without video").arg(callId); - toxav_answer(toxav, callId, transSettings); - } - - delete transSettings; -} - -void Core::hangupCall(int callId) -{ - qDebug() << QString("Core: hanging up call %1").arg(callId); - calls[callId].active = false; - toxav_hangup(toxav, callId); -} - -void Core::startCall(int friendId, bool video) -{ - int callId; - ToxAvCSettings cSettings = av_DefaultSettings; - cSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH; - cSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT; - if (video) - { - qDebug() << QString("Core: Starting new call with %1 with video").arg(friendId); - cSettings.call_type = TypeVideo; - toxav_call(toxav, &callId, friendId, &cSettings, TOXAV_RINGING_TIME); - calls[callId].videoEnabled=true; - } - else - { - qDebug() << QString("Core: Starting new call with %1 without video").arg(friendId); - cSettings.call_type = TypeAudio; - toxav_call(toxav, &callId, friendId, &cSettings, TOXAV_RINGING_TIME); - calls[callId].videoEnabled=false; - } -} - -void Core::cancelCall(int callId, int friendId) -{ - qDebug() << QString("Core: Cancelling call with %1").arg(friendId); - calls[callId].active = false; - toxav_cancel(toxav, callId, friendId, 0); -} - -void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled) -{ - qDebug() << QString("Core: preparing call %1").arg(callId); - calls[callId].callId = callId; - calls[callId].friendId = friendId; - // the following three lines are also now redundant from startCall, but are - // necessary there for outbound and here for inbound - calls[callId].codecSettings = av_DefaultSettings; - calls[callId].codecSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH; - calls[callId].codecSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT; - calls[callId].videoEnabled = videoEnabled; - toxav_prepare_transmission(toxav, callId, av_jbufdc, av_VADd, videoEnabled); - - // Prepare output - QAudioFormat format; - format.setSampleRate(calls[callId].codecSettings.audio_sample_rate); - format.setChannelCount(calls[callId].codecSettings.audio_channels); - format.setSampleSize(16); - format.setCodec("audio/pcm"); - format.setByteOrder(QAudioFormat::LittleEndian); - format.setSampleType(QAudioFormat::SignedInt); - if (!QAudioDeviceInfo::defaultOutputDevice().isFormatSupported(format)) - { - calls[callId].audioOutput = nullptr; - qWarning() << "Core: Raw audio format not supported by output backend, cannot play audio."; - } - else if (calls[callId].audioOutput==nullptr) - { - calls[callId].audioOutput = new QAudioOutput(format); - calls[callId].audioOutput->setBufferSize(1900*30); // Make this bigger to get less underflows, but more latency - calls[callId].audioOutput->start(&calls[callId].audioBuffer); - int error = calls[callId].audioOutput->error(); - if (error != QAudio::NoError) - { - qWarning() << QString("Core: Error %1 when starting audio output").arg(error); - } - } - - // Start input - if (!QAudioDeviceInfo::defaultInputDevice().isFormatSupported(format)) - { - calls[callId].audioInput = nullptr; - qWarning() << "Core: Default input format not supported, cannot record audio"; - } - else if (calls[callId].audioInput==nullptr) - { - qDebug() << "Core: Starting new audio input"; - calls[callId].audioInput = new QAudioInput(format); - calls[callId].audioInputDevice = calls[callId].audioInput->start(); - } - else if (calls[callId].audioInput->state() == QAudio::StoppedState) - { - calls[callId].audioInputDevice = calls[callId].audioInput->start(); - } - - // Go - calls[callId].active = true; - - if (calls[callId].audioInput != nullptr) - { - calls[callId].sendAudioTimer->setInterval(2); - calls[callId].sendAudioTimer->setSingleShot(true); - connect(calls[callId].sendAudioTimer, &QTimer::timeout, [=](){sendCallAudio(callId,toxav);}); - calls[callId].sendAudioTimer->start(); - } - - if (calls[callId].videoEnabled) - { - calls[callId].sendVideoTimer->setInterval(50); - calls[callId].sendVideoTimer->setSingleShot(true); - calls[callId].sendVideoTimer->start(); - - Widget::getInstance()->getCamera()->suscribe(); - } - else if (calls[callId].audioInput == nullptr && calls[callId].audioOutput == nullptr) - { - qWarning() << "Audio only call can neither play nor record audio, killing call"; - toxav_hangup(toxav, callId); - } -} - -void Core::cleanupCall(int callId) -{ - qDebug() << QString("Core: cleaning up call %1").arg(callId); - calls[callId].active = false; - disconnect(calls[callId].sendAudioTimer,0,0,0); - calls[callId].sendAudioTimer->stop(); - calls[callId].sendVideoTimer->stop(); - if (calls[callId].audioOutput != nullptr) - { - calls[callId].audioOutput->stop(); - } - if (calls[callId].audioInput != nullptr) - { - calls[callId].audioInput->stop(); - } - if (calls[callId].videoEnabled) - Widget::getInstance()->getCamera()->unsuscribe(); - calls[callId].audioBuffer.clear(); -} - -void Core::playCallAudio(ToxAv*, int32_t callId, int16_t *data, int length, void *user_data) -{ - Q_UNUSED(user_data); - - if (!calls[callId].active || calls[callId].audioOutput == nullptr) - return; - calls[callId].audioBuffer.write((char*)data, length*2); - int state = calls[callId].audioOutput->state(); - if (state != QAudio::ActiveState) - { - qDebug() << QString("Core: Audio state is %1").arg(state); - calls[callId].audioOutput->start(&calls[callId].audioBuffer); - } - int error = calls[callId].audioOutput->error(); - if (error != QAudio::NoError) - qWarning() << QString("Core::playCallAudio: Error: %1").arg(error); -} - -void Core::sendCallAudio(int callId, ToxAv* toxav) -{ - if (!calls[callId].active || calls[callId].audioInput == nullptr) - return; - 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); - calls[callId].sendAudioTimer->start(); - return; - } - result = toxav_send_audio(toxav, callId, dest, result); - if (result < 0) - { - qWarning() << QString("Core: Unable to send audio frame, error %1").arg(result); - calls[callId].sendAudioTimer->start(); - return; - } - calls[callId].sendAudioTimer->start(); - } - else - calls[callId].sendAudioTimer->start(); -} - -void Core::playCallVideo(ToxAv*, int32_t callId, vpx_image_t* img, void *user_data) -{ - Q_UNUSED(user_data); - - if (!calls[callId].active || !calls[callId].videoEnabled) - return; - - if (videoBusyness >= 1) - qWarning() << "Core: playCallVideo: Busy, dropping current frame"; - else - emit Widget::getInstance()->getCore()->videoFrameReceived(img); - vpx_img_free(img); -} - -void Core::sendCallVideo(int callId) -{ - if (!calls[callId].active || !calls[callId].videoEnabled) - return; - - vpx_image frame = camera->getLastVPXImage(); - if (frame.w && frame.h) - { - int result; - if((result = toxav_prepare_video_frame(toxav, callId, videobuf, videobufsize, &frame)) < 0) - { - qDebug() << QString("Core: toxav_prepare_video_frame: error %1").arg(result); - vpx_img_free(&frame); - calls[callId].sendVideoTimer->start(); - return; - } - - if((result = toxav_send_video(toxav, callId, (uint8_t*)videobuf, result)) < 0) - qDebug() << QString("Core: toxav_send_video error: %1").arg(result); - - vpx_img_free(&frame); - } - else - qDebug("Core::sendCallVideo: Invalid frame (bad camera ?)"); - - calls[callId].sendVideoTimer->start(); -} - void Core::groupInviteFriend(int friendId, int groupId) { tox_invite_friend(tox, friendId, groupId); @@ -1517,21 +1031,3 @@ void Core::createGroup() { emit emptyGroupCreated(tox_add_groupchat(tox)); } - -void Core::increaseVideoBusyness() -{ - videoBusyness++; -} - -void Core::decreaseVideoBusyness() -{ - videoBusyness--; -} - -void Core::micMuteToggle(int callId) -{ - if (calls[callId].audioInput->state() == QAudio::ActiveState) - calls[callId].audioInput->suspend(); - else - calls[callId].audioInput->start(); -} diff --git a/coreav.cpp b/coreav.cpp new file mode 100644 index 000000000..99be2a286 --- /dev/null +++ b/coreav.cpp @@ -0,0 +1,512 @@ +#include "core.h" +#include "widget/widget.h" + +#include +#include + +ToxCall Core::calls[TOXAV_MAX_CALLS]; +const int Core::videobufsize{TOXAV_MAX_VIDEO_WIDTH * TOXAV_MAX_VIDEO_HEIGHT * 4}; +uint8_t* Core::videobuf; +int Core::videoBusyness; + +void Core::onAvMediaChange(void*, int32_t, void*) +{ + // HALP, PLS COMPLETE MEH + qWarning() << "If you see this, please complain on GitHub about seeing me! (Don't forget to say what caused me!)"; +} + +void Core::answerCall(int callId) +{ + int friendId = toxav_get_peer_id(toxav, callId, 0); + if (friendId < 0) + { + qWarning() << "Core: Received invalid AV answer peer ID"; + return; + } + + ToxAvCSettings* transSettings = new ToxAvCSettings; + int err = toxav_get_peer_csettings(toxav, callId, 0, transSettings); + if (err != ErrorNone) + { + qWarning() << "Core::answerCall: error getting call settings"; + delete transSettings; + return; + } + + if (transSettings->call_type == TypeVideo) + { + qDebug() << QString("Core: answering call %1 with video").arg(callId); + toxav_answer(toxav, callId, transSettings); + } + else + { + qDebug() << QString("Core: answering call %1 without video").arg(callId); + toxav_answer(toxav, callId, transSettings); + } + + delete transSettings; +} + +void Core::hangupCall(int callId) +{ + qDebug() << QString("Core: hanging up call %1").arg(callId); + calls[callId].active = false; + toxav_hangup(toxav, callId); +} + +void Core::startCall(int friendId, bool video) +{ + int callId; + ToxAvCSettings cSettings = av_DefaultSettings; + cSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH; + cSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT; + if (video) + { + qDebug() << QString("Core: Starting new call with %1 with video").arg(friendId); + cSettings.call_type = TypeVideo; + toxav_call(toxav, &callId, friendId, &cSettings, TOXAV_RINGING_TIME); + calls[callId].videoEnabled=true; + } + else + { + qDebug() << QString("Core: Starting new call with %1 without video").arg(friendId); + cSettings.call_type = TypeAudio; + toxav_call(toxav, &callId, friendId, &cSettings, TOXAV_RINGING_TIME); + calls[callId].videoEnabled=false; + } +} + +void Core::cancelCall(int callId, int friendId) +{ + qDebug() << QString("Core: Cancelling call with %1").arg(friendId); + calls[callId].active = false; + toxav_cancel(toxav, callId, friendId, 0); +} + +void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled) +{ + qDebug() << QString("Core: preparing call %1").arg(callId); + calls[callId].callId = callId; + calls[callId].friendId = friendId; + // the following three lines are also now redundant from startCall, but are + // necessary there for outbound and here for inbound + calls[callId].codecSettings = av_DefaultSettings; + calls[callId].codecSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH; + calls[callId].codecSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT; + calls[callId].videoEnabled = videoEnabled; + toxav_prepare_transmission(toxav, callId, av_jbufdc, av_VADd, videoEnabled); + + // Prepare output + QAudioFormat format; + format.setSampleRate(calls[callId].codecSettings.audio_sample_rate); + format.setChannelCount(calls[callId].codecSettings.audio_channels); + format.setSampleSize(16); + format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::SignedInt); + if (!QAudioDeviceInfo::defaultOutputDevice().isFormatSupported(format)) + { + calls[callId].audioOutput = nullptr; + qWarning() << "Core: Raw audio format not supported by output backend, cannot play audio."; + } + else if (calls[callId].audioOutput==nullptr) + { + calls[callId].audioOutput = new QAudioOutput(format); + calls[callId].audioOutput->setBufferSize(1900*30); // Make this bigger to get less underflows, but more latency + calls[callId].audioOutput->start(&calls[callId].audioBuffer); + int error = calls[callId].audioOutput->error(); + if (error != QAudio::NoError) + { + qWarning() << QString("Core: Error %1 when starting audio output").arg(error); + } + } + + // Start input + if (!QAudioDeviceInfo::defaultInputDevice().isFormatSupported(format)) + { + calls[callId].audioInput = nullptr; + qWarning() << "Core: Default input format not supported, cannot record audio"; + } + else if (calls[callId].audioInput==nullptr) + { + qDebug() << "Core: Starting new audio input"; + calls[callId].audioInput = new QAudioInput(format); + calls[callId].audioInputDevice = calls[callId].audioInput->start(); + } + else if (calls[callId].audioInput->state() == QAudio::StoppedState) + { + calls[callId].audioInputDevice = calls[callId].audioInput->start(); + } + + // Go + calls[callId].active = true; + + if (calls[callId].audioInput != nullptr) + { + calls[callId].sendAudioTimer->setInterval(2); + calls[callId].sendAudioTimer->setSingleShot(true); + connect(calls[callId].sendAudioTimer, &QTimer::timeout, [=](){sendCallAudio(callId,toxav);}); + calls[callId].sendAudioTimer->start(); + } + + if (calls[callId].videoEnabled) + { + calls[callId].sendVideoTimer->setInterval(50); + calls[callId].sendVideoTimer->setSingleShot(true); + calls[callId].sendVideoTimer->start(); + + Widget::getInstance()->getCamera()->suscribe(); + } + else if (calls[callId].audioInput == nullptr && calls[callId].audioOutput == nullptr) + { + qWarning() << "Audio only call can neither play nor record audio, killing call"; + toxav_hangup(toxav, callId); + } +} + +void Core::cleanupCall(int callId) +{ + qDebug() << QString("Core: cleaning up call %1").arg(callId); + calls[callId].active = false; + disconnect(calls[callId].sendAudioTimer,0,0,0); + calls[callId].sendAudioTimer->stop(); + calls[callId].sendVideoTimer->stop(); + if (calls[callId].audioOutput != nullptr) + { + calls[callId].audioOutput->stop(); + } + if (calls[callId].audioInput != nullptr) + { + calls[callId].audioInput->stop(); + } + if (calls[callId].videoEnabled) + Widget::getInstance()->getCamera()->unsuscribe(); + calls[callId].audioBuffer.clear(); +} + +void Core::playCallAudio(ToxAv*, int32_t callId, int16_t *data, int length, void *user_data) +{ + Q_UNUSED(user_data); + + if (!calls[callId].active || calls[callId].audioOutput == nullptr) + return; + calls[callId].audioBuffer.write((char*)data, length*2); + int state = calls[callId].audioOutput->state(); + if (state != QAudio::ActiveState) + { + qDebug() << QString("Core: Audio state is %1").arg(state); + calls[callId].audioOutput->start(&calls[callId].audioBuffer); + } + int error = calls[callId].audioOutput->error(); + if (error != QAudio::NoError) + qWarning() << QString("Core::playCallAudio: Error: %1").arg(error); +} + +void Core::sendCallAudio(int callId, ToxAv* toxav) +{ + if (!calls[callId].active || calls[callId].audioInput == nullptr) + return; + 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); + calls[callId].sendAudioTimer->start(); + return; + } + result = toxav_send_audio(toxav, callId, dest, result); + if (result < 0) + { + qWarning() << QString("Core: Unable to send audio frame, error %1").arg(result); + calls[callId].sendAudioTimer->start(); + return; + } + calls[callId].sendAudioTimer->start(); + } + else + calls[callId].sendAudioTimer->start(); +} + +void Core::playCallVideo(ToxAv*, int32_t callId, vpx_image_t* img, void *user_data) +{ + Q_UNUSED(user_data); + + if (!calls[callId].active || !calls[callId].videoEnabled) + return; + + if (videoBusyness >= 1) + qWarning() << "Core: playCallVideo: Busy, dropping current frame"; + else + emit Widget::getInstance()->getCore()->videoFrameReceived(img); + vpx_img_free(img); +} + +void Core::sendCallVideo(int callId) +{ + if (!calls[callId].active || !calls[callId].videoEnabled) + return; + + vpx_image frame = camera->getLastVPXImage(); + if (frame.w && frame.h) + { + int result; + if((result = toxav_prepare_video_frame(toxav, callId, videobuf, videobufsize, &frame)) < 0) + { + qDebug() << QString("Core: toxav_prepare_video_frame: error %1").arg(result); + vpx_img_free(&frame); + calls[callId].sendVideoTimer->start(); + return; + } + + if((result = toxav_send_video(toxav, callId, (uint8_t*)videobuf, result)) < 0) + qDebug() << QString("Core: toxav_send_video error: %1").arg(result); + + vpx_img_free(&frame); + } + else + qDebug("Core::sendCallVideo: Invalid frame (bad camera ?)"); + + calls[callId].sendVideoTimer->start(); +} + + +void Core::increaseVideoBusyness() +{ + videoBusyness++; +} + +void Core::decreaseVideoBusyness() +{ + videoBusyness--; +} + +void Core::micMuteToggle(int callId) +{ + if (calls[callId].audioInput->state() == QAudio::ActiveState) + calls[callId].audioInput->suspend(); + else + calls[callId].audioInput->start(); +} + +void Core::onAvCancel(void* _toxav, int32_t call_index, void* core) +{ + ToxAv* toxav = static_cast(_toxav); + + int friendId = toxav_get_peer_id(toxav, call_index, 0); + if (friendId < 0) + { + qWarning() << "Core: Received invalid AV cancel"; + return; + } + qDebug() << QString("Core: AV cancel from %1").arg(friendId); + + emit static_cast(core)->avCancel(friendId, call_index); +} + +void Core::onAvReject(void*, int32_t, void*) +{ + qDebug() << "Core: AV reject"; +} + +void Core::onAvEnd(void* _toxav, int32_t call_index, void* core) +{ + ToxAv* toxav = static_cast(_toxav); + + int friendId = toxav_get_peer_id(toxav, call_index, 0); + if (friendId < 0) + { + qWarning() << "Core: Received invalid AV end"; + return; + } + qDebug() << QString("Core: AV end from %1").arg(friendId); + + cleanupCall(call_index); + + emit static_cast(core)->avEnd(friendId, call_index); +} + +void Core::onAvRinging(void* _toxav, int32_t call_index, void* core) +{ + ToxAv* toxav = static_cast(_toxav); + + int friendId = toxav_get_peer_id(toxav, call_index, 0); + if (friendId < 0) + { + qWarning() << "Core: Received invalid AV ringing"; + return; + } + + if (calls[call_index].videoEnabled) + { + qDebug() << QString("Core: AV ringing with %1 with video").arg(friendId); + emit static_cast(core)->avRinging(friendId, call_index, true); + } + else + { + qDebug() << QString("Core: AV ringing with %1 without video").arg(friendId); + emit static_cast(core)->avRinging(friendId, call_index, false); + } +} + +void Core::onAvStarting(void* _toxav, int32_t call_index, void* core) +{ + ToxAv* toxav = static_cast(_toxav); + + int friendId = toxav_get_peer_id(toxav, call_index, 0); + if (friendId < 0) + { + qWarning() << "Core: Received invalid AV starting"; + return; + } + + ToxAvCSettings* transSettings = new ToxAvCSettings; + int err = toxav_get_peer_csettings(toxav, call_index, 0, transSettings); + if (err != ErrorNone) + { + qWarning() << "Core::onAvStarting: error getting call type"; + delete transSettings; + return; + } + + if (transSettings->call_type == TypeVideo) + { + qDebug() << QString("Core: AV starting from %1 with video").arg(friendId); + prepareCall(friendId, call_index, toxav, true); + emit static_cast(core)->avStarting(friendId, call_index, true); + } + else + { + qDebug() << QString("Core: AV starting from %1 without video").arg(friendId); + prepareCall(friendId, call_index, toxav, false); + emit static_cast(core)->avStarting(friendId, call_index, false); + } + + delete transSettings; +} + +void Core::onAvEnding(void* _toxav, int32_t call_index, void* core) +{ + ToxAv* toxav = static_cast(_toxav); + + int friendId = toxav_get_peer_id(toxav, call_index, 0); + if (friendId < 0) + { + qWarning() << "Core: Received invalid AV ending"; + return; + } + qDebug() << QString("Core: AV ending from %1").arg(friendId); + + cleanupCall(call_index); + + emit static_cast(core)->avEnding(friendId, call_index); +} + +void Core::onAvRequestTimeout(void* _toxav, int32_t call_index, void* core) +{ + ToxAv* toxav = static_cast(_toxav); + + int friendId = toxav_get_peer_id(toxav, call_index, 0); + if (friendId < 0) + { + qWarning() << "Core: Received invalid AV request timeout"; + return; + } + qDebug() << QString("Core: AV request timeout with %1").arg(friendId); + + cleanupCall(call_index); + + emit static_cast(core)->avRequestTimeout(friendId, call_index); +} + +void Core::onAvPeerTimeout(void* _toxav, int32_t call_index, void* core) +{ + ToxAv* toxav = static_cast(_toxav); + + int friendId = toxav_get_peer_id(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); + + cleanupCall(call_index); + + emit static_cast(core)->avPeerTimeout(friendId, call_index); +} + + +void Core::onAvInvite(void* _toxav, int32_t call_index, void* core) +{ + ToxAv* toxav = static_cast(_toxav); + + int friendId = toxav_get_peer_id(toxav, call_index, 0); + if (friendId < 0) + { + qWarning() << "Core: Received invalid AV invite"; + return; + } + + ToxAvCSettings* transSettings = new ToxAvCSettings; + int err = toxav_get_peer_csettings(toxav, call_index, 0, transSettings); + if (err != ErrorNone) + { + qWarning() << "Core::onAvInvite: error getting call type"; + delete transSettings; + return; + } + + if (transSettings->call_type == TypeVideo) + { + qDebug() << QString("Core: AV invite from %1 with video").arg(friendId); + emit static_cast(core)->avInvite(friendId, call_index, true); + } + else + { + qDebug() << QString("Core: AV invite from %1 without video").arg(friendId); + emit static_cast(core)->avInvite(friendId, call_index, false); + } + + delete transSettings; +} + +void Core::onAvStart(void* _toxav, int32_t call_index, void* core) +{ + ToxAv* toxav = static_cast(_toxav); + + int friendId = toxav_get_peer_id(toxav, call_index, 0); + if (friendId < 0) + { + qWarning() << "Core: Received invalid AV start"; + return; + } + + ToxAvCSettings* transSettings = new ToxAvCSettings; + int err = toxav_get_peer_csettings(toxav, call_index, 0, transSettings); + if (err != ErrorNone) + { + qWarning() << "Core::onAvStart: error getting call type"; + delete transSettings; + return; + } + + if (transSettings->call_type == TypeVideo) + { + qDebug() << QString("Core: AV start from %1 with video").arg(friendId); + prepareCall(friendId, call_index, toxav, true); + emit static_cast(core)->avStart(friendId, call_index, true); + } + else + { + qDebug() << QString("Core: AV start from %1 without video").arg(friendId); + prepareCall(friendId, call_index, toxav, false); + emit static_cast(core)->avStart(friendId, call_index, false); + } + + delete transSettings; +} diff --git a/qtox.pro b/qtox.pro index 84ec1e54e..f52023936 100644 --- a/qtox.pro +++ b/qtox.pro @@ -128,4 +128,5 @@ SOURCES += \ style.cpp \ widget/adjustingscrollarea.cpp \ widget/croppinglabel.cpp \ - widget/friendlistwidget.cpp + widget/friendlistwidget.cpp \ + coreav.cpp From 1c44f7fdf474299c070a48446fab635707567561 Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 28 Aug 2014 15:46:11 +0200 Subject: [PATCH 05/18] OpenAL init and output No cleanup yet! --- core.h | 11 ++- coreav.cpp | 227 ++++++++++++++++++++++++++++++++--------------------- qtox.pro | 4 +- 3 files changed, 150 insertions(+), 92 deletions(-) diff --git a/core.h b/core.h index b07f9a60e..d80c00935 100644 --- a/core.h +++ b/core.h @@ -22,6 +22,9 @@ #include #include +#include +#include + #include #include #include @@ -106,6 +109,9 @@ public: int friendId; bool videoEnabled; bool active; + ALCdevice* alOutDev, *alInDev; + ALCcontext* alContext; + ALuint alSource; }; class Core : public QObject @@ -268,8 +274,9 @@ private: static void prepareCall(int friendId, int callId, ToxAv *toxav, bool videoEnabled); static void cleanupCall(int callId); - static void playCallAudio(ToxAv *toxav, int32_t callId, int16_t *data, int length, void *user_data); // Callback + static void playCallAudio(ToxAv *toxav, int32_t callId, int16_t *data, int samples, void *user_data); // Callback static void sendCallAudio(int callId, ToxAv* toxav); + static void playAudioBuffer(int callId, int16_t *data, int samples); static void playCallVideo(ToxAv* toxav, int32_t callId, vpx_image_t* img, void *user_data); void sendCallVideo(int callId); @@ -278,8 +285,8 @@ private: void loadConfiguration(); void loadFriends(); - static void sendAllFileData(Core* core, ToxFile* file); + static void sendAllFileData(Core* core, ToxFile* file); static void removeFileFromQueue(bool sendQueue, int friendId, int fileId); void checkLastOnline(int friendId); diff --git a/coreav.cpp b/coreav.cpp index 99be2a286..be380c47e 100644 --- a/coreav.cpp +++ b/coreav.cpp @@ -1,88 +1,11 @@ #include "core.h" #include "widget/widget.h" -#include -#include - ToxCall Core::calls[TOXAV_MAX_CALLS]; const int Core::videobufsize{TOXAV_MAX_VIDEO_WIDTH * TOXAV_MAX_VIDEO_HEIGHT * 4}; uint8_t* Core::videobuf; int Core::videoBusyness; -void Core::onAvMediaChange(void*, int32_t, void*) -{ - // HALP, PLS COMPLETE MEH - qWarning() << "If you see this, please complain on GitHub about seeing me! (Don't forget to say what caused me!)"; -} - -void Core::answerCall(int callId) -{ - int friendId = toxav_get_peer_id(toxav, callId, 0); - if (friendId < 0) - { - qWarning() << "Core: Received invalid AV answer peer ID"; - return; - } - - ToxAvCSettings* transSettings = new ToxAvCSettings; - int err = toxav_get_peer_csettings(toxav, callId, 0, transSettings); - if (err != ErrorNone) - { - qWarning() << "Core::answerCall: error getting call settings"; - delete transSettings; - return; - } - - if (transSettings->call_type == TypeVideo) - { - qDebug() << QString("Core: answering call %1 with video").arg(callId); - toxav_answer(toxav, callId, transSettings); - } - else - { - qDebug() << QString("Core: answering call %1 without video").arg(callId); - toxav_answer(toxav, callId, transSettings); - } - - delete transSettings; -} - -void Core::hangupCall(int callId) -{ - qDebug() << QString("Core: hanging up call %1").arg(callId); - calls[callId].active = false; - toxav_hangup(toxav, callId); -} - -void Core::startCall(int friendId, bool video) -{ - int callId; - ToxAvCSettings cSettings = av_DefaultSettings; - cSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH; - cSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT; - if (video) - { - qDebug() << QString("Core: Starting new call with %1 with video").arg(friendId); - cSettings.call_type = TypeVideo; - toxav_call(toxav, &callId, friendId, &cSettings, TOXAV_RINGING_TIME); - calls[callId].videoEnabled=true; - } - else - { - qDebug() << QString("Core: Starting new call with %1 without video").arg(friendId); - cSettings.call_type = TypeAudio; - toxav_call(toxav, &callId, friendId, &cSettings, TOXAV_RINGING_TIME); - calls[callId].videoEnabled=false; - } -} - -void Core::cancelCall(int callId, int friendId) -{ - qDebug() << QString("Core: Cancelling call with %1").arg(friendId); - calls[callId].active = false; - toxav_cancel(toxav, callId, friendId, 0); -} - void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled) { qDebug() << QString("Core: preparing call %1").arg(callId); @@ -96,6 +19,24 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled calls[callId].videoEnabled = videoEnabled; toxav_prepare_transmission(toxav, callId, av_jbufdc, av_VADd, videoEnabled); + // Audio output + calls[callId].alOutDev = alcOpenDevice(nullptr); + if (!calls[callId].alOutDev) + { + qWarning() << "Coreav: Cannot open output audio device, hanging up call"; + toxav_hangup(toxav, callId); + return; + } + calls[callId].alContext=alcCreateContext(calls[callId].alOutDev,nullptr); + if (!alcMakeContextCurrent(calls[callId].alContext)) + { + qWarning() << "Coreav: Cannot create output audio context, hanging up call"; + alcCloseDevice(calls[callId].alOutDev); + toxav_hangup(toxav, callId); + return; + } + alGenSources(1, &calls[callId].alSource); + // Prepare output QAudioFormat format; format.setSampleRate(calls[callId].codecSettings.audio_sample_rate); @@ -164,6 +105,80 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled } } +void Core::onAvMediaChange(void*, int32_t, void*) +{ + // HALP, PLS COMPLETE MEH + qWarning() << "If you see this, please complain on GitHub about seeing me! (Don't forget to say what caused me!)"; +} + +void Core::answerCall(int callId) +{ + int friendId = toxav_get_peer_id(toxav, callId, 0); + if (friendId < 0) + { + qWarning() << "Core: Received invalid AV answer peer ID"; + return; + } + + ToxAvCSettings* transSettings = new ToxAvCSettings; + int err = toxav_get_peer_csettings(toxav, callId, 0, transSettings); + if (err != ErrorNone) + { + qWarning() << "Core::answerCall: error getting call settings"; + delete transSettings; + return; + } + + if (transSettings->call_type == TypeVideo) + { + qDebug() << QString("Core: answering call %1 with video").arg(callId); + toxav_answer(toxav, callId, transSettings); + } + else + { + qDebug() << QString("Core: answering call %1 without video").arg(callId); + toxav_answer(toxav, callId, transSettings); + } + + delete transSettings; +} + +void Core::hangupCall(int callId) +{ + qDebug() << QString("Core: hanging up call %1").arg(callId); + calls[callId].active = false; + toxav_hangup(toxav, callId); +} + +void Core::startCall(int friendId, bool video) +{ + int callId; + ToxAvCSettings cSettings = av_DefaultSettings; + cSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH; + cSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT; + if (video) + { + qDebug() << QString("Core: Starting new call with %1 with video").arg(friendId); + cSettings.call_type = TypeVideo; + toxav_call(toxav, &callId, friendId, &cSettings, TOXAV_RINGING_TIME); + calls[callId].videoEnabled=true; + } + else + { + qDebug() << QString("Core: Starting new call with %1 without video").arg(friendId); + cSettings.call_type = TypeAudio; + toxav_call(toxav, &callId, friendId, &cSettings, TOXAV_RINGING_TIME); + calls[callId].videoEnabled=false; + } +} + +void Core::cancelCall(int callId, int friendId) +{ + qDebug() << QString("Core: Cancelling call with %1").arg(friendId); + calls[callId].active = false; + toxav_cancel(toxav, callId, friendId, 0); +} + void Core::cleanupCall(int callId) { qDebug() << QString("Core: cleaning up call %1").arg(callId); @@ -184,22 +199,14 @@ void Core::cleanupCall(int callId) calls[callId].audioBuffer.clear(); } -void Core::playCallAudio(ToxAv*, int32_t callId, int16_t *data, int length, void *user_data) +void Core::playCallAudio(ToxAv*, int32_t callId, int16_t *data, int samples, void *user_data) { Q_UNUSED(user_data); if (!calls[callId].active || calls[callId].audioOutput == nullptr) return; - calls[callId].audioBuffer.write((char*)data, length*2); - int state = calls[callId].audioOutput->state(); - if (state != QAudio::ActiveState) - { - qDebug() << QString("Core: Audio state is %1").arg(state); - calls[callId].audioOutput->start(&calls[callId].audioBuffer); - } - int error = calls[callId].audioOutput->error(); - if (error != QAudio::NoError) - qWarning() << QString("Core::playCallAudio: Error: %1").arg(error); + + playAudioBuffer(callId, data, samples); } void Core::sendCallAudio(int callId, ToxAv* toxav) @@ -510,3 +517,47 @@ void Core::onAvStart(void* _toxav, int32_t call_index, void* core) delete transSettings; } + +void Core::playAudioBuffer(int callId, int16_t *data, int samples) +{ + unsigned channels = calls[callId].codecSettings.audio_channels; + if(!channels || channels > 2) { + qWarning() << "Core::playAudioBuffer: trying to play on "< Date: Thu, 28 Aug 2014 15:53:58 +0200 Subject: [PATCH 06/18] Remove Qt audio output code --- core.h | 1 - coreav.cpp | 32 ++++++-------------------------- 2 files changed, 6 insertions(+), 27 deletions(-) diff --git a/core.h b/core.h index d80c00935..454be391e 100644 --- a/core.h +++ b/core.h @@ -100,7 +100,6 @@ struct ToxCall { public: AudioBuffer audioBuffer; - QAudioOutput* audioOutput; QAudioInput* audioInput; QIODevice* audioInputDevice; ToxAvCSettings codecSettings; diff --git a/coreav.cpp b/coreav.cpp index be380c47e..628442eaa 100644 --- a/coreav.cpp +++ b/coreav.cpp @@ -37,7 +37,7 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled } alGenSources(1, &calls[callId].alSource); - // Prepare output + // Start input QAudioFormat format; format.setSampleRate(calls[callId].codecSettings.audio_sample_rate); format.setChannelCount(calls[callId].codecSettings.audio_channels); @@ -45,24 +45,6 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled format.setCodec("audio/pcm"); format.setByteOrder(QAudioFormat::LittleEndian); format.setSampleType(QAudioFormat::SignedInt); - if (!QAudioDeviceInfo::defaultOutputDevice().isFormatSupported(format)) - { - calls[callId].audioOutput = nullptr; - qWarning() << "Core: Raw audio format not supported by output backend, cannot play audio."; - } - else if (calls[callId].audioOutput==nullptr) - { - calls[callId].audioOutput = new QAudioOutput(format); - calls[callId].audioOutput->setBufferSize(1900*30); // Make this bigger to get less underflows, but more latency - calls[callId].audioOutput->start(&calls[callId].audioBuffer); - int error = calls[callId].audioOutput->error(); - if (error != QAudio::NoError) - { - qWarning() << QString("Core: Error %1 when starting audio output").arg(error); - } - } - - // Start input if (!QAudioDeviceInfo::defaultInputDevice().isFormatSupported(format)) { calls[callId].audioInput = nullptr; @@ -98,10 +80,11 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled Widget::getInstance()->getCamera()->suscribe(); } - else if (calls[callId].audioInput == nullptr && calls[callId].audioOutput == nullptr) + else if (calls[callId].audioInput == nullptr) { - qWarning() << "Audio only call can neither play nor record audio, killing call"; + qWarning() << "Audio only call can not record audio, killing call"; toxav_hangup(toxav, callId); + return; } } @@ -186,10 +169,6 @@ void Core::cleanupCall(int callId) disconnect(calls[callId].sendAudioTimer,0,0,0); calls[callId].sendAudioTimer->stop(); calls[callId].sendVideoTimer->stop(); - if (calls[callId].audioOutput != nullptr) - { - calls[callId].audioOutput->stop(); - } if (calls[callId].audioInput != nullptr) { calls[callId].audioInput->stop(); @@ -203,7 +182,7 @@ void Core::playCallAudio(ToxAv*, int32_t callId, int16_t *data, int samples, voi { Q_UNUSED(user_data); - if (!calls[callId].active || calls[callId].audioOutput == nullptr) + if (!calls[callId].active) return; playAudioBuffer(callId, data, samples); @@ -518,6 +497,7 @@ void Core::onAvStart(void* _toxav, int32_t call_index, void* core) delete transSettings; } +// This function's logic was shamelessly stolen from uTox void Core::playAudioBuffer(int callId, int16_t *data, int samples) { unsigned channels = calls[callId].codecSettings.audio_channels; From 28b1678a889b7bafb01a0d1e6aa0832390e5d648 Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 28 Aug 2014 15:58:05 +0200 Subject: [PATCH 07/18] Remove all AudioBuffer code --- audiobuffer.cpp | 84 ------------------------------------------------- audiobuffer.h | 42 ------------------------- core.cpp | 1 - core.h | 3 -- coreav.cpp | 3 -- qtox.pro | 2 -- 6 files changed, 135 deletions(-) delete mode 100644 audiobuffer.cpp delete mode 100644 audiobuffer.h diff --git a/audiobuffer.cpp b/audiobuffer.cpp deleted file mode 100644 index 9ca0960bb..000000000 --- a/audiobuffer.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - Copyright (C) 2014 by Project Tox - - This file is part of qTox, a Qt-based graphical interface for Tox. - - This program is libre 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. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the COPYING file for more details. -*/ - -#include "audiobuffer.h" - -AudioBuffer::AudioBuffer() : - QIODevice(0) -{ - open(QIODevice::ReadWrite); -} - -AudioBuffer::~AudioBuffer() -{ - close(); -} - -qint64 AudioBuffer::readData(char *data, qint64 len) -{ - qint64 total; - bufferMutex.lock(); - try { - total = qMin((qint64)buffer.size(), len); - memcpy(data, buffer.constData(), total); - buffer = buffer.mid(total); - } - catch (...) - { - bufferMutex.unlock(); - return 0; - } - bufferMutex.unlock(); - return total; -} - -qint64 AudioBuffer::writeData(const char* data, qint64 len) -{ - bufferMutex.lock(); - try { - buffer.append(data, len); - } - catch (...) - { - bufferMutex.unlock(); - return 0; - } - bufferMutex.unlock(); - return len; -} - -qint64 AudioBuffer::bytesAvailable() const -{ - bufferMutex.lock(); - long long size = buffer.size() + QIODevice::bytesAvailable(); - bufferMutex.unlock(); - return size; -} - -qint64 AudioBuffer::bufferSize() const -{ - bufferMutex.lock(); - long long size = buffer.size(); - bufferMutex.unlock(); - return size; -} - -void AudioBuffer::clear() -{ - bufferMutex.lock(); - buffer.clear(); - bufferMutex.unlock(); -} diff --git a/audiobuffer.h b/audiobuffer.h deleted file mode 100644 index e94b70447..000000000 --- a/audiobuffer.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright (C) 2014 by Project Tox - - This file is part of qTox, a Qt-based graphical interface for Tox. - - This program is libre 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. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the COPYING file for more details. -*/ - -#ifndef AUDIOBUFFER_H -#define AUDIOBUFFER_H - -#include -#include -#include - -class AudioBuffer : public QIODevice -{ - Q_OBJECT -public: - explicit AudioBuffer(); - ~AudioBuffer(); - - qint64 readData(char *data, qint64 maxlen); - qint64 writeData(const char *data, qint64 len); - qint64 bytesAvailable() const; - qint64 bufferSize() const; - void clear(); - -private: - QByteArray buffer; - mutable QMutex bufferMutex; -}; - -#endif // AUDIOBUFFER_H diff --git a/core.cpp b/core.cpp index 7ad6d0a3c..5c4b16537 100644 --- a/core.cpp +++ b/core.cpp @@ -60,7 +60,6 @@ Core::Core(Camera* cam, QThread *coreThread) : { calls[i].sendAudioTimer = new QTimer(); calls[i].sendVideoTimer = new QTimer(); - calls[i].audioBuffer.moveToThread(coreThread); calls[i].sendAudioTimer->moveToThread(coreThread); calls[i].sendVideoTimer->moveToThread(coreThread); connect(calls[i].sendVideoTimer, &QTimer::timeout, [this,i](){sendCallVideo(i);}); diff --git a/core.h b/core.h index 454be391e..3a44d8f3c 100644 --- a/core.h +++ b/core.h @@ -17,8 +17,6 @@ #ifndef CORE_HPP #define CORE_HPP -#include "audiobuffer.h" - #include #include @@ -99,7 +97,6 @@ struct ToxFile struct ToxCall { public: - AudioBuffer audioBuffer; QAudioInput* audioInput; QIODevice* audioInputDevice; ToxAvCSettings codecSettings; diff --git a/coreav.cpp b/coreav.cpp index 628442eaa..550383273 100644 --- a/coreav.cpp +++ b/coreav.cpp @@ -170,12 +170,9 @@ void Core::cleanupCall(int callId) calls[callId].sendAudioTimer->stop(); calls[callId].sendVideoTimer->stop(); if (calls[callId].audioInput != nullptr) - { calls[callId].audioInput->stop(); - } if (calls[callId].videoEnabled) Widget::getInstance()->getCamera()->unsuscribe(); - calls[callId].audioBuffer.clear(); } void Core::playCallAudio(ToxAv*, int32_t callId, int16_t *data, int samples, void *user_data) diff --git a/qtox.pro b/qtox.pro index fdc562a09..544a4d815 100644 --- a/qtox.pro +++ b/qtox.pro @@ -79,7 +79,6 @@ HEADERS += widget/form/addfriendform.h \ friendlist.h \ cdata.h \ cstring.h \ - audiobuffer.h \ widget/selfcamview.h \ widget/videosurface.h \ widget/camera.h \ @@ -117,7 +116,6 @@ SOURCES += \ settings.cpp \ cdata.cpp \ cstring.cpp \ - audiobuffer.cpp \ widget/selfcamview.cpp \ widget/videosurface.cpp \ widget/camera.cpp \ From 92ff16ffb65e88c58f820590fcc5d3a4db139852 Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 28 Aug 2014 16:02:32 +0200 Subject: [PATCH 08/18] Add OpenAL cleanup --- coreav.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/coreav.cpp b/coreav.cpp index 550383273..93c7e7f18 100644 --- a/coreav.cpp +++ b/coreav.cpp @@ -173,6 +173,9 @@ void Core::cleanupCall(int callId) calls[callId].audioInput->stop(); if (calls[callId].videoEnabled) Widget::getInstance()->getCamera()->unsuscribe(); + alcMakeContextCurrent(nullptr); + alcDestroyContext(calls[callId].alContext); + alcCloseDevice(calls[callId].alOutDev); } void Core::playCallAudio(ToxAv*, int32_t callId, int16_t *data, int samples, void *user_data) From 5adce652ca867f159610599bc1dd98ee7293141e Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 28 Aug 2014 17:13:26 +0200 Subject: [PATCH 09/18] OpenAL input --- coreav.cpp | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/coreav.cpp b/coreav.cpp index 93c7e7f18..eaad3b67c 100644 --- a/coreav.cpp +++ b/coreav.cpp @@ -37,6 +37,16 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled } alGenSources(1, &calls[callId].alSource); + // Audio Input + calls[callId].alInDev = alcCaptureOpenDevice(NULL,av_DefaultSettings.audio_sample_rate, AL_FORMAT_MONO16, (av_DefaultSettings.audio_frame_duration * av_DefaultSettings.audio_sample_rate * 4) / 1000); + if (!calls[callId].alInDev) + { + qWarning() << "Coreav: Cannot open input audio device, hanging up call"; + toxav_hangup(toxav, callId); + return; + } + alcCaptureStart(calls[callId].alInDev); + // Start input QAudioFormat format; format.setSampleRate(calls[callId].codecSettings.audio_sample_rate); @@ -190,10 +200,37 @@ void Core::playCallAudio(ToxAv*, int32_t callId, int16_t *data, int samples, voi void Core::sendCallAudio(int callId, ToxAv* toxav) { - if (!calls[callId].active || calls[callId].audioInput == nullptr) + if (!calls[callId].active) return; + int framesize = (calls[callId].codecSettings.audio_frame_duration * calls[callId].codecSettings.audio_sample_rate) / 1000; uint8_t buf[framesize*2], dest[framesize*2]; + + bool frame = false; + ALint samples; + alcGetIntegerv(calls[callId].alInDev, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples); + if(samples >= framesize) + { + alcCaptureSamples(calls[callId].alInDev, buf, framesize); + frame = 1; + } + + if(frame) + { + int r; + if((r = toxav_prepare_audio_frame(toxav, callId, dest, framesize*2, (int16_t*)buf, framesize)) < 0) + { + qDebug() << "Core: toxav_prepare_audio_frame error"; + calls[callId].sendAudioTimer->start(); + return; + } + + if((r = toxav_send_audio(toxav, callId, dest, r)) < 0) + qDebug() << "Core: toxav_send_audio error"; + } + calls[callId].sendAudioTimer->start(); + + /* int bytesReady = calls[callId].audioInput->bytesReady(); if (bytesReady >= framesize*2) { @@ -216,6 +253,7 @@ void Core::sendCallAudio(int callId, ToxAv* toxav) } else calls[callId].sendAudioTimer->start(); + */ } void Core::playCallVideo(ToxAv*, int32_t callId, vpx_image_t* img, void *user_data) From d49e1042fa6c04eff47c17da1cee4691f70ac689 Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 28 Aug 2014 17:32:12 +0200 Subject: [PATCH 10/18] OpenAL input cleanup --- coreav.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/coreav.cpp b/coreav.cpp index eaad3b67c..e8c8399af 100644 --- a/coreav.cpp +++ b/coreav.cpp @@ -186,6 +186,8 @@ void Core::cleanupCall(int callId) alcMakeContextCurrent(nullptr); alcDestroyContext(calls[callId].alContext); alcCloseDevice(calls[callId].alOutDev); + alcCaptureStop(calls[callId].alInDev); + alcCaptureCloseDevice(calls[callId].alInDev); } void Core::playCallAudio(ToxAv*, int32_t callId, int16_t *data, int samples, void *user_data) From 34b87064082e0b4c413aa47a1b6be914f25cfc53 Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 28 Aug 2014 17:46:45 +0200 Subject: [PATCH 11/18] Remove Qt audio input code --- core.h | 3 +- coreav.cpp | 83 ++++++++---------------------------------------------- 2 files changed, 13 insertions(+), 73 deletions(-) diff --git a/core.h b/core.h index 3a44d8f3c..dd32bc0c2 100644 --- a/core.h +++ b/core.h @@ -97,14 +97,13 @@ struct ToxFile struct ToxCall { public: - QAudioInput* audioInput; - QIODevice* audioInputDevice; ToxAvCSettings codecSettings; QTimer *sendAudioTimer, *sendVideoTimer; int callId; int friendId; bool videoEnabled; bool active; + bool muteMic; ALCdevice* alOutDev, *alInDev; ALCcontext* alContext; ALuint alSource; diff --git a/coreav.cpp b/coreav.cpp index e8c8399af..0daa8c09e 100644 --- a/coreav.cpp +++ b/coreav.cpp @@ -11,6 +11,7 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled qDebug() << QString("Core: preparing call %1").arg(callId); calls[callId].callId = callId; calls[callId].friendId = friendId; + calls[callId].muteMic = false; // the following three lines are also now redundant from startCall, but are // necessary there for outbound and here for inbound calls[callId].codecSettings = av_DefaultSettings; @@ -47,55 +48,19 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled } alcCaptureStart(calls[callId].alInDev); - // Start input - QAudioFormat format; - format.setSampleRate(calls[callId].codecSettings.audio_sample_rate); - format.setChannelCount(calls[callId].codecSettings.audio_channels); - format.setSampleSize(16); - format.setCodec("audio/pcm"); - format.setByteOrder(QAudioFormat::LittleEndian); - format.setSampleType(QAudioFormat::SignedInt); - if (!QAudioDeviceInfo::defaultInputDevice().isFormatSupported(format)) - { - calls[callId].audioInput = nullptr; - qWarning() << "Core: Default input format not supported, cannot record audio"; - } - else if (calls[callId].audioInput==nullptr) - { - qDebug() << "Core: Starting new audio input"; - calls[callId].audioInput = new QAudioInput(format); - calls[callId].audioInputDevice = calls[callId].audioInput->start(); - } - else if (calls[callId].audioInput->state() == QAudio::StoppedState) - { - calls[callId].audioInputDevice = calls[callId].audioInput->start(); - } - // Go calls[callId].active = true; - - if (calls[callId].audioInput != nullptr) - { - calls[callId].sendAudioTimer->setInterval(2); - calls[callId].sendAudioTimer->setSingleShot(true); - connect(calls[callId].sendAudioTimer, &QTimer::timeout, [=](){sendCallAudio(callId,toxav);}); - calls[callId].sendAudioTimer->start(); - } - + calls[callId].sendAudioTimer->setInterval(5); + calls[callId].sendAudioTimer->setSingleShot(true); + connect(calls[callId].sendAudioTimer, &QTimer::timeout, [=](){sendCallAudio(callId,toxav);}); + calls[callId].sendAudioTimer->start(); if (calls[callId].videoEnabled) { calls[callId].sendVideoTimer->setInterval(50); calls[callId].sendVideoTimer->setSingleShot(true); calls[callId].sendVideoTimer->start(); - Widget::getInstance()->getCamera()->suscribe(); } - else if (calls[callId].audioInput == nullptr) - { - qWarning() << "Audio only call can not record audio, killing call"; - toxav_hangup(toxav, callId); - return; - } } void Core::onAvMediaChange(void*, int32_t, void*) @@ -179,8 +144,6 @@ void Core::cleanupCall(int callId) disconnect(calls[callId].sendAudioTimer,0,0,0); calls[callId].sendAudioTimer->stop(); calls[callId].sendVideoTimer->stop(); - if (calls[callId].audioInput != nullptr) - calls[callId].audioInput->stop(); if (calls[callId].videoEnabled) Widget::getInstance()->getCamera()->unsuscribe(); alcMakeContextCurrent(nullptr); @@ -205,6 +168,12 @@ void Core::sendCallAudio(int callId, ToxAv* toxav) if (!calls[callId].active) return; + if (calls[callId].muteMic) + { + calls[callId].sendAudioTimer->start(); + return; + } + int framesize = (calls[callId].codecSettings.audio_frame_duration * calls[callId].codecSettings.audio_sample_rate) / 1000; uint8_t buf[framesize*2], dest[framesize*2]; @@ -231,31 +200,6 @@ void Core::sendCallAudio(int callId, ToxAv* toxav) qDebug() << "Core: toxav_send_audio error"; } calls[callId].sendAudioTimer->start(); - - /* - 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); - calls[callId].sendAudioTimer->start(); - return; - } - result = toxav_send_audio(toxav, callId, dest, result); - if (result < 0) - { - qWarning() << QString("Core: Unable to send audio frame, error %1").arg(result); - calls[callId].sendAudioTimer->start(); - return; - } - calls[callId].sendAudioTimer->start(); - } - else - calls[callId].sendAudioTimer->start(); - */ } void Core::playCallVideo(ToxAv*, int32_t callId, vpx_image_t* img, void *user_data) @@ -313,10 +257,7 @@ void Core::decreaseVideoBusyness() void Core::micMuteToggle(int callId) { - if (calls[callId].audioInput->state() == QAudio::ActiveState) - calls[callId].audioInput->suspend(); - else - calls[callId].audioInput->start(); + calls[callId].muteMic = !calls[callId].muteMic; } void Core::onAvCancel(void* _toxav, int32_t call_index, void* core) From af9c370169ee36900eae605863929a303e75f88e Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 28 Aug 2014 19:06:51 +0200 Subject: [PATCH 12/18] Forbid sending sequential files They aren't seekable, and tend to be infinitely big.. --- widget/form/chatform.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/widget/form/chatform.cpp b/widget/form/chatform.cpp index cbb245afb..f11936b12 100644 --- a/widget/form/chatform.cpp +++ b/widget/form/chatform.cpp @@ -29,6 +29,7 @@ #include #include #include +#include ChatForm::ChatForm(Friend* chatFriend) : f(chatFriend), curRow{0}, lockSliderToBottom{true} @@ -286,6 +287,12 @@ void ChatForm::onAttachClicked() QFile file(path); if (!file.exists() || !file.open(QIODevice::ReadOnly)) return; + if (file.isSequential()) + { + QMessageBox::critical(0, "Bad Idea", "You're trying to send a special (sequential) file, that's not going to work!"); + return; + file.close(); + } long long filesize = file.size(); file.close(); QFileInfo fi(path); From 74e9d7a4f5a25c747e20fd4e891d2667e89cb1e6 Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 28 Aug 2014 19:21:48 +0200 Subject: [PATCH 13/18] Fix division by 0 when receiving from empty files Like /dev/zero. --- widget/filetransfertwidget.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/widget/filetransfertwidget.cpp b/widget/filetransfertwidget.cpp index 36027257f..b72d54e1a 100644 --- a/widget/filetransfertwidget.cpp +++ b/widget/filetransfertwidget.cpp @@ -171,10 +171,15 @@ void FileTransfertWidget::onFileTransferInfo(int FriendId, int FileNum, int64_t etaTime = etaTime.addSecs(etaSecs); eta->setText(etaTime.toString("mm:ss")); if (!Filesize) + { progress->setValue(0); + qDebug() << QString("FT: received %1 bytes of an empty file, stop sending sequential devices, zetok!").arg(BytesSent); + } else + { progress->setValue(BytesSent*100/Filesize); - qDebug() << QString("FT: received %1/%2 bytes, progress is %3%").arg(BytesSent).arg(Filesize).arg(BytesSent*100/Filesize); + qDebug() << QString("FT: received %1/%2 bytes, progress is %3%").arg(BytesSent).arg(Filesize).arg(BytesSent*100/Filesize); + } lastUpdate = newtime; lastBytesSent = BytesSent; } From 56edd76df9233e42ea80e752865f199c441f6bd6 Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 28 Aug 2014 20:20:28 +0200 Subject: [PATCH 14/18] Fix use after free on file received --- core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core.cpp b/core.cpp index 5c4b16537..2c908d663 100644 --- a/core.cpp +++ b/core.cpp @@ -337,9 +337,9 @@ void Core::onFileControlCallback(Tox* tox, int32_t friendnumber, uint8_t receive .arg(file->fileNum).arg(file->friendId); file->status = ToxFile::STOPPED; emit static_cast(core)->fileTransferFinished(*file); - removeFileFromQueue((bool)receive_send, file->friendId, file->fileNum); // confirm receive is complete tox_file_send_control(tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_FINISHED, nullptr, 0); + removeFileFromQueue((bool)receive_send, file->friendId, file->fileNum); } else { From a75d1c5c6e81caf9515012e203c1c5cad440c311 Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 28 Aug 2014 20:52:26 +0200 Subject: [PATCH 15/18] Don't call tox_do from FT functions --- core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core.cpp b/core.cpp index 2c908d663..8481988c9 100644 --- a/core.cpp +++ b/core.cpp @@ -1007,7 +1007,7 @@ void Core::sendAllFileData(Core *core, ToxFile* file) if (tox_file_send_data(core->tox, file->friendId, file->fileNum, data, readSize) == -1) { //qWarning("Core::fileHeartbeat: Error sending data chunk"); - core->process(); + //core->process(); delete[] data; QThread::msleep(5); continue; From 3fc37d2d4cd63672f18b1f59ffbfce09aab0f7ae Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 28 Aug 2014 21:56:55 +0200 Subject: [PATCH 16/18] Move file transfer in the core thread Toxcore is not thread safe --- core.cpp | 135 ++++++++++++++++++++++++++++++------------------------- core.h | 4 +- 2 files changed, 77 insertions(+), 62 deletions(-) diff --git a/core.cpp b/core.cpp index 8481988c9..ae1190dee 100644 --- a/core.cpp +++ b/core.cpp @@ -21,6 +21,7 @@ #include "widget/widget.h" #include +#include #include #include @@ -45,8 +46,6 @@ Core::Core(Camera* cam, QThread *coreThread) : toxTimer->setSingleShot(true); //saveTimer = new QTimer(this); //saveTimer->start(TOX_SAVE_INTERVAL); - //fileTimer = new QTimer(this); - //fileTimer->start(TOX_FILE_INTERVAL); bootstrapTimer = new QTimer(this); bootstrapTimer->start(TOX_BOOTSTRAP_INTERVAL); connect(toxTimer, &QTimer::timeout, this, &Core::process); @@ -304,7 +303,10 @@ void Core::onFileControlCallback(Tox* tox, int32_t friendnumber, uint8_t receive file->status = ToxFile::TRANSMITTING; emit static_cast(core)->fileTransferAccepted(*file); qDebug() << "Core: File control callback, file accepted"; - file->sendFuture = QtConcurrent::run(sendAllFileData, static_cast(core), file); + file->sendTimer = new QTimer(static_cast(core)); + connect(file->sendTimer, &QTimer::timeout, std::bind(sendAllFileData,static_cast(core), file)); + file->sendTimer->setSingleShot(true); + file->sendTimer->start(TOX_FILE_INTERVAL); } else if (receive_send == 1 && control_type == TOX_FILECONTROL_KILL) { @@ -312,7 +314,7 @@ void Core::onFileControlCallback(Tox* tox, int32_t friendnumber, uint8_t receive .arg(file->fileNum).arg(file->friendId); file->status = ToxFile::STOPPED; emit static_cast(core)->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING); - file->sendFuture.waitForFinished(); // Wait for sendAllFileData to return before deleting the ToxFile + while (file->sendTimer) QThread::msleep(1); // Wait for sendAllFileData to return before deleting the ToxFile removeFileFromQueue((bool)receive_send, file->friendId, file->fileNum); } else if (receive_send == 1 && control_type == TOX_FILECONTROL_FINISHED) @@ -532,7 +534,7 @@ void Core::cancelFileSend(int friendId, int fileNum) file->status = ToxFile::STOPPED; emit fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING); tox_file_send_control(tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0); - file->sendFuture.waitForFinished(); // Wait until sendAllFileData returns before deleting + while (file->sendTimer) QThread::msleep(1); // Wait until sendAllFileData returns before deleting removeFileFromQueue(true, friendId, fileNum); } @@ -961,64 +963,77 @@ void Core::removeFileFromQueue(bool sendQueue, int friendId, int fileId) void Core::sendAllFileData(Core *core, ToxFile* file) { - while (file->bytesSent < file->filesize) + if (file->status == ToxFile::PAUSED) { - if (file->status == ToxFile::PAUSED) - { - QThread::sleep(1); - continue; - } - else if (file->status == ToxFile::STOPPED) - { - qWarning("Core::sendAllFileData: File is stopped"); - return; - } - emit core->fileTransferInfo(file->friendId, file->fileNum, file->filesize, file->bytesSent, ToxFile::SENDING); - qApp->processEvents(); - long long chunkSize = tox_file_data_size(core->tox, file->friendId); - if (chunkSize == -1) - { - qWarning("Core::fileHeartbeat: Error getting preffered chunk size, aborting file send"); - file->status = ToxFile::STOPPED; - emit core->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING); - tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0); - removeFileFromQueue(true, file->friendId, file->fileNum); - return; - } - //qDebug() << "chunkSize: " << chunkSize; - chunkSize = std::min(chunkSize, file->filesize); - uint8_t* data = new uint8_t[chunkSize]; - file->file->seek(file->bytesSent); - int readSize = file->file->read((char*)data, chunkSize); - if (readSize == -1) - { - qWarning() << QString("Core::sendAllFileData: Error reading from file: %1").arg(file->file->errorString()); - delete[] data; - QThread::msleep(5); - continue; - } - else if (readSize == 0) - { - qWarning() << QString("Core::sendAllFileData: Nothing to read from file: %1").arg(file->file->errorString()); - delete[] data; - QThread::msleep(5); - continue; - } - if (tox_file_send_data(core->tox, file->friendId, file->fileNum, data, readSize) == -1) - { - //qWarning("Core::fileHeartbeat: Error sending data chunk"); - //core->process(); - delete[] data; - QThread::msleep(5); - continue; - } + file->sendTimer->start(TOX_FILE_INTERVAL); + return; + } + else if (file->status == ToxFile::STOPPED) + { + qWarning("Core::sendAllFileData: File is stopped"); + file->sendTimer->disconnect(); + delete file->sendTimer; + file->sendTimer = nullptr; + return; + } + emit core->fileTransferInfo(file->friendId, file->fileNum, file->filesize, file->bytesSent, ToxFile::SENDING); + qApp->processEvents(); + long long chunkSize = tox_file_data_size(core->tox, file->friendId); + if (chunkSize == -1) + { + qWarning("Core::fileHeartbeat: Error getting preffered chunk size, aborting file send"); + file->status = ToxFile::STOPPED; + emit core->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING); + tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0); + removeFileFromQueue(true, file->friendId, file->fileNum); + return; + } + //qDebug() << "chunkSize: " << chunkSize; + chunkSize = std::min(chunkSize, file->filesize); + uint8_t* data = new uint8_t[chunkSize]; + file->file->seek(file->bytesSent); + int readSize = file->file->read((char*)data, chunkSize); + if (readSize == -1) + { + qWarning() << QString("Core::sendAllFileData: Error reading from file: %1").arg(file->file->errorString()); + delete[] data; + file->sendTimer->start(TOX_FILE_INTERVAL); + return; + } + else if (readSize == 0) + { + qWarning() << QString("Core::sendAllFileData: Nothing to read from file: %1").arg(file->file->errorString()); + delete[] data; + file->sendTimer->start(TOX_FILE_INTERVAL); + return; + } + if (tox_file_send_data(core->tox, file->friendId, file->fileNum, data, readSize) == -1) + { + //qWarning("Core::fileHeartbeat: Error sending data chunk"); + //core->process(); delete[] data; - file->bytesSent += readSize; - //qDebug() << QString("Core::fileHeartbeat: sent %1/%2 bytes").arg(file->bytesSent).arg(file->fileData.size()); + QThread::msleep(1); + file->sendTimer->start(TOX_FILE_INTERVAL); + return; + } + delete[] data; + file->bytesSent += readSize; + //qDebug() << QString("Core::fileHeartbeat: sent %1/%2 bytes").arg(file->bytesSent).arg(file->fileData.size()); + + if (file->bytesSent < file->filesize) + { + file->sendTimer->start(TOX_FILE_INTERVAL); + return; + } + else + { + qDebug("Core: File transfer finished"); + file->sendTimer->disconnect(); + delete file->sendTimer; + file->sendTimer = nullptr; + tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_FINISHED, nullptr, 0); + emit core->fileTransferFinished(*file); } - qDebug("Core::fileHeartbeat: Transfer finished"); - tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_FINISHED, nullptr, 0); - emit core->fileTransferFinished(*file); } void Core::groupInviteFriend(int friendId, int groupId) diff --git a/core.h b/core.h index dd32bc0c2..794d866c7 100644 --- a/core.h +++ b/core.h @@ -39,7 +39,7 @@ #define TOXAV_MAX_CALLS 16 #define GROUPCHAT_MAX_SIZE 32 #define TOX_SAVE_INTERVAL 30*1000 -#define TOX_FILE_INTERVAL 20 +#define TOX_FILE_INTERVAL 1 #define TOX_BOOTSTRAP_INTERVAL 10*1000 #define TOXAV_RINGING_TIME 15 @@ -91,7 +91,7 @@ struct ToxFile long long filesize; FileStatus status; FileDirection direction; - QFuture sendFuture; + QTimer* sendTimer; }; struct ToxCall From 5fba31927fb27bdfd2b6482ed170f2c2b33b0062 Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 28 Aug 2014 23:06:53 +0200 Subject: [PATCH 17/18] Fix bad audio quality --- core.h | 2 +- coreav.cpp | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/core.h b/core.h index 794d866c7..fe2a332a7 100644 --- a/core.h +++ b/core.h @@ -271,7 +271,7 @@ private: static void cleanupCall(int callId); static void playCallAudio(ToxAv *toxav, int32_t callId, int16_t *data, int samples, void *user_data); // Callback static void sendCallAudio(int callId, ToxAv* toxav); - static void playAudioBuffer(int callId, int16_t *data, int samples); + static void playAudioBuffer(int callId, int16_t *data, int samples, unsigned channels, int sampleRate); static void playCallVideo(ToxAv* toxav, int32_t callId, vpx_image_t* img, void *user_data); void sendCallVideo(int callId); diff --git a/coreav.cpp b/coreav.cpp index 0daa8c09e..24281d8de 100644 --- a/coreav.cpp +++ b/coreav.cpp @@ -153,14 +153,17 @@ void Core::cleanupCall(int callId) alcCaptureCloseDevice(calls[callId].alInDev); } -void Core::playCallAudio(ToxAv*, int32_t callId, int16_t *data, int samples, void *user_data) +void Core::playCallAudio(ToxAv* toxav, int32_t callId, int16_t *data, int samples, void *user_data) { Q_UNUSED(user_data); if (!calls[callId].active) return; - playAudioBuffer(callId, data, samples); + ToxAvCSettings dest; + if(toxav_get_peer_csettings(toxav, callId, 0, &dest) == 0) + playAudioBuffer(callId, data, samples, dest.audio_channels, dest.audio_sample_rate); + //playAudioBuffer(callId, data, samples, 1, av_DefaultSettings.audio_sample_rate); } void Core::sendCallAudio(int callId, ToxAv* toxav) @@ -479,10 +482,10 @@ void Core::onAvStart(void* _toxav, int32_t call_index, void* core) } // This function's logic was shamelessly stolen from uTox -void Core::playAudioBuffer(int callId, int16_t *data, int samples) +void Core::playAudioBuffer(int callId, int16_t *data, int samples, unsigned channels, int sampleRate) { - unsigned channels = calls[callId].codecSettings.audio_channels; - if(!channels || channels > 2) { + if(!channels || channels > 2) + { qWarning() << "Core::playAudioBuffer: trying to play on "< Date: Thu, 28 Aug 2014 23:11:33 +0200 Subject: [PATCH 18/18] Remove some debug output --- coreav.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/coreav.cpp b/coreav.cpp index 24281d8de..f64a90ae0 100644 --- a/coreav.cpp +++ b/coreav.cpp @@ -163,7 +163,6 @@ void Core::playCallAudio(ToxAv* toxav, int32_t callId, int16_t *data, int sample ToxAvCSettings dest; if(toxav_get_peer_csettings(toxav, callId, 0, &dest) == 0) playAudioBuffer(callId, data, samples, dest.audio_channels, dest.audio_sample_rate); - //playAudioBuffer(callId, data, samples, 1, av_DefaultSettings.audio_sample_rate); } void Core::sendCallAudio(int callId, ToxAv* toxav) @@ -509,7 +508,7 @@ void Core::playAudioBuffer(int callId, int16_t *data, int samples, unsigned chan } else { - qDebug() << "Core: Dropped audio frame" << sampleRate << ", " << channels<<", "<