海思Hi3531||瑞芯微RK1109用rtsp服务器实现推流
了解RTP包头
一般RTSP流都是以UDP协议为主,每个包的包头(12个字节)我们称为RTP包头
了解H264的nalu头
RTP包的格式一:RTP包头+nalu头(一个字节 )+数据
格式二:RTP包头+fu分包头(两个字节)+数据
格式一
当nalu数据小于等于RTP包最大长度时会采用
nalu头格式:
F:forbidden_zero_bit.1 位,如果有语法冲突,则为 1。当网络识别此单元存在比特错误时,可将其设为 1,以便接收方丢掉该单元。
NRI:nal_ref_idc.2 位,用来指示该NALU 的重要性等级。值越大,表示当前NALU越重要。具体大于0 时取何值,没有具体规定。
Type:5 位,指出NALU 的类型。具体如表所示:
格式二
当nalu数据大于RTP包最大长度时会采用
fu分包头格式:
第一个字节:
跟上面nalu头一样,只是Type=28而已
第二个字节:
S: 1 bit 当设置成1,开始位指示分片NAL单元的开始。当跟随的FU荷载不是分片NAL单元荷载的开始,开始位设为0。
E: 1 bit 当设置成1, 结束位指示分片NAL单元的结束,即, 荷载的最后字节也是分片NAL单元的最后一个字节。
当跟随的FU荷载不是分片NAL单元的最后分片,结束位设置为0。
如果不是首包和尾包S和E设置为0
R: 1 bit
保留位必须设置为0,接收者必须忽略该位。
Type: 5 bits
此处的Type就是NALU头中的Type,取1-23的那个值,表示 NAL单元荷载类型定义
服务器源码
main函数
RTSP_PROGRESS_1函数
EventLoop_1函数
ScheduleConnections函数调用RtspServer
RtspServer函数
这个函数是RTSP服务器处理接收的各种数据并返回数据的过程。如果调用更改函数出现ERR_CONNECTION_CLOSE和ERR_GENERIC这两种错误,会进行一些资源的释放,主要是连接出现问题后的处理。
int RtspServer(RTSP_buffer *rtsp, int rtsp_num)
{
fd_set rset,wset; /*读写I/O描述集*/
struct timeval t;
int size;
static char buffer[RTSP_BUFFERSIZE+1]; /* +1 to control the final '\0'*/
int n;
int res;
struct sockaddr ClientAddr;
#ifdef RTSP_DEBUG
// fprintf(stderr, "%s, %d\n", __FUNCTION__, __LINE__);
#endif
// memset((void *)&ClientAddr,0,sizeof(ClientAddr));
if (rtsp == NULL)
{
return ERR_NOERROR;
}
/*变量初始化*/
FD_ZERO(&rset);
FD_ZERO(&wset);
t.tv_sec=0; /*select 时间间隔*/
t.tv_usec=100000;
FD_SET(rtsp->fd,&rset);
/*调用select等待对应描述符变化*/
if (select(g_s32Maxfd+1,&rset,0,0,&t)<0)
{
fprintf(stderr,"select error %s %d\n", __FILE__, __LINE__);
send_reply(500, NULL, rtsp);
return ERR_GENERIC; //errore interno al server
}
/*有可供读进的rtsp包*/
if (FD_ISSET(rtsp->fd,&rset))
{
memset(buffer,0,sizeof(buffer));
size=sizeof(buffer)-1; /*最后一位用于填充字符串结束标识*/
/*读入数据到缓冲区中*/
#ifdef RTSP_DEBUG
// fprintf(stderr, "tcp_read, %d\n", __LINE__);
#endif
n= tcp_read(rtsp->fd, buffer, size, &ClientAddr);
if (n==0)
{
return ERR_CONNECTION_CLOSE;
}
if (n<0)
{
fprintf(stderr,"read() error %s %d\n", __FILE__, __LINE__);
send_reply(500, NULL, rtsp); //服务器内部错误消息
return ERR_GENERIC;
}
//检查读入的数据是否产生溢出
if (rtsp->in_size+n>RTSP_BUFFERSIZE)
{
fprintf(stderr,"RTSP buffer overflow (input RTSP message is most likely invalid).\n");
send_reply(500, NULL, rtsp);
return ERR_GENERIC;//数据溢出错误
}
#ifdef RTSP_DEBUG
fprintf(stderr,"INPUT_BUFFER was:%s\n", buffer);
#endif
/*填充数据*/
memcpy(&(rtsp->in_buffer[rtsp->in_size]),buffer,n);
rtsp->in_size+=n;
//清空buffer
memset(buffer, 0, n);
//添加客户端地址信息
memcpy( &rtsp->stClientAddr, &ClientAddr, sizeof(ClientAddr));
/*处理缓冲区的数据,进行rtsp处理*/
if ((res=RTSP_handler(rtsp, rtsp_num))==ERR_GENERIC)
{
fprintf(stderr,"Invalid input message.\n");
return ERR_NOERROR;
}
}
/*有发送数据*/
if (rtsp->out_size>0)
{
//将数据发送出去
n= tcp_write(rtsp->fd,rtsp->out_buffer,rtsp->out_size);
if (n<0)
{
fprintf(stderr,"tcp_write error %s %i\n", __FILE__, __LINE__);
send_reply(500, NULL, rtsp);
return ERR_GENERIC; //errore interno al server
}
#ifdef RTSP_DEBUG
//fprintf(stderr,"OUTPUT_BUFFER length %d\n%s\n", rtsp->out_size, rtsp->out_buffer);
#endif
//清空发送缓冲区
memset(rtsp->out_buffer, 0, rtsp->out_size);
rtsp->out_size = 0;
}
//如果需要RTCP在此出加入对RTCP数据的接收,并存放在缓存中。
//继而在schedule_do线程中对其处理。
//rtcp控制处理,检查读入RTCP数据报
return ERR_NOERROR;
}
RTSP_handler函数
RTSP_state_machine函数
主要调用RTSP_state_machine(pRtspBuf, s32Meth);函数来进行各种客户端请求报文的处理,然后应答对应的响应报文。
附图,客户端和服务器的RTSP交互协议
schedule1_do函数
函数太长就不贴出来了
这个是在RTSP_PROGRESS_1中的ScheduleInit_1函数中调用
schedule_do主要功能就是打开h264文件调用RtpSend函数把数据打包RTP发送出去。
VLC播放成功
源码下载
写博客不易,如果对你有关注,记得点赞收藏谢谢~~~~;有问题可加VX:a812417530