Merge remote-tracking branch 'john/rtc' into feature/rtc

pull/1691/head
winlin 5 years ago
commit e65b6283df

2
trunk/configure vendored

@ -279,7 +279,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

@ -68,15 +68,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);
@ -843,10 +692,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();
@ -1064,19 +909,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());
@ -1089,6 +921,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");
}
@ -1244,7 +1096,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");
}
@ -1270,6 +1122,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; }

@ -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;
}

@ -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…
Cancel
Save