首页| 论坛| 消息
主题:Qt实现外网双向音视频通话/支持嵌入式板子/实时性好延迟低/可以加水印
liudianwu发表于 2025-07-07 09:19
## 一、前言说明
之前已经在推流基础上,实现了音视频通话,原理就是采集到音视频的数据推流到流媒体服务程序,然后从流媒体服务程序拉流播放对方的音视频流即可,目前演示效果是做的一对一通话,稍微拓展一丢丢就可以支持多对多通话。之前演示的都是在局域网,很多人问是否可以外网通话,那肯定可以的啊,只需要搞个云服务器将流媒体程序放上面即可,需要他进行中转,那有些菜鸡又要问,能不能不要中转直接就能外网通信呢,答案是否定的,用屁股想想也知道,局域网的设备,没有固定的公网ip,对方要和你通信,连IP地址都不知道,怎么通信,无论如何都需要外网进行中转的,哪怕是握手打洞的中转。别说什么花生壳绑定公网地址啥的,非常鸡肋几乎不好用,还需要路由器设备支持。
实现外网音视频通话,只需要在系统设置那边,将推流和拉流地址,对应的IP都换成云服务器的地址即可,也就是通话方A的音视频采集的流,会推送到服务器,通话B的采集的流也是推送到服务器,然后双方都从云服务器拉取各自需要的流即可。当然也可以加上权限验证的一系列自己的逻辑处理,避免串流。总体用下来效果还是非常不错的,唯一要注意的是网络带宽,如果双方都是1080P的摄像头,码流一般在2-4MB,意外着你的云服务器的网络带宽至少4MB,留一点冗余的话至少要5MB,主要是视频占用带宽,音频数据几乎可以忽略。如果带宽太小无法改变,那只能降低摄像头的分辨率,一般采集的时候可以设置按照多大的分辨率来采集。
## 二、效果图

## 三、相关代码
```cpp
#include "frmconfig.h"
#include "frmmain.h"
#include "ui_frmmain.h"
#include "qthelper.h"
#include "apphelper.h"
#include "osdgraph.h"
#include "ffmpegthread.h"
#include "ffmpegthreadcheck.h"
frmMain::frmMain(QWidget *parent) : QWidget(parent), ui(new Ui::frmMain)
{
ui->setupUi(this);
this->initForm();
this->installEventFilter(this);
QMetaObject::invokeMethod(this, "formChanged", Qt::QueuedConnection);
}
frmMain::~frmMain()
{
delete ui;
}
void frmMain::savePos()
{
AppConfig::FormMax = this->isMaximized();
if (!AppConfig::FormMax) {
AppConfig::FormGeometry = this->geometry();
}
AppConfig::writeConfig();
}
bool frmMain::eventFilter(QObject *watched, QEvent *event)
{
//尺寸发生变化或者窗体移动位置记住窗体位置
int type = event->type();
if (type == QEvent::Resize || type == QEvent::Move) {
QMetaObject::invokeMethod(this, "savePos", Qt::QueuedConnection);
} else if (type == QEvent::Close) {
audioInput->stop(false);
audioOutput->stop(false);
exit(0);
}
//尺寸发生变化重新调整小预览窗体的位置
if (this->isVisible() && type == QEvent::Resize) {
this->formChanged();
}
return QWidget::eventFilter(watched, event);
}
void frmMain::initForm()
{
//初始化输入输出视频控件
int decodeType = AppConfig::DecodeType;
AppHelper::initVideoWidget(ui->videoInput, decodeType);
AppHelper::initVideoWidget(ui->videoOutput, decodeType);
//初始化输入输出音频线程
audioInput = new FFmpegThread(this);
audioOutput = new FFmpegThread(this);
checkInput = new FFmpegThreadCheck(audioInput, this);
AppHelper::initAudioThread(audioInput, ui->levelInput, decodeType);
AppHelper::initAudioThread(audioOutput, ui->levelOutput, decodeType);
//输入打开成功后立即推流
connect(audioInput, SIGNAL(receivePlayStart(int)), this, SLOT(receivePlayStart(int)));
connect(ui->videoInput, SIGNAL(sig_receivePlayStart(int)), this, SLOT(receivePlayStart(int)));
ui->ckInput->setChecked(AppConfig::MuteInput ? Qt::Checked : Qt::Unchecked);
ui->ckOutput->setChecked(AppConfig::MuteOutput ? Qt::Checked : Qt::Unchecked);
if (AppConfig::StartServer) {
on_btnStart_clicked();
}
//打印完整地址
FFmpegThread::debugInfo = 1;
}
void frmMain::clearLevel()
{
ui->levelInput->setLevel(0);
ui->levelOutput->setLevel(0);
}
void frmMain::formChanged()
{
AppHelper::changeWidget(ui->videoInput, ui->videoOutput, ui->gridLayout, NULL);
}
void frmMain::receivePlayStart(int time)<

浏览大图
下一页 (1/4)
回帖(0):

全部回帖(0)»
最新回帖
收藏本帖
发新帖