Merge branch 'rtc' of https://github.com/xiaozhihong/srs into xiaozhihong-rtc

pull/1642/head
winlin 5 years ago
commit d21ef106a0

Binary file not shown.

@ -242,6 +242,27 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
if [ ! -f ${SRS_OBJS}/st/libst.a ]; then echo "Build state-threads static lib failed."; exit -1; fi
fi
#####################################################################################
# srtp
#####################################################################################
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
# Patched ST from https://github.com/ossrs/state-threads/tree/srs
if [[ -f ${SRS_OBJS}/srtp2/lib/libsrtp2.a ]]; then
echo "The srtp2 is ok.";
else
echo "Building srtp2.";
(
rm -rf ${SRS_OBJS}/srtp2 && cd ${SRS_OBJS} &&
unzip -q ../3rdparty/libsrtp-2.0.0.zip && cd libsrtp-2.0.0 &&
./configure --prefix=`pwd`/_release && make ${SRS_JOBS} && make install &&
cd .. && rm -f srtp2 && ln -sf libsrtp-2.0.0/_release srtp2
)
fi
# check status
ret=$?; if [[ $ret -ne 0 ]]; then echo "Build srtp2 failed, ret=$ret"; exit $ret; fi
if [ ! -f ${SRS_OBJS}/srtp2/lib/libsrtp2.a ]; then echo "Build srtp2 static lib failed."; exit -1; fi
fi
#####################################################################################
# nginx for HLS, nginx-1.5.0
#####################################################################################

@ -0,0 +1,40 @@
# main config for srs.
# @see full.conf for detail config.
listen 1935;
max_connections 1000;
srs_log_tank file;
srs_log_file ./objs/srs.log;
http_api {
enabled on;
listen 1985;
raw_api {
enabled on;
allow_reload on;
allow_query on;
allow_update on;
}
}
http_server {
enabled on;
listen 8080;
dir ./objs/nginx/html;
}
rtc {
enabled on;
listen 9527;
# candidate device ip: *(all interface), 192.168.1.1 ...
candidate *;
}
stats {
network 0;
disk sda sdb xvda xvdb;
}
vhost __defaultVhost__ {
http_remux {
enabled on;
mount [vhost]/[app]/[stream].flv;
}
}

27
trunk/configure vendored

@ -148,6 +148,9 @@ END
# st(state-threads) the basic network library for SRS.
LibSTRoot="${SRS_OBJS_DIR}/st"; LibSTfile="${LibSTRoot}/libst.a"
if [[ $SRS_SHARED_ST == YES ]]; then LibSTfile="-lst"; fi
# srtp
LibSrtpRoot="${SRS_OBJS_DIR}/srtp2/include"; LibSrtpFile="${SRS_OBJS_DIR}/srtp2/lib/libsrtp2.a"
if [[ $SRS_SHARED_SRTP == YES ]]; then LibSrtpFile="-lsrtp2"; fi
# openssl-1.1.0e, for the RTMP complex handshake.
LibSSLRoot="";LibSSLfile=""
if [[ $SRS_SSL == YES && $SRS_USE_SYS_SSL == NO ]]; then
@ -201,7 +204,7 @@ MODULE_ID="KERNEL"
MODULE_DEPENDS=("CORE")
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot})
MODULE_FILES=("srs_kernel_error" "srs_kernel_log" "srs_kernel_buffer"
"srs_kernel_utility" "srs_kernel_flv" "srs_kernel_codec" "srs_kernel_io"
"srs_kernel_utility" "srs_kernel_flv" "srs_kernel_rtp" "srs_kernel_codec" "srs_kernel_io"
"srs_kernel_consts" "srs_kernel_aac" "srs_kernel_mp3" "srs_kernel_ts"
"srs_kernel_stream" "srs_kernel_balance" "srs_kernel_mp4" "srs_kernel_file")
KERNEL_INCS="src/kernel"; MODULE_DIR=${KERNEL_INCS} . auto/modules.sh
@ -214,7 +217,7 @@ ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot})
MODULE_FILES=("srs_protocol_amf0" "srs_protocol_io" "srs_rtmp_stack"
"srs_rtmp_handshake" "srs_protocol_utility" "srs_rtmp_msg_array" "srs_protocol_stream"
"srs_raw_avc" "srs_rtsp_stack" "srs_http_stack" "srs_protocol_kbps" "srs_protocol_json"
"srs_protocol_format")
"srs_stun_stack" "srs_protocol_format")
PROTOCOL_INCS="src/protocol"; MODULE_DIR=${PROTOCOL_INCS} . auto/modules.sh
PROTOCOL_OBJS="${MODULE_OBJS[@]}"
#
@ -233,7 +236,7 @@ fi
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
MODULE_ID="SERVICE"
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL")
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibSSLRoot})
ModuleLibIncs=(${LibSTRoot} ${LibSrtpRoot} ${SRS_OBJS_DIR} ${LibSSLRoot})
MODULE_FILES=("srs_service_log" "srs_service_st" "srs_service_http_client"
"srs_service_http_conn" "srs_service_rtmp_conn" "srs_service_utility"
"srs_service_conn")
@ -246,7 +249,7 @@ fi
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
MODULE_ID="APP"
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE")
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibSSLRoot})
ModuleLibIncs=(${LibSTRoot} ${LibSrtpRoot} ${SRS_OBJS_DIR} ${LibSSLRoot})
MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_source"
"srs_app_refer" "srs_app_hls" "srs_app_forward" "srs_app_encoder" "srs_app_http_stream"
"srs_app_thread" "srs_app_bandwidth" "srs_app_st" "srs_app_log" "srs_app_config"
@ -254,7 +257,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
"srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_edge"
"srs_app_heartbeat" "srs_app_empty" "srs_app_http_client" "srs_app_http_static"
"srs_app_recv_thread" "srs_app_security" "srs_app_statistic" "srs_app_hds"
"srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call"
"srs_app_mpegts_udp" "srs_app_rtp" "srs_app_rtc_conn" "srs_app_dtls" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call"
"srs_app_caster_flv" "srs_app_process" "srs_app_ng_exec"
"srs_app_hourglass" "srs_app_dash" "srs_app_fragment" "srs_app_dvr"
"srs_app_coworkers" "srs_app_hybrid")
@ -284,7 +287,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
if [[ $SRS_SRT == YES ]]; then
MODULE_DEPENDS+=("SRT")
fi
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot})
ModuleLibIncs=(${LibSTRoot} ${LibSrtpRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot})
if [[ $SRS_SRT == YES ]]; then
ModuleLibIncs+=("${LibSRTRoot[*]}")
fi
@ -297,7 +300,7 @@ fi
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
MODULE_ID="MAIN"
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE")
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot})
ModuleLibIncs=(${LibSTRoot} ${LibSrtpRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot})
MODULE_FILES=()
DEFINES=""
# add each modules for main
@ -324,13 +327,13 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
done
#
# all depends libraries
ModuleLibFiles=(${LibSTfile} ${LibSSLfile} ${LibGperfFile})
ModuleLibFiles=(${LibSTfile} ${LibSrtpFile} ${LibSSLfile} ${LibGperfFile})
if [[ $SRS_SRT == YES ]]; then
ModuleLibFiles+=("${LibSRTfile[*]}")
fi
# all depends objects
MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${SERVICE_OBJS[@]} ${APP_OBJS[@]} ${SERVER_OBJS[@]}"
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot})
ModuleLibIncs=(${LibSTRoot} ${LibSrtpRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot})
if [[ $SRS_SRT == YES ]]; then
MODULE_OBJS="${MODULE_OBJS} ${SRT_OBJS[@]}"
fi
@ -341,7 +344,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
#
# For modules, without the app module.
MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${SERVICE_OBJS[@]} ${MAIN_OBJS[@]}"
ModuleLibFiles=(${LibSTfile} ${LibSSLfile} ${LibGperfFile})
ModuleLibFiles=(${LibSTfile} ${LibSrtpFile} ${LibSSLfile} ${LibGperfFile})
#
for SRS_MODULE in ${SRS_MODULES[*]}; do
. $SRS_MODULE/config
@ -361,11 +364,11 @@ if [ $SRS_UTEST = YES ]; then
MODULE_FILES=("srs_utest" "srs_utest_amf0" "srs_utest_protocol" "srs_utest_kernel" "srs_utest_core"
"srs_utest_config" "srs_utest_rtmp" "srs_utest_http" "srs_utest_avc" "srs_utest_reload"
"srs_utest_mp4" "srs_utest_service" "srs_utest_app")
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibSSLRoot})
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibSrtpRoot} ${LibSSLRoot})
if [[ $SRS_SRT == YES ]]; then
ModuleLibIncs+=("${LibSRTRoot[*]}")
fi
ModuleLibFiles=(${LibSTfile} ${LibSSLfile})
ModuleLibFiles=(${LibSTfile} ${LibSrtpFile} ${LibSSLfile})
if [[ $SRS_SRT == YES ]]; then
ModuleLibFiles+=("${LibSRTfile[*]}")
fi

@ -0,0 +1,90 @@
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="utf-8">
</head>
<body>
rtc_media_player: <br>
<video id = "rtc_media_player" autoplay></video>
</body>
<script>
var PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
var SessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription || window.webkitRTCSessionDescription;
var url = "http://hw.com:1985/api/v1/sdp/";
var method = "POST";
var shouldBeAsync = true;
var request = new XMLHttpRequest();
request.open(method, url, shouldBeAsync);
request.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
var pc = new PeerConnection(null);
var constraints = {
mandatory: {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
}
};
var sendViewerOfferFn = function(desc) {
console.log('sendViewerOfferFn:', desc);
pc.setLocalDescription(desc);
var sdp_json = {"sdp":desc.sdp, "app":"webrtc", "stream":"test"};
request.send(JSON.stringify(sdp_json));
};
pc.createOffer(sendViewerOfferFn,
function(error) {
console.log('sendViewerOfferFn error:' + error);
},
constraints
);
pc.onaddstream = function(event) {
console.log('onaddstream');
document.getElementById('rtc_media_player').srcObject = event.stream;
rtc_media_player.load();
};
pc.onicecandidate = function(event) {
console.log('onicecandidate');
};
pc.onconnectionstatechange = function(event) {
console.log('onconnectionstatechange');
};
pc.onicegatheringstatechange = function(event) {
console.log('onicegatheringstatechange');
};
pc.onsignalingstatechange = function(event) {
console.log('onsignalingstatechange');
};
request.onerror = function(event) {
console.log('http error');
};
request.onload = function () {
console.log('onload,' , request.responseText);
var json = JSON.parse(request.responseText);
console.log('onmessage viewerResponse:', json.sdp);
pc.setRemoteDescription(new SessionDescription({type:'answer', sdp:json.sdp}));
}
</script>
</html>

@ -0,0 +1,97 @@
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="utf-8">
</head>
<body>
<br>local_media_player: <br>
<video id = "local_media_player" autoplay></video>
<br>rtc_media_player: <br>
<video id = "rtc_media_player" autoplay></video>
<script>
var PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
var SessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription || window.webkitRTCSessionDescription;
var url = "http://hw.com:1985/api/v1/sdp/";
var method = "POST";
var shouldBeAsync = true;
var request = new XMLHttpRequest();
request.open(method, url, shouldBeAsync);
request.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
var pc = new PeerConnection(null);
var sendSdpOffer = function(desc) {
console.log('sendSdpOffer:', desc);
pc.setLocalDescription(desc);
var sdp_json = {"sdp":desc.sdp, "app":"webrtc", "stream":"test"};
request.send(JSON.stringify(sdp_json));
};
pc.onaddstream = function(event) {
console.log('onaddstream');
document.getElementById('rtc_media_player').srcObject = event.stream;
rtc_media_player.load();
};
pc.onicecandidate = function(event) {
console.log('onicecandidate');
};
pc.onconnectionstatechange = function(event) {
console.log('onconnectionstatechange');
};
pc.onicegatheringstatechange = function(event) {
console.log('onicegatheringstatechange');
};
pc.onsignalingstatechange = function(event) {
console.log('onsignalingstatechange');
};
navigator.webkitGetUserMedia({
"audio": true,
"video": {
width: 800,
height:600,
frameRate:30,
bitRate:8000,
}
}, function(stream){
document.getElementById('local_media_player').srcObject = stream;
pc.addStream(stream);
pc.createOffer(sendSdpOffer, function (error) {
console.log('Failure callback: ' + error);
});
}, function(error){
//处理媒体流创建失败错误
console.log('getUserMedia error: ' + error);
});
request.onerror = function(event) {
console.log('http error');
};
request.onload = function () {
console.log('onload,' , request.responseText);
var json = JSON.parse(request.responseText);
console.log('onmessage viewerResponse:', json.sdp);
pc.setRemoteDescription(new SessionDescription({type:'answer', sdp:json.sdp}));
}
</script>
</body>
</html>

@ -0,0 +1,90 @@
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="utf-8">
</head>
<body>
rtc_media_player: <br>
<video id = "rtc_media_player" autoplay></video>
</body>
<script>
var PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
var SessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription || window.webkitRTCSessionDescription;
var url = "http://hw.com:1985/api/v1/sdp/";
var method = "POST";
var shouldBeAsync = true;
var request = new XMLHttpRequest();
request.open(method, url, shouldBeAsync);
request.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
var pc = new PeerConnection(null);
var constraints = {
mandatory: {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
}
};
var sendViewerOfferFn = function(desc) {
console.log('sendViewerOfferFn:', desc);
pc.setLocalDescription(desc);
var sdp_json = {"sdp":desc.sdp, "app":"webrtc", "stream":"test"};
request.send(JSON.stringify(sdp_json));
};
pc.createOffer(sendViewerOfferFn,
function(error) {
console.log('sendViewerOfferFn error:' + error);
},
constraints
);
pc.onaddstream = function(event) {
console.log('onaddstream');
document.getElementById('rtc_media_player').srcObject = event.stream;
rtc_media_player.load();
};
pc.onicecandidate = function(event) {
console.log('onicecandidate');
};
pc.onconnectionstatechange = function(event) {
console.log('onconnectionstatechange');
};
pc.onicegatheringstatechange = function(event) {
console.log('onicegatheringstatechange');
};
pc.onsignalingstatechange = function(event) {
console.log('onsignalingstatechange');
};
request.onerror = function(event) {
console.log('http error');
};
request.onload = function () {
console.log('onload,' , request.responseText);
var json = JSON.parse(request.responseText);
console.log('onmessage viewerResponse:', json.sdp);
pc.setRemoteDescription(new SessionDescription({type:'answer', sdp:json.sdp}));
}
</script>
</html>

@ -3486,7 +3486,7 @@ srs_error_t SrsConfig::check_normal_config()
&& n != "srs_log_tank" && n != "srs_log_level" && n != "srs_log_file"
&& n != "max_connections" && n != "daemon" && n != "heartbeat"
&& n != "http_api" && n != "stats" && n != "vhost" && n != "pithy_print_ms"
&& n != "http_server" && n != "stream_caster" && n != "srt_server"
&& n != "http_server" && n != "stream_caster" && n != "rtc" && n != "srt_server"
&& n != "utc_time" && n != "work_dir" && n != "asprocess"
&& n != "ff_log_level" && n != "grace_final_wait" && n != "force_grace_quit"
&& n != "grace_start_wait" && n != "empty_ip_ok" && n != "disable_daemon_for_docker"
@ -4266,6 +4266,62 @@ int SrsConfig::get_stream_caster_rtp_port_max(SrsConfDirective* conf)
return ::atoi(conf->arg0().c_str());
}
int SrsConfig::get_rtc_enabled()
{
SrsConfDirective* conf = root->get("rtc");
return get_rtc_enabled(conf);
}
bool SrsConfig::get_rtc_enabled(SrsConfDirective* conf)
{
static bool DEFAULT = false;
if (!conf) {
return DEFAULT;
}
conf = conf->get("enabled");
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
return SRS_CONF_PERFER_FALSE(conf->arg0());
}
int SrsConfig::get_rtc_listen()
{
static int DEFAULT = 9527;
SrsConfDirective* conf = root->get("rtc");
if (!conf) {
return DEFAULT;
}
conf = conf->get("listen");
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
return ::atoi(conf->arg0().c_str());
}
std::string SrsConfig::get_rtc_candidates()
{
static string DEFAULT = "*";
SrsConfDirective* conf = root->get("rtc");
if (!conf) {
return DEFAULT;
}
conf = conf->get("candidate");
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
return (conf->arg0().c_str());
}
SrsConfDirective* SrsConfig::get_vhost(string vhost, bool try_default_vhost)
{
srs_assert(root);

@ -498,6 +498,14 @@ public:
virtual int get_stream_caster_rtp_port_min(SrsConfDirective* conf);
// Get the max udp port for rtp of stream caster rtsp.
virtual int get_stream_caster_rtp_port_max(SrsConfDirective* conf);
// rtc section
public:
virtual int get_rtc_enabled();
virtual bool get_rtc_enabled(SrsConfDirective* conf);
virtual int get_rtc_listen();
virtual std::string get_rtc_candidates();
// vhost specified section
public:
// Get the vhost directive by vhost name.

@ -0,0 +1,137 @@
/**
* 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_dtls.hpp>
using namespace std;
#include <string.h>
#include <srs_kernel_log.hpp>
#include <srtp2/srtp.h>
SrsDtls* SrsDtls::_instance = NULL;
SrsDtls::SrsDtls()
{
}
SrsDtls::~SrsDtls()
{
}
SrsDtls* SrsDtls::instance()
{
if (!_instance) {
_instance = new SrsDtls();
_instance->init();
}
return _instance;
}
void SrsDtls::init()
{
// srtp init first
srs_assert(srtp_init() == 0);
// init dtls context
EVP_PKEY* dtls_private_key = EVP_PKEY_new();
srs_assert(dtls_private_key);
RSA* rsa = RSA_new();
srs_assert(rsa);
BIGNUM* exponent = BN_new();
srs_assert(exponent);
BN_set_word(exponent, RSA_F4);
const std::string& aor = "www.hw.com";
int expire_day = 365;
int private_key_len = 1024;
RSA_generate_key_ex(rsa, private_key_len, exponent, NULL);
srs_assert(EVP_PKEY_set1_RSA(dtls_private_key, rsa) == 1);
X509* dtls_cert = X509_new();
srs_assert(dtls_cert);
X509_NAME* subject = X509_NAME_new();
srs_assert(subject);
int serial = rand();
ASN1_INTEGER_set(X509_get_serialNumber(dtls_cert), serial);
X509_NAME_add_entry_by_txt(subject, "CN", MBSTRING_ASC, (unsigned char *) aor.data(), aor.size(), -1, 0);
X509_set_issuer_name(dtls_cert, subject);
X509_set_subject_name(dtls_cert, subject);
const long cert_duration = 60*60*24*expire_day;
X509_gmtime_adj(X509_get_notBefore(dtls_cert), 0);
X509_gmtime_adj(X509_get_notAfter(dtls_cert), cert_duration);
srs_assert(X509_set_pubkey(dtls_cert, dtls_private_key) == 1);
srs_assert(X509_sign(dtls_cert, dtls_private_key, EVP_sha1()) != 0);
// cleanup
RSA_free(rsa);
BN_free(exponent);
X509_NAME_free(subject);
dtls_ctx = SSL_CTX_new(DTLSv1_2_method());
srs_assert(SSL_CTX_use_certificate(dtls_ctx, dtls_cert) == 1);
srs_assert(SSL_CTX_use_PrivateKey(dtls_ctx, dtls_private_key) == 1);
srs_assert(SSL_CTX_set_cipher_list(dtls_ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH") == 1);
srs_assert(SSL_CTX_set_tlsext_use_srtp(dtls_ctx, "SRTP_AES128_CM_SHA1_80") == 0);
SSL_CTX_set_verify_depth (dtls_ctx, 4);
SSL_CTX_set_read_ahead(dtls_ctx, 1);
// dtls fingerprint
char fp[100] = {0};
char *p = fp;
unsigned char md[EVP_MAX_MD_SIZE];
unsigned int n = 0;
int r = X509_digest(dtls_cert, EVP_sha256(), md, &n);
for (unsigned int i = 0; i < n; i++, ++p) {
sprintf(p, "%02X", md[i]);
p += 2;
if(i < (n-1)) {
*p = ':';
} else {
*p = '\0';
}
}
fingerprint.assign(fp, strlen(fp));
srs_trace("fingerprint=%s", fingerprint.c_str());
}

@ -0,0 +1,52 @@
/**
* 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_DTLS_HPP
#define SRS_APP_DTLS_HPP
#include <srs_core.hpp>
#include <string>
#include <openssl/ssl.h>
class SrsDtls
{
private:
static SrsDtls* _instance;
private:
std::string fingerprint;
SSL_CTX* dtls_ctx;
private:
SrsDtls();
virtual ~SrsDtls();
void init();
public:
static SrsDtls* instance();
SSL_CTX* get_dtls_ctx() { return dtls_ctx; }
public:
std::string get_fingerprint() const { return fingerprint; }
};
#endif

@ -46,6 +46,7 @@ using namespace std;
#include <srs_protocol_amf0.hpp>
#include <srs_protocol_utility.hpp>
#include <srs_app_coworkers.hpp>
#include <srs_app_rtc_conn.hpp>
srs_error_t srs_api_response_jsonp(ISrsHttpResponseWriter* w, string callback, string data)
{
@ -780,6 +781,96 @@ srs_error_t SrsGoApiStreams::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa
return srs_api_response(w, r, obj->dumps());
}
SrsGoApiSdp::SrsGoApiSdp(SrsServer* svr, SrsRtcServer* rtc_svr)
{
server = svr;
rtc_server = rtc_svr;
}
SrsGoApiSdp::~SrsGoApiSdp()
{
}
srs_error_t SrsGoApiSdp::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
srs_error_t err = srs_success;
SrsStatistic* stat = SrsStatistic::instance();
// path: {pattern}{stream_id}
// e.g. /api/v1/streams/100 pattern= /api/v1/streams/, stream_id=100
int sid = r->parse_rest_id(entry->pattern);
SrsStatisticStream* stream = NULL;
if (sid >= 0 && (stream = stat->find_stream(sid)) == NULL) {
return srs_api_response_code(w, r, ERROR_RTMP_STREAM_NOT_FOUND);
}
string req_json;
r->body_read_all(req_json);
srs_trace("req_json=%s", req_json.c_str());
SrsJsonAny* json = SrsJsonAny::loads(req_json);
SrsJsonObject* req_obj = json->to_object();
SrsJsonAny* remote_sdp_obj = req_obj->get_property("sdp");
SrsJsonAny* app_obj = req_obj->get_property("app");
SrsJsonAny* stream_name_obj = req_obj->get_property("stream");
if (remote_sdp_obj == NULL || app_obj == NULL || stream_name_obj == NULL) {
return srs_api_response_code(w, r, SRS_CONSTS_HTTP_BadRequest);
}
string remote_sdp_str = remote_sdp_obj->to_str();
string app = app_obj->to_str();
string stream_name = stream_name_obj->to_str();
srs_trace("remote_sdp_str=%s", remote_sdp_str.c_str());
srs_trace("app=%s, stream=%s", app.c_str(), stream_name.c_str());
SrsSdp remote_sdp;
err = remote_sdp.decode(remote_sdp_str);
if (err != srs_success) {
return srs_api_response_code(w, r, SRS_CONSTS_HTTP_BadRequest);
}
SrsSdp local_sdp;
SrsRtcSession* rtc_session = rtc_server->create_rtc_session(remote_sdp, local_sdp);
rtc_session->set_app_stream(app, stream_name);
string local_sdp_str = "";
err = local_sdp.encode(local_sdp_str);
if (err != srs_success) {
return srs_api_response_code(w, r, SRS_CONSTS_HTTP_BadRequest);
}
SrsJsonObject* obj = SrsJsonAny::object();
SrsAutoFree(SrsJsonObject, obj);
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
obj->set("server", SrsJsonAny::integer(stat->server_id()));
// XXX: ice candidate
//string candidate_str = "candidate:1 1 udp 2115783679 192.168.170.129:9527 typ host generation 0 ufrag "
// + local_sdp.get_ice_ufrag() + "netwrok-cost 50";
//SrsJsonObject* candidate_obj = SrsJsonAny::object();
//SrsAutoFree(SrsJsonObject, candidate_obj);
//candidate_obj->set("candidate", SrsJsonAny::str(candidate_str.c_str()));
//candidate_obj->set("sdpMid", SrsJsonAny::str("0"));
//candidate_obj->set("sdpMLineIndex", SrsJsonAny::str("0"));
if (r->is_http_post()) {
obj->set("sdp", SrsJsonAny::str(local_sdp_str.c_str()));
// obj->set("candidate", candidate_obj);
} else {
return srs_go_http_error(w, SRS_CONSTS_HTTP_MethodNotAllowed);
}
return srs_api_response(w, r, obj->dumps());
}
SrsGoApiClients::SrsGoApiClients()
{
}

@ -31,6 +31,7 @@ class ISrsHttpMessage;
class SrsHttpParser;
class SrsHttpHandler;
class SrsServer;
class SrsRtcServer;
#include <srs_app_st.hpp>
#include <srs_app_conn.hpp>
@ -164,6 +165,18 @@ public:
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
};
class SrsGoApiSdp : public ISrsHttpHandler
{
private:
SrsServer* server;
SrsRtcServer* rtc_server;
public:
SrsGoApiSdp(SrsServer* svr, SrsRtcServer* rtc_svr);
virtual ~SrsGoApiSdp();
public:
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
};
class SrsGoApiClients : public ISrsHttpHandler
{
public:

@ -26,6 +26,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
@ -59,6 +60,19 @@ srs_error_t ISrsUdpHandler::on_stfd_change(srs_netfd_t /*fd*/)
return srs_success;
}
ISrsUdpMuxHandler::ISrsUdpMuxHandler()
{
}
ISrsUdpMuxHandler::~ISrsUdpMuxHandler()
{
}
srs_error_t ISrsUdpMuxHandler::on_stfd_change(srs_netfd_t /*fd*/)
{
return srs_success;
}
ISrsTcpHandler::ISrsTcpHandler()
{
}
@ -207,3 +221,194 @@ srs_error_t SrsTcpListener::cycle()
return err;
}
SrsUdpMuxSocket::SrsUdpMuxSocket(srs_netfd_t fd)
{
nb_buf = SRS_UDP_MAX_PACKET_SIZE;
buf = new char[nb_buf];
nread = 0;
lfd = fd;
fromlen = 0;
}
SrsUdpMuxSocket::~SrsUdpMuxSocket()
{
srs_freepa(buf);
}
SrsUdpMuxSocket::SrsUdpMuxSocket(const SrsUdpMuxSocket& rhs)
{
operator=(rhs);
}
SrsUdpMuxSocket& SrsUdpMuxSocket::operator=(const SrsUdpMuxSocket& rhs)
{
buf = NULL;
nb_buf = 0;
nread = 0;
lfd = rhs.lfd;
from = rhs.from;
fromlen = rhs.fromlen;
peer_ip = rhs.peer_ip;
peer_port = rhs.peer_port;
return *this;
}
int SrsUdpMuxSocket::recvfrom(srs_utime_t timeout)
{
fromlen = sizeof(from);
nread = srs_recvfrom(lfd, buf, nb_buf, (sockaddr*)&from, &fromlen, timeout);
if (nread > 0) {
char address_string[64];
char port_string[16];
if (getnameinfo((sockaddr*)&from, fromlen,
(char*)&address_string, sizeof(address_string),
(char*)&port_string, sizeof(port_string),
NI_NUMERICHOST|NI_NUMERICSERV)) {
return -1;
}
peer_ip = std::string(address_string);
peer_port = atoi(port_string);
}
return nread;
}
int SrsUdpMuxSocket::sendto(void* data, int size, srs_utime_t timeout)
{
return srs_sendto(lfd, data, size, (sockaddr*)&from, fromlen, timeout);
}
int SrsUdpMuxSocket::sendtov(struct iovec* iov, size_t iovlen, srs_utime_t timeout)
{
struct msghdr udphdr = {0};
udphdr.msg_name = &from;
udphdr.msg_namelen = fromlen;
udphdr.msg_iov = iov;
udphdr.msg_iovlen = iovlen;
return srs_sendmsg(lfd, &udphdr, 0, timeout);
}
std::string SrsUdpMuxSocket::get_peer_id()
{
char id_buf[1024];
int len = snprintf(id_buf, sizeof(id_buf), "%s:%d", peer_ip.c_str(), peer_port);
return string(id_buf, len);
}
SrsUdpMuxListener::SrsUdpMuxListener(ISrsUdpMuxHandler* h, std::string i, int p)
{
handler = h;
ip = i;
port = p;
lfd = NULL;
nb_buf = SRS_UDP_MAX_PACKET_SIZE;
buf = new char[nb_buf];
trd = new SrsDummyCoroutine();
}
SrsUdpMuxListener::~SrsUdpMuxListener()
{
srs_freep(trd);
srs_close_stfd(lfd);
srs_freepa(buf);
}
int SrsUdpMuxListener::fd()
{
return srs_netfd_fileno(lfd);
}
srs_netfd_t SrsUdpMuxListener::stfd()
{
return lfd;
}
srs_error_t SrsUdpMuxListener::listen()
{
srs_error_t err = srs_success;
if ((err = srs_udp_listen(ip, port, &lfd)) != srs_success) {
return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port);
}
set_socket_buffer();
srs_freep(trd);
trd = new SrsSTCoroutine("udp", this);
if ((err = trd->start()) != srs_success) {
return srs_error_wrap(err, "start thread");
}
return err;
}
void SrsUdpMuxListener::set_socket_buffer()
{
int sndbuf_size = 0;
socklen_t opt_len = sizeof(sndbuf_size);
getsockopt(fd(), SOL_SOCKET, SO_SNDBUF, (void*)&sndbuf_size, &opt_len);
srs_trace("default udp remux socket sndbuf=%d", sndbuf_size);
sndbuf_size = 1024*1024*10; // 10M
if (setsockopt(fd(), SOL_SOCKET, SO_SNDBUF, (void*)&sndbuf_size, sizeof(sndbuf_size)) < 0) {
srs_warn("set sock opt SO_SNDBUFFORCE failed");
}
opt_len = sizeof(sndbuf_size);
getsockopt(fd(), SOL_SOCKET, SO_SNDBUF, (void*)&sndbuf_size, &opt_len);
srs_trace("udp remux socket sndbuf=%d", sndbuf_size);
int rcvbuf_size = 0;
opt_len = sizeof(rcvbuf_size);
getsockopt(fd(), SOL_SOCKET, SO_RCVBUF, (void*)&rcvbuf_size, &opt_len);
srs_trace("default udp remux socket rcvbuf=%d", rcvbuf_size);
rcvbuf_size = 1024*1024*10; // 10M
if (setsockopt(fd(), SOL_SOCKET, SO_RCVBUF, (void*)&rcvbuf_size, sizeof(rcvbuf_size)) < 0) {
srs_warn("set sock opt SO_RCVBUFFORCE failed");
}
opt_len = sizeof(rcvbuf_size);
getsockopt(fd(), SOL_SOCKET, SO_RCVBUF, (void*)&rcvbuf_size, &opt_len);
srs_trace("udp remux socket rcvbuf=%d", rcvbuf_size);
}
srs_error_t SrsUdpMuxListener::cycle()
{
srs_error_t err = srs_success;
while (true) {
if ((err = trd->pull()) != srs_success) {
return srs_error_wrap(err, "udp listener");
}
SrsUdpMuxSocket udp_mux_skt(lfd);
if (udp_mux_skt.recvfrom(SRS_UTIME_NO_TIMEOUT) <= 0) {
srs_error("udp recv error");
// remux udp never return
continue;
}
if ((err = handler->on_udp_packet(&udp_mux_skt)) != srs_success) {
// remux udp never return
srs_error("udp packet handler error:%s", srs_error_desc(err).c_str());
continue;
}
if (SrsUdpPacketRecvCycleInterval > 0) {
srs_usleep(SrsUdpPacketRecvCycleInterval);
}
}
return err;
}

@ -26,6 +26,8 @@
#include <srs_core.hpp>
#include <sys/socket.h>
#include <string>
#include <srs_app_st.hpp>
@ -33,6 +35,8 @@
struct sockaddr;
class SrsUdpMuxSocket;
// The udp packet handler.
class ISrsUdpHandler
{
@ -54,6 +58,16 @@ public:
virtual srs_error_t on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf) = 0;
};
class ISrsUdpMuxHandler
{
public:
ISrsUdpMuxHandler();
virtual ~ISrsUdpMuxHandler();
public:
virtual srs_error_t on_stfd_change(srs_netfd_t fd);
virtual srs_error_t on_udp_packet(SrsUdpMuxSocket* udp_mux_skt) = 0;
};
// The tcp connection handler.
class ISrsTcpHandler
{
@ -68,13 +82,13 @@ public:
// Bind udp port, start thread to recv packet and handler it.
class SrsUdpListener : public ISrsCoroutineHandler
{
private:
protected:
srs_netfd_t lfd;
SrsCoroutine* trd;
private:
protected:
char* buf;
int nb_buf;
private:
protected:
ISrsUdpHandler* handler;
std::string ip;
int port;
@ -113,4 +127,60 @@ public:
virtual srs_error_t cycle();
};
class SrsUdpMuxSocket
{
private:
char* buf;
int nb_buf;
int nread;
srs_netfd_t lfd;
sockaddr_storage from;
int fromlen;
std::string peer_ip;
int peer_port;
public:
SrsUdpMuxSocket(srs_netfd_t fd);
virtual ~SrsUdpMuxSocket();
SrsUdpMuxSocket(const SrsUdpMuxSocket& rhs);
SrsUdpMuxSocket& operator=(const SrsUdpMuxSocket& rhs);
int recvfrom(srs_utime_t timeout);
int sendto(void* data, int size, srs_utime_t timeout);
int sendtov(struct iovec* iov, size_t iovlen, srs_utime_t timeout);
char* data() { return buf; }
int size() { return nread; }
std::string get_peer_ip() const { return peer_ip; }
int get_peer_port() const { return peer_port; }
std::string get_peer_id();
};
class SrsUdpMuxListener : public ISrsCoroutineHandler
{
protected:
srs_netfd_t lfd;
SrsCoroutine* trd;
protected:
char* buf;
int nb_buf;
protected:
ISrsUdpMuxHandler* handler;
std::string ip;
int port;
public:
SrsUdpMuxListener(ISrsUdpMuxHandler* h, std::string i, int p);
virtual ~SrsUdpMuxListener();
public:
virtual int fd();
virtual srs_netfd_t stfd();
public:
virtual srs_error_t listen();
// Interface ISrsReusableThreadHandler.
public:
virtual srs_error_t cycle();
private:
void set_socket_buffer();
};
#endif

@ -37,7 +37,7 @@
#include <srs_kernel_utility.hpp>
// the max size of a line of log.
#define LOG_MAX_SIZE 4096
#define LOG_MAX_SIZE 4096000
// the tail append to each log.
#define LOG_TAIL '\n'

File diff suppressed because it is too large Load Diff

@ -0,0 +1,277 @@
/**
* 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_RTC_CONN_HPP
#define SRS_APP_RTC_CONN_HPP
#include <srs_core.hpp>
#include <srs_app_listener.hpp>
#include <srs_service_st.hpp>
#include <srs_kernel_utility.hpp>
#include <string>
#include <map>
#include <vector>
#include <openssl/ssl.h>
#include <srtp2/srtp.h>
class SrsUdpMuxSocket;
class SrsServer;
class SrsConsumer;
class SrsStunPacket;
class SrsRtcServer;
class SrsRtcSession;
class SrsSharedPtrMessage;
const uint8_t kSR = 200;
const uint8_t kRR = 201;
const uint8_t kSDES = 202;
const uint8_t kBye = 203;
const uint8_t kApp = 204;
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
{
INIT = -1,
WAITING_STUN = 1,
DOING_DTLS_HANDSHAKE = 2,
ESTABLISHED = 3,
CLOSED = 4,
};
class SrsDtlsSession
{
private:
SrsRtcSession* rtc_session;
SSL* dtls;
BIO* bio_in;
BIO* bio_out;
std::string client_key;
std::string server_key;
srtp_t srtp_send;
srtp_t srtp_recv;
bool handshake_done;
public:
SrsDtlsSession(SrsRtcSession* s);
virtual ~SrsDtlsSession();
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);
void 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);
srs_error_t protect_rtcp(char* protected_buf, const char* ori_buf, int& nb_protected_buf);
srs_error_t unprotect_rtcp(char* unprotected_buf, const char* ori_buf, int& nb_unprotected_buf);
private:
srs_error_t handshake(SrsUdpMuxSocket* udp_mux_skt);
private:
srs_error_t srtp_initialize();
srs_error_t srtp_send_init();
srs_error_t srtp_recv_init();
};
class SrsRtcSenderThread : public ISrsCoroutineHandler
{
protected:
SrsCoroutine* trd;
int _parent_cid;
private:
SrsRtcSession* rtc_session;
SrsUdpMuxSocket ukt;
public:
// Constructor.
// @param tm The receive timeout in srs_utime_t.
SrsRtcSenderThread(SrsRtcSession* s, SrsUdpMuxSocket* u, int parent_cid);
virtual ~SrsRtcSenderThread();
public:
virtual int cid();
public:
virtual srs_error_t start();
virtual void stop();
virtual void stop_loop();
public:
virtual srs_error_t cycle();
private:
void send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, SrsUdpMuxSocket* udp_mux_skt);
};
class SrsRtcSession
{
friend class SrsRtcSenderThread;
private:
SrsServer* server;
SrsRtcServer* rtc_server;
SrsSdp remote_sdp;
SrsSdp local_sdp;
SrsRtcSessionStateType session_state;
SrsDtlsSession* dtls_session;
SrsRtcSenderThread* strd;
std::string username;
std::string peer_id;
srs_utime_t last_stun_time;
public:
std::string app;
std::string stream;
public:
SrsRtcSession(SrsServer* svr, SrsRtcServer* rtc_svr, const std::string& un);
virtual ~SrsRtcSession();
public:
SrsSdp* get_local_sdp() { return &local_sdp; }
void set_local_sdp(const SrsSdp& sdp) { local_sdp = sdp; }
SrsSdp* get_remote_sdp() { return &remote_sdp; }
void set_remote_sdp(const SrsSdp& sdp) { remote_sdp = sdp; }
SrsRtcSessionStateType get_session_state() { return session_state; }
void set_session_state(SrsRtcSessionStateType state) { session_state = state; }
std::string id() const { return peer_id + "_" + username; }
void set_app_stream(const std::string& a, const std::string& s) { app = a; stream = s; }
std::string get_peer_id() const { return peer_id; }
void set_peer_id(const std::string& id) { peer_id = id; }
public:
srs_error_t on_stun(SrsUdpMuxSocket* udp_mux_skt, SrsStunPacket* stun_req);
srs_error_t on_dtls(SrsUdpMuxSocket* udp_mux_skt);
srs_error_t on_rtp(SrsUdpMuxSocket* udp_mux_skt);
srs_error_t on_rtcp(SrsUdpMuxSocket* udp_mux_skt);
public:
srs_error_t send_client_hello(SrsUdpMuxSocket* udp_mux_skt);
void on_connection_established(SrsUdpMuxSocket* udp_mux_skt);
srs_error_t start_play(SrsUdpMuxSocket* udp_mux_skt);
public:
bool is_stun_timeout() { return last_stun_time + kSrsRtcSessionStunTimeoutUs < srs_get_system_time(); }
private:
srs_error_t on_binding_request(SrsUdpMuxSocket* udp_mux_skt, SrsStunPacket* stun_req);
private:
srs_error_t do_playing(SrsConsumer* consumer, SrsUdpMuxSocket* udp_mux_skt);
};
// XXX: is there any other timer thread?
class SrsRtcTimerThread : public ISrsCoroutineHandler
{
protected:
SrsCoroutine* trd;
int _parent_cid;
private:
SrsRtcServer* rtc_server;
public:
// Constructor.
// @param tm The receive timeout in srs_utime_t.
SrsRtcTimerThread(SrsRtcServer* rtc_svr, int parent_cid);
virtual ~SrsRtcTimerThread();
public:
virtual int cid();
public:
virtual srs_error_t start();
virtual void stop();
virtual void stop_loop();
public:
virtual srs_error_t cycle();
};
class SrsRtcServer : public ISrsUdpMuxHandler
{
private:
SrsServer* server;
SrsRtcTimerThread* rttrd;
private:
std::map<std::string, SrsRtcSession*> map_username_session; // key: username(local_ufrag + ":" + remote_ufrag)
std::map<std::string, SrsRtcSession*> map_id_session; // key: peerip(ip + ":" + port)
public:
SrsRtcServer(SrsServer* svr);
virtual ~SrsRtcServer();
public:
virtual srs_error_t initialize();
virtual srs_error_t on_udp_packet(SrsUdpMuxSocket* udp_mux_skt);
SrsRtcSession* create_rtc_session(const SrsSdp& remote_sdp, SrsSdp& local_sdp);
bool insert_into_id_sessions(const std::string& peer_id, SrsRtcSession* rtc_session);
void check_and_clean_timeout_session();
private:
srs_error_t on_stun(SrsUdpMuxSocket* udp_mux_skt);
srs_error_t on_dtls(SrsUdpMuxSocket* udp_mux_skt);
srs_error_t on_rtp_or_rtcp(SrsUdpMuxSocket* udp_mux_skt);
private:
SrsRtcSession* find_rtc_session_by_username(const std::string& ufrag);
SrsRtcSession* find_rtc_session_by_peer_id(const std::string& peer_id);
};
#endif

@ -0,0 +1,366 @@
/**
* 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_rtp.hpp>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <algorithm>
#include <sstream>
using namespace std;
#include <srs_kernel_buffer.hpp>
#include <srs_kernel_error.hpp>
#include <srs_kernel_codec.hpp>
#include <srs_kernel_flv.hpp>
#include <srs_kernel_rtp.hpp>
#include <srs_app_config.hpp>
#include <srs_app_source.hpp>
#include <srs_core_autofree.hpp>
#include <srs_app_pithy_print.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_kernel_codec.hpp>
#include <srs_kernel_file.hpp>
#include <srs_app_utility.hpp>
#include <srs_app_http_hooks.hpp>
#include <srs_protocol_format.hpp>
#include <openssl/rand.h>
SrsRtpMuxer::SrsRtpMuxer()
{
sequence = 0;
}
SrsRtpMuxer::~SrsRtpMuxer()
{
}
srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsFormat* format)
{
srs_error_t err = srs_success;
if (format->is_avc_sequence_header()) {
sps.assign(format->vcodec->sequenceParameterSetNALUnit.data(), format->vcodec->sequenceParameterSetNALUnit.size());
pps.assign(format->vcodec->pictureParameterSetNALUnit.data(), format->vcodec->pictureParameterSetNALUnit.size());
}
vector<SrsRtpSharedPacket*> rtp_packet_vec;
for (int i = 0; i < format->video->nb_samples; ++i) {
SrsSample sample = format->video->samples[i];
uint8_t header = sample.bytes[0];
uint8_t nal_type = header & kNalTypeMask;
if (nal_type == 0x06) {
srs_trace("ignore SEI");
continue;
}
if (sample.size <= max_payload_size) {
packet_single_nalu(shared_frame, format, &sample, rtp_packet_vec);
} else {
packet_fu_a(shared_frame, format, &sample, rtp_packet_vec);
}
#if 0
srs_trace("nal size=%d, nal=%s", sample.size, dump_string_hex(sample.bytes, sample.size, sample.size).c_str());
for (int i = 0; i < shared_frame->nb_rtp_fragments; ++i) {
srs_trace("rtp=%s", dump_string_hex(shared_frame->rtp_fragments[i].bytes, shared_frame->rtp_fragments[i].size, kRtpPacketSize).c_str());
}
#endif
}
shared_frame->set_rtp_packets(rtp_packet_vec);
return err;
}
srs_error_t SrsRtpMuxer::packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, vector<SrsRtpSharedPacket*>& rtp_packet_vec)
{
srs_error_t err = srs_success;
char* p = sample->bytes + 1;
int nb_left = sample->size - 1;
uint8_t header = sample->bytes[0];
uint8_t nal_type = header & kNalTypeMask;
if (nal_type == kIdr) {
packet_stap_a(sps, pps, shared_frame, rtp_packet_vec);
}
int num_of_packet = (sample->size - 1 + max_payload_size) / max_payload_size;
int avg_packet_size = sample->size / num_of_packet;
for (int i = 0; i < num_of_packet; ++i) {
char* buf = new char[kRtpPacketSize];
SrsBuffer* stream = new SrsBuffer(buf, kRtpPacketSize);
SrsAutoFree(SrsBuffer, stream);
int packet_size = min(nb_left, max_payload_size);
// v=2,p=0,x=0,cc=0
stream->write_1bytes(0x80);
// marker payloadtype
if (i == num_of_packet - 1) {
stream->write_1bytes(kMarker | kH264PayloadType);
} else {
stream->write_1bytes(kH264PayloadType);
}
// sequence
stream->write_2bytes(sequence);
// timestamp
stream->write_4bytes(int32_t(shared_frame->timestamp * 90));
// ssrc
stream->write_4bytes(int32_t(kVideoSSRC));
// fu-indicate
uint8_t fu_indicate = kFuA;
fu_indicate |= (header & (~kNalTypeMask));
stream->write_1bytes(fu_indicate);
uint8_t fu_header = nal_type;
if (i == 0)
fu_header |= kStart;
if (i == num_of_packet - 1)
fu_header |= kEnd;
stream->write_1bytes(fu_header);
stream->write_bytes(p, packet_size);
p += packet_size;
nb_left -= packet_size;
SrsRtpSharedPacket* rtp_shared_pkt = new SrsRtpSharedPacket();
rtp_shared_pkt->create((shared_frame->timestamp * 90), sequence++, kVideoSSRC, kH264PayloadType, stream->data(), stream->pos());
rtp_packet_vec.push_back(rtp_shared_pkt);
}
}
srs_error_t SrsRtpMuxer::packet_single_nalu(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, vector<SrsRtpSharedPacket*>& rtp_packet_vec)
{
srs_error_t err = srs_success;
uint8_t header = sample->bytes[0];
uint8_t nal_type = header & kNalTypeMask;
char* buf = new char[kRtpPacketSize];
SrsBuffer* stream = new SrsBuffer(buf, kRtpPacketSize);
SrsAutoFree(SrsBuffer, stream);
if (nal_type == kIdr) {
packet_stap_a(sps, pps, shared_frame, rtp_packet_vec);
}
// v=2,p=0,x=0,cc=0
stream->write_1bytes(0x80);
// marker payloadtype
stream->write_1bytes(kMarker | kH264PayloadType);
// sequenct
stream->write_2bytes(sequence);
// timestamp
stream->write_4bytes(int32_t(shared_frame->timestamp * 90));
// ssrc
stream->write_4bytes(int32_t(kVideoSSRC));
stream->write_bytes(sample->bytes, sample->size);
SrsRtpSharedPacket* rtp_shared_pkt = new SrsRtpSharedPacket();
rtp_shared_pkt->create((shared_frame->timestamp * 90), sequence++, kVideoSSRC, kH264PayloadType, stream->data(), stream->pos());
rtp_packet_vec.push_back(rtp_shared_pkt);
return err;
}
srs_error_t SrsRtpMuxer::packet_stap_a(const string &sps, const string& pps, SrsSharedPtrMessage* shared_frame, vector<SrsRtpSharedPacket*>& rtp_packet_vec)
{
srs_error_t err = srs_success;
uint8_t header = sps[0];
uint8_t nal_type = header & kNalTypeMask;
char* buf = new char[kRtpPacketSize];
SrsBuffer* stream = new SrsBuffer(buf, kRtpPacketSize);
SrsAutoFree(SrsBuffer, stream);
// v=2,p=0,x=0,cc=0
stream->write_1bytes(0x80);
// marker payloadtype
stream->write_1bytes(kMarker | kH264PayloadType);
// sequenct
stream->write_2bytes(sequence);
// timestamp
stream->write_4bytes(int32_t(shared_frame->timestamp * 90));
// ssrc
stream->write_4bytes(int32_t(kVideoSSRC));
// stap-a header
uint8_t stap_a_header = kStapA;
stap_a_header |= (nal_type & (~kNalTypeMask));
stream->write_1bytes(stap_a_header);
stream->write_2bytes(sps.size());
stream->write_bytes((char*)sps.data(), sps.size());
stream->write_2bytes(pps.size());
stream->write_bytes((char*)pps.data(), pps.size());
SrsRtpSharedPacket* rtp_shared_pkt = new SrsRtpSharedPacket();
rtp_shared_pkt->create((shared_frame->timestamp * 90), sequence++, kVideoSSRC, kH264PayloadType, stream->data(), stream->pos());
rtp_packet_vec.push_back(rtp_shared_pkt);
return err;
}
SrsRtp::SrsRtp()
{
req = NULL;
hub = NULL;
enabled = false;
disposable = false;
last_update_time = 0;
}
SrsRtp::~SrsRtp()
{
}
void SrsRtp::dispose()
{
if (enabled) {
on_unpublish();
}
}
srs_error_t SrsRtp::cycle()
{
srs_error_t err = srs_success;
return err;
}
srs_error_t SrsRtp::initialize(SrsOriginHub* h, SrsRequest* r)
{
srs_error_t err = srs_success;
hub = h;
req = r;
rtp_h264_muxer = new SrsRtpMuxer();
return err;
}
srs_error_t SrsRtp::on_publish()
{
srs_error_t err = srs_success;
// update the hls time, for hls_dispose.
last_update_time = srs_get_system_time();
// support multiple publish.
if (enabled) {
return err;
}
// if enabled, open the muxer.
enabled = true;
// ok, the hls can be dispose, or need to be dispose.
disposable = true;
return err;
}
void SrsRtp::on_unpublish()
{
srs_error_t err = srs_success;
// support multiple unpublish.
if (!enabled) {
return;
}
enabled = false;
}
srs_error_t SrsRtp::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format)
{
srs_error_t err = srs_success;
if (!enabled) {
return err;
}
// Ignore if no format->acodec, it means the codec is not parsed, or unknown codec.
// @issue https://github.com/ossrs/srs/issues/1506#issuecomment-562079474
if (!format->acodec) {
return err;
}
// update the hls time, for hls_dispose.
last_update_time = srs_get_system_time();
// ts support audio codec: aac/mp3
SrsAudioCodecId acodec = format->acodec->id;
if (acodec != SrsAudioCodecIdAAC && acodec != SrsAudioCodecIdMP3) {
return err;
}
// ignore sequence header
srs_assert(format->audio);
// TODO: rtc no support aac
return err;
}
srs_error_t SrsRtp::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format)
{
srs_error_t err = srs_success;
if (!enabled) {
return err;
}
// Ignore if no format->vcodec, it means the codec is not parsed, or unknown codec.
// @issue https://github.com/ossrs/srs/issues/1506#issuecomment-562079474
if (!format->vcodec) {
return err;
}
// update the hls time, for hls_dispose.
last_update_time = srs_get_system_time();
// ignore info frame,
// @see https://github.com/ossrs/srs/issues/288#issuecomment-69863909
srs_assert(format->video);
return rtp_h264_muxer->frame_to_packet(shared_video, format);
}

@ -0,0 +1,97 @@
/**
* 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_RTP_HPP
#define SRS_APP_RTP_HPP
#include <srs_core.hpp>
#include <string>
#include <vector>
#include <map>
class SrsFormat;
class SrsSample;
class SrsSharedPtrMessage;
class SrsRtpSharedPacket;
class SrsRequest;
class SrsOriginHub;
const int max_payload_size = 1200;
const int kRtpPacketSize = 1500;
const uint8_t kMarker = 0x80;
const uint8_t kH264PayloadType = 102;
const uint8_t kNalTypeMask = 0x1F;
const uint8_t kIdr = 5;
const uint8_t kStapA = 24;
const uint8_t kFuA = 28;
const uint8_t kStart = 0x80;
const uint8_t kEnd = 0x40;
const uint32_t kVideoSSRC = 3233846889;
class SrsRtpMuxer
{
private:
uint16_t sequence;
std::string sps;
std::string pps;
public:
SrsRtpMuxer();
virtual ~SrsRtpMuxer();
public:
srs_error_t frame_to_packet(SrsSharedPtrMessage* shared_video, SrsFormat* format);
private:
srs_error_t packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, std::vector<SrsRtpSharedPacket*>& rtp_packet_vec);
srs_error_t packet_single_nalu(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, std::vector<SrsRtpSharedPacket*>& rtp_packet_vec);
srs_error_t packet_stap_a(const std::string &sps, const std::string& pps, SrsSharedPtrMessage* shared_frame, std::vector<SrsRtpSharedPacket*>& rtp_packet_vec);
};
class SrsRtp
{
private:
SrsRequest* req;
bool enabled;
bool disposable;
srs_utime_t last_update_time;
SrsRtpMuxer* rtp_h264_muxer;
SrsOriginHub* hub;
public:
SrsRtp();
virtual ~SrsRtp();
public:
virtual void dispose();
virtual srs_error_t cycle();
public:
virtual srs_error_t initialize(SrsOriginHub* h, SrsRequest* r);
virtual srs_error_t on_publish();
virtual void on_unpublish();
virtual srs_error_t on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format);
virtual srs_error_t on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format);
};
#endif

@ -45,6 +45,7 @@ using namespace std;
#include <srs_app_utility.hpp>
#include <srs_app_heartbeat.hpp>
#include <srs_app_mpegts_udp.hpp>
#include <srs_app_rtc_conn.hpp>
#include <srs_app_rtsp.hpp>
#include <srs_app_statistic.hpp>
#include <srs_app_caster_flv.hpp>
@ -109,6 +110,8 @@ std::string srs_listener_type2string(SrsListenerType type)
return "RTSP";
case SrsListenerFlv:
return "HTTP-FLV";
case SrsListenerRtc:
return "RTC";
default:
return "UNKONWN";
}
@ -339,6 +342,45 @@ SrsUdpCasterListener::~SrsUdpCasterListener()
srs_freep(caster);
}
SrsRtcListener::SrsRtcListener(SrsServer* svr, SrsRtcServer* rtc_svr, SrsListenerType t) : SrsListener(svr, t)
{
srs_assert(type == SrsListenerRtc);
rtc = rtc_svr;
}
SrsRtcListener::~SrsRtcListener()
{
}
srs_error_t SrsRtcListener::listen(std::string i, int p)
{
srs_error_t err = srs_success;
// the caller already ensure the type is ok,
// we just assert here for unknown stream caster.
srs_assert(type == SrsListenerRtc);
ip = i;
port = p;
srs_freep(listener);
listener = new SrsUdpMuxListener(rtc, ip, port);
if ((err = listener->listen()) != srs_success) {
return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port);
}
// notify the handler the fd changed.
if ((err = rtc->on_stfd_change(listener->stfd())) != srs_success) {
return srs_error_wrap(err, "notify fd change failed");
}
string v = srs_listener_type2string(type);
srs_trace("%s listen at udp://%s:%d, fd=%d", v.c_str(), ip.c_str(), port, listener->fd());
return err;
}
SrsSignalManager* SrsSignalManager::instance = NULL;
SrsSignalManager::SrsSignalManager(SrsServer* s)
@ -630,6 +672,7 @@ SrsServer::SrsServer()
// new these objects in initialize instead.
http_api_mux = new SrsHttpServeMux();
http_server = new SrsHttpServer(this);
rtc_server = new SrsRtcServer(this);
http_heartbeat = new SrsHttpHeartbeat();
ingester = new SrsIngester();
}
@ -754,6 +797,10 @@ srs_error_t SrsServer::initialize(ISrsServerCycle* ch)
if ((err = http_server->initialize()) != srs_success) {
return srs_error_wrap(err, "http server initialize");
}
if ((err = rtc_server->initialize()) != srs_success) {
return srs_error_wrap(err, "rtc server initialize");
}
return err;
}
@ -876,6 +923,10 @@ srs_error_t SrsServer::listen()
if ((err = listen_stream_caster()) != srs_success) {
return srs_error_wrap(err, "stream caster listen");
}
if ((err = listen_rtc()) != srs_success) {
return srs_error_wrap(err, "rtc listen");
}
if ((err = conn_manager->start()) != srs_success) {
return srs_error_wrap(err, "connection manager");
@ -938,6 +989,9 @@ srs_error_t SrsServer::http_handle()
if ((err = http_api_mux->handle("/api/v1/streams/", new SrsGoApiStreams())) != srs_success) {
return srs_error_wrap(err, "handle streams");
}
if ((err = http_api_mux->handle("/api/v1/sdp/", new SrsGoApiSdp(this, rtc_server))) != srs_success) {
return srs_error_wrap(err, "handle sdp");
}
if ((err = http_api_mux->handle("/api/v1/clients/", new SrsGoApiClients())) != srs_success) {
return srs_error_wrap(err, "handle clients");
}
@ -1346,6 +1400,35 @@ srs_error_t SrsServer::listen_stream_caster()
return err;
}
srs_error_t SrsServer::listen_rtc()
{
srs_error_t err = srs_success;
close_listeners(SrsListenerRtc);
if (!_srs_config->get_rtc_enabled()) {
return err;
}
SrsListener* listener = NULL;
listener = new SrsRtcListener(this, rtc_server, SrsListenerRtc);
srs_assert(listener != NULL);
listeners.push_back(listener);
int port = _srs_config->get_rtc_listen();
if (port <= 0) {
return srs_error_new(ERROR_RTC_PORT, "invalid port=%d", port);
}
if ((err = listener->listen(srs_any_address_for_listener(), port)) != srs_success) {
return srs_error_wrap(err, "listen at %d", port);
}
return err;
}
void SrsServer::close_listeners(SrsListenerType type)
{
std::vector<SrsListener*>::iterator it;
@ -1362,6 +1445,23 @@ void SrsServer::close_listeners(SrsListenerType type)
}
}
SrsListener* SrsServer::find_listener(SrsListenerType type)
{
std::vector<SrsListener*>::iterator it;
for (it = listeners.begin(); it != listeners.end();) {
SrsListener* listener = *it;
if (listener->listen_type() != type) {
++it;
continue;
}
return *it;
}
return NULL;
}
void SrsServer::resample_kbps()
{
SrsStatistic* stat = SrsStatistic::instance();

@ -41,6 +41,7 @@ class SrsServer;
class SrsConnection;
class SrsHttpServeMux;
class SrsHttpServer;
class SrsRtcServer;
class SrsIngester;
class SrsHttpHeartbeat;
class SrsKbps;
@ -69,6 +70,8 @@ enum SrsListenerType
SrsListenerRtsp = 4,
// TCP stream, FLV stream over HTTP.
SrsListenerFlv = 5,
// UDP remux, rtp over udp
SrsListenerRtc = 6,
};
// A common tcp listener, for RTMP/HTTP server.
@ -156,6 +159,19 @@ public:
virtual ~SrsUdpCasterListener();
};
// A UDP listener, for udp remux rtc server
class SrsRtcListener : public SrsListener
{
protected:
SrsUdpMuxListener* listener;
ISrsUdpMuxHandler* rtc;
public:
SrsRtcListener(SrsServer* svr, SrsRtcServer* rtc_svr, SrsListenerType t);
virtual ~SrsRtcListener();
public:
virtual srs_error_t listen(std::string i, int p);
};
// Convert signal to io,
// @see: st-1.9/docs/notes.html
class SrsSignalManager : public ISrsCoroutineHandler
@ -225,6 +241,7 @@ private:
// TODO: FIXME: rename to http_api
SrsHttpServeMux* http_api_mux;
SrsHttpServer* http_server;
SrsRtcServer* rtc_server;
SrsHttpHeartbeat* http_heartbeat;
SrsIngester* ingester;
SrsCoroutineManager* conn_manager;
@ -303,6 +320,7 @@ private:
virtual srs_error_t listen_http_api();
virtual srs_error_t listen_http_stream();
virtual srs_error_t listen_stream_caster();
virtual srs_error_t listen_rtc();
// Close the listeners for specified type,
// Remove the listen object from manager.
virtual void close_listeners(SrsListenerType type);
@ -338,6 +356,8 @@ public:
public:
virtual srs_error_t on_publish(SrsSource* s, SrsRequest* r);
virtual void on_unpublish(SrsSource* s, SrsRequest* r);
// listeners commuction
virtual SrsListener* find_listener(SrsListenerType type);
};
#endif

@ -32,6 +32,7 @@ using namespace std;
#include <srs_protocol_amf0.hpp>
#include <srs_kernel_codec.hpp>
#include <srs_app_hls.hpp>
#include <srs_app_rtp.hpp>
#include <srs_app_forward.hpp>
#include <srs_app_config.hpp>
#include <srs_app_encoder.hpp>
@ -824,6 +825,7 @@ SrsOriginHub::SrsOriginHub()
dash = new SrsDash();
dvr = new SrsDvr();
encoder = new SrsEncoder();
rtp = new SrsRtp();
#ifdef SRS_AUTO_HDS
hds = new SrsHds();
#endif
@ -868,6 +870,10 @@ srs_error_t SrsOriginHub::initialize(SrsSource* s, SrsRequest* r)
return srs_error_wrap(err, "format initialize");
}
if ((err = rtp->initialize(this, req)) != srs_success) {
return srs_error_wrap(err, "rtp initialize");
}
if ((err = hls->initialize(this, req)) != srs_success) {
return srs_error_wrap(err, "hls initialize");
}
@ -965,6 +971,12 @@ srs_error_t SrsOriginHub::on_audio(SrsSharedPtrMessage* shared_audio)
flv_sample_sizes[c->sound_size], flv_sound_types[c->sound_type],
srs_flv_srates[c->sound_rate]);
}
if ((err = rtp->on_audio(msg, format)) != srs_success) {
srs_warn("rtp: ignore audio error %s", srs_error_desc(err).c_str());
srs_error_reset(err);
rtp->on_unpublish();
}
if ((err = hls->on_audio(msg, format)) != srs_success) {
// apply the error strategy for hls.
@ -1058,6 +1070,12 @@ srs_error_t SrsOriginHub::on_video(SrsSharedPtrMessage* shared_video, bool is_se
if (format->vcodec && !format->vcodec->is_avc_codec_ok()) {
return err;
}
if ((err = rtp->on_video(msg, format)) != srs_success) {
srs_warn("rtp: ignore video error %s", srs_error_desc(err).c_str());
srs_error_reset(err);
rtp->on_unpublish();
}
if ((err = hls->on_video(msg, format)) != srs_success) {
// apply the error strategy for hls.
@ -1126,6 +1144,10 @@ srs_error_t SrsOriginHub::on_publish()
return srs_error_wrap(err, "encoder publish");
}
if ((err = rtp->on_publish()) != srs_success) {
return srs_error_wrap(err, "rtp publish");
}
if ((err = hls->on_publish()) != srs_success) {
return srs_error_wrap(err, "hls publish");
}
@ -1163,6 +1185,7 @@ void SrsOriginHub::on_unpublish()
destroy_forwarders();
encoder->on_unpublish();
rtp->on_unpublish();
hls->on_unpublish();
dash->on_unpublish();
dvr->on_unpublish();
@ -1705,6 +1728,7 @@ srs_error_t SrsSourceManager::fetch_or_create(SrsRequest* r, ISrsSourceHandler*
SrsSource* source = NULL;
if ((source = fetch(r)) != NULL) {
srs_trace("found source");
*pps = source;
return err;
}
@ -1714,6 +1738,8 @@ srs_error_t SrsSourceManager::fetch_or_create(SrsRequest* r, ISrsSourceHandler*
// should always not exists for create a source.
srs_assert (pool.find(stream_url) == pool.end());
srs_trace("new source, stream_url=%s", stream_url.c_str());
source = new SrsSource();
if ((err = source->initialize(r, h)) != srs_success) {
@ -2256,7 +2282,7 @@ srs_error_t SrsSource::on_video(SrsCommonMessage* shared_video)
return srs_error_wrap(err, "create message");
}
// directly process the audio message.
// directly process the video message.
if (!mix_correct) {
return on_video_imp(&msg);
}
@ -2306,7 +2332,7 @@ srs_error_t SrsSource::on_video_imp(SrsSharedPtrMessage* msg)
if ((err = hub->on_video(msg, is_sequence_header)) != srs_success) {
return srs_error_wrap(err, "hub consume video");
}
// copy to all consumer
if (!drop_for_reduce) {
for (int i = 0; i < (int)consumers.size(); i++) {

@ -54,6 +54,7 @@ class SrsNgExec;
class SrsConnection;
class SrsMessageHeader;
class SrsHls;
class SrsRtp;
class SrsDvr;
class SrsDash;
class SrsEncoder;
@ -335,6 +336,8 @@ private:
private:
// The format, codec information.
SrsRtmpFormat* format;
// rtp handler
SrsRtp* rtp;
// hls handler.
SrsHls* hls;
// The DASH encoder.

@ -1193,3 +1193,37 @@ void srs_api_dump_summaries(SrsJsonObject* obj)
sys->set("conn_srs", SrsJsonAny::integer(nrs->nb_conn_srs));
}
string dump_string_hex(const std::string& str, const int& max_len)
{
return dump_string_hex(str.c_str(), str.size(), max_len);
}
string dump_string_hex(const char* buf, const int nb_buf, const int& max_len)
{
string ret;
ret.reserve(max_len * 4);
char tmp_buf[1024*16];
tmp_buf[0] = '\n';
int len = 1;
for (int i = 0; i < nb_buf && i < max_len; ++i) {
//int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 2, "(%03d)%02X ", i, (uint8_t)buf[i]);
int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 2, "%02X ", (uint8_t)buf[i]);
if (nb <= 0)
break;
len += nb;
if (i % 48 == 47) {
tmp_buf[len++] = '\n';
ret.append(tmp_buf, len);
len = 0;
}
}
tmp_buf[len] = '\0';
ret.append(tmp_buf, len);
return ret;
}

@ -30,6 +30,7 @@
#include <string>
#include <sstream>
#include <limits.h>
#include <arpa/inet.h>
#include <sys/resource.h>
@ -649,5 +650,8 @@ extern bool srs_is_boolean(std::string str);
// Dump summaries for /api/v1/summaries.
extern void srs_api_dump_summaries(SrsJsonObject* obj);
extern std::string dump_string_hex(const std::string& str, const int& max_len = INT_MAX);
extern std::string dump_string_hex(const char* buf, const int nb_buf, const int& max_len = INT_MAX);
#endif

@ -324,6 +324,8 @@
#define ERROR_HTTP_302_INVALID 4038
#define ERROR_BASE64_DECODE 4039
#define ERROR_HTTP_STREAM_EOF 4040
#define ERROR_RTC_PORT 4041
#define ERROR_RTP_PACKET_CREATE 4042
///////////////////////////////////////////////////////
// HTTP API error.

@ -36,6 +36,7 @@ using namespace std;
#include <srs_kernel_error.hpp>
#include <srs_kernel_buffer.hpp>
#include <srs_kernel_file.hpp>
#include <srs_kernel_rtp.hpp>
#include <srs_kernel_codec.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_core_mem_watch.hpp>
@ -228,6 +229,10 @@ SrsSharedPtrMessage::~SrsSharedPtrMessage()
ptr->shared_count--;
}
}
for (int i = 0; i < rtp_packets.size(); ++i) {
srs_freep(rtp_packets[i]);
}
}
srs_error_t SrsSharedPtrMessage::create(SrsCommonMessage* msg)
@ -345,10 +350,19 @@ SrsSharedPtrMessage* SrsSharedPtrMessage::copy()
copy->stream_id = stream_id;
copy->payload = ptr->payload;
copy->size = ptr->size;
for (int i = 0; i < rtp_packets.size(); ++i) {
copy->rtp_packets.push_back(rtp_packets[i]->copy());
}
return copy;
}
void SrsSharedPtrMessage::set_rtp_packets(const std::vector<SrsRtpSharedPacket*>& pkts)
{
rtp_packets = pkts;
}
SrsFlvTransmuxer::SrsFlvTransmuxer()
{
writer = NULL;

@ -27,6 +27,7 @@
#include <srs_core.hpp>
#include <string>
#include <vector>
// For srs-librtmp, @see https://github.com/ossrs/srs/issues/213
#ifndef _WIN32
@ -38,6 +39,8 @@ class ISrsWriter;
class ISrsReader;
class SrsFileReader;
class SrsPacket;
class SrsSample;
class SrsRtpSharedPacket;
#define SRS_FLV_TAG_HEADER_SIZE 11
#define SRS_FLV_PREVIOUS_TAG_SIZE 4
@ -285,6 +288,8 @@ public:
// @remark, not all message payload can be decoded to packet. for example,
// video/audio packet use raw bytes, no video/audio packet.
char* payload;
std::vector<SrsRtpSharedPacket*> rtp_packets;
private:
class SrsSharedPtrPayload
{
@ -339,6 +344,8 @@ public:
// copy current shared ptr message, use ref-count.
// @remark, assert object is created.
virtual SrsSharedPtrMessage* copy();
public:
virtual void set_rtp_packets(const std::vector<SrsRtpSharedPacket*>& pkts);
};
// Transmux RTMP packets to FLV stream.

@ -0,0 +1,112 @@
/**
* 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_kernel_rtp.hpp>
#include <fcntl.h>
#include <sstream>
using namespace std;
#include <srs_kernel_log.hpp>
#include <srs_kernel_error.hpp>
#include <srs_kernel_buffer.hpp>
#include <srs_kernel_utility.hpp>
SrsRtpSharedPacket::SrsRtpSharedPacketPayload::SrsRtpSharedPacketPayload()
{
payload = NULL;
size = 0;
shared_count = 0;
}
SrsRtpSharedPacket::SrsRtpSharedPacketPayload::~SrsRtpSharedPacketPayload()
{
srs_freepa(payload);
}
SrsRtpSharedPacket::SrsRtpSharedPacket()
{
payload_ptr = NULL;
payload = NULL;
size = 0;
timestamp = -1;
sequence = 0;
ssrc = 0;
payload_type = 0;
}
SrsRtpSharedPacket::~SrsRtpSharedPacket()
{
if (payload_ptr) {
if (payload_ptr->shared_count == 0) {
srs_freep(payload_ptr);
} else {
--payload_ptr->shared_count;
}
}
}
srs_error_t SrsRtpSharedPacket::create(int64_t t, uint16_t seq, uint32_t sc, uint16_t pt, char* p, int s)
{
srs_error_t err = srs_success;
if (size < 0) {
return srs_error_new(ERROR_RTP_PACKET_CREATE, "create packet size=%d", size);
}
srs_assert(!payload_ptr);
timestamp = t;
sequence = seq;
ssrc = sc;
payload_type = pt;
payload_ptr = new SrsRtpSharedPacketPayload();
payload_ptr->payload = p;
payload_ptr->size = s;
payload = payload_ptr->payload;
size = payload_ptr->size;
return err;
}
SrsRtpSharedPacket* SrsRtpSharedPacket::copy()
{
SrsRtpSharedPacket* copy = new SrsRtpSharedPacket();
copy->payload_ptr = payload_ptr;
payload_ptr->shared_count++;
copy->payload = payload;
copy->size = size;
copy->timestamp = timestamp;
copy->sequence = sequence;
copy->ssrc = ssrc;
copy->payload_type = payload_type;
return copy;
}

@ -0,0 +1,62 @@
/**
* 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_KERNEL_RTP_HPP
#define SRS_KERNEL_RTP_HPP
#include <srs_core.hpp>
#include <string>
class SrsRtpSharedPacket
{
private:
class SrsRtpSharedPacketPayload
{
public:
char* payload;
int size;
int shared_count;
public:
SrsRtpSharedPacketPayload();
virtual ~SrsRtpSharedPacketPayload();
};
private:
SrsRtpSharedPacketPayload* payload_ptr;
public:
char* payload;
int size;
public:
int64_t timestamp;
uint16_t sequence;
uint32_t ssrc;
uint16_t payload_type;
public:
SrsRtpSharedPacket();
virtual ~SrsRtpSharedPacket();
public:
srs_error_t create(int64_t t, uint16_t seq, uint32_t sc, uint16_t pt, char* p, int s);
SrsRtpSharedPacket* copy();
};
#endif

@ -0,0 +1,273 @@
#include <srs_stun_stack.hpp>
using namespace std;
#include <openssl/dh.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/ssl.h>
#include <srs_core_autofree.hpp>
#include <srs_kernel_buffer.hpp>
#include <srs_kernel_log.hpp>
#include <srs_kernel_utility.hpp>
static string dump_string_hex(const std::string& str, const int& max_len = 128)
{
char buf[1024*16];
int len = 0;
for (int i = 0; i < str.size() && i < max_len; ++i) {
int nb = snprintf(buf + len, sizeof(buf) - len - 1, "%02X ", (uint8_t)str[i]);
if (nb <= 0)
break;
len += nb;
}
buf[len] = '\0';
return string(buf, len);
}
static srs_error_t hmac_encode(const std::string& algo, const char* key, const int& key_length,
const char* input, const int input_length, char* output, unsigned int& output_length)
{
srs_error_t err = srs_success;
const EVP_MD* engine = NULL;
if (algo == "sha512") {
engine = EVP_sha512();
} else if(algo == "sha256") {
engine = EVP_sha256();
} else if(algo == "sha1") {
engine = EVP_sha1();
} else if(algo == "md5") {
engine = EVP_md5();
} else if(algo == "sha224") {
engine = EVP_sha224();
} else if(algo == "sha384") {
engine = EVP_sha384();
} else {
return srs_error_wrap(err, "unknown algo=%s", algo.c_str());
}
HMAC_CTX* ctx = HMAC_CTX_new();
if (HMAC_Init_ex(ctx, key, key_length, engine, NULL) < 0) {
HMAC_CTX_free(ctx);
return srs_error_wrap(err, "hmac init faied");
}
if (HMAC_Update(ctx, (const unsigned char*)input, input_length) < 0) {
HMAC_CTX_free(ctx);
return srs_error_wrap(err, "hmac update faied");
}
if (HMAC_Final(ctx, (unsigned char*)output, &output_length) < 0) {
HMAC_CTX_free(ctx);
return srs_error_wrap(err, "hmac final faied");
}
HMAC_CTX_free(ctx);
return err;
}
SrsStunPacket::SrsStunPacket()
{
message_type = 0;
local_ufrag = "";
remote_ufrag = "";
}
SrsStunPacket::~SrsStunPacket()
{
}
srs_error_t SrsStunPacket::decode(const char* buf, const int nb_buf)
{
srs_error_t err = srs_success;
SrsBuffer* stream = new SrsBuffer(const_cast<char*>(buf), nb_buf);
SrsAutoFree(SrsBuffer, stream);
if (stream->left() < 20) {
return srs_error_wrap(err, "invalid stun packet, size=%d", stream->size());
}
srs_trace("stun packet, nb_buf=%d", nb_buf);
message_type = stream->read_2bytes();
uint16_t message_len = stream->read_2bytes();
string magic_cookie = stream->read_string(4);
transcation_id = stream->read_string(12);
srs_trace("message_type=%u, message_len=%u, magic_cookie=%s, transcation_id=%s",
message_type, message_len, magic_cookie.c_str(), transcation_id.c_str());
if (nb_buf != 20 + message_len) {
return srs_error_wrap(err, "invalid stun packet, message_len=%d, nb_buf=%d", message_len, nb_buf);
}
while (stream->left() >= 4) {
uint16_t type = stream->read_2bytes();
uint16_t len = stream->read_2bytes();
srs_trace("type=%u, len=%u", type, len);
if (stream->left() < len) {
return srs_error_wrap(err, "invalid stun packet");
}
string val = stream->read_string(len);
// padding
if (len % 4 != 0) {
stream->read_string(4 - (len % 4));
}
//srs_trace("val=%s", val.c_str());
switch (type) {
// FIXME: enum
case 6: {
username = val;
size_t p = val.find(":");
if (p != string::npos) {
local_ufrag = val.substr(0, p);
remote_ufrag = val.substr(p + 1);
srs_trace("stun packet local_ufrag=%s, remote_ufrag=%s", local_ufrag.c_str(), remote_ufrag.c_str());
}
break;
}
default: {
break;
}
}
}
return err;
}
srs_error_t SrsStunPacket::encode(const string& pwd, SrsBuffer* stream)
{
srs_error_t err = srs_success;
if (is_binding_response()) {
return encode_binding_response(pwd, stream);
}
return srs_error_wrap(err, "unknown stun type=%d", get_message_type());
}
// FIXME: make this function easy to read
srs_error_t SrsStunPacket::encode_binding_response(const string& pwd, SrsBuffer* stream)
{
srs_error_t err = srs_success;
string property_username = encode_username();
string mapped_address = encode_mapped_address();
stream->write_2bytes(BindingResponse);
stream->write_2bytes(property_username.size() + mapped_address.size());
stream->write_4bytes(0x2112A442);
stream->write_string(transcation_id);
stream->write_string(property_username);
stream->write_string(mapped_address);
stream->data()[2] = ((stream->pos() - 20 + 20 + 4) & 0x0000FF00) >> 8;
stream->data()[3] = ((stream->pos() - 20 + 20 + 4) & 0x000000FF);
char hmac_buf[20] = {0};
unsigned int hmac_buf_len = 0;
if (hmac_encode("sha1", pwd.c_str(), pwd.size(), stream->data(), stream->pos(), hmac_buf, hmac_buf_len) != srs_success) {
return srs_error_wrap(err, "hmac encode failed");
}
string hmac = encode_hmac(hmac_buf, hmac_buf_len);
stream->write_string(hmac);
stream->data()[2] = ((stream->pos() - 20 + 8) & 0x0000FF00) >> 8;
stream->data()[3] = ((stream->pos() - 20 + 8) & 0x000000FF);
uint32_t crc32 = srs_crc32_ieee(stream->data(), stream->pos(), 0) ^ 0x5354554E;
string fingerprint = encode_fingerprint(crc32);
stream->write_string(fingerprint);
stream->data()[2] = ((stream->pos() - 20) & 0x0000FF00) >> 8;
stream->data()[3] = ((stream->pos() - 20) & 0x000000FF);
return err;
}
string SrsStunPacket::encode_username()
{
char buf[1460];
SrsBuffer* stream = new SrsBuffer(buf, sizeof(buf));
SrsAutoFree(SrsBuffer, stream);
string username = remote_ufrag + ":" + local_ufrag;
stream->write_2bytes(Username);
stream->write_2bytes(username.size());
stream->write_string(username);
if (stream->pos() % 4 != 0) {
static char padding[4] = {0};
stream->write_bytes(padding, 4 - (stream->pos() % 4));
}
return string(stream->data(), stream->pos());
}
string SrsStunPacket::encode_mapped_address()
{
char buf[1460];
SrsBuffer* stream = new SrsBuffer(buf, sizeof(buf));
SrsAutoFree(SrsBuffer, stream);
uint32_t magic_cookie = 0x2112A442;
#if 1
stream->write_2bytes(XorMappedAddress);
stream->write_2bytes(8);
stream->write_1bytes(0); // ignore this bytes
stream->write_1bytes(1); // ipv4 family
stream->write_2bytes(mapped_port ^ (magic_cookie >> 16));
stream->write_4bytes(mapped_address ^ magic_cookie);
#else
stream->write_2bytes(MappedAddress);
stream->write_2bytes(8);
stream->write_1bytes(0); // ignore this bytes
stream->write_1bytes(1); // ipv4 family
stream->write_2bytes(mapped_port);
stream->write_4bytes(mapped_address);
#endif
return string(stream->data(), stream->pos());
}
string SrsStunPacket::encode_hmac(char* hmac_buf, const int hmac_buf_len)
{
char buf[1460];
SrsBuffer* stream = new SrsBuffer(buf, sizeof(buf));
SrsAutoFree(SrsBuffer, stream);
stream->write_2bytes(MessageIntegrity);
stream->write_2bytes(hmac_buf_len);
stream->write_bytes(hmac_buf, hmac_buf_len);
return string(stream->data(), stream->pos());
}
string SrsStunPacket::encode_fingerprint(uint32_t crc32)
{
char buf[1460];
SrsBuffer* stream = new SrsBuffer(buf, sizeof(buf));
SrsAutoFree(SrsBuffer, stream);
stream->write_2bytes(Fingerprint);
stream->write_2bytes(4);
stream->write_4bytes(crc32);
return string(stream->data(), stream->pos());
}

@ -0,0 +1,111 @@
/**
* 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_PROTOCOL_STUN_HPP
#define SRS_PROTOCOL_STUN_HPP
#include <string>
#include <srs_core.hpp>
#include <srs_kernel_error.hpp>
class SrsBuffer;
enum SrsStunMessageType
{
// see @ https://tools.ietf.org/html/rfc3489#section-11.1
BindingRequest = 0x0001,
BindingResponse = 0x0101,
BindingErrorResponse = 0x0111,
SharedSecretRequest = 0x0002,
SharedSecretResponse = 0x0102,
SharedSecretErrorResponse = 0x0112,
};
enum SrsStunMessageAttribute
{
// see @ https://tools.ietf.org/html/rfc3489#section-11.2
MappedAddress = 0x0001,
ResponseAddress = 0x0002,
ChangeRequest = 0x0003,
SourceAddress = 0x0004,
ChangedAddress = 0x0005,
Username = 0x0006,
Password = 0x0007,
MessageIntegrity = 0x0008,
ErrorCode = 0x0009,
UnknownAttributes = 0x000A,
ReflectedFrom = 0x000B,
// see @ https://tools.ietf.org/html/rfc5389#section-18.2
Realm = 0x0014,
Nonce = 0x0015,
XorMappedAddress = 0x0020,
Software = 0x8022,
AlternateServer = 0x8023,
Fingerprint = 0x8028,
};
class SrsStunPacket
{
private:
uint16_t message_type;
std::string username;
std::string local_ufrag;
std::string remote_ufrag;
std::string transcation_id;
uint32_t mapped_address;
uint16_t mapped_port;
public:
SrsStunPacket();
virtual ~SrsStunPacket();
bool is_binding_request() const { return message_type == BindingRequest; }
bool is_binding_response() const { return message_type == BindingResponse; }
uint16_t get_message_type() const { return message_type; }
std::string get_username() const { return username; }
std::string get_local_ufrag() const { return local_ufrag; }
std::string get_remote_ufrag() const { return remote_ufrag; }
std::string get_transcation_id() const { return transcation_id; }
uint32_t get_mapped_address() const { return mapped_address; }
uint16_t get_mapped_port() const { return mapped_port; }
void set_message_type(const uint16_t& m) { message_type = m; }
void set_local_ufrag(const std::string& u) { local_ufrag = u; }
void set_remote_ufrag(const std::string& u) { remote_ufrag = u; }
void set_transcation_id(const std::string& t) { transcation_id = t; }
void set_mapped_address(const uint32_t& addr) { mapped_address = addr; }
void set_mapped_port(const uint32_t& port) { mapped_port = port; }
srs_error_t decode(const char* buf, const int nb_buf);
srs_error_t encode(const std::string& pwd, SrsBuffer* stream);
private:
srs_error_t encode_binding_response(const std::string& pwd, SrsBuffer* stream);
std::string encode_username();
std::string encode_mapped_address();
std::string encode_hmac(char* hamc_buf, const int hmac_buf_len);
std::string encode_fingerprint(uint32_t crc32);
};
#endif

@ -397,6 +397,16 @@ int srs_recvfrom(srs_netfd_t stfd, void *buf, int len, struct sockaddr *from, in
return st_recvfrom((st_netfd_t)stfd, buf, len, from, fromlen, (st_utime_t)timeout);
}
int srs_sendto(srs_netfd_t stfd, void *buf, int len, const struct sockaddr * to, int tolen, srs_utime_t timeout)
{
return st_sendto((st_netfd_t)stfd, buf, len, to, tolen, (st_utime_t)timeout);
}
int srs_sendmsg(srs_netfd_t stfd, const struct msghdr *msg, int flags, srs_utime_t timeout)
{
return st_sendmsg((st_netfd_t)stfd, msg, flags, (st_utime_t)timeout);
}
srs_netfd_t srs_accept(srs_netfd_t stfd, struct sockaddr *addr, int *addrlen, srs_utime_t timeout)
{
return (srs_netfd_t)st_accept((st_netfd_t)stfd, addr, addrlen, (st_utime_t)timeout);

@ -88,6 +88,8 @@ extern srs_netfd_t srs_netfd_open_socket(int osfd);
extern srs_netfd_t srs_netfd_open(int osfd);
extern int srs_recvfrom(srs_netfd_t stfd, void *buf, int len, struct sockaddr *from, int *fromlen, srs_utime_t timeout);
extern int srs_sendto(srs_netfd_t stfd, void *buf, int len, const struct sockaddr *to, int tolen, srs_utime_t timeout);
extern int srs_sendmsg(srs_netfd_t stfd, const struct msghdr *msg, int flags, srs_utime_t timeout);
extern srs_netfd_t srs_accept(srs_netfd_t stfd, struct sockaddr *addr, int *addrlen, srs_utime_t timeout);

Loading…
Cancel
Save