基础

假定是udp上接收包,端口 5060 ,如果是tcp是要加一些额外得代码,
可以使用jrtplib,或者自己写udpserver 放出udp socket 接收包,当然使用jrtplib等稍微简单一些。不过也一样失去了一些灵活性。

步骤

1 接收udp over rtp包
2 接收后分析ps包头,获取es流
3 如果不是ps则接收到包后根据rtp协议再解析rtp, 获取分片等信息组合成包
4 分析es流

code

#define FILE_STORAGE 0Service_RTP_Analyse::Service_RTP_Analyse()
{}Service_RTP_Analyse::~Service_RTP_Analyse()
{}
int Service_RTP_Analyse::analyse_rtp(uint8_t * rtp, size_t len)
{return 0;
}//注意这里只是适应h264
int Service_RTP_Analyse::analyse_h264es(uint8_t * pos, int len, uint8_t ** nalu, size_t &nalulen)
{uint8_t* p = pos + 4 + 2 + 2; //跳过4字节00 00 01 E0 和两个字节长度 以及两个字节的跳空int skip = *p;p = p + skip + 1; //p 指向 00 00 00 01 67 等字节上*nalu = p;while (!*(p++));uint8_t c1 = (uint8_t)(*p);unsigned char nal_type = c1 & 0x1f;nalulen = len - (int)(*nalu - pos); //因为 00 00 01 E0 以及两个代表长度的字节没有计算return nal_type;
}int Service_RTP_Analyse::analyseps0(uint8_t * ps, size_t len, int &type, int &length, uint8_t **next, size_t &nextlen)
{#define D(y,x) (*(y+x))if (D(ps, 0) == 0x00 && D(ps, 1) == 0x00 && D(ps, 2) == 0x01){uint8_t num = D(ps, 3);switch (num){case 0xBA:{int skip1 = D(ps, 13) & 0x07;uint8_t * nps = ps + 14 + skip1;length = 14;type = PS_PS;*next = nps;nextlen = len - (nps - ps);}return 0;case 0xBB://系统标题起始码字段case 0xBC://映射流标识字段 {char temp[2];temp[0] = D(ps, 5);temp[1] = D(ps, 4);length = *(unsigned short*)(&temp[0]) + 6; //00 00 01 xx 以及两个字节的长度type = PS_SYS;*next = ps + length;nextlen = len - (int)(*next - ps);}return 0;case 0xE0:{char temp[2];temp[0] = D(ps, 5);temp[1] = D(ps, 4);length = *(unsigned short*)(&temp[0]) + 6;//00 00 01 xx 以及两个字节的长度type = PS_VIDEO;*next = ps + length;nextlen = len -(int)(*next - ps);}return 0;case 0xBD:case 0xC0:{char temp[2];temp[0] = D(ps, 5);temp[1] = D(ps, 4);length = *(unsigned short*)(&temp[0]) + 6;//00 00 01 xx 以及两个字节的长度if (num == 0xBD)type = PS_PRIVATE;elsetype = PS_AUDIO;*next = ps + length;nextlen = len - (int)(*next - ps);}return 0;default:{length = 0;type = PS_UNKONWN;}return -1;}}else{length = 0;type = PS_UNKONWN;return -1;}
}/*
//该函数解一帧完整的ps,输入时一帧完整的ps帧
//rc 上下文环境
//pos ps流数据指针
//len 长度
*/
int Service_RTP_Analyse::analyseps(rtp_context *prc, uint8_t * pos, size_t len)
{int type = 0;int length = 0; //当前段长度uint8_t * ps = pos;size_t pslen = len;uint8_t * nextpos = NULL;size_t nextlen = 0;uint8_t * sps = NULL;int spslen = 0;uint8_t * pps = NULL;int ppslen = 0;uint8_t * se = NULL;int selen = 0;uint8_t *nalu = NULL;size_t nalulen = 0;while (1){int ret = analyseps0(ps, pslen, type, length, &nextpos, nextlen);if (ret == -1)return -1;switch (type) //处理当前段{case PS_PS:ps = nextpos;pslen = nextlen;break;case PS_SYS:ps = nextpos;pslen = nextlen;break;case PS_VIDEO:{int type = analyse_h264es(ps, length, &nalu, nalulen);switch (type){case 0x07:sps = nalu;spslen = (int)nalulen;break;case 0x08:pps = nalu;ppslen = (int)nalulen;break;case 0x06: //skipse = nalu;selen = (int)nalulen;break;case 0x05:case 0x01://skip,because except sps pps ,only one 0x05 key frame nalu break;default:break;}ps = nextpos;pslen = nextlen;}break;case PS_PRIVATE:ps = nextpos;pslen = nextlen;break;case PS_AUDIO:ps = nextpos;pslen = nextlen;break;}if (nextlen <= 0)break;}//顺序找到了流以后,开始移动//有关键帧,则移动sps和pps
#if FILE_STORAGEif (ar->fp == NULL)ar->fp = fopen("h264save.264", "wb");if (ar->fpps == NULL)ar->fpps = fopen("h264saveps.264", "wb");fwrite(pos, 1, len, ar->fpps);
#endifif (sps != NULL && pps != NULL){//获取完整的一帧//移动少量数据,达到意义上的零拷贝memmove(nalu - ppslen, pps, ppslen);memmove(nalu - ppslen - spslen, sps, spslen);sps = nalu - ppslen - spslen;pps = nalu - ppslen;if (prc->_callback != NULL){prc->_callback(prc->ip,prc->port,sps,ppslen + spslen + nalulen,KEY_FRAME,sps,spslen + ppslen,prc->pts++);}
#if FILE_STORAGEfwrite(sps, 1, spslen + ppslen + nalulen, ar->fp);
#endif}else //非关键帧{if (prc->_callback != NULL)prc->_callback(prc->ip, prc->port, nalu, nalulen, NOT_KEY_FRAME,NULL,0, prc->pts++);#if FILE_STORAGEfwrite(nalu, 1, nalulen, ar->fp);
#endif}return 0;
}
//ar 因为零拷贝的事情buffer可能没有值int Service_RTP_Analyse::insert(uint32_t ip, uint16_t port, void *pkt0)
{RTPPacket * pkt = (RTPPacket*)pkt0;//if (pkt->GetPayloadLength() > BUF_LENGTH) //这里还是要修改 钱波//   return -1;auto iter = _hash.find(ip);if (iter == _hash.end()) //没有找到该IP地址的存储{OutputDebugString(L"not found");uint8_t * ps = pkt->GetPayloadData();//分析ps流if (*ps == 0x00 && *(ps + 1) == 0x00 && *(ps + 2) == 0x01 && *(ps + 3) == 0xBA){analyse_struct as;as.in = pkt->GetPayloadData();as.inlen = pkt->GetPayloadLength();AnalyseH264Frame(&as);if (as.ret == 0) //非关键帧{OutputDebugString(L"第一RTP包非关键帧头部,丢弃\n");return -1;}rtp_context rc;rc.ip = ip;rc.port = port;rc._callback = _callback;if (pkt->HasMarker()){rc.blen = 0;analyseps(&rc, pkt->GetPayloadData(), pkt->GetPayloadLength());//send_func(&rc, ip, port, pkt->GetPayloadData(), pkt->GetPayloadLength(), 0);}else{ALLOC_MEM(rc);rc.blen= pkt->GetPayloadLength();memcpy(rc.buffer, pkt->GetPayloadData(), rc.blen);}//fixme:qianbo//_hash.emplace(pair<uint32_t, rtp_context>(ip, as0));_hash.insert(pair<uint32_t, rtp_context>(ip, rc));}else //分析普通rtp流{}}else //找到存储{//代码优化零拷贝rtp_context &rc = iter->second;size_t &len = rc.blen;if (pkt->HasMarker()) // 回调启用{if (len == 0) {//执行零拷贝操作analyseps(&rc, pkt->GetPayloadData(), pkt->GetPayloadLength());//send_func(&rc, ip, port, pkt->GetPayloadData(), pkt->GetPayloadLength(), 0);}else // len!=0{uint8_t * buf = iter->second.buffer;memcpy(buf + len, pkt->GetPayloadData(), pkt->GetPayloadLength());len += pkt->GetPayloadLength();analyseps(&rc, buf, len);//send_func(&rc, ip, port, buf, len, 0);}len = 0;}else{ALLOC_MEM(rc);uint8_t * buf = iter->second.buffer;memcpy(buf + len, pkt->GetPayloadData(), pkt->GetPayloadLength());len += pkt->GetPayloadLength();}}return 0;}// 功能:解码RTP H.264视频// 参数:1.RTP包缓冲地址 2.RTP包数据大小 3.H264输出地址 4.输出数据大小// 返回:true:表示一帧结束  false:FU-A分片未结束或帧未结束bool  Service_RTP_Analyse::decode_rtp_h264(void *bufIn, int len, void **pBufOut, int *pOutLen){*pOutLen = 0;if (len  <  RTP_HEADLEN){return   false;}uint8_t *src = (uint8_t*)bufIn + RTP_HEADLEN;//第1字节uint8_t  head_1 = *src;//第2字节uint8_t  head_2 = *(src + 1);//FU indicator的类型域,uint8_t  nal = head_1 & 0x1f;//FU header的前三位,当前是分包的1开始 2中间 3结束uint8_t  flag = head_2 & 0xe0;uint8_t  nal_fua = (head_1 & 0xe0) | (head_2 & 0x1f); // FU_A nalbool  bFinishFrame = false;if (nal == 0x1c) // 判断NAL的类型为0x1c=28,说明是FU-A分片{ // fu-aif (flag == 0x80) // 开始{*pBufOut = src - 3;*((int *)(*pBufOut)) = 0x01000000; // *((char *)(*pBufOut) + 4) = nal_fua;*pOutLen = len - RTP_HEADLEN + 3;}else if (flag == 0x40) // 结束{*pBufOut = src + 2;*pOutLen = len - RTP_HEADLEN - 2;}else // 中间{*pBufOut = src + 2;*pOutLen = len - RTP_HEADLEN - 2;}}else // 单包数据{*pBufOut = src - 4;*((int *)(*pBufOut)) = 0x01000000; // 大模式会有问题*pOutLen = len - RTP_HEADLEN + 4;}uint8_t *  tmp = (unsigned  char*)bufIn;bFinishFrame = (tmp[1] & 0x80) ? true : false;return  bFinishFrame;}//功能:解RTP AAC音频包,声道和采样频率必须知道。//参数:1.RTP包缓冲地址 2.RTP包数据大小 3.H264输出地址 4.输出数据大小//返回:true:表示一帧结束  false:帧未结束 一般AAC音频包比较小,没有分片。bool Service_RTP_Analyse::decode_rtp_aac(void * bufIn, int recvLen, void** pBufOut, int* pOutLen){unsigned char*  bufRecv = (unsigned char*)bufIn;//char strFileName[20];unsigned char ADTS[] = { 0xFF, 0xF1, 0x00, 0x00, 0x00, 0x00, 0xFC };int audioSamprate = 32000;//音频采样率int audioChannel = 2;//音频声道 1或2int audioBit = 16;//16位 固定switch (audioSamprate){case  16000:ADTS[2] = 0x60;break;case  32000:ADTS[2] = 0x54;break;case  44100:ADTS[2] = 0x50;break;case  48000:ADTS[2] = 0x4C;break;case  96000:ADTS[2] = 0x40;break;default:break;}ADTS[3] = (audioChannel == 2) ? 0x80 : 0x40;int len = recvLen - 16 + 7;len <<= 5;//8bit * 2 - 11 = 5(headerSize 11bit)len |= 0x1F;//5 bit    1            ADTS[4] = len >> 8;ADTS[5] = len & 0xFF;*pBufOut = (char*)bufIn + 16 - 7;memcpy(*pBufOut, ADTS, sizeof(ADTS));*pOutLen = recvLen - 16 + 7;unsigned char* bufTmp = (unsigned char*)bufIn;bool bFinishFrame = false;if (bufTmp[1] & 0x80){//DebugTrace::D("Marker");bFinishFrame = true;}else{bFinishFrame = false;}return true;}int Service_RTP_Analyse::insert_rtp(uint32_t ip, uint16_t port, void * pkt_)
{RTPPacket * pkt = (RTPPacket*)pkt_;uint8_t *buf = pkt->GetPayloadData();size_t len   = pkt->GetPayloadLength();uint8_t * out_head = NULL;int       out_len = 0;uint64_t key = ((uint64_t)ip << 32) | port;//std::string key = itoa()uint32_t rssrc  = pkt->GetSSRC();uint32_t rstamp = pkt->GetTimestamp();auto iter = _hash.find(key);if (iter == _hash.end()){rtp_context s;ALLOC_MEM(s);_hash.insert(pair<uint64_t, rtp_context>(key,s));s.start_timestamp = rstamp;decode_rtp_h264(buf, len,(void**)&out_head, &out_len);if (s.blen - s.pos > out_len){memcpy(s.buffer + s.pos, out_head, out_len);s.pos += out_len;}}else{decode_rtp_h264(buf, len, (void**)&out_head, &out_len);rtp_context &s = iter->second;if (s.blen - s.pos > out_len){memcpy(s.buffer + s.pos, out_head, out_len);s.pos += out_len;}}return 0;
}

gb28181 ps流文件解析相关推荐

  1. 海康摄像头PS流格式解析(RTP/PS/H264)

    海康威视视频录像以PS格式打包,解析的过程按照PS包-->system header--->program stream map--->音视频PES包一路下来,海康在包中自定义了一些 ...

  2. GB28181 PS流传输格式详解

    1.PS流传输格式预览 1.视频关键帧的封装 RTP + PS header + PS system header + PS system Map + PES header +h264 data 2. ...

  3. MPEG-2 PS流

    一个完整的MPEG-2文件就是一个PS流文件. MPEG-2文件的构造格式如下图: 以一个实际的MPEG-2文件为例来分析一下PS流,MPEG-2文件: http://download.csdn.ne ...

  4. 海康威视实时预览回调PS流用EasyRTMP向RTMP服务器推流中视频数据处理的代码

    在上一篇方案<EasyRTMP结合海康HCNetSDK获取海康摄像机H.264实时流并转化成为RTMP直播推流(附源码)>我们介绍了将海康安防摄像机进行互联网直播的整体方案流程,其中有一个 ...

  5. PS流详解(载荷H264)

    目录 PS简介 标准结构 标准H264流结构 定长音频帧和其他流式私有数据的结构 PS流封装标准 PSH结构 PES包结构 PSM包结构体 元素流 PS 封装规则 H264元素流封装规则 音频元素流封 ...

  6. 音视频学习(十)——ps流

    1. 简介 PS的封装格式需要支持MPEG2/MPEG4/H.264等视频和MPEG系列的音频,支持在多个层次加入私有数据,方便解码.拖动和加入延时,同时考虑到标准的PS.TS 和 RTP 封装方式间 ...

  7. EasyRTMP:RTMP推流海康威视实时预览回调PS流用EasyRTMP向RTMP服务器推流中视频数据处理的代码

    在上一篇方案<EasyRTMP结合海康HCNetSDK获取海康摄像机H.264实时流并转化成为RTMP直播推流(附源码)>中我们介绍了将海康安防摄像机进行互联网直播的整体方案流程,其中有一 ...

  8. Gb28181之Ps流解析H264

    gb28181发送码流选择PS流,PS流在封装H264的数据.本文详细描述如何通过ps流解析H264码流. *************************PSM流解析*************** ...

  9. javaCV简单解析gb28181的rtp ps流,并推流到rtmp服务

    本文转自javacv社区三群管理员"赶在时间前面":过去的过去了的博客,感谢大佬倾情贡献,支持javacv社区发展和壮大. 国标gb28181全系列都可以参考过去的过去了的博客,再 ...

最新文章

  1. 程序员面试系列——冒泡排序
  2. word文本转换为表格 ,如果文本是以硬回车的转换方式
  3. elementui 可伸缩侧边栏_ElementUI 侧边栏伸缩布局
  4. opencv-api fitEllipse
  5. 服务器重启宝塔面板打不开了【已完美解决】
  6. ThreadLocal的作用
  7. 常用算法案例之贪心法(C语言)
  8. windows socket 网络编程
  9. ParaView Volume MHD
  10. 数学之美番外篇:平凡而又神奇的贝叶斯方法
  11. H3C无线AP 瘦模式转胖模式 fit转fat
  12. 窗口函数 (转) rows between unbounded preceding and current row
  13. 实现pygame自定义鼠标外形
  14. (十)进度条媒体对象和 Well 组件
  15. MySQL 内连接、外连接、全连接
  16. 20220425二次型复习
  17. 徽章机器人gba_《真型机器人》GBA简单攻略
  18. 计算机排名次怎么操作,Excel表格怎么排名次?
  19. tp5使用monolog_使用Monolog记录:从Devtools到Slack
  20. oracle大于字符串时间,Oracle

热门文章

  1. 《C++ 沉思录》学习笔记——上篇
  2. 悟空说财经:雅虎推出全新聊天机器人 帮家庭成员管理日程安排
  3. 掉了几根头发,才弄懂了HTML。
  4. 投诉申告:招行竟然如此对待零存整取客户?
  5. 基于SSMSOCOOL街舞培训系统
  6. 看看你们用的虚拟主机评测第几?
  7. ftp 新建远程目录报错550
  8. 20个专业在线配色网站分享
  9. 一起学习Spark入门
  10. threejs精灵模型_THREEJS中的3D(动画)模型