目录

一. 前言

二. Video JitterBuffer架构

三. PacketBuffer

四. ReferenceFinder

五. FrameBuffer


一. 前言

音视频传输通常使用 UDP,由于网络中存在丢包,抖动,乱序等现象,接收端收到的媒体包需要有个包缓冲区存放,对于视频而言,一帧数据可能被打包到多个 RTP 包传输,因此接收端收到 RTP 包后会判断是否可以组成视频帧,如果可以组成视频帧还要判断其参考帧是否存在,如果存在则将该帧送入帧缓冲区,等待解码线程进行解码。

二. Video JitterBuffer架构

如上所示,RtpVideoStreamReceiver 是收视频包的处理类,其中的 Video JitterBuffer 逻辑主要由 PacketBuffer,RtpFrameReferenceFinder 和 FrameBuffer 互相协作实现。

PacketBuffer 是 RTP 包的缓冲区,收到 RTP 包后根据序列号存放到环形数组的特定位置,如果某一帧对应的 RTP 包收集完整则弹出该帧的所有 RTP 包数据。

RtpFrameReferenceFinder 用于帧参考关系的查找确认,例如对于 I 帧不需要参考其他帧就可以进行解码,P 帧则需要前向参考,而 B 帧则需要前后双向参考,因此某一帧的数据收集完整后,还需要等待其参考帧就绪才能送入帧缓冲区等待解码。

FrameBuffer 为帧缓冲区,解码线程会读取该缓冲区的视频帧数据进行解码。

三. PacketBuffer

一个视频帧在发送时可能被打包成多个 RTP 包,接收端接收时需要有个包级别的缓冲区,该缓冲区等待接收帧的完整 RTP 包,然后提交给下个流程进行组帧等处理。

PacketBuffer 的实现代码在 modules/video_coding/packet_buffer.h 和 modules/video_coding/packet_buffer.cc 中。

PacketBuffer 类涉及的成员变量和方法如上所示,其中最重要的成员是 std::vector<std::unique_ptr<Packet>> buffer_,它是一个用于存放 Packet 的动态环形数组(起始大小为 512,最大为 2048),即接收到 RTP 包后根据其序列号将包存放到该环形数组的对应位置(index = seq_num % buffer_.size()),每次插入 RTP 包都会判断缓冲区靠前的 RTP 包是否已经是某一视频帧的完整数据,如果是则将这些 RTP 包带在 InsertPacket 函数的返回值 InsertResult 中。

对于 PacketBuffer 最重要的方法为 InsertPacket,它首先计算 RTP 包存放在环形数组的位置,如果该位置当前有存放数据,则通过包序号判断是否为重复包,是重复包则不做处理,如果不是重复包说明此时缓冲区已经不够用了,需要调用 ExpandBuffer 扩容后再继续不断尝试将包塞入缓冲区。

每次 InsertPacket 将 Packet 存放到 buffer_[index] 后会再调用 FindFrames(seq_nunm),它查找当前是否收到了帧的完整数据,如果是则将帧对应的完整 RTP 包返回存放到 result.packets 中,FindFrames 逻辑如下。

FindFrames 在 buffer_ 大小不为 0 的情况下,判断插入 seq_num 的包后是否可能拿到帧的完整 RTP 包,如果不可能则先跳过,下次 InsertPacket 还会再调用 FindFrames 查看是否能拿到帧的完整 RTP 包。

PotentialNewFrame(uint16_t seq_num) 判断的逻辑如下。

1. 如果是帧的第一个包,它是有可能凑成帧的完整数据的,一方面是有些非关键帧只需要一个 RTP 包,即便是需要多个 RTP 包的关键帧,seq_num 之后的包可能早就收齐存在于缓冲区了

2. 如果不是帧的第一个包,并且缓冲区 index 位置的前一个位置是空数据,说明这个帧的数据肯定不完整,是不可能凑出帧完整数据的

3. 如果 index 位置的前一个位置的包序号不是 seq_num - 1,或者时间戳跟 seq_num 包的时间戳不一样,说明它们是没有关系的包

4. 如果 index 前一个位置的包的 continuous 为 true,说明 seq_num 对应的帧在 seq_num 之前的包已经是收齐连续的了,再收到该包是可能凑齐帧完整数据的

如果 PotentialNewFrame 为 true 并且 buffer_[index] 是帧的最后一个包,说明此时可以拿到帧对应的完整 RTP 包数据了。

start_index 的值是从帧的最后一个包不断往前递减,如果遇到对应的位置是 is_first_packet_in_frame 说明帧的包数据已经从尾部遍历到首部,此时只要把 [start_seq_num, end_seq_num) 位置的包都存放到 found_frames 中即可,start_seq_num 是从 seq_num 从后一直遍历往前到帧的第一个包的。

至此 PacketBuffer 的收包存放以及当帧对应的 RTP 包完整时如何获取到的流程已经介绍完成。

四. ReferenceFinder

ReferenceFinder 的作用是判断当前帧的参考帧是否存在,如果存在其参考帧则将当前帧送到 FrameBuffer,等待解码线程对其进行解码,如果不存在则暂存等待其参考帧到达后再把它送到 FrameBuffer。(例如对于 I 帧不需要参考其他帧,对于 P 帧需要前向参考,对于 B 帧需要前后双向参考)

如果调用 PacketBuffer InsertPacket 能组成完整帧数据,则其返回的 struct InsertResult 值中会携带该帧对应的完整 Packet 数据,然后调用 OnInsertedPacket。

struct InsertResult {std::vector<std::unique_ptr<Packet>> packets;// Indicates if the packet buffer was cleared, which means that a key// frame request should be sent.bool buffer_cleared = false;
};
OnInsertedPacket(packet_buffer_.InsertPacket(std::move(packet)));

OnInsertedPacket 逻辑如下,如果 result.packets 不为空则遍历 result.packets 将 Packet 的负载数据存放到 payloads,把 Packet 的信息存放到 packet_infos,当遍历到帧的最后一个包时会调用 video depacketizer 的 AssembleFrame 将 payloads 存放到 bitstream 中(bitstream 实际上是一个 uint8_t 数组),然后再调用 OnAssembledFrame。

RtpVideoStreamReceiver::OnAssembledFrame 逻辑如下,如果之前还没收到过任何帧并且当前的帧也不是关键帧则进行关键帧请求(RequestKeyFrame),之后再判断 current_codec_ 与 frame codec 是否一致或设置 current_codec_,然后再调用 video_coding::RtpFrameReferenceFinder 的 ManageFrame 方法查找当前 frame 对应的参考帧。

RtpFrameReferenceFinder::ManageFrame 逻辑如下,主要是调用 ManageFrameInternal 判断当前 frame 是否有对应的参考帧,如果还没找到参考帧则将 frame 暂时保存到 stashed_frames_ 中,如果当前 frame 已经找到参考帧则调用 HandOffFrame 处理将其送进 FrameBuffer,然后再把暂存的 frame 重新弹出查看它们是否能找到参考帧,如果找到参考帧则也送到 FrameBuffer 中。

关于如何查找帧对应的参考帧是否存在,VP8,VP9,H264 的判断逻辑不相同,具体可以查看 ManageFrameVp8,ManageFrameVp9,ManageFrameH264,本文不对此进行展开。

五. FrameBuffer

ReferenceFinder 查找到帧对应的参考帧后会将该帧送入 FrameBuffer,解码线程会对帧进行解码,HandOffFrame 函数调用 frame_callback_->OnCompleteFrame 将帧送入 FrameBuffer。

如下 VideoReceiveStream::OnCompleteFrame 中最关键的逻辑为 frame_buffer_->InsertFrame。

FrameBuffer 中最重要的成员为 frames_,它是一个 FrameMap 类型的对象,存储 frameId 与 frame 的映射关系,FrameBuffer::InsertFrame 就是将 frame 插入到 FrameMap 中。

using FrameMap = std::map<VideoLayerFrameId, FrameInfo>;

解码逻辑如下所示,调用 frame_buffer_->NextFrame 取出 buffer 中的帧,然后调用 HandleEncodedFrame 进行解码。

WebRTC Video JitterBuffer相关推荐

  1. WebRTC视频JitterBuffer详解

    WebRTC视频JitterBuffer详解 1 WebRTC版本 2 概要 3 JitterBuffer结构和基本流程 4 帧完整性 - PacketBuffer 4.1 包缓存 4.2 帧的开始和 ...

  2. WebRtC视频jitterbuffer原理机制

    WebRTC视频JitterBuffer详解_一朵喇叭花压海棠的博客-CSDN博客_jitterbuffer本文从代码层面详细描述了WebRTC的视频JitterBuffer模块的主要功能模块,可以作 ...

  3. WebRTC的JitterBuffer笔记

    接收类图不全 一  主要的类 类video_coding::PacketBuffer是接收RTP包,类RtpVideoStreamReceiver2中有用到. packet_buffer_(clock ...

  4. 转:Webrtc video framerate/resolution 自适应

    转:https://xie.infoq.cn/article/50b7931b8a023f8ca7f25d4e9 一,引言 音视频会议使用者的设备性能往往是参差不齐的,当我们一味的去追求视频的高清,高 ...

  5. webrtc jitterbuffer 学习

    注:详情可关注微信公众号Deverloper_Taoists 视频通话中的jitterbuffer分析 1. 概述 Jitterbuffer在实时通讯中起了重要作用,用于数据接收端,它缓冲了接收到的数 ...

  6. Android IOS WebRTC 音视频开发总结(八十七)-- WebRTC中丢包重传NACK实现分析

    Android IOS WebRTC 音视频开发总结(八十七)-- WebRTC中丢包重传NACK实现分析 本文主要介绍WebRTC中丢包重传NACK的实现,作者:weizhenwei ,文章最早发表 ...

  7. 资讯|WebRTC M94 更新

    WebRTC M94 目前已在 Chrome 测试版中发布,包含 1 个新特性以及超过 19 个 Bug 修复,功能增强,稳定性与性能等方面的改进. 欢迎关注网易云信知乎机构号,我们将定期翻译 Web ...

  8. 资讯|WebRTC M93 更新

    WebRTC M93 目前已在 Chrome 测试版中发布,包含 12 个新特性以及超过 40 个 bug 修复,功能增强,稳定性与性能等方面的改进. 欢迎关注网易云信公众号,我们将定期翻译 WebR ...

  9. 资讯|WebRTC M92 更新

    WebRTC M92目前已在Chrome测试版中发布,包含5个新特性以及超过33个bug修复,功能增强,稳定性与性能等方面的改进. 欢迎关注网易云信公众号,我们将定期翻译WebRTC相关内容,帮助开发 ...

最新文章

  1. Spring MVC 中使用 Google kaptcha 验证码
  2. 研效优化实践:Python单测——从入门到起飞
  3. 分类结果可视化python_可视化分类结果的另一种方法
  4. 100套精美英文HTML页面源码
  5. 排序算法对比、总结(Python代码)
  6. Mac下安装java运行环境
  7. HTML5超级链接、图片与多媒体
  8. vue开发银行流水查询系统--基于巨杉数据库
  9. Linux防火墙关闭方法
  10. android显示器,古董 or 真香? ThinkVision 28: 28'' 4K Android 显示器开箱
  11. 论文写作——如何作图(visio/ppt+Adobe Acrobat Pro)
  12. NumPy 快速入门系列:应用统计学基础概念、相关统计指标与NumPy的实现
  13. 什么是绩效管理?企业如何做好绩效管理
  14. discuz 如何去掉:导读-最新发表
  15. Adobe Flash Player安装遇上错误:未能初始化的解决方法
  16. 考研语法?看这篇就够了 #考研英语语法#从零开始#英语一
  17. 服务端使用Axis2-1.6.3发布webservice服务、客户端使用Axis1.4实现调用
  18. 【mySQL】mysql数据库分页查询讨论专题
  19. dcs world f15c教学_DCS信号干扰原因分析及解决方法,收藏备用!
  20. PAT乙级------1006~1010附代码及思路

热门文章

  1. Codeforces 718E Matvey's Birthday bfs
  2. 我看所谓“汉语编程”
  3. 银河系中心黑洞的第一张照片,本文带你了解发现的过程
  4. linux时间如何设置成英文单词,linux时间命令date、clock、hwclock
  5. r5 5600u和锐龙r5 4500u的区别
  6. iOS开发-进阶:被误解的MVC和被神化的MVVM(作者:唐巧)
  7. Leetcode 488.祖玛游戏
  8. 解决pdf不能打印,不能注释,不能修改,不能保存等文档限制
  9. 计算机睡眠状态游戏还在运行,win10睡眠主机还在运行正常嘛_win10睡眠模式主机还运行如何处理-win7之家...
  10. M OP N数值运算问题