From 87c53dab92d54d146b0e3d91125f4282b8cb504e Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 29 Mar 2021 23:03:55 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90sdp=E7=9A=84=E5=88=86?= =?UTF-8?q?=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webrtc/Sdp.cpp | 173 ++++++++++++++++++++++++++++++++++++++++++------- webrtc/Sdp.h | 62 ++++++++++++++---- 2 files changed, 198 insertions(+), 37 deletions(-) diff --git a/webrtc/Sdp.cpp b/webrtc/Sdp.cpp index b30e6039..0cfa1892 100644 --- a/webrtc/Sdp.cpp +++ b/webrtc/Sdp.cpp @@ -189,7 +189,7 @@ SdpItem::Ptr RtcSdpBase::getItem(char key, const char *attr_key) const { return item; } auto attr = dynamic_pointer_cast(item); - if (attr && attr->detail->getKey() == attr_key) { + if (attr && !strcmp(attr->detail->getKey() , attr_key)) { return attr->detail; } } @@ -554,23 +554,18 @@ string SdpAttrRtpMap::toString() const { return SdpItem::toString(); } -void SdpAttrRtcpFb::parse(const string &str) { - auto vec = split(str, " "); - if (vec.size() < 2) { +void SdpAttrRtcpFb::parse(const string &str_in) { + auto str = str_in + "\n"; + char rtcp_type_buf[32] = {0}; + if (2 != sscanf(str.data(), "%" SCNu8 " %31[^\n]", &pt, rtcp_type_buf)) { SDP_THROW(); } - pt = atoi(vec[0].data()); - vec.erase(vec.begin()); - arr = std::move(vec); + rtcp_type = rtcp_type_buf; } string SdpAttrRtcpFb::toString() const { if (value.empty()) { - value = to_string(pt); - for (auto &item : arr) { - value += ' '; - value += item; - } + value = to_string(pt) + " " + rtcp_type; } return SdpItem::toString(); } @@ -870,6 +865,13 @@ void test_sdp(){ } InfoL << sdp1.toString(); InfoL << sdp2.toString(); + + RtcSession session1; + session1.loadFrom(str1); + + RtcSession session2; + session2.loadFrom(str2); + InfoL; } void RtcSession::loadFrom(const string &str) { @@ -882,32 +884,153 @@ void RtcSession::loadFrom(const string &str) { session_info = sdp.getSessionInfo(); connection = sdp.getConnection(); bandwidth = sdp.getBandwidth(); - auto group = sdp.getItemClass('a', "group"); - auto mids = group.mids; - + msid_semantic = sdp.getItemClass('a', "msid-semantic"); for (auto &media : sdp.medias) { auto mline = media.getItemClass('m'); switch (mline.type) { case TrackVideo: case TrackAudio: - case TrackApplication: - break; + case TrackApplication: break; default: throw std::invalid_argument(StrPrinter << "不识别的media类型:" << mline.toString()); } - RtcMedia rtc_media; + this->media.emplace_back(); + auto &rtc_media = this->media.back(); rtc_media.type = mline.type; rtc_media.mid = media.getStringItem('a', "mid"); rtc_media.proto = mline.proto; rtc_media.type = mline.type; rtc_media.port = mline.port; - rtc_media.direction = media.getDirection(); - rtc_media.rtp_addr = media.getItemClass('c'); - rtc_media.rtcp_addr = media.getItemClass('a',"rtcp"); - rtc_media.ice_ufrag = media.getStringItem('a',"ice-ufrag"); - rtc_media.ice_ufrag = media.getStringItem('a',"ice-pwd"); + rtc_media.addr = media.getItemClass('c'); + rtc_media.ice_ufrag = media.getStringItem('a', "ice-ufrag"); + rtc_media.ice_pwd = media.getStringItem('a', "ice-pwd"); + rtc_media.role = media.getItemClass('a', "setup").role; rtc_media.fingerprint = media.getItemClass('a', "fingerprint"); - rtc_media.role = media.getItemClass('a',"setup").role; -// rtc_media.rtcp_mux = + rtc_media.ice_trickle = media.getItem('a', "ice-trickle").operator bool(); + rtc_media.ice_lite = media.getItem('a', "ice-lite").operator bool(); + rtc_media.ice_renomination = media.getItem('a', "ice-renomination").operator bool(); + rtc_media.candidate = media.getAllItem('a', "candidate"); + + if (mline.type == TrackType::TrackApplication) { + rtc_media.sctp_port = atoi(media.getStringItem('a', "sctp-port").data()); + rtc_media.sctpmap = media.getItemClass('a', "sctpmap"); + continue; + } + rtc_media.rtcp_addr = media.getItemClass('a', "rtcp"); + rtc_media.direction = media.getDirection(); + rtc_media.extmap = media.getAllItem('a', "extmap"); + rtc_media.rtcp_mux = media.getItem('a', "rtcp-mux").operator bool(); + rtc_media.rtcp_rsize = media.getItem('a', "rtcp-rsize").operator bool(); + + map rtc_ssrc_map; + for (auto &ssrc : media.getAllItem('a', "ssrc")) { + auto &rtc_ssrc = rtc_ssrc_map[ssrc.ssrc]; + rtc_ssrc.ssrc = ssrc.ssrc; + if (ssrc.attribute == "cname") { + rtc_ssrc.cname = ssrc.attribute_value; + continue; + } + if (ssrc.attribute == "msid") { + rtc_ssrc.msid = ssrc.attribute_value; + continue; + } + if (ssrc.attribute == "mslabel") { + rtc_ssrc.mslabel = ssrc.attribute_value; + continue; + } + if (ssrc.attribute == "label") { + rtc_ssrc.label = ssrc.attribute_value; + continue; + } + } + + 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"); + for (auto &group : ssrc_groups) { + if (group.isFID()) { + ssrc_rtp = group.u.fid.rtp_ssrc; + ssrc_rtx = group.u.fid.rtx_ssrc; + rtc_media.rtx = true; + } else if (group.isSIM()) { + rtc_media.simulcast = true; + 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; + } + } + + if (!ssrc_rtp) { + //没有指定ssrc-group字段,那么只有一个ssrc + if (rtc_ssrc_map.size() > 1) { + throw std::invalid_argument("sdp中不存在a=ssrc-group:FID字段,但是ssrc却有多个"); + } + ssrc_rtp = rtc_ssrc_map.begin()->second.ssrc; + } + 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; + } + } + + auto rtpmap_arr = media.getAllItem('a', "rtpmap"); + auto rtcpfb_arr = media.getAllItem('a', "rtcp-fb"); + auto fmtp_aar = media.getAllItem('a', "fmtp"); + //方便根据pt查找rtpmap,一个pt必有一条 + map rtpmap_map; + //方便根据pt查找rtcp-fb,一个pt可能有多条或0条 + multimap rtcpfb_map; + //方便根据pt查找fmtp,一个pt最多一条 + map fmtp_map; + + for (auto &rtpmap : rtpmap_arr) { + if (!rtpmap_map.emplace(rtpmap.pt, rtpmap).second) { + //添加失败,有多条 + throw std::invalid_argument(StrPrinter << "该pt存在多条a=rtpmap:" << rtpmap.pt); + } + } + for (auto &rtpfb : rtcpfb_arr) { + rtcpfb_map.emplace(rtpfb.pt, rtpfb); + } + for (auto &fmtp : fmtp_aar) { + if (!fmtp_map.emplace(fmtp.pt, fmtp).second) { + //添加失败,有多条 + throw std::invalid_argument(StrPrinter << "该pt存在多条a=fmtp:" << fmtp.pt); + } + } + for (auto &pt : mline.fmts) { + //遍历所有编码方案的pt + rtc_media.plan.emplace_back(); + auto &plan = rtc_media.plan.back(); + auto rtpmap_it = rtpmap_map.find(pt); + if (rtpmap_it == rtpmap_map.end()) { + throw std::invalid_argument(StrPrinter << "该pt不存在相对于的a=rtpmap:" << pt); + } + plan.pt = rtpmap_it->second.pt; + plan.codec = rtpmap_it->second.codec; + plan.sample_rate = rtpmap_it->second.sample_rate; + plan.channel = rtpmap_it->second.channel; + auto fmtp_it = fmtp_map.find(pt); + if (fmtp_it != fmtp_map.end()) { + plan.fmtp = fmtp_it->second.arr; + } + for (auto rtpfb_it = rtcpfb_map.find(pt); + rtpfb_it != rtcpfb_map.end() && rtpfb_it->second.pt == pt; ++rtpfb_it) { + plan.rtcp_fb.emplace_back(rtpfb_it->second.rtcp_type); + } + } } + group = sdp.getItemClass('a', "group"); } diff --git a/webrtc/Sdp.h b/webrtc/Sdp.h index 78ee3b6d..ed2ddad5 100644 --- a/webrtc/Sdp.h +++ b/webrtc/Sdp.h @@ -294,7 +294,7 @@ class SdpAttrExtmap : public SdpItem { public: //https://aggresss.blog.csdn.net/article/details/106436703 //a=extmap:1[/sendonly] urn:ietf:params:rtp-hdrext:ssrc-audio-level - int index; + uint32_t index; RtpDirection direction{RtpDirection::invalid}; string ext; void parse(const string &str) override; @@ -307,8 +307,8 @@ public: //a=rtpmap:111 opus/48000/2 uint8_t pt; string codec; - int sample_rate; - int channel {0}; + uint32_t sample_rate; + uint32_t channel {0}; void parse(const string &str) override; string toString() const override; const char* getKey() const override { return "rtpmap";} @@ -323,7 +323,7 @@ public: //a=rtcp-fb:120 goog-remb 支持 REMB (Receiver Estimated Maximum Bitrate) 。 //a=rtcp-fb:120 transport-cc 支持 TCC (Transport Congest Control) 。 uint8_t pt; - vector arr; + string rtcp_type; void parse(const string &str) override; string toString() const override; const char* getKey() const override { return "rtcp-fb";} @@ -383,6 +383,8 @@ public: } sim; } u; + bool isFID() const { return type == "FID"; } + bool isSIM() const { return type == "SIM"; } void parse(const string &str) override; string toString() const override; const char* getKey() const override { return "ssrc-group";} @@ -395,7 +397,7 @@ public: //a=sctpmap: sctpmap-number media-subtypes [streams] uint16_t port; string subtypes; - int streams; + uint32_t streams; void parse(const string &str) override; string toString() const override; const char* getKey() const override { return "sctpmap";} @@ -426,6 +428,7 @@ public: vector items; public: + virtual ~RtcSdpBase() = default; virtual string toString() const; int getVersion() const; @@ -461,8 +464,31 @@ public: return item->toString(); } -private: SdpItem::Ptr getItem(char key, const char *attr_key = nullptr) const; + + template + vector getAllItem(char key, const char *attr_key = nullptr) const { + vector ret; + for (auto item : items) { + if (item->getKey()[0] == key) { + if (!attr_key) { + auto c = dynamic_pointer_cast(item); + if (c) { + ret.emplace_back(*c); + } + } else { + auto attr = dynamic_pointer_cast(item); + if (attr && !strcmp(attr->detail->getKey(), attr_key)) { + auto c = dynamic_pointer_cast(attr->detail); + if (c) { + ret.emplace_back(*c); + } + } + } + } + } + return ret; + } }; class RtcSessionSdp : public RtcSdpBase{ @@ -486,7 +512,7 @@ enum class RtcSSRCType { //ssrc相关信息 class RtcSSRC{ public: - RtcSSRCType type; + uint32_t ssrc {0}; string cname; string msid; string mslabel; @@ -514,15 +540,22 @@ public: string proto; //////// rtp //////// - RtcSSRC ssrc; - SdpConnection rtp_addr; + RtcSSRC rtp_ssrc; + // for simulcast + bool simulcast{false}; + RtcSSRC rtp_ssrc_low; + RtcSSRC rtp_ssrc_mid; + RtcSSRC rtp_ssrc_high; + + SdpConnection addr; RtpDirection direction; vector plan; //////// rtx - rtcp //////// + bool rtx{false}; bool rtcp_mux; bool rtcp_rsize; - uint32_t rtx_ssrc; + RtcSSRC rtx_ssrc; SdpAttrRtcp rtcp_addr; //////// ice //////// @@ -539,18 +572,23 @@ public: //////// extmap //////// vector extmap; + + //////// sctp //////////// + SdpAttrSctpMap sctpmap; + uint32_t sctp_port {0}; }; class RtcSession{ public: - int version; + uint32_t version; SdpOrigin origin; string session_name; string session_info; SdpConnection connection; SdpBandwidth bandwidth; - set group_bundle; + SdpAttrMsidSemantic msid_semantic; vector media; + SdpAttrGroup group; void loadFrom(const string &sdp); };