librtsp近一周给调稳定了, 确实发现了好几个问题,记录下。
多线程涉及的线程安全问题。
长时间运行稳定性问题,内存泄漏问题。
支持TCP报文分发;
修改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