mirror of https://github.com/ossrs/srs.git
Merge branch 'rtc' of https://github.com/xiaozhihong/srs into xiaozhihong-rtc
commit
d21ef106a0
Binary file not shown.
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>
|
@ -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
|
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
|
@ -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
|
Loading…
Reference in New Issue