多路码流RTSP传输并对每路视频图像叠加OSD信息

news2024/12/26 11:11:44

一、先保证多路视频码流保存到本地,并且分辨率正确

注意点:
1.VPSS分两路通道
2.VENC分两路通道

if(s32ChnNum>=1)
{
    s32Ret = SAMPLE_COMM_SYS_GetPicSize(gs_enNorm, enSize[0], &stSize);
        if (HI_SUCCESS != s32Ret)
        {
        SAMPLE_PRT("SAMPLE_COMM_SYS_GetPicSize failed!\n");
        goto END_VENC_720P_CLASSIC_1;
        }

        VpssGrp = 0;
    stVpssGrpAttr.u32MaxW = stSize.u32Width;
    stVpssGrpAttr.u32MaxH = stSize.u32Height;
    stVpssGrpAttr.bIeEn = HI_FALSE;
    stVpssGrpAttr.bNrEn = HI_TRUE;
    stVpssGrpAttr.bHistEn = HI_FALSE;
    stVpssGrpAttr.bDciEn = HI_FALSE;
    stVpssGrpAttr.enDieMode = VPSS_DIE_MODE_NODIE;
    stVpssGrpAttr.enPixFmt = PIXEL_FORMAT_YUV_SEMIPLANAR_420;

    s32Ret = SAMPLE_COMM_VPSS_StartGroup(VpssGrp, &stVpssGrpAttr);
        if (HI_SUCCESS != s32Ret)
        {
        SAMPLE_PRT("Start Vpss failed!\n");
        goto END_VENC_720P_CLASSIC_2;
        }

    s32Ret = SAMPLE_COMM_VI_BindVpss(stViConfig.enViMode);
        if (HI_SUCCESS != s32Ret)
        {
        SAMPLE_PRT("Vi bind Vpss failed!\n");
        goto END_VENC_720P_CLASSIC_3;
        }

        VpssChn = 0;
    stVpssChnMode.enChnMode      = VPSS_CHN_MODE_USER;
    stVpssChnMode.bDouble        = HI_FALSE;
    stVpssChnMode.enPixelFormat  = PIXEL_FORMAT_YUV_SEMIPLANAR_420;
    stVpssChnMode.u32Width       = stSize.u32Width;
    stVpssChnMode.u32Height      = stSize.u32Height;
    stVpssChnMode.enCompressMode = COMPRESS_MODE_SEG;
        memset(&stVpssChnAttr, 0, sizeof(stVpssChnAttr));
    stVpssChnAttr.s32SrcFrameRate = -1;
    stVpssChnAttr.s32DstFrameRate = -1;
    enRcMode = SAMPLE_RC_CBR;
    s32Ret = SAMPLE_COMM_VPSS_EnableChn(VpssGrp, VpssChn, &stVpssChnAttr, &stVpssChnMode, HI_NULL);
        if (HI_SUCCESS != s32Ret)
        {
        SAMPLE_PRT("Enable vpss chn failed!\n");
        goto END_VENC_720P_CLASSIC_4;
        }
}

//--------Modify point-----------
#ifdef Modity_point

        if(s32ChnNum>=2)
        {
        s32Ret = SAMPLE_COMM_SYS_GetPicSize(gs_enNorm, enSize[1], &stSize);
            if (HI_SUCCESS != s32Ret)
            {
        SAMPLE_PRT("SAMPLE_COMM_SYS_GetPicSize failed!\n");
        goto END_VENC_720P_CLASSIC_1;
            }
            VpssChn = 1;
            stVpssChnMode.enChnMode      = VPSS_CHN_MODE_USER;
            stVpssChnMode.bDouble        = HI_FALSE;
            stVpssChnMode.enPixelFormat  = PIXEL_FORMAT_YUV_SEMIPLANAR_420;
            stVpssChnMode.u32Width       = stSize.u32Width;
            stVpssChnMode.u32Height      = stSize.u32Height;
            stVpssChnMode.enCompressMode = COMPRESS_MODE_SEG;
            memset(&stVpssChnAttr, 0, sizeof(stVpssChnAttr));
            stVpssChnAttr.s32SrcFrameRate = -1;
            stVpssChnAttr.s32DstFrameRate = -1;
            enRcMode = SAMPLE_RC_CBR;
            s32Ret = SAMPLE_COMM_VPSS_EnableChn(VpssGrp, VpssChn, &stVpssChnAttr, &stVpssChnMode, HI_NULL);
            if (HI_SUCCESS != s32Ret)
        {
        SAMPLE_PRT("Enable vpss chn failed!\n");
        goto END_VENC_720P_CLASSIC_4;
            }
}
#endif

二、将每个视频码流加入各自的环形缓冲区

比如将720P的码流加入缓冲区0,VGA的码流加入缓冲区1
注意点:
1.不能共用一个,如果共用一个会导致数据混乱
2.只有VLC连接过来并且服务器进入了PLAY状态,才开始将视频码流加入环形缓冲区

HI_S32 saveStream(int chnnum,VENC_STREAM_S *pstStream)
{
    HI_S32 i,lens=0;

//---------Moidty point-----------
        //for(;j<MAX_RTSP_CLIENT;)//have atleast a connect//MAX_RTSP_CLIENT = 2
        //{
        //只有播放器连接过来我才开始将编码数据加入环形缓冲区
        if(g_rtspClients[chnnum].status == RTSP_SENDING)//可发送状态
        {
            for (i = 0; i < pstStream->u32PackCount; i++)//u32PackCount:码流包个数
            {
                RTPbuf_s *p = (RTPbuf_s *)malloc(sizeof(RTPbuf_s));    //生成一个链表节点
                INIT_LIST_HEAD(&(p->list));    //并且指针都指向自己

                //填充结构体
                lens = pstStream->pstPack[i].u32Len-pstStream->pstPack[i].u32Offset;
                p->buf = (char *)malloc(lens);
                p->len = lens;
                memcpy(p->buf,pstStream->pstPack[i].pu8Addr+pstStream->pstPack[i].u32Offset,lens);
//---------Moidty point-----------
#ifdef Modity_point

                if(chnnum==0)
                    list_add_tail(&(p->list),&RTPbuf_head);//节点加入环形链表
                    
                if(chnnum==1)
                    list_add_tail(&(p->list),&RTPbuf_head2);//节点加入环形链表2
        
#endif
                count[chnnum]++;            //链表节点的个数
                //count++;            //链表节点的个数
                //printf("count = %d\n",count);
            }
          }
//    }

    return HI_SUCCESS;
}

三、监听线程会等待播放器客户端连接,每当有一个链接就会创建一个线程去对接,在此线程函数中进行完OPTHIONS、Describe、Setup的响应后, 会生成一个udp套接字专门用于发送视频数据,然后创建各自的发送线程来使用各自的udp套接字进行发送

int PlayAnswer(char *cseq, int sock,int SessionId,char* urlPre,char* recvbuf,int index)
{
    if (sock != 0)
    {
        char buf[1024];
        memset(buf,0,1024);
        char *pTemp = buf;
        char*localip;
        localip = GetLocalIP(sock);
        pTemp += sprintf(pTemp,"RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sRange: npt=0.000-\r\nSession: %d\r\nRTP-Info: url=rtsp://%s/%s;seq=0\r\n\r\n",
            cseq,dateHeader(),SessionId,localip,urlPre);

        free(localip);
        
        int reg = send(sock, buf,strlen(buf),0);
        if(reg <= 0)
        {
            return FALSE;
        }
        else
        {
            printf(">>>>>%s",buf);
            udpfd[index] = socket(AF_INET,SOCK_DGRAM,0);//为每个链接创建一个UDP套接字,专门用来发送
            struct sockaddr_in server;
            server.sin_family=AF_INET;
               server.sin_port=htons(g_rtspClients[index].rtpport[index]);          
               server.sin_addr.s_addr=inet_addr(g_rtspClients[index].IP);
            connect(udpfd[index],(struct sockaddr *)&server,sizeof(server));
                    printf("udp up\n");
            pthread_create(&gs_RtpPid[index], 0, vdRTPSendThread, (void *)&index);//为每个链接创建一个发送线程
        }
        return TRUE;
    }
    return FALSE;
}

注意点:保证最后需要几路码流就生成几个udp套接字和几个发送线程,每路码流对应一个udp套接字和一个发送线程

四、由于所有发送线程共用一个发送函数vdRTPSendThread,所以要此函数中对各个线程进行区分,执行不同的函数体(可以通过线程传参来实现),在发送函数中判断环形缓冲区是否为空,不为空则一次一次地调用发送函数发出去;为空则阻塞

HI_VOID* vdRTPSendThread(HI_VOID *p)
{
        HI_S32 index = *((HI_S32*)p);//线程传参,用于区分不同的发送线程
        while(1)    //每5ms判断当前是否有数据要发送
        {
                if(index == 0){
#if 1

        //printf("kongkongkong");
            if(!list_empty(&RTPbuf_head))    //链表为空,则表明当前没有数据要发送
            {
                //printf("feikong-hhhhhhhhhhhhhhhh");
            //有执行
            RTPbuf_s *p = get_first_item(&RTPbuf_head,RTPbuf_s,list);
            //有发
            VENC_Sent(p->buf,p->len,0);
            list_del(&(p->list));
            free(p->buf);
            free(p);
            p = NULL;
            count[0]--;
            //printf("count = %d\n",count);        
            }
                }
#endif

#ifdef Modity_point    
            else if(index == 1){
         
            if(!list_empty(&RTPbuf_head2))    //链表为空,则表明当前没有数据要发送
            {  
            
            RTPbuf_s *p = get_first_item(&RTPbuf_head2,RTPbuf_s,list);
            VENC_Sent(p->buf,p->len,1);
            list_del(&(p->list));
            free(p->buf);
            free(p);
            p = NULL;
            count[1]--;
            //printf("count = %d\n",count);
        
            }
                  }

#endif
        usleep(5000);                //延时5ms
    }
}

五、发送函数(对RTP协议的封包发送)只管发一次RTP包出去

注意点:因为有多个环形缓冲区需要发送,所以设计时要考虑根据udp套接字的不同而发送

六、多路视频图像如何为每路叠加OSD—单独为每个通道各自分配一个区域

最终效果:
![在这里插入图片描述](https://img-blog.csdnimg.cn/636cc65ef88b4be2a81e98acfcbff15d.png

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

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

相关文章

下载网页视频的软件 下载网页视频的方法

视频已然成为一种非常有力的展示方式&#xff0c;我们想要下载视频内容&#xff0c;需要用到下载网页视频的软件&#xff0c;如今这类软件有很多。下载网页视频的方法&#xff0c;也因为使用的软件不同&#xff0c;方法各异。下面我们就来看详细介绍吧&#xff01; 一、下载网…

【DC-DC】AP9196 DC-DC升压恒流电源管理芯片 升降压3-12V输出9V LED驱动方案

1&#xff0c;方案;升降压3-12V输出9V LED驱动方案BOM表 ​ 2&#xff0c;方案;升降压3-12V输出9V LED驱动方案线路图 3&#xff0c;产品说明 AP9196 是一系列外围电路简洁的宽调光比升压调光恒流驱动器&#xff0c;适用于 3-40V 输入电压范围的 LED照明领域。AP9196 采用我…

Leveldb代码阅读笔记

整体架构 如上图&#xff0c;leveldb的数据存储在内存以及磁盘上&#xff0c;其中&#xff1a; memtable&#xff1a;存储在内存中的数据&#xff0c;使用skiplist实现。immutable memtable&#xff1a;与memtable一样&#xff0c;只不过这个memtable不能再进行修改&#xff0…

使用模拟电路制造CPU——从硬件到软件的设计

计算机中蕴藏的哲理 最基本的思想是&#xff1a;通过基本电路的接线&#xff0c;确立输入-输出规则&#xff0c;类似函数的入参和返回值&#xff0c;便构成一个功能电路单元。单元套单元组成新单元&#xff0c;如此往复。“一生二&#xff0c;二生三&#xff0c;三生万物”。这…

盒马上市,即时零售最大“变量”

若盒马年内成功上市&#xff0c;等待完成下一轮融资的朴朴超市的处境恐将更加尴尬&#xff0c;另区域性中小商超或将迎来新一轮倒闭潮。 疫情过后&#xff0c;国内消费市场一直处于走弱态势。据商务大数据监测&#xff0c;今年端午假期&#xff0c;部分地区零售和餐饮数据远不及…

数据结构记录和leetcode刷题记录

背景 时间复杂度 算法的执行时间与输入值之间的关系。 参考&#xff1a;算法中七种常见的时间复杂度 - 掘金 参考视频&#xff1a;1. 算法的时间复杂度_哔哩哔哩_bilibili 空间复杂度 算法的存储空间与输入值之间的关系。 参考&#xff1a;2. 算法的空间复杂度_哔哩哔哩_bili…

【花雕】全国青少年机器人技术一级考试备考实操搭建手册1

目录 1、秋千 2、跷跷板 3、搅拌器 4、奇怪的钟 5、起重机 6、烤肉架 7、手摇风扇 8、履带车 9、直升机 10、后轮驱动车 秋千 &#xff08;闽南语、广东话称千秋&#xff09;是一种座椅&#xff0c;常见于儿童游乐场、杂技演员马戏团或门廊上放松的地方。 秋千的座位通常悬挂在…

蘑菇街被裁后,成功入职字节跳动的那一天,我哭了...

前言 先说一下个人情况&#xff0c;18 届应届生&#xff0c;通过校招进入到了蘑菇街&#xff0c;然后一待就待了差不多 2 年多的时间&#xff0c;可惜的是今年 4 月份受疫情影响遇到了大裁员&#xff0c;而他也是其中一员。好在早有预感&#xff0c;提前做了准备&#xff0c;之…

C++primer(第五版)第九章(顺序容器)

简单来说存入顺序和取出顺序一致的容器被称为顺序容器. 9.1顺序容器概述 vector和string将元素保存在连续的内存空间中,所以每次添加或删除元素都会非常耗时,但是通过下标计算内存地址来读取元素是非常快的. list和forward_list为链表,添加或删除元素的操作很快,但是不支持元…

QT——使用QListWidget、QListWidgetItem、QWidget实现自定义管理列表

作者&#xff1a;小 琛 欢迎转载&#xff0c;请标明出处 文章目录 需求场景思路描述Qt模块QListWidgetQListWidgetItem自定义QWidget配合QListWidget 例子&#xff1a;实现一个json文件管理窗口 需求场景 因工作需要&#xff0c;开发一个文件管理窗口&#xff0c;要让使用者可…

蓝牙通过串口获取电量!!!

问题&#xff1a;样机短时间出现电池没电情况&#xff08;异常放电&#xff09;&#xff0c;经过检查&#xff0c;蓝牙在休眠是会有2.1V左右的电压&#xff0c;导致电池电量消耗过快。机器由于有语音功能&#xff0c;播放语音时需要很大电流&#xff0c;给机器装的是两节南孚电…

UE4/5数字人Metahuman与iClone的使用【数字人与动画】

iClone软件下载 iClone 8 是一款由Reallusion开发的3D动画软件&#xff0c;接下来就不多说了&#xff0c;首先进入官网&#xff0c;我们可以看到两个下载的东西&#xff0c;左边是下面这个软件&#xff0c;直接下载即可&#xff0c;右边的我们在后面下载&#xff1a; iClone插…

将DES解密用Python实现

将此段代码用python实现 var CryptoJS require("crypto-js"); var ciphertext "1MpdxK203ZrnyxuJRrYatKSBxHUIi1TSdQF2BQKXOG54plwfaB2GA"; var key CryptoJS.enc.Utf8.parse("11"); var parsedCiphertext CryptoJS.enc.Base64.parse(ciphe…

6月VR大数据:Quest 2下跌2个百分点,SteamVR统计加入大朋E4

Hello大家好&#xff0c;每月一期的VR内容/硬件大数据统计又和大家见面了。 想了解VR软硬件行情么&#xff1f;关注这里就对了。我们会统计Steam平台的用户及内容等数据&#xff0c;每月初准时为你推送&#xff0c;不要错过喔&#xff01; 本数据报告包含&#xff1a;Steam VR硬…

计算机找不到msvcr120.dll无法安装启动,三招修复方法

在安装运行软件的时候&#xff0c;计算机提示“找不到msvcr120.dll”&#xff0c;“msvcr120.dll缺失”无法启动运行是什么原因呢&#xff1f;msvcr120.dll是一个Windows系统的动态链接库&#xff0c;它是Microsoft Visual C Redistributable for Visual Studio 2013的一部分。…

Rosetta从头蛋白抗体设计、结构优化及在药物研发中的应用专题

第一天 时间:AM9:00~9:50 内容:一.从蛋白质折叠到蛋白质设计 教学目标&#xff1a;了解本方向内容、理论基础、研究意义。主要知识点 主要知识点:1蛋白质折叠与结构预测简介 1.1主链二面角与二级结构 1.2侧链堆积与三级结构 2蛋白质设计简介 2.1蛋白质设计的分类及应用 时间:AM…

DataGrip编写SQL语句操作Spark(Spark ThriftServer)

文章目录 1.Spark ThriftServer2.启动 Spark ThriftServer3.Beeline方式连接4.DataGrip方式连接5. 代码方式6. SparkSQL运行方式7.参考文章 1.Spark ThriftServer Spark ThriftServer 相当于一个持续性的Spark on Hive集成模式&#xff0c;可以启动并监听在10000端口&#xff…

盘口策略 | 交易中最重要的是什么?

量化策略开发&#xff0c;高质量社群&#xff0c;交易思路分享等相关内容 『正文』 ˇ 交易中最重要的是什么&#xff1f; 当然是Timing啊~~~ “时机是这个世界上最难得到&#xff0c;又最容易失去的东西”夫难得而易失者,时也&#xff1b;时至而不旋踵者,机也,故圣人常顺时…

浅谈Java8中map的新方法

Map在java8中新增了两个replace的方法 1.replace(k,v) 在指定的键已经存在并且有与之相关的映射值时才会将指定的键映射到指定的值&#xff08;新值&#xff09; 在指定的键不存在时&#xff0c;方法会return回来一个null javadoc的注释解释了该默认值方法的实现的等价Java…

Web安全-Godzilla(哥斯拉)Webshell管理工具使用

为方便您的阅读&#xff0c;可点击下方蓝色字体&#xff0c;进行跳转↓↓↓ 01 工具下载地址02 运行环境03 工具介绍04 使用案例 01 工具下载地址 https://github.com/BeichenDream/Godzilla点击页面右侧"releases"&#xff0c;进入工具的版本下载页面。 在个人终端…