|
|
|
|
@ -2,6 +2,8 @@
|
|
|
|
|
#include "callwindow.h" |
|
|
|
|
#include "../transport/gui_bridge.h" |
|
|
|
|
#include "call_audio_engine.h" |
|
|
|
|
#include "call_video_engine.h" |
|
|
|
|
#include "../../../src/call/call_video_port.h" |
|
|
|
|
#include "sound_manager.h" |
|
|
|
|
|
|
|
|
|
#include <QVBoxLayout> |
|
|
|
|
@ -9,6 +11,8 @@
|
|
|
|
|
#include <QLabel> |
|
|
|
|
#include <QPushButton> |
|
|
|
|
#include <QComboBox> |
|
|
|
|
#include <QCheckBox> |
|
|
|
|
#include <QSpinBox> |
|
|
|
|
#include <QTimer> |
|
|
|
|
#include <QDateTime> |
|
|
|
|
#include <QCloseEvent> |
|
|
|
|
@ -138,7 +142,8 @@ static QWidget* wrapWithCaption(QWidget* btn, const QString& caption, QLabel** c
|
|
|
|
|
CallWindow::CallWindow(QWidget* parent) |
|
|
|
|
: QWidget(parent, Qt::Window) { |
|
|
|
|
setWindowTitle(QCoreApplication::translate("ChatGui", "Call")); |
|
|
|
|
setFixedSize(360, 480); |
|
|
|
|
resize(640, 800); |
|
|
|
|
setMinimumSize(400, 600); |
|
|
|
|
setAttribute(Qt::WA_DeleteOnClose, false); |
|
|
|
|
|
|
|
|
|
m_avatarLabel = new QLabel(this); |
|
|
|
|
@ -193,6 +198,45 @@ CallWindow::CallWindow(QWidget* parent)
|
|
|
|
|
lay->addWidget(m_connectionLabel); |
|
|
|
|
lay->addWidget(m_bgpPathLabel); |
|
|
|
|
lay->addWidget(m_statsLabel); |
|
|
|
|
m_video = new CallVideoEngine(this); |
|
|
|
|
m_videoImage = new QLabel(this); m_videoImage->setMinimumHeight(180); m_videoImage->setAlignment(Qt::AlignCenter); |
|
|
|
|
lay->addWidget(m_videoImage, 1); |
|
|
|
|
m_videoStatus = new QLabel(this); m_videoStatus->setWordWrap(true); lay->addWidget(m_videoStatus); |
|
|
|
|
m_videoControls = new QWidget(this); auto* videoLayout = new QVBoxLayout(m_videoControls); |
|
|
|
|
videoLayout->setContentsMargins(0, 0, 0, 0); |
|
|
|
|
m_videoEnabled = new QCheckBox(tr("Send video"), this); videoLayout->addWidget(m_videoEnabled); |
|
|
|
|
auto* modes = new QHBoxLayout(); |
|
|
|
|
m_camera = new QComboBox(this); |
|
|
|
|
for (const auto& camera : CallVideoEngine::cameras()) m_camera->addItem(camera.first, camera.second); |
|
|
|
|
m_resolution = new QComboBox(this); m_resolution->addItem("640×480", QSize(640, 480)); m_resolution->addItem("1280×720", QSize(1280, 720)); |
|
|
|
|
m_codec = new QComboBox(this); m_codec->addItem(tr("Auto (H.265 → H.264)"), 0); m_codec->addItem("H.265", 2); m_codec->addItem("H.264", 1); |
|
|
|
|
modes->addWidget(m_camera, 1); modes->addWidget(m_resolution); modes->addWidget(m_codec); videoLayout->addLayout(modes); |
|
|
|
|
auto* rates = new QHBoxLayout(); rates->addWidget(new QLabel(tr("Bitrate range, kbit/s:"), this)); |
|
|
|
|
m_videoMin = new QSpinBox(this); m_videoMax = new QSpinBox(this); |
|
|
|
|
for (auto* spin : {m_videoMin, m_videoMax}) { spin->setRange(300, 4000); spin->setSingleStep(100); rates->addWidget(spin); } |
|
|
|
|
m_videoMin->setValue(300); m_videoMax->setValue(4000); videoLayout->addLayout(rates); lay->addWidget(m_videoControls); |
|
|
|
|
connect(m_videoEnabled, &QCheckBox::toggled, this, [this] { applyVideo(); }); |
|
|
|
|
for (auto* combo : {m_camera, m_resolution, m_codec}) |
|
|
|
|
connect(combo, QOverload<int>::of(&QComboBox::activated), this, [this] { applyVideo(); }); |
|
|
|
|
auto updateRange = [this] { |
|
|
|
|
if (m_videoMin->value() > m_videoMax->value()) m_videoMax->setValue(m_videoMin->value()); |
|
|
|
|
if (m_videoCall) call_video_port_range(m_videoCall, m_videoMin->value() * 1000, m_videoMax->value() * 1000); |
|
|
|
|
}; |
|
|
|
|
connect(m_videoMin, &QSpinBox::editingFinished, this, updateRange); |
|
|
|
|
connect(m_videoMax, &QSpinBox::editingFinished, this, updateRange); |
|
|
|
|
connect(m_video, &CallVideoEngine::statusChanged, m_videoStatus, &QLabel::setText); |
|
|
|
|
connect(m_video, &CallVideoEngine::imageChanged, this, [this](const QImage& image) { |
|
|
|
|
m_videoImage->setPixmap(image.isNull() ? QPixmap() : QPixmap::fromImage(image).scaled(m_videoImage->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); |
|
|
|
|
}); |
|
|
|
|
auto* videoTimer = new QTimer(this); videoTimer->setInterval(100); |
|
|
|
|
connect(videoTimer, &QTimer::timeout, this, [this] { |
|
|
|
|
if (m_state != Active || !CallAudioEngine::instance()->isActive() || CallAudioEngine::instance()->callId() != m_callId) return; |
|
|
|
|
if (m_videoCall != m_callId) { m_videoCall = m_callId; m_videoRangeReady = false; m_video->start(m_callId); applyVideo(); } |
|
|
|
|
struct call_video_port_status status{}; |
|
|
|
|
if (!m_videoRangeReady && call_video_port_status(m_callId, &status) == 0 && status.minimum_bps) { |
|
|
|
|
m_videoMin->setValue(status.minimum_bps / 1000); m_videoMax->setValue(status.maximum_bps / 1000); m_videoRangeReady = true; |
|
|
|
|
} |
|
|
|
|
}); videoTimer->start(); |
|
|
|
|
lay->addStretch(1); |
|
|
|
|
|
|
|
|
|
m_acceptCell = wrapWithCaption(m_acceptBtn, QCoreApplication::translate("ChatGui", "Accept")); |
|
|
|
|
@ -269,6 +313,7 @@ void CallWindow::setState(State s) {
|
|
|
|
|
m_muteBtn->setChecked(false); |
|
|
|
|
break; |
|
|
|
|
case Ended: |
|
|
|
|
m_video->stop(); m_videoCall = 0; |
|
|
|
|
setConnectionType(0, 0); |
|
|
|
|
m_statusLabel->setText(QCoreApplication::translate("ChatGui", "Ended")); |
|
|
|
|
m_durationTimer->stop(); |
|
|
|
|
@ -300,6 +345,15 @@ void CallWindow::updateButtons() {
|
|
|
|
|
m_hangupCell->setVisible(m_state == Outgoing || m_state == Ringing || m_state == Active); |
|
|
|
|
m_muteCell->setVisible(m_state == Active); |
|
|
|
|
m_deviceCombo->setVisible(m_state == Active); |
|
|
|
|
m_videoControls->setVisible(m_state == Active); |
|
|
|
|
m_videoImage->setVisible(m_state == Active); |
|
|
|
|
m_videoStatus->setVisible(m_state == Active); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void CallWindow::applyVideo() { |
|
|
|
|
if (!m_videoCall) return; |
|
|
|
|
QSize size = m_resolution->currentData().toSize(); |
|
|
|
|
m_video->setVideo(m_videoEnabled->isChecked(), m_camera->currentData().toByteArray(), size.width(), size.height(), m_codec->currentData().toInt()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void CallWindow::postCallFn(void (*fn)(void*), uint64_t callId) { |
|
|
|
|
@ -338,6 +392,7 @@ void CallWindow::onDeviceChanged(int index) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void CallWindow::closeEvent(QCloseEvent* event) { |
|
|
|
|
m_video->stop(); m_videoCall = 0; |
|
|
|
|
if (m_state == Outgoing || m_state == Ringing || m_state == Active) |
|
|
|
|
postCallFn(call_hangup_trampoline, m_callId); |
|
|
|
|
emit closed(m_callId); |
|
|
|
|
|