pgm不太能用,没有想象中的可靠,重传机制貌似仍然使用组播重传,丢包率80%的网络感觉没啥改进,如果有所好转延迟估计也是个不小的问题。

后听说rtp也有nack机制,webrtc基于rtp实现了重传在一定程度上保证可靠性。

在各路大神的指引下找到了rfc4585,看到了这么一段

RTCP扩展反馈报文,有一种nack报文

当FMT=1并且PT=205时,代表此报文是个NACK报文

Name Value Brief Description
RTPFB 205 Transport layer FB message
PSFB 206 Pyload-specific FB message
0:    unassigned
1: Generic NACK
2-30: unassigned
31: reserved for future expansion of the identifier number space

The Generic NACK message is identified by PT=RTPFB and FMT=1.

FCI字段会有如下图所示的数据

PID:表示Packet ID,用于表明当前接收端丢失的数据包的序号,是接收端期待收到的下一个数据包

BLP:表示bitmask of following lost lost packets,占两个字节,16位,表示接着PID后面的16个数据包的丢包情况。

rtp协议本身不会帮你重传。应用应该自己解析rtcp做处理

webrtc关于nack的实现

我突然想起来,我入职的时候下过webrtc的源码,还没删除(可能是太大了,删太慢了就没删),于是就把源码拿出来看了看webrtc对于这个部分的实现

这个部分的代码量也不多,很好懂,大概就是发送端的rtcp receiver接收到rtcp数据包,解析发现是个nack,告诉rtp发送端重新发送接收端请求重传的数据包

bool RTCPReceiver::IncomingPacket(const uint8_t* packet, size_t packet_size) {
if (packet_size == 0) {
LOG(LS_WARNING) << "Incoming empty RTCP packet";
return false;
} PacketInformation packet_information;
if (!ParseCompoundPacket(packet, packet + packet_size, &packet_information))
return false;
TriggerCallbacksFromRTCPPacket(packet_information);
return true;
}

上述代码是rtcp receiver接收到rtcp数据包后的初步判断,ParseCompoundPacket函数用于解析rtcp数据包,将关键信息摘出储存到PacketInformation结构体中传递给触发回调,TriggerCallbacksFromRTCPPacket函数用于触发收到rtcp数据包回调。

下面是ParseCompoundPacket结构体的实现

struct RTCPReceiver::PacketInformation {
uint32_t packet_type_flags = 0; // RTCPPacketTypeFlags bit field. uint32_t remote_ssrc = 0;
std::vector<uint16_t> nack_sequence_numbers;
ReportBlockList report_blocks;
int64_t rtt_ms = 0;
uint8_t sli_picture_id = 0;
uint64_t rpsi_picture_id = 0;
uint32_t receiver_estimated_max_bitrate_bps = 0;
std::unique_ptr<rtcp::TransportFeedback> transport_feedback;
};

nack_sequence_numbers已经是解析过后的接收端没有收到的数据包的序号了,解析过程也很简单,是个拆包过的成就不再展开描述了。

void RTCPReceiver::TriggerCallbacksFromRTCPPacket(
const PacketInformation& packet_information) {
...
if (!receiver_only_ && (packet_information.packet_type_flags & kRtcpNack)) {
if (!packet_information.nack_sequence_numbers.empty()) {
LOG(LS_VERBOSE) << "Incoming NACK length: "
<< packet_information.nack_sequence_numbers.size();
_rtpRtcp.OnReceivedNack(packet_information.nack_sequence_numbers);
}
...
}

TriggerCallbacksFromRTCPPacket函数会根据解析的数据包信息判断出当前rtcp数据包类型是nack,触发回调,该回调并不会直接到rtp sender而是到rtp-rtcp module由这个module调用rtp sender,这个module是rtp和rtcp的中心组件(和webrtc结构有关),也起到了解耦的作用

这个中间调用的代码量不多

void ModuleRtpRtcpImpl::OnReceivedNack(
const std::vector<uint16_t>& nack_sequence_numbers) {
for (uint16_t nack_sequence_number : nack_sequence_numbers) {
send_loss_stats_.AddLostPacket(nack_sequence_number);
}
if (!rtp_sender_.StorePackets() ||
nack_sequence_numbers.size() == 0) {
return;
}
// Use RTT from RtcpRttStats class if provided.
int64_t rtt = rtt_ms();
if (rtt == 0) {
rtcp_receiver_.RTT(rtcp_receiver_.RemoteSSRC(), NULL, &rtt, NULL, NULL);
}
rtp_sender_.OnReceivedNack(nack_sequence_numbers, rtt);
}

一开始做了一些记录,记录丢包情况,然后rtt是用来做流控的,收到nack当次并不一定会重传,会用到rtt做判断。

下面是rtp sender的代码用于重传数据包

void RTPSender::OnReceivedNack(
const std::vector<uint16_t>& nack_sequence_numbers,
int64_t avg_rtt) {
TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"),
"RTPSender::OnReceivedNACK", "num_seqnum",
nack_sequence_numbers.size(), "avg_rtt", avg_rtt);
for (uint16_t seq_no : nack_sequence_numbers) {
const int32_t bytes_sent = ReSendPacket(seq_no, 5 + avg_rtt);
if (bytes_sent < 0) {
// Failed to send one Sequence number. Give up the rest in this nack.
LOG(LS_WARNING) << "Failed resending RTP packet " << seq_no
<< ", Discard rest of packets";
break;
}
}
}

TRACE_EVENT是google调试使用的机制,不用管它,这个函数会循环重发丢失队列中的数据包,但是不一定发送成功,数据包缓存是有限制的,如果要重新发送的数据包已经不再缓存中了,总不能变出来吧?

int32_t RTPSender::ReSendPacket(uint16_t packet_id, int64_t min_resend_time) {
std::unique_ptr<RtpPacketToSend> packet =
packet_history_.GetPacketAndSetSendTime(packet_id, min_resend_time, true);
if (!packet) {
// Packet not found.
return 0;
} // Check if we're overusing retransmission bitrate.
// TODO(sprang): Add histograms for nack success or failure reasons.
RTC_DCHECK(retransmission_rate_limiter_);
if (!retransmission_rate_limiter_->TryUseRate(packet->size()))
return -1; if (paced_sender_) {
// Convert from TickTime to Clock since capture_time_ms is based on
// TickTime.
int64_t corrected_capture_tims_ms =
packet->capture_time_ms() + clock_delta_ms_;
paced_sender_->InsertPacket(RtpPacketSender::kNormalPriority,
packet->Ssrc(), packet->SequenceNumber(),
corrected_capture_tims_ms,
packet->payload_size(), true); return packet->size();
}
bool rtx = (RtxStatus() & kRtxRetransmitted) > 0;
int32_t packet_size = static_cast<int32_t>(packet->size());
if (!PrepareAndSendPacket(std::move(packet), rtx, true,
PacketInfo::kNotAProbe))
return -1;
return packet_size;
}
  • 重发数据包操作会先检查历史缓存中有没有数据包,如果没有,继续外层循环,重发下一个包。
  • 如果有带宽限制,需要看当前分给重发机制的带宽是否已经被用完,用完了就停止循环重发操作。
  • min_resend_time时间用于检测。如果之前有请求过重传同样序号的数据包,在短时间内是不会再重传的

【webrtc】webrtc的rtp重传代码分析的更多相关文章

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

    本文主要介绍WebRTC中丢包重传NACK的实现,作者:weizhenwei ,文章最早发表在编风网,微信ID:befoio 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID ...

  2. wifi display代码 分析

    转自:http://blog.csdn.net/lilian0118/article/details/23168531 这一章中我们来看Wifi Display连接过程的建立,包含P2P的部分和RTS ...

  3. 【miscellaneous】海康相机RTSP连接代码分析

    海康相机RTSP连接代码分析 最近在做海康相机rtsp连接获取音视频的工作,现在介绍一下分析过程和源码. [源码在我上传的共享资料中: http://download.csdn.net/detail/ ...

  4. Android代码分析工具lint学习

    1 lint简介 1.1 概述 lint是随Android SDK自带的一个静态代码分析工具.它用来对Android工程的源文件进行检查,找出在正确性.安全.性能.可使用性.可访问性及国际化等方面可能 ...

  5. pmd静态代码分析

    在正式进入测试之前,进行一定的静态代码分析及code review对代码质量及系统提高是有帮助的,以上为数据证明 Pmd 它是一个基于静态规则集的Java源码分析器,它可以识别出潜在的如下问题:– 可 ...

  6. [Asp.net 5] DependencyInjection项目代码分析-目录

    微软DI文章系列如下所示: [Asp.net 5] DependencyInjection项目代码分析 [Asp.net 5] DependencyInjection项目代码分析2-Autofac [ ...

  7. [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(5)(IEnumerable<>补充)

    Asp.net 5的依赖注入注入系列可以参考链接: [Asp.net 5] DependencyInjection项目代码分析-目录 我们在之前讲微软的实现时,对于OpenIEnumerableSer ...

  8. 完整全面的Java资源库(包括构建、操作、代码分析、编译器、数据库、社区等等)

    构建 这里搜集了用来构建应用程序的工具. Apache Maven:Maven使用声明进行构建并进行依赖管理,偏向于使用约定而不是配置进行构建.Maven优于Apache Ant.后者采用了一种过程化 ...

  9. STM32启动代码分析 IAR 比较好

    stm32启动代码分析 (2012-06-12 09:43:31) 转载▼     最近开始使用ST的stm32w108芯片(也是一款zigbee芯片).开始看他的启动代码看的晕晕呼呼呼的. 还好在c ...

随机推荐

  1. 78.3D立体轮播图(完整兼容手机端和pc端)

    效果呈现来源于http://www.jq22.com/demo/jR3DCarousel-master20160315/ 在此基础上改成需要的3个分类的3D图 由于原有的不支持粘贴复制显示3个分类 我 ...

  2. 关于IOS AFNetWorking内存泄漏的问题

    之前项目中用Instruments的leaks检测APP,结果发现APP的网络请求会出现内存泄漏,暂时我先使用单例的方式暂时解决了内存泄漏的原因,但是我还没有找打根本原因.希望有研究的小伙伴可以相互探 ...

  3. 微信小程序开发--常用开发实例

    一.常用商品列表的换行排布 <view class="box_max"> <view class="box_min">限时秒杀</ ...

  4. idea使用过程中的一些常见问题,做个笔记

    :当实现这个接口方法时重载是不允许的. 首先我相信我的代码肯定没问题,因为我实现的接口确实有这个方法.在编程阶段就提示这个错误,于是我有理由相信应该是编译错误!通过google,解决办法so easy ...

  5. Django框架(十一)-- 补充:inclusion_tag、defer、only、choice、事务、创建多对多的第三张表、mvc和mtv模式

    一.inclusion_tag 1.作用 用于生成HTML片段,是数据由参数传入而变成动态 2.使用 # 1.app下新建一个模块,templatetags # 2.创建一个py文件(mytag.py ...

  6. eclipse 工作空间配置UTF-8编码格式

    配置前端页面编码格式 1. Windows-->preferences 2. web-->jsp file-->Encoding 3. OK保存  配置java文件编码格式 1. W ...

  7. 十八、Python面向对象之魔术方法

    1.类的比较 class A(object): def __init__(self,value): self.value = value def __eq__(self,other): return ...

  8. PAT 乙级 1021.个位数统计 C++/Java

    题目来源 给定一个 k 位整数 1 (0, ,, d​k−1​​>0),请编写程序统计每种不同的个位数字出现的次数.例如:给定 0,则有 2 个 0,3 个 1,和 1 个 3. 输入格式: 每 ...

  9. js实现时分秒毫秒计时器

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. JavaScript三种方法获取地址栏参数的方法

    今天碰到要在一个页面获取另外一个页面url传过来的参数,一开始很本能的想到了用 split(“?”)这样一步步的分解出需要的参数. 后来想了一下,肯定会有更加简单的方法的!所以在网上找到了两个很又简单 ...