diff --git a/webrtc/Sdp.cpp b/webrtc/Sdp.cpp index 8f633566..02ecef91 100644 --- a/webrtc/Sdp.cpp +++ b/webrtc/Sdp.cpp @@ -761,19 +761,35 @@ void SdpAttrSimulcast::parse(const string &str) { SDP_THROW(); } direction = vec[0]; - vec = split(vec[1], ";"); - rid0 = vec[0]; - if (vec.size() > 1) { - rid1 = vec[1]; - if (vec.size() > 2) { - rid2 = vec[2]; - } - } + rids = split(vec[1], ";"); } string SdpAttrSimulcast::toString() const { if (value.empty()) { - value = direction + " " + rid0 + ";" + rid1 + ";" + rid2; + value = direction + " "; + bool first = true; + for (auto &rid : rids) { + if (first) { + first = false; + } else { + value += ';'; + } + value += rid; + } + } + return SdpItem::toString(); +} + +void SdpAttrRid::parse(const string &str) { + auto vec = split(str, " "); + CHECK(vec.size() >= 2); + rid = vec[0]; + direction = vec[1]; +} + +string SdpAttrRid::toString() const { + if (value.empty()) { + value = rid + " " + direction; } return SdpItem::toString(); } @@ -837,7 +853,8 @@ void RtcSession::loadFrom(const string &str, bool check) { rtc_media.rtcp_rsize = media.getItem('a', "rtcp-rsize").operator bool(); map rtc_ssrc_map; - for (auto &ssrc : media.getAllItem('a', "ssrc")) { + auto ssrc_attr = media.getAllItem('a', "ssrc"); + for (auto &ssrc : ssrc_attr) { auto &rtc_ssrc = rtc_ssrc_map[ssrc.ssrc]; rtc_ssrc.ssrc = ssrc.ssrc; if (!strcasecmp(ssrc.attribute.data(), "cname")) { @@ -880,15 +897,24 @@ void RtcSession::loadFrom(const string &str, bool check) { } CHECK(rtc_media.rtp_rtx_ssrc.size() == 2); } else { - //没有指定ssrc-group:FID字段,那么只有1个或0个ssrc - if (rtc_ssrc_map.size() == 1) { - rtc_media.rtp_rtx_ssrc.emplace_back(rtc_ssrc_map.begin()->second); - } else if (rtc_ssrc_map.size() > 1) { - throw std::invalid_argument("sdp中不存在a=ssrc-group:FID字段,但是ssrc却大于1个"); + auto simulcast = media.getItemClass('a', "simulcast"); + if (simulcast.empty()) { + //没有指定ssrc-group:FID字段,也不是simulcast,那么只有1个或0个ssrc + if (rtc_ssrc_map.size() == 1) { + rtc_media.rtp_rtx_ssrc.emplace_back(rtc_ssrc_map.begin()->second); + } else if (rtc_ssrc_map.size() > 1) { + throw std::invalid_argument("sdp中不存在a=ssrc-group:FID字段,但是ssrc却大于1个"); + } + } else { + //开启simulcast + rtc_media.rtp_rids = simulcast.rids; + //simulcast最少要求2种方案 + CHECK(rtc_media.rtp_rids.size() >= 2); } } if (ssrc_group_sim) { + //指定了a=ssrc-group:SIM for (auto ssrc : ssrc_group_sim->ssrcs) { auto it = rtc_ssrc_map.find(ssrc); if (it == rtc_ssrc_map.end()) { @@ -896,6 +922,11 @@ void RtcSession::loadFrom(const string &str, bool check) { } rtc_media.rtp_ssrc_sim.emplace_back(it->second); } + } else if (!rtc_media.rtp_rids.empty()) { + //未指定a=ssrc-group:SIM,但是指定了a=simulcast,且可能指定了ssrc + for (auto &attr : ssrc_attr) { + rtc_media.rtp_ssrc_sim.emplace_back(rtc_ssrc_map[attr.ssrc]); + } } auto rtpmap_arr = media.getAllItem('a', "rtpmap"); @@ -1188,7 +1219,22 @@ RtcSessionSdp::Ptr RtcSession::toRtcSessionSdp() const{ group->type = "SIM"; sdp_media.items.emplace_back(wrapSdpAttr(std::move(group))); } + + if (m.rtp_rids.size() >= 2) { + auto simulcast = std::make_shared(); + simulcast->direction = "recv"; + simulcast->rids = m.rtp_rids; + sdp_media.items.emplace_back(wrapSdpAttr(std::move(simulcast))); + + for (auto &rid : m.rtp_rids) { + auto attr_rid = std::make_shared(); + attr_rid->rid = rid; + attr_rid->direction = "recv"; + sdp_media.items.emplace_back(wrapSdpAttr(std::move(attr_rid))); + } + } } + } else { sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared(m.sctpmap))); sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared("sctp-port", to_string(m.sctp_port)))); @@ -1252,7 +1298,10 @@ void RtcMedia::checkValid() const{ CHECK(direction != RtpDirection::invalid || type == TrackApplication); CHECK(!plan.empty() || type == TrackApplication ); bool send_rtp = (direction == RtpDirection::sendonly || direction == RtpDirection::sendrecv); - CHECK(!rtp_rtx_ssrc.empty() || !send_rtp); + if (rtp_rids.empty() && rtp_ssrc_sim.empty()) { + //非simulcast时,检查有没有指定rtp ssrc + CHECK(!rtp_rtx_ssrc.empty() || !send_rtp); + } #if 0 //todo 发现Firefox(88.0)在mac平台下,开启rtx后没有指定ssrc @@ -1539,6 +1588,8 @@ RETRY: answer_media.fingerprint = configure.fingerprint; answer_media.ice_lite = configure.ice_lite; answer_media.candidate = configure.candidate; + answer_media.rtp_rids = offer_media.rtp_rids; + answer_media.rtp_ssrc_sim = offer_media.rtp_ssrc_sim; switch (offer_media.role) { case DtlsRole::actpass : case DtlsRole::active : { diff --git a/webrtc/Sdp.h b/webrtc/Sdp.h index 0f0a5c1c..3a111649 100644 --- a/webrtc/Sdp.h +++ b/webrtc/Sdp.h @@ -475,17 +475,18 @@ public: const char* getKey() const override { return "simulcast";} void parse(const string &str) override; string toString() const override; - + bool empty() const { return rids.empty(); } string direction; - string rid0; - string rid1; - string rid2; + vector rids; }; class SdpAttrRid : public SdpItem{ public: - //todo + void parse(const string &str) override; + string toString() const override; const char* getKey() const override { return "rid";} + string direction; + string rid; }; class RtcSdpBase { @@ -612,6 +613,7 @@ public: //////// simulcast //////// vector rtp_ssrc_sim; + vector rtp_rids; //////// rtcp //////// bool rtcp_mux{false};