|
|
|
@ -36,8 +36,6 @@
|
|
|
|
|
#include <deque>
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
uint32_t SrsGoApiRtcPlay::ssrc_num = 0;
|
|
|
|
|
|
|
|
|
|
SrsGoApiRtcPlay::SrsGoApiRtcPlay(SrsRtcServer* server)
|
|
|
|
|
{
|
|
|
|
|
server_ = server;
|
|
|
|
@ -260,159 +258,6 @@ srs_error_t SrsGoApiRtcPlay::check_remote_sdp(const SrsSdp& remote_sdp)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsGoApiRtcPlay::exchange_sdp(SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp)
|
|
|
|
|
{
|
|
|
|
|
srs_error_t err = srs_success;
|
|
|
|
|
local_sdp.version_ = "0";
|
|
|
|
|
|
|
|
|
|
local_sdp.username_ = RTMP_SIG_SRS_SERVER;
|
|
|
|
|
local_sdp.session_id_ = srs_int2str((int64_t)this);
|
|
|
|
|
local_sdp.session_version_ = "2";
|
|
|
|
|
local_sdp.nettype_ = "IN";
|
|
|
|
|
local_sdp.addrtype_ = "IP4";
|
|
|
|
|
local_sdp.unicast_address_ = "0.0.0.0";
|
|
|
|
|
|
|
|
|
|
local_sdp.session_name_ = "SRSPlaySession";
|
|
|
|
|
|
|
|
|
|
local_sdp.msid_semantic_ = "WMS";
|
|
|
|
|
local_sdp.msids_.push_back(req->app + "/" + req->stream);
|
|
|
|
|
|
|
|
|
|
local_sdp.group_policy_ = "BUNDLE";
|
|
|
|
|
|
|
|
|
|
bool nack_enabled = _srs_config->get_rtc_nack_enabled(req->vhost);
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < remote_sdp.media_descs_.size(); ++i) {
|
|
|
|
|
const SrsMediaDesc& remote_media_desc = remote_sdp.media_descs_[i];
|
|
|
|
|
|
|
|
|
|
if (remote_media_desc.is_audio()) {
|
|
|
|
|
local_sdp.media_descs_.push_back(SrsMediaDesc("audio"));
|
|
|
|
|
} else if (remote_media_desc.is_video()) {
|
|
|
|
|
local_sdp.media_descs_.push_back(SrsMediaDesc("video"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsMediaDesc& local_media_desc = local_sdp.media_descs_.back();
|
|
|
|
|
|
|
|
|
|
if (remote_media_desc.is_audio()) {
|
|
|
|
|
// TODO: check opus format specific param
|
|
|
|
|
std::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("opus");
|
|
|
|
|
for (std::vector<SrsMediaPayloadType>::iterator iter = payloads.begin(); iter != payloads.end(); ++iter) {
|
|
|
|
|
local_media_desc.payload_types_.push_back(*iter);
|
|
|
|
|
SrsMediaPayloadType& payload_type = local_media_desc.payload_types_.back();
|
|
|
|
|
|
|
|
|
|
// TODO: FIXME: Only support some transport algorithms.
|
|
|
|
|
vector<string> rtcp_fb;
|
|
|
|
|
payload_type.rtcp_fb_.swap(rtcp_fb);
|
|
|
|
|
for (int j = 0; j < (int)rtcp_fb.size(); j++) {
|
|
|
|
|
if (nack_enabled) {
|
|
|
|
|
if (rtcp_fb.at(j) == "nack" || rtcp_fb.at(j) == "nack pli") {
|
|
|
|
|
payload_type.rtcp_fb_.push_back(rtcp_fb.at(j));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only choose one match opus.
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (local_media_desc.payload_types_.empty()) {
|
|
|
|
|
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no found valid opus payload type");
|
|
|
|
|
}
|
|
|
|
|
} else if (remote_media_desc.is_video()) {
|
|
|
|
|
std::deque<SrsMediaPayloadType> backup_payloads;
|
|
|
|
|
std::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("H264");
|
|
|
|
|
for (std::vector<SrsMediaPayloadType>::iterator iter = payloads.begin(); iter != payloads.end(); ++iter) {
|
|
|
|
|
if (iter->format_specific_param_.empty()) {
|
|
|
|
|
backup_payloads.push_front(*iter);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
H264SpecificParam h264_param;
|
|
|
|
|
if ((err = srs_parse_h264_fmtp(iter->format_specific_param_, h264_param)) != srs_success) {
|
|
|
|
|
srs_error_reset(err); continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try to pick the "best match" H.264 payload type.
|
|
|
|
|
if (h264_param.packetization_mode == "1" && h264_param.level_asymmerty_allow == "1") {
|
|
|
|
|
local_media_desc.payload_types_.push_back(*iter);
|
|
|
|
|
SrsMediaPayloadType& payload_type = local_media_desc.payload_types_.back();
|
|
|
|
|
|
|
|
|
|
// TODO: FIXME: Only support some transport algorithms.
|
|
|
|
|
vector<string> rtcp_fb;
|
|
|
|
|
payload_type.rtcp_fb_.swap(rtcp_fb);
|
|
|
|
|
for (int j = 0; j < (int)rtcp_fb.size(); j++) {
|
|
|
|
|
if (nack_enabled) {
|
|
|
|
|
if (rtcp_fb.at(j) == "nack" || rtcp_fb.at(j) == "nack pli") {
|
|
|
|
|
payload_type.rtcp_fb_.push_back(rtcp_fb.at(j));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only choose first match H.264 payload type.
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
backup_payloads.push_back(*iter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try my best to pick at least one media payload type.
|
|
|
|
|
if (local_media_desc.payload_types_.empty() && ! backup_payloads.empty()) {
|
|
|
|
|
srs_warn("choose backup H.264 payload type=%d", backup_payloads.front().payload_type_);
|
|
|
|
|
local_media_desc.payload_types_.push_back(backup_payloads.front());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (local_media_desc.payload_types_.empty()) {
|
|
|
|
|
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no found valid H.264 payload type");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local_media_desc.mid_ = remote_media_desc.mid_;
|
|
|
|
|
local_sdp.groups_.push_back(local_media_desc.mid_);
|
|
|
|
|
|
|
|
|
|
local_media_desc.port_ = 9;
|
|
|
|
|
local_media_desc.protos_ = "UDP/TLS/RTP/SAVPF";
|
|
|
|
|
|
|
|
|
|
if (remote_media_desc.session_info_.setup_ == "active") {
|
|
|
|
|
local_media_desc.session_info_.setup_ = "passive";
|
|
|
|
|
} else if (remote_media_desc.session_info_.setup_ == "passive") {
|
|
|
|
|
local_media_desc.session_info_.setup_ = "active";
|
|
|
|
|
} else if (remote_media_desc.session_info_.setup_ == "actpass") {
|
|
|
|
|
local_media_desc.session_info_.setup_ = local_sdp.session_config_.dtls_role;
|
|
|
|
|
} else {
|
|
|
|
|
// @see: https://tools.ietf.org/html/rfc4145#section-4.1
|
|
|
|
|
// The default value of the setup attribute in an offer/answer exchange
|
|
|
|
|
// is 'active' in the offer and 'passive' in the answer.
|
|
|
|
|
local_media_desc.session_info_.setup_ = "passive";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (remote_media_desc.sendonly_) {
|
|
|
|
|
local_media_desc.recvonly_ = true;
|
|
|
|
|
} else if (remote_media_desc.recvonly_) {
|
|
|
|
|
local_media_desc.sendonly_ = true;
|
|
|
|
|
} else if (remote_media_desc.sendrecv_) {
|
|
|
|
|
local_media_desc.sendrecv_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local_media_desc.rtcp_mux_ = true;
|
|
|
|
|
local_media_desc.rtcp_rsize_ = true;
|
|
|
|
|
|
|
|
|
|
// TODO: FIXME: Avoid SSRC collision.
|
|
|
|
|
if (!ssrc_num) {
|
|
|
|
|
ssrc_num = ::getpid() * 10000 + ::getpid() * 100 + ::getpid();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (local_media_desc.sendonly_ || local_media_desc.sendrecv_) {
|
|
|
|
|
SrsSSRCInfo ssrc_info;
|
|
|
|
|
ssrc_info.ssrc_ = ++ssrc_num;
|
|
|
|
|
// TODO:use formated cname
|
|
|
|
|
ssrc_info.cname_ = "stream";
|
|
|
|
|
local_media_desc.ssrc_infos_.push_back(ssrc_info);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t SrsGoApiRtcPublish::ssrc_num = 0;
|
|
|
|
|
|
|
|
|
|
SrsGoApiRtcPublish::SrsGoApiRtcPublish(SrsRtcServer* server)
|
|
|
|
|
{
|
|
|
|
|
server_ = server;
|
|
|
|
@ -625,170 +470,6 @@ srs_error_t SrsGoApiRtcPublish::check_remote_sdp(const SrsSdp& remote_sdp)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsGoApiRtcPublish::exchange_sdp(SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp)
|
|
|
|
|
{
|
|
|
|
|
srs_error_t err = srs_success;
|
|
|
|
|
local_sdp.version_ = "0";
|
|
|
|
|
|
|
|
|
|
local_sdp.username_ = RTMP_SIG_SRS_SERVER;
|
|
|
|
|
local_sdp.session_id_ = srs_int2str((int64_t)this);
|
|
|
|
|
local_sdp.session_version_ = "2";
|
|
|
|
|
local_sdp.nettype_ = "IN";
|
|
|
|
|
local_sdp.addrtype_ = "IP4";
|
|
|
|
|
local_sdp.unicast_address_ = "0.0.0.0";
|
|
|
|
|
|
|
|
|
|
local_sdp.session_name_ = "SRSPublishSession";
|
|
|
|
|
|
|
|
|
|
local_sdp.msid_semantic_ = "WMS";
|
|
|
|
|
local_sdp.msids_.push_back(req->app + "/" + req->stream);
|
|
|
|
|
|
|
|
|
|
local_sdp.group_policy_ = "BUNDLE";
|
|
|
|
|
|
|
|
|
|
bool nack_enabled = _srs_config->get_rtc_nack_enabled(req->vhost);
|
|
|
|
|
bool twcc_enabled = _srs_config->get_rtc_twcc_enabled(req->vhost);
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < remote_sdp.media_descs_.size(); ++i) {
|
|
|
|
|
const SrsMediaDesc& remote_media_desc = remote_sdp.media_descs_[i];
|
|
|
|
|
|
|
|
|
|
if (remote_media_desc.is_audio()) {
|
|
|
|
|
local_sdp.media_descs_.push_back(SrsMediaDesc("audio"));
|
|
|
|
|
} else if (remote_media_desc.is_video()) {
|
|
|
|
|
local_sdp.media_descs_.push_back(SrsMediaDesc("video"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsMediaDesc& local_media_desc = local_sdp.media_descs_.back();
|
|
|
|
|
|
|
|
|
|
// Whether feature enabled in remote extmap.
|
|
|
|
|
int remote_twcc_id = 0;
|
|
|
|
|
if (true) {
|
|
|
|
|
map<int, string> extmaps = remote_media_desc.get_extmaps();
|
|
|
|
|
for(map<int, string>::iterator it = extmaps.begin(); it != extmaps.end(); ++it) {
|
|
|
|
|
if (it->second == kTWCCExt) {
|
|
|
|
|
remote_twcc_id = it->first;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (twcc_enabled && remote_twcc_id) {
|
|
|
|
|
local_media_desc.extmaps_[remote_twcc_id] = kTWCCExt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (remote_media_desc.is_audio()) {
|
|
|
|
|
// TODO: check opus format specific param
|
|
|
|
|
std::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("opus");
|
|
|
|
|
for (std::vector<SrsMediaPayloadType>::iterator iter = payloads.begin(); iter != payloads.end(); ++iter) {
|
|
|
|
|
local_media_desc.payload_types_.push_back(*iter);
|
|
|
|
|
SrsMediaPayloadType& payload_type = local_media_desc.payload_types_.back();
|
|
|
|
|
|
|
|
|
|
// TODO: FIXME: Only support some transport algorithms.
|
|
|
|
|
vector<string> rtcp_fb;
|
|
|
|
|
payload_type.rtcp_fb_.swap(rtcp_fb);
|
|
|
|
|
for (int j = 0; j < (int)rtcp_fb.size(); j++) {
|
|
|
|
|
if (nack_enabled) {
|
|
|
|
|
if (rtcp_fb.at(j) == "nack" || rtcp_fb.at(j) == "nack pli") {
|
|
|
|
|
payload_type.rtcp_fb_.push_back(rtcp_fb.at(j));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (twcc_enabled && remote_twcc_id) {
|
|
|
|
|
if (rtcp_fb.at(j) == "transport-cc") {
|
|
|
|
|
payload_type.rtcp_fb_.push_back(rtcp_fb.at(j));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only choose one match opus.
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (local_media_desc.payload_types_.empty()) {
|
|
|
|
|
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no valid found opus payload type");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (remote_media_desc.is_video()) {
|
|
|
|
|
std::deque<SrsMediaPayloadType> backup_payloads;
|
|
|
|
|
std::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("H264");
|
|
|
|
|
for (std::vector<SrsMediaPayloadType>::iterator iter = payloads.begin(); iter != payloads.end(); ++iter) {
|
|
|
|
|
if (iter->format_specific_param_.empty()) {
|
|
|
|
|
backup_payloads.push_front(*iter);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
H264SpecificParam h264_param;
|
|
|
|
|
if ((err = srs_parse_h264_fmtp(iter->format_specific_param_, h264_param)) != srs_success) {
|
|
|
|
|
srs_error_reset(err); continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try to pick the "best match" H.264 payload type.
|
|
|
|
|
if (h264_param.packetization_mode == "1" && h264_param.level_asymmerty_allow == "1") {
|
|
|
|
|
local_media_desc.payload_types_.push_back(*iter);
|
|
|
|
|
SrsMediaPayloadType& payload_type = local_media_desc.payload_types_.back();
|
|
|
|
|
|
|
|
|
|
// TODO: FIXME: Only support some transport algorithms.
|
|
|
|
|
vector<string> rtcp_fb;
|
|
|
|
|
payload_type.rtcp_fb_.swap(rtcp_fb);
|
|
|
|
|
for (int j = 0; j < (int)rtcp_fb.size(); j++) {
|
|
|
|
|
if (nack_enabled) {
|
|
|
|
|
if (rtcp_fb.at(j) == "nack" || rtcp_fb.at(j) == "nack pli") {
|
|
|
|
|
payload_type.rtcp_fb_.push_back(rtcp_fb.at(j));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (twcc_enabled && remote_twcc_id) {
|
|
|
|
|
if (rtcp_fb.at(j) == "transport-cc") {
|
|
|
|
|
payload_type.rtcp_fb_.push_back(rtcp_fb.at(j));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only choose first match H.264 payload type.
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
backup_payloads.push_back(*iter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try my best to pick at least one media payload type.
|
|
|
|
|
if (local_media_desc.payload_types_.empty() && ! backup_payloads.empty()) {
|
|
|
|
|
srs_warn("choose backup H.264 payload type=%d", backup_payloads.front().payload_type_);
|
|
|
|
|
local_media_desc.payload_types_.push_back(backup_payloads.front());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (local_media_desc.payload_types_.empty()) {
|
|
|
|
|
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no found valid H.264 payload type");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: FIXME: Support RRTR?
|
|
|
|
|
//local_media_desc.payload_types_.back().rtcp_fb_.push_back("rrtr");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local_media_desc.mid_ = remote_media_desc.mid_;
|
|
|
|
|
local_sdp.groups_.push_back(local_media_desc.mid_);
|
|
|
|
|
|
|
|
|
|
local_media_desc.port_ = 9;
|
|
|
|
|
local_media_desc.protos_ = "UDP/TLS/RTP/SAVPF";
|
|
|
|
|
|
|
|
|
|
if (remote_media_desc.session_info_.setup_ == "active") {
|
|
|
|
|
local_media_desc.session_info_.setup_ = "passive";
|
|
|
|
|
} else if (remote_media_desc.session_info_.setup_ == "passive") {
|
|
|
|
|
local_media_desc.session_info_.setup_ = "active";
|
|
|
|
|
} else if (remote_media_desc.session_info_.setup_ == "actpass") {
|
|
|
|
|
local_media_desc.session_info_.setup_ = local_sdp.session_config_.dtls_role;
|
|
|
|
|
} else {
|
|
|
|
|
// @see: https://tools.ietf.org/html/rfc4145#section-4.1
|
|
|
|
|
// The default value of the setup attribute in an offer/answer exchange
|
|
|
|
|
// is 'active' in the offer and 'passive' in the answer.
|
|
|
|
|
local_media_desc.session_info_.setup_ = "passive";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local_media_desc.rtcp_mux_ = true;
|
|
|
|
|
|
|
|
|
|
// For publisher, we are always sendonly.
|
|
|
|
|
local_media_desc.sendonly_ = false;
|
|
|
|
|
local_media_desc.recvonly_ = true;
|
|
|
|
|
local_media_desc.sendrecv_ = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsGoApiRtcNACK::SrsGoApiRtcNACK(SrsRtcServer* server)
|
|
|
|
|
{
|
|
|
|
|
server_ = server;
|
|
|
|
|