webrtc-IDR request 关键帧

webrtc 采用UDP协议传输媒体数据,丢包的发生不可避免,webrtc为了弥补问题,使用了包括NACK、FEC、IDR request等方式进行处理。

其中NACK,可以理解问反向ack,当接收端发现有packet丢失时候,主动告知发送端进行数据发送。

nack一般处理逻辑,伪代码

def check_and_add_packet():
        missed = False

        if self.max_seq is None:
            self.max_seq = packet.sequence_number
            return missed

        # mark missing packets
        if uint16_gt(packet.sequence_number, self.max_seq):
            seq = uint16_add(self.max_seq, 1)
            while uint16_gt(packet.sequence_number, seq):
                self.missing.add(seq)
                missed = True
                seq = uint16_add(seq, 1)
            self.max_seq = packet.sequence_number
        else:
            self.missing.discard(packet.sequence_number)

就是判断当前packet的sequence_number和历史进行对比,发现中间差距大于1,说明有包丢失。

但是有些场景下,重传包会带来其他问题,增大延迟时间、缓存过大,如果丢失的包发现发送端已经遗失了,就会导致无法重传等。

关键帧请求场景

由于关键帧可以单独解码出图像,不参考前后视频帧,所以会采取请求关键帧这种更便捷的方式替代重传该数据包,使解码端能立刻刷新出新图像,避免丢包过多,长时间等待重传数据包导致的画面停顿问题,以及获取不到重传包导致后续数据解码花屏问题。

丢包过多

在网络环境不好时候,出现了大量丢包场景,使用NACK通知发送端发送,会有可能重传但是网络环境依旧不好,导致大量对接,NACK又开始让重传,情况异常更为严重。 数据只能先进行缓存到队列,jitterbuffer大小也随之增大。  可以使用关键帧方式,由于关键帧可以单独解码,所以不会造成解码端花屏马赛克现象。但是由于前面那些视频帧都丢弃了,此时生成的关键帧会与之前播放的视频存在不连贯性,所以画面变化大时会有轻微卡顿现象,相当于跳帧了。

如何实现呢,可以定义nack丢包数量,当发现数据量大于某一个阈值时候,可以进行请求关键帧处理。

获取帧数据超时

要解码的帧数据存放在FrameBuffer中,解码器解码时如果超过规定时间一直无法从FrameBuffer中获取解码数据,此时就会请求关键帧。

在上面我们列举了几种需要关键帧请求的情况,我们只需要规定好RTCP报文格式,就能通知编码发送端发送关键帧。关键帧请求RTCP报文格式比较简单,在RFC4585(RTP/AVPF)以及RFC5104(AVPF)规定了两种不同的关键帧请求报文格式:Picture Loss Indication (PLI)Full Intra Request (FIR)。WebRTC中关键帧请求也只用到了这两种消息