由于hi3516a海思自带的开发应用程序是通过摄像头接口或HDMI接口获取视频数据后并进行存储。然而,在实际应用中,多是获取数据后直接通过网络把数据发送出去。那么本文章将开始学习hi3516a获取数据后通过网线和RTP协议把数据实时发送出去。
背景:hi3516a开发板通过HDMI接口获取BT1120数据后进行压缩,并通过RTP协议进行实时的视频直播。
硬件平台:hi3516a
软件平台:Hi3516A_SDK_V1.0.5.0
视频数据接口:HDMI
下面继续上一篇文章的源码分析,分析void EventLoop(int s32MainFd)函数。
void EventLoop(int s32MainFd)
{
static int s32ConCnt = 0;//已经连接的客户端数
int s32Fd = -1;
static RTSP_buffer *pRtspList=NULL;
RTSP_buffer *p=NULL;
unsigned int u32FdFound; //找到fd标识,找到就置1
/*接收连接,创建一个新的socket*/
if (s32ConCnt!=-1)
{
s32Fd= tcp_accept(s32MainFd);
}
/*处理新创建的连接*/
if (s32Fd >= 0)
{
/*查找列表中是否存在此连接的socket*/
for (u32FdFound=0,p=pRtspList; p!=NULL; p=p->next)
{
if (p->fd == s32Fd)
{
u32FdFound=1;
break;
}
}
if (!u32FdFound)
{
/*创建一个连接,增加一个客户端*/
if (s32ConCnt
上述主要有两个关键点,若链表中没有找到对应的连接,会新建一个连接并调用AddClient(&pRtspList,s32Fd); 在链表中新增一个链表,每个客户端的首次连接会调动该部分。
最后,调用ScheduleConnections(&pRtspList,&s32ConCnt)函数,这个函数主要对已有的连接进行调度。
void ScheduleConnections(RTSP_buffer **rtsp_list, int *conn_count)
{
int res;
RTSP_buffer *pRtsp=*rtsp_list,*pRtspN=NULL;
RTP_session *r=NULL, *t=NULL;
#ifdef RTSP_DEBUG
// fprintf(stderr, "%s\n", __FUNCTION__);
#endif
while (pRtsp!=NULL)
{
if ( (res = RtspServer(pRtsp))!=ERR_NOERROR )
{
if (res==ERR_CONNECTION_CLOSE || res==ERR_GENERIC)
{
/*连接已经关闭*/
if (res==ERR_CONNECTION_CLOSE)
fprintf(stderr,"fd:%d,RTSP connection closed by client.\n",pRtsp->fd);
else
fprintf(stderr,"fd:%d,RTSP connection closed by server.\n",pRtsp->fd);
/*客户端在发送TEARDOWN 之前就截断了连接,但是会话却没有被释放*/
if (pRtsp->session_list!=NULL)
{
r=pRtsp->session_list->rtp_session;
/*释放所有会话*/
while (r!=NULL)
{
t = r->next;
RtpDelete((unsigned int)(r->hndRtp));
schedule_remove(r->sched_id);
r=t;
}
/*释放链表头指针*/
free(pRtsp->session_list);
pRtsp->session_list=NULL;
g_s32DoPlay--;
if (g_s32DoPlay == 0)
{
printf("user abort! no user online now resetfifo\n");
ringreset;
/* 重新将所有可用的RTP端口号放入到port_pool[MAX_SESSION] 中 */
RTP_port_pool_init(RTP_DEFAULT_PORT);
}
fprintf(stderr,"WARNING! fd:%d RTSP connection truncated before ending operations.\n",pRtsp->fd);
}
// wait for
close(pRtsp->fd);
--*conn_count;
num_conn--;
/*释放rtsp缓冲区*/
if (pRtsp==*rtsp_list)
{
//链表第一个元素就出错,则pRtspN为空
printf("first error,pRtsp is null\n");
*rtsp_list=pRtsp->next;
free(pRtsp);
pRtsp=*rtsp_list;
}
else
{
//不是链表中的第一个,则把当前出错任务删除,并把next任务存放在pRtspN(上一个没有出错的任务)
//指向的next,和当前需要处理的pRtsp中.
printf("dell current fd:%d\n",pRtsp->fd);
pRtspN->next=pRtsp->next;
free(pRtsp);
pRtsp=pRtspN->next;
printf("current next fd:%d\n",pRtsp->fd);
}
/*适当情况下,释放调度器本身*/
if (pRtsp==NULL && *conn_count<0)
{
fprintf(stderr,"to stop cchedule_do thread\n");
stop_schedule=1;
}
}
else
{
printf("current fd:%d\n",pRtsp->fd);
pRtsp = pRtsp->next;
}
}
else
{
//没有出错
//上一个处理没有出错的list存放在pRtspN中,需要处理的任务放在pRtst中
pRtspN = pRtsp;
pRtsp = pRtsp->next;
}
}
}
该函数的重点是RtspServer(pRtsp)函数,这个函数是RTSP服务器处理接收的各种数据并返回数据的过程。如果调用更改函数出现ERR_CONNECTION_CLOSE和ERR_GENERIC这两种错误,会进行一些资源的释放,主要是连接出现问题后的处理。下面重点看**RtspServer()**这个函数。
int RtspServer(RTSP_buffer *rtsp)
{
fd_set rset,wset; /*读写I/O描述集*/
struct timeval t;
int size;
static char buffer[RTSP_BUFFERSIZE+1]; /* +1 to control the final '\0'*/ //4096+1
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; //100000us=100ms=0.1s
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))==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);
fprintf(stderr,"S---tcp_write:");
fflush(stderr);
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;
}
RtspServer函数中,主要调用selcet()函数来监控是否有数据发送过来。若收到数据,把数据存到buffer中并调用RTSP_handler(rtsp)进行处理,然后服务器调用tcp_write函数发送应答的数据。RTSP_handler()函数如下:
int RTSP_handler(RTSP_buffer *pRtspBuf)
{
int s32Meth;
#ifdef RTSP_DEBUG
// trace_point();
#endif
while(pRtspBuf->in_size)
{
s32Meth = RTSP_validate_method(pRtspBuf);
if (s32Meth < 0)
{
//错误的请求,请求的方法不存在
fprintf(stderr,"Bad Request %s,%d\n", __FILE__, __LINE__);
printf("bad request, requestion not exit %d",s32Meth);
send_reply(400, NULL, pRtspBuf);
}
else
{
//进入到状态机,处理接收的请求
RTSP_state_machine(pRtspBuf, s32Meth);
printf("exit Rtsp_state_machine\r\n");
}
//丢弃处理之后的消息
RTSP_discard_msg(pRtspBuf);
printf("4\r\n");
}
return ERR_NOERROR;
}
具体RTSP_hander和tcp_write函数应答的数据可参考hi3516a——网络流媒体协议之RTSP协议。主要调用RTSP_state_machine(pRtspBuf, s32Meth);函数来进行各种请求报文的处理,然后应答对应的响应报文。
/*rtsp状态机,服务器端*/
void RTSP_state_machine(RTSP_buffer * pRtspBuf, int method)
{
#ifdef RTSP_DEBUG
trace_point();
#endif
/*除了播放过程中发送的最后一个数据流,
*所有的状态迁移都在这里被处理
* 状态迁移位于stream_event中
*/
char *s;
RTSP_session *pRtspSess;
long int session_id;
char trash[255];
char szDebug[255];
/*找到会话位置*/
if ((s = strstr(pRtspBuf->in_buffer, HDR_SESSION)) != NULL) //在pRtspBuf->in_buffer数据中查找"Session"字符串
{
if (sscanf(s, "%254s %ld", trash, &session_id) != 2) //把s按照"%254s %ld"读取到trash, &session_id中
{
fprintf(stderr,"Invalid Session number %s,%i\n", __FILE__, __LINE__);
send_reply(454, 0, pRtspBuf); /* 没有此会话*/
return;
}
}
/*打开会话列表*/
pRtspSess = pRtspBuf->session_list;
if (pRtspSess == NULL)
{
return;
}
#ifdef RTSP_DEBUG
sprintf(szDebug,"state_machine:current state is ");
strcat(szDebug,((pRtspSess->cur_state==0)?"init state":((pRtspSess->cur_state==1)?"ready state":"play state")));
//strcat把第二个字符串参数拼接到第一个字符串参数
printf("%s\n", szDebug);
#endif
/*根据状态迁移规则,从当前状态开始迁移*/
switch (pRtspSess->cur_state)
{
case INIT_STATE: /*初始态*/
{
#ifdef RTSP_DEBUG
fprintf(stderr,"current method code is: %d \n",method);
#endif
switch (method)
{
case RTSP_ID_DESCRIBE: //状态不变
RTSP_describe(pRtspBuf);
//printf("3\r\n");
break;
case RTSP_ID_SETUP: //状态变为就绪态
//printf("4\r\n");
if (RTSP_setup(pRtspBuf) == ERR_NOERROR)
{
//printf("5\r\n");
pRtspSess->cur_state = READY_STATE;
fprintf(stderr,"TRANSFER TO READY STATE!\n");
}
break;
case RTSP_ID_TEARDOWN: //状态不变
RTSP_teardown(pRtspBuf);
break;
case RTSP_ID_OPTIONS:
if (RTSP_options(pRtspBuf) == ERR_NOERROR)
{
pRtspSess->cur_state = INIT_STATE; //状态不变
}
break;
case RTSP_ID_PLAY: //method not valid this state.
case RTSP_ID_PAUSE:
send_reply(455, 0, pRtspBuf);
break;
default:
send_reply(501, 0, pRtspBuf);
break;
}
break;
}
case READY_STATE:
{
#ifdef RTSP_DEBUG
fprintf(stderr,"current method code is:%d\n",method);
#endif
switch (method)
{
case RTSP_ID_PLAY: //状态迁移为播放态
if (RTSP_play(pRtspBuf) == ERR_NOERROR)
{
fprintf(stderr,"\tStart Playing!\n");
pRtspSess->cur_state = PLAY_STATE;
}
break;
case RTSP_ID_SETUP:
if (RTSP_setup(pRtspBuf) == ERR_NOERROR) //状态不变
{
pRtspSess->cur_state = READY_STATE;
}
break;
case RTSP_ID_TEARDOWN:
RTSP_teardown(pRtspBuf); //状态变为初始态
break;
case RTSP_ID_OPTIONS:
if (RTSP_options(pRtspBuf) == ERR_NOERROR)
{
pRtspSess->cur_state = INIT_STATE; //状态不变
}
break;
case RTSP_ID_PAUSE: // method not valid this state.
send_reply(455, 0, pRtspBuf);
break;
case RTSP_ID_DESCRIBE:
RTSP_describe(pRtspBuf);
break;
default:
send_reply(501, 0, pRtspBuf);
break;
}
break;
}
case PLAY_STATE:
{
switch (method)
{
case RTSP_ID_PLAY:
// Feature not supported
fprintf(stderr,"UNSUPPORTED: Play while playing.\n");
send_reply(551, 0, pRtspBuf); // Option not supported
break;
/* //不支持暂停命令
case RTSP_ID_PAUSE: //状态变为就绪态
if (RTSP_pause(pRtspBuf) == ERR_NOERROR)
{
pRtspSess->cur_state = READY_STATE;
}
break;
*/
case RTSP_ID_TEARDOWN:
RTSP_teardown(pRtspBuf); //状态迁移为初始态
break;
case RTSP_ID_OPTIONS:
break;
case RTSP_ID_DESCRIBE:
RTSP_describe(pRtspBuf);
break;
case RTSP_ID_SETUP:
break;
}
break;
}/* PLAY state */
default:
{
/* invalid/unexpected current state. */
fprintf(stderr,"%s State error: unknown state=%d, method code=%d\n", __FUNCTION__, pRtspSess->cur_state, method);
}
break;
}/* end of current state switch */
#ifdef RTSP_DEBUG
printf("leaving rtsp_state_machine!\n");
#endif
}