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.rhkb.cn/news/246916.html

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

相关文章

Qt/QML编程之路:ListView实现横排图片列表的示例(40)

ListView列表,在QML中使用非常多,排列一个行,一个列或者一个表格,都会用到ListView。 ListView显示从内置QML类型(如ListModel和XmlListModel)创建的模型中的数据,或在C++中定义的从QAbstractItemModel或QAbstract ListModel继承的自定义模型类中的数据。 ListView有一…

[GYCTF2020]Ezsqli1

打开环境&#xff0c;下面有个提交表单 提交1&#xff0c;2有正确的查询结果&#xff0c;3以后都显示Error Occured When Fetch Result. 题目是sql&#xff0c;应该考察的是sql注入 简单fuzz一下 发现information_schema被过滤了&#xff0c;猜测是盲注了。 测试发现只要有东…

(七)for循环控制

文章目录 用法while的用法for的用法两者之间的联系可以相互等价用for改写while示例for和while的死循环怎么写for循环见怪不怪表达式1省略第一.三个表达式省略&#xff08;for 改 while&#xff09;全省略即死循环&#xff08;上面已介绍&#xff09; 用法 类比学习while语句 …

mac配置L2TP连接公司内网

1. 打开系统设置 2. 打开网络 3. 点击网络页面其他服务右下角三个点&#xff0c;添加VPN配置中的L2TP 4. 配置VPN&#xff0c;服务器填写公司的服务器ip&#xff0c;共享密钥没有可以随便填写 5. 打开终端编辑文件 sudo vim /etc/ppp/opt…

华为机考入门python3--(4)牛客4-字符串分隔

分类&#xff1a;字符串 知识点&#xff1a; 复制符号* 复制3个0 0*3 000 字符串截取 截取第i位到j-1位 str[i:j] 题目来自【牛客】 input_str input().strip()# 先补齐 if len(input_str) % 8 ! 0: input_str 0 * (8 - len(input_str) % 8) # 每8个分 out…

哪些 3D 建模软件值得推荐?

云端地球是一款免费的在线实景三维建模软件&#xff0c;不需要复杂的技巧&#xff0c;只要需要手机&#xff0c;多拍几张照片&#xff0c;就可以得到完整的三维模型&#xff01; 无论是大场景倾斜摄影测量还是小场景、小物体建模&#xff0c;都可以通过云端地球将二维数据向三…

matplotlib实现动画效果

实现正弦波动画 import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation import numpy as np# 创建图像和轴 fig, ax plt.subplots()# 生成平均分布在0~2*pi之间的100个坐标点 x_data np.linspace(0, 2 * np.pi, 100) # 画出初始图 line, ax.plo…

【鸿蒙】大模型对话应用(一):大模型接口对接与调试

Demo介绍 本demo对接阿里云和百度的大模型API&#xff0c;实现一个简单的对话应用。 DecEco Studio版本&#xff1a;DevEco Studio 3.1.1 Release HarmonyOS API版本&#xff1a;API9 关键点&#xff1a;ArkTS、ArkUI、UIAbility、网络http请求、列表布局 官方接口文档 此…

21.Arrays类

Arrays类 1. 概述2. 常见方法3. sort 方法的自定义排序4. 代码示例5. 输出结果6. 注意事项 具体信息请查看 API 帮助文档 1. 概述 Arrays类是Java中的一个工具类&#xff0c;位于java.util包中。 它提供了一组静态方法&#xff0c;用于操作数组。通过Arrays类&#xff0c;我们…

台式电脑的ip地址在哪里找

在网络连接方面&#xff0c;IP地址是非常重要的信息&#xff0c;它是用于标识网络设备的唯一地址。对于台式电脑用户来说&#xff0c;了解自己设备的IP地址是非常有必要的&#xff0c;因为它可以帮助解决网络连接问题&#xff0c;进行远程访问和共享文件等功能。本文将指导读者…

Redis(秒杀活动、持久化之RDB、AOF)

目录 秒杀活动 一、测压工具jmete的使用 二、java实现秒杀活动 1、myseckillcontroller 2、先启动pos请求添加商品&#xff0c;再启动jmeter进行压测 Redis持久化 一 、Redis持久化之RDB 1.RDB是什么 2. 备份是如何执行的 3.Fork 4. RDB持久化流程 5. dump.rdb文件 6…

想要透明拼接屏展现更加效果,视频源是技术活,尤其作为直播背景

随着科技的飞速发展&#xff0c;视频制作和显示技术也在不断进步。透明拼接屏视频作为一种新型的视频形式&#xff0c;在许多场合都得到了广泛的应用。尼伽小编将深入探讨透明拼接屏视频的制作过程、要求、清晰度&#xff0c;以及目前常作为直播背景的优势。 一、透明拼接屏视频…

【数据结构1-2】二叉树

树形结构不仅能表示数据间的指向关系&#xff0c;还能表示出数据的层次关系&#xff0c;而有很明显的递归性质。因此&#xff0c;我们可以利用树的性质解决更多种类的问题。 但是在平常的使用中&#xff0c;我们并不需要使用这么复杂的结构&#xff0c;只需要建立一个包含int r…

Vue深入学习4—指令和生命周期

1.Vue是怎么识别 v- 指令的&#xff1f; 首先将HTML结构解析成属性列表&#xff0c;存入到数组中&#xff0c;接着遍历数组中的每一个节点&#xff0c;获取到不同指令对应的方法。 // 将HTML看作真正的属性列表 var ndoeAttrs node.attributes; var self this; // 类数组对象…

modbus poll测试工具测试modbus tcp与PLC设备连接使用方法

socket默认端口是502&#xff0c;socket连上之后&#xff0c; 按照modbuspoll工具设置的读写参数 生成的RTU命令格式去组装读PLC的设备数据 modbuspoll工具配置&#xff0c;以v9.9.2中文破解版为例&#xff1a; 首先点连接菜单&#xff08;connection&#xff09;建立连接&…

C#学习(十一)——Array和Collection

一、集合 集合重要且常用 孤立的数据是没有意义的&#xff0c;集合可以作为大量数据的处理&#xff0c;可进行数据的搜索、迭代、添加、删除。 C#中&#xff0c;所有集合都必须实现ICollection接口&#xff08;数组Array除外&#xff09; 集合说明Array数组&#xff0c;固定长…

消息中间件之八股面试回答篇:三、RabbitMQ如何解决消息堆积问题(100万条消息堆积)+RabbitMQ高可用性和强一致性机制+回答模板

RabbitMQ中的消息堆积问题 当生产者发送消息的速度超过了消费者处理消息的速度&#xff0c;就会导致队列中的消息堆积&#xff0c;直到队列存储消息达到上限。之后发送的消息就会成为死信&#xff0c;可能会被丢弃&#xff0c;这就是消息堆积问题。 解决消息堆积有三种种思路…

【Java Kubernates】Java调用kubernates提交Yaml到SparkOperator

背景 目前查询框架使用的是trino&#xff0c;但是trino也有其局限性&#xff0c;需要准备一个备用的查询框架。考虑使用spark&#xff0c;spark operator也已经部署到k8s&#xff0c;现在需要定向提交spark sql到k8s的sparkoperator上&#xff0c;使用k8s资源执行sql。 对比 …

LeetCode 40.组合总和 II

组合总和 II 给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用 一次 。 注意&#xff1a;解集不能包含重复的组合。 方法一、回溯 由于题目要求解集…

C语言第十一弹---函数(下)

​ ✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 函数 1、嵌套调用和链式访问 1.1、嵌套调用 1.2、链式访问 2、函数的声明和定义 2.1、单个文件 2.2、多个文件 2.3、static 和 extern 2.3.1、static…