最近使用Live555遇到一个堆栈溢出问题,各位可能也会遇到,写出来分享一下;
应用场景如下图所示:
Mediaserver是基于live555的一个流媒体服务器,负责解析platform送过来的码流,并建立RTSP服务;decoder向mediaserver发送RTSP请求,收到码流后再进行处理;
现在的情况是mediaserver有概率会出现堆栈溢出,通过日志分析,堆栈溢出时platform没有送码流,而decoder仍然不断向mediaserver发送RTSP请求;根据崩溃dump中显示的堆栈调用层次,走读代码并画出程序处理流程图如下:
乍一看这个流程图可能会有点晕,不捉急,先来回顾一下live555框架设计的基本思想;
1) live555框架是单线程结构,所有任务都是单线程执行;
2) live555所有任务都会在doEventLoop函数中执行;
3) live555网络模型是select模型,有连接到来时会把相应的socket加入到select集合中;扫描处理完select中的socket之后,再进行tasklist处理,具体可以参考doEventLoop函数源码。
再了解一下RTSP请求的基本流程,一般来说就四个:
OPTION-------DESCRIBE---------SETUP----------PLAY
好了,关于本文,框架相关的了解上面这些就差不多了,下面看具体流程处理:
1) 正常没有请求时,doEventLoop循环;
2) 收到RTSP请求(这里具体指describe请求),live555需要从码流中解析出SDP信息,然后回送给请求方;
问题就在这里了,由于platform并没有送码流给mediaserver,所以live555就取不到码流进行解析,那么解析SDP信息的任务就卡住了;
下面是一段解析SDP相关的代码:
无论解析SDP信息成功还是失败,都会调用doEventLoop函数,实际上就是在当前执行的loop1中新建了一个loop2; 分两种情况讨论:
a) 解析SDP成功
解析成功后fDoneFlag是有效的,loop2会直接跳转到break;语句,退出循环,getAuxSDPLine可以直接返回;
b) 解析SDP失败
解析失败,先来看检验解析结果的函数checkForAuxSDPLine
函数最后一行就是把checkForAuxSDPLine任务重新添加到tasklist中,此后会在loop2中调用tasklist,执行SDP信息检验任务;若SDP信息获取完成,loop2自然会退出,loop1中的getAuxSDPLine也可以直接返回;
但在某些情况下,如platform不送码流给mediaserver,live555是解析sdp的任务就会永远卡在loop2,无法进行下去; decoder发送RTSP请求后,迟迟收不到响应包,就会重发RTSP请求,而这个新的RTSP请求会被loop2接收,重新再走一遍DESCRIBE命令处理流程,由于还是获取不到SDP信息,所以会新建循环loop3,然后卡在loop3里面……….
loop1
{
loop2
{
loop3
{…}
}
}
这样再对比上面的流程图的就很清楚了,decoder每发一个RTSP请求,就会多一层loop的嵌套,发个几百个请求堆栈自然就溢出了。
既然知道了问题原因,那么解决就比较简单了,只要能做到避免嵌套的方案都可以;这里提供一个简单的计数方案:
handleCmd_DESCRIBE函数中有一个mediasession的计数:
session->incrementReferenceCount();
只要做一个计数的限制,比如说count值大于10,就在subsession中调用setDoneFlag函数退出循环。