**
1 QT下视频通话的实现
**
本文使用QT完成了两个不同终端的视频通话,笔记本电脑+Linux开发板。
1.1 硬件资源介绍
带摄像头的电脑 + 正点原子Alpha Linux开发板(由于Linux开发板上没有购买摄像头,所以本次实现仅仅单向的视频通话)
1.2 软件编程实现
1.2.1 电脑端QT编程
电脑端搭建一个TCP服务器,开启一个视频捕获线程,使用QCamera、QCameraViewfinder、QCameraImageCapture、QTimer,定时捕获视频中的图片,并传送给主线程使用TCP发给客户端。下面是核心的代码实现
(1)捕获电脑的视频,这里是捕获视频的核心代码,实现的大致思路是,开启一个QCaerma,通过主线程的信号槽开启,定时VIDEO_SAMPLE_INTERVAL时间截取一张图片,此时会自动触发SIGNAL(imageCaptured(int,QImage))这样一个信号,在主线程中编写槽函数接收捕获到的QImage。这个cpp参考网上一位老哥的编写,将QVideoProbe换成了QCameraImageCapture来实现。https://blog.csdn.net/xiaolong1126626497/article/details/105122636
void VideoReadThread::Camear_Init(void){
qDebug() << "Camear_Init";
// 初始化定时器
ImageCaptureTimer = new QTimer(this);
connect(ImageCaptureTimer,SIGNAL(timeout()),this,SLOT(captureImage()));
// currentCaerma 为当前选择的摄像头
camera = new QCamera(qtUtil.camera);
//设置取景器
viewfinder = qtUtil.viewfinder;
imageCapture = new QCameraImageCapture(camera);
camera->setViewfinder(viewfinder);
//配置 Camera 为静止帧捕获
camera->setCaptureMode(QCamera::CaptureStillImage);
//启动摄像头
camera->start();
//配置摄像头参数
QList ViewSets = camera->supportedViewfinderSettings();
int i = 0;
qDebug() << "viewfinderResolutions sizes.len = " << ViewSets.length();
foreach (QCameraViewfinderSettings ViewSet, ViewSets) {
qDebug() << i++ <
"Format="<
}
//可以打印出来自己电脑端的settings,进行选择
camera->setViewfinderSettings(ViewSets[9]);
//开启定时器
ImageCaptureTimer->start(VIDEO_SAMPLE_INTERVAL);
connect(imageCapture, SIGNAL(imageCaptured(int,QImage)), this, SLOT(displayImage(int,QImage)));
}
void VideoReadThread::captureImage()
{
imageCapture->capture();
}
(2)使用TCP传输图片,参考飞扬青云的帖子https://www.cnblogs.com/feiyangqingyun/p/12041561.html 使用base64编码传输,接收到以后将base64字符串解码出来生成图片。
// 获取图片并转为base64字符串
QByteArray Widget::getImageData2(const QImage &image)
{
QByteArray imageData;
QBuffer buffer(&imageData);
image.save(&buffer, "jpg");
imageData = imageData.toBase64();
return imageData;
}
//base64字符串转图片
QImage Widget::getImage(const QString &data)
{
QByteArray imageData = QByteArray::fromBase64(data.toLatin1());
QImage image;
image.loadFromData(imageData);
return image;
}
(3)TCP传输过程中粘包,拆包处理,服务器端规定协议,增加数据包的开头和结尾,观察图片转换正的Base64码中发现没有@ 和 # ,因此使用这两个当做一包数据的头和尾。栽面是服务器端封包的代码。
//封装发送图片的数据包
// startFlag: @ endFlag:#
QByteArray Widget::packageImageData(QImage image)
{
QByteArray resultData;
imageData = getImageData2(image);
resultData.append(startFlag).append(imageData).append(endFlag);
qDebug()<
// qDebug()<
return resultData;
}
1.2.2 Linux开发板QT编程
使用正电原子的Linux开发板,使用之前先配置好开发板上QT的运行环境,参考正点原子文档。开发板中主要是实现了数据包的解析,代码如下:
```css
/*解析数据*/
void Widget::parseImageData(QByteArray receiveData)
{
QByteArray bufData = receiveData;
static QByteArray pastData;
static QString resultStr;
if(bufData.contains("@")&(!bufData.contains("#")))
{
pastData.clear();
pastData.append(bufData);
}
//无头无尾中部数据 直接追加
if((!bufData.contains("@"))&(!bufData.contains("#"))&(!pastData.isEmpty()))
{
pastData.append(bufData);
}
//无头有尾 如果数据之前不为空则继续追加 已经读取完成 清空变量
if((!bufData.contains("@"))&bufData.contains("#")&(!pastData.isEmpty()))
{
pastData.append(bufData);
resultStr = tr(pastData);
pastData.clear();
}
//有头有尾 清空之前数据直接追加
if(bufData.contains("@")&bufData.contains("#"))
{
pastData.clear();
pastData.append(bufData);
resultStr = tr(pastData);
pastData.clear();
}
if(!resultStr.isEmpty())
{
resultStr.remove("@");
resultStr.remove("#");
emit sendImageDataBase64(resultStr.toUtf8());
// qDebug()<
resultStr.clear();
}
}
解析后的数据,通过一个信号传递给处理图片的槽函数,将编码转换为Qimage,显示在Qlabel上。
1.3 实验效果
我设置的定时器间隔为100ms,视频传输感觉较为流畅。电脑端,布局比较乱
Linux 开发板