TSSRT Support

This commit is contained in:
Phencys 2020-07-26 16:19:14 +02:00 committed by Thulinma
parent 974380ab30
commit 19199cbff8
17 changed files with 1471 additions and 15 deletions

View file

@ -0,0 +1,58 @@
#include OUTPUTTYPE
#include <mist/config.h>
#include <mist/defines.h>
#include <mist/socket.h>
#include <mist/socket_srt.h>
#include <mist/util.h>
int spawnForked(Socket::SRTConnection &S){
int fds[2];
pipe(fds);
Socket::Connection Sconn(fds[0], fds[1]);
mistOut tmp(Sconn, S.getSocket());
return tmp.run();
}
void handleUSR1(int signum, siginfo_t *sigInfo, void *ignore){
HIGH_MSG("USR1 received - triggering rolling restart");
Util::Config::is_restarting = true;
Util::logExitReason("signal USR1");
Util::Config::is_active = false;
}
int main(int argc, char *argv[]){
DTSC::trackValidMask = TRACK_VALID_EXT_HUMAN;
Util::redirectLogsIfNeeded();
Util::Config conf(argv[0]);
mistOut::init(&conf);
if (conf.parseArgs(argc, argv)){
if (conf.getBool("json")){
mistOut::capa["version"] = PACKAGE_VERSION;
std::cout << mistOut::capa.toString() << std::endl;
return -1;
}
conf.activate();
if (mistOut::listenMode()){
{
struct sigaction new_action;
new_action.sa_sigaction = handleUSR1;
sigemptyset(&new_action.sa_mask);
new_action.sa_flags = 0;
sigaction(SIGUSR1, &new_action, NULL);
}
mistOut::listener(conf, spawnForked);
if (conf.is_restarting && Socket::checkTrueSocket(0)){
INFO_MSG("Reloading input while re-using server socket");
execvp(argv[0], argv);
FAIL_MSG("Error reloading: %s", strerror(errno));
}
}else{
Socket::Connection S(fileno(stdout), fileno(stdin));
mistOut tmp(S, -1);
return tmp.run();
}
}
INFO_MSG("Exit reason: %s", Util::exitReason);
return 0;
}

112
src/output/output_tssrt.cpp Normal file
View file

@ -0,0 +1,112 @@
#include "mist/socket_srt.h"
#include "output_tssrt.h"
#include <mist/defines.h>
#include <mist/http_parser.h>
#include <mist/url.h>
namespace Mist{
OutTSSRT::OutTSSRT(Socket::Connection &conn, SRTSOCKET _srtSock) : TSOutput(conn){
// NOTE: conn is useless for SRT, as it uses a different socket type.
sendRepeatingHeaders = 500; // PAT/PMT every 500ms (DVB spec)
streamName = config->getString("streamname");
pushOut = false;
std::string tracks;
// Push output configuration
if (config->getString("target").size()){
HTTP::URL target(config->getString("target"));
if (target.protocol != "srt"){
FAIL_MSG("Target %s must begin with srt://, aborting", target.getUrl().c_str());
onFail("Invalid srt target: doesn't start with srt://", true);
return;
}
if (!target.getPort()){
FAIL_MSG("Target %s must contain a port, aborting", target.getUrl().c_str());
onFail("Invalid srt target: missing port", true);
return;
}
pushOut = true;
if (targetParams.count("tracks")){tracks = targetParams["tracks"];}
size_t connectCnt = 0;
do{
srtConn.connect(target.host, target.getPort(), "output");
if (!srtConn){Util::sleep(1000);}
++connectCnt;
}while (!srtConn && connectCnt < 10);
wantRequest = false;
parseData = true;
}else{
// Pull output configuration, In this case we have an srt connection in the second constructor parameter.
srtConn = Socket::SRTConnection(_srtSock);
parseData = true;
wantRequest = false;
// Handle override / append of streamname options
std::string sName = srtConn.getStreamName();
if (sName != ""){
streamName = sName;
HIGH_MSG("Requesting stream %s", streamName.c_str());
}
}
initialize();
}
OutTSSRT::~OutTSSRT(){}
void OutTSSRT::init(Util::Config *cfg){
Output::init(cfg);
capa["name"] = "TSSRT";
capa["friendly"] = "TS over SRT";
capa["desc"] = "Real time streaming of TS data over SRT";
capa["deps"] = "";
capa["required"]["streamname"]["name"] = "Stream";
capa["required"]["streamname"]["help"] = "What streamname to serve. For multiple streams, add "
"this protocol multiple times using different ports, "
"or use the streamid option on the srt connection";
capa["required"]["streamname"]["type"] = "str";
capa["required"]["streamname"]["option"] = "--stream";
capa["required"]["streamname"]["short"] = "s";
capa["codecs"][0u][0u].append("HEVC");
capa["codecs"][0u][0u].append("H264");
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");
cfg->addConnectorOptions(8889, capa);
config = cfg;
capa["push_urls"].append("srt://*");
JSON::Value opt;
opt["arg"] = "string";
opt["default"] = "";
opt["arg_num"] = 1;
opt["help"] = "Target srt:// URL to push out towards.";
cfg->addOption("target", opt);
}
// Buffer internally in the class, and send once we have over 1000 bytes of data.
void OutTSSRT::sendTS(const char *tsData, size_t len){
if (packetBuffer.size() >= 1000){
srtConn.SendNow(packetBuffer);
if (!srtConn){
// Allow for proper disconnect
parseData = false;
}
packetBuffer.clear();
}
packetBuffer.append(tsData, len);
}
bool OutTSSRT::setAlternateConnectionStats(Comms::Statistics &statComm){
statComm.setUp(srtConn.dataUp());
statComm.setDown(srtConn.dataDown());
statComm.setTime(Util::bootSecs() - srtConn.connTime());
return true;
}
void OutTSSRT::handleLossyStats(Comms::Statistics &statComm){
statComm.setPacketCount(srtConn.packetCount());
statComm.setPacketLostCount(srtConn.packetLostCount());
statComm.setPacketRetransmitCount(srtConn.packetRetransmitCount());
}
}// namespace Mist

33
src/output/output_tssrt.h Normal file
View file

@ -0,0 +1,33 @@
#include "output_ts_base.h"
#include <mist/ts_stream.h>
#include <mist/socket_srt.h>
namespace Mist{
class OutTSSRT : public TSOutput{
public:
OutTSSRT(Socket::Connection &conn, SRTSOCKET _srtSock);
~OutTSSRT();
static bool listenMode(){return !(config->getString("target").size());}
static void init(Util::Config *cfg);
void sendTS(const char *tsData, size_t len = 188);
bool isReadyForPlay(){return true;}
protected:
// Stats handling
virtual bool setAlternateConnectionStats(Comms::Statistics &statComm);
virtual void handleLossyStats(Comms::Statistics &statComm);
private:
bool pushOut;
std::string packetBuffer;
Socket::UDPConnection pushSock;
TS::Stream tsIn;
Socket::SRTConnection srtConn;
};
}// namespace Mist
typedef Mist::OutTSSRT mistOut;