librtsp源码问题记录


librtsp近一周给调稳定了, 确实发现了好几个问题,记录下。

  1. 多线程涉及的线程安全问题。

  2. 长时间运行稳定性问题,内存泄漏问题。

  3. 支持TCP报文分发;

  4. 修改BUG等等。


1、多线程涉及的线程安全问题:

schedule_do线程负责RTP包的转发,但这个线程用到了主线程中创建的rtsp_session对象,在某些场景下退出会出现奔溃。

rtsp客户端结束播放退出时,rtp_session 的释放交给schedule_do线程负责。


修改schedule_remove方法,不置空rtp_session对象。

int schedule_remove(int id)
{
    sched[id].valid=0;
    //这里暂时不置空
    //sched[id].rtp_session = NULL;
    sched[id].BeginFrame=0;
    printf("(schedule_remove id:)%d\n", id);
    return ERR_NOERROR;
}

到了发送失败后才销毁rtp_session和hrtp对象:

schedule_do方法修改:
void *schedule_do(void *arg)
{
                RTP_session *rtp_session = sched[i].rtp_session;
            	if (rtp_session != NULL){
	    			printf("FREE, rtp_session->hndRtp:%d,sched[i].rtp_session:%d\r\n",  (int)rtp_session->hndRtp, (int)sched[i].rtp_session);  
		        	RtpDelete((unsigned int)rtp_session->hndRtp);
            		free(sched[i].rtp_session);
            		sched[i].rtp_session = NULL;
            		rtp_session->hndRtp = NULL;
            	}



2、稳定性问题。

长时间运行后,主线程不再接收新的rtsp请求,导致播放失败。

原来代码线程关系比较繁琐,导致主线程可能不处理新的rtsp请求。修改为epoll负责监听rtsp的连接,然后分发给子线程处理。

int set_nonblocking(int fd) {
    int flags, s;
    flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1) {
        perror("fcntl F_GETFL");
        return -1;
    }
    flags |= O_NONBLOCK;
    s = fcntl(fd, F_SETFL, flags);
    if (s == -1) {
        perror("fcntl F_SETFL");
        return -1;
    }
    return 0;
} 

// 处理客户端连接的函数
void* handle_client(void *arg) {
	int listen_fd = (int)arg;
	printf("handle_client:%d\n", listen_fd);
	// 处理所有新到来的连接
	EventLoopOneClient(listen_fd); 
	printf("handle_client:%d exit\n", listen_fd);
	pthread_detach(pthread_self());
	return NULL;
}

static int rtsp_server_running = 0;
static void* rtsp_server_thread(void *arg){ 
       UNUSED(arg);    
       int epoll_fd;
       int listen_fd = s32MainFd;
       
	struct timespec ts = { 2, 0 };
        printf("g_s32Quit:%d, s32MainFd:%d", g_s32Quit, s32MainFd);
        // 设置非阻塞
        if (set_nonblocking(s32MainFd) == -1) {
	    close(listen_fd);
	    printf("%s\n", __FUNCTION__);
	    return NULL;
        }
	// 创建epoll实例
	epoll_fd = epoll_create1(0);
	if (epoll_fd == -1) {
		perror("epoll_create1");
		close(listen_fd);
		exit(EXIT_FAILURE);
	}

	// 将监听套接字添加到epoll
	struct epoll_event event;
	event.events = POLL_IN;
	event.data.fd = listen_fd;
	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event) == -1) {
		perror("epoll_ctl: listen_fd");
		close(listen_fd);
		close(epoll_fd);
		exit(EXIT_FAILURE);
	} 

	// 事件循环
	struct epoll_event events[MAX_EVENTS];

	while (!g_s32Quit)
	{
		int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
		if (n == -1) {
		    if (errno == EINTR)
		        continue;
		    perror("epoll_wait");
		    break;
		}
        	for (int i = 0; i < n; ++i) {
		    if (events[i].data.fd == listen_fd) {
			pthread_t threads_id;
				
			    struct sockaddr_in client_addr;
			    socklen_t client_len = sizeof(client_addr);
			    int client_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_len);
			    if (client_fd == -1) {
			        if (errno == EAGAIN || errno == EWOULDBLOCK)
			            continue; // 所有连接都已处理
			        else {
			            perror("accept");
			            continue;
			        }
			    }
        		printf("listen_fd:%d, enter\r\n", client_fd);
			if (pthread_create(&threads_id, NULL, handle_client, (void *)client_fd) != 0) {
				continue;
			}
		    }
		}

		nanosleep(&ts, NULL);
	}
	
	
	// 清理资源
	close(listen_fd);
	close(epoll_fd);

	return NULL;
}

EventLoopOneClient方法修改:

EventLoopOneClient

extern int g_s32Quit; 
void ScheduleOneConnections(RTSP_buffer *pRtsp)
{
    int res; 
    RTP_session *r=NULL, *t=NULL;
    
    if (pRtsp == NULL){
        return;
    }

#ifdef RTSP_DEBUG
    //printf( "%s\n", __FUNCTION__);
#endif

    while (!g_s32Quit)
    {
        if ((res = RtspServer(pRtsp, 1))!=ERR_NOERROR)
        {
            if (res==ERR_CONNECTION_CLOSE || res==ERR_GENERIC )
            {
                /*连接已经关闭*/
                if (res==ERR_CONNECTION_CLOSE){
                    printf("fd:%d,RTSP connection closed by client,get_schedule_valid:%d .\n",pRtsp->fd, get_schedule_valid(pRtsp->fd)); 
                }else{
                	printf("fd:%d, ,get_schedule_valid:%d RTSP connection closed by server.\n",pRtsp->fd, get_schedule_valid(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);
					}
                    printf("WARNING! fd:%d RTSP connection truncated before ending operations.\n",pRtsp->fd);
                }

                // wait for
                close(pRtsp->fd);
                //--*conn_count;
                num_conn--;

                /*释放rtsp缓冲区*/
                free(pRtsp);
                
                return;
            } else{
            
	    	printf("6-0-get_schedule_valid:%d\r\n", get_schedule_valid(pRtsp->fd)); 
            }
        }
        else
        {
	    printf("6:%d\r\n",get_schedule_valid(pRtsp->fd)); 
        }
    }
}
void EventLoopOneClient(int s32Fd)
{
//	static unsigned int s32ChdCnt=0;
	static int s32ConCnt = 0;//已经连接的客户端数 
	RTSP_buffer *pRtspList=NULL; 
  
	printf("%s\n", __FUNCTION__); 

	printf("%s-s32Fd:%d\n", __FUNCTION__, s32Fd);
	/*处理新创建的连接*/
	if (s32Fd >= 0)
	{ 
		++s32ConCnt;
		AddClient(&pRtspList,s32Fd); 
		printf( "%s Connection reached: %d\n", __FUNCTION__, num_conn); 
	}else{
		return;
	}

	/*对已有的连接进行调度*/
	printf("27-s32ConCnt:%d-\r\n", s32Fd);
	ScheduleOneConnections(pRtspList);
	printf("28-s32ConCnt:%d-\r\n", s32Fd);
}


3、支持TCP报文分发:

static int sendNalu264UseTCP(HndRtp hRtp, char *pSendBuf, int s32Bytes){
    if (hRtp == NULL || pSendBuf == NULL){
        return -1;
    }
    
    if (hRtp->tcpSock <= 0){
        return -1;
    }
    //printf("---111RTP_rtp_avp_tcp len:%d", s32Bytes);
		                    //Transport.rtp_fd
    u_int8_t framingHeader[4];
    framingHeader[0] = '$';
    framingHeader[1] =  0;//Transport.u.tcp.interleaved.RTP
    framingHeader[2] = (u_int8_t) ((s32Bytes&0xFF00)>>8);
    framingHeader[3] = (u_int8_t) (s32Bytes&0xFF);
	     
    int ret = tcp_write(hRtp->tcpSock, (char *)framingHeader, sizeof(framingHeader));     
    if (ret == -1){
        //peer-close
        close(hRtp->tcpSock);
    	hRtp->tcpSock = -1;
		    	
	if(hRtp->s32Sock >= 0) {
	    //
	    close(hRtp->s32Sock);
	    hRtp->s32Sock = -1;
	} 
        return -1;
    }
    ret = tcp_write(hRtp->tcpSock, (char *)pSendBuf, s32Bytes);   
    if (ret == -1){
        close(hRtp->tcpSock);
        //peer-close
    	hRtp->tcpSock = -1;
	if(hRtp->s32Sock >= 0)
	{
	    close(hRtp->s32Sock);
	    hRtp->s32Sock = -1;
	} 
        return -1;
    }
    return 0;
}


4、修复一点小BUG: 开始时TCP接入和RTP普通接入 有播放器总是播放黑屏和跳帧,发现是少了一些字节。

RtpSend处理_h264分发的时候存在BUG,最后一个包不对。

修改前的代码:

        if(pData > pNalBuf)
        {
            s32NalSize = (int)(pData - pNalBuf);
            if(SendNalu264(hRtp, pNalBuf, s32NalSize) == -1)
            {
                return -1;
            }
        }

修改后的代码:

        if(pData > pNalBuf)
        {
            s32NalSize = (int)(pDataEnd - pNalBuf) ;
            if(SendNalu264(hRtp, pNalBuf, s32NalSize, 1) == -1)
            {
                return -1;
            }
        }
        
SendNalu264的FU-A组包包头有问题,修改后。
     if (s32NaluRemain == (s32NalBufSize - 1)){
            //if (s32NaluRemain == s32NalBufSize){
                *(pSendBuf + 12) = (u8NaluBytes & 0x60) | 28;
                *(pSendBuf + 13) = (u8NaluBytes & 0x1f) | (1<<7);
            }else{
                *(pSendBuf + 12) = 28;
                if (s32NaluRemain < MAX_RTP_PKT_LENGTH) {
                	*(pSendBuf + 13) = ((u8NaluBytes & 0x1f) | (1<<6)); 
			if (markEnd != -1){
                		*(pSendBuf + 13) = ((u8NaluBytes & 0x1f) | (markEnd<<6));  
			}
                }else{
                	*(pSendBuf + 13) = (u8NaluBytes & 0x1f) ; 
                } 
            }


呱牛笔记

-------------------广告线---------------
项目、合作,欢迎勾搭,邮箱:promall@qq.com


本文为呱牛笔记原创文章,转载无需和我联系,但请注明来自呱牛笔记 ,it3q.com

请先登录后发表评论
  • 最新评论
  • 总共0条评论