JCHub

  • Home
  • Category
    • A/V
    • WebRTC
    • Beauty of Programming
    • Linux
    • Windows
    • Moments of Life
    • Campus Life
  • Reference
    • API Reference
    • Utilities
    • AV Test
    • Doc
  • Message Board
  • About
WebRTC
WebRTC

WebRTC研究:RTP报头扩展

RTP Header RTP协议中,RTP Header(报头)包括固定报头(Fixed Header)与报头扩展(Header extension,可选)。 RTP Fixed Header结构如下,其中前12字节内容必须包含的。 [crayon-69bcbaff2b36a076487072/] 但是这Fixed Header携带的信息满足不了更复杂的需求。所以引入了RTP Header Extension,可以携带更多的信息,同时方便各种扩展。 RTP Header Extension 如果RTP Fixed Header中,X字段为1,说明后面跟着RTP Header Extension。RTP Header Extension结构如下: [crayon-69bcbaff2b371468379591/] defined by profile:决定使用哪种Header Extension:one-byte或者two-byte header length:表示Header Extension的长度:length x 4字节 根据Header Extension中ID+len字段所占字节,RTP Header Extension分为One-Byte Header和Two-Byte Header类型。 One-Byte Header 对于One-Byte Header,"defined by profile"字段为固定的0xBEDE。接着后面的结构如下,占用1个byte: [crayon-69bcbaff2b373849753275/] ID:4-bit长度的ID表示本地标识符 len:表示extension data长度,范围:0~15,为0表示长度为1字节,15表示16字节 所以叫做One-Byte Header。 如下是一个One-Byte Header的示例: [crayon-69bcbaff2b374892626914/] 首先是0xBEDE固定字段开头,接着length长度为3,说明后面跟着3x4字节长度的header extension 。对于第一个header extension:L=0,表示data长度为1字节。对于第二个header extension:L=1,表示data长度为2字节。由于按4字节对齐,所以接着是值为0的填充数据。最后一个header extension:L=3,表示data长度为4字节。 WireShark抓包分析 如下是WireShark抓包WebRTC视频流解析的一个RTP包结构: Defined by profile字段为0xBEDE,表示One-Byte Header,Extension length为1,表示Header Extension长度为1x4字节,对于Header Extension:ID为3,Lengh为2。 相关代码Review 构造相关代码位于RtpPacket::AllocateRawExtension中 解析相关代码位于RtpPacket::ParseBuffer中 大家可以对着WebRTC相关代码熟悉下协议。 Two-Byte Header "defined by profile"字段结构如下: [crayon-69bcbaff2b375797782070/] 接着后面跟着的每个扩展元素结构如下,占用2个byte: [crayon-69bcbaff2b377818799676/] ID:本地标识符 length:表示extension data长度,范围1~255 如下是一个Two-Byte Header示例: [crayon-69bcbaff2b378826876812/] 首先"defined by profile"字段为0x1000,length为3,后面跟着3x4字节长度扩展,对于第一个header extension:L=0,数据长度为0,对于第二个header extension:L=1,data长度为1,接着是填充数据,对于第三个header extension:L=4,后面跟着4字节长度数据。 由于WebRTC中默认都是One-Byte Header,所以就不抓包分析了,具体构造解析代码跟One-Byte Header位于同一地方。 常见RTP Header Extension 在WebRTC中定义了很多RTP Header Extension,最常见的要数用于带宽估计的Transport-CC扩展,记录传输层序列号:TransportSequenceNumber,默认每个RTP包都带有此扩展。 还有记录音频能量大小的AudioLevel扩展,记录发送时间的AbsoluteSendTime扩展等等。 看下目前WebRTC支持的几种Header Extension: [crayon-69bcbaff2b379794348045/] SDP 如下是某个示例SDP内容: [crayon-69bcbaff2b37a777771268/] SDP中,a=extmap开头即为RTP Header Extension, 对于RFC中定义的RTP Header Extension,SDP格式如下: [crayon-69bcbaff2b37c327379753/] 对于WebRTC中自定义的RTP Header Extension,SDP格式如下: [crayon-69bcbaff2b37d274721538/] value值就是Header extension的本地标识符(ID值),ID值中0是预留位,所以我们可以看到从1开始递增,对于One-Byte Header来说,15也是预留位。 下面结合该SDP看下某个视频包的Wireshark解析: 可以看到该RTP包共有4个Header extension,这四个Header extension ID分别为:2,3,4,10。根据示例SDP,可知分别是:abs-send-time,transport-wide-cc-extensions,mid,rtp-stream-id扩展。 相关代码 RTP header extension的解析以及构造代码位于rtp_header_extensions.cc中。这里以TransportSequenceNumber扩展为例,看下代码: [crayon-69bcbaff2b37e411677976/] 前面SDP章节提到了,Header extensin ID跟Header extensin类型的对应。它们的对应关系处理在RtpHeaderExtensionMap类中,当进行SetLocalSdp操作时候,会调用: [crayon-69bcbaff2b37f259725193/] 绑定它们的关系。 总结 本文介绍了RTP包中的Header Extension,通过本文可以了解下Header Extension的结构,以及如果根据SDP以及Wireshark分析。 参考 [1] RTP: A Transport Protocol for Real-Time Applications.https://tools.ietf.org/html/rfc3550. [2] A General Mechanism for RTP Header Extensions.https://tools.ietf.org/html/rfc5285.

2020年10月25日 7comments 7460hotness 10likes Jeff Read all
WebRTC

WebRTC研究:音频带内FEC

在WebRTC中,对于音频丢包,目前有三种丢包恢复方案: 带内FEC 带外FEC NACK 本文介绍其中最简单的带内FEC。带内FEC属于WebRTC中默认启用的功能,由Opus编解码器实现,经过我们测试,30%随机丢包率下语音聊天,仍有不错的质量。不过单一靠带内FEC是无法实现更高的抗丢包要求,例如突发丢包环境或者30%以上随机丢包率,这也是各个厂商的优化点。 Opus编解码器 Opus编解码器其实是一种混合音频编解码器,融合了SILK与CELT两种编解码器。 SILK编解码器 由Skype开发 使用线性预测 适合语音 CELT编解码器 由Xiph.Org开发 使用改进的离散余弦变换 适合音乐等音质要求高的 支持特性 FEC(Forward Error Correction):前向纠错。使用低码率编码前面音频包数据,作为冗余信息。 DTX(Discontinuous Transmission):非连续传输。在安静的情况时自动降低编码码率。 PLC(Packet Loss Concealment):丢包隐藏。在解码端实现。通过前面音频数据包和后面音频数据包的相关性来预测当前丢失的音频数据包,对丢失的音频数据包进行补偿,隐藏当前的丢包错误。 关于Opus中SILK与CELT为什么在一起的故事,大家可以看下声网社区分享的这一篇文章: 编解码器杂谈:浅析Opus 带内FEC原理 使用低码率编码前面的历史音频包数据,作为冗余信息插入当前音频包中,同时结合解码端的PLC处理,从而实现不错的丢包恢复效果。 假设当前音频包采样时间为T,采样间隔为Δ,那么某音频编码后数据如下: [crayon-69bcbaff2dafa310211594/] 携带T时刻编码数据,以及低码率编码的T - Δ时刻数据。 假设Δ为1,时间戳T从1开始,那么前6个包数据如下: [crayon-69bcbaff2db00205206899/] 如果时刻4的包丢了,那么接收端可以从时刻5的数据包恢复时刻4数据。 如果出现突发连续丢包,例如时刻4,5的数据包都丢了,那么时刻4数据就无法还原了,此时就要通过PLC算法进行丢包恢复。当然,如果使用了带外FEC以及NACK,也可以配合带内FEC用于丢包恢复。 代码导读 带内FEC整个闭环处理比较简单: 从Receiver Report中获取丢包率 平滑处理丢包率 平滑后的丢包率反馈给Opus编码器 AudioEncoderOpusImpl::OnReceivedUplinkPacketLossFraction负责相关处理。 接收通过RTCP反馈的丢包率,传给Opus编码器。 [crayon-69bcbaff2db02269072796/] SDP处理 这里我们看下使用Opus编码器时默认生成的SDP: [crayon-69bcbaff2db03520949346/] 可以看到useinbandfec=1字段,表示开启带内FEC,最后通过WebRtcOpus_EnableFec接口启用带内FEC功能。 优势与不足 由于带内FEC由编解码器实现,所以我们的工作量很少,可以很方便地将音频FEC的能力集成到我们的应用中。不过带内FEC中,音频包的冗余信息用的是低码率编码,所以还原后的音质会降低。 参考 [1] RTP Payload Format for the Opus Speech and Audio Codec.https://tools.ietf.org/html/rfc7587.

2020年10月24日 6comments 5255hotness 9likes Jeff Read all
WebRTC

WebRTC研究:BBR拥塞控制被移除了

本来想写篇文章分析下WebRTC拥塞控制中的BBR算法代码,不过更新最新代码后发现BBR相关代码已经被移除了,所以也没必要了。WebRTC中对应的代码提交如下: [crayon-69bcbaff2ef97427897384/] 给出的理由是WebRTC中性能表现很糟糕,BBR得等到以后我研究完QUIC协议再专门分析了。

2020年8月22日 5comments 6228hotness 10likes Jeff Read all
WebRTC

WebRTC安卓编译

系统要求 系统:Ubuntu 18.04 磁盘空间:至少16GB磁盘空间 安装工具 [crayon-69bcbaff303ed457448999/] 安装depot tools [crayon-69bcbaff303f3976923093/] 获取代码 [crayon-69bcbaff303f5301909626/] gclient sync也可以拆分为如下两步执行: [crayon-69bcbaff303f8670162165/] 依赖安装 [crayon-69bcbaff303f9737859462/] 编译 直接使用aar编译工具编译,这里我们指定编译armeabi-v7a与arm64-v8a两种架构。 [crayon-69bcbaff303fa238034136/] 生成的libwebrtc.aar文件文件位于src目录下,编译过程产生的文件,例如libjingle_peerconnection_so.so位于src/out目录下。 崩溃问题排查 使用addr2line定位WebRTC底层崩溃代码位置。 [crayon-69bcbaff303fb398514684/] 编译特定版本 这里我们以更新到m79版本为例。 [crayon-69bcbaff303fd586526557/] 更新WebRTC代码到m79版本: [crayon-69bcbaff303ff106065607/] 同时也要更新对应的depot_tools,通过git log查看m79版本对应日期,然后进入depot_tools目录,也使用git log查看与m79版本日期相近的某个版本,并更新到该版本。 [crayon-69bcbaff30400093607471/] 最后进入WebRTC目录: [crayon-69bcbaff30401795666539/] 然后参照前面步骤重新编译即可。

2020年8月17日 2comments 3911hotness 7likes Jeff Read all
WebRTC

音视频开发入门:视频基础

数字图像 平时我们看到的视频其实是由一幅幅图像组成,所以先来了解下数字图像。 数字图像由一个个像素组成,是二维图像用有限数字数值像素的表示。如下路所示,我们放大一个图像后,能明显看到一个个像素块: 图像分辨率 水平分辨率指的一幅图像的宽,垂直分辨率指的一幅图像的高。一般说的分辨率用水平分辨率(宽)X 垂直分辨率(高)表示。 常见分辨率有: QVGA:320x240 CIF:352×288 VGA:640x480 HD:1360x768 FHD:1920x1080 WQHD:2560x1440 4K UHD:3840x2160 数字图像编码方法 指的是图像中每一个像素点在计算机中用什么编码方法表示。 RGB 人的眼睛是根据所看见的光的波长来识别颜色的。可见光谱中的大部分颜色可以由三种基本色光按不同的比例混合而成,这三种基本色光的颜色就是红(Red)、绿(Green)、蓝(Blue)三原色光。这三种光以相同的比例混合、且达到一定的强度,就呈现白色(白光);若三种光的强度均为零,就是黑色(黑暗)。这就是加色法原理,加色法原理被广泛应用于电视机、监视器等主动发光的产品中。 RGB存储表示 每个像素至少包含R、G、B分量,逐个像素存储。 RGB16:每像素占2字节,R、G、B分别用5bit,6 bit,5 bit表示。 RGB24:每像素占3字节,R、G、B各占一字节。 RGB32:每像素占4字节,A(Alpha)、R、G、B各占一字节。 YUV "Y"表示明亮度(Luminance),"U"和"V"则是色度、浓度(Chrominance、Chroma)。之所以使用YUV编码方法,主要有这些原因: R、G、B三个分量的相关性很强,存在冗余,使用YUV后可进行数据压缩 人眼对亮度信号较敏感,对色度信号较不敏感,可通过较少UV分量进行数据压缩 彩色信号能很好兼容黑白电视,黑白视频只有Y分量 YUV、YCbCr YUV解决彩色信号对黑白电视的兼容,黑白电视也能接收彩色电视信号。 Y:亮度(黑白图像) UV:彩度 YUV针对模拟信号。YCbCr模型针对数字信号,是YUV压缩和偏移的版本。一般俗称的YUV大多是指YCbCr。如下是某YCbCr图像的三个分量组成,其中其中Y是指亮度分量,Cb指蓝色色度分量,而Cr指红色色度分量。 YUV与RGB的转换 YUV与RGB可以按公式进行互相转换,具体可参考文末的参考[1]。 YUV采样 YUV 4:4:4采样,每一个Y对应一组UV分量。24 Bits per Pixel。 YUV 4:2:2采样,每两个Y共用一组UV分量。16 Bits per Pixel。 YUV 4:2:0采样,每四个Y共用一组UV分量。12 Bits per Pixel。 上图中黑点表示采样该像素点的Y分量,以空心圆圈表示采用该像素点的UV分量。 YUV 存储格式 YUV的存储格式有两大类: 紧缩格式(packed formats):将Y、U、V值存储成Macro Pixels数组,和RGB的存放方式类似。YUV4:4:4格式而言,用紧缩格式最合适。大概格式为:YUVYUVYUVYUV 平面格式(planar formats):将Y、U、V的三个分量分别存放在不同的矩阵中。大概格式为:YYYYUV。 YUV常见表示方法 根据采样方式和存储格式的不同,就有了多种 YUV 格式。这些格式主要是基于 YUV 4:2:2 和 YUV 4:2:0 采样。 常见的几乎都是基于YUV4:2:0: YU12。先存储 Y 分量,再存储 U、V 分量。例如:YYYYYYYYUUVV。 YV12 ,又称作 I420 格式。它的存储格式就是把 V 和 U 反过来了。例如:YYYYYYYYVVUU。 NV12。先存储了Y分量,但接下来并不是再存储所有的U或者V分量,而是把UV分量交替连续存储。例如:YYYYYYYYUVUV。NV12在硬件编解码中用的比较多。 如下图所示,为YUV420的存储格式,每4个Y对应一组UV: 视频 连续的图像变化每秒超过24帧(frame)画面以上时,根据视觉暂留原理,人眼无法辨别单幅的静态画面,看上去是平滑连续的视觉效果,这样连续的画面叫做视频。视频里面的每一幅图像称为一帧。 视频图像扫描 包括显示器扫描和摄像机视频捕获扫描两个环节。分为逐行扫描和隔行扫描两种扫描方式。 隔行扫描(Interlace Scanning) 每帧分为两场:奇数行组成奇数场,偶数行组成偶数场。 在显示器扫描中,隔行扫描指在显示一幅图像时,先扫描奇数行,再扫描偶数行,因此每幅图像需扫描两次才能完成。 摄像机在进行视频捕获时,例如对于25帧视频,每秒钟扫描50场,每一场只有半幅图像,意味着我们看到的图像的每一帧都是由两个采集时间不同的“半幅”图像合并而成的。当我们观看隔行扫描的视频时,会出现行间闪烁、运动模糊、垂直边缘锯齿等现象,从而影响画面清晰度。一般比较早期的视频是隔行扫描采集的。如下是一个隔行扫描视频截图,可以看到人物运动时有明显的锯齿。 该视频链接如下,大家可以认真看下人物运动时的边缘: https://music.163.com/#/mv?id=5309079 逐行扫描(Progressive Scanning) 每次扫描完所有行,从上到下逐行扫描。在显示器扫描中,逐行扫描指从上到下的扫描每一行图像。摄像机在进行视频捕获时,例如对于25帧视频,每秒钟扫描25场,每一场是完整一副图像。画面平滑干净、细节清晰、没有闪烁感、也不会产生运动模糊与锯齿现象。 1080P和1080i的区别 1080i:就是1920x1080分辨率。不过这种高清图像是隔行扫描(Interlace Scanning)的。每一个奇数行图像都在每一偶数行图像后面显示出来,当然图像就不会那么平滑。1080i适于表现纪录片和野生动物等题材,但是不是那么适合播放运动和电影类的内容。 1080p:也是1920x1080分辨率。和1080i的区别就在于1080p不是隔行扫描的,而是逐行扫描(Progressive Scanning)。每一线都同时表现在画面上,因此比隔行扫描更加得平滑。这是更高的高清标准。 视频时间戳(Timestamp) 每一帧的采样时间tn(相对)为时间戳。时间单位为采样频率或具体时间单位(如毫秒)。 RTP timestamp是用采样频率表示时间的。两帧之间RTP timestamp的增量 = 采样频率 / 帧率 例如:90kHz作为视频采样频率,视频帧率为25fps,相邻帧间RTP timestamp增量值 = 90000/25 = 3600。 视频帧率(Frame rate) 每秒播放的图像帧数量(Frames per Second,简:fps)。目前常见帧率:25fps、30fps、60fps、90fps、120fps。 视频码率(Bitrate) 单位时间传送的数据bit数,一般我们用的单位是kbps,也就是数据bit数除以1000(不是1024,发现很多人这个很容易搞错)。 码率越大,单位时间包含的数据越多,视频质量越好。目前有如下两种码率类型: 可变码率(Variable bitrate,简称VBR),指编码码率会随图像的复杂程度的不同而变化。 固定码率(Constant bitrate,简称CBR),指编码码率是固定的。 视频压缩的必要性 1920x1080@30fps视频的每秒数据量: RGB:1920x1080x3x30 = 186624000B = 186M YUV420:1920x1080x1.5x30 = 93M 可以看到数据量非常大。所以为了存储以及网络带宽要求,需要对视频数据进行压缩。 视频压缩原理 利用了视频数据的冗余。例如如空间冗余、时间冗余、结构冗余、信息熵冗余等,即图像的各像素之间存在着很强的相关性。通过消除这些冗余信息实现视频数据的压缩。 时间上的冗余信息(temporal redundancy)。在视频数据中,相邻的帧(frame)与帧之间通常有很强的关连性,这样的关连性即为时间上的冗余信息。 空间上的冗余信息(spatial redundancy)。在同一张帧之中,相邻的像素之间通常有很强的关连性,这样的关连性即为空间上的冗余信息。 统计上的冗余信息(statistical redundancy)。统计上的冗余信息指的是欲编码的符号(symbol)的几率分布是不均匀(non-uniform)的。 视觉冗余。人眼的一些特性比如亮度辨别阈值,视觉阈值,对亮度和色度的敏感度不同,使得在编码的时候引入适量的误差,也不会被察觉出来。可以利用人眼的视觉特性,以一定的客观失真换取数据压缩。 如下图为视频相邻帧间时间上的冗余信息,可以看到只有头部对应的像素发生了变化: 下图为利用空间上的冗余信息进行帧内压缩(后面提到的I帧压缩原理): 下图为利用时间上的冗余信息进行数据压缩(后面提到的P帧压缩原理),参考了前面的视频帧: 下图为利用时间上的冗余信息进行数据压缩(后面提到的B帧压缩原理),参考了前面以及后面的视频帧: 视频编解码器(Codec) 能够对数字视频进行压缩或者解压缩的程序或者设备。下图为一个典型的视频编码器。在进行当前信号编码时,编码器首先会产生对当前信号做预测的信号,称作预测信号(predicted signal),预测的方式可以是时间上的预测(inter prediction),亦即使用先前帧的信号做预测,或是空间上的预测(intra prediction),亦即使用同一张帧之中相邻像素的信号做预测。得到预测信号后,编码器会将当前信号与预测信号相减得到残余信号(residual signal),并只对残余信号进行编码,如此一来,可以去除一部分时间上或是空间上的冗余信息。接着,编码器并不会直接对残余信号进行编码,而是先将残余信号经过变换(通常为离散余弦变换)然后量化以进一步去除空间上和感知上的冗余信息。量化后得到的量化系数会再透过熵编码,去除统计上的冗余信息。在解码端,透过类似的相反操作,可以得到重建的视频数据。 对于编解码器,有硬件以及软件类型,硬件一般基于显卡或者FPGA设备,性能较高,能耗低,软件编解码器一般是指通过CPU进行视频编解码。 I帧、B帧、P帧 详细压缩原理在前面小节分析过了。 I帧:帧内编码图像帧(关键帧),帧内压缩。不依赖其它帧就可以解码还原出完整一帧图像。 P帧:预测编码图像帧,帧间压缩。依赖前面的I帧或P帧进行解码 B帧:双向预测编码图像帧,帧间压缩。提供最高的压缩比,它既需要之前的图像帧(I帧或P帧),也需要后来的图像帧(P帧),采用运动预测的方式进行帧间双向预测编码。 Note:对于实时视频(如摄像头),一般不产生B帧,因为用B帧,意味着会导致一定的帧延时 关键帧 VS 参考帧 GOP(Group of picture) GOP指画面组,一个GOP就是一组连续的画面。I帧表示一个GOP的开始。GOP大小指两个I帧之间的距离(两I帧之间的帧数量)。例如如下帧序列: IBBPBBPBBPBBI GOP大小为12。在视频直播中,为了秒开,GOP大小不宜设置过大。 DTS、PTS DTS:Decoder Timestamp,用来表示图像的解码时间 PTS:Presentation Timestamp,用来表示图像的显示时间 引入这两变量主要是因为,在存在双向参考帧(B帧)时,图像在编码图像的顺序和编码输出的顺序不同,而对于audio 来说,audio没有双向的预测, DTS 和 PTS 可以看成是一个顺序的,因此可一直采用一个,即可只采用 PTS。 如下是一个含有B帧的码流,在解码器端,对于前面4帧来说,得先解码B帧参考的I帧以及P帧,才能解码后续的B帧,所以解码顺序为IPBB,但是实际显示顺序为IBBP,所以DTS与PTS就不一样。 视频编码格式 目前主要有如下几种视频编码格式: H264/AVC。迄今为止业界最为成功的音视频编解码标准。 H265/HEVC。H.264官方的继任者。进一步的工业化目前受到了专利授权费用的显著影响,未来仍不明朗。 VP9。HEVC的一个不可忽视的竞争对手。人气不够高,Google在使用。性能没有受到业界的广泛认可。 AV1(AOMedia Video 1)。针对HEVC的专利问题,Google为首的多家公司成立了AOM(Alliance of Open Media,http://aomedia.org/)组织,专门研发的替代HEVC的免费开放视频编解码标准。 下图是视频编解码器发展历史。 视频容器 也叫封装格式,把已经编码好的视频、音频裸流按照一定的规范放到一起。里面可能还有元信息(metadata)、字幕、脚本之类。 常见容器格式有:MP4,AVI,FLV,MKV。如下是一个MP4容器文件包含的内容: 参考 [1] YUV.https://en.wikipedia.org/wiki/YUV. [2] 视频.https://baike.baidu.com/item/%E8%A7%86%E9%A2%91/321962?fr=aladdin. 本文已收录到大话WebRTC专栏,更多精彩请访问《大话WebRTC》。

2020年8月16日 5comments 4647hotness 28likes Jeff Read all
WebRTC

WebRTC研究:FEC之RED封装

首先看下webrtcglossary中的定义。 RED stands for REDundant coding and it is a RTP payload format defined in RFC 2198 for encoding redundant audio or video data. The primary motivation of sending redundant data is to be able to recover packets lost under lossy network conditions. If a packet is lost then the missing information may be reconstructed at the receiver from the redundant data that arrives in the following packet(s). The use of RED is negotiated in the SDP as an additional payload type and when used the audio/video RTP packets are packaged using RED format with a 6 bytes header, before the primary and secondary payloads conveying the actual audio/video packet and the redundant information. RED封装简介 RED指冗余编码,并且依据RFC 2198定义了一种RTP有效载荷格式,用于携带冗余编码的音视频数据。RED封装的数据作为payload跟在RTP报头后面。RED封装的数据有自己的Header。在一个RTP包中,会包含多个RED Header,指定了后面携带的编码数据信息。这些RED Header依次跟在RTP报头后面,在这些RED Header后就是各个RED Header对应的冗余编码数据。 如下是一个携带两个RED Header的RTP包结构: |RTP header| RED header | RED headr | RED encoding data | RED encoding data| RED Header结构如下: [crayon-69bcbaff31809919240129/] F: 1 bit长度,说明后面是否还有其他RED Header跟着。为1说明还有其他RED Header跟着,为0说明这是最后一个RED Header blcok PT:7 bits长度,表示该RED Header对应的冗余编码类型 timeoffset: 14 bits长度,表示无符号长度时间戳偏移,该偏移是相对于RTP Header的时间戳。用无符号长度做偏移意味着冗余编码数据必须在发送完原始数据后才能发送 block length: 10 bits长度,表示该RED Header对应编码数据块长度,该长度包含RED Header字段 对于RTP包中最后一个RED Header,可以忽略block length以及timestamp。因为它们可以从RTP Fixed Header以及整个RTP包长度计算得到。最后一个RED Header结构如下: [crayon-69bcbaff3180e410416591/] 其中F-bit位始终为0。 如下是一个完整RTP包示例,包含两个编码数据块:DVI4编码块,以及一个LPC冗余编码块。可以看到有两个RED Header跟在RTP Fixed Header后,第一个RED header F-bit为1,第二个RED Header由于是最后一个,所以F-bit为0,不包含时间戳偏移以及数据块长度信息。在RED Headrs后就是对应的编码数据块。 [crayon-69bcbaff3180f544606727/] 视频RED封装代码 这里我们以视频FEC为例,在WebRTC中音频的带外FEC也用到了RED编码。 UlpfecGenerator::GetUlpfecPacketsAsRed WebRTC视频相关RED封装代码位于UlpfecGenerator类中。RED封装主要配合UlpFEC使用。这里我们看下UlpFEC编码数据封装为RED包的过程: [crayon-69bcbaff31811011222694/] RedPacket::CreateHeader 对于视频,WebRTC中RED HeaderF-bit总是为0,不包含时间戳偏移等信息,也就是说一个RTP包只包含一个RED header: [crayon-69bcbaff31813428547046/] 视频RED解析代码 相关处理位于UlpfecReceiverImpl类中,我们看下主要的代码: [crayon-69bcbaff31814624493073/] 参考 [1] RED (REDundant coding).https://webrtcglossary.com/red/. [2] RTP Payload for Redundant Audio Data.https://tools.ietf.org/html/rfc2198. 本文已收录到大话WebRTC专栏,更多精彩请访问《大话WebRTC》。

2020年8月16日 10comments 6422hotness 6likes Jeff Read all
WebRTC

WebRTC研究:带宽估计中的稳定估计值

WebRTC带宽估计后,得到的结果会存到TargetTransferRate结构体中,然后回调给上层。TargetTransferRate定义如下: [crayon-69bcbaff32d08623246512/] 可以看到该结构体有两个估计码率值,本文我们介绍下其中的stable_target_rate,该值在后续的码率分配中有用到,配合target_rate进行相关判断。 在GoogCcNetworkController中,TargetTransferRate结果值按如下得到: [crayon-69bcbaff32d0d775531091/] 其中pushback_target_rate为我们实际的带宽估计值,stable_target_rate取带宽估计值与EstimatedLinkCapacity小的一个。 SendSideBandwidthEstimation 如下代码可知,LinkCapacity通过LinkCapacityTracker(link_capacity_)计算得到,接下来我们看看这个LinkCapacityTracker究竟是什么东东。 [crayon-69bcbaff32d0f193681162/] LinkCapacityTracker LinkCapacityTracker类比较简单,接口不多,这里我们介绍下主要的两个接口,通过这两个接口进行LinkCapacity的更新。 UpdateDelayBasedEstimate 主要两个输入参数如下: at_time: 当前时间 delay_based_bitrate:当前基于时延的带宽估计值 这里我们分析下该函数代码: [crayon-69bcbaff32d10416263790/] OnRateUpdate 主要有如下三个输入参数: acknowledged:根据接收端反馈的信息计算的接收码率(通过Transport-CC报文,该报文详细分析可去我博客搜索下) target:当前综合时延以及丢包信息得到的带宽估计值 at_time:当前时间 该函数具体分析如下: [crayon-69bcbaff32d11512259620/] 通过前面两个函数,得到的capacity_estimate_bps_即为我们前面说的稳定估计值stable_target_rate。由前面分析可知,该stable_target_rate综合了当前带宽估计值以及实际接收码率,从计算过程可知比较保守。从实际测试效果看,该估计值也正如其名,相对实际带宽估计值target_rate会更平稳,值也会相对小些。

2020年8月12日 2comments 3106hotness 5likes Jeff Read all
WebRTC

Windows平台WebRTC编译(持续更新)

在音视频领域,想深入研究的话,必定会接触WebRTC。WebRTC是一个庞大的工程,就像是音视频领域的百科全书,音视频采集,编解码,传输,渲染等一条龙在WebRTC里都有,而且WebRTC还有很多先进的音视频处理算法。由于WebRTC代码过于庞大,所以最好单步调试跟踪代码运行,这样才可以更好地学习WebRTC,否则很难有头绪。工欲善其事必先利其器,作为调试神器,宇宙第一IDE Visual Studio必不可少。所以本篇文章主要讲下如何在Windows上编译WebRTC,同时得到VS工程,然后调试。 很早以前写过VS2017上的编译,不过那篇文章有点跟不上时代,因为WebRTC用到的编译工具以及版本总是变化的,所以另开此文章,同步更新Windows平台最新编译步骤。 本文内容截止2025.08.18,最新代码测试编译通过 系统要求 Win10及以上64位系统。 内存至少8G,当然越大越好。 至少100G磁盘空间(NTFS格式),不能是FAT32,因为会生成大于4G的文件。 Visual Studio安装 要求 Visual Studio 2022 (>=17.0.0)。这里我们选择VS2022的社区版。安装VS2022时选择自定义安装,必须勾选如下几项: Desktop development with C++组件中10.0.26100.3323或以上的Win11 SDK(如果没看到该版本,去左侧Individual components那里勾选) Desktop development with C++组件中MFC以及ATL这两项 Desktop development with C++组件中适用于Windows的C++ Clang工具,这个很重要,新版WebRTC已经不支持MSVC编译器了 安装完VS2022后,必须安装SDK调试工具。打开控制面板->程序与功能,找到刚才安装的最新Windows Software Development Kit,鼠标右键->change。 勾选Debugging Tools For Windows,然后点击change。 depot_tools安装 下载depot_tools然后解压到某个目录,比我的解压到E盘根目录。接着将该depot_tools目录的路径加到系统环境变量Path里,然后把该路径移到最前面(避免已安装的python与git造成影响)。 或者直接通过如下命令行快速安装设置: [crayon-69bcbaff340f7945587238/] 获取WebRTC源码 由于WebRTC的源码地址被墙了,所以需要通过科学工具访问才能得到源码。后面都是命令行操作,打开cmd窗口,例如我用的是Clash代理,在cmd窗口设置如下: [crayon-69bcbaff340fd981689560/] 设置当前cmd窗口代理上网,如果cmd窗口关闭了重开得重新设置。 接着执行gclient命令,更新depot_tools。一般我们刚下载的都是最新的,可以跳过这一步。 [crayon-69bcbaff340fe183659098/] 后续如果不希望执行gclient有关命令时候自动更新浪费时间,可以通过如下命令设置: [crayon-69bcbaff340ff590735509/] 再接着设置一些环境变量,设置下我们的VS安装路径以及Windows SDK路径,这里我是安装在C盘。由于我用的社区版,所以路径名后缀是Community,其他VS版本可按实际修改。 [crayon-69bcbaff34100477868047/] 然后cd到要放源码的地方,执行: [crayon-69bcbaff34101083554217/] 这一过程是个漫长的等待,包括源码,第三方库,以及一些测试的音视频文件资源等,同时自动执行一些hook任务。如果因为网络等原因中断了,就再执行gclient sync。如果这一步一直卡着不动,可以执行ctrl+c,然后执行gclient sync。 这里为了避免中途中断,重新执行gclient sync时间太久,可以将gclient sync分两步执行: [crayon-69bcbaff34102270486984/] 编译工程 生成VS工程文件 首先需要生成工程文件。WebRTC默认使用Ninja作为编译系统,Ninja工程文件通过GN生成,由于我们需要使用VS进行代码编辑调试等,所以使用GN生成Ninja工程时需要配置--ide=vs生成VS的工程文件。通过如下命令生成工程文件(Debug编译,工程文件位于out\Default目录下): [crayon-69bcbaff34103611869068/] 我们可以在src\out\Default下得到 VS2022的all.sln解决方案文件。 如果需要Release编译,通过如下命令生成工程文件: [crayon-69bcbaff34105406577406/] 如果不想使用默认编译参数,可以使用gn args out/Default --list查看当前编译参数,通过类似如下方式设置: [crayon-69bcbaff34106488356072/] 编译 生成工程文件后,就可以在src目录下执行编译命令: [crayon-69bcbaff34107035832748/] 用VS2022打开: 可以看到众多工程,到此算是完成了。找到我们感兴趣的,就可以用VS单步调试,跟踪代码运行了。 代码更新 后续如需要更新代码,按照如下步骤: [crayon-69bcbaff34108693258309/] 然后参考前面步骤重新生成工程文件,编译即可。 引用WebRTC库 WebRTC编译后会在src\out\Default\obj目录下生成整个WebRTC工程的静态库:webrtc.lib,链接下这个就可以了。 PS:最新版只支持Clang编译器。如果VS应用没配置Clang编译器,链接这个webrtc.lib,生成工程文件时需要配置如下参数: gn gen --ide=vs out/Default --args="is_clang=false use_lld=false" 禁用clang与lld 由于最新版只支持Clang编译器,同时WebRTC也带有自己的Clang编译器(third_party\llvm-build目录下),所以可能跟VS前面配置下载的Clang编译器版本不同,导致出现webrtc.lib链接错误,所以需要配置WebRTC使用Visual Studio的Clang编译器。 拷贝Visual Studio的Clang编译器目录Llvm到webrtc-checkout目录下,当然也可以直接覆盖替换WebRTC自带的。Clang编译器目录位置举例:C:\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm gn gen参数增加:clang_base_path = "..\..\..\..\Llvm",告诉WebRTC使用的Clang编译器目录。也可以修改out\Default\args.gn,增加clang_base_path gn gen参数增加:use_custom_libcxx=false,告诉WebRTC构建系统gn不使用WebRTC 源码中自带的C++标准库(libcxx),而是使用系统或工具链提供的标准C++库,不配置为false,使用Clang编译的会报链接错误 最后修改我们的Visual Studio应用属性配置,使用Clang编译。修改位置:General->Platform Toolset->LLVM (clang-cl) 总结 总之WebRTC在Windows上的编译很考验耐心,只要你的科学上网工具够好,编译就会顺畅很多。 如果遇到问题,可以多看下文章,是不是哪里漏了,或者看下评论,不要一上来就问为什么编译不了。 参考 [1] WebRTC Native code Development.https://webrtc.github.io/webrtc-org/native-code/development/. [2] Chromium’s build instructions for Windows.https://chromium.googlesource.com/chromium/src/+/master/docs/windows_build_instructions.md.

2020年8月11日 85comments 33266hotness 56likes Jeff Read all
12345
Copyright Statement

Unauthorized reproduction or plagiarism in any form is strictly prohibited. For reprint requests, please contact via email.

Recent Comments
Mirzoemele Published at 2 months ago(01 01202613104 06 06pm26) Double blind randomised controlled trial of two to...
PedarPhago Published at 7 months ago(08 08202583109 12 12pm25) Association between selective serotonin reuptake i...
EsielTooft Published at 8 months ago(07 07202573112 29 29am25) International scientific apply guidelines for the ...
dongxuh Published at 8 months ago(07 07202573103 27 27pm25) 真心不错的博客,有机会能一起分享
南南 Published at 8 months ago(07 07202573103 15 15pm25) 写的超棒!

COPYRIGHT © 2026 jianchihu.net. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang