mirror of
https://github.com/ossrs/srs.git
synced 2025-02-15 04:42:04 +00:00
sdp exchange.
This commit is contained in:
parent
ccd170a813
commit
49da2099c1
12 changed files with 1435 additions and 298 deletions
2
trunk/configure
vendored
2
trunk/configure
vendored
|
@ -284,7 +284,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
|
|||
"srs_app_hourglass" "srs_app_dash" "srs_app_fragment" "srs_app_dvr"
|
||||
"srs_app_coworkers" "srs_app_hybrid")
|
||||
if [[ $SRS_RTC == YES ]]; then
|
||||
MODULE_FILES+=("srs_app_rtc" "srs_app_rtc_conn" "srs_app_dtls" "srs_app_audio_recode")
|
||||
MODULE_FILES+=("srs_app_rtc" "srs_app_rtc_conn" "srs_app_dtls" "srs_app_audio_recode" "srs_app_sdp")
|
||||
fi
|
||||
DEFINES=""
|
||||
# add each modules for app
|
||||
|
|
|
@ -70,15 +70,13 @@
|
|||
$('#rtc_media_player').prop('srcObject', event.stream);
|
||||
};
|
||||
new Promise(function(resolve, reject) {
|
||||
pc.addTransceiver("audio", {direction: "recvonly"});
|
||||
pc.addTransceiver("video", {direction: "recvonly"});
|
||||
|
||||
pc.createOffer(function(offer){
|
||||
resolve(offer);
|
||||
},function(reason){
|
||||
reject(reason);
|
||||
},{
|
||||
mandatory: {
|
||||
OfferToReceiveAudio: true,
|
||||
OfferToReceiveVideo: true
|
||||
}
|
||||
});
|
||||
}).then(function(offer) {
|
||||
return pc.setLocalDescription(offer).then(function(){ return offer; });
|
||||
|
|
|
@ -784,12 +784,14 @@ srs_error_t SrsGoApiStreams::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa
|
|||
}
|
||||
|
||||
#ifdef SRS_AUTO_RTC
|
||||
SrsGoApiSdp::SrsGoApiSdp(SrsRtcServer* rtc_svr)
|
||||
uint32_t SrsGoApiRtcPlay::ssrc_num = 0;
|
||||
|
||||
SrsGoApiRtcPlay::SrsGoApiRtcPlay(SrsRtcServer* rtc_svr)
|
||||
{
|
||||
rtc_server = rtc_svr;
|
||||
}
|
||||
|
||||
SrsGoApiSdp::~SrsGoApiSdp()
|
||||
SrsGoApiRtcPlay::~SrsGoApiRtcPlay()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -803,7 +805,7 @@ SrsGoApiSdp::~SrsGoApiSdp()
|
|||
// Response:
|
||||
// {"sdp":"answer...", "sid":"..."}
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
srs_error_t SrsGoApiSdp::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
|
||||
srs_error_t SrsGoApiRtcPlay::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
|
@ -818,7 +820,7 @@ srs_error_t SrsGoApiSdp::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage*
|
|||
return srs_api_response(w, r, res->dumps());
|
||||
}
|
||||
|
||||
srs_error_t SrsGoApiSdp::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsJsonObject* res)
|
||||
srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsJsonObject* res)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
|
@ -881,23 +883,36 @@ srs_error_t SrsGoApiSdp::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessag
|
|||
|
||||
// TODO: FIXME: It seems remote_sdp doesn't represents the full SDP information.
|
||||
SrsSdp remote_sdp;
|
||||
if ((err = remote_sdp.decode(remote_sdp_str)) != srs_success) {
|
||||
return srs_error_wrap(err, "decode sdp");
|
||||
if ((err = remote_sdp.parse(remote_sdp_str)) != srs_success) {
|
||||
return srs_error_wrap(err, "parse sdp failed");
|
||||
}
|
||||
|
||||
if ((err = check_remote_sdp(remote_sdp)) != srs_success) {
|
||||
return srs_error_wrap(err, "remote sdp check failed");
|
||||
}
|
||||
|
||||
SrsSdp local_sdp;
|
||||
if ((err = exchange_sdp(app, stream_name, remote_sdp, local_sdp)) != srs_success) {
|
||||
return srs_error_wrap(err, "remote sdp have error or unsupport attributes");
|
||||
}
|
||||
|
||||
SrsRequest request;
|
||||
request.app = app;
|
||||
request.stream = stream_name;
|
||||
SrsSdp local_sdp;
|
||||
|
||||
// TODO: FIXME: Maybe need a better name?
|
||||
// TODO: FIXME: When server enabled, but vhost disabled, should report error.
|
||||
SrsRtcSession* rtc_session = rtc_server->create_rtc_session(request, remote_sdp, local_sdp);
|
||||
|
||||
string local_sdp_str = "";
|
||||
if ((err = local_sdp.encode(local_sdp_str)) != srs_success) {
|
||||
ostringstream os;
|
||||
if ((err = local_sdp.encode(os)) != srs_success) {
|
||||
return srs_error_wrap(err, "encode sdp");
|
||||
}
|
||||
|
||||
string local_sdp_str = os.str();
|
||||
|
||||
srs_trace("local_sdp=%s", local_sdp_str.c_str());
|
||||
|
||||
res->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||
res->set("server", SrsJsonAny::integer(SrsStatistic::instance()->server_id()));
|
||||
|
||||
|
@ -910,6 +925,138 @@ srs_error_t SrsGoApiSdp::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessag
|
|||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsGoApiRtcPlay::check_remote_sdp(const SrsSdp& remote_sdp)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (remote_sdp.group_policy_ != "BUNDLE") {
|
||||
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "now only support BUNDLE, group policy=%s", remote_sdp.group_policy_.c_str());
|
||||
}
|
||||
|
||||
if (remote_sdp.media_descs_.empty()) {
|
||||
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no media descriptions");
|
||||
}
|
||||
|
||||
for (std::vector<SrsMediaDesc>::const_iterator iter = remote_sdp.media_descs_.begin(); iter != remote_sdp.media_descs_.end(); ++iter) {
|
||||
if (iter->type_ != "audio" && iter->type_ != "video") {
|
||||
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "unsupport media type=%s", iter->type_.c_str());
|
||||
}
|
||||
|
||||
if (! iter->rtcp_mux_) {
|
||||
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "now only suppor rtcp-mux");
|
||||
}
|
||||
|
||||
for (std::vector<SrsMediaPayloadType>::const_iterator iter_media = iter->payload_types_.begin(); iter_media != iter->payload_types_.end(); ++iter_media) {
|
||||
if (iter->sendonly_) {
|
||||
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "play API only support sendrecv/recvonly");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsGoApiRtcPlay::exchange_sdp(const std::string& app, const std::string& stream, 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_ = "live_play_session";
|
||||
|
||||
local_sdp.msid_semantic_ = "WMS";
|
||||
local_sdp.msids_.push_back(app + "/" + stream);
|
||||
|
||||
local_sdp.group_policy_ = "BUNDLE";
|
||||
|
||||
int mid = 0;
|
||||
|
||||
for (int 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) {
|
||||
// Only choose one match opus codec.
|
||||
local_media_desc.payload_types_.push_back(*iter);
|
||||
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::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("H264");
|
||||
for (std::vector<SrsMediaPayloadType>::iterator iter = payloads.begin(); iter != payloads.end(); ++iter) {
|
||||
H264SpecificParam h264_param;
|
||||
if (parse_h264_fmtp(iter->format_specific_param_, h264_param) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (h264_param.packetization_mode == 1 && h264_param.level_asymmerty_allow == 1) {
|
||||
// Only choose first match H.264 payload type.
|
||||
local_media_desc.payload_types_.push_back(*iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (local_media_desc.payload_types_.empty()) {
|
||||
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no valid found 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_ = "passive";
|
||||
}
|
||||
|
||||
local_sdp.media_descs_.back().session_info_.ice_options_ = "trickle";
|
||||
|
||||
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;
|
||||
|
||||
SrsSSRCInfo ssrc_info;
|
||||
ssrc_info.ssrc_ = ++ssrc_num;
|
||||
ssrc_info.cname_ = "test_sdp_cname";
|
||||
local_media_desc.ssrc_infos_.push_back(ssrc_info);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
SrsGoApiClients::SrsGoApiClients()
|
||||
|
|
|
@ -33,6 +33,7 @@ class SrsHttpHandler;
|
|||
class SrsServer;
|
||||
class SrsRtcServer;
|
||||
class SrsJsonObject;
|
||||
class SrsSdp;
|
||||
|
||||
#include <srs_app_st.hpp>
|
||||
#include <srs_app_conn.hpp>
|
||||
|
@ -167,17 +168,21 @@ public:
|
|||
};
|
||||
|
||||
#ifdef SRS_AUTO_RTC
|
||||
class SrsGoApiSdp : public ISrsHttpHandler
|
||||
class SrsGoApiRtcPlay : public ISrsHttpHandler
|
||||
{
|
||||
public:
|
||||
static uint32_t ssrc_num;
|
||||
private:
|
||||
SrsRtcServer* rtc_server;
|
||||
public:
|
||||
SrsGoApiSdp(SrsRtcServer* rtc_svr);
|
||||
virtual ~SrsGoApiSdp();
|
||||
SrsGoApiRtcPlay(SrsRtcServer* rtc_svr);
|
||||
virtual ~SrsGoApiRtcPlay();
|
||||
public:
|
||||
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
||||
private:
|
||||
virtual srs_error_t do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsJsonObject* res);
|
||||
srs_error_t exchange_sdp(const std::string& app, const std::string& stream, const SrsSdp& remote_sdp, SrsSdp& local_sdp);
|
||||
srs_error_t check_remote_sdp(const SrsSdp& remote_sdp);
|
||||
};
|
||||
#endif
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ const int max_payload_size = 1200;
|
|||
const int kRtpPacketSize = 1500;
|
||||
|
||||
const uint8_t kOpusPayloadType = 111;
|
||||
const uint8_t kH264PayloadType = 102;
|
||||
const uint8_t kH264PayloadType = 95;
|
||||
|
||||
const uint8_t kNalTypeMask = 0x1F;
|
||||
|
||||
|
|
|
@ -89,15 +89,7 @@ static string gen_random_str(int len)
|
|||
const int SRTP_MASTER_KEY_KEY_LEN = 16;
|
||||
const int SRTP_MASTER_KEY_SALT_LEN = 14;
|
||||
|
||||
SrsCandidate::SrsCandidate()
|
||||
{
|
||||
}
|
||||
|
||||
SrsCandidate::~SrsCandidate()
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<std::string> SrsCandidate::get_candidate_ips()
|
||||
static std::vector<std::string> get_candidate_ips()
|
||||
{
|
||||
std::vector<std::string> candidate_ips;
|
||||
|
||||
|
@ -116,173 +108,6 @@ std::vector<std::string> SrsCandidate::get_candidate_ips()
|
|||
return candidate_ips;
|
||||
}
|
||||
|
||||
SrsSdpMediaInfo::SrsSdpMediaInfo()
|
||||
{
|
||||
}
|
||||
|
||||
SrsSdpMediaInfo::~SrsSdpMediaInfo()
|
||||
{
|
||||
}
|
||||
|
||||
SrsSdp::SrsSdp()
|
||||
{
|
||||
}
|
||||
|
||||
SrsSdp::~SrsSdp()
|
||||
{
|
||||
}
|
||||
|
||||
srs_error_t SrsSdp::decode(const string& sdp_str)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (sdp_str.size() < 2 || sdp_str[0] != 'v' || sdp_str[1] != '=') {
|
||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid sdp_str");
|
||||
}
|
||||
|
||||
string line;
|
||||
istringstream is(sdp_str);
|
||||
while (getline(is, line)) {
|
||||
srs_verbose("line=%s", line.c_str());
|
||||
|
||||
if (line.size() < 2 || line[1] != '=') {
|
||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid sdp line=%s", line.c_str());
|
||||
}
|
||||
|
||||
switch (line[0]) {
|
||||
case 'v' :{
|
||||
break;
|
||||
}
|
||||
case 'o' :{
|
||||
break;
|
||||
}
|
||||
case 's' :{
|
||||
break;
|
||||
}
|
||||
case 't' :{
|
||||
break;
|
||||
}
|
||||
case 'c' :{
|
||||
break;
|
||||
}
|
||||
case 'a' :{
|
||||
if ((err = parse_attr(line)) != srs_success) {
|
||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "decode sdp line=%s failed", line.c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'm' :{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSdp::encode(string& sdp_str)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
string candidate_lines = "";
|
||||
|
||||
std::vector<string> candidate_ips = SrsCandidate::get_candidate_ips();
|
||||
for (int i = 0; i < (int)candidate_ips.size(); ++i) {
|
||||
ostringstream os;
|
||||
os << "a=candidate:10 1 udp 2115783679 " << candidate_ips[i] << " " << _srs_config->get_rtc_server_listen() <<" typ host generation 0\\r\\n";
|
||||
candidate_lines += os.str();
|
||||
}
|
||||
|
||||
// FIXME:
|
||||
sdp_str =
|
||||
"v=0\\r\\n"
|
||||
"o=- 0 0 IN IP4 127.0.0.1\\r\\n"
|
||||
"s=-\\r\\n"
|
||||
"t=0 0\\r\\n"
|
||||
"a=ice-lite\\r\\n"
|
||||
"a=group:BUNDLE 0 1\\r\\n"
|
||||
"a=msid-semantic: WMS 6VrfBKXrwK\\r\\n"
|
||||
"m=audio 9 UDP/TLS/RTP/SAVPF 111\\r\\n"
|
||||
"c=IN IP4 0.0.0.0\\r\\n"
|
||||
+ candidate_lines +
|
||||
"a=rtcp:9 IN IP4 0.0.0.0\\r\\n"
|
||||
"a=ice-ufrag:" + ice_ufrag + "\\r\\n"
|
||||
"a=ice-pwd:" + ice_pwd + "\\r\\n"
|
||||
"a=ice-options:trickle\\r\\n"
|
||||
"a=fingerprint:sha-256 " + SrsDtls::instance()->get_fingerprint() + "\\r\\n"
|
||||
"a=sendrecv\\r\\n"
|
||||
"a=mid:0\\r\\n"
|
||||
"a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\\r\\n"
|
||||
"a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\\r\\n"
|
||||
"a=rtcp-mux\\r\\n"
|
||||
"a=rtpmap:111 opus/48000/2\\r\\n"
|
||||
"a=fmtp:111 minptime=10;useinbandfec=1\\r\\n"
|
||||
"a=maxptime:60\\r\\n"
|
||||
"a=ssrc:3233846890 cname:o/i14u9pJrxRKAsu\\r\\n"
|
||||
"a=ssrc:3233846890 msid:6VrfBKXrwK a0\\r\\n"
|
||||
"a=ssrc:3233846890 mslabel:6VrfBKXrwK\\r\\n"
|
||||
"a=ssrc:3233846890 label:6VrfBKXrwKa0\\r\\n"
|
||||
"m=video 9 UDP/TLS/RTP/SAVPF 102\\r\\n"
|
||||
"c=IN IP4 0.0.0.0\\r\\n"
|
||||
+ candidate_lines +
|
||||
"a=rtcp:9 IN IP4 0.0.0.0\\r\\n"
|
||||
"b=as:2000000\\r\\n"
|
||||
"a=ice-ufrag:" + ice_ufrag + "\\r\\n"
|
||||
"a=ice-pwd:" + ice_pwd + "\\r\\n"
|
||||
"a=ice-options:trickle\\r\\n"
|
||||
"a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\\r\\n"
|
||||
"a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\\r\\n"
|
||||
"a=extmap:4 urn:3gpp:video-orientation\\r\\n"
|
||||
"a=fingerprint:sha-256 " + SrsDtls::instance()->get_fingerprint() + "\\r\\n"
|
||||
"a=sendrecv\\r\\n"
|
||||
"a=mid:1\\r\\n"
|
||||
"a=rtcp-mux\\r\\n"
|
||||
"a=rtpmap:102 H264/90000\\r\\n"
|
||||
"a=rtcp-fb:102 goog-remb\\r\\n"
|
||||
"a=rtcp-fb:102 transport-cc\\r\\n"
|
||||
"a=rtcp-fb:102 ccm fir \\r\\n"
|
||||
"a=rtcp-fb:102 nack\\r\\n"
|
||||
"a=rtcp-fb:102 nack pli \\r\\n"
|
||||
"a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f\\r\\n"
|
||||
"a=ssrc:3233846889 cname:o/i14u9pJrxRKAsu\\r\\n"
|
||||
"a=ssrc:3233846889 msid:6VrfBKXrwK v0\\r\\n"
|
||||
"a=ssrc:3233846889 mslabel:6VrfBKXrwK\\r\\n"
|
||||
"a=ssrc:3233846889 label:6VrfBKXrwKv0\\r\\n";
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSdp::parse_attr(const string& line)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
string key = "";
|
||||
string val = "";
|
||||
string* p = &key;
|
||||
for (int i = 2; i < (int)line.size(); ++i) {
|
||||
if (line[i] == ':' && p == &key) {
|
||||
p = &val;
|
||||
} else {
|
||||
if (line[i] != '\r' && line[i] != '\n') {
|
||||
p->append(1, line[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
srs_verbose("sdp attribute key=%s, val=%s", key.c_str(), val.c_str());
|
||||
|
||||
if (key == "ice-ufrag") {
|
||||
ice_ufrag = val;
|
||||
} else if (key == "ice-pwd") {
|
||||
ice_pwd = val;
|
||||
} else if (key == "fingerprint") {
|
||||
|
||||
} else {
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
SrsDtlsSession::SrsDtlsSession(SrsRtcSession* s)
|
||||
{
|
||||
rtc_session = s;
|
||||
|
@ -317,6 +142,31 @@ SrsDtlsSession::~SrsDtlsSession()
|
|||
}
|
||||
}
|
||||
|
||||
srs_error_t SrsDtlsSession::initialize()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((dtls = SSL_new(SrsDtls::instance()->get_dtls_ctx())) == NULL) {
|
||||
return srs_error_new(ERROR_OpenSslCreateSSL, "SSL_new dtls");
|
||||
}
|
||||
|
||||
// Dtls setup passive, as server role.
|
||||
SSL_set_accept_state(dtls);
|
||||
|
||||
if ((bio_in = BIO_new(BIO_s_mem())) == NULL) {
|
||||
return srs_error_new(ERROR_OpenSslBIONew, "BIO_new in");
|
||||
}
|
||||
|
||||
if ((bio_out = BIO_new(BIO_s_mem())) == NULL) {
|
||||
BIO_free(bio_in);
|
||||
return srs_error_new(ERROR_OpenSslBIONew, "BIO_new out");
|
||||
}
|
||||
|
||||
SSL_set_bio(dtls, bio_in, bio_out);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsDtlsSession::handshake(SrsUdpMuxSocket* udp_mux_skt)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
@ -412,36 +262,6 @@ srs_error_t SrsDtlsSession::on_dtls_application_data(const char* buf, const int
|
|||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsDtlsSession::send_client_hello(SrsUdpMuxSocket* udp_mux_skt)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (dtls == NULL) {
|
||||
srs_verbose("send client hello");
|
||||
|
||||
if ((dtls = SSL_new(SrsDtls::instance()->get_dtls_ctx())) == NULL) {
|
||||
return srs_error_new(ERROR_OpenSslCreateSSL, "SSL_new dtls");
|
||||
}
|
||||
|
||||
SSL_set_connect_state(dtls);
|
||||
|
||||
if ((bio_in = BIO_new(BIO_s_mem())) == NULL) {
|
||||
return srs_error_new(ERROR_OpenSslBIONew, "BIO_new in");
|
||||
}
|
||||
|
||||
if ((bio_out = BIO_new(BIO_s_mem())) == NULL) {
|
||||
BIO_free(bio_in);
|
||||
return srs_error_new(ERROR_OpenSslBIONew, "BIO_new out");
|
||||
}
|
||||
|
||||
SSL_set_bio(dtls, bio_in, bio_out);
|
||||
|
||||
return handshake(udp_mux_skt);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsDtlsSession::srtp_initialize()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
@ -494,11 +314,11 @@ srs_error_t SrsDtlsSession::srtp_send_init()
|
|||
policy.allow_repeat_tx = 1;
|
||||
policy.next = NULL;
|
||||
|
||||
uint8_t *key = new uint8_t[client_key.size()];
|
||||
memcpy(key, client_key.data(), client_key.size());
|
||||
uint8_t *key = new uint8_t[server_key.size()];
|
||||
memcpy(key, server_key.data(), server_key.size());
|
||||
policy.key = key;
|
||||
|
||||
if (srtp_create(&srtp_send, &policy) != 0) {
|
||||
if (srtp_create(&srtp_send, &policy) != srtp_err_status_ok) {
|
||||
srs_freepa(key);
|
||||
return srs_error_new(ERROR_RTC_SRTP_INIT, "srtp_create failed");
|
||||
}
|
||||
|
@ -526,11 +346,11 @@ srs_error_t SrsDtlsSession::srtp_recv_init()
|
|||
policy.allow_repeat_tx = 1;
|
||||
policy.next = NULL;
|
||||
|
||||
uint8_t *key = new uint8_t[server_key.size()];
|
||||
memcpy(key, server_key.data(), server_key.size());
|
||||
uint8_t *key = new uint8_t[client_key.size()];
|
||||
memcpy(key, client_key.data(), client_key.size());
|
||||
policy.key = key;
|
||||
|
||||
if (srtp_create(&srtp_recv, &policy) != 0) {
|
||||
if (srtp_create(&srtp_recv, &policy) != srtp_err_status_ok) {
|
||||
srs_freepa(key);
|
||||
return srs_error_new(ERROR_RTC_SRTP_INIT, "srtp_create failed");
|
||||
}
|
||||
|
@ -594,7 +414,7 @@ srs_error_t SrsDtlsSession::unprotect_rtcp(char* out_buf, const char* in_buf, in
|
|||
|
||||
if (srtp_recv) {
|
||||
memcpy(out_buf, in_buf, nb_out_buf);
|
||||
if (srtp_unprotect_rtcp(srtp_recv, out_buf, &nb_out_buf) != 0) {
|
||||
if (srtp_unprotect_rtcp(srtp_recv, out_buf, &nb_out_buf) != srtp_err_status_ok) {
|
||||
return srs_error_new(ERROR_RTC_SRTP_UNPROTECT, "rtcp unprotect failed");
|
||||
}
|
||||
|
||||
|
@ -620,6 +440,19 @@ SrsRtcSenderThread::~SrsRtcSenderThread()
|
|||
srs_freep(sendonly_ukt);
|
||||
}
|
||||
|
||||
srs_error_t SrsRtcSenderThread::initialize(const uint32_t& vssrc, const uint32_t& assrc, const uint16_t& v_pt, const uint16_t& a_pt)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
video_ssrc = vssrc;
|
||||
audio_ssrc = assrc;
|
||||
|
||||
video_payload_type = v_pt;
|
||||
audio_payload_type = a_pt;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int SrsRtcSenderThread::cid()
|
||||
{
|
||||
return trd->cid();
|
||||
|
@ -723,6 +556,16 @@ void SrsRtcSenderThread::send_and_free_messages(SrsSharedPtrMessage** msgs, int
|
|||
|
||||
SrsRtpSharedPacket* pkt = msg->rtp_packets[i];
|
||||
|
||||
if (msg->is_video()) {
|
||||
pkt->set_payload_type(video_payload_type);
|
||||
pkt->set_ssrc(video_ssrc);
|
||||
}
|
||||
|
||||
if (msg->is_audio()) {
|
||||
pkt->set_payload_type(audio_payload_type);
|
||||
pkt->set_ssrc(audio_ssrc);
|
||||
}
|
||||
|
||||
int length = pkt->size;
|
||||
char buf[kRtpPacketSize];
|
||||
if ((err = rtc_session->dtls_session->protect_rtp(buf, pkt->payload, length)) != srs_success) {
|
||||
|
@ -746,7 +589,8 @@ SrsRtcSession::SrsRtcSession(SrsRtcServer* rtc_svr, const SrsRequest& req, const
|
|||
{
|
||||
rtc_server = rtc_svr;
|
||||
session_state = INIT;
|
||||
dtls_session = NULL;
|
||||
dtls_session = new SrsDtlsSession(this);
|
||||
dtls_session->initialize();
|
||||
strd = NULL;
|
||||
|
||||
username = un;
|
||||
|
@ -769,6 +613,11 @@ SrsRtcSession::~SrsRtcSession()
|
|||
srs_freep(strd);
|
||||
}
|
||||
|
||||
void SrsRtcSession::set_local_sdp(const SrsSdp& sdp)
|
||||
{
|
||||
local_sdp = sdp;
|
||||
}
|
||||
|
||||
void SrsRtcSession::switch_to_context()
|
||||
{
|
||||
_srs_context->set_id(cid);
|
||||
|
@ -836,10 +685,6 @@ srs_error_t SrsRtcSession::on_binding_request(SrsUdpMuxSocket* udp_mux_skt, SrsS
|
|||
}
|
||||
|
||||
if (get_session_state() == WAITING_STUN) {
|
||||
if ((err = send_client_hello(udp_mux_skt)) != srs_success) {
|
||||
return srs_error_wrap(err, "send client hello, failed");
|
||||
}
|
||||
|
||||
set_session_state(DOING_DTLS_HANDSHAKE);
|
||||
|
||||
peer_id = udp_mux_skt->get_peer_id();
|
||||
|
@ -1057,19 +902,6 @@ block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtcSession::send_client_hello(SrsUdpMuxSocket* udp_mux_skt)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (dtls_session == NULL) {
|
||||
dtls_session = new SrsDtlsSession(this);
|
||||
}
|
||||
|
||||
dtls_session->send_client_hello(udp_mux_skt);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtcSession::on_connection_established(SrsUdpMuxSocket* udp_mux_skt)
|
||||
{
|
||||
srs_trace("rtc session=%s, connection established", id().c_str());
|
||||
|
@ -1082,6 +914,26 @@ srs_error_t SrsRtcSession::start_play(SrsUdpMuxSocket* udp_mux_skt)
|
|||
|
||||
srs_freep(strd);
|
||||
strd = new SrsRtcSenderThread(this, udp_mux_skt, _srs_context->get_id());
|
||||
|
||||
uint32_t video_ssrc = 0;
|
||||
uint32_t audio_ssrc = 0;
|
||||
uint16_t video_payload_type = 0;
|
||||
uint16_t audio_payload_type = 0;
|
||||
for (int i = 0; i < local_sdp.media_descs_.size(); ++i) {
|
||||
const SrsMediaDesc& media_desc = local_sdp.media_descs_[i];
|
||||
if (media_desc.is_audio()) {
|
||||
audio_ssrc = media_desc.ssrc_infos_[0].ssrc_;
|
||||
audio_payload_type = media_desc.payload_types_[0].payload_type_;
|
||||
} else if (media_desc.is_video()) {
|
||||
video_ssrc = media_desc.ssrc_infos_[0].ssrc_;
|
||||
video_payload_type = media_desc.payload_types_[0].payload_type_;
|
||||
}
|
||||
}
|
||||
|
||||
if ((err =strd->initialize(video_ssrc, audio_ssrc, video_payload_type, audio_payload_type)) != srs_success) {
|
||||
return srs_error_wrap(err, "SrsRtcSenderThread init");
|
||||
}
|
||||
|
||||
if ((err = strd->start()) != srs_success) {
|
||||
return srs_error_wrap(err, "start SrsRtcSenderThread");
|
||||
}
|
||||
|
@ -1237,7 +1089,7 @@ srs_error_t SrsRtcServer::listen_api()
|
|||
|
||||
// TODO: FIXME: Fetch api from hybrid manager.
|
||||
SrsHttpServeMux* http_api_mux = _srs_hybrid->srs()->instance()->api_server();
|
||||
if ((err = http_api_mux->handle("/rtc/v1/play/", new SrsGoApiSdp(this))) != srs_success) {
|
||||
if ((err = http_api_mux->handle("/rtc/v1/play/", new SrsGoApiRtcPlay(this))) != srs_success) {
|
||||
return srs_error_wrap(err, "handle sdp");
|
||||
}
|
||||
|
||||
|
@ -1263,6 +1115,12 @@ SrsRtcSession* SrsRtcServer::create_rtc_session(const SrsRequest& req, const Srs
|
|||
|
||||
local_sdp.set_ice_ufrag(local_ufrag);
|
||||
local_sdp.set_ice_pwd(local_pwd);
|
||||
local_sdp.set_fingerprint_algo("sha-256");
|
||||
local_sdp.set_fingerprint(SrsDtls::instance()->get_fingerprint());
|
||||
std::vector<string> candidate_ips = get_candidate_ips();
|
||||
for (int i = 0; i < (int)candidate_ips.size(); ++i) {
|
||||
local_sdp.add_candidate(candidate_ips[i], _srs_config->get_rtc_server_listen(), "host");
|
||||
}
|
||||
|
||||
session->set_remote_sdp(remote_sdp);
|
||||
session->set_local_sdp(local_sdp);
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <srs_rtmp_stack.hpp>
|
||||
#include <srs_app_hybrid.hpp>
|
||||
#include <srs_app_hourglass.hpp>
|
||||
#include <srs_app_sdp.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
@ -65,50 +66,6 @@ const uint8_t kAFB = 15;
|
|||
|
||||
const srs_utime_t kSrsRtcSessionStunTimeoutUs = 10*1000*1000LL;
|
||||
|
||||
class SrsCandidate
|
||||
{
|
||||
private:
|
||||
public:
|
||||
SrsCandidate();
|
||||
virtual ~SrsCandidate();
|
||||
|
||||
static std::vector<std::string> get_candidate_ips();
|
||||
};
|
||||
|
||||
class SrsSdpMediaInfo
|
||||
{
|
||||
private:
|
||||
public:
|
||||
SrsSdpMediaInfo();
|
||||
virtual ~SrsSdpMediaInfo();
|
||||
};
|
||||
|
||||
class SrsSdp
|
||||
{
|
||||
private:
|
||||
std::string sdp;
|
||||
int version;
|
||||
std::string ice_ufrag;
|
||||
std::string ice_pwd;
|
||||
std::string fingerprint;
|
||||
std::string setup;
|
||||
std::vector<SrsSdpMediaInfo> media_infos;
|
||||
public:
|
||||
SrsSdp();
|
||||
virtual ~SrsSdp();
|
||||
|
||||
srs_error_t decode(const std::string& sdp_str);
|
||||
srs_error_t encode(std::string& sdp_str);
|
||||
|
||||
std::string get_ice_ufrag() const { return ice_ufrag; }
|
||||
std::string get_ice_pwd() const { return ice_pwd; }
|
||||
|
||||
void set_ice_ufrag(const std::string& u) { ice_ufrag = u; }
|
||||
void set_ice_pwd(const std::string& p) { ice_pwd = p; }
|
||||
private:
|
||||
srs_error_t parse_attr(const std::string& line);
|
||||
};
|
||||
|
||||
enum SrsRtcSessionStateType
|
||||
{
|
||||
// TODO: FIXME: Should prefixed by enum name.
|
||||
|
@ -140,11 +97,11 @@ public:
|
|||
SrsDtlsSession(SrsRtcSession* s);
|
||||
virtual ~SrsDtlsSession();
|
||||
|
||||
srs_error_t initialize();
|
||||
|
||||
srs_error_t on_dtls(SrsUdpMuxSocket* udp_mux_skt);
|
||||
srs_error_t on_dtls_handshake_done(SrsUdpMuxSocket* udp_mux_skt);
|
||||
srs_error_t on_dtls_application_data(const char* data, const int len);
|
||||
|
||||
srs_error_t send_client_hello(SrsUdpMuxSocket* udp_mux_skt);
|
||||
public:
|
||||
srs_error_t protect_rtp(char* protected_buf, const char* ori_buf, int& nb_protected_buf);
|
||||
srs_error_t unprotect_rtp(char* unprotected_buf, const char* ori_buf, int& nb_unprotected_buf);
|
||||
|
@ -165,11 +122,17 @@ protected:
|
|||
int _parent_cid;
|
||||
private:
|
||||
SrsRtcSession* rtc_session;
|
||||
uint32_t video_ssrc;
|
||||
uint32_t audio_ssrc;
|
||||
uint16_t video_payload_type;
|
||||
uint16_t audio_payload_type;
|
||||
public:
|
||||
SrsUdpMuxSocket* sendonly_ukt;
|
||||
public:
|
||||
SrsRtcSenderThread(SrsRtcSession* s, SrsUdpMuxSocket* u, int parent_cid);
|
||||
virtual ~SrsRtcSenderThread();
|
||||
public:
|
||||
srs_error_t initialize(const uint32_t& vssrc, const uint32_t& assrc, const uint16_t& v_pt, const uint16_t& a_pt);
|
||||
public:
|
||||
virtual int cid();
|
||||
public:
|
||||
|
@ -208,7 +171,7 @@ public:
|
|||
virtual ~SrsRtcSession();
|
||||
public:
|
||||
SrsSdp* get_local_sdp() { return &local_sdp; }
|
||||
void set_local_sdp(const SrsSdp& sdp) { local_sdp = sdp; }
|
||||
void set_local_sdp(const SrsSdp& sdp);
|
||||
|
||||
SrsSdp* get_remote_sdp() { return &remote_sdp; }
|
||||
void set_remote_sdp(const SrsSdp& sdp) { remote_sdp = sdp; }
|
||||
|
|
917
trunk/src/app/srs_app_sdp.cpp
Normal file
917
trunk/src/app/srs_app_sdp.cpp
Normal file
|
@ -0,0 +1,917 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2020 Winlin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <srs_app_sdp.hpp>
|
||||
using namespace std;
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_kernel_log.hpp>
|
||||
|
||||
const std::string kCRLF = "\\r\\n";
|
||||
|
||||
#define FETCH(is,word) \
|
||||
if (! (is >> word)) {\
|
||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "fetch failed");\
|
||||
}\
|
||||
|
||||
#define FETCH_WITH_DELIM(is,word,delim) \
|
||||
if (! getline(is,word,delim)) {\
|
||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "fetch with delim failed");\
|
||||
}\
|
||||
|
||||
static std::vector<std::string> split_str(const std::string& str, const std::string& delim)
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
size_t pre_pos = 0;
|
||||
std::string tmp;
|
||||
size_t pos = 0;
|
||||
do {
|
||||
pos = str.find(delim, pre_pos);
|
||||
tmp = str.substr(pre_pos, pos - pre_pos);
|
||||
ret.push_back(tmp);
|
||||
pre_pos = pos + delim.size();
|
||||
} while (pos != std::string::npos);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void skip_first_spaces(std::string& str)
|
||||
{
|
||||
while (! str.empty() && str[0] == ' ') {
|
||||
str.erase(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
srs_error_t parse_h264_fmtp(const std::string& fmtp, H264SpecificParam& h264_param)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
std::vector<std::string> vec = split_str(fmtp, ";");
|
||||
for (int i = 0; i < vec.size(); ++i) {
|
||||
std::vector<std::string> kv = split_str(vec[i], "=");
|
||||
if (kv.size() == 2) {
|
||||
if (kv[0] == "profile-level-id") {
|
||||
h264_param.profile_level_id = atoi(kv[1].c_str());
|
||||
} else if (kv[0] == "packetization-mode") {
|
||||
h264_param.packetization_mode = atoi(kv[1].c_str());
|
||||
} else if (kv[0] == "level-asymmetry-allowed") {
|
||||
h264_param.level_asymmerty_allow = atoi(kv[1].c_str());
|
||||
} else {
|
||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid h264 param=%s", kv[0].c_str());
|
||||
}
|
||||
} else {
|
||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid h264 param=%s", vec[i].c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
SrsSessionInfo::SrsSessionInfo()
|
||||
{
|
||||
}
|
||||
|
||||
SrsSessionInfo::~SrsSessionInfo()
|
||||
{
|
||||
}
|
||||
|
||||
srs_error_t SrsSessionInfo::parse_attribute(const std::string& attribute, const std::string& value)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
if (attribute == "ice-ufrag") {
|
||||
ice_ufrag_ = value;
|
||||
} else if (attribute == "ice-pwd") {
|
||||
ice_pwd_ = value;
|
||||
} else if (attribute == "ice-options") {
|
||||
ice_options_ = value;
|
||||
} else if (attribute == "fingerprint") {
|
||||
std::istringstream is(value);
|
||||
FETCH(is, fingerprint_algo_);
|
||||
FETCH(is, fingerprint_);
|
||||
} else if (attribute == "setup") {
|
||||
// @see: https://tools.ietf.org/html/rfc4145#section-4
|
||||
setup_ = value;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSessionInfo::encode(std::ostringstream& os)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
if (! ice_ufrag_.empty()) {
|
||||
os << "a=ice-ufrag:" << ice_ufrag_ << kCRLF;
|
||||
}
|
||||
if (! ice_pwd_.empty()) {
|
||||
os << "a=ice-pwd:" << ice_pwd_ << kCRLF;
|
||||
}
|
||||
if (! ice_options_.empty()) {
|
||||
os << "a=ice-options:" << ice_options_ << kCRLF;
|
||||
}
|
||||
if (! fingerprint_algo_.empty() && ! fingerprint_.empty()) {
|
||||
os << "a=fingerprint:" << fingerprint_algo_ << " " << fingerprint_ << kCRLF;
|
||||
}
|
||||
if (! setup_.empty()) {
|
||||
os << "a=setup:" << setup_ << kCRLF;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
bool SrsSessionInfo::operator=(const SrsSessionInfo& rhs)
|
||||
{
|
||||
return ice_ufrag_ == rhs.ice_ufrag_ &&
|
||||
ice_pwd_ == rhs.ice_pwd_ &&
|
||||
ice_options_ == rhs.ice_options_ &&
|
||||
fingerprint_algo_ == rhs.fingerprint_algo_ &&
|
||||
fingerprint_ == rhs.fingerprint_ &&
|
||||
setup_ == rhs.setup_;
|
||||
}
|
||||
|
||||
SrsSSRCInfo::SrsSSRCInfo()
|
||||
{
|
||||
ssrc_ = 0;
|
||||
}
|
||||
|
||||
SrsSSRCInfo::~SrsSSRCInfo()
|
||||
{
|
||||
}
|
||||
|
||||
srs_error_t SrsSSRCInfo::encode(std::ostringstream& os)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
if (ssrc_ == 0) {
|
||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid ssrc");
|
||||
}
|
||||
|
||||
os << "a=ssrc:" << ssrc_ << " cname:" << cname_ << kCRLF;
|
||||
if (! msid_.empty()) {
|
||||
os << "a=ssrc:" << ssrc_ << " msid:" << msid_;
|
||||
if (! msid_tracker_.empty()) {
|
||||
os << " " << msid_tracker_;
|
||||
}
|
||||
os << kCRLF;
|
||||
}
|
||||
if (! mslabel_.empty()) {
|
||||
os << "a=ssrc:" << ssrc_ << " mslabel:" << mslabel_ << kCRLF;
|
||||
}
|
||||
if (! label_.empty()) {
|
||||
os << "a=ssrc:" << ssrc_ << " label:" << label_ << kCRLF;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
SrsMediaPayloadType::SrsMediaPayloadType(int payload_type)
|
||||
{
|
||||
payload_type_ = payload_type;
|
||||
}
|
||||
|
||||
SrsMediaPayloadType::~SrsMediaPayloadType()
|
||||
{
|
||||
}
|
||||
|
||||
srs_error_t SrsMediaPayloadType::encode(std::ostringstream& os)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
os << "a=rtpmap:" << payload_type_ << " " << encoding_name_ << "/" << clock_rate_;
|
||||
if (! encoding_param_.empty()) {
|
||||
os << "/" << encoding_param_;
|
||||
}
|
||||
os << kCRLF;
|
||||
|
||||
for (std::vector<std::string>::iterator iter = rtcp_fb_.begin(); iter != rtcp_fb_.end(); ++iter) {
|
||||
os << "a=rtcp-fb:" << payload_type_ << " " << *iter << kCRLF;
|
||||
}
|
||||
|
||||
if (! format_specific_param_.empty()) {
|
||||
os << "a=fmtp:" << payload_type_ << " " << format_specific_param_ << kCRLF;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
SrsMediaDesc::SrsMediaDesc(const std::string& type)
|
||||
{
|
||||
type_ = type;
|
||||
|
||||
rtcp_mux_ = false;
|
||||
|
||||
sendrecv_ = false;
|
||||
recvonly_ = false;
|
||||
sendonly_ = false;
|
||||
inactive_ = false;
|
||||
}
|
||||
|
||||
SrsMediaDesc::~SrsMediaDesc()
|
||||
{
|
||||
}
|
||||
|
||||
SrsMediaPayloadType* SrsMediaDesc::find_media_with_payload_type(int payload_type)
|
||||
{
|
||||
for (size_t i = 0; i < payload_types_.size(); ++i) {
|
||||
if (payload_types_[i].payload_type_ == payload_type) {
|
||||
return &payload_types_[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vector<SrsMediaPayloadType> SrsMediaDesc::find_media_with_encoding_name(const std::string& encoding_name) const
|
||||
{
|
||||
std::vector<SrsMediaPayloadType> payloads;
|
||||
|
||||
for (size_t i = 0; i < payload_types_.size(); ++i) {
|
||||
if (payload_types_[i].encoding_name_ == encoding_name) {
|
||||
payloads.push_back(payload_types_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return payloads;
|
||||
}
|
||||
|
||||
srs_error_t SrsMediaDesc::parse_line(const std::string& line)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
std::string content = line.substr(2);
|
||||
|
||||
switch (line[0]) {
|
||||
case 'a': {
|
||||
return parse_attribute(content);
|
||||
}
|
||||
case 'c': {
|
||||
// TODO: process c-line
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
srs_trace("ignore media line=%s", line.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsMediaDesc::encode(std::ostringstream& os)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
os << "m=" << type_ << " " << port_ << " " << protos_;
|
||||
for (std::vector<SrsMediaPayloadType>::iterator iter = payload_types_.begin(); iter != payload_types_.end(); ++iter) {
|
||||
os << " " << iter->payload_type_;
|
||||
}
|
||||
|
||||
os << kCRLF;
|
||||
|
||||
// TODO:nettype and address type
|
||||
os << "c=IN IP4 0.0.0.0" << kCRLF;
|
||||
|
||||
if ((err = session_info_.encode(os)) != srs_success) {
|
||||
return srs_error_wrap(err, "encode session info failed");
|
||||
}
|
||||
|
||||
os << "a=mid:" << mid_ << kCRLF;
|
||||
if (! msid_.empty()) {
|
||||
os << "a=msid:" << msid_;
|
||||
|
||||
if (! msid_tracker_.empty()) {
|
||||
os << " " << msid_tracker_;
|
||||
}
|
||||
|
||||
os << kCRLF;
|
||||
}
|
||||
|
||||
if (sendonly_) {
|
||||
os << "a=sendonly" << kCRLF;
|
||||
}
|
||||
if (recvonly_) {
|
||||
os << "a=recvonly" << kCRLF;
|
||||
}
|
||||
if (sendrecv_) {
|
||||
os << "a=sendrecv" << kCRLF;
|
||||
}
|
||||
if (inactive_) {
|
||||
os << "a=inactive" << kCRLF;
|
||||
}
|
||||
|
||||
if (rtcp_mux_) {
|
||||
os << "a=rtcp-mux" << kCRLF;
|
||||
}
|
||||
|
||||
for (std::vector<SrsMediaPayloadType>::iterator iter = payload_types_.begin(); iter != payload_types_.end(); ++iter) {
|
||||
if ((err = iter->encode(os)) != srs_success) {
|
||||
return srs_error_wrap(err, "encode media payload failed");
|
||||
}
|
||||
}
|
||||
|
||||
for (std::vector<SrsSSRCInfo>::iterator iter = ssrc_infos_.begin(); iter != ssrc_infos_.end(); ++iter) {
|
||||
SrsSSRCInfo& ssrc_info = *iter;
|
||||
|
||||
if ((err = ssrc_info.encode(os)) != srs_success) {
|
||||
return srs_error_wrap(err, "encode ssrc failed");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Candidate priority
|
||||
for (std::vector<SrsCandidate>::iterator iter = candidates_.begin(); iter != candidates_.end(); ++iter) {
|
||||
os << "a=candidate:10 1 udp 2115783679 " << iter->ip_ << " " << iter->port_ <<" typ " << iter->type_ << " generation 0" << kCRLF;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsMediaDesc::parse_attribute(const std::string& content)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
std::string attribute = "";
|
||||
std::string value = "";
|
||||
size_t pos = content.find_first_of(":");
|
||||
|
||||
if (pos != std::string::npos) {
|
||||
attribute = content.substr(0, pos);
|
||||
value = content.substr(pos + 1);
|
||||
} else {
|
||||
attribute = content;
|
||||
}
|
||||
|
||||
if (attribute == "extmap") {
|
||||
// TODO:We don't parse "extmap" currently.
|
||||
return 0;
|
||||
} else if (attribute == "rtpmap") {
|
||||
return parse_attr_rtpmap(value);
|
||||
} else if (attribute == "rtcp-fb") {
|
||||
return parse_attr_rtcp_fb(value);
|
||||
} else if (attribute == "fmtp") {
|
||||
return parse_attr_fmtp(value);
|
||||
} else if (attribute == "mid") {
|
||||
return parse_attr_mid(value);
|
||||
} else if (attribute == "msid") {
|
||||
return parse_attr_msid(value);
|
||||
} else if (attribute == "ssrc") {
|
||||
return parse_attr_ssrc(value);
|
||||
} else if (attribute == "ssrc-group") {
|
||||
return parse_attr_ssrc_group(value);
|
||||
} else if (attribute == "rtcp-mux") {
|
||||
rtcp_mux_ = true;
|
||||
} else if (attribute == "recvonly") {
|
||||
recvonly_ = true;
|
||||
} else if (attribute == "sendonly") {
|
||||
sendonly_ = true;
|
||||
} else if (attribute == "sendrecv") {
|
||||
sendrecv_ = true;
|
||||
} else if (attribute == "inactive") {
|
||||
inactive_ = true;
|
||||
} else {
|
||||
return session_info_.parse_attribute(attribute, value);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsMediaDesc::parse_attr_rtpmap(const std::string& value)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
// @see: https://tools.ietf.org/html/rfc4566#page-25
|
||||
// a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]
|
||||
|
||||
std::istringstream is(value);
|
||||
|
||||
int payload_type = 0;
|
||||
FETCH(is, payload_type);
|
||||
|
||||
SrsMediaPayloadType* payload = find_media_with_payload_type(payload_type);
|
||||
if (payload == NULL) {
|
||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "can not find payload %d when pase rtpmap", payload_type);
|
||||
}
|
||||
|
||||
std::string word;
|
||||
FETCH(is, word);
|
||||
|
||||
std::vector<std::string> vec = split_str(word, "/");
|
||||
if (vec.size() < 2) {
|
||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid rtpmap line=%s", value);
|
||||
}
|
||||
|
||||
payload->encoding_name_ = vec[0];
|
||||
payload->clock_rate_ = atoi(vec[1].c_str());
|
||||
|
||||
if (vec.size() == 3) {
|
||||
payload->encoding_param_ = vec[2];
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsMediaDesc::parse_attr_rtcp_fb(const std::string& value)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
// @see: https://tools.ietf.org/html/rfc5104#section-7.1
|
||||
|
||||
std::istringstream is(value);
|
||||
|
||||
int payload_type = 0;
|
||||
FETCH(is, payload_type);
|
||||
|
||||
SrsMediaPayloadType* payload = find_media_with_payload_type(payload_type);
|
||||
if (payload == NULL) {
|
||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "can not find payload %d when pase rtcp-fb", payload_type);
|
||||
}
|
||||
|
||||
std::string rtcp_fb = is.str().substr(is.tellg());
|
||||
skip_first_spaces(rtcp_fb);
|
||||
|
||||
payload->rtcp_fb_.push_back(rtcp_fb);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsMediaDesc::parse_attr_fmtp(const std::string& value)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
// @see: https://tools.ietf.org/html/rfc4566#page-30
|
||||
// a=fmtp:<format> <format specific parameters>
|
||||
|
||||
std::istringstream is(value);
|
||||
|
||||
int payload_type = 0;
|
||||
FETCH(is, payload_type);
|
||||
|
||||
SrsMediaPayloadType* payload = find_media_with_payload_type(payload_type);
|
||||
if (payload == NULL) {
|
||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "can not find payload %d when pase fmtp", payload_type);
|
||||
}
|
||||
|
||||
std::string word;
|
||||
FETCH(is, word);
|
||||
|
||||
payload->format_specific_param_ = word;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsMediaDesc::parse_attr_mid(const std::string& value)
|
||||
{
|
||||
// @see: https://tools.ietf.org/html/rfc3388#section-3
|
||||
srs_error_t err = srs_success;
|
||||
std::istringstream is(value);
|
||||
// mid_ means m-line id
|
||||
FETCH(is, mid_);
|
||||
srs_trace("mid=%s", mid_.c_str());
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsMediaDesc::parse_attr_msid(const std::string& value)
|
||||
{
|
||||
// @see: https://tools.ietf.org/id/draft-ietf-mmusic-msid-08.html#rfc.section.2
|
||||
// TODO: msid and msid_tracker
|
||||
srs_error_t err = srs_success;
|
||||
std::istringstream is(value);
|
||||
// msid_ means media stream id
|
||||
FETCH(is, msid_);
|
||||
is >> msid_tracker_;
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsMediaDesc::parse_attr_ssrc(const std::string& value)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
// @see: https://tools.ietf.org/html/rfc5576#section-4.1
|
||||
|
||||
std::istringstream is(value);
|
||||
|
||||
uint32_t ssrc = 0;
|
||||
FETCH(is, ssrc);
|
||||
|
||||
std::string ssrc_attr = "";
|
||||
FETCH_WITH_DELIM(is, ssrc_attr, ':');
|
||||
skip_first_spaces(ssrc_attr);
|
||||
|
||||
std::string ssrc_value = is.str().substr(is.tellg());
|
||||
skip_first_spaces(ssrc_value);
|
||||
|
||||
SrsSSRCInfo& ssrc_info = fetch_or_create_ssrc_info(ssrc);
|
||||
|
||||
if (ssrc_attr == "cname") {
|
||||
// @see: https://tools.ietf.org/html/rfc5576#section-6.1
|
||||
ssrc_info.cname_ = ssrc_value;
|
||||
ssrc_info.ssrc_ = ssrc;
|
||||
} else if (ssrc_attr == "msid") {
|
||||
std::vector<std::string> vec = split_str(ssrc_value, " ");
|
||||
if (vec.empty()) {
|
||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid ssrc line=%s", value);
|
||||
}
|
||||
|
||||
ssrc_info.msid_ = vec[0];
|
||||
if (vec.size() > 1) {
|
||||
ssrc_info.msid_tracker_ = vec[1];
|
||||
}
|
||||
} else if (ssrc_attr == "mslabel") {
|
||||
ssrc_info.mslabel_ = ssrc_value;
|
||||
} else if (ssrc_attr == "label") {
|
||||
ssrc_info.label_ = ssrc_value;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsMediaDesc::parse_attr_ssrc_group(const std::string& value)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
// @see: https://tools.ietf.org/html/rfc5576#section-4.2
|
||||
// a=ssrc-group:<semantics> <ssrc-id> ...
|
||||
|
||||
std::istringstream is(value);
|
||||
|
||||
std::string semantics;
|
||||
FETCH(is, semantics);
|
||||
|
||||
// TODO: ssrc group process
|
||||
if (semantics == "FID") {
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
SrsSSRCInfo& SrsMediaDesc::fetch_or_create_ssrc_info(uint32_t ssrc)
|
||||
{
|
||||
for (size_t i = 0; i < ssrc_infos_.size(); ++i) {
|
||||
if (ssrc_infos_[i].ssrc_ == ssrc) {
|
||||
return ssrc_infos_[i];
|
||||
}
|
||||
}
|
||||
|
||||
SrsSSRCInfo ssrc_info;
|
||||
ssrc_info.ssrc_ = ssrc;
|
||||
ssrc_infos_.push_back(ssrc_info);
|
||||
|
||||
return ssrc_infos_.back();
|
||||
}
|
||||
|
||||
SrsSdp::SrsSdp()
|
||||
{
|
||||
in_media_session_ = false;
|
||||
|
||||
start_time_ = 0;
|
||||
end_time_ = 0;
|
||||
}
|
||||
|
||||
SrsSdp::~SrsSdp()
|
||||
{
|
||||
}
|
||||
|
||||
srs_error_t SrsSdp::parse(const std::string& sdp_str)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// All webrtc SrsSdp annotated example
|
||||
// @see: https://tools.ietf.org/html/draft-ietf-rtcweb-SrsSdp-11
|
||||
// Sdp example
|
||||
// session info
|
||||
// v=
|
||||
// o=
|
||||
// s=
|
||||
// t=
|
||||
// media description
|
||||
// m=
|
||||
// a=
|
||||
// ...
|
||||
// media description
|
||||
// m=
|
||||
// a=
|
||||
// ...
|
||||
std::istringstream is(sdp_str);
|
||||
std::string line;
|
||||
while (getline(is, line)) {
|
||||
srs_trace("%s", line.c_str());
|
||||
if (line.size() < 2 || line[1] != '=') {
|
||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid sdp line=%s", line);
|
||||
}
|
||||
if (! line.empty() && line[line.size()-1] == '\r') {
|
||||
line.erase(line.size()-1, 1);
|
||||
}
|
||||
|
||||
if ((err = parse_line(line)) != srs_success) {
|
||||
return srs_error_wrap(err, "parse sdp line failed");
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSdp::encode(std::ostringstream& os)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
os << "v=" << version_ << kCRLF;
|
||||
os << "o=" << username_ << " " << session_id_ << " " << session_version_ << " " << nettype_ << " " << addrtype_ << " " << unicast_address_ << kCRLF;
|
||||
os << "s=" << session_name_ << kCRLF;
|
||||
os << "t=" << start_time_ << " " << end_time_ << kCRLF;
|
||||
// TODO: ice options
|
||||
os << "a=ice-lite" << kCRLF;
|
||||
|
||||
if (! groups_.empty()) {
|
||||
os << "a=group:" << group_policy_;
|
||||
for (std::vector<std::string>::iterator iter = groups_.begin(); iter != groups_.end(); ++iter) {
|
||||
os << " " << *iter;
|
||||
}
|
||||
os << kCRLF;
|
||||
}
|
||||
|
||||
os << "a=msid-semantic: " << msid_semantic_;
|
||||
for (std::vector<std::string>::iterator iter = msids_.begin(); iter != msids_.end(); ++iter) {
|
||||
os << " " << *iter;
|
||||
}
|
||||
os << kCRLF;
|
||||
|
||||
if ((err = session_info_.encode(os)) != srs_success) {
|
||||
return srs_error_wrap(err, "encode session info failed");
|
||||
}
|
||||
|
||||
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||
if ((err = (*iter).encode(os)) != srs_success) {
|
||||
return srs_error_wrap(err, "encode media failed");
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
const SrsMediaDesc* SrsSdp::find_media_desc(const std::string& type) const
|
||||
{
|
||||
for (std::vector<SrsMediaDesc>::const_iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||
if (iter->type_ == type) {
|
||||
return &(*iter);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void SrsSdp::set_ice_ufrag(const std::string& ufrag)
|
||||
{
|
||||
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||
iter->session_info_.ice_ufrag_ = ufrag;
|
||||
}
|
||||
}
|
||||
|
||||
void SrsSdp::set_ice_pwd(const std::string& pwd)
|
||||
{
|
||||
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||
iter->session_info_.ice_pwd_ = pwd;
|
||||
}
|
||||
}
|
||||
|
||||
void SrsSdp::set_fingerprint_algo(const std::string& algo)
|
||||
{
|
||||
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||
iter->session_info_.fingerprint_algo_ = algo;
|
||||
}
|
||||
}
|
||||
|
||||
void SrsSdp::set_fingerprint(const std::string& fingerprint)
|
||||
{
|
||||
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||
iter->session_info_.fingerprint_ = fingerprint;
|
||||
}
|
||||
}
|
||||
|
||||
void SrsSdp::add_candidate(const std::string& ip, const int& port, const std::string& type)
|
||||
{
|
||||
// @see: https://tools.ietf.org/id/draft-ietf-mmusic-ice-sip-sdp-14.html#rfc.section.5.1
|
||||
SrsCandidate candidate;
|
||||
candidate.ip_ = ip;
|
||||
candidate.port_ = port;
|
||||
candidate.type_ = type;
|
||||
|
||||
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||
iter->candidates_.push_back(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
std::string SrsSdp::get_ice_ufrag() const
|
||||
{
|
||||
// Becaues we use BUNDLE, so we can choose the first element.
|
||||
for (std::vector<SrsMediaDesc>::const_iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||
return iter->session_info_.ice_ufrag_;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string SrsSdp::get_ice_pwd() const
|
||||
{
|
||||
// Becaues we use BUNDLE, so we can choose the first element.
|
||||
for (std::vector<SrsMediaDesc>::const_iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||
return iter->session_info_.ice_pwd_;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
srs_error_t SrsSdp::parse_line(const std::string& line)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
std::string content = line.substr(2);
|
||||
|
||||
switch (line[0]) {
|
||||
case 'o': {
|
||||
return parse_origin(content);
|
||||
}
|
||||
case 'v': {
|
||||
return parse_version(content);
|
||||
}
|
||||
case 's': {
|
||||
return parse_session_name(content);
|
||||
}
|
||||
case 't': {
|
||||
return parse_timing(content);
|
||||
}
|
||||
case 'a': {
|
||||
if (in_media_session_) {
|
||||
return media_descs_.back().parse_line(line);
|
||||
}
|
||||
return parse_attribute(content);
|
||||
}
|
||||
case 'm': {
|
||||
return parse_media_description(content);
|
||||
}
|
||||
case 'c': {
|
||||
// TODO: process c-line
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
srs_trace("ignore sdp line=%s", line.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSdp::parse_origin(const std::string& content)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// @see: https://tools.ietf.org/html/rfc4566#section-5.2
|
||||
// o=<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>
|
||||
// eg. o=- 9164462281920464688 2 IN IP4 127.0.0.1
|
||||
std::istringstream is(content);
|
||||
|
||||
FETCH(is, username_);
|
||||
FETCH(is, session_id_);
|
||||
FETCH(is, session_version_);
|
||||
FETCH(is, nettype_);
|
||||
FETCH(is, addrtype_);
|
||||
FETCH(is, unicast_address_);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSdp::parse_version(const std::string& content)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
// @see: https://tools.ietf.org/html/rfc4566#section-5.1
|
||||
|
||||
std::istringstream is(content);
|
||||
|
||||
FETCH(is, version_);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSdp::parse_session_name(const std::string& content)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
// @see: https://tools.ietf.org/html/rfc4566#section-5.3
|
||||
// s=<session name>
|
||||
|
||||
session_name_ = content;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSdp::parse_timing(const std::string& content)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
// @see: https://tools.ietf.org/html/rfc4566#section-5.9
|
||||
// t=<start-time> <stop-time>
|
||||
|
||||
std::istringstream is(content);
|
||||
|
||||
FETCH(is, start_time_);
|
||||
FETCH(is, end_time_);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSdp::parse_attribute(const std::string& content)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
// @see: https://tools.ietf.org/html/rfc4566#section-5.13
|
||||
// a=<attribute>
|
||||
// a=<attribute>:<value>
|
||||
|
||||
std::string attribute = "";
|
||||
std::string value = "";
|
||||
size_t pos = content.find_first_of(":");
|
||||
|
||||
if (pos != std::string::npos) {
|
||||
attribute = content.substr(0, pos);
|
||||
value = content.substr(pos + 1);
|
||||
}
|
||||
|
||||
if (attribute == "group") {
|
||||
return parse_attr_group(value);
|
||||
} else if (attribute == "msid-semantic") {
|
||||
std::istringstream is(value);
|
||||
FETCH(is, msid_semantic_);
|
||||
|
||||
std::string msid;
|
||||
while (is >> msid) {
|
||||
msids_.push_back(msid);
|
||||
}
|
||||
} else {
|
||||
return session_info_.parse_attribute(attribute, value);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSdp::parse_attr_group(const std::string& value)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
// @see: https://tools.ietf.org/html/rfc5888#section-5
|
||||
|
||||
std::istringstream is(value);
|
||||
|
||||
FETCH(is, group_policy_);
|
||||
|
||||
std::string word;
|
||||
while (is >> word) {
|
||||
groups_.push_back(word);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSdp::parse_media_description(const std::string& content)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// @see: https://tools.ietf.org/html/rfc4566#section-5.14
|
||||
// m=<media> <port> <proto> <fmt> ...
|
||||
// m=<media> <port>/<number of ports> <proto> <fmt> ...
|
||||
std::istringstream is(content);
|
||||
|
||||
std::string media;
|
||||
FETCH(is, media);
|
||||
|
||||
int port;
|
||||
FETCH(is, port);
|
||||
|
||||
std::string proto;
|
||||
FETCH(is, proto);
|
||||
|
||||
media_descs_.push_back(SrsMediaDesc(media));
|
||||
media_descs_.back().protos_ = proto;
|
||||
media_descs_.back().port_ = port;
|
||||
|
||||
int fmt;
|
||||
while (is >> fmt) {
|
||||
media_descs_.back().payload_types_.push_back(SrsMediaPayloadType(fmt));
|
||||
}
|
||||
|
||||
if (! in_media_session_) {
|
||||
in_media_session_ = true;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
219
trunk/src/app/srs_app_sdp.hpp
Normal file
219
trunk/src/app/srs_app_sdp.hpp
Normal file
|
@ -0,0 +1,219 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2020 Winlin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef SRS_APP_SDP_HPP
|
||||
#define SRS_APP_SDP_HPP
|
||||
|
||||
#include <srs_core.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class SrsSessionInfo
|
||||
{
|
||||
public:
|
||||
SrsSessionInfo();
|
||||
~SrsSessionInfo();
|
||||
|
||||
srs_error_t parse_attribute(const std::string& attribute, const std::string& value);
|
||||
srs_error_t encode(std::ostringstream& os);
|
||||
|
||||
bool operator=(const SrsSessionInfo& rhs);
|
||||
public:
|
||||
std::string ice_ufrag_;
|
||||
std::string ice_pwd_;
|
||||
std::string ice_options_;
|
||||
std::string fingerprint_algo_;
|
||||
std::string fingerprint_;
|
||||
std::string setup_;
|
||||
};
|
||||
|
||||
class SrsSSRCInfo
|
||||
{
|
||||
public:
|
||||
SrsSSRCInfo();
|
||||
virtual ~SrsSSRCInfo();
|
||||
public:
|
||||
srs_error_t encode(std::ostringstream& os);
|
||||
public:
|
||||
uint32_t ssrc_;
|
||||
std::string cname_;
|
||||
std::string msid_;
|
||||
std::string msid_tracker_;
|
||||
std::string mslabel_;
|
||||
std::string label_;
|
||||
};
|
||||
|
||||
class SrsSSRCGroup
|
||||
{
|
||||
};
|
||||
|
||||
struct H264SpecificParam
|
||||
{
|
||||
int profile_level_id;
|
||||
int packetization_mode;
|
||||
int level_asymmerty_allow;
|
||||
};
|
||||
|
||||
extern srs_error_t parse_h264_fmtp(const std::string& fmtp, H264SpecificParam& h264_param);
|
||||
|
||||
class SrsMediaPayloadType
|
||||
{
|
||||
public:
|
||||
SrsMediaPayloadType(int payload_type);
|
||||
~SrsMediaPayloadType();
|
||||
|
||||
srs_error_t encode(std::ostringstream& os);
|
||||
public:
|
||||
int payload_type_;
|
||||
|
||||
std::string encoding_name_;
|
||||
int clock_rate_;
|
||||
std::string encoding_param_;
|
||||
|
||||
std::vector<std::string> rtcp_fb_;
|
||||
std::string format_specific_param_;
|
||||
};
|
||||
|
||||
struct SrsCandidate
|
||||
{
|
||||
std::string ip_;
|
||||
int port_;
|
||||
std::string type_;
|
||||
};
|
||||
|
||||
class SrsMediaDesc
|
||||
{
|
||||
public:
|
||||
SrsMediaDesc(const std::string& type);
|
||||
~SrsMediaDesc();
|
||||
public:
|
||||
srs_error_t parse_line(const std::string& line);
|
||||
srs_error_t encode(std::ostringstream& os);
|
||||
SrsMediaPayloadType* find_media_with_payload_type(int payload_type);
|
||||
std::vector<SrsMediaPayloadType> find_media_with_encoding_name(const std::string& encoding_name) const;
|
||||
|
||||
bool is_audio() const { return type_ == "audio"; }
|
||||
bool is_video() const { return type_ == "video"; }
|
||||
private:
|
||||
srs_error_t parse_attribute(const std::string& content);
|
||||
srs_error_t parse_attr_rtpmap(const std::string& value);
|
||||
srs_error_t parse_attr_rtcp_fb(const std::string& value);
|
||||
srs_error_t parse_attr_fmtp(const std::string& value);
|
||||
srs_error_t parse_attr_mid(const std::string& value);
|
||||
srs_error_t parse_attr_msid(const std::string& value);
|
||||
srs_error_t parse_attr_ssrc(const std::string& value);
|
||||
srs_error_t parse_attr_ssrc_group(const std::string& value);
|
||||
private:
|
||||
SrsSSRCInfo& fetch_or_create_ssrc_info(uint32_t ssrc);
|
||||
|
||||
public:
|
||||
SrsSessionInfo session_info_;
|
||||
std::string type_;
|
||||
int port_;
|
||||
|
||||
bool rtcp_mux_;
|
||||
|
||||
bool sendonly_;
|
||||
bool recvonly_;
|
||||
bool sendrecv_;
|
||||
bool inactive_;
|
||||
|
||||
std::string mid_;
|
||||
std::string msid_;
|
||||
std::string msid_tracker_;
|
||||
std::string protos_;
|
||||
std::vector<SrsMediaPayloadType> payload_types_;
|
||||
|
||||
std::vector<SrsCandidate> candidates_;
|
||||
std::vector<SrsSSRCGroup> ssrc_groups_;
|
||||
std::vector<SrsSSRCInfo> ssrc_infos_;
|
||||
};
|
||||
|
||||
class SrsSdp
|
||||
{
|
||||
public:
|
||||
SrsSdp();
|
||||
~SrsSdp();
|
||||
public:
|
||||
srs_error_t parse(const std::string& sdp_str);
|
||||
srs_error_t encode(std::ostringstream& os);
|
||||
public:
|
||||
public:
|
||||
const SrsMediaDesc* find_media_desc(const std::string& type) const;
|
||||
public:
|
||||
void set_ice_ufrag(const std::string& ufrag);
|
||||
void set_ice_pwd(const std::string& pwd);
|
||||
void set_fingerprint_algo(const std::string& algo);
|
||||
void set_fingerprint(const std::string& fingerprint);
|
||||
void add_candidate(const std::string& ip, const int& port, const std::string& type);
|
||||
|
||||
std::string get_ice_ufrag() const;
|
||||
std::string get_ice_pwd() const;
|
||||
private:
|
||||
srs_error_t parse_line(const std::string& line);
|
||||
private:
|
||||
srs_error_t parse_origin(const std::string& content);
|
||||
srs_error_t parse_version(const std::string& content);
|
||||
srs_error_t parse_session_name(const std::string& content);
|
||||
srs_error_t parse_timing(const std::string& content);
|
||||
srs_error_t parse_attribute(const std::string& content);
|
||||
srs_error_t parse_media_description(const std::string& content);
|
||||
srs_error_t parse_attr_group(const std::string& content);
|
||||
private:
|
||||
bool in_media_session_;
|
||||
public:
|
||||
// version
|
||||
std::string version_;
|
||||
|
||||
// origin
|
||||
std::string username_;
|
||||
std::string session_id_;
|
||||
std::string session_version_;
|
||||
std::string nettype_;
|
||||
std::string addrtype_;
|
||||
std::string unicast_address_;
|
||||
|
||||
// session_name
|
||||
std::string session_name_;
|
||||
|
||||
// timing
|
||||
int64_t start_time_;
|
||||
int64_t end_time_;
|
||||
|
||||
SrsSessionInfo session_info_;
|
||||
|
||||
std::vector<std::string> groups_;
|
||||
std::string group_policy_;
|
||||
|
||||
std::string msid_semantic_;
|
||||
std::vector<std::string> msids_;
|
||||
|
||||
// m-line, media sessions
|
||||
std::vector<SrsMediaDesc> media_descs_;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -346,6 +346,7 @@
|
|||
#define ERROR_RTC_SRTP_UNPROTECT 5015
|
||||
#define ERROR_RTC_RTCP_CHECK 5016
|
||||
#define ERROR_RTC_SOURCE_CHECK 5017
|
||||
#define ERROR_RTC_SDP_EXCHANGE 5018
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// HTTP API error.
|
||||
|
|
|
@ -126,3 +126,30 @@ srs_error_t SrsRtpSharedPacket::set_marker(bool marker)
|
|||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtpSharedPacket::set_ssrc(uint32_t ssrc)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (payload_ptr == NULL || payload_ptr->payload == NULL || payload_ptr->size < 12) {
|
||||
return srs_error_new(ERROR_RTC_RTP_MUXER, "rtp payload incorrect");
|
||||
}
|
||||
|
||||
SrsBuffer stream(payload_ptr->payload + 8, 4);
|
||||
stream.write_4bytes(ssrc);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtpSharedPacket::set_payload_type(uint8_t pt)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (payload_ptr == NULL || payload_ptr->payload == NULL || payload_ptr->size < 2) {
|
||||
return srs_error_new(ERROR_RTC_RTP_MUXER, "rtp payload incorrect");
|
||||
}
|
||||
|
||||
payload_ptr->payload[1] = (payload_ptr->payload[1] & 0x80) | pt;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -62,6 +62,8 @@ public:
|
|||
// interface to modify rtp header
|
||||
public:
|
||||
srs_error_t set_marker(bool marker);
|
||||
srs_error_t set_ssrc(uint32_t ssrc);
|
||||
srs_error_t set_payload_type(uint8_t pt);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue