mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-22 19:00:01 +08:00
完善sdp对ssrc的处理
This commit is contained in:
parent
26762b6a6f
commit
e81d91988d
1
.gitignore
vendored
1
.gitignore
vendored
@ -46,3 +46,4 @@
|
||||
/3rdpart/media-server/.idea/
|
||||
/ios/
|
||||
/cmake-build-*
|
||||
/3rdpart/ZLToolKit/cmake-build-mq/
|
||||
|
190
webrtc/Sdp.cpp
190
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<SdpAttrSSRCGroup>('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<SdpAttrRtcpFb>();
|
||||
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<SdpAttrFmtp>();
|
||||
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<SdpAttrMsid>();
|
||||
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<SdpAttrMsid>();
|
||||
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<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) {
|
||||
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<SdpAttrSSRC>(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<SdpAttrSSRCGroup>();
|
||||
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<SdpAttrSSRCGroup>();
|
||||
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<SdpAttrSSRCGroup>();
|
||||
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<SdpAttrSctpMap>(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
29
webrtc/Sdp.h
29
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<uint32_t> 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<RtcCodecPlan> plan;
|
||||
|
||||
//////// rtp ////////
|
||||
RtcSSRC rtp_ssrc;
|
||||
RtcSSRC rtx_ssrc;
|
||||
vector<RtcSSRC> rtp_rtx_ssrc;
|
||||
|
||||
//////// simulcast ////////
|
||||
RtcSSRC rtp_ssrc_low;
|
||||
RtcSSRC rtp_ssrc_mid;
|
||||
RtcSSRC rtp_ssrc_high;
|
||||
vector<RtcSSRC> rtp_ssrc_sim;
|
||||
|
||||
//////// rtcp ////////
|
||||
bool rtcp_mux{false};
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user