转自:http://tomyz0223.iteye.com/blog/855224
一、为什么要带宽检测?
在多媒体应用中,尤其在桌面共享应用系统的实现中,带宽检查通常提高用户体验一种依据:
1.可以根据用户的带宽,如果用户的带宽过低,可以降低桌面抓屏的图片的质量,从而减少发送的数据,增强用户使用的稳定性。
2.可以根据用户的带宽,如果带宽比较低,则应该降低抓屏的频率,因为在网络带宽比较差的情况下,如果capture screen频率过高,会造成网络拥堵。此时可以降低capture screen的频率,从而提高用户使用的平滑性,不会造成大的延时。
二、如何做带宽检测以及应该注意的问题?
检测带宽想想是不是很容易:
一段时间内server接收到数据的byte数 /interval(s)
这里需要注意问题和原则:
1.做带宽检查的参考帧,一定要大,太小了误差很大,比如说,以2KB的包为例,可能几毫秒就可以完成,这样得到的数据会非常大
2.带宽检测的检测包在检测过程中要保证数据是一直发送的,例如如果20s之内,只有少量的数据发送,这样计算的结果会偏小
在Red5里面要做到带宽检测,需要修改两个地方:
1.需要知道一个桌面视频帧何时开始发送
2.需要知道一个桌面视频帧何时接收完毕
3.需要知道一帧发送开始和结束发送的字节数
注:Red5的网络层使用的是Mina作为数据传输层,它有个api专门进行带宽的检测的,它的原理简单说来就是,你告诉它时间点,它告诉你收到了多少数据,显然,有这个api,我们的带宽检查不是问题了
/**
* {@inheritDoc}
*/
public final void updateThroughput(long currentTime, boolean force) {
int interval = (int) (currentTime - lastThroughputCalculationTime);
long minInterval = getConfig().getThroughputCalculationIntervalInMillis();
if (minInterval == 0 || interval < minInterval) {
if (!force) {
return;
}
}
readBytesThroughput = (readBytes - lastReadBytes) * 1000.0 / interval;
writtenBytesThroughput = (writtenBytes - lastWrittenBytes) * 1000.0 / interval;
readMessagesThroughput = (readMessages - lastReadMessages) * 1000.0 / interval;
writtenMessagesThroughput = (writtenMessages - lastWrittenMessages) * 1000.0 / interval;
lastReadBytes = readBytes;
lastWrittenBytes = writtenBytes;
lastReadMessages = readMessages;
lastWrittenMessages = writtenMessages;
lastThroughputCalculationTime = currentTime;
}
三、修改Red5源码达到带宽检测的效果
有两个时间点,我们就可以大胆做了:
1.在上一个数据帧decode完毕后,做个check point
在org.red5.server.net.rtmp.BaseRTMPHandler类中messageReceived方法末尾加上:
/** {@inheritDoc} */
public void messageReceived(RTMPConnection conn, ProtocolState state, Object in) throws Exception {
log.info("Begin to calculate the bandwidth======");
RTMPMinaConnection connC = (RTMPMinaConnection) Red5
.getConnectionLocal();
final IoSession session = connC.getIoSession();
session.updateThroughput(System.currentTimeMillis(), true);
2.在一个视频数据帧收发完毕后,做个check point
修改 org.red5.server.net.rtmp.codec.RTMPProtocolDecoder,增加以下红色部分的代码;
public List<Object> decodeBuffer(ProtocolState state, IoBuffer buffer) {
-------- 省略了部分代码
if (state.hasDecodedObject()) {
if (decodedObject != null) {
if(decodedObject instanceof Packet ){
Header header = ((Packet) decodedObject).getHeader();
log.info("The check package size is====="+header.getSize()/1024+"KB.");
if(header.getDataType()==TYPE_VIDEO_DATA && header.getSize()>(100*1024)){
session.updateThroughput(System.currentTimeMillis(), true);
log.info("The bandwidth is ====="+session.getReadBytesThroughput()/1024d+"KB/s");
}
}
result.add(decodedObject);
}