以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。

前言

博文第一季2:HI3518EV200的初体验中,所提供的测试文件sample_venc实现了基于RTSP协议的实时视频流传输功能。当时直接提供了二进制文件,现在我们来分析其对应的源码文件。

“基于RTSP协议的实时视频流传输”,以下统一简称“RTSP视频传输”。

一、RTSP视频传输实验

(1)将上面提到的源码文件解压到虚拟机的mpp/目录,并修改名字为sample_rtsp_ar0130(只是为了辨认方便)。

root@ubuntu:/home/xjh/iot/hisi_development/Hi3518E_SDK/Hi3518E_SDK_V1.0.3.0/mpp/sample_rtsp_ar0130# ls
audio  common  hifb  ive  Makefile  Makefile.param  readme  region  scene_auto  tde  venc  vio
root@ubuntu:/home/xjh/iot/hisi_development/Hi3518E_SDK/Hi3518E_SDK_V1.0.3.0/mpp/sample_rtsp_ar0130# 

(2)在mpp/sample_rtsp_ar0130/venc目录下执行make。

(3)接下来与第一季2:HI3518EV200的初体验中第一节第7点的(2)(3)一致,不再赘述。

二、源码分析

RTSP视频传输源码,是基于海思SDK的sample_venc.c来编写的,主要修改sample_venc.c文件、sample_comm_venc.c文件。后续可以对比两个sample的内容,深化对流程的理解。

1、函数调用关系

利用SI软件建立上述源码的工程,然后在sample_venc.c文件中找到main函数并分析调用情况:

mainRtspServer_init //VLC是客服端,HI3518E当做RTSP服务器,后者肯定要先运行起来RtspServerListen //线程RtspClientMsg//线程,针对每个客户端的线程ParseRequestString//解析消息头vdRTPSendThread  //线程,有赖于RtspServer_init的内容,这里是传输数据VENC_Sent//真正发送的函数SAMPLE_VENC_720P_CLASSICSAMPLE_COMM_VENC_StartGetStream//和之前sample不同的地方SAMPLE_COMM_VENC_Sentjin//rtsp的发送VENC_Sent//真正发送的函数

2、分析RtspServer_init函数

(1)RtspServer_init函数内容如下。

void RtspServer_init(void)
{int i;pthread_t threadId = 0;memset(&g_rtp_playload,0,sizeof(g_rtp_playload));strcpy(&g_rtp_playload,"G726-32");g_audio_rate = 8000;pthread_mutex_init(&g_sendmutex,NULL);pthread_mutex_init(&g_mutex,NULL);pthread_cond_init(&g_cond,NULL);//g_rtspClients用来存储一些客户端的信息memset(&g_rtspClients,0,sizeof(RTSP_CLIENT)*MAX_RTSP_CLIENT);//pthread_create(&g_SendDataThreadId, NULL, SendDataThread, NULL);struct sched_param thdsched;thdsched.sched_priority = 2;//定义线程优先级//to listen visitingpthread_create(&threadId, NULL, RtspServerListen, NULL);//pthread_setschedparam(threadId,SCHED_RR,&thdsched);printf("RTSP:-----Init Rtsp server\n");pthread_create(&gs_RtpPid, 0, vdRTPSendThread, NULL);//exitok++;}

(2)RtspServerListen函数的内容与分析

void * RtspServerListen(void*pParam)
{int s32Socket;struct sockaddr_in servaddr;int s32CSocket;int s32Rtn;int s32Socket_opt_value = 1;int nAddrLen;struct sockaddr_in addrAccept;int bResult;memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(RTSP_SERVER_PORT); //554,所以VLC设置那里端口也是554s32Socket = socket(AF_INET, SOCK_STREAM, 0);//这里使用TCPif (setsockopt(s32Socket ,SOL_SOCKET,SO_REUSEADDR,&s32Socket_opt_value,sizeof(int)) == -1) //设置一些属性    {return (void *)(-1);}s32Rtn = bind(s32Socket, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in));if(s32Rtn < 0){return (void *)(-2);}s32Rtn = listen(s32Socket, 50);   if(s32Rtn < 0){return (void *)(-2);}nAddrLen = sizeof(struct sockaddr_in);int nSessionId = 1000;while ((s32CSocket = accept(s32Socket, (struct sockaddr*)&addrAccept, &nAddrLen)) >= 0)//accept是阻塞的,然后等待客服端的连接,连接上了就执行下面内容{printf("<<<<RTSP Client %s Connected...\n", inet_ntoa(addrAccept.sin_addr));int nMaxBuf = 10 * 1024; // 脧碌脥鲁艙芦禄谩路脰脜盲 2 x nMaxBuf 碌脛禄潞鲁氓沤贸脨隆if(setsockopt(s32CSocket, SOL_SOCKET, SO_SNDBUF, (char*)&nMaxBuf, sizeof(nMaxBuf)) == -1)printf("RTSP:!!!!!! Enalarge socket sending buffer error !!!!!!\n");int i;int bAdd=FALSE;for(i=0;i<MAX_RTSP_CLIENT;i++){if(g_rtspClients[i].status == RTSP_IDLE){memset(&g_rtspClients[i],0,sizeof(RTSP_CLIENT));g_rtspClients[i].index = i;g_rtspClients[i].socket = s32CSocket;g_rtspClients[i].status = RTSP_CONNECTED ;//RTSP_SENDING;g_rtspClients[i].sessionid = nSessionId++;strcpy(g_rtspClients[i].IP,inet_ntoa(addrAccept.sin_addr));pthread_t threadIdlsn = 0;struct sched_param sched;sched.sched_priority = 1;//to return ACKechopthread_create(&threadIdlsn, NULL, RtspClientMsg, &g_rtspClients[i]);//pthread_setschedparam(threadIdlsn,SCHED_RR,&sched);bAdd = TRUE;break;}}if(bAdd==FALSE){memset(&g_rtspClients[0],0,sizeof(RTSP_CLIENT));g_rtspClients[0].index = 0;g_rtspClients[0].socket = s32CSocket;g_rtspClients[0].status = RTSP_CONNECTED ;//RTSP_SENDING;g_rtspClients[0].sessionid = nSessionId++;strcpy(g_rtspClients[0].IP,inet_ntoa(addrAccept.sin_addr));pthread_t threadIdlsn = 0;struct sched_param sched;sched.sched_priority = 1;//to return ACKechopthread_create(&threadIdlsn, NULL, RtspClientMsg, &g_rtspClients[0]);//pthread_setschedparam(threadIdlsn,SCHED_RR,&sched);bAdd = TRUE;}//if(exitok){ exitok++;return NULL; }  }if(s32CSocket < 0){// HI_OUT_Printf(0, "RTSP listening on port %d,accept err, %d\n", RTSP_SERVER_PORT, s32CSocket);}printf("----- INIT_RTSP_Listen() Exit !! \n");return NULL;
}

(3)RtspClientMsg函数内容与分析

void * RtspClientMsg(void*pParam)
{pthread_detach(pthread_self());//让线程自己回收自己int nRes;char pRecvBuf[RTSP_RECV_SIZE];RTSP_CLIENT * pClient = (RTSP_CLIENT*)pParam;//由传参而来memset(pRecvBuf,0,sizeof(pRecvBuf));printf("RTSP:-----Create Client %s\n",pClient->IP);while(pClient->status != RTSP_IDLE)//肯定不等于idle{nRes = recv(pClient->socket, pRecvBuf, RTSP_RECV_SIZE,0);//printf("-------------------%d\n",nRes);if(nRes < 1)//没有读到数据的异常情况{//usleep(1000);printf("RTSP:Recv Error--- %d\n",nRes);g_rtspClients[pClient->index].status = RTSP_IDLE;g_rtspClients[pClient->index].seqnum = 0;g_rtspClients[pClient->index].tsvid = 0;g_rtspClients[pClient->index].tsaud = 0;close(pClient->socket);break;}char cmdName[PARAM_STRING_MAX];char urlPreSuffix[PARAM_STRING_MAX];char urlSuffix[PARAM_STRING_MAX];char cseq[PARAM_STRING_MAX];
ParseRequestString(pRecvBuf,nRes,cmdName,sizeof(cmdName),urlPreSuffix,sizeof(urlPreSuffix),urlSuffix,sizeof(urlSuffix),cseq,sizeof(cseq));char *p = pRecvBuf;printf("<<<<<%s\n",p);//printf("\--------------------------\n");//printf("%s %s\n",urlPreSuffix,urlSuffix);if(strstr(cmdName, "OPTIONS")){OptionAnswer(cseq,pClient->socket);}else if(strstr(cmdName, "DESCRIBE")){DescribeAnswer(cseq,pClient->socket,urlSuffix,p);//printf("-----------------------------DescribeAnswer %s %s\n",//    urlPreSuffix,urlSuffix);}else if(strstr(cmdName, "SETUP")){int rtpport,rtcpport;int trackID=0;SetupAnswer(cseq,pClient->socket,pClient->sessionid,urlPreSuffix,p,&rtpport,&rtcpport);sscanf(urlSuffix, "trackID=%u", &trackID);//printf("----------------------------------------------TrackId %d\n",trackID);if(trackID<0 || trackID>=2)trackID=0;g_rtspClients[pClient->index].rtpport[trackID] = rtpport;g_rtspClients[pClient->index].rtcpport= rtcpport;g_rtspClients[pClient->index].reqchn = atoi(urlPreSuffix);if(strlen(urlPreSuffix)<100)strcpy(g_rtspClients[pClient->index].urlPre,urlPreSuffix);//printf("-----------------------------SetupAnswer %s-%d-%d\n",//    urlPreSuffix,g_rtspClients[pClient->index].reqchn,rtpport);}else if(strstr(cmdName, "PLAY")){PlayAnswer(cseq,pClient->socket,pClient->sessionid,g_rtspClients[pClient->index].urlPre,p);g_rtspClients[pClient->index].status = RTSP_SENDING;printf("Start Play\n",pClient->index);//printf("-----------------------------PlayAnswer %d %d\n",pClient->index);//usleep(100);}else if(strstr(cmdName, "PAUSE")){PauseAnswer(cseq,pClient->socket,p);}else if(strstr(cmdName, "TEARDOWN")){TeardownAnswer(cseq,pClient->socket,pClient->sessionid,p);g_rtspClients[pClient->index].status = RTSP_IDLE;g_rtspClients[pClient->index].seqnum = 0;g_rtspClients[pClient->index].tsvid = 0;g_rtspClients[pClient->index].tsaud = 0;close(pClient->socket);}//if(exitok){ exitok++;return NULL; } }printf("RTSP:-----Exit Client %s\n",pClient->IP);return NULL;
}

(4)ParseRequestString函数的内容与分析

int ParseRequestString(char const* reqStr,unsigned reqStrSize,char* resultCmdName,unsigned resultCmdNameMaxSize,char* resultURLPreSuffix,unsigned resultURLPreSuffixMaxSize,char* resultURLSuffix,unsigned resultURLSuffixMaxSize,char* resultCSeq,unsigned resultCSeqMaxSize)
{// This parser is currently rather dumb; it should be made smarter #####// Read everything up to the first space as the command name:int parseSucceeded = FALSE;unsigned i;for (i = 0; i < resultCmdNameMaxSize-1 && i < reqStrSize; ++i) {char c = reqStr[i];if (c == ' ' || c == '\t') {parseSucceeded = TRUE;break;}resultCmdName[i] = c;}resultCmdName[i] = '\0';if (!parseSucceeded) return FALSE;// Skip over the prefix of any "rtsp://" or "rtsp:/" URL that follows:unsigned j = i+1;while (j < reqStrSize && (reqStr[j] == ' ' || reqStr[j] == '\t')) ++j; // skip over any additional white spacefor (j = i+1; j < reqStrSize-8; ++j) {if ((reqStr[j] == 'r' || reqStr[j] == 'R')&& (reqStr[j+1] == 't' || reqStr[j+1] == 'T')&& (reqStr[j+2] == 's' || reqStr[j+2] == 'S')&& (reqStr[j+3] == 'p' || reqStr[j+3] == 'P')&& reqStr[j+4] == ':' && reqStr[j+5] == '/') {j += 6;if (reqStr[j] == '/') {// This is a "rtsp://" URL; skip over the host:port part that follows:++j;while (j < reqStrSize && reqStr[j] != '/' && reqStr[j] != ' ') ++j;} else {// This is a "rtsp:/" URL; back up to the "/":--j;}i = j;break;}}// Look for the URL suffix (before the following "RTSP/"):parseSucceeded = FALSE;unsigned k;for (k = i+1; k < reqStrSize-5; ++k) {if (reqStr[k] == 'R' && reqStr[k+1] == 'T' &&reqStr[k+2] == 'S' && reqStr[k+3] == 'P' && reqStr[k+4] == '/') {while (--k >= i && reqStr[k] == ' ') {} // go back over all spaces before "RTSP/"unsigned k1 = k;while (k1 > i && reqStr[k1] != '/' && reqStr[k1] != ' ') --k1;// the URL suffix comes from [k1+1,k]// Copy "resultURLSuffix":if (k - k1 + 1 > resultURLSuffixMaxSize) return FALSE; // there's no roomunsigned n = 0, k2 = k1+1;while (k2 <= k) resultURLSuffix[n++] = reqStr[k2++];resultURLSuffix[n] = '\0';// Also look for the URL 'pre-suffix' before this:unsigned k3 = --k1;while (k3 > i && reqStr[k3] != '/' && reqStr[k3] != ' ') --k3;// the URL pre-suffix comes from [k3+1,k1]// Copy "resultURLPreSuffix":if (k1 - k3 + 1 > resultURLPreSuffixMaxSize) return FALSE; // there's no roomn = 0; k2 = k3+1;while (k2 <= k1) resultURLPreSuffix[n++] = reqStr[k2++];resultURLPreSuffix[n] = '\0';i = k + 7; // to go past " RTSP/"parseSucceeded = TRUE;break;}}if (!parseSucceeded) return FALSE;// Look for "CSeq:", skip whitespace,// then read everything up to the next \r or \n as 'CSeq':parseSucceeded = FALSE;for (j = i; j < reqStrSize-5; ++j) {if (reqStr[j] == 'C' && reqStr[j+1] == 'S' && reqStr[j+2] == 'e' &&reqStr[j+3] == 'q' && reqStr[j+4] == ':') {j += 5;unsigned n;while (j < reqStrSize && (reqStr[j] ==  ' ' || reqStr[j] == '\t')) ++j;for (n = 0; n < resultCSeqMaxSize-1 && j < reqStrSize; ++n,++j) {char c = reqStr[j];if (c == '\r' || c == '\n') {parseSucceeded = TRUE;break;}resultCSeq[n] = c;}resultCSeq[n] = '\0';break;}}if (!parseSucceeded) return FALSE;return TRUE;
}

3、分析vdRTPSendThread函数

(1)vdRTPSendThread函数的内容如下:

HI_VOID* vdRTPSendThread(HI_VOID *p)
{while(1){if(!list_empty(&RTPbuf_head)){RTPbuf_s *p = get_first_item(&RTPbuf_head,RTPbuf_s,list);VENC_Sent(p->buf,p->len);list_del(&(p->list));free(p->buf);free(p);p = NULL;count--;//printf("count = %d\n",count);}usleep(5000);}
}

第6季2:基于RTSP协议的实时视频流传输的源码分析相关推荐

  1. python实现流媒体传输_基于OpenCV的网络实时视频流传输的实现

    很多小伙伴都不会在家里或者办公室安装网络摄像头或监视摄像头.但是有时,大家又希望能够随时随地观看视频直播. 大多数人会选择使用IP摄像机(Internet协议摄像机)而不是CCTV(闭路电视),因为它 ...

  2. 海豚php源码,基于 ThinkPHP5.1 实现的海豚后台登录源码分析

    基于 thinkphp5.1 实现的海豚后台登录源码分析 一. 首先来到登录代码处,部分代码截图,大家有兴趣可以自己去看源码 登录处开始 public function signin() { if ( ...

  3. 基于 OpenCV 的网络实时视频流传输

    作者 | 努比 来源 | 小白学视觉 大多数人会选择使用IP摄像机(Internet协议摄像机)而不是CCTV(闭路电视),因为它们具有更高的分辨率并降低了布线成本.在本文中,我们将重点介绍IP摄像机 ...

  4. 基于OpenCV的网络实时视频流传输

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 很多小伙伴都不会在家里或者办公室安装网络摄像头或监视摄像头.但是有 ...

  5. serverless搭建html,基于ServerLess的极简网页计数器:源码分析与实践

    这几天基于支持HTML5无感认证的ServerLess平台开发了一款博客.门户网站等web平台常用的PV统计工具:page-counter .主要用到的技术是js+webpack. 回首看来,解决了以 ...

  6. Android 蓝牙hfp协议连接slc连接rfcomm连接源码分析(2)- HF侧发送和接受数据流程

    android-蓝牙A2dp-avrcp-hfp-opp-配对流程-ble-rfcomm源码流程 Android 蓝牙hfp初始化.rfcomm连接.slc连接.sco连接源代码分析大全 - 点击下载 ...

  7. 【我的渲染技术进阶之旅】Google开源的基于物理的实时渲染引擎Filament源码分析:Android版本的Filament第一个示例:sample-hello-triangle

    文章目录 一.效果展示 二.之前的博客 三.示例工程sample-hello-triangle源码分析 3.1 项目源码路径 3.2 分析源码 3.2.1 分析AndroidManifest.xml ...

  8. 【我的渲染技术进阶之旅】Google开源的基于物理的实时渲染引擎Filament源码分析:在android中如何使用filamesh命令将.obj或者.fbx文件转换为.filamesh文件?

    文章目录 一.需求描述 1.1 为啥要学习`filamesh`命令 1.2 从android项目的build.gradle看起 1.3 查看FilamentToolsPlugin插件源代码 1.3.1 ...

  9. 【我的渲染技术进阶之旅】Google开源的基于物理的实时渲染引擎Filament源码分析:在android中如何使用cmgen命令自动将.hdr文件转换为.ktx文件或者.rgb32文件等?

    文章目录 一.需求描述 1.1 为啥要学习cmgen命令 1.1 bug描述 1.1.1 运行错误描述:java.io.FileNotFoundException: envs/flower_road_ ...

最新文章

  1. 闲得蛋疼在linux下装鸟个svn
  2. 【高级Java架构师系统学习】java问答社区系统
  3. php多规格多价格,不同规格的不同价格是怎么实现的呢?? 看这里
  4. 从头搭建 IntelliJ IDEA 环境,从放弃到爱不释手!
  5. python按hash分组_Python操作redis系列以 哈希(Hash)命令详解(四)
  6. group_concat 不是可以识别的 内置函数名称。_Python 函数库 APIs 编写指南
  7. 面向.NET开发人员的Dapr——目录
  8. linux线程负载,linux 排查cpu负载过高异常(转载)
  9. python技巧 计算字符串中字母出现的次数并取出最大
  10. java如何编写屏幕保护程序_将Java App变为Windows屏幕保护程序
  11. 电商后台管理项目的步骤分析
  12. 人脸识别闸机系统解决方案
  13. Python实现日程表
  14. windows cmd 批处理将文件名改为大写:https://blog.csdn.net/llq108/article/details/47185279
  15. filter grok 判断_Grok 正则捕获
  16. 如何与别人解释云计算是什么意思?
  17. Python OLS模型
  18. 京东商城系统架构设计原则
  19. 用c语言验证欧拉定理,欧拉定理_欧拉定理的意义
  20. 初识Vulkan渲染管线

热门文章

  1. GSview 5.0可用注册码:55555-24868
  2. 常见预测方法及其特点
  3. frp实现内网穿透(花生壳也可以)
  4. 特征提取算法--ORB
  5. 森海塞尔PXC 250主动降噪耳机静静地在努力!
  6. A lpad、rpad填充函数--mysql随即订单生成
  7. java日期转换为oracle日期
  8. Docker虚悬镜像
  9. 离散小波变换(DWT)去噪
  10. 爱情有备胎,数据中心有MC-LAG