mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-25 12:11:36 +08:00
webrtc answer m-lines次序错误 (#1695)
Co-authored-by: james <mrjamesli@126.com>
This commit is contained in:
parent
483a75284e
commit
3a9408bcec
262
webrtc/Sdp.cpp
262
webrtc/Sdp.cpp
@ -1521,15 +1521,17 @@ void RtcConfigure::enableREMB(bool enable, TrackType type){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<RtcSession> RtcConfigure::createAnswer(const RtcSession &offer){
|
shared_ptr<RtcSession> RtcConfigure::createAnswer(const RtcSession &offer) const {
|
||||||
shared_ptr<RtcSession> ret = std::make_shared<RtcSession>();
|
shared_ptr<RtcSession> ret = std::make_shared<RtcSession>();
|
||||||
ret->version = offer.version;
|
ret->version = offer.version;
|
||||||
ret->origin = offer.origin;
|
ret->origin = offer.origin;
|
||||||
ret->session_name = offer.session_name;
|
ret->session_name = offer.session_name;
|
||||||
ret->msid_semantic = offer.msid_semantic;
|
ret->msid_semantic = offer.msid_semantic;
|
||||||
matchMedia(ret, TrackAudio, offer.media, audio);
|
|
||||||
matchMedia(ret, TrackVideo, offer.media, video);
|
for (auto &m : offer.media) {
|
||||||
matchMedia(ret, TrackApplication, offer.media, application);
|
matchMedia(ret, m);
|
||||||
|
}
|
||||||
|
|
||||||
//设置音视频端口复用
|
//设置音视频端口复用
|
||||||
if (!offer.group.mids.empty()) {
|
if (!offer.group.mids.empty()) {
|
||||||
for (auto &m : ret->media) {
|
for (auto &m : ret->media) {
|
||||||
@ -1573,153 +1575,157 @@ static DtlsRole mathDtlsRole(DtlsRole role){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtcConfigure::matchMedia(const shared_ptr<RtcSession> &ret, TrackType type, const vector<RtcMedia> &medias, const RtcTrackConfigure &configure){
|
void RtcConfigure::matchMedia(const std::shared_ptr<RtcSession> &ret,const RtcMedia &offer_media) const {
|
||||||
bool check_profile = true;
|
bool check_profile = true;
|
||||||
bool check_codec = true;
|
bool check_codec = true;
|
||||||
|
const RtcTrackConfigure *cfg_ptr = nullptr;
|
||||||
|
|
||||||
|
switch (offer_media.type) {
|
||||||
|
case TrackAudio: cfg_ptr = &audio; break;
|
||||||
|
case TrackVideo: cfg_ptr = &video; break;
|
||||||
|
case TrackApplication: cfg_ptr = &application; break;
|
||||||
|
default: return;
|
||||||
|
}
|
||||||
|
auto &configure = *cfg_ptr;
|
||||||
|
|
||||||
RETRY:
|
RETRY:
|
||||||
|
|
||||||
for (auto &offer_media : medias) {
|
if (offer_media.type == TrackApplication) {
|
||||||
if (offer_media.type != type) {
|
RtcMedia answer_media = offer_media;
|
||||||
|
answer_media.role = mathDtlsRole(offer_media.role);
|
||||||
|
#ifdef ENABLE_SCTP
|
||||||
|
answer_media.direction = matchDirection(offer_media.direction, configure.direction);
|
||||||
|
answer_media.candidate = configure.candidate;
|
||||||
|
#else
|
||||||
|
answer_media.direction = RtpDirection::inactive;
|
||||||
|
#endif
|
||||||
|
ret->media.emplace_back(answer_media);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (auto &codec : configure.preferred_codec) {
|
||||||
|
if (offer_media.ice_lite && configure.ice_lite) {
|
||||||
|
WarnL << "answer sdp配置为ice_lite模式,与offer sdp中的ice_lite模式冲突";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (type == TrackApplication) {
|
const RtcCodecPlan *selected_plan = nullptr;
|
||||||
RtcMedia answer_media = offer_media;
|
for (auto &plan : offer_media.plan) {
|
||||||
answer_media.role = mathDtlsRole(offer_media.role);
|
//先检查编码格式是否为偏好
|
||||||
#ifdef ENABLE_SCTP
|
if (check_codec && getCodecId(plan.codec) != codec) {
|
||||||
answer_media.direction = matchDirection(offer_media.direction, configure.direction);
|
continue;
|
||||||
answer_media.candidate = configure.candidate;
|
}
|
||||||
#else
|
//命中偏好的编码格式,然后检查规格
|
||||||
answer_media.direction = RtpDirection::inactive;
|
if (check_profile && !onCheckCodecProfile(plan, codec)) {
|
||||||
#endif
|
continue;
|
||||||
ret->media.emplace_back(answer_media);
|
}
|
||||||
return;
|
//找到中意的codec
|
||||||
|
selected_plan = &plan;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
for (auto &codec : configure.preferred_codec) {
|
if (!selected_plan) {
|
||||||
if (offer_media.ice_lite && configure.ice_lite) {
|
//offer中该媒体的所有的codec都不支持
|
||||||
WarnL << "answer sdp配置为ice_lite模式,与offer sdp中的ice_lite模式冲突";
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
RtcMedia answer_media;
|
||||||
const RtcCodecPlan *selected_plan = nullptr;
|
answer_media.type = offer_media.type;
|
||||||
|
answer_media.mid = offer_media.mid;
|
||||||
|
answer_media.proto = offer_media.proto;
|
||||||
|
answer_media.port = offer_media.port;
|
||||||
|
answer_media.addr = offer_media.addr;
|
||||||
|
answer_media.rtcp_addr = offer_media.rtcp_addr;
|
||||||
|
answer_media.rtcp_mux = offer_media.rtcp_mux && configure.rtcp_mux;
|
||||||
|
answer_media.rtcp_rsize = offer_media.rtcp_rsize && configure.rtcp_rsize;
|
||||||
|
answer_media.ice_trickle = offer_media.ice_trickle && configure.ice_trickle;
|
||||||
|
answer_media.ice_renomination = offer_media.ice_renomination && configure.ice_renomination;
|
||||||
|
answer_media.ice_ufrag = configure.ice_ufrag;
|
||||||
|
answer_media.ice_pwd = configure.ice_pwd;
|
||||||
|
answer_media.fingerprint = configure.fingerprint;
|
||||||
|
answer_media.ice_lite = configure.ice_lite;
|
||||||
|
answer_media.candidate = configure.candidate;
|
||||||
|
// copy simulicast setting
|
||||||
|
answer_media.rtp_rids = offer_media.rtp_rids;
|
||||||
|
answer_media.rtp_ssrc_sim = offer_media.rtp_ssrc_sim;
|
||||||
|
|
||||||
|
answer_media.role = mathDtlsRole(offer_media.role);
|
||||||
|
|
||||||
|
//如果codec匹配失败,那么禁用该track
|
||||||
|
answer_media.direction = check_codec ? matchDirection(offer_media.direction, configure.direction)
|
||||||
|
: RtpDirection::inactive;
|
||||||
|
if (answer_media.direction == RtpDirection::invalid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (answer_media.direction == RtpDirection::sendrecv) {
|
||||||
|
//如果是收发双向,那么我们拷贝offer sdp的ssrc,确保ssrc一致
|
||||||
|
answer_media.rtp_rtx_ssrc = offer_media.rtp_rtx_ssrc;
|
||||||
|
}
|
||||||
|
|
||||||
|
//添加媒体plan
|
||||||
|
answer_media.plan.emplace_back(*selected_plan);
|
||||||
|
onSelectPlan(answer_media.plan.back(), codec);
|
||||||
|
|
||||||
|
set<uint8_t> pt_selected = {selected_plan->pt};
|
||||||
|
|
||||||
|
//添加rtx,red,ulpfec plan
|
||||||
|
if (configure.support_red || configure.support_rtx || configure.support_ulpfec) {
|
||||||
for (auto &plan : offer_media.plan) {
|
for (auto &plan : offer_media.plan) {
|
||||||
//先检查编码格式是否为偏好
|
if (!strcasecmp(plan.codec.data(), "rtx")) {
|
||||||
if (check_codec && getCodecId(plan.codec) != codec) {
|
if (configure.support_rtx && atoi(plan.getFmtp("apt").data()) == selected_plan->pt) {
|
||||||
|
answer_media.plan.emplace_back(plan);
|
||||||
|
pt_selected.emplace(plan.pt);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
//命中偏好的编码格式,然后检查规格
|
if (!strcasecmp(plan.codec.data(), "red")) {
|
||||||
if (check_profile && !onCheckCodecProfile(plan, codec)) {
|
if (configure.support_red) {
|
||||||
|
answer_media.plan.emplace_back(plan);
|
||||||
|
pt_selected.emplace(plan.pt);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
//找到中意的codec
|
if (!strcasecmp(plan.codec.data(), "ulpfec")) {
|
||||||
selected_plan = &plan;
|
if (configure.support_ulpfec) {
|
||||||
break;
|
answer_media.plan.emplace_back(plan);
|
||||||
}
|
pt_selected.emplace(plan.pt);
|
||||||
if (!selected_plan) {
|
|
||||||
//offer中该媒体的所有的codec都不支持
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
RtcMedia answer_media;
|
|
||||||
answer_media.type = offer_media.type;
|
|
||||||
answer_media.mid = offer_media.mid;
|
|
||||||
answer_media.proto = offer_media.proto;
|
|
||||||
answer_media.port = offer_media.port;
|
|
||||||
answer_media.addr = offer_media.addr;
|
|
||||||
answer_media.rtcp_addr = offer_media.rtcp_addr;
|
|
||||||
answer_media.rtcp_mux = offer_media.rtcp_mux && configure.rtcp_mux;
|
|
||||||
answer_media.rtcp_rsize = offer_media.rtcp_rsize && configure.rtcp_rsize;
|
|
||||||
answer_media.ice_trickle = offer_media.ice_trickle && configure.ice_trickle;
|
|
||||||
answer_media.ice_renomination = offer_media.ice_renomination && configure.ice_renomination;
|
|
||||||
answer_media.ice_ufrag = configure.ice_ufrag;
|
|
||||||
answer_media.ice_pwd = configure.ice_pwd;
|
|
||||||
answer_media.fingerprint = configure.fingerprint;
|
|
||||||
answer_media.ice_lite = configure.ice_lite;
|
|
||||||
answer_media.candidate = configure.candidate;
|
|
||||||
// copy simulicast setting
|
|
||||||
answer_media.rtp_rids = offer_media.rtp_rids;
|
|
||||||
answer_media.rtp_ssrc_sim = offer_media.rtp_ssrc_sim;
|
|
||||||
|
|
||||||
answer_media.role = mathDtlsRole(offer_media.role);
|
|
||||||
|
|
||||||
//如果codec匹配失败,那么禁用该track
|
|
||||||
answer_media.direction = check_codec ? matchDirection(offer_media.direction, configure.direction)
|
|
||||||
: RtpDirection::inactive;
|
|
||||||
if (answer_media.direction == RtpDirection::invalid) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (answer_media.direction == RtpDirection::sendrecv) {
|
|
||||||
//如果是收发双向,那么我们拷贝offer sdp的ssrc,确保ssrc一致
|
|
||||||
answer_media.rtp_rtx_ssrc = offer_media.rtp_rtx_ssrc;
|
|
||||||
}
|
|
||||||
|
|
||||||
//添加媒体plan
|
|
||||||
answer_media.plan.emplace_back(*selected_plan);
|
|
||||||
onSelectPlan(answer_media.plan.back(), codec);
|
|
||||||
|
|
||||||
set<uint8_t> pt_selected = {selected_plan->pt};
|
|
||||||
|
|
||||||
//添加rtx,red,ulpfec plan
|
|
||||||
if (configure.support_red || configure.support_rtx || configure.support_ulpfec) {
|
|
||||||
for (auto &plan : offer_media.plan) {
|
|
||||||
if (!strcasecmp(plan.codec.data(), "rtx")) {
|
|
||||||
if (configure.support_rtx && atoi(plan.getFmtp("apt").data()) == selected_plan->pt) {
|
|
||||||
answer_media.plan.emplace_back(plan);
|
|
||||||
pt_selected.emplace(plan.pt);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strcasecmp(plan.codec.data(), "red")) {
|
|
||||||
if (configure.support_red) {
|
|
||||||
answer_media.plan.emplace_back(plan);
|
|
||||||
pt_selected.emplace(plan.pt);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strcasecmp(plan.codec.data(), "ulpfec")) {
|
|
||||||
if (configure.support_ulpfec) {
|
|
||||||
answer_media.plan.emplace_back(plan);
|
|
||||||
pt_selected.emplace(plan.pt);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//对方和我方都支持的扩展,那么我们才支持
|
//对方和我方都支持的扩展,那么我们才支持
|
||||||
for (auto &ext : offer_media.extmap) {
|
for (auto &ext : offer_media.extmap) {
|
||||||
auto it = configure.extmap.find(RtpExt::getExtType(ext.ext));
|
auto it = configure.extmap.find(RtpExt::getExtType(ext.ext));
|
||||||
if (it != configure.extmap.end()) {
|
if (it != configure.extmap.end()) {
|
||||||
auto new_dir = matchDirection(ext.direction, it->second);
|
auto new_dir = matchDirection(ext.direction, it->second);
|
||||||
switch (new_dir) {
|
switch (new_dir) {
|
||||||
case RtpDirection::invalid:
|
case RtpDirection::invalid:
|
||||||
case RtpDirection::inactive: continue;
|
case RtpDirection::inactive: continue;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
|
||||||
answer_media.extmap.emplace_back(ext);
|
|
||||||
answer_media.extmap.back().direction = new_dir;
|
|
||||||
}
|
}
|
||||||
|
answer_media.extmap.emplace_back(ext);
|
||||||
|
answer_media.extmap.back().direction = new_dir;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto &rtcp_fb_ref = answer_media.plan[0].rtcp_fb;
|
auto &rtcp_fb_ref = answer_media.plan[0].rtcp_fb;
|
||||||
rtcp_fb_ref.clear();
|
rtcp_fb_ref.clear();
|
||||||
//对方和我方都支持的rtcpfb,那么我们才支持
|
//对方和我方都支持的rtcpfb,那么我们才支持
|
||||||
for (auto &fp : selected_plan->rtcp_fb) {
|
for (auto &fp : selected_plan->rtcp_fb) {
|
||||||
if (configure.rtcp_fb.find(fp) != configure.rtcp_fb.end()) {
|
if (configure.rtcp_fb.find(fp) != configure.rtcp_fb.end()) {
|
||||||
//对方该rtcp被我们支持
|
//对方该rtcp被我们支持
|
||||||
rtcp_fb_ref.emplace(fp);
|
rtcp_fb_ref.emplace(fp);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
//todo 此处为添加无效的plan,webrtc sdp通过调节plan pt顺序选择匹配的codec,意味着后面的codec其实放在sdp中是无意义的
|
//todo 此处为添加无效的plan,webrtc sdp通过调节plan pt顺序选择匹配的codec,意味着后面的codec其实放在sdp中是无意义的
|
||||||
for (auto &plan : offer_media.plan) {
|
for (auto &plan : offer_media.plan) {
|
||||||
if (pt_selected.find(plan.pt) == pt_selected.end()) {
|
if (pt_selected.find(plan.pt) == pt_selected.end()) {
|
||||||
answer_media.plan.emplace_back(plan);
|
answer_media.plan.emplace_back(plan);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
ret->media.emplace_back(answer_media);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
ret->media.emplace_back(answer_media);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_profile) {
|
if (check_profile) {
|
||||||
@ -1765,7 +1771,7 @@ void RtcConfigure::setPlayRtspInfo(const string &sdp){
|
|||||||
static const string kProfile{"profile-level-id"};
|
static const string kProfile{"profile-level-id"};
|
||||||
static const string kMode{"packetization-mode"};
|
static const string kMode{"packetization-mode"};
|
||||||
|
|
||||||
bool RtcConfigure::onCheckCodecProfile(const RtcCodecPlan &plan, CodecId codec){
|
bool RtcConfigure::onCheckCodecProfile(const RtcCodecPlan &plan, CodecId codec) const {
|
||||||
if (_rtsp_audio_plan && codec == getCodecId(_rtsp_audio_plan->codec)) {
|
if (_rtsp_audio_plan && codec == getCodecId(_rtsp_audio_plan->codec)) {
|
||||||
if (plan.sample_rate != _rtsp_audio_plan->sample_rate || plan.channel != _rtsp_audio_plan->channel) {
|
if (plan.sample_rate != _rtsp_audio_plan->sample_rate || plan.channel != _rtsp_audio_plan->channel) {
|
||||||
//音频采样率和通道数必须相同
|
//音频采样率和通道数必须相同
|
||||||
@ -1785,7 +1791,7 @@ bool RtcConfigure::onCheckCodecProfile(const RtcCodecPlan &plan, CodecId codec){
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtcConfigure::onSelectPlan(RtcCodecPlan &plan, CodecId codec){
|
void RtcConfigure::onSelectPlan(RtcCodecPlan &plan, CodecId codec) const {
|
||||||
if (_rtsp_video_plan && codec == CodecH264 && getCodecId(_rtsp_video_plan->codec) == CodecH264) {
|
if (_rtsp_video_plan && codec == CodecH264 && getCodecId(_rtsp_video_plan->codec) == CodecH264) {
|
||||||
//h264时,设置packetization-mod为一致
|
//h264时,设置packetization-mod为一致
|
||||||
auto mode = _rtsp_video_plan->fmtp[kMode];
|
auto mode = _rtsp_video_plan->fmtp[kMode];
|
||||||
|
@ -720,7 +720,7 @@ public:
|
|||||||
void setDefaultSetting(std::string ice_ufrag, std::string ice_pwd, RtpDirection direction, const SdpAttrFingerprint &fingerprint);
|
void setDefaultSetting(std::string ice_ufrag, std::string ice_pwd, RtpDirection direction, const SdpAttrFingerprint &fingerprint);
|
||||||
void addCandidate(const SdpAttrCandidate &candidate, mediakit::TrackType type = mediakit::TrackInvalid);
|
void addCandidate(const SdpAttrCandidate &candidate, mediakit::TrackType type = mediakit::TrackInvalid);
|
||||||
|
|
||||||
std::shared_ptr<RtcSession> createAnswer(const RtcSession &offer);
|
std::shared_ptr<RtcSession> createAnswer(const RtcSession &offer) const;
|
||||||
|
|
||||||
void setPlayRtspInfo(const std::string &sdp);
|
void setPlayRtspInfo(const std::string &sdp);
|
||||||
|
|
||||||
@ -728,9 +728,9 @@ public:
|
|||||||
void enableREMB(bool enable = true, mediakit::TrackType type = mediakit::TrackInvalid);
|
void enableREMB(bool enable = true, mediakit::TrackType type = mediakit::TrackInvalid);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void matchMedia(const std::shared_ptr<RtcSession> &ret, mediakit::TrackType type, const std::vector<RtcMedia> &medias, const RtcTrackConfigure &configure);
|
void matchMedia(const std::shared_ptr<RtcSession> &ret, const RtcMedia &media) const;
|
||||||
bool onCheckCodecProfile(const RtcCodecPlan &plan, mediakit::CodecId codec);
|
bool onCheckCodecProfile(const RtcCodecPlan &plan, mediakit::CodecId codec) const;
|
||||||
void onSelectPlan(RtcCodecPlan &plan, mediakit::CodecId codec);
|
void onSelectPlan(RtcCodecPlan &plan, mediakit::CodecId codec) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RtcCodecPlan::Ptr _rtsp_video_plan;
|
RtcCodecPlan::Ptr _rtsp_video_plan;
|
||||||
|
Loading…
Reference in New Issue
Block a user