海思Hi3531||瑞芯微RK1109用rtsp客户端实现h264拉流

认识RTP包头和h264的nalu头

参考服务器的包头介绍

认识RTSP协议

参考服务器RTSP_state_machine函数对RTSP协议的解释

客户端源码

连接服务器

在这里插入图片描述

发送RTSP协议

/* MAXBUF use to receive message from stream server */
    bzero(buffer, MAXBUF + 1);
	/* send message to server */
	Handel_OPTION(buffer);//给服务器发RTSP的option协议
    len = send(sockfd, buffer, strlen(buffer), 0);
    if (len < 0)
        printf("_____%d_____OPTION Reply Msg:\n %s send error! error code is %d, error content is  %s \n", __LINE__,buffer, errno, strerror(errno));
    else
        printf("_____%d_____OPTION Reply Msg:\n %s send successfully,total content is %d bytes!\n", __LINE__, buffer, len);
	bzero(buffer, MAXBUF + 1);
	printf("____%d____\n",__LINE__);
    /* receive message from server */
    len = recv(sockfd, buffer, MAXBUF, 0); /* 确认服务器回复OPTION Reply */
    if (len < 0){
        printf("_____%d_____OPTION Reply Msg:\n %s receive error! error code is %d, error content is  %s \n", __LINE__, buffer, errno, strerror(errno));
	}
    else{
        printf("_____%d_____OPTION Reply Msg:\n %s receive successfully,total content is %dbytes!\n", __LINE__,buffer, len);
	}
	if (memcmp(buffer, successfullyReplyStr, sizeof(successfullyReplyStr) -1) == 0){
		Handel_DESCRIBE(buffer, sockfd);//给服务器发RTSP的DESCRIBE协议
	}
	else{
		printf("_____%d_____the OPTION answer Msg is wrong!\n", __LINE__);
		return 0;
	}
	bzero(buffer, MAXBUF + 1);
    /* DESCRIBE Reply */
    len = recv(sockfd, buffer, MAXBUF, 0);/* 确认服务器回复OPTION Reply */
    if (len < 0){
        printf("_____%d_____DESCRIBE Reply Msg:\n %s receive error! error code is %d, error content is  %s \n",  __LINE__,buffer, errno, strerror(errno));
	}
    else{
        printf("_____%d_____DESCRIBE Reply Msg:\n %s receive successfully,total content is %dbytes!\n", __LINE__,buffer, len);
	}
	if (memcmp(buffer, successfullyReplyStr, sizeof(successfullyReplyStr)-1) == 0){
		Handel_SETUP(buffer, sockfd);//给服务器发RTSP的SETUP协议
	}
	else{
		printf("_____%d_____the DESCRIBE answer Msg is wrong!\n",__LINE__);
		return 0;
	}
	bzero(buffer, MAXBUF + 1);    
    len = recv(sockfd, buffer, MAXBUF, 0);/* 确认服务器回复OPTION Reply */
    if (len < 0){
	    printf("_____%d_____SETUP Reply Msg:\n %s receive error! error code is %d, error content is  %s \n", __LINE__,buffer, errno, strerror(errno));
	}
    else{
		printf("_____%d_____SETUP Reply Msg:\n %s receive successfully,total content is %dbytes!\n", __LINE__,buffer, len);
	}  
	char sessionIdTmp[30];
	bzero(sessionIdTmp,sizeof(sessionIdTmp));
	strsearch(buffer, "Session: ", 9, sessionIdTmp);
	printf("___%d___sessionIDTmp:%s\n",__LINE__,sessionIdTmp);
	if (memcmp(buffer, successfullyReplyStr, sizeof(successfullyReplyStr)-1) == 0)
	{
		char searchStrTmp[13]="port=";
		udpPort=intsearch(buffer,searchStrTmp,5);
		if(udpPort==0){
			return 0;
		}
		printf("the udp port is %d\n",udpPort);
		
		bzero(&RTPAddr,sizeof(RTPAddr));
		bzero(&RTCPAddr,sizeof(RTCPAddr));
		
		rtpsockfd  = init_udpsocket(udpPort, &RTPAddr, mcast_addr);
		//rtcpsockfd = init_udpsocket(udpPort+1, &RTCPAddr, mcast_addr);
	
		Handel_PLAY(buffer, sockfd,sessionIdTmp);//给服务器发RTSP的SETUP协议
	}
	else
	{
		printf("_____%d_____the SETUP answer Msg is wrong!\n",__LINE__);
		return 0;
	}
	bzero(buffer, MAXBUF + 1);
	len = recv(sockfd, buffer, MAXBUF, 0);/* 确认服务器回复OPTION Reply */
    if (len < 0){	
		printf("_____%d_____PLAY Reply Msg:\n %s receive error! error code is %d, error content is  %s \n", __LINE__,buffer, errno, strerror(errno));
	}else{
		printf("_____%d_____PLAY Reply Msg:\n %s receive successfully,total content is %dbytes!\n", __LINE__,buffer, len);
	}    
	if (memcmp(buffer, successfullyReplyStr, sizeof(successfullyReplyStr)-1) != 0){
		printf("_____%d_____the play answer Msg is wrong!\n",__LINE__);
		return 0;
	}
	printf("_____%d_____now,receiving RTP packets data......\n",__LINE__);

关键函数

int  H264rtp_Handle(unsigned char *write_buf, int write_size)
{
	rtp_header_t *rtp_header = (rtp_header_t *)write_buf;//获取RTP的包头
	unsigned char *rtp_payload = write_buf + RTP_HEADER_LEN; // 地址偏移获取数据起始位置
	uint8_t nalu_type = rtp_payload[0] & 0x1F; // NaluHeader的后5  FuIndicator的后5位,参考NALU头和FU分包头的描述,在这里可以区分它们
	stream_p pointer_addr = calloc(sizeof(stream), 1);
	pointer_addr->stream_buffer = NULL;
	if (pointer_addr == NULL)
	{
		perror("calloc failed");
		return 0;
	}
	if (nalu_type == 0x1C) // 0x1C为FU包否则为NALU包
	{
		uint8_t fua_type = rtp_payload[1] & 0xE0; // FuHeader 的前三位,首包为100b,中间包000,尾包010参考FU包的介绍
		int header_len = RTP_HEADER_LEN + FU_INDICATOR_LEN + FU_HEADER_LEN;
		unsigned char *nalu_payload = write_buf + header_len; // 地址偏移
		if (fua_type == 0x80) // Fu包为 Nalu的起始位置,需要写入 NaluStarter + NaluHeader + NaluPayload.
		{
			uint8_t nalu_header = (rtp_payload[0] & 0xE0) | (rtp_payload[1] & 0x1F); // FuIndicator 的前3位和 FuHeader的后5
			int input_size = NALU_STARTER_LEN + NALU_HEADER_LEN + (write_size - header_len);
			pointer_addr->stream_buffer = calloc(input_size,1);
			if (pointer_addr->stream_buffer == NULL)
			{
				perror("calloc failed");
				return 0;
			}
			memset(pointer_addr->stream_buffer, 0, input_size);
			pointer_addr->stream_buffer[NALU_STARTER_LEN - 1] = 1; // NaluStarter - [00, 00, 00, 01]
			memcpy(pointer_addr->stream_buffer + NALU_STARTER_LEN, &nalu_header, NALU_HEADER_LEN); // NaluHeader
			memcpy(pointer_addr->stream_buffer + NALU_STARTER_LEN + NALU_HEADER_LEN, nalu_payload, write_size - header_len); // NaluPayload
			pointer_addr->stream_size = input_size;
			tail_insert_in_ker_list(stream_head, pointer_addr);
			printf("111%d\n", input_size);
		}
		else // Fu包为 Nalu的其他位置,只需要写入 NaluPayload.
		{
			int input_size = write_size - header_len;
			pointer_addr->stream_buffer = calloc(input_size, 1);
			if (pointer_addr->stream_buffer == NULL)
			{
				perror("calloc failed");
				return 0;
			}
			memset(pointer_addr->stream_buffer, 0, input_size);
			memcpy(pointer_addr->stream_buffer, nalu_payload, input_size); // NaluPayload
			pointer_addr->stream_size = input_size;
			tail_insert_in_ker_list(stream_head, pointer_addr);
			printf("222%d\n", input_size);
		}
	}
	else // 完整 Nalu 包需要写入 NaluStarter + NaluHeader + NaluPayload.
	{
		int input_size = NALU_STARTER_LEN + (write_size - RTP_HEADER_LEN);
		pointer_addr->stream_buffer = calloc(input_size, 1);
		if (pointer_addr->stream_buffer == NULL)
		{
			perror("calloc failed");
			return 0;
		}
		memset(pointer_addr->stream_buffer, 0, input_size);
		pointer_addr->stream_buffer[NALU_STARTER_LEN - 1] = 1; // NaluStarter - [00, 00, 00, 01]
		memcpy(pointer_addr->stream_buffer + NALU_STARTER_LEN, rtp_payload, write_size - RTP_HEADER_LEN); // RtpPayload = NaluHeader + NaluPayload.
		pointer_addr->stream_size = input_size;
		tail_insert_in_ker_list(stream_head, pointer_addr);
		printf("3333%d\n", input_size);
	}
}

效果解包数据保存到文件,用VLC成功播放~~

在这里插入图片描述

代码下载(点这)

写博客不易,对您有帮助记得收藏点赞谢谢~~~,有什么问题可以咨询 VX:a812417530

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

I&You

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值