diff --git a/.gitignore b/.gitignore index ffb0e5b3..37c326cd 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ /3rdpart/media-server/.idea/ /ios/ /cmake-build-* +/3rdpart/ZLToolKit/cmake-build-mq/ diff --git a/webrtc/Sdp.cpp b/webrtc/Sdp.cpp index affbec7b..35cad68e 100644 --- a/webrtc/Sdp.cpp +++ b/webrtc/Sdp.cpp @@ -659,21 +659,13 @@ string SdpAttrSSRC::toString() const { void SdpAttrSSRCGroup::parse(const string &str) { auto vec = split(str, " "); - if (vec.size() == 3) { - if (vec[0] != "FID") { - SDP_THROW(); - } + if (vec.size() >= 3) { type = std::move(vec[0]); - u.fid.rtp_ssrc = atoll(vec[1].data()) & 0xFFFFFFFF; - u.fid.rtx_ssrc = atoll(vec[2].data()) & 0xFFFFFFFF; - } else if (vec.size() == 4) { - if (vec[0] != "SIM") { - SDP_THROW(); + CHECK(isFID() || isSIM()); + vec.erase(vec.begin()); + for (auto ssrc : vec) { + ssrcs.emplace_back((uint32_t)atoll(ssrc.data())); } - type = std::move(vec[0]); - u.sim.rtp_ssrc_low = atoll(vec[1].data()) & 0xFFFFFFFF; - u.sim.rtp_ssrc_mid = atoll(vec[2].data()) & 0xFFFFFFFF; - u.sim.rtp_ssrc_high = atoll(vec[3].data()) & 0xFFFFFFFF; } else { SDP_THROW(); } @@ -681,12 +673,12 @@ void SdpAttrSSRCGroup::parse(const string &str) { string SdpAttrSSRCGroup::toString() const { if (value.empty()) { - if (isFID()) { - value = type + " " + to_string(u.fid.rtp_ssrc) + " " + to_string(u.fid.rtx_ssrc); - } else if (isSIM()) { - value = type + " " + to_string(u.sim.rtp_ssrc_low) + " " + to_string(u.sim.rtp_ssrc_mid) + " " + to_string(u.sim.rtp_ssrc_high); - } else { - SDP_THROW2(); + value = type; + //最少要求2个ssrc + CHECK(ssrcs.size() >= 2); + for (auto &ssrc : ssrcs) { + value += ' '; + value += to_string(ssrc); } } return SdpItem::toString(); @@ -758,6 +750,34 @@ string SdpAttrCandidate::toString() const { return SdpItem::toString(); } +void SdpAttrSimulcast::parse(const string &str) { + //https://www.meetecho.com/blog/simulcast-janus-ssrc/ + //a=simulcast:send/recv q;h;f + //a=simulcast:send/recv [rid=]q;h;f + //a=simulcast: recv h;m;l + // + auto vec = split(str, " "); + if (vec.size() != 2) { + 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]; + } + } +} + +string SdpAttrSimulcast::toString() const { + if (value.empty()) { + value = direction + " " + rid0 + ";" + rid1 + ";" + rid2; + } + return SdpItem::toString(); +} + void RtcSession::loadFrom(const string &str, bool check) { RtcSessionSdp sdp; sdp.parse(str); @@ -838,44 +858,43 @@ void RtcSession::loadFrom(const string &str, bool check) { } } - uint32_t ssrc_rtp = 0, ssrc_rtx = 0, ssrc_rtp_low = 0, ssrc_rtp_mid = 0, ssrc_rtp_high = 0; auto ssrc_groups = media.getAllItem('a', "ssrc-group"); + SdpAttrSSRCGroup *ssrc_group_sim = nullptr; + SdpAttrSSRCGroup *ssrc_group_fid = nullptr; for (auto &group : ssrc_groups) { if (group.isFID()) { - ssrc_rtp = group.u.fid.rtp_ssrc; - ssrc_rtx = group.u.fid.rtx_ssrc; + ssrc_group_fid = &group; } else if (group.isSIM()) { - ssrc_rtp_low = group.u.sim.rtp_ssrc_low; - ssrc_rtp_mid = group.u.sim.rtp_ssrc_mid; - ssrc_rtp_high = group.u.sim.rtp_ssrc_high; + ssrc_group_sim = &group; } } - if (!ssrc_rtp) { - //没有指定ssrc-group字段,那么只有一个ssrc - if (rtc_ssrc_map.size() > 1) { - throw std::invalid_argument("sdp中不存在a=ssrc-group:FID字段,但是ssrc却有多个"); + if (ssrc_group_fid) { + //指定了ssrc-group:FID字段 + for (auto ssrc : ssrc_group_fid->ssrcs) { + auto it = rtc_ssrc_map.find(ssrc); + if (it == rtc_ssrc_map.end()) { + throw std::invalid_argument("a=ssrc-group:FID字段指定的ssrc未找到"); + } + rtc_media.rtp_rtx_ssrc.emplace_back(it->second); } + CHECK(rtc_media.rtp_rtx_ssrc.size() == 2); + } else { + //没有指定ssrc-group:FID字段,那么只有1个或0个ssrc if (rtc_ssrc_map.size() == 1) { - ssrc_rtp = rtc_ssrc_map.begin()->second.ssrc; + 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个"); } } - for (auto &pr : rtc_ssrc_map) { - auto &rtc_ssrc = pr.second; - if (rtc_ssrc.ssrc == ssrc_rtp) { - rtc_media.rtp_ssrc = rtc_ssrc; - } - if (rtc_ssrc.ssrc == ssrc_rtx) { - rtc_media.rtx_ssrc = rtc_ssrc; - } - if (rtc_ssrc.ssrc == ssrc_rtp_low) { - rtc_media.rtp_ssrc_low = rtc_ssrc; - } - if (rtc_ssrc.ssrc == ssrc_rtp_mid) { - rtc_media.rtp_ssrc_mid = rtc_ssrc; - } - if (rtc_ssrc.ssrc == ssrc_rtp_high) { - rtc_media.rtp_ssrc_high = rtc_ssrc; + + if (ssrc_group_sim) { + for (auto ssrc : ssrc_group_sim->ssrcs) { + auto it = rtc_ssrc_map.find(ssrc); + if (it == rtc_ssrc_map.end()) { + throw std::invalid_argument("a=ssrc-group:SIM字段指定的ssrc未找到"); + } + rtc_media.rtp_ssrc_sim.emplace_back(it->second); } } @@ -1081,12 +1100,14 @@ RtcSessionSdp::Ptr RtcSession::toRtcSessionSdp() const{ rtp_map->codec = p.codec; rtp_map->sample_rate = p.sample_rate; rtp_map->channel = p.channel; + //添加a=rtpmap sdp_media.items.emplace_back(wrapSdpAttr(std::move(rtp_map))); for (auto &fb : p.rtcp_fb) { auto rtcp_fb = std::make_shared(); rtcp_fb->pt = p.pt; rtcp_fb->rtcp_type = fb; + //添加a=rtcp-fb sdp_media.items.emplace_back(wrapSdpAttr(std::move(rtcp_fb))); } @@ -1094,29 +1115,26 @@ RtcSessionSdp::Ptr RtcSession::toRtcSessionSdp() const{ auto fmtp = std::make_shared(); fmtp->pt = p.pt; fmtp->fmtp = p.fmtp; + //添加a=fmtp sdp_media.items.emplace_back(wrapSdpAttr(std::move(fmtp))); } } - if (!m.rtp_ssrc.empty()) { - auto msid = std::make_shared(); - if (!m.rtp_ssrc.msid.empty()) { - msid->parse(m.rtp_ssrc.msid); - } else { - msid->parse("mslabel label"); + { + //添加a=msid字段 + if (!m.rtp_rtx_ssrc.empty()) { + auto msid = std::make_shared(); + if (!m.rtp_rtx_ssrc[0].msid.empty()) { + msid->parse(m.rtp_rtx_ssrc[0].msid); + } else { + msid->parse("mslabel label"); + } + sdp_media.items.emplace_back(wrapSdpAttr(std::move(msid))); } - sdp_media.items.emplace_back(wrapSdpAttr(std::move(msid))); - } - - if (!m.rtp_ssrc.empty() && !m.rtx_ssrc.empty()) { - auto group = std::make_shared(); - group->type = "FID"; - group->u.fid.rtp_ssrc = m.rtp_ssrc.ssrc; - group->u.fid.rtx_ssrc = m.rtx_ssrc.ssrc; - sdp_media.items.emplace_back(wrapSdpAttr(std::move(group))); } static auto addSSRCItem = [](const RtcSSRC &rtp_ssrc, vector &items) { + CHECK(!rtp_ssrc.empty()); SdpAttrSSRC ssrc; ssrc.ssrc = rtp_ssrc.ssrc; @@ -1142,27 +1160,34 @@ RtcSessionSdp::Ptr RtcSession::toRtcSessionSdp() const{ items.emplace_back(wrapSdpAttr(std::make_shared(ssrc))); } }; - if (!m.rtp_ssrc.empty()) { - addSSRCItem(m.rtp_ssrc, sdp_media.items); - } - if (!m.rtx_ssrc.empty()) { - addSSRCItem(m.rtx_ssrc, sdp_media.items); + + { + auto group = std::make_shared(); + for (auto &ssrc : m.rtp_rtx_ssrc) { + //添加a=ssrc字段 + addSSRCItem(ssrc, sdp_media.items); + group->ssrcs.emplace_back(ssrc.ssrc); + } + if (group->ssrcs.size() >= 2) { + group->type = "FID"; + //生成a=ssrc-group:FID字段 + sdp_media.items.emplace_back(wrapSdpAttr(std::move(group))); + } } - bool enable_sim = false; - if (!m.rtp_ssrc_low.empty() && !m.rtp_ssrc_mid.empty() && !m.rtp_ssrc_high.empty()) { - auto group = std::make_shared(); - group->type = "SIM"; - group->u.sim.rtp_ssrc_low = m.rtp_ssrc_low.ssrc; - group->u.sim.rtp_ssrc_mid = m.rtp_ssrc_mid.ssrc; - group->u.sim.rtp_ssrc_high = m.rtp_ssrc_high.ssrc; - sdp_media.items.emplace_back(wrapSdpAttr(std::move(group))); - enable_sim = true; - } - if (enable_sim) { - addSSRCItem(m.rtp_ssrc_low, sdp_media.items); - addSSRCItem(m.rtp_ssrc_mid, sdp_media.items); - addSSRCItem(m.rtp_ssrc_high, sdp_media.items); + { + if (m.rtp_ssrc_sim.size() >= 2) { + //simulcast 要求 2~3路 + auto group = std::make_shared(); + for (auto &ssrc : m.rtp_ssrc_sim) { + //添加simulcast的ssrc + addSSRCItem(ssrc, sdp_media.items); + group->ssrcs.emplace_back(ssrc.ssrc); + } + //添加a=ssrc-group:SIM字段 + group->type = "SIM"; + sdp_media.items.emplace_back(wrapSdpAttr(std::move(group))); + } } } else { sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared(m.sctpmap))); @@ -1227,12 +1252,11 @@ 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_ssrc.empty() || !send_rtp); + CHECK(!rtp_rtx_ssrc.empty() || !send_rtp); auto rtx_plan = getPlan("rtx"); if (rtx_plan) { //开启rtx后必须指定rtx_ssrc - //todo 此处不确定 -// CHECK(!rtx_ssrc.empty() || !send_rtp); + CHECK(rtp_rtx_ssrc.size() >= 2 || !send_rtp); } } diff --git a/webrtc/Sdp.h b/webrtc/Sdp.h index bfff21eb..0f0a5c1c 100644 --- a/webrtc/Sdp.h +++ b/webrtc/Sdp.h @@ -414,17 +414,7 @@ public: //a=ssrc-group:SIM 360918977 360918978 360918980 // 在 Chrome 独有的 SDP munging 风格的 simulcast 中使用,将三组编码质量由低到高的 MediaStreamTrack 关联在一起。 string type{"FID"}; - union { - struct { - uint32_t rtp_ssrc; - uint32_t rtx_ssrc; - } fid; - struct { - uint32_t rtp_ssrc_low; - uint32_t rtp_ssrc_mid; - uint32_t rtp_ssrc_high; - } sim; - } u; + vector ssrcs; bool isFID() const { return type == "FID"; } bool isSIM() const { return type == "SIM"; } @@ -480,8 +470,16 @@ public: class SdpAttrSimulcast : public SdpItem{ public: - //todo + //https://www.meetecho.com/blog/simulcast-janus-ssrc/ + //https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-14 const char* getKey() const override { return "simulcast";} + void parse(const string &str) override; + string toString() const override; + + string direction; + string rid0; + string rid1; + string rid2; }; class SdpAttrRid : public SdpItem{ @@ -610,13 +608,10 @@ public: vector plan; //////// rtp //////// - RtcSSRC rtp_ssrc; - RtcSSRC rtx_ssrc; + vector rtp_rtx_ssrc; //////// simulcast //////// - RtcSSRC rtp_ssrc_low; - RtcSSRC rtp_ssrc_mid; - RtcSSRC rtp_ssrc_high; + vector rtp_ssrc_sim; //////// rtcp //////// bool rtcp_mux{false}; diff --git a/webrtc/WebRtcTransport.cpp b/webrtc/WebRtcTransport.cpp index ac48d0b1..f2e232d8 100644 --- a/webrtc/WebRtcTransport.cpp +++ b/webrtc/WebRtcTransport.cpp @@ -401,7 +401,7 @@ void WebRtcTransportImp::onStartWebRTC() { } //获取offer端rtp的ssrc和pt相关信息 auto &ref = _rtp_info_pt[plan.pt]; - _rtp_info_ssrc[m.rtp_ssrc.ssrc] = &ref; + _rtp_info_ssrc[m.rtp_rtx_ssrc[0].ssrc] = &ref; ref.plan = &plan; ref.media = &m; ref.is_common_rtp = getCodecId(plan.codec) != CodecInvalid; @@ -482,16 +482,17 @@ void WebRtcTransportImp::onCheckSdp(SdpType type, RtcSession &sdp){ continue; } //添加answer sdp的ssrc信息 - m.rtp_ssrc.ssrc = _play_src->getSsrc(m.type); - m.rtp_ssrc.cname = RTP_CNAME; - m.rtp_ssrc.label = RTP_LABEL; - m.rtp_ssrc.mslabel = RTP_MSLABEL; - m.rtp_ssrc.msid = RTP_MSID; + m.rtp_rtx_ssrc.emplace_back(); + m.rtp_rtx_ssrc[0].ssrc = _play_src->getSsrc(m.type); + m.rtp_rtx_ssrc[0].cname = RTP_CNAME; + m.rtp_rtx_ssrc[0].label = RTP_LABEL; + m.rtp_rtx_ssrc[0].mslabel = RTP_MSLABEL; + m.rtp_rtx_ssrc[0].msid = RTP_MSID; - //todo 先屏蔽rtx,因为chrome报错 if (m.getRelatedRtxPlan(m.plan[0].pt)) { - m.rtx_ssrc = m.rtp_ssrc; - m.rtx_ssrc.ssrc += RTX_SSRC_OFFSET; + m.rtp_rtx_ssrc.emplace_back(); + m.rtp_rtx_ssrc[1] = m.rtp_rtx_ssrc[0]; + m.rtp_rtx_ssrc[1].ssrc += RTX_SSRC_OFFSET; } } }