海思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