今天在调试程序,发现跑一会儿就会有问题(图像花屏),但是把保存下来的纯视频用暴风播就很正常。
后来在程序中加断点,发现每当花屏的时候,发现对应帧的长度不对,而且帧标志会有一次重复。经过流程的分析,确定是帧信息分析的代码问题。
由于音视频数据往往有着巨大的数据需要分析,所以在帧分析的位置添加打印,而不是单纯的断点。经过一个下午的跟踪,终于问题解决,原因就是map的一个特点之前没有碰到过,所以在这样大量项存在的情况下问题爆发了。
原始的代码逻辑如下:
map<UINT16, FrameInfo> m_mapFrame; // 这是一个类中的全局变量
void GotAVData( char *buf, int len )
{
put_data_to_map( buf, len, m_mapFrame ); //通过对buf的分析,拆分成一帧一帧后放入到m_mapFrame中
FrameInfo fi;
map<UINT16, FrameInfo>::iterator iter;
iter = m_mapFrame.begin();
while ( iter != m_mapFrame.end() )
{
fi = iter->second;
put_frame_to_decoder(fi);
iter = m_mapFrame.erase(iter);
}
}
FrameInfo结构中有一个帧ID字段:UINT16 fid,其值的范围为:0~65535,m_mapFrame的key也就是这个fid。当值达到65535后,再返回到0开始。
以上代码是每次收到服务器发送过来的视频数据时就会调用。在put_data_to_map()中,每获取到一帧数据后,加入map的动作如下:
FrameInfo fi;
... // 分析数据并对fi附值
m_mapFrame[fi.fid] = fi;
显然,在put_data_to_map()中,最后来的数据,应该是最后才加入到了m_mapFrame中,在GotAVData()中用m_mapFrame.begin()获取它最早加入的项,但是结果不对。
比如一次性先后加入了如下的帧ID到m_mapFrame中:
65533,65534,65535,0,1,2,3,4,5,6,7
则在GotAVData()中的iter = m_mapFrame.begin();一行,得到的结果iter却是fid为0的帧,即不是第一个加入的65533!
这个情况导致我的判断不正常,从而导致拿到的帧数据是不正常的,最终导致解码出来的图像花掉。
后做如下修改解决问题,总体思路是记录当前取到的一个帧ID,然后固定地取对应帧ID的项,主要是对GotAVData()中的m_mapFrame操作进行了修改,大到如下:
void GotAVData( char *buf, int len )
{
put_data_to_map( buf, len, m_mapFrame ); //通过对buf的分析,拆分成一帧一帧后放入到m_mapFrame中
FrameInfo fi;
map<UINT16, FrameInfo>::iterator iter;
while ( GetAFrame(m_mapFrame, iter) )
{
fi = iter->second;
put_frame_to_decoder(fi);
}
}
其中的GetAFrame()大致如下:
bool GetAFrame( map<UINT16, FrameInfo> mapFrame, map<UINT16, FrameInfo>::iterator &iter )
{
static UINT6 frmId = 0;
iter = mapFrame.Find(frmId);
frmId++;
if ( iter == mapFrame.end() )
return false;
return true;
}
以上代码只是逻辑上的,仅用于展示问题,实际的工程代码还加入了异常处理的内容。
本文仅用于记录之前没有注意到的一个map使用上的问题,大致包含了问题的出现情况和解决办法。