RTP工具改进(五)--使用qt

前篇

第四篇
RTP工具改进(四) - rtmp协议推送

前面使用的工具一直为mfc,今天将使用qt 来做界面,使用qt 来进行程序和协议的编写,qt部分目前还不包括rtp ps流和rtmp,暂时只有rtp 直接传输,关于rtmp协议和ps流协议,先使用vs的mfc。增加和改变的模块为rtp,和 rtp_recv,如下图,以前的vs MFC版本都放到vs下面,有关于qt的 gb28181 的sip server 和 rtp 发送接收等都放到qt下面,所有可执行都放到外层的bin下面
在这里插入图片描述

代码地址
https://gitee.com/guanzhi0319/rtp

QT 加入

除了gb28181 的可视化界面,增加了两个程序,一个qt_rtp,一个qt_rtp_recv,打开后如下所示
在这里插入图片描述

2.1 发送端

在这里插入图片描述
目前制作还是比较简陋,先以能执行为主,使用qt 5.14,mingw,不依赖于vs,所以读者可以不安装vs就可以使用该代码,首先制作一个CameraVideoSurface类,用来读摄像头

#include "c_cameravideo.h"
#include <QDebug>
//c_cameravideo::c_cameravideo(QObject * parent)
//{//}
CameraVideoSurface::CameraVideoSurface(QObject *parent): QAbstractVideoSurface(parent)
{//this->InitEncoder();
}CameraVideoSurface::~CameraVideoSurface()
{
//    avformat_close_input(&pOutputFormatCtx);
//    av_frame_free(&yuvFrame);
//    av_packet_free(&packet);
//    avcodec_close(pCodecCtx);
}void CameraVideoSurface::setCameraResolution(const QSize &size)
{this->setNativeResolution(size);
}
QList<QVideoFrame::PixelFormat> CameraVideoSurface::supportedPixelFormats
(QAbstractVideoBuffer::HandleType handleType) const
{QList<QVideoFrame::PixelFormat > pixelFormats;pixelFormats.append(QVideoFrame::Format_BGR24);pixelFormats.append(QVideoFrame::Format_RGB32);pixelFormats.append(QVideoFrame::Format_YUV420P);return pixelFormats;
}bool CameraVideoSurface::present(const QVideoFrame &frame)
{if (frame.isValid()){QVideoFrame cloneFrame(frame);cloneFrame.map(QAbstractVideoBuffer::ReadOnly);QImage image(cloneFrame.bits(), cloneFrame.width(), cloneFrame.height(),QVideoFrame::imageFormatFromPixelFormat(frame.pixelFormat()));
//        QImage image2(cloneFrame.bits(), cloneFrame.width(), cloneFrame.height(),
//                     QVideoFrame::imageFormatFromPixelFormat(QVideoFrame::Format_BGR24));image = image.mirrored(true, true);// rgb 转 yuv
//        uint8_t *data[AV_NUM_DATA_POINTERS] = {0};
//        data[0] = (uint8_t *)image.constBits();
//        int linesize[AV_NUM_DATA_POINTERS] = {0};
//        linesize[0] = pCodecCtx->width * 4;
//        sws_scale(image_convert_ctx, data, linesize, 0, pCodecCtx->height,
//                  yuvFrame->data, yuvFrame->linesize);
//        // 编码
//        this->Encode(yuvFrame);emit showFrame(image);cloneFrame.unmap();return true;}return false;
}#if 0
void CameraVideoSurface::InitEncoder()
{//av_register_all();avformat_network_init();avcodec_register_all();QString outputFileName = "output.h264";QString encoderName = "libx264";//QString rtmpAddress = "rtmp://192.168.1.111/live/livestream";pCodec = avcodec_find_encoder_by_name(encoderName.toStdString().c_str());if(NULL == pCodec){qDebug() <<"查找视频编码器失败!";return;}pCodecCtx = avcodec_alloc_context3(pCodec);if(NULL == pCodecCtx){qDebug() <<"开辟编解码器上下文";return;}// 输入样本参数pCodecCtx->bit_rate = 400000;pCodecCtx->width = 1280;pCodecCtx->height = 720;pCodecCtx->time_base = {1, 25};pCodecCtx->framerate = {25, 1};pCodecCtx->gop_size = 10;pCodecCtx->max_b_frames = 1;pCodecCtx->qmin = 10;pCodecCtx->qmax = 51;pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;if(AV_CODEC_ID_H264 == pCodecCtx->codec_id){av_opt_set(pCodecCtx->priv_data, "preset", "slow", 0);av_opt_set(pCodecCtx->priv_data, "tune", "zerolatency", 0);}// 打开编码器if(avcodec_open2(pCodecCtx, pCodec, NULL) < 0){qDebug() <<"打开编码器失败 !";return;}pOutputFormatCtx = avformat_alloc_context();if(NULL == pOutputFormatCtx){qDebug() <<"视频封装器开辟失败!";return;}AVOutputFormat *outputFormat = av_guess_format(NULL, outputFileName.toStdString().c_str(), NULL);if(NULL == outputFormat){qDebug() <<"猜测outputformat失败 !";return;}pOutputFormatCtx->oformat = outputFormat;// oprn urlif(avio_open(&pOutputFormatCtx->pb, outputFileName.toStdString().c_str(), AVIO_FLAG_READ_WRITE) < 0){qDebug() <<"打开输出文件失败!";return;}pOutputStream = avformat_new_stream(pOutputFormatCtx, NULL);if(NULL == pOutputStream){qDebug() <<"新建输出流失败 !";return;}// 输出详细信息av_dump_format(pOutputFormatCtx, 0, outputFileName.toStdString().c_str(), 1);// 新建数据包packet = av_packet_alloc();if(NULL == packet){qDebug() <<"新建数据包失败 !";return;}// yuvFrame 初始化yuvFrame = av_frame_alloc();if(NULL == yuvFrame){qDebug() <<"开辟AVFrame失败 !";return;}yuvFrame->width = pCodecCtx->width;yuvFrame->height = pCodecCtx->height;yuvFrame->format = pCodecCtx->pix_fmt;// 初始化 image 空间av_image_alloc(yuvFrame->data, yuvFrame->linesize, yuvFrame->width, yuvFrame->height,pCodecCtx->pix_fmt, 32);// 转换上下文image_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB32, pCodecCtx->width,pCodecCtx->height, AV_PIX_FMT_YUV420P,SWS_BICUBIC, NULL, NULL, NULL);if(NULL == image_convert_ctx){qDebug() <<"转换上下文失败 !";return;}// 写封装头if(avformat_write_header(pOutputFormatCtx, NULL) < 0){qDebug() <<"视频封装头写失败 !";return;}
}// 编码为 h.264
void CameraVideoSurface::Encode(AVFrame *frame)
{static int index = 0;frame->pts = index++;int ret = 0;if((ret = avcodec_send_frame(pCodecCtx, frame)) < 0){qDebug() <<"avcodec_send_frame 失败 !";return;}while(ret >= 0){ret = avcodec_receive_packet(pCodecCtx, packet);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){return;}else if (ret < 0){qDebug() <<  "编码时出错";return;}packet->stream_index = 0;av_interleaved_write_frame(pOutputFormatCtx, packet); // write frameav_packet_unref(packet);}
}
#endif
void CameraVideoSurface::cameraStopSlot()
{qDebug()<<"关闭close";// av_write_trailer(pOutputFormatCtx);
}

然后就需要使用udp进行发送,使用 QUdpSocket 类来发送,把c_rtp类从QObject类去继承

class c_rtp:public QObject
{Q_OBJECT
private:QUdpSocket *v_udpSocket = NULL;QHostAddress v_host;//QString  v_host_str;quint16 v_port;
private:unsigned short _seq_num = 0 ;unsigned char sendbuf[MAX_LENGTH];unsigned int v_ssrc = 1001;//the default value must hash("live/1001")public:c_rtp(){}~c_rtp(){if(v_udpSocket!=NULL){v_udpSocket->abort();delete v_udpSocket;}}void func_init(const char *ip,uint16_t port);int  func_send_video(uint8_t * data, int len);//发送ps流int  func_send_video_ps(uint8_t* data, int len);};

2.2 接收端

接收端主要使用IO_Thread 来接收包,拼接包,也就是意味着可以有多个接收,同时显示
在这里插入图片描述

class MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void on_pushButton_clicked();private://ssrc-->s_rtp_contextstd::unordered_map<uint32_t, IO_Thread*> v_ctxs;Ui::MainWindow *ui;QUdpSocket *v_udpSocket = NULL;//IO_Thread* v_iothd = NULL;IO_Thread * getctx(uint32_t ssrc);QTimer* v_timer = NULL;//int v_id1; //定时器1的唯一标示
};

实现使用QUdpSocket 去产生一个服务端来接收数据,根据不同的ssrc来分发,显示在不同的QLabel组件上

#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);ui->iptext->setText("127.0.0.1");ui->porttext->setText("6000");v_udpSocket = new QUdpSocket(this);//v_udpSocket->bind(QHostAddress::LocalHost, 6000);v_udpSocket->bind(6000);//收包程序connect(v_udpSocket,&QUdpSocket::readyRead,[=](){// 获取报文长度大小qint64 size = v_udpSocket->pendingDatagramSize();// 读取报文QByteArray array = QByteArray(size,0);v_udpSocket->readDatagram(array.data(),size);uint8_t* data = (uint8_t*)array.data();int inlen = (int)size;int outlen =0;uint32_t last_ts,ssrc;uint16_t seq;uint8_t payloadtype;uint8_t *buffer = rtp_payload(data, inlen, &outlen, last_ts, ssrc,seq, payloadtype);IO_Thread* ctx = getctx(ssrc);ctx->v_last_ts = last_ts;ctx->v_seq = seq;ctx->v_payloadtype = payloadtype;//ctx->v_ssrc = ssrc;live_rtp_unpack(data,ctx,buffer,outlen);});v_timer = new QTimer(this);//启动定时器v_timer->start(40);connect(v_timer,&QTimer::timeout,[=](){//static int num = 1;auto iter = v_ctxs.begin();if(iter!= v_ctxs.end()){IO_Thread* ctx = iter->second;ctx->LockBuffer();//显示uint8_t* rgbBuffer = ctx->out_buffer_out;int w = ctx->m_w;int h = ctx->m_h;if(rgbBuffer!=NULL && w>0 && h >0){int iw = ui->label_showv->width();int ih = ui->label_showv->height();QImage tmpImg(rgbBuffer,w,h,QImage::Format_RGB32);QImage imageScale = tmpImg.scaled(QSize(iw,ih));QImage image = imageScale.mirrored(true, false);QPixmap pixmap = QPixmap::fromImage(image);ui->label_showv->setPixmap(pixmap);}ctx->UnLockBuffer();}});}MainWindow::~MainWindow()
{delete ui;
}IO_Thread * MainWindow::getctx(uint32_t ssrc)
{auto it = v_ctxs.find(ssrc);if (it != v_ctxs.end())return it->second;else{IO_Thread *sc = new IO_Thread();v_ctxs[ssrc] = sc;sc->Start();return sc;}
}

后续

改进的地方为:
1 MFC 版本增加音频,包括rtp的音频和rtmp协议的音频
2 QT 版本修改为player,将会丰富QT版本
3 增加rtmp服务器,有想法的同志可以加入一起做。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/651847.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在 VUE 项目中,使用 Axios 请求数据时,提示跨域,该怎么解决?

在 VUE 项目开发时&#xff0c;遇到个问题&#xff0c;正常设置使用 Axios 库请求数据时&#xff0c;报错提示跨域问题。 那在生产坏境下&#xff0c;该去怎么解决呢&#xff1f; 其可以通过以下几种方式去尝试解决&#xff1a; 1、设置允许跨域请求的响应头 1.1 在响应头中…

STM32实现软件IIC协议操作OLED显示屏(2)

时间记录&#xff1a;2024/1/27 一、OLED相关介绍 &#xff08;1&#xff09;显示分辨率128*64点阵 &#xff08;2&#xff09;IIC作为从机的地址0x78 &#xff08;3&#xff09;操作步骤&#xff1a;主机先发送IIC起始信号S&#xff0c;然后发送OLED的地址0x78&#xff0c;然…

机器学习的数据库积累........

https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf1_detection_zoo.md ​​​​​​​ 另一个database:&#xff08;网址:Object Detection Made Easy with TensorFlow Hub: Tutorial&#xff09; Object Detection Made Easy with Ten…

VR拍摄+制作

1.VR制作需要的图片宽高是2:1&#xff0c;需要360✖️180的图片&#xff0c;拍摄设备主要有两种&#xff1a; 1&#xff09;通过鱼眼相机拍摄&#xff0c;拍摄一组图片&#xff0c;然后通过PTGui来合成(拍摄复杂) 2&#xff09;全景相机&#xff0c;一键拍摄直接就能合成需要的…

【动态规划】【逆向思考】【C++算法】960. 删列造序 III

作者推荐 【动态规划】【map】【C算法】1289. 下降路径最小和 II 本文涉及知识点 动态规划汇总 LeetCode960. 删列造序 III 给定由 n 个小写字母字符串组成的数组 strs &#xff0c;其中每个字符串长度相等。 选取一个删除索引序列&#xff0c;对于 strs 中的每个字符串&a…

群晖nas 中的 VideoStation 关于豆瓣刮刮和TheMovieDb无法链接问题

网上各种教学文档很多&#xff0c;但是都有各种的坑&#xff0c;这偏文章仅对坑进行修正&#xff0c;具体可以参考其他文章&#xff0c;不同点以此文章为准。 第一部分&#xff0c;豆瓣刮刮 是 VideoStation的最佳搭配&#xff0c;汉字匹配&#xff0c;速度肯定是TheMovieDb无…

贾玲新片刚刚发出紧急声明,是什么情况。

♥ 为方便您进行讨论和分享&#xff0c;同时也为能带给您不一样的参与感。请您在阅读本文之前&#xff0c;点击一下“关注”&#xff0c;非常感谢您的支持&#xff01; 文 |猴哥聊娱乐 编 辑|徐 婷 校 对|侯欢庭 1月22日&#xff0c;一则“多位明星参演的电影涉影视投资诈骗…

[晓理紫]每日论文分享(有中文摘要,源码或项目地址)--强化学习、模仿学习、机器人、开放词汇

专属领域论文订阅 关注{晓理紫|小李子}&#xff0c;每日更新论文&#xff0c;如感兴趣&#xff0c;请转发给有需要的同学&#xff0c;谢谢支持 如果你感觉对你有所帮助&#xff0c;请关注我&#xff0c;每日准时为你推送最新论文。 分类: 大语言模型LLM视觉模型VLM扩散模型视觉…

STM32 freertos 使用软件模拟串口uart

如题&#xff0c;为什么要这样做&#xff1f; 最近做的一个项目上使用了74HC595作为指示灯板使用&#xff1b; 这个灯板与驱动板是通过排线连接&#xff0c;排线约25cm长&#xff1b; 在实验室测试一切正常&#xff0c;发到客户手上使用就出现了某个LED跳动情况&#xff1b;…

GitHub 一周热点汇总第7期(2024/01/21-01/27)

GitHub一周热点汇总第7期 (2024/01/21-01/27) &#xff0c;梳理每周热门的GitHub项目&#xff0c;离春节越来越近了&#xff0c;不知道大家都买好回家的票没有&#xff0c;希望大家都能顺利买到票&#xff0c;一起来看看这周的项目吧。 #1 rustdesk 项目名称&#xff1a;rust…

解决Linux部署报错No main manifest attribute, in XXX.jar

这是我近期遇到的一个问题&#xff0c;报错原因就是没找到主类&#xff0c;首先你在你本地运行&#xff0c;本地运行ok的话&#xff0c;解压生成的jar包&#xff0c;里面有个META-INF文件&#xff0c;打开MANIFEST.MF文件&#xff0c;该文件是一个清单文件。该文件包含有关JAR文…

vulnhub靶场之Five86-2

一.环境搭建 1.靶场描述 Five86-2 is another purposely built vulnerable lab with the intent of gaining experience in the world of penetration testing. The ultimate goal of this challenge is to get root and to read the one and only flag. Linux skills and fa…

vusui css 使用,简单明了 适合后端人员 已解决

vusui-cssopen in new window 免除开发者繁复的手写 CSS 样式&#xff0c;让 WEB 前端开发更简单、灵活、便捷&#xff01;如果喜欢就点个 ★Staropen in new window 吧。 移动设备优先&#xff1a; vusui-css 包含了贯穿于整个库的移动设备优先的样式。浏览器支持&#xff1a…

三数之和----双指针

https://leetcode.cn/problems/3sum/description/?envType=study-plan-v2&envId=top-100-liked “三数之和”在某些人的口中被叫做“程序员之梦破碎的地方”。既然如此,这个题肯定是有难度的,尤其是其中的细节,很多,很细。 其中nums代表给定的数组,numsSize代表给定数…

GLog开源库使用

Glog地址&#xff1a;https://github.com/google/glog 官方文档&#xff1a;http://google-glog.googlecode.com/svn/trunk/doc/glog.html 1.利用CMake进行编译&#xff0c;生成VS解决方案 &#xff08;1&#xff09;在glog-master文件夹内新建一个build文件夹&#xff0c;用…

【JAVA】什么是自旋

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;JAVA ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 结语 我的其他博客 前言 在计算机科学的领域中&#xff0c;多线程和并发编程已成为处理复杂任务和提高系统性能的不可或缺的手段。…

【时间序列篇】基于LSTM的序列分类-Pytorch实现 part2 自有数据集构建

系列文章目录 【时间序列篇】基于LSTM的序列分类-Pytorch实现 part1 案例复现 【时间序列篇】基于LSTM的序列分类-Pytorch实现 part2 自有数据集构建 【时间序列篇】基于LSTM的序列分类-Pytorch实现 part3 化为己用 在一个人体姿态估计的任务中&#xff0c;需要用深度学习模型…

深度学习(6)--Keras项目详解

目录 一.项目介绍 二.项目流程详解 2.1.导入所需要的工具包 2.2.输入参数 2.3.获取图像路径并遍历读取数据 2.4.数据集的切分和标签转换 2.5.网络模型构建 2.6.绘制结果曲线并将结果保存到本地 三.完整代码 四.首次运行结果 五.学习率对结果的影响 六.Dropout操作…

一个基于electron自动化桌面应用-流程图构建

前期工作已搞定&#xff0c;现在可以搭建桌面应用了。这个阶段可以结合前面定义好的数据格式构建流程图。 模板 还是使用熟悉的技术栈vite react electron&#xff0c;模板 流程图 官方文档 自定义 节点样式 因为配置化的操作类型较多&#xff0c;因此可以利用自定义节…

【开源】基于JAVA语言的就医保险管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 科室档案模块2.2 医生档案模块2.3 预约挂号模块2.4 我的挂号模块 三、系统展示四、核心代码4.1 用户查询全部医生4.2 新增医生4.3 查询科室4.4 新增号源4.5 预约号源 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVue…