完善sdp对ssrc的处理

This commit is contained in:
xia-chu 2021-05-08 18:39:35 +08:00
parent 26762b6a6f
commit e81d91988d
4 changed files with 130 additions and 109 deletions

1
.gitignore vendored
View File

@ -46,3 +46,4 @@
/3rdpart/media-server/.idea/ /3rdpart/media-server/.idea/
/ios/ /ios/
/cmake-build-* /cmake-build-*
/3rdpart/ZLToolKit/cmake-build-mq/

View File

@ -659,21 +659,13 @@ string SdpAttrSSRC::toString() const {
void SdpAttrSSRCGroup::parse(const string &str) { void SdpAttrSSRCGroup::parse(const string &str) {
auto vec = split(str, " "); auto vec = split(str, " ");
if (vec.size() == 3) { if (vec.size() >= 3) {
if (vec[0] != "FID") {
SDP_THROW();
}
type = std::move(vec[0]); type = std::move(vec[0]);
u.fid.rtp_ssrc = atoll(vec[1].data()) & 0xFFFFFFFF; CHECK(isFID() || isSIM());
u.fid.rtx_ssrc = atoll(vec[2].data()) & 0xFFFFFFFF; vec.erase(vec.begin());
} else if (vec.size() == 4) { for (auto ssrc : vec) {
if (vec[0] != "SIM") { ssrcs.emplace_back((uint32_t)atoll(ssrc.data()));
SDP_THROW();
} }
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 { } else {
SDP_THROW(); SDP_THROW();
} }
@ -681,12 +673,12 @@ void SdpAttrSSRCGroup::parse(const string &str) {
string SdpAttrSSRCGroup::toString() const { string SdpAttrSSRCGroup::toString() const {
if (value.empty()) { if (value.empty()) {
if (isFID()) { value = type;
value = type + " " + to_string(u.fid.rtp_ssrc) + " " + to_string(u.fid.rtx_ssrc); //最少要求2个ssrc
} else if (isSIM()) { CHECK(ssrcs.size() >= 2);
value = type + " " + to_string(u.sim.rtp_ssrc_low) + " " + to_string(u.sim.rtp_ssrc_mid) + " " + to_string(u.sim.rtp_ssrc_high); for (auto &ssrc : ssrcs) {
} else { value += ' ';
SDP_THROW2(); value += to_string(ssrc);
} }
} }
return SdpItem::toString(); return SdpItem::toString();
@ -758,6 +750,34 @@ string SdpAttrCandidate::toString() const {
return SdpItem::toString(); 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) { void RtcSession::loadFrom(const string &str, bool check) {
RtcSessionSdp sdp; RtcSessionSdp sdp;
sdp.parse(str); 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<SdpAttrSSRCGroup>('a', "ssrc-group"); auto ssrc_groups = media.getAllItem<SdpAttrSSRCGroup>('a', "ssrc-group");
SdpAttrSSRCGroup *ssrc_group_sim = nullptr;
SdpAttrSSRCGroup *ssrc_group_fid = nullptr;
for (auto &group : ssrc_groups) { for (auto &group : ssrc_groups) {
if (group.isFID()) { if (group.isFID()) {
ssrc_rtp = group.u.fid.rtp_ssrc; ssrc_group_fid = &group;
ssrc_rtx = group.u.fid.rtx_ssrc;
} else if (group.isSIM()) { } else if (group.isSIM()) {
ssrc_rtp_low = group.u.sim.rtp_ssrc_low; ssrc_group_sim = &group;
ssrc_rtp_mid = group.u.sim.rtp_ssrc_mid;
ssrc_rtp_high = group.u.sim.rtp_ssrc_high;
} }
} }
if (!ssrc_rtp) { if (ssrc_group_fid) {
//没有指定ssrc-group字段那么只有一个ssrc //指定了ssrc-group:FID字段
if (rtc_ssrc_map.size() > 1) { for (auto ssrc : ssrc_group_fid->ssrcs) {
throw std::invalid_argument("sdp中不存在a=ssrc-group:FID字段,但是ssrc却有多个"); 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) { 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 (ssrc_group_sim) {
if (rtc_ssrc.ssrc == ssrc_rtp) { for (auto ssrc : ssrc_group_sim->ssrcs) {
rtc_media.rtp_ssrc = rtc_ssrc; auto it = rtc_ssrc_map.find(ssrc);
} if (it == rtc_ssrc_map.end()) {
if (rtc_ssrc.ssrc == ssrc_rtx) { throw std::invalid_argument("a=ssrc-group:SIM字段指定的ssrc未找到");
rtc_media.rtx_ssrc = rtc_ssrc; }
} rtc_media.rtp_ssrc_sim.emplace_back(it->second);
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;
} }
} }
@ -1081,12 +1100,14 @@ RtcSessionSdp::Ptr RtcSession::toRtcSessionSdp() const{
rtp_map->codec = p.codec; rtp_map->codec = p.codec;
rtp_map->sample_rate = p.sample_rate; rtp_map->sample_rate = p.sample_rate;
rtp_map->channel = p.channel; rtp_map->channel = p.channel;
//添加a=rtpmap
sdp_media.items.emplace_back(wrapSdpAttr(std::move(rtp_map))); sdp_media.items.emplace_back(wrapSdpAttr(std::move(rtp_map)));
for (auto &fb : p.rtcp_fb) { for (auto &fb : p.rtcp_fb) {
auto rtcp_fb = std::make_shared<SdpAttrRtcpFb>(); auto rtcp_fb = std::make_shared<SdpAttrRtcpFb>();
rtcp_fb->pt = p.pt; rtcp_fb->pt = p.pt;
rtcp_fb->rtcp_type = fb; rtcp_fb->rtcp_type = fb;
//添加a=rtcp-fb
sdp_media.items.emplace_back(wrapSdpAttr(std::move(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<SdpAttrFmtp>(); auto fmtp = std::make_shared<SdpAttrFmtp>();
fmtp->pt = p.pt; fmtp->pt = p.pt;
fmtp->fmtp = p.fmtp; fmtp->fmtp = p.fmtp;
//添加a=fmtp
sdp_media.items.emplace_back(wrapSdpAttr(std::move(fmtp))); sdp_media.items.emplace_back(wrapSdpAttr(std::move(fmtp)));
} }
} }
if (!m.rtp_ssrc.empty()) { {
auto msid = std::make_shared<SdpAttrMsid>(); //添加a=msid字段
if (!m.rtp_ssrc.msid.empty()) { if (!m.rtp_rtx_ssrc.empty()) {
msid->parse(m.rtp_ssrc.msid); auto msid = std::make_shared<SdpAttrMsid>();
} else { if (!m.rtp_rtx_ssrc[0].msid.empty()) {
msid->parse("mslabel label"); 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<SdpAttrSSRCGroup>();
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<SdpItem::Ptr> &items) { static auto addSSRCItem = [](const RtcSSRC &rtp_ssrc, vector<SdpItem::Ptr> &items) {
CHECK(!rtp_ssrc.empty());
SdpAttrSSRC ssrc; SdpAttrSSRC ssrc;
ssrc.ssrc = rtp_ssrc.ssrc; ssrc.ssrc = rtp_ssrc.ssrc;
@ -1142,27 +1160,34 @@ RtcSessionSdp::Ptr RtcSession::toRtcSessionSdp() const{
items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrSSRC>(ssrc))); items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrSSRC>(ssrc)));
} }
}; };
if (!m.rtp_ssrc.empty()) {
addSSRCItem(m.rtp_ssrc, sdp_media.items); {
} auto group = std::make_shared<SdpAttrSSRCGroup>();
if (!m.rtx_ssrc.empty()) { for (auto &ssrc : m.rtp_rtx_ssrc) {
addSSRCItem(m.rtx_ssrc, sdp_media.items); //添加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()) { if (m.rtp_ssrc_sim.size() >= 2) {
auto group = std::make_shared<SdpAttrSSRCGroup>(); //simulcast 要求 2~3路
group->type = "SIM"; auto group = std::make_shared<SdpAttrSSRCGroup>();
group->u.sim.rtp_ssrc_low = m.rtp_ssrc_low.ssrc; for (auto &ssrc : m.rtp_ssrc_sim) {
group->u.sim.rtp_ssrc_mid = m.rtp_ssrc_mid.ssrc; //添加simulcast的ssrc
group->u.sim.rtp_ssrc_high = m.rtp_ssrc_high.ssrc; addSSRCItem(ssrc, sdp_media.items);
sdp_media.items.emplace_back(wrapSdpAttr(std::move(group))); group->ssrcs.emplace_back(ssrc.ssrc);
enable_sim = true; }
} //添加a=ssrc-group:SIM字段
if (enable_sim) { group->type = "SIM";
addSSRCItem(m.rtp_ssrc_low, sdp_media.items); sdp_media.items.emplace_back(wrapSdpAttr(std::move(group)));
addSSRCItem(m.rtp_ssrc_mid, sdp_media.items); }
addSSRCItem(m.rtp_ssrc_high, sdp_media.items);
} }
} else { } else {
sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrSctpMap>(m.sctpmap))); sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrSctpMap>(m.sctpmap)));
@ -1227,12 +1252,11 @@ void RtcMedia::checkValid() const{
CHECK(direction != RtpDirection::invalid || type == TrackApplication); CHECK(direction != RtpDirection::invalid || type == TrackApplication);
CHECK(!plan.empty() || type == TrackApplication ); CHECK(!plan.empty() || type == TrackApplication );
bool send_rtp = (direction == RtpDirection::sendonly || direction == RtpDirection::sendrecv); 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"); auto rtx_plan = getPlan("rtx");
if (rtx_plan) { if (rtx_plan) {
//开启rtx后必须指定rtx_ssrc //开启rtx后必须指定rtx_ssrc
//todo 此处不确定 CHECK(rtp_rtx_ssrc.size() >= 2 || !send_rtp);
// CHECK(!rtx_ssrc.empty() || !send_rtp);
} }
} }

View File

@ -414,17 +414,7 @@ public:
//a=ssrc-group:SIM 360918977 360918978 360918980 //a=ssrc-group:SIM 360918977 360918978 360918980
// 在 Chrome 独有的 SDP munging 风格的 simulcast 中使用,将三组编码质量由低到高的 MediaStreamTrack 关联在一起。 // 在 Chrome 独有的 SDP munging 风格的 simulcast 中使用,将三组编码质量由低到高的 MediaStreamTrack 关联在一起。
string type{"FID"}; string type{"FID"};
union { vector<uint32_t> ssrcs;
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;
bool isFID() const { return type == "FID"; } bool isFID() const { return type == "FID"; }
bool isSIM() const { return type == "SIM"; } bool isSIM() const { return type == "SIM"; }
@ -480,8 +470,16 @@ public:
class SdpAttrSimulcast : public SdpItem{ class SdpAttrSimulcast : public SdpItem{
public: 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";} 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{ class SdpAttrRid : public SdpItem{
@ -610,13 +608,10 @@ public:
vector<RtcCodecPlan> plan; vector<RtcCodecPlan> plan;
//////// rtp //////// //////// rtp ////////
RtcSSRC rtp_ssrc; vector<RtcSSRC> rtp_rtx_ssrc;
RtcSSRC rtx_ssrc;
//////// simulcast //////// //////// simulcast ////////
RtcSSRC rtp_ssrc_low; vector<RtcSSRC> rtp_ssrc_sim;
RtcSSRC rtp_ssrc_mid;
RtcSSRC rtp_ssrc_high;
//////// rtcp //////// //////// rtcp ////////
bool rtcp_mux{false}; bool rtcp_mux{false};

View File

@ -401,7 +401,7 @@ void WebRtcTransportImp::onStartWebRTC() {
} }
//获取offer端rtp的ssrc和pt相关信息 //获取offer端rtp的ssrc和pt相关信息
auto &ref = _rtp_info_pt[plan.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.plan = &plan;
ref.media = &m; ref.media = &m;
ref.is_common_rtp = getCodecId(plan.codec) != CodecInvalid; ref.is_common_rtp = getCodecId(plan.codec) != CodecInvalid;
@ -482,16 +482,17 @@ void WebRtcTransportImp::onCheckSdp(SdpType type, RtcSession &sdp){
continue; continue;
} }
//添加answer sdp的ssrc信息 //添加answer sdp的ssrc信息
m.rtp_ssrc.ssrc = _play_src->getSsrc(m.type); m.rtp_rtx_ssrc.emplace_back();
m.rtp_ssrc.cname = RTP_CNAME; m.rtp_rtx_ssrc[0].ssrc = _play_src->getSsrc(m.type);
m.rtp_ssrc.label = RTP_LABEL; m.rtp_rtx_ssrc[0].cname = RTP_CNAME;
m.rtp_ssrc.mslabel = RTP_MSLABEL; m.rtp_rtx_ssrc[0].label = RTP_LABEL;
m.rtp_ssrc.msid = RTP_MSID; 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)) { if (m.getRelatedRtxPlan(m.plan[0].pt)) {
m.rtx_ssrc = m.rtp_ssrc; m.rtp_rtx_ssrc.emplace_back();
m.rtx_ssrc.ssrc += RTX_SSRC_OFFSET; m.rtp_rtx_ssrc[1] = m.rtp_rtx_ssrc[0];
m.rtp_rtx_ssrc[1].ssrc += RTX_SSRC_OFFSET;
} }
} }
} }