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

@ -893,18 +893,21 @@ namespace Mist{
statComm.setCRC(getpid());
statComm.setStream(streamName);
statComm.setConnector("INPUT:" + capa["name"].asStringRef());
statComm.setUp(0);
statComm.setDown(streamByteCount());
statComm.setTime(now - startTime);
statComm.setLastSecond(0);
statComm.setHost(getConnectedBinHost());
handleLossyStats(statComm);
connStats(statComm);
}
statTimer = Util::bootSecs();
}
}
}
void Input::connStats(Comms::Statistics &statComm){
statComm.setUp(0);
statComm.setDown(streamByteCount());
statComm.setHost(getConnectedBinHost());
}
void Input::realtimeMainLoop(){
uint64_t statTimer = 0;

View file

@ -69,11 +69,7 @@ namespace Mist{
virtual void userOnActive(size_t id);
virtual void userOnDisconnect(size_t id);
virtual void userLeadOut();
virtual void handleLossyStats(Comms::Statistics & statComm){}
virtual bool preventBufferStart() {return false;}
virtual void connStats(Comms::Statistics & statComm);
virtual void parseHeader();
bool bufferFrame(size_t track, uint32_t keyNum);

View file

@ -191,6 +191,7 @@ namespace Mist{
inputProcess = 0;
isFinished = false;
#ifndef WITH_SRT
{
pid_t srt_tx = -1;
const char *args[] ={"srt-live-transmit", 0};
@ -199,12 +200,13 @@ namespace Mist{
capa["source_match"].append("srt://*");
capa["always_match"].append("srt://*");
capa["desc"] =
capa["desc"].asStringRef() + " SRT support (srt://*) is installed and available.";
capa["desc"].asStringRef() + " Non-native SRT support (srt://*) is installed and available.";
}else{
capa["desc"] = capa["desc"].asStringRef() +
" To enable SRT support, please install the srt-live-transmit binary.";
" To enable non-native SRT support, please install the srt-live-transmit binary.";
}
}
#endif
capa["optional"]["DVR"]["name"] = "Buffer time (ms)";
capa["optional"]["DVR"]["help"] =
@ -534,43 +536,7 @@ namespace Mist{
gettingData = true;
INFO_MSG("Now receiving UDP data...");
}
size_t offset = 0;
size_t amount = 188-leftData.size();
if (leftData.size() && udpCon.data.size() >= amount){
//Attempt to re-assemble a packet from the leftovers of last time + current head
if (udpCon.data.size() == amount || udpCon.data[amount] == 0x47){
VERYHIGH_MSG("Assembled scrap packet");
//Success!
leftData.append(udpCon.data, amount);
liveStream.add(leftData);
if (!liveStream.isDataTrack(tsBuf.getPID())){liveStream.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 < udpCon.data.size()){
if (udpCon.data[offset] == 0x47 && (offset+188 >= udpCon.data.size() || udpCon.data[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 <= udpCon.data.size()){
tsBuf.FromPointer(udpCon.data + offset);
liveStream.add(tsBuf);
if (!liveStream.isDataTrack(tsBuf.getPID())){liveStream.parse(tsBuf.getPID());}
}else{
leftData.assign(udpCon.data + offset, udpCon.data.size() - offset);
}
offset += 188;
}else{
++junk;
++offset;
}
}
assembler.assemble(liveStream, udpCon.data, udpCon.data.size());
}
if (!received){
Util::sleep(100);

View file

@ -33,7 +33,7 @@ namespace Mist{
void streamMainLoop();
void finish();
FILE *inFile; ///< The input file with ts data
Util::ResizeablePointer leftData;
TS::Assembler assembler;
TS::Stream tsStream; ///< Used for parsing the incoming ts stream
Socket::UDPConnection udpCon;
Socket::Connection tcpCon;

View file

@ -24,9 +24,17 @@
Util::Config *cfgPointer = NULL;
std::string baseStreamName;
Socket::SRTServer sSock;
void (*oldSignal)(int, siginfo_t *,void *) = 0;
void signal_handler(int signum, siginfo_t *sigInfo, void *ignore){
sSock.close();
if (oldSignal){
oldSignal(signum, sigInfo, ignore);
}
}
/// Global, so that all tracks stay in sync
int64_t timeStampOffset = 0;
// We use threads here for multiple input pushes, because of the internals of the SRT Library
static void callThreadCallbackSRT(void *socknum){
@ -54,8 +62,10 @@ namespace Mist{
capa["codecs"][0u][0u].append("HEVC");
capa["codecs"][0u][0u].append("MPEG2");
capa["codecs"][0u][1u].append("AAC");
capa["codecs"][0u][1u].append("MP3");
capa["codecs"][0u][1u].append("AC3");
capa["codecs"][0u][1u].append("MP2");
capa["codecs"][0u][1u].append("opus");
JSON::Value option;
option["arg"] = "integer";
@ -64,6 +74,7 @@ namespace Mist{
option["help"] = "DVR buffer time in ms";
option["value"].append(50000);
config->addOption("bufferTime", option);
option.null();
capa["optional"]["DVR"]["name"] = "Buffer time (ms)";
capa["optional"]["DVR"]["help"] =
"The target available buffer time for this live stream, in milliseconds. This is the time "
@ -73,13 +84,44 @@ namespace Mist{
capa["optional"]["DVR"]["type"] = "uint";
capa["optional"]["DVR"]["default"] = 50000;
option["arg"] = "integer";
option["long"] = "acceptable";
option["short"] = "T";
option["help"] = "Acceptable pushed streamids (0 = use streamid as wildcard, 1 = ignore all streamids, 2 = disallow non-matching streamids)";
option["value"].append(0);
config->addOption("acceptable", option);
capa["optional"]["acceptable"]["name"] = "Acceptable pushed streamids";
capa["optional"]["acceptable"]["help"] = "What to do with the streamids for incoming pushes, if this is a listener SRT connection";
capa["optional"]["acceptable"]["option"] = "--acceptable";
capa["optional"]["acceptable"]["short"] = "T";
capa["optional"]["acceptable"]["default"] = 0;
capa["optional"]["acceptable"]["type"] = "select";
capa["optional"]["acceptable"]["select"][0u][0u] = 0;
capa["optional"]["acceptable"]["select"][0u][1u] = "Set streamid as wildcard";
capa["optional"]["acceptable"]["select"][1u][0u] = 1;
capa["optional"]["acceptable"]["select"][1u][1u] = "Ignore all streamids";
capa["optional"]["acceptable"]["select"][2u][0u] = 2;
capa["optional"]["acceptable"]["select"][2u][1u] = "Disallow non-matching streamid";
// Setup if we are called form with a thread for push-based input.
if (s != -1){
srtConn = Socket::SRTConnection(s);
streamName = baseStreamName;
if (srtConn.getStreamName() != ""){streamName += "+" + srtConn.getStreamName();}
std::string streamid = srtConn.getStreamName();
int64_t acc = config->getInteger("acceptable");
if (acc == 0){
if (streamid.size()){streamName += "+" + streamid;}
}else if(acc == 2){
if (streamName != streamid){
FAIL_MSG("Stream ID '%s' does not match stream name, push blocked", streamid.c_str());
srtConn.close();
}
}
Util::setStreamName(streamName);
}
lastTimeStamp = 0;
timeStampOffset = 0;
singularFlag = true;
}
@ -96,9 +138,27 @@ namespace Mist{
INFO_MSG("Parsed url: %s", u.getUrl().c_str());
if (Socket::interpretSRTMode(u) == "listener"){
sSock = Socket::SRTServer(u.getPort(), u.host, false);
config->registerSRTSockPtr(&sSock);
struct sigaction new_action;
struct sigaction cur_action;
new_action.sa_sigaction = signal_handler;
sigemptyset(&new_action.sa_mask);
new_action.sa_flags = SA_SIGINFO;
sigaction(SIGINT, &new_action, &cur_action);
if (cur_action.sa_sigaction && cur_action.sa_sigaction != oldSignal){
if (oldSignal){WARN_MSG("Multiple signal handlers! I can't deal with this.");}
oldSignal = cur_action.sa_sigaction;
}
sigaction(SIGHUP, &new_action, &cur_action);
if (cur_action.sa_sigaction && cur_action.sa_sigaction != oldSignal){
if (oldSignal){WARN_MSG("Multiple signal handlers! I can't deal with this.");}
oldSignal = cur_action.sa_sigaction;
}
sigaction(SIGTERM, &new_action, &cur_action);
if (cur_action.sa_sigaction && cur_action.sa_sigaction != oldSignal){
if (oldSignal){WARN_MSG("Multiple signal handlers! I can't deal with this.");}
oldSignal = cur_action.sa_sigaction;
}
}else{
INFO_MSG("A");
std::map<std::string, std::string> arguments;
HTTP::parseVars(u.args, arguments);
size_t connectCnt = 0;
@ -117,34 +177,12 @@ namespace Mist{
void inputTSSRT::getNext(size_t idx){
thisPacket.null();
bool hasPacket = tsStream.hasPacket();
bool firstloop = true;
while (!hasPacket && srtConn.connected() && config->is_active){
firstloop = false;
// Receive data from the socket. SRT Sockets handle some internal timing as well, based on the provided settings.
leftBuffer.append(srtConn.RecvNow());
if (leftBuffer.size()){
size_t offset = 0;
size_t garbage = 0;
while ((offset + 188) < leftBuffer.size()){
if (leftBuffer[offset] != 0x47){
++garbage;
if (garbage % 100 == 0){INFO_MSG("Accumulated %zu bytes of garbage", garbage);}
++offset;
continue;
}
if (garbage != 0){
WARN_MSG("Thrown away %zu bytes of garbage data", garbage);
garbage = 0;
}
if (offset + 188 <= leftBuffer.size()){
tsBuf.FromPointer(leftBuffer.data() + offset);
tsStream.parse(tsBuf, 0);
offset += 188;
}
}
leftBuffer.erase(0, offset);
hasPacket = tsStream.hasPacket();
}else if (srtConn.connected()){
while (!hasPacket && srtConn && config->is_active){
size_t recvSize = srtConn.RecvNow();
if (recvSize){
if (assembler.assemble(tsStream, srtConn.recvbuf, recvSize, true)){hasPacket = tsStream.hasPacket();}
}else if (srtConn){
// This should not happen as the SRT socket is read blocking and won't return until there is
// data. But if it does, wait before retry
Util::sleep(10);
@ -153,7 +191,12 @@ namespace Mist{
if (hasPacket){tsStream.getEarliestPacket(thisPacket);}
if (!thisPacket){
INFO_MSG("Could not getNext TS packet!");
if (srtConn){
INFO_MSG("Could not getNext TS packet!");
Util::logExitReason("internal TS parser error");
}else{
Util::logExitReason("SRT connection close");
}
return;
}
@ -208,7 +251,10 @@ namespace Mist{
void inputTSSRT::setSingular(bool newSingular){singularFlag = newSingular;}
void inputTSSRT::handleLossyStats(Comms::Statistics &statComm){
void inputTSSRT::connStats(Comms::Statistics &statComm){
statComm.setUp(srtConn.dataUp());
statComm.setDown(srtConn.dataDown());
statComm.setHost(getConnectedBinHost());
statComm.setPacketCount(srtConn.packetCount());
statComm.setPacketLostCount(srtConn.packetLostCount());
statComm.setPacketRetransmitCount(srtConn.packetRetransmitCount());

View file

@ -8,13 +8,17 @@
#include <string>
namespace Mist{
/// This class contains all functions needed to implement TS Input
class inputTSSRT : public Input{
public:
inputTSSRT(Util::Config *cfg, SRTSOCKET s = -1);
~inputTSSRT();
void setSingular(bool newSingular);
virtual bool needsLock();
virtual std::string getConnectedBinHost(){
if (srtConn){return srtConn.getBinHost();}
return Input::getConnectedBinHost();
}
protected:
// Private Functions
@ -22,7 +26,6 @@ namespace Mist{
bool preRun();
virtual void getNext(size_t idx = INVALID_TRACK_ID);
virtual bool needHeader(){return false;}
virtual bool preventBufferStart(){return srtConn.getSocket() == -1;}
virtual bool isSingular(){return singularFlag;}
virtual bool isThread(){return !singularFlag;}
@ -31,17 +34,14 @@ namespace Mist{
void streamMainLoop();
TS::Stream tsStream; ///< Used for parsing the incoming ts stream
TS::Packet tsBuf;
std::string leftBuffer;
TS::Assembler assembler;
int64_t timeStampOffset;
uint64_t lastTimeStamp;
Socket::SRTServer sSock;
Socket::SRTConnection srtConn;
bool singularFlag;
size_t tmpIdx;
virtual size_t streamByteCount(){
return srtConn.dataDown();
}; // For live streams: to update the stats with correct values.
virtual void handleLossyStats(Comms::Statistics &statComm);
virtual void connStats(Comms::Statistics &statComm);
};
}// namespace Mist