SRT improvements:

- Made SRT support optional
- Make build options visible in cmake-gui
- Improved generic connection stats for outputs
- Added streamid handling configuration for MistInTSSRT
- Push input support over SRT
- Fixed support for SRT settings in push outputs
- Fix parsing of SRT-passed stream names
- Fixed hostnames in MistOutTSSRT, fixed PUSH_REWRITE trigger payload
- Opus support in TS-SRT
- Fixed SRT socket stats, fixed SRT socket address logic, improved SRT socket rolling restart support
- Fixed SRT push deny
This commit is contained in:
Thulinma 2020-08-28 00:42:38 +02:00
parent 19199cbff8
commit 0bd5d742f6
19 changed files with 686 additions and 347 deletions

View file

@ -3,7 +3,6 @@
#include "config.h"
#include "defines.h"
#include "lib/socket_srt.h"
#include "stream.h"
#include "timing.h"
#include "tinythread.h"
@ -39,7 +38,6 @@
bool Util::Config::is_active = false;
bool Util::Config::is_restarting = false;
static Socket::Server *serv_sock_pointer = 0;
static Socket::SRTServer *serv_srt_sock_pointer = 0; ///< Holds a pointer to SRT Server, if it is connected
uint32_t Util::printDebugLevel = DEBUG;
std::string Util::streamName;
char Util::exitReason[256] ={0};
@ -55,13 +53,6 @@ void Util::logExitReason(const char *format, ...){
std::string Util::listenInterface;
uint32_t Util::listenPort = 0;
// Sets pointer to the SRT Server, for proper cleanup later.
//
// Currently used for TSSRT Input only, as this doesn't use the config library to setup a listener
void Util::Config::registerSRTSockPtr(Socket::SRTServer *ptr){
serv_srt_sock_pointer = ptr;
}
Util::Config::Config(){
// global options here
vals["debug"]["long"] = "debug";
@ -331,23 +322,6 @@ struct callbackData{
int (*cb)(Socket::Connection &);
};
// As above, but using an SRT Connection
struct callbackSRTData{
Socket::SRTConnection *sock;
int (*cb)(Socket::SRTConnection &);
};
// Callback for SRT-serving threads
static void callThreadCallbackSRT(void *cDataArg){
INSANE_MSG("Thread for %p started", cDataArg);
callbackSRTData *cData = (callbackSRTData *)cDataArg;
cData->cb(*(cData->sock));
cData->sock->close();
delete cData->sock;
delete cData;
INSANE_MSG("Thread for %p ended", cDataArg);
}
static void callThreadCallback(void *cDataArg){
INSANE_MSG("Thread for %p started", cDataArg);
callbackData *cData = (callbackData *)cDataArg;
@ -430,53 +404,6 @@ int Util::Config::serveThreadedSocket(int (*callback)(Socket::Connection &)){
return r;
}
// This is a THREADED server!! Fork does not work as the SRT library itself already starts up a
// thread, and forking after thread creation messes up all control flow internal to the library.
int Util::Config::serveSRTSocket(int (*callback)(Socket::SRTConnection &S)){
Socket::SRTServer server_socket;
if (vals.isMember("port") && vals.isMember("interface")){
server_socket = Socket::SRTServer(getInteger("port"), getString("interface"), false, "output");
}
if (!server_socket.connected()){
DEVEL_MSG("Failure to open socket");
return 1;
}
serv_srt_sock_pointer = &server_socket;
activate();
if (server_socket.getSocket()){
int oldSock = server_socket.getSocket();
if (!dup2(oldSock, 0)){
server_socket = Socket::SRTServer(0);
close(oldSock);
}
}
int r = SRTServer(server_socket, callback);
serv_srt_sock_pointer = 0;
return r;
}
int Util::Config::SRTServer(Socket::SRTServer &server_socket, int (*callback)(Socket::SRTConnection &)){
Util::Procs::socketList.insert(server_socket.getSocket());
while (is_active && server_socket.connected()){
Socket::SRTConnection S = server_socket.accept(false, "output");
if (S.connected()){// check if the new connection is valid
callbackSRTData *cData = new callbackSRTData;
cData->sock = new Socket::SRTConnection(S);
cData->cb = callback;
// spawn a new thread for this connection
tthread::thread T(callThreadCallbackSRT, (void *)cData);
// detach it, no need to keep track of it anymore
T.detach();
HIGH_MSG("Spawned new thread for socket %i", S.getSocket());
}else{
Util::sleep(10); // sleep 10ms
}
}
Util::Procs::socketList.erase(server_socket.getSocket());
if (!is_restarting){server_socket.close();}
return 0;
}
int Util::Config::serveForkedSocket(int (*callback)(Socket::Connection &S)){
Socket::Server server_socket;
if (Socket::checkTrueSocket(0)){
@ -541,8 +468,6 @@ void Util::Config::signal_handler(int signum, siginfo_t *sigInfo, void *ignore){
case SIGHUP:
case SIGTERM:
if (serv_sock_pointer){serv_sock_pointer->close();}
// Close the srt server as well, if set
if (serv_srt_sock_pointer){serv_srt_sock_pointer->close();}
#if DEBUG >= DLVL_DEVEL
static int ctr = 0;
if (!is_active && ++ctr > 4){BACKTRACE;}

View file

@ -8,7 +8,6 @@
#endif
#include "json.h"
#include "socket_srt.h"
#include <signal.h>
#include <string>
@ -42,7 +41,6 @@ namespace Util{
int64_t getInteger(std::string optname);
bool getBool(std::string optname);
void activate();
void registerSRTSockPtr(Socket::SRTServer *ptr);
int threadServer(Socket::Server &server_socket, int (*callback)(Socket::Connection &S));
int forkServer(Socket::Server &server_socket, int (*callback)(Socket::Connection &S));
int serveThreadedSocket(int (*callback)(Socket::Connection &S));
@ -51,9 +49,6 @@ namespace Util{
void addOptionsFromCapabilities(const JSON::Value &capabilities);
void addBasicConnectorOptions(JSON::Value &capabilities);
void addConnectorOptions(int port, JSON::Value &capabilities);
int serveSRTSocket(int (*callback)(Socket::SRTConnection &S));
int SRTServer(Socket::SRTServer &server_socket, int (*callback)(Socket::SRTConnection &S));
};
/// The interface address the current serveSocket function is listening on

View file

@ -1,6 +1,8 @@
#include "defines.h"
#include "lib/http_parser.h"
#include "socket_srt.h"
#include "json.h"
#include "timing.h"
#include <cstdlib>
#include <sstream>
@ -69,13 +71,20 @@ namespace Socket{
return interpretSRTMode(params.count("mode") ? params.at("mode") : "default", u.host, "");
}
SRTConnection::SRTConnection(){initializeEmpty();}
SRTConnection::SRTConnection(){
initializeEmpty();
lastGood = Util::bootMS();
}
SRTConnection::SRTConnection(const std::string &_host, int _port, const std::string &_direction,
const std::map<std::string, std::string> &_params){
connect(_host, _port, _direction, _params);
}
SRTConnection::SRTConnection(SRTSOCKET alreadyConnected){
sock = alreadyConnected;
}
std::string SRTConnection::getStreamName(){
int sNameLen = 512;
char sName[sNameLen];
@ -84,29 +93,89 @@ namespace Socket{
return "";
}
/// Updates the downbuffer internal variable.
/// Returns true if new data was received, false otherwise.
std::string SRTConnection::RecvNow(){
char recvbuf[5000];
std::string SRTConnection::getBinHost(){
char tmpBuffer[17] = "\000\000\000\000\000\000\000\000\000\000\377\377\000\000\000\000";
switch (remoteaddr.sin6_family){
case AF_INET:
memcpy(tmpBuffer + 12, &(reinterpret_cast<const sockaddr_in *>(&remoteaddr)->sin_addr.s_addr), 4);
break;
case AF_INET6: memcpy(tmpBuffer, &(remoteaddr.sin6_addr.s6_addr), 16); break;
default: return ""; break;
}
return std::string(tmpBuffer, 16);
}
size_t SRTConnection::RecvNow(){
bool blockState = blocking;
setBlocking(true);
if (!blockState){setBlocking(true);}
SRT_MSGCTRL mc = srt_msgctrl_default;
int32_t receivedBytes = srt_recvmsg2(sock, recvbuf, 5000, &mc);
if (prev_pktseq != 0 && (mc.pktseq - prev_pktseq > 1)){WARN_MSG("Packet lost");}
//if (prev_pktseq != 0 && (mc.pktseq - prev_pktseq > 1)){WARN_MSG("Packet lost");}
prev_pktseq = mc.pktseq;
setBlocking(blockState);
if (!blockState){setBlocking(blockState);}
if (receivedBytes == -1){
int err = srt_getlasterror(0);
if (err == SRT_ECONNLOST){
close();
return 0;
}
if (err == SRT_ENOCONN){
if (Util::bootMS() > lastGood + 5000){
ERROR_MSG("SRT connection timed out - closing");
close();
}
return 0;
}
ERROR_MSG("Unable to receive data over socket: %s", srt_getlasterror_str());
if (srt_getsockstate(sock) != SRTS_CONNECTED){close();}
return "";
return 0;
}
if (receivedBytes == 0){
close();
}else{
lastGood = Util::bootMS();
}
srt_bstats(sock, &performanceMonitor, false);
return std::string(recvbuf, receivedBytes);
return receivedBytes;
}
///Attempts a read, obeying the current blocking setting.
///May result in socket being disconnected when connection was lost during read.
///Returns amount of bytes actually read
size_t SRTConnection::Recv(){
SRT_MSGCTRL mc = srt_msgctrl_default;
int32_t receivedBytes = srt_recvmsg2(sock, recvbuf, 5000, &mc);
prev_pktseq = mc.pktseq;
if (receivedBytes == -1){
int err = srt_getlasterror(0);
if (err == SRT_EASYNCRCV){return 0;}
if (err == SRT_ECONNLOST){
close();
return 0;
}
if (err == SRT_ENOCONN){
if (Util::bootMS() > lastGood + 5000){
ERROR_MSG("SRT connection timed out - closing");
close();
}
return 0;
}
ERROR_MSG("Unable to receive data over socket: %s", srt_getlasterror_str());
if (srt_getsockstate(sock) != SRTS_CONNECTED){close();}
return 0;
}
if (receivedBytes == 0){
close();
}else{
lastGood = Util::bootMS();
}
srt_bstats(sock, &performanceMonitor, false);
return receivedBytes;
}
void SRTConnection::connect(const std::string &_host, int _port, const std::string &_direction,
@ -143,6 +212,7 @@ namespace Socket{
HIGH_MSG("Going to connect sock %d", sock);
if (srt_connect(sock, psa, sizeof sa) == SRT_ERROR){
srt_close(sock);
sock = -1;
ERROR_MSG("Can't connect SRT Socket");
return;
}
@ -153,6 +223,7 @@ namespace Socket{
return;
}
INFO_MSG("Caller SRT socket %" PRId32 " success targetting %s:%u", sock, _host.c_str(), _port);
lastGood = Util::bootMS();
return;
}
if (modeName == "listener"){
@ -163,14 +234,17 @@ namespace Socket{
if (srt_bind(sock, psa, sizeof sa) == SRT_ERROR){
srt_close(sock);
ERROR_MSG("Can't connect SRT Socket");
sock = -1;
ERROR_MSG("Can't connect SRT Socket: %s", srt_getlasterror_str());
return;
}
if (srt_listen(sock, 1) == SRT_ERROR){
srt_close(sock);
sock = -1;
ERROR_MSG("Can not listen on Socket");
}
INFO_MSG("Listener SRT socket sucess @ %s:%u", _host.c_str(), _port);
lastGood = Util::bootMS();
return;
}
if (modeName == "rendezvous"){
@ -182,6 +256,7 @@ namespace Socket{
if (srt_bind(sock, psa, sizeof sa) == SRT_ERROR){
srt_close(sock);
sock = -1;
ERROR_MSG("Can't connect SRT Socket");
return;
}
@ -191,6 +266,7 @@ namespace Socket{
if (srt_connect(sock, psb, sizeof sb) == SRT_ERROR){
srt_close(sock);
sock = -1;
ERROR_MSG("Can't connect SRT Socket");
return;
}
@ -200,6 +276,7 @@ namespace Socket{
return;
}
INFO_MSG("Rendezvous SRT socket sucess @ %s:%u", _host.c_str(), _port);
lastGood = Util::bootMS();
return;
}
ERROR_MSG("Invalid mode parameter. Use 'client' or 'server'");
@ -220,8 +297,23 @@ namespace Socket{
int res = srt_sendmsg2(sock, data, len, NULL);
if (res == SRT_ERROR){
int err = srt_getlasterror(0);
//Do not report normal connection lost errors
if (err == SRT_ECONNLOST){
close();
return;
}
if (err == SRT_ENOCONN){
if (Util::bootMS() > lastGood + 5000){
ERROR_MSG("SRT connection timed out - closing");
close();
}
return;
}
ERROR_MSG("Unable to send data over socket %" PRId32 ": %s", sock, srt_getlasterror_str());
if (srt_getsockstate(sock) != SRTS_CONNECTED){close();}
}else{
lastGood = Util::bootMS();
}
srt_bstats(sock, &performanceMonitor, false);
}
@ -236,16 +328,16 @@ namespace Socket{
uint64_t SRTConnection::dataDown(){return performanceMonitor.byteRecvTotal;}
uint64_t SRTConnection::packetCount(){
return (direction == "input" ? performanceMonitor.pktRecvTotal : performanceMonitor.pktSentTotal);
return (direction == "output" ? performanceMonitor.pktSentTotal : performanceMonitor.pktRecvTotal);
}
uint64_t SRTConnection::packetLostCount(){
return (direction == "input" ? performanceMonitor.pktRcvLossTotal : performanceMonitor.pktSndLossTotal);
return (direction == "output" ? performanceMonitor.pktSndLossTotal : performanceMonitor.pktRcvLossTotal);
}
uint64_t SRTConnection::packetRetransmitCount(){
//\todo This should be updated with pktRcvRetransTotal on the retrieving end once srt has implemented this.
return (direction == "input" ? 0 : performanceMonitor.pktRetransTotal);
return (direction == "output" ? performanceMonitor.pktRetransTotal : 0);
}
void SRTConnection::initializeEmpty(){
@ -259,10 +351,8 @@ namespace Socket{
void SRTConnection::setBlocking(bool _blocking){
if (_blocking == blocking){return;}
// If we have an error setting the new blocking state, the state is unchanged so we return early.
if (srt_setsockopt(sock, 0, (direction == "output" ? SRTO_SNDSYN : SRTO_RCVSYN), &_blocking,
sizeof _blocking) == -1){
return;
}
if (srt_setsockopt(sock, 0, SRTO_SNDSYN, &_blocking, sizeof _blocking) == -1){return;}
if (srt_setsockopt(sock, 0, SRTO_RCVSYN, &_blocking, sizeof _blocking) == -1){return;}
blocking = _blocking;
}
@ -289,7 +379,7 @@ namespace Socket{
if (adapter == "" && modeName == "listener"){adapter = _host;}
tsbpdMode = ((params.count("tsbpd") && isFalseString(params.at("tsbpd"))) ? false : true);
tsbpdMode = (params.count("tsbpd") && JSON::Value(params.at("tsbpd")).asBool());
outgoing_port = (params.count("port") ? strtol(params.at("port").c_str(), 0, 0) : 0);
@ -332,14 +422,11 @@ namespace Socket{
int SRTConnection::postConfigureSocket(){
bool no = false;
if (srt_setsockopt(sock, 0, (direction == "output" ? SRTO_SNDSYN : SRTO_RCVSYN), &no, sizeof no) == -1){
return -1;
}
if (srt_setsockopt(sock, 0, SRTO_SNDSYN, &no, sizeof no) == -1){return -1;}
if (srt_setsockopt(sock, 0, SRTO_RCVSYN, &no, sizeof no) == -1){return -1;}
if (timeout){
if (srt_setsockopt(sock, 0, (direction == "output" ? SRTO_SNDTIMEO : SRTO_RCVTIMEO), &timeout,
sizeof timeout) == -1){
return -1;
}
if (srt_setsockopt(sock, 0, SRTO_SNDTIMEO, &timeout, sizeof timeout) == -1){return -1;}
if (srt_setsockopt(sock, 0, SRTO_RCVTIMEO, &timeout, sizeof timeout) == -1){return -1;}
}
std::string errMsg = configureSocketLoop(SRT::SockOpt::POST);
if (errMsg.size()){
@ -455,15 +542,7 @@ namespace Socket{
}
}break;
case SRT::SockOpt::BOOL:{
bool tmp;
if (isFalseString(v)){
tmp = true;
}else if (isTrueString(v)){
tmp = true;
}else{
return false;
}
val.b = tmp;
val.b = JSON::Value(v).asBool();
val.value = &val.b;
val.size = sizeof val.b;
}break;

View file

@ -1,11 +1,8 @@
#pragma once
#include "socket.h"
#include "url.h"
#include <map>
#include <string>
#include <srt/srt.h>
typedef std::map<std::string, int> SockOptVals;
@ -13,15 +10,6 @@ typedef std::map<std::string, std::string> paramList;
namespace Socket{
std::string interpretSRTMode(const HTTP::URL &u);
inline bool isFalseString(const std::string &_val){
return _val == "0" || _val == "no" || _val == "off" || _val == "false";
}
inline bool isTrueString(const std::string &_val){
return _val == "1" || _val == "yes" || _val == "on" || _val == "true";
}
sockaddr_in createInetAddr(const std::string &_host, int _port);
namespace SRT{
@ -39,7 +27,7 @@ namespace Socket{
class SRTConnection{
public:
SRTConnection();
SRTConnection(SRTSOCKET alreadyConnected){sock = alreadyConnected;}
SRTConnection(SRTSOCKET alreadyConnected);
SRTConnection(const std::string &_host, int _port, const std::string &_direction = "input",
const paramList &_params = paramList());
@ -52,7 +40,10 @@ namespace Socket{
void setBlocking(bool blocking); ///< Set this socket to be blocking (true) or nonblocking (false).
bool isBlocking(); ///< Check if this socket is blocking (true) or nonblocking (false).
std::string RecvNow();
size_t RecvNow();
size_t Recv();
char recvbuf[5000]; ///< Buffer where received data is stored in
void SendNow(const std::string &data);
void SendNow(const char *data, size_t len);
@ -73,7 +64,7 @@ namespace Socket{
struct sockaddr_in6 remoteaddr;
std::string remotehost;
std::string getBinHost();
private:
SRTSOCKET sock;
CBytePerfMon performanceMonitor;
@ -81,10 +72,11 @@ namespace Socket{
std::string host;
int outgoing_port;
int32_t prev_pktseq;
uint64_t lastGood;
uint32_t chunkTransmitSize;
// From paramaeter parsing
// From parameter parsing
std::string adapter;
std::string modeName;
int timeout;
@ -100,7 +92,7 @@ namespace Socket{
bool blocking;
};
/// This class is for easily setting up listening socket, either TCP or Unix.
/// This class is for easily setting up a listening SRT socket
class SRTServer{
public:
SRTServer();
@ -130,7 +122,6 @@ namespace Socket{
class SocketOption{
public:
//{"enforcedencryption", 0, SRTO_ENFORCEDENCRYPTION, SRT::SockOpt::PRE, SRT::SockOpt::BOOL, nullptr},
SocketOption(const std::string &_name, int _protocol, int _symbol, SRT::SockOpt::Binding _binding,
SRT::SockOpt::Type _type, const SockOptVals &_values = SockOptVals())
: name(_name), protocol(_protocol), symbol(_symbol), binding(_binding), type(_type),
@ -142,11 +133,8 @@ namespace Socket{
SRT::SockOpt::Binding binding;
SRT::SockOpt::Type type;
SockOptVals valmap;
bool apply(int socket, const std::string &value, bool isSrtOpt = true);
static int setSo(int socket, int protocol, int symbol, const void *data, size_t size, bool isSrtOpt = true);
bool extract(const std::string &v, OptionValue &val, SRT::SockOpt::Type asType);
};

View file

@ -15,6 +15,51 @@ tthread::recursive_mutex tMutex;
namespace TS{
bool Assembler::assemble(Stream & TSStrm, char * ptr, size_t len){
bool ret = false;
size_t offset = 0;
size_t amount = 188-leftData.size();
if (leftData.size() && len >= amount){
//Attempt to re-assemble a packet from the leftovers of last time + current head
if (len == amount || ptr[amount] == 0x47){
VERYHIGH_MSG("Assembled scrap packet");
//Success!
leftData.append(ptr, amount);
tsBuf.FromPointer(leftData);
TSStrm.add(tsBuf);
ret = true;
if (!TSStrm.isDataTrack(tsBuf.getPID())){TSStrm.parse(tsBuf.getPID());}
offset = amount;
leftData.assign(0,0);
}
//On failure, hope we might live to succeed another day
}
// Try to read full TS Packets
// Watch out! We push here to a global, in order for threads to be able to access it.
size_t junk = 0;
while (offset < len){
if (ptr[offset] == 0x47 && (offset+188 >= len || ptr[offset+188] == 0x47)){// check for sync byte
if (junk){
INFO_MSG("%zu bytes of non-sync-byte data received", junk);
junk = 0;
}
if (offset + 188 <= len){
tsBuf.FromPointer(ptr + offset);
TSStrm.add(tsBuf);
if (!TSStrm.isDataTrack(tsBuf.getPID())){TSStrm.parse(tsBuf.getPID());}
ret = true;
}else{
leftData.assign(ptr + offset, len - offset);
}
offset += 188;
}else{
++junk;
++offset;
}
}
return ret;
}
void ADTSRemainder::setRemainder(const aac::adts &p, const void *source, uint32_t avail, uint64_t bPos){
if (!p.getCompleteSize()){return;}

View file

@ -106,4 +106,13 @@ namespace TS{
void parsePES(size_t tid, bool finished = false);
};
class Assembler{
public:
bool assemble(Stream & TSStrm, char * ptr, size_t len);
private:
Util::ResizeablePointer leftData;
TS::Packet tsBuf;
};
}// namespace TS