SRS作为接收方,对端作为发送方

生成Nack RTCP包的流程


SrsRtcConnection::SrsRtcConnection(SrsRtcServer* s, const SrsContextId& cid)
{req = NULL;cid_ = cid;hijacker_ = NULL;sendonly_skt = NULL;server_ = s;transport_ = new SrsSecurityTransport(this);cache_iov_ = new iovec();cache_iov_->iov_base = new char[kRtpPacketSize];cache_iov_->iov_len = kRtpPacketSize;cache_buffer_ = new SrsBuffer((char*)cache_iov_->iov_base, kRtpPacketSize);state_ = INIT;last_stun_time = 0;session_timeout = 0;disposing_ = false;twcc_id_ = 0;nn_simulate_player_nack_drop = 0;pp_address_change = new SrsErrorPithyPrint();pli_epp = new SrsErrorPithyPrint();nack_enabled_ = false;timer_nack_ = new SrsRtcConnectionNackTimer(this);_srs_rtc_manager->subscribe(this);
}

在SrsRtcConnection构造函数中,创建nack的定时器:timer_nack_ = new SrsRtcConnectionNackTimer(this)。nack定时器用于定时发送nack包。

SrsRtcConnectionNackTimer::SrsRtcConnectionNackTimer(SrsRtcConnection* p) : p_(p)
{_srs_hybrid->timer20ms()->subscribe(this);
}

RTC连接的Nack定时器注册到服务的全局定时器_srs_hybrid中,使用的是20毫秒定时器。定时器的回调函数如下:

srs_error_t SrsRtcConnectionNackTimer::on_timer(srs_utime_t interval)
{srs_error_t err = srs_success;//判断是否启用NACKif (!p_->nack_enabled_) {return err;}//全局的统计++_srs_pps_conn->sugar;// If circuit-breaker is enabled, disable nack.//如果断路器启用并且过载,则禁用Nack。断路器用于保护系统不过载。if (_srs_circuit_breaker->hybrid_critical_water_level()) {++_srs_pps_snack4->sugar;return err;}//查找所有的rtc推流,检测是否需要发送Nack包。std::map<std::string, SrsRtcPublishStream*>::iterator it;for (it = p_->publishers_.begin(); it != p_->publishers_.end(); it++) {SrsRtcPublishStream* publisher = it->second;if ((err = publisher->check_send_nacks()) != srs_success) {srs_warn("ignore nack err %s", srs_error_desc(err).c_str());srs_freep(err);}}return err;
}

对于每个RTC推流,都拥有音频接收轨道SrsRtcAudioRecvTrack和视频接收轨道SrsRtcVideoRecvTrack。分别去请求发送视频轨道和音频轨道的NACK信息


srs_error_t SrsRtcPublishStream::check_send_nacks()
{srs_error_t err = srs_success;if (!nack_enabled_) {return err;}for (int i = 0; i < (int)video_tracks_.size(); ++i) {SrsRtcVideoRecvTrack* track = video_tracks_.at(i);if ((err = track->check_send_nacks()) != srs_success) {return srs_error_wrap(err, "video track=%s", track->get_track_id().c_str());}}for (int i = 0; i < (int)audio_tracks_.size(); ++i) {SrsRtcAudioRecvTrack* track = audio_tracks_.at(i);if ((err = track->check_send_nacks()) != srs_success) {return srs_error_wrap(err, "audio track=%s", track->get_track_id().c_str());}}return err;
}

对于视频轨道:调用父类的do_check_send_nacks函数,可以获取超时的nack包数量,如果有超时的RTP包,可以去请求PLI,即请求视频关键帧数据(未实现)。

srs_error_t SrsRtcVideoRecvTrack::check_send_nacks()
{srs_error_t err = srs_success;++_srs_pps_svnack->sugar;uint32_t timeout_nacks = 0;if ((err = do_check_send_nacks(timeout_nacks)) != srs_success) {return srs_error_wrap(err, "video");}// If NACK timeout, start PLI if not requesting.if (timeout_nacks == 0) {return err;}srs_trace2(TAG_MAYBE, "RTC: NACK timeout=%u, request PLI, track=%s, ssrc=%u", timeout_nacks,track_desc_->id_.c_str(), track_desc_->ssrc_);return err;
}

对于音频轨道:直接调用父类的do_check_send_nacks函数即可。

srs_error_t SrsRtcAudioRecvTrack::check_send_nacks()
{srs_error_t err = srs_success;++_srs_pps_sanack->sugar;uint32_t timeout_nacks = 0;if ((err = do_check_send_nacks(timeout_nacks)) != srs_success) {return srs_error_wrap(err, "audio");}return err;
}

父类的do_check_send_nacks直接调用所在会话的check_send_nacks。

srs_error_t SrsRtcRecvTrack::do_check_send_nacks(uint32_t& timeout_nacks)
{srs_error_t err = srs_success;uint32_t sent_nacks = 0;session_->check_send_nacks(nack_receiver_, track_desc_->ssrc_, sent_nacks, timeout_nacks);return err;
}
void SrsRtcConnection::check_send_nacks(SrsRtpNackForReceiver* nack, uint32_t ssrc, uint32_t& sent_nacks, uint32_t& timeout_nacks)
{//全局统计数据++_srs_pps_snack->sugar;//创建RTCP Nack对象SrsRtcpNack rtcpNack(ssrc);//设置nack对象的ssrc,获取并设置需要反馈的丢包数据rtcpNack.set_media_ssrc(ssrc);nack->get_nack_seqs(rtcpNack, timeout_nacks);if(rtcpNack.empty()){return;}++_srs_pps_snack2->sugar;++_srs_pps_srtcps->sugar;char buf[kRtcpPacketSize];SrsBuffer stream(buf, sizeof(buf));// 对Nack对象进行封装为RTCP包的操作// TODO: FIXME: Check error.rtcpNack.encode(&stream);//加密RTCP包// TODO: FIXME: Check error.int nb_protected_buf = stream.pos();transport_->protect_rtcp(stream.data(), &nb_protected_buf);//将加密后的RTCP数据发送出去。// TODO: FIXME: Check error.sendonly_skt->sendto(stream.data(), nb_protected_buf, 0);
}

获取需要请求NACK的丢包数据


void SrsRtpNackForReceiver::get_nack_seqs(SrsRtcpNack& seqs, uint32_t& timeout_nacks)
{// If circuit-breaker is enabled, disable nack.if (_srs_circuit_breaker->hybrid_high_water_level()) {queue_.clear();++_srs_pps_snack4->sugar;return;}srs_utime_t now = srs_get_system_time();srs_utime_t interval = now - pre_check_time_;if (interval < opts_.nack_check_interval) {return;}pre_check_time_ = now;std::map<uint16_t, SrsRtpNackInfo>::iterator iter = queue_.begin();while (iter != queue_.end()) {const uint16_t& seq = iter->first;SrsRtpNackInfo& nack_info = iter->second;int alive_time = now - nack_info.generate_time_;if (alive_time > opts_.max_alive_time || nack_info.req_nack_count_ > opts_.max_count) {++timeout_nacks;rtp_->notify_drop_seq(seq);queue_.erase(iter++);continue;}// TODO:Statistics unorder packet.if (now - nack_info.generate_time_ < opts_.first_nack_interval) {break;}srs_utime_t nack_interval = srs_max(opts_.min_nack_interval, opts_.nack_interval / 3);if(opts_.nack_interval < 50 * SRS_UTIME_MILLISECONDS){nack_interval = srs_max(opts_.min_nack_interval, opts_.nack_interval);}if (now - nack_info.pre_req_nack_time_ >= nack_interval ) {++nack_info.req_nack_count_;nack_info.pre_req_nack_time_ = now;seqs.add_lost_sn(seq);}++iter;}
}

1、首先判断当前系统的负载水位。
2、获取当前时间。后面的多个操作都依赖时间差值比较。
3、如果上次nack的检测时间和当前时间差值小于配置的最小检测间隔,则直接返回。否则设置上次检测时间为当前时间,供下一轮比较使用。
4、遍历丢包的queue_,queue_是一个map。
5、获取nack_info已经在queue_中的生存时间alive_time,如果alive_time大于配置的最大生存时间;或者该nack_info请求重传的次数已经大于配置的最大重传次数,就会从queue_中删除并通知SrsRtpRingBuffer清除该nack_info对应的序列号。这是为了避免重传过早的数据导致增加延迟。
6、如果nack_info生成时间和当前时间差值小于配置的最小首次请求nack间隔,直接跳过。这是为了避免请求重传刚刚发现的丢包数据,可能并不是真正的丢包,只是还在网络链路传输中。
7、计算nack_interval,和rtt相关。在update_rtt中会重新计算opts_.nack_interval
8、当前时间减去上次请求nack的时间超过nack_interval时间差的丢包数据才会加入到SrsRtcpNack对象seqs中,请求重传。

接收数据,判断是否加入到nack_receiver_中

#mermaid-svg-EKVF92Gbe8q52R1D .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-EKVF92Gbe8q52R1D .label text{fill:#333}#mermaid-svg-EKVF92Gbe8q52R1D .node rect,#mermaid-svg-EKVF92Gbe8q52R1D .node circle,#mermaid-svg-EKVF92Gbe8q52R1D .node ellipse,#mermaid-svg-EKVF92Gbe8q52R1D .node polygon,#mermaid-svg-EKVF92Gbe8q52R1D .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-EKVF92Gbe8q52R1D .node .label{text-align:center;fill:#333}#mermaid-svg-EKVF92Gbe8q52R1D .node.clickable{cursor:pointer}#mermaid-svg-EKVF92Gbe8q52R1D .arrowheadPath{fill:#333}#mermaid-svg-EKVF92Gbe8q52R1D .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-EKVF92Gbe8q52R1D .flowchart-link{stroke:#333;fill:none}#mermaid-svg-EKVF92Gbe8q52R1D .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-EKVF92Gbe8q52R1D .edgeLabel rect{opacity:0.9}#mermaid-svg-EKVF92Gbe8q52R1D .edgeLabel span{color:#333}#mermaid-svg-EKVF92Gbe8q52R1D .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-EKVF92Gbe8q52R1D .cluster text{fill:#333}#mermaid-svg-EKVF92Gbe8q52R1D div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-EKVF92Gbe8q52R1D .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-EKVF92Gbe8q52R1D text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-EKVF92Gbe8q52R1D .actor-line{stroke:grey}#mermaid-svg-EKVF92Gbe8q52R1D .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-EKVF92Gbe8q52R1D .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-EKVF92Gbe8q52R1D #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-EKVF92Gbe8q52R1D .sequenceNumber{fill:#fff}#mermaid-svg-EKVF92Gbe8q52R1D #sequencenumber{fill:#333}#mermaid-svg-EKVF92Gbe8q52R1D #crosshead path{fill:#333;stroke:#333}#mermaid-svg-EKVF92Gbe8q52R1D .messageText{fill:#333;stroke:#333}#mermaid-svg-EKVF92Gbe8q52R1D .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-EKVF92Gbe8q52R1D .labelText,#mermaid-svg-EKVF92Gbe8q52R1D .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-EKVF92Gbe8q52R1D .loopText,#mermaid-svg-EKVF92Gbe8q52R1D .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-EKVF92Gbe8q52R1D .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-EKVF92Gbe8q52R1D .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-EKVF92Gbe8q52R1D .noteText,#mermaid-svg-EKVF92Gbe8q52R1D .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-EKVF92Gbe8q52R1D .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-EKVF92Gbe8q52R1D .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-EKVF92Gbe8q52R1D .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-EKVF92Gbe8q52R1D .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-EKVF92Gbe8q52R1D .section{stroke:none;opacity:0.2}#mermaid-svg-EKVF92Gbe8q52R1D .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-EKVF92Gbe8q52R1D .section2{fill:#fff400}#mermaid-svg-EKVF92Gbe8q52R1D .section1,#mermaid-svg-EKVF92Gbe8q52R1D .section3{fill:#fff;opacity:0.2}#mermaid-svg-EKVF92Gbe8q52R1D .sectionTitle0{fill:#333}#mermaid-svg-EKVF92Gbe8q52R1D .sectionTitle1{fill:#333}#mermaid-svg-EKVF92Gbe8q52R1D .sectionTitle2{fill:#333}#mermaid-svg-EKVF92Gbe8q52R1D .sectionTitle3{fill:#333}#mermaid-svg-EKVF92Gbe8q52R1D .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-EKVF92Gbe8q52R1D .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-EKVF92Gbe8q52R1D .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-EKVF92Gbe8q52R1D .grid path{stroke-width:0}#mermaid-svg-EKVF92Gbe8q52R1D .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-EKVF92Gbe8q52R1D .task{stroke-width:2}#mermaid-svg-EKVF92Gbe8q52R1D .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-EKVF92Gbe8q52R1D .taskText:not([font-size]){font-size:11px}#mermaid-svg-EKVF92Gbe8q52R1D .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-EKVF92Gbe8q52R1D .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-EKVF92Gbe8q52R1D .task.clickable{cursor:pointer}#mermaid-svg-EKVF92Gbe8q52R1D .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-EKVF92Gbe8q52R1D .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-EKVF92Gbe8q52R1D .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-EKVF92Gbe8q52R1D .taskText0,#mermaid-svg-EKVF92Gbe8q52R1D .taskText1,#mermaid-svg-EKVF92Gbe8q52R1D .taskText2,#mermaid-svg-EKVF92Gbe8q52R1D .taskText3{fill:#fff}#mermaid-svg-EKVF92Gbe8q52R1D .task0,#mermaid-svg-EKVF92Gbe8q52R1D .task1,#mermaid-svg-EKVF92Gbe8q52R1D .task2,#mermaid-svg-EKVF92Gbe8q52R1D .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-EKVF92Gbe8q52R1D .taskTextOutside0,#mermaid-svg-EKVF92Gbe8q52R1D .taskTextOutside2{fill:#000}#mermaid-svg-EKVF92Gbe8q52R1D .taskTextOutside1,#mermaid-svg-EKVF92Gbe8q52R1D .taskTextOutside3{fill:#000}#mermaid-svg-EKVF92Gbe8q52R1D .active0,#mermaid-svg-EKVF92Gbe8q52R1D .active1,#mermaid-svg-EKVF92Gbe8q52R1D .active2,#mermaid-svg-EKVF92Gbe8q52R1D .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-EKVF92Gbe8q52R1D .activeText0,#mermaid-svg-EKVF92Gbe8q52R1D .activeText1,#mermaid-svg-EKVF92Gbe8q52R1D .activeText2,#mermaid-svg-EKVF92Gbe8q52R1D .activeText3{fill:#000 !important}#mermaid-svg-EKVF92Gbe8q52R1D .done0,#mermaid-svg-EKVF92Gbe8q52R1D .done1,#mermaid-svg-EKVF92Gbe8q52R1D .done2,#mermaid-svg-EKVF92Gbe8q52R1D .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-EKVF92Gbe8q52R1D .doneText0,#mermaid-svg-EKVF92Gbe8q52R1D .doneText1,#mermaid-svg-EKVF92Gbe8q52R1D .doneText2,#mermaid-svg-EKVF92Gbe8q52R1D .doneText3{fill:#000 !important}#mermaid-svg-EKVF92Gbe8q52R1D .crit0,#mermaid-svg-EKVF92Gbe8q52R1D .crit1,#mermaid-svg-EKVF92Gbe8q52R1D .crit2,#mermaid-svg-EKVF92Gbe8q52R1D .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-EKVF92Gbe8q52R1D .activeCrit0,#mermaid-svg-EKVF92Gbe8q52R1D .activeCrit1,#mermaid-svg-EKVF92Gbe8q52R1D .activeCrit2,#mermaid-svg-EKVF92Gbe8q52R1D .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-EKVF92Gbe8q52R1D .doneCrit0,#mermaid-svg-EKVF92Gbe8q52R1D .doneCrit1,#mermaid-svg-EKVF92Gbe8q52R1D .doneCrit2,#mermaid-svg-EKVF92Gbe8q52R1D .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-EKVF92Gbe8q52R1D .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-EKVF92Gbe8q52R1D .milestoneText{font-style:italic}#mermaid-svg-EKVF92Gbe8q52R1D .doneCritText0,#mermaid-svg-EKVF92Gbe8q52R1D .doneCritText1,#mermaid-svg-EKVF92Gbe8q52R1D .doneCritText2,#mermaid-svg-EKVF92Gbe8q52R1D .doneCritText3{fill:#000 !important}#mermaid-svg-EKVF92Gbe8q52R1D .activeCritText0,#mermaid-svg-EKVF92Gbe8q52R1D .activeCritText1,#mermaid-svg-EKVF92Gbe8q52R1D .activeCritText2,#mermaid-svg-EKVF92Gbe8q52R1D .activeCritText3{fill:#000 !important}#mermaid-svg-EKVF92Gbe8q52R1D .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-EKVF92Gbe8q52R1D g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-EKVF92Gbe8q52R1D g.classGroup text .title{font-weight:bolder}#mermaid-svg-EKVF92Gbe8q52R1D g.clickable{cursor:pointer}#mermaid-svg-EKVF92Gbe8q52R1D g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-EKVF92Gbe8q52R1D g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-EKVF92Gbe8q52R1D .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-EKVF92Gbe8q52R1D .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-EKVF92Gbe8q52R1D .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-EKVF92Gbe8q52R1D .dashed-line{stroke-dasharray:3}#mermaid-svg-EKVF92Gbe8q52R1D #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-EKVF92Gbe8q52R1D #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-EKVF92Gbe8q52R1D #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-EKVF92Gbe8q52R1D #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-EKVF92Gbe8q52R1D #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-EKVF92Gbe8q52R1D #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-EKVF92Gbe8q52R1D #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-EKVF92Gbe8q52R1D #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-EKVF92Gbe8q52R1D .commit-id,#mermaid-svg-EKVF92Gbe8q52R1D .commit-msg,#mermaid-svg-EKVF92Gbe8q52R1D .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-EKVF92Gbe8q52R1D .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-EKVF92Gbe8q52R1D .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-EKVF92Gbe8q52R1D g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-EKVF92Gbe8q52R1D g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-EKVF92Gbe8q52R1D g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-EKVF92Gbe8q52R1D g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-EKVF92Gbe8q52R1D g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-EKVF92Gbe8q52R1D g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-EKVF92Gbe8q52R1D .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-EKVF92Gbe8q52R1D .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-EKVF92Gbe8q52R1D .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-EKVF92Gbe8q52R1D .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-EKVF92Gbe8q52R1D .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-EKVF92Gbe8q52R1D .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-EKVF92Gbe8q52R1D .edgeLabel text{fill:#333}#mermaid-svg-EKVF92Gbe8q52R1D .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-EKVF92Gbe8q52R1D .node circle.state-start{fill:black;stroke:black}#mermaid-svg-EKVF92Gbe8q52R1D .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-EKVF92Gbe8q52R1D #statediagram-barbEnd{fill:#9370db}#mermaid-svg-EKVF92Gbe8q52R1D .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-EKVF92Gbe8q52R1D .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-EKVF92Gbe8q52R1D .statediagram-state .divider{stroke:#9370db}#mermaid-svg-EKVF92Gbe8q52R1D .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-EKVF92Gbe8q52R1D .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-EKVF92Gbe8q52R1D .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-EKVF92Gbe8q52R1D .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-EKVF92Gbe8q52R1D .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-EKVF92Gbe8q52R1D .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-EKVF92Gbe8q52R1D .note-edge{stroke-dasharray:5}#mermaid-svg-EKVF92Gbe8q52R1D .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-EKVF92Gbe8q52R1D .error-icon{fill:#522}#mermaid-svg-EKVF92Gbe8q52R1D .error-text{fill:#522;stroke:#522}#mermaid-svg-EKVF92Gbe8q52R1D .edge-thickness-normal{stroke-width:2px}#mermaid-svg-EKVF92Gbe8q52R1D .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-EKVF92Gbe8q52R1D .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-EKVF92Gbe8q52R1D .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-EKVF92Gbe8q52R1D .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-EKVF92Gbe8q52R1D .marker{fill:#333}#mermaid-svg-EKVF92Gbe8q52R1D .marker.cross{stroke:#333}:root { --mermaid-font-family: "trebuchet ms", verdana, arial;}#mermaid-svg-EKVF92Gbe8q52R1D {color: rgba(0, 0, 0, 0.75);font: ;}

解密后的rtp数据
SrsRtcServer::on_udp_packet
session->on_rtp
publisher->on_rtp
on_rtp_plaintext
do_on_rtp_plaintext
audio_track->on_nack
video_track->on_nack
SrsRtcRecvTrack::on_nack

可以看到SRS对接收到到每个RTP包都会进入到SrsRtcRecvTrack::on_nack的处理逻辑中,接下来我们分析下该函数的具体逻辑。


srs_error_t SrsRtcRecvTrack::on_nack(SrsRtpPacket** ppkt)
{srs_error_t err = srs_success;SrsRtpPacket* pkt = *ppkt;uint16_t seq = pkt->header.get_sequence();SrsRtpNackInfo* nack_info = nack_receiver_->find(seq);if (nack_info) {// seq had been received.nack_receiver_->remove(seq);return err;}// insert check nack listuint16_t nack_first = 0, nack_last = 0;if (!rtp_queue_->update(seq, nack_first, nack_last)) {srs_warn("NACK: too old seq %u, range [%u, %u]", seq, rtp_queue_->begin, rtp_queue_->end);}if (srs_rtp_seq_distance(nack_first, nack_last) > 0) {srs_trace("NACK: update seq=%u, nack range [%u, %u]", seq, nack_first, nack_last);nack_receiver_->insert(nack_first, nack_last);nack_receiver_->check_queue_size();}// insert into video_queue and audio_queue// We directly use the pkt, never copy it, so we should set the pkt to NULL.if (nack_no_copy_) {rtp_queue_->set(seq, pkt);*ppkt = NULL;} else {rtp_queue_->set(seq, pkt->copy());}return err;
}

1、判断该序号到pkt是否已经加入到nack_receiver_中;

2、根据当前接收到的包的序号更新rtp_queue_中的起始和终止位置;

3、如果nack_last大于nack_first,将nack_first到nack_last之间的所有包都当作未接收到的包;

4、将当前接收到的RTP包加入到已经接收到的RtpRingBuffer中。

SRS RTC NACK源码分析—1相关推荐

  1. SRS流媒体服务器源码分析(一):Rtmp publish流程

    1.线程模型 srs使用了state-threads协程库,是单线程多协程模型. 这个协程的概念类似于lua的协程,都是单线程中可以创建多个协程.而golang中的goroutine协程是多线程并发的 ...

  2. SRS源码分析-rtmp转rtc流程

    前言 SRS4.0支持将RTMP流转换成RTC流,本文将结合源码分析下这个过程. 配置 首先,需要在SRS4.0的启动配置文件里面开启RTC Server和RTC 能力,可以参考官方提供的配置文件./ ...

  3. SRS(simple-rtmp-server)流媒体服务器源码分析--启动

    SRS(simple-rtmp-server)流媒体服务器源码分析--系统启动 一.前言 小卒最近看SRS源码,随手写下博客,其一为了整理思路,其二也是为日后翻看方便.如果不足之处,请指教! 首先总结 ...

  4. srs源码分析3-srs的启动

    本文分析的srs版本是0.6.0 srs源码分析1-搭建环境 srs源码分析2-浅析state_threads srs源码分析3-srs的启动 srs源码分析4-客户端的连接 srs源码分析5-han ...

  5. SRS流媒体服务器架构设计及源码分析丨音视频开发丨C/C++音视频丨Android开发丨嵌入式开发

    SRS流媒体服务器架构设计及源码分析 1.SRS流媒体服务器架构设计 2.协程-连接之间的关系 3.推流-转发-拉流之间的关系 4.如何手把手调试SRS源码 视频讲解如下,点击观看: SRS流媒体服务 ...

  6. SRS4.0源码分析-SrsRecvThread::cycle

    本文采用的 SRS 版本是 4.0-b8 , 下载地址:github 从<SRS4.0源码分析-SrsRtmpConn::stream_service_cycle> 得知 ,真正接受客户端 ...

  7. SRS4.0源码分析-RTMP入口

    本文采用的 SRS 版本是 4.0-b8 , 下载地址:github 上篇文章 <SRS4.0源码分析-main> 讲解了 SRS main 函数的基本流程,但是可能有些朋友还是比较懵逼. ...

  8. SRS4.0源码分析-推流总结

    本文采用的 SRS 版本是 4.0-b8 , 下载地址:github 本文主要对前面的文章做下总结. <SRS4.0源码分析-main>,<SRS4.0源码分析-RTMP入口> ...

  9. pmon 源码分析之start.S

    快速发布新主题  首页 | 登录 | 现在注册    新闻 技术文章 下载 视频 专题 论坛 博客 小组 微博 在线研讨会 • 模拟设计 • 电源技术 • 嵌入式系统 • 测试与测量 • 通信 • E ...

  10. SRS4.0源码分析-CMake

    本文采用的 SRS 版本是 4.0-b8 , 下载地址:github <SRS4.0源码分析-调试环境搭建> 讲了 SRS 在 Clion 里面的调试,本文主要讲解 srs-4.0-b8\ ...

最新文章

  1. 三年程序猿的实战开发经验之谈-做人做事建议
  2. CentOs环境下PHP支持PDO_MYSQL
  3. C和指针笔记 3.8 static关键字
  4. ios 按钮图片充满按钮_iOS有一些非常危险的按钮-UX评论
  5. Nacos 发布0.3.0版本,迄今为止最好看的版本
  6. 关于脚本log返回乱码解决方法
  7. 请求转发与重定向的区别
  8. 悟透JavaScript--可爱与智慧并存,灵感与诙谐共生
  9. 中文版Photoshop.CS6完全自学教程 李金明.全彩版.pdf
  10. 20年中国人均GDP排名变化
  11. (1.4.10.1)SXF测试笔试题
  12. 【拥塞管理】配置低延迟队列LLQ
  13. oracle数据投毒,Oracle Database Server TNS Listener远程数据投毒漏洞(CVE-2012-1675)的完美解决方法...
  14. 数据包络分析--SBM模型(第一篇)
  15. fedora11上安装fcitx
  16. oracle从11.0.2.4.0打PSU 11.0.2.4.8
  17. 设计师悲剧:你一天内要完成100张海报
  18. Elasticsearch-ais使用方式
  19. 华为eNSP模拟器操作技巧之关闭信息提示
  20. 使用浏览器抓包获取API

热门文章

  1. 平面几何----蝴蝶定理的证明
  2. 你离顶尖Java程序员,只差这11本书的距离 172 分享 分享到新浪微博 分享到QQ空间
  3. 国学传承美德,走进一德大脑屋国学启蒙课
  4. 【目标检测】“复制-粘贴”数据增强实现
  5. 使用keras实现YOLO v3报错‘str‘ object has no attribute ‘decode‘
  6. 实现类CAD的交互式命令系统
  7. 红蓝眼睛(答案在文章中找)
  8. 如何快速发表一篇SCI论文
  9. 22东华大学计算机专硕854考研上岸实录
  10. 迷宫(Maze)项目实现