hi3516a——RTSP播放H.264视频流(2)

前言

由于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<MAX_CONNECTION)  //#define MAX_CONNECTION	10/*rtsp connection*/
			{
				++s32ConCnt;
				AddClient(&pRtspList,s32Fd);   //在rtsp_buffer链表中新增一个链表
			}
			else
			{
				fprintf(stderr, "exceed the MAX client, ignore this connecting\n");
				return;
			}
			num_conn++;
			fprintf(stderr, "%s Connection reached: %d\n", __FUNCTION__, num_conn);
		}
	}

	/*对已有的连接进行调度*/

	ScheduleConnections(&pRtspList,&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
}

猜你喜欢

转载自blog.csdn.net/u014470361/article/details/89632244