您当前的位置: 首页 > 

qianbo_insist

暂无认证

  • 0浏览

    0关注

    399博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

gb28181 ps流文件解析

qianbo_insist 发布时间:2022-02-11 19:38:48 ,浏览量:0

基础

假定是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 0

Service_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;
			else
				type = 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: //skip
				se = 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 fp == NULL)
		ar->fp = fopen("h264save.264", "wb");

	if (ar->fpps == NULL)
		ar->fpps = fopen("h264saveps.264", "wb");
	fwrite(pos, 1, len, ar->fpps);
#endif
	if (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_STORAGE
		fwrite(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_STORAGE
		fwrite(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(ip, as0));
			_hash.insert(pair(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  GetPayloadData();
	size_t len   = pkt->GetPayloadLength();
	uint8_t * out_head = NULL;
	int       out_len = 0;
	uint64_t key = ((uint64_t)ip GetTimestamp();
	

	auto iter = _hash.find(key);
	if (iter == _hash.end())
	{
		rtp_context s;
		ALLOC_MEM(s);
		_hash.insert(pair(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;
}
关注
打赏
1663161521
查看更多评论
立即登录/注册

微信扫码登录

0.1785s