[issues] webrtc 接入SRS丢包率不正确问题


原因和解决方法

直接原因: SRS暂不支持RTX通道发送nack重传包,重传包以media形式发送,sdk没有区分开来,nack重传包未计入丢包统计,得到的丢包率远低于实际丢包率

解决方法:

  • 1.StreamStatisticianImpl::UpdateOutOfOrder->IsRetransmitOfOldPacket 优化计算重传包函数,可以根据重传包达到时间与RTT比较,将时差长的包计入丢包,粗略计算丢包率。

  • 2.srs 增加rtx 通道发送nack,需要修改SDP协商部分,和rtx编码封包nack重传包.

srs增加rtx

这里简单过来拉流端增加方法

RTX在webrtc SDK里面视频是默认打开RTX的[M88],SRS没有支持,需要先在SRS SDP协商里面解析和分配rtx相关字段.

SDP协商

SrsRtcConnection::negotiate_play_capability 里面分配 rtx_ssrc_, rtx_pt, rtx_apt。其他还有 FID ssrc_groups

    // TODO: FIXME: set audio_payload rtcp_fbs_,
    // according by whether downlink is support transport algorithms.
    // TODO: FIXME: if we support downlink RTX, MUST assign rtx_ssrc_, rtx_pt, rtx_apt
    // not support rtx
    vector<SrsMediaPayloadType> rtx_pts = remote_media_desc.find_media_with_encoding_name("rtx");
    if (true) {
        //srs_freep(track->rtx_);
        //track->rtx_ssrc_ = 0;
        track->rtx_ssrc_ = SrsRtcSSRCGenerator::instance()->generate_ssrc();
        for (size_t i = 0; i < rtx_pts.size(); i++) {
            SrsMediaPayloadType rtx_pt = rtx_pts.at(i);
            uint8_t pt = ::atol(rtx_pt.format_specific_param_.substr(4, 3).c_str());
            if (track->media_->pt_ == pt) {
                if (!track->rtx_) {
                    track->rtx_ = new SrsCodecPayload();
                }
                track->rtx_->pt_of_publisher_ = track->rtx_->pt_;
                track->rtx_->pt_ = rtx_pt.payload_type_;
                track->rtx_->sample_ = rtx_pt.clock_rate_;
                ((SrsRtxPayloadDes*)(track->rtx_))->apt_ = pt;//::atol(rtx_pt.format_specific_param_.c_str());
                break;
            }
        }
    }

sdp 协商调试后,sdk 收到的sdp rtx部分大致长这个样子:

a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=ssrc-group:FID 5333335 5333336
a=ssrc:5333335 cname:15817311631_8010
a=ssrc:5333335 msid:stream_id video_label
a=ssrc:5333335 mslabel:stream_id
a=ssrc:5333335 label:video_label
a=ssrc:5333336 cname:15817311631_8010
a=ssrc:5333336 msid:stream_id video_label
a=ssrc:5333336 mslabel:stream_id
a=ssrc:5333336 label:video_label
a=candidate:0 1 udp 2130706431 192.168.6.54 8000 typ host generation 0

构建RTX包

rtx构建很简单,新定义个 SrsRtpRtxPayload, rtp头重写下type 序列号等, payload copy一下 头两个字节写media 的序列号, 对照着SDK里面加,打印下hex调试下即可。

// RTX Payload.
class SrsRtpRtxPayload : public ISrsRtpPayloader
{
public:
    char* payload;
    int size;
    uint16_t sequence_number;
public:
    SrsRtpRtxPayload();
    virtual ~SrsRtpRtxPayload();
// interface ISrsRtpPayloader
public:
    virtual uint64_t nb_bytes();
    virtual srs_error_t encode(SrsBuffer* buf);
    virtual srs_error_t decode(SrsBuffer* buf);
    virtual ISrsRtpPayloader* copy();
};

SrsRtpPacket* SrsRtcSendTrack::build_rtx_packet(
    SrsRtpPacket* packet) {
    
    SrsRtpPacket* rtx_packet;
    SrsRtcTrackDescription* track_desc = get_rtc_track_desc();
    SrsRtxPayloadDes* rtx_desc = (SrsRtxPayloadDes*)(track_desc->rtx_);

    uint8_t pt = packet->header.get_payload_type();
    uint8_t ppt = rtx_desc->pt_;
    uint8_t apt = rtx_desc->apt_;

    rtx_packet = packet->copy_with_no_payload();
    rtx_packet->header = packet->header;
    rtx_packet->header.set_payload_type(ppt);
    static uint16_t sequence_number_rtx_ = 1234; //only test
    rtx_packet->header.set_sequence(sequence_number_rtx_++);
    rtx_packet->header.set_ssrc(track_desc->rtx_ssrc_);
    
    char buf[kRtpPacketSize];
    SrsBuffer* stream = new SrsBuffer(buf, sizeof(buf));
    SrsAutoFree(SrsBuffer, stream);

    packet->payload()->encode(stream);
    
    SrsRtpRtxPayload* rtx_payload = new SrsRtpRtxPayload();
    rtx_payload->decode(stream);
    rtx_payload->sequence_number = packet->header.get_sequence();
    rtx_packet->set_payload(rtx_payload, SrsRtspPacketPayloadTypeRTX);
    rtx_packet->retransmission_ = true;

    return rtx_packet;
}