在前一篇文章
《数据流量监测工具源玛共享[视频会议/DVR]》提到编写这个一个测试的小工具的目的和实现。在最近的一段测试中,发现这个工具对发现数据走向的诊断十分有效。在shell 上输入rate_stat命令就可以全部显示数据在某模块中的所有走向;输入rate_show命令就可以知道某数据在某个检测点的数据包头信息,比如数据包长度、序列号、尾包标志等参数。
static TCallerInfo g_tCaller[MAX_CALLERS];
static TPacketInfo g_tPacket[MAX_CALLERS];
static NW_INT32 g_nCallers = 0;
static NW_BOOL g_bActivated = FALSE;
static NW_INT32 g_eStreamType = 0;
for (nCaller = 0; nCaller < MAX_CALLERS; nCaller++)
{
ptCaller = &g_tCaller[nCaller];
ptPacket = &g_tPacket[nCaller];
NOTE:编码及测试环境为Tornado2.0(VxWorks 5.0)
a.如何插入监测代码
主要在需要监测的位置添加rate_probe代码段。
b.如何使用rate_stat命令显示所有监测点的信息
在连接到设备的超级终端(SuperTerminal)或Telnet上,输入rate_stat命令,并带上合适的参数。比如,如果要显示视频数据的走向就输入"video"字符串。
c.如何使用rate_show打印某监测点的RTP头信息
RTP 头数据结构定义如下:
typedef struct _tagRtpParam
{
IN OUT NW_UINT32 timestamp;
IN OUT NW_UINT32 marker;
IN OUT NW_BYTE payload;
OUT NW_UINT32 sSrc;
OUT NW_UINT16 sequenceNumber;
OUT NW_INT32 sByte;
OUT NW_INT32 len;
} TRtpParam;
{
IN OUT NW_UINT32 timestamp;
IN OUT NW_UINT32 marker;
IN OUT NW_BYTE payload;
OUT NW_UINT32 sSrc;
OUT NW_UINT16 sequenceNumber;
OUT NW_INT32 sByte;
OUT NW_INT32 len;
} TRtpParam;
如果想知道某个检测点的具体数据情况,可以使用rate_show命令来打印数据信息。
rate_show带三个参数,分别是检测点的ID(这里称caller id)、通道ID以及准备打印的数据包数目。
#ifdef _PACKET_PROBER_
#define MAX_CALLERS 100 /*流量统计监测点的最大数目*/
#define MAX_CHANNELS (MAX_CONF_ATTENDEE_NUM+2)/*监测点通道的最大数目*/
#define MAX_FUNC_NAME_LEN 50 /*监测点调用函数的函数名最大长度*/
#define MAX_HINT_INFO_LEN 80 /*监测点调用函数的函数名最大长度*/
#define TICKS_PER_SECOND 60
#define MAX_CALLERS 100 /*流量统计监测点的最大数目*/
#define MAX_CHANNELS (MAX_CONF_ATTENDEE_NUM+2)/*监测点通道的最大数目*/
#define MAX_FUNC_NAME_LEN 50 /*监测点调用函数的函数名最大长度*/
#define MAX_HINT_INFO_LEN 80 /*监测点调用函数的函数名最大长度*/
#define TICKS_PER_SECOND 60
typedef struct _TagCallerInfo
{
NW_CHAR pchFuncName[MAX_FUNC_NAME_LEN];/*调用函数的函数名*/
NW_CHAR nFuncLen; /*调用函数的函数名实际长度*/
NW_SHORT nLine; /*调用函数的调用点位置*/
{
NW_CHAR pchFuncName[MAX_FUNC_NAME_LEN];/*调用函数的函数名*/
NW_CHAR nFuncLen; /*调用函数的函数名实际长度*/
NW_SHORT nLine; /*调用函数的调用点位置*/
}TCallerInfo;
typedef struct _TagPacketInfo
{
NW_INT32 nPackets[MAX_CHANNELS];/*监测点流经总包数*/
NW_INT32 nPacketRate[MAX_CHANNELS];/*监测点流量*/
NW_INT32 nPrevPackets[MAX_CHANNELS];/*该点上一次监测的累计包数*/
NW_INT32 nPrevTicks[MAX_CHANNELS];/*该点上一次监测的累计时间*/
NW_INT32 nPacketShown[MAX_CHANNELS];/*显示数据包头的数目*/
NW_INT32 nInterval[MAX_CHANNELS];/*前后两次监测的时间跨度*/
NW_BOOL bFrame[MAX_CHANNELS];/*如果统计单位为帧*/
EChannelType eChannelType[MAX_CHANNELS];/*通道类型*/
{
NW_INT32 nPackets[MAX_CHANNELS];/*监测点流经总包数*/
NW_INT32 nPacketRate[MAX_CHANNELS];/*监测点流量*/
NW_INT32 nPrevPackets[MAX_CHANNELS];/*该点上一次监测的累计包数*/
NW_INT32 nPrevTicks[MAX_CHANNELS];/*该点上一次监测的累计时间*/
NW_INT32 nPacketShown[MAX_CHANNELS];/*显示数据包头的数目*/
NW_INT32 nInterval[MAX_CHANNELS];/*前后两次监测的时间跨度*/
NW_BOOL bFrame[MAX_CHANNELS];/*如果统计单位为帧*/
EChannelType eChannelType[MAX_CHANNELS];/*通道类型*/
}TPacketInfo;
static TCallerInfo g_tCaller[MAX_CALLERS];
static TPacketInfo g_tPacket[MAX_CALLERS];
static NW_INT32 g_nCallers = 0;
static NW_BOOL g_bActivated = FALSE;
static NW_INT32 g_eStreamType = 0;
NW_VOID rate_start(void);
NW_VOID rate_stop(void);
NW_BOOL rate_started(void);
NW_VOID rate_print(void);
NW_VOID rate_stop(void);
NW_BOOL rate_started(void);
NW_VOID rate_print(void);
NW_VOID rate_probe(const NW_CHAR* pchFuncName,const NW_SHORT nLine,NW_SHORT* pCallerID,const EChannelType eChannelType,NW_SHORT nChannelID,const EStreamType eStreamType,TRtpParam* pRtpParam)
{
NW_INT32 nCaller = 0;
NW_INT32 nChannel = 0;
NW_INT32 nCurrTicks = 0;
static NW_BOOL bInitialized = FALSE;
TCallerInfo* ptCaller = NULL;
TPacketInfo* ptPacket = NULL;
{
NW_INT32 nCaller = 0;
NW_INT32 nChannel = 0;
NW_INT32 nCurrTicks = 0;
static NW_BOOL bInitialized = FALSE;
TCallerInfo* ptCaller = NULL;
TPacketInfo* ptPacket = NULL;
if (!rate_started())
{
return;
}
if (NULL == pchFuncName || NULL == pCallerID)
{
printf("invalid function name and pos pointer!");
return;
}
{
return;
}
if (NULL == pchFuncName || NULL == pCallerID)
{
printf("invalid function name and pos pointer!");
return;
}
if (nChannelID < 0 || nChannelID >= MAX_CHANNELS)
{
printf("invalid channel id %d!",nChannelID);
return;
}
{
printf("invalid channel id %d!",nChannelID);
return;
}
if (CHANNEL_CALL_ID != eChannelType && CHANNEL_RING_ID != eChannelType )
{
printf("invalid channel type %d!",eChannelType);
return;
}
{
printf("invalid channel type %d!",eChannelType);
return;
}
if (!(eStreamType & g_eStreamType))
{/*过滤不想统计的通道*/
return;
}
if (!bInitialized)
{ /*第一次要初始化*/
memset(&g_tCaller[0],0,sizeof(TCallerInfo)*MAX_CALLERS);
memset(&g_tPacket[0],0,sizeof(TPacketInfo)*MAX_CALLERS);
{/*过滤不想统计的通道*/
return;
}
if (!bInitialized)
{ /*第一次要初始化*/
memset(&g_tCaller[0],0,sizeof(TCallerInfo)*MAX_CALLERS);
memset(&g_tPacket[0],0,sizeof(TPacketInfo)*MAX_CALLERS);
for (nCaller = 0; nCaller < MAX_CALLERS; nCaller++)
{
for (nChannel = 0; nChannel < MAX_CHANNELS; nChannel++)
{
g_tPacket[nCaller].bFrame[nChannel] = FALSE;
}
}
bInitialized = TRUE;
}
{
for (nChannel = 0; nChannel < MAX_CHANNELS; nChannel++)
{
g_tPacket[nCaller].bFrame[nChannel] = FALSE;
}
}
bInitialized = TRUE;
}
if (INVALID_VALUE == *pCallerID)
{ /*如果是某监测点第一次调用该函数,初始值必须为INVALID_VALUE*/
nCaller = g_nCallers++;
ptCaller = &g_tCaller[nCaller];
ptCaller->nFuncLen = strlen(pchFuncName);
{ /*如果是某监测点第一次调用该函数,初始值必须为INVALID_VALUE*/
nCaller = g_nCallers++;
ptCaller = &g_tCaller[nCaller];
ptCaller->nFuncLen = strlen(pchFuncName);
if (ptCaller->nFuncLen > MAX_FUNC_NAME_LEN)
{
printf("too long function name!/n");
return;
}
ptCaller->nFuncLen = sprintf(ptCaller->pchFuncName,"%s",pchFuncName);
ptCaller->nLine = nLine;
*pCallerID = nCaller;/*返回当前监测点的ID,以备后用*/
}
else
{
nCaller = *pCallerID;
}
{
printf("too long function name!/n");
return;
}
ptCaller->nFuncLen = sprintf(ptCaller->pchFuncName,"%s",pchFuncName);
ptCaller->nLine = nLine;
*pCallerID = nCaller;/*返回当前监测点的ID,以备后用*/
}
else
{
nCaller = *pCallerID;
}
ptCaller = &g_tCaller[nCaller];
ptPacket = &g_tPacket[nCaller];
ptPacket = &g_tPacket[nCaller];
if (nCaller >= 0 && nCaller < MAX_CALLERS)
{
if (CHANNEL_CALL_ID != ptPacket->eChannelType[nChannelID]
&& CHANNEL_RING_ID != ptPacket->eChannelType[nChannelID] )
{
ptPacket->eChannelType[nChannelID] = eChannelType;
}
if (NULL != pRtpParam && ptPacket->nPacketShown[nChannelID]>0 && ptPacket->nPacketShown[nChannelID]-->0)
{
printf("%s(%4d):len %4d,marker %d,seq %5d/n",ptCaller->pchFuncName,ptCaller->nLine,pRtpParam->len,pRtpParam->marker,pRtpParam->sequenceNumber);
return;
}
{
if (CHANNEL_CALL_ID != ptPacket->eChannelType[nChannelID]
&& CHANNEL_RING_ID != ptPacket->eChannelType[nChannelID] )
{
ptPacket->eChannelType[nChannelID] = eChannelType;
}
if (NULL != pRtpParam && ptPacket->nPacketShown[nChannelID]>0 && ptPacket->nPacketShown[nChannelID]-->0)
{
printf("%s(%4d):len %4d,marker %d,seq %5d/n",ptCaller->pchFuncName,ptCaller->nLine,pRtpParam->len,pRtpParam->marker,pRtpParam->sequenceNumber);
return;
}
/*等待用户设置检测时间*/
if (1 > ptPacket->nInterval[nChannelID])
{
return;
}
if (1 > ptPacket->nInterval[nChannelID])
{
return;
}
/*如果统计单位是帧*/
if (STREAM_VIDEO == eStreamType)
{
if (NULL != pRtpParam && pRtpParam->marker && !ptPacket->bFrame[nChannelID])
{
ptPacket->bFrame[nChannelID] = TRUE;
}
if (NULL != pRtpParam && ptPacket->bFrame[nChannelID] && !pRtpParam->marker)
{
return;
}
}
if (STREAM_VIDEO == eStreamType)
{
if (NULL != pRtpParam && pRtpParam->marker && !ptPacket->bFrame[nChannelID])
{
ptPacket->bFrame[nChannelID] = TRUE;
}
if (NULL != pRtpParam && ptPacket->bFrame[nChannelID] && !pRtpParam->marker)
{
return;
}
}
ptPacket->nPackets[nChannelID]++;
nCurrTicks = tickGet();
if (0 == ptPacket->nPrevTicks[nChannelID])
{
ptPacket->nPrevPackets[nChannelID] = ptPacket->nPackets[nChannelID];
ptPacket->nPrevTicks[nChannelID] = nCurrTicks;
}
else
{
if (nCurrTicks != ptPacket->nPrevTicks[nChannelID]
&& nCurrTicks-ptPacket->nPrevTicks[nChannelID] >= ptPacket->nInterval[nChannelID])
{
ptPacket->nPacketRate[nChannelID] = TICKS_PER_SECOND*(ptPacket->nPackets[nChannelID] - ptPacket->nPrevPackets[nChannelID])
/(nCurrTicks - ptPacket->nPrevTicks[nChannelID]);
{
ptPacket->nPrevPackets[nChannelID] = ptPacket->nPackets[nChannelID];
ptPacket->nPrevTicks[nChannelID] = nCurrTicks;
}
else
{
if (nCurrTicks != ptPacket->nPrevTicks[nChannelID]
&& nCurrTicks-ptPacket->nPrevTicks[nChannelID] >= ptPacket->nInterval[nChannelID])
{
ptPacket->nPacketRate[nChannelID] = TICKS_PER_SECOND*(ptPacket->nPackets[nChannelID] - ptPacket->nPrevPackets[nChannelID])
/(nCurrTicks - ptPacket->nPrevTicks[nChannelID]);
ptPacket->nPrevTicks[nChannelID] = 0;
ptPacket->nInterval[nChannelID] = 0;
}
}
}
}
ptPacket->nInterval[nChannelID] = 0;
}
}
}
}
NW_VOID rate_stat(NW_CHAR* pchMediaType)
{
TCallerInfo* ptCaller = NULL;
TPacketInfo* ptPacket = NULL;
NW_INT32 nCaller = 0;
NW_INT32 nChannel = 0;
static const NW_INT32 nInterval = 4*TICKS_PER_SECOND;/*second*/
static const NW_INT32 nErrorPercentage = 5;/*误差占整个计时的百分比*/
{
TCallerInfo* ptCaller = NULL;
TPacketInfo* ptPacket = NULL;
NW_INT32 nCaller = 0;
NW_INT32 nChannel = 0;
static const NW_INT32 nInterval = 4*TICKS_PER_SECOND;/*second*/
static const NW_INT32 nErrorPercentage = 5;/*误差占整个计时的百分比*/
if (NULL == pchMediaType)
{
if (g_eStreamType > 0)
{
rate_print();
return;
}
printf("NW_VOID rate_stat(NW_CHAR* pchMediaType)/n");
printf("please enter string 'audio' or 'video'.");
return;
}
{
if (g_eStreamType > 0)
{
rate_print();
return;
}
printf("NW_VOID rate_stat(NW_CHAR* pchMediaType)/n");
printf("please enter string 'audio' or 'video'.");
return;
}
if (1 > nInterval || 1 > nErrorPercentage)
{
printf("invalid param,interval %d,error %d!/n",nInterval,nErrorPercentage);
return;
}
g_eStreamType = 0;
if (0 == strcmp(pchMediaType,"audio"))
{
g_eStreamType |= STREAM_AUDIO;
}
else if (0 == strcmp(pchMediaType,"video"))
{
g_eStreamType |= STREAM_VIDEO;
}
else
{
printf("please enter string 'audio' or 'video'.");
return;
}
{
printf("invalid param,interval %d,error %d!/n",nInterval,nErrorPercentage);
return;
}
g_eStreamType = 0;
if (0 == strcmp(pchMediaType,"audio"))
{
g_eStreamType |= STREAM_AUDIO;
}
else if (0 == strcmp(pchMediaType,"video"))
{
g_eStreamType |= STREAM_VIDEO;
}
else
{
printf("please enter string 'audio' or 'video'.");
return;
}
/*等待初始化完毕*/
if (!rate_started())
{
rate_start();
taskDelay(TICKS_PER_SECOND/10);
if (!rate_started())
{
rate_start();
taskDelay(TICKS_PER_SECOND/10);
/*试探一下有没有数据流*/
if (1 > g_nCallers)
{
printf("no stream coming!/n");
rate_stop();
return;
}
else
{
/*如果有数据流,设置统计时间*/
for (nCaller = 0; nCaller < MAX_CALLERS; nCaller++)
{
for (nChannel = 0; nChannel < MAX_CHANNELS; nChannel++)
{
g_tPacket[nCaller].nInterval[nChannel] = nInterval;
g_tPacket[nCaller].nPackets[nChannel] = 0;
g_tPacket[nCaller].nPacketRate[nChannel] = 0;
}
}
}
}
if (1 > g_nCallers)
{
printf("no stream coming!/n");
rate_stop();
return;
}
else
{
/*如果有数据流,设置统计时间*/
for (nCaller = 0; nCaller < MAX_CALLERS; nCaller++)
{
for (nChannel = 0; nChannel < MAX_CHANNELS; nChannel++)
{
g_tPacket[nCaller].nInterval[nChannel] = nInterval;
g_tPacket[nCaller].nPackets[nChannel] = 0;
g_tPacket[nCaller].nPacketRate[nChannel] = 0;
}
}
}
}
printf("please wait for %d seconds.../n",((TICKS_PER_SECOND/10)+nInterval+nInterval/(100/nErrorPercentage))/TICKS_PER_SECOND);
taskDelay(nInterval);/*统计时间*/
taskDelay(nInterval/(100/nErrorPercentage));/*考虑大概%20 的偏差*/
rate_print();
rate_stop();
}
taskDelay(nInterval);/*统计时间*/
taskDelay(nInterval/(100/nErrorPercentage));/*考虑大概%20 的偏差*/
rate_print();
rate_stop();
}
/*显示数据包RTP 头信息*/
NW_VOID rate_show(NW_INT32 nCallerID,NW_INT32 nChannelID,NW_INT32 nPacketNum)
{
TCallerInfo* ptCaller = NULL;
TPacketInfo* ptPacket = NULL;
NW_INT32 nCaller = 0;
NW_INT32 nChannel = 0;
NW_BOOL bFound = 0;
NW_VOID rate_show(NW_INT32 nCallerID,NW_INT32 nChannelID,NW_INT32 nPacketNum)
{
TCallerInfo* ptCaller = NULL;
TPacketInfo* ptPacket = NULL;
NW_INT32 nCaller = 0;
NW_INT32 nChannel = 0;
NW_BOOL bFound = 0;
if (0 == nCallerID && 0 == nChannelID && 0 == nPacketNum)
{
printf("NW_VOID rate_show(NW_INT32 nCallerID,NW_INT32 nChannelID,NW_INT32 nPacketNum)/n");
return;
}
{
printf("NW_VOID rate_show(NW_INT32 nCallerID,NW_INT32 nChannelID,NW_INT32 nPacketNum)/n");
return;
}
if (nCallerID <0 || nCallerID >= MAX_CALLERS)
{
printf("please enter valid function caller ID(first param)./n");
return;
}
if (nChannelID < 0 || nChannelID >= MAX_CHANNELS)
{
printf("please enter valid channel ID(second param)./n",nChannelID);
return;
}
if (1 > nPacketNum)
{
printf("please enter valid packet number to show(third param)./n");
return;
}
{
printf("please enter valid function caller ID(first param)./n");
return;
}
if (nChannelID < 0 || nChannelID >= MAX_CHANNELS)
{
printf("please enter valid channel ID(second param)./n",nChannelID);
return;
}
if (1 > nPacketNum)
{
printf("please enter valid packet number to show(third param)./n");
return;
}
/*等待初始化完毕, 如果rate_stat 还没有使用过*/
if (!rate_started())
{
for (nCaller = 0; nCaller < MAX_CALLERS; nCaller++)
{
memset(&g_tPacket[nCaller].nPacketShown[0],0,MAX_CHANNELS*sizeof(NW_INT32));
}
if (!rate_started())
{
for (nCaller = 0; nCaller < MAX_CALLERS; nCaller++)
{
memset(&g_tPacket[nCaller].nPacketShown[0],0,MAX_CHANNELS*sizeof(NW_INT32));
}
rate_start();
taskDelay(TICKS_PER_SECOND/10);
taskDelay(TICKS_PER_SECOND/10);
/*试探一下有没有数据流*/
if (1 > g_nCallers)
{
printf("no stream coming!/n");
rate_stop();
return;
}
else
{
ptCaller = &g_tCaller[nCallerID];
ptPacket = &g_tPacket[nCallerID];
if (1 > g_nCallers)
{
printf("no stream coming!/n");
rate_stop();
return;
}
else
{
ptCaller = &g_tCaller[nCallerID];
ptPacket = &g_tPacket[nCallerID];
if (1 > ptCaller->nFuncLen)
{
return;
}
{
return;
}
ptPacket->nPacketShown[nChannelID] = nPacketNum;
}
}
}
}
taskDelay(300*nPacketNum/TICKS_PER_SECOND);/*300 为一秒中可能接收到的最大包数目*/
rate_stop();
}
rate_stop();
}
NW_VOID rate_print(void)
{
TCallerInfo* ptCaller = NULL;
TPacketInfo* ptPacket = NULL;
NW_INT32 nCaller = 0;
NW_INT32 nChannel = 0;
NW_BOOL bFound = 0;
{
TCallerInfo* ptCaller = NULL;
TPacketInfo* ptPacket = NULL;
NW_INT32 nCaller = 0;
NW_INT32 nChannel = 0;
NW_BOOL bFound = 0;
if (1 > g_nCallers)
{
printf("no stream coming!/n");
return;
}
{
printf("no stream coming!/n");
return;
}
printf("-------------------------------------------------------------------------------/n");
for (nCaller = 0; nCaller < MAX_CALLERS; nCaller++)
{
ptCaller = &g_tCaller[nCaller];
ptPacket = &g_tPacket[nCaller];
if (1 > ptCaller->nFuncLen)
{
continue;
}
bFound = FALSE;
for (nChannel = 0; nChannel < MAX_CHANNELS; nChannel++)
{
if (ptPacket->nInterval[nChannel] > 0)
{
ptPacket->nInterval[nChannel] = 0;
}
{
continue;
}
bFound = FALSE;
for (nChannel = 0; nChannel < MAX_CHANNELS; nChannel++)
{
if (ptPacket->nInterval[nChannel] > 0)
{
ptPacket->nInterval[nChannel] = 0;
}
if (ptPacket->nPacketRate[nChannel] > 0)
{
printf("%28s(%4d) caller %2d,%4s id%3d:rate %2d %s/s,%3d %7s/n",
ptCaller->pchFuncName,ptCaller->nLine,nCaller,CHANNEL_CALL_ID != ptPacket->eChannelType[nChannel]? "ring":"call",nChannel,
ptPacket->nPacketRate[nChannel],ptPacket->bFrame[nChannel]? "f":"p",
ptPacket->nPackets[nChannel],ptPacket->bFrame[nChannel]? "frames":"packets");
{
printf("%28s(%4d) caller %2d,%4s id%3d:rate %2d %s/s,%3d %7s/n",
ptCaller->pchFuncName,ptCaller->nLine,nCaller,CHANNEL_CALL_ID != ptPacket->eChannelType[nChannel]? "ring":"call",nChannel,
ptPacket->nPacketRate[nChannel],ptPacket->bFrame[nChannel]? "f":"p",
ptPacket->nPackets[nChannel],ptPacket->bFrame[nChannel]? "frames":"packets");
bFound = TRUE;
}
}
if (bFound)
{
printf("/n");
}
}
printf("-------------------------------------------------------------------------------/n");
}
}
}
if (bFound)
{
printf("/n");
}
}
printf("-------------------------------------------------------------------------------/n");
}
NW_VOID rate_start(void)
{
g_bActivated = TRUE;
}
{
g_bActivated = TRUE;
}
NW_VOID rate_stop(void)
{
g_bActivated = FALSE;
}
{
g_bActivated = FALSE;
}
NW_BOOL rate_started(void)
{
return g_bActivated;
}
{
return g_bActivated;
}