Add live call video controls and display to the desktop call window
This commit is contained in:
1 parent
e682f30ca9
commit
af35d03399
3 files changed
+70
-3
No files matched your search
@@ -139,7 +139,7 @@ if(PkgConfig_FOUND)
|
||||
endif()
|
||||
|
||||
# Общий sender остаётся в libutun; Qt захватывает видео, FFmpeg/Opus работают в publisher workers.
|
||||
add_library(chatgui_live_video STATIC src/live_video_encoder.cpp src/call_video_encoder.cpp)
|
||||
add_library(chatgui_live_video STATIC src/live_video_encoder.cpp src/call_video_encoder.cpp src/call_video_decoder.cpp)
|
||||
target_link_libraries(chatgui_live_video PUBLIC ${QT_LIBS} utun PRIVATE ${FFMPEG_TARGET} ${FFMPEG_LIBRARIES})
|
||||
target_include_directories(chatgui_live_video PRIVATE ${FFMPEG_INCLUDE_DIRS})
|
||||
if(FFMPEG_FOUND)
|
||||
@@ -150,7 +150,7 @@ if(FFMPEG_FOUND)
|
||||
add_test(NAME test_call_video_encoder COMMAND test_call_video_encoder)
|
||||
set_tests_properties(test_call_video_encoder PROPERTIES TIMEOUT 120)
|
||||
endif()
|
||||
add_library(chatgui_stream_publish STATIC src/stream_publish_engine.cpp src/stream_publish_window.cpp)
|
||||
add_library(chatgui_stream_publish STATIC src/stream_publish_engine.cpp src/stream_publish_window.cpp src/call_video_engine.cpp)
|
||||
target_link_libraries(chatgui_stream_publish PUBLIC chatgui_live_video)
|
||||
target_include_directories(chatgui_stream_publish PRIVATE ${CMAKE_SOURCE_DIR}/../../lib/libopus/include)
|
||||
set(GUI_CAPTURE_ENABLED 0)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -7,6 +7,9 @@ class QLabel;
|
||||
class QPushButton;
|
||||
class QComboBox;
|
||||
class QTimer;
|
||||
class QCheckBox;
|
||||
class QSpinBox;
|
||||
class CallVideoEngine;
|
||||
|
||||
/* Окно звонка: состояния Outgoing (пытаемся связаться), Ringing (звоним),
|
||||
* Incoming (принять/отклонить), Active (идёт разговор, таймер), Ended (завершён). */
|
||||
@@ -46,6 +49,15 @@ private:
|
||||
void updateButtons();
|
||||
void populateDeviceCombo();
|
||||
void postCallFn(void (*fn)(void*), uint64_t callId);
|
||||
void applyVideo();
|
||||
CallVideoEngine* m_video;
|
||||
QCheckBox* m_videoEnabled;
|
||||
QComboBox *m_camera, *m_resolution, *m_codec;
|
||||
QSpinBox *m_videoMin, *m_videoMax;
|
||||
QLabel *m_videoImage, *m_videoStatus;
|
||||
QWidget* m_videoControls;
|
||||
quint64 m_videoCall = 0;
|
||||
bool m_videoRangeReady = false;
|
||||
|
||||
quint64 m_callId = 0;
|
||||
State m_state = Outgoing;
|
||||
|
||||
Reference in new issue
Block a user