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
