背景描述:
在流媒体传输的时候,I P P P B B P I P B B I 类似的一帧传输,但是经常会随机出现丢包的情况,如何改善用户体验,就显的特别重要
其中花屏的体验十分差,花屏是因为 I帧中间某一帧丢了包导致的问题,因为每一帧之间的变化和差距不是很大,所以最好的办法就是直接跳到下一个I帧 ,或者通过RTCP 请求丢失的包,重新排序解码
这篇优化,采用第一种,环境在live555中
模拟丢包环境
在live555 中rtspserver 已经默认有一个丢包的 宏开关
MultiFramedRTPSink::sendPacketIfNecessary(){
// Send the packet:
#ifdef TEST_LOSS
if ((our_random()%350) != 0) // simulate 10% packet loss #####
#endif
if (!fRTPInterface.sendPacket(fOutBuf->packet(), fOutBuf->curPacketSize())) {
// if failure handler has been specified, call it
if (fOnSendErrorFunc != NULL) (*fOnSendErrorFunc)(fOnSendErrorData);
}
++fPacketCount;
fTotalOctetCount += fOutBuf->curPacketSize();
fOctetCount += fOutBuf->curPacketSize()
- rtpHeaderSize - fSpecialHeaderSize - fTotalFrameSpecificHeaderSizes;
++fSeqNo; // for next time
//.........
}
define TEST_LOSS 修改 被取余的数 我改为350 ,改的太小丢包频率太高会导致一直卡顿
客户端优化
因为rtsp server 发包会丢包,那么客户端就需要丢弃已经丢包的帧,直到系一个I帧到来
MultiFramedRTPSource::doGetNextFrame1() 对 获取的每一个包组成一个帧,并且传递给客户端解码播放
while (fNeedDelivery) {
BufferedPacket* nextPacket
= fReorderingBuffer->getNextCompletedPacket(packetLossPrecededThis);//获取每个包
processSpecialHeader(nextPacket, specialHeaderSize);//处理 FU-A FU-B分包情况
nextPacket->use(fTo, fMaxSize, frameSize, fNumTruncatedBytes,
fCurPacketRTPSeqNum, fCurPacketRTPTimestamp,
fPresentationTime, fCurPacketHasBeenSynchronizedUsingRTCP,
fCurPacketMarkerBit); //将包组成一帧
fReorderingBuffer->releaseUsedPacket(nextPacket); //统计期望的包rtpseqnum
}
主要是这四个方法
fCurrentPacketBeginsFrame 用于分包中,为true 就是FU—A start 分包的开始,false 就不是分包开始
packetLossPrecededThis true表示 丢包开始的那一个帧
fPacketLossInFragmentedFrame true 表示 获取的包 ,还在已经丢包的那个帧中
举例 I P P P B B P I P B B I 中
第一个P帧丢包了,live555 默认会帮我们丢弃包,然后根据fNeedDelivery 继续获取下一包 知道出现下一帧P开始,fPacketLossInFragmentedFrame 为false
if (fPacketLossInFragmentedFrame) {
// This packet is unusable; reject it:
fReorderingBuffer->releaseUsedPacket(nextPacket);
fNeedDelivery = True;
continue;
}
第二个P帧开始,通过nextPacket->use 组成完整一帧,那么就需要判断 当前帧 nal_unit_type 是不是5 即I帧,不然也会被丢弃,所以这个P帧会被抛弃
通过读取RTP header 和 nalu header 方式 获取nal_unit_type
unsigned char* headerStart = nextPacket->data();
unsigned char type = (headerStart[0]&0x1F);
if(28==type||29==type){
nal_unit_type = (headerStart[1]&0x1F);
}else{
nal_unit_type = type;
}
根据是不是 nal_unit_type = 5 决定是否冻屏直到下一个I帧过来