经过大量的实验后,发现了如下几个BUG:
1、客户机的关闭导致服务器崩溃,出现断错误
2、当服务器段错误出现后,再次开启服务器无法再次进行视频编码,移除视频文件后正常
3、打开多个客户端时帧率下降很快
4、打开多个客户端时会发生死机现象
BUG1
首先这个问题和客户机有关,我们把代码定位在net.c函数中。在仔细分析,问题应该在处理客户机连接的线程里面,我们使用gdb调试,发现当关闭后调用
//检测客户机是否断开
getsockopt(c->sock, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);
函数时发生了段错误。这个函数是用来检测客户机是否断开的,如果断开,则清除和这次连接有关的所有事件:
//关闭sock
close(c->sock);
epoll_del_event(c->srv->epfd, c->ev_tx);
epoll_del_event(c->srv->epfd, c->ev_tx);
free(c);
但是在检测到断开,并释放相关变量后没有使用break跳出while,导致下次执行getsockopt函数时发生段错误。因此修改如下:
while(1)
{
//检测客户机是否断开
getsockopt(c->sock, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);
if((info.tcpi_state==TCP_ESTABLISHED))
{
fds = epoll_wait(c->srv->epfd, events, 10, 1000);
for(i=0; i<fds; i++)
{
event = events[i].events;
e = events[i].data.ptr;
if((event & EPOLLIN) && (e->events & EPOLLIN))
{
e->handler(e->fd, e->arg);
}
if((event & EPOLLOUT) && (e->events & EPOLLOUT))
{
e->handler(e->fd, e->arg);
}
if((event & EPOLLERR) && (e->events & EPOLLERR))
{
e->handler(e->fd, e->arg);
}
}
}
else
{
//关闭sock
close(c->sock);
epoll_del_event(c->srv->epfd, c->ev_tx);
epoll_del_event(c->srv->epfd, c->ev_tx);
free(c);
break;
}
}
修改后这个BUG完美解决。
BUG2
首先可以确定BUG2的问题应该是由BUG1引起的。由于代码非正常退出,导致视频文件没有写文件尾,或者发生了某种损坏,导致下次编码无法再次进行。
BUG3
BUG3的原因很好发现,因为服务器中的摄像头子系统和网络子系统之间需要互斥访问图片文件。摄像头子系统负责写图片,网络子系统负责读图片,如果连接了很多客户机,那么只有当所有的客户机都读取完图片之后,服务器才可以对图片文件进行写操作,因此,这种方法大大的降低了客户机帧率。
在运行过程中,帧率情况如下:
1个客户机 80pts左右
2个客户机 50pts左右
3个客户机 30pts左右
4个客户机 20pts左右
所以连接的客户机越多,帧率越低。
先来分析一下到底能不能优化帧率,摄像头输出格式是yuv的,如果要压缩成jpg格式的图片从而在客户端显示,那么必定要把图片文件给保存起来,这样的话帧率就无法优化帧率了。同时我们知道V4L2的帧缓冲存放的是图片原始数据,摄像头输出的图片直接写入这些缓存中,我们可以认为写的时间可以忽略,如果把缓存数据直接拷贝出来发送给客户机,也就不需要互斥访问了。所以有2中方法:
1、如果GTK可以直接显示YUV格式的图片,则直接发送YUV图片的原始数据
2、如果GTK不能显示YUV格式的图片,则把YUV原始数据的处理交给客户端来做
BUG4
在查阅相关资料后,发现死机是由于2个或2个以上的线程访问同一段代码,但是这段代码没有做可重入处理造成的。在整个工程中,tx_hander和rx_hander函数及其子函数。具体的可重入函数的实现方法见
总结
总体来说系统大部分的功能都可以正常实现,在这其中也学习到了很多关于线程编程的东西,比如说访问共享资源需要互斥,访问相同代码需要做可重入处理等等。但是系统的总体性能还不如意,我在想能不能用UDP的方式来实现视频监控而不是TCP,或许这样能很好的提高帧率。这个想法就交给以后来实现好了。