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 url
    if(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 frame
        av_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_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();

private:
    //ssrc-->s_rtp_context
    std::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.mfbz.cn/a/353474.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL--创建数据表(5)

创建 MySQL 数据表需要以下信息&#xff1a; 表名表字段名定义每个表字段的数据类型 语法 以下为创建 MySQL 数据表的 SQL 通用语法&#xff1a; CREATE TABLE table_name (column1 datatype,column2 datatype,... );参数说明&#xff1a; table_name 是你要创建的表的名称…

“海洋天堂——助成长计划”走进安徽省科学技术馆

为了助力困境儿童、青少年有效地参与社会生活&#xff0c;培养他们团队精神&#xff0c;引导他们掌握社会规则&#xff0c;增强自信&#xff0c;合肥市庐阳区为民社会工作服务中心于2024年1月24日上午&#xff0c;组织有四名老师带领18名困境儿童、青少年&#xff0c;通过徒步、…

使用PHP自定义一个加密算法,实现编码配合加密,将自己姓名的明文加密一下

<meta charset"UTF-8"> <?phpfunction customEncrypt($lin, $key mySecretKey){// 定义一个简单的替换规则$li array(L > M, I > Y, Y > O, A > N, E > Q, );$yan ;for($i 0; $i < strlen($lin); $i){$char $lin[$i];if(isset($li[…

Self-Attention 和 Multi-Head Attention 的区别——附最通俗理解!!

文章目录 前言 一、简要介绍 二、工作流程 三、两者对比 四、通俗理解 前言 随着Transformer模型的迅速普及&#xff0c;Self-Attention&#xff08;自注意力机制&#xff09;和Multi-Head Attention&#xff08;多头注意力机制&#xff09;成为了自然语言处理&#xff08;NLP…

sqli-labs靶场第七关

7、第七关 id1 --单引号报错,id1" --双引号不报错,可以判断是单引号闭合 id1) --也报错&#xff0c;尝试两个括号闭合&#xff0c;id1)) --不报错 接下来用脚本爆库 import stringimport requestsnumbers [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] letters2 list(string.ascii_…

1.【Vue3】前端开发引入、Vue 简介

1. 前端开发引入 1.1 前端开发前置知识 通过之前的学习&#xff0c;已经通过 SpringBoot 和一些三方技术完成了大事件项目的后端开发。接下来开始学习大事件项目的前端开发&#xff0c;前端部分借助两个框架实现&#xff1a; Vue3&#xff08;一个 JS 框架&#xff09;基于 …

一篇博客读懂排序

目录 一、常见的排序 二、冒泡排序 2.1基本思想&#xff1a; 2.2代码&#xff1a; 三、插入排序 3.1基本思想&#xff1a; 3.2思路讲解&#xff1a; 3.3代码&#xff1a; 3.4时间复杂度&#xff1a; 四、希尔排序 4.1基本思路&#xff1a; 4.2思路讲解&#xff1a;…

openssl3.2 - 测试程序的学习 - test\acvp_test.c

文章目录 openssl3.2 - 测试程序的学习 - test\acvp_test.c概述笔记要单步学习的测试函数备注END openssl3.2 - 测试程序的学习 - test\acvp_test.c 概述 openssl3.2 - 测试程序的学习 将test*.c 收集起来后, 就不准备看makefile和make test的日志参考了. 按照收集的.c, 按照…

IPv6报文格式(全网最详细)

IPv6报文格式 报文格式 图1 IPv6报文头格式 表1 IP头字段解释 字段长度含义Version4比特 4&#xff1a;表示为IPV4&#xff1b;6&#xff1a;表示为IPV6。Traffic class8比特流量类别。该字段及其功能类似于IPv4的业务类型字段。该字段以区分业务编码点&#xff08;DSCP&…

C语言——操作符详解2

目录 0.过渡0.1 不创建临时变量&#xff0c;交换两数0.2 求整数转成二进制后1的总数 1.单目表达式2. 逗号表达式3. 下标访问[ ]、函数调用( )3.1 下标访问[ ]3.2 函数调用( ) 4. 结构体成员访问操作符4.1 结构体4.1.1 结构体的申明4.1.2 结构体变量的定义和初始化 4.2 结构体成…

房产信息网源码,房产系统,二手房小程序源码,租房小程序系统楼盘系统房产经纪人系统

房产门户系统、多城市房产网、房产小程序 房产网系统、地方房产门户信息网 带im即时通讯聊天 二手房 租房 楼盘 置业顾问 经纪人 腾房云房产网 分为单城市版本 和多城市版本 多城市 自动定位当前城市 每个分站对应独立管理员分站管理 thinkphpuniapp 独立开源

【开源】基于JAVA语言的二手车交易系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 二手车档案管理模块2.3 车辆预约管理模块2.4 车辆预定管理模块2.5 车辆留言板管理模块2.6 车辆资讯管理模块 三、系统设计3.1 E-R图设计3.2 可行性分析3.2.1 技术可行性分析3.2.2 操作可行性3.2.3 经济…

02-opencv-上

机器视觉概述 机器视觉是人工智能正在快速发展的一个分支。简单说来&#xff0c;机器视觉就是用机器代替人眼来做测量和判断。机器视觉系统是通过机器视觉产品(即图像摄取装置&#xff0c;分CMOS和CCD两种)将被摄取目标转换成图像信号&#xff0c;传送给专用的图像处理系统&…

[Python图像处理] 使用OpenCV创建深度图

使用OpenCV创建深度图 双目视觉创建深度图相关链接双目视觉 在传统的立体视觉中,两个摄像机彼此水平移动,用于获得场景上的两个不同视图(作为立体图像),就像人类的双目视觉系统: 通过比较这两个图像,可以以视差的形式获得相对深度信息,该视差编码对应图像点的水平坐标的…

C++ 内存占用分析(内存泄漏)

文章目录 前言一、什么是内存泄漏二、如何检测内存泄漏1、内存占用变化排查法2、valgrind定位法3、mtrace定位法 参考资料 前言 内存占用分析是一个比较庞大的话题&#xff0c;在C程序中&#xff0c;进程的内存持续上涨&#xff0c;有可能是正常的内存占用&#xff0c;有可能是…

Linux之安装配置CentOS 7

一、CentOS简介 CentOS&#xff08;Community Enterprise Operating System&#xff0c;中文意思是社区企业操作系统&#xff09;是Linux发行版之一&#xff0c;它是来自于Red Hat Enterprise Linux依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码&#xff0c…

1、PDManer 快速入门

文章目录 序言一、快速入门1.1 PDMan 介绍1.2 特点1.3 下载和安装 小结 序言 本人长期以来一直从事于应用软件的研发以及项目实施工作&#xff0c;经常做数据库建模&#xff08;数据表设计&#xff09;。有一款称心如意的数据库建模工具&#xff0c;自然能够事半功倍&#xff0…

uniapp 实现路由拦截,权限或者登录控制

背景&#xff1a; 项目需要判断token&#xff0c;即是否登录&#xff0c;登录之后权限 参考uni-app官方&#xff1a; 为了兼容其他端的跳转权限控制&#xff0c;uni-app并没有用vue router路由&#xff0c;而是内部实现一个类似此功能的钩子&#xff1a;拦截器&#xff0c;由…

「研发部」GitFlow规范-升级版(二)

前言 上一篇文章简单整理过一次产研团队的GitFlow《Git 分支管理及Code Review 流程 (一)》 GitFlow是一种流行的Git分支管理策略&#xff0c;它提供了一种结构化的方式来管理项目的开发和发布流程。以下是GitFlow规范的主要组成部分&#xff1a; 主要分支&#xff1a; mast…

[Tomcat] [从安装到关闭] MAC部署方式

安装Tomcat 官网下载&#xff1a;Apache Tomcat - Apache Tomcat 9 Software Downloads 配置Tomcat 1、输入cd空格&#xff0c;打开Tomca目录&#xff0c;把bin文件夹直接拖拉到终端 2、授权bin目录下的所有操作&#xff1a;终端输入[sudo chmod 755 *.sh]&#xff0c;回车 …